Skip to content

Commit 32b3057

Browse files
Merge branch 'master' into webb/toxfix
2 parents 61d0f78 + 7d6ad08 commit 32b3057

File tree

6 files changed

+73
-20
lines changed

6 files changed

+73
-20
lines changed

.github/workflows/ai-integration-test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ jobs:
3434
token: ${{ secrets.GITHUB_TOKEN }}
3535

3636
- name: Run Python SDK Tests
37-
uses: getsentry/testing-ai-sdk-integrations@121da677853244cedfe11e95184b2b431af102eb
37+
uses: getsentry/testing-ai-sdk-integrations@285c012e522f241581534dfc89bd99ec3b1da4f6
3838
env:
3939
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
4040
with:

sentry_sdk/integrations/pydantic_ai/patches/tools.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ async def wrapped_execute_tool_call(
5050
call = validated.call
5151
name = call.tool_name
5252
tool = self.tools.get(name) if self.tools else None
53+
selected_tool_definition = getattr(tool, "tool_def", None)
5354

5455
# Determine tool type by checking tool.toolset
5556
tool_type = "function"
@@ -73,6 +74,7 @@ async def wrapped_execute_tool_call(
7374
args_dict,
7475
agent,
7576
tool_type=tool_type,
77+
tool_definition=selected_tool_definition,
7678
) as span:
7779
try:
7880
result = await original_execute_tool_call(
@@ -127,6 +129,7 @@ async def wrapped_call_tool(
127129
# Extract tool info before calling original
128130
name = call.tool_name
129131
tool = self.tools.get(name) if self.tools else None
132+
selected_tool_definition = getattr(tool, "tool_def", None)
130133

131134
# Determine tool type by checking tool.toolset
132135
tool_type = "function" # default
@@ -150,6 +153,7 @@ async def wrapped_call_tool(
150153
args_dict,
151154
agent,
152155
tool_type=tool_type,
156+
tool_definition=selected_tool_definition,
153157
) as span:
154158
try:
155159
result = await original_call_tool(

sentry_sdk/integrations/pydantic_ai/spans/execute_tool.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,15 @@
99

1010
if TYPE_CHECKING:
1111
from typing import Any, Optional
12+
from pydantic_ai._tool_manager import ToolDefinition # type: ignore
1213

1314

1415
def execute_tool_span(
15-
tool_name: str, tool_args: "Any", agent: "Any", tool_type: str = "function"
16+
tool_name: str,
17+
tool_args: "Any",
18+
agent: "Any",
19+
tool_type: str = "function",
20+
tool_definition: "Optional[ToolDefinition]" = None,
1621
) -> "sentry_sdk.tracing.Span":
1722
"""Create a span for tool execution.
1823
@@ -21,6 +26,7 @@ def execute_tool_span(
2126
tool_args: The arguments passed to the tool
2227
agent: The agent executing the tool
2328
tool_type: The type of tool ("function" for regular tools, "mcp" for MCP services)
29+
tool_definition: The definition of the tool, if available
2430
"""
2531
span = sentry_sdk.start_span(
2632
op=OP.GEN_AI_EXECUTE_TOOL,
@@ -32,6 +38,12 @@ def execute_tool_span(
3238
span.set_data(SPANDATA.GEN_AI_TOOL_TYPE, tool_type)
3339
span.set_data(SPANDATA.GEN_AI_TOOL_NAME, tool_name)
3440

41+
if tool_definition is not None and hasattr(tool_definition, "description"):
42+
span.set_data(
43+
SPANDATA.GEN_AI_TOOL_DESCRIPTION,
44+
tool_definition.description,
45+
)
46+
3547
_set_agent_data(span, agent)
3648

3749
if _should_send_prompts() and tool_args is not None:

tests/integrations/openai_agents/test_openai_agents.py

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1231,7 +1231,7 @@ def simple_test_tool(message: str) -> str:
12311231
"type": "object",
12321232
"additionalProperties": False,
12331233
},
1234-
"on_invoke_tool": "<function agents.tool.function_tool.<locals>._create_function_tool.<locals>._on_invoke_tool>",
1234+
"on_invoke_tool": mock.ANY,
12351235
"strict_json_schema": True,
12361236
"is_enabled": True,
12371237
}
@@ -1259,16 +1259,17 @@ def simple_test_tool(message: str) -> str:
12591259
}
12601260
)
12611261

1262-
available_tools = safe_serialize(available_tools)
1263-
12641262
assert transaction["transaction"] == "test_agent workflow"
12651263
assert transaction["contexts"]["trace"]["origin"] == "auto.ai.openai_agents"
12661264

12671265
assert agent_span["description"] == "invoke_agent test_agent"
12681266
assert agent_span["origin"] == "auto.ai.openai_agents"
12691267
assert agent_span["data"]["gen_ai.agent.name"] == "test_agent"
12701268
assert agent_span["data"]["gen_ai.operation.name"] == "invoke_agent"
1271-
assert agent_span["data"]["gen_ai.request.available_tools"] == available_tools
1269+
assert (
1270+
json.loads(agent_span["data"]["gen_ai.request.available_tools"])
1271+
== available_tools
1272+
)
12721273
assert agent_span["data"]["gen_ai.request.max_tokens"] == 100
12731274
assert agent_span["data"]["gen_ai.request.model"] == "gpt-4"
12741275
assert agent_span["data"]["gen_ai.request.temperature"] == 0.7
@@ -1279,7 +1280,10 @@ def simple_test_tool(message: str) -> str:
12791280
assert ai_client_span1["data"]["gen_ai.operation.name"] == "chat"
12801281
assert ai_client_span1["data"]["gen_ai.system"] == "openai"
12811282
assert ai_client_span1["data"]["gen_ai.agent.name"] == "test_agent"
1282-
assert ai_client_span1["data"]["gen_ai.request.available_tools"] == available_tools
1283+
assert (
1284+
json.loads(ai_client_span1["data"]["gen_ai.request.available_tools"])
1285+
== available_tools
1286+
)
12831287
assert ai_client_span1["data"]["gen_ai.request.max_tokens"] == 100
12841288
assert ai_client_span1["data"]["gen_ai.request.messages"] == safe_serialize(
12851289
[
@@ -1320,11 +1324,7 @@ def simple_test_tool(message: str) -> str:
13201324
assert tool_span["data"]["gen_ai.agent.name"] == "test_agent"
13211325
assert tool_span["data"]["gen_ai.operation.name"] == "execute_tool"
13221326
assert (
1323-
re.sub(
1324-
"<.*>(,)",
1325-
r"'NOT_CHECKED'\1",
1326-
agent_span["data"]["gen_ai.request.available_tools"],
1327-
)
1327+
json.loads(agent_span["data"]["gen_ai.request.available_tools"])
13281328
== available_tools
13291329
)
13301330
assert tool_span["data"]["gen_ai.request.max_tokens"] == 100
@@ -1342,11 +1342,7 @@ def simple_test_tool(message: str) -> str:
13421342
assert ai_client_span2["data"]["gen_ai.agent.name"] == "test_agent"
13431343
assert ai_client_span2["data"]["gen_ai.operation.name"] == "chat"
13441344
assert (
1345-
re.sub(
1346-
"<.*>(,)",
1347-
r"'NOT_CHECKED'\1",
1348-
agent_span["data"]["gen_ai.request.available_tools"],
1349-
)
1345+
json.loads(agent_span["data"]["gen_ai.request.available_tools"])
13501346
== available_tools
13511347
)
13521348
assert ai_client_span2["data"]["gen_ai.request.max_tokens"] == 100

tests/integrations/pydantic_ai/test_pydantic_ai.py

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
from pydantic_ai import Agent
1717
from pydantic_ai.messages import BinaryContent, UserPromptPart
1818
from pydantic_ai.usage import RequestUsage
19-
from pydantic_ai.models.test import TestModel
2019
from pydantic_ai.exceptions import ModelRetry, UnexpectedModelBehavior
2120

2221

@@ -2386,7 +2385,9 @@ async def test_execute_tool_span_with_mcp_type(sentry_init, capture_events):
23862385
Test execute_tool span with MCP tool type.
23872386
"""
23882387
import sentry_sdk
2389-
from sentry_sdk.integrations.pydantic_ai.spans.execute_tool import execute_tool_span
2388+
from sentry_sdk.integrations.pydantic_ai.spans.execute_tool import (
2389+
execute_tool_span,
2390+
)
23902391

23912392
sentry_init(
23922393
integrations=[PydanticAIIntegration()],
@@ -2794,3 +2795,42 @@ async def test_set_usage_data_with_cache_tokens(sentry_init, capture_events):
27942795
(span_data,) = event["spans"]
27952796
assert span_data["data"][SPANDATA.GEN_AI_USAGE_INPUT_TOKENS_CACHED] == 80
27962797
assert span_data["data"][SPANDATA.GEN_AI_USAGE_INPUT_TOKENS_CACHE_WRITE] == 20
2798+
2799+
2800+
@pytest.mark.asyncio
2801+
async def test_tool_description_in_execute_tool_span(sentry_init, capture_events):
2802+
"""
2803+
Test that tool description from the tool's docstring is included in execute_tool spans.
2804+
"""
2805+
agent = Agent(
2806+
"test",
2807+
name="test_agent",
2808+
system_prompt="You are a helpful test assistant.",
2809+
)
2810+
2811+
@agent.tool_plain
2812+
def multiply_numbers(a: int, b: int) -> int:
2813+
"""Multiply two numbers and return the product."""
2814+
return a * b
2815+
2816+
sentry_init(
2817+
integrations=[PydanticAIIntegration()],
2818+
traces_sample_rate=1.0,
2819+
send_default_pii=True,
2820+
)
2821+
2822+
events = capture_events()
2823+
2824+
result = await agent.run("What is 5 times 3?")
2825+
assert result is not None
2826+
2827+
(transaction,) = events
2828+
spans = transaction["spans"]
2829+
2830+
tool_spans = [s for s in spans if s["op"] == "gen_ai.execute_tool"]
2831+
assert len(tool_spans) >= 1
2832+
2833+
tool_span = tool_spans[0]
2834+
assert tool_span["data"]["gen_ai.tool.name"] == "multiply_numbers"
2835+
assert SPANDATA.GEN_AI_TOOL_DESCRIPTION in tool_span["data"]
2836+
assert "Multiply two numbers" in tool_span["data"][SPANDATA.GEN_AI_TOOL_DESCRIPTION]

tests/integrations/pyramid/test_pyramid.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import pytest
77
from pyramid.authorization import ACLAuthorizationPolicy
88
from pyramid.response import Response
9+
from packaging.version import Version
910
from werkzeug.test import Client
1011

1112
from sentry_sdk import capture_message, add_breadcrumb
@@ -18,7 +19,7 @@
1819
try:
1920
from importlib.metadata import version
2021

21-
PYRAMID_VERSION = tuple(map(int, version("pyramid").split(".")))
22+
PYRAMID_VERSION = Version(version("pyramid")).release
2223

2324
except ImportError:
2425
# < py3.8

0 commit comments

Comments
 (0)