diff --git a/ui/litellm-dashboard/src/components/templates/key_info_view.tsx b/ui/litellm-dashboard/src/components/templates/key_info_view.tsx index 018880b70aa..3216081e251 100644 --- a/ui/litellm-dashboard/src/components/templates/key_info_view.tsx +++ b/ui/litellm-dashboard/src/components/templates/key_info_view.tsx @@ -23,6 +23,7 @@ import ObjectPermissionsView from "../object_permissions_view"; import { RegenerateKeyModal } from "../organisms/RegenerateKeyModal"; import { parseErrorMessage } from "../shared/errorUtils"; import { KeyEditView } from "./key_edit_view"; +import { Trans, useTranslation } from "react-i18next"; interface KeyInfoViewProps { keyId: string; @@ -61,8 +62,9 @@ export default function KeyInfoView({ teams, onKeyDataUpdate, onDelete, - backButtonText = "Back to Keys", + backButtonText, }: KeyInfoViewProps) { + const { t } = useTranslation(); const { accessToken, userId: userID, userRole, premiumUser } = useAuthorized(); const canEditGuardrails = premiumUser || (userRole != null && rolesWithWriteAccess.includes(userRole)); const { teams: teamsData } = useTeams(); @@ -140,9 +142,9 @@ export default function KeyInfoView({ return (
- Key not found + {t("templates.keyInfoView.keyNotFound")}
); } @@ -262,7 +264,7 @@ export default function KeyInfoView({ }; } catch (error) { console.error("Error parsing metadata JSON:", error); - NotificationManager.error("Invalid metadata JSON"); + NotificationManager.error(t("templates.keyInfoView.invalidMetadataJson")); return; } } else { @@ -307,7 +309,7 @@ export default function KeyInfoView({ if (onKeyDataUpdate) { onKeyDataUpdate(newKeyValues); } - NotificationManager.success("Key updated successfully"); + NotificationManager.success(t("templates.keyInfoView.keyUpdatedSuccess")); setIsEditing(false); // Refresh key data here if needed } catch (error) { @@ -321,7 +323,7 @@ export default function KeyInfoView({ setDeleteLoading(true); if (!accessToken) return; await keyDeleteCall(accessToken as string, currentKeyData.token || currentKeyData.token_id); - NotificationManager.success("Key deleted successfully"); + NotificationManager.success(t("templates.keyInfoView.keyDeletedSuccess")); if (onDelete) { onDelete(); } @@ -401,7 +403,7 @@ export default function KeyInfoView({ if (onKeyDataUpdate) { onKeyDataUpdate({ spend: 0 }); } - NotificationManager.success("Key spend reset to $0"); + NotificationManager.success(t("templates.keyInfoView.keySpendResetSuccess")); setIsResetSpendModalOpen(false); }, onError: (error) => { @@ -415,7 +417,7 @@ export default function KeyInfoView({
setIsRegenerateModalOpen(true)} onDelete={() => setIsDeleteModalOpen(true)} onResetSpend={canResetSpend ? () => setIsResetSpendModalOpen(true) : undefined} canModifyKey={canModifyKey} - backButtonText={backButtonText} + backButtonText={backButtonText ?? t("templates.keyInfoView.backToKeys")} regenerateDisabled={!premiumUser} - regenerateTooltip={ - !premiumUser ? "This is a LiteLLM Enterprise feature, and requires a valid key to use." : undefined - } + regenerateTooltip={!premiumUser ? t("templates.keyInfoView.enterpriseFeatureTooltip") : undefined} /> {/* Add RegenerateKeyModal */} @@ -453,27 +453,27 @@ export default function KeyInfoView({ {/* Delete Confirmation Modal */} setIsResetSpendModalOpen(false)} - okText="Reset" + okText={t("common.reset")} okButtonProps={{ danger: true }} confirmLoading={resetSpendLoading} >

- Reset spend for {currentKeyData?.key_alias || currentKeyData?.token_id || "this key"} to{" "} - $0? + }} + />

- Current spend: ${formatNumberWithCommas(currentKeyData.spend, 4)}. Spend history is preserved - in logs. This resets the current period spend counter, the same as an automatic budget reset. + }} + />

- Overview - Settings + {t("templates.keyInfoView.tabOverview")} + {t("common.settings")} @@ -517,28 +525,42 @@ export default function KeyInfoView({ - Spend + {t("templates.keyInfoView.spend")}
${formatNumberWithCommas(currentKeyData.spend, 4)} - of{" "} + {t("templates.keyInfoView.of")}{" "} {currentKeyData.max_budget !== null ? `$${formatNumberWithCommas(currentKeyData.max_budget, 2)}` - : "Unlimited"} + : t("templates.keyInfoView.unlimited")}
- Rate Limits + {t("templates.keyInfoView.rateLimits")}
- TPM: {currentKeyData.tpm_limit !== null ? currentKeyData.tpm_limit : "Unlimited"} - RPM: {currentKeyData.rpm_limit !== null ? currentKeyData.rpm_limit : "Unlimited"} + + {t("templates.keyInfoView.tpmValue", { + value: + currentKeyData.tpm_limit !== null + ? currentKeyData.tpm_limit + : t("templates.keyInfoView.unlimited"), + })} + + + {t("templates.keyInfoView.rpmValue", { + value: + currentKeyData.rpm_limit !== null + ? currentKeyData.rpm_limit + : t("templates.keyInfoView.unlimited"), + })} +
- Models + {t("templates.keyInfoView.models")}
{currentKeyData.models && currentKeyData.models.length > 0 ? ( currentKeyData.models.map((model, index) => ( @@ -547,7 +569,7 @@ export default function KeyInfoView({ )) ) : ( - No models specified + {t("templates.keyInfoView.noModelsSpecified")} )}
@@ -561,7 +583,7 @@ export default function KeyInfoView({ - Guardrails + {t("templates.keyInfoView.guardrails")} {Array.isArray(currentKeyData.metadata?.guardrails) && currentKeyData.metadata.guardrails.length > 0 ? (
{currentKeyData.metadata.guardrails.map((guardrail: string, index: number) => ( @@ -571,29 +593,35 @@ export default function KeyInfoView({ ))}
) : ( - No guardrails configured + {t("templates.keyInfoView.noGuardrailsConfigured")} )} {typeof currentKeyData.metadata?.disable_global_guardrails === "boolean" && currentKeyData.metadata.disable_global_guardrails === true && (
- Global Guardrails Disabled + {t("templates.keyInfoView.globalGuardrailsDisabled")}
)}
- Policies + {t("templates.keyInfoView.policies")} {Array.isArray(currentKeyData.metadata?.policies) && currentKeyData.metadata.policies.length > 0 ? (
{currentKeyData.metadata.policies.map((policy: string, index: number) => (
{policy} - {loadingPolicies && Loading guardrails...} + {loadingPolicies && ( + + {t("templates.keyInfoView.loadingGuardrails")} + + )}
{!loadingPolicies && policyGuardrails[policy] && policyGuardrails[policy].length > 0 && (
- Resolved Guardrails: + + {t("templates.keyInfoView.resolvedGuardrails")} +
{policyGuardrails[policy].map((guardrail: string, gIndex: number) => ( @@ -607,7 +635,7 @@ export default function KeyInfoView({ ))}
) : ( - No policies configured + {t("templates.keyInfoView.noPoliciesConfigured")} )} @@ -636,8 +664,10 @@ export default function KeyInfoView({
- Key Settings - {!isEditing && canModifyKey && } + {t("templates.keyInfoView.keySettingsTitle")} + {!isEditing && canModifyKey && ( + + )}
{isEditing ? ( @@ -654,28 +684,28 @@ export default function KeyInfoView({ ) : (
- Key ID + {t("templates.keyInfoView.fieldKeyId")} {currentKeyData.token_id || currentKeyData.token}
- Key Alias - {currentKeyData.key_alias || "Not Set"} + {t("templates.keyInfoView.fieldKeyAlias")} + {currentKeyData.key_alias || t("templates.keyInfoView.notSet")}
- Secret Key + {t("templates.keyInfoView.fieldSecretKey")} {currentKeyData.key_name}
- Team ID - {currentKeyData.team_id || "Not Set"} + {t("templates.keyInfoView.fieldTeamId")} + {currentKeyData.team_id || t("templates.keyInfoView.notSet")}
{enableProjectsUI && (
- Project + {t("templates.keyInfoView.fieldProject")} {currentKeyData.project_id ? (() => { @@ -684,36 +714,38 @@ export default function KeyInfoView({ ? `${project.project_alias} (${currentKeyData.project_id})` : currentKeyData.project_id; })() - : "Not Set"} + : t("templates.keyInfoView.notSet")}
)}
- Organization - {(currentKeyData.organization_id ?? currentKeyData.org_id) || "Not Set"} + {t("templates.keyInfoView.fieldOrganization")} + + {(currentKeyData.organization_id ?? currentKeyData.org_id) || t("templates.keyInfoView.notSet")} +
- Created + {t("templates.keyInfoView.fieldCreated")} {formatTimestamp(currentKeyData.created_at)}
{lastRegeneratedAt && (
- Last Regenerated + {t("templates.keyInfoView.fieldLastRegenerated")}
{formatTimestamp(lastRegeneratedAt)} - Recent + {t("templates.keyInfoView.recentBadge")}
)}
- Expires - {currentKeyData.expires ? formatTimestamp(currentKeyData.expires) : "Never"} + {t("templates.keyInfoView.fieldExpires")} + {currentKeyData.expires ? formatTimestamp(currentKeyData.expires) : t("common.never")}
- Spend - ${formatNumberWithCommas(currentKeyData.spend, 4)} USD -
- -
- Budget + {t("templates.keyInfoView.spend")} - {currentKeyData.max_budget !== null - ? `$${formatNumberWithCommas(currentKeyData.max_budget, 2)}` - : "Unlimited"} + {t("templates.keyInfoView.spendValue", { + amount: formatNumberWithCommas(currentKeyData.spend, 4), + })}
- Tags + {t("templates.keyInfoView.budget")} + + {currentKeyData.max_budget !== null + ? `$${formatNumberWithCommas(currentKeyData.max_budget, 2)}` + : t("templates.keyInfoView.unlimited")} + +
+ +
+ {t("templates.keyInfoView.fieldTags")}
{Array.isArray(currentKeyData.metadata?.tags) && currentKeyData.metadata.tags.length > 0 ? currentKeyData.metadata.tags.map((tag, index) => ( @@ -749,12 +785,12 @@ export default function KeyInfoView({ {tag} )) - : "No tags specified"} + : t("templates.keyInfoView.noTagsSpecified")}
- Prompts + {t("templates.keyInfoView.fieldPrompts")} {Array.isArray(currentKeyData.metadata?.prompts) && currentKeyData.metadata.prompts.length > 0 ? currentKeyData.metadata.prompts.map((prompt, index) => ( @@ -762,12 +798,12 @@ export default function KeyInfoView({ {prompt} )) - : "No prompts specified"} + : t("templates.keyInfoView.noPromptsSpecified")}
- Allowed Routes + {t("templates.keyInfoView.fieldAllowedRoutes")}
{Array.isArray(currentKeyData.allowed_routes) && currentKeyData.allowed_routes.length > 0 ? ( currentKeyData.allowed_routes.map((route, index) => ( @@ -776,13 +812,13 @@ export default function KeyInfoView({ )) ) : ( - All routes allowed + {t("templates.keyInfoView.allRoutesAllowed")} )}
- Allowed Pass Through Routes + {t("templates.keyInfoView.fieldAllowedPassThroughRoutes")} {Array.isArray(currentKeyData.metadata?.allowed_passthrough_routes) && currentKeyData.metadata.allowed_passthrough_routes.length > 0 @@ -791,23 +827,23 @@ export default function KeyInfoView({ {route} )) - : "No pass through routes specified"} + : t("templates.keyInfoView.noPassThroughRoutesSpecified")}
- Disable Global Guardrails + {t("templates.keyInfoView.fieldDisableGlobalGuardrails")} {currentKeyData.metadata?.disable_global_guardrails === true ? ( - Enabled - Global guardrails bypassed + {t("templates.keyInfoView.globalGuardrailsBypassed")} ) : ( - Disabled - Global guardrails active + {t("templates.keyInfoView.globalGuardrailsActive")} )}
- Models + {t("templates.keyInfoView.models")}
{currentKeyData.models && currentKeyData.models.length > 0 ? ( currentKeyData.models.map((model, index) => ( @@ -816,37 +852,55 @@ export default function KeyInfoView({ )) ) : ( - No models specified + {t("templates.keyInfoView.noModelsSpecified")} )}
- Rate Limits - TPM: {currentKeyData.tpm_limit !== null ? currentKeyData.tpm_limit : "Unlimited"} - RPM: {currentKeyData.rpm_limit !== null ? currentKeyData.rpm_limit : "Unlimited"} + {t("templates.keyInfoView.rateLimits")} - Max Parallel Requests:{" "} - {currentKeyData.max_parallel_requests !== null - ? currentKeyData.max_parallel_requests - : "Unlimited"} + {t("templates.keyInfoView.tpmValue", { + value: + currentKeyData.tpm_limit !== null + ? currentKeyData.tpm_limit + : t("templates.keyInfoView.unlimited"), + })} - Model TPM Limits:{" "} - {currentKeyData.metadata?.model_tpm_limit - ? JSON.stringify(currentKeyData.metadata.model_tpm_limit) - : "Unlimited"} + {t("templates.keyInfoView.rpmValue", { + value: + currentKeyData.rpm_limit !== null + ? currentKeyData.rpm_limit + : t("templates.keyInfoView.unlimited"), + })} - Model RPM Limits:{" "} - {currentKeyData.metadata?.model_rpm_limit - ? JSON.stringify(currentKeyData.metadata.model_rpm_limit) - : "Unlimited"} + {t("templates.keyInfoView.maxParallelRequests", { + value: + currentKeyData.max_parallel_requests !== null + ? currentKeyData.max_parallel_requests + : t("templates.keyInfoView.unlimited"), + })} + + + {t("templates.keyInfoView.modelTpmLimits", { + value: currentKeyData.metadata?.model_tpm_limit + ? JSON.stringify(currentKeyData.metadata.model_tpm_limit) + : t("templates.keyInfoView.unlimited"), + })} + + + {t("templates.keyInfoView.modelRpmLimits", { + value: currentKeyData.metadata?.model_rpm_limit + ? JSON.stringify(currentKeyData.metadata.model_rpm_limit) + : t("templates.keyInfoView.unlimited"), + })}
- Metadata + {t("templates.keyInfoView.fieldMetadata")}
                       {formatMetadataForDisplay(stripTagsFromMetadata(currentKeyData.metadata))}
                     
diff --git a/ui/litellm-dashboard/src/locales/en.json b/ui/litellm-dashboard/src/locales/en.json index 93855433caf..165d3783e00 100644 --- a/ui/litellm-dashboard/src/locales/en.json +++ b/ui/litellm-dashboard/src/locales/en.json @@ -7870,6 +7870,73 @@ "metadata": "Metadata", "saveChanges": "Save Changes", "errorFetchingTags": "Error fetching tags: {{error}}" + }, + "keyInfoView": { + "backToKeys": "Back to Keys", + "keyNotFound": "Key not found", + "invalidMetadataJson": "Invalid metadata JSON", + "keyUpdatedSuccess": "Key updated successfully", + "keyDeletedSuccess": "Key deleted successfully", + "keySpendResetSuccess": "Key spend reset to $0", + "virtualKey": "Virtual Key", + "enterpriseFeatureTooltip": "This is a LiteLLM Enterprise feature, and requires a valid key to use.", + "deleteKeyTitle": "Delete Key", + "deleteKeyAlertMessage": "This action is irreversible and will immediately revoke access for any applications using this key.", + "deleteKeyMessage": "Are you sure you want to delete this Virtual Key?", + "keyInformation": "Key Information", + "keyAlias": "Key Alias", + "keyId": "Key ID", + "teamId": "Team ID", + "spend": "Spend", + "resetKeySpendTitle": "Reset Key Spend", + "thisKey": "this key", + "resetSpendConfirm": "Reset spend for {{keyName}} to $0?", + "resetSpendCurrentSpend": "Current spend: ${{amount}}. Spend history is preserved in logs. This resets the current period spend counter, the same as an automatic budget reset.", + "tabOverview": "Overview", + "of": "of", + "unlimited": "Unlimited", + "rateLimits": "Rate Limits", + "tpmValue": "TPM: {{value}}", + "rpmValue": "RPM: {{value}}", + "models": "Models", + "noModelsSpecified": "No models specified", + "guardrails": "Guardrails", + "noGuardrailsConfigured": "No guardrails configured", + "globalGuardrailsDisabled": "Global Guardrails Disabled", + "policies": "Policies", + "loadingGuardrails": "Loading guardrails...", + "resolvedGuardrails": "Resolved Guardrails:", + "noPoliciesConfigured": "No policies configured", + "keySettingsTitle": "Key Settings", + "editSettings": "Edit Settings", + "fieldKeyId": "Key ID", + "fieldKeyAlias": "Key Alias", + "notSet": "Not Set", + "fieldSecretKey": "Secret Key", + "fieldTeamId": "Team ID", + "fieldProject": "Project", + "fieldOrganization": "Organization", + "fieldCreated": "Created", + "fieldLastRegenerated": "Last Regenerated", + "recentBadge": "Recent", + "fieldExpires": "Expires", + "spendValue": "${{amount}} USD", + "budget": "Budget", + "fieldTags": "Tags", + "noTagsSpecified": "No tags specified", + "fieldPrompts": "Prompts", + "noPromptsSpecified": "No prompts specified", + "fieldAllowedRoutes": "Allowed Routes", + "allRoutesAllowed": "All routes allowed", + "fieldAllowedPassThroughRoutes": "Allowed Pass Through Routes", + "noPassThroughRoutesSpecified": "No pass through routes specified", + "fieldDisableGlobalGuardrails": "Disable Global Guardrails", + "globalGuardrailsBypassed": "Enabled - Global guardrails bypassed", + "globalGuardrailsActive": "Disabled - Global guardrails active", + "maxParallelRequests": "Max Parallel Requests: {{value}}", + "modelTpmLimits": "Model TPM Limits: {{value}}", + "modelRpmLimits": "Model RPM Limits: {{value}}", + "fieldMetadata": "Metadata" } } } diff --git a/ui/litellm-dashboard/src/locales/zh-CN.json b/ui/litellm-dashboard/src/locales/zh-CN.json index 3dfd51c9d19..29da5ab6acf 100644 --- a/ui/litellm-dashboard/src/locales/zh-CN.json +++ b/ui/litellm-dashboard/src/locales/zh-CN.json @@ -7870,6 +7870,73 @@ "metadata": "元数据", "saveChanges": "保存更改", "errorFetchingTags": "获取标签时出错:{{error}}" + }, + "keyInfoView": { + "backToKeys": "返回密钥列表", + "keyNotFound": "密钥未找到", + "invalidMetadataJson": "无效的元数据 JSON", + "keyUpdatedSuccess": "密钥更新成功", + "keyDeletedSuccess": "密钥删除成功", + "keySpendResetSuccess": "密钥花费已重置为 $0", + "virtualKey": "虚拟密钥", + "enterpriseFeatureTooltip": "这是 LiteLLM 企业版功能,需要有效密钥才能使用。", + "deleteKeyTitle": "删除密钥", + "deleteKeyAlertMessage": "此操作不可撤销,将立即撤销所有使用此密钥的应用程序的访问权限。", + "deleteKeyMessage": "确定要删除此虚拟密钥吗?", + "keyInformation": "密钥信息", + "keyAlias": "密钥别名", + "keyId": "密钥 ID", + "teamId": "团队 ID", + "spend": "花费", + "resetKeySpendTitle": "重置密钥花费", + "thisKey": "此密钥", + "resetSpendConfirm": "将 {{keyName}} 的花费重置为 $0?", + "resetSpendCurrentSpend": "当前花费:${{amount}}。花费历史记录保留在日志中。这将重置当前周期的花费计数器,与自动预算重置相同。", + "tabOverview": "概览", + "of": ",共", + "unlimited": "无限制", + "rateLimits": "速率限制", + "tpmValue": "TPM:{{value}}", + "rpmValue": "RPM:{{value}}", + "models": "模型", + "noModelsSpecified": "未指定模型", + "guardrails": "护栏", + "noGuardrailsConfigured": "未配置护栏", + "globalGuardrailsDisabled": "全局护栏已禁用", + "policies": "策略", + "loadingGuardrails": "正在加载护栏...", + "resolvedGuardrails": "已解析护栏:", + "noPoliciesConfigured": "未配置策略", + "keySettingsTitle": "密钥设置", + "editSettings": "编辑设置", + "fieldKeyId": "密钥 ID", + "fieldKeyAlias": "密钥别名", + "notSet": "未设置", + "fieldSecretKey": "密钥值", + "fieldTeamId": "团队 ID", + "fieldProject": "项目", + "fieldOrganization": "组织", + "fieldCreated": "创建时间", + "fieldLastRegenerated": "最近重新生成", + "recentBadge": "最近", + "fieldExpires": "过期时间", + "spendValue": "${{amount}} USD", + "budget": "预算", + "fieldTags": "标签", + "noTagsSpecified": "未指定标签", + "fieldPrompts": "提示词", + "noPromptsSpecified": "未指定提示词", + "fieldAllowedRoutes": "允许的路由", + "allRoutesAllowed": "所有路由均允许", + "fieldAllowedPassThroughRoutes": "允许的透传路由", + "noPassThroughRoutesSpecified": "未指定透传路由", + "fieldDisableGlobalGuardrails": "禁用全局护栏", + "globalGuardrailsBypassed": "已启用 - 全局护栏已绕过", + "globalGuardrailsActive": "已禁用 - 全局护栏已生效", + "maxParallelRequests": "最大并发请求数:{{value}}", + "modelTpmLimits": "模型 TPM 限制:{{value}}", + "modelRpmLimits": "模型 RPM 限制:{{value}}", + "fieldMetadata": "元数据" } } }