mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-05 02:41:56 +00:00
feat(ui): i18n for add guardrail form
This commit is contained in:
parent
6484d548ec
commit
faed97b869
3 changed files with 231 additions and 89 deletions
|
|
@ -1,5 +1,7 @@
|
|||
import { Form, Input, Modal, Select, Tag, Typography, Button } from "antd";
|
||||
import type { TFunction } from "i18next";
|
||||
import React, { useEffect, useMemo, useState } from "react";
|
||||
import { Trans, useTranslation } from "react-i18next";
|
||||
import NotificationsManager from "../molecules/notifications_manager";
|
||||
import {
|
||||
createGuardrailCall,
|
||||
|
|
@ -29,15 +31,14 @@ import ToolPermissionRulesEditor, { ToolPermissionConfig } from "./tool_permissi
|
|||
const { Title, Text, Link } = Typography;
|
||||
const { Option } = Select;
|
||||
|
||||
// Define human-friendly descriptions for each mode
|
||||
const modeDescriptions = {
|
||||
pre_call: "Before LLM Call - Runs before the LLM call and checks the input (Recommended)",
|
||||
during_call: "During LLM Call - Runs in parallel with the LLM call, with response held until check completes",
|
||||
post_call: "After LLM Call - Runs after the LLM call and checks only the output",
|
||||
logging_only: "Logging Only - Only runs on logging callbacks without affecting the LLM call",
|
||||
pre_mcp_call: "Before MCP Tool Call - Runs before MCP tool execution and validates tool calls",
|
||||
during_mcp_call: "During MCP Tool Call - Runs in parallel with MCP tool execution for monitoring",
|
||||
};
|
||||
const getModeDescriptions = (t: TFunction) => ({
|
||||
pre_call: t("guardrails.addGuardrailForm.modeDesc.pre_call"),
|
||||
during_call: t("guardrails.addGuardrailForm.modeDesc.during_call"),
|
||||
post_call: t("guardrails.addGuardrailForm.modeDesc.post_call"),
|
||||
logging_only: t("guardrails.addGuardrailForm.modeDesc.logging_only"),
|
||||
pre_mcp_call: t("guardrails.addGuardrailForm.modeDesc.pre_mcp_call"),
|
||||
during_mcp_call: t("guardrails.addGuardrailForm.modeDesc.during_mcp_call"),
|
||||
});
|
||||
|
||||
interface GuardrailPreset {
|
||||
provider: string;
|
||||
|
|
@ -106,6 +107,8 @@ interface ProviderParamsResponse {
|
|||
}
|
||||
|
||||
const AddGuardrailForm: React.FC<AddGuardrailFormProps> = ({ visible, onClose, accessToken, onSuccess, preset }) => {
|
||||
const { t } = useTranslation();
|
||||
const modeDescriptions = useMemo(() => getModeDescriptions(t), [t]);
|
||||
const [form] = Form.useForm();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [selectedProvider, setSelectedProvider] = useState<string | null>(null);
|
||||
|
|
@ -175,7 +178,7 @@ const AddGuardrailForm: React.FC<AddGuardrailFormProps> = ({ visible, onClose, a
|
|||
populateGuardrailProviderMap(providerParamsResp);
|
||||
} catch (error) {
|
||||
console.error("Error fetching guardrail data:", error);
|
||||
NotificationsManager.fromBackend("Failed to load guardrail configuration");
|
||||
NotificationsManager.fromBackend(t("guardrails.addGuardrailForm.failedToLoadConfig"));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -318,7 +321,7 @@ const AddGuardrailForm: React.FC<AddGuardrailFormProps> = ({ visible, onClose, a
|
|||
// Validate configuration steps
|
||||
if (currentStep === 1) {
|
||||
if (shouldRenderPIIConfigSettings(selectedProvider) && selectedEntities.length === 0) {
|
||||
NotificationsManager.fromBackend("Please select at least one PII entity to continue");
|
||||
NotificationsManager.fromBackend(t("guardrails.addGuardrailForm.selectAtLeastOnePii"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -473,9 +476,7 @@ const AddGuardrailForm: React.FC<AddGuardrailFormProps> = ({ visible, onClose, a
|
|||
selectedContentCategories.length === 0 &&
|
||||
!hasCompetitorIntent
|
||||
) {
|
||||
NotificationsManager.fromBackend(
|
||||
"Please configure at least one content filter setting (category, pattern, keyword, or competitor intent)",
|
||||
);
|
||||
NotificationsManager.fromBackend(t("guardrails.addGuardrailForm.configureAtLeastOneFilter"));
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
|
|
@ -528,7 +529,7 @@ const AddGuardrailForm: React.FC<AddGuardrailFormProps> = ({ visible, onClose, a
|
|||
// For some guardrails, the config values need to be in litellm_params
|
||||
guardrailData.guardrail_info = configObj;
|
||||
} catch (error) {
|
||||
NotificationsManager.fromBackend("Invalid JSON in configuration");
|
||||
NotificationsManager.fromBackend(t("guardrails.addGuardrailForm.invalidJsonConfig"));
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
|
|
@ -537,13 +538,13 @@ const AddGuardrailForm: React.FC<AddGuardrailFormProps> = ({ visible, onClose, a
|
|||
if (guardrailProvider === "llm_as_a_judge") {
|
||||
const criteria: any[] = values.criteria || [];
|
||||
if (criteria.length === 0) {
|
||||
NotificationsManager.fromBackend("Add at least one evaluation criterion");
|
||||
NotificationsManager.fromBackend(t("guardrails.addGuardrailForm.addAtLeastOneCriterion"));
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
const weightTotal = criteria.reduce((sum: number, c: any) => sum + (Number(c?.weight) || 0), 0);
|
||||
if (weightTotal !== 100) {
|
||||
NotificationsManager.fromBackend(`Criterion weights must sum to 100% (currently ${weightTotal}%)`);
|
||||
NotificationsManager.fromBackend(t("guardrails.addGuardrailForm.weightsMustSum100", { weightTotal }));
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
|
|
@ -559,7 +560,7 @@ const AddGuardrailForm: React.FC<AddGuardrailFormProps> = ({ visible, onClose, a
|
|||
|
||||
if (guardrailProvider === "tool_permission") {
|
||||
if (toolPermissionConfig.rules.length === 0) {
|
||||
NotificationsManager.fromBackend("Add at least one tool permission rule");
|
||||
NotificationsManager.fromBackend(t("guardrails.addGuardrailForm.addAtLeastOneRule"));
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
|
|
@ -641,7 +642,7 @@ const AddGuardrailForm: React.FC<AddGuardrailFormProps> = ({ visible, onClose, a
|
|||
console.log("Sending guardrail data:", JSON.stringify(guardrailData));
|
||||
await createGuardrailCall(accessToken, guardrailData);
|
||||
|
||||
NotificationsManager.success("Guardrail created successfully");
|
||||
NotificationsManager.success(t("guardrails.addGuardrailForm.createSuccess"));
|
||||
|
||||
// Reset form and close modal
|
||||
resetForm();
|
||||
|
|
@ -650,7 +651,9 @@ const AddGuardrailForm: React.FC<AddGuardrailFormProps> = ({ visible, onClose, a
|
|||
} catch (error) {
|
||||
console.error("Failed to create guardrail:", error);
|
||||
NotificationsManager.fromBackend(
|
||||
"Failed to create guardrail: " + (error instanceof Error ? error.message : String(error)),
|
||||
t("guardrails.addGuardrailForm.createFailed", {
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
}),
|
||||
);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
|
|
@ -662,19 +665,19 @@ const AddGuardrailForm: React.FC<AddGuardrailFormProps> = ({ visible, onClose, a
|
|||
<>
|
||||
<Form.Item
|
||||
name="guardrail_name"
|
||||
label="Guardrail Name"
|
||||
rules={[{ required: true, message: "Please enter a guardrail name" }]}
|
||||
label={t("guardrails.addGuardrailForm.guardrailNameLabel")}
|
||||
rules={[{ required: true, message: t("guardrails.addGuardrailForm.guardrailNameRequired") }]}
|
||||
>
|
||||
<Input placeholder="Enter a name for this guardrail" />
|
||||
<Input placeholder={t("guardrails.addGuardrailForm.guardrailNamePlaceholder")} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="provider"
|
||||
label="Guardrail Provider"
|
||||
rules={[{ required: true, message: "Please select a provider" }]}
|
||||
label={t("guardrails.addGuardrailForm.providerLabel")}
|
||||
rules={[{ required: true, message: t("guardrails.addGuardrailForm.providerRequired") }]}
|
||||
>
|
||||
<Select
|
||||
placeholder="Select a guardrail provider"
|
||||
placeholder={t("guardrails.addGuardrailForm.providerPlaceholder")}
|
||||
onChange={handleProviderChange}
|
||||
labelInValue={false}
|
||||
optionLabelProp="label"
|
||||
|
|
@ -733,9 +736,9 @@ const AddGuardrailForm: React.FC<AddGuardrailFormProps> = ({ visible, onClose, a
|
|||
|
||||
<Form.Item
|
||||
name="mode"
|
||||
label="Mode"
|
||||
tooltip="How the guardrail should be applied"
|
||||
rules={[{ required: true, message: "Please select a mode" }]}
|
||||
label={t("guardrails.addGuardrailForm.modeLabel")}
|
||||
tooltip={t("guardrails.addGuardrailForm.modeTooltip")}
|
||||
rules={[{ required: true, message: t("guardrails.addGuardrailForm.modeRequired") }]}
|
||||
>
|
||||
<Select optionLabelProp="label" mode="multiple">
|
||||
{guardrailSettings?.supported_modes?.map((mode) => (
|
||||
|
|
@ -745,7 +748,7 @@ const AddGuardrailForm: React.FC<AddGuardrailFormProps> = ({ visible, onClose, a
|
|||
<strong>{mode}</strong>
|
||||
{mode === "pre_call" && (
|
||||
<Tag color="green" style={{ marginLeft: "8px" }}>
|
||||
Recommended
|
||||
{t("guardrails.addGuardrailForm.recommended")}
|
||||
</Tag>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -759,7 +762,7 @@ const AddGuardrailForm: React.FC<AddGuardrailFormProps> = ({ visible, onClose, a
|
|||
<Option value="pre_call" label="pre_call">
|
||||
<div>
|
||||
<div>
|
||||
<strong>pre_call</strong> <Tag color="green">Recommended</Tag>
|
||||
<strong>pre_call</strong> <Tag color="green">{t("guardrails.addGuardrailForm.recommended")}</Tag>
|
||||
</div>
|
||||
<div style={{ fontSize: "12px", color: "#888" }}>{modeDescriptions.pre_call}</div>
|
||||
</div>
|
||||
|
|
@ -795,36 +798,36 @@ const AddGuardrailForm: React.FC<AddGuardrailFormProps> = ({ visible, onClose, a
|
|||
|
||||
<Form.Item
|
||||
name="default_on"
|
||||
label="Always On"
|
||||
tooltip="If enabled, this guardrail will be applied to all requests by default."
|
||||
label={t("guardrails.addGuardrailForm.alwaysOnLabel")}
|
||||
tooltip={t("guardrails.addGuardrailForm.alwaysOnTooltip")}
|
||||
>
|
||||
<Select>
|
||||
<Select.Option value={true}>Yes</Select.Option>
|
||||
<Select.Option value={false}>No</Select.Option>
|
||||
<Select.Option value={true}>{t("common.yes")}</Select.Option>
|
||||
<Select.Option value={false}>{t("common.no")}</Select.Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="skip_system_message_choice"
|
||||
label="Skip system messages in guardrail"
|
||||
tooltip="Unified guardrails only: omit role: system from guardrail evaluation input (OpenAI chat + Anthropic messages). The model still receives full messages. Use global default follows litellm_settings.skip_system_message_in_guardrail."
|
||||
label={t("guardrails.addGuardrailForm.skipSystemMsgLabel")}
|
||||
tooltip={t("guardrails.addGuardrailForm.skipSystemMsgTooltip")}
|
||||
>
|
||||
<Select>
|
||||
<Select.Option value="inherit">Use global default</Select.Option>
|
||||
<Select.Option value="yes">Yes — exclude from guardrail scan</Select.Option>
|
||||
<Select.Option value="no">No — always include in scan</Select.Option>
|
||||
<Select.Option value="inherit">{t("guardrails.addGuardrailForm.useGlobalDefault")}</Select.Option>
|
||||
<Select.Option value="yes">{t("guardrails.addGuardrailForm.yesExcludeFromScan")}</Select.Option>
|
||||
<Select.Option value="no">{t("guardrails.addGuardrailForm.noAlwaysInclude")}</Select.Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="skip_tool_message_choice"
|
||||
label="Skip tool messages in guardrail"
|
||||
tooltip="Unified guardrails only: omit role: tool from guardrail evaluation input (OpenAI chat + Anthropic messages). The model still receives full messages. Use global default follows litellm_settings.skip_tool_message_in_guardrail."
|
||||
label={t("guardrails.addGuardrailForm.skipToolMsgLabel")}
|
||||
tooltip={t("guardrails.addGuardrailForm.skipToolMsgTooltip")}
|
||||
>
|
||||
<Select>
|
||||
<Select.Option value="inherit">Use global default</Select.Option>
|
||||
<Select.Option value="yes">Yes — exclude from guardrail scan</Select.Option>
|
||||
<Select.Option value="no">No — always include in scan</Select.Option>
|
||||
<Select.Option value="inherit">{t("guardrails.addGuardrailForm.useGlobalDefault")}</Select.Option>
|
||||
<Select.Option value="yes">{t("guardrails.addGuardrailForm.yesExcludeFromScan")}</Select.Option>
|
||||
<Select.Option value="no">{t("guardrails.addGuardrailForm.noAlwaysInclude")}</Select.Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
|
||||
|
|
@ -969,33 +972,35 @@ const AddGuardrailForm: React.FC<AddGuardrailFormProps> = ({ visible, onClose, a
|
|||
|
||||
return (
|
||||
<div className="flex justify-end space-x-2 mt-4">
|
||||
{currentStep > 0 && <Button onClick={prevStep}>Previous</Button>}
|
||||
{currentStep > 0 && <Button onClick={prevStep}>{t("common.previous")}</Button>}
|
||||
{isCategoriesStep ? (
|
||||
<>
|
||||
<Button onClick={nextStep}>Skip</Button>
|
||||
<Button onClick={nextStep}>{t("guardrails.addGuardrailForm.skip")}</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => handleAddAndContinue(hasCompetitorIntentConfigured)}
|
||||
disabled={!canContinueFromCategoriesStep}
|
||||
>
|
||||
{hasPendingCategory ? "Add & Continue →" : "Continue →"}
|
||||
{hasPendingCategory
|
||||
? t("guardrails.addGuardrailForm.addAndContinue")
|
||||
: t("guardrails.addGuardrailForm.continueArrow")}
|
||||
</Button>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{!isLastStep && (
|
||||
<Button type="primary" onClick={nextStep}>
|
||||
Next
|
||||
{t("common.next")}
|
||||
</Button>
|
||||
)}
|
||||
{isLastStep && (
|
||||
<Button type="primary" onClick={handleSubmit} loading={loading}>
|
||||
Create Guardrail
|
||||
{t("guardrails.addGuardrailForm.createGuardrailButton")}
|
||||
</Button>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
<Button onClick={handleClose}>Cancel</Button>
|
||||
<Button onClick={handleClose}>{t("common.cancel")}</Button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
@ -1005,15 +1010,16 @@ const AddGuardrailForm: React.FC<AddGuardrailFormProps> = ({ visible, onClose, a
|
|||
<div className="space-y-6">
|
||||
<div>
|
||||
<p className="text-sm text-gray-500">
|
||||
Configure settings for a specific call type. Most guardrails don't need this — skip it unless you're using a
|
||||
specific endpoint like <code>/v1/realtime</code>.
|
||||
<Trans i18nKey="guardrails.addGuardrailForm.endpointSettingsDesc" components={{ code: <code /> }} />
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">Call type</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||
{t("guardrails.addGuardrailForm.callTypeLabel")}
|
||||
</label>
|
||||
<Select
|
||||
placeholder="Select a call type"
|
||||
placeholder={t("guardrails.addGuardrailForm.callTypePlaceholder")}
|
||||
value={selectedEndpointType || undefined}
|
||||
onChange={(v) => {
|
||||
setSelectedEndpointType(v);
|
||||
|
|
@ -1023,7 +1029,7 @@ const AddGuardrailForm: React.FC<AddGuardrailFormProps> = ({ visible, onClose, a
|
|||
allowClear
|
||||
options={[{ value: "realtime", label: "/v1/realtime" }]}
|
||||
/>
|
||||
<p className="text-xs text-gray-400 mt-1">More call types coming soon.</p>
|
||||
<p className="text-xs text-gray-400 mt-1">{t("guardrails.addGuardrailForm.moreCallTypesSoon")}</p>
|
||||
</div>
|
||||
|
||||
{selectedEndpointType === "realtime" && (
|
||||
|
|
@ -1033,7 +1039,7 @@ const AddGuardrailForm: React.FC<AddGuardrailFormProps> = ({ visible, onClose, a
|
|||
onClick={() => setEndpointSettingsOpen((o) => !o)}
|
||||
className="w-full flex items-center justify-between px-4 py-3 bg-gray-50 hover:bg-gray-100 text-sm font-medium text-gray-700"
|
||||
>
|
||||
<span>/v1/realtime settings</span>
|
||||
<span>{t("guardrails.addGuardrailForm.realtimeSettingsTitle")}</span>
|
||||
<svg
|
||||
className={`w-4 h-4 text-gray-500 transition-transform ${endpointSettingsOpen ? "rotate-180" : ""}`}
|
||||
fill="none"
|
||||
|
|
@ -1048,11 +1054,10 @@ const AddGuardrailForm: React.FC<AddGuardrailFormProps> = ({ visible, onClose, a
|
|||
{endpointSettingsOpen && (
|
||||
<div className="space-y-5 px-4 py-4 border-t border-gray-200">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">End session after X violations</label>
|
||||
<p className="text-xs text-gray-400 mb-2">
|
||||
Automatically close the session after this many guardrail violations. Leave empty to never
|
||||
auto-close.
|
||||
</p>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||
{t("guardrails.addGuardrailForm.endSessionLabel")}
|
||||
</label>
|
||||
<p className="text-xs text-gray-400 mb-2">{t("guardrails.addGuardrailForm.endSessionDesc")}</p>
|
||||
<input
|
||||
type="number"
|
||||
min={1}
|
||||
|
|
@ -1066,7 +1071,9 @@ const AddGuardrailForm: React.FC<AddGuardrailFormProps> = ({ visible, onClose, a
|
|||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">On violation</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
{t("guardrails.addGuardrailForm.onViolationLabel")}
|
||||
</label>
|
||||
<div className="space-y-2">
|
||||
{(["warn", "end_session"] as const).map((opt) => (
|
||||
<label key={opt} className="flex items-start gap-2 cursor-pointer">
|
||||
|
|
@ -1080,12 +1087,14 @@ const AddGuardrailForm: React.FC<AddGuardrailFormProps> = ({ visible, onClose, a
|
|||
/>
|
||||
<div>
|
||||
<span className="text-sm font-medium text-gray-800">
|
||||
{opt === "warn" ? "Warn" : "End session"}
|
||||
{opt === "warn"
|
||||
? t("guardrails.addGuardrailForm.violationWarn")
|
||||
: t("guardrails.addGuardrailForm.violationEndSession")}
|
||||
</span>
|
||||
<p className="text-xs text-gray-400 m-0">
|
||||
{opt === "warn"
|
||||
? "Bot speaks the message, session continues"
|
||||
: "Bot speaks the message, connection closes immediately"}
|
||||
? t("guardrails.addGuardrailForm.violationWarnDesc")
|
||||
: t("guardrails.addGuardrailForm.violationEndSessionDesc")}
|
||||
</p>
|
||||
</div>
|
||||
</label>
|
||||
|
|
@ -1094,14 +1103,13 @@ const AddGuardrailForm: React.FC<AddGuardrailFormProps> = ({ visible, onClose, a
|
|||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">Message the user hears</label>
|
||||
<p className="text-xs text-gray-400 mb-2">
|
||||
What the bot says aloud when this guardrail fires. Falls back to the default violation message if
|
||||
empty.
|
||||
</p>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||
{t("guardrails.addGuardrailForm.violationMessageLabel")}
|
||||
</label>
|
||||
<p className="text-xs text-gray-400 mb-2">{t("guardrails.addGuardrailForm.violationMessageDesc")}</p>
|
||||
<textarea
|
||||
rows={3}
|
||||
placeholder="e.g. I'm not able to continue this conversation. Please contact us at 1-800-774-2678."
|
||||
placeholder={t("guardrails.addGuardrailForm.violationMessagePlaceholder")}
|
||||
value={realtimeViolationMessage}
|
||||
onChange={(e) => setRealtimeViolationMessage(e.target.value)}
|
||||
className="border border-gray-300 rounded px-3 py-2 text-sm w-full resize-none"
|
||||
|
|
@ -1118,22 +1126,22 @@ const AddGuardrailForm: React.FC<AddGuardrailFormProps> = ({ visible, onClose, a
|
|||
const getStepConfigs = () => {
|
||||
if (shouldRenderContentFilterConfigSettings(selectedProvider)) {
|
||||
return [
|
||||
{ title: "Basic Info", optional: false },
|
||||
{ title: "Topics", optional: false },
|
||||
{ title: "Patterns", optional: false },
|
||||
{ title: "Keywords", optional: false },
|
||||
{ title: "Endpoint Settings (Optional)", optional: true },
|
||||
{ title: t("guardrails.addGuardrailForm.stepBasicInfo"), optional: false },
|
||||
{ title: t("guardrails.addGuardrailForm.stepTopics"), optional: false },
|
||||
{ title: t("guardrails.addGuardrailForm.stepPatterns"), optional: false },
|
||||
{ title: t("guardrails.addGuardrailForm.stepKeywords"), optional: false },
|
||||
{ title: t("guardrails.addGuardrailForm.stepEndpointSettings"), optional: true },
|
||||
];
|
||||
}
|
||||
if (shouldRenderPIIConfigSettings(selectedProvider)) {
|
||||
return [
|
||||
{ title: "Basic Info", optional: false },
|
||||
{ title: "PII Configuration", optional: false },
|
||||
{ title: t("guardrails.addGuardrailForm.stepBasicInfo"), optional: false },
|
||||
{ title: t("guardrails.addGuardrailForm.stepPiiConfig"), optional: false },
|
||||
];
|
||||
}
|
||||
return [
|
||||
{ title: "Basic Info", optional: false },
|
||||
{ title: "Provider Configuration", optional: false },
|
||||
{ title: t("guardrails.addGuardrailForm.stepBasicInfo"), optional: false },
|
||||
{ title: t("guardrails.addGuardrailForm.stepProviderConfig"), optional: false },
|
||||
];
|
||||
};
|
||||
|
||||
|
|
@ -1156,7 +1164,7 @@ const AddGuardrailForm: React.FC<AddGuardrailFormProps> = ({ visible, onClose, a
|
|||
<div className="flex flex-col">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between px-6 py-4 border-b border-gray-200">
|
||||
<h3 className="text-base font-semibold text-gray-900 m-0">Create guardrail</h3>
|
||||
<h3 className="text-base font-semibold text-gray-900 m-0">{t("guardrails.addGuardrailForm.modalTitle")}</h3>
|
||||
<button
|
||||
onClick={handleClose}
|
||||
className="text-gray-400 hover:text-gray-600 bg-transparent border-none cursor-pointer text-base leading-none p-1"
|
||||
|
|
@ -1226,8 +1234,12 @@ const AddGuardrailForm: React.FC<AddGuardrailFormProps> = ({ visible, onClose, a
|
|||
>
|
||||
{step.title}
|
||||
</span>
|
||||
{step.optional && !isCurrent && <span className="text-[11px] text-slate-400">optional</span>}
|
||||
{isDone && <span className="text-[11px] text-indigo-500 hover:underline">Edit</span>}
|
||||
{step.optional && !isCurrent && (
|
||||
<span className="text-[11px] text-slate-400">{t("common.optional")}</span>
|
||||
)}
|
||||
{isDone && (
|
||||
<span className="text-[11px] text-indigo-500 hover:underline">{t("common.edit")}</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Expanded form content for current step */}
|
||||
|
|
@ -1241,15 +1253,15 @@ const AddGuardrailForm: React.FC<AddGuardrailFormProps> = ({ visible, onClose, a
|
|||
|
||||
{/* Bottom bar */}
|
||||
<div className="flex items-center justify-end space-x-3 px-6 py-3 border-t border-gray-200">
|
||||
<Button onClick={handleClose}>Cancel</Button>
|
||||
{currentStep > 0 && <Button onClick={prevStep}>Previous</Button>}
|
||||
<Button onClick={handleClose}>{t("common.cancel")}</Button>
|
||||
{currentStep > 0 && <Button onClick={prevStep}>{t("common.previous")}</Button>}
|
||||
{currentStep < stepConfigs.length - 1 ? (
|
||||
<Button type="primary" onClick={nextStep}>
|
||||
Next
|
||||
{t("common.next")}
|
||||
</Button>
|
||||
) : (
|
||||
<Button type="primary" onClick={handleSubmit} loading={loading}>
|
||||
Create Guardrail
|
||||
{t("guardrails.addGuardrailForm.createGuardrailButton")}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1300,6 +1300,71 @@
|
|||
"formInvalidJson": "Invalid JSON",
|
||||
"formGuardrailInfo": "Guardrail Info (optional)",
|
||||
"formGuardrailInfoPlaceholder": "{\"description\": \"Detects PII in requests\"}"
|
||||
},
|
||||
"addGuardrailForm": {
|
||||
"modalTitle": "Create guardrail",
|
||||
"guardrailNameLabel": "Guardrail Name",
|
||||
"guardrailNameRequired": "Please enter a guardrail name",
|
||||
"guardrailNamePlaceholder": "Enter a name for this guardrail",
|
||||
"providerLabel": "Guardrail Provider",
|
||||
"providerRequired": "Please select a provider",
|
||||
"providerPlaceholder": "Select a guardrail provider",
|
||||
"modeLabel": "Mode",
|
||||
"modeTooltip": "How the guardrail should be applied",
|
||||
"modeRequired": "Please select a mode",
|
||||
"recommended": "Recommended",
|
||||
"alwaysOnLabel": "Always On",
|
||||
"alwaysOnTooltip": "If enabled, this guardrail will be applied to all requests by default.",
|
||||
"skipSystemMsgLabel": "Skip system messages in guardrail",
|
||||
"skipSystemMsgTooltip": "Unified guardrails only: omit role: system from guardrail evaluation input (OpenAI chat + Anthropic messages). The model still receives full messages. Use global default follows litellm_settings.skip_system_message_in_guardrail.",
|
||||
"skipToolMsgLabel": "Skip tool messages in guardrail",
|
||||
"skipToolMsgTooltip": "Unified guardrails only: omit role: tool from guardrail evaluation input (OpenAI chat + Anthropic messages). The model still receives full messages. Use global default follows litellm_settings.skip_tool_message_in_guardrail.",
|
||||
"useGlobalDefault": "Use global default",
|
||||
"yesExcludeFromScan": "Yes — exclude from guardrail scan",
|
||||
"noAlwaysInclude": "No — always include in scan",
|
||||
"modeDesc": {
|
||||
"pre_call": "Before LLM Call - Runs before the LLM call and checks the input (Recommended)",
|
||||
"during_call": "During LLM Call - Runs in parallel with the LLM call, with response held until check completes",
|
||||
"post_call": "After LLM Call - Runs after the LLM call and checks only the output",
|
||||
"logging_only": "Logging Only - Only runs on logging callbacks without affecting the LLM call",
|
||||
"pre_mcp_call": "Before MCP Tool Call - Runs before MCP tool execution and validates tool calls",
|
||||
"during_mcp_call": "During MCP Tool Call - Runs in parallel with MCP tool execution for monitoring"
|
||||
},
|
||||
"stepBasicInfo": "Basic Info",
|
||||
"stepTopics": "Topics",
|
||||
"stepPatterns": "Patterns",
|
||||
"stepKeywords": "Keywords",
|
||||
"stepEndpointSettings": "Endpoint Settings (Optional)",
|
||||
"stepPiiConfig": "PII Configuration",
|
||||
"stepProviderConfig": "Provider Configuration",
|
||||
"skip": "Skip",
|
||||
"addAndContinue": "Add & Continue →",
|
||||
"continueArrow": "Continue →",
|
||||
"createGuardrailButton": "Create Guardrail",
|
||||
"endpointSettingsDesc": "Configure settings for a specific call type. Most guardrails don't need this — skip it unless you're using a specific endpoint like <code>/v1/realtime</code>.",
|
||||
"callTypeLabel": "Call type",
|
||||
"callTypePlaceholder": "Select a call type",
|
||||
"moreCallTypesSoon": "More call types coming soon.",
|
||||
"realtimeSettingsTitle": "/v1/realtime settings",
|
||||
"endSessionLabel": "End session after X violations",
|
||||
"endSessionDesc": "Automatically close the session after this many guardrail violations. Leave empty to never auto-close.",
|
||||
"onViolationLabel": "On violation",
|
||||
"violationWarn": "Warn",
|
||||
"violationEndSession": "End session",
|
||||
"violationWarnDesc": "Bot speaks the message, session continues",
|
||||
"violationEndSessionDesc": "Bot speaks the message, connection closes immediately",
|
||||
"violationMessageLabel": "Message the user hears",
|
||||
"violationMessageDesc": "What the bot says aloud when this guardrail fires. Falls back to the default violation message if empty.",
|
||||
"violationMessagePlaceholder": "e.g. I'm not able to continue this conversation. Please contact us at 1-800-774-2678.",
|
||||
"failedToLoadConfig": "Failed to load guardrail configuration",
|
||||
"selectAtLeastOnePii": "Please select at least one PII entity to continue",
|
||||
"configureAtLeastOneFilter": "Please configure at least one content filter setting (category, pattern, keyword, or competitor intent)",
|
||||
"invalidJsonConfig": "Invalid JSON in configuration",
|
||||
"addAtLeastOneCriterion": "Add at least one evaluation criterion",
|
||||
"weightsMustSum100": "Criterion weights must sum to 100% (currently {{weightTotal}}%)",
|
||||
"addAtLeastOneRule": "Add at least one tool permission rule",
|
||||
"createSuccess": "Guardrail created successfully",
|
||||
"createFailed": "Failed to create guardrail: {{error}}"
|
||||
}
|
||||
},
|
||||
"keyValueInput": {
|
||||
|
|
|
|||
|
|
@ -1300,7 +1300,72 @@
|
|||
"labelMode": "模式",
|
||||
"labelDefaultOn": "默认启用",
|
||||
"deleteSuccess": "护栏「{{name}}」已成功删除",
|
||||
"deleteFailed": "删除护栏失败"
|
||||
"deleteFailed": "删除护栏失败",
|
||||
"addGuardrailForm": {
|
||||
"modalTitle": "创建护栏",
|
||||
"guardrailNameLabel": "护栏名称",
|
||||
"guardrailNameRequired": "请输入护栏名称",
|
||||
"guardrailNamePlaceholder": "为此护栏输入名称",
|
||||
"providerLabel": "护栏提供商",
|
||||
"providerRequired": "请选择提供商",
|
||||
"providerPlaceholder": "选择护栏提供商",
|
||||
"modeLabel": "模式",
|
||||
"modeTooltip": "护栏的应用方式",
|
||||
"modeRequired": "请选择模式",
|
||||
"recommended": "推荐",
|
||||
"alwaysOnLabel": "始终启用",
|
||||
"alwaysOnTooltip": "启用后,此护栏将默认应用于所有请求。",
|
||||
"skipSystemMsgLabel": "在护栏中跳过系统消息",
|
||||
"skipSystemMsgTooltip": "仅限统一护栏:从护栏评估输入中省略 role: system(OpenAI chat + Anthropic messages)。模型仍接收完整消息。使用全局默认则遵循 litellm_settings.skip_system_message_in_guardrail。",
|
||||
"skipToolMsgLabel": "在护栏中跳过工具消息",
|
||||
"skipToolMsgTooltip": "仅限统一护栏:从护栏评估输入中省略 role: tool(OpenAI chat + Anthropic messages)。模型仍接收完整消息。使用全局默认则遵循 litellm_settings.skip_tool_message_in_guardrail。",
|
||||
"useGlobalDefault": "使用全局默认",
|
||||
"yesExcludeFromScan": "是 — 从护栏扫描中排除",
|
||||
"noAlwaysInclude": "否 — 始终包含在扫描中",
|
||||
"modeDesc": {
|
||||
"pre_call": "LLM 调用前 - 在 LLM 调用前运行并检查输入(推荐)",
|
||||
"during_call": "LLM 调用中 - 与 LLM 调用并行运行,响应等待检查完成后才返回",
|
||||
"post_call": "LLM 调用后 - 在 LLM 调用后运行,仅检查输出",
|
||||
"logging_only": "仅日志 - 仅在日志回调中运行,不影响 LLM 调用",
|
||||
"pre_mcp_call": "MCP 工具调用前 - 在 MCP 工具执行前运行并验证工具调用",
|
||||
"during_mcp_call": "MCP 工具调用中 - 与 MCP 工具执行并行运行用于监控"
|
||||
},
|
||||
"stepBasicInfo": "基本信息",
|
||||
"stepTopics": "主题",
|
||||
"stepPatterns": "模式",
|
||||
"stepKeywords": "关键词",
|
||||
"stepEndpointSettings": "端点设置(可选)",
|
||||
"stepPiiConfig": "PII 配置",
|
||||
"stepProviderConfig": "提供商配置",
|
||||
"skip": "跳过",
|
||||
"addAndContinue": "添加并继续 →",
|
||||
"continueArrow": "继续 →",
|
||||
"createGuardrailButton": "创建护栏",
|
||||
"endpointSettingsDesc": "为特定调用类型配置设置。大多数护栏不需要此项 — 除非您使用特定端点(如 <code>/v1/realtime</code>),否则请跳过。",
|
||||
"callTypeLabel": "调用类型",
|
||||
"callTypePlaceholder": "选择调用类型",
|
||||
"moreCallTypesSoon": "更多调用类型即将推出。",
|
||||
"realtimeSettingsTitle": "/v1/realtime 设置",
|
||||
"endSessionLabel": "在 X 次违规后结束会话",
|
||||
"endSessionDesc": "在达到指定护栏违规次数后自动关闭会话。留空则永不自动关闭。",
|
||||
"onViolationLabel": "违规时",
|
||||
"violationWarn": "警告",
|
||||
"violationEndSession": "结束会话",
|
||||
"violationWarnDesc": "机器人播报消息,会话继续",
|
||||
"violationEndSessionDesc": "机器人播报消息,连接立即关闭",
|
||||
"violationMessageLabel": "用户听到的消息",
|
||||
"violationMessageDesc": "护栏触发时机器人播报的内容。若留空则回退到默认违规消息。",
|
||||
"violationMessagePlaceholder": "例如:我无法继续此对话。请拨打 1-800-774-2678 联系我们。",
|
||||
"failedToLoadConfig": "加载护栏配置失败",
|
||||
"selectAtLeastOnePii": "请至少选择一个 PII 实体后继续",
|
||||
"configureAtLeastOneFilter": "请至少配置一项内容过滤设置(类别、模式、关键词或竞争对手意图)",
|
||||
"invalidJsonConfig": "配置中的 JSON 格式无效",
|
||||
"addAtLeastOneCriterion": "请至少添加一条评估标准",
|
||||
"weightsMustSum100": "标准权重之和必须为 100%(当前为 {{weightTotal}}%)",
|
||||
"addAtLeastOneRule": "请至少添加一条工具权限规则",
|
||||
"createSuccess": "护栏创建成功",
|
||||
"createFailed": "创建护栏失败:{{error}}"
|
||||
}
|
||||
},
|
||||
"keyValueInput": {
|
||||
"headerNamePlaceholder": "请求头名称",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue