Skip to content

Commit 2a1ce08

Browse files
committed
simplify
1 parent e7d9b64 commit 2a1ce08

File tree

1 file changed

+13
-76
lines changed

1 file changed

+13
-76
lines changed

sentry_sdk/integrations/langgraph.py

Lines changed: 13 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,14 @@ def __init__(self, include_prompts=True):
2828
@staticmethod
2929
def setup_once():
3030
# type: () -> None
31-
# Wrap StateGraph methods - these get called when the graph is compiled
31+
# LangGraph lets users create agents using a StateGraph or the Functional API.
32+
# StateGraphs are then compiled to a CompiledStateGraph. Both CompiledStateGraph and
33+
# the functional API execute on a Pregel instance. Pregel is the runtime for the graph
34+
# and the invocation happens on Pregel, so patching the invoke methods takes care of both.
35+
# The streaming methods are not patched, because due to some internal reasons, LangGraph
36+
# will automatically patch the streaming methods to run through invoke, and by doing this
37+
# we prevent duplicate spans for invocations.
3238
StateGraph.compile = _wrap_state_graph_compile(StateGraph.compile)
33-
34-
# Wrap Pregel methods - these are the actual execution methods on compiled graphs
3539
if hasattr(Pregel, "invoke"):
3640
Pregel.invoke = _wrap_pregel_invoke(Pregel.invoke)
3741
if hasattr(Pregel, "ainvoke"):
@@ -40,12 +44,6 @@ def setup_once():
4044

4145
def _get_graph_name(graph_obj):
4246
# type: (Any) -> Optional[str]
43-
"""Extract graph name from various possible attributes."""
44-
# Check for Sentry-specific agent name first
45-
if hasattr(graph_obj, "_sentry_agent_name"):
46-
return graph_obj._sentry_agent_name
47-
48-
# Try to get name from different possible attributes
4947
for attr in ["name", "graph_name", "__name__", "_name"]:
5048
if hasattr(graph_obj, attr):
5149
name = getattr(graph_obj, attr)
@@ -54,41 +52,8 @@ def _get_graph_name(graph_obj):
5452
return None
5553

5654

57-
def _get_graph_metadata(graph_obj):
58-
# type: (Any) -> tuple[Optional[str], Optional[List[str]]]
59-
"""Extract graph name and node names if available."""
60-
graph_name = _get_graph_name(graph_obj)
61-
62-
# Try to get node names from the graph
63-
node_names = None
64-
if hasattr(graph_obj, "nodes"):
65-
try:
66-
nodes = graph_obj.nodes
67-
if isinstance(nodes, dict):
68-
node_names = list(nodes.keys())
69-
elif hasattr(nodes, "__iter__"):
70-
node_names = list(nodes)
71-
except Exception:
72-
pass
73-
elif hasattr(graph_obj, "graph") and hasattr(graph_obj.graph, "nodes"):
74-
try:
75-
nodes = graph_obj.graph.nodes
76-
if isinstance(nodes, dict):
77-
node_names = list(nodes.keys())
78-
elif hasattr(nodes, "__iter__"):
79-
node_names = list(nodes)
80-
except Exception:
81-
pass
82-
83-
return graph_name, node_names
84-
85-
8655
def _parse_langgraph_messages(state):
8756
# type: (Any) -> Optional[List[Any]]
88-
role_map = {
89-
"human": "user",
90-
"ai": "assistant",
91-
}
9257
if not state:
9358
return None
9459

@@ -110,33 +75,15 @@ def _parse_langgraph_messages(state):
11075
normalized_messages = []
11176
for message in messages:
11277
try:
113-
if isinstance(message, dict):
114-
role = message.get("role") or message.get("type")
115-
if role in role_map:
116-
message = dict(message)
117-
message["role"] = role_map[role]
118-
normalized_messages.append(message)
119-
elif hasattr(message, "type") and hasattr(message, "content"):
120-
role = getattr(message, "type", None)
121-
mapped_role = role_map.get(role, role)
122-
parsed = {"role": mapped_role, "content": message.content}
123-
if hasattr(message, "additional_kwargs"):
124-
parsed.update(message.additional_kwargs)
125-
normalized_messages.append(parsed)
126-
elif hasattr(message, "role") and hasattr(message, "content"):
127-
role = getattr(message, "role", None)
128-
mapped_role = role_map.get(role, role)
129-
parsed = {"role": mapped_role, "content": message.content}
78+
if hasattr(message, "content"):
79+
parsed = {"content": message.content}
13080
for attr in ["name", "tool_calls", "function_call"]:
13181
if hasattr(message, attr):
13282
value = getattr(message, attr)
13383
if value is not None:
13484
parsed[attr] = value
13585
normalized_messages.append(parsed)
136-
elif hasattr(message, "__dict__"):
137-
normalized_messages.append(vars(message))
138-
else:
139-
normalized_messages.append({"content": str(message)})
86+
14087
except Exception:
14188
continue
14289

@@ -158,7 +105,6 @@ def new_compile(self, *args, **kwargs):
158105
name="create_agent",
159106
origin=LanggraphIntegration.origin,
160107
) as span:
161-
# import ipdb; ipdb.set_trace()
162108
span.set_data(SPANDATA.GEN_AI_OPERATION_NAME, "create_agent")
163109
span.set_data(
164110
SPANDATA.GEN_AI_AGENT_NAME, getattr(compiled_graph, "name", None)
@@ -176,9 +122,6 @@ def new_compile(self, *args, **kwargs):
176122
if data and hasattr(data, "tools_by_name"):
177123
tools = list(data.tools_by_name.keys())
178124
span.set_data(SPANDATA.GEN_AI_REQUEST_AVAILABLE_TOOLS, tools)
179-
compiled_graph = f(self, *args, **kwargs)
180-
if hasattr(self, "__dict__"):
181-
compiled_graph._sentry_source_graph = self
182125
return compiled_graph
183126

184127
return new_compile
@@ -194,7 +137,7 @@ def new_invoke(self, *args, **kwargs):
194137
if integration is None:
195138
return f(self, *args, **kwargs)
196139

197-
graph_name, node_names = _get_graph_metadata(self)
140+
graph_name = _get_graph_name(self)
198141

199142
with sentry_sdk.start_span(
200143
op=OP.GEN_AI_INVOKE_AGENT,
@@ -258,18 +201,13 @@ async def new_ainvoke(self, *args, **kwargs):
258201
if integration is None:
259202
return await f(self, *args, **kwargs)
260203

261-
graph_name, node_names = _get_graph_metadata(self)
204+
graph_name = _get_graph_name(self)
262205

263206
with sentry_sdk.start_span(
264207
op=OP.GEN_AI_INVOKE_AGENT,
265-
name=(
266-
f"invoke_agent ainvoke {graph_name}".strip()
267-
if graph_name
268-
else "invoke_agent"
269-
),
208+
name="invoke_agent",
270209
origin=LanggraphIntegration.origin,
271210
) as span:
272-
# Set agent metadata
273211
if graph_name:
274212
set_ai_pipeline_name(graph_name)
275213
span.set_data(SPANDATA.GEN_AI_PIPELINE_NAME, graph_name)
@@ -278,7 +216,6 @@ async def new_ainvoke(self, *args, **kwargs):
278216
span.set_data(SPANDATA.GEN_AI_OPERATION_NAME, "invoke_agent")
279217
span.set_data(SPANDATA.GEN_AI_RESPONSE_STREAMING, False)
280218

281-
# Capture input messages if PII is allowed
282219
if (
283220
len(args) > 0
284221
and should_send_default_pii()

0 commit comments

Comments
 (0)