mirror of
https://github.com/tiennm99/litellm.git
synced 2026-08-10 10:22:42 +00:00
Update Ui for adding method to passthrough endpoints
This commit is contained in:
@@ -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<AddFallbacksProps> = ({
|
||||
const [targetValue, setTargetValue] = useState("");
|
||||
const [includeSubpath, setIncludeSubpath] = useState(true);
|
||||
const [authEnabled, setAuthEnabled] = useState(false);
|
||||
const [selectedMethods, setSelectedMethods] = useState<string[]>([]);
|
||||
const [guardrails, setGuardrails] = useState<Record<string, { request_fields?: string[]; response_fields?: string[] } | null>>({});
|
||||
const handleCancel = () => {
|
||||
form.resetFields();
|
||||
setPathValue("");
|
||||
setTargetValue("");
|
||||
setIncludeSubpath(true);
|
||||
setSelectedMethods([]);
|
||||
setGuardrails({});
|
||||
setIsModalVisible(false);
|
||||
};
|
||||
@@ -86,6 +90,11 @@ const AddPassThroughEndpoint: React.FC<AddFallbacksProps> = ({
|
||||
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<AddFallbacksProps> = ({
|
||||
setPathValue("");
|
||||
setTargetValue("");
|
||||
setIncludeSubpath(true);
|
||||
setSelectedMethods([]);
|
||||
setGuardrails({});
|
||||
setIsModalVisible(false);
|
||||
} catch (error) {
|
||||
@@ -204,6 +214,41 @@ const AddPassThroughEndpoint: React.FC<AddFallbacksProps> = ({
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label={
|
||||
<span className="text-sm font-medium text-gray-700 flex items-center">
|
||||
HTTP Methods (Optional)
|
||||
<Tooltip title="Select specific HTTP methods. Leave empty to support all methods (GET, POST, PUT, DELETE, PATCH). Useful when the same path needs different targets for different methods.">
|
||||
<InfoCircleOutlined className="ml-2 text-blue-400 hover:text-blue-600 cursor-help" />
|
||||
</Tooltip>
|
||||
</span>
|
||||
}
|
||||
name="methods"
|
||||
extra={
|
||||
<div className="text-xs text-gray-500 mt-1">
|
||||
{selectedMethods.length === 0
|
||||
? "All HTTP methods supported (default)"
|
||||
: `Only ${selectedMethods.join(", ")} requests will be routed to this endpoint`}
|
||||
</div>
|
||||
}
|
||||
className="mb-4"
|
||||
>
|
||||
<Select2
|
||||
mode="multiple"
|
||||
placeholder="Select methods (leave empty for all)"
|
||||
value={selectedMethods}
|
||||
onChange={setSelectedMethods}
|
||||
allowClear
|
||||
style={{ width: "100%" }}
|
||||
>
|
||||
{HTTP_METHODS.map((method) => (
|
||||
<Option key={method} value={method}>
|
||||
{method}
|
||||
</Option>
|
||||
))}
|
||||
</Select2>
|
||||
</Form.Item>
|
||||
|
||||
<div className="flex items-center justify-between py-3">
|
||||
<div>
|
||||
<div className="text-sm font-medium text-gray-700">Include Subpaths</div>
|
||||
|
||||
+25
-6
@@ -12,6 +12,11 @@ interface PassThroughRoutesSelectorProps {
|
||||
teamId?: string | null;
|
||||
}
|
||||
|
||||
interface PassThroughEndpoint {
|
||||
path: string;
|
||||
methods?: string[];
|
||||
}
|
||||
|
||||
const PassThroughRoutesSelector: React.FC<PassThroughRoutesSelectorProps> = ({
|
||||
onChange,
|
||||
value,
|
||||
@@ -21,7 +26,7 @@ const PassThroughRoutesSelector: React.FC<PassThroughRoutesSelectorProps> = ({
|
||||
disabled = false,
|
||||
teamId,
|
||||
}) => {
|
||||
const [passThroughRoutes, setPassThroughRoutes] = useState<string[]>([]);
|
||||
const [passThroughRoutes, setPassThroughRoutes] = useState<Array<{ label: string; value: string }>>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -32,7 +37,24 @@ const PassThroughRoutesSelector: React.FC<PassThroughRoutesSelectorProps> = ({
|
||||
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<PassThroughRoutesSelectorProps> = ({
|
||||
loading={loading}
|
||||
className={className}
|
||||
allowClear
|
||||
options={passThroughRoutes.map((route) => ({
|
||||
label: route,
|
||||
value: route,
|
||||
}))}
|
||||
options={passThroughRoutes}
|
||||
optionFilterProp="label"
|
||||
showSearch
|
||||
style={{ width: "100%" }}
|
||||
|
||||
@@ -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<string, { request_fields?: string[]; response_fields?: string[] } | null>;
|
||||
}
|
||||
|
||||
@@ -70,6 +74,7 @@ const PassThroughInfoView: React.FC<PassThroughInfoProps> = ({
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [isEditing, setIsEditing] = useState(false);
|
||||
const [authEnabled, setAuthEnabled] = useState(initialEndpointData?.auth || false);
|
||||
const [selectedMethods, setSelectedMethods] = useState<string[]>(initialEndpointData?.methods || []);
|
||||
const [guardrails, setGuardrails] = useState<Record<string, { request_fields?: string[]; response_fields?: string[] } | null>>(
|
||||
initialEndpointData?.guardrails || {}
|
||||
);
|
||||
@@ -97,6 +102,7 @@ const PassThroughInfoView: React.FC<PassThroughInfoProps> = ({
|
||||
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<PassThroughInfoProps> = ({
|
||||
{endpointData.auth ? "Auth Required" : "No Auth"}
|
||||
</Badge>
|
||||
</div>
|
||||
{endpointData.methods && endpointData.methods.length > 0 && (
|
||||
<div>
|
||||
<Text className="text-xs text-gray-500">HTTP Methods:</Text>
|
||||
<div className="flex flex-wrap gap-1 mt-1">
|
||||
{endpointData.methods.map((method) => (
|
||||
<Badge key={method} color="indigo" size="sm">
|
||||
{method}
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{(!endpointData.methods || endpointData.methods.length === 0) && (
|
||||
<div>
|
||||
<Text className="text-xs text-gray-500">All HTTP methods supported</Text>
|
||||
</div>
|
||||
)}
|
||||
{endpointData.cost_per_request !== undefined && (
|
||||
<div>
|
||||
<Text>Cost per request: ${endpointData.cost_per_request}</Text>
|
||||
@@ -277,6 +300,7 @@ const PassThroughInfoView: React.FC<PassThroughInfoProps> = ({
|
||||
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<PassThroughInfoProps> = ({
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label="HTTP Methods (Optional)"
|
||||
name="methods"
|
||||
extra={
|
||||
selectedMethods.length === 0
|
||||
? "All HTTP methods supported (default)"
|
||||
: `Only ${selectedMethods.join(", ")} requests will be routed to this endpoint`
|
||||
}
|
||||
>
|
||||
<Select
|
||||
mode="multiple"
|
||||
placeholder="Select methods (leave empty for all)"
|
||||
value={selectedMethods}
|
||||
onChange={setSelectedMethods}
|
||||
allowClear
|
||||
style={{ width: "100%" }}
|
||||
>
|
||||
{HTTP_METHODS.map((method) => (
|
||||
<Option key={method} value={method}>
|
||||
{method}
|
||||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label="Include Subpath" name="include_subpath" valuePropName="checked">
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
|
||||
@@ -39,6 +39,7 @@ export interface passThroughItem {
|
||||
include_subpath?: boolean;
|
||||
cost_per_request?: number;
|
||||
auth?: boolean;
|
||||
methods?: string[];
|
||||
guardrails?: Record<string, { request_fields?: string[]; response_fields?: string[] } | null>;
|
||||
}
|
||||
|
||||
@@ -147,6 +148,32 @@ const PassThroughSettings: React.FC<GeneralSettingsPageProps> = ({ accessToken,
|
||||
accessorKey: "target",
|
||||
cell: (info: any) => <Text>{info.getValue()}</Text>,
|
||||
},
|
||||
{
|
||||
header: () => (
|
||||
<div className="flex items-center gap-1">
|
||||
<span>Methods</span>
|
||||
<Tooltip title="HTTP methods supported by this endpoint">
|
||||
<InformationCircleIcon className="w-4 h-4 text-gray-400 cursor-help" />
|
||||
</Tooltip>
|
||||
</div>
|
||||
),
|
||||
accessorKey: "methods",
|
||||
cell: (info: any) => {
|
||||
const methods = info.getValue();
|
||||
if (!methods || methods.length === 0) {
|
||||
return <Badge color="blue">ALL</Badge>;
|
||||
}
|
||||
return (
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{methods.map((method: string) => (
|
||||
<Badge key={method} color="indigo" className="text-xs">
|
||||
{method}
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
header: () => (
|
||||
<div className="flex items-center gap-1">
|
||||
|
||||
Reference in New Issue
Block a user