From 24e20e6ae8a9389fa47f9fc38d304749a116fce4 Mon Sep 17 00:00:00 2001 From: Leon Date: Sat, 18 Apr 2026 00:08:06 +0800 Subject: [PATCH 1/3] fix: _load_show_reasoning should resolve per-platform config MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Delegates to resolve_display_setting() so the resolution order (platform override → global → default) stays in one place. The /reasoning on command saves to display.platforms..show_reasoning but _load_show_reasoning() only read the global display.show_reasoning. After a gateway restart, the in-memory _show_reasoning was always False even though per-platform config had it set to True. --- gateway/run.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/gateway/run.py b/gateway/run.py index ca07254268..d4230fd678 100644 --- a/gateway/run.py +++ b/gateway/run.py @@ -1236,15 +1236,23 @@ class GatewayRunner: return None @staticmethod - def _load_show_reasoning() -> bool: - """Load show_reasoning toggle from config.yaml display section.""" + def _load_show_reasoning(platform_key: str = "") -> bool: + """Load show_reasoning toggle — resolves per-platform then global. + + Delegates to resolve_display_setting() so the resolution order + (platform override → global → default) stays in one place. + """ try: + from gateway.display_config import resolve_display_setting import yaml as _y cfg_path = _hermes_home / "config.yaml" + user_cfg = {} if cfg_path.exists(): with open(cfg_path, encoding="utf-8") as _f: - cfg = _y.safe_load(_f) or {} - return bool(cfg.get("display", {}).get("show_reasoning", False)) + user_cfg = _y.safe_load(_f) or {} + return bool(resolve_display_setting( + user_cfg, platform_key, "show_reasoning", False + )) except Exception: pass return False @@ -6075,7 +6083,8 @@ class GatewayRunner: args = event.get_command_args().strip().lower() config_path = _hermes_home / "config.yaml" self._reasoning_config = self._load_reasoning_config() - self._show_reasoning = self._load_show_reasoning() + _pk = _platform_config_key(event.source.platform) + self._show_reasoning = self._load_show_reasoning(_pk) def _save_config_key(key_path: str, value): """Save a dot-separated key to config.yaml.""" From 5eb338a578b2a45894412f8a154112005baab922 Mon Sep 17 00:00:00 2001 From: Leon Date: Sat, 18 Apr 2026 00:29:53 +0800 Subject: [PATCH 2/3] test: add per-platform show_reasoning resolution tests --- tests/gateway/test_reasoning_command.py | 61 +++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/tests/gateway/test_reasoning_command.py b/tests/gateway/test_reasoning_command.py index e39ed1123d..5f41c7a7cc 100644 --- a/tests/gateway/test_reasoning_command.py +++ b/tests/gateway/test_reasoning_command.py @@ -99,6 +99,67 @@ class TestReasoningCommand: assert runner._reasoning_config == {"enabled": False} assert runner._show_reasoning is True + @pytest.mark.asyncio + async def test_load_show_reasoning_resolves_per_platform(self, tmp_path, monkeypatch): + """Per-platform show_reasoning overrides global setting.""" + hermes_home = tmp_path / "hermes" + hermes_home.mkdir() + (hermes_home / "config.yaml").write_text( + "display:\n" + " show_reasoning: false\n" + " platforms:\n" + " wecom:\n" + " show_reasoning: true\n", + encoding="utf-8", + ) + monkeypatch.setattr(gateway_run, "_hermes_home", hermes_home) + + # Per-platform: wecom should pick up the override + assert gateway_run.GatewayRunner._load_show_reasoning("wecom") is True + # Per-platform: telegram has no override → falls back to global (false) + assert gateway_run.GatewayRunner._load_show_reasoning("telegram") is False + # No platform key → global (false) + assert gateway_run.GatewayRunner._load_show_reasoning("") is False + + @pytest.mark.asyncio + async def test_load_show_reasoning_global_fallback(self, tmp_path, monkeypatch): + """Global show_reasoning is used when no per-platform override exists.""" + hermes_home = tmp_path / "hermes" + hermes_home.mkdir() + (hermes_home / "config.yaml").write_text( + "display:\n" + " show_reasoning: true\n", + encoding="utf-8", + ) + monkeypatch.setattr(gateway_run, "_hermes_home", hermes_home) + + assert gateway_run.GatewayRunner._load_show_reasoning("wecom") is True + assert gateway_run.GatewayRunner._load_show_reasoning("telegram") is True + assert gateway_run.GatewayRunner._load_show_reasoning("") is True + + @pytest.mark.asyncio + async def test_reasoning_command_per_platform_status(self, tmp_path, monkeypatch): + """The /reasoning status display reflects per-platform config.""" + hermes_home = tmp_path / "hermes" + hermes_home.mkdir() + (hermes_home / "config.yaml").write_text( + "display:\n" + " show_reasoning: false\n" + " platforms:\n" + " wecom:\n" + " show_reasoning: true\n", + encoding="utf-8", + ) + monkeypatch.setattr(gateway_run, "_hermes_home", hermes_home) + + runner = _make_runner() + runner._reasoning_config = None + # Simulate a WeCom message + event = _make_event(text="/reasoning", platform=Platform.WECOM) + result = await runner._handle_reasoning_command(event) + + assert "**Display:** on ✓" in result + @pytest.mark.asyncio async def test_handle_reasoning_command_updates_config_and_cache(self, tmp_path, monkeypatch): hermes_home = tmp_path / "hermes" From c6ec3e91f38532c199976f5e9ad5f87ac6a8c563 Mon Sep 17 00:00:00 2001 From: Leon Date: Mon, 20 Apr 2026 01:45:11 +0800 Subject: [PATCH 3/3] fix(gateway): remove _show_reasoning cache, always resolve per-platform config Remove GatewayRunner._show_reasoning field that was initialized once at startup using only global display.show_reasoning config. All consumers now resolve show_reasoning per-platform via resolve_display_setting(), ensuring display.platforms..show_reasoning overrides work correctly for all platforms (especially WeCom). Changes: - Delete self._show_reasoning initialization in __init__ - Update _handle_reasoning_command to resolve per-platform on each call - Update reasoning_callback wiring to resolve per-platform - Update response prepend logic to resolve per-platform - Remove fallback to cached self._show_reasoning Fixes: WeCom reasoning display respects per-platform config Co-Authored-By: Claude Sonnet 4.6 --- gateway/run.py | 20 +++++++++++++------- tests/gateway/test_reasoning_command.py | 3 --- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/gateway/run.py b/gateway/run.py index feb55eb1d6..ab46ba3495 100644 --- a/gateway/run.py +++ b/gateway/run.py @@ -624,7 +624,6 @@ class GatewayRunner: self._ephemeral_system_prompt = self._load_ephemeral_system_prompt() self._reasoning_config = self._load_reasoning_config() self._service_tier = self._load_service_tier() - self._show_reasoning = self._load_show_reasoning() self._busy_input_mode = self._load_busy_input_mode() self._restart_drain_timeout = self._load_restart_drain_timeout() self._provider_routing = self._load_provider_routing() @@ -4401,10 +4400,10 @@ class GatewayRunner: _load_gateway_config(), _platform_config_key(source.platform), "show_reasoning", - getattr(self, "_show_reasoning", False), + False, ) except Exception: - _show_reasoning_effective = getattr(self, "_show_reasoning", False) + _show_reasoning_effective = False if _show_reasoning_effective and response: last_reasoning = agent_result.get("last_reasoning") if last_reasoning: @@ -6597,7 +6596,6 @@ class GatewayRunner: config_path = _hermes_home / "config.yaml" self._reasoning_config = self._load_reasoning_config() _pk = _platform_config_key(event.source.platform) - self._show_reasoning = self._load_show_reasoning(_pk) def _save_config_key(key_path: str, value): """Save a dot-separated key to config.yaml.""" @@ -6628,7 +6626,17 @@ class GatewayRunner: level = "none (disabled)" else: level = rc.get("effort", "medium") - display_state = "on ✓" if self._show_reasoning else "off" + try: + from gateway.display_config import resolve_display_setting as _rds + _show_reasoning = bool(_rds( + _load_gateway_config(), + _pk, + "show_reasoning", + False, + )) + except Exception: + _show_reasoning = False + display_state = "on ✓" if _show_reasoning else "off" return ( "🧠 **Reasoning Settings**\n\n" f"**Effort:** `{level}`\n" @@ -6639,7 +6647,6 @@ class GatewayRunner: # Display toggle (per-platform) platform_key = _platform_config_key(event.source.platform) if args in ("show", "on"): - self._show_reasoning = True _save_config_key(f"display.platforms.{platform_key}.show_reasoning", True) return ( "🧠 ✓ Reasoning display: **ON**\n" @@ -6647,7 +6654,6 @@ class GatewayRunner: ) if args in ("hide", "off"): - self._show_reasoning = False _save_config_key(f"display.platforms.{platform_key}.show_reasoning", False) return f"🧠 ✓ Reasoning display: **OFF** for **{platform_key}**" diff --git a/tests/gateway/test_reasoning_command.py b/tests/gateway/test_reasoning_command.py index 5f41c7a7cc..0cb6cb9b13 100644 --- a/tests/gateway/test_reasoning_command.py +++ b/tests/gateway/test_reasoning_command.py @@ -33,7 +33,6 @@ def _make_runner(): runner._ephemeral_system_prompt = "" runner._prefill_messages = [] runner._reasoning_config = None - runner._show_reasoning = False runner._provider_routing = {} runner._fallback_model = None runner._running_agents = {} @@ -90,14 +89,12 @@ class TestReasoningCommand: runner = _make_runner() runner._reasoning_config = {"enabled": True, "effort": "xhigh"} - runner._show_reasoning = False result = await runner._handle_reasoning_command(_make_event("/reasoning")) assert "**Effort:** `none (disabled)`" in result assert "**Display:** on ✓" in result assert runner._reasoning_config == {"enabled": False} - assert runner._show_reasoning is True @pytest.mark.asyncio async def test_load_show_reasoning_resolves_per_platform(self, tmp_path, monkeypatch):