Spaces:
Paused
Paused
| import React, { useState, useEffect } from 'react'; | |
| import { Form, Typography, Select, Input, Switch, Modal, message, Divider } from 'antd'; | |
| import { Button, TextInput } from '@tremor/react'; | |
| import { GuardrailProviders, guardrail_provider_map, guardrailLogoMap } from './guardrail_info_helpers'; | |
| import { getGuardrailUISettings } from '../networking'; | |
| import PiiConfiguration from './pii_configuration'; | |
| const { Title, Text } = Typography; | |
| const { Option } = Select; | |
| interface EditGuardrailFormProps { | |
| visible: boolean; | |
| onClose: () => void; | |
| accessToken: string | null; | |
| onSuccess: () => void; | |
| guardrailId: string; | |
| initialValues: { | |
| guardrail_name: string; | |
| provider: string; | |
| mode: string; | |
| default_on: boolean; | |
| pii_entities_config?: {[key: string]: string}; | |
| [key: string]: any; | |
| }; | |
| } | |
| interface GuardrailSettings { | |
| supported_entities: string[]; | |
| supported_actions: string[]; | |
| supported_modes: string[]; | |
| pii_entity_categories: Array<{ | |
| category: string; | |
| entities: string[]; | |
| }>; | |
| } | |
| const EditGuardrailForm: React.FC<EditGuardrailFormProps> = ({ | |
| visible, | |
| onClose, | |
| accessToken, | |
| onSuccess, | |
| guardrailId, | |
| initialValues | |
| }) => { | |
| const [form] = Form.useForm(); | |
| const [loading, setLoading] = useState(false); | |
| const [selectedProvider, setSelectedProvider] = useState<string | null>(initialValues?.provider || null); | |
| const [guardrailSettings, setGuardrailSettings] = useState<GuardrailSettings | null>(null); | |
| const [selectedEntities, setSelectedEntities] = useState<string[]>([]); | |
| const [selectedActions, setSelectedActions] = useState<{[key: string]: string}>({}); | |
| // Fetch guardrail settings when the component mounts | |
| useEffect(() => { | |
| const fetchGuardrailSettings = async () => { | |
| try { | |
| if (!accessToken) return; | |
| const data = await getGuardrailUISettings(accessToken); | |
| setGuardrailSettings(data); | |
| } catch (error) { | |
| console.error('Error fetching guardrail settings:', error); | |
| message.error('Failed to load guardrail settings'); | |
| } | |
| }; | |
| fetchGuardrailSettings(); | |
| }, [accessToken]); | |
| // Initialize selected entities and actions from initialValues | |
| useEffect(() => { | |
| if (initialValues?.pii_entities_config && Object.keys(initialValues.pii_entities_config).length > 0) { | |
| const entities = Object.keys(initialValues.pii_entities_config); | |
| setSelectedEntities(entities); | |
| setSelectedActions(initialValues.pii_entities_config); | |
| } | |
| }, [initialValues]); | |
| const handleProviderChange = (value: string) => { | |
| setSelectedProvider(value); | |
| // Reset form fields that are provider-specific | |
| form.setFieldsValue({ | |
| config: undefined | |
| }); | |
| // Reset PII selections when changing provider | |
| setSelectedEntities([]); | |
| setSelectedActions({}); | |
| }; | |
| const handleEntitySelect = (entity: string) => { | |
| setSelectedEntities(prev => { | |
| if (prev.includes(entity)) { | |
| return prev.filter(e => e !== entity); | |
| } else { | |
| return [...prev, entity]; | |
| } | |
| }); | |
| }; | |
| const handleActionSelect = (entity: string, action: string) => { | |
| setSelectedActions(prev => ({ | |
| ...prev, | |
| [entity]: action | |
| })); | |
| }; | |
| const handleSubmit = async () => { | |
| try { | |
| setLoading(true); | |
| const values = await form.validateFields(); | |
| // Get the guardrail provider value from the map | |
| const guardrailProvider = guardrail_provider_map[values.provider]; | |
| // Prepare the guardrail data with proper types for litellm_params | |
| const guardrailData: { | |
| guardrail_id: string; | |
| guardrail: { | |
| guardrail_name: string; | |
| litellm_params: { | |
| guardrail: string; | |
| mode: string; | |
| default_on: boolean; | |
| [key: string]: any; // Allow dynamic properties | |
| }; | |
| guardrail_info: any; | |
| } | |
| } = { | |
| guardrail_id: guardrailId, | |
| guardrail: { | |
| guardrail_name: values.guardrail_name, | |
| litellm_params: { | |
| guardrail: guardrailProvider, | |
| mode: values.mode, | |
| default_on: values.default_on | |
| }, | |
| guardrail_info: {} | |
| } | |
| }; | |
| // For Presidio PII, add the entity and action configurations | |
| if (values.provider === 'PresidioPII' && selectedEntities.length > 0) { | |
| const piiEntitiesConfig: {[key: string]: string} = {}; | |
| selectedEntities.forEach(entity => { | |
| piiEntitiesConfig[entity] = selectedActions[entity] || 'MASK'; // Default to MASK if no action selected | |
| }); | |
| guardrailData.guardrail.litellm_params.pii_entities_config = piiEntitiesConfig; | |
| } | |
| // Add config values to the guardrail_info if provided | |
| else if (values.config) { | |
| try { | |
| const configObj = JSON.parse(values.config); | |
| // For some guardrails, the config values need to be in litellm_params | |
| // Especially for providers like Bedrock that need guardrailIdentifier and guardrailVersion | |
| if (values.provider === 'Bedrock' && configObj) { | |
| if (configObj.guardrail_id) { | |
| guardrailData.guardrail.litellm_params.guardrailIdentifier = configObj.guardrail_id; | |
| } | |
| if (configObj.guardrail_version) { | |
| guardrailData.guardrail.litellm_params.guardrailVersion = configObj.guardrail_version; | |
| } | |
| } else { | |
| // For other providers, add the config to guardrail_info | |
| guardrailData.guardrail.guardrail_info = configObj; | |
| } | |
| } catch (error) { | |
| message.error('Invalid JSON in configuration'); | |
| setLoading(false); | |
| return; | |
| } | |
| } | |
| if (!accessToken) { | |
| throw new Error("No access token available"); | |
| } | |
| console.log("Sending guardrail update data:", JSON.stringify(guardrailData)); | |
| // Call the update endpoint | |
| const url = `/guardrails/${guardrailId}`; | |
| const response = await fetch(url, { | |
| method: "PUT", | |
| headers: { | |
| "Authorization": `Bearer ${accessToken}`, | |
| "Content-Type": "application/json", | |
| }, | |
| body: JSON.stringify(guardrailData), | |
| }); | |
| if (!response.ok) { | |
| const errorData = await response.text(); | |
| throw new Error(errorData || "Failed to update guardrail"); | |
| } | |
| message.success('Guardrail updated successfully'); | |
| // Reset and close | |
| onSuccess(); | |
| onClose(); | |
| } catch (error) { | |
| console.error("Failed to update guardrail:", error); | |
| message.error('Failed to update guardrail: ' + (error instanceof Error ? error.message : String(error))); | |
| } finally { | |
| setLoading(false); | |
| } | |
| }; | |
| const renderPiiConfiguration = () => { | |
| if (!guardrailSettings || !selectedProvider || selectedProvider !== 'PresidioPII') return null; | |
| return ( | |
| <PiiConfiguration | |
| entities={guardrailSettings.supported_entities} | |
| actions={guardrailSettings.supported_actions} | |
| selectedEntities={selectedEntities} | |
| selectedActions={selectedActions} | |
| onEntitySelect={handleEntitySelect} | |
| onActionSelect={handleActionSelect} | |
| entityCategories={guardrailSettings.pii_entity_categories} | |
| /> | |
| ); | |
| }; | |
| const renderProviderSpecificFields = () => { | |
| if (!selectedProvider) return null; | |
| // For Presidio, we use the new PII configuration UI | |
| if (selectedProvider === 'PresidioPII') { | |
| return renderPiiConfiguration(); | |
| } | |
| switch (selectedProvider) { | |
| case 'Aporia': | |
| return ( | |
| <Form.Item | |
| label="Aporia Configuration" | |
| name="config" | |
| tooltip="JSON configuration for Aporia" | |
| > | |
| <Input.TextArea | |
| rows={4} | |
| placeholder={`{ | |
| "api_key": "your_aporia_api_key", | |
| "project_name": "your_project_name" | |
| }`} | |
| /> | |
| </Form.Item> | |
| ); | |
| case 'AimSecurity': | |
| return ( | |
| <Form.Item | |
| label="Aim Security Configuration" | |
| name="config" | |
| tooltip="JSON configuration for Aim Security" | |
| > | |
| <Input.TextArea | |
| rows={4} | |
| placeholder={`{ | |
| "api_key": "your_aim_api_key" | |
| }`} | |
| /> | |
| </Form.Item> | |
| ); | |
| case 'Bedrock': | |
| return ( | |
| <Form.Item | |
| label="Amazon Bedrock Configuration" | |
| name="config" | |
| tooltip="JSON configuration for Amazon Bedrock guardrails" | |
| > | |
| <Input.TextArea | |
| rows={4} | |
| placeholder={`{ | |
| "guardrail_id": "your_guardrail_id", | |
| "guardrail_version": "your_guardrail_version" | |
| }`} | |
| /> | |
| </Form.Item> | |
| ); | |
| case 'GuardrailsAI': | |
| return ( | |
| <Form.Item | |
| label="Guardrails.ai Configuration" | |
| name="config" | |
| tooltip="JSON configuration for Guardrails.ai" | |
| > | |
| <Input.TextArea | |
| rows={4} | |
| placeholder={`{ | |
| "api_key": "your_guardrails_api_key", | |
| "guardrail_id": "your_guardrail_id" | |
| }`} | |
| /> | |
| </Form.Item> | |
| ); | |
| case 'LakeraAI': | |
| return ( | |
| <Form.Item | |
| label="Lakera AI Configuration" | |
| name="config" | |
| tooltip="JSON configuration for Lakera AI" | |
| > | |
| <Input.TextArea | |
| rows={4} | |
| placeholder={`{ | |
| "api_key": "your_lakera_api_key" | |
| }`} | |
| /> | |
| </Form.Item> | |
| ); | |
| case 'PromptInjection': | |
| return ( | |
| <Form.Item | |
| label="Prompt Injection Configuration" | |
| name="config" | |
| tooltip="JSON configuration for prompt injection detection" | |
| > | |
| <Input.TextArea | |
| rows={4} | |
| placeholder={`{ | |
| "threshold": 0.8 | |
| }`} | |
| /> | |
| </Form.Item> | |
| ); | |
| default: | |
| return ( | |
| <Form.Item | |
| label="Custom Configuration" | |
| name="config" | |
| tooltip="JSON configuration for your custom guardrail" | |
| > | |
| <Input.TextArea | |
| rows={4} | |
| placeholder={`{ | |
| "key1": "value1", | |
| "key2": "value2" | |
| }`} | |
| /> | |
| </Form.Item> | |
| ); | |
| } | |
| }; | |
| return ( | |
| <Modal | |
| title="Edit Guardrail" | |
| open={visible} | |
| onCancel={onClose} | |
| footer={null} | |
| width={700} | |
| > | |
| <Form | |
| form={form} | |
| layout="vertical" | |
| initialValues={initialValues} | |
| > | |
| <Form.Item | |
| name="guardrail_name" | |
| label="Guardrail Name" | |
| rules={[{ required: true, message: 'Please enter a guardrail name' }]} | |
| > | |
| <TextInput placeholder="Enter a name for this guardrail" /> | |
| </Form.Item> | |
| <Form.Item | |
| name="provider" | |
| label="Guardrail Provider" | |
| rules={[{ required: true, message: 'Please select a provider' }]} | |
| > | |
| <Select | |
| placeholder="Select a guardrail provider" | |
| onChange={handleProviderChange} | |
| disabled={true} // Disable changing provider in edit mode | |
| optionLabelProp="label" | |
| > | |
| {Object.entries(GuardrailProviders).map(([key, value]) => ( | |
| <Option | |
| key={key} | |
| value={key} | |
| label={value} | |
| > | |
| <div style={{ display: 'flex', alignItems: 'center' }}> | |
| {guardrailLogoMap[value] && ( | |
| <img | |
| src={guardrailLogoMap[value]} | |
| alt="" | |
| style={{ | |
| height: '20px', | |
| width: '20px', | |
| marginRight: '8px', | |
| objectFit: 'contain' | |
| }} | |
| onError={(e) => { | |
| // Hide broken image icon if image fails to load | |
| e.currentTarget.style.display = 'none'; | |
| }} | |
| /> | |
| )} | |
| <span>{value}</span> | |
| </div> | |
| </Option> | |
| ))} | |
| </Select> | |
| </Form.Item> | |
| <Form.Item | |
| name="mode" | |
| label="Mode" | |
| tooltip="How the guardrail should be applied" | |
| rules={[{ required: true, message: 'Please select a mode' }]} | |
| > | |
| <Select> | |
| {guardrailSettings?.supported_modes?.map(mode => ( | |
| <Option key={mode} value={mode}>{mode}</Option> | |
| )) || ( | |
| <> | |
| <Option value="pre_call">pre_call</Option> | |
| <Option value="post_call">post_call</Option> | |
| </> | |
| )} | |
| </Select> | |
| </Form.Item> | |
| <Form.Item | |
| name="default_on" | |
| label="Always On" | |
| tooltip="If enabled, this guardrail will be applied to all requests by default" | |
| valuePropName="checked" | |
| > | |
| <Switch /> | |
| </Form.Item> | |
| {renderProviderSpecificFields()} | |
| <div className="flex justify-end space-x-2 mt-4"> | |
| <Button | |
| variant="secondary" | |
| onClick={onClose} | |
| > | |
| Cancel | |
| </Button> | |
| <Button | |
| onClick={handleSubmit} | |
| loading={loading} | |
| > | |
| Update Guardrail | |
| </Button> | |
| </div> | |
| </Form> | |
| </Modal> | |
| ); | |
| }; | |
| export default EditGuardrailForm; |