diff --git a/ui/litellm-dashboard/src/components/common_components/PassThroughRoutesSelector.tsx b/ui/litellm-dashboard/src/components/common_components/PassThroughRoutesSelector.tsx new file mode 100644 index 00000000000..a2ed9c6d66b --- /dev/null +++ b/ui/litellm-dashboard/src/components/common_components/PassThroughRoutesSelector.tsx @@ -0,0 +1,67 @@ +import React, { useEffect, useState } from "react"; +import { Select } from "antd"; +import { getPassThroughEndpointsCall } from "../networking"; + +interface PassThroughRoutesSelectorProps { + onChange: (selectedRoutes: string[]) => void; + value?: string[]; + className?: string; + accessToken: string; + placeholder?: string; + disabled?: boolean; +} + +const PassThroughRoutesSelector: React.FC = ({ + onChange, + value, + className, + accessToken, + placeholder = "Select pass through routes", + disabled = false, +}) => { + const [passThroughRoutes, setPassThroughRoutes] = useState([]); + const [loading, setLoading] = useState(false); + + useEffect(() => { + const fetchPassThroughRoutes = async () => { + if (!accessToken) return; + + setLoading(true); + try { + const response = await getPassThroughEndpointsCall(accessToken); + if (response.endpoints) { + const routes = response.endpoints.map((route: { path: string }) => route.path); + setPassThroughRoutes(routes); + } + } catch (error) { + console.error("Error fetching pass through routes:", error); + } finally { + setLoading(false); + } + }; + + fetchPassThroughRoutes(); + }, [accessToken]); + + return ( + form.setFieldValue("allowed_passthrough_routes", values)} + value={form.getFieldValue("allowed_passthrough_routes")} + accessToken={accessToken} placeholder={ !premiumUser ? "Premium feature - Upgrade to set pass through routes by key" : "Select or enter pass through routes" } - options={passThroughRoutesList.map((name) => ({ value: name, label: name }))} + disabled={!premiumUser} /> = ({ /> + + form.setFieldValue("allowed_passthrough_routes", values)} + value={form.getFieldValue("allowed_passthrough_routes")} + accessToken={accessToken || ""} + placeholder="Select pass through routes" + /> + + form.setFieldValue("mcp_servers_and_groups", val)} diff --git a/ui/litellm-dashboard/src/components/teams.tsx b/ui/litellm-dashboard/src/components/teams.tsx index 590e0952944..a7399ba4230 100644 --- a/ui/litellm-dashboard/src/components/teams.tsx +++ b/ui/litellm-dashboard/src/components/teams.tsx @@ -63,6 +63,7 @@ import AvailableTeamsPanel from "@/components/team/available_teams"; import VectorStoreSelector from "./vector_store_management/VectorStoreSelector"; import PremiumLoggingSettings from "./common_components/PremiumLoggingSettings"; import type { KeyResponse, Team } from "./key_team_helpers/key_list"; +import PassThroughRoutesSelector from "./common_components/PassThroughRoutesSelector"; import { formatNumberWithCommas } from "../utils/dataUtils"; import { AlertTriangleIcon, XIcon } from "lucide-react"; import MCPServerSelector from "./mcp_server_management/MCPServerSelector"; @@ -1251,6 +1252,26 @@ const Teams: React.FC = ({ placeholder="Select vector stores (optional)" /> + + Allowed Pass Through Routes{" "} + + + + + } + name="allowed_passthrough_routes" + className="mt-8" + help="Select pass through routes this team can access. Leave empty for access to all pass through routes" + > + form.setFieldValue("allowed_passthrough_routes", values)} + value={form.getFieldValue("allowed_passthrough_routes")} + accessToken={accessToken || ""} + placeholder="Select pass through routes (optional)" + /> + 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 04dbee673b4..05c6acfe68f 100644 --- a/ui/litellm-dashboard/src/components/templates/key_edit_view.tsx +++ b/ui/litellm-dashboard/src/components/templates/key_edit_view.tsx @@ -3,7 +3,7 @@ import { Form, Input, Select, Button as AntdButton, Tooltip } from "antd"; import { Button as TremorButton, TextInput } from "@tremor/react"; import { KeyResponse } from "../key_team_helpers/key_list"; import { fetchTeamModels } from "../organisms/create_key_button"; -import { modelAvailableCall, getPromptsList, getPassThroughEndpointsCall } from "../networking"; +import { modelAvailableCall, getPromptsList } from "../networking"; import NumericalInput from "../shared/numerical_input"; import VectorStoreSelector from "../vector_store_management/VectorStoreSelector"; import MCPServerSelector from "../mcp_server_management/MCPServerSelector"; @@ -15,6 +15,7 @@ import { mapInternalToDisplayNames, mapDisplayToInternalNames } from "../callbac import GuardrailSelector from "@/components/guardrails/GuardrailSelector"; import KeyLifecycleSettings from "../common_components/KeyLifecycleSettings"; import RateLimitTypeFormItem from "../common_components/RateLimitTypeFormItem"; +import PassThroughRoutesSelector from "../common_components/PassThroughRoutesSelector"; interface KeyEditViewProps { keyData: KeyResponse; @@ -59,7 +60,6 @@ export function KeyEditView({ const [form] = Form.useForm(); const [userModels, setUserModels] = useState([]); const [promptsList, setPromptsList] = useState([]); - const [passThroughRoutesList, setPassThroughRoutesList] = useState([]); const team = teams?.find((team) => team.team_id === keyData.team_id); const [availableModels, setAvailableModels] = useState([]); const [mcpAccessGroups, setMcpAccessGroups] = useState([]); @@ -114,19 +114,8 @@ export function KeyEditView({ } }; - const fetchPassThroughRoutes = async () => { - if (!accessToken) return; - try { - const response = await getPassThroughEndpointsCall(accessToken); - setPassThroughRoutesList(response.endpoints.map((route: { path: string }) => route.path)); - } catch (error) { - console.error("Failed to fetch pass through routes:", error); - } - }; - fetchPrompts(); fetchModels(); - fetchPassThroughRoutes(); }, [userID, userRole, accessToken, team, keyData.team_id]); // Sync disabled callbacks with form when component mounts @@ -290,10 +279,10 @@ export function KeyEditView({ -