gptel [63/105]
Table of Contents
- 1. PROJECT gptel-eshell: hammer out the kinks
- 2. PROJECT Prompt buffer and presets refactor
- 3. TODO Allow test curl command to be copied
- 4. PROJECT gptel-tools: two-level menu
- 5. TODO gptel tools + reasoning bug report
- 6. PROJECT Write a user manual and developer manual
- 7. TODO reasoning + streaming response: handle too much whitespace between the reasoning block and rest
- 8. TODO Mike Olson’s list of small fixes to the gptel UI
- 9. TODO Make
gptel-backend
easier to specify - 10. PROJECT Robustly distinguish between prompts and responses
- 11. TODO Send a message based on stopreason
- 12. TODO Find a way to provide gptel–suffix-send with a custom fsm
- 13. TODO Make it possible to attach description or instructions to context chunks
- 14. TODO gptel: Replace crowdsourced prompts with fabric prompts
- 15. TODO Stop tokens
- 16. TODO If redirecting to new gptel session, write entire template to it
- 17. TODO Find a better way to handle Ollama tool use + no-stream
- 18. TODO gptel-rewrite tweaks suggested by mike olson
- 19. TODO Designate a column in gptel-menu for extensions
- 20. TODO gptel-rewrite: extract function
- 21. TODO auto-update models for a backend
- 22. DONE Failing mcp connections are still not handled correctly, inform
- 23. SUSPENDED Review gptel-openai-assistant by Ahmed Shariff ATTACH
- 24. SUSPENDED gptel-claude still losing data chunks ATTACH
- 25. PROJECT RAG solution for gptel
- 26. MAYBE gptel-mode <- globalized-minor-mode
- 27. MAYBE gptel: elisp
format
can do whitespace padding - 28. MAYBE Use transient levels instead of gptel-expert-commands
- 29. MAYBE gptel: Add an undo boundary before the response
- 30. MAYBE Add a gptel-curl-extra-args for global extra Curl args
- 31. MAYBE gptel: Put region pulsing in the after-response-hook.
- 32. MAYBE Good rewrite directives for gptel by madmacs
- 33. MAYBE gptel-rewrite: Launch by prompting for the rewrite instruction
- 34. PROJECT Buffer-local context sets via links
- 35. PROJECT Tool use
[28/34]
ATTACH - 36. PROJECT MCP integration
- 37. PROJECT FIM completions for gptel
- 38. PROJECT Make gptel-request standalone
- 39. PROJECT Dynamic system messages and template support
[6/8]
- 40. PROJECT Customizable/Org-aware prompt processing
- 41. PROJECT Structured/JSON output
- 42. PROJECT Add better per-run processing support
- 43. PROJECT gptel: Editing other buffers from the chat buffer
- 44. PROJECT Vision
- 45. gptel 0.9.8 reddit announcement
- 46. erts files for testing
- 47. DONE Handle Deepseek’s
reasoning_content
- 48. CLOSED Process chain executor (FSM) for gptel
[8/8]
- 49. DONE gptel: Stripping properties from org buffer before sending
- 50. CLOSED Better diff interface
[3/3]
- 51. CLOSED gptel model-specific behavior framework
- 52. SUSPENDED gptel-tasks
- 53. DONE Perplexity citation support
- 54. SUSPENDED Move to overlay tracking
- 55. SUSPENDED Implement gptel-complete
- 56. SUSPENDED Function calling demos with Anthropic
- 57. SUSPENDED gptel: stateless design
- 58. CANCELLED gptel: more context options
[1/2]
- 59. CANCELLED Add support for older versions of curl (< 1.8)
- 60. CANCELLED gptel editing prompt: use
string-edit
- 61. CANCELLED Use the :around cl-defmethod extra param to handle Org
- 62. CANCELLED Reset ollama context after changing model
- 63. CANCELLED gptel & nixos: Write expression for Emacs 27.1 to test
- 64. CANCELLED azure backend with multiple models
- 65. CANCELLED gptel: copilot-style completion UI
- 66. CANCELLED Maybe try using the built-in hierarchy library to inspect context?
- 67. DONE Why is gptel-anthropic’s maxtokens set to 1024 by default?
- 68. DONE gptel-deepseek parser update required ATTACH
- 69. DONE gptel: Remove support for gemini-pro
- 70. DONE Add gptel-curl-extra-args to cover global extra curl args
- 71. DONE Claude error: text content blocks must contain non-whitespace text ATTACH
- 72. DONE Check out evedel
- 73. DONE Composable menus for gptel
- 74. DONE jwiegely change the behavior of M-x gptel
- 75. DONE Fix gptel oneshot (non-streaming) Markdown -> Org converter
- 76. DONE markdown -> org stream converter: handling backticks
- 77. DONE gptel: handle headings in the markdown converter
- 78. DONE Update Joao Tavora about commits I added
- 79. DONE gptel: always-available diffing
- 80. DONE gptel: Add logging
- 81. DONE gptel-request: the
:buffer
and =:position keyword args aren’t clear - 82. DONE gptel: multi-line directive writing
- 83. DONE gptel: Google gemini support
- 84. DONE
defgeneric
system for querying other APIs? - 85. DONE Stateless design + save state
- 86. DONE Tag new
gptel
release - 87. DONE Add algal and d1egoaz to the gptel acknowledgments section
- 88. DONE gptel error handling for local llms
- 89. DONE gptel: Add kill-ring as source of prompt
- 90. DONE gptel: make defining openai backends easier
- 91. DONE Reply to ahyatt’s proposal for llm actions
- 92. DONE file-local-var storage with newlines: reply to Eli
- 93. DONE LLM UI experiments – reply to Andrew
- 94. DONE gptel: post-response hook’s beginning position is wrong
- 95. DONE gptel-anthropic parser losing text ATTACH
- 96. DONE Reply to Jan Rychter’s email
- 97. DONE Make gptel bugfix release
- 98. DONE gptel: Add a non-chat boolean for regular buffers
- 99. DONE gptel: context indicator in header-line
- 100. DONE Reply to tarsius
- 101. DONE Further fix gptel org formatter ATTACH
- 102. DONE Fontification malfunction when streaming responses
- 103. DONE gptel-gemini parser:
:parts
can have multiple:text
blocks - 104. DONE purpose-mode interferes with gptel’s display
- 105. DONE Improve docs around :key
Exported at: May 25 2025, 23:06
1. PROJECT gptel-eshell: hammer out the kinks
2. PROJECT Prompt buffer and presets refactor
TODO Describe preset indicator with help-echo when hovering
MAYBE Move gptel org element stripping to the transform step
DONE Move context injection to the transform step
file:///home/karthik/.local/share/git/elpaca/repos/gptel/gptel.el#MissingReference
Currently it’s part of the realize step.
DONE gptel: Make it so augment handlers are debuggable
[BROKEN LINK: No match for fuzzy expression: defun gptel–handle-augment (fsm]
Currently it fails to debug even when using condition-case-unless-debug and debug-on-error.
3. TODO Allow test curl command to be copied
From the dry-run output menu, and perhaps even from the transient menu.
Should there be a gptel-debug
command?
4. PROJECT gptel-tools: two-level menu
file:///home/karthik/.local/share/git/elpaca/repos/gptel-main/gptel-transient.el#MissingReference
Unfortunately it’s not (easily) possible to track the suffixes hidden/disabled groups when exporting the prefix. For example, in the below example if the “w t” suffix is hidden because the group is hidden, “w” cannot be exported even if it was selected before hiding the group. (To hide the group, use the second “z” option.)
(setq toggle nil) (transient-define-prefix testprefix () "TODO" :refresh-suffixes t :transient-suffix #'transient--do-call [:description "Test" :class transient-subgroups :setup-children (lambda (_) (transient-parse-suffixes 'testprefix ;; (list [(:info "Test1")]) (list [:if (lambda () toggle) ("w t" "Test1" "w")] [("z" "Test2" (lambda () (interactive) (cl-callf not toggle)))] [("y" "Test3" "y") ("RET" "Done" (lambda (args) (interactive (list (transient-args transient-current-command))) (message "%S" args)))])))])
6. PROJECT Write a user manual and developer manual
These are intended to be different sections of the same manual (single document).
The user manual should contain
[ ]
a brief explanation of what gptel is[ ]
a brief overview of how LLM APIs work[ ]
explanations of gptel’s features[ ]
along with customization relevant to each feature[ ]
explanation of (WIP) RAG and tool use
The developer manual should include
[ ]
building on gptel: examples of gptel-request usage[ ]
a brief explanation of gptel’s design goals, focusing on- its scope: what kinds of features will/won’t be added
- philosophy: integrate with Emacs, reduce cognitive burden, don’t introduce syntax
[ ]
a brief overview of gptel’s main control flow and state management[ ]
data structures (plists, plists everywhere!)
Resources:
- Uniline for drawing line diagrams for info?
gptel-request diagram
(User options) GPTEL-SEND (Hooks for customization) ║ │ ║ v v v ╭────────────┴─────────────╮ │ Copy region │ ╭───────────────────────────╮ │ (or buffer above cursor) │ │ (Org mode only) │ │ to a temporary buffer │ │ gptel-org-ignore-elements │ ╰────────────┬─────────────╯ │gptel-org-branching-context├─>─────────────┤·╶─╴·╶─╴·╶─╴· gptel-prompt-filter-hook ╰───────────────────────────╯ v ╭───────────────────────────╮ ╭────────────┴──────────────╮ │ Add base64-encoded media │ │ Create messages array, │ │ from links ├─>│ Assign user and LLM roles │ │ gptel-track-media │ │ to text │ ╰───────────────────────────╯ ╰────────────┬──────────────╯ │ ╭─────────────────────────╮ │ │ Collect context │ │ │(regions, buffers, files)├──╮ v │ gptel-use-context │ │ ╭─────────┴──────────╮ ╰─────────────────────────╯ │ │ │ ╭─────────────────────────╮ ├──>│ Create payload │ │ Prepare tools │ │ │ │ │ gptel-use-tools ├──┤ ╰─────────┬──────────╯ │ gptel-tools │ │ v ╰─────────────────────────╯ │ ╔═════════╧══════════╗ ╭─────────────────────────╮ │ ║ Send request ║ │ Run and add directive │ │ ╚═════════╤══════════╝ │ gptel--system-message ├──┤ │ ╰─────────────────────────╯ │ │·╶─╴·╶─╴·╶─╴·╶gptel-post-request-hook ╭─────────────────────────╮ │ v │ Backend parameters │ │ ╶──┴──╴ │ gptel-backend ├──┤ ╭ ─ ─ ─ ─ ─╮ ╰─────────────────────────╯ │ ASYNC WAIT ╭─────────────────────────╮ │ ╰ ─ ─ ─ ─ ╯ │ gptel-model ├──╯ ╶──┬──╴ ╰─────────────────────────╯ v·╶─╴·╶─╴·╶─╴· gptel-pre-response-hook ╭──────────┴───────────╮ ╭─<──┤Parse partial response├╮ │ ╰──────────────────────╯│ ╭─o ├·gptel-post-stream-hook │ │ ╭───────────────────────╮│ │ ╰─>─┤ Insert response chunk ├╯ │ ╰───────────────────────╯ ╰─────────────────╮ v·╶─╴·╶─╴·╶─╴· gptel-post-response-functions ╶──┴──╴
(USER OPTIONS) GPTEL-SEND (HOOKS) ║ │ ║ v v v ╭───────────────────────────╮ ╭────────────┴─────────────╮ │ (Org mode only) │ │ Copy region │ │ gptel-org-ignore-elements │ │ (or buffer above cursor) │ │gptel-org-branching-context├───>┤ to a temporary buffer │ ╰───────────────────────────╯ ╰────────────┬─────────────╯ ╭──────────────────────────╮ │·╶─╴·╶─╴·╶─╴· gptel-prompt-filter-hook │ gptel-track-response ├──╮ v ╰──────────────────────────╯ │ ╭────────────┴──────────────╮ ╭───────────────────────────╮ │ │ Create messages array, │ │ Add base64-encoded media │ ├─>┤ Assign user and LLM roles │ │ from links ├─╯ │ to messages │ │ gptel-track-media │ ╰────────────┬──────────────╯ ╰───────────────────────────╯ │ ╭─────────────────────────╮ │ │ Collect context │ │ │(regions, buffers, files)├──╮ v │ gptel-use-context │ │ ╭─────────┴──────────╮ ╰─────────────────────────╯ │ │ │ ╭─────────────────────────╮ ├────>│ Create payload │ │ Prepare tools │ │ │ │ │ gptel-use-tools ├──┤ ╰─────────┬──────────╯ │ gptel-tools │ │ v ╰─────────────────────────╯ │ ╔═════════╧══════════╗ ╭─────────────────────────╮ │ ║ Send request ║ │ Run and add directive │ │ ╚═════════╤══════════╝ │ gptel-directives ├──┤ │·╶─╴·╶─╴·╶─╴· gptel-post-request-hook │ gptel--system-message │ │ │ ╰─────────────────────────╯ │ v ╭─────────────────────────╮ │ ╶──┴──╴ │ Backend parameters │ │ ╭ ─ ─ ─ ─ ─╮ │ gptel-backend ├──┤ ASYNC WAIT ╰─────────────────────────╯ │ ╰ ─ ─ ─ ─ ╯ ╭─────────────────────────╮ │ ╶──┬──╴ │ gptel-model ├──╯ v ╰─────────────────────────╯ ├·╶─╴·╶─╴·╶─╴· gptel-pre-response-hook ╭──────────────────────────╮ ╭───────────────────────╮ │ Handle "Reasoning" ├─────>┤ │ │ gptel-include-reasoning │ ╭─<─┤ Parse partial response│ ╰──────────────────────────╯ │╭<─┤ │<╮ ││ ╰───────────────────────╯ │ ││ ├ gptel-post-stream-hook ││ ╭───────────────────────╮ │ │╰──┤ Insert response chunk ├─o │ ╰───────────────────────╯ │ ╭──────────────────────────╮ │ ╭───────────────────────╮ │ │ gptel-confirm-tool-calls ├─>o──>┤ Confirm tool calls │ v ╰──────────────────────────╯ │ ╰───────────────────────╯ │ ╭──────────────────────────╮ │ ╭───────────────────────╮ │ │gptel-include-tool-results├─>┴──>┤ Insert tool results │ │ ╰──────────────────────────╯ ╰───────────┬───────────╯ │ ├─────────────╯ v·╶─╴·╶─╴·╶─╴· gptel-post-response-functions ╶──┴──╴
GPTEL-REQUEST │ v ╭────────────────────────────╮ ╭────────────┴──────────────╮ │ Environment │ │Single or multi-part PROMPT│ │ │ │ │ │ gptel-model │ │Single or multi-part SYSTEM│ │ gptel-backend │ ╰────────────┬──────────────╯ │ │ v │ gptel-use-context │ ╭─────────┴──────────╮ │ gptel-use-tools ├────>│ Create payload ├·····>·· │ gptel-tools │ │ INFO │ · │ gptel-cache │ ╰─────────┬──────────╯ · │ gptel-include-reasoning │ v · │ gptel-track-response │ ╔═════════╧══════════╗ · │ │ ║ Send request ║ · │ gptel-org-convert-response │ ╚═════════╤══════════╝ · ╰────────────────────────────╯ v · ╶──┴──╴ · ╭ ─ ─ ─ ─ ─╮ · ASYNC WAIT · ╰ ─ ─ ─ ─ ╯ · ╶──┬──╴ · v · ╭────────────┴─────────────╮ · │ Call │ · │ (CALLBACK response INFO) │··<·· ╰────────────┬─────────────╯ v ╶──┴──╴
7. TODO reasoning + streaming response: handle too much whitespace between the reasoning block and rest
[BROKEN LINK: No match for fuzzy expression: copy-marker tracking-marker nil)))))))]
8. TODO Mike Olson’s list of small fixes to the gptel UI
https://github.com/mwolson/emacs-shared/blob/master/init/shared-init.el#L699
I also ended up writing small wrappers starting here to assign each of the following to different keybinds, some of which could potentially be made a bit more streamlined:
- Add current function to context (uses the mark-function that I wrote in the gist)
- Add the current file to context
- View all context (note - it would also be nice if the cursor was positioned a bit forward so that hitting “d” would immediately mark the first item, currently “d” just moves to the first item without marking it)
- When the view context UI is opened, assign a key to apply changes and quit without further prompts
- Add the current function to context and immediately open a split gptel window off to the right to ask a question about it
- Start gptel itself without any transient menus, immediately getting into a gptel buffer for the default backend and model
9. TODO Make gptel-backend
easier to specify
#556 Customization of `gptel-backend` causes recursive require error, prevents gptel from loading #529 Instructions for adding to the list of Gemini models
The idea is to allow gptel-backend
to be a string (that’s the name of the backend). In gptel-request
, we can check the type of the variable:
(let ((backend (or (cl-etypecase gptel-backend (gptel-backend gptel-backend) (string (alist-get gptel-backend gptel--known-backends nil nil #'equal))) (user-error "Backend \"%s\" is not known to be defined" gptel-backend)))) ...)
We just have to be sure that we do this everywhere gptel-backend
is let-bound. Alternatively, we can use gptel--backend
as the actual backend and gptel-backend
can always be a string.
We may also need a gv-settable gptel-get-backend
that can be used in situations like:
(push 'gpt-4o (gptel-backend-models (gptel-get-backend "ChatGPT")))
10. PROJECT Robustly distinguish between prompts and responses
Disadvantages of using text-properties for this:
- Roles are invisible
- If the text-properties are sticky, self-inserted text in a response region is assigned the response role, but yanked or otherwise inserted text isn’t.
- If the text-properties are non-sticky, there’s no way to modify the response text at all.
- Persistence across Emacs sessions is fragile (requires gptel-mode to be turned on)
There are several proposals for improving this:
- #321 How should gptel distinguish between LLM responses and user text?
- #343 Add response color-coding & role setting for gptel buffer
- #565 Use zero-width delimiters for role tracking in gptel-mode
- #546 Naive ideas for making gptel simpler yet more powerful
In short, they suggest:
- Using syntax, i.e. user-defined prompt and response prefixes in chat buffers.
- Using zero-width delimiters
- Providing response-highlighting and role-setting commands
- Using overlays
MAYBE Add a file hash for gptel buffers (robustness)
[BROKEN LINK: No match for fuzzy expression: :END:]
11. TODO Send a message based on stopreason
12. TODO Find a way to provide gptel–suffix-send with a custom fsm
This is required for packages like gptel-openai-assistant to reuse gptel-menu.
13. TODO Make it possible to attach description or instructions to context chunks
The idea is to allow a little before-string
before each context overlay, then incldue that before the chunk when creating the context. I’m not sure yet how to make this feature discoverable.
- Calling
gptel-add
with a prefix arg, or using a keybinding at the context overlay, should allow us to attach a description or instructionsgptel-context-annotate
bound toC-c C-a
gptel-context-clear
bound toC-c C-k
- This annotation should then be included with the chunk when creating the context string.
- This annotation doesn’t need to be stored as a variable, we can store it in the overlay and display it as a before string, maybe.
14. TODO gptel: Replace crowdsourced prompts with fabric prompts
15. TODO Stop tokens
- Claude
- JSON array of strings that will cause the model to stop generating text if encountered, specified at the top level of the API request(?)
16. TODO If redirecting to new gptel session, write entire template to it
[BROKEN LINK: No match for fuzzy expression: I don't want to populate the menu with even more options, at least for now. Dumping the full prompt if it's a new gptel session sounds like a good compromise to me, I]
17. TODO Find a better way to handle Ollama tool use + no-stream
18. TODO gptel-rewrite tweaks suggested by mike olson
19. TODO Designate a column in gptel-menu for extensions
20. TODO gptel-rewrite: extract function
https://old.reddit.com/r/emacs/comments/1i0vsz9/tech_demo_completing_functions_using_gptel/mc8ponx/
There is one small thing that would make it slightly easier to use some of the transient-wrapped functions in gptel-rewrite without undergoing too complex a change. Using gptel–suffix-rewrite as an example, it would be nice to have the code from (interactive …) onwards moved into its own named function, so that the transient-define-suffix is just a key, description, and function call. This would provide easier access from .emacs config and other libs to be able to directly call those named functions and skip the initial transient entry point. I basically forked that function to get that kind of access.
It will be interesting to see how the context changes turn out. Hopefully there will remain a good way to visualize and edit context outside of a chat buffer for flows that want to skip having one (or at least, not show one immediately to the user).
What I meant specifically about opening a chat session is that just calling the gptel function will prompt about which backend to use, which I currently have to write a small wrapper to be able to skip. It has to do: * Get the backend name and format it with “%s” * Call (gptel formatted-backend-name nil “###”) * Call switch-to-buffer since the buffer doesn’t get raised when called non-interactively (and args can’t get passed interactively AFAICT)
Maybe a helper function named like gptel-start or similar could help differentiate the programmatic and interactive cases? Variables to control backend prompting is another possibility.
21. TODO auto-update models for a backend
Since I don’t want to make automatic network requests to fetch model information from the API,
#447 Enumerate Ollama models on the server pointed to by :host directive #567 Determine which models exist with API calls rather than hard coding them.
here’s an idea.
- Define a cl-defgeneric that dispatches on the backend type, and updates the model list for
gptel-backend
or any backend. - Define a new command,
gptel-update-models
, that calls these methods.
Now the user can run this whenever/wherever they want to update the list of models, like in a hook.
22. DONE Failing mcp connections are still not handled correctly, inform
The github mcp server can still fail to start without triggering the error
state in the connection.
23. SUSPENDED Review gptel-openai-assistant by Ahmed Shariff ATTACH
24. SUSPENDED gptel-claude still losing data chunks ATTACH
25. PROJECT RAG solution for gptel
Retrieval Augmented Generation, i.e. automatically fetching context for gptel requests.
It’s not clear yet how to do this. Just like we want to make gptel-tools
work with externally defined tool collections, I think it will be good to make RAG a plug-in option. There are many sources for RAG.
Simple vector store
RAG brief explanation
providing the LLM with global and local context for gptel-complete
https://www.youtube.com/watch?v=wS1si5Lh9lA
Constructs the global context from files in the directory and LSP symbols. Can use imenu.
starhugger: fim completions – check how context is found.
26. MAYBE gptel-mode <- globalized-minor-mode
27. MAYBE gptel: elisp format
can do whitespace padding
So some of the code involving formatting spaces in headers and annotation functions can be simplified using format
.
28. MAYBE Use transient levels instead of gptel-expert-commands
29. MAYBE gptel: Add an undo boundary before the response
#235 Feature request: setup an undo-boundary
The problem with this is coordinating async insertions into the undo change group – it’s tough to do.
30. MAYBE Add a gptel-curl-extra-args for global extra Curl args
31. MAYBE gptel: Put region pulsing in the after-response-hook.
32. MAYBE Good rewrite directives for gptel by madmacs
We can ask them if they want to contribute it to gptel.
https://github.com/certainty/madmacs/blob/main/modules/tools/madmacs-tools-ai.el
33. MAYBE gptel-rewrite: Launch by prompting for the rewrite instruction
So that the user doesn’t need to press “d” to get started.
34. PROJECT Buffer-local context sets via links
Different context sets are useful in different chat buffers (or regular buffers), so it would be good to have a way to specify this. With a single global set of contexts, you often have to engage in the add-context/dump-context dance.
The plan we’ve arrived on is to add buffer-local context via links to files in Org/Markdown mode buffers – this doesn’t work for buffers or regions, unfortunately, unless we add a link type.
[ ]
Add buffer-local context support[ ]
Find a way to indicate that a link will be included in the context, perhaps theactivate-func
Org link property, and something bespoke for Markdown?[ ]
Send text file links along with media files
35. PROJECT Tool use [28/34]
ATTACH
Jester7’s minimal gptel tools Jester7’s gptel tools
[ ]
Update Juri Linkov about gptel-request tools callback API change[ ]
An explicit indication of how many tools were called (Where do we put this?)[ ]
Allow editing tool calls from the confirmation prompt[ ]
Support(gptel-get-tool '(("emacs" ("read_buffer" "open_file"))))
[ ]
README: Add instructions for tool-use with OpenWebUI.[X]
Go through the model list and assign tool capabilities correctly.[ ]
Then ensure that the tool options in the menu only show up when appropriate.[X]
Log arguments as well: (truncated arguments)
Changed the tool call API:
- tool call confirmation: callback called with ((tool arg-values callback) …)
- tool call result: callback called with ((name arg-values result) …)
[X]
Juri Linkov’s tool-use logs, reply[X]
Decide on whether we want t, auto and nil, or force, t and nil for tool-use options.[X]
Ollama: Turn off streaming automatically if tools are used This is done in a hacky way right now. Eventually I need a better method.[X]
Tool call failures for async calls, should we handle them? This is difficult to handle.[X]
PrivateGPT tool use with/without streaming Doesn’t support, AFAICT[X]
README: Add demos for tool use.[X]
README: Add explanation of tool use.[X]
Handle tool use confirmation and result inclusion in the custom callbacks used by
gptel--suffix-send
. Perhaps we can pop up a buffer that displays the confirmation? Or just use the minibuffer? We can try to reuse the existing mechanism used in dedicated chat buffers, perhaps with some temporary function advice.Ended up using the minibuffer. It’s good enough for now.
[X]
Handle non-string, structured return values from tools. Tools can return only simple types or JSON objects/arrays. These are returned as plists/arrays, processed viagptel--json-read
.[X]
tool specification for tools that don’t need to be run We just specify:confirm t
in the tool specification. You can still send it if required that way.[X]
How do we control including tools in gptel? (gptel-use-tools
)[X]
Configuring tool calls: auto, always, any etc[X]
Handle tool call failures (wrap withcondition-case-unless-debug
?)[X]
Test OpenAI compatible APIs like Groq[X]
Don’t run post response hook etc when a request ends and we’re waiting on a tool call[X]
Ollama tool path with streaming This is not supported, but we need to turn off streaming automatically when tools are used. (Ollama returns a non-streaming JSON response.)[X]
Tool creation/appending code ingptel-request
- Check for model-tool capability
[X]
Ollama tool path without streaming (see note) Gives me the following error:
"message": "tools.0.type: Extra inputs are not permitted"
But it works once in a while! What the hell: Error in tool object construction, now fixed.[X]
Gemini tool path without streaming[X]
Gemini tool path with streaming[X]
Anthropic tool path without streaming[X]
Anthropic tool path with streaming[X]
OpenAI tool path without streaming[X]
OpenAI tool path with streaming[X]
Kagi tool path without streaming: No Kagi support[X]
Kagi tool path with streaming: No Kagi support
DONE gptel-mcp and gptel-integrations: Make more user-friendly
TODO Create test from gptel Anthropic stream log for testing ATTACH
TODO Support Anthropic built-in text-editor tool
36. PROJECT MCP integration
MCP server for a coding assistant
TODO Read the MCP examples and spec
37. PROJECT FIM completions for gptel
38. PROJECT Make gptel-request standalone
- Move all the networking code to gptel-request.el (renamed from gptel-curl.el) and make it standalone.
- gptel.el will contain all the non-transient UI code.
This will make it easier for third party packages to use only gptel-request
via (require 'gptel-request)
, and they can safely ignore the rest of gptel. This can be spun out into another package at some point, although naming issues will cause trouble.
No change to the other files.
39. PROJECT Dynamic system messages and template support [6/8]
The idea is to allow entries in gptel-directives
to be
- strings (as usual)
- lists (for templates)
- functions that return strings or lists of strings
This allows for
- DONE Dynamically generated system messages
This is useful for constructing a system message that has many parts. Depending on the context, we might want a system message to set
- the general tenor and tone of the conversation,
- instructions about formatting, control characters and special cases,
- instructions for the specific task in the next message,
- additional context strings from other buffers etc (already handled by
gptel-context
)
and possibly more. Instead of providing specific templates to do these things, which will always be inadequate, allowing the directive to be a function lets the user fine-tune their system message to the situation however they’d like.
- DONE Templates (Lists of strings)
The idea is to shove the initial part of the conversation into the directive as a template. The reason is that the LLM response can be guided more by providing a conversation history, and that the responses are often better this way.
For example, instead of:
You are an emacs-lisp programmer. Follow instructions and provide code and only code as output, without any explanations or markdown code fences. Refactor the provided code to do X, Y. Here is code for context: <code here>
in the system message, followed by
user: ;; Code to be refactored here
in the selected region, we use
You are an emacs-lisp programmer. Follow instructions and provide code and only code as output, without any explanations or markdown code fences. Here is code for context: <code here>
in the system message, followed by the templated exchange
user: ;; Code to be refactored here assistant: What is the required change user: Refactor this code to do X, Y.
In this case, the directive will be a function like this:
(defun gptel--rewrite-message (rewrite-text message) "Create a rewrite template with text and system message" `("You are an emacs-lisp programmer. Follow..." ;system message, can be nil (concat "The code is the following:" ,rewrite-text) ;user "What is the required change?" ;assistant ,message)) ;user
The question is how this should be implemented.
- Option 1:
gptel--system-message
can be a function
This is possibly simpler to implement. Somewhere in
gptel-request
we can let-bindgptel--system-message
to the final system message string, prepending the rest to the full user prompt. But the:system
arg ofgptel-request
makes less sense this way.It also makes updating the transient menu for the system message easier.
Maybe we can rename
gptel--system-message
to something more descriptive, likegptel--directive
? Where a “directive” can include the template like before. - Option 2:
gptel--system-message
is always the final system message string
and we use a new variable,
gptel-directive
orgptel-template
, to hold the function or list of strings.This makes sense since the
:system
arg togptel-request
should be the system message itself for clarity. It’ll also be easier to understand the code and to debug it.The question is where in the chain involving
gptel-request
we can do the required processing.
- Option 1:
- MAYBE template support for system prompt editor (accessed via the transient menu)
Done, but there’s no support for editing functions or lists, only static system message strings.
- DONE template support for
gptel-rewrite
How to split up the task into the prompt and the system message: Now that
gptel-request
supports a list of strings as a prompt, we can split it up this way:Let the system message be a string, customizable maybe?
(setq gptel-rewrite-directive "You are a %s programmer. Rewrite without comments or explanations, ...")
Let the prompt be the exchange that looks like:
(setq prompt '("<code here>" ;user "What is the required change?")) ;llm
Feed it to gptel request as
(gptel-request prompt :system gptel-rewrite-directive)
[ ]
Handle the case where the system message isn’t supported by the model?
- DONE template support for persistence (
gptel--save-state
)
Right now it expects the system message to be a string.Done, but it only saves the system message as a string. Saving lists and functions is… difficult.
- DONE template support for header-line in gptel-mode
Right now it expects the system message to be a string.
- DONE No system message
For models that don’t support system messages, or when the user does not want to supply one at all, we need to allow this by allowing the directive to be
nil
.
40. PROJECT Customizable/Org-aware prompt processing
To allow for features like https://github.com/karthink/gptel/issues/328, always create the prompt in a temp buffer. That way, we can add a list of filters to transform the text before sending, like Org’s export filters.
One commonly requested feature is removing the PROPERTIES drawers from the prompt in Org buffers before sending the request.
41. PROJECT Structured/JSON output
- Claude
- Via tool-use
- OpenAI
- Via tool-use or a “structured-output” mode
42. PROJECT Add better per-run processing support
We need a way to specify per-run pre- and post-response hooks, as well as transformation hooks.
TODO gptel-rewrite: Add post-rewrite hook that is called with the overlay
To apply custom user logic after the rewrite is ready, we could add a gptel-post-rewrite-functions
hook that is called with the overlay.
But anything added to this hook is buffer-local at best, and cannot be tailored to individual rewrite regions. An alternative is to add some kind of post-response callback behavior to gptel-request (or the gptel-rewrite command) itself.
This latter approach will solve a problem where we cannot modify gptel-request
’s callback for just one call.
43. PROJECT gptel: Editing other buffers from the chat buffer
This is an oft-requested feature.
44. PROJECT Vision
TODO Allow adding images from image data, not just image files
TODO Clean up gptel--wrap-user-prompt
, it’s a mess
This function is brittle and hardcodes the context.
Use the newly added gptel--inject-prompt
to make this more generic. The data model is simple: messages is just a list/array of prompts, and we want to add a prompt containing the context string to it at the right place. Work at this level of abstraction instead of what gptel--wrap-user-prompt
does right now.
45. gptel 0.9.8 reddit announcement
46. erts files for testing
47. DONE Handle Deepseek’s reasoning_content
48. CLOSED Process chain executor (FSM) for gptel [8/8]
To handle multi-turn requests, like those required for tool-use, we need something more robust than shoving function calls into callbacks. Unfortunately we don’t have access to a good promise API or to org-async right now1.
I may need to write a process executor ☹️.
[X]
Merge![X]
Create agptel-send
/gptel--suffix-send
specific transition table[X]
Separate the user messaging in gptel-send from the callbacks Almost – only the “Typing…” instruction is part of the process filter.[X]
Reuse the WAIT state after calling a tool[X]
Make gptel-request return just the fsm, no callback.[X]
Make gptel-request standalone.[X]
Rename fsm callbacks to fsm handlers[X]
Use a Finite State Machine (fsm) to drive gptel-request.
49. DONE gptel: Stripping properties from org buffer before sending
(while (re-search-forward org-property-drawer-re) (let ((drawer (org-element-at-point))) (when (org-element-type-p drawer 'property-drawer) (delete-region (org-element-begin drawer) (org-element-end drawer)))))
50. CLOSED Better diff interface [3/3]
There are a few things we need.
DONE A better default refactoring message and a better way to construct a refactoring message. Something that constrains the model more to output only code, without further questions or qualifications.
You are a % programmer. Generate ONLY code, with NO explanations. Do not ask for further qualifications, make assumptions as necessary. Do not use markdown code fences.
Refactor the following code.
Next, we add a hook where you can add functions to find a suitable initial refactoring message. For example, one of the hook functions can search the buffer for the string COMMITMSG, and provide a suitable one, and so on. The last one in the chain will produce a generic refactoring directive. We call this hook with run-hook-with-args-until-success
.
Next, this message should be editable in a temporary buffer. We want to reuse the system message editing buffer for this, so that needs to be made more generic. (Too bad string-edit
is not provided by compat
.)
CANCELLED A better system for entering the refactor message. Ideally we’d like to just add the message to an overlay at the location of the region being refactored.
The current system (minibuffer entry with extra context) is fine.
DONE A way to deal with the response that’s not immediate. Something like this:
- After selecting the region and specifying the refactoring message, we run gptel–rewrite
- We check heuristically to see if the output makes sense. i.e. it shouldn’t look like “What would you like to know.”
- When the LLM output is ready, we message the user with “Rewrite ready, M-x gptel-rewrite to apply”. This should also be available from the rewrite transient menu.
- What we do is: store the LLM output in a global variable.
- Now the user can choose to apply the output, or (e)diff or inline-diff against the original.
51. CLOSED gptel model-specific behavior framework
This is a collection of features supported by different APIs that I’d like to support on a model-level basis in gptel. I need to figure out how to structure the symbol-plist
of gptel models (which will now be symbols, not strings)
If all APIs support a feature (like stoptokens) we don’t need specialized behavior for it, we can just add it to the transient menu directly.
Feature | Global | Anthropic | OpenAI | Gemini | Ollama |
---|---|---|---|---|---|
stop tokens | Yes | ||||
Vision | |||||
Audio | |||||
Prompt caching | Yes | ||||
Function calling | Yes | ||||
JSON output | |||||
Fill-In-Middle |
As a precursor to adding vision support, function calling support, fill-in-middle support etc, we need a more granular way to specify model capabilities. The idea is to
- make
gptel-model
a symbol instead of a string, - whose
symbol-plist
will list the capabilities of that model, - which will be consulted when preparing the prompt
This will also require more prompt construction method for the various APIs.
52. SUSPENDED gptel-tasks
TODO Add JSON reply support
53. DONE Perplexity citation support
Instead of writing new response parsers wholesale for Perplexity, it might be possible to piggyback on the OpenAI parsers using cl-call-next-method
. Writing a response parser is significantly more work now that we support tool-use.
54. SUSPENDED Move to overlay tracking
CANCELLED Testing overlay tracking (gh issues/edge cases)
DONE Write some tests to check if promps are generated correctly.
55. SUSPENDED Implement gptel-complete
TODO gptel-complete: reimplement using completions
Using the chat API to generate completions is more or less unworkable. Need to use the completions APIs provided by LLMs instead and see how that fares.
56. SUSPENDED Function calling demos with Anthropic
57. SUSPENDED gptel: stateless design
TODO When gptel properties (like the system message or model) are set for a heading, use them under that heading instead of the buffer-local values.
MAYBE Store model, temperature etc as markdown front matter
- Note taken on
Just going to use file local vars for these for now. I might change my mind if I can find a simple toml parser eventually.
Should they be file-local vars instead? This is easier to handle, I don’t need to write a toml reader.
DONE Store response boundary marker positions as file local vars in markdown
Should it be file-local header line vars?
DONE How do I look up an org property/tag in the AST using org-element
?
Note taken on
(org-entry-get (point) "ID" 'inherit)
(org-entry-put (point) "testprop" "testvalue")
DONE Add prompts from awesome chatgpt prompts
The csv file is at https://github.com/f/awesome-chatgpt-prompts/raw/main/prompts.csv
DONE Add support for context from Org heading
DONE Store model, temperature, etc as top level Org properties
DONE Store response boundary marker positions in an org drawer or property
DONE When gptel-mode
is on, add to the buffer-local before-save-hook
to update these quantities.
58. CANCELLED gptel: more context options [1/2]
[-]
Integration with gptel-complete[X]
Branching conversations in Org
Sending other files/buffers
The options are:
- Send the full buffer
- Specify file(s) to include
- All open project buffers?
59. CANCELLED Add support for older versions of curl (< 1.8)
60. CANCELLED gptel editing prompt: use string-edit
- Note taken on
string-edit is Emacs 29.1+ only.
61. CANCELLED Use the :around cl-defmethod extra param to handle Org
Note taken on
Unfortunately this doesn’t work: there are too many places in the call chain that refer to gptel-backend et al, so it’s not sufficient to add extra defmethods forgptel--parse-buffer
andgptel--request-data
. Some of the other places includegptel-send
andgptel--suffix-send
(for messaging the user), andgptel--url-get-response
andgptel-curl--get-response
.Another problem with this approach: The org properties will have to be scanned for again by each defmethod.
The advice method is probably the best we can do.
In the feature-org branch: Currently the prompt parsing functions in gptel are advised to find the gptel parameters as Org properties if in Org mode. We can do away with this by using the qualifiers provided by cl-defmethod, see elisp#Generic Functions. This does the same thing as advice but is cleaner in the code. Might be harder to debug though.
62. CANCELLED Reset ollama context after changing model
- Note taken on
Switched to the Ollama chat API so there is no longer a context vector.
#279 (Ollama) Old context information being sent after switching to a different model
63. CANCELLED gptel & nixos: Write expression for Emacs 27.1 to test
64. CANCELLED azure backend with multiple models
65. CANCELLED gptel: copilot-style completion UI
This can be an add-on package – gptel-copilot
.
The main challenge is going to be finding a way to include enough context.
C-g
will abort the whole thing.C-=
will launch a diff/ediff.- Doing anything else will accept the completion.
66. CANCELLED Maybe try using the built-in hierarchy library to inspect context?
[BROKEN LINK: No match for fuzzy expression: ;;; hierarchy.el — Library to create and display hierarchical structures -*- lexical-binding: t; -*-]
67. DONE Why is gptel-anthropic’s maxtokens set to 1024 by default?
68. DONE gptel-deepseek parser update required ATTACH
#709 Unable to determine the end of reasoning
I check for the end of the reasoning block like this:
(when-let* ((content (plist-get delta :content)) ((not (eq content :null)))) (if (plist-member delta :reasoning_content) ;Check for reasoning model (plist-put info :reasoning-block t) ;End of streaming reasoning block (plist-put info :reasoning-block 'done)) ;Not using a reasoning model (throw 'done t))
This requires that the response continue to have :reasoning_content
blocks after the reasoning content is done, with the value of the key set to :null
. This is how we check if a reasoning model is being used. However, this is not the case in the attached log, where there is no :reasoning_content
key at all. As a result, :reasoning-block
is never being set to t
.
log around end of reasoning block:
data: {"choices":[{"delta":{"content":"","reasoning_content":".","role":"assistant"},"index":0}],"created":1742127218,"id":"021742127218161a94c19b4b22e17b450210441606b508df776fc","model":"deepseek-r1-250120","service_tier":"default","object":"chat.completion.chunk","usage":null} data: {"choices":[{"delta":{"content":"","reasoning_content":"\n","role":"assistant"},"index":0}],"created":1742127218,"id":"021742127218161a94c19b4b22e17b450210441606b508df776fc","model":"deepseek-r1-250120","service_tier":"default","object":"chat.completion.chunk","usage":null} data: {"choices":[{"delta":{"content":"","role":"assistant"},"index":0}],"created":1742127218,"id":"021742127218161a94c19b4b22e17b450210441606b508df776fc","model":"deepseek-r1-250120","service_tier":"default","object":"chat.completion.chunk","usage":null} data: {"choices":[{"delta":{"content":"\n\n","role":"assistant"},"index":0}],"created":1742127218,"id":"021742127218161a94c19b4b22e17b450210441606b508df776fc","model":"deepseek-r1-250120","service_tier":"default","object":"chat.completion.chunk","usage":null}
69. DONE gptel: Remove support for gemini-pro
70. DONE Add gptel-curl-extra-args to cover global extra curl args
71. DONE Claude error: text content blocks must contain non-whitespace text ATTACH
72. DONE Check out evedel
73. DONE Composable menus for gptel
Other packages can find it very useful to pick and choose components from gptel menus for their own purposes. However there doesn’t seem to be a way to do it.
75. DONE Fix gptel oneshot (non-streaming) Markdown -> Org converter
76. DONE markdown -> org stream converter: handling backticks
Here’s how the backtick parsing works in Markdown:
- A verbatim/src block can begin with any number of backticks.
- If it begins with one backtick
- this backtick is not followed by another backtick.
- The contents of the verbatim block can include any number of backticks.
- The end of the block is signalled by a backtick that is not next to a backtick.
- If it begins with multiple backticks
- Any sequence of fewer backticks is part of the verbatim block
- A sequence of as many backticks ends the verbatim block
So here’s how we set in-src-block:
If we see a backtick, check if the char-after is a backtick
If yes,
- hold on until we see something other than a backtick,
- then set in-src-block. Keep count of the number of backticks.
- If we see this many backticks again, turn off in-src-block.
If no,
- set in-src-block.
- If we see another backtick, wait to make sure that it’s isolated – i.e. not preceeded/followed by a backtick.
- When this is the case, turn off in-src-block
77. DONE gptel: handle headings in the markdown converter
gpt-4 has started using ###
headings in its answers. Convert to Org headings or do something with them.
78. DONE Update Joao Tavora about commits I added
https://github.com/karthink/gptel/issues/184
And the ones I’m not adding and why.
79. DONE gptel: always-available diffing
It would be great to be able to (e)diff the last thing added to the buffer by the LLM. It would be even better to ediff across all previous states, but I’m not sure how to do this – some kind of integration with undo?
For one-step ediffs, here’s the info we need:
- state of the buffer before gptel killed some text, if any.
- state of the buffer after the response.
Other options: hook into after-change-functions – bad idea?
Since gptel’s edits to the buffer are local: (optionally) kills text and adds text at point, we can get away without diffing the whole buffer. (There may be user-driven edits in other parts of the buffer in the meantime that we don’t care about anyway.)
So:
- If killing text: keep the text in a buffer-local var
- In post-response-functions, add (at the end, at position 99), store the text in another buffer-local var. Or it could be a single cons containing the previous string (if any), along with other metadata.
- In the transient menu, we provide an option to (e)diff last LLM response in this buffer. When called… ?
To implement the feature described, the strategy seems plausibly efficient. Here are some potential steps:
- The functionality to (e)diff between two content states can be implemented as a separate function, perhaps
gptel-ediff-last-response()
. - You can make use of two buffer-local variables to keep track of the content state before and after the LLM’s edit. You can name these variables as
gptel-last-pre-edit-content
andgptel-last-post-edit-content
respectively. Be aware to accurately update these variables whenever an edit operation happens. - You would probably want a third buffer-local variable to keep track of the position where the LLM made its last edit (e.g.,
gptel-last-edit-position
). - Additionally, you might want to make these states persistent across emacs sessions for the users. You can use standard emacs lisp constructs like
save-excursion
to preserve point and other state in the process. - In the
gptel-ediff-last-response()
, create two temporary buffers (ordiff
buffers). You can insert the content ofgptel-last-pre-edit-content
into the first buffer, andgptel-last-post-edit-content
into the second buffer. - After storing the content, use
ediff-buffers
or similar functionality to perform the diffing.
It will also be nice to have a mechanism to clear the tracked state, maybe a function gptel-clear-diff-state()
. Also, consider adding a new key to the gptel’s transient menu which can be used to trigger this ediff operation. Users can also bind this function to a keystroke if they use it frequently.
By allowing users to trigger this diff function it would make it easier for them to visually see and understand what the LLM has changed in their content.
Remember to handle error states and edge cases gracefully - for example, what happens if a user tries to ediff when no edit operation has been made by the LLM yet (i.e., the buffer-local variables are null or undefined)? Effective user feedback and good documentation will be key to making this feature successful.
Finally, an additional feature to consider for the future might be to allow users to ediff between any two arbitrary past states, not just the most recent one. For this feature, a history of past states must be maintained. That could be a more significant design and performance endeavor, but it could also be a powerful tool for users.
80. DONE gptel: Add logging
81. DONE gptel-request: the :buffer
and =:position keyword args aren’t clear
- Note taken on
- buffer
- the buffer where the response is inserted
- position
- the position in :buffer= where the response is inserted
It’s not clear what these keyword args do, or even what they should do exactly.
82. DONE gptel: multi-line directive writing
83. DONE gptel: Google gemini support
Documentation: Rest API: https://ai.google.dev/tutorials/rest_quickstart Model parameters: https://ai.google.dev/docs/concepts#model_parameters
84. DONE defgeneric
system for querying other APIs?
- Note taken on
Settled for using cl-structs instead. Might still use defgeneric if the parsing differs considerably between backends.
This will make it easier to add support for
- Azure
- Dall-E
- Local LLMs, etc
85. DONE Stateless design + save state
86. DONE Tag new gptel
release
87. DONE Add algal and d1egoaz to the gptel acknowledgments section
88. DONE gptel error handling for local llms
[BROKEN LINK: No match for fuzzy expression: ;; TODO: Handle this for ollama, see the new code in `gptel-curl–stream-cleanup']
89. DONE gptel: Add kill-ring as source of prompt
90. DONE gptel: make defining openai backends easier
Mainly the :header (lambda () bearer-token-stuff)
needs to be automated, so that it can work with and without a specified key for that backend.
91. DONE Reply to ahyatt’s proposal for llm actions
92. DONE file-local-var storage with newlines: reply to Eli
93. DONE LLM UI experiments – reply to Andrew
94. DONE gptel: post-response hook’s beginning position is wrong
It includes the response prefix.
95. DONE gptel-anthropic parser losing text ATTACH
96. DONE Reply to Jan Rychter’s email
Email from Jan Rychter: Re: [karthink/gptel] Separate system prompts and directives (Issue #249)
How is gptel more complex than the web UI?
97. DONE Make gptel bugfix release
98. DONE gptel: Add a non-chat boolean for regular buffers
See this issue.
99. DONE gptel: context indicator in header-line
100. DONE Reply to tarsius
101. DONE Further fix gptel org formatter ATTACH
102. DONE Fontification malfunction when streaming responses
103. DONE gptel-gemini parser: :parts
can have multiple :text
blocks
[BROKEN LINK: No match for fuzzy expression: :parts 0 :text)))]
104. DONE purpose-mode interferes with gptel’s display
105. DONE Improve docs around :key
Thank you, didnt know that. Mind you add this as an example to the docs, please? The docs are a bit misleading as it says:
The :key can be a function that returns the key (more secure).
So i was thinking i had to develop my own function.
Footnotes:
Org-async wouldn’t work anyway since running down the process tree depends on more than just the success/failure of the previous process.