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
evalkit/client/sync.py
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import shutil
|
|
4
|
+
import subprocess
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
from evalkit.client.api import SyncApi
|
|
9
|
+
from evalkit.contracts.cases import REGISTRY_FILENAME, is_case_yaml, load_cases_from_yaml
|
|
10
|
+
from evalkit.core.fixtures import load_registries, sha256_hex
|
|
11
|
+
from evalkit.core.workspace import Reference, parse_reference
|
|
12
|
+
from evalkit.store.base import GitRevision, Selection
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass
|
|
16
|
+
class ResolvedPath:
|
|
17
|
+
ref: Reference
|
|
18
|
+
dataset_dir: Path | None
|
|
19
|
+
selection: Selection
|
|
20
|
+
subpath: str | None = None
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _case_ids(file: Path) -> list[str]:
|
|
24
|
+
cases, _ = load_cases_from_yaml(file)
|
|
25
|
+
return [case.id for case in cases]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _selection_for(subpath: str, dataset_dir: Path | None) -> Selection:
|
|
29
|
+
if not subpath:
|
|
30
|
+
return Selection(all=True)
|
|
31
|
+
on_disk = dataset_dir / subpath if dataset_dir is not None else None
|
|
32
|
+
if on_disk is not None and on_disk.is_dir():
|
|
33
|
+
return Selection(tag=on_disk.name)
|
|
34
|
+
if on_disk is not None and on_disk.is_file():
|
|
35
|
+
return Selection(case_ids=_case_ids(on_disk))
|
|
36
|
+
if Path(subpath).suffix in (".yaml", ".yml"):
|
|
37
|
+
raise ValueError(f"{subpath} is not on disk; selecting a file needs its case ids")
|
|
38
|
+
return Selection(tag=Path(subpath).name)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _on_disk(candidate: Path, cases_root: Path | None) -> tuple[Path, str]:
|
|
42
|
+
resolved = candidate.resolve()
|
|
43
|
+
if cases_root is not None and resolved.is_relative_to(cases_root.resolve()):
|
|
44
|
+
relative = resolved.relative_to(cases_root.resolve())
|
|
45
|
+
if not relative.parts:
|
|
46
|
+
raise ValueError(f"{candidate} is the cases root; pass a dataset directory under it")
|
|
47
|
+
return cases_root.resolve() / relative.parts[0], Path(*relative.parts[1:]).as_posix()
|
|
48
|
+
if resolved.is_dir():
|
|
49
|
+
return resolved, ""
|
|
50
|
+
return resolved.parent, resolved.name
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def resolve_path(arg: str, cases_root: Path | None, my_workspace: str) -> ResolvedPath:
|
|
54
|
+
candidate = Path(arg)
|
|
55
|
+
if candidate.exists():
|
|
56
|
+
dataset_dir, subpath = _on_disk(candidate, cases_root)
|
|
57
|
+
ref = Reference(dataset_dir.name, my_workspace)
|
|
58
|
+
if subpath == ".":
|
|
59
|
+
subpath = ""
|
|
60
|
+
return ResolvedPath(ref, dataset_dir, _selection_for(subpath, dataset_dir), subpath or None)
|
|
61
|
+
ref_text, _, subpath = arg.partition("/")
|
|
62
|
+
ref = parse_reference(ref_text, my_workspace)
|
|
63
|
+
own = ref.workspace == my_workspace
|
|
64
|
+
if own and cases_root is None:
|
|
65
|
+
raise ValueError(
|
|
66
|
+
f"{ref.name} is in your workspace, so it syncs from disk: set `cases:` in "
|
|
67
|
+
"evalkit.yaml, or pass the dataset's directory"
|
|
68
|
+
)
|
|
69
|
+
on_disk = cases_root / ref.name if cases_root is not None else None
|
|
70
|
+
if on_disk is not None and not on_disk.is_dir():
|
|
71
|
+
on_disk = None
|
|
72
|
+
if own and on_disk is None:
|
|
73
|
+
raise ValueError(f"no dataset directory {ref.name!r} under {cases_root}")
|
|
74
|
+
return ResolvedPath(
|
|
75
|
+
ref, on_disk if own else None, _selection_for(subpath, on_disk), subpath or None
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def resolve_dataset(dataset: str, workspace: str | None, my_workspace: str) -> Reference:
|
|
80
|
+
ref = parse_reference(dataset, my_workspace)
|
|
81
|
+
if "@" in dataset and workspace:
|
|
82
|
+
raise ValueError("dataset already names a workspace; drop the workspace argument")
|
|
83
|
+
if workspace:
|
|
84
|
+
return Reference(ref.name, workspace)
|
|
85
|
+
return ref
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def _git(args: list[str], cwd: Path) -> str | None:
|
|
89
|
+
if shutil.which("git") is None:
|
|
90
|
+
return None
|
|
91
|
+
result = subprocess.run(["git", *args], cwd=cwd, capture_output=True, text=True)
|
|
92
|
+
return result.stdout.strip() if result.returncode == 0 else None
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def git_revision(path: Path) -> GitRevision:
|
|
96
|
+
cwd = path if path.is_dir() else path.parent
|
|
97
|
+
sha = _git(["rev-parse", "HEAD"], cwd)
|
|
98
|
+
if sha is None:
|
|
99
|
+
return GitRevision()
|
|
100
|
+
status = _git(["status", "--porcelain", "--", str(path)], cwd)
|
|
101
|
+
return GitRevision(
|
|
102
|
+
sha=sha, branch=_git(["rev-parse", "--abbrev-ref", "HEAD"], cwd), dirty=bool(status)
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _nearest_registry(start: Path) -> Path | None:
|
|
107
|
+
for directory in (start, *start.parents):
|
|
108
|
+
candidate = directory / REGISTRY_FILENAME
|
|
109
|
+
if candidate.is_file():
|
|
110
|
+
return candidate
|
|
111
|
+
return None
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def sync_files(dataset_dir: Path, subpath: str | None = None) -> list[tuple[str, Path]]:
|
|
115
|
+
start = dataset_dir / subpath if subpath else dataset_dir
|
|
116
|
+
if not start.exists():
|
|
117
|
+
raise ValueError(
|
|
118
|
+
f"{subpath} is not a folder or file under {dataset_dir}; sync needs one on disk"
|
|
119
|
+
)
|
|
120
|
+
if start.is_dir():
|
|
121
|
+
files = sorted(
|
|
122
|
+
f
|
|
123
|
+
for pattern in ("*.yaml", "*.yml")
|
|
124
|
+
for f in start.rglob(pattern)
|
|
125
|
+
if f.is_file()
|
|
126
|
+
and not any(part.startswith(".") for part in f.relative_to(dataset_dir).parts)
|
|
127
|
+
and is_case_yaml(f.relative_to(dataset_dir))
|
|
128
|
+
)
|
|
129
|
+
else:
|
|
130
|
+
files = [start]
|
|
131
|
+
out = [(f.relative_to(dataset_dir).as_posix(), f) for f in files]
|
|
132
|
+
registry = _nearest_registry(dataset_dir)
|
|
133
|
+
if registry is not None:
|
|
134
|
+
out.append((REGISTRY_FILENAME, registry))
|
|
135
|
+
return out
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def upload_fixtures(
|
|
139
|
+
client: SyncApi, files: list[tuple[str, Path]], fixtures_dir: Path
|
|
140
|
+
) -> list[str]:
|
|
141
|
+
registry = [path for name, path in files if name == REGISTRY_FILENAME]
|
|
142
|
+
entries, _ = load_registries(registry)
|
|
143
|
+
referenced: set[str] = set()
|
|
144
|
+
for name, path in files:
|
|
145
|
+
if name == REGISTRY_FILENAME:
|
|
146
|
+
continue
|
|
147
|
+
cases, _ = load_cases_from_yaml(path)
|
|
148
|
+
referenced.update(key for case in cases for key in case.fixtures)
|
|
149
|
+
uploaded: list[str] = []
|
|
150
|
+
for key in sorted(referenced):
|
|
151
|
+
entry = entries.get(key)
|
|
152
|
+
if entry is None:
|
|
153
|
+
continue
|
|
154
|
+
url = f"/api/fixtures/sha256/{entry.sha256}"
|
|
155
|
+
if client.head(url).status_code == 200:
|
|
156
|
+
continue
|
|
157
|
+
local = fixtures_dir / key
|
|
158
|
+
if not local.is_file():
|
|
159
|
+
raise ValueError(
|
|
160
|
+
f"fixture {key!r} is registered but {local} does not exist and the server has "
|
|
161
|
+
f"no bytes with sha256 {entry.sha256}"
|
|
162
|
+
)
|
|
163
|
+
data = local.read_bytes()
|
|
164
|
+
if sha256_hex(data) != entry.sha256:
|
|
165
|
+
raise ValueError(
|
|
166
|
+
f"fixture {key!r} on disk has sha256 {sha256_hex(data)} but {REGISTRY_FILENAME} "
|
|
167
|
+
f"says {entry.sha256}; update the registry entry"
|
|
168
|
+
)
|
|
169
|
+
client.put(url, content=data, retry=True)
|
|
170
|
+
uploaded.append(key)
|
|
171
|
+
return uploaded
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def sync_dataset(
|
|
175
|
+
client: SyncApi,
|
|
176
|
+
dataset_dir: Path,
|
|
177
|
+
*,
|
|
178
|
+
name: str,
|
|
179
|
+
workspace: str,
|
|
180
|
+
revision: GitRevision,
|
|
181
|
+
subpath: str | None = None,
|
|
182
|
+
fixtures_dir: Path | None = None,
|
|
183
|
+
actor: str | None = None,
|
|
184
|
+
) -> dict:
|
|
185
|
+
params: dict[str, object] = {
|
|
186
|
+
"dataset": name,
|
|
187
|
+
"workspace": workspace,
|
|
188
|
+
"prune": subpath is None,
|
|
189
|
+
}
|
|
190
|
+
for key, value in revision.model_dump().items():
|
|
191
|
+
if value is not None:
|
|
192
|
+
params[key] = value
|
|
193
|
+
if actor is not None:
|
|
194
|
+
params["actor"] = actor
|
|
195
|
+
files = sync_files(dataset_dir, subpath)
|
|
196
|
+
if fixtures_dir is not None:
|
|
197
|
+
upload_fixtures(client, files, fixtures_dir)
|
|
198
|
+
return client.post_files("/api/sync", files, params=params).json()
|
evalkit/config.py
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from collections.abc import Mapping
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Literal
|
|
7
|
+
|
|
8
|
+
import yaml
|
|
9
|
+
from pydantic import BaseModel, ConfigDict, model_validator
|
|
10
|
+
|
|
11
|
+
from evalkit.credentials import CredentialsConfig, load_dotenv_beside
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class MissingEnvError(ValueError):
|
|
15
|
+
def __init__(self, source: Path, key: str, name: str):
|
|
16
|
+
self.source = source
|
|
17
|
+
self.key = key
|
|
18
|
+
self.name = name
|
|
19
|
+
super().__init__(f"{source}: {key} needs env {name} (or env:{name}:<default>)")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class ConfigModel(BaseModel):
|
|
23
|
+
model_config = ConfigDict(extra="forbid", hide_input_in_errors=True)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class ServerConfig(ConfigModel):
|
|
27
|
+
base_url: str
|
|
28
|
+
accent_color: str | None = None
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class DbConfig(ConfigModel):
|
|
32
|
+
url: str
|
|
33
|
+
migrate_on_start: bool = False
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class BlobConfig(ConfigModel):
|
|
37
|
+
url: str
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class CostMapConfig(ConfigModel):
|
|
41
|
+
source: str = "bundled"
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class AuthConfig(ConfigModel):
|
|
45
|
+
token_ttl_hours: int = 720
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class McpOAuthConfig(ConfigModel):
|
|
49
|
+
issuer: str
|
|
50
|
+
jwks_uri: str
|
|
51
|
+
audience: str | None = None
|
|
52
|
+
algorithm: str = "RS256"
|
|
53
|
+
scope: str = "evalkit:read"
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class McpConfig(ConfigModel):
|
|
57
|
+
oauth: McpOAuthConfig | None = None
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class OtelConfig(ConfigModel):
|
|
61
|
+
settle_seconds: int = 10
|
|
62
|
+
max_batch_bytes: int = 16 * 1024 * 1024
|
|
63
|
+
max_session_bytes: int = 384 * 1024 * 1024
|
|
64
|
+
import_page_traces: int = 25
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class SentryConfig(ConfigModel):
|
|
68
|
+
dsn: str
|
|
69
|
+
environment: str | None = None
|
|
70
|
+
traces_sample_rate: float = 0.0
|
|
71
|
+
max_request_body_size: Literal["never", "small", "medium", "always"] = "never"
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
MB = 1024 * 1024
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class LimitsConfig(ConfigModel):
|
|
78
|
+
fixture_upload_bytes: int = 500 * MB
|
|
79
|
+
experiment_blob_bytes: int = 500 * MB
|
|
80
|
+
case_upload_bytes: int = 50 * MB
|
|
81
|
+
request_bytes: int = 10 * MB
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class RunnerConfig(ConfigModel):
|
|
85
|
+
cost_map: CostMapConfig = CostMapConfig()
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
# TODO: remove in 0.5.0, two minor versions after 0.3.0 moved credentials and
|
|
89
|
+
# judge_default to the database; _shell and _moved_to_db_commands only serve that check.
|
|
90
|
+
def _shell(value: object) -> str:
|
|
91
|
+
text = str(value)
|
|
92
|
+
return f"${text.removeprefix('env:')}" if text.startswith("env:") else text
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def _moved_to_db_commands(data: dict) -> list[str]:
|
|
96
|
+
credentials = data.get("credentials") or {}
|
|
97
|
+
judge = data.get("judge_default") or {}
|
|
98
|
+
commands = [
|
|
99
|
+
f"evalkit llm-providers create <name> --base-url {_shell(url)} "
|
|
100
|
+
f"--api-key-env {str(ref).removeprefix('env:')}"
|
|
101
|
+
for url, ref in (credentials.get("judges") or {}).items()
|
|
102
|
+
]
|
|
103
|
+
if judge:
|
|
104
|
+
commands.append(
|
|
105
|
+
f"evalkit judge-default set --provider <name> "
|
|
106
|
+
f"--model {_shell(judge.get('model', '<model>'))}"
|
|
107
|
+
)
|
|
108
|
+
commands += [
|
|
109
|
+
f"evalkit agents edit {ref} --api-key-env {str(env).removeprefix('env:')}"
|
|
110
|
+
for ref, env in (credentials.get("agents") or {}).items()
|
|
111
|
+
]
|
|
112
|
+
commands += [
|
|
113
|
+
f"evalkit evaluators edit {ref} --api-key-env {str(env).removeprefix('env:')}"
|
|
114
|
+
for ref, env in (credentials.get("evaluators") or {}).items()
|
|
115
|
+
]
|
|
116
|
+
return commands
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
class Config(RunnerConfig):
|
|
120
|
+
server: ServerConfig
|
|
121
|
+
db: DbConfig
|
|
122
|
+
blob: BlobConfig
|
|
123
|
+
auth: AuthConfig = AuthConfig()
|
|
124
|
+
mcp: McpConfig = McpConfig()
|
|
125
|
+
otel: OtelConfig = OtelConfig()
|
|
126
|
+
limits: LimitsConfig = LimitsConfig()
|
|
127
|
+
sentry: SentryConfig | None = None
|
|
128
|
+
encryption_key: str | None = None
|
|
129
|
+
allow_private_endpoints: bool = False
|
|
130
|
+
|
|
131
|
+
# TODO: remove in 0.5.0, two minor versions after 0.3.0 moved credentials and
|
|
132
|
+
# judge_default to the database.
|
|
133
|
+
@model_validator(mode="before")
|
|
134
|
+
@classmethod
|
|
135
|
+
def _keys_moved_to_db(cls, data: object) -> object:
|
|
136
|
+
if isinstance(data, dict) and ("credentials" in data or "judge_default" in data):
|
|
137
|
+
commands = "\n".join(f" {c}" for c in _moved_to_db_commands(data))
|
|
138
|
+
raise ValueError(
|
|
139
|
+
"credentials and judge_default moved to the database. Remove them from the "
|
|
140
|
+
"config, start the server, then run (as an admin, with the keys in your env):\n"
|
|
141
|
+
f"{commands}"
|
|
142
|
+
)
|
|
143
|
+
return data
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
LOCAL_CONFIG_FILENAME = "evalkit.yaml"
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
class LocalConfig(RunnerConfig):
|
|
150
|
+
path: Path
|
|
151
|
+
credentials: CredentialsConfig = CredentialsConfig()
|
|
152
|
+
cases: Path | None = None
|
|
153
|
+
fixtures: Path | None = None
|
|
154
|
+
|
|
155
|
+
@classmethod
|
|
156
|
+
def find(cls, start: Path) -> LocalConfig | None:
|
|
157
|
+
start = start.resolve()
|
|
158
|
+
for directory in (start, *start.parents):
|
|
159
|
+
candidate = directory / LOCAL_CONFIG_FILENAME
|
|
160
|
+
if candidate.is_file():
|
|
161
|
+
return load_local_config(candidate)
|
|
162
|
+
return None
|
|
163
|
+
|
|
164
|
+
def environ(self) -> dict[str, str]:
|
|
165
|
+
return load_dotenv_beside(self.path)
|
|
166
|
+
|
|
167
|
+
def resolve_env(self, *, scoring: bool = True) -> LocalConfig:
|
|
168
|
+
data = self.model_dump()
|
|
169
|
+
if scoring:
|
|
170
|
+
environ = self.environ()
|
|
171
|
+
data["cost_map"] = resolve_env_refs(data["cost_map"], self.path, "cost_map", environ)
|
|
172
|
+
return LocalConfig.model_validate(data)
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
UNRESOLVED_KEYS = frozenset({"credentials"})
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def resolve_env_refs(obj: object, source: Path, key: str, environ: Mapping[str, str]) -> object:
|
|
179
|
+
if isinstance(obj, dict):
|
|
180
|
+
return {
|
|
181
|
+
k: v
|
|
182
|
+
if k in UNRESOLVED_KEYS and not key
|
|
183
|
+
else resolve_env_refs(v, source, _join(key, k), environ)
|
|
184
|
+
for k, v in obj.items()
|
|
185
|
+
}
|
|
186
|
+
if isinstance(obj, list):
|
|
187
|
+
return [
|
|
188
|
+
resolve_env_refs(v, source, f"{key}[{_item_label(v, i)}]", environ)
|
|
189
|
+
for i, v in enumerate(obj)
|
|
190
|
+
]
|
|
191
|
+
if isinstance(obj, str) and obj.startswith("env:"):
|
|
192
|
+
name, has_default, default = obj[4:].partition(":")
|
|
193
|
+
if has_default:
|
|
194
|
+
return environ.get(name, default)
|
|
195
|
+
if name not in environ:
|
|
196
|
+
raise MissingEnvError(source, key, name)
|
|
197
|
+
return environ[name]
|
|
198
|
+
return obj
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def _join(key: str, child: object) -> str:
|
|
202
|
+
return f"{key}.{child}" if key else str(child)
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def _item_label(item: object, index: int) -> object:
|
|
206
|
+
if isinstance(item, dict) and isinstance(item.get("name"), str):
|
|
207
|
+
return item["name"]
|
|
208
|
+
return index
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def load_config(path: Path) -> Config:
|
|
212
|
+
raw = yaml.safe_load(path.read_text())
|
|
213
|
+
data = resolve_env_refs(raw, path, "", os.environ)
|
|
214
|
+
return Config.model_validate(data)
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def load_local_config(path: Path) -> LocalConfig:
|
|
218
|
+
data = yaml.safe_load(path.read_text()) or {}
|
|
219
|
+
assert isinstance(data, dict)
|
|
220
|
+
base = path.resolve().parent
|
|
221
|
+
cfg = LocalConfig.model_validate({**data, "path": path.resolve()})
|
|
222
|
+
return cfg.model_copy(
|
|
223
|
+
update={
|
|
224
|
+
"cases": (base / cfg.cases).resolve() if cfg.cases is not None else None,
|
|
225
|
+
"fixtures": (base / cfg.fixtures).resolve() if cfg.fixtures is not None else None,
|
|
226
|
+
}
|
|
227
|
+
)
|
|
File without changes
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
from typing import Any, Literal
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel, ConfigDict, ValidationError
|
|
4
|
+
|
|
5
|
+
ACCEPTED_SCHEMA_VERSIONS = frozenset({"ATIF-v1.8"})
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class TrajectoryValidationError(Exception):
|
|
9
|
+
pass
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class AtifModel(BaseModel):
|
|
13
|
+
model_config = ConfigDict(extra="allow")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class AtifAgent(AtifModel):
|
|
17
|
+
name: str
|
|
18
|
+
version: str | None = None
|
|
19
|
+
model_name: str | None = None
|
|
20
|
+
tool_definitions: list[dict[str, Any]] | None = None
|
|
21
|
+
extra: dict[str, Any] | None = None
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class ContentPart(AtifModel):
|
|
25
|
+
type: str
|
|
26
|
+
text: str | None = None
|
|
27
|
+
media_type: str | None = None
|
|
28
|
+
path: str | None = None
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class ToolCall(AtifModel):
|
|
32
|
+
tool_call_id: str
|
|
33
|
+
function_name: str
|
|
34
|
+
arguments: dict[str, Any]
|
|
35
|
+
extra: dict[str, Any] | None = None
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class SubagentTrajectoryRef(AtifModel):
|
|
39
|
+
trajectory_id: str | None = None
|
|
40
|
+
trajectory_path: str | None = None
|
|
41
|
+
session_id: str | None = None
|
|
42
|
+
extra: dict[str, Any] | None = None
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class ObservationResult(AtifModel):
|
|
46
|
+
source_call_id: str | None = None
|
|
47
|
+
content: str | list[ContentPart] | None = None
|
|
48
|
+
subagent_trajectory_ref: list[SubagentTrajectoryRef] | None = None
|
|
49
|
+
extra: dict[str, Any] | None = None
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class Observation(AtifModel):
|
|
53
|
+
results: list[ObservationResult]
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class StepMetrics(AtifModel):
|
|
57
|
+
prompt_tokens: int | None = None
|
|
58
|
+
completion_tokens: int | None = None
|
|
59
|
+
cached_tokens: int | None = None
|
|
60
|
+
cost_usd: float | None = None
|
|
61
|
+
extra: dict[str, Any] | None = None
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class FinalMetrics(AtifModel):
|
|
65
|
+
total_prompt_tokens: int | None = None
|
|
66
|
+
total_completion_tokens: int | None = None
|
|
67
|
+
total_cached_tokens: int | None = None
|
|
68
|
+
total_cost_usd: float | None = None
|
|
69
|
+
total_steps: int | None = None
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class Step(AtifModel):
|
|
73
|
+
step_id: int
|
|
74
|
+
timestamp: str | None = None
|
|
75
|
+
source: Literal["user", "agent", "system"]
|
|
76
|
+
message: str | list[ContentPart] | None = None
|
|
77
|
+
reasoning_content: str | None = None
|
|
78
|
+
tool_calls: list[ToolCall] | None = None
|
|
79
|
+
observation: Observation | None = None
|
|
80
|
+
metrics: StepMetrics | None = None
|
|
81
|
+
llm_call_count: int | None = None
|
|
82
|
+
model_name: str | None = None
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class Trajectory(AtifModel):
|
|
86
|
+
schema_version: str
|
|
87
|
+
session_id: str | None = None
|
|
88
|
+
trajectory_id: str | None = None
|
|
89
|
+
agent: AtifAgent | None = None
|
|
90
|
+
steps: list[Step]
|
|
91
|
+
final_metrics: FinalMetrics | None = None
|
|
92
|
+
subagent_trajectories: list["Trajectory"] | None = None
|
|
93
|
+
extra: dict[str, Any] | None = None
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def validate_trajectory(data: dict) -> Trajectory:
|
|
97
|
+
version = data.get("schema_version")
|
|
98
|
+
if version not in ACCEPTED_SCHEMA_VERSIONS:
|
|
99
|
+
raise TrajectoryValidationError(
|
|
100
|
+
f"unsupported schema_version {version!r}; accepted: {sorted(ACCEPTED_SCHEMA_VERSIONS)}"
|
|
101
|
+
)
|
|
102
|
+
try:
|
|
103
|
+
return Trajectory.model_validate(data)
|
|
104
|
+
except ValidationError as exc:
|
|
105
|
+
missing = "; ".join(
|
|
106
|
+
f"{'.'.join(str(loc) for loc in err['loc'])}: {err['msg']}" for err in exc.errors()
|
|
107
|
+
)
|
|
108
|
+
raise TrajectoryValidationError(f"invalid ATIF document: {missing}") from exc
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def subagent_link_warnings(trajectory: Trajectory) -> list[str]:
|
|
112
|
+
missing = sum(1 for sub in trajectory.subagent_trajectories or [] if not sub.trajectory_id)
|
|
113
|
+
if not missing:
|
|
114
|
+
return []
|
|
115
|
+
return [f"{missing} embedded subagent(s) have no trajectory_id (required since ATIF-v1.7)"]
|