Skip to content

chore: sync upstream/main into litellm_internal_staging (2026-08-03, ~372 commits) - #36

Merged
blackflame007 merged 379 commits into
litellm_internal_stagingfrom
sync/upstream-main-2026-08-03
Aug 3, 2026
Merged

chore: sync upstream/main into litellm_internal_staging (2026-08-03, ~372 commits)#36
blackflame007 merged 379 commits into
litellm_internal_stagingfrom
sync/upstream-main-2026-08-03

Conversation

@blackflame007

@blackflame007 blackflame007 commented Aug 3, 2026

Copy link
Copy Markdown

Weekly-cadence upstream pull, following the v1.93.0 (#15) and 2026-07-28 (#24) precedents. We were 497 behind upstream staging / 372 behind upstream main with 78 fork commits ahead; this merges upstream/main (their release branch, same source as previous syncs).

Conflicts: 7, all resolved

file resolution
test_server_root_path.yml deleted upstream — deletion taken (supersedes our #28 Node unpin for a workflow that no longer exists)
test-model-map.yaml semantic union: our backup-map validation + duplicate-key check (#27) retained, upstream's new schema-sync check added
ui package.json + lock upstream taken — their postcss 8.5.22 supersedes our 8.5.18 security floor; merged lock resolves brace-expansion@5.0.8, consistent with our retained override
uv.lock upstream taken
ruff-strict-budget.json, type-discipline-budget.json both sides had ratcheted independently — regenerated against the merged tree with make lint-*-budget-update

Verified

  • All custom surfaces survived: kling, xai/Grok-Imagine (videos + image_generation), openrouter/videos, fal_ai image_generation, plugin_routes.py, duplicate-key checker, grok-imagine + elevenlabs pricing.
  • 496 fork-critical tests pass on the merged tree (tests/test_litellm/llms/{kling,xai,fal_ai,elevenlabs,openrouter}), 9.8s.
  • Proxy import smoke passes with proxy extras (upstream added an orjson dependency — declared correctly in pyproject, so image builds get it).

Also in this PR

openrouter/deepseek/deepseek-v4-flash-0731 (the fleet brain since NOL-376) added to both price maps byte-identical — neither upstream branch prices the dated slug, and prod runs LITELLM_LOCAL_MODEL_COST_MAP=True, so the packaged backup map is what actually serves. Until now spend for the fleet brain depended entirely on the litellm-config model_info pin; this makes it defense-in-depth. Dup-key check passes.

Not in this PR

Deploying the new proxy image. build.sh --env stg + stg litellm_image_tag bump + apply is the canary path per images/litellm/README.md; recommended as the follow-up, prod after stg soak.

Pre-existing canonical/backup price-map drift (recraft V4 family is backup-only, from #16) noted but not expanded here — runtime uses the backup, so those prices are live.

Base re-merge and CI follow-ups

litellm_internal_staging moved four commits ahead mid-review (MiniMax providers #31, blind-catch narrowing #33, Gemini Veo fixes #34/#35). Merged it in; the only conflict was test_openapi_compliance.py, where base (#31) and upstream (BerriAI#35161) had independently loosened the same Content-discriminator assertion after Google's spec dropped the keyword. Kept upstream's, a strict superset (accepts a discriminator mapping or a per-variant type const/1-item enum, requires the values be distinct, pins TextContent to text).

Also fixed three reds:

  • schema-sync was failing on toolchain, not drift: uv run --frozen builds the litellm-rust bridge via maturin, and the locked aws-sdk crates need rustc 1.94.1 vs the image's 1.92.0. Added the repo's existing setup-rust-toolchain action to test-model-map.yaml (12 other workflows already use it). Regenerated the schema too; it was stale on output_cost_per_audio before this merge.
  • OSV: postcss 8.5.22 → 8.5.23 (GHSA-fxqj-rqcc-2cmp) and brace-expansion 5.0.8 → 5.0.9 (GHSA-rgw5-rvv9-x895, dev), applied to both the pin and the security overrides.
  • Lint budgets: the merge's max-union loosened 14 limits. Recomputed each as the tightest value that still passes the gate, recovering base's stricter ratchet on 9 rules.

Important

budget-ratchet is red on purpose and needs a human ack (it is non-gating and stays out of required checks by design). Five ceilings genuinely had to rise, because both sides ratcheted independently since the branch point and the merged tree contains both sides' code: BLE001 +1, DTZ005 230→233, I001 267→270, TRY004 98→100, LIT002 27511→27678. The merge adds no net-new violations: every file's LIT002 count in the merged tree equals one of its two parents. Driving these back down would mean editing upstream code this sync is only carrying.


View with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is enabled.

yuneng-berri and others added 30 commits July 25, 2026 17:16
…ld-039815

chore(ui): rebuild Next.js build artifacts
The shadcn migration carried the antd modal's 1000px width over as an
unprefixed max-w-[1000px], which tailwind-merge keeps alongside the
DialogContent base class sm:max-w-md; the responsive variant wins from
640px up, so the dialog rendered at 448px. Prefix the override so the
merge drops the base clamp
The MCP Servers page was the only page-level tab bar using the segmented
(pill) TabsList stretched with w-full, which rendered a full-width grey
bar with a lone pill on the left. Every other page-level tab bar
(budgets, vector stores, access groups, organizations, routing groups,
API reference) uses the underlined line variant, so use that here too.
… body

Vertex passthrough classified any target URL containing "stream" as a streaming
request. `:streamRawPredict` carries that substring, so a unary Claude-on-Vertex
call whose body omits `stream` was routed through the streaming logging path.
That path never consults the response content-type, so a complete
`"type": "message"` JSON body was handed to the Anthropic SSE chunk parser,
which recognises none of it; the spend log recorded 0 prompt tokens,
0 completion tokens and zero cost

Streaming for the rawPredict family now comes from the request body, which is
what the Anthropic Messages contract uses for those endpoints. The
generateContent family keeps its URL signal because the Gemini REST body has no
`stream` field, and `?alt=sse` is still appended for every request that is
classified as streaming, so Gemini framing and its usage parsing are unchanged

Both passthrough streaming predicates read `.get("stream")` off a body that is
only annotated as a dict; `_read_request_body` returns whatever the JSON parser
produced, so an array body raised AttributeError. The two predicates are now one
owner that answers False for any non-object body, which covers the vertex,
mistral, anthropic, vllm and azure passthrough routes
…_context

fix(azure_ai): advertise 1M context window for Claude Opus 4.6+ on Foundry
…e button

DialogContent's close button is absolutely positioned 16px from the right
edge at 32px wide, so it overlays the rightmost 24px of the p-6 content
box. The justify-between header pins "+ Custom Server" to that same edge
and, being out of flow, the close button reserves nothing. Give the action
a right margin that clears it; keeping the margin on the button rather
than the row leaves the header rule full-bleed
The shadcn separator primitive ships `data-vertical:self-stretch` so a bare
vertical divider fills its row, but every call site overrides the height with
`h-5`. A definite cross size makes `align-self: stretch` behave as
`flex-start`, so the dividers rendered flush with the top of their flex line
instead of centered: 0px above and 18px below in the dashboard header, 0px
above and 12px below in the models table toolbar

Routes the three vertical dividers through a ToolbarSeparator that pairs the
fixed height with a same-variant `data-vertical:self-center`. Matching the
variant is what matters; tailwind-merge then drops the conflicting class
outright, whereas a plain `self-center` ties on specificity (the variant is
defined with `:where()`) and loses on utility order. The CLI-managed primitive
is left untouched
…m_resource

feat(mcp): send RFC 8707 resource indicators on upstream OAuth legs
…up in llm_as_a_judge (BerriAI#34509)

* fix(guardrails): resolve judge_model credentials via Router in llm_as_a_judge

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

* fix(guardrails): wire llm_router into DB-backed judge guardrail init paths

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

* test(guardrails): assert patch endpoint forwards llm_router to sync

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

* refactor(guardrails): resolve judge Router lazily and fix wildcard/alias dispatch

Resolve the proxy Router at judge-call time via an injected provider instead of
capturing it at construction, so a DB-backed judge guardrail created before the
Router exists no longer captures None permanently. Select the Router path with
router.get_model_list(model_name=judge_model) so wildcard routes and
model_group_alias keys resolve, not just literal deployment names. Isolate the
judge call from user-traffic routing with num_retries=0 and fallbacks=[].

Revert the llm_router threading through the DB sync/reinit/create/approve/patch
paths since the lazy provider makes it unnecessary. Replace mocked-Router tests
with real Router coverage for plain deployments, model_group_alias, and wildcard
routes, plus lazy per-call resolution.

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

* fix(guardrails): harden judge verdict parsing and guard proxy import

Strip markdown fences and surrounding prose before json.loads so fencing-prone
judge models evaluate instead of failing open, guard the proxy_server import in
_default_router_provider so an unimportable proxy falls back to the SDK, and
snapshot/restore global callback lists in the DB-path judge registry tests

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

* fix(guardrails): reject non-object judge verdicts instead of failing open as success

* fix(guardrails): route hidden model_group_alias judge models through the Router

---------

Co-authored-by: milan <milan@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: yucheng-berri <yucheng@berri.ai>
GET /v1/tool/spend served the Cost Optimization card with two raw queries
over LiteLLM_SpendLogToolIndex x LiteLLM_SpendLogs on every dashboard load;
the totals query's driving scan was all of SpendLogs in the window. Both
per-request tables reach 1M+ rows at customer scale, so the card cost
O(traffic) per view and had to be capped at 30 days.

The index writer also mined proxy_server_request.tools, i.e. tools DECLARED
in the request body, attributing each request's full spend to tools that
never ran; and all non-MCP mining ran against payload fields that are '{}'
unless store_prompts_in_spend_logs is enabled, so non-MCP coverage silently
depended on a privacy setting.

Now the spend writer builds a ToolUsageTransaction at request time from
invoked tools only, resolved by the shared get_tool_calls_from_response
normalizer so every response surface (chat completions, Responses API,
Anthropic Messages) is covered; the tool registry's response arm delegates
to the same owner. Transactions queue beside the spend-log queue and the
flush job writes index rows plus a new LiteLLM_DailyToolSpend rollup
(date, tool_name PK) in one transaction, retrying connection errors with
backoff (a failed batch commits nothing, so the retry cannot double-count)
and dropping the batch with an error log on anything else.

The endpoint aggregates in SQL: by_tool is the top TOOL_SPEND_TOP_TOOLS
tools by spend via group_by and daily covers only those tools, so the
response is bounded by days x TOOL_SPEND_TOP_TOOLS regardless of range or
tool-name cardinality; the 30-day clamp is gone. total_spend is dropped
from the response; it was never rendered and its deduplicated semantics
are not computable from a rollup. Spend-log retention deliberately does
not touch the rollup, so tool spend history outlives per-request rows.
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>
The Team dropdown popup is pinned to the trigger width via
w-(--anchor-width) and clips its overflow, while Base UI's ItemText
wrapper is flex-1 shrink-0 with min-width: auto, so it sizes itself to
the full nowrap label and simply overflows the popup. Teams without a
team_alias render their 36-char id, so those options were sliced
mid-character with no ellipsis.

Clears min-width: auto off the text wrapper and truncates the label at
the call site. The underlying gap is in the shared Select primitive,
which any long-labelled select in the dashboard will hit; that is left
for a separate change.
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
`/customer/aliases` shipped two days ago and has not been in a release, so its
wire contract is still free to change. This lands it on the control-plane
contract before that stops being true, since after a release the path, the param
names and the envelope would all need a permanent legacy adapter

The endpoint becomes `GET /management/v1/spend_logs/end_users`. It is a facet,
the distinct values one column takes over a filtered query on a resource, not an
entity collection; naming it after `customers` implied it listed the end-user
table when it actually reads spend logs, which is a different row set. Serving it
under the parent resource means its filters are the parent's filters, so the
dropdown offers exactly the values the logs table can show without two endpoints
having to keep agreeing on that

Contract changes: `size` becomes `page_size`, `search` becomes `q`, the window
moves from flat `start_date` / `end_date` to `filter[startTime][gte]` / `[lte]`,
and the body becomes `{data, meta, links}`. Unknown query params are now a 400
rather than being silently dropped, because an ignored filter over-returns data.
Errors are RFC 9457 problem documents on this prefix only; every other route
keeps the shape its callers already parse

`links` is what makes the rest deferrable. The dashboard hook follows the
server's `links.next` instead of computing `page + 1`, so moving this to cursor
pagination later changes the links and nothing the client does. That matters
because the inner scan is a sliding window, so offset paging can currently skip
or repeat an end user across pages; the fix is a follow-up, and the hypermedia
means it will not be a breaking one

Cursor mode, `sort`, `include`, ETag / `If-None-Match` and the generic `ListSpec`
framework are all deliberately out of scope here. They are additive or internal,
so none of them needs to beat the release
The RFC 9457 `type` was `https://docs.litellm.ai/errors/<slug>`, copied from the
standard's own error example. That path is a 404 and there is no docs section
behind it, so every error body shipped a broken link

RFC 9457 only requires `type` to identify the problem type; it encourages, but
does not require, that dereferencing it yield documentation. An https URI makes a
promise we are not keeping, so use `urn:litellm:error:<slug>` instead, which
carries the same machine-readable identity with nothing to resolve. Switching to
an https base later is a contract change for anyone matching on `type`, so that
should wait for pages that actually exist

A test pins the identifier against regressing to an https docs URL, since the
existing assertion built the expected value from the same constant and would have
stayed green whatever it held
…ollup

Three fixes from an adversarial review of this branch, each at the owning
seam rather than the report site.

The flush retried DB_CONNECTION_ERROR_TYPES, which includes ReadTimeout.
A ReadTimeout is the committed-but-unacked case: the review reproduced the
engine abandoning the transaction open on the pooled connection, the retry
stacking its statements into it, and one commit applying both increment
sets while the flush reports success. The retry now covers only
ConnectError, the one failure that proves the statements never reached the
database; post-send failures drop the batch with an error log. The
docstring no longer claims an idempotency the pattern does not have. The
same hazard exists in the untouched daily spend writer and is left for its
own change.

get_tool_calls_from_response read choices[0] only, so a tool invoked in a
later choice of an n>1 response earned spend but never reached the rollup,
the index, or the registry. Choice scope is now an explicit parameter:
accounting passes include_all_choices=True because every choice costs
money; guardrails keep the primary-choice default because they rebuild the
primary assistant message. First multi-choice fixtures in the suite pin
both scopes.

maxBarSize=64 had been added to the shared BarChart unconditionally,
resizing every existing consumer. It is now a prop; only the tool spend
charts opt in. The legend flex-wrap changes stay global because clipping
overflow was a defect, not a preference.
…iles

Replace Any-typed seams with real types in the files carrying the highest
reportAny/reportExplicitAny density: typed Prisma read helpers in the MCP
db layer and verification token repository, TypedDicts for OAuth credential
payloads and aggregated spend rows, a DailySpendRecord protocol for the
daily activity endpoints, and concrete request/response types in the
volcengine, openai evals, azure batches, azure_ai count_tokens, and ocr
transformation modules. Modernize touched annotations to PEP 604/585 forms.

No casts, no type: ignore, no noqa, no new Any annotations, no behavior
changes. Whole-tree basedpyright: reportAny 27,005 -> 24,427,
reportExplicitAny 7,439 -> 7,280, no rule increased anywhere. Budgets
ratcheted: basedpyright -2,869, ruff-strict -1,505, type-discipline -167.
uv selects an interpreter before resolving dependencies, so with no
--python request the stock macOS /usr/bin/python3 (3.9.6) satisfies the
unconstrained request and resolution then fails against litellm's
requires-python (>=3.10,<3.15) instead of downloading a managed Python.
Request the requires-python range explicitly in install-cli.sh and
install.sh so uv reuses a compatible system interpreter when present and
downloads a managed one otherwise. The manual-fallback hint in the die
message carries the same flag so it no longer reproduces the failure.
Both failures are from this branch, not pre-existing

The component allowlist test asserts the gateway and backend route sets union to
the whole app, so any route on neither is a 404 on both pods. Allowlist the
`/management/v1/` prefix on the backend, next to the other control plane
entries, so every resource that moves under it later is covered without a
per-resource edit

The otel handler test builds its request as a SimpleNamespace carrying only
`state`. The validation handler now reads `request.url.path` to decide whether
the caller is on a surface with its own error contract, so the fake needs a url;
a real Request always has one, which is why the handler does not guard for it

The control plane branch returns early, and nothing covered that it still closes
the dangling SERVER span first, so those requests would have leaked a span
apiece. Added a case that pins it; removing the close call fails it
…ng-dd340c

style(ui): match MCP Servers tabs to the dashboard's line tab pattern
…vider-center-b75b6d

fix(ui): center vertical toolbar dividers
…toration-c06977

fix(ui): restore the Add MCP Server dialog size and header spacing
…opdown-truncate-2b2a3f

fix(ui): truncate long team names in the models table team dropdown
Same cause as the otel handler test: this file builds its request as a
SimpleNamespace carrying only `state`, and the validation handler now reads
`request.url.path` to pick an error contract, so the fake needs a url

While here, cover what the two existing tests do not. They only exercise the
proxy-wide 422, and the control plane's 400 problem document was reachable only
through the route test, which registers its own copy of the handler in a local
app rather than the real one. Two cases now pin the real handler directly: a
`/management/v1` path returns problem+json with a `detail` string, and paths that
merely resemble the prefix (`/management`, `/v1/management/foo`) keep the 422
shape their callers parse
tin-berri and others added 21 commits July 30, 2026 19:26
…n classifier call (BerriAI#35300)

The complexity router's classifier sub-call copies the parent request's metadata
verbatim, so its spend log row carries the caller's key, team and user and is
indistinguishable from traffic the caller actually sent. Nothing on the row says
otherwise: call_type is "acompletion" either way, model_group is overwritten to the
classifier's own model group so the row never looks auto-routed, and routing_decision
is absent exactly as it is on an ordinary request.

Record the fact the system already knows at call time. internal_call_origin is
declared on SpendLogsMetadata, which is the allowlist _get_spend_logs_metadata
projects onto, and stamped in _classifier_call_metadata; both classifier paths
already route through that one function and it feeds the metadata and
litellm_metadata buckets alike, so every request surface is covered at one site.
The key is reserved rather than caller-supplied, so it joins routing_decision in the
untrusted-metadata strip and a caller cannot label their own traffic as router
overhead.

The classifier call also inherited no session identity, so the router minted a fresh
trace id and the row landed in a session of its own. Forwarding the parent's session
puts it in the trace of the request that triggered it, which is where an operator
looks for what the routing cost.
…s_guardrail_info

fix(logging): bind litellm_metadata by reference in function_setup so guardrail info reaches spend logs
…erriAI#35291)

* feat(s3): support SSE-KMS encryption params on both S3 logging paths

* fix(s3): ignore non-string SSE config values instead of crashing logger init

* Update litellm/integrations/s3.py

Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix(s3): invalidate only the mistyped SSE field instead of dropping both

---------

Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…_survive_db_sync

fix(policy_engine): preserve config-defined policies across DB sync and expose them via list APIs
…_guardrail_flake

test: fix order-dependent flake in passthrough guardrail call-type test
fix(pricing): correct gpt-5.6 prices for openai, bedrock, and flex long context
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…ice_tier

fix(cost): bill the fast service tier at the priority rate
…_code_map

fix(responses): map all documented in-stream error codes to real HTTP statuses
…est_gaps

test(pricing): cover gpt-5.6 cache-cost plumbing and bedrock_mantle responses billing
… ~372 commits)

Weekly-cadence sync following the v1.93.0 and 2026-07-28 precedents.
Conflicts (7):
- test_server_root_path.yml: deleted upstream, deletion taken (supersedes
  our #28 Node unpin for a workflow that no longer exists)
- test-model-map.yaml: semantic union — our backup-map validation +
  duplicate-key check retained, upstream's schema sync check added
- ui package.json/lock, uv.lock: upstream taken (upstream postcss 8.5.22
  supersedes our 8.5.18 security floor)
- ruff/type-discipline budgets: max-union placeholder; regenerated to the
  merged tree's actual counts in the follow-up commit
The fleet brain since NOL-376. Neither upstream branch prices the dated
OpenRouter slug, so proxy spend depended entirely on the litellm-config
model_info pin; with the key in the map the pricing is defense-in-depth.
Added to canonical and backup byte-identical (the #29 lesson: the two
maps drift and only one is live in any given deployment).
OpenRouter list 2026-08-03: $0.09/M in, $0.18/M out, $0.018/M cache read.
make lint-ruff-budget-update / lint-type-discipline-budget-update over the
post-sync tree, replacing the merge's max-union placeholders with actual
counts (both sides had ratcheted independently since the last sync).
Base moved four commits ahead (MiniMax video/image providers #31, the blind-catch
narrowing #33, and the two Gemini Veo route fixes #34/#35). Everything auto-merged
except tests/test_litellm/interactions/test_openapi_compliance.py, where both sides
had independently loosened the same Content-discriminator assertion after Google's
spec dropped the keyword: base via #31, upstream via BerriAI#35161. Kept upstream's
version, which is a strict superset (it accepts a discriminator mapping *or* a
per-variant `type` const/1-item enum, asserts the values are distinct, and pins
TextContent to "text") and matches the `_declared_type_value` helper already in
the file.

Budget ceilings: LIT002 (27511 -> 27678) and TRY004 (98 -> 100) were the only two
rules over limit on the merged tree with a count above the base, so the gates would
have failed. The merge adds no net-new violations: every file's LIT002 count in the
merged tree equals one of its two parents, so the overage is purely the union of
ceilings both sides had ratcheted down independently since the branch point. Raised
those two to the merged tree's actual counts; every other ratchet is untouched.

Also regenerated model_prices_and_context_window.schema.json, which the new upstream
sync check flagged as stale (missing `output_cost_per_audio`) already before this
merge.

Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>
@blackflame007 blackflame007 changed the title Sync upstream/main into litellm_internal_staging (2026-08-03, ~372 commits) chore: sync upstream/main into litellm_internal_staging (2026-08-03, ~372 commits) Aug 3, 2026
@blacksmith-sh

blacksmith-sh Bot commented Aug 3, 2026

Copy link
Copy Markdown

Found 1 test failure on Blacksmith runners:

Failure

Test View Logs
budget limit(s) loosened vs base bda9a9727efa1cebb1babc33f8152554ea735660 (merge-base b
da9a9727efa):/
budget limit(s) loosened vs base bda9a9727efa1cebb1babc33f8152554ea735660 (merge-base b
da9a9727efa):
View Logs

Fix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.

…ed budgets

Three reds on the sync branch, all CI/infra rather than product code.

test-model-map's schema-sync step runs `uv run --frozen`, which installs the
project and therefore builds the litellm-rust bridge through maturin. The locked
aws-sdk/aws-smithy crates need rustc 1.94.1 and the runner image preinstalls
1.92.0, so cargo failed before the check ever ran. The repo already has a
composite action for exactly this (12 other workflows use it); this job was
missing it because neither the fork's nor upstream's copy of the workflow had a
Rust step before upstream added the uv-based check.

OSV flagged two advisories against the merged UI lock: postcss 8.5.22
(GHSA-fxqj-rqcc-2cmp) and brace-expansion 5.0.8 (GHSA-rgw5-rvv9-x895, dev).
Both are patch bumps to 8.5.23 / 5.0.9, applied to the devDependency pin and the
security overrides so the floors stay explicit. Regenerating the lock also pruned
three stale @playwright/test optional-peer entries; nothing in the dashboard's
package.json, workflows, or vitest config references playwright.

Budget ceilings: the previous commit cleared the two gate breaches but left the
merge's max-union values in place, which loosened 14 limits. Recomputed each as
the tightest value that still passes the gate (the stricter of the two sides,
raised to the merged tree's actual count only when that count also exceeds the
base's count). That recovers base's stricter ratchet on 9 rules and leaves 5
unavoidable raises: BLE001 +1, DTZ005 230->233, I001 267->270, TRY004 98->100,
LIT002 27511->27678. The non-gating budget-ratchet job stays red on those by
design so a human acknowledges them; removing them would mean editing upstream
code this sync is only carrying.

Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a3f35a1bed

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@@ -31072,6 +31078,21 @@
"supports_reasoning": true,
"supports_tool_choice": true
},
"openrouter/deepseek/deepseek-v4-flash-0731": {
"input_cost_per_token": 9e-08,
"input_cost_per_token_cache_hit": 1.8e-08,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Use the cache-read pricing key

When OpenRouter reports cached prompt tokens for this model, the generic cost path reads cache_read_input_token_cost and multiplies cached tokens by that value; input_cost_per_token_cache_hit is never consumed by the cost calculator. Consequently this entry—and its identical backup-map entry—prices cache hits at zero instead of the intended $0.018/M, undercounting spend whenever the deployment relies on the packaged map. Store the rate as cache_read_input_token_cost and cover cached-token cost calculation for this model.

AGENTS.md reference: AGENTS.md:L15-L15

Useful? React with 👍 / 👎.


if in_response_to is not None:
authn_key = f"{_SAML_AUTHN_REQUEST_CACHE_PREFIX}:{in_response_to}"
if cache.get_cache(key=authn_key) is None:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Share SAML request state across workers

In a multi-worker or multi-pod deployment where enable_redis_auth_cache remains at its default false, the SAML login request ID is stored only in the initiating worker's in-memory user_api_key_cache; this remains true even when a coordination Redis is configured. If the IdP callback lands on another worker, this lookup misses and rejects an otherwise valid SP-initiated login with 401, making admin login fail intermittently behind a normal load balancer. Store SAML request and replay state in an always-shared cache when Redis is available, or explicitly require sticky sessions/shared state for SAML.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

saml_sso.py is entirely upstream code (upstream PR BerriAI#31429), not introduced or modified by this sync PR. Reworking upstream's brand-new SAML request-state storage would create fork divergence in a security-sensitive file upstream is actively developing; it should be fixed upstream.

Comment on lines +1071 to 1073
ProxyStartupEvent._add_proxy_budget_to_db()
asyncio.create_task(
ProxyStartupEvent._warm_global_spend_cache(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Finish proxy-budget initialization before warming spend

When an existing litellm-proxy-budget row has lifetime spend and a null budget_reset_at, startup launches the upsert/backfill and cache warm-up as separate tasks. The warm-up can read and cache the old spend while _upsert_proxy_budget_with_reset_at_backfill is awaiting its DB writes, after which the backfill zeroes the row without invalidating the value just cached. The proxy can then reject requests against stale pre-window spend until the cache expires; await the upsert/backfill before warming the global spend cache, or invalidate the cache after the backfill.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The _add_proxy_budget_to_db() / _warm_global_spend_cache task pair is upstream code (blame: upstream commits from 2026-01-29 and 2026-07-25), untouched by this fork. Reordering upstream's startup sequence is out of scope for a sync PR and belongs upstream.

blackflame007 and others added 2 commits August 3, 2026 17:52
…eads

`openrouter/deepseek/deepseek-v4-flash-0731` carried its $0.018/M cache-read rate
only as `input_cost_per_token_cache_hit`. ModelInfoBase declares that field but no
cost calculator reads it: `_calculate_input_cost` bills cached prompt tokens at
`cache_read_input_token_cost`, which `_get_token_base_cost` resolves to 0.0 when
absent. So cache hits billed at $0 rather than $0.018/M, undercounting spend on the
fleet brain, and prod feels it because it runs LITELLM_LOCAL_MODEL_COST_MAP=True
and bills off the packaged backup map.

Added `cache_read_input_token_cost` at the same rate to both maps, keeping the
cache_hit spelling as the 23 other dual-key entries (deepseek/deepseek-chat and
the novita family) already do. Covered the billing through the local map so a
regression shows up as the money it costs, plus a map-shape pin next to the other
canonical/backup drift guards.

Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>
Upstream BerriAI#34885 wrapped the Tabs props at printWidth 100, but this fork's
ui/litellm-dashboard/.prettierrc sets printWidth 120, so `prettier --check`
in the gating UI Lint job rejected the file. Whitespace only, produced by
the pinned prettier 3.2.5.

Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>
@blackflame007

Copy link
Copy Markdown
Author

budget-ratchet acknowledgment (merging over the red per the check's own contract).

The five raises are all violations inherited from upstream code in this ~372-commit sync, not new fork code:

budget rule raise
ruff-strict BLE001 2898 → 2899 (+1)
ruff-strict DTZ005 230 → 233 (+3)
ruff-strict I001 267 → 270 (+3)
ruff-strict TRY004 98 → 100 (+2)
type-discipline LIT002 27511 → 27678 (+167)

The check states: "This check is non-gating: if the increase is justified (e.g. a newly banned API), a human can merge over the red after acknowledging it." This is that acknowledgment. Rewriting ~176 lines of upstream style in-fork would permanently grow our delta and the conflict surface of every future sync — the wrong trade. Budgets were regenerated to the merged tree's actual counts with the repo's own make lint-*-budget-update targets, so the ratchet is exact (not padded) and future PRs ratchet down from here. The other 80 checks are green, including the full test matrix.

@blackflame007
blackflame007 merged commit c592498 into litellm_internal_staging Aug 3, 2026
80 of 81 checks passed
@blackflame007
blackflame007 deleted the sync/upstream-main-2026-08-03 branch August 3, 2026 18:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.