branch: elpa/gptel
commit 566a1fb27704148abe77db2f62ad1e5f62ba7262
Author: Karthik Chikmagalur <[email protected]>
Commit: Karthik Chikmagalur <[email protected]>

    gptel-rewrite: Handle tool calls in gptel-rewrite
    
    * gptel-rewrite.el (gptel--rewrite-directive-default): Add
    additional instruction about not producing intermediate text.
    
    (gptel--rewrite-callback): Handle tool calls in the middle of a
    gptel-rewrite command.  The most common use for this is to include
    a file/buffer-read tool to allow the LLM to fetch more context
    before doing the rewrite.
    
    The way this works is the following:
    
    1. Tool calls requiring confirmation are displayed the usual way,
    using the in-buffer overlays or the minibuffer if the buffer is
    read-only.
    
    2. Tool call results are ignored.
    
    3. Text is inserted into the temp buffer as it is received, but
    every time there is a tool call, all the text up to that point is
    deleted.  Only the text generated after the final tool call is
    retained in the overlay.
    
    (gptel--rewrite-handlers): Dedicated FSM handler table for
    gptel-rewrite.  We mainly need this for `gptel--update-tool-call',
    which records the request in progress into `gptel--fsm-last'.  In
    turn, this is needed to be able to call sub-agents or more
    specialized tools.
    
    (gptel--suffix-rewrite): Use the new handlers.
---
 gptel-rewrite.el | 33 ++++++++++++++++++++++++++-------
 1 file changed, 26 insertions(+), 7 deletions(-)

diff --git a/gptel-rewrite.el b/gptel-rewrite.el
index a7c2bc0004f..acd08b24193 100644
--- a/gptel-rewrite.el
+++ b/gptel-rewrite.el
@@ -161,6 +161,7 @@ which see."
                             "- Generate ONLY %s code as output, without "
                             "any explanation or markdown code fences.\n"
                             "- Generate code in full, do not abbreviate or 
omit code.\n"
+                            "- Do not produce intermediate text or report on 
your progress.\n"
                             "- Do not ask for further clarification, and make "
                             "any assumptions you need to follow instructions.")
                     article lang lang lang)
@@ -169,9 +170,15 @@ which see."
                "You are an editor."
              (format "You are %s %s editor." article lang))
            "  Follow my instructions and improve or rewrite the text I 
provide."
+           "  Do not produce intermediate text or report on your progress."
            "  Generate ONLY the replacement text,"
            " without any explanation or markdown code fences.")))))
 
+(defvar gptel--rewrite-handlers
+  `((WAIT ,#'gptel--handle-wait)
+    (TOOL ,#'gptel--update-tool-call ,#'gptel--handle-tool-use))
+  "Alist specifying FSM handlers for `gptel-rewrite' state transitions.")
+
 ;; * Helper functions
 
 (defun gptel--rewrite-key-help (callback)
@@ -481,8 +488,8 @@ INFO is the async communication channel for the rewrite 
request."
               (proc-buf (cdr ov-and-buf))
               (buf (overlay-buffer ov)))
     (cond
-     ((stringp response)                ;partial or fully successful result
-      (with-current-buffer proc-buf     ;auxiliary buffer, insert text here 
and copy to overlay
+     ((stringp response)            ;partial or fully successful result
+      (with-current-buffer proc-buf ;auxiliary buffer, insert text here and 
copy to overlay
         (let ((inhibit-modification-hooks nil)
               (inhibit-read-only t))
           (when (= (buffer-size) 0)
@@ -499,17 +506,28 @@ INFO is the async communication channel for the rewrite 
request."
           (font-lock-ensure)
           (overlay-put ov 'display (buffer-string))))
       (unless (plist-get info :stream) (gptel--rewrite-callback t info)))
+
      ((eq response 'abort)              ;request aborted
       (when-let* ((proc-buf (cdr-safe (plist-get info :context))))
         (kill-buffer proc-buf))
       (delete-overlay ov))
+
+     ((eq (car-safe response) 'tool-call) ;tool call confirmation
+      (gptel--display-tool-calls          ;use minibuffer if buffer is 
read-only
+       (cdr response) info (buffer-local-value 'buffer-read-only buf)))
+
      ((null response)                   ;finished with error
       (message (concat "LLM response error: %s. Rewrite in buffer %s 
canceled.")
                (plist-get info :status) (plist-get info :buffer))
       (gptel--rewrite-callback 'abort info))
-     ((consp response)) ;reasoning or tool calls -- don't care and not 
implemented, respectively
-     (t (let ((proc-buf (cdr-safe (plist-get info :context))) ;finished 
successfully
-              (mkb (propertize "<mouse-1>" 'face 'help-key-binding)))
+
+     ((consp response))             ;reasoning or tool call result -- don't 
care
+
+     (t
+      (if (plist-get info :tool-use)    ;stopped to use tools
+          ;; Clear text inserted so far
+          (with-current-buffer proc-buf (delete-region (point-min) (point)))
+        (let ((mkb (propertize "<mouse-1>" 'face 'help-key-binding))) ;or 
finished successfully
           (with-current-buffer proc-buf
             (let ((inhibit-read-only t))
               (delete-region (point) (point-max))
@@ -549,7 +567,7 @@ INFO is the async communication channel for the rewrite 
request."
                         (unless (eq (current-buffer) buf)
                           (format " in buffer %s " (buffer-name buf)))
                         (concat " ready: " mkb ", " (propertize "RET" 'face 
'help-key-binding)
-                                " or " (substitute-command-keys 
"\\[gptel-rewrite] to continue.")))))))))))
+                                " or " (substitute-command-keys 
"\\[gptel-rewrite] to continue."))))))))))))
 
 ;; * Transient Prefixes for rewriting
 
@@ -702,7 +720,7 @@ generated from functions."
           (and gptel-use-context (if nosystem 'user 'system)))
          (prompt (list (or (get-char-property (point) 'gptel-rewrite)
                            (buffer-substring-no-properties (region-beginning) 
(region-end)))
-                       "What is the required change?"
+                       "What is the required change?  I will generate only the 
final replacement."
                        (or rewrite-message gptel--rewrite-message))))
     (when nosystem
       (setcar prompt (concat (car-safe (gptel--parse-directive
@@ -720,6 +738,7 @@ generated from functions."
                ;; NOTE: Switch to `generate-new-buffer' after we drop Emacs 
27.1 (#724)
                (cons ov (gptel--temp-buffer " *gptel-rewrite*")))
              :transforms gptel-prompt-transform-functions
+             :fsm (gptel-make-fsm :handlers gptel--rewrite-handlers)
              :callback #'gptel--rewrite-callback)
       ;; Move back so that the cursor is on the overlay when done.
       (unless (get-char-property (point) 'gptel-rewrite)

Reply via email to