diff --git a/hermes_cli/config.py b/hermes_cli/config.py index d5a063a97..cec27809f 100644 --- a/hermes_cli/config.py +++ b/hermes_cli/config.py @@ -1345,7 +1345,27 @@ DEFAULT_CONFIG = { # responses and content messages are never touched. Default 0 # (disabled) preserves prior behavior. "ephemeral_system_ttl": 0, - "platforms": {}, # Per-platform display overrides: {"telegram": {"tool_progress": "all"}, "slack": {"tool_progress": "off"}} + # Per-platform display/streaming overrides. Each key is a gateway + # platform ("telegram", "discord", "slack", …) mapping to a dict of + # display settings that override the global value for that platform + # only. A setting left unset here falls through to the global default. + # + # Shipped defaults encode the streaming experience that works best + # per platform: + # - Telegram has native animated draft streaming (sendMessageDraft), + # which is smooth, so streaming is on by default there. + # - Discord/Slack/etc. only have edit-based streaming (repeated + # editMessage), which flickers and is noticeably jankier, so + # streaming is off by default there. + # These are gap-fillers: a user who explicitly sets, e.g., + # display.platforms.discord.streaming: true keeps their value + # (config deep-merge has user values win over defaults). The global + # streaming.enabled master switch still gates everything — these + # per-platform flags only take effect once streaming is enabled. + "platforms": { + "telegram": {"streaming": True}, + "discord": {"streaming": False}, + }, # Gateway runtime-metadata footer appended to the FINAL message of a turn # (disabled by default to keep replies minimal). When enabled, renders # e.g. `model · 68% · ~/projects/hermes`. Per-platform overrides go under diff --git a/tests/gateway/test_per_platform_streaming_defaults.py b/tests/gateway/test_per_platform_streaming_defaults.py new file mode 100644 index 000000000..4b183e666 --- /dev/null +++ b/tests/gateway/test_per_platform_streaming_defaults.py @@ -0,0 +1,65 @@ +"""Per-platform streaming defaults + dashboard exposure. + +Streaming is smooth on Telegram (native sendMessageDraft) but flickers on +edit-only platforms like Discord. The shipped defaults encode that: +display.platforms.telegram.streaming=true, .discord.streaming=false. These are +gap-fillers (user values win via deep-merge) and, because the dashboard schema +is generated from DEFAULT_CONFIG, they automatically appear as editable toggles +in the web UI. +""" + +from __future__ import annotations + + +def test_default_per_platform_streaming_flags(): + from hermes_cli.config import DEFAULT_CONFIG + plats = DEFAULT_CONFIG["display"]["platforms"] + assert plats["telegram"]["streaming"] is True + assert plats["discord"]["streaming"] is False + + +def test_resolver_telegram_on_discord_off_when_global_enabled(): + """With global streaming on, the per-platform defaults make Telegram stream + and Discord not — matching the platforms' actual streaming quality.""" + from hermes_cli.config import DEFAULT_CONFIG + from gateway.display_config import resolve_display_setting + + cfg = dict(DEFAULT_CONFIG) + cfg["streaming"] = {"enabled": True, "transport": "auto"} + + def streams(plat): + ov = resolve_display_setting(cfg, plat, "streaming") + # global enabled; None override = follow global (True) + return True if ov is None else bool(ov) + + assert streams("telegram") is True + assert streams("discord") is False + # A platform with no default entry follows the global switch. + assert streams("slack") is True + + +def test_user_override_wins_over_default(): + """A user who explicitly enables Discord streaming keeps their value — the + default false must not clobber it (config deep-merge: user wins).""" + from hermes_cli.config import DEFAULT_CONFIG, _deep_merge + + user = {"display": {"platforms": {"discord": {"streaming": True}}}} + merged = _deep_merge(dict(DEFAULT_CONFIG), user) + assert merged["display"]["platforms"]["discord"]["streaming"] is True + # Partial override must not wipe the sibling telegram default. + assert merged["display"]["platforms"]["telegram"]["streaming"] is True + + +def test_dashboard_schema_exposes_per_platform_streaming(): + """Because the web settings schema is built from DEFAULT_CONFIG, the + per-platform streaming toggles surface in the dashboard automatically.""" + import pytest + pytest.importorskip("fastapi") # web_server requires fastapi/uvicorn + from hermes_cli.web_server import CONFIG_SCHEMA + + assert "display.platforms.telegram.streaming" in CONFIG_SCHEMA + assert "display.platforms.discord.streaming" in CONFIG_SCHEMA + assert CONFIG_SCHEMA["display.platforms.discord.streaming"]["type"] == "boolean" + # Global streaming controls are exposed too. + assert "streaming.enabled" in CONFIG_SCHEMA + assert "streaming.transport" in CONFIG_SCHEMA