diff --git a/ui/litellm-dashboard/package-lock.json b/ui/litellm-dashboard/package-lock.json index 1f459bc50ea..0b71b51dc3f 100644 --- a/ui/litellm-dashboard/package-lock.json +++ b/ui/litellm-dashboard/package-lock.json @@ -13,6 +13,7 @@ "@headlessui/tailwindcss": "0.2.2", "@heroicons/react": "1.0.6", "@hookform/resolvers": "5.4.0", + "@shadcn/react": "0.3.1", "@tanstack/react-pacer": "0.22.1", "@tanstack/react-query": "5.100.7", "@tanstack/react-table": "8.21.3", @@ -2990,6 +2991,24 @@ "dev": true, "license": "MIT" }, + "node_modules/@shadcn/react": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@shadcn/react/-/react-0.3.1.tgz", + "integrity": "sha512-2gOR0HDMtWeRsCZfNDaU0YDFdgH3zsDQ6lz67Fv/y/qjY9y+R8kJqAn6q56phqv7/zHi0wqURjntrNO9zL7vnQ==", + "license": "MIT", + "peerDependencies": { + "@types/react": ">=19", + "react": ">=19" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "react": { + "optional": true + } + } + }, "node_modules/@standard-schema/spec": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", diff --git a/ui/litellm-dashboard/package.json b/ui/litellm-dashboard/package.json index 9786d5c1d6e..233a0e63881 100644 --- a/ui/litellm-dashboard/package.json +++ b/ui/litellm-dashboard/package.json @@ -29,6 +29,7 @@ "@headlessui/tailwindcss": "0.2.2", "@heroicons/react": "1.0.6", "@hookform/resolvers": "5.4.0", + "@shadcn/react": "0.3.1", "@tanstack/react-pacer": "0.22.1", "@tanstack/react-query": "5.100.7", "@tanstack/react-table": "8.21.3", diff --git a/ui/litellm-dashboard/src/app/(dashboard)/hooks/proxySettings/useProxySettings.ts b/ui/litellm-dashboard/src/app/(dashboard)/hooks/proxySettings/useProxySettings.ts index 7925af223ae..b7b28c47f9c 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/hooks/proxySettings/useProxySettings.ts +++ b/ui/litellm-dashboard/src/app/(dashboard)/hooks/proxySettings/useProxySettings.ts @@ -1,4 +1,4 @@ -import { fetchProxySettings } from "@/utils/proxyUtils"; +import { getProxyBaseUrl, getProxyUISettings } from "@/components/networking"; import { useQuery } from "@tanstack/react-query"; import { createQueryKeys } from "../common/queryKeysFactory"; @@ -18,11 +18,19 @@ const EMPTY_PROXY_SETTINGS: ProxySettings = { LITELLM_UI_API_DOC_BASE_URL: null, }; -export default function useProxySettings(accessToken: string | null): ProxySettings { - const { data } = useQuery({ - queryKey: [...proxySettingsKeys.all, accessToken], - queryFn: () => fetchProxySettings(accessToken), +export function useProxySettingsQuery(accessToken: string | null) { + const managementBaseUrl = getProxyBaseUrl(); + return useQuery({ + queryKey: [...proxySettingsKeys.all, managementBaseUrl, accessToken], + queryFn: () => { + if (getProxyBaseUrl() !== managementBaseUrl) throw new Error("Gateway changed while loading settings."); + return accessToken ? getProxyUISettings(accessToken) : null; + }, enabled: Boolean(accessToken), }); +} + +export default function useProxySettings(accessToken: string | null): ProxySettings { + const { data } = useProxySettingsQuery(accessToken); return data ?? EMPTY_PROXY_SETTINGS; } diff --git a/ui/litellm-dashboard/src/app/(dashboard)/layout.tsx b/ui/litellm-dashboard/src/app/(dashboard)/layout.tsx index 2e903c7b150..03612cee3c6 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/layout.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/layout.tsx @@ -13,6 +13,7 @@ import { NoRedisWarningBanner } from "@/components/NoRedisWarningBanner"; import { EnvCredentialLoginWarningBanner } from "@/components/EnvCredentialLoginWarningBanner"; import { LicenseExpiryBanner } from "@/components/LicenseExpiryBanner"; import { UserBanner } from "@/components/UserBanner"; +import LiteAdmin from "@/components/liteadmin/LiteAdmin"; import { UpgradeBanner } from "@/components/UpgradeBanner"; import { uiHref } from "@/utils/uiHref"; import { PluginModeProvider, usePluginMode } from "@/contexts/PluginModeContext"; @@ -141,6 +142,7 @@ function DashboardShell({ children }: { children: React.ReactNode }) {
{children}
+ ); diff --git a/ui/litellm-dashboard/src/app/(dashboard)/playground/components/chat_ui/ChatComposer.tsx b/ui/litellm-dashboard/src/app/(dashboard)/playground/components/chat_ui/ChatComposer.tsx index 4925e775ac7..5d2edbdfc94 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/playground/components/chat_ui/ChatComposer.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/playground/components/chat_ui/ChatComposer.tsx @@ -56,14 +56,16 @@ export function ChatComposer({ {showSuggestions && suggestions.length > 0 && (
{suggestions.map((suggestion) => ( - + {suggestion} + ))}
)} diff --git a/ui/litellm-dashboard/src/components/chat/ChatMessages.tsx b/ui/litellm-dashboard/src/components/chat/ChatMessages.tsx index 099105586c8..5b331cc81bc 100644 --- a/ui/litellm-dashboard/src/components/chat/ChatMessages.tsx +++ b/ui/litellm-dashboard/src/components/chat/ChatMessages.tsx @@ -4,8 +4,11 @@ import { Wrench, Copy, Check, Pencil } from "lucide-react"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"; import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible"; import { Button } from "@/components/ui/button"; +import { Bubble, BubbleContent } from "@/components/ui/Bubble"; +import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"; +import { Textarea } from "@/components/ui/textarea"; import React, { useEffect, useRef, useState } from "react"; -import ReactMarkdown from "react-markdown"; +import ReactMarkdown, { type Components } from "react-markdown"; import remarkGfm from "remark-gfm"; import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"; import { coy } from "react-syntax-highlighter/dist/esm/styles/prism"; @@ -64,6 +67,25 @@ function MarkdownCodeRenderer({ ); } +const markdownComponents: Components = { + code: MarkdownCodeRenderer, + pre: ({ node, ...props }) =>
,
+  p: ({ node, ...props }) => 

, + ul: ({ node, ...props }) =>

    , + ol: ({ node, ...props }) =>
      , + table: ({ node, ...props }) => , + thead: ({ node, ...props }) => , + tbody: ({ node, ...props }) => , + tr: ({ node, ...props }) => , + th: ({ node, ...props }) => , + td: ({ node, ...props }) => , +}; + +const markdownWithoutImages: Components = { + ...markdownComponents, + img: ({ alt }) => {alt || "Image omitted"}, +}; + interface UserBubbleProps { message: ChatMessage; onEdit?: (messageId: string, newContent: string) => void; @@ -75,6 +97,7 @@ function UserBubble({ message, onEdit, isStreaming }: UserBubbleProps) { const [editing, setEditing] = useState(false); const [editValue, setEditValue] = useState(message.content); const textareaRef = useRef(null); + const showEditAction = hovered && !isStreaming && Boolean(onEdit); useEffect(() => { if (editing && textareaRef.current) { @@ -113,8 +136,9 @@ function UserBubble({ message, onEdit, isStreaming }: UserBubbleProps) { return (
      -