Please update gptel first -- errors are often fixed by the time they're reported.
Bug Description
First of all, great tool, thank you. Lots of potential. Now the issue.
One would think that non-async tools will be evaluated in the order the LLM requests them. But they are not.
This leads to catastrophic results if the tools mutate something, be it Emacs state or filesystem. For me, it was when I had a tool for "dump particular text into a new emacs buffer with particular name" and a tool for "show a particular buffer to the user" (for presentation of time-consuming tasks). The LLM would correctly sequence the 2 tool calls in one "turn" but the second call would consistently fail with buffer allegedly not existing. I was quite confused because the buffer was indeed created and the arguments used by the LLM were indeed correct. And the second tool would consistently succeed if called in a second conversation "turn".
After making a minimum viable test case (see the test case section), the underlying cause (see the title of this report) was clear.
Is this behavior intended? I thought, maybe it's an intended limitation because the LLM can mix async and sync tools and it would be difficult to guarantee an ordering, so it's just evaluated in a YOLO manner. But I find it weird that the calls are not merely out of order, they are so reliably and in a particular way - always exactly reverse. I couldn't find any mention of this surprising behavior in the manual.
I don't have the elisp skills necessary to understand this codebase to see why this is happening.
Backend
Other (please specify in Additional Context)
Steps to Reproduce
Tools:
(defvar counter 0)
(gptel-make-tool
:name "tool-x"
:description "Use this when the user requests tool-x"
:category "core"
:async nil
:function
(lambda ()
(setq counter (+ counter 1))
(message "X %0.6f %d" (float-time) counter)))
(gptel-make-tool
:name "tool-y"
:description "Use this when the user requests tool-y."
:category "core"
:async nil
:function
(lambda ()
(setq counter (+ counter 1))
(message "Y %0.6f %d" (float-time) counter)))
(gptel-make-tool
:name "tool-z"
:description "Use this when the user requests tool-z."
:category "core"
:async nil
:function
(lambda ()
(setq counter (+ counter 1))
(message "Z %0.6f %d" (float-time) counter)))
LLM chat:
### use tool-x, tool-y and tool-z
``` tool (tool-x nil)
(:name "tool-x" :args nil)
X 1784533301.935472 3
```
``` tool (tool-y nil)
(:name "tool-y" :args nil)
Y 1784533301.935415 2
```
``` tool (tool-z nil)
(:name "tool-z" :args nil)
Z 1784533301.935281 1
```
As can be seen from both the timestamps and the counter variable printout, the tools are requested by the LLM in order X-Y-Z but are evaluated as Z-Y-X.
Additional Context
Emacs: 30.2
OS: NixOS unstable
gptel: commit 8701e2b, per gptel-pkg.el
backend: llama.cpp with qwen3
Backtrace
Log Information
Please update gptel first -- errors are often fixed by the time they're reported.
Bug Description
First of all, great tool, thank you. Lots of potential. Now the issue.
One would think that non-async tools will be evaluated in the order the LLM requests them. But they are not.
This leads to catastrophic results if the tools mutate something, be it Emacs state or filesystem. For me, it was when I had a tool for "dump particular text into a new emacs buffer with particular name" and a tool for "show a particular buffer to the user" (for presentation of time-consuming tasks). The LLM would correctly sequence the 2 tool calls in one "turn" but the second call would consistently fail with buffer allegedly not existing. I was quite confused because the buffer was indeed created and the arguments used by the LLM were indeed correct. And the second tool would consistently succeed if called in a second conversation "turn".
After making a minimum viable test case (see the test case section), the underlying cause (see the title of this report) was clear.
Is this behavior intended? I thought, maybe it's an intended limitation because the LLM can mix async and sync tools and it would be difficult to guarantee an ordering, so it's just evaluated in a YOLO manner. But I find it weird that the calls are not merely out of order, they are so reliably and in a particular way - always exactly reverse. I couldn't find any mention of this surprising behavior in the manual.
I don't have the elisp skills necessary to understand this codebase to see why this is happening.
Backend
Other (please specify in Additional Context)
Steps to Reproduce
Tools:
LLM chat:
As can be seen from both the timestamps and the counter variable printout, the tools are requested by the LLM in order X-Y-Z but are evaluated as Z-Y-X.
Additional Context
Emacs: 30.2
OS: NixOS unstable
gptel: commit 8701e2b, per gptel-pkg.el
backend: llama.cpp with qwen3
Backtrace
Log Information