gptel: API agnostic response error handling

* gptel.el (gptel--url-get-response, gptel--url-parse-response):

- When the query fails, the error message format (in the JSON)
differs between APIs.  Ultimately it may be required to dispatch
error handling via a generic function, but for now: try to make
the error handling API agnostic.

- Mention the backend name in the error message.  Pass the backend
to the (non-streaming response) parsers to be able to do this.

* gptel-curl.el (gptel-curl--stream-cleanup,
gptel-curl--parse-response):  Same changes.
This commit is contained in:
Karthik Chikmagalur 2023-11-08 13:29:39 -08:00
parent 3308449761
commit 0109d0d1c0
2 changed files with 39 additions and 18 deletions

View file

@ -155,6 +155,9 @@ PROCESS and _STATUS are process parameters."
(clone-buffer "*gptel-error*" 'show))) (clone-buffer "*gptel-error*" 'show)))
(let* ((info (alist-get process gptel-curl--process-alist)) (let* ((info (alist-get process gptel-curl--process-alist))
(gptel-buffer (plist-get info :buffer)) (gptel-buffer (plist-get info :buffer))
(backend-name
(gptel-backend-name
(buffer-local-value 'gptel-backend gptel-buffer)))
(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))
@ -177,14 +180,16 @@ PROCESS and _STATUS are process parameters."
(json-object-type 'plist) (json-object-type 'plist)
(response (progn (goto-char header-size) (response (progn (goto-char header-size)
(condition-case nil (json-read) (condition-case nil (json-read)
(json-readtable-error 'json-read-error))))) (json-readtable-error 'json-read-error))))
(error-data (plist-get response :error)))
(cond (cond
((plist-get response :error) (error-data
(let* ((error-plist (plist-get response :error)) (if (stringp error-data)
(error-msg (plist-get error-plist :message)) (message "%s error: (%s) %s" backend-name http-msg error-data)
(error-type (plist-get error-plist :type))) (when-let ((error-msg (plist-get error-data :message)))
(message "ChatGPT error: (%s) %s" http-msg error-msg) (message "%s error: (%s) %s" backend-name http-msg error-msg))
(setq http-msg (concat "(" http-msg ") " (string-trim error-type))))) (when-let ((error-type (plist-get error-data :type)))
(setq http-msg (concat "(" http-msg ") " (string-trim error-type))))))
((eq response 'json-read-error) ((eq response 'json-read-error)
(message "ChatGPT error (%s): Malformed JSON in response." http-msg)) (message "ChatGPT error (%s): Malformed JSON in response." http-msg))
(t (message "ChatGPT error (%s): Could not parse HTTP response." http-msg))))) (t (message "ChatGPT error (%s): Could not parse HTTP response." http-msg)))))
@ -339,10 +344,19 @@ buffer."
(funcall parser nil response proc-info)) (funcall parser nil response proc-info))
http-msg)) http-msg))
((plist-get response :error) ((plist-get response :error)
(let* ((error-plist (plist-get response :error)) (let* ((error-data (plist-get response :error))
(error-msg (plist-get error-plist :message)) (error-msg (plist-get error-data :message))
(error-type (plist-get error-plist :type))) (error-type (plist-get error-data :type))
(list nil (concat "(" http-msg ") " (string-trim error-type)) error-msg))) (backend-name
(gptel-backend-name
(buffer-local-value 'gptel-backend (plist-get proc-info :buffer)))))
(if (stringp error-data)
(progn (message "%s error: (%s) %s" backend-name http-msg error-data)
(setq error-msg (string-trim error-data)))
(when (stringp error-msg)
(message "%s error: (%s) %s" backend-name http-msg (string-trim error-msg)))
(when error-type (setq http-msg (concat "(" http-msg ") " (string-trim error-type)))))
(list nil (concat "(" http-msg ") " (or error-msg "")))))
((eq response 'json-read-error) ((eq response 'json-read-error)
(list nil (concat "(" http-msg ") Malformed JSON in response.") (list nil (concat "(" http-msg ") Malformed JSON in response.")
"Malformed JSON in response")) "Malformed JSON in response"))

View file

@ -855,7 +855,7 @@ the response is inserted into the current buffer after point."
(url-retrieve (gptel-backend-url gptel-backend) (url-retrieve (gptel-backend-url gptel-backend)
(lambda (_) (lambda (_)
(pcase-let ((`(,response ,http-msg ,error) (pcase-let ((`(,response ,http-msg ,error)
(gptel--url-parse-response (current-buffer)))) (gptel--url-parse-response backend (current-buffer))))
(plist-put info :status http-msg) (plist-put info :status http-msg)
(when error (plist-put info :error error)) (when error (plist-put info :error error))
(funcall (or callback #'gptel--insert-response) (funcall (or callback #'gptel--insert-response)
@ -873,7 +873,7 @@ RESPONSE is the parsed JSON of the response, as a plist.
PROC-INFO is a plist with process information and other context. PROC-INFO is a plist with process information and other context.
See `gptel-curl--get-response' for its contents.") See `gptel-curl--get-response' for its contents.")
(defun gptel--url-parse-response (response-buffer) (defun gptel--url-parse-response (backend response-buffer)
"Parse response in RESPONSE-BUFFER." "Parse response in RESPONSE-BUFFER."
(when (buffer-live-p response-buffer) (when (buffer-live-p response-buffer)
(when gptel--debug (when gptel--debug
@ -892,14 +892,21 @@ See `gptel-curl--get-response' for its contents.")
(json-readtable-error 'json-read-error)))))) (json-readtable-error 'json-read-error))))))
(cond (cond
((string-match-p "200 OK" http-msg) ((string-match-p "200 OK" http-msg)
(list (string-trim (gptel--parse-response gptel-backend response (list (string-trim (gptel--parse-response backend response
'(:buffer response-buffer))) '(:buffer response-buffer)))
http-msg)) http-msg))
((plist-get response :error) ((plist-get response :error)
(let* ((error-plist (plist-get response :error)) (let* ((error-data (plist-get response :error))
(error-msg (plist-get error-plist :message)) (error-msg (plist-get error-data :message))
(error-type (plist-get error-plist :type))) (error-type (plist-get error-data :type))
(list nil (concat "(" http-msg ") " error-type) error-msg))) (backend-name (gptel-backend-name backend)))
(if (stringp error-data)
(progn (message "%s error: (%s) %s" backend-name http-msg error-data)
(setq error-msg (string-trim error-data)))
(when (stringp error-msg)
(message "%s error: (%s) %s" backend-name http-msg (string-trim error-msg)))
(when error-type (setq http-msg (concat "(" http-msg ") " (string-trim error-type)))))
(list nil (concat "(" http-msg ") " (or error-msg "")))))
((eq response 'json-read-error) ((eq response 'json-read-error)
(list nil (concat "(" http-msg ") Malformed JSON in response.") "json-read-error")) (list nil (concat "(" http-msg ") Malformed JSON in response.") "json-read-error"))
(t (list nil (concat "(" http-msg ") Could not parse HTTP response.") (t (list nil (concat "(" http-msg ") Could not parse HTTP response.")