From db48411c0ff2c53f06d9f83ca945873b3e0530c7 Mon Sep 17 00:00:00 2001 From: Rick <26716961+Bytechoreographer@users.noreply.github.com> Date: Mon, 2 Nov 2026 15:36:25 +0800 Subject: [PATCH] fix(ui): keep the request log model cell to one model A session row rendered every model the conversation ever used, comma joined. A long conversation produced a huge string that the auto-layout table turned into a very wide Model column, pushing the rest of the columns off screen. Render only the model of the row's own call (the newest call in the session) and show an overflow count. The full model list stays in the hover popup, including a note when the server capped the list. Co-Authored-By: AaronHowell <111272993+AaronHowell@users.noreply.github.com> --- .../request/RequestLogsTableColumns.test.tsx | 36 ++++++++++--- .../logs/request/RequestLogsTableColumns.tsx | 50 ++++++++++++------- 2 files changed, 61 insertions(+), 25 deletions(-) diff --git a/ui/litellm-dashboard/src/components/logs/request/RequestLogsTableColumns.test.tsx b/ui/litellm-dashboard/src/components/logs/request/RequestLogsTableColumns.test.tsx index 2f2246a3878..7aef0f81411 100644 --- a/ui/litellm-dashboard/src/components/logs/request/RequestLogsTableColumns.test.tsx +++ b/ui/litellm-dashboard/src/components/logs/request/RequestLogsTableColumns.test.tsx @@ -368,7 +368,24 @@ describe("Request ID column", () => { }); describe("Model column", () => { - it("lists every model used across a conversation, not only the representative call's model", () => { + it("keeps a long conversation's inline cell to the latest call's model", () => { + const conversationCall: Partial = { + request_id: "req-session", + model: "gpt-5.6", + session_id: "sess-1", + session_total_count: 4, + session_models: ["claude-sonnet-5", "gemini-3-pro", "gpt-5.6", "o4-mini"], + }; + renderRows([logEntry(conversationCall)]); + + expect(screen.getByText("gpt-5.6")).toBeInTheDocument(); + for (const otherModel of ["claude-sonnet-5", "gemini-3-pro", "o4-mini"]) { + expect(screen.queryByText(otherModel)).not.toBeInTheDocument(); + } + }); + + it("reveals the conversation's other models in the hover popup", async () => { + const user = userEvent.setup(); const conversationCall: Partial = { request_id: "req-session", model: "gpt-5.6", @@ -378,12 +395,14 @@ describe("Model column", () => { }; renderRows([logEntry(conversationCall)]); - expect(screen.getByText("claude-sonnet-5, gpt-5.6")).toBeInTheDocument(); - expect(screen.queryByText("gpt-5.6")).not.toBeInTheDocument(); + await user.hover(screen.getByText("+1")); + + expect(await screen.findByText("claude-sonnet-5")).toBeInTheDocument(); }); - it("marks a conversation whose model list was capped by the server", () => { - const cappedCall = { + it("flags a conversation whose model list was capped by the server", async () => { + const user = userEvent.setup(); + const cappedCall: Partial = { request_id: "req-capped", model: "gpt-5.6", session_id: "sess-2", @@ -393,13 +412,16 @@ describe("Model column", () => { }; renderRows([logEntry(cappedCall)]); - expect(screen.getByText("claude-sonnet-5, gpt-5.6, ...")).toBeInTheDocument(); + await user.hover(screen.getByText("+1")); + + expect(await screen.findByText("More models not shown")).toBeInTheDocument(); }); - it("keeps a single call's own model", () => { + it("keeps a single call's own model and shows no overflow marker", () => { renderRows([logEntry({ request_id: "req-single", model: "gpt-5.6" })]); expect(screen.getByText("gpt-5.6")).toBeInTheDocument(); + expect(screen.queryByText(/^\+\d+$/)).not.toBeInTheDocument(); }); }); diff --git a/ui/litellm-dashboard/src/components/logs/request/RequestLogsTableColumns.tsx b/ui/litellm-dashboard/src/components/logs/request/RequestLogsTableColumns.tsx index b3d44361d0a..e4b0343a637 100644 --- a/ui/litellm-dashboard/src/components/logs/request/RequestLogsTableColumns.tsx +++ b/ui/litellm-dashboard/src/components/logs/request/RequestLogsTableColumns.tsx @@ -301,28 +301,42 @@ export const getRequestLogsTableColumns = ({ enableSorting: true, cell: ({ row }) => { const log = row.original; - const provider = log.custom_llm_provider; const sessionModels = log.session_models ?? []; - const modelNames = sessionModels.length > 0 ? sessionModels : [log.model ?? ""]; - const modelLabel = log.session_models_truncated ? `${modelNames.join(", ")}, ...` : modelNames.join(", "); - const isSingleModel = modelNames.length === 1; + const latestModel = log.model || sessionModels[0] || ""; + const otherModels = sessionModels.filter((model) => model !== latestModel); + const models = [latestModel, ...otherModels].filter((model) => model !== ""); + if (models.length === 0) return -; return ( -
- {provider && isSingleModel && ( - { - event.currentTarget.style.display = "none"; - }} - /> - )} +
+ {models.map((model) => ( + {model} + ))} + {log.session_models_truncated === true && ( + More models not shown + )} +
+ } trigger={ - - {modelLabel} + + {log.custom_llm_provider && ( + { + event.currentTarget.style.display = "none"; + }} + /> + )} + {latestModel} + {otherModels.length > 0 && ( + + +{otherModels.length} + + )} } />