Skip to content
Merged
31 changes: 30 additions & 1 deletion sentry_sdk/integrations/langchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,14 @@ def on_chat_model_start(self, serialized, messages, *, run_id, **kwargs):
elif "openai" in ai_type:
span.set_data(SPANDATA.GEN_AI_SYSTEM, "openai")

agent_name = (
sentry_sdk.get_current_scope()
._contexts.get("langchain_agent", {})
.get("agent_name")
)
if agent_name:
span.set_data(SPANDATA.GEN_AI_AGENT_NAME, agent_name)

for key, attribute in DATA_FIELDS.items():
if key in all_params and all_params[key] is not None:
set_data_normalized(span, attribute, all_params[key], unpack=False)
Expand Down Expand Up @@ -428,6 +436,14 @@ def on_tool_start(self, serialized, input_str, *, run_id, **kwargs):
if tool_description is not None:
span.set_data(SPANDATA.GEN_AI_TOOL_DESCRIPTION, tool_description)

agent_name = (
sentry_sdk.get_current_scope()
._contexts.get("langchain_agent", {})
.get("agent_name")
)
if agent_name:
span.set_data(SPANDATA.GEN_AI_AGENT_NAME, agent_name)

if should_send_default_pii() and self.include_prompts:
set_data_normalized(
span,
Expand Down Expand Up @@ -756,6 +772,9 @@ def new_invoke(self, *args, **kwargs):
name=f"invoke_agent {agent_name}" if agent_name else "invoke_agent",
origin=LangchainIntegration.origin,
) as span:
sentry_sdk.get_current_scope().set_context(
"langchain_agent", {"agent_name": agent_name, "tools": tools}
)
if agent_name:
span.set_data(SPANDATA.GEN_AI_AGENT_NAME, agent_name)

Expand Down Expand Up @@ -794,6 +813,8 @@ def new_invoke(self, *args, **kwargs):
):
set_data_normalized(span, SPANDATA.GEN_AI_RESPONSE_TEXT, output)

sentry_sdk.get_current_scope().remove_context("langchain_agent")

return result

return new_invoke
Expand All @@ -814,11 +835,15 @@ def new_stream(self, *args, **kwargs):

span = start_span_function(
op=OP.GEN_AI_INVOKE_AGENT,
name=f"invoke_agent {agent_name}".strip(),
name=f"invoke_agent {agent_name}" if agent_name else "invoke_agent",
origin=LangchainIntegration.origin,
)
span.__enter__()

sentry_sdk.get_current_scope().set_context(
"langchain_agent", {"agent_name": agent_name, "tools": tools}
)

if agent_name:
span.set_data(SPANDATA.GEN_AI_AGENT_NAME, agent_name)

Expand Down Expand Up @@ -868,6 +893,8 @@ def new_iterator():
):
set_data_normalized(span, SPANDATA.GEN_AI_RESPONSE_TEXT, output)

sentry_sdk.get_current_scope().remove_context("langchain_agent")
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Context Leak in Exception Handling

Context leak in _wrap_agent_executor_stream: if f(self, *args, **kwargs) throws an exception after the context is set (line 843-845), the iterators containing the remove_context calls (lines 896, 917) are never created, causing the "langchain_agent" context to leak permanently.

Fix in Cursor Fix in Web


span.__exit__(None, None, None)

async def new_iterator_async():
Expand All @@ -887,6 +914,8 @@ async def new_iterator_async():
):
set_data_normalized(span, SPANDATA.GEN_AI_RESPONSE_TEXT, output)

sentry_sdk.get_current_scope().remove_context("langchain_agent")

span.__exit__(None, None, None)

if str(type(result)) == "<class 'async_generator'>":
Expand Down