@oh-my-pi/omp-stats 18.15.0 → 18.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/dist/client/index.css +1 -1
- package/dist/client/index.js +102 -99
- package/dist/types/client/api.d.ts +4 -1
- package/dist/types/client/app/nav.d.ts +1 -1
- package/dist/types/client/charts/Chart.d.ts +16 -8
- package/dist/types/client/charts/DotWhisker.d.ts +50 -0
- package/dist/types/client/charts/Sparkline.d.ts +1 -1
- package/dist/types/client/charts/Waterfall.d.ts +26 -0
- package/dist/types/client/charts/index.d.ts +2 -0
- package/dist/types/client/charts/types.d.ts +8 -2
- package/dist/types/client/routes/SpotlightRoute.d.ts +7 -0
- package/dist/types/client/routes/index.d.ts +1 -0
- package/dist/types/client/spotlight/Compounding.d.ts +8 -0
- package/dist/types/client/spotlight/Coverage.d.ts +6 -0
- package/dist/types/client/spotlight/Economics.d.ts +10 -0
- package/dist/types/client/spotlight/FetchBack.d.ts +9 -0
- package/dist/types/client/spotlight/Hero.d.ts +12 -0
- package/dist/types/client/spotlight/Overhead.d.ts +6 -0
- package/dist/types/client/spotlight/Ranking.d.ts +6 -0
- package/dist/types/client/spotlight/ResultsFeed.d.ts +6 -0
- package/dist/types/client/spotlight/Savings.d.ts +10 -0
- package/dist/types/client/spotlight/ShareTrend.d.ts +8 -0
- package/dist/types/client/spotlight/Steps.d.ts +6 -0
- package/dist/types/client/spotlight/TimeCost.d.ts +6 -0
- package/dist/types/client/spotlight/chips.d.ts +16 -0
- package/dist/types/client/spotlight/meta.d.ts +64 -0
- package/dist/types/db.d.ts +26 -0
- package/dist/types/index.d.ts +2 -1
- package/dist/types/parser.d.ts +4 -0
- package/dist/types/rollup.d.ts +22 -1
- package/dist/types/shared-types.d.ts +419 -0
- package/dist/types/spotlight-aggregator.d.ts +189 -0
- package/dist/types/spotlight-bootstrap.d.ts +32 -0
- package/dist/types/spotlight-ingest.d.ts +258 -0
- package/dist/types/spotlight-measures.d.ts +51 -0
- package/package.json +5 -5
- package/src/aggregator.ts +3 -1
- package/src/client/App.tsx +3 -0
- package/src/client/api.ts +21 -0
- package/src/client/app/nav.ts +4 -1
- package/src/client/charts/Chart.tsx +105 -22
- package/src/client/charts/DotWhisker.tsx +149 -0
- package/src/client/charts/Sparkline.tsx +3 -2
- package/src/client/charts/Waterfall.tsx +86 -0
- package/src/client/charts/index.ts +2 -0
- package/src/client/charts/types.ts +9 -2
- package/src/client/data/query.ts +12 -4
- package/src/client/react-css.d.ts +1 -1
- package/src/client/routes/SpotlightRoute.tsx +138 -0
- package/src/client/routes/index.ts +1 -0
- package/src/client/spotlight/Compounding.tsx +222 -0
- package/src/client/spotlight/Coverage.tsx +96 -0
- package/src/client/spotlight/Economics.tsx +167 -0
- package/src/client/spotlight/FetchBack.tsx +222 -0
- package/src/client/spotlight/Hero.tsx +177 -0
- package/src/client/spotlight/Overhead.tsx +135 -0
- package/src/client/spotlight/Ranking.tsx +248 -0
- package/src/client/spotlight/ResultsFeed.tsx +354 -0
- package/src/client/spotlight/Savings.tsx +355 -0
- package/src/client/spotlight/ShareTrend.tsx +103 -0
- package/src/client/spotlight/Steps.tsx +225 -0
- package/src/client/spotlight/TimeCost.tsx +64 -0
- package/src/client/spotlight/chips.tsx +63 -0
- package/src/client/spotlight/meta.ts +206 -0
- package/src/client/spotlight/spotlight.css +1387 -0
- package/src/client/styles.css +289 -1
- package/src/client/traces/SpanDrawer.tsx +2 -1
- package/src/db.ts +480 -2
- package/src/index.ts +10 -1
- package/src/live.ts +21 -9
- package/src/parser.ts +44 -8
- package/src/rollup.ts +203 -10
- package/src/server.ts +42 -0
- package/src/shared-types.ts +453 -0
- package/src/spotlight-aggregator.ts +1738 -0
- package/src/spotlight-bootstrap.ts +110 -0
- package/src/spotlight-ingest.ts +981 -0
- package/src/spotlight-measures.ts +667 -0
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import { DotWhisker, type DotWhiskerMark, type DotWhiskerRow } from "../charts";
|
|
2
|
+
import { formatCompact, formatInteger } from "../data/formatters";
|
|
3
|
+
import type { SpotlightDashboardStats, SpotlightHeatPosition, SpotlightRankingCell } from "../types";
|
|
4
|
+
import { Card } from "../ui";
|
|
5
|
+
import { GroupLegend } from "./chips";
|
|
6
|
+
import { CUT_GROUP, formatInterval } from "./meta";
|
|
7
|
+
|
|
8
|
+
type RankingGroup = SpotlightRankingCell["group"];
|
|
9
|
+
|
|
10
|
+
/** Scored cuts first (the figure), then what they are read against. */
|
|
11
|
+
const GROUPS: readonly RankingGroup[] = ["applied", "scoreFree", "holdout"];
|
|
12
|
+
|
|
13
|
+
const MARK_OFFSET: Record<RankingGroup, number> = { applied: -7, scoreFree: 0, holdout: 7 };
|
|
14
|
+
|
|
15
|
+
interface Measure {
|
|
16
|
+
key: "auc" | "topLift" | "keptLift";
|
|
17
|
+
title: string;
|
|
18
|
+
/** What the chance line means for this measure. */
|
|
19
|
+
chanceLabel: string;
|
|
20
|
+
chance: number;
|
|
21
|
+
rule: string;
|
|
22
|
+
format: (value: number) => string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const MEASURES: readonly Measure[] = [
|
|
26
|
+
{
|
|
27
|
+
key: "auc",
|
|
28
|
+
title: "Heat ranks needed lines higher",
|
|
29
|
+
chanceLabel: "AUC · chance 0.5",
|
|
30
|
+
chance: 0.5,
|
|
31
|
+
rule: "P(a needed line is hotter than an unneeded line of the same result), ties half",
|
|
32
|
+
format: v => v.toFixed(2),
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
key: "topLift",
|
|
36
|
+
title: "Hottest tenth holds them",
|
|
37
|
+
chanceLabel: "lift · chance 1.0",
|
|
38
|
+
chance: 1,
|
|
39
|
+
rule: "Needed-line rate among each result's hottest 10% of lines over its rate among all lines",
|
|
40
|
+
format: v => `${v.toFixed(2)}×`,
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
key: "keptLift",
|
|
44
|
+
title: "The cut keeps them",
|
|
45
|
+
chanceLabel: "lift · random cut 1.0",
|
|
46
|
+
chance: 1,
|
|
47
|
+
rule: "Share of needed lines the cut kept over the share of all lines it kept",
|
|
48
|
+
format: v => `${v.toFixed(2)}×`,
|
|
49
|
+
},
|
|
50
|
+
];
|
|
51
|
+
|
|
52
|
+
const NEED_SOURCES = [
|
|
53
|
+
{ key: "reread", label: "Read again", rule: "A later read of the same file selected the lines" },
|
|
54
|
+
{ key: "edit", label: "Edited", rule: "A later edit's Find text matched the lines" },
|
|
55
|
+
] as const;
|
|
56
|
+
|
|
57
|
+
/** Per-line ranking quality: Spotlight's heat and the cut against the lines the agent needed later. */
|
|
58
|
+
export function Ranking({ stats, stale }: { stats: SpotlightDashboardStats; stale: boolean }) {
|
|
59
|
+
const { ranking } = stats;
|
|
60
|
+
const cells: ReadonlyMap<RankingGroup, SpotlightRankingCell> = new Map(
|
|
61
|
+
ranking.cells.map(cell => [cell.group, cell]),
|
|
62
|
+
);
|
|
63
|
+
const measured = GROUPS.some(group => (cells.get(group)?.results ?? 0) > 0);
|
|
64
|
+
|
|
65
|
+
return (
|
|
66
|
+
<Card
|
|
67
|
+
index={4}
|
|
68
|
+
title="Does Spotlight keep what the agent needs?"
|
|
69
|
+
description="Lines of a shaped read the agent read again or edited later, against Spotlight's heat and the cut"
|
|
70
|
+
actions={<GroupLegend groups={GROUPS} />}
|
|
71
|
+
stale={stale}
|
|
72
|
+
footer="Shaped reads with at least one needed line, until the file's first edit or a later whole-file read. Scored and score-free cuts are biased alike (the agent re-reads what a cut hid), so compare them with each other; held-out would-be cuts, shown whole, are the unbiased measure. Score-free cuts have no heat, so only the cut's lift exists for them. Dashed lines are chance; whiskers are 90% session bootstrap intervals, from 3 sessions."
|
|
73
|
+
>
|
|
74
|
+
{!measured ? (
|
|
75
|
+
<div className="empty">No shaped read was needed again in this range.</div>
|
|
76
|
+
) : (
|
|
77
|
+
<div className="spot-rank">
|
|
78
|
+
<div className="spot-rank-panels">
|
|
79
|
+
{MEASURES.map(measure => (
|
|
80
|
+
<RankPanel key={measure.key} measure={measure} cells={cells} />
|
|
81
|
+
))}
|
|
82
|
+
</div>
|
|
83
|
+
<div className="spot-rank-foot">
|
|
84
|
+
<SampleTable cells={cells} />
|
|
85
|
+
<PositionHeat positions={ranking.position} />
|
|
86
|
+
<section className="spot-rank-sources" aria-label="How the need was seen">
|
|
87
|
+
<h3 className="spot-panel-title">How the need was seen</h3>
|
|
88
|
+
<dl className="spot-facts spot-rank-facts">
|
|
89
|
+
{NEED_SOURCES.map(source => (
|
|
90
|
+
<div key={source.key} title={source.rule}>
|
|
91
|
+
<dt>{source.label}</dt>
|
|
92
|
+
<dd className="num">
|
|
93
|
+
{formatInteger(ranking.sources[source.key])}
|
|
94
|
+
<span className="dim"> results</span>
|
|
95
|
+
</dd>
|
|
96
|
+
</div>
|
|
97
|
+
))}
|
|
98
|
+
</dl>
|
|
99
|
+
</section>
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
)}
|
|
103
|
+
</Card>
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function RankPanel({ measure, cells }: { measure: Measure; cells: ReadonlyMap<RankingGroup, SpotlightRankingCell> }) {
|
|
108
|
+
const marks: DotWhiskerMark[] = GROUPS.flatMap(group => {
|
|
109
|
+
const cell = cells.get(group);
|
|
110
|
+
if (cell === undefined || cell.results === 0) return [];
|
|
111
|
+
const estimate = cell[measure.key];
|
|
112
|
+
const interval = formatInterval(estimate, measure.format);
|
|
113
|
+
return [
|
|
114
|
+
{
|
|
115
|
+
key: group,
|
|
116
|
+
...estimate,
|
|
117
|
+
color: CUT_GROUP[group].color,
|
|
118
|
+
shape: CUT_GROUP[group].shape,
|
|
119
|
+
offset: MARK_OFFSET[group],
|
|
120
|
+
title: `${CUT_GROUP[group].label}: ${estimate.value === null ? "–" : measure.format(estimate.value)}${interval === "" ? "" : ` (${interval})`} over ${formatInteger(cell.results)} results, ${formatInteger(cell.neededLines)} needed lines`,
|
|
121
|
+
},
|
|
122
|
+
];
|
|
123
|
+
});
|
|
124
|
+
const applied = cells.get("applied")?.[measure.key].value ?? null;
|
|
125
|
+
const rows: DotWhiskerRow[] = [
|
|
126
|
+
{
|
|
127
|
+
key: "reads",
|
|
128
|
+
label: "Shaped reads",
|
|
129
|
+
marks,
|
|
130
|
+
figure: (
|
|
131
|
+
<span style={{ color: CUT_GROUP.applied.color }}>{applied === null ? "–" : measure.format(applied)}</span>
|
|
132
|
+
),
|
|
133
|
+
detail: (
|
|
134
|
+
<>
|
|
135
|
+
{(["scoreFree", "holdout"] as const).map((group, i) => {
|
|
136
|
+
const value = cells.get(group)?.[measure.key].value ?? null;
|
|
137
|
+
return (
|
|
138
|
+
<span key={group} title={CUT_GROUP[group].label}>
|
|
139
|
+
{i > 0 && " · "}
|
|
140
|
+
<span className="spot-fb-detail" data-group={group}>
|
|
141
|
+
{value === null ? "–" : measure.format(value)}
|
|
142
|
+
</span>
|
|
143
|
+
</span>
|
|
144
|
+
);
|
|
145
|
+
})}
|
|
146
|
+
</>
|
|
147
|
+
),
|
|
148
|
+
},
|
|
149
|
+
];
|
|
150
|
+
return (
|
|
151
|
+
<section className="spot-rank-panel" aria-label={measure.title}>
|
|
152
|
+
<h3 className="spot-panel-title" title={measure.rule}>
|
|
153
|
+
{measure.title}
|
|
154
|
+
<span className="spot-rank-chance">{measure.chanceLabel}</span>
|
|
155
|
+
</h3>
|
|
156
|
+
<DotWhisker
|
|
157
|
+
rows={rows}
|
|
158
|
+
guides={[{ key: "chance", value: measure.chance, color: "var(--ink-3)" }]}
|
|
159
|
+
format={measure.format}
|
|
160
|
+
formatTick={v => String(Number(v.toFixed(2)))}
|
|
161
|
+
/>
|
|
162
|
+
</section>
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/** Results and needed lines behind every cell of the panels. */
|
|
167
|
+
function SampleTable({ cells }: { cells: ReadonlyMap<RankingGroup, SpotlightRankingCell> }) {
|
|
168
|
+
return (
|
|
169
|
+
<section aria-label="Sample per cell">
|
|
170
|
+
<h3 className="spot-panel-title">
|
|
171
|
+
Sample per cell
|
|
172
|
+
<span className="spot-rank-chance">results · needed lines</span>
|
|
173
|
+
</h3>
|
|
174
|
+
<table className="spot-rank-n">
|
|
175
|
+
<thead>
|
|
176
|
+
<tr>
|
|
177
|
+
{GROUPS.map(group => (
|
|
178
|
+
<th key={group} scope="col" title={CUT_GROUP[group].rule}>
|
|
179
|
+
{CUT_GROUP[group].label}
|
|
180
|
+
</th>
|
|
181
|
+
))}
|
|
182
|
+
</tr>
|
|
183
|
+
</thead>
|
|
184
|
+
<tbody>
|
|
185
|
+
<tr>
|
|
186
|
+
{GROUPS.map(group => {
|
|
187
|
+
const cell = cells.get(group);
|
|
188
|
+
return (
|
|
189
|
+
<td key={group} className="num" data-thin={cell !== undefined && cell.results < 20}>
|
|
190
|
+
{cell === undefined || cell.results === 0 ? (
|
|
191
|
+
<span className="dim">–</span>
|
|
192
|
+
) : (
|
|
193
|
+
<span title={`${formatInteger(cell.neededLines)} needed lines`}>
|
|
194
|
+
{formatInteger(cell.results)}
|
|
195
|
+
<span className="dim"> · {formatCompact(cell.neededLines)}</span>
|
|
196
|
+
</span>
|
|
197
|
+
)}
|
|
198
|
+
</td>
|
|
199
|
+
);
|
|
200
|
+
})}
|
|
201
|
+
</tr>
|
|
202
|
+
</tbody>
|
|
203
|
+
</table>
|
|
204
|
+
</section>
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/** Mean heat by line position. */
|
|
209
|
+
function PositionHeat({ positions }: { positions: readonly SpotlightHeatPosition[] }) {
|
|
210
|
+
const top = Math.max(0, ...positions.map(p => p.meanHeat));
|
|
211
|
+
return (
|
|
212
|
+
<section className="spot-rank-pos" aria-label="Mean heat by position">
|
|
213
|
+
<h3 className="spot-panel-title">
|
|
214
|
+
Mean heat by position
|
|
215
|
+
<span className="spot-rank-chance">0–9, outputs ≥ 40 lines</span>
|
|
216
|
+
</h3>
|
|
217
|
+
{positions.length === 0 || top === 0 ? (
|
|
218
|
+
<div className="dim spot-gap-empty">No heat recorded</div>
|
|
219
|
+
) : (
|
|
220
|
+
<>
|
|
221
|
+
<div className="spot-rank-bars">
|
|
222
|
+
{positions.map(p => (
|
|
223
|
+
<div
|
|
224
|
+
key={p.label}
|
|
225
|
+
className="spot-rank-bar"
|
|
226
|
+
title={`${p.label}: mean heat ${p.meanHeat.toFixed(2)} over ${formatInteger(p.lines)} lines`}
|
|
227
|
+
>
|
|
228
|
+
<span className="spot-rank-bar-track">
|
|
229
|
+
<span className="spot-rank-bar-value num">{p.meanHeat.toFixed(1)}</span>
|
|
230
|
+
{/* The value's line (14px) stays clear above the tallest bar. */}
|
|
231
|
+
<span
|
|
232
|
+
className="spot-rank-bar-fill"
|
|
233
|
+
style={{ height: `calc((100% - 14px) * ${p.meanHeat / top})` }}
|
|
234
|
+
/>
|
|
235
|
+
</span>
|
|
236
|
+
{/* `line 1` → `L1`, `lines 2–3` → `L2–3`, `0–10%` → `0–10`: short enough to sit under a bar. */}
|
|
237
|
+
<span className="spot-rank-bar-label num">
|
|
238
|
+
{p.label.replace(/^lines? /, "L").replace(/%$/, "")}
|
|
239
|
+
</span>
|
|
240
|
+
</div>
|
|
241
|
+
))}
|
|
242
|
+
</div>
|
|
243
|
+
<div className="micro spot-rank-axis">L = line of the output, then each tenth of it (% into it)</div>
|
|
244
|
+
</>
|
|
245
|
+
)}
|
|
246
|
+
</section>
|
|
247
|
+
);
|
|
248
|
+
}
|
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
import { CornerDownRight, Scissors } from "lucide-react";
|
|
2
|
+
import { useCallback, useMemo, useState } from "react";
|
|
3
|
+
import {
|
|
4
|
+
formatCompact,
|
|
5
|
+
formatCost,
|
|
6
|
+
formatDurationMs,
|
|
7
|
+
formatElapsed,
|
|
8
|
+
formatInteger,
|
|
9
|
+
formatRelativeTime,
|
|
10
|
+
formatTimestamp,
|
|
11
|
+
} from "../data/formatters";
|
|
12
|
+
import type { SpotlightRecoveryKind, SpotlightResultRow } from "../types";
|
|
13
|
+
import { Badge, Card, type Column, Drawer, KeyValues, Table } from "../ui";
|
|
14
|
+
import { KindBadge } from "./chips";
|
|
15
|
+
import { formatSignedCompact, RECOVERY_KIND, RECOVERY_KINDS, toolIcon } from "./meta";
|
|
16
|
+
|
|
17
|
+
function rowKey(row: SpotlightResultRow): string {
|
|
18
|
+
return `${row.sessionFile}\u0000${row.toolCallId}`;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function NetCell({ value }: { value: number }) {
|
|
22
|
+
return (
|
|
23
|
+
<span className={value < 0 ? "num tone-bad" : value > 0 ? "num tone-ok" : "num"}>
|
|
24
|
+
{formatSignedCompact(value)}
|
|
25
|
+
</span>
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Plain → out with a bar of what survived the cut (would have, when held out). */
|
|
30
|
+
function ShrinkCell({ row }: { row: SpotlightResultRow }) {
|
|
31
|
+
const { plainTokens, outTokens } = row;
|
|
32
|
+
const kept = plainTokens > 0 ? Math.min(1, outTokens / plainTokens) : 1;
|
|
33
|
+
return (
|
|
34
|
+
<div
|
|
35
|
+
className="spot-shrink"
|
|
36
|
+
data-holdout={row.holdout}
|
|
37
|
+
title={`plain would show ${formatInteger(plainTokens)} tokens, the cut ${row.holdout ? "would show" : "showed"} ${formatInteger(outTokens)} (shaping read ${formatInteger(row.inTokens)})`}
|
|
38
|
+
>
|
|
39
|
+
<span className="num">
|
|
40
|
+
{formatCompact(plainTokens)}
|
|
41
|
+
<span className="dim"> → </span>
|
|
42
|
+
{formatCompact(outTokens)}
|
|
43
|
+
</span>
|
|
44
|
+
<span className="spot-shrink-bar" aria-hidden="true">
|
|
45
|
+
<span className="spot-shrink-kept" style={{ width: `${kept * 100}%` }} />
|
|
46
|
+
</span>
|
|
47
|
+
</div>
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function kindCounts(row: SpotlightResultRow): [SpotlightRecoveryKind, number][] {
|
|
52
|
+
const counts = new Map<SpotlightRecoveryKind, number>();
|
|
53
|
+
for (const r of row.recoveries) counts.set(r.kind, (counts.get(r.kind) ?? 0) + 1);
|
|
54
|
+
return RECOVERY_KINDS.flatMap(kind => {
|
|
55
|
+
const count = counts.get(kind);
|
|
56
|
+
return count ? [[kind, count]] : [];
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const COLUMNS: Column<SpotlightResultRow>[] = [
|
|
61
|
+
{
|
|
62
|
+
key: "target",
|
|
63
|
+
header: "Result",
|
|
64
|
+
width: "40%",
|
|
65
|
+
sort: row => row.timestamp,
|
|
66
|
+
render: row => {
|
|
67
|
+
const Icon = toolIcon(row.tool);
|
|
68
|
+
return (
|
|
69
|
+
<div className="spot-result">
|
|
70
|
+
<span className="spot-tool" title={row.tool}>
|
|
71
|
+
<Icon size={14} aria-hidden="true" />
|
|
72
|
+
<span className="sr-only">{row.tool}</span>
|
|
73
|
+
</span>
|
|
74
|
+
<div className="stack spot-result-text">
|
|
75
|
+
<span className="spot-target truncate" title={row.target}>
|
|
76
|
+
{row.target}
|
|
77
|
+
</span>
|
|
78
|
+
<span className="row spot-result-meta">
|
|
79
|
+
{row.holdout && <HoldoutBadge />}
|
|
80
|
+
{row.scoreFree && <ScoreFreeBadge />}
|
|
81
|
+
<span className="dim" title={formatTimestamp(row.timestamp)}>
|
|
82
|
+
{formatRelativeTime(row.timestamp)}
|
|
83
|
+
</span>
|
|
84
|
+
</span>
|
|
85
|
+
</div>
|
|
86
|
+
</div>
|
|
87
|
+
);
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
key: "shrink",
|
|
92
|
+
header: "Plain → out",
|
|
93
|
+
align: "right",
|
|
94
|
+
sort: row => (row.plainTokens > 0 ? row.outTokens / row.plainTokens : 1),
|
|
95
|
+
title: "What pi would have shown unshaped (plain) for the call, and what the cut showed; the bar is the share that survived",
|
|
96
|
+
render: row => <ShrinkCell row={row} />,
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
key: "saved",
|
|
100
|
+
header: "Saved",
|
|
101
|
+
align: "right",
|
|
102
|
+
sort: row => row.savedTokens,
|
|
103
|
+
render: row =>
|
|
104
|
+
row.holdout ? (
|
|
105
|
+
<span className="spot-would-save" title="Held out: what the cut would have saved">
|
|
106
|
+
<span className="dim">would save </span>
|
|
107
|
+
<span className="num">{formatSignedCompact(row.savedTokens)}</span>
|
|
108
|
+
</span>
|
|
109
|
+
) : (
|
|
110
|
+
<span className="num">{formatSignedCompact(row.savedTokens)}</span>
|
|
111
|
+
),
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
key: "hidden",
|
|
115
|
+
header: "Hidden lines",
|
|
116
|
+
align: "right",
|
|
117
|
+
sort: row => row.hiddenLines,
|
|
118
|
+
render: row =>
|
|
119
|
+
row.hiddenLines > 0 ? (
|
|
120
|
+
<span className="num">{formatInteger(row.hiddenLines)}</span>
|
|
121
|
+
) : (
|
|
122
|
+
<span className="dim">–</span>
|
|
123
|
+
),
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
key: "recoveries",
|
|
127
|
+
header: "Fetched back",
|
|
128
|
+
sort: row => row.recoveries.length,
|
|
129
|
+
render: row => {
|
|
130
|
+
const counts = kindCounts(row);
|
|
131
|
+
return counts.length === 0 ? (
|
|
132
|
+
<span className="dim" title="Nothing fetched back">
|
|
133
|
+
–
|
|
134
|
+
</span>
|
|
135
|
+
) : (
|
|
136
|
+
<span className="row spot-kinds">
|
|
137
|
+
{counts.map(([kind, count]) => (
|
|
138
|
+
<KindBadge key={kind} kind={kind} count={count} />
|
|
139
|
+
))}
|
|
140
|
+
</span>
|
|
141
|
+
);
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
key: "net",
|
|
146
|
+
header: "Net",
|
|
147
|
+
align: "right",
|
|
148
|
+
sort: row => row.netTokens,
|
|
149
|
+
title: "Saved tokens minus the tokens of the calls that fetched hidden output back",
|
|
150
|
+
render: row =>
|
|
151
|
+
row.holdout ? (
|
|
152
|
+
<span className="dim" title="Held out: the model saw the whole result, so nothing was saved">
|
|
153
|
+
–
|
|
154
|
+
</span>
|
|
155
|
+
) : (
|
|
156
|
+
<NetCell value={row.netTokens} />
|
|
157
|
+
),
|
|
158
|
+
},
|
|
159
|
+
];
|
|
160
|
+
|
|
161
|
+
function HoldoutBadge() {
|
|
162
|
+
return (
|
|
163
|
+
<span title="Held out at random: the model saw the original, unshortened result">
|
|
164
|
+
<Badge>held out</Badge>
|
|
165
|
+
</span>
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function ScoreFreeBadge() {
|
|
170
|
+
return (
|
|
171
|
+
<span title="A random coin skipped Spotlight: the no-score fallback rules made this cut">
|
|
172
|
+
<Badge>score-free</Badge>
|
|
173
|
+
</span>
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/** The 60 newest shortened results; a row opens its recovery timeline. */
|
|
178
|
+
export function ResultsFeed({ rows, stale }: { rows: readonly SpotlightResultRow[]; stale: boolean }) {
|
|
179
|
+
const [selected, setSelected] = useState<string | null>(null);
|
|
180
|
+
const close = useCallback(() => setSelected(null), []);
|
|
181
|
+
const current = useMemo(() => rows.find(row => rowKey(row) === selected) ?? null, [rows, selected]);
|
|
182
|
+
|
|
183
|
+
return (
|
|
184
|
+
<>
|
|
185
|
+
<Card
|
|
186
|
+
index={6}
|
|
187
|
+
title="Recent results"
|
|
188
|
+
description="The newest shortened and held-out tool results and what the agent fetched back"
|
|
189
|
+
flush
|
|
190
|
+
stale={stale}
|
|
191
|
+
footer="Saved is what plain would have shown minus what the cut showed (the would-be cut for held-out ones, which save nothing); recovery tokens are the recovery calls' results (chars ÷ 4 when no exact count exists)."
|
|
192
|
+
>
|
|
193
|
+
<Table
|
|
194
|
+
rows={rows}
|
|
195
|
+
rowKey={rowKey}
|
|
196
|
+
columns={COLUMNS}
|
|
197
|
+
onRowClick={row => setSelected(rowKey(row))}
|
|
198
|
+
selectedKey={selected}
|
|
199
|
+
limit={15}
|
|
200
|
+
empty={<div className="empty">No shortened or held-out results in this range.</div>}
|
|
201
|
+
/>
|
|
202
|
+
</Card>
|
|
203
|
+
<RecoveryDrawer row={current} onClose={close} />
|
|
204
|
+
</>
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function RecoveryDrawer({ row, onClose }: { row: SpotlightResultRow | null; onClose: () => void }) {
|
|
209
|
+
if (row === null) return null;
|
|
210
|
+
const Icon = toolIcon(row.tool);
|
|
211
|
+
const recovered = row.savedTokens - row.netTokens;
|
|
212
|
+
return (
|
|
213
|
+
<Drawer
|
|
214
|
+
open
|
|
215
|
+
onClose={onClose}
|
|
216
|
+
width={600}
|
|
217
|
+
title={
|
|
218
|
+
<span className="spot-drawer-title" title={row.target}>
|
|
219
|
+
{row.target}
|
|
220
|
+
</span>
|
|
221
|
+
}
|
|
222
|
+
subtitle={
|
|
223
|
+
<span className="row spot-drawer-subtitle">
|
|
224
|
+
<Icon size={13} aria-hidden="true" />
|
|
225
|
+
{row.tool}
|
|
226
|
+
{row.holdout && <HoldoutBadge />}
|
|
227
|
+
{row.scoreFree && <ScoreFreeBadge />}
|
|
228
|
+
<span>{formatTimestamp(row.timestamp)}</span>
|
|
229
|
+
</span>
|
|
230
|
+
}
|
|
231
|
+
>
|
|
232
|
+
<div className="spot-drawer-summary">
|
|
233
|
+
<div>
|
|
234
|
+
<div className="spot-eyebrow">{row.holdout ? "Would save" : "Saved"}</div>
|
|
235
|
+
<div className="spot-drawer-figure num">{formatSignedCompact(row.savedTokens)}</div>
|
|
236
|
+
</div>
|
|
237
|
+
<div>
|
|
238
|
+
<div className="spot-eyebrow">Fetched back</div>
|
|
239
|
+
<div className={recovered > 0 ? "spot-drawer-figure num tone-warn" : "spot-drawer-figure num"}>
|
|
240
|
+
{formatCompact(recovered)}
|
|
241
|
+
</div>
|
|
242
|
+
</div>
|
|
243
|
+
<div>
|
|
244
|
+
<div className="spot-eyebrow">Net</div>
|
|
245
|
+
{row.holdout ? (
|
|
246
|
+
<div className="spot-drawer-figure dim" title="Held out: the model saw the whole result">
|
|
247
|
+
–
|
|
248
|
+
</div>
|
|
249
|
+
) : (
|
|
250
|
+
<div
|
|
251
|
+
className={
|
|
252
|
+
row.netTokens < 0 ? "spot-drawer-figure num tone-bad" : "spot-drawer-figure num tone-ok"
|
|
253
|
+
}
|
|
254
|
+
>
|
|
255
|
+
{formatSignedCompact(row.netTokens)}
|
|
256
|
+
</div>
|
|
257
|
+
)}
|
|
258
|
+
</div>
|
|
259
|
+
</div>
|
|
260
|
+
|
|
261
|
+
<KeyValues
|
|
262
|
+
items={[
|
|
263
|
+
{ key: "plain", label: "Plain would show", value: formatInteger(row.plainTokens) },
|
|
264
|
+
{ key: "out", label: "Tokens out", value: formatInteger(row.outTokens) },
|
|
265
|
+
{ key: "in", label: "Full output read", value: formatInteger(row.inTokens) },
|
|
266
|
+
{
|
|
267
|
+
key: "hidden",
|
|
268
|
+
label: row.holdout ? "Lines it would hide" : "Hidden lines",
|
|
269
|
+
value: formatInteger(row.hiddenLines),
|
|
270
|
+
},
|
|
271
|
+
{ key: "steps", label: "Steps", value: row.steps.length > 0 ? row.steps.join(" → ") : "–" },
|
|
272
|
+
{ key: "cost", label: "Spotlight cost", value: formatCost(row.spotlightUsd) },
|
|
273
|
+
{ key: "ms", label: "Shaping time", value: formatDurationMs(row.shapeMs) },
|
|
274
|
+
]}
|
|
275
|
+
/>
|
|
276
|
+
|
|
277
|
+
<div>
|
|
278
|
+
<div className="section-label">Call</div>
|
|
279
|
+
<pre className="code-block spot-drawer-call">{row.target}</pre>
|
|
280
|
+
</div>
|
|
281
|
+
|
|
282
|
+
<div>
|
|
283
|
+
<div className="section-label">Recovery timeline</div>
|
|
284
|
+
<ol className="spot-timeline">
|
|
285
|
+
<li className="spot-timeline-item" data-kind={row.holdout ? "holdout" : "cut"}>
|
|
286
|
+
<span className="spot-timeline-node" aria-hidden="true">
|
|
287
|
+
<Scissors size={12} />
|
|
288
|
+
</span>
|
|
289
|
+
<div className="spot-timeline-body">
|
|
290
|
+
<div className="row spot-timeline-head">
|
|
291
|
+
<span className="cell-primary">
|
|
292
|
+
{row.holdout ? "Held out: the model saw the original" : "Shortened"}
|
|
293
|
+
</span>
|
|
294
|
+
<span className="dim num">
|
|
295
|
+
{row.holdout && "would be "}
|
|
296
|
+
{formatCompact(row.plainTokens)} → {formatCompact(row.outTokens)} tokens
|
|
297
|
+
{row.hiddenLines > 0 && `, ${formatInteger(row.hiddenLines)} lines hidden`}
|
|
298
|
+
</span>
|
|
299
|
+
</div>
|
|
300
|
+
</div>
|
|
301
|
+
</li>
|
|
302
|
+
{row.recoveries.map(r => (
|
|
303
|
+
<li
|
|
304
|
+
key={r.toolCallId}
|
|
305
|
+
className="spot-timeline-item"
|
|
306
|
+
data-kind={r.kind}
|
|
307
|
+
style={{ "--kind": RECOVERY_KIND[r.kind].color }}
|
|
308
|
+
>
|
|
309
|
+
<span className="spot-timeline-node" aria-hidden="true">
|
|
310
|
+
<CornerDownRight size={12} />
|
|
311
|
+
</span>
|
|
312
|
+
<div className="spot-timeline-body">
|
|
313
|
+
<div className="row spot-timeline-head">
|
|
314
|
+
<KindBadge kind={r.kind} />
|
|
315
|
+
{r.turn && <Badge tone="warn">extra turn</Badge>}
|
|
316
|
+
<span className="spot-timeline-spacer" />
|
|
317
|
+
<span className="num tone-warn">+{formatCompact(r.tokens)}</span>
|
|
318
|
+
</div>
|
|
319
|
+
<code className="spot-timeline-target" title={r.target}>
|
|
320
|
+
<span className="dim">{r.tool} </span>
|
|
321
|
+
{r.target}
|
|
322
|
+
</code>
|
|
323
|
+
<div className="micro">
|
|
324
|
+
{r.after === 0
|
|
325
|
+
? "The next call"
|
|
326
|
+
: `${formatInteger(r.after)} ${r.after === 1 ? "call" : "calls"} later`}
|
|
327
|
+
{` · ${formatElapsed(Math.max(0, r.timestamp - row.timestamp))} after the cut`}
|
|
328
|
+
</div>
|
|
329
|
+
</div>
|
|
330
|
+
</li>
|
|
331
|
+
))}
|
|
332
|
+
<li className="spot-timeline-item" data-kind="net">
|
|
333
|
+
<span className="spot-timeline-node" aria-hidden="true" />
|
|
334
|
+
<div className="spot-timeline-body">
|
|
335
|
+
<div className="row spot-timeline-head">
|
|
336
|
+
<span className="cell-primary">
|
|
337
|
+
{row.holdout
|
|
338
|
+
? row.recoveries.length === 0
|
|
339
|
+
? "Nothing fetched back"
|
|
340
|
+
: "Fetched more although it saw everything"
|
|
341
|
+
: row.recoveries.length === 0
|
|
342
|
+
? "Nothing fetched back: the full saving stands"
|
|
343
|
+
: "Net"}
|
|
344
|
+
</span>
|
|
345
|
+
<span className="spot-timeline-spacer" />
|
|
346
|
+
{row.holdout ? <span className="dim">saves nothing</span> : <NetCell value={row.netTokens} />}
|
|
347
|
+
</div>
|
|
348
|
+
</div>
|
|
349
|
+
</li>
|
|
350
|
+
</ol>
|
|
351
|
+
</div>
|
|
352
|
+
</Drawer>
|
|
353
|
+
);
|
|
354
|
+
}
|