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/db/runs.py ADDED
@@ -0,0 +1,533 @@
1
+ from datetime import datetime
2
+ from typing import Literal
3
+
4
+ from pydantic import BaseModel
5
+ from sqlalchemy import Select, delete, func, select
6
+ from sqlalchemy.dialects.postgresql import insert
7
+ from sqlalchemy.ext.asyncio import AsyncSession
8
+
9
+ from evalkit.contracts.enums import EvaluationSource, EvaluatorKind, ExperimentStatus, RunStatus
10
+ from evalkit.core.runner import ACTIVE
11
+ from evalkit.core.scorecard import EXCLUDED_FROM_BUCKETS, bucket_for
12
+ from evalkit.db import models
13
+ from evalkit.db.cases import case_views
14
+ from evalkit.store.base import (
15
+ AgentRunRecord,
16
+ CaseRep,
17
+ Conflict,
18
+ EvaluationRow,
19
+ InvalidRun,
20
+ RunDetail,
21
+ RunEvalContext,
22
+ RunForScoring,
23
+ RunRef,
24
+ UnknownExperiment,
25
+ )
26
+
27
+ RunSource = Literal["app", "eval", "all"]
28
+
29
+
30
+ async def _experiment(session: AsyncSession, exp_id: int) -> models.Experiment:
31
+ exp = await session.get(models.Experiment, exp_id)
32
+ if exp is None:
33
+ raise UnknownExperiment(exp_id)
34
+ return exp
35
+
36
+
37
+ async def _leased_experiment(
38
+ session: AsyncSession, exp_id: int, holder: str | None
39
+ ) -> models.Experiment:
40
+ exp = await session.scalar(
41
+ select(models.Experiment).where(models.Experiment.id == exp_id).with_for_update(read=True)
42
+ )
43
+ if exp is None:
44
+ raise UnknownExperiment(exp_id)
45
+ if holder is not None and (
46
+ exp.lease_holder != holder or ExperimentStatus(exp.status) not in ACTIVE
47
+ ):
48
+ raise Conflict(
49
+ f"experiment {exp_id} is {exp.status}, held by {exp.lease_holder or 'nobody'}; "
50
+ f"{holder} no longer holds its lease"
51
+ )
52
+ return exp
53
+
54
+
55
+ async def _used_harness_run_id(session: AsyncSession, agent_id: int, harness_run_id: str) -> str:
56
+ row = (
57
+ await session.execute(
58
+ select(models.AgentRun.id, models.ExperimentRun.experiment_id)
59
+ .outerjoin(
60
+ models.ExperimentRun, models.ExperimentRun.agent_run_id == models.AgentRun.id
61
+ )
62
+ .where(
63
+ models.AgentRun.agent_id == agent_id,
64
+ models.AgentRun.harness_run_id == harness_run_id,
65
+ )
66
+ )
67
+ ).one()
68
+ where = f" in experiment {row[1]}" if row[1] is not None else ""
69
+ return (
70
+ f"run or session id {harness_run_id} was already used for agent run {row[0]}{where}; "
71
+ "every run needs its own id"
72
+ )
73
+
74
+
75
+ async def harness_run_id_used(
76
+ session: AsyncSession, exp_id: int, harness_run_id: str
77
+ ) -> str | None:
78
+ exp = await _experiment(session, exp_id)
79
+ taken = await session.scalar(
80
+ select(models.AgentRun.id).where(
81
+ models.AgentRun.agent_id == exp.agent_id,
82
+ models.AgentRun.harness_run_id == harness_run_id,
83
+ )
84
+ )
85
+ if taken is None:
86
+ return None
87
+ return await _used_harness_run_id(session, exp.agent_id, harness_run_id)
88
+
89
+
90
+ async def record_run(
91
+ session: AsyncSession,
92
+ exp_id: int,
93
+ case_id: str,
94
+ repetition: int,
95
+ run: AgentRunRecord,
96
+ created_by: int | None,
97
+ *,
98
+ holder: str | None = None,
99
+ ) -> RunRef:
100
+ exp = await _leased_experiment(session, exp_id, holder)
101
+ if not 1 <= repetition <= exp.repetitions:
102
+ raise InvalidRun(f"repetition {repetition} is outside 1..{exp.repetitions}")
103
+ case_ref = await session.scalar(
104
+ select(models.ExperimentCase.case_ref)
105
+ .join(models.Case, models.Case.id == models.ExperimentCase.case_ref)
106
+ .where(models.ExperimentCase.experiment_id == exp_id, models.Case.case_id == case_id)
107
+ )
108
+ if case_ref is None:
109
+ raise InvalidRun(f"case {case_id} is not pinned in experiment {exp_id}")
110
+ current = (
111
+ await session.execute(
112
+ select(models.ExperimentRun, models.AgentRun.status)
113
+ .join(models.AgentRun, models.AgentRun.id == models.ExperimentRun.agent_run_id)
114
+ .where(
115
+ models.ExperimentRun.experiment_id == exp_id,
116
+ models.ExperimentRun.case_ref == case_ref,
117
+ models.ExperimentRun.repetition == repetition,
118
+ )
119
+ .with_for_update(of=models.ExperimentRun)
120
+ )
121
+ ).one_or_none()
122
+ if current is not None and current[1] == RunStatus.COMPLETED.value:
123
+ raise Conflict(f"{case_id} #{repetition} already has a completed run")
124
+ agent_run_id = await session.scalar(
125
+ insert(models.AgentRun)
126
+ .values(
127
+ agent_id=exp.agent_id,
128
+ created_by=created_by if created_by is not None else exp.created_by,
129
+ **run.model_dump(mode="json", exclude_none=True),
130
+ )
131
+ .on_conflict_do_nothing(constraint="uq_agent_runs_agent_harness_run")
132
+ .returning(models.AgentRun.id)
133
+ )
134
+ if agent_run_id is None:
135
+ raise Conflict(await _used_harness_run_id(session, exp.agent_id, run.harness_run_id))
136
+ if current is None:
137
+ experiment_run = models.ExperimentRun(
138
+ experiment_id=exp_id,
139
+ case_ref=case_ref,
140
+ repetition=repetition,
141
+ agent_run_id=agent_run_id,
142
+ )
143
+ session.add(experiment_run)
144
+ await session.flush()
145
+ else:
146
+ experiment_run = current[0]
147
+ experiment_run.agent_run_id = agent_run_id
148
+ await session.execute(
149
+ delete(models.Evaluation).where(
150
+ models.Evaluation.experiment_run_id == experiment_run.id
151
+ )
152
+ )
153
+ await session.flush()
154
+ return RunRef(experiment_run_id=experiment_run.id, agent_run_id=agent_run_id)
155
+
156
+
157
+ async def pending_runs(session: AsyncSession, exp_id: int) -> list[CaseRep]:
158
+ exp = await _experiment(session, exp_id)
159
+ pinned = await session.execute(
160
+ select(models.Case.case_id, models.ExperimentCase.case_ref)
161
+ .join(models.Case, models.Case.id == models.ExperimentCase.case_ref)
162
+ .where(models.ExperimentCase.experiment_id == exp_id)
163
+ )
164
+ completed = set(
165
+ (
166
+ await session.execute(
167
+ select(models.ExperimentRun.case_ref, models.ExperimentRun.repetition)
168
+ .join(models.AgentRun, models.AgentRun.id == models.ExperimentRun.agent_run_id)
169
+ .where(
170
+ models.ExperimentRun.experiment_id == exp_id,
171
+ models.AgentRun.status == RunStatus.COMPLETED.value,
172
+ )
173
+ )
174
+ ).all()
175
+ )
176
+ return [
177
+ CaseRep(case_id=case_id, repetition=rep)
178
+ for case_id, ref in sorted(pinned.all())
179
+ for rep in range(1, exp.repetitions + 1)
180
+ if (ref, rep) not in completed
181
+ ]
182
+
183
+
184
+ def _record(row: models.AgentRun) -> AgentRunRecord:
185
+ return AgentRunRecord.model_validate(row, from_attributes=True)
186
+
187
+
188
+ async def runs_to_score(session: AsyncSession, exp_id: int, *, force: bool) -> list[RunForScoring]:
189
+ exp = await _experiment(session, exp_id)
190
+ rows = (
191
+ await session.execute(
192
+ select(models.ExperimentRun, models.AgentRun, models.ExperimentCase)
193
+ .join(models.AgentRun, models.AgentRun.id == models.ExperimentRun.agent_run_id)
194
+ .join(
195
+ models.ExperimentCase,
196
+ (models.ExperimentCase.experiment_id == models.ExperimentRun.experiment_id)
197
+ & (models.ExperimentCase.case_ref == models.ExperimentRun.case_ref),
198
+ )
199
+ .where(
200
+ models.ExperimentRun.experiment_id == exp_id,
201
+ models.AgentRun.error.is_(None) | models.AgentRun.output.is_not(None),
202
+ )
203
+ .order_by(models.ExperimentRun.id)
204
+ )
205
+ ).all()
206
+ pinned = {ec.case_ref: ec for _, _, ec in rows}
207
+ dataset = await session.get(models.Dataset, exp.dataset_id)
208
+ assert dataset is not None
209
+ views = {
210
+ v.case_ref: v
211
+ for v in await case_views(
212
+ session,
213
+ dataset.workspace_id,
214
+ [(ref, ec.content_hash) for ref, ec in pinned.items()],
215
+ agent=None,
216
+ dataset_id=dataset.id,
217
+ )
218
+ }
219
+ done: dict[int, set[str]] = {}
220
+ if not force:
221
+ succeeded = await session.execute(
222
+ select(models.Evaluation.experiment_run_id, models.Evaluation.name).where(
223
+ models.Evaluation.experiment_run_id.in_([er.id for er, _, _ in rows]),
224
+ models.Evaluation.error.is_(None),
225
+ )
226
+ )
227
+ for run_id, name in succeeded.all():
228
+ done.setdefault(run_id, set()).add(name)
229
+ return [
230
+ RunForScoring(
231
+ experiment_run_id=er.id,
232
+ case=views[er.case_ref].model_copy(
233
+ update={"effective_evaluators": pinned[er.case_ref].effective_evaluators}
234
+ ),
235
+ repetition=er.repetition,
236
+ agent_run=_record(ar),
237
+ agent_run_id=ar.id,
238
+ done=done.get(er.id, set()),
239
+ )
240
+ for er, ar, _ in rows
241
+ ]
242
+
243
+
244
+ def _bucket(row: EvaluationRow) -> str | None:
245
+ if row.error is not None or row.kind is EvaluatorKind.GAUGE:
246
+ return None
247
+ if row.name in EXCLUDED_FROM_BUCKETS:
248
+ return None
249
+ source = "builtin" if row.source is EvaluationSource.BUILTIN else f"custom:{row.producer}"
250
+ return bucket_for(row.name, source)
251
+
252
+
253
+ async def publish_evaluations(
254
+ session: AsyncSession,
255
+ experiment_run_id: int,
256
+ rows: list[EvaluationRow],
257
+ *,
258
+ force: bool = False,
259
+ holder: str | None = None,
260
+ ) -> None:
261
+ if holder is not None:
262
+ exp_id = await session.scalar(
263
+ select(models.ExperimentRun.experiment_id).where(
264
+ models.ExperimentRun.id == experiment_run_id
265
+ )
266
+ )
267
+ if exp_id is None:
268
+ raise InvalidRun(f"unknown experiment run: {experiment_run_id}")
269
+ await _leased_experiment(session, exp_id, holder)
270
+ succeeded = {r.producer for r in rows if r.error is None}
271
+ if succeeded:
272
+ await session.execute(
273
+ delete(models.Evaluation).where(
274
+ models.Evaluation.experiment_run_id == experiment_run_id,
275
+ models.Evaluation.producer.in_(succeeded),
276
+ models.Evaluation.error.is_not(None),
277
+ )
278
+ )
279
+ for row in rows:
280
+ values = {
281
+ **row.model_dump(mode="json"),
282
+ "experiment_run_id": experiment_run_id,
283
+ "bucket": _bucket(row),
284
+ }
285
+ statement = insert(models.Evaluation).values(**values)
286
+ update_set = {k: statement.excluded[k] for k in values if k != "experiment_run_id"}
287
+ upsert = statement.on_conflict_do_update(
288
+ constraint="uq_evaluations_run_name",
289
+ set_=update_set,
290
+ where=None if force else models.Evaluation.error.is_not(None),
291
+ )
292
+ await session.execute(upsert)
293
+
294
+
295
+ def _evaluation(row: models.Evaluation) -> EvaluationRow:
296
+ return EvaluationRow(
297
+ producer=row.producer,
298
+ name=row.name,
299
+ kind=EvaluatorKind(row.kind),
300
+ source=EvaluationSource(row.source),
301
+ value=row.value,
302
+ label=row.label,
303
+ explanation=row.explanation,
304
+ error=row.error,
305
+ )
306
+
307
+
308
+ async def run_details(session: AsyncSession, exp_id: int) -> list[RunDetail]:
309
+ await _experiment(session, exp_id)
310
+ rows = (
311
+ await session.execute(
312
+ select(models.ExperimentRun, models.AgentRun, models.Case.case_id)
313
+ .join(models.AgentRun, models.AgentRun.id == models.ExperimentRun.agent_run_id)
314
+ .join(models.Case, models.Case.id == models.ExperimentRun.case_ref)
315
+ .where(models.ExperimentRun.experiment_id == exp_id)
316
+ .order_by(models.Case.case_id, models.ExperimentRun.repetition)
317
+ )
318
+ ).all()
319
+ evaluations: dict[int, list[EvaluationRow]] = {}
320
+ if rows:
321
+ found = await session.scalars(
322
+ select(models.Evaluation)
323
+ .where(models.Evaluation.experiment_run_id.in_([er.id for er, _, _ in rows]))
324
+ .order_by(models.Evaluation.id)
325
+ )
326
+ for row in found.all():
327
+ evaluations.setdefault(row.experiment_run_id, []).append(_evaluation(row))
328
+ return [
329
+ RunDetail(
330
+ experiment_run_id=er.id,
331
+ case_id=case_id,
332
+ repetition=er.repetition,
333
+ agent_run_id=ar.id,
334
+ agent_run=_record(ar),
335
+ evaluations=evaluations.get(er.id, []),
336
+ )
337
+ for er, ar, case_id in rows
338
+ ]
339
+
340
+
341
+ class AgentRunItem(BaseModel):
342
+ id: int
343
+ harness_run_id: str
344
+ title: str | None
345
+ status: RunStatus
346
+ started_at: datetime
347
+ created_by: str | None
348
+ experiment_id: int | None
349
+ experiment_number: int | None
350
+ case_id: str | None
351
+ trajectory_key: str | None
352
+
353
+
354
+ def _items_query() -> Select:
355
+ return (
356
+ select(
357
+ models.AgentRun,
358
+ models.User.username,
359
+ models.Experiment.id,
360
+ models.Experiment.number,
361
+ models.Case.case_id,
362
+ )
363
+ .outerjoin(models.User, models.User.id == models.AgentRun.created_by)
364
+ .outerjoin(models.ExperimentRun, models.ExperimentRun.agent_run_id == models.AgentRun.id)
365
+ .outerjoin(models.Experiment, models.Experiment.id == models.ExperimentRun.experiment_id)
366
+ .outerjoin(models.Case, models.Case.id == models.ExperimentRun.case_ref)
367
+ )
368
+
369
+
370
+ def _item(row) -> AgentRunItem:
371
+ run, username, exp_id, number, case_id = row
372
+ return AgentRunItem(
373
+ id=run.id,
374
+ harness_run_id=run.harness_run_id,
375
+ title=run.title,
376
+ status=RunStatus(run.status),
377
+ started_at=run.started_at,
378
+ created_by=username,
379
+ experiment_id=exp_id,
380
+ experiment_number=number,
381
+ case_id=case_id,
382
+ trajectory_key=run.trajectory_key,
383
+ )
384
+
385
+
386
+ async def list_agent_runs(
387
+ session: AsyncSession,
388
+ agent_id: int,
389
+ *,
390
+ query: str | None,
391
+ limit: int,
392
+ offset: int = 0,
393
+ created_by: str | None = None,
394
+ since: datetime | None = None,
395
+ until: datetime | None = None,
396
+ source: RunSource = "all",
397
+ ) -> tuple[list[AgentRunItem], int]:
398
+ stmt = _items_query().where(models.AgentRun.agent_id == agent_id)
399
+ if since is not None:
400
+ stmt = stmt.where(models.AgentRun.started_at >= since)
401
+ if until is not None:
402
+ stmt = stmt.where(models.AgentRun.started_at < until)
403
+ if source == "eval":
404
+ stmt = stmt.where(models.ExperimentRun.id.is_not(None))
405
+ elif source == "app":
406
+ stmt = stmt.where(models.ExperimentRun.id.is_(None))
407
+ if created_by is not None:
408
+ stmt = stmt.where(models.User.username == created_by)
409
+ if query:
410
+ stmt = stmt.where(
411
+ models.AgentRun.title.ilike(f"%{query}%")
412
+ | models.Case.case_id.ilike(f"%{query}%")
413
+ | models.AgentRun.harness_run_id.startswith(query)
414
+ )
415
+ total = await session.scalar(select(func.count()).select_from(stmt.subquery()))
416
+ rows = await session.execute(
417
+ stmt.order_by(models.AgentRun.started_at.desc(), models.AgentRun.id.desc())
418
+ .limit(limit)
419
+ .offset(offset)
420
+ )
421
+ return [_item(row) for row in rows.all()], total or 0
422
+
423
+
424
+ def eval_run_ids(agent_id: int) -> Select:
425
+ return (
426
+ select(models.AgentRun.harness_run_id)
427
+ .join(models.ExperimentRun, models.ExperimentRun.agent_run_id == models.AgentRun.id)
428
+ .where(models.AgentRun.agent_id == agent_id)
429
+ )
430
+
431
+
432
+ async def runs_by_harness_ids(
433
+ session: AsyncSession, agent_id: int, harness_run_ids: list[str]
434
+ ) -> dict[str, AgentRunItem]:
435
+ if not harness_run_ids:
436
+ return {}
437
+ rows = await session.execute(
438
+ _items_query().where(
439
+ models.AgentRun.agent_id == agent_id,
440
+ models.AgentRun.harness_run_id.in_(harness_run_ids),
441
+ )
442
+ )
443
+ return {item.harness_run_id: item for item in map(_item, rows.all())}
444
+
445
+
446
+ async def trajectory_keys(
447
+ session: AsyncSession, agent_id: int, harness_run_ids: list[str]
448
+ ) -> dict[str, str]:
449
+ if not harness_run_ids:
450
+ return {}
451
+ rows = await session.execute(
452
+ select(models.AgentRun.harness_run_id, models.AgentRun.trajectory_key).where(
453
+ models.AgentRun.agent_id == agent_id,
454
+ models.AgentRun.harness_run_id.in_(harness_run_ids),
455
+ models.AgentRun.trajectory_key.is_not(None),
456
+ )
457
+ )
458
+ return {run_id: key for run_id, key in rows.all()}
459
+
460
+
461
+ async def agent_run(
462
+ session: AsyncSession, agent_id: int, harness_run_id: str
463
+ ) -> AgentRunRecord | None:
464
+ row = await session.scalar(
465
+ select(models.AgentRun).where(
466
+ models.AgentRun.agent_id == agent_id,
467
+ models.AgentRun.harness_run_id == harness_run_id,
468
+ )
469
+ )
470
+ return None if row is None else _record(row)
471
+
472
+
473
+ async def eval_context(
474
+ session: AsyncSession, agent_id: int, harness_run_id: str
475
+ ) -> RunEvalContext | None:
476
+ row = (
477
+ await session.execute(
478
+ select(models.ExperimentRun, models.ExperimentCase, models.Experiment)
479
+ .join(models.AgentRun, models.AgentRun.id == models.ExperimentRun.agent_run_id)
480
+ .join(models.Experiment, models.Experiment.id == models.ExperimentRun.experiment_id)
481
+ .join(
482
+ models.ExperimentCase,
483
+ (models.ExperimentCase.experiment_id == models.ExperimentRun.experiment_id)
484
+ & (models.ExperimentCase.case_ref == models.ExperimentRun.case_ref),
485
+ )
486
+ .where(
487
+ models.AgentRun.agent_id == agent_id,
488
+ models.AgentRun.harness_run_id == harness_run_id,
489
+ )
490
+ )
491
+ ).one_or_none()
492
+ if row is None:
493
+ return None
494
+ experiment_run, pinned, exp = row
495
+ dataset = await session.get(models.Dataset, exp.dataset_id)
496
+ assert dataset is not None
497
+ (view,) = await case_views(
498
+ session,
499
+ dataset.workspace_id,
500
+ [(pinned.case_ref, pinned.content_hash)],
501
+ agent=None,
502
+ dataset_id=dataset.id,
503
+ )
504
+ found = await session.scalars(
505
+ select(models.Evaluation)
506
+ .where(models.Evaluation.experiment_run_id == experiment_run.id)
507
+ .order_by(models.Evaluation.id)
508
+ )
509
+ return RunEvalContext(
510
+ experiment_id=exp.id,
511
+ repetition=experiment_run.repetition,
512
+ case=view.model_copy(update={"effective_evaluators": pinned.effective_evaluators}),
513
+ evaluations=[_evaluation(e) for e in found.all()],
514
+ )
515
+
516
+
517
+ async def experiment_run_ids(
518
+ session: AsyncSession, exp_id: int, *, limit: int, offset: int
519
+ ) -> tuple[int, list[str], int]:
520
+ exp = await _experiment(session, exp_id)
521
+ stmt = (
522
+ select(models.AgentRun.harness_run_id)
523
+ .join(models.ExperimentRun, models.ExperimentRun.agent_run_id == models.AgentRun.id)
524
+ .join(models.Case, models.Case.id == models.ExperimentRun.case_ref)
525
+ .where(models.ExperimentRun.experiment_id == exp_id)
526
+ )
527
+ total = await session.scalar(select(func.count()).select_from(stmt.subquery()))
528
+ ids = await session.scalars(
529
+ stmt.order_by(models.Case.case_id, models.ExperimentRun.repetition)
530
+ .limit(limit)
531
+ .offset(offset)
532
+ )
533
+ return exp.agent_id, list(ids.all()), total or 0