Skip to content

fix(cot_agent): ReAct parser: stale JSON flush state, spurious flags, final-answer variants, EOF caches - #3952

Merged
crazywoola merged 1 commit into
langgenius:mainfrom
hpiclaranet:fix/cot-agent-parser-bugs
Sep 28, 2026
Merged

crazywoola merged 1 commit into
langgenius:mainfrom
hpiclaranet:fix/cot-agent-parser-bugs

Conversation

@hpiclaranet

Copy link
Copy Markdown
Contributor

Summary

Four ReAct stream-parser fixes in output_parser/cot_output_parser.py (+ the shared is_final_action() predicate and its three call sites in strategies/ReAct.py), each with a regression test that fails against the current code:

  1. Stale prefix state across JSON blobs — after a completed JSON blob is flushed, last_character kept the closing }/], so a prefix right after a blob ({...}Action:{...}, {...}FinalAnswer:) was leaked as literal thought text (and }FinalAnswer: additionally blocked live answer streaming). Fix: reset last_character to a delimiter in the flush block and accept \t/\r as prefix delimiters.
  2. Spurious "Action parse failed" flags — every non-action JSON blob in THINKING was flagged, so innocent JSON (citations [1], markdown links, {}, arbitrary objects) killed the tool round with a misleading error. Fix: only flag action-shaped blobs (emitted right after Action: or carrying action-ish keys); everything else is plain thought text.
  3. FinalAnswer/final_answer not recognized as final — only the spaced "final answer" was treated as final, so the compact/underscore JSON action names fell into "there is not a tool named ..." and wasted a round. Fix: shared is_final_action() predicate (lower + strip _/spaces) used at the round-finality check and both assistant-history formatters; the SDK is_final() spellings remain covered.
  4. Partial prefixes dropped at EOF — a stream cut mid-prefix (e.g. ...FinalAns) silently dropped the buffered characters. Fix: flush all three PrefixMatcher caches as text in the current state at stream end.

Also: malformed (non-dict/str) action_input (e.g. a list) is coerced to a JSON string so it never raises out of the parser (strategy-side handling is a documented follow-up).

Testing

  • Full plugin suite: 96 passed, 15 skipped (baseline 76 passed) — 17 new tests across test_cot_output_parser.py and test_react.py, all verified to fail against the pre-fix code (stashed-source run: 17 failed) and pass with the fix.
  • New coverage: citations/links/{}/arbitrary JSON not flagged; arbitrary JSON around a real action (real action wins); Action:-prefixed invalid JSON still flagged; }Action:/}FinalAnswer: adjacency; tab/CR before prefixes; truncated prefix at EOF; list action_input coercion; FinalAnswer/final_answer/final answer variants through the full strategy (tool not invoked, final answer streamed).
  • ruff check on the touched files: zero new findings (10 pre-existing E402 from the tests' sys.path pattern, unchanged).
  • Regressions fix(cot_agent): recover wrapped action JSON and surface ReAct parse failures #3703/fix(cot_agent): ReAct parser edge cases from #3705 (adjacent JSON, function envelopes, split think tags) #3706 preserved: tool_call wrappers, OpenAI function envelopes, adjacent roots, split think tags — all existing tests green.

Notes

  • Manifest bumped to 0.0.50 (0.0.49 is claimed by the open draft feat(anthropic): add Claude Opus 5.5 #3919 dependencies-refresh batch; no collision).
  • Scope: only the parser + the is_final_action predicate/call sites + tests + manifest version. No strategy-loop, budget, truncation, prompt, or YAML changes.

Release Notes

  • Fixed ReAct stream parsing: tool calls directly after a JSON blob are no longer dropped or leaked as thought text (}Action: / }FinalAnswer:), and live final-answer streaming is no longer blocked.
  • Innocent JSON in model thoughts (citations, links, {}) no longer triggers a spurious "Action parse failed" error.
  • FinalAnswer and final_answer JSON action names are now recognized as final answers.
  • A stream cut mid-prefix no longer loses the trailing characters.
  • Malformed tool inputs (e.g. arrays) no longer crash the parser (they are surfaced as a tool invoke error instead).

Change Type

  • Documentation / non-plugin change
  • Non-LLM plugin (tools, extensions, datasource, etc.)
  • Agent strategy plugin

…final-answer variants, dropped EOF prefixes

- Reset the prefix matcher state after a completed JSON blob flush so
  a prefix right after a blob ({...}Action: / {...}FinalAnswer:) is no
  longer leaked as thought text or blocks live answer streaming
  ({, }, tab and CR are now accepted as prefix delimiters).
- Only flag JSON blobs as parse failures when they are action-shaped
  (emitted after Action: or carrying action-ish keys); innocent JSON
  such as citations, markdown links or arbitrary objects is now plain
  thought text, so the round is no longer killed by a spurious
  "Action parse failed".
- Accept the compact "FinalAnswer" and underscore "final_answer" JSON
  action names as final answers via a shared is_final_action()
  predicate used by the strategy (the SDK AgentScratchpadUnit.is_final
  spellings remain covered).
- Flush partial prefix matcher caches at stream end so a response cut
  mid-prefix no longer silently drops those characters.
- Coerce malformed (non-dict/str) action_input to a JSON string so a
  list/scalar input never raises out of the parser.
@crazywoola
crazywoola temporarily deployed to agent-strategies/cot_agent September 28, 2026 02:35 — with GitHub Actions Inactive
@crazywoola
crazywoola merged commit 3b4e77c into langgenius:main Sep 28, 2026
4 checks passed

This branch was previously deployed

1 inactive deployment
agent-strategies/cot_agent — 456c7f64 Deployed Sep 28, 2026 by crazywoola via test (agent-strategies/cot_agent) #2795
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