Add Model Form Refactor

This commit is contained in:
yuneng-jiang 2025-12-30 13:30:54 -08:00
parent 04989d2a18
commit 16f663778d
5 changed files with 441 additions and 387 deletions

View file

@ -0,0 +1,18 @@
import { useQuery, UseQueryResult } from "@tanstack/react-query";
import { createQueryKeys } from "../common/queryKeysFactory";
import { getGuardrailsList } from "@/components/networking";
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
const guardrailKeys = createQueryKeys("guardrails");
export const useGuardrails = (): UseQueryResult<string[]> => {
const { accessToken, userId, userRole } = useAuthorized();
return useQuery<string[]>({
queryKey: guardrailKeys.list({}),
queryFn: async () => {
const response = await getGuardrailsList(accessToken!);
return response.guardrails.map((g: { guardrail_name: string }) => g.guardrail_name);
},
enabled: Boolean(accessToken && userId && userRole),
});
};

View file

@ -0,0 +1,16 @@
import { useQuery, UseQueryResult } from "@tanstack/react-query";
import { createQueryKeys } from "../common/queryKeysFactory";
import { tagListCall } from "@/components/networking";
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
import { TagListResponse } from "@/components/tag_management/types";
const tagKeys = createQueryKeys("tags");
export const useTags = (): UseQueryResult<TagListResponse> => {
const { accessToken, userId, userRole } = useAuthorized();
return useQuery<TagListResponse>({
queryKey: tagKeys.list({}),
queryFn: async () => await tagListCall(accessToken!),
enabled: Boolean(accessToken && userId && userRole),
});
};

View file

@ -679,7 +679,6 @@ const ModelsAndEndpointsView: React.FC<ModelDashboardProps> = ({
credentials={credentialsList}
accessToken={accessToken}
userRole={userRole}
premiumUser={premiumUser}
/>
</TabPanel>
)}

View file

@ -0,0 +1,386 @@
import { useProviderFields } from "@/app/(dashboard)/hooks/providers/useProviderFields";
import { useGuardrails } from "@/app/(dashboard)/hooks/guardrails/useGuardrails";
import { useTags } from "@/app/(dashboard)/hooks/tags/useTags";
import { all_admin_roles } from "@/utils/roles";
import { Switch, Text } from "@tremor/react";
import type { FormInstance } from "antd";
import { Select as AntdSelect, Button, Card, Col, Form, Modal, Row, Tooltip, Typography } from "antd";
import type { UploadProps } from "antd/es/upload";
import React, { useEffect, useMemo, useState } from "react";
import TeamDropdown from "../common_components/team_dropdown";
import type { Team } from "../key_team_helpers/key_list";
import { type CredentialItem, type ProviderCreateInfo, modelAvailableCall } from "../networking";
import { Providers, providerLogoMap } from "../provider_info_helpers";
import { ProviderLogo } from "../molecules/models/ProviderLogo";
import AdvancedSettings from "./advanced_settings";
import ConditionalPublicModelName from "./conditional_public_model_name";
import LiteLLMModelNameField from "./litellm_model_name";
import ConnectionErrorDisplay from "./model_connection_test";
import ProviderSpecificFields from "./provider_specific_fields";
import { TEST_MODES } from "./add_model_modes";
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
interface AddModelFormProps {
form: FormInstance; // For the Add Model tab
handleOk: () => void;
selectedProvider: Providers;
setSelectedProvider: (provider: Providers) => void;
providerModels: string[];
setProviderModelsFn: (provider: Providers) => void;
getPlaceholder: (provider: Providers) => string;
uploadProps: UploadProps;
showAdvancedSettings: boolean;
setShowAdvancedSettings: (show: boolean) => void;
teams: Team[] | null;
credentials: CredentialItem[];
}
const { Title, Link } = Typography;
const AddModelForm: React.FC<AddModelFormProps> = ({
form,
handleOk,
selectedProvider,
setSelectedProvider,
providerModels,
setProviderModelsFn,
getPlaceholder,
uploadProps,
showAdvancedSettings,
setShowAdvancedSettings,
teams,
credentials,
}) => {
const [testMode, setTestMode] = useState<string>("chat");
const [isResultModalVisible, setIsResultModalVisible] = useState<boolean>(false);
const [isTestingConnection, setIsTestingConnection] = useState<boolean>(false);
// Using a unique ID to force the ConnectionErrorDisplay to remount and run a fresh test
const [connectionTestId, setConnectionTestId] = useState<string>("");
const { accessToken, userRole, premiumUser } = useAuthorized();
const {
data: providerMetadata,
isLoading: isProviderMetadataLoading,
error: providerMetadataError,
} = useProviderFields();
const { data: guardrailsList, isLoading: isGuardrailsLoading, error: guardrailsError } = useGuardrails();
const { data: tagsList, isLoading: isTagsLoading, error: tagsError } = useTags();
const handleTestConnection = async () => {
setIsTestingConnection(true);
setConnectionTestId(`test-${Date.now()}`);
setIsResultModalVisible(true);
};
const [isTeamOnly, setIsTeamOnly] = useState<boolean>(false);
const [modelAccessGroups, setModelAccessGroups] = useState<string[]>([]);
useEffect(() => {
const fetchModelAccessGroups = async () => {
const response = await modelAvailableCall(accessToken, "", "", false, null, true, true);
setModelAccessGroups(response["data"].map((model: any) => model["id"]));
};
fetchModelAccessGroups();
}, [accessToken]);
const sortedProviderMetadata: ProviderCreateInfo[] = useMemo(() => {
if (!providerMetadata) {
return [];
}
return [...providerMetadata].sort((a, b) => a.provider_display_name.localeCompare(b.provider_display_name));
}, [providerMetadata]);
const providerMetadataErrorText = providerMetadataError
? providerMetadataError instanceof Error
? providerMetadataError.message
: "Failed to load providers"
: null;
const isAdmin = all_admin_roles.includes(userRole);
return (
<>
<Title level={2}>Add Model</Title>
<Card>
<Form
form={form}
onFinish={(values) => {
console.log("🔥 Form onFinish triggered with values:", values);
handleOk();
}}
onFinishFailed={(errorInfo) => {
console.log("💥 Form onFinishFailed triggered:", errorInfo);
}}
labelCol={{ span: 10 }}
wrapperCol={{ span: 16 }}
labelAlign="left"
>
<>
{/* Provider Selection */}
<Form.Item
rules={[{ required: true, message: "Required" }]}
label="Provider:"
name="custom_llm_provider"
tooltip="E.g. OpenAI, Azure OpenAI, Anthropic, Bedrock, etc."
labelCol={{ span: 10 }}
labelAlign="left"
>
<AntdSelect
virtual={false}
showSearch
loading={isProviderMetadataLoading}
placeholder={isProviderMetadataLoading ? "Loading providers..." : "Select a provider"}
optionFilterProp="data-label"
onChange={(value) => {
setSelectedProvider(value as Providers);
setProviderModelsFn(value as Providers);
form.setFieldsValue({
custom_llm_provider: value,
});
form.setFieldsValue({
model: [],
model_name: undefined,
});
}}
>
{providerMetadataErrorText && sortedProviderMetadata.length === 0 && (
<AntdSelect.Option key="__error" value="">
{providerMetadataErrorText}
</AntdSelect.Option>
)}
{sortedProviderMetadata.map((providerInfo) => {
const displayName = providerInfo.provider_display_name;
const providerKey = providerInfo.provider;
const logoSrc = providerLogoMap[displayName] ?? "";
return (
<AntdSelect.Option key={providerKey} value={providerKey} data-label={displayName}>
<div className="flex items-center space-x-2">
<ProviderLogo provider={providerKey} className="w-5 h-5" />
<span>{displayName}</span>
</div>
</AntdSelect.Option>
);
})}
</AntdSelect>
</Form.Item>
<LiteLLMModelNameField
selectedProvider={selectedProvider}
providerModels={providerModels}
getPlaceholder={getPlaceholder}
/>
{/* Conditionally Render "Public Model Name" */}
<ConditionalPublicModelName />
{/* Select Mode */}
<Form.Item label="Mode" name="mode" className="mb-1">
<AntdSelect
style={{ width: "100%" }}
value={testMode}
onChange={(value) => setTestMode(value)}
options={TEST_MODES}
/>
</Form.Item>
<Row>
<Col span={10}></Col>
<Col span={10}>
<Text className="mb-5 mt-1">
<strong>Optional</strong> - LiteLLM endpoint to use when health checking this model{" "}
<Link href="https://docs.litellm.ai/docs/proxy/health#health" target="_blank">
Learn more
</Link>
</Text>
</Col>
</Row>
{/* Credentials */}
<div className="mb-4">
<Typography.Text className="text-sm text-gray-500 mb-2">
Either select existing credentials OR enter new provider credentials below
</Typography.Text>
</div>
<Form.Item label="Existing Credentials" name="litellm_credential_name" initialValue={null}>
<AntdSelect
showSearch
placeholder="Select or search for existing credentials"
optionFilterProp="children"
filterOption={(input, option) => (option?.label ?? "").toLowerCase().includes(input.toLowerCase())}
options={[
{ value: null, label: "None" },
...credentials.map((credential) => ({
value: credential.credential_name,
label: credential.credential_name,
})),
]}
allowClear
/>
</Form.Item>
<Form.Item
noStyle
shouldUpdate={(prevValues, currentValues) =>
prevValues.litellm_credential_name !== currentValues.litellm_credential_name ||
prevValues.provider !== currentValues.provider
}
>
{({ getFieldValue }) => {
const credentialName = getFieldValue("litellm_credential_name");
console.log("🔑 Credential Name Changed:", credentialName);
// Only show provider specific fields if no credentials selected
if (!credentialName) {
return (
<>
<div className="flex items-center my-4">
<div className="flex-grow border-t border-gray-200"></div>
<span className="px-4 text-gray-500 text-sm">OR</span>
<div className="flex-grow border-t border-gray-200"></div>
</div>
<ProviderSpecificFields selectedProvider={selectedProvider} uploadProps={uploadProps} />
</>
);
}
return null;
}}
</Form.Item>
<div className="flex items-center my-4">
<div className="flex-grow border-t border-gray-200"></div>
<span className="px-4 text-gray-500 text-sm">Additional Model Info Settings</span>
<div className="flex-grow border-t border-gray-200"></div>
</div>
{/* Team-only Model Switch */}
<Form.Item
label="Team-BYOK Model"
tooltip="Only use this model + credential combination for this team. Useful when teams want to onboard their own OpenAI keys."
className="mb-4"
>
<Tooltip
title={
!premiumUser
? "This is an enterprise-only feature. Upgrade to premium to restrict model+credential combinations to a specific team."
: ""
}
placement="top"
>
<Switch
checked={isTeamOnly}
onChange={(checked) => {
setIsTeamOnly(checked);
if (!checked) {
form.setFieldValue("team_id", undefined);
}
}}
disabled={!premiumUser}
/>
</Tooltip>
</Form.Item>
{/* Conditional Team Selection */}
{isTeamOnly && (
<Form.Item
label="Select Team"
name="team_id"
className="mb-4"
tooltip="Only keys for this team will be able to call this model."
rules={[
{
required: isTeamOnly && !isAdmin,
message: "Please select a team.",
},
]}
>
<TeamDropdown teams={teams} disabled={!premiumUser} />
</Form.Item>
)}
{isAdmin && (
<>
<Form.Item
label="Model Access Group"
name="model_access_group"
className="mb-4"
tooltip="Use model access groups to give users access to select models, and add new ones to the group over time."
>
<AntdSelect
mode="tags"
showSearch
placeholder="Select existing groups or type to create new ones"
optionFilterProp="children"
tokenSeparators={[","]}
options={modelAccessGroups.map((group) => ({
value: group,
label: group,
}))}
maxTagCount="responsive"
allowClear
/>
</Form.Item>
</>
)}
<AdvancedSettings
showAdvancedSettings={showAdvancedSettings}
setShowAdvancedSettings={setShowAdvancedSettings}
teams={teams}
guardrailsList={guardrailsList || []}
tagsList={tagsList || {}}
/>
<div className="flex justify-between items-center mb-4">
<Tooltip title="Get help on our github">
<Typography.Link href="https://github.com/BerriAI/litellm/issues">Need Help?</Typography.Link>
</Tooltip>
<div className="space-x-2">
<Button onClick={handleTestConnection} loading={isTestingConnection}>
Test Connect
</Button>
<Button htmlType="submit">Add Model</Button>
</div>
</div>
</>
</Form>
</Card>
{/* Test Connection Results Modal */}
<Modal
title="Connection Test Results"
open={isResultModalVisible}
onCancel={() => {
setIsResultModalVisible(false);
setIsTestingConnection(false);
}}
footer={[
<Button
key="close"
onClick={() => {
setIsResultModalVisible(false);
setIsTestingConnection(false);
}}
>
Close
</Button>,
]}
width={700}
>
{/* Only render the ConnectionErrorDisplay when modal is visible and we have a test ID */}
{isResultModalVisible && (
<ConnectionErrorDisplay
// The key prop tells React to create a fresh component instance when it changes
key={connectionTestId}
formValues={form.getFieldsValue()}
accessToken={accessToken}
testMode={testMode}
modelName={form.getFieldValue("model_name") || form.getFieldValue("model")}
onClose={() => {
setIsResultModalVisible(false);
setIsTestingConnection(false);
}}
onTestComplete={() => setIsTestingConnection(false)}
/>
)}
</Modal>
</>
);
};
export default AddModelForm;

