diff --git a/ui/litellm-dashboard/.eslintrc.json b/ui/litellm-dashboard/.eslintrc.json index b901c5faf6b..c15ba3e5ef7 100644 --- a/ui/litellm-dashboard/.eslintrc.json +++ b/ui/litellm-dashboard/.eslintrc.json @@ -59,6 +59,7 @@ "src/components/guardrails/content_filter/PatternTable.tsx", "src/components/guardrails/content_filter/ContentFilterDisplay.tsx", "src/components/prompts/prompt_editor_view/PromptEditorHeader.tsx", + "src/components/prompts/prompt_editor_view/VersionHistorySidePanel.tsx", "src/app/(dashboard)/teams/components/TeamsTable/ModelsCell.tsx", "src/components/prompts/prompt_editor_view/conversation_panel/MessageBubble.tsx", "src/components/playground/chat_ui/SessionManagement.tsx", diff --git a/ui/litellm-dashboard/src/components/prompts/prompt_editor_view/VersionHistorySidePanel.tsx b/ui/litellm-dashboard/src/components/prompts/prompt_editor_view/VersionHistorySidePanel.tsx index 5eddd59cdb2..92278da443d 100644 --- a/ui/litellm-dashboard/src/components/prompts/prompt_editor_view/VersionHistorySidePanel.tsx +++ b/ui/litellm-dashboard/src/components/prompts/prompt_editor_view/VersionHistorySidePanel.tsx @@ -1,9 +1,15 @@ -import { Drawer, List, Skeleton, Tag, Typography } from "antd"; +import { + Sheet, + SheetContent, + SheetHeader, + SheetTitle, +} from "@/components/ui/sheet"; +import { Badge } from "@/components/ui/badge"; +import { Skeleton } from "@/components/ui/skeleton"; +import { cn } from "@/lib/utils"; import React, { useEffect, useState } from "react"; import { getPromptVersions, PromptSpec } from "../../networking"; -const { Text } = Typography; - interface VersionHistorySidePanelProps { isOpen: boolean; onClose: () => void; @@ -25,33 +31,34 @@ const VersionHistorySidePanel: React.FC = ({ const [loading, setLoading] = useState(false); useEffect(() => { + const fetchVersions = async () => { + setLoading(true); + try { + const basePromptId = promptId.includes(".v") + ? promptId.split(".v")[0] + : promptId; + const response = await getPromptVersions(accessToken!, basePromptId); + setVersions(response.prompts); + } catch (error) { + console.error("Error fetching prompt versions:", error); + } finally { + setLoading(false); + } + }; + if (isOpen && accessToken && promptId) { fetchVersions(); } }, [isOpen, accessToken, promptId]); - const fetchVersions = async () => { - setLoading(true); - try { - // Strip .v suffix if present to get base ID for querying all versions - const basePromptId = promptId.includes(".v") ? promptId.split(".v")[0] : promptId; - const response = await getPromptVersions(accessToken!, basePromptId); - setVersions(response.prompts); - } catch (error) { - console.error("Error fetching prompt versions:", error); - } finally { - setLoading(false); - } - }; - const getVersionNumber = (prompt: PromptSpec) => { - // Use explicit version field if available, otherwise try to extract from litellm_params.prompt_id if (prompt.version) { return `v${prompt.version}`; } - - // Fallback: try to extract from litellm_params.prompt_id - const versionedId = (prompt.litellm_params as any)?.prompt_id || prompt.prompt_id; + + const versionedId = + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (prompt.litellm_params as any)?.prompt_id || prompt.prompt_id; if (versionedId.includes(".v")) { return `v${versionedId.split(".v")[1]}`; } @@ -67,73 +74,95 @@ const VersionHistorySidePanel: React.FC = ({ }; return ( - (!o ? onClose() : undefined)} + modal={false} > - {loading ? ( - - ) : versions.length === 0 ? ( -
No version history available.
- ) : ( - { - // Use version field for comparison since all items have the same prompt_id - const itemVersionNum = item.version || parseInt(getVersionNumber(item).replace('v', '')); - - // Extract version number from activeVersionId (may have .vX suffix) - let activeVersionNum: number | null = null; - if (activeVersionId) { - if (activeVersionId.includes('.v')) { - activeVersionNum = parseInt(activeVersionId.split('.v')[1]); - } else if (activeVersionId.includes('_v')) { - activeVersionNum = parseInt(activeVersionId.split('_v')[1]); - } - } - - // Default to latest (first item) if no activeVersionId - const isSelected = activeVersionNum ? itemVersionNum === activeVersionNum : index === 0; - - return ( -
onSelectVersion?.(item)} - > -
-
- - {getVersionNumber(item)} - - {index === 0 && Latest} -
- {isSelected && ( - - Active - - )} -
+ e.preventDefault()} + > + + Version History + + {loading ? ( +
+ {[1, 2, 3, 4].map((i) => ( + + ))} +
+ ) : versions.length === 0 ? ( +
+ No version history available. +
+ ) : ( +
+ {versions.map((item, index) => { + const itemVersionNum = + item.version || + parseInt(getVersionNumber(item).replace("v", "")); -
- {formatDate(item.created_at)} - - {item.prompt_info?.prompt_type === "db" ? "Saved to Database" : "Config Prompt"} - + let activeVersionNum: number | null = null; + if (activeVersionId) { + if (activeVersionId.includes(".v")) { + activeVersionNum = parseInt(activeVersionId.split(".v")[1]); + } else if (activeVersionId.includes("_v")) { + activeVersionNum = parseInt(activeVersionId.split("_v")[1]); + } + } + + const isSelected = activeVersionNum + ? itemVersionNum === activeVersionNum + : index === 0; + + return ( +
onSelectVersion?.(item)} + > +
+
+ + {getVersionNumber(item)} + + {index === 0 && ( + + Latest + + )} +
+ {isSelected && ( + + Active + + )} +
+ +
+ + {formatDate(item.created_at)} + + + {item.prompt_info?.prompt_type === "db" + ? "Saved to Database" + : "Config Prompt"} + +
-
- ); - }} - /> - )} - + ); + })} +
+ )} +
+ ); };