From 7a1b2bfe72bdf8acbd175c0f06ec2bcd26243f79 Mon Sep 17 00:00:00 2001 From: AJV20 <24819659+AJV20@users.noreply.github.com> Date: Sun, 31 May 2026 21:40:35 -0400 Subject: [PATCH] fix: carry WebUI reply polish into gateway chat --- CHANGELOG.md | 1 + api/gateway_chat.py | 6 +++++- api/streaming.py | 3 ++- tests/test_webui_gateway_chat_backend.py | 8 +++++++- tests/test_webui_surface_context.py | 2 ++ 5 files changed, 17 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f9ede1a..ae0a235b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Fixed - `ctl.sh` now honors `HERMES_WEBUI_NO_DOTENV=1`, letting tests and scripted launches opt out of repo-local `.env` loading so host-specific `HERMES_WEBUI_STATE_DIR` values do not make the ctl test suite flaky (#3246). +- Gateway-backed chat now carries the same WebUI final-answer polish guidance as the in-process chat paths, so terse scratchpad fragments such as "Need script" are not encouraged as visible assistant replies. ## [v0.51.192] — 2026-05-31 — Release FL (stage-batch4 — per-model context_length default-only guard) diff --git a/api/gateway_chat.py b/api/gateway_chat.py index 6b323a35..5676f913 100644 --- a/api/gateway_chat.py +++ b/api/gateway_chat.py @@ -247,13 +247,17 @@ def _run_gateway_chat_streaming( cfg = get_config() try: from api.streaming import ( + _WEBUI_PROGRESS_PROMPT, _load_webui_prefill_context, _prefill_messages_with_webui_context, _public_prefill_context_status, ) prefill_context = _load_webui_prefill_context(cfg) - prefill_messages = _prefill_messages_with_webui_context(prefill_context, cfg) + prefill_messages = [ + {"role": "system", "content": _WEBUI_PROGRESS_PROMPT}, + *_prefill_messages_with_webui_context(prefill_context, cfg), + ] put_gateway_event("context_status", { "session_id": session_id, "prefill": _public_prefill_context_status(prefill_context), diff --git a/api/streaming.py b/api/streaming.py index 11d8c9e9..610e559a 100644 --- a/api/streaming.py +++ b/api/streaming.py @@ -203,7 +203,8 @@ WebUI progress guidance: - Each update should say what you are about to check, what you just confirmed, or why the next tool call is needed. - Keep updates concise, factual, and in the user's language. One or two short sentences are enough. - Do not reveal hidden reasoning, chain-of-thought, private scratchpads, secrets, raw logs, or long tool output. -- Do not include terse planning fragments or scratchpad shorthand in visible assistant text. Avoid fragments like "Need check logs", "Need inspect email", or "maybe invite"; either omit them or rewrite them as clear user-facing progress. +- Final visible assistant replies must be clear user-facing English, not private planning notes. +- Do not include terse planning fragments or scratchpad shorthand in visible assistant text. Avoid fragments like "Need script", "Need check logs", "Need inspect email", or "maybe invite"; either omit them or rewrite them as clear user-facing progress. - For direct answers or very short tasks, skip progress updates and answer normally. """.strip() diff --git a/tests/test_webui_gateway_chat_backend.py b/tests/test_webui_gateway_chat_backend.py index 9f3291f4..35f64265 100644 --- a/tests/test_webui_gateway_chat_backend.py +++ b/tests/test_webui_gateway_chat_backend.py @@ -286,10 +286,14 @@ def test_gateway_chat_worker_translates_sse_and_persists_session(tmp_path, monke assert '"stream": true' in captured["body"] payload = json.loads(captured["body"]) assert [m["content"] for m in payload["messages"]] == [ + streaming._WEBUI_PROGRESS_PROMPT, "prefill", "webui session context", "Say hello", ] + assert payload["messages"][0]["role"] == "system" + assert "Final visible assistant replies" in payload["messages"][0]["content"] + assert "Need script" in payload["messages"][0]["content"] events = [] while not subscriber.empty(): events.append(subscriber.get_nowait()) @@ -361,7 +365,9 @@ def test_gateway_chat_worker_forwards_image_attachments_as_multimodal_parts(tmp_ ) content = captured["body"]["messages"][-1]["content"] - assert captured["body"]["messages"][0] == {"role": "user", "content": "webui session context"} + assert captured["body"]["messages"][0]["role"] == "system" + assert "Final visible assistant replies" in captured["body"]["messages"][0]["content"] + assert captured["body"]["messages"][1] == {"role": "user", "content": "webui session context"} assert content[0] == {"type": "text", "text": "What is in this image?"} assert content[1]["type"] == "image_url" assert content[1]["image_url"]["url"].startswith("data:image/png;base64,") diff --git a/tests/test_webui_surface_context.py b/tests/test_webui_surface_context.py index c613db2f..e42c7240 100644 --- a/tests/test_webui_surface_context.py +++ b/tests/test_webui_surface_context.py @@ -26,6 +26,8 @@ def test_webui_ephemeral_prompt_includes_browser_surface_context(): assert "explicit captures" in prompt assert "durable user preferences" in prompt assert "Do not include terse planning fragments" in prompt + assert "Final visible assistant replies" in prompt + assert "Need script" in prompt assert "Need inspect email" in prompt assert "clear user-facing progress" in prompt