diff --git a/ui/litellm-dashboard/src/components/add_model/AddModelForm.tsx b/ui/litellm-dashboard/src/components/add_model/AddModelForm.tsx index 58414a0bab5..7cd4da4c29e 100644 --- a/ui/litellm-dashboard/src/components/add_model/AddModelForm.tsx +++ b/ui/litellm-dashboard/src/components/add_model/AddModelForm.tsx @@ -7,6 +7,7 @@ import type { FormInstance } from "antd"; import { Select as AntdSelect, Button, Card, Col, Form, Modal, Row, Tooltip, Typography, Alert } from "antd"; import type { UploadProps } from "antd/es/upload"; import React, { useEffect, useMemo, useState } from "react"; +import { Trans, useTranslation } from "react-i18next"; import TeamDropdown from "../common_components/team_dropdown"; import type { Team } from "../key_team_helpers/key_list"; import { type CredentialItem, type ProviderCreateInfo, modelAvailableCall } from "../networking"; @@ -17,7 +18,7 @@ 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 { getTestModes } from "./add_model_modes"; import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized"; interface AddModelFormProps { @@ -51,6 +52,7 @@ const AddModelForm: React.FC = ({ teams, credentials, }) => { + const { t } = useTranslation(); const [testMode, setTestMode] = useState("chat"); const [isResultModalVisible, setIsResultModalVisible] = useState(false); const [isTestingConnection, setIsTestingConnection] = useState(false); @@ -86,6 +88,8 @@ const AddModelForm: React.FC = ({ fetchModelAccessGroups(); }, [accessToken]); + const testModes = useMemo(() => getTestModes(t), [t]); + const sortedProviderMetadata: ProviderCreateInfo[] = useMemo(() => { if (!providerMetadata) { return []; @@ -96,7 +100,7 @@ const AddModelForm: React.FC = ({ const providerMetadataErrorText = providerMetadataError ? providerMetadataError instanceof Error ? providerMetadataError.message - : "Failed to load providers" + : t("addModel.addModelForm.providerLoadFailed") : null; const isAdmin = all_admin_roles.includes(userRole); @@ -104,7 +108,7 @@ const AddModelForm: React.FC = ({ return ( <> - Add Model + {t("addModel.addModelForm.title")}
= ({ {isTeamAdmin && !isAdmin && ( <> { @@ -139,8 +143,8 @@ const AddModelForm: React.FC = ({ {!teamAdminSelectedTeam && ( = ({ {(isAdmin || (isTeamAdmin && teamAdminSelectedTeam)) && ( <> @@ -162,7 +166,11 @@ const AddModelForm: React.FC = ({ virtual={false} showSearch loading={isProviderMetadataLoading} - placeholder={isProviderMetadataLoading ? "Loading providers..." : "Select a provider"} + placeholder={ + isProviderMetadataLoading + ? t("addModel.addModelForm.providerLoadingPlaceholder") + : t("addModel.addModelForm.providerSelectPlaceholder") + } optionFilterProp="data-label" onChange={(value) => { setSelectedProvider(value as Providers); @@ -207,22 +215,31 @@ const AddModelForm: React.FC = ({ {/* Select Mode */} - + setTestMode(value)} - options={TEST_MODES} + options={testModes} /> - Optional - LiteLLM endpoint to use when health checking this model{" "} - - Learn more - + , + learnMoreLink: ( + + ), + }} + /> @@ -230,18 +247,22 @@ const AddModelForm: React.FC = ({ {/* Credentials */}
- Either select existing credentials OR enter new provider credentials below + {t("addModel.addModelForm.credentialsSectionDesc")}
- + (option?.label ?? "").toLowerCase().includes(input.toLowerCase())} options={[ - { value: null, label: "None" }, + { value: null, label: t("addModel.addModelForm.credentialsNone") }, ...credentials.map((credential) => ({ value: credential.credential_name, label: credential.credential_name, @@ -279,22 +300,20 @@ const AddModelForm: React.FC = ({
- Additional Model Info Settings + + {t("addModel.addModelForm.additionalModelInfoDivider")} +
{/* Team-only Model Switch - Only show for proxy admins, not team admins */} {(isAdmin || !isTeamAdmin) && ( = ({ {/* Conditional Team Selection */} {isTeamOnly && (isAdmin || !isTeamAdmin) && ( @@ -331,15 +350,15 @@ const AddModelForm: React.FC = ({ {isAdmin && ( <> ({ @@ -363,15 +382,17 @@ const AddModelForm: React.FC = ({ )}
- - Need Help? + + + {t("addModel.addModelForm.needHelp")} +
@@ -381,7 +402,7 @@ const AddModelForm: React.FC = ({ {/* Test Connection Results Modal */} { setIsResultModalVisible(false); @@ -395,7 +416,7 @@ const AddModelForm: React.FC = ({ setIsTestingConnection(false); }} > - Close + {t("common.close")} , ]} width={700} diff --git a/ui/litellm-dashboard/src/components/add_model/ComplexityRouterConfig.tsx b/ui/litellm-dashboard/src/components/add_model/ComplexityRouterConfig.tsx index a20cb969e33..17ba791a3df 100644 --- a/ui/litellm-dashboard/src/components/add_model/ComplexityRouterConfig.tsx +++ b/ui/litellm-dashboard/src/components/add_model/ComplexityRouterConfig.tsx @@ -1,6 +1,7 @@ import { InfoCircleOutlined } from "@ant-design/icons"; import { Select as AntdSelect, Card, Divider, Space, Tooltip, Typography } from "antd"; import React from "react"; +import { useTranslation } from "react-i18next"; import { ModelGroup } from "../playground/llm_calls/fetch_models"; const { Text } = Typography; @@ -18,30 +19,41 @@ interface ComplexityRouterConfigProps { onChange: (tiers: ComplexityTiers) => void; } -const TIER_DESCRIPTIONS: Record = { - SIMPLE: { - label: "Simple", - description: "Basic questions, greetings, simple factual queries", - examples: '"Hello!", "What is Python?", "Thanks!"', - }, - MEDIUM: { - label: "Medium", - description: "Standard queries requiring some reasoning or explanation", - examples: '"Explain how REST APIs work", "Debug this error"', - }, - COMPLEX: { - label: "Complex", - description: "Technical, multi-part requests requiring deep knowledge", - examples: '"Design a microservices architecture", "Implement a rate limiter"', - }, - REASONING: { - label: "Reasoning", - description: "Chain-of-thought, analysis, explicit reasoning requests", - examples: '"Think step by step...", "Analyze the pros and cons..."', - }, -}; - const ComplexityRouterConfig: React.FC = ({ modelInfo, value, onChange }) => { + const { t } = useTranslation(); + + const TIER_CONFIGS: Array<{ + key: keyof ComplexityTiers; + labelKey: string; + descriptionKey: string; + examplesKey: string; + }> = [ + { + key: "SIMPLE", + labelKey: "addModel.complexityRouterConfig.simpleTierLabel", + descriptionKey: "addModel.complexityRouterConfig.simpleTierDescription", + examplesKey: "addModel.complexityRouterConfig.simpleTierExamples", + }, + { + key: "MEDIUM", + labelKey: "addModel.complexityRouterConfig.mediumTierLabel", + descriptionKey: "addModel.complexityRouterConfig.mediumTierDescription", + examplesKey: "addModel.complexityRouterConfig.mediumTierExamples", + }, + { + key: "COMPLEX", + labelKey: "addModel.complexityRouterConfig.complexTierLabel", + descriptionKey: "addModel.complexityRouterConfig.complexTierDescription", + examplesKey: "addModel.complexityRouterConfig.complexTierExamples", + }, + { + key: "REASONING", + labelKey: "addModel.complexityRouterConfig.reasoningTierLabel", + descriptionKey: "addModel.complexityRouterConfig.reasoningTierDescription", + examplesKey: "addModel.complexityRouterConfig.reasoningTierExamples", + }, + ]; + // Prepare model options for dropdowns const modelOptions = modelInfo.map((model) => ({ value: model.model_group, @@ -59,40 +71,43 @@ const ComplexityRouterConfig: React.FC = ({ modelIn
- Complexity Tier Configuration + {t("addModel.complexityRouterConfig.title")} - + - The complexity router automatically classifies requests by complexity using rule-based scoring (no API calls, - <1ms latency). Configure which model handles each tier. + {t("addModel.complexityRouterConfig.subtitle")} - {(Object.keys(TIER_DESCRIPTIONS) as Array).map((tier, index) => { - const tierInfo = TIER_DESCRIPTIONS[tier]; + {TIER_CONFIGS.map((tierConfig, index) => { + const label = t(tierConfig.labelKey); + const description = t(tierConfig.descriptionKey); + const examples = t(tierConfig.examplesKey); return ( -
+
{index > 0 && }
- {tierInfo.label} Tier + {t("addModel.complexityRouterConfig.tierLabel", { label })} - +
- Examples: {tierInfo.examples} + {t("addModel.complexityRouterConfig.examplesLabel", { examples })} handleTierChange(tier, model)} - placeholder={`Select model for ${tierInfo.label.toLowerCase()} queries`} + value={value[tierConfig.key]} + onChange={(model) => handleTierChange(tierConfig.key, model)} + placeholder={t("addModel.complexityRouterConfig.selectModelPlaceholder", { + label: label.toLowerCase(), + })} showSearch style={{ width: "100%" }} options={modelOptions} @@ -107,12 +122,10 @@ const ComplexityRouterConfig: React.FC = ({ modelIn - How Classification Works + {t("addModel.complexityRouterConfig.howClassificationWorksTitle")} - The router scores each request across 7 dimensions: token count, code presence, reasoning markers, technical - terms, simple indicators, multi-step patterns, and question complexity. The weighted score determines the - tier: + {t("addModel.complexityRouterConfig.howClassificationWorksDesc")}
  • diff --git a/ui/litellm-dashboard/src/components/add_model/RouterConfigBuilder.tsx b/ui/litellm-dashboard/src/components/add_model/RouterConfigBuilder.tsx index 17c962c7e25..4f81e1b9248 100644 --- a/ui/litellm-dashboard/src/components/add_model/RouterConfigBuilder.tsx +++ b/ui/litellm-dashboard/src/components/add_model/RouterConfigBuilder.tsx @@ -14,6 +14,7 @@ import { Typography, } from "antd"; import React, { useEffect, useState } from "react"; +import { useTranslation } from "react-i18next"; import { ModelGroup } from "../playground/llm_calls/fetch_models"; const { Text } = Typography; @@ -48,6 +49,7 @@ interface RouterConfigBuilderProps { } const RouterConfigBuilder: React.FC = ({ modelInfo, value, onChange }) => { + const { t } = useTranslation(); const [routes, setRoutes] = useState([]); const [showJsonPreview, setShowJsonPreview] = useState(false); const [expandedRoutes, setExpandedRoutes] = useState([]); @@ -106,8 +108,8 @@ const RouterConfigBuilder: React.FC = ({ modelInfo, va }; // Handle updating a route - const updateRoute = (routeId: string, field: keyof Route, value: any) => { - const updatedRoutes = routes.map((route) => (route.id === routeId ? { ...route, [field]: value } : route)); + const updateRoute = (routeId: string, field: keyof Route, val: any) => { + const updatedRoutes = routes.map((route) => (route.id === routeId ? { ...route, [field]: val } : route)); setRoutes(updatedRoutes); updateConfig(updatedRoutes); }; @@ -156,21 +158,21 @@ const RouterConfigBuilder: React.FC = ({ modelInfo, va - Routes Configuration + {t("addModel.routerConfigBuilder.title")} - + {/* Routes */} {routes.length === 0 ? ( - + ) : ( = ({ modelInfo, va key: route.id, label: ( - Route {index + 1}: {route.model || "Unnamed"} + {t("addModel.routerConfigBuilder.routeLabel", { + index: index + 1, + model: route.model || t("addModel.routerConfigBuilder.routeUnnamed"), + })} ), extra: ( @@ -200,11 +205,11 @@ const RouterConfigBuilder: React.FC = ({ modelInfo, va {/* Model Selection */}
    - Model + {t("addModel.routerConfigBuilder.modelLabel")} updateRoute(route.id, "model", value)} - placeholder="Select model" + onChange={(val) => updateRoute(route.id, "model", val)} + placeholder={t("addModel.routerConfigBuilder.modelPlaceholder")} showSearch style={{ width: "100%" }} options={modelOptions} @@ -213,11 +218,13 @@ const RouterConfigBuilder: React.FC = ({ modelInfo, va {/* Description */}
    - Description + + {t("addModel.routerConfigBuilder.descriptionLabel")} +