From 2e3a20fcdfb4dfba05eee0c329f835eccbffbbf2 Mon Sep 17 00:00:00 2001 From: LIghtJUNction Date: Mon, 23 Mar 2026 01:07:21 +0800 Subject: [PATCH] fix: improve bool param validation and preserve None tools - Fix bool type checking in CommandFilter.validate_and_convert_params (was using isinstance(bool_instance, bool) instead of 'bool is bool') - Preserve tools=None when persona explicitly has no tools - Add missing provider_wake_prefix in test setup --- astrbot/core/star/filter/command.py | 9 ++++++--- astrbot/core/subagent_orchestrator.py | 6 +++++- tests/unit/test_internal_agent_sub_stage.py | 2 ++ 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/astrbot/core/star/filter/command.py b/astrbot/core/star/filter/command.py index c8df56e57..753c41237 100644 --- a/astrbot/core/star/filter/command.py +++ b/astrbot/core/star/filter/command.py @@ -136,7 +136,7 @@ class CommandFilter(HandlerFilter): elif isinstance(param_type_or_default_val, str): # 如果 param_type_or_default_val 是字符串,直接赋值 result[param_name] = params[i] - elif isinstance(param_type_or_default_val, bool): + elif param_type_or_default_val is bool: # 处理布尔类型 lower_param = str(params[i]).lower() if lower_param in ["true", "yes", "1"]: @@ -166,10 +166,13 @@ class CommandFilter(HandlerFilter): result[param_name] = params[i] else: result[param_name] = param_type_or_default_val(params[i]) - except ValueError: + except ValueError as e: + # Re-raise if we raised it ourselves with a custom message + if str(e).startswith("参数"): + raise raise ValueError( f"参数 {param_name} 类型错误。完整参数: {self.print_types()}", - ) + ) from e return result def get_complete_command_names(self): diff --git a/astrbot/core/subagent_orchestrator.py b/astrbot/core/subagent_orchestrator.py index 526a1f5a5..4e12cbed3 100644 --- a/astrbot/core/subagent_orchestrator.py +++ b/astrbot/core/subagent_orchestrator.py @@ -61,7 +61,7 @@ class SubAgentOrchestrator: provider_id = item.get("provider_id") if provider_id is not None: provider_id = str(provider_id).strip() or None - tools: list[str | FunctionTool] | None = item.get("tools", []) + tools: list[str | FunctionTool] | None = item.get("tools") begin_dialogs = None if persona_data: @@ -74,6 +74,10 @@ class SubAgentOrchestrator: persona_tools = persona_data.get("tools") if isinstance(persona_tools, list): tools = [str(t).strip() for t in persona_tools if str(t).strip()] + elif persona_tools is None: + # persona exists but explicitly has tools=None -> use None + # This preserves the case where persona has no tools + tools = None else: tools = [] if public_description == "" and prompt: diff --git a/tests/unit/test_internal_agent_sub_stage.py b/tests/unit/test_internal_agent_sub_stage.py index a9abe15bb..3b6abd095 100644 --- a/tests/unit/test_internal_agent_sub_stage.py +++ b/tests/unit/test_internal_agent_sub_stage.py @@ -28,6 +28,8 @@ async def test_internal_stage_uses_effective_runner_streaming_flag(): stage = InternalAgentSubStage() stage.ctx = MagicMock() stage.ctx.plugin_manager.context = MagicMock() + stage.ctx.astrbot_config = {"provider_settings": {"wake_prefix": "!"}} + stage.provider_wake_prefix = "!" stage.streaming_response = True stage.unsupported_streaming_strategy = "realtime_segmenting" stage.max_step = 5