Skip to content

feat(ui): migrate chat UI from antd to shadcn/ui + add key management and usage panels - #32074

Merged
yuneng-berri merged 9 commits into
litellm_internal_stagingfrom
litellm_chat-keys-usage
Jul 4, 2026
Merged

feat(ui): migrate chat UI from antd to shadcn/ui + add key management and usage panels#32074
yuneng-berri merged 9 commits into
litellm_internal_stagingfrom
litellm_chat-keys-usage

Conversation

@devin-ai-integration

@devin-ai-integration devin-ai-integration Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Relevant issues

Chat UI feature request from Slack thread

Pre-Submission checklist

  • I have added meaningful tests
  • My PR passes all CI/CD checks (e.g., lint, format, unit tests)
  • My PR's scope is as isolated as possible; it only solves 1 specific problem
  • I have requested a Greptile review by commenting @greptileai and received a Confidence Score of at least 4/5 before requesting a maintainer review

Screenshots / Proof of Fix

Screenshots below are from the initial antd -> shadcn migration commit. Several things they show have since changed in follow-up commits on this same branch: comparison mode and the sidebar collapse toggle were removed, the model selector moved from a standalone top bar into the chat composer, and Chats/Integrations/Credentials/API Keys/Usage are now real routes (/chat, /chat/integrations, /chat/credentials, /chat/api-keys, /chat/usage) instead of client-only tab state.

Main chat view - sidebar with all tabs (Chats, Apps, Credentials, API Keys, Usage), greeting, input bar with model selector and prompt suggestions:

Main chat view

API Keys panel - key table with rotate functionality (enterprise-gated):

API Keys panel

Usage panel - spend and request stats with time range selector (7d/30d/90d):

Usage panel

MCP Apps panel - server browsing with All/Connected tabs and search:

MCP Apps panel

Credentials panel - OAuth connections overview:

Credentials panel

Model selector popover - search + scrollable model list from Popover + ScrollArea (now lives in the composer, not the top bar; single-select only, comparison mode removed):

Model selector

To verify locally:

  1. Start the proxy with python litellm/proxy/proxy_cli.py --config litellm/proxy/dev_config.yaml --detailed_debug --reload
  2. Navigate to http://localhost:4000/ui/chat
  3. Verify the sidebar shows Chats, Integrations, Credentials, API Keys, Usage, each with its own URL (/ui/chat, /ui/chat/integrations, etc.) that survives a hard refresh
  4. Click API Keys to see key table with rotate button (enterprise only)
  5. Click Usage to see spend/request stats
  6. In a chat, open the model picker from the composer (not a top bar), confirm it's single-select and the list actually scrolls
  7. Click the topnav "Chat" / "AI Gateway" switcher and confirm it navigates correctly in both directions

Type

🆕 New Feature
🧹 Refactoring

Changes

Migrates the entire chat UI component tree from Ant Design to shadcn/ui (Radix + Tailwind). Every antd import in src/components/chat/ is replaced with shadcn primitives and Lucide React icons. A design.md spec was written first and guides all styling decisions.

Key changes per component (initial migration):

  • ChatPage.tsx: antd Tooltip/Popover/Skeleton -> shadcn equivalents; all inline style={} replaced with Tailwind classes; sidebar nav uses TooltipProvider when collapsed; model selector uses Popover+PopoverTrigger+PopoverContent+ScrollArea; input bar MCP picker same pattern
  • ConversationList.tsx: antd Modal -> shadcn Dialog for Cmd+K search; Popconfirm -> AlertDialog for delete confirmation; list wrapped in ScrollArea; hover actions with Tooltip
  • ChatMessages.tsx: antd Tooltip/Tag -> shadcn Tooltip/Badge; Collapsible for tool call cards; Copy/Check icon toggle on copy button
  • MCPAppsPanel.tsx: antd Input/Spin/Tabs -> shadcn Tabs+TabsList+TabsTrigger+TabsContent; search input with Search icon; Switch for server connect; Skeleton for loading
  • MCPConnectPicker.tsx: antd Switch/Spin -> shadcn Switch + Loader2
  • MCPCredentialsTab.tsx: antd Table/Modal/Spin -> shadcn Table + Badge + Loader2; Trash2 icon for delete action
  • KeysPanel.tsx (new): API key table with inline budget/expiry badges; key rotation dialog gated behind premiumUser; manual FormState + validate() replaces antd Form; RefreshCw/Copy/Check/KeyRound icons
  • UsagePanel.tsx (new): spend and request stats with sparkline bar charts; segmented time range selector (7d/30d/90d); stat cards using Tailwind border/rounded utilities; Loader2 for loading, BarChart3 for empty state