View file

@ -1,34 +1,19 @@
import { useProviderFields } from "@/app/(dashboard)/hooks/providers/useProviderFields";
import { all_admin_roles } from "@/utils/roles";
import { Switch, Tab, TabGroup, TabList, TabPanel, TabPanels, Text } from "@tremor/react";
import { Tab, TabGroup, TabList, TabPanel, TabPanels } from "@tremor/react";
import type { FormInstance } from "antd";
import { Select as AntdSelect, Button, Card, Col, Form, Modal, Row, Tooltip, Typography } from "antd";
import { Form } from "antd";
import type { UploadProps } from "antd/es/upload";
import React, { useEffect, useMemo, useState } from "react";
import TeamDropdown from "../common_components/team_dropdown";
import React from "react";
import type { Team } from "../key_team_helpers/key_list";
import {
type CredentialItem,
type ProviderCreateInfo,
getGuardrailsList,
modelAvailableCall,
tagListCall,
} from "../networking";
import { Providers, providerLogoMap } from "../provider_info_helpers";
import { ProviderLogo } from "../molecules/models/ProviderLogo";
import { Tag } from "../tag_management/types";
import { type CredentialItem } from "../networking";
import { Providers } from "../provider_info_helpers";
import AddAutoRouterTab from "./add_auto_router_tab";
import { TEST_MODES } from "./add_model_modes";
import AddModelForm from "./AddModelForm";
import AdvancedSettings from "./advanced_settings";
import ConditionalPublicModelName from "./conditional_public_model_name";
import { handleAddAutoRouterSubmit } from "./handle_add_auto_router_submit";
import LiteLLMModelNameField from "./litellm_model_name";
import ConnectionErrorDisplay from "./model_connection_test";
import ProviderSpecificFields from "./provider_specific_fields";
interface AddModelTabProps {
form: FormInstance; // For the Add Model tab
handleOk: () => void;
handleOk: (values?: any) => void;
selectedProvider: Providers;
setSelectedProvider: (provider: Providers) => void;
providerModels: string[];
@ -41,11 +26,8 @@ interface AddModelTabProps {
credentials: CredentialItem[];
accessToken: string;
userRole: string;
premiumUser: boolean;
}
const { Title, Link } = Typography;
const AddModelTab: React.FC<AddModelTabProps> = ({
form,
handleOk,
@ -61,90 +43,9 @@ const AddModelTab: React.FC<AddModelTabProps> = ({
credentials,
accessToken,
userRole,
premiumUser,
}) => {
// Create separate form instance for auto router
const [autoRouterForm] = Form.useForm();
// State for test mode and connection testing
const [testMode, setTestMode] = useState<string>("chat");
const [isResultModalVisible, setIsResultModalVisible] = useState<boolean>(false);
const [isTestingConnection, setIsTestingConnection] = useState<boolean>(false);
const [guardrailsList, setGuardrailsList] = useState<string[]>([]);
const [tagsList, setTagsList] = useState<Record<string, Tag>>({});
// Using a unique ID to force the ConnectionErrorDisplay to remount and run a fresh test
const [connectionTestId, setConnectionTestId] = useState<string>("");
// Provider metadata for driving the provider select from backend config
const {
data: providerMetadata,
isLoading: isProviderMetadataLoading,
error: providerMetadataError,
} = useProviderFields();
useEffect(() => {
const fetchGuardrails = async () => {
try {
const response = await getGuardrailsList(accessToken);
const guardrailNames = response.guardrails.map((g: { guardrail_name: string }) => g.guardrail_name);
setGuardrailsList(guardrailNames);
} catch (error) {
console.error("Failed to fetch guardrails:", error);
}
};
fetchGuardrails();
}, [accessToken]);
useEffect(() => {
const fetchTags = async () => {
try {
const response = await tagListCall(accessToken);
setTagsList(response);
} catch (error) {
console.error("Failed to fetch tags:", error);
}
};
fetchTags();
}, [accessToken]);
// Test connection when button is clicked
const handleTestConnection = async () => {
setIsTestingConnection(true);
// Generate a new test ID (using timestamp for uniqueness)
// This forces React to create a new instance of ConnectionErrorDisplay
setConnectionTestId(`test-${Date.now()}`);
// Show the modal with the fresh test
setIsResultModalVisible(true);
};
// State for team-only switch
const [isTeamOnly, setIsTeamOnly] = useState<boolean>(false);
const [modelAccessGroups, setModelAccessGroups] = useState<string[]>([]);
useEffect(() => {
const fetchModelAccessGroups = async () => {
const response = await modelAvailableCall(accessToken, "", "", false, null, true, true);
setModelAccessGroups(response["data"].map((model: any) => model["id"]));
};
fetchModelAccessGroups();
}, [accessToken]);
const sortedProviderMetadata: ProviderCreateInfo[] = useMemo(() => {
if (!providerMetadata) {
return [];
}
return [...providerMetadata].sort((a, b) => a.provider_display_name.localeCompare(b.provider_display_name));
}, [providerMetadata]);
const providerMetadataErrorText = providerMetadataError
? providerMetadataError instanceof Error
? providerMetadataError.message
: "Failed to load providers"
: null;
const isAdmin = all_admin_roles.includes(userRole);
const handleAutoRouterOk = () => {
autoRouterForm
@ -166,247 +67,20 @@ const AddModelTab: React.FC<AddModelTabProps> = ({
</TabList>
<TabPanels>
<TabPanel>
<Title level={2}>Add Model</Title>
<Card>
<Form
form={form}
onFinish={(values) => {
console.log("🔥 Form onFinish triggered with values:", values);
handleOk();
}}
onFinishFailed={(errorInfo) => {
console.log("💥 Form onFinishFailed triggered:", errorInfo);
}}
labelCol={{ span: 10 }}
wrapperCol={{ span: 16 }}
labelAlign="left"
>
<>
{/* Provider Selection */}
<Form.Item
rules={[{ required: true, message: "Required" }]}
label="Provider:"
name="custom_llm_provider"
tooltip="E.g. OpenAI, Azure OpenAI, Anthropic, Bedrock, etc."
labelCol={{ span: 10 }}
labelAlign="left"
>
<AntdSelect
virtual={false}
showSearch
loading={isProviderMetadataLoading}
placeholder={isProviderMetadataLoading ? "Loading providers..." : "Select a provider"}
optionFilterProp="data-label"
onChange={(value) => {
setSelectedProvider(value as Providers);
setProviderModelsFn(value as Providers);
form.setFieldsValue({
custom_llm_provider: value,
});
form.setFieldsValue({
model: [],
model_name: undefined,
});
}}
>
{providerMetadataErrorText && sortedProviderMetadata.length === 0 && (
<AntdSelect.Option key="__error" value="">
{providerMetadataErrorText}
</AntdSelect.Option>
)}
{sortedProviderMetadata.map((providerInfo) => {
const displayName = providerInfo.provider_display_name;
const providerKey = providerInfo.provider;
const logoSrc = providerLogoMap[displayName] ?? "";
return (
<AntdSelect.Option key={providerKey} value={providerKey} data-label={displayName}>
<div className="flex items-center space-x-2">
<ProviderLogo provider={providerKey} className="w-5 h-5" />
<span>{displayName}</span>
</div>
</AntdSelect.Option>
);
})}
</AntdSelect>
</Form.Item>
<LiteLLMModelNameField
selectedProvider={selectedProvider}
providerModels={providerModels}
getPlaceholder={getPlaceholder}
/>
{/* Conditionally Render "Public Model Name" */}
<ConditionalPublicModelName />
{/* Select Mode */}
<Form.Item label="Mode" name="mode" className="mb-1">
<AntdSelect
style={{ width: "100%" }}
value={testMode}
onChange={(value) => setTestMode(value)}
options={TEST_MODES}
/>
</Form.Item>
<Row>
<Col span={10}></Col>
<Col span={10}>
<Text className="mb-5 mt-1">
<strong>Optional</strong> - LiteLLM endpoint to use when health checking this model{" "}
<Link href="https://docs.litellm.ai/docs/proxy/health#health" target="_blank">
Learn more
</Link>
</Text>
</Col>
</Row>
{/* Credentials */}
<div className="mb-4">
<Typography.Text className="text-sm text-gray-500 mb-2">
Either select existing credentials OR enter new provider credentials below
</Typography.Text>
</div>
<Form.Item label="Existing Credentials" name="litellm_credential_name" initialValue={null}>
<AntdSelect
showSearch
placeholder="Select or search for existing credentials"
optionFilterProp="children"
filterOption={(input, option) =>
(option?.label ?? "").toLowerCase().includes(input.toLowerCase())
}
options={[
{ value: null, label: "None" },
...credentials.map((credential) => ({
value: credential.credential_name,
label: credential.credential_name,
})),
]}
allowClear
/>
</Form.Item>
<Form.Item
noStyle
shouldUpdate={(prevValues, currentValues) =>
prevValues.litellm_credential_name !== currentValues.litellm_credential_name ||
prevValues.provider !== currentValues.provider
}
>
{({ getFieldValue }) => {
const credentialName = getFieldValue("litellm_credential_name");
console.log("🔑 Credential Name Changed:", credentialName);
// Only show provider specific fields if no credentials selected
if (!credentialName) {
return (
<>
<div className="flex items-center my-4">
<div className="flex-grow border-t border-gray-200"></div>
<span className="px-4 text-gray-500 text-sm">OR</span>
<div className="flex-grow border-t border-gray-200"></div>
</div>
<ProviderSpecificFields selectedProvider={selectedProvider} uploadProps={uploadProps} />
</>
);
}
return null;
}}
</Form.Item>
<div className="flex items-center my-4">
<div className="flex-grow border-t border-gray-200"></div>
<span className="px-4 text-gray-500 text-sm">Additional Model Info Settings</span>
<div className="flex-grow border-t border-gray-200"></div>
</div>
{/* Team-only Model Switch */}
<Form.Item
label="Team-BYOK Model"
tooltip="Only use this model + credential combination for this team. Useful when teams want to onboard their own OpenAI keys."
className="mb-4"
>
<Tooltip
title={
!premiumUser
? "This is an enterprise-only feature. Upgrade to premium to restrict model+credential combinations to a specific team."
: ""
}
placement="top"
>
<Switch
checked={isTeamOnly}
onChange={(checked) => {
setIsTeamOnly(checked);
if (!checked) {
form.setFieldValue("team_id", undefined);
}
}}
disabled={!premiumUser}
/>
</Tooltip>
</Form.Item>
{/* Conditional Team Selection */}
{isTeamOnly && (
<Form.Item
label="Select Team"
name="team_id"
className="mb-4"
tooltip="Only keys for this team will be able to call this model."
rules={[
{
required: isTeamOnly && !isAdmin,
message: "Please select a team.",
},
]}
>
<TeamDropdown teams={teams} disabled={!premiumUser} />
</Form.Item>
)}
{isAdmin && (
<>
<Form.Item
label="Model Access Group"
name="model_access_group"
className="mb-4"
tooltip="Use model access groups to give users access to select models, and add new ones to the group over time."
>
<AntdSelect
mode="tags"
showSearch
placeholder="Select existing groups or type to create new ones"
optionFilterProp="children"
tokenSeparators={[","]}
options={modelAccessGroups.map((group) => ({
value: group,
label: group,
}))}
maxTagCount="responsive"
allowClear
/>
</Form.Item>
</>
)}
<AdvancedSettings
showAdvancedSettings={showAdvancedSettings}
setShowAdvancedSettings={setShowAdvancedSettings}
teams={teams}
guardrailsList={guardrailsList}
tagsList={tagsList}
/>
<div className="flex justify-between items-center mb-4">
<Tooltip title="Get help on our github">
<Typography.Link href="https://github.com/BerriAI/litellm/issues">Need Help?</Typography.Link>
</Tooltip>
<div className="space-x-2">
<Button onClick={handleTestConnection} loading={isTestingConnection}>
Test Connect
</Button>
<Button htmlType="submit">Add Model</Button>
</div>
</div>
</>
</Form>
</Card>
<AddModelForm
form={form}
handleOk={handleOk}
selectedProvider={selectedProvider}
setSelectedProvider={setSelectedProvider}
providerModels={providerModels}
setProviderModelsFn={setProviderModelsFn}
getPlaceholder={getPlaceholder}
uploadProps={uploadProps}
showAdvancedSettings={showAdvancedSettings}
setShowAdvancedSettings={setShowAdvancedSettings}
teams={teams}
credentials={credentials}
/>
</TabPanel>
<TabPanel>
<AddAutoRouterTab
@ -418,45 +92,6 @@ const AddModelTab: React.FC<AddModelTabProps> = ({
</TabPanel>
</TabPanels>
</TabGroup>
{/* Test Connection Results Modal */}
<Modal
title="Connection Test Results"
open={isResultModalVisible}
onCancel={() => {
setIsResultModalVisible(false);
setIsTestingConnection(false);
}}
footer={[
<Button
key="close"
onClick={() => {
setIsResultModalVisible(false);
setIsTestingConnection(false);
}}
>
Close
</Button>,
]}
width={700}
>
{/* Only render the ConnectionErrorDisplay when modal is visible and we have a test ID */}
{isResultModalVisible && (
<ConnectionErrorDisplay
// The key prop tells React to create a fresh component instance when it changes
key={connectionTestId}
formValues={form.getFieldsValue()}
accessToken={accessToken}
testMode={testMode}
modelName={form.getFieldValue("model_name") || form.getFieldValue("model")}
onClose={() => {
setIsResultModalVisible(false);
setIsTestingConnection(false);
}}
onTestComplete={() => setIsTestingConnection(false)}
/>
)}
</Modal>
</>
);
};