diff --git a/ui/litellm-dashboard/src/components/add_pass_through.tsx b/ui/litellm-dashboard/src/components/add_pass_through.tsx index ff724e42eb..07850bca08 100644 --- a/ui/litellm-dashboard/src/components/add_pass_through.tsx +++ b/ui/litellm-dashboard/src/components/add_pass_through.tsx @@ -30,6 +30,8 @@ import PassThroughSecuritySection from "./common_components/PassThroughSecurityS import PassThroughGuardrailsSection from "./common_components/PassThroughGuardrailsSection"; const { Option } = Select2; +const HTTP_METHODS = ["GET", "POST", "PUT", "DELETE", "PATCH"]; + interface AddFallbacksProps { // models: string[] | undefined; accessToken: string; @@ -52,12 +54,14 @@ const AddPassThroughEndpoint: React.FC = ({ const [targetValue, setTargetValue] = useState(""); const [includeSubpath, setIncludeSubpath] = useState(true); const [authEnabled, setAuthEnabled] = useState(false); + const [selectedMethods, setSelectedMethods] = useState([]); const [guardrails, setGuardrails] = useState>({}); const handleCancel = () => { form.resetFields(); setPathValue(""); setTargetValue(""); setIncludeSubpath(true); + setSelectedMethods([]); setGuardrails({}); setIsModalVisible(false); }; @@ -86,6 +90,11 @@ const AddPassThroughEndpoint: React.FC = ({ formValues.guardrails = guardrails; } + // Add methods to formValues (only if specific methods are selected) + if (selectedMethods && selectedMethods.length > 0) { + formValues.methods = selectedMethods; + } + console.log(`formValues: ${JSON.stringify(formValues)}`); const response = await createPassThroughEndpoint(accessToken, formValues); @@ -101,6 +110,7 @@ const AddPassThroughEndpoint: React.FC = ({ setPathValue(""); setTargetValue(""); setIncludeSubpath(true); + setSelectedMethods([]); setGuardrails({}); setIsModalVisible(false); } catch (error) { @@ -204,6 +214,41 @@ const AddPassThroughEndpoint: React.FC = ({ /> + + HTTP Methods (Optional) + + + + + } + name="methods" + extra={ +
+ {selectedMethods.length === 0 + ? "All HTTP methods supported (default)" + : `Only ${selectedMethods.join(", ")} requests will be routed to this endpoint`} +
+ } + className="mb-4" + > + + {HTTP_METHODS.map((method) => ( + + ))} + +
+
Include Subpaths
diff --git a/ui/litellm-dashboard/src/components/common_components/PassThroughRoutesSelector.tsx b/ui/litellm-dashboard/src/components/common_components/PassThroughRoutesSelector.tsx index 10482dffb2..d80e3790c3 100644 --- a/ui/litellm-dashboard/src/components/common_components/PassThroughRoutesSelector.tsx +++ b/ui/litellm-dashboard/src/components/common_components/PassThroughRoutesSelector.tsx @@ -12,6 +12,11 @@ interface PassThroughRoutesSelectorProps { teamId?: string | null; } +interface PassThroughEndpoint { + path: string; + methods?: string[]; +} + const PassThroughRoutesSelector: React.FC = ({ onChange, value, @@ -21,7 +26,7 @@ const PassThroughRoutesSelector: React.FC = ({ disabled = false, teamId, }) => { - const [passThroughRoutes, setPassThroughRoutes] = useState([]); + const [passThroughRoutes, setPassThroughRoutes] = useState>([]); const [loading, setLoading] = useState(false); useEffect(() => { @@ -32,7 +37,24 @@ const PassThroughRoutesSelector: React.FC = ({ try { const response = await getPassThroughEndpointsCall(accessToken, teamId); if (response.endpoints) { - const routes = response.endpoints.map((route: { path: string }) => route.path); + const routes = response.endpoints.flatMap((endpoint: PassThroughEndpoint) => { + const path = endpoint.path; + const methods = endpoint.methods; + + // If methods are specified, create one entry per method + if (methods && methods.length > 0) { + return methods.map((method) => ({ + label: `${method} ${path}`, + value: path, // Keep value as path for backward compatibility + })); + } + + // If no methods specified, show just the path (all methods supported) + return [{ + label: path, + value: path, + }]; + }); setPassThroughRoutes(routes); } } catch (error) { @@ -54,10 +76,7 @@ const PassThroughRoutesSelector: React.FC = ({ loading={loading} className={className} allowClear - options={passThroughRoutes.map((route) => ({ - label: route, - value: route, - }))} + options={passThroughRoutes} optionFilterProp="label" showSearch style={{ width: "100%" }} diff --git a/ui/litellm-dashboard/src/components/pass_through_info.tsx b/ui/litellm-dashboard/src/components/pass_through_info.tsx index fe3b204b50..9a8d2a3a07 100644 --- a/ui/litellm-dashboard/src/components/pass_through_info.tsx +++ b/ui/litellm-dashboard/src/components/pass_through_info.tsx @@ -13,7 +13,7 @@ import { TabPanels, TextInput, } from "@tremor/react"; -import { Button, Form, Input, Switch, InputNumber } from "antd"; +import { Button, Form, Input, Switch, InputNumber, Select } from "antd"; import { updatePassThroughEndpoint, deletePassThroughEndpointsCall } from "./networking"; import { Eye, EyeOff } from "lucide-react"; import RoutePreview from "./route_preview"; @@ -21,6 +21,9 @@ import NotificationsManager from "./molecules/notifications_manager"; import PassThroughSecuritySection from "./common_components/PassThroughSecuritySection"; import PassThroughGuardrailsSection from "./common_components/PassThroughGuardrailsSection"; +const HTTP_METHODS = ["GET", "POST", "PUT", "DELETE", "PATCH"]; +const { Option } = Select; + export interface PassThroughInfoProps { endpointData: PassThroughEndpoint; onClose: () => void; @@ -38,6 +41,7 @@ interface PassThroughEndpoint { include_subpath?: boolean; cost_per_request?: number; auth?: boolean; + methods?: string[]; guardrails?: Record; } @@ -70,6 +74,7 @@ const PassThroughInfoView: React.FC = ({ const [loading, setLoading] = useState(false); const [isEditing, setIsEditing] = useState(false); const [authEnabled, setAuthEnabled] = useState(initialEndpointData?.auth || false); + const [selectedMethods, setSelectedMethods] = useState(initialEndpointData?.methods || []); const [guardrails, setGuardrails] = useState>( initialEndpointData?.guardrails || {} ); @@ -97,6 +102,7 @@ const PassThroughInfoView: React.FC = ({ include_subpath: values.include_subpath, cost_per_request: values.cost_per_request, auth: premiumUser ? values.auth : undefined, + methods: selectedMethods && selectedMethods.length > 0 ? selectedMethods : undefined, guardrails: guardrails && Object.keys(guardrails).length > 0 ? guardrails : undefined, }; @@ -191,6 +197,23 @@ const PassThroughInfoView: React.FC = ({ {endpointData.auth ? "Auth Required" : "No Auth"}
+ {endpointData.methods && endpointData.methods.length > 0 && ( +
+ HTTP Methods: +
+ {endpointData.methods.map((method) => ( + + {method} + + ))} +
+
+ )} + {(!endpointData.methods || endpointData.methods.length === 0) && ( +
+ All HTTP methods supported +
+ )} {endpointData.cost_per_request !== undefined && (
Cost per request: ${endpointData.cost_per_request} @@ -277,6 +300,7 @@ const PassThroughInfoView: React.FC = ({ include_subpath: endpointData.include_subpath || false, cost_per_request: endpointData.cost_per_request, auth: endpointData.auth || false, + methods: endpointData.methods || [], }} layout="vertical" > @@ -295,6 +319,31 @@ const PassThroughInfoView: React.FC = ({ /> + + + + diff --git a/ui/litellm-dashboard/src/components/pass_through_settings.tsx b/ui/litellm-dashboard/src/components/pass_through_settings.tsx index b37d3df7e4..4c2de26638 100644 --- a/ui/litellm-dashboard/src/components/pass_through_settings.tsx +++ b/ui/litellm-dashboard/src/components/pass_through_settings.tsx @@ -39,6 +39,7 @@ export interface passThroughItem { include_subpath?: boolean; cost_per_request?: number; auth?: boolean; + methods?: string[]; guardrails?: Record; } @@ -147,6 +148,32 @@ const PassThroughSettings: React.FC = ({ accessToken, accessorKey: "target", cell: (info: any) => {info.getValue()}, }, + { + header: () => ( +
+ Methods + + + +
+ ), + accessorKey: "methods", + cell: (info: any) => { + const methods = info.getValue(); + if (!methods || methods.length === 0) { + return ALL; + } + return ( +
+ {methods.map((method: string) => ( + + {method} + + ))} +
+ ); + }, + }, { header: () => (