Commit graph

5518 commits

Author SHA1 Message Date
Tin Chi Lo
099d26204f fix(ui): read the preset catalog at runtime in the vitest mock
The autoRouterPresets mock imported litellm/proxy/public_endpoints/autorouter_presets.json
as a module. That path sits outside ui/litellm-dashboard, the only directory the UI
Dockerfile copies, so `next build` type-checking inside the image failed with
"Cannot find module" and the ui-image job went red on every PR that touched an
image-scan path. Read the file with fs at runtime instead; vitest still derives
expectations from the real bundled catalog.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HqEPCNLDrssxsuezhAaL4j
2026-09-02 19:54:09 -07:00
yassin
9ecc953618 Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_fix_agent_mcp_grants
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-03 02:51:45 +00:00
tin-berri
64e45a069d
feat(complexity_router): opt-in modality override of a kept session-affinity pin (#39454)
A session pinned to a text-only model failed every image turn with a provider 400, because the modality gate exempts a kept pin by cause. Add modality_pin_override so that exemption is conditional: the image turn is re-placed on a capable model for that request only, reported as cause modality_pin_override, and the stored pin is left untouched so the next text turn replays it.

The pin write on the replay path already happens upstream of the gate and stores the session's own model, so pin survival is structural rather than bookkeeping. The new cause joins the non-pinnable set. Default off at every layer.
2026-09-02 19:36:09 -07:00
mateo-berri
55853c10f5 Merge branch 'litellm_internal_staging' of https://github.com/BerriAI/litellm into litellm_spend_log_request_id_call_id 2026-09-02 19:22:02 -07:00
mateo-berri
872e115295 fix(containers): pass upstream error status through and forward list pagination params
The container retrieve, list, delete, create and file routes validated the
provider's error body against the success model, so a deleted or unknown
container and a rejected API key surfaced as 500 pydantic errors instead of
the upstream 404 or 401. The handlers now raise the provider error class with
the upstream status and message before transforming the response.

GET /v1/containers dropped after, limit and order before calling the
provider, and GET /v1/containers/{id}/files dropped the same three, so
paginated list calls ignored their pagination arguments. Both routes now
forward their declared query params.
2026-09-02 18:46:00 -07:00
mateo-berri
45988143bc Merge branch 'litellm_internal_staging' of https://github.com/BerriAI/litellm into litellm_fix_agent_mcp_grants
# Conflicts:
#	type-discipline-budget.json
2026-09-02 18:42:33 -07:00
devin-ai-integration[bot]
8441dd6e8c
fix(proxy): keep SpendLogs and callback session ids in sync when the request has none (#39450)
* fix(proxy): keep SpendLogs and callback session ids in sync when the request has none

Add general_settings.missing_session_id (generate | reject). In generate mode one id is
stamped into litellm_session_id, litellm_trace_id and metadata.session_id before callbacks
run, so LiteLLM_SpendLogs.session_id and the Langfuse session id match. In reject mode such
requests get a 400. Unset keeps the legacy behavior. MCP routes are not affected

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

* chore(proxy): regenerate schema.d.ts and shorten mutable-ok comment for ruff format

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

* fix(proxy): mark generated session ids so affinity consumers do not pin on them

Fireworks x-session-affinity, the router session_affinity pre-call check and the
complexity router session pin all read metadata.session_id as a caller-chosen
stable key. A missing_session_id: generate id is fresh per request, so it now
carries metadata.litellm_session_id_generated and those consumers skip it

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

---------

Co-authored-by: yassin <yassin@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-02 18:28:06 -07:00
mateo-berri
0c2be34fb1 Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_spend_log_request_id_call_id
# Conflicts:
#	litellm/proxy/spend_tracking/spend_management_endpoints.py
2026-09-02 18:04:37 -07:00
tin-berri
993766be0e
feat(proxy): serve the auto-router preset catalog at runtime (#39412)
The dashboard's template picker imported autorouter_presets.json at build time, so every
catalog change needed a dashboard rebuild and artifacts refresh. The catalog now lives in
litellm/proxy/public_endpoints/ and GET /public/autorouter_presets serves it, fetching
litellm.autorouter_presets_url (GitHub raw on main, 1h in-process cache, bundled fallback)
so a merged catalog change propagates to running proxies like the model cost map does.
The dashboard fetches it at runtime via useAutoRouterPresets and keeps no local copy.

Resolves LIT-6764
2026-09-02 18:03:22 -07:00
tin-berri
ff1f21aea9
fix(ui): paginate request logs by session groups server-side (#39257)
* fix(ui): paginate request logs by session groups server-side

The logs table server-paginated raw spend logs and then collapsed
multi-call sessions client-side, so a page could render 3 rows while
the footer claimed 25 and sessions straddled pages. Adds an opt-in
group_by_session param to /spend/logs/ui that pages and counts one
representative row per session (DISTINCT ON, newest non-MCP call),
keeps the bounded count contract, enriches whole-session llm/agent
composition counts, and deletes the client-side collapse pipeline.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QxT89fiygmzz2ALcjpu7Ve

* feat(ui): add a 10 rows-per-page option and default request logs to it

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QxT89fiygmzz2ALcjpu7Ve

* fix(ui): key session aggregates per api key in the logs enrichment

Grouped pagination splits a reused session id into one row per api key,
but the enrichment still aggregated by session_id alone, so both rows
showed combined spend and counts. The aggregate query now groups by
(session_id, api_key), the count folds into it (the separate group_by
query is deleted), and each row reads its own key's totals.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QxT89fiygmzz2ALcjpu7Ve

* fix(ui): treat an empty api_key as a real session group value

The spend-log schema defaults api_key to an empty string; truthiness
guards in the enrichment treated it as missing, so keyless multi-call
sessions lost their count and spend. Only None means missing now.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QxT89fiygmzz2ALcjpu7Ve

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-09-02 17:54:17 -07:00
mateo-berri
6d297737c9 Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_spend_log_request_id_call_id 2026-09-02 17:49:58 -07:00
mateo-berri
a196a50450 fix(spend_logs): resolve the caller's own row before cold storage and list the exact request_id row first 2026-09-02 17:36:30 -07:00
yassin
61e2e3e646 Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_fix_agent_mcp_grants
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-03 00:25:04 +00:00
mateo-berri
f7ef2899ec chore: merge litellm_internal_staging into litellm_fix_agent_mcp_grants 2026-09-02 17:09:01 -07:00
tin-berri
632898b2a6
feat(router): add a hybrid classifier that defers near tier boundaries (#39403) 2026-09-02 23:51:43 +00:00
tin-berri
9aeeca4ce3
feat(router): add heuristic v2 complexity routing (#39276)
* feat(router): add trained heuristic complexity routing

* feat(router): expose heuristic v2 classifier

* style(router): format heuristic v2 predictor
2026-09-02 23:33:04 +00:00
yujonglee
082bea851e
Merge pull request #39334 from BerriAI/litellm_rust_opt_in_configuration
feat(python): unify Rust opt-in and bridge policy
2026-09-02 16:26:36 -07:00
mateo-berri
0f628f78e7 Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_spend_log_request_id_call_id 2026-09-02 16:15:36 -07:00
mateo-berri
cbeef3b98c fix(proxy): bound client x-litellm-call-id, open log deep links by call id, prefer exact request_id rows 2026-09-02 15:45:55 -07:00
yassin
659ed03502 Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_fix_agent_mcp_grants
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-02 22:15:20 +00:00
yucheng-berri
b4f5b6aa94
fix(logging): redact credential query params from the uvicorn access log (#39293)
* fix(logging): redact credential query params from the uvicorn access log

Raw virtual keys reached container stdout two ways:

- `GET /key/info?key=sk-...`, `/global/spend/report?api_key=sk-...`,
  `/key/spend/report`, `/spend/logs`, `/user/daily/activity` and the Gemini
  passthrough routes all put the credential in the request target, and
  `uvicorn.access` had no redaction filter (only `uvicorn.error` did).
- the key budget error interpolates `LiteLLM_VerificationToken.key_name`,
  a column with no enforced shape, into a message that is both logged and
  returned to the caller.

`SecretRedactionFilter` cannot be reused on an access logger: it collapses the
record into `record.msg` and clears `record.args`, and uvicorn's AccessFormatter
unpacks those args at emit time, so every access line would raise TypeError.
`AccessLogRedactionFilter` scrubs the positional args in place instead.

An access line is the one input to the secret regex an unauthenticated caller
controls end to end, so two bounds go with it. The request target is cut back to
a whole query parameter under 512 characters before it is scanned, since a half
parameter is too short to match its own pattern and would be logged raw, and the
dropped tail is not logged at all. The connection-string pattern is bounded too,
because its user half could previously re-scan the rest of the string from every
`://`: a 16 KB URL of `a://` pairs took 314s and now takes 0.12s, with the caps
set high enough that an RDS IAM auth token used as a DSN password still redacts.

Credential query params are terminated by `&` like the existing `key=` and
`sig=` patterns, so redacting one param no longer swallows the rest of the
request line, and a second credential in the same query string is now redacted
on its own instead of surviving once the first one stops the span. `key_name` is
echoed into the budget error only when it still has the masked `sk-...abcd`
shape `abbreviate_api_key` writes, so a value put there by a direct DB write or
a migration falls back to the key alias.

Also point the `/key/info` and spend-report examples at the sha256 hash both
endpoints already accept, so callers stop putting raw keys in URLs that
third-party access logs record.

Resolves LIT-5909

* test(logging): assert on emitted access lines instead of filter registration

The two registration tests checked that an AccessLogRedactionFilter instance
sits in uvicorn.access.filters, which is the shape of the code rather than its
behavior. Handing the logger a real access record and reading what a handler
wrote covers the same wiring and still fails when the registration is removed.

* fix(logging): redact percent-encoded credentials from access logs

?k%65y=sk%2D... is a working credential once the request parser decodes it,
but the redaction patterns match literal text and never see it. Decode the
request target as a detector and drop the query when decoding reveals a
secret. The decoded text is never logged back, so a %0A cannot forge a
following log line

Also accept any four non-space characters in the masked key_name check, since
abbreviate_api_key copies the last four characters of a custom key verbatim
and those can be punctuation or non-ASCII

* fix(auth): keep control codes out of the masked key label

/key/generate accepts a custom key ending in an escape sequence, and
abbreviate_api_key copies those four characters into key_name verbatim, so
the over-budget message carried them to a terminal and a log viewer. Bar
whitespace and C0/C1 control codes from the four, and keep everything else
2026-09-02 15:10:36 -07:00
devin-ai-integration[bot]
a76cb6feaf
feat(mcp): semantic tool search for the native MCP Gateway (#39404)
The mcp_tool_search virtual tool only did substring token matching, so a native MCP client asking for "FX" could not find a tool described as "foreign exchange rates" even though the same catalog is ranked by embeddings on /responses and /chat/completions.

Adds litellm_settings.mcp_tool_search (embedding_model, top_k, similarity_threshold, core_tools). With an embedding model the caller's authorized catalog from _list_mcp_tools is ranked by cosine similarity of name plus description; configured core tools the caller can reach come first and do not consume top_k. Without an embedding model the keyword fallback keeps the old behavior. Settings are hot-reloadable from the DB, exposed on /get and /update mcp_tool_search_settings, and editable from the Admin UI under MCP Servers > Tool Search. The embedding index is shared with agent_search via a new SemanticTextIndex.

Resolves LIT-6751

Co-authored-by: yassin <yassin@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-02 15:09:44 -07:00
yassin
615b404d12 Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_fix_agent_mcp_grants
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-02 22:09:19 +00:00
Yassin Kortam
25991fe78a
feat(auth): enforce configurable password policy and SSO-only login (#39381)
Adds a configurable password-strength policy (default: min 12 chars,
upper/lower/number/special, all individually toggleable, floored at 8
so a misconfigured minimum cannot disable the length check, and
unicode-aware so an accented letter cannot satisfy the special-
character requirement) enforced on every path that sets a local
user's password: /user/update, /user/bulk_update, and the invitation
onboarding claim flow.

Adds general_settings.disable_password_login_when_sso_enabled, which
rejects username/password login on /login, /v2/login and /v3/login
(including the UI_USERNAME/UI_PASSWORD admin fallback) once ANY
configured SSO provider is FULLY ready: every companion secret/
endpoint an OAuth provider needs, checked independently per provider
so a stray leftover client id for an unused provider can't mask a
different, fully configured one; and for SAML, the optional
python3-saml runtime being importable, checked without letting a
fully-missing package's ModuleNotFoundError take down password login
itself. SSO becomes the enforced boundary for interactive UI access
without an incomplete, mixed, or half-installed SSO setup locking
every admin out or breaking login outright. Master-key API access is
untouched, and unsetting the setting plus a restart restores password
login as the documented recovery path.
2026-09-02 14:28:13 -07:00
Yassin Kortam
646f3404a5
fix(security): restrict and validate file uploads at /v1/files and /upload/logo (#39379)
* fix(security): restrict and validate file uploads at /v1/files and /upload/logo

Extends fast-fail upload validation to every purpose at POST /v1/files,
not just purpose=batch: a configurable max_file_size_mb size cap and a
blocked_file_extensions denylist, plus rejection of filenames carrying a
directory-traversal component before anything is read, stored, or
forwarded to a provider.

Also fixes two concrete gaps found while auditing every upload surface:
the Azure Blob Storage backend derived a blob path's extension with
filename.split(".")[-1], which does not parse path structure and let a
crafted filename embed a directory traversal sequence into the stored
blob path; and POST /upload/logo (the admin UI logo upload) had no
role check at all, so any authenticated API key, not just a proxy
admin, could write a file to the server's disk.

* fix(lint): drop cast()/mutation from settings coercion, sync blocked_file_extensions on reload

Replaces the TypeAdapter+cast() reads of max_file_size_mb and
blocked_file_extensions with small isinstance-based validators, since the
codebase's cast() budget (LIT006) had no headroom left. Also adds the
blocked_file_extensions reload block that was missing from
_update_general_settings: it was registered as an editable setting but
never re-synced into runtime state, so a value set through the DB-backed
settings editor would silently never take effect (Greptile finding).

* fix(security): declare max_file_size_mb and blocked_file_extensions on ConfigGeneralSettings

The DB-backed general-settings update endpoints validate every field
through ConfigGeneralSettings.model_fields before persisting it, so
without these declarations an operator could never actually set either
setting through that path even though both were registered for the
Admin UI's settings editor and reloaded on config refresh (Greptile
finding). blocked_file_extensions is typed as a tuple, not a list, to
stay out of the immutable-collections lint budget; the stored JSON
value is unaffected since the raw request payload, not the validated
model, is what gets persisted.

* chore: regenerate schema.d.ts for the new ConfigGeneralSettings fields

* fix(security): normalize configured blocked_file_extensions casing

check_blocked_extension lowercased the uploaded filename's extension
before comparing but compared it against blocked_extensions verbatim,
so an admin-configured blocked_file_extensions: ['.EXE'] would never
match an uploaded payload.exe (Greptile finding). Normalizes the
configured values the same way at comparison time, and adds the
missing case (mismatched-case config, lowercase upload) as a
regression test, mutation-checked against the unfixed comparison.

* fix(security): restore caller-owned stream position after size inspection

_file_size_bytes unconditionally seeked back to 0 after measuring a
BinaryIO's length, discarding wherever the caller had actually
positioned it (Greptile finding). Saves and restores the original
position instead. Rewrites the existing test that had encoded the
old "always resets to 0" behavior as its expectation, and adds a
sibling case for the under-cap path; both are mutation-checked
against the unfixed always-reset-to-0 behavior.
2026-09-02 14:27:46 -07:00
tin-berri
2ade3e16a9
feat(ui): update OpenAI preset model tiers (#39396) 2026-09-02 13:45:16 -07:00
yassin
095be37ce5 Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_fix_agent_mcp_grants
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-02 20:35:21 +00:00
Yassin Kortam
711430216e
fix(ui): preserve full AgentCore runtime ARN in agent edit form (#39382)
parseDynamicAgentForForm recovered a credential field's value from a
stored model string by splitting both the model_template and the model
on "/" and matching by array index. That breaks for any placeholder
value that itself contains "/", such as a Bedrock AgentCore runtime ARN
resource path (runtime/<runtime-id>), silently dropping everything
after the first slash when populating the edit form. Saving without
touching the field then persisted the truncated ARN.

Replace the index-matching split with a non-mutating template parse
(split on the placeholder pattern, escape and rejoin the literal
segments into a regex) so a placeholder captures everything it needs
regardless of embedded slashes. Also add a lightweight ARN-shape
validator for the AgentCore runtime ARN field, guarded against a
malformed pattern string, so a truncated value is rejected client-side
before it reaches the backend.

Resolves LIT-6737
2026-09-02 13:33:14 -07:00
Yuneng Jiang
517e508700 Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_/mongodb-vector-store-e4ff63
# Conflicts:
#	uv.lock
2026-09-02 12:10:20 -07:00
Mateo Wang
b600f02fc2
Merge pull request #34788 from BerriAI/litellm_fix_s3_vectors_search
fix(vector_stores): s3 vectors search router bypass + rag query config drop + ui error swallow
2026-09-02 11:35:56 -07:00
Yuneng Jiang
5d7bf187a4
refactor(ui): pick the vector store id placeholder from a map
The chain had grown to four nested ternaries with a fifth level inside the
Vertex Search branch, which no-nested-ternary had two suppressions for. A lookup
keyed by provider drops both suppressions and leaves one condition, the Vertex
Search case that depends on whether an engine id has been entered.

Also hoists the MongoDB form fixtures in the tests, which the inline-object
budget counts.
2026-09-02 11:30:36 -07:00
Yuneng Jiang
1f8cbee8aa
feat(ui): add MongoDB Atlas to the vector store provider dropdown
The create form now offers MongoDB Atlas with its connection string, database,
collection, embedding model, vector field, text field and candidate count. The
connection string renders as a password input because it carries the database
user's password, and the embedding model is picked from the proxy's own models,
matching how Milvus and Valkey do it.

The vector store id doubles as the Atlas Vector Search index name, so the
placeholder says so.
2026-09-02 10:22:01 -07:00
mateo-berri
bf6aa9c390 Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_hide_admin_tabs_view_only
# Conflicts:
#	ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/page.test.tsx
2026-09-01 21:31:39 -07:00
mateo
0608f0a00f fix: reject unknown runtime router settings
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-02 02:17:02 +00:00
mateo-berri
e4c6badca2 Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_fix_agent_mcp_grants 2026-09-01 18:37:31 -07:00
mateo-berri
659eb0462e fix(mcp): deny every zero-resolution scoped request uniformly and persist agent MCP toolsets from the UI
Any scoped MCP request (/mcp/<name> path or x-mcp-servers header) that resolves to zero allowed servers now returns one generic 403, so unknown, unauthorized, and access-group names are indistinguishable and the error cannot be used to enumerate servers. The agent-attributed variant reruns the same scope resolver with the agent binding stripped and fires only when that rerun resolves, naming the vetoed server or access group and both fix paths

AgentObjectPermission now declares mcp_toolsets so PATCH /v1/agents keeps it, and the agent edit form round-trips toolsets alongside servers and access groups instead of dropping them on save. The two scope-resolver params only iterate their names, so they take Sequence[str] and the LIT001 budget ratchets down by the two annotations this widens
2026-09-01 18:37:23 -07:00
devin-ai-integration[bot]
2b616fc479
feat(scim): add placeholder listing and merge so a shadowed account can be healed (#39231)
Co-authored-by: yassin <yassin@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-02 01:04:53 +00:00
mateo-berri
c4982ca407 fix(mcp): error instead of silent empty tools when scoped MCP access is denied; grant agent MCP servers from the UI 2026-09-01 17:04:48 -07:00
ryan-crabbe-berri
944d018c72 test(ui): query the screen in models page tests and drop restating comments
The merge of #38872 into staging kept the destructured render queries
in the models-and-endpoints page test, which pushes
testing-library/prefer-screen-queries to 21 against a budget of 18 and
fails frontend-lint for every PR on top of it. Two comments that only
restated the list merge below them are gone as well.

Claude-Session: https://claude.ai/code/session_01QvQzYztinxj8ZuD5YxbVdL
2026-09-01 16:25:15 -07:00
ryan-crabbe-berri
fc1a5fd7f9
Merge pull request #39206 from BerriAI/litellm_lit_3925_clear_team_key_create
fix: stop a cleared Team field from blocking personal key creation
2026-09-01 16:21:52 -07:00
ryan-crabbe-berri
1964d92fc6 test(ui): query the clear button and the models page tabs through accessible screen queries 2026-09-01 16:04:45 -07:00
ryan-crabbe-berri
4acc1d15fb fix(ui): map a cleared Team dropdown back to an empty string in the auto-router form 2026-09-01 15:50:19 -07:00
ryan-crabbe-berri
c493fc855c fix(ui): show MCP servers and agents inherited from access groups on the team overview
The team Overview and Settings tabs fed only object_permission into the Object
Permissions card, so a team whose access group grants MCP servers or agents read
"MCP Servers 0" and "Agents 0" while the Models card next to it already listed the
inherited models. /team/info has returned access_group_mcp_server_ids and
access_group_agent_ids for a while, nothing in the dashboard read them.

ObjectPermissionsView now accepts the inherited ids and MCPServerPermissions /
AgentPermissions merge them into their lists with an Inherited tag, deduped
against direct grants, so an admin can tell a group grant from a direct one.

Claude-Session: https://claude.ai/code/session_01QvQzYztinxj8ZuD5YxbVdL
2026-09-01 15:46:14 -07:00
ryan-crabbe-berri
55d638412b fix: stop a cleared Team field from blocking personal key creation
Clearing the Team combobox in the Create Key modal left team_id set to an
empty string, so /key/generate treated the request as team key generation
and failed with a team-not-found error for non-admin members.

TeamDropdown now emits null on clear, and GenerateKeyRequest normalizes an
empty team_id to None so the request runs the personal key path.
2026-09-01 15:17:00 -07:00
devin-ai-integration[bot]
846900320e
feat(alerting): slack alerts for per-user daily/monthly spend thresholds and spend anomaly detection (#38438)
* feat(alerting): slack alerts for per-user daily/monthly spend thresholds and spend anomaly detection

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

* test(alerting): use specific ValidationError matches in config rejection test

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

* fix(proxy): tolerate mocked slack alerting args when scheduling user spend scan

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

* fix(alerting): reject non-finite values in user spend alert settings

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

---------

Co-authored-by: yassin <yassin@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-01 15:09:03 -07:00
Mateo Wang
bad55da9bf
Merge pull request #38872 from BerriAI/litellm_fix_viewer_add_model_tab
fix(ui): hide model write affordances from view-only admin sessions
2026-09-01 14:52:54 -07:00
ryan-crabbe-berri
4914914801 feat(guardrails): roll up Bedrock guardrail cost per usage counter
The daily guardrail usage rollup stored billable units per counter but no
cost, so the usage endpoints could only report units. The Bedrock hook now
stamps guardrail_cost_by_unit next to guardrail_usage, the spend-log
aggregator sums it into a new nullable cost column on
LiteLLM_DailyGuardrailUsageUnits, and /guardrails/usage/overview and
/guardrails/usage/detail/{id} return cost, totalCost and cost_by_unit /
cost_by_team / cost_by_key alongside the existing unit breakdowns.

Cost is nullable on purpose. Rows written before this migration, and rows
whose hook had no pricing entry, read as null rather than $0, and a single
unpriced increment keeps that row's cost unknown instead of partial.
guardrail_cost and the spend/budget path are untouched.

Claude-Session: https://claude.ai/code/session_01EX13mWex6RaBo9PYnkAtFW
2026-09-01 14:45:12 -07:00
mateo-berri
cb5201305a use screen queries in models page tests to satisfy lint budget 2026-09-01 14:20:41 -07:00
devin-ai-integration[bot]
558f42e304
fix(proxy): default max_idle_connection_lifetime to 60s on DB URLs (#39134)
* fix(proxy): default max_idle_connection_lifetime to 60s on DB URLs

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

* fix(proxy): regenerate schema.d.ts for database_max_idle_connection_lifetime

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

* fix(proxy): keep URL-pinned max_idle_connection_lifetime over config value

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

---------

Co-authored-by: yassin <yassin@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-01 14:16:51 -07:00
yuneng-jiang
8bc862f52c
Merge pull request #39129 from BerriAI/litellm_/litellm-issue-39078-c025be
fix(ui): render the logs Tools panel with theme tokens
2026-09-01 13:58:03 -07:00