@@ -172,12 +172,79 @@ def _setup_span(entity_name, tlp_span_kind, version):
172172    return  span , ctx , ctx_token 
173173
174174
175+ def  _sanitize_for_serialization (obj ):
176+     """ 
177+     Recursively sanitize objects for JSON serialization by replacing 
178+     unpicklable objects with string representations. 
179+     """ 
180+     import  dataclasses 
181+     import  copy 
182+ 
183+     # Handle None 
184+     if  obj  is  None :
185+         return  None 
186+ 
187+     # Handle primitive types 
188+     if  isinstance (obj , (str , int , float , bool )):
189+         return  obj 
190+ 
191+     # Handle lists and tuples 
192+     if  isinstance (obj , (list , tuple )):
193+         return  type (obj )(
194+             _sanitize_for_serialization (item ) for  item  in  obj 
195+         )
196+ 
197+     # Handle dictionaries 
198+     if  isinstance (obj , dict ):
199+         return  {
200+             key : _sanitize_for_serialization (value )
201+             for  key , value  in  obj .items ()
202+         }
203+ 
204+     # Handle dataclasses - try to convert, catch unpicklable objects 
205+     if  dataclasses .is_dataclass (obj ):
206+         try :
207+             # Try shallow copy first to test if it's picklable 
208+             copy .copy (obj )
209+             # If successful, try to convert to dict 
210+             obj_dict  =  {}
211+             for  field  in  dataclasses .fields (obj ):
212+                 field_value  =  getattr (obj , field .name )
213+                 # Check if the field value is picklable 
214+                 try :
215+                     copy .deepcopy (field_value )
216+                     obj_dict [field .name ] =  _sanitize_for_serialization (
217+                         field_value 
218+                     )
219+                 except  (TypeError , ValueError , AttributeError ):
220+                     # If not picklable, use string representation 
221+                     obj_dict [field .name ] =  (
222+                         f"<unpicklable: { type (field_value ).__name__ }  >" 
223+                     )
224+             return  obj_dict 
225+         except  (TypeError , ValueError , AttributeError ):
226+             return  f"<unpicklable dataclass: { type (obj ).__name__ }  >" 
227+ 
228+     # For other objects, try to serialize them 
229+     try :
230+         copy .deepcopy (obj )
231+         # If deepcopy succeeds, return as-is for JSON encoder 
232+         return  obj 
233+     except  (TypeError , ValueError , AttributeError ):
234+         # If not picklable, return a string representation 
235+         return  f"<unpicklable: { type (obj ).__name__ }  >" 
236+ 
237+ 
175238def  _handle_span_input (span , args , kwargs , cls = None ):
176239    """Handles entity input logging in JSON for both sync and async functions""" 
177240    try :
178241        if  _should_send_prompts ():
242+ 
243+             # Create a sanitized copy of args and kwargs that excludes unpicklable objects 
244+             sanitized_args  =  _sanitize_for_serialization (args )
245+             sanitized_kwargs  =  _sanitize_for_serialization (kwargs )
179246            json_input  =  json .dumps (
180-                 {"args" : args , "kwargs" : kwargs }, ** ({"cls" : cls } if  cls  else  {})
247+                 {"args" : sanitized_args , "kwargs" : sanitized_kwargs }, ** ({"cls" : cls } if  cls  else  {})
181248            )
182249            truncated_json  =  _truncate_json_if_needed (json_input )
183250            span .set_attribute (
@@ -192,13 +259,14 @@ def _handle_span_output(span, res, cls=None):
192259    """Handles entity output logging in JSON for both sync and async functions""" 
193260    try :
194261        if  _should_send_prompts ():
195-             json_output  =  json .dumps (res , ** ({"cls" : cls } if  cls  else  {}))
262+             sanitized_res  =  _sanitize_for_serialization (res )
263+             json_output  =  json .dumps (sanitized_res , ** ({"cls" : cls } if  cls  else  {}))
196264            truncated_json  =  _truncate_json_if_needed (json_output )
197265            span .set_attribute (
198266                SpanAttributes .TRACELOOP_ENTITY_OUTPUT ,
199267                truncated_json ,
200268            )
201-     except  TypeError  as  e :
269+     except  ( TypeError ,  ValueError )  as  e :
202270        Telemetry ().log_exception (e )
203271
204272
0 commit comments