feat(ui): i18n for add_model batch 2

This commit is contained in:
吹雪 2026-06-11 00:47:25 +09:00
parent 203eaff982
commit 6a21cb423b
8 changed files with 1127 additions and 2180 deletions

View file

@ -4,6 +4,7 @@ import { Text, Accordion, AccordionHeader, AccordionBody, TextInput } from "@tre
import { Row, Col, Typography } from "antd";
import TextArea from "antd/es/input/TextArea";
import { InfoCircleOutlined } from "@ant-design/icons";
import { useTranslation, Trans } from "react-i18next";
import { Team } from "../key_team_helpers/key_list";
import CacheControlSettings from "./cache_control_settings";
import VectorStoreSelector from "../vector_store_management/VectorStoreSelector";
@ -28,18 +29,18 @@ const AdvancedSettings: React.FC<AdvancedSettingsProps> = ({
tagsList,
accessToken,
}) => {
const { t } = useTranslation();
const [form] = Form.useForm();
const [customPricing, setCustomPricing] = React.useState(false);
const [pricingModel, setPricingModel] = React.useState<"per_token" | "per_second">("per_token");
const [showCacheControl, setShowCacheControl] = React.useState(false);
// Add validation function for numbers
const validateNumber = (_: any, value: string) => {
if (!value) {
return Promise.resolve();
}
if (isNaN(Number(value)) || Number(value) < 0) {
return Promise.reject("Please enter a valid positive number");
return Promise.reject(t("addModel.advancedSettings.validateNumberError"));
}
return Promise.resolve();
};
@ -106,19 +107,24 @@ const AdvancedSettings: React.FC<AdvancedSettingsProps> = ({
<>
<Accordion className="mt-2 mb-4">
<AccordionHeader>
<b>Advanced Settings</b>
<b>{t("addModel.advancedSettings.title")}</b>
</AccordionHeader>
<AccordionBody>
<div className="bg-white rounded-lg">
<Form.Item label="Custom Pricing" name="custom_pricing" valuePropName="checked" className="mb-4">
<Form.Item
label={t("addModel.advancedSettings.customPricingLabel")}
name="custom_pricing"
valuePropName="checked"
className="mb-4"
>
<Switch onChange={handleCustomPricingChange} className="bg-gray-600" />
</Form.Item>
<Form.Item
label={
<span>
Attached Knowledge Bases (RAG){" "}
<Tooltip title="Vector stores to use for RAG. Every request to this model will automatically retrieve context from these knowledge bases.">
{t("addModel.advancedSettings.attachedKnowledgeBasesLabel")}{" "}
<Tooltip title={t("addModel.advancedSettings.attachedKnowledgeBasesTooltip")}>
<a
href="https://docs.litellm.ai/docs/completion/knowledgebase"
target="_blank"
@ -132,25 +138,25 @@ const AdvancedSettings: React.FC<AdvancedSettingsProps> = ({
}
name="vector_store_ids"
className="mt-4"
help="Select vector stores to attach. Requests to this model will automatically use these for RAG. Set up vector stores in Tools > Vector Stores."
help={t("addModel.advancedSettings.attachedKnowledgeBasesHelp")}
>
<VectorStoreSelector
onChange={() => {}}
accessToken={accessToken}
placeholder="Select knowledge bases (optional)"
placeholder={t("addModel.advancedSettings.knowledgeBasesPlaceholder")}
/>
</Form.Item>
<Form.Item
label={
<span>
Guardrails{" "}
<Tooltip title="Apply safety guardrails to this key to filter content or enforce policies">
{t("addModel.advancedSettings.guardrailsLabel")}{" "}
<Tooltip title={t("addModel.advancedSettings.guardrailsTooltip")}>
<a
href="https://docs.litellm.ai/docs/proxy/guardrails/quick_start"
target="_blank"
rel="noopener noreferrer"
onClick={(e) => e.stopPropagation()} // Prevent accordion from collapsing when clicking link
onClick={(e) => e.stopPropagation()}
>
<InfoCircleOutlined style={{ marginLeft: "4px" }} />
</a>
@ -159,21 +165,21 @@ const AdvancedSettings: React.FC<AdvancedSettingsProps> = ({
}
name="guardrails"
className="mt-4"
help="Select existing guardrails. Go to 'Guardrails' tab to create new guardrails."
help={t("addModel.advancedSettings.guardrailsHelp")}
>
<Select
mode="tags"
style={{ width: "100%" }}
placeholder="Select or enter guardrails"
placeholder={t("addModel.advancedSettings.guardrailsPlaceholder")}
options={guardrailsList.map((name) => ({ value: name, label: name }))}
/>
</Form.Item>
<Form.Item label="Tags" name="tags" className="mb-4">
<Form.Item label={t("addModel.advancedSettings.tagsLabel")} name="tags" className="mb-4">
<Select
mode="tags"
style={{ width: "100%" }}
placeholder="Select or enter tags"
placeholder={t("addModel.advancedSettings.tagsPlaceholder")}
options={Object.values(tagsList).map((tag) => ({
value: tag.name,
label: tag.name,
@ -184,13 +190,17 @@ const AdvancedSettings: React.FC<AdvancedSettingsProps> = ({
{customPricing && (
<div className="ml-6 pl-4 border-l-2 border-gray-200">
<Form.Item label="Pricing Model" name="pricing_model" className="mb-4">
<Form.Item
label={t("addModel.advancedSettings.pricingModelLabel")}
name="pricing_model"
className="mb-4"
>
<Select
defaultValue="per_token"
onChange={(value: "per_token" | "per_second") => setPricingModel(value)}
options={[
{ value: "per_token", label: "Per Million Tokens" },
{ value: "per_second", label: "Per Second" },
{ value: "per_token", label: t("addModel.advancedSettings.perMillionTokens") },
{ value: "per_second", label: t("addModel.advancedSettings.perSecond") },
]}
/>
</Form.Item>
@ -198,7 +208,7 @@ const AdvancedSettings: React.FC<AdvancedSettingsProps> = ({
{pricingModel === "per_token" ? (
<>
<Form.Item
label="Input Cost (per 1M tokens)"
label={t("addModel.advancedSettings.inputCostLabel")}
name="input_cost_per_token"
rules={[{ validator: validateNumber }]}
className="mb-4"
@ -206,7 +216,7 @@ const AdvancedSettings: React.FC<AdvancedSettingsProps> = ({
<TextInput />
</Form.Item>
<Form.Item
label="Output Cost (per 1M tokens)"
label={t("addModel.advancedSettings.outputCostLabel")}
name="output_cost_per_token"
rules={[{ validator: validateNumber }]}
className="mb-4"
@ -214,27 +224,27 @@ const AdvancedSettings: React.FC<AdvancedSettingsProps> = ({
<TextInput />
</Form.Item>
<Form.Item
label="Cache Read Cost (per 1M tokens)"
label={t("addModel.advancedSettings.cacheReadCostLabel")}
name="cache_read_input_token_cost"
rules={[{ validator: validateNumber }]}
tooltip="If left blank, defaults to Input Cost."
tooltip={t("addModel.advancedSettings.cacheReadCostTooltip")}
className="mb-4"
>
<TextInput placeholder="Defaults to Input Cost if blank" />
<TextInput placeholder={t("addModel.advancedSettings.cacheReadCostPlaceholder")} />
</Form.Item>
<Form.Item
label="Cache Write Cost (per 1M tokens)"
label={t("addModel.advancedSettings.cacheWriteCostLabel")}
name="cache_creation_input_token_cost"
rules={[{ validator: validateNumber }]}
tooltip="If left blank, defaults to Input Cost (the backend falls back to input_cost_per_token when no cache-write rate is set)."
tooltip={t("addModel.advancedSettings.cacheWriteCostTooltip")}
className="mb-4"
>
<TextInput placeholder="Defaults to Input Cost if blank" />
<TextInput placeholder={t("addModel.advancedSettings.cacheWriteCostPlaceholder")} />
</Form.Item>
</>
) : (
<Form.Item
label="Cost Per Second"
label={t("addModel.advancedSettings.costPerSecondLabel")}
name="input_cost_per_second"
rules={[{ validator: validateNumber }]}
className="mb-4"
@ -246,17 +256,17 @@ const AdvancedSettings: React.FC<AdvancedSettingsProps> = ({
)}
<Form.Item
label="Use in pass through routes"
label={t("addModel.advancedSettings.useInPassThroughLabel")}
name="use_in_pass_through"
valuePropName="checked"
className="mb-4 mt-4"
tooltip={
<span>
Allow using these credentials in pass through routes.{" "}
<Link href="https://docs.litellm.ai/docs/pass_through/vertex_ai" target="_blank">
Learn more
</Link>
</span>
<Trans
i18nKey="addModel.advancedSettings.useInPassThroughTooltip"
components={{
learnMoreLink: <Link href="https://docs.litellm.ai/docs/pass_through/vertex_ai" target="_blank" />,
}}
/>
}
>
<Switch onChange={handlePassThroughChange} className="bg-gray-600" />
@ -268,9 +278,9 @@ const AdvancedSettings: React.FC<AdvancedSettingsProps> = ({
onCacheControlChange={handleCacheControlChange}
/>
<Form.Item
label="LiteLLM Params"
label={t("addModel.advancedSettings.litellmParamsLabel")}
name="litellm_extra_params"
tooltip="Optional litellm params used for making a litellm.completion() call."
tooltip={t("addModel.advancedSettings.litellmParamsTooltip")}
className="mb-4 mt-4"
rules={[{ validator: formItemValidateJSON }]}
>
@ -287,17 +297,17 @@ const AdvancedSettings: React.FC<AdvancedSettingsProps> = ({
<Col span={10}></Col>
<Col span={10}>
<Text className="text-gray-600 text-sm">
Pass JSON of litellm supported params{" "}
{t("addModel.advancedSettings.litellmParamsHelpText")}{" "}
<Link href="https://docs.litellm.ai/docs/completion/input" target="_blank">
litellm.completion() call
{t("addModel.advancedSettings.litellmParamsHelpLink")}
</Link>
</Text>
</Col>
</Row>
<Form.Item
label="Model Info"
label={t("addModel.advancedSettings.modelInfoLabel")}
name="model_info_params"
tooltip="Optional model info params. Returned when calling `/model/info` endpoint."
tooltip={t("addModel.advancedSettings.modelInfoTooltip")}
className="mb-0"
rules={[{ validator: formItemValidateJSON }]}
>

View file

@ -1,6 +1,7 @@
import React from "react";
import { Form, Switch, Select, Typography } from "antd";
import { PlusOutlined, MinusCircleOutlined } from "@ant-design/icons";
import { useTranslation } from "react-i18next";
import NumericalInput from "../shared/numerical_input";
const { Text } = Typography;
@ -22,6 +23,8 @@ const CacheControlSettings: React.FC<CacheControlSettingsProps> = ({
showCacheControl,
onCacheControlChange,
}) => {
const { t } = useTranslation();
const updateCacheControlPoints = (injectionPoints: CacheControlInjectionPoint[]) => {
const currentParams = form.getFieldValue("litellm_extra_params");
try {
@ -44,21 +47,18 @@ const CacheControlSettings: React.FC<CacheControlSettingsProps> = ({
return (
<>
<Form.Item
label="Cache Control Injection Points"
label={t("addModel.cacheControlSettings.injectionPointsLabel")}
name="cache_control"
valuePropName="checked"
className="mb-4"
tooltip="Tell litellm where to inject cache control checkpoints. You can specify either by role (to apply to all messages of that role) or by specific message index."
tooltip={t("addModel.cacheControlSettings.injectionPointsTooltip")}
>
<Switch onChange={onCacheControlChange} className="bg-gray-600" />
</Form.Item>
{showCacheControl && (
<div className="ml-6 pl-4 border-l-2 border-gray-200">
<Text className="text-sm text-gray-500 block mb-4">
Providers like Anthropic, Bedrock API require users to specify where to inject cache control checkpoints,
litellm can automatically add them for you as a cost saving feature.
</Text>
<Text className="text-sm text-gray-500 block mb-4">{t("addModel.cacheControlSettings.helpText")}</Text>
<Form.List name="cache_control_injection_points" initialValue={[{ location: "message" }]}>
{(fields, { add, remove }) => (
@ -67,30 +67,33 @@ const CacheControlSettings: React.FC<CacheControlSettingsProps> = ({
<div key={field.key} className="flex items-center mb-4 gap-4">
<Form.Item
{...field}
label="Type"
label={t("addModel.cacheControlSettings.typeLabel")}
name={[field.name, "location"]}
initialValue="message"
className="mb-0"
style={{ width: "180px" }}
>
<Select disabled options={[{ value: "message", label: "Message" }]} />
<Select
disabled
options={[{ value: "message", label: t("addModel.cacheControlSettings.messageType") }]}
/>
</Form.Item>
<Form.Item
{...field}
label="Role"
label={t("addModel.cacheControlSettings.roleLabel")}
name={[field.name, "role"]}
className="mb-0"
style={{ width: "180px" }}
tooltip="LiteLLM will mark all messages of this role as cacheable"
tooltip={t("addModel.cacheControlSettings.roleTooltip")}
>
<Select
placeholder="Select a role"
placeholder={t("addModel.cacheControlSettings.rolePlaceholder")}
allowClear
options={[
{ value: "user", label: "User" },
{ value: "system", label: "System" },
{ value: "assistant", label: "Assistant" },
{ value: "user", label: t("addModel.cacheControlSettings.roleUser") },
{ value: "system", label: t("addModel.cacheControlSettings.roleSystem") },
{ value: "assistant", label: t("addModel.cacheControlSettings.roleAssistant") },
]}
onChange={() => {
const values = form.getFieldValue("cache_control_points");
@ -101,15 +104,15 @@ const CacheControlSettings: React.FC<CacheControlSettingsProps> = ({
<Form.Item
{...field}
label="Index"
label={t("addModel.cacheControlSettings.indexLabel")}
name={[field.name, "index"]}
className="mb-0"
style={{ width: "180px" }}
tooltip="(Optional) If set litellm will mark the message at this index as cacheable"
tooltip={t("addModel.cacheControlSettings.indexTooltip")}
>
<NumericalInput
type="number"
placeholder="Optional"
placeholder={t("addModel.cacheControlSettings.indexPlaceholder")}
step={1}
onChange={() => {
const values = form.getFieldValue("cache_control_points");
@ -140,7 +143,7 @@ const CacheControlSettings: React.FC<CacheControlSettingsProps> = ({
onClick={() => add()}
>
<PlusOutlined className="mr-2" />
Add Injection Point
{t("addModel.cacheControlSettings.addInjectionPoint")}
</button>
</Form.Item>
</>

View file

@ -1,12 +1,14 @@
import React, { useEffect, useState } from "react";
import { Form, Table } from "antd";
import { TextInput } from "@tremor/react";
import { Trans, useTranslation } from "react-i18next";
import { Tooltip } from "../atoms/index";
import { Providers } from "../provider_info_helpers";
const ConditionalPublicModelName: React.FC = () => {
const { t } = useTranslation();
const form = Form.useFormInstance();
const [tableKey, setTableKey] = useState(0); // Add a key to force table re-render
const [tableKey, setTableKey] = useState(0);
// Watch the 'model' field for changes and ensure it's always an array
const modelValue = Form.useWatch("model", form) || [];
@ -95,30 +97,40 @@ const ConditionalPublicModelName: React.FC = () => {
const publicNameTooltipContent = (
<>
<div className="mb-2 font-normal">The name you specify in your API calls to LiteLLM Proxy</div>
<div className="mb-2 font-normal">{t("addModel.conditionalPublicModelName.publicModelNameTooltip")}</div>
<div className="mb-2 font-normal">
<strong>Example:</strong> If you name your public model{" "}
<code className="bg-gray-700 px-1 py-0.5 rounded text-xs">example-name</code>, and choose{" "}
<code className="bg-gray-700 px-1 py-0.5 rounded text-xs">openai/qwen-plus-latest</code> as the LiteLLM model
<Trans
i18nKey="addModel.conditionalPublicModelName.tooltipExample"
components={{
strong: <strong />,
publicName: <code className="bg-gray-700 px-1 py-0.5 rounded text-xs" />,
modelName: <code className="bg-gray-700 px-1 py-0.5 rounded text-xs" />,
}}
/>
</div>
<div className="mb-2 font-normal">
<strong>Usage:</strong> You make an API call to the LiteLLM proxy with{" "}
<code className="bg-gray-700 px-1 py-0.5 rounded text-xs">model = &quot;example-name&quot;</code>
<strong>{t("addModel.conditionalPublicModelName.tooltipUsage")}</strong>{" "}
<code className="bg-gray-700 px-1 py-0.5 rounded text-xs">
{t("addModel.conditionalPublicModelName.tooltipUsageCode")}
</code>
</div>
<div className="font-normal">
<strong>Result:</strong> LiteLLM sends{" "}
<code className="bg-gray-700 px-1 py-0.5 rounded text-xs">qwen-plus-latest</code> to the provider
<strong>{t("addModel.conditionalPublicModelName.tooltipResult")}</strong>{" "}
<code className="bg-gray-700 px-1 py-0.5 rounded text-xs">
{t("addModel.conditionalPublicModelName.tooltipResultCode")}
</code>{" "}
{t("addModel.conditionalPublicModelName.tooltipResultSuffix")}
</div>
</>
);
const liteLLMModelTooltipContent = <div>The model name LiteLLM will send to the LLM API</div>;
const liteLLMModelTooltipContent = <div>{t("addModel.conditionalPublicModelName.litellmModelNameTooltip")}</div>;
const columns = [
{
title: (
<span className="flex items-center">
Public Model Name
{t("addModel.conditionalPublicModelName.publicModelNameColumn")}
<Tooltip content={publicNameTooltipContent} width="500px" />
</span>
),
@ -163,7 +175,7 @@ const ConditionalPublicModelName: React.FC = () => {
{
title: (
<span className="flex items-center">
LiteLLM Model Name
{t("addModel.conditionalPublicModelName.litellmModelNameColumn")}
<Tooltip content={liteLLMModelTooltipContent} width="360px" />
</span>
),
@ -175,9 +187,9 @@ const ConditionalPublicModelName: React.FC = () => {
return (
<>
<Form.Item
label="Model Mappings"
label={t("addModel.conditionalPublicModelName.modelMappingsLabel")}
name="model_mappings"
tooltip="Map public model names to LiteLLM model names for load balancing"
tooltip={t("addModel.conditionalPublicModelName.modelMappingsTooltip")}
labelCol={{ span: 10 }}
wrapperCol={{ span: 16 }}
labelAlign="left"
@ -186,21 +198,20 @@ const ConditionalPublicModelName: React.FC = () => {
required: true,
validator: async (_, value) => {
if (!value || value.length === 0) {
throw new Error("At least one model mapping is required");
throw new Error(t("addModel.conditionalPublicModelName.mappingRequired"));
}
// Check if all mappings have valid public names
const invalidMappings = value.filter(
(mapping: any) => !mapping.public_name || mapping.public_name.trim() === "",
);
if (invalidMappings.length > 0) {
throw new Error("All model mappings must have valid public names");
throw new Error(t("addModel.conditionalPublicModelName.publicNameRequired"));
}
},
},
]}
>
<Table
key={tableKey} // Add key to force re-render
key={tableKey}
dataSource={form.getFieldValue("model_mappings")}
columns={columns}
pagination={false}

View file

@ -2,6 +2,7 @@ import React from "react";
import { Form, Select as AntSelect } from "antd";
import { TextInput, Text } from "@tremor/react";
import { Row, Col } from "antd";
import { useTranslation } from "react-i18next";
import { Providers } from "../provider_info_helpers";
interface LiteLLMModelNameFieldProps {
@ -15,6 +16,7 @@ const LiteLLMModelNameField: React.FC<LiteLLMModelNameFieldProps> = ({
providerModels,
getPlaceholder,
}) => {
const { t } = useTranslation();
const form = Form.useFormInstance();
const handleModelChange = (value: string | string[]) => {
@ -101,8 +103,8 @@ const LiteLLMModelNameField: React.FC<LiteLLMModelNameFieldProps> = ({
return (
<>
<Form.Item
label="LiteLLM Model Name(s)"
tooltip="The model name LiteLLM will send to the LLM API"
label={t("addModel.litellmModelName.fieldLabel")}
tooltip={t("addModel.litellmModelName.fieldTooltip")}
className="mb-0"
>
<Form.Item
@ -110,7 +112,10 @@ const LiteLLMModelNameField: React.FC<LiteLLMModelNameFieldProps> = ({
rules={[
{
required: true,
message: `Please enter ${selectedProvider === Providers.Azure ? "a deployment name" : "at least one model"}.`,
message:
selectedProvider === Providers.Azure
? t("addModel.litellmModelName.deploymentNameRequired")
: t("addModel.litellmModelName.modelRequired"),
},
]}
noStyle
@ -130,17 +135,17 @@ const LiteLLMModelNameField: React.FC<LiteLLMModelNameFieldProps> = ({
mode="multiple"
allowClear
showSearch
placeholder="Select models"
placeholder={t("addModel.litellmModelName.selectModelsPlaceholder")}
onChange={handleModelChange}
optionFilterProp="children"
filterOption={(input, option) => (option?.label ?? "").toLowerCase().includes(input.toLowerCase())}
options={[
{
label: "Custom Model Name (Enter below)",
label: t("addModel.litellmModelName.customModelOption"),
value: "custom",
},
{
label: `All ${selectedProvider} Models (Wildcard)`,
label: t("addModel.litellmModelName.allModelsWildcard", { provider: selectedProvider }),
value: "all-wildcard",
},
...providerModels.map((model) => ({
@ -155,7 +160,6 @@ const LiteLLMModelNameField: React.FC<LiteLLMModelNameFieldProps> = ({
)}
</Form.Item>
{/* Custom Model Name field */}
<Form.Item noStyle shouldUpdate={(prevValues, currentValues) => prevValues.model !== currentValues.model}>
{({ getFieldValue }) => {
const selectedModels = getFieldValue("model") || [];
@ -164,12 +168,14 @@ const LiteLLMModelNameField: React.FC<LiteLLMModelNameFieldProps> = ({
modelArray.includes("custom") && (
<Form.Item
name="custom_model_name"
rules={[{ required: true, message: "Please enter a custom model name." }]}
rules={[{ required: true, message: t("addModel.litellmModelName.customModelRequired") }]}
className="mt-2"
>
<TextInput
placeholder={
selectedProvider === Providers.Azure ? "Enter Azure deployment name" : "Enter custom model name"
selectedProvider === Providers.Azure
? t("addModel.litellmModelName.enterAzureDeploymentPlaceholder")
: t("addModel.litellmModelName.enterCustomModelPlaceholder")
}
onChange={handleCustomModelNameChange}
/>
@ -184,8 +190,8 @@ const LiteLLMModelNameField: React.FC<LiteLLMModelNameFieldProps> = ({
<Col span={14}>
<Text className="mb-3 mt-1">
{selectedProvider === Providers.Azure
? "Your deployment name will be saved as the public model name, and LiteLLM will use 'azure/deployment-name' internally"
: "The model name LiteLLM will send to the LLM API"}
? t("addModel.litellmModelName.azureDeploymentDescription")
: t("addModel.litellmModelName.modelDescription")}
</Text>
</Col>
</Row>

View file

@ -1,6 +1,7 @@
import React from "react";
import { Typography, Button, Divider } from "antd";
import { WarningOutlined, InfoCircleOutlined, CopyOutlined } from "@ant-design/icons";
import { useTranslation } from "react-i18next";
import { testConnectionRequest } from "../networking";
import { prepareModelAddRequest } from "./handle_add_model_submit";
import NotificationsManager from "../molecules/notifications_manager";
@ -23,6 +24,7 @@ const ModelConnectionTest: React.FC<ModelConnectionTestProps> = ({
onClose,
onTestComplete,
}) => {
const { t } = useTranslation();
const [error, setError] = React.useState<Error | string | null>(null);
const [rawRequest, setRawRequest] = React.useState<any>(null);
const [rawResponse, setRawResponse] = React.useState<any>(null);
@ -47,7 +49,7 @@ const ModelConnectionTest: React.FC<ModelConnectionTestProps> = ({
if (!result) {
console.log("No result from prepareModelAddRequest");
setError("Failed to prepare model data. Please check your form inputs.");
setError(t("addModel.modelConnectionTest.prepareFailed"));
setIsSuccess(false);
setIsLoading(false);
return;
@ -59,7 +61,7 @@ const ModelConnectionTest: React.FC<ModelConnectionTestProps> = ({
const response = await testConnectionRequest(accessToken, litellmParamsObj, modelInfoObj, modelInfoObj?.mode);
if (response.status === "success") {
NotificationsManager.success("Connection test successful!");
NotificationsManager.success(t("addModel.modelConnectionTest.connectionTestSuccessful"));
setError(null);
setIsSuccess(true);
} else {
@ -90,7 +92,7 @@ const ModelConnectionTest: React.FC<ModelConnectionTestProps> = ({
}, []); // Empty dependency array means this runs once on mount
const getCleanErrorMessage = (errorMsg: string) => {
if (!errorMsg) return "Unknown error";
if (!errorMsg) return t("addModel.modelConnectionTest.unknownError");
const mainError = errorMsg.split("stack trace:")[0].trim();
@ -104,7 +106,7 @@ const ModelConnectionTest: React.FC<ModelConnectionTestProps> = ({
? getCleanErrorMessage(error)
: error?.message
? getCleanErrorMessage(error.message)
: "Unknown error";
: t("addModel.modelConnectionTest.unknownError");
const formatCurlCommand = (
apiBase: string,
@ -154,7 +156,7 @@ ${formattedBody}
}}
/>
</div>
<Text style={{ fontSize: "16px" }}>Testing connection to {modelName}...</Text>
<Text style={{ fontSize: "16px" }}>{t("addModel.modelConnectionTest.testingConnection", { modelName })}</Text>
<style jsx>{`
@keyframes spin {
0% {
@ -186,7 +188,7 @@ ${formattedBody}
type="success"
style={{ fontSize: "18px", fontWeight: 500, marginLeft: "10px" }}
>
Connection to {modelName} successful!
{t("addModel.modelConnectionTest.connectionSuccess", { modelName })}
</Text>
</div>
) : (
@ -195,7 +197,7 @@ ${formattedBody}
<div style={{ display: "flex", alignItems: "center", marginBottom: "20px" }}>
<WarningOutlined style={{ color: "#ff4d4f", fontSize: "24px", marginRight: "12px" }} />
<Text data-testid="connection-failure-msg" type="danger" style={{ fontSize: "18px", fontWeight: 500 }}>
Connection to {modelName} failed
{t("addModel.modelConnectionTest.connectionFailed", { modelName })}
</Text>
</div>
@ -210,7 +212,7 @@ ${formattedBody}
}}
>
<Text strong style={{ display: "block", marginBottom: "8px" }}>
Error:{" "}
{t("addModel.modelConnectionTest.errorLabel")}
</Text>
<Text type="danger" style={{ fontSize: "14px", lineHeight: "1.5" }}>
{errorMessage}
@ -223,7 +225,9 @@ ${formattedBody}
onClick={() => setShowDetails(!showDetails)}
style={{ paddingLeft: 0, height: "auto" }}
>
{showDetails ? "Hide Details" : "Show Details"}
{showDetails
? t("addModel.modelConnectionTest.hideDetails")
: t("addModel.modelConnectionTest.showDetails")}
</Button>
</div>
)}
@ -232,7 +236,7 @@ ${formattedBody}
{showDetails && (
<div style={{ marginBottom: "20px" }}>
<Text strong style={{ display: "block", marginBottom: "8px", fontSize: "15px" }}>
Troubleshooting Details
{t("addModel.modelConnectionTest.troubleshootingDetails")}
</Text>
<pre
style={{
@ -253,7 +257,7 @@ ${formattedBody}
<div>
<Text strong style={{ display: "block", marginBottom: "8px", fontSize: "15px" }}>
API Request
{t("addModel.modelConnectionTest.apiRequest")}
</Text>
<pre
style={{
@ -267,17 +271,17 @@ ${formattedBody}
lineHeight: "1.5",
}}
>
{curlCommand || "No request data available"}
{curlCommand || t("addModel.modelConnectionTest.noRequestData")}
</pre>
<Button
style={{ marginTop: "8px" }}
icon={<CopyOutlined />}
onClick={() => {
navigator.clipboard.writeText(curlCommand || "");
NotificationsManager.success("Copied to clipboard");
NotificationsManager.success(t("addModel.modelConnectionTest.copiedToClipboard"));
}}
>
Copy to Clipboard
{t("addModel.modelConnectionTest.copyToClipboard")}
</Button>
</div>
</div>
@ -286,7 +290,7 @@ ${formattedBody}
<Divider style={{ margin: "24px 0 16px" }} />
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
<Button type="link" href="https://docs.litellm.ai/docs/providers" target="_blank" icon={<InfoCircleOutlined />}>
View Documentation
{t("addModel.modelConnectionTest.viewDocumentation")}
</Button>
</div>
</div>

View file

@ -3,6 +3,7 @@ import { UploadOutlined } from "@ant-design/icons";
import { Text, TextInput } from "@tremor/react";
import { Button as Button2, Col, Form, Input, Row, Select, Typography, Upload, UploadProps } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { CredentialItem, ProviderCredentialFieldMetadata } from "../networking";
import { provider_map, Providers } from "../provider_info_helpers";
const { Link } = Typography;
@ -93,8 +94,9 @@ export const createCredentialFromModel = (provider: string, modelData: any): Cre
};
const ProviderSpecificFields: React.FC<ProviderSpecificFieldsProps> = ({ selectedProvider, uploadProps }) => {
const { t } = useTranslation();
const selectedProviderEnum = Providers[selectedProvider as keyof typeof Providers] as Providers;
const form = Form.useFormInstance(); // Get form instance from context
const form = Form.useFormInstance();
const { data: providerMetadata, isLoading, error: loadError } = useProviderFields();
@ -201,7 +203,7 @@ const ProviderSpecificFields: React.FC<ProviderSpecificFieldsProps> = ({ selecte
{isLoading && allFields.length === 0 && (
<Row>
<Col span={24}>
<Text className="mb-2">Loading provider fields...</Text>
<Text className="mb-2">{t("addModel.providerSpecificFields.loadingFields")}</Text>
</Col>
</Row>
)}
@ -209,7 +211,7 @@ const ProviderSpecificFields: React.FC<ProviderSpecificFieldsProps> = ({ selecte
<Row>
<Col span={24}>
<Text className="mb-2 text-red-500">
{loadError instanceof Error ? loadError.message : "Failed to load provider credential fields"}
{loadError instanceof Error ? loadError.message : t("addModel.providerSpecificFields.loadFailed")}
</Text>
</Col>
</Row>
@ -219,7 +221,9 @@ const ProviderSpecificFields: React.FC<ProviderSpecificFieldsProps> = ({ selecte
<Form.Item
label={field.label}
name={field.key}
rules={field.required ? [{ required: true, message: "Required" }] : undefined}
rules={
field.required ? [{ required: true, message: t("addModel.providerSpecificFields.required") }] : undefined
}
tooltip={field.tooltip}
className={field.key === "vertex_credentials" ? "mb-0" : undefined}
>
@ -247,7 +251,7 @@ const ProviderSpecificFields: React.FC<ProviderSpecificFieldsProps> = ({ selecte
}, 500);
}}
>
<Button2 icon={<UploadOutlined />}>Click to Upload</Button2>
<Button2 icon={<UploadOutlined />}>{t("addModel.providerSpecificFields.clickToUpload")}</Button2>
</Upload>
) : field.type === "textarea" ? (
<Input.TextArea
@ -269,23 +273,22 @@ const ProviderSpecificFields: React.FC<ProviderSpecificFieldsProps> = ({ selecte
{field.key === "vertex_credentials" && (
<Row>
<Col>
<Text className="mb-3 mt-1">Give a gcp service account(.json file)</Text>
<Text className="mb-3 mt-1">{t("addModel.providerSpecificFields.vertexCredentialsHelp")}</Text>
</Col>
</Row>
)}
{/* Special case for Azure Base Model help text */}
{field.key === "base_model" && (
<Row>
<Col span={10}></Col>
<Col span={10}>
<Text className="mb-2">
The actual model your azure deployment uses. Used for accurate cost tracking. Select name from{" "}
{t("addModel.providerSpecificFields.baseModelHelp")}{" "}
<Link
href="https://github.com/BerriAI/litellm/blob/main/model_prices_and_context_window.json"
target="_blank"
>
here
{t("addModel.providerSpecificFields.baseModelHelpLink")}
</Link>
</Text>
</Col>

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff