Skip to content

Commit 0c46af1

Browse files
committed
Attributes on spans
1 parent 3f4e2a8 commit 0c46af1

File tree

3 files changed

+112
-0
lines changed

3 files changed

+112
-0
lines changed

sentry_sdk/consts.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -467,6 +467,12 @@ class SPANDATA:
467467
Example: "The weather in Paris is rainy and overcast, with temperatures around 57°F"
468468
"""
469469

470+
GEN_AI_GUARDRAIL_TRIPWIRE_TRIGGERED = "gen_ai.guardrail.tripwire_triggered"
471+
"""
472+
Whether the guardrail tripwire was triggered.
473+
Example: true
474+
"""
475+
470476
GEN_AI_OPERATION_NAME = "gen_ai.operation.name"
471477
"""
472478
The name of the operation being performed.
@@ -796,6 +802,8 @@ class OP:
796802
GEN_AI_EMBEDDINGS = "gen_ai.embeddings"
797803
GEN_AI_EXECUTE_TOOL = "gen_ai.execute_tool"
798804
GEN_AI_GENERATE_TEXT = "gen_ai.generate_text"
805+
GEN_AI_GUARDRAIL_INPUT = "gen_ai.guardrail.input"
806+
GEN_AI_GUARDRAIL_OUTPUT = "gen_ai.guardrail.output"
799807
GEN_AI_HANDOFF = "gen_ai.handoff"
800808
GEN_AI_PIPELINE = "gen_ai.pipeline"
801809
GEN_AI_INVOKE_AGENT = "gen_ai.invoke_agent"

sentry_sdk/integrations/openai_agents/patches/agent_run.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
from functools import wraps
22

33
from sentry_sdk.integrations import DidNotEnable
4+
from sentry_sdk.integrations.openai_agents.spans.guardrail import (
5+
guardrail_span,
6+
update_guardrail_span,
7+
)
48
from ..spans import invoke_agent_span, update_invoke_agent_span, handoff_span
59

610
from typing import TYPE_CHECKING
@@ -26,6 +30,13 @@ def _patch_agent_run():
2630
original_execute_handoffs = agents._run_impl.RunImpl.execute_handoffs
2731
original_execute_final_output = agents._run_impl.RunImpl.execute_final_output
2832

33+
original_run_single_input_guardrail = (
34+
agents._run_impl.RunImpl.run_single_input_guardrail
35+
)
36+
original_run_single_output_guardrail = (
37+
agents._run_impl.RunImpl.run_single_output_guardrail
38+
)
39+
2940
def _start_invoke_agent_span(context_wrapper, agent, kwargs):
3041
# type: (agents.RunContextWrapper, agents.Agent, dict[str, Any]) -> None
3142
"""Start an agent invocation span"""
@@ -132,9 +143,47 @@ async def patched_execute_final_output(cls, *args, **kwargs):
132143

133144
return result
134145

146+
@wraps(
147+
original_run_single_input_guardrail.__func__
148+
if hasattr(original_run_single_input_guardrail, "__func__")
149+
else original_run_single_input_guardrail
150+
)
151+
async def patched_run_single_input_guardrail(cls, *args, **kwargs):
152+
# type: (agents.Runner, *Any, **Any) -> Any
153+
agent = args[0]
154+
guardrail = args[1]
155+
156+
with guardrail_span(guardrail, "input", args, kwargs) as span:
157+
result = await original_run_single_input_guardrail(*args, **kwargs)
158+
update_guardrail_span(span, agent, guardrail, "input", result)
159+
160+
return result
161+
162+
@wraps(
163+
original_run_single_output_guardrail.__func__
164+
if hasattr(original_run_single_output_guardrail, "__func__")
165+
else original_run_single_output_guardrail
166+
)
167+
async def patched_run_single_output_guardrail(cls, *args, **kwargs):
168+
# type: (agents.Runner, *Any, **Any) -> Any
169+
guardrail = args[0]
170+
agent = args[1]
171+
172+
with guardrail_span(guardrail, "output", args, kwargs) as span:
173+
result = await original_run_single_output_guardrail(*args, **kwargs)
174+
update_guardrail_span(span, agent, guardrail, "output", result)
175+
176+
return result
177+
135178
# Apply patches
136179
agents.run.AgentRunner._run_single_turn = classmethod(patched_run_single_turn)
137180
agents._run_impl.RunImpl.execute_handoffs = classmethod(patched_execute_handoffs)
138181
agents._run_impl.RunImpl.execute_final_output = classmethod(
139182
patched_execute_final_output
140183
)
184+
agents._run_impl.RunImpl.run_single_input_guardrail = classmethod(
185+
patched_run_single_input_guardrail
186+
)
187+
agents._run_impl.RunImpl.run_single_output_guardrail = classmethod(
188+
patched_run_single_output_guardrail
189+
)
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import sentry_sdk
2+
from sentry_sdk.consts import OP, SPANDATA
3+
4+
from ..consts import SPAN_ORIGIN
5+
6+
from typing import TYPE_CHECKING
7+
8+
if TYPE_CHECKING:
9+
import agents
10+
from typing import Any
11+
12+
13+
def guardrail_span(guardrail, guardrail_type, args, kwargs):
14+
# type: (agents.Guardrail, tuple[Any, ...], dict[str, Any]) -> sentry_sdk.tracing.Span
15+
op = (
16+
OP.GEN_AI_GUARDRAIL_OUTPUT
17+
if guardrail_type == "output"
18+
else OP.GEN_AI_GUARDRAIL_INPUT
19+
)
20+
21+
span = sentry_sdk.start_span(
22+
op=op,
23+
name=f"guardrail {guardrail.name or ''}".strip(),
24+
origin=SPAN_ORIGIN,
25+
)
26+
27+
span.set_data(SPANDATA.GEN_AI_OPERATION_NAME, "guardrail")
28+
span.set_data(SPANDATA.GEN_AI_TOOL_TYPE, f"guardrail.{guardrail_type}")
29+
30+
if guardrail.name is not None:
31+
span.set_data(SPANDATA.GEN_AI_TOOL_NAME, guardrail.name)
32+
33+
try:
34+
input = args[2]
35+
except IndexError:
36+
input = None
37+
38+
if input is not None:
39+
span.set_data(SPANDATA.GEN_AI_TOOL_INPUT, input)
40+
41+
return span
42+
43+
44+
def update_guardrail_span(span, agent, guardrail, guardrail_type, result):
45+
# type: (sentry_sdk.tracing.Span, agents.Agent, agents.Guardrail, Any) -> None
46+
if agent.name is not None:
47+
span.set_data(SPANDATA.GEN_AI_AGENT_NAME, agent.name)
48+
49+
output = result.output.output_info.get("reason")
50+
if output is not None:
51+
span.set_data(SPANDATA.GEN_AI_TOOL_OUTPUT, output)
52+
53+
tripwire_triggered = result.output.tripwire_triggered
54+
if tripwire_triggered is not None:
55+
span.set_data(SPANDATA.GEN_AI_GUARDRAIL_TRIPWIRE_TRIGGERED, tripwire_triggered)

0 commit comments

Comments
 (0)