karthink / gptel

A simple LLM client for Emacs
GNU General Public License v3.0
1.04k stars 113 forks source link

Error handling for Gemini backend #210

Open karthink opened 4 months ago

karthink commented 4 months ago

Original comment by @cashpw (#208)


I'm also seeing this error after running gptel-send for Gemini (gemini-pro).

Steps to reproduce:

  1. My config

    
    (use-package! gptel
      :config
      (setq
       gptel-default-mode 'org-mode)
      (setq-default
       gptel-model "gemini-pro"
       gptel-backend (gptel-make-gemini "Gemini"
                       :key (cashpw/get-secret "personal-gemini")
                       :models '("gemini-pro")
                       :stream t)))
  2. Open a scratch buffer (SPC x) and type "What's 2+2?"
  3. M-x gptel-send
  4. See (wrong-type-argument markerp nil) in minibuffer

Debug info

Debugger output ```elisp Debugger entered--Lisp error: (wrong-type-argument markerp nil) marker-position(nil) (setq response-end (marker-position tracking-marker)) (progn (setq response-beg (+ start-marker 2)) (setq response-end (marker-position tracking-marker))) (save-current-buffer (set-buffer (marker-buffer start-marker)) (progn (setq response-beg (+ start-marker 2)) (setq response-end (marker-position tracking-marker))) (pulse-momentary-highlight-region response-beg tracking-marker) (if gptel-mode (progn (save-excursion (goto-char tracking-marker) (insert "\n\n" (gptel-prompt-prefix-string)))))) (progn (save-current-buffer (set-buffer (marker-buffer start-marker)) (progn (setq response-beg (+ start-marker 2)) (setq response-end (marker-position tracking-marker))) (pulse-momentary-highlight-region response-beg tracking-marker) (if gptel-mode (progn (save-excursion (goto-char tracking-marker) (insert "\n\n" (gptel-prompt-prefix-string)))))) (save-current-buffer (set-buffer gptel-buffer) (if gptel-mode (progn (gptel--update-status " Ready" 'success))))) (if (equal http-status "200") (progn (save-current-buffer (set-buffer (marker-buffer start-marker)) (progn (setq response-beg (+ start-marker 2)) (setq response-end (marker-position tracking-marker))) (pulse-momentary-highlight-region response-beg tracking-marker) (if gptel-mode (progn (save-excursion (goto-char tracking-marker) (insert "\n\n" (gptel-prompt-prefix-string)))))) (save-current-buffer (set-buffer gptel-buffer) (if gptel-mode (progn (gptel--update-status " Ready" 'success))))) (save-current-buffer (set-buffer proc-buf) (goto-char (point-max)) (search-backward (plist-get info :token)) (backward-char) (let* ((val (read (current-buffer)))) (progn (ignore (consp val)) (let* ((x421 (cdr-safe val))) (let ((header-size x421)) (let* (... ... ...) (cond ... ... ...))))))) (save-current-buffer (set-buffer gptel-buffer) (if gptel-mode (progn (gptel--update-status (format " Response Error: %s" http-msg) 'error))))) (let* ((info (alist-get process gptel-curl--process-alist)) (gptel-buffer (plist-get info :buffer)) (backend-name (let ((cl-x (buffer-local-value 'gptel-backend gptel-buffer))) (progn (or (progn (and ... t)) (signal 'wrong-type-argument (list ... cl-x))) (aref cl-x 1)))) (tracking-marker (plist-get info :tracking-marker)) (start-marker (plist-get info :position)) (http-status (plist-get info :http-status)) (http-msg (plist-get info :status)) response-beg response-end) (if gptel-log-level (progn (gptel-curl--log-response proc-buf info))) (if (equal http-status "200") (progn (save-current-buffer (set-buffer (marker-buffer start-marker)) (progn (setq response-beg (+ start-marker 2)) (setq response-end (marker-position tracking-marker))) (pulse-momentary-highlight-region response-beg tracking-marker) (if gptel-mode (progn (save-excursion (goto-char tracking-marker) (insert "\n\n" ...))))) (save-current-buffer (set-buffer gptel-buffer) (if gptel-mode (progn (gptel--update-status " Ready" 'success))))) (save-current-buffer (set-buffer proc-buf) (goto-char (point-max)) (search-backward (plist-get info :token)) (backward-char) (let* ((val (read (current-buffer)))) (progn (ignore (consp val)) (let* ((x421 ...)) (let (...) (let* ... ...)))))) (save-current-buffer (set-buffer gptel-buffer) (if gptel-mode (progn (gptel--update-status (format " Response Error: %s" http-msg) 'error))))) (save-current-buffer (set-buffer gptel-buffer) (run-hook-with-args 'gptel-post-response-functions response-beg response-end))) (let ((proc-buf (process-buffer process))) (let* ((info (alist-get process gptel-curl--process-alist)) (gptel-buffer (plist-get info :buffer)) (backend-name (let ((cl-x (buffer-local-value ... gptel-buffer))) (progn (or (progn ...) (signal ... ...)) (aref cl-x 1)))) (tracking-marker (plist-get info :tracking-marker)) (start-marker (plist-get info :position)) (http-status (plist-get info :http-status)) (http-msg (plist-get info :status)) response-beg response-end) (if gptel-log-level (progn (gptel-curl--log-response proc-buf info))) (if (equal http-status "200") (progn (save-current-buffer (set-buffer (marker-buffer start-marker)) (progn (setq response-beg (+ start-marker 2)) (setq response-end (marker-position tracking-marker))) (pulse-momentary-highlight-region response-beg tracking-marker) (if gptel-mode (progn (save-excursion ... ...)))) (save-current-buffer (set-buffer gptel-buffer) (if gptel-mode (progn (gptel--update-status " Ready" ...))))) (save-current-buffer (set-buffer proc-buf) (goto-char (point-max)) (search-backward (plist-get info :token)) (backward-char) (let* ((val (read ...))) (progn (ignore (consp val)) (let* (...) (let ... ...))))) (save-current-buffer (set-buffer gptel-buffer) (if gptel-mode (progn (gptel--update-status (format " Response Error: %s" http-msg) 'error))))) (save-current-buffer (set-buffer gptel-buffer) (run-hook-with-args 'gptel-post-response-functions response-beg response-end))) (let* ((p (if (and nil (not (eq nil ...))) (assoc process gptel-curl--process-alist nil) (assq process gptel-curl--process-alist)))) (progn (if p (setq gptel-curl--process-alist (delq p gptel-curl--process-alist))) nil)) (kill-buffer proc-buf)) gptel-curl--stream-cleanup(# "finished\n") ```
*gptel-log* ``` { "gptel": "request headers", "timestamp": "2024-02-07 16:57:02" } { "Content-Type": "application/json" } { "gptel": "request body", "timestamp": "2024-02-07 16:57:02" } { "contents": [ { "role": "user", "parts": { "text": "You are a large language model living in Emacs and a helpful assistant. Respond concisely.\n\nWhat's 2+2?" } } ], "generationConfig": { "temperature": 1.0 } } { "gptel": "request Curl command", "timestamp": "2024-02-07 16:57:02" } [ "curl", "--disable", "--location", "--silent", "--compressed", "-XPOST", "-y300", "-Y1", "-D-", "-w(86f2883e17467c81f3ee5c1e90f6aa94 . %{size_header})", "-d{\"contents\":[{\"role\":\"user\",\"parts\":{\"text\":\"You are a large language model living in Emacs and a helpful assistant. Respond concisely.\\n\\nWhat's 2+2?\"}}],\"generationConfig\":{\"temperature\":1.0}}", "-HContent-Type: application/json", "https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:streamGenerateContent?key=" ] { "gptel": "response headers", "timestamp": "2024-02-07 16:57:03" } "HTTP/2 200 \r\ncontent-type: application/json; charset=UTF-8\r\nvary: Origin\r\nvary: X-Origin\r\nvary: Referer\r\ncontent-encoding: gzip\r\ndate: Thu, 08 Feb 2024 00:57:03 GMT\r\nserver: scaffolding on HTTPServer2\r\ncache-control: private\r\nx-xss-protection: 0\r\nx-frame-options: SAMEORIGIN\r\nx-content-type-options: nosniff\r\nserver-timing: gfet4t7; dur=772\r\nalt-svc: h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000\r\n\r" { "gptel": "response body", "timestamp": "2024-02-07 16:57:03" } [ { "candidates": [ { "finishReason": "SAFETY", "index": 0, "safetyRatings": [ { "category": "HARM_CATEGORY_SEXUALLY_EXPLICIT", "probability": "NEGLIGIBLE" }, { "category": "HARM_CATEGORY_HATE_SPEECH", "probability": "LOW" }, { "category": "HARM_CATEGORY_HARASSMENT", "probability": "MEDIUM" }, { "category": "HARM_CATEGORY_DANGEROUS_CONTENT", "probability": "NEGLIGIBLE" } ] } ], "promptFeedback": { "safetyRatings": [ { "category": "HARM_CATEGORY_SEXUALLY_EXPLICIT", "probability": "NEGLIGIBLE" }, { "category": "HARM_CATEGORY_HATE_SPEECH", "probability": "NEGLIGIBLE" }, { "category": "HARM_CATEGORY_HARASSMENT", "probability": "NEGLIGIBLE" }, { "category": "HARM_CATEGORY_DANGEROUS_CONTENT", "probability": "NEGLIGIBLE" } ] } } ] ```

I also tested a few prompts with curl directly; example:

curl \
  -H 'Content-Type: application/json' \
  -d '{"contents":[{"parts":[{"text":"What is 2+2?"}]}]}' \
  -X POST https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent?key=YOUR_API_KEY

Root cause

The log indicates the output stopped due to SAFETY (a response with content has a finishReason: STOP).

~The issue is the directive~ I thought the issue was the directive. However, omitting it (i.e. What is 2+2? rather than You are a large language model living in Emacs and a helpful assistant. Respond concisely.\n\nWhat is 2+2?) also gets a "finishReason": "SAFETY". Omitting the "2+2" (e.g. What is a cat?) gets a good response.

I am baffled that What is 2+2? is considered unsafe?

Suggestion(s)

We should check the curl response for the presence of the top-level "content" key and fail gracefully by checking the "finishReason" if it's missing.

Originally posted by @cashpw in https://github.com/karthink/gptel/issues/208#issuecomment-1933210921