Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(Claude) Request fails with "all messages must have non-empty content" #409

Closed
agzam opened this issue Oct 4, 2024 · 16 comments
Closed
Labels
bug Something isn't working

Comments

@agzam
Copy link

agzam commented Oct 4, 2024

I set gptel-default-mode to org-mode and at first, it seemed that it's worked, but it sometimes can fail.

What I did:

  • Set gptel-default-mode to org-mode
  • Opened gptel buffer, started using it normally
  • at some point it fails with:
Claude error: (HTTP/2 400) messages.2: all messages must have non-empty content except for the optional final assistant message
@karthink karthink changed the title Fails to send proper request in org-mode (Claude) Request fails with "all messages must have non-empty content" Oct 4, 2024
@karthink
Copy link
Owner

karthink commented Oct 4, 2024

Ah yeah, I'm aware of this issue. It's unrelated to Org mode. See prior discussions in #406, #321, #351, #343.

There is no easy fix for this yet. Rather, there are several easy ways to fix it but all of them create new problems. Here are three independent things you can do to avoid the error (i.e., any one of them will work, don't do them all):

  1. Don't modify Claude's response in any way, either using gptel-post-request-functions or manually. (Obviously this is not an acceptable compromise.)
  2. Switch to the feature-overlays branch of gptel.
  3. Run
(setf (alist-get 'gptel 'text-property-default-nonsticky nil 'remove) nil)

Each of these causes other problems, but it's possible you may not encounter any of them.

Eventually I need to find a solution that works with minimal compromises.

@karthink karthink added the bug Something isn't working label Oct 4, 2024
@agzam
Copy link
Author

agzam commented Oct 5, 2024

It's unrelated to Org mode

For some reason I don't see it (at least not as often) when using Markdown mode.

@wlauppe
Copy link

wlauppe commented Oct 9, 2024

when i looked at the code after a recent "invalid_request_error" I realized, that my buffer had adjacent GPTEL_BOUNDS
shoudnt it just merge:
:GPTEL_BOUNDS: ((359 . 835) (836 . 2720) (2721 . 3388))
to
:GPTEL_BOUNDS: ((359 . 2720) (2721 . 3388))
and then there would be no empty user message? Would this be a solution?

@wlauppe
Copy link

wlauppe commented Oct 15, 2024

I got the feeling, that gptel sometimes produces these invalid_request_error-hickups even if you dont edit the response. For debugging purposes i wrote some functions, which visually show the gptel-bounds:

;; gptel chat coloring 
(defvar gptel-bounds-overlay nil
  "Variable to store the overlay for GPTEL_BOUNDS.")

(defun show-and-highlight-gptel-bounds ()
  "Display the first occurrence of GPTEL_BOUNDS property in the file and highlight the bounds."
  (interactive)
  (save-excursion
    (goto-char (point-min))
    (let ((bounds-str (org-entry-get (point) "GPTEL_BOUNDS")))
      (if bounds-str
          (let ((bounds (read bounds-str)))
            (message "GPTEL_BOUNDS: %s" bounds-str)
            (highlight-bounds bounds))
        (message "No GPTEL_BOUNDS property found in the file.")))))

(defun highlight-bounds (bounds-list)
  "Highlight the regions specified by BOUNDS-LIST."
  (dolist (range bounds-list)
    (let ((start (car range))
          (end (cdr range)))
      (when (and (numberp start) (numberp end) (<= start end))
        (let ((overlay (make-overlay start end)))
          (overlay-put overlay 'face '(:background "light sky blue"))
          (overlay-put (make-overlay start (1+ start)) 'face '(:background "steel blue"))
          (overlay-put (make-overlay (1- end) end) 'face '(:background "steel blue"))
          (push overlay gptel-bounds-overlay))))))

(defun toggle-highlight-gptel-bounds ()
  "Toggle the highlighting of GPTEL_BOUNDS."
  (interactive)
  (if gptel-bounds-overlay
      (progn
        (mapc 'delete-overlay gptel-bounds-overlay)
        (setq gptel-bounds-overlay nil)
        (message "GPTEL_BOUNDS highlighting removed."))
    (save-excursion
      (goto-char (point-min))
      (let ((bounds-str (org-entry-get (point) "GPTEL_BOUNDS")))
        (if bounds-str
            (let ((bounds (read bounds-str)))
              (highlight-bounds bounds)
              (message "GPTEL_BOUNDS highlighting enabled."))
          (message "No GPTEL_BOUNDS property found in the file."))))))

(global-set-key (kbd "C-c h") 'toggle-highlight-gptel-bounds)

if you execute this code you can toggle highlighting of system messages, and get a clearer image what is going on.
assistant messages are highlighted with a blue background, with the start and end character in a darker blue.
For example i had the following conversation:
chat_error_annotated

you see that a system message is ending at character 971 and the next one starting right at 972. If i want to continue the chat with a second question, thats were empty user messages are created, an then claude complains.

@wlauppe
Copy link

wlauppe commented Oct 15, 2024

thats the relevant snippet from gptel-log

(re-search-forward \":GPTEL_BOUNDS: (\" nil t)"
    },
    {
      "role": "user",
      "content": ""
    },
    {
      "role": "assistant",
      "content": "\n      (let ((bounds

karthink added a commit that referenced this issue Oct 15, 2024
* gptel-anthropic.el (gptel--parse-buffer): Ignore blank user
promps in the buffer when creating the full prompt.  The most
common way to unintentionally produce these blank regions is to
reformat the response in some way, such as by filling the response
region.  Until a better solution for tracking responses can be
found, this will have to do.  Address #409, #406 and #351.  Also
relevant to the discussion about overlay-based tracking in #321.
@karthink
Copy link
Owner

@agzam, @wlauppe could you try it now? I added a clause to the logic for Anthropic where I don't include "empty" user prompts in the message.

(Even if this works, this is a stopgap solution -- as I haven't been able to find a response tracking method that is both robust and simple.)

@agzam
Copy link
Author

agzam commented Oct 16, 2024

Apologies for the delay, just wanted to make sure it's good. This is what I have in my config

(setq gptel-default-mode 'org-mode)
(setf (alist-get 'org-mode gptel-prompt-prefix-alist) "* ")

Sorry, with org-indent-mode my converstations were weirdly shifting onto the right, so I removed a few asterisks.

Been using it for a couple of days, so far no issues with the requests. Looks good. Thank you very much.

@karthink
Copy link
Owner

Closing this now.

@wlauppe and @agzam, feel free to reopen if you can still reproduce the error.

@wlauppe
Copy link

wlauppe commented Oct 17, 2024

karthik, if you write an updated :GPTEL_BOUNDS: line, do you account for the changes in length?
For example if it had length 10 and the new :GPTEL_BOUNDS: line has length 20. the number in the
cons cell should be 10 higher, becouse GPTEL_BOUNDS is inserted at the beginning of the buffer. (359 . 835) (836 . 2720) becomes (369 . 845) (846 . 2730) do you do that?

@karthink
Copy link
Owner

karthik, if you write an updated :GPTEL_BOUNDS: line, do you account for the changes in length? For example if it had length 10 and the new :GPTEL_BOUNDS: line has length 20. the number in the cons cell should be 10 higher, becouse GPTEL_BOUNDS is inserted at the beginning of the buffer. (359 . 835) (836 . 2720) becomes (369 . 845) (846 . 2730) do you do that?

Fixed-point iteration: https://github.com/karthink/gptel/blob/master/gptel-org.el#L410

@wlauppe
Copy link

wlauppe commented Oct 17, 2024

I think it is nice to have a workaround hack, to be able to work with Claude in conversation mode even if something is broken at the moment, but at the same time I think we are very close at finding the real problem, and this workaround obscures the fact that something is broken. Do you understand why the gptel--get-buffer-bounds() function. from gptel.el

(defun gptel--get-buffer-bounds ()
  "Return the gptel response boundaries in the buffer as an alist."
  (save-excursion
    (save-restriction
      (widen)
      (goto-char (point-max))
      (let ((prop) (bounds))
        (while (setq prop (text-property-search-backward
                           'gptel 'response t))
          (push (cons (prop-match-beginning prop)
                      (prop-match-end prop))
                bounds))
        bounds))))

returns, two adjacent cells of a text which has consecutively the text property ' gptel 'response? eg.
:GPTEL_BOUNDS: ((359 . 2720) (2721 . 3388))
At the same time i got some spurious errors, and dont know if there are connected to the workaround hack. Thats why havent answered with 👍
I may play around with the old version for a bit, maybe I find something out.

@karthink
Copy link
Owner

and this workaround obscures the fact that something is broken.

If you are referring to the workaround chosen as the solution in this thread, it's a workaround, but nothing is broken. The text-properties are applied and read as intended.

:GPTEL_BOUNDS: ((359 . 2720) (2721 . 3388))

These bounds are correct, you can have one character without the gptel text-property between 2720 and 2721. Text property bounds are the "edges" between chars. Typically this character ends up being some kind of whitespace.

The real question is if we should also apply this text property to the range 2720-2721. Unfortunately making the gptel property sticky causes a bunch of other problems. There has been extensive discussion about this, see the issues linked in this comment above, and see (info "(elisp) Sticky Properties") for details on sticky text properties.

@wlauppe
Copy link

wlauppe commented Oct 17, 2024

Text property bounds are the "edges" between chars.

i thought the above means. character 359 up to and included character 2720 is an assistant message?
and character 2721 up to and included character 3388 too? This wrong?

@wlauppe
Copy link

wlauppe commented Oct 17, 2024

thanks for pointing me to the sticky text properties documentation.

@karthink
Copy link
Owner

i thought the above means. character 359 up to and included character 2720 is an assistant message? and character 2721 up to and included character 3388 too? This wrong?

There is no character at 359, a character occupies the range from 359 to 360. Similarly, there is no character at 2720. So the above means that all characters between points 359 and 2720 are part of an assistant message, as are characters between points 2721 and 3388.

The single character between the points 2720 and 2721 is part of the user prompt. It's recognized as user input because it was inserted by the user, possibly by running a command like fill-region or typing in the newline character there.

@daedsidog
Copy link
Contributor

Anecdotally, I never had this error.

karthink added a commit that referenced this issue Dec 8, 2024
* gptel-anthropic.el (gptel--parse-buffer): Guard against response
regions that are whitespace-only (#452, #409, #406, #351, #321).
This is an Anthropic-API specific problem.  Eventually this will
need to be fixed more robustly, using rear-sticky text properties.
karthink added a commit that referenced this issue Dec 8, 2024
* gptel-anthropic.el (gptel--parse-buffer): Following from the
previous approach, change how we guard against blank response
regions (#452, #409, #406, #351, #321).  This time we compare the
current and previous values of (point), after skipping whitespace,
when parsing the buffer and constructing the prompt.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

4 participants