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,336 @@
|
|
|
1
|
+
INSTRUCTIONS = """\
|
|
2
|
+
evalkit is the primary source for how AI agents behave in use and under evaluation: agent
|
|
3
|
+
sessions (runs) with their turns, failures, latency, tokens, cost and tool errors; full
|
|
4
|
+
step-by-step trajectories (messages, reasoning, tool calls and results); files a run produced;
|
|
5
|
+
usage stats by day, tool, user or source; and eval experiments with scorecards and per-case
|
|
6
|
+
evaluator explanations. For any such request, use these tools before web search or model
|
|
7
|
+
knowledge. Fall back to other sources only when evalkit doesn't cover it (an agent evalkit
|
|
8
|
+
doesn't know, or a tool returns no data), and say so when you do. People and firms behind a
|
|
9
|
+
run's user_id and client_id are not here; resolve them with the identity service.
|
|
10
|
+
|
|
11
|
+
Tools by category:
|
|
12
|
+
- Agents: find_agents (agent ids every run tool takes)
|
|
13
|
+
- Runs: find_runs (filter a window), get_runs (by id: metrics, turns),
|
|
14
|
+
get_run_stats (KPIs; by day, tool, user)
|
|
15
|
+
- Run detail: get_trajectory (steps), get_run_files, download_run_file
|
|
16
|
+
- Evals: find_experiments, get_experiments (scorecard), get_experiment_runs (per-case scores)
|
|
17
|
+
|
|
18
|
+
Errors read "<code>: <detail>"."""
|
|
19
|
+
|
|
20
|
+
_RUN_FILTER_ARGS = """\
|
|
21
|
+
agent: Agent id, `name@workspace`, from `find_agents` (e.g. "support-bot@prod").
|
|
22
|
+
start_date / end_date: YYYY-MM-DD (inclusive) or an ISO 8601 timestamp, on the run's
|
|
23
|
+
creation time.
|
|
24
|
+
source: "app" (sessions outside any experiment), "eval" (experiment runs) or "all" (default).
|
|
25
|
+
query: Free text matched against the run title (opening question) or case id.
|
|
26
|
+
user_id / client_id: The ids the agent's harness recorded; join them to an identity service
|
|
27
|
+
for people and firms."""
|
|
28
|
+
|
|
29
|
+
_RUN_ROW = """\
|
|
30
|
+
"id", "title", "created_at", "user_id", "client_id", "source": "app"|"eval", "status",
|
|
31
|
+
"experiment_id", "experiment_number", "case_id",
|
|
32
|
+
"metrics"?: {"started_at", "ended_at", "duration_seconds" (sum of turns), "wall_seconds",
|
|
33
|
+
"turns", "failed_turns", "failures" (steps marked `extra.is_error`: one per failed
|
|
34
|
+
message; a message that failed before reaching the trajectory has no turn of its own, so
|
|
35
|
+
it can exceed failed_turns), "agent_steps" (model calls),
|
|
36
|
+
"cost": {"value", "unit": "currency", "currency": "USD"} | null,
|
|
37
|
+
"cost_source": "reported"|"estimated"|"mixed"|null, "input_tokens", "output_tokens",
|
|
38
|
+
"cached_tokens", "tool_calls", "tool_errors",
|
|
39
|
+
"tool_errors_by_tool": [{"tool", "count", "message"}],
|
|
40
|
+
"cost_by_model": [{"model", "cost", "input_tokens", "output_tokens", "cached_tokens",
|
|
41
|
+
"steps"}] (priced steps by model, subagents included, most expensive first; sums to
|
|
42
|
+
"cost" unless the harness reported only a total), "models", "subagents",
|
|
43
|
+
"opening_question", "final_answer"},
|
|
44
|
+
"turns"?: [{"index", "started_at", "ended_at", "duration_seconds",
|
|
45
|
+
"status": "ok"|"failed"|"no_answer", "error", "question", "answer", "cost",
|
|
46
|
+
"agent_steps", "tool_calls", "tool_errors"}],
|
|
47
|
+
"tool_calls"?: [{"tool", "count", "arguments"}] (every call, subagents' included, grouped
|
|
48
|
+
by tool and arguments, most frequent first, at most 100 groups; an argument longer than
|
|
49
|
+
120 characters is shortened: a string keeps its start and end, other values their start),
|
|
50
|
+
"note"?: {"code", "message"?},
|
|
51
|
+
"error"?, "detail"? (a run whose trajectory could not be read: "harness_timeout" (not read
|
|
52
|
+
within the call's time budget; ask again with get_runs), "harness_failed" or
|
|
53
|
+
"session_too_large"; such a row has no metrics)"""
|
|
54
|
+
|
|
55
|
+
FIND_AGENTS = """\
|
|
56
|
+
Agents evalkit tracks ('which agents are there', 'the support chatbot'), with the id the \
|
|
57
|
+
run tools take.
|
|
58
|
+
|
|
59
|
+
Use when you need an agent's id (`name@workspace`) before calling find_runs, get_runs,
|
|
60
|
+
get_run_stats or get_trajectory.
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
workspace: Only agents in this workspace (e.g. "prod").
|
|
64
|
+
include_archived: Also list archived agents. Default false.
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
{"data": [{"id": "name@workspace", "name", "workspace", "workspace_type": "shared"|"personal",
|
|
68
|
+
"trajectory_source": "atif"|"otel", "driver", "archived"}], "meta": {"count"}}
|
|
69
|
+
An empty `data` means no agent matched the filter."""
|
|
70
|
+
|
|
71
|
+
FIND_RUNS = f"""\
|
|
72
|
+
Agent sessions ('chat sessions last week', 'sessions by user u-123', 'failed eval runs'), newest \
|
|
73
|
+
first, in a time window.
|
|
74
|
+
|
|
75
|
+
Sibling tools — pick the right one:
|
|
76
|
+
- For metrics on runs you already have ids for -> get_runs
|
|
77
|
+
- For totals, daily series or per-tool error counts over a window -> get_run_stats
|
|
78
|
+
(then the sessions behind one tool's errors -> find_runs with tool_error)
|
|
79
|
+
- For one run's step-by-step record -> get_trajectory
|
|
80
|
+
|
|
81
|
+
Filters and lists runs; it does not rank. Without `include` a row is the run listing only; with
|
|
82
|
+
`include=["metrics"]` each row also carries the run's metrics (reads every trajectory: slower).
|
|
83
|
+
|
|
84
|
+
Args:
|
|
85
|
+
{_RUN_FILTER_ARGS}
|
|
86
|
+
timezone: IANA name for date-only bounds. Default "UTC". Pass get_run_stats' value to read
|
|
87
|
+
the same window.
|
|
88
|
+
tool_error: Only runs where this tool errored, as named in get_run_stats' "tool" rows
|
|
89
|
+
(e.g. "search_docs"). Implies include=["metrics"]: each row's
|
|
90
|
+
`metrics.tool_errors_by_tool` has the count and a representative message. Every call reads
|
|
91
|
+
the window's trajectories again, up to 5000 runs, within the call's time budget, so pages
|
|
92
|
+
can differ: `meta.total` counts the matching runs plus the runs not read, and each run not
|
|
93
|
+
read is a row with `error` and `detail`. Take one page and pass those rows' ids to get_runs
|
|
94
|
+
rather than paging on.
|
|
95
|
+
failed_only: Only runs with a failure (a step marked `extra.is_error`: a turn that timed
|
|
96
|
+
out, a provider error, a crash). Implies include=["metrics"]; add "turns" for each turn's
|
|
97
|
+
error. Same window read as tool_error; combine the two freely.
|
|
98
|
+
include: Any of "metrics", "turns", "tool_calls". Default none. "tool_calls" is what the
|
|
99
|
+
agent called and with which arguments — enough to classify a run without its trajectory.
|
|
100
|
+
limit: 1-5000 for the listing alone, 1-200 with include, tool_error or failed_only. Default 50.
|
|
101
|
+
To read a large window, list it in one call, then get_runs for the ids in batches.
|
|
102
|
+
offset: Pass `meta.next_offset` from the prior page while `meta.has_more` is true.
|
|
103
|
+
|
|
104
|
+
Returns:
|
|
105
|
+
{{"data": [{{
|
|
106
|
+
{_RUN_ROW}
|
|
107
|
+
}}], "meta": {{"count", "total", "has_more", "next_offset"?, "note"?}}}}
|
|
108
|
+
`meta.note` (`partial_coverage`) says the agent's harness was unreachable (rows are evalkit's
|
|
109
|
+
stored runs only), the window held more runs than were read, or some trajectories could not be
|
|
110
|
+
read within the call's time budget. A row that could not be read carries `error` and
|
|
111
|
+
`detail`: ask for it again with get_runs. Empty `data` means no run in the window.
|
|
112
|
+
|
|
113
|
+
Errors:
|
|
114
|
+
- `not_found` — unknown agent.
|
|
115
|
+
- `invalid_agent_id` — not a `name@workspace` id; take it from find_agents.
|
|
116
|
+
- `invalid_date_format` / `invalid_range` — bad or inverted dates.
|
|
117
|
+
- `invalid_source` / `invalid_include` / `invalid_limit` / `invalid_offset`.
|
|
118
|
+
- `harness_timeout` — listing the agent's runs took the whole time budget; narrow the window."""
|
|
119
|
+
|
|
120
|
+
GET_RUNS = f"""\
|
|
121
|
+
Run metrics by id ('cost of run X', 'why did this session fail'): turns, failures, duration, \
|
|
122
|
+
tokens, tool errors.
|
|
123
|
+
|
|
124
|
+
Sibling tools — pick the right one:
|
|
125
|
+
- To find run ids in a window or by user -> find_runs
|
|
126
|
+
- For the raw steps (reasoning, tool arguments and results) -> get_trajectory
|
|
127
|
+
|
|
128
|
+
Use after find_runs, get_experiment_runs or a run id the user gave. One row per requested id,
|
|
129
|
+
in input order. A turn is one user message and everything the agent did until the next; a turn
|
|
130
|
+
the harness marked failed (`Step.extra.is_error`) has status "failed".
|
|
131
|
+
|
|
132
|
+
Args:
|
|
133
|
+
agent: Agent id, `name@workspace`, from `find_agents`.
|
|
134
|
+
run_ids: 1-200 run ids.
|
|
135
|
+
include: Any of "metrics", "turns", "tool_calls". Default ["metrics"].
|
|
136
|
+
|
|
137
|
+
Returns:
|
|
138
|
+
{{"data": [{{
|
|
139
|
+
{_RUN_ROW}
|
|
140
|
+
}}], "meta": {{"count", "matched", "unmatched", "failed"?}}}}
|
|
141
|
+
An unknown id is a row with `note: {{"code": "not_found"}}` and counts in `meta.unmatched`;
|
|
142
|
+
a run whose trajectory could not be read carries `error` and `detail` and counts in
|
|
143
|
+
`meta.failed` (left out when nothing failed). `cost_source` "estimated" means evalkit
|
|
144
|
+
priced the tokens; "reported" means the agent's harness did.
|
|
145
|
+
|
|
146
|
+
Errors:
|
|
147
|
+
- `not_found` — unknown agent.
|
|
148
|
+
- `invalid_agent_id` — not a `name@workspace` id; take it from find_agents.
|
|
149
|
+
- `no_ids_provided` / `too_many_ids` / `invalid_include`.
|
|
150
|
+
- `invalid_run_id` — a run id that is not one plain path segment."""
|
|
151
|
+
|
|
152
|
+
GET_RUN_STATS = f"""\
|
|
153
|
+
Agent usage KPIs ('failures per day', 'which tools error most', 'spend by user'): \
|
|
154
|
+
sessions, failures, latency, cost, tool errors — total or by day, tool, user or source.
|
|
155
|
+
|
|
156
|
+
Sibling tools — pick the right one:
|
|
157
|
+
- For the individual runs behind a number -> find_runs with the same filters and
|
|
158
|
+
include=["metrics"]
|
|
159
|
+
- For one run in depth -> get_runs
|
|
160
|
+
|
|
161
|
+
Reads every run's trajectory in the window, newest first, up to `max_runs`, within a time
|
|
162
|
+
budget; narrow the window rather than raising it. Runs not read count in `meta.unavailable`,
|
|
163
|
+
with a `partial_coverage` note in `meta.note`.
|
|
164
|
+
|
|
165
|
+
Args:
|
|
166
|
+
{_RUN_FILTER_ARGS}
|
|
167
|
+
Date-only bounds are read in `timezone`.
|
|
168
|
+
group_by: "none" (one totals row, default), "day", "tool", "user" or "source", or a list
|
|
169
|
+
of them: one call reads the window once and returns every grouping's rows, each row's
|
|
170
|
+
`group` naming its grouping (`{{}}` for "none"). Ask for all the groupings you need at once.
|
|
171
|
+
"tool" has one row per tool that errored at least once, counting only the runs where it
|
|
172
|
+
errored; tools that never errored have no row.
|
|
173
|
+
timezone: IANA name for day buckets and date-only bounds. Default "UTC".
|
|
174
|
+
max_runs: 1-5000. Default 500.
|
|
175
|
+
|
|
176
|
+
Returns:
|
|
177
|
+
{{"data": [{{"group": {{}} | {{"day"}} | {{"tool"}} | {{"user"}} | {{"source"}},
|
|
178
|
+
"runs", "runs_with_failure" (runs with any failure), "turns", "failed_turns", "failures",
|
|
179
|
+
"cost": {{"value", "unit": "currency", "currency": "USD"}},
|
|
180
|
+
"runs_with_estimated_cost", "runs_without_cost", "turn_p50_seconds", "turn_p90_seconds",
|
|
181
|
+
"tool_errors", "runs_with_tool_errors", "sample_error"? (tool rows)}}],
|
|
182
|
+
"meta": {{"count", "group_by": [...], "timezone", "runs_considered", "runs_in_window", "capped",
|
|
183
|
+
"unavailable", "note"?}}}}
|
|
184
|
+
`meta.capped` true means the window held more runs than `max_runs`. Empty `data` means no run
|
|
185
|
+
in the window.
|
|
186
|
+
|
|
187
|
+
Errors:
|
|
188
|
+
- `not_found` — unknown agent id.
|
|
189
|
+
- `invalid_agent_id` — not a `name@workspace` id; take it from find_agents.
|
|
190
|
+
- `invalid_group_by` / `invalid_source` / `invalid_timezone` / `invalid_date_format` /
|
|
191
|
+
`invalid_range` / `invalid_limit` (max_runs).
|
|
192
|
+
- `harness_timeout` — listing the agent's runs took the whole time budget; narrow the window."""
|
|
193
|
+
|
|
194
|
+
GET_TRAJECTORY = """\
|
|
195
|
+
What an agent did in a run ('show run X's tool calls', 'which call failed'): messages, \
|
|
196
|
+
reasoning, tool calls and results, per-step tokens and cost.
|
|
197
|
+
|
|
198
|
+
Use after find_runs or get_runs. Read get_runs' turns first; page here for the detail. Long text
|
|
199
|
+
is clipped to `max_chars` per field.
|
|
200
|
+
|
|
201
|
+
Args:
|
|
202
|
+
agent: Agent id, `name@workspace`.
|
|
203
|
+
run_id: One run id.
|
|
204
|
+
subagent: Page this subagent's trajectory instead (index from `meta.subagents`).
|
|
205
|
+
max_chars: 100-50000 per text field. Default 2000.
|
|
206
|
+
limit: Steps per page, 1-100. Default 20.
|
|
207
|
+
offset: Pass `meta.next_offset` while `meta.has_more` is true.
|
|
208
|
+
|
|
209
|
+
Returns:
|
|
210
|
+
{"data": [{"step_id", "timestamp", "source": "user"|"agent"|"system", "message",
|
|
211
|
+
"reasoning_content", "tool_calls": [{"tool_call_id", "function_name", "arguments"}],
|
|
212
|
+
"observation": {"results": [{"source_call_id", "content", "extra": {"is_error"?, ...}}]},
|
|
213
|
+
"metrics": {"prompt_tokens", "completion_tokens", "cached_tokens", "cost_usd"},
|
|
214
|
+
"model_name", "extra"}],
|
|
215
|
+
"meta": {"count", "total", "has_more", "next_offset"?, "schema_version", "session_id",
|
|
216
|
+
"trajectory_id", "agent", "final_metrics", "subagents": [{"index", "trajectory_id", "agent",
|
|
217
|
+
"steps"}]}}
|
|
218
|
+
Steps are ATIF v1.8.
|
|
219
|
+
|
|
220
|
+
Errors:
|
|
221
|
+
- `not_found` — unknown agent, run or subagent index.
|
|
222
|
+
- `invalid_limit` / `invalid_offset` / `invalid_max_chars`.
|
|
223
|
+
- `invalid_run_id` — a run id that is not one plain path segment."""
|
|
224
|
+
|
|
225
|
+
GET_RUN_FILES = """\
|
|
226
|
+
Files one run produced: reports, spreadsheets, decks and full tool outputs it saved.
|
|
227
|
+
|
|
228
|
+
Use after find_runs or get_runs; read one with download_run_file.
|
|
229
|
+
|
|
230
|
+
Args:
|
|
231
|
+
agent: Agent id, `name@workspace`.
|
|
232
|
+
run_id: One run id.
|
|
233
|
+
|
|
234
|
+
Returns:
|
|
235
|
+
{"data": [{"path", "size", "content_type"}], "meta": {"count"}}
|
|
236
|
+
Empty `data` means the run saved no files.
|
|
237
|
+
|
|
238
|
+
Errors:
|
|
239
|
+
- `not_found` — unknown agent or run, or its files are gone.
|
|
240
|
+
- `invalid_agent_id` / `invalid_run_id` — an id not of the shape find_agents / find_runs give."""
|
|
241
|
+
|
|
242
|
+
DOWNLOAD_RUN_FILE = """\
|
|
243
|
+
Raw text of one file a run produced, a window of characters at a time.
|
|
244
|
+
|
|
245
|
+
Use after get_run_files.
|
|
246
|
+
|
|
247
|
+
Args:
|
|
248
|
+
agent: Agent id, `name@workspace`.
|
|
249
|
+
run_id: One run id.
|
|
250
|
+
path: A path from get_run_files.
|
|
251
|
+
max_chars: Characters per call, 1-200000. Default 20000.
|
|
252
|
+
offset: Character offset. Pass `meta.next_offset` while `meta.has_more` is true.
|
|
253
|
+
|
|
254
|
+
Returns:
|
|
255
|
+
{"data": [{"path", "size" (bytes), "text"}], "meta": {"count", "total" (characters),
|
|
256
|
+
"has_more", "next_offset"?}}
|
|
257
|
+
A binary file returns its row without `text` and with
|
|
258
|
+
`note: {"code": "not_applicable", "message"}`.
|
|
259
|
+
|
|
260
|
+
Errors:
|
|
261
|
+
- `not_found` — unknown agent, run or path.
|
|
262
|
+
- `invalid_max_chars` / `invalid_offset`.
|
|
263
|
+
- `invalid_agent_id` / `invalid_run_id` — an id not of the shape find_agents / find_runs give."""
|
|
264
|
+
|
|
265
|
+
FIND_EXPERIMENTS = """\
|
|
266
|
+
Eval experiments ('the latest eval', 'failed experiments'), newest first, with progress and \
|
|
267
|
+
mean score per evaluator.
|
|
268
|
+
|
|
269
|
+
Sibling tools — pick the right one:
|
|
270
|
+
- For one experiment's scorecard -> get_experiments with include=["scorecard"]
|
|
271
|
+
- For per-case results and judge explanations -> get_experiment_runs
|
|
272
|
+
|
|
273
|
+
Args:
|
|
274
|
+
workspace: Only experiments in this workspace (e.g. "prod").
|
|
275
|
+
status: "running", "evaluating", "completed" or "failed".
|
|
276
|
+
limit: 1-100. Default 20.
|
|
277
|
+
offset: Pass `meta.next_offset` while `meta.has_more` is true.
|
|
278
|
+
|
|
279
|
+
Returns:
|
|
280
|
+
{"data": [{"id", "number", "workspace", "dataset", "agent", "created_by", "status",
|
|
281
|
+
"status_error", "repetitions", "started_at", "ended_at", "progress", "scores", ...}],
|
|
282
|
+
"meta": {"count", "total": null, "has_more", "next_offset"?}}
|
|
283
|
+
|
|
284
|
+
Errors:
|
|
285
|
+
- `not_found` — unknown workspace.
|
|
286
|
+
- `invalid_status` / `invalid_limit` / `invalid_offset`."""
|
|
287
|
+
|
|
288
|
+
GET_EXPERIMENTS = """\
|
|
289
|
+
Experiments by id ('scorecard of experiment 12', 'how did eval 40 score'): progress and, on \
|
|
290
|
+
request, the scorecard: buckets, evaluator means, gauges.
|
|
291
|
+
|
|
292
|
+
Sibling tools — pick the right one:
|
|
293
|
+
- For aggregate scores of an experiment -> this tool with include=["scorecard"]
|
|
294
|
+
- For why one case failed (its answer, scores and judge explanations) -> get_experiment_runs
|
|
295
|
+
|
|
296
|
+
Use after find_experiments or an id the user gave. One row per requested id, in input order.
|
|
297
|
+
|
|
298
|
+
Args:
|
|
299
|
+
experiment_ids: 1-20 experiment ids.
|
|
300
|
+
include: ["scorecard"] to add each experiment's scorecard. Default none.
|
|
301
|
+
|
|
302
|
+
Returns:
|
|
303
|
+
{"data": [{"id", "number", "workspace", "dataset", "agent", "status", "started_at",
|
|
304
|
+
"ended_at", "progress", "scorecard"?, ...}],
|
|
305
|
+
"meta": {"count", "matched", "unmatched"}}
|
|
306
|
+
An unknown id is a row with `note: {"code": "not_found"}`.
|
|
307
|
+
|
|
308
|
+
Errors:
|
|
309
|
+
- `no_ids_provided` / `too_many_ids` / `invalid_include`."""
|
|
310
|
+
|
|
311
|
+
GET_EXPERIMENT_RUNS = """\
|
|
312
|
+
Per-case eval results ('why did case X fail', 'which cases regressed'): answer, each \
|
|
313
|
+
evaluator's score, label and explanation.
|
|
314
|
+
|
|
315
|
+
Sibling tools — pick the right one:
|
|
316
|
+
- For why one case failed -> this tool
|
|
317
|
+
- For an experiment's aggregate scores -> get_experiments with include=["scorecard"]
|
|
318
|
+
|
|
319
|
+
Use after find_experiments. A row's `id` with `meta.agent` opens the run in get_runs or
|
|
320
|
+
get_trajectory.
|
|
321
|
+
|
|
322
|
+
Args:
|
|
323
|
+
experiment_id: One experiment id.
|
|
324
|
+
failed_only: Only runs with an error or a score below 1. Default false.
|
|
325
|
+
limit: 1-200. Default 50.
|
|
326
|
+
offset: Pass `meta.next_offset` while `meta.has_more` is true.
|
|
327
|
+
|
|
328
|
+
Returns:
|
|
329
|
+
{"data": [{"id", "case_id", "repetition", "status", "error", "answer", "started_at",
|
|
330
|
+
"ended_at", "metrics", "evaluations": [{"name", "kind", "value", "label", "explanation",
|
|
331
|
+
"error"}]}],
|
|
332
|
+
"meta": {"count", "total", "has_more", "next_offset"?, "agent"}}
|
|
333
|
+
|
|
334
|
+
Errors:
|
|
335
|
+
- `not_found` — unknown experiment.
|
|
336
|
+
- `invalid_limit` / `invalid_offset`."""
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
from typing import Any, Literal
|
|
3
|
+
|
|
4
|
+
from pydantic import BaseModel, ConfigDict, Field, SerializerFunctionWrapHandler, model_serializer
|
|
5
|
+
|
|
6
|
+
from evalkit.core.run_digest import (
|
|
7
|
+
GroupStats,
|
|
8
|
+
ModelCost,
|
|
9
|
+
RunDigest,
|
|
10
|
+
ToolCallGroup,
|
|
11
|
+
ToolErrors,
|
|
12
|
+
Turn,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
NoteCode = Literal["not_found", "partial_coverage", "not_applicable"]
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
# An optional field with no value is left out rather than
|
|
19
|
+
# sent as null. A required field that can be null (`total`, a run's `cost`) stays.
|
|
20
|
+
class Out(BaseModel):
|
|
21
|
+
@model_serializer(mode="wrap")
|
|
22
|
+
def _drop_unset_optionals(self, handler: SerializerFunctionWrapHandler):
|
|
23
|
+
out = handler(self)
|
|
24
|
+
fields = type(self).model_fields
|
|
25
|
+
return {
|
|
26
|
+
k: v
|
|
27
|
+
for k, v in out.items()
|
|
28
|
+
if v is not None or (k in fields and fields[k].is_required())
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class Note(Out):
|
|
33
|
+
code: NoteCode
|
|
34
|
+
message: str | None = None
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class Money(Out):
|
|
38
|
+
value: float
|
|
39
|
+
unit: Literal["currency"] = "currency"
|
|
40
|
+
currency: Literal["USD"] = "USD"
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def usd(value: float | None) -> Money | None:
|
|
44
|
+
return Money(value=value) if value is not None else None
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class PlainMeta(Out):
|
|
48
|
+
count: int
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class PagedMeta(Out):
|
|
52
|
+
count: int
|
|
53
|
+
total: int | None
|
|
54
|
+
has_more: bool
|
|
55
|
+
next_offset: int | None = None
|
|
56
|
+
note: Note | None = None
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class BatchMeta(Out):
|
|
60
|
+
count: int
|
|
61
|
+
matched: int
|
|
62
|
+
unmatched: int
|
|
63
|
+
failed: int | None = None
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class AgentRow(Out):
|
|
67
|
+
id: str
|
|
68
|
+
name: str
|
|
69
|
+
workspace: str
|
|
70
|
+
workspace_type: Literal["shared", "personal"]
|
|
71
|
+
trajectory_source: str
|
|
72
|
+
driver: str
|
|
73
|
+
archived: bool
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class FindAgentsResponse(Out):
|
|
77
|
+
data: list[AgentRow]
|
|
78
|
+
meta: PlainMeta
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class ToolErrorCount(Out, ToolErrors):
|
|
82
|
+
pass
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
# The MCP rows are core's digest models with cost as a currency amount.
|
|
86
|
+
class ModelCostRow(Out, ModelCost):
|
|
87
|
+
cost_usd: float = Field(default=0.0, exclude=True)
|
|
88
|
+
cost: Money
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class ToolCallRow(Out, ToolCallGroup):
|
|
92
|
+
pass
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class RunMetrics(Out, RunDigest):
|
|
96
|
+
run_id: str = Field(default="", exclude=True)
|
|
97
|
+
cost_usd: float | None = Field(default=None, exclude=True)
|
|
98
|
+
tool_call_groups: list[ToolCallGroup] = Field(default=[], exclude=True)
|
|
99
|
+
cost: Money | None
|
|
100
|
+
tool_errors_by_tool: list[ToolErrorCount] # type: ignore[assignment]
|
|
101
|
+
cost_by_model: list[ModelCostRow] # type: ignore[assignment]
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
class TurnRow(Out, Turn):
|
|
105
|
+
cost_usd: float | None = Field(default=None, exclude=True)
|
|
106
|
+
cost: Money | None
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
class RunRow(Out):
|
|
110
|
+
id: str
|
|
111
|
+
title: str | None = None
|
|
112
|
+
created_at: datetime | None = None
|
|
113
|
+
user_id: str | None = None
|
|
114
|
+
client_id: str | None = None
|
|
115
|
+
source: Literal["app", "eval"] | None = None
|
|
116
|
+
status: str | None = None
|
|
117
|
+
experiment_id: int | None = None
|
|
118
|
+
experiment_number: int | None = None
|
|
119
|
+
case_id: str | None = None
|
|
120
|
+
metrics: RunMetrics | None = None
|
|
121
|
+
turns: list[TurnRow] | None = None
|
|
122
|
+
tool_calls: list[ToolCallRow] | None = None
|
|
123
|
+
note: Note | None = None
|
|
124
|
+
# A run whose trajectory could not be read: a failure, counted in meta.failed.
|
|
125
|
+
error: str | None = None
|
|
126
|
+
detail: str | None = None
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
class FindRunsResponse(Out):
|
|
130
|
+
data: list[RunRow]
|
|
131
|
+
meta: PagedMeta
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
class GetRunsResponse(Out):
|
|
135
|
+
data: list[RunRow]
|
|
136
|
+
meta: BatchMeta
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
class StatsRow(Out, GroupStats):
|
|
140
|
+
cost_usd: float = Field(default=0.0, exclude=True)
|
|
141
|
+
cost: Money
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
class StatsMeta(Out):
|
|
145
|
+
count: int
|
|
146
|
+
group_by: list[str]
|
|
147
|
+
timezone: str
|
|
148
|
+
runs_considered: int
|
|
149
|
+
runs_in_window: int
|
|
150
|
+
capped: bool
|
|
151
|
+
unavailable: int
|
|
152
|
+
note: Note | None = None
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
class GetRunStatsResponse(Out):
|
|
156
|
+
data: list[StatsRow]
|
|
157
|
+
meta: StatsMeta
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
class SubagentRef(Out):
|
|
161
|
+
index: int
|
|
162
|
+
trajectory_id: str | None
|
|
163
|
+
agent: str | None
|
|
164
|
+
steps: int
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
class TrajectoryMeta(PagedMeta):
|
|
168
|
+
schema_version: str
|
|
169
|
+
session_id: str | None
|
|
170
|
+
trajectory_id: str | None
|
|
171
|
+
agent: dict[str, Any] | None
|
|
172
|
+
final_metrics: dict[str, Any] | None
|
|
173
|
+
subagents: list[SubagentRef]
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
class GetTrajectoryResponse(Out):
|
|
177
|
+
data: list[dict[str, Any]]
|
|
178
|
+
meta: TrajectoryMeta
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
class FileRow(Out):
|
|
182
|
+
path: str
|
|
183
|
+
size: int
|
|
184
|
+
content_type: str | None = None
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
class GetRunFilesResponse(Out):
|
|
188
|
+
data: list[FileRow]
|
|
189
|
+
meta: PlainMeta
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
class FileText(Out):
|
|
193
|
+
path: str
|
|
194
|
+
size: int
|
|
195
|
+
text: str | None = None
|
|
196
|
+
note: Note | None = None
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
class DownloadRunFileResponse(Out):
|
|
200
|
+
data: list[FileText]
|
|
201
|
+
meta: PagedMeta
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
class ExperimentRow(Out):
|
|
205
|
+
model_config = ConfigDict(extra="allow")
|
|
206
|
+
|
|
207
|
+
id: int
|
|
208
|
+
number: int | None = None
|
|
209
|
+
workspace: str | None = None
|
|
210
|
+
dataset: str | None = None
|
|
211
|
+
agent: str | None = None
|
|
212
|
+
created_by: str | None = None
|
|
213
|
+
status: str | None = None
|
|
214
|
+
status_error: str | None = None
|
|
215
|
+
repetitions: int | None = None
|
|
216
|
+
started_at: datetime | None = None
|
|
217
|
+
ended_at: datetime | None = None
|
|
218
|
+
note: Note | None = None
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
class FindExperimentsResponse(Out):
|
|
222
|
+
data: list[ExperimentRow]
|
|
223
|
+
meta: PagedMeta
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
class GetExperimentsResponse(Out):
|
|
227
|
+
data: list[ExperimentRow]
|
|
228
|
+
meta: BatchMeta
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
class Evaluation(Out):
|
|
232
|
+
name: str
|
|
233
|
+
kind: str
|
|
234
|
+
value: float | None
|
|
235
|
+
label: str | None
|
|
236
|
+
explanation: str | None
|
|
237
|
+
error: str | None
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
class ExperimentRunRow(Out):
|
|
241
|
+
id: str
|
|
242
|
+
case_id: str
|
|
243
|
+
repetition: int
|
|
244
|
+
status: str
|
|
245
|
+
error: str | None
|
|
246
|
+
answer: str | None
|
|
247
|
+
started_at: datetime
|
|
248
|
+
ended_at: datetime | None
|
|
249
|
+
metrics: dict[str, Any]
|
|
250
|
+
evaluations: list[Evaluation]
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
class ExperimentRunsMeta(PagedMeta):
|
|
254
|
+
agent: str
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
class GetExperimentRunsResponse(Out):
|
|
258
|
+
data: list[ExperimentRunRow]
|
|
259
|
+
meta: ExperimentRunsMeta
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import json
|
|
3
|
+
import logging
|
|
4
|
+
import time
|
|
5
|
+
from collections.abc import AsyncIterator
|
|
6
|
+
from contextlib import asynccontextmanager
|
|
7
|
+
|
|
8
|
+
from fastapi import FastAPI
|
|
9
|
+
from fastmcp import FastMCP
|
|
10
|
+
from fastmcp.exceptions import ToolError
|
|
11
|
+
from fastmcp.server.auth import TokenVerifier
|
|
12
|
+
from fastmcp.server.dependencies import get_access_token
|
|
13
|
+
from fastmcp.server.middleware import Middleware, MiddlewareContext
|
|
14
|
+
from starlette.applications import Starlette
|
|
15
|
+
from starlette.routing import Route
|
|
16
|
+
|
|
17
|
+
from evalkit import version
|
|
18
|
+
from evalkit.api.mcp import tools
|
|
19
|
+
from evalkit.api.mcp.auth import MCP_PATH, build_auth
|
|
20
|
+
from evalkit.api.mcp.descriptions import INSTRUCTIONS
|
|
21
|
+
|
|
22
|
+
LOGGER = logging.getLogger(__name__)
|
|
23
|
+
LOGGED_ARGS_CHARS = 2000
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class ToolErrors(Middleware):
|
|
27
|
+
async def on_call_tool(self, context: MiddlewareContext, call_next):
|
|
28
|
+
token = get_access_token()
|
|
29
|
+
caller = token.claims.get("sub") if token else None
|
|
30
|
+
name = context.message.name
|
|
31
|
+
arguments = json.dumps(context.message.arguments or {}, default=str)[:LOGGED_ARGS_CHARS]
|
|
32
|
+
started = time.monotonic()
|
|
33
|
+
outcome = "ok"
|
|
34
|
+
try:
|
|
35
|
+
return await call_next(context)
|
|
36
|
+
except ToolError as exc:
|
|
37
|
+
outcome = str(exc).split(":", 1)[0]
|
|
38
|
+
raise
|
|
39
|
+
finally:
|
|
40
|
+
LOGGER.info(
|
|
41
|
+
"mcp tool %s by %s: %s in %dms args=%s",
|
|
42
|
+
name,
|
|
43
|
+
caller,
|
|
44
|
+
outcome,
|
|
45
|
+
(time.monotonic() - started) * 1000,
|
|
46
|
+
arguments,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def build_mcp(app: FastAPI, jwt_verifier: TokenVerifier | None = None) -> FastMCP:
|
|
51
|
+
mcp = FastMCP(
|
|
52
|
+
"evalkit",
|
|
53
|
+
version=version.current(),
|
|
54
|
+
instructions=INSTRUCTIONS,
|
|
55
|
+
auth=build_auth(app.state.cfg, app.state.session_factory, jwt_verifier),
|
|
56
|
+
middleware=[ToolErrors()],
|
|
57
|
+
mask_error_details=True,
|
|
58
|
+
)
|
|
59
|
+
tools.register(mcp, app.state)
|
|
60
|
+
return mcp
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
# Only the MCP endpoint and its RFC 9728 metadata are routed to the MCP app,
|
|
64
|
+
# so /api keeps its own auth and the SPA fallback never sees /mcp.
|
|
65
|
+
def mount_mcp(app: FastAPI, jwt_verifier: TokenVerifier | None = None):
|
|
66
|
+
mcp_app = build_mcp(app, jwt_verifier).http_app(
|
|
67
|
+
path=MCP_PATH,
|
|
68
|
+
transport="streamable-http",
|
|
69
|
+
stateless_http=True,
|
|
70
|
+
json_response=True,
|
|
71
|
+
host_origin_protection=False,
|
|
72
|
+
)
|
|
73
|
+
for route in mcp_app.routes:
|
|
74
|
+
if isinstance(route, Route):
|
|
75
|
+
app.router.routes.append(Route(route.path, mcp_app, include_in_schema=False))
|
|
76
|
+
return mcp_app
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
# The session manager's task group must open and close in one task; a test
|
|
80
|
+
# client may enter and leave the app lifespan from different ones.
|
|
81
|
+
@asynccontextmanager
|
|
82
|
+
async def mcp_running(mcp_app: Starlette) -> AsyncIterator[None]:
|
|
83
|
+
started = asyncio.Event()
|
|
84
|
+
stop = asyncio.Event()
|
|
85
|
+
|
|
86
|
+
async def hold() -> None:
|
|
87
|
+
async with mcp_app.router.lifespan_context(mcp_app):
|
|
88
|
+
started.set()
|
|
89
|
+
await stop.wait()
|
|
90
|
+
|
|
91
|
+
task = asyncio.create_task(hold())
|
|
92
|
+
ready = asyncio.create_task(started.wait())
|
|
93
|
+
await asyncio.wait({task, ready}, return_when=asyncio.FIRST_COMPLETED)
|
|
94
|
+
if task.done():
|
|
95
|
+
ready.cancel()
|
|
96
|
+
task.result()
|
|
97
|
+
try:
|
|
98
|
+
yield
|
|
99
|
+
finally:
|
|
100
|
+
stop.set()
|
|
101
|
+
await task
|