mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-03 02:22:24 +00:00
feat(ui): i18n for credentials management
This commit is contained in:
parent
7ddaa3a005
commit
4b0371ae44
6 changed files with 152 additions and 45 deletions
|
|
@ -2,6 +2,7 @@ import { TextInput } from "@tremor/react";
|
|||
import { Select as AntdSelect, Button, Form, Modal, Tooltip, Typography } from "antd";
|
||||
import type { UploadProps } from "antd/es/upload";
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import ProviderSpecificFields from "../add_model/provider_specific_fields";
|
||||
import { Providers, providerLogoMap } from "../provider_info_helpers";
|
||||
const { Link } = Typography;
|
||||
|
|
@ -14,6 +15,7 @@ interface AddCredentialsModalProps {
|
|||
}
|
||||
|
||||
const AddCredentialsModal: React.FC<AddCredentialsModalProps> = ({ open, onCancel, onAddCredential, uploadProps }) => {
|
||||
const { t } = useTranslation();
|
||||
const [form] = Form.useForm();
|
||||
const [selectedProvider, setSelectedProvider] = useState<Providers>(Providers.OpenAI);
|
||||
|
||||
|
|
@ -30,7 +32,7 @@ const AddCredentialsModal: React.FC<AddCredentialsModalProps> = ({ open, onCance
|
|||
|
||||
return (
|
||||
<Modal
|
||||
title="Add New Credential"
|
||||
title={t("modelAdd.addCredentialModal.title")}
|
||||
open={open}
|
||||
onCancel={() => {
|
||||
onCancel();
|
||||
|
|
@ -42,19 +44,19 @@ const AddCredentialsModal: React.FC<AddCredentialsModalProps> = ({ open, onCance
|
|||
<Form form={form} onFinish={handleSubmit} layout="vertical">
|
||||
{/* Credential Name */}
|
||||
<Form.Item
|
||||
label="Credential Name:"
|
||||
label={t("modelAdd.addCredentialModal.credentialNameLabel")}
|
||||
name="credential_name"
|
||||
rules={[{ required: true, message: "Credential name is required" }]}
|
||||
rules={[{ required: true, message: t("modelAdd.addCredentialModal.credentialNameRequired") }]}
|
||||
>
|
||||
<TextInput placeholder="Enter a friendly name for these credentials" />
|
||||
<TextInput placeholder={t("modelAdd.addCredentialModal.credentialNamePlaceholder")} />
|
||||
</Form.Item>
|
||||
|
||||
{/* Provider Selection */}
|
||||
<Form.Item
|
||||
rules={[{ required: true, message: "Required" }]}
|
||||
label="Provider:"
|
||||
rules={[{ required: true, message: t("common.required") }]}
|
||||
label={t("modelAdd.addCredentialModal.providerLabel")}
|
||||
name="custom_llm_provider"
|
||||
tooltip="Helper to auto-populate provider specific fields"
|
||||
tooltip={t("modelAdd.addCredentialModal.providerTooltip")}
|
||||
>
|
||||
<AntdSelect
|
||||
showSearch
|
||||
|
|
@ -93,8 +95,8 @@ const AddCredentialsModal: React.FC<AddCredentialsModalProps> = ({ open, onCance
|
|||
|
||||
{/* Modal Footer */}
|
||||
<div className="flex justify-between items-center">
|
||||
<Tooltip title="Get help on our github">
|
||||
<Link href="https://github.com/BerriAI/litellm/issues">Need Help?</Link>
|
||||
<Tooltip title={t("modelAdd.addCredentialModal.needHelpTooltip")}>
|
||||
<Link href="https://github.com/BerriAI/litellm/issues">{t("modelAdd.addCredentialModal.needHelp")}</Link>
|
||||
</Tooltip>
|
||||
|
||||
<div>
|
||||
|
|
@ -105,9 +107,9 @@ const AddCredentialsModal: React.FC<AddCredentialsModalProps> = ({ open, onCance
|
|||
}}
|
||||
style={{ marginRight: 10 }}
|
||||
>
|
||||
Cancel
|
||||
{t("common.cancel")}
|
||||
</Button>
|
||||
<Button htmlType="submit">{"Add Credential"}</Button>
|
||||
<Button htmlType="submit">{t("modelAdd.addCredentialModal.addCredential")}</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Form>
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { TextInput } from "@tremor/react";
|
|||
import { Select as AntdSelect, Button, Form, Modal, Tooltip, Typography } from "antd";
|
||||
import type { UploadProps } from "antd/es/upload";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import ProviderSpecificFields from "../add_model/provider_specific_fields";
|
||||
import { CredentialItem } from "../networking";
|
||||
import { Providers, providerLogoMap } from "../provider_info_helpers";
|
||||
|
|
@ -22,6 +23,7 @@ export default function EditCredentialsModal({
|
|||
uploadProps,
|
||||
existingCredential,
|
||||
}: EditCredentialsModalProps) {
|
||||
const { t } = useTranslation();
|
||||
const [form] = Form.useForm();
|
||||
const [selectedProvider, setSelectedProvider] = useState<Providers>(Providers.Anthropic);
|
||||
|
||||
|
|
@ -58,7 +60,7 @@ export default function EditCredentialsModal({
|
|||
|
||||
return (
|
||||
<Modal
|
||||
title="Edit Credential"
|
||||
title={t("modelAdd.editCredentialModal.title")}
|
||||
open={open}
|
||||
onCancel={() => {
|
||||
onCancel();
|
||||
|
|
@ -71,23 +73,23 @@ export default function EditCredentialsModal({
|
|||
<Form form={form} onFinish={handleSubmit} layout="vertical">
|
||||
{/* Credential Name */}
|
||||
<Form.Item
|
||||
label="Credential Name:"
|
||||
label={t("modelAdd.editCredentialModal.credentialNameLabel")}
|
||||
name="credential_name"
|
||||
rules={[{ required: true, message: "Credential name is required" }]}
|
||||
rules={[{ required: true, message: t("modelAdd.editCredentialModal.credentialNameRequired") }]}
|
||||
initialValue={existingCredential?.credential_name}
|
||||
>
|
||||
<TextInput
|
||||
placeholder="Enter a friendly name for these credentials"
|
||||
placeholder={t("modelAdd.editCredentialModal.credentialNamePlaceholder")}
|
||||
disabled={existingCredential?.credential_name ? true : false}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
{/* Provider Selection */}
|
||||
<Form.Item
|
||||
rules={[{ required: true, message: "Required" }]}
|
||||
label="Provider:"
|
||||
rules={[{ required: true, message: t("common.required") }]}
|
||||
label={t("modelAdd.editCredentialModal.providerLabel")}
|
||||
name="custom_llm_provider"
|
||||
tooltip="Helper to auto-populate provider specific fields"
|
||||
tooltip={t("modelAdd.editCredentialModal.providerTooltip")}
|
||||
>
|
||||
<AntdSelect
|
||||
showSearch
|
||||
|
|
@ -126,8 +128,8 @@ export default function EditCredentialsModal({
|
|||
|
||||
{/* Modal Footer */}
|
||||
<div className="flex justify-between items-center">
|
||||
<Tooltip title="Get help on our github">
|
||||
<Link href="https://github.com/BerriAI/litellm/issues">Need Help?</Link>
|
||||
<Tooltip title={t("modelAdd.editCredentialModal.needHelpTooltip")}>
|
||||
<Link href="https://github.com/BerriAI/litellm/issues">{t("modelAdd.editCredentialModal.needHelp")}</Link>
|
||||
</Tooltip>
|
||||
|
||||
<div>
|
||||
|
|
@ -138,9 +140,9 @@ export default function EditCredentialsModal({
|
|||
}}
|
||||
style={{ marginRight: 10 }}
|
||||
>
|
||||
Cancel
|
||||
{t("common.cancel")}
|
||||
</Button>
|
||||
<Button htmlType="submit">{"Update Credential"}</Button>
|
||||
<Button htmlType="submit">{t("modelAdd.editCredentialModal.updateCredential")}</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Form>
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import {
|
|||
import { Form } from "antd";
|
||||
import { UploadProps } from "antd/es/upload";
|
||||
import { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import DeleteResourceModal from "../common_components/DeleteResourceModal";
|
||||
import NotificationsManager from "../molecules/notifications_manager";
|
||||
import AddCredentialsTab from "./AddCredentialModal";
|
||||
|
|
@ -32,6 +33,7 @@ interface CredentialsPanelProps {
|
|||
}
|
||||
|
||||
const CredentialsPanel: React.FC<CredentialsPanelProps> = ({ uploadProps }) => {
|
||||
const { t } = useTranslation();
|
||||
const { accessToken, userRole } = useAuthorized();
|
||||
// Admin Viewer follows the read-parity rule: see credentials, do not modify.
|
||||
const canModifyCredentials = isProxyAdminRole(userRole ?? "");
|
||||
|
|
@ -65,7 +67,7 @@ const CredentialsPanel: React.FC<CredentialsPanelProps> = ({ uploadProps }) => {
|
|||
};
|
||||
|
||||
await credentialUpdateCall(accessToken, values.credential_name, newCredential);
|
||||
NotificationsManager.success("Credential updated successfully");
|
||||
NotificationsManager.success(t("modelAdd.credentials.updatedSuccess"));
|
||||
setIsUpdateModalOpen(false);
|
||||
await refetchCredentials();
|
||||
};
|
||||
|
|
@ -89,7 +91,7 @@ const CredentialsPanel: React.FC<CredentialsPanelProps> = ({ uploadProps }) => {
|
|||
|
||||
// Add to list and close modal
|
||||
await credentialCreateCall(accessToken, newCredential);
|
||||
NotificationsManager.success("Credential added successfully");
|
||||
NotificationsManager.success(t("modelAdd.credentials.addedSuccess"));
|
||||
setIsAddModalOpen(false);
|
||||
await refetchCredentials();
|
||||
};
|
||||
|
|
@ -117,10 +119,10 @@ const CredentialsPanel: React.FC<CredentialsPanelProps> = ({ uploadProps }) => {
|
|||
setIsCredentialDeleting(true);
|
||||
try {
|
||||
await credentialDeleteCall(accessToken, credentialToDelete.credential_name);
|
||||
NotificationsManager.success("Credential deleted successfully");
|
||||
NotificationsManager.success(t("modelAdd.credentials.deletedSuccess"));
|
||||
await refetchCredentials();
|
||||
} catch (error) {
|
||||
NotificationsManager.error("Failed to delete credential");
|
||||
NotificationsManager.error(t("modelAdd.credentials.deleteFailed"));
|
||||
} finally {
|
||||
setCredentialToDelete(null);
|
||||
setIsDeleteModalOpen(false);
|
||||
|
|
@ -140,25 +142,27 @@ const CredentialsPanel: React.FC<CredentialsPanelProps> = ({ uploadProps }) => {
|
|||
|
||||
return (
|
||||
<div className="w-full mx-auto flex-auto overflow-y-auto p-2">
|
||||
{canModifyCredentials && <Button onClick={() => setIsAddModalOpen(true)}>Add Credential</Button>}
|
||||
{canModifyCredentials && (
|
||||
<Button onClick={() => setIsAddModalOpen(true)}>{t("modelAdd.credentials.addCredential")}</Button>
|
||||
)}
|
||||
<div className="flex justify-between items-center mt-4 mb-4">
|
||||
<Text>Configured credentials for different AI providers. Add and manage your API credentials.</Text>
|
||||
<Text>{t("modelAdd.credentials.configuredCredentials")}</Text>
|
||||
</div>
|
||||
|
||||
<Card>
|
||||
<Table>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableHeaderCell>Credential Name</TableHeaderCell>
|
||||
<TableHeaderCell>Provider</TableHeaderCell>
|
||||
<TableHeaderCell>Actions</TableHeaderCell>
|
||||
<TableHeaderCell>{t("modelAdd.credentials.credentialName")}</TableHeaderCell>
|
||||
<TableHeaderCell>{t("modelAdd.credentials.provider")}</TableHeaderCell>
|
||||
<TableHeaderCell>{t("common.actions")}</TableHeaderCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{!credentialList || credentialList.length === 0 ? (
|
||||
<TableRow>
|
||||
<TableCell colSpan={4} className="text-center py-4 text-gray-500">
|
||||
No credentials configured
|
||||
{t("modelAdd.credentials.noCredentials")}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : (
|
||||
|
|
@ -219,12 +223,15 @@ const CredentialsPanel: React.FC<CredentialsPanelProps> = ({ uploadProps }) => {
|
|||
isOpen={isDeleteModalOpen}
|
||||
onCancel={closeDeleteModal}
|
||||
onOk={handleDeleteCredential}
|
||||
title="Delete Credential?"
|
||||
message="Are you sure you want to delete this credential? This action cannot be undone and may break existing integrations."
|
||||
resourceInformationTitle="Credential Information"
|
||||
title={t("modelAdd.credentials.deleteTitle")}
|
||||
message={t("modelAdd.credentials.deleteMessage")}
|
||||
resourceInformationTitle={t("modelAdd.credentials.credentialInformation")}
|
||||
resourceInformation={[
|
||||
{ label: "Credential Name", value: credentialToDelete?.credential_name },
|
||||
{ label: "Provider", value: credentialToDelete?.credential_info?.custom_llm_provider || "-" },
|
||||
{ label: t("modelAdd.credentials.credentialName"), value: credentialToDelete?.credential_name },
|
||||
{
|
||||
label: t("modelAdd.credentials.provider"),
|
||||
value: credentialToDelete?.credential_info?.custom_llm_provider || "-",
|
||||
},
|
||||
]}
|
||||
confirmLoading={isCredentialDeleting}
|
||||
requiredConfirmation={credentialToDelete?.credential_name}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import React from "react";
|
||||
import { Form, Button, Tooltip, Typography, Modal } from "antd";
|
||||
import { TextInput } from "@tremor/react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { CredentialItem } from "../networking";
|
||||
const { Title, Link } = Typography;
|
||||
|
||||
|
|
@ -19,6 +20,7 @@ const ReuseCredentialsModal: React.FC<ReuseCredentialsModalProps> = ({
|
|||
existingCredential,
|
||||
setIsCredentialModalOpen,
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const [form] = Form.useForm();
|
||||
|
||||
console.log(`existingCredential in add credentials tab: ${JSON.stringify(existingCredential)}`);
|
||||
|
|
@ -31,7 +33,7 @@ const ReuseCredentialsModal: React.FC<ReuseCredentialsModalProps> = ({
|
|||
|
||||
return (
|
||||
<Modal
|
||||
title="Reuse Credentials"
|
||||
title={t("modelAdd.reuseCredentials.title")}
|
||||
open={isVisible}
|
||||
onCancel={() => {
|
||||
onCancel();
|
||||
|
|
@ -43,12 +45,12 @@ const ReuseCredentialsModal: React.FC<ReuseCredentialsModalProps> = ({
|
|||
<Form form={form} onFinish={handleSubmit} layout="vertical">
|
||||
{/* Credential Name */}
|
||||
<Form.Item
|
||||
label="Credential Name:"
|
||||
label={t("modelAdd.reuseCredentials.credentialNameLabel")}
|
||||
name="credential_name"
|
||||
rules={[{ required: true, message: "Credential name is required" }]}
|
||||
rules={[{ required: true, message: t("modelAdd.reuseCredentials.credentialNameRequired") }]}
|
||||
initialValue={existingCredential?.credential_name}
|
||||
>
|
||||
<TextInput placeholder="Enter a friendly name for these credentials" />
|
||||
<TextInput placeholder={t("modelAdd.reuseCredentials.credentialNamePlaceholder")} />
|
||||
</Form.Item>
|
||||
|
||||
{/* Display Credential Values of existingCredential, don't allow user to edit. Credential values is a dictionary */}
|
||||
|
|
@ -60,8 +62,8 @@ const ReuseCredentialsModal: React.FC<ReuseCredentialsModalProps> = ({
|
|||
|
||||
{/* Modal Footer */}
|
||||
<div className="flex justify-between items-center">
|
||||
<Tooltip title="Get help on our github">
|
||||
<Link href="https://github.com/BerriAI/litellm/issues">Need Help?</Link>
|
||||
<Tooltip title={t("modelAdd.reuseCredentials.needHelpTooltip")}>
|
||||
<Link href="https://github.com/BerriAI/litellm/issues">{t("modelAdd.reuseCredentials.needHelp")}</Link>
|
||||
</Tooltip>
|
||||
|
||||
<div>
|
||||
|
|
@ -72,9 +74,9 @@ const ReuseCredentialsModal: React.FC<ReuseCredentialsModalProps> = ({
|
|||
}}
|
||||
style={{ marginRight: 10 }}
|
||||
>
|
||||
Cancel
|
||||
{t("common.cancel")}
|
||||
</Button>
|
||||
<Button htmlType="submit">Reuse Credentials</Button>
|
||||
<Button htmlType="submit">{t("modelAdd.reuseCredentials.reuseCredentials")}</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Form>
|
||||
|
|
|
|||
|
|
@ -7569,5 +7569,52 @@
|
|||
"tagUpdatedSuccess": "Tag updated successfully",
|
||||
"errorUpdatingTag": "Error updating tag: {{error}}"
|
||||
}
|
||||
},
|
||||
"modelAdd": {
|
||||
"addCredentialModal": {
|
||||
"title": "Add New Credential",
|
||||
"credentialNameLabel": "Credential Name:",
|
||||
"credentialNameRequired": "Credential name is required",
|
||||
"credentialNamePlaceholder": "Enter a friendly name for these credentials",
|
||||
"providerLabel": "Provider:",
|
||||
"providerTooltip": "Helper to auto-populate provider specific fields",
|
||||
"needHelpTooltip": "Get help on our github",
|
||||
"needHelp": "Need Help?",
|
||||
"addCredential": "Add Credential"
|
||||
},
|
||||
"editCredentialModal": {
|
||||
"title": "Edit Credential",
|
||||
"credentialNameLabel": "Credential Name:",
|
||||
"credentialNameRequired": "Credential name is required",
|
||||
"credentialNamePlaceholder": "Enter a friendly name for these credentials",
|
||||
"providerLabel": "Provider:",
|
||||
"providerTooltip": "Helper to auto-populate provider specific fields",
|
||||
"needHelpTooltip": "Get help on our github",
|
||||
"needHelp": "Need Help?",
|
||||
"updateCredential": "Update Credential"
|
||||
},
|
||||
"credentials": {
|
||||
"addCredential": "Add Credential",
|
||||
"configuredCredentials": "Configured credentials for different AI providers. Add and manage your API credentials.",
|
||||
"credentialName": "Credential Name",
|
||||
"provider": "Provider",
|
||||
"noCredentials": "No credentials configured",
|
||||
"updatedSuccess": "Credential updated successfully",
|
||||
"addedSuccess": "Credential added successfully",
|
||||
"deletedSuccess": "Credential deleted successfully",
|
||||
"deleteFailed": "Failed to delete credential",
|
||||
"deleteTitle": "Delete Credential?",
|
||||
"deleteMessage": "Are you sure you want to delete this credential? This action cannot be undone and may break existing integrations.",
|
||||
"credentialInformation": "Credential Information"
|
||||
},
|
||||
"reuseCredentials": {
|
||||
"title": "Reuse Credentials",
|
||||
"credentialNameLabel": "Credential Name:",
|
||||
"credentialNameRequired": "Credential name is required",
|
||||
"credentialNamePlaceholder": "Enter a friendly name for these credentials",
|
||||
"needHelpTooltip": "Get help on our github",
|
||||
"needHelp": "Need Help?",
|
||||
"reuseCredentials": "Reuse Credentials"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7569,5 +7569,52 @@
|
|||
"tagUpdatedSuccess": "标签更新成功",
|
||||
"errorUpdatingTag": "更新标签失败:{{error}}"
|
||||
}
|
||||
},
|
||||
"modelAdd": {
|
||||
"addCredentialModal": {
|
||||
"title": "添加新凭据",
|
||||
"credentialNameLabel": "凭据名称:",
|
||||
"credentialNameRequired": "凭据名称不能为空",
|
||||
"credentialNamePlaceholder": "输入这组凭据的友好名称",
|
||||
"providerLabel": "提供商:",
|
||||
"providerTooltip": "帮助自动填充提供商特定字段",
|
||||
"needHelpTooltip": "在我们的 GitHub 上获取帮助",
|
||||
"needHelp": "需要帮助?",
|
||||
"addCredential": "添加凭据"
|
||||
},
|
||||
"editCredentialModal": {
|
||||
"title": "编辑凭据",
|
||||
"credentialNameLabel": "凭据名称:",
|
||||
"credentialNameRequired": "凭据名称不能为空",
|
||||
"credentialNamePlaceholder": "输入这组凭据的友好名称",
|
||||
"providerLabel": "提供商:",
|
||||
"providerTooltip": "帮助自动填充提供商特定字段",
|
||||
"needHelpTooltip": "在我们的 GitHub 上获取帮助",
|
||||
"needHelp": "需要帮助?",
|
||||
"updateCredential": "更新凭据"
|
||||
},
|
||||
"credentials": {
|
||||
"addCredential": "添加凭据",
|
||||
"configuredCredentials": "已为不同 AI 提供商配置的凭据。添加并管理您的 API 凭据。",
|
||||
"credentialName": "凭据名称",
|
||||
"provider": "提供商",
|
||||
"noCredentials": "暂无已配置的凭据",
|
||||
"updatedSuccess": "凭据更新成功",
|
||||
"addedSuccess": "凭据添加成功",
|
||||
"deletedSuccess": "凭据删除成功",
|
||||
"deleteFailed": "删除凭据失败",
|
||||
"deleteTitle": "删除凭据?",
|
||||
"deleteMessage": "确定要删除此凭据吗?此操作不可撤销,且可能破坏现有集成。",
|
||||
"credentialInformation": "凭据信息"
|
||||
},
|
||||
"reuseCredentials": {
|
||||
"title": "复用凭据",
|
||||
"credentialNameLabel": "凭据名称:",
|
||||
"credentialNameRequired": "凭据名称不能为空",
|
||||
"credentialNamePlaceholder": "输入这组凭据的友好名称",
|
||||
"needHelpTooltip": "在我们的 GitHub 上获取帮助",
|
||||
"needHelp": "需要帮助?",
|
||||
"reuseCredentials": "复用凭据"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue