HERMES-GIELIE/tests/test_webui_surface_context.py

178 lines
6.4 KiB
Python
Raw Normal View History

Release v0.51.262 — Release ID (stage-r12) (#3617) ## Release v0.51.262 — Release ID (stage-r12) Phase-3 light slice (no-screenshot items) — 3 PRs. ### Fixed | Issue/PR | Author | Fix | |----------|--------|-----| | #3432 (#3532) | @franksong2702 | Normalize the recall-prefill terminal `user` turn so WebUI doesn't send adjacent `user` roles to strict chat templates (Mistral/Gemma/Jinja). `_normalize_prefill_messages_before_user_turn()` in both `streaming.py` + `gateway_chat.py`; drops only the terminal user tail, preserves assistant/system/mid-list context. **Rebased onto master** (was CONFLICTING). | | #2558 (#3516) | @rodboev | "Reveal in file manager" now translates container workspace paths (`/workspace`) back to the host mount path for Docker deployments (traversal-safe via `safe_resolve` + sibling-prefix guard). | ### Changed | Issue/PR | Author | Change | |----------|--------|--------| | (#3539) | @Lyr-GW | Completed the Chinese (Simplified) `zh` localization (MCP controls, tool-list pagination) with all interpolations preserved, and the language dropdown now applies the locale **instantly** on change. | ### Review fix absorbed (Codex) #3539 also added an `allowed=['en','zh']` filter to the Settings language dropdown, which dropped the 9 other shipped locales (it/ja/ru/es/de/pt/ko/fr/tr) — and since save falls back to `en` when the select has no matching option, an existing user of those locales would be **silently reset to English** on a Settings save. Removed the filter (dropdown enumerates all `LOCALES` again, matching master); partially-translated locales fall back per-key to English at render. + regression test `test_issue3539_language_dropdown_all_locales.py`. ### Gate - Full pytest suite: **7701 passed, 0 failed** - ESLint: CLEAN · ruff: CLEAN · browser-smoke: CLEAN - Codex (regression): SHIP-ONLY-WITH-FIXES (dropdown drops-locales) → fixed → **SAFE TO SHIP** (verified prefill drops only terminal user tail in both paths, Docker path-translation traversal-safe, zh interpolations preserved) Co-authored-by: franksong2702 <franksong2702@users.noreply.github.com> Co-authored-by: rodboev <rodboev@users.noreply.github.com> Co-authored-by: Lyr-GW <Lyr-GW@users.noreply.github.com>
2026-06-05 00:16:31 +00:00
import logging
from api.streaming import (
_normalize_prefill_messages_before_user_turn,
_prefill_messages_with_webui_context,
_webui_ephemeral_system_prompt,
)
def test_webui_ephemeral_prompt_includes_browser_surface_context():
prompt = _webui_ephemeral_system_prompt(
"Use a concise tone.",
surface_context={
"source": "webui",
"session_id": "session-123",
"profile": "default",
"workspace": "/tmp/example-workspace",
},
)
assert "Use a concise tone." in prompt
assert "WebUI session context" in prompt
assert "Source: webui" in prompt
assert "Session ID: session-123" in prompt
assert "Profile: default" in prompt
assert "Workspace: /tmp/example-workspace" in prompt
assert "not the same live transcript as Telegram" in prompt
assert "Do not copy or dump this browser transcript" in prompt
2026-05-29 16:14:00 +00:00
assert "Write to external notes or durable memory only" in prompt
assert "otherwise leave notes unchanged" in prompt
assert "what note/section changed" in prompt
assert "explicit captures" in prompt
assert "durable user preferences" in prompt
2026-05-29 17:01:14 +00:00
assert "Do not include terse planning fragments" in prompt
assert "Final visible assistant replies" in prompt
assert "user-facing English" not in prompt
assert "in the user's language" in prompt
assert "Need script" in prompt
2026-05-29 17:01:14 +00:00
assert "Need inspect email" in prompt
assert "clear user-facing progress" in prompt
def test_webui_ephemeral_prompt_skips_empty_surface_fields():
prompt = _webui_ephemeral_system_prompt(
None,
surface_context={
"source": "webui",
"session_id": "",
"profile": None,
"workspace": " ",
},
)
assert "WebUI session context" in prompt
assert "Source: webui" in prompt
assert "Session ID:" not in prompt
assert "Profile:" not in prompt
assert "Workspace:" not in prompt
def test_ephemeral_prompt_avoids_platform_info_when_no_config():
"""Without config_data, the delivery context falls back to defaults."""
prompt = _webui_ephemeral_system_prompt(
"Be concise.",
surface_context={"source": "webui"},
)
# Core platform headings should still appear (fallback data).
assert "Connected Platforms:" in prompt
assert "Delivery options for scheduled tasks:" in prompt
# But home channels are only present when the config has them.
assert "Home Channels" not in prompt
def test_prefill_no_longer_adds_session_context_user_message():
"""_prefill_messages_with_webui_context must NOT append a user message.
Strict chat templates (Mistral, Gemma) require user/assistant alternation.
Adding a 'user' session context message creates two consecutive user turns.
"""
prefill = {"messages": [{"role": "system", "content": "recall note"}]}
result = _prefill_messages_with_webui_context(prefill)
assert len(result) == 1
assert result[0]["role"] == "system"
assert "Connected Platforms" not in result[0].get("content", "")
Release v0.51.262 — Release ID (stage-r12) (#3617) ## Release v0.51.262 — Release ID (stage-r12) Phase-3 light slice (no-screenshot items) — 3 PRs. ### Fixed | Issue/PR | Author | Fix | |----------|--------|-----| | #3432 (#3532) | @franksong2702 | Normalize the recall-prefill terminal `user` turn so WebUI doesn't send adjacent `user` roles to strict chat templates (Mistral/Gemma/Jinja). `_normalize_prefill_messages_before_user_turn()` in both `streaming.py` + `gateway_chat.py`; drops only the terminal user tail, preserves assistant/system/mid-list context. **Rebased onto master** (was CONFLICTING). | | #2558 (#3516) | @rodboev | "Reveal in file manager" now translates container workspace paths (`/workspace`) back to the host mount path for Docker deployments (traversal-safe via `safe_resolve` + sibling-prefix guard). | ### Changed | Issue/PR | Author | Change | |----------|--------|--------| | (#3539) | @Lyr-GW | Completed the Chinese (Simplified) `zh` localization (MCP controls, tool-list pagination) with all interpolations preserved, and the language dropdown now applies the locale **instantly** on change. | ### Review fix absorbed (Codex) #3539 also added an `allowed=['en','zh']` filter to the Settings language dropdown, which dropped the 9 other shipped locales (it/ja/ru/es/de/pt/ko/fr/tr) — and since save falls back to `en` when the select has no matching option, an existing user of those locales would be **silently reset to English** on a Settings save. Removed the filter (dropdown enumerates all `LOCALES` again, matching master); partially-translated locales fall back per-key to English at render. + regression test `test_issue3539_language_dropdown_all_locales.py`. ### Gate - Full pytest suite: **7701 passed, 0 failed** - ESLint: CLEAN · ruff: CLEAN · browser-smoke: CLEAN - Codex (regression): SHIP-ONLY-WITH-FIXES (dropdown drops-locales) → fixed → **SAFE TO SHIP** (verified prefill drops only terminal user tail in both paths, Docker path-translation traversal-safe, zh interpolations preserved) Co-authored-by: franksong2702 <franksong2702@users.noreply.github.com> Co-authored-by: rodboev <rodboev@users.noreply.github.com> Co-authored-by: Lyr-GW <Lyr-GW@users.noreply.github.com>
2026-06-05 00:16:31 +00:00
def test_prefill_boundary_normalizer_removes_terminal_user_tail():
"""Trailing user messages are removed until the first non-user boundary."""
raw = [
{"role": "assistant", "content": "recall summary"},
{"role": "user", "content": "session context"},
]
assert _normalize_prefill_messages_before_user_turn(raw) == [
{"role": "assistant", "content": "recall summary"},
]
assert _normalize_prefill_messages_before_user_turn([
{"role": "assistant", "content": "prefill"},
{"role": "user", "content": "legacy user"},
{"role": "assistant", "content": "assistant follow-up"},
]) == [
{"role": "assistant", "content": "prefill"},
{"role": "user", "content": "legacy user"},
{"role": "assistant", "content": "assistant follow-up"},
]
assert _normalize_prefill_messages_before_user_turn([
{"role": "assistant", "content": "tail",},
{"role": "user", "content": "first"},
{"role": "user", "content": "second"},
]) == [
{"role": "assistant", "content": "tail",},
]
assert _normalize_prefill_messages_before_user_turn([]) == []
assert _normalize_prefill_messages_before_user_turn([{"role": "user", "content": "only user"}]) == []
def test_prefill_boundary_normalizer_logs_when_user_tail_dropped(caplog):
"""Dropping trailing user messages is logged with the count."""
caplog.set_level(logging.DEBUG, logger="api.streaming")
messages = [
{"role": "assistant", "content": "prefill"},
{"role": "user", "content": "first"},
{"role": "user", "content": "second"},
]
assert _normalize_prefill_messages_before_user_turn(messages) == [
{"role": "assistant", "content": "prefill"},
]
assert any(
rec.message == "Dropped 2 trailing user message(s) from prefill" for rec in caplog.records
)
def test_prefill_boundary_normalizer_no_log_when_no_terminal_user(caplog):
"""No-op normalization must not emit the prefill-dropping debug log."""
caplog.set_level(logging.DEBUG, logger="api.streaming")
messages = [
{"role": "assistant", "content": "turn 1"},
{"role": "user", "content": "turn 2"},
{"role": "assistant", "content": "turn 3"},
]
assert _normalize_prefill_messages_before_user_turn(messages) == messages
assert len(caplog.records) == 0
def test_prefill_preserves_empty_and_none_messages():
"""Edge cases: empty prefill stays empty, missing key returns empty."""
assert _prefill_messages_with_webui_context({"messages": []}) == []
assert _prefill_messages_with_webui_context({}) == []
assert _prefill_messages_with_webui_context({"messages": None}) == []
def test_delivery_context_includes_home_channels_when_configured():
"""When config_data has platforms with a home_channel, the prompt includes it."""
config = {
"platforms": {
"telegram": {
"enabled": True,
"home_channel": {"name": "General"},
},
},
}
prompt = _webui_ephemeral_system_prompt(
None,
surface_context={"source": "webui"},
config_data=config,
)
assert "Connected Platforms:" in prompt
assert "Home Channels (default destinations):" in prompt
assert "telegram: General" in prompt
assert "telegram" in prompt and "Home channel" in prompt