-
Notifications
You must be signed in to change notification settings - Fork 1k
fix(langchain): include content attribute when assistant messages have tool calls #3287
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 14 commits
81e1d56
220137b
43a49d5
1945e67
64c3c4c
868da16
5531949
71374eb
f0b1619
efa54f5
df3ad15
d06a826
0e1108d
50919c1
c4df270
ef8e8d1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,6 +1,7 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """Unit tests configuration module.""" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import os | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import sys | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import pytest | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from opentelemetry._events import get_event_logger | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -148,6 +149,30 @@ def environment(): | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| os.environ["LANGSMITH_API_KEY"] = "test" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @pytest.fixture(autouse=True) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def force_langsmith_requests(): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """Force langsmith to use requests instead of httpx for VCR compatibility.""" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # For Python 3.11+, we need to ensure langsmith uses requests instead of httpx | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # since VCR has better support for requests | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if sys.version_info >= (3, 11): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import requests | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Monkey patch langsmith to always use requests session | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import langsmith.client | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| original_langsmith_init = langsmith.client.Client.__init__ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def patched_langsmith_init(self, *args, **kwargs): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if "session" not in kwargs: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| kwargs["session"] = requests.Session() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return original_langsmith_init(self, *args, **kwargs) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| langsmith.client.Client.__init__ = patched_langsmith_init | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| except ImportError: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pass # langsmith not available | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Avoid global monkey-patch leaks and wrapper stacking; use pytest’s monkeypatch and handle session=None The autouse fixture patches langsmith.client.Client.init but never restores it. Because this is function-scoped and autouse, each test wraps the already-wrapped init, leading to stacked wrappers over the entire test run. This can cause confusing call stacks and performance degradation. Also, if a caller passes session=None, the intent (force requests) won’t be applied. Patch with pytest’s monkeypatch (auto-restores per test) and set a requests.Session when session is missing or None. Apply this diff: -@pytest.fixture(autouse=True)
-def force_langsmith_requests():
+@pytest.fixture(autouse=True)
+def force_langsmith_requests(monkeypatch):
"""Force langsmith to use requests instead of httpx for VCR compatibility."""
# For Python 3.11+, we need to ensure langsmith uses requests instead of httpx
# since VCR has better support for requests
- if sys.version_info >= (3, 11):
- import requests
-
- # Monkey patch langsmith to always use requests session
- try:
- import langsmith.client
-
- original_langsmith_init = langsmith.client.Client.__init__
-
- def patched_langsmith_init(self, *args, **kwargs):
- if "session" not in kwargs:
- kwargs["session"] = requests.Session()
- return original_langsmith_init(self, *args, **kwargs)
-
- langsmith.client.Client.__init__ = patched_langsmith_init
- except ImportError:
- pass # langsmith not available
+ if sys.version_info < (3, 11):
+ return
+ try:
+ import requests
+ import langsmith.client
+ except ImportError:
+ return # langsmith not available
+
+ original_langsmith_init = langsmith.client.Client.__init__
+
+ def patched_langsmith_init(self, *args, **kwargs):
+ if kwargs.get("session") is None:
+ kwargs["session"] = requests.Session()
+ return original_langsmith_init(self, *args, **kwargs)
+
+ # Ensure restoration after each test to avoid accumulating wrappers
+ monkeypatch.setattr(langsmith.client.Client, "__init__", patched_langsmith_init)📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @pytest.fixture(scope="module") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def vcr_config(): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def before_record_request(request): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -170,7 +195,10 @@ def before_record_request(request): | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return request | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "filter_headers": ["authorization", "x-api-key"], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "match_on": ["method", "scheme", "host", "port", "path", "query"], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "filter_headers": ["authorization", "x-api-key", "user-agent"], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "match_on": ["method", "scheme", "host", "port", "path"], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "before_record_request": before_record_request, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "ignore_localhost": True, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "decode_compressed_response": True, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "allow_playback_repeats": True, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,160 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Test for the fix of the issue where assistant message content is missing | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| when tool calls are present in LangGraph/LangChain instrumentation. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| This test reproduces the issue reported in GitHub where gen_ai.prompt.X.content | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| attributes were missing for assistant messages that contained tool_calls. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from unittest.mock import Mock | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from langchain_core.messages import AIMessage, HumanMessage, ToolMessage | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from opentelemetry.instrumentation.langchain.span_utils import set_chat_request | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from opentelemetry.semconv_ai import SpanAttributes | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def test_assistant_message_with_tool_calls_includes_content(): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Test that when an assistant message has both content and tool_calls, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| both the content and tool_calls are included in the span attributes. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| This addresses the issue where content was missing when tool_calls were present. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mock_span = Mock() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mock_span.set_attribute = Mock() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mock_span_holder = Mock() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mock_span_holder.request_model = None | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| messages = [ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| [ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| HumanMessage(content="what is the current time? First greet me."), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| AIMessage( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| content="Hello! Let me check the current time for you.", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| tool_calls=[ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "id": "call_qU7pH3EdQvzwkPyKPOdpgaKA", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "name": "get_current_time", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "args": {}, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ToolMessage( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| content="2025-08-15 08:15:21", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| tool_call_id="call_qU7pH3EdQvzwkPyKPOdpgaKA", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| AIMessage(content="The current time is 2025-08-15 08:15:21"), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| set_chat_request(mock_span, {}, messages, {}, mock_span_holder) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| call_args = [call[0] for call in mock_span.set_attribute.call_args_list] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| attributes = {args[0]: args[1] for args in call_args} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert f"{SpanAttributes.LLM_PROMPTS}.0.role" in attributes | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert attributes[f"{SpanAttributes.LLM_PROMPTS}.0.role"] == "user" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert f"{SpanAttributes.LLM_PROMPTS}.0.content" in attributes | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| attributes[f"{SpanAttributes.LLM_PROMPTS}.0.content"] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| == "what is the current time? First greet me." | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert f"{SpanAttributes.LLM_PROMPTS}.1.role" in attributes | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert attributes[f"{SpanAttributes.LLM_PROMPTS}.1.role"] == "assistant" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert f"{SpanAttributes.LLM_PROMPTS}.1.content" in attributes | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| attributes[f"{SpanAttributes.LLM_PROMPTS}.1.content"] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| == "Hello! Let me check the current time for you." | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert f"{SpanAttributes.LLM_PROMPTS}.1.tool_calls.0.id" in attributes | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| attributes[f"{SpanAttributes.LLM_PROMPTS}.1.tool_calls.0.id"] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| == "call_qU7pH3EdQvzwkPyKPOdpgaKA" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert f"{SpanAttributes.LLM_PROMPTS}.1.tool_calls.0.name" in attributes | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| attributes[f"{SpanAttributes.LLM_PROMPTS}.1.tool_calls.0.name"] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| == "get_current_time" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert f"{SpanAttributes.LLM_PROMPTS}.2.role" in attributes | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert attributes[f"{SpanAttributes.LLM_PROMPTS}.2.role"] == "tool" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert f"{SpanAttributes.LLM_PROMPTS}.2.content" in attributes | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| attributes[f"{SpanAttributes.LLM_PROMPTS}.2.content"] == "2025-08-15 08:15:21" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert f"{SpanAttributes.LLM_PROMPTS}.2.tool_call_id" in attributes | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| attributes[f"{SpanAttributes.LLM_PROMPTS}.2.tool_call_id"] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| == "call_qU7pH3EdQvzwkPyKPOdpgaKA" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert f"{SpanAttributes.LLM_PROMPTS}.3.role" in attributes | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert attributes[f"{SpanAttributes.LLM_PROMPTS}.3.role"] == "assistant" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert f"{SpanAttributes.LLM_PROMPTS}.3.content" in attributes | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| attributes[f"{SpanAttributes.LLM_PROMPTS}.3.content"] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| == "The current time is 2025-08-15 08:15:21" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def test_assistant_message_with_only_tool_calls_no_content(): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Test that when an assistant message has only tool_calls and no content, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| the tool_calls are still included and no content attribute is set. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+96
to
+100
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Docstring contradicts the intended fix; content should be present even with tool_calls The implementation now always sets - """
- Test that when an assistant message has only tool_calls and no content,
- the tool_calls are still included and no content attribute is set.
- """
+ """
+ Test that when an assistant message has only tool_calls and empty content,
+ both the tool_calls are included and the content attribute is set to an
+ empty string (per the fix to always include content).
+ """📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mock_span = Mock() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mock_span.set_attribute = Mock() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mock_span_holder = Mock() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mock_span_holder.request_model = None | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| messages = [ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| [ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| AIMessage( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| content="", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| tool_calls=[ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| {"id": "call_123", "name": "some_tool", "args": {"param": "value"}} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| set_chat_request(mock_span, {}, messages, {}, mock_span_holder) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| call_args = [call[0] for call in mock_span.set_attribute.call_args_list] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| attributes = {args[0]: args[1] for args in call_args} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert f"{SpanAttributes.LLM_PROMPTS}.0.role" in attributes | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert attributes[f"{SpanAttributes.LLM_PROMPTS}.0.role"] == "assistant" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert f"{SpanAttributes.LLM_PROMPTS}.0.content" not in attributes | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert f"{SpanAttributes.LLM_PROMPTS}.0.tool_calls.0.id" in attributes | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert attributes[f"{SpanAttributes.LLM_PROMPTS}.0.tool_calls.0.id"] == "call_123" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert f"{SpanAttributes.LLM_PROMPTS}.0.tool_calls.0.name" in attributes | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| attributes[f"{SpanAttributes.LLM_PROMPTS}.0.tool_calls.0.name"] == "some_tool" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+119
to
+130
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix test expectation: content attribute should exist (empty string) when only tool_calls are present The updated set_chat_request always emits - call_args = [call[0] for call in mock_span.set_attribute.call_args_list]
- attributes = {args[0]: args[1] for args in call_args}
+ attributes = {
+ c.args[0]: c.args[1] for c in mock_span.set_attribute.call_args_list
+ }
@@
- assert f"{SpanAttributes.LLM_PROMPTS}.0.content" not in attributes
+ assert f"{SpanAttributes.LLM_PROMPTS}.0.content" in attributes
+ assert attributes[f"{SpanAttributes.LLM_PROMPTS}.0.content"] == ""📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def test_assistant_message_with_only_content_no_tool_calls(): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Test that when an assistant message has only content and no tool_calls, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| the content is included and no tool_calls attributes are set. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mock_span = Mock() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mock_span.set_attribute = Mock() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mock_span_holder = Mock() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mock_span_holder.request_model = None | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| messages = [[AIMessage(content="Just a regular response with no tool calls")]] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| set_chat_request(mock_span, {}, messages, {}, mock_span_holder) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| call_args = [call[0] for call in mock_span.set_attribute.call_args_list] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| attributes = {args[0]: args[1] for args in call_args} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert f"{SpanAttributes.LLM_PROMPTS}.0.role" in attributes | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert attributes[f"{SpanAttributes.LLM_PROMPTS}.0.role"] == "assistant" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert f"{SpanAttributes.LLM_PROMPTS}.0.content" in attributes | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| attributes[f"{SpanAttributes.LLM_PROMPTS}.0.content"] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| == "Just a regular response with no tool calls" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| tool_call_attributes = [attr for attr in attributes.keys() if "tool_calls" in attr] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert len(tool_call_attributes) == 0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Repeated 'Free up disk space' step found in multiple jobs. Consider refactoring this block into a reusable composite action or workflow for DRY code.