mirror of
https://github.com/tiennm99/litellm.git
synced 2026-08-18 08:25:10 +00:00
[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
This commit is contained in:
@@ -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<AddFallbacksProps> = ({
|
||||
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<string, any>) => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
@@ -65,6 +86,9 @@ const AddPassThroughEndpoint: React.FC<AddFallbacksProps> = ({
|
||||
|
||||
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<AddFallbacksProps> = ({
|
||||
}
|
||||
};
|
||||
|
||||
const copyToClipboard = (text: string) => {
|
||||
navigator.clipboard.writeText(text);
|
||||
message.success('Copied to clipboard!');
|
||||
};
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<div>
|
||||
@@ -100,103 +130,165 @@ const AddPassThroughEndpoint: React.FC<AddFallbacksProps> = ({
|
||||
}}
|
||||
>
|
||||
<div className="mt-6">
|
||||
<Alert
|
||||
message="What is a Pass-Through Endpoint?"
|
||||
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."
|
||||
type="info"
|
||||
showIcon
|
||||
className="mb-6"
|
||||
/>
|
||||
|
||||
<Form
|
||||
form={form}
|
||||
onFinish={addPassThrough}
|
||||
layout="vertical"
|
||||
className="space-y-6"
|
||||
initialValues={{ include_subpath: true }}
|
||||
>
|
||||
<div className="grid grid-cols-1 gap-6">
|
||||
<Form.Item
|
||||
label={
|
||||
<span className="text-sm font-medium text-gray-700 flex items-center">
|
||||
Path
|
||||
<Tooltip title="The route to be added to the LiteLLM Proxy Server (e.g., /my-endpoint)">
|
||||
<InfoCircleOutlined className="ml-2 text-blue-400 hover:text-blue-600 cursor-help" />
|
||||
</Tooltip>
|
||||
</span>
|
||||
}
|
||||
name="path"
|
||||
rules={[{ required: true, message: 'Please enter the endpoint path' }]}
|
||||
>
|
||||
<TextInput
|
||||
placeholder="e.g., /my-endpoint"
|
||||
className="rounded-lg border-gray-300 focus:border-blue-500 focus:ring-blue-500"
|
||||
/>
|
||||
</Form.Item>
|
||||
{/* Route Configuration Section */}
|
||||
<Card className="p-5">
|
||||
<Title className="text-lg font-semibold text-gray-900 mb-2">Route Configuration</Title>
|
||||
<Subtitle className="text-gray-600 mb-5">Configure how requests to your domain will be forwarded to the target API</Subtitle>
|
||||
|
||||
<div className="space-y-5">
|
||||
<Form.Item
|
||||
label={
|
||||
<span className="text-sm font-medium text-gray-700">
|
||||
Path Prefix <span className="text-red-500">*</span>
|
||||
</span>
|
||||
}
|
||||
name="path"
|
||||
rules={[
|
||||
{ required: true, message: '' },
|
||||
{ pattern: /^\//, message: '' }
|
||||
]}
|
||||
extra={
|
||||
<div className="text-xs text-gray-500 mt-1">
|
||||
Example: /bria, /openai, /anthropic
|
||||
</div>
|
||||
}
|
||||
className="mb-4"
|
||||
>
|
||||
<div className="flex items-center">
|
||||
<span className="mr-2 text-gray-500 font-mono text-base">/</span>
|
||||
<TextInput
|
||||
placeholder="bria"
|
||||
value={pathValue.startsWith('/') ? pathValue.slice(1) : pathValue}
|
||||
onChange={(e) => handlePathChange(e.target.value)}
|
||||
className="flex-1"
|
||||
/>
|
||||
</div>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label={
|
||||
<span className="text-sm font-medium text-gray-700 flex items-center">
|
||||
Target URL
|
||||
<Tooltip title="The URL to which requests for this path should be forwarded">
|
||||
<InfoCircleOutlined className="ml-2 text-blue-400 hover:text-blue-600 cursor-help" />
|
||||
</Tooltip>
|
||||
</span>
|
||||
}
|
||||
name="target"
|
||||
rules={[
|
||||
{ required: true, message: 'Please enter the target URL' },
|
||||
{ type: 'url', message: 'Please enter a valid URL' }
|
||||
]}
|
||||
>
|
||||
<TextInput
|
||||
placeholder="https://your-service.com/api"
|
||||
className="rounded-lg border-gray-300 focus:border-blue-500 focus:ring-blue-500"
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={
|
||||
<span className="text-sm font-medium text-gray-700">
|
||||
Target URL <span className="text-red-500">*</span>
|
||||
</span>
|
||||
}
|
||||
name="target"
|
||||
rules={[
|
||||
{ required: true, message: '' },
|
||||
{ type: 'url', message: '' }
|
||||
]}
|
||||
extra={
|
||||
<div className="text-xs text-gray-500 mt-1">
|
||||
Example: https://api.openai.com, https://engine.prod.bria-api.com
|
||||
</div>
|
||||
}
|
||||
className="mb-4"
|
||||
>
|
||||
<TextInput
|
||||
placeholder="https://engine.prod.bria-api.com"
|
||||
value={targetValue}
|
||||
onChange={(e) => setTargetValue(e.target.value)}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<div className="flex items-center justify-between py-3">
|
||||
<div>
|
||||
<div className="text-sm font-medium text-gray-700">Include Subpaths</div>
|
||||
<div className="text-xs text-gray-500 mt-0.5">Forward all subpaths to the target API (recommended for REST APIs)</div>
|
||||
</div>
|
||||
<Form.Item
|
||||
name="include_subpath"
|
||||
valuePropName="checked"
|
||||
className="mb-0"
|
||||
>
|
||||
<Switch
|
||||
checked={includeSubpath}
|
||||
onChange={setIncludeSubpath}
|
||||
/>
|
||||
</Form.Item>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{/* Route Preview Section */}
|
||||
<RoutePreview
|
||||
pathValue={pathValue}
|
||||
targetValue={targetValue}
|
||||
includeSubpath={includeSubpath}
|
||||
/>
|
||||
|
||||
{/* Headers Section */}
|
||||
<Card className="p-6">
|
||||
<Title className="text-lg font-semibold text-gray-900 mb-2">Headers</Title>
|
||||
<Subtitle className="text-gray-600 mb-6">Add headers that will be sent with every request to the target API</Subtitle>
|
||||
|
||||
<Form.Item
|
||||
label={
|
||||
<span className="text-sm font-medium text-gray-700 flex items-center">
|
||||
Headers
|
||||
<Tooltip title="Key-value pairs of headers to be forwarded with the request">
|
||||
Authentication Headers
|
||||
<Tooltip title="Authentication and other headers to forward with requests">
|
||||
<InfoCircleOutlined className="ml-2 text-blue-400 hover:text-blue-600 cursor-help" />
|
||||
</Tooltip>
|
||||
</span>
|
||||
}
|
||||
name="headers"
|
||||
rules={[{ required: true, message: 'Please configure the headers' }]}
|
||||
extra={
|
||||
<div className="text-xs text-gray-500 mt-2">
|
||||
<div className="font-medium mb-1">Add authentication tokens and other required headers</div>
|
||||
<div>Common examples: auth_token, Authorization, x-api-key</div>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<KeyValueInput/>
|
||||
</Form.Item>
|
||||
</Card>
|
||||
|
||||
<Form.Item
|
||||
label={
|
||||
<span className="text-sm font-medium text-gray-700 flex items-center">
|
||||
Include Subpath
|
||||
<Tooltip title="If enabled, requests to subpaths will also be forwarded to the target endpoint">
|
||||
<InfoCircleOutlined className="ml-2 text-gray-400 hover:text-gray-600" />
|
||||
</Tooltip>
|
||||
</span>
|
||||
}
|
||||
name="include_subpath"
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
|
||||
{/* Billing Section */}
|
||||
<Card className="p-6">
|
||||
<Title className="text-lg font-semibold text-gray-900 mb-2">Billing</Title>
|
||||
<Subtitle className="text-gray-600 mb-6">Optional cost tracking for this endpoint</Subtitle>
|
||||
|
||||
<Form.Item
|
||||
label={
|
||||
<span className="text-sm font-medium text-gray-700 flex items-center">
|
||||
Cost Per Request (USD)
|
||||
<Tooltip title="The cost in USD per request to the target endpoint">
|
||||
<Tooltip title="Optional: Track costs for requests to this endpoint">
|
||||
<InfoCircleOutlined className="ml-2 text-gray-400 hover:text-gray-600" />
|
||||
</Tooltip>
|
||||
</span>
|
||||
}
|
||||
name="cost_per_request"
|
||||
extra={
|
||||
<div className="text-xs text-gray-500 mt-2">
|
||||
The cost charged for each request through this endpoint
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<InputNumber
|
||||
min={0}
|
||||
step={0.001}
|
||||
precision={6}
|
||||
placeholder="0.000000"
|
||||
placeholder="2.000000"
|
||||
size="large"
|
||||
className="rounded-lg w-full"
|
||||
/>
|
||||
</Form.Item>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<div className="flex items-center justify-end space-x-3 pt-6 border-t border-gray-100">
|
||||
<Button
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
import React from "react";
|
||||
import { Card, Title, Subtitle } from "@tremor/react";
|
||||
import { RightOutlined, InfoCircleOutlined } from "@ant-design/icons";
|
||||
import { getProxyBaseUrl } from "./networking";
|
||||
|
||||
interface RoutePreviewProps {
|
||||
pathValue: string;
|
||||
targetValue: string;
|
||||
includeSubpath: boolean;
|
||||
}
|
||||
|
||||
const RoutePreview: React.FC<RoutePreviewProps> = ({
|
||||
pathValue,
|
||||
targetValue,
|
||||
includeSubpath
|
||||
}) => {
|
||||
const proxyBaseUrl = getProxyBaseUrl();
|
||||
|
||||
const getLiteLLMProxyUrl = () => {
|
||||
return pathValue ? `${proxyBaseUrl}${pathValue}` : '';
|
||||
};
|
||||
|
||||
// Only show if both path and target are provided
|
||||
if (!pathValue || !targetValue) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Card className="p-5">
|
||||
<Title className="text-lg font-semibold text-gray-900 mb-2">Route Preview</Title>
|
||||
<Subtitle className="text-gray-600 mb-5">How your requests will be routed</Subtitle>
|
||||
|
||||
<div className="space-y-5">
|
||||
{/* Basic routing */}
|
||||
<div>
|
||||
<div className="text-base font-semibold text-gray-900 mb-3">Basic routing:</div>
|
||||
<div className="flex items-center gap-4">
|
||||
{/* Your endpoint */}
|
||||
<div className="flex-1 bg-gray-50 border border-gray-200 rounded-lg p-3">
|
||||
<div className="text-sm text-gray-600 mb-2">Your endpoint</div>
|
||||
<code className="font-mono text-sm text-gray-900">{getLiteLLMProxyUrl()}</code>
|
||||
</div>
|
||||
|
||||
{/* Arrow */}
|
||||
<div className="text-gray-400">
|
||||
<RightOutlined className="text-lg" />
|
||||
</div>
|
||||
|
||||
{/* Forwards to */}
|
||||
<div className="flex-1 bg-gray-50 border border-gray-200 rounded-lg p-3">
|
||||
<div className="text-sm text-gray-600 mb-2">Forwards to</div>
|
||||
<code className="font-mono text-sm text-gray-900">{targetValue}</code>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{includeSubpath && (
|
||||
<>
|
||||
{/* With subpaths */}
|
||||
<div>
|
||||
<div className="text-base font-semibold text-gray-900 mb-3">With subpaths:</div>
|
||||
<div className="flex items-center gap-4">
|
||||
{/* Your endpoint + subpath */}
|
||||
<div className="flex-1 bg-gray-50 border border-gray-200 rounded-lg p-3">
|
||||
<div className="text-sm text-gray-600 mb-2">Your endpoint + subpath</div>
|
||||
<code className="font-mono text-sm text-gray-900">
|
||||
{pathValue && `${proxyBaseUrl}${pathValue}`}
|
||||
<span className="text-blue-600">/v1/text-to-image/base/model</span>
|
||||
</code>
|
||||
</div>
|
||||
|
||||
{/* Arrow */}
|
||||
<div className="text-gray-400">
|
||||
<RightOutlined className="text-lg" />
|
||||
</div>
|
||||
|
||||
{/* Forwards to with subpath */}
|
||||
<div className="flex-1 bg-gray-50 border border-gray-200 rounded-lg p-3">
|
||||
<div className="text-sm text-gray-600 mb-2">Forwards to</div>
|
||||
<code className="font-mono text-sm text-gray-900">
|
||||
{targetValue}
|
||||
<span className="text-blue-600">/v1/text-to-image/base/model</span>
|
||||
</code>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Note */}
|
||||
<div className="mt-3 text-sm text-gray-600">
|
||||
Any path after {pathValue} will be appended to the target URL
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{!includeSubpath && (
|
||||
<div className="mt-4 p-3 bg-blue-50 rounded-md border border-blue-200">
|
||||
<div className="flex items-start">
|
||||
<InfoCircleOutlined className="text-blue-500 mt-0.5 mr-2 flex-shrink-0" />
|
||||
<div className="text-sm text-blue-700">
|
||||
<span className="font-medium">Not seeing the routing you wanted?</span> Try enabling "Include Subpaths" above - this allows subroutes like <code className="bg-blue-100 px-1 py-0.5 rounded font-mono text-xs">/api/v1/models</code> to be forwarded automatically.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
export default RoutePreview;
|
||||
Reference in New Issue
Block a user