The discount and margin removal confirmation used AlertDialogAction, which
renders AlertDialogPrimitive.Close and dismisses the dialog on click. The
dialog therefore disappeared while the removal request was still in flight,
leaving the admin with no sign that anything happened and free to fire a
duplicate removal.
Swap the confirm control for a plain destructive Button, track an isRemoving
pending state that disables Cancel and relabels Remove to "Removing...", and
clear the pending removal in a finally block once the request settles.
* feat(proxy): serve Anthropic-native /v1/models for Claude Code gateway discovery
* refactor(proxy): move Anthropic model-list formatter into llms/anthropic/common_utils
* fix(proxy): make model_list request param optional for direct callers
* style: apply ruff format to changed lines
* style: satisfy ruff strict-rule budget (UP006, I001)
* style: satisfy type-discipline budget (LIT002 mutable-ok, LIT009 pyright ignore)
* style: satisfy LIT001/LIT010 and drop explanatory comment per contributor rules
* fix(proxy): translate team model names in the Anthropic /v1/models response
* ci: trigger buildkite status report
* feat(proxy): carry token limits into the Anthropic-native /v1/models entries
* fix(proxy): cast the injected request so the anthropic-version guard is a real comparison
* fix(proxy): explain the model listing casts so the type-discipline gate passes
---------
Co-authored-by: yuneng-jiang <yuneng@berri.ai>
Co-authored-by: Yassin Kortam <yassin@berri.ai>
Rebuilds the provider discount and margin tables, the pricing calculator and
its multi-cost results on the in-repo shadcn layer, and swaps the imperative
antd modal.confirm removals for AlertDialog. Row actions gained accessible
names, which replace the Tremor stub mocks the tests used to drive.
cost_tracking_settings keeps its two antd Modals and Forms, since they wrap
the two add forms that stay on antd for now.
Rebuilds settings.tsx and bulk_create_users_button.tsx on the in-repo shadcn
layer. The settings callback form moves from antd Form to react-hook-form with
the shared Field primitives, and the CSV drop zone replaces antd Upload with a
native file input plus drag handlers. Both public prop signatures are
unchanged, so no caller moves.
The invitation dialog opens over the still-antd Invite User modal. Lifting
only the shadcn dialog content above antd's mask leaves its own backdrop
underneath, so an outside click reaches the wrong modal. Adding a second
backdrop stops that but does not restore dismissal, and the same hazard
already ships in three guardrails modals, so the stacking needs one shared
fix rather than a fourth local workaround.
Rebuilds nine components under src/components on the in-repo shadcn layer:
both banners, the navbar chrome, the onboarding link dialog, the model
filters, the model group alias table, the object permissions and logging
settings views, and the user dashboard grid. Every public prop signature is
unchanged, so no caller moves.
DeleteResourceModal let escape, the backdrop and the close button dismiss it
while the delete request was still in flight. SearchSelect blanked its field
whenever the value was missing from options, which happens while they load;
it now falls back to the raw value the way PaginatedSearchSelect already did.
The mock returned only CopyIcon and CheckIcon, so any icon a child later
imports resolves to undefined. DeleteResourceModal now renders CircleAlert,
which broke all twelve cases in this file.
The rewritten suite only ever picked one ordinary model, so a
regression that replaced the selection instead of appending to it
would have gone unnoticed. The case passes against the antd version
too, so it pins behavior the migration preserves rather than adds.
Rebuilds the thirteen form-free components under common_components on
the in-repo shadcn layer, so they inherit the dashboard's design tokens
instead of styling themselves through Ant Design and Tremor.
SearchSelect and the three dropdowns that wrap it now forward an
optional input id, so an antd Form.Item label still resolves to its
control. The e2e steps that reached into antd's Select and Modal
internals now go through the test id, role and data-slot.
extraColumns was typed as antd's ColumnsType while the adapter only
honoured string/ReactNode titles, plain-string dataIndex values and
element/string/number render results, so several valid antd column
forms produced blank cells. MemberTableColumn now describes exactly
what the table renders, and a column with a dataIndex but no render
falls back to the member value instead of rendering nothing.
Rebuilds public_model_hub, MakeSkillPublicForm, ModelSelect and the
guardrail LogViewer on the in-repo shadcn layer, so they inherit the
dashboard's design tokens instead of styling themselves through Ant
Design and Tremor.
Public prop signatures are unchanged, so no caller moves. The two
teams e2e steps that reached into antd's Select internals now drive
the combobox through its test id, role and data-slot instead.
Replaces Ant Design and Tremor in the fallbacks views, the router general
settings panel, and the two shared banner and badge components.
- Tremor Card, Table and Icon become the ui/card, ui/table and lucide
equivalents, reproducing Tremor's icon box so click targets keep their size
- antd Alert becomes a composed role="alert" region, since the shadcn CLI's
alert pulls in class-variance-authority, which this repo does not have
- antd InputNumber becomes a native number input, and Switch onChange becomes
onCheckedChange
- shadcn TableCell ships whitespace-nowrap where Tremor's did not, so cells
holding model names and setting descriptions get whitespace-normal back
- adds a DeprecationBanner test covering naming, the link, and dismissal,
proven against the antd version first and mutation checked
- drops the eslint suppressions these files no longer need
Replaces Ant Design and Tremor in the key info header and detail view, the
agent and vector store permission panels, and the team member permissions
table.
- antd Popover, Dropdown and Modal become HoverCard, DropdownMenu and Dialog,
and Tremor TabGroup becomes Tabs with keepMounted so panel state survives
a tab switch the way Tremor's did
- the key id copy control moves to the shared CopyButton, which also fixes an
icon that rendered at 24px because it inherited the heading font size
- antd Checkbox onChange becomes onCheckedChange
- every public prop signature is unchanged, since these are shared views
- three member permission tests were passing vacuously: they searched for an
unchecked box by reading .checked, which is undefined on a Base UI checkbox,
so the assertions sat inside an if that never ran. They now scope the
checkbox to its own row and assert the toggle, the save and the revert
- drops the eslint suppressions these files no longer need
The migration closed a double submit hole that antd left open, but the
rewritten tests only proved the flow had not completed, so removing the
guard would not have failed them. Verified by mutation: dropping
disabled={loading} fails exactly this case.
Replaces Ant Design and Tremor in the six shared components under
src/components/common_components, which between them are reached by
nine routes.
- antd Table becomes the ui/table primitives, and the Actions column keeps
antd's fixed: "right" behaviour via a sticky cell
- Tremor Icon, Text and Badge become a plain span, p and StatusBadge
- antd Tooltip and Typography copyable become the shadcn Tooltip and the
shared CopyButton
- every public prop signature is unchanged, since these are shared components
and a renamed prop would break callers far from this folder
- two tests dropped assertions on antd internal class names and on DOM
structure, and gained cases proving a disabled action does not fire onClick
MemberTable keeps a type-only import of antd's ColumnsType because a consumer
annotates its own column array with it. No antd code ships from the file.
Screen readers announced an unnamed dialog. The visible header is a custom
layout, so the title is visually hidden to keep the drawer layout unchanged.
Replaces Ant Design and Tremor across src/components/AIHub, so the model,
agent, MCP and skill hub views compose @/components/ui primitives.
- Modal becomes Dialog, tremor TabGroup becomes Tabs, tremor Card and Table
become their shadcn counterparts, and Tag and tremor Badge become Badge
- the three publish forms wrapped antd Form around zero Form.Item fields, so
the wrapper became a div and the dead useForm and resetFields calls went
with it, rather than pulling in react-hook-form for a form with no fields
- antd Steps has no shadcn equivalent, so each form inlines a small ol stepper
- cells holding model names, server ids and URLs gained min-w-0 and break-words
so a long value cannot bleed into the neighbouring column
- the three form tests dropped assertions invented by their antd mocks in
favour of roles and rendered text
- drops the eslint suppressions these files no longer need
Replaces Ant Design across every source file under src/components/view_logs,
so the request log drawer and its viewers compose @/components/ui primitives.
- Drawer becomes Sheet, Collapse becomes Collapsible, Segmented and Radio.Group
become Tabs, Tag becomes Badge, Descriptions becomes a local grid helper
- every lucide icon carries an explicit size class, since antd icons render at
1em while lucide defaults to 24px
- two tests dropped assertions on antd internal class names in favour of
rendered text and roles, and the Pretty/JSON case now proves the toggle
actually swaps the body rather than only that both controls render
- drops the eslint suppressions these files no longer need
Replaces Ant Design with the in-repo shadcn layer across every Navbar
component, removing the last antd imports from src/components/Navbar.
- CommunityEngagementButtons, NotificationsBell, ViewSwitcher, BlogDropdown,
WorkerDropdown and UserDropdown now compose @/components/ui primitives
- antd icons render at 1em while lucide defaults to 24px, so every icon
carries an explicit size class matching what it replaced
- UserDropdown uses Popover rather than DropdownMenu: its panel holds
switches and badges, and form controls inside role="menu" are invalid
- WorkerDropdown moves to Combobox since shadcn Select has no search
- drops the nine no-restricted-imports suppressions these files no longer need
Request metadata carries the whole UserAPIKeyAuth object, whose team_metadata
holds the customer's own langfuse callback_vars. The only filter on the emitted
blob was a four key deny list written as a circular reference crash guard, so
those credentials reached the customer's own langfuse traces.
The emitted blob is now the StandardLoggingPayload allowlist plus the litellm
computed enrichments, and nothing is copied across from raw request metadata.
That makes the credential exclusion structural rather than a filter someone has
to keep correct. Steering keys keep reading raw metadata, matching literal_ai.
Proxy callers are unaffected: their request metadata already rides under the
allowlisted requester_metadata key, nesting intact.
debug_langfuse dumped raw request metadata into the trace as a second copy of
the same leak. It now emits caller scalars only.
When StandardLoggingPayload is absent the trace is still emitted with the
existing trace_id fallback, so failure traces survive.
Both are covered on GitHub Actions. test-litellm-ui-build.yml runs the
dashboard build on every PR, and test-litellm-ui-unit.yml runs the vitest
suite with ui-unit-tests already a required check, so neither CircleCI job
gates anything that GHA does not already gate.
ui_build additionally produced nothing anyone consumed. It persisted
litellm/proxy/_experimental/out to the workspace, and the only job
downstream of it was ui_unit_tests, which never attached the workspace and
reinstalled from source instead. The requires edge was pure sequencing, so
the build output was written and discarded on every client-touching PR.
One real narrowing comes with this, and it is deliberate. ui_unit_tests ran
the full vitest suite on PRs, while the GHA job scopes PR runs to tests
reachable from the diff and keeps the full suite on pushes to staging. That
split was a measured decision in #34175 and it still holds: the suite is
252s and 248s of that is CreateMCPServer.integration.test.tsx alone, so
running everything per PR buys about four minutes to re-run one file.
Note that assert-ci-coverage does not speak to this. It walks
tests/**/test_*.py only, so it is blind to vitest files by construction;
it stays green here because no Python test lost a runner, which is a
narrower claim than the UI side being unaffected.
auth_ui_unit_tests is a different job, a Python suite on a Postgres
sidecar, and is untouched
* fix(proxy): track spend for OpenAI passthrough /v1/embeddings
OpenAI passthrough embeddings returned 200 but wrote no spend because the
route was unsupported and Cohere's /v1/embed prefix stole the match.
* fix(proxy): clear embeddings lint and Greptile comment nits
Inline embeddings cost tracking to avoid new LIT001/002 hits, trim
redundant doc comments, and cover the Cohere /v1/embeddings collision.
* fix(proxy): drop unreachable embeddings TypeError guard
convert_to_model_response_object with response_type=embedding already
returns EmbeddingResponse; the isinstance check was dead patch coverage.
A deployment with PTU flat-cost attribution also billed every request per
token, so a team paid for reserved capacity and again for the traffic that
capacity serves. Nothing set the per-token price and an unset price falls
back to the public cost map, which made the double charge the default.
/model/new and /model/{id}/update now store zero for every pricing field the
cost map could otherwise fill, refuse a price the caller supplies alongside
PTU config with a 400 naming the field, zero a price already on the row
rather than rejecting later edits of unrelated fields, and drop the zeros
again when the PTU config goes.
A PTU deployment is no longer read as a free model by the budget checks,
which would have waived every budget for it.
* fix(mcp): expose client HTTP headers to logging callbacks and hooks
MCP protocol tool calls built a synthetic Request with only content-type, so metadata.headers reaching logging callbacks and guardrails was empty while /mcp-rest/tools/call exposed the full set. Rebuild the synthetic request from the connection's raw headers (shared with the sampling path), and pass sanitized headers to the pre-call hook, the MCP to LLM guardrail bridge and the Responses API MCP bridge. Credential headers stay masked and proxy key headers stripped.
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix(mcp): strip custom proxy key and upstream MCP credential headers from logging copies
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* refactor(mcp): make client side auth header name accessor public
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix(mcp): strip custom proxy key and client redaction opt-out from mcp headers
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix(mcp): drop custom proxy key header in the synthetic request builder
Strips general_settings.litellm_key_header_name in build_synthetic_mcp_request so every caller, including sampling, is covered, and reverts passing general_settings into add_litellm_data_to_request on the tool call path since that also switches on enforced_params.
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>
Co-authored-by: shivam <shivam@berri.ai>