diff --git a/litellm/litellm_core_utils/llm_request_utils.py b/litellm/litellm_core_utils/llm_request_utils.py new file mode 100644 index 000000000..cd371ee1d --- /dev/null +++ b/litellm/litellm_core_utils/llm_request_utils.py @@ -0,0 +1,30 @@ +from typing import Dict, Optional + +from langfuse.model import TextPromptClient + + +def _ensure_extra_body_is_safe(extra_body: Optional[Dict]) -> Optional[Dict]: + """ + Ensure that the extra_body sent in the request is safe, otherwise users will see this error + + "Object of type TextPromptClient is not JSON serializable + + + Relevant Issue: https://github.com/BerriAI/litellm/issues/4140 + """ + if extra_body is None: + return None + + if not isinstance(extra_body, dict): + return extra_body + + if "metadata" in extra_body and isinstance(extra_body["metadata"], dict): + if "prompt" in extra_body["metadata"]: + _prompt = extra_body["metadata"].get("prompt") + + # users can send Langfuse TextPromptClient objects, so we need to convert them to dicts + # Langfuse TextPromptClients have .__dict__ attribute + if _prompt is not None and hasattr(_prompt, "__dict__"): + extra_body["metadata"]["prompt"] = _prompt.__dict__ + + return extra_body diff --git a/litellm/utils.py b/litellm/utils.py index eac07c550..795526a32 100644 --- a/litellm/utils.py +++ b/litellm/utils.py @@ -50,6 +50,7 @@ import litellm._service_logger # for storing API inputs, outputs, and metadata import litellm.litellm_core_utils from litellm.caching import DualCache from litellm.litellm_core_utils.core_helpers import map_finish_reason +from litellm.litellm_core_utils.llm_request_utils import _ensure_extra_body_is_safe from litellm.litellm_core_utils.redact_messages import ( redact_message_input_output_from_logging, ) @@ -3256,6 +3257,10 @@ def get_optional_params( extra_body[k] = passed_params[k] optional_params.setdefault("extra_body", {}) optional_params["extra_body"] = {**optional_params["extra_body"], **extra_body} + + optional_params["extra_body"] = _ensure_extra_body_is_safe( + extra_body=optional_params["extra_body"] + ) else: # if user passed in non-default kwargs for specific providers/models, pass them along for k in passed_params.keys():