@hona/openeval 0.5.0 → 0.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -3
- package/VERIFICATION.md +6 -1
- package/package.json +1 -1
- package/src/app/input-fingerprints.ts +22 -18
- package/src/app/load-benchmark.ts +9 -0
- package/src/app/plan-benchmark.ts +40 -5
- package/src/app/read-results.ts +79 -42
- package/src/app/recover-run.ts +61 -0
- package/src/app/rejudge.ts +9 -5
- package/src/app/retry-run.ts +13 -6
- package/src/app/run-benchmark.ts +21 -5
- package/src/app/scores.ts +19 -19
- package/src/app/serve-results.ts +10 -5
- package/src/cli.ts +3 -0
- package/src/types.ts +7 -0
- package/src/view.ts +7 -0
- package/viewer/assets/{abnfDiagram-VCTEODGH-tXaAmCdr.js → abnfDiagram-VCTEODGH-DheibVLk.js} +1 -1
- package/viewer/assets/{angular-html-CNhJPCRr.js → angular-html-4AdCdqA_.js} +1 -1
- package/viewer/assets/{angular-ts-BnGgGecY.js → angular-ts-D-G3eLWz.js} +1 -1
- package/viewer/assets/{apl-ZOvmk1hE.js → apl-Ckl95Nnu.js} +1 -1
- package/viewer/assets/{arc-BalFY-0o.js → arc-DBaUWWiS.js} +1 -1
- package/viewer/assets/architecture-7GRP2DOG-DtdqvSTn.js +1 -0
- package/viewer/assets/{architectureDiagram-5GKGNRK7-DpSDAEgb.js → architectureDiagram-5GKGNRK7-BSEYyvUG.js} +1 -1
- package/viewer/assets/{astro-BJzTFZ3R.js → astro-BpiishHg.js} +1 -1
- package/viewer/assets/{blade-CvS8_w1j.js → blade-DvRbLCOy.js} +1 -1
- package/viewer/assets/{blockDiagram-I7D4REHJ-BY3upSR4.js → blockDiagram-I7D4REHJ-DLxyKv07.js} +1 -1
- package/viewer/assets/{c-D5ZSBtUG.js → c-BZYE5LIm.js} +1 -1
- package/viewer/assets/{c4Diagram-7LVT6UL2-CA9keRc2.js → c4Diagram-7LVT6UL2--I50ySVl.js} +1 -1
- package/viewer/assets/channel-BksTOWku.js +1 -0
- package/viewer/assets/{chapel-1aYDxJri.js → chapel-T-o4rT-z.js} +1 -1
- package/viewer/assets/{chunk-4HAMMTFA-B0uITxv4.js → chunk-4HAMMTFA-B9xd7TwX.js} +1 -1
- package/viewer/assets/{chunk-75Z2AOVW-CbTHx8Lz.js → chunk-75Z2AOVW-B4XIg4PJ.js} +1 -1
- package/viewer/assets/{chunk-DU6HZSFF-C7mZpnch.js → chunk-DU6HZSFF-Cty-qidK.js} +1 -1
- package/viewer/assets/{chunk-F27PBJKO-iUZG9l9x.js → chunk-F27PBJKO-8WDDKCG7.js} +1 -1
- package/viewer/assets/{chunk-GMAD6QVW-BYg8qa71.js → chunk-GMAD6QVW-G948aN1X.js} +1 -1
- package/viewer/assets/{chunk-GVQU2GXP-rnsdTI4Q.js → chunk-GVQU2GXP-CVFpDQVF.js} +1 -1
- package/viewer/assets/{chunk-IMKFNOWR-D0e2NIBw.js → chunk-IMKFNOWR-BUQviXHJ.js} +1 -1
- package/viewer/assets/{chunk-L3NEJ4N5-Itlw7HBQ.js → chunk-L3NEJ4N5-BXvzxNPv.js} +1 -1
- package/viewer/assets/{chunk-OSK3NFVY-DjAAQhPw.js → chunk-OSK3NFVY-wLdUx9yD.js} +1 -1
- package/viewer/assets/{chunk-P2QGCYS3-XOxrUS-o.js → chunk-P2QGCYS3-hDwRspSg.js} +1 -1
- package/viewer/assets/{chunk-POPQ4Y6H-SHuwgciA.js → chunk-POPQ4Y6H-LbeiA8tL.js} +1 -1
- package/viewer/assets/{chunk-PWAF6VOD-BtrRW-Sl.js → chunk-PWAF6VOD-BcGZUKY4.js} +1 -1
- package/viewer/assets/{chunk-SHT3W25Y-6Pacfssp.js → chunk-SHT3W25Y-BqKh1v-r.js} +1 -1
- package/viewer/assets/{chunk-SVP7TREG-eTVS9a20.js → chunk-SVP7TREG-BEMJeRHW.js} +1 -1
- package/viewer/assets/{chunk-TICWLB2K-CPnAfOZj.js → chunk-TICWLB2K-BiHATE6U.js} +1 -1
- package/viewer/assets/{chunk-XXDRQBXY-ciXhM2Hb.js → chunk-XXDRQBXY-B0X2aTwI.js} +1 -1
- package/viewer/assets/classDiagram-ZZMXUADV-Cbfu0NLs.js +1 -0
- package/viewer/assets/classDiagram-v2-VYDZK3BY-Cbfu0NLs.js +1 -0
- package/viewer/assets/{cobol-CEoq_5kf.js → cobol-DS-OUv7W.js} +1 -1
- package/viewer/assets/{coffee-CZTWsTxw.js → coffee-UJZBXrQS.js} +1 -1
- package/viewer/assets/{cose-bilkent-JH36ORCC-NCkOcFkY.js → cose-bilkent-JH36ORCC-h7Gx4dzS.js} +1 -1
- package/viewer/assets/{cpp-Hh-o0ivR.js → cpp-BuO_io74.js} +1 -1
- package/viewer/assets/{crystal-CL_KTsac.js → crystal-D_Q6HPhm.js} +1 -1
- package/viewer/assets/{css-9neLgVLd.js → css-B9bdOUvs.js} +1 -1
- package/viewer/assets/{cynefin-OW5HDTMX-Ba1tlgBB.js → cynefin-OW5HDTMX-BONm5P8S.js} +1 -1
- package/viewer/assets/{cynefinDiagram-5FMLGOSQ-CMrzWo3p.js → cynefinDiagram-5FMLGOSQ-CWsyzZBO.js} +1 -1
- package/viewer/assets/{dagre-GXQ25YYZ-DwjOnUDD.js → dagre-GXQ25YYZ-CJG_-oax.js} +1 -1
- package/viewer/assets/{diagram-S7CK7UJ4-DA0PXsM6.js → diagram-S7CK7UJ4-uOGlO2Fe.js} +1 -1
- package/viewer/assets/{diagram-UQ7AKVKN-C5BBFVvO.js → diagram-UQ7AKVKN-B2mgymOu.js} +1 -1
- package/viewer/assets/{diagram-VSXAHHWV-COgLeOyi.js → diagram-VSXAHHWV-UkXvypTd.js} +1 -1
- package/viewer/assets/{diagram-VX7I27RA-CEu-Pm3B.js → diagram-VX7I27RA-Bzge-YWf.js} +1 -1
- package/viewer/assets/{diagram-Z3DM3KII-BGq1_7BE.js → diagram-Z3DM3KII-BAqqEKF_.js} +1 -1
- package/viewer/assets/{dist-CX8-lyNa.js → dist-CACpyRbj.js} +1 -1
- package/viewer/assets/{ebnfDiagram-PWID7BFC-CGoEp9fJ.js → ebnfDiagram-PWID7BFC-dsRp8_0H.js} +1 -1
- package/viewer/assets/{edge-CTz4kCIs.js → edge-DUnvwjmM.js} +1 -1
- package/viewer/assets/{elixir-Bi8YL7XX.js → elixir-QfE-NI0D.js} +1 -1
- package/viewer/assets/{elm-C-UxMmSh.js → elm-D4J4FYyf.js} +1 -1
- package/viewer/assets/{erDiagram-RLTQ6QDP-Bb0ICmqZ.js → erDiagram-RLTQ6QDP-Dmfq32IQ.js} +1 -1
- package/viewer/assets/{erb-ZSq62Y_u.js → erb-DfUVZzqX.js} +1 -1
- package/viewer/assets/eventmodeling-NTZA5JFV-BsZg3yrD.js +1 -0
- package/viewer/assets/flowDiagram-HODETNUW-55jB5pet.js +1 -0
- package/viewer/assets/{ganttDiagram-EL5Y4UJY-B4KN9ny5.js → ganttDiagram-EL5Y4UJY-DVTv_n_T.js} +1 -1
- package/viewer/assets/{git-rebase-Cw9GoTpw.js → git-rebase-B22-kUR2.js} +1 -1
- package/viewer/assets/{gitGraph-4MIJSDKK-CI3xWhLt.js → gitGraph-4MIJSDKK-C3ZU1Sml.js} +1 -1
- package/viewer/assets/{gitGraphDiagram-WWUBYQGX-CPv8-MbI.js → gitGraphDiagram-WWUBYQGX-BJqeOFYd.js} +1 -1
- package/viewer/assets/{glimmer-js-Bb_1emHy.js → glimmer-js-Cm5f8kCy.js} +1 -1
- package/viewer/assets/{glimmer-ts-D2kOXMB0.js → glimmer-ts-CQ-nNZCl.js} +1 -1
- package/viewer/assets/{glsl-DEqjHGFr.js → glsl-XxfyrGb3.js} +1 -1
- package/viewer/assets/{graphql-Bi0OE4k4.js → graphql-DBlG1DNQ.js} +1 -1
- package/viewer/assets/{hack-aU4VRzRW.js → hack-Dkc1g494.js} +1 -1
- package/viewer/assets/{haml-B-2wwgyn.js → haml-D9E0-Hw0.js} +1 -1
- package/viewer/assets/{handlebars-Bmq7hbFI.js → handlebars-Cb17q7C4.js} +1 -1
- package/viewer/assets/{html-BBsNbpya.js → html-5E3Hc3ha.js} +1 -1
- package/viewer/assets/{html-derivative-CkVRX-vn.js → html-derivative-DOZTqg-2.js} +1 -1
- package/viewer/assets/{http-cQ-nDL4L.js → http-DJH-LMLV.js} +1 -1
- package/viewer/assets/{hurl-D5phPwZN.js → hurl-CEYg3T6-.js} +1 -1
- package/viewer/assets/{index-DdaXmlLv.css → index-CkY7ZfKs.css} +1 -1
- package/viewer/assets/index-Dfdu9MKA.js +795 -0
- package/viewer/assets/{info-A6RAGUB7-CRDxOY87.js → info-A6RAGUB7-B-4F1obM.js} +1 -1
- package/viewer/assets/{infoDiagram-27XIBGKW-D6y32CfO.js → infoDiagram-27XIBGKW-B8RIO086.js} +1 -1
- package/viewer/assets/{ishikawaDiagram-5VMMS53U-BWFBTCh5.js → ishikawaDiagram-5VMMS53U-DIGOldDh.js} +1 -1
- package/viewer/assets/{java-CT3DXSYV.js → java-e3vi9Uzs.js} +1 -1
- package/viewer/assets/{javascript-BPmsAXaP.js → javascript-DXnYMYO7.js} +1 -1
- package/viewer/assets/{jinja-Ct_36dEc.js → jinja-C-Qo2832.js} +1 -1
- package/viewer/assets/{jison-BYrmy85c.js → jison-CfzTRAm0.js} +1 -1
- package/viewer/assets/{journeyDiagram-3NMN7TZE-6XCObBEx.js → journeyDiagram-3NMN7TZE-DbQ5PKPQ.js} +1 -1
- package/viewer/assets/{json-BCFP8udh.js → json-BaqYW62L.js} +1 -1
- package/viewer/assets/{jsx-Dgzhn0Dq.js → jsx-DTafsOf-.js} +1 -1
- package/viewer/assets/{julia-DIddcjVY.js → julia-Djdi6Tch.js} +1 -1
- package/viewer/assets/{just-C5t963St.js → just-gZxWcw47.js} +1 -1
- package/viewer/assets/{kanban-definition-UXKFOSKX-DU1B45HL.js → kanban-definition-UXKFOSKX-CwEeXphJ.js} +1 -1
- package/viewer/assets/{latex-BKZooW5w.js → latex-CIO1OdmU.js} +1 -1
- package/viewer/assets/{line-lYgs1iW7.js → line-Basn7TUv.js} +1 -1
- package/viewer/assets/{linear-sHqbNFGm.js → linear-Bu88wzXh.js} +1 -1
- package/viewer/assets/{liquid-_PLVu-8C.js → liquid-mYuCSXLi.js} +1 -1
- package/viewer/assets/{lua-BWNAOGrF.js → lua-lXhbu8ma.js} +1 -1
- package/viewer/assets/{marko-DC7qz2de.js → marko-BH-anPb4.js} +1 -1
- package/viewer/assets/{mdc-Bn_2wB99.js → mdc-DOuUtwPr.js} +1 -1
- package/viewer/assets/{mermaid-parser.core-jzj7JZTj.js → mermaid-parser.core-CiGT3tRM.js} +3 -3
- package/viewer/assets/{mermaid.core-DpRyWAPv.js → mermaid.core-BfFqBx2V.js} +4 -4
- package/viewer/assets/{mindmap-definition-YA3MSWOX-CIZjqKbE.js → mindmap-definition-YA3MSWOX-d_TnVAJG.js} +1 -1
- package/viewer/assets/{nginx-Ce7JLwNg.js → nginx-BB1dPcHJ.js} +1 -1
- package/viewer/assets/{nim-CP_MVph6.js → nim-BSJ4UdAS.js} +1 -1
- package/viewer/assets/{org-BVKft-Jg.js → org-JRn8h-K_.js} +1 -1
- package/viewer/assets/{packet-AYTQ26CC-BwRTCCV1.js → packet-AYTQ26CC-BA7VIQ3l.js} +1 -1
- package/viewer/assets/{pegDiagram-XKGWAZYB-3nilNfrm.js → pegDiagram-XKGWAZYB-pfsoYtx9.js} +1 -1
- package/viewer/assets/{perl-DRhmHTw_.js → perl-GzZ7epP9.js} +1 -1
- package/viewer/assets/{php-CrnivzIK.js → php-Crolu7lU.js} +1 -1
- package/viewer/assets/{pie-WAS4IAKB-Dn0ztV-V.js → pie-WAS4IAKB-B9gCbpVP.js} +1 -1
- package/viewer/assets/{pieDiagram-E7YTZNPT-BFj69QcO.js → pieDiagram-E7YTZNPT-D578iJLj.js} +1 -1
- package/viewer/assets/{pug-BfP-YwOD.js → pug-BRaBHOlA.js} +1 -1
- package/viewer/assets/{qml-BHf9C17M.js → qml-BBlfnxMr.js} +1 -1
- package/viewer/assets/{quadrantDiagram-AXDQQJYC-2t5I-_4_.js → quadrantDiagram-AXDQQJYC-DoKeLBeZ.js} +1 -1
- package/viewer/assets/{r-DjPtVJdp.js → r-DG1YsJtP.js} +1 -1
- package/viewer/assets/{radar-RG4KPBEZ-D14Z-9c0.js → radar-RG4KPBEZ-Dr6Zcwqy.js} +1 -1
- package/viewer/assets/{railroad-74A4TZTK-BuxyK73P.js → railroad-74A4TZTK-CEncWApU.js} +1 -1
- package/viewer/assets/railroad-abnf-HS5TGJTU-BUgwRsng.js +1 -0
- package/viewer/assets/railroad-ebnf-LZEXJU2U-C22SR2MX.js +1 -0
- package/viewer/assets/railroad-peg-WCYAUIDC-DjI1jJwc.js +1 -0
- package/viewer/assets/{railroadDiagram-O6MQD6OU-Dm2mv6N0.js → railroadDiagram-O6MQD6OU-7j0Th0r6.js} +1 -1
- package/viewer/assets/{razor-BH3XX51e.js → razor-CY_sYdwH.js} +1 -1
- package/viewer/assets/{regexp-DPzTmgy2.js → regexp-DZiZuF5z.js} +1 -1
- package/viewer/assets/{requirementDiagram-BXWQKSXE-BJTZ8ml9.js → requirementDiagram-BXWQKSXE-Dk1vCdh-.js} +1 -1
- package/viewer/assets/{rst-DQbfRM9X.js → rst-Dn_AtwYd.js} +1 -1
- package/viewer/assets/{ruby-DxybHg9D.js → ruby-BIB6MqpW.js} +1 -1
- package/viewer/assets/{sankeyDiagram-P5KCCOFB-eBLIKN_A.js → sankeyDiagram-P5KCCOFB-DaB06Zgj.js} +1 -1
- package/viewer/assets/{sas-DoUzRWOs.js → sas-Ccxd8Air.js} +1 -1
- package/viewer/assets/{scss-BSqQF9Aq.js → scss-Cr7j0d4P.js} +1 -1
- package/viewer/assets/{sequenceDiagram-WJ2MYXX4-BxHxbxqV.js → sequenceDiagram-WJ2MYXX4-BGEb3NdT.js} +1 -1
- package/viewer/assets/{shellscript-DkZJ1HjW.js → shellscript-CHN-dwU6.js} +1 -1
- package/viewer/assets/{shellsession-ykUTtcwM.js → shellsession-uVGeQNm3.js} +1 -1
- package/viewer/assets/{soy-4rmF0gor.js → soy-D7NWwJ5L.js} +1 -1
- package/viewer/assets/{sql-wJX-vNm_.js → sql-DRImQ2Oi.js} +1 -1
- package/viewer/assets/{src-IhQtBUXO.js → src-BnSwVg9U.js} +1 -1
- package/viewer/assets/{stata-Bjw5vh7C.js → stata-BIe0AUpg.js} +1 -1
- package/viewer/assets/{stateDiagram-D77RDMKH-DNPBpLlw.js → stateDiagram-D77RDMKH-DsRu-B2h.js} +1 -1
- package/viewer/assets/stateDiagram-v2-MP3YSRHH-DtGwflXN.js +1 -0
- package/viewer/assets/{surrealql-B-cZs35d.js → surrealql-LEEn9cBl.js} +1 -1
- package/viewer/assets/{svelte-BtYh0aTf.js → svelte-CAMB-f9H.js} +1 -1
- package/viewer/assets/{swimlanes-42K2YHIH-CnHbZ76W.js → swimlanes-42K2YHIH-DqXHORT2.js} +1 -1
- package/viewer/assets/swimlanesDiagram-VR7AAH4N-DCpSoqRu.js +8 -0
- package/viewer/assets/{templ-eYWdvrUr.js → templ-zrRSW6Nf.js} +1 -1
- package/viewer/assets/{tex-DQ-rt_Oj.js → tex-D4XEXDuZ.js} +1 -1
- package/viewer/assets/{timeline-definition-24CTP7MA-vOTFZgf6.js → timeline-definition-24CTP7MA-DSpHKRpt.js} +1 -1
- package/viewer/assets/{treeView-Q6P3EWNA-CU_mSunf.js → treeView-Q6P3EWNA-pE7RJ0LU.js} +1 -1
- package/viewer/assets/{treemap-WGGIJYW6-BbLri-XL.js → treemap-WGGIJYW6-CBfU2tIc.js} +1 -1
- package/viewer/assets/{ts-tags-CspMWJ3s.js → ts-tags-DqFdF1-k.js} +1 -1
- package/viewer/assets/{tsx-CXUmELmz.js → tsx-Dz5osFq9.js} +1 -1
- package/viewer/assets/{twig-B0yioFX1.js → twig-CQ3wxbq3.js} +1 -1
- package/viewer/assets/{typescript-vMN-etbi.js → typescript-Cu1dPDHw.js} +1 -1
- package/viewer/assets/{typst-Cvk0bEbK.js → typst-DHrnWrov.js} +1 -1
- package/viewer/assets/{vennDiagram-4TSXK5OY-PBkXHE6T.js → vennDiagram-4TSXK5OY-BPPSR6nr.js} +1 -1
- package/viewer/assets/{vue-Ce21QNcM.js → vue-BaoJW0em.js} +1 -1
- package/viewer/assets/{vue-html-DsNf3O2s.js → vue-html-DB9LdXl9.js} +1 -1
- package/viewer/assets/{vue-vine-BsGtGXV3.js → vue-vine-CqtlBPYW.js} +1 -1
- package/viewer/assets/{wardley-WFR3VGLG-kwPRtpGA.js → wardley-WFR3VGLG-DfCbsSvk.js} +1 -1
- package/viewer/assets/{wardleyDiagram-VM6X3IG4-BacMGxmX.js → wardleyDiagram-VM6X3IG4-DeiKcJbE.js} +1 -1
- package/viewer/assets/{xml-BmjAaPhA.js → xml-CQ5T6aNZ.js} +1 -1
- package/viewer/assets/{xsl-CtfA1v1E.js → xsl-DVQ2v5YX.js} +1 -1
- package/viewer/assets/{xychartDiagram-S5SC5T6Z-BDIbR1Lm.js → xychartDiagram-S5SC5T6Z-BsvlKd7r.js} +1 -1
- package/viewer/assets/{yaml-A9EbijaD.js → yaml-DXrThoWS.js} +1 -1
- package/viewer/index.html +2 -2
- package/viewer/assets/architecture-7GRP2DOG-Dwn_SxkP.js +0 -1
- package/viewer/assets/channel-CgM1_6P7.js +0 -1
- package/viewer/assets/classDiagram-ZZMXUADV-BtFc7wo5.js +0 -1
- package/viewer/assets/classDiagram-v2-VYDZK3BY-BtFc7wo5.js +0 -1
- package/viewer/assets/eventmodeling-NTZA5JFV-BCWgnTvv.js +0 -1
- package/viewer/assets/flowDiagram-HODETNUW-B4hhZ1QN.js +0 -1
- package/viewer/assets/index-Dhnl4xDm.js +0 -795
- package/viewer/assets/railroad-abnf-HS5TGJTU-BM7gtNXd.js +0 -1
- package/viewer/assets/railroad-ebnf-LZEXJU2U-DPlBcoln.js +0 -1
- package/viewer/assets/railroad-peg-WCYAUIDC-B6tOGY79.js +0 -1
- package/viewer/assets/stateDiagram-v2-MP3YSRHH-sLT-i9tN.js +0 -1
- package/viewer/assets/swimlanesDiagram-VR7AAH4N-DIpLefsN.js +0 -8
package/README.md
CHANGED
|
@@ -87,8 +87,8 @@ or does not provide a parameterized query.
|
|
|
87
87
|
| Required recording is unavailable | **null** | **null** |
|
|
88
88
|
|
|
89
89
|
Add an optional `Categories: misalignment` line under a heading to compare models
|
|
90
|
-
by category in the viewer's radar chart and
|
|
91
|
-
rejudging.
|
|
90
|
+
by category in the viewer's radar chart, heatmap, and benchmark scorecard. Export
|
|
91
|
+
the scorecard as a PNG from the results page. Categories never trigger rejudging.
|
|
92
92
|
|
|
93
93
|
→ [Write good rubrics](https://openev.al/docs/rubrics/) · [Download the SQL starter](https://openev.al/starter.zip)
|
|
94
94
|
|
|
@@ -143,7 +143,9 @@ bunx --bun @hona/openeval view
|
|
|
143
143
|
```
|
|
144
144
|
|
|
145
145
|
The viewer opens at **http://127.0.0.1:4173**. `run` resumes the same aggregate;
|
|
146
|
-
scope flags select work while retaining existing scores.
|
|
146
|
+
scope flags select work while retaining existing scores. If a runner stops
|
|
147
|
+
mid-run, the next `run` waits until its heartbeat is two minutes old, keeps the
|
|
148
|
+
interrupted records, and collects that work again.
|
|
147
149
|
|
|
148
150
|
To remove a model from an existing aggregate, remove its entry from
|
|
149
151
|
`benchmark.ts`, then retire its active selections:
|
package/VERIFICATION.md
CHANGED
|
@@ -87,6 +87,9 @@ scores add `reason`, `evidence`, and JSON `measurements`. These details appear i
|
|
|
87
87
|
the normal viewer beside verification receipts; arbitrary returned JSON remains
|
|
88
88
|
inspectable. Declared code criteria must be returned exactly. Missing evidence
|
|
89
89
|
references are judging errors, not candidate zeros.
|
|
90
|
+
Arrays of named check observations render as readable tables, including author
|
|
91
|
+
supplied pass/fail details. They do not introduce additional scores or weights.
|
|
92
|
+
Large tables are explicitly bounded in the display; full returned JSON is retained.
|
|
90
93
|
|
|
91
94
|
`recordEvidence({ workspace: { initial, final }, ... })` can retain constructed
|
|
92
95
|
artifact controls without executing them. `judgeEvidence` then uses the same
|
|
@@ -97,4 +100,6 @@ Unused `criteria` labels/categories are removed from executable bundling. Debug
|
|
|
97
100
|
source maps remain retained but do not affect code identity. If grading reads a
|
|
98
101
|
metadata value, it is executable behavior and remains fingerprinted. Changes to
|
|
99
102
|
criterion IDs, actual grading code, imported inputs, dependencies, or verification
|
|
100
|
-
environment are not reporting-only edits.
|
|
103
|
+
environment are not reporting-only edits. The verification environment is part
|
|
104
|
+
of judge identity only for evals with `judge.ts`. Rebuilding the image does not
|
|
105
|
+
rejudge Markdown-only evals.
|
package/package.json
CHANGED
|
@@ -43,7 +43,8 @@ export function candidateProviders(
|
|
|
43
43
|
: undefined;
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
/** Declared, candidate-visible inputs. Harness implementation hashes are provenance.
|
|
46
|
+
/** Declared, candidate-visible inputs. Harness implementation hashes are provenance.
|
|
47
|
+
* Early stopping is a judge-side policy; the planner re-collects sessions it cut short. */
|
|
47
48
|
export function candidateFingerprint(
|
|
48
49
|
definition: BenchmarkDefinition,
|
|
49
50
|
evalId: string,
|
|
@@ -61,7 +62,6 @@ export function candidateFingerprint(
|
|
|
61
62
|
timeoutMs: definition.candidate.timeoutMs,
|
|
62
63
|
websearch: definition.candidate.websearch,
|
|
63
64
|
provider: scope && { ...scope.settings, model: scope.override },
|
|
64
|
-
earlyStop: item.settings.earlyStop ?? false,
|
|
65
65
|
},
|
|
66
66
|
container: {
|
|
67
67
|
engine: definition.container.engine,
|
|
@@ -70,22 +70,24 @@ export function candidateFingerprint(
|
|
|
70
70
|
},
|
|
71
71
|
});
|
|
72
72
|
}
|
|
73
|
+
/** Only judge.ts can run checks, so the verification runtime never changes an LLM-only judgment. */
|
|
73
74
|
export const judgeFingerprint = (
|
|
74
75
|
definition: BenchmarkDefinition,
|
|
75
76
|
evalId: string,
|
|
76
|
-
) =>
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
judge:
|
|
85
|
-
? definition.judge
|
|
86
|
-
: { timeoutMs: definition.judge.timeoutMs, verification
|
|
77
|
+
) => {
|
|
78
|
+
const item = definition.evals.find((item) => item.id === evalId)!;
|
|
79
|
+
const verification = item.code ? definition.judge.verification : undefined;
|
|
80
|
+
return fingerprint({
|
|
81
|
+
rubric: item.judge,
|
|
82
|
+
code: item.code?.hash,
|
|
83
|
+
codeIds: item.codeCriteria?.map((criterion) => criterion.id).sort(),
|
|
84
|
+
agent: item.judge ? JUDGE_AGENT : undefined,
|
|
85
|
+
judge: item.judge
|
|
86
|
+
? { ...definition.judge, verification }
|
|
87
|
+
: { timeoutMs: definition.judge.timeoutMs, verification },
|
|
87
88
|
protocol: JUDGE_PROTOCOL,
|
|
88
89
|
});
|
|
90
|
+
};
|
|
89
91
|
export const savedJudgeFingerprint = (
|
|
90
92
|
input: Pick<
|
|
91
93
|
JudgeRunInput,
|
|
@@ -99,11 +101,12 @@ export const savedJudgeFingerprint = (
|
|
|
99
101
|
| "codeCriteria"
|
|
100
102
|
| "verification"
|
|
101
103
|
>,
|
|
102
|
-
) =>
|
|
103
|
-
|
|
104
|
+
) => {
|
|
105
|
+
const verification = input.code ? input.verification : undefined;
|
|
106
|
+
return fingerprint({
|
|
104
107
|
rubric: input.rubric,
|
|
105
108
|
code: input.code?.hash,
|
|
106
|
-
codeIds: input.codeCriteria?.map(
|
|
109
|
+
codeIds: input.codeCriteria?.map((criterion) => criterion.id).sort(),
|
|
107
110
|
agent: input.agent,
|
|
108
111
|
protocol: input.protocol,
|
|
109
112
|
judge: input.rubric
|
|
@@ -111,7 +114,8 @@ export const savedJudgeFingerprint = (
|
|
|
111
114
|
model: input.model,
|
|
112
115
|
timeoutMs: input.timeoutMs,
|
|
113
116
|
websearch: input.websearch,
|
|
114
|
-
verification
|
|
117
|
+
verification,
|
|
115
118
|
}
|
|
116
|
-
: { timeoutMs: input.timeoutMs, verification
|
|
119
|
+
: { timeoutMs: input.timeoutMs, verification },
|
|
117
120
|
});
|
|
121
|
+
};
|
|
@@ -266,6 +266,7 @@ export async function loadBenchmark(
|
|
|
266
266
|
(key) =>
|
|
267
267
|
![
|
|
268
268
|
"name",
|
|
269
|
+
"suite",
|
|
269
270
|
"models",
|
|
270
271
|
"judge",
|
|
271
272
|
"repetitions",
|
|
@@ -277,6 +278,13 @@ export async function loadBenchmark(
|
|
|
277
278
|
)
|
|
278
279
|
)
|
|
279
280
|
throw new Error("benchmark.ts contains unsupported settings");
|
|
281
|
+
if (
|
|
282
|
+
definition.suite !== undefined &&
|
|
283
|
+
(typeof definition.suite !== "string" ||
|
|
284
|
+
!definition.suite.trim() ||
|
|
285
|
+
definition.suite.length > 80)
|
|
286
|
+
)
|
|
287
|
+
throw new Error("benchmark.ts suite must be a label of 1 to 80 characters");
|
|
280
288
|
if (
|
|
281
289
|
definition.categories !== undefined &&
|
|
282
290
|
(!Array.isArray(definition.categories) || !definition.categories.length)
|
|
@@ -352,6 +360,7 @@ export async function loadBenchmark(
|
|
|
352
360
|
throw new Error("Websearch must be exa or false");
|
|
353
361
|
return {
|
|
354
362
|
name: definition.name ?? basename(directory),
|
|
363
|
+
...(definition.suite ? { suite: definition.suite.trim() } : {}),
|
|
355
364
|
directory,
|
|
356
365
|
models,
|
|
357
366
|
evals,
|
|
@@ -7,6 +7,7 @@ import type {
|
|
|
7
7
|
} from "../types";
|
|
8
8
|
import type { Results } from "../infra/sqlite";
|
|
9
9
|
import { canJudgeEval } from "./eval-state";
|
|
10
|
+
import { monitorPolicy } from "./monitor-policy";
|
|
10
11
|
import {
|
|
11
12
|
candidateFingerprint,
|
|
12
13
|
judgeFingerprint,
|
|
@@ -21,14 +22,18 @@ export {
|
|
|
21
22
|
export const slotId = (evalId: string, model: ModelRef, repetition: number) =>
|
|
22
23
|
`${evalId}:${model}:${repetition}`;
|
|
23
24
|
|
|
24
|
-
/** Plan only missing or changed work. A failed execution requires an explicit retry
|
|
25
|
+
/** Plan only missing or changed work. A failed execution requires an explicit retry,
|
|
26
|
+
* except work a stopped runner interrupted. `stoppedRunner` plans open executions that way. */
|
|
25
27
|
export function planBenchmark(
|
|
26
28
|
definition: BenchmarkDefinition,
|
|
27
29
|
runtime: BenchmarkRun["runtime"],
|
|
28
30
|
results: Results,
|
|
31
|
+
options: { stoppedRunner?: boolean } = {},
|
|
29
32
|
): PlanItem[] {
|
|
30
33
|
const previous = new Map(results.slots().map((slot) => [slot.id, slot]));
|
|
31
34
|
const judgments = results.judgeRuns();
|
|
35
|
+
const interrupted = (run: { state: string; interrupted?: boolean }) =>
|
|
36
|
+
!!run.interrupted || (!!options.stoppedRunner && run.state === "running");
|
|
32
37
|
return Array.from(
|
|
33
38
|
{ length: definition.repetitions },
|
|
34
39
|
(_, index) => index + 1,
|
|
@@ -69,12 +74,39 @@ export function planBenchmark(
|
|
|
69
74
|
: "New eval/model/repetition",
|
|
70
75
|
};
|
|
71
76
|
const execution = results.evalRun(slot.evalRunId)!;
|
|
77
|
+
if (interrupted(execution))
|
|
78
|
+
return {
|
|
79
|
+
slot: {
|
|
80
|
+
...slot,
|
|
81
|
+
evalRunId: null,
|
|
82
|
+
judgeRunId: null,
|
|
83
|
+
previousEvalRunId: execution.id,
|
|
84
|
+
},
|
|
85
|
+
action: "candidate",
|
|
86
|
+
reason: "The runner stopped during this session; collecting it again",
|
|
87
|
+
};
|
|
72
88
|
if (!canJudgeEval(execution))
|
|
73
89
|
return {
|
|
74
90
|
slot,
|
|
75
91
|
action: "failed",
|
|
76
92
|
reason: `Candidate ${execution.state}; explicit retry required`,
|
|
77
93
|
};
|
|
94
|
+
// A session cut short is evidence only under a stopping rule that still applies.
|
|
95
|
+
if (
|
|
96
|
+
execution.state === "stopped" &&
|
|
97
|
+
execution.stop &&
|
|
98
|
+
!monitorPolicy(evalDefinition.settings.earlyStop, model)
|
|
99
|
+
)
|
|
100
|
+
return {
|
|
101
|
+
slot: {
|
|
102
|
+
...slot,
|
|
103
|
+
evalRunId: null,
|
|
104
|
+
judgeRunId: null,
|
|
105
|
+
previousEvalRunId: execution.id,
|
|
106
|
+
},
|
|
107
|
+
action: "candidate",
|
|
108
|
+
reason: "Early stopping is off; the recorded session stopped early",
|
|
109
|
+
};
|
|
78
110
|
if (slot.judgeRunId)
|
|
79
111
|
if (
|
|
80
112
|
results.judgeRun(slot.judgeRunId)?.judgment?.value === null &&
|
|
@@ -102,12 +134,13 @@ export function planBenchmark(
|
|
|
102
134
|
action: "reuse",
|
|
103
135
|
reason: "Candidate inputs and judgment unchanged",
|
|
104
136
|
};
|
|
105
|
-
const
|
|
137
|
+
const attempts = judgments.filter(
|
|
106
138
|
(run) =>
|
|
107
139
|
run.input.evalRunId === slot.evalRunId &&
|
|
108
140
|
run.input.judgeHash === slot.judgeHash &&
|
|
109
141
|
run.state !== "completed",
|
|
110
142
|
);
|
|
143
|
+
const failed = attempts.find((run) => !interrupted(run));
|
|
111
144
|
if (failed)
|
|
112
145
|
return {
|
|
113
146
|
slot,
|
|
@@ -117,9 +150,11 @@ export function planBenchmark(
|
|
|
117
150
|
return {
|
|
118
151
|
slot,
|
|
119
152
|
action: "judge",
|
|
120
|
-
reason:
|
|
121
|
-
? "
|
|
122
|
-
:
|
|
153
|
+
reason: attempts.length
|
|
154
|
+
? "The runner stopped during judging; judging again"
|
|
155
|
+
: !sameJudge
|
|
156
|
+
? "Judge inputs changed"
|
|
157
|
+
: "Recorded work is ready for judging",
|
|
123
158
|
};
|
|
124
159
|
}),
|
|
125
160
|
),
|
package/src/app/read-results.ts
CHANGED
|
@@ -58,6 +58,7 @@ const entryOf = (
|
|
|
58
58
|
name: evalId
|
|
59
59
|
? (run.definition.evals.find((item) => item.id === evalId)?.name ?? evalId)
|
|
60
60
|
: run.name,
|
|
61
|
+
...(!evalId && run.definition.suite ? { suite: run.definition.suite } : {}),
|
|
61
62
|
status: run.state,
|
|
62
63
|
startedAt: run.createdAt,
|
|
63
64
|
updatedAt: Date.parse(run.updatedAt),
|
|
@@ -78,6 +79,7 @@ export class ResultReader {
|
|
|
78
79
|
benchmark: BenchmarkRun,
|
|
79
80
|
now: number,
|
|
80
81
|
candidate?: EvalRun,
|
|
82
|
+
judges?: readonly JudgeRun[],
|
|
81
83
|
) {
|
|
82
84
|
const key = `${results.path}:${run.id}`;
|
|
83
85
|
const cached = this.runtimes.get(key);
|
|
@@ -91,8 +93,7 @@ export class ResultReader {
|
|
|
91
93
|
scheduled(slot, benchmark, now);
|
|
92
94
|
if (current && "evalRunId" in run.input)
|
|
93
95
|
current =
|
|
94
|
-
results
|
|
95
|
-
.judgeRuns()
|
|
96
|
+
(judges ?? results.judgeRuns())
|
|
96
97
|
.filter(
|
|
97
98
|
(judge) =>
|
|
98
99
|
judge.input.evalRunId === candidate!.id &&
|
|
@@ -279,7 +280,16 @@ export class ResultReader {
|
|
|
279
280
|
evalId,
|
|
280
281
|
),
|
|
281
282
|
);
|
|
282
|
-
|
|
283
|
+
// Runtime and cost describe the work behind the active selections, not retired history.
|
|
284
|
+
const active = new Set(
|
|
285
|
+
results
|
|
286
|
+
.slots()
|
|
287
|
+
.filter((slot) => slot.active)
|
|
288
|
+
.map((slot) => slot.id),
|
|
289
|
+
);
|
|
290
|
+
const candidates = results
|
|
291
|
+
.evalRuns()
|
|
292
|
+
.filter((candidate) => active.has(candidate.slotId)),
|
|
283
293
|
allJudges = results.judgeRuns();
|
|
284
294
|
const now = Date.now(),
|
|
285
295
|
candidateIndex = new Map(candidates.map((run) => [run.id, run]));
|
|
@@ -291,7 +301,9 @@ export class ResultReader {
|
|
|
291
301
|
: (execution as EvalRun);
|
|
292
302
|
if (!candidate) continue;
|
|
293
303
|
const intervals = evalRuntime.get(candidate.input.evalId) ?? [];
|
|
294
|
-
intervals.push(
|
|
304
|
+
intervals.push(
|
|
305
|
+
...this.runtime(execution, results, run, now, candidate, allJudges),
|
|
306
|
+
);
|
|
295
307
|
evalRuntime.set(candidate.input.evalId, intervals);
|
|
296
308
|
}
|
|
297
309
|
for (const [id, intervals] of evalRuntime)
|
|
@@ -323,6 +335,9 @@ export class ResultReader {
|
|
|
323
335
|
entry: entryOf(run, id, evalId),
|
|
324
336
|
overview: {
|
|
325
337
|
name: evalId ? evals[0].name : run.name,
|
|
338
|
+
...(!evalId && run.definition.suite
|
|
339
|
+
? { suite: run.definition.suite }
|
|
340
|
+
: {}),
|
|
326
341
|
kind: evalId ? "eval" : "benchmark",
|
|
327
342
|
status: run.state,
|
|
328
343
|
startedAt: run.createdAt,
|
|
@@ -353,59 +368,69 @@ export class ResultReader {
|
|
|
353
368
|
if (split < 0) throw new Error("Select an eval");
|
|
354
369
|
const evalId = decodeURIComponent(id.slice(split + 1));
|
|
355
370
|
using results = await this.database(id);
|
|
356
|
-
const benchmark = results.benchmark
|
|
357
|
-
executions = results.evalRuns(),
|
|
358
|
-
judges = results.judgeRuns();
|
|
359
|
-
const runs = results
|
|
360
|
-
.slots()
|
|
361
|
-
.filter((slot) => slot.active && slot.evalId === evalId)
|
|
362
|
-
.map((slot) => {
|
|
363
|
-
const candidate = executions.find((run) => run.id === slot.evalRunId);
|
|
364
|
-
const current = liveRun(
|
|
365
|
-
slot,
|
|
366
|
-
candidate,
|
|
367
|
-
judges,
|
|
368
|
-
benchmark,
|
|
369
|
-
results,
|
|
370
|
-
(run) => this.runtime(run, results, benchmark, Date.now(), candidate),
|
|
371
|
-
);
|
|
372
|
-
const related = executions.filter((run) => run.slotId === slot.id),
|
|
373
|
-
ids = new Set(related.map((run) => run.id));
|
|
374
|
-
return {
|
|
375
|
-
...current,
|
|
376
|
-
cost: runCosts(
|
|
377
|
-
[
|
|
378
|
-
...related,
|
|
379
|
-
...judges.filter((run) => ids.has(run.input.evalRunId)),
|
|
380
|
-
],
|
|
381
|
-
results,
|
|
382
|
-
),
|
|
383
|
-
elapsedMs: runtimeMs(
|
|
384
|
-
[...current.eval.runtime, ...current.judge.runtime],
|
|
385
|
-
Date.now(),
|
|
386
|
-
),
|
|
387
|
-
};
|
|
388
|
-
});
|
|
371
|
+
const benchmark = results.benchmark!;
|
|
389
372
|
return {
|
|
390
373
|
entry: entryOf(benchmark, id, evalId),
|
|
391
374
|
eval: evalId,
|
|
392
375
|
benchmarkId: benchmark.id,
|
|
393
|
-
runs,
|
|
376
|
+
runs: this.liveRuns(results, [evalId]).get(evalId)!,
|
|
394
377
|
};
|
|
395
378
|
}
|
|
379
|
+
/** Active slot rows for the requested evals, from one read of the store. */
|
|
380
|
+
private liveRuns(results: Results, evalIds: readonly string[]) {
|
|
381
|
+
const benchmark = results.benchmark!,
|
|
382
|
+
executions = results.evalRuns(),
|
|
383
|
+
judges = results.judgeRuns(),
|
|
384
|
+
now = Date.now();
|
|
385
|
+
const byId = new Map(executions.map((run) => [run.id, run]));
|
|
386
|
+
const bySlot = Map.groupBy(executions, (run) => run.slotId);
|
|
387
|
+
const judgesFor = Map.groupBy(judges, (run) => run.input.evalRunId);
|
|
388
|
+
const out = new Map<string, EvalRunSummary[]>(
|
|
389
|
+
evalIds.map((evalId) => [evalId, []]),
|
|
390
|
+
);
|
|
391
|
+
for (const slot of results.slots()) {
|
|
392
|
+
const rows = out.get(slot.evalId);
|
|
393
|
+
if (!slot.active || !rows) continue;
|
|
394
|
+
const candidate = slot.evalRunId ? byId.get(slot.evalRunId) : undefined;
|
|
395
|
+
const current = liveRun(
|
|
396
|
+
slot,
|
|
397
|
+
candidate,
|
|
398
|
+
candidate ? (judgesFor.get(candidate.id) ?? []) : [],
|
|
399
|
+
benchmark,
|
|
400
|
+
results,
|
|
401
|
+
(run) => this.runtime(run, results, benchmark, now, candidate, judges),
|
|
402
|
+
);
|
|
403
|
+
const related = bySlot.get(slot.id) ?? [];
|
|
404
|
+
rows.push({
|
|
405
|
+
...current,
|
|
406
|
+
cost: runCosts(
|
|
407
|
+
[...related, ...related.flatMap((run) => judgesFor.get(run.id) ?? [])],
|
|
408
|
+
results,
|
|
409
|
+
),
|
|
410
|
+
elapsedMs: runtimeMs(
|
|
411
|
+
[...current.eval.runtime, ...current.judge.runtime],
|
|
412
|
+
now,
|
|
413
|
+
),
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
return out;
|
|
417
|
+
}
|
|
396
418
|
async activity(): Promise<ActivityRun[]> {
|
|
397
419
|
const index = await this.index(),
|
|
398
420
|
out: ActivityRun[] = [];
|
|
399
421
|
for (const entry of index.entries) {
|
|
422
|
+
const summary = await this.summary(entry.id);
|
|
400
423
|
using results = await this.database(entry.id);
|
|
401
424
|
const benchmark = results.benchmark!;
|
|
402
|
-
const
|
|
425
|
+
const live = this.liveRuns(
|
|
426
|
+
results,
|
|
427
|
+
benchmark.definition.evals.map((item) => item.id),
|
|
428
|
+
);
|
|
403
429
|
for (const [
|
|
404
430
|
evalOrder,
|
|
405
431
|
evalDefinition,
|
|
406
432
|
] of benchmark.definition.evals.entries()) {
|
|
407
|
-
const id = evalResultId(benchmark.id, evalDefinition.id)
|
|
408
|
-
runs = await this.evalRuns(id);
|
|
433
|
+
const id = evalResultId(benchmark.id, evalDefinition.id);
|
|
409
434
|
out.push({
|
|
410
435
|
id,
|
|
411
436
|
benchmarkId: benchmark.id,
|
|
@@ -415,15 +440,27 @@ export class ResultReader {
|
|
|
415
440
|
id,
|
|
416
441
|
eval: evalDefinition.id,
|
|
417
442
|
benchmark: benchmark.id,
|
|
443
|
+
name: benchmark.name,
|
|
444
|
+
...(benchmark.definition.suite
|
|
445
|
+
? { suite: benchmark.definition.suite }
|
|
446
|
+
: {}),
|
|
418
447
|
startedAt: benchmark.createdAt,
|
|
419
448
|
heartbeatAt: benchmark.updatedAt,
|
|
420
449
|
status: benchmark.state,
|
|
421
450
|
concurrency: benchmark.definition.concurrency,
|
|
422
451
|
evalOrder,
|
|
423
|
-
runs:
|
|
452
|
+
runs: live.get(evalDefinition.id)!,
|
|
424
453
|
runtime: summary.related.find(
|
|
425
454
|
(item) => item.eval === evalDefinition.id,
|
|
426
455
|
)!.runtime,
|
|
456
|
+
...(benchmark.execution
|
|
457
|
+
? {
|
|
458
|
+
invocation: {
|
|
459
|
+
startedAt: benchmark.execution.startedAt,
|
|
460
|
+
spentUSD: benchmark.execution.spentUSD,
|
|
461
|
+
},
|
|
462
|
+
}
|
|
463
|
+
: {}),
|
|
427
464
|
},
|
|
428
465
|
});
|
|
429
466
|
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { BenchmarkRun } from "../types";
|
|
2
|
+
import type { Results } from "../infra/sqlite";
|
|
3
|
+
|
|
4
|
+
/** A live runner saves its heartbeat every five seconds. */
|
|
5
|
+
export const STOPPED_RUNNER_MS = 120_000;
|
|
6
|
+
|
|
7
|
+
export const runnerStopped = (
|
|
8
|
+
benchmark: Pick<BenchmarkRun, "state" | "updatedAt">,
|
|
9
|
+
now = Date.now(),
|
|
10
|
+
) =>
|
|
11
|
+
benchmark.state === "running" &&
|
|
12
|
+
now - Date.parse(benchmark.updatedAt) > STOPPED_RUNNER_MS;
|
|
13
|
+
|
|
14
|
+
/** Close the executions a stopped runner left open. Their records stay; the planner repeats the work. */
|
|
15
|
+
export function recoverStoppedRunner(results: Results, now = new Date()) {
|
|
16
|
+
const time = now.toISOString();
|
|
17
|
+
const elapsedMs = (startedAt: string) =>
|
|
18
|
+
Math.max(0, now.getTime() - Date.parse(startedAt));
|
|
19
|
+
const recovered = { evalRuns: 0, judgeRuns: 0 };
|
|
20
|
+
results.transaction(() => {
|
|
21
|
+
for (const run of results.judgeRuns()) {
|
|
22
|
+
if (run.state !== "running") continue;
|
|
23
|
+
for (const check of results.judgeChecks(run.id))
|
|
24
|
+
if (check.state === "running")
|
|
25
|
+
results.finishJudgeCheck({
|
|
26
|
+
...check,
|
|
27
|
+
state: "cancelled",
|
|
28
|
+
completedAt: time,
|
|
29
|
+
});
|
|
30
|
+
results.finishJudge({
|
|
31
|
+
...run,
|
|
32
|
+
state: "failed",
|
|
33
|
+
interrupted: true,
|
|
34
|
+
error: "The runner stopped before judging finished",
|
|
35
|
+
completedAt: time,
|
|
36
|
+
elapsedMs: elapsedMs(run.startedAt),
|
|
37
|
+
});
|
|
38
|
+
recovered.judgeRuns++;
|
|
39
|
+
}
|
|
40
|
+
for (const run of results.evalRuns()) {
|
|
41
|
+
if (run.state !== "running") continue;
|
|
42
|
+
results.finishEval({
|
|
43
|
+
...run,
|
|
44
|
+
state: "failed",
|
|
45
|
+
interrupted: true,
|
|
46
|
+
error: "The runner stopped before this session finished",
|
|
47
|
+
completedAt: time,
|
|
48
|
+
elapsedMs: elapsedMs(run.startedAt),
|
|
49
|
+
...(run.stop ? { stop: { ...run.stop, applied: false } } : {}),
|
|
50
|
+
});
|
|
51
|
+
recovered.evalRuns++;
|
|
52
|
+
}
|
|
53
|
+
results.saveBenchmark({
|
|
54
|
+
...results.benchmark!,
|
|
55
|
+
state: "incomplete",
|
|
56
|
+
scheduledSlotIds: [],
|
|
57
|
+
updatedAt: time,
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
return recovered;
|
|
61
|
+
}
|
package/src/app/rejudge.ts
CHANGED
|
@@ -8,6 +8,7 @@ import { executeQueue, finishBenchmark } from "./run-benchmark";
|
|
|
8
8
|
import { judgingFingerprint } from "../infra/judging";
|
|
9
9
|
import { canJudgeEval } from "./eval-state";
|
|
10
10
|
import { verificationRuntime } from "../infra/verification/image";
|
|
11
|
+
import { recoverStoppedRunner, runnerStopped } from "./recover-run";
|
|
11
12
|
|
|
12
13
|
/** Rejudging changes only the selected judgment. The candidate execution is never repeated. */
|
|
13
14
|
export async function judgeRun(
|
|
@@ -26,13 +27,16 @@ export async function judgeRuns(
|
|
|
26
27
|
): Promise<JudgeRun[]> {
|
|
27
28
|
if (!evalRunIds.length) return [];
|
|
28
29
|
using results = new Results(resolve(directory, "runner.db"));
|
|
29
|
-
|
|
30
|
-
if (previous.mergedInto)
|
|
30
|
+
if (results.benchmark!.mergedInto)
|
|
31
31
|
throw new Error(
|
|
32
|
-
`This run was merged into ${
|
|
32
|
+
`This run was merged into ${results.benchmark!.mergedInto}; use the aggregate run`,
|
|
33
33
|
);
|
|
34
|
-
if (
|
|
35
|
-
|
|
34
|
+
if (results.benchmark!.state === "running") {
|
|
35
|
+
if (!runnerStopped(results.benchmark!))
|
|
36
|
+
throw new Error("Wait for the active benchmark run");
|
|
37
|
+
recoverStoppedRunner(results);
|
|
38
|
+
}
|
|
39
|
+
const previous = results.benchmark!;
|
|
36
40
|
const candidates = [...new Set(evalRunIds)].map((evalRunId) => {
|
|
37
41
|
const candidate = results.evalRun(evalRunId);
|
|
38
42
|
if (!canJudgeEval(candidate))
|
package/src/app/retry-run.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { candidateFingerprint } from "./plan-benchmark";
|
|
|
6
6
|
import { runEvalPipeline } from "./run-eval-pipeline";
|
|
7
7
|
import { ExecutionBudget } from "./execution-budget";
|
|
8
8
|
import { finishBenchmark } from "./run-benchmark";
|
|
9
|
+
import { recoverStoppedRunner, runnerStopped } from "./recover-run";
|
|
9
10
|
|
|
10
11
|
export async function retryEvalRun(
|
|
11
12
|
directory: string,
|
|
@@ -13,13 +14,16 @@ export async function retryEvalRun(
|
|
|
13
14
|
onEvent?: ExecutionObserver,
|
|
14
15
|
) {
|
|
15
16
|
using results = new Results(resolve(directory, "runner.db"));
|
|
16
|
-
|
|
17
|
-
if (benchmark.mergedInto)
|
|
17
|
+
if (results.benchmark!.mergedInto)
|
|
18
18
|
throw new Error(
|
|
19
|
-
`This run was merged into ${benchmark
|
|
19
|
+
`This run was merged into ${results.benchmark!.mergedInto}; use the aggregate run`,
|
|
20
20
|
);
|
|
21
|
-
if (benchmark
|
|
22
|
-
|
|
21
|
+
if (results.benchmark!.state === "running") {
|
|
22
|
+
if (!runnerStopped(results.benchmark!))
|
|
23
|
+
throw new Error("Wait for the active benchmark run");
|
|
24
|
+
recoverStoppedRunner(results);
|
|
25
|
+
}
|
|
26
|
+
const benchmark = results.benchmark!;
|
|
23
27
|
const previous = results.evalRun(evalRunId);
|
|
24
28
|
if (!previous || previous.state === "completed")
|
|
25
29
|
throw new Error(
|
|
@@ -28,7 +32,10 @@ export async function retryEvalRun(
|
|
|
28
32
|
const slot = results.slot(previous.slotId)!;
|
|
29
33
|
if (slot.evalRunId !== previous.id)
|
|
30
34
|
throw new Error("Select the current eval run for this repetition");
|
|
31
|
-
const runtime = await runtimeFingerprint(
|
|
35
|
+
const runtime = await runtimeFingerprint(
|
|
36
|
+
benchmark.definition.container,
|
|
37
|
+
benchmark.definition.judge.verification,
|
|
38
|
+
);
|
|
32
39
|
if (
|
|
33
40
|
candidateFingerprint(
|
|
34
41
|
benchmark.definition,
|
package/src/app/run-benchmark.ts
CHANGED
|
@@ -23,6 +23,7 @@ import { canJudgeEval } from "./eval-state";
|
|
|
23
23
|
import { isScored } from "../judgment";
|
|
24
24
|
import { benchmarkScores } from "./scores";
|
|
25
25
|
import { CostBudget, estimateWork } from "./cost-plan";
|
|
26
|
+
import { recoverStoppedRunner, runnerStopped } from "./recover-run";
|
|
26
27
|
import {
|
|
27
28
|
categoryKey,
|
|
28
29
|
categoryList,
|
|
@@ -101,7 +102,8 @@ export function finishBenchmark(context: ExecutionContext) {
|
|
|
101
102
|
(run) =>
|
|
102
103
|
run.input.evalRunId === slot.evalRunId &&
|
|
103
104
|
run.input.judgeHash === slot.judgeHash &&
|
|
104
|
-
["failed", "timed_out"].includes(run.state)
|
|
105
|
+
["failed", "timed_out"].includes(run.state) &&
|
|
106
|
+
!run.interrupted,
|
|
105
107
|
) &&
|
|
106
108
|
!slot.judgeRunId),
|
|
107
109
|
);
|
|
@@ -183,6 +185,7 @@ export async function runBenchmark(
|
|
|
183
185
|
(!options.fresh
|
|
184
186
|
? await currentBenchmarkRun(definition.directory)
|
|
185
187
|
: undefined);
|
|
188
|
+
let stopped = false;
|
|
186
189
|
if (directory) {
|
|
187
190
|
using previous = new Results(resolve(directory, "runner.db"), true);
|
|
188
191
|
const saved = previous.benchmark!;
|
|
@@ -190,8 +193,9 @@ export async function runBenchmark(
|
|
|
190
193
|
throw new Error(
|
|
191
194
|
`This run was merged into ${saved.mergedInto}; use the aggregate run`,
|
|
192
195
|
);
|
|
193
|
-
if (saved.state === "running")
|
|
196
|
+
if (saved.state === "running" && !runnerStopped(saved))
|
|
194
197
|
throw new Error("This benchmark run is already running");
|
|
198
|
+
stopped = saved.state === "running";
|
|
195
199
|
definition.models = [
|
|
196
200
|
...new Set([...saved.definition.models, ...definition.models]),
|
|
197
201
|
];
|
|
@@ -220,10 +224,16 @@ export async function runBenchmark(
|
|
|
220
224
|
? new Results(database, true)
|
|
221
225
|
: new Results(":memory:")
|
|
222
226
|
: new Results(database);
|
|
227
|
+
// A stopped runner leaves sessions open. A dry run plans them as interrupted work.
|
|
228
|
+
const recovered = stopped && !options.dryRun
|
|
229
|
+
? recoverStoppedRunner(results)
|
|
230
|
+
: undefined;
|
|
223
231
|
const previousSelections = new Map(
|
|
224
232
|
results.slots().map((slot) => [slot.id, slot]),
|
|
225
233
|
);
|
|
226
|
-
let plan = planBenchmark(definition, runtime, results
|
|
234
|
+
let plan = planBenchmark(definition, runtime, results, {
|
|
235
|
+
stoppedRunner: stopped,
|
|
236
|
+
});
|
|
227
237
|
if (selected || onlyEvals || options.onlyRepetitions) {
|
|
228
238
|
const retained = new Map(results.slots().map((slot) => [slot.id, slot]));
|
|
229
239
|
plan = plan.map((item) =>
|
|
@@ -260,7 +270,7 @@ export async function runBenchmark(
|
|
|
260
270
|
fingerprint(previous.runtime) === fingerprint(runtime) &&
|
|
261
271
|
fingerprint(previous.modelNames ?? {}) === fingerprint(modelNames)
|
|
262
272
|
)
|
|
263
|
-
return { directory, plan, estimate, benchmark: previous };
|
|
273
|
+
return { directory, plan, estimate, benchmark: previous, recovered };
|
|
264
274
|
const benchmark: BenchmarkRun = {
|
|
265
275
|
id: previous?.id ?? `benchmark_${randomUUID()}`,
|
|
266
276
|
name: definition.name,
|
|
@@ -397,7 +407,13 @@ export async function runBenchmark(
|
|
|
397
407
|
deferred: plan.filter((item) => item.action === "deferred").length,
|
|
398
408
|
},
|
|
399
409
|
});
|
|
400
|
-
return {
|
|
410
|
+
return {
|
|
411
|
+
directory,
|
|
412
|
+
plan,
|
|
413
|
+
estimate,
|
|
414
|
+
benchmark: finishBenchmark(context),
|
|
415
|
+
recovered,
|
|
416
|
+
};
|
|
401
417
|
} catch (error) {
|
|
402
418
|
results.saveBenchmark({
|
|
403
419
|
...results.benchmark!,
|