evalkit-py 0.2.0.dev12__py3-none-any.whl
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.
- evalkit/__init__.py +0 -0
- evalkit/api/__init__.py +0 -0
- evalkit/api/access.py +74 -0
- evalkit/api/agents.py +159 -0
- evalkit/api/app.py +360 -0
- evalkit/api/auth.py +225 -0
- evalkit/api/cli_dist.py +10 -0
- evalkit/api/datasets.py +169 -0
- evalkit/api/deps.py +79 -0
- evalkit/api/download.py +15 -0
- evalkit/api/evaluators.py +121 -0
- evalkit/api/experiments.py +616 -0
- evalkit/api/fixtures.py +95 -0
- evalkit/api/harness_check.py +48 -0
- evalkit/api/imports.py +180 -0
- evalkit/api/limits.py +84 -0
- evalkit/api/llm_providers.py +120 -0
- evalkit/api/mcp/__init__.py +0 -0
- evalkit/api/mcp/auth.py +100 -0
- evalkit/api/mcp/descriptions.py +336 -0
- evalkit/api/mcp/models.py +259 -0
- evalkit/api/mcp/server.py +101 -0
- evalkit/api/mcp/tools.py +844 -0
- evalkit/api/meta.py +19 -0
- evalkit/api/otel.py +350 -0
- evalkit/api/pull.py +197 -0
- evalkit/api/review.py +271 -0
- evalkit/api/runs.py +555 -0
- evalkit/api/server_keys.py +44 -0
- evalkit/api/tokens.py +51 -0
- evalkit/api/trace_imports.py +173 -0
- evalkit/api/trace_sources.py +49 -0
- evalkit/api/traces.py +196 -0
- evalkit/api/users.py +72 -0
- evalkit/api/workspaces.py +131 -0
- evalkit/bundled/contracts/atif-accepted.json +5 -0
- evalkit/bundled/contracts/custom-evaluator-api-v1.json +243 -0
- evalkit/bundled/contracts/harness-api-v1.json +938 -0
- evalkit/bundled/contracts/span-mapping-v1.json +314 -0
- evalkit/bundled/docs/concepts.md +26 -0
- evalkit/bundled/docs/config.md +38 -0
- evalkit/bundled/docs/harness-api.md +44 -0
- evalkit/bundled/docs/install.md +60 -0
- evalkit/bundled/docs/login.md +36 -0
- evalkit/bundled/docs/mappings.md +136 -0
- evalkit/bundled/docs/mcp.md +44 -0
- evalkit/bundled/docs/quickstart.md +142 -0
- evalkit/bundled/docs/sdk-runs.md +171 -0
- evalkit/bundled/docs/tracing.md +171 -0
- evalkit/bundled/docs/try-locally.md +288 -0
- evalkit/bundled/docs/workspaces.md +45 -0
- evalkit/bundled/skills/authoring-eval-cases/SKILL.md +387 -0
- evalkit/bundled/skills/authoring-eval-cases/references/overlays.md +83 -0
- evalkit/bundled/skills/authoring-eval-cases/references/tool-checks.md +71 -0
- evalkit/bundled/skills/connect-traces/SKILL.md +296 -0
- evalkit/bundled/skills/connect-traces/references/gen_ai.json +42 -0
- evalkit/bundled/skills/connect-traces/references/openinference.json +35 -0
- evalkit/bundled/skills/connect-traces/references/span-mapping-schema.md +88 -0
- evalkit/bundled/skills/eval-driven-development/SKILL.md +458 -0
- evalkit/bundled/skills/eval-driven-development/references/analyze-run.md +97 -0
- evalkit/bundled/skills/eval-driven-development/references/diagnose.md +130 -0
- evalkit/bundled/skills/eval-driven-development/references/synthesize-cases.md +87 -0
- evalkit/bundled/skills/eval-driven-development/references/synthesize.md +165 -0
- evalkit/bundled/skills/evalkit-deployment/SKILL.md +332 -0
- evalkit/bundled/skills/evalkit-deployment/config.example.yaml +49 -0
- evalkit/bundled/skills/evalkit-deployment/docker-compose.yml +51 -0
- evalkit/bundled/skills/evalkit-deployment/references/config-and-deploy.md +333 -0
- evalkit/bundled/skills/evalkit-deployment/references/harness-api.md +139 -0
- evalkit/bundled/skills/evalkit-deployment/try/.env.example +11 -0
- evalkit/bundled/skills/evalkit-deployment/try/agent.py +131 -0
- evalkit/bundled/skills/evalkit-deployment/try/cases/acme/balance-sheet/cases.yaml +25 -0
- evalkit/bundled/skills/evalkit-deployment/try/cases/acme/income-statement/cases.yaml +43 -0
- evalkit/bundled/skills/evalkit-deployment/try/config.yaml +17 -0
- evalkit/bundled/skills/evalkit-deployment/try/docker-compose.yml +82 -0
- evalkit/bundled/skills/evalkit-deployment/try/evalkit.yaml +4 -0
- evalkit/bundled/skills/evalkit-deployment/try/mapping.json +42 -0
- evalkit/bundled/skills/evalkit-deployment/try/run.py +33 -0
- evalkit/bundled/skills/evalkit-usage/SKILL.md +509 -0
- evalkit/bundled/skills/evalkit-usage/references/suite-fixtures.md +42 -0
- evalkit/bundled/ui/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
- evalkit/bundled/ui/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
- evalkit/bundled/ui/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
- evalkit/bundled/ui/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
- evalkit/bundled/ui/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
- evalkit/bundled/ui/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
- evalkit/bundled/ui/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
- evalkit/bundled/ui/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
- evalkit/bundled/ui/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
- evalkit/bundled/ui/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
- evalkit/bundled/ui/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
- evalkit/bundled/ui/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
- evalkit/bundled/ui/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
- evalkit/bundled/ui/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
- evalkit/bundled/ui/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
- evalkit/bundled/ui/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
- evalkit/bundled/ui/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
- evalkit/bundled/ui/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
- evalkit/bundled/ui/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
- evalkit/bundled/ui/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
- evalkit/bundled/ui/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
- evalkit/bundled/ui/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
- evalkit/bundled/ui/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
- evalkit/bundled/ui/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
- evalkit/bundled/ui/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
- evalkit/bundled/ui/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
- evalkit/bundled/ui/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
- evalkit/bundled/ui/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
- evalkit/bundled/ui/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
- evalkit/bundled/ui/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
- evalkit/bundled/ui/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
- evalkit/bundled/ui/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
- evalkit/bundled/ui/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
- evalkit/bundled/ui/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
- evalkit/bundled/ui/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
- evalkit/bundled/ui/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
- evalkit/bundled/ui/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
- evalkit/bundled/ui/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
- evalkit/bundled/ui/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
- evalkit/bundled/ui/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
- evalkit/bundled/ui/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
- evalkit/bundled/ui/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
- evalkit/bundled/ui/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
- evalkit/bundled/ui/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
- evalkit/bundled/ui/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
- evalkit/bundled/ui/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
- evalkit/bundled/ui/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
- evalkit/bundled/ui/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
- evalkit/bundled/ui/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
- evalkit/bundled/ui/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
- evalkit/bundled/ui/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
- evalkit/bundled/ui/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
- evalkit/bundled/ui/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
- evalkit/bundled/ui/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
- evalkit/bundled/ui/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
- evalkit/bundled/ui/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
- evalkit/bundled/ui/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
- evalkit/bundled/ui/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
- evalkit/bundled/ui/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
- evalkit/bundled/ui/assets/abap-08VXUWAP.js +1 -0
- evalkit/bundled/ui/assets/apex-BWPQTe0t.js +1 -0
- evalkit/bundled/ui/assets/azcli-Bc_sGQ0U.js +1 -0
- evalkit/bundled/ui/assets/bat-i0X4ZdIN.js +1 -0
- evalkit/bundled/ui/assets/bicep-B5-_aFwp.js +2 -0
- evalkit/bundled/ui/assets/cameligo-DMUM7wLl.js +1 -0
- evalkit/bundled/ui/assets/clojure-Cm7r79vr.js +1 -0
- evalkit/bundled/ui/assets/codicon-DF1abBS2.ttf +0 -0
- evalkit/bundled/ui/assets/coffee-Ba7i2nA0.js +1 -0
- evalkit/bundled/ui/assets/cpp-C7h46wYY.js +1 -0
- evalkit/bundled/ui/assets/csharp-BKxtCVv1.js +1 -0
- evalkit/bundled/ui/assets/csp-bTuwJoIa.js +1 -0
- evalkit/bundled/ui/assets/css-DIMkf-bt.js +3 -0
- evalkit/bundled/ui/assets/css.worker-DwfUZ3fB.js +96 -0
- evalkit/bundled/ui/assets/cssMode-C97l1pbB.js +1 -0
- evalkit/bundled/ui/assets/cypher-CVaqCwHa.js +1 -0
- evalkit/bundled/ui/assets/dart-onAF5SnQ.js +1 -0
- evalkit/bundled/ui/assets/dockerfile-DZFCIeNp.js +1 -0
- evalkit/bundled/ui/assets/ecl-94qehhXe.js +1 -0
- evalkit/bundled/ui/assets/editor-DjpsE8to.css +1 -0
- evalkit/bundled/ui/assets/editor.api-NTesIxw5.js +908 -0
- evalkit/bundled/ui/assets/editor.worker-CRz__AKW.js +33 -0
- evalkit/bundled/ui/assets/elixir-6RTg0lbw.js +1 -0
- evalkit/bundled/ui/assets/flow9-C5_-GSwl.js +1 -0
- evalkit/bundled/ui/assets/freemarker2-BWqEFagp.js +3 -0
- evalkit/bundled/ui/assets/fsharp-C8Ef5oNN.js +1 -0
- evalkit/bundled/ui/assets/go-C-y9NEjX.js +1 -0
- evalkit/bundled/ui/assets/graphql-fmXr3nnJ.js +1 -0
- evalkit/bundled/ui/assets/handlebars-DkIF-whN.js +1 -0
- evalkit/bundled/ui/assets/hcl-CpzslTdj.js +1 -0
- evalkit/bundled/ui/assets/html-C_rpeQXJ.js +1 -0
- evalkit/bundled/ui/assets/html.worker-D9ugpX4y.js +509 -0
- evalkit/bundled/ui/assets/htmlMode-B0YIq3DF.js +1 -0
- evalkit/bundled/ui/assets/index-Cw-jBH6M.js +736 -0
- evalkit/bundled/ui/assets/index-DKtoQZtm.css +1 -0
- evalkit/bundled/ui/assets/ini-sBoK_t0W.js +1 -0
- evalkit/bundled/ui/assets/java-BEtHBSE6.js +1 -0
- evalkit/bundled/ui/assets/javascript-D4EGhYA6.js +1 -0
- evalkit/bundled/ui/assets/jetbrains-mono-cyrillic-wght-normal-D73BlboJ.woff2 +0 -0
- evalkit/bundled/ui/assets/jetbrains-mono-greek-wght-normal-Bw9x6K1M.woff2 +0 -0
- evalkit/bundled/ui/assets/jetbrains-mono-latin-ext-wght-normal-DBQx-q_a.woff2 +0 -0
- evalkit/bundled/ui/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2 +0 -0
- evalkit/bundled/ui/assets/jetbrains-mono-vietnamese-wght-normal-Bt-aOZkq.woff2 +0 -0
- evalkit/bundled/ui/assets/json.worker-CV9fqkUp.js +65 -0
- evalkit/bundled/ui/assets/jsonMode-pQjSfIH6.js +7 -0
- evalkit/bundled/ui/assets/julia-Bri6UV-V.js +1 -0
- evalkit/bundled/ui/assets/kotlin-BOotOW0E.js +1 -0
- evalkit/bundled/ui/assets/less-B9JPFI3C.js +2 -0
- evalkit/bundled/ui/assets/lexon-CfSJPG6W.js +1 -0
- evalkit/bundled/ui/assets/liquid-DbrvoYjD.js +1 -0
- evalkit/bundled/ui/assets/lspLanguageFeatures-D0B7cH6y.js +4 -0
- evalkit/bundled/ui/assets/lua-CsQS60Ue.js +1 -0
- evalkit/bundled/ui/assets/m3-D-oSqn_W.js +1 -0
- evalkit/bundled/ui/assets/markdown-Cimd5fb3.js +1 -0
- evalkit/bundled/ui/assets/mdx-B0HrujLR.js +1 -0
- evalkit/bundled/ui/assets/mips-CIPQ_RoX.js +1 -0
- evalkit/bundled/ui/assets/monacoEditor-DsoPncxW.js +68 -0
- evalkit/bundled/ui/assets/monacoEditor-n7clI0si.css +1 -0
- evalkit/bundled/ui/assets/msdax-DauUninz.js +1 -0
- evalkit/bundled/ui/assets/mysql-SOo6toE5.js +1 -0
- evalkit/bundled/ui/assets/objective-c-FvmIjYaQ.js +1 -0
- evalkit/bundled/ui/assets/pascal-DrH0SRf2.js +1 -0
- evalkit/bundled/ui/assets/pascaligo-D-ptJ9y-.js +1 -0
- evalkit/bundled/ui/assets/perl-oz_6vUea.js +1 -0
- evalkit/bundled/ui/assets/pgsql-DTj74zXo.js +1 -0
- evalkit/bundled/ui/assets/php-nr791fC2.js +1 -0
- evalkit/bundled/ui/assets/pla-CopQ2nXW.js +1 -0
- evalkit/bundled/ui/assets/postiats-43DmfD33.js +1 -0
- evalkit/bundled/ui/assets/powerquery-D3hlyOfw.js +1 -0
- evalkit/bundled/ui/assets/powershell-DmHpPYUd.js +1 -0
- evalkit/bundled/ui/assets/protobuf-C531GsRP.js +2 -0
- evalkit/bundled/ui/assets/pug-Z5eAx3Zn.js +1 -0
- evalkit/bundled/ui/assets/python-CieHVM9n.js +1 -0
- evalkit/bundled/ui/assets/qsharp-DkqhCAOL.js +1 -0
- evalkit/bundled/ui/assets/r-BwWrilGY.js +1 -0
- evalkit/bundled/ui/assets/razor-sLewSMyp.js +1 -0
- evalkit/bundled/ui/assets/redis-ClamHrr6.js +1 -0
- evalkit/bundled/ui/assets/redshift-DT7zqm-g.js +1 -0
- evalkit/bundled/ui/assets/restructuredtext-BYgofb2h.js +1 -0
- evalkit/bundled/ui/assets/ruby-DezsRK8O.js +1 -0
- evalkit/bundled/ui/assets/rust-DdL9SqIa.js +1 -0
- evalkit/bundled/ui/assets/sb-CcwsVR0C.js +1 -0
- evalkit/bundled/ui/assets/scala-DHpiXF5c.js +1 -0
- evalkit/bundled/ui/assets/scheme-BeGwcela.js +1 -0
- evalkit/bundled/ui/assets/scss-gp-XZpBa.js +3 -0
- evalkit/bundled/ui/assets/shell-CC2rA5mh.js +1 -0
- evalkit/bundled/ui/assets/solidity-BEEn4gHE.js +1 -0
- evalkit/bundled/ui/assets/sophia-CRfGWb83.js +1 -0
- evalkit/bundled/ui/assets/sparql-D_Lu-MrJ.js +1 -0
- evalkit/bundled/ui/assets/sql-NEE52Syq.js +1 -0
- evalkit/bundled/ui/assets/st-DbInun42.js +1 -0
- evalkit/bundled/ui/assets/swift-Bxkupp3x.js +1 -0
- evalkit/bundled/ui/assets/systemverilog-Bz4Y3fRF.js +1 -0
- evalkit/bundled/ui/assets/tcl-DISqw1ZD.js +1 -0
- evalkit/bundled/ui/assets/ts.worker-DIAQkoB3.js +67741 -0
- evalkit/bundled/ui/assets/tsMode-BcVQ1nu0.js +11 -0
- evalkit/bundled/ui/assets/twig-De2hgUGE.js +1 -0
- evalkit/bundled/ui/assets/typescript-CUvNWd9p.js +1 -0
- evalkit/bundled/ui/assets/typespec-B8J7ngcE.js +1 -0
- evalkit/bundled/ui/assets/vb-DV3o63ZY.js +1 -0
- evalkit/bundled/ui/assets/wgsl-DpFanUEy.js +298 -0
- evalkit/bundled/ui/assets/workers-CwpYQtzp.js +1 -0
- evalkit/bundled/ui/assets/xml-C1HnXMdF.js +1 -0
- evalkit/bundled/ui/assets/yaml-D7Ey3HQu.js +1 -0
- evalkit/bundled/ui/favicon.svg +6 -0
- evalkit/bundled/ui/index.html +14 -0
- evalkit/cli/__init__.py +0 -0
- evalkit/cli/admin_commands.py +590 -0
- evalkit/cli/client.py +67 -0
- evalkit/cli/init_server.py +165 -0
- evalkit/cli/local_run.py +195 -0
- evalkit/cli/main.py +1365 -0
- evalkit/cli/runs_commands.py +539 -0
- evalkit/cli/server_commands.py +130 -0
- evalkit/cli/skill_targets.py +43 -0
- evalkit/cli/tokens_commands.py +52 -0
- evalkit/cli/traces_commands.py +332 -0
- evalkit/cli/update.py +85 -0
- evalkit/client/__init__.py +0 -0
- evalkit/client/agents.py +25 -0
- evalkit/client/api.py +193 -0
- evalkit/client/blob.py +104 -0
- evalkit/client/connection.py +88 -0
- evalkit/client/evaluators.py +11 -0
- evalkit/client/sessions.py +17 -0
- evalkit/client/store.py +139 -0
- evalkit/client/summary.py +46 -0
- evalkit/client/sync.py +198 -0
- evalkit/config.py +227 -0
- evalkit/contracts/__init__.py +0 -0
- evalkit/contracts/atif.py +115 -0
- evalkit/contracts/cases.py +456 -0
- evalkit/contracts/custom_evaluator.py +58 -0
- evalkit/contracts/enums.py +86 -0
- evalkit/contracts/esf.py +120 -0
- evalkit/contracts/harness.py +281 -0
- evalkit/contracts/notes.json +4 -0
- evalkit/contracts/notes.py +7 -0
- evalkit/contracts/settings.py +41 -0
- evalkit/core/__init__.py +0 -0
- evalkit/core/access.py +75 -0
- evalkit/core/agents.py +65 -0
- evalkit/core/blob.py +46 -0
- evalkit/core/capabilities.py +36 -0
- evalkit/core/costmap.py +90 -0
- evalkit/core/custom_eval.py +50 -0
- evalkit/core/data/cost_map.json +81546 -0
- evalkit/core/endpoints.py +31 -0
- evalkit/core/evaluators/__init__.py +29 -0
- evalkit/core/evaluators/base.py +121 -0
- evalkit/core/evaluators/gauges.py +84 -0
- evalkit/core/evaluators/presentation.py +166 -0
- evalkit/core/evaluators/response_time.py +21 -0
- evalkit/core/evaluators/rubrics.py +494 -0
- evalkit/core/evaluators/tool_trajectory.py +187 -0
- evalkit/core/evaluators/trajectory_rubrics.py +198 -0
- evalkit/core/fixtures.py +57 -0
- evalkit/core/harness.py +227 -0
- evalkit/core/harness_check.py +191 -0
- evalkit/core/importer.py +257 -0
- evalkit/core/judge.py +78 -0
- evalkit/core/lint.py +265 -0
- evalkit/core/overlays.py +76 -0
- evalkit/core/paths.py +25 -0
- evalkit/core/promotion.py +94 -0
- evalkit/core/pull.py +198 -0
- evalkit/core/python_eval.py +59 -0
- evalkit/core/remote_evaluators.py +32 -0
- evalkit/core/review.py +220 -0
- evalkit/core/run_digest.py +421 -0
- evalkit/core/runner.py +995 -0
- evalkit/core/scorecard.py +255 -0
- evalkit/core/traces/__init__.py +0 -0
- evalkit/core/traces/convert.py +336 -0
- evalkit/core/traces/importer.py +113 -0
- evalkit/core/traces/mapping.py +385 -0
- evalkit/core/traces/poll.py +38 -0
- evalkit/core/traces/raw.py +21 -0
- evalkit/core/traces/sessions.py +172 -0
- evalkit/core/traces/settle.py +14 -0
- evalkit/core/traces/sources/__init__.py +19 -0
- evalkit/core/traces/sources/base.py +25 -0
- evalkit/core/traces/sources/phoenix.py +121 -0
- evalkit/core/trajectory.py +248 -0
- evalkit/core/workspace.py +30 -0
- evalkit/credentials.py +119 -0
- evalkit/db/__init__.py +0 -0
- evalkit/db/agents.py +289 -0
- evalkit/db/alembic/env.py +90 -0
- evalkit/db/alembic/script.py.mako +28 -0
- evalkit/db/alembic/versions/0011_baseline.py +1002 -0
- evalkit/db/alembic/versions/0012_ticket_token_kind.py +27 -0
- evalkit/db/alembic/versions/0013_keys_and_llm_providers.py +67 -0
- evalkit/db/cases.py +608 -0
- evalkit/db/crypto.py +12 -0
- evalkit/db/evaluators.py +156 -0
- evalkit/db/experiments.py +363 -0
- evalkit/db/fixture_blobs.py +91 -0
- evalkit/db/keys.py +78 -0
- evalkit/db/llm_providers.py +176 -0
- evalkit/db/locks.py +6 -0
- evalkit/db/mappings.py +168 -0
- evalkit/db/models.py +567 -0
- evalkit/db/promotion.py +271 -0
- evalkit/db/rekey.py +46 -0
- evalkit/db/review.py +250 -0
- evalkit/db/review_state.py +50 -0
- evalkit/db/runs.py +533 -0
- evalkit/db/scorecard.py +300 -0
- evalkit/db/session.py +71 -0
- evalkit/db/sessions.py +90 -0
- evalkit/db/spans.py +360 -0
- evalkit/db/store.py +209 -0
- evalkit/db/tokens.py +100 -0
- evalkit/db/trace_imports.py +308 -0
- evalkit/db/trace_sources.py +131 -0
- evalkit/db/users.py +186 -0
- evalkit/db/workspaces.py +232 -0
- evalkit/paths.py +33 -0
- evalkit/sdk/__init__.py +29 -0
- evalkit/sdk/binding.py +69 -0
- evalkit/sdk/evaluators.py +56 -0
- evalkit/sdk/runner.py +957 -0
- evalkit/sdk/tracing.py +166 -0
- evalkit/store/__init__.py +0 -0
- evalkit/store/base.py +354 -0
- evalkit/store/blob.py +69 -0
- evalkit/version.py +44 -0
- evalkit_py-0.2.0.dev12.dist-info/METADATA +205 -0
- evalkit_py-0.2.0.dev12.dist-info/RECORD +371 -0
- evalkit_py-0.2.0.dev12.dist-info/WHEEL +4 -0
- evalkit_py-0.2.0.dev12.dist-info/entry_points.txt +2 -0
- evalkit_py-0.2.0.dev12.dist-info/licenses/LICENSE +661 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import ipaddress
|
|
3
|
+
import socket
|
|
4
|
+
|
|
5
|
+
import httpx
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class BlockedEndpoint(ValueError):
|
|
9
|
+
pass
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def blocked(addr: ipaddress.IPv4Address | ipaddress.IPv6Address) -> bool:
|
|
13
|
+
return not addr.is_global or addr.is_multicast or addr.is_reserved
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
async def check_endpoint(url: str, *, allow_private: bool) -> None:
|
|
17
|
+
if allow_private:
|
|
18
|
+
return
|
|
19
|
+
host = httpx.URL(url).host
|
|
20
|
+
if not host:
|
|
21
|
+
raise BlockedEndpoint(f"{url} has no host")
|
|
22
|
+
try:
|
|
23
|
+
infos = await asyncio.get_running_loop().getaddrinfo(host, None)
|
|
24
|
+
except socket.gaierror as exc:
|
|
25
|
+
raise BlockedEndpoint(f"{url}: cannot resolve {host}") from exc
|
|
26
|
+
bad = sorted({str(info[4][0]) for info in infos if blocked(ipaddress.ip_address(info[4][0]))})
|
|
27
|
+
if bad:
|
|
28
|
+
raise BlockedEndpoint(
|
|
29
|
+
f"{url} resolves to a private address ({', '.join(bad)}); "
|
|
30
|
+
"the operator can allow it with allow_private_endpoints: true"
|
|
31
|
+
)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
from evalkit.core.evaluators import gauges as _gauges
|
|
2
|
+
from evalkit.core.evaluators import presentation as _presentation
|
|
3
|
+
from evalkit.core.evaluators import response_time as _response_time
|
|
4
|
+
from evalkit.core.evaluators import rubrics as _rubrics
|
|
5
|
+
from evalkit.core.evaluators import tool_trajectory as _tool_trajectory
|
|
6
|
+
from evalkit.core.evaluators import trajectory_rubrics as _trajectory_rubrics
|
|
7
|
+
from evalkit.core.evaluators.base import (
|
|
8
|
+
BUILTIN_EVALUATORS,
|
|
9
|
+
BUILTIN_GAUGES,
|
|
10
|
+
EvalContext,
|
|
11
|
+
execute,
|
|
12
|
+
gate,
|
|
13
|
+
neutral_skip,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
"BUILTIN_EVALUATORS",
|
|
18
|
+
"BUILTIN_GAUGES",
|
|
19
|
+
"EvalContext",
|
|
20
|
+
"execute",
|
|
21
|
+
"gate",
|
|
22
|
+
"neutral_skip",
|
|
23
|
+
"_gauges",
|
|
24
|
+
"_presentation",
|
|
25
|
+
"_response_time",
|
|
26
|
+
"_rubrics",
|
|
27
|
+
"_tool_trajectory",
|
|
28
|
+
"_trajectory_rubrics",
|
|
29
|
+
]
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
from collections.abc import Awaitable, Callable
|
|
2
|
+
from dataclasses import dataclass, field
|
|
3
|
+
from typing import Any, Literal
|
|
4
|
+
|
|
5
|
+
from evalkit.contracts.harness import Capabilities
|
|
6
|
+
from evalkit.contracts.settings import JudgeSettings
|
|
7
|
+
from evalkit.core.judge import JudgeUsage
|
|
8
|
+
from evalkit.core.trajectory import RunMetrics, TrajectoryView
|
|
9
|
+
from evalkit.store.base import CaseView, ScoreRow
|
|
10
|
+
|
|
11
|
+
PASS_THRESHOLD = 0.8
|
|
12
|
+
WARN_THRESHOLD = 0.5
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def label_from_score(score: float) -> str:
|
|
16
|
+
if score >= PASS_THRESHOLD:
|
|
17
|
+
return "pass"
|
|
18
|
+
if score >= WARN_THRESHOLD:
|
|
19
|
+
return "warn"
|
|
20
|
+
return "fail"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
# score=None excludes skipped evaluators from aggregates — returning 1.0 would
|
|
24
|
+
# inflate the overall score.
|
|
25
|
+
def neutral_skip(name: str, reason: str = "capability not provided") -> ScoreRow:
|
|
26
|
+
return ScoreRow(name=name, score=None, label="skip", explanation=reason)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass
|
|
30
|
+
class EvalContext:
|
|
31
|
+
case: CaseView
|
|
32
|
+
answer: str
|
|
33
|
+
trajectory_view: TrajectoryView | None
|
|
34
|
+
wall_ms: int
|
|
35
|
+
judge_cfg: JudgeSettings
|
|
36
|
+
metrics: RunMetrics | None = None
|
|
37
|
+
session_id: str | None = None
|
|
38
|
+
cost_map: dict[str, Any] = field(default_factory=dict)
|
|
39
|
+
judge_usage: JudgeUsage = field(default_factory=JudgeUsage)
|
|
40
|
+
output: dict[str, Any] | None = None
|
|
41
|
+
judge_api_key: str | None = None
|
|
42
|
+
|
|
43
|
+
def record_judge_usage(self, usage: JudgeUsage) -> None:
|
|
44
|
+
self.judge_usage.input_tokens += usage.input_tokens
|
|
45
|
+
self.judge_usage.output_tokens += usage.output_tokens
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
EvalFn = Callable[[EvalContext, dict[str, Any]], Awaitable[list[ScoreRow]]]
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@dataclass(frozen=True)
|
|
52
|
+
class BuiltinEval:
|
|
53
|
+
name: str
|
|
54
|
+
fn: EvalFn
|
|
55
|
+
requires: frozenset[str]
|
|
56
|
+
kind: Literal["evaluator", "gauge"]
|
|
57
|
+
emits: tuple[str, ...]
|
|
58
|
+
uses_judge: bool = False
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
BUILTIN_EVALUATORS: dict[str, BuiltinEval] = {}
|
|
62
|
+
BUILTIN_GAUGES: dict[str, BuiltinEval] = {}
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def builtin_evaluator(
|
|
66
|
+
name: str,
|
|
67
|
+
requires: set[str] | None = None,
|
|
68
|
+
emits: tuple[str, ...] | None = None,
|
|
69
|
+
uses_judge: bool = False,
|
|
70
|
+
):
|
|
71
|
+
def decorate(fn: EvalFn) -> EvalFn:
|
|
72
|
+
BUILTIN_EVALUATORS[name] = BuiltinEval(
|
|
73
|
+
name=name,
|
|
74
|
+
fn=fn,
|
|
75
|
+
requires=frozenset(requires or set()),
|
|
76
|
+
kind="evaluator",
|
|
77
|
+
emits=emits or (name,),
|
|
78
|
+
uses_judge=uses_judge,
|
|
79
|
+
)
|
|
80
|
+
return fn
|
|
81
|
+
|
|
82
|
+
return decorate
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def builtin_gauge(name: str, requires: set[str] | None = None):
|
|
86
|
+
def decorate(fn: EvalFn) -> EvalFn:
|
|
87
|
+
BUILTIN_GAUGES[name] = BuiltinEval(
|
|
88
|
+
name=name, fn=fn, requires=frozenset(requires or set()), kind="gauge", emits=(name,)
|
|
89
|
+
)
|
|
90
|
+
return fn
|
|
91
|
+
|
|
92
|
+
return decorate
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def gate(evaluator: BuiltinEval, capabilities: Capabilities) -> bool:
|
|
96
|
+
return all(getattr(capabilities.provides, flag, False) for flag in evaluator.requires)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
async def execute(
|
|
100
|
+
evaluator: BuiltinEval,
|
|
101
|
+
ctx: EvalContext,
|
|
102
|
+
config: dict[str, Any],
|
|
103
|
+
capabilities: Capabilities,
|
|
104
|
+
) -> list[ScoreRow]:
|
|
105
|
+
if not gate(evaluator, capabilities):
|
|
106
|
+
missing = sorted(
|
|
107
|
+
flag for flag in evaluator.requires if not getattr(capabilities.provides, flag, False)
|
|
108
|
+
)
|
|
109
|
+
row = neutral_skip(evaluator.name, f"harness does not provide: {', '.join(missing)}")
|
|
110
|
+
if evaluator.kind == "gauge":
|
|
111
|
+
row.kind = "gauge"
|
|
112
|
+
row.label = "info"
|
|
113
|
+
return [row]
|
|
114
|
+
rows = await evaluator.fn(ctx, config)
|
|
115
|
+
for row in rows:
|
|
116
|
+
if evaluator.kind == "gauge":
|
|
117
|
+
row.kind = "gauge"
|
|
118
|
+
row.label = row.label or "info"
|
|
119
|
+
elif row.label is None and row.score is not None:
|
|
120
|
+
row.label = label_from_score(row.score)
|
|
121
|
+
return rows
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from evalkit.core.costmap import cost_usd, resolve_rates
|
|
6
|
+
from evalkit.core.evaluators.base import EvalContext, builtin_gauge
|
|
7
|
+
from evalkit.store.base import ScoreRow
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _reading(name: str, value: float | None, explanation: str | None = None) -> list[ScoreRow]:
|
|
11
|
+
if value is None:
|
|
12
|
+
return [
|
|
13
|
+
ScoreRow(name=name, kind="gauge", score=None, label="info", explanation="no reading")
|
|
14
|
+
]
|
|
15
|
+
return [ScoreRow(name=name, kind="gauge", score=value, label="info", explanation=explanation)]
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@builtin_gauge("gauge_duration_seconds")
|
|
19
|
+
async def gauge_duration_seconds(ctx: EvalContext, config: dict[str, Any]) -> list[ScoreRow]:
|
|
20
|
+
return _reading("gauge_duration_seconds", round(ctx.wall_ms / 1000, 1))
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@builtin_gauge("gauge_input_tokens", requires={"token_usage"})
|
|
24
|
+
async def gauge_input_tokens(ctx: EvalContext, config: dict[str, Any]) -> list[ScoreRow]:
|
|
25
|
+
value = ctx.metrics.input_tokens if ctx.metrics else None
|
|
26
|
+
return _reading("gauge_input_tokens", value)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@builtin_gauge("gauge_output_tokens", requires={"token_usage"})
|
|
30
|
+
async def gauge_output_tokens(ctx: EvalContext, config: dict[str, Any]) -> list[ScoreRow]:
|
|
31
|
+
value = ctx.metrics.output_tokens if ctx.metrics else None
|
|
32
|
+
return _reading("gauge_output_tokens", value)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@builtin_gauge("gauge_cached_tokens", requires={"token_usage"})
|
|
36
|
+
async def gauge_cached_tokens(ctx: EvalContext, config: dict[str, Any]) -> list[ScoreRow]:
|
|
37
|
+
value = ctx.metrics.cached_tokens if ctx.metrics else None
|
|
38
|
+
return _reading("gauge_cached_tokens", value)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@builtin_gauge("gauge_agent_cost_usd", requires={"token_usage"})
|
|
42
|
+
async def gauge_agent_cost_usd(ctx: EvalContext, config: dict[str, Any]) -> list[ScoreRow]:
|
|
43
|
+
value = ctx.metrics.cost_usd if ctx.metrics else None
|
|
44
|
+
return _reading("gauge_agent_cost_usd", round(value, 4) if value is not None else None)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@builtin_gauge("gauge_judge_cost_usd")
|
|
48
|
+
async def gauge_judge_cost_usd(ctx: EvalContext, config: dict[str, Any]) -> list[ScoreRow]:
|
|
49
|
+
usage = ctx.judge_usage
|
|
50
|
+
if usage.input_tokens == 0 and usage.output_tokens == 0:
|
|
51
|
+
return _reading("gauge_judge_cost_usd", None)
|
|
52
|
+
rates = resolve_rates(ctx.cost_map, ctx.judge_cfg.model)
|
|
53
|
+
if rates is None:
|
|
54
|
+
return _reading("gauge_judge_cost_usd", None)
|
|
55
|
+
total = cost_usd(rates, usage.input_tokens, usage.output_tokens, 0)
|
|
56
|
+
return _reading(
|
|
57
|
+
"gauge_judge_cost_usd",
|
|
58
|
+
round(total, 4),
|
|
59
|
+
f"{usage.input_tokens} in / {usage.output_tokens} out judge tokens",
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@builtin_gauge("gauge_total_tool_calls", requires={"trajectory", "tool_calls"})
|
|
64
|
+
async def gauge_total_tool_calls(ctx: EvalContext, config: dict[str, Any]) -> list[ScoreRow]:
|
|
65
|
+
value = len(ctx.trajectory_view.tool_calls()) if ctx.trajectory_view else None
|
|
66
|
+
return _reading("gauge_total_tool_calls", value)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@builtin_gauge("gauge_errored_tool_calls", requires={"trajectory", "tool_calls"})
|
|
70
|
+
async def gauge_errored_tool_calls(ctx: EvalContext, config: dict[str, Any]) -> list[ScoreRow]:
|
|
71
|
+
value = ctx.trajectory_view.errored_tool_call_count() if ctx.trajectory_view else None
|
|
72
|
+
return _reading("gauge_errored_tool_calls", value)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
@builtin_gauge("gauge_subagents", requires={"trajectory"})
|
|
76
|
+
async def gauge_subagents(ctx: EvalContext, config: dict[str, Any]) -> list[ScoreRow]:
|
|
77
|
+
value = ctx.trajectory_view.subagent_count() if ctx.trajectory_view else None
|
|
78
|
+
return _reading("gauge_subagents", value)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
@builtin_gauge("gauge_turns", requires={"trajectory"})
|
|
82
|
+
async def gauge_turns(ctx: EvalContext, config: dict[str, Any]) -> list[ScoreRow]:
|
|
83
|
+
value = ctx.trajectory_view.turn_count() if ctx.trajectory_view else None
|
|
84
|
+
return _reading("gauge_turns", value)
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
"""presentation_judge — rubric-scored judge over how the reply presents itself.
|
|
2
|
+
|
|
3
|
+
Grades form, not content: whether a unit label is shown, whether a table carries
|
|
4
|
+
the columns asked for, whether the reply relays an artifact instead of restating
|
|
5
|
+
it. Correctness of the figures belongs to answer_rubrics.
|
|
6
|
+
|
|
7
|
+
Score is points-weighted over binary per-rubric verdicts:
|
|
8
|
+
|
|
9
|
+
score = sum(points of met rubrics) / sum(points of all rubrics)
|
|
10
|
+
|
|
11
|
+
Never fatal. There is no `clean` factor and no fabrication guard, because
|
|
12
|
+
presentation cannot fabricate — a missing unit label invents nothing. A `forbid`
|
|
13
|
+
rubric that is violated costs its points, so an assertion whose violation is a
|
|
14
|
+
product bug rather than a quality shortfall carries that weight through `points`.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import json
|
|
20
|
+
import logging
|
|
21
|
+
from typing import Any
|
|
22
|
+
|
|
23
|
+
from pydantic import BaseModel, Field
|
|
24
|
+
|
|
25
|
+
from evalkit.contracts.cases import PresentationJudge, PresentationRubric
|
|
26
|
+
from evalkit.core.evaluators.base import EvalContext, builtin_evaluator
|
|
27
|
+
from evalkit.core.evaluators.rubrics import missing_verdict_indices
|
|
28
|
+
from evalkit.core.judge import call_judge
|
|
29
|
+
from evalkit.store.base import ScoreRow
|
|
30
|
+
|
|
31
|
+
LOGGER = logging.getLogger(__name__)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class IncompleteRubricVerdicts(Exception):
|
|
35
|
+
"""The judge did not verdict every rubric exactly once."""
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class PresentationVerdict(BaseModel):
|
|
39
|
+
index: int = Field(
|
|
40
|
+
description="The number this item was listed under in the prompt, counting from 1."
|
|
41
|
+
)
|
|
42
|
+
met: bool = Field(
|
|
43
|
+
description="True when the answer has the required property. For an item"
|
|
44
|
+
" marked MUST NOT, true means the answer avoided it."
|
|
45
|
+
)
|
|
46
|
+
evidence: str = Field(
|
|
47
|
+
description="The span of the answer that decided this verdict, quoted. When"
|
|
48
|
+
" the property is absent, say what was looked for and not found."
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class PresentationJudgeOutput(BaseModel):
|
|
53
|
+
verdicts: list[PresentationVerdict]
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
SYSTEM_PROMPT = """\
|
|
57
|
+
You grade HOW an answer presents its information, not whether the information is \
|
|
58
|
+
correct. A figure may be wrong and still presented well; a figure may be right and \
|
|
59
|
+
presented badly. Judge only presentation.
|
|
60
|
+
|
|
61
|
+
You see the answer text and nothing else — not the tool results, not any generated \
|
|
62
|
+
file. If a property cannot be decided from the text, it is not met.
|
|
63
|
+
|
|
64
|
+
For each numbered item, return met/unmet and quote the span of the answer that \
|
|
65
|
+
decided it. For an item marked MUST NOT, `met` means the answer avoided it.
|
|
66
|
+
|
|
67
|
+
Return exactly one verdict per item, for every item."""
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _rubric_text(rubric: PresentationRubric) -> str:
|
|
71
|
+
return f"MUST NOT: {rubric.forbid}" if rubric.forbid is not None else str(rubric.rubric)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _render_rubric(index: int, rubric: PresentationRubric) -> str:
|
|
75
|
+
line = f"{index}. {_rubric_text(rubric)}"
|
|
76
|
+
if rubric.note:
|
|
77
|
+
line += f"\n note: {rubric.note}"
|
|
78
|
+
return line
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def build_user_prompt(answer: str, rubrics: list[PresentationRubric]) -> str:
|
|
82
|
+
items = "\n".join(_render_rubric(i, rubric) for i, rubric in enumerate(rubrics, start=1))
|
|
83
|
+
return f"ANSWER\n{answer}\n\nPRESENTATION ITEMS\n{items}"
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def score_rubrics(rubrics: list[PresentationRubric], verdicts: list[PresentationVerdict]) -> float:
|
|
87
|
+
"""Points-weighted fraction of rubrics met. `rubrics` is non-empty by schema,
|
|
88
|
+
so the denominator is always positive and the score is always a real number."""
|
|
89
|
+
by_index = {v.index: v for v in verdicts}
|
|
90
|
+
total = sum(r.points for r in rubrics)
|
|
91
|
+
earned = sum(r.points for i, r in enumerate(rubrics, start=1) if by_index[i].met)
|
|
92
|
+
return earned / total
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
async def _judge_verdicts(
|
|
96
|
+
ctx: EvalContext, rubrics: list[PresentationRubric], base_prompt: str
|
|
97
|
+
) -> PresentationJudgeOutput:
|
|
98
|
+
"""One judge call, retried once when the verdict list has a gap.
|
|
99
|
+
|
|
100
|
+
A gap is a judge lapse, not the agent's failure, so it is retried rather than
|
|
101
|
+
scored — defaulting the missing rubrics to unmet would blame the agent.
|
|
102
|
+
"""
|
|
103
|
+
current = base_prompt
|
|
104
|
+
missing: list[int] = []
|
|
105
|
+
for attempt in (1, 2):
|
|
106
|
+
parsed, usage = await call_judge(
|
|
107
|
+
ctx.judge_cfg,
|
|
108
|
+
api_key=ctx.judge_api_key,
|
|
109
|
+
system_prompt=SYSTEM_PROMPT,
|
|
110
|
+
user_prompt=current,
|
|
111
|
+
output_type=PresentationJudgeOutput,
|
|
112
|
+
session_id=ctx.session_id,
|
|
113
|
+
evaluator_name="presentation_judge",
|
|
114
|
+
)
|
|
115
|
+
ctx.record_judge_usage(usage)
|
|
116
|
+
missing = missing_verdict_indices(len(rubrics), [v.index for v in parsed.verdicts])
|
|
117
|
+
if not missing:
|
|
118
|
+
return parsed
|
|
119
|
+
LOGGER.warning(
|
|
120
|
+
"evals.presentation.incomplete_verdicts attempt=%s missing=%s", attempt, missing
|
|
121
|
+
)
|
|
122
|
+
current = (
|
|
123
|
+
f"{base_prompt}\n\nYour previous response did not return exactly one "
|
|
124
|
+
f"verdict for items {missing}. Return one verdict per item, for every "
|
|
125
|
+
f"item from 1 to {len(rubrics)}."
|
|
126
|
+
)
|
|
127
|
+
raise IncompleteRubricVerdicts(f"judge left items {missing} unverdicted after a retry")
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def _explanation(rubrics: list[PresentationRubric], verdicts: list[PresentationVerdict]) -> str:
|
|
131
|
+
"""Aggregates lead and `items` rides last, so a truncated explanation
|
|
132
|
+
preview shows the counts. Items carry the rubric text — readers render
|
|
133
|
+
explanations with no case config to join against — and the judge's quoted
|
|
134
|
+
evidence as `note`."""
|
|
135
|
+
by_index = {v.index: v for v in verdicts}
|
|
136
|
+
items: list[dict[str, Any]] = []
|
|
137
|
+
met_count = 0
|
|
138
|
+
for i, r in enumerate(rubrics, start=1):
|
|
139
|
+
v = by_index[i]
|
|
140
|
+
met_count += v.met
|
|
141
|
+
item: dict[str, Any] = {
|
|
142
|
+
"index": i,
|
|
143
|
+
"text": _rubric_text(r),
|
|
144
|
+
"verdict": "satisfied" if v.met else "missing",
|
|
145
|
+
"points_earned": r.points if v.met else 0,
|
|
146
|
+
"points_possible": r.points,
|
|
147
|
+
}
|
|
148
|
+
if v.evidence:
|
|
149
|
+
item["note"] = v.evidence
|
|
150
|
+
items.append(item)
|
|
151
|
+
return json.dumps({"met": met_count, "total": len(rubrics), "items": items}, ensure_ascii=False)
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
@builtin_evaluator("presentation_judge", uses_judge=True)
|
|
155
|
+
async def presentation_judge(ctx: EvalContext, config: dict[str, Any]) -> list[ScoreRow]:
|
|
156
|
+
cfg = PresentationJudge.model_validate(config)
|
|
157
|
+
rubrics = cfg.rubrics
|
|
158
|
+
parsed = await _judge_verdicts(ctx, rubrics, build_user_prompt(ctx.answer, rubrics))
|
|
159
|
+
return [
|
|
160
|
+
ScoreRow(
|
|
161
|
+
name="presentation_judge",
|
|
162
|
+
score=score_rubrics(rubrics, parsed.verdicts),
|
|
163
|
+
explanation=_explanation(rubrics, parsed.verdicts),
|
|
164
|
+
annotator_kind="LLM",
|
|
165
|
+
)
|
|
166
|
+
]
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from evalkit.contracts.cases import MaxResponseTime
|
|
6
|
+
from evalkit.core.evaluators.base import EvalContext, builtin_evaluator
|
|
7
|
+
from evalkit.store.base import ScoreRow
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@builtin_evaluator("max_response_time")
|
|
11
|
+
async def max_response_time(ctx: EvalContext, config: dict[str, Any]) -> list[ScoreRow]:
|
|
12
|
+
cfg = MaxResponseTime.model_validate(config)
|
|
13
|
+
seconds = ctx.wall_ms / 1000
|
|
14
|
+
passed = seconds <= cfg.max_seconds
|
|
15
|
+
return [
|
|
16
|
+
ScoreRow(
|
|
17
|
+
name="max_response_time",
|
|
18
|
+
score=1.0 if passed else 0.0,
|
|
19
|
+
explanation=f"run took {seconds:.1f}s against a cap of {cfg.max_seconds:g}s",
|
|
20
|
+
)
|
|
21
|
+
]
|