modelrig 0.0.1 → 0.2.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 (420) hide show
  1. package/CHANGELOG.md +94 -0
  2. package/LICENSE +201 -0
  3. package/README.md +145 -8
  4. package/data/PROVENANCE.md +19 -0
  5. package/data/pricing-manual.json +22 -0
  6. package/data/pricing-map.json +46362 -0
  7. package/data/pricing-overlay.json +419 -0
  8. package/dist/adapters/anthropic.d.ts +90 -0
  9. package/dist/adapters/anthropic.d.ts.map +1 -0
  10. package/dist/adapters/anthropic.js +296 -0
  11. package/dist/adapters/build-adapters.d.ts +11 -0
  12. package/dist/adapters/build-adapters.d.ts.map +1 -0
  13. package/dist/adapters/build-adapters.js +45 -0
  14. package/dist/adapters/deepseek.d.ts +47 -0
  15. package/dist/adapters/deepseek.d.ts.map +1 -0
  16. package/dist/adapters/deepseek.js +216 -0
  17. package/dist/adapters/gemini.d.ts +44 -0
  18. package/dist/adapters/gemini.d.ts.map +1 -0
  19. package/dist/adapters/gemini.js +262 -0
  20. package/dist/adapters/grok.d.ts +53 -0
  21. package/dist/adapters/grok.d.ts.map +1 -0
  22. package/dist/adapters/grok.js +232 -0
  23. package/dist/adapters/openai-compat.d.ts +80 -0
  24. package/dist/adapters/openai-compat.d.ts.map +1 -0
  25. package/dist/adapters/openai-compat.js +244 -0
  26. package/dist/adapters/openai-stream.d.ts +58 -0
  27. package/dist/adapters/openai-stream.d.ts.map +1 -0
  28. package/dist/adapters/openai-stream.js +120 -0
  29. package/dist/adapters/openai.d.ts +55 -0
  30. package/dist/adapters/openai.d.ts.map +1 -0
  31. package/dist/adapters/openai.js +281 -0
  32. package/dist/adapters/sampling.d.ts +28 -0
  33. package/dist/adapters/sampling.d.ts.map +1 -0
  34. package/dist/adapters/sampling.js +34 -0
  35. package/dist/adapters/tool-mapping.d.ts +92 -0
  36. package/dist/adapters/tool-mapping.d.ts.map +1 -0
  37. package/dist/adapters/tool-mapping.js +143 -0
  38. package/dist/artifacts/artifact.d.ts +137 -0
  39. package/dist/artifacts/artifact.d.ts.map +1 -0
  40. package/dist/artifacts/artifact.js +377 -0
  41. package/dist/artifacts/content-uploader.d.ts +87 -0
  42. package/dist/artifacts/content-uploader.d.ts.map +1 -0
  43. package/dist/artifacts/content-uploader.js +124 -0
  44. package/dist/artifacts/index.d.ts +63 -0
  45. package/dist/artifacts/index.d.ts.map +1 -0
  46. package/dist/artifacts/index.js +80 -0
  47. package/dist/artifacts/run-context.d.ts +129 -0
  48. package/dist/artifacts/run-context.d.ts.map +1 -0
  49. package/dist/artifacts/run-context.js +208 -0
  50. package/dist/artifacts/scrub.d.ts +60 -0
  51. package/dist/artifacts/scrub.d.ts.map +1 -0
  52. package/dist/artifacts/scrub.js +139 -0
  53. package/dist/artifacts/step.d.ts +26 -0
  54. package/dist/artifacts/step.d.ts.map +1 -0
  55. package/dist/artifacts/step.js +37 -0
  56. package/dist/artifacts/taint.d.ts +63 -0
  57. package/dist/artifacts/taint.d.ts.map +1 -0
  58. package/dist/artifacts/taint.js +76 -0
  59. package/dist/bakeoff/cache-sim.d.ts +99 -0
  60. package/dist/bakeoff/cache-sim.d.ts.map +1 -0
  61. package/dist/bakeoff/cache-sim.js +126 -0
  62. package/dist/bakeoff/engine.d.ts +52 -0
  63. package/dist/bakeoff/engine.d.ts.map +1 -0
  64. package/dist/bakeoff/engine.js +163 -0
  65. package/dist/bakeoff/eval-cases.d.ts +96 -0
  66. package/dist/bakeoff/eval-cases.d.ts.map +1 -0
  67. package/dist/bakeoff/eval-cases.js +105 -0
  68. package/dist/bakeoff/replay.d.ts +52 -0
  69. package/dist/bakeoff/replay.d.ts.map +1 -0
  70. package/dist/bakeoff/replay.js +94 -0
  71. package/dist/bakeoff/report.d.ts +41 -0
  72. package/dist/bakeoff/report.d.ts.map +1 -0
  73. package/dist/bakeoff/report.js +142 -0
  74. package/dist/bakeoff/samples.d.ts +77 -0
  75. package/dist/bakeoff/samples.d.ts.map +1 -0
  76. package/dist/bakeoff/samples.js +148 -0
  77. package/dist/bakeoff/stats.d.ts +13 -0
  78. package/dist/bakeoff/stats.d.ts.map +1 -0
  79. package/dist/bakeoff/stats.js +48 -0
  80. package/dist/bakeoff/types.d.ts +37 -0
  81. package/dist/bakeoff/types.d.ts.map +1 -0
  82. package/dist/bakeoff/types.js +10 -0
  83. package/dist/cli.d.ts +25 -0
  84. package/dist/cli.d.ts.map +1 -0
  85. package/dist/cli.js +590 -0
  86. package/dist/config.d.ts +204 -0
  87. package/dist/config.d.ts.map +1 -0
  88. package/dist/config.js +252 -0
  89. package/dist/deprecations.d.ts +32 -0
  90. package/dist/deprecations.d.ts.map +1 -0
  91. package/dist/deprecations.js +59 -0
  92. package/dist/feedback/coverage.d.ts +32 -0
  93. package/dist/feedback/coverage.d.ts.map +1 -0
  94. package/dist/feedback/coverage.js +95 -0
  95. package/dist/feedback/feedback.d.ts +35 -0
  96. package/dist/feedback/feedback.d.ts.map +1 -0
  97. package/dist/feedback/feedback.js +39 -0
  98. package/dist/gateway/http-protocol.d.ts +175 -0
  99. package/dist/gateway/http-protocol.d.ts.map +1 -0
  100. package/dist/gateway/http-protocol.js +105 -0
  101. package/dist/index.d.ts +242 -0
  102. package/dist/index.d.ts.map +1 -0
  103. package/dist/index.js +576 -0
  104. package/dist/init/finder.d.ts +94 -0
  105. package/dist/init/finder.d.ts.map +1 -0
  106. package/dist/init/finder.js +343 -0
  107. package/dist/init/init-cli.d.ts +10 -0
  108. package/dist/init/init-cli.d.ts.map +1 -0
  109. package/dist/init/init-cli.js +86 -0
  110. package/dist/init/observe-cli.d.ts +19 -0
  111. package/dist/init/observe-cli.d.ts.map +1 -0
  112. package/dist/init/observe-cli.js +73 -0
  113. package/dist/init/observe.d.ts +81 -0
  114. package/dist/init/observe.d.ts.map +1 -0
  115. package/dist/init/observe.js +245 -0
  116. package/dist/init/run.d.ts +99 -0
  117. package/dist/init/run.d.ts.map +1 -0
  118. package/dist/init/run.js +281 -0
  119. package/dist/init/signatures.d.ts +39 -0
  120. package/dist/init/signatures.d.ts.map +1 -0
  121. package/dist/init/signatures.js +116 -0
  122. package/dist/matrix/aggregate.d.ts +89 -0
  123. package/dist/matrix/aggregate.d.ts.map +1 -0
  124. package/dist/matrix/aggregate.js +186 -0
  125. package/dist/query/grammar.d.ts +35 -0
  126. package/dist/query/grammar.d.ts.map +1 -0
  127. package/dist/query/grammar.js +279 -0
  128. package/dist/query/run.d.ts +8 -0
  129. package/dist/query/run.d.ts.map +1 -0
  130. package/dist/query/run.js +21 -0
  131. package/dist/registry/build.d.ts +219 -0
  132. package/dist/registry/build.d.ts.map +1 -0
  133. package/dist/registry/build.js +313 -0
  134. package/dist/registry/cache-policy.d.ts +94 -0
  135. package/dist/registry/cache-policy.d.ts.map +1 -0
  136. package/dist/registry/cache-policy.js +238 -0
  137. package/dist/registry/call-notes.d.ts +11 -0
  138. package/dist/registry/call-notes.d.ts.map +1 -0
  139. package/dist/registry/call-notes.js +110 -0
  140. package/dist/registry/claims.d.ts +63 -0
  141. package/dist/registry/claims.d.ts.map +1 -0
  142. package/dist/registry/claims.js +107 -0
  143. package/dist/registry/effective-rate.d.ts +151 -0
  144. package/dist/registry/effective-rate.d.ts.map +1 -0
  145. package/dist/registry/effective-rate.js +156 -0
  146. package/dist/registry/facts.d.ts +32 -0
  147. package/dist/registry/facts.d.ts.map +1 -0
  148. package/dist/registry/facts.js +136 -0
  149. package/dist/registry/fingerprint.d.ts +20 -0
  150. package/dist/registry/fingerprint.d.ts.map +1 -0
  151. package/dist/registry/fingerprint.js +150 -0
  152. package/dist/registry/parity.d.ts +52 -0
  153. package/dist/registry/parity.d.ts.map +1 -0
  154. package/dist/registry/parity.js +82 -0
  155. package/dist/registry/pricing-shape.d.ts +113 -0
  156. package/dist/registry/pricing-shape.d.ts.map +1 -0
  157. package/dist/registry/pricing-shape.js +123 -0
  158. package/dist/registry/pricing.d.ts +43 -0
  159. package/dist/registry/pricing.d.ts.map +1 -0
  160. package/dist/registry/pricing.js +127 -0
  161. package/dist/registry/rows.d.ts +16 -0
  162. package/dist/registry/rows.d.ts.map +1 -0
  163. package/dist/registry/rows.js +57 -0
  164. package/dist/registry/seed.d.ts +43 -0
  165. package/dist/registry/seed.d.ts.map +1 -0
  166. package/dist/registry/seed.js +97 -0
  167. package/dist/registry/select.d.ts +68 -0
  168. package/dist/registry/select.d.ts.map +1 -0
  169. package/dist/registry/select.js +141 -0
  170. package/dist/registry/staleness.d.ts +18 -0
  171. package/dist/registry/staleness.d.ts.map +1 -0
  172. package/dist/registry/staleness.js +39 -0
  173. package/dist/rigindex/bootstrap.d.ts +36 -0
  174. package/dist/rigindex/bootstrap.d.ts.map +1 -0
  175. package/dist/rigindex/bootstrap.js +97 -0
  176. package/dist/rigindex/fit.d.ts +24 -0
  177. package/dist/rigindex/fit.d.ts.map +1 -0
  178. package/dist/rigindex/fit.js +76 -0
  179. package/dist/rigindex/gates.d.ts +52 -0
  180. package/dist/rigindex/gates.d.ts.map +1 -0
  181. package/dist/rigindex/gates.js +109 -0
  182. package/dist/rigindex/graph.d.ts +31 -0
  183. package/dist/rigindex/graph.d.ts.map +1 -0
  184. package/dist/rigindex/graph.js +120 -0
  185. package/dist/rigindex/index.d.ts +23 -0
  186. package/dist/rigindex/index.d.ts.map +1 -0
  187. package/dist/rigindex/index.js +112 -0
  188. package/dist/rigindex/types.d.ts +76 -0
  189. package/dist/rigindex/types.d.ts.map +1 -0
  190. package/dist/rigindex/types.js +8 -0
  191. package/dist/routes/cache-lint.d.ts +41 -0
  192. package/dist/routes/cache-lint.d.ts.map +1 -0
  193. package/dist/routes/cache-lint.js +81 -0
  194. package/dist/routes/draft.d.ts +83 -0
  195. package/dist/routes/draft.d.ts.map +1 -0
  196. package/dist/routes/draft.js +135 -0
  197. package/dist/routes/loader.d.ts +63 -0
  198. package/dist/routes/loader.d.ts.map +1 -0
  199. package/dist/routes/loader.js +574 -0
  200. package/dist/routes/manifest.d.ts +24 -0
  201. package/dist/routes/manifest.d.ts.map +1 -0
  202. package/dist/routes/manifest.js +118 -0
  203. package/dist/routes/resolved-bundle.d.ts +79 -0
  204. package/dist/routes/resolved-bundle.d.ts.map +1 -0
  205. package/dist/routes/resolved-bundle.js +148 -0
  206. package/dist/routes/sampling-config.d.ts +32 -0
  207. package/dist/routes/sampling-config.d.ts.map +1 -0
  208. package/dist/routes/sampling-config.js +91 -0
  209. package/dist/routes/serveability.d.ts +52 -0
  210. package/dist/routes/serveability.d.ts.map +1 -0
  211. package/dist/routes/serveability.js +73 -0
  212. package/dist/routes/template.d.ts +18 -0
  213. package/dist/routes/template.d.ts.map +1 -0
  214. package/dist/routes/template.js +94 -0
  215. package/dist/run/ajv.d.ts +22 -0
  216. package/dist/run/ajv.d.ts.map +1 -0
  217. package/dist/run/ajv.js +32 -0
  218. package/dist/run/cache-handle.d.ts +58 -0
  219. package/dist/run/cache-handle.d.ts.map +1 -0
  220. package/dist/run/cache-handle.js +54 -0
  221. package/dist/run/cache-stamp.d.ts +65 -0
  222. package/dist/run/cache-stamp.d.ts.map +1 -0
  223. package/dist/run/cache-stamp.js +0 -0
  224. package/dist/run/capture-write.d.ts +33 -0
  225. package/dist/run/capture-write.d.ts.map +1 -0
  226. package/dist/run/capture-write.js +55 -0
  227. package/dist/run/cost.d.ts +26 -0
  228. package/dist/run/cost.d.ts.map +1 -0
  229. package/dist/run/cost.js +41 -0
  230. package/dist/run/dispatch.d.ts +99 -0
  231. package/dist/run/dispatch.d.ts.map +1 -0
  232. package/dist/run/dispatch.js +191 -0
  233. package/dist/run/envelope.d.ts +21 -0
  234. package/dist/run/envelope.d.ts.map +1 -0
  235. package/dist/run/envelope.js +45 -0
  236. package/dist/run/grounding.d.ts +56 -0
  237. package/dist/run/grounding.d.ts.map +1 -0
  238. package/dist/run/grounding.js +89 -0
  239. package/dist/run/raw.d.ts +88 -0
  240. package/dist/run/raw.d.ts.map +1 -0
  241. package/dist/run/raw.js +286 -0
  242. package/dist/run/record-attempt.d.ts +40 -0
  243. package/dist/run/record-attempt.d.ts.map +1 -0
  244. package/dist/run/record-attempt.js +99 -0
  245. package/dist/run/repair.d.ts +36 -0
  246. package/dist/run/repair.d.ts.map +1 -0
  247. package/dist/run/repair.js +61 -0
  248. package/dist/run/resolve.d.ts +99 -0
  249. package/dist/run/resolve.d.ts.map +1 -0
  250. package/dist/run/resolve.js +203 -0
  251. package/dist/run/retry.d.ts +19 -0
  252. package/dist/run/retry.d.ts.map +1 -0
  253. package/dist/run/retry.js +50 -0
  254. package/dist/run/run.d.ts +55 -0
  255. package/dist/run/run.d.ts.map +1 -0
  256. package/dist/run/run.js +540 -0
  257. package/dist/run/stream.d.ts +49 -0
  258. package/dist/run/stream.d.ts.map +1 -0
  259. package/dist/run/stream.js +87 -0
  260. package/dist/run/validate.d.ts +15 -0
  261. package/dist/run/validate.d.ts.map +1 -0
  262. package/dist/run/validate.js +55 -0
  263. package/dist/savings/cache-ledger.d.ts +108 -0
  264. package/dist/savings/cache-ledger.d.ts.map +1 -0
  265. package/dist/savings/cache-ledger.js +174 -0
  266. package/dist/savings/cache-reconcile.d.ts +64 -0
  267. package/dist/savings/cache-reconcile.d.ts.map +1 -0
  268. package/dist/savings/cache-reconcile.js +59 -0
  269. package/dist/savings/cache-stats.d.ts +63 -0
  270. package/dist/savings/cache-stats.d.ts.map +1 -0
  271. package/dist/savings/cache-stats.js +108 -0
  272. package/dist/savings/emit.d.ts +38 -0
  273. package/dist/savings/emit.d.ts.map +1 -0
  274. package/dist/savings/emit.js +100 -0
  275. package/dist/savings/odometer.d.ts +32 -0
  276. package/dist/savings/odometer.d.ts.map +1 -0
  277. package/dist/savings/odometer.js +70 -0
  278. package/dist/savings/swap-execute.d.ts +58 -0
  279. package/dist/savings/swap-execute.d.ts.map +1 -0
  280. package/dist/savings/swap-execute.js +130 -0
  281. package/dist/savings/swaps-client.d.ts +55 -0
  282. package/dist/savings/swaps-client.d.ts.map +1 -0
  283. package/dist/savings/swaps-client.js +95 -0
  284. package/dist/settings/api-fetcher.d.ts +39 -0
  285. package/dist/settings/api-fetcher.d.ts.map +1 -0
  286. package/dist/settings/api-fetcher.js +92 -0
  287. package/dist/settings/index.d.ts +10 -0
  288. package/dist/settings/index.d.ts.map +1 -0
  289. package/dist/settings/index.js +15 -0
  290. package/dist/settings/task-settings.d.ts +91 -0
  291. package/dist/settings/task-settings.d.ts.map +1 -0
  292. package/dist/settings/task-settings.js +116 -0
  293. package/dist/status.d.ts +77 -0
  294. package/dist/status.d.ts.map +1 -0
  295. package/dist/status.js +113 -0
  296. package/dist/telemetry/artifact-store.d.ts +48 -0
  297. package/dist/telemetry/artifact-store.d.ts.map +1 -0
  298. package/dist/telemetry/artifact-store.js +189 -0
  299. package/dist/telemetry/cache-miss-streak.d.ts +79 -0
  300. package/dist/telemetry/cache-miss-streak.d.ts.map +1 -0
  301. package/dist/telemetry/cache-miss-streak.js +99 -0
  302. package/dist/telemetry/capture.d.ts +31 -0
  303. package/dist/telemetry/capture.d.ts.map +1 -0
  304. package/dist/telemetry/capture.js +81 -0
  305. package/dist/telemetry/exporter-setup.d.ts +28 -0
  306. package/dist/telemetry/exporter-setup.d.ts.map +1 -0
  307. package/dist/telemetry/exporter-setup.js +34 -0
  308. package/dist/telemetry/exporter.d.ts +137 -0
  309. package/dist/telemetry/exporter.d.ts.map +1 -0
  310. package/dist/telemetry/exporter.js +510 -0
  311. package/dist/telemetry/health.d.ts +76 -0
  312. package/dist/telemetry/health.d.ts.map +1 -0
  313. package/dist/telemetry/health.js +92 -0
  314. package/dist/telemetry/ingest-protocol.d.ts +310 -0
  315. package/dist/telemetry/ingest-protocol.d.ts.map +1 -0
  316. package/dist/telemetry/ingest-protocol.js +363 -0
  317. package/dist/telemetry/load-error-report.d.ts +24 -0
  318. package/dist/telemetry/load-error-report.d.ts.map +1 -0
  319. package/dist/telemetry/load-error-report.js +50 -0
  320. package/dist/telemetry/otel-map.d.ts +121 -0
  321. package/dist/telemetry/otel-map.d.ts.map +1 -0
  322. package/dist/telemetry/otel-map.js +224 -0
  323. package/dist/telemetry/otel-normalize.d.ts +104 -0
  324. package/dist/telemetry/otel-normalize.d.ts.map +1 -0
  325. package/dist/telemetry/otel-normalize.js +530 -0
  326. package/dist/telemetry/otlp-emitter.d.ts +52 -0
  327. package/dist/telemetry/otlp-emitter.d.ts.map +1 -0
  328. package/dist/telemetry/otlp-emitter.js +144 -0
  329. package/dist/telemetry/otlp-proto.d.ts +29 -0
  330. package/dist/telemetry/otlp-proto.d.ts.map +1 -0
  331. package/dist/telemetry/otlp-proto.js +266 -0
  332. package/dist/telemetry/sinks.d.ts +90 -0
  333. package/dist/telemetry/sinks.d.ts.map +1 -0
  334. package/dist/telemetry/sinks.js +180 -0
  335. package/dist/telemetry/store.d.ts +65 -0
  336. package/dist/telemetry/store.d.ts.map +1 -0
  337. package/dist/telemetry/store.js +547 -0
  338. package/dist/tenancy/api-key-format.d.ts +77 -0
  339. package/dist/tenancy/api-key-format.d.ts.map +1 -0
  340. package/dist/tenancy/api-key-format.js +110 -0
  341. package/dist/tenancy/constants.d.ts +23 -0
  342. package/dist/tenancy/constants.d.ts.map +1 -0
  343. package/dist/tenancy/constants.js +25 -0
  344. package/dist/tenancy/scrub.d.ts +47 -0
  345. package/dist/tenancy/scrub.d.ts.map +1 -0
  346. package/dist/tenancy/scrub.js +85 -0
  347. package/dist/types/config-teaching.d.ts +62 -0
  348. package/dist/types/config-teaching.d.ts.map +1 -0
  349. package/dist/types/config-teaching.js +183 -0
  350. package/dist/types/failures-teaching.d.ts +56 -0
  351. package/dist/types/failures-teaching.d.ts.map +1 -0
  352. package/dist/types/failures-teaching.js +177 -0
  353. package/dist/types/failures.d.ts +27 -0
  354. package/dist/types/failures.d.ts.map +1 -0
  355. package/dist/types/failures.js +41 -0
  356. package/dist/types/provider.d.ts +200 -0
  357. package/dist/types/provider.d.ts.map +1 -0
  358. package/dist/types/provider.js +40 -0
  359. package/dist/types/route.d.ts +205 -0
  360. package/dist/types/route.d.ts.map +1 -0
  361. package/dist/types/route.js +6 -0
  362. package/dist/types/telemetry.d.ts +206 -0
  363. package/dist/types/telemetry.d.ts.map +1 -0
  364. package/dist/types/telemetry.js +6 -0
  365. package/dist/validate-cli.d.ts +12 -0
  366. package/dist/validate-cli.d.ts.map +1 -0
  367. package/dist/validate-cli.js +57 -0
  368. package/dist/verify/capture.d.ts +66 -0
  369. package/dist/verify/capture.d.ts.map +1 -0
  370. package/dist/verify/capture.js +180 -0
  371. package/dist/verify/cli-run.d.ts +23 -0
  372. package/dist/verify/cli-run.d.ts.map +1 -0
  373. package/dist/verify/cli-run.js +57 -0
  374. package/dist/verify/diff.d.ts +48 -0
  375. package/dist/verify/diff.d.ts.map +1 -0
  376. package/dist/verify/diff.js +157 -0
  377. package/dist/verify/index.d.ts +17 -0
  378. package/dist/verify/index.d.ts.map +1 -0
  379. package/dist/verify/index.js +24 -0
  380. package/dist/verify/report.d.ts +14 -0
  381. package/dist/verify/report.d.ts.map +1 -0
  382. package/dist/verify/report.js +88 -0
  383. package/dist/verify/types.d.ts +101 -0
  384. package/dist/verify/types.d.ts.map +1 -0
  385. package/dist/verify/types.js +17 -0
  386. package/dist/verify/verify.d.ts +29 -0
  387. package/dist/verify/verify.d.ts.map +1 -0
  388. package/dist/verify/verify.js +148 -0
  389. package/dist/watch/budget.d.ts +19 -0
  390. package/dist/watch/budget.d.ts.map +1 -0
  391. package/dist/watch/budget.js +35 -0
  392. package/dist/watch/evaluate.d.ts +225 -0
  393. package/dist/watch/evaluate.d.ts.map +1 -0
  394. package/dist/watch/evaluate.js +155 -0
  395. package/dist/watch/monitor.d.ts +116 -0
  396. package/dist/watch/monitor.d.ts.map +1 -0
  397. package/dist/watch/monitor.js +199 -0
  398. package/dist/watch/proposals.d.ts +89 -0
  399. package/dist/watch/proposals.d.ts.map +1 -0
  400. package/dist/watch/proposals.js +197 -0
  401. package/dist/watch/run-record.d.ts +64 -0
  402. package/dist/watch/run-record.d.ts.map +1 -0
  403. package/dist/watch/run-record.js +86 -0
  404. package/dist/watch/run-reported.d.ts +20 -0
  405. package/dist/watch/run-reported.d.ts.map +1 -0
  406. package/dist/watch/run-reported.js +53 -0
  407. package/dist/watch/run.d.ts +73 -0
  408. package/dist/watch/run.d.ts.map +1 -0
  409. package/dist/watch/run.js +83 -0
  410. package/dist/watch/state-store.d.ts +48 -0
  411. package/dist/watch/state-store.d.ts.map +1 -0
  412. package/dist/watch/state-store.js +86 -0
  413. package/dist/watch/state.d.ts +21 -0
  414. package/dist/watch/state.d.ts.map +1 -0
  415. package/dist/watch/state.js +93 -0
  416. package/package.json +56 -14
  417. package/registry/LICENSE +20 -0
  418. package/registry/leaderboard.json +3639 -0
  419. package/registry/registry.json +7263 -0
  420. package/index.js +0 -3
