fix(gateway): forward Codex commentary to API Server streaming clients (#67580) - #67593
fix(gateway): forward Codex commentary to API Server streaming clients (#67580)#67593PRATHAMESH75 wants to merge 3 commits into
Conversation
|
Thanks for tracing the callback boundary and keeping commentary separate from final content. The session-SSE wiring is consistent with the existing Codex callback contract. Problems
Suggested changes
Automated hermes-sweeper review. |
|
Thanks for the review — addressed both points in fcff829e7. 1.
2. Documentation. Updated
Tests: |
Related to #67580: this is its focused API-server streaming implementation. |
|
Thanks @teknium1 — both points addressed in
|
|
Thanks for the review — both points are now addressed in
So the PR no longer narrows to the session endpoint — both supported streaming surfaces now forward commentary, with tests and docs for each. |
Related to #67705: the current Responses-stream implementation emits commentary as a completed typed item without output_text.delta. This is an event-contract decision for maintainers, not a duplicate relationship. |
28a6e68 to
956527d
Compare
SummaryTwo open PRs address #67580 by wiring Codex commentary into API Server streaming. #67593 covers both session SSE and Related pull requests
Duplicates#67593 and #67613 duplicate the same callback wiring and session-SSE commentary event; #67613 is the narrower subset of #67593. Suggested consolidationKeep open with a salvage path for #67593: retain its issue-specific callback wiring, duplicate suppression, two-surface coverage, tests, and documentation, while reviewing or splitting the unrelated test additions within its large diff. Close #67613 as duplicate of #67593 because its narrower implementation retains the duplicate-emission defect and omits the Responses surface, tests, and documentation. Complex graphflowchart LR
classDef open fill:#dbeafe,stroke:#1d4ed8,color:#1e3a8a
classDef merged fill:#dcfce7,stroke:#15803d,color:#14532d
classDef closed fill:#e5e7eb,stroke:#6b7280,color:#1f2937
classDef unverified fill:#f3f4f6,stroke:#9ca3af,color:#374151
classDef best stroke-width:3px,stroke:#b45309
classDef target stroke-width:3px,stroke:#4338ca
I67580(["issue #67580 (open)"])
subgraph Dup67593 ["PRs duplicating each other"]
P67593["PR #67593 (open)"]
P67613["PR #67613 (open)"]
end
P67593 -->|best fix| I67580
class I67580 open
class P67593 open
class P67613 open
class P67593 best
class P67593 target
click I67580 "https://github.com/NousResearch/hermes-agent/issues/67580"
click P67593 "https://github.com/NousResearch/hermes-agent/pull/67593"
click P67613 "https://github.com/NousResearch/hermes-agent/pull/67613"
Graph: solid arrow = fixes / best fix, dashed arrow = partial or unverified (see edge label); boxed group = PRs duplicating each other; amber border = best fix; indigo border = target; gray node = closed (state tag in the node label). Cross-PR triage: Reviewed 2 pull requests and 1 issue in this complex. Each diff was read against this issue; Assessment working set: 46 kB of PR diffs, 13 kB of issue/PR text, 7 kB of discussion (9 comments), 3 verify verdicts. verdicts reflect diff content, not PR titles. Part of an automated triage batch. |
956527d to
cd0a7fd
Compare
NousResearch#67580) Codex emits user-facing progress preambles as phase="commentary" assistant items, which the core routes through interim_assistant_callback. The API Server adapter forwarded stream_delta / tool callbacks but never the interim callback, so long tool-heavy turns stayed silent until the final answer on the session chat stream endpoint. Wire interim_assistant_callback through APIServerAdapter._create_agent and _run_agent, and emit commentary on the session chat stream as a distinct typed assistant.commentary event. Commentary is never concatenated into the final answer, an already-streamed interim is dropped to avoid duplicates, and analysis/reasoning stays on the reasoning path (no CoT leak). The core display.show_commentary=false gate still suppresses commentary entirely.
…ument event Extends NousResearch#67580 to the second API Server streaming surface. The Responses SSE handler now wires interim_assistant_callback and emits Codex phase="commentary" preambles as a distinct assistant message output item carrying "phase": "commentary" — separate from the final answer item and never fed into final_text_parts, so it is never concatenated into final content. An already-streamed interim is dropped to avoid duplicates, and analysis/reasoning stays on the reasoning path (no CoT leak), matching the session-stream behavior. Also documents the new assistant.commentary session event and the Responses commentary item in the API Server feature docs, and adds a /v1/responses regression test (distinct item, phase marker, duplicate drop, final answer stays clean).
…tip test hermes_state now guards writes to a session ended by compression, so the test's replace_messages(source_id, []) setup step tripped CompressionSessionClosedError. Drop it: the /messages endpoint follows the compression tip regardless of the source's residual messages, which is what the test asserts.
cd0a7fd to
4bfd630
Compare
fix(gateway): forward Codex commentary to API Server streaming clients (#67580)
|
What does this PR do?
When Hermes uses the
openai-codexbackend behind API Server, Codex emits user-facing progress preambles as assistant items withphase="commentary". The core already separates these — routing them toon_commentary_message→interim_assistant_callback, whilephase="analysis"stays on the reasoning path and final text uses the normal text callback (added in #66115).But
APIServerAdapterforwardedstream_delta_callback, tool-progress, tool-start, and tool-complete callbacks toAIAgentand neverinterim_assistant_callback. So on the streaming session endpoint (POST /api/sessions/{id}/chat/stream), long tool-heavy turns stayed silent until the final answer — the commentary was generated and stored incodex_message_itemsbut lost at the API Server boundary, making active work indistinguishable from a stalled run.This wires the existing callback through the two adapter methods and surfaces commentary on the session stream as a distinct, typed
assistant.commentaryevent — never concatenated into the final answer.Related Issue
Fixes #67580
Type of Change
Changes Made
gateway/platforms/api_server.py_create_agent()— acceptinterim_assistant_callbackand forward it to theAIAgent(...)constructor (stored on the agent viaagent_init, same attribute the native gateway sets)._run_agent()— acceptinterim_assistant_callbackand forward it to_create_agent()._handle_session_chat_stream()— add a_commentarycallback that enqueues a typedassistant.commentarySSE event, and pass it asinterim_assistant_callbackto_run_agent(). It dropsalready_streamed=Trueinterims (no duplicate of text already sent viaassistant.delta) and only emits non-empty text.tests/gateway/test_api_server.py—test_create_agent_forwards_interim_assistant_callback: asserts_create_agentforwards the callback toAIAgent.tests/gateway/test_session_api.py—test_session_chat_stream_emits_codex_commentary_as_typed_event: asserts a commentary event is emitted with its text, analready_streamedinterim is dropped, and commentary is not concatenated into the finalassistant.completedcontent.Design notes / non-goals
phase="analysis", raw chain-of-thought, and reasoning summaries never reach this callback (the core keeps them on the reasoning path), so no CoT leaks through the new event.display.show_commentary: falsegate still suppresses commentary entirely (the core doesn't fire the interim callback when it's off), so quiet behavior is preserved.How to Test
The two new tests fail without the wiring: the stream handler must pass
interim_assistant_callback=_commentary(otherwisekwargs["interim_assistant_callback"]KeyErrors in the fake run and noassistant.commentaryevent is produced), and_create_agentmust forward it toAIAgent.Checklist
Code
fix(scope):,feat(scope):, etc.)Documentation & Housekeeping
docs/, docstrings) — or N/Acli-config.yaml.exampleif I added/changed config keys — or N/A (no new config keys)CONTRIBUTING.mdorAGENTS.mdif I changed architecture or workflows — or N/A