From 4bcf77e54e6c20981c357e0b292001ce555694f3 Mon Sep 17 00:00:00 2001 From: jesus Date: Sun, 11 Oct 2026 00:13:34 +0000 Subject: [PATCH] fix(ui): label session rows by their destination Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- .../RequestLogsPanel.integration.test.tsx | 16 ++++++ .../logs/request/RequestLogsPanel.tsx | 4 +- .../logs/request/RequestLogsTable.tsx | 3 +- ...questLogsTableColumns.integration.test.tsx | 3 +- .../logs/request/logDetailRouting.test.ts | 53 +++++++++++++++++++ .../logs/request/logDetailRouting.ts | 13 +++++ 6 files changed, 88 insertions(+), 4 deletions(-) create mode 100644 ui/litellm-dashboard/src/components/logs/request/logDetailRouting.test.ts diff --git a/ui/litellm-dashboard/src/components/logs/request/RequestLogsPanel.integration.test.tsx b/ui/litellm-dashboard/src/components/logs/request/RequestLogsPanel.integration.test.tsx index c78a89e2993..63d667d332e 100644 --- a/ui/litellm-dashboard/src/components/logs/request/RequestLogsPanel.integration.test.tsx +++ b/ui/litellm-dashboard/src/components/logs/request/RequestLogsPanel.integration.test.tsx @@ -558,6 +558,22 @@ describe("RequestLogsPanel", () => { expect(urlParams().get("log_id")).toBe("req-keyboard"); }); + it("opens a multi-request session from its focused row and exposes its accessible name", async () => { + const user = userEvent.setup(); + respondWith([ + logEntry({ request_id: "req-session-keyboard", session_id: "sess-keyboard", session_total_count: 3 }), + ]); + renderPanel(); + + const sessionRow = await screen.findByRole("row", { name: "Open session sess-keyboard" }); + sessionRow.focus(); + expect(sessionRow).toHaveFocus(); + await user.keyboard("{Enter}"); + + await waitFor(() => expect(drawer()).toHaveAttribute("data-session-id", "sess-keyboard")); + expect(urlParams().get("log_id")).toBe("req-session-keyboard"); + }); + it("opens the drawer on load when ?log_id= matches a log in the loaded page", async () => { respondWith([logEntry({ request_id: "req-1" }), logEntry({ request_id: "req-2" })]); renderPanel("?log_id=req-2"); diff --git a/ui/litellm-dashboard/src/components/logs/request/RequestLogsPanel.tsx b/ui/litellm-dashboard/src/components/logs/request/RequestLogsPanel.tsx index 605ff373c7c..c91ef5028fb 100644 --- a/ui/litellm-dashboard/src/components/logs/request/RequestLogsPanel.tsx +++ b/ui/litellm-dashboard/src/components/logs/request/RequestLogsPanel.tsx @@ -22,7 +22,7 @@ import { type PaginatedResponse, useLogFilterLogic, } from "./useLogFilterLogic"; -import { useLogDetailRouting } from "./logDetailRouting"; +import { isMultiRequestSession, useLogDetailRouting } from "./logDetailRouting"; import { LogDetailsDrawer } from "../detail"; import { defaultLogsTimeRange, @@ -253,7 +253,7 @@ export default function RequestLogsPanel({ accessToken, token, userRole, userID, const handleRowClick = useCallback( (log: LogEntry) => { setSelectedLog(log); - if (log.session_id && (log.session_total_count || 1) > 1) { + if (isMultiRequestSession(log) && log.session_id) { openSession(log.session_id, log.request_id); } else { openLog(log.request_id); diff --git a/ui/litellm-dashboard/src/components/logs/request/RequestLogsTable.tsx b/ui/litellm-dashboard/src/components/logs/request/RequestLogsTable.tsx index 0a88b4de97e..3378c4c4e76 100644 --- a/ui/litellm-dashboard/src/components/logs/request/RequestLogsTable.tsx +++ b/ui/litellm-dashboard/src/components/logs/request/RequestLogsTable.tsx @@ -13,6 +13,7 @@ import { CREDENTIAL_LABELS, SPAN_TYPE_LABELS } from "../constants"; import { LOG_FILTER_IDS, LOG_FILTER_LABELS, type LogsWindow } from "./useLogFilterLogic"; import { RequestLogsFilters } from "./RequestLogsFilters"; import { getRequestLogsTableColumns } from "./RequestLogsTableColumns"; +import { requestLogRowLabel } from "./logDetailRouting"; interface RequestLogsTableProps { data: LogEntry[]; @@ -116,7 +117,7 @@ export function RequestLogsTable({ noDataMessage={} size="compact" onRowClick={onRowClick} - getRowLabel={(row) => `Open request ${row.request_id}`} + getRowLabel={requestLogRowLabel} toolbar={(table) => ( <> ({ copyToClipboardMock: vi.fn() })); @@ -303,7 +304,7 @@ describe("Request ID column", () => { getRowId={(row) => row.request_id} size="compact" onRowClick={onRowClick} - getRowLabel={(row) => `Open request ${row.request_id}`} + getRowLabel={requestLogRowLabel} />, ); diff --git a/ui/litellm-dashboard/src/components/logs/request/logDetailRouting.test.ts b/ui/litellm-dashboard/src/components/logs/request/logDetailRouting.test.ts new file mode 100644 index 00000000000..8c7a8fc47bc --- /dev/null +++ b/ui/litellm-dashboard/src/components/logs/request/logDetailRouting.test.ts @@ -0,0 +1,53 @@ +import { describe, expect, it } from "vitest"; + +import type { LogEntry } from "../types"; +import { isMultiRequestSession, requestLogRowLabel } from "./logDetailRouting"; + +const logEntry = (overrides: Partial = {}): LogEntry => ({ + request_id: "r1", + api_key: "key-1", + team_id: "team-1", + model: "gpt-4o", + model_id: "model-1", + call_type: "acompletion", + spend: 0, + total_tokens: 0, + prompt_tokens: 0, + completion_tokens: 0, + startTime: "2026-07-07T09:50:13Z", + endTime: "2026-07-07T09:50:14Z", + cache_hit: "false", + messages: [], + response: {}, + ...overrides, +}); + +describe("Request Logs row labels", () => { + it("labels a multi-request session by its session id", () => { + const log = logEntry({ session_id: "s1", session_total_count: 3 }); + + expect(isMultiRequestSession(log)).toBe(true); + expect(requestLogRowLabel(log)).toBe("Open session s1"); + }); + + it("labels a single-request session by its request id", () => { + const log = logEntry({ session_id: "s1", session_total_count: 1 }); + + expect(isMultiRequestSession(log)).toBe(false); + expect(requestLogRowLabel(log)).toBe("Open request r1"); + }); + + it("labels a request without a session by its request id", () => { + const log = logEntry({ session_total_count: 3 }); + + expect(isMultiRequestSession(log)).toBe(false); + expect(requestLogRowLabel(log)).toBe("Open request r1"); + }); + + it("defaults an undefined session count to a request label", () => { + const log = logEntry({ request_id: "", session_id: "s1", session_total_count: undefined }); + + expect(isMultiRequestSession(log)).toBe(false); + expect(requestLogRowLabel(log)).toBe("Open request"); + }); +}); diff --git a/ui/litellm-dashboard/src/components/logs/request/logDetailRouting.ts b/ui/litellm-dashboard/src/components/logs/request/logDetailRouting.ts index 967164053c3..b53c0280284 100644 --- a/ui/litellm-dashboard/src/components/logs/request/logDetailRouting.ts +++ b/ui/litellm-dashboard/src/components/logs/request/logDetailRouting.ts @@ -1,9 +1,22 @@ import { parseAsString, useQueryStates } from "nuqs"; import { useCallback } from "react"; +import type { LogEntry } from "../types"; + export const LOG_ID_QUERY_PARAM = "log_id"; export const SESSION_ID_QUERY_PARAM = "session_id"; +export function isMultiRequestSession(log: LogEntry): boolean { + return Boolean(log.session_id) && (log.session_total_count || 1) > 1; +} + +export function requestLogRowLabel(log: LogEntry): string { + if (isMultiRequestSession(log)) { + return `Open session ${log.session_id}`; + } + return log.request_id ? `Open request ${log.request_id}` : "Open request"; +} + export interface LogDetailRouting { logId: string | null; sessionId: string | null;