11from __future__ import annotations
22
3+ import base64
34import json
45from typing import Any
56
67from opentelemetry ._events import Event , EventLogger , EventLoggerProvider
78from opentelemetry .instrumentation .google_genai import GoogleGenAiSdkInstrumentor
89from opentelemetry .trace import get_current_span
10+ from typing_extensions import TypeAlias
911
1012import logfire
1113from logfire ._internal .utils import handle_internal_errors
1214
15+ Part : TypeAlias = 'dict[str, Any] | str'
16+
17+
18+ def default_json (x : Any ) -> str :
19+ return base64 .b64encode (x ).decode ('utf-8' ) if isinstance (x , bytes ) else x
20+
1321
1422class SpanEventLogger (EventLogger ):
1523 @handle_internal_errors
@@ -18,18 +26,25 @@ def emit(self, event: Event) -> None:
1826 assert isinstance (event .body , dict )
1927 body : dict [str , Any ] = {** event .body }
2028 if event .name == 'gen_ai.choice' :
21- parts = body .pop ('content' )['parts' ]
22- new_parts : list [dict [str , Any ] | str ] = []
23- for part in parts :
24- new_part : str | dict [str , Any ] = {k : v for k , v in part .items () if v is not None }
25- if list (new_part .keys ()) == ['text' ]: # pragma: no branch
26- new_part = new_part ['text' ]
27- new_parts .append (new_part )
28- body ['message' ] = {'role' : 'assistant' , 'content' : new_parts }
29+ if 'content' in body : # pragma: no branch
30+ parts = body .pop ('content' )['parts' ]
31+ new_parts = [transform_part (part ) for part in parts ]
32+ body ['message' ] = {'role' : 'assistant' , 'content' : new_parts }
2933 else :
34+ if 'content' in body : # pragma: no branch
35+ body ['content' ] = transform_part (body ['content' ])
3036 body ['role' ] = body .get ('role' , event .name .split ('.' )[1 ])
3137
32- span .add_event (event .name , attributes = {'event_body' : json .dumps (body )})
38+ span .add_event (event .name , attributes = {'event_body' : json .dumps (body , default = default_json )})
39+
40+
41+ def transform_part (part : Part ) -> Part :
42+ if isinstance (part , str ):
43+ return part
44+ new_part = {k : v for k , v in part .items () if v is not None }
45+ if list (new_part .keys ()) == ['text' ]:
46+ return new_part ['text' ]
47+ return new_part
3348
3449
3550class SpanEventLoggerProvider (EventLoggerProvider ):
0 commit comments