mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-04 02:31:27 +00:00
feat(ui): i18n for legacy teams view
This commit is contained in:
parent
cb21813d80
commit
7bf4de9997
3 changed files with 417 additions and 143 deletions
|
|
@ -3,6 +3,7 @@ import AvailableTeamsPanel from "@/components/team/available_teams";
|
|||
import TeamInfoView from "@/components/team/TeamInfo";
|
||||
import TeamSSOSettings from "@/components/TeamSSOSettings";
|
||||
import { isProxyAdminRole } from "@/utils/roles";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { InfoCircleOutlined, PlusOutlined, TeamOutlined, ReloadOutlined } from "@ant-design/icons";
|
||||
import { Accordion, AccordionBody, AccordionHeader, TextInput } from "@tremor/react";
|
||||
import {
|
||||
|
|
@ -176,6 +177,7 @@ const Teams: React.FC<TeamProps> = ({
|
|||
premiumUser = false,
|
||||
}) => {
|
||||
console.log(`organizations: ${JSON.stringify(organizations)}`);
|
||||
const { t } = useTranslation();
|
||||
const { data: organizationsData } = useOrganizations();
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [fetchError, setFetchError] = useState<string | null>(null);
|
||||
|
|
@ -224,7 +226,7 @@ const Teams: React.FC<TeamProps> = ({
|
|||
setTeams(response.teams ?? []);
|
||||
setTotalTeams(response.total ?? 0);
|
||||
} catch (err: any) {
|
||||
setFetchError(err?.message || "Failed to fetch teams");
|
||||
setFetchError(err?.message || t("oldTeams.failedToFetchTeams"));
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
|
|
@ -407,9 +409,9 @@ const Teams: React.FC<TeamProps> = ({
|
|||
setIsTeamDeleting(true);
|
||||
await teamDeleteCall(accessToken, teamToDelete.team_id);
|
||||
await fetchTeamsV2();
|
||||
NotificationsManager.success("Team deleted successfully");
|
||||
NotificationsManager.success(t("oldTeams.teamDeletedSuccess"));
|
||||
} catch (error) {
|
||||
NotificationsManager.fromBackend("Error deleting the team: " + error);
|
||||
NotificationsManager.fromBackend(t("oldTeams.deleteTeamFailed", { error }));
|
||||
} finally {
|
||||
setIsTeamDeleting(false);
|
||||
setIsDeleteModalOpen(false);
|
||||
|
|
@ -458,7 +460,7 @@ const Teams: React.FC<TeamProps> = ({
|
|||
throw new Error(`Team alias ${newTeamAlias} already exists, please pick another alias`);
|
||||
}
|
||||
|
||||
NotificationsManager.info("Creating Team");
|
||||
NotificationsManager.info(t("oldTeams.creatingTeam"));
|
||||
|
||||
// Handle logging settings in metadata
|
||||
if (loggingSettings.length > 0) {
|
||||
|
|
@ -578,7 +580,7 @@ const Teams: React.FC<TeamProps> = ({
|
|||
}
|
||||
|
||||
await teamCreateCall(accessToken, formValues);
|
||||
NotificationsManager.success("Team created");
|
||||
NotificationsManager.success(t("oldTeams.teamCreatedSuccess"));
|
||||
await fetchTeamsV2({
|
||||
page: currentPage,
|
||||
size: pageSize,
|
||||
|
|
@ -592,7 +594,7 @@ const Teams: React.FC<TeamProps> = ({
|
|||
}
|
||||
} catch (error) {
|
||||
console.error("Error creating the team:", error);
|
||||
NotificationsManager.fromBackend("Error creating the team: " + error);
|
||||
NotificationsManager.fromBackend(t("oldTeams.createTeamFailed", { error }));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -680,7 +682,7 @@ const Teams: React.FC<TeamProps> = ({
|
|||
const teamColumns: ColumnsType<Team> = useMemo(
|
||||
() => [
|
||||
{
|
||||
title: "Team ID",
|
||||
title: t("oldTeams.columns.teamId"),
|
||||
dataIndex: "team_id",
|
||||
key: "team_id",
|
||||
width: 170,
|
||||
|
|
@ -700,7 +702,7 @@ const Teams: React.FC<TeamProps> = ({
|
|||
),
|
||||
},
|
||||
{
|
||||
title: "Team Alias",
|
||||
title: t("oldTeams.columns.teamAlias"),
|
||||
dataIndex: "team_alias",
|
||||
key: "team_alias",
|
||||
ellipsis: true,
|
||||
|
|
@ -716,7 +718,7 @@ const Teams: React.FC<TeamProps> = ({
|
|||
),
|
||||
},
|
||||
{
|
||||
title: "Organization",
|
||||
title: t("oldTeams.columns.organization"),
|
||||
key: "organization",
|
||||
width: 160,
|
||||
ellipsis: true,
|
||||
|
|
@ -732,7 +734,7 @@ const Teams: React.FC<TeamProps> = ({
|
|||
},
|
||||
},
|
||||
{
|
||||
title: "Resources",
|
||||
title: t("oldTeams.columns.resources"),
|
||||
key: "resources",
|
||||
width: 240,
|
||||
render: (_: unknown, record: Team) => {
|
||||
|
|
@ -741,7 +743,7 @@ const Teams: React.FC<TeamProps> = ({
|
|||
const keyCount = perTeamInfo?.[record.team_id]?.keys_count ?? 0;
|
||||
return (
|
||||
<Flex gap={12} align="center">
|
||||
<Tooltip title={`${memberCount} Members`}>
|
||||
<Tooltip title={t("oldTeams.resources.members", { count: memberCount })}>
|
||||
<Tag color="purple" style={{ fontSize: 14, padding: "2px 8px", margin: 0 }}>
|
||||
<Flex align="center" gap={6}>
|
||||
<UsersIcon size={14} />
|
||||
|
|
@ -749,7 +751,7 @@ const Teams: React.FC<TeamProps> = ({
|
|||
</Flex>
|
||||
</Tag>
|
||||
</Tooltip>
|
||||
<Tooltip title={`${modelCount} Models`}>
|
||||
<Tooltip title={t("oldTeams.resources.models", { count: modelCount })}>
|
||||
<Tag color="blue" style={{ fontSize: 14, padding: "2px 8px", margin: 0 }}>
|
||||
<Flex align="center" gap={6}>
|
||||
<LayersIcon size={14} />
|
||||
|
|
@ -757,7 +759,7 @@ const Teams: React.FC<TeamProps> = ({
|
|||
</Flex>
|
||||
</Tag>
|
||||
</Tooltip>
|
||||
<Tooltip title={`${keyCount} Keys`}>
|
||||
<Tooltip title={t("oldTeams.resources.keys", { count: keyCount })}>
|
||||
<Tag color="cyan" style={{ fontSize: 14, padding: "2px 8px", margin: 0 }}>
|
||||
<Flex align="center" gap={6}>
|
||||
<KeyIcon size={14} />
|
||||
|
|
@ -770,7 +772,7 @@ const Teams: React.FC<TeamProps> = ({
|
|||
},
|
||||
},
|
||||
{
|
||||
title: "Spend / Budget",
|
||||
title: t("oldTeams.columns.spendBudget"),
|
||||
key: "spend",
|
||||
width: 200,
|
||||
sorter: true,
|
||||
|
|
@ -781,7 +783,7 @@ const Teams: React.FC<TeamProps> = ({
|
|||
const budgetStr =
|
||||
budgetVal != null
|
||||
? `$${budgetVal.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`
|
||||
: "Unlimited";
|
||||
: t("oldTeams.unlimited");
|
||||
const percent = budgetVal != null && budgetVal > 0 ? Math.min((spendVal / budgetVal) * 100, 100) : null;
|
||||
return (
|
||||
<Flex vertical gap={2}>
|
||||
|
|
@ -806,7 +808,7 @@ const Teams: React.FC<TeamProps> = ({
|
|||
},
|
||||
},
|
||||
{
|
||||
title: "Created",
|
||||
title: t("oldTeams.columns.created"),
|
||||
dataIndex: "created_at",
|
||||
key: "created_at",
|
||||
width: 130,
|
||||
|
|
@ -821,7 +823,7 @@ const Teams: React.FC<TeamProps> = ({
|
|||
),
|
||||
},
|
||||
{
|
||||
title: "Actions",
|
||||
title: t("common.actions"),
|
||||
key: "actions",
|
||||
width: 120,
|
||||
align: "right" as const,
|
||||
|
|
@ -829,19 +831,19 @@ const Teams: React.FC<TeamProps> = ({
|
|||
<Space size={4}>
|
||||
<TableIconActionButton
|
||||
variant="Copy"
|
||||
tooltipText="Copy Team ID"
|
||||
tooltipText={t("oldTeams.actions.copyTeamId")}
|
||||
onClick={() => {
|
||||
navigator.clipboard
|
||||
.writeText(record.team_id)
|
||||
.then(() => message.success("Team ID copied"))
|
||||
.catch(() => message.error("Failed to copy"));
|
||||
.then(() => message.success(t("oldTeams.actions.teamIdCopied")))
|
||||
.catch(() => message.error(t("oldTeams.actions.failedToCopy")));
|
||||
}}
|
||||
/>
|
||||
{userRole === "Admin" && (
|
||||
<>
|
||||
<TableIconActionButton
|
||||
variant="Edit"
|
||||
tooltipText="Edit team"
|
||||
tooltipText={t("oldTeams.actions.editTeam")}
|
||||
dataTestId="edit-team-button"
|
||||
onClick={() => {
|
||||
setSelectedTeamId(record.team_id);
|
||||
|
|
@ -850,7 +852,7 @@ const Teams: React.FC<TeamProps> = ({
|
|||
/>
|
||||
<TableIconActionButton
|
||||
variant="Delete"
|
||||
tooltipText="Delete team"
|
||||
tooltipText={t("oldTeams.actions.deleteTeam")}
|
||||
dataTestId="delete-team-button"
|
||||
onClick={() => handleDelete(record)}
|
||||
/>
|
||||
|
|
@ -860,7 +862,7 @@ const Teams: React.FC<TeamProps> = ({
|
|||
),
|
||||
},
|
||||
],
|
||||
[userRole, perTeamInfo, organizationsData, organizations],
|
||||
[userRole, perTeamInfo, organizationsData, organizations, t],
|
||||
);
|
||||
|
||||
const displayTeams = useMemo(() => teams ?? [], [teams]);
|
||||
|
|
@ -878,13 +880,13 @@ const Teams: React.FC<TeamProps> = ({
|
|||
return (
|
||||
<Flex vertical align="center" gap={16} style={{ padding: "64px 0" }}>
|
||||
<Text type="danger" style={{ fontSize: 15 }}>
|
||||
Failed to load teams
|
||||
{t("oldTeams.failedToLoadTeams")}
|
||||
</Text>
|
||||
<Text type="secondary" style={{ fontSize: 13 }}>
|
||||
{fetchError}
|
||||
</Text>
|
||||
<Button icon={<ReloadOutlined />} onClick={handleRetry}>
|
||||
Retry
|
||||
{t("common.retry")}
|
||||
</Button>
|
||||
</Flex>
|
||||
);
|
||||
|
|
@ -902,11 +904,11 @@ const Teams: React.FC<TeamProps> = ({
|
|||
<div style={{ padding: "64px 0", textAlign: "center" }}>
|
||||
<TeamOutlined style={{ fontSize: 40, color: "#d9d9d9", marginBottom: 12 }} />
|
||||
<div>
|
||||
<Text style={{ fontSize: 15, color: "#595959" }}>No teams yet</Text>
|
||||
<Text style={{ fontSize: 15, color: "#595959" }}>{t("oldTeams.noTeamsYet")}</Text>
|
||||
</div>
|
||||
<div style={{ marginTop: 4 }}>
|
||||
<Text type="secondary" style={{ fontSize: 13 }}>
|
||||
Create your first team to organize members and manage access to models.
|
||||
{t("oldTeams.createFirstTeamHint")}
|
||||
</Text>
|
||||
</div>
|
||||
{canCreateOrManageTeams(userRole, userID, organizations) && (
|
||||
|
|
@ -917,7 +919,7 @@ const Teams: React.FC<TeamProps> = ({
|
|||
style={{ marginTop: 16 }}
|
||||
data-testid="create-team-button"
|
||||
>
|
||||
Create Team
|
||||
{t("oldTeams.createTeam")}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -932,7 +934,7 @@ const Teams: React.FC<TeamProps> = ({
|
|||
const tabItems = [
|
||||
{
|
||||
key: "your-teams",
|
||||
label: "Your Teams",
|
||||
label: t("oldTeams.tabs.yourTeams"),
|
||||
children: (
|
||||
<>
|
||||
<Card styles={{ body: { padding: 0 } }}>
|
||||
|
|
@ -941,7 +943,7 @@ const Teams: React.FC<TeamProps> = ({
|
|||
<Input
|
||||
prefix={<SearchIcon size={16} />}
|
||||
suffix={isSearching ? <AntDLoadingSpinner size="small" /> : null}
|
||||
placeholder="Search teams by name or ID..."
|
||||
placeholder={t("oldTeams.searchPlaceholder")}
|
||||
onChange={(e) => handleSearchChange(e.target.value)}
|
||||
allowClear
|
||||
style={{ maxWidth: 400 }}
|
||||
|
|
@ -963,7 +965,7 @@ const Teams: React.FC<TeamProps> = ({
|
|||
fetchTeamsV2({ page, size });
|
||||
}}
|
||||
size="small"
|
||||
showTotal={(total) => `${total} teams`}
|
||||
showTotal={(total) => t("oldTeams.totalTeams", { count: total })}
|
||||
showSizeChanger
|
||||
pageSizeOptions={["10", "20", "50"]}
|
||||
/>
|
||||
|
|
@ -974,23 +976,23 @@ const Teams: React.FC<TeamProps> = ({
|
|||
|
||||
<DeleteResourceModal
|
||||
isOpen={isDeleteModalOpen}
|
||||
title="Delete Team?"
|
||||
title={t("oldTeams.deleteModal.title")}
|
||||
alertMessage={(() => {
|
||||
const deleteKeyCount = teamToDelete?.keys_count ?? teamToDelete?.keys?.length ?? 0;
|
||||
return deleteKeyCount === 0
|
||||
? undefined
|
||||
: `Warning: This team has ${deleteKeyCount} keys associated with it. Deleting the team will also delete all associated keys. This action is irreversible.`;
|
||||
: t("oldTeams.deleteModal.alertMessage", { count: deleteKeyCount });
|
||||
})()}
|
||||
message="Are you sure you want to delete this team and all its keys? This action cannot be undone."
|
||||
resourceInformationTitle="Team Information"
|
||||
message={t("oldTeams.deleteModal.message")}
|
||||
resourceInformationTitle={t("oldTeams.deleteModal.resourceInformationTitle")}
|
||||
resourceInformation={[
|
||||
{ label: "Team ID", value: teamToDelete?.team_id, code: true },
|
||||
{ label: "Team Name", value: teamToDelete?.team_alias },
|
||||
{ label: t("oldTeams.deleteModal.labelTeamId"), value: teamToDelete?.team_id, code: true },
|
||||
{ label: t("oldTeams.deleteModal.labelTeamName"), value: teamToDelete?.team_alias },
|
||||
{
|
||||
label: "Keys",
|
||||
label: t("oldTeams.deleteModal.labelKeys"),
|
||||
value: teamToDelete?.keys_count ?? teamToDelete?.keys?.length ?? 0,
|
||||
},
|
||||
{ label: "Members", value: teamToDelete?.members_with_roles?.length },
|
||||
{ label: t("oldTeams.deleteModal.labelMembers"), value: teamToDelete?.members_with_roles?.length },
|
||||
]}
|
||||
requiredConfirmation={teamToDelete?.team_alias}
|
||||
onCancel={cancelDelete}
|
||||
|
|
@ -1002,14 +1004,14 @@ const Teams: React.FC<TeamProps> = ({
|
|||
},
|
||||
{
|
||||
key: "available-teams",
|
||||
label: "Available Teams",
|
||||
label: t("oldTeams.tabs.availableTeams"),
|
||||
children: <AvailableTeamsPanel accessToken={accessToken} userID={userID} />,
|
||||
},
|
||||
...(isProxyAdminRole(userRole || "")
|
||||
? [
|
||||
{
|
||||
key: "default-settings",
|
||||
label: "Default Team Settings",
|
||||
label: t("oldTeams.tabs.defaultTeamSettings"),
|
||||
children: <TeamSSOSettings accessToken={accessToken} userID={userID || ""} userRole={userRole || ""} />,
|
||||
},
|
||||
]
|
||||
|
|
@ -1052,9 +1054,9 @@ const Teams: React.FC<TeamProps> = ({
|
|||
<Space direction="vertical" size={0}>
|
||||
<Title level={2} style={{ margin: 0 }}>
|
||||
<TeamOutlined style={{ marginRight: 8 }} />
|
||||
Teams
|
||||
{t("oldTeams.pageTitle")}
|
||||
</Title>
|
||||
<Text type="secondary">Manage teams, members, and their access to models and budgets</Text>
|
||||
<Text type="secondary">{t("oldTeams.pageSubtitle")}</Text>
|
||||
</Space>
|
||||
{canCreateOrManageTeams(userRole, userID, organizations) && (
|
||||
<Button
|
||||
|
|
@ -1063,7 +1065,7 @@ const Teams: React.FC<TeamProps> = ({
|
|||
onClick={() => setIsTeamModalVisible(true)}
|
||||
data-testid="create-team-button"
|
||||
>
|
||||
Create Team
|
||||
{t("oldTeams.createTeam")}
|
||||
</Button>
|
||||
)}
|
||||
</Flex>
|
||||
|
|
@ -1074,7 +1076,7 @@ const Teams: React.FC<TeamProps> = ({
|
|||
|
||||
{canCreateOrManageTeams(userRole, userID, organizations) && (
|
||||
<Modal
|
||||
title="Create Team"
|
||||
title={t("oldTeams.createTeam")}
|
||||
open={isTeamModalVisible}
|
||||
width={1000}
|
||||
footer={null}
|
||||
|
|
@ -1084,12 +1086,12 @@ const Teams: React.FC<TeamProps> = ({
|
|||
<Form form={form} onFinish={handleCreate} labelCol={{ span: 8 }} wrapperCol={{ span: 16 }} labelAlign="left">
|
||||
<>
|
||||
<Form.Item
|
||||
label="Team Name"
|
||||
label={t("oldTeams.form.teamName")}
|
||||
name="team_alias"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: "Please input a team name",
|
||||
message: t("oldTeams.form.teamNameRequired"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
|
|
@ -1106,11 +1108,11 @@ const Teams: React.FC<TeamProps> = ({
|
|||
<Form.Item
|
||||
label={
|
||||
<span>
|
||||
Organization{" "}
|
||||
{t("oldTeams.form.organization")}{" "}
|
||||
<Tooltip
|
||||
title={
|
||||
<span>
|
||||
Organizations can have multiple teams. Learn more about{" "}
|
||||
{t("oldTeams.form.organizationTooltipPrefix")}{" "}
|
||||
<a
|
||||
href="https://docs.litellm.ai/docs/proxy/user_management_heirarchy"
|
||||
target="_blank"
|
||||
|
|
@ -1121,7 +1123,7 @@ const Teams: React.FC<TeamProps> = ({
|
|||
}}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
user management hierarchy
|
||||
{t("oldTeams.form.userManagementHierarchy")}
|
||||
</a>
|
||||
</span>
|
||||
}
|
||||
|
|
@ -1138,24 +1140,22 @@ const Teams: React.FC<TeamProps> = ({
|
|||
? [
|
||||
{
|
||||
required: true,
|
||||
message: "Please select an organization",
|
||||
message: t("oldTeams.form.organizationRequired"),
|
||||
},
|
||||
]
|
||||
: []
|
||||
}
|
||||
help={
|
||||
isSingleOrg
|
||||
? "You can only create teams within this organization"
|
||||
: isOrgAdmin
|
||||
? "required"
|
||||
: ""
|
||||
}
|
||||
help={isSingleOrg ? t("oldTeams.form.singleOrgHelp") : isOrgAdmin ? t("common.required") : ""}
|
||||
>
|
||||
<Select
|
||||
showSearch
|
||||
allowClear={!isOrgAdmin}
|
||||
disabled={isSingleOrg}
|
||||
placeholder={hasNoOrgs ? "No organizations available" : "Search or select an Organization"}
|
||||
placeholder={
|
||||
hasNoOrgs
|
||||
? t("oldTeams.form.noOrganizationsAvailable")
|
||||
: t("oldTeams.form.searchOrSelectOrganization")
|
||||
}
|
||||
onChange={(value) => {
|
||||
form.setFieldValue("organization_id", value);
|
||||
setCurrentOrgForCreateTeam(adminOrgs?.find((org) => org.organization_id === value) || null);
|
||||
|
|
@ -1179,10 +1179,7 @@ const Teams: React.FC<TeamProps> = ({
|
|||
{/* Show message when org admin needs to select organization */}
|
||||
{isOrgAdmin && !isSingleOrg && adminOrgs.length > 1 && (
|
||||
<div className="mb-8 p-4 bg-blue-50 border border-blue-200 rounded-md">
|
||||
<Text style={{ color: "#1e40af", fontSize: 14 }}>
|
||||
Please select an organization to create a team for. You can only create teams within
|
||||
organizations where you are an admin.
|
||||
</Text>
|
||||
<Text style={{ color: "#1e40af", fontSize: 14 }}>{t("oldTeams.form.selectOrgAdminHint")}</Text>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
|
|
@ -1191,8 +1188,8 @@ const Teams: React.FC<TeamProps> = ({
|
|||
<Form.Item
|
||||
label={
|
||||
<span>
|
||||
Models{" "}
|
||||
<Tooltip title="These are the models that your selected team has access to">
|
||||
{t("oldTeams.form.models")}{" "}
|
||||
<Tooltip title={t("oldTeams.form.modelsTooltip")}>
|
||||
<InfoCircleOutlined style={{ marginLeft: "4px" }} />
|
||||
</Tooltip>
|
||||
</span>
|
||||
|
|
@ -1200,7 +1197,7 @@ const Teams: React.FC<TeamProps> = ({
|
|||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: "Please select at least one model",
|
||||
message: t("oldTeams.form.modelsRequired"),
|
||||
},
|
||||
]}
|
||||
name="models"
|
||||
|
|
@ -1218,20 +1215,20 @@ const Teams: React.FC<TeamProps> = ({
|
|||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label="Max Budget (USD)" name="max_budget">
|
||||
<Form.Item label={t("oldTeams.form.maxBudget")} name="max_budget">
|
||||
<NumericalInput step={0.01} precision={2} width={200} />
|
||||
</Form.Item>
|
||||
<Form.Item className="mt-8" label="Reset Budget" name="budget_duration">
|
||||
<Form.Item className="mt-8" label={t("oldTeams.form.resetBudget")} name="budget_duration">
|
||||
<Select defaultValue={null} placeholder="n/a">
|
||||
<Select.Option value="24h">daily</Select.Option>
|
||||
<Select.Option value="7d">weekly</Select.Option>
|
||||
<Select.Option value="30d">monthly</Select.Option>
|
||||
<Select.Option value="24h">{t("oldTeams.form.budgetDaily")}</Select.Option>
|
||||
<Select.Option value="7d">{t("oldTeams.form.budgetWeekly")}</Select.Option>
|
||||
<Select.Option value="30d">{t("oldTeams.form.budgetMonthly")}</Select.Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item label="Tokens per minute Limit (TPM)" name="tpm_limit">
|
||||
<Form.Item label={t("oldTeams.form.tpmLimit")} name="tpm_limit">
|
||||
<NumericalInput step={1} width={400} />
|
||||
</Form.Item>
|
||||
<Form.Item label="Requests per minute Limit (RPM)" name="rpm_limit">
|
||||
<Form.Item label={t("oldTeams.form.rpmLimit")} name="rpm_limit">
|
||||
<NumericalInput step={1} width={400} />
|
||||
</Form.Item>
|
||||
|
||||
|
|
@ -1245,14 +1242,10 @@ const Teams: React.FC<TeamProps> = ({
|
|||
}}
|
||||
>
|
||||
<AccordionHeader>
|
||||
<b>Additional Settings</b>
|
||||
<b>{t("oldTeams.accordions.additionalSettings")}</b>
|
||||
</AccordionHeader>
|
||||
<AccordionBody>
|
||||
<Form.Item
|
||||
label="Team ID"
|
||||
name="team_id"
|
||||
help="ID of the team you want to create. If not provided, it will be generated automatically."
|
||||
>
|
||||
<Form.Item label={t("oldTeams.form.teamId")} name="team_id" help={t("oldTeams.form.teamIdHelp")}>
|
||||
<TextInput
|
||||
onChange={(e) => {
|
||||
e.target.value = e.target.value.trim();
|
||||
|
|
@ -1260,48 +1253,44 @@ const Teams: React.FC<TeamProps> = ({
|
|||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="Team Member Budget (USD)"
|
||||
label={t("oldTeams.form.teamMemberBudget")}
|
||||
name="team_member_budget"
|
||||
normalize={(value) => (value ? Number(value) : undefined)}
|
||||
tooltip="This is the individual budget for a user in the team."
|
||||
tooltip={t("oldTeams.form.teamMemberBudgetTooltip")}
|
||||
>
|
||||
<NumericalInput step={0.01} precision={2} width={200} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="Team Member Key Duration (eg: 1d, 1mo)"
|
||||
label={t("oldTeams.form.teamMemberKeyDuration")}
|
||||
name="team_member_key_duration"
|
||||
tooltip="Set a limit to the duration of a team member's key. Format: 30s (seconds), 30m (minutes), 30h (hours), 30d (days), 1mo (month)"
|
||||
tooltip={t("oldTeams.form.teamMemberKeyDurationTooltip")}
|
||||
>
|
||||
<TextInput placeholder="e.g., 30d" />
|
||||
<TextInput placeholder={t("oldTeams.form.teamMemberKeyDurationPlaceholder")} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="Team Member RPM Limit"
|
||||
label={t("oldTeams.form.teamMemberRpmLimit")}
|
||||
name="team_member_rpm_limit"
|
||||
tooltip="The RPM (Requests Per Minute) limit for individual team members"
|
||||
tooltip={t("oldTeams.form.teamMemberRpmLimitTooltip")}
|
||||
>
|
||||
<NumericalInput step={1} width={400} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="Team Member TPM Limit"
|
||||
label={t("oldTeams.form.teamMemberTpmLimit")}
|
||||
name="team_member_tpm_limit"
|
||||
tooltip="The TPM (Tokens Per Minute) limit for individual team members"
|
||||
tooltip={t("oldTeams.form.teamMemberTpmLimitTooltip")}
|
||||
>
|
||||
<NumericalInput step={1} width={400} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="Metadata"
|
||||
name="metadata"
|
||||
help="Additional team metadata. Enter metadata as JSON object."
|
||||
>
|
||||
<Form.Item label={t("oldTeams.form.metadata")} name="metadata" help={t("oldTeams.form.metadataHelp")}>
|
||||
<Input.TextArea rows={4} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="Secret Manager Settings"
|
||||
label={t("oldTeams.form.secretManagerSettings")}
|
||||
name="secret_manager_settings"
|
||||
help={
|
||||
premiumUser
|
||||
? "Enter secret manager configuration as a JSON object."
|
||||
: "Premium feature - Upgrade to manage secret manager settings."
|
||||
? t("oldTeams.form.secretManagerSettingsHelp")
|
||||
: t("oldTeams.form.secretManagerSettingsPremiumHelp")
|
||||
}
|
||||
rules={[
|
||||
{
|
||||
|
|
@ -1313,7 +1302,7 @@ const Teams: React.FC<TeamProps> = ({
|
|||
JSON.parse(value);
|
||||
return Promise.resolve();
|
||||
} catch (error) {
|
||||
return Promise.reject(new Error("Please enter valid JSON"));
|
||||
return Promise.reject(new Error(t("oldTeams.form.invalidJson")));
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
@ -1328,8 +1317,8 @@ const Teams: React.FC<TeamProps> = ({
|
|||
<Form.Item
|
||||
label={
|
||||
<span>
|
||||
Guardrails{" "}
|
||||
<Tooltip title="Setup your first guardrail">
|
||||
{t("oldTeams.form.guardrails")}{" "}
|
||||
<Tooltip title={t("oldTeams.form.guardrailsTooltip")}>
|
||||
<a
|
||||
href="https://docs.litellm.ai/docs/proxy/guardrails/quick_start"
|
||||
target="_blank"
|
||||
|
|
@ -1343,12 +1332,12 @@ const Teams: React.FC<TeamProps> = ({
|
|||
}
|
||||
name="guardrails"
|
||||
className="mt-8"
|
||||
help="Select existing guardrails or enter new ones"
|
||||
help={t("oldTeams.form.guardrailsHelp")}
|
||||
>
|
||||
<Select
|
||||
mode="tags"
|
||||
style={{ width: "100%" }}
|
||||
placeholder="Select or enter guardrails"
|
||||
placeholder={t("oldTeams.form.guardrailsPlaceholder")}
|
||||
options={guardrailsList.map((name) => ({
|
||||
value: name,
|
||||
label: name,
|
||||
|
|
@ -1358,8 +1347,8 @@ const Teams: React.FC<TeamProps> = ({
|
|||
<Form.Item
|
||||
label={
|
||||
<span>
|
||||
Disable Global Guardrails{" "}
|
||||
<Tooltip title="When enabled, this team will bypass any guardrails configured to run on every request (global guardrails)">
|
||||
{t("oldTeams.form.disableGlobalGuardrails")}{" "}
|
||||
<Tooltip title={t("oldTeams.form.disableGlobalGuardrailsTooltip")}>
|
||||
<InfoCircleOutlined style={{ marginLeft: "4px" }} />
|
||||
</Tooltip>
|
||||
</span>
|
||||
|
|
@ -1367,23 +1356,23 @@ const Teams: React.FC<TeamProps> = ({
|
|||
name="disable_global_guardrails"
|
||||
className="mt-4"
|
||||
valuePropName="checked"
|
||||
help="Bypass global guardrails for this team"
|
||||
help={t("oldTeams.form.disableGlobalGuardrailsHelp")}
|
||||
>
|
||||
<Switch
|
||||
disabled={!premiumUser}
|
||||
checkedChildren={
|
||||
premiumUser ? "Yes" : "Premium feature - Upgrade to disable global guardrails by team"
|
||||
premiumUser ? t("common.yes") : t("oldTeams.form.disableGlobalGuardrailsPremium")
|
||||
}
|
||||
unCheckedChildren={
|
||||
premiumUser ? "No" : "Premium feature - Upgrade to disable global guardrails by team"
|
||||
premiumUser ? t("common.no") : t("oldTeams.form.disableGlobalGuardrailsPremium")
|
||||
}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={
|
||||
<span>
|
||||
Policies{" "}
|
||||
<Tooltip title="Apply policies to this team to control guardrails and other settings">
|
||||
{t("oldTeams.form.policies")}{" "}
|
||||
<Tooltip title={t("oldTeams.form.policiesTooltip")}>
|
||||
<a
|
||||
href="https://docs.litellm.ai/docs/proxy/guardrails/guardrail_policies"
|
||||
target="_blank"
|
||||
|
|
@ -1397,12 +1386,12 @@ const Teams: React.FC<TeamProps> = ({
|
|||
}
|
||||
name="policies"
|
||||
className="mt-8"
|
||||
help="Select existing policies or enter new ones"
|
||||
help={t("oldTeams.form.policiesHelp")}
|
||||
>
|
||||
<Select
|
||||
mode="tags"
|
||||
style={{ width: "100%" }}
|
||||
placeholder="Select or enter policies"
|
||||
placeholder={t("oldTeams.form.policiesPlaceholder")}
|
||||
options={policiesList.map((name) => ({
|
||||
value: name,
|
||||
label: name,
|
||||
|
|
@ -1412,45 +1401,49 @@ const Teams: React.FC<TeamProps> = ({
|
|||
<Form.Item
|
||||
label={
|
||||
<span>
|
||||
Access Groups{" "}
|
||||
<Tooltip title="Assign access groups to this team. Access groups control which models, MCP servers, and agents this team can use">
|
||||
{t("oldTeams.form.accessGroups")}{" "}
|
||||
<Tooltip title={t("oldTeams.form.accessGroupsTooltip")}>
|
||||
<InfoCircleOutlined style={{ marginLeft: "4px" }} />
|
||||
</Tooltip>
|
||||
</span>
|
||||
}
|
||||
name="access_group_ids"
|
||||
className="mt-8"
|
||||
help="Select access groups to assign to this team"
|
||||
help={t("oldTeams.form.accessGroupsHelp")}
|
||||
>
|
||||
<AccessGroupSelector placeholder="Select access groups (optional)" />
|
||||
<AccessGroupSelector placeholder={t("oldTeams.form.accessGroupsPlaceholder")} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={
|
||||
<span>
|
||||
Allowed Vector Stores{" "}
|
||||
<Tooltip title="Select which vector stores this team can access by default. Leave empty for access to all vector stores">
|
||||
{t("oldTeams.form.allowedVectorStores")}{" "}
|
||||
<Tooltip title={t("oldTeams.form.allowedVectorStoresTooltip")}>
|
||||
<InfoCircleOutlined style={{ marginLeft: "4px" }} />
|
||||
</Tooltip>
|
||||
</span>
|
||||
}
|
||||
name="allowed_vector_store_ids"
|
||||
className="mt-8"
|
||||
help="Select vector stores this team can access. Leave empty for access to all vector stores"
|
||||
help={t("oldTeams.form.allowedVectorStoresHelp")}
|
||||
>
|
||||
<VectorStoreSelector
|
||||
onChange={(values: string[]) => form.setFieldValue("allowed_vector_store_ids", values)}
|
||||
value={form.getFieldValue("allowed_vector_store_ids")}
|
||||
accessToken={accessToken || ""}
|
||||
placeholder="Select vector stores (optional)"
|
||||
placeholder={t("oldTeams.form.allowedVectorStoresPlaceholder")}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item label="Allowed Pass Through Routes" name="allowed_passthrough_routes" className="mt-8">
|
||||
<Form.Item
|
||||
label={t("oldTeams.form.allowedPassThroughRoutes")}
|
||||
name="allowed_passthrough_routes"
|
||||
className="mt-8"
|
||||
>
|
||||
<Tooltip
|
||||
title={
|
||||
!premiumUser
|
||||
? "Premium feature - Upgrade to set allowed pass through routes"
|
||||
? t("oldTeams.form.allowedPassThroughRoutesPremiumTooltip")
|
||||
: !isProxyAdminRole(userRole || "")
|
||||
? "Only proxy admins can set allowed pass through routes"
|
||||
? t("oldTeams.form.allowedPassThroughRoutesAdminTooltip")
|
||||
: ""
|
||||
}
|
||||
placement="top"
|
||||
|
|
@ -1459,7 +1452,7 @@ const Teams: React.FC<TeamProps> = ({
|
|||
onChange={(values: string[]) => form.setFieldValue("allowed_passthrough_routes", values)}
|
||||
value={form.getFieldValue("allowed_passthrough_routes")}
|
||||
accessToken={accessToken || ""}
|
||||
placeholder="Select pass through routes (optional)"
|
||||
placeholder={t("oldTeams.form.allowedPassThroughRoutesPlaceholder")}
|
||||
disabled={!premiumUser || !isProxyAdminRole(userRole || "")}
|
||||
/>
|
||||
</Tooltip>
|
||||
|
|
@ -1469,27 +1462,27 @@ const Teams: React.FC<TeamProps> = ({
|
|||
|
||||
<Accordion className="mt-8 mb-8">
|
||||
<AccordionHeader>
|
||||
<b>MCP Settings</b>
|
||||
<b>{t("oldTeams.accordions.mcpSettings")}</b>
|
||||
</AccordionHeader>
|
||||
<AccordionBody>
|
||||
<Form.Item
|
||||
label={
|
||||
<span>
|
||||
Allowed MCP Servers{" "}
|
||||
<Tooltip title="Select which MCP servers or access groups this team can access">
|
||||
{t("oldTeams.form.allowedMcpServers")}{" "}
|
||||
<Tooltip title={t("oldTeams.form.allowedMcpServersTooltip")}>
|
||||
<InfoCircleOutlined style={{ marginLeft: "4px" }} />
|
||||
</Tooltip>
|
||||
</span>
|
||||
}
|
||||
name="allowed_mcp_servers_and_groups"
|
||||
className="mt-4"
|
||||
help="Select MCP servers or access groups this team can access"
|
||||
help={t("oldTeams.form.allowedMcpServersHelp")}
|
||||
>
|
||||
<MCPServerSelector
|
||||
onChange={(val: any) => form.setFieldValue("allowed_mcp_servers_and_groups", val)}
|
||||
value={form.getFieldValue("allowed_mcp_servers_and_groups")}
|
||||
accessToken={accessToken || ""}
|
||||
placeholder="Select MCP servers or access groups (optional)"
|
||||
placeholder={t("oldTeams.form.allowedMcpServersPlaceholder")}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
|
|
@ -1521,27 +1514,27 @@ const Teams: React.FC<TeamProps> = ({
|
|||
|
||||
<Accordion className="mt-8 mb-8">
|
||||
<AccordionHeader>
|
||||
<b>Agent Settings</b>
|
||||
<b>{t("oldTeams.accordions.agentSettings")}</b>
|
||||
</AccordionHeader>
|
||||
<AccordionBody>
|
||||
<Form.Item
|
||||
label={
|
||||
<span>
|
||||
Allowed Agents{" "}
|
||||
<Tooltip title="Select which agents or access groups this team can access">
|
||||
{t("oldTeams.form.allowedAgents")}{" "}
|
||||
<Tooltip title={t("oldTeams.form.allowedAgentsTooltip")}>
|
||||
<InfoCircleOutlined style={{ marginLeft: "4px" }} />
|
||||
</Tooltip>
|
||||
</span>
|
||||
}
|
||||
name="allowed_agents_and_groups"
|
||||
className="mt-4"
|
||||
help="Select agents or access groups this team can access"
|
||||
help={t("oldTeams.form.allowedAgentsHelp")}
|
||||
>
|
||||
<AgentSelector
|
||||
onChange={(val: any) => form.setFieldValue("allowed_agents_and_groups", val)}
|
||||
value={form.getFieldValue("allowed_agents_and_groups")}
|
||||
accessToken={accessToken || ""}
|
||||
placeholder="Select agents or access groups (optional)"
|
||||
placeholder={t("oldTeams.form.allowedAgentsPlaceholder")}
|
||||
/>
|
||||
</Form.Item>
|
||||
</AccordionBody>
|
||||
|
|
@ -1549,27 +1542,27 @@ const Teams: React.FC<TeamProps> = ({
|
|||
|
||||
<Accordion className="mt-8 mb-8">
|
||||
<AccordionHeader>
|
||||
<b>Search Tool Settings</b>
|
||||
<b>{t("oldTeams.accordions.searchToolSettings")}</b>
|
||||
</AccordionHeader>
|
||||
<AccordionBody>
|
||||
<Form.Item
|
||||
label={
|
||||
<span>
|
||||
Allowed Search Tools{" "}
|
||||
<Tooltip title="Select which search tools this team can access. Leave empty to allow all search tools.">
|
||||
{t("oldTeams.form.allowedSearchTools")}{" "}
|
||||
<Tooltip title={t("oldTeams.form.allowedSearchToolsTooltip")}>
|
||||
<InfoCircleOutlined style={{ marginLeft: "4px" }} />
|
||||
</Tooltip>
|
||||
</span>
|
||||
}
|
||||
name="object_permission_search_tools"
|
||||
className="mt-4"
|
||||
help="Restrict which configured search tools keys on this team may call."
|
||||
help={t("oldTeams.form.allowedSearchToolsHelp")}
|
||||
>
|
||||
<SearchToolSelector
|
||||
onChange={(vals: string[]) => form.setFieldValue("object_permission_search_tools", vals)}
|
||||
value={form.getFieldValue("object_permission_search_tools")}
|
||||
accessToken={accessToken || ""}
|
||||
placeholder="Select search tools (optional, empty = all allowed)"
|
||||
placeholder={t("oldTeams.form.allowedSearchToolsPlaceholder")}
|
||||
/>
|
||||
</Form.Item>
|
||||
</AccordionBody>
|
||||
|
|
@ -1577,7 +1570,7 @@ const Teams: React.FC<TeamProps> = ({
|
|||
|
||||
<Accordion className="mt-8 mb-8">
|
||||
<AccordionHeader>
|
||||
<b>Logging Settings</b>
|
||||
<b>{t("oldTeams.accordions.loggingSettings")}</b>
|
||||
</AccordionHeader>
|
||||
<AccordionBody>
|
||||
<div className="mt-4">
|
||||
|
|
@ -1592,7 +1585,7 @@ const Teams: React.FC<TeamProps> = ({
|
|||
|
||||
<Accordion key={`router-settings-accordion-${routerSettingsKey}`} className="mt-8 mb-8">
|
||||
<AccordionHeader>
|
||||
<b>Router Settings</b>
|
||||
<b>{t("oldTeams.accordions.routerSettings")}</b>
|
||||
</AccordionHeader>
|
||||
<AccordionBody>
|
||||
<div className="mt-4 w-full">
|
||||
|
|
@ -1611,13 +1604,12 @@ const Teams: React.FC<TeamProps> = ({
|
|||
|
||||
<Accordion className="mt-8 mb-8">
|
||||
<AccordionHeader>
|
||||
<b>Model Aliases</b>
|
||||
<b>{t("oldTeams.accordions.modelAliases")}</b>
|
||||
</AccordionHeader>
|
||||
<AccordionBody>
|
||||
<div className="mt-4">
|
||||
<Text type="secondary" style={{ fontSize: 14, marginBottom: 16, display: "block" }}>
|
||||
Create custom aliases for models that can be used by team members in API calls. This allows you to
|
||||
create shortcuts for specific models.
|
||||
{t("oldTeams.form.modelAliasesDescription")}
|
||||
</Text>
|
||||
<ModelAliasManager
|
||||
accessToken={accessToken || ""}
|
||||
|
|
@ -1631,7 +1623,7 @@ const Teams: React.FC<TeamProps> = ({
|
|||
</>
|
||||
<div style={{ textAlign: "right", marginTop: "10px" }}>
|
||||
<Button htmlType="submit" data-testid="create-team-submit">
|
||||
Create Team
|
||||
{t("oldTeams.createTeam")}
|
||||
</Button>
|
||||
</div>
|
||||
</Form>
|
||||
|
|
|
|||
|
|
@ -571,6 +571,147 @@
|
|||
"helpIconAriaLabel": "Help information",
|
||||
"docsMenuLabel": "Docs"
|
||||
},
|
||||
"oldTeams": {
|
||||
"failedToFetchTeams": "Failed to fetch teams",
|
||||
"failedToLoadTeams": "Failed to load teams",
|
||||
"teamDeletedSuccess": "Team deleted successfully",
|
||||
"creatingTeam": "Creating Team",
|
||||
"teamCreatedSuccess": "Team created",
|
||||
"noTeamsYet": "No teams yet",
|
||||
"createFirstTeamHint": "Create your first team to organize members and manage access to models.",
|
||||
"createTeam": "Create Team",
|
||||
"searchPlaceholder": "Search teams by name or ID...",
|
||||
"totalTeams_one": "{{count}} team",
|
||||
"totalTeams_other": "{{count}} teams",
|
||||
"unlimited": "Unlimited",
|
||||
"pageTitle": "Teams",
|
||||
"pageSubtitle": "Manage teams, members, and their access to models and budgets",
|
||||
"columns": {
|
||||
"teamId": "Team ID",
|
||||
"teamAlias": "Team Alias",
|
||||
"organization": "Organization",
|
||||
"resources": "Resources",
|
||||
"spendBudget": "Spend / Budget",
|
||||
"created": "Created"
|
||||
},
|
||||
"resources": {
|
||||
"members_one": "{{count}} Member",
|
||||
"members_other": "{{count}} Members",
|
||||
"models_one": "{{count}} Model",
|
||||
"models_other": "{{count}} Models",
|
||||
"keys_one": "{{count}} Key",
|
||||
"keys_other": "{{count}} Keys"
|
||||
},
|
||||
"actions": {
|
||||
"copyTeamId": "Copy Team ID",
|
||||
"teamIdCopied": "Team ID copied",
|
||||
"failedToCopy": "Failed to copy",
|
||||
"editTeam": "Edit team",
|
||||
"deleteTeam": "Delete team"
|
||||
},
|
||||
"tabs": {
|
||||
"yourTeams": "Your Teams",
|
||||
"availableTeams": "Available Teams",
|
||||
"defaultTeamSettings": "Default Team Settings"
|
||||
},
|
||||
"deleteModal": {
|
||||
"title": "Delete Team?",
|
||||
"alertMessage": "Warning: This team has {{count}} keys associated with it. Deleting the team will also delete all associated keys. This action is irreversible.",
|
||||
"message": "Are you sure you want to delete this team and all its keys? This action cannot be undone.",
|
||||
"resourceInformationTitle": "Team Information",
|
||||
"labelTeamId": "Team ID",
|
||||
"labelTeamName": "Team Name",
|
||||
"labelKeys": "Keys",
|
||||
"labelMembers": "Members"
|
||||
},
|
||||
"form": {
|
||||
"teamName": "Team Name",
|
||||
"teamNameRequired": "Please input a team name",
|
||||
"organization": "Organization",
|
||||
"organizationTooltipPrefix": "Organizations can have multiple teams. Learn more about",
|
||||
"userManagementHierarchy": "user management hierarchy",
|
||||
"organizationRequired": "Please select an organization",
|
||||
"singleOrgHelp": "You can only create teams within this organization",
|
||||
"noOrganizationsAvailable": "No organizations available",
|
||||
"searchOrSelectOrganization": "Search or select an Organization",
|
||||
"selectOrgAdminHint": "Please select an organization to create a team for. You can only create teams within organizations where you are an admin.",
|
||||
"models": "Models",
|
||||
"modelsTooltip": "These are the models that your selected team has access to",
|
||||
"modelsRequired": "Please select at least one model",
|
||||
"maxBudget": "Max Budget (USD)",
|
||||
"resetBudget": "Reset Budget",
|
||||
"budgetDaily": "daily",
|
||||
"budgetWeekly": "weekly",
|
||||
"budgetMonthly": "monthly",
|
||||
"tpmLimit": "Tokens per minute Limit (TPM)",
|
||||
"rpmLimit": "Requests per minute Limit (RPM)",
|
||||
"teamId": "Team ID",
|
||||
"teamIdHelp": "ID of the team you want to create. If not provided, it will be generated automatically.",
|
||||
"teamMemberBudget": "Team Member Budget (USD)",
|
||||
"teamMemberBudgetTooltip": "This is the individual budget for a user in the team.",
|
||||
"teamMemberKeyDuration": "Team Member Key Duration (eg: 1d, 1mo)",
|
||||
"teamMemberKeyDurationTooltip": "Set a limit to the duration of a team member's key. Format: 30s (seconds), 30m (minutes), 30h (hours), 30d (days), 1mo (month)",
|
||||
"teamMemberKeyDurationPlaceholder": "e.g., 30d",
|
||||
"teamMemberRpmLimit": "Team Member RPM Limit",
|
||||
"teamMemberRpmLimitTooltip": "The RPM (Requests Per Minute) limit for individual team members",
|
||||
"teamMemberTpmLimit": "Team Member TPM Limit",
|
||||
"teamMemberTpmLimitTooltip": "The TPM (Tokens Per Minute) limit for individual team members",
|
||||
"metadata": "Metadata",
|
||||
"metadataHelp": "Additional team metadata. Enter metadata as JSON object.",
|
||||
"secretManagerSettings": "Secret Manager Settings",
|
||||
"secretManagerSettingsHelp": "Enter secret manager configuration as a JSON object.",
|
||||
"secretManagerSettingsPremiumHelp": "Premium feature - Upgrade to manage secret manager settings.",
|
||||
"invalidJson": "Please enter valid JSON",
|
||||
"guardrails": "Guardrails",
|
||||
"guardrailsTooltip": "Setup your first guardrail",
|
||||
"guardrailsHelp": "Select existing guardrails or enter new ones",
|
||||
"guardrailsPlaceholder": "Select or enter guardrails",
|
||||
"disableGlobalGuardrails": "Disable Global Guardrails",
|
||||
"disableGlobalGuardrailsTooltip": "When enabled, this team will bypass any guardrails configured to run on every request (global guardrails)",
|
||||
"disableGlobalGuardrailsHelp": "Bypass global guardrails for this team",
|
||||
"disableGlobalGuardrailsPremium": "Premium feature - Upgrade to disable global guardrails by team",
|
||||
"policies": "Policies",
|
||||
"policiesTooltip": "Apply policies to this team to control guardrails and other settings",
|
||||
"policiesHelp": "Select existing policies or enter new ones",
|
||||
"policiesPlaceholder": "Select or enter policies",
|
||||
"accessGroups": "Access Groups",
|
||||
"accessGroupsTooltip": "Assign access groups to this team. Access groups control which models, MCP servers, and agents this team can use",
|
||||
"accessGroupsHelp": "Select access groups to assign to this team",
|
||||
"accessGroupsPlaceholder": "Select access groups (optional)",
|
||||
"allowedVectorStores": "Allowed Vector Stores",
|
||||
"allowedVectorStoresTooltip": "Select which vector stores this team can access by default. Leave empty for access to all vector stores",
|
||||
"allowedVectorStoresHelp": "Select vector stores this team can access. Leave empty for access to all vector stores",
|
||||
"allowedVectorStoresPlaceholder": "Select vector stores (optional)",
|
||||
"allowedPassThroughRoutes": "Allowed Pass Through Routes",
|
||||
"allowedPassThroughRoutesPremiumTooltip": "Premium feature - Upgrade to set allowed pass through routes",
|
||||
"allowedPassThroughRoutesAdminTooltip": "Only proxy admins can set allowed pass through routes",
|
||||
"allowedPassThroughRoutesPlaceholder": "Select pass through routes (optional)",
|
||||
"allowedMcpServers": "Allowed MCP Servers",
|
||||
"allowedMcpServersTooltip": "Select which MCP servers or access groups this team can access",
|
||||
"allowedMcpServersHelp": "Select MCP servers or access groups this team can access",
|
||||
"allowedMcpServersPlaceholder": "Select MCP servers or access groups (optional)",
|
||||
"allowedAgents": "Allowed Agents",
|
||||
"allowedAgentsTooltip": "Select which agents or access groups this team can access",
|
||||
"allowedAgentsHelp": "Select agents or access groups this team can access",
|
||||
"allowedAgentsPlaceholder": "Select agents or access groups (optional)",
|
||||
"allowedSearchTools": "Allowed Search Tools",
|
||||
"allowedSearchToolsTooltip": "Select which search tools this team can access. Leave empty to allow all search tools.",
|
||||
"allowedSearchToolsHelp": "Restrict which configured search tools keys on this team may call.",
|
||||
"allowedSearchToolsPlaceholder": "Select search tools (optional, empty = all allowed)",
|
||||
"modelAliasesDescription": "Create custom aliases for models that can be used by team members in API calls. This allows you to create shortcuts for specific models."
|
||||
},
|
||||
"accordions": {
|
||||
"additionalSettings": "Additional Settings",
|
||||
"mcpSettings": "MCP Settings",
|
||||
"agentSettings": "Agent Settings",
|
||||
"searchToolSettings": "Search Tool Settings",
|
||||
"loggingSettings": "Logging Settings",
|
||||
"routerSettings": "Router Settings",
|
||||
"modelAliases": "Model Aliases"
|
||||
},
|
||||
"deleteTeamFailed": "Error deleting the team: {{error}}",
|
||||
"createTeamFailed": "Error creating the team: {{error}}"
|
||||
},
|
||||
"common": {
|
||||
"save": "Save",
|
||||
"cancel": "Cancel",
|
||||
|
|
|
|||
|
|
@ -571,6 +571,147 @@
|
|||
"helpIconAriaLabel": "帮助信息",
|
||||
"docsMenuLabel": "文档"
|
||||
},
|
||||
"oldTeams": {
|
||||
"failedToFetchTeams": "获取团队失败",
|
||||
"failedToLoadTeams": "加载团队失败",
|
||||
"teamDeletedSuccess": "团队已成功删除",
|
||||
"creatingTeam": "正在创建团队",
|
||||
"teamCreatedSuccess": "团队已创建",
|
||||
"noTeamsYet": "暂无团队",
|
||||
"createFirstTeamHint": "创建您的第一个团队,以便组织成员并管理对模型的访问权限。",
|
||||
"createTeam": "创建团队",
|
||||
"searchPlaceholder": "按名称或 ID 搜索团队...",
|
||||
"totalTeams_one": "{{count}} 个团队",
|
||||
"totalTeams_other": "{{count}} 个团队",
|
||||
"unlimited": "无限制",
|
||||
"pageTitle": "团队",
|
||||
"pageSubtitle": "管理团队、成员及其对模型和预算的访问权限",
|
||||
"columns": {
|
||||
"teamId": "团队 ID",
|
||||
"teamAlias": "团队别名",
|
||||
"organization": "组织",
|
||||
"resources": "资源",
|
||||
"spendBudget": "花费 / 预算",
|
||||
"created": "创建时间"
|
||||
},
|
||||
"resources": {
|
||||
"members_one": "{{count}} 位成员",
|
||||
"members_other": "{{count}} 位成员",
|
||||
"models_one": "{{count}} 个模型",
|
||||
"models_other": "{{count}} 个模型",
|
||||
"keys_one": "{{count}} 个密钥",
|
||||
"keys_other": "{{count}} 个密钥"
|
||||
},
|
||||
"actions": {
|
||||
"copyTeamId": "复制团队 ID",
|
||||
"teamIdCopied": "团队 ID 已复制",
|
||||
"failedToCopy": "复制失败",
|
||||
"editTeam": "编辑团队",
|
||||
"deleteTeam": "删除团队"
|
||||
},
|
||||
"tabs": {
|
||||
"yourTeams": "我的团队",
|
||||
"availableTeams": "可用团队",
|
||||
"defaultTeamSettings": "默认团队设置"
|
||||
},
|
||||
"deleteModal": {
|
||||
"title": "删除团队?",
|
||||
"alertMessage": "警告:此团队有 {{count}} 个关联密钥。删除团队将同时删除所有关联密钥,此操作不可逆。",
|
||||
"message": "确定要删除此团队及其所有密钥吗?此操作无法撤销。",
|
||||
"resourceInformationTitle": "团队信息",
|
||||
"labelTeamId": "团队 ID",
|
||||
"labelTeamName": "团队名称",
|
||||
"labelKeys": "密钥",
|
||||
"labelMembers": "成员"
|
||||
},
|
||||
"form": {
|
||||
"teamName": "团队名称",
|
||||
"teamNameRequired": "请输入团队名称",
|
||||
"organization": "组织",
|
||||
"organizationTooltipPrefix": "组织可以有多个团队。了解更多关于",
|
||||
"userManagementHierarchy": "用户管理层级",
|
||||
"organizationRequired": "请选择一个组织",
|
||||
"singleOrgHelp": "您只能在此组织内创建团队",
|
||||
"noOrganizationsAvailable": "暂无可用组织",
|
||||
"searchOrSelectOrganization": "搜索或选择组织",
|
||||
"selectOrgAdminHint": "请选择要在其中创建团队的组织。您只能在您担任管理员的组织中创建团队。",
|
||||
"models": "模型",
|
||||
"modelsTooltip": "这些是您所选团队可以访问的模型",
|
||||
"modelsRequired": "请至少选择一个模型",
|
||||
"maxBudget": "最大预算(USD)",
|
||||
"resetBudget": "重置预算",
|
||||
"budgetDaily": "每天",
|
||||
"budgetWeekly": "每周",
|
||||
"budgetMonthly": "每月",
|
||||
"tpmLimit": "每分钟 Token 限制(TPM)",
|
||||
"rpmLimit": "每分钟请求限制(RPM)",
|
||||
"teamId": "团队 ID",
|
||||
"teamIdHelp": "您要创建的团队 ID。若不填写,将自动生成。",
|
||||
"teamMemberBudget": "团队成员预算(USD)",
|
||||
"teamMemberBudgetTooltip": "这是团队中单个用户的独立预算。",
|
||||
"teamMemberKeyDuration": "团队成员密钥有效期(例如:1d、1mo)",
|
||||
"teamMemberKeyDurationTooltip": "设置团队成员密钥的有效期限制。格式:30s(秒)、30m(分钟)、30h(小时)、30d(天)、1mo(月)",
|
||||
"teamMemberKeyDurationPlaceholder": "例如:30d",
|
||||
"teamMemberRpmLimit": "团队成员 RPM 限制",
|
||||
"teamMemberRpmLimitTooltip": "单个团队成员的每分钟请求数(RPM)限制",
|
||||
"teamMemberTpmLimit": "团队成员 TPM 限制",
|
||||
"teamMemberTpmLimitTooltip": "单个团队成员的每分钟 Token 数(TPM)限制",
|
||||
"metadata": "元数据",
|
||||
"metadataHelp": "附加团队元数据。以 JSON 对象格式输入。",
|
||||
"secretManagerSettings": "密钥管理器设置",
|
||||
"secretManagerSettingsHelp": "以 JSON 对象格式输入密钥管理器配置。",
|
||||
"secretManagerSettingsPremiumHelp": "高级功能 - 升级以管理密钥管理器设置。",
|
||||
"invalidJson": "请输入有效的 JSON",
|
||||
"guardrails": "护栏",
|
||||
"guardrailsTooltip": "设置您的第一个护栏",
|
||||
"guardrailsHelp": "选择现有护栏或输入新的护栏",
|
||||
"guardrailsPlaceholder": "选择或输入护栏",
|
||||
"disableGlobalGuardrails": "禁用全局护栏",
|
||||
"disableGlobalGuardrailsTooltip": "启用后,此团队将绕过配置为在每次请求上运行的护栏(全局护栏)",
|
||||
"disableGlobalGuardrailsHelp": "为此团队绕过全局护栏",
|
||||
"disableGlobalGuardrailsPremium": "高级功能 - 升级以按团队禁用全局护栏",
|
||||
"policies": "策略",
|
||||
"policiesTooltip": "为此团队应用策略以控制护栏和其他设置",
|
||||
"policiesHelp": "选择现有策略或输入新的策略",
|
||||
"policiesPlaceholder": "选择或输入策略",
|
||||
"accessGroups": "访问组",
|
||||
"accessGroupsTooltip": "为此团队分配访问组。访问组控制该团队可以使用的模型、MCP 服务器和 Agent",
|
||||
"accessGroupsHelp": "选择要分配给此团队的访问组",
|
||||
"accessGroupsPlaceholder": "选择访问组(可选)",
|
||||
"allowedVectorStores": "允许的向量存储",
|
||||
"allowedVectorStoresTooltip": "选择此团队默认可以访问的向量存储。留空则允许访问所有向量存储",
|
||||
"allowedVectorStoresHelp": "选择此团队可以访问的向量存储。留空则允许访问所有向量存储",
|
||||
"allowedVectorStoresPlaceholder": "选择向量存储(可选)",
|
||||
"allowedPassThroughRoutes": "允许的透传路由",
|
||||
"allowedPassThroughRoutesPremiumTooltip": "高级功能 - 升级以设置允许的透传路由",
|
||||
"allowedPassThroughRoutesAdminTooltip": "只有代理管理员可以设置允许的透传路由",
|
||||
"allowedPassThroughRoutesPlaceholder": "选择透传路由(可选)",
|
||||
"allowedMcpServers": "允许的 MCP 服务器",
|
||||
"allowedMcpServersTooltip": "选择此团队可以访问的 MCP 服务器或访问组",
|
||||
"allowedMcpServersHelp": "选择此团队可以访问的 MCP 服务器或访问组",
|
||||
"allowedMcpServersPlaceholder": "选择 MCP 服务器或访问组(可选)",
|
||||
"allowedAgents": "允许的 Agent",
|
||||
"allowedAgentsTooltip": "选择此团队可以访问的 Agent 或访问组",
|
||||
"allowedAgentsHelp": "选择此团队可以访问的 Agent 或访问组",
|
||||
"allowedAgentsPlaceholder": "选择 Agent 或访问组(可选)",
|
||||
"allowedSearchTools": "允许的搜索工具",
|
||||
"allowedSearchToolsTooltip": "选择此团队可以访问的搜索工具。留空则允许使用所有搜索工具。",
|
||||
"allowedSearchToolsHelp": "限制此团队上的密钥可以调用的已配置搜索工具。",
|
||||
"allowedSearchToolsPlaceholder": "选择搜索工具(可选,留空 = 允许所有)",
|
||||
"modelAliasesDescription": "为模型创建自定义别名,供团队成员在 API 调用中使用。这样您可以为特定模型创建快捷方式。"
|
||||
},
|
||||
"accordions": {
|
||||
"additionalSettings": "附加设置",
|
||||
"mcpSettings": "MCP 设置",
|
||||
"agentSettings": "Agent 设置",
|
||||
"searchToolSettings": "搜索工具设置",
|
||||
"loggingSettings": "日志设置",
|
||||
"routerSettings": "路由设置",
|
||||
"modelAliases": "模型别名"
|
||||
},
|
||||
"deleteTeamFailed": "删除团队失败:{{error}}",
|
||||
"createTeamFailed": "创建团队失败:{{error}}"
|
||||
},
|
||||
"common": {
|
||||
"save": "保存",
|
||||
"cancel": "取消",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue