Skip to content

Commit e8d5f1b

Browse files
fix(integrations): properly handle exceptions in tool calls (#5065)
### Description Properly wrap tool calls in try/except and capture exceptions so that the Sentry issues are actually linked to the tool calls Also fixed issue related to tool call argument retrieval #### Issues Closes https://linear.app/getsentry/issue/TET-1357/issue-associated-with-invoke-agent-instead-of-execute-tool #### Reminders - Please add tests to validate your changes, and lint your code using `tox -e linters`. - Add GH Issue ID _&_ Linear ID (if applicable) - PR title should use [conventional commit](https://develop.sentry.dev/engineering-practices/commit-messages/#type) style (`feat:`, `fix:`, `ref:`, `meta:`) - For external contributors: [CONTRIBUTING.md](https://github.com/getsentry/sentry-python/blob/master/CONTRIBUTING.md), [Sentry SDK development docs](https://develop.sentry.dev/sdk/), [Discord community](https://discord.gg/Ww9hbqr)
1 parent 5b05589 commit e8d5f1b

File tree

3 files changed

+30
-26
lines changed

3 files changed

+30
-26
lines changed

sentry_sdk/integrations/pydantic_ai/patches/agent_run.py

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
from functools import wraps
22

33
import sentry_sdk
4-
from sentry_sdk.tracing_utils import set_span_errored
5-
from sentry_sdk.utils import event_from_exception
64

75
from ..spans import invoke_agent_span, update_invoke_agent_span
6+
from ..utils import _capture_exception
87

98
from typing import TYPE_CHECKING
109
from pydantic_ai.agent import Agent # type: ignore
@@ -13,18 +12,6 @@
1312
from typing import Any, Callable, Optional
1413

1514

16-
def _capture_exception(exc):
17-
# type: (Any) -> None
18-
set_span_errored()
19-
20-
event, hint = event_from_exception(
21-
exc,
22-
client_options=sentry_sdk.get_client().options,
23-
mechanism={"type": "pydantic_ai", "handled": False},
24-
)
25-
sentry_sdk.capture_event(event, hint=hint)
26-
27-
2815
class _StreamingContextManagerWrapper:
2916
"""Wrapper for streaming methods that return async context managers."""
3017

sentry_sdk/integrations/pydantic_ai/patches/tools.py

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import sentry_sdk
66

77
from ..spans import execute_tool_span, update_execute_tool_span
8+
from ..utils import _capture_exception
89

910
from typing import TYPE_CHECKING
1011

@@ -56,16 +57,21 @@ async def wrapped_call_tool(self, call, allow_partial, wrap_validation_errors):
5657
)
5758
agent = agent_data.get("_agent")
5859

59-
# Get args for span (before validation)
60-
# call.args can be a string (JSON) or dict
61-
args_dict = call.args if isinstance(call.args, dict) else {}
60+
try:
61+
args_dict = call.args_as_dict()
62+
except Exception:
63+
args_dict = call.args if isinstance(call.args, dict) else {}
6264

6365
with execute_tool_span(name, args_dict, agent, tool_type=tool_type) as span:
64-
result = await original_call_tool(
65-
self, call, allow_partial, wrap_validation_errors
66-
)
67-
update_execute_tool_span(span, result)
68-
return result
66+
try:
67+
result = await original_call_tool(
68+
self, call, allow_partial, wrap_validation_errors
69+
)
70+
update_execute_tool_span(span, result)
71+
return result
72+
except Exception as exc:
73+
_capture_exception(exc)
74+
raise exc from None
6975

7076
# No span context - just call original
7177
return await original_call_tool(

sentry_sdk/integrations/pydantic_ai/utils.py

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
import sentry_sdk
2-
from sentry_sdk.ai.utils import set_data_normalized
32
from sentry_sdk.consts import SPANDATA
43
from sentry_sdk.scope import should_send_default_pii
5-
from sentry_sdk.utils import safe_serialize
4+
from sentry_sdk.tracing_utils import set_span_errored
5+
from sentry_sdk.utils import event_from_exception, safe_serialize
66

77
from typing import TYPE_CHECKING
88

99
if TYPE_CHECKING:
10-
from typing import Any, List, Dict
11-
from pydantic_ai.usage import RequestUsage # type: ignore
10+
from typing import Any
1211

1312

1413
def _should_send_prompts():
@@ -173,3 +172,15 @@ def _set_available_tools(span, agent):
173172
except Exception:
174173
# If we can't extract tools, just skip it
175174
pass
175+
176+
177+
def _capture_exception(exc):
178+
# type: (Any) -> None
179+
set_span_errored()
180+
181+
event, hint = event_from_exception(
182+
exc,
183+
client_options=sentry_sdk.get_client().options,
184+
mechanism={"type": "pydantic_ai", "handled": False},
185+
)
186+
sentry_sdk.capture_event(event, hint=hint)

0 commit comments

Comments
 (0)