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:
#+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.

View file

@ -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)))

View file

@ -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)))
(setq response-beg (point)) ;Save response start position
(insert response)
(pulse-momentary-highlight-region p (point)))
(when gptel-mode (insert "\n\n" (gptel-prompt-prefix-string))))
(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)