Also installs 15 shadcn/ui components (dialog, alert-dialog, popover, tooltip, table, badge, tabs, switch, scroll-area, skeleton, separator, input, label, select, collapsible) and includes design.md as the design specification.

Follow-up changes (this branch, after the initial migration)

Routing. ChatPage.tsx is split into app/chat/layout.tsx (auth/feature-flag gate, topnav, sidebar shell), contexts/ChatShellContext.tsx (state shared across routes: MCP server selection, conversation history), components/chat/ChatShell.tsx (sidebar nav), and app/chat/page.tsx (the conversation view). Chats, Integrations, Credentials, API Keys, and Usage are now real nested routes (/chat, /chat/integrations, /chat/credentials, /chat/api-keys, /chat/usage) instead of client-only tab state, so each is bookmarkable and survives a hard reload. Along the way this fixes a real bug where sending a message navigated to /ui/chat instead of /chat in dev and 404'd, by switching to the same migratedHref helper the rest of the dashboard already uses.

Design-system audit. Added design.md (rewritten against the actual tokens available in this app's globals.css and components/ui/*, since the original spec recommended a sidebar background/active-state combination that resolves to the same color and is invisible) and AGENTS.md (decision trees, pre-commit checklist) to src/components/chat/, then audited every component in the directory against it: remaining raw <button> elements replaced with shadcn Button, spinners replaced with Skeleton for list/table loading states, a dark-mode contrast bug in the Integrations grid (cards used bg-background, identical to the page background in dark mode), Badge variants and status colors aligned with the documented semantics, and the sidebar's active-nav styling switched to the sidebar-* tokens instead of generic accent/secondary tokens that collapse to the same value in this theme.

Model picker. Disabled model comparison mode and multi-select in favor of a single active model, and moved the picker from a standalone top bar into the chat composer.

Sidebar cleanup. Removed the collapse toggle and the non-functional "Search chats" entry, moved "New Chat" to the top of the sidebar, renamed the conversation list's "Today" group to "Recents".

Scroll bug. The model picker's dropdown list, and the sidebar's conversation list, were unscrollable: their containers used max-height instead of an explicit height, which doesn't count as a definite size for the percentage-height Radix ScrollArea viewport to resolve against, so the viewport silently expanded to full content height instead of clipping, and scroll events fell through to the page behind it.

Other fixes: topnav's "AI Gateway" / "Chat" view switcher didn't actually navigate anywhere when clicked from the chat route (it only updated dashboard SPA state, which the chat route isn't part of); a pre-v0 feature banner was added across all chat routes.

Link to Devin session: https://app.devin.ai/sessions/397c201dbd15489c85e5cf3f45757da6
Requested by: @krrish-berri-2

Replace all Ant Design components (Table, Modal, Popover, Tooltip, Skeleton,
Select, Spin, Popconfirm, Switch) with shadcn/ui primitives and Lucide React
icons across all chat components:

- ChatPage: sidebar, model selector, input bar, comparison mode
- ConversationList: search dialog, delete confirmation, scroll area
- ChatMessages: message bubbles, tool cards, copy button
- MCPAppsPanel: list/detail views, OAuth2 flow, tabs
- MCPConnectPicker: server toggle switches
- MCPCredentialsTab: credentials table with delete
- KeysPanel: API key management with rotation dialog (enterprise)
- UsagePanel: spend/request stats with sparkline charts

Add design.md as the design specification guiding the migration.
Install 15 shadcn/ui components (dialog, popover, tooltip, table, etc.).
All existing functionality preserved; no backend changes.

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@krrish-berri-2 krrish-berri-2 self-assigned this Jul 3, 2026
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

@CLAassistant

CLAassistant commented Jul 3, 2026

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you all sign our Contributor License Agreement before we can accept your contribution.
0 out of 2 committers have signed the CLA.

❌ krrish-berri
❌ krrish-berri-2
You have signed the CLA already but the status is still pending? Let us recheck it.

@codecov

codecov Bot commented Jul 3, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 90.00000% with 2 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
litellm/responses/mcp/mcp_streaming_iterator.py 90.00% 2 Missing ⚠️

📢 Thoughts on this report? Let us know!

@greptile-apps

greptile-apps Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR migrates the chat UI from Ant Design to shadcn/ui (Radix + Tailwind), refactors the routing from a single-page client-tab model into nested Next.js routes (/chat, /chat/integrations, /chat/credentials, /chat/api-keys, /chat/usage), and fixes a multi-round MCP tool-call bug where a model retrying a failed tool in a second round would silently drop that retry instead of executing it.

  • UI migration: Every antd import in src/components/chat/ is replaced with shadcn/ui primitives; 15 new shadcn components are installed; ChatPage.tsx is split into layout.tsx (auth/feature-flag gate), ChatShell.tsx (sidebar), ChatShellContext.tsx (shared state), and page.tsx (conversation view).
  • New panels: KeysPanel.tsx adds an API-key table with an enterprise-gated rotate dialog; UsagePanel.tsx adds spend/request stats with sparkline bar charts and a time-range selector.
  • Python fix: MCPEnhancedStreamingIterator is extended with a tool_call_round counter, a _tool_results_for_response guard (prevents stale tool results from re-triggering follow-ups), and a MAX_MCP_TOOL_CALL_ROUNDS = 5 cap that drops tools from the request when the limit is hit, along with regression tests covering both the retry scenario and the cap.

Confidence Score: 5/5

Safe to merge. The Python multi-round MCP fix is well-guarded by two new regression tests, the UI migration is a clean antd-to-shadcn swap without behavioral regressions, and the routing refactor is covered by new layout and shell tests.

Both the Python streaming-iterator changes and the UI routing refactor are logically sound. The _tool_results_for_response guard correctly prevents stale tool results from re-triggering follow-up calls, and the MAX_MCP_TOOL_CALL_ROUNDS cap properly strips tools from the capped request to force a text response. The UI changes replace antd components 1:1 with shadcn equivalents and add real nested routes backed by tests. No correctness or security issues were found in the changed code.

The new ChatConversationPage in page.tsx is the most complex untested surface — its streaming, session-reset, and edit/retry paths have no unit coverage. Worth revisiting before this route is considered production-ready.

Important Files Changed

Filename Overview
litellm/responses/mcp/mcp_streaming_iterator.py Adds multi-round MCP tool-call support with a per-round counter, a stale-guard to prevent reuse of previous round's tool results, and a configurable cap that forces a text-only follow-up; logic is clearly documented and well-tested.
tests/test_litellm/responses/mcp/test_mcp_streaming_iterator.py New mock-only test file covering the retry regression and the round-cap behavior; all external I/O is patched via monkeypatch and AsyncMock, no real network calls.
ui/litellm-dashboard/src/app/chat/layout.tsx New layout that handles auth/feature-flag gating (previously in page.tsx), properly wraps in Suspense for useSearchParams compatibility, and delegates rendering to ChatShell.
ui/litellm-dashboard/src/app/chat/page.tsx Fully replaced with a 590-line ChatConversationPage containing all streaming, model selection, session management, and scroll-lock logic; complex component has no unit tests, though the routing shell logic moved to layout.test.tsx.
ui/litellm-dashboard/src/contexts/ChatShellContext.tsx New React context cleanly separates shared shell state (MCP server selection, conversation list) from per-route auth state; correctly requires Suspense wrapper via useSearchParams.
ui/litellm-dashboard/src/components/chat/ChatShell.tsx New sidebar component with real Next.js routes and active-state detection; strips trailing slash before matching to handle edge cases; well-tested.
ui/litellm-dashboard/src/components/chat/KeysPanel.tsx New key-management panel with rotation dialog, inline validation via DURATION_RE regex, budget/expiry badges, and enterprise gating; straightforward React Query + Dialog pattern.
ui/litellm-dashboard/src/components/chat/UsagePanel.tsx New usage panel with stat cards, sparkline bars, and a 7d/30d/90d time-range toggle; date range recomputed on render but correctly keyed by timeRange in React Query cache.
ui/litellm-dashboard/src/components/Navbar/ViewSwitcher.tsx Adds pathname-aware active-state logic for the chat route and a hard navigation (window.location.assign) when switching away from chat, correctly bypassing the dashboard SPA's mode state.
ui/litellm-dashboard/src/components/chat/ConversationList.tsx antd Modal/Popconfirm replaced with shadcn Dialog/AlertDialog; hover actions now use Tailwind group-hover; onNewChat prop removed; ScrollArea with h-0+flex-1 fixes unscrollable sidebar bug.

Reviews (2): Last reviewed commit: "fix(ui): forward ref on shadcn Input so ..." | Re-trigger Greptile

Comment thread ui/litellm-dashboard/src/components/ui/input.tsx Outdated
@@ -0,0 +1,360 @@
# LiteLLM Chat UI Design System

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Design spec committed to source tree

Per the team's custom rule, documentation files should live in the litellm-docs repo rather than in this codebase. design.md is an internal spec used to guide the UI migration, but committing it here adds noise to the source tree without benefiting runtime or tooling. Consider removing it from the PR (or moving it out of the src/ directory entirely, e.g. into a top-level docs/ directory that is explicitly excluded from builds).

Rule Used: Prevent documentation from being added - needs to ... (source)

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

krrish-berri and others added 5 commits July 3, 2026 20:23
…n badge and tabs

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

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Moves Chats/Integrations/Credentials/API Keys/Usage from client-side
tab state to real nested routes (/chat, /chat/integrations,
/chat/credentials, /chat/api-keys, /chat/usage) so each is bookmarkable
and survives a hard reload. Extracts the chat sidebar into ChatShell
and shared state (MCP server selection, conversation history) into
ChatShellContext, both consumed via the new app/chat/layout.tsx.

Along the way: fixes conversation URLs pointing at the wrong path
(/ui/chat instead of /chat in dev, which 404'd after sending the first
message) by reusing the existing migratedHref helper instead of a
one-off uiConfig-based path; fixes the topnav view-switcher always
showing "AI Gateway" as selected even while on the chat route; and
cleans up several shadcn/tailwind styling bugs introduced by the antd
migration (boxed tab outline instead of underline, model-selector
dropdown overflowing its popover, sidebar nav labels centered instead
of left-aligned, duplicate logo, dead non-interactive controls).
"AI Gateway" in the topnav view switcher only called setMode(), which
is meaningful inside the dashboard SPA shell but a no-op on /chat,
which lives outside it (only "Chat" had a real navigation). Now
switching modes from the chat route does a real navigation back to
the dashboard root.

Also adds a persistent banner across all chat routes flagging it as a
pre-v0 feature not for production use, with a feedback link.
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

Test Results: Chat UI shadcn Migration

Tested locally against dev server (localhost:3001) with auth bypassed. All 7 tests passed.

Test Results
  • Chat page renders with shadcn sidebar - passed
  • Sidebar collapse/expand with tooltips - passed
  • API Keys panel (/chat/api-keys) - passed
  • Usage panel (/chat/usage) - passed
  • Integrations + Credentials navigation - passed
  • Model selector popover + input bar - passed
  • No antd component classes in DOM - passed
Key Evidence

Main chat view - sidebar with all nav items, greeting, input bar, suggestion chips:

Main chat view

Collapsed sidebar with shadcn Tooltip on hover:

Collapsed sidebar tooltip

API Keys panel - empty state after loading:

API Keys panel

Usage panel - time range selector with 30d active:

Usage panel

Integrations panel - MCP Servers with tabs and search:

Integrations

Limitations

No live proxy backend, so could not verify: real key data in table, key rotation with actual regeneration, usage sparkline charts with real data, model list population. antd CSS variables (--ant-*) remain in global theme as expected; only component-level antd usage was migrated.

Devin session

Establishes a real design.md/AGENTS.md for the chat UI (tokens,
component patterns, decision trees) after several rounds of hand-rolled
Tailwind shipping invisible or broken states, then audits every
component in the directory against it: raw <button>s replaced with
shadcn Button throughout, spinners replaced with Skeleton for list/table
loading states, dark-mode contrast bugs fixed (MCPAppsPanel cards were
bg-background instead of bg-card, identical to the page background in
dark mode), Badge variants and status colors aligned with the documented
semantics, and the sidebar's active-nav-item styling switched to the
purpose-built sidebar-* tokens instead of the generic accent/secondary
tokens that collapse to the same value in this theme.

Also: disables model comparison mode and multi-select in favor of a
single active model, moves the model picker from a standalone top bar
into the composer, removes the sidebar collapse toggle and the
non-functional "Search chats" entry, and renames the conversation list's
"Today" group to "Recents".

Fixes a real scroll bug: the model picker's dropdown list was
unscrollable because its container used max-height instead of an
explicit height, which doesn't count as a definite size for the
percentage-height Radix ScrollArea viewport to resolve against — so the
viewport silently expanded to full content height instead of clipping,
and scroll events fell through to the page behind it. Same latent bug
fixed in the sidebar's conversation list.
…the stream

MCPEnhancedStreamingIterator only auto-executed one round of MCP tool calls.
When a model retried a tool (e.g. after an error) in its follow-up turn, that
second tool call was streamed but never executed, and the response ended with
no final text. Route follow-up calls back through the same completion-check
phase as the initial response, so further tool-call rounds are handled the
same way, capped at MAX_MCP_TOOL_CALL_ROUNDS to avoid an unbounded loop.
…act 18

Input didn't wrap its function component in React.forwardRef, so the ref
ConversationList passes for rename auto-focus/select silently never attached
under React 18 (function components need forwardRef to receive a ref; that
requirement is dropped in React 19, but this app is on 18.3.1).
@krrish-berri-2

Copy link
Copy Markdown
Contributor

@greptile review

@yuneng-berri
yuneng-berri merged commit 47f493a into litellm_internal_staging Jul 4, 2026
121 checks passed
@yuneng-berri
yuneng-berri deleted the litellm_chat-keys-usage branch July 4, 2026 17:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants