modelrig 0.4.0 → 0.6.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 (189) hide show
  1. package/CHANGELOG.md +238 -0
  2. package/README.md +65 -3
  3. package/dist/adapters/anthropic.d.ts.map +1 -1
  4. package/dist/adapters/anthropic.js +36 -7
  5. package/dist/adapters/attempt-meta-map.d.ts +63 -0
  6. package/dist/adapters/attempt-meta-map.d.ts.map +1 -0
  7. package/dist/adapters/attempt-meta-map.js +154 -0
  8. package/dist/adapters/deepseek.d.ts.map +1 -1
  9. package/dist/adapters/deepseek.js +25 -2
  10. package/dist/adapters/gemini.d.ts +39 -0
  11. package/dist/adapters/gemini.d.ts.map +1 -1
  12. package/dist/adapters/gemini.js +82 -4
  13. package/dist/adapters/grok.d.ts.map +1 -1
  14. package/dist/adapters/grok.js +25 -2
  15. package/dist/adapters/openai-compat.d.ts.map +1 -1
  16. package/dist/adapters/openai-compat.js +25 -2
  17. package/dist/adapters/openai-stream.d.ts +2 -0
  18. package/dist/adapters/openai-stream.d.ts.map +1 -1
  19. package/dist/adapters/openai-stream.js +23 -3
  20. package/dist/adapters/openai.d.ts.map +1 -1
  21. package/dist/adapters/openai.js +36 -6
  22. package/dist/artifacts/artifact.d.ts +3 -2
  23. package/dist/artifacts/artifact.d.ts.map +1 -1
  24. package/dist/artifacts/artifact.js +35 -9
  25. package/dist/artifacts/classification.d.ts +27 -0
  26. package/dist/artifacts/classification.d.ts.map +1 -0
  27. package/dist/artifacts/classification.js +38 -0
  28. package/dist/artifacts/index.d.ts +3 -1
  29. package/dist/artifacts/index.d.ts.map +1 -1
  30. package/dist/artifacts/index.js +10 -1
  31. package/dist/artifacts/run-context.d.ts +33 -2
  32. package/dist/artifacts/run-context.d.ts.map +1 -1
  33. package/dist/artifacts/run-context.js +95 -1
  34. package/dist/artifacts/scrub.d.ts +26 -0
  35. package/dist/artifacts/scrub.d.ts.map +1 -1
  36. package/dist/artifacts/scrub.js +53 -0
  37. package/dist/artifacts/step.d.ts +9 -0
  38. package/dist/artifacts/step.d.ts.map +1 -1
  39. package/dist/artifacts/step.js +9 -0
  40. package/dist/bakeoff/engine.d.ts +46 -4
  41. package/dist/bakeoff/engine.d.ts.map +1 -1
  42. package/dist/bakeoff/engine.js +269 -19
  43. package/dist/bakeoff/eval-cases.d.ts +5 -0
  44. package/dist/bakeoff/eval-cases.d.ts.map +1 -1
  45. package/dist/bakeoff/eval-cases.js +3 -0
  46. package/dist/bakeoff/judge.d.ts +39 -0
  47. package/dist/bakeoff/judge.d.ts.map +1 -0
  48. package/dist/bakeoff/judge.js +102 -0
  49. package/dist/bakeoff/quality.d.ts +44 -0
  50. package/dist/bakeoff/quality.d.ts.map +1 -0
  51. package/dist/bakeoff/quality.js +126 -0
  52. package/dist/bakeoff/reference.d.ts +34 -0
  53. package/dist/bakeoff/reference.d.ts.map +1 -0
  54. package/dist/bakeoff/reference.js +68 -0
  55. package/dist/bakeoff/replay.d.ts +25 -0
  56. package/dist/bakeoff/replay.d.ts.map +1 -1
  57. package/dist/bakeoff/replay.js +65 -2
  58. package/dist/bakeoff/report.d.ts +8 -1
  59. package/dist/bakeoff/report.d.ts.map +1 -1
  60. package/dist/bakeoff/report.js +83 -12
  61. package/dist/bakeoff/samples.d.ts +35 -2
  62. package/dist/bakeoff/samples.d.ts.map +1 -1
  63. package/dist/bakeoff/samples.js +67 -6
  64. package/dist/bakeoff/stats.d.ts +17 -1
  65. package/dist/bakeoff/stats.d.ts.map +1 -1
  66. package/dist/bakeoff/stats.js +67 -0
  67. package/dist/bakeoff/types.d.ts +76 -5
  68. package/dist/bakeoff/types.d.ts.map +1 -1
  69. package/dist/bakeoff/types.js +4 -3
  70. package/dist/cli-query.d.ts +14 -0
  71. package/dist/cli-query.d.ts.map +1 -0
  72. package/dist/cli-query.js +59 -0
  73. package/dist/cli.d.ts +2 -1
  74. package/dist/cli.d.ts.map +1 -1
  75. package/dist/cli.js +47 -24
  76. package/dist/config.d.ts +6 -0
  77. package/dist/config.d.ts.map +1 -1
  78. package/dist/config.js +14 -0
  79. package/dist/detect/detector.d.ts +82 -0
  80. package/dist/detect/detector.d.ts.map +1 -0
  81. package/dist/detect/detector.js +23 -0
  82. package/dist/index.d.ts +34 -23
  83. package/dist/index.d.ts.map +1 -1
  84. package/dist/index.js +49 -12
  85. package/dist/query/analytics.d.ts +84 -0
  86. package/dist/query/analytics.d.ts.map +1 -0
  87. package/dist/query/analytics.js +280 -0
  88. package/dist/query/columns.d.ts +59 -0
  89. package/dist/query/columns.d.ts.map +1 -0
  90. package/dist/query/columns.js +89 -0
  91. package/dist/query/grammar.d.ts +74 -7
  92. package/dist/query/grammar.d.ts.map +1 -1
  93. package/dist/query/grammar.js +179 -71
  94. package/dist/query/saved.d.ts +26 -0
  95. package/dist/query/saved.d.ts.map +1 -0
  96. package/dist/query/saved.js +51 -0
  97. package/dist/registry/select.d.ts +59 -0
  98. package/dist/registry/select.d.ts.map +1 -1
  99. package/dist/registry/select.js +66 -0
  100. package/dist/routes/loader-warnings.d.ts +28 -0
  101. package/dist/routes/loader-warnings.d.ts.map +1 -0
  102. package/dist/routes/loader-warnings.js +56 -0
  103. package/dist/routes/loader.d.ts +1 -11
  104. package/dist/routes/loader.d.ts.map +1 -1
  105. package/dist/routes/loader.js +54 -31
  106. package/dist/routes/objective-loader.d.ts +35 -0
  107. package/dist/routes/objective-loader.d.ts.map +1 -0
  108. package/dist/routes/objective-loader.js +286 -0
  109. package/dist/run/accept.d.ts +43 -0
  110. package/dist/run/accept.d.ts.map +1 -0
  111. package/dist/run/accept.js +65 -0
  112. package/dist/run/cost.d.ts +13 -0
  113. package/dist/run/cost.d.ts.map +1 -1
  114. package/dist/run/cost.js +27 -0
  115. package/dist/run/dispatch.d.ts +24 -1
  116. package/dist/run/dispatch.d.ts.map +1 -1
  117. package/dist/run/dispatch.js +19 -0
  118. package/dist/run/ladder-key.d.ts +4 -0
  119. package/dist/run/ladder-key.d.ts.map +1 -1
  120. package/dist/run/ladder-key.js +3 -0
  121. package/dist/run/quality-gate.d.ts +39 -0
  122. package/dist/run/quality-gate.d.ts.map +1 -0
  123. package/dist/run/quality-gate.js +70 -0
  124. package/dist/run/raw-defaults.d.ts +52 -0
  125. package/dist/run/raw-defaults.d.ts.map +1 -0
  126. package/dist/run/raw-defaults.js +123 -0
  127. package/dist/run/raw.d.ts +12 -0
  128. package/dist/run/raw.d.ts.map +1 -1
  129. package/dist/run/raw.js +107 -8
  130. package/dist/run/record-attempt.d.ts.map +1 -1
  131. package/dist/run/record-attempt.js +22 -1
  132. package/dist/run/resolve.d.ts +17 -1
  133. package/dist/run/resolve.d.ts.map +1 -1
  134. package/dist/run/resolve.js +20 -4
  135. package/dist/run/routing-docs.d.ts.map +1 -1
  136. package/dist/run/routing-docs.js +12 -5
  137. package/dist/run/run.d.ts +7 -1
  138. package/dist/run/run.d.ts.map +1 -1
  139. package/dist/run/run.js +54 -69
  140. package/dist/run/variant.d.ts +43 -0
  141. package/dist/run/variant.d.ts.map +1 -0
  142. package/dist/run/variant.js +86 -0
  143. package/dist/savings/emit.d.ts +33 -0
  144. package/dist/savings/emit.d.ts.map +1 -1
  145. package/dist/savings/emit.js +75 -3
  146. package/dist/settings/index.d.ts +1 -1
  147. package/dist/settings/index.d.ts.map +1 -1
  148. package/dist/settings/index.js +2 -1
  149. package/dist/settings/task-settings.d.ts +10 -0
  150. package/dist/settings/task-settings.d.ts.map +1 -1
  151. package/dist/settings/task-settings.js +17 -0
  152. package/dist/telemetry/attempt-meta.d.ts +178 -0
  153. package/dist/telemetry/attempt-meta.d.ts.map +1 -0
  154. package/dist/telemetry/attempt-meta.js +319 -0
  155. package/dist/telemetry/exporter.d.ts.map +1 -1
  156. package/dist/telemetry/exporter.js +6 -0
  157. package/dist/telemetry/ingest-protocol.d.ts +11 -0
  158. package/dist/telemetry/ingest-protocol.d.ts.map +1 -1
  159. package/dist/telemetry/ingest-protocol.js +3 -0
  160. package/dist/telemetry/otel-map.d.ts +10 -0
  161. package/dist/telemetry/otel-map.d.ts.map +1 -1
  162. package/dist/telemetry/otel-map.js +64 -0
  163. package/dist/telemetry/store-migrations.d.ts.map +1 -1
  164. package/dist/telemetry/store-migrations.js +15 -0
  165. package/dist/telemetry/store.d.ts.map +1 -1
  166. package/dist/telemetry/store.js +25 -7
  167. package/dist/types/failures-teaching.d.ts.map +1 -1
  168. package/dist/types/failures-teaching.js +20 -0
  169. package/dist/types/failures.d.ts +1 -1
  170. package/dist/types/failures.d.ts.map +1 -1
  171. package/dist/types/failures.js +1 -0
  172. package/dist/types/provider.d.ts +9 -0
  173. package/dist/types/provider.d.ts.map +1 -1
  174. package/dist/types/quality.d.ts +41 -0
  175. package/dist/types/quality.d.ts.map +1 -0
  176. package/dist/types/quality.js +12 -0
  177. package/dist/types/route.d.ts +120 -0
  178. package/dist/types/route.d.ts.map +1 -1
  179. package/dist/types/telemetry.d.ts +13 -0
  180. package/dist/types/telemetry.d.ts.map +1 -1
  181. package/dist/validate-cli.d.ts.map +1 -1
  182. package/dist/validate-cli.js +10 -0
  183. package/dist/watch/evaluate.d.ts +27 -1
  184. package/dist/watch/evaluate.d.ts.map +1 -1
  185. package/dist/watch/evaluate.js +13 -0
  186. package/dist/watch/proposals.d.ts +28 -0
  187. package/dist/watch/proposals.d.ts.map +1 -1
  188. package/dist/watch/proposals.js +51 -0
  189. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"samples.d.ts","sourceRoot":"","sources":["../../src/bakeoff/samples.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAEnD,4EAA4E;AAC5E,eAAO,MAAM,gBAAgB,QAAa,CAAC;AAE3C,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,KAAK,CAAC;AAE/C,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACjD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,qDAAqD;AACrD,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACjD,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC;QAC9B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;QAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;QAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;QACnC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;QAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;QACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;KAC5B,CAAC,CAAC;CACJ;AAED,0EAA0E;AAC1E,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,eAAe,EACrB,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,GAAG,IAAI,EACxB,OAAO,EAAE,SAAS,MAAM,EAAE,GACzB,MAAM,EAAE,CAKV;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAOjE;AAED;+DAC+D;AAC/D,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,SAAK,GAAG,MAAM,CAEhE;AAED,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,SAAS,UAAU,EAAE,EAC3B,SAAS,EAAE,MAAM,GAChB,aAAa,EAAE,CAoBjB;AAED,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;CACjC;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,CAAC,OAAO,EAAE,SAAS,aAAa,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzD,2EAA2E;IAC3E,KAAK,CAAC,IAAI,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CACxE;AAED,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,0BAA0B,GAAG,oBAAoB,CAoEnG;AAED;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE;IAC1C,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;CACjC,GAAG,kBAAkB,CAQrB"}
1
+ {"version":3,"file":"samples.d.ts","sourceRoot":"","sources":["../../src/bakeoff/samples.ts"],"names":[],"mappings":"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"}
@@ -9,6 +9,15 @@
9
9
  * key; read server-side only; deny-anon RLS. NEVER the anon staging posture.
10
10
  *
11
11
  * Transport is raw PostgREST over fetch (same pattern as the exporter).
12
+ *
13
+ * ── SCRUB BEFORE PERSIST (S7, screening B0-a) ───────────────────────────────
14
+ * `bakeoff_samples` is the largest server-side customer-content path, so the
15
+ * task's scrub setting — the SAME `ScrubMode` the content-custody + capture-write
16
+ * paths honor — runs on `inputText` AND `outputText` BEFORE any row is built or
17
+ * POSTed (mirrors src/run/capture-write.ts). off/detect ⇒ verbatim (detect still
18
+ * COUNTS findings onto the row's `arm_meta.scrub`, never values); redact ⇒ masked
19
+ * copy; block ⇒ a sample with any finding becomes metadata-only (input/output
20
+ * dropped) and counted. Findings are `{type,count}` only — never a matched value.
12
21
  */
13
22
  Object.defineProperty(exports, "__esModule", { value: true });
14
23
  exports.OUTPUT_MAX_BYTES = void 0;
@@ -18,6 +27,8 @@ exports.retentionExpiry = retentionExpiry;
18
27
  exports.buildBakeoffSamples = buildBakeoffSamples;
19
28
  exports.createBakeoffSamplesClient = createBakeoffSamplesClient;
20
29
  exports.buildDirectSamplesSink = buildDirectSamplesSink;
30
+ exports.resolveSampleScrubMode = resolveSampleScrubMode;
31
+ const scrub_1 = require("../artifacts/scrub");
21
32
  /** 256KB per output, per the architect's caps; TOAST compresses at rest. */
22
33
  exports.OUTPUT_MAX_BYTES = 256 * 1024;
23
34
  /** Default persists incumbent + winner only; "all" persists every arm. */
@@ -44,18 +55,53 @@ function truncateOutput(text) {
44
55
  function retentionExpiry(nowMs, days = 90) {
45
56
  return new Date(nowMs + days * 24 * 3600 * 1000).toISOString();
46
57
  }
47
- function buildBakeoffSamples(bakeoffId, route, arms, expiresAt) {
58
+ /** Sum two finding lists by type (counts only), sorted by type — the scrub
59
+ * invariant: a finding is `{type, count}`, never a value. */
60
+ function mergeFindings(a, b) {
61
+ const counts = new Map();
62
+ for (const f of [...a, ...b])
63
+ counts.set(f.type, (counts.get(f.type) ?? 0) + f.count);
64
+ return [...counts.entries()]
65
+ .map(([type, count]) => ({ type, count }))
66
+ .sort((x, y) => (x.type < y.type ? -1 : x.type > y.type ? 1 : 0));
67
+ }
68
+ /** One sample's input + output run through scrub `mode` (S7). off ⇒ verbatim,
69
+ * no findings; detect ⇒ verbatim, findings counted; redact ⇒ masked; block ⇒ a
70
+ * sample with any finding is dropped to metadata-only (both texts null). Pure. */
71
+ function scrubSample(inputText, outputText, mode) {
72
+ if (mode === "off")
73
+ return { inputText, outputText, findings: [] };
74
+ const inScrub = (0, scrub_1.scrubContent)(inputText, mode);
75
+ const outScrub = outputText === null ? null : (0, scrub_1.scrubContent)(outputText, mode);
76
+ const findings = mergeFindings(inScrub.findings, outScrub?.findings ?? []);
77
+ // block: ANY finding on either side ⇒ the whole sample is metadata-only.
78
+ if (mode === "block" && (inScrub.blocked || (outScrub?.blocked ?? false))) {
79
+ return { inputText: null, outputText: null, findings };
80
+ }
81
+ return { inputText: inScrub.text, outputText: outScrub === null ? null : outScrub.text, findings };
82
+ }
83
+ /**
84
+ * Flatten arms×samples into rows, scrubbing input + output with the task's
85
+ * `mode` (S7, default `off` ⇒ verbatim, byte-identical). Scrub findings ride the
86
+ * row's `arm_meta.scrub` as counts-by-type (never values); a `block`-mode sample
87
+ * with any finding is stored metadata-only (input/output dropped).
88
+ */
89
+ function buildBakeoffSamples(bakeoffId, route, arms, expiresAt, mode = "off") {
48
90
  const rows = [];
49
91
  for (const arm of arms) {
50
92
  for (const s of arm.samples) {
93
+ const scrubbed = scrubSample(s.inputText, s.outputText, mode);
94
+ const armMeta = scrubbed.findings.length > 0
95
+ ? { ...(arm.armMeta ?? {}), scrub: { mode, findings: scrubbed.findings } }
96
+ : arm.armMeta;
51
97
  rows.push({
52
98
  bakeoffId,
53
99
  route,
54
100
  arm: arm.arm,
55
- armMeta: arm.armMeta,
101
+ armMeta,
56
102
  inputRef: s.inputRef,
57
- inputText: s.inputText,
58
- outputText: truncateOutput(s.outputText),
103
+ inputText: scrubbed.inputText,
104
+ outputText: truncateOutput(scrubbed.outputText),
59
105
  conform: s.conform,
60
106
  costUsd: s.costUsd,
61
107
  latencyMs: s.latencyMs,
@@ -104,7 +150,11 @@ function createBakeoffSamplesClient(config) {
104
150
  arm: s.arm,
105
151
  arm_meta: s.armMeta,
106
152
  input_ref: s.inputRef,
107
- input_text: s.inputText,
153
+ // `input_text` is NOT NULL (00008); a block-mode drop persists ""
154
+ // (metadata-only — zero content), never SQL null. `output_text` is
155
+ // nullable and rides as-is. (Spec §6/B0-a asks for null on both; the
156
+ // live column disallows it and this mission is ZERO-DDL — see PR body.)
157
+ input_text: s.inputText ?? "",
108
158
  output_text: s.outputText,
109
159
  conform: s.conform,
110
160
  cost_usd: s.costUsd,
@@ -139,10 +189,21 @@ function createBakeoffSamplesClient(config) {
139
189
  */
140
190
  function buildDirectSamplesSink(cfg) {
141
191
  return async ({ bakeoffId, route, arms }) => {
142
- const rows = buildBakeoffSamples(bakeoffId, route, arms, retentionExpiry(Date.now()));
192
+ const mode = resolveSampleScrubMode(cfg.resolver ?? null, route, cfg.defaultScrub ?? "off");
193
+ const rows = buildBakeoffSamples(bakeoffId, route, arms, retentionExpiry(Date.now()), mode);
143
194
  await createBakeoffSamplesClient({
144
195
  supabaseUrl: cfg.supabaseUrl,
145
196
  serviceRoleKey: cfg.serviceRoleKey,
146
197
  }).insert(rows);
147
198
  };
148
199
  }
200
+ /**
201
+ * The effective scrub mode for a bake-off sample write — resolved EXACTLY as
202
+ * capture-write.ts does: a configured control plane WINS (`resolver.get(route)`),
203
+ * else the rig's config default. A local-only / direct-mode rig has no resolver,
204
+ * so it falls to `configDefault`. Pure and synchronous (the resolver read is
205
+ * cache-only, never a network hop).
206
+ */
207
+ function resolveSampleScrubMode(resolver, route, configDefault) {
208
+ return resolver !== null ? resolver.get(route).scrub : configDefault;
209
+ }
@@ -7,7 +7,23 @@
7
7
  * (base + retries + repair + search, F1-corrected) ÷ conformant count × 1000.
8
8
  */
9
9
  import type { ReplaySampleResult } from "./replay";
10
- import type { VariantMetrics } from "./types";
10
+ import type { RegretStats, VariantMetrics } from "./types";
11
+ /** Seeded so a bake-off's regret CI is reproducible given the same sample set
12
+ * (spec §4.2 — "seeded for determinism"). */
13
+ export declare const REGRET_BOOTSTRAP_REPS = 2000;
14
+ export declare const REGRET_BOOTSTRAP_SEED = 20260823;
15
+ /** Per-sample quality (spec §2): value accuracy when a golden/deterministic
16
+ * check/judge scored it, else 1/0 conformance. Always defined — so paired regret
17
+ * is always computable. */
18
+ export declare function sampleQuality(s: ReplaySampleResult): number;
11
19
  export declare function wilson95(successes: number, n: number): readonly [number, number];
12
20
  export declare function aggregateVariantMetrics(variant: string, samples: readonly ReplaySampleResult[]): VariantMetrics;
21
+ /**
22
+ * Paired regret of an arm vs the reference (spec §4.2). Pairs samples by
23
+ * captureId, takes per-sample quality differences `q(reference) − q(arm)`, and
24
+ * reports the mean, a seeded paired-bootstrap 95% CI, and n_paired. Samples
25
+ * present on only one side are excluded (and, by omission, counted: n_paired vs
26
+ * each arm's own sample count). Deterministic given the sample set + seed.
27
+ */
28
+ export declare function pairedRegret(armSamples: readonly ReplaySampleResult[], refSamples: readonly ReplaySampleResult[], seed?: number, reps?: number): RegretStats;
13
29
  //# sourceMappingURL=stats.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"stats.d.ts","sourceRoot":"","sources":["../../src/bakeoff/stats.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AACnD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE9C,wBAAgB,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAShF;AAMD,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,SAAS,kBAAkB,EAAE,GACrC,cAAc,CAuBhB"}
1
+ {"version":3,"file":"stats.d.ts","sourceRoot":"","sources":["../../src/bakeoff/stats.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AACnD,OAAO,KAAK,EAAiB,WAAW,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE1E;6CAC6C;AAC7C,eAAO,MAAM,qBAAqB,OAAO,CAAC;AAC1C,eAAO,MAAM,qBAAqB,WAAW,CAAC;AAE9C;;2BAE2B;AAC3B,wBAAgB,aAAa,CAAC,CAAC,EAAE,kBAAkB,GAAG,MAAM,CAE3D;AAED,wBAAgB,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAShF;AAMD,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,SAAS,kBAAkB,EAAE,GACrC,cAAc,CA0ChB;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAC1B,UAAU,EAAE,SAAS,kBAAkB,EAAE,EACzC,UAAU,EAAE,SAAS,kBAAkB,EAAE,EACzC,IAAI,GAAE,MAA8B,EACpC,IAAI,GAAE,MAA8B,GACnC,WAAW,CAwBb"}
@@ -8,8 +8,22 @@
8
8
  * (base + retries + repair + search, F1-corrected) ÷ conformant count × 1000.
9
9
  */
10
10
  Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.REGRET_BOOTSTRAP_SEED = exports.REGRET_BOOTSTRAP_REPS = void 0;
12
+ exports.sampleQuality = sampleQuality;
11
13
  exports.wilson95 = wilson95;
12
14
  exports.aggregateVariantMetrics = aggregateVariantMetrics;
15
+ exports.pairedRegret = pairedRegret;
16
+ const bootstrap_1 = require("../rigindex/bootstrap");
17
+ /** Seeded so a bake-off's regret CI is reproducible given the same sample set
18
+ * (spec §4.2 — "seeded for determinism"). */
19
+ exports.REGRET_BOOTSTRAP_REPS = 2000;
20
+ exports.REGRET_BOOTSTRAP_SEED = 20260823;
21
+ /** Per-sample quality (spec §2): value accuracy when a golden/deterministic
22
+ * check/judge scored it, else 1/0 conformance. Always defined — so paired regret
23
+ * is always computable. */
24
+ function sampleQuality(s) {
25
+ return s.valueAccuracy ?? (s.conform ? 1 : 0);
26
+ }
13
27
  function wilson95(successes, n) {
14
28
  if (n === 0)
15
29
  return [0, 1];
@@ -32,6 +46,16 @@ function aggregateVariantMetrics(variant, samples) {
32
46
  .map((s) => s.valueAccuracy)
33
47
  .filter((a) => a !== null);
34
48
  const totalCostUsd = samples.reduce((a, s) => a + s.costUsd, 0);
49
+ // RB-1: the metric is value_accuracy iff at least one sample was scored on
50
+ // value (eval case / value check / judge / Q1 gate); otherwise the arm's
51
+ // quality is 1/0 conformance. qualityMean averages per-sample quality either way.
52
+ const qualityMetric = accuracies.length > 0 ? "value_accuracy" : "conformance";
53
+ // Q1 quality pass rate (spec §4.2): a variant is "gated" once ANY sample
54
+ // carried a boolean verdict; a failed (unscored) sample counts against the
55
+ // pass rate as a non-pass, exactly as a conformance miss counts against
56
+ // conformance. null when no gate ran, so computeProposal can tell.
57
+ const gated = samples.some((s) => s.qualityPassed !== null && s.qualityPassed !== undefined);
58
+ const passing = samples.filter((s) => s.qualityPassed === true).length;
35
59
  return {
36
60
  variant,
37
61
  samples: n,
@@ -43,6 +67,49 @@ function aggregateVariantMetrics(variant, samples) {
43
67
  meanLatencyMs: mean(samples.map((s) => s.latencyMs)),
44
68
  meanCostUsd: n === 0 ? 0 : totalCostUsd / n,
45
69
  effectiveUsdPer1kConformant: conformant.length === 0 ? Number.POSITIVE_INFINITY : (totalCostUsd / conformant.length) * 1000,
70
+ qualityPassCi95: gated ? wilson95(passing, n) : null,
71
+ effectiveUsdPer1kPassing: !gated
72
+ ? null
73
+ : passing === 0
74
+ ? Number.POSITIVE_INFINITY
75
+ : (totalCostUsd / passing) * 1000,
46
76
  pricingMissing: samples.some((s) => s.pricingMissing),
77
+ qualityMean: n === 0 ? null : mean(samples.map(sampleQuality)),
78
+ qualityMetric,
47
79
  };
48
80
  }
81
+ /**
82
+ * Paired regret of an arm vs the reference (spec §4.2). Pairs samples by
83
+ * captureId, takes per-sample quality differences `q(reference) − q(arm)`, and
84
+ * reports the mean, a seeded paired-bootstrap 95% CI, and n_paired. Samples
85
+ * present on only one side are excluded (and, by omission, counted: n_paired vs
86
+ * each arm's own sample count). Deterministic given the sample set + seed.
87
+ */
88
+ function pairedRegret(armSamples, refSamples, seed = exports.REGRET_BOOTSTRAP_SEED, reps = exports.REGRET_BOOTSTRAP_REPS) {
89
+ const refByCapture = new Map(refSamples.map((s) => [s.captureId, s]));
90
+ const diffs = [];
91
+ for (const arm of armSamples) {
92
+ const ref = refByCapture.get(arm.captureId);
93
+ if (ref === undefined)
94
+ continue;
95
+ diffs.push(sampleQuality(ref) - sampleQuality(arm));
96
+ }
97
+ const nPaired = diffs.length;
98
+ if (nPaired === 0)
99
+ return { regretMean: 0, regretCi95: [0, 0], nPaired: 0 };
100
+ const regretMean = mean(diffs);
101
+ // Paired bootstrap: resample the DIFFERENCES with replacement (the pairing is
102
+ // already fixed), take each resample's mean, then the 2.5/97.5 percentiles.
103
+ const rng = (0, bootstrap_1.mulberry32)(seed);
104
+ const means = [];
105
+ for (let b = 0; b < reps; b += 1) {
106
+ let sum = 0;
107
+ for (let i = 0; i < nPaired; i += 1)
108
+ sum += diffs[Math.floor(rng() * nPaired)];
109
+ means.push(sum / nPaired);
110
+ }
111
+ means.sort((a, b) => a - b);
112
+ const lo = means[Math.floor(0.025 * reps)];
113
+ const hi = means[Math.min(reps - 1, Math.floor(0.975 * reps))];
114
+ return { regretMean, regretCi95: [lo, hi], nPaired };
115
+ }
@@ -1,8 +1,22 @@
1
1
  /**
2
- * Conformance bake-off contracts — Phase 3 spec WS5 (closed core).
3
- * The headline metric: effective $ per 1K conformant outputs, with repair
4
- * and search costs included (F1-corrected accounting).
2
+ * Conformance bake-off contracts — Phase 3 spec WS5 (closed core), extended for
3
+ * RB-1 regret-bounded objectives (near-optimal spec §4). The headline metric is
4
+ * effective $ per 1K conformant outputs (F1-corrected); RB-1 adds per-sample
5
+ * value accuracy, paired regret vs a reference arm, and an objective-aware gate.
5
6
  */
7
+ import type { ObjectiveMode } from "../types/route";
8
+ /** Which quality metric an arm's numbers are on (spec §2): value accuracy when a
9
+ * golden / value check / judge scored it, else schema conformance. */
10
+ export type QualityMetric = "value_accuracy" | "conformance";
11
+ /** An arm's quality gap to the reference on the SAME paired samples (spec §2).
12
+ * 0 = matches the reference; negative = beats it. CI is a seeded paired
13
+ * bootstrap of the per-sample differences. */
14
+ export interface RegretStats {
15
+ readonly regretMean: number;
16
+ readonly regretCi95: readonly [number, number];
17
+ /** Samples present on BOTH the arm and the reference; the CI's n. */
18
+ readonly nPaired: number;
19
+ }
6
20
  export interface VariantMetrics {
7
21
  readonly variant: string;
8
22
  readonly samples: number;
@@ -16,9 +30,58 @@ export interface VariantMetrics {
16
30
  readonly meanCostUsd: number;
17
31
  /** (base + repair + search costs) / conformant outputs × 1000 — the number. */
18
32
  readonly effectiveUsdPer1kConformant: number;
33
+ /** Q1 quality gate (spec §4.2): Wilson 95% CI on the quality PASS rate
34
+ * (samples the gate accepted / all samples). null when no gate ran on this
35
+ * variant — computeProposal's third clause only fires when both arms have one. */
36
+ readonly qualityPassCi95: readonly [number, number] | null;
37
+ /** Q1: (base + repair + search costs) / quality-PASSING outputs × 1000 —
38
+ * reported BESIDE effectiveUsdPer1kConformant (never redefining it; flag F6).
39
+ * ∞ when a gate ran but nothing passed; null when no gate ran. */
40
+ readonly effectiveUsdPer1kPassing: number | null;
19
41
  /** Addendum A1: any sample served by a model with no pricing entry —
20
42
  * cost columns under-count; this variant is never ranked or proposed. */
21
43
  readonly pricingMissing: boolean;
44
+ /** RB-1: mean per-sample quality (value accuracy where present, else 1/0
45
+ * conformance). The number regret is computed from. Null when no samples. */
46
+ readonly qualityMean: number | null;
47
+ /** RB-1: which metric qualityMean is on, named on the report. */
48
+ readonly qualityMetric: QualityMetric;
49
+ /** RB-1: this arm is the reference (ceiling) arm. */
50
+ readonly isReference?: boolean;
51
+ /** RB-1 (architect B-3): the objective gate proposed THIS arm — stamped on the
52
+ * winning variant so the mirror/console read the artifact's decision rather
53
+ * than recomputing a (mode-blind) winner client-side. */
54
+ readonly proposed?: boolean;
55
+ /** RB-1: this arm's regret vs the reference; null on the reference arm and
56
+ * when there is no reference. */
57
+ readonly regret?: RegretStats | null;
58
+ /** RB-1: the reference arm hit its envelope — regret is on the paired subset. */
59
+ readonly referencePartial?: boolean;
60
+ }
61
+ /** RB-1: the resolved objective recorded on the artifact (evidence + report). */
62
+ export interface BakeoffObjective {
63
+ readonly mode: ObjectiveMode;
64
+ readonly maxRegret: number | null;
65
+ readonly priceCeiling: {
66
+ readonly usdPer1k?: number;
67
+ readonly usdPerRun?: number;
68
+ } | null;
69
+ readonly lambda: number | null;
70
+ }
71
+ /** RB-1: the resolved reference arm recorded on the artifact. */
72
+ export interface BakeoffReference {
73
+ /** The arm name carrying the reference metrics. */
74
+ readonly variant: string;
75
+ /** "provider/model". */
76
+ readonly model: string;
77
+ /** Resolved via `reference: auto` (vs an explicit declaration). */
78
+ readonly auto: boolean;
79
+ /** The `auto` rule + source, when auto. */
80
+ readonly rule: string | null;
81
+ /** Out-of-candidate reference (reference_allow_external). */
82
+ readonly external: boolean;
83
+ /** The reference replay hit its envelope (partial paired set). */
84
+ readonly partial: boolean;
22
85
  }
23
86
  export interface BakeoffArtifact {
24
87
  readonly id: string;
@@ -27,11 +90,19 @@ export interface BakeoffArtifact {
27
90
  readonly ranAt: string;
28
91
  readonly replayN: number;
29
92
  readonly variants: readonly VariantMetrics[];
30
- /** Winner by effectiveUsdPer1kConformant among variants meeting the gates;
31
- * null when no variant beats the incumbent's gates. */
93
+ /** Winner among variants meeting the objective's gates; null when nobody beats
94
+ * the incumbent. `vsIncumbentSavingsPct` is the effective-cost saving (kept for
95
+ * every mode); `regret` is the proposed arm's regret vs the reference. */
32
96
  readonly proposal: {
33
97
  readonly variant: string;
34
98
  readonly vsIncumbentSavingsPct: number;
99
+ readonly regret?: RegretStats | null;
35
100
  } | null;
101
+ /** RB-1: the objective the gate ran under (default quality-first). */
102
+ readonly objective?: BakeoffObjective;
103
+ /** RB-1: the reference arm, or null when no reference was declared/resolved. */
104
+ readonly reference?: BakeoffReference | null;
105
+ /** RB-1: the metric the bake-off's quality numbers are on. */
106
+ readonly qualityMetric?: QualityMetric;
36
107
  }
37
108
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/bakeoff/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,WAAW,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChD,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IACnC,wFAAwF;IACxF,QAAQ,CAAC,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1C,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,+EAA+E;IAC/E,QAAQ,CAAC,2BAA2B,EAAE,MAAM,CAAC;IAC7C;6EACyE;IACzE,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;CAClC;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,SAAS,cAAc,EAAE,CAAC;IAC7C;2DACuD;IACvD,QAAQ,CAAC,QAAQ,EAAE;QAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,qBAAqB,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;CAChG"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/bakeoff/types.ts"],"names":[],"mappings":"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"}
@@ -1,8 +1,9 @@
1
1
  "use strict";
2
2
  /**
3
- * Conformance bake-off contracts — Phase 3 spec WS5 (closed core).
4
- * The headline metric: effective $ per 1K conformant outputs, with repair
5
- * and search costs included (F1-corrected accounting).
3
+ * Conformance bake-off contracts — Phase 3 spec WS5 (closed core), extended for
4
+ * RB-1 regret-bounded objectives (near-optimal spec §4). The headline metric is
5
+ * effective $ per 1K conformant outputs (F1-corrected); RB-1 adds per-sample
6
+ * value accuracy, paired regret vs a reference arm, and an objective-aware gate.
6
7
  */
7
8
  Object.defineProperty(exports, "__esModule", { value: true });
8
9
  // Implemented: engine.ts assembles this from replay results; stats.ts owns
@@ -0,0 +1,14 @@
1
+ /**
2
+ * `modelrig query` — WI-1b. Runs a telemetry-grammar query against the analytics
3
+ * surface of the configured server (`POST /v1/analytics/query`), authed with a
4
+ * `rig_sk_` key. Goes through the API boundary (T12: the CLI never holds a DB
5
+ * client); the server scopes to the key's org and folds the grammar.
6
+ *
7
+ * modelrig query "SELECT subject, sum(cost) FROM inferences GROUP BY subject"
8
+ * modelrig query --saved cost-per-subject
9
+ *
10
+ * Kept in its own file so cli.ts stays under the LOC cap (like validate-cli.ts).
11
+ */
12
+ import type { Flags } from "./cli";
13
+ export declare function cmdQuery(flags: Flags): Promise<number>;
14
+ //# sourceMappingURL=cli-query.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-query.d.ts","sourceRoot":"","sources":["../src/cli-query.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AAMnC,wBAAsB,QAAQ,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,CAyC5D"}
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ /**
3
+ * `modelrig query` — WI-1b. Runs a telemetry-grammar query against the analytics
4
+ * surface of the configured server (`POST /v1/analytics/query`), authed with a
5
+ * `rig_sk_` key. Goes through the API boundary (T12: the CLI never holds a DB
6
+ * client); the server scopes to the key's org and folds the grammar.
7
+ *
8
+ * modelrig query "SELECT subject, sum(cost) FROM inferences GROUP BY subject"
9
+ * modelrig query --saved cost-per-subject
10
+ *
11
+ * Kept in its own file so cli.ts stays under the LOC cap (like validate-cli.ts).
12
+ */
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.cmdQuery = cmdQuery;
15
+ const config_1 = require("./config");
16
+ const grammar_1 = require("./query/grammar");
17
+ function savedCatalogue() {
18
+ return grammar_1.SAVED_QUERIES.map((q) => ` ${q.id.padEnd(20)} ${q.title}`).join("\n");
19
+ }
20
+ async function cmdQuery(flags) {
21
+ const saved = flags.named.get("saved");
22
+ const grammar = flags.positional[0];
23
+ // Config is the only env reader (package-invariants). The analytics endpoint
24
+ // shares the api-mode ingest server, so base = that URL's origin, key = the api key.
25
+ const exp = (0, config_1.loadConfigFromEnv)().export;
26
+ const api = exp !== undefined && exp.mode === "api" ? exp : undefined;
27
+ const base = api !== undefined ? new URL(api.ingestUrl).origin : "";
28
+ const key = api?.apiKey ?? "";
29
+ if ((saved === undefined && (grammar === undefined || grammar === "")) || base === "" || key === "") {
30
+ console.error([
31
+ 'usage: modelrig query "<grammar>" | modelrig query --saved <id>',
32
+ "",
33
+ "Runs a telemetry query against your org's analytics surface (metadata only —",
34
+ "enums, numbers, hashes, identifiers; never content). Needs api-mode telemetry:",
35
+ " MODELRIG_API_KEY a rig_sk_ key with the `query` scope",
36
+ " MODELRIG_INGEST_URL optional — the server base; defaults to the hosted API",
37
+ "",
38
+ "Saved questions:",
39
+ savedCatalogue(),
40
+ ].join("\n"));
41
+ return 1;
42
+ }
43
+ const body = saved !== undefined ? { saved } : { query: grammar };
44
+ const res = await fetch(`${base}/v1/analytics/query`, {
45
+ method: "POST",
46
+ headers: { authorization: `Bearer ${key}`, "content-type": "application/json" },
47
+ body: JSON.stringify(body),
48
+ });
49
+ const payload = (await res.json().catch(() => ({})));
50
+ if (!res.ok) {
51
+ console.error(`query ${res.status}: ${typeof payload.error === "string" ? payload.error : "failed"}`);
52
+ return 1;
53
+ }
54
+ console.log(JSON.stringify(payload, null, 2));
55
+ if (payload.truncated === true) {
56
+ console.error("note: result truncated (LIMIT or the 10k-row scan cap) — narrow the window or raise LIMIT");
57
+ }
58
+ return 0;
59
+ }
package/dist/cli.d.ts CHANGED
@@ -2,7 +2,8 @@
2
2
  /**
3
3
  * modelrig CLI — Phase 3 plan §3 + Phase 4 plan §3:
4
4
  * modelrig bakeoff --route X --variants a,b,c --replay-last N
5
- * [--budget-usd usd] [--json] [--no-mirror] (--envelope is a kept alias)
5
+ * [--budget-usd usd] [--objective quality-first|balanced|cost-first]
6
+ * [--json] [--no-mirror] (--envelope is a kept alias)
6
7
  * modelrig bakeoff --from-proposal <id> [--replay-last N] [--json]
7
8
  * modelrig watch run [--events fixtures.json] [--json]
8
9
  *
package/dist/cli.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;GAeG;AAkCH,MAAM,WAAW,KAAK;IACpB,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IACvC,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5C,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;CACpC;AAeD,wBAAgB,SAAS,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,KAAK,CAuBxD;AA0gBD,wBAAsB,IAAI,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAqCnE"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;GAgBG;AAgDH,MAAM,WAAW,KAAK;IACpB,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IACvC,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5C,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;CACpC;AAeD,wBAAgB,SAAS,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,KAAK,CAuBxD;AAqhBD,wBAAsB,IAAI,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAuCnE"}
package/dist/cli.js CHANGED
@@ -3,7 +3,8 @@
3
3
  /**
4
4
  * modelrig CLI — Phase 3 plan §3 + Phase 4 plan §3:
5
5
  * modelrig bakeoff --route X --variants a,b,c --replay-last N
6
- * [--budget-usd usd] [--json] [--no-mirror] (--envelope is a kept alias)
6
+ * [--budget-usd usd] [--objective quality-first|balanced|cost-first]
7
+ * [--json] [--no-mirror] (--envelope is a kept alias)
7
8
  * modelrig bakeoff --from-proposal <id> [--replay-last N] [--json]
8
9
  * modelrig watch run [--events fixtures.json] [--json]
9
10
  *
@@ -33,6 +34,17 @@ const status_1 = require("./status");
33
34
  const engine_1 = require("./bakeoff/engine");
34
35
  const samples_1 = require("./bakeoff/samples");
35
36
  const report_1 = require("./bakeoff/report");
37
+ const OBJECTIVE_MODES = new Set(["quality-first", "balanced", "cost-first"]);
38
+ /** Parse & validate `--objective <mode>`; undefined when absent. */
39
+ function objectiveOf(flags) {
40
+ const raw = flags.named.get("objective");
41
+ if (raw === undefined)
42
+ return undefined;
43
+ if (!OBJECTIVE_MODES.has(raw)) {
44
+ throw new Error(`--objective must be one of [${[...OBJECTIVE_MODES].join(", ")}]`);
45
+ }
46
+ return raw;
47
+ }
36
48
  const cli_run_1 = require("./verify/cli-run");
37
49
  const loader_1 = require("./routes/loader");
38
50
  const rawonly_1 = require("./run/rawonly");
@@ -45,6 +57,7 @@ const state_1 = require("./watch/state");
45
57
  const state_store_1 = require("./watch/state-store");
46
58
  const swaps_client_1 = require("./savings/swaps-client");
47
59
  const swap_execute_1 = require("./savings/swap-execute");
60
+ const cli_query_1 = require("./cli-query");
48
61
  const BARE_FLAGS = new Set([
49
62
  "json",
50
63
  "no-mirror",
@@ -99,7 +112,7 @@ function keepOutputsMode(flags) {
99
112
  return "winner";
100
113
  return undefined;
101
114
  }
102
- async function runAndReportBakeoff(config, flags, route, variants, replayLastN, envelope) {
115
+ async function runAndReportBakeoff(config, flags, route, variants, replayLastN, envelope, objectiveMode) {
103
116
  const keep = keepOutputsMode(flags);
104
117
  if (keep !== undefined)
105
118
  (0, config_1.directExportOf)(config, "--keep-outputs (bakeoff_samples lives there)");
@@ -111,6 +124,7 @@ async function runAndReportBakeoff(config, flags, route, variants, replayLastN,
111
124
  replayLastN,
112
125
  ...(envelope !== undefined ? { envelopeUsdPerVariant: envelope } : {}),
113
126
  ...(keep !== undefined ? { keepOutputs: keep } : {}),
127
+ ...(objectiveMode !== undefined ? { objectiveMode } : {}),
114
128
  });
115
129
  const outDir = (0, node_path_1.join)((0, node_path_1.dirname)(config.telemetryDbPath), "bakeoffs");
116
130
  (0, node_fs_1.mkdirSync)(outDir, { recursive: true });
@@ -159,7 +173,27 @@ async function cmdBakeoff(flags) {
159
173
  }
160
174
  const routes = (0, loader_1.loadRoutes)((0, rawonly_1.requireRoutesDir)(config, "modelrig bakeoff"));
161
175
  const bundle = routes.get(proposal.route);
162
- const variants = ["default", ...bundle.variants.map((v) => v.name)];
176
+ // Declare-first (routing-console spec §4.4, R3): a manual_challenger proposal
177
+ // names ONE trial variant — bake off default vs that variant only. The trial
178
+ // variant must be DECLARED in the local routes (the human merged the
179
+ // declare-YAML); until then it isn't loadable, so fail with a teaching error
180
+ // rather than a bake-off that silently omits the challenger.
181
+ let variants;
182
+ const trigger = proposal.trigger;
183
+ if (trigger.type === "manual_challenger") {
184
+ const trialVariant = trigger.variant;
185
+ if (typeof trialVariant !== "string" || trialVariant === "") {
186
+ throw new Error(`proposal ${proposalId} is a manual_challenger with no trial variant name`);
187
+ }
188
+ if (!bundle.variants.some((v) => v.name === trialVariant)) {
189
+ throw new Error(`route "${proposal.route}" does not declare the trial variant "${trialVariant}" yet — ` +
190
+ "merge the declare-YAML from the proposal first (modelrig ... declare-body), then re-run");
191
+ }
192
+ variants = ["default", trialVariant];
193
+ }
194
+ else {
195
+ variants = ["default", ...bundle.variants.map((v) => v.name)];
196
+ }
163
197
  const lastRaw = flags.named.get("replay-last");
164
198
  const replayLastN = lastRaw !== undefined ? Number(lastRaw) : 25;
165
199
  if (!Number.isInteger(replayLastN) || replayLastN < 1) {
@@ -174,26 +208,12 @@ async function cmdBakeoff(flags) {
174
208
  const perVariant = reservation.remainingUsd / variants.length;
175
209
  console.log(`watch budget ${reservation.key}: $${reservation.remainingUsd.toFixed(2)} remaining → ` +
176
210
  `$${perVariant.toFixed(2)}/variant cap`);
177
- const artifact = await runAndReportBakeoff(config, flags, proposal.route, variants, replayLastN, perVariant);
211
+ const artifact = await runAndReportBakeoff(config, flags, proposal.route, variants, replayLastN, perVariant, objectiveOf(flags));
178
212
  const spentUsd = artifact.variants.reduce((a, m) => a + m.samples * m.meanCostUsd, 0);
179
213
  budgetStore.accrue(reservation.key, spentUsd);
180
- await client.attachEvidence(proposal.id, {
181
- bakeoff_id: artifact.id,
182
- replay_n: artifact.replayN,
183
- ran_at: artifact.ranAt,
184
- spent_usd: spentUsd,
185
- proposal: artifact.proposal,
186
- matrix: artifact.variants.map((m) => ({
187
- variant: m.variant,
188
- samples: m.samples,
189
- conform_rate: m.conformRate,
190
- conform_ci95: m.conformCi95,
191
- effective_usd_per_1k_conformant: Number.isFinite(m.effectiveUsdPer1kConformant)
192
- ? m.effectiveUsdPer1kConformant
193
- : null,
194
- pricing_missing: m.pricingMissing,
195
- })),
196
- });
214
+ // Evidence jsonb (winner + RB-1 objective/reference/regret/quality) is
215
+ // built by the engine so the CLI stays thin; the PR body reads it back.
216
+ await client.attachEvidence(proposal.id, (0, engine_1.bakeoffEvidence)(artifact, spentUsd));
197
217
  console.log(`evidence attached → proposal ${proposal.id} is now evidenced ` +
198
218
  `($${spentUsd.toFixed(2)} accrued to ${reservation.key}). ` +
199
219
  "Approval stays human: modelrig swap execute <proposalId> --approve");
@@ -214,7 +234,7 @@ async function cmdBakeoff(flags) {
214
234
  const env = envRaw !== undefined ? Number(envRaw) : undefined;
215
235
  if (env !== undefined && (!Number.isFinite(env) || env <= 0))
216
236
  throw new Error("--budget-usd must be positive");
217
- return (0, report_1.runEvalCaseBakeoffCli)(config, evalTask, vs, env, keepOutputsMode(flags), flags.bare.has("json"));
237
+ return (0, report_1.runEvalCaseBakeoffCli)(config, evalTask, vs, env, keepOutputsMode(flags), flags.bare.has("json"), objectiveOf(flags));
218
238
  }
219
239
  const route = flags.named.get("route");
220
240
  const variantsRaw = flags.named.get("variants");
@@ -233,7 +253,7 @@ async function cmdBakeoff(flags) {
233
253
  if (envelope !== undefined && (!Number.isFinite(envelope) || envelope <= 0)) {
234
254
  throw new Error("--budget-usd must be a positive number of dollars (per replay variant)");
235
255
  }
236
- await runAndReportBakeoff(config, flags, route, variants, replayLastN, envelope);
256
+ await runAndReportBakeoff(config, flags, route, variants, replayLastN, envelope, objectiveOf(flags));
237
257
  return 0;
238
258
  }
239
259
  const DEFAULT_WATCH_THRESHOLD_PCT = 10;
@@ -522,13 +542,14 @@ const USAGE = [
522
542
  " modelrig validate (structurally check rig.yaml + every route bundle — no provider key needed)",
523
543
  " modelrig status (print export/artifact/otlp posture)",
524
544
  " modelrig serve (needs the modelrig-server package)",
525
- " modelrig bakeoff --route <route> --variants a,b,c --replay-last N [--budget-usd usd] [--keep-outputs[=all]] [--json] [--no-mirror]",
545
+ " modelrig bakeoff --route <route> --variants a,b,c --replay-last N [--budget-usd usd] [--objective quality-first|balanced|cost-first] [--keep-outputs[=all]] [--json] [--no-mirror]",
526
546
  " modelrig bakeoff --from-proposal <id> [--replay-last N] [--keep-outputs[=all]] [--json]",
527
547
  " modelrig watch run [--events fixtures.json] [--json]",
528
548
  " modelrig verify-swap --route <route> --captures <fixture.json> [--budget-usd usd] [--out report.md] [--json]",
529
549
  " modelrig swap execute <proposalId> --approve [--approved-by name]",
530
550
  " modelrig swap revert <swapId>",
531
551
  " modelrig judge <runId> (E5: score a run with the bounded model-judge; explicit + metered)",
552
+ ' modelrig query "<grammar>" | query --saved <id> (WI-1b: telemetry analytics — metadata only, never content)',
532
553
  " modelrig samples purge [--bakeoff <id>]",
533
554
  ].join("\n");
534
555
  async function main(argv) {
@@ -562,6 +583,8 @@ async function main(argv) {
562
583
  return cmdVerifySwap(flags);
563
584
  case "judge":
564
585
  return cmdJudge(flags);
586
+ case "query":
587
+ return (0, cli_query_1.cmdQuery)(flags);
565
588
  case "serve":
566
589
  return cmdServe();
567
590
  default: