@hona/openeval 0.2.2 → 0.3.1
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/JUDGING.md +135 -23
- package/README.md +71 -9
- package/package.json +7 -6
- package/src/app/cost-plan.ts +20 -16
- package/src/app/eval-state.ts +2 -1
- package/src/app/grade-recording.ts +72 -0
- package/src/app/input-fingerprints.ts +22 -8
- package/src/app/judge-evidence.ts +26 -11
- package/src/app/judge-run.ts +36 -19
- package/src/app/load-benchmark.ts +48 -16
- package/src/app/read-results.ts +35 -2
- package/src/app/read-run.ts +23 -2
- package/src/app/rejudge.ts +2 -1
- package/src/app/run-benchmark.ts +8 -1
- package/src/app/run-eval-pipeline.ts +2 -1
- package/src/app/run-eval.ts +13 -0
- package/src/app/scores.ts +76 -34
- package/src/app/serve-results.ts +2 -0
- package/src/evidence.ts +4 -1
- package/src/index.ts +15 -3
- package/src/infra/containers/runtime/package.json +2 -2
- package/src/infra/containers/runtime/server.mjs +1 -1
- package/src/infra/evidence/index.ts +39 -2
- package/src/infra/evidence/tool-calls.ts +5 -2
- package/src/infra/judging/agent.ts +3 -2
- package/src/infra/judging/code-result.ts +102 -0
- package/src/infra/judging/code-source.ts +96 -0
- package/src/infra/judging/code-worker.ts +26 -0
- package/src/infra/judging/code.ts +97 -0
- package/src/infra/judging/contract.ts +82 -64
- package/src/infra/judging/evidence-tool.ts +3 -1
- package/src/infra/judging/index.ts +14 -0
- package/src/infra/judging/judge-agent.md +24 -18
- package/src/infra/judging/observer.ts +8 -7
- package/src/infra/judging/submission.ts +8 -8
- package/src/infra/judging/tools.ts +9 -9
- package/src/infra/opencode/host.ts +1 -1
- package/src/infra/opencode/read-recording.ts +6 -1
- package/src/infra/opencode/version.ts +2 -0
- package/src/infra/recording/index.ts +240 -0
- package/src/infra/recording/metrics.ts +193 -0
- package/src/infra/sqlite/index.ts +29 -19
- package/src/judge-context.ts +141 -0
- package/src/judgment.ts +26 -17
- package/src/types.ts +36 -17
- package/src/view.ts +40 -14
- package/viewer/THIRD_PARTY_LICENSES.md +2 -2
- package/viewer/assets/{abnfDiagram-VCTEODGH-B1kcYgQG.js → abnfDiagram-VCTEODGH-Ck7reUId.js} +1 -1
- package/viewer/assets/{angular-html-DrYCUiZv.js → angular-html-DzRW817u.js} +1 -1
- package/viewer/assets/{angular-ts-BgjwQn-Z.js → angular-ts-Dwpo5JSv.js} +1 -1
- package/viewer/assets/{apl-DlHqe8o4.js → apl-bt59SDvg.js} +1 -1
- package/viewer/assets/{arc-C4nf7ZoY.js → arc-Dwbl2Pkh.js} +1 -1
- package/viewer/assets/architecture-7GRP2DOG-BZpt0zSq.js +1 -0
- package/viewer/assets/{architectureDiagram-5GKGNRK7-BIWoe0fO.js → architectureDiagram-5GKGNRK7-BcmO-Qqp.js} +1 -1
- package/viewer/assets/{astro-BrlHnIh9.js → astro-B3siF1pJ.js} +1 -1
- package/viewer/assets/{blade-DOcerOiW.js → blade-BevF_cYv.js} +1 -1
- package/viewer/assets/{blockDiagram-I7D4REHJ-C3x13a9m.js → blockDiagram-I7D4REHJ-MzX9_xf9.js} +1 -1
- package/viewer/assets/{c-BMbThxEo.js → c-gavBjOcY.js} +1 -1
- package/viewer/assets/{c4Diagram-7LVT6UL2-BeWktchx.js → c4Diagram-7LVT6UL2-BbHHPJGg.js} +1 -1
- package/viewer/assets/channel-BnnBJr7O.js +1 -0
- package/viewer/assets/{chapel-k2cjkSmc.js → chapel-DgshBe1K.js} +1 -1
- package/viewer/assets/{chunk-4HAMMTFA-o6YtJsqE.js → chunk-4HAMMTFA-D1Gsg_qO.js} +1 -1
- package/viewer/assets/{chunk-75Z2AOVW-KohQa9Nw.js → chunk-75Z2AOVW-EfPG3FOY.js} +1 -1
- package/viewer/assets/{chunk-DU6HZSFF-C88wxc6m.js → chunk-DU6HZSFF-BxB0lceI.js} +1 -1
- package/viewer/assets/{chunk-F27PBJKO-mO4AP2as.js → chunk-F27PBJKO-DRBDr_-Q.js} +1 -1
- package/viewer/assets/{chunk-GMAD6QVW-DzQX2Cld.js → chunk-GMAD6QVW-AbIWvPs8.js} +1 -1
- package/viewer/assets/{chunk-GVQU2GXP-CS78RfJD.js → chunk-GVQU2GXP-aIYNUxo9.js} +1 -1
- package/viewer/assets/{chunk-IMKFNOWR-DxS42GUo.js → chunk-IMKFNOWR-BwpTTnl_.js} +1 -1
- package/viewer/assets/{chunk-L3NEJ4N5-DGo-uYLy.js → chunk-L3NEJ4N5-KtxD1mfg.js} +1 -1
- package/viewer/assets/{chunk-OSK3NFVY-BOcXQG16.js → chunk-OSK3NFVY-CinbAXbX.js} +1 -1
- package/viewer/assets/{chunk-P2QGCYS3-B7GnZdIQ.js → chunk-P2QGCYS3-Dst8DZSR.js} +1 -1
- package/viewer/assets/{chunk-POPQ4Y6H-DgBcYHog.js → chunk-POPQ4Y6H-DqvBdud7.js} +1 -1
- package/viewer/assets/{chunk-PWAF6VOD-DpEq6qHA.js → chunk-PWAF6VOD-Cl2eG05g.js} +1 -1
- package/viewer/assets/{chunk-SHT3W25Y-CrOGxKM2.js → chunk-SHT3W25Y-D9FbZ836.js} +1 -1
- package/viewer/assets/{chunk-SVP7TREG-4HY7Fljj.js → chunk-SVP7TREG-_4n_lRpO.js} +1 -1
- package/viewer/assets/{chunk-TICWLB2K-B1Rl31EC.js → chunk-TICWLB2K-DUXjjJlY.js} +1 -1
- package/viewer/assets/{chunk-XXDRQBXY-CK8giEW-.js → chunk-XXDRQBXY-BdRoSapX.js} +1 -1
- package/viewer/assets/classDiagram-ZZMXUADV-F9J3BKRU.js +1 -0
- package/viewer/assets/classDiagram-v2-VYDZK3BY-F9J3BKRU.js +1 -0
- package/viewer/assets/{cobol-CupUwvw9.js → cobol-D5zqvXzY.js} +1 -1
- package/viewer/assets/{coffee-C17pkozF.js → coffee-SCAyVn9N.js} +1 -1
- package/viewer/assets/{cose-bilkent-JH36ORCC-V1OmrmCv.js → cose-bilkent-JH36ORCC-B5uDWB-J.js} +1 -1
- package/viewer/assets/{cpp-BJwVQVXg.js → cpp-RUQ97EK5.js} +1 -1
- package/viewer/assets/{crystal-BF-oCN-L.js → crystal-FauNVdkE.js} +1 -1
- package/viewer/assets/{css-BMgkVI3c.js → css-DuzOk7di.js} +1 -1
- package/viewer/assets/{cynefin-OW5HDTMX-BEjY_gsc.js → cynefin-OW5HDTMX-D0N8Dm6P.js} +1 -1
- package/viewer/assets/{cynefinDiagram-5FMLGOSQ-bfSYgr6z.js → cynefinDiagram-5FMLGOSQ-IGGiqH_Y.js} +1 -1
- package/viewer/assets/{dagre-GXQ25YYZ-VtwTgGfa.js → dagre-GXQ25YYZ-PoopsFcY.js} +1 -1
- package/viewer/assets/{diagram-S7CK7UJ4-CHaAAyW7.js → diagram-S7CK7UJ4-D6GuIlRD.js} +1 -1
- package/viewer/assets/{diagram-UQ7AKVKN-CjL65-As.js → diagram-UQ7AKVKN-Cj8lu7Hi.js} +1 -1
- package/viewer/assets/{diagram-VSXAHHWV-Cnxrk1TJ.js → diagram-VSXAHHWV-bI4-SjAp.js} +1 -1
- package/viewer/assets/{diagram-VX7I27RA-D78jPXr7.js → diagram-VX7I27RA-jWvTT618.js} +1 -1
- package/viewer/assets/{diagram-Z3DM3KII-DKjnOaF2.js → diagram-Z3DM3KII-D6Vcvcr8.js} +1 -1
- package/viewer/assets/{dist-BTYPW0dz.js → dist-Cn65YNrn.js} +1 -1
- package/viewer/assets/{ebnfDiagram-PWID7BFC-7XiXh5Lx.js → ebnfDiagram-PWID7BFC-y-ZxfP2H.js} +1 -1
- package/viewer/assets/{edge-B34N_HjN.js → edge-Bc0EBQ7y.js} +1 -1
- package/viewer/assets/{elixir-CQzWMyKD.js → elixir-BmxsT0oc.js} +1 -1
- package/viewer/assets/{elm-BNJiG-wg.js → elm-BrTAZ55m.js} +1 -1
- package/viewer/assets/{erDiagram-RLTQ6QDP-YPFEgZxU.js → erDiagram-RLTQ6QDP-Wa1tikbb.js} +1 -1
- package/viewer/assets/{erb-D5Rm3UNa.js → erb-B60HRxmg.js} +1 -1
- package/viewer/assets/eventmodeling-NTZA5JFV-BNvBHnQf.js +1 -0
- package/viewer/assets/flowDiagram-HODETNUW-zGgqOVuO.js +1 -0
- package/viewer/assets/{ganttDiagram-EL5Y4UJY-ergSGtTP.js → ganttDiagram-EL5Y4UJY-DjskXB68.js} +1 -1
- package/viewer/assets/{git-rebase-BpIMZIRG.js → git-rebase-CK9f68l3.js} +1 -1
- package/viewer/assets/{gitGraph-4MIJSDKK-CZt4REVj.js → gitGraph-4MIJSDKK-CvRhO7Ae.js} +1 -1
- package/viewer/assets/{gitGraphDiagram-WWUBYQGX-h9QMzC2r.js → gitGraphDiagram-WWUBYQGX-Bg_OLNL1.js} +1 -1
- package/viewer/assets/{glimmer-js-SeSwns5_.js → glimmer-js-CVc7keaR.js} +1 -1
- package/viewer/assets/{glimmer-ts-BExI5tTz.js → glimmer-ts-DYraWlmH.js} +1 -1
- package/viewer/assets/{glsl-CXikKw2b.js → glsl-BpYswgfh.js} +1 -1
- package/viewer/assets/{graphql-6GpQIInt.js → graphql-C3iDzxap.js} +1 -1
- package/viewer/assets/{hack-i7rizvpB.js → hack-D8oEoipZ.js} +1 -1
- package/viewer/assets/{haml-C68gmkTf.js → haml-BJb-BQAN.js} +1 -1
- package/viewer/assets/{handlebars-BBn_c0Xt.js → handlebars-wB54lHaB.js} +1 -1
- package/viewer/assets/{html-BObmAZQA.js → html-HIvKkSZ3.js} +1 -1
- package/viewer/assets/{html-derivative-BjMuR1LM.js → html-derivative-CDavbmdn.js} +1 -1
- package/viewer/assets/{http-BJBk8gt0.js → http-DEgShh0-.js} +1 -1
- package/viewer/assets/{hurl-D7obCYfg.js → hurl-D8wvJcyh.js} +1 -1
- package/viewer/assets/{index-FMtYXcX7.js → index-Ck3NBCVJ.js} +9 -7
- package/viewer/assets/{index-Wz0qQyIk.css → index-DAuO1BM4.css} +1 -1
- package/viewer/assets/{info-A6RAGUB7-CrzitQIU.js → info-A6RAGUB7-DL3H6Iqn.js} +1 -1
- package/viewer/assets/{infoDiagram-27XIBGKW-D2CgRDxu.js → infoDiagram-27XIBGKW-DSLzhrL8.js} +1 -1
- package/viewer/assets/{ishikawaDiagram-5VMMS53U-BhnQMHOo.js → ishikawaDiagram-5VMMS53U-B-ycXUrd.js} +1 -1
- package/viewer/assets/{java-5VJU_EW8.js → java-C5iWdXOl.js} +1 -1
- package/viewer/assets/{javascript-r8UgndxQ.js → javascript-PMSXGhX1.js} +1 -1
- package/viewer/assets/{jinja-BY2ibHG5.js → jinja-CJ_l63gX.js} +1 -1
- package/viewer/assets/{jison-C_7YxymZ.js → jison-DGaZyJAT.js} +1 -1
- package/viewer/assets/{journeyDiagram-3NMN7TZE-DSBzKQEa.js → journeyDiagram-3NMN7TZE-2p56tZPm.js} +1 -1
- package/viewer/assets/{json-8e1WlJYD.js → json-CCQ1eo9o.js} +1 -1
- package/viewer/assets/{jsx-BYp5GD4Y.js → jsx-DeLT99JL.js} +1 -1
- package/viewer/assets/{julia-Bkob1uVX.js → julia-DODLsulx.js} +1 -1
- package/viewer/assets/{just-CUnSjty-.js → just-B_Nvjvhv.js} +1 -1
- package/viewer/assets/{kanban-definition-UXKFOSKX-CotbrQue.js → kanban-definition-UXKFOSKX-B42fPx4K.js} +1 -1
- package/viewer/assets/{latex-UdE3ivqk.js → latex-CCjy2I9z.js} +1 -1
- package/viewer/assets/{line-DDh6a6hz.js → line-DGK8UZYe.js} +1 -1
- package/viewer/assets/{linear-BxMsCbKb.js → linear-LxXu6wOl.js} +1 -1
- package/viewer/assets/{liquid-WL4MvEIT.js → liquid-C2tknZP0.js} +1 -1
- package/viewer/assets/{lua-CtOubZta.js → lua-D6UzQw5Z.js} +1 -1
- package/viewer/assets/{marko-C0GSQJi3.js → marko-CGd5W4Hn.js} +1 -1
- package/viewer/assets/{mdc-DUivddFc.js → mdc-Bp5z92Vo.js} +1 -1
- package/viewer/assets/{mermaid-parser.core-FhKE_uv2.js → mermaid-parser.core-xGBt_sNt.js} +3 -3
- package/viewer/assets/{mermaid.core-BSOWwyvd.js → mermaid.core-D1TUMBuT.js} +4 -4
- package/viewer/assets/{mindmap-definition-YA3MSWOX-tsLZ_Opn.js → mindmap-definition-YA3MSWOX-BY9cVMtI.js} +1 -1
- package/viewer/assets/{nginx-B__INAtC.js → nginx-DAsqAd3h.js} +1 -1
- package/viewer/assets/{nim-UEY12GJF.js → nim-COOJz623.js} +1 -1
- package/viewer/assets/{org-DDIZsz95.js → org-CBV1qqrA.js} +1 -1
- package/viewer/assets/{packet-AYTQ26CC-CqdJnqfo.js → packet-AYTQ26CC-61q5HcB3.js} +1 -1
- package/viewer/assets/{pegDiagram-XKGWAZYB-BJs7qu7m.js → pegDiagram-XKGWAZYB-DLEkmimO.js} +1 -1
- package/viewer/assets/{perl-D5KwNZt7.js → perl-6SV_ayGd.js} +1 -1
- package/viewer/assets/{php-BEs7d_Dk.js → php-BUb-Io9m.js} +1 -1
- package/viewer/assets/{pie-WAS4IAKB-lgzqBWqy.js → pie-WAS4IAKB-DgijP3YA.js} +1 -1
- package/viewer/assets/{pieDiagram-E7YTZNPT-Cxe9LnnE.js → pieDiagram-E7YTZNPT-Cp20J80-.js} +1 -1
- package/viewer/assets/{pug-B0dsfQOV.js → pug-D5s94d1b.js} +1 -1
- package/viewer/assets/{qml-XPIhrIzf.js → qml-D8g4NGVo.js} +1 -1
- package/viewer/assets/{quadrantDiagram-AXDQQJYC-D5JGwC11.js → quadrantDiagram-AXDQQJYC-Djsc9xpm.js} +1 -1
- package/viewer/assets/{r-C6hQdpIM.js → r-DXvz5K4A.js} +1 -1
- package/viewer/assets/{radar-RG4KPBEZ-B7JRLhUA.js → radar-RG4KPBEZ-BCmoUpe8.js} +1 -1
- package/viewer/assets/{railroad-74A4TZTK-Df7ZQBLn.js → railroad-74A4TZTK-DZ0SYESf.js} +1 -1
- package/viewer/assets/railroad-abnf-HS5TGJTU-C2CQDbu6.js +1 -0
- package/viewer/assets/railroad-ebnf-LZEXJU2U-6ssJubZr.js +1 -0
- package/viewer/assets/railroad-peg-WCYAUIDC-UOxDGPyg.js +1 -0
- package/viewer/assets/{railroadDiagram-O6MQD6OU-DZJzpmP8.js → railroadDiagram-O6MQD6OU-DmD4JR_t.js} +1 -1
- package/viewer/assets/{razor-C3KSUuOw.js → razor-D9CjUAvE.js} +1 -1
- package/viewer/assets/{regexp-n-T936Rk.js → regexp-CnuLvLnC.js} +1 -1
- package/viewer/assets/{requirementDiagram-BXWQKSXE-Be9lEFrk.js → requirementDiagram-BXWQKSXE-BTci7X6J.js} +1 -1
- package/viewer/assets/{rst-fVnHDyXD.js → rst-Chdv5DuC.js} +1 -1
- package/viewer/assets/{ruby-DuRgxlXF.js → ruby-DXQkNv8k.js} +1 -1
- package/viewer/assets/{sankeyDiagram-P5KCCOFB-BsgKAEBN.js → sankeyDiagram-P5KCCOFB-BaHSMANQ.js} +1 -1
- package/viewer/assets/{sas-C0WA2OFa.js → sas-B6S0Oq1H.js} +1 -1
- package/viewer/assets/{scss-CQDZ4smR.js → scss-BnFPXH54.js} +1 -1
- package/viewer/assets/{sequenceDiagram-WJ2MYXX4-BkUDWL0D.js → sequenceDiagram-WJ2MYXX4-BUk21g1t.js} +1 -1
- package/viewer/assets/{shellscript-CE84G0GP.js → shellscript-Brdg8vyE.js} +1 -1
- package/viewer/assets/{shellsession-BdJvdaqL.js → shellsession-Df33UGC7.js} +1 -1
- package/viewer/assets/{soy-BSxOREC0.js → soy-DE83qari.js} +1 -1
- package/viewer/assets/{sql-D4MN9uRI.js → sql-C838IanB.js} +1 -1
- package/viewer/assets/{src-BT_kESTA.js → src-tvfSkCy9.js} +1 -1
- package/viewer/assets/{stata-B6nXAaGV.js → stata-BjrFc6Ob.js} +1 -1
- package/viewer/assets/{stateDiagram-D77RDMKH-C80wp8xG.js → stateDiagram-D77RDMKH-B7yRJv9-.js} +1 -1
- package/viewer/assets/stateDiagram-v2-MP3YSRHH-BCwhllSg.js +1 -0
- package/viewer/assets/{surrealql-fVjZmhV3.js → surrealql-BQl2P64c.js} +1 -1
- package/viewer/assets/{svelte-DnBQ5p7d.js → svelte-WhVuP9VM.js} +1 -1
- package/viewer/assets/{swimlanes-42K2YHIH-Cff6yT8e.js → swimlanes-42K2YHIH-on6wyUF5.js} +1 -1
- package/viewer/assets/swimlanesDiagram-VR7AAH4N-D0QyhouX.js +8 -0
- package/viewer/assets/{templ-TCx51Uts.js → templ-GlyHWfam.js} +1 -1
- package/viewer/assets/{tex-CF6SMAQ_.js → tex-Wu25UU_Z.js} +1 -1
- package/viewer/assets/{timeline-definition-24CTP7MA-Cje7BvaN.js → timeline-definition-24CTP7MA-DsgJtBig.js} +1 -1
- package/viewer/assets/{treeView-Q6P3EWNA-D-QLY3IO.js → treeView-Q6P3EWNA-Bae2q_db.js} +1 -1
- package/viewer/assets/{treemap-WGGIJYW6-BCHcTa4N.js → treemap-WGGIJYW6-BJsxvsBb.js} +1 -1
- package/viewer/assets/{ts-tags-BqLRnADX.js → ts-tags-C6TgXLV6.js} +1 -1
- package/viewer/assets/{tsx-D7KpDX0b.js → tsx-DVoPHges.js} +1 -1
- package/viewer/assets/{twig-CEZLObpN.js → twig-CKzx4A42.js} +1 -1
- package/viewer/assets/{typescript-DAbIDLbf.js → typescript-DvXz8u_k.js} +1 -1
- package/viewer/assets/{typst-CqN7-whv.js → typst-BcepwfpV.js} +1 -1
- package/viewer/assets/{vennDiagram-4TSXK5OY-Bz20rx02.js → vennDiagram-4TSXK5OY-CKUR4uY_.js} +1 -1
- package/viewer/assets/{vue-kTxN6wdc.js → vue-Bqzme_T-.js} +1 -1
- package/viewer/assets/{vue-html-DQmvvKbd.js → vue-html-DZHKNOUy.js} +1 -1
- package/viewer/assets/{vue-vine-Did3qTVW.js → vue-vine-azvFs0P6.js} +1 -1
- package/viewer/assets/{wardley-WFR3VGLG-CpXaqOit.js → wardley-WFR3VGLG-Ddsu3qLB.js} +1 -1
- package/viewer/assets/{wardleyDiagram-VM6X3IG4-Cz06jHpA.js → wardleyDiagram-VM6X3IG4-CCHx9UX4.js} +1 -1
- package/viewer/assets/{xml-te2hFcAA.js → xml-BZNC4g25.js} +1 -1
- package/viewer/assets/{xsl-X4fuKSVl.js → xsl-DXEWaWc5.js} +1 -1
- package/viewer/assets/{xychartDiagram-S5SC5T6Z-BXCw0sfu.js → xychartDiagram-S5SC5T6Z-CkoTq405.js} +1 -1
- package/viewer/assets/{yaml-NHtDSZab.js → yaml-DLY3wDEk.js} +1 -1
- package/viewer/index.html +2 -2
- package/viewer/assets/architecture-7GRP2DOG-Be250VKZ.js +0 -1
- package/viewer/assets/channel-DtULXRee.js +0 -1
- package/viewer/assets/classDiagram-ZZMXUADV-Bmqu01Xf.js +0 -1
- package/viewer/assets/classDiagram-v2-VYDZK3BY-Bmqu01Xf.js +0 -1
- package/viewer/assets/eventmodeling-NTZA5JFV-CJCg5f6p.js +0 -1
- package/viewer/assets/flowDiagram-HODETNUW-BFzq4Zvs.js +0 -1
- package/viewer/assets/railroad-abnf-HS5TGJTU-Z8q3QVTf.js +0 -1
- package/viewer/assets/railroad-ebnf-LZEXJU2U-BWoRkG3W.js +0 -1
- package/viewer/assets/railroad-peg-WCYAUIDC-BzoV1cN7.js +0 -1
- package/viewer/assets/stateDiagram-v2-MP3YSRHH-CYdsKKlz.js +0 -1
- package/viewer/assets/swimlanesDiagram-VR7AAH4N-niTUGjMT.js +0 -8
package/src/app/judge-run.ts
CHANGED
|
@@ -1,22 +1,20 @@
|
|
|
1
1
|
import { resolve, relative } from "node:path";
|
|
2
2
|
import type { EvalRun, JudgeRun } from "../types";
|
|
3
3
|
import type { ExecutionContext } from "./context";
|
|
4
|
-
import {
|
|
4
|
+
import { gradeRecording } from "./grade-recording";
|
|
5
5
|
import { errorMessage } from "../infra/files";
|
|
6
6
|
import { canJudgeEval } from "./eval-state";
|
|
7
7
|
import { JUDGE_PROTOCOL } from "../judgment";
|
|
8
8
|
import { JUDGE_AGENT } from "../infra/judging/agent";
|
|
9
9
|
|
|
10
|
-
/** A new
|
|
10
|
+
/** A new grading execution becomes active only after all present judges succeed. */
|
|
11
11
|
export async function judgeEvalRun(
|
|
12
12
|
context: ExecutionContext,
|
|
13
13
|
candidate: EvalRun,
|
|
14
14
|
monitoring: Pick<JudgeRun, "monitorError" | "monitorErrorAt"> = {},
|
|
15
15
|
): Promise<JudgeRun> {
|
|
16
16
|
if (!canJudgeEval(candidate))
|
|
17
|
-
throw new Error(
|
|
18
|
-
"Judging requires finalized evidence from a completed, stopped, or timed-out eval run",
|
|
19
|
-
);
|
|
17
|
+
throw new Error("Judging requires finalized candidate evidence");
|
|
20
18
|
const slot = context.results.slot(candidate.slotId)!;
|
|
21
19
|
const definition = context.definition.evals.find(
|
|
22
20
|
(item) => item.id === slot.evalId,
|
|
@@ -25,20 +23,22 @@ export async function judgeEvalRun(
|
|
|
25
23
|
evalRunId: candidate.id,
|
|
26
24
|
evidence: candidate.evidence,
|
|
27
25
|
rubric: definition.judge,
|
|
28
|
-
agent: JUDGE_AGENT,
|
|
29
|
-
model: context.definition.judge.model,
|
|
26
|
+
agent: definition.judge ? JUDGE_AGENT : undefined,
|
|
27
|
+
model: definition.judge ? context.definition.judge.model : undefined,
|
|
28
|
+
kind: definition.code ? (definition.judge ? "hybrid" : "code") : "llm",
|
|
29
|
+
code: definition.code,
|
|
30
30
|
judgeHash: slot.judgeHash,
|
|
31
31
|
timeoutMs: context.definition.judge.timeoutMs,
|
|
32
32
|
websearch: context.definition.judge.websearch,
|
|
33
33
|
mode: "final",
|
|
34
34
|
runtimeHash: context.runtime.judgeHash,
|
|
35
35
|
protocol: JUDGE_PROTOCOL,
|
|
36
|
-
|
|
36
|
+
criteria: definition.criteria,
|
|
37
37
|
});
|
|
38
38
|
const directory = resolve(context.directory, "judge-runs", run.id);
|
|
39
39
|
let completed: JudgeRun;
|
|
40
40
|
try {
|
|
41
|
-
const
|
|
41
|
+
const graded = await gradeRecording(
|
|
42
42
|
{
|
|
43
43
|
...run.input,
|
|
44
44
|
evidence: {
|
|
@@ -46,6 +46,14 @@ export async function judgeEvalRun(
|
|
|
46
46
|
directory: resolve(context.directory, run.input.evidence.directory),
|
|
47
47
|
},
|
|
48
48
|
},
|
|
49
|
+
{
|
|
50
|
+
evidence: {
|
|
51
|
+
...candidate.evidence,
|
|
52
|
+
directory: resolve(context.directory, candidate.evidence.directory),
|
|
53
|
+
},
|
|
54
|
+
run: candidate,
|
|
55
|
+
runDirectory: context.directory,
|
|
56
|
+
},
|
|
49
57
|
directory,
|
|
50
58
|
(event) => {
|
|
51
59
|
const record = {
|
|
@@ -61,16 +69,25 @@ export async function judgeEvalRun(
|
|
|
61
69
|
completed = {
|
|
62
70
|
...run,
|
|
63
71
|
...monitoring,
|
|
64
|
-
state:
|
|
65
|
-
judgment,
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
72
|
+
state: graded.state,
|
|
73
|
+
judgment: graded.judgment,
|
|
74
|
+
code: graded.code
|
|
75
|
+
? {
|
|
76
|
+
...graded.code,
|
|
77
|
+
stdout: relative(context.directory, graded.code.stdout),
|
|
78
|
+
stderr: relative(context.directory, graded.code.stderr),
|
|
79
|
+
}
|
|
80
|
+
: undefined,
|
|
81
|
+
session: graded.session
|
|
82
|
+
? {
|
|
83
|
+
...graded.session,
|
|
84
|
+
database: relative(
|
|
85
|
+
context.directory,
|
|
86
|
+
resolve(directory, graded.session.database),
|
|
87
|
+
),
|
|
88
|
+
}
|
|
89
|
+
: undefined,
|
|
90
|
+
error: graded.error,
|
|
74
91
|
completedAt: new Date().toISOString(),
|
|
75
92
|
elapsedMs: Date.now() - Date.parse(run.startedAt),
|
|
76
93
|
};
|
|
@@ -9,7 +9,9 @@ import type {
|
|
|
9
9
|
ModelRef,
|
|
10
10
|
} from "../types";
|
|
11
11
|
import { CANDIDATE_TIMEOUT_MS } from "../types";
|
|
12
|
-
import {
|
|
12
|
+
import { rubricCriteria } from "../judgment";
|
|
13
|
+
import { compileCodeJudge } from "../infra/judging/code-source";
|
|
14
|
+
import { RUNTIME_IMAGE } from "../infra/opencode/version";
|
|
13
15
|
import { monitorPolicy } from "./monitor-policy";
|
|
14
16
|
import {
|
|
15
17
|
fingerprint,
|
|
@@ -41,9 +43,14 @@ async function declaration<T>(path: string): Promise<T> {
|
|
|
41
43
|
async function loadEval(directory: string): Promise<EvalDefinition> {
|
|
42
44
|
const id = basename(directory),
|
|
43
45
|
prompt = Bun.file(resolve(directory, "prompt.md")),
|
|
44
|
-
judge = Bun.file(resolve(directory, "judge.md"))
|
|
45
|
-
|
|
46
|
-
|
|
46
|
+
judge = Bun.file(resolve(directory, "judge.md")),
|
|
47
|
+
codeFile = resolve(directory, "judge.ts");
|
|
48
|
+
const hasMarkdown = await judge.exists(),
|
|
49
|
+
hasCode = await Bun.file(codeFile).exists();
|
|
50
|
+
if (!(await prompt.exists()) || (!hasMarkdown && !hasCode))
|
|
51
|
+
throw new Error(
|
|
52
|
+
`${id} requires prompt.md and at least one of judge.md or judge.ts`,
|
|
53
|
+
);
|
|
47
54
|
const settings = (await Bun.file(resolve(directory, "eval.ts")).exists())
|
|
48
55
|
? await declaration<Eval>(resolve(directory, "eval.ts"))
|
|
49
56
|
: {};
|
|
@@ -56,6 +63,10 @@ async function loadEval(directory: string): Promise<EvalDefinition> {
|
|
|
56
63
|
)
|
|
57
64
|
throw new Error(`${id}/eval.ts has unsupported settings`);
|
|
58
65
|
monitorPolicy(settings.earlyStop);
|
|
66
|
+
if (hasCode && settings.earlyStop)
|
|
67
|
+
throw new Error(
|
|
68
|
+
`${id}: judge.ts grades finalized recordings; earlyStop requires a Markdown-only judge`,
|
|
69
|
+
);
|
|
59
70
|
const source: unknown[] = [];
|
|
60
71
|
if (settings.workspace) {
|
|
61
72
|
const workspace = settings.workspace;
|
|
@@ -157,9 +168,10 @@ async function loadEval(directory: string): Promise<EvalDefinition> {
|
|
|
157
168
|
throw new Error(`${id}: preparation requires argv`);
|
|
158
169
|
}
|
|
159
170
|
const promptText = await prompt.text(),
|
|
160
|
-
judgeText = await judge.text();
|
|
161
|
-
if (!promptText.trim() || !judgeText.trim())
|
|
171
|
+
judgeText = hasMarkdown ? await judge.text() : "";
|
|
172
|
+
if (!promptText.trim() || (hasMarkdown && !judgeText.trim()))
|
|
162
173
|
throw new Error(`${id}: prompt and judge must not be empty`);
|
|
174
|
+
const code = hasCode ? await compileCodeJudge(codeFile) : undefined;
|
|
163
175
|
return {
|
|
164
176
|
id,
|
|
165
177
|
directory,
|
|
@@ -170,8 +182,9 @@ async function loadEval(directory: string): Promise<EvalDefinition> {
|
|
|
170
182
|
settings: { workspace: settings.workspace, prepare: settings.prepare },
|
|
171
183
|
source,
|
|
172
184
|
}),
|
|
173
|
-
judgeHash:
|
|
174
|
-
|
|
185
|
+
judgeHash: fingerprint({ rubric: judgeText, code: code?.hash }),
|
|
186
|
+
criteria: hasMarkdown ? rubricCriteria(judgeText) : [],
|
|
187
|
+
...(code ? { code } : {}),
|
|
175
188
|
name: /^# (.+)$/m.exec(judgeText)?.[1] ?? id,
|
|
176
189
|
};
|
|
177
190
|
}
|
|
@@ -187,10 +200,9 @@ export async function loadBenchmark(
|
|
|
187
200
|
if (
|
|
188
201
|
!definition ||
|
|
189
202
|
!Array.isArray(definition.models) ||
|
|
190
|
-
!definition.models.length
|
|
191
|
-
!definition.judge
|
|
203
|
+
!definition.models.length
|
|
192
204
|
)
|
|
193
|
-
throw new Error("benchmark.ts requires models
|
|
205
|
+
throw new Error("benchmark.ts requires models");
|
|
194
206
|
if (
|
|
195
207
|
Object.keys(definition).some(
|
|
196
208
|
(key) =>
|
|
@@ -207,6 +219,18 @@ export async function loadBenchmark(
|
|
|
207
219
|
)
|
|
208
220
|
throw new Error("benchmark.ts contains unsupported settings");
|
|
209
221
|
const models = definition.models.map(modelRef);
|
|
222
|
+
if (
|
|
223
|
+
definition.judge !== undefined &&
|
|
224
|
+
(!definition.judge ||
|
|
225
|
+
typeof definition.judge !== "object" ||
|
|
226
|
+
Array.isArray(definition.judge) ||
|
|
227
|
+
Object.keys(definition.judge).some(
|
|
228
|
+
(key) => !["model", "timeoutMs", "websearch"].includes(key),
|
|
229
|
+
))
|
|
230
|
+
)
|
|
231
|
+
throw new Error(
|
|
232
|
+
"judge must be an object with model, timeoutMs, or websearch settings",
|
|
233
|
+
);
|
|
210
234
|
if (new Set(models).size !== models.length)
|
|
211
235
|
throw new Error("Benchmark contains duplicate models");
|
|
212
236
|
const timeoutMs = positive(
|
|
@@ -224,6 +248,8 @@ export async function loadBenchmark(
|
|
|
224
248
|
const evals = await Promise.all(
|
|
225
249
|
directories.map((entry) => loadEval(resolve(evalRoot, entry.name))),
|
|
226
250
|
);
|
|
251
|
+
if (evals.some((item) => item.judge) && !definition.judge?.model)
|
|
252
|
+
throw new Error("A benchmark containing judge.md requires judge.model");
|
|
227
253
|
const concurrency = positive(definition.concurrency, 10, "Concurrency");
|
|
228
254
|
if (concurrency < 2 && evals.some((item) => item.settings.earlyStop))
|
|
229
255
|
throw new Error(
|
|
@@ -234,7 +260,7 @@ export async function loadBenchmark(
|
|
|
234
260
|
throw new Error("Container engine must be docker or podman");
|
|
235
261
|
for (const search of [
|
|
236
262
|
definition.candidate?.websearch,
|
|
237
|
-
definition.judge
|
|
263
|
+
definition.judge?.websearch,
|
|
238
264
|
])
|
|
239
265
|
if (search !== undefined && search !== false && search !== "exa")
|
|
240
266
|
throw new Error("Websearch must be exa or false");
|
|
@@ -253,13 +279,19 @@ export async function loadBenchmark(
|
|
|
253
279
|
: {}),
|
|
254
280
|
},
|
|
255
281
|
judge: {
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
282
|
+
...(definition.judge?.model
|
|
283
|
+
? { model: modelRef(definition.judge.model) }
|
|
284
|
+
: {}),
|
|
285
|
+
timeoutMs: positive(
|
|
286
|
+
definition.judge?.timeoutMs,
|
|
287
|
+
600_000,
|
|
288
|
+
"Judge timeout",
|
|
289
|
+
),
|
|
290
|
+
websearch: definition.judge?.websearch ?? "exa",
|
|
259
291
|
},
|
|
260
292
|
container: {
|
|
261
293
|
engine,
|
|
262
|
-
image: definition.container?.image ??
|
|
294
|
+
image: definition.container?.image ?? RUNTIME_IMAGE,
|
|
263
295
|
cpus: positive(definition.container?.cpus, 2, "CPU count"),
|
|
264
296
|
memoryMiB: positive(definition.container?.memoryMiB, 4096, "Memory"),
|
|
265
297
|
},
|
package/src/app/read-results.ts
CHANGED
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
} from "../runtime";
|
|
27
27
|
import { canJudgeEval } from "./eval-state";
|
|
28
28
|
import { CandidateEvidence, type EvidenceQuery } from "../infra/evidence";
|
|
29
|
+
import { contained } from "../infra/files";
|
|
29
30
|
|
|
30
31
|
const scheduled = (slot: Slot, benchmark: BenchmarkRun, now: number) =>
|
|
31
32
|
benchmark.state === "running" &&
|
|
@@ -33,6 +34,8 @@ const scheduled = (slot: Slot, benchmark: BenchmarkRun, now: number) =>
|
|
|
33
34
|
benchmark.scheduledSlotIds.includes(slot.id);
|
|
34
35
|
|
|
35
36
|
const costOf = (run: EvalRun | JudgeRun, results?: Results): Cost => {
|
|
37
|
+
if ("evalRunId" in run.input && run.input.kind === "code")
|
|
38
|
+
return { usd: 0, reportedUSD: 0, complete: true };
|
|
36
39
|
const usd = run.session?.accounting?.costUSD;
|
|
37
40
|
return {
|
|
38
41
|
usd: usd ?? null,
|
|
@@ -162,6 +165,21 @@ export class ResultReader {
|
|
|
162
165
|
using results = await this.database(benchmarkId);
|
|
163
166
|
const judge = results.judgeRun(judgeRunId);
|
|
164
167
|
if (!judge) throw new Error("Unknown judge run");
|
|
168
|
+
const candidate = results.evalRun(judge.input.evalRunId);
|
|
169
|
+
const criteria = new Map(
|
|
170
|
+
judge.input.criteria.map((criterion) => [criterion.id, criterion]),
|
|
171
|
+
);
|
|
172
|
+
for (const id of Object.keys(judge.judgment?.scores ?? {}))
|
|
173
|
+
if (!criteria.has(id))
|
|
174
|
+
criteria.set(id, { id, name: id.replaceAll("_", " ") });
|
|
175
|
+
const log = async (path?: string) =>
|
|
176
|
+
path
|
|
177
|
+
? (
|
|
178
|
+
await Bun.file(contained(dirname(results.path), path))
|
|
179
|
+
.text()
|
|
180
|
+
.catch(() => "")
|
|
181
|
+
).slice(0, 40_000)
|
|
182
|
+
: undefined;
|
|
165
183
|
return {
|
|
166
184
|
judge: {
|
|
167
185
|
id: judge.id,
|
|
@@ -172,8 +190,15 @@ export class ResultReader {
|
|
|
172
190
|
monitorErrorAt: judge.monitorErrorAt,
|
|
173
191
|
monitorLimit: judge.monitorLimit,
|
|
174
192
|
judgment: judge.judgment,
|
|
193
|
+
code: judge.code,
|
|
194
|
+
error: judge.error,
|
|
175
195
|
},
|
|
176
|
-
|
|
196
|
+
kind: judge.input.kind,
|
|
197
|
+
criteria: [...criteria.values()],
|
|
198
|
+
metrics: candidate?.metrics ?? judge.code?.metrics,
|
|
199
|
+
codeSource: judge.input.code?.source,
|
|
200
|
+
codeStdout: await log(judge.code?.stdout),
|
|
201
|
+
codeStderr: await log(judge.code?.stderr),
|
|
177
202
|
stop: results.evalRun(judge.input.evalRunId)?.stop,
|
|
178
203
|
checks: results.judgeChecks(judgeRunId).map((check) => {
|
|
179
204
|
const start = timestampMs(check.executionStartedAt);
|
|
@@ -421,6 +446,7 @@ function stageState(
|
|
|
421
446
|
runtime,
|
|
422
447
|
cost: costOf(run, results),
|
|
423
448
|
message: run.error,
|
|
449
|
+
...("kind" in run.input ? { kind: run.input.kind } : {}),
|
|
424
450
|
};
|
|
425
451
|
}
|
|
426
452
|
|
|
@@ -470,6 +496,13 @@ function liveRun(
|
|
|
470
496
|
evalStage.status,
|
|
471
497
|
);
|
|
472
498
|
const judgeStage: StageState = {
|
|
499
|
+
kind: benchmark.definition.evals.find((item) => item.id === slot.evalId)
|
|
500
|
+
?.code
|
|
501
|
+
? benchmark.definition.evals.find((item) => item.id === slot.evalId)
|
|
502
|
+
?.judge
|
|
503
|
+
? "hybrid"
|
|
504
|
+
: "code"
|
|
505
|
+
: "llm",
|
|
473
506
|
...stageState(
|
|
474
507
|
judge,
|
|
475
508
|
stale,
|
|
@@ -486,7 +519,7 @@ function liveRun(
|
|
|
486
519
|
...(judge?.state === "completed"
|
|
487
520
|
? {
|
|
488
521
|
score: judge.judgment?.value ?? null,
|
|
489
|
-
|
|
522
|
+
scores: judge.judgment?.scores,
|
|
490
523
|
}
|
|
491
524
|
: {}),
|
|
492
525
|
};
|
package/src/app/read-run.ts
CHANGED
|
@@ -4,6 +4,8 @@ import { CandidateEvidence } from "../infra/evidence";
|
|
|
4
4
|
import type { EvidenceRef, Judgment } from "../types";
|
|
5
5
|
import type { EvidenceView } from "../evidence";
|
|
6
6
|
import { validateCitations } from "../infra/judging/contract";
|
|
7
|
+
import { openRecording } from "../infra/recording";
|
|
8
|
+
import { tmpdir } from "node:os";
|
|
7
9
|
|
|
8
10
|
/** Read-only records for maintenance clients. Finalized evidence remains authoritative. */
|
|
9
11
|
export function readBenchmarkRun(directory: string) {
|
|
@@ -29,7 +31,26 @@ export async function readEvidence(
|
|
|
29
31
|
return (await CandidateEvidence.open(ref.directory, ref.hash)).view(ref);
|
|
30
32
|
}
|
|
31
33
|
|
|
32
|
-
/**
|
|
34
|
+
/** Open all recorded data with lazy native readers; dispose after inspection. */
|
|
35
|
+
export async function readRecording(directory: string, evalRunId: string) {
|
|
36
|
+
using results = new Results(resolve(directory, "runner.db"), true);
|
|
37
|
+
const run = results.evalRun(evalRunId);
|
|
38
|
+
if (!run?.evidence)
|
|
39
|
+
throw new Error("Select an EvalRun with finalized evidence");
|
|
40
|
+
return openRecording(
|
|
41
|
+
{
|
|
42
|
+
run,
|
|
43
|
+
runDirectory: resolve(directory),
|
|
44
|
+
evidence: {
|
|
45
|
+
...run.evidence,
|
|
46
|
+
directory: resolve(directory, run.evidence.directory),
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
process.platform === "win32" ? "C:/tmp/opencode" : tmpdir(),
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Reference integrity only; interpretation of task facts stays in the judges. */
|
|
33
54
|
export async function verifyJudgmentEvidence(
|
|
34
55
|
judgment: Judgment,
|
|
35
56
|
reference: EvidenceRef,
|
|
@@ -38,6 +59,6 @@ export async function verifyJudgmentEvidence(
|
|
|
38
59
|
await validateCitations(judgment, evidence);
|
|
39
60
|
return {
|
|
40
61
|
hash: evidence.checkpoint.hash,
|
|
41
|
-
|
|
62
|
+
criteria: Object.keys(judgment.scores).length,
|
|
42
63
|
};
|
|
43
64
|
}
|
package/src/app/rejudge.ts
CHANGED
package/src/app/run-benchmark.ts
CHANGED
|
@@ -20,6 +20,7 @@ import { judgeEvalRun } from "./judge-run";
|
|
|
20
20
|
import type { ExecutionContext } from "./context";
|
|
21
21
|
import { canJudgeEval } from "./eval-state";
|
|
22
22
|
import { isScored } from "../judgment";
|
|
23
|
+
import { benchmarkScores } from "./scores";
|
|
23
24
|
import { CostBudget, estimateWork } from "./cost-plan";
|
|
24
25
|
|
|
25
26
|
export type RunBenchmarkOptions = {
|
|
@@ -79,12 +80,18 @@ export function finishBenchmark(context: ExecutionContext) {
|
|
|
79
80
|
judges.some(
|
|
80
81
|
(run) => run.id === slot.judgeRunId && isScored(run.judgment?.value),
|
|
81
82
|
),
|
|
83
|
+
) &&
|
|
84
|
+
benchmarkScores(context.definition, slots, judges).every(
|
|
85
|
+
(score) => score.percentage !== null,
|
|
82
86
|
);
|
|
87
|
+
// Scoped collection deliberately retains older selected executions elsewhere.
|
|
88
|
+
// Only admitted work must match this invocation's runtime and judge inputs.
|
|
89
|
+
const admitted = new Set(context.results.benchmark!.scheduledSlotIds);
|
|
83
90
|
const currentInputs = planBenchmark(
|
|
84
91
|
context.definition,
|
|
85
92
|
context.runtime,
|
|
86
93
|
context.results,
|
|
87
|
-
).every((item) => item.action === "reuse");
|
|
94
|
+
).every((item) => !admitted.has(item.slot.id) || item.action === "reuse");
|
|
88
95
|
const previous = context.results.benchmark!;
|
|
89
96
|
const ended = [...executions, ...judges]
|
|
90
97
|
.map((run) => run.completedAt)
|
|
@@ -67,6 +67,7 @@ export async function runEvalPipeline(
|
|
|
67
67
|
evalRunId: candidate!.id,
|
|
68
68
|
evidence: saved,
|
|
69
69
|
rubric: definition.judge,
|
|
70
|
+
kind: "llm",
|
|
70
71
|
agent: JUDGE_AGENT,
|
|
71
72
|
model: context.definition.judge.model,
|
|
72
73
|
judgeHash: slot.judgeHash,
|
|
@@ -75,7 +76,7 @@ export async function runEvalPipeline(
|
|
|
75
76
|
mode: "monitor",
|
|
76
77
|
runtimeHash: context.runtime.judgeHash,
|
|
77
78
|
protocol: JUDGE_PROTOCOL,
|
|
78
|
-
|
|
79
|
+
criteria: definition.criteria,
|
|
79
80
|
monitor: policy,
|
|
80
81
|
});
|
|
81
82
|
return saved;
|
package/src/app/run-eval.ts
CHANGED
|
@@ -3,6 +3,8 @@ import type { Slot, EvalRun } from "../types";
|
|
|
3
3
|
import type { ExecutionContext } from "./context";
|
|
4
4
|
import { executeCandidate } from "../infra/containers/candidate";
|
|
5
5
|
import type { EvidenceFeed } from "../evidence";
|
|
6
|
+
import { CandidateEvidence } from "../infra/evidence";
|
|
7
|
+
import { measureRecording } from "../infra/recording/metrics";
|
|
6
8
|
|
|
7
9
|
/** The normal candidate use case: record its inputs, execute, finalize its evidence. */
|
|
8
10
|
export async function runEval(
|
|
@@ -88,6 +90,17 @@ export async function runEval(
|
|
|
88
90
|
}
|
|
89
91
|
: undefined,
|
|
90
92
|
};
|
|
93
|
+
if (completed.evidence) {
|
|
94
|
+
const evidence = await CandidateEvidence.open(
|
|
95
|
+
resolve(context.directory, completed.evidence.directory),
|
|
96
|
+
completed.evidence.hash,
|
|
97
|
+
);
|
|
98
|
+
completed.metrics = measureRecording(
|
|
99
|
+
evidence.rawEvents(),
|
|
100
|
+
evidence.toolCalls(),
|
|
101
|
+
completed.evidence,
|
|
102
|
+
);
|
|
103
|
+
}
|
|
91
104
|
context.results.finishEval(completed);
|
|
92
105
|
return completed;
|
|
93
106
|
}
|
package/src/app/scores.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { BenchmarkDefinition, JudgeRun, Slot } from "../types";
|
|
2
2
|
import { modelScore } from "../view";
|
|
3
|
+
import { isScored } from "../judgment";
|
|
3
4
|
|
|
4
5
|
/** Scores are derived from a single selection snapshot; every eval has equal weight. */
|
|
5
6
|
export function benchmarkScores(
|
|
@@ -9,46 +10,87 @@ export function benchmarkScores(
|
|
|
9
10
|
evalId?: string,
|
|
10
11
|
) {
|
|
11
12
|
const index = new Map(judges.map((run) => [run.id, run]));
|
|
13
|
+
const evals = definition.evals.filter(
|
|
14
|
+
(item) => !evalId || item.id === evalId,
|
|
15
|
+
);
|
|
16
|
+
const definitions = new Map(
|
|
17
|
+
evals.map((item) => {
|
|
18
|
+
const criteria = new Map(
|
|
19
|
+
item.criteria.map((criterion) => [criterion.id, criterion]),
|
|
20
|
+
);
|
|
21
|
+
for (const slot of slots.filter(
|
|
22
|
+
(slot) => slot.active && slot.evalId === item.id,
|
|
23
|
+
)) {
|
|
24
|
+
const judgment = slot.judgeRunId
|
|
25
|
+
? index.get(slot.judgeRunId)?.judgment
|
|
26
|
+
: undefined;
|
|
27
|
+
for (const id of Object.keys(judgment?.scores ?? {}))
|
|
28
|
+
if (!criteria.has(id))
|
|
29
|
+
criteria.set(id, { id, name: id.replaceAll("_", " ") });
|
|
30
|
+
}
|
|
31
|
+
return [item.id, [...criteria.values()]];
|
|
32
|
+
}),
|
|
33
|
+
);
|
|
12
34
|
return definition.models.map((model) =>
|
|
13
35
|
modelScore(
|
|
14
36
|
model,
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
.
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
37
|
+
evals.flatMap((item) => {
|
|
38
|
+
const criteria = definitions.get(item.id)!;
|
|
39
|
+
const selected = slots.filter(
|
|
40
|
+
(slot) =>
|
|
41
|
+
slot.active && slot.evalId === item.id && slot.model === model,
|
|
42
|
+
);
|
|
43
|
+
return criteria.map((criterion) => {
|
|
44
|
+
const values = selected
|
|
45
|
+
.map(
|
|
46
|
+
(slot) =>
|
|
47
|
+
(slot.judgeRunId
|
|
48
|
+
? index.get(slot.judgeRunId)?.judgment?.scores
|
|
49
|
+
: undefined)?.[criterion.id]?.value,
|
|
50
|
+
)
|
|
51
|
+
.filter(isScored);
|
|
52
|
+
const scoredSum = values.reduce<number>(
|
|
53
|
+
(sum, value) => sum + value,
|
|
54
|
+
0,
|
|
22
55
|
);
|
|
23
|
-
return
|
|
24
|
-
|
|
25
|
-
|
|
56
|
+
return {
|
|
57
|
+
eval: item.id,
|
|
58
|
+
criterion: criterion.id,
|
|
59
|
+
name: criterion.name,
|
|
60
|
+
value:
|
|
61
|
+
values.length === definition.repetitions
|
|
62
|
+
? scoredSum / definition.repetitions
|
|
63
|
+
: null,
|
|
64
|
+
scored: values.length,
|
|
65
|
+
expected: definition.repetitions,
|
|
66
|
+
passed: values.filter((value) => value === 1).length,
|
|
67
|
+
scoredSum,
|
|
68
|
+
};
|
|
69
|
+
});
|
|
70
|
+
}),
|
|
71
|
+
evals.map((item) => item.id),
|
|
72
|
+
evals
|
|
73
|
+
.filter(
|
|
74
|
+
(item) =>
|
|
75
|
+
item.code &&
|
|
76
|
+
slots
|
|
77
|
+
.filter(
|
|
26
78
|
(slot) =>
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
)?.find((value) => value.id === metric.id)?.value,
|
|
79
|
+
slot.active &&
|
|
80
|
+
slot.evalId === item.id &&
|
|
81
|
+
slot.model === model,
|
|
31
82
|
)
|
|
32
|
-
.
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
? scoredSum / definition.repetitions
|
|
44
|
-
: null,
|
|
45
|
-
scored: values.length,
|
|
46
|
-
expected: definition.repetitions,
|
|
47
|
-
passed: scoredSum,
|
|
48
|
-
scoredSum,
|
|
49
|
-
};
|
|
50
|
-
});
|
|
51
|
-
}),
|
|
83
|
+
.some((slot) => {
|
|
84
|
+
const judge = slot.judgeRunId
|
|
85
|
+
? index.get(slot.judgeRunId)
|
|
86
|
+
: undefined;
|
|
87
|
+
return (
|
|
88
|
+
judge?.code?.state !== "completed" ||
|
|
89
|
+
judge.input.code?.hash !== item.code!.hash
|
|
90
|
+
);
|
|
91
|
+
}),
|
|
92
|
+
)
|
|
93
|
+
.map((item) => item.id),
|
|
52
94
|
),
|
|
53
95
|
);
|
|
54
96
|
}
|
package/src/app/serve-results.ts
CHANGED
|
@@ -66,6 +66,8 @@ export async function serveResults(options: {
|
|
|
66
66
|
if (value !== null) query[key] = Number(value);
|
|
67
67
|
}
|
|
68
68
|
const revision = url.searchParams.get("revision");
|
|
69
|
+
if (url.searchParams.has("metric"))
|
|
70
|
+
query.metric = url.searchParams.get("metric")!;
|
|
69
71
|
if (revision === "initial" || revision === "final")
|
|
70
72
|
query.revision = revision;
|
|
71
73
|
return Response.json(
|
package/src/evidence.ts
CHANGED
|
@@ -11,7 +11,10 @@ export type EvidenceQuery = {
|
|
|
11
11
|
| "tool"
|
|
12
12
|
| "artifacts"
|
|
13
13
|
| "artifact"
|
|
14
|
-
| "diff"
|
|
14
|
+
| "diff"
|
|
15
|
+
| "metrics";
|
|
16
|
+
/** A dot-separated measurement path for the metrics query. */
|
|
17
|
+
metric?: string;
|
|
15
18
|
sessionID?: string;
|
|
16
19
|
type?: string;
|
|
17
20
|
id?: string;
|
package/src/index.ts
CHANGED
|
@@ -8,8 +8,8 @@ export type {
|
|
|
8
8
|
EvalRun,
|
|
9
9
|
JudgeRun,
|
|
10
10
|
Judgment,
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
CriterionDefinition,
|
|
12
|
+
CriterionScore,
|
|
13
13
|
EvidenceCitation,
|
|
14
14
|
ToolCall,
|
|
15
15
|
MonitorPolicy,
|
|
@@ -30,7 +30,18 @@ export type {
|
|
|
30
30
|
JudgeSession,
|
|
31
31
|
} from "./evidence";
|
|
32
32
|
export { CANDIDATE_TIMEOUT_MS } from "./types";
|
|
33
|
-
export {
|
|
33
|
+
export { rubricCriteria, criterionMean, isScored } from "./judgment";
|
|
34
|
+
export type {
|
|
35
|
+
JudgeContext,
|
|
36
|
+
JudgeFunction,
|
|
37
|
+
JsonValue,
|
|
38
|
+
ScoreValue,
|
|
39
|
+
RunMetrics,
|
|
40
|
+
ToolMetrics,
|
|
41
|
+
RecordedEvent,
|
|
42
|
+
RecordedMessage,
|
|
43
|
+
RecordedFile,
|
|
44
|
+
} from "./judge-context";
|
|
34
45
|
export { loadBenchmark } from "./app/load-benchmark";
|
|
35
46
|
export {
|
|
36
47
|
runBenchmark,
|
|
@@ -47,6 +58,7 @@ export { judgeEvidence, recordEvidence } from "./app/judge-evidence";
|
|
|
47
58
|
export {
|
|
48
59
|
readBenchmarkRun,
|
|
49
60
|
readEvidence,
|
|
61
|
+
readRecording,
|
|
50
62
|
verifyJudgmentEvidence,
|
|
51
63
|
} from "./app/read-run";
|
|
52
64
|
export { snapshotBenchmarkRun } from "./app/snapshot-run";
|
|
@@ -9,7 +9,7 @@ const runtime = Effect.runFork(
|
|
|
9
9
|
hostname: "0.0.0.0",
|
|
10
10
|
port: 4096,
|
|
11
11
|
password: process.env.OPENCODE_SERVER_PASSWORD,
|
|
12
|
-
app: { name: "opencode", version: "
|
|
12
|
+
app: { name: "opencode", version: "2.0.3" },
|
|
13
13
|
database: { path: "/home/dev/.local/share/opencode/opencode.db" },
|
|
14
14
|
events: { persist: true },
|
|
15
15
|
});
|