@oh-my-pi/omp-stats 18.16.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 +7 -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/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/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,96 @@
|
|
|
1
|
+
import { BarList, Legend, ShareBar } from "../charts";
|
|
2
|
+
import { formatCompact, formatInteger } from "../data/formatters";
|
|
3
|
+
import type { SpotlightDashboardStats } from "../types";
|
|
4
|
+
import { Badge, Card } from "../ui";
|
|
5
|
+
import { foldTools, formatShare } from "./meta";
|
|
6
|
+
|
|
7
|
+
const SHAPED_COLOR = "var(--spot-applied)";
|
|
8
|
+
const UNSHAPED_COLOR = "var(--spot-unshaped)";
|
|
9
|
+
|
|
10
|
+
/** Which tools' output the models of experiment sessions saw, shaped and not: where shaping could reach next. */
|
|
11
|
+
export function Coverage({ stats, stale }: { stats: SpotlightDashboardStats; stale: boolean }) {
|
|
12
|
+
const rows = [...stats.coverage].sort((a, b) => b.tokens - a.tokens);
|
|
13
|
+
const { listed, rest } = foldTools(rows);
|
|
14
|
+
let shaped = 0;
|
|
15
|
+
let unshaped = 0;
|
|
16
|
+
for (const row of rows) {
|
|
17
|
+
if (row.shaped) shaped += row.share;
|
|
18
|
+
else unshaped += row.share;
|
|
19
|
+
}
|
|
20
|
+
const items = listed.map(row => ({
|
|
21
|
+
key: row.tool,
|
|
22
|
+
label: (
|
|
23
|
+
<>
|
|
24
|
+
<span className="spot-coverage-tool">{row.tool}</span>
|
|
25
|
+
{row.shaped && <Badge>shaped</Badge>}
|
|
26
|
+
<span className="dim num">{formatInteger(row.calls)} calls</span>
|
|
27
|
+
</>
|
|
28
|
+
),
|
|
29
|
+
value: row.share,
|
|
30
|
+
display: `${formatShare(row.share)} · ${formatCompact(row.tokens)}`,
|
|
31
|
+
color: row.shaped ? SHAPED_COLOR : UNSHAPED_COLOR,
|
|
32
|
+
}));
|
|
33
|
+
if (rest.length > 0) {
|
|
34
|
+
let share = 0;
|
|
35
|
+
let tokens = 0;
|
|
36
|
+
let calls = 0;
|
|
37
|
+
for (const row of rest) {
|
|
38
|
+
share += row.share;
|
|
39
|
+
tokens += row.tokens;
|
|
40
|
+
calls += row.calls;
|
|
41
|
+
}
|
|
42
|
+
items.push({
|
|
43
|
+
key: "\u0000rest",
|
|
44
|
+
label: (
|
|
45
|
+
<>
|
|
46
|
+
<span className="spot-coverage-tool">
|
|
47
|
+
{formatInteger(rest.length)} more {rest.length === 1 ? "tool" : "tools"}
|
|
48
|
+
</span>
|
|
49
|
+
<span className="dim num">{formatInteger(calls)} calls</span>
|
|
50
|
+
</>
|
|
51
|
+
),
|
|
52
|
+
value: share,
|
|
53
|
+
display: `${formatShare(share)} · ${formatCompact(tokens)}`,
|
|
54
|
+
color: rest.some(row => row.shaped) ? SHAPED_COLOR : UNSHAPED_COLOR,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return (
|
|
59
|
+
<Card
|
|
60
|
+
index={5}
|
|
61
|
+
title="Not shaped yet"
|
|
62
|
+
description="Share of the tool output experiment sessions' models saw, by tool: the unshaped ones are where shaping could reach next"
|
|
63
|
+
actions={
|
|
64
|
+
<Legend
|
|
65
|
+
items={[
|
|
66
|
+
{ key: "shaped", label: "Shaped", color: SHAPED_COLOR },
|
|
67
|
+
{ key: "unshaped", label: "Not shaped", color: UNSHAPED_COLOR },
|
|
68
|
+
]}
|
|
69
|
+
/>
|
|
70
|
+
}
|
|
71
|
+
stale={stale}
|
|
72
|
+
footer="Tokens are tool-result chars ÷ 4 over the experiment's sessions in the range; a tool counts as shaped when the experiment evaluated any of its results in the range."
|
|
73
|
+
>
|
|
74
|
+
{rows.length === 0 ? (
|
|
75
|
+
<div className="empty">No tool output in this range.</div>
|
|
76
|
+
) : (
|
|
77
|
+
<div className="spot-coverage">
|
|
78
|
+
<div className="spot-coverage-summary">
|
|
79
|
+
<div className="spot-coverage-headline">
|
|
80
|
+
<span className="num">{formatShare(unshaped)}</span> of tool output comes from tools the experiment
|
|
81
|
+
did not shape in this range
|
|
82
|
+
</div>
|
|
83
|
+
<ShareBar
|
|
84
|
+
height={8}
|
|
85
|
+
segments={[
|
|
86
|
+
{ key: "shaped", label: "Shaped", value: shaped, color: SHAPED_COLOR },
|
|
87
|
+
{ key: "unshaped", label: "Not shaped", value: unshaped, color: UNSHAPED_COLOR },
|
|
88
|
+
]}
|
|
89
|
+
/>
|
|
90
|
+
</div>
|
|
91
|
+
<BarList items={items} />
|
|
92
|
+
</div>
|
|
93
|
+
)}
|
|
94
|
+
</Card>
|
|
95
|
+
);
|
|
96
|
+
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { formatCompact, formatCost, formatInteger } from "../data/formatters";
|
|
2
|
+
import type { SpotlightCutEconomics, SpotlightDashboardStats } from "../types";
|
|
3
|
+
import { Card } from "../ui";
|
|
4
|
+
import { foldTools, formatSignedCost, OTHER_TOOLS, toolIcon, toolsBySaving } from "./meta";
|
|
5
|
+
|
|
6
|
+
export interface EconomicsProps {
|
|
7
|
+
stats: SpotlightDashboardStats;
|
|
8
|
+
stale: boolean;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Tint strength of the strongest cell; the rest scale with √|net| so the sign reads first, size second, and one
|
|
13
|
+
* outlier does not wash out every other cell.
|
|
14
|
+
*/
|
|
15
|
+
const MAX_TINT = 30;
|
|
16
|
+
const MIN_TINT = 5;
|
|
17
|
+
|
|
18
|
+
/** Narrowest tool column before the table scrolls sideways instead of squeezing its cells. */
|
|
19
|
+
const TOOL_COLUMN_PX = 120;
|
|
20
|
+
|
|
21
|
+
function cellTitle(cell: SpotlightCutEconomics): string {
|
|
22
|
+
return [
|
|
23
|
+
`${cell.tool}: ${formatInteger(cell.cuts)} cuts`,
|
|
24
|
+
`Saved ${formatCompact(cell.savedTokens)} tokens (${formatCost(cell.savedUsd)})`,
|
|
25
|
+
`Fetched back ${formatCompact(cell.recoveredTokens)} tokens (${formatCost(cell.recoveryUsd)})`,
|
|
26
|
+
`${formatInteger(cell.extraTurns)} extra turns (${formatCost(cell.turnUsd)})`,
|
|
27
|
+
`Net ${formatSignedCost(cell.netUsd)}`,
|
|
28
|
+
].join("\n");
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** The folded tools' cells summed into an `other` cell. */
|
|
32
|
+
function otherCell(cells: readonly SpotlightCutEconomics[]): SpotlightCutEconomics | undefined {
|
|
33
|
+
if (cells.length === 0) return undefined;
|
|
34
|
+
const sum: SpotlightCutEconomics = {
|
|
35
|
+
tool: OTHER_TOOLS,
|
|
36
|
+
cuts: 0,
|
|
37
|
+
savedTokens: 0,
|
|
38
|
+
recoveredTokens: 0,
|
|
39
|
+
extraTurns: 0,
|
|
40
|
+
savedUsd: 0,
|
|
41
|
+
recoveryUsd: 0,
|
|
42
|
+
turnUsd: 0,
|
|
43
|
+
netUsd: 0,
|
|
44
|
+
extraTurnsPer100Cuts: 0,
|
|
45
|
+
netUsdPer100Cuts: 0,
|
|
46
|
+
};
|
|
47
|
+
for (const cell of cells) {
|
|
48
|
+
sum.cuts += cell.cuts;
|
|
49
|
+
sum.savedTokens += cell.savedTokens;
|
|
50
|
+
sum.recoveredTokens += cell.recoveredTokens;
|
|
51
|
+
sum.extraTurns += cell.extraTurns;
|
|
52
|
+
sum.savedUsd += cell.savedUsd;
|
|
53
|
+
sum.recoveryUsd += cell.recoveryUsd;
|
|
54
|
+
sum.turnUsd += cell.turnUsd;
|
|
55
|
+
sum.netUsd += cell.netUsd;
|
|
56
|
+
}
|
|
57
|
+
if (sum.cuts > 0) {
|
|
58
|
+
sum.extraTurnsPer100Cuts = (sum.extraTurns * 100) / sum.cuts;
|
|
59
|
+
sum.netUsdPer100Cuts = (sum.netUsd * 100) / sum.cuts;
|
|
60
|
+
}
|
|
61
|
+
return sum;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Per tool: what a hundred cuts earn after fetch-backs, extra turns and Spotlight's bill. Tools with cuts, the
|
|
66
|
+
* largest saving first; past the top ones the rest sum into one `other` column.
|
|
67
|
+
*/
|
|
68
|
+
export function Economics({ stats, stale }: EconomicsProps) {
|
|
69
|
+
const cut = new Set(stats.economics.filter(cell => cell.cuts > 0).map(cell => cell.tool));
|
|
70
|
+
const { listed, rest } = foldTools(toolsBySaving(stats).filter(tool => cut.has(tool)));
|
|
71
|
+
const folded = new Set(rest);
|
|
72
|
+
const cells = new Map(stats.economics.filter(cell => !folded.has(cell.tool)).map(cell => [cell.tool, cell]));
|
|
73
|
+
const other = otherCell(stats.economics.filter(cell => folded.has(cell.tool)));
|
|
74
|
+
if (other) cells.set(OTHER_TOOLS, other);
|
|
75
|
+
const tools = rest.length > 0 ? [...listed, OTHER_TOOLS] : listed;
|
|
76
|
+
let maxAbs = 0;
|
|
77
|
+
for (const cell of cells.values()) if (cell.cuts > 0) maxAbs = Math.max(maxAbs, Math.abs(cell.netUsdPer100Cuts));
|
|
78
|
+
|
|
79
|
+
return (
|
|
80
|
+
<Card
|
|
81
|
+
index={3}
|
|
82
|
+
title="What cuts earn, by tool"
|
|
83
|
+
description="Net dollars per 100 cuts after fetch-backs, extra turns and Spotlight's own bill"
|
|
84
|
+
actions={
|
|
85
|
+
maxAbs > 0 && (
|
|
86
|
+
<span className="spot-econ-scale" aria-hidden="true">
|
|
87
|
+
<span className="num">{formatSignedCost(-maxAbs)}</span>
|
|
88
|
+
<span className="spot-econ-ramp" />
|
|
89
|
+
<span className="num">{formatSignedCost(maxAbs)}</span>
|
|
90
|
+
</span>
|
|
91
|
+
)
|
|
92
|
+
}
|
|
93
|
+
stale={stale}
|
|
94
|
+
footer="Shortened results only; held-out ones save nothing. Every fetch-back counts against the cut here, the share held-out results show comes back anyway included, so this is the cautious view. An extra turn is a request whose every tool call fetched back output these cuts hid. Dollars price saved and fetched-back tokens as carried context, like Compounding."
|
|
95
|
+
>
|
|
96
|
+
{tools.length === 0 ? (
|
|
97
|
+
<div className="empty">No shortened results in this range.</div>
|
|
98
|
+
) : (
|
|
99
|
+
<div className="spot-econ-scroll">
|
|
100
|
+
<table className="spot-econ" style={{ minWidth: `${tools.length * TOOL_COLUMN_PX}px` }}>
|
|
101
|
+
<thead>
|
|
102
|
+
<tr>
|
|
103
|
+
{tools.map(tool => {
|
|
104
|
+
const Icon = toolIcon(tool);
|
|
105
|
+
return (
|
|
106
|
+
<th key={tool} scope="col" title={tool === OTHER_TOOLS ? rest.join(", ") : tool}>
|
|
107
|
+
<span className="row spot-econ-tool">
|
|
108
|
+
<Icon size={13} aria-hidden="true" />
|
|
109
|
+
<span className="spot-econ-tool-name">{tool}</span>
|
|
110
|
+
</span>
|
|
111
|
+
</th>
|
|
112
|
+
);
|
|
113
|
+
})}
|
|
114
|
+
</tr>
|
|
115
|
+
</thead>
|
|
116
|
+
<tbody>
|
|
117
|
+
<tr>
|
|
118
|
+
{tools.map(tool => {
|
|
119
|
+
const cell = cells.get(tool);
|
|
120
|
+
if (cell === undefined || cell.cuts === 0) {
|
|
121
|
+
return (
|
|
122
|
+
<td key={tool}>
|
|
123
|
+
<div className="spot-econ-cell" data-empty="true">
|
|
124
|
+
<span className="dim">No cuts</span>
|
|
125
|
+
</div>
|
|
126
|
+
</td>
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
const net = cell.netUsdPer100Cuts;
|
|
130
|
+
const tint =
|
|
131
|
+
maxAbs > 0 ? MIN_TINT + Math.sqrt(Math.abs(net) / maxAbs) * (MAX_TINT - MIN_TINT) : 0;
|
|
132
|
+
const tone = net < 0 ? "bad" : net > 0 ? "ok" : "neutral";
|
|
133
|
+
return (
|
|
134
|
+
<td key={tool}>
|
|
135
|
+
<div
|
|
136
|
+
className="spot-econ-cell"
|
|
137
|
+
data-tone={tone}
|
|
138
|
+
style={{ "--tint": `${tint}%` }}
|
|
139
|
+
title={cellTitle(cell)}
|
|
140
|
+
>
|
|
141
|
+
<span className="spot-econ-net num">{formatSignedCost(net)}</span>
|
|
142
|
+
<dl className="spot-econ-facts">
|
|
143
|
+
<div>
|
|
144
|
+
<dt>cuts</dt>
|
|
145
|
+
<dd className="num">{formatInteger(cell.cuts)}</dd>
|
|
146
|
+
</div>
|
|
147
|
+
<div>
|
|
148
|
+
<dt>saved</dt>
|
|
149
|
+
<dd className="num">{formatCompact(cell.savedTokens)}</dd>
|
|
150
|
+
</div>
|
|
151
|
+
<div>
|
|
152
|
+
<dt>extra turns /100</dt>
|
|
153
|
+
<dd className="num">{cell.extraTurnsPer100Cuts.toFixed(1)}</dd>
|
|
154
|
+
</div>
|
|
155
|
+
</dl>
|
|
156
|
+
</div>
|
|
157
|
+
</td>
|
|
158
|
+
);
|
|
159
|
+
})}
|
|
160
|
+
</tr>
|
|
161
|
+
</tbody>
|
|
162
|
+
</table>
|
|
163
|
+
</div>
|
|
164
|
+
)}
|
|
165
|
+
</Card>
|
|
166
|
+
);
|
|
167
|
+
}
|
|
@@ -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
|
+
}
|