From 771c36c1eec0d67fc9ee98150e14244e0a71ecce Mon Sep 17 00:00:00 2001 From: Yuneng Jiang Date: Thu, 13 Aug 2026 15:05:09 -0700 Subject: [PATCH] refactor(ui): migrate guardrails route to shadcn Ports the 21 route-owned guardrails components off antd and Tremor onto the installed shadcn base-vega primitives. Forms, tables and shared components stay where they are: the route analyzer buckets add_guardrail_form, guardrail_info and six others as DEFERRED, and eleven components under src/components as SHARED, so none of them are touched here. Three behaviours needed explicit handling because Base UI does not match antd by default. Base UI Tabs mounts only the active panel, so the playground draft and every panel's local state would reset on a tab switch; TabsContent now carries keepMounted. ComboboxList takes a function child to render the filtered items, and mapping over the array instead renders everything and silently disables the type to filter behaviour antd gave for free. SelectContent gets alignItemWithTrigger={false} so popups anchor below the trigger the way antd's did, which also removes a focus race that made the template picker intermittently unclickable. The three dialogs reached from inside the still-antd wizard need a layer above its z-index of 1000 to be visible at all. Adds three tests rather than editing the characterisation tests from the previous commit: a guard that every tab panel stays mounted, and one filtering regression each for the pattern and mode dropdowns. Prunes the guardrails entries from eslint-suppressions.json. --- ui/litellm-dashboard/eslint-suppressions.json | 86 +--- .../_components/GuardrailTestPanel.tsx | 98 +++-- .../_components/GuardrailTestPlayground.tsx | 207 +++++---- .../_components/GuardrailTestResults.tsx | 107 ++--- .../_components/GuardrailsPanel.test.tsx | 7 + .../_components/GuardrailsPanel.tsx | 219 +++++----- .../content_filter/CategoryTable.tsx | 71 +-- .../ContentCategoryConfiguration.tsx | 320 +++++++------- .../ContentFilterConfiguration.tsx | 113 +++-- .../content_filter/ContentFilterDisplay.tsx | 67 +-- .../content_filter/ContentFilterManager.tsx | 28 +- .../content_filter/CustomPatternModal.tsx | 110 +++-- .../content_filter/KeywordModal.tsx | 109 +++-- .../content_filter/KeywordTable.tsx | 30 +- .../content_filter/PatternModal.test.tsx | 14 + .../content_filter/PatternModal.tsx | 152 ++++--- .../content_filter/PatternTable.tsx | 42 +- .../content_filter/action_options.ts | 10 + .../content_filter/dialog_layering.ts | 1 + .../custom_code/CustomCodeModal.test.tsx | 11 + .../custom_code/CustomCodeModal.tsx | 403 +++++++++--------- .../_components/guardrail_garden.tsx | 68 ++- .../_components/guardrail_garden_card.tsx | 38 +- .../_components/guardrail_garden_detail.tsx | 31 +- .../guardrails/_components/pii_components.tsx | 229 +++++----- .../_components/pii_configuration.tsx | 9 +- .../ToolPermissionRulesEditor.tsx | 348 ++++++++------- 27 files changed, 1490 insertions(+), 1438 deletions(-) create mode 100644 ui/litellm-dashboard/src/app/(dashboard)/guardrails/_components/content_filter/action_options.ts create mode 100644 ui/litellm-dashboard/src/app/(dashboard)/guardrails/_components/content_filter/dialog_layering.ts diff --git a/ui/litellm-dashboard/eslint-suppressions.json b/ui/litellm-dashboard/eslint-suppressions.json index 2f4def9ffa2..8ba5d7aab3f 100644 --- a/ui/litellm-dashboard/eslint-suppressions.json +++ b/ui/litellm-dashboard/eslint-suppressions.json @@ -326,28 +326,12 @@ "count": 1 } }, - "src/app/(dashboard)/guardrails/_components/GuardrailTestPanel.tsx": { - "no-restricted-imports": { - "count": 2 - } - }, "src/app/(dashboard)/guardrails/_components/GuardrailTestPlayground.tsx": { "no-nested-ternary": { "count": 1 - }, - "no-restricted-imports": { - "count": 1 - } - }, - "src/app/(dashboard)/guardrails/_components/GuardrailTestResults.tsx": { - "no-restricted-imports": { - "count": 2 } }, "src/app/(dashboard)/guardrails/_components/GuardrailsPanel.tsx": { - "no-restricted-imports": { - "count": 1 - }, "react-hooks/set-state-in-effect": { "count": 1 } @@ -386,11 +370,6 @@ "count": 2 } }, - "src/app/(dashboard)/guardrails/_components/content_filter/CategoryTable.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, "src/app/(dashboard)/guardrails/_components/content_filter/CompetitorIntentConfiguration.tsx": { "no-nested-ternary": { "count": 1 @@ -403,14 +382,8 @@ } }, "src/app/(dashboard)/guardrails/_components/content_filter/ContentCategoryConfiguration.tsx": { - "local/no-complex-jsx-arrow": { - "count": 1 - }, "no-nested-ternary": { - "count": 3 - }, - "no-restricted-imports": { - "count": 1 + "count": 2 }, "react-hooks/set-state-in-effect": { "count": 1 @@ -419,62 +392,20 @@ "src/app/(dashboard)/guardrails/_components/content_filter/ContentFilterConfiguration.tsx": { "local/no-complex-jsx-arrow": { "count": 3 - }, - "no-restricted-imports": { - "count": 1 - } - }, - "src/app/(dashboard)/guardrails/_components/content_filter/ContentFilterDisplay.tsx": { - "no-restricted-imports": { - "count": 1 } }, "src/app/(dashboard)/guardrails/_components/content_filter/ContentFilterManager.tsx": { "max-params": { "count": 2 }, - "no-restricted-imports": { - "count": 1 - }, "react-hooks/set-state-in-effect": { "count": 1 } }, - "src/app/(dashboard)/guardrails/_components/content_filter/CustomPatternModal.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, - "src/app/(dashboard)/guardrails/_components/content_filter/KeywordModal.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, - "src/app/(dashboard)/guardrails/_components/content_filter/KeywordTable.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, - "src/app/(dashboard)/guardrails/_components/content_filter/PatternModal.tsx": { - "local/no-complex-jsx-arrow": { - "count": 1 - }, - "no-restricted-imports": { - "count": 1 - } - }, - "src/app/(dashboard)/guardrails/_components/content_filter/PatternTable.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, "src/app/(dashboard)/guardrails/_components/custom_code/CustomCodeModal.tsx": { "no-nested-ternary": { "count": 6 }, - "no-restricted-imports": { - "count": 2 - }, "react-hooks/set-state-in-effect": { "count": 1 } @@ -487,9 +418,6 @@ "src/app/(dashboard)/guardrails/_components/guardrail_garden.tsx": { "local/filename-pascal-case": { "count": 1 - }, - "no-restricted-imports": { - "count": 1 } }, "src/app/(dashboard)/guardrails/_components/guardrail_garden_card.tsx": { @@ -500,9 +428,6 @@ "src/app/(dashboard)/guardrails/_components/guardrail_garden_detail.tsx": { "local/filename-pascal-case": { "count": 1 - }, - "no-restricted-imports": { - "count": 1 } }, "src/app/(dashboard)/guardrails/_components/guardrail_info.tsx": { @@ -568,23 +493,14 @@ "src/app/(dashboard)/guardrails/_components/pii_components.tsx": { "local/filename-pascal-case": { "count": 1 - }, - "no-restricted-imports": { - "count": 1 } }, "src/app/(dashboard)/guardrails/_components/pii_configuration.tsx": { "local/filename-pascal-case": { "count": 1 - }, - "no-restricted-imports": { - "count": 1 } }, "src/app/(dashboard)/guardrails/_components/tool_permission/ToolPermissionRulesEditor.tsx": { - "no-restricted-imports": { - "count": 2 - }, "react-hooks/purity": { "count": 1 } diff --git a/ui/litellm-dashboard/src/app/(dashboard)/guardrails/_components/GuardrailTestPanel.tsx b/ui/litellm-dashboard/src/app/(dashboard)/guardrails/_components/GuardrailTestPanel.tsx index e310da55063..8297c1e1e3b 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/guardrails/_components/GuardrailTestPanel.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/guardrails/_components/GuardrailTestPanel.tsx @@ -1,13 +1,12 @@ import React, { useState } from "react"; -import { Button } from "@tremor/react"; -import { Input, Typography, Tooltip } from "antd"; -import { CopyOutlined, InfoCircleOutlined } from "@ant-design/icons"; +import { Copy, Info } from "lucide-react"; import NotificationsManager from "@/components/molecules/notifications_manager"; +import { Button } from "@/components/ui/button"; +import { Textarea } from "@/components/ui/textarea"; +import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; +import { UiLoadingSpinner } from "@/components/ui/ui-loading-spinner"; import GuardrailTestResults from "./GuardrailTestResults"; -const { TextArea } = Input; -const { Text } = Typography; - interface GuardrailTestPanelProps { guardrailNames: string[]; onSubmit: (text: string, metadata?: Record | null) => void; @@ -108,23 +107,23 @@ export function GuardrailTestPanel({ return (
{/* Header */} -
+
-
-

Test Guardrails:

+
+

Test Guardrails:

{guardrailNames.map((name) => (
- {name} + {name}
))}
-

+

Test {guardrailNames.length > 1 ? "guardrails" : "guardrail"} and compare results

@@ -135,46 +134,63 @@ export function GuardrailTestPanel({
-
+
- - - + + + + + + } + /> + Press Enter to submit. Use Shift+Enter for new line.
{inputText && ( - )}
-