Commit graph

10953 commits

Author SHA1 Message Date
Yassin Kortam
6db7328afb
chore(e2e): prune reliability.perf.latency.under_slo coverage cell (#34024)
It is a non-binary latency SLO threshold rather than a deterministic pass/fail
behavior a single e2e test can assert, so it does not fit the coverage registry's
one-test-per-cell contract. The registry README already flagged the perf cells for
a support-check or prune, and throughput SLO under load is covered structurally by
the Locust load suite. Removing it keeps the denominator to behaviors an e2e test
can deterministically prove.
2026-07-20 15:28:23 -07:00
Mateo Wang
885a637c6e
Merge pull request #34037 from BerriAI/litellm_fix_model_info_unpack_flake
test(proxy): make model_info endpoint tests hermetic to kill an order/merge-skew flake
2026-07-20 18:28:13 -04:00
Yassin Kortam
f21704c672
test(e2e): cover user update persistence via /user/info (#33998)
Co-authored-by: mubashir1osmani <mubashir.osmani777@gmail.com>
2026-07-20 22:18:36 +00:00
Yassin Kortam
71131190ec
test(e2e): cover model registration persistence in /model/info (#33996)
Co-authored-by: mubashir1osmani <mubashir.osmani777@gmail.com>
2026-07-20 22:17:40 +00:00
Mateo Wang
3819ee5dc0
Merge pull request #33733 from BerriAI/litellm_lit_4162_bedrock_batch_tags
feat(bedrock): forward bedrock_tags to CreateModelInvocationJob for batch jobs
2026-07-20 18:12:21 -04:00
mateo-berri
c2bd8699be fix(proxy): require admin opt-in for request-body bedrock_tags
Caller-supplied bedrock_tags land as AWS resource tags under the proxy's
AWS identity, letting an authenticated caller forge ownership or
cost-allocation labels. Add bedrock_tags to _BANNED_REQUEST_BODY_PARAMS
so per-request tags need general_settings.allow_client_side_credentials
or configurable_clientside_auth_params on the deployment, matching the
aws_bedrock_project_id precedent. Deployment-level bedrock_tags in
litellm_params are unaffected.

Also stop an explicit empty bedrock_tags list in litellm_params from
falling through to optional_params
2026-07-20 15:00:41 -07:00
Yassin Kortam
eb27447a1d
test(e2e): cover team update persistence via /team/info (#33997)
Co-authored-by: mubashir1osmani <mubashir.osmani777@gmail.com>
2026-07-20 21:56:07 +00:00
mateo-berri
249e1f8ce7 Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_lit_4162_bedrock_batch_tags
# Conflicts:
#	tests/test_litellm/test_router.py
2026-07-20 14:45:02 -07:00
Yassin Kortam
68be053e96
test(e2e): cover created user appearing in /user/list (#34016) 2026-07-20 14:29:13 -07:00
mateo-berri
44604620a4 test(proxy): make model_info endpoint tests hermetic to kill an order/merge-skew flake
The model_info / get_model_info_with_id endpoint tests drove refactored
endpoints with bare, unspec'd MagicMock routers and models. Because the
mocks were unspec'd, any attribute or method the (refactored) endpoints
newly read auto-materialized a child MagicMock, and whether that child
was reached depended on process-global state (premium_user, and the real
get_available_models_for_user chain reading litellm globals) that sibling
tests in the same xdist worker mutate. When reached, the MagicMock either
unpacked to empty (a, b = mock.method() -> 'not enough values to unpack
(expected 2, got 0)') or leaked into RouterModelInfo(**model_info) and
failed Pydantic str validation. Pass in isolation, fail under xdist.

The original TestModelInfoEndpoint failure (#33807 CI) was the same class
surfaced by merge skew: #33721 added a get_configured_token_limits unpack
to create_model_info_response, and CI's merge commit ran that against the
un-updated bare-mock test before the #33742 band-aid landed.

Fix (test-only, no product change):
- TestModelInfoEndpoint: mock the real seam (get_available_models_for_user),
  configure the router methods the endpoint actually calls, return a real
  Deployment, and drop the dead proxy_server.get_key_models/get_team_models/
  get_complete_model_list patches the refactor had stranded.
- TestGetModelInfoWithIdBlocked: spec the model mock so unset enterprise
  columns read as None instead of child MagicMocks.
- test_ProxyConfig_get_model_info_with_id_missing_model_id_raises: pin
  premium_user so the asserted AttributeError no longer flips with the
  ambient license global.
2026-07-20 14:28:09 -07:00
Yassin Kortam
4c77a5433a
test(e2e): cover created team appearing in /team/list (#34015) 2026-07-20 14:27:49 -07:00
mateo-berri
f5dc1a3010 test(router): prove request-level bedrock_tags override deployment-level tags for acreate_batch 2026-07-20 14:26:35 -07:00
Yassin Kortam
53f5a8c380
test(e2e): cover key block persisting to /key/info (#34014)
Co-authored-by: mubashir1osmani <mubashir.osmani777@gmail.com>
2026-07-20 14:26:18 -07:00
Yassin Kortam
b9c59c37cc
test(e2e): cover model update persisting to /model/info (#34017)
Co-authored-by: mubashir1osmani <mubashir.osmani777@gmail.com>
2026-07-20 21:13:54 +00:00
devin-ai-integration[bot]
0b8817afbb
perf(bedrock): audio transcription via rust core (py->rust bridge) (#33990)
* feat(bedrock): add audio transcription via Converse with py->rust bridge

Co-Authored-By: Ishaan Jaffer <155045088+ishaan-berri@users.noreply.github.com>

* fix(ci): exclude rust transcription rollout flag from docs check

Co-Authored-By: Ishaan Jaffer <155045088+ishaan-berri@users.noreply.github.com>

* fix(bedrock): await rust/python fallback in async transcription dispatch

Co-Authored-By: Ishaan Jaffer <155045088+ishaan-berri@users.noreply.github.com>

* test(bedrock): cover audio transcription rust dispatch

Co-Authored-By: Ishaan Jaffer <155045088+ishaan-berri@users.noreply.github.com>

* refactor(bedrock): route audio transcription through rust

Co-Authored-By: Ishaan Jaffer <155045088+ishaan-berri@users.noreply.github.com>

* refactor(bedrock): move rust transcription dispatch out of main

Co-Authored-By: Ishaan Jaffer <155045088+ishaan-berri@users.noreply.github.com>

* test(bedrock): include rust transcription coverage shard

Co-Authored-By: Ishaan Jaffer <155045088+ishaan-berri@users.noreply.github.com>

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Ishaan Jaffer <155045088+ishaan-berri@users.noreply.github.com>
2026-07-20 14:09:41 -07:00
Yassin Kortam
214945a223
test(e2e): cover organization deletion removing it from /organization/info (#34009)
Co-authored-by: mubashir1osmani <mubashir.osmani777@gmail.com>
2026-07-20 19:36:59 +00:00
Yassin Kortam
72be5a9bc0
test(e2e): cover tag creation persisting for spend categorization (#34018) 2026-07-20 12:24:50 -07:00
Yassin Kortam
51df801159
test(e2e): cover key regeneration rotating to a working new key (#34000) 2026-07-20 12:23:22 -07:00
tin-berri
ce88999ed1
Merge pull request #33182 from BerriAI/litellm_lit3637_session_token
feat(mcp): identity-only session tokens for the gateway DCR front door
2026-07-20 11:24:17 -07:00
Yuneng Jiang
b7e7fab741
Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_/ticket-implementation-review-409e1d 2026-07-20 10:04:46 -07:00
Yuneng Jiang
479e997eed
feat(spend): raise /spend/logs/v2 page_size cap to 1000
Clients exporting large spend-log ranges were forced into 100-row pages,
which meant a bounded COUNT plus an increasingly deep OFFSET scan per
request. Larger pages reduce both the request count and the cumulative
OFFSET cost for the same result set.

The handler already excludes the heavy JSON columns (messages, response,
proxy_server_request) from the paginated SELECT and bounds the COUNT via
SPEND_LOGS_PAGINATION_COUNT_CAP, so per-row cost does not grow with page
size. 1000 matches the ceiling already used by the user and user-agent
analytics list endpoints.
2026-07-20 09:55:38 -07:00
devin-ai-integration[bot]
3fcd19d7ad
fix(fireworks_ai): restore Content-Type application/json header (fixes 415) (#33929)
* fix(fireworks_ai): set Content-Type application/json in validate_environment

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

* refactor(fireworks_ai): delegate chat validate_environment to OpenAIGPTConfig

Instead of re-adding the JSON Content-Type default inside FireworksAIMixin,
FireworksAIConfig now delegates header construction to OpenAIGPTConfig and only
layers the Fireworks-specific x-session-affinity header on top, so the
Content-Type default can no longer drift away from the OpenAI base and reintroduce
the 415.

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

* test(fireworks_ai): cover missing api key error path in chat validate_environment

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

---------

Co-authored-by: Krrish Dholakia <krrishdholakia@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-07-20 09:52:35 -07:00
Yassin Kortam
8d96e959db
test(e2e): guard destructive spend-log truncate behind an explicit opt-in (#33751)
tests/e2e/conftest.py's pytest_sessionfinish truncated LiteLLM_SpendLogs
against whatever DATABASE_URL resolved to, gated only by "an e2e test body
ran". Pointed at a shared or staging DB, a routine local run wiped real
spend data. It also reached the truncate helper through a sys.path.insert
into quota_management/spend_tracking/spend_e2e_client.py, a cross-suite
import-by-path hack it then unwound in a finally.

The cleanup now routes through a new run_spend_log_cleanup in a top-level
tests/e2e/e2e_db.py, which fires the destructive truncate only when the
operator set E2E_RESET_SPEND_LOGS=1 and an e2e test actually ran. Any other
value (unset, 0, true, empty) leaves the DB untouched, so presence of the
variable alone or a test run alone never arms the truncate. The decision
plus the injectable truncate callable live in that pure helper, and conftest
is a thin adapter that supplies os.environ.get(...), the session stash, and
reset_spend_logs.

reset_spend_logs itself moved from spend_e2e_client.py into e2e_db.py
(implementation unchanged), sitting next to e2e_config and lifecycle so both
conftest and any suite import it by name; the sys.path munging is gone.
Nothing else imported reset_spend_logs, so spend_e2e_client.py drops the
definition, its __all__ entry, and the now-unused os import.
2026-07-20 08:47:39 -07:00
yucheng-berri
bd44c9e305
fix(langfuse): send v4 ingestion header for otel callback (#33907)
Some checks are pending
CodSpeed Benchmarks / benchmarks (push) Waiting to run
LiteLLM Rust / rustfmt, clippy, test (push) Waiting to run
GitHub Actions Security Analysis / zizmor (push) Waiting to run
* fix(langfuse): send v4 ingestion header for otel callback

* refactor(langfuse): inline otel ingestion header literals

* test(langfuse): assert v4 ingestion header on dynamic key config paths

* style: apply ruff format to langfuse otel header changes

* chore(langfuse): drop stale development annotation on json import

---------

Co-authored-by: Hassieb Pakzad <68423100+hassiebp@users.noreply.github.com>
2026-07-18 20:36:51 -07:00
tin-berri
ac1b4b35ce
Merge pull request #33174 from BerriAI/litellm_lit3637_aggregate_dcr
feat(mcp): always-on aggregate gateway DCR discovery front door
2026-07-18 19:25:26 -07:00
devin-ai-integration[bot]
cc45d18e9c
feat(complexity-router): add return_raw_model_name toggle for response model field (#33875)
* feat(complexity-router): optionally return raw model name

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

* test(proxy): restore asyncio import

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

* chore(tests): preserve staging asyncio import

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

* test(proxy): drop unused local asyncio import

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

* feat(dashboard): add complexity router raw model toggle

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

* refactor(complexity-router): move metadata key constant to constants.py

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

* style(proxy-tests): preserve module spacing

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

---------

Co-authored-by: Krrish Dholakia <krrishdholakia@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-07-18 19:24:56 -07:00
Tin Chi Lo
a22182f3c0 feat(mcp): add jti claim for per-mint session token uniqueness
Some checks failed
LiteLLM Rust / rustfmt, clippy, test (push) Has been cancelled
Terraform Provider / gofmt, vet, build, test (push) Has been cancelled
Terraform Provider / Provider endpoints vs proxy OpenAPI schema (push) Has been cancelled
2026-07-18 19:15:47 -07:00
Tin Chi Lo
7390f29b23 feat(mcp): identity-only session tokens for the gateway DCR front door 2026-07-18 19:15:47 -07:00
devin-ai-integration[bot]
f2e340cf2b
feat(rust): port BaseAWSLLM auth (credential resolution + SigV4) to litellm-core as a base provider (#33888)
* feat(rust): add feature-gated Bedrock AWS auth

Co-Authored-By: Ishaan Jaffer <155045088+ishaan-berri@users.noreply.github.com>

* refactor(rust): move Bedrock auth into core

Co-Authored-By: Ishaan Jaffer <155045088+ishaan-berri@users.noreply.github.com>

* fix(rust): fall through caller identity lookup errors

Co-Authored-By: Ishaan Jaffer <155045088+ishaan-berri@users.noreply.github.com>

* test(rust): add live Bedrock proof and CI coverage

Co-Authored-By: Ishaan Jaffer <155045088+ishaan-berri@users.noreply.github.com>

* refactor(rust): share in-memory cache with Bedrock auth

Co-Authored-By: Ishaan Jaffer <155045088+ishaan-berri@users.noreply.github.com>

* fix(rust): preserve web identity credential expiry

Co-Authored-By: Ishaan Jaffer <155045088+ishaan-berri@users.noreply.github.com>

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Ishaan Jaffer <155045088+ishaan-berri@users.noreply.github.com>
2026-07-18 19:12:00 -07:00
yuneng-jiang
f17a6ce009
Merge pull request #33903 from BerriAI/litellm_/quirky-heyrovsky-faafca
fix(proxy): treat malformed cost-map token limits as absent on /v1/models
2026-07-18 19:08:03 -07:00
Yuneng Jiang
ab02127b50
fix(proxy): treat malformed cost-map token limits as absent on /v1/models
create_model_info_response cast cost-map max_input_tokens / max_output_tokens
with unguarded int(). The surrounding try/except covers only the get_model_info
lookup, so a deployment whose model_info carries a non-numeric limit (e.g.
"128,000" or an empty string) raised inside the per-model listing loop and
failed the entire GET /v1/models and /models response with a 500, taking healthy
deployments down with it. A deployment's model_info is registered into
litellm.model_cost verbatim, so the malformed value reaches the cost map and not
just the router index.

Router.get_configured_token_limits already coerced this safely for the
deployment path; the cost-map path was missed, so the two together still
regressed. Both now share coerce_token_limit in litellm_core_utils, which
returns None for a malformed value so the listing omits that one limit instead
of failing, matching the graceful degradation the endpoint had before the
cost-map switch.
2026-07-18 18:56:24 -07:00
devin-ai-integration[bot]
7891388975
feat(rust): 1:1 port of OpenAI Responses API WebSockets to litellm-rust (#33849)
* feat(rust): add OpenAI Responses WebSocket gateway

Co-Authored-By: Ishaan Jaffer <155045088+ishaan-berri@users.noreply.github.com>

* test(rust): cover Responses WebSocket gateway behavior

Co-Authored-By: Ishaan Jaffer <155045088+ishaan-berri@users.noreply.github.com>

* fix(rust): align Responses WebSocket parity

Co-Authored-By: Ishaan Jaffer <155045088+ishaan-berri@users.noreply.github.com>

* feat(rust): expose Responses WebSockets through bridge

Co-Authored-By: Ishaan Jaffer <155045088+ishaan-berri@users.noreply.github.com>

* fix(rust): reject non-openai responses deployments early

Co-Authored-By: Ishaan Jaffer <155045088+ishaan-berri@users.noreply.github.com>

* fix(rust): align Responses WebSocket bridge semantics

Co-Authored-By: Ishaan Jaffer <155045088+ishaan-berri@users.noreply.github.com>

* refactor(rust): move Responses instrumentation into core

Co-Authored-By: Ishaan Jaffer <155045088+ishaan-berri@users.noreply.github.com>

* fix(rust): preserve Responses WebSocket callback dispatch

Co-Authored-By: Ishaan Jaffer <155045088+ishaan-berri@users.noreply.github.com>

* build(deps): authorize vcrpy and locust licenses in liccheck

Co-Authored-By: Ishaan Jaffer <155045088+ishaan-berri@users.noreply.github.com>

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Ishaan Jaffer <155045088+ishaan-berri@users.noreply.github.com>
2026-07-19 01:55:35 +00:00
Tin Chi Lo
8a57067d4a refactor(mcp): scope the root-path helper to the aggregate front door only
The SERVER_ROOT_PATH fix for the per-server pass-through challenge belongs with its sibling
in exceptions.py (both fabricate a per-server resource_metadata URL and both omit the root
segment), and both are pre-existing paths unrelated to the aggregate discovery this PR adds.
Reverting the server.py change keeps this PR to the aggregate front door and avoids leaving
the two per-server challenge builders inconsistent; the per-server root-path fix lands as its
own change covering both sites.
2026-07-18 18:49:52 -07:00
Tin Chi Lo
70bc9523ba test(mcp): isolate MCP discovery tests from a leaked SERVER_ROOT_PATH
tests/test_litellm/proxy/test_custom_proxy.py sets SERVER_ROOT_PATH at import time (its app
mounts under a custom path) and never restores it, so in a shared shard the value leaks into the
process. The discovery routes and the 401 challenges now read SERVER_ROOT_PATH to path-insert it
where they previously ignored it, so a leaked value rewrites every resource_metadata URL and the
exact-URL assertions in the delegate, pass-through, and aggregate challenge tests fail depending
on shard order

An autouse fixture clears SERVER_ROOT_PATH for the MCP discovery tests so they deterministically
exercise the default root-mounted deployment; the tests that assert a sub-path deployment set the
value explicitly within their own body. No assertion changed; the leak was invisible before only
because the code ignored the variable
2026-07-18 18:45:18 -07:00
Tin Chi Lo
5e1050709d fix(mcp): reserve mcp for the aggregate AS and root-path the discovery challenges
Two RFC 9728 / 8414 discovery fixes on the aggregate front door, both raised by Bugbot on this PR

The aggregate authorization-server document at /.well-known/oauth-authorization-server/mcp used to
defer to a per-server row literally named "mcp", serving issuer {base} while the aggregate
protected-resource document advertises {base}/mcp as its authorization server. A spec client
following that chain fails the RFC 8414 issuer check and cannot sign in. The single segment /mcp is
now reserved for the aggregate so the issuer stays {base}/mcp and matches the protected-resource
document; a server named "mcp" keeps its standard two-segment discovery at
/.well-known/oauth-authorization-server/mcp/mcp

The 401 challenges built the resource_metadata URL as {base}/.well-known/oauth-protected-resource/mcp
with no SERVER_ROOT_PATH segment, but the routes are registered with the path-inserted root segment,
so a proxy mounted under a sub-path pointed DCR clients at a URL that 404s. Both the aggregate
challenge and the pre-existing per-server pass-through challenge now derive the path from one
well_known_root_suffix helper that the route registrations also use, so the advertised URL cannot
drift from the served route
2026-07-18 18:45:18 -07:00
Tin Chi Lo
14b1647cd6 refactor(mcp): make the aggregate DCR front door always-on, remove the mcp_gateway_dcr flag
The flag guarded no breaking change: the aggregate discovery lives at new /mcp-suffixed
routes, the challenge only fires at aggregate scope, and the authorize/token/register/admission
arms self-gate on the llm_dcrc_/llm_session_ prefixes. Bare-origin and per-server discovery are
left exactly as they were, and a server literally named mcp keeps its own discovery via
disambiguation, so turning it on for everyone changes nothing about existing flows.
2026-07-18 18:45:18 -07:00
Tin Chi Lo
6d7a80ac75 feat(mcp): aggregate gateway DCR discovery front door behind mcp_gateway_dcr 2026-07-18 18:45:18 -07:00
devin-ai-integration[bot]
b83c60b9b7
test(e2e): cover credential-backed /v1/messages request (#33863)
* test(e2e): cover credential-backed /v1/messages request

Co-Authored-By: Ishaan Jaffer <155045088+ishaan-berri@users.noreply.github.com>

* test(e2e): use runtime Anthropic credential

Co-Authored-By: Ishaan Jaffer <155045088+ishaan-berri@users.noreply.github.com>

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Ishaan Jaffer <155045088+ishaan-berri@users.noreply.github.com>
2026-07-18 18:30:55 -07:00
devin-ai-integration[bot]
a198e0b0ca
feat(messages): route native Anthropic /messages through Rust behind LITELLM_RUST env var (#33848)
* feat(messages): route native Anthropic /messages through Rust behind RUST env var

Co-Authored-By: Ishaan Jaffer <155045088+ishaan-berri@users.noreply.github.com>

* test(docs): exclude RUST rollout flag from env-key documentation check

Co-Authored-By: Ishaan Jaffer <155045088+ishaan-berri@users.noreply.github.com>

* refactor(messages): rename RUST rollout env var to LITELLM_RUST

Co-Authored-By: Ishaan Jaffer <155045088+ishaan-berri@users.noreply.github.com>

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Ishaan Jaffer <155045088+ishaan-berri@users.noreply.github.com>
2026-07-18 18:27:20 -07:00
ryan-crabbe-berri
595e72472a
test(e2e): assert the long budget window keeps blocking after the short window resets (#33832)
* test(e2e): assert the long budget window keeps blocking after the short window resets

The multi-window budget tests proved the tight window blocks and self-heals
but never asserted the other direction: a long (1d) window whose cap the
accumulated spend already crossed must keep refusing calls even inside a
fresh short window. Adds one test per file (key and team) that drives spend
to a block, waits for the short window's reset_at to strictly advance (the
reset job zeroes that window's counter in the same pass), then polls until
the refusal is attributed to the 1d window ("over 1d budget"), failing
immediately if any call succeeds or a non-budget error leaks. Harness gains
per-window reset_at readback: BudgetWindowState in models.py and
key_window_reset_at / team_window_reset_at on BudgetClient.

* refactor(e2e): hoist shared budget-suite helpers into budget_client

drive_to_block and as_datetime existed as five and four per-file copies in
the budgets suite; both move to budget_client with each file keeping a thin
delegating wrapper so call sites and per-file pacing stay unchanged. The
three /team/info readers in budget_client now share a private _team_info.
Also guard the long-window reset_at snapshots with explicit non-None asserts
so the midnight-roll diagnostic cannot misreport when the window is missing
from the info response (greptile P2s).

* docs(e2e): tighten the multi-window module docstrings

* refactor(e2e): type window reset_at as datetime and expose plain window readers

BudgetWindowState.reset_at becomes a pydantic-parsed datetime, so the
multi-window tests compare real datetimes instead of hand-parsing strings.
The duration-keyed accessors are replaced by two plain readers,
key_budget_windows and team_budget_windows, with the pure window_reset_at
lookup exported; the client no longer encodes one test's access pattern.

* test(e2e): name the tiny short-window cap and comment the wait loops

* test(e2e): surface the 429 budget-block assert in the multi-window tests

drive_to_block now returns the blocking response so a test body can assert
on its shape; the two long-window tests assert status 429 explicitly, which
also pins the multi-window enforcement path's HTTP mapping (the enforcement
suite only covers the single-budget path). Other callers ignore the return
and are unchanged.

* refactor(e2e): scope this PR to the multi-window test, drop the cross-suite hoist

The helper hoist rewrote four unrelated budget test files (reset, reset_advances,
team_member_reset, user_across_keys) to pull drive_to_block and as_datetime out
of budget_client, which is refactor churn beyond this PR's multi-window scope.
This restores those four to their pre-PR state and gives the two multi-window
tests their own inline drive-to-block loop again, so the PR touches only the
multi-window feature: its two tests plus the budget_client window readers and the
reset_at datetime typing they actually use. The suite-wide helper dedup can land
on its own PR

* docs(e2e): number the long-window key test steps inline

* docs(e2e): number the long-window team test steps inline
2026-07-18 18:00:15 -07:00
tin-berri
3f3295b33f
feat(spend): track prompt compression saved tokens in daily spend aggregates (#33810)
* feat(spend): track prompt compression saved tokens in daily spend aggregates

Native compression interception now records tokens_before/after/saved into the
request litellm_metadata so savings land in the SpendLog metadata JSON under a
typed compression_savings key. A single normalizer
(extract_compression_saved_tokens) sums that key with Headroom guardrail
tokens_saved; the two writers are disjoint and run at different stages, so
summing never double-counts. The spend-log redactor now preserves purely
numeric compression stats inside guardrail_response so Headroom savings
survive the store_prompts_in_spend_logs=false default. compression_saved_tokens
is threaded through BaseDailySpendTransaction, queue aggregation, the daily
upsert blocks, a new BigInt column on all six daily spend tables, and the
daily activity read path (SpendMetrics, DailySpendMetadata, raw-SQL rollups)

* fix(spend): normalize legacy guardrail shapes and float token stats in compression savings reader

* feat(spend): aggregate compression and prompt caching dollar savings in daily rollups

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

* test(spend): update daily spend aggregation fixtures for savings columns

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

* feat(ui): add Cost Optimization dashboard page

New left-nav Cost Optimization page under Observability that surfaces money saved by prompt compression and prompt caching. It reads the daily activity rollup (userDailyActivityCall / get_daily_activity) and never scans SpendLogs, so it stays fast at 1M+ rows.

Renders a Total saved card, per-driver Compression and Prompt caching cards, a savings-over-time area chart, and a savings-by-driver donut, all aggregated in memory from the per-day metrics.compression_savings_spend and metrics.prompt_caching_savings_spend fields.

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

---------

Co-authored-by: Krrish Dholakia <krrishdholakia@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-07-18 17:47:54 -07:00
yuneng-jiang
ef7007c3dd
fix(router): treat malformed configured token limits as absent on /v1/models (#33864)
A deployment whose model_info carried a non-numeric max_input_tokens or
max_output_tokens (for example "128,000" or an empty string) made the
bare int() in get_configured_token_limits raise inside the per-model
/v1/models loop, so one misconfigured deployment turned the entire
listing into a 500. Coerce each configured limit safely and treat
malformed values as absent, matching the graceful degradation the
listing had before the cost-map switch
2026-07-18 15:27:07 -07:00
Yassin Kortam
e238e89537
test(e2e): spendlog cost for streaming /v1/messages via responses bridge (#33753)
Add a live spend-tracking e2e that drives a streaming anthropic-format
/v1/messages request through litellm's anthropic-messages -> OpenAI Responses
adapter and asserts the consumed stream writes exactly one SpendLogs row with
nonzero cost and token counts, attributed to the calling key under
custom_llm_provider openai and the /v1/messages call_type.

The deployment is a Responses-only OpenAI model (gpt-5.3-codex), so a served,
costed row proves the Responses path was taken; the chat-completions bridge
would have failed at OpenAI on an endpoint the model does not expose. Adds a
streaming /v1/messages method to the shared Gateway and the suite client, the
model to the inline compose config and driver-model registration, a coverage
registry row (quota_management.spend_tracking.messages_bridge.logs_cost), and
the matching variant vocab entry. The _summarize spend-row detail also gains
call_type and custom_llm_provider so a failed assertion prints the fields it
asserts on.

Resolves LIT-4546
2026-07-18 14:12:26 -07:00
devin-ai-integration[bot]
4f8d83ca85
test(e2e): cover /v1/responses OpenAI vision and Anthropic basic (#33838)
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Ishaan Jaffer <155045088+ishaan-berri@users.noreply.github.com>
2026-07-18 14:00:43 -07:00
devin-ai-integration[bot]
7a42f25550
test(e2e): cover /v1/responses openai cost_logged and tool_use (#33835)
* test(e2e): cover /v1/responses openai basic nonstream and stream

Co-Authored-By: Ishaan Jaffer <155045088+ishaan-berri@users.noreply.github.com>

* test(e2e): assert responses stream ends on final raw completed event

Co-Authored-By: Ishaan Jaffer <155045088+ishaan-berri@users.noreply.github.com>

* test(e2e): cover /v1/responses openai cost_logged and tool_use

Co-Authored-By: Ishaan Jaffer <155045088+ishaan-berri@users.noreply.github.com>

* test(e2e): centralize responses stream event models

Co-Authored-By: Ishaan Jaffer <155045088+ishaan-berri@users.noreply.github.com>

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Ishaan Jaffer <155045088+ishaan-berri@users.noreply.github.com>
2026-07-18 13:45:26 -07:00
devin-ai-integration[bot]
a1fb07f42c
test(e2e): cover /v1/responses openai basic nonstream and stream (#33830)
* test(e2e): cover /v1/responses openai basic nonstream and stream

Co-Authored-By: Ishaan Jaffer <155045088+ishaan-berri@users.noreply.github.com>

* test(e2e): assert responses stream ends on final raw completed event

Co-Authored-By: Ishaan Jaffer <155045088+ishaan-berri@users.noreply.github.com>

* test(e2e): centralize responses stream event models

Co-Authored-By: Ishaan Jaffer <155045088+ishaan-berri@users.noreply.github.com>

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Ishaan Jaffer <155045088+ishaan-berri@users.noreply.github.com>
2026-07-18 13:25:19 -07:00
devin-ai-integration[bot]
66dea7df8f
chore(e2e): remove tests/e2e/docker-compose.yml (#33837)
Co-authored-by: yassin <yassin@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-07-18 12:50:23 -07:00
mubashir1osmani
6a2e0a8528
fix(e2e): migrate load suite from e2e_gateway to ProxyClient (#33839)
* test(e2e): harden stage flakes for batches, UI, and MCP

Unique batch model names avoid load-balancing onto stale azure-batch
deployments that still pointed at the retired gpt-4.1-mini-batch, which
only the managed/unified path was hitting. Retry batch retrieve on 500
and /ui/api-keys navigation on ERR_ABORTED. Skip the MCP key-access suite
when the compose-only mcp-upstream is unreachable on stage k8s

* test(e2e): cover Datadog remote MCP via search_datadog_logs

Register the regional Datadog MCP endpoint with DD-API-KEY /
DD-APPLICATION-KEY static headers (CI-safe header auth; browser OAuth is
not headless-automatable). Seed a chat completion marked e2e-datadog-mcp-*,
assert the proxy shipped it, list tools, call search_datadog_logs for the
marker, and delete the server on teardown. Math-upstream key-access tests
only skip when that compose service is unreachable

* test(e2e): drop compose math MCP upstream; use Datadog only

Key-access denial and happy-path MCP e2e both register the real regional
Datadog remote MCP server with DD-API-KEY / DD-APPLICATION-KEY headers.
Remove the mcp-upstream compose service and FastMCP add/multiply fixture

* docs(e2e): require real Datadog MCP for all mcp suite tests

Document that tests/e2e/mcp must register via datadog_mcp helpers against
mcp.<site>/v1/mcp and must not introduce compose or fake MCP upstreams

* chore: restore mcp_e2e_upstream_server.py

Keep the FastMCP fixture file; e2e no longer wires it in compose, but the
module itself is not part of the Datadog-only cleanup

* fix(e2e): load tests/e2e/.env and fix datadog_reader importlib load

pytest on the host never inherited compose env_file keys, so DD_API_KEY
stayed empty. load_dotenv tests/e2e/.env in e2e_config. Register the
dynamically loaded datadog_reader module in sys.modules so dataclasses
do not crash under Python 3.12

* test(e2e/batches): harden azure/vertex unified lifecycle flakes

Put the provider deployment name in every JSONL body so Azure does not
depend on a perfect model rewrite. Retry create/retrieve/cancel on
transient statuses with backoff. Drop cancel assertions for azure and
vertex (registry only has a shared basic cell; create+retrieve prove
routing, cancel stays best-effort cleanup)

* test(e2e/ui): treat api-keys shell as success after SPA ERR_ABORTED

Post-login client redirects abort the first /ui/api-keys/ goto on stage.
Wait off /ui/login after cookie set, then accept the page once Create New
Key is visible even if goto raised ERR_ABORTED

* test(e2e): drop flaky key models dropdown Playwright suite

API management e2e already covers key generate/update persistence. The
UI Models-dropdown sentinel cases only added SPA ERR_ABORTED noise and
no unique product signal. Remove the suite and unused browser fixtures

* test(e2e/batches): fail clearly when OPENAI/AZURE provider is missing

Replace bare next() over PROVIDERS with _model_for that raises ValueError
naming the missing provider and the known list, instead of StopIteration

* fix(e2e): migrate load suite from e2e_gateway to ProxyClient

Stage collection failed with ModuleNotFoundError: e2e_gateway after the
Gateway rename. Wire load/conftest and LoadClient to the shared
ProxyClient fixture like every other suite

* fix(e2e): drop duplicate datadog_mcp_url and CLAUDE section after merge
2026-07-18 19:41:03 +00:00
mubashir1osmani
fdf380d0e3
test(e2e): harden stage flakes for batches, UI, and MCP (#33831)
* test(e2e): harden stage flakes for batches, UI, and MCP

Unique batch model names avoid load-balancing onto stale azure-batch
deployments that still pointed at the retired gpt-4.1-mini-batch, which
only the managed/unified path was hitting. Retry batch retrieve on 500
and /ui/api-keys navigation on ERR_ABORTED. Skip the MCP key-access suite
when the compose-only mcp-upstream is unreachable on stage k8s

* test(e2e): cover Datadog remote MCP via search_datadog_logs

Register the regional Datadog MCP endpoint with DD-API-KEY /
DD-APPLICATION-KEY static headers (CI-safe header auth; browser OAuth is
not headless-automatable). Seed a chat completion marked e2e-datadog-mcp-*,
assert the proxy shipped it, list tools, call search_datadog_logs for the
marker, and delete the server on teardown. Math-upstream key-access tests
only skip when that compose service is unreachable

* test(e2e): drop compose math MCP upstream; use Datadog only

Key-access denial and happy-path MCP e2e both register the real regional
Datadog remote MCP server with DD-API-KEY / DD-APPLICATION-KEY headers.
Remove the mcp-upstream compose service and FastMCP add/multiply fixture

* docs(e2e): require real Datadog MCP for all mcp suite tests

Document that tests/e2e/mcp must register via datadog_mcp helpers against
mcp.<site>/v1/mcp and must not introduce compose or fake MCP upstreams

* chore: restore mcp_e2e_upstream_server.py

Keep the FastMCP fixture file; e2e no longer wires it in compose, but the
module itself is not part of the Datadog-only cleanup

* fix(e2e): load tests/e2e/.env and fix datadog_reader importlib load

pytest on the host never inherited compose env_file keys, so DD_API_KEY
stayed empty. load_dotenv tests/e2e/.env in e2e_config. Register the
dynamically loaded datadog_reader module in sys.modules so dataclasses
do not crash under Python 3.12

* test(e2e/batches): harden azure/vertex unified lifecycle flakes

Put the provider deployment name in every JSONL body so Azure does not
depend on a perfect model rewrite. Retry create/retrieve/cancel on
transient statuses with backoff. Drop cancel assertions for azure and
vertex (registry only has a shared basic cell; create+retrieve prove
routing, cancel stays best-effort cleanup)

* test(e2e/ui): treat api-keys shell as success after SPA ERR_ABORTED

Post-login client redirects abort the first /ui/api-keys/ goto on stage.
Wait off /ui/login after cookie set, then accept the page once Create New
Key is visible even if goto raised ERR_ABORTED

* test(e2e): drop flaky key models dropdown Playwright suite

API management e2e already covers key generate/update persistence. The
UI Models-dropdown sentinel cases only added SPA ERR_ABORTED noise and
no unique product signal. Remove the suite and unused browser fixtures
2026-07-18 19:11:54 +00:00
Yassin Kortam
0439bcbfed
refactor(e2e): fold claude_code HTTP probes onto shared Gateway methods (#33760)
* refactor(e2e): fold claude_code HTTP probes onto shared Gateway methods

Migrate tests/e2e/claude_code/http_probe.py off its own httpx client onto the
shared transport, and promote count_tokens and native anthropic messages to
first-class Gateway methods (Gateway.count_tokens / Gateway.messages) with typed
request/response models in the shared models.py so other suites reuse them.

The probes now take an injected Gateway and issue their request through the
shared count_tokens/messages methods, reusing the split control/data-plane
routing, timeout, and typed Result handling the rest of tests/e2e uses. The wire
shape is preserved: the pydantic bodies serialize byte-for-byte to what the old
httpx probes sent, and the anthropic-version header is carried by a small
AnthropicHeaders model. httpx is gone from the module.

* test(e2e): drop unit-level probe harness test

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

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-07-18 19:03:01 +00:00