From 84d7816bc989cfffeedb591cbb83f1ebe50c7362 Mon Sep 17 00:00:00 2001 From: Ryan Crabbe Date: Mon, 13 Apr 2026 11:38:23 -0700 Subject: [PATCH] refactor(ui/team): extract GuardrailSettingsView and reuse across team views Pulls the Global / Team-specific subsection rendering out of TeamInfo.tsx into a shared GuardrailSettingsView component with card and inline variants, used on both the team Overview tab (inside the existing Tremor Card) and the Team Settings tab read view. The Global subsection header now carries a GlobalOutlined icon, and since the icon is load-bearing the edit-form chip coloring is simplified to a single blue instead of green/blue. --- .../src/components/GuardrailSettingsView.tsx | 97 +++++++++++++++++ .../src/components/team/TeamInfo.tsx | 103 +++--------------- 2 files changed, 115 insertions(+), 85 deletions(-) create mode 100644 ui/litellm-dashboard/src/components/GuardrailSettingsView.tsx diff --git a/ui/litellm-dashboard/src/components/GuardrailSettingsView.tsx b/ui/litellm-dashboard/src/components/GuardrailSettingsView.tsx new file mode 100644 index 0000000000..16cd46e37d --- /dev/null +++ b/ui/litellm-dashboard/src/components/GuardrailSettingsView.tsx @@ -0,0 +1,97 @@ +import React from "react"; +import { Badge, Text } from "@tremor/react"; +import { GlobalOutlined } from "@ant-design/icons"; + +interface GuardrailSettingsViewProps { + globalGuardrailNames: Set; + teamGuardrails?: string[]; + optedOutGlobalGuardrails?: string[]; + killSwitchOn?: boolean; + variant?: "card" | "inline"; + className?: string; +} + +export function GuardrailSettingsView({ + globalGuardrailNames, + teamGuardrails = [], + optedOutGlobalGuardrails = [], + killSwitchOn = false, + variant = "card", + className = "", +}: GuardrailSettingsViewProps) { + const optedOutSet = new Set(optedOutGlobalGuardrails); + const globalsRunning = Array.from(globalGuardrailNames).filter( + (n) => !optedOutSet.has(n), + ); + const nonGlobalOptIns = teamGuardrails.filter( + (n) => !globalGuardrailNames.has(n), + ); + + const isEmpty = + !killSwitchOn && globalsRunning.length === 0 && nonGlobalOptIns.length === 0; + + const content = isEmpty ? ( + No guardrails configured + ) : ( +
+
+ + + Global + + {killSwitchOn ? ( + Bypassed for this team + ) : globalsRunning.length > 0 ? ( +
+ {globalsRunning.map((name) => ( + + {name} + + ))} +
+ ) : ( + None configured + )} +
+
+ Team-specific + {nonGlobalOptIns.length > 0 ? ( +
+ {nonGlobalOptIns.map((name) => ( + + {name} + + ))} +
+ ) : ( + None configured + )} +
+
+ ); + + if (variant === "card") { + return ( +
+
+
+ Guardrails Settings + + Global and team-specific guardrails applied to this team + +
+
+ {content} +
+ ); + } + + return ( +
+ Guardrails Settings + {content} +
+ ); +} + +export default GuardrailSettingsView; diff --git a/ui/litellm-dashboard/src/components/team/TeamInfo.tsx b/ui/litellm-dashboard/src/components/team/TeamInfo.tsx index a28b41b2f2..3a9794b692 100644 --- a/ui/litellm-dashboard/src/components/team/TeamInfo.tsx +++ b/ui/litellm-dashboard/src/components/team/TeamInfo.tsx @@ -31,6 +31,7 @@ import DeleteResourceModal from "../common_components/DeleteResourceModal"; import DurationSelect from "../common_components/DurationSelect"; import PassThroughRoutesSelector from "../common_components/PassThroughRoutesSelector"; import { unfurlWildcardModelsInList } from "../key_team_helpers/fetch_available_models_team_key"; +import GuardrailSettingsView from "../GuardrailSettingsView"; import LoggingSettingsView from "../logging_settings_view"; import MCPServerSelector from "../mcp_server_management/MCPServerSelector"; import MCPToolPermissions from "../mcp_server_management/MCPToolPermissions"; @@ -613,9 +614,6 @@ const TeamInfoView: React.FC = ({ const nonGlobalOptIns: string[] = (info.metadata?.guardrails || []).filter( (n: string) => !globalGuardrailNames.has(n), ); - const globalsRunning: string[] = Array.from(globalGuardrailNames).filter( - (n) => !optedOutGlobals.has(n), - ); const effectiveGuardrails: string[] = initialKillSwitchOn ? nonGlobalOptIns : [ @@ -632,7 +630,7 @@ const TeamInfoView: React.FC = ({ const isGlobal = globalGuardrailNames.has(value); return ( = ({ /> - Guardrails - {!initialKillSwitchOn && - globalsRunning.length === 0 && - nonGlobalOptIns.length === 0 ? ( - No guardrails configured - ) : ( -
-
- Global - {initialKillSwitchOn ? ( - Bypassed for this team - ) : globalsRunning.length > 0 ? ( -
- {globalsRunning.map((name) => ( - - {name} - - ))} -
- ) : ( - None running - )} -
-
- Team-specific - {nonGlobalOptIns.length > 0 ? ( -
- {nonGlobalOptIns.map((name) => ( - - {name} - - ))} -
- ) : ( - None configured - )} -
-
- )} +
@@ -1484,48 +1450,6 @@ const TeamInfoView: React.FC = ({ {info.blocked ? "Blocked" : "Active"} -
- Guardrails - {!initialKillSwitchOn && - globalsRunning.length === 0 && - nonGlobalOptIns.length === 0 ? ( - No guardrails configured - ) : ( -
-
- Global - {initialKillSwitchOn ? ( - Bypassed for this team - ) : globalsRunning.length > 0 ? ( -
- {globalsRunning.map((name) => ( - - {name} - - ))} -
- ) : ( - None running - )} -
-
- Team-specific - {nonGlobalOptIns.length > 0 ? ( -
- {nonGlobalOptIns.map((name) => ( - - {name} - - ))} -
- ) : ( - None configured - )} -
-
- )} -
- = ({ accessToken={accessToken} /> + +