|
12 | 12 | # See the License for the specific language governing permissions and |
13 | 13 | # limitations under the License. |
14 | 14 |
|
15 | | -"""Tests for Enhanced ToolCall Type Definition""" |
| 15 | +"""Tests for ToolCallRequest and ToolCall types""" |
16 | 16 |
|
17 | 17 | import pytest |
18 | 18 |
|
19 | 19 | from opentelemetry.util.genai.types import ( |
20 | 20 | InputMessage, |
21 | 21 | OutputMessage, |
22 | 22 | ToolCall, |
| 23 | + ToolCallRequest, |
23 | 24 | ) |
24 | 25 |
|
25 | 26 |
|
26 | | -def test_toolcall_backward_compatibility(): |
27 | | - """Test backward compatibility as message part""" |
28 | | - tc = ToolCall( |
| 27 | +def test_toolcallrequest_basic(): |
| 28 | + """Test basic ToolCallRequest instantiation""" |
| 29 | + tcr = ToolCallRequest(arguments=None, name="get_weather", id=None) |
| 30 | + assert tcr.name == "get_weather" |
| 31 | + assert tcr.type == "tool_call" |
| 32 | + assert tcr.arguments is None |
| 33 | + assert tcr.id is None |
| 34 | + |
| 35 | + |
| 36 | +def test_toolcallrequest_with_all_fields(): |
| 37 | + """Test ToolCallRequest with all fields""" |
| 38 | + tcr = ToolCallRequest( |
29 | 39 | name="get_weather", |
30 | 40 | arguments={"location": "Paris"}, |
31 | 41 | id="call_123", |
32 | 42 | ) |
33 | | - assert tc.name == "get_weather" |
34 | | - assert tc.arguments == {"location": "Paris"} |
35 | | - assert tc.id == "call_123" |
36 | | - assert tc.type == "tool_call" |
| 43 | + assert tcr.name == "get_weather" |
| 44 | + assert tcr.arguments == {"location": "Paris"} |
| 45 | + assert tcr.id == "call_123" |
| 46 | + assert tcr.type == "tool_call" |
37 | 47 |
|
38 | 48 |
|
39 | | -def test_toolcall_in_message(): |
40 | | - """Test ToolCall works as message part in InputMessage""" |
41 | | - tc = ToolCall(name="get_weather", arguments={"location": "Paris"}) |
42 | | - msg = InputMessage(role="user", parts=[tc]) |
| 49 | +def test_toolcallrequest_in_message(): |
| 50 | + """Test ToolCallRequest works as message part""" |
| 51 | + tcr = ToolCallRequest( |
| 52 | + arguments={"location": "Paris"}, name="get_weather", id=None |
| 53 | + ) |
| 54 | + msg = InputMessage(role="user", parts=[tcr]) |
43 | 55 | assert len(msg.parts) == 1 |
44 | | - assert msg.parts[0] == tc |
| 56 | + assert msg.parts[0] == tcr |
| 57 | + |
| 58 | + |
| 59 | +def test_toolcall_inherits_from_toolcallrequest(): |
| 60 | + """Test that ToolCall inherits from ToolCallRequest""" |
| 61 | + tc = ToolCall(arguments=None, name="get_weather", id=None) |
| 62 | + assert isinstance(tc, ToolCallRequest) |
| 63 | + assert isinstance(tc, ToolCall) |
| 64 | + |
45 | 65 |
|
| 66 | +def test_toolcall_has_execution_fields(): |
| 67 | + """Test ToolCall has execution-only fields""" |
| 68 | + tc = ToolCall(arguments=None, name="get_weather", id=None) |
| 69 | + assert hasattr(tc, "tool_type") |
| 70 | + assert hasattr(tc, "tool_description") |
| 71 | + assert hasattr(tc, "tool_result") |
| 72 | + assert hasattr(tc, "error_type") |
46 | 73 |
|
47 | | -def test_toolcall_full_lifecycle(): |
48 | | - """Test complete tool call lifecycle with all fields""" |
49 | | - # Start with tool call request |
| 74 | + |
| 75 | +def test_toolcall_execution_fields_default_none(): |
| 76 | + """Test ToolCall execution fields default to None""" |
| 77 | + tc = ToolCall(arguments=None, name="get_weather", id=None) |
| 78 | + assert tc.tool_type is None |
| 79 | + assert tc.tool_description is None |
| 80 | + assert tc.tool_result is None |
| 81 | + assert tc.error_type is None |
| 82 | + |
| 83 | + |
| 84 | +def test_toolcall_with_execution_fields(): |
| 85 | + """Test ToolCall with execution fields set""" |
50 | 86 | tc = ToolCall( |
51 | 87 | name="get_weather", |
52 | | - arguments={"location": "Paris", "units": "metric"}, |
53 | | - id="call_abc123", |
| 88 | + arguments={"location": "Paris"}, |
| 89 | + id="call_123", |
54 | 90 | tool_type="function", |
55 | | - tool_description="Retrieves current weather for a location", |
| 91 | + tool_description="Get current weather", |
| 92 | + tool_result={"temp": 20, "condition": "sunny"}, |
56 | 93 | ) |
57 | | - |
58 | | - # Simulate successful execution - set result |
59 | | - tc.tool_result = {"temperature": 15, "condition": "cloudy"} |
60 | | - |
61 | 94 | assert tc.name == "get_weather" |
62 | 95 | assert tc.tool_type == "function" |
63 | | - assert tc.tool_result is not None |
64 | | - assert tc.error_type is None |
| 96 | + assert tc.tool_description == "Get current weather" |
| 97 | + assert tc.tool_result == {"temp": 20, "condition": "sunny"} |
65 | 98 |
|
66 | | - # Simulate failed execution - set error |
67 | | - tc_failed = ToolCall( |
68 | | - name="get_weather", |
| 99 | + |
| 100 | +def test_toolcall_with_error(): |
| 101 | + """Test ToolCall with error_type set""" |
| 102 | + tc = ToolCall( |
69 | 103 | arguments={"location": "Invalid"}, |
70 | | - id="call_xyz789", |
71 | | - tool_type="function", |
| 104 | + name="get_weather", |
| 105 | + id=None, |
| 106 | + error_type="InvalidLocationError", |
72 | 107 | ) |
73 | | - tc_failed.error_type = "InvalidLocationError" |
74 | | - |
75 | | - assert tc_failed.error_type == "InvalidLocationError" |
76 | | - assert tc_failed.tool_result is None |
| 108 | + assert tc.error_type == "InvalidLocationError" |
| 109 | + assert tc.tool_result is None |
77 | 110 |
|
78 | 111 |
|
79 | | -def test_toolcall_with_output_message(): |
80 | | - """Test ToolCall in OutputMessage (backward compatibility)""" |
| 112 | +def test_toolcall_backward_compatibility(): |
| 113 | + """Test ToolCall still works as message part (backward compatibility)""" |
81 | 114 | tc = ToolCall( |
82 | 115 | name="get_weather", |
83 | 116 | arguments={"location": "Paris"}, |
84 | 117 | id="call_123", |
85 | 118 | ) |
86 | | - msg = OutputMessage( |
| 119 | + # Should work in messages |
| 120 | + msg = InputMessage(role="user", parts=[tc]) |
| 121 | + assert len(msg.parts) == 1 |
| 122 | + |
| 123 | + # Should work in output messages |
| 124 | + out_msg = OutputMessage( |
87 | 125 | role="assistant", parts=[tc], finish_reason="tool_calls" |
88 | 126 | ) |
| 127 | + assert len(out_msg.parts) == 1 |
89 | 128 |
|
90 | | - assert len(msg.parts) == 1 |
91 | | - assert msg.parts[0].name == "get_weather" |
92 | | - assert msg.finish_reason == "tool_calls" |
93 | 129 |
|
| 130 | +def test_toolcallrequest_no_execution_fields(): |
| 131 | + """Test that ToolCallRequest doesn't have execution fields""" |
| 132 | + tcr = ToolCallRequest(arguments=None, name="get_weather", id=None) |
| 133 | + # ToolCallRequest should only have message part fields |
| 134 | + assert not hasattr(tcr, "tool_type") |
| 135 | + assert not hasattr(tcr, "tool_description") |
| 136 | + assert not hasattr(tcr, "tool_result") |
| 137 | + assert not hasattr(tcr, "error_type") |
94 | 138 |
|
95 | | -def test_toolcall_field_values(): |
96 | | - """Test that ToolCall fields can be set and retrieved correctly""" |
| 139 | + |
| 140 | +def test_mixed_types_in_message(): |
| 141 | + """Test using both ToolCallRequest and ToolCall in messages""" |
| 142 | + tcr = ToolCallRequest(arguments=None, name="simple_tool", id=None) |
97 | 143 | tc = ToolCall( |
98 | | - name="get_weather", |
99 | | - id="call_123", |
100 | | - tool_type="function", |
101 | | - tool_description="Weather tool", |
102 | | - arguments={"location": "Paris"}, |
103 | | - tool_result={"temp": 20}, |
| 144 | + arguments=None, name="complex_tool", id=None, tool_type="function" |
104 | 145 | ) |
105 | 146 |
|
106 | | - # Verify all field values are set correctly |
107 | | - assert tc.name == "get_weather" |
108 | | - assert tc.id == "call_123" |
109 | | - assert tc.tool_type == "function" |
110 | | - assert tc.tool_description == "Weather tool" |
111 | | - assert tc.arguments == {"location": "Paris"} |
112 | | - assert tc.tool_result == {"temp": 20} |
113 | | - assert tc.error_type is None |
114 | | - |
115 | | - # Verify these fields map to semantic convention attributes: |
116 | | - # - name -> gen_ai.tool.name |
117 | | - # - id -> gen_ai.tool.call.id |
118 | | - # - tool_type -> gen_ai.tool.type |
119 | | - # - tool_description -> gen_ai.tool.description |
120 | | - # - arguments -> gen_ai.tool.call.arguments (Opt-In) |
121 | | - # - tool_result -> gen_ai.tool.call.result (Opt-In) |
122 | | - # - error_type -> error.type |
| 147 | + msg = InputMessage(role="user", parts=[tcr, tc]) |
| 148 | + assert len(msg.parts) == 2 |
| 149 | + assert isinstance(msg.parts[0], ToolCallRequest) |
| 150 | + assert isinstance(msg.parts[1], ToolCall) |
| 151 | + # ToolCall is also a ToolCallRequest |
| 152 | + assert isinstance(msg.parts[1], ToolCallRequest) |
| 153 | + |
| 154 | + |
| 155 | +def test_toolcall_tool_type_values(): |
| 156 | + """Test valid tool_type values""" |
| 157 | + for tool_type in ["function", "extension", "datastore"]: |
| 158 | + tc = ToolCall( |
| 159 | + arguments=None, name="test", id=None, tool_type=tool_type |
| 160 | + ) |
| 161 | + assert tc.tool_type == tool_type |
123 | 162 |
|
124 | 163 |
|
125 | 164 | if __name__ == "__main__": |
|
0 commit comments