Skip to content

Commit 87c2be9

Browse files
authored
[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
1 parent 753ec72 commit 87c2be9

File tree

2 files changed

+261
-59
lines changed

2 files changed

+261
-59
lines changed

ui/litellm-dashboard/src/components/add_pass_through.tsx

Lines changed: 151 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,16 @@ import {
1818
Select as Select2,
1919
message,
2020
Tooltip,
21+
Alert,
22+
Divider,
23+
Collapse,
2124
} from "antd";
22-
import { InfoCircleOutlined, ApiOutlined } from "@ant-design/icons";
25+
import { InfoCircleOutlined, ApiOutlined, ExclamationCircleOutlined, CheckCircleOutlined, CopyOutlined } from "@ant-design/icons";
2326
import { keyCreateCall, slackBudgetAlertsHealthCheck, modelAvailableCall } from "./networking";
2427
import { list } from "postcss";
2528
import KeyValueInput from "./key_value_input";
2629
import { passThroughItem } from "./pass_through_settings";
30+
import RoutePreview from "./route_preview";
2731
const { Option } = Select2;
2832

2933
interface AddFallbacksProps {
@@ -40,11 +44,28 @@ const AddPassThroughEndpoint: React.FC<AddFallbacksProps> = ({
4044
const [isModalVisible, setIsModalVisible] = useState(false);
4145
const [isLoading, setIsLoading] = useState(false);
4246
const [selectedModel, setSelectedModel] = useState("");
47+
const [pathValue, setPathValue] = useState("");
48+
const [targetValue, setTargetValue] = useState("");
49+
const [includeSubpath, setIncludeSubpath] = useState(true);
50+
4351
const handleCancel = () => {
4452
form.resetFields();
53+
setPathValue("");
54+
setTargetValue("");
55+
setIncludeSubpath(true);
4556
setIsModalVisible(false);
4657
};
4758

59+
const handlePathChange = (value: string) => {
60+
// Auto-add leading slash if missing
61+
let formattedPath = value;
62+
if (value && !value.startsWith('/')) {
63+
formattedPath = '/' + value;
64+
}
65+
setPathValue(formattedPath);
66+
form.setFieldsValue({ path: formattedPath });
67+
};
68+
4869
const addPassThrough = async (formValues: Record<string, any>) => {
4970
setIsLoading(true);
5071
try {
@@ -65,6 +86,9 @@ const AddPassThroughEndpoint: React.FC<AddFallbacksProps> = ({
6586

6687
message.success("Pass-through endpoint created successfully");
6788
form.resetFields();
89+
setPathValue("");
90+
setTargetValue("");
91+
setIncludeSubpath(true);
6892
setIsModalVisible(false);
6993
} catch (error) {
7094
message.error("Error creating pass-through endpoint: " + error, 20);
@@ -73,6 +97,12 @@ const AddPassThroughEndpoint: React.FC<AddFallbacksProps> = ({
7397
}
7498
};
7599

100+
const copyToClipboard = (text: string) => {
101+
navigator.clipboard.writeText(text);
102+
message.success('Copied to clipboard!');
103+
};
104+
105+
76106

77107
return (
78108
<div>
@@ -100,103 +130,165 @@ const AddPassThroughEndpoint: React.FC<AddFallbacksProps> = ({
100130
}}
101131
>
102132
<div className="mt-6">
133+
<Alert
134+
message="What is a Pass-Through Endpoint?"
135+
description="Route requests from your LiteLLM proxy to any external API. Perfect for custom models, image generation APIs, or any service you want to proxy through LiteLLM."
136+
type="info"
137+
showIcon
138+
className="mb-6"
139+
/>
140+
103141
<Form
104142
form={form}
105143
onFinish={addPassThrough}
106144
layout="vertical"
107145
className="space-y-6"
146+
initialValues={{ include_subpath: true }}
108147
>
109-
<div className="grid grid-cols-1 gap-6">
110-
<Form.Item
111-
label={
112-
<span className="text-sm font-medium text-gray-700 flex items-center">
113-
Path
114-
<Tooltip title="The route to be added to the LiteLLM Proxy Server (e.g., /my-endpoint)">
115-
<InfoCircleOutlined className="ml-2 text-blue-400 hover:text-blue-600 cursor-help" />
116-
</Tooltip>
117-
</span>
118-
}
119-
name="path"
120-
rules={[{ required: true, message: 'Please enter the endpoint path' }]}
121-
>
122-
<TextInput
123-
placeholder="e.g., /my-endpoint"
124-
className="rounded-lg border-gray-300 focus:border-blue-500 focus:ring-blue-500"
125-
/>
126-
</Form.Item>
148+
{/* Route Configuration Section */}
149+
<Card className="p-5">
150+
<Title className="text-lg font-semibold text-gray-900 mb-2">Route Configuration</Title>
151+
<Subtitle className="text-gray-600 mb-5">Configure how requests to your domain will be forwarded to the target API</Subtitle>
152+
153+
<div className="space-y-5">
154+
<Form.Item
155+
label={
156+
<span className="text-sm font-medium text-gray-700">
157+
Path Prefix <span className="text-red-500">*</span>
158+
</span>
159+
}
160+
name="path"
161+
rules={[
162+
{ required: true, message: '' },
163+
{ pattern: /^\//, message: '' }
164+
]}
165+
extra={
166+
<div className="text-xs text-gray-500 mt-1">
167+
Example: /bria, /openai, /anthropic
168+
</div>
169+
}
170+
className="mb-4"
171+
>
172+
<div className="flex items-center">
173+
<span className="mr-2 text-gray-500 font-mono text-base">/</span>
174+
<TextInput
175+
placeholder="bria"
176+
value={pathValue.startsWith('/') ? pathValue.slice(1) : pathValue}
177+
onChange={(e) => handlePathChange(e.target.value)}
178+
className="flex-1"
179+
/>
180+
</div>
181+
</Form.Item>
127182

128-
<Form.Item
129-
label={
130-
<span className="text-sm font-medium text-gray-700 flex items-center">
131-
Target URL
132-
<Tooltip title="The URL to which requests for this path should be forwarded">
133-
<InfoCircleOutlined className="ml-2 text-blue-400 hover:text-blue-600 cursor-help" />
134-
</Tooltip>
135-
</span>
136-
}
137-
name="target"
138-
rules={[
139-
{ required: true, message: 'Please enter the target URL' },
140-
{ type: 'url', message: 'Please enter a valid URL' }
141-
]}
142-
>
143-
<TextInput
144-
placeholder="https://your-service.com/api"
145-
className="rounded-lg border-gray-300 focus:border-blue-500 focus:ring-blue-500"
146-
/>
147-
</Form.Item>
183+
<Form.Item
184+
label={
185+
<span className="text-sm font-medium text-gray-700">
186+
Target URL <span className="text-red-500">*</span>
187+
</span>
188+
}
189+
name="target"
190+
rules={[
191+
{ required: true, message: '' },
192+
{ type: 'url', message: '' }
193+
]}
194+
extra={
195+
<div className="text-xs text-gray-500 mt-1">
196+
Example: https://api.openai.com, https://engine.prod.bria-api.com
197+
</div>
198+
}
199+
className="mb-4"
200+
>
201+
<TextInput
202+
placeholder="https://engine.prod.bria-api.com"
203+
value={targetValue}
204+
onChange={(e) => setTargetValue(e.target.value)}
205+
/>
206+
</Form.Item>
148207

208+
<div className="flex items-center justify-between py-3">
209+
<div>
210+
<div className="text-sm font-medium text-gray-700">Include Subpaths</div>
211+
<div className="text-xs text-gray-500 mt-0.5">Forward all subpaths to the target API (recommended for REST APIs)</div>
212+
</div>
213+
<Form.Item
214+
name="include_subpath"
215+
valuePropName="checked"
216+
className="mb-0"
217+
>
218+
<Switch
219+
checked={includeSubpath}
220+
onChange={setIncludeSubpath}
221+
/>
222+
</Form.Item>
223+
</div>
224+
</div>
225+
</Card>
226+
227+
{/* Route Preview Section */}
228+
<RoutePreview
229+
pathValue={pathValue}
230+
targetValue={targetValue}
231+
includeSubpath={includeSubpath}
232+
/>
233+
234+
{/* Headers Section */}
235+
<Card className="p-6">
236+
<Title className="text-lg font-semibold text-gray-900 mb-2">Headers</Title>
237+
<Subtitle className="text-gray-600 mb-6">Add headers that will be sent with every request to the target API</Subtitle>
238+
149239
<Form.Item
150240
label={
151241
<span className="text-sm font-medium text-gray-700 flex items-center">
152-
Headers
153-
<Tooltip title="Key-value pairs of headers to be forwarded with the request">
242+
Authentication Headers
243+
<Tooltip title="Authentication and other headers to forward with requests">
154244
<InfoCircleOutlined className="ml-2 text-blue-400 hover:text-blue-600 cursor-help" />
155245
</Tooltip>
156246
</span>
157247
}
158248
name="headers"
159249
rules={[{ required: true, message: 'Please configure the headers' }]}
160-
>
161-
<KeyValueInput/>
162-
</Form.Item>
163-
164-
<Form.Item
165-
label={
166-
<span className="text-sm font-medium text-gray-700 flex items-center">
167-
Include Subpath
168-
<Tooltip title="If enabled, requests to subpaths will also be forwarded to the target endpoint">
169-
<InfoCircleOutlined className="ml-2 text-gray-400 hover:text-gray-600" />
170-
</Tooltip>
171-
</span>
250+
extra={
251+
<div className="text-xs text-gray-500 mt-2">
252+
<div className="font-medium mb-1">Add authentication tokens and other required headers</div>
253+
<div>Common examples: auth_token, Authorization, x-api-key</div>
254+
</div>
172255
}
173-
name="include_subpath"
174-
valuePropName="checked"
175256
>
176-
<Switch />
257+
<KeyValueInput/>
177258
</Form.Item>
259+
</Card>
178260

261+
{/* Billing Section */}
262+
<Card className="p-6">
263+
<Title className="text-lg font-semibold text-gray-900 mb-2">Billing</Title>
264+
<Subtitle className="text-gray-600 mb-6">Optional cost tracking for this endpoint</Subtitle>
265+
179266
<Form.Item
180267
label={
181268
<span className="text-sm font-medium text-gray-700 flex items-center">
182269
Cost Per Request (USD)
183-
<Tooltip title="The cost in USD per request to the target endpoint">
270+
<Tooltip title="Optional: Track costs for requests to this endpoint">
184271
<InfoCircleOutlined className="ml-2 text-gray-400 hover:text-gray-600" />
185272
</Tooltip>
186273
</span>
187274
}
188275
name="cost_per_request"
276+
extra={
277+
<div className="text-xs text-gray-500 mt-2">
278+
The cost charged for each request through this endpoint
279+
</div>
280+
}
189281
>
190282
<InputNumber
191283
min={0}
192284
step={0.001}
193285
precision={6}
194-
placeholder="0.000000"
286+
placeholder="2.000000"
195287
size="large"
196288
className="rounded-lg w-full"
197289
/>
198290
</Form.Item>
199-
</div>
291+
</Card>
200292

201293
<div className="flex items-center justify-end space-x-3 pt-6 border-t border-gray-100">
202294
<Button

0 commit comments

Comments
 (0)