Commit graph

18968 commits

Author SHA1 Message Date
mateo-berri
2c2aa5df11 Merge remote-tracking branch 'origin/main' into litellm_invalid_tool_choice_400
# Conflicts:
#	tests/test_litellm/responses/litellm_completion_transformation/test_litellm_completion_responses.py
#	tests/test_litellm/test_main.py
2026-09-19 02:35:29 -07:00
mateo-berri
6e0356a7a0 test: fail a required shard when a cost map provider is unregistered 2026-09-19 02:34:27 -07:00
mateo-berri
24064e3b31 fix(guardrails): treat the stored Javelin api_version default as unset for Azure Content Safety
Guardrails created through POST /guardrails on older releases have api_version "v1" saved in the database, because the writer persists every default. Azure Content Safety never accepts that value, so those guardrails kept answering 404 after the default moved to None. The Azure base now resolves "v1" to 2024-09-01 the same way it resolves a missing value. Also restores the OpenAPI snapshot line that a Python 3.14 regeneration had dedented
2026-09-19 02:29:43 -07:00
mateo-berri
a98c48f933 fix(rag): keep only per-upload caller options when ingesting into a registered store 2026-09-19 02:22:34 -07:00
mateo-berri
e0ebcb79fc Merge remote-tracking branch 'origin/main' into litellm_budget_alert_wording 2026-09-19 02:12:42 -07:00
mateo-berri
12bc9ad7b4 Merge remote-tracking branch 'origin/main' into litellm_pr38499_batch_retrieve_model_group 2026-09-19 02:12:25 -07:00
mateo-berri
2e3667b270 fix(proxy): keep the raw client model out of spend logs for rejections outside the router 2026-09-19 02:01:28 -07:00
mateo-berri
dd79c1f77d fix(cost): keep the deployment's OCR page rate when the model has no published price
When a deployment priced one OCR batch family and the other still needed a
published rate, a failed cost-map lookup returned zero for the whole line and
discarded the deployment rate that was already resolved. Those pages were
billed as free. The lookup failure now only logs, and the families the
deployment prices are billed at the configured rate
2026-09-19 01:59:39 -07:00
mateo-berri
167e3244ab fix(batches): shape LiteLLM-executed batch errors like OpenAI errors 2026-09-19 01:58:53 -07:00
ryan
84e56a60d2 Merge remote-tracking branch 'origin/main' into litellm_org_alias_from_team 2026-09-19 08:48:37 +00:00
mateo-berri
fd45412c89 feat(batches): run hosted_vllm batches inside LiteLLM
vLLM serves no /v1/files or /v1/batches, so a hosted_vllm deployment can never
host a batch. Batch inputs for such a deployment now land in a LiteLLM-owned
storage backend, the batch is executed line by line through the deployment's
own chat, completion, embedding, or responses route, and the batch plus its
output and error files are served back from the database under the creating key
2026-09-19 01:47:43 -07:00
yucheng
0fc6e7fd08 fix(proxy): validate input before starting background responses polling
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-19 08:46:11 +00:00
mateo-berri
6f4d1c5911 fix(guardrails): stop the Javelin api_version default leaking into Azure Content Safety
LitellmParams mixes every provider config model into one class, so the
Javelin api_version default of "v1" reached the Azure Content Safety
guardrails whenever config.yaml omitted api_version and Azure answered 404.
The shared field now defaults to None, Javelin keeps filling in "v1" itself,
and the Azure guardrails fall back to the documented 2024-09-01 at request
time so a DB update that omits api_version stays on the default too.
2026-09-19 01:41:35 -07:00
mateo-berri
b60b513f6a fix(rag): resolve registry stores on /v1/rag/ingest and reject providers without ingestion
POST /v1/rag/ingest authorized the managed vector store the request
named but then handed the raw request options to the ingestion
pipeline, which defaults to OpenAI. A request naming only a registered
store id uploaded the document to OpenAI Files, got an OpenAI 400, and
answered HTTP 200 with status "failed"; naming azure_ai explicitly
escaped as a 500.

The store's provider and litellm_params now merge into the request the
way /v1/rag/query already does (store wins, None values dropped), the
merged provider is checked against the ingestion registry before any
upload so unsupported providers get a 400 naming the supported ones, and
persistence keeps reading the caller's original options so registry
credentials never reach the database. A registry store with no database
row is no longer written as a new row.
2026-09-19 01:37:50 -07:00
Mateo Wang
5fc510a6fd
Merge pull request #41938 from BerriAI/litellm_gemini_cache_control_messages
fix(anthropic): keep cache_control for Gemini targets on /v1/messages and normalize Anthropic ttl units
2026-09-19 01:20:48 -07:00
Devin AI
fdb0fb648e fix(e2e): bind MCP OAuth acceptance to the owned gateway and snapshot the stored token once per phase
Co-Authored-By: bot_apk <apk@cognition.ai>
2026-09-19 08:12:35 +00:00
mateo-berri
00214ac371 test(router): cover the team-scoped credential deployment lookups
The router coverage gate in code-quality flags every router.py function
no router test calls by name, and the two helpers get_credential_deployment
gained (the team public-name lookup and the team-aware wildcard lookup)
were only reached through it. Each now has a test of its own: the
public-name lookup resolves only for the owning team, and the wildcard
lookup prefers the team's own pattern over the shared one and never hands
another team's wildcard deployment to a caller outside that team.
2026-09-19 01:05:01 -07:00
yucheng
7f451939a8 fix(proxy): return 400 instead of 500 for /v1/responses without input
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-19 08:01:33 +00:00
mateo-berri
5ad1847835 fix(gemini): drop ttl values whose expiry Google cannot store (past the year 9999) 2026-09-19 01:00:27 -07:00
yucheng
78a29ae08f fix(llmguard): scan list valued completion prompts
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-19 08:00:24 +00:00
yucheng
4e5d8004a4 Merge remote-tracking branch 'origin/main' into litellm_llm_guard_proxy_call_types 2026-09-19 08:00:24 +00:00
mateo-berri
7133baa777 fix(vector_stores): run the full model grant check on caller-supplied model hints
The vector-store file routes accept a model hint through the ?model= query
param and the x-litellm-model header. That hint was authorized with a hand
rolled check that covered only the key allowlist and the team allowlist, so
a key restricted by its project's model grant, a team-member restriction, or a
key config still routed through the hinted deployment. Greptile flagged the
gap as a P1 on the replacement PR.

The hint now goes through the same authorize_model_for_key path the batches
and files routes use, which runs can_key_call_resolved_model with every rule
the proxy enforces elsewhere. Keys those extra rules deny now get a 403 on
these routes. The two remaining behavioral differences are edge cases the
old check tolerated: a key whose team_models is set without a team_id no
longer runs the team allowlist, and a key with a config set skips the key
allowlist, both matching the rest of the proxy.

The regression test caches a project whose grant excludes the hinted model
and asserts the request is refused before any deployment lookup. The two
patch() calls on litellm.proxy.proxy_server carry a test-quality-ok reason
because can_key_call_resolved_model reads prisma_client and
user_api_key_cache through a lazy module import with no injection seam.
2026-09-19 00:54:41 -07:00
mateo-berri
3684e5cbcb fix(gemini): drop ttl values outside the protobuf Duration range and the explanatory docstrings 2026-09-19 00:50:37 -07:00
mateo-berri
0feca8641f fix(batches): price model-encoded batch retrievals by their deployment
A batch retrieved by its model-encoded id takes the direct (non-router) path,
which resolved credentials without stamping the deployment's model_info, so a
completed batch on a deployment with its own per-page pricing was billed at
the published rate with an empty model_id on the spend row.

