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/__init__.py
ADDED
|
File without changes
|
evalkit/api/__init__.py
ADDED
|
File without changes
|
evalkit/api/access.py
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
from typing import Annotated
|
|
2
|
+
|
|
3
|
+
from fastapi import Depends, HTTPException
|
|
4
|
+
from sqlalchemy import select
|
|
5
|
+
from sqlalchemy.ext.asyncio import AsyncSession
|
|
6
|
+
|
|
7
|
+
from evalkit.api.auth import CurrentUser
|
|
8
|
+
from evalkit.api.deps import SessionDep
|
|
9
|
+
from evalkit.config import Config
|
|
10
|
+
from evalkit.contracts.enums import WorkspaceRole
|
|
11
|
+
from evalkit.contracts.notes import PERSONAL_KEY_NOTE
|
|
12
|
+
from evalkit.core.access import Actor, WorkspaceRef, can_write
|
|
13
|
+
from evalkit.core.endpoints import check_endpoint
|
|
14
|
+
from evalkit.db.models import User, WorkspaceMember
|
|
15
|
+
from evalkit.db.workspaces import load_ref, workspace_row
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
async def load_actor(session: AsyncSession, user: User) -> Actor:
|
|
19
|
+
rows = (
|
|
20
|
+
await session.execute(
|
|
21
|
+
select(WorkspaceMember.workspace_id, WorkspaceMember.role).where(
|
|
22
|
+
WorkspaceMember.user_id == user.id
|
|
23
|
+
)
|
|
24
|
+
)
|
|
25
|
+
).all()
|
|
26
|
+
roles: dict[int, set[WorkspaceRole]] = {}
|
|
27
|
+
for ws_id, role in rows:
|
|
28
|
+
roles.setdefault(ws_id, set()).add(WorkspaceRole(role))
|
|
29
|
+
return Actor(user.id, user.username, user.is_admin, {k: frozenset(v) for k, v in roles.items()})
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
async def current_actor(session: SessionDep, user: CurrentUser) -> Actor:
|
|
33
|
+
return await load_actor(session, user)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
ActorDep = Annotated[Actor, Depends(current_actor)]
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
async def workspace_ref(session: AsyncSession, name: str) -> WorkspaceRef:
|
|
40
|
+
return await load_ref(session, await workspace_row(session, name))
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def require(allowed: bool, detail: str) -> None:
|
|
44
|
+
if not allowed:
|
|
45
|
+
raise HTTPException(status_code=403, detail=detail)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def ensure_active(ws: WorkspaceRef) -> None:
|
|
49
|
+
if ws.archived:
|
|
50
|
+
raise HTTPException(status_code=409, detail=f"workspace {ws.name} is archived")
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
async def writable_endpoint(
|
|
54
|
+
session: SessionDep,
|
|
55
|
+
actor: ActorDep,
|
|
56
|
+
cfg: Config,
|
|
57
|
+
workspace: str,
|
|
58
|
+
*,
|
|
59
|
+
api_key: bool = False,
|
|
60
|
+
url: str | None = None,
|
|
61
|
+
) -> None:
|
|
62
|
+
ws = await workspace_ref(session, workspace)
|
|
63
|
+
require(can_write(actor, ws), f"you cannot write to @{workspace}")
|
|
64
|
+
ensure_active(ws)
|
|
65
|
+
if api_key and not ws.is_shared:
|
|
66
|
+
raise HTTPException(status_code=400, detail=PERSONAL_KEY_NOTE)
|
|
67
|
+
if url is not None and ws.is_shared:
|
|
68
|
+
await check_endpoint(url, allow_private=cfg.allow_private_endpoints)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
async def writable(session: SessionDep, actor: ActorDep, workspace: str) -> None:
|
|
72
|
+
ws = await workspace_ref(session, workspace)
|
|
73
|
+
require(can_write(actor, ws), f"you cannot write to @{workspace}")
|
|
74
|
+
ensure_active(ws)
|
evalkit/api/agents.py
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
from typing import Annotated
|
|
2
|
+
|
|
3
|
+
from fastapi import APIRouter
|
|
4
|
+
from pydantic import AfterValidator, BaseModel
|
|
5
|
+
|
|
6
|
+
from evalkit.api.access import ActorDep, writable, writable_endpoint
|
|
7
|
+
from evalkit.api.deps import AgentStoreDep, ConfigDep, KeysDep, SessionDep
|
|
8
|
+
from evalkit.contracts.enums import TrajectorySource
|
|
9
|
+
from evalkit.contracts.settings import AgentSettings
|
|
10
|
+
from evalkit.core.agents import AgentDefinition, driver
|
|
11
|
+
from evalkit.core.workspace import Reference, slug
|
|
12
|
+
|
|
13
|
+
router = APIRouter(prefix="/api/agents", tags=["agents"])
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _http_url(value: str) -> str:
|
|
17
|
+
if not value.lower().startswith(("http://", "https://")):
|
|
18
|
+
raise ValueError("repo_url must start with http:// or https://")
|
|
19
|
+
return value
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
HttpUrlStr = Annotated[str, AfterValidator(_http_url)]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def agent_view(agent: AgentDefinition) -> dict:
|
|
26
|
+
return {
|
|
27
|
+
"id": agent.id,
|
|
28
|
+
"name": agent.name,
|
|
29
|
+
"workspace": agent.workspace,
|
|
30
|
+
"ref": agent.ref,
|
|
31
|
+
"slug": slug(Reference(agent.name, agent.workspace)),
|
|
32
|
+
"workspace_type": "shared" if agent.workspace_is_shared else "personal",
|
|
33
|
+
"harness_url": agent.harness_url,
|
|
34
|
+
"trajectory_source": agent.trajectory_source,
|
|
35
|
+
"repo_url": agent.repo_url,
|
|
36
|
+
"settings": agent.settings.model_dump(),
|
|
37
|
+
"driver": driver(agent),
|
|
38
|
+
"archived": agent.archived,
|
|
39
|
+
"has_api_key": agent.has_api_key,
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@router.get("")
|
|
44
|
+
async def list_agents(
|
|
45
|
+
agents: AgentStoreDep, workspace: str | None = None, include_archived: bool = False
|
|
46
|
+
) -> list[dict]:
|
|
47
|
+
return [
|
|
48
|
+
agent_view(a)
|
|
49
|
+
for a in await agents.list_agents(workspace=workspace, include_archived=include_archived)
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@router.get("/{ref}")
|
|
54
|
+
async def get_agent(ref: str, agents: AgentStoreDep) -> dict:
|
|
55
|
+
return agent_view(await agents.get(ref))
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class CreateAgent(BaseModel):
|
|
59
|
+
workspace: str
|
|
60
|
+
name: str
|
|
61
|
+
harness_url: str | None = None
|
|
62
|
+
trajectory_source: TrajectorySource = TrajectorySource.ATIF
|
|
63
|
+
repo_url: HttpUrlStr | None = None
|
|
64
|
+
settings: AgentSettings = AgentSettings()
|
|
65
|
+
mapping: dict | None = None
|
|
66
|
+
api_key: str | None = None
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@router.post("")
|
|
70
|
+
async def create_agent(
|
|
71
|
+
body: CreateAgent,
|
|
72
|
+
keys: KeysDep,
|
|
73
|
+
agents: AgentStoreDep,
|
|
74
|
+
session: SessionDep,
|
|
75
|
+
actor: ActorDep,
|
|
76
|
+
cfg: ConfigDep,
|
|
77
|
+
) -> dict:
|
|
78
|
+
await writable_endpoint(
|
|
79
|
+
session,
|
|
80
|
+
actor,
|
|
81
|
+
cfg,
|
|
82
|
+
body.workspace,
|
|
83
|
+
api_key="api_key" in body.model_fields_set,
|
|
84
|
+
url=body.harness_url,
|
|
85
|
+
)
|
|
86
|
+
created = await agents.create(
|
|
87
|
+
body.workspace,
|
|
88
|
+
body.name,
|
|
89
|
+
harness_url=body.harness_url,
|
|
90
|
+
trajectory_source=body.trajectory_source,
|
|
91
|
+
repo_url=body.repo_url,
|
|
92
|
+
settings=body.settings,
|
|
93
|
+
created_by=actor.id,
|
|
94
|
+
mapping=body.mapping,
|
|
95
|
+
api_key_encrypted=keys.seal(body.api_key),
|
|
96
|
+
)
|
|
97
|
+
return agent_view(created)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class EditAgent(BaseModel):
|
|
101
|
+
harness_url: str | None = None
|
|
102
|
+
repo_url: HttpUrlStr | None = None
|
|
103
|
+
settings: AgentSettings | None = None
|
|
104
|
+
trajectory_source: TrajectorySource | None = None
|
|
105
|
+
mapping: dict | None = None
|
|
106
|
+
api_key: str | None = None
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
@router.patch("/{ref}")
|
|
110
|
+
async def edit_agent(
|
|
111
|
+
ref: str,
|
|
112
|
+
body: EditAgent,
|
|
113
|
+
keys: KeysDep,
|
|
114
|
+
agents: AgentStoreDep,
|
|
115
|
+
session: SessionDep,
|
|
116
|
+
actor: ActorDep,
|
|
117
|
+
cfg: ConfigDep,
|
|
118
|
+
) -> dict:
|
|
119
|
+
current = await agents.get(ref)
|
|
120
|
+
url_changes = "harness_url" in body.model_fields_set and body.harness_url != current.harness_url
|
|
121
|
+
await writable_endpoint(
|
|
122
|
+
session,
|
|
123
|
+
actor,
|
|
124
|
+
cfg,
|
|
125
|
+
current.workspace,
|
|
126
|
+
api_key="api_key" in body.model_fields_set,
|
|
127
|
+
url=body.harness_url if url_changes else None,
|
|
128
|
+
)
|
|
129
|
+
clear = {k for k in ("harness_url", "repo_url", "api_key") if k in body.model_fields_set}
|
|
130
|
+
edited = await agents.edit(
|
|
131
|
+
ref,
|
|
132
|
+
harness_url=body.harness_url,
|
|
133
|
+
repo_url=body.repo_url,
|
|
134
|
+
settings=body.settings,
|
|
135
|
+
clear=frozenset(clear),
|
|
136
|
+
trajectory_source=body.trajectory_source,
|
|
137
|
+
mapping=body.mapping,
|
|
138
|
+
edited_by=actor.id,
|
|
139
|
+
api_key_encrypted=keys.seal(body.api_key),
|
|
140
|
+
)
|
|
141
|
+
return agent_view(edited)
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
@router.post("/{ref}/archive")
|
|
145
|
+
async def archive_agent(
|
|
146
|
+
ref: str, agents: AgentStoreDep, session: SessionDep, actor: ActorDep
|
|
147
|
+
) -> dict:
|
|
148
|
+
current = await agents.get(ref)
|
|
149
|
+
await writable(session, actor, current.workspace)
|
|
150
|
+
return agent_view(await agents.archive(ref))
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
@router.post("/{ref}/restore")
|
|
154
|
+
async def restore_agent(
|
|
155
|
+
ref: str, agents: AgentStoreDep, session: SessionDep, actor: ActorDep
|
|
156
|
+
) -> dict:
|
|
157
|
+
current = await agents.get(ref)
|
|
158
|
+
await writable(session, actor, current.workspace)
|
|
159
|
+
return agent_view(await agents.restore(ref))
|
evalkit/api/app.py
ADDED
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import logging
|
|
3
|
+
import os
|
|
4
|
+
import signal
|
|
5
|
+
import socket
|
|
6
|
+
import time
|
|
7
|
+
from contextlib import asynccontextmanager
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
from fastapi import Depends, FastAPI, HTTPException, Request
|
|
11
|
+
from fastmcp.server.auth import TokenVerifier
|
|
12
|
+
from starlette.exceptions import HTTPException as StarletteHTTPException
|
|
13
|
+
from starlette.responses import FileResponse, JSONResponse
|
|
14
|
+
from starlette.staticfiles import StaticFiles
|
|
15
|
+
|
|
16
|
+
from evalkit import version
|
|
17
|
+
from evalkit.api import (
|
|
18
|
+
agents as agents_api,
|
|
19
|
+
)
|
|
20
|
+
from evalkit.api import (
|
|
21
|
+
cli_dist,
|
|
22
|
+
datasets,
|
|
23
|
+
evaluators,
|
|
24
|
+
experiments,
|
|
25
|
+
fixtures,
|
|
26
|
+
harness_check,
|
|
27
|
+
imports,
|
|
28
|
+
llm_providers,
|
|
29
|
+
meta,
|
|
30
|
+
otel,
|
|
31
|
+
pull,
|
|
32
|
+
review,
|
|
33
|
+
runs,
|
|
34
|
+
tokens,
|
|
35
|
+
trace_imports,
|
|
36
|
+
trace_sources,
|
|
37
|
+
traces,
|
|
38
|
+
users,
|
|
39
|
+
workspaces,
|
|
40
|
+
)
|
|
41
|
+
from evalkit.api.auth import bootstrap_admin, current_user
|
|
42
|
+
from evalkit.api.auth import router as auth_router
|
|
43
|
+
from evalkit.api.limits import BodyLimitMiddleware
|
|
44
|
+
from evalkit.api.mcp.server import mcp_running, mount_mcp
|
|
45
|
+
from evalkit.config import Config
|
|
46
|
+
from evalkit.core.agents import UnknownAgent
|
|
47
|
+
from evalkit.core.blob import BlobStore
|
|
48
|
+
from evalkit.core.endpoints import BlockedEndpoint
|
|
49
|
+
from evalkit.core.remote_evaluators import UnknownEvaluator
|
|
50
|
+
from evalkit.core.runner import (
|
|
51
|
+
LEASE_SECONDS,
|
|
52
|
+
RENEW_EVERY_SECONDS,
|
|
53
|
+
AgentArchived,
|
|
54
|
+
LeaseLost,
|
|
55
|
+
RunnerDeps,
|
|
56
|
+
)
|
|
57
|
+
from evalkit.core.traces.sessions import SessionTooLarge
|
|
58
|
+
from evalkit.core.workspace import BareReference
|
|
59
|
+
from evalkit.credentials import MissingCredential
|
|
60
|
+
from evalkit.db.agents import AgentExists, AgentStore, InvalidAgent
|
|
61
|
+
from evalkit.db.cases import DatasetExists
|
|
62
|
+
from evalkit.db.crypto import SecretBox
|
|
63
|
+
from evalkit.db.evaluators import EvaluatorExists, InvalidEvaluator, RemoteEvaluatorStore
|
|
64
|
+
from evalkit.db.experiments import reap
|
|
65
|
+
from evalkit.db.keys import KeyLoader, NoKeysLoaded
|
|
66
|
+
from evalkit.db.llm_providers import (
|
|
67
|
+
LlmProviderExists,
|
|
68
|
+
LlmProviderStore,
|
|
69
|
+
ProviderInUse,
|
|
70
|
+
UnknownLlmProvider,
|
|
71
|
+
)
|
|
72
|
+
from evalkit.db.mappings import MappingStore
|
|
73
|
+
from evalkit.db.rekey import count_sealed
|
|
74
|
+
from evalkit.db.review import ReviewLog
|
|
75
|
+
from evalkit.db.session import make_session_factory, migrate_under_lock, verify_schema
|
|
76
|
+
from evalkit.db.sessions import DbSessionReader
|
|
77
|
+
from evalkit.db.spans import SpanStore
|
|
78
|
+
from evalkit.db.store import PgStore
|
|
79
|
+
from evalkit.db.tokens import AgentTokens
|
|
80
|
+
from evalkit.db.trace_imports import TraceImportStore, UnknownTraceImport, reap_imports
|
|
81
|
+
from evalkit.db.trace_sources import (
|
|
82
|
+
EncryptionKeyMissing,
|
|
83
|
+
TraceSourceExists,
|
|
84
|
+
TraceSourceStore,
|
|
85
|
+
UnknownTraceSource,
|
|
86
|
+
)
|
|
87
|
+
from evalkit.db.users import InvalidName, InvalidPassword, LastAdmin, NameTaken, UnknownUser
|
|
88
|
+
from evalkit.db.workspaces import PromotionLinkError, SharedOnly, UnknownWorkspace
|
|
89
|
+
from evalkit.paths import ui_root
|
|
90
|
+
from evalkit.store.base import Conflict, UnknownDataset, UnknownExperiment
|
|
91
|
+
from evalkit.store.blob import Obstore
|
|
92
|
+
|
|
93
|
+
LOGGER = logging.getLogger(__name__)
|
|
94
|
+
|
|
95
|
+
CONTENT_SECURITY_POLICY = "; ".join(
|
|
96
|
+
[
|
|
97
|
+
"default-src 'self'",
|
|
98
|
+
"script-src 'self'",
|
|
99
|
+
"style-src 'self' 'unsafe-inline'",
|
|
100
|
+
"font-src 'self' data:",
|
|
101
|
+
"img-src 'self' data: blob:",
|
|
102
|
+
"frame-src 'self' blob:",
|
|
103
|
+
"worker-src 'self' blob:",
|
|
104
|
+
"connect-src 'self'",
|
|
105
|
+
"object-src 'none'",
|
|
106
|
+
"base-uri 'self'",
|
|
107
|
+
"form-action 'self'",
|
|
108
|
+
"frame-ancestors 'none'",
|
|
109
|
+
]
|
|
110
|
+
)
|
|
111
|
+
SECURITY_HEADERS = {
|
|
112
|
+
"Content-Security-Policy": CONTENT_SECURITY_POLICY,
|
|
113
|
+
"X-Frame-Options": "DENY",
|
|
114
|
+
"X-Content-Type-Options": "nosniff",
|
|
115
|
+
"Referrer-Policy": "no-referrer",
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def pod_holder() -> str:
|
|
120
|
+
return f"pod:{os.environ.get('HOSTNAME') or socket.gethostname()}"
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
async def reap_forever(
|
|
124
|
+
session_factory,
|
|
125
|
+
*,
|
|
126
|
+
interval: float = RENEW_EVERY_SECONDS,
|
|
127
|
+
give_up_after: float = LEASE_SECONDS,
|
|
128
|
+
) -> None:
|
|
129
|
+
failing_since: float | None = None
|
|
130
|
+
while True:
|
|
131
|
+
try:
|
|
132
|
+
reaped = await reap(session_factory)
|
|
133
|
+
reaped_imports = await reap_imports(session_factory)
|
|
134
|
+
except Exception:
|
|
135
|
+
now = time.monotonic()
|
|
136
|
+
failing_since = now if failing_since is None else failing_since
|
|
137
|
+
if now - failing_since > give_up_after:
|
|
138
|
+
raise
|
|
139
|
+
LOGGER.warning("lease reaper pass failed; retrying", exc_info=True)
|
|
140
|
+
else:
|
|
141
|
+
failing_since = None
|
|
142
|
+
for exp_id in reaped:
|
|
143
|
+
LOGGER.warning(
|
|
144
|
+
"experiment %s failed: its runner stopped renewing the lease", exp_id
|
|
145
|
+
)
|
|
146
|
+
for import_id in reaped_imports:
|
|
147
|
+
LOGGER.warning(
|
|
148
|
+
"trace import %s failed: its task stopped renewing the lease", import_id
|
|
149
|
+
)
|
|
150
|
+
await asyncio.sleep(interval)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def stop_if_reaper_died(task: asyncio.Task) -> None:
|
|
154
|
+
if task.cancelled() or task.exception() is None:
|
|
155
|
+
return
|
|
156
|
+
LOGGER.critical("the lease reaper stopped; shutting the server down", exc_info=task.exception())
|
|
157
|
+
os.kill(os.getpid(), signal.SIGTERM)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def create_app(
|
|
161
|
+
cfg: Config,
|
|
162
|
+
*,
|
|
163
|
+
blob_store: BlobStore | None = None,
|
|
164
|
+
runner_deps: RunnerDeps | None = None,
|
|
165
|
+
agents: AgentStore | None = None,
|
|
166
|
+
session_factory=None,
|
|
167
|
+
check_schema: bool = True,
|
|
168
|
+
ui_dist: Path | None = None,
|
|
169
|
+
holder: str | None = None,
|
|
170
|
+
mcp_jwt_verifier: TokenVerifier | None = None,
|
|
171
|
+
) -> FastAPI:
|
|
172
|
+
resolved_blob_store = blob_store if blob_store is not None else Obstore(cfg.blob.url)
|
|
173
|
+
session_factory = (
|
|
174
|
+
session_factory if session_factory is not None else make_session_factory(cfg.db.url)
|
|
175
|
+
)
|
|
176
|
+
resolved_store = PgStore(session_factory)
|
|
177
|
+
resolved_agents = agents if agents is not None else AgentStore(session_factory)
|
|
178
|
+
resolved_spans = SpanStore(session_factory)
|
|
179
|
+
resolved_mappings = MappingStore(session_factory)
|
|
180
|
+
resolved_sessions = DbSessionReader(
|
|
181
|
+
resolved_agents, resolved_spans, resolved_mappings, cfg.otel
|
|
182
|
+
)
|
|
183
|
+
resolved_deps = (
|
|
184
|
+
runner_deps
|
|
185
|
+
if runner_deps is not None
|
|
186
|
+
else RunnerDeps(
|
|
187
|
+
cfg=cfg,
|
|
188
|
+
store=resolved_store,
|
|
189
|
+
blob_store=resolved_blob_store,
|
|
190
|
+
agents=resolved_agents,
|
|
191
|
+
evaluators=RemoteEvaluatorStore(session_factory),
|
|
192
|
+
credentials=NoKeysLoaded(),
|
|
193
|
+
sessions=resolved_sessions,
|
|
194
|
+
)
|
|
195
|
+
)
|
|
196
|
+
if resolved_deps.sessions is None:
|
|
197
|
+
resolved_deps.sessions = resolved_sessions
|
|
198
|
+
box = SecretBox(cfg.encryption_key) if cfg.encryption_key else None
|
|
199
|
+
key_loader = KeyLoader(session_factory, box)
|
|
200
|
+
if resolved_deps.agent_keys is None:
|
|
201
|
+
resolved_deps.agent_keys = key_loader
|
|
202
|
+
|
|
203
|
+
@asynccontextmanager
|
|
204
|
+
async def lifespan(app: FastAPI):
|
|
205
|
+
# Migrations are an explicit operator step (`evalkit db upgrade`) unless
|
|
206
|
+
# db.migrate_on_start opts in; then they run under an advisory lock so
|
|
207
|
+
# replicas never race. Either way the server verifies the schema and
|
|
208
|
+
# fails fast when it is missing or outdated.
|
|
209
|
+
if cfg.db.migrate_on_start:
|
|
210
|
+
await migrate_under_lock(cfg.db.url)
|
|
211
|
+
if check_schema:
|
|
212
|
+
await verify_schema(cfg.db.url)
|
|
213
|
+
if cfg.encryption_key is None and await count_sealed(app.state.session_factory):
|
|
214
|
+
raise RuntimeError(
|
|
215
|
+
"stored API keys exist but encryption_key is not configured; "
|
|
216
|
+
"set encryption_key: env:EVALKIT_ENCRYPTION_KEY in the server config"
|
|
217
|
+
)
|
|
218
|
+
await bootstrap_admin(app.state.session_factory, os.environ.get("EVALKIT_ADMIN_PASSWORD"))
|
|
219
|
+
reaper = asyncio.create_task(reap_forever(app.state.session_factory))
|
|
220
|
+
reaper.add_done_callback(stop_if_reaper_died)
|
|
221
|
+
async with mcp_running(mcp_app):
|
|
222
|
+
yield
|
|
223
|
+
reaper.cancel()
|
|
224
|
+
await asyncio.gather(reaper, return_exceptions=True)
|
|
225
|
+
await experiments.interrupt_tasks(resolved_store, app.state.tasks)
|
|
226
|
+
await trace_imports.interrupt_imports(app.state.trace_imports, app.state.import_tasks)
|
|
227
|
+
await resolved_deps.store.aclose()
|
|
228
|
+
engine = app.state.session_factory.kw.get("bind")
|
|
229
|
+
if engine is not None:
|
|
230
|
+
await engine.dispose()
|
|
231
|
+
|
|
232
|
+
app = FastAPI(title="evalkit", lifespan=lifespan, docs_url=None, redoc_url=None)
|
|
233
|
+
app.add_exception_handler(StarletteHTTPException, otel.log_rejection)
|
|
234
|
+
app.state.cfg = cfg
|
|
235
|
+
app.state.runner_deps = resolved_deps
|
|
236
|
+
app.state.review_log = ReviewLog(session_factory)
|
|
237
|
+
app.state.store = resolved_store
|
|
238
|
+
app.state.holder = holder or pod_holder()
|
|
239
|
+
app.state.agents = resolved_agents
|
|
240
|
+
app.state.tokens = AgentTokens(session_factory)
|
|
241
|
+
app.state.spans = resolved_spans
|
|
242
|
+
app.state.mappings = resolved_mappings
|
|
243
|
+
app.state.sessions = resolved_sessions
|
|
244
|
+
app.state.session_factory = session_factory
|
|
245
|
+
app.state.trace_sources = TraceSourceStore(session_factory, box)
|
|
246
|
+
app.state.keys = key_loader
|
|
247
|
+
app.state.llm_providers = LlmProviderStore(session_factory)
|
|
248
|
+
app.state.tasks = {}
|
|
249
|
+
app.state.trace_imports = TraceImportStore(session_factory)
|
|
250
|
+
app.state.import_tasks = {}
|
|
251
|
+
|
|
252
|
+
running_version = version.current()
|
|
253
|
+
|
|
254
|
+
app.add_exception_handler(SessionTooLarge, traces.session_too_large)
|
|
255
|
+
|
|
256
|
+
@app.exception_handler(UnknownDataset)
|
|
257
|
+
async def unknown_dataset(request: Request, exc: UnknownDataset) -> JSONResponse:
|
|
258
|
+
return JSONResponse(status_code=404, content={"detail": str(exc)})
|
|
259
|
+
|
|
260
|
+
@app.exception_handler(UnknownExperiment)
|
|
261
|
+
@app.exception_handler(UnknownWorkspace)
|
|
262
|
+
@app.exception_handler(UnknownUser)
|
|
263
|
+
@app.exception_handler(UnknownAgent)
|
|
264
|
+
@app.exception_handler(UnknownEvaluator)
|
|
265
|
+
@app.exception_handler(UnknownTraceSource)
|
|
266
|
+
@app.exception_handler(UnknownTraceImport)
|
|
267
|
+
@app.exception_handler(UnknownLlmProvider)
|
|
268
|
+
async def unknown_name(request: Request, exc: KeyError) -> JSONResponse:
|
|
269
|
+
return JSONResponse(status_code=404, content={"detail": exc.args[0]})
|
|
270
|
+
|
|
271
|
+
@app.exception_handler(NameTaken)
|
|
272
|
+
@app.exception_handler(LastAdmin)
|
|
273
|
+
@app.exception_handler(AgentExists)
|
|
274
|
+
@app.exception_handler(EvaluatorExists)
|
|
275
|
+
@app.exception_handler(TraceSourceExists)
|
|
276
|
+
@app.exception_handler(AgentArchived)
|
|
277
|
+
@app.exception_handler(Conflict)
|
|
278
|
+
@app.exception_handler(LeaseLost)
|
|
279
|
+
@app.exception_handler(PromotionLinkError)
|
|
280
|
+
@app.exception_handler(DatasetExists)
|
|
281
|
+
@app.exception_handler(LlmProviderExists)
|
|
282
|
+
@app.exception_handler(ProviderInUse)
|
|
283
|
+
async def conflict(request: Request, exc: Exception) -> JSONResponse:
|
|
284
|
+
return JSONResponse(status_code=409, content={"detail": str(exc)})
|
|
285
|
+
|
|
286
|
+
@app.exception_handler(BareReference)
|
|
287
|
+
@app.exception_handler(SharedOnly)
|
|
288
|
+
@app.exception_handler(InvalidName)
|
|
289
|
+
@app.exception_handler(InvalidPassword)
|
|
290
|
+
@app.exception_handler(InvalidEvaluator)
|
|
291
|
+
@app.exception_handler(InvalidAgent)
|
|
292
|
+
@app.exception_handler(MissingCredential)
|
|
293
|
+
@app.exception_handler(EncryptionKeyMissing)
|
|
294
|
+
@app.exception_handler(BlockedEndpoint)
|
|
295
|
+
async def bad_request(request: Request, exc: Exception) -> JSONResponse:
|
|
296
|
+
return JSONResponse(status_code=400, content={"detail": str(exc)})
|
|
297
|
+
|
|
298
|
+
app.add_middleware(BodyLimitMiddleware, cfg=cfg)
|
|
299
|
+
|
|
300
|
+
@app.middleware("http")
|
|
301
|
+
async def stamp_version(request, call_next):
|
|
302
|
+
response = await call_next(request)
|
|
303
|
+
response.headers["X-Evalkit-Version"] = running_version
|
|
304
|
+
response.headers.update(SECURITY_HEADERS)
|
|
305
|
+
if request.url.scheme == "https":
|
|
306
|
+
response.headers["Strict-Transport-Security"] = "max-age=31536000; includeSubDomains"
|
|
307
|
+
return response
|
|
308
|
+
|
|
309
|
+
app.include_router(cli_dist.router)
|
|
310
|
+
|
|
311
|
+
@app.get("/api/health")
|
|
312
|
+
async def health() -> dict:
|
|
313
|
+
return {"ok": True}
|
|
314
|
+
|
|
315
|
+
app.include_router(auth_router)
|
|
316
|
+
wall = [Depends(current_user)]
|
|
317
|
+
app.include_router(datasets.router, dependencies=wall)
|
|
318
|
+
app.include_router(meta.router, dependencies=wall)
|
|
319
|
+
app.include_router(experiments.router, dependencies=wall)
|
|
320
|
+
app.include_router(experiments.runs_router, dependencies=wall)
|
|
321
|
+
app.include_router(agents_api.router, dependencies=wall)
|
|
322
|
+
app.include_router(evaluators.router, dependencies=wall)
|
|
323
|
+
app.include_router(trace_sources.router, dependencies=wall)
|
|
324
|
+
app.include_router(llm_providers.router, dependencies=wall)
|
|
325
|
+
app.include_router(runs.router, dependencies=wall)
|
|
326
|
+
app.include_router(pull.router, dependencies=wall)
|
|
327
|
+
app.include_router(harness_check.router, dependencies=wall)
|
|
328
|
+
app.include_router(imports.router, dependencies=wall)
|
|
329
|
+
app.include_router(review.router, dependencies=wall)
|
|
330
|
+
app.include_router(fixtures.router, dependencies=wall)
|
|
331
|
+
app.include_router(users.router, dependencies=wall)
|
|
332
|
+
app.include_router(workspaces.router, dependencies=wall)
|
|
333
|
+
app.include_router(tokens.router, dependencies=wall)
|
|
334
|
+
app.include_router(traces.router, dependencies=wall)
|
|
335
|
+
app.include_router(trace_imports.router, dependencies=wall)
|
|
336
|
+
app.include_router(otel.router)
|
|
337
|
+
mcp_app = mount_mcp(app, mcp_jwt_verifier)
|
|
338
|
+
|
|
339
|
+
if ui_dist is None:
|
|
340
|
+
ui_dist = ui_root()
|
|
341
|
+
if ui_dist.is_dir():
|
|
342
|
+
assets = ui_dist / "assets"
|
|
343
|
+
if assets.is_dir():
|
|
344
|
+
app.mount("/assets", StaticFiles(directory=assets), name="assets")
|
|
345
|
+
index = ui_dist / "index.html"
|
|
346
|
+
root = ui_dist.resolve()
|
|
347
|
+
|
|
348
|
+
# SPA history fallback: real files are served as-is; any other
|
|
349
|
+
# non-API path gets index.html so client-side routes survive a
|
|
350
|
+
# refresh or a direct link. Unmatched /api/* paths stay 404s.
|
|
351
|
+
@app.get("/{path:path}", include_in_schema=False)
|
|
352
|
+
async def spa(path: str):
|
|
353
|
+
if path.startswith(("api/", "assets/")):
|
|
354
|
+
raise HTTPException(status_code=404)
|
|
355
|
+
candidate = (ui_dist / path).resolve()
|
|
356
|
+
if candidate.is_relative_to(root) and candidate.is_file():
|
|
357
|
+
return FileResponse(candidate)
|
|
358
|
+
return FileResponse(index)
|
|
359
|
+
|
|
360
|
+
return app
|