77The key innovation is that these are thin wrappers around the standard OpenAI models,
88avoiding code duplication while adding tracing capabilities.
99"""
10+
1011from __future__ import annotations
1112
1213import logging
@@ -51,24 +52,28 @@ def _serialize_item(item: Any) -> dict[str, Any]:
5152 Uses model_dump() for Pydantic models, otherwise extracts attributes manually.
5253 Filters out internal Pydantic fields that can't be serialized.
5354 """
54- if hasattr (item , ' model_dump' ):
55+ if hasattr (item , " model_dump" ):
5556 # Pydantic model - use model_dump for proper serialization
5657 try :
57- return item .model_dump (mode = ' json' , exclude_unset = True )
58+ return item .model_dump (mode = " json" , exclude_unset = True )
5859 except Exception :
5960 # Fallback to dict conversion
60- return dict (item ) if hasattr (item , ' __iter__' ) else {}
61+ return dict (item ) if hasattr (item , " __iter__" ) else {}
6162 else :
6263 # Not a Pydantic model - extract attributes manually
6364 item_dict = {}
6465 for attr_name in dir (item ):
65- if not attr_name .startswith ('_' ) and attr_name not in ('model_fields' , 'model_config' , 'model_computed_fields' ):
66+ if not attr_name .startswith ("_" ) and attr_name not in (
67+ "model_fields" ,
68+ "model_config" ,
69+ "model_computed_fields" ,
70+ ):
6671 try :
6772 attr_value = getattr (item , attr_name , None )
6873 # Skip methods and None values
6974 if attr_value is not None and not callable (attr_value ):
7075 # Convert to JSON-serializable format
71- if hasattr (attr_value , ' model_dump' ):
76+ if hasattr (attr_value , " model_dump" ):
7277 item_dict [attr_name ] = attr_value .model_dump ()
7378 elif isinstance (attr_value , (str , int , float , bool , list , dict )):
7479 item_dict [attr_name ] = attr_value
@@ -106,7 +111,9 @@ def __init__(self, openai_client: Optional[AsyncOpenAI] = None, **kwargs):
106111 # Initialize tracer for all models
107112 agentex_client = create_async_agentex_client ()
108113 self ._tracer = AsyncTracer (agentex_client )
109- logger .info (f"[TemporalTracingModelProvider] Initialized with AgentEx tracer, custom_client={ openai_client is not None } " )
114+ logger .info (
115+ f"[TemporalTracingModelProvider] Initialized with AgentEx tracer, custom_client={ openai_client is not None } "
116+ )
110117
111118 @override
112119 def get_model (self , model_name : Optional [str ]) -> Model :
@@ -126,10 +133,14 @@ def get_model(self, model_name: Optional[str]) -> Model:
126133 logger .info (f"[TemporalTracingModelProvider] Wrapping OpenAIResponsesModel '{ model_name } ' with tracing" )
127134 return TemporalTracingResponsesModel (base_model , self ._tracer ) # type: ignore[abstract]
128135 elif isinstance (base_model , OpenAIChatCompletionsModel ):
129- logger .info (f"[TemporalTracingModelProvider] Wrapping OpenAIChatCompletionsModel '{ model_name } ' with tracing" )
136+ logger .info (
137+ f"[TemporalTracingModelProvider] Wrapping OpenAIChatCompletionsModel '{ model_name } ' with tracing"
138+ )
130139 return TemporalTracingChatCompletionsModel (base_model , self ._tracer ) # type: ignore[abstract]
131140 else :
132- logger .warning (f"[TemporalTracingModelProvider] Unknown model type, returning without tracing: { type (base_model )} " )
141+ logger .warning (
142+ f"[TemporalTracingModelProvider] Unknown model type, returning without tracing: { type (base_model )} "
143+ )
133144 return base_model
134145
135146
@@ -181,7 +192,9 @@ async def get_response(
181192
182193 # If we have tracing context, wrap with span
183194 if trace_id and parent_span_id :
184- logger .debug (f"[TemporalTracingResponsesModel] Adding tracing span for task_id={ task_id } , trace_id={ trace_id } " )
195+ logger .debug (
196+ f"[TemporalTracingResponsesModel] Adding tracing span for task_id={ task_id } , trace_id={ trace_id } "
197+ )
185198
186199 trace = self ._tracer .trace (trace_id )
187200
@@ -199,7 +212,9 @@ async def get_response(
199212 "temperature" : model_settings .temperature ,
200213 "max_tokens" : model_settings .max_tokens ,
201214 "reasoning" : model_settings .reasoning ,
202- } if model_settings else None ,
215+ }
216+ if model_settings
217+ else None ,
203218 },
204219 ) as span :
205220 try :
@@ -222,7 +237,7 @@ async def get_response(
222237 new_items = []
223238 final_output = None
224239
225- if hasattr (response , ' output' ) and response .output :
240+ if hasattr (response , " output" ) and response .output :
226241 response_output = response .output if isinstance (response .output , list ) else [response .output ]
227242
228243 for item in response_output :
@@ -232,12 +247,12 @@ async def get_response(
232247 new_items .append (item_dict )
233248
234249 # Extract final_output from message type if available
235- if item_dict .get (' type' ) == ' message' and not final_output :
236- content = item_dict .get (' content' , [])
250+ if item_dict .get (" type" ) == " message" and not final_output :
251+ content = item_dict .get (" content" , [])
237252 if content and isinstance (content , list ):
238253 for content_part in content :
239- if isinstance (content_part , dict ) and ' text' in content_part :
240- final_output = content_part [' text' ]
254+ if isinstance (content_part , dict ) and " text" in content_part :
255+ final_output = content_part [" text" ]
241256 break
242257 except Exception as e :
243258 logger .warning (f"Failed to serialize item in temporal tracing model: { e } " )
@@ -329,7 +344,9 @@ async def get_response(
329344
330345 # If we have tracing context, wrap with span
331346 if trace_id and parent_span_id :
332- logger .debug (f"[TemporalTracingChatCompletionsModel] Adding tracing span for task_id={ task_id } , trace_id={ trace_id } " )
347+ logger .debug (
348+ f"[TemporalTracingChatCompletionsModel] Adding tracing span for task_id={ task_id } , trace_id={ trace_id } "
349+ )
333350
334351 trace = self ._tracer .trace (trace_id )
335352
@@ -346,7 +363,9 @@ async def get_response(
346363 "model_settings" : {
347364 "temperature" : model_settings .temperature ,
348365 "max_tokens" : model_settings .max_tokens ,
349- } if model_settings else None ,
366+ }
367+ if model_settings
368+ else None ,
350369 },
351370 ) as span :
352371 try :
@@ -366,7 +385,7 @@ async def get_response(
366385 new_items = []
367386 final_output = None
368387
369- if hasattr (response , ' output' ) and response .output :
388+ if hasattr (response , " output" ) and response .output :
370389 response_output = response .output if isinstance (response .output , list ) else [response .output ]
371390
372391 for item in response_output :
@@ -376,12 +395,12 @@ async def get_response(
376395 new_items .append (item_dict )
377396
378397 # Extract final_output from message type if available
379- if item_dict .get (' type' ) == ' message' and not final_output :
380- content = item_dict .get (' content' , [])
398+ if item_dict .get (" type" ) == " message" and not final_output :
399+ content = item_dict .get (" content" , [])
381400 if content and isinstance (content , list ):
382401 for content_part in content :
383- if isinstance (content_part , dict ) and ' text' in content_part :
384- final_output = content_part [' text' ]
402+ if isinstance (content_part , dict ) and " text" in content_part :
403+ final_output = content_part [" text" ]
385404 break
386405 except Exception as e :
387406 logger .warning (f"Failed to serialize item in temporal tracing model: { e } " )
@@ -406,7 +425,9 @@ async def get_response(
406425 raise
407426 else :
408427 # No tracing context, just pass through
409- logger .debug ("[TemporalTracingChatCompletionsModel] No tracing context available, calling base model directly" )
428+ logger .debug (
429+ "[TemporalTracingChatCompletionsModel] No tracing context available, calling base model directly"
430+ )
410431 return await self ._base_model .get_response (
411432 system_instructions = system_instructions ,
412433 input = input ,
@@ -422,4 +443,4 @@ async def get_response(
422443 def stream_response (self , * args , ** kwargs ):
423444 """Streaming is handled via get_response in Temporal activities.
424445 Required so the class is concrete and instantiable at runtime."""
425- raise NotImplementedError ("stream_response is not used in Temporal activities - use get_response instead" )
446+ raise NotImplementedError ("stream_response is not used in Temporal activities - use get_response instead" )
0 commit comments