Commit graph

5574 commits

Author SHA1 Message Date
ryan-crabbe-berri
5156dbc67f fix(ui): show the saved team in read mode before the model query refetches
Read mode built the Team display from the react-query snapshot while save only wrote localModelData, so the old team flashed until the parent's invalidation refetched. Read the team from localModelData like the neighbouring fields and look up its alias from the local team id. The regression test also waits for the Select trigger to report expanded before clicking an option, because Base UI opens the popup asynchronously and keeps the closed popup mounted, which made back-to-back option clicks flaky.
2026-09-17 17:04:14 -07:00
ryan-crabbe-berri
790c3ab71b refactor(ui): move the model team selector into ModelTeamSelect to keep ModelInfoEditForm under the eslint line cap
Some checks failed
LiteLLM Rust / rust-lint (push) Has been cancelled
LiteLLM Rust / rust-test (push) Has been cancelled
LiteLLM Rust / rust-wheel (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-09-17 16:32:17 -07:00
Devin AI
6db76b5f39 fix(ui): match the Team ID selector to the backend's proxy-admin-only bypass and show its placeholder
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-17 16:25:37 -07:00
Devin AI
500790a93a fix(ui): keep every team selectable for org admins in the Team ID selector
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-17 16:25:37 -07:00
Devin AI
59693d5019 fix(ui): only offer team admins the teams they administer in the Team ID selector
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-17 16:25:37 -07:00
Devin AI
a7daa21df7 fix(ui): let admins change a model's team from the model edit page
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-17 16:25:17 -07:00
ryan-crabbe-berri
10616d7407
Merge pull request #40875 from BerriAI/litellm_cache_leakage_all_models
fix(ui): list every provider in the cache leakage by-model table
2026-09-17 16:18:16 -07:00
ryan-crabbe-berri
29a959b3e8
Merge pull request #41632 from BerriAI/litellm_bulk_team_member_budget_update
feat(management_v1): bulk update team member budgets
2026-09-17 15:58:32 -07:00
yuneng-jiang
1dd4c13815
Merge pull request #41374 from BerriAI/litellm_fix_guardrail_lifecycle_untimed_entries
fix(ui): keep untimed guardrail entries on the request lifecycle
2026-09-17 13:44:18 -07:00
Yuneng Jiang
6544671a31
fix(ui): order each lifecycle phase on its own clock 2026-09-17 13:29:45 -07:00
ryan-crabbe-berri
648373a260 feat(management_v1): bulk update team member budgets
Adds POST /management/v1/teams/{team_id}/members/bulk_update, a merge patch
over per-member limits (max_budget_in_team, tpm_limit, rpm_limit,
budget_duration, allowed_models) for up to 500 members in one transaction.

Editing a team's default member budget has never reached members who already
have a budget row, because /team/member_add clones the default per member.
This gives admins one call to roll a new cap out across the roster, and each
result carries max_budget_source so a caller can see whether a member is on
their own cap or on the team default.

Reads run on the writer inside the batch transaction, and any budget row more
than one membership points at is cloned before it is written, so raising one
member's cap never moves another's.
2026-09-17 12:30:46 -07:00
ryan-crabbe-berri
8b64f1ef03
Merge pull request #41525 from BerriAI/litellm_team_admin_rpm_budget_fields
feat(proxy): let team admins edit rpm_limit and max_budget when enabled
2026-09-16 20:39:05 -07:00
ryan-crabbe-berri
d3f0607820 test(ui): find the max_budget checkbox by its new label 2026-09-16 17:50:57 -07:00
ryan-crabbe-berri
37c56df054 feat(proxy): let team admins edit rpm_limit and max_budget when enabled
Adds both fields to the team admin editable allow-list and the dashboard's team admin form. The existing budget authority check still stops a team admin from raising or removing a standalone team's budget.
2026-09-16 17:25:36 -07:00
ryan-crabbe-berri
3dfd24a8da
Merge pull request #41445 from BerriAI/litellm_ui_url_state_orgs-projects
feat(ui): persist organizations and projects list, detail tab and key table state in the URL
2026-09-16 17:17:01 -07:00
ryan-crabbe-berri
43713f7508
Merge pull request #39996 from BerriAI/litellm_team_admin_editable_fields
feat(proxy): let proxy admins choose which team fields team admins may edit
2026-09-16 17:07:13 -07:00
ryan-crabbe-berri
3ee8d43fdd fix(ui): send only a changed TPM limit from the team admin settings form
Some checks failed
LiteLLM Rust / rust-lint (push) Has been cancelled
LiteLLM Rust / rust-test (push) Has been cancelled
LiteLLM Rust / rust-wheel (push) Has been cancelled
Save stays disabled until the value differs from the team's, so an unchanged form never reaches /team/update
2026-09-16 16:37:35 -07:00
ryan-crabbe-berri
249a23b09c chore(ui): prune stale eslint suppressions for projects page 2026-09-16 16:12:48 -07:00
ryan-crabbe-berri
ef8e066c77 fix(ui): address review on orgs-projects url state
Keep /projects list paging as a pushed history entry, validate the
project key table page size against its offered options, and clear the
key table params through the table-state setters instead of a copied
key list.
2026-09-16 16:12:48 -07:00
ryan-crabbe-berri
df2dd9b7f2 feat(ui): keep projects search and project key table state in the URL
The projects list search lives in ?project_search= and its pagination now goes through useUrlTableState, keeping the page and page_size keys. The key table inside a project reads keys_search, keys_page and keys_page_size, resets to its first page on a new search, and no longer snaps a deep-linked page while the key fetch is failing. Closing a project drops its keys_ params so they do not leak into the next project
2026-09-16 16:12:47 -07:00
ryan-crabbe-berri
417cc5c4fb feat(ui): keep organizations list and detail tab state in the URL
The organizations list now reads its search (org_search), org ID filter (filter_org_id), sort (sort_by, sort_order) and pagination (page, page_size) from the URL through useUrlTableState. The detail view tabs are controlled by ?org_tab=, and the Edit row action opens ?org=<id>&org_tab=settings in one history entry instead of passing an editOrg flag
2026-09-16 16:12:47 -07:00
ryan-crabbe-berri
d29753c521 fix(ui): let another tab's column save replace a toggle this tab could not save
A column toggle that localStorage refused was kept in memory and read ahead of storage, so a later save from another tab stayed hidden until this tab saved again. A storage event now drops the in-memory copy for its key, or all of them when another tab clears storage
2026-09-16 15:51:45 -07:00
ryan-crabbe-berri
adc937c493 fix(ui): read persisted column visibility from storage instead of a mounted copy
usePersistedColumnVisibility kept a useState copy seeded once at mount, so a later tableId or defaults change showed the old table's columns and saved them under the new key. It now reads localStorage through useSyncExternalStore, keeping only writes that storage refused in memory, so the hook has no copy to go stale. Stored choices are layered over the defaults on every read, and changes saved in another tab show up.
2026-09-16 15:51:45 -07:00
ryan-crabbe-berri
4d30bbce45 fix(ui): keep controlled client-side table pages across data reloads
Controlled client-mode DataTables no longer let TanStack reset the page index when rows change, since the owner of the pagination state decides the page. Once rows settle, a page past the end snaps back to the last page, matching server mode
2026-09-16 15:51:45 -07:00
ryan-crabbe-berri
eda81fff59 feat(ui): shared URL-state layer for tables and tabs
Add useUrlTableState (search, sort, page, page size and filter_<column>
in the query string via one nuqs useQueryStates call, with keyPrefix and
urlKeys for routes that host two tables or need legacy key names) and
useUrlTab (validated ?tab= param with a role-aware fallback). Migrate
the Virtual Keys table onto useUrlTableState with a byte-identical URL
contract and bind the Playground tab strip to ?tab=.

DataTable gains controlled columnVisibility/onColumnVisibilityChange
plus usePersistedColumnVisibility (localStorage per table id), and an
isError prop that keeps the server page clamp from rewriting a
deep-linked ?page= after a failed fetch. Virtual Keys uses both.

The expired-session redirect in handleError now keeps the query string
and hash so the return URL captured on re-login restores the filtered
view instead of the bare list.

Delete useTabRouting and tabRoutes, the pathname tab router left over
from the reverted path-per-tab attempt (#34327, reverted in #34629);
tab persistence has to be a query param on the static export.
2026-09-16 15:51:45 -07:00
ryan-crabbe-berri
cac521246a style(ui): right-align the Save banner button 2026-09-16 15:38:30 -07:00
ryan-crabbe-berri
d9de2fc5f7 style(ui): right-align the team admin editable fields Save button 2026-09-16 15:38:30 -07:00
ryan-crabbe-berri
b92bd98df6 feat(ui): save team admin editable fields from their own card with a Save button
Ticking a field only changes the draft. The allow-list is written when the proxy admin clicks Save, and the card sits next to UI Settings instead of inside its auto-saving toggles.
2026-09-16 15:38:30 -07:00
ryan-crabbe-berri
a44a58e91a feat(proxy): let team admins edit tpm_limit when a proxy admin enables it
tpm_limit is the first field in the team-admin allow-list registry. The team
settings tab gives a team admin a form with only the enabled fields and sends
only those on save, and UI Settings labels the checkbox the same way
2026-09-16 15:38:30 -07:00
ryan-crabbe-berri
66519da9b6 fix(proxy): report the caller's team edit access on /team/info
The dashboard gated the team settings form on a role it guessed from the
is_* props, the members list and the org list. The org list is premium
gated and empty while loading, so a team admin who is also an org admin
was told team admins cannot edit, although /team/update accepts them as
an org admin

/team/info now returns caller_edit_access, resolved by the same helper
/team/update uses, and TeamInfo keys the form and the toast off that
field. The org list is only read for the organization dropdown now, and
general_settings is read through one validated accessor in both handlers
2026-09-16 15:38:30 -07:00
ryan-crabbe-berri
af0312ba8f refactor(ui): name the admin checks behind the team-admin edit branch
The five-condition chain pushed local/no-long-condition-chain one over its
ceiling and failed the dashboard lint gate.

Claude-Session: https://claude.ai/code/session_018PUCupsaarVLJy4iDFx256
2026-09-16 15:38:06 -07:00
ryan-crabbe-berri
9b77b5c2cb feat(proxy): let proxy admins choose which team fields team admins may edit
Team admins could never reach POST /team/update: the route gate answered
401 before the handler's team-admin branch ran. This moves /team/update
into the self-managed routes, resolves proxy admin, org admin or team
admin inside the handler, and filters team admins through a new
proxy-wide UI setting, team_admin_editable_team_fields. The setting is
an allow-list of team fields. Empty means team admins cannot edit team
settings and get a 403 pointing at the proxy admin, and changing a field
outside the list fails 403 naming that field. Only values that differ
from what is stored count, since the dashboard resends the whole form.

The registry of fields the setting accepts ships empty on purpose. Each
field lands in its own follow-up PR with its value diff and dashboard
wiring. The Admin UI gains a "Team admin editable fields" section under
Settings > UI and a toast on the team page while editing is disabled.

Refs LIT-5722

Claude-Session: https://claude.ai/code/session_01A6SkwJdfZUmkzfUkrEkqX8
2026-09-16 15:38:06 -07:00
ryan-crabbe-berri
9357491f91
Merge pull request #35418 from BerriAI/litellm_skills_ssh_sources
feat(ui): accept ssh clone urls when registering a skill
2026-09-16 15:36:18 -07:00
Yassin Kortam
f62f140f75
Merge pull request #41403 from BerriAI/litellm_key_total_spend
feat(proxy): expose lifetime total_spend on virtual keys
2026-09-16 14:49:02 -07:00
Yassin Kortam
2bfa10b6d9
Merge pull request #41311 from BerriAI/litellm_key_status_filter_deleted_lookup
feat(keys): filter /key/list by active, expired, revoked or deleted status and serve deleted keys from /key/info
2026-09-16 14:48:34 -07:00
Yassin Kortam
95abc9fb0b
Merge pull request #41330 from BerriAI/litellm_team_model_max_budget_v2
feat(team): team-level model_max_budget with key-level overrides
2026-09-16 14:48:29 -07:00
Yassin Kortam
314c0d71a5
Merge pull request #41327 from BerriAI/litellm_s3_log_prompts_only
feat(s3): add s3_log_prompts_only option to log prompts without responses
2026-09-16 14:48:18 -07:00
ryan
2bb478fb59 fix(ui): keep http and upper-case https skill sources clickable on the detail page
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-16 18:45:11 +00:00
ryan
7cd6869cfa test(ui): match skill source links by exact name so codeql stops flagging the host regexes
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-16 18:40:10 +00:00
ryan-crabbe-berri
ecd72c51da refactor(ui): register ssh skill sources verbatim and share the https host rules
The ssh parser rebuilt the clone url it was given, stripping a trailing .git and
appending one back. Git treats that suffix as optional, and the hosts whose clone
paths are not org/repo break when it is forced on, so an Azure DevOps v3 or a
CodeCommit v1/repos url registered through the form would no longer clone.

It also carried its own host pattern, which demanded an alphabetic final label and
so rejected internal hosts like gitlab.internal.k8s2 that the https path accepts.

Rewrite the scp form into an ssh:// url purely to validate it, reuse the https host
and credential checks through a shared isSafeHost, and store exactly what the user
typed. Only a url that survives the round trip unchanged is accepted, which is what
keeps traversal segments out of the feed, so the two ssh regexes, the dots-only
guard and the clone-url builders all collapse into one function.
2026-09-16 11:29:25 -07:00
yassin
6cf35ed71b feat(proxy): expose lifetime total_spend on virtual keys
Adds a persistent total_spend column to LiteLLM_VerificationToken and LiteLLM_DeletedVerificationToken, incremented in the same write as spend and left alone by budget resets. Surfaces it on /key/info, /key/list and the Admin UI Virtual Keys table and key detail view

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-16 09:42:20 +00:00
Yuneng Jiang
d67c7894dd
fix(ui): keep recorded order when an untimed guardrail shares a phase 2026-09-15 22:25:30 -07:00
Yuneng Jiang
8bb496154a
test(ui): scope lifecycle assertions with within instead of parentElement
The four .parentElement reads in the new lifecycle tests pushed
testing-library/no-node-access to 712 against a 707 budget, failing
frontend-lint. The rows now carry data-testid="lifecycle-row" and the test
picks a row with within(), which keeps the assertion tied to the specific row
rather than the whole panel and takes the count back to 707.
2026-09-15 22:18:42 -07:00
Yuneng Jiang
d20469b0dd
Merge remote-tracking branch 'origin/main' into litellm_fix_guardrail_lifecycle_untimed_entries 2026-09-15 21:44:22 -07:00
Yuneng Jiang
51a243e3ce
fix(ui): keep untimed guardrail entries on the request lifecycle
#39050 changed RequestLifecycle from sorting every entry with
(a.start_time ?? 0) to filtering on isTimed, which drops any entry whose
start_time/end_time are null. That was the right call for the not_run entries
the PR introduced, but it also drops entries that DID run and simply carry no
timing, and those are pre-existing: add_standard_logging_guardrail_information_to_request_data
defaults start_time, end_time and duration to None, and the conduct guardrail
passes none of them. One such entry used to draw the whole four-row lifecycle
and now draws nothing, so an admin opening that log sees an empty
Request Lifecycle panel.

An entry now stays on the lifecycle when it is timed OR when it ran, so not_run
keeps the exclusion #39050 wanted and every other shape comes back. Offsets are
number | null and render as an em dash rather than a fabricated T+0ms, which is
what a null minus a null used to produce on the base. Entries without timing
sort after the timed ones and the base time comes from the timed entries, so
real offsets are unchanged.

The two new tests fail on the base component and pass here; #39050's own
not_run tests keep passing untouched, which is what makes this additive rather
than a revert.
2026-09-15 21:44:19 -07:00
Tin Chi Lo
9d640b86ed fix(ui): simplify Capability and Fuse routing options 2026-09-15 20:58:38 -07:00
tin-berri
3b14631f06
Merge pull request #41315 from BerriAI/litellm_capability_fuse_classifier_ui
feat(ui): configure capability and Fuse v2 classifiers
2026-09-15 20:13:57 -07:00
Tin Chi Lo
39b7812810 feat(ui): configure capability and Fuse v2 classifiers 2026-09-15 19:30:50 -07:00
yucheng-berri
41eb2dbfeb
Merge pull request #41128 from BerriAI/litellm_llm_judge_pre_call
feat(guardrails): support pre_call and during_call modes for llm_as_a_judge
2026-09-15 18:58:21 -07:00
Yassin Kortam
67cb0bc089
Merge pull request #41313 from BerriAI/litellm_model_activity_response_time
feat(ui): show average response time per model in usage model activity
2026-09-15 18:43:48 -07:00