from collections.abc import AsyncIterator, Generator, Iterator from datetime import datetime from loguru import logger from pydantic import BaseModel from pydantic.v1 import BaseModel as BaseModelV1 def recursive_serialize_or_str(obj): try: if isinstance(obj, type) and issubclass(obj, BaseModel | BaseModelV1): # This a type BaseModel and not an instance of it return repr(obj) if isinstance(obj, str): return obj if isinstance(obj, datetime): return obj.isoformat() if isinstance(obj, dict): return {k: recursive_serialize_or_str(v) for k, v in obj.items()} if isinstance(obj, list): return [recursive_serialize_or_str(v) for v in obj] if isinstance(obj, BaseModel | BaseModelV1): if hasattr(obj, "model_dump"): obj_dict = obj.model_dump() elif hasattr(obj, "dict"): obj_dict = obj.dict() return {k: recursive_serialize_or_str(v) for k, v in obj_dict.items()} if isinstance(obj, AsyncIterator | Generator | Iterator): # contain memory addresses # without consuming the iterator # return list(obj) consumes the iterator # return f"{obj}" this generates '' # it is not useful return "Unconsumed Stream" if hasattr(obj, "dict") and not isinstance(obj, type): return {k: recursive_serialize_or_str(v) for k, v in obj.dict().items()} if hasattr(obj, "model_dump") and not isinstance(obj, type): return {k: recursive_serialize_or_str(v) for k, v in obj.model_dump().items()} return str(obj) except Exception: # noqa: BLE001 logger.debug(f"Cannot serialize object {obj}") return str(obj)