feat(ui): migrate chat UI from antd to shadcn/ui + add key management and usage panels - #32074
Conversation
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>
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
|
|
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
Greptile SummaryThis 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 (
Confidence Score: 5/5Safe 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.
|
| 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
| @@ -0,0 +1,360 @@ | |||
| # LiteLLM Chat UI Design System | |||
There was a problem hiding this comment.
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!
…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).
…tellm_chat-keys-usage
"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.
Test Results: Chat UI shadcn MigrationTested locally against dev server (localhost:3001) with auth bypassed. All 7 tests passed. Test Results
Key EvidenceMain chat view - sidebar with all nav items, greeting, input bar, suggestion chips: Collapsed sidebar with shadcn Tooltip on hover: API Keys panel - empty state after loading: Usage panel - time range selector with 30d active: Integrations panel - MCP Servers with tabs and search: LimitationsNo 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 ( |
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).
|
@greptile review |
Relevant issues
Chat UI feature request from Slack thread
Pre-Submission checklist
@greptileaiand received a Confidence Score of at least 4/5 before requesting a maintainer reviewScreenshots / 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:
API Keys panel - key table with rotate functionality (enterprise-gated):
Usage panel - spend and request stats with time range selector (7d/30d/90d):
MCP Apps panel - server browsing with All/Connected tabs and search:
Credentials panel - OAuth connections overview:
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):
To verify locally:
python litellm/proxy/proxy_cli.py --config litellm/proxy/dev_config.yaml --detailed_debug --reload/ui/chat,/ui/chat/integrations, etc.) that survives a hard refreshType
🆕 New Feature
🧹 Refactoring
Changes
Migrates the entire chat UI component tree from Ant Design to shadcn/ui (Radix + Tailwind). Every
antdimport insrc/components/chat/is replaced with shadcn primitives and Lucide React icons. Adesign.mdspec was written first and guides all styling decisions.Key changes per component (initial migration):
antdTooltip/Popover/Skeleton-> shadcn equivalents; all inlinestyle={}replaced with Tailwind classes; sidebar nav usesTooltipProviderwhen collapsed; model selector usesPopover+PopoverTrigger+PopoverContent+ScrollArea; input bar MCP picker same patternantdModal-> shadcnDialogfor Cmd+K search;Popconfirm->AlertDialogfor delete confirmation; list wrapped inScrollArea; hover actions withTooltipantdTooltip/Tag-> shadcnTooltip/Badge;Collapsiblefor tool call cards;Copy/Checkicon toggle on copy buttonantdInput/Spin/Tabs-> shadcnTabs+TabsList+TabsTrigger+TabsContent; search input withSearchicon;Switchfor server connect;Skeletonfor loadingantdSwitch/Spin-> shadcnSwitch+Loader2antdTable/Modal/Spin-> shadcnTable+Badge+Loader2;Trash2icon for delete actionpremiumUser; manualFormState+validate()replacesantdForm;RefreshCw/Copy/Check/KeyRoundiconsLoader2for loading,BarChart3for empty stateAlso installs 15 shadcn/ui components (
dialog,alert-dialog,popover,tooltip,table,badge,tabs,switch,scroll-area,skeleton,separator,input,label,select,collapsible) and includesdesign.mdas the design specification.Follow-up changes (this branch, after the initial migration)
Routing.
ChatPage.tsxis split intoapp/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), andapp/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/chatinstead of/chatin dev and 404'd, by switching to the samemigratedHrefhelper the rest of the dashboard already uses.Design-system audit. Added
design.md(rewritten against the actual tokens available in this app'sglobals.cssandcomponents/ui/*, since the original spec recommended a sidebar background/active-state combination that resolves to the same color and is invisible) andAGENTS.md(decision trees, pre-commit checklist) tosrc/components/chat/, then audited every component in the directory against it: remaining raw<button>elements replaced with shadcnButton, spinners replaced withSkeletonfor list/table loading states, a dark-mode contrast bug in the Integrations grid (cards usedbg-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 thesidebar-*tokens instead of genericaccent/secondarytokens 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-heightinstead of an explicitheight, which doesn't count as a definite size for the percentage-height RadixScrollAreaviewport 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