92 lines
3.2 KiB
Python
92 lines
3.2 KiB
Python
|
|
from typing import Any, Dict, Optional, Tuple
|
||
|
|
|
||
|
|
from litellm.exceptions import ContentPolicyViolationError
|
||
|
|
|
||
|
|
|
||
|
|
class AzureOpenAIExceptionMapping:
|
||
|
|
"""
|
||
|
|
Class for creating Azure OpenAI specific exceptions
|
||
|
|
"""
|
||
|
|
|
||
|
|
@staticmethod
|
||
|
|
def create_content_policy_violation_error(
|
||
|
|
message: str,
|
||
|
|
model: str,
|
||
|
|
extra_information: str,
|
||
|
|
original_exception: Exception,
|
||
|
|
) -> ContentPolicyViolationError:
|
||
|
|
"""
|
||
|
|
Create a content policy violation error
|
||
|
|
"""
|
||
|
|
azure_error, inner_error = AzureOpenAIExceptionMapping._extract_azure_error(
|
||
|
|
original_exception
|
||
|
|
)
|
||
|
|
|
||
|
|
# Prefer the provider message/type/code when present.
|
||
|
|
provider_message = (
|
||
|
|
azure_error.get("message") if isinstance(azure_error, dict) else None
|
||
|
|
) or message
|
||
|
|
provider_type = (
|
||
|
|
azure_error.get("type") if isinstance(azure_error, dict) else None
|
||
|
|
)
|
||
|
|
provider_code = (
|
||
|
|
azure_error.get("code") if isinstance(azure_error, dict) else None
|
||
|
|
)
|
||
|
|
|
||
|
|
# Keep the OpenAI-style body fields populated so downstream (proxy + SDK)
|
||
|
|
# can surface `type` / `code` correctly.
|
||
|
|
openai_style_body: Dict[str, Any] = {
|
||
|
|
"message": provider_message,
|
||
|
|
"type": provider_type or "invalid_request_error",
|
||
|
|
"code": provider_code or "content_policy_violation",
|
||
|
|
"param": None,
|
||
|
|
}
|
||
|
|
|
||
|
|
raise ContentPolicyViolationError(
|
||
|
|
message=provider_message,
|
||
|
|
llm_provider="azure",
|
||
|
|
model=model,
|
||
|
|
litellm_debug_info=extra_information,
|
||
|
|
response=getattr(original_exception, "response", None),
|
||
|
|
provider_specific_fields={
|
||
|
|
# Preserve legacy key for backward compatibility.
|
||
|
|
"innererror": inner_error,
|
||
|
|
# Prefer Azure's current naming.
|
||
|
|
"inner_error": inner_error,
|
||
|
|
# Include the full Azure error object for clients that want it.
|
||
|
|
"azure_error": azure_error or None,
|
||
|
|
},
|
||
|
|
body=openai_style_body,
|
||
|
|
)
|
||
|
|
|
||
|
|
@staticmethod
|
||
|
|
def _extract_azure_error(
|
||
|
|
original_exception: Exception,
|
||
|
|
) -> Tuple[Dict[str, Any], Optional[dict]]:
|
||
|
|
"""Extract Azure OpenAI error payload and inner error details.
|
||
|
|
|
||
|
|
Azure error formats can vary by endpoint/version. Common shapes:
|
||
|
|
- {"innererror": {...}} (legacy)
|
||
|
|
- {"error": {"code": "...", "message": "...", "type": "...", "inner_error": {...}}}
|
||
|
|
- {"code": "...", "message": "...", "type": "..."} (already flattened)
|
||
|
|
"""
|
||
|
|
body_dict = getattr(original_exception, "body", None) or {}
|
||
|
|
if not isinstance(body_dict, dict):
|
||
|
|
return {}, None
|
||
|
|
|
||
|
|
# Some SDKs place the payload under "error".
|
||
|
|
azure_error: Dict[str, Any]
|
||
|
|
if isinstance(body_dict.get("error"), dict):
|
||
|
|
azure_error = body_dict.get("error", {}) # type: ignore[assignment]
|
||
|
|
else:
|
||
|
|
azure_error = body_dict
|
||
|
|
|
||
|
|
inner_error = (
|
||
|
|
azure_error.get("inner_error")
|
||
|
|
or azure_error.get("innererror")
|
||
|
|
or body_dict.get("innererror")
|
||
|
|
or body_dict.get("inner_error")
|
||
|
|
)
|
||
|
|
|
||
|
|
return azure_error, inner_error
|