From 87c2be93c745fa49777d84968ebe8330cae866e4 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Thu, 19 Jun 2025 16:05:29 -0700 Subject: [PATCH] [Feat] V2 Add Pass through endpoints on UI (#11905) * add pass through ui * fix accordion for route path * working route path renderer * fix use sections * clean up add pass through form * docs fix add pass through routing * clean up route preview * add route preview --- .../src/components/add_pass_through.tsx | 210 +++++++++++++----- .../src/components/route_preview.tsx | 110 +++++++++ 2 files changed, 261 insertions(+), 59 deletions(-) create mode 100644 ui/litellm-dashboard/src/components/route_preview.tsx diff --git a/ui/litellm-dashboard/src/components/add_pass_through.tsx b/ui/litellm-dashboard/src/components/add_pass_through.tsx index 800c4ac934..f2e8c22531 100644 --- a/ui/litellm-dashboard/src/components/add_pass_through.tsx +++ b/ui/litellm-dashboard/src/components/add_pass_through.tsx @@ -18,12 +18,16 @@ import { Select as Select2, message, Tooltip, + Alert, + Divider, + Collapse, } from "antd"; -import { InfoCircleOutlined, ApiOutlined } from "@ant-design/icons"; +import { InfoCircleOutlined, ApiOutlined, ExclamationCircleOutlined, CheckCircleOutlined, CopyOutlined } from "@ant-design/icons"; import { keyCreateCall, slackBudgetAlertsHealthCheck, modelAvailableCall } from "./networking"; import { list } from "postcss"; import KeyValueInput from "./key_value_input"; import { passThroughItem } from "./pass_through_settings"; +import RoutePreview from "./route_preview"; const { Option } = Select2; interface AddFallbacksProps { @@ -40,11 +44,28 @@ const AddPassThroughEndpoint: React.FC = ({ const [isModalVisible, setIsModalVisible] = useState(false); const [isLoading, setIsLoading] = useState(false); const [selectedModel, setSelectedModel] = useState(""); + const [pathValue, setPathValue] = useState(""); + const [targetValue, setTargetValue] = useState(""); + const [includeSubpath, setIncludeSubpath] = useState(true); + const handleCancel = () => { form.resetFields(); + setPathValue(""); + setTargetValue(""); + setIncludeSubpath(true); setIsModalVisible(false); }; + const handlePathChange = (value: string) => { + // Auto-add leading slash if missing + let formattedPath = value; + if (value && !value.startsWith('/')) { + formattedPath = '/' + value; + } + setPathValue(formattedPath); + form.setFieldsValue({ path: formattedPath }); + }; + const addPassThrough = async (formValues: Record) => { setIsLoading(true); try { @@ -65,6 +86,9 @@ const AddPassThroughEndpoint: React.FC = ({ message.success("Pass-through endpoint created successfully"); form.resetFields(); + setPathValue(""); + setTargetValue(""); + setIncludeSubpath(true); setIsModalVisible(false); } catch (error) { message.error("Error creating pass-through endpoint: " + error, 20); @@ -73,6 +97,12 @@ const AddPassThroughEndpoint: React.FC = ({ } }; + const copyToClipboard = (text: string) => { + navigator.clipboard.writeText(text); + message.success('Copied to clipboard!'); + }; + + return (
@@ -100,103 +130,165 @@ const AddPassThroughEndpoint: React.FC = ({ }} >
+ +
-
- - Path - - - - - } - name="path" - rules={[{ required: true, message: 'Please enter the endpoint path' }]} - > - - + {/* Route Configuration Section */} + + Route Configuration + Configure how requests to your domain will be forwarded to the target API + +
+ + Path Prefix * + + } + name="path" + rules={[ + { required: true, message: '' }, + { pattern: /^\//, message: '' } + ]} + extra={ +
+ Example: /bria, /openai, /anthropic +
+ } + className="mb-4" + > +
+ / + handlePathChange(e.target.value)} + className="flex-1" + /> +
+
- - Target URL - - - - - } - name="target" - rules={[ - { required: true, message: 'Please enter the target URL' }, - { type: 'url', message: 'Please enter a valid URL' } - ]} - > - - + + Target URL * + + } + name="target" + rules={[ + { required: true, message: '' }, + { type: 'url', message: '' } + ]} + extra={ +
+ Example: https://api.openai.com, https://engine.prod.bria-api.com +
+ } + className="mb-4" + > + setTargetValue(e.target.value)} + /> +
+
+
+
Include Subpaths
+
Forward all subpaths to the target API (recommended for REST APIs)
+
+ + + +
+
+
+ + {/* Route Preview Section */} + + + {/* Headers Section */} + + Headers + Add headers that will be sent with every request to the target API + - Headers - + Authentication Headers + } name="headers" rules={[{ required: true, message: 'Please configure the headers' }]} + extra={ +
+
Add authentication tokens and other required headers
+
Common examples: auth_token, Authorization, x-api-key
+
+ } >
+
- - Include Subpath - - - - - } - name="include_subpath" - valuePropName="checked" - > - - - + {/* Billing Section */} + + Billing + Optional cost tracking for this endpoint + Cost Per Request (USD) - + } name="cost_per_request" + extra={ +
+ The cost charged for each request through this endpoint +
+ } >
-
+