Spaces:
Configuration error
Configuration error
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; |