feat(ui): i18n for guardrail garden and info views

This commit is contained in:
吹雪 2026-06-10 18:19:57 +09:00
parent 327f3ad020
commit eccf54a990
6 changed files with 334 additions and 93 deletions

View file

@ -1,6 +1,7 @@
import React, { useState } from "react";
import { Input } from "antd";
import { SearchOutlined, ArrowRightOutlined } from "@ant-design/icons";
import { useTranslation } from "react-i18next";
import { GuardrailCardInfo, ALL_CARDS } from "./guardrail_garden_data";
import GuardrailCard from "./guardrail_garden_card";
import GuardrailDetailView from "./guardrail_garden_detail";
@ -11,6 +12,7 @@ interface GuardrailGardenProps {
}
const GuardrailGarden: React.FC<GuardrailGardenProps> = ({ accessToken, onGuardrailCreated }) => {
const { t } = useTranslation();
const [searchQuery, setSearchQuery] = useState("");
const [selectedCard, setSelectedCard] = useState<GuardrailCardInfo | null>(null);
const [showAllLitellm, setShowAllLitellm] = useState(false);
@ -47,7 +49,7 @@ const GuardrailGarden: React.FC<GuardrailGardenProps> = ({ accessToken, onGuardr
<div style={{ marginBottom: 24 }}>
<Input
size="large"
placeholder="Search guardrails"
placeholder={t("guardrails.guardrailGarden.searchPlaceholder")}
prefix={<SearchOutlined style={{ color: "#9ca3af" }} />}
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
@ -58,7 +60,9 @@ const GuardrailGarden: React.FC<GuardrailGardenProps> = ({ accessToken, onGuardr
{/* LiteLLM Content Filter Section */}
<div style={{ marginBottom: 40 }}>
<div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 4 }}>
<h2 style={{ fontSize: 20, fontWeight: 600, color: "#111827", margin: 0 }}>LiteLLM Content Filter</h2>
<h2 style={{ fontSize: 20, fontWeight: 600, color: "#111827", margin: 0 }}>
{t("guardrails.guardrailGarden.litellmSectionTitle")}
</h2>
<span
style={{
display: "inline-flex",
@ -71,17 +75,17 @@ const GuardrailGarden: React.FC<GuardrailGardenProps> = ({ accessToken, onGuardr
onClick={() => setShowAllLitellm(!showAllLitellm)}
>
{showAllLitellm ? (
<>Show less</>
<>{t("guardrails.guardrailGarden.showLess")}</>
) : (
<>
<ArrowRightOutlined style={{ fontSize: 12 }} />
{`Show all (${litellmCards.length})`}
{t("guardrails.guardrailGarden.showAll", { count: litellmCards.length })}
</>
)}
</span>
</div>
<p style={{ fontSize: 13, color: "#6b7280", margin: "4px 0 20px 0" }}>
Built-in guardrails powered by LiteLLM. Zero latency, no external dependencies, no additional cost.
{t("guardrails.guardrailGarden.litellmSectionDesc")}
</p>
<div
style={{
@ -98,9 +102,11 @@ const GuardrailGarden: React.FC<GuardrailGardenProps> = ({ accessToken, onGuardr
{/* Partner Guardrails Section */}
<div style={{ marginBottom: 40 }}>
<h2 style={{ fontSize: 20, fontWeight: 600, color: "#111827", margin: "0 0 4px 0" }}>Partner Guardrails</h2>
<h2 style={{ fontSize: 20, fontWeight: 600, color: "#111827", margin: "0 0 4px 0" }}>
{t("guardrails.guardrailGarden.partnerSectionTitle")}
</h2>
<p style={{ fontSize: 13, color: "#6b7280", margin: "4px 0 20px 0" }}>
Third-party guardrail integrations from leading AI security providers.
{t("guardrails.guardrailGarden.partnerSectionDesc")}
</p>
<div
style={{

View file

@ -1,5 +1,6 @@
import React, { useState } from "react";
import { CheckCircleFilled } from "@ant-design/icons";
import { useTranslation } from "react-i18next";
import { GuardrailCardInfo } from "./guardrail_garden_data";
const LogoWithFallback: React.FC<{ src: string; name: string }> = ({ src, name }) => {
@ -38,6 +39,7 @@ const LogoWithFallback: React.FC<{ src: string; name: string }> = ({ src, name }
};
const GuardrailCard: React.FC<{ card: GuardrailCardInfo; onClick: () => void }> = ({ card, onClick }) => {
const { t } = useTranslation();
const [hovered, setHovered] = useState(false);
return (
@ -74,7 +76,7 @@ const GuardrailCard: React.FC<{ card: GuardrailCardInfo; onClick: () => void }>
<div style={{ marginTop: 10, display: "flex", alignItems: "center", gap: 4 }}>
<CheckCircleFilled style={{ color: "#16a34a", fontSize: 12 }} />
<span style={{ fontSize: 11, color: "#16a34a", fontWeight: 500 }}>
F1: {card.eval.f1}% &middot; {card.eval.testCases} test cases
{t("guardrails.guardrailGardenCard.evalBadge", { f1: card.eval.f1, testCases: card.eval.testCases })}
</span>
</div>
)}

View file

@ -1,6 +1,7 @@
import React, { useState } from "react";
import { Button } from "antd";
import { ArrowLeftOutlined } from "@ant-design/icons";
import { useTranslation } from "react-i18next";
import AddGuardrailForm from "./add_guardrail_form";
import { GUARDRAIL_PRESETS } from "./guardrail_garden_configs";
import { GuardrailCardInfo } from "./guardrail_garden_data";
@ -13,30 +14,53 @@ interface GuardrailDetailViewProps {
}
const GuardrailDetailView: React.FC<GuardrailDetailViewProps> = ({ card, onBack, accessToken, onGuardrailCreated }) => {
const { t } = useTranslation();
const [isAddFormVisible, setIsAddFormVisible] = useState(false);
const [activeTab, setActiveTab] = useState("overview");
const detailRows = [
{ property: "Provider", value: card.category === "litellm" ? "LiteLLM Content Filter" : "Partner Guardrail" },
...(card.subcategory ? [{ property: "Subcategory", value: card.subcategory }] : []),
...(card.category === "litellm" ? [{ property: "Cost", value: "$0 / request" }] : []),
...(card.category === "litellm" ? [{ property: "External Dependencies", value: "None" }] : []),
...(card.category === "litellm" ? [{ property: "Latency", value: card.eval?.latency || "<1ms" }] : []),
{
property: t("guardrails.guardrailGardenDetail.propertyProvider"),
value:
card.category === "litellm"
? t("guardrails.guardrailGardenDetail.litellmContentFilter")
: t("guardrails.guardrailGardenDetail.partnerGuardrail"),
},
...(card.subcategory
? [{ property: t("guardrails.guardrailGardenDetail.propertySubcategory"), value: card.subcategory }]
: []),
...(card.category === "litellm"
? [
{
property: t("guardrails.guardrailGardenDetail.propertyCost"),
value: t("guardrails.guardrailGardenDetail.costFree"),
},
]
: []),
...(card.category === "litellm"
? [{ property: t("guardrails.guardrailGardenDetail.propertyExternalDeps"), value: t("common.none") }]
: []),
...(card.category === "litellm"
? [{ property: t("guardrails.guardrailGardenDetail.propertyLatency"), value: card.eval?.latency || "<1ms" }]
: []),
];
const evalRows = card.eval
? [
{ metric: "Precision", value: `${card.eval.precision}%` },
{ metric: "Recall", value: `${card.eval.recall}%` },
{ metric: "F1 Score", value: `${card.eval.f1}%` },
{ metric: "Test Cases", value: String(card.eval.testCases) },
{ metric: "False Positives", value: "0" },
{ metric: "False Negatives", value: "0" },
{ metric: "Latency (p50)", value: card.eval.latency },
{ metric: t("guardrails.guardrailGardenDetail.metricPrecision"), value: `${card.eval.precision}%` },
{ metric: t("guardrails.guardrailGardenDetail.metricRecall"), value: `${card.eval.recall}%` },
{ metric: t("guardrails.guardrailGardenDetail.metricF1Score"), value: `${card.eval.f1}%` },
{ metric: t("guardrails.guardrailGardenDetail.metricTestCases"), value: String(card.eval.testCases) },
{ metric: t("guardrails.guardrailGardenDetail.metricFalsePositives"), value: "0" },
{ metric: t("guardrails.guardrailGardenDetail.metricFalseNegatives"), value: "0" },
{ metric: t("guardrails.guardrailGardenDetail.metricLatencyP50"), value: card.eval.latency },
]
: [];
const tabs = [{ key: "overview", label: "Overview" }, ...(card.eval ? [{ key: "eval", label: "Eval Results" }] : [])];
const tabs = [
{ key: "overview", label: t("guardrails.guardrailGardenDetail.tabOverview") },
...(card.eval ? [{ key: "eval", label: t("guardrails.guardrailGardenDetail.tabEval") }] : []),
];
return (
<div style={{ maxWidth: 960, margin: "0 auto" }}>
@ -86,7 +110,7 @@ const GuardrailDetailView: React.FC<GuardrailDetailViewProps> = ({ card, onBack,
fontSize: 14,
}}
>
Create Guardrail
{t("guardrails.guardrailGardenDetail.createGuardrail")}
</Button>
</div>
@ -118,17 +142,23 @@ const GuardrailDetailView: React.FC<GuardrailDetailViewProps> = ({ card, onBack,
<div style={{ display: "flex", gap: 64 }}>
{/* Left column — overview + details table */}
<div style={{ flex: 1, minWidth: 0 }}>
<h2 style={{ fontSize: 18, fontWeight: 400, color: "#202124", margin: "0 0 12px 0" }}>Overview</h2>
<h2 style={{ fontSize: 18, fontWeight: 400, color: "#202124", margin: "0 0 12px 0" }}>
{t("guardrails.guardrailGardenDetail.tabOverview")}
</h2>
<p style={{ fontSize: 14, color: "#3c4043", lineHeight: 1.7, margin: "0 0 32px 0" }}>{card.description}</p>
<h2 style={{ fontSize: 18, fontWeight: 400, color: "#202124", margin: "0 0 4px 0" }}>Guardrail Details</h2>
<p style={{ fontSize: 13, color: "#5f6368", margin: "0 0 16px 0" }}>Details are as follows</p>
<h2 style={{ fontSize: 18, fontWeight: 400, color: "#202124", margin: "0 0 4px 0" }}>
{t("guardrails.guardrailGardenDetail.guardrailDetails")}
</h2>
<p style={{ fontSize: 13, color: "#5f6368", margin: "0 0 16px 0" }}>
{t("guardrails.guardrailGardenDetail.detailsSubtitle")}
</p>
<table style={{ width: "100%", borderCollapse: "collapse", fontSize: 14 }}>
<thead>
<tr style={{ borderBottom: "1px solid #dadce0" }}>
<th style={{ textAlign: "left", padding: "12px 0", color: "#5f6368", fontWeight: 500, width: 200 }}>
Property
{t("guardrails.guardrailGardenDetail.propertyHeader")}
</th>
<th style={{ textAlign: "left", padding: "12px 0", color: "#5f6368", fontWeight: 500 }}>
{card.name}
@ -150,22 +180,30 @@ const GuardrailDetailView: React.FC<GuardrailDetailViewProps> = ({ card, onBack,
<div style={{ width: 240, flexShrink: 0 }}>
{/* Guardrail ID */}
<div style={{ marginBottom: 28 }}>
<div style={{ fontSize: 12, color: "#5f6368", marginBottom: 4 }}>Guardrail ID</div>
<div style={{ fontSize: 12, color: "#5f6368", marginBottom: 4 }}>
{t("guardrails.guardrailGardenDetail.guardrailId")}
</div>
<div style={{ fontSize: 13, color: "#202124", wordBreak: "break-all" }}>litellm/{card.id}</div>
</div>
{/* Type */}
<div style={{ marginBottom: 28 }}>
<div style={{ fontSize: 12, color: "#5f6368", marginBottom: 4 }}>Type</div>
<div style={{ fontSize: 12, color: "#5f6368", marginBottom: 4 }}>
{t("guardrails.guardrailGardenDetail.typeLabel")}
</div>
<div style={{ fontSize: 13, color: "#202124" }}>
{card.category === "litellm" ? "Content Filter" : "Partner"}
{card.category === "litellm"
? t("guardrails.guardrailGardenDetail.typeContentFilter")
: t("guardrails.guardrailGardenDetail.typePartner")}
</div>
</div>
{/* Tags — pill style like Vertex */}
{card.tags.length > 0 && (
<div style={{ marginBottom: 28 }}>
<div style={{ fontSize: 12, color: "#5f6368", marginBottom: 8 }}>Tags</div>
<div style={{ fontSize: 12, color: "#5f6368", marginBottom: 8 }}>
{t("guardrails.guardrailGardenDetail.tagsLabel")}
</div>
<div style={{ display: "flex", flexWrap: "wrap", gap: 6 }}>
{card.tags.map((tag) => (
<span
@ -191,12 +229,18 @@ const GuardrailDetailView: React.FC<GuardrailDetailViewProps> = ({ card, onBack,
{activeTab === "eval" && (
<div>
<h2 style={{ fontSize: 18, fontWeight: 400, color: "#202124", margin: "0 0 16px 0" }}>Eval Results</h2>
<h2 style={{ fontSize: 18, fontWeight: 400, color: "#202124", margin: "0 0 16px 0" }}>
{t("guardrails.guardrailGardenDetail.tabEval")}
</h2>
<table style={{ width: "100%", maxWidth: 560, borderCollapse: "collapse", fontSize: 14 }}>
<thead>
<tr style={{ backgroundColor: "#f8f9fa", borderBottom: "1px solid #dadce0" }}>
<th style={{ textAlign: "left", padding: "12px 16px", color: "#5f6368", fontWeight: 500 }}>Metric</th>
<th style={{ textAlign: "left", padding: "12px 16px", color: "#5f6368", fontWeight: 500 }}>Value</th>
<th style={{ textAlign: "left", padding: "12px 16px", color: "#5f6368", fontWeight: 500 }}>
{t("guardrails.guardrailGardenDetail.metricHeader")}
</th>
<th style={{ textAlign: "left", padding: "12px 16px", color: "#5f6368", fontWeight: 500 }}>
{t("guardrails.guardrailGardenDetail.valueHeader")}
</th>
</tr>
</thead>
<tbody>

View file

@ -11,6 +11,7 @@ import { Badge, Card, Grid, Tab, TabGroup, TabList, TabPanel, TabPanels, Text, T
import { Button, Divider, Form, Input, Select, Tooltip } from "antd";
import { CheckIcon, CopyIcon } from "lucide-react";
import React, { useCallback, useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import NotificationsManager from "../molecules/notifications_manager";
import ContentFilterManager, { formatContentFilterDataForAPI } from "./content_filter/ContentFilterManager";
import CustomCodeModal, { EditGuardrailData } from "./custom_code/CustomCodeModal";
@ -51,6 +52,7 @@ interface ProviderParamsResponse {
}
const GuardrailInfoView: React.FC<GuardrailInfoProps> = ({ guardrailId, onClose, accessToken, isAdmin }) => {
const { t } = useTranslation();
const [guardrailData, setGuardrailData] = useState<any>(null);
const [guardrailProviderSpecificParams, setGuardrailProviderSpecificParams] = useState<any>(null);
const [loading, setLoading] = useState(true);
@ -162,7 +164,7 @@ const GuardrailInfoView: React.FC<GuardrailInfoProps> = ({ guardrailId, onClose,
setSelectedPiiActions({});
}
} catch (error) {
NotificationsManager.fromBackend("Failed to load guardrail information");
NotificationsManager.fromBackend(t("guardrails.guardrailInfo.loadFailed"));
console.error("Error fetching guardrail info:", error);
} finally {
setLoading(false);
@ -447,28 +449,28 @@ const GuardrailInfoView: React.FC<GuardrailInfoProps> = ({ guardrailId, onClose,
// Only proceed with update if there are actual changes
if (Object.keys(updateData).length === 0) {
NotificationsManager.info("No changes detected");
NotificationsManager.info(t("guardrails.guardrailInfo.noChanges"));
setIsEditing(false);
return;
}
await updateGuardrailCall(accessToken, guardrailId, updateData);
NotificationsManager.success("Guardrail updated successfully");
NotificationsManager.success(t("guardrails.guardrailInfo.updateSuccess"));
setHasUnsavedContentFilterChanges(false);
fetchGuardrailInfo();
setIsEditing(false);
} catch (error) {
console.error("Error updating guardrail:", error);
NotificationsManager.fromBackend("Failed to update guardrail");
NotificationsManager.fromBackend(t("guardrails.guardrailInfo.updateFailed"));
}
};
if (loading) {
return <div className="p-4">Loading...</div>;
return <div className="p-4">{t("common.loading")}</div>;
}
if (!guardrailData) {
return <div className="p-4">Guardrail not found</div>;
return <div className="p-4">{t("guardrails.guardrailInfo.notFound")}</div>;
}
// Format date helper function
@ -497,9 +499,9 @@ const GuardrailInfoView: React.FC<GuardrailInfoProps> = ({ guardrailId, onClose,
<div className="p-4">
<div>
<Button type="text" icon={<ArrowLeftIcon className="w-4 h-4" />} onClick={onClose} className="mb-4">
Back to Guardrails
{t("guardrails.guardrailInfo.backToGuardrails")}
</Button>
<Title>{guardrailData.guardrail_name || "Unnamed Guardrail"}</Title>
<Title>{guardrailData.guardrail_name || t("guardrails.guardrailInfo.unnamedGuardrail")}</Title>
<div className="flex items-center cursor-pointer">
<Text className="text-gray-500 font-mono">{guardrailData.guardrail_id}</Text>
@ -519,8 +521,8 @@ const GuardrailInfoView: React.FC<GuardrailInfoProps> = ({ guardrailId, onClose,
<TabGroup>
<TabList className="mb-4">
<Tab key="overview">Overview</Tab>
{isAdmin ? <Tab key="settings">Settings</Tab> : <></>}
<Tab key="overview">{t("guardrails.guardrailInfo.tabOverview")}</Tab>
{isAdmin ? <Tab key="settings">{t("guardrails.guardrailInfo.tabSettings")}</Tab> : <></>}
</TabList>
<TabPanels>
@ -528,7 +530,7 @@ const GuardrailInfoView: React.FC<GuardrailInfoProps> = ({ guardrailId, onClose,
<TabPanel>
<Grid numItems={1} numItemsSm={2} numItemsLg={3} className="gap-6">
<Card>
<Text>Provider</Text>
<Text>{t("guardrails.guardrailInfo.provider")}</Text>
<div className="mt-2 flex items-center space-x-2">
{logo && (
<img
@ -546,20 +548,24 @@ const GuardrailInfoView: React.FC<GuardrailInfoProps> = ({ guardrailId, onClose,
</Card>
<Card>
<Text>Mode</Text>
<Text>{t("guardrails.guardrailInfo.mode")}</Text>
<div className="mt-2">
<Title>{guardrailData.litellm_params?.mode || "-"}</Title>
<Badge color={guardrailData.litellm_params?.default_on ? "green" : "gray"}>
{guardrailData.litellm_params?.default_on ? "Default On" : "Default Off"}
{guardrailData.litellm_params?.default_on
? t("guardrails.guardrailInfo.defaultOn")
: t("guardrails.guardrailInfo.defaultOff")}
</Badge>
</div>
</Card>
<Card>
<Text>Created At</Text>
<Text>{t("common.createdAt")}</Text>
<div className="mt-2">
<Title>{formatDate(guardrailData.created_at)}</Title>
<Text>Last Updated: {formatDate(guardrailData.updated_at)}</Text>
<Text>
{t("guardrails.guardrailInfo.lastUpdated", { date: formatDate(guardrailData.updated_at) })}
</Text>
</div>
</Card>
</Grid>
@ -568,9 +574,11 @@ const GuardrailInfoView: React.FC<GuardrailInfoProps> = ({ guardrailId, onClose,
Object.keys(guardrailData.litellm_params.pii_entities_config).length > 0 && (
<Card className="mt-6">
<div className="flex justify-between items-center">
<Text className="font-medium">PII Protection</Text>
<Text className="font-medium">{t("guardrails.guardrailInfo.piiProtection")}</Text>
<Badge color="blue">
{Object.keys(guardrailData.litellm_params.pii_entities_config).length} PII entities configured
{t("guardrails.guardrailInfo.piiEntitiesConfigured", {
count: Object.keys(guardrailData.litellm_params.pii_entities_config).length,
})}
</Badge>
</div>
</Card>
@ -579,11 +587,17 @@ const GuardrailInfoView: React.FC<GuardrailInfoProps> = ({ guardrailId, onClose,
{guardrailData.litellm_params?.pii_entities_config &&
Object.keys(guardrailData.litellm_params.pii_entities_config).length > 0 && (
<Card className="mt-6">
<Text className="mb-4 text-lg font-semibold">PII Entity Configuration</Text>
<Text className="mb-4 text-lg font-semibold">
{t("guardrails.guardrailInfo.piiEntityConfiguration")}
</Text>
<div className="border rounded-lg overflow-hidden shadow-sm">
<div className="bg-gray-50 px-5 py-3 border-b flex">
<Text className="flex-1 font-semibold text-gray-700">Entity Type</Text>
<Text className="flex-1 font-semibold text-gray-700">Configuration</Text>
<Text className="flex-1 font-semibold text-gray-700">
{t("guardrails.guardrailInfo.entityType")}
</Text>
<Text className="flex-1 font-semibold text-gray-700">
{t("guardrails.guardrailInfo.configuration")}
</Text>
</div>
<div className="max-h-[400px] overflow-y-auto">
{Object.entries(guardrailData.litellm_params?.pii_entities_config).map(([key, value]) => (
@ -618,11 +632,11 @@ const GuardrailInfoView: React.FC<GuardrailInfoProps> = ({ guardrailId, onClose,
<div className="flex justify-between items-center mb-4">
<div className="flex items-center gap-2">
<CodeOutlined className="text-blue-500" />
<Text className="font-medium text-lg">Custom Code</Text>
<Text className="font-medium text-lg">{t("guardrails.guardrailInfo.customCode")}</Text>
</div>
{isAdmin && !isConfigGuardrail && (
<Button size="small" icon={<CodeOutlined />} onClick={() => setCustomCodeModalVisible(true)}>
Edit Code
{t("guardrails.guardrailInfo.editCode")}
</Button>
)}
</div>
@ -651,9 +665,9 @@ const GuardrailInfoView: React.FC<GuardrailInfoProps> = ({ guardrailId, onClose,
<TabPanel>
<Card>
<div className="flex justify-between items-center mb-4">
<Title>Guardrail Settings</Title>
<Title>{t("guardrails.guardrailInfo.guardrailSettings")}</Title>
{isConfigGuardrail && (
<Tooltip title="Guardrail is defined in the config file and cannot be edited.">
<Tooltip title={t("guardrails.guardrailInfo.configGuardrailTooltip")}>
<InfoCircleOutlined />
</Tooltip>
)}
@ -661,10 +675,10 @@ const GuardrailInfoView: React.FC<GuardrailInfoProps> = ({ guardrailId, onClose,
!isConfigGuardrail &&
(guardrailData.litellm_params?.guardrail === "custom_code" ? (
<Button icon={<CodeOutlined />} onClick={() => setCustomCodeModalVisible(true)}>
Edit Code
{t("guardrails.guardrailInfo.editCode")}
</Button>
) : (
<Button onClick={() => setIsEditing(true)}>Edit Settings</Button>
<Button onClick={() => setIsEditing(true)}>{t("guardrails.guardrailInfo.editSettings")}</Button>
))}
</div>
@ -697,47 +711,47 @@ const GuardrailInfoView: React.FC<GuardrailInfoProps> = ({ guardrailId, onClose,
layout="vertical"
>
<Form.Item
label="Guardrail Name"
label={t("guardrails.guardrailInfo.guardrailName")}
name="guardrail_name"
rules={[{ required: true, message: "Please input a guardrail name" }]}
rules={[{ required: true, message: t("guardrails.guardrailInfo.guardrailNameRequired") }]}
>
<Input placeholder="Enter guardrail name" />
<Input placeholder={t("guardrails.guardrailInfo.guardrailNamePlaceholder")} />
</Form.Item>
<Form.Item label="Default On" name="default_on">
<Form.Item label={t("guardrails.guardrailInfo.defaultOnLabel")} name="default_on">
<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
label="Skip system messages in guardrail"
label={t("guardrails.guardrailInfo.skipSystemMsg")}
name="skip_system_message_choice"
tooltip="Unified guardrails: omit role: system from guardrail input (LLM still gets full messages). Use global default follows litellm_settings.skip_system_message_in_guardrail."
tooltip={t("guardrails.guardrailInfo.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.guardrailInfo.useGlobalDefault")}</Select.Option>
<Select.Option value="yes">{t("guardrails.guardrailInfo.yesExcludeFromScan")}</Select.Option>
<Select.Option value="no">{t("guardrails.guardrailInfo.noAlwaysInclude")}</Select.Option>
</Select>
</Form.Item>
<Form.Item
label="Skip tool messages in guardrail"
label={t("guardrails.guardrailInfo.skipToolMsg")}
name="skip_tool_message_choice"
tooltip="Unified guardrails: omit role: tool from guardrail input (LLM still gets full messages). Use global default follows litellm_settings.skip_tool_message_in_guardrail."
tooltip={t("guardrails.guardrailInfo.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.guardrailInfo.useGlobalDefault")}</Select.Option>
<Select.Option value="yes">{t("guardrails.guardrailInfo.yesExcludeFromScan")}</Select.Option>
<Select.Option value="no">{t("guardrails.guardrailInfo.noAlwaysInclude")}</Select.Option>
</Select>
</Form.Item>
{guardrailData.litellm_params?.guardrail === "presidio" && (
<>
<Divider orientation="left">PII Protection</Divider>
<Divider orientation="left">{t("guardrails.guardrailInfo.piiProtection")}</Divider>
<div className="mb-6">
{guardrailSettings && (
<PiiConfiguration
@ -764,7 +778,9 @@ const GuardrailInfoView: React.FC<GuardrailInfoProps> = ({ guardrailId, onClose,
/>
{(guardrailData.litellm_params?.guardrail === "tool_permission" ||
guardrailProviderSpecificParams) && <Divider orientation="left">Provider Settings</Divider>}
guardrailProviderSpecificParams) && (
<Divider orientation="left">{t("guardrails.guardrailInfo.providerSettings")}</Divider>
)}
{guardrailData.litellm_params?.guardrail === "tool_permission" ? (
<ToolPermissionRulesEditor value={toolPermissionConfig} onChange={setToolPermissionConfig} />
@ -806,8 +822,8 @@ const GuardrailInfoView: React.FC<GuardrailInfoProps> = ({ guardrailId, onClose,
</>
)}
<Divider orientation="left">Advanced Settings</Divider>
<Form.Item label="Guardrail Information" name="guardrail_info">
<Divider orientation="left">{t("guardrails.guardrailInfo.advancedSettings")}</Divider>
<Form.Item label={t("guardrails.guardrailInfo.guardrailInformation")} name="guardrail_info">
<Input.TextArea rows={5} />
</Form.Item>
@ -819,57 +835,58 @@ const GuardrailInfoView: React.FC<GuardrailInfoProps> = ({ guardrailId, onClose,
resetToolPermissionEditor();
}}
>
Cancel
{t("common.cancel")}
</Button>
<Button type="primary" htmlType="submit">
Save Changes
{t("guardrails.guardrailInfo.saveChanges")}
</Button>
</div>
</Form>
) : (
<div className="space-y-4">
<div>
<Text className="font-medium">Guardrail ID</Text>
<Text className="font-medium">{t("guardrails.guardrailInfo.guardrailIdLabel")}</Text>
<div className="font-mono">{guardrailData.guardrail_id}</div>
</div>
<div>
<Text className="font-medium">Guardrail Name</Text>
<div>{guardrailData.guardrail_name || "Unnamed Guardrail"}</div>
<Text className="font-medium">{t("guardrails.guardrailInfo.guardrailName")}</Text>
<div>{guardrailData.guardrail_name || t("guardrails.guardrailInfo.unnamedGuardrail")}</div>
</div>
<div>
<Text className="font-medium">Provider</Text>
<Text className="font-medium">{t("guardrails.guardrailInfo.provider")}</Text>
<div>{displayName}</div>
</div>
<div>
<Text className="font-medium">Mode</Text>
<Text className="font-medium">{t("guardrails.guardrailInfo.mode")}</Text>
<div>{guardrailData.litellm_params?.mode || "-"}</div>
</div>
<div>
<Text className="font-medium">Default On</Text>
<Text className="font-medium">{t("guardrails.guardrailInfo.defaultOnLabel")}</Text>
<Badge color={guardrailData.litellm_params?.default_on ? "green" : "gray"}>
{guardrailData.litellm_params?.default_on ? "Yes" : "No"}
{guardrailData.litellm_params?.default_on ? t("common.yes") : t("common.no")}
</Badge>
</div>
{guardrailData.litellm_params?.pii_entities_config &&
Object.keys(guardrailData.litellm_params.pii_entities_config).length > 0 && (
<div>
<Text className="font-medium">PII Protection</Text>
<Text className="font-medium">{t("guardrails.guardrailInfo.piiProtection")}</Text>
<div className="mt-2">
<Badge color="blue">
{Object.keys(guardrailData.litellm_params.pii_entities_config).length} PII entities
configured
{t("guardrails.guardrailInfo.piiEntitiesConfigured", {
count: Object.keys(guardrailData.litellm_params.pii_entities_config).length,
})}
</Badge>
</div>
</div>
)}
<div>
<Text className="font-medium">Created At</Text>
<Text className="font-medium">{t("common.createdAt")}</Text>
<div>{formatDate(guardrailData.created_at)}</div>
</div>
<div>
<Text className="font-medium">Last Updated</Text>
<Text className="font-medium">{t("guardrails.guardrailInfo.lastUpdatedLabel")}</Text>
<div>{formatDate(guardrailData.updated_at)}</div>
</div>

View file

@ -2862,6 +2862,92 @@
"invalidJsonConfig": "Invalid JSON in configuration",
"loadSettingsFailed": "Failed to load guardrail settings",
"updateFailed": "Failed to update guardrail: {{error}}"
},
"guardrailGarden": {
"searchPlaceholder": "Search guardrails",
"litellmSectionTitle": "LiteLLM Content Filter",
"showLess": "Show less",
"showAll": "Show all ({{count}})",
"litellmSectionDesc": "Built-in guardrails powered by LiteLLM. Zero latency, no external dependencies, no additional cost.",
"partnerSectionTitle": "Partner Guardrails",
"partnerSectionDesc": "Third-party guardrail integrations from leading AI security providers."
},
"guardrailGardenCard": {
"evalBadge": "F1: {{f1}}% · {{testCases}} test cases"
},
"guardrailGardenDetail": {
"propertyProvider": "Provider",
"litellmContentFilter": "LiteLLM Content Filter",
"partnerGuardrail": "Partner Guardrail",
"propertySubcategory": "Subcategory",
"propertyCost": "Cost",
"costFree": "$0 / request",
"propertyExternalDeps": "External Dependencies",
"propertyLatency": "Latency",
"metricPrecision": "Precision",
"metricRecall": "Recall",
"metricF1Score": "F1 Score",
"metricTestCases": "Test Cases",
"metricFalsePositives": "False Positives",
"metricFalseNegatives": "False Negatives",
"metricLatencyP50": "Latency (p50)",
"tabOverview": "Overview",
"tabEval": "Eval Results",
"createGuardrail": "Create Guardrail",
"guardrailDetails": "Guardrail Details",
"detailsSubtitle": "Details are as follows",
"propertyHeader": "Property",
"guardrailId": "Guardrail ID",
"typeLabel": "Type",
"typeContentFilter": "Content Filter",
"typePartner": "Partner",
"tagsLabel": "Tags",
"metricHeader": "Metric",
"valueHeader": "Value"
},
"guardrailInfo": {
"backToGuardrails": "Back to Guardrails",
"unnamedGuardrail": "Unnamed Guardrail",
"tabOverview": "Overview",
"tabSettings": "Settings",
"provider": "Provider",
"mode": "Mode",
"defaultOn": "Default On",
"defaultOff": "Default Off",
"lastUpdated": "Last Updated: {{date}}",
"piiProtection": "PII Protection",
"piiEntitiesConfigured_one": "{{count}} PII entity configured",
"piiEntitiesConfigured_other": "{{count}} PII entities configured",
"piiEntityConfiguration": "PII Entity Configuration",
"entityType": "Entity Type",
"configuration": "Configuration",
"customCode": "Custom Code",
"editCode": "Edit Code",
"guardrailSettings": "Guardrail Settings",
"configGuardrailTooltip": "Guardrail is defined in the config file and cannot be edited.",
"editSettings": "Edit Settings",
"guardrailName": "Guardrail Name",
"guardrailNameRequired": "Please input a guardrail name",
"guardrailNamePlaceholder": "Enter guardrail name",
"defaultOnLabel": "Default On",
"useGlobalDefault": "Use global default",
"yesExcludeFromScan": "Yes — exclude from guardrail scan",
"noAlwaysInclude": "No — always include in scan",
"skipSystemMsg": "Skip system messages in guardrail",
"skipSystemMsgTooltip": "Unified guardrails: omit role: system from guardrail input (LLM still gets full messages). Use global default follows litellm_settings.skip_system_message_in_guardrail.",
"skipToolMsg": "Skip tool messages in guardrail",
"skipToolMsgTooltip": "Unified guardrails: omit role: tool from guardrail input (LLM still gets full messages). Use global default follows litellm_settings.skip_tool_message_in_guardrail.",
"providerSettings": "Provider Settings",
"advancedSettings": "Advanced Settings",
"guardrailInformation": "Guardrail Information",
"saveChanges": "Save Changes",
"guardrailIdLabel": "Guardrail ID",
"lastUpdatedLabel": "Last Updated",
"loadFailed": "Failed to load guardrail information",
"noChanges": "No changes detected",
"updateSuccess": "Guardrail updated successfully",
"updateFailed": "Failed to update guardrail",
"notFound": "Guardrail not found"
}
}
}

View file

@ -2862,6 +2862,92 @@
"invalidJsonConfig": "配置中的 JSON 格式无效",
"loadSettingsFailed": "加载护栏设置失败",
"updateFailed": "更新护栏失败:{{error}}"
},
"guardrailGarden": {
"searchPlaceholder": "搜索护栏",
"litellmSectionTitle": "LiteLLM 内容过滤器",
"showLess": "收起",
"showAll": "显示全部({{count}})",
"litellmSectionDesc": "由 LiteLLM 内置的护栏,零延迟,无外部依赖,无额外费用。",
"partnerSectionTitle": "合作伙伴护栏",
"partnerSectionDesc": "来自领先 AI 安全提供商的第三方护栏集成。"
},
"guardrailGardenCard": {
"evalBadge": "F1:{{f1}}% · {{testCases}} 个测试用例"
},
"guardrailGardenDetail": {
"propertyProvider": "提供商",
"litellmContentFilter": "LiteLLM 内容过滤器",
"partnerGuardrail": "合作伙伴护栏",
"propertySubcategory": "子类别",
"propertyCost": "费用",
"costFree": "$0 / 次请求",
"propertyExternalDeps": "外部依赖",
"propertyLatency": "延迟",
"metricPrecision": "精确率",
"metricRecall": "召回率",
"metricF1Score": "F1 分数",
"metricTestCases": "测试用例",
"metricFalsePositives": "误报",
"metricFalseNegatives": "漏报",
"metricLatencyP50": "延迟(p50)",
"tabOverview": "概览",
"tabEval": "评估结果",
"createGuardrail": "创建护栏",
"guardrailDetails": "护栏详情",
"detailsSubtitle": "详细信息如下",
"propertyHeader": "属性",
"guardrailId": "护栏 ID",
"typeLabel": "类型",
"typeContentFilter": "内容过滤器",
"typePartner": "合作伙伴",
"tagsLabel": "标签",
"metricHeader": "指标",
"valueHeader": "值"
},
"guardrailInfo": {
"backToGuardrails": "返回护栏列表",
"unnamedGuardrail": "未命名护栏",
"tabOverview": "概览",
"tabSettings": "设置",
"provider": "提供商",
"mode": "模式",
"defaultOn": "默认启用",
"defaultOff": "默认关闭",
"lastUpdated": "最近更新:{{date}}",
"piiProtection": "PII 保护",
"piiEntitiesConfigured_one": "已配置 {{count}} 个 PII 实体",
"piiEntitiesConfigured_other": "已配置 {{count}} 个 PII 实体",
"piiEntityConfiguration": "PII 实体配置",
"entityType": "实体类型",
"configuration": "配置",
"customCode": "自定义代码",
"editCode": "编辑代码",
"guardrailSettings": "护栏设置",
"configGuardrailTooltip": "护栏在配置文件中定义,无法编辑。",
"editSettings": "编辑设置",
"guardrailName": "护栏名称",
"guardrailNameRequired": "请输入护栏名称",
"guardrailNamePlaceholder": "输入护栏名称",
"defaultOnLabel": "默认启用",
"useGlobalDefault": "使用全局默认",
"yesExcludeFromScan": "是 — 从护栏扫描中排除",
"noAlwaysInclude": "否 — 始终包含在扫描中",
"skipSystemMsg": "在护栏中跳过系统消息",
"skipSystemMsgTooltip": "统一护栏:从护栏输入中省略 role: system(LLM 仍接收完整消息)。使用全局默认值遵循 litellm_settings.skip_system_message_in_guardrail。",
"skipToolMsg": "在护栏中跳过工具消息",
"skipToolMsgTooltip": "统一护栏:从护栏输入中省略 role: tool(LLM 仍接收完整消息)。使用全局默认值遵循 litellm_settings.skip_tool_message_in_guardrail。",
"providerSettings": "提供商设置",
"advancedSettings": "高级设置",
"guardrailInformation": "护栏信息",
"saveChanges": "保存更改",
"guardrailIdLabel": "护栏 ID",
"lastUpdatedLabel": "最近更新",
"loadFailed": "加载护栏信息失败",
"noChanges": "未检测到更改",
"updateSuccess": "护栏已成功更新",
"updateFailed": "更新护栏失败",
"notFound": "未找到护栏"
}
}
}