@@ -18,12 +18,16 @@ import {
18
18
Select as Select2 ,
19
19
message ,
20
20
Tooltip ,
21
+ Alert ,
22
+ Divider ,
23
+ Collapse ,
21
24
} from "antd" ;
22
- import { InfoCircleOutlined , ApiOutlined } from "@ant-design/icons" ;
25
+ import { InfoCircleOutlined , ApiOutlined , ExclamationCircleOutlined , CheckCircleOutlined , CopyOutlined } from "@ant-design/icons" ;
23
26
import { keyCreateCall , slackBudgetAlertsHealthCheck , modelAvailableCall } from "./networking" ;
24
27
import { list } from "postcss" ;
25
28
import KeyValueInput from "./key_value_input" ;
26
29
import { passThroughItem } from "./pass_through_settings" ;
30
+ import RoutePreview from "./route_preview" ;
27
31
const { Option } = Select2 ;
28
32
29
33
interface AddFallbacksProps {
@@ -40,11 +44,28 @@ const AddPassThroughEndpoint: React.FC<AddFallbacksProps> = ({
40
44
const [ isModalVisible , setIsModalVisible ] = useState ( false ) ;
41
45
const [ isLoading , setIsLoading ] = useState ( false ) ;
42
46
const [ selectedModel , setSelectedModel ] = useState ( "" ) ;
47
+ const [ pathValue , setPathValue ] = useState ( "" ) ;
48
+ const [ targetValue , setTargetValue ] = useState ( "" ) ;
49
+ const [ includeSubpath , setIncludeSubpath ] = useState ( true ) ;
50
+
43
51
const handleCancel = ( ) => {
44
52
form . resetFields ( ) ;
53
+ setPathValue ( "" ) ;
54
+ setTargetValue ( "" ) ;
55
+ setIncludeSubpath ( true ) ;
45
56
setIsModalVisible ( false ) ;
46
57
} ;
47
58
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
+
48
69
const addPassThrough = async ( formValues : Record < string , any > ) => {
49
70
setIsLoading ( true ) ;
50
71
try {
@@ -65,6 +86,9 @@ const AddPassThroughEndpoint: React.FC<AddFallbacksProps> = ({
65
86
66
87
message . success ( "Pass-through endpoint created successfully" ) ;
67
88
form . resetFields ( ) ;
89
+ setPathValue ( "" ) ;
90
+ setTargetValue ( "" ) ;
91
+ setIncludeSubpath ( true ) ;
68
92
setIsModalVisible ( false ) ;
69
93
} catch ( error ) {
70
94
message . error ( "Error creating pass-through endpoint: " + error , 20 ) ;
@@ -73,6 +97,12 @@ const AddPassThroughEndpoint: React.FC<AddFallbacksProps> = ({
73
97
}
74
98
} ;
75
99
100
+ const copyToClipboard = ( text : string ) => {
101
+ navigator . clipboard . writeText ( text ) ;
102
+ message . success ( 'Copied to clipboard!' ) ;
103
+ } ;
104
+
105
+
76
106
77
107
return (
78
108
< div >
@@ -100,103 +130,165 @@ const AddPassThroughEndpoint: React.FC<AddFallbacksProps> = ({
100
130
} }
101
131
>
102
132
< 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
+
103
141
< Form
104
142
form = { form }
105
143
onFinish = { addPassThrough }
106
144
layout = "vertical"
107
145
className = "space-y-6"
146
+ initialValues = { { include_subpath : true } }
108
147
>
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 >
127
182
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 >
148
207
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
+
149
239
< Form . Item
150
240
label = {
151
241
< 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 " >
154
244
< InfoCircleOutlined className = "ml-2 text-blue-400 hover:text-blue-600 cursor-help" />
155
245
</ Tooltip >
156
246
</ span >
157
247
}
158
248
name = "headers"
159
249
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 >
172
255
}
173
- name = "include_subpath"
174
- valuePropName = "checked"
175
256
>
176
- < Switch />
257
+ < KeyValueInput />
177
258
</ Form . Item >
259
+ </ Card >
178
260
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
+
179
266
< Form . Item
180
267
label = {
181
268
< span className = "text-sm font-medium text-gray-700 flex items-center" >
182
269
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" >
184
271
< InfoCircleOutlined className = "ml-2 text-gray-400 hover:text-gray-600" />
185
272
</ Tooltip >
186
273
</ span >
187
274
}
188
275
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
+ }
189
281
>
190
282
< InputNumber
191
283
min = { 0 }
192
284
step = { 0.001 }
193
285
precision = { 6 }
194
- placeholder = "0 .000000"
286
+ placeholder = "2 .000000"
195
287
size = "large"
196
288
className = "rounded-lg w-full"
197
289
/>
198
290
</ Form . Item >
199
- </ div >
291
+ </ Card >
200
292
201
293
< div className = "flex items-center justify-end space-x-3 pt-6 border-t border-gray-100" >
202
294
< Button
0 commit comments