@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.
Files changed (77) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dist/client/index.css +1 -1
  3. package/dist/client/index.js +102 -99
  4. package/dist/types/client/api.d.ts +4 -1
  5. package/dist/types/client/app/nav.d.ts +1 -1
  6. package/dist/types/client/charts/Chart.d.ts +16 -8
  7. package/dist/types/client/charts/DotWhisker.d.ts +50 -0
  8. package/dist/types/client/charts/Sparkline.d.ts +1 -1
  9. package/dist/types/client/charts/Waterfall.d.ts +26 -0
  10. package/dist/types/client/charts/index.d.ts +2 -0
  11. package/dist/types/client/charts/types.d.ts +8 -2
  12. package/dist/types/client/routes/SpotlightRoute.d.ts +7 -0
  13. package/dist/types/client/routes/index.d.ts +1 -0
  14. package/dist/types/client/spotlight/Compounding.d.ts +8 -0
  15. package/dist/types/client/spotlight/Coverage.d.ts +6 -0
  16. package/dist/types/client/spotlight/Economics.d.ts +10 -0
  17. package/dist/types/client/spotlight/FetchBack.d.ts +9 -0
  18. package/dist/types/client/spotlight/Hero.d.ts +12 -0
  19. package/dist/types/client/spotlight/Overhead.d.ts +6 -0
  20. package/dist/types/client/spotlight/Ranking.d.ts +6 -0
  21. package/dist/types/client/spotlight/ResultsFeed.d.ts +6 -0
  22. package/dist/types/client/spotlight/Savings.d.ts +10 -0
  23. package/dist/types/client/spotlight/ShareTrend.d.ts +8 -0
  24. package/dist/types/client/spotlight/Steps.d.ts +6 -0
  25. package/dist/types/client/spotlight/TimeCost.d.ts +6 -0
  26. package/dist/types/client/spotlight/chips.d.ts +16 -0
  27. package/dist/types/client/spotlight/meta.d.ts +64 -0
  28. package/dist/types/db.d.ts +26 -0
  29. package/dist/types/index.d.ts +2 -1
  30. package/dist/types/parser.d.ts +4 -0
  31. package/dist/types/rollup.d.ts +22 -1
  32. package/dist/types/shared-types.d.ts +419 -0
  33. package/dist/types/spotlight-aggregator.d.ts +189 -0
  34. package/dist/types/spotlight-bootstrap.d.ts +32 -0
  35. package/dist/types/spotlight-ingest.d.ts +258 -0
  36. package/dist/types/spotlight-measures.d.ts +51 -0
  37. package/package.json +5 -5
  38. package/src/aggregator.ts +3 -1
  39. package/src/client/App.tsx +3 -0
  40. package/src/client/api.ts +21 -0
  41. package/src/client/app/nav.ts +4 -1
  42. package/src/client/charts/Chart.tsx +105 -22
  43. package/src/client/charts/DotWhisker.tsx +149 -0
  44. package/src/client/charts/Sparkline.tsx +3 -2
  45. package/src/client/charts/Waterfall.tsx +86 -0
  46. package/src/client/charts/index.ts +2 -0
  47. package/src/client/charts/types.ts +9 -2
  48. package/src/client/react-css.d.ts +1 -1
  49. package/src/client/routes/SpotlightRoute.tsx +138 -0
  50. package/src/client/routes/index.ts +1 -0
  51. package/src/client/spotlight/Compounding.tsx +222 -0
  52. package/src/client/spotlight/Coverage.tsx +96 -0
  53. package/src/client/spotlight/Economics.tsx +167 -0
  54. package/src/client/spotlight/FetchBack.tsx +222 -0
  55. package/src/client/spotlight/Hero.tsx +177 -0
  56. package/src/client/spotlight/Overhead.tsx +135 -0
  57. package/src/client/spotlight/Ranking.tsx +248 -0
  58. package/src/client/spotlight/ResultsFeed.tsx +354 -0
  59. package/src/client/spotlight/Savings.tsx +355 -0
  60. package/src/client/spotlight/ShareTrend.tsx +103 -0
  61. package/src/client/spotlight/Steps.tsx +225 -0
  62. package/src/client/spotlight/TimeCost.tsx +64 -0
  63. package/src/client/spotlight/chips.tsx +63 -0
  64. package/src/client/spotlight/meta.ts +206 -0
  65. package/src/client/spotlight/spotlight.css +1387 -0
  66. package/src/client/styles.css +289 -1
  67. package/src/db.ts +480 -2
  68. package/src/index.ts +10 -1
  69. package/src/live.ts +21 -9
  70. package/src/parser.ts +44 -8
  71. package/src/rollup.ts +203 -10
  72. package/src/server.ts +42 -0
  73. package/src/shared-types.ts +453 -0
  74. package/src/spotlight-aggregator.ts +1738 -0
  75. package/src/spotlight-bootstrap.ts +110 -0
  76. package/src/spotlight-ingest.ts +981 -0
  77. package/src/spotlight-measures.ts +667 -0
