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.
Files changed (371) hide show
  1. evalkit/__init__.py +0 -0
  2. evalkit/api/__init__.py +0 -0
  3. evalkit/api/access.py +74 -0
  4. evalkit/api/agents.py +159 -0
  5. evalkit/api/app.py +360 -0
  6. evalkit/api/auth.py +225 -0
  7. evalkit/api/cli_dist.py +10 -0
  8. evalkit/api/datasets.py +169 -0
  9. evalkit/api/deps.py +79 -0
  10. evalkit/api/download.py +15 -0
  11. evalkit/api/evaluators.py +121 -0
  12. evalkit/api/experiments.py +616 -0
  13. evalkit/api/fixtures.py +95 -0
  14. evalkit/api/harness_check.py +48 -0
  15. evalkit/api/imports.py +180 -0
  16. evalkit/api/limits.py +84 -0
  17. evalkit/api/llm_providers.py +120 -0
  18. evalkit/api/mcp/__init__.py +0 -0
  19. evalkit/api/mcp/auth.py +100 -0
  20. evalkit/api/mcp/descriptions.py +336 -0
  21. evalkit/api/mcp/models.py +259 -0
  22. evalkit/api/mcp/server.py +101 -0
  23. evalkit/api/mcp/tools.py +844 -0
  24. evalkit/api/meta.py +19 -0
  25. evalkit/api/otel.py +350 -0
  26. evalkit/api/pull.py +197 -0
  27. evalkit/api/review.py +271 -0
  28. evalkit/api/runs.py +555 -0
  29. evalkit/api/server_keys.py +44 -0
  30. evalkit/api/tokens.py +51 -0
  31. evalkit/api/trace_imports.py +173 -0
  32. evalkit/api/trace_sources.py +49 -0
  33. evalkit/api/traces.py +196 -0
  34. evalkit/api/users.py +72 -0
  35. evalkit/api/workspaces.py +131 -0
  36. evalkit/bundled/contracts/atif-accepted.json +5 -0
  37. evalkit/bundled/contracts/custom-evaluator-api-v1.json +243 -0
  38. evalkit/bundled/contracts/harness-api-v1.json +938 -0
  39. evalkit/bundled/contracts/span-mapping-v1.json +314 -0
  40. evalkit/bundled/docs/concepts.md +26 -0
  41. evalkit/bundled/docs/config.md +38 -0
  42. evalkit/bundled/docs/harness-api.md +44 -0
  43. evalkit/bundled/docs/install.md +60 -0
  44. evalkit/bundled/docs/login.md +36 -0
  45. evalkit/bundled/docs/mappings.md +136 -0
  46. evalkit/bundled/docs/mcp.md +44 -0
  47. evalkit/bundled/docs/quickstart.md +142 -0
  48. evalkit/bundled/docs/sdk-runs.md +171 -0
  49. evalkit/bundled/docs/tracing.md +171 -0
  50. evalkit/bundled/docs/try-locally.md +288 -0
  51. evalkit/bundled/docs/workspaces.md +45 -0
  52. evalkit/bundled/skills/authoring-eval-cases/SKILL.md +387 -0
  53. evalkit/bundled/skills/authoring-eval-cases/references/overlays.md +83 -0
  54. evalkit/bundled/skills/authoring-eval-cases/references/tool-checks.md +71 -0
  55. evalkit/bundled/skills/connect-traces/SKILL.md +296 -0
  56. evalkit/bundled/skills/connect-traces/references/gen_ai.json +42 -0
  57. evalkit/bundled/skills/connect-traces/references/openinference.json +35 -0
  58. evalkit/bundled/skills/connect-traces/references/span-mapping-schema.md +88 -0
  59. evalkit/bundled/skills/eval-driven-development/SKILL.md +458 -0
  60. evalkit/bundled/skills/eval-driven-development/references/analyze-run.md +97 -0
  61. evalkit/bundled/skills/eval-driven-development/references/diagnose.md +130 -0
  62. evalkit/bundled/skills/eval-driven-development/references/synthesize-cases.md +87 -0
  63. evalkit/bundled/skills/eval-driven-development/references/synthesize.md +165 -0
  64. evalkit/bundled/skills/evalkit-deployment/SKILL.md +332 -0
  65. evalkit/bundled/skills/evalkit-deployment/config.example.yaml +49 -0
  66. evalkit/bundled/skills/evalkit-deployment/docker-compose.yml +51 -0
  67. evalkit/bundled/skills/evalkit-deployment/references/config-and-deploy.md +333 -0
  68. evalkit/bundled/skills/evalkit-deployment/references/harness-api.md +139 -0
  69. evalkit/bundled/skills/evalkit-deployment/try/.env.example +11 -0
  70. evalkit/bundled/skills/evalkit-deployment/try/agent.py +131 -0
  71. evalkit/bundled/skills/evalkit-deployment/try/cases/acme/balance-sheet/cases.yaml +25 -0
  72. evalkit/bundled/skills/evalkit-deployment/try/cases/acme/income-statement/cases.yaml +43 -0
  73. evalkit/bundled/skills/evalkit-deployment/try/config.yaml +17 -0
  74. evalkit/bundled/skills/evalkit-deployment/try/docker-compose.yml +82 -0
  75. evalkit/bundled/skills/evalkit-deployment/try/evalkit.yaml +4 -0
  76. evalkit/bundled/skills/evalkit-deployment/try/mapping.json +42 -0
  77. evalkit/bundled/skills/evalkit-deployment/try/run.py +33 -0
  78. evalkit/bundled/skills/evalkit-usage/SKILL.md +509 -0
  79. evalkit/bundled/skills/evalkit-usage/references/suite-fixtures.md +42 -0
  80. evalkit/bundled/ui/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  81. evalkit/bundled/ui/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  82. evalkit/bundled/ui/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  83. evalkit/bundled/ui/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  84. evalkit/bundled/ui/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  85. evalkit/bundled/ui/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  86. evalkit/bundled/ui/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  87. evalkit/bundled/ui/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  88. evalkit/bundled/ui/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  89. evalkit/bundled/ui/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  90. evalkit/bundled/ui/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  91. evalkit/bundled/ui/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  92. evalkit/bundled/ui/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  93. evalkit/bundled/ui/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  94. evalkit/bundled/ui/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  95. evalkit/bundled/ui/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  96. evalkit/bundled/ui/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  97. evalkit/bundled/ui/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  98. evalkit/bundled/ui/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  99. evalkit/bundled/ui/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  100. evalkit/bundled/ui/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  101. evalkit/bundled/ui/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  102. evalkit/bundled/ui/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  103. evalkit/bundled/ui/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  104. evalkit/bundled/ui/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  105. evalkit/bundled/ui/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  106. evalkit/bundled/ui/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  107. evalkit/bundled/ui/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  108. evalkit/bundled/ui/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  109. evalkit/bundled/ui/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  110. evalkit/bundled/ui/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  111. evalkit/bundled/ui/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  112. evalkit/bundled/ui/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  113. evalkit/bundled/ui/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  114. evalkit/bundled/ui/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  115. evalkit/bundled/ui/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  116. evalkit/bundled/ui/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  117. evalkit/bundled/ui/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  118. evalkit/bundled/ui/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  119. evalkit/bundled/ui/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  120. evalkit/bundled/ui/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  121. evalkit/bundled/ui/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  122. evalkit/bundled/ui/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  123. evalkit/bundled/ui/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  124. evalkit/bundled/ui/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  125. evalkit/bundled/ui/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  126. evalkit/bundled/ui/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  127. evalkit/bundled/ui/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  128. evalkit/bundled/ui/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  129. evalkit/bundled/ui/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  130. evalkit/bundled/ui/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  131. evalkit/bundled/ui/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  132. evalkit/bundled/ui/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  133. evalkit/bundled/ui/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  134. evalkit/bundled/ui/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  135. evalkit/bundled/ui/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  136. evalkit/bundled/ui/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  137. evalkit/bundled/ui/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  138. evalkit/bundled/ui/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  139. evalkit/bundled/ui/assets/abap-08VXUWAP.js +1 -0
  140. evalkit/bundled/ui/assets/apex-BWPQTe0t.js +1 -0
  141. evalkit/bundled/ui/assets/azcli-Bc_sGQ0U.js +1 -0
  142. evalkit/bundled/ui/assets/bat-i0X4ZdIN.js +1 -0
  143. evalkit/bundled/ui/assets/bicep-B5-_aFwp.js +2 -0
  144. evalkit/bundled/ui/assets/cameligo-DMUM7wLl.js +1 -0
  145. evalkit/bundled/ui/assets/clojure-Cm7r79vr.js +1 -0
  146. evalkit/bundled/ui/assets/codicon-DF1abBS2.ttf +0 -0
  147. evalkit/bundled/ui/assets/coffee-Ba7i2nA0.js +1 -0
  148. evalkit/bundled/ui/assets/cpp-C7h46wYY.js +1 -0
  149. evalkit/bundled/ui/assets/csharp-BKxtCVv1.js +1 -0
  150. evalkit/bundled/ui/assets/csp-bTuwJoIa.js +1 -0
  151. evalkit/bundled/ui/assets/css-DIMkf-bt.js +3 -0
  152. evalkit/bundled/ui/assets/css.worker-DwfUZ3fB.js +96 -0
  153. evalkit/bundled/ui/assets/cssMode-C97l1pbB.js +1 -0
  154. evalkit/bundled/ui/assets/cypher-CVaqCwHa.js +1 -0
  155. evalkit/bundled/ui/assets/dart-onAF5SnQ.js +1 -0
  156. evalkit/bundled/ui/assets/dockerfile-DZFCIeNp.js +1 -0
  157. evalkit/bundled/ui/assets/ecl-94qehhXe.js +1 -0
  158. evalkit/bundled/ui/assets/editor-DjpsE8to.css +1 -0
  159. evalkit/bundled/ui/assets/editor.api-NTesIxw5.js +908 -0
  160. evalkit/bundled/ui/assets/editor.worker-CRz__AKW.js +33 -0
  161. evalkit/bundled/ui/assets/elixir-6RTg0lbw.js +1 -0
  162. evalkit/bundled/ui/assets/flow9-C5_-GSwl.js +1 -0
  163. evalkit/bundled/ui/assets/freemarker2-BWqEFagp.js +3 -0
  164. evalkit/bundled/ui/assets/fsharp-C8Ef5oNN.js +1 -0
  165. evalkit/bundled/ui/assets/go-C-y9NEjX.js +1 -0
  166. evalkit/bundled/ui/assets/graphql-fmXr3nnJ.js +1 -0
  167. evalkit/bundled/ui/assets/handlebars-DkIF-whN.js +1 -0
  168. evalkit/bundled/ui/assets/hcl-CpzslTdj.js +1 -0
  169. evalkit/bundled/ui/assets/html-C_rpeQXJ.js +1 -0
  170. evalkit/bundled/ui/assets/html.worker-D9ugpX4y.js +509 -0
  171. evalkit/bundled/ui/assets/htmlMode-B0YIq3DF.js +1 -0
  172. evalkit/bundled/ui/assets/index-Cw-jBH6M.js +736 -0
  173. evalkit/bundled/ui/assets/index-DKtoQZtm.css +1 -0
  174. evalkit/bundled/ui/assets/ini-sBoK_t0W.js +1 -0
  175. evalkit/bundled/ui/assets/java-BEtHBSE6.js +1 -0
  176. evalkit/bundled/ui/assets/javascript-D4EGhYA6.js +1 -0
  177. evalkit/bundled/ui/assets/jetbrains-mono-cyrillic-wght-normal-D73BlboJ.woff2 +0 -0
  178. evalkit/bundled/ui/assets/jetbrains-mono-greek-wght-normal-Bw9x6K1M.woff2 +0 -0
  179. evalkit/bundled/ui/assets/jetbrains-mono-latin-ext-wght-normal-DBQx-q_a.woff2 +0 -0
  180. evalkit/bundled/ui/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2 +0 -0
  181. evalkit/bundled/ui/assets/jetbrains-mono-vietnamese-wght-normal-Bt-aOZkq.woff2 +0 -0
  182. evalkit/bundled/ui/assets/json.worker-CV9fqkUp.js +65 -0
  183. evalkit/bundled/ui/assets/jsonMode-pQjSfIH6.js +7 -0
  184. evalkit/bundled/ui/assets/julia-Bri6UV-V.js +1 -0
  185. evalkit/bundled/ui/assets/kotlin-BOotOW0E.js +1 -0
  186. evalkit/bundled/ui/assets/less-B9JPFI3C.js +2 -0
  187. evalkit/bundled/ui/assets/lexon-CfSJPG6W.js +1 -0
  188. evalkit/bundled/ui/assets/liquid-DbrvoYjD.js +1 -0
  189. evalkit/bundled/ui/assets/lspLanguageFeatures-D0B7cH6y.js +4 -0
  190. evalkit/bundled/ui/assets/lua-CsQS60Ue.js +1 -0
  191. evalkit/bundled/ui/assets/m3-D-oSqn_W.js +1 -0
  192. evalkit/bundled/ui/assets/markdown-Cimd5fb3.js +1 -0
  193. evalkit/bundled/ui/assets/mdx-B0HrujLR.js +1 -0
  194. evalkit/bundled/ui/assets/mips-CIPQ_RoX.js +1 -0
  195. evalkit/bundled/ui/assets/monacoEditor-DsoPncxW.js +68 -0
  196. evalkit/bundled/ui/assets/monacoEditor-n7clI0si.css +1 -0
  197. evalkit/bundled/ui/assets/msdax-DauUninz.js +1 -0
  198. evalkit/bundled/ui/assets/mysql-SOo6toE5.js +1 -0
  199. evalkit/bundled/ui/assets/objective-c-FvmIjYaQ.js +1 -0
  200. evalkit/bundled/ui/assets/pascal-DrH0SRf2.js +1 -0
  201. evalkit/bundled/ui/assets/pascaligo-D-ptJ9y-.js +1 -0
  202. evalkit/bundled/ui/assets/perl-oz_6vUea.js +1 -0
  203. evalkit/bundled/ui/assets/pgsql-DTj74zXo.js +1 -0
  204. evalkit/bundled/ui/assets/php-nr791fC2.js +1 -0
  205. evalkit/bundled/ui/assets/pla-CopQ2nXW.js +1 -0
  206. evalkit/bundled/ui/assets/postiats-43DmfD33.js +1 -0
  207. evalkit/bundled/ui/assets/powerquery-D3hlyOfw.js +1 -0
  208. evalkit/bundled/ui/assets/powershell-DmHpPYUd.js +1 -0
  209. evalkit/bundled/ui/assets/protobuf-C531GsRP.js +2 -0
  210. evalkit/bundled/ui/assets/pug-Z5eAx3Zn.js +1 -0
  211. evalkit/bundled/ui/assets/python-CieHVM9n.js +1 -0
  212. evalkit/bundled/ui/assets/qsharp-DkqhCAOL.js +1 -0
  213. evalkit/bundled/ui/assets/r-BwWrilGY.js +1 -0
  214. evalkit/bundled/ui/assets/razor-sLewSMyp.js +1 -0
  215. evalkit/bundled/ui/assets/redis-ClamHrr6.js +1 -0
  216. evalkit/bundled/ui/assets/redshift-DT7zqm-g.js +1 -0
  217. evalkit/bundled/ui/assets/restructuredtext-BYgofb2h.js +1 -0
  218. evalkit/bundled/ui/assets/ruby-DezsRK8O.js +1 -0
  219. evalkit/bundled/ui/assets/rust-DdL9SqIa.js +1 -0
  220. evalkit/bundled/ui/assets/sb-CcwsVR0C.js +1 -0
  221. evalkit/bundled/ui/assets/scala-DHpiXF5c.js +1 -0
  222. evalkit/bundled/ui/assets/scheme-BeGwcela.js +1 -0
  223. evalkit/bundled/ui/assets/scss-gp-XZpBa.js +3 -0
  224. evalkit/bundled/ui/assets/shell-CC2rA5mh.js +1 -0
  225. evalkit/bundled/ui/assets/solidity-BEEn4gHE.js +1 -0
  226. evalkit/bundled/ui/assets/sophia-CRfGWb83.js +1 -0
  227. evalkit/bundled/ui/assets/sparql-D_Lu-MrJ.js +1 -0
  228. evalkit/bundled/ui/assets/sql-NEE52Syq.js +1 -0
  229. evalkit/bundled/ui/assets/st-DbInun42.js +1 -0
  230. evalkit/bundled/ui/assets/swift-Bxkupp3x.js +1 -0
  231. evalkit/bundled/ui/assets/systemverilog-Bz4Y3fRF.js +1 -0
  232. evalkit/bundled/ui/assets/tcl-DISqw1ZD.js +1 -0
  233. evalkit/bundled/ui/assets/ts.worker-DIAQkoB3.js +67741 -0
  234. evalkit/bundled/ui/assets/tsMode-BcVQ1nu0.js +11 -0
  235. evalkit/bundled/ui/assets/twig-De2hgUGE.js +1 -0
  236. evalkit/bundled/ui/assets/typescript-CUvNWd9p.js +1 -0
  237. evalkit/bundled/ui/assets/typespec-B8J7ngcE.js +1 -0
  238. evalkit/bundled/ui/assets/vb-DV3o63ZY.js +1 -0
  239. evalkit/bundled/ui/assets/wgsl-DpFanUEy.js +298 -0
  240. evalkit/bundled/ui/assets/workers-CwpYQtzp.js +1 -0
  241. evalkit/bundled/ui/assets/xml-C1HnXMdF.js +1 -0
  242. evalkit/bundled/ui/assets/yaml-D7Ey3HQu.js +1 -0
  243. evalkit/bundled/ui/favicon.svg +6 -0
  244. evalkit/bundled/ui/index.html +14 -0
  245. evalkit/cli/__init__.py +0 -0
  246. evalkit/cli/admin_commands.py +590 -0
  247. evalkit/cli/client.py +67 -0
  248. evalkit/cli/init_server.py +165 -0
  249. evalkit/cli/local_run.py +195 -0
  250. evalkit/cli/main.py +1365 -0
  251. evalkit/cli/runs_commands.py +539 -0
  252. evalkit/cli/server_commands.py +130 -0
  253. evalkit/cli/skill_targets.py +43 -0
  254. evalkit/cli/tokens_commands.py +52 -0
  255. evalkit/cli/traces_commands.py +332 -0
  256. evalkit/cli/update.py +85 -0
  257. evalkit/client/__init__.py +0 -0
  258. evalkit/client/agents.py +25 -0
  259. evalkit/client/api.py +193 -0
  260. evalkit/client/blob.py +104 -0
  261. evalkit/client/connection.py +88 -0
  262. evalkit/client/evaluators.py +11 -0
  263. evalkit/client/sessions.py +17 -0
  264. evalkit/client/store.py +139 -0
  265. evalkit/client/summary.py +46 -0
  266. evalkit/client/sync.py +198 -0
  267. evalkit/config.py +227 -0
  268. evalkit/contracts/__init__.py +0 -0
  269. evalkit/contracts/atif.py +115 -0
  270. evalkit/contracts/cases.py +456 -0
  271. evalkit/contracts/custom_evaluator.py +58 -0
  272. evalkit/contracts/enums.py +86 -0
  273. evalkit/contracts/esf.py +120 -0
  274. evalkit/contracts/harness.py +281 -0
  275. evalkit/contracts/notes.json +4 -0
  276. evalkit/contracts/notes.py +7 -0
  277. evalkit/contracts/settings.py +41 -0
  278. evalkit/core/__init__.py +0 -0
  279. evalkit/core/access.py +75 -0
  280. evalkit/core/agents.py +65 -0
  281. evalkit/core/blob.py +46 -0
  282. evalkit/core/capabilities.py +36 -0
  283. evalkit/core/costmap.py +90 -0
  284. evalkit/core/custom_eval.py +50 -0
  285. evalkit/core/data/cost_map.json +81546 -0
  286. evalkit/core/endpoints.py +31 -0
  287. evalkit/core/evaluators/__init__.py +29 -0
  288. evalkit/core/evaluators/base.py +121 -0
  289. evalkit/core/evaluators/gauges.py +84 -0
  290. evalkit/core/evaluators/presentation.py +166 -0
  291. evalkit/core/evaluators/response_time.py +21 -0
  292. evalkit/core/evaluators/rubrics.py +494 -0
  293. evalkit/core/evaluators/tool_trajectory.py +187 -0
  294. evalkit/core/evaluators/trajectory_rubrics.py +198 -0
  295. evalkit/core/fixtures.py +57 -0
  296. evalkit/core/harness.py +227 -0
  297. evalkit/core/harness_check.py +191 -0
  298. evalkit/core/importer.py +257 -0
  299. evalkit/core/judge.py +78 -0
  300. evalkit/core/lint.py +265 -0
  301. evalkit/core/overlays.py +76 -0
  302. evalkit/core/paths.py +25 -0
  303. evalkit/core/promotion.py +94 -0
  304. evalkit/core/pull.py +198 -0
  305. evalkit/core/python_eval.py +59 -0
  306. evalkit/core/remote_evaluators.py +32 -0
  307. evalkit/core/review.py +220 -0
  308. evalkit/core/run_digest.py +421 -0
  309. evalkit/core/runner.py +995 -0
  310. evalkit/core/scorecard.py +255 -0
  311. evalkit/core/traces/__init__.py +0 -0
  312. evalkit/core/traces/convert.py +336 -0
  313. evalkit/core/traces/importer.py +113 -0
  314. evalkit/core/traces/mapping.py +385 -0
  315. evalkit/core/traces/poll.py +38 -0
  316. evalkit/core/traces/raw.py +21 -0
  317. evalkit/core/traces/sessions.py +172 -0
  318. evalkit/core/traces/settle.py +14 -0
  319. evalkit/core/traces/sources/__init__.py +19 -0
  320. evalkit/core/traces/sources/base.py +25 -0
  321. evalkit/core/traces/sources/phoenix.py +121 -0
  322. evalkit/core/trajectory.py +248 -0
  323. evalkit/core/workspace.py +30 -0
  324. evalkit/credentials.py +119 -0
  325. evalkit/db/__init__.py +0 -0
  326. evalkit/db/agents.py +289 -0
  327. evalkit/db/alembic/env.py +90 -0
  328. evalkit/db/alembic/script.py.mako +28 -0
  329. evalkit/db/alembic/versions/0011_baseline.py +1002 -0
  330. evalkit/db/alembic/versions/0012_ticket_token_kind.py +27 -0
  331. evalkit/db/alembic/versions/0013_keys_and_llm_providers.py +67 -0
  332. evalkit/db/cases.py +608 -0
  333. evalkit/db/crypto.py +12 -0
  334. evalkit/db/evaluators.py +156 -0
  335. evalkit/db/experiments.py +363 -0
  336. evalkit/db/fixture_blobs.py +91 -0
  337. evalkit/db/keys.py +78 -0
  338. evalkit/db/llm_providers.py +176 -0
  339. evalkit/db/locks.py +6 -0
  340. evalkit/db/mappings.py +168 -0
  341. evalkit/db/models.py +567 -0
  342. evalkit/db/promotion.py +271 -0
  343. evalkit/db/rekey.py +46 -0
  344. evalkit/db/review.py +250 -0
  345. evalkit/db/review_state.py +50 -0
  346. evalkit/db/runs.py +533 -0
  347. evalkit/db/scorecard.py +300 -0
  348. evalkit/db/session.py +71 -0
  349. evalkit/db/sessions.py +90 -0
  350. evalkit/db/spans.py +360 -0
  351. evalkit/db/store.py +209 -0
  352. evalkit/db/tokens.py +100 -0
  353. evalkit/db/trace_imports.py +308 -0
  354. evalkit/db/trace_sources.py +131 -0
  355. evalkit/db/users.py +186 -0
  356. evalkit/db/workspaces.py +232 -0
  357. evalkit/paths.py +33 -0
  358. evalkit/sdk/__init__.py +29 -0
  359. evalkit/sdk/binding.py +69 -0
  360. evalkit/sdk/evaluators.py +56 -0
  361. evalkit/sdk/runner.py +957 -0
  362. evalkit/sdk/tracing.py +166 -0
  363. evalkit/store/__init__.py +0 -0
  364. evalkit/store/base.py +354 -0
  365. evalkit/store/blob.py +69 -0
  366. evalkit/version.py +44 -0
  367. evalkit_py-0.2.0.dev12.dist-info/METADATA +205 -0
  368. evalkit_py-0.2.0.dev12.dist-info/RECORD +371 -0
  369. evalkit_py-0.2.0.dev12.dist-info/WHEEL +4 -0
  370. evalkit_py-0.2.0.dev12.dist-info/entry_points.txt +2 -0
  371. evalkit_py-0.2.0.dev12.dist-info/licenses/LICENSE +661 -0
evalkit/__init__.py ADDED
File without changes
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