diff --git a/ui/litellm-dashboard/src/components/templates/KeyInfoHeader.tsx b/ui/litellm-dashboard/src/components/templates/KeyInfoHeader.tsx index c9c393352cc..5b40ce0c0c1 100644 --- a/ui/litellm-dashboard/src/components/templates/KeyInfoHeader.tsx +++ b/ui/litellm-dashboard/src/components/templates/KeyInfoHeader.tsx @@ -13,6 +13,7 @@ import { TransactionOutlined, FieldTimeOutlined, } from "@ant-design/icons"; +import { useTranslation } from "react-i18next"; import LabeledField from "../common_components/LabeledField"; import DefaultProxyAdminTag from "../common_components/DefaultProxyAdminTag"; @@ -45,13 +46,14 @@ interface KeyInfoHeaderProps { } function UserField({ userAlias, userEmail, userId }: { userAlias?: string | null; userEmail: string; userId: string }) { + const { t } = useTranslation(); const labelEl = ( - User + {t("templates.keyInfoHeader.user")} ); @@ -74,9 +76,9 @@ function UserField({ userAlias, userEmail, userId }: { userAlias?: string | null const popoverContent = (
{[ - { label: "User Alias", value: userAlias ?? null }, - { label: "User Email", value: userEmail || null }, - { label: "User ID", value: userId || null }, + { label: t("templates.keyInfoHeader.userAlias"), value: userAlias ?? null }, + { label: t("templates.keyInfoHeader.userEmail"), value: userEmail || null }, + { label: t("templates.keyInfoHeader.userId"), value: userId || null }, ].map(({ label, value }) => (
{label} @@ -134,33 +136,42 @@ export function KeyInfoHeader({ onDelete, onResetSpend, canModifyKey = true, - backButtonText = "Back to Keys", + backButtonText, regenerateDisabled = false, regenerateTooltip, }: KeyInfoHeaderProps) { + const { t } = useTranslation(); + const resolvedBackButtonText = backButtonText ?? t("templates.keyInfoHeader.backToKeys"); return (
{onCreateNew && (
)}
- + <Title + level={3} + copyable={{ tooltips: [t("templates.keyInfoHeader.copyKeyAlias"), t("common.copied")] }} + style={{ margin: 0 }} + > {data.keyName} - - Key ID: {data.keyId} + + {t("templates.keyInfoHeader.keyId", { keyId: data.keyId })}
{canModifyKey && ( @@ -168,17 +179,17 @@ export function KeyInfoHeader({ {onResetSpend && ( )} )} @@ -187,15 +198,19 @@ export function KeyInfoHeader({ - } /> + } + /> - } /> + } /> } truncate @@ -207,8 +222,16 @@ export function KeyInfoHeader({ - } /> - } /> + } + /> + } + />
diff --git a/ui/litellm-dashboard/src/components/templates/key_edit_view.tsx b/ui/litellm-dashboard/src/components/templates/key_edit_view.tsx index fe0fa1ab0c2..bc2fd90bc10 100644 --- a/ui/litellm-dashboard/src/components/templates/key_edit_view.tsx +++ b/ui/litellm-dashboard/src/components/templates/key_edit_view.tsx @@ -7,6 +7,7 @@ import { InfoCircleOutlined } from "@ant-design/icons"; import { TextInput, Button as TremorButton } from "@tremor/react"; import { Form, Input, Select, Switch, Tooltip } from "antd"; import { useEffect, useState } from "react"; +import { useTranslation } from "react-i18next"; import { rolesWithWriteAccess } from "../../utils/roles"; import AgentSelector from "../agent_management/AgentSelector"; import AccessGroupSelector from "../common_components/AccessGroupSelector"; @@ -88,6 +89,7 @@ export function KeyEditView({ userRole, premiumUser = false, }: KeyEditViewProps) { + const { t } = useTranslation(); const canEditGuardrails = premiumUser || (userRole != null && rolesWithWriteAccess.includes(userRole)); const [form] = Form.useForm(); const [promptsList, setPromptsList] = useState([]); @@ -250,7 +252,7 @@ export function KeyEditView({ const response = await tagListCall(accessToken); setTagsList(response); } catch (error) { - NotificationsManager.fromBackend("Error fetching tags: " + error); + NotificationsManager.fromBackend(t("templates.keyEditView.errorFetchingTags", { error })); } }; fetchTags(); @@ -303,11 +305,11 @@ export function KeyEditView({ return (
- + - + @@ -331,14 +333,16 @@ export function KeyEditView({ <> {isDisabled && (
- Models field is disabled for this key type + {t("templates.keyEditView.modelsDisabled")}
)} @@ -356,7 +360,7 @@ export function KeyEditView({
- + prevValues.allowed_routes !== currentValues.allowed_routes} @@ -375,7 +379,7 @@ export function KeyEditView({ return ( + - - + + - + - Budget Windows{" "} - + {t("templates.keyEditView.budgetWindows")}{" "} + @@ -463,31 +471,31 @@ export function KeyEditView({ - + - + - + - + - + - + {accessToken && ( { @@ -502,8 +510,8 @@ export function KeyEditView({ - Disable Global Guardrails{" "} - + {t("templates.keyEditView.disableGlobalGuardrails")}{" "} + @@ -511,14 +519,14 @@ export function KeyEditView({ name="disable_global_guardrails" valuePropName="checked" > - + - Policies{" "} - + {t("templates.keyEditView.policies")}{" "} + @@ -536,11 +544,11 @@ export function KeyEditView({ )} - + 0 - ? `Current: ${keyData.metadata.prompts.join(", ")}` - : "Select or enter prompts" + ? t("templates.keyEditView.currentPrompts", { prompts: keyData.metadata.prompts.join(", ") }) + : t("templates.keyEditView.selectOrEnterPrompts") } options={promptsList.map((name) => ({ value: name, label: name }))} /> @@ -570,54 +578,53 @@ export function KeyEditView({ - Access Groups{" "} - + {t("templates.keyEditView.accessGroups")}{" "} + } name="access_group_ids" > - + - - + + form.setFieldValue("allowed_passthrough_routes", values)} value={form.getFieldValue("allowed_passthrough_routes")} accessToken={accessToken || ""} placeholder={ !premiumUser - ? "Premium feature - Upgrade to set allowed pass through routes by key" + ? t("templates.keyEditView.passThroughPremiumPlaceholder") : Array.isArray(keyData.metadata?.allowed_passthrough_routes) && keyData.metadata.allowed_passthrough_routes.length > 0 - ? `Current: ${keyData.metadata.allowed_passthrough_routes.join(", ")}` - : "Select or enter allowed pass through routes" + ? t("templates.keyEditView.currentPassThroughRoutes", { + routes: keyData.metadata.allowed_passthrough_routes.join(", "), + }) + : t("templates.keyEditView.selectOrEnterPassThroughRoutes") } disabled={!premiumUser} /> - + form.setFieldValue("vector_stores", values)} value={form.getFieldValue("vector_stores")} accessToken={accessToken || ""} - placeholder="Select vector stores" + placeholder={t("templates.keyEditView.selectVectorStores")} /> - + form.setFieldValue("mcp_servers_and_groups", val)} value={form.getFieldValue("mcp_servers_and_groups")} accessToken={accessToken || ""} - placeholder="Select MCP servers or access groups (optional)" + placeholder={t("templates.keyEditView.selectMcpServers")} /> @@ -645,20 +652,20 @@ export function KeyEditView({ )} - + form.setFieldValue("agents_and_groups", val)} value={form.getFieldValue("agents_and_groups")} accessToken={accessToken || ""} - placeholder="Select agents or access groups (optional)" + placeholder={t("templates.keyEditView.selectAgents")} /> - Organization{" "} - + {t("templates.keyEditView.organization")}{" "} + @@ -677,12 +684,12 @@ export function KeyEditView({ )} - + form.setFieldValue("logging_settings", values)} @@ -734,7 +741,7 @@ export function KeyEditView({ /> - + @@ -775,10 +782,10 @@ export function KeyEditView({
- Cancel + {t("common.cancel")} - Save Changes + {t("templates.keyEditView.saveChanges")}
diff --git a/ui/litellm-dashboard/src/locales/en.json b/ui/litellm-dashboard/src/locales/en.json index 3ff1aa2c280..93855433caf 100644 --- a/ui/litellm-dashboard/src/locales/en.json +++ b/ui/litellm-dashboard/src/locales/en.json @@ -7784,5 +7784,92 @@ "saveFailed": "Failed to save routing group", "deleteFailed": "Failed to delete routing group" } + }, + "templates": { + "keyInfoHeader": { + "user": "User", + "userAlias": "User Alias", + "userEmail": "User Email", + "userId": "User ID", + "createNewKey": "Create New Key", + "backToKeys": "Back to Keys", + "copyKeyAlias": "Copy Key Alias", + "copyKeyId": "Copy Key ID", + "keyId": "Key ID: {{keyId}}", + "regenerateKey": "Regenerate Key", + "resetSpend": "Reset Spend", + "deleteKey": "Delete Key", + "expires": "Expires", + "createdBy": "Created By", + "lastUpdated": "Last Updated", + "lastActive": "Last Active" + }, + "keyEditView": { + "keyAlias": "Key Alias", + "models": "Models", + "selectModels": "Select models", + "allTeamModels": "All Team Models", + "modelsDisabled": "Models field is disabled for this key type", + "keyType": "Key Type", + "selectKeyType": "Select key type", + "fullAccess": "Full Access", + "fullAccessDesc": "Can call all routes (AI APIs, Management, and read-only)", + "aiApis": "AI APIs", + "aiApisDesc": "Can call only AI API routes (chat/completions, embeddings, etc.)", + "management": "Management", + "managementDesc": "Can call only management routes (user/team/key management)", + "allowedRoutes": "Allowed Routes", + "allowedRoutesTooltip": "List of allowed routes for the key (comma-separated). Can be specific routes (e.g., '/chat/completions') or route patterns (e.g., 'llm_api_routes', 'management_routes', '/keys/*'). Leave empty to allow all routes.", + "allowedRoutesPlaceholder": "Enter allowed routes (comma-separated). Special values: llm_api_routes, management_routes. Examples: llm_api_routes, /chat/completions, /keys/*. Leave empty to allow all routes", + "maxBudget": "Max Budget (USD)", + "enterNumericalValue": "Enter a numerical value", + "resetBudget": "Reset Budget", + "daily": "Daily", + "weekly": "Weekly", + "monthly": "Monthly", + "budgetWindows": "Budget Windows", + "budgetWindowsTooltip": "Set multiple independent budget windows (e.g., hourly $10 AND monthly $200). Each window tracks spend separately and resets on its own schedule.", + "tpmLimit": "TPM Limit", + "rpmLimit": "RPM Limit", + "maxParallelRequests": "Max Parallel Requests", + "modelTpmLimit": "Model TPM Limit", + "modelRpmLimit": "Model RPM Limit", + "guardrails": "Guardrails", + "disableGlobalGuardrails": "Disable Global Guardrails", + "disableGlobalGuardrailsTooltip": "When enabled, this key will bypass any guardrails configured to run on every request (global guardrails)", + "policies": "Policies", + "policiesTooltip": "Apply policies to this key to control guardrails and other settings", + "tags": "Tags", + "selectOrEnterTags": "Select or enter tags", + "prompts": "Prompts", + "promptsPremiumTooltip": "Setting prompts by key is a premium feature", + "promptsPremiumPlaceholder": "Premium feature - Upgrade to set prompts by key", + "currentPrompts": "Current: {{prompts}}", + "selectOrEnterPrompts": "Select or enter prompts", + "accessGroups": "Access Groups", + "accessGroupsTooltip": "Assign access groups to this key. Access groups control which models, MCP servers, and agents this key can use", + "selectAccessGroups": "Select access groups (optional)", + "allowedPassThroughRoutes": "Allowed Pass Through Routes", + "passThroughPremiumTooltip": "Setting allowed pass through routes by key is a premium feature", + "passThroughPremiumPlaceholder": "Premium feature - Upgrade to set allowed pass through routes by key", + "currentPassThroughRoutes": "Current: {{routes}}", + "selectOrEnterPassThroughRoutes": "Select or enter allowed pass through routes", + "vectorStores": "Vector Stores", + "selectVectorStores": "Select vector stores", + "mcpServers": "MCP Servers / Access Groups", + "selectMcpServers": "Select MCP servers or access groups (optional)", + "agents": "Agents / Access Groups", + "selectAgents": "Select agents or access groups (optional)", + "organization": "Organization", + "organizationTooltip": "The organization this key belongs to. Selecting an organization filters the available teams.", + "teamId": "Team ID", + "teamLockedByProject": "Team is locked because this key belongs to a project", + "selectTeam": "Select team", + "project": "Project", + "loggingSettings": "Logging Settings", + "metadata": "Metadata", + "saveChanges": "Save Changes", + "errorFetchingTags": "Error fetching tags: {{error}}" + } } } diff --git a/ui/litellm-dashboard/src/locales/zh-CN.json b/ui/litellm-dashboard/src/locales/zh-CN.json index a59949acd43..3dfd51c9d19 100644 --- a/ui/litellm-dashboard/src/locales/zh-CN.json +++ b/ui/litellm-dashboard/src/locales/zh-CN.json @@ -7784,5 +7784,92 @@ "saveFailed": "保存路由组失败", "deleteFailed": "删除路由组失败" } + }, + "templates": { + "keyInfoHeader": { + "user": "用户", + "userAlias": "用户别名", + "userEmail": "用户邮箱", + "userId": "用户 ID", + "createNewKey": "创建新密钥", + "backToKeys": "返回密钥列表", + "copyKeyAlias": "复制密钥别名", + "copyKeyId": "复制密钥 ID", + "keyId": "密钥 ID:{{keyId}}", + "regenerateKey": "重新生成密钥", + "resetSpend": "重置花费", + "deleteKey": "删除密钥", + "expires": "过期时间", + "createdBy": "创建人", + "lastUpdated": "最近更新", + "lastActive": "最近活跃" + }, + "keyEditView": { + "keyAlias": "密钥别名", + "models": "模型", + "selectModels": "选择模型", + "allTeamModels": "所有团队模型", + "modelsDisabled": "该密钥类型已禁用模型字段", + "keyType": "密钥类型", + "selectKeyType": "选择密钥类型", + "fullAccess": "完全访问", + "fullAccessDesc": "可调用所有路由(AI API、管理及只读)", + "aiApis": "AI API", + "aiApisDesc": "仅可调用 AI API 路由(chat/completions、embeddings 等)", + "management": "管理", + "managementDesc": "仅可调用管理路由(用户/团队/密钥管理)", + "allowedRoutes": "允许的路由", + "allowedRoutesTooltip": "密钥允许的路由列表(逗号分隔)。可以是具体路由(如 '/chat/completions')或路由模式(如 'llm_api_routes'、'management_routes'、'/keys/*')。留空表示允许所有路由。", + "allowedRoutesPlaceholder": "输入允许的路由(逗号分隔)。特殊值:llm_api_routes、management_routes。示例:llm_api_routes, /chat/completions, /keys/*。留空表示允许所有路由", + "maxBudget": "最大预算(USD)", + "enterNumericalValue": "请输入数值", + "resetBudget": "重置预算", + "daily": "每日", + "weekly": "每周", + "monthly": "每月", + "budgetWindows": "预算窗口", + "budgetWindowsTooltip": "设置多个独立的预算窗口(例如每小时 $10 且每月 $200)。每个窗口独立追踪花费并按各自周期重置。", + "tpmLimit": "TPM 限制", + "rpmLimit": "RPM 限制", + "maxParallelRequests": "最大并发请求数", + "modelTpmLimit": "模型 TPM 限制", + "modelRpmLimit": "模型 RPM 限制", + "guardrails": "护栏", + "disableGlobalGuardrails": "禁用全局护栏", + "disableGlobalGuardrailsTooltip": "启用后,该密钥将跳过配置为对每个请求运行的护栏(全局护栏)", + "policies": "策略", + "policiesTooltip": "为该密钥应用策略以控制护栏和其他设置", + "tags": "标签", + "selectOrEnterTags": "选择或输入标签", + "prompts": "提示词", + "promptsPremiumTooltip": "按密钥设置提示词是高级功能", + "promptsPremiumPlaceholder": "高级功能 - 升级后可按密钥设置提示词", + "currentPrompts": "当前:{{prompts}}", + "selectOrEnterPrompts": "选择或输入提示词", + "accessGroups": "访问组", + "accessGroupsTooltip": "为该密钥分配访问组。访问组控制该密钥可使用的模型、MCP 服务器和 Agent", + "selectAccessGroups": "选择访问组(可选)", + "allowedPassThroughRoutes": "允许的透传路由", + "passThroughPremiumTooltip": "按密钥设置允许的透传路由是高级功能", + "passThroughPremiumPlaceholder": "高级功能 - 升级后可按密钥设置允许的透传路由", + "currentPassThroughRoutes": "当前:{{routes}}", + "selectOrEnterPassThroughRoutes": "选择或输入允许的透传路由", + "vectorStores": "向量存储", + "selectVectorStores": "选择向量存储", + "mcpServers": "MCP 服务器 / 访问组", + "selectMcpServers": "选择 MCP 服务器或访问组(可选)", + "agents": "Agent / 访问组", + "selectAgents": "选择 Agent 或访问组(可选)", + "organization": "组织", + "organizationTooltip": "该密钥所属的组织。选择组织可筛选可用团队。", + "teamId": "团队 ID", + "teamLockedByProject": "团队已锁定,因为该密钥属于某个项目", + "selectTeam": "选择团队", + "project": "项目", + "loggingSettings": "日志设置", + "metadata": "元数据", + "saveChanges": "保存更改", + "errorFetchingTags": "获取标签时出错:{{error}}" + } } }