@@ -0,0 +1,64 @@
1
+ import { Legend } from "../charts";
2
+ import { formatElapsed, formatInteger } from "../data/formatters";
3
+ import type { SpotlightDashboardStats } from "../types";
4
+ import { Card } from "../ui";
5
+ import { formatShare } from "./meta";
6
+
7
+ const LEGEND = [
8
+ { key: "fetch", label: "Fetch-back turns", color: "var(--spot-turns)" },
9
+ { key: "shape", label: "Shaping", color: "var(--spot-cost)" },
10
+ ];
11
+
12
+ /** Share of the experiment's agent time spent on requests that only fetched cut output back, and on shaping. */
13
+ export function TimeCost({ stats, stale }: { stats: SpotlightDashboardStats; stale: boolean }) {
14
+ const row = stats.timeCost;
15
+ const fetchShare = row.agentMs > 0 ? row.fetchBackMs / row.agentMs : 0;
16
+ const shapeShare = row.agentMs > 0 ? row.shapeMs / row.agentMs : 0;
17
+ // Shares are a few percent: scale the bars to the total (with headroom) so they stay readable.
18
+ const top = fetchShare + shapeShare;
19
+ const scale = top > 0 ? top * 1.15 : 1;
20
+ const fetchWidth = (fetchShare / scale) * 100;
21
+ const shapeWidth = (shapeShare / scale) * 100;
22
+
23
+ return (
24
+ <Card
25
+ index={3}
26
+ title="Time cost"
27
+ description="Share of the experiment sessions' model time spent on requests that only fetched cut output back, plus the time tool calls spent shaping"
28
+ actions={<Legend items={LEGEND} />}
29
+ stale={stale}
30
+ footer={`Model time is the summed duration of the experiment sessions' requests. A fetch-back turn is a request whose every tool call fetched back output a cut hid; shaping is the wall time tool calls spent shaping (Spotlight's requests included, held-out results too). Bars run to ${formatShare(scale)} of model time.`}
31
+ >
32
+ {row.agentMs === 0 ? (
33
+ <div className="empty">No experiment requests in this range.</div>
34
+ ) : (
35
+ <ul className="spot-time">
36
+ <li className="spot-time-row">
37
+ <span className="spot-time-label">Shaped sessions</span>
38
+ <span className="spot-time-track" aria-hidden="true">
39
+ <span
40
+ className="spot-time-fill"
41
+ style={{ width: `${fetchWidth}%`, background: "var(--spot-turns)" }}
42
+ />
43
+ <span
44
+ className="spot-time-fill"
45
+ style={{ width: `${shapeWidth}%`, background: "var(--spot-cost)" }}
46
+ />
47
+ </span>
48
+ <span className="spot-time-share num">{formatShare(top)}</span>
49
+ <span className="spot-time-detail">
50
+ <span className="num">{formatElapsed(row.fetchBackMs)}</span> in{" "}
51
+ <span className="num">{formatInteger(row.fetchBackTurns)}</span>{" "}
52
+ {row.fetchBackTurns === 1 ? "turn" : "turns"} ·{" "}
53
+ <span className="num">{formatElapsed(row.shapeMs)}</span> shaping
54
+ <span className="dim">
55
+ {" "}
56
+ of <span className="num">{formatElapsed(row.agentMs)}</span>
57
+ </span>
58
+ </span>
59
+ </li>
60
+ </ul>
61
+ )}
62
+ </Card>
63
+ );
64
+ }
@@ -0,0 +1,63 @@
1
+ import type { SpotlightCutGroup, SpotlightRecoveryKind } from "../types";
2
+ import { CUT_GROUP, RECOVERY_KIND } from "./meta";
3
+
4
+ /** Key to the fetch-back groups: each group's mark shape and color, its rule on hover. */
5
+ export function GroupLegend({ groups }: { groups: readonly SpotlightCutGroup[] }) {
6
+ return (
7
+ <div className="legend spot-group-legend">
8
+ {groups.map(group => (
9
+ <span
10
+ key={group}
11
+ className="legend-item"
12
+ title={CUT_GROUP[group].rule}
13
+ style={{ "--dot": CUT_GROUP[group].color }}
14
+ >
15
+ <span className="spot-group-mark" data-shape={CUT_GROUP[group].shape} aria-hidden="true" />
16
+ {CUT_GROUP[group].label}
17
+ </span>
18
+ ))}
19
+ </div>
20
+ );
21
+ }
22
+
23
+ /** Shows or hides the tools a card folded away (see `foldTools`). */
24
+ export function MoreTools({
25
+ hidden,
26
+ expanded,
27
+ onToggle,
28
+ }: {
29
+ hidden: readonly string[];
30
+ expanded: boolean;
31
+ onToggle: () => void;
32
+ }) {
33
+ return (
34
+ <button
35
+ type="button"
36
+ className="btn spot-more-tools"
37
+ data-variant="ghost"
38
+ data-size="sm"
39
+ aria-expanded={expanded}
40
+ title={expanded ? undefined : hidden.join(", ")}
41
+ onClick={onToggle}
42
+ >
43
+ {expanded ? "Show fewer tools" : `Show ${hidden.length} more tools`}
44
+ </button>
45
+ );
46
+ }
47
+
48
+ /** Recovery kind pill: icon, label (or a count) in the kind's color. */
49
+ export function KindBadge({ kind, count }: { kind: SpotlightRecoveryKind; count?: number }) {
50
+ const meta = RECOVERY_KIND[kind];
51
+ const Icon = meta.icon;
52
+ return (
53
+ <span
54
+ className="spot-kind"
55
+ style={{ "--kind": meta.color }}
56
+ title={count === undefined ? meta.rule : `${count} × ${meta.label.toLowerCase()}: ${meta.rule}`}
57
+ >
58
+ <Icon size={12} aria-hidden="true" />
59
+ {count === undefined ? meta.label : <span className="num">{count}</span>}
60
+ {count !== undefined && <span className="sr-only">{meta.plural}</span>}
61
+ </span>
62
+ );
63
+ }
@@ -0,0 +1,206 @@
1
+ /**
2
+ * Spotlight vocabulary shared by the page's sections: recovery-kind and
3
+ * tool labels, their colors (CSS variables from `spotlight.css`, so both
4
+ * themes resolve them) and signed number formatting.
5
+ */
6
+
7
+ import {
8
+ Archive,
9
+ Bot,
10
+ Braces,
11
+ CloudDownload,
12
+ Ellipsis,
13
+ FilePenLine,
14
+ FilePlus,
15
+ FileSearch,
16
+ FileText,
17
+ FolderSearch,
18
+ Globe,
19
+ Hourglass,
20
+ ListTodo,
21
+ type LucideIcon,
22
+ Network,
23
+ Repeat2,
24
+ ScanSearch,
25
+ SquareTerminal,
26
+ TextSearch,
27
+ Wrench,
28
+ } from "lucide-react";
29
+ import { formatCompact, formatCost } from "../data/formatters";
30
+ import type {
31
+ SpotlightCutGroup,
32
+ SpotlightDashboardStats,
33
+ SpotlightEstimate,
34
+ SpotlightGapBin,
35
+ SpotlightRecoveryKind,
36
+ SpotlightSizeBin,
37
+ SpotlightTool,
38
+ } from "../types";
39
+
40
+ export const RECOVERY_KINDS: readonly SpotlightRecoveryKind[] = ["artifact", "reread", "rerun"];
41
+
42
+ export interface RecoveryKindMeta {
43
+ label: string;
44
+ /** Plural, for waterfall steps and totals. */
45
+ plural: string;
46
+ icon: LucideIcon;
47
+ color: string;
48
+ /** One-line rule, for tooltips. */
49
+ rule: string;
50
+ }
51
+
52
+ export const RECOVERY_KIND: Record<SpotlightRecoveryKind, RecoveryKindMeta> = {
53
+ artifact: {
54
+ label: "Artifact dive",
55
+ plural: "Artifact dives",
56
+ icon: Archive,
57
+ color: "var(--spot-artifact)",
58
+ rule: "A later call read or searched an artifact the shortened result named",
59
+ },
60
+ reread: {
61
+ label: "Re-read",
62
+ plural: "Re-reads",
63
+ icon: FileSearch,
64
+ color: "var(--spot-reread)",
65
+ rule: "A later read of the same file overlapping the hidden lines, or the whole file again",
66
+ },
67
+ rerun: {
68
+ label: "Rerun",
69
+ plural: "Reruns",
70
+ icon: Repeat2,
71
+ color: "var(--spot-rerun)",
72
+ rule: "A call of the same tool within 12 calls repeating the shortened one: a bash command the same, with a pipe or redirect added, or ≥ 60% shared tokens; any other tool with ≥ 60% shared argument tokens",
73
+ },
74
+ };
75
+
76
+ /** The range's evaluated tools, the largest saving first (the order of `gross.byTool`). */
77
+ export function toolsBySaving(stats: SpotlightDashboardStats): SpotlightTool[] {
78
+ return stats.gross.byTool.map(row => row.tool);
79
+ }
80
+
81
+ /** Tools a card lists by name before the rest fold into `other` (or behind a "more tools" toggle). */
82
+ export const TOP_TOOLS = 6;
83
+
84
+ /** The folded rest's label where tools are rows or columns. */
85
+ export const OTHER_TOOLS = "other";
86
+
87
+ /**
88
+ * Split tools (ordered) into the first `limit` a card lists and the rest it folds; a lone leftover is listed
89
+ * instead.
90
+ */
91
+ export function foldTools<T>(items: readonly T[], limit = TOP_TOOLS): { listed: readonly T[]; rest: readonly T[] } {
92
+ if (items.length <= limit + 1) return { listed: items, rest: [] };
93
+ return { listed: items.slice(0, limit), rest: items.slice(limit) };
94
+ }
95
+
96
+ export interface CutGroupMeta {
97
+ label: string;
98
+ color: string;
99
+ /** Its mark in dot-whisker charts. */
100
+ shape: "dot" | "ring" | "diamond" | "square";
101
+ /** One-line definition, for tooltips and legends. */
102
+ rule: string;
103
+ }
104
+
105
+ /**
106
+ * The fetch-back comparison's groups. The cut is vivid; the score-free cut
107
+ * (the same shaping without Spotlight's scores) takes its own hue; the two
108
+ * baselines are neutral inks, the held-out one (the true counterfactual) the
109
+ * stronger.
110
+ */
111
+ export const CUT_GROUP: Record<SpotlightCutGroup, CutGroupMeta> = {
112
+ applied: {
113
+ label: "Shortened",
114
+ color: "var(--spot-applied)",
115
+ shape: "dot",
116
+ rule: "Spotlight scored the result, shaping shortened it and the model saw the cut",
117
+ },
118
+ scoreFree: {
119
+ label: "Score-free",
120
+ color: "var(--spot-score-free)",
121
+ shape: "square",
122
+ rule: "A random coin skipped Spotlight: the no-score fallback rules shortened it and the model saw the cut",
123
+ },
124
+ holdout: {
125
+ label: "Held out",
126
+ color: "var(--spot-holdout)",
127
+ shape: "ring",
128
+ rule: "Spotlight would have shortened it, but it was held out at random and the model saw it whole",
129
+ },
130
+ reference: {
131
+ label: "Unchanged",
132
+ color: "var(--spot-reference)",
133
+ shape: "diamond",
134
+ rule: "Results shaping left unchanged, re-weighted to the cuts' original-size mix",
135
+ },
136
+ gated: {
137
+ label: "Gated",
138
+ color: "var(--spot-gated)",
139
+ shape: "ring",
140
+ rule: "A read pi's former edit gate (removed) kept whole: the model saw it uncut",
141
+ },
142
+ };
143
+
144
+ export const CUT_GROUPS: readonly SpotlightCutGroup[] = ["applied", "scoreFree", "holdout", "gated", "reference"];
145
+
146
+ export const SIZE_BINS: readonly SpotlightSizeBin[] = ["<250", "250–1k", "1k–4k", "≥4k"];
147
+
148
+ export const GAP_BINS: readonly SpotlightGapBin[] = ["1", "2", "3", "4–6", "7–12"];
149
+
150
+ const TOOL_ICON: Readonly<Record<string, LucideIcon>> = {
151
+ read: FileText,
152
+ bash: SquareTerminal,
153
+ eval: Braces,
154
+ grep: TextSearch,
155
+ find: ScanSearch,
156
+ glob: FolderSearch,
157
+ edit: FilePenLine,
158
+ write: FilePlus,
159
+ web_search: Globe,
160
+ task: Bot,
161
+ todo: ListTodo,
162
+ wait: Hourglass,
163
+ fetch: CloudDownload,
164
+ lsp: Network,
165
+ [OTHER_TOOLS]: Ellipsis,
166
+ };
167
+
168
+ /** A tool's icon; tools without one of their own (MCP and extension tools) get a generic wrench. */
169
+ export function toolIcon(tool: SpotlightTool): LucideIcon {
170
+ return Object.hasOwn(TOOL_ICON, tool) ? TOOL_ICON[tool] : Wrench;
171
+ }
172
+
173
+ /** What one time bucket of the range is called in prose ("hour", "day"). */
174
+ export function bucketNoun(bucketMs: number): string {
175
+ if (bucketMs >= 86_400_000) return "day";
176
+ if (bucketMs >= 3_600_000) return "hour";
177
+ return `${Math.round(bucketMs / 60_000)} minutes`;
178
+ }
179
+
180
+ /** Compact token count with a real minus sign. */
181
+ export function formatSignedCompact(value: number): string {
182
+ return value < 0 ? `−${formatCompact(-value)}` : formatCompact(value);
183
+ }
184
+
185
+ /** Dollar amount with a real minus sign (`formatCost` alone prints `$-0.12`). */
186
+ export function formatSignedCost(value: number): string {
187
+ return value < 0 ? `−${formatCost(-value)}` : formatCost(value);
188
+ }
189
+
190
+ /** A relative change as a signed percentage (`+12%`, `−3.5%`), `–` when undefined. */
191
+ export function formatSignedShare(value: number | null): string {
192
+ return value !== null && value > 0 ? `+${formatShare(value)}` : formatShare(value);
193
+ }
194
+
195
+ /** An estimate's 90% interval as `lo–hi`, empty when it has none. */
196
+ export function formatInterval(estimate: SpotlightEstimate, format: (value: number) => string): string {
197
+ return estimate.lo === null || estimate.hi === null ? "" : `${format(estimate.lo)}–${format(estimate.hi)}`;
198
+ }
199
+
200
+ /** Share as a whole or one-decimal percentage, `–` when undefined. */
201
+ export function formatShare(value: number | null): string {
202
+ if (value === null || !Number.isFinite(value)) return "–";
203
+ if (value === 0) return "0%";
204
+ const pct = Math.abs(value * 100);
205
+ return `${value < 0 ? "−" : ""}${pct >= 10 ? pct.toFixed(0) : pct.toFixed(1)}%`;
206
+ }