mirror of
https://github.com/tiennm99/litellm.git
synced 2026-08-03 08:23:16 +00:00
fix(perplexity): remove models from shared TypedDict and restore error handling
- Remove Perplexity-specific `models` field from ResponsesAPIOptionalRequestParams TypedDict to avoid polluting the shared OpenAI type. The param is still passed through via get_supported_openai_params + map_openai_params at runtime. - Add transform_response_api_response override to catch Perplexity's HTTP 200 with status:"failed" and raise BaseLLMException instead of silently succeeding.
This commit is contained in:
@@ -4,15 +4,20 @@ Perplexity Responses API — OpenAI-compatible.
|
||||
The only provider quirks:
|
||||
- cost returned as dict → handled by ResponseAPIUsage.parse_cost validator
|
||||
- preset models (preset/pro-search) → handled by transform_responses_api_request
|
||||
- HTTP 200 with status:"failed" → raised as exception in transform_response_api_response
|
||||
|
||||
Ref: https://docs.perplexity.ai/api-reference/responses-post
|
||||
"""
|
||||
|
||||
from typing import Dict, Optional, Union
|
||||
|
||||
import httpx
|
||||
|
||||
from litellm.litellm_core_utils.litellm_logging import Logging as LiteLLMLoggingObj
|
||||
from litellm.llms.base_llm.chat.transformation import BaseLLMException
|
||||
from litellm.llms.openai.responses.transformation import OpenAIResponsesAPIConfig
|
||||
from litellm.secret_managers.main import get_secret_str
|
||||
from litellm.types.llms.openai import ResponseInputParam
|
||||
from litellm.types.llms.openai import ResponseInputParam, ResponsesAPIResponse
|
||||
from litellm.types.router import GenericLiteLLMParams
|
||||
from litellm.types.utils import LlmProviders
|
||||
|
||||
@@ -78,6 +83,34 @@ class PerplexityResponsesConfig(OpenAIResponsesAPIConfig):
|
||||
headers=headers,
|
||||
)
|
||||
|
||||
def transform_response_api_response(
|
||||
self,
|
||||
model: str,
|
||||
raw_response: httpx.Response,
|
||||
logging_obj: LiteLLMLoggingObj,
|
||||
) -> ResponsesAPIResponse:
|
||||
"""Check for Perplexity's status:'failed' on HTTP 200 before delegating to base."""
|
||||
try:
|
||||
raw_response_json = raw_response.json()
|
||||
except Exception:
|
||||
raw_response_json = None
|
||||
|
||||
if (
|
||||
isinstance(raw_response_json, dict)
|
||||
and raw_response_json.get("status") == "failed"
|
||||
):
|
||||
error = raw_response_json.get("error", {})
|
||||
raise BaseLLMException(
|
||||
status_code=raw_response.status_code,
|
||||
message=error.get("message", "Unknown Perplexity error"),
|
||||
)
|
||||
|
||||
return super().transform_response_api_response(
|
||||
model=model,
|
||||
raw_response=raw_response,
|
||||
logging_obj=logging_obj,
|
||||
)
|
||||
|
||||
def supports_native_websocket(self) -> bool:
|
||||
"""Perplexity does not support native WebSocket for Responses API"""
|
||||
return False
|
||||
|
||||
@@ -1155,8 +1155,6 @@ class ResponsesAPIOptionalRequestParams(TypedDict, total=False):
|
||||
] # Number of partial images to generate (1-3) for streaming image generation
|
||||
context_management: Optional[List[ContextManagementEntry]]
|
||||
"""Context management configuration. E.g. [{\"type\": \"compaction\", \"compact_threshold\": 200000}] for server-side compaction (minimum 1000)."""
|
||||
models: Optional[List[str]]
|
||||
"""Model fallback chain (e.g. Perplexity). Models are tried in order until one succeeds."""
|
||||
|
||||
|
||||
class ResponsesAPIRequestParams(ResponsesAPIOptionalRequestParams, total=False):
|
||||
|
||||
+93
@@ -7,11 +7,17 @@ transformations for the Agent API (Responses API).
|
||||
Source: litellm/llms/perplexity/responses/transformation.py
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
|
||||
import httpx
|
||||
import pytest
|
||||
|
||||
sys.path.insert(0, os.path.abspath("../../../../.."))
|
||||
|
||||
from litellm.litellm_core_utils.litellm_logging import Logging as LiteLLMLoggingObj
|
||||
from litellm.llms.base_llm.chat.transformation import BaseLLMException
|
||||
from litellm.llms.perplexity.responses.transformation import PerplexityResponsesConfig
|
||||
from litellm.types.llms.openai import ResponsesAPIOptionalRequestParams
|
||||
from litellm.types.utils import LlmProviders
|
||||
@@ -359,3 +365,90 @@ class TestPerplexityResponsesTransformation:
|
||||
assert config is not None
|
||||
assert isinstance(config, PerplexityResponsesConfig)
|
||||
assert config.custom_llm_provider == LlmProviders.PERPLEXITY
|
||||
|
||||
def test_failed_status_raises_exception(self):
|
||||
"""Perplexity HTTP 200 with status:'failed' must raise BaseLLMException"""
|
||||
config = PerplexityResponsesConfig()
|
||||
|
||||
failed_body = {
|
||||
"status": "failed",
|
||||
"error": {"message": "Model quota exceeded"},
|
||||
}
|
||||
|
||||
raw_response = httpx.Response(
|
||||
status_code=200,
|
||||
json=failed_body,
|
||||
request=httpx.Request("POST", "https://api.perplexity.ai/v1/responses"),
|
||||
)
|
||||
|
||||
logging_obj = LiteLLMLoggingObj(
|
||||
model="perplexity/openai/gpt-5.2",
|
||||
messages=[],
|
||||
stream=False,
|
||||
call_type="responses",
|
||||
start_time=None,
|
||||
litellm_call_id="test",
|
||||
function_id="test",
|
||||
)
|
||||
|
||||
with pytest.raises(BaseLLMException) as exc_info:
|
||||
config.transform_response_api_response(
|
||||
model="perplexity/openai/gpt-5.2",
|
||||
raw_response=raw_response,
|
||||
logging_obj=logging_obj,
|
||||
)
|
||||
|
||||
assert "Model quota exceeded" in str(exc_info.value.message)
|
||||
|
||||
def test_successful_response_passes_through(self):
|
||||
"""Normal completed response delegates to base OpenAI handler"""
|
||||
config = PerplexityResponsesConfig()
|
||||
|
||||
success_body = {
|
||||
"id": "resp_123",
|
||||
"object": "response",
|
||||
"created_at": 1700000000,
|
||||
"status": "completed",
|
||||
"model": "openai/gpt-5.2",
|
||||
"output": [
|
||||
{
|
||||
"type": "message",
|
||||
"id": "msg_123",
|
||||
"role": "assistant",
|
||||
"status": "completed",
|
||||
"content": [
|
||||
{"type": "output_text", "text": "Hello!", "annotations": []}
|
||||
],
|
||||
}
|
||||
],
|
||||
"usage": {
|
||||
"input_tokens": 10,
|
||||
"output_tokens": 5,
|
||||
"total_tokens": 15,
|
||||
},
|
||||
}
|
||||
|
||||
raw_response = httpx.Response(
|
||||
status_code=200,
|
||||
json=success_body,
|
||||
request=httpx.Request("POST", "https://api.perplexity.ai/v1/responses"),
|
||||
)
|
||||
|
||||
logging_obj = LiteLLMLoggingObj(
|
||||
model="perplexity/openai/gpt-5.2",
|
||||
messages=[],
|
||||
stream=False,
|
||||
call_type="responses",
|
||||
start_time=None,
|
||||
litellm_call_id="test",
|
||||
function_id="test",
|
||||
)
|
||||
|
||||
response = config.transform_response_api_response(
|
||||
model="perplexity/openai/gpt-5.2",
|
||||
raw_response=raw_response,
|
||||
logging_obj=logging_obj,
|
||||
)
|
||||
|
||||
assert response.id == "resp_123"
|
||||
assert response.status == "completed"
|
||||
|
||||
Reference in New Issue
Block a user