Coverage for agentos/agent/tests/test_tool_agent.py: 100%
75 statements
« prev ^ index » next coverage.py v7.14.3, created at 2026-07-09 10:28 +0800
« prev ^ index » next coverage.py v7.14.3, created at 2026-07-09 10:28 +0800
1"""agentos/agent/tool_agent.py 单元测试。"""
3import json
5from agentos.agent.tool_agent import (
6 AgentConfig,
7 AgentResult,
8 AgentStep,
9 ToolExecutor,
10)
11from agentos.llm.base import Tool, ToolCall, ToolParameter
13# ── 工具定义 ──────────────────────────────────────────────────────
15MOCK_TOOL = Tool.from_function(
16 name="double",
17 description="将输入数字翻倍",
18 parameters={"x": ToolParameter(type="number", description="输入数字")},
19)
22# ── ToolExecutor ─────────────────────────────────────────────────
25class TestToolExecutor:
27 def test_register_and_list_schemas(self):
28 ex = ToolExecutor()
29 ex.register(MOCK_TOOL, lambda x: str(x * 2))
30 schemas = ex.get_schemas()
31 assert len(schemas) == 1
32 assert schemas[0].function.name == "double"
34 def test_execute_success(self):
35 ex = ToolExecutor()
36 ex.register(MOCK_TOOL, lambda x: str(x * 2))
37 tc = ToolCall(id="c1", name="double", arguments=json.dumps({"x": 5}))
38 result = ex.execute(tc)
39 assert result == "10"
41 def test_execute_unknown_tool(self):
42 ex = ToolExecutor()
43 tc = ToolCall(id="c1", name="no_such", arguments=json.dumps({}))
44 result = ex.execute(tc)
45 assert "Unknown tool" in result
47 def test_execute_error(self):
48 ex = ToolExecutor()
49 ex.register(MOCK_TOOL, lambda x: str(int(x) * 2)) # int(x) would fail for non-number
50 tc = ToolCall(id="c1", name="double", arguments=json.dumps({"x": "NOT_A_NUMBER"}))
51 result = ex.execute(tc)
52 assert "error" in result
54 def test_multiple_register(self):
55 ex = ToolExecutor()
56 t2 = Tool.from_function(name="triple", description="三倍")
57 ex.register(MOCK_TOOL, lambda x: str(x * 2))
58 ex.register(t2, lambda: "tripled!")
59 assert len(ex.get_schemas()) == 2
60 assert ex.execute(ToolCall(id="c2", name="triple", arguments=json.dumps({}))) == "tripled!"
63# ── AgentConfig ──────────────────────────────────────────────────
66class TestAgentConfig:
68 def test_defaults(self):
69 c = AgentConfig()
70 assert c.max_steps == 10
71 assert c.temperature == 0.0
72 assert c.stop_on_error is True
73 assert c.verbose is False
75 def test_custom(self):
76 c = AgentConfig(max_steps=5, temperature=0.7, verbose=True, stop_on_error=False)
77 assert c.max_steps == 5
78 assert c.temperature == 0.7
79 assert c.verbose is True
80 assert c.stop_on_error is False
83# ── AgentStep ────────────────────────────────────────────────────
86class TestAgentStep:
88 def test_empty_step(self):
89 s = AgentStep(step=1)
90 assert s.step == 1
91 assert s.thought == ""
92 assert s.tool_calls == []
93 assert s.tool_results == {}
95 def test_full_step(self):
96 tc = ToolCall(id="c1", name="double", arguments=json.dumps({"x": 3}))
97 s = AgentStep(
98 step=2,
99 thought="Let me double 3",
100 tool_calls=[tc],
101 tool_results={"c1": "6"},
102 finish_reason="tool_calls",
103 tokens_used=150,
104 cost_usd=0.0002,
105 duration_ms=320.0,
106 )
107 assert s.step == 2
108 assert len(s.tool_calls) == 1
109 assert s.tool_results["c1"] == "6"
110 assert s.tokens_used == 150
111 assert s.cost_usd == 0.0002
112 assert s.duration_ms == 320.0
115# ── AgentResult ──────────────────────────────────────────────────
118class TestAgentResult:
120 def test_success_result(self):
121 r = AgentResult(
122 success=True,
123 final_answer="答案是 42",
124 total_steps=2,
125 total_tokens=500,
126 total_cost_usd=0.001,
127 total_duration_ms=1200,
128 )
129 assert r.success is True
130 assert r.final_answer == "答案是 42"
131 assert r.total_steps == 2
132 assert r.error is None
134 def test_failure_result(self):
135 r = AgentResult(success=False, final_answer="", total_steps=10, error="max steps")
136 assert r.success is False
137 assert r.error == "max steps"