@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,222 @@
|
|
|
1
|
+
import { useState } from "react";
|
|
2
|
+
import { DotWhisker, type DotWhiskerMark, type DotWhiskerRow } from "../charts";
|
|
3
|
+
import { formatInteger } from "../data/formatters";
|
|
4
|
+
import type {
|
|
5
|
+
SpotlightCutGroup,
|
|
6
|
+
SpotlightDashboardStats,
|
|
7
|
+
SpotlightFetchBackCell,
|
|
8
|
+
SpotlightGapCount,
|
|
9
|
+
SpotlightTool,
|
|
10
|
+
} from "../types";
|
|
11
|
+
import { Card } from "../ui";
|
|
12
|
+
import { GroupLegend, MoreTools } from "./chips";
|
|
13
|
+
import { CUT_GROUP, CUT_GROUPS, foldTools, formatShare, GAP_BINS, toolIcon, toolsBySaving } from "./meta";
|
|
14
|
+
|
|
15
|
+
/** Vertical spread of a row's marks, so overlapping intervals stay readable. */
|
|
16
|
+
const MARK_OFFSET: Record<SpotlightCutGroup, number> = {
|
|
17
|
+
applied: -12,
|
|
18
|
+
scoreFree: -6,
|
|
19
|
+
holdout: 0,
|
|
20
|
+
gated: 6,
|
|
21
|
+
reference: 12,
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/** What a scored cut's rate is read against, in the row's small print: the score-free cut, then the baselines. */
|
|
25
|
+
const COMPARISONS: readonly SpotlightCutGroup[] = ["scoreFree", "holdout", "reference"];
|
|
26
|
+
|
|
27
|
+
/** Groups the gap histogram counts (`SpotlightGapCount` fields). */
|
|
28
|
+
type GapGroup = "applied" | "holdout";
|
|
29
|
+
const GAP_GROUPS: readonly GapGroup[] = ["applied", "holdout"];
|
|
30
|
+
|
|
31
|
+
const FETCH_BACK_RULE =
|
|
32
|
+
"One rule for every group: within 12 calls, a read of the same path overlapping the result's own window (the whole file without a selector), a rerun of the call (a similar bash command; for other tools, ≥ 60% shared argument tokens), or a read of an artifact it named; not after an edit of that path (reads) or any edit (reruns), nor from the same request.";
|
|
33
|
+
|
|
34
|
+
export interface FetchBackProps {
|
|
35
|
+
stats: SpotlightDashboardStats;
|
|
36
|
+
stale: boolean;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Fetch-back rate per tool: the scored cut against the score-free one, the random holdout and the unchanged results. */
|
|
40
|
+
export function FetchBack({ stats, stale }: FetchBackProps) {
|
|
41
|
+
const [expanded, setExpanded] = useState(false);
|
|
42
|
+
const cells = new Map<string, SpotlightFetchBackCell>();
|
|
43
|
+
for (const cell of stats.fetchBack) cells.set(`${cell.tool}/${cell.group}`, cell);
|
|
44
|
+
const measured = new Set(stats.fetchBack.filter(cell => cell.results > 0).map(cell => cell.tool));
|
|
45
|
+
const { listed, rest } = foldTools(toolsBySaving(stats).filter(tool => measured.has(tool)));
|
|
46
|
+
// Only reads were gated, and only while pi's former edit gate ran: the legend names the group where it has results.
|
|
47
|
+
const legend = CUT_GROUPS.filter(
|
|
48
|
+
group => group !== "gated" || stats.fetchBack.some(cell => cell.group === "gated" && cell.results > 0),
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
const rows: DotWhiskerRow[] = [];
|
|
52
|
+
for (const tool of expanded ? [...listed, ...rest] : listed) {
|
|
53
|
+
const Icon = toolIcon(tool);
|
|
54
|
+
const groups = CUT_GROUPS.flatMap(group => {
|
|
55
|
+
const cell = cells.get(`${tool}/${group}`);
|
|
56
|
+
return cell !== undefined && cell.results > 0 ? [cell] : [];
|
|
57
|
+
});
|
|
58
|
+
if (groups.length === 0) continue;
|
|
59
|
+
const byGroup = new Map(groups.map(cell => [cell.group, cell]));
|
|
60
|
+
const applied = byGroup.get("applied");
|
|
61
|
+
const marks: DotWhiskerMark[] = groups.map(cell => ({
|
|
62
|
+
key: cell.group,
|
|
63
|
+
...cell.rate,
|
|
64
|
+
color: CUT_GROUP[cell.group].color,
|
|
65
|
+
shape: CUT_GROUP[cell.group].shape,
|
|
66
|
+
offset: MARK_OFFSET[cell.group],
|
|
67
|
+
title: `${CUT_GROUP[cell.group].label}: ${formatShare(cell.rate.value)} of ${formatInteger(cell.results)} results in ${formatInteger(cell.sessions)} sessions`,
|
|
68
|
+
}));
|
|
69
|
+
rows.push({
|
|
70
|
+
key: tool,
|
|
71
|
+
label: (
|
|
72
|
+
<span className="spot-fb-tool" title={tool}>
|
|
73
|
+
<Icon size={13} aria-hidden="true" />
|
|
74
|
+
<span className="spot-fb-tool-name">{tool}</span>
|
|
75
|
+
</span>
|
|
76
|
+
),
|
|
77
|
+
marks,
|
|
78
|
+
figure: (
|
|
79
|
+
<span style={{ color: CUT_GROUP.applied.color }} title={CUT_GROUP.applied.rule}>
|
|
80
|
+
{applied === undefined ? "–" : formatShare(applied.rate.value)}
|
|
81
|
+
</span>
|
|
82
|
+
),
|
|
83
|
+
detail: (
|
|
84
|
+
<>
|
|
85
|
+
{COMPARISONS.map((group, i) => {
|
|
86
|
+
const cell = byGroup.get(group);
|
|
87
|
+
return (
|
|
88
|
+
<span key={group} title={`${CUT_GROUP[group].label}: ${CUT_GROUP[group].rule}`}>
|
|
89
|
+
{i > 0 && " · "}
|
|
90
|
+
<span className="spot-fb-detail" data-group={group}>
|
|
91
|
+
{cell === undefined ? "–" : formatShare(cell.rate.value)}
|
|
92
|
+
</span>
|
|
93
|
+
</span>
|
|
94
|
+
);
|
|
95
|
+
})}
|
|
96
|
+
</>
|
|
97
|
+
),
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return (
|
|
102
|
+
<Card
|
|
103
|
+
index={3}
|
|
104
|
+
title="Does the agent come back for it?"
|
|
105
|
+
description="Share of results the agent fetched more of within 12 calls: cut with Spotlight's scores, cut without them, held out at random, kept whole by pi's former edit gate, and left unchanged"
|
|
106
|
+
stale={stale}
|
|
107
|
+
footer={`${FETCH_BACK_RULE} Held-out results are the true counterfactual: drawn at random from the would-be cuts and shown whole; until they accumulate, unchanged results stand in, re-weighted to the cuts' size mix. Score-free cuts skip Spotlight by a second coin and take the no-score fallback rules: if they come back as often as scored cuts, the scores add nothing. Gated reads are reads pi's former edit gate (removed) kept whole, listed only where the range has any. Small print: score-free · held out · unchanged. Whiskers are 90% session bootstrap intervals, from 3 sessions.`}
|
|
108
|
+
>
|
|
109
|
+
{rows.length === 0 ? (
|
|
110
|
+
<div className="empty">No evaluated results in this range.</div>
|
|
111
|
+
) : (
|
|
112
|
+
<div className="spot-fb">
|
|
113
|
+
<GroupLegend groups={legend} />
|
|
114
|
+
<DotWhisker rows={rows} format={formatShare} formatTick={v => `${Math.round(v * 100)}%`} />
|
|
115
|
+
{rest.length > 0 && (
|
|
116
|
+
<MoreTools hidden={rest} expanded={expanded} onToggle={() => setExpanded(value => !value)} />
|
|
117
|
+
)}
|
|
118
|
+
</div>
|
|
119
|
+
)}
|
|
120
|
+
</Card>
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** Fetch-backs per gap bin as each group's share of its own fetch-backs (the groups differ in size). */
|
|
125
|
+
function GapPanel({ tool, counts }: { tool: SpotlightTool; counts: readonly SpotlightGapCount[] }) {
|
|
126
|
+
const Icon = toolIcon(tool);
|
|
127
|
+
const byBin = new Map(counts.map(c => [c.gap, c]));
|
|
128
|
+
let applied = 0;
|
|
129
|
+
let holdout = 0;
|
|
130
|
+
for (const c of counts) {
|
|
131
|
+
applied += c.applied;
|
|
132
|
+
holdout += c.holdout;
|
|
133
|
+
}
|
|
134
|
+
const totals: Record<GapGroup, number> = { applied, holdout };
|
|
135
|
+
let top = 0;
|
|
136
|
+
for (const c of counts) {
|
|
137
|
+
if (applied > 0) top = Math.max(top, c.applied / applied);
|
|
138
|
+
if (holdout > 0) top = Math.max(top, c.holdout / holdout);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return (
|
|
142
|
+
<section className="spot-gap" aria-label={`${tool} fetch-back gaps`}>
|
|
143
|
+
<h3 className="spot-panel-title row spot-gap-title">
|
|
144
|
+
<Icon size={13} aria-hidden="true" />
|
|
145
|
+
{tool}
|
|
146
|
+
<span className="dim">
|
|
147
|
+
<span className="num">{formatInteger(applied)}</span> shortened ·{" "}
|
|
148
|
+
<span className="num">{formatInteger(holdout)}</span> held out
|
|
149
|
+
</span>
|
|
150
|
+
</h3>
|
|
151
|
+
{applied + holdout === 0 ? (
|
|
152
|
+
<div className="spot-gap-empty dim">No fetch-backs</div>
|
|
153
|
+
) : (
|
|
154
|
+
<div className="spot-gap-bins">
|
|
155
|
+
{GAP_BINS.map(bin => {
|
|
156
|
+
const count = byBin.get(bin);
|
|
157
|
+
return (
|
|
158
|
+
<div key={bin} className="spot-gap-bin">
|
|
159
|
+
<div className="spot-gap-bars">
|
|
160
|
+
{GAP_GROUPS.map(group => {
|
|
161
|
+
const n = count?.[group] ?? 0;
|
|
162
|
+
const total = totals[group];
|
|
163
|
+
const share = total > 0 ? n / total : 0;
|
|
164
|
+
const noun = bin === "1" ? "call" : "calls";
|
|
165
|
+
return (
|
|
166
|
+
<span
|
|
167
|
+
key={group}
|
|
168
|
+
className="spot-gap-bar"
|
|
169
|
+
data-group={group}
|
|
170
|
+
data-empty={total === 0}
|
|
171
|
+
title={`${bin} ${noun} later: ${formatInteger(n)} of ${formatInteger(total)} ${CUT_GROUP[group].label.toLowerCase()} fetch-backs`}
|
|
172
|
+
>
|
|
173
|
+
{total > 0 && <span className="spot-gap-value num">{formatShare(share)}</span>}
|
|
174
|
+
<span
|
|
175
|
+
className="spot-gap-fill"
|
|
176
|
+
style={{ height: `${top > 0 ? (share / top) * 100 : 0}%` }}
|
|
177
|
+
/>
|
|
178
|
+
</span>
|
|
179
|
+
);
|
|
180
|
+
})}
|
|
181
|
+
</div>
|
|
182
|
+
<span className="spot-gap-label num">{bin}</span>
|
|
183
|
+
</div>
|
|
184
|
+
);
|
|
185
|
+
})}
|
|
186
|
+
</div>
|
|
187
|
+
)}
|
|
188
|
+
</section>
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/** How many calls after the cut the agent fetched back, per tool, shortened against held out. */
|
|
193
|
+
export function FetchBackGaps({ stats, stale }: FetchBackProps) {
|
|
194
|
+
const [expanded, setExpanded] = useState(false);
|
|
195
|
+
const counted = new Set(stats.gaps.map(count => count.tool));
|
|
196
|
+
const { listed, rest } = foldTools(toolsBySaving(stats).filter(tool => counted.has(tool)));
|
|
197
|
+
return (
|
|
198
|
+
<Card
|
|
199
|
+
index={3}
|
|
200
|
+
title="How soon it comes back"
|
|
201
|
+
description="Calls between a result and the first fetch-back after it"
|
|
202
|
+
stale={stale}
|
|
203
|
+
footer="Bars are each group's share of its own fetch-backs, so shortened and held-out results compare despite their different counts (next to each tool). Same rule as above; nothing after 12 calls counts."
|
|
204
|
+
>
|
|
205
|
+
{listed.length === 0 ? (
|
|
206
|
+
<div className="empty">No fetch-backs in this range.</div>
|
|
207
|
+
) : (
|
|
208
|
+
<>
|
|
209
|
+
<GroupLegend groups={GAP_GROUPS} />
|
|
210
|
+
<div className="spot-gaps">
|
|
211
|
+
{(expanded ? [...listed, ...rest] : listed).map(tool => (
|
|
212
|
+
<GapPanel key={tool} tool={tool} counts={stats.gaps.filter(c => c.tool === tool)} />
|
|
213
|
+
))}
|
|
214
|
+
</div>
|
|
215
|
+
{rest.length > 0 && (
|
|
216
|
+
<MoreTools hidden={rest} expanded={expanded} onToggle={() => setExpanded(value => !value)} />
|
|
217
|
+
)}
|
|
218
|
+
</>
|
|
219
|
+
)}
|
|
220
|
+
</Card>
|
|
221
|
+
);
|
|
222
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { TriangleAlert } from "lucide-react";
|
|
2
|
+
import { formatCompact, formatInteger } from "../data/formatters";
|
|
3
|
+
import type { SpotlightDashboardStats } from "../types";
|
|
4
|
+
import { Stat } from "../ui";
|
|
5
|
+
import { foldTools, formatInterval, formatShare, formatSignedCompact, formatSignedCost, OTHER_TOOLS } from "./meta";
|
|
6
|
+
|
|
7
|
+
export interface SpotlightHeroProps {
|
|
8
|
+
stats: SpotlightDashboardStats;
|
|
9
|
+
/** Prose window ("the last 7 days"). */
|
|
10
|
+
windowLabel: string;
|
|
11
|
+
stale: boolean;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Headline: the holdout-adjusted net (tokens and $, with intervals) against what `plain` would have shown, the old
|
|
16
|
+
* full-output figure struck through as the baseline note, then gross, the excess fetch-back and the sample.
|
|
17
|
+
*/
|
|
18
|
+
export function SpotlightHero({ stats, windowLabel, stale }: SpotlightHeroProps) {
|
|
19
|
+
const { gross, clawback, adjusted } = stats;
|
|
20
|
+
const net = adjusted.netTokens;
|
|
21
|
+
const netUsd = adjusted.netUsd;
|
|
22
|
+
const tokenInterval = formatInterval(net, formatSignedCompact);
|
|
23
|
+
const usdInterval = formatInterval(netUsd, formatSignedCost);
|
|
24
|
+
// A tool's baseline only matters when it has cuts to adjust; `adjusted.byTool` runs the largest saving first.
|
|
25
|
+
const thin = adjusted.byTool.filter(row => row.cuts > 0 && row.holdouts < adjusted.minHoldouts).map(row => row.tool);
|
|
26
|
+
// Prose list: `read`, `read and bash`, `read, bash and eval`, `read, bash, … and 4 more tools`.
|
|
27
|
+
const thinFold = foldTools(thin);
|
|
28
|
+
const thinNames = thinFold.rest.length > 0 ? [...thinFold.listed, `${thinFold.rest.length} more tools`] : thin;
|
|
29
|
+
const thinProse =
|
|
30
|
+
thinNames.length < 2 ? (thinNames[0] ?? "") : `${thinNames.slice(0, -1).join(", ")} and ${thinNames.at(-1)}`;
|
|
31
|
+
const heldFold = foldTools(adjusted.byTool);
|
|
32
|
+
const heldRows = heldFold.listed.map(row => ({
|
|
33
|
+
tool: row.tool,
|
|
34
|
+
holdouts: row.holdouts,
|
|
35
|
+
thin: thin.includes(row.tool),
|
|
36
|
+
}));
|
|
37
|
+
if (heldFold.rest.length > 0) {
|
|
38
|
+
heldRows.push({
|
|
39
|
+
tool: OTHER_TOOLS,
|
|
40
|
+
holdouts: heldFold.rest.reduce((sum, row) => sum + row.holdouts, 0),
|
|
41
|
+
thin: heldFold.rest.some(row => thin.includes(row.tool)),
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
const overstated = gross.fullSavedTokens > 0 ? 1 - gross.savedTokens / gross.fullSavedTokens : null;
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<section className="card rise spot-hero" data-stale={stale} aria-label="Spotlight savings summary">
|
|
48
|
+
<div className="spot-hero-main">
|
|
49
|
+
<div className="spot-hero-headline">
|
|
50
|
+
<div className="spot-eyebrow">Net saved in {windowLabel}, after fetch-backs a cut caused</div>
|
|
51
|
+
<div className="spot-hero-figures">
|
|
52
|
+
<span
|
|
53
|
+
className="spot-hero-value gradient-text"
|
|
54
|
+
title={net.value === null ? undefined : `${formatInteger(net.value)} tokens`}
|
|
55
|
+
>
|
|
56
|
+
{net.value === null ? "–" : formatSignedCompact(net.value)}
|
|
57
|
+
</span>
|
|
58
|
+
<span className="spot-hero-unit">tokens</span>
|
|
59
|
+
{netUsd.value !== null && (
|
|
60
|
+
<span
|
|
61
|
+
className="spot-hero-usd"
|
|
62
|
+
data-negative={netUsd.value < 0}
|
|
63
|
+
title="Avoided context priced at each later request's cache rates, minus the fetch-backs beyond the holdout baseline (priced the same way) and Spotlight's own bill"
|
|
64
|
+
>
|
|
65
|
+
{formatSignedCost(netUsd.value)}
|
|
66
|
+
</span>
|
|
67
|
+
)}
|
|
68
|
+
</div>
|
|
69
|
+
{(tokenInterval !== "" || usdInterval !== "") && (
|
|
70
|
+
<div className="spot-hero-interval">
|
|
71
|
+
90% interval{" "}
|
|
72
|
+
<span className="num">{[tokenInterval, usdInterval].filter(Boolean).join(" · ")}</span>
|
|
73
|
+
</div>
|
|
74
|
+
)}
|
|
75
|
+
</div>
|
|
76
|
+
<div className="spot-hero-aside">
|
|
77
|
+
<p className="spot-hero-note">
|
|
78
|
+
Counted against what pi would have shown unshaped (<code>plain</code>) for the same call (its inline
|
|
79
|
+
cap and read page), with fetch-backs that held-out results show happen anyway added back.{" "}
|
|
80
|
+
{gross.fullSavedTokens > gross.savedTokens && (
|
|
81
|
+
<>
|
|
82
|
+
Against the full output the shaping read, the cuts would claim{" "}
|
|
83
|
+
<s className="spot-struck num">{formatCompact(gross.fullSavedTokens)}</s>, overstating the gross
|
|
84
|
+
by {formatShare(overstated)}.{" "}
|
|
85
|
+
</>
|
|
86
|
+
)}
|
|
87
|
+
{gross.widerThanPlain > 0 &&
|
|
88
|
+
`${formatInteger(gross.widerThanPlain)} ${gross.widerThanPlain === 1 ? "result was" : "results were"} wider than plain would have shown: cuts shown that way count as losses, results pi capped at plain's text save nothing. `}
|
|
89
|
+
{gross.estimatedPlain > 0 &&
|
|
90
|
+
`${formatInteger(gross.estimatedPlain)} older ${gross.estimatedPlain === 1 ? "result estimates" : "results estimate"} plain from the inline cap.`}
|
|
91
|
+
</p>
|
|
92
|
+
{thin.length > 0 && (
|
|
93
|
+
<p className="spot-hero-warn" role="note">
|
|
94
|
+
<TriangleAlert size={14} aria-hidden="true" />
|
|
95
|
+
<span>
|
|
96
|
+
{thinProse} {thin.length === 1 ? "has" : "have"} fewer than{" "}
|
|
97
|
+
{formatInteger(adjusted.minHoldouts)} held-out results: {thin.length === 1 ? "its" : "their"}{" "}
|
|
98
|
+
part of the net rests on a thin baseline.
|
|
99
|
+
</span>
|
|
100
|
+
</p>
|
|
101
|
+
)}
|
|
102
|
+
</div>
|
|
103
|
+
</div>
|
|
104
|
+
|
|
105
|
+
<div className="spot-hero-stats">
|
|
106
|
+
<Stat
|
|
107
|
+
size="sm"
|
|
108
|
+
label="Gross vs plain"
|
|
109
|
+
title="Σ (plain − shortened) tokens over shortened results; a cut that showed more than plain counts negative"
|
|
110
|
+
value={formatSignedCompact(gross.savedTokens)}
|
|
111
|
+
hint={`${formatShare(gross.ofToolOutput)} of tool output · ${formatInteger(gross.shaped)} of ${formatInteger(gross.results)} results shortened`}
|
|
112
|
+
/>
|
|
113
|
+
<Stat
|
|
114
|
+
size="sm"
|
|
115
|
+
label="Fetched back beyond baseline"
|
|
116
|
+
title="Fetch-back tokens beyond what held-out results (shown whole) fetch anyway, per tool (reads split by whether the agent was likely to edit them); 90% interval below"
|
|
117
|
+
value={adjusted.excessTokens.value === null ? "–" : formatSignedCompact(-adjusted.excessTokens.value)}
|
|
118
|
+
hint={
|
|
119
|
+
<>
|
|
120
|
+
of {formatCompact(clawback.tokens)} fetched back
|
|
121
|
+
{adjusted.excessTokens.lo !== null && adjusted.excessTokens.hi !== null && (
|
|
122
|
+
<>
|
|
123
|
+
{" "}
|
|
124
|
+
<span className="dim">
|
|
125
|
+
·{" "}
|
|
126
|
+
<span className="spot-nowrap">
|
|
127
|
+
90% {formatInterval(adjusted.excessTokens, formatCompact)}
|
|
128
|
+
</span>
|
|
129
|
+
</span>
|
|
130
|
+
</>
|
|
131
|
+
)}
|
|
132
|
+
</>
|
|
133
|
+
}
|
|
134
|
+
/>
|
|
135
|
+
<Stat
|
|
136
|
+
size="sm"
|
|
137
|
+
label="Held out"
|
|
138
|
+
title={`Would-be cuts shown whole at random: the baseline per tool (under ${adjusted.minHoldouts} is thin)`}
|
|
139
|
+
value={formatInteger(stats.holdouts)}
|
|
140
|
+
hint={
|
|
141
|
+
<span className="spot-hero-tools">
|
|
142
|
+
{heldRows.map(row => (
|
|
143
|
+
<span
|
|
144
|
+
key={row.tool}
|
|
145
|
+
data-thin={row.thin}
|
|
146
|
+
title={
|
|
147
|
+
row.tool === OTHER_TOOLS
|
|
148
|
+
? heldFold.rest.map(rest => `${rest.tool} ${formatInteger(rest.holdouts)}`).join(", ")
|
|
149
|
+
: row.thin
|
|
150
|
+
? `${row.tool}: under ${adjusted.minHoldouts} held out`
|
|
151
|
+
: undefined
|
|
152
|
+
}
|
|
153
|
+
>
|
|
154
|
+
{row.tool} <span className="num">{formatInteger(row.holdouts)}</span>
|
|
155
|
+
{row.thin && <TriangleAlert size={11} aria-label="thin baseline" />}
|
|
156
|
+
</span>
|
|
157
|
+
))}
|
|
158
|
+
</span>
|
|
159
|
+
}
|
|
160
|
+
/>
|
|
161
|
+
<Stat
|
|
162
|
+
size="sm"
|
|
163
|
+
label="Score-free cuts"
|
|
164
|
+
title="Cuts a random coin made without Spotlight (the no-score fallback rules): what Spotlight's scores add is the difference"
|
|
165
|
+
value={formatInteger(stats.scoreFree)}
|
|
166
|
+
hint="cut without Spotlight, for comparison"
|
|
167
|
+
/>
|
|
168
|
+
<Stat
|
|
169
|
+
size="sm"
|
|
170
|
+
label="Sessions"
|
|
171
|
+
value={formatInteger(stats.sessions)}
|
|
172
|
+
hint="experiment sessions, all shaped"
|
|
173
|
+
/>
|
|
174
|
+
</div>
|
|
175
|
+
</section>
|
|
176
|
+
);
|
|
177
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { TriangleAlert } from "lucide-react";
|
|
2
|
+
import { formatCost, formatDurationMs, formatInteger } from "../data/formatters";
|
|
3
|
+
import type { SpotlightDashboardStats, SpotlightOverhead } from "../types";
|
|
4
|
+
import { Badge, Card, type Column, errorRateTone, Table } from "../ui";
|
|
5
|
+
import { formatShare } from "./meta";
|
|
6
|
+
|
|
7
|
+
/** p50 with p90 beside it, dim. */
|
|
8
|
+
function Percentiles({ p50, p90 }: { p50: number | null; p90: number | null }) {
|
|
9
|
+
if (p50 === null) return <span className="dim">–</span>;
|
|
10
|
+
return (
|
|
11
|
+
<span className="num">
|
|
12
|
+
{formatDurationMs(p50)}
|
|
13
|
+
<span className="dim"> / {formatDurationMs(p90)}</span>
|
|
14
|
+
</span>
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const COLUMNS: Column<SpotlightOverhead>[] = [
|
|
19
|
+
{
|
|
20
|
+
key: "failures",
|
|
21
|
+
header: "Spotlight failures",
|
|
22
|
+
align: "right",
|
|
23
|
+
sort: row => row.failureRate ?? -1,
|
|
24
|
+
title: "Results whose Spotlight calls failed or timed out, of those that called it: they were cut by the no-score fallback",
|
|
25
|
+
render: row =>
|
|
26
|
+
row.failureRate === null ? (
|
|
27
|
+
<span className="dim">–</span>
|
|
28
|
+
) : (
|
|
29
|
+
<Badge tone={errorRateTone(row.failureRate)} mono>
|
|
30
|
+
{formatShare(row.failureRate)}
|
|
31
|
+
</Badge>
|
|
32
|
+
),
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
key: "failedWait",
|
|
36
|
+
header: "Waited on failures",
|
|
37
|
+
align: "right",
|
|
38
|
+
sort: row => row.failedWaitMs,
|
|
39
|
+
title: "Wall time tool calls spent on Spotlight calls that failed or timed out",
|
|
40
|
+
render: row =>
|
|
41
|
+
row.failedWaitMs > 0 ? (
|
|
42
|
+
<span className="num">{formatDurationMs(row.failedWaitMs)}</span>
|
|
43
|
+
) : (
|
|
44
|
+
<span className="dim">–</span>
|
|
45
|
+
),
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
key: "results",
|
|
49
|
+
header: "Results",
|
|
50
|
+
align: "right",
|
|
51
|
+
sort: row => row.results,
|
|
52
|
+
title: "Results Spotlight evaluated, held-out ones included",
|
|
53
|
+
render: row => <span className="num">{formatInteger(row.results)}</span>,
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
key: "shape",
|
|
57
|
+
header: "Shaping p50 / p90",
|
|
58
|
+
align: "right",
|
|
59
|
+
sort: row => row.shapeMsP50 ?? -1,
|
|
60
|
+
title: "Wall time shaping added to a tool call",
|
|
61
|
+
render: row => <Percentiles p50={row.shapeMsP50} p90={row.shapeMsP90} />,
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
key: "spotlight",
|
|
65
|
+
header: "Spotlight p50 / p90",
|
|
66
|
+
align: "right",
|
|
67
|
+
sort: row => row.spotlightMsP50 ?? -1,
|
|
68
|
+
title: "Spotlight's own request time, over results whose calls all answered",
|
|
69
|
+
render: row => <Percentiles p50={row.spotlightMsP50} p90={row.spotlightMsP90} />,
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
key: "usd",
|
|
73
|
+
header: "Spotlight $",
|
|
74
|
+
align: "right",
|
|
75
|
+
sort: row => row.spotlightUsd,
|
|
76
|
+
render: row => <span className="num">{formatCost(row.spotlightUsd)}</span>,
|
|
77
|
+
},
|
|
78
|
+
];
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Failed Spotlight calls mean the cut came from the no-score fallback, not Spotlight's scorer: every comparison then
|
|
82
|
+
* partly measures the fallback. Called out above the table once the failure rate reaches the warning rate.
|
|
83
|
+
*/
|
|
84
|
+
function FailureCallout({ overhead }: { overhead: SpotlightOverhead }) {
|
|
85
|
+
const rate = overhead.failureRate;
|
|
86
|
+
if (rate === null) return null;
|
|
87
|
+
const tone = errorRateTone(rate);
|
|
88
|
+
if (tone === "ok") return null;
|
|
89
|
+
const waitedMs = overhead.failedWaitMs;
|
|
90
|
+
return (
|
|
91
|
+
<div className="spot-callout" data-tone={tone} role="note">
|
|
92
|
+
<TriangleAlert size={16} aria-hidden="true" />
|
|
93
|
+
<div>
|
|
94
|
+
<div className="spot-callout-head">
|
|
95
|
+
<span className="num">{formatShare(rate)}</span> of results that called Spotlight failed
|
|
96
|
+
</div>
|
|
97
|
+
<p className="spot-callout-copy">
|
|
98
|
+
A failed or timed-out call leaves the cut to the no-score fallback, so those results measure the fallback
|
|
99
|
+
rather than Spotlight's scorer. Read every comparison on this page with that share in mind.
|
|
100
|
+
{waitedMs > 0 && (
|
|
101
|
+
<>
|
|
102
|
+
{" "}
|
|
103
|
+
Tool calls waited <span className="num">{formatDurationMs(waitedMs)}</span> on those failures.
|
|
104
|
+
</>
|
|
105
|
+
)}
|
|
106
|
+
</p>
|
|
107
|
+
</div>
|
|
108
|
+
</div>
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** What shaping costs in latency, reliability and Spotlight's bill. */
|
|
113
|
+
export function Overhead({ stats, stale }: { stats: SpotlightDashboardStats; stale: boolean }) {
|
|
114
|
+
const { overhead } = stats;
|
|
115
|
+
const rows = overhead.results > 0 ? [overhead] : [];
|
|
116
|
+
return (
|
|
117
|
+
<Card
|
|
118
|
+
index={3}
|
|
119
|
+
title="Overhead"
|
|
120
|
+
description="What shaping costs: Spotlight failures, added latency and its bill"
|
|
121
|
+
flush
|
|
122
|
+
stale={stale}
|
|
123
|
+
footer="Every evaluated result counts, held-out ones included: they were shaped too, just not shown."
|
|
124
|
+
>
|
|
125
|
+
{overhead.results > 0 && <FailureCallout overhead={overhead} />}
|
|
126
|
+
<Table
|
|
127
|
+
rows={rows}
|
|
128
|
+
rowKey={() => "overhead"}
|
|
129
|
+
columns={COLUMNS}
|
|
130
|
+
dense
|
|
131
|
+
empty={<div className="empty">No evaluated results in this range.</div>}
|
|
132
|
+
/>
|
|
133
|
+
</Card>
|
|
134
|
+
);
|
|
135
|
+
}
|