Merge pull request #22122 from BerriAI/litellm_ui_spend_logs_duration

[Feature] UI - Logs: Use backend request_duration_ms and make Duration sortable
This commit is contained in:
yuneng-jiang
2026-02-25 16:47:22 -08:00
committed by GitHub
7 changed files with 33 additions and 17 deletions
@@ -23,7 +23,7 @@ const createLogEntry = (overrides: Partial<LogEntry> = {}): LogEntry =>
startTime: "2025-11-14T00:00:00Z",
endTime: "2025-11-14T00:00:01Z",
cache_hit: "miss",
duration: 1,
request_duration_ms: 1000,
messages: [{ role: "user", content: "hello" }],
response: { choices: [{ message: { content: "hi" } }] },
metadata: { status: "success" },
@@ -283,7 +283,7 @@ function MetricsSection({ logEntry, metadata }: { logEntry: LogEntry; metadata:
/>
</Descriptions.Item>
<Descriptions.Item label="Cost">${formatNumberWithCommas(logEntry.spend || 0, 8)}</Descriptions.Item>
<Descriptions.Item label="Duration">{logEntry.duration?.toFixed(3)} s</Descriptions.Item>
<Descriptions.Item label="Duration">{logEntry.request_duration_ms != null ? (logEntry.request_duration_ms / 1000).toFixed(3) : "-"} s</Descriptions.Item>
{hasCacheActivity && (
<>
@@ -47,8 +47,8 @@ interface TraceEventRowProps {
function TraceEventRow({ row, isSelected, onClick }: TraceEventRowProps) {
const isMcp = MCP_CALL_TYPES.includes(row.call_type);
const durationValue =
row.duration != null
? row.duration.toFixed(3)
row.request_duration_ms != null
? (row.request_duration_ms / 1000).toFixed(3)
: row.startTime && row.endTime
? ((Date.parse(row.endTime) - Date.parse(row.startTime)) / 1000).toFixed(3)
: "-";
@@ -125,7 +125,7 @@ export function LogDetailsDrawer({
return allSessionLogs
.map((row) => ({
...row,
duration: (Date.parse(row.endTime) - Date.parse(row.startTime)) / 1000,
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;
@@ -21,7 +21,7 @@ const baseLogEntry: LogEntry = {
startTime: "2025-11-14T00:00:00Z",
endTime: "2025-11-14T00:00:00Z",
cache_hit: "miss",
duration: 1,
request_duration_ms: 1000,
messages: [{ role: "user", content: "hello" }],
response: { status: "ok" },
metadata: {
@@ -14,6 +14,7 @@ export const LOGS_SORT_FIELD_MAP = {
startTime: "startTime",
spend: "spend",
total_tokens: "total_tokens",
request_duration_ms: "request_duration_ms",
} as const;
export type LogsSortField = keyof typeof LOGS_SORT_FIELD_MAP;
@@ -61,7 +62,7 @@ export type LogEntry = {
proxy_server_request?: string | any[] | Record<string, any>;
session_id?: string;
status?: string;
duration?: number;
request_duration_ms?: number;
session_total_count?: number;
session_total_spend?: number;
mcp_tool_call_count?: number;
@@ -231,13 +232,28 @@ export const createColumns = (sortProps?: LogsSortProps): ColumnDef<LogEntry>[]
},
},
{
header: "Duration (s)",
accessorKey: "duration",
cell: (info: any) => (
<Tooltip title={String(info.getValue() || "-")}>
<span className="max-w-[15ch] truncate block">{String(info.getValue() || "-")}</span>
</Tooltip>
),
header: sortProps
? () => (
<SortableHeader
label="Duration (s)"
field="request_duration_ms"
sortBy={sortProps.sortBy}
sortOrder={sortProps.sortOrder}
onSortChange={sortProps.onSortChange}
/>
)
: "Duration (s)",
accessorKey: "request_duration_ms",
cell: (info: any) => {
const ms = info.getValue();
if (ms == null) return <span>-</span>;
const seconds = (ms / 1000).toFixed(2);
return (
<Tooltip title={`${ms}ms`}>
<span className="max-w-[15ch] truncate block">{seconds}</span>
</Tooltip>
);
},
},
{
header: "Team Name",
@@ -55,7 +55,7 @@ const baseLogEntry: LogEntry = {
startTime: "2025-11-14T00:00:00Z",
endTime: "2025-11-14T00:00:00Z",
cache_hit: "miss",
duration: 1,
request_duration_ms: 1000,
messages: [{ role: "user", content: "hello" }],
response: { status: "ok" },
metadata: {
@@ -341,7 +341,7 @@ export default function SpendLogsTable({
const sessionComposition = log.session_id ? sessionCompositionById[log.session_id] : undefined;
return {
...log,
duration: (Date.parse(log.endTime) - Date.parse(log.startTime)) / 1000,
request_duration_ms: log.request_duration_ms,
session_llm_count: sessionComposition?.llm ?? undefined,
session_mcp_count: sessionComposition?.mcp ?? undefined,
onKeyHashClick: (keyHash: string) => setSelectedKeyIdInfoView(keyHash),
@@ -943,7 +943,7 @@ export function RequestViewer({ row, onOpenSettings }: { row: Row<LogEntry>; onO
</div>
<div className="flex">
<span className="font-medium w-1/3">Duration:</span>
<span>{row.original.duration} s.</span>
<span>{row.original.request_duration_ms != null ? (row.original.request_duration_ms / 1000).toFixed(3) : "-"} s.</span>
</div>
{row.original.metadata?.litellm_overhead_time_ms !== undefined && (
<div className="flex">