Commit graph

4663 commits

Author SHA1 Message Date
Praveen Ghuge
fee9c1023c fix(mavvrik): replace real-network e2e test with mock-based integration tests
BerriAI rule: tests/test_litellm/ must contain only mock-based tests.
The previous test_e2e_upload.py hit the live Mavvrik API.

Replaced with fully mock-based integration tests covering:
  - Client: register, advance_marker, get_signed_url, report_error
  - Uploader: upload bulk path, empty payload skip, idempotency
  - Full pipeline: register → upload → advance in sequence

No credentials required, always runs in CI. 164 tests passing.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-04-25 22:06:00 +05:30
Praveen Ghuge
7a24f4ec6c fix(mavvrik): address new P1 issues from Greptile review
P1 — exporter.py: lazy polars import (SDK users)
  polars is optional [proxy] dep. Importing at module level caused SDK users
  to get ModuleNotFoundError via the import chain:
  custom_logger_registry → mavvrik/__init__ → exporter → polars.
  Now imported inside each method that uses it. TYPE_CHECKING guard for
  type annotations only.

P1 — orchestrator.py + exporter.py: distinguish DB error vs zero-traffic day
  _stream_pages now raises RuntimeError when DB is None instead of silently
  returning. This propagates through _export → caught by _run_pipeline →
  report_error, marker NOT advanced. Zero-traffic days yield nothing without
  raising → advance IS called (correct).

P1 — uploader.py: accept 200 or 201 for GCS session initiation
  Standard GCS returns 200 OK for resumable upload initiation, not 201.

P1 — test_e2e_upload.py: moved to tests/local_testing/test_mavvrik_e2e.py
  BerriAI rule: tests/test_litellm/ must contain only mock-based tests.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-04-24 17:26:35 +05:30
Praveen Ghuge
b320f949fa fix(mavvrik): address P0/P1/P2 issues from Greptile + CI failures
P1 — orchestrator: gate _advance on total_bytes > 0
  When DB is unavailable, _export returns 0 bytes. Previously _advance was
  called unconditionally, permanently skipping those dates (marker advances
  past them). Now raises RuntimeError on 0 bytes so the single try/except
  catches it, calls report_error, and leaves the marker unchanged — the
  date is retried on the next scheduled run.

P1 — __init__.py: remove top-level `import polars as pl`
  polars is an optional [proxy] dependency. `custom_logger_registry.py`
  imports Logger from this package at module level, which triggered the
  polars import for all SDK users. polars is not used directly in __init__.py
  (Exporter uses it internally) so the import can be removed entirely.

P2 — uploader: add Content-Range header to _finalize_upload
  GCS resumable upload spec requires Content-Range: bytes 0-{last}/{total}
  on the final PUT. _finalize_upload was omitting it. Now consistent with
  _put_chunk(final=True) which already sends Content-Range correctly.

CI — documentation_test_env_keys: env vars already in config_settings.md
  (lines 607-611), failure was from an older commit.

CI — lint: factory.py Black format — pre-existing upstream issue, not our code.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-04-24 17:08:37 +05:30
Praveen Ghuge
376d9a59ee fix(mavvrik): use get_async_httpx_client instead of httpx.AsyncClient per request
BerriAI enforce_async_clients check requires using the shared cached client
from get_async_httpx_client() instead of creating httpx.AsyncClient() per
request. Creating per-request clients adds +500ms latency overhead.

_http.py now calls get_async_httpx_client(LoggingCallback).client to get the
shared httpx.AsyncClient from LiteLLM's in-memory client cache, then calls
.request() on it directly (AsyncHTTPHandler only has verb-specific wrappers,
not a generic request() method).

