From 34db5f4813ab3449ef489a17b9d7b3da9d7c6635 Mon Sep 17 00:00:00 2001 From: Thibault Serot Date: Wed, 8 Jul 2026 16:26:47 +1000 Subject: [PATCH 1/4] feat(ui): add start time sort toggle to session logs sidebar --- .../LogDetailsDrawer.test.tsx | 105 ++++++++++++++++++ .../LogDetailsDrawer/LogDetailsDrawer.tsx | 39 ++++--- .../view_logs/LogDetailsDrawer/utils.test.ts | 30 +++++ .../view_logs/LogDetailsDrawer/utils.ts | 21 ++++ 4 files changed, 179 insertions(+), 16 deletions(-) create mode 100644 ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailsDrawer.test.tsx create mode 100644 ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/utils.test.ts diff --git a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailsDrawer.test.tsx b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailsDrawer.test.tsx new file mode 100644 index 00000000000..db0d168fcac --- /dev/null +++ b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailsDrawer.test.tsx @@ -0,0 +1,105 @@ +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { fireEvent, render, screen, waitFor } from "@testing-library/react"; +import { describe, expect, it, vi } from "vitest"; +import { LogDetailsDrawer } from "./LogDetailsDrawer"; +import { sessionSpendLogsCall } from "../../networking"; +import { LogEntry } from "../columns"; + +vi.mock("../../networking", () => ({ + sessionSpendLogsCall: vi.fn(), +})); + +vi.mock("@/app/(dashboard)/hooks/logDetails/useLogDetails", () => ({ + useLogDetails: () => ({ data: null, isLoading: false }), +})); + +vi.mock("./LogDetailContent", () => ({ + LogDetailContent: () => null, + GuardrailJumpLink: () => null, +})); + +vi.mock("./DrawerHeader", () => ({ + DrawerHeader: () => null, +})); + +const makeLog = (overrides: Partial): LogEntry => ({ + request_id: "req", + api_key: "", + team_id: "", + model: "", + model_id: "", + call_type: "acompletion", + spend: 0, + total_tokens: 0, + prompt_tokens: 0, + completion_tokens: 0, + startTime: "2026-07-08T10:00:00.000Z", + endTime: "2026-07-08T10:00:01.000Z", + cache_hit: "false", + messages: [], + response: {}, + ...overrides, +}); + +const sessionLogs = [ + makeLog({ + request_id: "llm-early", + model: "llm-early", + startTime: "2026-07-08T10:00:00.000Z", + endTime: "2026-07-08T10:00:02.000Z", + }), + makeLog({ + request_id: "mcp-early", + model: "tool-early", + call_type: "call_mcp_tool", + startTime: "2026-07-08T10:00:01.000Z", + endTime: "2026-07-08T10:00:01.500Z", + }), + makeLog({ + request_id: "llm-late", + model: "llm-late", + startTime: "2026-07-08T10:00:02.000Z", + endTime: "2026-07-08T10:00:04.000Z", + }), + makeLog({ + request_id: "mcp-late", + model: "tool-late", + call_type: "call_mcp_tool", + startTime: "2026-07-08T10:00:03.000Z", + endTime: "2026-07-08T10:00:03.500Z", + }), +]; + +const renderSessionDrawer = () => { + vi.mocked(sessionSpendLogsCall).mockResolvedValue({ data: sessionLogs, total: 4, total_pages: 1 }); + const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false } } }); + render( + + {}} logEntry={null} sessionId="session-1" accessToken="token" /> + , + ); +}; + +const sidebarEventNames = () => + screen.queryAllByText(/^(llm-early|llm-late|tool-early|tool-late)$/).map((el) => el.textContent); + +describe("LogDetailsDrawer session sidebar sorting", () => { + it("defaults to grouped order: LLM calls newest first, MCP calls grouped last", async () => { + renderSessionDrawer(); + await waitFor(() => expect(sidebarEventNames()).toHaveLength(4)); + expect(sidebarEventNames()).toEqual(["llm-late", "llm-early", "tool-late", "tool-early"]); + }); + + it("switches to chronological order across LLM and MCP calls when Start time is selected", async () => { + renderSessionDrawer(); + await waitFor(() => expect(sidebarEventNames()).toHaveLength(4)); + + fireEvent.click(screen.getByText("Start time")); + + await waitFor(() => expect(sidebarEventNames()).toEqual(["llm-early", "tool-early", "llm-late", "tool-late"])); + + fireEvent.click(screen.getByText("Grouped")); + + await waitFor(() => expect(sidebarEventNames()).toEqual(["llm-late", "llm-early", "tool-late", "tool-early"])); + }); +}); diff --git a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailsDrawer.tsx b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailsDrawer.tsx index bf3360a5371..36299139a13 100644 --- a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailsDrawer.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailsDrawer.tsx @@ -1,5 +1,5 @@ import { useEffect, useMemo, useState } from "react"; -import { Button, Drawer } from "antd"; +import { Button, Drawer, Segmented } from "antd"; import { CheckOutlined, CopyOutlined, LeftOutlined, RightOutlined } from "@ant-design/icons"; import { Bot, Sparkles, Wrench } from "lucide-react"; import { LogEntry } from "../columns"; @@ -11,7 +11,7 @@ import { LogDetailContent, GuardrailJumpLink } from "./LogDetailContent"; import { sessionSpendLogsCall } from "../../networking"; import { useQuery } from "@tanstack/react-query"; import { getSpendString } from "@/utils/dataUtils"; -import { normalizeGuardrailEntries } from "./utils"; +import { normalizeGuardrailEntries, sortSessionLogs, SessionLogSortMode } from "./utils"; import { DRAWER_WIDTH } from "./constants"; import { useLogDetails } from "@/app/(dashboard)/hooks/logDetails/useLogDetails"; @@ -117,6 +117,7 @@ export function LogDetailsDrawer({ }: LogDetailsDrawerProps) { const isSessionMode = Boolean(sessionId); const [selectedSessionRequestId, setSelectedSessionRequestId] = useState(null); + const [sessionSortMode, setSessionSortMode] = useState("grouped"); const [isSidebarCollapsed, setIsSidebarCollapsed] = useState(false); const [copiedLeftPanelId, setCopiedLeftPanelId] = useState(false); @@ -152,26 +153,20 @@ export function LogDetailsDrawer({ // backend omits total, so the truncation note reflects what was fetched. const total: number = firstPage.total ?? rows.length; - const logs = rows - .map((row) => ({ - ...row, - request_duration_ms: row.request_duration_ms ?? Date.parse(row.endTime) - Date.parse(row.startTime), - })) - .sort((a, b) => { - const aIsMcp = MCP_CALL_TYPES.includes(a.call_type) ? 1 : 0; - const bIsMcp = MCP_CALL_TYPES.includes(b.call_type) ? 1 : 0; - if (aIsMcp !== bIsMcp) return aIsMcp - bIsMcp; - // Newest first, matching the all-sessions logs overview. MCP calls - // stay grouped last (above), newest-first within that group too. - return new Date(b.startTime).getTime() - new Date(a.startTime).getTime(); - }); + const logs = rows.map((row) => ({ + ...row, + request_duration_ms: row.request_duration_ms ?? Date.parse(row.endTime) - Date.parse(row.startTime), + })); return { logs, total }; }, enabled: Boolean(open && isSessionMode && sessionId && accessToken), }); - const sessionLogs: LogEntry[] = sessionData?.logs ?? []; + const sessionLogs: LogEntry[] = useMemo( + () => sortSessionLogs(sessionData?.logs ?? [], sessionSortMode), + [sessionData, sessionSortMode], + ); // total reported by the backend; when the page cap truncates the fetch this // exceeds sessionLogs.length, which drives the "showing most recent" note. const sessionTotalCount = sessionData?.total ?? sessionLogs.length; @@ -391,6 +386,18 @@ export function LogDetailsDrawer({ Showing most recent {logsForList.length} of {sessionTotalCount} )} + {isSessionMode && ( + setSessionSortMode(value as SessionLogSortMode)} + /> + )}
diff --git a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/utils.test.ts b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/utils.test.ts new file mode 100644 index 00000000000..cbe12f5c101 --- /dev/null +++ b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/utils.test.ts @@ -0,0 +1,30 @@ +import { describe, expect, it } from "vitest"; +import { sortSessionLogs } from "./utils"; + +const llm = (id: string, startTime: string) => ({ request_id: id, call_type: "acompletion", startTime }); +const mcp = (id: string, startTime: string) => ({ request_id: id, call_type: "call_mcp_tool", startTime }); + +const ids = (rows: { request_id: string }[]) => rows.map((row) => row.request_id); + +describe("sortSessionLogs", () => { + const rows = [ + mcp("mcp-early", "2026-07-08T10:00:01.000Z"), + llm("llm-late", "2026-07-08T10:00:02.000Z"), + mcp("mcp-late", "2026-07-08T10:00:03.000Z"), + llm("llm-early", "2026-07-08T10:00:00.000Z"), + ]; + + it("grouped mode keeps MCP calls last, newest first within each group", () => { + expect(ids(sortSessionLogs(rows, "grouped"))).toEqual(["llm-late", "llm-early", "mcp-late", "mcp-early"]); + }); + + it("chronological mode interleaves all calls by start time, oldest first", () => { + expect(ids(sortSessionLogs(rows, "chronological"))).toEqual(["llm-early", "mcp-early", "llm-late", "mcp-late"]); + }); + + it("does not mutate the input array", () => { + const input = [...rows]; + sortSessionLogs(input, "chronological"); + expect(ids(input)).toEqual(ids(rows)); + }); +}); diff --git a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/utils.ts b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/utils.ts index 61301cf5b54..5a1a0e81f96 100644 --- a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/utils.ts +++ b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/utils.ts @@ -3,6 +3,27 @@ * These functions handle data formatting, validation, and guardrail calculations. */ +import { MCP_CALL_TYPES } from "../constants"; + +export type SessionLogSortMode = "grouped" | "chronological"; + +export function sortSessionLogs( + rows: T[], + mode: SessionLogSortMode, +): T[] { + if (mode === "chronological") { + return [...rows].sort((a, b) => new Date(a.startTime).getTime() - new Date(b.startTime).getTime()); + } + return [...rows].sort((a, b) => { + const aIsMcp = MCP_CALL_TYPES.includes(a.call_type) ? 1 : 0; + const bIsMcp = MCP_CALL_TYPES.includes(b.call_type) ? 1 : 0; + if (aIsMcp !== bIsMcp) return aIsMcp - bIsMcp; + // Newest first, matching the all-sessions logs overview. MCP calls + // stay grouped last (above), newest-first within that group too. + return new Date(b.startTime).getTime() - new Date(a.startTime).getTime(); + }); +} + /** * Formats data for display. If input is a string, attempts to parse as JSON. * @param input - Data to format (string or object) From df2d44bab1e2c2ffd3acf68bbc0abf6ab1160f85 Mon Sep 17 00:00:00 2001 From: Thibault Serot Date: Wed, 8 Jul 2026 16:54:39 +1000 Subject: [PATCH 2/4] feat(ui): sort session sidebar by duration or start time --- .../LogDetailsDrawer.test.tsx | 12 +++---- .../LogDetailsDrawer/LogDetailsDrawer.tsx | 30 ++++++++-------- .../view_logs/LogDetailsDrawer/utils.test.ts | 36 +++++++++++++------ .../view_logs/LogDetailsDrawer/utils.ts | 23 +++++------- 4 files changed, 55 insertions(+), 46 deletions(-) diff --git a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailsDrawer.test.tsx b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailsDrawer.test.tsx index db0d168fcac..1d23fecb5da 100644 --- a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailsDrawer.test.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailsDrawer.test.tsx @@ -53,13 +53,13 @@ const sessionLogs = [ model: "tool-early", call_type: "call_mcp_tool", startTime: "2026-07-08T10:00:01.000Z", - endTime: "2026-07-08T10:00:01.500Z", + endTime: "2026-07-08T10:00:06.000Z", }), makeLog({ request_id: "llm-late", model: "llm-late", startTime: "2026-07-08T10:00:02.000Z", - endTime: "2026-07-08T10:00:04.000Z", + endTime: "2026-07-08T10:00:05.000Z", }), makeLog({ request_id: "mcp-late", @@ -84,10 +84,10 @@ const sidebarEventNames = () => screen.queryAllByText(/^(llm-early|llm-late|tool-early|tool-late)$/).map((el) => el.textContent); describe("LogDetailsDrawer session sidebar sorting", () => { - it("defaults to grouped order: LLM calls newest first, MCP calls grouped last", async () => { + it("defaults to duration order, longest call first across LLM and MCP calls", async () => { renderSessionDrawer(); await waitFor(() => expect(sidebarEventNames()).toHaveLength(4)); - expect(sidebarEventNames()).toEqual(["llm-late", "llm-early", "tool-late", "tool-early"]); + expect(sidebarEventNames()).toEqual(["tool-early", "llm-late", "llm-early", "tool-late"]); }); it("switches to chronological order across LLM and MCP calls when Start time is selected", async () => { @@ -98,8 +98,8 @@ describe("LogDetailsDrawer session sidebar sorting", () => { await waitFor(() => expect(sidebarEventNames()).toEqual(["llm-early", "tool-early", "llm-late", "tool-late"])); - fireEvent.click(screen.getByText("Grouped")); + fireEvent.click(screen.getByText("Duration")); - await waitFor(() => expect(sidebarEventNames()).toEqual(["llm-late", "llm-early", "tool-late", "tool-early"])); + await waitFor(() => expect(sidebarEventNames()).toEqual(["tool-early", "llm-late", "llm-early", "tool-late"])); }); }); diff --git a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailsDrawer.tsx b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailsDrawer.tsx index 36299139a13..79592216942 100644 --- a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailsDrawer.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailsDrawer.tsx @@ -117,7 +117,7 @@ export function LogDetailsDrawer({ }: LogDetailsDrawerProps) { const isSessionMode = Boolean(sessionId); const [selectedSessionRequestId, setSelectedSessionRequestId] = useState(null); - const [sessionSortMode, setSessionSortMode] = useState("grouped"); + const [sessionSortMode, setSessionSortMode] = useState("duration"); const [isSidebarCollapsed, setIsSidebarCollapsed] = useState(false); const [copiedLeftPanelId, setCopiedLeftPanelId] = useState(false); @@ -173,9 +173,9 @@ export function LogDetailsDrawer({ const sessionTruncated = sessionTotalCount > sessionLogs.length; // Default selection for a freshly opened session: the most recent log (latest - // startTime). The list is sorted newest-first, but MCP calls are grouped last, - // so the latest log by time is not necessarily sessionLogs[0]; compute it - // explicitly. A clicked/remembered log still wins over this default. + // startTime). The list is ordered by the selected sort mode, so the latest + // log by time is not necessarily sessionLogs[0]; compute it explicitly. + // A clicked/remembered log still wins over this default. const mostRecentLog = useMemo( () => sessionLogs.reduce( @@ -387,16 +387,18 @@ export function LogDetailsDrawer({
)} {isSessionMode && ( - setSessionSortMode(value as SessionLogSortMode)} - /> +
+ Sort by + setSessionSortMode(value as SessionLogSortMode)} + /> +
)} diff --git a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/utils.test.ts b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/utils.test.ts index cbe12f5c101..f59a20529d0 100644 --- a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/utils.test.ts +++ b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/utils.test.ts @@ -1,30 +1,44 @@ import { describe, expect, it } from "vitest"; import { sortSessionLogs } from "./utils"; -const llm = (id: string, startTime: string) => ({ request_id: id, call_type: "acompletion", startTime }); -const mcp = (id: string, startTime: string) => ({ request_id: id, call_type: "call_mcp_tool", startTime }); +const log = (id: string, startTime: string, endTime: string, request_duration_ms?: number) => ({ + request_id: id, + startTime, + endTime, + request_duration_ms, +}); const ids = (rows: { request_id: string }[]) => rows.map((row) => row.request_id); describe("sortSessionLogs", () => { const rows = [ - mcp("mcp-early", "2026-07-08T10:00:01.000Z"), - llm("llm-late", "2026-07-08T10:00:02.000Z"), - mcp("mcp-late", "2026-07-08T10:00:03.000Z"), - llm("llm-early", "2026-07-08T10:00:00.000Z"), + log("mid-duration", "2026-07-08T10:00:01.000Z", "2026-07-08T10:00:01.500Z", 2000), + log("longest", "2026-07-08T10:00:02.000Z", "2026-07-08T10:00:02.500Z", 5000), + log("shortest", "2026-07-08T10:00:03.000Z", "2026-07-08T10:00:03.500Z", 300), + log("earliest-no-duration-field", "2026-07-08T10:00:00.000Z", "2026-07-08T10:00:04.000Z"), ]; - it("grouped mode keeps MCP calls last, newest first within each group", () => { - expect(ids(sortSessionLogs(rows, "grouped"))).toEqual(["llm-late", "llm-early", "mcp-late", "mcp-early"]); + it("duration mode sorts longest call first, deriving duration from timestamps when the field is missing", () => { + expect(ids(sortSessionLogs(rows, "duration"))).toEqual([ + "longest", + "earliest-no-duration-field", + "mid-duration", + "shortest", + ]); }); - it("chronological mode interleaves all calls by start time, oldest first", () => { - expect(ids(sortSessionLogs(rows, "chronological"))).toEqual(["llm-early", "mcp-early", "llm-late", "mcp-late"]); + it("start_time mode sorts calls in the order they started", () => { + expect(ids(sortSessionLogs(rows, "start_time"))).toEqual([ + "earliest-no-duration-field", + "mid-duration", + "longest", + "shortest", + ]); }); it("does not mutate the input array", () => { const input = [...rows]; - sortSessionLogs(input, "chronological"); + sortSessionLogs(input, "duration"); expect(ids(input)).toEqual(ids(rows)); }); }); diff --git a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/utils.ts b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/utils.ts index 5a1a0e81f96..d313d07361c 100644 --- a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/utils.ts +++ b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/utils.ts @@ -3,25 +3,18 @@ * These functions handle data formatting, validation, and guardrail calculations. */ -import { MCP_CALL_TYPES } from "../constants"; +export type SessionLogSortMode = "duration" | "start_time"; -export type SessionLogSortMode = "grouped" | "chronological"; +type SortableSessionLog = { startTime: string; endTime: string; request_duration_ms?: number }; -export function sortSessionLogs( - rows: T[], - mode: SessionLogSortMode, -): T[] { - if (mode === "chronological") { +const durationMs = (row: SortableSessionLog): number => + row.request_duration_ms ?? Date.parse(row.endTime) - Date.parse(row.startTime); + +export function sortSessionLogs(rows: T[], mode: SessionLogSortMode): T[] { + if (mode === "start_time") { return [...rows].sort((a, b) => new Date(a.startTime).getTime() - new Date(b.startTime).getTime()); } - return [...rows].sort((a, b) => { - const aIsMcp = MCP_CALL_TYPES.includes(a.call_type) ? 1 : 0; - const bIsMcp = MCP_CALL_TYPES.includes(b.call_type) ? 1 : 0; - if (aIsMcp !== bIsMcp) return aIsMcp - bIsMcp; - // Newest first, matching the all-sessions logs overview. MCP calls - // stay grouped last (above), newest-first within that group too. - return new Date(b.startTime).getTime() - new Date(a.startTime).getTime(); - }); + return [...rows].sort((a, b) => durationMs(b) - durationMs(a)); } /** From 5d89be551bbed49c493f2a1cea0bddf4ea8e468b Mon Sep 17 00:00:00 2001 From: Thibault Serot Date: Wed, 8 Jul 2026 17:06:38 +1000 Subject: [PATCH 3/4] fix(ui): fit session sort toggle inside sidebar column --- .../LogDetailsDrawer/LogDetailsDrawer.tsx | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailsDrawer.tsx b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailsDrawer.tsx index 79592216942..92cf90fad63 100644 --- a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailsDrawer.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailsDrawer.tsx @@ -387,18 +387,17 @@ export function LogDetailsDrawer({ )} {isSessionMode && ( -
- Sort by - setSessionSortMode(value as SessionLogSortMode)} - /> -
+ setSessionSortMode(value as SessionLogSortMode)} + /> )} From 6d2090a21b19d6277d44367983d9d70ee10e8a0c Mon Sep 17 00:00:00 2001 From: Thibault Serot Date: Wed, 8 Jul 2026 17:17:44 +1000 Subject: [PATCH 4/4] fix(ui): reset session sort mode when drawer closes --- .../LogDetailsDrawer.test.tsx | 21 ++++++++++++++++--- .../LogDetailsDrawer/LogDetailsDrawer.tsx | 1 + 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailsDrawer.test.tsx b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailsDrawer.test.tsx index 1d23fecb5da..5a49cccec70 100644 --- a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailsDrawer.test.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailsDrawer.test.tsx @@ -73,11 +73,13 @@ const sessionLogs = [ const renderSessionDrawer = () => { vi.mocked(sessionSpendLogsCall).mockResolvedValue({ data: sessionLogs, total: 4, total_pages: 1 }); const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false } } }); - render( + const drawer = (open: boolean) => ( - {}} logEntry={null} sessionId="session-1" accessToken="token" /> - , + {}} logEntry={null} sessionId="session-1" accessToken="token" /> + ); + const { rerender } = render(drawer(true)); + return { rerender, drawer }; }; const sidebarEventNames = () => @@ -102,4 +104,17 @@ describe("LogDetailsDrawer session sidebar sorting", () => { await waitFor(() => expect(sidebarEventNames()).toEqual(["tool-early", "llm-late", "llm-early", "tool-late"])); }); + + it("resets the sort mode back to duration when the drawer is closed and reopened", async () => { + const { rerender, drawer } = renderSessionDrawer(); + await waitFor(() => expect(sidebarEventNames()).toHaveLength(4)); + + fireEvent.click(screen.getByText("Start time")); + await waitFor(() => expect(sidebarEventNames()).toEqual(["llm-early", "tool-early", "llm-late", "tool-late"])); + + rerender(drawer(false)); + rerender(drawer(true)); + + await waitFor(() => expect(sidebarEventNames()).toEqual(["tool-early", "llm-late", "llm-early", "tool-late"])); + }); }); diff --git a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailsDrawer.tsx b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailsDrawer.tsx index 92cf90fad63..cc087a611b0 100644 --- a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailsDrawer.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailsDrawer.tsx @@ -217,6 +217,7 @@ export function LogDetailsDrawer({ setIsSidebarCollapsed(false); } else { if (isSessionMode) setSelectedSessionRequestId(null); + setSessionSortMode("duration"); setCopiedLeftPanelId(false); } }, [open, isSessionMode]);