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

1"""agentos/agent/tool_agent.py 单元测试。""" 

2 

3import json 

4 

5from agentos.agent.tool_agent import ( 

6 AgentConfig, 

7 AgentResult, 

8 AgentStep, 

9 ToolExecutor, 

10) 

11from agentos.llm.base import Tool, ToolCall, ToolParameter 

12 

13# ── 工具定义 ────────────────────────────────────────────────────── 

14 

15MOCK_TOOL = Tool.from_function( 

16 name="double", 

17 description="将输入数字翻倍", 

18 parameters={"x": ToolParameter(type="number", description="输入数字")}, 

19) 

20 

21 

22# ── ToolExecutor ───────────────────────────────────────────────── 

23 

24 

25class TestToolExecutor: 

26 

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" 

33 

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" 

40 

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 

46 

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 

53 

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

61 

62 

63# ── AgentConfig ────────────────────────────────────────────────── 

64 

65 

66class TestAgentConfig: 

67 

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 

74 

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 

81 

82 

83# ── AgentStep ──────────────────────────────────────────────────── 

84 

85 

86class TestAgentStep: 

87 

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 == {} 

94 

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 

113 

114 

115# ── AgentResult ────────────────────────────────────────────────── 

116 

117 

118class TestAgentResult: 

119 

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 

133 

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"