diff --git a/ui/litellm-dashboard/src/components/routing_groups/RoutingGroupModal.tsx b/ui/litellm-dashboard/src/components/routing_groups/RoutingGroupModal.tsx index 5b7c0fce6ef..96219ceb770 100644 --- a/ui/litellm-dashboard/src/components/routing_groups/RoutingGroupModal.tsx +++ b/ui/litellm-dashboard/src/components/routing_groups/RoutingGroupModal.tsx @@ -2,6 +2,7 @@ import React, { useMemo } from "react"; import { Form, Input, Modal, Select, Space, Typography } from "antd"; +import { useTranslation } from "react-i18next"; import type { RoutingGroup, RoutingStrategy } from "./types"; const { Text, Paragraph } = Typography; @@ -43,6 +44,7 @@ const RoutingGroupModal: React.FC = ({ onSubmit, saving, }) => { + const { t } = useTranslation(); const [form] = Form.useForm(); const selectedStrategy = Form.useWatch("routing_strategy", form); @@ -71,7 +73,7 @@ const RoutingGroupModal: React.FC = ({ form.setFields([ { name: "routing_strategy_args", - errors: ["Must be valid JSON"], + errors: [t("routingGroups.routingGroupModal.strategyArgsMustBeJson")], }, ]); return; @@ -88,12 +90,20 @@ const RoutingGroupModal: React.FC = ({ return ( = ({ initialValues={initialValues} > { if (!value) return Promise.resolve(); if (reservedNames.has(value.trim().toLowerCase())) { - return Promise.reject(new Error("A group with this name already exists")); + return Promise.reject(new Error(t("routingGroups.routingGroupModal.groupNameExists"))); } return Promise.resolve(); }, }, ]} - extra="Use this name as the model in API calls — LiteLLM routes the request to one of the group's models." + extra={t("routingGroups.routingGroupModal.groupNameExtra")} > - + ({ label: s, value: s }))} placeholder="Select strategy" /> + } - placeholder="Search groups..." + placeholder={t("routingGroups.index.searchPlaceholder")} value={searchQuery} onChange={(e) => setSearchQuery(e.target.value)} className="max-w-sm" /> - Showing {filteredGroups.length} {filteredGroups.length === 1 ? "result" : "results"} + {t("routingGroups.index.showingResults", { count: filteredGroups.length })} @@ -146,16 +148,19 @@ const RoutingGroups: React.FC = () => { setDeletingGroup(null)} > - Models in {deletingGroup?.group_name} will fall back to the proxy's top-level routing - strategy. This cannot be undone. + }} + /> diff --git a/ui/litellm-dashboard/src/locales/en.json b/ui/litellm-dashboard/src/locales/en.json index 4162fbc08df..3ff1aa2c280 100644 --- a/ui/litellm-dashboard/src/locales/en.json +++ b/ui/litellm-dashboard/src/locales/en.json @@ -7729,5 +7729,60 @@ "title": "Vector Stores", "empty": "No vector stores configured" } + }, + "routingGroups": { + "routingGroupModal": { + "titleCreate": "Create Routing Group", + "titleEdit": "Edit {{groupName}}", + "okTextCreate": "Create Group", + "okTextEdit": "Save Changes", + "groupNameLabel": "Group Name", + "groupNameRequired": "Group name is required", + "groupNameMaxLength": "Must be {{max}} characters or fewer", + "groupNamePattern": "Only letters, numbers, dot, underscore, and dash are allowed", + "groupNameExists": "A group with this name already exists", + "groupNameExtra": "Use this name as the model in API calls — LiteLLM routes the request to one of the group's models.", + "groupNamePlaceholder": "fast-chat", + "modelsLabel": "Models", + "modelsRequired": "Select at least one model", + "modelsExtra": "Models from your model list that this group routes between.", + "modelsPlaceholder": "Select models", + "routingStrategyLabel": "Routing Strategy", + "routingStrategyRequired": "Strategy is required", + "strategyPlaceholder": "Select strategy", + "strategyArgsLabel": "Strategy Arguments (JSON)", + "strategyArgsExampleLatency": "Example: { \"ttl\": 3600, \"lowest_latency_buffer\": 0 }", + "strategyArgsExampleUsage": "Example: { \"ttl\": 60 }", + "strategyArgsMustBeJson": "Must be valid JSON", + "strategyArgsPlaceholder": "{ \"ttl\": 3600 }", + "fallbackNote": "Models not claimed by an explicit group fall through to the proxy's top-level routing strategy." + }, + "routingGroupsTable": { + "colGroupName": "GROUP NAME", + "colModels": "MODELS", + "colStrategy": "STRATEGY", + "strategySimpleShuffle": "Simple Shuffle", + "strategyLeastBusy": "Least Busy", + "strategyUsageBased": "Usage Based", + "strategyLatencyBased": "Latency Based", + "howRoutingWorksTitle": "How routing works for this group", + "howRoutingWorksDesc": "Callers request any model in the group by name — LiteLLM picks a deployment behind the scenes using the {{strategy}} strategy.", + "tabCurl": "cURL", + "tabPython": "Python (OpenAI SDK)", + "tabJavascript": "JavaScript (OpenAI SDK)" + }, + "index": { + "searchPlaceholder": "Search groups...", + "showingResults_one": "Showing {{count}} result", + "showingResults_other": "Showing {{count}} results", + "createGroup": "Create Group", + "deleteTitle": "Delete routing group?", + "deleteFallbackNote": "Models in {{groupName}} will fall back to the proxy's top-level routing strategy. This cannot be undone.", + "createSucceeded": "Created routing group \"{{groupName}}\"", + "updateSucceeded": "Updated routing group \"{{groupName}}\"", + "deleteSucceeded": "Deleted routing group \"{{groupName}}\"", + "saveFailed": "Failed to save routing group", + "deleteFailed": "Failed to delete routing group" + } } } diff --git a/ui/litellm-dashboard/src/locales/zh-CN.json b/ui/litellm-dashboard/src/locales/zh-CN.json index db82c024e9a..a59949acd43 100644 --- a/ui/litellm-dashboard/src/locales/zh-CN.json +++ b/ui/litellm-dashboard/src/locales/zh-CN.json @@ -7729,5 +7729,60 @@ "title": "向量存储", "empty": "未配置任何向量存储" } + }, + "routingGroups": { + "routingGroupModal": { + "titleCreate": "创建路由组", + "titleEdit": "编辑 {{groupName}}", + "okTextCreate": "创建组", + "okTextEdit": "保存更改", + "groupNameLabel": "组名", + "groupNameRequired": "组名不能为空", + "groupNameMaxLength": "不能超过 {{max}} 个字符", + "groupNamePattern": "只允许字母、数字、点、下划线和短横线", + "groupNameExists": "已存在同名组", + "groupNameExtra": "在 API 调用中将此名称作为模型使用 — LiteLLM 会将请求路由到组内某个模型。", + "groupNamePlaceholder": "fast-chat", + "modelsLabel": "模型", + "modelsRequired": "至少选择一个模型", + "modelsExtra": "此组路由的模型列表中的模型。", + "modelsPlaceholder": "选择模型", + "routingStrategyLabel": "路由策略", + "routingStrategyRequired": "策略不能为空", + "strategyPlaceholder": "选择策略", + "strategyArgsLabel": "策略参数(JSON)", + "strategyArgsExampleLatency": "示例:{ \"ttl\": 3600, \"lowest_latency_buffer\": 0 }", + "strategyArgsExampleUsage": "示例:{ \"ttl\": 60 }", + "strategyArgsMustBeJson": "必须是有效的 JSON", + "strategyArgsPlaceholder": "{ \"ttl\": 3600 }", + "fallbackNote": "未被明确指定到组的模型将回退到代理的顶级路由策略。" + }, + "routingGroupsTable": { + "colGroupName": "组名", + "colModels": "模型", + "colStrategy": "策略", + "strategySimpleShuffle": "简单随机", + "strategyLeastBusy": "最少繁忙", + "strategyUsageBased": "基于用量", + "strategyLatencyBased": "基于延迟", + "howRoutingWorksTitle": "此组的路由方式", + "howRoutingWorksDesc": "调用方按名称请求组内任意模型 — LiteLLM 在后台使用 {{strategy}} 策略选取部署。", + "tabCurl": "cURL", + "tabPython": "Python(OpenAI SDK)", + "tabJavascript": "JavaScript(OpenAI SDK)" + }, + "index": { + "searchPlaceholder": "搜索组...", + "showingResults_one": "显示 {{count}} 条结果", + "showingResults_other": "显示 {{count}} 条结果", + "createGroup": "创建组", + "deleteTitle": "删除路由组?", + "deleteFallbackNote": "{{groupName}} 中的模型将回退到代理的顶级路由策略。此操作不可撤销。", + "createSucceeded": "已创建路由组\"{{groupName}}\"", + "updateSucceeded": "已更新路由组\"{{groupName}}\"", + "deleteSucceeded": "已删除路由组\"{{groupName}}\"", + "saveFailed": "保存路由组失败", + "deleteFailed": "删除路由组失败" + } } }