mirror of
https://github.com/BerriAI/litellm.git
synced 2026-11-08 08:08:37 +00:00
143 commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
22ad3c5fff
|
refactor(lens): connect LiteLLM to the independent Lens service (#45529)
* feat(lens): consume the independent Lens service and shared UI * fix(lens): keep embedded setup stable and vendor UI before builds * chore(lens): pin embedded UI provenance to published Lens source * fix(lens): complete embedded UI extraction across CI builds * docs(lens): record latest main and removal-boundary validation * fix(lens): wire external chart credentials and ingestion modes * test(lens): cover adapter failures and fix extraction CI * docs(lens): refresh bundled chart setup instructions * test(lens): restore gateway adapter test package marker * build(lens): pin clean-cut UI and supported storage chart * test(ui): await guardrail scope menu before checking options * perf(lens): omit unused trace-team lookup from product requests * docs(lens): record final-image adapter permission checks * test(lens): assert delegated investigation authorization * refactor(lens): remove copied trace runtime and preserve spend logging * test(lens): verify independent gateway image upgrades and outages * test(lens): pin the chart qualification database image * fix(lens): update embedded UI metadata filters * test: wait for Lens chart pod identity convergence * fix(e2e): select Lens chart pods by deployment ownership * fix(e2e): select Helm migration ownership for Lens upgrades * test(lens): retain migration Jobs through rollout assertions * docs(helm): require existing database for migration hooks * test(lens): qualify release boundaries with embedded navigation update * docs(lens): record browser qualification for embedded tabs * fix(lens): remove unrelated CI and guardrail test changes * test(lens): give qualification tenants unique key aliases * feat(lens): embed the latest canonical Lens interface * fix(lens): qualify split inference through the gateway * fix(lens): preserve scoped auth and isolate delegated credentials * chore(lens): remove unrelated documentation and lint changes * fix(lens): bound concurrent forwarding buffers through response delivery * fix(lens): preserve OAuth2 dispatch without inference policies * chore(lens): adopt current shared onboarding UI * fix(lens): refresh shared setup UI and review context * fix(lens): bound uploads before gateway authentication * docs(lens): remove extraction evidence from the gateway repo * docs(lens): refresh shared setup prompt for paired releases |
||
|
|
5b95142ef6
|
chore(ui): bump next to 16.3.8 (#45207) | ||
|
|
5147aefa8b
|
fix(lens): reuse trace reviews and consolidate findings across runs (#44778)
* fix(lens): reuse trace reviews and consolidate findings across runs * chore: sync schema.prisma copies from root * fix(lens): preserve partial reviews and bound budget admission * fix(lens): resolve CI regressions and clarify reused runs * test(lens): exercise review reuse in worker container smoke * fix(lens): show actual reviews when a run finishes * fix(lens): distinguish reuse plans and stop blocked scans * fix(lens): retain partial findings when model requests stop * docs(lens): clarify budget edits during active runs * fix(lens): publish findings only after reconciliation completes * fix(lens): guide insufficient-budget runs to budget settings * fix(lens): renew budget holds and bound admission waits * fix(lens): preserve provider errors during budget cleanup * fix(ci): update PgBouncer and sharp for current builds * fix(lens): serialize settlement and fence review checkpoints * fix(lens): defer generated Prisma client type import * test(lens): verify checkpoint and progress rollback together --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
fe24be2e3d
|
fix(lens): make tool steps and conversations readable (#44645)
* feat(lens): add per-trace review models to jobs and progress
* feat(lens): append worker reviews to the job, capped, and count every review
* feat(lens): report a review with reasoning for each screened trace
* chore(ui): regenerate api types for lens job reviews
* feat(lens): type job reviews and fill them in lens fixtures
* feat(lens): add live review playback model
* feat(lens): pick the analysis model and slow single-review pacing
* feat(lens): add sample reviews for previewing the live run
* feat(lens): add live run layout with queue, reading trace and conclusions
* feat(lens): show the live run on investigations and open it from run now
* feat(lens): stream large review backlogs at 150ms or less and list newest first
* fix(lens): show the live run only for real reviews and keep fixtures test-only
* refactor(lens): restyle the live run as the native progress panel
* fix(lens): retry contended investigation updates with jittered backoff
* feat(lens): add a reading ticker line and replay for finished runs
* feat(lens): collapse the live run to an ambient line with show work
* fix(ui): crop the cerebras logo viewBox to its mark so it reads at icon size
* feat(lens): format review span previews as readable messages
* feat(lens): derive strip status, honest issue counts and drawer focus from a job
* feat(lens): track active jobs before their first review
* feat(lens): add a live trace results drawer with readable spans
* feat(lens): put the live strip under the progress bar and drop the inline panel
* feat(lens): add an ambient live strip that opens the drawer
* fix(lens): wait out provider rate limits and retry model calls four times
* style(lens): format repository contention tests
* feat(lens): read review spans as a conversation timeline
Turns spans into the user's ask, tool calls with args and results, and the agent's reply, dropping system prompts. Also handles a preview cut that lands inside the Output header.
* fix(lens): list recorded agents in the run now dialog
The run now agent field used a native datalist, whose suggestions do not show inside the modal dialog, so the agent list looked empty even though /lens/agents returned names. Use the same Combobox as investigation setup.
* feat(lens): pace live playback so each trace stays readable
Every trace now stays up for at least 1.5s. A backlog is cleared by skipping to the newest few instead of flickering through them. Conclusions count traces per check and kind, and new helpers cover share bars, group filters and flashes.
* feat(lens): keep the live run ambient until View run is clicked
The drawer no longer opens on Run or when entering a running investigation. LiveRun takes reviews as a prop so it can move to a dedicated reviews endpoint.
* feat(lens): show the live run as a two-pane trace and conclusions view
Left pane: the trace being reviewed as a readable timeline, followed by Lens's reasoning and the verdict. Right pane: ranked conclusion groups with share bars, plus a trace list you can filter.
* fix(lens): run several investigations per worker and poll every two seconds
* feat(lens): add worker slot and poll interval settings
* feat(lens): add list summaries and an incremental review filter
* perf(lens): strip reviews and run attributes from the lens list and serve reviews separately
* test(lens): cover list summaries, review polling and review access
* feat(lens): explain why a queued investigation is waiting
Works out whether no worker is connected, the worker is busy (with its running investigations and an estimated start time), or it is just being picked up.
* feat(lens): show the queue reason and what the worker is doing in the live strip
The progress header and the strip replace "Queued for your worker" with the concrete reason. While waiting, the strip lists the busy worker's investigations; click one to open it.
* feat(lens): add a review page model carrying the total reviewed count
* fix(lens): page live reviews by index so out-of-order reviews are never skipped
* feat(lens): take an index cursor on the reviews endpoint
* test(lens): cover index cursors across out-of-order and rolled-over reviews
* chore(ui): regenerate api types for the lens reviews endpoint
* feat(lens): page job reviews by index cursor
Adds api.reviews for GET /lens/{id}/runs/{job}/reviews?after=N, with a demo implementation. appendPage adds pages in arrival order and keeps the latest 200. liveJob now keys off reviewed, since the list no longer carries reviews.
* feat(ui): add a lens reviews query that polls the index cursor while live
* fix(lens): feed the live run from the reviews endpoint and keep View run open
LiveRun now gets its reviews from useJobReviews instead of the list, which no longer carries them. View run stays clickable while a run is queued or running, and before the first trace the opened view says what the worker is doing.
* fix(lens): split live conclusions into issues and patterns
A check could show up twice with the same label, once as an issue and once as a pattern.
* fix(lens): group live conclusions by check with short labels
There is now one group per check_id: issue traces are the main count and pattern traces a secondary note, so there are no duplicate red and grey cards. A long instruction falls back to the humanized check id. Adds briefReasoning and traceRows for the simplified trace list, and drops helpers nothing uses.
* feat(lens): simplify View run to traces and conclusions
The left pane is the trace list. A soft highlighter carrying the provider and model slides to the trace being reviewed, and clicking a row shows just Lens's reasoning and verdicts. The right pane keeps one conclusion card per check.
* refactor(lens): drop client-side replay in favour of real in-flight rows
Removes the playback reducer and its pacing. liveRows lists the traces the worker is reading, from job.reading, followed by completed reviews newest first, keyed by execution_id so a trace keeps its row when it finishes.
* feat(lens): show what the worker is reading and make View run obvious
Each trace in flight gets a highlighted row with the model and a live timer, and becomes its completed row in place. Completed rows show the real review time. View run is an outline button next to the progress line, and clicking anywhere on the strip opens it too.
* feat(lens): sum up a finished live run with time taken
doneLine reads like "Reviewed 30 traces in 31s with", measured from when reading started.
* feat(lens): slide one model rectangle over the traces being read
A single rounded rectangle carrying the provider logo and model wraps the real in-flight rows from job.reading. It translates and resizes over 250ms as traces finish in place. Before job.reading arrives it sits on a top slot showing the honest progress line, and when the run completes it fades out over 400ms. Rows have a fixed height and stable execution_id keys, so polls don't cause jumps or flicker.
* feat(lens): add in-flight runs to jobs and worker progress
* feat(lens): store in-flight runs from progress and clear them when a job ends
* refactor(lens): route progress, cancel and results through shared job transitions
* feat(lens): report each run as in flight when its review starts
* feat(lens): send in-flight runs with worker progress
* test(lens): cover in-flight runs across progress, old workers and terminal states
* test(lens): cover in-flight reporting under original run ids
* chore(ui): regenerate api types for lens in-flight runs
* feat(lens): model live reading lanes from in-flight runs and reviews
* feat(lens): show a now reading stage that types each trace's reasoning
* feat(lens): put the now reading stage above the trace list in View run
* fix(lens): resolve the analysis provider logo from the model catalog
* fix(lens): give demo jobs an empty in-flight list
* style(lens): format endpoint tests
* refactor(lens): name the run now handler in investigations view
* refactor(lens): name now reading conditions
* refactor(lens): name inline objects in the live run
* style(lens): format live run files
* fix(lens): keep worker settings inside the standalone worker package
* refactor(lens): keep update retry settings next to the repository
* fix(lens): start review history over when a run is reclaimed
* chore(lens): drop the unused review fixture
* refactor(lens): remove dead live helpers and use generated in-flight types
* fix(lens): keep polling a finished run until its last reviews arrive
* perf(lens): tick fast only while reasoning is typing
* fix(lens): isolate retried reviews and finding identities
* fix(lens): space the model name in run summary
* Update review.md
* fix(lens): make tool steps and conversations readable
* fix(lens): address trace rendering review and test failures
* fix(lens): preserve conversations with incomplete tool calls
* test(lens): retain failed tool styling coverage
* fix(lens): keep tool metadata in accessible result groups
* refactor(lens): build stable agent labels without mutation
* perf(lens): group and sort agent labels without repeated scans
* test(lens): await trace status filter option
---------
Co-authored-by: Ishaan Jaff <ishaan@berri.ai>
|
||
|
|
461a58c40a
|
refactor(lens): storage-independent trace reads, shared keyset pager, typed read failures (#44422)
* feat(lens): own trace reads behind a cached TraceStore port Move storage-independent trace reads into litellm-traces-cache behind a TraceStore port that ClickHouse implements. One keyset pager drives the span, list span and spend reads, and a run list batch reads spend once. Trace opens, pages and list summaries share one resolved read per trace in an in-process cache with single-flight loading. Live traces and reads with unknown spend expire after 5s, quiet traces after 10 minutes, failed reads are never cached, and the accepted list page size is remembered per scope. Trace read failures map to their own status and code (400, 409, 413, 503 with Retry-After), and the trace drawer retries temporary failures while offering only a refresh for changed or oversized traces. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * perf(lens): seed large profiles with server-side copies and long sessions Replay one copy through the proxy, then copy it inside ClickHouse and PostgreSQL with INSERT ... SELECT, rewriting trace, span and call IDs so every copy keeps its own spend. Add three long single-trace sessions for drawer paging and the oversized read path Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chores * style(lens): float the investigation setup badge on the tab edge Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(lens): restructure the trace drawer and polish its layout Split the 547-line TraceDrawer into run/, tree/, span/, content/ and conversation/ modules. Step rows now sit on one line with colored span family tiles, and the per-row timing bar moved into an optional Waterfall layout with a time axis. The steps and details panes are separated by the shadcn Resizable handle, with the split remembered per orientation. Span payloads go through one pure classifier (payloadView) that picks messages, a tool result, a nested field tree or text. JSON-encoded field values unfold into a tree, prose renders as markdown, repr and tracebacks stay monospace, and every section offers a Raw view. LangChain's serialized messages now render as conversation cards. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * typesafety * wip * fmt --------- Co-authored-by: Yujong Lee <yujong@berri.ai> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
9a5e828310
|
feat(ui): inline Lens settings tab and investigation editor (#44479)
* feat(ui): move worker status into the Lens notch and New investigation into the list toolbar Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * feat(ui): make the Lens notch entry a settings gear that houses the worker section Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * feat(ui): replace the Lens worker modal with an inline Settings tab Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * feat(ui): section the Lens settings tab with tracing status and worker cards Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix(ui): let Lens settings sections span the full card width Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * feat(ui): replace the investigation setup modal with an inline side-by-side editor New, edit, and duplicate now take over the Investigations tab body: matching activity on the left, every setting on the right, with no wizard steps or modal Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * feat(ui): step the inline investigation setup vertically with traces alongside Setup now sits on the left as three progressive steps (activity, criteria, run) that collapse to a summary once done and reopen on click. Matching activity stays on the right for every step. The editor gets a back control and the Investigations notch shows a New, Editing, or Duplicate badge while the editor is open Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * feat(ui): page the matching activity preview with useInfiniteQuery as it scrolls Replace the Previous/Next offset buttons with the same infinite query and near-tail prefetch the traces list uses, so the preview keeps loaded runs and its title while the next page arrives Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * refactor(ui): make the investigation step field map exhaustive over the form schema Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix(ui): always show the Settings tab label in the Lens mode switch Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * refactor(ui): collapse Lens worker cards into compact status rows Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix(ui): keep the Lens Settings tab icon-only in every state Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * wip * fix(ui): tick the Lens worker health dot so an expired heartbeat goes stale Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * refactor(ui): regroup Lens settings, model and api layers Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * refactor(ui): dedupe Lens formatting helpers Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix(ui): poll Lens once, drive the interval from data, settle mutations before invalidating Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * refactor(ui): let Lens leaves fetch their own data Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * refactor(ui): bind drawer and trace shortcuts through react-hotkeys-hook One useShortcut hook replaces the three hand-rolled keydown listeners in SidePanel, the trace step tree and the log drawer, with a layer option deciding which keys a pane claims from the panel around it. The span tree footer now renders ShortcutHints from what is actually bound instead of hand-typed kbd text. * refactor(ui): extract Inspector from SidePanel Inspector.Root owns the open item, J/K stepping, Escape and full screen; Inspector.Row marks a list entry with aria-selected and data-state and toggles it on click or Enter/Space; Inspector.Panel is the resizable side panel with the exit animation and click-outside rules. The runs table and section compose these parts directly, so RunDrawer and the SidePanel prop bag go away. * refactor(ui): model the Lens worker screen as a tagged union and slot in its ready action workerScreen() decides between list, form and install from the worker rows, the registration result and the edit target, so WorkerSettings switches on one value and each card owns its own copy. The post-install CTA is now a ReactNode slot that LensWorkspace fills instead of an onReady callback threaded through LensSettings and WorkerSettings. Clipboard copy state lives in WorkerInstall as mutations, and the styled settings leaves export Props types, set data-slot and accept native element props. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * refactor(ui): compose the Lens setup stepper from SetupStep children Each step's heading, summary and fields now live together in one SetupStep instead of four parallel structures keyed by index, and the last-step spacing comes from CSS rather than a passed index. The mode prop is now required since InvestigationsView always passes it. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix(ui): keep the Lens Settings panel mounted so a pending worker install survives tab switches The settings TabsContent unmounted WorkerSettings whenever another tab was active, dropping the one-time worker token shown during install. The panel now uses keepMounted, and the workspace test registers a worker, switches tabs and back, then follows the connected worker into the first investigation through the slotted CTA. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * test(ui): cover the Lens worker install waiting-to-connected transition Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * refactor(ui): give the Lens activity preview a grouped contract and a structural debounce useMatchingActivity now owns its return types (scope options, preview status, page and optional manual selection) instead of borrowing them from the components it feeds, and the preview takes those groups plus the section attributes. The clear-selection action moves into the preview footer, RunList becomes a RunRow leaf, and ScopeFields drops the unused nameField and id props now that MetadataFilters calls useId itself. The preview scope settles through a hashKey-based useDebouncedValue instead of JSON round-tripping into state, and the loading title follows isPlaceholderData since the query keeps previous data. RunFields and AnalysisModelField take the analysis models and the model gate as two objects instead of seven flat props. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * refactor(ui): select the Lens investigations screen with a pure tagged union investigationScreen maps the list query and the route to one of loading, failed, welcome, list, detail, setup or missing, so the view can switch instead of juggling mutually exclusive booleans. The status model gains activeJob, carries connected inside Readiness and folds the activity probe into one ActivityCheck value for the welcome page Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * test(ui): cover the Lens preview footer clear action and the preview debounce Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * refactor(ui): let Lens investigation leaves own their URL slice and express intent InvestigationsView renders the screen union and owns every write through useInvestigationActions, so leaves receive on* handlers instead of the API writer. useInvestigationResults becomes useRunSnapshot; FindingsTab, HistoryTab, RunPicker and RequestEvidenceSheet read their own nuqs slice and run their own queries. The finding sheet becomes an Inspector side panel (FindingDetails) keyed per finding, with the trace and request evidence sheets grouped in EvidenceSheets. WatchAllBanner owns its mutation, the welcome page takes the readiness and activity values, the progress sampler records on the wall clock outside render, and run history invalidates when the list reports a scheduler-started job Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * test(ui): cover Lens investigation intents, pause, cancel, history refresh and the finding panel Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix(ui): keep the inspector open behind sheet overlays and mount HotkeysProvider from a client wrapper * feat(ui): open Lens runs and evidence in the inspector panel A quote's original trace step or logged request now stacks inside the finding panel behind a back link, keeping the finding and its feedback draft mounted. The detail Runs tab and the setup activity preview open runs in the same panel with J/K stepping, so the TraceSheet and RequestEvidenceSheet modals are gone. Picking a different finding or run clears any stacked evidence from the URL. * feat(ui): open Lens investigations in the inspector panel beside the list The investigations list stays on screen and a row opens its investigation in the side panel, so J/K walk investigations and their open findings in display order and the selected row carries the same highlight as runs. The panel body is the former detail page; findings and runs opened inside it nest their own inspector, which claims the keys from the one around it while open. Opening an investigation and peeking at a finding now replace each other in the URL. * fix(ui): run the Lens notch border along the tab pill and flag only a disconnected worker Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix(ui): show the shortcut hints in every inspector panel Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * refactor(ui): extract the Lens dot field into composable DotFieldRoot and DotFieldCanvas Move the dot grid model and canvas painter out of TracesTimeline into components/lens/dotField so other Lens surfaces can reuse it. The root owns layout and context; overlays compose as children. agoLabel moves to lens/model/format and the unused columnTop helper is dropped. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * refactor(ui): drop the manual refresh button from the runs time controls Live polls and range changes refetch, so the button only cleared the zoom, which Escape already does Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(ui): extract the Lens run search into a composable SearchBox primitive Move the query parser, glob matcher and autocomplete out of runSearch into components/lens/search, generic over a QueryLanguage (field specs plus what free text searches). SearchBox.Root owns the ProseMirror state, menu and keyboard; SearchBox.Input and SearchBox.Suggestions compose under it. Clause highlighting becomes a ProseMirror plugin built from the language. RunSearch now only declares the run fields and composes the parts, so the investigations tab can define its own language and reuse the same box. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix(ui): label the runs range by preset while Live and pin it once paused Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(ui): split the Lens search language from where its data lives QueryLanguage is now pure vocabulary (keys, groups, icons). Reading fields off loaded items moves to a ClientIndex consumed by a separate evaluator, and value suggestions come from an injectable ValueSource, so a server-backed runs list can plug in a facet lookup while the investigations tab keeps filtering in memory. The parsed query serializes to a typed SearchQuery (text terms plus eq/neq/glob/nglob filters) that the client evaluator consumes today and a server can consume later. The suggestion menu shows a loading row while a source is still answering. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * style(ui): format the Lens SearchBox and its test with the project prettier config Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * feat(ui): mirror the Lens run filters as trace SQL with a copyable curl in the search footer The suggestions footer gains a slot, and SearchBox.ApiHint fills it with the API equivalent of the typed query: a dialect chip, a one-line preview and a Copy as curl button. The runs box translates each filter to a predicate over the agent_traces_by_key rollup, bounded to the range the list shows, and copies a POST to /v1/traces/query. Any other list can plug its own translate into the same part. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * feat(ui): show the Lens introduction as a first-visit dialog with a typed don't-show-again The guided setup no longer replaces the Lens tabs. It opens in a dialog on the first visit of a session or from ?setup=lens, with a close and a "Don't show this again" checkbox in its top-right corner. The header "Set up Lens" button is gone. Dismissal state lives in a new schema-validated web storage helper (src/lib/storage.ts) that reads through useSyncExternalStore, so server renders see the fallback and other tabs stay in sync; only Lens uses it for now. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * refactor(ui): keep only Copy as curl in the Lens run search footer Drop the SQL chip and predicate preview; SearchBox.ApiHint becomes SearchBox.CopyCommand, which takes the command for the current query. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * feat(ui): align the Lens investigations list with the traces list Use the shared query SearchBox with investigation fields (name, agent, status, schedule), match the traces toolbar, and drop the count footer and inner padding. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(ui): let Lens settings bring back the introduction after don't show again Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(ui): share one InspectorTable between the traces list and the Lens investigations tree Compose TanStack Table, react-virtual and the shadcn table cells into InspectorTable parts (Root, Grid, Header, Body, Row, Indent). Investigations get findings as real sub-rows with TanStack expansion instead of a hand-rolled flattener. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(ui): break Lens import cycles and move shared pieces out of lens Search and the dot field go to components/shared, run search and the preview button go to view_logs where they are consumed. Lens api, services and demo live under data/, all URL state in route.ts, storage keys in storage.ts, and the session frame styles become cva variants. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(ui): one Lens readiness source and one onboarding flow Readiness is computed once in model/readiness and read through useLensReadiness, replacing useLensSetup, status.readiness and the welcome screen's own checks. The Investigations welcome now renders the same onboarding steps as the introduction dialog, with permissions and actions coming from an OnboardingProvider instead of props passed down four levels. StepIndicator and StateMessage are shared lens components, and the step panels are labelled accordion regions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(ui): read the Lens token from services and use semantic status colors Lens services carry the access token they were built for, so trace evidence, readiness and onboarding read it from context instead of a prop threaded through six components. List and history invalidation lives in one data hook. Status colors use the success, warning and destructive tokens, and template-literal class names go through cn. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(ui): split Lens demo fixtures from the fake demo APIs Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(ui): show Lens check history as a dot timeline Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chores * fix(ui): clear stale Lens evidence on run change and keep read-only users off Settings Also names inline option objects to bring local/no-large-inline-object-arg back under budget. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Yujong Lee <yujong@berri.ai> Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com> |
||
|
|
5ddcc45b3a
|
feat(ui): share trace drawer as a closable SidePanel and polish Lens (#44473)
Some checks failed
Unit Tests: Proxy DB Operations / budgets (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / custom-logging (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / db-and-spend (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / endpoints-and-responses (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / guardrails-hooks (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / jwt-and-keys (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / key-generation (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / logging-misc (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / proxy-runtime (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / proxy-server-core (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / proxy-utils (push) Blocked by required conditions
Unit Tests / caching-local (push) Waiting to run
Unit Tests / core-utils (push) Waiting to run
Unit Tests / enterprise-package (push) Waiting to run
Unit Tests / enterprise-routing (push) Waiting to run
Unit Tests / integrations (push) Waiting to run
Unit Tests / All Other Providers (push) Waiting to run
Unit Tests / Vertex AI (push) Waiting to run
Unit Tests / mcp-integration (push) Waiting to run
Unit Tests / misc (push) Waiting to run
Unit Tests / proxy-auth (push) Waiting to run
Unit Tests / proxy-endpoints (push) Waiting to run
Unit Tests / proxy-extras (push) Waiting to run
Unit Tests / proxy-infra (push) Waiting to run
Unit Tests / proxy-infra-root (push) Waiting to run
Unit Tests / proxy-server (push) Waiting to run
Unit Tests / responses-caching-types (push) Waiting to run
GitHub Actions Security Analysis / zizmor (push) Waiting to run
Terraform Provider / gofmt, vet, build, test (push) Has been cancelled
Terraform Provider / Provider endpoints vs proxy OpenAPI schema (push) Has been cancelled
* feat(ui): extract trace drawer into a shared SidePanel that closes on outside press
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat(ui): center Lens mode switch in a notch joined to the content card
Larger Traces/Investigations switch, a subtle dot when an investigation is running or queued, and a bigger Lens title with a docs link.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(ui): stronger Lens frame border and header spacing
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(ui): align Lens notch fillet with the notch border
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(ui): centered Lens loading/error states and proxy JSON calls in dev
The dev server answered GET /lens with the Lens page HTML because the UI route shadowed the proxy fallback rewrite. JSON API requests now go to the proxy before page routes, and a non-JSON success body raises a readable ApiError.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* refactor(ui): theme-scale type and shape in trace views, aligned pane bars
Add local/no-arbitrary-design-value, scoped to TraceView and Lens, banning
arbitrary font size, tracking, leading, radius, border and CSS property
values. Map the Figma-export values onto the theme scale and replace hex
colors with info/destructive tokens.
Add PaneBar, a fixed-height bordered row, and build the step tree and span
detail headers from it so their borders line up across the split.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(ui): restore AgentTracesSection emptied in
|
||
|
|
62fb808d4b
|
feat: improve Lens dev seeding and live UI (#44468)
* feat: seed Lens dev with configurable load profiles * feat: run Lens UI live through the dev launcher * fix: verify Lens UI startup before seeding * fix(ui): render run timestamps on one compact line The agent runs table printed the long locale form with timezone, which wrapped to two lines per row. Use a fixed-width 24h form with milliseconds that matches the timeline axis, keep the long form in the hover title, and show the timezone once in the column header Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * refactor(ui): use the root query client for the Lens demo Drop the demo's nested QueryClient. Cache keys are already partitioned by scope, and the root client now skips retries on 4xx ApiErrors, which covers the demo's not-in-demo and read-only rejections and live 4xx alike. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore: drop unused synthetic_spend reference from query help Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(traces): drop the deeplite fixtures and map every SDK to a logo The deeplite captures predate the example repository and carried synthetic spend rows, which leaked a fixture-only column into the query help SQL and pinned tests to its shape. Replace them with the SDK captures in the ClickHouse round trip and query API tests, and derive the seed tenant lookup from the capture metadata The runs table only knew the two Anthropic framework slugs. Register the slugs the normalizer emits for LangChain, LangGraph, Deep Agents, CrewAI, Google ADK, LlamaIndex, OpenAI Agents, Pydantic AI, Strands, Vercel AI SDK, Codex, Cursor and Copilot, and fall back to the generic agent glyph when a run has no known framework Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * refactor(ui): inject the Lens sample through data sources instead of demo checks Components no longer ask whether they are in the demo. The traces source carries live and handoff, navigation state comes from a URL or memory route, and the preview action comes from context instead of onDemo props Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(ui): infinite scroll for the agent runs list Replace the Load more button with a sentinel that fetches the next cursor page as the list nears its end. Placeholder rows hold the tail while more runs exist, and a failed page stops auto-loading until Retry. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(ui): keep the whole Lens view in the URL Lens navigation now lives entirely in query params through nuqs: the sample session (demo=true, with a Demo data switch in the header), the open run (trace, trace_ref), the selected step, view and detail section (span, view, span_tab) and the list filters and range (q, agent, status, hours). Any Lens view is a shareable link and the back button walks runs RunView takes its selection injected: the drawer feeds it URL state and the investigations evidence sheet keeps a local one, so a finding's original run never writes step ids into the URL. Leaving the sample session clears every Lens key except the tab so sample ids never point at live data Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * style(traces): cargo fmt captures tests Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --------- Co-authored-by: Yujong Lee <yujong@berri.ai> Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com> Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> |
||
|
|
e340e546e2
|
feat(traces): tracing development seed (#44363)
* feat(dev): seed linked tracing and spend fixtures * chore(dev): use OpenAI model in tracing config * chore(dev): align tracing credentials with UI E2E * fix(dev): update fixture seeder query scope * feat(dev): seed linked tracing and spend fixtures * chore(dev): use OpenAI model in tracing config * chore(dev): align tracing credentials with UI E2E * fix(dev): update fixture seeder query scope * wip * wip * wip * chore(trace): checkpoint ongoing Rust migration * refactor(trace): group Python bridge under trace package * refactor(traces): read span conventions through a Convention trait Each span format (Claude Code, LangSmith, OpenInference, gen_ai) now lives under normalize/convention/ as a unit struct implementing Convention, owning both its detection and its extraction. Precedence is one ordered registry instead of an if-chain in mod.rs that reached into each module differently. The modules now share one way to read attributes: present() for the first non-empty key and Payload for a text that also reports the key it consumed, replacing three different idioms and the &mut Vec threaded through payload readers. Instrumentation::adjust returns a new Extraction instead of mutating one, with each SDK rule as its own function, and the LangChain middleware suffix list exists once. * feat(trace): export Rust-owned wire schemas and enforce contract bounds * fix(trace): bound quoted counts in ClickHouse wire schemas * feat(trace): generate Python wire contracts with datamodel-code-generator * test(trace): validate migrated callers and generated contracts at the native boundary * refactor(traces): rename normalization convention to format * fix(traces): reconcile spend evidence and preserve unknown costs * feat(traces): normalize additional telemetry formats * test(traces): cover captured normalization fixtures * refactor(traces): isolate SDK normalization rules * feat(tracing): seed all trace exports for local dashboard * fix(clickhouse): preserve custom LiteLLM request metadata * docs(traces): define normalization module boundaries * docs(traces): define resolution and OTLP boundaries * fix(ui): normalize nullable trace message names * refactor(traces): split resolver modules and cover resolution behavior * test(traces): replace normalization snapshots with behavior assertions * fix(ui): align dashboard API contracts with generated types * refactor(traces): type normalization and storage boundaries * fix(traces): seed captured SDK spend and preserve provider identities * wip * test(traces): verify guide discovery and content ordering --------- Co-authored-by: Yujong Lee <yujong@berri.ai> Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> |
||
|
|
0c86d6bfc9
|
refactor(dashboard): migrate to zod 4 and openai 6 (#44345)
* refactor(dashboard): migrate to zod 4 and openai 6 Bump the dashboard to real zod 4.6.5 and openai 6.49.0 so every module imports from bare "zod" instead of "zod/v4". Ports the ten files that still used the zod 3 API (error params, record, passthrough, strict, email/date validators, union discriminator codes) and adapts the LiteAdmin tool schemas and form plumbing where openai 6 and zod 4 changed behaviour. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * style(dashboard): prettier format zod 4 schema files Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * fix(dashboard): restore system one missing state message under zod 4 Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --------- Co-authored-by: Yujong Lee <yujong@berri.ai> Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> |
||
|
|
268eb4d6e6
|
feat(tracing): add OTLP trace ingestion and reads (#43915) | ||
|
|
82eb7405f5
|
chore(deps): bump pyjwt, moment and brace-expansion to clear osv-scan (#43792)
pyjwt 2.13.0 -> 2.14.0 (uv.lock only, pyproject floor unchanged), moment 2.30.1 -> 2.31.0 and the brace-expansion override 5.0.9 -> 5.0.12 in the dashboard. oauthlib's only fixed release (4.0.0, 2026-09-28) is still inside the 3-day uv exclude-newer cooldown, so its two findings are ignored until 2026-10-02 |
||
|
|
5b287f66d7
|
feat(ui): add native LiteAdmin assistant (#42443) | ||
|
|
bc8305810f
|
fix(ui): align SDK mocks and remaining dependency patches | ||
|
|
568ea11ea1
|
chore(ui): bump Next.js and Vitest dependencies | ||
|
|
47c988e05c
|
refactor(ui): move the dashboard onto class-variance-authority (#38125)
The dashboard used `cva@1.0.0-beta.4` with the object-argument API behind
`@/lib/cva.config`, while shadcn emits `class-variance-authority` with the
positional API. Every `shadcn add` of a cva-based primitive therefore needed a
hand fix-up before it compiled, which meant `components/ui/` could never match
a fresh CLI run and `shadcn add <name> --diff` reported the whole file as
changed instead of showing real upstream drift.
Swap the dependency, and regenerate `badge`, `button`, `button-group`,
`input-group` and `tabs` straight from the base-vega registry so they are now
byte-identical to the CLI output plus prettier.
Two primitives could not be regenerated because they are local code rather
than registry items, so they move out of `components/ui/`: `sidebar` (203
lines against upstream's 730, and only `leftnav` consumes it) and `meter`
(no registry entry at all, it wraps Base UI's Meter).
The customisations that were baked into the regenerated files move to
wrappers, following the rule that `components/ui/` holds CLI output and
anything on top of it lives outside:
- badge carried info, success and warning variants that duplicated the
existing `StatusBadge` tone map, so its five call sites now use
`StatusBadge`, which gains an optional `className`
- input-group's addon focuses `[data-slot=input-group-control]` rather than
upstream's `input`, which matters because the chat composer puts a textarea
there. That handler now sits at the one call site that needs it
`cx` keeps its previous twMerge behaviour. It came from the old
`defineConfig({hooks: {onComplete: twMerge}})`, and CVA's own `cx` is plain
clsx, so pointing it at `cn` avoids silently dropping conflict resolution in
the six files that use it.
`Sidebar.test.tsx` covers the failure mode this migration can hide: passing
the object form to the positional API is accepted by clsx and renders the
literal class string "base variants defaultVariants", so the component loses
every style while the type checker and the existing suite stay green.
|
||
|
|
933e28d900
|
feat(ui): add a light/dark/system theme toggle to the top bar (#37669)
* feat(ui): add a light/dark/system theme toggle The dashboard already carried a full `.dark` palette, dark-aware surfaces and a dark logo variant, but nothing ever put the `dark` class on the document, so none of it could be reached. next-themes now owns that class: it reads the stored choice, falls back to the OS preference, and stamps the class from an inline script before first paint so there is no light flash on load. The toggle is a three-way System / Light / Dark control in the account menu, in both the sidebar menu and the older navbar dropdown, so it is reachable from the gateway dashboard, chat and the model hub alike. useIsDarkMode watched the root element with a MutationObserver purely to answer a question next-themes now answers directly, so it goes, and useSyntaxTheme reads resolvedTheme instead. The toaster follows the resolved theme too. * feat(ui): move the theme control to the top bar and default to light The toggle now lives in the header toolbar of both shells, the gateway dashboard's DashboardHeader and the older full-width Navbar, where it replaces the placeholder comment that had been holding its spot. It reads better there as a single icon button with a System / Light / Dark menu than as a segmented row buried in the account popover, so the account menus lose their theme row. Dark mode is still being rolled out, so an install that has never touched the control now stays light instead of following the OS. System is still a choice, just no longer the default. While dark is active the toolbar carries a small Experimental badge, so nobody mistakes an unstyled surface for a bug. * fix(ui): serve the dark logo in the legacy navbar too The sidebar already paired its logo with a dark variant, but the full-width navbar kept a single light-only image. That did not matter while dark mode was unreachable; now that the toggle sits in that shell's own top bar, the white JPEG slab lands on a dark bar. It gets the same two-image swap the sidebar uses, and a test that pins the pairing so the two shells cannot drift apart again. |
||
|
|
7b574b9df6
|
chore(ui): drop the antd dependency and its leftovers (#37574)
Some checks failed
Unit Tests: Proxy DB Operations / assert-shard-coverage (push) Waiting to run
Unit Tests: Proxy DB Operations / auth-checks (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / budgets (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / custom-logging (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / db-and-spend (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / endpoints-and-responses (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / guardrails-hooks (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / jwt-and-keys (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / key-generation (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / logging-misc (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / proxy-runtime (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / proxy-server-core (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / proxy-utils (push) Blocked by required conditions
CodSpeed Benchmarks / benchmarks (push) Waiting to run
Publish basedpyright base counts / publish (push) Waiting to run
Code Quality Checks / code-quality (push) Waiting to run
UI Unit Tests / ui-unit-tests (push) Waiting to run
Unit Tests: Documentation Validation / documentation (push) Waiting to run
GitHub Actions Security Analysis / zizmor (push) Waiting to run
Unit Tests: Core Utilities / core-utils (push) Has been cancelled
Unit Tests: Enterprise, Google GenAI & Routing / enterprise-routing (push) Has been cancelled
Unit Tests: Integrations (Callbacks & Logging) / integrations (push) Has been cancelled
Unit Tests: LLM Provider Transformations / Vertex AI (push) Has been cancelled
Unit Tests: LLM Provider Transformations / All Other Providers (push) Has been cancelled
Unit Tests: MCP, Secrets, Containers & Misc / misc (push) Has been cancelled
Unit Tests: Proxy Auth & Key Management / proxy-auth (push) Has been cancelled
Unit Tests: Proxy Infrastructure / proxy-infra (push) Has been cancelled
Unit Tests: Responses, Caching & Types / responses-caching-types (push) Has been cancelled
Unit Tests: Proxy API Endpoints / proxy-endpoints (push) Has been cancelled
Unit Tests: Proxy API Endpoints / proxy-server (push) Has been cancelled
Nothing in the dashboard renders antd any more, so the package and the
scaffolding around it can go. This removes `antd` and
`@ant-design/cssinjs` from package.json, deletes the global StyleProvider
the root layout wrapped every page in, drops the `antd` cascade layer and
the z-index override that lifted Base UI popups over an antd Modal, and
retires the lint rules that policed antd imports and antd class selectors
in tests.
Fifteen test files still carried `vi.mock("antd", ...)` factories for
components that stopped importing antd during the migration. They were
inert, and they resolve the real module, so they would have broken the
moment the package left node_modules.
The compatibility shims keep their behaviour and lose the antd name:
`antdRules`/`antdRequired` become `validatorRules`/`requiredRule`,
`isAntdUrl` becomes `isValidUrl`, and `ABOVE_ANTD_MODAL` becomes
`NESTED_DIALOG_LAYER`. Comments that explain why a contract looks the way
it does still name antd, because that history is the reason.
|
||
|
|
629d7683f2
|
refactor(ui): swap @ant-design/icons for lucide-react (#37553)
The dashboard drew its icons from two libraries at once: lucide-react, which shadcn/ui ships with, and @ant-design/icons, left over from antd. This moves the last 39 files onto lucide and drops the dependency, so the icon set matches the component library everywhere. antd icons sized themselves from the inherited font-size and rendered as role="img" with an aria-label, neither of which a lucide svg does, so the swap carries explicit size classes and gives the two icon-only plugin buttons real accessible names. |
||
|
|
f1e143a87c
|
chore(ui): upgrade the dashboard to React 19 (#37411)
* chore(ui): upgrade the dashboard to React 19 Bumps react and react-dom from 18.3.1 to 19.2.8 with matching @types. Next 16 already required a React 19 peer, so this aligns the dashboard with what the framework expects and unblocks Base UI and shadcn work that assumes the React 19 ref model. React 19 passes ref through as a regular prop, so the setup file's forwardRef tripwire and the ref-forwarding test's forwardRef case no longer describe real behavior; both now assert the React 19 contract instead. useRef<T>(null) now yields RefObject<T | null>, which is the one prop type MessageList had to widen. * test(ui): wait for a Base UI select popup to open before clicking an option The option lands in the DOM one render before the popup finishes entering, while its positioner still carries pointer-events: none, so clicking it throws. Waiting on the option's text alone was a race that React 19's flush timing loses, which is why four ToolPolicies cases went red on the bump. chooseSelectOption in test-utils opens the trigger, finds the option by role, waits for it to stop being pointer-blocked, then clicks. It also replaces the last-match-by-text hack, which only worked because the popup happens to portal after the table. |
||
|
|
7675ba8717
|
test(ui): split the vitest suite into unit, component, integration and type projects (#37488)
* test(ui): split the vitest suite into unit, component and integration tiers Every test file booted jsdom, including the ~1800 that assert pure functions and never render. They now run as a separate vitest project in the node environment, where the whole tier finishes in under four seconds. The tiers are vitest projects rather than a naming convention, so CI can run them as independent jobs. A .test.ts that renders React, a hook test being the usual case, is listed explicitly and stays in the jsdom tier. * test(ui): report per-test duration against a per-tier budget A timeout only catches a hung test, and it has to stay generous enough to survive a loaded runner, so it never reports the multi-second render tests that make CI fail the moment the box is busy. Budgets are separate and far tighter: 50ms unit, 1s component, 3s integration. The counts are laptop measurements, so the CI job is report-only for now. Flipping it to blocking is one line once CI has published its own numbers. * test(ui): run the tiers as separate CI jobs and stop clicking popups by text The old job ran every file in one process, so the single slowest file set the wall clock and a bigger box bought nothing. The tiers now run as separate jobs with the component tier sharded four ways. getByText and findByText match hidden nodes, so they resolve against a closed Base UI popup whose positioner still carries pointer-events: none, and the click lands or not depending on how far the open transition got. Two files failed this way, one three runs in five and one every run. Querying the option by role waits for it to be visible, and both are now stable. A lint rule keeps the pattern from coming back. * test(ui): give React Testing Library's async queries a CI-sized window findBy* and waitFor run on asyncUtilTimeout, which defaults to 1000ms and is independent of vitest's testTimeout. Raising the vitest timeout therefore did nothing for them: a query still gave up after one second while the test had 59 seconds of budget left, which is why a loaded runner produced 'Unable to find role=...' rather than a timeout. UserSearchModal is the worked example. The role query it makes resolves in 249ms on a laptop and blew past 1000ms on CI, failing the run at 1494ms. Five seconds keeps the same assertions and only widens the window a failing query waits before reporting; a passing query still resolves the moment the element appears. * test(ui): calibrate the tier budgets from real CI numbers and report by default The first CI run showed the laptop counts were badly off: component 176 local against 326 on CI, integration 87 against 128. The maxima now come from that run with headroom. continue-on-error still painted the check red, which is the opposite of the point, so the report-only decision moves into test-budgets.json as an explicit enforce flag. The job passes and prints the counts; flipping enforce to true makes it a gate. * docs(ui): drop the CLAUDE.md edits from the tier split Keeping this PR to the vitest, CI and test changes. * ci(ui): run every tier in one job instead of eight check rows Sharding bought nothing. Measured on the first run of this branch, the component tier unsharded finishes in 198s while the integration tier is floored at 384s by a single file, so integration was always the critical path and the four component shards only added rows. One job running every project comes in around 384s against the 426s the split jobs took. Eight rows named things like 'component (2)' also told a reviewer nothing, on a PR page that already carries forty checks. The job keeps the id ui-unit-tests because guard-internal-staging requires that exact context; renaming the jobs had silently stopped it reporting, which would have blocked every merge on a check that no longer existed. The workflow's display name becomes UI Tests since it runs more than unit tests. The tier split itself is untouched: it lives in the vitest projects config, so the unit tier still runs in node with no jsdom, and each tier keeps its own timeout and budget. * fix(ui): stop the type check from running the whole suite a second time test:types was 'vitest --run --typecheck.only'. Under test.projects that flag is ignored and the root-level typecheck block is not inherited, so the step collected each project's normal include and ran all 8464 runtime tests instead of type-checking. It took 542s on CI against 33s on the flat config it replaced, and the job then ran the same suite again in the next step. Typecheck now belongs to a project of its own, with an empty include so it contributes no runtime tests, and the CI job runs one vitest invocation for all four. The type tier adds about 3s to a full run and reports 'Type Errors: no errors' rather than a suite of tests. Verified it still catches things: breaking SortingState in DataTable.test-d.tsx fails with 'Type number is not assignable to type string' and exit 1, and restoring it passes. * test(ui): scope the split down to the vitest tier projects Removes everything from this branch that was not the tier split. The three lint rules brought 1381 lines of grandfathered suppressions in eslint-suppressions.json, which is 81% of the branch's added lines and debt nobody is going to pay down. The per-test duration budget does not scale as a CI step. Both are gone, along with the two query rewrites the no-click-by-text rule forced: those files pass 10/10 at this base, quiet and under load, so there was no failure behind them. The workflow is byte-identical to the base again. It already runs npm run test:types and then vitest related on pull requests, so PR cost is unchanged; the split only repoints test:types at the new project. That project is required, not optional: vitest silently ignores --typecheck.only under test.projects, so without it the type script collects the whole suite instead of the one typed file. Restores the base 60s testTimeout on the unit tier. The 5s cap was not part of the split and failed ChatShell.serverRootPath.test.ts, a 960ms test, under load. |
||
|
|
bb8324c119
|
refactor(ui): drop @tremor/react and the theming scaffolding it needed (#37394)
The last tremor component import left the dashboard when the primitive sweep merged, so the package, its v3 compatibility shim, its @theme token block and the palette safelist it needed at runtime all have no consumer. Removing the safelist is what shrinks the shipped stylesheet: tremor built class names at runtime, so Tailwind had to emit every bg/text/border/ring/ stroke/fill utility across 22 palettes and 11 shades in case one was used. Nothing in the app constructs a class name that way any more, so the scanner finds every utility on its own. The date-fns overrides pin also goes. It only existed because tremor and react-day-picker@8 peered on date-fns 3 while Base UI wanted 4, and the lockfile still resolves a single hoisted 4.4.0 without it. |
||
|
|
68d4ba5da5
|
refactor(ui): move dashboard toasts from antd message/notification onto sonner (#37207)
* refactor(ui): move dashboard toasts from antd message/notification onto sonner
Add lib/toast.ts as the single toast surface (success/info/warning/error/
fromError/dismiss) backed by sonner, with a <Toaster /> in the root layout.
fromError titles a toast from the proxy error type or the HTTP status instead
of matching prose phrases, and shows the extracted proxy message as the
description.
MessageManager and NotificationManager become thin facades over lib/toast so
the ~250 existing call sites keep working; the mutable antd instance setters,
setMessageInstance/setNotificationInstance, and the antd App/message/
notification providers in AntdGlobalProvider are gone. Prunes the eslint
suppression baseline accordingly.
* test(ui): mock the MessageManager seam in the Fallbacks tests and drop toast doc comments
AddFallbacks and FallbackSelectionForm asserted on a mocked antd message spy
that MessageManager no longer calls; they now mock the facade the components
import. Also removes the explanatory comments Greptile flagged in lib/toast.ts
and both facades.
* fix(ui): keep NotificationManager's antd config-object contract on the sonner facade
success/info/warning/error accept the { message, description, duration } object
form again (CreateMCPServer's admin-review notice uses it) and fromBackend keeps
its extra.duration seconds argument, both mapped onto lib/toast. Prunes stale
suppressions picked up by the rebase.
* feat(ui): read the proxy error type and code out of JSON envelopes embedded in string errors
Legacy networking helpers throw new Error(responseText) and callers prefix
that text, so the envelope arrives as a substring. fromError now parses the
first embedded JSON object for type/code and shows the unwrapped message in
its place, so those toasts get a status title (Request Error, Not Found) and
a readable description instead of raw JSON.
|
||
|
|
41d6dbb50c |
build(ui): gate dashboard test assertions with testing-library and jest-dom
Enables 14 rules at error with zero violations, so the vacuous-assertion class
the previous commit fixed cannot come back. No budget file and no suppressions
baseline: a rule is on only if it is already at zero.
prefer-to-have-value stays off. It matches any attribute whose name contains
"value", so it rewrites toHaveAttribute("aria-valuenow", n) into toHaveValue(n),
and jest-dom's toHaveValue supports only form controls, which breaks every
role="meter" element the dashboard renders.
Records the enabled set, the rules left off with their measured counts, and the
seven ways these plugins' autofixers produce broken output.
|
||
|
|
729ec315e2
|
refactor(ui): make illegal DataTable prop combinations unrepresentable
DataTable accepted any mix of its 40-odd props and rejected the incoherent combinations at runtime, from a validator that threw during the first render. A caller only found out it had wired server sorting without a `sorting` prop when the page blew up in front of them. Split the public prop type into mode-keyed unions instead, so the compiler rejects those combinations at the call site. `validateDataTableConfig` and `DataTableConfigError` go away; the component body reads an unchanged flat `DataTableResolvedProps`, which every union member is assignable to, so there is no narrowing inside it. All 44 existing call sites typecheck against the new union unchanged, which `next build` covers. That build only typechecks the app module graph, so the prop type itself needed a gate of its own: `npm run test:types` runs vitest's typecheck mode over `*.test-d.tsx`, and the unit workflow now runs it. The four guards deleted from `DataTable.test.tsx` come back there as compile-time assertions, and loosening the union back to the flat shape fails all five. |
||
|
|
0253154780
|
build(deps-dev): bump js-yaml to 4.3.1
Closes GHSA-5p4m-2wfm-xmqj (CVSS 7.5), flagged by osv-scan against ui/litellm-dashboard/package-lock.json. js-yaml is pinned by an exact npm override, so the override and the lock move together. Dev-only dependency: js-yaml reaches the tree through eslintrc, knip and @redocly/openapi-core, none of which ship in the built dashboard. 4.3.1 published 2026-07-31, clear of the 3-day min-release-age cooldown. |
||
|
|
2dc49a913c
|
refactor(ui): replace hand-rolled query-param routing with nuqs (#35871)
* refactor(ui): replace hand-rolled query-param routing with nuqs The dashboard carried five copies of the same pushState-based detail routing hook plus a shared navigateWithParams helper, each with its own plumbing test and a copy-pasted reactive useSearchParams mock in component tests. nuqs provides the same shallow history-API routing behind useQueryState/useQueryStates, so the key, team and org hooks are deleted in favor of inline useQueryState at their single consumers, while the models and logs hooks keep their interfaces but drop their hand-rolled internals. Component tests now mount NuqsTestingAdapter (via renderWithProviders or locally) instead of patching window.history, and URL assertions go through onUrlUpdate spies that can additionally distinguish push from replace, which the old window.location checks could not * test(ui): assert browser back closes the log drawer after in-drawer selection Greptile flagged that the nuqs port of the switching-logs test stopped at asserting emitted push and replace modes. The test now replays those recorded modes against a history stack and performs the back step, so a regression to push-on-select or broken URL-derived drawer state fails the test instead of passing silently |
||
|
|
64aab7be85
|
ci: pin Node on the Playwright UI lanes so npm ci meets the engines floor
e2e_ui_testing and e2e_ui_testing_server_root_path run on cimg/python:3.12-browsers, the one UI executor whose image supplies Node rather than taking it from a cimg/node tag. That image ships Node 24.14.0, which bundles npm 11.9.0, so both lanes have failed EBADENGINE against the engines floor added in #35801. Every Node 24 release through 24.14.0 bundles an npm below 11.10.0, so engines.node also rises to 24.14.1 (npm 11.11.0), the first release where the two floors agree The pinned install goes into /opt/node with /opt/node/bin prepended to PATH instead of unpacking over /usr/local. On this image /usr/local already holds npm 11.9.0, and extracting the tarball on top of it merges the two trees into an npm that reports 11.17.0 and then exits 1 on npm ci printing no error text at all, which is a worse failure than the one being fixed The install moves into a reusable install_node command so the version and its checksum have one home, shared with proxy_pass_through_endpoint_tests, and the command refuses to run when it disagrees with ui/litellm-dashboard/.nvmrc. A lane drifting off the version the rest of the toolchain uses is what produced this failure, so that mismatch now stops the job instead of surfacing later as an install error The e2e node_modules cache key moves to v4 because the saved trees were built by the old npm |
||
|
|
487074f602
|
chore(build): move the Admin UI toolchain to Node 24 (#35801)
* chore(build): move the Admin UI toolchain to Node 24 Node 18 and Node 20 both reached end of life (2025-04-30 and 2026-04-30), and the release images along with every CI lane were still building on them. Node 24 is the current LTS through 2028-04-30, so this moves the four UI build images, the CircleCI lanes, and the four GitHub Actions workflows onto it Node 24 also ships npm 11.17, which is the first line that implements the min-release-age setting this repo already carries in its .npmrc files. On npm 10 the key is parsed and discarded, so the release-age gate has had no effect regardless of its value. Tightening the dashboard's engines range and turning on engine-strict makes an unsupported npm fail loudly rather than skip the gate quietly, and a new step in the UI build workflow probes an impossible cooldown so an inert setting cannot pass unnoticed again Node 24's bundled undici tightened its brand check on RequestInit.signal, which rejects the AbortSignal jsdom installs and broke the two cases in src/lib/http/api.test.ts that rebase a request onto a runtime base url. Under jsdom the Request global comes from Node while AbortSignal comes from jsdom; tests/jsdomFetchEnv.ts delegates to the jsdom environment and then restores Node's native AbortController and AbortSignal so both come from one realm. Upgrading jsdom does not address this, as jsdom still does not own Request The workflows now read ui/litellm-dashboard/.nvmrc instead of repeating a literal, so the Node version has a single source of truth, and ui/Dockerfile is pinned by digest to match the other three build images. The lockfile changes are npm 11 normalising the engines range and dropping optional peer entries it no longer records * fix(build): point every Admin UI build script at .nvmrc The enterprise Docker path was left on Node 18. docker/build_admin_ui.sh runs only when enterprise/enterprise_ui/enterprise_colors.json is present, which it never is in the OSS tree, so neither CI nor a default image build reaches it; it pinned nvm to v18.17.0 and then built the dashboard, which now requires Node 24, so a customized enterprise image would have failed EBADENGINE All three UI build scripts now resolve the version from ui/litellm-dashboard/.nvmrc rather than carrying their own pin, so the Node version has a single home across Docker, CI, and local builds. build_ui.sh was on v20 and build_ui_custom_path.sh on v18.17.0 Also drops the dependency-cooldown probe from the UI build workflow. The engines floor plus engine-strict already fails an unsupported npm loudly at install time, so the probe was redundant, and treating any nonzero exit from a live registry call as proof of enforcement made it unsound besides |
||
|
|
3bc4989ce4
|
chore(ui): update brace-expansion and postcss to current patch releases
The dashboard pins both packages exactly in `overrides`, so the lockfile stays on whatever those pins say. Move brace-expansion from 5.0.8 to 5.0.9 and postcss from 8.5.22 to 8.5.23, both upstream patch releases, and regenerate the lockfile. `npm ci`, `next build`, and the 5888-test vitest suite all pass on the updated lockfile. |
||
|
|
c9d067fccc
|
chore(deps): bump gitpython to 3.1.55 and brace-expansion to 5.0.8
gitpython arrives transitively through mlflow-skinny; re-resolved with uv so the lock moves that one package only. brace-expansion is a dev-only transitive dep already pinned in the dashboard 'overrides' block, so the pin is bumped alongside the lockfile to keep the change durable across reinstalls. 5.0.8 narrows its engines range from '18 || 20 || >=22' to '20 || >=22'; the dashboard already requires node >=20.9.0 and every CI job pins node 20, so nothing loses support. |
||
|
|
3467871007
|
chore(deps): bump gitpython and postcss to advisory-clear versions
Clears five OSV findings the scanner flags on every PR: four gitpython advisories fixed in 3.1.54, and one postcss advisory fixed in 8.5.18. gitpython 3.1.55 and brace-expansion 5.0.8 are left for a follow-up; both were published less than three days ago and are still inside the dependency cooldown window. |
||
|
|
301a02b7be
|
chore(ui): bump next to 16.2.11 (#34329)
Moves the dashboard's next pin from 16.2.6 to the latest 16.2.x patch and bumps eslint-config-next to match. Regenerating the lock also healed in explicit bundled-dependency records under @tailwindcss/oxide-wasm32-wasi |
||
|
|
0fcaadf11c
|
test(e2e): move Admin UI Playwright suite to tests/e2e/ui (#34196)
Relocates ui/litellm-dashboard/e2e_tests to tests/e2e/ui so all end to end suites live under tests/e2e. The suite stays in TypeScript and becomes a self-contained npm package with its own package.json, lockfile and tsconfig instead of leaning on the dashboard's toolchain; the dashboard drops its @playwright/test dependency, e2e scripts and knip/vitest/tsconfig carve-outs. CI paths follow the move: both CircleCI jobs (main e2e and the SERVER_ROOT_PATH migration smoke) and the test_server_root_path workflow now install and run Playwright from tests/e2e/ui, with the node cache keyed on both lockfiles. classify_changes.sh treats tests/e2e/ui as client so spec edits keep skipping backend jobs. The suite's mock LLM fixture is excluded from the e2e basedpyright zero-error gate in pyrightconfig.json since it belongs to the TS suite, not the typed Python harness. |
||
|
|
20a4666ec6
|
chore(ui): bump sharp to 0.35.x via npm override (#34193)
sharp reaches the dashboard only as an optional dependency of next, which pins it to ^0.34.5. A caret range on a 0.x version cannot resolve past 0.34.x, and every stable next through 16.2.11 still declares that same range, so there is no transitive path to the 0.35 line. Add an overrides entry, matching how the other pinned transitives in this package are already handled. The dashboard builds with output: "export" and images.unoptimized, so sharp is never loaded; this keeps the lockfile current rather than changing runtime behaviour. |
||
|
|
72d458e416
|
feat(ui): add react-hook-form + zod form infrastructure (#34170)
* feat(ui): add react-hook-form + zod form infrastructure
Introduce the shared form layer the dashboard's antd forms will migrate onto,
with no user-visible change yet.
- pin react-hook-form, @hookform/resolvers, and zod (kept on 3.25.76 and
imported via the zod/v4 entrypoint so openai's optional zod ^3 peer still
resolves and npm ci stays clean)
- vendor the base-vega Field family into components/shared/form as forwardRef
components on the repo's cva.config, since base-vega ships no form primitive
and its field source imports class-variance-authority and is React 19 style
- add a FormField bridge that binds a react-hook-form Controller to the Field
layer and wires label, description, and error ids into aria attributes
- add pickDirty, which narrows a submitted body to the top-level keys the user
actually touched so a partial update stops re-sending untouched fields
pickDirty reads dirtiness at the top level because react-hook-form tracks it
per leaf, so an edited array arrives as [true, false] and a cleared list as an
empty array that still carries its default-length dirty markers; the falsy
clear tokens (null, [], {}, 0, false) all survive.
Tests cover the Field primitives, the FormField aria wiring against a live
zod resolver, and pickDirty both as a unit and driven through a real
react-hook-form instance.
* test(ui): lock pickDirty behavior on a pure field-array reorder
react-hook-form compares each array element to its default positionally by
value, so useFieldArray move/swap and a reordered scalar array all mark the
moved indices dirty and pickDirty sends the whole array; a swap of two equal
elements is a value-level no-op and is correctly omitted. Covers the reorder
case a review flagged as untested.
|
||
|
|
f2ca0a149b
|
build(deps-dev): bump js-yaml to 4.3.0 and brace-expansion to 5.0.7
Both are dev-only build and lint tooling in the dashboard, not part of the browser bundle. The bumps pull in upstream maintenance releases that address inefficient handling of certain inputs js-yaml is force-pinned through the overrides block because @redocly/openapi-core exact-pins an older copy; a plain lockfile change would not hold since the tree re-spawns a nested stale version on re-resolution, so the override moves from 4.2.0 to 4.3.0. brace-expansion is added to overrides at 5.0.7 so npm install does not leave the previously resolved 5.0.6 in place. Both resolve to a single deduped copy after the change |
||
|
|
539bc30e04
|
refactor(ui): migrate callback debounce sites to react-pacer with regression tests (#33043)
* refactor(ui): standardize debounce waits behind shared DEBOUNCE_WAIT_MS constant * build(ui): bump @tanstack/react-pacer from 0.2.0 to 0.22.1 * refactor(ui): migrate straightforward value debounces to react-pacer * refactor(ui): migrate callback debounce sites to react-pacer with regression tests * chore(ui): restore trailing newline in eslint-suppressions.json * test(ui): mock all pacer debounce hooks in VirtualKeysTable test * fix(ui): update merged debounce tests for OldTeams to Teams rename |
||
|
|
3a42011350
|
build(ui): bump @tanstack/react-pacer from 0.2.0 to 0.22.1 (#33041)
* refactor(ui): standardize debounce waits behind shared DEBOUNCE_WAIT_MS constant * build(ui): bump @tanstack/react-pacer from 0.2.0 to 0.22.1 |
||
|
|
d0428cdd53
|
ci(ui): report only error-level knip findings in CI (#32971) | ||
|
|
8c5473f198
|
feat(ui): adopt openapi-react-query ($api) and convert useCustomers (#32949)
* feat(ui): adopt openapi-react-query and convert useCustomers to $api
Add openapi-react-query and expose $api = createQueryClient(fetchClient)
alongside fetchClient. Rewrite useCustomers as
$api.useQuery("get", "/customer/list", {}, { enabled, select }), which
derives the query key from method + path (dropping the hand-written
createQueryKeys entry and the manual key) and forwards the request signal
for cancellation. The response type still flows from schema.d.ts as
CustomerResponse[]. Tests assert the path, the admin/token enabled gate,
and the empty-body select fallback.
* test(ui): read the last render's options in useCustomers helper
The lastCallOptions helper was named for the last call but read
mock.calls[0]. Harmless while each test renders once, but it would
silently assert against first-render options if a test ever re-renders.
Read the final call instead.
|
||
|
|
7cdf42d770
|
chore(ui): remove eslint-metrics.json lint-count snapshot
The eslint-metrics.json snapshot duplicated the violation counts already enforced by eslint-budgets.json. Keeping it current added a CI drift check, a pre-commit regenerate-and-flag step, and a standalone npm run lint:metrics script, none of which caught anything the budget gate did not, yet all of which failed noisily whenever the snapshot went stale. This drops the file and that machinery while leaving eslint-budgets.json as the actual ratchet gate |
||
|
|
0bf81e2496
|
feat(ui): typed openapi-fetch foundation (fetchClient) + first typed caller (useCustomers) (#29884)
* feat(ui): add the typed openapi-fetch client (fetchClient) as the dashboard fetch foundation
Introduces fetchClient (openapi-fetch) bound to schema.d.ts, used inside ordinary TanStack Query hooks so path/query/body types come from the proxy's OpenAPI spec. A small runtime registry feeds the client the base URL and auth header name (registered by networking) and the session token (published by AuthContext), so call sites carry no token plumbing; auth-header injection and ApiError mapping live in openapi-fetch middleware reusing deriveErrorMessage/ApiError from client.ts, and non-2xx maps to a thrown ApiError so query functions just read .data.
The base URL default resolves from NEXT_PUBLIC_BASE_URL so a request still targets the right origin if it fires before networking registers its getter. AuthContext clears accessToken alongside the token on logout so no query fires unauthenticated after the session ends.
Foundation only; callers migrate one at a time, each fully typed, in follow-up changes.
* feat(ui): migrate useCustomers to the typed fetchClient
Converts useCustomers from allEndUsersCall to fetchClient.GET("/customer/list"); the response is typed as LiteLLM_EndUserTable[] from the schema, so the hand-written Customer/CustomersResponse types are deleted. They were also inaccurate (allowed_model_region was string but is "eu"|"us", and a budget_id the table has no field for). No cast; the schema type flows to the one consumer. First caller on the new pattern.
* fix(ui): route typed-client errors through the session-expiry handler
The typed fetchClient middleware threw ApiError without invoking the
handleError side effect that the legacy createApiClient wires via
onError, so a migrated caller hitting an expired key no longer triggered
the auto-logout. Add an error-handler seam to runtime.ts, register
handleError from networking.tsx alongside the base-url/header getters,
and call it in the middleware before throwing so both clients behave the
same. Regression test asserts the handler fires with the derived message
on non-2xx and stays silent on success
* fix(ui): point the customers EndUser type at CustomerResponse
The /customer/list response model was renamed to CustomerResponse on
staging; the merged branch still aliased EndUser to LiteLLM_EndUserTable,
so the exported type and its test mock had drifted from what the schema
actually returns. CustomerResponse is also the accurate shape (it types
allowed_model_region as 'eu' | 'us' and carries budget_id)
* chore(ui): refresh eslint-metrics baseline after staging merge
The recorded baseline predated the litellm_internal_staging merge, so its
no-explicit-any and no-large-inline-object-arg counts were higher than the
merged tree actually has. Regenerate via npm run lint:metrics so the gate
reflects current reality
* refactor(ui): source the typed client token from the session cookie, not AuthContext
The typed client read its bearer from a runtime value that AuthContext pushed
via setAuthToken, but migrated hooks gate enabled on useAuthorized, which
decodes the cookie directly. Two independent derivations of the same cookie with
different timing: on first load the query fires (useAuthorized sees the token)
before AuthContext's async effect publishes it, so the first request goes out
unauthenticated and only succeeds on a React Query retry.
Make the token a registered getter like the base-url and header-name getters,
reading the same cookie useAuthorized decodes, so the client's token and the
gate can't diverge. Revert the AuthContext changes entirely; nothing is pushed
from React state anymore.
|
||
|
|
e2eee36438
|
chore(ui): make knip trustworthy and enforce dead-code in CI (#32727)
knip was producing garbage locally and was never wired into CI, so nobody trusted it. Two structural problems: it silently degrades when deps are missing (a partial worktree install flagged all 436 test files as unused), and its config had blind spots that surfaced as false positives. Fixes so a knip run means something: - Register every playwright config (serverRootPath + migration variants), not just the main one. serverRootPath.config.ts is invoked via --config in test_server_root_path.yml, which knip can't see; it was falsely flagged as an unused file - Treat src/components/ui/** as entry points. These are shadcn design-system primitives, intentionally part of the palette before every one is consumed; knip was flagging not-yet-used ones (e.g. select.tsx) as dead files and their sub-exports as unused. Marking the directory as the design-system surface is the correct fix, not deleting components someone is about to use - Declare @ant-design/icons as a direct dependency. It was imported in ~198 files but only resolved via antd hoisting, so every one showed up as an "unlisted dependency" - Add an explicit vitest plugin block so test-file classification no longer rides on auto-detection - Stage severities via rules: gate the now-clean categories (files, dependencies, unlisted, unresolved) as errors and keep exports/types/ duplicates as warnings, so CI enforces what's at zero today while the remaining findings ratchet down in follow-ups - Run npm run knip in the frontend-lint CI job, which installs with npm ci so it never sees a partial tree knip now exits 0 with the gated categories clean |
||
|
|
592510ec18
|
feat(ui): shadcn charts foundation with tremor-compatible wrappers (#32668) | ||
|
|
cfe9e39e55
|
refactor(ui): switch shadcn primitives from Radix to Base UI (#32124)
* refactor(ui): switch shadcn primitives from Radix to Base UI shadcn made Base UI the default primitive library in July 2026 and our only shadcn component so far is the Button canary, so this is the last cheap moment to switch before the primitives phase adds the full set. components.json style moves from new-york (a legacy alias that resolves to the Radix variant) to base-vega. Button is regenerated from the base-vega registry with the same local adaptations as before: cva beta object form via lib/cva.config and a React 18 forwardRef wrapper. The polymorphic asChild prop becomes Base UI's render prop. radix-ui is replaced by @base-ui/react 1.6.0. Base UI optionally peers on date-fns 4 while tremor pins 3, so date-fns is bumped to 4.4.0 with an npm override; our only usage (add) is API-identical and the override can go away when tremor does. * refactor(ui): convert chat UI shadcn components from Radix to Base UI The chat UI migration landed 11 components/ui files generated against the old Radix registry config after this branch cut over to Base UI, which would have left them importing a deleted package. All 11 (dialog, alert-dialog, select, popover, tooltip, tabs, switch, scroll-area, collapsible, separator, label) are regenerated from the base-vega registry, with the repo conventions re-applied where relevant (cva beta object form from lib/cva.config in tabs; the Button canary keeps its React 18 forwardRef adaptation). Chat feature call sites move from the Radix asChild pattern to Base UI's render prop, and TooltipProvider delayDuration becomes delay. * fix(ui): restore security override pins clobbered by the date-fns override The date-fns 4 override was written by replacing the whole overrides object, dropping the ten security pins (prismjs, js-yaml, glob, minimatch, lodash, ws, braces, axios, postcss, esbuild) that keep patched versions in the lockfile; osv-scan caught the vulnerable versions resurfacing. Restores the pins alongside date-fns and regenerates the lockfile. * test(ui): pin tremor DateRangePicker behavior on date-fns 4 The date-fns 4 override forces react-day-picker 8 (authored against v3) onto v4 at runtime, which a build or lint pass cannot validate. This renders the shared UsageDatePicker wrapper, opens the calendar, checks the month grid, and selects a day, so a date-fns API break in the tremor date path fails tests instead of throwing in production. Delete alongside the override when tremor is removed. * fix(ui): close the alert dialog when AlertDialogAction is clicked The base-vega registry template renders AlertDialogAction as a plain Button with no Close binding, so confirm buttons fired their onClick but left the dialog open; both consumers (conversation delete, MCP credential revoke) were written against the Radix semantics where Action dismisses on click. Binds Action to AlertDialogPrimitive.Close via the render prop, mirroring AlertDialogCancel, and pins the behavior with a test so a future shadcn add --overwrite cannot silently reintroduce the template's non-closing Action. |
||
|
|
e06adb5588
|
feat(ui): re-add chat UI, allow simple UI for MCP OBO auth (#31893)
Some checks are pending
GitHub Actions Security Analysis / zizmor (push) Waiting to run
|
||
|
|
27069bd74f
|
feat(ui): shadcn migration foundation: Tailwind v4, shadcn init, antd cascade fix (#31995)
* feat(ui): shadcn migration foundation: Tailwind v4, shadcn init, antd cascade fix
Upgrade the dashboard from Tailwind v3 to v4 with CSS-first config: the
official upgrade codemod renamed utilities across 151 files, and
tailwind.config.js (plus the dead tailwind.config.ts) is replaced by
@theme tokens, @source globs, and @plugin directives in globals.css. The
Tremor safelist becomes @source inline patterns and the legacy tremor
theme tokens carry over verbatim. ui_colors.json was build-time only and
fed the dying Tremor palette, so its brand values are inlined and the
file removed; runtime theming replaces that path next.
shadcn is initialized with a hand-authored components.json (rsc,
cssVariables, baseColor gray) pointing utils at the existing
lib/cva.config.ts, which now exports cn (cva beta cx + twMerge) instead
of adding class-variance-authority as a second variant library. The two
ad-hoc cn helpers fold into it. Button lands as the canary primitive,
adapted to cva beta and React 18 forwardRef, with tests covering the
variant, twMerge, asChild, and ref seams. --radius is 0.5rem so the
shadcn radius scale reproduces Tailwind defaults and legacy rounded-*
classes render unchanged.
antd v5 emits unlayered CSS-in-JS that would beat every layered v4
utility, so AntdGlobalProvider now wraps the app in StyleProvider layer
and ConfigProvider cssVar, and globals.css declares
@layer theme, base, antd, components, utilities. antd wins over
preflight but yields to utilities, which is what lets migrated shadcn
pages coexist with legacy antd pages. Preflight stays global with the
three v3 behaviors pinned (default border color, button cursor,
placeholder color).
* fix(ui): restore tremor opacity tints removed by tailwind v4
Tailwind v4 removed the *-opacity-* utilities, but the precompiled
@tremor/react dist still composes them with shade-500 palette classes
(bg-opacity-10 over bg-<color>-500 etc.), so Badge, BadgeDelta, Callout,
light Icon and Button, BarList, and ProgressBar lost their tints and
rendered solid 500-shade fills. Adversarial review caught it; the
original smoke pages only exercised antd Tags.
tremor-v3-compat.css restores exactly the pairs tremor emits: for each
of the 22 safelisted colors, bg-opacity-{10,20,40}, hover/group-hover
bg-opacity-{20,30}, and ring-opacity-{20,40} against the -500 shade,
via color-mix into the utilities layer. Tremor's colorPalette maps both
background and iconRing to 500, so the -500 pairing covers every
composition in the dist; dark: variants are inert until dark mode ships.
The shim dies with @tremor/react at the end of the migration.
The upgrade codemod also missed two hand-rolled modal scrims using
bg-black bg-opacity-{30,50} (solid black under v4); now bg-black/30 and
bg-black/50. Removed the docker/build_admin_ui.sh copy of
enterprise_colors.json into the deleted ui_colors.json; that build-time
rebrand path is retired and its runtime replacement lands with the
theming phase.
* fix(ui): pair ring-opacity-40 with shade 300 in tremor compat shim
Tremor's colorPalette maps ring to shade 300, and the only consumer of
ring-opacity-40 (Icon variant outlined) composes it with that shade,
so the shade-500 rows were dead and outlined icon rings would render
at full opacity. Latent today (no dashboard usage of the outlined
variant); caught by adversarial review. ring-opacity-20 stays at 500
(iconRing), matching Badge and BadgeDelta.
|
||
|
|
2a9dbc4c0d
|
chore(ui): remove unused dep, delete dead file, and unblock knip (#31933)
Knip flagged remark-gfm as unused and date-fns as imported-but-undeclared, so drop remark-gfm (which prunes its transitive markdown subtree from the lockfile) and declare date-fns, which keyExpiryUtils.ts imports but only received transitively. Also delete the dead memory/components/index.tsx barrel, since nothing imports it once the page pulls MemoryView from its module directly Knip itself could not run: its Playwright plugin imports every config referenced by a --config flag in package.json scripts, and migration.serverRootPath.config.ts threw at import time when SERVER_ROOT_PATH was unset. Move that guard into a config-specific globalSetup so importing the config is side-effect-free; the check still fires loudly before any test runs when the prefix is missing |
||
|
|
f2f6cacb19
|
feat(ui): track frontend lint counts in a committed snapshot (#31157)
* feat(ui): track frontend lint counts in a committed snapshot Persist the eslint budget-rule counts (no-explicit-any, complexity, max-depth) to eslint-metrics.json so the trend is queryable straight from git history and can later feed a dashboard. A CI drift check regenerated from the same lint report keeps the snapshot honest, so a PR that shifts a count has to run npm run lint:metrics and commit it * fix(ui): harden lint-metrics drift check and eslint failure handling Make the drift comparison symmetric over the union of committed and actual keys so a phantom rule left in eslint-metrics.json (for example after a rule is dropped from eslint-budgets.json) is caught instead of silently passing. Only swallow eslint's lint-errors exit code in the generator and rethrow anything else, so a fatal eslint failure surfaces its real output rather than a confusing ENOENT on the missing report |