Skip to content

fix(gateway,acp): deliver plugin-transformed final_response through streaming gate#31433

Merged
teknium1 merged 7 commits into
mainfrom
hermes/hermes-3714ac10
May 24, 2026
Merged

fix(gateway,acp): deliver plugin-transformed final_response through streaming gate#31433
teknium1 merged 7 commits into
mainfrom
hermes/hermes-3714ac10

Conversation

@teknium1

@teknium1 teknium1 commented May 24, 2026

Copy link
Copy Markdown
Contributor

Plugin hooks on transform_llm_output now reach end users on streaming platforms (Discord, Telegram, ACP, etc.). Before this, the hook's output was silently discarded after the stream completed.

Changes

  • agent/conversation_loop.py: set response_transformed=True in the run dict when a hook returns a non-empty string.
  • gateway/run.py: propagate the flag through run_sync(); when set, edit the existing streamed message in place with the transformed text instead of suppressing the final send (no duplicate message).
  • gateway/stream_consumer.py: expose message_id so the gateway can target the streamed bubble.
  • acp_adapter/server.py: deliver the final response after streaming when response_transformed=True (otherwise keep the existing not streamed_message guard so non-transformed streams don't double-send).
  • Regression tests in tests/gateway/test_run_progress_topics.py and tests/acp/test_server.py.

Validation

Before After
transform_llm_output hook output on streaming Discord/Telegram/Matrix silently dropped edited into the streamed message
transform_llm_output hook output via ACP dropped when streamed delivered as the final update_agent_message_text
Non-transformed streamed response on ACP single agent_message_chunk unchanged (no duplicate)
Normal streaming (no transform) already_sent=True, no duplicate unchanged

Targeted suite: 112 passed in tests/acp/test_server.py + tests/gateway/test_run_progress_topics.py + tests/test_transform_llm_output_hook.py.

Credit

Salvages PR #29119 by @kenyonxu — original commits cherry-picked, contributor authorship preserved.

Infographic

PR #31433 — plugin-transformed responses survive the streaming gate

kenyonxu and others added 6 commits May 24, 2026 04:01
…ut hook now visible

When streaming is active, streamed_message=True skipped the final_response
update, causing plugin hooks like transform_llm_output to be silently
invisible. Remove the `not streamed_message` guard so the final response
(possibly transformed by plugins) is always delivered to the ACP client.
…s streaming suppression

When a transform_llm_output hook modifies final_response after streaming,
the gateway was silently discarding the transformed content because
streamed=True / content_delivered=True triggered the final-send
suppression. Three changes:

1. conversation_loop: set `_response_transformed=True` when a
   transform_llm_output hook returns a non-empty string, and expose it
   as `response_transformed` in the result dict.

2. gateway/run: skip the final-send suppression when
   `response_transformed` is True — the transformed response must
   reach the client even if streaming already sent the original text.

3. acp_adapter/server: remove `not streamed_message` guard so
   final_response is always delivered (ACP path fixed separately).
…turn dict

run_sync() cherry-picks fields from the run_conversation result dict into
a new response dict for the gateway. response_transformed was missing from
the cherry-pick list, so the gateway always saw it as False and suppressed
the final send even though a transform_llm_output hook had modified the content.
… response_transformed

When a transform_llm_output hook appends content after streaming, the previous
fix skipped the final-send suppression which caused the full response to be
sent as a NEW message (duplicate). Instead, edit the existing streamed message
in-place to append the transformed content, then set already_sent=True.

Added stream_consumer.message_id and .accumulated_text public properties.
…ming

Adds a test that fails without the gateway fix, exercising the
response_transformed=True branch in _finalize_response: a streamed
response whose final text was modified by a transform_llm_output
plugin hook must be edit_message'd in place (not duplicate-sent),
with already_sent=True so the normal final-send is skipped.

Also drops two minor leftovers from the salvaged PR #29119:

  * accumulated_text property on GatewayStreamConsumer (unused)
  * duplicate _response_transformed=False inside the hook try block
@github-actions

github-actions Bot commented May 24, 2026

Copy link
Copy Markdown
Contributor

🔎 Lint report: hermes/hermes-3714ac10 vs origin/main

ruff

Total: 0 on HEAD, 0 on base (➖ 0)

🆕 New issues: none

✅ Fixed issues: none

Unchanged: 0 pre-existing issues carried over.

ty (type checker)

Total: 9067 on HEAD, 9067 on base (➖ 0)

🆕 New issues: none

✅ Fixed issues: none

Unchanged: 4826 pre-existing issues carried over.

Diagnostics are surfaced as warnings — this check never fails the build.

PR #29119 dropped the 'not streamed_message' guard unconditionally so
that plugin-transformed responses (transform_llm_output hook) would
reach ACP clients. That regressed test_prompt_does_not_duplicate_streamed_final_message:
when no transform happened, the streamed text was re-sent as a duplicate
final delivery.

Tighten the condition to mirror the gateway side: deliver after streaming
only when response_transformed=True. Otherwise keep the old guard.

Adds test_prompt_delivers_transformed_response_after_streaming so the
transformed path stays covered.
@alt-glitch alt-glitch added type/bug Something isn't working P3 Low — cosmetic, nice to have comp/gateway Gateway runner, session dispatch, delivery comp/acp Agent Communication Protocol adapter comp/agent Core agent loop, run_agent.py, prompt builder comp/plugins Plugin system and bundled plugins labels May 24, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

Supersedes #29119 (same fix, cherry-picked commits with authorship preserved, clean branch without the ~3295 graphify-out cache files). Also supersedes #29074 (ACP-only subset).

@teknium1 teknium1 merged commit 60d20a3 into main May 24, 2026
26 checks passed
@teknium1 teknium1 deleted the hermes/hermes-3714ac10 branch May 24, 2026 11:31
kenyonxu added a commit to kenyonxu/hermes-agent that referenced this pull request Jun 2, 2026
记录PR NousResearch#31433 (fix: plugin-transformed final_response通过streaming gate) 已被合并到main。
原PR由@kenyonxu提交(NousResearch#29119),由@teknium1 salvage并合并。
嵌入Nous Research官方info graphic。
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/acp Agent Communication Protocol adapter comp/agent Core agent loop, run_agent.py, prompt builder comp/gateway Gateway runner, session dispatch, delivery comp/plugins Plugin system and bundled plugins P3 Low — cosmetic, nice to have type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants