aidial_adapter_bedrock/utils/json.py (77 lines of code) (raw):

""" Utilities for pretty-printing JSON in debug logs. These functions are useful for dumping large data structures, with options to trim long strings and lists to specified limits. """ import json from dataclasses import asdict, is_dataclass from enum import Enum from typing import Any from pydantic import BaseModel def remove_nones(d: dict) -> dict: return {k: v for k, v in d.items() if v is not None} def json_dumps_short( obj: Any, *, string_limit: int = 100, list_len_limit: int = 10, **kwargs ) -> str: def default(obj) -> str: return _truncate_strings(str(obj), string_limit) return json.dumps( _truncate_lists( _truncate_strings(_to_dict(obj, **kwargs), string_limit), list_len_limit, ), default=default, ) def json_dumps(obj: Any, **kwargs) -> str: return json.dumps(_to_dict(obj, **kwargs)) def _to_dict(obj: Any, **kwargs) -> Any: def rec(val): return _to_dict(val, **kwargs) def dict_field(key: str, val: Any) -> Any: if key in kwargs.get("excluded_keys", []): return "<excluded>" return val if isinstance(obj, bytes): return f"<bytes>({len(obj):_} B)" if isinstance(obj, Enum): return obj.value if isinstance(obj, dict): return {key: rec(dict_field(key, value)) for key, value in obj.items()} if isinstance(obj, list): return [rec(element) for element in obj] if isinstance(obj, tuple): return tuple(rec(element) for element in obj) if isinstance(obj, BaseModel): return rec(obj.dict()) if hasattr(obj, "to_dict"): return rec(obj.to_dict()) if is_dataclass(type(obj)): return rec(asdict(obj)) return obj def _truncate_strings(obj: Any, limit: int) -> Any: def rec(val): return _truncate_strings(val, limit) if isinstance(obj, dict): return {key: rec(value) for key, value in obj.items()} if isinstance(obj, list): return [rec(element) for element in obj] if isinstance(obj, tuple): return tuple(rec(element) for element in obj) if isinstance(obj, str) and len(obj) > limit: skip = len(obj) - limit return ( obj[: limit // 2] + f"...({skip:_} skipped)..." + obj[-limit // 2 :] ) return obj def _truncate_lists(obj: Any, limit: int) -> Any: def rec(val): return _truncate_lists(val, limit) if isinstance(obj, dict): return {key: rec(value) for key, value in obj.items()} if isinstance(obj, list): if len(obj) > limit: skip = len(obj) - limit obj = ( obj[: limit // 2] + [f"...({skip:_} skipped)..."] + obj[-limit // 2 :] ) return [rec(element) for element in obj] if isinstance(obj, tuple): return tuple(rec(element) for element in obj) return obj