diff --git a/ui/litellm-dashboard/src/components/add_model/advanced_settings.tsx b/ui/litellm-dashboard/src/components/add_model/advanced_settings.tsx index 554fe86bf66..ce22084a375 100644 --- a/ui/litellm-dashboard/src/components/add_model/advanced_settings.tsx +++ b/ui/litellm-dashboard/src/components/add_model/advanced_settings.tsx @@ -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 = ({ 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 = ({ <> - Advanced Settings + {t("addModel.advancedSettings.title")}
- + - Attached Knowledge Bases (RAG){" "} - + {t("addModel.advancedSettings.attachedKnowledgeBasesLabel")}{" "} + = ({ } 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")} > {}} accessToken={accessToken} - placeholder="Select knowledge bases (optional)" + placeholder={t("addModel.advancedSettings.knowledgeBasesPlaceholder")} /> - Guardrails{" "} - + {t("addModel.advancedSettings.guardrailsLabel")}{" "} + e.stopPropagation()} // Prevent accordion from collapsing when clicking link + onClick={(e) => e.stopPropagation()} > @@ -159,21 +165,21 @@ const AdvancedSettings: React.FC = ({ } name="guardrails" className="mt-4" - help="Select existing guardrails. Go to 'Guardrails' tab to create new guardrails." + help={t("addModel.advancedSettings.guardrailsHelp")} > ({ value: tag.name, label: tag.name, @@ -184,13 +190,17 @@ const AdvancedSettings: React.FC = ({ {customPricing && (
- + + { const values = form.getFieldValue("cache_control_points"); @@ -101,15 +104,15 @@ const CacheControlSettings: React.FC = ({ { const values = form.getFieldValue("cache_control_points"); @@ -140,7 +143,7 @@ const CacheControlSettings: React.FC = ({ onClick={() => add()} > - Add Injection Point + {t("addModel.cacheControlSettings.addInjectionPoint")} diff --git a/ui/litellm-dashboard/src/components/add_model/conditional_public_model_name.tsx b/ui/litellm-dashboard/src/components/add_model/conditional_public_model_name.tsx index 0a77c252133..9fab5dd470a 100644 --- a/ui/litellm-dashboard/src/components/add_model/conditional_public_model_name.tsx +++ b/ui/litellm-dashboard/src/components/add_model/conditional_public_model_name.tsx @@ -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 = ( <> -
The name you specify in your API calls to LiteLLM Proxy
+
{t("addModel.conditionalPublicModelName.publicModelNameTooltip")}
- Example: If you name your public model{" "} - example-name, and choose{" "} - openai/qwen-plus-latest as the LiteLLM model + , + publicName: , + modelName: , + }} + />
- Usage: You make an API call to the LiteLLM proxy with{" "} - model = "example-name" + {t("addModel.conditionalPublicModelName.tooltipUsage")}{" "} + + {t("addModel.conditionalPublicModelName.tooltipUsageCode")} +
- Result: LiteLLM sends{" "} - qwen-plus-latest to the provider + {t("addModel.conditionalPublicModelName.tooltipResult")}{" "} + + {t("addModel.conditionalPublicModelName.tooltipResultCode")} + {" "} + {t("addModel.conditionalPublicModelName.tooltipResultSuffix")}
); - const liteLLMModelTooltipContent =
The model name LiteLLM will send to the LLM API
; + const liteLLMModelTooltipContent =
{t("addModel.conditionalPublicModelName.litellmModelNameTooltip")}
; const columns = [ { title: ( - Public Model Name + {t("addModel.conditionalPublicModelName.publicModelNameColumn")} ), @@ -163,7 +175,7 @@ const ConditionalPublicModelName: React.FC = () => { { title: ( - LiteLLM Model Name + {t("addModel.conditionalPublicModelName.litellmModelNameColumn")} ), @@ -175,9 +187,9 @@ const ConditionalPublicModelName: React.FC = () => { return ( <> { 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")); } }, }, ]} > = ({ providerModels, getPlaceholder, }) => { + const { t } = useTranslation(); const form = Form.useFormInstance(); const handleModelChange = (value: string | string[]) => { @@ -101,8 +103,8 @@ const LiteLLMModelNameField: React.FC = ({ return ( <> = ({ 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 = ({ 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 = ({ )} - {/* Custom Model Name field */} prevValues.model !== currentValues.model}> {({ getFieldValue }) => { const selectedModels = getFieldValue("model") || []; @@ -164,12 +168,14 @@ const LiteLLMModelNameField: React.FC = ({ modelArray.includes("custom") && ( @@ -184,8 +190,8 @@ const LiteLLMModelNameField: React.FC = ({ {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")} diff --git a/ui/litellm-dashboard/src/components/add_model/model_connection_test.tsx b/ui/litellm-dashboard/src/components/add_model/model_connection_test.tsx index b71874e625c..a9fe0d0aeaa 100644 --- a/ui/litellm-dashboard/src/components/add_model/model_connection_test.tsx +++ b/ui/litellm-dashboard/src/components/add_model/model_connection_test.tsx @@ -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 = ({ onClose, onTestComplete, }) => { + const { t } = useTranslation(); const [error, setError] = React.useState(null); const [rawRequest, setRawRequest] = React.useState(null); const [rawResponse, setRawResponse] = React.useState(null); @@ -47,7 +49,7 @@ const ModelConnectionTest: React.FC = ({ 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 = ({ 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 = ({ }, []); // 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 = ({ ? getCleanErrorMessage(error) : error?.message ? getCleanErrorMessage(error.message) - : "Unknown error"; + : t("addModel.modelConnectionTest.unknownError"); const formatCurlCommand = ( apiBase: string, @@ -154,7 +156,7 @@ ${formattedBody} }} /> - Testing connection to {modelName}... + {t("addModel.modelConnectionTest.testingConnection", { modelName })}