gptel icon indicating copy to clipboard operation
gptel copied to clipboard

gptel having issue correctly parsing array data when calling tools.

Open beacoder opened this issue 5 months ago • 13 comments

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


beacoder avatar Jun 23 '25 14:06 beacoder

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 ?

beacoder avatar Jun 25 '25 07:06 beacoder

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:

Image

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?

karthink avatar Jun 26 '25 07:06 karthink

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.

Image

beacoder avatar Jun 27 '25 07:06 beacoder

  1. Turn on tool call confirmation, with (setq gptel-confirm-tool-calls t).
  2. Make a request that produces a tool call. You'll get a confirmation prompt
  3. 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).
  4. The tool call should show up with the key :tool-use in the resulting diagnostic buffer.

Provide the contents of that line here.

karthink avatar Jun 29 '25 02:06 karthink

Image 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. Image is it because the params seems missing second new_string, which leads to paras parsing failure?

beacoder avatar Jul 02 '25 04:07 beacoder

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*.

karthink avatar Jul 03 '25 19:07 karthink

I've pasted the logs in this link, https://gist.github.com/beacoder/170517b00cafac998d03d109f29ac728 can you access this log ?

beacoder avatar Jul 05 '25 04:07 beacoder

Hi @karthink, any update?

beacoder avatar Jul 11 '25 02:07 beacoder

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.

karthink avatar Jul 11 '25 19:07 karthink

Hi,

Image Image

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.

beacoder avatar Jul 12 '25 07:07 beacoder

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.

karthink avatar Jul 13 '25 07:07 karthink

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

beacoder avatar Jul 14 '25 02:07 beacoder

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.

jweaver avatar Jul 19 '25 16:07 jweaver