mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-04 02:31:27 +00:00
feat(ui): i18n for guardrails modals and edit form
This commit is contained in:
parent
535e00626d
commit
327f3ad020
8 changed files with 546 additions and 160 deletions
|
|
@ -1,5 +1,6 @@
|
|||
import React from "react";
|
||||
import { Typography, Select, Modal, Space, Button, Input } from "antd";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const { Text } = Typography;
|
||||
const { Option } = Select;
|
||||
|
|
@ -27,13 +28,15 @@ const KeywordModal: React.FC<KeywordModalProps> = ({
|
|||
onAdd,
|
||||
onCancel,
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Modal title="Add blocked keyword" open={visible} onCancel={onCancel} footer={null} width={800}>
|
||||
<Modal title={t("guardrails.keywordModal.title")} open={visible} onCancel={onCancel} footer={null} width={800}>
|
||||
<Space direction="vertical" style={{ width: "100%" }} size="large">
|
||||
<div>
|
||||
<Text strong>Keyword</Text>
|
||||
<Text strong>{t("guardrails.keywordModal.keywordLabel")}</Text>
|
||||
<Input
|
||||
placeholder="Enter sensitive keyword or phrase"
|
||||
placeholder={t("guardrails.keywordModal.keywordPlaceholder")}
|
||||
value={keyword}
|
||||
onChange={(e) => onKeywordChange(e.target.value)}
|
||||
style={{ marginTop: 8 }}
|
||||
|
|
@ -41,20 +44,20 @@ const KeywordModal: React.FC<KeywordModalProps> = ({
|
|||
</div>
|
||||
|
||||
<div>
|
||||
<Text strong>Action</Text>
|
||||
<Text strong>{t("guardrails.keywordModal.actionLabel")}</Text>
|
||||
<Text type="secondary" style={{ display: "block", marginTop: 4, marginBottom: 8 }}>
|
||||
Choose what action the guardrail should take when this keyword is detected
|
||||
{t("guardrails.keywordModal.actionDesc")}
|
||||
</Text>
|
||||
<Select value={action} onChange={onActionChange} style={{ width: "100%" }}>
|
||||
<Option value="BLOCK">Block</Option>
|
||||
<Option value="MASK">Mask</Option>
|
||||
<Option value="BLOCK">{t("guardrails.keywordModal.actionBlock")}</Option>
|
||||
<Option value="MASK">{t("guardrails.keywordModal.actionMask")}</Option>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Text strong>Description (optional)</Text>
|
||||
<Text strong>{t("guardrails.keywordModal.descriptionLabel")}</Text>
|
||||
<Input.TextArea
|
||||
placeholder="Explain why this keyword is sensitive"
|
||||
placeholder={t("guardrails.keywordModal.descriptionPlaceholder")}
|
||||
value={description}
|
||||
onChange={(e) => onDescriptionChange(e.target.value)}
|
||||
rows={3}
|
||||
|
|
@ -64,9 +67,9 @@ const KeywordModal: React.FC<KeywordModalProps> = ({
|
|||
</Space>
|
||||
|
||||
<div style={{ display: "flex", justifyContent: "flex-end", gap: "8px", marginTop: "24px" }}>
|
||||
<Button onClick={onCancel}>Cancel</Button>
|
||||
<Button onClick={onCancel}>{t("common.cancel")}</Button>
|
||||
<Button type="primary" onClick={onAdd}>
|
||||
Add
|
||||
{t("common.add")}
|
||||
</Button>
|
||||
</div>
|
||||
</Modal>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { DeleteOutlined } from "@ant-design/icons";
|
||||
import { Button, Select, Table, Typography } from "antd";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const { Text } = Typography;
|
||||
const { Option } = Select;
|
||||
|
|
@ -19,14 +20,16 @@ interface KeywordTableProps {
|
|||
}
|
||||
|
||||
const KeywordTable: React.FC<KeywordTableProps> = ({ keywords, onActionChange, onRemove }) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: "Keyword",
|
||||
title: t("guardrails.keywordTable.colKeyword"),
|
||||
dataIndex: "keyword",
|
||||
key: "keyword",
|
||||
},
|
||||
{
|
||||
title: "Action",
|
||||
title: t("guardrails.keywordTable.colAction"),
|
||||
dataIndex: "action",
|
||||
key: "action",
|
||||
width: 150,
|
||||
|
|
@ -37,13 +40,13 @@ const KeywordTable: React.FC<KeywordTableProps> = ({ keywords, onActionChange, o
|
|||
style={{ width: 120 }}
|
||||
size="small"
|
||||
>
|
||||
<Option value="BLOCK">Block</Option>
|
||||
<Option value="MASK">Mask</Option>
|
||||
<Option value="BLOCK">{t("guardrails.keywordTable.actionBlock")}</Option>
|
||||
<Option value="MASK">{t("guardrails.keywordTable.actionMask")}</Option>
|
||||
</Select>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "Description",
|
||||
title: t("guardrails.keywordTable.colDescription"),
|
||||
dataIndex: "description",
|
||||
key: "description",
|
||||
render: (desc: string) => desc || "-",
|
||||
|
|
@ -54,14 +57,18 @@ const KeywordTable: React.FC<KeywordTableProps> = ({ keywords, onActionChange, o
|
|||
width: 100,
|
||||
render: (_: any, record: BlockedWord) => (
|
||||
<Button type="text" danger size="small" icon={<DeleteOutlined />} onClick={() => onRemove(record.id)}>
|
||||
Delete
|
||||
{t("common.delete")}
|
||||
</Button>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
if (keywords.length === 0) {
|
||||
return <div style={{ textAlign: "center", padding: "40px 0", color: "#999" }}>No keywords added.</div>;
|
||||
return (
|
||||
<div style={{ textAlign: "center", padding: "40px 0", color: "#999" }}>
|
||||
{t("guardrails.keywordTable.noKeywords")}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return <Table dataSource={keywords} columns={columns} rowKey="id" pagination={false} size="small" />;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import React from "react";
|
||||
import { Typography, Select, Modal, Space, Button } from "antd";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const { Text } = Typography;
|
||||
const { Option } = Select;
|
||||
|
|
@ -34,13 +35,15 @@ const PatternModal: React.FC<PatternModalProps> = ({
|
|||
onAdd,
|
||||
onCancel,
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Modal title="Add prebuilt pattern" open={visible} onCancel={onCancel} footer={null} width={800}>
|
||||
<Modal title={t("guardrails.patternModal.title")} open={visible} onCancel={onCancel} footer={null} width={800}>
|
||||
<Space direction="vertical" style={{ width: "100%" }} size="large">
|
||||
<div>
|
||||
<Text strong>Pattern type</Text>
|
||||
<Text strong>{t("guardrails.patternModal.patternTypeLabel")}</Text>
|
||||
<Select
|
||||
placeholder="Choose pattern type"
|
||||
placeholder={t("guardrails.patternModal.patternTypePlaceholder")}
|
||||
value={selectedPatternName}
|
||||
onChange={onPatternNameChange}
|
||||
style={{ width: "100%", marginTop: 8 }}
|
||||
|
|
@ -74,21 +77,21 @@ const PatternModal: React.FC<PatternModalProps> = ({
|
|||
</div>
|
||||
|
||||
<div>
|
||||
<Text strong>Action</Text>
|
||||
<Text strong>{t("guardrails.patternModal.actionLabel")}</Text>
|
||||
<Text type="secondary" style={{ display: "block", marginTop: 4, marginBottom: 8 }}>
|
||||
Choose what action the guardrail should take when this pattern is detected
|
||||
{t("guardrails.patternModal.actionDesc")}
|
||||
</Text>
|
||||
<Select value={patternAction} onChange={onActionChange} style={{ width: "100%" }}>
|
||||
<Option value="BLOCK">Block</Option>
|
||||
<Option value="MASK">Mask</Option>
|
||||
<Option value="BLOCK">{t("guardrails.patternModal.actionBlock")}</Option>
|
||||
<Option value="MASK">{t("guardrails.patternModal.actionMask")}</Option>
|
||||
</Select>
|
||||
</div>
|
||||
</Space>
|
||||
|
||||
<div style={{ display: "flex", justifyContent: "flex-end", gap: "8px", marginTop: "24px" }}>
|
||||
<Button onClick={onCancel}>Cancel</Button>
|
||||
<Button onClick={onCancel}>{t("common.cancel")}</Button>
|
||||
<Button type="primary" onClick={onAdd}>
|
||||
Add
|
||||
{t("common.add")}
|
||||
</Button>
|
||||
</div>
|
||||
</Modal>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import React from "react";
|
||||
import { Typography, Select, Table, Tag, Button } from "antd";
|
||||
import { DeleteOutlined } from "@ant-design/icons";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const { Text } = Typography;
|
||||
const { Option } = Select;
|
||||
|
|
@ -21,24 +22,28 @@ interface PatternTableProps {
|
|||
}
|
||||
|
||||
const PatternTable: React.FC<PatternTableProps> = ({ patterns, onActionChange, onRemove }) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: "Type",
|
||||
title: t("guardrails.patternTable.colType"),
|
||||
dataIndex: "type",
|
||||
key: "type",
|
||||
width: 100,
|
||||
render: (type: string) => (
|
||||
<Tag color={type === "prebuilt" ? "blue" : "green"}>{type === "prebuilt" ? "Prebuilt" : "Custom"}</Tag>
|
||||
<Tag color={type === "prebuilt" ? "blue" : "green"}>
|
||||
{type === "prebuilt" ? t("guardrails.patternTable.typePrebuilt") : t("guardrails.patternTable.typeCustom")}
|
||||
</Tag>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "Pattern name",
|
||||
title: t("guardrails.patternTable.colPatternName"),
|
||||
dataIndex: "name",
|
||||
key: "name",
|
||||
render: (_: string, record: Pattern) => record.display_name || record.name,
|
||||
},
|
||||
{
|
||||
title: "Regex pattern",
|
||||
title: t("guardrails.patternTable.colRegexPattern"),
|
||||
dataIndex: "pattern",
|
||||
key: "pattern",
|
||||
render: (pattern: string) =>
|
||||
|
|
@ -51,7 +56,7 @@ const PatternTable: React.FC<PatternTableProps> = ({ patterns, onActionChange, o
|
|||
),
|
||||
},
|
||||
{
|
||||
title: "Action",
|
||||
title: t("guardrails.patternTable.colAction"),
|
||||
dataIndex: "action",
|
||||
key: "action",
|
||||
width: 150,
|
||||
|
|
@ -62,8 +67,8 @@ const PatternTable: React.FC<PatternTableProps> = ({ patterns, onActionChange, o
|
|||
style={{ width: 120 }}
|
||||
size="small"
|
||||
>
|
||||
<Option value="BLOCK">Block</Option>
|
||||
<Option value="MASK">Mask</Option>
|
||||
<Option value="BLOCK">{t("guardrails.patternTable.actionBlock")}</Option>
|
||||
<Option value="MASK">{t("guardrails.patternTable.actionMask")}</Option>
|
||||
</Select>
|
||||
),
|
||||
},
|
||||
|
|
@ -73,14 +78,18 @@ const PatternTable: React.FC<PatternTableProps> = ({ patterns, onActionChange, o
|
|||
width: 100,
|
||||
render: (_: any, record: Pattern) => (
|
||||
<Button type="text" danger size="small" icon={<DeleteOutlined />} onClick={() => onRemove(record.id)}>
|
||||
Delete
|
||||
{t("common.delete")}
|
||||
</Button>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
if (patterns.length === 0) {
|
||||
return <div style={{ textAlign: "center", padding: "40px 0", color: "#999" }}>No patterns added.</div>;
|
||||
return (
|
||||
<div style={{ textAlign: "center", padding: "40px 0", color: "#999" }}>
|
||||
{t("guardrails.patternTable.noPatterns")}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return <Table dataSource={patterns} columns={columns} rowKey="id" pagination={false} size="small" />;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useState, useRef, useEffect } from "react";
|
||||
import React, { useState, useRef, useEffect, useMemo } from "react";
|
||||
import { Modal, Select, Switch, Collapse, Input, Divider } from "antd";
|
||||
import { Button, TextInput } from "@tremor/react";
|
||||
import {
|
||||
|
|
@ -11,6 +11,8 @@ import {
|
|||
UsergroupAddOutlined,
|
||||
ExportOutlined,
|
||||
} from "@ant-design/icons";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import type { TFunction } from "i18next";
|
||||
import { createGuardrailCall, updateGuardrailCall, testCustomCodeGuardrail } from "../../networking";
|
||||
import NotificationsManager from "../../molecules/notifications_manager";
|
||||
|
||||
|
|
@ -93,47 +95,6 @@ const CODE_TEMPLATES = {
|
|||
},
|
||||
};
|
||||
|
||||
// Available primitives organized by category
|
||||
const PRIMITIVES = {
|
||||
"Return Values": [
|
||||
{ name: "allow()", desc: "Let request/response through" },
|
||||
{ name: "block(reason)", desc: "Reject with message" },
|
||||
{ name: "modify(texts=[], images=[], tool_calls=[])", desc: "Transform content" },
|
||||
],
|
||||
"HTTP Requests (async)": [
|
||||
{ name: "await http_request(url, method, headers, body)", desc: "Make async HTTP request" },
|
||||
{ name: "await http_get(url, headers)", desc: "Async GET request" },
|
||||
{ name: "await http_post(url, body, headers)", desc: "Async POST request" },
|
||||
],
|
||||
"Regex Functions": [
|
||||
{ name: "regex_match(text, pattern)", desc: "Returns True if pattern found" },
|
||||
{ name: "regex_replace(text, pattern, replacement)", desc: "Replace all matches" },
|
||||
{ name: "regex_find_all(text, pattern)", desc: "Return list of matches" },
|
||||
],
|
||||
"JSON Functions": [
|
||||
{ name: "json_parse(text)", desc: "Parse JSON string, returns None on error" },
|
||||
{ name: "json_stringify(obj)", desc: "Convert to JSON string" },
|
||||
{ name: "json_schema_valid(obj, schema)", desc: "Validate against JSON schema" },
|
||||
],
|
||||
"URL Functions": [
|
||||
{ name: "extract_urls(text)", desc: "Extract all URLs from text" },
|
||||
{ name: "is_valid_url(url)", desc: "Check if URL is valid" },
|
||||
{ name: "all_urls_valid(text)", desc: "Check all URLs in text are valid" },
|
||||
],
|
||||
"Code Detection": [
|
||||
{ name: "detect_code(text)", desc: "Returns True if code detected" },
|
||||
{ name: "detect_code_languages(text)", desc: "Returns list of detected languages" },
|
||||
{ name: 'contains_code_language(text, ["sql"])', desc: "Check for specific languages" },
|
||||
],
|
||||
"Text Utilities": [
|
||||
{ name: "contains(text, substring)", desc: "Check if substring exists" },
|
||||
{ name: "contains_any(text, [substr1, substr2])", desc: "Check if any substring exists" },
|
||||
{ name: "word_count(text)", desc: "Count words" },
|
||||
{ name: "char_count(text)", desc: "Count characters" },
|
||||
{ name: "lower(text) / upper(text) / trim(text)", desc: "String transforms" },
|
||||
],
|
||||
};
|
||||
|
||||
const MODE_OPTIONS = [
|
||||
{ value: "pre_call", label: "pre_call (Request)" },
|
||||
{ value: "post_call", label: "post_call (Response)" },
|
||||
|
|
@ -144,6 +105,49 @@ const MODE_OPTIONS = [
|
|||
{ value: "during_mcp_call", label: "during_mcp_call (During MCP Tool Call)" },
|
||||
];
|
||||
|
||||
const getPrimitives = (t: TFunction) => ({
|
||||
"Return Values": [
|
||||
{ name: "allow()", desc: t("guardrails.customCodeModal.primAllowDesc") },
|
||||
{ name: "block(reason)", desc: t("guardrails.customCodeModal.primBlockDesc") },
|
||||
{ name: "modify(texts=[], images=[], tool_calls=[])", desc: t("guardrails.customCodeModal.primModifyDesc") },
|
||||
],
|
||||
"HTTP Requests (async)": [
|
||||
{
|
||||
name: "await http_request(url, method, headers, body)",
|
||||
desc: t("guardrails.customCodeModal.primHttpRequestDesc"),
|
||||
},
|
||||
{ name: "await http_get(url, headers)", desc: t("guardrails.customCodeModal.primHttpGetDesc") },
|
||||
{ name: "await http_post(url, body, headers)", desc: t("guardrails.customCodeModal.primHttpPostDesc") },
|
||||
],
|
||||
"Regex Functions": [
|
||||
{ name: "regex_match(text, pattern)", desc: t("guardrails.customCodeModal.primRegexMatchDesc") },
|
||||
{ name: "regex_replace(text, pattern, replacement)", desc: t("guardrails.customCodeModal.primRegexReplaceDesc") },
|
||||
{ name: "regex_find_all(text, pattern)", desc: t("guardrails.customCodeModal.primRegexFindAllDesc") },
|
||||
],
|
||||
"JSON Functions": [
|
||||
{ name: "json_parse(text)", desc: t("guardrails.customCodeModal.primJsonParseDesc") },
|
||||
{ name: "json_stringify(obj)", desc: t("guardrails.customCodeModal.primJsonStringifyDesc") },
|
||||
{ name: "json_schema_valid(obj, schema)", desc: t("guardrails.customCodeModal.primJsonSchemaValidDesc") },
|
||||
],
|
||||
"URL Functions": [
|
||||
{ name: "extract_urls(text)", desc: t("guardrails.customCodeModal.primExtractUrlsDesc") },
|
||||
{ name: "is_valid_url(url)", desc: t("guardrails.customCodeModal.primIsValidUrlDesc") },
|
||||
{ name: "all_urls_valid(text)", desc: t("guardrails.customCodeModal.primAllUrlsValidDesc") },
|
||||
],
|
||||
"Code Detection": [
|
||||
{ name: "detect_code(text)", desc: t("guardrails.customCodeModal.primDetectCodeDesc") },
|
||||
{ name: "detect_code_languages(text)", desc: t("guardrails.customCodeModal.primDetectCodeLangsDesc") },
|
||||
{ name: 'contains_code_language(text, ["sql"])', desc: t("guardrails.customCodeModal.primContainsCodeLangDesc") },
|
||||
],
|
||||
"Text Utilities": [
|
||||
{ name: "contains(text, substring)", desc: t("guardrails.customCodeModal.primContainsDesc") },
|
||||
{ name: "contains_any(text, [substr1, substr2])", desc: t("guardrails.customCodeModal.primContainsAnyDesc") },
|
||||
{ name: "word_count(text)", desc: t("guardrails.customCodeModal.primWordCountDesc") },
|
||||
{ name: "char_count(text)", desc: t("guardrails.customCodeModal.primCharCountDesc") },
|
||||
{ name: "lower(text) / upper(text) / trim(text)", desc: t("guardrails.customCodeModal.primStringTransformsDesc") },
|
||||
],
|
||||
});
|
||||
|
||||
// Data for editing an existing guardrail
|
||||
export interface EditGuardrailData {
|
||||
guardrail_id: string;
|
||||
|
|
@ -166,7 +170,9 @@ interface CustomCodeModalProps {
|
|||
}
|
||||
|
||||
const CustomCodeModal: React.FC<CustomCodeModalProps> = ({ visible, onClose, onSuccess, accessToken, editData }) => {
|
||||
const { t } = useTranslation();
|
||||
const isEditMode = !!editData;
|
||||
const primitives = useMemo(() => getPrimitives(t), [t]);
|
||||
const [guardrailName, setGuardrailName] = useState("");
|
||||
const [mode, setMode] = useState<string[]>(["pre_call"]);
|
||||
const [defaultOn, setDefaultOn] = useState(false);
|
||||
|
|
@ -338,15 +344,15 @@ const CustomCodeModal: React.FC<CustomCodeModalProps> = ({ visible, onClose, onS
|
|||
// Save guardrail (create or update)
|
||||
const handleSave = async () => {
|
||||
if (!guardrailName.trim()) {
|
||||
NotificationsManager.fromBackend("Please enter a guardrail name");
|
||||
NotificationsManager.fromBackend(t("guardrails.customCodeModal.noGuardrailName"));
|
||||
return;
|
||||
}
|
||||
if (!code.trim()) {
|
||||
NotificationsManager.fromBackend("Please enter custom code");
|
||||
NotificationsManager.fromBackend(t("guardrails.customCodeModal.noCustomCode"));
|
||||
return;
|
||||
}
|
||||
if (!accessToken) {
|
||||
NotificationsManager.fromBackend("No access token available");
|
||||
NotificationsManager.fromBackend(t("guardrails.customCodeModal.noAccessToken"));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -374,7 +380,7 @@ const CustomCodeModal: React.FC<CustomCodeModalProps> = ({ visible, onClose, onS
|
|||
}
|
||||
|
||||
await updateGuardrailCall(accessToken, editData.guardrail_id, updateData);
|
||||
NotificationsManager.success("Custom code guardrail updated successfully");
|
||||
NotificationsManager.success(t("guardrails.customCodeModal.updateSuccess"));
|
||||
} else {
|
||||
// Create new guardrail
|
||||
const guardrailData = {
|
||||
|
|
@ -389,15 +395,17 @@ const CustomCodeModal: React.FC<CustomCodeModalProps> = ({ visible, onClose, onS
|
|||
};
|
||||
|
||||
await createGuardrailCall(accessToken, guardrailData);
|
||||
NotificationsManager.success("Custom code guardrail created successfully");
|
||||
NotificationsManager.success(t("guardrails.customCodeModal.createSuccess"));
|
||||
}
|
||||
onSuccess();
|
||||
onClose();
|
||||
} catch (error) {
|
||||
console.error("Failed to save guardrail:", error);
|
||||
NotificationsManager.fromBackend(
|
||||
`Failed to ${isEditMode ? "update" : "create"} guardrail: ` +
|
||||
(error instanceof Error ? error.message : String(error)),
|
||||
t("guardrails.customCodeModal.saveFailed", {
|
||||
action: isEditMode ? "update" : "create",
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
}),
|
||||
);
|
||||
} finally {
|
||||
setIsSaving(false);
|
||||
|
|
@ -420,7 +428,7 @@ const CustomCodeModal: React.FC<CustomCodeModalProps> = ({ visible, onClose, onS
|
|||
try {
|
||||
parsedInput = JSON.parse(testInput);
|
||||
} catch (e) {
|
||||
setTestResult({ error: "Invalid test input JSON" });
|
||||
setTestResult({ error: t("guardrails.customCodeModal.invalidTestJson") });
|
||||
setIsTesting(false);
|
||||
return;
|
||||
}
|
||||
|
|
@ -457,7 +465,7 @@ const CustomCodeModal: React.FC<CustomCodeModalProps> = ({ visible, onClose, onS
|
|||
error_type: response.error_type,
|
||||
});
|
||||
} else {
|
||||
setTestResult({ error: "Unknown error occurred" });
|
||||
setTestResult({ error: t("guardrails.customCodeModal.unknownError") });
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to test custom code:", error);
|
||||
|
|
@ -485,19 +493,27 @@ const CustomCodeModal: React.FC<CustomCodeModalProps> = ({ visible, onClose, onS
|
|||
{/* Header */}
|
||||
<div className="pb-4 border-b border-gray-200">
|
||||
<h2 className="text-xl font-semibold text-gray-900">
|
||||
{isEditMode ? "Edit Custom Guardrail" : "Create Custom Guardrail"}
|
||||
{isEditMode ? t("guardrails.customCodeModal.editTitle") : t("guardrails.customCodeModal.createTitle")}
|
||||
</h2>
|
||||
<p className="text-sm text-gray-500 mt-1">Define custom logic using Python-like syntax</p>
|
||||
<p className="text-sm text-gray-500 mt-1">{t("guardrails.customCodeModal.subtitle")}</p>
|
||||
</div>
|
||||
|
||||
{/* Top Controls */}
|
||||
<div className="flex items-center gap-4 py-4 border-b border-gray-100">
|
||||
<div className="flex-1 max-w-[200px]">
|
||||
<label className="block text-xs font-medium text-gray-600 mb-1">Guardrail Name</label>
|
||||
<TextInput value={guardrailName} onValueChange={setGuardrailName} placeholder="e.g., block-pii-custom" />
|
||||
<label className="block text-xs font-medium text-gray-600 mb-1">
|
||||
{t("guardrails.customCodeModal.guardrailNameLabel")}
|
||||
</label>
|
||||
<TextInput
|
||||
value={guardrailName}
|
||||
onValueChange={setGuardrailName}
|
||||
placeholder={t("guardrails.customCodeModal.guardrailNamePlaceholder")}
|
||||
/>
|
||||
</div>
|
||||
<div className="w-[280px]">
|
||||
<label className="block text-xs font-medium text-gray-600 mb-1">Mode (can select multiple)</label>
|
||||
<label className="block text-xs font-medium text-gray-600 mb-1">
|
||||
{t("guardrails.customCodeModal.modeLabel")}
|
||||
</label>
|
||||
<Select
|
||||
mode="multiple"
|
||||
value={mode}
|
||||
|
|
@ -505,11 +521,13 @@ const CustomCodeModal: React.FC<CustomCodeModalProps> = ({ visible, onClose, onS
|
|||
options={MODE_OPTIONS}
|
||||
className="w-full"
|
||||
size="middle"
|
||||
placeholder="Select modes"
|
||||
placeholder={t("guardrails.customCodeModal.modePlaceholder")}
|
||||
/>
|
||||
</div>
|
||||
<div className="w-[180px]">
|
||||
<label className="block text-xs font-medium text-gray-600 mb-1">Template</label>
|
||||
<label className="block text-xs font-medium text-gray-600 mb-1">
|
||||
{t("guardrails.customCodeModal.templateLabel")}
|
||||
</label>
|
||||
<Select
|
||||
value={selectedTemplate}
|
||||
onChange={handleTemplateChange}
|
||||
|
|
@ -541,13 +559,13 @@ const CustomCodeModal: React.FC<CustomCodeModalProps> = ({ visible, onClose, onS
|
|||
}}
|
||||
>
|
||||
<UsergroupAddOutlined />
|
||||
<span>Browse Community templates</span>
|
||||
<span>{t("guardrails.customCodeModal.browseCommunityTemplates")}</span>
|
||||
<ExportOutlined style={{ fontSize: "10px" }} />
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
>
|
||||
<Select.OptGroup label="STANDARD">
|
||||
<Select.OptGroup label={t("guardrails.customCodeModal.templateGroupStandard")}>
|
||||
{Object.entries(CODE_TEMPLATES).map(([key, template]) => (
|
||||
<Select.Option key={key} value={key}>
|
||||
{template.name}
|
||||
|
|
@ -557,7 +575,7 @@ const CustomCodeModal: React.FC<CustomCodeModalProps> = ({ visible, onClose, onS
|
|||
</Select>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 pt-5">
|
||||
<span className="text-sm text-gray-600">Default On</span>
|
||||
<span className="text-sm text-gray-600">{t("guardrails.customCodeModal.defaultOnLabel")}</span>
|
||||
<Switch checked={defaultOn} onChange={setDefaultOn} />
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -567,8 +585,10 @@ const CustomCodeModal: React.FC<CustomCodeModalProps> = ({ visible, onClose, onS
|
|||
{/* Code Editor */}
|
||||
<div className="flex-[2] flex flex-col min-w-0 overflow-y-auto">
|
||||
<div className="flex items-center justify-between mb-2 flex-shrink-0">
|
||||
<span className="text-xs font-semibold text-gray-500 uppercase tracking-wide">Python Logic</span>
|
||||
<span className="text-xs text-gray-400">Restricted environment (no imports)</span>
|
||||
<span className="text-xs font-semibold text-gray-500 uppercase tracking-wide">
|
||||
{t("guardrails.customCodeModal.pythonLogicLabel")}
|
||||
</span>
|
||||
<span className="text-xs text-gray-400">{t("guardrails.customCodeModal.restrictedEnv")}</span>
|
||||
</div>
|
||||
<div
|
||||
className="relative rounded-lg overflow-hidden border border-gray-700 bg-[#1e1e1e] flex-shrink-0"
|
||||
|
|
@ -617,7 +637,7 @@ const CustomCodeModal: React.FC<CustomCodeModalProps> = ({ visible, onClose, onS
|
|||
header={
|
||||
<span className="flex items-center gap-2 text-sm font-medium">
|
||||
<PlayCircleOutlined className="text-blue-500" />
|
||||
Test Your Guardrail
|
||||
{t("guardrails.customCodeModal.testPanelHeader")}
|
||||
</span>
|
||||
}
|
||||
key="test"
|
||||
|
|
@ -625,29 +645,33 @@ const CustomCodeModal: React.FC<CustomCodeModalProps> = ({ visible, onClose, onS
|
|||
<div className="space-y-3">
|
||||
<div>
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<label className="block text-xs font-medium text-gray-600">Test Input (JSON)</label>
|
||||
<label className="block text-xs font-medium text-gray-600">
|
||||
{t("guardrails.customCodeModal.testInputLabel")}
|
||||
</label>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-xs text-gray-500">Load example:</span>
|
||||
<span className="text-xs text-gray-500">
|
||||
{t("guardrails.customCodeModal.loadExampleLabel")}
|
||||
</span>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setTestInput(JSON.stringify(TEST_INPUT_EXAMPLES.pre_call.data, null, 2))}
|
||||
className="px-2 py-1 text-xs rounded border border-orange-200 bg-orange-50 text-orange-700 hover:bg-orange-100 transition-colors"
|
||||
>
|
||||
Pre-call
|
||||
{t("guardrails.customCodeModal.examplePreCall")}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setTestInput(JSON.stringify(TEST_INPUT_EXAMPLES.pre_mcp_call.data, null, 2))}
|
||||
className="px-2 py-1 text-xs rounded border border-purple-200 bg-purple-50 text-purple-700 hover:bg-purple-100 transition-colors"
|
||||
>
|
||||
Pre MCP
|
||||
{t("guardrails.customCodeModal.examplePreMcp")}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setTestInput(JSON.stringify(TEST_INPUT_EXAMPLES.post_call.data, null, 2))}
|
||||
className="px-2 py-1 text-xs rounded border border-green-200 bg-green-50 text-green-700 hover:bg-green-100 transition-colors"
|
||||
>
|
||||
Post-call
|
||||
{t("guardrails.customCodeModal.examplePostCall")}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -686,7 +710,9 @@ const CustomCodeModal: React.FC<CustomCodeModalProps> = ({ visible, onClose, onS
|
|||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<Button size="xs" onClick={handleTest} disabled={isTesting} icon={PlayCircleOutlined}>
|
||||
{isTesting ? "Running..." : "Run Test"}
|
||||
{isTesting
|
||||
? t("guardrails.customCodeModal.runningButton")
|
||||
: t("guardrails.customCodeModal.runTestButton")}
|
||||
</Button>
|
||||
{testResult && (
|
||||
<div
|
||||
|
|
@ -710,15 +736,16 @@ const CustomCodeModal: React.FC<CustomCodeModalProps> = ({ visible, onClose, onS
|
|||
</>
|
||||
) : testResult.action === "allow" ? (
|
||||
<>
|
||||
<CheckCircleOutlined /> Allowed
|
||||
<CheckCircleOutlined /> {t("guardrails.customCodeModal.testResultAllowed")}
|
||||
</>
|
||||
) : testResult.action === "block" ? (
|
||||
<>
|
||||
<CloseCircleOutlined /> Blocked: {testResult.reason}
|
||||
<CloseCircleOutlined />{" "}
|
||||
{t("guardrails.customCodeModal.testResultBlocked", { reason: testResult.reason })}
|
||||
</>
|
||||
) : testResult.action === "modify" ? (
|
||||
<>
|
||||
<CheckCircleOutlined /> Modified
|
||||
<CheckCircleOutlined /> {t("guardrails.customCodeModal.testResultModified")}
|
||||
{testResult.texts && testResult.texts.length > 0 && (
|
||||
<span className="text-xs text-gray-500 ml-1">
|
||||
→ {testResult.texts[0].substring(0, 50)}
|
||||
|
|
@ -728,7 +755,10 @@ const CustomCodeModal: React.FC<CustomCodeModalProps> = ({ visible, onClose, onS
|
|||
</>
|
||||
) : (
|
||||
<>
|
||||
<CheckCircleOutlined /> {testResult.action || "Unknown"}
|
||||
<CheckCircleOutlined />{" "}
|
||||
{t("guardrails.customCodeModal.testResultUnknown", {
|
||||
action: testResult.action || t("common.unknown"),
|
||||
})}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -744,8 +774,10 @@ const CustomCodeModal: React.FC<CustomCodeModalProps> = ({ visible, onClose, onS
|
|||
<UsergroupAddOutlined className="text-blue-600 text-lg" />
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-sm font-medium text-gray-900">Built a useful guardrail?</div>
|
||||
<div className="text-xs text-gray-600">Share it with the community and help others build faster</div>
|
||||
<div className="text-sm font-medium text-gray-900">
|
||||
{t("guardrails.customCodeModal.contributeBannerTitle")}
|
||||
</div>
|
||||
<div className="text-xs text-gray-600">{t("guardrails.customCodeModal.contributeBannerDesc")}</div>
|
||||
</div>
|
||||
</div>
|
||||
<Button
|
||||
|
|
@ -754,7 +786,7 @@ const CustomCodeModal: React.FC<CustomCodeModalProps> = ({ visible, onClose, onS
|
|||
icon={ExportOutlined}
|
||||
className="bg-blue-600 hover:bg-blue-700 text-white border-0"
|
||||
>
|
||||
Contribute Template
|
||||
{t("guardrails.customCodeModal.contributeButton")}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -763,23 +795,25 @@ const CustomCodeModal: React.FC<CustomCodeModalProps> = ({ visible, onClose, onS
|
|||
<div className="w-[300px] flex-shrink-0 overflow-auto border-l border-gray-200 pl-6">
|
||||
<div className="flex items-center gap-2 mb-3">
|
||||
<CodeOutlined className="text-blue-500" />
|
||||
<span className="font-semibold text-gray-700">Available Primitives</span>
|
||||
<span className="font-semibold text-gray-700">
|
||||
{t("guardrails.customCodeModal.availablePrimitivesTitle")}
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-xs text-gray-500 mb-3">Click to copy functions to clipboard</p>
|
||||
<p className="text-xs text-gray-500 mb-3">{t("guardrails.customCodeModal.availablePrimitivesDesc")}</p>
|
||||
|
||||
<Collapse
|
||||
defaultActiveKey={["Return Values"]}
|
||||
className="primitives-collapse bg-transparent border-0"
|
||||
expandIconPosition="end"
|
||||
>
|
||||
{Object.entries(PRIMITIVES).map(([category, primitives]) => (
|
||||
{Object.entries(primitives).map(([category, categoryPrimitives]) => (
|
||||
<Panel
|
||||
header={<span className="text-sm font-medium text-gray-700">{category}</span>}
|
||||
key={category}
|
||||
className="bg-white mb-2 rounded-lg border border-gray-200"
|
||||
>
|
||||
<div className="space-y-2">
|
||||
{primitives.map((p) => (
|
||||
{categoryPrimitives.map((p) => (
|
||||
<button
|
||||
key={p.name}
|
||||
onClick={() => copyPrimitive(p.name)}
|
||||
|
|
@ -789,7 +823,7 @@ const CustomCodeModal: React.FC<CustomCodeModalProps> = ({ visible, onClose, onS
|
|||
>
|
||||
{copiedPrimitive === p.name ? (
|
||||
<span className="flex items-center gap-1 text-xs font-mono text-green-700">
|
||||
<CheckCircleOutlined /> Copied!
|
||||
<CheckCircleOutlined /> {t("guardrails.customCodeModal.copied")}
|
||||
</span>
|
||||
) : (
|
||||
<>
|
||||
|
|
@ -808,10 +842,10 @@ const CustomCodeModal: React.FC<CustomCodeModalProps> = ({ visible, onClose, onS
|
|||
|
||||
{/* Footer */}
|
||||
<div className="flex items-center justify-between pt-4 mt-4 border-t border-gray-200">
|
||||
<span className="text-xs text-gray-400">Changes are auto-saved to local draft</span>
|
||||
<span className="text-xs text-gray-400">{t("guardrails.customCodeModal.autoSaveNotice")}</span>
|
||||
<div className="flex items-center gap-3">
|
||||
<Button variant="secondary" onClick={onClose}>
|
||||
Cancel
|
||||
{t("common.cancel")}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={handleSave}
|
||||
|
|
@ -819,7 +853,7 @@ const CustomCodeModal: React.FC<CustomCodeModalProps> = ({ visible, onClose, onS
|
|||
disabled={isSaving || !guardrailName.trim()}
|
||||
icon={SaveOutlined}
|
||||
>
|
||||
{isEditMode ? "Update Guardrail" : "Save Guardrail"}
|
||||
{isEditMode ? t("guardrails.customCodeModal.updateButton") : t("guardrails.customCodeModal.saveButton")}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import React, { useState, useEffect } from "react";
|
||||
import { Form, Typography, Select, Input, Switch, Modal } from "antd";
|
||||
import { Button, TextInput } from "@tremor/react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
guardrail_provider_map,
|
||||
guardrailLogoMap,
|
||||
|
|
@ -54,6 +55,7 @@ const EditGuardrailForm: React.FC<EditGuardrailFormProps> = ({
|
|||
fullLitellmParams,
|
||||
initialValues,
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const [form] = Form.useForm();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [selectedProvider, setSelectedProvider] = useState<string | null>(initialValues?.provider || null);
|
||||
|
|
@ -71,7 +73,7 @@ const EditGuardrailForm: React.FC<EditGuardrailFormProps> = ({
|
|||
setGuardrailSettings(data);
|
||||
} catch (error) {
|
||||
console.error("Error fetching guardrail settings:", error);
|
||||
NotificationsManager.fromBackend("Failed to load guardrail settings");
|
||||
NotificationsManager.fromBackend(t("guardrails.editGuardrailForm.loadSettingsFailed"));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -178,7 +180,7 @@ const EditGuardrailForm: React.FC<EditGuardrailFormProps> = ({
|
|||
guardrail_info = configObj;
|
||||
}
|
||||
} catch (error) {
|
||||
NotificationsManager.fromBackend("Invalid JSON in configuration");
|
||||
NotificationsManager.fromBackend(t("guardrails.editGuardrailForm.invalidJsonConfig"));
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
|
|
@ -222,7 +224,7 @@ const EditGuardrailForm: React.FC<EditGuardrailFormProps> = ({
|
|||
throw new Error(errorData || "Failed to update guardrail");
|
||||
}
|
||||
|
||||
NotificationsManager.success("Guardrail updated successfully");
|
||||
NotificationsManager.success(t("guardrails.editGuardrailForm.updateSuccess"));
|
||||
|
||||
// Reset and close
|
||||
onSuccess();
|
||||
|
|
@ -230,7 +232,9 @@ const EditGuardrailForm: React.FC<EditGuardrailFormProps> = ({
|
|||
} catch (error) {
|
||||
console.error("Failed to update guardrail:", error);
|
||||
NotificationsManager.fromBackend(
|
||||
"Failed to update guardrail: " + (error instanceof Error ? error.message : String(error)),
|
||||
t("guardrails.editGuardrailForm.updateFailed", {
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
}),
|
||||
);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
|
|
@ -264,7 +268,11 @@ const EditGuardrailForm: React.FC<EditGuardrailFormProps> = ({
|
|||
switch (selectedProvider) {
|
||||
case "Aporia":
|
||||
return (
|
||||
<Form.Item label="Aporia Configuration" name="config" tooltip="JSON configuration for Aporia">
|
||||
<Form.Item
|
||||
label={t("guardrails.editGuardrailForm.aporiaConfigLabel")}
|
||||
name="config"
|
||||
tooltip={t("guardrails.editGuardrailForm.aporiaConfigTooltip")}
|
||||
>
|
||||
<Input.TextArea
|
||||
rows={4}
|
||||
placeholder={`{
|
||||
|
|
@ -276,7 +284,11 @@ const EditGuardrailForm: React.FC<EditGuardrailFormProps> = ({
|
|||
);
|
||||
case "AimSecurity":
|
||||
return (
|
||||
<Form.Item label="Aim Security Configuration" name="config" tooltip="JSON configuration for Aim Security">
|
||||
<Form.Item
|
||||
label={t("guardrails.editGuardrailForm.aimConfigLabel")}
|
||||
name="config"
|
||||
tooltip={t("guardrails.editGuardrailForm.aimConfigTooltip")}
|
||||
>
|
||||
<Input.TextArea
|
||||
rows={4}
|
||||
placeholder={`{
|
||||
|
|
@ -288,9 +300,9 @@ const EditGuardrailForm: React.FC<EditGuardrailFormProps> = ({
|
|||
case "Bedrock":
|
||||
return (
|
||||
<Form.Item
|
||||
label="Amazon Bedrock Configuration"
|
||||
label={t("guardrails.editGuardrailForm.bedrockConfigLabel")}
|
||||
name="config"
|
||||
tooltip="JSON configuration for Amazon Bedrock guardrails"
|
||||
tooltip={t("guardrails.editGuardrailForm.bedrockConfigTooltip")}
|
||||
>
|
||||
<Input.TextArea
|
||||
rows={4}
|
||||
|
|
@ -303,7 +315,11 @@ const EditGuardrailForm: React.FC<EditGuardrailFormProps> = ({
|
|||
);
|
||||
case "CatoNetworks":
|
||||
return (
|
||||
<Form.Item label="Cato Networks Configuration" name="config" tooltip="JSON configuration for Cato Networks">
|
||||
<Form.Item
|
||||
label={t("guardrails.editGuardrailForm.catoConfigLabel")}
|
||||
name="config"
|
||||
tooltip={t("guardrails.editGuardrailForm.catoConfigTooltip")}
|
||||
>
|
||||
<Input.TextArea
|
||||
rows={4}
|
||||
placeholder={`{
|
||||
|
|
@ -314,7 +330,11 @@ const EditGuardrailForm: React.FC<EditGuardrailFormProps> = ({
|
|||
);
|
||||
case "GuardrailsAI":
|
||||
return (
|
||||
<Form.Item label="Guardrails.ai Configuration" name="config" tooltip="JSON configuration for Guardrails.ai">
|
||||
<Form.Item
|
||||
label={t("guardrails.editGuardrailForm.guardrailsAiConfigLabel")}
|
||||
name="config"
|
||||
tooltip={t("guardrails.editGuardrailForm.guardrailsAiConfigTooltip")}
|
||||
>
|
||||
<Input.TextArea
|
||||
rows={4}
|
||||
placeholder={`{
|
||||
|
|
@ -326,7 +346,11 @@ const EditGuardrailForm: React.FC<EditGuardrailFormProps> = ({
|
|||
);
|
||||
case "LakeraAI":
|
||||
return (
|
||||
<Form.Item label="Lakera AI Configuration" name="config" tooltip="JSON configuration for Lakera AI">
|
||||
<Form.Item
|
||||
label={t("guardrails.editGuardrailForm.lakeraConfigLabel")}
|
||||
name="config"
|
||||
tooltip={t("guardrails.editGuardrailForm.lakeraConfigTooltip")}
|
||||
>
|
||||
<Input.TextArea
|
||||
rows={4}
|
||||
placeholder={`{
|
||||
|
|
@ -338,9 +362,9 @@ const EditGuardrailForm: React.FC<EditGuardrailFormProps> = ({
|
|||
case "PromptInjection":
|
||||
return (
|
||||
<Form.Item
|
||||
label="Prompt Injection Configuration"
|
||||
label={t("guardrails.editGuardrailForm.promptInjectionConfigLabel")}
|
||||
name="config"
|
||||
tooltip="JSON configuration for prompt injection detection"
|
||||
tooltip={t("guardrails.editGuardrailForm.promptInjectionConfigTooltip")}
|
||||
>
|
||||
<Input.TextArea
|
||||
rows={4}
|
||||
|
|
@ -352,7 +376,11 @@ const EditGuardrailForm: React.FC<EditGuardrailFormProps> = ({
|
|||
);
|
||||
default:
|
||||
return (
|
||||
<Form.Item label="Custom Configuration" name="config" tooltip="JSON configuration for your custom guardrail">
|
||||
<Form.Item
|
||||
label={t("guardrails.editGuardrailForm.customConfigLabel")}
|
||||
name="config"
|
||||
tooltip={t("guardrails.editGuardrailForm.customConfigTooltip")}
|
||||
>
|
||||
<Input.TextArea
|
||||
rows={4}
|
||||
placeholder={`{
|
||||
|
|
@ -366,25 +394,31 @@ const EditGuardrailForm: React.FC<EditGuardrailFormProps> = ({
|
|||
};
|
||||
|
||||
return (
|
||||
<Modal title="Edit Guardrail" open={visible} onCancel={onClose} footer={null} width={700}>
|
||||
<Modal
|
||||
title={t("guardrails.editGuardrailForm.modalTitle")}
|
||||
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" }]}
|
||||
label={t("guardrails.editGuardrailForm.guardrailNameLabel")}
|
||||
rules={[{ required: true, message: t("guardrails.editGuardrailForm.guardrailNameRequired") }]}
|
||||
>
|
||||
<TextInput placeholder="Enter a name for this guardrail" />
|
||||
<TextInput placeholder={t("guardrails.editGuardrailForm.guardrailNamePlaceholder")} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="provider"
|
||||
label="Guardrail Provider"
|
||||
rules={[{ required: true, message: "Please select a provider" }]}
|
||||
label={t("guardrails.editGuardrailForm.providerLabel")}
|
||||
rules={[{ required: true, message: t("guardrails.editGuardrailForm.providerRequired") }]}
|
||||
>
|
||||
<Select
|
||||
placeholder="Select a guardrail provider"
|
||||
placeholder={t("guardrails.editGuardrailForm.providerPlaceholder")}
|
||||
onChange={handleProviderChange}
|
||||
disabled={true} // Disable changing provider in edit mode
|
||||
disabled={true}
|
||||
optionLabelProp="label"
|
||||
>
|
||||
{Object.entries(getGuardrailProviders()).map(([key, value]) => (
|
||||
|
|
@ -415,9 +449,9 @@ const EditGuardrailForm: React.FC<EditGuardrailFormProps> = ({
|
|||
|
||||
<Form.Item
|
||||
name="mode"
|
||||
label="Mode"
|
||||
tooltip="How the guardrail should be applied"
|
||||
rules={[{ required: true, message: "Please select a mode" }]}
|
||||
label={t("guardrails.editGuardrailForm.modeLabel")}
|
||||
tooltip={t("guardrails.editGuardrailForm.modeTooltip")}
|
||||
rules={[{ required: true, message: t("guardrails.editGuardrailForm.modeRequired") }]}
|
||||
>
|
||||
<Select>
|
||||
{guardrailSettings?.supported_modes?.map((mode) => (
|
||||
|
|
@ -435,8 +469,8 @@ const EditGuardrailForm: React.FC<EditGuardrailFormProps> = ({
|
|||
|
||||
<Form.Item
|
||||
name="default_on"
|
||||
label="Always On"
|
||||
tooltip="If enabled, this guardrail will be applied to all requests by default"
|
||||
label={t("guardrails.editGuardrailForm.alwaysOnLabel")}
|
||||
tooltip={t("guardrails.editGuardrailForm.alwaysOnTooltip")}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
|
|
@ -444,25 +478,25 @@ const EditGuardrailForm: React.FC<EditGuardrailFormProps> = ({
|
|||
|
||||
<Form.Item
|
||||
name="skip_system_message_choice"
|
||||
label="Skip system messages in guardrail"
|
||||
tooltip="Unified guardrails only: whether role: system content is omitted from guardrail input (LLM still receives full messages). Use global default follows litellm_settings.skip_system_message_in_guardrail."
|
||||
label={t("guardrails.editGuardrailForm.skipSystemMsgLabel")}
|
||||
tooltip={t("guardrails.editGuardrailForm.skipSystemMsgTooltip")}
|
||||
>
|
||||
<Select>
|
||||
<Option value="inherit">Use global default</Option>
|
||||
<Option value="yes">Yes — exclude from guardrail scan</Option>
|
||||
<Option value="no">No — always include in scan</Option>
|
||||
<Option value="inherit">{t("guardrails.editGuardrailForm.useGlobalDefault")}</Option>
|
||||
<Option value="yes">{t("guardrails.editGuardrailForm.yesExcludeFromScan")}</Option>
|
||||
<Option value="no">{t("guardrails.editGuardrailForm.noAlwaysInclude")}</Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="skip_tool_message_choice"
|
||||
label="Skip tool messages in guardrail"
|
||||
tooltip="Unified guardrails only: whether role: tool content is omitted from guardrail input (LLM still receives full messages). Use global default follows litellm_settings.skip_tool_message_in_guardrail."
|
||||
label={t("guardrails.editGuardrailForm.skipToolMsgLabel")}
|
||||
tooltip={t("guardrails.editGuardrailForm.skipToolMsgTooltip")}
|
||||
>
|
||||
<Select>
|
||||
<Option value="inherit">Use global default</Option>
|
||||
<Option value="yes">Yes — exclude from guardrail scan</Option>
|
||||
<Option value="no">No — always include in scan</Option>
|
||||
<Option value="inherit">{t("guardrails.editGuardrailForm.useGlobalDefault")}</Option>
|
||||
<Option value="yes">{t("guardrails.editGuardrailForm.yesExcludeFromScan")}</Option>
|
||||
<Option value="no">{t("guardrails.editGuardrailForm.noAlwaysInclude")}</Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
|
||||
|
|
@ -470,10 +504,10 @@ const EditGuardrailForm: React.FC<EditGuardrailFormProps> = ({
|
|||
|
||||
<div className="flex justify-end space-x-2 mt-4">
|
||||
<Button variant="secondary" onClick={onClose}>
|
||||
Cancel
|
||||
{t("common.cancel")}
|
||||
</Button>
|
||||
<Button onClick={handleSubmit} loading={loading}>
|
||||
Update Guardrail
|
||||
{t("guardrails.editGuardrailForm.updateButton")}
|
||||
</Button>
|
||||
</div>
|
||||
</Form>
|
||||
|
|
|
|||
|
|
@ -2714,6 +2714,154 @@
|
|||
"actionHelp": "Choose what action the guardrail should take when this pattern is detected",
|
||||
"actionBlock": "Block",
|
||||
"actionMask": "Mask"
|
||||
},
|
||||
"keywordModal": {
|
||||
"title": "Add blocked keyword",
|
||||
"keywordLabel": "Keyword",
|
||||
"keywordPlaceholder": "Enter sensitive keyword or phrase",
|
||||
"actionLabel": "Action",
|
||||
"actionDesc": "Choose what action the guardrail should take when this keyword is detected",
|
||||
"actionBlock": "Block",
|
||||
"actionMask": "Mask",
|
||||
"descriptionLabel": "Description (optional)",
|
||||
"descriptionPlaceholder": "Explain why this keyword is sensitive"
|
||||
},
|
||||
"keywordTable": {
|
||||
"colKeyword": "Keyword",
|
||||
"colAction": "Action",
|
||||
"colDescription": "Description",
|
||||
"actionBlock": "Block",
|
||||
"actionMask": "Mask",
|
||||
"noKeywords": "No keywords added."
|
||||
},
|
||||
"patternModal": {
|
||||
"title": "Add prebuilt pattern",
|
||||
"patternTypeLabel": "Pattern type",
|
||||
"patternTypePlaceholder": "Choose pattern type",
|
||||
"actionLabel": "Action",
|
||||
"actionDesc": "Choose what action the guardrail should take when this pattern is detected",
|
||||
"actionBlock": "Block",
|
||||
"actionMask": "Mask"
|
||||
},
|
||||
"patternTable": {
|
||||
"colType": "Type",
|
||||
"colPatternName": "Pattern name",
|
||||
"colRegexPattern": "Regex pattern",
|
||||
"colAction": "Action",
|
||||
"typePrebuilt": "Prebuilt",
|
||||
"typeCustom": "Custom",
|
||||
"actionBlock": "Block",
|
||||
"actionMask": "Mask",
|
||||
"noPatterns": "No patterns added."
|
||||
},
|
||||
"customCodeModal": {
|
||||
"editTitle": "Edit Custom Guardrail",
|
||||
"createTitle": "Create Custom Guardrail",
|
||||
"subtitle": "Define custom logic using Python-like syntax",
|
||||
"guardrailNameLabel": "Guardrail Name",
|
||||
"guardrailNamePlaceholder": "e.g., block-pii-custom",
|
||||
"modeLabel": "Mode (can select multiple)",
|
||||
"modePlaceholder": "Select modes",
|
||||
"templateLabel": "Template",
|
||||
"templateGroupStandard": "STANDARD",
|
||||
"browseCommunityTemplates": "Browse Community templates",
|
||||
"defaultOnLabel": "Default On",
|
||||
"pythonLogicLabel": "Python Logic",
|
||||
"restrictedEnv": "Restricted environment (no imports)",
|
||||
"testPanelHeader": "Test Your Guardrail",
|
||||
"testInputLabel": "Test Input (JSON)",
|
||||
"loadExampleLabel": "Load example:",
|
||||
"examplePreCall": "Pre-call",
|
||||
"examplePreMcp": "Pre MCP",
|
||||
"examplePostCall": "Post-call",
|
||||
"runTestButton": "Run Test",
|
||||
"runningButton": "Running...",
|
||||
"testResultAllowed": "Allowed",
|
||||
"testResultBlocked": "Blocked: {{reason}}",
|
||||
"testResultModified": "Modified",
|
||||
"testResultUnknown": "{{action}}",
|
||||
"contributeBannerTitle": "Built a useful guardrail?",
|
||||
"contributeBannerDesc": "Share it with the community and help others build faster",
|
||||
"contributeButton": "Contribute Template",
|
||||
"availablePrimitivesTitle": "Available Primitives",
|
||||
"availablePrimitivesDesc": "Click to copy functions to clipboard",
|
||||
"copied": "Copied!",
|
||||
"autoSaveNotice": "Changes are auto-saved to local draft",
|
||||
"updateButton": "Update Guardrail",
|
||||
"saveButton": "Save Guardrail",
|
||||
"noGuardrailName": "Please enter a guardrail name",
|
||||
"noCustomCode": "Please enter custom code",
|
||||
"noAccessToken": "No access token available",
|
||||
"updateSuccess": "Custom code guardrail updated successfully",
|
||||
"createSuccess": "Custom code guardrail created successfully",
|
||||
"saveFailed": "Failed to {{action}} guardrail: {{error}}",
|
||||
"invalidTestJson": "Invalid test input JSON",
|
||||
"unknownError": "Unknown error occurred",
|
||||
"primAllowDesc": "Let request/response through",
|
||||
"primBlockDesc": "Reject with message",
|
||||
"primModifyDesc": "Transform content",
|
||||
"primHttpRequestDesc": "Make async HTTP request",
|
||||
"primHttpGetDesc": "Async GET request",
|
||||
"primHttpPostDesc": "Async POST request",
|
||||
"primRegexMatchDesc": "Returns True if pattern found",
|
||||
"primRegexReplaceDesc": "Replace all matches",
|
||||
"primRegexFindAllDesc": "Return list of matches",
|
||||
"primJsonParseDesc": "Parse JSON string, returns None on error",
|
||||
"primJsonStringifyDesc": "Convert to JSON string",
|
||||
"primJsonSchemaValidDesc": "Validate against JSON schema",
|
||||
"primExtractUrlsDesc": "Extract all URLs from text",
|
||||
"primIsValidUrlDesc": "Check if URL is valid",
|
||||
"primAllUrlsValidDesc": "Check all URLs in text are valid",
|
||||
"primDetectCodeDesc": "Returns True if code detected",
|
||||
"primDetectCodeLangsDesc": "Returns list of detected languages",
|
||||
"primContainsCodeLangDesc": "Check for specific languages",
|
||||
"primContainsDesc": "Check if substring exists",
|
||||
"primContainsAnyDesc": "Check if any substring exists",
|
||||
"primWordCountDesc": "Count words",
|
||||
"primCharCountDesc": "Count characters",
|
||||
"primStringTransformsDesc": "String transforms"
|
||||
},
|
||||
"editGuardrailForm": {
|
||||
"modalTitle": "Edit 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",
|
||||
"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: whether role: system content is omitted from guardrail input (LLM 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: whether role: tool content is omitted from guardrail input (LLM 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",
|
||||
"updateButton": "Update Guardrail",
|
||||
"aporiaConfigLabel": "Aporia Configuration",
|
||||
"aporiaConfigTooltip": "JSON configuration for Aporia",
|
||||
"aimConfigLabel": "Aim Security Configuration",
|
||||
"aimConfigTooltip": "JSON configuration for Aim Security",
|
||||
"bedrockConfigLabel": "Amazon Bedrock Configuration",
|
||||
"bedrockConfigTooltip": "JSON configuration for Amazon Bedrock guardrails",
|
||||
"catoConfigLabel": "Cato Networks Configuration",
|
||||
"catoConfigTooltip": "JSON configuration for Cato Networks",
|
||||
"guardrailsAiConfigLabel": "Guardrails.ai Configuration",
|
||||
"guardrailsAiConfigTooltip": "JSON configuration for Guardrails.ai",
|
||||
"lakeraConfigLabel": "Lakera AI Configuration",
|
||||
"lakeraConfigTooltip": "JSON configuration for Lakera AI",
|
||||
"promptInjectionConfigLabel": "Prompt Injection Configuration",
|
||||
"promptInjectionConfigTooltip": "JSON configuration for prompt injection detection",
|
||||
"customConfigLabel": "Custom Configuration",
|
||||
"customConfigTooltip": "JSON configuration for your custom guardrail",
|
||||
"updateSuccess": "Guardrail updated successfully",
|
||||
"invalidJsonConfig": "Invalid JSON in configuration",
|
||||
"loadSettingsFailed": "Failed to load guardrail settings",
|
||||
"updateFailed": "Failed to update guardrail: {{error}}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2714,6 +2714,154 @@
|
|||
"actionHelp": "选择当检测到此模式时护栏应执行的操作",
|
||||
"actionBlock": "拦截",
|
||||
"actionMask": "遮蔽"
|
||||
},
|
||||
"keywordModal": {
|
||||
"title": "添加屏蔽关键词",
|
||||
"keywordLabel": "关键词",
|
||||
"keywordPlaceholder": "输入敏感关键词或短语",
|
||||
"actionLabel": "操作",
|
||||
"actionDesc": "选择当检测到此关键词时护栏应执行的操作",
|
||||
"actionBlock": "拦截",
|
||||
"actionMask": "遮蔽",
|
||||
"descriptionLabel": "描述(可选)",
|
||||
"descriptionPlaceholder": "说明为何该关键词属于敏感内容"
|
||||
},
|
||||
"keywordTable": {
|
||||
"colKeyword": "关键词",
|
||||
"colAction": "操作",
|
||||
"colDescription": "描述",
|
||||
"actionBlock": "拦截",
|
||||
"actionMask": "遮蔽",
|
||||
"noKeywords": "暂无关键词。"
|
||||
},
|
||||
"patternModal": {
|
||||
"title": "添加预置模式",
|
||||
"patternTypeLabel": "模式类型",
|
||||
"patternTypePlaceholder": "选择模式类型",
|
||||
"actionLabel": "操作",
|
||||
"actionDesc": "选择当检测到此模式时护栏应执行的操作",
|
||||
"actionBlock": "拦截",
|
||||
"actionMask": "遮蔽"
|
||||
},
|
||||
"patternTable": {
|
||||
"colType": "类型",
|
||||
"colPatternName": "模式名称",
|
||||
"colRegexPattern": "正则表达式",
|
||||
"colAction": "操作",
|
||||
"typePrebuilt": "预置",
|
||||
"typeCustom": "自定义",
|
||||
"actionBlock": "拦截",
|
||||
"actionMask": "遮蔽",
|
||||
"noPatterns": "暂无模式。"
|
||||
},
|
||||
"customCodeModal": {
|
||||
"editTitle": "编辑自定义护栏",
|
||||
"createTitle": "创建自定义护栏",
|
||||
"subtitle": "使用类 Python 语法定义自定义逻辑",
|
||||
"guardrailNameLabel": "护栏名称",
|
||||
"guardrailNamePlaceholder": "例如:block-pii-custom",
|
||||
"modeLabel": "模式(可多选)",
|
||||
"modePlaceholder": "选择模式",
|
||||
"templateLabel": "模板",
|
||||
"templateGroupStandard": "标准",
|
||||
"browseCommunityTemplates": "浏览社区模板",
|
||||
"defaultOnLabel": "默认开启",
|
||||
"pythonLogicLabel": "Python 逻辑",
|
||||
"restrictedEnv": "受限环境(不可导入模块)",
|
||||
"testPanelHeader": "测试护栏",
|
||||
"testInputLabel": "测试输入(JSON)",
|
||||
"loadExampleLabel": "加载示例:",
|
||||
"examplePreCall": "Pre-call",
|
||||
"examplePreMcp": "Pre MCP",
|
||||
"examplePostCall": "Post-call",
|
||||
"runTestButton": "运行测试",
|
||||
"runningButton": "运行中...",
|
||||
"testResultAllowed": "已放行",
|
||||
"testResultBlocked": "已拦截:{{reason}}",
|
||||
"testResultModified": "已修改",
|
||||
"testResultUnknown": "{{action}}",
|
||||
"contributeBannerTitle": "构建了实用的护栏?",
|
||||
"contributeBannerDesc": "分享给社区,帮助他人更快构建",
|
||||
"contributeButton": "贡献模板",
|
||||
"availablePrimitivesTitle": "可用原语",
|
||||
"availablePrimitivesDesc": "点击将函数复制到剪贴板",
|
||||
"copied": "已复制!",
|
||||
"autoSaveNotice": "更改已自动保存为本地草稿",
|
||||
"updateButton": "更新护栏",
|
||||
"saveButton": "保存护栏",
|
||||
"noGuardrailName": "请输入护栏名称",
|
||||
"noCustomCode": "请输入自定义代码",
|
||||
"noAccessToken": "无可用访问令牌",
|
||||
"updateSuccess": "自定义代码护栏已成功更新",
|
||||
"createSuccess": "自定义代码护栏已成功创建",
|
||||
"saveFailed": "{{action}}护栏失败:{{error}}",
|
||||
"invalidTestJson": "测试输入 JSON 格式无效",
|
||||
"unknownError": "发生未知错误",
|
||||
"primAllowDesc": "放行请求/响应",
|
||||
"primBlockDesc": "拒绝并返回消息",
|
||||
"primModifyDesc": "转换内容",
|
||||
"primHttpRequestDesc": "发起异步 HTTP 请求",
|
||||
"primHttpGetDesc": "异步 GET 请求",
|
||||
"primHttpPostDesc": "异步 POST 请求",
|
||||
"primRegexMatchDesc": "若找到匹配则返回 True",
|
||||
"primRegexReplaceDesc": "替换所有匹配项",
|
||||
"primRegexFindAllDesc": "返回所有匹配项列表",
|
||||
"primJsonParseDesc": "解析 JSON 字符串,出错时返回 None",
|
||||
"primJsonStringifyDesc": "转换为 JSON 字符串",
|
||||
"primJsonSchemaValidDesc": "根据 JSON schema 验证",
|
||||
"primExtractUrlsDesc": "从文本中提取所有 URL",
|
||||
"primIsValidUrlDesc": "检查 URL 是否有效",
|
||||
"primAllUrlsValidDesc": "检查文本中所有 URL 是否有效",
|
||||
"primDetectCodeDesc": "若检测到代码则返回 True",
|
||||
"primDetectCodeLangsDesc": "返回检测到的语言列表",
|
||||
"primContainsCodeLangDesc": "检查特定语言",
|
||||
"primContainsDesc": "检查子字符串是否存在",
|
||||
"primContainsAnyDesc": "检查是否存在任意子字符串",
|
||||
"primWordCountDesc": "统计单词数",
|
||||
"primCharCountDesc": "统计字符数",
|
||||
"primStringTransformsDesc": "字符串转换"
|
||||
},
|
||||
"editGuardrailForm": {
|
||||
"modalTitle": "编辑护栏",
|
||||
"guardrailNameLabel": "护栏名称",
|
||||
"guardrailNameRequired": "请输入护栏名称",
|
||||
"guardrailNamePlaceholder": "为此护栏输入名称",
|
||||
"providerLabel": "护栏提供商",
|
||||
"providerRequired": "请选择提供商",
|
||||
"providerPlaceholder": "选择护栏提供商",
|
||||
"modeLabel": "模式",
|
||||
"modeTooltip": "护栏的应用方式",
|
||||
"modeRequired": "请选择模式",
|
||||
"alwaysOnLabel": "始终开启",
|
||||
"alwaysOnTooltip": "启用后,此护栏将默认应用于所有请求",
|
||||
"skipSystemMsgLabel": "在护栏中跳过系统消息",
|
||||
"skipSystemMsgTooltip": "仅统一护栏:是否从护栏输入中省略 role: system 内容(LLM 仍接收完整消息)。使用全局默认值遵循 litellm_settings.skip_system_message_in_guardrail。",
|
||||
"skipToolMsgLabel": "在护栏中跳过工具消息",
|
||||
"skipToolMsgTooltip": "仅统一护栏:是否从护栏输入中省略 role: tool 内容(LLM 仍接收完整消息)。使用全局默认值遵循 litellm_settings.skip_tool_message_in_guardrail。",
|
||||
"useGlobalDefault": "使用全局默认值",
|
||||
"yesExcludeFromScan": "是 — 从护栏扫描中排除",
|
||||
"noAlwaysInclude": "否 — 始终包含在扫描中",
|
||||
"updateButton": "更新护栏",
|
||||
"aporiaConfigLabel": "Aporia 配置",
|
||||
"aporiaConfigTooltip": "Aporia 的 JSON 配置",
|
||||
"aimConfigLabel": "Aim Security 配置",
|
||||
"aimConfigTooltip": "Aim Security 的 JSON 配置",
|
||||
"bedrockConfigLabel": "Amazon Bedrock 配置",
|
||||
"bedrockConfigTooltip": "Amazon Bedrock 护栏的 JSON 配置",
|
||||
"catoConfigLabel": "Cato Networks 配置",
|
||||
"catoConfigTooltip": "Cato Networks 的 JSON 配置",
|
||||
"guardrailsAiConfigLabel": "Guardrails.ai 配置",
|
||||
"guardrailsAiConfigTooltip": "Guardrails.ai 的 JSON 配置",
|
||||
"lakeraConfigLabel": "Lakera AI 配置",
|
||||
"lakeraConfigTooltip": "Lakera AI 的 JSON 配置",
|
||||
"promptInjectionConfigLabel": "提示词注入配置",
|
||||
"promptInjectionConfigTooltip": "提示词注入检测的 JSON 配置",
|
||||
"customConfigLabel": "自定义配置",
|
||||
"customConfigTooltip": "自定义护栏的 JSON 配置",
|
||||
"updateSuccess": "护栏已成功更新",
|
||||
"invalidJsonConfig": "配置中的 JSON 格式无效",
|
||||
"loadSettingsFailed": "加载护栏设置失败",
|
||||
"updateFailed": "更新护栏失败:{{error}}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue