diff --git a/README.org b/README.org index 2d34371..1107379 100644 --- a/README.org +++ b/README.org @@ -319,7 +319,7 @@ To be minimally annoying, GPTel does not move the cursor by default. Add the fo To be minimally annoying, GPTel does not move the cursor by default. Add the following to your configuration to move the cursor: #+begin_src emacs-lisp -(add-hook 'gptel-post-response-hook 'gptel-end-of-response) +(add-hook 'gptel-post-response-functions 'gptel-end-of-response) #+end_src You can also call =gptel-end-of-response= as a command at any time. @@ -420,6 +420,8 @@ There are several more: [[https://github.com/CarlQLange/chatgpt-arcana.el][chatg ** Breaking Changes +- =gptel-post-response-hook= has been renamed to =gptel-post-response-functions=, and functions in this hook are now called with two arguments: the start and end buffer positions of the response. This should make it easy to act on the response text without having to locate it first. + - Possible breakage, see #120: If streaming responses stop working for you after upgrading to v0.5, try reinstalling gptel and deleting its native comp eln cache in =native-comp-eln-load-path=. - The user option =gptel-host= is deprecated. If the defaults don't work for you, use =gptel-make-openai= (which see) to customize server settings. diff --git a/gptel-curl.el b/gptel-curl.el index 4f624f9..e290787 100644 --- a/gptel-curl.el +++ b/gptel-curl.el @@ -67,11 +67,11 @@ PROMPTS is the data to send, TOKEN is a unique identifier." (list (format "-d%s" data)) (letrec ((temp-filename (make-temp-file "gptel-curl-data" nil ".json" data)) - (cleanup-fn (lambda () + (cleanup-fn (lambda (&rest _) (when (file-exists-p temp-filename) (delete-file temp-filename) - (remove-hook 'gptel-post-response-hook cleanup-fn))))) - (add-hook 'gptel-post-response-hook cleanup-fn) + (remove-hook 'gptel-post-response-functions cleanup-fn))))) + (add-hook 'gptel-post-response-functions cleanup-fn) (list "--data-binary" (format "@%s" temp-filename)))) (when (not (string-empty-p gptel-proxy)) @@ -177,12 +177,15 @@ PROCESS and _STATUS are process parameters." (tracking-marker (plist-get info :tracking-marker)) (start-marker (plist-get info :position)) (http-status (plist-get info :http-status)) - (http-msg (plist-get info :status))) + (http-msg (plist-get info :status)) + response-beg response-end) (if (equal http-status "200") (progn ;; Finish handling response (with-current-buffer (marker-buffer start-marker) - (pulse-momentary-highlight-region (+ start-marker 2) tracking-marker) + (setq response-beg (+ start-marker 2) + response-end (marker-position tracking-marker)) + (pulse-momentary-highlight-region response-beg tracking-marker) (when gptel-mode (save-excursion (goto-char tracking-marker) (insert "\n\n" (gptel-prompt-prefix-string))))) (with-current-buffer gptel-buffer @@ -214,7 +217,7 @@ PROCESS and _STATUS are process parameters." (gptel--update-status (format " Response Error: %s" http-msg) 'error)))) (with-current-buffer gptel-buffer - (run-hooks 'gptel-post-response-hook))) + (run-hook-with-args 'gptel-post-response-functions response-beg response-end))) (setf (alist-get process gptel-curl--process-alist nil 'remove) nil) (kill-buffer proc-buf))) diff --git a/gptel.el b/gptel.el index 8d9bae7..b97a523 100644 --- a/gptel.el +++ b/gptel.el @@ -193,13 +193,17 @@ if the command-line argument size is limited by the operating system." '(gptel--convert-org) "Abnormal hook for transforming the response from ChatGPT. -This is useful if you want to format the response in some way, -such as filling paragraphs, adding annotations or recording -information in the response like links. +This is used to format the response in some way, such as filling +paragraphs, adding annotations or recording information in the +response like links. Each function in this hook receives two arguments, the response string to transform and the ChatGPT interaction buffer. It -should return the transformed string." +should return the transformed string. + +NOTE: This is only used for non-streaming responses. To +transform streaming responses, use `gptel-post-stream-hook' and +`gptel-post-response-functions'." :group 'gptel :type 'hook) @@ -211,12 +215,21 @@ to ChatGPT. Note: this hook only runs if the request succeeds." :group 'gptel :type 'hook) -(defcustom gptel-post-response-hook nil - "Hook run after inserting the LLM response into the current buffer. +(define-obsolete-variable-alias + 'gptel-post-response-hook 'gptel-post-response-functions + "0.6.0" + "Post-response functions are now called with two arguments: the +start and end buffer positions of the response.") + +(defcustom gptel-post-response-functions nil + "Abnormal hook run after inserting the LLM response into the current buffer. This hook is called in the buffer from which the prompt was sent -to the LLM, and after the full response has been inserted. Note: -this hook runs even if the request fails." +to the LLM, and after the full response has been inserted. Each +function is called with two arguments: the response beginning and +end positions. + +Note: this hook runs even if the request fails." :group 'gptel :type 'hook) @@ -497,9 +510,9 @@ Note: This will move the cursor." (scroll-up-command)) (error nil)))) -(defun gptel-end-of-response (&optional arg) +(defun gptel-end-of-response (_ _ &optional arg) "Move point to the end of the LLM response ARG times." - (interactive "p") + (interactive (list nil nil current-prefix-arg)) (dotimes (_ (if arg (abs arg) 1)) (text-property-search-forward 'gptel 'response t) (when (looking-at (concat "\n\\{1,2\\}" @@ -859,7 +872,8 @@ INFO is a plist containing information relevant to this buffer. See `gptel--url-get-response' for details." (let* ((status-str (plist-get info :status)) (gptel-buffer (plist-get info :buffer)) - (start-marker (plist-get info :position))) + (start-marker (plist-get info :position)) + response-beg response-end) ;; Handle read-only buffers (when (with-current-buffer gptel-buffer (or buffer-read-only @@ -890,16 +904,17 @@ See `gptel--url-get-response' for details." (insert "\n\n") (when gptel-mode (insert (gptel-response-prefix-string)))) - (let ((p (point))) - (insert response) - (pulse-momentary-highlight-region p (point))) - (when gptel-mode (insert "\n\n" (gptel-prompt-prefix-string)))) + (setq response-beg (point)) ;Save response start position + (insert response) + (setq response-end (point)) + (pulse-momentary-highlight-region response-beg response-end) + (when gptel-mode (insert "\n\n" (gptel-prompt-prefix-string)))) ;Save response end position (when gptel-mode (gptel--update-status " Ready" 'success)))) (gptel--update-status (format " Response Error: %s" status-str) 'error) (message "ChatGPT response error: (%s) %s" status-str (plist-get info :error))) - (run-hooks 'gptel-post-response-hook)))) + (run-hook-with-args 'gptel-post-response-functions response-beg response-end)))) (defun gptel-set-topic () "Set a topic and limit this conversation to the current heading. @@ -1192,12 +1207,12 @@ text stream." (temp-buf (generate-new-buffer-name "*gptel-temp*")) (start-pt (make-marker)) (cleanup-fn - (lambda () + (lambda (&rest _) (when (buffer-live-p (get-buffer temp-buf)) (set-marker start-pt nil) (kill-buffer temp-buf)) - (remove-hook 'gptel-post-response-hook cleanup-fn)))) - (add-hook 'gptel-post-response-hook cleanup-fn) + (remove-hook 'gptel-post-response-functions cleanup-fn)))) + (add-hook 'gptel-post-response-functions cleanup-fn) (lambda (str) (let ((noop-p)) (with-current-buffer (get-buffer-create temp-buf)