mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-13 03:59:12 +00:00
feat(ui): add Delete to the guardrail detail Settings card for DB guardrails
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
parent
c5ac1845b0
commit
c651ebcbee
6 changed files with 330 additions and 24 deletions
|
|
@ -0,0 +1,93 @@
|
|||
import { Code, Trash2 } from "lucide-react";
|
||||
import React, { useState } from "react";
|
||||
import DeleteResourceModal from "@/components/common_components/DeleteResourceModal";
|
||||
import { deleteGuardrailCall } from "@/components/networking";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { toast } from "@/lib/toast";
|
||||
import { formatGuardrailMode, getGuardrailLogoAndName } from "./guardrail_info_helpers";
|
||||
|
||||
const DANGLING_REFERENCE_WARNING =
|
||||
"Keys, teams, or policies that reference this guardrail by name will silently stop running it after deletion. Their requests will not return an error.";
|
||||
|
||||
interface GuardrailSettingsActionsProps {
|
||||
guardrail: {
|
||||
guardrail_id: string;
|
||||
guardrail_definition_location?: string;
|
||||
guardrail_name?: string | null;
|
||||
litellm_params?: { guardrail?: string; mode?: unknown; default_on?: boolean | null } | null;
|
||||
};
|
||||
isEditing: boolean;
|
||||
onEdit: () => void;
|
||||
onEditCode: () => void;
|
||||
accessToken: string | null;
|
||||
onDeleted: () => void;
|
||||
}
|
||||
|
||||
export function GuardrailSettingsActions({
|
||||
guardrail,
|
||||
isEditing,
|
||||
onEdit,
|
||||
onEditCode,
|
||||
accessToken,
|
||||
onDeleted,
|
||||
}: GuardrailSettingsActionsProps) {
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
const [isDeleting, setIsDeleting] = useState(false);
|
||||
const guardrailName = guardrail.guardrail_name || guardrail.guardrail_id;
|
||||
const providerDisplayName = getGuardrailLogoAndName(guardrail.litellm_params?.guardrail || "").displayName;
|
||||
|
||||
const handleDelete = async () => {
|
||||
if (!accessToken) return;
|
||||
setIsDeleting(true);
|
||||
try {
|
||||
await deleteGuardrailCall(accessToken, guardrail.guardrail_id);
|
||||
toast.success(`Guardrail "${guardrailName}" deleted successfully`);
|
||||
setIsModalOpen(false);
|
||||
onDeleted();
|
||||
} catch {
|
||||
toast.fromError("Failed to delete guardrail");
|
||||
setIsModalOpen(false);
|
||||
} finally {
|
||||
setIsDeleting(false);
|
||||
}
|
||||
};
|
||||
|
||||
if (isEditing || guardrail.guardrail_definition_location === "config") return null;
|
||||
|
||||
return (
|
||||
<>
|
||||
{guardrail.litellm_params?.guardrail === "custom_code" ? (
|
||||
<Button variant="outline" onClick={onEditCode}>
|
||||
<Code />
|
||||
Edit Code
|
||||
</Button>
|
||||
) : (
|
||||
<Button variant="outline" onClick={onEdit}>
|
||||
Edit Settings
|
||||
</Button>
|
||||
)}
|
||||
<Button variant="destructive" onClick={() => setIsModalOpen(true)}>
|
||||
<Trash2 />
|
||||
Delete
|
||||
</Button>
|
||||
<DeleteResourceModal
|
||||
isOpen={isModalOpen}
|
||||
title="Delete Guardrail"
|
||||
alertMessage={DANGLING_REFERENCE_WARNING}
|
||||
message={`Are you sure you want to delete guardrail: ${guardrailName}? This action cannot be undone.`}
|
||||
resourceInformationTitle="Guardrail Information"
|
||||
resourceInformation={[
|
||||
{ label: "Name", value: guardrail.guardrail_name },
|
||||
{ label: "ID", value: guardrail.guardrail_id, code: true },
|
||||
{ label: "Provider", value: providerDisplayName },
|
||||
{ label: "Mode", value: formatGuardrailMode(guardrail.litellm_params?.mode) },
|
||||
{ label: "Default On", value: guardrail.litellm_params?.default_on ? "Yes" : "No" },
|
||||
]}
|
||||
onCancel={() => setIsModalOpen(false)}
|
||||
onOk={handleDelete}
|
||||
confirmLoading={isDeleting}
|
||||
requiredConfirmation={guardrailName}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,131 @@
|
|||
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import GuardrailsPanel from "./GuardrailsPanel";
|
||||
import {
|
||||
deleteGuardrailCall,
|
||||
getGuardrailInfo,
|
||||
getGuardrailProviderSpecificParams,
|
||||
getGuardrailsList,
|
||||
getGuardrailUISettings,
|
||||
} from "@/components/networking";
|
||||
import { fireEvent, renderWithProviders, screen, waitFor, within } from "@/../tests/test-utils";
|
||||
|
||||
vi.mock("@/components/networking", () => ({
|
||||
getGuardrailsList: vi.fn(),
|
||||
deleteGuardrailCall: vi.fn(),
|
||||
getGuardrailInfo: vi.fn(),
|
||||
getGuardrailUISettings: vi.fn(),
|
||||
getGuardrailProviderSpecificParams: vi.fn(),
|
||||
updateGuardrailCall: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("./add_guardrail_form", () => ({ __esModule: true, default: () => null }));
|
||||
vi.mock("./guardrail_garden", () => ({ __esModule: true, default: () => null }));
|
||||
vi.mock("./GuardrailTestPlayground", () => ({ __esModule: true, default: () => null }));
|
||||
vi.mock("./TeamGuardrailsTab", () => ({ TeamGuardrailsTab: () => null }));
|
||||
|
||||
const DB_GUARDRAIL = {
|
||||
guardrail_id: "db-guardrail-1",
|
||||
guardrail_name: "db-word-block",
|
||||
litellm_params: { guardrail: "litellm_content_filter", mode: "pre_call", default_on: true },
|
||||
guardrail_info: null,
|
||||
created_at: "2024-01-01T00:00:00Z",
|
||||
updated_at: "2024-01-01T00:00:00Z",
|
||||
guardrail_definition_location: "db",
|
||||
};
|
||||
|
||||
const CONFIG_GUARDRAIL = {
|
||||
...DB_GUARDRAIL,
|
||||
guardrail_id: "config-guardrail-1",
|
||||
guardrail_name: "cfg-word-block",
|
||||
guardrail_definition_location: "config",
|
||||
};
|
||||
|
||||
beforeAll(() => {
|
||||
Object.defineProperty(window, "matchMedia", {
|
||||
writable: true,
|
||||
value: vi.fn().mockImplementation((query: string) => ({
|
||||
matches: false,
|
||||
media: query,
|
||||
onchange: null,
|
||||
addListener: vi.fn(),
|
||||
removeListener: vi.fn(),
|
||||
addEventListener: vi.fn(),
|
||||
removeEventListener: vi.fn(),
|
||||
dispatchEvent: vi.fn(),
|
||||
})),
|
||||
});
|
||||
});
|
||||
|
||||
describe("GuardrailsPanel delete from the guardrail detail Settings card", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
vi.mocked(getGuardrailsList).mockResolvedValue({ guardrails: [DB_GUARDRAIL, CONFIG_GUARDRAIL] });
|
||||
vi.mocked(getGuardrailInfo).mockImplementation(async (_token: string, id: string) =>
|
||||
id === CONFIG_GUARDRAIL.guardrail_id ? CONFIG_GUARDRAIL : DB_GUARDRAIL,
|
||||
);
|
||||
vi.mocked(getGuardrailUISettings).mockResolvedValue({
|
||||
supported_entities: [],
|
||||
supported_actions: [],
|
||||
pii_entity_categories: [],
|
||||
supported_modes: ["pre_call", "post_call"],
|
||||
});
|
||||
vi.mocked(getGuardrailProviderSpecificParams).mockResolvedValue({});
|
||||
});
|
||||
|
||||
const renderDetail = (guardrailId: string) =>
|
||||
renderWithProviders(<GuardrailsPanel accessToken="test-token" userRole="Admin" />, {
|
||||
searchParams: `?guardrail=${guardrailId}`,
|
||||
});
|
||||
|
||||
it("deletes a DB guardrail only after the admin types its name, then returns to the refreshed table", async () => {
|
||||
vi.mocked(deleteGuardrailCall).mockResolvedValue({});
|
||||
renderDetail(DB_GUARDRAIL.guardrail_id);
|
||||
|
||||
fireEvent.click(await screen.findByRole("tab", { name: "Settings" }));
|
||||
fireEvent.click(await screen.findByRole("button", { name: "Delete" }));
|
||||
|
||||
const modal = within(await screen.findByRole("dialog"));
|
||||
expect(modal.getByText(/keys, teams, or policies that reference/i)).toBeInTheDocument();
|
||||
const confirmButton = modal.getByRole("button", { name: "Delete" });
|
||||
expect(confirmButton).toBeDisabled();
|
||||
|
||||
const confirmationInput = modal.getByPlaceholderText(DB_GUARDRAIL.guardrail_name);
|
||||
fireEvent.change(confirmationInput, { target: { value: "db-word" } });
|
||||
expect(confirmButton).toBeDisabled();
|
||||
fireEvent.change(confirmationInput, { target: { value: DB_GUARDRAIL.guardrail_name } });
|
||||
expect(confirmButton).toBeEnabled();
|
||||
|
||||
fireEvent.click(confirmButton);
|
||||
|
||||
await waitFor(() => expect(deleteGuardrailCall).toHaveBeenCalledWith("test-token", DB_GUARDRAIL.guardrail_id));
|
||||
expect(await screen.findByRole("table")).toBeInTheDocument();
|
||||
expect(screen.queryByRole("button", { name: "Edit Settings" })).not.toBeInTheDocument();
|
||||
expect(getGuardrailsList).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it("keeps the admin on the detail page when the delete request fails", async () => {
|
||||
vi.mocked(deleteGuardrailCall).mockRejectedValue(new Error("boom"));
|
||||
renderDetail(DB_GUARDRAIL.guardrail_id);
|
||||
|
||||
fireEvent.click(await screen.findByRole("tab", { name: "Settings" }));
|
||||
fireEvent.click(await screen.findByRole("button", { name: "Delete" }));
|
||||
const modal = within(await screen.findByRole("dialog"));
|
||||
fireEvent.change(modal.getByPlaceholderText(DB_GUARDRAIL.guardrail_name), {
|
||||
target: { value: DB_GUARDRAIL.guardrail_name },
|
||||
});
|
||||
fireEvent.click(modal.getByRole("button", { name: "Delete" }));
|
||||
|
||||
await waitFor(() => expect(deleteGuardrailCall).toHaveBeenCalledTimes(1));
|
||||
await waitFor(() => expect(screen.queryByRole("dialog")).not.toBeInTheDocument());
|
||||
expect(screen.getByRole("button", { name: "Delete" })).toBeInTheDocument();
|
||||
expect(screen.queryByRole("table")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("does not offer Delete on a config guardrail's detail page", async () => {
|
||||
renderDetail(CONFIG_GUARDRAIL.guardrail_id);
|
||||
|
||||
fireEvent.click(await screen.findByRole("tab", { name: "Settings" }));
|
||||
expect(await screen.findByRole("img", { name: "Config guardrail details" })).toBeInTheDocument();
|
||||
expect(screen.queryByRole("button", { name: "Delete" })).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
|
@ -184,6 +184,10 @@ const GuardrailsPanel: React.FC<GuardrailsPanelProps> = ({ accessToken, userRole
|
|||
<GuardrailInfoView
|
||||
guardrailId={selectedGuardrailId}
|
||||
onClose={closeGuardrailDetail}
|
||||
onDeleted={() => {
|
||||
closeGuardrailDetail();
|
||||
fetchGuardrails();
|
||||
}}
|
||||
accessToken={accessToken}
|
||||
isAdmin={isAdmin}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -84,7 +84,8 @@ const saveChanges = async (user: ReturnType<typeof userEvent.setup>) => {
|
|||
await user.click(screen.getByText("Save Changes"));
|
||||
};
|
||||
|
||||
const renderView = () => render(<GuardrailInfoView guardrailId="123" onClose={() => {}} accessToken="123" isAdmin />);
|
||||
const renderView = () =>
|
||||
render(<GuardrailInfoView guardrailId="123" onClose={() => {}} onDeleted={() => {}} accessToken="123" isAdmin />);
|
||||
|
||||
const lastPayload = () => vi.mocked(networking.updateGuardrailCall).mock.calls.at(-1)?.[2];
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ vi.mock("@/components/networking", () => ({
|
|||
getGuardrailUISettings: vi.fn(),
|
||||
getGuardrailProviderSpecificParams: vi.fn(),
|
||||
updateGuardrailCall: vi.fn(),
|
||||
deleteGuardrailCall: vi.fn(),
|
||||
}));
|
||||
|
||||
// Mock ContentFilterManager
|
||||
|
|
@ -65,7 +66,9 @@ describe("Guardrail Info", () => {
|
|||
|
||||
vi.mocked(networking.getGuardrailProviderSpecificParams).mockResolvedValue({});
|
||||
|
||||
render(<GuardrailInfoView guardrailId="123" onClose={() => {}} accessToken="123" isAdmin={true} />);
|
||||
render(
|
||||
<GuardrailInfoView guardrailId="123" onClose={() => {}} onDeleted={() => {}} accessToken="123" isAdmin={true} />,
|
||||
);
|
||||
|
||||
// Wait for the loading to complete and data to be rendered
|
||||
await waitFor(() => {
|
||||
|
|
@ -103,7 +106,9 @@ describe("Guardrail Info", () => {
|
|||
|
||||
vi.mocked(networking.getGuardrailProviderSpecificParams).mockResolvedValue({});
|
||||
|
||||
render(<GuardrailInfoView guardrailId="123" onClose={() => {}} accessToken="123" isAdmin={true} />);
|
||||
render(
|
||||
<GuardrailInfoView guardrailId="123" onClose={() => {}} onDeleted={() => {}} accessToken="123" isAdmin={true} />,
|
||||
);
|
||||
|
||||
expect(await screen.findAllByText("pre_call, post_call (tag-based)")).not.toHaveLength(0);
|
||||
});
|
||||
|
|
@ -131,7 +136,9 @@ describe("Guardrail Info", () => {
|
|||
|
||||
vi.mocked(networking.getGuardrailProviderSpecificParams).mockResolvedValue({});
|
||||
|
||||
render(<GuardrailInfoView guardrailId="123" onClose={() => {}} accessToken="123" isAdmin={true} />);
|
||||
render(
|
||||
<GuardrailInfoView guardrailId="123" onClose={() => {}} onDeleted={() => {}} accessToken="123" isAdmin={true} />,
|
||||
);
|
||||
|
||||
const logo = await screen.findByAltText("Presidio PII logo");
|
||||
expect(logo).toHaveAttribute("src", expect.stringContaining("microsoft_azure.svg"));
|
||||
|
|
@ -162,7 +169,7 @@ describe("Guardrail Info", () => {
|
|||
vi.mocked(networking.getGuardrailProviderSpecificParams).mockResolvedValue({});
|
||||
|
||||
const { container } = render(
|
||||
<GuardrailInfoView guardrailId="123" onClose={() => {}} accessToken="123" isAdmin={true} />,
|
||||
<GuardrailInfoView guardrailId="123" onClose={() => {}} onDeleted={() => {}} accessToken="123" isAdmin={true} />,
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
|
|
@ -212,7 +219,9 @@ describe("Guardrail Info", () => {
|
|||
|
||||
vi.mocked(networking.getGuardrailProviderSpecificParams).mockResolvedValue({});
|
||||
|
||||
render(<GuardrailInfoView guardrailId="123" onClose={() => {}} accessToken="123" isAdmin={true} />);
|
||||
render(
|
||||
<GuardrailInfoView guardrailId="123" onClose={() => {}} onDeleted={() => {}} accessToken="123" isAdmin={true} />,
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("PII Entity Configuration")).toBeInTheDocument();
|
||||
|
|
@ -245,7 +254,9 @@ describe("Guardrail Info", () => {
|
|||
vi.mocked(networking.getGuardrailProviderSpecificParams).mockResolvedValue({});
|
||||
vi.mocked(networking.updateGuardrailCall).mockResolvedValue({ status: "success" });
|
||||
|
||||
render(<GuardrailInfoView guardrailId="123" onClose={() => {}} accessToken="123" isAdmin={true} />);
|
||||
render(
|
||||
<GuardrailInfoView guardrailId="123" onClose={() => {}} onDeleted={() => {}} accessToken="123" isAdmin={true} />,
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Settings")).toBeInTheDocument();
|
||||
|
|
@ -331,7 +342,9 @@ describe("Guardrail Info", () => {
|
|||
});
|
||||
vi.mocked(networking.getGuardrailProviderSpecificParams).mockResolvedValue({});
|
||||
|
||||
render(<GuardrailInfoView guardrailId="123" onClose={() => {}} accessToken="123" isAdmin={true} />);
|
||||
render(
|
||||
<GuardrailInfoView guardrailId="123" onClose={() => {}} onDeleted={() => {}} accessToken="123" isAdmin={true} />,
|
||||
);
|
||||
|
||||
expect(await screen.findByRole("tab", { name: "Overview" })).toHaveAttribute("aria-selected", "true");
|
||||
expect(screen.getByRole("tab", { name: "Settings" })).toHaveAttribute("aria-selected", "false");
|
||||
|
|
@ -355,10 +368,76 @@ describe("Guardrail Info when the guardrail cannot be loaded", () => {
|
|||
vi.mocked(networking.getGuardrailProviderSpecificParams).mockResolvedValue({});
|
||||
const onClose = vi.fn();
|
||||
|
||||
render(<GuardrailInfoView guardrailId="stale-id" onClose={onClose} accessToken="123" isAdmin={true} />);
|
||||
render(
|
||||
<GuardrailInfoView
|
||||
guardrailId="stale-id"
|
||||
onClose={onClose}
|
||||
onDeleted={() => {}}
|
||||
accessToken="123"
|
||||
isAdmin={true}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(await screen.findByText("Guardrail not found")).toBeInTheDocument();
|
||||
fireEvent.click(screen.getByRole("button", { name: /back to guardrails/i }));
|
||||
expect(onClose).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
describe("Delete from the Settings card", () => {
|
||||
const mockDbGuardrail = () => {
|
||||
vi.mocked(networking.getGuardrailInfo).mockResolvedValue({
|
||||
guardrail_id: "123",
|
||||
guardrail_name: "Test Guardrail",
|
||||
litellm_params: { guardrail: "presidio", mode: "pre_call", default_on: true },
|
||||
guardrail_definition_location: "db",
|
||||
});
|
||||
vi.mocked(networking.getGuardrailUISettings).mockResolvedValue({
|
||||
supported_entities: [],
|
||||
supported_actions: [],
|
||||
pii_entity_categories: [],
|
||||
supported_modes: ["pre_call"],
|
||||
});
|
||||
vi.mocked(networking.getGuardrailProviderSpecificParams).mockResolvedValue({});
|
||||
};
|
||||
|
||||
const openDeleteModalAndConfirm = async () => {
|
||||
fireEvent.click(await screen.findByRole("tab", { name: "Settings" }));
|
||||
fireEvent.click(await screen.findByRole("button", { name: "Delete" }));
|
||||
const modal = within(await screen.findByRole("dialog"));
|
||||
expect(modal.getByText(/silently stop running it/)).toBeInTheDocument();
|
||||
const confirmButton = modal.getByRole("button", { name: "Delete" });
|
||||
expect(confirmButton).toBeDisabled();
|
||||
fireEvent.change(modal.getByPlaceholderText("Test Guardrail"), { target: { value: "Test Guardrail" } });
|
||||
fireEvent.click(confirmButton);
|
||||
};
|
||||
|
||||
it("deletes the guardrail and calls onDeleted once the admin types its name", async () => {
|
||||
mockDbGuardrail();
|
||||
vi.mocked(networking.deleteGuardrailCall).mockResolvedValue({});
|
||||
const onDeleted = vi.fn();
|
||||
render(
|
||||
<GuardrailInfoView guardrailId="123" onClose={() => {}} onDeleted={onDeleted} accessToken="tok" isAdmin />,
|
||||
);
|
||||
|
||||
await openDeleteModalAndConfirm();
|
||||
|
||||
await waitFor(() => expect(onDeleted).toHaveBeenCalledTimes(1));
|
||||
expect(networking.deleteGuardrailCall).toHaveBeenCalledWith("tok", "123");
|
||||
});
|
||||
|
||||
it("does not call onDeleted when the delete request fails", async () => {
|
||||
mockDbGuardrail();
|
||||
vi.mocked(networking.deleteGuardrailCall).mockRejectedValue(new Error("boom"));
|
||||
const onDeleted = vi.fn();
|
||||
render(
|
||||
<GuardrailInfoView guardrailId="123" onClose={() => {}} onDeleted={onDeleted} accessToken="tok" isAdmin />,
|
||||
);
|
||||
|
||||
await openDeleteModalAndConfirm();
|
||||
|
||||
await waitFor(() => expect(screen.queryByRole("dialog")).not.toBeInTheDocument());
|
||||
expect(networking.deleteGuardrailCall).toHaveBeenCalledTimes(1);
|
||||
expect(onDeleted).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ import {
|
|||
} from "./GuardrailFormField";
|
||||
import ContentFilterManager, { formatContentFilterDataForAPI } from "./content_filter/ContentFilterManager";
|
||||
import CustomCodeModal, { EditGuardrailData } from "./custom_code/CustomCodeModal";
|
||||
import { GuardrailSettingsActions } from "./GuardrailSettingsActions";
|
||||
import { GuardrailModeCard } from "./GuardrailModeDisplay";
|
||||
import { GuardrailReadOnlyDetails } from "./GuardrailReadOnlyDetails";
|
||||
import {
|
||||
|
|
@ -72,11 +73,12 @@ const SectionHeading: React.FC<{ children: React.ReactNode }> = ({ children }) =
|
|||
export interface GuardrailInfoProps {
|
||||
guardrailId: string;
|
||||
onClose: () => void;
|
||||
onDeleted: () => void;
|
||||
accessToken: string | null;
|
||||
isAdmin: boolean;
|
||||
}
|
||||
|
||||
const GuardrailInfoView: React.FC<GuardrailInfoProps> = ({ guardrailId, onClose, accessToken, isAdmin }) => {
|
||||
const GuardrailInfoView: React.FC<GuardrailInfoProps> = ({ guardrailId, onClose, onDeleted, accessToken, isAdmin }) => {
|
||||
const [guardrailData, setGuardrailData] = useState<any>(null);
|
||||
const [guardrailProviderSpecificParams, setGuardrailProviderSpecificParams] = useState<any>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
|
@ -686,25 +688,21 @@ const GuardrailInfoView: React.FC<GuardrailInfoProps> = ({ guardrailId, onClose,
|
|||
{isAdmin && (
|
||||
<TabsContent value="settings" keepMounted>
|
||||
<Card className="block p-6">
|
||||
<div className="flex justify-between items-center mb-4">
|
||||
<h3 className="text-lg font-medium">Guardrail Settings</h3>
|
||||
<div className="flex items-center gap-2 mb-4">
|
||||
<h3 className="mr-auto text-lg font-medium">Guardrail Settings</h3>
|
||||
{isConfigGuardrail && (
|
||||
<SimpleTooltip content="Guardrail is defined in the config file and cannot be edited.">
|
||||
<Info role="img" aria-label="Config guardrail details" className="size-4 text-muted-foreground" />
|
||||
</SimpleTooltip>
|
||||
)}
|
||||
{!isEditing &&
|
||||
!isConfigGuardrail &&
|
||||
(guardrailData.litellm_params?.guardrail === "custom_code" ? (
|
||||
<Button variant="outline" onClick={() => setCustomCodeModalVisible(true)}>
|
||||
<Code />
|
||||
Edit Code
|
||||
</Button>
|
||||
) : (
|
||||
<Button variant="outline" onClick={() => setIsEditing(true)}>
|
||||
Edit Settings
|
||||
</Button>
|
||||
))}
|
||||
<GuardrailSettingsActions
|
||||
guardrail={guardrailData}
|
||||
isEditing={isEditing}
|
||||
onEdit={() => setIsEditing(true)}
|
||||
onEditCode={() => setCustomCodeModalVisible(true)}
|
||||
accessToken={accessToken}
|
||||
onDeleted={onDeleted}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{isEditing ? (
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue