diff --git a/ui/litellm-dashboard/eslint-suppressions.json b/ui/litellm-dashboard/eslint-suppressions.json index d7f71a5840d..0c5bcc65702 100644 --- a/ui/litellm-dashboard/eslint-suppressions.json +++ b/ui/litellm-dashboard/eslint-suppressions.json @@ -1055,9 +1055,6 @@ "no-nested-ternary": { "count": 2 }, - "no-restricted-imports": { - "count": 1 - }, "react-hooks/set-state-in-effect": { "count": 5 } @@ -1098,9 +1095,6 @@ "no-nested-ternary": { "count": 2 }, - "no-restricted-imports": { - "count": 1 - }, "react-hooks/immutability": { "count": 2 }, @@ -1115,9 +1109,6 @@ "no-nested-ternary": { "count": 4 }, - "no-restricted-imports": { - "count": 1 - }, "react-hooks/set-state-in-effect": { "count": 1 } @@ -1125,9 +1116,6 @@ "src/app/(dashboard)/playground/components/compareUI/components/ComparisonPanel.tsx": { "local/no-complex-jsx-arrow": { "count": 2 - }, - "no-restricted-imports": { - "count": 1 } }, "src/app/(dashboard)/playground/components/compareUI/components/MessageDisplay.tsx": { @@ -1135,21 +1123,11 @@ "count": 1 } }, - "src/app/(dashboard)/playground/components/compareUI/components/MessageInput.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, "src/app/(dashboard)/playground/components/compareUI/components/ModelSelector.tsx": { "no-restricted-imports": { "count": 2 } }, - "src/app/(dashboard)/playground/components/compareUI/components/UnifiedSelector.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, "src/app/(dashboard)/playground/components/complianceUI/ComplianceUI.tsx": { "local/no-complex-jsx-arrow": { "count": 2 @@ -3582,6 +3560,14 @@ "count": 1 } }, + "src/components/ui/slider.tsx": { + "local/filename-pascal-case": { + "count": 1 + }, + "no-nested-ternary": { + "count": 1 + } + }, "src/components/ui/switch.tsx": { "local/filename-pascal-case": { "count": 1 diff --git a/ui/litellm-dashboard/src/app/(dashboard)/playground/components/chat_ui/AgentBuilderView.tsx b/ui/litellm-dashboard/src/app/(dashboard)/playground/components/chat_ui/AgentBuilderView.tsx index d4333b95c62..44995707b10 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/playground/components/chat_ui/AgentBuilderView.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/playground/components/chat_ui/AgentBuilderView.tsx @@ -1,16 +1,24 @@ "use client"; -import { - CommentOutlined, - DeleteOutlined, - ExperimentOutlined, - LinkOutlined, - PlusOutlined, - RobotOutlined, - SaveOutlined, -} from "@ant-design/icons"; -import { Button, Input, Modal, Select, Spin, Tabs } from "antd"; +import { Bot, FlaskConical, Link as LinkIcon, MessageSquare, Plus, Save, Trash2 } from "lucide-react"; import React, { useCallback, useEffect, useState } from "react"; +import { + AlertDialog, + AlertDialogAction, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, +} from "@/components/ui/alert-dialog"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { Textarea } from "@/components/ui/textarea"; +import { UiLoadingSpinner } from "@/components/ui/ui-loading-spinner"; +import { MultiSelect } from "@/components/shared/MultiSelect"; +import { useVisitedTabs } from "@/hooks/useVisitedTabs"; import CodeBlock from "@/components/CodeBlock"; import NotificationsManager from "@/components/molecules/notifications_manager"; import { @@ -27,8 +35,6 @@ import { fetchAvailableModels, ModelGroup } from "@/components/llm_calls/fetch_m import ComplianceUI from "../complianceUI/ComplianceUI"; import ChatUI from "./ChatUI"; -const { TextArea } = Input; - export interface AgentBuilderViewProps { accessToken: string | null; token: string | null; @@ -45,6 +51,8 @@ export interface AgentBuilderViewProps { const NEW_AGENT_ID = "__new__"; +type AgentTab = "configure" | "chat" | "test" | "connect"; + function getConnectTabBaseUrl( proxySettings: AgentBuilderViewProps["proxySettings"], customProxyBaseUrl?: string, @@ -116,7 +124,7 @@ function ConnectTabContent({ Create a virtual key that can only call this agent. The key will be scoped to you (user_id) and restricted to the model {agentName}.

- {disabledPersonalKeyCreation && ( @@ -190,7 +198,12 @@ export default function AgentBuilderView({ const [modelGroups, setModelGroups] = useState([]); const [loadingAgents, setLoadingAgents] = useState(true); const [selectedId, setSelectedId] = useState(null); - const [activeTab, setActiveTab] = useState<"configure" | "chat" | "test" | "connect">("configure"); + const [activeTab, setActiveTab] = useState("configure"); + const { onTabChange, hasVisited } = useVisitedTabs("configure"); + const goToTab = (tab: AgentTab) => { + setActiveTab(tab); + onTabChange(tab); + }; const [creatingKey, setCreatingKey] = useState(false); const [createdKeyValue, setCreatedKeyValue] = useState(null); @@ -207,6 +220,7 @@ export default function AgentBuilderView({ const [saving, setSaving] = useState(false); const [deleting, setDeleting] = useState(false); + const [confirmingDelete, setConfirmingDelete] = useState(false); const effectiveApiKey = apiKey || accessToken || ""; const selectedAgent = @@ -314,7 +328,7 @@ export default function AgentBuilderView({ setDraftTemperature(0.7); setDraftMaxTokens(4096); setDraftTools([]); - setActiveTab("configure"); + goToTab("configure"); }; const handleSaveAgent = async () => { @@ -344,7 +358,7 @@ export default function AgentBuilderView({ ? list.find((a) => getAgentModelId(a) === createdId) ?? list.find((a) => a.model_name === draftName.trim()) : list.find((a) => a.model_name === draftName.trim()); setSelectedId(created ? getAgentSelectionKey(created) : list[0] ? getAgentSelectionKey(list[0]) : null); - setActiveTab("chat"); + goToTab("chat"); } catch (e) { NotificationsManager.fromBackend("Failed to save agent"); } finally { @@ -411,27 +425,24 @@ export default function AgentBuilderView({ const handleDeleteAgent = () => { if (!selectedAgent || !selectedAgentModelId || !accessToken) return; - Modal.confirm({ - title: "Delete agent", - content: `Are you sure you want to delete "${selectedAgent.model_name}"? This cannot be undone.`, - okText: "Delete", - okType: "danger", - cancelText: "Cancel", - onOk: async () => { - setDeleting(true); - try { - await modelDeleteCall(accessToken, selectedAgentModelId); - NotificationsManager.success("Agent deleted"); - const list = await loadAgents(); - const remaining = list.filter((a) => getAgentModelId(a) !== selectedAgentModelId); - setSelectedId(remaining.length > 0 ? getAgentSelectionKey(remaining[0]) : null); - } catch (e) { - NotificationsManager.fromBackend("Failed to delete agent"); - } finally { - setDeleting(false); - } - }, - }); + setConfirmingDelete(true); + }; + + const handleConfirmDelete = async () => { + if (!selectedAgent || !selectedAgentModelId || !accessToken) return; + setDeleting(true); + try { + await modelDeleteCall(accessToken, selectedAgentModelId); + NotificationsManager.success("Agent deleted"); + const list = await loadAgents(); + const remaining = list.filter((a) => getAgentModelId(a) !== selectedAgentModelId); + setSelectedId(remaining.length > 0 ? getAgentSelectionKey(remaining[0]) : null); + } catch (e) { + NotificationsManager.fromBackend("Failed to delete agent"); + } finally { + setDeleting(false); + setConfirmingDelete(false); + } }; if (!accessToken || !userID || !userRole) { @@ -446,13 +457,8 @@ export default function AgentBuilderView({
Agent Builder {isNewAgent ? ( - ) : ( @@ -460,7 +466,7 @@ export default function AgentBuilderView({ )}
- + Agent Builder is experimental and may change or be removed without notice. We’d love your feedback—email us at{" "} @@ -477,12 +483,14 @@ export default function AgentBuilderView({
Agents -
{loadingAgents ? ( -
- +
+
) : ( <> @@ -509,7 +517,7 @@ export default function AgentBuilderView({ onClick={handleAddAgent} className="mb-1 w-full rounded-md border border-dashed border-gray-300 px-3 py-2 text-left text-sm text-gray-500 hover:border-blue-400 hover:bg-blue-50/50 hover:text-gray-700" > - New agent + New agent )} @@ -526,227 +534,231 @@ export default function AgentBuilderView({ {(selectedId !== null || isNewAgent) && ( <> setActiveTab(k as "configure" | "chat" | "test" | "connect")} - className="flex-1 overflow-hidden [&_.ant-tabs-content]:h-full [&_.ant-tabs-tabpane]:h-full [&_.ant-tabs-nav]:pl-4" - items={[ - { - key: "configure", - label: ( - - Configure - - ), - children: ( -
- {isNewAgent || selectedAgent ? ( -
- {!selectedAgentModelId && selectedAgent && ( -
- This agent cannot be updated or deleted here (missing model id). Manage it from Models - & Endpoints. -
- )} -
- - setDraftName(e.target.value)} - placeholder="My Agent" - /> -
-
- -