mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-04 02:31:27 +00:00
feat(ui): AIHub/AgentHubTableColumns to shadcn
- @tremor Button/Badge/Text + antd Tooltip/Tag + @ant-design Copy/ InfoCircle \u2192 shadcn Button/Badge/Tooltip + lucide Copy/Info. - Categorical badge palette: blue (version), emerald (public/ capabilities), purple (skills), gray (fallback). Added to overrides. Gates: TS \u2713 | Build \u2713. Co-authored-by: yuneng-jiang <yuneng-berri@users.noreply.github.com>
This commit is contained in:
parent
10868011ac
commit
e959382ec2
2 changed files with 215 additions and 190 deletions
|
|
@ -69,6 +69,7 @@
|
|||
"src/components/CostTrackingSettings/how_it_works.tsx",
|
||||
"src/components/Settings/AdminSettings/UISettings/PageVisibilitySettings.tsx",
|
||||
"src/components/AIHub/marketplace_table_columns.tsx",
|
||||
"src/components/AIHub/AgentHubTableColumns.tsx",
|
||||
"src/components/UsagePage/components/EntityUsage/SpendByProvider.tsx",
|
||||
"src/components/view_logs/ToolsSection/FormattedToolView.tsx",
|
||||
"src/components/Settings/LoggingAndAlerts/LoggingCallbacks/LoggingCallbacksTable.tsx",
|
||||
|
|
|
|||
|
|
@ -1,7 +1,14 @@
|
|||
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 { Badge } from "@/components/ui/badge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipProvider,
|
||||
TooltipTrigger,
|
||||
} from "@/components/ui/tooltip";
|
||||
import { Copy, Info } from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
export interface AgentHubData {
|
||||
agent_id?: string;
|
||||
|
|
@ -12,6 +19,7 @@ export interface AgentHubData {
|
|||
version: string;
|
||||
capabilities?: {
|
||||
streaming?: boolean;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
[key: string]: any;
|
||||
};
|
||||
defaultInputModes?: string[];
|
||||
|
|
@ -25,207 +33,223 @@ export interface AgentHubData {
|
|||
}>;
|
||||
supportsAuthenticatedExtendedCard?: boolean;
|
||||
is_public?: boolean;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
const badgeClass = {
|
||||
blue: "bg-blue-100 text-blue-700 dark:bg-blue-950 dark:text-blue-300",
|
||||
emerald:
|
||||
"bg-emerald-100 text-emerald-700 dark:bg-emerald-950 dark:text-emerald-300",
|
||||
purple:
|
||||
"bg-purple-100 text-purple-700 dark:bg-purple-950 dark:text-purple-300",
|
||||
gray: "bg-muted text-muted-foreground",
|
||||
};
|
||||
|
||||
export const getAgentHubTableColumns = (
|
||||
showModal: (agent: AgentHubData) => void,
|
||||
copyToClipboard: (text: string) => void,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
publicPage: boolean = false,
|
||||
): ColumnDef<AgentHubData>[] => {
|
||||
const allColumns: ColumnDef<AgentHubData>[] = [
|
||||
{
|
||||
header: "Agent Name",
|
||||
accessorKey: "name",
|
||||
enableSorting: true,
|
||||
sortingFn: "alphanumeric",
|
||||
cell: ({ row }) => {
|
||||
const agent = row.original;
|
||||
|
||||
return (
|
||||
<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">
|
||||
<CopyOutlined
|
||||
onClick={() => copyToClipboard(agent.name)}
|
||||
className="cursor-pointer text-gray-500 hover:text-blue-500 text-xs"
|
||||
/>
|
||||
): ColumnDef<AgentHubData>[] => [
|
||||
{
|
||||
header: "Agent Name",
|
||||
accessorKey: "name",
|
||||
enableSorting: true,
|
||||
sortingFn: "alphanumeric",
|
||||
cell: ({ row }) => {
|
||||
const agent = row.original;
|
||||
return (
|
||||
<div className="space-y-1">
|
||||
<div className="flex items-center space-x-2">
|
||||
<span className="font-medium text-sm">{agent.name}</span>
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => copyToClipboard(agent.name)}
|
||||
className="cursor-pointer text-muted-foreground hover:text-primary"
|
||||
aria-label="Copy agent name"
|
||||
>
|
||||
<Copy className="h-3 w-3" />
|
||||
</button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>Copy agent name</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
{/* Show description on mobile */}
|
||||
<div className="md:hidden">
|
||||
<Text className="text-xs text-gray-600">{agent.description}</Text>
|
||||
</div>
|
||||
</TooltipProvider>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
header: "Description",
|
||||
accessorKey: "description",
|
||||
enableSorting: true,
|
||||
sortingFn: "alphanumeric",
|
||||
cell: ({ row }) => {
|
||||
const agent = row.original;
|
||||
|
||||
return <Text className="text-xs line-clamp-2">{agent.description || "-"}</Text>;
|
||||
},
|
||||
meta: {
|
||||
className: "hidden md:table-cell",
|
||||
},
|
||||
},
|
||||
{
|
||||
header: "Version",
|
||||
accessorKey: "version",
|
||||
enableSorting: true,
|
||||
sortingFn: "alphanumeric",
|
||||
cell: ({ row }) => {
|
||||
const agent = row.original;
|
||||
|
||||
return (
|
||||
<Badge color="blue" size="sm">
|
||||
v{agent.version}
|
||||
</Badge>
|
||||
);
|
||||
},
|
||||
meta: {
|
||||
className: "hidden lg:table-cell",
|
||||
},
|
||||
},
|
||||
{
|
||||
header: "Protocol",
|
||||
accessorKey: "protocolVersion",
|
||||
enableSorting: true,
|
||||
sortingFn: "alphanumeric",
|
||||
cell: ({ row }) => {
|
||||
const agent = row.original;
|
||||
|
||||
return <Text className="text-xs">{agent.protocolVersion || "-"}</Text>;
|
||||
},
|
||||
meta: {
|
||||
className: "hidden lg:table-cell",
|
||||
},
|
||||
},
|
||||
{
|
||||
header: "Skills",
|
||||
accessorKey: "skills",
|
||||
enableSorting: false,
|
||||
cell: ({ row }) => {
|
||||
const agent = row.original;
|
||||
const skills = agent.skills || [];
|
||||
|
||||
return (
|
||||
<div className="space-y-1">
|
||||
<Text className="text-xs font-medium">
|
||||
{skills.length} skill{skills.length !== 1 ? "s" : ""}
|
||||
</Text>
|
||||
{skills.length > 0 && (
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{skills.slice(0, 2).map((skill) => (
|
||||
<Tag key={skill.id} color="purple" className="text-xs">
|
||||
{skill.name}
|
||||
</Tag>
|
||||
))}
|
||||
{skills.length > 2 && <Text className="text-xs text-gray-500">+{skills.length - 2}</Text>}
|
||||
</div>
|
||||
)}
|
||||
<div className="md:hidden">
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{agent.description}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
</div>
|
||||
);
|
||||
},
|
||||
{
|
||||
header: "Capabilities",
|
||||
accessorKey: "capabilities",
|
||||
enableSorting: false,
|
||||
cell: ({ row }) => {
|
||||
const agent = row.original;
|
||||
const capabilities = agent.capabilities || {};
|
||||
const capabilityList = Object.entries(capabilities)
|
||||
.filter(([_, value]) => value === true)
|
||||
.map(([key]) => key);
|
||||
|
||||
return (
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{capabilityList.length === 0 ? (
|
||||
<Text className="text-gray-500 text-xs">-</Text>
|
||||
) : (
|
||||
capabilityList.map((capability) => (
|
||||
<Badge key={capability} color="green" size="xs">
|
||||
{capability}
|
||||
},
|
||||
{
|
||||
header: "Description",
|
||||
accessorKey: "description",
|
||||
enableSorting: true,
|
||||
sortingFn: "alphanumeric",
|
||||
cell: ({ row }) => (
|
||||
<p className="text-xs line-clamp-2">{row.original.description || "-"}</p>
|
||||
),
|
||||
meta: {
|
||||
className: "hidden md:table-cell",
|
||||
},
|
||||
},
|
||||
{
|
||||
header: "Version",
|
||||
accessorKey: "version",
|
||||
enableSorting: true,
|
||||
sortingFn: "alphanumeric",
|
||||
cell: ({ row }) => (
|
||||
<Badge className={cn("text-xs", badgeClass.blue)}>
|
||||
v{row.original.version}
|
||||
</Badge>
|
||||
),
|
||||
meta: {
|
||||
className: "hidden lg:table-cell",
|
||||
},
|
||||
},
|
||||
{
|
||||
header: "Protocol",
|
||||
accessorKey: "protocolVersion",
|
||||
enableSorting: true,
|
||||
sortingFn: "alphanumeric",
|
||||
cell: ({ row }) => (
|
||||
<span className="text-xs">{row.original.protocolVersion || "-"}</span>
|
||||
),
|
||||
meta: {
|
||||
className: "hidden lg:table-cell",
|
||||
},
|
||||
},
|
||||
{
|
||||
header: "Skills",
|
||||
accessorKey: "skills",
|
||||
enableSorting: false,
|
||||
cell: ({ row }) => {
|
||||
const agent = row.original;
|
||||
const skills = agent.skills || [];
|
||||
return (
|
||||
<div className="space-y-1">
|
||||
<span className="text-xs font-medium">
|
||||
{skills.length} skill{skills.length !== 1 ? "s" : ""}
|
||||
</span>
|
||||
{skills.length > 0 && (
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{skills.slice(0, 2).map((skill) => (
|
||||
<Badge
|
||||
key={skill.id}
|
||||
className={cn("text-xs", badgeClass.purple)}
|
||||
>
|
||||
{skill.name}
|
||||
</Badge>
|
||||
))
|
||||
)}
|
||||
))}
|
||||
{skills.length > 2 && (
|
||||
<span className="text-xs text-muted-foreground">
|
||||
+{skills.length - 2}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
header: "Capabilities",
|
||||
accessorKey: "capabilities",
|
||||
enableSorting: false,
|
||||
cell: ({ row }) => {
|
||||
const agent = row.original;
|
||||
const capabilities = agent.capabilities || {};
|
||||
const capabilityList = Object.entries(capabilities)
|
||||
.filter(([, value]) => value === true)
|
||||
.map(([key]) => key);
|
||||
|
||||
return (
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{capabilityList.length === 0 ? (
|
||||
<span className="text-muted-foreground text-xs">-</span>
|
||||
) : (
|
||||
capabilityList.map((capability) => (
|
||||
<Badge
|
||||
key={capability}
|
||||
className={cn("text-xs", badgeClass.emerald)}
|
||||
>
|
||||
{capability}
|
||||
</Badge>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
header: "I/O Modes",
|
||||
accessorKey: "defaultInputModes",
|
||||
enableSorting: false,
|
||||
cell: ({ row }) => {
|
||||
const agent = row.original;
|
||||
const inputModes = agent.defaultInputModes || [];
|
||||
const outputModes = agent.defaultOutputModes || [];
|
||||
|
||||
return (
|
||||
<div className="space-y-1">
|
||||
<div className="text-xs">
|
||||
<span className="font-medium">In:</span>{" "}
|
||||
{inputModes.join(", ") || "-"}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
header: "I/O Modes",
|
||||
accessorKey: "defaultInputModes",
|
||||
enableSorting: false,
|
||||
cell: ({ row }) => {
|
||||
const agent = row.original;
|
||||
const inputModes = agent.defaultInputModes || [];
|
||||
const outputModes = agent.defaultOutputModes || [];
|
||||
|
||||
return (
|
||||
<div className="space-y-1">
|
||||
<Text className="text-xs">
|
||||
<span className="font-medium">In:</span> {inputModes.join(", ") || "-"}
|
||||
</Text>
|
||||
<Text className="text-xs">
|
||||
<span className="font-medium">Out:</span> {outputModes.join(", ") || "-"}
|
||||
</Text>
|
||||
<div className="text-xs">
|
||||
<span className="font-medium">Out:</span>{" "}
|
||||
{outputModes.join(", ") || "-"}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
meta: {
|
||||
className: "hidden xl:table-cell",
|
||||
},
|
||||
</div>
|
||||
);
|
||||
},
|
||||
{
|
||||
header: "Public",
|
||||
accessorKey: "is_public",
|
||||
enableSorting: true,
|
||||
sortingFn: (rowA, rowB) => {
|
||||
const publicA = rowA.original.is_public === true ? 1 : 0;
|
||||
const publicB = rowB.original.is_public === true ? 1 : 0;
|
||||
return publicA - publicB;
|
||||
},
|
||||
cell: ({ row }) => {
|
||||
const agent = row.original;
|
||||
|
||||
return agent.is_public === true ? (
|
||||
<Badge color="green" size="xs">
|
||||
Yes
|
||||
</Badge>
|
||||
) : (
|
||||
<Badge color="gray" size="xs">
|
||||
No
|
||||
</Badge>
|
||||
);
|
||||
},
|
||||
meta: {
|
||||
className: "hidden md:table-cell",
|
||||
},
|
||||
meta: {
|
||||
className: "hidden xl:table-cell",
|
||||
},
|
||||
{
|
||||
header: "Details",
|
||||
id: "details",
|
||||
enableSorting: false,
|
||||
cell: ({ row }) => {
|
||||
const agent = row.original;
|
||||
|
||||
return (
|
||||
<Button size="xs" variant="secondary" onClick={() => showModal(agent)} icon={InfoCircleOutlined}>
|
||||
<span className="hidden lg:inline">Details</span>
|
||||
<span className="lg:hidden">Info</span>
|
||||
</Button>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
header: "Public",
|
||||
accessorKey: "is_public",
|
||||
enableSorting: true,
|
||||
sortingFn: (rowA, rowB) => {
|
||||
const publicA = rowA.original.is_public === true ? 1 : 0;
|
||||
const publicB = rowB.original.is_public === true ? 1 : 0;
|
||||
return publicA - publicB;
|
||||
},
|
||||
];
|
||||
|
||||
return allColumns;
|
||||
};
|
||||
cell: ({ row }) =>
|
||||
row.original.is_public === true ? (
|
||||
<Badge className={cn("text-xs", badgeClass.emerald)}>Yes</Badge>
|
||||
) : (
|
||||
<Badge className={cn("text-xs", badgeClass.gray)}>No</Badge>
|
||||
),
|
||||
meta: {
|
||||
className: "hidden md:table-cell",
|
||||
},
|
||||
},
|
||||
{
|
||||
header: "Details",
|
||||
id: "details",
|
||||
enableSorting: false,
|
||||
cell: ({ row }) => {
|
||||
const agent = row.original;
|
||||
return (
|
||||
<Button
|
||||
size="sm"
|
||||
variant="secondary"
|
||||
onClick={() => showModal(agent)}
|
||||
>
|
||||
<Info className="h-3.5 w-3.5" />
|
||||
<span className="hidden lg:inline">Details</span>
|
||||
<span className="lg:hidden">Info</span>
|
||||
</Button>
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue