Spaces:
Paused
Paused
| import React, { useState, useEffect } from "react"; | |
| import { | |
| Card, | |
| Title, | |
| Text, | |
| Grid, | |
| Badge, | |
| Button as TremorButton, | |
| Tab, | |
| TabGroup, | |
| TabList, | |
| TabPanel, | |
| TabPanels, | |
| TextInput, | |
| } from "@tremor/react"; | |
| import { Button, Form, Input, Select, message, Tooltip, Divider } from "antd"; | |
| import { InfoCircleOutlined } from '@ant-design/icons'; | |
| import { getGuardrailInfo, updateGuardrailCall, getGuardrailUISettings } from "@/components/networking"; | |
| import { getGuardrailLogoAndName } from "./guardrail_info_helpers"; | |
| import PiiConfiguration from "./pii_configuration"; | |
| export interface GuardrailInfoProps { | |
| guardrailId: string; | |
| onClose: () => void; | |
| accessToken: string | null; | |
| isAdmin: boolean; | |
| } | |
| const GuardrailInfoView: React.FC<GuardrailInfoProps> = ({ | |
| guardrailId, | |
| onClose, | |
| accessToken, | |
| isAdmin | |
| }) => { | |
| const [guardrailData, setGuardrailData] = useState<any>(null); | |
| const [loading, setLoading] = useState(true); | |
| const [isEditing, setIsEditing] = useState(false); | |
| const [form] = Form.useForm(); | |
| const [selectedPiiEntities, setSelectedPiiEntities] = useState<string[]>([]); | |
| const [selectedPiiActions, setSelectedPiiActions] = useState<{[key: string]: string}>({}); | |
| const [guardrailSettings, setGuardrailSettings] = useState<{ | |
| supported_entities: string[]; | |
| supported_actions: string[]; | |
| pii_entity_categories: Array<{ | |
| category: string; | |
| entities: string[]; | |
| }>; | |
| supported_modes: string[]; | |
| } | null>(null); | |
| const fetchGuardrailInfo = async () => { | |
| try { | |
| setLoading(true); | |
| if (!accessToken) return; | |
| const response = await getGuardrailInfo(accessToken, guardrailId); | |
| setGuardrailData(response); | |
| // Initialize PII configuration from guardrail data | |
| if (response.litellm_params?.pii_entities_config) { | |
| const piiConfig = response.litellm_params.pii_entities_config; | |
| // Clear previous selections | |
| setSelectedPiiEntities([]); | |
| setSelectedPiiActions({}); | |
| // Only if there are entities configured | |
| if (Object.keys(piiConfig).length > 0) { | |
| const entities: string[] = []; | |
| const actions: {[key: string]: string} = {}; | |
| Object.entries(piiConfig).forEach(([entity, action]: [string, any]) => { | |
| entities.push(entity); | |
| actions[entity] = typeof action === 'string' ? action : "MASK"; | |
| }); | |
| setSelectedPiiEntities(entities); | |
| setSelectedPiiActions(actions); | |
| } | |
| } else { | |
| // Clear selections if no PII config exists | |
| setSelectedPiiEntities([]); | |
| setSelectedPiiActions({}); | |
| } | |
| } catch (error) { | |
| message.error("Failed to load guardrail information"); | |
| console.error("Error fetching guardrail info:", error); | |
| } finally { | |
| setLoading(false); | |
| } | |
| }; | |
| const fetchGuardrailUISettings = async () => { | |
| try { | |
| if (!accessToken) return; | |
| const uiSettings = await getGuardrailUISettings(accessToken); | |
| setGuardrailSettings(uiSettings); | |
| } catch (error) { | |
| console.error("Error fetching guardrail UI settings:", error); | |
| } | |
| }; | |
| useEffect(() => { | |
| fetchGuardrailInfo(); | |
| fetchGuardrailUISettings(); | |
| }, [guardrailId, accessToken]); | |
| const handlePiiEntitySelect = (entity: string) => { | |
| setSelectedPiiEntities(prev => { | |
| if (prev.includes(entity)) { | |
| return prev.filter(e => e !== entity); | |
| } else { | |
| return [...prev, entity]; | |
| } | |
| }); | |
| }; | |
| const handlePiiActionSelect = (entity: string, action: string) => { | |
| setSelectedPiiActions(prev => ({ | |
| ...prev, | |
| [entity]: action | |
| })); | |
| }; | |
| const handleGuardrailUpdate = async (values: any) => { | |
| try { | |
| if (!accessToken) return; | |
| // Prepare update data object | |
| const updateData: any = { | |
| guardrail_name: values.guardrail_name, | |
| litellm_params: { | |
| default_on: values.default_on, | |
| }, | |
| guardrail_info: values.guardrail_info ? JSON.parse(values.guardrail_info) : undefined | |
| }; | |
| // Only add PII entities config if we have selected entities | |
| if (selectedPiiEntities.length > 0) { | |
| // Create PII config object only with selected entities | |
| const piiEntitiesConfig: {[key: string]: string} = {}; | |
| selectedPiiEntities.forEach(entity => { | |
| piiEntitiesConfig[entity] = selectedPiiActions[entity] || "MASK"; | |
| }); | |
| // Add to litellm_params only if we have entities | |
| updateData.litellm_params.pii_entities_config = piiEntitiesConfig; | |
| } else { | |
| // If no entities selected, explicitly set to empty object | |
| // This will clear any existing PII config | |
| updateData.litellm_params.pii_entities_config = {}; | |
| } | |
| await updateGuardrailCall(accessToken, guardrailId, updateData); | |
| message.success("Guardrail updated successfully"); | |
| fetchGuardrailInfo(); | |
| setIsEditing(false); | |
| } catch (error) { | |
| console.error("Error updating guardrail:", error); | |
| message.error("Failed to update guardrail"); | |
| } | |
| }; | |
| if (loading) { | |
| return <div className="p-4">Loading...</div>; | |
| } | |
| if (!guardrailData) { | |
| return <div className="p-4">Guardrail not found</div>; | |
| } | |
| // Format date helper function | |
| const formatDate = (dateString?: string) => { | |
| if (!dateString) return "-"; | |
| const date = new Date(dateString); | |
| return date.toLocaleString(); | |
| }; | |
| // Format the provider display name and logo | |
| const { logo, displayName } = getGuardrailLogoAndName(guardrailData.litellm_params?.guardrail || ""); | |
| return ( | |
| <div className="p-4"> | |
| <div className="flex justify-between items-center mb-6"> | |
| <div> | |
| <Button onClick={onClose} className="mb-4">← Back</Button> | |
| <Title>{guardrailData.guardrail_name || "Unnamed Guardrail"}</Title> | |
| <Text className="text-gray-500 font-mono">{guardrailData.guardrail_id}</Text> | |
| </div> | |
| </div> | |
| <TabGroup> | |
| <TabList className="mb-4"> | |
| <Tab key="overview">Overview</Tab> | |
| {isAdmin ? <Tab key="settings">Settings</Tab> : <></>} | |
| </TabList> | |
| <TabPanels> | |
| {/* Overview Panel */} | |
| <TabPanel> | |
| <Grid numItems={1} numItemsSm={2} numItemsLg={3} className="gap-6"> | |
| <Card> | |
| <Text>Provider</Text> | |
| <div className="mt-2 flex items-center space-x-2"> | |
| {logo && ( | |
| <img | |
| src={logo} | |
| alt={`${displayName} logo`} | |
| className="w-6 h-6" | |
| onError={(e) => { | |
| // Hide broken image | |
| (e.target as HTMLImageElement).style.display = 'none'; | |
| }} | |
| /> | |
| )} | |
| <Title>{displayName}</Title> | |
| </div> | |
| </Card> | |
| <Card> | |
| <Text>Mode</Text> | |
| <div className="mt-2"> | |
| <Title>{guardrailData.litellm_params?.mode || "-"}</Title> | |
| <Badge color={guardrailData.litellm_params?.default_on ? "green" : "gray"}> | |
| {guardrailData.litellm_params?.default_on ? "Default On" : "Default Off"} | |
| </Badge> | |
| </div> | |
| </Card> | |
| <Card> | |
| <Text>Created At</Text> | |
| <div className="mt-2"> | |
| <Title>{formatDate(guardrailData.created_at)}</Title> | |
| <Text>Last Updated: {formatDate(guardrailData.updated_at)}</Text> | |
| </div> | |
| </Card> | |
| </Grid> | |
| {guardrailData.litellm_params?.pii_entities_config && Object.keys(guardrailData.litellm_params.pii_entities_config).length > 0 && ( | |
| <Card className="mt-6"> | |
| <div className="flex justify-between items-center"> | |
| <Text className="font-medium">PII Protection</Text> | |
| <Badge color="blue"> | |
| {Object.keys(guardrailData.litellm_params.pii_entities_config).length} PII entities configured | |
| </Badge> | |
| </div> | |
| </Card> | |
| )} | |
| {guardrailData.guardrail_info && Object.keys(guardrailData.guardrail_info).length > 0 && ( | |
| <Card className="mt-6"> | |
| <Text>Guardrail Info</Text> | |
| <div className="mt-2 space-y-2"> | |
| {Object.entries(guardrailData.guardrail_info).map(([key, value]) => ( | |
| <div key={key} className="flex"> | |
| <Text className="font-medium w-1/3">{key}</Text> | |
| <Text className="w-2/3"> | |
| {typeof value === 'object' | |
| ? JSON.stringify(value, null, 2) | |
| : String(value)} | |
| </Text> | |
| </div> | |
| ))} | |
| </div> | |
| </Card> | |
| )} | |
| </TabPanel> | |
| {/* Settings Panel (only for admins) */} | |
| {isAdmin && ( | |
| <TabPanel> | |
| <Card> | |
| <div className="flex justify-between items-center mb-4"> | |
| <Title>Guardrail Settings</Title> | |
| {!isEditing && ( | |
| <TremorButton | |
| onClick={() => setIsEditing(true)} | |
| > | |
| Edit Settings | |
| </TremorButton> | |
| )} | |
| </div> | |
| {isEditing ? ( | |
| <Form | |
| form={form} | |
| onFinish={handleGuardrailUpdate} | |
| initialValues={{ | |
| guardrail_name: guardrailData.guardrail_name, | |
| ...guardrailData.litellm_params, | |
| guardrail_info: guardrailData.guardrail_info | |
| ? JSON.stringify(guardrailData.guardrail_info, null, 2) | |
| : "", | |
| }} | |
| layout="vertical" | |
| > | |
| <Form.Item | |
| label="Guardrail Name" | |
| name="guardrail_name" | |
| rules={[{ required: true, message: "Please input a guardrail name" }]} | |
| > | |
| <TextInput /> | |
| </Form.Item> | |
| <Form.Item | |
| label="Default On" | |
| name="default_on" | |
| > | |
| <Select> | |
| <Select.Option value={true}>Yes</Select.Option> | |
| <Select.Option value={false}>No</Select.Option> | |
| </Select> | |
| </Form.Item> | |
| <Divider orientation="left">PII Protection</Divider> | |
| <div className="mb-6"> | |
| {guardrailSettings && ( | |
| <PiiConfiguration | |
| entities={guardrailSettings.supported_entities} | |
| actions={guardrailSettings.supported_actions} | |
| selectedEntities={selectedPiiEntities} | |
| selectedActions={selectedPiiActions} | |
| onEntitySelect={handlePiiEntitySelect} | |
| onActionSelect={handlePiiActionSelect} | |
| entityCategories={guardrailSettings.pii_entity_categories} | |
| /> | |
| )} | |
| </div> | |
| <Divider orientation="left">Advanced Settings</Divider> | |
| <Form.Item | |
| label="Guardrail Information" | |
| name="guardrail_info" | |
| > | |
| <Input.TextArea rows={5} /> | |
| </Form.Item> | |
| <div className="flex justify-end gap-2 mt-6"> | |
| <Button onClick={() => setIsEditing(false)}> | |
| Cancel | |
| </Button> | |
| <TremorButton> | |
| Save Changes | |
| </TremorButton> | |
| </div> | |
| </Form> | |
| ) : ( | |
| <div className="space-y-4"> | |
| <div> | |
| <Text className="font-medium">Guardrail ID</Text> | |
| <div className="font-mono">{guardrailData.guardrail_id}</div> | |
| </div> | |
| <div> | |
| <Text className="font-medium">Guardrail Name</Text> | |
| <div>{guardrailData.guardrail_name || "Unnamed Guardrail"}</div> | |
| </div> | |
| <div> | |
| <Text className="font-medium">Provider</Text> | |
| <div>{displayName}</div> | |
| </div> | |
| <div> | |
| <Text className="font-medium">Mode</Text> | |
| <div>{guardrailData.litellm_params?.mode || "-"}</div> | |
| </div> | |
| <div> | |
| <Text className="font-medium">Default On</Text> | |
| <Badge color={guardrailData.litellm_params?.default_on ? "green" : "gray"}> | |
| {guardrailData.litellm_params?.default_on ? "Yes" : "No"} | |
| </Badge> | |
| </div> | |
| {guardrailData.litellm_params?.pii_entities_config && Object.keys(guardrailData.litellm_params.pii_entities_config).length > 0 && ( | |
| <div> | |
| <Text className="font-medium">PII Protection</Text> | |
| <div className="mt-2"> | |
| <Badge color="blue"> | |
| {Object.keys(guardrailData.litellm_params.pii_entities_config).length} PII entities configured | |
| </Badge> | |
| </div> | |
| </div> | |
| )} | |
| <div> | |
| <Text className="font-medium">Created At</Text> | |
| <div>{formatDate(guardrailData.created_at)}</div> | |
| </div> | |
| <div> | |
| <Text className="font-medium">Last Updated</Text> | |
| <div>{formatDate(guardrailData.updated_at)}</div> | |
| </div> | |
| </div> | |
| )} | |
| </Card> | |
| </TabPanel> | |
| )} | |
| </TabPanels> | |
| </TabGroup> | |
| </div> | |
| ); | |
| }; | |
| export default GuardrailInfoView; |