Test mocks updated to patch get_async_httpx_client instead of httpx.AsyncClient.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-04-24 17:00:50 +05:30
Praveen Ghuge
4e0b1907d4 feat(mavvrik): add Mavvrik integration for automatic LLM spend export (#12) 2026-04-24 16:43:05 +05:30
shubham-arora-clear
e5786c6c35
fix(bedrock): preserve cache_control TTL on tools for Claude 4.5+ (#25855)
Bedrock enforces non-increasing TTL ordering across cache_control blocks
(tools → system → messages). The tool cache_control TTL was being
unconditionally dropped to the default 5m, while system blocks preserved
the user-specified TTL for Claude 4.5+ models. This mismatch caused
"a ttl='1h' block must not come after a ttl='5m' block" errors when
users set ttl='1h' on both tools and system.

Converse path: add_cache_point_tool_block() now accepts a model param
and preserves TTL for Claude 4.5+, matching _get_cache_point_block().

Invoke path: _remove_ttl_from_cache_control() now also processes tools
(was only processing system and messages).

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-23 21:54:26 -07:00
ryan-crabbe-berri
c4c1861389
Merge pull request #26195 from BerriAI/litellm_team_member_total_spend
Track per-member total spend on team memberships
2026-04-22 18:20:16 -07:00
michelligabriele
c67d193400
fix(docker.non_root): use numeric UID 65534 for K8s runAsNonRoot (#26268) 2026-04-22 18:00:04 -07:00
yuneng-jiang
fb39683521
Merge pull request #25772 from BerriAI/litellm_wildcard_order_fallback
fix(router): wildcard order fallback to higher-order deployments
2026-04-22 15:05:14 -07:00
shin-berri
b6fdd46636
Merge pull request #26270 from BerriAI/litellm_/lucid-kowalevski-de832f
[Fix] Stabilize flaky spend accuracy tests + patch Redis buffer data-loss path
2026-04-22 15:02:24 -07:00
Yuneng Jiang
288d403529
[Fix] Preserve in-memory spend updates when Redis rpush fails
store_in_memory_spend_updates_in_redis drained the in-memory queues
into local variables before the rpush pipeline. If rpush raised (cloud
Redis hiccup, timeout, connection blip), those already-drained
transactions were garbage-collected with the scheduler job, silently
losing all spend aggregated during that tick.

Wrap the rpush in try/except. On failure, re-enqueue the aggregated
transactions into their respective in-memory queues so the next
scheduler tick retries.

Add a unit test that seeds real queues, simulates an rpush failure,
and asserts the transactions land back in-memory.
2026-04-22 14:10:40 -07:00
Milan
ec735074a2
fix(proxy): reapply Bedrock guardrail spend logging (#25854)
Restore guardrail spend/UI event_type wiring, request_data on streaming
OUTPUT paths, and centralized match redaction after the upstream revert.

Made-with: Cursor
2026-04-22 23:00:45 +03:00
yuneng-jiang
24aec61e4b
Merge pull request #26049 from BerriAI/litellm_adaptive_routing
Litellm adaptive routing
2026-04-22 08:52:51 -07:00
ishaan-berri
0e42d4cb08
April 21st Ishaan Branch (#26213)
* fix(otel): preserve Splunk Observability Cloud trace OTLP endpoint (#26183)

* fix(otel): preserve Splunk Observability Cloud trace OTLP URL

Splunk ingest uses /v2/trace/otlp; _normalize_otel_endpoint must not append /v1/traces.

- Return trace endpoints unchanged when they match Splunk OTLP path patterns
- Add unit tests for observability.splunkcloud.com, signalfx.com, and /trace/otlp suffix
- Set OTEL_EXPORTER_OTLP_PROTOCOL in protocol selection tests (from_env precedence over OTEL_EXPORTER)

Made-with: Cursor

* test(otel): use parameterized.expand for Splunk OTLP URL cases

Made-with: Cursor

* fix(otel): narrow Splunk trace URL guard to /v2/trace/otlp only

Made-with: Cursor

* test(otel): cover OTEL_EXPORTER fallback when OTLP protocol env unset

Made-with: Cursor

* Add Openrouter Opus 4.7 Entry (#26130)

---------

Co-authored-by: milan-berri <milan@berri.ai>
Co-authored-by: Matt Greathouse <matt5316@gmail.com>
2026-04-21 20:18:56 -07:00
ishaan-berri
e6897f5510
add moonshot/kimi-k2.6 to model registry (#26203)
* add moonshot/kimi-k2.6 to model registry

* add moonshot/kimi-k2.6 to backup model registry

* add tests for moonshot/kimi-k2.6 model registry

* fix moonshot/kimi-k2.6 pricing and add reasoning support

* fix moonshot/kimi-k2.6 pricing and add reasoning support in backup

* update kimi-k2.6 tests: fix pricing, add tool_choice and reasoning checks

* fix: load kimi-k2.6 registry tests from local backup instead of remote cost map
2026-04-21 19:58:43 -07:00
yuneng-jiang
eebb80fbef
Merge pull request #26208 from BerriAI/litellm_individual-team-member-budgets
Some checks are pending
Unit Tests: Proxy DB Operations / proxy-db (auth-checks, tests/proxy_unit_tests/test_auth_checks.py tests/proxy_unit_tests/test_user_api_key_auth.py, 20, 8) (push) Waiting to run
Unit Tests: Proxy DB Operations / proxy-db (key-generation, tests/proxy_unit_tests/test_key_generate_prisma.py, 30, 0) (push) Waiting to run
Unit Tests: Proxy DB Operations / proxy-db (proxy-utils, tests/proxy_unit_tests/test_proxy_utils.py, 20, 8) (push) Waiting to run
Unit Tests: Proxy DB Operations / proxy-db (remaining, tests/proxy_unit_tests --ignore=tests/proxy_unit_tests/test_key_generate_prisma.py --ignore=tests/proxy_unit_tests/test_auth_checks.py --ignore=tests/proxy_unit_tests/test_user_api_key_auth.py --ignore=tests/proxy_unit_tests/test_p… (push) Waiting to run
Unit Tests: Security / security (push) Waiting to run
Litellm individual team member budgets
2026-04-21 18:38:00 -07:00
Krrish Dholakia
e50f945ef7 refactor(adaptive_router): move update_queue out of litellm.proxy
Some checks failed
Unit Tests: Proxy DB Operations / proxy-db (auth-checks, tests/proxy_unit_tests/test_auth_checks.py tests/proxy_unit_tests/test_user_api_key_auth.py, 20, 8) (push) Has been cancelled
Unit Tests: Proxy DB Operations / proxy-db (key-generation, tests/proxy_unit_tests/test_key_generate_prisma.py, 30, 0) (push) Has been cancelled
Unit Tests: Proxy DB Operations / proxy-db (proxy-utils, tests/proxy_unit_tests/test_proxy_utils.py, 20, 8) (push) Has been cancelled
Unit Tests: Proxy DB Operations / proxy-db (remaining, tests/proxy_unit_tests --ignore=tests/proxy_unit_tests/test_key_generate_prisma.py --ignore=tests/proxy_unit_tests/test_auth_checks.py --ignore=tests/proxy_unit_tests/test_user_api_key_auth.py --ignore=tests/proxy_unit_tests/test_p… (push) Has been cancelled
Unit Tests: Security / security (push) Has been cancelled
P1 review: adaptive_router.py had a top-level import of
AdaptiveRouterUpdateQueue from litellm.proxy.db, which broke the
SDK/proxy boundary that every other router strategy respects. No
other router_strategy module imports from litellm.proxy at module
level.

The queue only depends on litellm._logging — it never needed to
live under litellm.proxy. Moved:

  litellm/proxy/db/db_transaction_queue/adaptive_router_update_queue.py
  → litellm/router_strategy/adaptive_router/update_queue.py

  tests/test_litellm/proxy/db/db_transaction_queue/
    test_adaptive_router_update_queue.py
  → tests/test_litellm/router_strategy/adaptive_router/test_update_queue.py

Also switched the queue's logger from verbose_proxy_logger to
verbose_router_logger to match the new module's ownership.

P2 review: drop unused constant STAGNATION_JACCARD_EXACT from
config.py — it was defined but never referenced.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 18:02:44 -07:00
shivam
5770af0068
Merge branch 'litellm_internal_staging' into litellm_individual-team-member-budgets 2026-04-21 17:59:29 -07:00
shivam
27a105bcf9
fix: give each team member an independent budget instead of sharing the team default
Previously, members added to a team without an explicit per-member budget were
all linked to the same `litellm_budgettable` row referenced by the team's
`metadata.team_member_budget_id`. Updating one member's budget via
`/team/member_update` mutated the shared row and silently changed every other
member's budget too.

Now both write paths produce a private, per-member budget:

- `add_new_member` clones the team's default budget into a fresh row when a
  member is added without `max_budget_in_team`/`allowed_models`. If no team
  default exists, the membership is created with no budget.
- `_upsert_budget_and_membership` detects when an existing membership still
  points at the team's default budget id and clones-on-write, relinking the
  membership to the new private budget before applying the update.
- `team_member_update` reads `team_member_budget_id` from team metadata and
  passes it through so the helper can make this distinction.

Adds unit tests for clone-on-write, in-place update of a private budget, and
the no-default-no-budget add path.

Made-with: Cursor
2026-04-21 17:58:50 -07:00
yuneng-jiang
d9494b6990
Merge pull request #26144 from BerriAI/litellm_post_call_non_streaming
fix(bedrock_guardrails): use Bedrock OUTPUT source for apply_guardrail when scanning model responses
2026-04-21 17:50:16 -07:00
Krrish Dholakia
f1da202d9e fix(adaptive_router): P1 flusher hot-reload + P2 hook accumulation + CI
P1: start the adaptive-router flusher loop unconditionally at proxy boot
instead of gating on 'adaptive_routers is non-empty'. Adaptive routers
added via /config/reload after boot now have their queues drained.
State is lazy-loaded per router on first flush tick (new _state_loaded
flag on AdaptiveRouter) so hot-reloaded routers still get their
persisted priors.

P2: _finalize_adaptive_router_if_configured now prunes stale
AdaptiveRouterPostCallHook callbacks from every litellm callback list
before registering new ones. Without this, every Router replacement
left the old hooks wired up in litellm.callbacks and double-fired
signal recording for every request. Uses
logging_callback_manager.remove_callbacks_by_type (same pattern as the
semantic tool filter).

CI fixes:
- black --check failure: reformatted litellm/router.py
- schema migration diff: aligned @@index with the explicit index name
  ('idx_adaptive_router_session_activity') from the original migration
  by adding 'map:' to all three schema.prisma copies. No new migration
  needed.

Tests: 1 new covering the prune-on-hot-reload path.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 17:49:38 -07:00
yuneng-jiang
5dc2926a1e
Merge pull request #26194 from BerriAI/litellm_fix_migration_thrashing
[Feature] Proxy: opt-in v2 migration resolver
2026-04-21 16:55:54 -07:00
Krrish Dholakia
ecd9a83e61 fix(adaptive_router): P2 review items — @updatedAt + snapshot samples
- Mark last_updated_at (AdaptiveRouterState) and last_activity_at
  (AdaptiveRouterSession) with @updatedAt so Prisma refreshes the
  timestamps on every write. Without this the fields stayed frozen at
  INSERT time and the last_activity_at index was misleading for any
  future TTL/eviction logic. Applied to all three schema.prisma copies;
  no migration SQL change needed (Prisma @updatedAt is a client-side
  annotation that doesn't touch DDL).

- get_state_snapshot: report cell.total_samples instead of alpha+beta
  for the 'samples' field. The previous value inflated every cell by
  the COLD_START_MASS prior (e.g. showed 10.0 before any real traffic
  arrived), which confused operators reading /adaptive_router/.../state.
  Updated docs + the snapshot test to match.

Also fixes two pre-existing merge-break syntax errors in router.py
(missing ')' on the AdaptiveRouter TYPE_CHECKING import; truncated
async_pre_routing_hook dispatch call for the adaptive router branch)
that were masking the rest of the file from the interpreter.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 16:27:01 -07:00
Krrish Dholakia
c7342bdc4f
Merge branch 'litellm_internal_staging' into litellm_adaptive_routing 2026-04-21 16:22:38 -07:00
Yuneng Jiang
8d52b1edd1
Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_yj_apr20 2026-04-21 16:21:10 -07:00
shivam
67567d10f5
Merge branch 'litellm_internal_staging' into litellm_wildcard_order_fallback
Some checks failed
Unit Tests: Proxy DB Operations / proxy-db (auth-checks, tests/proxy_unit_tests/test_auth_checks.py tests/proxy_unit_tests/test_user_api_key_auth.py, 20, 8) (push) Has been cancelled
Unit Tests: Proxy DB Operations / proxy-db (key-generation, tests/proxy_unit_tests/test_key_generate_prisma.py, 30, 0) (push) Has been cancelled
Unit Tests: Proxy DB Operations / proxy-db (proxy-utils, tests/proxy_unit_tests/test_proxy_utils.py, 20, 8) (push) Has been cancelled
Unit Tests: Proxy DB Operations / proxy-db (remaining, tests/proxy_unit_tests --ignore=tests/proxy_unit_tests/test_key_generate_prisma.py --ignore=tests/proxy_unit_tests/test_auth_checks.py --ignore=tests/proxy_unit_tests/test_user_api_key_auth.py --ignore=tests/proxy_unit_tests/test_p… (push) Has been cancelled
Unit Tests: Security / security (push) Has been cancelled
2026-04-21 15:49:44 -07:00
ishaan-berri
a302613eb5
feat(bedrock): add support for bedrock-mantle endpoint (Claude Mythos Preview) (#26196)
* add anthropic.claude-mythos-preview to model_prices_and_context_window.json

* add mantle route to bedrock common_utils: route detection, chat config, messages config dispatch

* add AmazonMantleConfig for bedrock/mantle /chat/completions endpoint

* add AmazonMantleMessagesConfig for bedrock/mantle /messages endpoint

* register AmazonMantleMessagesConfig in __init__.py and lazy imports registry

* add unit tests for bedrock mantle route and config dispatch

* add e2e tests for bedrock mantle: URL, body, SigV4 header, region routing
2026-04-21 15:41:58 -07:00
ishaan-berri
8a4a775b1b
fix(logging): add litellm_call_id to StandardLoggingPayload and OTel span (#26133)
* add litellm_call_id field to StandardLoggingPayload

* populate litellm_call_id in get_standard_logging_object_payload

* emit litellm.call_id span attribute in OTel integration

* test: litellm_call_id is present in StandardLoggingPayload

* test: litellm.call_id emitted as OTel span attribute

* test: allow litellm. prefix attributes in redacted span validator
2026-04-21 15:24:32 -07:00
Yuneng Jiang
88b1823f51
[Test] Fix setup_database call-signature assertions for v2 flag
Existing tests pinned exact kwargs on `PrismaManager.setup_database`,
but the opt-in v2 resolver added `use_v2_resolver=False` to every call.
Update the three assertions to reflect the new signature.

Fixes:
- TestHealthAppFactory::test_use_prisma_db_push_flag_behavior
- TestHealthAppFactory::test_startup_fails_when_db_setup_fails
2026-04-21 14:45:29 -07:00
Ryan Crabbe
e5f3e15969
Track per-member total spend on team memberships
Adds total_spend column to LiteLLM_TeamMembership that accumulates
continuously and is not zeroed by the budget cycle reset job. This
enables UI surfaces to distinguish current-cycle spend (the existing
spend column, which resets) from lifetime spend per team member.

Also exposes budget_reset_at on LiteLLM_BudgetTable so /team/info
callers can see when a member's budget window next resets. The field
was already stored in the DB but stripped by the response Pydantic
model.

Includes regression tests that:
- Guard the reset job against ever writing total_spend: 0
- Verify the spend writer increments both spend and total_spend in
  one UPDATE statement.
2026-04-21 13:56:44 -07:00
shivam
62c2c553d7
Merge remote-tracking branch 'upstream/litellm_internal_staging' into litellm_post_call_non_streaming 2026-04-21 11:45:26 -07:00
SwiftWinds
6da9ee9511 Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_fix-bedrock-invoke-allowlist 2026-04-21 11:38:12 -07:00
SwiftWinds
583bdd34a2 fix(bedrock): allowlist Bedrock Invoke body fields and filter all anthropic-beta values
Two fail-safes for the /v1/messages → Bedrock Invoke pass-through so new
Anthropic-only extensions Claude Code starts sending can't reach Bedrock
and trigger a 400 "Extra inputs are not permitted":

1. Top-level body fields are filtered to a typed allowlist. New
   `BedrockInvokeAnthropicMessagesRequest` TypedDict (in
   `litellm/types/llms/bedrock.py`) captures the Bedrock Invoke Anthropic
   Messages body schema; the runtime allowlist is derived from its
   `__annotations__` so the type and the filter can't drift. Anchored to
   the AWS reference page in docstrings + transform comment. An
   exact-set test pins the resolved allowlist so any future edit forces
   conscious review.

   Drops context_management, output_config, speed, mcp_servers,
   container, inference_geo, internal litellm_metadata, and any future
   Anthropic addition. output_format stays as an active inline-schema
   conversion (not just a strip).

2. The anthropic-beta header list is filtered + transformed against the
   bedrock mapping for ALL betas, not just auto-injected ones. The
   previous code union'd user-provided betas back in unfiltered, so a
   client on a new Anthropic-direct beta (e.g. advisor-tool-…,
   context-management-…) could still pin the request to fail. In a proxy
   context the client can't know the backend is Bedrock; the provider
   mapping is authoritative. User-provided drops are logged at WARNING
   so intentional overrides leave a breadcrumb.

Updates one existing test that happened to assert on the old buggy
pass-through (it used output-128k-2025-02-19, which is null in the
bedrock mapping and would 400 at runtime); rewrote it against a
bedrock-supported beta.

Scope: messages/invoke only. The same user-beta bypass exists in
chat/invoke but that's a different code path with different
user-expectation trade-offs — follow-up.
2026-04-21 11:26:15 -07:00
Sameer Kankute
378d80a9ad
Merge branch 'litellm_internal_staging' into litellm_wildcard_order_fallback 2026-04-21 09:19:12 +05:30
shivam
6beba97d20
test(bedrock_guardrails): assert apply_guardrail maps response to OUTPUT source
Add regression tests that mock make_bedrock_api_request and verify
input_type=request uses source=INPUT with user messages, and
input_type=response uses source=OUTPUT with synthetic ModelResponse.

Made-with: Cursor
2026-04-20 19:53:49 -07:00
Krrish Dholakia
bd3ee987b3 fix(adaptive_router): bound owner cache, drop PK from upsert update, redact PII
Some checks are pending
Unit Tests: Proxy DB Operations / proxy-db (auth-checks, tests/proxy_unit_tests/test_auth_checks.py tests/proxy_unit_tests/test_user_api_key_auth.py, 20, 8) (push) Waiting to run
Unit Tests: Proxy DB Operations / proxy-db (key-generation, tests/proxy_unit_tests/test_key_generate_prisma.py, 30, 0) (push) Waiting to run
Unit Tests: Proxy DB Operations / proxy-db (remaining, tests/proxy_unit_tests --ignore=tests/proxy_unit_tests/test_key_generate_prisma.py --ignore=tests/proxy_unit_tests/test_auth_checks.py --ignore=tests/proxy_unit_tests/test_user_api_key_auth.py, 30, 8) (push) Waiting to run
Unit Tests: Security / security (push) Waiting to run
- _owner_cache now opportunistically sweeps expired entries past
  _OWNER_CACHE_SWEEP_THRESHOLD live entries. Previously sessions that never
  came back piled up forever.
- flush_session_to_db strips session_id/router_name/model_name from the update
  payload. Prisma rejects writes to @@id fields.
- record_turn no longer persists last_user_content / last_assistant_content /
  tool_call_history / pending_tool_calls. Those are needed only in-memory for
  the next turn's signal detection; writing user prompts and tool payloads to
  the DB would store PII for every conversation.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 17:53:52 -07:00
Krrish Dholakia
bcc093d8c5 fix(adaptive_router): enforce satisfaction gate, stop false-flagging empty tool output
- SessionState now carries clean_credit_awarded + last_processed_turn (matching
  the DB schema). Satisfaction only fires once per session AND only after
  MIN_TURNS_FOR_CLEAN_CREDIT turns of context — early "thanks" no longer
  inflates alpha.
- _detect_failure no longer treats empty content as failure. Many tools
  legitimately return empty output (zero-result searches, silent bash);
  penalizing those corrupted the bandit posterior. Only is_error fires now.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 17:47:25 -07:00
Yuneng Jiang
9deefc0f76
fix: align MCP broker endpoint access controls with existing auth patterns 2026-04-20 16:52:59 -07:00
Krrish Dholakia
e7bc316db0
Litellm krrish staging 04 20 2026 (#26138)
* feat(router): add auto_router/quality_router for quality-tier routing (#25987)

* feat(router): add auto_router/quality_router for quality-tier routing

Adds a new auto-router type that routes a request to a model at a target
quality tier. The quality tier is inferred by re-using the existing
ComplexityRouter's classification, then mapped through an admin-configured
complexity_to_quality table. Each candidate model declares its own
quality_tier in model_info.litellm_routing_preferences.

Resolution strategy: exact tier match, else round up to the next higher
tier, else fall back to default_model.

Co-Authored-By: Claude Opus 4 (1M context) <noreply@anthropic.com>

* feat(quality_router): add capability-based filtering

Each deployment can declare a `capabilities: List[str]` field in
`model_info.litellm_routing_preferences` (e.g. ["vision",
"function_calling"]). Requests can pass `litellm_capabilities` in
`request_kwargs` to require specific capabilities — the router will only
route to deployments whose declared capabilities are a superset.

Resolution still walks tier (exact → round up), but at each tier filters
by capability before picking. Falls back to default_model only when it
also satisfies the required capabilities; otherwise raises rather than
silently routing to a model that lacks a required capability.

Co-Authored-By: Claude Opus 4 (1M context) <noreply@anthropic.com>

* feat(quality_router): expose routing decision in response headers

For transparency, expose the QualityRouter's routing decision in the
proxy response headers:

  x-litellm-quality-router-model       → picked model_name (e.g. "haiku-vision")
  x-litellm-quality-router-tier        → resolved quality tier (e.g. "1")
  x-litellm-quality-router-complexity  → ComplexityTier name (e.g. "SIMPLE")

Mechanism: the pre-routing hook stashes the decision in
request_kwargs["metadata"]["quality_router_decision"]. After the call
returns, Router.set_response_headers lifts the decision into
response._hidden_params["additional_headers"] alongside the existing
x-litellm-model-group / x-litellm-model-id headers. Existing metadata
keys (trace_id, user_id, etc.) are preserved.

Co-Authored-By: Claude Opus 4 (1M context) <noreply@anthropic.com>

* feat(quality_router): replace capabilities with keyword override

Drops the capability-based filtering in favor of a keyword-based override
for v0:

- RoutingPreferences.keywords: List[str] (replaces capabilities) — each
  deployment can declare substring keywords.
- If any declared keyword (case-insensitive) appears in the user message,
  the router short-circuits the complexity-classification flow and routes
  to the matching deployment.
- Tiebreaker for overlapping keyword matches: quality_tier DESC, then
  cheapest model_info.input_cost_per_token ASC. Unpriced models lose ties
  to priced ones.

Decision metadata + headers now expose the override:
  x-litellm-quality-router-via       → "keyword" | "quality_tier"
  x-litellm-quality-router-keyword   → matched keyword (only on keyword route)
  x-litellm-quality-router-complexity → complexity tier (only on tier route)

Removes:
- request_kwargs["litellm_capabilities"] reading
- _model_capabilities, _model_supports_capabilities,
  _first_capable_model_at_tier, capability filter in
  _resolve_model_for_quality_tier

Co-Authored-By: Claude Opus 4 (1M context) <noreply@anthropic.com>

* feat(quality_router): add explicit `order` to RoutingPreferences

Adds an explicit priority field to RoutingPreferences for resolving
collisions deterministically:

  RoutingPreferences.order: Optional[int]   # lower wins; unset = +inf

Used as the PRIMARY tiebreaker in two places:

1. Keyword overlap: when multiple deployments declare the same matching
   keyword, sort by (order ASC, quality_tier DESC, input_cost_per_token
   ASC, model_name ASC). Explicit always beats implicit.

2. Tier resolution: when multiple deployments share a quality tier,
   `_resolve_model_for_quality_tier` picks the one with the lowest
   order. The tier list is now sorted at index-build time.

This lets admins make routing decisions explicit when the natural
quality-and-price ordering would pick the wrong model.

Co-Authored-By: Claude Opus 4 (1M context) <noreply@anthropic.com>

* feat(quality_router): reorder tiebreak to (quality, order, price)

Changes the tiebreak ordering so quality_tier always wins first, then
explicit `order` is used to break ties within the same tier, then price
breaks the rest:

  1. quality_tier DESC      ← best model wins first
  2. order ASC              ← explicit priority within a tier
  3. input_cost_per_token ASC
  4. model_name ASC

Previously `order` was the primary key — that meant a tier-2 model with
`order=1` would beat a tier-3 model with no `order`, which is the wrong
default. Now `order` only resolves collisions among same-tier candidates.

Tier resolution (within a single tier) keeps the same key minus quality:
(order ASC, cost ASC, name).

Test renames + flips:
  - test_explicit_order_overrides_quality_tier → test_quality_wins_over_explicit_order
  - new: test_order_breaks_tie_within_same_quality_tier

Co-Authored-By: Claude Opus 4 (1M context) <noreply@anthropic.com>

* fix(quality_router): resolve Greptile review feedback

Addresses four P1 findings from PR review plus test coverage:

1. set_model_list missing quality_routers reset
   - Hot-reloading the Router would leave stale QualityRouter instances
     pointing at the old model_list. `set_model_list` now clears
     `self.quality_routers` alongside the other indices.

2. Round-down fallback before default_model
   - `_resolve_model_for_quality_tier` now rounds DOWN to the closest
     lower tier after round-up fails, before falling back to
     `default_model`. Degrades gracefully rather than jumping straight
     off-tier.

3. RoutingPreferences validation bypass
   - `_build_tier_index` now instantiates `RoutingPreferences(**prefs)`
     so invalid shapes (e.g. non-int quality_tier) raise a clear
     ValueError instead of silently succeeding.

4. Config-ordering dependency
   - `_tier_to_models` is now built lazily on first access. Previously,
     eager construction in `__init__` meant a QualityRouter deployment
     had to appear AFTER all its referenced models in config.yaml,
     because `Router._create_deployment` populates `model_list`
     incrementally. Any `available_models` defined after the router
     entry would silently be reported as missing.

Also adds 6 new tests covering each fix:
- test_invalid_quality_tier_type_raises_clear_error
- test_router_can_be_instantiated_before_its_targets_exist
- test_set_model_list_clears_quality_routers_registry
- test_rounds_down_when_no_higher_tier_exists
- test_rounds_down_prefers_closest_lower_tier
- test_prefers_round_up_over_round_down

Co-Authored-By: Claude Opus 4 (1M context) <noreply@anthropic.com>

* style: apply black 24.10.0 formatting to pre-existing offenders

Unblocks the LiteLLM Linting check for this PR — these 12 files are already
failing `black --check` on main (the lint workflow only runs on PRs, so main
drifts). No behavior changes; formatting-only.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Update litellm/router.py

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

---------

Co-authored-by: Claude Opus 4 (1M context) <noreply@anthropic.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* Support /v1/responses in complexity router (#26137)

* feat(proxy): add --reload flag for uvicorn hot reload (dev only)

Opt-in CLI flag, off by default, no env var. Only affects the uvicorn
run path; gunicorn/hypercorn paths and prod (which doesn't pass the
flag) are unaffected.

* Feature/add audio support for scaleway (#26110)

* feat(scaleway): add SCALEWAY to LlmProviders enum

* feat(scaleway): add audio transcription config and dispatch wiring

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* test(scaleway): add behavior tests for audio transcription config

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore(scaleway): advertise audio_transcriptions in endpoint-support JSON

* docs(scaleway): document audio transcription support

* fix(scaleway): address PR review — plain-text response_format + missing-key fail-fast

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* test(scaleway): cover new response paths, drop gettysburg.wav coupling

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* Prompt Compression - add it to the proxy (#25729)

* refactor: new agentic loop event hook

simplifies how to create logic for tool based multi llm calls

* fix: compress - make it work on anthropic input as well

* fix(compress.py): working prompt compression for claude code

ensures claude code messages can run through proxy easily

* docs: add agentic loop hook guide

* docs: add agentic_loop_hook to sidebar

* fix: fix multiple arguments error

* fix: fix tool call loop for compression on streaming /v1/messages

* fix: fix linting errors

* fix: fix ci/cd errors

* feat(litellm_pre_call_utils.py): use claude code session for litellm session id

allows claude code logs to be stitched together, making it easy to know they were all part of the same conversation

* fix: suppress incorrect mypy warning rE: module

* revert: drop PR's changes to litellm/proxy/_experimental/out/

Restores the 34 HTML files under _experimental/out/ to their pre-PR
paths (X/index.html -> X.html). All renames are R100 (content
unchanged); no other files are touched.

* fix: address greptile review comments on PR #25729

- Skip ``kwargs["tools"] = []`` injection when compression is a no-op —
  Anthropic Messages rejects empty tool arrays on requests that did not
  originally declare tools.
- Move agentic-loop safety guards (fingerprint cycle / max depth) out of
  the per-callback try/except so they propagate instead of being swallowed
  by the generic exception handler. Extracted _check_agentic_loop_safety.
- Gate generic ``x-<vendor>-session-id`` capture behind the
  LITELLM_CAPTURE_VENDOR_SESSION_HEADERS env var (off by default) to
  preserve backwards compatibility; explicit x-litellm-* headers are
  unaffected.
- Fix monkeypatch target in pre-call-hook test to patch the actual
  module-level binding
  (litellm.integrations.compression_interception.handler.compress).
- Add regression tests for empty-tools skip and opt-in session capture.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* revert: drop LITELLM_CAPTURE_VENDOR_SESSION_HEADERS flag

Generic x-<vendor>-session-id header capture is a new feature and only
runs *after* the explicit x-litellm-trace-id / x-litellm-session-id
checks, so it does not change behavior for any existing caller that was
already using the LiteLLM headers — no backwards-incompatibility to gate.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor(compress): replace input_type with CallTypes call_type

Drop the bespoke ``CompressionInputType`` literal and use the existing
``litellm.types.utils.CallTypes`` enum instead.  ``litellm.compress()``
now takes ``call_type: Union[CallTypes, str]`` (default
``CallTypes.completion``) — no new concept to learn, and the enum is
already the way the rest of the codebase talks about request shapes.

Supported values: ``completion`` / ``acompletion`` (OpenAI chat-completions
shape) and ``anthropic_messages`` (Anthropic structured content blocks).

Updated: compress(), the compression_interception handler, tests, docs,
and the two eval scripts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* Support /v1/responses in complexity router

Adds cross-format support to the complexity router via the guardrail
translation handler dispatch. Adds get_structured_messages to base
translation plus OpenAI chat, Responses, and Anthropic handlers.
Auto-router helper _extract_text_from_messages handles tool-call and
multimodal messages. Widens async_pre_routing_hook messages type to
Dict[str, Any].

Fixes https://github.com/BerriAI/litellm/issues/25134

* chore: apply black formatting

* fix: fallback to trying each handler when route inference fails

---------

Co-authored-by: Ryan Crabbe <ryan@berri.ai>
Co-authored-by: nhyy244 <106547304+nhyy244@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* test: cover _is_quality_router_deployment and init_quality_router_deployment

* fix: reset auto_routers on set_model_list to prevent hot-reload ValueError

* style: apply black formatting to websearch_interception and agentic_streaming_iterator

---------

Co-authored-by: yuneng-jiang <yuneng@berri.ai>
Co-authored-by: Claude Opus 4 (1M context) <noreply@anthropic.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: Ryan Crabbe <ryan@berri.ai>
Co-authored-by: nhyy244 <106547304+nhyy244@users.noreply.github.com>
2026-04-20 16:22:12 -07:00
Krrish Dholakia
e99955ac52 test(adaptive_router/hooks): align stale tests with current hook API
Six tests in test_hooks.py were written against an older API and had been
failing in CI. Updated:

- test_resolve_session_key_* (4 tests): _resolve_session_key now requires
  at least SIGNAL_GATE_MIN_MESSAGES messages before deriving a hash (it
  returns None on shorter convos to match the signal-processing gate).
  Switched the tests to use _long_messages() so they hit the hash path.

- test_post_call_success_hook_* (2 tests): the hook was migrated from
  async_post_call_success_hook (mutates response._hidden_params) to
  async_post_call_response_headers_hook (returns a headers dict) because
  the former fires too late for streaming responses. Rewrote the tests
  against the new API; added a metadata-not-dict noop case.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 16:03:32 -07:00
ishaan-berri
9aee0da7d8
fix: /health/readiness 503 loop when DB is unreachable (#26134)
* fix: /health/readiness returns 503 when DB is unreachable due to handle_db_exception re-raising

handle_db_exception() re-raises the Prisma exception inside _db_health_readiness_check's
except block, which propagates out to health_readiness() and gets wrapped in a 503.
The health endpoint never reached the reconnect path and the service never recovered.

Fix:
- Remove handle_db_exception() call from _db_health_readiness_check — that helper is
  for API request handlers (allow_requests_on_db_unavailable flag), not health checks
- Replace raw disconnect()+connect() with attempt_db_reconnect(), which uses the proper
  lock, cooldown, escalation, and heavy-reconnect (recreate_prisma_client) machinery

* test: update health readiness tests for handle_db_exception removal

- Remove tests that expected handle_db_exception to re-raise (old buggy behaviour)
- Remove tests asserting disconnect()/connect() calls (replaced by attempt_db_reconnect)
- Add regression tests covering the 503 loop fix:
  - transport errors never raise (ClientNotConnectedError, httpx.ConnectError, etc.)
  - reconnect success path returns 'connected'
  - reconnect failure path returns 'disconnected' without raising
  - non-transport errors return 'disconnected', skip reconnect

---------

Co-authored-by: yuneng-jiang <yuneng@berri.ai>
2026-04-20 15:29:43 -07:00
Krrish Dholakia
b6fc75b3ce
Merge branch 'litellm_internal_staging' into litellm_adaptive_routing 2026-04-20 15:28:08 -07:00
Krrish Dholakia
0cfcec68e9 fix(adaptive_router/hooks): populate tool_results so failure signal fires
The post-call hook was hardcoding tool_results=[] on every Turn, so the
failure detector never saw tool errors and the bandit only learned from
satisfaction — never from negative tool outcomes.

Added _recent_tool_results(messages): walks the request messages from the
tail and collects the contiguous run of role=='tool' entries — those are
the results from the most recent assistant tool_calls round. Normalizes
each to {content, is_error}, the only fields signals._detect_failure /
_detect_exhaustion read.

Tests: 6 new covering empty input, trailing-run extraction, is_error
propagation, boundary at first non-tool message, no-trailing-tool case,
and the end-to-end path from hook -> Turn.tool_results.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 15:25:51 -07:00
Krrish Dholakia
fba736ca3c fix(adaptive_router): 3 P1 review defects
- Use 'auto_router/adaptive_router' prefix in example yaml, docs, and
  README — the old 'adaptive_router/...' and 'openai/gpt-4o-mini' values
  silently skipped adaptive-router init because detection requires the
  'auto_router/adaptive_router' prefix.

- Read x-litellm-min-quality-tier from request headers (and the
  'min_quality_tier' metadata key as fallback) in async_pre_routing_hook.
  Previously the documented header was defined but never extracted, so
  the quality-floor feature was inert.

- Evict expired entries from _session_states. The cache grew without
  bound — added a parallel expiry map (same TTL as _owner_cache) and an
  opportunistic bulk sweep when the cache crosses a size threshold.

- Align adaptive-router migration SQL with Prisma schema: all count
  columns and the 'clean_credit_awarded' / 'last_processed_turn' fields
  are NOT NULL in the data model, so the migration now declares them
  NOT NULL. Fixes test_aaaasschema_migration_check.

Tests: 8 new covering header/metadata/precedence/invalid-value paths for
min_quality_tier and TTL-based eviction of _session_states.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 15:22:18 -07:00
Krrish Dholakia
386f334fee
Prompt Compression - add it to the proxy (#25729)
* refactor: new agentic loop event hook

simplifies how to create logic for tool based multi llm calls

* fix: compress - make it work on anthropic input as well

* fix(compress.py): working prompt compression for claude code

ensures claude code messages can run through proxy easily

* docs: add agentic loop hook guide

* docs: add agentic_loop_hook to sidebar

* fix: fix multiple arguments error

* fix: fix tool call loop for compression on streaming /v1/messages

* fix: fix linting errors

* fix: fix ci/cd errors

* feat(litellm_pre_call_utils.py): use claude code session for litellm session id

allows claude code logs to be stitched together, making it easy to know they were all part of the same conversation

* fix: suppress incorrect mypy warning rE: module

* revert: drop PR's changes to litellm/proxy/_experimental/out/

Restores the 34 HTML files under _experimental/out/ to their pre-PR
paths (X/index.html -> X.html). All renames are R100 (content
unchanged); no other files are touched.

* fix: address greptile review comments on PR #25729

- Skip ``kwargs["tools"] = []`` injection when compression is a no-op —
  Anthropic Messages rejects empty tool arrays on requests that did not
  originally declare tools.
- Move agentic-loop safety guards (fingerprint cycle / max depth) out of
  the per-callback try/except so they propagate instead of being swallowed
  by the generic exception handler. Extracted _check_agentic_loop_safety.
- Gate generic ``x-<vendor>-session-id`` capture behind the
  LITELLM_CAPTURE_VENDOR_SESSION_HEADERS env var (off by default) to
  preserve backwards compatibility; explicit x-litellm-* headers are
  unaffected.
- Fix monkeypatch target in pre-call-hook test to patch the actual
  module-level binding
  (litellm.integrations.compression_interception.handler.compress).
- Add regression tests for empty-tools skip and opt-in session capture.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* revert: drop LITELLM_CAPTURE_VENDOR_SESSION_HEADERS flag

Generic x-<vendor>-session-id header capture is a new feature and only
runs *after* the explicit x-litellm-trace-id / x-litellm-session-id
checks, so it does not change behavior for any existing caller that was
already using the LiteLLM headers — no backwards-incompatibility to gate.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor(compress): replace input_type with CallTypes call_type

Drop the bespoke ``CompressionInputType`` literal and use the existing
``litellm.types.utils.CallTypes`` enum instead.  ``litellm.compress()``
now takes ``call_type: Union[CallTypes, str]`` (default
``CallTypes.completion``) — no new concept to learn, and the enum is
already the way the rest of the codebase talks about request shapes.

Supported values: ``completion`` / ``acompletion`` (OpenAI chat-completions
shape) and ``anthropic_messages`` (Anthropic structured content blocks).

Updated: compress(), the compression_interception handler, tests, docs,
and the two eval scripts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-20 15:08:00 -07:00
nhyy244
a19bff4ca6
Feature/add audio support for scaleway (#26110)
* feat(scaleway): add SCALEWAY to LlmProviders enum

* feat(scaleway): add audio transcription config and dispatch wiring

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* test(scaleway): add behavior tests for audio transcription config

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore(scaleway): advertise audio_transcriptions in endpoint-support JSON

* docs(scaleway): document audio transcription support

* fix(scaleway): address PR review — plain-text response_format + missing-key fail-fast

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* test(scaleway): cover new response paths, drop gettysburg.wav coupling

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 14:49:41 -07:00
Krrish Dholakia
f0efc5f670 test: cover _finalize_adaptive_router_if_configured
Router coverage check flagged this method as untested. Adds two cases:
- initializes AdaptiveRouter from model_list and is idempotent on re-entry
- no-op when no adaptive deployments are configured

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 07:43:53 -07:00
Krrish Dholakia
dd4a1d2be2 feat: add adaptive routing to litellm
allow model routing to improve based on conversation signals

ensures router is picking best model for task
2026-04-18 16:35:17 -07:00
Ryan Crabbe
c8b7c1bafa
Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_feat-multi_threshold_budget_alerts 2026-04-18 15:26:02 -07:00
Ryan Crabbe
eb6fd98611
Merge remote-tracking branch 'origin/main' into litellm_feat-multi_threshold_budget_alerts 2026-04-18 14:54:26 -07:00