From 28ef76ba16ec7a089f036b4ef8f8e5092cd433b1 Mon Sep 17 00:00:00 2001 From: Ishaan Jaffer Date: Fri, 23 Jan 2026 18:37:44 -0800 Subject: [PATCH] ui for policy mgmt --- .../components/modals/CreateTeamModal.tsx | 48 ++++++- .../src/components/leftnav.tsx | 8 +- .../src/components/networking.tsx | 26 ++++ .../organisms/create_key_button.tsx | 49 +++++++ .../policies/add_attachment_form.tsx | 40 +++++- .../components/policies/add_policy_form.tsx | 18 ++- .../src/components/policies/index.tsx | 65 ++++++++- .../src/components/team/team_info.tsx | 126 ++++++++++++++++++ .../components/templates/key_edit_view.tsx | 23 ++++ .../components/templates/key_info_view.tsx | 87 +++++++++++- 10 files changed, 473 insertions(+), 17 deletions(-) diff --git a/ui/litellm-dashboard/src/app/(dashboard)/teams/components/modals/CreateTeamModal.tsx b/ui/litellm-dashboard/src/app/(dashboard)/teams/components/modals/CreateTeamModal.tsx index 972c39d49d8..0aa42b69a04 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/teams/components/modals/CreateTeamModal.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/teams/components/modals/CreateTeamModal.tsx @@ -14,7 +14,7 @@ import PremiumLoggingSettings from "@/components/common_components/PremiumLoggin import ModelAliasManager from "@/components/common_components/ModelAliasManager"; import React, { useEffect, useState } from "react"; import NotificationsManager from "@/components/molecules/notifications_manager"; -import { fetchMCPAccessGroups, getGuardrailsList, Organization, Team, teamCreateCall } from "@/components/networking"; +import { fetchMCPAccessGroups, getGuardrailsList, getPoliciesList, Organization, Team, teamCreateCall } from "@/components/networking"; import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized"; import MCPToolPermissions from "@/components/mcp_server_management/MCPToolPermissions"; @@ -76,6 +76,7 @@ const CreateTeamModal = ({ const [currentOrgForCreateTeam, setCurrentOrgForCreateTeam] = useState(null); const [modelsToPick, setModelsToPick] = useState([]); const [guardrailsList, setGuardrailsList] = useState([]); + const [policiesList, setPoliciesList] = useState([]); const [mcpAccessGroups, setMcpAccessGroups] = useState([]); const [mcpAccessGroupsLoaded, setMcpAccessGroupsLoaded] = useState(false); @@ -136,7 +137,22 @@ const CreateTeamModal = ({ } }; + const fetchPolicies = async () => { + try { + if (accessToken == null) { + return; + } + + const response = await getPoliciesList(accessToken); + const policyNames = response.policies.map((p: { policy_name: string }) => p.policy_name); + setPoliciesList(policyNames); + } catch (error) { + console.error("Failed to fetch policies:", error); + } + }; + fetchGuardrails(); + fetchPolicies(); }, [accessToken]); const handleCreate = async (formValues: Record) => { @@ -531,6 +547,36 @@ const CreateTeamModal = ({ unCheckedChildren="No" /> + + Policies{" "} + + e.stopPropagation()} + > + + + + + } + name="policies" + className="mt-8" + help="Select existing policies or enter new ones" + > + ({ + value: name, + label: name, + }))} + /> + diff --git a/ui/litellm-dashboard/src/components/leftnav.tsx b/ui/litellm-dashboard/src/components/leftnav.tsx index 543d95d2ccd..f24f4d60219 100644 --- a/ui/litellm-dashboard/src/components/leftnav.tsx +++ b/ui/litellm-dashboard/src/components/leftnav.tsx @@ -107,7 +107,7 @@ const Sidebar: React.FC = ({ setPage, defaultSelectedKey, collapse { key: "agents", page: "agents", - label: Agents, + label: "Agents", icon: , roles: rolesWithWriteAccess, }, @@ -127,7 +127,11 @@ const Sidebar: React.FC = ({ setPage, defaultSelectedKey, collapse { key: "policies", page: "policies", - label: "Policies", + label: ( + + Policies + + ), icon: , roles: all_admin_roles, }, diff --git a/ui/litellm-dashboard/src/components/networking.tsx b/ui/litellm-dashboard/src/components/networking.tsx index ffc0f80911d..5407c85bf98 100644 --- a/ui/litellm-dashboard/src/components/networking.tsx +++ b/ui/litellm-dashboard/src/components/networking.tsx @@ -5377,6 +5377,32 @@ export const getPoliciesList = async (accessToken: string) => { } }; +export const getPolicyInfoWithGuardrails = async (accessToken: string, policyName: string) => { + try { + const url = proxyBaseUrl ? `${proxyBaseUrl}/policy/info/${policyName}` : `/policy/info/${policyName}`; + const response = await fetch(url, { + method: "GET", + headers: { + [globalLitellmHeaderName]: `Bearer ${accessToken}`, + "Content-Type": "application/json", + }, + }); + + if (!response.ok) { + const errorData = await response.json(); + const errorMessage = deriveErrorMessage(errorData); + handleError(errorMessage); + throw new Error(errorMessage); + } + + const data = await response.json(); + return data; + } catch (error) { + console.error(`Failed to get policy info for ${policyName}:`, error); + throw error; + } +}; + export const createPolicyCall = async (accessToken: string, policyData: any) => { try { const url = proxyBaseUrl ? `${proxyBaseUrl}/policies` : `/policies`; diff --git a/ui/litellm-dashboard/src/components/organisms/create_key_button.tsx b/ui/litellm-dashboard/src/components/organisms/create_key_button.tsx index 6bfb3696606..1edbba28afc 100644 --- a/ui/litellm-dashboard/src/components/organisms/create_key_button.tsx +++ b/ui/litellm-dashboard/src/components/organisms/create_key_button.tsx @@ -29,6 +29,7 @@ import MCPToolPermissions from "../mcp_server_management/MCPToolPermissions"; import NotificationsManager from "../molecules/notifications_manager"; import { getGuardrailsList, + getPoliciesList, getPossibleUserRoles, getPromptsList, keyCreateCall, @@ -150,6 +151,7 @@ const CreateKey: React.FC = ({ team, teams, data, addKey }) => { const [keyOwner, setKeyOwner] = useState("you"); const [predefinedTags, setPredefinedTags] = useState(getPredefinedTags(data)); const [guardrailsList, setGuardrailsList] = useState([]); + const [policiesList, setPoliciesList] = useState([]); const [promptsList, setPromptsList] = useState([]); const [loggingSettings, setLoggingSettings] = useState([]); const [selectedCreateKeyTeam, setSelectedCreateKeyTeam] = useState(team); @@ -211,6 +213,16 @@ const CreateKey: React.FC = ({ team, teams, data, addKey }) => { } }; + const fetchPolicies = async () => { + try { + const response = await getPoliciesList(accessToken); + const policyNames = response.policies.map((p: { policy_name: string }) => p.policy_name); + setPoliciesList(policyNames); + } catch (error) { + console.error("Failed to fetch policies:", error); + } + }; + const fetchPrompts = async () => { try { const response = await getPromptsList(accessToken); @@ -221,6 +233,7 @@ const CreateKey: React.FC = ({ team, teams, data, addKey }) => { }; fetchGuardrails(); + fetchPolicies(); fetchPrompts(); }, [accessToken]); @@ -915,6 +928,42 @@ const CreateKey: React.FC = ({ team, teams, data, addKey }) => { > + + Policies{" "} + + e.stopPropagation()} // Prevent accordion from collapsing when clicking link + > + + + + + } + name="policies" + className="mt-4" + help={ + premiumUser + ? "Select existing policies or enter new ones" + : "Premium feature - Upgrade to set policies by key" + } + > + ({ + label: model, + value: model, + }))} tokenSeparators={[","]} + showSearch + filterOption={(input, option) => + (option?.label ?? "").toLowerCase().includes(input.toLowerCase()) + } style={{ width: "100%" }} /> diff --git a/ui/litellm-dashboard/src/components/policies/add_policy_form.tsx b/ui/litellm-dashboard/src/components/policies/add_policy_form.tsx index 0b4f83a1611..383e9c45dfb 100644 --- a/ui/litellm-dashboard/src/components/policies/add_policy_form.tsx +++ b/ui/litellm-dashboard/src/components/policies/add_policy_form.tsx @@ -344,6 +344,14 @@ const AddPolicyForm: React.FC = ({ Conditions (Optional) + + = ({ {modelConditionType === "model" ? ( ({ value: name, label: name }))} + /> + + form.setFieldValue("vector_stores", values)} diff --git a/ui/litellm-dashboard/src/components/templates/key_edit_view.tsx b/ui/litellm-dashboard/src/components/templates/key_edit_view.tsx index 836ebb385f1..2d3eaf2bb7f 100644 --- a/ui/litellm-dashboard/src/components/templates/key_edit_view.tsx +++ b/ui/litellm-dashboard/src/components/templates/key_edit_view.tsx @@ -1,4 +1,5 @@ import GuardrailSelector from "@/components/guardrails/GuardrailSelector"; +import PolicySelector from "@/components/policies/PolicySelector"; import { InfoCircleOutlined } from "@ant-design/icons"; import { TextInput, Button as TremorButton } from "@tremor/react"; import { Form, Input, Select, Switch, Tooltip } from "antd"; @@ -406,6 +407,28 @@ export function KeyEditView({ + + Policies{" "} + + + + + } + name="policies" + > + {accessToken && ( + { + form.setFieldValue("policies", v); + }} + accessToken={accessToken} + disabled={!premiumUser} + /> + )} + +