Skip to content

sse_emitter: log sanitized single-line tool_buffer on parse failure (Stage 3) - #650

Merged
davide221 merged 4 commits into
Luce-Org:mainfrom
dpavlin:stage3-tool-buffer-logging
Aug 24, 2026
Merged

sse_emitter: log sanitized single-line tool_buffer on parse failure (Stage 3)#650
davide221 merged 4 commits into
Luce-Org:mainfrom
dpavlin:stage3-tool-buffer-logging

Conversation

@dpavlin

@dpavlin dpavlin commented Aug 23, 2026

Copy link
Copy Markdown
Contributor

Refs #644 (Stage 3 of 3, builds upon Stage 2)

Description

This PR improves observability and diagnostic logging for streaming tool call parse failures:

  • When tool parsing fails on an intercepted tool buffer, SseEmitter::emit_finish logs the raw intercepted buffer.
  • To prevent multiline splitting and corruption in line-oriented log aggregators (e.g. journalctl), the payload is escaped via escape_for_logging:
    • Standard escape sequences (\n, \r, \t, \\, \', \0).
    • Non-printable control characters escaped using fixed-width 4-digit \u00%02x (avoiding variable-width hex ambiguity when followed by literal hex characters).

Verification

  • Added test_escape_for_logging unit test covering NUL bytes, standard escapes, and control characters followed by hex digits.
  • Added test_emitter_suppresses_malformed_multiline_tool_buffer asserting that the full escaped payload appears on a single stderr line.
  • All 402 unit tests passing (100%).

Review in cubic

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 6 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="server/src/server/tool_parser.cpp">

<violation number="1" location="server/src/server/tool_parser.cpp:585">
P2: Malformed `arguments`/`parameters` strings are now accepted instead of rejected. Previously `json::parse(...)` in a try/catch returned `false`, dropping the call; now `parse_arg_string_or_obj` returns `true` with an empty args object while preserving the raw (unparseable) string as `tool_call.arguments`. Because `parse_tool_calls` returns that call, `tool_calls_` is non-empty in `emit_finish`, so the new "parse failed; suppressing buffered tool text" logging never fires for these buffers, and a call whose arguments are not valid JSON is emitted to clients as a valid tool call. Confirm this is the intended Stage-3 behavior; if the goal is only better diagnostics, consider keeping the suppression for unparseable argument strings.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread server/src/server/tool_parser.cpp
Comment thread server/src/server/sse_emitter.cpp Outdated
if (!parsed.is_discarded() && parsed.is_object()) {
out_args = std::move(parsed);
} else {
out_args = json::object();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Malformed arguments/parameters strings are now accepted instead of rejected. Previously json::parse(...) in a try/catch returned false, dropping the call; now parse_arg_string_or_obj returns true with an empty args object while preserving the raw (unparseable) string as tool_call.arguments. Because parse_tool_calls returns that call, tool_calls_ is non-empty in emit_finish, so the new "parse failed; suppressing buffered tool text" logging never fires for these buffers, and a call whose arguments are not valid JSON is emitted to clients as a valid tool call. Confirm this is the intended Stage-3 behavior; if the goal is only better diagnostics, consider keeping the suppression for unparseable argument strings.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At server/src/server/tool_parser.cpp, line 585:

<comment>Malformed `arguments`/`parameters` strings are now accepted instead of rejected. Previously `json::parse(...)` in a try/catch returned `false`, dropping the call; now `parse_arg_string_or_obj` returns `true` with an empty args object while preserving the raw (unparseable) string as `tool_call.arguments`. Because `parse_tool_calls` returns that call, `tool_calls_` is non-empty in `emit_finish`, so the new "parse failed; suppressing buffered tool text" logging never fires for these buffers, and a call whose arguments are not valid JSON is emitted to clients as a valid tool call. Confirm this is the intended Stage-3 behavior; if the goal is only better diagnostics, consider keeping the suppression for unparseable argument strings.</comment>

<file context>
@@ -569,82 +569,59 @@ static bool parse_complete_parameter_body(const std::string & body,
+        if (!parsed.is_discarded() && parsed.is_object()) {
+            out_args = std::move(parsed);
+        } else {
+            out_args = json::object();
+        }
+        return true;
</file context>

Comment thread server/test/test_server_unit.cpp
Comment thread server/src/server/tool_parser.cpp
@dpavlin
dpavlin force-pushed the stage3-tool-buffer-logging branch 2 times, most recently from a2a3005 to ff4092e Compare August 23, 2026 12:19
@dpavlin

dpavlin commented Aug 23, 2026

Copy link
Copy Markdown
Contributor Author

@cubic-dev-ai review

@cubic-dev-ai

cubic-dev-ai Bot commented Aug 23, 2026

Copy link
Copy Markdown
Contributor

@cubic-dev-ai review

@dpavlin I have started the AI code review. It will take a few minutes to complete.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 6 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread server/src/server/tool_parser.cpp Outdated
Comment thread server/src/server/tool_parser.cpp Outdated
Comment thread server/src/server/http_server.cpp Outdated
…ason

- http_server: suppress 'length' / 'max_tokens' finish reason when generation terminates on EOS token
- tool_parser: reject empty tool names and scalar/array arguments strings while preserving object syntax error fallback
- tool_parser: extract top-level tool name outside arguments object span in syntax error fallback
- tool_parser: support mixed <invoke> and JSON lines siblings in <function_calls> blocks
- test_server_unit: add regression tests for all 4 cases
@dpavlin
dpavlin force-pushed the stage3-tool-buffer-logging branch from ff4092e to a5cd646 Compare August 23, 2026 13:26
- escape_for_logging: escape control bytes unambiguously with fixed-width \u00%02x and \u0000
- log full escaped tool_buffer_ payload on parse failure for journalctl compatibility
- test_server_unit: add StderrCapture RAII test guard and logging verification tests
@dpavlin
dpavlin force-pushed the stage3-tool-buffer-logging branch from a5cd646 to 7ebe581 Compare August 23, 2026 13:34
@davide221
davide221 merged commit d3bdf5d into Luce-Org:main Aug 24, 2026
9 checks passed
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.

2 participants