mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-03 02:22:24 +00:00
feat(ui): i18n for AI hub tables
This commit is contained in:
parent
fd28940f49
commit
71624ed3b7
6 changed files with 380 additions and 121 deletions
|
|
@ -2,8 +2,35 @@ import { render, screen } from "@testing-library/react";
|
|||
import userEvent from "@testing-library/user-event";
|
||||
import { vi } from "vitest";
|
||||
import { flexRender, getCoreRowModel, useReactTable } from "@tanstack/react-table";
|
||||
import { TFunction } from "i18next";
|
||||
import { getAgentHubTableColumns, AgentHubData } from "./AgentHubTableColumns";
|
||||
|
||||
const mockT = ((key: string, opts?: Record<string, unknown>) => {
|
||||
if (key === "aiHub.agentHubTableColumns.skillCount") {
|
||||
const count = opts?.count as number;
|
||||
return count === 1 ? `${count} skill` : `${count} skills`;
|
||||
}
|
||||
const last = key.split(".").pop() ?? key;
|
||||
const map: Record<string, string> = {
|
||||
colAgentName: "Agent Name",
|
||||
copyAgentName: "Copy agent name",
|
||||
colVersion: "Version",
|
||||
colProtocol: "Protocol",
|
||||
colSkills: "Skills",
|
||||
colCapabilities: "Capabilities",
|
||||
colIOModes: "I/O Modes",
|
||||
ioIn: "In:",
|
||||
ioOut: "Out:",
|
||||
colPublic: "Public",
|
||||
detailsShort: "Info",
|
||||
description: "Description",
|
||||
details: "Details",
|
||||
yes: "Yes",
|
||||
no: "No",
|
||||
};
|
||||
return map[last] ?? key;
|
||||
}) as unknown as TFunction;
|
||||
|
||||
const mockAgent: AgentHubData = {
|
||||
agent_id: "agent-1",
|
||||
protocolVersion: "1.0",
|
||||
|
|
@ -33,7 +60,7 @@ function TestTable({
|
|||
showModal?: ReturnType<typeof vi.fn>;
|
||||
copyToClipboard?: ReturnType<typeof vi.fn>;
|
||||
}) {
|
||||
const columns = getAgentHubTableColumns(showModal, copyToClipboard, publicPage);
|
||||
const columns = getAgentHubTableColumns(mockT, showModal, copyToClipboard, publicPage);
|
||||
const table = useReactTable({ data, columns, getCoreRowModel: getCoreRowModel() });
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { ColumnDef } from "@tanstack/react-table";
|
|||
import { Button, Badge, Text } from "@tremor/react";
|
||||
import { Tooltip, Tag } from "antd";
|
||||
import { CopyOutlined, InfoCircleOutlined } from "@ant-design/icons";
|
||||
import { TFunction } from "i18next";
|
||||
|
||||
export interface AgentHubData {
|
||||
agent_id?: string;
|
||||
|
|
@ -29,13 +30,14 @@ export interface AgentHubData {
|
|||
}
|
||||
|
||||
export const getAgentHubTableColumns = (
|
||||
t: TFunction,
|
||||
showModal: (agent: AgentHubData) => void,
|
||||
copyToClipboard: (text: string) => void,
|
||||
publicPage: boolean = false,
|
||||
): ColumnDef<AgentHubData>[] => {
|
||||
const allColumns: ColumnDef<AgentHubData>[] = [
|
||||
{
|
||||
header: "Agent Name",
|
||||
header: t("aiHub.agentHubTableColumns.colAgentName"),
|
||||
accessorKey: "name",
|
||||
enableSorting: true,
|
||||
sortingFn: "alphanumeric",
|
||||
|
|
@ -46,7 +48,7 @@ export const getAgentHubTableColumns = (
|
|||
<div className="space-y-1">
|
||||
<div className="flex items-center space-x-2">
|
||||
<Text className="font-medium text-sm">{agent.name}</Text>
|
||||
<Tooltip title="Copy agent name">
|
||||
<Tooltip title={t("aiHub.agentHubTableColumns.copyAgentName")}>
|
||||
<CopyOutlined
|
||||
onClick={() => copyToClipboard(agent.name)}
|
||||
className="cursor-pointer text-gray-500 hover:text-blue-500 text-xs"
|
||||
|
|
@ -62,7 +64,7 @@ export const getAgentHubTableColumns = (
|
|||
},
|
||||
},
|
||||
{
|
||||
header: "Description",
|
||||
header: t("common.description"),
|
||||
accessorKey: "description",
|
||||
enableSorting: true,
|
||||
sortingFn: "alphanumeric",
|
||||
|
|
@ -76,7 +78,7 @@ export const getAgentHubTableColumns = (
|
|||
},
|
||||
},
|
||||
{
|
||||
header: "Version",
|
||||
header: t("aiHub.agentHubTableColumns.colVersion"),
|
||||
accessorKey: "version",
|
||||
enableSorting: true,
|
||||
sortingFn: "alphanumeric",
|
||||
|
|
@ -94,7 +96,7 @@ export const getAgentHubTableColumns = (
|
|||
},
|
||||
},
|
||||
{
|
||||
header: "Protocol",
|
||||
header: t("aiHub.agentHubTableColumns.colProtocol"),
|
||||
accessorKey: "protocolVersion",
|
||||
enableSorting: true,
|
||||
sortingFn: "alphanumeric",
|
||||
|
|
@ -108,7 +110,7 @@ export const getAgentHubTableColumns = (
|
|||
},
|
||||
},
|
||||
{
|
||||
header: "Skills",
|
||||
header: t("aiHub.agentHubTableColumns.colSkills"),
|
||||
accessorKey: "skills",
|
||||
enableSorting: false,
|
||||
cell: ({ row }) => {
|
||||
|
|
@ -118,7 +120,7 @@ export const getAgentHubTableColumns = (
|
|||
return (
|
||||
<div className="space-y-1">
|
||||
<Text className="text-xs font-medium">
|
||||
{skills.length} skill{skills.length !== 1 ? "s" : ""}
|
||||
{t("aiHub.agentHubTableColumns.skillCount", { count: skills.length })}
|
||||
</Text>
|
||||
{skills.length > 0 && (
|
||||
<div className="flex flex-wrap gap-1">
|
||||
|
|
@ -135,7 +137,7 @@ export const getAgentHubTableColumns = (
|
|||
},
|
||||
},
|
||||
{
|
||||
header: "Capabilities",
|
||||
header: t("aiHub.agentHubTableColumns.colCapabilities"),
|
||||
accessorKey: "capabilities",
|
||||
enableSorting: false,
|
||||
cell: ({ row }) => {
|
||||
|
|
@ -161,7 +163,7 @@ export const getAgentHubTableColumns = (
|
|||
},
|
||||
},
|
||||
{
|
||||
header: "I/O Modes",
|
||||
header: t("aiHub.agentHubTableColumns.colIOModes"),
|
||||
accessorKey: "defaultInputModes",
|
||||
enableSorting: false,
|
||||
cell: ({ row }) => {
|
||||
|
|
@ -172,10 +174,11 @@ export const getAgentHubTableColumns = (
|
|||
return (
|
||||
<div className="space-y-1">
|
||||
<Text className="text-xs">
|
||||
<span className="font-medium">In:</span> {inputModes.join(", ") || "-"}
|
||||
<span className="font-medium">{t("aiHub.agentHubTableColumns.ioIn")}</span> {inputModes.join(", ") || "-"}
|
||||
</Text>
|
||||
<Text className="text-xs">
|
||||
<span className="font-medium">Out:</span> {outputModes.join(", ") || "-"}
|
||||
<span className="font-medium">{t("aiHub.agentHubTableColumns.ioOut")}</span>{" "}
|
||||
{outputModes.join(", ") || "-"}
|
||||
</Text>
|
||||
</div>
|
||||
);
|
||||
|
|
@ -185,7 +188,7 @@ export const getAgentHubTableColumns = (
|
|||
},
|
||||
},
|
||||
{
|
||||
header: "Public",
|
||||
header: t("aiHub.agentHubTableColumns.colPublic"),
|
||||
accessorKey: "is_public",
|
||||
enableSorting: true,
|
||||
sortingFn: (rowA, rowB) => {
|
||||
|
|
@ -198,11 +201,11 @@ export const getAgentHubTableColumns = (
|
|||
|
||||
return agent.is_public === true ? (
|
||||
<Badge color="green" size="xs">
|
||||
Yes
|
||||
{t("common.yes")}
|
||||
</Badge>
|
||||
) : (
|
||||
<Badge color="gray" size="xs">
|
||||
No
|
||||
{t("common.no")}
|
||||
</Badge>
|
||||
);
|
||||
},
|
||||
|
|
@ -211,7 +214,7 @@ export const getAgentHubTableColumns = (
|
|||
},
|
||||
},
|
||||
{
|
||||
header: "Details",
|
||||
header: t("common.details"),
|
||||
id: "details",
|
||||
enableSorting: false,
|
||||
cell: ({ row }) => {
|
||||
|
|
@ -219,8 +222,8 @@ export const getAgentHubTableColumns = (
|
|||
|
||||
return (
|
||||
<Button size="xs" variant="secondary" onClick={() => showModal(agent)} icon={InfoCircleOutlined}>
|
||||
<span className="hidden lg:inline">Details</span>
|
||||
<span className="lg:hidden">Info</span>
|
||||
<span className="hidden lg:inline">{t("common.details")}</span>
|
||||
<span className="lg:hidden">{t("aiHub.agentHubTableColumns.detailsShort")}</span>
|
||||
</Button>
|
||||
);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ import { useRouter } from "next/navigation";
|
|||
import React, { useCallback, useEffect, useState } from "react";
|
||||
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
|
||||
import { useUISettings } from "@/app/(dashboard)/hooks/uiSettings/useUISettings";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { checkTokenValidity } from "@/utils/jwtUtils";
|
||||
import { getCookie } from "@/utils/cookieUtils";
|
||||
|
||||
|
|
@ -64,6 +65,7 @@ const ModelHubTable: React.FC<ModelHubTableProps> = ({ accessToken, publicPage,
|
|||
// Admin Viewer follows the read-parity rule: see the AI Hub catalog, but
|
||||
// cannot toggle public visibility (write).
|
||||
const canModify = isProxyAdminRole(userRole || "");
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [publicPageAllowed, setPublicPageAllowed] = useState<boolean>(false);
|
||||
const [modelHubData, setModelHubData] = useState<ModelGroupInfo[] | null>(null);
|
||||
|
|
@ -306,7 +308,7 @@ const ModelHubTable: React.FC<ModelHubTableProps> = ({ accessToken, publicPage,
|
|||
|
||||
const copyToClipboard = (text: string) => {
|
||||
navigator.clipboard.writeText(text);
|
||||
NotificationsManager.success("Copied to clipboard!");
|
||||
NotificationsManager.success(t("aiHub.modelHubTable.copiedToClipboard"));
|
||||
};
|
||||
|
||||
const formatCapabilityName = (key: string) => {
|
||||
|
|
@ -399,23 +401,21 @@ const ModelHubTable: React.FC<ModelHubTableProps> = ({ accessToken, publicPage,
|
|||
{/* Header with Title, Description and URL */}
|
||||
<div className="flex justify-between items-center mb-6">
|
||||
<div className="flex flex-col items-start">
|
||||
<Title className="text-center">AI Hub</Title>
|
||||
<Title className="text-center">{t("aiHub.modelHubTable.title")}</Title>
|
||||
{isAdminRole(userRole || "") ? (
|
||||
<p className="text-sm text-gray-600">
|
||||
Make models, agents, and MCP servers public for developers to know what's available.
|
||||
</p>
|
||||
<p className="text-sm text-gray-600">{t("aiHub.modelHubTable.adminDescription")}</p>
|
||||
) : (
|
||||
<p className="text-sm text-gray-600">A list of all public model names personally available to you.</p>
|
||||
<p className="text-sm text-gray-600">{t("aiHub.modelHubTable.userDescription")}</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center space-x-4">
|
||||
<Text>Model Hub URL:</Text>
|
||||
<Text>{t("aiHub.modelHubTable.modelHubUrl")}</Text>
|
||||
<div className="flex items-center bg-gray-200 px-2 py-1 rounded">
|
||||
<Text className="mr-2">{`${getProxyBaseUrl()}/ui/model_hub_table`}</Text>
|
||||
<button
|
||||
onClick={() => copyToClipboard(`${getProxyBaseUrl()}/ui/model_hub_table`)}
|
||||
className="p-1 hover:bg-gray-300 rounded transition-colors"
|
||||
title="Copy URL"
|
||||
title={t("aiHub.modelHubTable.copyUrl")}
|
||||
>
|
||||
<Copy size={16} className="text-gray-600" />
|
||||
</button>
|
||||
|
|
@ -433,10 +433,10 @@ const ModelHubTable: React.FC<ModelHubTableProps> = ({ accessToken, publicPage,
|
|||
{/* Tab System for Model Hub, Agent Hub, MCP Hub, and Plugin Marketplace */}
|
||||
<TabGroup>
|
||||
<TabList className="mb-4">
|
||||
<Tab>Model Hub</Tab>
|
||||
<Tab>Agent Hub</Tab>
|
||||
<Tab>MCP Hub</Tab>
|
||||
<Tab>Skill Hub</Tab>
|
||||
<Tab>{t("aiHub.modelHubTable.tabModelHub")}</Tab>
|
||||
<Tab>{t("aiHub.modelHubTable.tabAgentHub")}</Tab>
|
||||
<Tab>{t("aiHub.modelHubTable.tabMcpHub")}</Tab>
|
||||
<Tab>{t("aiHub.modelHubTable.tabSkillHub")}</Tab>
|
||||
</TabList>
|
||||
|
||||
<TabPanels>
|
||||
|
|
@ -447,7 +447,9 @@ const ModelHubTable: React.FC<ModelHubTableProps> = ({ accessToken, publicPage,
|
|||
{/* Header with Make Public Button */}
|
||||
{publicPage == false && canModify && (
|
||||
<div className="flex justify-end mb-4">
|
||||
<Button onClick={() => handleMakePublicPage()}>Select Models to Make Public</Button>
|
||||
<Button onClick={() => handleMakePublicPage()}>
|
||||
{t("aiHub.modelHubTable.selectModelsPublic")}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
|
@ -465,7 +467,10 @@ const ModelHubTable: React.FC<ModelHubTableProps> = ({ accessToken, publicPage,
|
|||
|
||||
<div className="mt-4 text-center space-y-2">
|
||||
<Text className="text-sm text-gray-600">
|
||||
Showing {filteredData.length} of {modelHubData?.length || 0} models
|
||||
{t("aiHub.modelHubTable.showingModels", {
|
||||
filtered: filteredData.length,
|
||||
total: modelHubData?.length || 0,
|
||||
})}
|
||||
</Text>
|
||||
</div>
|
||||
</TabPanel>
|
||||
|
|
@ -476,13 +481,15 @@ const ModelHubTable: React.FC<ModelHubTableProps> = ({ accessToken, publicPage,
|
|||
{/* Header with Make Public Button */}
|
||||
{publicPage == false && canModify && (
|
||||
<div className="flex justify-end mb-4">
|
||||
<Button onClick={() => handleMakeAgentPublicPage()}>Select Agents to Make Public</Button>
|
||||
<Button onClick={() => handleMakeAgentPublicPage()}>
|
||||
{t("aiHub.modelHubTable.selectAgentsPublic")}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Agent Table */}
|
||||
<ModelDataTable
|
||||
columns={getAgentHubTableColumns(showAgentModal, copyToClipboard, publicPage)}
|
||||
columns={getAgentHubTableColumns(t, showAgentModal, copyToClipboard, publicPage)}
|
||||
data={agentHubData || []}
|
||||
isLoading={agentLoading}
|
||||
defaultSorting={[{ id: "name", desc: false }]}
|
||||
|
|
@ -491,7 +498,7 @@ const ModelHubTable: React.FC<ModelHubTableProps> = ({ accessToken, publicPage,
|
|||
|
||||
<div className="mt-4 text-center space-y-2">
|
||||
<Text className="text-sm text-gray-600">
|
||||
Showing {agentHubData?.length || 0} agent{agentHubData?.length !== 1 ? "s" : ""}
|
||||
{t("aiHub.modelHubTable.showingAgents", { count: agentHubData?.length || 0 })}
|
||||
</Text>
|
||||
</div>
|
||||
</TabPanel>
|
||||
|
|
@ -502,7 +509,9 @@ const ModelHubTable: React.FC<ModelHubTableProps> = ({ accessToken, publicPage,
|
|||
{/* Header with Make Public Button */}
|
||||
{publicPage == false && canModify && (
|
||||
<div className="flex justify-end mb-4">
|
||||
<Button onClick={() => handleMakeMcpPublicPage()}>Select MCP Servers to Make Public</Button>
|
||||
<Button onClick={() => handleMakeMcpPublicPage()}>
|
||||
{t("aiHub.modelHubTable.selectMcpPublic")}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
|
@ -517,7 +526,7 @@ const ModelHubTable: React.FC<ModelHubTableProps> = ({ accessToken, publicPage,
|
|||
|
||||
<div className="mt-4 text-center space-y-2">
|
||||
<Text className="text-sm text-gray-600">
|
||||
Showing {mcpHubData?.length || 0} MCP server{mcpHubData?.length !== 1 ? "s" : ""}
|
||||
{t("aiHub.modelHubTable.showingMcpServers", { count: mcpHubData?.length || 0 })}
|
||||
</Text>
|
||||
</div>
|
||||
</TabPanel>
|
||||
|
|
@ -526,7 +535,9 @@ const ModelHubTable: React.FC<ModelHubTableProps> = ({ accessToken, publicPage,
|
|||
<TabPanel>
|
||||
{publicPage == false && canModify && (
|
||||
<div className="flex justify-end mb-4">
|
||||
<Button onClick={() => setIsMakeSkillPublicModalVisible(true)}>Select Skills to Make Public</Button>
|
||||
<Button onClick={() => setIsMakeSkillPublicModalVisible(true)}>
|
||||
{t("aiHub.modelHubTable.selectSkillsPublic")}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
<SkillHubDashboard
|
||||
|
|
@ -546,14 +557,14 @@ const ModelHubTable: React.FC<ModelHubTableProps> = ({ accessToken, publicPage,
|
|||
</div>
|
||||
) : (
|
||||
<Card className="mx-auto max-w-xl mt-10">
|
||||
<Text className="text-xl text-center mb-2 text-black">Public Model Hub not enabled.</Text>
|
||||
<p className="text-base text-center text-slate-800">Ask your proxy admin to enable this on their Admin UI.</p>
|
||||
<Text className="text-xl text-center mb-2 text-black">{t("aiHub.modelHubTable.publicHubNotEnabled")}</Text>
|
||||
<p className="text-base text-center text-slate-800">{t("aiHub.modelHubTable.askAdminToEnable")}</p>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{/* Public Page Modal */}
|
||||
<Modal
|
||||
title="Public Model Hub"
|
||||
title={t("aiHub.modelHubTable.publicModelHubTitle")}
|
||||
width={600}
|
||||
open={isPublicPageModalVisible}
|
||||
footer={null}
|
||||
|
|
@ -562,20 +573,20 @@ const ModelHubTable: React.FC<ModelHubTableProps> = ({ accessToken, publicPage,
|
|||
>
|
||||
<div className="pt-5 pb-5">
|
||||
<div className="flex justify-between mb-4">
|
||||
<Text className="text-base mr-2">Shareable Link:</Text>
|
||||
<Text className="text-base mr-2">{t("aiHub.modelHubTable.shareableLink")}</Text>
|
||||
<Text className="max-w-sm ml-2 bg-gray-200 pr-2 pl-2 pt-1 pb-1 text-center rounded">
|
||||
{`${getProxyBaseUrl()}/ui/model_hub_table`}
|
||||
</Text>
|
||||
</div>
|
||||
<div className="flex justify-end">
|
||||
<Button onClick={goToPublicModelPage}>See Page</Button>
|
||||
<Button onClick={goToPublicModelPage}>{t("aiHub.modelHubTable.seePage")}</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
|
||||
{/* Model Details Modal */}
|
||||
<Modal
|
||||
title={selectedModel?.model_group || "Model Details"}
|
||||
title={selectedModel?.model_group || t("aiHub.modelHubTable.modelDetails")}
|
||||
width={1000}
|
||||
open={isModalVisible}
|
||||
footer={null}
|
||||
|
|
@ -586,18 +597,18 @@ const ModelHubTable: React.FC<ModelHubTableProps> = ({ accessToken, publicPage,
|
|||
<div className="space-y-6">
|
||||
{/* Model Overview */}
|
||||
<div>
|
||||
<Text className="text-lg font-semibold mb-4">Model Overview</Text>
|
||||
<Text className="text-lg font-semibold mb-4">{t("aiHub.modelHubTable.modelOverview")}</Text>
|
||||
<div className="grid grid-cols-2 gap-4 mb-4">
|
||||
<div>
|
||||
<Text className="font-medium">Model Group:</Text>
|
||||
<Text className="font-medium">{t("aiHub.modelHubTable.fieldModelGroup")}</Text>
|
||||
<Text>{selectedModel.model_group}</Text>
|
||||
</div>
|
||||
<div>
|
||||
<Text className="font-medium">Mode:</Text>
|
||||
<Text>{selectedModel.mode || "Not specified"}</Text>
|
||||
<Text className="font-medium">{t("aiHub.modelHubTable.fieldMode")}</Text>
|
||||
<Text>{selectedModel.mode || t("aiHub.modelHubTable.notSpecified")}</Text>
|
||||
</div>
|
||||
<div>
|
||||
<Text className="font-medium">Providers:</Text>
|
||||
<Text className="font-medium">{t("aiHub.modelHubTable.fieldProviders")}</Text>
|
||||
<div className="flex flex-wrap gap-1 mt-1">
|
||||
{selectedModel.providers.map((provider) => (
|
||||
<Badge key={provider} color="blue">
|
||||
|
|
@ -611,30 +622,34 @@ const ModelHubTable: React.FC<ModelHubTableProps> = ({ accessToken, publicPage,
|
|||
|
||||
{/* Token and Cost Information */}
|
||||
<div>
|
||||
<Text className="text-lg font-semibold mb-4">Token & Cost Information</Text>
|
||||
<Text className="text-lg font-semibold mb-4">{t("aiHub.modelHubTable.tokenCostInfo")}</Text>
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<Text className="font-medium">Max Input Tokens:</Text>
|
||||
<Text>{selectedModel.max_input_tokens?.toLocaleString() || "Not specified"}</Text>
|
||||
</div>
|
||||
<div>
|
||||
<Text className="font-medium">Max Output Tokens:</Text>
|
||||
<Text>{selectedModel.max_output_tokens?.toLocaleString() || "Not specified"}</Text>
|
||||
</div>
|
||||
<div>
|
||||
<Text className="font-medium">Input Cost per 1M Tokens:</Text>
|
||||
<Text className="font-medium">{t("aiHub.modelHubTable.fieldMaxInputTokens")}</Text>
|
||||
<Text>
|
||||
{selectedModel.input_cost_per_token
|
||||
? formatCost(selectedModel.input_cost_per_token)
|
||||
: "Not specified"}
|
||||
{selectedModel.max_input_tokens?.toLocaleString() || t("aiHub.modelHubTable.notSpecified")}
|
||||
</Text>
|
||||
</div>
|
||||
<div>
|
||||
<Text className="font-medium">Output Cost per 1M Tokens:</Text>
|
||||
<Text className="font-medium">{t("aiHub.modelHubTable.fieldMaxOutputTokens")}</Text>
|
||||
<Text>
|
||||
{selectedModel.max_output_tokens?.toLocaleString() || t("aiHub.modelHubTable.notSpecified")}
|
||||
</Text>
|
||||
</div>
|
||||
<div>
|
||||
<Text className="font-medium">{t("aiHub.modelHubTable.fieldInputCost")}</Text>
|
||||
<Text>
|
||||
{selectedModel.input_cost_per_token
|
||||
? formatCost(selectedModel.input_cost_per_token)
|
||||
: t("aiHub.modelHubTable.notSpecified")}
|
||||
</Text>
|
||||
</div>
|
||||
<div>
|
||||
<Text className="font-medium">{t("aiHub.modelHubTable.fieldOutputCost")}</Text>
|
||||
<Text>
|
||||
{selectedModel.output_cost_per_token
|
||||
? formatCost(selectedModel.output_cost_per_token)
|
||||
: "Not specified"}
|
||||
: t("aiHub.modelHubTable.notSpecified")}
|
||||
</Text>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -642,14 +657,14 @@ const ModelHubTable: React.FC<ModelHubTableProps> = ({ accessToken, publicPage,
|
|||
|
||||
{/* Capabilities */}
|
||||
<div>
|
||||
<Text className="text-lg font-semibold mb-4">Capabilities</Text>
|
||||
<Text className="text-lg font-semibold mb-4">{t("aiHub.modelHubTable.sectionCapabilities")}</Text>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{(() => {
|
||||
const capabilities = getModelCapabilities(selectedModel);
|
||||
const colors = ["green", "blue", "purple", "orange", "red", "yellow"];
|
||||
|
||||
if (capabilities.length === 0) {
|
||||
return <Text className="text-gray-500">No special capabilities listed</Text>;
|
||||
return <Text className="text-gray-500">{t("aiHub.modelHubTable.noCapabilities")}</Text>;
|
||||
}
|
||||
|
||||
return capabilities.map((capability, index) => (
|
||||
|
|
@ -664,17 +679,17 @@ const ModelHubTable: React.FC<ModelHubTableProps> = ({ accessToken, publicPage,
|
|||
{/* Rate Limits */}
|
||||
{(selectedModel.tpm || selectedModel.rpm) && (
|
||||
<div>
|
||||
<Text className="text-lg font-semibold mb-4">Rate Limits</Text>
|
||||
<Text className="text-lg font-semibold mb-4">{t("aiHub.modelHubTable.sectionRateLimits")}</Text>
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
{selectedModel.tpm && (
|
||||
<div>
|
||||
<Text className="font-medium">Tokens per Minute:</Text>
|
||||
<Text className="font-medium">{t("aiHub.modelHubTable.fieldTokensPerMinute")}</Text>
|
||||
<Text>{selectedModel.tpm.toLocaleString()}</Text>
|
||||
</div>
|
||||
)}
|
||||
{selectedModel.rpm && (
|
||||
<div>
|
||||
<Text className="font-medium">Requests per Minute:</Text>
|
||||
<Text className="font-medium">{t("aiHub.modelHubTable.fieldRequestsPerMinute")}</Text>
|
||||
<Text>{selectedModel.rpm.toLocaleString()}</Text>
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -685,7 +700,7 @@ const ModelHubTable: React.FC<ModelHubTableProps> = ({ accessToken, publicPage,
|
|||
{/* Supported OpenAI Parameters */}
|
||||
{selectedModel.supported_openai_params && (
|
||||
<div>
|
||||
<Text className="text-lg font-semibold mb-4">Supported OpenAI Parameters</Text>
|
||||
<Text className="text-lg font-semibold mb-4">{t("aiHub.modelHubTable.sectionSupportedParams")}</Text>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{selectedModel.supported_openai_params.map((param) => (
|
||||
<Badge key={param} color="green">
|
||||
|
|
@ -698,7 +713,7 @@ const ModelHubTable: React.FC<ModelHubTableProps> = ({ accessToken, publicPage,
|
|||
|
||||
{/* Usage Example */}
|
||||
<div>
|
||||
<Text className="text-lg font-semibold mb-4">Usage Example</Text>
|
||||
<Text className="text-lg font-semibold mb-4">{t("aiHub.modelHubTable.sectionUsageExample")}</Text>
|
||||
<SyntaxHighlighter language="python" className="text-sm">
|
||||
{`import openai
|
||||
|
||||
|
|
@ -726,7 +741,7 @@ print(response.choices[0].message.content)`}
|
|||
|
||||
{/* Agent Details Modal */}
|
||||
<Modal
|
||||
title={selectedAgent?.name || "Agent Details"}
|
||||
title={selectedAgent?.name || t("aiHub.modelHubTable.agentDetails")}
|
||||
width={1000}
|
||||
open={isAgentModalVisible}
|
||||
footer={null}
|
||||
|
|
@ -737,22 +752,22 @@ print(response.choices[0].message.content)`}
|
|||
<div className="space-y-6">
|
||||
{/* Agent Overview */}
|
||||
<div>
|
||||
<Text className="text-lg font-semibold mb-4">Agent Overview</Text>
|
||||
<Text className="text-lg font-semibold mb-4">{t("aiHub.modelHubTable.agentOverview")}</Text>
|
||||
<div className="grid grid-cols-2 gap-4 mb-4">
|
||||
<div>
|
||||
<Text className="font-medium">Name:</Text>
|
||||
<Text className="font-medium">{t("aiHub.modelHubTable.fieldName")}</Text>
|
||||
<Text>{selectedAgent.name}</Text>
|
||||
</div>
|
||||
<div>
|
||||
<Text className="font-medium">Version:</Text>
|
||||
<Text className="font-medium">{t("aiHub.modelHubTable.fieldVersion")}</Text>
|
||||
<Badge color="blue">v{selectedAgent.version}</Badge>
|
||||
</div>
|
||||
<div>
|
||||
<Text className="font-medium">Protocol Version:</Text>
|
||||
<Text className="font-medium">{t("aiHub.modelHubTable.fieldProtocolVersion")}</Text>
|
||||
<Text>{selectedAgent.protocolVersion}</Text>
|
||||
</div>
|
||||
<div>
|
||||
<Text className="font-medium">URL:</Text>
|
||||
<Text className="font-medium">{t("aiHub.modelHubTable.fieldUrl")}</Text>
|
||||
<div className="flex items-center space-x-2">
|
||||
<Text className="truncate">{selectedAgent.url}</Text>
|
||||
<CopyOutlined
|
||||
|
|
@ -763,7 +778,7 @@ print(response.choices[0].message.content)`}
|
|||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<Text className="font-medium">Description:</Text>
|
||||
<Text className="font-medium">{t("common.description")}:</Text>
|
||||
<Text className="mt-1">{selectedAgent.description}</Text>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -771,7 +786,7 @@ print(response.choices[0].message.content)`}
|
|||
{/* Capabilities */}
|
||||
{selectedAgent.capabilities && Object.keys(selectedAgent.capabilities).length > 0 && (
|
||||
<div>
|
||||
<Text className="text-lg font-semibold mb-4">Capabilities</Text>
|
||||
<Text className="text-lg font-semibold mb-4">{t("aiHub.modelHubTable.sectionCapabilities")}</Text>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{Object.entries(selectedAgent.capabilities)
|
||||
.filter(([_, value]) => value === true)
|
||||
|
|
@ -786,26 +801,26 @@ print(response.choices[0].message.content)`}
|
|||
|
||||
{/* Input/Output Modes */}
|
||||
<div>
|
||||
<Text className="text-lg font-semibold mb-4">Input/Output Modes</Text>
|
||||
<Text className="text-lg font-semibold mb-4">{t("aiHub.modelHubTable.sectionInputOutputModes")}</Text>
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<Text className="font-medium">Input Modes:</Text>
|
||||
<Text className="font-medium">{t("aiHub.modelHubTable.fieldInputModes")}</Text>
|
||||
<div className="flex flex-wrap gap-1 mt-1">
|
||||
{selectedAgent.defaultInputModes?.map((mode) => (
|
||||
<Badge key={mode} color="blue">
|
||||
{mode}
|
||||
</Badge>
|
||||
)) || <Text>Not specified</Text>}
|
||||
)) || <Text>{t("aiHub.modelHubTable.notSpecified")}</Text>}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<Text className="font-medium">Output Modes:</Text>
|
||||
<Text className="font-medium">{t("aiHub.modelHubTable.fieldOutputModes")}</Text>
|
||||
<div className="flex flex-wrap gap-1 mt-1">
|
||||
{selectedAgent.defaultOutputModes?.map((mode) => (
|
||||
<Badge key={mode} color="purple">
|
||||
{mode}
|
||||
</Badge>
|
||||
)) || <Text>Not specified</Text>}
|
||||
)) || <Text>{t("aiHub.modelHubTable.notSpecified")}</Text>}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -814,14 +829,16 @@ print(response.choices[0].message.content)`}
|
|||
{/* Skills */}
|
||||
{selectedAgent.skills && selectedAgent.skills.length > 0 && (
|
||||
<div>
|
||||
<Text className="text-lg font-semibold mb-4">Skills</Text>
|
||||
<Text className="text-lg font-semibold mb-4">{t("aiHub.modelHubTable.sectionSkills")}</Text>
|
||||
<div className="space-y-4">
|
||||
{selectedAgent.skills.map((skill) => (
|
||||
<div key={skill.id} className="border border-gray-200 rounded p-4">
|
||||
<div className="flex justify-between items-start mb-2">
|
||||
<div>
|
||||
<Text className="font-medium text-base">{skill.name}</Text>
|
||||
<Text className="text-xs text-gray-500">ID: {skill.id}</Text>
|
||||
<Text className="text-xs text-gray-500">
|
||||
{t("aiHub.modelHubTable.skillId", { id: skill.id })}
|
||||
</Text>
|
||||
</div>
|
||||
{skill.tags && skill.tags.length > 0 && (
|
||||
<div className="flex flex-wrap gap-1">
|
||||
|
|
@ -836,7 +853,9 @@ print(response.choices[0].message.content)`}
|
|||
<Text className="text-sm mb-2">{skill.description}</Text>
|
||||
{skill.examples && skill.examples.length > 0 && (
|
||||
<div>
|
||||
<Text className="text-xs font-medium text-gray-700">Examples:</Text>
|
||||
<Text className="text-xs font-medium text-gray-700">
|
||||
{t("aiHub.modelHubTable.skillExamples")}
|
||||
</Text>
|
||||
<div className="flex flex-wrap gap-1 mt-1">
|
||||
{skill.examples.map((example, idx) => (
|
||||
<Badge key={idx} color="gray" size="xs">
|
||||
|
|
@ -855,8 +874,8 @@ print(response.choices[0].message.content)`}
|
|||
{/* Additional Properties */}
|
||||
{selectedAgent.supportsAuthenticatedExtendedCard && (
|
||||
<div>
|
||||
<Text className="text-lg font-semibold mb-4">Additional Features</Text>
|
||||
<Badge color="green">Supports Authenticated Extended Card</Badge>
|
||||
<Text className="text-lg font-semibold mb-4">{t("aiHub.modelHubTable.sectionAdditionalFeatures")}</Text>
|
||||
<Badge color="green">{t("aiHub.modelHubTable.supportsAuthCard")}</Badge>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -865,7 +884,7 @@ print(response.choices[0].message.content)`}
|
|||
|
||||
{/* MCP Server Details Modal */}
|
||||
<Modal
|
||||
title={selectedMcpServer?.server_name || "MCP Server Details"}
|
||||
title={selectedMcpServer?.server_name || t("aiHub.modelHubTable.mcpServerDetails")}
|
||||
width={1000}
|
||||
open={isMcpModalVisible}
|
||||
footer={null}
|
||||
|
|
@ -876,14 +895,14 @@ print(response.choices[0].message.content)`}
|
|||
<div className="space-y-6">
|
||||
{/* Server Overview */}
|
||||
<div>
|
||||
<Text className="text-lg font-semibold mb-4">Server Overview</Text>
|
||||
<Text className="text-lg font-semibold mb-4">{t("aiHub.modelHubTable.mcpServerOverview")}</Text>
|
||||
<div className="grid grid-cols-2 gap-4 mb-4">
|
||||
<div>
|
||||
<Text className="font-medium">Server Name:</Text>
|
||||
<Text className="font-medium">{t("aiHub.modelHubTable.fieldServerName")}</Text>
|
||||
<Text>{selectedMcpServer.server_name}</Text>
|
||||
</div>
|
||||
<div>
|
||||
<Text className="font-medium">Server ID:</Text>
|
||||
<Text className="font-medium">{t("aiHub.modelHubTable.fieldServerId")}</Text>
|
||||
<div className="flex items-center space-x-2">
|
||||
<Text className="text-xs truncate">{selectedMcpServer.server_id}</Text>
|
||||
<CopyOutlined
|
||||
|
|
@ -894,22 +913,22 @@ print(response.choices[0].message.content)`}
|
|||
</div>
|
||||
{selectedMcpServer.alias && (
|
||||
<div>
|
||||
<Text className="font-medium">Alias:</Text>
|
||||
<Text className="font-medium">{t("aiHub.modelHubTable.fieldAlias")}</Text>
|
||||
<Text>{selectedMcpServer.alias}</Text>
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
<Text className="font-medium">Transport:</Text>
|
||||
<Text className="font-medium">{t("aiHub.modelHubTable.fieldTransport")}</Text>
|
||||
<Badge color="blue">{selectedMcpServer.transport}</Badge>
|
||||
</div>
|
||||
<div>
|
||||
<Text className="font-medium">Auth Type:</Text>
|
||||
<Text className="font-medium">{t("aiHub.modelHubTable.fieldAuthType")}</Text>
|
||||
<Badge color={selectedMcpServer.auth_type === "none" ? "gray" : "green"}>
|
||||
{selectedMcpServer.auth_type}
|
||||
</Badge>
|
||||
</div>
|
||||
<div>
|
||||
<Text className="font-medium">Status:</Text>
|
||||
<Text className="font-medium">{t("common.status")}:</Text>
|
||||
<Badge
|
||||
color={
|
||||
selectedMcpServer.status === "active" || selectedMcpServer.status === "healthy"
|
||||
|
|
@ -919,13 +938,13 @@ print(response.choices[0].message.content)`}
|
|||
: "gray"
|
||||
}
|
||||
>
|
||||
{selectedMcpServer.status || "unknown"}
|
||||
{selectedMcpServer.status || t("common.unknown")}
|
||||
</Badge>
|
||||
</div>
|
||||
</div>
|
||||
{selectedMcpServer.description && (
|
||||
<div className="mt-2">
|
||||
<Text className="font-medium">Description:</Text>
|
||||
<Text className="font-medium">{t("common.description")}:</Text>
|
||||
<Text className="mt-1">{selectedMcpServer.description}</Text>
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -933,10 +952,10 @@ print(response.choices[0].message.content)`}
|
|||
|
||||
{/* Connection Details */}
|
||||
<div>
|
||||
<Text className="text-lg font-semibold mb-4">Connection Details</Text>
|
||||
<Text className="text-lg font-semibold mb-4">{t("aiHub.modelHubTable.sectionConnectionDetails")}</Text>
|
||||
<div className="space-y-2">
|
||||
<div>
|
||||
<Text className="font-medium">URL:</Text>
|
||||
<Text className="font-medium">{t("aiHub.modelHubTable.fieldUrl")}</Text>
|
||||
<div className="flex items-center space-x-2 mt-1">
|
||||
<Text className="text-sm break-all bg-gray-100 p-2 rounded flex-1">{selectedMcpServer.url}</Text>
|
||||
<CopyOutlined
|
||||
|
|
@ -947,7 +966,7 @@ print(response.choices[0].message.content)`}
|
|||
</div>
|
||||
{selectedMcpServer.command && (
|
||||
<div>
|
||||
<Text className="font-medium">Command:</Text>
|
||||
<Text className="font-medium">{t("aiHub.modelHubTable.fieldCommand")}</Text>
|
||||
<Text className="text-sm bg-gray-100 p-2 rounded mt-1 font-mono">{selectedMcpServer.command}</Text>
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -957,7 +976,7 @@ print(response.choices[0].message.content)`}
|
|||
{/* Tools */}
|
||||
{selectedMcpServer.allowed_tools && selectedMcpServer.allowed_tools.length > 0 && (
|
||||
<div>
|
||||
<Text className="text-lg font-semibold mb-4">Allowed Tools</Text>
|
||||
<Text className="text-lg font-semibold mb-4">{t("aiHub.modelHubTable.sectionAllowedTools")}</Text>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{selectedMcpServer.allowed_tools.map((tool, idx) => (
|
||||
<Badge key={idx} color="purple">
|
||||
|
|
@ -971,7 +990,7 @@ print(response.choices[0].message.content)`}
|
|||
{/* Teams */}
|
||||
{selectedMcpServer.teams && selectedMcpServer.teams.length > 0 && (
|
||||
<div>
|
||||
<Text className="text-lg font-semibold mb-4">Teams</Text>
|
||||
<Text className="text-lg font-semibold mb-4">{t("aiHub.modelHubTable.sectionTeams")}</Text>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{selectedMcpServer.teams.map((team, idx) => (
|
||||
<Badge key={idx} color="blue">
|
||||
|
|
@ -985,7 +1004,7 @@ print(response.choices[0].message.content)`}
|
|||
{/* Access Groups */}
|
||||
{selectedMcpServer.mcp_access_groups && selectedMcpServer.mcp_access_groups.length > 0 && (
|
||||
<div>
|
||||
<Text className="text-lg font-semibold mb-4">Access Groups</Text>
|
||||
<Text className="text-lg font-semibold mb-4">{t("aiHub.modelHubTable.sectionAccessGroups")}</Text>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{selectedMcpServer.mcp_access_groups.map((group, idx) => (
|
||||
<Badge key={idx} color="green">
|
||||
|
|
@ -998,34 +1017,34 @@ print(response.choices[0].message.content)`}
|
|||
|
||||
{/* Metadata */}
|
||||
<div>
|
||||
<Text className="text-lg font-semibold mb-4">Metadata</Text>
|
||||
<Text className="text-lg font-semibold mb-4">{t("aiHub.modelHubTable.sectionMetadata")}</Text>
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<Text className="font-medium">Created By:</Text>
|
||||
<Text className="font-medium">{t("aiHub.modelHubTable.fieldCreatedBy")}</Text>
|
||||
<Text>{selectedMcpServer.created_by}</Text>
|
||||
</div>
|
||||
<div>
|
||||
<Text className="font-medium">Updated By:</Text>
|
||||
<Text className="font-medium">{t("aiHub.modelHubTable.fieldUpdatedBy")}</Text>
|
||||
<Text>{selectedMcpServer.updated_by}</Text>
|
||||
</div>
|
||||
<div>
|
||||
<Text className="font-medium">Created At:</Text>
|
||||
<Text className="font-medium">{t("common.createdAt")}:</Text>
|
||||
<Text className="text-sm">{new Date(selectedMcpServer.created_at).toLocaleString()}</Text>
|
||||
</div>
|
||||
<div>
|
||||
<Text className="font-medium">Updated At:</Text>
|
||||
<Text className="font-medium">{t("common.updatedAt")}:</Text>
|
||||
<Text className="text-sm">{new Date(selectedMcpServer.updated_at).toLocaleString()}</Text>
|
||||
</div>
|
||||
{selectedMcpServer.last_health_check && (
|
||||
<div>
|
||||
<Text className="font-medium">Last Health Check:</Text>
|
||||
<Text className="font-medium">{t("aiHub.modelHubTable.fieldLastHealthCheck")}</Text>
|
||||
<Text className="text-sm">{new Date(selectedMcpServer.last_health_check).toLocaleString()}</Text>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{selectedMcpServer.health_check_error && (
|
||||
<div className="mt-2 p-2 bg-red-50 rounded">
|
||||
<Text className="font-medium text-red-700">Health Check Error:</Text>
|
||||
<Text className="font-medium text-red-700">{t("aiHub.modelHubTable.fieldHealthCheckError")}</Text>
|
||||
<Text className="text-sm text-red-600 mt-1">{selectedMcpServer.health_check_error}</Text>
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -1033,7 +1052,7 @@ print(response.choices[0].message.content)`}
|
|||
|
||||
{/* Usage Example */}
|
||||
<div>
|
||||
<Text className="text-lg font-semibold mb-4">Usage Example</Text>
|
||||
<Text className="text-lg font-semibold mb-4">{t("aiHub.modelHubTable.sectionUsageExample")}</Text>
|
||||
<SyntaxHighlighter language="python" className="text-sm">
|
||||
{`from fastmcp import Client
|
||||
import asyncio
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ const SkillHubDashboard: React.FC<SkillHubDashboardProps> = ({
|
|||
}
|
||||
|
||||
if (isLoading) {
|
||||
return <div className="text-center py-16 text-gray-400">Loading skills...</div>;
|
||||
return <div className="text-center py-16 text-gray-400">{t("aiHub.skillHubDashboard.loadingSkills")}</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
@ -80,15 +80,15 @@ const SkillHubDashboard: React.FC<SkillHubDashboardProps> = ({
|
|||
{/* Stats row */}
|
||||
<div className="grid grid-cols-3 gap-4">
|
||||
<div className="border border-gray-200 rounded-lg p-4">
|
||||
<div className="text-xs text-gray-500 mb-1">Total Skills</div>
|
||||
<div className="text-xs text-gray-500 mb-1">{t("aiHub.skillHubDashboard.statTotalSkills")}</div>
|
||||
<div className="text-2xl font-semibold text-gray-900">{totalSkills}</div>
|
||||
</div>
|
||||
<div className="border border-gray-200 rounded-lg p-4">
|
||||
<div className="text-xs text-gray-500 mb-1">Namespaces</div>
|
||||
<div className="text-xs text-gray-500 mb-1">{t("aiHub.skillHubDashboard.statNamespaces")}</div>
|
||||
<div className="text-2xl font-semibold text-gray-900">{namespaces.length}</div>
|
||||
</div>
|
||||
<div className="border border-gray-200 rounded-lg p-4">
|
||||
<div className="text-xs text-gray-500 mb-1">Domains</div>
|
||||
<div className="text-xs text-gray-500 mb-1">{t("aiHub.skillHubDashboard.statDomains")}</div>
|
||||
<div className="text-2xl font-semibold text-gray-900">{domains.length}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -96,10 +96,12 @@ const SkillHubDashboard: React.FC<SkillHubDashboardProps> = ({
|
|||
{/* Search + filters + table */}
|
||||
<div>
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<h3 className="text-sm font-semibold text-gray-700">All {publicPage ? "Public " : ""}Skills</h3>
|
||||
<h3 className="text-sm font-semibold text-gray-700">
|
||||
{publicPage ? t("aiHub.skillHubDashboard.allPublicSkills") : t("aiHub.skillHubDashboard.allSkills")}
|
||||
</h3>
|
||||
<div className="flex items-center gap-2">
|
||||
<Select
|
||||
placeholder="All Domains"
|
||||
placeholder={t("aiHub.skillHubDashboard.allDomainsPlaceholder")}
|
||||
allowClear
|
||||
value={domainFilter}
|
||||
onChange={(val) => setDomainFilter(val)}
|
||||
|
|
@ -108,7 +110,7 @@ const SkillHubDashboard: React.FC<SkillHubDashboardProps> = ({
|
|||
/>
|
||||
<Input
|
||||
prefix={<SearchOutlined className="text-gray-400" />}
|
||||
placeholder="Search by name, namespace, or tag…"
|
||||
placeholder={t("aiHub.skillHubDashboard.searchPlaceholder")}
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.target.value)}
|
||||
style={{ width: 280 }}
|
||||
|
|
@ -124,7 +126,7 @@ const SkillHubDashboard: React.FC<SkillHubDashboardProps> = ({
|
|||
/>
|
||||
<div className="mt-3 text-center">
|
||||
<Text className="text-sm text-gray-500">
|
||||
Showing {filteredSkills.length} of {totalSkills} skill{totalSkills !== 1 ? "s" : ""}
|
||||
{t("aiHub.skillHubDashboard.showing", { filtered: filteredSkills.length, count: totalSkills })}
|
||||
</Text>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -6490,5 +6490,109 @@
|
|||
"saveSuccess": "Permissions updated successfully",
|
||||
"saveFailed": "Failed to update permissions"
|
||||
}
|
||||
},
|
||||
"aiHub": {
|
||||
"agentHubTableColumns": {
|
||||
"colAgentName": "Agent Name",
|
||||
"copyAgentName": "Copy agent name",
|
||||
"colVersion": "Version",
|
||||
"colProtocol": "Protocol",
|
||||
"colSkills": "Skills",
|
||||
"skillCount_one": "{{count}} skill",
|
||||
"skillCount_other": "{{count}} skills",
|
||||
"colCapabilities": "Capabilities",
|
||||
"colIOModes": "I/O Modes",
|
||||
"ioIn": "In:",
|
||||
"ioOut": "Out:",
|
||||
"colPublic": "Public",
|
||||
"detailsShort": "Info"
|
||||
},
|
||||
"modelHubTable": {
|
||||
"title": "AI Hub",
|
||||
"adminDescription": "Make models, agents, and MCP servers public for developers to know what's available.",
|
||||
"userDescription": "A list of all public model names personally available to you.",
|
||||
"modelHubUrl": "Model Hub URL:",
|
||||
"copyUrl": "Copy URL",
|
||||
"copiedToClipboard": "Copied to clipboard!",
|
||||
"tabModelHub": "Model Hub",
|
||||
"tabAgentHub": "Agent Hub",
|
||||
"tabMcpHub": "MCP Hub",
|
||||
"tabSkillHub": "Skill Hub",
|
||||
"selectModelsPublic": "Select Models to Make Public",
|
||||
"selectAgentsPublic": "Select Agents to Make Public",
|
||||
"selectMcpPublic": "Select MCP Servers to Make Public",
|
||||
"selectSkillsPublic": "Select Skills to Make Public",
|
||||
"showingModels": "Showing {{filtered}} of {{total}} models",
|
||||
"showingAgents_one": "Showing {{count}} agent",
|
||||
"showingAgents_other": "Showing {{count}} agents",
|
||||
"showingMcpServers_one": "Showing {{count}} MCP server",
|
||||
"showingMcpServers_other": "Showing {{count}} MCP servers",
|
||||
"publicHubNotEnabled": "Public Model Hub not enabled.",
|
||||
"askAdminToEnable": "Ask your proxy admin to enable this on their Admin UI.",
|
||||
"publicModelHubTitle": "Public Model Hub",
|
||||
"shareableLink": "Shareable Link:",
|
||||
"seePage": "See Page",
|
||||
"modelDetails": "Model Details",
|
||||
"modelOverview": "Model Overview",
|
||||
"fieldModelGroup": "Model Group:",
|
||||
"fieldMode": "Mode:",
|
||||
"fieldProviders": "Providers:",
|
||||
"fieldMaxInputTokens": "Max Input Tokens:",
|
||||
"fieldMaxOutputTokens": "Max Output Tokens:",
|
||||
"fieldInputCost": "Input Cost per 1M Tokens:",
|
||||
"fieldOutputCost": "Output Cost per 1M Tokens:",
|
||||
"notSpecified": "Not specified",
|
||||
"tokenCostInfo": "Token & Cost Information",
|
||||
"sectionCapabilities": "Capabilities",
|
||||
"noCapabilities": "No special capabilities listed",
|
||||
"sectionRateLimits": "Rate Limits",
|
||||
"fieldTokensPerMinute": "Tokens per Minute:",
|
||||
"fieldRequestsPerMinute": "Requests per Minute:",
|
||||
"sectionSupportedParams": "Supported OpenAI Parameters",
|
||||
"sectionUsageExample": "Usage Example",
|
||||
"agentDetails": "Agent Details",
|
||||
"agentOverview": "Agent Overview",
|
||||
"fieldName": "Name:",
|
||||
"fieldVersion": "Version:",
|
||||
"fieldProtocolVersion": "Protocol Version:",
|
||||
"fieldUrl": "URL:",
|
||||
"sectionInputOutputModes": "Input/Output Modes",
|
||||
"fieldInputModes": "Input Modes:",
|
||||
"fieldOutputModes": "Output Modes:",
|
||||
"sectionSkills": "Skills",
|
||||
"skillId": "ID: {{id}}",
|
||||
"skillExamples": "Examples:",
|
||||
"sectionAdditionalFeatures": "Additional Features",
|
||||
"supportsAuthCard": "Supports Authenticated Extended Card",
|
||||
"mcpServerDetails": "MCP Server Details",
|
||||
"mcpServerOverview": "Server Overview",
|
||||
"fieldServerName": "Server Name:",
|
||||
"fieldServerId": "Server ID:",
|
||||
"fieldAlias": "Alias:",
|
||||
"fieldTransport": "Transport:",
|
||||
"fieldAuthType": "Auth Type:",
|
||||
"sectionConnectionDetails": "Connection Details",
|
||||
"fieldCommand": "Command:",
|
||||
"sectionAllowedTools": "Allowed Tools",
|
||||
"sectionTeams": "Teams",
|
||||
"sectionAccessGroups": "Access Groups",
|
||||
"sectionMetadata": "Metadata",
|
||||
"fieldCreatedBy": "Created By:",
|
||||
"fieldUpdatedBy": "Updated By:",
|
||||
"fieldLastHealthCheck": "Last Health Check:",
|
||||
"fieldHealthCheckError": "Health Check Error:"
|
||||
},
|
||||
"skillHubDashboard": {
|
||||
"loadingSkills": "Loading skills...",
|
||||
"statTotalSkills": "Total Skills",
|
||||
"statNamespaces": "Namespaces",
|
||||
"statDomains": "Domains",
|
||||
"allSkills": "All Skills",
|
||||
"allPublicSkills": "All Public Skills",
|
||||
"allDomainsPlaceholder": "All Domains",
|
||||
"searchPlaceholder": "Search by name, namespace, or tag…",
|
||||
"showing_one": "Showing {{filtered}} of {{count}} skill",
|
||||
"showing_other": "Showing {{filtered}} of {{count}} skills"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6490,5 +6490,109 @@
|
|||
"saveSuccess": "权限更新成功",
|
||||
"saveFailed": "更新权限失败"
|
||||
}
|
||||
},
|
||||
"aiHub": {
|
||||
"agentHubTableColumns": {
|
||||
"colAgentName": "Agent 名称",
|
||||
"copyAgentName": "复制 Agent 名称",
|
||||
"colVersion": "版本",
|
||||
"colProtocol": "协议",
|
||||
"colSkills": "技能",
|
||||
"skillCount_one": "{{count}} 个技能",
|
||||
"skillCount_other": "{{count}} 个技能",
|
||||
"colCapabilities": "能力",
|
||||
"colIOModes": "输入/输出模式",
|
||||
"ioIn": "输入:",
|
||||
"ioOut": "输出:",
|
||||
"colPublic": "公开",
|
||||
"detailsShort": "详情"
|
||||
},
|
||||
"modelHubTable": {
|
||||
"title": "AI Hub",
|
||||
"adminDescription": "将模型、Agent 和 MCP 服务器设为公开,让开发者了解可用资源。",
|
||||
"userDescription": "您个人可用的所有公开模型名称列表。",
|
||||
"modelHubUrl": "模型 Hub URL:",
|
||||
"copyUrl": "复制 URL",
|
||||
"copiedToClipboard": "已复制到剪贴板!",
|
||||
"tabModelHub": "模型 Hub",
|
||||
"tabAgentHub": "Agent Hub",
|
||||
"tabMcpHub": "MCP Hub",
|
||||
"tabSkillHub": "技能 Hub",
|
||||
"selectModelsPublic": "选择要公开的模型",
|
||||
"selectAgentsPublic": "选择要公开的 Agent",
|
||||
"selectMcpPublic": "选择要公开的 MCP 服务器",
|
||||
"selectSkillsPublic": "选择要公开的技能",
|
||||
"showingModels": "显示 {{filtered}} / {{total}} 个模型",
|
||||
"showingAgents_one": "显示 {{count}} 个 Agent",
|
||||
"showingAgents_other": "显示 {{count}} 个 Agent",
|
||||
"showingMcpServers_one": "显示 {{count}} 个 MCP 服务器",
|
||||
"showingMcpServers_other": "显示 {{count}} 个 MCP 服务器",
|
||||
"publicHubNotEnabled": "公开模型 Hub 未启用。",
|
||||
"askAdminToEnable": "请联系代理管理员在管理界面中启用此功能。",
|
||||
"publicModelHubTitle": "公开模型 Hub",
|
||||
"shareableLink": "可分享链接:",
|
||||
"seePage": "查看页面",
|
||||
"modelDetails": "模型详情",
|
||||
"modelOverview": "模型概览",
|
||||
"fieldModelGroup": "模型组:",
|
||||
"fieldMode": "模式:",
|
||||
"fieldProviders": "提供商:",
|
||||
"fieldMaxInputTokens": "最大输入 Token 数:",
|
||||
"fieldMaxOutputTokens": "最大输出 Token 数:",
|
||||
"fieldInputCost": "每百万 Token 输入费用:",
|
||||
"fieldOutputCost": "每百万 Token 输出费用:",
|
||||
"notSpecified": "未指定",
|
||||
"tokenCostInfo": "Token 与费用信息",
|
||||
"sectionCapabilities": "能力",
|
||||
"noCapabilities": "暂无特殊能力说明",
|
||||
"sectionRateLimits": "速率限制",
|
||||
"fieldTokensPerMinute": "每分钟 Token 数:",
|
||||
"fieldRequestsPerMinute": "每分钟请求数:",
|
||||
"sectionSupportedParams": "支持的 OpenAI 参数",
|
||||
"sectionUsageExample": "使用示例",
|
||||
"agentDetails": "Agent 详情",
|
||||
"agentOverview": "Agent 概览",
|
||||
"fieldName": "名称:",
|
||||
"fieldVersion": "版本:",
|
||||
"fieldProtocolVersion": "协议版本:",
|
||||
"fieldUrl": "URL:",
|
||||
"sectionInputOutputModes": "输入/输出模式",
|
||||
"fieldInputModes": "输入模式:",
|
||||
"fieldOutputModes": "输出模式:",
|
||||
"sectionSkills": "技能",
|
||||
"skillId": "ID:{{id}}",
|
||||
"skillExamples": "示例:",
|
||||
"sectionAdditionalFeatures": "附加功能",
|
||||
"supportsAuthCard": "支持已认证扩展卡片",
|
||||
"mcpServerDetails": "MCP 服务器详情",
|
||||
"mcpServerOverview": "服务器概览",
|
||||
"fieldServerName": "服务器名称:",
|
||||
"fieldServerId": "服务器 ID:",
|
||||
"fieldAlias": "别名:",
|
||||
"fieldTransport": "传输方式:",
|
||||
"fieldAuthType": "认证类型:",
|
||||
"sectionConnectionDetails": "连接详情",
|
||||
"fieldCommand": "命令:",
|
||||
"sectionAllowedTools": "允许的工具",
|
||||
"sectionTeams": "团队",
|
||||
"sectionAccessGroups": "访问组",
|
||||
"sectionMetadata": "元数据",
|
||||
"fieldCreatedBy": "创建人:",
|
||||
"fieldUpdatedBy": "更新人:",
|
||||
"fieldLastHealthCheck": "最后健康检查:",
|
||||
"fieldHealthCheckError": "健康检查错误:"
|
||||
},
|
||||
"skillHubDashboard": {
|
||||
"loadingSkills": "正在加载技能...",
|
||||
"statTotalSkills": "技能总数",
|
||||
"statNamespaces": "命名空间",
|
||||
"statDomains": "域",
|
||||
"allSkills": "全部技能",
|
||||
"allPublicSkills": "全部公开技能",
|
||||
"allDomainsPlaceholder": "所有域",
|
||||
"searchPlaceholder": "按名称、命名空间或标签搜索…",
|
||||
"showing_one": "显示 {{filtered}} / {{count}} 个技能",
|
||||
"showing_other": "显示 {{filtered}} / {{count}} 个技能"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue