"""U5: SkillConfig 扩展 + 专业 Agent 执行模式路由测试""" import os import pytest from datetime import datetime, timezone from unittest.mock import AsyncMock, MagicMock, patch import yaml from agentkit.skills.base import SkillConfig from agentkit.core.exceptions import ConfigValidationError from agentkit.core.protocol import TaskMessage def _make_task(**overrides): defaults = dict( task_id="t1", agent_name="test", task_type="test", priority=1, input_data={"query": "test"}, callback_url=None, created_at=datetime.now(timezone.utc), ) defaults.update(overrides) return TaskMessage(**defaults) class TestSkillConfigExecutionModes: """SkillConfig.VALID_EXECUTION_MODES 扩展测试""" def test_rewoo_is_valid_mode(self): config = SkillConfig(name="test_rewoo", agent_type="test", execution_mode="rewoo", prompt={"identity": "test", "instructions": "test"}) assert config.execution_mode == "rewoo" def test_plan_exec_is_valid_mode(self): config = SkillConfig(name="test_plan_exec", agent_type="test", execution_mode="plan_exec", prompt={"identity": "test", "instructions": "test"}) assert config.execution_mode == "plan_exec" def test_reflexion_is_valid_mode(self): config = SkillConfig(name="test_reflexion", agent_type="test", execution_mode="reflexion", prompt={"identity": "test", "instructions": "test"}) assert config.execution_mode == "reflexion" def test_existing_modes_still_valid(self): for mode in ("react", "direct", "custom"): config = SkillConfig(name=f"test_{mode}", agent_type="test", execution_mode=mode, prompt={"identity": "test", "instructions": "test"}) assert config.execution_mode == mode def test_invalid_mode_raises_error(self): with pytest.raises(ConfigValidationError): SkillConfig(name="test_invalid", agent_type="test", execution_mode="nonexistent", prompt={"identity": "test", "instructions": "test"}) def test_all_six_modes_in_valid_set(self): expected = {"react", "direct", "custom", "rewoo", "plan_exec", "reflexion"} assert SkillConfig.VALID_EXECUTION_MODES == expected class TestYAMLConfigLoading: """专业 Agent YAML 配置加载测试""" YAML_DIR = "/Users/Chiguyong/Code/Fischer/fischer-agentkit/configs/skills" def _load_yaml(self, filename): path = os.path.join(self.YAML_DIR, filename) with open(path) as f: return yaml.safe_load(f) def test_rewoo_agent_yaml_loads(self): data = self._load_yaml("rewoo_agent.yaml") config = SkillConfig(**data) assert config.execution_mode == "rewoo" assert config.agent_type == "parallel_data_fetch" def test_plan_exec_agent_yaml_loads(self): data = self._load_yaml("plan_exec_agent.yaml") config = SkillConfig(**data) assert config.execution_mode == "plan_exec" assert config.agent_type == "structured_planning" def test_reflexion_agent_yaml_loads(self): data = self._load_yaml("reflexion_agent.yaml") config = SkillConfig(**data) assert config.execution_mode == "reflexion" assert config.agent_type == "high_precision" def test_react_agent_yaml_loads(self): data = self._load_yaml("react_agent.yaml") config = SkillConfig(**data) assert config.execution_mode == "react" assert config.agent_type == "dynamic_tool_chain" def test_direct_agent_yaml_loads(self): data = self._load_yaml("direct_agent.yaml") config = SkillConfig(**data) assert config.execution_mode == "direct" assert config.agent_type == "simple_generation" def test_different_models_per_agent(self): direct_data = self._load_yaml("direct_agent.yaml") assert direct_data["llm"]["model"] == "openai/gpt-4o-mini" plan_data = self._load_yaml("plan_exec_agent.yaml") assert plan_data["llm"]["model"] == "anthropic/claude-opus-4-20250514" react_data = self._load_yaml("react_agent.yaml") assert react_data["llm"]["model"] == "anthropic/claude-sonnet-4-20250514" def test_direct_agent_has_no_tools(self): data = self._load_yaml("direct_agent.yaml") assert data["tools"] == [] def test_capabilities_parsed(self): data = self._load_yaml("react_agent.yaml") config = SkillConfig(**data) cap_tags = [c.tag if hasattr(c, 'tag') else c for c in config.capabilities] assert "dynamic_adaptation" in cap_tags class TestConfigDrivenAgentRouting: """ConfigDrivenAgent execution_mode 路由测试""" def _make_agent(self, execution_mode): from agentkit.core.config_driven import ConfigDrivenAgent from agentkit.llm.gateway import LLMGateway config = SkillConfig( name=f"test_{execution_mode}", agent_type="test", execution_mode=execution_mode, prompt={"identity": "test", "instructions": "test"}, ) llm_gateway = MagicMock(spec=LLMGateway) llm_gateway.chat = AsyncMock() agent = ConfigDrivenAgent(config=config, llm_gateway=llm_gateway) return agent @pytest.mark.asyncio async def test_rewoo_routes_to_handle_rewoo(self): agent = self._make_agent("rewoo") with patch.object(agent, '_handle_rewoo', new_callable=AsyncMock, return_value={"content": "rewoo result"}) as mock: result = await agent.handle_task(_make_task()) mock.assert_called_once() assert result == {"content": "rewoo result"} @pytest.mark.asyncio async def test_plan_exec_routes_to_handle_plan_exec(self): agent = self._make_agent("plan_exec") with patch.object(agent, '_handle_plan_exec', new_callable=AsyncMock, return_value={"content": "plan_exec result"}) as mock: result = await agent.handle_task(_make_task()) mock.assert_called_once() assert result == {"content": "plan_exec result"} @pytest.mark.asyncio async def test_reflexion_routes_to_handle_reflexion(self): agent = self._make_agent("reflexion") with patch.object(agent, '_handle_reflexion', new_callable=AsyncMock, return_value={"content": "reflexion result"}) as mock: result = await agent.handle_task(_make_task()) mock.assert_called_once() assert result == {"content": "reflexion result"} @pytest.mark.asyncio async def test_react_still_routes_correctly(self): agent = self._make_agent("react") with patch.object(agent, '_handle_react', new_callable=AsyncMock, return_value={"content": "react result"}) as mock: result = await agent.handle_task(_make_task()) mock.assert_called_once() assert result == {"content": "react result"}