This commit is contained in:
stardust 2026-09-12 23:48:22 -07:00 committed by GitHub
commit 050c7bf955
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 72 additions and 1 deletions

View file

@ -22,6 +22,18 @@ from litellm.types.router import GenericLiteLLMParams
from litellm.types.utils import LlmProviders
def perplexity_uses_agent_api(model: str) -> bool:
"""Whether a Perplexity model id names the Agent API rather than the chat endpoint.
Agent API ids keep their own vendor namespace once litellm's ``perplexity/`` prefix is
stripped (``perplexity/sonar``, ``openai/gpt-5.2``, ``preset/pro-search``), while chat,
embedding and search ids are a single segment (``sonar``, ``sonar-pro``). Reading the id
keeps routing independent of the cost map, which does not carry every model Perplexity
serves and whose flat ``perplexity/sonar`` chat entry otherwise shadows the Agent API one.
"""
return "/" in model
class PerplexityResponsesConfig(OpenAIResponsesAPIConfig):
def get_supported_openai_params(self, model: str) -> list:
"""Ref: https://docs.perplexity.ai/api-reference/responses-post"""

View file

@ -108,6 +108,7 @@ from litellm.llms.cohere.common_utils import CohereModelInfo
from litellm.llms.custom_httpx.http_handler import AsyncHTTPHandler, HTTPHandler
from litellm.llms.openai.chat.gpt_5_transformation import OpenAIGPT5Config
from litellm.llms.openai_like.json_loader import JSONProviderRegistry
from litellm.llms.perplexity.responses.transformation import perplexity_uses_agent_api
from litellm.llms.vertex_ai.common_utils import (
VertexAIModelRoute,
get_vertex_ai_model_route,
@ -1084,6 +1085,9 @@ def responses_api_bridge_check(
mode = "responses"
model_info["mode"] = mode
if custom_llm_provider == "perplexity" and perplexity_uses_agent_api(model):
model_info["mode"] = "responses"
# OpenAI/Azure GPT-5 chat-completions that need Responses-only fields (e.g.
# ``reasoningSummary`` in ``extra_body``) must be bridged; Chat Completions rejects
# those keys.

View file

@ -15,7 +15,10 @@ import pytest
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.llms.perplexity.responses.transformation import (
PerplexityResponsesConfig,
perplexity_uses_agent_api,
)
from litellm.types.llms.openai import ResponsesAPIOptionalRequestParams
from litellm.types.utils import LlmProviders
from litellm.utils import ProviderConfigManager
@ -587,3 +590,55 @@ class TestPerplexityResponsesTransformation:
assert result.type == "response.completed"
assert result.response.usage.cost == 0.0003
assert isinstance(result.response.usage.cost, float)
class TestPerplexityAgentApiRouting:
"""Routing an Agent API deployment to /v1/responses must not depend on the cost map.
``completion()`` picks the responses bridge off ``mode`` in the cost map, so an Agent API
model litellm ships no pricing for was unroutable on /v1/chat/completions, and
``perplexity/perplexity/sonar`` resolved to the older flat ``perplexity/sonar`` chat entry
several steps before the doubled-prefix key. Both went out on Perplexity's chat endpoint
and came back 400 naming a model id nobody wrote.
"""
@pytest.mark.parametrize(
"model",
[
"perplexity/sonar",
"perplexity/glm-5.2",
"perplexity/nemotron-3-ultra-550b-a55b",
"openai/gpt-5.2",
"anthropic/claude-opus-4-7",
"preset/pro-search",
],
)
def test_agent_api_models_bridge_to_responses(self, model):
from litellm.main import responses_api_bridge_check
model_info, _ = responses_api_bridge_check(model=model, custom_llm_provider="perplexity")
assert model_info.get("mode") == "responses"
@pytest.mark.parametrize("model", ["sonar", "sonar-pro", "sonar-reasoning", "llama-3.1-70b-instruct"])
def test_chat_models_stay_on_chat_completions(self, model):
from litellm.main import responses_api_bridge_check
model_info, _ = responses_api_bridge_check(model=model, custom_llm_provider="perplexity")
assert model_info.get("mode") != "responses"
def test_other_providers_keep_a_slash_in_the_model_name_on_chat(self):
from litellm.main import responses_api_bridge_check
model_info, _ = responses_api_bridge_check(
model="meta-llama/Llama-3.3-70B-Instruct", custom_llm_provider="together_ai"
)
assert model_info.get("mode") != "responses"
def test_agent_api_ids_are_told_apart_by_their_vendor_namespace(self):
assert perplexity_uses_agent_api("perplexity/sonar") is True
assert perplexity_uses_agent_api("preset/pro-search") is True
assert perplexity_uses_agent_api("sonar") is False
assert perplexity_uses_agent_api("sonar-pro") is False