feat(openai_like): add Responses API support to JSON providers - #21398
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Greptile SummaryThis PR introduces Responses API ( Key changes:
Issue found:
Confidence Score: 4/5
Last reviewed commit: da76e17 |
d4599f7 to
21af7df
Compare
|
@greptile, could u re-review please? |
21af7df to
df40a19
Compare
Greptile SummaryThis PR adds Responses API (
Confidence Score: 4/5
|
| Filename | Overview |
|---|---|
| litellm/llms/openai_like/dynamic_config.py | Adds create_responses_config_class() with proper caching (addresses the prior review concern). Minor: sends empty bearer token when no API key is configured. |
| litellm/llms/openai_like/json_loader.py | Adds supported_endpoints field and supports_responses_api() registry method. Clean, minimal additions with sensible defaults. |
| litellm/llms/openai_like/responses/transformation.py | New base class for OpenAI-like responses. get_complete_url falls back to empty string when no base URL is configured, producing an invalid /responses URL instead of raising an error. |
| litellm/llms/perplexity/responses/transformation.py | Significantly simplified from ~410 to ~40 lines. Custom tool transformation, response parsing, and streaming logic removed in favor of OpenAI base class inheritance. Only preset model handling and supported params remain. |
| litellm/responses/main.py | Removes LlmProviders() enum wrapping from custom_llm_provider before passing to get_provider_responses_api_config, allowing string-based JSON providers to be resolved. Clean, minimal change across 6 call sites. |
| litellm/utils.py | Refactors get_provider_responses_api_config to accept Union[LlmProviders, str], check Python classes first (priority), then fall back to JSON providers. Extracts existing logic into _get_python_responses_api_config. Well-structured with clear priority ordering. |
| litellm/types/llms/openai.py | Adds parse_cost field validator to ResponseAPIUsage to handle dict-format cost (e.g. Perplexity's {total_cost: 0.01}). Clean Pydantic validator approach. |
| litellm/types/utils.py | Adds dict-to-float cost parsing in Usage.__init__. Handles the case where dict lacks total_cost key by deleting the attribute (consistent with the else branch). |
| tests/test_litellm/llms/openai_like/responses/test_openai_like_responses.py | 19 unit tests covering config defaults, registry methods, dynamic class generation, URL building, header validation, inheritance, and ProviderConfigManager integration. All mock-based, no network calls. |
Flowchart
flowchart TD
A["litellm.responses(model='provider/model')"] --> B["responses/main.py\nResolve custom_llm_provider"]
B --> C["ProviderConfigManager\n.get_provider_responses_api_config()"]
C --> D{"Is provider a\nknown LlmProviders enum?"}
D -->|Yes| E["_get_python_responses_api_config()\nCheck Python class registry"]
D -->|No| F["JSONProviderRegistry\n.exists() + .supports_responses_api()"]
E -->|Found| G["Return Python config\ne.g. PerplexityResponsesConfig"]
E -->|Not found| F
F -->|Supported| H["create_responses_config_class()\nGenerate from JSON + cache"]
H --> I["Return JSONProviderResponsesConfig\ninherits OpenAILikeResponsesConfig"]
F -->|Not supported| J["Return None\nFallback to chat-based handler"]
G --> K["Execute Responses API request"]
I --> K
Last reviewed commit: df40a19
Add infrastructure for JSON-declared providers to support /v1/responses via `supported_endpoints` field in providers.json. Simplify Perplexity responses config from 410 to 40 lines by moving cost dict→float parsing to generic validators in ResponseAPIUsage and Usage. - Add `supported_endpoints` field to SimpleProviderConfig (default: []) - Add `supports_responses_api()` to JSONProviderRegistry - Create OpenAILikeResponsesConfig base class for responses API - Add `create_responses_config_class()` with class caching - ProviderConfigManager: Python classes take priority over JSON fallback - Fix ResponseAPIUsage.cost field_validator to handle dict cost objects - Fix Usage.__init__ to handle dict cost from chat completions - Simplify PerplexityResponsesConfig with get_supported_openai_params guard - Add 20 unit tests including Python-over-JSON priority test
df40a19 to
0c27f20
Compare
Resolve conflict in perplexity/responses/transformation.py by keeping the simplified ~50 line version (PR's goal) instead of main's ~410 line version. Added supports_native_websocket() -> False from main.
Tested and confirmed both o4-mini and o4-mini-2025-04-16 support web_search_preview via the Responses API.
…hat_config - Invert lookup order in get_provider_chat_config so Python classes (with custom overrides) are checked before JSON providers, matching the pattern already used in get_provider_responses_api_config. Prevents regression where providers like Perplexity (declared in providers.json) would silently lose their custom chat config. - Add 'models' to Perplexity Responses API supported params (fallback chain feature documented in Perplexity API).
- Add 'models' field to ResponsesAPIOptionalRequestParams TypedDict so the Perplexity fallback chain param is not silently filtered out by get_requested_response_api_optional_param(). Verified with curl that Perplexity's models param works (tries models in order until one succeeds). - Fix test_python_class_takes_priority_over_json: save and restore the original JSONProviderRegistry entry instead of deleting it, preventing state corruption for subsequent tests.
- Update test_all_supported_params_declared to match the simplified 8-param list (was 24 params from old 410-line implementation) - Replace test_cost_transformation tests: use ResponseAPIUsage validator directly instead of removed _transform_usage method (cost parsing moved to generic parse_cost validator)
The dynamic responses config appends /responses to base_url, so it must end with /v1 to produce the correct endpoint URL.
|
@greptile |
|
Got it - thanks for the clarification. If |
…r 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.
…reaming cost test Remove the isinstance(cost, dict) guard from Usage.__init__ — ResponseAPIUsage.parse_cost validator already converts Perplexity's cost dict to float before it reaches Usage. Add test_streaming_cost_dict_to_float_via_validator to verify the dict→float conversion works end-to-end through transform_streaming_response (pydantic recursive construction).
|
@greptile. preset and models do not work in main as explicit parameters — they were filtered out by |
Perplexity has a dedicated Python class (PerplexityResponsesConfig) that always takes priority over JSON config. The JSON entry was dead code.
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Perplexity requires type='message' on input items. Add _ensure_message_type to automatically set it when missing, matching the old _format_input behavior.
260c37d
into
BerriAI:litellm_oss_staging_03_10_2026
| def get_complete_url( | ||
| self, | ||
| api_base: Optional[str], | ||
| litellm_params: dict, | ||
| ) -> str: | ||
| if not api_base: | ||
| if provider.api_base_env: | ||
| api_base = get_secret_str(provider.api_base_env) | ||
| if not api_base: | ||
| api_base = provider.base_url | ||
|
|
||
| if api_base is None: | ||
| raise ValueError( | ||
| f"api_base is required for provider {provider.slug}" | ||
| ) | ||
|
|
||
| api_base = api_base.rstrip("/") | ||
| return f"{api_base}/responses" |
There was a problem hiding this comment.
The generated URL appends only /responses to api_base. This silently produces an incorrect endpoint for any provider whose base_url does NOT already end with /v1 (e.g., "https://api.provider.com"). The documentation examples show base_url ending in /v1 (e.g., "https://api.yourprovider.com/v1"), but nothing in the code validates or enforces this convention.
If a provider sets base_url: "https://api.provider.com" (without /v1) and supported_endpoints: ["/v1/responses"], the generated URL becomes https://api.provider.com/responses — which fails at runtime with a confusing 404.
Consider adding a validation check to enforce the convention and provide a clear error message:
| def get_complete_url( | |
| self, | |
| api_base: Optional[str], | |
| litellm_params: dict, | |
| ) -> str: | |
| if not api_base: | |
| if provider.api_base_env: | |
| api_base = get_secret_str(provider.api_base_env) | |
| if not api_base: | |
| api_base = provider.base_url | |
| if api_base is None: | |
| raise ValueError( | |
| f"api_base is required for provider {provider.slug}" | |
| ) | |
| api_base = api_base.rstrip("/") | |
| return f"{api_base}/responses" | |
| if api_base is None: | |
| raise ValueError( | |
| f"api_base is required for provider {provider.slug}" | |
| ) | |
| api_base = api_base.rstrip("/") | |
| # Validate that base_url ends with /v1 for responses endpoint | |
| if not api_base.endswith("/v1"): | |
| raise ValueError( | |
| f"base_url for provider '{provider.slug}' must end with '/v1' to use the Responses API " | |
| f"(got '{api_base}'). Example: 'https://api.yourprovider.com/v1'" | |
| ) | |
| return f"{api_base}/responses" |
…ses-api feat(openai_like): add Responses API support to JSON providers
Relevant issues
Enables JSON-declared providers to support
/v1/responseswithout Python code. Simplifies Perplexity responses config (410 → 40 lines).Pre-Submission checklist
Please complete all items before asking a LiteLLM maintainer to review your PR
tests/litellm/directory, Adding at least 1 test is a hard requirement - see detailsmake test-unit@greptileaiand received a Confidence Score of at least 4/5 before requesting a maintainer reviewType
🆕 New Feature
🧹 Refactoring
Changes
Responses API infrastructure for JSON providers
supported_endpointsfield toSimpleProviderConfig(default:[])supports_responses_api()toJSONProviderRegistryOpenAILikeResponsesConfigbase class inlitellm/llms/openai_like/responses/create_responses_config_class()for dynamic config generation from JSONProviderConfigManager.get_provider_responses_api_config()now acceptsUnion[LlmProviders, str]and checks JSON providers firstPerplexity (410 → 40 lines)
dict→floatparsing to generic validators (ResponseAPIUsage.parse_cost+Usage.__init__)PerplexityResponsesConfig: onlypreset/model handling remains, everything else inherited fromOpenAIResponsesAPIConfigHow to use
Add
supported_endpointsto any JSON provider inproviders.json:{ "your_provider": { "base_url": "https://api.provider.com/v1", "api_key_env": "PROVIDER_API_KEY", "supported_endpoints": ["/v1/chat/completions", "/v1/responses"] } }Tests
supported_endpointsdefaults,supports_responses_api(), dynamic class generation (URL, headers, inheritance),ProviderConfigManagerintegration