mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-16 23:41:43 +00:00
Use sanitize deep copy style to replace deepcopy usage
This commit is contained in:
parent
af35286cb2
commit
ed0350fa3c
1 changed files with 36 additions and 15 deletions
|
|
@ -7,7 +7,6 @@
|
|||
import enum
|
||||
import json
|
||||
import os
|
||||
from copy import deepcopy
|
||||
from datetime import datetime
|
||||
from typing import TYPE_CHECKING, Any, Literal, Optional, Type, cast
|
||||
from urllib.parse import urlparse
|
||||
|
|
@ -131,6 +130,35 @@ class NomaV2Guardrail(CustomGuardrail):
|
|||
|
||||
raise ValueError("Noma v2 response missing valid action")
|
||||
|
||||
@staticmethod
|
||||
def _json_safe_copy(data: Any) -> Any:
|
||||
"""JSON round-trip producing a deep, JSON-serializable copy of `data`.
|
||||
|
||||
Fast path: `json.dumps` (C-level) with a `default=` callback that
|
||||
handles Pydantic models and falls back to `str(obj)` for anything
|
||||
non-serializable (e.g. uvloop.Loop, locks, other C-extension objects
|
||||
whose __reduce__ raises — which is why this replaces deepcopy).
|
||||
|
||||
Slow path: if `json.dumps` raises (circular refs, etc.), retry with
|
||||
`safe_dumps` which walks the tree in Python with explicit cycle
|
||||
detection. Finally `safe_json_loads` parses back to a Python tree.
|
||||
"""
|
||||
|
||||
def _default(obj: Any) -> Any:
|
||||
if hasattr(obj, "model_dump"):
|
||||
try:
|
||||
return obj.model_dump()
|
||||
except Exception:
|
||||
pass
|
||||
return str(obj)
|
||||
|
||||
try:
|
||||
json_str = json.dumps(data, default=_default)
|
||||
except (ValueError, TypeError):
|
||||
json_str = safe_dumps(data)
|
||||
|
||||
return safe_json_loads(json_str, default={})
|
||||
|
||||
def _build_scan_payload(
|
||||
self,
|
||||
inputs: GenericGuardrailAPIInputs,
|
||||
|
|
@ -139,7 +167,12 @@ class NomaV2Guardrail(CustomGuardrail):
|
|||
logging_obj: Optional["LiteLLMLoggingObj"],
|
||||
application_id: Optional[str],
|
||||
) -> dict:
|
||||
payload_request_data = deepcopy(request_data)
|
||||
# JSON round-trip rather than deepcopy: on post_call / during_call /
|
||||
# during_mcp_call, request_data contains uvloop.Loop and other C-extension
|
||||
# objects whose __reduce__ raises, crashing pickle-based copy.
|
||||
payload_request_data = self._json_safe_copy(request_data)
|
||||
if not isinstance(payload_request_data, dict):
|
||||
payload_request_data = {}
|
||||
if logging_obj is not None:
|
||||
payload_request_data["litellm_logging_obj"] = getattr(
|
||||
logging_obj, "model_call_details", None
|
||||
|
|
@ -157,20 +190,8 @@ class NomaV2Guardrail(CustomGuardrail):
|
|||
|
||||
@staticmethod
|
||||
def _sanitize_payload_for_transport(payload: dict) -> dict:
|
||||
def _default(obj: Any) -> Any:
|
||||
if hasattr(obj, "model_dump"):
|
||||
try:
|
||||
return obj.model_dump()
|
||||
except Exception:
|
||||
pass
|
||||
return str(obj)
|
||||
safe_payload = NomaV2Guardrail._json_safe_copy(payload)
|
||||
|
||||
try:
|
||||
json_str = json.dumps(payload, default=_default)
|
||||
except (ValueError, TypeError):
|
||||
json_str = safe_dumps(payload)
|
||||
|
||||
safe_payload = safe_json_loads(json_str, default={})
|
||||
if safe_payload == {} and payload:
|
||||
verbose_proxy_logger.warning(
|
||||
"Noma v2 guardrail: payload serialization failed, falling back to empty payload"
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue