feat(ui): Guardrail Garden - guardrail marketplace (#21732)

* feat(ui): add Guardrail Garden page with Vertex-style card layout

* feat(ui): add guardrail garden preset configs for form pre-fill

* feat(ui): add Guardrail Garden as first tab on guardrails page

* feat(ui): support preset prop in AddGuardrailForm for garden pre-fill

* fix(ui): fix merge syntax error in ComplianceUI

* refactor(ui): split guardrail_garden.tsx into focused modules
This commit is contained in:
Ishaan Jaff
2026-02-20 19:47:04 -08:00
committed by GitHub
parent 0888e17272
commit 8e2aeeae0b
7 changed files with 1074 additions and 3 deletions
@@ -13,6 +13,7 @@ import { Guardrail, GuardrailDefinitionLocation } from "./guardrails/types";
import DeleteResourceModal from "./common_components/DeleteResourceModal";
import { getGuardrailLogoAndName } from "./guardrails/guardrail_info_helpers";
import { CustomCodeModal } from "./guardrails/custom_code";
import GuardrailGarden from "./guardrails/guardrail_garden";
interface GuardrailsPanelProps {
accessToken: string | null;
@@ -106,12 +107,11 @@ const GuardrailsPanel: React.FC<GuardrailsPanelProps> = ({ accessToken, userRole
const handleDeleteConfirm = async () => {
if (!guardrailToDelete || !accessToken) return;
// Log removed to maintain clean production code
setIsDeleting(true);
try {
await deleteGuardrailCall(accessToken, guardrailToDelete.guardrail_id);
NotificationsManager.success(`Guardrail "${guardrailToDelete.guardrail_name}" deleted successfully`);
await fetchGuardrails(); // Refresh the list
await fetchGuardrails();
} catch (error) {
console.error("Error deleting guardrail:", error);
NotificationsManager.fromBackend("Failed to delete guardrail");
@@ -136,11 +136,21 @@ const GuardrailsPanel: React.FC<GuardrailsPanelProps> = ({ accessToken, userRole
<div className="w-full mx-auto flex-auto overflow-y-auto m-8 p-2">
<TabGroup index={activeTab} onIndexChange={setActiveTab}>
<TabList className="mb-4">
<Tab>Guardrail Garden</Tab>
<Tab>Guardrails</Tab>
<Tab disabled={!accessToken || guardrailsList.length === 0}>Test Playground</Tab>
</TabList>
<TabPanels>
{/* Guardrail Garden Tab */}
<TabPanel>
<GuardrailGarden
accessToken={accessToken}
onGuardrailCreated={handleSuccess}
/>
</TabPanel>
{/* Existing Guardrails Tab */}
<TabPanel>
<div className="flex justify-between items-center mb-4">
<Dropdown
@@ -223,6 +233,7 @@ const GuardrailsPanel: React.FC<GuardrailsPanelProps> = ({ accessToken, userRole
/>
</TabPanel>
{/* Test Playground Tab */}
<TabPanel>
<GuardrailTestPlayground
guardrailsList={guardrailsList}
@@ -33,11 +33,20 @@ const modeDescriptions = {
during_mcp_call: "During MCP Tool Call - Runs in parallel with MCP tool execution for monitoring",
};
interface GuardrailPreset {
provider: string;
categoryName?: string;
guardrailNameSuggestion: string;
mode: string;
defaultOn: boolean;
}
interface AddGuardrailFormProps {
visible: boolean;
onClose: () => void;
accessToken: string | null;
onSuccess: () => void;
preset?: GuardrailPreset;
}
interface GuardrailSettings {
@@ -90,7 +99,7 @@ interface ProviderParamsResponse {
[provider: string]: { [key: string]: ProviderParam };
}
const AddGuardrailForm: React.FC<AddGuardrailFormProps> = ({ visible, onClose, accessToken, onSuccess }) => {
const AddGuardrailForm: React.FC<AddGuardrailFormProps> = ({ visible, onClose, accessToken, onSuccess, preset }) => {
const [form] = Form.useForm();
const [loading, setLoading] = useState(false);
const [selectedProvider, setSelectedProvider] = useState<string | null>(null);
@@ -154,6 +163,36 @@ const AddGuardrailForm: React.FC<AddGuardrailFormProps> = ({ visible, onClose, a
fetchData();
}, [accessToken]);
// Apply preset when settings are loaded and form becomes visible
useEffect(() => {
if (!preset || !visible || !guardrailSettings) return;
// Set provider
setSelectedProvider(preset.provider);
form.setFieldsValue({
provider: preset.provider,
guardrail_name: preset.guardrailNameSuggestion,
mode: preset.mode,
default_on: preset.defaultOn,
});
// Pre-select content category if specified
if (preset.categoryName && guardrailSettings.content_filter_settings?.content_categories) {
const category = guardrailSettings.content_filter_settings.content_categories.find(
(c: any) => c.name === preset.categoryName
);
if (category) {
setSelectedContentCategories([{
id: `category-${Date.now()}`,
category: category.name,
display_name: category.display_name,
action: category.default_action as "BLOCK" | "MASK",
severity_threshold: "medium",
}]);
}
}
}, [preset, visible, guardrailSettings]);
const handleProviderChange = (value: string) => {
setSelectedProvider(value);
// Reset form fields that are provider-specific
@@ -0,0 +1,110 @@
import React, { useState } from "react";
import { Input } from "antd";
import { SearchOutlined, ArrowRightOutlined } from "@ant-design/icons";
import { GuardrailCardInfo, LITELLM_CONTENT_FILTER_CARDS, PARTNER_GUARDRAIL_CARDS, ALL_CARDS } from "./guardrail_garden_data";
import GuardrailCard from "./guardrail_garden_card";
import GuardrailDetailView from "./guardrail_garden_detail";
interface GuardrailGardenProps {
accessToken: string | null;
onGuardrailCreated: () => void;
}
const GuardrailGarden: React.FC<GuardrailGardenProps> = ({ accessToken, onGuardrailCreated }) => {
const [searchQuery, setSearchQuery] = useState("");
const [selectedCard, setSelectedCard] = useState<GuardrailCardInfo | null>(null);
const [showAllLitellm, setShowAllLitellm] = useState(false);
const CARDS_PER_ROW = 5;
const VISIBLE_ROWS = 2;
const filteredCards = ALL_CARDS.filter((card) => {
if (!searchQuery) return true;
const q = searchQuery.toLowerCase();
return (
card.name.toLowerCase().includes(q) ||
card.description.toLowerCase().includes(q) ||
card.tags.some((t) => t.toLowerCase().includes(q))
);
});
const litellmCards = filteredCards.filter((c) => c.category === "litellm");
const partnerCards = filteredCards.filter((c) => c.category === "partner");
if (selectedCard) {
return (
<GuardrailDetailView
card={selectedCard}
onBack={() => setSelectedCard(null)}
accessToken={accessToken}
onGuardrailCreated={onGuardrailCreated}
/>
);
}
return (
<div>
{/* Search Bar */}
<div style={{ marginBottom: 24 }}>
<Input
size="large"
placeholder="Search guardrails"
prefix={<SearchOutlined style={{ color: "#9ca3af" }} />}
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
style={{ borderRadius: 8 }}
/>
</div>
{/* 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>
<span
style={{ display: "inline-flex", alignItems: "center", gap: 6, fontSize: 14, color: "#1a73e8", cursor: "pointer" }}
onClick={() => setShowAllLitellm(!showAllLitellm)}
>
{showAllLitellm ? (
<>Show less</>
) : (
<>
<ArrowRightOutlined style={{ fontSize: 12 }} />
{`Show all (${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.
</p>
<div style={{
display: "grid",
gridTemplateColumns: "repeat(auto-fill, minmax(220px, 1fr))",
gap: 16,
}}>
{(showAllLitellm ? litellmCards : litellmCards.slice(0, CARDS_PER_ROW * VISIBLE_ROWS)).map((card) => (
<GuardrailCard key={card.id} card={card} onClick={() => setSelectedCard(card)} />
))}
</div>
</div>
{/* Partner Guardrails Section */}
<div style={{ marginBottom: 40 }}>
<h2 style={{ fontSize: 20, fontWeight: 600, color: "#111827", margin: "0 0 4px 0" }}>Partner Guardrails</h2>
<p style={{ fontSize: 13, color: "#6b7280", margin: "4px 0 20px 0" }}>
Third-party guardrail integrations from leading AI security providers.
</p>
<div style={{
display: "grid",
gridTemplateColumns: "repeat(auto-fill, minmax(220px, 1fr))",
gap: 16,
}}>
{partnerCards.map((card) => (
<GuardrailCard key={card.id} card={card} onClick={() => setSelectedCard(card)} />
))}
</div>
</div>
</div>
);
};
export default GuardrailGarden;
@@ -0,0 +1,58 @@
import React, { useState } from "react";
import { CheckCircleFilled } from "@ant-design/icons";
import { GuardrailCardInfo } from "./guardrail_garden_data";
const GuardrailCard: React.FC<{ card: GuardrailCardInfo; onClick: () => void }> = ({ card, onClick }) => {
const [hovered, setHovered] = useState(false);
return (
<div
onClick={onClick}
onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)}
style={{
borderRadius: 12,
border: hovered ? "1px solid #93c5fd" : "1px solid #e5e7eb",
backgroundColor: "#ffffff",
padding: "20px 20px 16px 20px",
cursor: "pointer",
transition: "border-color 0.15s, box-shadow 0.15s",
display: "flex",
flexDirection: "column",
minHeight: 170,
boxShadow: hovered ? "0 1px 6px rgba(59,130,246,0.08)" : "none",
}}
>
{/* Icon + Name row */}
<div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 10 }}>
<img
src={card.logo}
alt=""
style={{ width: 28, height: 28, borderRadius: 6, objectFit: "contain", flexShrink: 0 }}
onError={(e) => { (e.target as HTMLImageElement).style.display = "none"; }}
/>
<span style={{ fontSize: 14, fontWeight: 600, color: "#111827", lineHeight: 1.3 }}>{card.name}</span>
</div>
{/* Description */}
<p
className="line-clamp-3"
style={{ fontSize: 12, color: "#6b7280", lineHeight: 1.6, margin: 0, flex: 1 }}
>
{card.description}
</p>
{/* Eval badge */}
{card.eval && (
<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
</span>
</div>
)}
</div>
);
};
export default GuardrailCard;
@@ -0,0 +1,255 @@
export interface GuardrailPreset {
provider: string;
categoryName?: string;
guardrailNameSuggestion: string;
mode: string;
defaultOn: boolean;
}
export const GUARDRAIL_PRESETS: Record<string, GuardrailPreset> = {
// ── LiteLLM Content Filter: Content Categories ──
cf_denied_financial: {
provider: "LitellmContentFilter",
categoryName: "denied_financial_advice",
guardrailNameSuggestion: "Denied Financial Advice",
mode: "pre_call",
defaultOn: false,
},
cf_denied_legal: {
provider: "LitellmContentFilter",
categoryName: "denied_legal_advice",
guardrailNameSuggestion: "Denied Legal Advice",
mode: "pre_call",
defaultOn: false,
},
cf_denied_medical: {
provider: "LitellmContentFilter",
categoryName: "denied_medical_advice",
guardrailNameSuggestion: "Denied Medical Advice",
mode: "pre_call",
defaultOn: false,
},
cf_denied_insults: {
provider: "LitellmContentFilter",
categoryName: "denied_insults",
guardrailNameSuggestion: "Insults & Personal Attacks",
mode: "pre_call",
defaultOn: false,
},
cf_harmful_violence: {
provider: "LitellmContentFilter",
categoryName: "harmful_violence",
guardrailNameSuggestion: "Harmful Violence",
mode: "pre_call",
defaultOn: false,
},
cf_harmful_self_harm: {
provider: "LitellmContentFilter",
categoryName: "harmful_self_harm",
guardrailNameSuggestion: "Harmful Self-Harm",
mode: "pre_call",
defaultOn: false,
},
cf_harmful_child_safety: {
provider: "LitellmContentFilter",
categoryName: "harmful_child_safety",
guardrailNameSuggestion: "Harmful Child Safety",
mode: "pre_call",
defaultOn: false,
},
cf_harmful_illegal_weapons: {
provider: "LitellmContentFilter",
categoryName: "harmful_illegal_weapons",
guardrailNameSuggestion: "Harmful Illegal Weapons",
mode: "pre_call",
defaultOn: false,
},
cf_bias_gender: {
provider: "LitellmContentFilter",
categoryName: "bias_gender",
guardrailNameSuggestion: "Bias: Gender",
mode: "pre_call",
defaultOn: false,
},
cf_bias_racial: {
provider: "LitellmContentFilter",
categoryName: "bias_racial",
guardrailNameSuggestion: "Bias: Racial",
mode: "pre_call",
defaultOn: false,
},
cf_bias_religious: {
provider: "LitellmContentFilter",
categoryName: "bias_religious",
guardrailNameSuggestion: "Bias: Religious",
mode: "pre_call",
defaultOn: false,
},
cf_bias_sexual_orientation: {
provider: "LitellmContentFilter",
categoryName: "bias_sexual_orientation",
guardrailNameSuggestion: "Bias: Sexual Orientation",
mode: "pre_call",
defaultOn: false,
},
cf_prompt_injection_jailbreak: {
provider: "LitellmContentFilter",
categoryName: "prompt_injection_jailbreak",
guardrailNameSuggestion: "Prompt Injection: Jailbreak",
mode: "pre_call",
defaultOn: false,
},
cf_prompt_injection_data_exfil: {
provider: "LitellmContentFilter",
categoryName: "prompt_injection_data_exfiltration",
guardrailNameSuggestion: "Prompt Injection: Data Exfiltration",
mode: "pre_call",
defaultOn: false,
},
cf_prompt_injection_sql: {
provider: "LitellmContentFilter",
categoryName: "prompt_injection_sql",
guardrailNameSuggestion: "Prompt Injection: SQL",
mode: "pre_call",
defaultOn: false,
},
cf_prompt_injection_malicious_code: {
provider: "LitellmContentFilter",
categoryName: "prompt_injection_malicious_code",
guardrailNameSuggestion: "Prompt Injection: Malicious Code",
mode: "pre_call",
defaultOn: false,
},
cf_prompt_injection_system_prompt: {
provider: "LitellmContentFilter",
categoryName: "prompt_injection_system_prompt",
guardrailNameSuggestion: "Prompt Injection: System Prompt",
mode: "pre_call",
defaultOn: false,
},
cf_toxic_abuse: {
provider: "LitellmContentFilter",
categoryName: "harm_toxic_abuse",
guardrailNameSuggestion: "Toxic & Abusive Language",
mode: "pre_call",
defaultOn: false,
},
// ── LiteLLM Content Filter: Patterns & Keywords (no category) ──
cf_patterns: {
provider: "LitellmContentFilter",
guardrailNameSuggestion: "Pattern Matching",
mode: "pre_call",
defaultOn: false,
},
cf_keywords: {
provider: "LitellmContentFilter",
guardrailNameSuggestion: "Keyword Blocking",
mode: "pre_call",
defaultOn: false,
},
// ── Partner Guardrails ──
presidio: {
provider: "PresidioPII",
guardrailNameSuggestion: "Presidio PII",
mode: "pre_call",
defaultOn: false,
},
bedrock: {
provider: "Bedrock",
guardrailNameSuggestion: "Bedrock Guardrail",
mode: "pre_call",
defaultOn: false,
},
lakera: {
provider: "Lakera",
guardrailNameSuggestion: "Lakera",
mode: "pre_call",
defaultOn: false,
},
openai_moderation: {
provider: "OpenaiModeration",
guardrailNameSuggestion: "OpenAI Moderation",
mode: "pre_call",
defaultOn: false,
},
google_model_armor: {
provider: "ModelArmor",
guardrailNameSuggestion: "Google Cloud Model Armor",
mode: "pre_call",
defaultOn: false,
},
guardrails_ai: {
provider: "GuardrailsAi",
guardrailNameSuggestion: "Guardrails AI",
mode: "pre_call",
defaultOn: false,
},
zscaler: {
provider: "ZscalerAiGuard",
guardrailNameSuggestion: "Zscaler AI Guard",
mode: "pre_call",
defaultOn: false,
},
panw: {
provider: "PanwPrismaAirs",
guardrailNameSuggestion: "PANW Prisma AIRS",
mode: "pre_call",
defaultOn: false,
},
noma: {
provider: "Noma",
guardrailNameSuggestion: "Noma Security",
mode: "pre_call",
defaultOn: false,
},
aporia: {
provider: "AporiaAi",
guardrailNameSuggestion: "Aporia AI",
mode: "pre_call",
defaultOn: false,
},
aim: {
provider: "Aim",
guardrailNameSuggestion: "AIM Guardrail",
mode: "pre_call",
defaultOn: false,
},
prompt_security: {
provider: "PromptSecurity",
guardrailNameSuggestion: "Prompt Security",
mode: "pre_call",
defaultOn: false,
},
lasso: {
provider: "Lasso",
guardrailNameSuggestion: "Lasso Guardrail",
mode: "pre_call",
defaultOn: false,
},
pangea: {
provider: "Pangea",
guardrailNameSuggestion: "Pangea Guardrail",
mode: "pre_call",
defaultOn: false,
},
enkryptai: {
provider: "Enkryptai",
guardrailNameSuggestion: "EnkryptAI",
mode: "pre_call",
defaultOn: false,
},
javelin: {
provider: "Javelin",
guardrailNameSuggestion: "Javelin Guardrails",
mode: "pre_call",
defaultOn: false,
},
pillar: {
provider: "Pillar",
guardrailNameSuggestion: "Pillar Guardrail",
mode: "pre_call",
defaultOn: false,
},
};
@@ -0,0 +1,360 @@
export interface GuardrailCardInfo {
id: string;
name: string;
description: string;
category: "litellm" | "partner";
subcategory?: string;
logo: string;
tags: string[];
eval?: {
f1: number;
precision: number;
recall: number;
testCases: number;
latency: string;
};
providerKey?: string;
}
const ASSET_PREFIX = "../assets/logos/";
export const LITELLM_CONTENT_FILTER_CARDS: GuardrailCardInfo[] = [
{
id: "cf_denied_financial",
name: "Denied Financial Advice",
description: "Detects requests for personalized financial advice, investment recommendations, or financial planning.",
category: "litellm",
subcategory: "Content Category",
logo: `${ASSET_PREFIX}litellm_logo.jpg`,
tags: ["Content Category", "Topic Blocker"],
eval: {
f1: 100.0,
precision: 100.0,
recall: 100.0,
testCases: 207,
latency: "<0.1ms",
},
},
{
id: "cf_denied_legal",
name: "Denied Legal Advice",
description: "Detects requests for unauthorized legal advice, case analysis, or legal recommendations.",
category: "litellm",
subcategory: "Content Category",
logo: `${ASSET_PREFIX}litellm_logo.jpg`,
tags: ["Content Category", "Topic Blocker"],
},
{
id: "cf_denied_medical",
name: "Denied Medical Advice",
description: "Detects requests for medical diagnosis, treatment recommendations, or health advice.",
category: "litellm",
subcategory: "Content Category",
logo: `${ASSET_PREFIX}litellm_logo.jpg`,
tags: ["Content Category", "Topic Blocker"],
},
{
id: "cf_harmful_violence",
name: "Harmful Violence",
description: "Detects content related to violence, criminal planning, attacks, and violent threats.",
category: "litellm",
subcategory: "Content Category",
logo: `${ASSET_PREFIX}litellm_logo.jpg`,
tags: ["Content Category", "Safety"],
},
{
id: "cf_harmful_self_harm",
name: "Harmful Self-Harm",
description: "Detects content related to self-harm, suicide, and dangerous self-destructive behavior.",
category: "litellm",
subcategory: "Content Category",
logo: `${ASSET_PREFIX}litellm_logo.jpg`,
tags: ["Content Category", "Safety"],
},
{
id: "cf_harmful_child_safety",
name: "Harmful Child Safety",
description: "Detects content that could endanger child safety or exploit minors.",
category: "litellm",
subcategory: "Content Category",
logo: `${ASSET_PREFIX}litellm_logo.jpg`,
tags: ["Content Category", "Safety"],
},
{
id: "cf_harmful_illegal_weapons",
name: "Harmful Illegal Weapons",
description: "Detects content related to illegal weapons manufacturing, distribution, or acquisition.",
category: "litellm",
subcategory: "Content Category",
logo: `${ASSET_PREFIX}litellm_logo.jpg`,
tags: ["Content Category", "Safety"],
},
{
id: "cf_bias_gender",
name: "Bias: Gender",
description: "Detects gender-based discrimination, stereotypes, and biased language.",
category: "litellm",
subcategory: "Content Category",
logo: `${ASSET_PREFIX}litellm_logo.jpg`,
tags: ["Content Category", "Bias"],
},
{
id: "cf_bias_racial",
name: "Bias: Racial",
description: "Detects racial discrimination, stereotypes, and racially biased content.",
category: "litellm",
subcategory: "Content Category",
logo: `${ASSET_PREFIX}litellm_logo.jpg`,
tags: ["Content Category", "Bias"],
},
{
id: "cf_bias_religious",
name: "Bias: Religious",
description: "Detects religious discrimination, intolerance, and religiously biased content.",
category: "litellm",
subcategory: "Content Category",
logo: `${ASSET_PREFIX}litellm_logo.jpg`,
tags: ["Content Category", "Bias"],
},
{
id: "cf_bias_sexual_orientation",
name: "Bias: Sexual Orientation",
description: "Detects discrimination based on sexual orientation and related biased content.",
category: "litellm",
subcategory: "Content Category",
logo: `${ASSET_PREFIX}litellm_logo.jpg`,
tags: ["Content Category", "Bias"],
},
{
id: "cf_prompt_injection_jailbreak",
name: "Prompt Injection: Jailbreak",
description: "Detects jailbreak attempts designed to bypass AI safety guidelines and restrictions.",
category: "litellm",
subcategory: "Content Category",
logo: `${ASSET_PREFIX}litellm_logo.jpg`,
tags: ["Content Category", "Prompt Injection"],
},
{
id: "cf_prompt_injection_data_exfil",
name: "Prompt Injection: Data Exfiltration",
description: "Detects attempts to extract sensitive data through prompt manipulation.",
category: "litellm",
subcategory: "Content Category",
logo: `${ASSET_PREFIX}litellm_logo.jpg`,
tags: ["Content Category", "Prompt Injection"],
},
{
id: "cf_prompt_injection_sql",
name: "Prompt Injection: SQL",
description: "Detects SQL injection attempts embedded in prompts.",
category: "litellm",
subcategory: "Content Category",
logo: `${ASSET_PREFIX}litellm_logo.jpg`,
tags: ["Content Category", "Prompt Injection"],
},
{
id: "cf_prompt_injection_malicious_code",
name: "Prompt Injection: Malicious Code",
description: "Detects attempts to inject malicious code through prompts.",
category: "litellm",
subcategory: "Content Category",
logo: `${ASSET_PREFIX}litellm_logo.jpg`,
tags: ["Content Category", "Prompt Injection"],
},
{
id: "cf_prompt_injection_system_prompt",
name: "Prompt Injection: System Prompt",
description: "Detects attempts to extract or override system prompts.",
category: "litellm",
subcategory: "Content Category",
logo: `${ASSET_PREFIX}litellm_logo.jpg`,
tags: ["Content Category", "Prompt Injection"],
},
{
id: "cf_denied_insults",
name: "Insults & Personal Attacks",
description: "Detects insults, name-calling, and personal attacks directed at the chatbot, staff, or other people.",
category: "litellm",
subcategory: "Content Category",
logo: `${ASSET_PREFIX}litellm_logo.jpg`,
tags: ["Content Category", "Topic Blocker"],
eval: {
f1: 100.0,
precision: 100.0,
recall: 100.0,
testCases: 299,
latency: "<0.1ms",
},
},
{
id: "cf_toxic_abuse",
name: "Toxic & Abusive Language",
description: "Detects toxic, abusive, and hateful language across multiple languages (EN, AU, DE, ES, FR).",
category: "litellm",
subcategory: "Content Category",
logo: `${ASSET_PREFIX}litellm_logo.jpg`,
tags: ["Content Category", "Toxicity"],
},
{
id: "cf_patterns",
name: "Pattern Matching",
description: "Detect and block sensitive data patterns like SSNs, credit card numbers, API keys, and custom regex patterns.",
category: "litellm",
subcategory: "Patterns",
logo: `${ASSET_PREFIX}litellm_logo.jpg`,
tags: ["PII", "Regex", "Data Protection"],
},
{
id: "cf_keywords",
name: "Keyword Blocking",
description: "Block or mask content containing specific keywords or phrases. Upload custom word lists or add individual terms.",
category: "litellm",
subcategory: "Keywords",
logo: `${ASSET_PREFIX}litellm_logo.jpg`,
tags: ["Keywords", "Blocklist"],
},
];
export const PARTNER_GUARDRAIL_CARDS: GuardrailCardInfo[] = [
{
id: "presidio",
name: "Presidio PII",
description: "Microsoft Presidio for PII detection and anonymization. Supports 30+ entity types with configurable actions.",
category: "partner",
logo: `${ASSET_PREFIX}presidio.png`,
tags: ["PII", "Microsoft"],
providerKey: "PresidioPII",
},
{
id: "bedrock",
name: "Bedrock Guardrail",
description: "AWS Bedrock Guardrails for content filtering, topic avoidance, and sensitive information detection.",
category: "partner",
logo: `${ASSET_PREFIX}bedrock.svg`,
tags: ["AWS", "Content Safety"],
providerKey: "Bedrock",
},
{
id: "lakera",
name: "Lakera",
description: "AI security platform protecting against prompt injections, data leakage, and harmful content.",
category: "partner",
logo: `${ASSET_PREFIX}lakeraai.jpeg`,
tags: ["Security", "Prompt Injection"],
providerKey: "Lakera",
},
{
id: "openai_moderation",
name: "OpenAI Moderation",
description: "OpenAI's content moderation API for detecting harmful content across multiple categories.",
category: "partner",
logo: `${ASSET_PREFIX}openai_small.svg`,
tags: ["Content Moderation", "OpenAI"],
},
{
id: "google_model_armor",
name: "Google Cloud Model Armor",
description: "Google Cloud's model protection service for safe and responsible AI deployments.",
category: "partner",
logo: `${ASSET_PREFIX}google.svg`,
tags: ["Google Cloud", "Safety"],
},
{
id: "guardrails_ai",
name: "Guardrails AI",
description: "Open-source framework for adding structural, type, and quality guarantees to LLM outputs.",
category: "partner",
logo: `${ASSET_PREFIX}guardrails_ai.jpeg`,
tags: ["Open Source", "Validation"],
},
{
id: "zscaler",
name: "Zscaler AI Guard",
description: "Enterprise AI security from Zscaler for monitoring and protecting AI/ML workloads.",
category: "partner",
logo: `${ASSET_PREFIX}zscaler.svg`,
tags: ["Enterprise", "Security"],
},
{
id: "panw",
name: "PANW Prisma AIRS",
description: "Palo Alto Networks Prisma AI Runtime Security for securing AI applications in production.",
category: "partner",
logo: `${ASSET_PREFIX}palo_alto_networks.jpeg`,
tags: ["Enterprise", "Security"],
},
{
id: "noma",
name: "Noma Security",
description: "AI security platform for detecting and preventing AI-specific threats and vulnerabilities.",
category: "partner",
logo: `${ASSET_PREFIX}noma_security.png`,
tags: ["Security", "Threat Detection"],
},
{
id: "aporia",
name: "Aporia AI",
description: "Real-time AI guardrails for hallucination detection, topic control, and policy enforcement.",
category: "partner",
logo: `${ASSET_PREFIX}aporia.png`,
tags: ["Hallucination", "Policy"],
},
{
id: "aim",
name: "AIM Guardrail",
description: "AIM Security guardrails for comprehensive AI threat detection and mitigation.",
category: "partner",
logo: `${ASSET_PREFIX}aim_security.jpeg`,
tags: ["Security", "Threat Detection"],
},
{
id: "prompt_security",
name: "Prompt Security",
description: "Protect against prompt injection attacks, data leakage, and other LLM security threats.",
category: "partner",
logo: `${ASSET_PREFIX}prompt_security.png`,
tags: ["Prompt Injection", "Security"],
},
{
id: "lasso",
name: "Lasso Guardrail",
description: "Content moderation and safety guardrails for responsible AI deployments.",
category: "partner",
logo: `${ASSET_PREFIX}lasso.png`,
tags: ["Content Moderation"],
},
{
id: "pangea",
name: "Pangea Guardrail",
description: "Pangea's AI guardrails for secure, compliant, and trustworthy AI applications.",
category: "partner",
logo: `${ASSET_PREFIX}pangea.png`,
tags: ["Compliance", "Security"],
},
{
id: "enkryptai",
name: "EnkryptAI",
description: "AI security and governance platform for enterprise AI safety and compliance.",
category: "partner",
logo: `${ASSET_PREFIX}enkrypt_ai.avif`,
tags: ["Enterprise", "Governance"],
},
{
id: "javelin",
name: "Javelin Guardrails",
description: "AI gateway with built-in guardrails for secure and compliant AI operations.",
category: "partner",
logo: `${ASSET_PREFIX}javelin.png`,
tags: ["Gateway", "Security"],
},
{
id: "pillar",
name: "Pillar Guardrail",
description: "AI safety platform for monitoring, testing, and securing AI systems.",
category: "partner",
logo: `${ASSET_PREFIX}pillar.jpeg`,
tags: ["Monitoring", "Safety"],
},
];
export const ALL_CARDS = [...LITELLM_CONTENT_FILTER_CARDS, ...PARTNER_GUARDRAIL_CARDS];
@@ -0,0 +1,238 @@
import React, { useState } from "react";
import { Button } from "antd";
import { ArrowLeftOutlined } from "@ant-design/icons";
import AddGuardrailForm from "./add_guardrail_form";
import { GUARDRAIL_PRESETS } from "./guardrail_garden_configs";
import { GuardrailCardInfo } from "./guardrail_garden_data";
interface GuardrailDetailViewProps {
card: GuardrailCardInfo;
onBack: () => void;
accessToken: string | null;
onGuardrailCreated: () => void;
}
const GuardrailDetailView: React.FC<GuardrailDetailViewProps> = ({
card,
onBack,
accessToken,
onGuardrailCreated,
}) => {
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" }] : []),
];
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 },
]
: [];
const tabs = [
{ key: "overview", label: "Overview" },
...(card.eval ? [{ key: "eval", label: "Eval Results" }] : []),
];
return (
<div style={{ maxWidth: 960, margin: "0 auto" }}>
{/* Back link */}
<div
onClick={onBack}
style={{
display: "inline-flex",
alignItems: "center",
gap: 6,
color: "#5f6368",
cursor: "pointer",
fontSize: 14,
marginBottom: 24,
}}
>
<ArrowLeftOutlined style={{ fontSize: 11 }} />
<span>{card.name}</span>
</div>
{/* ── Header block (Vertex-style) ── */}
<div style={{ display: "flex", alignItems: "center", gap: 16, marginBottom: 8 }}>
<img
src={card.logo}
alt=""
style={{ width: 40, height: 40, borderRadius: 8, objectFit: "contain" }}
onError={(e) => { (e.target as HTMLImageElement).style.display = "none"; }}
/>
<h1 style={{ fontSize: 28, fontWeight: 400, color: "#202124", margin: 0, lineHeight: 1.2 }}>
{card.name}
</h1>
</div>
<p style={{ fontSize: 14, color: "#5f6368", margin: "0 0 20px 0", lineHeight: 1.6 }}>
{card.description}
</p>
{/* Action buttons — outlined style like Vertex */}
<div style={{ display: "flex", gap: 10, marginBottom: 32 }}>
<Button
onClick={() => setIsAddFormVisible(true)}
style={{
borderRadius: 20,
padding: "4px 20px",
height: 36,
borderColor: "#dadce0",
color: "#1a73e8",
fontWeight: 500,
fontSize: 14,
}}
>
Create Guardrail
</Button>
</div>
{/* ── Tab bar ──────────────────────────────────── */}
<div style={{ borderBottom: "1px solid #dadce0", marginBottom: 28 }}>
<div style={{ display: "flex", gap: 0 }}>
{tabs.map((tab) => (
<div
key={tab.key}
onClick={() => setActiveTab(tab.key)}
style={{
padding: "12px 20px",
fontSize: 14,
color: activeTab === tab.key ? "#1a73e8" : "#5f6368",
borderBottom: activeTab === tab.key ? "3px solid #1a73e8" : "3px solid transparent",
cursor: "pointer",
fontWeight: activeTab === tab.key ? 500 : 400,
marginBottom: -1,
}}
>
{tab.label}
</div>
))}
</div>
</div>
{/* ── Tab content ──────────────────────────────── */}
{activeTab === "overview" && (
<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>
<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>
<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</th>
<th style={{ textAlign: "left", padding: "12px 0", color: "#5f6368", fontWeight: 500 }}>{card.name}</th>
</tr>
</thead>
<tbody>
{detailRows.map((row, i) => (
<tr key={i} style={{ borderBottom: "1px solid #f1f3f4" }}>
<td style={{ padding: "12px 0", color: "#3c4043" }}>{row.property}</td>
<td style={{ padding: "12px 0", color: "#202124" }}>{row.value}</td>
</tr>
))}
</tbody>
</table>
</div>
{/* Right column — metadata sidebar like Vertex */}
<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: 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: 13, color: "#202124" }}>
{card.category === "litellm" ? "Content Filter" : "Partner"}
</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={{ display: "flex", flexWrap: "wrap", gap: 6 }}>
{card.tags.map((tag) => (
<span
key={tag}
style={{
fontSize: 12,
padding: "4px 12px",
borderRadius: 16,
border: "1px solid #dadce0",
color: "#3c4043",
backgroundColor: "#fff",
}}
>
{tag}
</span>
))}
</div>
</div>
)}
</div>
</div>
)}
{activeTab === "eval" && (
<div>
<h2 style={{ fontSize: 18, fontWeight: 400, color: "#202124", margin: "0 0 16px 0" }}>Eval Results</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>
</tr>
</thead>
<tbody>
{evalRows.map((row, i) => (
<tr key={i} style={{ borderBottom: "1px solid #f1f3f4" }}>
<td style={{ padding: "12px 16px", color: "#3c4043" }}>{row.metric}</td>
<td style={{ padding: "12px 16px", color: "#202124", fontWeight: 500 }}>{row.value}</td>
</tr>
))}
</tbody>
</table>
</div>
)}
<AddGuardrailForm
visible={isAddFormVisible}
onClose={() => setIsAddFormVisible(false)}
accessToken={accessToken}
onSuccess={() => {
setIsAddFormVisible(false);
onGuardrailCreated();
}}
preset={GUARDRAIL_PRESETS[card.id]}
/>
</div>
);
};
export default GuardrailDetailView;