gptel-curl: Better error handling when streaming

* gptel-curl.el (gptel-curl--stream-filter,
gptel-curl--stream-cleanup): When streaming responses, move the
error handling from the Curl process filter to the cleanup
sentinel. This simplifies the filter code a fair bit.
This commit is contained in:
Karthik Chikmagalur 2023-04-08 09:57:38 -07:00
parent 6c47c0a483
commit 1cd0f58552

View file

@ -109,15 +109,41 @@ PROCESS and STATUS are process parameters."
(let* ((info (alist-get process gptel-curl--process-alist)) (let* ((info (alist-get process gptel-curl--process-alist))
(gptel-buffer (plist-get info :gptel-buffer)) (gptel-buffer (plist-get info :gptel-buffer))
(tracking-marker (plist-get info :tracking-marker)) (tracking-marker (plist-get info :tracking-marker))
(start-marker (plist-get info :start-marker))) (start-marker (plist-get info :start-marker))
(pulse-momentary-highlight-region (+ start-marker 2) tracking-marker) (http-status (plist-get info :http-status))
(with-current-buffer gptel-buffer (http-msg (plist-get info :http-msg)))
(when (equal (plist-get info :http-status) "200") (if (equal http-status "200")
(gptel--update-header-line " Ready" 'success) ;; Finish handling response
(with-current-buffer gptel-buffer
(pulse-momentary-highlight-region (+ start-marker 2) tracking-marker)
(when gptel-mode
(gptel--update-header-line " Ready" 'success)
(save-excursion (goto-char tracking-marker)
(insert "\n\n" (gptel-prompt-string)))))
;; Or Capture error message
(with-current-buffer proc-buf
(goto-char (point-max))
(search-backward (plist-get info :token))
(backward-char)
(pcase-let* ((`(,_ . ,header-size) (read (current-buffer)))
(response (progn (goto-char header-size)
(condition-case nil (json-read)
(json-readtable-error 'json-read-error)))))
(cond
((plist-get response :error)
(let* ((error-plist (plist-get response :error))
(error-msg (plist-get error-plist :message))
(error-type (plist-get error-plist :type)))
(message "ChatGPT error: %s" error-msg)
(setq http-msg (concat http-msg ": " (string-trim error-type)))))
((eq response 'json-read-error)
(message "ChatGPT error (%s): Malformed JSON in response." http-msg))
(t (message "ChatGPT error (%s): Could not parse HTTP response." http-msg)))))
(with-current-buffer gptel-buffer
(when gptel-mode (when gptel-mode
(save-excursion (goto-char tracking-marker) (gptel--update-header-line
(insert "\n\n" (gptel-prompt-string))))) (format " Response Error: %s" http-msg) 'error)))))
(run-hooks 'gptel-post-response-hook))) (run-hooks 'gptel-post-response-hook)
(setf (alist-get process gptel-curl--process-alist nil 'remove) nil) (setf (alist-get process gptel-curl--process-alist nil 'remove) nil)
(kill-buffer proc-buf))) (kill-buffer proc-buf)))
@ -174,32 +200,29 @@ See `gptel--url-get-response' for details."
(and (string-match "HTTP/[.0-9]+ +\\([0-9]+\\)" http-msg) (and (string-match "HTTP/[.0-9]+ +\\([0-9]+\\)" http-msg)
(match-string 1 http-msg))))) (match-string 1 http-msg)))))
(plist-put proc-info :http-status http-status) (plist-put proc-info :http-status http-status)
(plist-put proc-info :http-msg http-msg) (plist-put proc-info :http-msg http-msg))))
(unless (equal http-status "200")
(message "%s" (concat (string-trim http-msg) ": Could not parse HTTP response."))))))
(when-let ((http-msg (plist-get proc-info :http-msg)) (when-let ((http-msg (plist-get proc-info :http-msg))
(http-status (plist-get proc-info :http-status))) (http-status (plist-get proc-info :http-status)))
;; Find data chunk(s) and run callback ;; Find data chunk(s) and run callback
(funcall (or (plist-get proc-info :callback) (when (equal http-status "200")
#'gptel-curl--stream-insert-response) (funcall (or (plist-get proc-info :callback)
(if (equal http-status "200") #'gptel-curl--stream-insert-response)
(let* ((json-object-type 'plist) (let* ((json-object-type 'plist)
(response) (content-str)) (response) (content-str))
(condition-case nil (condition-case nil
(while (re-search-forward "^data:" nil t) (while (re-search-forward "^data:" nil t)
(save-match-data (save-match-data
(unless (looking-at " *\\[DONE\\]") (unless (looking-at " *\\[DONE\\]")
(when-let* ((response (json-read)) (when-let* ((response (json-read))
(delta (map-nested-elt (delta (map-nested-elt
response '(:choices 0 :delta))) response '(:choices 0 :delta)))
(content (plist-get delta :content))) (content (plist-get delta :content)))
(push content content-strs))))) (push content content-strs)))))
(error (error
(goto-char (match-beginning 0)))) (goto-char (match-beginning 0))))
(list :content (apply #'concat (nreverse content-strs)) :status http-msg)) (list :content (apply #'concat (nreverse content-strs)) :status http-msg))
(list :content nil :status http-msg)) proc-info))))))
proc-info)))))
(defun gptel-curl--sentinel (process status) (defun gptel-curl--sentinel (process status)
"Process sentinel for GPTel curl requests. "Process sentinel for GPTel curl requests.