@@ -17,166 +17,145 @@ def test_assistant_message_with_tool_calls_includes_content():
17
17
"""
18
18
Test that when an assistant message has both content and tool_calls,
19
19
both the content and tool_calls are included in the span attributes.
20
-
20
+
21
21
This addresses the issue where content was missing when tool_calls were present.
22
22
"""
23
- # Create a mock span
24
23
mock_span = Mock ()
25
24
mock_span .set_attribute = Mock ()
26
-
27
- # Create a mock span_holder
28
25
mock_span_holder = Mock ()
29
26
mock_span_holder .request_model = None
30
-
31
- # Create messages that reproduce the issue:
32
- # 1. User message
33
- # 2. Assistant message with BOTH content AND tool_calls
34
- messages = [[
35
- HumanMessage (content = "what is the current time? First greet me." ),
36
- AIMessage (
37
- content = "Hello! Let me check the current time for you." ,
38
- tool_calls = [{
39
- 'id' : 'call_qU7pH3EdQvzwkPyKPOdpgaKA' ,
40
- 'name' : 'get_current_time' ,
41
- 'args' : {}
42
- }]
43
- ),
44
- ToolMessage (
45
- content = "2025-08-15 08:15:21" ,
46
- tool_call_id = "call_qU7pH3EdQvzwkPyKPOdpgaKA"
47
- ),
48
- AIMessage (content = "The current time is 2025-08-15 08:15:21" )
49
- ]]
50
-
51
- # Call the function that was previously buggy
27
+ messages = [
28
+ [
29
+ HumanMessage (content = "what is the current time? First greet me." ),
30
+ AIMessage (
31
+ content = "Hello! Let me check the current time for you." ,
32
+ tool_calls = [
33
+ {
34
+ "id" : "call_qU7pH3EdQvzwkPyKPOdpgaKA" ,
35
+ "name" : "get_current_time" ,
36
+ "args" : {},
37
+ }
38
+ ],
39
+ ),
40
+ ToolMessage (
41
+ content = "2025-08-15 08:15:21" ,
42
+ tool_call_id = "call_qU7pH3EdQvzwkPyKPOdpgaKA" ,
43
+ ),
44
+ AIMessage (content = "The current time is 2025-08-15 08:15:21" ),
45
+ ]
46
+ ]
47
+
52
48
set_chat_request (mock_span , {}, messages , {}, mock_span_holder )
53
-
54
- # Verify that set_attribute was called with the expected attributes
49
+
55
50
call_args = [call [0 ] for call in mock_span .set_attribute .call_args_list ]
56
-
57
- # Extract all attribute names and values
58
51
attributes = {args [0 ]: args [1 ] for args in call_args }
59
-
60
- # Check user message (prompt.0)
52
+
61
53
assert f"{ SpanAttributes .LLM_PROMPTS } .0.role" in attributes
62
54
assert attributes [f"{ SpanAttributes .LLM_PROMPTS } .0.role" ] == "user"
63
55
assert f"{ SpanAttributes .LLM_PROMPTS } .0.content" in attributes
64
- assert attributes [ f" { SpanAttributes . LLM_PROMPTS } .0.content" ] == "what is the current time? First greet me."
65
-
66
- # Check assistant message with tool calls (prompt.1)
67
- # This is the critical test - BOTH content AND tool_calls should be present
56
+ assert (
57
+ attributes [ f" { SpanAttributes . LLM_PROMPTS } .0.content" ]
58
+ == "what is the current time? First greet me."
59
+ )
68
60
assert f"{ SpanAttributes .LLM_PROMPTS } .1.role" in attributes
69
61
assert attributes [f"{ SpanAttributes .LLM_PROMPTS } .1.role" ] == "assistant"
70
-
71
- # The fix should ensure that content is present even when tool_calls exist
72
62
assert f"{ SpanAttributes .LLM_PROMPTS } .1.content" in attributes
73
- assert attributes [f"{ SpanAttributes .LLM_PROMPTS } .1.content" ] == "Hello! Let me check the current time for you."
74
-
75
- # Tool calls should also be present
63
+ assert (
64
+ attributes [f"{ SpanAttributes .LLM_PROMPTS } .1.content" ]
65
+ == "Hello! Let me check the current time for you."
66
+ )
76
67
assert f"{ SpanAttributes .LLM_PROMPTS } .1.tool_calls.0.id" in attributes
77
- assert attributes [f"{ SpanAttributes .LLM_PROMPTS } .1.tool_calls.0.id" ] == "call_qU7pH3EdQvzwkPyKPOdpgaKA"
78
- assert f"{ SpanAttributes .LLM_PROMPTS } .1.tool_calls.0.name" in attributes
79
- assert attributes [f"{ SpanAttributes .LLM_PROMPTS } .1.tool_calls.0.name" ] == "get_current_time"
80
-
81
- # Check tool message (prompt.2)
68
+ assert (
69
+ attributes [f"{ SpanAttributes .LLM_PROMPTS } .1.tool_calls.0.id" ]
70
+ == "call_qU7pH3EdQvzwkPyKPOdpgaKA"
71
+ )
72
+ assert f"{ SpanAttributes .LLM_PROMPTS } .1.tool_calls.0.name" in attributes
73
+ assert (
74
+ attributes [f"{ SpanAttributes .LLM_PROMPTS } .1.tool_calls.0.name" ]
75
+ == "get_current_time"
76
+ )
82
77
assert f"{ SpanAttributes .LLM_PROMPTS } .2.role" in attributes
83
78
assert attributes [f"{ SpanAttributes .LLM_PROMPTS } .2.role" ] == "tool"
84
79
assert f"{ SpanAttributes .LLM_PROMPTS } .2.content" in attributes
85
- assert attributes [f"{ SpanAttributes .LLM_PROMPTS } .2.content" ] == "2025-08-15 08:15:21"
80
+ assert (
81
+ attributes [f"{ SpanAttributes .LLM_PROMPTS } .2.content" ] == "2025-08-15 08:15:21"
82
+ )
86
83
assert f"{ SpanAttributes .LLM_PROMPTS } .2.tool_call_id" in attributes
87
- assert attributes [f"{ SpanAttributes .LLM_PROMPTS } .2.tool_call_id" ] == "call_qU7pH3EdQvzwkPyKPOdpgaKA"
88
-
89
- # Check final assistant message (prompt.3)
84
+ assert (
85
+ attributes [f"{ SpanAttributes .LLM_PROMPTS } .2.tool_call_id" ]
86
+ == "call_qU7pH3EdQvzwkPyKPOdpgaKA"
87
+ )
90
88
assert f"{ SpanAttributes .LLM_PROMPTS } .3.role" in attributes
91
89
assert attributes [f"{ SpanAttributes .LLM_PROMPTS } .3.role" ] == "assistant"
92
90
assert f"{ SpanAttributes .LLM_PROMPTS } .3.content" in attributes
93
- assert attributes [f"{ SpanAttributes .LLM_PROMPTS } .3.content" ] == "The current time is 2025-08-15 08:15:21"
91
+ assert (
92
+ attributes [f"{ SpanAttributes .LLM_PROMPTS } .3.content" ]
93
+ == "The current time is 2025-08-15 08:15:21"
94
+ )
94
95
95
96
96
97
def test_assistant_message_with_only_tool_calls_no_content ():
97
98
"""
98
99
Test that when an assistant message has only tool_calls and no content,
99
100
the tool_calls are still included and no content attribute is set.
100
101
"""
101
- # Create a mock span
102
102
mock_span = Mock ()
103
103
mock_span .set_attribute = Mock ()
104
-
105
- # Create a mock span_holder
106
104
mock_span_holder = Mock ()
107
105
mock_span_holder .request_model = None
108
-
109
- # Create message with only tool_calls, no content
110
- messages = [[
111
- AIMessage (
112
- content = "" , # Empty content
113
- tool_calls = [{
114
- 'id' : 'call_123' ,
115
- 'name' : 'some_tool' ,
116
- 'args' : {'param' : 'value' }
117
- }]
118
- )
119
- ]]
120
-
121
- # Call the function
106
+
107
+ messages = [
108
+ [
109
+ AIMessage (
110
+ content = "" ,
111
+ tool_calls = [
112
+ {"id" : "call_123" , "name" : "some_tool" , "args" : {"param" : "value" }}
113
+ ],
114
+ )
115
+ ]
116
+ ]
117
+
122
118
set_chat_request (mock_span , {}, messages , {}, mock_span_holder )
123
-
124
- # Verify that set_attribute was called with the expected attributes
119
+
125
120
call_args = [call [0 ] for call in mock_span .set_attribute .call_args_list ]
126
-
127
- # Extract all attribute names and values
128
121
attributes = {args [0 ]: args [1 ] for args in call_args }
129
-
130
- # Check assistant message
122
+
131
123
assert f"{ SpanAttributes .LLM_PROMPTS } .0.role" in attributes
132
124
assert attributes [f"{ SpanAttributes .LLM_PROMPTS } .0.role" ] == "assistant"
133
-
134
- # Content should NOT be set when it's empty (due to _set_span_attribute logic)
135
- # This is expected behavior to avoid cluttering spans with empty values
136
125
assert f"{ SpanAttributes .LLM_PROMPTS } .0.content" not in attributes
137
-
138
- # Tool calls should be present
139
126
assert f"{ SpanAttributes .LLM_PROMPTS } .0.tool_calls.0.id" in attributes
140
127
assert attributes [f"{ SpanAttributes .LLM_PROMPTS } .0.tool_calls.0.id" ] == "call_123"
141
128
assert f"{ SpanAttributes .LLM_PROMPTS } .0.tool_calls.0.name" in attributes
142
- assert attributes [f"{ SpanAttributes .LLM_PROMPTS } .0.tool_calls.0.name" ] == "some_tool"
129
+ assert (
130
+ attributes [f"{ SpanAttributes .LLM_PROMPTS } .0.tool_calls.0.name" ] == "some_tool"
131
+ )
143
132
144
133
145
134
def test_assistant_message_with_only_content_no_tool_calls ():
146
135
"""
147
136
Test that when an assistant message has only content and no tool_calls,
148
137
the content is included and no tool_calls attributes are set.
149
138
"""
150
- # Create a mock span
151
139
mock_span = Mock ()
152
140
mock_span .set_attribute = Mock ()
153
-
154
- # Create a mock span_holder
155
141
mock_span_holder = Mock ()
156
142
mock_span_holder .request_model = None
157
-
158
- # Create message with only content, no tool_calls
159
- messages = [[
160
- AIMessage (content = "Just a regular response with no tool calls" )
161
- ]]
162
-
163
- # Call the function
143
+
144
+ messages = [[AIMessage (content = "Just a regular response with no tool calls" )]]
145
+
164
146
set_chat_request (mock_span , {}, messages , {}, mock_span_holder )
165
-
166
- # Verify that set_attribute was called with the expected attributes
147
+
167
148
call_args = [call [0 ] for call in mock_span .set_attribute .call_args_list ]
168
-
169
- # Extract all attribute names and values
149
+
170
150
attributes = {args [0 ]: args [1 ] for args in call_args }
171
-
172
- # Check assistant message
151
+
173
152
assert f"{ SpanAttributes .LLM_PROMPTS } .0.role" in attributes
174
153
assert attributes [f"{ SpanAttributes .LLM_PROMPTS } .0.role" ] == "assistant"
175
-
176
- # Content should be present
177
154
assert f"{ SpanAttributes .LLM_PROMPTS } .0.content" in attributes
178
- assert attributes [f"{ SpanAttributes .LLM_PROMPTS } .0.content" ] == "Just a regular response with no tool calls"
179
-
180
- # No tool call attributes should be present
155
+ assert (
156
+ attributes [f"{ SpanAttributes .LLM_PROMPTS } .0.content" ]
157
+ == "Just a regular response with no tool calls"
158
+ )
159
+
181
160
tool_call_attributes = [attr for attr in attributes .keys () if "tool_calls" in attr ]
182
- assert len (tool_call_attributes ) == 0
161
+ assert len (tool_call_attributes ) == 0
0 commit comments