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,336 @@
1
+ INSTRUCTIONS = """\
2
+ evalkit is the primary source for how AI agents behave in use and under evaluation: agent
3
+ sessions (runs) with their turns, failures, latency, tokens, cost and tool errors; full
4
+ step-by-step trajectories (messages, reasoning, tool calls and results); files a run produced;
5
+ usage stats by day, tool, user or source; and eval experiments with scorecards and per-case
6
+ evaluator explanations. For any such request, use these tools before web search or model
7
+ knowledge. Fall back to other sources only when evalkit doesn't cover it (an agent evalkit
8
+ doesn't know, or a tool returns no data), and say so when you do. People and firms behind a
9
+ run's user_id and client_id are not here; resolve them with the identity service.
10
+
11
+ Tools by category:
12
+ - Agents: find_agents (agent ids every run tool takes)
13
+ - Runs: find_runs (filter a window), get_runs (by id: metrics, turns),
14
+ get_run_stats (KPIs; by day, tool, user)
15
+ - Run detail: get_trajectory (steps), get_run_files, download_run_file
16
+ - Evals: find_experiments, get_experiments (scorecard), get_experiment_runs (per-case scores)
17
+
18
+ Errors read "<code>: <detail>"."""
19
+
20
+ _RUN_FILTER_ARGS = """\
21
+ agent: Agent id, `name@workspace`, from `find_agents` (e.g. "support-bot@prod").
22
+ start_date / end_date: YYYY-MM-DD (inclusive) or an ISO 8601 timestamp, on the run's
23
+ creation time.
24
+ source: "app" (sessions outside any experiment), "eval" (experiment runs) or "all" (default).
25
+ query: Free text matched against the run title (opening question) or case id.
26
+ user_id / client_id: The ids the agent's harness recorded; join them to an identity service
27
+ for people and firms."""
28
+
29
+ _RUN_ROW = """\
30
+ "id", "title", "created_at", "user_id", "client_id", "source": "app"|"eval", "status",
31
+ "experiment_id", "experiment_number", "case_id",
32
+ "metrics"?: {"started_at", "ended_at", "duration_seconds" (sum of turns), "wall_seconds",
33
+ "turns", "failed_turns", "failures" (steps marked `extra.is_error`: one per failed
34
+ message; a message that failed before reaching the trajectory has no turn of its own, so
35
+ it can exceed failed_turns), "agent_steps" (model calls),
36
+ "cost": {"value", "unit": "currency", "currency": "USD"} | null,
37
+ "cost_source": "reported"|"estimated"|"mixed"|null, "input_tokens", "output_tokens",
38
+ "cached_tokens", "tool_calls", "tool_errors",
39
+ "tool_errors_by_tool": [{"tool", "count", "message"}],
40
+ "cost_by_model": [{"model", "cost", "input_tokens", "output_tokens", "cached_tokens",
41
+ "steps"}] (priced steps by model, subagents included, most expensive first; sums to
42
+ "cost" unless the harness reported only a total), "models", "subagents",
43
+ "opening_question", "final_answer"},
44
+ "turns"?: [{"index", "started_at", "ended_at", "duration_seconds",
45
+ "status": "ok"|"failed"|"no_answer", "error", "question", "answer", "cost",
46
+ "agent_steps", "tool_calls", "tool_errors"}],
47
+ "tool_calls"?: [{"tool", "count", "arguments"}] (every call, subagents' included, grouped
48
+ by tool and arguments, most frequent first, at most 100 groups; an argument longer than
49
+ 120 characters is shortened: a string keeps its start and end, other values their start),
50
+ "note"?: {"code", "message"?},
51
+ "error"?, "detail"? (a run whose trajectory could not be read: "harness_timeout" (not read
52
+ within the call's time budget; ask again with get_runs), "harness_failed" or
53
+ "session_too_large"; such a row has no metrics)"""
54
+
55
+ FIND_AGENTS = """\
56
+ Agents evalkit tracks ('which agents are there', 'the support chatbot'), with the id the \
57
+ run tools take.
58
+
59
+ Use when you need an agent's id (`name@workspace`) before calling find_runs, get_runs,
60
+ get_run_stats or get_trajectory.
61
+
62
+ Args:
63
+ workspace: Only agents in this workspace (e.g. "prod").
64
+ include_archived: Also list archived agents. Default false.
65
+
66
+ Returns:
67
+ {"data": [{"id": "name@workspace", "name", "workspace", "workspace_type": "shared"|"personal",
68
+ "trajectory_source": "atif"|"otel", "driver", "archived"}], "meta": {"count"}}
69
+ An empty `data` means no agent matched the filter."""
70
+
71
+ FIND_RUNS = f"""\
72
+ Agent sessions ('chat sessions last week', 'sessions by user u-123', 'failed eval runs'), newest \
73
+ first, in a time window.
74
+
75
+ Sibling tools — pick the right one:
76
+ - For metrics on runs you already have ids for -> get_runs
77
+ - For totals, daily series or per-tool error counts over a window -> get_run_stats
78
+ (then the sessions behind one tool's errors -> find_runs with tool_error)
79
+ - For one run's step-by-step record -> get_trajectory
80
+
81
+ Filters and lists runs; it does not rank. Without `include` a row is the run listing only; with
82
+ `include=["metrics"]` each row also carries the run's metrics (reads every trajectory: slower).
83
+
84
+ Args:
85
+ {_RUN_FILTER_ARGS}
86
+ timezone: IANA name for date-only bounds. Default "UTC". Pass get_run_stats' value to read
87
+ the same window.
88
+ tool_error: Only runs where this tool errored, as named in get_run_stats' "tool" rows
89
+ (e.g. "search_docs"). Implies include=["metrics"]: each row's
90
+ `metrics.tool_errors_by_tool` has the count and a representative message. Every call reads
91
+ the window's trajectories again, up to 5000 runs, within the call's time budget, so pages
92
+ can differ: `meta.total` counts the matching runs plus the runs not read, and each run not
93
+ read is a row with `error` and `detail`. Take one page and pass those rows' ids to get_runs
94
+ rather than paging on.
95
+ failed_only: Only runs with a failure (a step marked `extra.is_error`: a turn that timed
96
+ out, a provider error, a crash). Implies include=["metrics"]; add "turns" for each turn's
97
+ error. Same window read as tool_error; combine the two freely.
98
+ include: Any of "metrics", "turns", "tool_calls". Default none. "tool_calls" is what the
99
+ agent called and with which arguments — enough to classify a run without its trajectory.
100
+ limit: 1-5000 for the listing alone, 1-200 with include, tool_error or failed_only. Default 50.
101
+ To read a large window, list it in one call, then get_runs for the ids in batches.
102
+ offset: Pass `meta.next_offset` from the prior page while `meta.has_more` is true.
103
+
104
+ Returns:
105
+ {{"data": [{{
106
+ {_RUN_ROW}
107
+ }}], "meta": {{"count", "total", "has_more", "next_offset"?, "note"?}}}}
108
+ `meta.note` (`partial_coverage`) says the agent's harness was unreachable (rows are evalkit's
109
+ stored runs only), the window held more runs than were read, or some trajectories could not be
110
+ read within the call's time budget. A row that could not be read carries `error` and
111
+ `detail`: ask for it again with get_runs. Empty `data` means no run in the window.
112
+
113
+ Errors:
114
+ - `not_found` — unknown agent.
115
+ - `invalid_agent_id` — not a `name@workspace` id; take it from find_agents.
116
+ - `invalid_date_format` / `invalid_range` — bad or inverted dates.
117
+ - `invalid_source` / `invalid_include` / `invalid_limit` / `invalid_offset`.
118
+ - `harness_timeout` — listing the agent's runs took the whole time budget; narrow the window."""
119
+
120
+ GET_RUNS = f"""\
121
+ Run metrics by id ('cost of run X', 'why did this session fail'): turns, failures, duration, \
122
+ tokens, tool errors.
123
+
124
+ Sibling tools — pick the right one:
125
+ - To find run ids in a window or by user -> find_runs
126
+ - For the raw steps (reasoning, tool arguments and results) -> get_trajectory
127
+
128
+ Use after find_runs, get_experiment_runs or a run id the user gave. One row per requested id,
129
+ in input order. A turn is one user message and everything the agent did until the next; a turn
130
+ the harness marked failed (`Step.extra.is_error`) has status "failed".
131
+
132
+ Args:
133
+ agent: Agent id, `name@workspace`, from `find_agents`.
134
+ run_ids: 1-200 run ids.
135
+ include: Any of "metrics", "turns", "tool_calls". Default ["metrics"].
136
+
137
+ Returns:
138
+ {{"data": [{{
139
+ {_RUN_ROW}
140
+ }}], "meta": {{"count", "matched", "unmatched", "failed"?}}}}
141
+ An unknown id is a row with `note: {{"code": "not_found"}}` and counts in `meta.unmatched`;
142
+ a run whose trajectory could not be read carries `error` and `detail` and counts in
143
+ `meta.failed` (left out when nothing failed). `cost_source` "estimated" means evalkit
144
+ priced the tokens; "reported" means the agent's harness did.
145
+
146
+ Errors:
147
+ - `not_found` — unknown agent.
148
+ - `invalid_agent_id` — not a `name@workspace` id; take it from find_agents.
149
+ - `no_ids_provided` / `too_many_ids` / `invalid_include`.
150
+ - `invalid_run_id` — a run id that is not one plain path segment."""
151
+
152
+ GET_RUN_STATS = f"""\
153
+ Agent usage KPIs ('failures per day', 'which tools error most', 'spend by user'): \
154
+ sessions, failures, latency, cost, tool errors — total or by day, tool, user or source.
155
+
156
+ Sibling tools — pick the right one:
157
+ - For the individual runs behind a number -> find_runs with the same filters and
158
+ include=["metrics"]
159
+ - For one run in depth -> get_runs
160
+
161
+ Reads every run's trajectory in the window, newest first, up to `max_runs`, within a time
162
+ budget; narrow the window rather than raising it. Runs not read count in `meta.unavailable`,
163
+ with a `partial_coverage` note in `meta.note`.
164
+
165
+ Args:
166
+ {_RUN_FILTER_ARGS}
167
+ Date-only bounds are read in `timezone`.
168
+ group_by: "none" (one totals row, default), "day", "tool", "user" or "source", or a list
169
+ of them: one call reads the window once and returns every grouping's rows, each row's
170
+ `group` naming its grouping (`{{}}` for "none"). Ask for all the groupings you need at once.
171
+ "tool" has one row per tool that errored at least once, counting only the runs where it
172
+ errored; tools that never errored have no row.
173
+ timezone: IANA name for day buckets and date-only bounds. Default "UTC".
174
+ max_runs: 1-5000. Default 500.
175
+
176
+ Returns:
177
+ {{"data": [{{"group": {{}} | {{"day"}} | {{"tool"}} | {{"user"}} | {{"source"}},
178
+ "runs", "runs_with_failure" (runs with any failure), "turns", "failed_turns", "failures",
179
+ "cost": {{"value", "unit": "currency", "currency": "USD"}},
180
+ "runs_with_estimated_cost", "runs_without_cost", "turn_p50_seconds", "turn_p90_seconds",
181
+ "tool_errors", "runs_with_tool_errors", "sample_error"? (tool rows)}}],
182
+ "meta": {{"count", "group_by": [...], "timezone", "runs_considered", "runs_in_window", "capped",
183
+ "unavailable", "note"?}}}}
184
+ `meta.capped` true means the window held more runs than `max_runs`. Empty `data` means no run
185
+ in the window.
186
+
187
+ Errors:
188
+ - `not_found` — unknown agent id.
189
+ - `invalid_agent_id` — not a `name@workspace` id; take it from find_agents.
190
+ - `invalid_group_by` / `invalid_source` / `invalid_timezone` / `invalid_date_format` /
191
+ `invalid_range` / `invalid_limit` (max_runs).
192
+ - `harness_timeout` — listing the agent's runs took the whole time budget; narrow the window."""
193
+
194
+ GET_TRAJECTORY = """\
195
+ What an agent did in a run ('show run X's tool calls', 'which call failed'): messages, \
196
+ reasoning, tool calls and results, per-step tokens and cost.
197
+
198
+ Use after find_runs or get_runs. Read get_runs' turns first; page here for the detail. Long text
199
+ is clipped to `max_chars` per field.
200
+
201
+ Args:
202
+ agent: Agent id, `name@workspace`.
203
+ run_id: One run id.
204
+ subagent: Page this subagent's trajectory instead (index from `meta.subagents`).
205
+ max_chars: 100-50000 per text field. Default 2000.
206
+ limit: Steps per page, 1-100. Default 20.
207
+ offset: Pass `meta.next_offset` while `meta.has_more` is true.
208
+
209
+ Returns:
210
+ {"data": [{"step_id", "timestamp", "source": "user"|"agent"|"system", "message",
211
+ "reasoning_content", "tool_calls": [{"tool_call_id", "function_name", "arguments"}],
212
+ "observation": {"results": [{"source_call_id", "content", "extra": {"is_error"?, ...}}]},
213
+ "metrics": {"prompt_tokens", "completion_tokens", "cached_tokens", "cost_usd"},
214
+ "model_name", "extra"}],
215
+ "meta": {"count", "total", "has_more", "next_offset"?, "schema_version", "session_id",
216
+ "trajectory_id", "agent", "final_metrics", "subagents": [{"index", "trajectory_id", "agent",
217
+ "steps"}]}}
218
+ Steps are ATIF v1.8.
219
+
220
+ Errors:
221
+ - `not_found` — unknown agent, run or subagent index.
222
+ - `invalid_limit` / `invalid_offset` / `invalid_max_chars`.
223
+ - `invalid_run_id` — a run id that is not one plain path segment."""
224
+
225
+ GET_RUN_FILES = """\
226
+ Files one run produced: reports, spreadsheets, decks and full tool outputs it saved.
227
+
228
+ Use after find_runs or get_runs; read one with download_run_file.
229
+
230
+ Args:
231
+ agent: Agent id, `name@workspace`.
232
+ run_id: One run id.
233
+
234
+ Returns:
235
+ {"data": [{"path", "size", "content_type"}], "meta": {"count"}}
236
+ Empty `data` means the run saved no files.
237
+
238
+ Errors:
239
+ - `not_found` — unknown agent or run, or its files are gone.
240
+ - `invalid_agent_id` / `invalid_run_id` — an id not of the shape find_agents / find_runs give."""
241
+
242
+ DOWNLOAD_RUN_FILE = """\
243
+ Raw text of one file a run produced, a window of characters at a time.
244
+
245
+ Use after get_run_files.
246
+
247
+ Args:
248
+ agent: Agent id, `name@workspace`.
249
+ run_id: One run id.
250
+ path: A path from get_run_files.
251
+ max_chars: Characters per call, 1-200000. Default 20000.
252
+ offset: Character offset. Pass `meta.next_offset` while `meta.has_more` is true.
253
+
254
+ Returns:
255
+ {"data": [{"path", "size" (bytes), "text"}], "meta": {"count", "total" (characters),
256
+ "has_more", "next_offset"?}}
257
+ A binary file returns its row without `text` and with
258
+ `note: {"code": "not_applicable", "message"}`.
259
+
260
+ Errors:
261
+ - `not_found` — unknown agent, run or path.
262
+ - `invalid_max_chars` / `invalid_offset`.
263
+ - `invalid_agent_id` / `invalid_run_id` — an id not of the shape find_agents / find_runs give."""
264
+
265
+ FIND_EXPERIMENTS = """\
266
+ Eval experiments ('the latest eval', 'failed experiments'), newest first, with progress and \
267
+ mean score per evaluator.
268
+
269
+ Sibling tools — pick the right one:
270
+ - For one experiment's scorecard -> get_experiments with include=["scorecard"]
271
+ - For per-case results and judge explanations -> get_experiment_runs
272
+
273
+ Args:
274
+ workspace: Only experiments in this workspace (e.g. "prod").
275
+ status: "running", "evaluating", "completed" or "failed".
276
+ limit: 1-100. Default 20.
277
+ offset: Pass `meta.next_offset` while `meta.has_more` is true.
278
+
279
+ Returns:
280
+ {"data": [{"id", "number", "workspace", "dataset", "agent", "created_by", "status",
281
+ "status_error", "repetitions", "started_at", "ended_at", "progress", "scores", ...}],
282
+ "meta": {"count", "total": null, "has_more", "next_offset"?}}
283
+
284
+ Errors:
285
+ - `not_found` — unknown workspace.
286
+ - `invalid_status` / `invalid_limit` / `invalid_offset`."""
287
+
288
+ GET_EXPERIMENTS = """\
289
+ Experiments by id ('scorecard of experiment 12', 'how did eval 40 score'): progress and, on \
290
+ request, the scorecard: buckets, evaluator means, gauges.
291
+
292
+ Sibling tools — pick the right one:
293
+ - For aggregate scores of an experiment -> this tool with include=["scorecard"]
294
+ - For why one case failed (its answer, scores and judge explanations) -> get_experiment_runs
295
+
296
+ Use after find_experiments or an id the user gave. One row per requested id, in input order.
297
+
298
+ Args:
299
+ experiment_ids: 1-20 experiment ids.
300
+ include: ["scorecard"] to add each experiment's scorecard. Default none.
301
+
302
+ Returns:
303
+ {"data": [{"id", "number", "workspace", "dataset", "agent", "status", "started_at",
304
+ "ended_at", "progress", "scorecard"?, ...}],
305
+ "meta": {"count", "matched", "unmatched"}}
306
+ An unknown id is a row with `note: {"code": "not_found"}`.
307
+
308
+ Errors:
309
+ - `no_ids_provided` / `too_many_ids` / `invalid_include`."""
310
+
311
+ GET_EXPERIMENT_RUNS = """\
312
+ Per-case eval results ('why did case X fail', 'which cases regressed'): answer, each \
313
+ evaluator's score, label and explanation.
314
+
315
+ Sibling tools — pick the right one:
316
+ - For why one case failed -> this tool
317
+ - For an experiment's aggregate scores -> get_experiments with include=["scorecard"]
318
+
319
+ Use after find_experiments. A row's `id` with `meta.agent` opens the run in get_runs or
320
+ get_trajectory.
321
+
322
+ Args:
323
+ experiment_id: One experiment id.
324
+ failed_only: Only runs with an error or a score below 1. Default false.
325
+ limit: 1-200. Default 50.
326
+ offset: Pass `meta.next_offset` while `meta.has_more` is true.
327
+
328
+ Returns:
329
+ {"data": [{"id", "case_id", "repetition", "status", "error", "answer", "started_at",
330
+ "ended_at", "metrics", "evaluations": [{"name", "kind", "value", "label", "explanation",
331
+ "error"}]}],
332
+ "meta": {"count", "total", "has_more", "next_offset"?, "agent"}}
333
+
334
+ Errors:
335
+ - `not_found` — unknown experiment.
336
+ - `invalid_limit` / `invalid_offset`."""
@@ -0,0 +1,259 @@
1
+ from datetime import datetime
2
+ from typing import Any, Literal
3
+
4
+ from pydantic import BaseModel, ConfigDict, Field, SerializerFunctionWrapHandler, model_serializer
5
+
6
+ from evalkit.core.run_digest import (
7
+ GroupStats,
8
+ ModelCost,
9
+ RunDigest,
10
+ ToolCallGroup,
11
+ ToolErrors,
12
+ Turn,
13
+ )
14
+
15
+ NoteCode = Literal["not_found", "partial_coverage", "not_applicable"]
16
+
17
+
18
+ # An optional field with no value is left out rather than
19
+ # sent as null. A required field that can be null (`total`, a run's `cost`) stays.
20
+ class Out(BaseModel):
21
+ @model_serializer(mode="wrap")
22
+ def _drop_unset_optionals(self, handler: SerializerFunctionWrapHandler):
23
+ out = handler(self)
24
+ fields = type(self).model_fields
25
+ return {
26
+ k: v
27
+ for k, v in out.items()
28
+ if v is not None or (k in fields and fields[k].is_required())
29
+ }
30
+
31
+
32
+ class Note(Out):
33
+ code: NoteCode
34
+ message: str | None = None
35
+
36
+
37
+ class Money(Out):
38
+ value: float
39
+ unit: Literal["currency"] = "currency"
40
+ currency: Literal["USD"] = "USD"
41
+
42
+
43
+ def usd(value: float | None) -> Money | None:
44
+ return Money(value=value) if value is not None else None
45
+
46
+
47
+ class PlainMeta(Out):
48
+ count: int
49
+
50
+
51
+ class PagedMeta(Out):
52
+ count: int
53
+ total: int | None
54
+ has_more: bool
55
+ next_offset: int | None = None
56
+ note: Note | None = None
57
+
58
+
59
+ class BatchMeta(Out):
60
+ count: int
61
+ matched: int
62
+ unmatched: int
63
+ failed: int | None = None
64
+
65
+
66
+ class AgentRow(Out):
67
+ id: str
68
+ name: str
69
+ workspace: str
70
+ workspace_type: Literal["shared", "personal"]
71
+ trajectory_source: str
72
+ driver: str
73
+ archived: bool
74
+
75
+
76
+ class FindAgentsResponse(Out):
77
+ data: list[AgentRow]
78
+ meta: PlainMeta
79
+
80
+
81
+ class ToolErrorCount(Out, ToolErrors):
82
+ pass
83
+
84
+
85
+ # The MCP rows are core's digest models with cost as a currency amount.
86
+ class ModelCostRow(Out, ModelCost):
87
+ cost_usd: float = Field(default=0.0, exclude=True)
88
+ cost: Money
89
+
90
+
91
+ class ToolCallRow(Out, ToolCallGroup):
92
+ pass
93
+
94
+
95
+ class RunMetrics(Out, RunDigest):
96
+ run_id: str = Field(default="", exclude=True)
97
+ cost_usd: float | None = Field(default=None, exclude=True)
98
+ tool_call_groups: list[ToolCallGroup] = Field(default=[], exclude=True)
99
+ cost: Money | None
100
+ tool_errors_by_tool: list[ToolErrorCount] # type: ignore[assignment]
101
+ cost_by_model: list[ModelCostRow] # type: ignore[assignment]
102
+
103
+
104
+ class TurnRow(Out, Turn):
105
+ cost_usd: float | None = Field(default=None, exclude=True)
106
+ cost: Money | None
107
+
108
+
109
+ class RunRow(Out):
110
+ id: str
111
+ title: str | None = None
112
+ created_at: datetime | None = None
113
+ user_id: str | None = None
114
+ client_id: str | None = None
115
+ source: Literal["app", "eval"] | None = None
116
+ status: str | None = None
117
+ experiment_id: int | None = None
118
+ experiment_number: int | None = None
119
+ case_id: str | None = None
120
+ metrics: RunMetrics | None = None
121
+ turns: list[TurnRow] | None = None
122
+ tool_calls: list[ToolCallRow] | None = None
123
+ note: Note | None = None
124
+ # A run whose trajectory could not be read: a failure, counted in meta.failed.
125
+ error: str | None = None
126
+ detail: str | None = None
127
+
128
+
129
+ class FindRunsResponse(Out):
130
+ data: list[RunRow]
131
+ meta: PagedMeta
132
+
133
+
134
+ class GetRunsResponse(Out):
135
+ data: list[RunRow]
136
+ meta: BatchMeta
137
+
138
+
139
+ class StatsRow(Out, GroupStats):
140
+ cost_usd: float = Field(default=0.0, exclude=True)
141
+ cost: Money
142
+
143
+
144
+ class StatsMeta(Out):
145
+ count: int
146
+ group_by: list[str]
147
+ timezone: str
148
+ runs_considered: int
149
+ runs_in_window: int
150
+ capped: bool
151
+ unavailable: int
152
+ note: Note | None = None
153
+
154
+
155
+ class GetRunStatsResponse(Out):
156
+ data: list[StatsRow]
157
+ meta: StatsMeta
158
+
159
+
160
+ class SubagentRef(Out):
161
+ index: int
162
+ trajectory_id: str | None
163
+ agent: str | None
164
+ steps: int
165
+
166
+
167
+ class TrajectoryMeta(PagedMeta):
168
+ schema_version: str
169
+ session_id: str | None
170
+ trajectory_id: str | None
171
+ agent: dict[str, Any] | None
172
+ final_metrics: dict[str, Any] | None
173
+ subagents: list[SubagentRef]
174
+
175
+
176
+ class GetTrajectoryResponse(Out):
177
+ data: list[dict[str, Any]]
178
+ meta: TrajectoryMeta
179
+
180
+
181
+ class FileRow(Out):
182
+ path: str
183
+ size: int
184
+ content_type: str | None = None
185
+
186
+
187
+ class GetRunFilesResponse(Out):
188
+ data: list[FileRow]
189
+ meta: PlainMeta
190
+
191
+
192
+ class FileText(Out):
193
+ path: str
194
+ size: int
195
+ text: str | None = None
196
+ note: Note | None = None
197
+
198
+
199
+ class DownloadRunFileResponse(Out):
200
+ data: list[FileText]
201
+ meta: PagedMeta
202
+
203
+
204
+ class ExperimentRow(Out):
205
+ model_config = ConfigDict(extra="allow")
206
+
207
+ id: int
208
+ number: int | None = None
209
+ workspace: str | None = None
210
+ dataset: str | None = None
211
+ agent: str | None = None
212
+ created_by: str | None = None
213
+ status: str | None = None
214
+ status_error: str | None = None
215
+ repetitions: int | None = None
216
+ started_at: datetime | None = None
217
+ ended_at: datetime | None = None
218
+ note: Note | None = None
219
+
220
+
221
+ class FindExperimentsResponse(Out):
222
+ data: list[ExperimentRow]
223
+ meta: PagedMeta
224
+
225
+
226
+ class GetExperimentsResponse(Out):
227
+ data: list[ExperimentRow]
228
+ meta: BatchMeta
229
+
230
+
231
+ class Evaluation(Out):
232
+ name: str
233
+ kind: str
234
+ value: float | None
235
+ label: str | None
236
+ explanation: str | None
237
+ error: str | None
238
+
239
+
240
+ class ExperimentRunRow(Out):
241
+ id: str
242
+ case_id: str
243
+ repetition: int
244
+ status: str
245
+ error: str | None
246
+ answer: str | None
247
+ started_at: datetime
248
+ ended_at: datetime | None
249
+ metrics: dict[str, Any]
250
+ evaluations: list[Evaluation]
251
+
252
+
253
+ class ExperimentRunsMeta(PagedMeta):
254
+ agent: str
255
+
256
+
257
+ class GetExperimentRunsResponse(Out):
258
+ data: list[ExperimentRunRow]
259
+ meta: ExperimentRunsMeta
@@ -0,0 +1,101 @@
1
+ import asyncio
2
+ import json
3
+ import logging
4
+ import time
5
+ from collections.abc import AsyncIterator
6
+ from contextlib import asynccontextmanager
7
+
8
+ from fastapi import FastAPI
9
+ from fastmcp import FastMCP
10
+ from fastmcp.exceptions import ToolError
11
+ from fastmcp.server.auth import TokenVerifier
12
+ from fastmcp.server.dependencies import get_access_token
13
+ from fastmcp.server.middleware import Middleware, MiddlewareContext
14
+ from starlette.applications import Starlette
15
+ from starlette.routing import Route
16
+
17
+ from evalkit import version
18
+ from evalkit.api.mcp import tools
19
+ from evalkit.api.mcp.auth import MCP_PATH, build_auth
20
+ from evalkit.api.mcp.descriptions import INSTRUCTIONS
21
+
22
+ LOGGER = logging.getLogger(__name__)
23
+ LOGGED_ARGS_CHARS = 2000
24
+
25
+
26
+ class ToolErrors(Middleware):
27
+ async def on_call_tool(self, context: MiddlewareContext, call_next):
28
+ token = get_access_token()
29
+ caller = token.claims.get("sub") if token else None
30
+ name = context.message.name
31
+ arguments = json.dumps(context.message.arguments or {}, default=str)[:LOGGED_ARGS_CHARS]
32
+ started = time.monotonic()
33
+ outcome = "ok"
34
+ try:
35
+ return await call_next(context)
36
+ except ToolError as exc:
37
+ outcome = str(exc).split(":", 1)[0]
38
+ raise
39
+ finally:
40
+ LOGGER.info(
41
+ "mcp tool %s by %s: %s in %dms args=%s",
42
+ name,
43
+ caller,
44
+ outcome,
45
+ (time.monotonic() - started) * 1000,
46
+ arguments,
47
+ )
48
+
49
+
50
+ def build_mcp(app: FastAPI, jwt_verifier: TokenVerifier | None = None) -> FastMCP:
51
+ mcp = FastMCP(
52
+ "evalkit",
53
+ version=version.current(),
54
+ instructions=INSTRUCTIONS,
55
+ auth=build_auth(app.state.cfg, app.state.session_factory, jwt_verifier),
56
+ middleware=[ToolErrors()],
57
+ mask_error_details=True,
58
+ )
59
+ tools.register(mcp, app.state)
60
+ return mcp
61
+
62
+
63
+ # Only the MCP endpoint and its RFC 9728 metadata are routed to the MCP app,
64
+ # so /api keeps its own auth and the SPA fallback never sees /mcp.
65
+ def mount_mcp(app: FastAPI, jwt_verifier: TokenVerifier | None = None):
66
+ mcp_app = build_mcp(app, jwt_verifier).http_app(
67
+ path=MCP_PATH,
68
+ transport="streamable-http",
69
+ stateless_http=True,
70
+ json_response=True,
71
+ host_origin_protection=False,
72
+ )
73
+ for route in mcp_app.routes:
74
+ if isinstance(route, Route):
75
+ app.router.routes.append(Route(route.path, mcp_app, include_in_schema=False))
76
+ return mcp_app
77
+
78
+
79
+ # The session manager's task group must open and close in one task; a test
80
+ # client may enter and leave the app lifespan from different ones.
81
+ @asynccontextmanager
82
+ async def mcp_running(mcp_app: Starlette) -> AsyncIterator[None]:
83
+ started = asyncio.Event()
84
+ stop = asyncio.Event()
85
+
86
+ async def hold() -> None:
87
+ async with mcp_app.router.lifespan_context(mcp_app):
88
+ started.set()
89
+ await stop.wait()
90
+
91
+ task = asyncio.create_task(hold())
92
+ ready = asyncio.create_task(started.wait())
93
+ await asyncio.wait({task, ready}, return_when=asyncio.FIRST_COMPLETED)
94
+ if task.done():
95
+ ready.cancel()
96
+ task.result()
97
+ try:
98
+ yield
99
+ finally:
100
+ stop.set()
101
+ await task