From a0443e8f9e90b586448cac69df85f270884a511e Mon Sep 17 00:00:00 2001 From: hanyuanling Date: Tue, 28 Apr 2026 21:11:40 +0800 Subject: [PATCH] fix(channels): address progress override review --- nanobot/channels/manager.py | 18 +++---------- .../test_channel_manager_delta_coalescing.py | 26 +++++++++++++++++++ 2 files changed, 29 insertions(+), 15 deletions(-) diff --git a/nanobot/channels/manager.py b/nanobot/channels/manager.py index 81d13293c..e2fc0dafc 100644 --- a/nanobot/channels/manager.py +++ b/nanobot/channels/manager.py @@ -7,6 +7,7 @@ from pathlib import Path from typing import TYPE_CHECKING, Any from loguru import logger +from pydantic.alias_generators import to_camel from nanobot.bus.events import OutboundMessage from nanobot.bus.queue import MessageBus @@ -28,21 +29,8 @@ def _default_webui_dist() -> Path | None: return candidate if candidate.is_dir() else None -def _snake_to_camel(value: str) -> str: - head, *tail = value.split("_") - return head + "".join(part.capitalize() for part in tail) - - def _coerce_optional_bool(value: Any) -> bool | None: - if isinstance(value, bool): - return value - if isinstance(value, str): - normalized = value.strip().lower() - if normalized in {"true", "1", "yes", "on"}: - return True - if normalized in {"false", "0", "no", "off"}: - return False - return None + return value if isinstance(value, bool) else None # Retry delays for message sending (exponential backoff: 1s, 2s, 4s) @@ -161,7 +149,7 @@ class ChannelManager: if section is None: return None - camel_key = _snake_to_camel(key) + camel_key = to_camel(key) if isinstance(section, dict): value = section.get(key, section.get(camel_key)) return _coerce_optional_bool(value) diff --git a/tests/channels/test_channel_manager_delta_coalescing.py b/tests/channels/test_channel_manager_delta_coalescing.py index 7956c96f0..ea4b68334 100644 --- a/tests/channels/test_channel_manager_delta_coalescing.py +++ b/tests/channels/test_channel_manager_delta_coalescing.py @@ -325,6 +325,32 @@ class TestProgressFiltering: assert manager._should_send_progress("other", tool_hint=False) is True assert manager._should_send_progress("other", tool_hint=True) is False + def test_progress_visibility_uses_snake_case_channel_overrides(self, manager): + manager.config.channels = ChannelsConfig.model_validate({ + "sendProgress": True, + "sendToolHints": False, + "mock": { + "send_progress": False, + "send_tool_hints": True, + }, + }) + + assert manager._should_send_progress("mock", tool_hint=False) is False + assert manager._should_send_progress("mock", tool_hint=True) is True + + def test_progress_visibility_ignores_non_bool_channel_overrides(self, manager): + manager.config.channels = ChannelsConfig.model_validate({ + "sendProgress": True, + "sendToolHints": False, + "mock": { + "sendProgress": "false", + "sendToolHints": "true", + }, + }) + + assert manager._should_send_progress("mock", tool_hint=False) is True + assert manager._should_send_progress("mock", tool_hint=True) is False + @pytest.mark.asyncio async def test_channel_override_can_drop_progress_message(self, manager, bus): manager.config.channels = ChannelsConfig.model_validate({