Fix request log detail token layout (#1096)

Integrated into release/v3.5.9
This commit is contained in:
Randi
2026-04-09 20:15:14 -04:00
committed by GitHub
parent f118082f6b
commit 9f55159bd5
2 changed files with 107 additions and 22 deletions

View File

@@ -109,6 +109,15 @@ export default function RequestLoggerDetail({ log, detail, loading, onClose, onC
: [];
const requestJson = detail?.requestBody ? toPrettyJson(detail.requestBody) : null;
const responseJson = detail?.responseBody ? toPrettyJson(detail.responseBody) : null;
const tokenStats = {
totalIn: detail?.tokens?.in ?? log.tokens?.in ?? 0,
totalOut: detail?.tokens?.out ?? log.tokens?.out ?? 0,
cacheRead: detail?.tokens?.cacheRead ?? log.tokens?.cacheRead,
cacheWrite: detail?.tokens?.cacheWrite ?? log.tokens?.cacheWrite,
reasoning: detail?.tokens?.reasoning ?? log.tokens?.reasoning,
};
const formatTokenValue = (value) => (value != null ? value.toLocaleString() : "N/A");
return (
<div
@@ -146,9 +155,14 @@ export default function RequestLoggerDetail({ log, detail, loading, onClose, onC
<div className="p-6 flex flex-col gap-6">
{/* Metadata Grid */}
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 p-4 bg-bg-subtle rounded-xl border border-border">
<div
className="grid grid-cols-2 md:grid-cols-4 gap-4 p-4 bg-bg-subtle rounded-xl border border-border"
data-testid="request-log-metadata-grid"
>
<div>
<div className="text-[10px] text-text-muted uppercase tracking-wider mb-1">Time</div>
<div className="text-[10px] text-text-muted uppercase tracking-wider mb-1">
Completed Time
</div>
<div className="text-sm font-medium">{formatDate(log.timestamp)}</div>
</div>
<div>
@@ -158,33 +172,29 @@ export default function RequestLoggerDetail({ log, detail, loading, onClose, onC
<div className="text-sm font-medium">{formatDuration(log.duration)}</div>
</div>
<div>
<div className="text-[10px] text-text-muted uppercase tracking-wider mb-1">
Tokens
</div>
<div className="flex flex-wrap items-center gap-1.5">
<div className="text-[10px] text-text-muted uppercase tracking-wider mb-1">Input</div>
<div className="flex flex-wrap items-center gap-1.5" data-testid="token-group-input">
<span className="px-2 py-0.5 rounded bg-primary/20 text-primary text-xs font-bold">
Total In: {(detail?.tokens?.in ?? log.tokens?.in ?? 0).toLocaleString()}
</span>
<span className="px-2 py-0.5 rounded bg-emerald-500/20 text-emerald-700 dark:text-emerald-400 text-xs font-bold">
Total Out: {(detail?.tokens?.out ?? log.tokens?.out ?? 0).toLocaleString()}
Total In: {tokenStats.totalIn.toLocaleString()}
</span>
<span className="px-2 py-0.5 rounded bg-sky-500/20 text-sky-700 dark:text-sky-400 text-xs font-bold">
Cache Read:{" "}
{(detail?.tokens?.cacheRead ?? log.tokens?.cacheRead) != null
? (detail?.tokens?.cacheRead ?? log.tokens?.cacheRead).toLocaleString()
: "N/A"}
Cache Read: {formatTokenValue(tokenStats.cacheRead)}
</span>
<span className="px-2 py-0.5 rounded bg-amber-500/20 text-amber-700 dark:text-amber-400 text-xs font-bold">
Cache Write:{" "}
{(detail?.tokens?.cacheWrite ?? log.tokens?.cacheWrite) != null
? (detail?.tokens?.cacheWrite ?? log.tokens?.cacheWrite).toLocaleString()
: "N/A"}
Cache Write: {formatTokenValue(tokenStats.cacheWrite)}
</span>
</div>
</div>
<div>
<div className="text-[10px] text-text-muted uppercase tracking-wider mb-1">
Output
</div>
<div className="flex flex-wrap items-center gap-1.5" data-testid="token-group-output">
<span className="px-2 py-0.5 rounded bg-emerald-500/20 text-emerald-700 dark:text-emerald-400 text-xs font-bold">
Total Out: {tokenStats.totalOut.toLocaleString()}
</span>
<span className="px-2 py-0.5 rounded bg-violet-500/20 text-violet-700 dark:text-violet-400 text-xs font-bold">
Reasoning:{" "}
{(detail?.tokens?.reasoning ?? log.tokens?.reasoning) != null
? (detail?.tokens?.reasoning ?? log.tokens?.reasoning).toLocaleString()
: "N/A"}
Reasoning: {formatTokenValue(tokenStats.reasoning)}
</span>
</div>
</div>

View File

@@ -0,0 +1,75 @@
import test from "node:test";
import assert from "node:assert/strict";
import React from "react";
import { renderToStaticMarkup } from "react-dom/server";
const { default: RequestLoggerDetail } =
await import("../../src/shared/components/RequestLoggerDetail.tsx");
test("request log detail splits token badges into input and output groups", () => {
const html = renderToStaticMarkup(
React.createElement(RequestLoggerDetail, {
log: {
status: 200,
method: "POST",
path: "/v1/chat/completions",
timestamp: "2026-04-09T21:27:08.000Z",
duration: 2500,
provider: "openai-compatible-sp-openai",
sourceFormat: "openai-chat",
model: "gpt-5.4",
requestedModel: "openai-compatible-sp-openai/gpt-5.4",
account: "main",
apiKeyName: "tools",
apiKeyId: "29d9***7e37",
comboName: "_Latest-Discounted",
tokens: {
in: 21818,
out: 42,
cacheRead: 21632,
cacheWrite: null,
reasoning: null,
},
},
detail: {
account: "main",
apiKeyName: "tools",
apiKeyId: "29d9***7e37",
comboName: "_Latest-Discounted",
requestedModel: "openai-compatible-sp-openai/gpt-5.4",
tokens: {
in: 21818,
out: 42,
cacheRead: 21632,
cacheWrite: null,
reasoning: null,
},
},
loading: false,
onClose: () => {},
onCopy: async () => true,
})
);
const inputLabelIndex = html.indexOf(">Input<");
const outputLabelIndex = html.indexOf(">Output<");
const modelLabelIndex = html.indexOf(">Model<");
const requestedModelLabelIndex = html.indexOf(">Requested Model<");
assert.notEqual(html.indexOf(">Completed Time<"), -1);
assert.equal(html.includes(">Time<"), false);
assert.notEqual(inputLabelIndex, -1);
assert.notEqual(outputLabelIndex, -1);
assert.notEqual(modelLabelIndex, -1);
assert.notEqual(requestedModelLabelIndex, -1);
assert.equal(html.includes(">Tokens<"), false);
assert.equal(inputLabelIndex < outputLabelIndex, true);
assert.equal(outputLabelIndex < modelLabelIndex, true);
assert.equal(modelLabelIndex < requestedModelLabelIndex, true);
assert.match(
html,
/data-testid="token-group-input"[\s\S]*Total In: 21,818[\s\S]*Cache Read: 21,632[\s\S]*Cache Write: N\/A/
);
assert.match(html, /data-testid="token-group-output"[\s\S]*Total Out: 42[\s\S]*Reasoning: N\/A/);
});