gptel: org support for streaming WIP
* gptel.el (gptel--convert-playback-markdown->org): New converter for markdown->org that works on text chunks while maintaining the parse state until the text stream is finished. * gptel-curl.el (gptel--insert-response-stream, gptel-curl-get-response): When using `gptel-playback' and requesting ChatGPT's responses in org-mode, run the above converter on the received response. This works by storing the converter and associated state as a closure in the async info plist that is supplied along with the response, and running it repeatedly on each chunk of text in the response stream before it is inserted into the buffer. FIXME: Note that `gptel-response-filter-functions' is currently ignored if using `gptel-stream'.
This commit is contained in:
parent
d5ad620555
commit
c9795fe9e8
2 changed files with 66 additions and 4 deletions
|
@ -59,6 +59,8 @@ PROMPTS is the data to send, TOKEN is a unique identifier."
|
|||
(push (format "-d%s" data) args)
|
||||
(nreverse (cons url args))))
|
||||
|
||||
;;TODO: The :transformer argument here is an alternate implementation of
|
||||
;;`gptel-response-filter-functions'. The two need to be unified.
|
||||
;;;###autoload
|
||||
(defun gptel-curl-get-response (info &optional callback)
|
||||
"Retrieve response to prompt in INFO.
|
||||
|
@ -83,7 +85,13 @@ the response is inserted into the current buffer after point."
|
|||
:callback (or callback
|
||||
(if gptel-playback
|
||||
#'gptel--insert-response-stream
|
||||
#'gptel--insert-response)))
|
||||
#'gptel--insert-response))
|
||||
:transformer (when (or (eq gptel-default-mode 'org-mode)
|
||||
(eq (buffer-local-value
|
||||
'major-mode
|
||||
(plist-get info :gptel-buffer))
|
||||
'org-mode))
|
||||
(gptel--convert-playback-markdown->org)))
|
||||
info))
|
||||
(if gptel-playback
|
||||
(progn (set-process-sentinel process #'gptel-curl--cleanup-stream)
|
||||
|
@ -122,7 +130,8 @@ See `gptel--url-get-response' for details."
|
|||
(status-str (plist-get response :status))
|
||||
(gptel-buffer (plist-get info :gptel-buffer))
|
||||
(start-marker (plist-get info :insert-marker))
|
||||
(tracking-marker (plist-get info :tracking-marker)))
|
||||
(tracking-marker (plist-get info :tracking-marker))
|
||||
(transformer (plist-get info :transformer)))
|
||||
(if content-str
|
||||
(with-current-buffer gptel-buffer
|
||||
(save-excursion
|
||||
|
@ -134,8 +143,9 @@ See `gptel--url-get-response' for details."
|
|||
(set-marker-insertion-type tracking-marker t)
|
||||
(plist-put info :tracking-marker tracking-marker))
|
||||
|
||||
(setq content-str (gptel--transform-response
|
||||
content-str gptel-buffer))
|
||||
(when transformer
|
||||
(setq content-str (funcall transformer content-str)))
|
||||
|
||||
(put-text-property 0 (length content-str) 'gptel 'response content-str)
|
||||
(goto-char tracking-marker)
|
||||
(insert content-str)))
|
||||
|
|
52
gptel.el
52
gptel.el
|
@ -550,6 +550,58 @@ elements."
|
|||
(insert "/")))))))
|
||||
(buffer-string)))
|
||||
|
||||
(defun gptel--convert-playback-markdown->org ()
|
||||
""
|
||||
(let ((in-src-block)
|
||||
(temp-buf (generate-new-buffer-name "*gptel-temp*"))
|
||||
(start-pt (make-marker)))
|
||||
(lambda (str)
|
||||
(let ((noop-p))
|
||||
(with-current-buffer (get-buffer-create temp-buf)
|
||||
(save-excursion (goto-char (point-max))
|
||||
(insert str))
|
||||
(when (marker-position start-pt) (goto-char start-pt))
|
||||
(save-excursion
|
||||
(while (re-search-forward "`\\|\\*\\{1,2\\}\\|_" nil t)
|
||||
(pcase (match-string 0)
|
||||
("`"
|
||||
(cond
|
||||
((looking-at "``")
|
||||
(backward-char 1)
|
||||
(delete-char 3)
|
||||
(if in-src-block
|
||||
(progn (insert "#+end_src")
|
||||
(setq in-src-block nil))
|
||||
(insert "#+begin_src ")
|
||||
(setq in-src-block t)))
|
||||
((looking-at "`\\|$")
|
||||
(setq noop-p t)
|
||||
(set-marker start-pt (1- (point)))
|
||||
(unless (eobp) (forward-char 1)))
|
||||
((not in-src-block) (replace-match "="))))
|
||||
((and "**" (guard (not in-src-block)))
|
||||
(cond
|
||||
((looking-at "\\*\\(?:[[:word:]]\\|\s\\)")
|
||||
(delete-char 1))
|
||||
((looking-back "\\(?:[[:word:]]\\|\s\\)\\*\\{2\\}"
|
||||
(max (- (point) 3) (point-min)))
|
||||
(backward-delete-char 1))))
|
||||
((and (or "_" "*") (guard (not in-src-block)))
|
||||
(when (save-match-data
|
||||
(save-excursion
|
||||
(backward-char 2)
|
||||
(or
|
||||
(looking-at
|
||||
"[^[:space:][:punct:]\n]\\(?:_\\|\\*\\)\\(?:[[:space:][:punct:]]\\|$\\)")
|
||||
(looking-at
|
||||
"\\(?:[[:space:][:punct:]]\\)\\(?:_\\|\\*\\)\\([^[:space:][:punct:]]\\|$\\)"))))
|
||||
(backward-delete-char 1)
|
||||
(insert "/"))))))
|
||||
(if noop-p
|
||||
(buffer-substring (point) start-pt)
|
||||
(prog1 (buffer-substring (point) (point-max))
|
||||
(set-marker start-pt (point-max)))))))))
|
||||
|
||||
(defun gptel--playback (buf content-str start-pt)
|
||||
"Playback CONTENT-STR in BUF.
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue