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/db/agents.py
ADDED
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from datetime import UTC, datetime
|
|
4
|
+
|
|
5
|
+
from sqlalchemy import delete, select
|
|
6
|
+
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker
|
|
7
|
+
|
|
8
|
+
from evalkit.contracts.enums import ExperimentStatus, TokenKind, TrajectorySource
|
|
9
|
+
from evalkit.contracts.esf import MappingDocument
|
|
10
|
+
from evalkit.contracts.settings import AgentSettings
|
|
11
|
+
from evalkit.core.agents import AgentDefinition, UnknownAgent
|
|
12
|
+
from evalkit.core.workspace import Reference, parse_slug
|
|
13
|
+
from evalkit.db import models
|
|
14
|
+
from evalkit.db.locks import lock_name
|
|
15
|
+
from evalkit.db.mappings import activate_document, active_mapping
|
|
16
|
+
from evalkit.db.workspaces import workspace_row
|
|
17
|
+
from evalkit.store.base import Conflict
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
async def agent_id_for(session: AsyncSession, ref: str) -> int:
|
|
21
|
+
parsed = parse_slug(ref)
|
|
22
|
+
agent_id = await session.scalar(
|
|
23
|
+
select(models.Agent.id)
|
|
24
|
+
.join(models.Workspace, models.Workspace.id == models.Agent.workspace_id)
|
|
25
|
+
.where(models.Workspace.name == parsed.workspace, models.Agent.name == parsed.name)
|
|
26
|
+
)
|
|
27
|
+
if agent_id is None:
|
|
28
|
+
raise UnknownAgent(ref)
|
|
29
|
+
return agent_id
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class AgentExists(ValueError):
|
|
33
|
+
def __init__(self, ref: str):
|
|
34
|
+
super().__init__(f"agent exists: {ref}")
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class InvalidAgent(ValueError):
|
|
38
|
+
pass
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _definition(row: models.Agent, ws: models.Workspace) -> AgentDefinition:
|
|
42
|
+
return AgentDefinition(
|
|
43
|
+
id=row.id,
|
|
44
|
+
name=row.name,
|
|
45
|
+
workspace=ws.name,
|
|
46
|
+
workspace_is_shared=ws.owner_user_id is None,
|
|
47
|
+
harness_url=row.harness_url,
|
|
48
|
+
trajectory_source=TrajectorySource(row.trajectory_source),
|
|
49
|
+
repo_url=row.repo_url,
|
|
50
|
+
settings=AgentSettings.model_validate(row.config or {}),
|
|
51
|
+
archived=row.archived_at is not None,
|
|
52
|
+
has_api_key=row.api_key_encrypted is not None,
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _check_provides(
|
|
57
|
+
harness_url: str | None, source: TrajectorySource, settings: AgentSettings | None
|
|
58
|
+
) -> None:
|
|
59
|
+
if harness_url and source is TrajectorySource.ATIF and settings and settings.provides:
|
|
60
|
+
raise InvalidAgent(
|
|
61
|
+
"settings.provides is only allowed next to a harness_url when trajectories come "
|
|
62
|
+
"from traces; a harness declares its own capabilities"
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _ref(ref: str) -> Reference:
|
|
67
|
+
name, sep, workspace = ref.partition("@")
|
|
68
|
+
if not sep or not name or not workspace:
|
|
69
|
+
raise InvalidAgent(f"agent references are name@workspace: {ref!r}")
|
|
70
|
+
return Reference(name, workspace)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class AgentStore:
|
|
74
|
+
def __init__(self, session_factory: async_sessionmaker[AsyncSession]):
|
|
75
|
+
self._sessions = session_factory
|
|
76
|
+
|
|
77
|
+
async def _row(self, session: AsyncSession, ref: str) -> tuple[models.Agent, models.Workspace]:
|
|
78
|
+
name, workspace = _ref(ref)
|
|
79
|
+
found = (
|
|
80
|
+
await session.execute(
|
|
81
|
+
select(models.Agent, models.Workspace)
|
|
82
|
+
.join(models.Workspace, models.Workspace.id == models.Agent.workspace_id)
|
|
83
|
+
.where(models.Agent.name == name, models.Workspace.name == workspace)
|
|
84
|
+
)
|
|
85
|
+
).one_or_none()
|
|
86
|
+
if found is None:
|
|
87
|
+
raise UnknownAgent(ref)
|
|
88
|
+
return found[0], found[1]
|
|
89
|
+
|
|
90
|
+
async def _agent_id(self, session: AsyncSession, name: str, workspace: str) -> int | None:
|
|
91
|
+
return await session.scalar(
|
|
92
|
+
select(models.Agent.id)
|
|
93
|
+
.join(models.Workspace, models.Workspace.id == models.Agent.workspace_id)
|
|
94
|
+
.where(models.Agent.name == name, models.Workspace.name == workspace)
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
async def get(self, ref: str) -> AgentDefinition:
|
|
98
|
+
async with self._sessions() as session:
|
|
99
|
+
return _definition(*await self._row(session, ref))
|
|
100
|
+
|
|
101
|
+
async def find(self, ref: str) -> AgentDefinition | None:
|
|
102
|
+
name, workspace = _ref(ref)
|
|
103
|
+
async with self._sessions() as session:
|
|
104
|
+
found = (
|
|
105
|
+
await session.execute(
|
|
106
|
+
select(models.Agent, models.Workspace)
|
|
107
|
+
.join(models.Workspace, models.Workspace.id == models.Agent.workspace_id)
|
|
108
|
+
.where(models.Agent.name == name, models.Workspace.name == workspace)
|
|
109
|
+
)
|
|
110
|
+
).one_or_none()
|
|
111
|
+
return _definition(found[0], found[1]) if found is not None else None
|
|
112
|
+
|
|
113
|
+
async def get_by_id(self, agent_id: int) -> AgentDefinition:
|
|
114
|
+
async with self._sessions() as session:
|
|
115
|
+
found = (
|
|
116
|
+
await session.execute(
|
|
117
|
+
select(models.Agent, models.Workspace)
|
|
118
|
+
.join(models.Workspace, models.Workspace.id == models.Agent.workspace_id)
|
|
119
|
+
.where(models.Agent.id == agent_id)
|
|
120
|
+
)
|
|
121
|
+
).one_or_none()
|
|
122
|
+
if found is None:
|
|
123
|
+
raise UnknownAgent(str(agent_id))
|
|
124
|
+
return _definition(found[0], found[1])
|
|
125
|
+
|
|
126
|
+
async def create(
|
|
127
|
+
self,
|
|
128
|
+
workspace: str,
|
|
129
|
+
name: str,
|
|
130
|
+
*,
|
|
131
|
+
harness_url: str | None = None,
|
|
132
|
+
trajectory_source: TrajectorySource = TrajectorySource.ATIF,
|
|
133
|
+
repo_url: str | None = None,
|
|
134
|
+
settings: AgentSettings | None = None,
|
|
135
|
+
created_by: int | None = None,
|
|
136
|
+
mapping: dict | None = None,
|
|
137
|
+
api_key_encrypted: bytes | None = None,
|
|
138
|
+
) -> AgentDefinition:
|
|
139
|
+
if "@" in name or not name:
|
|
140
|
+
raise InvalidAgent(f"invalid agent name: {name!r}")
|
|
141
|
+
if trajectory_source is TrajectorySource.OTEL and mapping is None:
|
|
142
|
+
raise InvalidAgent("otel agents need a mapping")
|
|
143
|
+
if mapping is not None and trajectory_source is not TrajectorySource.OTEL:
|
|
144
|
+
raise InvalidAgent("only otel agents take a mapping")
|
|
145
|
+
_check_provides(harness_url, trajectory_source, settings)
|
|
146
|
+
if mapping is not None:
|
|
147
|
+
MappingDocument.model_validate(mapping)
|
|
148
|
+
async with self._sessions() as session:
|
|
149
|
+
ws = await workspace_row(session, workspace)
|
|
150
|
+
await lock_name(session, f"agent:{ws.id}", name)
|
|
151
|
+
if await self._agent_id(session, name, workspace) is not None:
|
|
152
|
+
raise AgentExists(f"{name}@{workspace}")
|
|
153
|
+
row = models.Agent(
|
|
154
|
+
workspace_id=ws.id,
|
|
155
|
+
name=name,
|
|
156
|
+
harness_url=harness_url,
|
|
157
|
+
trajectory_source=trajectory_source.value,
|
|
158
|
+
repo_url=repo_url,
|
|
159
|
+
config=(settings or AgentSettings()).model_dump(),
|
|
160
|
+
api_key_encrypted=api_key_encrypted,
|
|
161
|
+
created_by=created_by,
|
|
162
|
+
)
|
|
163
|
+
session.add(row)
|
|
164
|
+
await session.flush()
|
|
165
|
+
if mapping is not None:
|
|
166
|
+
await activate_document(session, row.id, mapping, created_by)
|
|
167
|
+
await session.commit()
|
|
168
|
+
return _definition(row, ws)
|
|
169
|
+
|
|
170
|
+
async def edit(
|
|
171
|
+
self,
|
|
172
|
+
ref: str,
|
|
173
|
+
*,
|
|
174
|
+
harness_url: str | None = None,
|
|
175
|
+
repo_url: str | None = None,
|
|
176
|
+
settings: AgentSettings | None = None,
|
|
177
|
+
clear: frozenset[str] = frozenset(),
|
|
178
|
+
trajectory_source: TrajectorySource | None = None,
|
|
179
|
+
mapping: dict | None = None,
|
|
180
|
+
edited_by: int | None = None,
|
|
181
|
+
api_key_encrypted: bytes | None = None,
|
|
182
|
+
) -> AgentDefinition:
|
|
183
|
+
if mapping is not None:
|
|
184
|
+
MappingDocument.model_validate(mapping)
|
|
185
|
+
async with self._sessions() as session:
|
|
186
|
+
row, ws = await self._row(session, ref)
|
|
187
|
+
if (harness_url is not None or "harness_url" in clear) and (
|
|
188
|
+
harness_url != row.harness_url
|
|
189
|
+
):
|
|
190
|
+
row.harness_url = harness_url
|
|
191
|
+
row.api_key_encrypted = None
|
|
192
|
+
if api_key_encrypted is not None or "api_key" in clear:
|
|
193
|
+
row.api_key_encrypted = api_key_encrypted
|
|
194
|
+
if repo_url is not None or "repo_url" in clear:
|
|
195
|
+
row.repo_url = repo_url
|
|
196
|
+
if settings is not None:
|
|
197
|
+
row.config = settings.model_dump()
|
|
198
|
+
source = trajectory_source or TrajectorySource(row.trajectory_source)
|
|
199
|
+
if mapping is not None and source is not TrajectorySource.OTEL:
|
|
200
|
+
raise InvalidAgent("only otel agents take a mapping")
|
|
201
|
+
if source.value != row.trajectory_source:
|
|
202
|
+
await self._switch_source(session, row, ref, source, mapping, edited_by)
|
|
203
|
+
elif mapping is not None:
|
|
204
|
+
await activate_document(session, row.id, mapping, edited_by)
|
|
205
|
+
_check_provides(
|
|
206
|
+
row.harness_url,
|
|
207
|
+
TrajectorySource(row.trajectory_source),
|
|
208
|
+
AgentSettings.model_validate(row.config or {}),
|
|
209
|
+
)
|
|
210
|
+
await session.commit()
|
|
211
|
+
return _definition(row, ws)
|
|
212
|
+
|
|
213
|
+
async def _switch_source(
|
|
214
|
+
self,
|
|
215
|
+
session: AsyncSession,
|
|
216
|
+
row: models.Agent,
|
|
217
|
+
ref: str,
|
|
218
|
+
source: TrajectorySource,
|
|
219
|
+
mapping: dict | None,
|
|
220
|
+
edited_by: int | None,
|
|
221
|
+
) -> None:
|
|
222
|
+
active = await session.scalar(
|
|
223
|
+
select(models.Experiment.number)
|
|
224
|
+
.where(
|
|
225
|
+
models.Experiment.agent_id == row.id,
|
|
226
|
+
models.Experiment.status.in_(
|
|
227
|
+
[ExperimentStatus.RUNNING.value, ExperimentStatus.EVALUATING.value]
|
|
228
|
+
),
|
|
229
|
+
)
|
|
230
|
+
.limit(1)
|
|
231
|
+
)
|
|
232
|
+
if active is not None:
|
|
233
|
+
raise Conflict(
|
|
234
|
+
f"{ref} has a running experiment; change its trajectory source when it has finished"
|
|
235
|
+
)
|
|
236
|
+
if source is TrajectorySource.OTEL:
|
|
237
|
+
if mapping is not None:
|
|
238
|
+
await activate_document(session, row.id, mapping, edited_by)
|
|
239
|
+
elif await active_mapping(session, row.id) is None:
|
|
240
|
+
raise InvalidAgent(
|
|
241
|
+
f"{ref} has no active mapping; pass one to switch its trajectory source to otel"
|
|
242
|
+
)
|
|
243
|
+
elif row.harness_url:
|
|
244
|
+
row.config = {**(row.config or {}), "provides": None}
|
|
245
|
+
row.trajectory_source = source.value
|
|
246
|
+
|
|
247
|
+
async def sealed_key(self, ref: str) -> bytes | None:
|
|
248
|
+
async with self._sessions() as session:
|
|
249
|
+
row, _ = await self._row(session, ref)
|
|
250
|
+
return row.api_key_encrypted
|
|
251
|
+
|
|
252
|
+
async def archive(self, ref: str) -> AgentDefinition:
|
|
253
|
+
async with self._sessions() as session:
|
|
254
|
+
row, ws = await self._row(session, ref)
|
|
255
|
+
row.archived_at = datetime.now(UTC)
|
|
256
|
+
await session.execute(
|
|
257
|
+
delete(models.AuthToken).where(
|
|
258
|
+
models.AuthToken.agent_id == row.id,
|
|
259
|
+
models.AuthToken.kind == TokenKind.AGENT.value,
|
|
260
|
+
)
|
|
261
|
+
)
|
|
262
|
+
await session.commit()
|
|
263
|
+
return _definition(row, ws)
|
|
264
|
+
|
|
265
|
+
async def restore(self, ref: str) -> AgentDefinition:
|
|
266
|
+
async with self._sessions() as session:
|
|
267
|
+
row, ws = await self._row(session, ref)
|
|
268
|
+
row.archived_at = None
|
|
269
|
+
await session.commit()
|
|
270
|
+
return _definition(row, ws)
|
|
271
|
+
|
|
272
|
+
async def list_agents(
|
|
273
|
+
self, workspace: str | None = None, include_archived: bool = False
|
|
274
|
+
) -> list[AgentDefinition]:
|
|
275
|
+
query = (
|
|
276
|
+
select(models.Agent, models.Workspace)
|
|
277
|
+
.join(models.Workspace, models.Workspace.id == models.Agent.workspace_id)
|
|
278
|
+
.order_by(
|
|
279
|
+
models.Workspace.owner_user_id.is_not(None),
|
|
280
|
+
models.Workspace.name,
|
|
281
|
+
models.Agent.name,
|
|
282
|
+
)
|
|
283
|
+
)
|
|
284
|
+
if workspace is not None:
|
|
285
|
+
query = query.where(models.Workspace.name == workspace)
|
|
286
|
+
if not include_archived:
|
|
287
|
+
query = query.where(models.Agent.archived_at.is_(None))
|
|
288
|
+
async with self._sessions() as session:
|
|
289
|
+
return [_definition(a, w) for a, w in (await session.execute(query)).all()]
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
from logging.config import fileConfig
|
|
3
|
+
|
|
4
|
+
from alembic import context
|
|
5
|
+
from sqlalchemy import pool
|
|
6
|
+
from sqlalchemy.engine import Connection
|
|
7
|
+
from sqlalchemy.ext.asyncio import async_engine_from_config
|
|
8
|
+
|
|
9
|
+
from evalkit.db.models import Base
|
|
10
|
+
|
|
11
|
+
# this is the Alembic Config object, which provides
|
|
12
|
+
# access to the values within the .ini file in use.
|
|
13
|
+
config = context.config
|
|
14
|
+
|
|
15
|
+
# Interpret the config file for Python logging.
|
|
16
|
+
# This line sets up loggers basically.
|
|
17
|
+
if config.config_file_name is not None:
|
|
18
|
+
fileConfig(config.config_file_name)
|
|
19
|
+
|
|
20
|
+
# add your model's MetaData object here
|
|
21
|
+
# for 'autogenerate' support
|
|
22
|
+
# from myapp import mymodel
|
|
23
|
+
# target_metadata = mymodel.Base.metadata
|
|
24
|
+
target_metadata = Base.metadata
|
|
25
|
+
|
|
26
|
+
# other values from the config, defined by the needs of env.py,
|
|
27
|
+
# can be acquired:
|
|
28
|
+
# my_important_option = config.get_main_option("my_important_option")
|
|
29
|
+
# ... etc.
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def run_migrations_offline() -> None:
|
|
33
|
+
"""Run migrations in 'offline' mode.
|
|
34
|
+
|
|
35
|
+
This configures the context with just a URL
|
|
36
|
+
and not an Engine, though an Engine is acceptable
|
|
37
|
+
here as well. By skipping the Engine creation
|
|
38
|
+
we don't even need a DBAPI to be available.
|
|
39
|
+
|
|
40
|
+
Calls to context.execute() here emit the given string to the
|
|
41
|
+
script output.
|
|
42
|
+
|
|
43
|
+
"""
|
|
44
|
+
url = config.get_main_option("sqlalchemy.url")
|
|
45
|
+
context.configure(
|
|
46
|
+
url=url,
|
|
47
|
+
target_metadata=target_metadata,
|
|
48
|
+
literal_binds=True,
|
|
49
|
+
dialect_opts={"paramstyle": "named"},
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
with context.begin_transaction():
|
|
53
|
+
context.run_migrations()
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def do_run_migrations(connection: Connection) -> None:
|
|
57
|
+
context.configure(connection=connection, target_metadata=target_metadata)
|
|
58
|
+
|
|
59
|
+
with context.begin_transaction():
|
|
60
|
+
context.run_migrations()
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
async def run_async_migrations() -> None:
|
|
64
|
+
"""In this scenario we need to create an Engine
|
|
65
|
+
and associate a connection with the context.
|
|
66
|
+
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
connectable = async_engine_from_config(
|
|
70
|
+
config.get_section(config.config_ini_section, {}),
|
|
71
|
+
prefix="sqlalchemy.",
|
|
72
|
+
poolclass=pool.NullPool,
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
async with connectable.connect() as connection:
|
|
76
|
+
await connection.run_sync(do_run_migrations)
|
|
77
|
+
|
|
78
|
+
await connectable.dispose()
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def run_migrations_online() -> None:
|
|
82
|
+
"""Run migrations in 'online' mode."""
|
|
83
|
+
|
|
84
|
+
asyncio.run(run_async_migrations())
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
if context.is_offline_mode():
|
|
88
|
+
run_migrations_offline()
|
|
89
|
+
else:
|
|
90
|
+
run_migrations_online()
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""${message}
|
|
2
|
+
|
|
3
|
+
Revision ID: ${up_revision}
|
|
4
|
+
Revises: ${down_revision | comma,n}
|
|
5
|
+
Create Date: ${create_date}
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
from typing import Sequence, Union
|
|
9
|
+
|
|
10
|
+
from alembic import op
|
|
11
|
+
import sqlalchemy as sa
|
|
12
|
+
${imports if imports else ""}
|
|
13
|
+
|
|
14
|
+
# revision identifiers, used by Alembic.
|
|
15
|
+
revision: str = ${repr(up_revision)}
|
|
16
|
+
down_revision: Union[str, Sequence[str], None] = ${repr(down_revision)}
|
|
17
|
+
branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)}
|
|
18
|
+
depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)}
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def upgrade() -> None:
|
|
22
|
+
"""Upgrade schema."""
|
|
23
|
+
${upgrades if upgrades else "pass"}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def downgrade() -> None:
|
|
27
|
+
"""Downgrade schema."""
|
|
28
|
+
${downgrades if downgrades else "pass"}
|