gptel: Consolidate HTTP request process

* gptel.el (gptel-request, gptel-send, gptel--url-get-response):
Consolidate the HTTP query construction into `gptel-request`,
and use it as the single point for sending gptel queries.  This
simplifies the code, makes it easier to debug and (later) advise
or otherwise modify.  To this end, `gptel-send` reuses
`gptel-request` and no longer does its own thing.  Adjust other
HTTP request-related functions accordingly.

* gptel-curl.el (gptel-curl--get-args, gptel-curl--get-response):
Receive the full request data instead of constructing it partly in
`gptel-curl--get-args`.
This commit is contained in:
Karthik Chikmagalur 2024-03-13 21:48:46 -07:00
parent e5f54d1d09
commit 260be9d8d4
2 changed files with 30 additions and 35 deletions

View file

@ -47,16 +47,14 @@
(defvar gptel-curl--process-alist nil
"Alist of active GPTel curl requests.")
(defun gptel-curl--get-args (prompts token)
(defun gptel-curl--get-args (data token)
"Produce list of arguments for calling Curl.
PROMPTS is the data to send, TOKEN is a unique identifier."
REQUEST-DATA is the data to send, TOKEN is a unique identifier."
(let* ((url (let ((backend-url (gptel-backend-url gptel-backend)))
(if (functionp backend-url)
(funcall backend-url) backend-url)))
(data (encode-coding-string
(gptel--json-encode (gptel--request-data gptel-backend prompts))
'utf-8))
(data-json (encode-coding-string (gptel--json-encode data) 'utf-8))
(headers
(append '(("Content-Type" . "application/json"))
(when-let ((header (gptel-backend-header gptel-backend)))
@ -68,15 +66,15 @@ PROMPTS is the data to send, TOKEN is a unique identifier."
(mapcar (lambda (pair) (cons (intern (car pair)) (cdr pair)))
headers))
"request headers"))
(gptel--log data "request body"))
(gptel--log data-json "request body"))
(append
gptel-curl--common-args
(gptel-backend-curl-args gptel-backend)
(list (format "-w(%s . %%{size_header})" token))
(if (length< data gptel-curl-file-size-threshold)
(list (format "-d%s" data))
(if (length< data-json gptel-curl-file-size-threshold)
(list (format "-d%s" data-json))
(letrec
((temp-filename (make-temp-file "gptel-curl-data" nil ".json" data))
((temp-filename (make-temp-file "gptel-curl-data" nil ".json" data-json))
(cleanup-fn (lambda (&rest _)
(when (file-exists-p temp-filename)
(delete-file temp-filename)
@ -99,7 +97,7 @@ PROMPTS is the data to send, TOKEN is a unique identifier."
"Retrieve response to prompt in INFO.
INFO is a plist with the following keys:
- :prompt (the prompt being sent)
- :data (the data being sent)
- :buffer (the gptel buffer)
- :position (marker at which to insert the response).
@ -108,7 +106,7 @@ the response is inserted into the current buffer after point."
(let* ((token (md5 (format "%s%s%s%s"
(random) (emacs-pid) (user-full-name)
(recent-keys))))
(args (gptel-curl--get-args (plist-get info :prompt) token))
(args (gptel-curl--get-args (plist-get info :data) token))
(stream (and gptel-stream (gptel-backend-stream gptel-backend)))
(process (apply #'start-process "gptel-curl"
(generate-new-buffer "*gptel-curl*") "curl" args)))

View file

@ -805,11 +805,14 @@ file."
(cl-defun gptel-request
(&optional prompt &key callback
(buffer (current-buffer))
position context
position context dry-run
(stream nil) (in-place nil)
(system gptel--system-message))
"Request a response from the `gptel-backend' for PROMPT.
The request is asynchronous, the function immediately returns
with the data that was sent.
Note: This function is not fully self-contained. Consider
let-binding the parameters `gptel-backend' and `gptel-model'
around calls to it as required.
@ -832,7 +835,7 @@ with the RESPONSE (a string) and INFO (a plist):
RESPONSE is nil if there was no response or an error.
The INFO plist has (at least) the following keys:
:prompt - The full prompt that was sent with the request
:data - The request data included with the query
:position - marker at the point the request was sent, unless
POSITION is specified.
:buffer - The buffer current when the request was sent,
@ -887,6 +890,9 @@ STREAM is a boolean that determines if the response should be
streamed, as in `gptel-stream'. Do not set this if you are
specifying a custom CALLBACK!
If DRY-RUN is non-nil, construct and return the full
query data as usual, but do not send the request.
Model parameters can be let-bound around calls to this function."
(declare (indent 1))
(let* ((gptel-stream stream)
@ -908,19 +914,22 @@ Model parameters can be let-bound around calls to this function."
;; FIXME Dear reader, welcome to Jank City:
(with-temp-buffer
(let ((gptel--system-message system)
(gptel-model (buffer-local-value 'gptel-model buffer))
(gptel-backend (buffer-local-value 'gptel-backend buffer)))
(insert prompt)
(gptel--create-prompt))))
((consp prompt) prompt)))
(info (list :prompt full-prompt
(request-data (gptel--request-data gptel-backend full-prompt))
(info (list :data request-data
:buffer buffer
:position start-marker)))
(when context (plist-put info :context context))
(when in-place (plist-put info :in-place in-place))
(funcall
(if gptel-use-curl
(unless dry-run
(funcall (if gptel-use-curl
#'gptel-curl-get-response #'gptel--url-get-response)
info callback)))
info callback))
request-data))
;; TODO: Handle multiple requests(#15). (Only one request from one buffer at a time?)
;;;###autoload
@ -943,18 +952,7 @@ waiting for the response."
(call-interactively #'gptel-menu)
(message "Querying %s..." (gptel-backend-name gptel-backend))
(gptel--sanitize-model)
(let* ((response-pt
(if (use-region-p)
(set-marker (make-marker) (region-end))
(gptel--at-word-end (point-marker))))
(gptel-buffer (current-buffer))
(full-prompt (gptel--create-prompt response-pt)))
(funcall
(if gptel-use-curl
#'gptel-curl-get-response #'gptel--url-get-response)
(list :prompt full-prompt
:buffer gptel-buffer
:position response-pt)))
(gptel-request nil :stream gptel-stream)
(gptel--update-status " Waiting..." 'warning)))
(defun gptel--insert-response (response info)
@ -1113,7 +1111,7 @@ BUFFER is the LLM interaction buffer."
"Fetch response to prompt in INFO from the LLM.
INFO is a plist with the following keys:
- :prompt (the prompt being sent)
- :data (the data being sent)
- :buffer (the gptel buffer)
- :position (marker at which to insert the response).
@ -1130,8 +1128,7 @@ the response is inserted into the current buffer after point."
(funcall header) header))))
(url-request-data
(encode-coding-string
(gptel--json-encode (gptel--request-data
gptel-backend (plist-get info :prompt)))
(gptel--json-encode (plist-get info :data))
'utf-8)))
(when gptel-log-level ;logging
(when (eq gptel-log-level 'debug)