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(ui): stop a deselected MCP server keeping its grant on a virtual key
The key editor sent `mcp_tool_permissions` unfiltered, and the MCP resolver
counts a server named only under `mcp_tool_permissions` as entitled, unioning
`tool_perm_servers` into `all_servers` at four sites in
`user_api_key_auth_mcp.py`. Deselecting a server, or removing the access group
that supplied it, therefore left a stale entry that kept the key reaching that
server with its old tool allowlist attached.
Reuse `extractMcpEntitlement`, which already landed for the internal-user
surface, so the key surface drops an entry only once the server is known and no
longer granted, and keeps it whenever a retained access group or toolset could
still supply it. The helper moves to a shared module so the key template does
not import a users page component.
Setting the map unconditionally is part of the same fix: the old
`Object.keys(...).length > 0` guard let the previous map ride through the
`object_permission` spread, which filtering to an empty map would otherwise hit
in exactly the case the fix is for.
* fix(ui): resolve retained MCP groups and toolsets per server when pruning tool permissions
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* test(ui): mock the MCP toolsets hook in the key update suite
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>
/ui/chat never rendered a metrics bar. The Responses helper already parses
usage off the response.completed event, but the chat page passed positional
undefined where onTimingData, onUsageData and onTotalLatency sit, ChatMessage
had nowhere to hold them, and ChatMessages never rendered ResponseMetrics.
Thread the three callbacks through, persist the values on the assistant
message, and reuse the playground's ResponseMetrics to show latency, TTFT,
input/output/total tokens and cost. Also map the cost the proxy reports on the
streamed usage object, which only the chat-completions helper did before.
Two behaviour differences the port introduced, both found in browser QA
The antd Select matched a prebuilt pattern on its display_name and its
internal name; a Base UI Combobox only searches itemToStringLabel, so
queries like "amex" and "sg" stopped matching. Restore the second field
with a filter predicate on the Root, covered by a regression test that
searches on a token the visible label does not contain
Base UI portals a popup into a positioner whose "isolate z-50" is fixed
in the primitive, so inside an antd Modal at z-index 1000 the options
were visible but not clickable. antd hid this because its own dropdowns
and tooltips already sat above its Modal. The positioner is not reachable
from the call site, so this needs one app-wide rule keyed on an antd
modal being present, and it becomes deletable when the last one goes
The chat metrics bar reported In/Out/Reasoning/Total/cost only, so a
playground user had no signal that provider prompt caching worked. The
cached-token counts were already visible in the Logs drawer, which meant
the answer to "does caching work here" lived on a different page.
Adds cacheReadTokens and cacheCreationTokens to TokenUsage and renders
them as two chips, reusing the prompt-cache tooltip wording already
introduced for the Logs drawer so both surfaces say the same thing.
A single helper, extractPromptCacheTokens, normalizes the three usage
shapes the playground consumes: Anthropic Messages
(cache_read_input_tokens / cache_creation_input_tokens), chat
completions (prompt_tokens_details) and the Responses API
(input_tokens_details). All three producers call it instead of parsing
per surface. Counts that are absent, zero or non-finite are dropped, so
providers without prompt caching render exactly what they render today.
The repository's comment policy keeps reasoning out of the source, so the
notes on the delete dialog, the tooltip wrapper, and the test query choices
move to the pull request instead.
The assertion pinned the Tailwind track string, which jsdom can never
evaluate: it does no layout, so the test could not fail for the reason
that matters, and its pattern accepted any minmax minimum, staying green
if that minimum changed enough to break the layout outright.
Only a viewport-resize browser test can observe this, which belongs in
tests/e2e/ui rather than a route's unit tests. The responsive behaviour
itself is unchanged; the widths it reproduces are recorded in b341a22339
Replaces antd and Tremor with shadcn primitives across the six route-owned,
form-free playground components: the compare view and its panel, message input
and unified selector, plus the realtime playground and the agent builder.
Markup only, no behaviour change. The characterisation tests added in the
previous commit are untouched here and stay green through the swap.
Adds ui/slider.tsx via the shadcn CLI and retires the six antd
no-restricted-imports suppressions the migration made obsolete.
The antd Row and Col the migration replaced never squeezed the summary
cards below their content width; they wrapped onto a second line instead.
Measured on a live dashboard, antd laid out five per row at 1280 and 1024,
then four plus one at 900 and three plus two at 820, never narrower than
about 126px. A fixed grid-cols-5 kept all five on one line and compressed
them to 86px at 820, so the metric values overflowed their cards.
An auto-fit track with a 7rem minimum reproduces antd's wrap points and
card widths exactly at all four measured viewports, and is identical at
1280, so the route's visual baseline is unchanged.
The added test fails against grid-cols-5 and passes against the track
Rewrite the playground tests that reached for antd class names so they
locate controls by role, text, placeholder or lucide icon instead. Add
characterisation suites for AgentBuilderView and RealtimePlayground,
which had none, including tab state that must survive a round trip
through another tab.
Every assertion here passes against the current antd components.
Replaces the antd Button, Row, Col, Spin and Typography usage in
GuardrailsOverview with the shadcn button, a CSS grid and the house
loading wrapper, and swaps the ant-design icons for lucide equivalents
that the already-migrated GuardrailDetail sibling uses. The busy state
keeps the aria-busy attribute the antd Spin exposed, so the loading
contract is unchanged for assistive tech.
The route's other analyzer-listed file, GuardrailsMonitorView, keeps its
DateRangePickerValue type import because the shared AdvancedDatePicker
prop contract requires it, so there is nothing to migrate there.
Retires the file's now unused no-restricted-imports suppression
Ports the 21 route-owned guardrails components off antd and Tremor onto the
installed shadcn base-vega primitives. Forms, tables and shared components stay
where they are: the route analyzer buckets add_guardrail_form, guardrail_info
and six others as DEFERRED, and eleven components under src/components as
SHARED, so none of them are touched here.
Three behaviours needed explicit handling because Base UI does not match antd by
default. Base UI Tabs mounts only the active panel, so the playground draft and
every panel's local state would reset on a tab switch; TabsContent now carries
keepMounted. ComboboxList takes a function child to render the filtered items,
and mapping over the array instead renders everything and silently disables the
type to filter behaviour antd gave for free. SelectContent gets
alignItemWithTrigger={false} so popups anchor below the trigger the way antd's
did, which also removes a focus race that made the template picker
intermittently unclickable.
The three dialogs reached from inside the still-antd wizard need a layer above
its z-index of 1000 to be visible at all.
Adds three tests rather than editing the characterisation tests from the
previous commit: a guard that every tab panel stays mounted, and one filtering
regression each for the pattern and mode dropdowns.
Prunes the guardrails entries from eslint-suppressions.json.
Replaces antd and Tremor markup on the usage route with the installed shadcn
base-vega primitives. Behaviour is unchanged: the route's characterisation
tests were rewritten to role and text queries in the previous commit, proven
green against the antd components, and pass through this commit unedited.
Tremor tab panels stayed mounted once rendered, so every migrated TabsContent
and the collapsible model sections in activity_metrics carry keepMounted to
keep view-mode and expansion state alive across tab switches.
* chore(ui): remove Agent Platform announcement bell from navbar
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat(ui): highlight Auto Router in the navbar announcement
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix(ui): open Auto Router docs link in a new tab
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* Revert "fix(ui): open Auto Router docs link in a new tab"
This reverts commit 3be861bafe.
* chore(ui): title the navbar announcement LiteLLM Auto Router
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
---------
Co-authored-by: Mubashir Osmani <mubashir@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Covers the header and export action, all five summary metric cards, the
table toolbar heading, the evaluation settings modal wiring, the busy
state and the request failure message. Every assertion is role, title or
text based so it holds against both the antd markup and its shadcn
replacement, letting the migration commit land without editing this file
Rewrites the usage route's library-coupled assertions to role and text
queries so they characterise behaviour rather than the widget library that
happens to render it. Every test here is green against the current antd and
Tremor components and is meant to survive a migration unedited.
Drops the wholesale vi.mock of antd, @ant-design/icons and @tremor/react in
UsagePageView, UsageViewSelect and activity_metrics, and drives the real
controls instead. Two of those mocks were hiding behaviour: activity_metrics
asserted panel order through a heading role that only existed because the
mock faked an h2, and the Tremor tab stubs flattened panel selection away
entirely.
Replaces DOM-shape lookups with anchors that do not move: a chart is found
from its own heading rather than a fixed wrapper depth, an active panel is
identified by the inactive markers both tab libraries set, and select options
are matched by text since antd's real options carry no option role.
Adds the missing characterisation test for team_multi_select, and covers the
mount contracts the route depends on: Tremor keeps every tab panel mounted,
and antd Collapse keeps a section mounted once it has been expanded, which is
what preserves the view-mode state a model section owns.
Rewrite the PatternModal test off antd class selectors onto role and text
queries, and add characterisation tests for the guardrails components that
had none: the keyword modal, the content filter display and configuration,
the guardrail garden and the custom code modal.
Also covers two behaviours the migration must preserve: the action and
severity dropdowns in the content filter tables, and test playground state
surviving a tab switch away and back.
Every assertion here passes against the current antd and Tremor components
so the same file can prove the shadcn versions unedited.
Prettier flagged the two test files added while fixing review findings.
Migrating these components off Ant Design also retired the lint suppressions
they carried, so prune those 15 entries and leave the unrelated ones for the
PRs that made them stale.
antd's Collapse kept the panel mounted once opened, so a tool a user had
expanded stayed expanded after closing and reopening Tools. Base UI renders
only the open branch, so the migration silently reset every ToolItem.
The regression test passes against the antd original, fails against the
migration without keepMounted, and passes with it.
InputCard and OutputCard located SectionHeader's copy button by querying for a
descendant with aria-label="copy", which is the antd CopyOutlined icon. That
selector reaches into SectionHeader's internals, so migrating it off antd left
copyButton undefined and failed four tests.
getByRole("button", { name: /copy/i }) is green against both the antd and the
shadcn SectionHeader, verified by running these two files against each.