gptel
gptel copied to clipboard
gptel having issue correctly parsing array data when calling tools.
Please update gptel first -- errors are often fixed by the time they're reported.
- [x] I have updated gptel to the latest commit and tested that the issue still exists
Bug Description
I have defined an edit_file tool as below, most of the time, when I try to use gptel to edit file. when the tool got invoked, the args are (nil nil).
(gptel-make-tool
:function #'my-gptel--edit_file
:name "edit_file"
:description "Edit file with a list of edits, each edit contains a line-number,
a old-string and a new-string, new-string will replace the old-string at the specified line."
:args (list '(:name "file-path"
:type string
:description "The full path of the file to edit")
'(:name "file-edits"
:type array
:items (:type object
:properties
(:line_number
(:type integer :description "The line number of the file where edit starts.")
:old_string
(:type string :description "The old-string to be replaced.")
:new_string
(:type string :description "The new-string to replace old-string.")))
:description "The list of edits to apply on the file"))
:category "filesystem")
(defun my-gptel--edit_file (file-path file-edits)
"In FILE-PATH, apply FILE-EDITS with pattern matching and replacing."
(if (and file-path (not (string= file-path "")) file-edits)
(with-current-buffer (get-buffer-create "*edit-file*")
(insert-file-contents (expand-file-name file-path))
(let ((inhibit-read-only t)
(case-fold-search nil)
(file-name (expand-file-name file-path))
(edit-success nil))
;; apply changes
(dolist (file-edit (seq-into file-edits 'list))
(when-let ((line-number (plist-get file-edit :line_number))
(old-string (plist-get file-edit :old_string))
(new-string (plist-get file-edit :new_string))
(is-valid-old-string (not (string= old-string ""))))
(goto-char (point-min))
(forward-line (1- line-number))
(when (search-forward old-string nil t)
(replace-match new-string t t)
(setq edit-success t))))
;; return result to gptel
(if edit-success
(progn
;; show diffs
(ediff-buffers (find-file-noselect file-name) (current-buffer))
(format "Successfully edited %s" file-name))
(format "Failed to edited %s" file-name))))
(format "Failed to edited %s" file-path)))
Backend
None
Steps to Reproduce
define the tool use the code as I provided. then use gptel to select the edit_file and read_file as well. then use prompt to ask gptel to make some modification to any file. then edit_file tool will be called, and you will see most of the time, the args will be (nil nil).
Additional Context
GNU Emacs 30.1 (build 1, x86_64-pc-linux-gnu, X toolkit, Xaw scroll bars) of 2025-03-03
(defun my-gptel--read_file(filepath)
(with-temp-message (format "Reading file: %s" filepath)
(with-temp-buffer
(insert-file-contents (expand-file-name filepath))
(buffer-string))))
(gptel-make-tool
:function #'my-gptel--read_file
:name "read_file"
:description "Read and display the contents of a file."
:args (list '(:name "filepath"
:type string
:description "Path to the file to read. Supports relative paths and ~."))
:category "filesystem")
Backtrace
Log Information
from the gptel-log buffer, I can see the param is not nil, but when tool got invoked, the param became nil. what's the function which is suiteable for debugging this issue ?
I wasn't able to reproduce it. I have gptel-confirm-tool-calls set to t, so that a confirmation prompt appears in the buffer. The params received are indeed not nil:
When I ran the tool, it opened up an Ediff session where I was able to make the replacements. It worked as expected.
Which backend/model were you using?
I'm using company's internal qwen3-32B model. I could see the params in gptel-log, but as you can see "Failed to edited nil", which means param parsed is nil instead of the correct one.
- Turn on tool call confirmation, with
(setq gptel-confirm-tool-calls t). - Make a request that produces a tool call. You'll get a confirmation prompt
- Inspect the tool call with
C-c C-i. (In a chat buffer you can also click on the gptel status in the header line). - The tool call should show up with the key
:tool-usein the resulting diagnostic buffer.
Provide the contents of that line here.
above is the snapshot when I hit C-c C-i before calling the tool.
below is the snapshot when I hit C-c C-c to continue tool execution.
is it because the params seems missing second new_string, which leads to paras parsing failure?
is it because the params seems missing second new_string, which leads to paras parsing failure?
"arguments" might be invalid JSON returned by the API, in which case there's not much I can do.
It will help to look at the log.
Can you turn on logging, (setq gptel-log-level 'info), and paste the contents of the response here? It will be in a buffer named *gptel-log*.
I've pasted the logs in this link, https://gist.github.com/beacoder/170517b00cafac998d03d109f29ac728 can you access this log ?
Hi @karthink, any update?
Looking at line 1061, the tool call argument seems to have been captured correctly on the previous turn: https://gist.github.com/beacoder/170517b00cafac998d03d109f29ac728#file-gistfile1-txt-L1061
So I'm not sure what's going on. At the end of the gist you have some error that's unrelated to tool calls, as far as I can tell.
Hi,
My question is why in the logs it appeared that the param returned from llm are correct, but when my tool funcion (edit_file) got called, the param is nil instead of the correct param in the logs.
thanks.
Do you have this issue when you use non-streaming responses? (setq gptel-stream nil)
If yes it will be much easier to figure out the reason.
strange, after I use the non-streaming with (setq gptel-stream nil), it seems worked. here is the gptel-log https://gist.github.com/beacoder/d47fdfc36838d4447fb54123c89796da
I've found not all backends support streaming and tool use, even if the model says it supports it. There's quite a lot of chatter out there about some backend providers (eg Ollama) not supporting streaming for models that do support streaming (like deepseek). Qwen3 may also be one of those, I dunno. There's threads/talk around all of this.