diff --git a/ui/litellm-dashboard/src/components/policies/pipeline_flow_builder.tsx b/ui/litellm-dashboard/src/components/policies/pipeline_flow_builder.tsx index 7d0282a58f4..259378f2e19 100644 --- a/ui/litellm-dashboard/src/components/policies/pipeline_flow_builder.tsx +++ b/ui/litellm-dashboard/src/components/policies/pipeline_flow_builder.tsx @@ -3,8 +3,9 @@ import { Select, Typography, message } from "antd"; import { Button, TextInput } from "@tremor/react"; import { ArrowLeftIcon, PlusIcon } from "@heroicons/react/outline"; import { DotsVerticalIcon } from "@heroicons/react/solid"; -import { GuardrailPipeline, PipelineStep, PolicyCreateRequest, PolicyUpdateRequest, Policy } from "./types"; +import { GuardrailPipeline, PipelineStep, PipelineTestResult, PolicyCreateRequest, PolicyUpdateRequest, Policy } from "./types"; import { Guardrail } from "../guardrails/types"; +import { testPipelineCall } from "../networking"; import NotificationsManager from "../molecules/notifications_manager"; const { Text } = Typography; @@ -536,6 +537,252 @@ export const PipelineInfoDisplay: React.FC = ({ pipeli ); +// ───────────────────────────────────────────────────────────────────────────── +// Pipeline Test Panel (right drawer) +// ───────────────────────────────────────────────────────────────────────────── + +interface PipelineTestPanelProps { + pipeline: GuardrailPipeline; + accessToken: string | null; + onClose: () => void; +} + +const OUTCOME_STYLES: Record = { + pass: { bg: "#f0fdf4", color: "#16a34a", label: "PASS" }, + fail: { bg: "#fef2f2", color: "#dc2626", label: "FAIL" }, + error: { bg: "#fffbeb", color: "#d97706", label: "ERROR" }, +}; + +const TERMINAL_STYLES: Record = { + allow: { bg: "#f0fdf4", color: "#16a34a" }, + block: { bg: "#fef2f2", color: "#dc2626" }, + modify_response: { bg: "#eff6ff", color: "#2563eb" }, +}; + +const PipelineTestPanel: React.FC = ({ + pipeline, + accessToken, + onClose, +}) => { + const [testMessage, setTestMessage] = useState("Hello, can you help me?"); + const [isRunning, setIsRunning] = useState(false); + const [result, setResult] = useState(null); + const [error, setError] = useState(null); + + const handleRunTest = async () => { + if (!accessToken) return; + + const emptySteps = pipeline.steps.filter((s) => !s.guardrail); + if (emptySteps.length > 0) { + setError("All steps must have a guardrail selected"); + return; + } + + setIsRunning(true); + setResult(null); + setError(null); + + try { + const data = await testPipelineCall( + accessToken, + pipeline, + [{ role: "user", content: testMessage }] + ); + setResult(data); + } catch (e) { + setError(e instanceof Error ? e.message : String(e)); + } finally { + setIsRunning(false); + } + }; + + return ( +
+ {/* Panel header */} +
+ Test Pipeline + +
+ + {/* Input section */} +
+ +