From 7db51368dac5ffe6cb301a5ccb4be5ceb80b1b60 Mon Sep 17 00:00:00 2001 From: HiranoMasaaki Date: Fri, 13 Mar 2026 18:44:03 +0000 Subject: [PATCH 1/3] fix: correct cache hit rate calculation and improve display format MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The cache hit rate formula was double-counting cached tokens because inputTokens (from AI SDK's prompt_tokens) already includes cached tokens. The old formula `cached / (input + cached)` produced ~47% when the actual rate was ~88%. Also restructures the token display from: Tokens: In 525.1k · Out 5.1k · Cache 461.3k/46.77% To: Tokens: In 525.1k (Cached 461.3K, Cache Hit 87.85%) · Out 5.1k Co-Authored-By: Claude Opus 4.6 (1M context) --- .changeset/fix-cache-hit-rate.md | 5 ++++ .../execution/components/interface-panel.tsx | 7 ++--- .../execution/hooks/use-delegation-tree.ts | 7 ++--- .../components/log-info-content.tsx | 30 +++++++++++-------- 4 files changed, 27 insertions(+), 22 deletions(-) create mode 100644 .changeset/fix-cache-hit-rate.md diff --git a/.changeset/fix-cache-hit-rate.md b/.changeset/fix-cache-hit-rate.md new file mode 100644 index 00000000..9f5a24ed --- /dev/null +++ b/.changeset/fix-cache-hit-rate.md @@ -0,0 +1,5 @@ +--- +"@perstack/tui-components": patch +--- + +Fix cache hit rate double-counting cached tokens in denominator and improve display format diff --git a/packages/tui-components/src/execution/components/interface-panel.tsx b/packages/tui-components/src/execution/components/interface-panel.tsx index 407f73d0..8cb2668b 100644 --- a/packages/tui-components/src/execution/components/interface-panel.tsx +++ b/packages/tui-components/src/execution/components/interface-panel.tsx @@ -59,12 +59,11 @@ export const InterfacePanel = ({ Tokens: - In {formattedInputTokens} - · Out {formattedOutputTokens} - {" "} - · Cache {formattedCachedInputTokens}/{cacheHitRate}% + In {formattedInputTokens} (Cached {formattedCachedInputTokens}, Cache Hit{" "} + {cacheHitRate}%) + · Out {formattedOutputTokens} {formattedReasoningTokens !== "0" ? ( · Reasoning {formattedReasoningTokens} ) : null} diff --git a/packages/tui-components/src/execution/hooks/use-delegation-tree.ts b/packages/tui-components/src/execution/hooks/use-delegation-tree.ts index 7146b15c..d4231c4a 100644 --- a/packages/tui-components/src/execution/hooks/use-delegation-tree.ts +++ b/packages/tui-components/src/execution/hooks/use-delegation-tree.ts @@ -449,11 +449,8 @@ export function useDelegationTree() { formattedOutputTokens: formatTokenCount(state.jobOutputTokens), providerName: state.providerName, cacheHitRate: - state.jobInputTokens + state.jobCachedInputTokens > 0 - ? ( - (state.jobCachedInputTokens / (state.jobInputTokens + state.jobCachedInputTokens)) * - 100 - ).toFixed(2) + state.jobInputTokens > 0 + ? ((state.jobCachedInputTokens / state.jobInputTokens) * 100).toFixed(2) : "0.00", } } diff --git a/packages/tui-components/src/log-viewer/components/log-info-content.tsx b/packages/tui-components/src/log-viewer/components/log-info-content.tsx index 725831b1..2280783c 100644 --- a/packages/tui-components/src/log-viewer/components/log-info-content.tsx +++ b/packages/tui-components/src/log-viewer/components/log-info-content.tsx @@ -44,8 +44,10 @@ function statusColor(status: string): string { export function JobInfoContent({ job }: { job: Job }): React.ReactNode { const duration = formatDuration(job.startedAt, job.finishedAt) - const totalInput = job.usage.inputTokens + (job.usage.cachedInputTokens ?? 0) - const cacheRate = totalInput > 0 ? ((job.usage.cachedInputTokens ?? 0) / totalInput) * 100 : 0 + const cacheRate = + job.usage.inputTokens > 0 + ? ((job.usage.cachedInputTokens ?? 0) / job.usage.inputTokens) * 100 + : 0 return ( <> @@ -60,14 +62,15 @@ export function JobInfoContent({ job }: { job: Job }): React.ReactNode { {job.finishedAt ? ` · Finished ${formatShortDate(job.finishedAt)}` : ""} - Tokens: In {formatTokenCount(job.usage.inputTokens)} · Out{" "} - {formatTokenCount(job.usage.outputTokens)} + Tokens: In {formatTokenCount(job.usage.inputTokens)} {(job.usage.cachedInputTokens ?? 0) > 0 ? ( <> - {" · Cache "} - {formatTokenCount(job.usage.cachedInputTokens ?? 0)}/{cacheRate.toFixed(2)}% + {" "}(Cached {formatTokenCount(job.usage.cachedInputTokens ?? 0)}, Cache Hit{" "} + {cacheRate.toFixed(2)}%) ) : null} + {" · Out "} + {formatTokenCount(job.usage.outputTokens)} ) @@ -112,19 +115,20 @@ export function RunInfoContent({ ) : null} {treeNode ? ( - Tokens: In {formatTokenCount(treeNode.inputTokens)} · Out{" "} - {formatTokenCount(treeNode.outputTokens)} + Tokens: In {formatTokenCount(treeNode.inputTokens)} {treeNode.cachedInputTokens > 0 ? ( <> - {" · Cache "} - {formatTokenCount(treeNode.cachedInputTokens)}/ + {" "}(Cached {formatTokenCount(treeNode.cachedInputTokens)}, Cache Hit{" "} {( - (treeNode.cachedInputTokens / (treeNode.inputTokens + treeNode.cachedInputTokens)) * - 100 + (treeNode.inputTokens > 0 + ? (treeNode.cachedInputTokens / treeNode.inputTokens) * 100 + : 0) ).toFixed(2)} - % + %) ) : null} + {" · Out "} + {formatTokenCount(treeNode.outputTokens)} ) : null} From ac02f1b6682795c7b38d40bc9a6402b85f17b7b4 Mon Sep 17 00:00:00 2001 From: HiranoMasaaki Date: Fri, 13 Mar 2026 18:47:24 +0000 Subject: [PATCH 2/3] chore: include perstack in changeset Co-Authored-By: Claude Opus 4.6 (1M context) --- .changeset/fix-cache-hit-rate.md | 1 + 1 file changed, 1 insertion(+) diff --git a/.changeset/fix-cache-hit-rate.md b/.changeset/fix-cache-hit-rate.md index 9f5a24ed..527fadfb 100644 --- a/.changeset/fix-cache-hit-rate.md +++ b/.changeset/fix-cache-hit-rate.md @@ -1,5 +1,6 @@ --- "@perstack/tui-components": patch +"perstack": patch --- Fix cache hit rate double-counting cached tokens in denominator and improve display format From 8bd7c18717fdc3c38c4b22154b89685d687395a1 Mon Sep 17 00:00:00 2001 From: HiranoMasaaki Date: Fri, 13 Mar 2026 18:48:17 +0000 Subject: [PATCH 3/3] chore: fix formatting Co-Authored-By: Claude Opus 4.6 (1M context) --- .../src/log-viewer/components/log-info-content.tsx | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/packages/tui-components/src/log-viewer/components/log-info-content.tsx b/packages/tui-components/src/log-viewer/components/log-info-content.tsx index 2280783c..dcace0aa 100644 --- a/packages/tui-components/src/log-viewer/components/log-info-content.tsx +++ b/packages/tui-components/src/log-viewer/components/log-info-content.tsx @@ -65,7 +65,8 @@ export function JobInfoContent({ job }: { job: Job }): React.ReactNode { Tokens: In {formatTokenCount(job.usage.inputTokens)} {(job.usage.cachedInputTokens ?? 0) > 0 ? ( <> - {" "}(Cached {formatTokenCount(job.usage.cachedInputTokens ?? 0)}, Cache Hit{" "} + {" "} + (Cached {formatTokenCount(job.usage.cachedInputTokens ?? 0)}, Cache Hit{" "} {cacheRate.toFixed(2)}%) ) : null} @@ -118,11 +119,11 @@ export function RunInfoContent({ Tokens: In {formatTokenCount(treeNode.inputTokens)} {treeNode.cachedInputTokens > 0 ? ( <> - {" "}(Cached {formatTokenCount(treeNode.cachedInputTokens)}, Cache Hit{" "} - {( - (treeNode.inputTokens > 0 - ? (treeNode.cachedInputTokens / treeNode.inputTokens) * 100 - : 0) + {" "} + (Cached {formatTokenCount(treeNode.cachedInputTokens)}, Cache Hit{" "} + {(treeNode.inputTokens > 0 + ? (treeNode.cachedInputTokens / treeNode.inputTokens) * 100 + : 0 ).toFixed(2)} %)