From e6ee14a2490fb2eb58afa98dc0af2a697edf9796 Mon Sep 17 00:00:00 2001 From: Ishaan Jaffer Date: Thu, 23 Apr 2026 15:18:26 -0700 Subject: [PATCH] feat(ui): wire LLM-as-a-Judge into add guardrail form --- .../guardrails/add_guardrail_form.tsx | 49 +++++++++++++++++-- .../guardrails/guardrail_info_helpers.tsx | 7 +++ 2 files changed, 52 insertions(+), 4 deletions(-) diff --git a/ui/litellm-dashboard/src/components/guardrails/add_guardrail_form.tsx b/ui/litellm-dashboard/src/components/guardrails/add_guardrail_form.tsx index 4cbe5664c6b..5d78249e2fa 100644 --- a/ui/litellm-dashboard/src/components/guardrails/add_guardrail_form.tsx +++ b/ui/litellm-dashboard/src/components/guardrails/add_guardrail_form.tsx @@ -1,7 +1,7 @@ import { Form, Input, Modal, Select, Tag, Typography, Button } from "antd"; import React, { useEffect, useMemo, useState } from "react"; import NotificationsManager from "../molecules/notifications_manager"; -import { createGuardrailCall, getGuardrailProviderSpecificParams, getGuardrailUISettings } from "../networking"; +import { createGuardrailCall, getGuardrailProviderSpecificParams, getGuardrailUISettings, modelAvailableCall } from "../networking"; import ContentFilterConfiguration from "./content_filter/ContentFilterConfiguration"; import { choiceToSkipSystemForCreate, @@ -11,10 +11,12 @@ import { populateGuardrailProviderMap, populateGuardrailProviders, shouldRenderContentFilterConfigSettings, + shouldRenderLLMJudgeFields, shouldRenderPIIConfigSettings, } from "./guardrail_info_helpers"; import GuardrailOptionalParams from "./guardrail_optional_params"; import GuardrailProviderFields from "./guardrail_provider_fields"; +import LLMJudgeFields from "./llm_judge/LLMJudgeFields"; import PiiConfiguration from "./pii_configuration"; import ToolPermissionRulesEditor, { ToolPermissionConfig } from "./tool_permission/ToolPermissionRulesEditor"; @@ -126,6 +128,7 @@ const AddGuardrailForm: React.FC = ({ visible, onClose, a const [onViolation, setOnViolation] = useState<"warn" | "end_session">("warn"); const [realtimeViolationMessage, setRealtimeViolationMessage] = useState(""); const [endpointSettingsOpen, setEndpointSettingsOpen] = useState(false); + const [availableModels, setAvailableModels] = useState([]); const [toolPermissionConfig, setToolPermissionConfig] = useState({ rules: [], @@ -149,13 +152,17 @@ const AddGuardrailForm: React.FC = ({ visible, onClose, a const fetchData = async () => { try { // Parallel requests for speed - const [uiSettings, providerParamsResp] = await Promise.all([ + const [uiSettings, providerParamsResp, modelsResp] = await Promise.all([ getGuardrailUISettings(accessToken), getGuardrailProviderSpecificParams(accessToken), + modelAvailableCall(accessToken, "", "").catch(() => null), ]); setGuardrailSettings(uiSettings); setProviderParams(providerParamsResp); + if (modelsResp?.data) { + setAvailableModels(modelsResp.data.map((m: any) => m.id)); + } // Populate dynamic providers from API response populateGuardrailProviders(providerParamsResp); @@ -242,6 +249,11 @@ const AddGuardrailForm: React.FC = ({ visible, onClose, a on_disallowed_action: "block", violation_message_template: "", }); + + // Default LLM-as-a-Judge to post_call mode + if (value === "LlmAsAJudge") { + form.setFieldsValue({ mode: "post_call" }); + } }; const handleEntitySelect = (entity: string) => { @@ -510,6 +522,29 @@ const AddGuardrailForm: React.FC = ({ visible, onClose, a } } + if (guardrailProvider === "llm_as_a_judge") { + const criteria: any[] = values.criteria || []; + if (criteria.length === 0) { + NotificationsManager.fromBackend("Add at least one evaluation criterion"); + setLoading(false); + return; + } + const weightTotal = criteria.reduce((sum: number, c: any) => sum + (Number(c?.weight) || 0), 0); + if (weightTotal !== 100) { + NotificationsManager.fromBackend(`Criterion weights must sum to 100% (currently ${weightTotal}%)`); + setLoading(false); + return; + } + guardrailData.litellm_params.judge_model = values.judge_model; + guardrailData.litellm_params.overall_threshold = values.overall_threshold ?? 80; + guardrailData.litellm_params.on_failure = values.on_failure ?? "block"; + guardrailData.litellm_params.criteria = criteria.map((c: any) => ({ + name: c.name, + weight: Number(c.weight), + description: c.description || "", + })); + } + if (guardrailProvider === "tool_permission") { if (toolPermissionConfig.rules.length === 0) { NotificationsManager.fromBackend("Add at least one tool permission rule"); @@ -549,7 +584,8 @@ const AddGuardrailForm: React.FC = ({ visible, onClose, a console.log("values: ", JSON.stringify(values)); // Use pre-fetched provider params to copy recognised params - if (providerParams && selectedProvider) { + // Skip for providers that handle their own litellm_params (llm_as_a_judge, tool_permission, content filter, PII) + if (providerParams && selectedProvider && guardrailProvider !== "llm_as_a_judge") { const providerKey = guardrail_provider_map[selectedProvider]?.toLowerCase(); console.log("providerKey: ", providerKey); const providerSpecificParams = providerParams[providerKey] || {}; @@ -768,8 +804,13 @@ const AddGuardrailForm: React.FC = ({ visible, onClose, a + {/* LLM-as-a-Judge: dedicated criteria builder */} + {shouldRenderLLMJudgeFields(selectedProvider) && ( + + )} + {/* Use the GuardrailProviderFields component to render provider-specific fields */} - {!isToolPermissionProvider && !shouldRenderContentFilterConfigSettings(selectedProvider) && ( + {!isToolPermissionProvider && !shouldRenderContentFilterConfigSettings(selectedProvider) && !shouldRenderLLMJudgeFields(selectedProvider) && ( { @@ -49,6 +50,7 @@ export const guardrail_provider_map: Record = { ToolPermission: "tool_permission", BlockCodeExecution: "block_code_execution", Promptguard: "promptguard", + LlmAsAJudge: "llm_as_a_judge", }; // Function to populate provider map from API response - updates the original map @@ -103,6 +105,11 @@ export const shouldRenderContentFilterConfigSettings = (provider: string | null) return providerEnum === "LiteLLM Content Filter"; }; +export const shouldRenderLLMJudgeFields = (provider: string | null) => { + if (!provider) return false; + return guardrail_provider_map[provider] === "llm_as_a_judge"; +}; + const asset_logos_folder = "../ui/assets/logos/"; export const guardrailLogoMap: Record = {