From 0669d1b3cb7152c3ebc58618dd766a41705503c0 Mon Sep 17 00:00:00 2001 From: Yuneng Jiang Date: Thu, 25 Jun 2026 17:31:13 -0700 Subject: [PATCH 1/7] =?UTF-8?q?bump:=20version=200.1.43=20=E2=86=92=200.1.?= =?UTF-8?q?44?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- enterprise/pyproject.toml | 4 ++-- pyproject.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/enterprise/pyproject.toml b/enterprise/pyproject.toml index b032942427c..66f6aeb7abc 100644 --- a/enterprise/pyproject.toml +++ b/enterprise/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "litellm-enterprise" -version = "0.1.43" +version = "0.1.44" description = "Package for LiteLLM Enterprise features" readme = "README.md" requires-python = ">=3.9" @@ -26,7 +26,7 @@ required-version = ">=0.10.9" module-root = "" [tool.commitizen] -version = "0.1.43" +version = "0.1.44" version_files = [ "pyproject.toml:^version", "../pyproject.toml:litellm-enterprise==", diff --git a/pyproject.toml b/pyproject.toml index 6a6a47f540e..6e99d81f8f3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -63,7 +63,7 @@ proxy = [ "azure-storage-blob>=12.28.0,<13.0", "mcp>=1.26.0,<2.0", "litellm-proxy-extras==0.4.74", - "litellm-enterprise==0.1.43", + "litellm-enterprise==0.1.44", "RestrictedPython>=8.1,<9.0", "rich>=13.9.4,<14.0", "polars>=1.38.1,<2.0", From 2c8cd6ad4dc65371ac3b7f3dc58206c6120773ac Mon Sep 17 00:00:00 2001 From: Yuneng Jiang Date: Thu, 25 Jun 2026 17:35:56 -0700 Subject: [PATCH 2/7] uv lock --- uv.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/uv.lock b/uv.lock index 917dff39e38..da44ad25715 100644 --- a/uv.lock +++ b/uv.lock @@ -9,7 +9,7 @@ resolution-markers = [ ] [options] -exclude-newer = "2026-06-22T19:05:55.080417Z" +exclude-newer = "2026-06-23T00:31:52.495979Z" exclude-newer-span = "P3D" [manifest] @@ -3597,7 +3597,7 @@ proxy-dev = [ [[package]] name = "litellm-enterprise" -version = "0.1.43" +version = "0.1.44" source = { editable = "enterprise" } [[package]] From 4a6f0dbd8c4af8eb56d0ee6a60eaf7139d051931 Mon Sep 17 00:00:00 2001 From: Yuneng Jiang Date: Fri, 26 Jun 2026 00:10:14 -0700 Subject: [PATCH 3/7] fix(ui): size Request Logs table columns so it scrolls instead of overflowing Tremor's Table forwards className to a wrapper div rather than the inner table element, so the table-fixed class never reached the table and it stayed table-layout: auto. Across 16 whitespace-nowrap columns that expanded the table far past the viewport Give each spend-logs column an explicit pixel size and drive the table width from getCenterTotalSize(), matching the Virtual Keys table. The shared DataTable applies this only when columns declare sizes, so the other consumers keep their existing fluid layout --- .../src/components/view_logs/columns.tsx | 16 +++++++ .../src/components/view_logs/table.test.tsx | 44 +++++++++++++++++++ .../src/components/view_logs/table.tsx | 15 ++++++- 3 files changed, 73 insertions(+), 2 deletions(-) create mode 100644 ui/litellm-dashboard/src/components/view_logs/table.test.tsx diff --git a/ui/litellm-dashboard/src/components/view_logs/columns.tsx b/ui/litellm-dashboard/src/components/view_logs/columns.tsx index 1265b8449de..aeeb31f78f3 100644 --- a/ui/litellm-dashboard/src/components/view_logs/columns.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/columns.tsx @@ -119,11 +119,13 @@ export const createColumns = (sortProps?: LogsSortProps): ColumnDef[] ) : "Time", accessorKey: "startTime", + size: 200, cell: (info: any) => , }, { header: "Type", id: "type", + size: 90, cell: (info: any) => { const row = info.row.original; const sessionCount = row.session_total_count || 1; @@ -168,6 +170,7 @@ export const createColumns = (sortProps?: LogsSortProps): ColumnDef[] { header: "Status", accessorKey: "metadata.status", + size: 100, cell: (info: any) => { const status = info.getValue() || "Success"; const isSuccess = status.toLowerCase() !== "failure"; @@ -186,6 +189,7 @@ export const createColumns = (sortProps?: LogsSortProps): ColumnDef[] { header: "Session ID", accessorKey: "session_id", + size: 160, cell: (info: any) => { const value = String(info.getValue() || ""); const onSessionClick = info.row.original.onSessionClick; @@ -207,6 +211,7 @@ export const createColumns = (sortProps?: LogsSortProps): ColumnDef[] { header: "Request ID", accessorKey: "request_id", + size: 160, cell: (info: any) => ( {String(info.getValue() || "")} @@ -226,6 +231,7 @@ export const createColumns = (sortProps?: LogsSortProps): ColumnDef[] ) : "Cost", accessorKey: "spend", + size: 110, cell: (info: any) => { const row = info.row.original; const mcpCount = row.mcp_tool_call_count || 0; @@ -258,6 +264,7 @@ export const createColumns = (sortProps?: LogsSortProps): ColumnDef[] ) : "Duration (s)", accessorKey: "request_duration_ms", + size: 120, cell: (info: any) => { const ms = info.getValue(); if (ms == null) return -; @@ -282,6 +289,7 @@ export const createColumns = (sortProps?: LogsSortProps): ColumnDef[] ) : "TTFT (s)", accessorKey: "completionStartTime", + size: 110, cell: (info: any) => { const row = info.row.original; const completionStartTime = info.getValue(); @@ -301,6 +309,7 @@ export const createColumns = (sortProps?: LogsSortProps): ColumnDef[] { header: "Team Name", accessorKey: "metadata.user_api_key_team_alias", + size: 150, cell: (info: any) => ( {String(info.getValue() || "-")} @@ -310,6 +319,7 @@ export const createColumns = (sortProps?: LogsSortProps): ColumnDef[] { header: "Key Hash", accessorKey: "metadata.user_api_key", + size: 160, cell: (info: any) => { const value = String(info.getValue() || "-"); const onKeyHashClick = info.row.original.onKeyHashClick; @@ -329,6 +339,7 @@ export const createColumns = (sortProps?: LogsSortProps): ColumnDef[] { header: "Key Alias", accessorKey: "metadata.user_api_key_alias", + size: 150, cell: (info: any) => ( {String(info.getValue() || "-")} @@ -348,6 +359,7 @@ export const createColumns = (sortProps?: LogsSortProps): ColumnDef[] ) : "Model", accessorKey: "model", + size: 200, cell: (info: any) => { const row = info.row.original; const provider = row.custom_llm_provider; @@ -385,6 +397,7 @@ export const createColumns = (sortProps?: LogsSortProps): ColumnDef[] ) : "Tokens", accessorKey: "total_tokens", + size: 140, cell: (info: any) => { const row = info.row.original; return ( @@ -400,6 +413,7 @@ export const createColumns = (sortProps?: LogsSortProps): ColumnDef[] { header: "Internal User", accessorKey: "user", + size: 150, cell: (info: any) => ( {String(info.getValue() || "-")} @@ -409,6 +423,7 @@ export const createColumns = (sortProps?: LogsSortProps): ColumnDef[] { header: "End User", accessorKey: "end_user", + size: 140, cell: (info: any) => ( {String(info.getValue() || "-")} @@ -419,6 +434,7 @@ export const createColumns = (sortProps?: LogsSortProps): ColumnDef[] { header: "Tags", accessorKey: "request_tags", + size: 150, cell: (info: any) => { const tags = info.getValue(); if (!tags || Object.keys(tags).length === 0) return "-"; diff --git a/ui/litellm-dashboard/src/components/view_logs/table.test.tsx b/ui/litellm-dashboard/src/components/view_logs/table.test.tsx new file mode 100644 index 00000000000..f88e9bd75c8 --- /dev/null +++ b/ui/litellm-dashboard/src/components/view_logs/table.test.tsx @@ -0,0 +1,44 @@ +import type { ColumnDef } from "@tanstack/react-table"; +import { render, screen } from "@testing-library/react"; +import { describe, expect, it } from "vitest"; +import { DataTable } from "./table"; + +type Row = { request_id: string; a: string; b: string }; + +const data: Row[] = [{ request_id: "r1", a: "alpha", b: "beta" }]; + +const sizedColumns: ColumnDef[] = [ + { header: "A", accessorKey: "a", size: 120 }, + { header: "B", accessorKey: "b", size: 80 }, +]; + +const unsizedColumns: ColumnDef[] = [ + { header: "A", accessorKey: "a" }, + { header: "B", accessorKey: "b" }, +]; + +describe("DataTable column sizing", () => { + it("widths the table and every cell from column sizes when columns declare them", () => { + render(); + + expect(screen.getByRole("table").style.width).toBe("200px"); + + const headers = screen.getAllByRole("columnheader"); + expect(headers.map((h) => h.style.width)).toEqual(["120px", "80px"]); + + const cells = screen.getAllByRole("cell"); + expect(cells.map((c) => c.style.width)).toEqual(["120px", "80px"]); + }); + + it("leaves cells unsized and keeps the fluid table when no column declares a size", () => { + render(); + + const table = screen.getByRole("table"); + expect(table.style.width).toBe(""); + expect(table.style.minWidth).toBe("400px"); + + for (const cell of [...screen.getAllByRole("columnheader"), ...screen.getAllByRole("cell")]) { + expect(cell.style.width).toBe(""); + } + }); +}); diff --git a/ui/litellm-dashboard/src/components/view_logs/table.tsx b/ui/litellm-dashboard/src/components/view_logs/table.tsx index 6aa349513d5..a47bf5a8e5c 100644 --- a/ui/litellm-dashboard/src/components/view_logs/table.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/table.tsx @@ -41,6 +41,7 @@ export function DataTable({ enableSorting = false, }: DataTableProps) { const supportsExpansion = !!(renderSubComponent || renderChildRows) && !!getRowCanExpand; + const hasExplicitColumnSizes = columns.some((column) => column.size !== undefined); const [sorting, setSorting] = useState([]); const table = useReactTable({ @@ -63,9 +64,14 @@ export function DataTable({ ...(supportsExpansion && { getExpandedRowModel: getExpandedRowModel() }), }); + const tableClassName = hasExplicitColumnSizes + ? "[&_td]:py-0.5 [&_th]:py-1 [&_table]:table-fixed" + : "[&_td]:py-0.5 [&_th]:py-1 table-fixed w-full box-border"; + const tableStyle = hasExplicitColumnSizes ? { width: table.getCenterTotalSize() } : { minWidth: "400px" }; + return (
- +
{table.getHeaderGroups().map((headerGroup) => ( @@ -77,6 +83,7 @@ export function DataTable({ {header.isPlaceholder ? null : ( @@ -112,7 +119,11 @@ export function DataTable({ onClick={() => onRowClick?.(row.original)} > {row.getVisibleCells().map((cell) => ( - + {flexRender(cell.column.columnDef.cell, cell.getContext())} ))} From 014754be947e60cbefa1a3ecaae0a68a05a7443f Mon Sep 17 00:00:00 2001 From: Yuneng Jiang Date: Fri, 26 Jun 2026 18:34:07 -0700 Subject: [PATCH 4/7] fix(ui): let dashboard main pane shrink so wide tables scroll instead of overflowing The Request Logs page pushed the whole page past the viewport horizontally. The cause was the app shell flex layout:
is a flex item, and flex items default to min-width: auto, so they refuse to shrink below their content's intrinsic width. The logs table is intrinsically ~2300px across its 16 nowrap columns, so main grew to that width and dragged the page with it; the table's own overflow-x-auto wrapper never got the chance to scroll Add min-w-0 to main so it can shrink to the available width, at which point the existing overflow-x-auto wrapper engages and the table scrolls inside its card. This applies to every dashboard page, not just logs Also drop the dead max-w-screen class on the logs container (not a real Tailwind utility, so it was a no-op), and revert the earlier column-sizing attempt which targeted table-layout rather than the actual containment problem --- .../src/app/(dashboard)/layout.tsx | 2 +- .../src/components/view_logs/columns.tsx | 16 ------- .../src/components/view_logs/index.tsx | 2 +- .../src/components/view_logs/table.test.tsx | 44 ------------------- .../src/components/view_logs/table.tsx | 15 +------ 5 files changed, 4 insertions(+), 75 deletions(-) delete mode 100644 ui/litellm-dashboard/src/components/view_logs/table.test.tsx diff --git a/ui/litellm-dashboard/src/app/(dashboard)/layout.tsx b/ui/litellm-dashboard/src/app/(dashboard)/layout.tsx index a5e83436888..09951dc1923 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/layout.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/layout.tsx @@ -126,7 +126,7 @@ function DashboardShell({ children }: { children: React.ReactNode }) {
-
{children}
+
{children}
)} diff --git a/ui/litellm-dashboard/src/components/view_logs/columns.tsx b/ui/litellm-dashboard/src/components/view_logs/columns.tsx index aeeb31f78f3..1265b8449de 100644 --- a/ui/litellm-dashboard/src/components/view_logs/columns.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/columns.tsx @@ -119,13 +119,11 @@ export const createColumns = (sortProps?: LogsSortProps): ColumnDef[] ) : "Time", accessorKey: "startTime", - size: 200, cell: (info: any) => , }, { header: "Type", id: "type", - size: 90, cell: (info: any) => { const row = info.row.original; const sessionCount = row.session_total_count || 1; @@ -170,7 +168,6 @@ export const createColumns = (sortProps?: LogsSortProps): ColumnDef[] { header: "Status", accessorKey: "metadata.status", - size: 100, cell: (info: any) => { const status = info.getValue() || "Success"; const isSuccess = status.toLowerCase() !== "failure"; @@ -189,7 +186,6 @@ export const createColumns = (sortProps?: LogsSortProps): ColumnDef[] { header: "Session ID", accessorKey: "session_id", - size: 160, cell: (info: any) => { const value = String(info.getValue() || ""); const onSessionClick = info.row.original.onSessionClick; @@ -211,7 +207,6 @@ export const createColumns = (sortProps?: LogsSortProps): ColumnDef[] { header: "Request ID", accessorKey: "request_id", - size: 160, cell: (info: any) => ( {String(info.getValue() || "")} @@ -231,7 +226,6 @@ export const createColumns = (sortProps?: LogsSortProps): ColumnDef[] ) : "Cost", accessorKey: "spend", - size: 110, cell: (info: any) => { const row = info.row.original; const mcpCount = row.mcp_tool_call_count || 0; @@ -264,7 +258,6 @@ export const createColumns = (sortProps?: LogsSortProps): ColumnDef[] ) : "Duration (s)", accessorKey: "request_duration_ms", - size: 120, cell: (info: any) => { const ms = info.getValue(); if (ms == null) return -; @@ -289,7 +282,6 @@ export const createColumns = (sortProps?: LogsSortProps): ColumnDef[] ) : "TTFT (s)", accessorKey: "completionStartTime", - size: 110, cell: (info: any) => { const row = info.row.original; const completionStartTime = info.getValue(); @@ -309,7 +301,6 @@ export const createColumns = (sortProps?: LogsSortProps): ColumnDef[] { header: "Team Name", accessorKey: "metadata.user_api_key_team_alias", - size: 150, cell: (info: any) => ( {String(info.getValue() || "-")} @@ -319,7 +310,6 @@ export const createColumns = (sortProps?: LogsSortProps): ColumnDef[] { header: "Key Hash", accessorKey: "metadata.user_api_key", - size: 160, cell: (info: any) => { const value = String(info.getValue() || "-"); const onKeyHashClick = info.row.original.onKeyHashClick; @@ -339,7 +329,6 @@ export const createColumns = (sortProps?: LogsSortProps): ColumnDef[] { header: "Key Alias", accessorKey: "metadata.user_api_key_alias", - size: 150, cell: (info: any) => ( {String(info.getValue() || "-")} @@ -359,7 +348,6 @@ export const createColumns = (sortProps?: LogsSortProps): ColumnDef[] ) : "Model", accessorKey: "model", - size: 200, cell: (info: any) => { const row = info.row.original; const provider = row.custom_llm_provider; @@ -397,7 +385,6 @@ export const createColumns = (sortProps?: LogsSortProps): ColumnDef[] ) : "Tokens", accessorKey: "total_tokens", - size: 140, cell: (info: any) => { const row = info.row.original; return ( @@ -413,7 +400,6 @@ export const createColumns = (sortProps?: LogsSortProps): ColumnDef[] { header: "Internal User", accessorKey: "user", - size: 150, cell: (info: any) => ( {String(info.getValue() || "-")} @@ -423,7 +409,6 @@ export const createColumns = (sortProps?: LogsSortProps): ColumnDef[] { header: "End User", accessorKey: "end_user", - size: 140, cell: (info: any) => ( {String(info.getValue() || "-")} @@ -434,7 +419,6 @@ export const createColumns = (sortProps?: LogsSortProps): ColumnDef[] { header: "Tags", accessorKey: "request_tags", - size: 150, cell: (info: any) => { const tags = info.getValue(); if (!tags || Object.keys(tags).length === 0) return "-"; diff --git a/ui/litellm-dashboard/src/components/view_logs/index.tsx b/ui/litellm-dashboard/src/components/view_logs/index.tsx index 6c5fd03f0a0..cfe1bd6025a 100644 --- a/ui/litellm-dashboard/src/components/view_logs/index.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/index.tsx @@ -234,7 +234,7 @@ export default function SpendLogsTable({ accessToken, token, userRole, userID, p }; return ( -
+
setActiveTab(index === 0 ? "request logs" : "audit logs")}> Request Logs diff --git a/ui/litellm-dashboard/src/components/view_logs/table.test.tsx b/ui/litellm-dashboard/src/components/view_logs/table.test.tsx deleted file mode 100644 index f88e9bd75c8..00000000000 --- a/ui/litellm-dashboard/src/components/view_logs/table.test.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import type { ColumnDef } from "@tanstack/react-table"; -import { render, screen } from "@testing-library/react"; -import { describe, expect, it } from "vitest"; -import { DataTable } from "./table"; - -type Row = { request_id: string; a: string; b: string }; - -const data: Row[] = [{ request_id: "r1", a: "alpha", b: "beta" }]; - -const sizedColumns: ColumnDef[] = [ - { header: "A", accessorKey: "a", size: 120 }, - { header: "B", accessorKey: "b", size: 80 }, -]; - -const unsizedColumns: ColumnDef[] = [ - { header: "A", accessorKey: "a" }, - { header: "B", accessorKey: "b" }, -]; - -describe("DataTable column sizing", () => { - it("widths the table and every cell from column sizes when columns declare them", () => { - render(); - - expect(screen.getByRole("table").style.width).toBe("200px"); - - const headers = screen.getAllByRole("columnheader"); - expect(headers.map((h) => h.style.width)).toEqual(["120px", "80px"]); - - const cells = screen.getAllByRole("cell"); - expect(cells.map((c) => c.style.width)).toEqual(["120px", "80px"]); - }); - - it("leaves cells unsized and keeps the fluid table when no column declares a size", () => { - render(); - - const table = screen.getByRole("table"); - expect(table.style.width).toBe(""); - expect(table.style.minWidth).toBe("400px"); - - for (const cell of [...screen.getAllByRole("columnheader"), ...screen.getAllByRole("cell")]) { - expect(cell.style.width).toBe(""); - } - }); -}); diff --git a/ui/litellm-dashboard/src/components/view_logs/table.tsx b/ui/litellm-dashboard/src/components/view_logs/table.tsx index a47bf5a8e5c..6aa349513d5 100644 --- a/ui/litellm-dashboard/src/components/view_logs/table.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/table.tsx @@ -41,7 +41,6 @@ export function DataTable({ enableSorting = false, }: DataTableProps) { const supportsExpansion = !!(renderSubComponent || renderChildRows) && !!getRowCanExpand; - const hasExplicitColumnSizes = columns.some((column) => column.size !== undefined); const [sorting, setSorting] = useState([]); const table = useReactTable({ @@ -64,14 +63,9 @@ export function DataTable({ ...(supportsExpansion && { getExpandedRowModel: getExpandedRowModel() }), }); - const tableClassName = hasExplicitColumnSizes - ? "[&_td]:py-0.5 [&_th]:py-1 [&_table]:table-fixed" - : "[&_td]:py-0.5 [&_th]:py-1 table-fixed w-full box-border"; - const tableStyle = hasExplicitColumnSizes ? { width: table.getCenterTotalSize() } : { minWidth: "400px" }; - return (
-
+
{table.getHeaderGroups().map((headerGroup) => ( @@ -83,7 +77,6 @@ export function DataTable({ {header.isPlaceholder ? null : ( @@ -119,11 +112,7 @@ export function DataTable({ onClick={() => onRowClick?.(row.original)} > {row.getVisibleCells().map((cell) => ( - + {flexRender(cell.column.columnDef.cell, cell.getContext())} ))} From 76be4461cad2dfe89fb2930d42eb99a65546d004 Mon Sep 17 00:00:00 2001 From: Yuneng Jiang Date: Fri, 26 Jun 2026 19:05:33 -0700 Subject: [PATCH 5/7] feat(ui): give Request Logs columns explicit widths and tighten the dense ones Now that the page-overflow bug is fixed by letting the main pane shrink, bring back per-column sizing purely to control widths. Columns declare explicit pixel sizes and the table derives its min-width from getCenterTotalSize(), so it stretches to fill a wide card but scrolls once the columns no longer fit. The shared DataTable applies this only when columns declare sizes, leaving the other consumers on their existing fluid layout Trim the columns that were eating horizontal space without earning it: Request ID and Key Hash drop ~30% (Key Hash now narrower than Key Alias, which is the more useful of the two), and Duration and TTFT shrink to fit their short numeric values --- .../src/components/view_logs/columns.tsx | 16 +++++++ .../src/components/view_logs/table.test.tsx | 46 +++++++++++++++++++ .../src/components/view_logs/table.tsx | 15 +++++- 3 files changed, 75 insertions(+), 2 deletions(-) create mode 100644 ui/litellm-dashboard/src/components/view_logs/table.test.tsx diff --git a/ui/litellm-dashboard/src/components/view_logs/columns.tsx b/ui/litellm-dashboard/src/components/view_logs/columns.tsx index 1265b8449de..310316d205e 100644 --- a/ui/litellm-dashboard/src/components/view_logs/columns.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/columns.tsx @@ -119,11 +119,13 @@ export const createColumns = (sortProps?: LogsSortProps): ColumnDef[] ) : "Time", accessorKey: "startTime", + size: 200, cell: (info: any) => , }, { header: "Type", id: "type", + size: 90, cell: (info: any) => { const row = info.row.original; const sessionCount = row.session_total_count || 1; @@ -168,6 +170,7 @@ export const createColumns = (sortProps?: LogsSortProps): ColumnDef[] { header: "Status", accessorKey: "metadata.status", + size: 100, cell: (info: any) => { const status = info.getValue() || "Success"; const isSuccess = status.toLowerCase() !== "failure"; @@ -186,6 +189,7 @@ export const createColumns = (sortProps?: LogsSortProps): ColumnDef[] { header: "Session ID", accessorKey: "session_id", + size: 160, cell: (info: any) => { const value = String(info.getValue() || ""); const onSessionClick = info.row.original.onSessionClick; @@ -207,6 +211,7 @@ export const createColumns = (sortProps?: LogsSortProps): ColumnDef[] { header: "Request ID", accessorKey: "request_id", + size: 110, cell: (info: any) => ( {String(info.getValue() || "")} @@ -226,6 +231,7 @@ export const createColumns = (sortProps?: LogsSortProps): ColumnDef[] ) : "Cost", accessorKey: "spend", + size: 110, cell: (info: any) => { const row = info.row.original; const mcpCount = row.mcp_tool_call_count || 0; @@ -258,6 +264,7 @@ export const createColumns = (sortProps?: LogsSortProps): ColumnDef[] ) : "Duration (s)", accessorKey: "request_duration_ms", + size: 90, cell: (info: any) => { const ms = info.getValue(); if (ms == null) return -; @@ -282,6 +289,7 @@ export const createColumns = (sortProps?: LogsSortProps): ColumnDef[] ) : "TTFT (s)", accessorKey: "completionStartTime", + size: 80, cell: (info: any) => { const row = info.row.original; const completionStartTime = info.getValue(); @@ -301,6 +309,7 @@ export const createColumns = (sortProps?: LogsSortProps): ColumnDef[] { header: "Team Name", accessorKey: "metadata.user_api_key_team_alias", + size: 150, cell: (info: any) => ( {String(info.getValue() || "-")} @@ -310,6 +319,7 @@ export const createColumns = (sortProps?: LogsSortProps): ColumnDef[] { header: "Key Hash", accessorKey: "metadata.user_api_key", + size: 110, cell: (info: any) => { const value = String(info.getValue() || "-"); const onKeyHashClick = info.row.original.onKeyHashClick; @@ -329,6 +339,7 @@ export const createColumns = (sortProps?: LogsSortProps): ColumnDef[] { header: "Key Alias", accessorKey: "metadata.user_api_key_alias", + size: 150, cell: (info: any) => ( {String(info.getValue() || "-")} @@ -348,6 +359,7 @@ export const createColumns = (sortProps?: LogsSortProps): ColumnDef[] ) : "Model", accessorKey: "model", + size: 200, cell: (info: any) => { const row = info.row.original; const provider = row.custom_llm_provider; @@ -385,6 +397,7 @@ export const createColumns = (sortProps?: LogsSortProps): ColumnDef[] ) : "Tokens", accessorKey: "total_tokens", + size: 140, cell: (info: any) => { const row = info.row.original; return ( @@ -400,6 +413,7 @@ export const createColumns = (sortProps?: LogsSortProps): ColumnDef[] { header: "Internal User", accessorKey: "user", + size: 150, cell: (info: any) => ( {String(info.getValue() || "-")} @@ -409,6 +423,7 @@ export const createColumns = (sortProps?: LogsSortProps): ColumnDef[] { header: "End User", accessorKey: "end_user", + size: 140, cell: (info: any) => ( {String(info.getValue() || "-")} @@ -419,6 +434,7 @@ export const createColumns = (sortProps?: LogsSortProps): ColumnDef[] { header: "Tags", accessorKey: "request_tags", + size: 150, cell: (info: any) => { const tags = info.getValue(); if (!tags || Object.keys(tags).length === 0) return "-"; diff --git a/ui/litellm-dashboard/src/components/view_logs/table.test.tsx b/ui/litellm-dashboard/src/components/view_logs/table.test.tsx new file mode 100644 index 00000000000..da9bcef1455 --- /dev/null +++ b/ui/litellm-dashboard/src/components/view_logs/table.test.tsx @@ -0,0 +1,46 @@ +import type { ColumnDef } from "@tanstack/react-table"; +import { render, screen } from "@testing-library/react"; +import { describe, expect, it } from "vitest"; +import { DataTable } from "./table"; + +type Row = { request_id: string; a: string; b: string }; + +const data: Row[] = [{ request_id: "r1", a: "alpha", b: "beta" }]; + +const sizedColumns: ColumnDef[] = [ + { header: "A", accessorKey: "a", size: 120 }, + { header: "B", accessorKey: "b", size: 80 }, +]; + +const unsizedColumns: ColumnDef[] = [ + { header: "A", accessorKey: "a" }, + { header: "B", accessorKey: "b" }, +]; + +describe("DataTable column sizing", () => { + it("min-widths the table to the column total and sizes every cell when columns declare sizes", () => { + render(); + + const table = screen.getByRole("table"); + expect(table.style.minWidth).toBe("200px"); + expect(table.style.width).toBe(""); + + const headers = screen.getAllByRole("columnheader"); + expect(headers.map((h) => h.style.width)).toEqual(["120px", "80px"]); + + const cells = screen.getAllByRole("cell"); + expect(cells.map((c) => c.style.width)).toEqual(["120px", "80px"]); + }); + + it("leaves cells unsized and keeps the fluid table when no column declares a size", () => { + render(); + + const table = screen.getByRole("table"); + expect(table.style.width).toBe(""); + expect(table.style.minWidth).toBe("400px"); + + for (const cell of [...screen.getAllByRole("columnheader"), ...screen.getAllByRole("cell")]) { + expect(cell.style.width).toBe(""); + } + }); +}); diff --git a/ui/litellm-dashboard/src/components/view_logs/table.tsx b/ui/litellm-dashboard/src/components/view_logs/table.tsx index 6aa349513d5..4510cc9a1f0 100644 --- a/ui/litellm-dashboard/src/components/view_logs/table.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/table.tsx @@ -41,6 +41,7 @@ export function DataTable({ enableSorting = false, }: DataTableProps) { const supportsExpansion = !!(renderSubComponent || renderChildRows) && !!getRowCanExpand; + const hasExplicitColumnSizes = columns.some((column) => column.size !== undefined); const [sorting, setSorting] = useState([]); const table = useReactTable({ @@ -63,9 +64,14 @@ export function DataTable({ ...(supportsExpansion && { getExpandedRowModel: getExpandedRowModel() }), }); + const tableClassName = hasExplicitColumnSizes + ? "[&_td]:py-0.5 [&_th]:py-1 [&_table]:table-fixed" + : "[&_td]:py-0.5 [&_th]:py-1 table-fixed w-full box-border"; + const tableStyle = hasExplicitColumnSizes ? { minWidth: table.getCenterTotalSize() } : { minWidth: "400px" }; + return (
-
+
{table.getHeaderGroups().map((headerGroup) => ( @@ -77,6 +83,7 @@ export function DataTable({ {header.isPlaceholder ? null : ( @@ -112,7 +119,11 @@ export function DataTable({ onClick={() => onRowClick?.(row.original)} > {row.getVisibleCells().map((cell) => ( - + {flexRender(cell.column.columnDef.cell, cell.getContext())} ))} From 84d7a320201edc57c4ab6a293a08375909bbb3fd Mon Sep 17 00:00:00 2001 From: Yuneng Jiang Date: Mon, 29 Jun 2026 21:08:40 -0700 Subject: [PATCH 6/7] fix(ui): revert Duration and TTFT column widths to default The explicit 90px/80px sizes were too narrow for the Duration (s) and TTFT (s) headers once the sort arrows were factored in, cramping the header labels. Dropping the size lets these two columns fall back to the default width like before --- ui/litellm-dashboard/src/components/view_logs/columns.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/ui/litellm-dashboard/src/components/view_logs/columns.tsx b/ui/litellm-dashboard/src/components/view_logs/columns.tsx index 310316d205e..73eecf9c01e 100644 --- a/ui/litellm-dashboard/src/components/view_logs/columns.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/columns.tsx @@ -264,7 +264,6 @@ export const createColumns = (sortProps?: LogsSortProps): ColumnDef[] ) : "Duration (s)", accessorKey: "request_duration_ms", - size: 90, cell: (info: any) => { const ms = info.getValue(); if (ms == null) return -; @@ -289,7 +288,6 @@ export const createColumns = (sortProps?: LogsSortProps): ColumnDef[] ) : "TTFT (s)", accessorKey: "completionStartTime", - size: 80, cell: (info: any) => { const row = info.row.original; const completionStartTime = info.getValue(); From 256b5aadfbf5168facfd1add1cfc956dce44773b Mon Sep 17 00:00:00 2001 From: Yuneng Jiang Date: Mon, 29 Jun 2026 21:16:59 -0700 Subject: [PATCH 7/7] fix(ui): revert Request ID width to default, tighten Session ID Drop the explicit size on Request ID so it falls back to the default width like the other reverted columns. Narrow Session ID from 160px to 120px since its truncated value needs less room --- ui/litellm-dashboard/src/components/view_logs/columns.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ui/litellm-dashboard/src/components/view_logs/columns.tsx b/ui/litellm-dashboard/src/components/view_logs/columns.tsx index 73eecf9c01e..8de67e6ae19 100644 --- a/ui/litellm-dashboard/src/components/view_logs/columns.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/columns.tsx @@ -189,7 +189,7 @@ export const createColumns = (sortProps?: LogsSortProps): ColumnDef[] { header: "Session ID", accessorKey: "session_id", - size: 160, + size: 120, cell: (info: any) => { const value = String(info.getValue() || ""); const onSessionClick = info.row.original.onSessionClick; @@ -211,7 +211,6 @@ export const createColumns = (sortProps?: LogsSortProps): ColumnDef[] { header: "Request ID", accessorKey: "request_id", - size: 110, cell: (info: any) => ( {String(info.getValue() || "")}