Extract the router's credential lookup into get_credential_deployment and
stamp the resolved deployment's model_info onto the retrieve call the way the
router does for routed calls.
2026-09-19 00:30:15 -07:00
Mateo Wang
5f1268c056
Merge pull request #41933 from BerriAI/litellm_deliver_multi_choice_stream_rewrites
fix(policy_engine): deliver guardrail text rewrites on multi-choice, unfinished, and envelope-less streams
2026-09-19 00:22:02 -07:00
yucheng
19cb6b855b test(llmguard): move call type alias tests to the mapped enterprise test file
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-19 07:15:51 +00:00
mateo-berri
2303379c20 fix(gemini): keep the 2048 cache minimum on Gemini 2.5 Pro only, per Google's live cachedContents API 2026-09-19 00:07:22 -07:00
mateo-berri
3289e22834 fix(anthropic): keep cache_control for Gemini targets on /v1/messages and normalize Anthropic ttl units 2026-09-19 00:01:42 -07:00
yucheng
31ec84862c Merge remote-tracking branch 'origin/main' into litellm_llm_guard_proxy_call_types 2026-09-19 06:58:56 +00:00
mateo-berri
f89ca64481 fix(batches): honor deployment OCR page pricing in batch cost and answer 400 for unsupported Mistral file purposes 2026-09-18 23:57:41 -07:00
yucheng-berri
8afbc51cb7
Merge pull request #41685 from BerriAI/litellm_prompt_injection_llm_api_check_dispatch
* fix(proxy): dispatch llm_api_check moderation through during_call_hook

ProxyLogging.during_call_hook only ran async_moderation_hook for CustomGuardrail callbacks, so a
CustomLogger such as the prompt injection detector with llm_api_check enabled never called the
configured moderation model. Dispatch any CustomLogger that overrides async_moderation_hook and hand
the proxy router to every registered prompt injection detector at startup so that call can route

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix(enterprise): resolve openai_moderations model at call time and default to omni-moderation-latest

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix(proxy): keep queued moderation running past a V1 pre_call guardrail

A V1 CustomGuardrail with moderation_check pre_call returned out of
during_call_hook before asyncio.gather, abandoning already-queued
CustomLogger moderation coroutines and skipping every later callback.
Skip only that guardrail instead.

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix(utils): skip null tool_calls when formatting prompts for moderation hooks

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

---------

Co-authored-by: yucheng <yucheng@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-18 23:53:14 -07:00
mateo-berri
4fe1549432 fix(policy_engine): keep the per-choice rebuilt response's choices a list so legacy hook rewrites survive the model_dump round-trip 2026-09-18 23:53:02 -07:00
mateo-berri
6019e451ce Merge branch 'main' into feat/gemini-cache-control-pass-through 2026-09-18 23:42:58 -07:00
mateo-berri
cb80e8773e fix(policy_engine): fail open when an unended Messages stream has no text delta to carry the rewrite 2026-09-18 23:24:49 -07:00
Mateo Wang
56116079c8
Merge pull request #41930 from BerriAI/litellm_mat602_upstream_500_error_type
fix(exceptions): keep internal_server_error as the public type of an upstream 500
2026-09-18 23:19:52 -07:00
Mateo Wang
0a792c0f6b
Merge pull request #40399 from adssoccer1/feat/websearch-multi-query-schema
feat(websearch): let the model emit objective + multi-query search shapes
2026-09-18 23:03:48 -07:00
Joshua Valluru
c91ca90477 fix(mcp): retain wire aliases in guardrail inspection payloads 2026-09-18 23:00:32 -07:00
mateo-berri
b3d9ba9e7b fix(policy_engine): deliver guardrail text rewrites on multi-choice, unfinished, and envelope-less streams
Post-call pipeline rewrites on buffered streams failed open on three shapes:
chat streams with n > 1 (the rebuilt response collapsed every choice into
index 0), streams that ended without a finish marker, and Responses streams
whose final event carried no response envelope.

The chat handler now rebuilds the ended stream one choice index at a time and
writes each choice's rewrite back to that choice's buffered deltas. The
Anthropic handler writes an unended stream's rewrite across its text deltas.
The Responses handler spreads an envelope-less rewrite over the buffered
output_text events, still failing open when a scanned event cannot be placed.

Tool-call rewrites on n > 1 chat streams keep failing open.
2026-09-18 22:59:52 -07:00
Yuneng Jiang
c9158fcc12
fix(proxy): keep a config-owned key's resolved value across a database reload
Applying a database row dropped the runtime layer for every key the row
carried, including keys the config file owns. Those runtime entries hold
the env-resolved config values, so after a reload a key written as
os.environ/<NAME> read back as that literal string. The store now keeps
the runtime entry for a key the config owns and clears only the rest.

Visible as store_model_in_db silently turning itself off: the reload read
the raw reference, coerced it to False, and overwrote the resolved global.
2026-09-18 22:58:08 -07:00
Mateo Wang
4eb13a0b2a
Merge pull request #41843 from BerriAI/litellm_lit8064_unpin_derived_pricing
fix(proxy): unpin cost-map pricing copied into model_info and report pricing overrides
2026-09-18 22:58:04 -07:00
Joshua Valluru
f5ab563499 fix(mcp): preserve session expiry signals and scope dependency CI 2026-09-18 22:52:10 -07:00
mateo-berri
5f6ffdc333 test: drop the docstring that restated the payload test's name 2026-09-18 22:48:42 -07:00
Yuneng Jiang
3a480a5d6c
Merge remote-tracking branch 'origin/main' into litellm_settings_store_immutable 2026-09-18 22:39:29 -07:00
Yuneng Jiang
a987efca2c
fix(proxy): refuse runtime writes to config-owned settings
A write into a settings store for a key the config file declares used to
land in the runtime layer and then lose to the config on every read, so
the caller saw success while nothing changed. It now raises
ConfigOwnedKeyError, and the allowed-IP routes turn that into a 400
naming the key instead of reporting success on a list they never changed.

Both allowed-IP routes now build a new list rather than mutating the one
the config layer holds, and the os.environ resolver rebuilds the config
it is given instead of writing back into it, so a reader can no longer
corrupt the raw values the store keeps for provenance.

The database reload leaves a config-owned key alone rather than writing a
normalized copy back over it, which would now raise and abort the rest of
the reconcile pass.
2026-09-18 22:39:27 -07:00
Mateo Wang
b7f07469bc
Merge pull request #41564 from BerriAI/litellm_responses_bridge_message_item_lit4622
fix(responses): announce message item before text events in the chat completions bridge
2026-09-18 22:36:28 -07:00
mateo-berri
5783a38e27 fix(proxy): enforce the unified batch model grant before the DB shortcut and skip it for registry-routed vector store models
retrieve_batch returned a terminal batch from the DB before checking that the key may use the model encoded in a unified batch id; the grant check now runs right after pre-call processing. The vector store file list helper authorized data["model"] through handle_model_based_routing even when the vector store registry set it server-side and even with no caller, which crashed on a None key; it now authorizes only a caller-supplied hint and resolves credentials directly.
2026-09-18 22:30:07 -07:00
mateo-berri
ddac683ec6 fix(exceptions): keep internal_server_error as the public type of an upstream 500
PR #40243 started carrying the upstream error body on InternalServerError so the Responses response.failed event can report the provider's code and message, and openai's APIError.__init__ took the body's type along with it. The proxy then answered an OpenAI-compatible upstream 500 with type server_error while a 502 and a 503 kept internal_server_error, and the integration contract in test_observed_routing.py went red. Pin the type the way RateLimitError pins throttling_error, keeping the body.
2026-09-18 22:28:58 -07:00
Joshua Valluru
aea13ee03b fix(mcp): preserve legacy behavior on SDK2 and streamline verification 2026-09-18 22:28:31 -07:00
mateo-berri
12f831e863 Merge origin/main into feat/websearch-multi-query-schema
Resolves handler.py against main's SearchOutcome refactor: the file is
main's version plus this PR's substantive hunks only (the RichWebSearchInput
import, the rich= wiring at the three _execute_search call sites, the
_rich_search_input and _provider_supports_rich_search helpers, and the
_execute_search forwarding), so the 88-column re-wrap noise the PR carried
is gone and the diff against main is the feature alone. RichWebSearchInput
sits beside main's new SearchSucceeded/SearchFailed types, and main's two
_execute_search test stubs accept the new rich argument.
2026-09-18 22:15:35 -07:00
yuneng-jiang
12ddb35aad
Merge pull request #41924 from BerriAI/litellm_role_permissions_normalization
fix(proxy): parse role_permissions where it is read
2026-09-18 22:15:20 -07:00