@@ -0,0 +1,510 @@
1
+ "use strict";
2
+ /**
3
+ * Async batch exporter: SQLite telemetry → wherever this rig's telemetry goes.
4
+ * Spec §5 (cloud sink) + §6 criteria 8–9. Fire-and-forget from the run loop:
5
+ * the inference path NEVER blocks on, or fails because of, this sink.
6
+ * At-least-once delivery with idempotent ids; unexported rows queue locally in
7
+ * SQLite (the inferences table itself, behind a rowid watermark) and flush on
8
+ * recovery; watermark bookkeeping per source instance.
9
+ *
10
+ * ── ONE LOOP, TWO TRANSPORTS (Phase 6 WS2) ───────────────────────────────
11
+ * Everything below is transport-agnostic. Batching, watermarks, backoff,
12
+ * once-per-process mirrors — all of it runs identically whether the batch
13
+ * leaves as a service-role PostgREST write (self-host) or as a `rig_sk_`-
14
+ * authenticated POST to api.modelrig.ai (hosted). That is not a tidiness
15
+ * preference: telemetry-ingestion-boundary.md §4 makes it a requirement, so
16
+ * that moving between self-host and hosted is a config change rather than a
17
+ * data-model change. `sinks.ts` holds the two transports; `ingest-protocol.ts`
18
+ * holds the one mapper both of them write rows through.
19
+ */
20
+ Object.defineProperty(exports, "__esModule", { value: true });
21
+ exports.DEFAULT_INTERVAL_MS = void 0;
22
+ exports.buildExportedRouteMirrors = buildExportedRouteMirrors;
23
+ exports.createTelemetryExporter = createTelemetryExporter;
24
+ exports.buildLoadErrorBatch = buildLoadErrorBatch;
25
+ const resolved_bundle_1 = require("../routes/resolved-bundle");
26
+ const ingest_protocol_1 = require("./ingest-protocol");
27
+ const sinks_1 = require("./sinks");
28
+ /**
29
+ * Build the `routes` mirror payload from loaded bundles (Phase D, spec §13).
30
+ * Each `b` is ALREADY file-resolved — the loader inlined the schema + prompt
31
+ * files at createRig() and FAILED THERE (teaching RouteConfigError) if one was
32
+ * missing, so nothing unresolved reaches here and the gateway never needs the
33
+ * customer's filesystem (§13.1 rule 6). Lives here rather than in index.ts to
34
+ * keep that file under the LOC cap.
35
+ */
36
+ function buildExportedRouteMirrors(bundles) {
37
+ return bundles.map((b) => {
38
+ const bundle = (0, resolved_bundle_1.resolveRouteBundle)(b);
39
+ return {
40
+ route: b.route,
41
+ version: b.version,
42
+ candidates: b.candidates.map((c) => ({ provider: c.provider, model: c.model })),
43
+ capture: b.capture,
44
+ bundle,
45
+ bundleSha: (0, resolved_bundle_1.hashResolvedBundle)(bundle),
46
+ };
47
+ });
48
+ }
49
+ const DEFAULT_BATCH_SIZE = 200;
50
+ /** The flush cadence. Exported so the settings resolver (§1.5) can beat on the
51
+ * SAME interval — "refreshes on the exporter interval" without a second knob. */
52
+ exports.DEFAULT_INTERVAL_MS = 10000;
53
+ /** After a sink failure, skip this many intervals, doubling per consecutive
54
+ * failure (simple exponential backoff, capped). */
55
+ const BACKOFF_MAX_SKIPS = 32;
56
+ /**
57
+ * Consecutive DETERMINISTIC failures on the SAME stuck batch before the
58
+ * exporter gives up and skips past it (E2 hardening Phase 2, spec §2.3.3).
59
+ * Five failures with the global exponential backoff between them is a
60
+ * multi-minute self-heal window — long enough for a still-deploying migration
61
+ * that adds a missing column to land, short enough to bound the wedge. A
62
+ * TRANSIENT failure never counts toward this, so an outage of any length can
63
+ * never trip it.
64
+ */
65
+ const POISON_SKIP_THRESHOLD = 5;
66
+ function toWireInference(row) {
67
+ return {
68
+ id: row.id,
69
+ ts: row.ts,
70
+ route: row.route,
71
+ route_version: row.routeVersion,
72
+ provider: row.provider,
73
+ model: row.model,
74
+ requested_tier: row.requestedTier,
75
+ served_tier: row.servedTier,
76
+ tokens_in: row.tokensIn,
77
+ tokens_out: row.tokensOut,
78
+ tokens_cached: row.tokensCached,
79
+ cost_estimate_usd: row.costEstimateUsd,
80
+ latency_ms: row.latencyMs,
81
+ ttfb_ms: row.ttfbMs,
82
+ failure_class: row.failureClass,
83
+ attempts: JSON.parse(row.attemptsJson),
84
+ tags: JSON.parse(row.tagsJson),
85
+ fingerprint_cell: row.fingerprintCell,
86
+ served_variant: row.servedVariant,
87
+ repaired_by: row.repairedBy,
88
+ pricing_missing: row.pricingMissing,
89
+ // Cache accounting (WS-A) — always emitted by a current SDK. Requires the
90
+ // `inferences` cache columns (migration 00049) to exist server-side; apply
91
+ // 00049 BEFORE deploying this build (migrate-first), else the insert 4xx's
92
+ // and the exporter's poison-skip drops the batch after its self-heal window.
93
+ tokens_cache_write: row.tokensCacheWrite ?? 0,
94
+ tokens_cache_write_1h: row.tokensCacheWrite1h ?? 0,
95
+ cache_key: row.cacheKey ?? null,
96
+ prefix_fingerprint: row.prefixFingerprint ?? null,
97
+ };
98
+ }
99
+ function toWireRun(row) {
100
+ return {
101
+ id: row.id,
102
+ pipeline: row.pipeline,
103
+ pipeline_version: row.pipelineVersion,
104
+ environment: row.environment,
105
+ episode_key: row.episodeKey,
106
+ started_at: row.startedAt,
107
+ ended_at: row.endedAt,
108
+ status: row.status,
109
+ };
110
+ }
111
+ function toWireStep(row) {
112
+ return {
113
+ run_id: row.runId,
114
+ step_key: row.stepKey,
115
+ attempt_no: row.attemptNo,
116
+ task: row.task,
117
+ provenance: row.provenance,
118
+ inference_id: row.inferenceId,
119
+ span_trace_id: row.spanTraceId,
120
+ span_id: row.spanId,
121
+ started_at: row.startedAt,
122
+ ended_at: row.endedAt,
123
+ failure_class: row.failureClass,
124
+ };
125
+ }
126
+ function toWireArtifact(row) {
127
+ return {
128
+ id: row.id,
129
+ run_id: row.runId,
130
+ step_key: row.stepKey,
131
+ attempt_no: row.attemptNo,
132
+ task: row.task,
133
+ name: row.name,
134
+ type: row.type,
135
+ version: row.version,
136
+ supersedes_artifact_id: row.supersedesArtifactId,
137
+ declared_schema: row.declaredSchema ?? null,
138
+ fingerprint_cell: row.fingerprintCell,
139
+ content_ref: row.contentRef,
140
+ content_hash: row.contentHash,
141
+ content_bytes: row.contentBytes,
142
+ data_classification: row.dataClassification,
143
+ expires_at: row.expiresAt,
144
+ // Per-row scrub findings (E4 chunk 0) — spread-iff-present so a null/absent
145
+ // scrub never widens the batch beyond what older servers accept.
146
+ ...(row.scrub !== undefined && row.scrub !== null ? { scrub: row.scrub } : {}),
147
+ };
148
+ }
149
+ function toWireEdge(row) {
150
+ return { from_artifact_id: row.fromArtifactId, to_artifact_id: row.toArtifactId, kind: row.kind };
151
+ }
152
+ function toWireEvalCase(row) {
153
+ return {
154
+ id: row.id,
155
+ task: row.task,
156
+ source_artifact_id: row.sourceArtifactId,
157
+ corrected_artifact_id: row.correctedArtifactId,
158
+ origin: row.origin,
159
+ note: row.note,
160
+ created_by: row.createdBy,
161
+ };
162
+ }
163
+ function toWireEvaluation(row) {
164
+ return {
165
+ id: row.id,
166
+ artifact_id: row.artifactId,
167
+ run_id: row.runId,
168
+ evaluator: row.evaluator,
169
+ evaluator_kind: row.evaluatorKind,
170
+ score: row.score,
171
+ passed: row.passed,
172
+ label: row.label,
173
+ explanation: row.explanation,
174
+ created_by: row.createdBy,
175
+ };
176
+ }
177
+ function toWireFeedback(row) {
178
+ return {
179
+ id: row.id,
180
+ ts: row.ts,
181
+ inference_id: row.inferenceId,
182
+ episode_key: row.episodeKey,
183
+ verdict: row.verdict,
184
+ score: row.score,
185
+ note: row.note,
186
+ source: row.source,
187
+ created_by: row.createdBy,
188
+ // Present only when the verdict names its own org. Omitted (not null) so
189
+ // that `stampBatch` can fall back to the exporter's tenant without having
190
+ // to distinguish "no org" from "explicitly no org".
191
+ ...(row.orgId !== null ? { org_id: row.orgId } : {}),
192
+ };
193
+ }
194
+ function createTelemetryExporter(store, config) {
195
+ const batchSize = config.batchSize ?? DEFAULT_BATCH_SIZE;
196
+ const intervalMs = config.intervalMs ?? exports.DEFAULT_INTERVAL_MS;
197
+ const { sink, sourceId } = config;
198
+ // The wire version this exporter announces (spec §1.3). v2 iff the artifact
199
+ // namespace is active; otherwise v1, byte-identical to before v2 existed.
200
+ const wireVersion = config.artifacts === true ? ingest_protocol_1.INGEST_PROTOCOL_VERSION : ingest_protocol_1.INGEST_PROTOCOL_VERSION_V1;
201
+ let timer = null;
202
+ let flushing = false;
203
+ let skipsRemaining = 0;
204
+ let consecutiveFailures = 0;
205
+ let routesMirrored = false;
206
+ let manifestMirrored = false;
207
+ // Poison-pill state (E2 hardening Phase 2). Per stream key, the deterministic
208
+ // failure count for the CURRENTLY-STUCK batch — which is identified by its
209
+ // watermark, since the read is always "rows after W, limit N", so an
210
+ // unadvanced watermark IS the same batch. Reset the instant the watermark
211
+ // moves (a success OR a skip). `poisonSkipped` is the cumulative, per-stream
212
+ // count of rows dropped this way — the never-silent surface `stats()` reads.
213
+ const poisonState = new Map();
214
+ const poisonSkipped = {};
215
+ /**
216
+ * The once-per-process mirrors, folded into whichever batch goes out first.
217
+ *
218
+ * They ride along rather than travelling alone because in api mode a batch
219
+ * is an HTTP request, and three requests where one would do is three times
220
+ * the chance of a partial flush.
221
+ */
222
+ function pendingHeader() {
223
+ const header = {};
224
+ if (!routesMirrored && config.routes !== undefined && config.routes.length > 0) {
225
+ header.routes = config.routes.map((r) => ({
226
+ route: r.route,
227
+ version: r.version,
228
+ candidates: r.candidates,
229
+ capture: r.capture ?? false,
230
+ // Phase D: carry the resolved bundle + hash when the caller built them
231
+ // (index.ts always does; a caller that doesn't stays metadata-only).
232
+ ...(r.bundle !== undefined ? { bundle: r.bundle, bundle_sha: r.bundleSha } : {}),
233
+ }));
234
+ }
235
+ if (!manifestMirrored && config.manifest !== undefined && config.manifest !== null) {
236
+ header.manifest = {
237
+ name: config.manifest.name,
238
+ dimensions: config.manifest.dimensions,
239
+ contribution: config.manifest.contribution,
240
+ // F1: names only — route, provider/model, and the env var to set.
241
+ serveability: (config.serveability ?? []).map((w) => ({
242
+ route: w.route,
243
+ inert: w.inert.map((c) => ({ provider: c.provider, model: c.model })),
244
+ serveable_count: w.serveableCount,
245
+ fix_hint: w.fixHint,
246
+ })),
247
+ // Follow-up #2: the describeConfigFailure shape, or [] on a clean load
248
+ // (which clears a stale failure from a prior broken deploy).
249
+ load_errors: [...(config.loadErrors ?? [])],
250
+ };
251
+ }
252
+ return header;
253
+ }
254
+ /** Mark the mirrors delivered — called ONLY after a confirmed send, so a
255
+ * failed first batch re-sends them rather than silently losing them. */
256
+ function markHeaderDelivered(header) {
257
+ if (header.routes !== undefined)
258
+ routesMirrored = true;
259
+ if (header.manifest !== undefined)
260
+ manifestMirrored = true;
261
+ }
262
+ /**
263
+ * Send one batch for a stream, applying the poison-pill state machine
264
+ * (spec §2.3.3). Returns:
265
+ * "sent" the send succeeded — the caller advances the watermark and marks
266
+ * the header delivered.
267
+ * "skip" a deterministic poison was skipped after K failures — the caller
268
+ * advances the watermark PAST this batch without it ever landing
269
+ * (the rows are counted + logged, never retried).
270
+ * THROWS the underlying error when the caller must abort this flush and let
271
+ * the global backoff apply: a transient failure (retry forever, never counted)
272
+ * OR a deterministic one still inside its self-heal window (failCount < K).
273
+ *
274
+ * A poison in one stream never blocks another: the skip breaks only its own
275
+ * stuck batch, and the caller then continues to the next batch and the next
276
+ * stream (spec §2.3.3).
277
+ */
278
+ async function sendOrPoison(stream, watermark, rowCount, batch) {
279
+ try {
280
+ await sink.send(batch);
281
+ // A success advances the watermark → this stream is unstuck; drop its
282
+ // failure record so a later, unrelated batch starts fresh.
283
+ poisonState.delete(stream);
284
+ return "sent";
285
+ }
286
+ catch (err) {
287
+ // Anything that is not a classified SinkSendError is treated as TRANSIENT
288
+ // — we never poison-skip (drop data) on an error we cannot reason about.
289
+ const retriable = err instanceof sinks_1.SinkSendError ? err.retriable : true;
290
+ if (retriable) {
291
+ // Transient: do NOT touch failCount. Rethrow so the existing global
292
+ // backoff owns it and the stream retries forever (at-least-once).
293
+ throw err;
294
+ }
295
+ // Deterministic. Count failures against THIS stuck batch (keyed by W); a
296
+ // watermark that no longer matches means a different batch, so reset.
297
+ const prior = poisonState.get(stream);
298
+ const failCount = (prior !== undefined && prior.watermark === watermark ? prior.failCount : 0) + 1;
299
+ if (failCount < POISON_SKIP_THRESHOLD) {
300
+ // Self-heal window: rethrow (retry next flush, with backoff). A
301
+ // still-deploying migration that adds the missing column lands here.
302
+ poisonState.set(stream, { watermark, failCount });
303
+ throw err;
304
+ }
305
+ // POISON. Advance past this batch WITHOUT sending, count it, log LOUDLY,
306
+ // reset — the stream (and every later stream) keeps flowing.
307
+ poisonState.delete(stream);
308
+ poisonSkipped[stream] = (poisonSkipped[stream] ?? 0) + rowCount;
309
+ const status = err instanceof sinks_1.SinkSendError ? err.status : undefined;
310
+ console.warn(`[modelrig exporter] POISON-SKIP: stream "${stream}" batch at watermark ${watermark} ` +
311
+ `(${rowCount} row(s), status ${status ?? "network"}) was rejected deterministically ` +
312
+ `${POISON_SKIP_THRESHOLD}× running — advancing the watermark PAST it WITHOUT writing. ` +
313
+ `These rows are dropped and will NOT be retried; investigate the rejection and backfill.`);
314
+ return "skip";
315
+ }
316
+ }
317
+ async function flushOnce() {
318
+ let header = pendingHeader();
319
+ /**
320
+ * Drain one watermarked stream to exhaustion, batch by batch, through the
321
+ * poison-pill state machine. Every stream (inferences, feedback, runs,
322
+ * steps, artifacts, edges, evals) is the SAME at-least-once dance behind its
323
+ * own export_state key — factored here so the watermark/header/poison
324
+ * bookkeeping lives in exactly one place and no stream can drift from
325
+ * another. A "skip" advances the watermark past a poison batch WITHOUT
326
+ * having sent it; a "sent" advances it and delivers the once-per-flush
327
+ * header. Reassigning the captured `header` is intentional: the header rides
328
+ * the first CONFIRMED send, so a skipped (unsent) batch leaves it for the
329
+ * next one.
330
+ */
331
+ async function drainStream(streamKey, read, toSection) {
332
+ for (;;) {
333
+ const watermark = store.getExportWatermark(streamKey);
334
+ const pending = read(watermark, batchSize);
335
+ if (pending.length === 0)
336
+ break;
337
+ const outcome = await sendOrPoison(streamKey, watermark, pending.length, {
338
+ v: wireVersion,
339
+ source: sourceId,
340
+ ...header,
341
+ ...toSection(pending.map((p) => p.row)),
342
+ });
343
+ if (outcome === "sent") {
344
+ markHeaderDelivered(header);
345
+ header = {};
346
+ }
347
+ // Advance past this batch on BOTH a send and a poison-skip — a skip's
348
+ // whole purpose is to move the watermark past the batch that will never
349
+ // land, so the stream keeps flowing (spec §2.3.3).
350
+ store.setExportWatermark(streamKey, pending[pending.length - 1].rowid);
351
+ if (pending.length < batchSize)
352
+ break;
353
+ }
354
+ }
355
+ // Inferences past the watermark. The watermark advances after a confirmed
356
+ // send (at-least-once; the sink's upsert makes the duplicate harmless) OR a
357
+ // poison-skip (the batch will never land). Inferences also carry watermarkAt.
358
+ await drainStream(sourceId, (w, n) => store.readInferencesAfter(w, n), (rows) => ({
359
+ inferences: rows.map((row) => toWireInference(row)),
360
+ watermarkAt: new Date().toISOString(),
361
+ }));
362
+ // Feedback, runs, steps, artifacts, edges, evals: the same dance, each
363
+ // behind its own export_state key so no stream ever blocks another — a
364
+ // poison in one is skipped past without touching the rest.
365
+ await drainStream(`${sourceId}:feedback`, (w, n) => store.readFeedbackAfter(w, n), (rows) => ({
366
+ feedback: rows.map((row) => toWireFeedback(row)),
367
+ }));
368
+ // Execution graph (Expansion E2 chunk A) — ONLY when the artifact namespace
369
+ // is active. When artifacts are off these streams never drain and no
370
+ // runs/steps section is ever built (byte-identical to before v2 existed).
371
+ if (config.artifacts === true) {
372
+ await drainStream(`${sourceId}:runs`, (w, n) => store.readRunsAfter(w, n), (rows) => ({
373
+ runs: rows.map((row) => toWireRun(row)),
374
+ }));
375
+ await drainStream(`${sourceId}:steps`, (w, n) => store.readStepsAfter(w, n), (rows) => ({
376
+ steps: rows.map((row) => toWireStep(row)),
377
+ }));
378
+ // Artifact metadata (chunk B): rows live in the SEPARATE artifact store;
379
+ // watermarks live in the main store's export_state, keyed `:artifacts` etc.
380
+ const artStore = config.artifactStore;
381
+ if (artStore !== undefined) {
382
+ await drainStream(`${sourceId}:artifacts`, (w, n) => artStore.readArtifactsAfter(w, n), (rows) => ({
383
+ artifacts: rows.map((row) => toWireArtifact(row)),
384
+ }));
385
+ await drainStream(`${sourceId}:edges`, (w, n) => artStore.readEdgesAfter(w, n), (rows) => ({
386
+ artifact_edges: rows.map((row) => toWireEdge(row)),
387
+ }));
388
+ await drainStream(`${sourceId}:evals`, (w, n) => artStore.readEvaluationsAfter(w, n), (rows) => ({
389
+ artifact_evaluations: rows.map((row) => toWireEvaluation(row)),
390
+ }));
391
+ // Promoted eval cases (E4 chunk A) — same at-least-once dance behind its
392
+ // own watermark key.
393
+ await drainStream(`${sourceId}:eval_cases`, (w, n) => artStore.readEvalCasesAfter(w, n), (rows) => ({
394
+ eval_cases: rows.map((row) => toWireEvalCase(row)),
395
+ }));
396
+ // Post-flush hook (E4 chunk 0, flag 2): the artifacts stream is now
397
+ // confirmed exported (every drainStream above returned without throwing),
398
+ // so any content upload that 404'd racing its own metadata can re-attempt
399
+ // — the row is durable. Reached ONLY on a clean artifact flush; a poison
400
+ // rethrow above skips it, so we never retry content while metadata export
401
+ // is failing. Never throws (its rejection would be swallowed anyway).
402
+ if (config.onArtifactsFlushed !== undefined) {
403
+ await config.onArtifactsFlushed();
404
+ }
405
+ }
406
+ }
407
+ // Envelopes: small table, full upsert each flush (no watermark). This send
408
+ // also carries any header the streams above never got the chance to — a rig
409
+ // that has routes but has not run yet must still appear in the console.
410
+ // A deterministic failure here poison-skips too (keyed by a synthetic 0
411
+ // watermark, since there is no cursor to advance): after K it is counted +
412
+ // logged and this flush moves on, rather than wedging the header forever.
413
+ const envelopes = store.listEnvelopes();
414
+ if (envelopes.length > 0 || Object.keys(header).length > 0) {
415
+ const outcome = await sendOrPoison(`${sourceId}:envelopes`, 0, Math.max(envelopes.length, 1), {
416
+ v: wireVersion,
417
+ source: sourceId,
418
+ ...header,
419
+ ...(envelopes.length > 0
420
+ ? {
421
+ envelopes: envelopes.map((e) => ({
422
+ envelope_key: e.envelopeKey,
423
+ budget_usd: e.budgetUsd,
424
+ spent_usd: e.spentUsd,
425
+ status: e.status,
426
+ })),
427
+ }
428
+ : {}),
429
+ });
430
+ if (outcome === "sent")
431
+ markHeaderDelivered(header);
432
+ }
433
+ }
434
+ async function guardedFlush() {
435
+ if (flushing)
436
+ return;
437
+ flushing = true;
438
+ try {
439
+ await flushOnce();
440
+ consecutiveFailures = 0;
441
+ }
442
+ catch (err) {
443
+ // Swallow-and-log, ALWAYS — sink failures must never reach the run loop.
444
+ consecutiveFailures += 1;
445
+ skipsRemaining = Math.min(2 ** consecutiveFailures, BACKOFF_MAX_SKIPS);
446
+ console.warn(`[modelrig exporter] ${sink.mode} sink flush failed (attempt ${consecutiveFailures}, ` +
447
+ `backing off ${skipsRemaining} intervals): ` +
448
+ `${err instanceof Error ? err.message : String(err)}`);
449
+ }
450
+ finally {
451
+ flushing = false;
452
+ }
453
+ }
454
+ return {
455
+ mode: sink.mode,
456
+ start() {
457
+ if (timer !== null)
458
+ return;
459
+ timer = setInterval(() => {
460
+ if (skipsRemaining > 0) {
461
+ skipsRemaining -= 1;
462
+ return;
463
+ }
464
+ void guardedFlush();
465
+ }, intervalMs);
466
+ // Never keep the process alive for telemetry export.
467
+ timer.unref?.();
468
+ },
469
+ async flush() {
470
+ await guardedFlush();
471
+ },
472
+ async stop() {
473
+ if (timer !== null) {
474
+ clearInterval(timer);
475
+ timer = null;
476
+ }
477
+ await guardedFlush();
478
+ },
479
+ stats() {
480
+ // A COPY, so a reader can never mutate the exporter's live counters.
481
+ return { poisonSkipped: { ...poisonSkipped } };
482
+ },
483
+ };
484
+ }
485
+ /**
486
+ * The one-shot manifest batch that carries a load-time config failure to the
487
+ * console (follow-up #2), for the case `createRig` throws BEFORE the exporter
488
+ * loop exists (a route bundle that fails to load never reaches the loop).
489
+ *
490
+ * It is a manifest-only batch: no inferences, no watermark. The manifest carries
491
+ * whatever `rig.yaml` could still provide (name/dimensions/contribution — the
492
+ * routes failing to load does not stop rig.yaml loading) plus the load errors;
493
+ * `serveability` is `[]` because it cannot be computed without loaded routes.
494
+ * A caller sends it fire-and-forget through the same sink the run loop uses.
495
+ */
496
+ function buildLoadErrorBatch(sourceId, manifest, loadErrors) {
497
+ return {
498
+ // A manifest-only batch carries no artifact sections — announce v1 so it is
499
+ // accepted by any server vintage, even one predating protocol v2.
500
+ v: ingest_protocol_1.INGEST_PROTOCOL_VERSION_V1,
501
+ source: sourceId,
502
+ manifest: {
503
+ name: manifest?.name ?? sourceId,
504
+ dimensions: manifest?.dimensions ?? [],
505
+ contribution: manifest?.contribution ?? false,
506
+ serveability: [],
507
+ load_errors: [...loadErrors],
508
+ },
509
+ };
510
+ }
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Observed-health aggregation — G-4 (observed-health/spec.md §3.1, §4.1).
3
+ *
4
+ * One home for the per-endpoint latency/TTFB percentile + error-rate + min-n
5
+ * math, shared by the live store reader (store.ts `readEndpointHealth`) and the
6
+ * observed-layer rollup (registry/build-observed.ts). Keeping the math here —
7
+ * not in run.ts (at the 700-LOC cap) — is deliberate.
8
+ *
9
+ * THE RIGINDEX WALL (spec §4.4, load-bearing): this module feeds routing +
10
+ * console surfaces ONLY. It must NEVER be imported by registry/build-leaderboard.ts
11
+ * or the matrix/Bradley-Terry path — production telemetry never produces rank
12
+ * (eval-methodology-and-rigindex.md:19). The import-guard test
13
+ * (health-rigindex-wall.test.ts) is the tripwire; do not weaken it.
14
+ */
15
+ import type { ProviderId } from "../types/provider";
16
+ /** Spec §4.1 example: below 20 samples, percentiles/error-rate surface as null. */
17
+ export declare const DEFAULT_HEALTH_MIN_N = 20;
18
+ export interface HealthPercentiles {
19
+ readonly p50: number;
20
+ readonly p90: number;
21
+ readonly p99: number;
22
+ }
23
+ /**
24
+ * Per-endpoint observed health over a rolling window. Percentiles + errorRate
25
+ * are `null` under the min-n gate (thin data must not read as a confident zero);
26
+ * `sampleN` is always reported so the surface can say "n too low", not "healthy".
27
+ */
28
+ export interface EndpointHealth {
29
+ readonly provider: ProviderId;
30
+ readonly model: string;
31
+ readonly windowStart: string;
32
+ readonly sampleN: number;
33
+ readonly latencyMs: HealthPercentiles | null;
34
+ readonly ttfbMs: HealthPercentiles | null;
35
+ readonly errorRate: number | null;
36
+ }
37
+ /**
38
+ * The minimal row shape the aggregation needs. Both the local SQLite store and
39
+ * the hosted rollup can project their rows onto this, so the math is identical
40
+ * on both lanes (spec §3.1).
41
+ */
42
+ export interface HealthInputRow {
43
+ readonly provider: string;
44
+ readonly model: string;
45
+ readonly ts: string;
46
+ readonly latency_ms: number;
47
+ readonly ttfb_ms: number | null;
48
+ readonly failure_class: string | null;
49
+ }
50
+ /**
51
+ * Nearest-rank percentile over a pre-sorted ascending array. Matches the
52
+ * convention already used by build-observed.ts so the two lanes agree.
53
+ */
54
+ export declare function percentile(sorted: readonly number[], p: number): number | null;
55
+ /**
56
+ * p50/p90/p99 over a set of values, dropping non-positive samples (a zero or
57
+ * negative latency/TTFB is a non-measurement, not a fast call). `null` when no
58
+ * positive sample remains.
59
+ */
60
+ export declare function computePercentiles(values: readonly number[]): HealthPercentiles | null;
61
+ export interface HealthWindowOptions {
62
+ readonly windowMinutes: number;
63
+ readonly minN: number;
64
+ /** Reference "now" in epoch ms — injected for deterministic windowing/tests. */
65
+ readonly now: number;
66
+ }
67
+ /**
68
+ * Aggregate windowed telemetry rows into per-(provider, model) health.
69
+ *
70
+ * Window: rows whose ts is within [now - windowMinutes, now]. Rows with an
71
+ * unparseable ts are dropped, never thrown on. Output is sorted by provider then
72
+ * model for deterministic rollups. Below `minN`, percentiles + errorRate are
73
+ * `null` (min-n gate) while `sampleN` is still reported.
74
+ */
75
+ export declare function aggregateEndpointHealth(rows: readonly HealthInputRow[], opts: HealthWindowOptions): EndpointHealth[];
76
+ //# sourceMappingURL=health.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"health.d.ts","sourceRoot":"","sources":["../../src/telemetry/health.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAEpD,mFAAmF;AACnF,eAAO,MAAM,oBAAoB,KAAK,CAAC;AAEvC,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;CACtB;AAED;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,QAAQ,EAAE,UAAU,CAAC;IAC9B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,EAAE,iBAAiB,GAAG,IAAI,CAAC;IAC7C,QAAQ,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI,CAAC;IAC1C,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CACnC;AAED;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;CACvC;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAI9E;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,iBAAiB,GAAG,IAAI,CAStF;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,gFAAgF;IAChF,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;CACtB;AAMD;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,SAAS,cAAc,EAAE,EAC/B,IAAI,EAAE,mBAAmB,GACxB,cAAc,EAAE,CAoClB"}
@@ -0,0 +1,92 @@
1
+ "use strict";
2
+ /**
3
+ * Observed-health aggregation — G-4 (observed-health/spec.md §3.1, §4.1).
4
+ *
5
+ * One home for the per-endpoint latency/TTFB percentile + error-rate + min-n
6
+ * math, shared by the live store reader (store.ts `readEndpointHealth`) and the
7
+ * observed-layer rollup (registry/build-observed.ts). Keeping the math here —
8
+ * not in run.ts (at the 700-LOC cap) — is deliberate.
9
+ *
10
+ * THE RIGINDEX WALL (spec §4.4, load-bearing): this module feeds routing +
11
+ * console surfaces ONLY. It must NEVER be imported by registry/build-leaderboard.ts
12
+ * or the matrix/Bradley-Terry path — production telemetry never produces rank
13
+ * (eval-methodology-and-rigindex.md:19). The import-guard test
14
+ * (health-rigindex-wall.test.ts) is the tripwire; do not weaken it.
15
+ */
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.DEFAULT_HEALTH_MIN_N = void 0;
18
+ exports.percentile = percentile;
19
+ exports.computePercentiles = computePercentiles;
20
+ exports.aggregateEndpointHealth = aggregateEndpointHealth;
21
+ /** Spec §4.1 example: below 20 samples, percentiles/error-rate surface as null. */
22
+ exports.DEFAULT_HEALTH_MIN_N = 20;
23
+ /**
24
+ * Nearest-rank percentile over a pre-sorted ascending array. Matches the
25
+ * convention already used by build-observed.ts so the two lanes agree.
26
+ */
27
+ function percentile(sorted, p) {
28
+ if (sorted.length === 0)
29
+ return null;
30
+ const index = Math.min(sorted.length - 1, Math.floor((p / 100) * sorted.length));
31
+ return sorted[index];
32
+ }
33
+ /**
34
+ * p50/p90/p99 over a set of values, dropping non-positive samples (a zero or
35
+ * negative latency/TTFB is a non-measurement, not a fast call). `null` when no
36
+ * positive sample remains.
37
+ */
38
+ function computePercentiles(values) {
39
+ const sorted = values.filter((v) => v > 0).sort((a, b) => a - b);
40
+ if (sorted.length === 0)
41
+ return null;
42
+ return {
43
+ // percentile() only returns null for an empty array, ruled out above.
44
+ p50: percentile(sorted, 50),
45
+ p90: percentile(sorted, 90),
46
+ p99: percentile(sorted, 99),
47
+ };
48
+ }
49
+ // Grouping-key separator: neither a ProviderId nor a model id contains a pipe,
50
+ // so the split back into (provider, model) is unambiguous.
51
+ const KEY_SEP = "|";
52
+ /**
53
+ * Aggregate windowed telemetry rows into per-(provider, model) health.
54
+ *
55
+ * Window: rows whose ts is within [now - windowMinutes, now]. Rows with an
56
+ * unparseable ts are dropped, never thrown on. Output is sorted by provider then
57
+ * model for deterministic rollups. Below `minN`, percentiles + errorRate are
58
+ * `null` (min-n gate) while `sampleN` is still reported.
59
+ */
60
+ function aggregateEndpointHealth(rows, opts) {
61
+ const windowStartMs = opts.now - opts.windowMinutes * 60000;
62
+ const windowStart = new Date(windowStartMs).toISOString();
63
+ const byEndpoint = new Map();
64
+ for (const row of rows) {
65
+ const t = Date.parse(row.ts);
66
+ if (Number.isNaN(t) || t < windowStartMs || t > opts.now)
67
+ continue;
68
+ const key = `${row.provider}${KEY_SEP}${row.model}`;
69
+ const list = byEndpoint.get(key) ?? [];
70
+ list.push(row);
71
+ byEndpoint.set(key, list);
72
+ }
73
+ const out = [];
74
+ for (const [key, group] of byEndpoint) {
75
+ const [provider, model] = key.split(KEY_SEP);
76
+ const sampleN = group.length;
77
+ const gated = sampleN < opts.minN;
78
+ const errorCount = group.filter((r) => r.failure_class !== null).length;
79
+ out.push({
80
+ provider: provider,
81
+ model,
82
+ windowStart,
83
+ sampleN,
84
+ latencyMs: gated ? null : computePercentiles(group.map((r) => r.latency_ms)),
85
+ ttfbMs: gated
86
+ ? null
87
+ : computePercentiles(group.map((r) => r.ttfb_ms).filter((t) => t !== null)),
88
+ errorRate: gated ? null : errorCount / sampleN,
89
+ });
90
+ }
91
+ return out.sort((a, b) => a.provider === b.provider ? a.model.localeCompare(b.model) : a.provider.localeCompare(b.provider));
92
+ }