gptel: Make gptel-post-response-* easier to use

* gptel.el (gptel-end-of-response, gptel-post-response-hook,
gptel-post-response-functions, gptel--insert-response,
gptel-response-filter-functions):
Rename gptel-post-response-hook -> gptel-post-response-functions
The new abnormal hook now calls its functions with the start and
end positions of the response, to make it easier to act on the
response.

* gptel-curl.el (gptel-curl--stream-cleanup): Corresponding changes.

* README.org: Mention breaking change.
This commit is contained in:
Karthik Chikmagalur 2024-01-12 20:53:40 -08:00
parent d6ef79f621
commit 612aea3456
3 changed files with 46 additions and 26 deletions

View file

@ -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: 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 #+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 #+end_src
You can also call =gptel-end-of-response= as a command at any time. 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 ** 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=. - 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. - 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.

View file

@ -67,11 +67,11 @@ PROMPTS is the data to send, TOKEN is a unique identifier."
(list (format "-d%s" data)) (list (format "-d%s" data))
(letrec (letrec
((temp-filename (make-temp-file "gptel-curl-data" nil ".json" data)) ((temp-filename (make-temp-file "gptel-curl-data" nil ".json" data))
(cleanup-fn (lambda () (cleanup-fn (lambda (&rest _)
(when (file-exists-p temp-filename) (when (file-exists-p temp-filename)
(delete-file temp-filename) (delete-file temp-filename)
(remove-hook 'gptel-post-response-hook cleanup-fn))))) (remove-hook 'gptel-post-response-functions cleanup-fn)))))
(add-hook 'gptel-post-response-hook cleanup-fn) (add-hook 'gptel-post-response-functions cleanup-fn)
(list "--data-binary" (list "--data-binary"
(format "@%s" temp-filename)))) (format "@%s" temp-filename))))
(when (not (string-empty-p gptel-proxy)) (when (not (string-empty-p gptel-proxy))
@ -177,12 +177,15 @@ PROCESS and _STATUS are process parameters."
(tracking-marker (plist-get info :tracking-marker)) (tracking-marker (plist-get info :tracking-marker))
(start-marker (plist-get info :position)) (start-marker (plist-get info :position))
(http-status (plist-get info :http-status)) (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") (if (equal http-status "200")
(progn (progn
;; Finish handling response ;; Finish handling response
(with-current-buffer (marker-buffer start-marker) (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) (when gptel-mode (save-excursion (goto-char tracking-marker)
(insert "\n\n" (gptel-prompt-prefix-string))))) (insert "\n\n" (gptel-prompt-prefix-string)))))
(with-current-buffer gptel-buffer (with-current-buffer gptel-buffer
@ -214,7 +217,7 @@ PROCESS and _STATUS are process parameters."
(gptel--update-status (gptel--update-status
(format " Response Error: %s" http-msg) 'error)))) (format " Response Error: %s" http-msg) 'error))))
(with-current-buffer gptel-buffer (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) (setf (alist-get process gptel-curl--process-alist nil 'remove) nil)
(kill-buffer proc-buf))) (kill-buffer proc-buf)))

View file

@ -193,13 +193,17 @@ if the command-line argument size is limited by the operating system."
'(gptel--convert-org) '(gptel--convert-org)
"Abnormal hook for transforming the response from ChatGPT. "Abnormal hook for transforming the response from ChatGPT.
This is useful if you want to format the response in some way, This is used to format the response in some way, such as filling
such as filling paragraphs, adding annotations or recording paragraphs, adding annotations or recording information in the
information in the response like links. response like links.
Each function in this hook receives two arguments, the response Each function in this hook receives two arguments, the response
string to transform and the ChatGPT interaction buffer. It 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 :group 'gptel
:type 'hook) :type 'hook)
@ -211,12 +215,21 @@ to ChatGPT. Note: this hook only runs if the request succeeds."
:group 'gptel :group 'gptel
:type 'hook) :type 'hook)
(defcustom gptel-post-response-hook nil (define-obsolete-variable-alias
"Hook run after inserting the LLM response into the current buffer. '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 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: to the LLM, and after the full response has been inserted. Each
this hook runs even if the request fails." function is called with two arguments: the response beginning and
end positions.
Note: this hook runs even if the request fails."
:group 'gptel :group 'gptel
:type 'hook) :type 'hook)
@ -497,9 +510,9 @@ Note: This will move the cursor."
(scroll-up-command)) (scroll-up-command))
(error nil)))) (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." "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)) (dotimes (_ (if arg (abs arg) 1))
(text-property-search-forward 'gptel 'response t) (text-property-search-forward 'gptel 'response t)
(when (looking-at (concat "\n\\{1,2\\}" (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." See `gptel--url-get-response' for details."
(let* ((status-str (plist-get info :status)) (let* ((status-str (plist-get info :status))
(gptel-buffer (plist-get info :buffer)) (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 ;; Handle read-only buffers
(when (with-current-buffer gptel-buffer (when (with-current-buffer gptel-buffer
(or buffer-read-only (or buffer-read-only
@ -890,16 +904,17 @@ See `gptel--url-get-response' for details."
(insert "\n\n") (insert "\n\n")
(when gptel-mode (when gptel-mode
(insert (gptel-response-prefix-string)))) (insert (gptel-response-prefix-string))))
(let ((p (point))) (setq response-beg (point)) ;Save response start position
(insert response) (insert response)
(pulse-momentary-highlight-region p (point))) (setq response-end (point))
(when gptel-mode (insert "\n\n" (gptel-prompt-prefix-string)))) (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)))) (when gptel-mode (gptel--update-status " Ready" 'success))))
(gptel--update-status (gptel--update-status
(format " Response Error: %s" status-str) 'error) (format " Response Error: %s" status-str) 'error)
(message "ChatGPT response error: (%s) %s" (message "ChatGPT response error: (%s) %s"
status-str (plist-get info :error))) 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 () (defun gptel-set-topic ()
"Set a topic and limit this conversation to the current heading. "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*")) (temp-buf (generate-new-buffer-name "*gptel-temp*"))
(start-pt (make-marker)) (start-pt (make-marker))
(cleanup-fn (cleanup-fn
(lambda () (lambda (&rest _)
(when (buffer-live-p (get-buffer temp-buf)) (when (buffer-live-p (get-buffer temp-buf))
(set-marker start-pt nil) (set-marker start-pt nil)
(kill-buffer temp-buf)) (kill-buffer temp-buf))
(remove-hook 'gptel-post-response-hook cleanup-fn)))) (remove-hook 'gptel-post-response-functions cleanup-fn))))
(add-hook 'gptel-post-response-hook cleanup-fn) (add-hook 'gptel-post-response-functions cleanup-fn)
(lambda (str) (lambda (str)
(let ((noop-p)) (let ((noop-p))
(with-current-buffer (get-buffer-create temp-buf) (with-current-buffer (get-buffer-create temp-buf)