Skip to content

fix(engine): find a message's end without counting braces inside strings - #821

Open
breken-ai wants to merge 1 commit into
cactus-compute:mainfrom
breken-ai:fix/message-unbalanced-brace
Open

breken-ai wants to merge 1 commit into
cactus-compute:mainfrom
breken-ai:fix/message-unbalanced-brace

Conversation

@breken-ai

Copy link
Copy Markdown

What

parse_messages_json found the end of each message object by counting every { and } in the JSON, including those inside strings. This PR uses find_matching_delimiter instead, which skips string contents and already bounds tool_calls in the same function. It changes one line.

Why

When a message's text holds an unmatched brace, its object never closes and runs to the end of the array. In coding chats that is common, for example "why does if (x) { not compile?". Every later message is dropped, and their images/audio are attached to the earlier message. Parsed on main (fa094ce8):

[{"role":"user","content":"why does `if (x) {` not compile?"},
 {"role":"assistant","content":"it is missing its closing brace"},
 {"role":"user","content":"thanks"}]
-> 1 message (the assistant turn and the latest user turn are gone)

[{"role":"user","content":"what is {"},
 {"role":"user","content":"describe this","images":["a.png"]}]
-> 1 message, and it carries a.png

The model then answers an earlier turn and never sees the latest question.

Tests

I added cactus-engine/tests/test_messages_json_braces.cpp. It needs no model.

  • On main, both tests fail: unbalanced_brace_in_content_keeps_later_messages and images_stay_with_their_message.
  • With this PR, both pass.
  • All engine test targets build on macOS arm64. test_bucket (10/10) and test_kv_compress (29/29) still pass. The model-backed suites were not run: they need a bundle.

This touches a different hunk of parse_messages_json than #764, so the two apply independently.

AI disclosure

I found and fixed this with help from an AI coding assistant. I checked the code path by hand and reproduced the failure on main before writing the fix. The commit is signed off for the DCO.

🤖 Generated with Claude Code

parse_messages_json found where each message object ends by counting
every { and } in the JSON, including those inside content strings. A
message whose text has an unmatched brace ("why does `if (x) {` fail?")
never closes, so its object runs to the end of the array: every later
message is dropped and their images and audio are attached to it
instead. The model then answers an earlier turn.

Use find_matching_delimiter, which already skips string contents and is
used for tool_calls in the same function.

Signed-off-by: breken <hello@breken.ai>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant