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
@@ -0,0 +1,31 @@
1
+ import asyncio
2
+ import ipaddress
3
+ import socket
4
+
5
+ import httpx
6
+
7
+
8
+ class BlockedEndpoint(ValueError):
9
+ pass
10
+
11
+
12
+ def blocked(addr: ipaddress.IPv4Address | ipaddress.IPv6Address) -> bool:
13
+ return not addr.is_global or addr.is_multicast or addr.is_reserved
14
+
15
+
16
+ async def check_endpoint(url: str, *, allow_private: bool) -> None:
17
+ if allow_private:
18
+ return
19
+ host = httpx.URL(url).host
20
+ if not host:
21
+ raise BlockedEndpoint(f"{url} has no host")
22
+ try:
23
+ infos = await asyncio.get_running_loop().getaddrinfo(host, None)
24
+ except socket.gaierror as exc:
25
+ raise BlockedEndpoint(f"{url}: cannot resolve {host}") from exc
26
+ bad = sorted({str(info[4][0]) for info in infos if blocked(ipaddress.ip_address(info[4][0]))})
27
+ if bad:
28
+ raise BlockedEndpoint(
29
+ f"{url} resolves to a private address ({', '.join(bad)}); "
30
+ "the operator can allow it with allow_private_endpoints: true"
31
+ )
@@ -0,0 +1,29 @@
1
+ from evalkit.core.evaluators import gauges as _gauges
2
+ from evalkit.core.evaluators import presentation as _presentation
3
+ from evalkit.core.evaluators import response_time as _response_time
4
+ from evalkit.core.evaluators import rubrics as _rubrics
5
+ from evalkit.core.evaluators import tool_trajectory as _tool_trajectory
6
+ from evalkit.core.evaluators import trajectory_rubrics as _trajectory_rubrics
7
+ from evalkit.core.evaluators.base import (
8
+ BUILTIN_EVALUATORS,
9
+ BUILTIN_GAUGES,
10
+ EvalContext,
11
+ execute,
12
+ gate,
13
+ neutral_skip,
14
+ )
15
+
16
+ __all__ = [
17
+ "BUILTIN_EVALUATORS",
18
+ "BUILTIN_GAUGES",
19
+ "EvalContext",
20
+ "execute",
21
+ "gate",
22
+ "neutral_skip",
23
+ "_gauges",
24
+ "_presentation",
25
+ "_response_time",
26
+ "_rubrics",
27
+ "_tool_trajectory",
28
+ "_trajectory_rubrics",
29
+ ]
@@ -0,0 +1,121 @@
1
+ from collections.abc import Awaitable, Callable
2
+ from dataclasses import dataclass, field
3
+ from typing import Any, Literal
4
+
5
+ from evalkit.contracts.harness import Capabilities
6
+ from evalkit.contracts.settings import JudgeSettings
7
+ from evalkit.core.judge import JudgeUsage
8
+ from evalkit.core.trajectory import RunMetrics, TrajectoryView
9
+ from evalkit.store.base import CaseView, ScoreRow
10
+
11
+ PASS_THRESHOLD = 0.8
12
+ WARN_THRESHOLD = 0.5
13
+
14
+
15
+ def label_from_score(score: float) -> str:
16
+ if score >= PASS_THRESHOLD:
17
+ return "pass"
18
+ if score >= WARN_THRESHOLD:
19
+ return "warn"
20
+ return "fail"
21
+
22
+
23
+ # score=None excludes skipped evaluators from aggregates — returning 1.0 would
24
+ # inflate the overall score.
25
+ def neutral_skip(name: str, reason: str = "capability not provided") -> ScoreRow:
26
+ return ScoreRow(name=name, score=None, label="skip", explanation=reason)
27
+
28
+
29
+ @dataclass
30
+ class EvalContext:
31
+ case: CaseView
32
+ answer: str
33
+ trajectory_view: TrajectoryView | None
34
+ wall_ms: int
35
+ judge_cfg: JudgeSettings
36
+ metrics: RunMetrics | None = None
37
+ session_id: str | None = None
38
+ cost_map: dict[str, Any] = field(default_factory=dict)
39
+ judge_usage: JudgeUsage = field(default_factory=JudgeUsage)
40
+ output: dict[str, Any] | None = None
41
+ judge_api_key: str | None = None
42
+
43
+ def record_judge_usage(self, usage: JudgeUsage) -> None:
44
+ self.judge_usage.input_tokens += usage.input_tokens
45
+ self.judge_usage.output_tokens += usage.output_tokens
46
+
47
+
48
+ EvalFn = Callable[[EvalContext, dict[str, Any]], Awaitable[list[ScoreRow]]]
49
+
50
+
51
+ @dataclass(frozen=True)
52
+ class BuiltinEval:
53
+ name: str
54
+ fn: EvalFn
55
+ requires: frozenset[str]
56
+ kind: Literal["evaluator", "gauge"]
57
+ emits: tuple[str, ...]
58
+ uses_judge: bool = False
59
+
60
+
61
+ BUILTIN_EVALUATORS: dict[str, BuiltinEval] = {}
62
+ BUILTIN_GAUGES: dict[str, BuiltinEval] = {}
63
+
64
+
65
+ def builtin_evaluator(
66
+ name: str,
67
+ requires: set[str] | None = None,
68
+ emits: tuple[str, ...] | None = None,
69
+ uses_judge: bool = False,
70
+ ):
71
+ def decorate(fn: EvalFn) -> EvalFn:
72
+ BUILTIN_EVALUATORS[name] = BuiltinEval(
73
+ name=name,
74
+ fn=fn,
75
+ requires=frozenset(requires or set()),
76
+ kind="evaluator",
77
+ emits=emits or (name,),
78
+ uses_judge=uses_judge,
79
+ )
80
+ return fn
81
+
82
+ return decorate
83
+
84
+
85
+ def builtin_gauge(name: str, requires: set[str] | None = None):
86
+ def decorate(fn: EvalFn) -> EvalFn:
87
+ BUILTIN_GAUGES[name] = BuiltinEval(
88
+ name=name, fn=fn, requires=frozenset(requires or set()), kind="gauge", emits=(name,)
89
+ )
90
+ return fn
91
+
92
+ return decorate
93
+
94
+
95
+ def gate(evaluator: BuiltinEval, capabilities: Capabilities) -> bool:
96
+ return all(getattr(capabilities.provides, flag, False) for flag in evaluator.requires)
97
+
98
+
99
+ async def execute(
100
+ evaluator: BuiltinEval,
101
+ ctx: EvalContext,
102
+ config: dict[str, Any],
103
+ capabilities: Capabilities,
104
+ ) -> list[ScoreRow]:
105
+ if not gate(evaluator, capabilities):
106
+ missing = sorted(
107
+ flag for flag in evaluator.requires if not getattr(capabilities.provides, flag, False)
108
+ )
109
+ row = neutral_skip(evaluator.name, f"harness does not provide: {', '.join(missing)}")
110
+ if evaluator.kind == "gauge":
111
+ row.kind = "gauge"
112
+ row.label = "info"
113
+ return [row]
114
+ rows = await evaluator.fn(ctx, config)
115
+ for row in rows:
116
+ if evaluator.kind == "gauge":
117
+ row.kind = "gauge"
118
+ row.label = row.label or "info"
119
+ elif row.label is None and row.score is not None:
120
+ row.label = label_from_score(row.score)
121
+ return rows
@@ -0,0 +1,84 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+ from evalkit.core.costmap import cost_usd, resolve_rates
6
+ from evalkit.core.evaluators.base import EvalContext, builtin_gauge
7
+ from evalkit.store.base import ScoreRow
8
+
9
+
10
+ def _reading(name: str, value: float | None, explanation: str | None = None) -> list[ScoreRow]:
11
+ if value is None:
12
+ return [
13
+ ScoreRow(name=name, kind="gauge", score=None, label="info", explanation="no reading")
14
+ ]
15
+ return [ScoreRow(name=name, kind="gauge", score=value, label="info", explanation=explanation)]
16
+
17
+
18
+ @builtin_gauge("gauge_duration_seconds")
19
+ async def gauge_duration_seconds(ctx: EvalContext, config: dict[str, Any]) -> list[ScoreRow]:
20
+ return _reading("gauge_duration_seconds", round(ctx.wall_ms / 1000, 1))
21
+
22
+
23
+ @builtin_gauge("gauge_input_tokens", requires={"token_usage"})
24
+ async def gauge_input_tokens(ctx: EvalContext, config: dict[str, Any]) -> list[ScoreRow]:
25
+ value = ctx.metrics.input_tokens if ctx.metrics else None
26
+ return _reading("gauge_input_tokens", value)
27
+
28
+
29
+ @builtin_gauge("gauge_output_tokens", requires={"token_usage"})
30
+ async def gauge_output_tokens(ctx: EvalContext, config: dict[str, Any]) -> list[ScoreRow]:
31
+ value = ctx.metrics.output_tokens if ctx.metrics else None
32
+ return _reading("gauge_output_tokens", value)
33
+
34
+
35
+ @builtin_gauge("gauge_cached_tokens", requires={"token_usage"})
36
+ async def gauge_cached_tokens(ctx: EvalContext, config: dict[str, Any]) -> list[ScoreRow]:
37
+ value = ctx.metrics.cached_tokens if ctx.metrics else None
38
+ return _reading("gauge_cached_tokens", value)
39
+
40
+
41
+ @builtin_gauge("gauge_agent_cost_usd", requires={"token_usage"})
42
+ async def gauge_agent_cost_usd(ctx: EvalContext, config: dict[str, Any]) -> list[ScoreRow]:
43
+ value = ctx.metrics.cost_usd if ctx.metrics else None
44
+ return _reading("gauge_agent_cost_usd", round(value, 4) if value is not None else None)
45
+
46
+
47
+ @builtin_gauge("gauge_judge_cost_usd")
48
+ async def gauge_judge_cost_usd(ctx: EvalContext, config: dict[str, Any]) -> list[ScoreRow]:
49
+ usage = ctx.judge_usage
50
+ if usage.input_tokens == 0 and usage.output_tokens == 0:
51
+ return _reading("gauge_judge_cost_usd", None)
52
+ rates = resolve_rates(ctx.cost_map, ctx.judge_cfg.model)
53
+ if rates is None:
54
+ return _reading("gauge_judge_cost_usd", None)
55
+ total = cost_usd(rates, usage.input_tokens, usage.output_tokens, 0)
56
+ return _reading(
57
+ "gauge_judge_cost_usd",
58
+ round(total, 4),
59
+ f"{usage.input_tokens} in / {usage.output_tokens} out judge tokens",
60
+ )
61
+
62
+
63
+ @builtin_gauge("gauge_total_tool_calls", requires={"trajectory", "tool_calls"})
64
+ async def gauge_total_tool_calls(ctx: EvalContext, config: dict[str, Any]) -> list[ScoreRow]:
65
+ value = len(ctx.trajectory_view.tool_calls()) if ctx.trajectory_view else None
66
+ return _reading("gauge_total_tool_calls", value)
67
+
68
+
69
+ @builtin_gauge("gauge_errored_tool_calls", requires={"trajectory", "tool_calls"})
70
+ async def gauge_errored_tool_calls(ctx: EvalContext, config: dict[str, Any]) -> list[ScoreRow]:
71
+ value = ctx.trajectory_view.errored_tool_call_count() if ctx.trajectory_view else None
72
+ return _reading("gauge_errored_tool_calls", value)
73
+
74
+
75
+ @builtin_gauge("gauge_subagents", requires={"trajectory"})
76
+ async def gauge_subagents(ctx: EvalContext, config: dict[str, Any]) -> list[ScoreRow]:
77
+ value = ctx.trajectory_view.subagent_count() if ctx.trajectory_view else None
78
+ return _reading("gauge_subagents", value)
79
+
80
+
81
+ @builtin_gauge("gauge_turns", requires={"trajectory"})
82
+ async def gauge_turns(ctx: EvalContext, config: dict[str, Any]) -> list[ScoreRow]:
83
+ value = ctx.trajectory_view.turn_count() if ctx.trajectory_view else None
84
+ return _reading("gauge_turns", value)
@@ -0,0 +1,166 @@
1
+ """presentation_judge — rubric-scored judge over how the reply presents itself.
2
+
3
+ Grades form, not content: whether a unit label is shown, whether a table carries
4
+ the columns asked for, whether the reply relays an artifact instead of restating
5
+ it. Correctness of the figures belongs to answer_rubrics.
6
+
7
+ Score is points-weighted over binary per-rubric verdicts:
8
+
9
+ score = sum(points of met rubrics) / sum(points of all rubrics)
10
+
11
+ Never fatal. There is no `clean` factor and no fabrication guard, because
12
+ presentation cannot fabricate — a missing unit label invents nothing. A `forbid`
13
+ rubric that is violated costs its points, so an assertion whose violation is a
14
+ product bug rather than a quality shortfall carries that weight through `points`.
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ import json
20
+ import logging
21
+ from typing import Any
22
+
23
+ from pydantic import BaseModel, Field
24
+
25
+ from evalkit.contracts.cases import PresentationJudge, PresentationRubric
26
+ from evalkit.core.evaluators.base import EvalContext, builtin_evaluator
27
+ from evalkit.core.evaluators.rubrics import missing_verdict_indices
28
+ from evalkit.core.judge import call_judge
29
+ from evalkit.store.base import ScoreRow
30
+
31
+ LOGGER = logging.getLogger(__name__)
32
+
33
+
34
+ class IncompleteRubricVerdicts(Exception):
35
+ """The judge did not verdict every rubric exactly once."""
36
+
37
+
38
+ class PresentationVerdict(BaseModel):
39
+ index: int = Field(
40
+ description="The number this item was listed under in the prompt, counting from 1."
41
+ )
42
+ met: bool = Field(
43
+ description="True when the answer has the required property. For an item"
44
+ " marked MUST NOT, true means the answer avoided it."
45
+ )
46
+ evidence: str = Field(
47
+ description="The span of the answer that decided this verdict, quoted. When"
48
+ " the property is absent, say what was looked for and not found."
49
+ )
50
+
51
+
52
+ class PresentationJudgeOutput(BaseModel):
53
+ verdicts: list[PresentationVerdict]
54
+
55
+
56
+ SYSTEM_PROMPT = """\
57
+ You grade HOW an answer presents its information, not whether the information is \
58
+ correct. A figure may be wrong and still presented well; a figure may be right and \
59
+ presented badly. Judge only presentation.
60
+
61
+ You see the answer text and nothing else — not the tool results, not any generated \
62
+ file. If a property cannot be decided from the text, it is not met.
63
+
64
+ For each numbered item, return met/unmet and quote the span of the answer that \
65
+ decided it. For an item marked MUST NOT, `met` means the answer avoided it.
66
+
67
+ Return exactly one verdict per item, for every item."""
68
+
69
+
70
+ def _rubric_text(rubric: PresentationRubric) -> str:
71
+ return f"MUST NOT: {rubric.forbid}" if rubric.forbid is not None else str(rubric.rubric)
72
+
73
+
74
+ def _render_rubric(index: int, rubric: PresentationRubric) -> str:
75
+ line = f"{index}. {_rubric_text(rubric)}"
76
+ if rubric.note:
77
+ line += f"\n note: {rubric.note}"
78
+ return line
79
+
80
+
81
+ def build_user_prompt(answer: str, rubrics: list[PresentationRubric]) -> str:
82
+ items = "\n".join(_render_rubric(i, rubric) for i, rubric in enumerate(rubrics, start=1))
83
+ return f"ANSWER\n{answer}\n\nPRESENTATION ITEMS\n{items}"
84
+
85
+
86
+ def score_rubrics(rubrics: list[PresentationRubric], verdicts: list[PresentationVerdict]) -> float:
87
+ """Points-weighted fraction of rubrics met. `rubrics` is non-empty by schema,
88
+ so the denominator is always positive and the score is always a real number."""
89
+ by_index = {v.index: v for v in verdicts}
90
+ total = sum(r.points for r in rubrics)
91
+ earned = sum(r.points for i, r in enumerate(rubrics, start=1) if by_index[i].met)
92
+ return earned / total
93
+
94
+
95
+ async def _judge_verdicts(
96
+ ctx: EvalContext, rubrics: list[PresentationRubric], base_prompt: str
97
+ ) -> PresentationJudgeOutput:
98
+ """One judge call, retried once when the verdict list has a gap.
99
+
100
+ A gap is a judge lapse, not the agent's failure, so it is retried rather than
101
+ scored — defaulting the missing rubrics to unmet would blame the agent.
102
+ """
103
+ current = base_prompt
104
+ missing: list[int] = []
105
+ for attempt in (1, 2):
106
+ parsed, usage = await call_judge(
107
+ ctx.judge_cfg,
108
+ api_key=ctx.judge_api_key,
109
+ system_prompt=SYSTEM_PROMPT,
110
+ user_prompt=current,
111
+ output_type=PresentationJudgeOutput,
112
+ session_id=ctx.session_id,
113
+ evaluator_name="presentation_judge",
114
+ )
115
+ ctx.record_judge_usage(usage)
116
+ missing = missing_verdict_indices(len(rubrics), [v.index for v in parsed.verdicts])
117
+ if not missing:
118
+ return parsed
119
+ LOGGER.warning(
120
+ "evals.presentation.incomplete_verdicts attempt=%s missing=%s", attempt, missing
121
+ )
122
+ current = (
123
+ f"{base_prompt}\n\nYour previous response did not return exactly one "
124
+ f"verdict for items {missing}. Return one verdict per item, for every "
125
+ f"item from 1 to {len(rubrics)}."
126
+ )
127
+ raise IncompleteRubricVerdicts(f"judge left items {missing} unverdicted after a retry")
128
+
129
+
130
+ def _explanation(rubrics: list[PresentationRubric], verdicts: list[PresentationVerdict]) -> str:
131
+ """Aggregates lead and `items` rides last, so a truncated explanation
132
+ preview shows the counts. Items carry the rubric text — readers render
133
+ explanations with no case config to join against — and the judge's quoted
134
+ evidence as `note`."""
135
+ by_index = {v.index: v for v in verdicts}
136
+ items: list[dict[str, Any]] = []
137
+ met_count = 0
138
+ for i, r in enumerate(rubrics, start=1):
139
+ v = by_index[i]
140
+ met_count += v.met
141
+ item: dict[str, Any] = {
142
+ "index": i,
143
+ "text": _rubric_text(r),
144
+ "verdict": "satisfied" if v.met else "missing",
145
+ "points_earned": r.points if v.met else 0,
146
+ "points_possible": r.points,
147
+ }
148
+ if v.evidence:
149
+ item["note"] = v.evidence
150
+ items.append(item)
151
+ return json.dumps({"met": met_count, "total": len(rubrics), "items": items}, ensure_ascii=False)
152
+
153
+
154
+ @builtin_evaluator("presentation_judge", uses_judge=True)
155
+ async def presentation_judge(ctx: EvalContext, config: dict[str, Any]) -> list[ScoreRow]:
156
+ cfg = PresentationJudge.model_validate(config)
157
+ rubrics = cfg.rubrics
158
+ parsed = await _judge_verdicts(ctx, rubrics, build_user_prompt(ctx.answer, rubrics))
159
+ return [
160
+ ScoreRow(
161
+ name="presentation_judge",
162
+ score=score_rubrics(rubrics, parsed.verdicts),
163
+ explanation=_explanation(rubrics, parsed.verdicts),
164
+ annotator_kind="LLM",
165
+ )
166
+ ]
@@ -0,0 +1,21 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+ from evalkit.contracts.cases import MaxResponseTime
6
+ from evalkit.core.evaluators.base import EvalContext, builtin_evaluator
7
+ from evalkit.store.base import ScoreRow
8
+
9
+
10
+ @builtin_evaluator("max_response_time")
11
+ async def max_response_time(ctx: EvalContext, config: dict[str, Any]) -> list[ScoreRow]:
12
+ cfg = MaxResponseTime.model_validate(config)
13
+ seconds = ctx.wall_ms / 1000
14
+ passed = seconds <= cfg.max_seconds
15
+ return [
16
+ ScoreRow(
17
+ name="max_response_time",
18
+ score=1.0 if passed else 0.0,
19
+ explanation=f"run took {seconds:.1f}s against a cap of {cfg.max_seconds:g}s",
20
+ )
21
+ ]