Litellm oss staging 03 10 2026 - #23276
Conversation
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
Replace if/elif chain in map_finish_reason() with _FINISH_REASON_MAP dict covering all known provider values. Unknown values now default to "stop" with a warning log. Fix Gemini FINISH_REASON_UNSPECIFIED and MALFORMED_FUNCTION_CALL returning non-OpenAI values. Add missing Gemini values (TOO_MANY_TOOL_CALLS, MALFORMED_RESPONSE). Clean OpenAIChatCompletionFinishReason type and OPENAI_FINISH_REASONS constant. Fixes #21744, #21041, #16651, #19744, #21348, #22003
…NISH_REASON_MAP Addresses Greptile review feedback on PR #22138 — removes duplicated Gemini finish reason dict in VertexGeminiConfig and delegates to the shared map_finish_reason() to prevent the two mappings from drifting apart.
…enAIChatCompletionFinishReason Fixes mypy errors where dict[str, str] was incompatible with the expected Literal type in get_finish_reason_mapping() and _check_finish_reason() return types.
…calls When using extended thinking with web search, Anthropic interleaves thinking blocks between server_tool_use/tool_result blocks. The previous code prepended all thinking blocks first, then appended tool calls last, breaking Anthropic's thinking block signature verification on round-trip. This change detects when both thinking_blocks and server tool calls (srvtoolu_*) are present, and interleaves them in the original order: each thinking block precedes its corresponding server tool use group. This preserves the signature positions that Anthropic validates. Fixes #23047
Add input_fidelity parameter ("high"/"low") to the image edit pipeline,
allowing users to control how much effort the model exerts to match
input image style and features. Fixes #22813.
Extend the "Proxy database access" section with guidelines to prevent common DB performance issues, tailored to actual Prisma usage patterns in the litellm codebase: N+1 queries, client-side processing, batching writes, bounding result sets, select on wide tables, index coverage, and schema file sync. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…meters
Gemini 2.0+ natively accepts JSON Schema in tool parameters, including
bare {} (TYPE_UNSPECIFIED), anyOf with null, and lowercase types. The
existing _build_vertex_schema pipeline was coercing {} to {"type": "object"},
breaking JsonValue/Any field semantics (issue #22391).
Add _build_vertex_schema_for_gemini_2() that only resolves $ref (which
Gemini doesn't support in tools) and filters unsupported fields. Use it
for Gemini 2.0+ models, keeping the full transform for Gemini 1.5.
…idelines docs: add DB query performance guidelines to CLAUDE.md
…_schema_for_gemini_2 Avoids silently removing $defs from the caller's dict, which could affect logging, caching, or retry logic referencing the same object.
* fix: add missing indexes for top CPU-consuming queries Add indexes to eliminate full table scans on two of the top 5 queries by CPU usage: 1. LiteLLM_VerificationToken(key_alias) — for ORDER BY key_alias ASC queries when listing verification tokens 2. LiteLLM_SpendLogs(user, startTime) — for WHERE user = $1 AND startTime BETWEEN $2 AND $3 GROUP BY queries on the spend logs Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: use CREATE INDEX CONCURRENTLY to avoid table locks Both indexes are now created with CONCURRENTLY and IF NOT EXISTS to avoid blocking writes on large production tables. Uses -- SkipTransactionBlock for Prisma migrate compatibility. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
When assistant content is already a list containing thinking blocks inline (not str/None), SEQUENTIAL MODE was still prepending all thinking_blocks from provider_specific_fields, causing duplication and breaking Anthropic's position-dependent signature verification. Now detects if the content list already has thinking blocks and skips the extend(thinking_blocks) to preserve the original interleaved order. Addresses the correctness gap identified by Greptile review where list-content messages bypass INTERLEAVED MODE. Fixes: #23047
…pecified to prevent ValidationError in stream_chunk_builder (#22673) * fix(streaming): map unknown finish_reason values to finish_reason_unspecified Some LLM providers return non-standard finish_reason values that are not in the OpenAIChatCompletionFinishReason Literal (e.g. ZhipuAI/GLM returns 'network_error' when a streaming error occurs mid-response). Previously map_finish_reason() fell through with return finish_reason, passing the unknown value directly to Choices.__init__() which calls Pydantic validation. This caused a ValidationError that was caught by stream_chunk_builder() and re-raised as the misleading: litellm.APIError: Error building chunks for logging/streaming usage calculation Fix: after all known provider-specific mappings, check if the value is in the valid set (stop, length, tool_calls, content_filter, function_call, guardrail_intervened, eos, finish_reason_unspecified, malformed_function_call). Any value not in this set is mapped to 'finish_reason_unspecified' instead of being returned as-is. This is consistent with how other unknown stop reasons (e.g. Vertex AI's FINISH_REASON_UNSPECIFIED) are already handled. * refactor: use get_args(OpenAIChatCompletionFinishReason) for valid set Per code review feedback: replace the hardcoded _valid_finish_reasons set with a module-level frozenset derived dynamically from the source-of-truth Literal type via typing.get_args(). This ensures the valid-reason check stays in sync automatically when new finish reasons are added to the Literal, and avoids recreating the set on every streaming chunk call. * test(map_finish_reason): add unit tests and warning log for unknown finish reasons - Add TestMapFinishReason class in test_core_helpers.py covering: - All known OpenAI-native values pass through unchanged (parametrized) - Provider-specific mappings: Anthropic, Cohere, Vertex AI - Unknown/provider-specific values map to 'finish_reason_unspecified' - Regression test for ZhipuAI/GLM-5 'network_error' case - Add verbose_logger.warning() in map_finish_reason() when an unknown finish_reason is encountered, so operators can track which providers return non-standard values
Greptile SummaryThis is a large staging-to-main sync commit (88 files, ~11 755 net line changes) bundling several independent features and fixes: Responses API support for JSON-declared OpenAI-like providers, a new Gemini 2.0+ schema builder for tool parameters, PANW Prisma AIRS MCP guardrail expansion, a lookup-table refactor of Key issues found:
Confidence Score: 2/5
|
| Filename | Overview |
|---|---|
| litellm/llms/fireworks_ai/chat/transformation.py | URL construction bug: removing the /v1 stripping causes a doubled path segment (/v1/v1/) in the Fireworks model-listing URL |
| litellm/proxy/credential_endpoints/endpoints.py | Route consolidation bug: /credentials/by_model/{model_id} route declares credential_name: str = Path(...) but {credential_name} is not in that route template, causing a FastAPI startup error |
| litellm/caching/dual_cache.py | Async TTL regression: default_in_memory_ttl is no longer propagated to async_set_cache and async_set_cache_pipeline, while sync set_cache still applies it — inconsistent TTL behavior |
| litellm/llms/vertex_ai/gemini/transformation.py | Removed _LITELLM_INTERNAL_EXTRA_BODY_KEYS filter — cache and tags from extra_body can now be forwarded to Vertex AI API, causing unrecognized field errors |
| litellm/litellm_core_utils/core_helpers.py | Refactored map_finish_reason to use a lookup table; get_args is imported but never used; MALFORMED_FUNCTION_CALL and FINISH_REASON_UNSPECIFIED now map to stop instead of their previous custom values |
| litellm/llms/bedrock/chat/converse_transformation.py | Removed completion_tokens_details (reasoning token breakdown) from Bedrock usage response; removed output_config (snake_case) cleanup from inference params |
| litellm/utils.py | Provider lookup priority reversed: Python classes now take precedence over JSON providers; get_provider_responses_api_config extended to support JSON providers for Responses API |
| litellm/router.py | Removed early-exit logic for non-retryable errors in retry loop — errors like 400 context-window-exceeded will now exhaust all retries instead of failing fast |
| litellm/llms/openai_like/dynamic_config.py | Added create_responses_config_class for dynamically generating Responses API configs from JSON provider declarations; classes are cached per slug |
| litellm/responses/litellm_completion_transformation/transformation.py | Removed consecutive function_call merging logic for multi-turn tool calls; this was required by Anthropic to avoid "tool_use ids found without tool_result blocks" rejection |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[Incoming Request] --> B{Provider Lookup\nProviderConfigManager}
B -->|Python class exists| C[Python Config Class\nhas priority]
B -->|No Python class| D[JSON Provider Registry\nopenai_like]
C --> E{Endpoint Type}
D --> E
E -->|Chat Completion| F[chat/transformation.py]
E -->|Responses API| G[responses/transformation.py]
F --> H{Provider-specific transform}
H -->|Vertex AI| I[_map_function\nGemini 2.0+ → _build_vertex_schema_for_gemini_2\nGemini 1.5 → _build_vertex_schema]
H -->|OpenAI GPT-5| J[gpt_5_transformation\nnormalize reasoning_effort]
H -->|Fireworks AI| K[FireworksAIConfig\nBUG: double /v1 in get_models URL]
G -->|Perplexity| L[PerplexityResponsesConfig]
G -->|JSON providers| M[OpenAILikeResponsesConfig\ncreate_responses_config_class]
N[Response] --> O{finish_reason mapping}
O --> P[_FINISH_REASON_MAP lookup table\ncore_helpers.py]
P -->|unmapped| Q[default: stop + warning log]
R[DualCache.async_set_cache] --> S{TTL source}
S -->|explicit kwarg| T[Use provided TTL]
S -->|no kwarg| U[BUG: default_in_memory_ttl\nno longer applied async]
style K fill:#ff9999
style U fill:#ff9999
Comments Outside Diff (7)
-
litellm/llms/fireworks_ai/chat/transformation.py, line 429-431 (link)Double
/v1segment in Fireworks model-listing URLThe default
api_basereturned by_get_openai_compatible_provider_infois"https://api.fireworks.ai/inference/v1". The old code explicitly stripped the trailing/v1before appending/v1/accounts/.... Now that stripping is gone, the constructed URL becomes:https://api.fireworks.ai/inference/v1/v1/accounts/{account_id}/modelsinstead of the correct:
https://api.fireworks.ai/inference/v1/accounts/{account_id}/modelsThis will result in a 404 / error for all calls to
get_models()when using the default API base. -
litellm/caching/dual_cache.py, line 343-356 (link)default_in_memory_ttlno longer applied in async cache writesThe synchronous
set_cache(line ~99) still appliesdefault_in_memory_ttlwhen no explicitttlis provided:if "ttl" not in kwargs and self.default_in_memory_ttl is not None: kwargs["ttl"] = self.default_in_memory_ttl
But this PR removed the same guard from both
async_set_cacheandasync_set_cache_pipeline. All async cache writes (the hot path for LiteLLM's async operations) will now ignoredefault_in_memory_ttl, causing items to be stored without TTL in the in-memory cache. This means stale entries will never expire, leading to unbounded memory growth and stale data being returned for users who configureddefault_in_memory_ttl. -
litellm/llms/vertex_ai/gemini/transformation.py, line 586-595 (link)LiteLLM-internal
extra_bodykeys now forwarded to Vertex AIThe removed
_LITELLM_INTERNAL_EXTRA_BODY_KEYS = frozenset({"cache", "tags"})filter was intentionally preventing thecacheandtagskeys (consumed internally by LiteLLM) from being forwarded to the Vertex AI API. Without the filter, any caller who passesextra_body={"cache": {...}}orextra_body={"tags": [...]}will have those keys merged into the outgoing Vertex AI request body. The Gemini API does not recognise these fields and will return a 400 validation error.These keys should either be filtered out here, or stripped upstream before
_pop_and_merge_extra_bodyis invoked. -
litellm/litellm_core_utils/core_helpers.py, line 3 (link)Unused import:
get_argsget_argsis imported fromtypingbut is never referenced anywhere in this file after the refactor. -
litellm/router.py, line 5505-5519 (link)Non-retryable errors now exhaust all retries before raising
The removed block short-circuited the retry loop for errors that
should_retry_this_erroridentifies as non-retryable (e.g. HTTP 400 context-window-exceeded, 401 auth errors). Without it, these errors will now go through allnum_retriesiterations — burning time and model credits — before finally surfacing to the caller.The associated test file
tests/test_litellm/test_router_retry_non_retryable_errors.pywas deleted in this same PR (251 lines), removing coverage for this behaviour. Please confirm this removal is intentional and that the equivalent protection exists elsewhere in the retry path. -
litellm/responses/litellm_completion_transformation/transformation.py, line 380-415 (link)Removal of consecutive
function_callmerging breaks Anthropic multi-tool callsThe deleted block merged back-to-back assistant messages that each contained a
tool_useblock into a single assistant message before forwarding to the chat-completion layer. This transformation was explicitly documented as required for Anthropic, which rejects requests where tool_use IDs appear in separate assistant messages:"tool_use ids were found without tool_result blocks immediately after"
Without this merge, multi-turn Responses-API calls that produce multiple sequential tool calls in the same assistant turn will generate separate assistant messages. When those messages are forwarded to Anthropic via
litellm.completion, the API will reject the request.Please add a test that exercises the multi-tool-call path against an Anthropic provider to confirm this regression is not present.
-
litellm/proxy/credential_endpoints/endpoints.py, line 145-157 (link)Path(...)parameter name mismatch causes FastAPI startup errorThe route template
/credentials/by_model/{model_id}contains the path variable{model_id}, but the function declarescredential_name: str = Path(...)— a required path parameter whose name iscredential_name. FastAPI validates at startup that everyPath(...)parameter name appears in the route template, and raises:FastAPIError: Path parameter 'credential_name' is not found in route '/credentials/by_model/{model_id}'For the
/credentials/by_name/{credential_name:path}route the binding is correct, but the second route registration will crash the proxy on boot.The fix is to either keep the two endpoints as separate functions (one for each route) or make
model_idan explicitPath(...)parameter for the/credentials/by_model/{model_id}route and use a query param or different mechanism to passcredential_name.
Last reviewed commit: f243e56
| if finish_reason not in _VALID_OPENAI_FINISH_REASONS: | ||
| verbose_logger.warning( | ||
| "litellm.map_finish_reason: unknown finish_reason %r from provider; " | ||
| "mapping to 'finish_reason_unspecified' to avoid ValidationError.", | ||
| finish_reason, | ||
| ) | ||
| return "finish_reason_unspecified" |
There was a problem hiding this comment.
Standalone if breaks the elif chain
The fallback check at line 110 is a standalone if, not an elif. While this is currently correct because every preceding branch unconditionally returns, it visually disconnects the guard from the if/elif chain above and becomes a latent bug the moment any future branch is added without a return (e.g. a branch that only does a side-effect and falls through).
Using elif here makes the intent explicit and prevents the guard from being accidentally "re-entered" from an earlier branch:
| if finish_reason not in _VALID_OPENAI_FINISH_REASONS: | |
| verbose_logger.warning( | |
| "litellm.map_finish_reason: unknown finish_reason %r from provider; " | |
| "mapping to 'finish_reason_unspecified' to avoid ValidationError.", | |
| finish_reason, | |
| ) | |
| return "finish_reason_unspecified" | |
| elif finish_reason not in _VALID_OPENAI_FINISH_REASONS: | |
| verbose_logger.warning( | |
| "litellm.map_finish_reason: unknown finish_reason %r from provider; " | |
| "mapping to 'finish_reason_unspecified' to avoid ValidationError.", | |
| finish_reason, | |
| ) | |
| return "finish_reason_unspecified" |
- Clean up aliases key from entries with empty aliases list - Strengthen test assertion for alias conflict warning
…il support (#22999) * feat(panw-prisma-airs): PANW Prisma AIRS guardrail with apply_guardrail support * fix(panw): honor masking and fallback behavior * fix(panw): clean up apply_guardrail MCP metadata handling * fix(panw): clean up apply_guardrail MCP metadata handling * fix(panw): harden apply_guardrail edge cases * fix(panw): apply MCP masked data on allow responses * fix(panw): scan latest developer message in anthropic mode * fix(panw): restore legacy user-only pre-call scanning * fix(panw): record apply_guardrail in applied guardrails header * fix(panw): scan developer role in legacy pre-call path * fix(panw): harden SSE parsing and narrow MCP name fallback * fix(panw): harden streaming attr lookup and document dual scans * fix(panw): fail closed on permanent 4xx and cover streaming observability
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
The _list_has_thinking guard only checked for type == "thinking" but Anthropic can also return redacted_thinking blocks (safety-filtered). These are also accumulated in thinking_blocks, so the same duplication bug would occur with redacted thinking content.
Add usage example with concrete model entry, explanation of load-time expansion, and cross-reference to model_alias_map to clarify the difference between the two features.
* fix(snowflake): transform tool_choice string to object format
Snowflake's Cortex API requires tool_choice to be an object, not a string.
For example, {"type": "auto"} instead of "auto".
Ref: https://docs.snowflake.com/en/developer-guide/snowflake-rest-api/reference/cortex-inference#post--api-v2-cortex-inference-complete-req-body-schema
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Keep unified _FINISH_REASON_MAP dict approach, discard upstream's inconsistent _VALID_OPENAI_FINISH_REASONS frozenset that mapped to values not in the OpenAIChatCompletionFinishReason Literal.
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.
Add usage example with concrete model entry, explanation of load-time expansion, and cross-reference to model_alias_map to clarify the difference between the two features.
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
feat: add model_cost aliases expansion support
…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).
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.
feat(openai_like): add Responses API support to JSON providers
Restore independent fixes from main that were collaterally removed when PR #23276 (staging_03_10 → main) carried a revert commit: - bedrock: restore output_config pop (PR #23240) - redact_messages: restore dict handling for ModelResponse (PR #23235) - model_checks: restore list() copies to avoid cache mutation (PR #23236) - openapi_to_mcp_generator: restore relative URL handling (PR #23238) - vertex_ai/gemini: restore _LITELLM_INTERNAL_EXTRA_BODY_KEYS check (PR #23131) - openai types: restore extra finish reasons (PR #22138) - completion_extras: restore usage transformation logic Accept main for: model_prices JSONs, credential_endpoints, team_endpoints, object_permission_utils, responses transformation.
…3151) Independent fix (base: main) collaterally removed by PR #23276. Restores: - _get_effort_level() for extracting effort from string or dict - is_model_gpt_5_4_plus_model() classmethod - effective_effort usage in xhigh/tool-drop/sampling/temperature guards - Azure: _get_effort_level import and usage for dict reasoning_effort - Azure: gpt-5.4+ tool+reasoning drop logic
…10_2026 Litellm oss staging 03 10 2026
Restore independent fixes from main that were collaterally removed when PR BerriAI#23276 (staging_03_10 → main) carried a revert commit: - bedrock: restore output_config pop (PR BerriAI#23240) - redact_messages: restore dict handling for ModelResponse (PR BerriAI#23235) - model_checks: restore list() copies to avoid cache mutation (PR BerriAI#23236) - openapi_to_mcp_generator: restore relative URL handling (PR BerriAI#23238) - vertex_ai/gemini: restore _LITELLM_INTERNAL_EXTRA_BODY_KEYS check (PR BerriAI#23131) - openai types: restore extra finish reasons (PR BerriAI#22138) - completion_extras: restore usage transformation logic Accept main for: model_prices JSONs, credential_endpoints, team_endpoints, object_permission_utils, responses transformation.
…rriAI#23151) Independent fix (base: main) collaterally removed by PR BerriAI#23276. Restores: - _get_effort_level() for extracting effort from string or dict - is_model_gpt_5_4_plus_model() classmethod - effective_effort usage in xhigh/tool-drop/sampling/temperature guards - Azure: _get_effort_level import and usage for dict reasoning_effort - Azure: gpt-5.4+ tool+reasoning drop logic
Relevant issues
Pre-Submission checklist
Please complete all items before asking a LiteLLM maintainer to review your PR
tests/test_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 reviewCI (LiteLLM team)
Branch creation CI run
Link:
CI run for the last commit
Link:
Merge / cherry-pick CI run
Links:
Type
🆕 New Feature
🐛 Bug Fix
🧹 Refactoring
📖 Documentation
🚄 Infrastructure
✅ Test
Changes