mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-09 03:18:44 +00:00
Models page row UI restructure (#13771)
* remove unused model dashboard * move model_dashboard to templates folder * moving columns to molecules directory * group model names and provider icon * increase table column name font size. cleanup extra title and description inside the tab * fix column width and truncate string * moved credentials column * combined created by and created at * combine in and out costs * remove edit button * remove 2nd extra tooltip
This commit is contained in:
parent
195ea6515e
commit
b0e3469202
4 changed files with 811 additions and 1150 deletions
|
|
@ -8,7 +8,7 @@ import { Team } from "@/components/key_team_helpers/key_list"
|
|||
import Navbar from "@/components/navbar"
|
||||
import { ThemeProvider } from "@/contexts/ThemeContext"
|
||||
import UserDashboard from "@/components/user_dashboard"
|
||||
import ModelDashboard from "@/components/model_dashboard"
|
||||
import ModelDashboard from "@/components/templates/model_dashboard"
|
||||
import ViewUserDashboard from "@/components/view_users"
|
||||
import Teams from "@/components/teams"
|
||||
import Organizations from "@/components/organizations"
|
||||
|
|
|
|||
|
|
@ -1,63 +0,0 @@
|
|||
import { useState } from 'react';
|
||||
import { Select, SelectItem, Text } from "@tremor/react";
|
||||
|
||||
interface ModelData {
|
||||
team_id: string;
|
||||
team_name: string;
|
||||
// Add other properties as needed
|
||||
}
|
||||
|
||||
interface ModelDashboardProps {
|
||||
modelData: ModelData[];
|
||||
}
|
||||
|
||||
export default function ModelDashboard({ modelData }: ModelDashboardProps) {
|
||||
const [selectedTeam, setSelectedTeam] = useState<string | null>(null);
|
||||
|
||||
const getTeamName = (teamId: string): string => {
|
||||
const team = modelData.find(item => item.team_id === teamId);
|
||||
return team?.team_name || 'Unknown Team';
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-col space-y-4">
|
||||
<div className="flex justify-between items-center mb-6">
|
||||
<div>
|
||||
<Text className="text-lg font-medium">Model Management</Text>
|
||||
<Text className="text-gray-500">Add and manage models for the proxy</Text>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<Text>Filter by Public Model Name:</Text>
|
||||
<Select
|
||||
className="w-64"
|
||||
defaultValue="all"
|
||||
>
|
||||
<SelectItem value="all">All Models</SelectItem>
|
||||
{/* Add model options here */}
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2">
|
||||
<Text>Filter by Team:</Text>
|
||||
<Select
|
||||
className="w-64"
|
||||
value={selectedTeam ?? "all"}
|
||||
onValueChange={(value) => setSelectedTeam(value === "all" ? null : value)}
|
||||
>
|
||||
<SelectItem value="all">All Teams</SelectItem>
|
||||
{Array.from(new Set(modelData.map(model => model.team_id)))
|
||||
.filter(teamId => teamId !== null)
|
||||
.map(teamId => (
|
||||
<SelectItem key={teamId} value={teamId}>
|
||||
{getTeamName(teamId)}
|
||||
</SelectItem>
|
||||
))}
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
import { ColumnDef } from "@tanstack/react-table";
|
||||
import { Button, Badge, Icon } from "@tremor/react";
|
||||
import { Tooltip } from "antd";
|
||||
import { getProviderLogoAndName } from "../provider_info_helpers";
|
||||
import { ModelData } from "./types";
|
||||
import { TrashIcon, PencilIcon, PencilAltIcon } from "@heroicons/react/outline";
|
||||
import DeleteModelButton from "../delete_model_button";
|
||||
import { getProviderLogoAndName } from "../../provider_info_helpers";
|
||||
import { ModelData } from "../../model_dashboard/types";
|
||||
import { TrashIcon, PencilIcon, PencilAltIcon, KeyIcon } from "@heroicons/react/outline";
|
||||
import DeleteModelButton from "../../delete_model_button";
|
||||
import { useState } from "react";
|
||||
|
||||
export const columns = (
|
||||
|
|
@ -21,7 +21,7 @@ export const columns = (
|
|||
setExpandedRows: (expandedRows: Set<string>) => void,
|
||||
): ColumnDef<ModelData>[] => [
|
||||
{
|
||||
header: "Model ID",
|
||||
header: () => <span className="text-sm font-semibold">Model ID</span>,
|
||||
accessorKey: "model_info.id",
|
||||
cell: ({ row }) => {
|
||||
const model = row.original;
|
||||
|
|
@ -38,77 +38,117 @@ export const columns = (
|
|||
},
|
||||
},
|
||||
{
|
||||
header: "Public Model Name",
|
||||
header: () => <span className="text-sm font-semibold">Model Information</span>,
|
||||
accessorKey: "model_name",
|
||||
size: 250, // Fixed column width
|
||||
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>
|
||||
);
|
||||
|
||||
return (
|
||||
<Tooltip title={displayName}>
|
||||
<div className="text-xs truncate whitespace-nowrap">
|
||||
{displayName}
|
||||
<Tooltip title={tooltipContent}>
|
||||
<div className="flex items-start space-x-2 min-w-0 w-full max-w-[250px]">
|
||||
{/* Provider Icon */}
|
||||
<div className="flex-shrink-0 mt-0.5">
|
||||
{model.provider ? (
|
||||
<img
|
||||
src={getProviderLogoAndName(model.provider).logo}
|
||||
alt={`${model.provider} logo`}
|
||||
className="w-4 h-4"
|
||||
onError={(e) => {
|
||||
const target = e.target as HTMLImageElement;
|
||||
const parent = target.parentElement;
|
||||
if (parent) {
|
||||
const fallbackDiv = document.createElement('div');
|
||||
fallbackDiv.className = 'w-4 h-4 rounded-full bg-gray-200 flex items-center justify-center text-xs';
|
||||
fallbackDiv.textContent = model.provider?.charAt(0) || '-';
|
||||
parent.replaceChild(fallbackDiv, target);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<div className="w-4 h-4 rounded-full bg-gray-200 flex items-center justify-center text-xs">
|
||||
-
|
||||
</div>
|
||||
)}
|
||||
</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]">
|
||||
{model.litellm_model_name || "-"}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Tooltip>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
header: "Provider",
|
||||
accessorKey: "provider",
|
||||
header: () => <span className="text-sm font-semibold">Credentials</span>,
|
||||
accessorKey: "litellm_credential_name",
|
||||
size: 180, // Fixed column width
|
||||
cell: ({ row }) => {
|
||||
const model = row.original;
|
||||
return (
|
||||
<div className="flex items-center space-x-2">
|
||||
{model.provider && (
|
||||
<img
|
||||
src={getProviderLogoAndName(model.provider).logo}
|
||||
alt={`${model.provider} logo`}
|
||||
className="w-4 h-4"
|
||||
onError={(e) => {
|
||||
const target = e.target as HTMLImageElement;
|
||||
const parent = target.parentElement;
|
||||
if (parent) {
|
||||
const fallbackDiv = document.createElement('div');
|
||||
fallbackDiv.className = 'w-4 h-4 rounded-full bg-gray-200 flex items-center justify-center text-xs';
|
||||
fallbackDiv.textContent = model.provider?.charAt(0) || '-';
|
||||
parent.replaceChild(fallbackDiv, target);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<p className="text-xs">{model.provider || "-"}</p>
|
||||
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]">
|
||||
<KeyIcon className="w-4 h-4 text-blue-500 flex-shrink-0" />
|
||||
<span className="text-xs truncate" title={credentialName}>
|
||||
{credentialName}
|
||||
</span>
|
||||
</div>
|
||||
</Tooltip>
|
||||
) : (
|
||||
<div className="flex items-center space-x-2 max-w-[180px]">
|
||||
<KeyIcon className="w-4 h-4 text-gray-300 flex-shrink-0" />
|
||||
<span className="text-xs text-gray-400">No credentials</span>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
header: "LiteLLM Model Name",
|
||||
accessorKey: "litellm_model_name",
|
||||
cell: ({ row }) => {
|
||||
const model = row.original;
|
||||
return (
|
||||
<Tooltip title={model.litellm_model_name}>
|
||||
<div className="text-xs truncate whitespace-nowrap">
|
||||
{model.litellm_model_name || "-"}
|
||||
</div>
|
||||
</Tooltip>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
header: "Created At",
|
||||
accessorKey: "model_info.created_at",
|
||||
header: () => <span className="text-sm font-semibold">Created By</span>,
|
||||
accessorKey: "model_info.created_by",
|
||||
sortingFn: "datetime",
|
||||
size: 160, // Fixed column width
|
||||
cell: ({ row }) => {
|
||||
const model = row.original;
|
||||
const createdBy = model.model_info.created_by;
|
||||
const createdAt = model.model_info.created_at
|
||||
? new Date(model.model_info.created_at).toLocaleDateString()
|
||||
: null;
|
||||
|
||||
return (
|
||||
<span className="text-xs">
|
||||
{model.model_info.created_at ? new Date(model.model_info.created_at).toLocaleDateString() : "-"}
|
||||
</span>
|
||||
<div className="flex flex-col min-w-0 max-w-[160px]">
|
||||
{/* Created By - Primary */}
|
||||
<div className="text-xs font-medium text-gray-900 truncate" title={createdBy || "Unknown"}>
|
||||
{createdBy || "Unknown"}
|
||||
</div>
|
||||
{/* Created At - Secondary */}
|
||||
<div className="text-xs text-gray-500 truncate mt-0.5" title={createdAt || "Unknown date"}>
|
||||
{createdAt || "Unknown date"}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
header: "Updated At",
|
||||
header: () => <span className="text-sm font-semibold">Updated At</span>,
|
||||
accessorKey: "model_info.updated_at",
|
||||
sortingFn: "datetime",
|
||||
cell: ({ row }) => {
|
||||
|
|
@ -121,51 +161,45 @@ export const columns = (
|
|||
},
|
||||
},
|
||||
{
|
||||
header: "Created By",
|
||||
accessorKey: "model_info.created_by",
|
||||
cell: ({ row }) => {
|
||||
const model = row.original;
|
||||
return (
|
||||
<span className="text-xs">
|
||||
{model.model_info.created_by || "-"}
|
||||
</span>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
header: () => (
|
||||
<Tooltip title="Cost per 1M tokens">
|
||||
<span>Input Cost</span>
|
||||
</Tooltip>
|
||||
),
|
||||
header: () => <span className="text-sm font-semibold">Costs</span>,
|
||||
accessorKey: "input_cost",
|
||||
size: 120, // Fixed column width
|
||||
cell: ({ row }) => {
|
||||
const model = row.original;
|
||||
const inputCost = model.input_cost;
|
||||
const outputCost = model.output_cost;
|
||||
|
||||
// If both costs are missing or undefined, show "-"
|
||||
if (!inputCost && !outputCost) {
|
||||
return (
|
||||
<div className="max-w-[120px]">
|
||||
<span className="text-xs text-gray-400">-</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<pre className="text-xs">
|
||||
{model.input_cost || "-"}
|
||||
</pre>
|
||||
<Tooltip title="Cost per 1M tokens">
|
||||
<div className="flex flex-col min-w-0 max-w-[120px]">
|
||||
{/* Input Cost - Primary */}
|
||||
{inputCost && (
|
||||
<div className="text-xs font-medium text-gray-900 truncate">
|
||||
In: ${inputCost}
|
||||
</div>
|
||||
)}
|
||||
{/* Output Cost - Secondary */}
|
||||
{outputCost && (
|
||||
<div className="text-xs text-gray-500 truncate mt-0.5">
|
||||
Out: ${outputCost}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Tooltip>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
header: () => (
|
||||
<Tooltip title="Cost per 1M tokens">
|
||||
<span>Output Cost</span>
|
||||
</Tooltip>
|
||||
),
|
||||
accessorKey: "output_cost",
|
||||
cell: ({ row }) => {
|
||||
const model = row.original;
|
||||
return (
|
||||
<pre className="text-xs">
|
||||
{model.output_cost || "-"}
|
||||
</pre>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
header: "Team ID",
|
||||
header: () => <span className="text-sm font-semibold">Team ID</span>,
|
||||
accessorKey: "model_info.team_id",
|
||||
cell: ({ row }) => {
|
||||
const model = row.original;
|
||||
|
|
@ -188,7 +222,7 @@ export const columns = (
|
|||
},
|
||||
},
|
||||
{
|
||||
header: "Model Access Group",
|
||||
header: () => <span className="text-sm font-semibold">Model Access Group</span>,
|
||||
accessorKey: "model_info.model_access_group",
|
||||
enableSorting: false,
|
||||
cell: ({ row }) => {
|
||||
|
|
@ -252,23 +286,7 @@ export const columns = (
|
|||
},
|
||||
},
|
||||
{
|
||||
header: "Credentials",
|
||||
accessorKey: "litellm_credential_name",
|
||||
cell: ({ row }) => {
|
||||
const model = row.original;
|
||||
return model.litellm_params && model.litellm_params.litellm_credential_name ? (
|
||||
<div className="overflow-hidden">
|
||||
<Tooltip title={model.litellm_params.litellm_credential_name}>
|
||||
{model.litellm_params.litellm_credential_name.slice(0, 7)}...
|
||||
</Tooltip>
|
||||
</div>
|
||||
) : (
|
||||
<span className="text-gray-400">-</span>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
header: "Status",
|
||||
header: () => <span className="text-sm font-semibold">Status</span>,
|
||||
accessorKey: "model_info.db_model",
|
||||
cell: ({ row }) => {
|
||||
const model = row.original;
|
||||
|
|
@ -292,17 +310,6 @@ export const columns = (
|
|||
const canEditModel = userRole === "Admin" || model.model_info?.created_by === userID;
|
||||
return (
|
||||
<div className="flex items-center justify-end gap-2 pr-4">
|
||||
<Icon
|
||||
icon={PencilAltIcon}
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
if (canEditModel) {
|
||||
setSelectedModelId(model.model_info.id);
|
||||
setEditModel(true);
|
||||
}
|
||||
}}
|
||||
className={!canEditModel ? "opacity-50 cursor-not-allowed" : "cursor-pointer"}
|
||||
/>
|
||||
<Icon
|
||||
icon={TrashIcon}
|
||||
size="sm"
|
||||
File diff suppressed because it is too large
Load diff
Loading…
Add table
Reference in a new issue