mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-07 02:59:05 +00:00
feat(ui/): improve ux around adding new content filter categories
easy to skip adding a category, so make it a 1-click thing
This commit is contained in:
parent
472546289a
commit
1da6656f20
3 changed files with 83 additions and 6 deletions
|
|
@ -109,6 +109,7 @@ const AddGuardrailForm: React.FC<AddGuardrailFormProps> = ({ visible, onClose, a
|
|||
const [selectedPatterns, setSelectedPatterns] = useState<any[]>([]);
|
||||
const [blockedWords, setBlockedWords] = useState<any[]>([]);
|
||||
const [selectedContentCategories, setSelectedContentCategories] = useState<any[]>([]);
|
||||
const [pendingCategorySelection, setPendingCategorySelection] = useState<string>("");
|
||||
const [toolPermissionConfig, setToolPermissionConfig] = useState<ToolPermissionConfig>({
|
||||
rules: [],
|
||||
default_action: "deny",
|
||||
|
|
@ -169,6 +170,12 @@ const AddGuardrailForm: React.FC<AddGuardrailFormProps> = ({ visible, onClose, a
|
|||
setGlobalSeverityThreshold(2);
|
||||
setCategorySpecificThresholds({});
|
||||
|
||||
// Reset Content Filter selections
|
||||
setSelectedPatterns([]);
|
||||
setBlockedWords([]);
|
||||
setSelectedContentCategories([]);
|
||||
setPendingCategorySelection("");
|
||||
|
||||
setToolPermissionConfig({
|
||||
rules: [],
|
||||
default_action: "deny",
|
||||
|
|
@ -247,6 +254,39 @@ const AddGuardrailForm: React.FC<AddGuardrailFormProps> = ({ visible, onClose, a
|
|||
setCurrentStep(currentStep - 1);
|
||||
};
|
||||
|
||||
const handleAddAndContinue = () => {
|
||||
if (!pendingCategorySelection || !guardrailSettings) return;
|
||||
|
||||
const contentFilterSettings = guardrailSettings.content_filter_settings;
|
||||
if (!contentFilterSettings) return;
|
||||
|
||||
const category = contentFilterSettings.content_categories?.find((c) => c.name === pendingCategorySelection);
|
||||
if (!category) return;
|
||||
|
||||
// Check if already added
|
||||
if (selectedContentCategories.some((c) => c.category === pendingCategorySelection)) {
|
||||
setPendingCategorySelection("");
|
||||
setCurrentStep(currentStep + 1);
|
||||
return;
|
||||
}
|
||||
|
||||
// Add the category
|
||||
setSelectedContentCategories([
|
||||
...selectedContentCategories,
|
||||
{
|
||||
id: `category-${Date.now()}`,
|
||||
category: category.name,
|
||||
display_name: category.display_name,
|
||||
action: category.default_action as "BLOCK" | "MASK",
|
||||
severity_threshold: "medium",
|
||||
},
|
||||
]);
|
||||
|
||||
// Clear pending selection and advance to next step
|
||||
setPendingCategorySelection("");
|
||||
setCurrentStep(currentStep + 1);
|
||||
};
|
||||
|
||||
const resetForm = () => {
|
||||
form.resetFields();
|
||||
setSelectedProvider(null);
|
||||
|
|
@ -258,6 +298,7 @@ const AddGuardrailForm: React.FC<AddGuardrailFormProps> = ({ visible, onClose, a
|
|||
setSelectedPatterns([]);
|
||||
setBlockedWords([]);
|
||||
setSelectedContentCategories([]);
|
||||
setPendingCategorySelection("");
|
||||
setToolPermissionConfig({
|
||||
rules: [],
|
||||
default_action: "deny",
|
||||
|
|
@ -667,6 +708,8 @@ const AddGuardrailForm: React.FC<AddGuardrailFormProps> = ({ visible, onClose, a
|
|||
selectedContentCategories.map((c) => (c.id === id ? { ...c, [field]: value } : c))
|
||||
);
|
||||
}}
|
||||
pendingCategorySelection={pendingCategorySelection}
|
||||
onPendingCategorySelectionChange={setPendingCategorySelection}
|
||||
accessToken={accessToken}
|
||||
showStep={step}
|
||||
/>
|
||||
|
|
@ -729,6 +772,8 @@ const AddGuardrailForm: React.FC<AddGuardrailFormProps> = ({ visible, onClose, a
|
|||
const renderStepButtons = () => {
|
||||
const totalSteps = shouldRenderContentFilterConfigSettings(selectedProvider) ? 4 : 2;
|
||||
const isLastStep = currentStep === totalSteps - 1;
|
||||
const isCategoriesStep = shouldRenderContentFilterConfigSettings(selectedProvider) && currentStep === 1;
|
||||
const hasPendingCategory = pendingCategorySelection !== "";
|
||||
|
||||
return (
|
||||
<div className="flex justify-end space-x-2 mt-4">
|
||||
|
|
@ -737,11 +782,30 @@ const AddGuardrailForm: React.FC<AddGuardrailFormProps> = ({ visible, onClose, a
|
|||
Previous
|
||||
</Button>
|
||||
)}
|
||||
{!isLastStep && <Button type="primary" onClick={nextStep}>Next</Button>}
|
||||
{isLastStep && (
|
||||
<Button type="primary" onClick={handleSubmit} loading={loading}>
|
||||
Create Guardrail
|
||||
</Button>
|
||||
{isCategoriesStep ? (
|
||||
<>
|
||||
<Button onClick={nextStep}>
|
||||
Skip
|
||||
</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={handleAddAndContinue}
|
||||
disabled={!hasPendingCategory}
|
||||
>
|
||||
Add & Continue →
|
||||
</Button>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{!isLastStep && (
|
||||
<Button type="primary" onClick={nextStep}>Next</Button>
|
||||
)}
|
||||
{isLastStep && (
|
||||
<Button type="primary" onClick={handleSubmit} loading={loading}>
|
||||
Create Guardrail
|
||||
</Button>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
<Button onClick={handleClose}>
|
||||
Cancel
|
||||
|
|
|
|||
|
|
@ -28,6 +28,8 @@ interface ContentCategoryConfigurationProps {
|
|||
onCategoryRemove: (id: string) => void;
|
||||
onCategoryUpdate: (id: string, field: string, value: any) => void;
|
||||
accessToken?: string | null;
|
||||
pendingSelection?: string;
|
||||
onPendingSelectionChange?: (value: string) => void;
|
||||
}
|
||||
|
||||
const ContentCategoryConfiguration: React.FC<ContentCategoryConfigurationProps> = ({
|
||||
|
|
@ -37,8 +39,13 @@ const ContentCategoryConfiguration: React.FC<ContentCategoryConfigurationProps>
|
|||
onCategoryRemove,
|
||||
onCategoryUpdate,
|
||||
accessToken,
|
||||
pendingSelection,
|
||||
onPendingSelectionChange,
|
||||
}) => {
|
||||
const [selectedCategoryName, setSelectedCategoryName] = React.useState<string>("");
|
||||
// Use controlled state if parent provides it, otherwise use local state
|
||||
const [localSelectedCategoryName, setLocalSelectedCategoryName] = React.useState<string>("");
|
||||
const selectedCategoryName = pendingSelection !== undefined ? pendingSelection : localSelectedCategoryName;
|
||||
const setSelectedCategoryName = onPendingSelectionChange || setLocalSelectedCategoryName;
|
||||
const [categoryYaml, setCategoryYaml] = React.useState<{ [key: string]: string }>({});
|
||||
const [categoryFileTypes, setCategoryFileTypes] = React.useState<{ [key: string]: string }>({});
|
||||
const [loadingYaml, setLoadingYaml] = React.useState<{ [key: string]: boolean }>({});
|
||||
|
|
|
|||
|
|
@ -69,6 +69,8 @@ interface ContentFilterConfigurationProps {
|
|||
onContentCategoryAdd?: (category: SelectedContentCategory) => void;
|
||||
onContentCategoryRemove?: (id: string) => void;
|
||||
onContentCategoryUpdate?: (id: string, field: string, value: any) => void;
|
||||
pendingCategorySelection?: string;
|
||||
onPendingCategorySelectionChange?: (value: string) => void;
|
||||
}
|
||||
|
||||
const ContentFilterConfiguration: React.FC<ContentFilterConfigurationProps> = ({
|
||||
|
|
@ -90,6 +92,8 @@ const ContentFilterConfiguration: React.FC<ContentFilterConfigurationProps> = ({
|
|||
onContentCategoryAdd,
|
||||
onContentCategoryRemove,
|
||||
onContentCategoryUpdate,
|
||||
pendingCategorySelection,
|
||||
onPendingCategorySelectionChange,
|
||||
}) => {
|
||||
const [patternModalVisible, setPatternModalVisible] = useState(false);
|
||||
const [keywordModalVisible, setKeywordModalVisible] = useState(false);
|
||||
|
|
@ -278,6 +282,8 @@ const ContentFilterConfiguration: React.FC<ContentFilterConfigurationProps> = ({
|
|||
onCategoryRemove={onContentCategoryRemove}
|
||||
onCategoryUpdate={onContentCategoryUpdate}
|
||||
accessToken={accessToken}
|
||||
pendingSelection={pendingCategorySelection}
|
||||
onPendingSelectionChange={onPendingCategorySelectionChange}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue