gptel: Remove aio dependency
* gptel.el (gptel-send, gptel--insert-response, gptel--url-get-response): Remove aio dependency, turn aio-defuns into regular functions. This requires splitting `gptel-send' into "before response" and "after response" functions, but the ability to debug the code fully is worth the inconvenience. The new "after response" function is `gptel--insert-response'. * gptel-curl.el (gptel-curl--sentinel, gptel-curl-get-response): Turn aio-defuns into regular functions.
This commit is contained in:
parent
8a6ef565f0
commit
040baad910
2 changed files with 61 additions and 49 deletions
|
@ -32,7 +32,6 @@
|
||||||
(require 'subr-x))
|
(require 'subr-x))
|
||||||
(require 'map)
|
(require 'map)
|
||||||
(require 'json)
|
(require 'json)
|
||||||
(require 'aio)
|
|
||||||
|
|
||||||
(defvar gptel-curl--process-alist nil
|
(defvar gptel-curl--process-alist nil
|
||||||
"Alist of active GPTel curl requests.")
|
"Alist of active GPTel curl requests.")
|
||||||
|
@ -67,26 +66,24 @@ PROMPTS is the data to send, TOKEN is a unique identifier."
|
||||||
(nreverse (cons url args))))
|
(nreverse (cons url args))))
|
||||||
|
|
||||||
;;;###autoload
|
;;;###autoload
|
||||||
(defun gptel-curl-get-response (prompts)
|
(defun gptel-curl-get-response (info)
|
||||||
"Retrieve response to PROMPTS."
|
"Retrieve response to prompt in INFO.
|
||||||
|
|
||||||
|
INFO is a plist with the following keys:
|
||||||
|
- :prompt (the prompt being sent)
|
||||||
|
- :gptel-buffer (the gptel buffer)
|
||||||
|
- :insert-marker (marker at which to insert the response)."
|
||||||
(with-current-buffer (generate-new-buffer "*gptel-curl*")
|
(with-current-buffer (generate-new-buffer "*gptel-curl*")
|
||||||
(let* ((token (md5 (format "%s%s%s%s"
|
(let* ((token (md5 (format "%s%s%s%s"
|
||||||
(random) (emacs-pid) (user-full-name)
|
(random) (emacs-pid) (user-full-name)
|
||||||
(recent-keys))))
|
(recent-keys))))
|
||||||
(args (gptel-curl--get-args prompts token))
|
(args (gptel-curl--get-args (plist-get info :prompt) token))
|
||||||
(process (apply #'start-process "gptel-curl" (current-buffer)
|
(process (apply #'start-process "gptel-curl" (current-buffer)
|
||||||
"curl" args))
|
"curl" args)))
|
||||||
(promise (aio-promise))
|
|
||||||
(cb (lambda (result)
|
|
||||||
(aio-resolve promise (lambda () result))
|
|
||||||
(setf (alist-get process
|
|
||||||
gptel-curl--process-alist nil 'remove)
|
|
||||||
nil))))
|
|
||||||
(prog1 promise
|
|
||||||
(set-process-query-on-exit-flag process nil)
|
(set-process-query-on-exit-flag process nil)
|
||||||
(setf (alist-get process gptel-curl--process-alist)
|
(setf (alist-get process gptel-curl--process-alist)
|
||||||
(list :callback cb :token token))
|
(nconc (list :token token) info))
|
||||||
(set-process-sentinel process #'gptel-curl--sentinel)))))
|
(set-process-sentinel process #'gptel-curl--sentinel))))
|
||||||
|
|
||||||
(defun gptel-curl--sentinel (process status)
|
(defun gptel-curl--sentinel (process status)
|
||||||
"Process sentinel for GPTel curl requests.
|
"Process sentinel for GPTel curl requests.
|
||||||
|
@ -96,13 +93,14 @@ PROCESS and STATUS are process parameters."
|
||||||
(when gptel--debug
|
(when gptel--debug
|
||||||
(with-current-buffer proc-buf
|
(with-current-buffer proc-buf
|
||||||
(clone-buffer "*gptel-error*" 'show)))
|
(clone-buffer "*gptel-error*" 'show)))
|
||||||
(if-let* ((ok-p (equal status "finished\n"))
|
(if-let* (((equal status "finished\n"))
|
||||||
(proc-info (alist-get process gptel-curl--process-alist))
|
(proc-info (alist-get process gptel-curl--process-alist))
|
||||||
(proc-token (plist-get proc-info :token))
|
(proc-token (plist-get proc-info :token))
|
||||||
(content (gptel-curl--parse-response proc-buf proc-token)))
|
(response (gptel-curl--parse-response proc-buf proc-token)))
|
||||||
(funcall (plist-get proc-info :callback) content)
|
(gptel--insert-response response proc-info)
|
||||||
;; Failed
|
;; Failed
|
||||||
(funcall (plist-get proc-info :callback) nil))
|
(gptel--insert-response (list :content nil :status status) proc-info))
|
||||||
|
(setf (alist-get process gptel-curl--process-alist nil 'remove) nil)
|
||||||
(kill-buffer proc-buf)))
|
(kill-buffer proc-buf)))
|
||||||
|
|
||||||
(defun gptel-curl--parse-response (buf token)
|
(defun gptel-curl--parse-response (buf token)
|
||||||
|
|
66
gptel.el
66
gptel.el
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
;; Author: Karthik Chikmagalur
|
;; Author: Karthik Chikmagalur
|
||||||
;; Version: 0.10
|
;; Version: 0.10
|
||||||
;; Package-Requires: ((emacs "27.1") (aio "1.0") (transient "0.3.7"))
|
;; Package-Requires: ((emacs "27.1") (transient "0.3.7"))
|
||||||
;; Keywords: convenience
|
;; Keywords: convenience
|
||||||
;; URL: https://github.com/karthink/gptel
|
;; URL: https://github.com/karthink/gptel
|
||||||
|
|
||||||
|
@ -33,9 +33,6 @@
|
||||||
;; - You need an OpenAI API key. Set the variable `gptel-api-key' to the key or to
|
;; - You need an OpenAI API key. Set the variable `gptel-api-key' to the key or to
|
||||||
;; a function of no arguments that returns the key.
|
;; a function of no arguments that returns the key.
|
||||||
;;
|
;;
|
||||||
;; - If installing manually: Install the package `emacs-aio' using `M-x package-install'
|
|
||||||
;; or however you install packages.
|
|
||||||
;;
|
|
||||||
;; - Not required but recommended: Install `markdown-mode'.
|
;; - Not required but recommended: Install `markdown-mode'.
|
||||||
;;
|
;;
|
||||||
;; Usage:
|
;; Usage:
|
||||||
|
@ -56,7 +53,7 @@
|
||||||
(require 'subr-x)
|
(require 'subr-x)
|
||||||
(require 'cl-lib))
|
(require 'cl-lib))
|
||||||
|
|
||||||
(require 'aio)
|
(require 'url)
|
||||||
(require 'json)
|
(require 'json)
|
||||||
(require 'map)
|
(require 'map)
|
||||||
(require 'text-property-search)
|
(require 'text-property-search)
|
||||||
|
@ -150,8 +147,13 @@ return the transformed string."
|
||||||
|
|
||||||
;; TODO: Handle read-only buffers. Should we spawn a new buffer automatically?
|
;; TODO: Handle read-only buffers. Should we spawn a new buffer automatically?
|
||||||
;; TODO: Handle multiple requests(#15). (Only one request from one buffer at a time?)
|
;; TODO: Handle multiple requests(#15). (Only one request from one buffer at a time?)
|
||||||
(aio-defun gptel-send (&optional arg)
|
;; TODO: Since we capture a marker for the insertion location, `gptel-buffer' no
|
||||||
"Submit this prompt to ChatGPT."
|
;; longer needs to be recorded
|
||||||
|
(defun gptel-send (&optional arg)
|
||||||
|
"Submit this prompt to ChatGPT.
|
||||||
|
|
||||||
|
With prefix arg ARG activate a transient menu with more options
|
||||||
|
instead."
|
||||||
(interactive "P")
|
(interactive "P")
|
||||||
(if (and arg (require 'gptel-transient nil t))
|
(if (and arg (require 'gptel-transient nil t))
|
||||||
(call-interactively #'gptel-send-menu)
|
(call-interactively #'gptel-send-menu)
|
||||||
|
@ -162,14 +164,23 @@ return the transformed string."
|
||||||
(set-marker (make-marker) (region-end))
|
(set-marker (make-marker) (region-end))
|
||||||
(point-marker)))
|
(point-marker)))
|
||||||
(gptel-buffer (current-buffer))
|
(gptel-buffer (current-buffer))
|
||||||
(full-prompt (gptel--create-prompt response-pt))
|
(full-prompt (gptel--create-prompt response-pt)))
|
||||||
(response (aio-await
|
|
||||||
(funcall
|
(funcall
|
||||||
(if gptel-use-curl
|
(if gptel-use-curl
|
||||||
#'gptel-curl-get-response #'gptel--url-get-response)
|
#'gptel-curl-get-response #'gptel--url-get-response)
|
||||||
full-prompt)))
|
(list :prompt full-prompt
|
||||||
(content-str (plist-get response :content))
|
:gptel-buffer gptel-buffer
|
||||||
(status-str (plist-get response :status)))
|
:insert-marker response-pt)))))
|
||||||
|
|
||||||
|
(defun gptel--insert-response (response info)
|
||||||
|
"Insert RESPONSE from ChatGPT into the gptel buffer.
|
||||||
|
|
||||||
|
INFO is a plist containing information relevant to this buffer.
|
||||||
|
See `gptel--url-get-response' for details."
|
||||||
|
(let* ((content-str (plist-get response :content))
|
||||||
|
(status-str (plist-get response :status))
|
||||||
|
(gptel-buffer (plist-get info :gptel-buffer))
|
||||||
|
(response-pt (plist-get info :insert-marker)))
|
||||||
(if content-str
|
(if content-str
|
||||||
(with-current-buffer gptel-buffer
|
(with-current-buffer gptel-buffer
|
||||||
(setq content-str (gptel--transform-response
|
(setq content-str (gptel--transform-response
|
||||||
|
@ -178,9 +189,6 @@ return the transformed string."
|
||||||
(put-text-property 0 (length content-str) 'gptel 'response content-str)
|
(put-text-property 0 (length content-str) 'gptel 'response content-str)
|
||||||
(message "Querying ChatGPT... done.")
|
(message "Querying ChatGPT... done.")
|
||||||
(goto-char response-pt)
|
(goto-char response-pt)
|
||||||
(display-buffer (current-buffer)
|
|
||||||
'((display-buffer-reuse-window
|
|
||||||
display-buffer-use-some-window)))
|
|
||||||
(unless (bobp) (insert-before-markers-and-inherit "\n\n"))
|
(unless (bobp) (insert-before-markers-and-inherit "\n\n"))
|
||||||
(if gptel-playback
|
(if gptel-playback
|
||||||
(gptel--playback gptel-buffer content-str response-pt)
|
(gptel--playback gptel-buffer content-str response-pt)
|
||||||
|
@ -192,7 +200,7 @@ return the transformed string."
|
||||||
(gptel--update-header-line " Ready" 'success))))
|
(gptel--update-header-line " Ready" 'success))))
|
||||||
(goto-char (- (point) 2)))
|
(goto-char (- (point) 2)))
|
||||||
(gptel--update-header-line
|
(gptel--update-header-line
|
||||||
(format " Response Error: %s" status-str) 'error)))))
|
(format " Response Error: %s" status-str) 'error))))
|
||||||
|
|
||||||
(defun gptel--create-prompt (&optional prompt-end)
|
(defun gptel--create-prompt (&optional prompt-end)
|
||||||
"Return a full conversation prompt from the contents of this buffer.
|
"Return a full conversation prompt from the contents of this buffer.
|
||||||
|
@ -273,10 +281,13 @@ BUFFER is the interaction buffer for ChatGPT."
|
||||||
('org-mode (gptel--convert-markdown->org content))
|
('org-mode (gptel--convert-markdown->org content))
|
||||||
(_ content)))
|
(_ content)))
|
||||||
|
|
||||||
(aio-defun gptel--url-get-response (prompts)
|
(defun gptel--url-get-response (info)
|
||||||
"Fetch response for PROMPTS from ChatGPT.
|
"Fetch response to prompt in INFO from ChatGPT.
|
||||||
|
|
||||||
Return the message received."
|
INFO is a plist with the following keys:
|
||||||
|
- :prompt (the prompt being sent)
|
||||||
|
- :gptel-buffer (the gptel buffer)
|
||||||
|
- :insert-marker (marker at which to insert the response)."
|
||||||
(let* ((inhibit-message t)
|
(let* ((inhibit-message t)
|
||||||
(message-log-max nil)
|
(message-log-max nil)
|
||||||
(api-key
|
(api-key
|
||||||
|
@ -289,13 +300,16 @@ Return the message received."
|
||||||
`(("Content-Type" . "application/json")
|
`(("Content-Type" . "application/json")
|
||||||
("Authorization" . ,(concat "Bearer " api-key))))
|
("Authorization" . ,(concat "Bearer " api-key))))
|
||||||
(url-request-data
|
(url-request-data
|
||||||
(encode-coding-string (json-encode (gptel--request-data prompts)) 'utf-8)))
|
(encode-coding-string
|
||||||
(pcase-let ((`(,_ . ,response-buffer)
|
(json-encode (gptel--request-data (plist-get info :prompt)))
|
||||||
(aio-await
|
'utf-8)))
|
||||||
(aio-url-retrieve "https://api.openai.com/v1/chat/completions"))))
|
(url-retrieve "https://api.openai.com/v1/chat/completions"
|
||||||
(prog1
|
(lambda (_)
|
||||||
(gptel--url-parse-response response-buffer)
|
(let ((response
|
||||||
(kill-buffer response-buffer)))))
|
(gptel--url-parse-response (current-buffer))))
|
||||||
|
(gptel--insert-response response info)
|
||||||
|
(kill-buffer)))
|
||||||
|
nil t nil)))
|
||||||
|
|
||||||
(defun gptel--url-parse-response (response-buffer)
|
(defun gptel--url-parse-response (response-buffer)
|
||||||
"Parse response in RESPONSE-BUFFER."
|
"Parse response in RESPONSE-BUFFER."
|
||||||
|
|
Loading…
Add table
Reference in a new issue