* fix: complexity_router fails on list-format message content (OpenAI multi-part messages)
When a client sends messages with list-format content
(e.g. [{"type": "text", "text": "..."}] as used by the OpenAI JS SDK
and other clients), the complexity_router's async_pre_routing_hook
skipped those messages because it only handled str content. This caused
user_message to be None, the hook returned None, and the router fell
through to selecting the complexity_router deployment itself
(model="auto_router/complexity_router") which litellm cannot dispatch,
resulting in LiteLLMUnknownProvider.
Fixes:
- Extract text from list-format content parts (type=text) before
classifying
- Return default_model instead of None when no user message can be
extracted, preventing the crash fallthrough
- Loosen PreRoutingHookResponse.messages type from Dict[str, str] to
Dict[str, Any] to accommodate list-format content values
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: update messages type annotation in async_pre_routing_hook to Dict[str, Any]
Consistent with PreRoutingHookResponse.messages type change and the
list-format content support added in the previous commit.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: normalize None content to empty string in complexity_router message parsing
msg.get("content", "") returns None when the key exists with value None
(e.g. assistant messages with tool calls). Use `or ""` to normalize
None to an empty string explicitly.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: strip whitespace from joined list content parts in complexity_router
Prevents leading/trailing spaces when some content parts have empty
text values (e.g. " ".join(["", "hello"]) → " hello").
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* (sap) ensure tool parameters have type='object' for SAP compatibility
Fix SAP GenAI Hub Orchestration Service rejecting tool calls with error:
"400 - LLM Module: tools.0.custom.input_schema.type: Input should be 'object'"
Root cause: When Claude Code uses tools (like web_search) with the SAP provider
through LiteLLM's Anthropic experimental pass-through adapter, Anthropic's
input_schema format doesn't always include the required type="object" field.
The adapter's translate_anthropic_tools_to_openai() function was directly
copying input_schema to OpenAI's parameters field without ensuring the
type="object" requirement that SAP's API strictly enforces.
Changes:
- Modified translate_anthropic_tools_to_openai() to check if input_schema
is missing the type field and add type="object" if absent
- Preserves existing type field if already present
- Added comprehensive test suite (6 tests) covering:
- Missing type field scenario (now adds type="object")
- Existing type preservation
- Empty input_schema handling
- Multiple tools transformation
- Additional schema properties preservation
- SAP-specific compatibility regression test
Testing:
- All new tests pass (6/6 in test_anthropic_tool_schema_fix.py)
- All existing Anthropic tool tests pass (57/57 tool-related tests)
- SAP tool parameter validation tests pass (9/9 in test_sap_tool_parameters.py)
* (sap) enable native response_format for anthropic models
* (sap) filter strict param from model_params for GPT models only
* (sap) revert Anthropic adapter type='object' fix
The SAP FunctionTool Pydantic validator in litellm/llms/sap/chat/models.py
already ensures type='object' is added to all tool parameters for SAP
API compatibility.
The Anthropic adapter change affected ALL consumers, not just SAP, which
was broader scope than intended for this PR.
- Revert input_schema modification in Anthropic adapter
- Remove Anthropic-specific test file (SAP tests still cover this case)
* (sap) gate markdown stripping to Anthropic models only
SAP GenAI Hub with Anthropic models sometimes returns JSON wrapped in
markdown code blocks. GPT/Gemini/Mistral models don't exhibit this
behavior, so stripping is now gated to avoid accidentally modifying
valid responses that may contain markdown in JSON string values.
The user-specified async client was being overwritten by
`litellm.module_level_aclient` in `streaming_handler.py` when using
async+streaming with Gemini.
This fix adds a `gemini_client` parameter to `make_call()` (matching
the existing pattern in `make_sync_call()`) so the user's custom client
is preserved and not overwritten.
Fixes#17148
- Log provider token counting failures instead of silently swallowing
- Fall back to local tokenizer when provider returns error response
- Map assistant tool_calls to Responses API function_call items
- Concatenate multiple system messages instead of overwriting
- Hide internal error details from proxy API responses
- Narrow exception catch in handler to network/JSON errors only
- Update test to match new fallback behavior
- Add OpenAITokenCounter using POST /v1/responses/input_tokens endpoint
- Add litellm.acount_tokens() public async API that auto-routes to provider APIs
- Add proxy endpoint POST /v1/responses/input_tokens for OpenAI-compatible counting
- Transform chat tools format to Responses API format for correct token counting
- Fall back to local tiktoken when provider API unavailable
Fixes#22302
- Remove token field from JWTKeyMappingResponse to prevent hashed key exposure
- Use _to_response() helper on all CRUD endpoints to control returned fields
- Return 409 for unique constraint violations, 400 for FK violations, 404 for not found
- Add response_model to endpoint decorators
- Add 8 new unit tests covering error handling and token redaction
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Kontext models (flux-kontext-pro, flux-kontext-max) support both
text-to-image and image editing. Add them to IMAGE_GENERATION_MODELS
and update supported_endpoints in model prices JSON.
- Create handler.py for image generation and image edit
- Move polling logic from transformation to handlers
- Handlers use _get_httpx_client() / get_async_httpx_client()
- Transformation files now only transform request/response data
- Follows Bedrock pattern for provider-specific handlers
Addresses feedback: transformation files should not make HTTP requests
Add native text-to-image generation for Black Forest Labs Flux models
(flux-pro-1.1, flux-pro-1.1-ultra, flux-dev, flux-pro).
- Polling-based async API with sync and async support
- OpenAI-compatible parameter mapping (size, n, quality)
- Reuses shared HTTP clients via _get_httpx_client()
- 39 unit tests added
Replace direct httpx.get() calls with _get_httpx_client() to reuse
cached HTTP client, following the pattern used by other providers
(RunwayML, Azure AI OCR, Sagemaker, etc.).
Add native integration for Black Forest Labs image editing models
(flux-kontext-pro, flux-kontext-max, flux-pro-1.0-fill, flux-pro-1.0-expand).
Changes:
- Add BlackForestLabsImageEditConfig for BFL API transformation
- Add BLACK_FOREST_LABS to LlmProviders enum
- Add use_multipart_form_data() to BaseImageEditConfig for JSON vs form-data
- Modify image_edit_handler to support JSON request bodies
- Add comprehensive unit tests
Closes#11401
Providers like Cerebras return delta.reasoning in streaming responses
for gpt-oss models, but LiteLLM's Delta class expects reasoning_content.
This causes reasoning content to be silently dropped during streaming.
Fixes#13300
Add global media_resolution support for Gemini 2.x models (2.0, 2.5) when
using OpenAI's detail parameter on images. Previously, the detail parameter
was only working for Gemini 3+ models (per-part) and was silently ignored
for older Gemini models.
- Add _get_highest_media_resolution() and _extract_max_media_resolution_from_messages()
to extract highest detail from all images/files in a request
- Update _transform_request_body() to add mediaResolution to generationConfig
for Gemini 2.x models only (not 1.x which doesn't support it, not 3+ which
uses per-part)
- Add mediaResolution field to GenerationConfig TypedDict
- Support detail extraction from both image_url and file content types
- Add comprehensive unit tests and update documentation
Add MistralAudioTranscriptionConfig for Mistral's /v1/audio/transcriptions
endpoint, enabling litellm.transcription() with mistral/voxtral-mini-latest
and other Voxtral models. Supports multipart form-data with OpenAI-compatible
params (language, temperature, response_format, timestamp_granularities)
plus Mistral-specific params like diarize.
* azure content enhancement...
* rafactored to increase confidence score
* improvements based on additional feedback
* removed unused import
* Force-split any word longer than max length allowed
* preserve whitespace in text splitting
* moving common initialization to base class
* consolidate enforcement into async_make_request as single point, remove redundant caller-side checks, extract shared init/HTTP logic into base, and fix stale log messages
* clean up
* clean up tests
PR #22785 used pytest.importorskip which causes exit code 5 (all
skipped) in CI. Instead, add tenacity to the CI workflow pip install
and restore direct imports.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add cache_read_input_token_cost_per_audio_token, supports_code_execution,
and supports_file_search to the JSON schema used by the model prices
validation test.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
tenacity is not in pyproject.toml dependencies, causing ImportError
during test collection. Use pytest.importorskip to gracefully skip
when tenacity is not available.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The _encrypt_response_id method now receives request_cache=None as a
keyword argument from async_post_call_success_hook. Updated the mock
assertion to expect this parameter.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Tests used call_policy throughout but the actual API model uses
input_policy and output_policy. Updated _make_tool_row helper,
list filter query param, and policy update request/response assertions.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The aresponses_websocket CallType was recently added but not included
in the test exclusion list. It uses WebSocket passthrough (not Azure SDK
client initialization), so it correctly doesn't call
initialize_azure_sdk_client.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The test_streaming_mcp_events_validation test was flaky because:
1. It didn't mock the nested aresponses() call inside the iterator's
_create_initial_response_iterator(), causing real API calls that fail
without credentials
2. The iterator silently swallowed exceptions and set phase="finished",
discarding pre-generated MCP discovery events
3. The _execute_tool_calls mock had wrong signature (missing tool_server_map)
Production fix: MCPEnhancedStreamingIterator no longer sets phase="finished"
on LLM call failure — it falls through to emit MCP discovery events first.
Test fix: Added mock for litellm.responses.main.aresponses returning a fake
async streaming iterator, fixed mock signatures, removed try/except that
masked failures.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix translate_thinking_to_reasoning in responses_adapters/transformation.py
to make summary opt-in (was hardcoded to "detailed")
- Update e2e test to mock litellm.responses (new OpenAI routing path)
- Add tests for Responses API adapter summary preservation
- Resolve merge conflict in test file
Remove hardcoded summary="detailed" injection — summary is opt-in per
OpenAI spec and increases costs. Users opt-in per-request via LiteLLM
extension: thinking={"type": "enabled", "budget_tokens": N, "summary": "concise"}.
Also preserve summary in translate_thinking_for_model() which previously
dropped it when converting thinking → reasoning_effort for non-Claude models.
Fixes#20998