summaryrefslogtreecommitdiffhomepage
path: root/src/core/metrics/format.ts
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-06-10 10:34:37 +0900
committerAdam Malczewski <[email protected]>2026-06-10 10:34:37 +0900
commit89ca80bac1e143a4ec5ba6e2e1d4998acce2553c (patch)
tree7af49f5fd2385c02153accb5fdb0a6c43ba37229 /src/core/metrics/format.ts
parentf8bf715abc8a89ec0c6370b40403c509b1ce2870 (diff)
downloaddispatch-web-89ca80bac1e143a4ec5ba6e2e1d4998acce2553c.tar.gz
dispatch-web-89ca80bac1e143a4ec5ba6e2e1d4998acce2553c.zip
feat(metrics): inline cache hit-rate badges (last turn + chat total)
Derive cache hit rate (cacheReadTokens / inputTokens) from data already folded in core/metrics — no backend/contract change. - core/metrics: computeCachePct + viewCacheRate (pct + success/warning/error level by 66/33 thresholds + isHit); thread a running cumulativeUsage onto each finalized turn-metrics row for the conversation total. - ChatView: render two labelled, colour-coded percentage badges in the turn-total bubble — "Last turn:" (that turn) and "Chat Total:" (cumulative). - Honour backend caveats: absent cache fields -> 0, divide-by-zero guarded, a legitimate 0% renders plainly (not "no data").
Diffstat (limited to 'src/core/metrics/format.ts')
-rw-r--r--src/core/metrics/format.ts28
1 files changed, 27 insertions, 1 deletions
diff --git a/src/core/metrics/format.ts b/src/core/metrics/format.ts
index 3a4078c..cc86976 100644
--- a/src/core/metrics/format.ts
+++ b/src/core/metrics/format.ts
@@ -1,5 +1,5 @@
import type { StepMetrics, TurnMetrics, Usage } from "@dispatch/wire";
-import type { StepMetricsView, TurnMetricsView } from "./types";
+import type { CacheRateView, StepMetricsView, TurnMetricsView } from "./types";
function formatTokens(n: number): string {
return n.toLocaleString("en-US");
@@ -49,6 +49,32 @@ export function viewStepMetrics(step: StepMetrics, index: number): StepMetricsVi
};
}
+/**
+ * Cache hit rate as a 0..100 integer percentage: `cacheReadTokens / inputTokens`,
+ * clamped to [0,1]. Absent cache field counts as 0; a 0% rate is legitimate (not
+ * missing data). Returns 0 when there are no input tokens.
+ */
+export function computeCachePct(u: Usage): number {
+ const read = u.cacheReadTokens ?? 0;
+ if (u.inputTokens <= 0) return 0;
+ const rate = read / u.inputTokens;
+ const clamped = rate < 0 ? 0 : rate > 1 ? 1 : rate;
+ return Math.round(clamped * 100);
+}
+
+/** Colour severity for a cache hit percentage (badge colour). */
+function cacheLevel(pct: number): "success" | "warning" | "error" {
+ if (pct >= 66) return "success";
+ if (pct >= 33) return "warning";
+ return "error";
+}
+
+/** Build a view of a cache hit rate (percentage + colour level + hit flag). */
+export function viewCacheRate(u: Usage): CacheRateView {
+ const pct = computeCachePct(u);
+ return { pct, level: cacheLevel(pct), isHit: (u.cacheReadTokens ?? 0) > 0 };
+}
+
/** Build a formatted view of a turn's aggregate metrics. */
export function viewTurnMetrics(turn: TurnMetrics): TurnMetricsView {
const total = totalTokens(turn.usage);