77
88import json
99import logging
10- from collections .abc import Mapping
1110from typing import Any , Optional
1211
1312from opentelemetry .context import Context
14- from opentelemetry .sdk .trace import Span , SpanProcessor
15- from opentelemetry .util .types import AttributeValue
13+ from opentelemetry .sdk .trace import ReadableSpan , Span , SpanProcessor
1614
1715logger = logging .getLogger (__name__ )
1816
@@ -27,17 +25,16 @@ def on_start(self, span: Span, parent_context: Optional[Context] = None) -> None
2725 """Called when span starts - no action needed."""
2826 pass
2927
30- def on_end (self , span : Span ) -> None :
28+ def on_end (self , span : ReadableSpan ) -> None :
3129 """Normalize tool call attributes before span is consumed by exporters/terminal."""
32- if not span ._attributes :
30+ # Access internal mutable attributes (BoundedAttributes at runtime)
31+ # ReadableSpan._attributes is typed as Mapping but mutable in practice
32+ attrs = getattr (span , "_attributes" , None )
33+ if not attrs :
3334 return
3435
3536 try :
36- # Get the mutable internal attributes dict
37- attrs : Mapping [str , AttributeValue ] = span ._attributes
38-
3937 if attrs .get ("openinference.span.kind" , None ) == "TOOL" :
40- # Normalize tool call attributes
4138 for key in ("input.value" , "output.value" ):
4239 if key in attrs :
4340 original = attrs [key ]
@@ -52,7 +49,6 @@ def on_end(self, span: Span) -> None:
5249 )
5350
5451 except Exception as e :
55- # Don't crash span processing if normalization fails
5652 logger .debug (
5753 f"Failed to normalize span '{ getattr (span , 'name' , 'unknown' )} ': { e } "
5854 )
0 commit comments