77from typing import TYPE_CHECKING
88
99import sentry_sdk
10- from sentry_sdk .ai .monitoring import set_ai_pipeline_name
1110from sentry_sdk .ai .utils import (
1211 GEN_AI_ALLOWED_MESSAGE_ROLES ,
1312 get_start_span_function ,
@@ -241,38 +240,28 @@ def setup_once() -> None:
241240 _patch_embeddings_provider (OllamaEmbeddings )
242241
243242
244- class WatchedSpan :
245- span : "Span" = None # type: ignore[assignment]
246- children : "List[WatchedSpan]" = []
247- is_pipeline : bool = False
248-
249- def __init__ (self , span : "Span" ) -> None :
250- self .span = span
251-
252-
253243class SentryLangchainCallback (BaseCallbackHandler ): # type: ignore[misc]
254244 """Callback handler that creates Sentry spans."""
255245
256246 def __init__ (
257247 self , max_span_map_size : "Optional[int]" , include_prompts : bool
258248 ) -> None :
259- self .span_map : "OrderedDict[UUID, WatchedSpan ]" = OrderedDict ()
249+ self .span_map : "OrderedDict[UUID, sentry_sdk.tracing.Span ]" = OrderedDict ()
260250 self .max_span_map_size = max_span_map_size
261251 self .include_prompts = include_prompts
262252
263253 def gc_span_map (self ) -> None :
264254 if self .max_span_map_size is not None :
265255 while len (self .span_map ) > self .max_span_map_size :
266- run_id , watched_span = self .span_map .popitem (last = False )
267- self ._exit_span (watched_span , run_id )
256+ run_id , span = self .span_map .popitem (last = False )
257+ self ._exit_span (span , run_id )
268258
269259 def _handle_error (self , run_id : "UUID" , error : "Any" ) -> None :
270260 with capture_internal_exceptions ():
271261 if not run_id or run_id not in self .span_map :
272262 return
273263
274- span_data = self .span_map [run_id ]
275- span = span_data .span
264+ span = self .span_map [run_id ]
276265
277266 sentry_sdk .capture_exception (error , span .scope )
278267
@@ -291,29 +280,27 @@ def _create_span(
291280 run_id : "UUID" ,
292281 parent_id : "Optional[Any]" ,
293282 ** kwargs : "Any" ,
294- ) -> "WatchedSpan " :
295- watched_span : "Optional[WatchedSpan]" = None
283+ ) -> "sentry_sdk.tracing.Span " :
284+ span = None
296285 if parent_id :
297- parent_span : "Optional[WatchedSpan]" = self .span_map .get (parent_id )
286+ parent_span : "Optional[sentry_sdk.tracing.Span]" = self .span_map .get (
287+ parent_id
288+ )
298289 if parent_span :
299- watched_span = WatchedSpan (parent_span .span .start_child (** kwargs ))
300- parent_span .children .append (watched_span )
290+ span = parent_span .start_child (** kwargs )
301291
302- if watched_span is None :
303- watched_span = WatchedSpan ( sentry_sdk .start_span (** kwargs ) )
292+ if span is None :
293+ span = sentry_sdk .start_span (** kwargs )
304294
305- watched_span . span .__enter__ ()
306- self .span_map [run_id ] = watched_span
295+ span .__enter__ ()
296+ self .span_map [run_id ] = span
307297 self .gc_span_map ()
308- return watched_span
298+ return span
309299
310300 def _exit_span (
311- self : "SentryLangchainCallback" , span_data : "WatchedSpan " , run_id : "UUID"
301+ self : "SentryLangchainCallback" , span : "sentry_sdk.tracing.Span " , run_id : "UUID"
312302 ) -> None :
313- if span_data .is_pipeline :
314- set_ai_pipeline_name (None )
315-
316- span_data .span .__exit__ (None , None , None )
303+ span .__exit__ (None , None , None )
317304 del self .span_map [run_id ]
318305
319306 def on_llm_start (
@@ -341,14 +328,13 @@ def on_llm_start(
341328 or ""
342329 )
343330
344- watched_span = self ._create_span (
331+ span = self ._create_span (
345332 run_id ,
346333 parent_run_id ,
347334 op = OP .GEN_AI_TEXT_COMPLETION ,
348335 name = f"text_completion { model } " .strip (),
349336 origin = LangchainIntegration .origin ,
350337 )
351- span = watched_span .span
352338
353339 span .set_data (SPANDATA .GEN_AI_OPERATION_NAME , "text_completion" )
354340
@@ -421,14 +407,13 @@ def on_chat_model_start(
421407 or ""
422408 )
423409
424- watched_span = self ._create_span (
410+ span = self ._create_span (
425411 run_id ,
426412 kwargs .get ("parent_run_id" ),
427413 op = OP .GEN_AI_CHAT ,
428414 name = f"chat { model } " .strip (),
429415 origin = LangchainIntegration .origin ,
430416 )
431- span = watched_span .span
432417
433418 span .set_data (SPANDATA .GEN_AI_OPERATION_NAME , "chat" )
434419 if model :
@@ -505,8 +490,7 @@ def on_chat_model_end(
505490 if not run_id or run_id not in self .span_map :
506491 return
507492
508- span_data = self .span_map [run_id ]
509- span = span_data .span
493+ span = self .span_map [run_id ]
510494
511495 if should_send_default_pii () and self .include_prompts :
512496 set_data_normalized (
@@ -516,7 +500,7 @@ def on_chat_model_end(
516500 )
517501
518502 _record_token_usage (span , response )
519- self ._exit_span (span_data , run_id )
503+ self ._exit_span (span , run_id )
520504
521505 def on_llm_end (
522506 self : "SentryLangchainCallback" ,
@@ -530,8 +514,7 @@ def on_llm_end(
530514 if not run_id or run_id not in self .span_map :
531515 return
532516
533- span_data = self .span_map [run_id ]
534- span = span_data .span
517+ span = self .span_map [run_id ]
535518
536519 try :
537520 generation = response .generations [0 ][0 ]
@@ -579,7 +562,7 @@ def on_llm_end(
579562 )
580563
581564 _record_token_usage (span , response )
582- self ._exit_span (span_data , run_id )
565+ self ._exit_span (span , run_id )
583566
584567 def on_llm_error (
585568 self : "SentryLangchainCallback" ,
@@ -612,15 +595,14 @@ def on_agent_finish(
612595 if not run_id or run_id not in self .span_map :
613596 return
614597
615- span_data = self .span_map [run_id ]
616- span = span_data .span
598+ span = self .span_map [run_id ]
617599
618600 if should_send_default_pii () and self .include_prompts :
619601 set_data_normalized (
620602 span , SPANDATA .GEN_AI_RESPONSE_TEXT , finish .return_values .items ()
621603 )
622604
623- self ._exit_span (span_data , run_id )
605+ self ._exit_span (span , run_id )
624606
625607 def on_tool_start (
626608 self : "SentryLangchainCallback" ,
@@ -637,14 +619,13 @@ def on_tool_start(
637619
638620 tool_name = serialized .get ("name" ) or kwargs .get ("name" ) or ""
639621
640- watched_span = self ._create_span (
622+ span = self ._create_span (
641623 run_id ,
642624 kwargs .get ("parent_run_id" ),
643625 op = OP .GEN_AI_EXECUTE_TOOL ,
644626 name = f"execute_tool { tool_name } " .strip (),
645627 origin = LangchainIntegration .origin ,
646628 )
647- span = watched_span .span
648629
649630 span .set_data (SPANDATA .GEN_AI_OPERATION_NAME , "execute_tool" )
650631 span .set_data (SPANDATA .GEN_AI_TOOL_NAME , tool_name )
@@ -681,13 +662,12 @@ def on_tool_end(
681662 if not run_id or run_id not in self .span_map :
682663 return
683664
684- span_data = self .span_map [run_id ]
685- span = span_data .span
665+ span = self .span_map [run_id ]
686666
687667 if should_send_default_pii () and self .include_prompts :
688668 set_data_normalized (span , SPANDATA .GEN_AI_TOOL_OUTPUT , output )
689669
690- self ._exit_span (span_data , run_id )
670+ self ._exit_span (span , run_id )
691671
692672 def on_tool_error (
693673 self ,
0 commit comments