fix model page col resize

This commit is contained in:
yuneng-jiang
2026-02-06 12:27:03 -08:00
parent 5733f6213b
commit 8df6cfe9d8
4 changed files with 922 additions and 42 deletions
@@ -95,7 +95,14 @@ export function AllModelsDataTable<TData, TValue>({
<div className="rounded-lg custom-border relative">
<div className="overflow-x-auto">
<div className="relative min-w-full">
<Table className="[&_td]:py-2 [&_th]:py-2 w-full">
<Table
className="[&_td]:py-2 [&_th]:py-2"
style={{
width: tableInstance.getTotalSize(),
minWidth: "100%",
tableLayout: "fixed",
}}
>
<TableHead>
{tableInstance.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}>
@@ -171,7 +178,7 @@ export function AllModelsDataTable<TData, TValue>({
{row.getVisibleCells().map((cell) => (
<TableCell
key={cell.id}
className={`py-0.5 ${cell.column.id === "actions"
className={`py-0.5 overflow-hidden ${cell.column.id === "actions"
? "sticky right-0 bg-white shadow-[-4px_0_8px_-6px_rgba(0,0,0,0.1)] w-[120px] ml-8"
: ""
} ${cell.column.columnDef.meta?.className || ""}`}
@@ -94,7 +94,14 @@ export function ModelDataTable<TData, TValue>({
<div className="rounded-lg custom-border relative">
<div className="overflow-x-auto">
<div className="relative min-w-full">
<Table className="[&_td]:py-2 [&_th]:py-2 w-full">
<Table
className="[&_td]:py-2 [&_th]:py-2"
style={{
width: tableInstance.getTotalSize(),
minWidth: "100%",
tableLayout: "fixed",
}}
>
<TableHead>
{tableInstance.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}>
@@ -161,7 +168,7 @@ export function ModelDataTable<TData, TValue>({
{row.getVisibleCells().map((cell) => (
<TableCell
key={cell.id}
className={`py-0.5 ${
className={`py-0.5 overflow-hidden ${
cell.column.id === "actions"
? "sticky right-0 bg-white shadow-[-4px_0_8px_-6px_rgba(0,0,0,0.1)] w-[120px] ml-8"
: ""
@@ -0,0 +1,808 @@
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { describe, expect, it, vi, beforeEach } from "vitest";
import { useReactTable, getCoreRowModel, flexRender } from "@tanstack/react-table";
import { columns } from "./columns";
import { ModelData } from "../../model_dashboard/types";
import { Table, TableHead, TableHeaderCell, TableBody, TableRow, TableCell } from "@tremor/react";
import * as providerInfoHelpers from "../../provider_info_helpers";
vi.mock("../../provider_info_helpers");
vi.mock("@tremor/react", async (importOriginal) => {
const React = await import("react");
const actual = await importOriginal<typeof import("@tremor/react")>();
return {
...actual,
Icon: React.forwardRef<HTMLButtonElement, any>(({ icon: IconComponent, onClick, className, ...props }, ref) => {
const ariaLabel = className?.includes("cursor-not-allowed")
? "Config model cannot be deleted on the dashboard. Please delete it from the config file."
: "Delete model";
return React.createElement(
"button",
{ ...props, onClick, className, ref, "aria-label": ariaLabel },
IconComponent && React.createElement(IconComponent, { className: "w-4 h-4" }),
);
}),
};
});
const createMockModel = (overrides: Partial<ModelData> = {}): ModelData => ({
model_info: {
id: "test-model-id",
created_at: "2024-01-01T00:00:00Z",
updated_at: "2024-01-02T00:00:00Z",
created_by: "test-user",
team_id: "test-team-id",
db_model: true,
access_groups: ["group1"],
},
model_name: "test-model",
provider: "openai",
litellm_model_name: "gpt-4",
input_cost: 0.01,
output_cost: 0.03,
max_tokens: 4096,
max_input_tokens: 8192,
litellm_params: {
model: "gpt-4",
litellm_credential_name: "test-credential",
},
cleanedLitellmParams: {},
...overrides,
});
const TestTable = ({
data,
columns: cols,
}: {
data: ModelData[];
columns: ReturnType<typeof columns>;
}) => {
const table = useReactTable({
data,
columns: cols,
getCoreRowModel: getCoreRowModel(),
});
return (
<Table>
<TableHead>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}>
{headerGroup.headers.map((header) => (
<TableHeaderCell key={header.id}>
{header.isPlaceholder
? null
: flexRender(header.column.columnDef.header, header.getContext())}
</TableHeaderCell>
))}
</TableRow>
))}
</TableHead>
<TableBody>
{table.getRowModel().rows.map((row) => (
<TableRow key={row.id}>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</TableCell>
))}
</TableRow>
))}
</TableBody>
</Table>
);
};
describe("columns", () => {
beforeEach(() => {
vi.mocked(providerInfoHelpers.getProviderLogoAndName).mockImplementation((provider: string) => {
const providerMap: Record<string, { displayName: string; logo: string }> = {
openai: { displayName: "OpenAI", logo: "/openai-logo.png" },
anthropic: { displayName: "Anthropic", logo: "/anthropic-logo.png" },
};
return providerMap[provider] || { displayName: provider || "Unknown provider", logo: "" };
});
});
const defaultProps = {
userRole: "Admin",
userID: "test-user",
premiumUser: false,
setSelectedModelId: vi.fn(),
setSelectedTeamId: vi.fn(),
getDisplayModelName: vi.fn((model: ModelData) => model.model_name || "-"),
handleEditClick: vi.fn(),
handleRefreshClick: vi.fn(),
expandedRows: new Set<string>(),
setExpandedRows: vi.fn(),
};
it("should render columns with table structure", () => {
const cols = columns(
defaultProps.userRole,
defaultProps.userID,
defaultProps.premiumUser,
defaultProps.setSelectedModelId,
defaultProps.setSelectedTeamId,
defaultProps.getDisplayModelName,
defaultProps.handleEditClick,
defaultProps.handleRefreshClick,
defaultProps.expandedRows,
defaultProps.setExpandedRows,
);
const model = createMockModel();
render(<TestTable data={[model]} columns={cols} />);
expect(screen.getByText("Model ID")).toBeInTheDocument();
expect(screen.getByText("Model Information")).toBeInTheDocument();
expect(screen.getByText("Credentials")).toBeInTheDocument();
expect(screen.getByText("Created By")).toBeInTheDocument();
expect(screen.getByText("Updated At")).toBeInTheDocument();
expect(screen.getByText("Costs")).toBeInTheDocument();
expect(screen.getByText("Team ID")).toBeInTheDocument();
expect(screen.getByText("Model Access Group")).toBeInTheDocument();
expect(screen.getByText("Status")).toBeInTheDocument();
expect(screen.getByText("Actions")).toBeInTheDocument();
});
it("should display model information with provider logo", () => {
const cols = columns(
defaultProps.userRole,
defaultProps.userID,
defaultProps.premiumUser,
defaultProps.setSelectedModelId,
defaultProps.setSelectedTeamId,
defaultProps.getDisplayModelName,
defaultProps.handleEditClick,
defaultProps.handleRefreshClick,
defaultProps.expandedRows,
defaultProps.setExpandedRows,
);
const model = createMockModel({
model_name: "GPT-4",
provider: "openai",
litellm_model_name: "gpt-4",
});
render(<TestTable data={[model]} columns={cols} />);
expect(screen.getByText("GPT-4")).toBeInTheDocument();
expect(screen.getByText("gpt-4")).toBeInTheDocument();
});
it("should display credential name when available", () => {
const cols = columns(
defaultProps.userRole,
defaultProps.userID,
defaultProps.premiumUser,
defaultProps.setSelectedModelId,
defaultProps.setSelectedTeamId,
defaultProps.getDisplayModelName,
defaultProps.handleEditClick,
defaultProps.handleRefreshClick,
defaultProps.expandedRows,
defaultProps.setExpandedRows,
);
const model = createMockModel({
litellm_params: {
model: "gpt-4",
litellm_credential_name: "my-credential",
},
});
render(<TestTable data={[model]} columns={cols} />);
expect(screen.getByText("my-credential")).toBeInTheDocument();
});
it("should display 'No credentials' when credential name is missing", () => {
const cols = columns(
defaultProps.userRole,
defaultProps.userID,
defaultProps.premiumUser,
defaultProps.setSelectedModelId,
defaultProps.setSelectedTeamId,
defaultProps.getDisplayModelName,
defaultProps.handleEditClick,
defaultProps.handleRefreshClick,
defaultProps.expandedRows,
defaultProps.setExpandedRows,
);
const model = createMockModel({
litellm_params: {
model: "gpt-4",
},
});
render(<TestTable data={[model]} columns={cols} />);
expect(screen.getByText("No credentials")).toBeInTheDocument();
});
it("should display created by information for DB models", () => {
const cols = columns(
defaultProps.userRole,
defaultProps.userID,
defaultProps.premiumUser,
defaultProps.setSelectedModelId,
defaultProps.setSelectedTeamId,
defaultProps.getDisplayModelName,
defaultProps.handleEditClick,
defaultProps.handleRefreshClick,
defaultProps.expandedRows,
defaultProps.setExpandedRows,
);
const model = createMockModel({
model_info: {
...createMockModel().model_info,
db_model: true,
created_by: "admin-user",
created_at: "2024-01-15T10:30:00Z",
},
});
render(<TestTable data={[model]} columns={cols} />);
expect(screen.getByText("admin-user")).toBeInTheDocument();
});
it("should display 'Defined in config' for config models", () => {
const cols = columns(
defaultProps.userRole,
defaultProps.userID,
defaultProps.premiumUser,
defaultProps.setSelectedModelId,
defaultProps.setSelectedTeamId,
defaultProps.getDisplayModelName,
defaultProps.handleEditClick,
defaultProps.handleRefreshClick,
defaultProps.expandedRows,
defaultProps.setExpandedRows,
);
const model = createMockModel({
model_info: {
...createMockModel().model_info,
db_model: false,
},
});
render(<TestTable data={[model]} columns={cols} />);
expect(screen.getByText("Defined in config")).toBeInTheDocument();
});
it("should display costs when available", () => {
const cols = columns(
defaultProps.userRole,
defaultProps.userID,
defaultProps.premiumUser,
defaultProps.setSelectedModelId,
defaultProps.setSelectedTeamId,
defaultProps.getDisplayModelName,
defaultProps.handleEditClick,
defaultProps.handleRefreshClick,
defaultProps.expandedRows,
defaultProps.setExpandedRows,
);
const model = createMockModel({
input_cost: 0.01,
output_cost: 0.03,
});
render(<TestTable data={[model]} columns={cols} />);
expect(screen.getByText("In: $0.01")).toBeInTheDocument();
expect(screen.getByText("Out: $0.03")).toBeInTheDocument();
});
it("should display '-' when costs are missing", () => {
const cols = columns(
defaultProps.userRole,
defaultProps.userID,
defaultProps.premiumUser,
defaultProps.setSelectedModelId,
defaultProps.setSelectedTeamId,
defaultProps.getDisplayModelName,
defaultProps.handleEditClick,
defaultProps.handleRefreshClick,
defaultProps.expandedRows,
defaultProps.setExpandedRows,
);
const model = createMockModel({
input_cost: undefined as any,
output_cost: undefined as any,
});
render(<TestTable data={[model]} columns={cols} />);
const costCells = screen.getAllByText("-");
expect(costCells.length).toBeGreaterThan(0);
});
it("should display '-' when team ID is missing", () => {
const cols = columns(
defaultProps.userRole,
defaultProps.userID,
defaultProps.premiumUser,
defaultProps.setSelectedModelId,
defaultProps.setSelectedTeamId,
defaultProps.getDisplayModelName,
defaultProps.handleEditClick,
defaultProps.handleRefreshClick,
defaultProps.expandedRows,
defaultProps.setExpandedRows,
);
const model = createMockModel({
model_info: {
...createMockModel().model_info,
team_id: "",
},
});
render(<TestTable data={[model]} columns={cols} />);
const teamIdCells = screen.getAllByText("-");
expect(teamIdCells.length).toBeGreaterThan(0);
});
it("should display access groups", () => {
const cols = columns(
defaultProps.userRole,
defaultProps.userID,
defaultProps.premiumUser,
defaultProps.setSelectedModelId,
defaultProps.setSelectedTeamId,
defaultProps.getDisplayModelName,
defaultProps.handleEditClick,
defaultProps.handleRefreshClick,
defaultProps.expandedRows,
defaultProps.setExpandedRows,
);
const model = createMockModel({
model_info: {
...createMockModel().model_info,
access_groups: ["group1", "group2"],
},
});
render(<TestTable data={[model]} columns={cols} />);
expect(screen.getByText("group1")).toBeInTheDocument();
expect(screen.getByText("+1")).toBeInTheDocument();
});
it("should expand access groups when expand button is clicked", async () => {
const user = userEvent.setup();
const setExpandedRows = vi.fn();
const expandedRows = new Set<string>();
const cols = columns(
defaultProps.userRole,
defaultProps.userID,
defaultProps.premiumUser,
defaultProps.setSelectedModelId,
defaultProps.setSelectedTeamId,
defaultProps.getDisplayModelName,
defaultProps.handleEditClick,
defaultProps.handleRefreshClick,
expandedRows,
setExpandedRows,
);
const model = createMockModel({
model_info: {
...createMockModel().model_info,
id: "model-with-groups",
access_groups: ["group1", "group2", "group3"],
},
});
render(<TestTable data={[model]} columns={cols} />);
const expandButton = screen.getByText("+2");
expect(expandButton).toBeInTheDocument();
await user.click(expandButton);
expect(setExpandedRows).toHaveBeenCalled();
});
it("should display '-' when access groups are empty", () => {
const cols = columns(
defaultProps.userRole,
defaultProps.userID,
defaultProps.premiumUser,
defaultProps.setSelectedModelId,
defaultProps.setSelectedTeamId,
defaultProps.getDisplayModelName,
defaultProps.handleEditClick,
defaultProps.handleRefreshClick,
defaultProps.expandedRows,
defaultProps.setExpandedRows,
);
const model = createMockModel({
model_info: {
...createMockModel().model_info,
access_groups: null,
},
});
render(<TestTable data={[model]} columns={cols} />);
const emptyCells = screen.getAllByText("-");
expect(emptyCells.length).toBeGreaterThan(0);
});
it("should display 'DB Model' status for DB models", () => {
const cols = columns(
defaultProps.userRole,
defaultProps.userID,
defaultProps.premiumUser,
defaultProps.setSelectedModelId,
defaultProps.setSelectedTeamId,
defaultProps.getDisplayModelName,
defaultProps.handleEditClick,
defaultProps.handleRefreshClick,
defaultProps.expandedRows,
defaultProps.setExpandedRows,
);
const model = createMockModel({
model_info: {
...createMockModel().model_info,
db_model: true,
},
});
render(<TestTable data={[model]} columns={cols} />);
expect(screen.getByText("DB Model")).toBeInTheDocument();
});
it("should display 'Config Model' status for config models", () => {
const cols = columns(
defaultProps.userRole,
defaultProps.userID,
defaultProps.premiumUser,
defaultProps.setSelectedModelId,
defaultProps.setSelectedTeamId,
defaultProps.getDisplayModelName,
defaultProps.handleEditClick,
defaultProps.handleRefreshClick,
defaultProps.expandedRows,
defaultProps.setExpandedRows,
);
const model = createMockModel({
model_info: {
...createMockModel().model_info,
db_model: false,
},
});
render(<TestTable data={[model]} columns={cols} />);
expect(screen.getByText("Config Model")).toBeInTheDocument();
});
it("should allow Admin to delete DB models", async () => {
const user = userEvent.setup();
const setSelectedModelId = vi.fn();
const cols = columns(
"Admin",
"admin-user",
defaultProps.premiumUser,
setSelectedModelId,
defaultProps.setSelectedTeamId,
defaultProps.getDisplayModelName,
defaultProps.handleEditClick,
defaultProps.handleRefreshClick,
defaultProps.expandedRows,
defaultProps.setExpandedRows,
);
const model = createMockModel({
model_info: {
...createMockModel().model_info,
db_model: true,
id: "deletable-model",
},
});
render(<TestTable data={[model]} columns={cols} />);
const deleteButton = screen.getByRole("button", { name: "Delete model" });
expect(deleteButton).toBeInTheDocument();
await user.click(deleteButton);
expect(setSelectedModelId).toHaveBeenCalledWith("deletable-model");
});
it("should allow model creator to delete their own DB models", async () => {
const user = userEvent.setup();
const setSelectedModelId = vi.fn();
const cols = columns(
"User",
"model-creator",
defaultProps.premiumUser,
setSelectedModelId,
defaultProps.setSelectedTeamId,
defaultProps.getDisplayModelName,
defaultProps.handleEditClick,
defaultProps.handleRefreshClick,
defaultProps.expandedRows,
defaultProps.setExpandedRows,
);
const model = createMockModel({
model_info: {
...createMockModel().model_info,
db_model: true,
created_by: "model-creator",
id: "user-model",
},
});
render(<TestTable data={[model]} columns={cols} />);
const deleteButton = screen.getByRole("button", { name: "Delete model" });
expect(deleteButton).toBeInTheDocument();
await user.click(deleteButton);
expect(setSelectedModelId).toHaveBeenCalledWith("user-model");
});
it("should disable delete for config models", () => {
const cols = columns(
"Admin",
"admin-user",
defaultProps.premiumUser,
defaultProps.setSelectedModelId,
defaultProps.setSelectedTeamId,
defaultProps.getDisplayModelName,
defaultProps.handleEditClick,
defaultProps.handleRefreshClick,
defaultProps.expandedRows,
defaultProps.setExpandedRows,
);
const model = createMockModel({
model_info: {
...createMockModel().model_info,
db_model: false,
},
});
render(<TestTable data={[model]} columns={cols} />);
const deleteButton = screen.getByRole("button", { name: /config model cannot be deleted/i });
expect(deleteButton).toBeInTheDocument();
expect(deleteButton).toHaveClass("cursor-not-allowed");
});
it("should display collapsed access groups with expand button", () => {
const cols = columns(
defaultProps.userRole,
defaultProps.userID,
defaultProps.premiumUser,
defaultProps.setSelectedModelId,
defaultProps.setSelectedTeamId,
defaultProps.getDisplayModelName,
defaultProps.handleEditClick,
defaultProps.handleRefreshClick,
new Set<string>(),
defaultProps.setExpandedRows,
);
const model = createMockModel({
model_info: {
...createMockModel().model_info,
access_groups: ["group1", "group2", "group3"],
},
});
render(<TestTable data={[model]} columns={cols} />);
expect(screen.getByText("group1")).toBeInTheDocument();
expect(screen.getByText("+2")).toBeInTheDocument();
expect(screen.queryByText("group2")).not.toBeInTheDocument();
expect(screen.queryByText("group3")).not.toBeInTheDocument();
});
it("should display expanded access groups when expanded", () => {
const cols = columns(
defaultProps.userRole,
defaultProps.userID,
defaultProps.premiumUser,
defaultProps.setSelectedModelId,
defaultProps.setSelectedTeamId,
defaultProps.getDisplayModelName,
defaultProps.handleEditClick,
defaultProps.handleRefreshClick,
new Set<string>(["test-model-id"]),
defaultProps.setExpandedRows,
);
const model = createMockModel({
model_info: {
...createMockModel().model_info,
id: "test-model-id",
access_groups: ["group1", "group2", "group3"],
},
});
render(<TestTable data={[model]} columns={cols} />);
expect(screen.getByText("group1")).toBeInTheDocument();
expect(screen.getByText("group2")).toBeInTheDocument();
expect(screen.getByText("group3")).toBeInTheDocument();
expect(screen.getByText("")).toBeInTheDocument();
});
it("should display single access group without expand button", () => {
const cols = columns(
defaultProps.userRole,
defaultProps.userID,
defaultProps.premiumUser,
defaultProps.setSelectedModelId,
defaultProps.setSelectedTeamId,
defaultProps.getDisplayModelName,
defaultProps.handleEditClick,
defaultProps.handleRefreshClick,
defaultProps.expandedRows,
defaultProps.setExpandedRows,
);
const model = createMockModel({
model_info: {
...createMockModel().model_info,
access_groups: ["group1"],
},
});
render(<TestTable data={[model]} columns={cols} />);
expect(screen.getByText("group1")).toBeInTheDocument();
expect(screen.queryByText(/\+/)).not.toBeInTheDocument();
});
it("should handle missing display name gracefully", () => {
const getDisplayModelName = vi.fn(() => "");
const cols = columns(
defaultProps.userRole,
defaultProps.userID,
defaultProps.premiumUser,
defaultProps.setSelectedModelId,
defaultProps.setSelectedTeamId,
getDisplayModelName,
defaultProps.handleEditClick,
defaultProps.handleRefreshClick,
defaultProps.expandedRows,
defaultProps.setExpandedRows,
);
const model = createMockModel();
render(<TestTable data={[model]} columns={cols} />);
expect(screen.getByText("-")).toBeInTheDocument();
});
it("should handle missing created_at date", () => {
const cols = columns(
defaultProps.userRole,
defaultProps.userID,
defaultProps.premiumUser,
defaultProps.setSelectedModelId,
defaultProps.setSelectedTeamId,
defaultProps.getDisplayModelName,
defaultProps.handleEditClick,
defaultProps.handleRefreshClick,
defaultProps.expandedRows,
defaultProps.setExpandedRows,
);
const model = createMockModel({
model_info: {
...createMockModel().model_info,
created_at: "",
},
});
render(<TestTable data={[model]} columns={cols} />);
expect(screen.getByText("Unknown date")).toBeInTheDocument();
});
it("should handle missing updated_at date", () => {
const cols = columns(
defaultProps.userRole,
defaultProps.userID,
defaultProps.premiumUser,
defaultProps.setSelectedModelId,
defaultProps.setSelectedTeamId,
defaultProps.getDisplayModelName,
defaultProps.handleEditClick,
defaultProps.handleRefreshClick,
defaultProps.expandedRows,
defaultProps.setExpandedRows,
);
const model = createMockModel({
model_info: {
...createMockModel().model_info,
updated_at: "",
},
});
render(<TestTable data={[model]} columns={cols} />);
const updatedAtCells = screen.getAllByText("-");
expect(updatedAtCells.length).toBeGreaterThan(0);
});
it("should handle missing created_by for DB models", () => {
const cols = columns(
defaultProps.userRole,
defaultProps.userID,
defaultProps.premiumUser,
defaultProps.setSelectedModelId,
defaultProps.setSelectedTeamId,
defaultProps.getDisplayModelName,
defaultProps.handleEditClick,
defaultProps.handleRefreshClick,
defaultProps.expandedRows,
defaultProps.setExpandedRows,
);
const model = createMockModel({
model_info: {
...createMockModel().model_info,
db_model: true,
created_by: "",
},
});
render(<TestTable data={[model]} columns={cols} />);
expect(screen.getByText("Unknown")).toBeInTheDocument();
});
it("should display only input cost when output cost is missing", () => {
const cols = columns(
defaultProps.userRole,
defaultProps.userID,
defaultProps.premiumUser,
defaultProps.setSelectedModelId,
defaultProps.setSelectedTeamId,
defaultProps.getDisplayModelName,
defaultProps.handleEditClick,
defaultProps.handleRefreshClick,
defaultProps.expandedRows,
defaultProps.setExpandedRows,
);
const model = createMockModel({
input_cost: 0.01,
output_cost: undefined as any,
});
render(<TestTable data={[model]} columns={cols} />);
expect(screen.getByText("In: $0.01")).toBeInTheDocument();
expect(screen.queryByText(/Out:/)).not.toBeInTheDocument();
});
it("should display only output cost when input cost is missing", () => {
const cols = columns(
defaultProps.userRole,
defaultProps.userID,
defaultProps.premiumUser,
defaultProps.setSelectedModelId,
defaultProps.setSelectedTeamId,
defaultProps.getDisplayModelName,
defaultProps.handleEditClick,
defaultProps.handleRefreshClick,
defaultProps.expandedRows,
defaultProps.setExpandedRows,
);
const model = createMockModel({
input_cost: undefined as any,
output_cost: 0.03,
});
render(<TestTable data={[model]} columns={cols} />);
expect(screen.getByText("Out: $0.03")).toBeInTheDocument();
expect(screen.queryByText(/In:/)).not.toBeInTheDocument();
});
});
@@ -1,10 +1,12 @@
import { KeyIcon, TrashIcon } from "@heroicons/react/outline";
import { ColumnDef } from "@tanstack/react-table";
import { Badge, Button, Icon } from "@tremor/react";
import { Tooltip } from "antd";
import { Popover, Tooltip, Typography, Space, Flex } from "antd";
import { ModelData } from "../../model_dashboard/types";
import { ProviderLogo } from "./ProviderLogo";
const { Text } = Typography;
export const columns = (
userRole: string,
userID: string,
@@ -21,16 +23,20 @@ export const columns = (
header: () => <span className="text-sm font-semibold">Model ID</span>,
accessorKey: "model_info.id",
enableSorting: false,
size: 130,
minSize: 80,
cell: ({ row }) => {
const model = row.original;
return (
<Tooltip title={model.model_info.id}>
<div
className="font-mono text-blue-500 bg-blue-50 hover:bg-blue-100 text-xs font-normal px-2 py-0.5 text-left w-full truncate whitespace-nowrap cursor-pointer max-w-[15ch]"
<Text
ellipsis
className="text-blue-500 bg-blue-50 hover:bg-blue-100 text-xs cursor-pointer w-full block"
style={{ fontSize: 14, padding: '1px 8px' }}
onClick={() => setSelectedModelId(model.model_info.id)}
>
{model.model_info.id}
</div>
</Text>
</Tooltip>
);
},
@@ -38,28 +44,67 @@ export const columns = (
{
header: () => <span className="text-sm font-semibold">Model Information</span>,
accessorKey: "model_name",
size: 250, // Fixed column width
size: 250,
minSize: 120,
cell: ({ row }) => {
const model = row.original;
const displayName = getDisplayModelName(row.original) || "-";
const tooltipContent = (
<div>
<div>
<strong>Provider:</strong> {model.provider || "-"}
</div>
<div>
<strong>Public Model Name:</strong> {displayName}
</div>
<div>
<strong>LiteLLM Model Name:</strong> {model.litellm_model_name || "-"}
</div>
</div>
const popoverContent = (
<Space
direction="vertical"
size={12}
style={{ minWidth: 220 }}
>
<Flex align="center" gap={8}>
<ProviderLogo provider={model.provider} />
<Text
type="secondary"
style={{ fontSize: 12 }}
ellipsis
>
{model.provider || "Unknown provider"}
</Text>
</Flex>
<Space direction="vertical" size={6}>
<Space direction="vertical" size={2} style={{ width: "100%" }}>
<Text type="secondary" style={{ fontSize: 11 }}>
Public Model Name
</Text>
<Text
strong
style={{ fontSize: 13, maxWidth: 480 }}
ellipsis
title={displayName}
>
{displayName}
</Text>
</Space>
<Space direction="vertical" size={2}>
<Text type="secondary" style={{ fontSize: 11 }}>
LiteLLM Model Name
</Text>
<Text
style={{ fontSize: 13 }}
copyable={{ text: model.litellm_model_name || "-" }}
ellipsis
title={model.litellm_model_name || "-"}
>
{model.litellm_model_name || "-"}
</Text>
</Space>
</Space>
</Space>
);
return (
<Tooltip title={tooltipContent}>
<div className="flex items-start space-x-2 min-w-0 w-full max-w-[250px]">
{/* Provider Icon */}
<Popover content={popoverContent} placement="right" arrow={{ pointAtCenter: true }} styles={{
root: {
maxWidth: 500,
}
}}>
<div className="flex items-start space-x-2 min-w-0 w-full cursor-pointer">
<div className="flex-shrink-0 mt-0.5">
{model.provider ? (
<ProviderLogo provider={model.provider} />
@@ -68,17 +113,16 @@ export const columns = (
)}
</div>
{/* Model Names Container */}
<div className="flex flex-col min-w-0 flex-1">
{/* Public Model Name */}
<div className="text-xs font-medium text-gray-900 truncate max-w-[210px]">{displayName}</div>
{/* LiteLLM Model Name */}
<div className="text-xs text-gray-500 truncate mt-0.5 max-w-[210px]">
<Text ellipsis className="text-gray-900" style={{ fontSize: 12, fontWeight: 500, lineHeight: '16px' }}>
{displayName}
</Text>
<Text ellipsis type="secondary" style={{ fontSize: 12, lineHeight: '16px', marginTop: 2 }}>
{model.litellm_model_name || "-"}
</div>
</Text>
</div>
</div>
</Tooltip>
</Popover>
);
},
},
@@ -86,14 +130,15 @@ export const columns = (
header: () => <span className="text-sm font-semibold">Credentials</span>,
accessorKey: "litellm_credential_name",
enableSorting: false,
size: 180, // Fixed column width
size: 180,
minSize: 100,
cell: ({ row }) => {
const model = row.original;
const credentialName = model.litellm_params?.litellm_credential_name;
return credentialName ? (
<Tooltip title={`Credential: ${credentialName}`}>
<div className="flex items-center space-x-2 max-w-[180px]">
<div className="flex items-center space-x-2 min-w-0 w-full">
<KeyIcon className="w-4 h-4 text-blue-500 flex-shrink-0" />
<span className="text-xs truncate" title={credentialName}>
{credentialName}
@@ -101,7 +146,7 @@ export const columns = (
</div>
</Tooltip>
) : (
<div className="flex items-center space-x-2 max-w-[180px]">
<div className="flex items-center space-x-2 min-w-0 w-full">
<KeyIcon className="w-4 h-4 text-gray-300 flex-shrink-0" />
<span className="text-xs text-gray-400">No credentials</span>
</div>
@@ -112,7 +157,8 @@ export const columns = (
header: () => <span className="text-sm font-semibold">Created By</span>,
accessorKey: "model_info.created_by",
sortingFn: "datetime",
size: 160, // Fixed column width
size: 160,
minSize: 100,
cell: ({ row }) => {
const model = row.original;
const isConfigModel = !model.model_info?.db_model;
@@ -120,7 +166,7 @@ export const columns = (
const createdAt = model.model_info.created_at ? new Date(model.model_info.created_at).toLocaleDateString() : null;
return (
<div className="flex flex-col min-w-0 max-w-[160px]">
<div className="flex flex-col min-w-0 w-full">
{/* Created By - Primary */}
<div
className="text-xs font-medium text-gray-900 truncate"
@@ -143,6 +189,8 @@ export const columns = (
header: () => <span className="text-sm font-semibold">Updated At</span>,
accessorKey: "model_info.updated_at",
sortingFn: "datetime",
size: 120,
minSize: 80,
cell: ({ row }) => {
const model = row.original;
return (
@@ -155,7 +203,8 @@ export const columns = (
{
header: () => <span className="text-sm font-semibold">Costs</span>,
accessorKey: "input_cost",
size: 120, // Fixed column width
size: 120,
minSize: 80,
cell: ({ row }) => {
const model = row.original;
const inputCost = model.input_cost;
@@ -164,7 +213,7 @@ export const columns = (
// If both costs are missing or undefined, show "-"
if (!inputCost && !outputCost) {
return (
<div className="max-w-[120px]">
<div className="w-full">
<span className="text-xs text-gray-400">-</span>
</div>
);
@@ -172,7 +221,7 @@ export const columns = (
return (
<Tooltip title="Cost per 1M tokens">
<div className="flex flex-col min-w-0 max-w-[120px]">
<div className="flex flex-col min-w-0 w-full">
{/* Input Cost - Primary */}
{inputCost && <div className="text-xs font-medium text-gray-900 truncate">In: ${inputCost}</div>}
{/* Output Cost - Secondary */}
@@ -186,15 +235,17 @@ export const columns = (
header: () => <span className="text-sm font-semibold">Team ID</span>,
accessorKey: "model_info.team_id",
enableSorting: false,
size: 130,
minSize: 80,
cell: ({ row }) => {
const model = row.original;
return model.model_info.team_id ? (
<div className="overflow-hidden">
<div className="overflow-hidden w-full">
<Tooltip title={model.model_info.team_id}>
<Button
size="xs"
variant="light"
className="font-mono text-blue-500 bg-blue-50 hover:bg-blue-100 text-xs font-normal px-2 py-0.5 text-left overflow-hidden truncate max-w-[200px]"
className="font-mono text-blue-500 bg-blue-50 hover:bg-blue-100 text-xs font-normal px-2 py-0.5 text-left overflow-hidden truncate w-full"
onClick={() => setSelectedTeamId(model.model_info.team_id)}
>
{model.model_info.team_id.slice(0, 7)}...
@@ -210,6 +261,8 @@ export const columns = (
header: () => <span className="text-sm font-semibold">Model Access Group</span>,
accessorKey: "model_info.model_access_group",
enableSorting: false,
size: 180,
minSize: 100,
cell: ({ row }) => {
const model = row.original;
const accessGroups = model.model_info.access_groups;
@@ -233,7 +286,7 @@ export const columns = (
};
return (
<div className="flex items-center gap-1 overflow-hidden">
<div className="flex items-center gap-1 overflow-hidden w-full">
<Badge size="xs" color="blue" className="text-xs px-1.5 py-0.5 h-5 leading-tight flex-shrink-0">
{accessGroups[0]}
</Badge>
@@ -268,6 +321,8 @@ export const columns = (
{
header: () => <span className="text-sm font-semibold">Status</span>,
accessorKey: "model_info.db_model",
size: 120,
minSize: 80,
cell: ({ row }) => {
const model = row.original;
return (
@@ -285,6 +340,9 @@ export const columns = (
{
id: "actions",
header: () => <span className="text-sm font-semibold">Actions</span>,
size: 60,
minSize: 40,
enableResizing: false,
cell: ({ row }) => {
const model = row.original;
const canEditModel = userRole === "Admin" || model.model_info?.created_by === userID;