feat(ui): i18n for api reference and models pages

This commit is contained in:
吹雪 2026-06-11 07:55:30 +09:00
parent 20b4eb3fdb
commit 29d8b86112
7 changed files with 260 additions and 86 deletions

View file

@ -3,6 +3,7 @@ import React from "react";
import { Text, Tab, TabGroup, TabList, TabPanel, TabPanels, Grid } from "@tremor/react";
import CodeBlock from "./components/CodeBlock";
import DocLink from "@/app/(dashboard)/api-reference/components/DocLink";
import { useTranslation } from "react-i18next";
interface ApiRefProps {
proxySettings: {
@ -12,6 +13,7 @@ interface ApiRefProps {
}
const APIReferenceView: React.FC<ApiRefProps> = ({ proxySettings }) => {
const { t } = useTranslation();
let base_url = "<your_proxy_base_url>";
const customDocBaseUrl = proxySettings?.LITELLM_UI_API_DOC_BASE_URL;
if (customDocBaseUrl && customDocBaseUrl.trim()) {
@ -27,21 +29,18 @@ const APIReferenceView: React.FC<ApiRefProps> = ({ proxySettings }) => {
{/* Header row with Docs link on the right */}
<div className="flex items-center justify-between">
<p className="text-2xl text-tremor-content-strong dark:text-dark-tremor-content-strong font-semibold">
OpenAI Compatible Proxy: API Reference
{t("pages.apiReference.title")}
</p>
<DocLink className="ml-3 shrink-0" href="https://docs.litellm.ai/docs/proxy/user_keys" />
</div>
<Text className="mt-2 mb-2">
LiteLLM is OpenAI Compatible. This means your API Key works with the OpenAI SDK. Just replace the base_url
to point to your litellm proxy. Example Below{" "}
</Text>
<Text className="mt-2 mb-2">{t("pages.apiReference.description")}</Text>
<TabGroup>
<TabList>
<Tab>OpenAI Python SDK</Tab>
<Tab>LlamaIndex</Tab>
<Tab>Langchain Py</Tab>
<Tab>{t("pages.apiReference.tabOpenAI")}</Tab>
<Tab>{t("pages.apiReference.tabLlamaIndex")}</Tab>
<Tab>{t("pages.apiReference.tabLangchain")}</Tab>
</TabList>
<TabPanels>
<TabPanel>

View file

@ -2,6 +2,7 @@ import { useState } from "react";
import { CheckIcon, ClipboardIcon } from "lucide-react";
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
import { oneLight } from "react-syntax-highlighter/dist/esm/styles/prism";
import { useTranslation } from "react-i18next";
interface CodeBlockProps {
code: string;
@ -9,6 +10,7 @@ interface CodeBlockProps {
}
const CodeBlock = ({ code, language }: CodeBlockProps) => {
const { t } = useTranslation();
const [copied, setCopied] = useState(false);
const copyToClipboard = () => {
navigator.clipboard.writeText(code);
@ -21,7 +23,7 @@ const CodeBlock = ({ code, language }: CodeBlockProps) => {
<button
onClick={copyToClipboard}
className="absolute top-3 right-3 p-2 rounded-md bg-gray-100 hover:bg-gray-200 text-gray-600 z-10"
aria-label="Copy code"
aria-label={t("pages.apiReference.copyCode")}
>
{copied ? <CheckIcon size={16} /> : <ClipboardIcon size={16} />}
</button>

View file

@ -1,5 +1,6 @@
import React from "react";
import { ExternalLink } from "lucide-react";
import { useTranslation } from "react-i18next";
function cn(...parts: Array<string | undefined>) {
return parts.filter(Boolean).join(" ");
@ -11,21 +12,22 @@ export type DocLinkProps = {
};
const DocLink = ({ href, className }: DocLinkProps) => {
const { t } = useTranslation();
return (
<a
href={href}
target="_blank"
rel="noopener noreferrer"
title="Open documentation in a new tab"
title={t("pages.apiReference.docLinkTitle")}
className={cn(
"inline-flex items-center gap-2 rounded-xl border border-zinc-200 bg-white/80 px-3.5 py-2 text-sm font-medium text-zinc-700 shadow-sm",
"hover:bg-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 active:translate-y-[0.5px]",
className,
)}
>
<span>API Reference Docs</span>
<span>{t("pages.apiReference.docLinkText")}</span>
<ExternalLink aria-hidden className="h-4 w-4 opacity-80" />
<span className="sr-only">(opens in a new tab)</span>
<span className="sr-only">{t("pages.apiReference.docLinkSrOnly")}</span>
</a>
);
};

View file

@ -20,6 +20,7 @@ import type { UploadProps } from "antd";
import { Form } from "antd";
import { PlusCircleOutlined } from "@ant-design/icons";
import React, { useEffect, useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
import AddModelTab from "../../../components/add_model/add_model_tab";
import HealthCheckComponent from "../../../components/model_dashboard/HealthCheckComponent";
import ModelGroupAliasSettings from "../../../components/model_group_alias_settings";
@ -49,6 +50,7 @@ interface GlobalRetryPolicyObject {
const HEALTH_PAGE_SIZE = 50;
const ModelsAndEndpointsView: React.FC<ModelDashboardProps> = ({ premiumUser, teams }) => {
const { t } = useTranslation();
const { accessToken, token, userRole, userId: userID } = useAuthorized();
const [addModelForm] = Form.useForm();
const [lastRefreshed, setLastRefreshed] = useState("");
@ -178,9 +180,9 @@ const ModelsAndEndpointsView: React.FC<ModelDashboardProps> = ({ premiumUser, te
},
onChange(info) {
if (info.file.status === "done") {
NotificationsManager.success(`${info.file.name} file uploaded successfully`);
NotificationsManager.success(t("pages.modelsAndEndpoints.fileUploadSuccess", { name: info.file.name }));
} else if (info.file.status === "error") {
NotificationsManager.fromBackend(`${info.file.name} file upload failed.`);
NotificationsManager.fromBackend(t("pages.modelsAndEndpoints.fileUploadFailed", { name: info.file.name }));
}
},
};
@ -207,17 +209,19 @@ const ModelsAndEndpointsView: React.FC<ModelDashboardProps> = ({ premiumUser, te
if (globalRetryPolicy) {
payload.router_settings.retry_policy = globalRetryPolicy;
}
NotificationsManager.success("Global retry settings saved successfully");
NotificationsManager.success(t("pages.modelsAndEndpoints.globalRetrySaved"));
} else {
if (modelGroupRetryPolicy) {
payload.router_settings.model_group_retry_policy = modelGroupRetryPolicy;
}
NotificationsManager.success(`Retry settings saved successfully for ${selectedModelGroup}`);
NotificationsManager.success(
t("pages.modelsAndEndpoints.modelGroupRetrySaved", { modelGroup: selectedModelGroup }),
);
}
await setCallbacksCall(accessToken, payload);
} catch (error) {
NotificationsManager.fromBackend("Failed to save retry settings");
NotificationsManager.fromBackend(t("pages.modelsAndEndpoints.failedToSaveRetry"));
}
};
@ -266,7 +270,7 @@ const ModelsAndEndpointsView: React.FC<ModelDashboardProps> = ({ premiumUser, te
return `${field.name.join(".")}: ${field.errors.join(", ")}`;
})
.join(" | ") || "Unknown validation error";
NotificationsManager.fromBackend(`Please fill in the following required fields: ${errorMessages}`);
NotificationsManager.fromBackend(t("pages.modelsAndEndpoints.validationError", { fields: errorMessages }));
}
};
@ -297,11 +301,11 @@ const ModelsAndEndpointsView: React.FC<ModelDashboardProps> = ({ premiumUser, te
{/* Model Management Header */}
<div className="flex justify-between items-center mb-4">
<div>
<h2 className="text-lg font-semibold">Model Management</h2>
<h2 className="text-lg font-semibold">{t("pages.modelsAndEndpoints.modelManagement")}</h2>
{!all_admin_roles.includes(userRole) ? (
<p className="text-sm text-gray-600">Add models for teams you are an admin for.</p>
<p className="text-sm text-gray-600">{t("pages.modelsAndEndpoints.addModelsForTeams")}</p>
) : (
<p className="text-sm text-gray-600">Add and manage models for the proxy</p>
<p className="text-sm text-gray-600">{t("pages.modelsAndEndpoints.addAndManageModels")}</p>
)}
</div>
{!showMissingProviderBanner && (
@ -312,7 +316,7 @@ const ModelsAndEndpointsView: React.FC<ModelDashboardProps> = ({ premiumUser, te
className="inline-flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium text-[#6366f1] hover:text-[#5558e3] border border-[#6366f1] hover:border-[#5558e3] rounded-lg transition-colors"
>
<PlusCircleOutlined style={{ fontSize: "12px" }} />
Request Provider
{t("pages.modelsAndEndpoints.requestProvider")}
</a>
)}
</div>
@ -324,11 +328,10 @@ const ModelsAndEndpointsView: React.FC<ModelDashboardProps> = ({ premiumUser, te
<PlusCircleOutlined style={{ fontSize: "18px", color: "#6366f1" }} />
</div>
<div className="flex-1 min-w-0">
<h4 className="text-gray-900 font-semibold text-sm m-0">Missing a provider?</h4>
<p className="text-gray-500 text-xs m-0 mt-0.5">
The LiteLLM engineering team is constantly adding support for new LLM models, providers, endpoints. If
you don&apos;t see the one you need, let us know and we&apos;ll prioritize it.
</p>
<h4 className="text-gray-900 font-semibold text-sm m-0">
{t("pages.modelsAndEndpoints.missingProviderTitle")}
</h4>
<p className="text-gray-500 text-xs m-0 mt-0.5">{t("pages.modelsAndEndpoints.missingProviderDesc")}</p>
</div>
<a
href="https://models.litellm.ai/?request=true"
@ -336,7 +339,7 @@ const ModelsAndEndpointsView: React.FC<ModelDashboardProps> = ({ premiumUser, te
rel="noopener noreferrer"
className="flex-shrink-0 inline-flex items-center gap-2 px-4 py-2 bg-[#6366f1] hover:bg-[#5558e3] text-white text-sm font-medium rounded-lg transition-colors"
>
Request Provider
{t("pages.modelsAndEndpoints.requestProvider")}
<svg
xmlns="http://www.w3.org/2000/svg"
className="h-4 w-4"
@ -358,7 +361,7 @@ const ModelsAndEndpointsView: React.FC<ModelDashboardProps> = ({ premiumUser, te
localStorage.setItem("hideMissingProviderBanner", "true");
}}
className="flex-shrink-0 p-1 text-gray-400 hover:text-gray-600 hover:bg-gray-100 rounded-full transition-colors"
aria-label="Dismiss banner"
aria-label={t("pages.modelsAndEndpoints.dismissBanner")}
>
<svg
xmlns="http://www.w3.org/2000/svg"
@ -399,7 +402,13 @@ const ModelsAndEndpointsView: React.FC<ModelDashboardProps> = ({ premiumUser, te
const isAdmin = all_admin_roles.includes(userRole);
const visibleTabs: Array<{ tab: React.ReactElement; panel: React.ReactElement }> = [
{
tab: <Tab key="all-models">{isAdmin ? "All Models" : "Your Models"}</Tab>,
tab: (
<Tab key="all-models">
{isAdmin
? t("pages.modelsAndEndpoints.tabAllModels")
: t("pages.modelsAndEndpoints.tabYourModels")}
</Tab>
),
panel: (
<AllModelsTab
key="all-models"
@ -415,7 +424,7 @@ const ModelsAndEndpointsView: React.FC<ModelDashboardProps> = ({ premiumUser, te
];
if (!shouldHideAddModelTab) {
visibleTabs.push({
tab: <Tab key="add-model">Add Model</Tab>,
tab: <Tab key="add-model">{t("pages.modelsAndEndpoints.tabAddModel")}</Tab>,
panel: (
<TabPanel key="add-model" className="h-full">
<AddModelTab
@ -441,7 +450,7 @@ const ModelsAndEndpointsView: React.FC<ModelDashboardProps> = ({ premiumUser, te
if (isAdmin) {
visibleTabs.push(
{
tab: <Tab key="llm-credentials">LLM Credentials</Tab>,
tab: <Tab key="llm-credentials">{t("pages.modelsAndEndpoints.tabLLMCredentials")}</Tab>,
panel: (
<TabPanel key="llm-credentials">
<CredentialsPanel uploadProps={uploadProps} />
@ -449,7 +458,7 @@ const ModelsAndEndpointsView: React.FC<ModelDashboardProps> = ({ premiumUser, te
),
},
{
tab: <Tab key="pass-through">Pass-Through Endpoints</Tab>,
tab: <Tab key="pass-through">{t("pages.modelsAndEndpoints.tabPassThrough")}</Tab>,
panel: (
<TabPanel key="pass-through">
<PassThroughSettings
@ -463,7 +472,7 @@ const ModelsAndEndpointsView: React.FC<ModelDashboardProps> = ({ premiumUser, te
),
},
{
tab: <Tab key="health-status">Health Status</Tab>,
tab: <Tab key="health-status">{t("pages.modelsAndEndpoints.tabHealthStatus")}</Tab>,
panel: (
<TabPanel key="health-status">
<HealthCheckComponent
@ -483,7 +492,7 @@ const ModelsAndEndpointsView: React.FC<ModelDashboardProps> = ({ premiumUser, te
),
},
{
tab: <Tab key="model-retry-settings">Model Retry Settings</Tab>,
tab: <Tab key="model-retry-settings">{t("pages.modelsAndEndpoints.tabModelRetrySettings")}</Tab>,
panel: (
<ModelRetrySettingsTab
key="model-retry-settings"
@ -500,7 +509,7 @@ const ModelsAndEndpointsView: React.FC<ModelDashboardProps> = ({ premiumUser, te
),
},
{
tab: <Tab key="model-group-alias">Model Group Alias</Tab>,
tab: <Tab key="model-group-alias">{t("pages.modelsAndEndpoints.tabModelGroupAlias")}</Tab>,
panel: (
<TabPanel key="model-group-alias">
<ModelGroupAliasSettings
@ -512,7 +521,7 @@ const ModelsAndEndpointsView: React.FC<ModelDashboardProps> = ({ premiumUser, te
),
},
{
tab: <Tab key="price-data-reload">Price Data Reload</Tab>,
tab: <Tab key="price-data-reload">{t("pages.modelsAndEndpoints.tabPriceDataReload")}</Tab>,
panel: <PriceDataManagementTab key="price-data-reload" />,
},
);
@ -527,7 +536,11 @@ const ModelsAndEndpointsView: React.FC<ModelDashboardProps> = ({ premiumUser, te
<div className="flex">{visibleTabs.map((t) => t.tab)}</div>
<div className="flex items-center space-x-2 self-center">
{lastRefreshed && <span className="text-xs text-gray-500">Last Refreshed: {lastRefreshed}</span>}
{lastRefreshed && (
<span className="text-xs text-gray-500">
{t("pages.modelsAndEndpoints.lastRefreshed", { time: lastRefreshed })}
</span>
)}
<Icon
icon={RefreshIcon}
variant="shadow"

View file

@ -16,6 +16,7 @@ import { Badge, Button, Select, Skeleton, Space, Typography } from "antd";
import ModelSettingsModal from "@/components/model_dashboard/ModelSettingsModal/ModelSettingsModal";
import debounce from "lodash/debounce";
import { useEffect, useMemo, useState } from "react";
import { useTranslation, Trans } from "react-i18next";
import { useModelsInfo } from "../../hooks/models/useModels";
import { transformModelData } from "../utils/modelDataTransformer";
type ModelViewMode = "all" | "current_team";
@ -38,6 +39,7 @@ const AllModelsTab = ({
setSelectedModelId,
setSelectedTeamId,
}: AllModelsTabProps) => {
const { t } = useTranslation();
const { data: modelCostMapData, isLoading: isLoadingModelCostMap } = useModelCostMap();
const { accessToken, userId, userRole, premiumUser } = useAuthorized();
const { data: teams, isLoading: isLoadingTeams } = useTeams();
@ -204,7 +206,7 @@ const AllModelsTab = ({
try {
setDeleteLoading(true);
await modelDeleteCall(accessToken, deleteModalModelId);
NotificationsManager.success("Model deleted successfully");
NotificationsManager.success(t("pages.allModelsTab.modelDeletedSuccess"));
queryClient.invalidateQueries({ queryKey: ["models", "list"] });
refetchModels();
} catch (error) {
@ -223,7 +225,9 @@ const AllModelsTab = ({
try {
setPausingModelId(modelId);
await modelPatchUpdateCall(accessToken, { blocked }, modelId);
NotificationsManager.success(blocked ? "Model paused" : "Model resumed");
NotificationsManager.success(
blocked ? t("pages.allModelsTab.modelPaused") : t("pages.allModelsTab.modelResumed"),
);
// invalidateQueries already schedules a refetch for active observers
// on this key — no need to also call refetchModels() (would double-fetch).
queryClient.invalidateQueries({ queryKey: ["models", "list"] });
@ -244,7 +248,7 @@ const AllModelsTab = ({
<div className="border-b px-6 py-4 bg-gray-50">
<div className="flex items-center justify-between">
<div className="flex items-center gap-4">
<Text className="text-lg font-semibold text-gray-900">Current Team:</Text>
<Text className="text-lg font-semibold text-gray-900">{t("pages.allModelsTab.currentTeam")}</Text>
<div className="w-80">
{isLoading ? (
<Skeleton.Input active block size="large" />
@ -277,7 +281,7 @@ const AllModelsTab = ({
label: (
<Space direction="horizontal" align="center">
<Badge color="blue" size="small" />
<Text style={{ fontSize: 16 }}>Personal</Text>
<Text style={{ fontSize: 16 }}>{t("pages.allModelsTab.personal")}</Text>
</Space>
),
},
@ -300,7 +304,7 @@ const AllModelsTab = ({
</div>
</div>
<div className="flex items-center gap-4">
<Text className="text-lg font-semibold text-gray-900">View:</Text>
<Text className="text-lg font-semibold text-gray-900">{t("pages.allModelsTab.view")}</Text>
<div className="w-64">
{isLoading ? (
<Skeleton.Input active block size="large" />
@ -317,7 +321,7 @@ const AllModelsTab = ({
label: (
<Space direction="horizontal" align="center">
<Badge color="purple" size="small" />
<Text style={{ fontSize: 16 }}>Current Team Models</Text>
<Text style={{ fontSize: 16 }}>{t("pages.allModelsTab.currentTeamModels")}</Text>
</Space>
),
},
@ -326,7 +330,7 @@ const AllModelsTab = ({
label: (
<Space direction="horizontal" align="center">
<Badge color="gray" size="small" />
<Text style={{ fontSize: 16 }}>All Available Models</Text>
<Text style={{ fontSize: 16 }}>{t("pages.allModelsTab.allAvailableModels")}</Text>
</Space>
),
},
@ -343,25 +347,35 @@ const AllModelsTab = ({
<div className="text-xs text-gray-500">
{currentTeam === "personal" ? (
<span>
To access these models: Create a Virtual Key without selecting a team on the{" "}
<a
href="/public?login=success&page=api-keys"
className="text-gray-600 hover:text-gray-800 underline"
>
Virtual Keys page
</a>
<Trans
i18nKey="pages.allModelsTab.accessInfoPersonal"
components={{
a: (
<a
href="/public?login=success&page=api-keys"
className="text-gray-600 hover:text-gray-800 underline"
/>
),
}}
/>
</span>
) : (
<span>
To access these models: Create a Virtual Key and select Team as &quot;
{typeof currentTeam !== "string" ? currentTeam.team_alias || currentTeam.team_id : ""}&quot; on
the{" "}
<a
href="/public?login=success&page=api-keys"
className="text-gray-600 hover:text-gray-800 underline"
>
Virtual Keys page
</a>
<Trans
i18nKey="pages.allModelsTab.accessInfoTeam"
values={{
teamName:
typeof currentTeam !== "string" ? currentTeam.team_alias || currentTeam.team_id : "",
}}
components={{
a: (
<a
href="/public?login=success&page=api-keys"
className="text-gray-600 hover:text-gray-800 underline"
/>
),
}}
/>
</span>
)}
</div>
@ -379,7 +393,7 @@ const AllModelsTab = ({
<div className="relative w-64">
<input
type="text"
placeholder="Search model names..."
placeholder={t("pages.allModelsTab.searchPlaceholder")}
data-testid="model-search-input"
className="w-full px-3 py-2 pl-8 border rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
value={modelNameSearch}
@ -413,7 +427,7 @@ const AllModelsTab = ({
d="M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.293A1 1 0 013 6.586V4z"
/>
</svg>
Filters
{t("pages.allModelsTab.filters")}
</button>
{/* Reset Filters Button */}
@ -429,7 +443,7 @@ const AllModelsTab = ({
d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"
/>
</svg>
Reset Filters
{t("pages.allModelsTab.resetFilters")}
</button>
</div>
@ -437,7 +451,7 @@ const AllModelsTab = ({
<Button
icon={<SettingOutlined />}
onClick={() => setIsModelSettingsModalVisible(true)}
title="Model Settings"
title={t("pages.allModelsTab.modelSettings")}
/>
</div>
@ -450,11 +464,11 @@ const AllModelsTab = ({
className="w-full"
value={selectedModelGroup ?? "all"}
onChange={(value) => setSelectedModelGroup(value === "all" ? "all" : value)}
placeholder="Filter by Public Model Name"
placeholder={t("pages.allModelsTab.filterByPublicModelName")}
showSearch
options={[
{ value: "all", label: "All Models" },
{ value: "wildcard", label: "Wildcard Models (*)" },
{ value: "all", label: t("pages.allModelsTab.allModels") },
{ value: "wildcard", label: t("pages.allModelsTab.wildcardModels") },
...availableModelGroups.map((group, idx) => ({
value: group,
label: group,
@ -469,10 +483,10 @@ const AllModelsTab = ({
className="w-full"
value={selectedModelAccessGroupFilter ?? "all"}
onChange={(value) => setSelectedModelAccessGroupFilter(value === "all" ? null : value)}
placeholder="Filter by Model Access Group"
placeholder={t("pages.allModelsTab.filterByModelAccessGroup")}
showSearch
options={[
{ value: "all", label: "All Model Access Groups" },
{ value: "all", label: t("pages.allModelsTab.allModelAccessGroups") },
...availableModelAccessGroups.map((accessGroup, idx) => ({
value: accessGroup,
label: accessGroup,
@ -490,8 +504,12 @@ const AllModelsTab = ({
) : (
<span data-testid="models-results-count" className="text-sm text-gray-700">
{paginationMeta.total_count > 0
? `Showing ${(currentPage - 1) * pageSize + 1} - ${Math.min(currentPage * pageSize, paginationMeta.total_count)} of ${paginationMeta.total_count} results`
: "Showing 0 results"}
? t("pages.allModelsTab.showingResults", {
from: (currentPage - 1) * pageSize + 1,
to: Math.min(currentPage * pageSize, paginationMeta.total_count),
total: paginationMeta.total_count,
})
: t("pages.allModelsTab.showingZeroResults")}
</span>
)}
@ -510,7 +528,7 @@ const AllModelsTab = ({
currentPage === 1 ? "bg-gray-100 text-gray-400 cursor-not-allowed" : "hover:bg-gray-50"
}`}
>
Previous
{t("common.previous")}
</button>
)}
@ -530,7 +548,7 @@ const AllModelsTab = ({
: "hover:bg-gray-50"
}`}
>
Next
{t("common.next")}
</button>
)}
</div>
@ -569,28 +587,28 @@ const AllModelsTab = ({
<DeleteResourceModal
isOpen={!!deleteModalModelId}
title="Delete Model"
alertMessage="This action cannot be undone."
message="Are you sure you want to delete this model?"
resourceInformationTitle="Model Information"
title={t("pages.allModelsTab.deleteModelTitle")}
alertMessage={t("pages.allModelsTab.deleteAlertMessage")}
message={t("pages.allModelsTab.deleteMessage")}
resourceInformationTitle={t("pages.allModelsTab.modelInformation")}
resourceInformation={
modelToDelete
? [
{
label: "Model Name",
value: modelToDelete.model_name || "Not Set",
label: t("pages.allModelsTab.modelName"),
value: modelToDelete.model_name || t("pages.allModelsTab.notSet"),
},
{
label: "LiteLLM Model Name",
value: modelToDelete.litellm_model_name || "Not Set",
label: t("pages.allModelsTab.litellmModelName"),
value: modelToDelete.litellm_model_name || t("pages.allModelsTab.notSet"),
},
{
label: "Provider",
value: modelToDelete.provider || "Not Set",
label: t("pages.allModelsTab.provider"),
value: modelToDelete.provider || t("pages.allModelsTab.notSet"),
},
{
label: "Created By",
value: modelToDelete.model_info?.created_by || "Not Set",
label: t("pages.allModelsTab.createdBy"),
value: modelToDelete.model_info?.created_by || t("pages.allModelsTab.notSet"),
},
]
: []

View file

@ -8512,5 +8512,75 @@
"less": "less",
"more": "more"
}
},
"pages": {
"apiReference": {
"title": "OpenAI Compatible Proxy: API Reference",
"description": "LiteLLM is OpenAI Compatible. This means your API Key works with the OpenAI SDK. Just replace the base_url to point to your litellm proxy. Example Below",
"tabOpenAI": "OpenAI Python SDK",
"tabLlamaIndex": "LlamaIndex",
"tabLangchain": "Langchain Py",
"copyCode": "Copy code",
"docLinkTitle": "Open documentation in a new tab",
"docLinkText": "API Reference Docs",
"docLinkSrOnly": "(opens in a new tab)"
},
"modelsAndEndpoints": {
"modelManagement": "Model Management",
"addModelsForTeams": "Add models for teams you are an admin for.",
"addAndManageModels": "Add and manage models for the proxy",
"requestProvider": "Request Provider",
"missingProviderTitle": "Missing a provider?",
"missingProviderDesc": "The LiteLLM engineering team is constantly adding support for new LLM models, providers, endpoints. If you don't see the one you need, let us know and we'll prioritize it.",
"dismissBanner": "Dismiss banner",
"tabAllModels": "All Models",
"tabYourModels": "Your Models",
"tabAddModel": "Add Model",
"tabLLMCredentials": "LLM Credentials",
"tabPassThrough": "Pass-Through Endpoints",
"tabHealthStatus": "Health Status",
"tabModelRetrySettings": "Model Retry Settings",
"tabModelGroupAlias": "Model Group Alias",
"tabPriceDataReload": "Price Data Reload",
"lastRefreshed": "Last Refreshed: {{time}}",
"fileUploadSuccess": "{{name}} file uploaded successfully",
"fileUploadFailed": "{{name}} file upload failed.",
"globalRetrySaved": "Global retry settings saved successfully",
"modelGroupRetrySaved": "Retry settings saved successfully for {{modelGroup}}",
"failedToSaveRetry": "Failed to save retry settings",
"validationError": "Please fill in the following required fields: {{fields}}"
},
"allModelsTab": {
"currentTeam": "Current Team:",
"personal": "Personal",
"view": "View:",
"currentTeamModels": "Current Team Models",
"allAvailableModels": "All Available Models",
"accessInfoPersonal": "To access these models: Create a Virtual Key without selecting a team on the <a>Virtual Keys page</a>",
"accessInfoTeam": "To access these models: Create a Virtual Key and select Team as \"{{teamName}}\" on the <a>Virtual Keys page</a>",
"searchPlaceholder": "Search model names...",
"filters": "Filters",
"resetFilters": "Reset Filters",
"filterByPublicModelName": "Filter by Public Model Name",
"allModels": "All Models",
"wildcardModels": "Wildcard Models (*)",
"filterByModelAccessGroup": "Filter by Model Access Group",
"allModelAccessGroups": "All Model Access Groups",
"showingResults": "Showing {{from}} - {{to}} of {{total}} results",
"showingZeroResults": "Showing 0 results",
"modelSettings": "Model Settings",
"deleteModelTitle": "Delete Model",
"deleteAlertMessage": "This action cannot be undone.",
"deleteMessage": "Are you sure you want to delete this model?",
"modelInformation": "Model Information",
"modelName": "Model Name",
"litellmModelName": "LiteLLM Model Name",
"provider": "Provider",
"createdBy": "Created By",
"notSet": "Not Set",
"modelDeletedSuccess": "Model deleted successfully",
"modelPaused": "Model paused",
"modelResumed": "Model resumed"
}
}
}

View file

@ -8512,5 +8512,75 @@
"less": "收起",
"more": "展开"
}
},
"pages": {
"apiReference": {
"title": "OpenAI 兼容代理:API 参考",
"description": "LiteLLM 与 OpenAI 兼容,这意味着您的 API 密钥可与 OpenAI SDK 配合使用。只需将 base_url 替换为指向您的 litellm 代理即可。示例如下",
"tabOpenAI": "OpenAI Python SDK",
"tabLlamaIndex": "LlamaIndex",
"tabLangchain": "Langchain Py",
"copyCode": "复制代码",
"docLinkTitle": "在新标签页中打开文档",
"docLinkText": "API 参考文档",
"docLinkSrOnly": "(在新标签页中打开)"
},
"modelsAndEndpoints": {
"modelManagement": "模型管理",
"addModelsForTeams": "为您担任管理员的团队添加模型。",
"addAndManageModels": "为代理添加和管理模型",
"requestProvider": "申请提供商",
"missingProviderTitle": "缺少某个提供商?",
"missingProviderDesc": "LiteLLM 工程团队持续添加对新 LLM 模型、提供商和端点的支持。如果您没有找到所需的提供商,请告知我们,我们将优先处理。",
"dismissBanner": "关闭横幅",
"tabAllModels": "全部模型",
"tabYourModels": "我的模型",
"tabAddModel": "添加模型",
"tabLLMCredentials": "LLM 凭证",
"tabPassThrough": "透传端点",
"tabHealthStatus": "健康检查",
"tabModelRetrySettings": "模型重试设置",
"tabModelGroupAlias": "模型组别名",
"tabPriceDataReload": "价格数据重载",
"lastRefreshed": "上次刷新:{{time}}",
"fileUploadSuccess": "{{name}} 文件上传成功",
"fileUploadFailed": "{{name}} 文件上传失败。",
"globalRetrySaved": "全局重试设置保存成功",
"modelGroupRetrySaved": "{{modelGroup}} 的重试设置保存成功",
"failedToSaveRetry": "保存重试设置失败",
"validationError": "请填写以下必填字段:{{fields}}"
},
"allModelsTab": {
"currentTeam": "当前团队:",
"personal": "个人",
"view": "视图:",
"currentTeamModels": "当前团队模型",
"allAvailableModels": "全部可用模型",
"accessInfoPersonal": "要访问这些模型:在<a>虚拟密钥页面</a>创建一个虚拟密钥,不要选择团队",
"accessInfoTeam": "要访问这些模型:在<a>虚拟密钥页面</a>创建虚拟密钥并选择团队\"{{teamName}}\"",
"searchPlaceholder": "搜索模型名称...",
"filters": "筛选",
"resetFilters": "重置筛选",
"filterByPublicModelName": "按公开模型名称筛选",
"allModels": "全部模型",
"wildcardModels": "通配符模型 (*)",
"filterByModelAccessGroup": "按模型访问组筛选",
"allModelAccessGroups": "全部模型访问组",
"showingResults": "显示第 {{from}} - {{to}} 条,共 {{total}} 条",
"showingZeroResults": "共 0 条结果",
"modelSettings": "模型设置",
"deleteModelTitle": "删除模型",
"deleteAlertMessage": "此操作无法撤销。",
"deleteMessage": "确定要删除此模型吗?",
"modelInformation": "模型信息",
"modelName": "模型名称",
"litellmModelName": "LiteLLM 模型名称",
"provider": "提供商",
"createdBy": "创建者",
"notSet": "未设置",
"modelDeletedSuccess": "模型已成功删除",
"modelPaused": "模型已暂停",
"modelResumed": "模型已恢复"
}
}
}