Skip to content

Commit f286231

Browse files
committed
manual: Update
1 parent ab0f847 commit f286231

File tree

1 file changed

+301
-43
lines changed

1 file changed

+301
-43
lines changed

doc/manual.org

Lines changed: 301 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -67,35 +67,6 @@ gptel is a Large Language Model client for Emacs, with support for
6767
multiple models and backends. It works in the spirit of Emacs,
6868
available at any time and uniformly in any buffer.
6969

70-
gptel supports the following services.
71-
72-
#+html: <div align="center">
73-
#+attr_texinfo: :columns .2 .7
74-
| LLM Backend | Requires |
75-
|--------------------+----------------------------|
76-
| ChatGPT | [[https://platform.openai.com/account/api-keys][API key]] |
77-
| Anthropic (Claude) | [[https://www.anthropic.com/api][API key]] |
78-
| Gemini | [[https://makersuite.google.com/app/apikey][API key]] |
79-
| Ollama | [[https://ollama.ai/][Ollama running locally]] |
80-
| Llama.cpp | [[https://github.com/ggerganov/llama.cpp/tree/master/examples/server#quick-start][Llama.cpp running locally]] |
81-
| Llamafile | [[https://github.com/Mozilla-Ocho/llamafile#quickstart][Local Llamafile server]] |
82-
| GPT4All | [[https://gpt4all.io/index.html][GPT4All running locally]] |
83-
| Kagi FastGPT | [[https://kagi.com/settings?p=api][API key]] |
84-
| Kagi Summarizer | [[https://kagi.com/settings?p=api][API key]] |
85-
| Azure | Deployment and API key |
86-
| Groq | [[https://console.groq.com/keys][API key]] |
87-
| Perplexity | [[https://docs.perplexity.ai/docs/getting-started][API key]] |
88-
| OpenRouter | [[https://openrouter.ai/keys][API key]] |
89-
| together.ai | [[https://api.together.xyz/settings/api-keys][API key]] |
90-
| Anyscale | [[https://docs.endpoints.anyscale.com/][API key]] |
91-
| PrivateGPT | [[https://github.com/zylon-ai/private-gpt#-documentation][PrivateGPT running locally]] |
92-
| DeepSeek | [[https://platform.deepseek.com/api_keys][API key]] |
93-
| Cerebras | [[https://cloud.cerebras.ai/][API key]] |
94-
| Github Models | [[https://github.com/settings/tokens][Token]] |
95-
| Novita AI | [[https://novita.ai/model-api/product/llm-api?utm_source=github_gptel&utm_medium=github_readme&utm_campaign=link][Token]] |
96-
| xAI | [[https://console.x.ai?utm_source=github_gptel&utm_medium=github_readme&utm_campaign=link][API key]] |
97-
#+html: </div>
98-
9970
** Basic concepts
10071

10172
#+cindex: Large Language Model
@@ -218,7 +189,11 @@ some extra niceties for chat interaction.
218189

219190
** Chat persistence
220191

221-
** The rewrite interface
192+
** TODO The rewrite interface
193+
194+
** TODO gptel in Org mode
195+
196+
gptel offers a few extra conveniences in Org mode.
222197

223198
* TODO gptel's design
224199

@@ -451,14 +426,6 @@ always contains the last request as a gptel state-machine object (see
451426
[[*gptel's finite state machine][gptel's state machine]]).
452427

453428
** TODO gptel chat buffer UI
454-
455-
<<gptel-prompt-prefix-alist>>
456-
#+vindex: gptel-prompt-prefix-alist
457-
- ~gptel-prompt-prefix-alist~
458-
459-
#+vindex: gptel-response-prefix-alist
460-
- ~gptel-response-prefix-alist~
461-
462429
*** gptel in Org mode
463430

464431
#+vindex: gptel-org-branching-context
@@ -828,9 +795,12 @@ To use tools in gptel, you need
828795
currently include any tool specifications out of the box.
829796

830797
*** Obtaining tools
831-
*** Writing tools
798+
*** TODO Writing tools
832799
:PROPERTIES:
833800
:CUSTOM_ID: writing-tools
801+
:LAST_REVIEW: [2025-09-12 Fri]
802+
:NEXT_REVIEW: [2025-09-12 Fri]
803+
:REVIEWS: 0
834804
:END:
835805

836806
A gptel tool is a structure specifying an Elisp function, the format
@@ -903,6 +873,43 @@ arguments.
903873
subsequent requests in the same buffer. This is primarily useful
904874
in chat buffers.
905875

876+
For example, to not prompt for user confirmation when creating files
877+
in the current working directory but in all other directories:
878+
879+
#+begin_src emacs-lisp
880+
(gptel-make-tool
881+
:name \"create_file\"
882+
:description \"Create a new file with the specified content\"
883+
:confirm (lambda (&rest args)
884+
(cl-destructuring-bind (path filename content)
885+
args
886+
(not (my-filepath-within-current-directory-p path))))
887+
:function (lambda (path filename content)
888+
(let ((full-path (expand-file-name filename path)))
889+
(with-temp-buffer
890+
(insert content)
891+
(write-file full-path))
892+
(format \"Created file %s in %s\" filename path)))
893+
:args (list '(:name \"path\"
894+
:type string
895+
:description \"The directory where to create the file\")
896+
'(:name \"filename\"
897+
:type string
898+
:description \"The name of the file to create\")
899+
'(:name \"content\"
900+
:type string
901+
:description \"The content to write to the file\"))
902+
:category \"filesystem\")
903+
904+
(defun my-filepath-within-current-directory-p (filepath)
905+
\"Return t if FILEPATH is within the current working directory or its subdirectories.
906+
FILEPATH can be relative or absolute, and may or may not end in a slash.\"
907+
(let* ((current-dir-truename (file-truename (file-name-as-directory default-directory)))
908+
(filepath-truename (file-truename (expand-file-name filepath)))
909+
(filepath-dir-truename (file-name-as-directory (file-name-directory filepath-truename))))
910+
(string-prefix-p current-dir-truename filepath-dir-truename)))
911+
#+end_src
912+
906913
**** Specifying tool arguments
907914

908915
Tool arguments are specified in an Elisp format that mirrors the JSON
@@ -2041,13 +2048,264 @@ This is a sentence that will be filled in later.
20412048
:REVIEWS: 0
20422049
:END:
20432050

2044-
gptel is really the combination of two libraries: the request API and the =gptel= UI. gptel's opinionated UI -- the chat buffer, the transient menus, the presentation of tool calls and so on -- comprise one way of using gptel, but it is certainly not the only one.
2051+
gptel is really the combination of two libraries: the request API and
2052+
the =gptel= UI. For convenience both are made available in a single
2053+
Emacs package. gptel's opinionated UI -- the chat buffer, the
2054+
transient menus, the presentation of tool calls and so on -- comprise
2055+
one way of using gptel, but it is certainly not the only one.
2056+
2057+
Accordingly, the first few sections of this cookbook describe how to
2058+
customize gptel's UIs beyond simply setting user options. The final
2059+
section, [[*Building applications with ~gptel-request~]] covers the use of
2060+
gptel's API to write specialized LLM interaction commands, alternative
2061+
UIs or packages.
2062+
2063+
** Improving the chat buffer experience
2064+
2065+
A "chat buffer" is any text, Markdown or Org mode buffer where
2066+
~gptel-mode~ is turned on. This is a regular Emacs buffer and is
2067+
freely editable.
2068+
2069+
While gptel provides a few knobs for modifying its behavior in chat
2070+
buffers ([[*gptel chat buffer UI]]), it makes minimal assumptions about
2071+
the format of the chat or the structure, layout of chat buffers. In
2072+
particular, it does not add any automatic behavior. This section
2073+
provides recipes for some quality of life improvements to gptel chat
2074+
buffer behavior.
2075+
2076+
*** Automatically persist chats to disk
2077+
2078+
LLM chats can be one-off, transient interactions whose value is
2079+
limited to the moment, or perhaps to the side effects they produce via
2080+
tool calls (such as editing code in other files). In this case there
2081+
is little reason to keep the chat buffers around. But the
2082+
conversations may also contain information you may want to refer to in
2083+
the future.
2084+
2085+
Since they are regular Emacs buffers, they can be written to disk via
2086+
~save-buffer~ (=C-x C-s= by default), but you will have to manually
2087+
specify a location and a filename. With a little elisp, you can
2088+
automate this process so the chat buffers are automatically written to
2089+
a predtermined directory with a timestamped name when you run
2090+
~save-buffer~:
2091+
2092+
#+begin_src emacs-lisp
2093+
(require 'xdg)
2094+
2095+
(defvar gptel-mode-chat-directory
2096+
(file-name-concat (xdg-data-home) "gptel-chat")
2097+
"Directory in which to store gptel chats")
2098+
2099+
(defun gptel-mode-assign-filename ()
2100+
"If this is a dissociated chat buffer, save it to a predetermined location.
2101+
2102+
Intended to be added to `before-save-hook' in gptel chat buffers. Use a
2103+
prefix-arg to save manually."
2104+
(unless (or (buffer-file-name) current-prefix-arg)
2105+
(make-directory gptel-mode-chat-directory t)
2106+
(setq buffer-file-name
2107+
(file-name-concat
2108+
gptel-mode-chat-directory
2109+
(concat
2110+
(format-time-string "%Y%m%d%H%M%2S-")
2111+
(file-name-sans-extension
2112+
(replace-regexp-in-string " " "-" (buffer-name)))
2113+
(pcase major-mode
2114+
('org-mode ".org") ('markdown-mode ".md") (_ ".txt")))))
2115+
(rename-buffer (file-name-nondirectory buffer-file-name) t)))
2116+
#+end_src
2117+
2118+
Unless the chat buffer is already associated with a file,
2119+
~gptel-mode-assign-filename~ sets a suitable timestamped file name for
2120+
a buffer. We ensure that this runs in gptel chat buffers right before
2121+
~save-buffer~ is called:
2122+
2123+
#+begin_src emacs-lisp
2124+
(defun gptel-mode-auto-save-chat ()
2125+
"Enable saving chat buffers to predetermined location."
2126+
(add-hook 'before-save-hook #'gptel-mode-assign-filename
2127+
nil 'local))
2128+
2129+
(add-hook 'gptel-mode-hook #'gptel-mode-auto-save-chat)
2130+
#+end_src
2131+
2132+
Note that ~gptel-mode-assign-filename~ does not alter the value of
2133+
~default-directory~ in the buffer, and the chat buffer will continue
2134+
to have access to the same environment until it is closed and reopened
2135+
as a file from the custom location. This is important, for example,
2136+
if tool calls involving file paths are involved.
2137+
2138+
*** Resume chat sessions more robustly
2139+
2140+
gptel adds metadata to a chat buffer when saving it to a file. Among
2141+
other information, this metadata includes the model, backend and tools
2142+
in use, as well as the response boundaries. When opening this file,
2143+
~gptel-mode~ is not automatically enabled, so modifying the buffer can
2144+
cause the recorded response boundaries to go out of sync.
2145+
2146+
~gptel-mode~ can be automatically enabled on opening the file by
2147+
adding a [[info:emacs#Specifying File Variables][property-line]] at the top of the file, like
2148+
2149+
#+begin_src
2150+
# -*- eval: (gptel-mode 1) -*-
2151+
#+end_src
2152+
2153+
We can get gptel to include this line automatically as follows:
2154+
2155+
#+begin_src emacs-lisp
2156+
(defun gptel-mode-auto-enable ()
2157+
"Ensure that this file opens with `gptel-mode' enabled."
2158+
(save-excursion
2159+
(let ((enable-local-variables t)) ; Ensure we can modify local variables
2160+
(if (and (save-excursion
2161+
(goto-char (point-min))
2162+
(looking-at ".*-\\*-"))) ; If there's a -*- line
2163+
;; First remove any existing eval, then add the new one
2164+
(modify-file-local-variable-prop-line
2165+
'eval nil 'delete))
2166+
;; Always add our eval
2167+
(add-file-local-variable-prop-line
2168+
'eval '(and (fboundp 'gptel-mode) (gptel-mode 1))))))
2169+
2170+
(add-hook 'gptel-save-state-hook #'gptel-mode-auto-enable)
2171+
#+end_src
2172+
2173+
~gptel-save-state-hook~ runs just before gptel updates its metadata in
2174+
the file.
2175+
2176+
Note that this function will overwrite any other =eval= local variable
2177+
specification already present on the line.
2178+
2179+
** Tweaking LLM responses
2180+
2181+
gptel does not modify or post-process LLM responses to ~gptel-send~
2182+
commands in any way except one: when in an Org mode buffer, it
2183+
converts (possibly) Markdown output to Org mode. ([[*gptel in Org mode]])
2184+
2185+
There are several commonly requested modifications that fall under the
2186+
umbrella of "sanitizing" the response. This section explains how to
2187+
apply them when using ~gptel-send~ via the
2188+
=gptel-post-response-functions= hook.
2189+
2190+
Further below we cover more involved response transformations you can
2191+
do by reaching into the guts of gptel's state machine.
2192+
2193+
*** Response transformations with ~gptel-post-response-functions~
2194+
2195+
~gptel-post-response-functions~ is called after inserting the LLM's
2196+
response to ~gptel-send~, with buffer positions delimiting the
2197+
response as arguments.
2198+
2199+
Two simple uses. Both functions here accept a start and end point as
2200+
arguments, so they can simply be added to the hook.
2201+
2202+
- Preview any LaTeX in the response:
2203+
2204+
#+begin_src emacs-lisp
2205+
(add-hook 'gptel-post-response-functions #'org--latex-preview-region)
2206+
#+end_src
2207+
2208+
This is an Org mode function, but it will work in Markdown mode too!
2209+
(Expect Org mode to complain about it though.)
2210+
2211+
- Fill the response region:
2212+
2213+
#+begin_src emacs-lisp
2214+
(add-hook 'gptel-post-response-functions #'fill-region)
2215+
#+end_src
2216+
2217+
Unfortunately, this will also fill any code blocks in the region,
2218+
which can be catastrophic.
2219+
2220+
We fix this problem below, where we use the hook to perform a number
2221+
of different transformations:
2222+
2223+
- In chat buffers, if ~auto-fill-mode~ is turned on, we fill the
2224+
response region while avoiding filling code blocks.
2225+
- In code buffers, we remove any code fences (=```=) inserted around
2226+
code, and indent it.
2227+
2228+
#+begin_src emacs-lisp
2229+
(add-hook 'gptel-post-response-functions #'gptel-sanitize-response)
2230+
2231+
(defun gptel-sanitize-response (beg end)
2232+
"Sanitize gptel-send response reasons."
2233+
(unless (= beg end) ;no response, skip
2234+
(cond
2235+
;; Remove code fences from prog-mode buffers
2236+
((derived-mode-p 'prog-mode)
2237+
(save-excursion
2238+
(goto-char end)
2239+
(when (looking-back "```\s-*" (line-beginning-position))
2240+
(delete-region (match-beginning 0) (point))
2241+
(setq end (point)))
2242+
(goto-char beg)
2243+
(when (looking-at "\s-*```")
2244+
(delete-region (point) (line-end-position))
2245+
(setq beg (point)))
2246+
(indent-region beg end)))
2247+
;; Fill response regions in gptel-mode
2248+
((and gptel-mode auto-fill-function)
2249+
(save-excursion
2250+
(if (derived-mode-p 'org-mode)
2251+
(let ((transient-mark-mode t))
2252+
(goto-char end) (push-mark)
2253+
(goto-char beg) (activate-mark)
2254+
(org-fill-paragraph nil t))
2255+
(goto-char beg)
2256+
(while (<= (point) end)
2257+
(markdown-fill-paragraph)
2258+
(markdown-forward-paragraph))))))))
2259+
#+end_src
2260+
2261+
Here is a second example. If you use ~gptel-org-branching-context~
2262+
([[*gptel in Org mode]]), the heading lineage at any point in the buffer
2263+
determines the conversation context. Any extra headings inserted by
2264+
the LLM will then mess this up. We use the post-response-functions
2265+
hook to convert headings in the response into bold text instead:
2266+
2267+
#+begin_src emacs-lisp
2268+
(add-hook 'gptel-post-response-functions #'gptel-org-remove-headings)
2269+
2270+
(defun gptel-org-remove-headings (beg end)
2271+
"Convert Org headings between BEG and END to bold text."
2272+
(when (derived-mode-p 'org-mode)
2273+
(save-excursion
2274+
(goto-char beg)
2275+
(while (re-search-forward org-heading-regexp end t)
2276+
(forward-line 0)
2277+
(delete-char (1+ (length (match-string 1))))
2278+
(insert-and-inherit "*")
2279+
(end-of-line)
2280+
(skip-chars-backward " \t\r")
2281+
(insert-and-inherit "*")))))
2282+
#+end_src
2283+
2284+
*** TODO Response transformations using gptel's state machine
2285+
2286+
** Adding UI elements to gptel
2287+
** Extending gptel's Transient menus
2288+
** TODO Building applications with ~gptel-request~
2289+
2290+
The entry point to the request library is the ~gptel-request~
2291+
function, which presents a unified way to interact with any LLM that
2292+
gptel supports. It can be used as a building block for custom
2293+
commands that live in your configuration, for custom workflows, or
2294+
even to build complex packages.
2295+
2296+
If you intend to use ~gptel-request~ to write specialized LLM
2297+
interaction commands or build a different UI, you can require only the
2298+
=gptel-request= feature:
2299+
2300+
#+begin_src emacs-lisp
2301+
(require 'gptel-request)
2302+
#+end_src
20452303

2046-
The entry point to the request library is the ~gptel-request~ function, which presents a unified way to interact with any LLM that gptel supports. It can be used as a building block for custom commands that live in your configuration, custom workflows, or even to build complex packages.
2304+
This way you avoid loading unnecessary code, such as gptel's chat buffer UI or Transient menus.
20472305

20482306
By way of examples, this chapter describes how to do these things with ~gptel-request~.
20492307

2050-
** Simple ~gptel-request~ commands
2308+
*** Simple ~gptel-request~ commands
20512309

20522310
gptel provides gptel-request, a lower level function, to query ChatGPT with custom behavior. It accepts a prompt to send to the the active ~gptel-model~, along with several keyword arguments that modify what will be sent and how the response will be handled:
20532311

@@ -2096,7 +2354,7 @@ It is not recommended to grab the buffer contents as a string, as this will caus
20962354

20972355
We can now write our first (admittedly useless) custom command:
20982356

2099-
** Building an application
2357+
*** Building an application
21002358

21012359
#+INCLUDE: ../README.org::*FAQ :minlevel 1
21022360
#+INCLUDE: ../README.org::*Alternatives :minlevel 1

0 commit comments

Comments
 (0)