Support client-side Harmony format parsing and stateful streaming scrubbing - #46330
Support client-side Harmony format parsing and stateful streaming scrubbing#46330tsaipifong wants to merge 1 commit into
Conversation
…streaming scrubbing
|
Thanks for tackling a real compatibility gap: current Problems
Suggested changes
This is an automated hermes-sweeper review. |
|
I run local models over the same The diff is almost entirely line endings. Of the +10,093/−9,525, every removed line reappears identically with CRLF appended - the branch converts This reaches beyond gpt-oss. Every harmony leak captured in my local message store came from the gemma4 family via Ollama: three on
The sweeper's bypass point applies to my PR as well. I have #62680 open for the degraded shapes (scrubbing at the persist/CLI/stream sites); its streaming gate also hangs off The two PRs cover different halves of the bug: yours catches canonical tokens anywhere in the stream and extracts text-form tool calls, mine handles the degraded shapes and the persisted-content path. I'd rather reconcile than land two overlapping scrubbers - happy to rebase mine around yours, or fold my leak fixtures into your tests, whichever direction the maintainers prefer. |
Support Client-Side Harmony Format Parsing & Stateful Streaming Scrubbing
This pull request implements support for client-side Harmony format parsing and stateful streaming scrubbing for standard OpenAI-compatible endpoints under the
chat_completionstransport.The Problem
When running local/custom models (such as
gpt-oss-120bvia LM Studio) that generate standard Harmony tag schemas under the permissivechat_completionstransport:<|channel|>and<|end|>) remained in the user-visible content, and tool calls were not extracted if the model returned them as plain text rather than native structuredtool_calls.The Solution
Client-Side Non-Streaming Parser:
_parse_harmony_tool_callsinChatCompletionsTransport(chat_completions.py) using a brace-matching parser (_extract_json_object).<|channel|>) and text-style (leakedto=functions.tool_name) formats.ToolCallobjects and cleans the visible text response, settingfinish_reason = "tool_calls".Stateful Streaming Scrubber:
StreamingHarmonyScrubber(agent/harmony_scrubber.py) using a state machine (SCANNING->IN_HEADER->IN_CHANNEL_FINAL/IN_CHANNEL_SUPPRESSED) to parse streaming tokens incrementally.commentary,analysis,thought) entirely from real-time terminal output, preventing leaked JSON payloads and internal headers from flashing in the terminal.agent_init.py), turn context resetting (turn_context.py), and stream callbacks/flushing (run_agent.py).Robust Test Suite:
test_harmony_scrubber.py.test_turn_context.pyandtest_chat_completions.py(totaling 136 passing tests).