chore: initial public snapshot for github upload
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
"""
|
||||
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 Any, Dict, List, 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, ResponsesAPIResponse
|
||||
from litellm.types.router import GenericLiteLLMParams
|
||||
from litellm.types.utils import LlmProviders
|
||||
|
||||
|
||||
class PerplexityResponsesConfig(OpenAIResponsesAPIConfig):
|
||||
def get_supported_openai_params(self, model: str) -> list:
|
||||
"""Ref: https://docs.perplexity.ai/api-reference/responses-post"""
|
||||
return [
|
||||
"max_output_tokens",
|
||||
"stream",
|
||||
"temperature",
|
||||
"top_p",
|
||||
"tools",
|
||||
"reasoning",
|
||||
"instructions",
|
||||
"models",
|
||||
]
|
||||
|
||||
@property
|
||||
def custom_llm_provider(self) -> LlmProviders:
|
||||
return LlmProviders.PERPLEXITY
|
||||
|
||||
def validate_environment(
|
||||
self, headers: dict, model: str, litellm_params: Optional[GenericLiteLLMParams]
|
||||
) -> dict:
|
||||
litellm_params = litellm_params or GenericLiteLLMParams()
|
||||
api_key = (
|
||||
litellm_params.api_key
|
||||
or get_secret_str("PERPLEXITYAI_API_KEY")
|
||||
or get_secret_str("PERPLEXITY_API_KEY")
|
||||
)
|
||||
if api_key:
|
||||
headers["Authorization"] = f"Bearer {api_key}"
|
||||
return headers
|
||||
|
||||
def get_complete_url(self, api_base: Optional[str], litellm_params: dict) -> str:
|
||||
api_base = (
|
||||
api_base
|
||||
or get_secret_str("PERPLEXITY_API_BASE")
|
||||
or "https://api.perplexity.ai"
|
||||
)
|
||||
return f"{api_base.rstrip('/')}/v1/responses"
|
||||
|
||||
def _ensure_message_type(
|
||||
self, input: Union[str, ResponseInputParam]
|
||||
) -> Union[str, ResponseInputParam]:
|
||||
"""Ensure list input items have type='message' (required by Perplexity)."""
|
||||
if isinstance(input, str):
|
||||
return input
|
||||
if isinstance(input, list):
|
||||
result: List[Any] = []
|
||||
for item in input:
|
||||
if isinstance(item, dict) and "type" not in item:
|
||||
new_item = dict(item) # convert to plain dict to avoid TypedDict checking
|
||||
new_item["type"] = "message"
|
||||
result.append(new_item)
|
||||
else:
|
||||
result.append(item)
|
||||
return result
|
||||
return input
|
||||
|
||||
def transform_responses_api_request(
|
||||
self,
|
||||
model: str,
|
||||
input: Union[str, ResponseInputParam],
|
||||
response_api_optional_request_params: Dict,
|
||||
litellm_params: GenericLiteLLMParams,
|
||||
headers: dict,
|
||||
) -> Dict:
|
||||
"""Handle preset/ model prefix: send as {"preset": name} instead of {"model": name}."""
|
||||
input = self._ensure_message_type(input)
|
||||
if model.startswith("preset/"):
|
||||
input = self._validate_input_param(input)
|
||||
data: Dict = {
|
||||
"preset": model[len("preset/") :],
|
||||
"input": input,
|
||||
}
|
||||
data.update(response_api_optional_request_params)
|
||||
return data
|
||||
return super().transform_responses_api_request(
|
||||
model=model,
|
||||
input=input,
|
||||
response_api_optional_request_params=response_api_optional_request_params,
|
||||
litellm_params=litellm_params,
|
||||
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
|
||||
Reference in New Issue
Block a user