fischer-agentkit/tests/unit/test_execution_modes.py

175 lines
6.9 KiB
Python

"""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"}