Noma v1 resolved application_id from user_api_key_alias when no explicit
value was set (PR #16832). Noma v2 (PR #21400) was rewritten from scratch
and this fallback was not ported, causing all requests from shared LiteLLM
instances to appear as a single generic "litellm" application in the Noma
dashboard — breaking per-user traceability.
Fix: after checking dynamic_params and self.application_id, fall back to
user_api_key_alias from litellm_metadata or metadata. This matches the
pattern used by PromptSecurityGuardrail._resolve_key_alias_from_request_data()
and restores the v1 behavior where each API key gets its own application
entry in the Noma dashboard.
Fixes#25794
Co-authored-by: Brendan Smith-Elion <brendan.smith-elion@arcadia.io>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ollama): propagate done_reason='length' as finish_reason for max_tokens truncation
Ollama returns done_reason='length' when a response is cut off by num_predict
(the max_tokens limit). Previously, non-streaming responses hardcoded
finish_reason='stop', and streaming used chunk.get('done_reason', 'stop')
which also defaulted to 'stop' when done_reason was absent.
This meant callers (e.g. the Anthropic pass-through adapter, which maps
OpenAI 'length' -> Anthropic 'max_tokens') could never detect truncation,
making stop_reason always appear as 'end_turn' even for cut-off responses.
Fix: read done_reason from the response JSON in the non-streaming path and
use `chunk.get('done_reason') or 'stop'` in the streaming path, so Ollama's
actual done_reason passes through to the caller unchanged.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Update test_ollama_chat_transformation.py
* Update litellm/llms/ollama/chat/transformation.py
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
The Vertex AI count-tokens endpoint rejects model names that include
version suffixes (@default, @20251001, etc.) with:
"claude-sonnet-4-6@default is not supported for token counting"
The same model without the suffix ("claude-sonnet-4-6") works correctly.
Strip @suffix from both the model parameter and request_data["model"]
in handle_count_tokens_request before sending to the API.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Bedrock /v1/messages streams can report cache tokens only on message_start while message_delta carries only uncached input tokens. Merge cache fields onto the final delta usage and clamp negative text-token remainders in cost calc to keep usage/cost consistent.
Made-with: Cursor
- Release v1.82.3: note removal of injected default when reasoning_effort omitted
- Blog gemini_3: correct defaults and reasoning_effort mapping guidance
- Provider gemini.md: align tip and mapping table with implementation
Made-with: Cursor
MaskedHTTPStatusError constructs a new httpx.Response from the original
error. Two bugs surfaced under real HTTP error responses:
1. The new Response was created without request=, so response.request
raised RuntimeError("The .request property has not been set.") for
any downstream caller (e.g. exception_mapping_utils) that inspected it.
2. The decoded response bytes were passed together with the original
Content-Encoding header. On construction httpx tried to decompress
the already-decoded bytes and raised httpx.DecodingError
("Error -3 while decompressing data: incorrect header check").
Set response.request to the masked Request and strip Content-Encoding
(and the now-stale Content-Length) before rebuilding the Response.
URL/message masking is unchanged; the new request carries the already
masked URL.
Also update test_logging_key_masking_gemini: the security commit
25f93bed91 moved Gemini API keys from ?key=... URL params to the
x-goog-api-key header, so api_base no longer contains the key.
The projected-spend alert in _update_key_cache read from
existing_spend_obj.litellm_budget_table["soft_budget"], but the nested
dict is never populated for virtual keys (the combined_view SQL maps
budget fields to flat top-level attributes instead). This made the
check dead code — it silently short-circuited on every request, and
when unblocked, crashed update_cache with a Pydantic ValidationError
because _get_projected_spend_over_limit returns a date object but
CallInfo.projected_exceeded_date expects str.
Fixes: read from the flat existing_spend_obj.soft_budget field that IS
populated, and stringify projected_exceeded_date.
Also marks team soft budget email alerts as enterprise in docs.
Closes#20324
RestrictedPython (ZPL-2.1, a BSD-style permissive license) was added as
a dependency for the custom_code guardrail sandbox, but the license
checker didn't recognize it. Add to authorized packages list.
- vertex_ai_context_caching.py: add explicit Optional[str] annotation on
auth_header so later branches that assign vertex_auth_header (Optional[str])
type-check against the first branch's dict assignment (which already has
type: ignore[assignment]).
- path_utils.py: remove unused pathlib.Path import (F401).
- emulated_handler.py: extract _extract_tool_call_fields,
_resolve_queries_from_args, _execute_file_search_tool_calls, and
_build_follow_up_input helpers to drop aresponses_with_emulated_file_search
below ruff's PLR0915 statement limit. Behavior unchanged.
Add null byte rejection to safe_join and safe_filename. Normalize
backslash separators in safe_filename for cross-platform safety.
Include resolved path in ValueError for debugging. Move imports
to module level per project conventions.
Add safe_join() and safe_filename() in proxy/common_utils/path_utils.py
for constructing filesystem paths from user-controlled inputs. Apply to
guardrail category YAML endpoint and dotprompt file converter.
- factory.py: fix _sort_bedrock_assistant_content_blocks to treat
cachePoint blocks with the same sort key as toolUse so Python's
stable sort keeps each cachePoint paired with its preceding toolUse
block (PR #24368)
- responses/transformation.py: remove cyclic import of OpenAIGPT5Config
inside map_openai_params; add _is_gpt_5_model and
_supports_reasoning_effort_none static methods that replicate the
same logic without the import cycle. _is_gpt_5_model now also
excludes pass-through models from other providers (e.g.
perplexity/openai/gpt-5.2) that contain 'gpt-5' in their name but
should not be subject to OpenAI GPT-5 temperature restrictions
(PR #24371)
- streaming_iterator.py: adopted main's more defensive version of the
tool-arg queueing check (.get() instead of [], isinstance guard) —
same logic, same behavior, lower crash surface
- model_prices_and_context_window.json + backup: combined staging's
search_context_cost_per_query fields (PR #24372) with main's new
supports_service_tier field — both are independent additions to the
same Gemini model entries
- test_streaming_handler.py: kept Azure streaming regression test
(PR #24354) and added main's two new Gemini legacy vertex
finish_reason normalization tests
- test_gemini_batch_embeddings.py: kept staging's unsupported-params
filtering tests (PR #24370) and added main's index/order test
Resolved conflicts:
- streaming_handler.py: combined role check (PR #24354, Azure streaming)
with reasoning_items check (new in main) — both are independent OR
conditions in is_chunk_non_empty()
- CI/CD: accepted main's versions throughout
- Redis tests migrated to CircleCI (PR #25354): removed enable-redis
from GH Actions workflows
- E2E UI tests restructured (PR #25365): simplified CircleCI job
- Coverage via Codecov added to all GH Actions unit test workflows
- Deleted test-litellm-matrix.yml and test-proxy-e2e-azure-batches.yml
(removed in main)
* [Test] Add Azure async chat completion timeout test. WIP
* Capture TTFT for /v1/messages streaming responses
The pass-through streaming path for /v1/messages (Anthropic, Bedrock,
Vertex AI, Azure AI, Minimax) logged completion_start_time only after
the entire stream finished. async_success_handler then fell back to
end_time, making TTFT equal to total duration or null in the UI and
Prometheus.
Record the timestamp of the first chunk in async_sse_wrapper and
propagate it to model_call_details before the logging handler runs,
so gen_ai.response.time_to_first_token reflects the real first-chunk
latency.
Fixes#25598
* [Refactor] Implement timeout resolution logic in completion function
add fetch ``request_timeout`` from litellm_settings
* remove stale test case
* remove extra print statement
* default request timeout value in constants to 600s to match timeout defaults handled in the proxy
* fix request timeout if using default value from constants.py
* update code structure, test cases
* only override if the global timeout sets timeout to 6000s
* update code structure, move hard coded values to const and make the reslve function readable by moving fallback logic to a seperate function
* modify default timeout values, replacing hard coded ones with default values defined
---------
Co-authored-by: harish876 <harishgokul01@gmail.com>
Co-authored-by: Joaquin Hui Gomez <joaquinhuigomez@users.noreply.github.com>
Tighten validation of request body parameters in the proxy routing
layer. Use context variables for internal call state management
instead of passing flags through request kwargs. Clean up metadata
handling at the proxy boundary.