;
isAdmin: boolean;
- onPromptClick?: (id: string) => void;
- onDeleteClick?: (id: string, name: string) => void;
+ onPromptClick?: (id: string, environment?: string) => void;
+ onDeleteClick?: (id: string, name: string, environment?: string) => void;
}
export const getPromptTableColumns = ({
@@ -128,7 +130,7 @@ export const getPromptTableColumns = ({
title={row.original.prompt_id}
titleClassName="font-mono text-xs font-normal"
className="max-w-60"
- onClick={onPromptClick ? () => onPromptClick(row.original.prompt_id) : undefined}
+ onClick={onPromptClick ? () => onPromptClick(row.original.prompt_id, row.original.environment) : undefined}
/>
),
},
diff --git a/ui/litellm-dashboard/src/app/(dashboard)/prompts/_components/index.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/prompts/_components/index.test.tsx
index d6a4aaea2e1..cc7b2ed2603 100644
--- a/ui/litellm-dashboard/src/app/(dashboard)/prompts/_components/index.test.tsx
+++ b/ui/litellm-dashboard/src/app/(dashboard)/prompts/_components/index.test.tsx
@@ -18,11 +18,11 @@ vi.mock("./PromptTable", () => ({
onDeleteClick,
}: {
isLoading: boolean;
- onDeleteClick: (id: string, name: string) => void;
+ onDeleteClick: (id: string, name: string, environment?: string) => void;
}) => (
{isLoading ? "table-loading" : "table-loaded"}
-
@@ -162,7 +162,7 @@ describe("PromptsPanel delete confirmation", () => {
await user.click(screen.getByRole("button", { name: /^delete$/i }));
- await waitFor(() => expect(mockDeletePromptCall).toHaveBeenCalledWith("sk-test", "prompt-1"));
+ await waitFor(() => expect(mockDeletePromptCall).toHaveBeenCalledWith("sk-test", "prompt-1", "staging"));
});
it("should abandon the delete when the confirmation is dismissed", async () => {
@@ -191,7 +191,7 @@ describe("PromptsPanel delete confirmation", () => {
await user.click(await screen.findByRole("button", { name: "row-delete" }));
await screen.findByText(/delete prompt: my-prompt/i);
await user.click(screen.getByRole("button", { name: /^delete$/i }));
- await waitFor(() => expect(mockDeletePromptCall).toHaveBeenCalledWith("sk-test", "prompt-1"));
+ await waitFor(() => expect(mockDeletePromptCall).toHaveBeenCalledWith("sk-test", "prompt-1", "staging"));
await user.keyboard("{Escape}");
expect(screen.getByText(/delete prompt: my-prompt/i)).toBeInTheDocument();
diff --git a/ui/litellm-dashboard/src/app/(dashboard)/prompts/_components/index.tsx b/ui/litellm-dashboard/src/app/(dashboard)/prompts/_components/index.tsx
index 3594e933704..8db1fa43820 100644
--- a/ui/litellm-dashboard/src/app/(dashboard)/prompts/_components/index.tsx
+++ b/ui/litellm-dashboard/src/app/(dashboard)/prompts/_components/index.tsx
@@ -41,11 +41,12 @@ const PromptsPanel: React.FC = ({ accessToken, userRole }) => {
const [isLoading, setIsLoading] = useState(true);
const [selectedEnvironment, setSelectedEnvironment] = useState(undefined);
const [selectedPromptId, setSelectedPromptId] = useState(null);
+ const [selectedPromptEnvironment, setSelectedPromptEnvironment] = useState(undefined);
const [isAddModalVisible, setIsAddModalVisible] = useState(false);
const [showEditorView, setShowEditorView] = useState(false);
const [editPromptData, setEditPromptData] = useState(null);
const [isDeleting, setIsDeleting] = useState(false);
- const [promptToDelete, setPromptToDelete] = useState<{ id: string; name: string } | null>(null);
+ const [promptToDelete, setPromptToDelete] = useState<{ id: string; name: string; environment?: string } | null>(null);
// Admin Viewer follows the read-parity rule: see prompts, no writes.
const canModify = userRole ? isProxyAdminRole(userRole) : false;
@@ -71,8 +72,9 @@ const PromptsPanel: React.FC = ({ accessToken, userRole }) => {
fetchPrompts();
}, [accessToken, selectedEnvironment]);
- const handlePromptClick = (promptId: string) => {
+ const handlePromptClick = (promptId: string, environment?: string) => {
setSelectedPromptId(promptId);
+ setSelectedPromptEnvironment(environment);
};
const handleAddPrompt = () => {
@@ -111,8 +113,8 @@ const PromptsPanel: React.FC = ({ accessToken, userRole }) => {
setSelectedPromptId(null);
};
- const handleDeleteClick = (promptId: string, promptName: string) => {
- setPromptToDelete({ id: promptId, name: promptName });
+ const handleDeleteClick = (promptId: string, promptName: string, environment?: string) => {
+ setPromptToDelete({ id: promptId, name: promptName, environment });
};
const handleDeleteConfirm = async () => {
@@ -120,7 +122,7 @@ const PromptsPanel: React.FC = ({ accessToken, userRole }) => {
setIsDeleting(true);
try {
- await deletePromptCall(accessToken, promptToDelete.id);
+ await deletePromptCall(accessToken, promptToDelete.id, promptToDelete.environment);
toast.success(`Prompt "${promptToDelete.name}" deleted successfully`);
fetchPrompts(); // Refresh the list
} catch (error) {
@@ -148,7 +150,11 @@ const PromptsPanel: React.FC = ({ accessToken, userRole }) => {
) : selectedPromptId ? (
setSelectedPromptId(null)}
+ environment={selectedPromptEnvironment}
+ onClose={() => {
+ setSelectedPromptId(null);
+ setSelectedPromptEnvironment(undefined);
+ }}
accessToken={accessToken}
isAdmin={canModify}
onDelete={fetchPrompts}
diff --git a/ui/litellm-dashboard/src/app/(dashboard)/prompts/_components/prompt_info.tsx b/ui/litellm-dashboard/src/app/(dashboard)/prompts/_components/prompt_info.tsx
index c6a6f09fcaa..3ce1070e466 100644
--- a/ui/litellm-dashboard/src/app/(dashboard)/prompts/_components/prompt_info.tsx
+++ b/ui/litellm-dashboard/src/app/(dashboard)/prompts/_components/prompt_info.tsx
@@ -20,6 +20,7 @@ import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from "
export interface PromptInfoProps {
promptId: string;
+ environment?: string;
onClose: () => void;
accessToken: string | null;
isAdmin: boolean;
@@ -27,7 +28,15 @@ export interface PromptInfoProps {
onEdit?: (promptData: any) => void;
}
-const PromptInfoView: React.FC = ({ promptId, onClose, accessToken, isAdmin, onDelete, onEdit }) => {
+const PromptInfoView: React.FC = ({
+ promptId,
+ environment,
+ onClose,
+ accessToken,
+ isAdmin,
+ onDelete,
+ onEdit,
+}) => {
const [promptData, setPromptData] = useState(null);
const [promptTemplate, setPromptTemplate] = useState(null);
const [rawApiResponse, setRawApiResponse] = useState(null);
@@ -86,11 +95,11 @@ const PromptInfoView: React.FC = ({ promptId, onClose, accessTo
const isInitialMount = useRef(true);
useEffect(() => {
- setSelectedEnv(null);
+ setSelectedEnv(environment ?? null);
setEnvironments([]);
setVersionHistory([]);
- fetchPromptInfo();
- }, [promptId, accessToken]);
+ fetchPromptInfo(environment);
+ }, [promptId, accessToken, environment]);
// When environment changes (user clicks tab), re-fetch — skip initial mount
useEffect(() => {
@@ -140,7 +149,7 @@ const PromptInfoView: React.FC = ({ promptId, onClose, accessTo
if (!accessToken || !promptData) return;
setIsDeleting(true);
try {
- await deletePromptCall(accessToken, basePromptId);
+ await deletePromptCall(accessToken, basePromptId, selectedEnv ?? undefined);
toast.success(`Prompt "${basePromptId}" deleted successfully`);
onDelete?.();
onClose();
diff --git a/ui/litellm-dashboard/src/components/networking.tsx b/ui/litellm-dashboard/src/components/networking.tsx
index 5b6d70b4771..e297bcc5270 100644
--- a/ui/litellm-dashboard/src/components/networking.tsx
+++ b/ui/litellm-dashboard/src/components/networking.tsx
@@ -4625,9 +4625,12 @@ export const updatePromptCall = async (accessToken: string, promptId: string, pr
}
};
-export const deletePromptCall = async (accessToken: string, promptId: string) => {
+export const deletePromptCall = async (accessToken: string, promptId: string, environment?: string) => {
try {
- const data = await apiClient.delete(`/prompts/${promptId}`, { accessToken });
+ const data = await apiClient.delete(`/prompts/${promptId}`, {
+ accessToken,
+ query: { environment: environment || undefined },
+ });
return data;
} catch (error) {
console.error("Failed to delete prompt:", error);