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,120 @@
1
+ from datetime import datetime
2
+ from typing import Any, Literal
3
+
4
+ from pydantic import BaseModel, ConfigDict, Field
5
+
6
+ EsfKind = Literal["llm", "tool", "agent", "chain", "retriever", "other"]
7
+ MessageFormat = Literal["openinference", "gen_ai", "openai"]
8
+ MAPPING_SCHEMA_VERSION = "1"
9
+
10
+
11
+ class EsfToolCall(BaseModel):
12
+ id: str
13
+ name: str
14
+ arguments: dict[str, Any] | str
15
+
16
+
17
+ class EsfMessage(BaseModel):
18
+ role: str
19
+ text: str | None = None
20
+ reasoning: str | None = None
21
+ tool_calls: list[EsfToolCall] | None = None
22
+ tool_call_id: str | None = None
23
+
24
+
25
+ class EsfSpan(BaseModel):
26
+ span_id: str
27
+ trace_id: str
28
+ parent_id: str | None
29
+ name: str
30
+ kind: EsfKind
31
+ started_at: datetime
32
+ ended_at: datetime | None
33
+ error: str | None
34
+ model: str | None = None
35
+ input_messages: list[EsfMessage] | None = None
36
+ output_messages: list[EsfMessage] | None = None
37
+ input: Any | None = None
38
+ output: Any | None = None
39
+ tool_name: str | None = None
40
+ tool_call_id: str | None = None
41
+ agent_name: str | None = None
42
+ prompt_tokens: int | None = None
43
+ completion_tokens: int | None = None
44
+ cached_tokens: int | None = None
45
+ cache_write_tokens: int | None = None
46
+ cost_usd: float | None = None
47
+ unmapped: list[str] = Field(default_factory=list)
48
+
49
+
50
+ class MappingModel(BaseModel):
51
+ model_config = ConfigDict(extra="forbid", populate_by_name=True)
52
+
53
+
54
+ class AttributeRule(MappingModel):
55
+ attribute: str = Field(description="The span attribute key to read.")
56
+ json_: bool = Field(
57
+ default=False,
58
+ alias="json",
59
+ description="Parse the value with json.loads when it is a string; keep the string "
60
+ "on failure.",
61
+ )
62
+ values: dict[str, str] | None = Field(
63
+ default=None, description="Translate the read value through this table."
64
+ )
65
+ message_format: MessageFormat | None = Field(
66
+ default=None,
67
+ description="Collect messages laid out per this format under the attribute key.",
68
+ )
69
+
70
+
71
+ class TokenRules(MappingModel):
72
+ prompt: AttributeRule | None = None
73
+ completion: AttributeRule | None = None
74
+ cached: AttributeRule | None = None
75
+ cache_write: AttributeRule | None = None
76
+
77
+
78
+ class ToolRules(MappingModel):
79
+ name: AttributeRule | None = None
80
+ call_id: AttributeRule | None = None
81
+ arguments: AttributeRule | None = None
82
+ result: AttributeRule | None = None
83
+
84
+
85
+ class MappingDocument(MappingModel):
86
+ schema_version: Literal["1"]
87
+ kind: AttributeRule | None = None
88
+ session_id: AttributeRule | None = None
89
+ model: AttributeRule | None = None
90
+ system_instructions: AttributeRule | None = Field(
91
+ default=None,
92
+ description="System prompt kept outside the input messages: a string or a list of "
93
+ "text parts. Read on llm spans and put first in the input messages.",
94
+ )
95
+ input_messages: AttributeRule | None = None
96
+ output_messages: AttributeRule | None = None
97
+ tokens: TokenRules = Field(default_factory=TokenRules)
98
+ cost_usd: AttributeRule | None = None
99
+ tool: ToolRules = Field(default_factory=ToolRules)
100
+ agent_name: AttributeRule | None = Field(
101
+ default=None,
102
+ description="The agent's name on agent spans; names the subagent trajectory. The span "
103
+ "name is used when it is absent.",
104
+ )
105
+ input: AttributeRule | None = None
106
+ output: AttributeRule | None = None
107
+ known_attributes: list[str] = Field(
108
+ default_factory=list,
109
+ description="Attributes no rule reads that are expected and need nothing: not reported "
110
+ "as unmapped. Glob patterns such as llm.tools.* are allowed.",
111
+ )
112
+ answer: str = Field(
113
+ default="root",
114
+ description="Where the run's answer comes from: root, a span name, or "
115
+ "<span name>.<output key>.",
116
+ )
117
+ exclude_spans: list[str] = Field(
118
+ default_factory=list,
119
+ description="Span names left out of the trajectory.",
120
+ )
@@ -0,0 +1,281 @@
1
+ import json
2
+ from datetime import datetime
3
+ from typing import Literal
4
+
5
+ from jsonschema.exceptions import SchemaError
6
+ from jsonschema.validators import validator_for
7
+ from pydantic import BaseModel, ConfigDict, Field, model_validator
8
+
9
+
10
+ class HarnessModel(BaseModel):
11
+ model_config = ConfigDict(extra="allow")
12
+
13
+
14
+ DEFAULT_INPUT_SCHEMA: dict = {
15
+ "type": "object",
16
+ "required": ["question"],
17
+ "properties": {"question": {"type": "string"}},
18
+ }
19
+
20
+
21
+ def input_errors(schema: dict, run_input: dict) -> list[str]:
22
+ validator_cls = validator_for(schema)
23
+ validator = validator_cls(schema)
24
+ errors = sorted(validator.iter_errors(run_input), key=lambda e: list(e.absolute_path))
25
+ return [
26
+ f"{'/'.join(str(part) for part in e.absolute_path) or '<root>'}: {e.message}"
27
+ for e in errors
28
+ ]
29
+
30
+
31
+ def answer_text(answer: str | None, output: dict | None) -> str:
32
+ if answer is not None:
33
+ return answer
34
+ return json.dumps(output, sort_keys=True, ensure_ascii=False)
35
+
36
+
37
+ class Provides(HarnessModel):
38
+ trajectory: bool = Field(
39
+ default=False, description="Can serve GET runs/{id}/trajectory at all."
40
+ )
41
+ tool_calls: bool = Field(default=False, description="Trajectory reliably contains tool calls.")
42
+ thinking: bool = Field(
43
+ default=False, description="Trajectory reliably contains reasoning content."
44
+ )
45
+ token_usage: bool = Field(default=False, description="Trajectory carries per-step token usage.")
46
+ artifacts: bool = Field(
47
+ default=False, description="Run status reports artifacts the agent produced."
48
+ )
49
+ workspace: bool = Field(default=False, description="Files endpoints are implemented.")
50
+ run_listing: bool = Field(
51
+ default=False,
52
+ description="GET /runs (list) is implemented. Powers the sessions browser.",
53
+ )
54
+
55
+
56
+ class Accepts(HarnessModel):
57
+ files: bool = Field(default=False, description="POST /runs accepts multipart file attachments.")
58
+ file_urls: bool = Field(
59
+ default=False,
60
+ description="POST /runs (JSON body) accepts `files: [{name, url}]` and the harness "
61
+ "fetches each url itself. Preferred over multipart when both are declared. The url "
62
+ "is evalkit's blob store url plus the object key (e.g. s3://bucket/fixtures/<key>); "
63
+ "the harness must have access to it.",
64
+ )
65
+ model_override: bool = Field(default=False, description="POST /runs honours the 'model' field.")
66
+ stop: bool = Field(default=False, description="POST /runs/{id}/stop is implemented.")
67
+
68
+ @property
69
+ def file_input(self) -> bool:
70
+ return self.file_urls or self.files
71
+
72
+
73
+ class Limits(HarnessModel):
74
+ max_concurrent_runs: int | None = Field(
75
+ default=None,
76
+ description="Harness-side cap. evalkit uses min(this, its own config).",
77
+ )
78
+
79
+
80
+ class Capabilities(HarnessModel):
81
+ contract_version: str = Field(
82
+ default="v1", description="Harness API version this harness implements."
83
+ )
84
+ agent: str = Field(description="Harness's own name for itself. Informational.")
85
+ revision: str | None = Field(
86
+ default=None,
87
+ description="Optional. Git commit SHA of the deployed harness; evalkit "
88
+ "records it on the experiment so results are traceable to a build.",
89
+ )
90
+ branch: str | None = Field(
91
+ default=None, description="Optional. Git branch the deployed harness was built from."
92
+ )
93
+ input_schema: dict | None = Field(
94
+ default=None,
95
+ description="Optional. JSON Schema (draft 2020-12 unless `$schema` says otherwise) "
96
+ "of the run input this agent accepts. Excludes `files`, which evalkit sends "
97
+ "separately. When absent, evalkit assumes the default question-and-answer schema: "
98
+ "an object with a required string `question`. evalkit checks case inputs against "
99
+ "the declared or default schema before running an experiment and in "
100
+ "`evalkit lint --agent`.",
101
+ )
102
+ check_input: dict | None = Field(
103
+ default=None,
104
+ description="Optional. The run input `evalkit harness check` sends. Must pass "
105
+ "input_schema, or the default question-and-answer schema when none is declared.",
106
+ )
107
+ provides: Provides = Field(default_factory=Provides)
108
+ accepts: Accepts = Field(default_factory=Accepts)
109
+ limits: Limits = Field(default_factory=Limits)
110
+
111
+ @property
112
+ def effective_input_schema(self) -> dict:
113
+ return self.input_schema or DEFAULT_INPUT_SCHEMA
114
+
115
+ @model_validator(mode="after")
116
+ def _schema_and_check_input_agree(self) -> "Capabilities":
117
+ if self.input_schema is not None:
118
+ try:
119
+ validator_for(self.input_schema).check_schema(self.input_schema)
120
+ except SchemaError as exc:
121
+ raise ValueError(f"input_schema is not a valid JSON Schema: {exc.message}") from exc
122
+ if self.check_input is not None:
123
+ errors = input_errors(self.effective_input_schema, self.check_input)
124
+ if errors:
125
+ raise ValueError(f"check_input fails input_schema: {errors[0]}")
126
+ return self
127
+
128
+
129
+ class RunMetadata(HarnessModel):
130
+ experiment_id: str | None = Field(
131
+ default=None,
132
+ description="Experiment id, so eval traffic is distinguishable and traceable.",
133
+ )
134
+ case_id: str | None = Field(default=None, description="Case id for the harness's records.")
135
+ repetition: int | None = Field(default=None, description="Repetition number, 1-based.")
136
+
137
+
138
+ class FileRef(HarnessModel):
139
+ name: str = Field(description="File name to present to the agent — the fixture key.")
140
+ url: str | None = Field(
141
+ default=None,
142
+ description="Where the harness fetches the bytes from. Present on a JSON request "
143
+ "(accepts.file_urls); absent on multipart, where the bytes arrive as the part with "
144
+ "this name. Opaque to evalkit's experiment runner; the UI still serves the same "
145
+ "fixture via GET /api/fixtures/{name}.",
146
+ )
147
+ context: str | None = Field(
148
+ default=None,
149
+ description="What the file holds, from the suite's fixture registry. evalkit no "
150
+ "longer prepends it to input.question; show it to the agent however suits it.",
151
+ )
152
+
153
+
154
+ class RunRequest(HarnessModel):
155
+ input: dict = Field(
156
+ description="The case input, verbatim; evalkit never rewrites it. Free-form "
157
+ "object (see capabilities.input_schema); the harness maps it "
158
+ "onto its own invocation."
159
+ )
160
+ model: str | None = Field(
161
+ default=None, description="Optional. Only sent if accepts.model_override."
162
+ )
163
+ metadata: RunMetadata | None = Field(
164
+ default=None,
165
+ description="Optional, for the harness's own records. The harness may ignore it.",
166
+ )
167
+ files: list[FileRef] | None = Field(
168
+ default=None,
169
+ description="Optional. The case's fixture files, each with its context. On a JSON "
170
+ "request (accepts.file_urls) each carries the url to fetch; on multipart it is in "
171
+ "the payload part without urls, next to one file part per entry.",
172
+ )
173
+
174
+
175
+ class RunCreated(HarnessModel):
176
+ run_id: str = Field(
177
+ description="The harness's own identifier — typically its session id. "
178
+ "Key for every other endpoint."
179
+ )
180
+
181
+
182
+ class ArtifactRef(HarnessModel):
183
+ name: str = Field(description="Artifact display name.")
184
+ path: str | None = Field(
185
+ default=None, description="Workspace path, fetchable via the files endpoint."
186
+ )
187
+ content_type: str | None = Field(default=None, description="MIME type, if known.")
188
+
189
+
190
+ class RunStatus(HarnessModel):
191
+ status: Literal["queued", "running", "completed", "failed", "cancelled"] = Field(
192
+ description="Run state. evalkit polls until terminal or timeout."
193
+ )
194
+ answer: str | None = Field(
195
+ default=None,
196
+ description="Final user-visible answer. A completed run carries this, `output`, or both.",
197
+ )
198
+ output: dict | None = Field(
199
+ default=None,
200
+ description="Optional. The agent's structured result, for agents that return "
201
+ "typed data. A completed run carries this, `answer`, or both.",
202
+ )
203
+ model: str = Field(
204
+ description="Required. The model this run executes on — the requested "
205
+ "model when honoured, otherwise the harness's own default. evalkit "
206
+ "records it as the experiment's model and prefers it over the request.",
207
+ )
208
+ artifacts: list[ArtifactRef] | None = Field(
209
+ default=None, description="Optional. Files the agent produced as deliverables."
210
+ )
211
+ error: str | None = Field(
212
+ default=None,
213
+ description="Message when status=failed. A completed run may also carry a "
214
+ "non-fatal error (e.g. partial answer).",
215
+ )
216
+
217
+ @model_validator(mode="after")
218
+ def _completed_carries_answer_or_output(self) -> "RunStatus":
219
+ if self.status == "completed" and self.answer is None and self.output is None:
220
+ raise ValueError("a completed run must carry an answer or an output")
221
+ return self
222
+
223
+
224
+ class RunListParams(BaseModel):
225
+ query: str | None = Field(
226
+ default=None,
227
+ description="Free-text filter over runs. Text that looks like an id (8 or more "
228
+ "hex characters; dashes and case ignored) matches runs whose id starts with it. "
229
+ "Any other text matches runs whose title contains it, case-insensitive.",
230
+ )
231
+ user_id: str | None = Field(default=None, description="Filter to one user's runs.")
232
+ client_id: str | None = Field(default=None, description="Filter to one client's runs.")
233
+ limit: int = Field(default=50, description="Page size.")
234
+ offset: int = Field(default=0, description="Page start.")
235
+ since: datetime | None = Field(
236
+ default=None, description="Only runs created at or after this time (ISO 8601)."
237
+ )
238
+ until: datetime | None = Field(
239
+ default=None, description="Only runs created before this time (ISO 8601)."
240
+ )
241
+
242
+ def as_query(self) -> dict[str, str | int]:
243
+ return {
244
+ k: v.isoformat().replace("+00:00", "Z") if isinstance(v, datetime) else v
245
+ for k, v in self.model_dump().items()
246
+ if v is not None
247
+ }
248
+
249
+
250
+ class RunListItem(HarnessModel):
251
+ run_id: str = Field(description="Usable with the other /runs/{id} endpoints.")
252
+ title: str | None = Field(default=None, description="Display title for the run/session.")
253
+ user_id: str | None = Field(
254
+ default=None, description="Optional filter value; fixed dummy if no multi-tenancy."
255
+ )
256
+ client_id: str | None = Field(
257
+ default=None, description="Optional filter value; fixed dummy if no multi-tenancy."
258
+ )
259
+ created_at: datetime | None = Field(default=None, description="Run creation time.")
260
+ session_id: str | None = Field(
261
+ default=None,
262
+ description="The session this run belongs to, for a harness that keeps several of its "
263
+ "own runs per session (for example one per chat message) and answers each of them with "
264
+ "the whole session. evalkit's run is the session: items sharing a session_id are listed "
265
+ "once, as the newest of them.",
266
+ )
267
+
268
+
269
+ class RunList(HarnessModel):
270
+ runs: list[RunListItem] = Field(description="Page of runs, newest first.")
271
+ total: int = Field(description="Total runs matching the query.")
272
+
273
+
274
+ class FileEntry(HarnessModel):
275
+ path: str = Field(description="Workspace-relative path.")
276
+ size: int = Field(description="Size in bytes.")
277
+ content_type: str | None = Field(default=None, description="MIME type, if known.")
278
+
279
+
280
+ class FileList(HarnessModel):
281
+ files: list[FileEntry] = Field(description="Workspace listing.")
@@ -0,0 +1,4 @@
1
+ {
2
+ "endpoint_note": "Check this URL before saving. The server at this address receives the API key you enter here and the data evalkit sends it: cases, answers and trajectories.",
3
+ "personal_key_note": "Personal agents and evaluators read their API key from your evalkit.yaml (credentials:), never from the server."
4
+ }
@@ -0,0 +1,7 @@
1
+ import json
2
+ from importlib.resources import files
3
+
4
+ _NOTES = json.loads(files("evalkit.contracts").joinpath("notes.json").read_text())
5
+
6
+ ENDPOINT_NOTE: str = _NOTES["endpoint_note"]
7
+ PERSONAL_KEY_NOTE: str = _NOTES["personal_key_note"]
@@ -0,0 +1,41 @@
1
+ from pydantic import BaseModel, ConfigDict, Field
2
+
3
+
4
+ class JudgeSettings(BaseModel):
5
+ """The LLM judge an experiment uses; exported from `evalkit.sdk` as `Judge`.
6
+
7
+ Its API key comes from `credentials.judges` in `evalkit.yaml`, keyed by `base_url`.
8
+
9
+ Attributes:
10
+ base_url: An OpenAI-compatible endpoint that supports structured output.
11
+ model: The model name at that endpoint.
12
+ reasoning_effort: Passed to the model as `reasoning_effort`.
13
+ max_tokens: The most tokens one judge reply may use.
14
+ concurrency: How many judge calls run at once.
15
+ """
16
+
17
+ model_config = ConfigDict(extra="forbid")
18
+
19
+ base_url: str
20
+ model: str
21
+ reasoning_effort: str = "high"
22
+ max_tokens: int = Field(32768, ge=1)
23
+ concurrency: int = Field(16, ge=1)
24
+
25
+
26
+ class AgentSettings(BaseModel):
27
+ model_config = ConfigDict(extra="forbid")
28
+
29
+ max_concurrent_runs: int = Field(default=4, ge=1)
30
+ trajectory_wait_seconds: int = Field(default=60, ge=0)
31
+ run_timeout_seconds: int = Field(default=840, ge=0)
32
+ poll_interval_seconds: int = Field(default=5, ge=0)
33
+ stop_grace_seconds: int = Field(default=120, ge=0)
34
+ provides: dict[str, bool] | None = None
35
+
36
+
37
+ class RemoteEvaluatorSettings(BaseModel):
38
+ model_config = ConfigDict(extra="forbid")
39
+
40
+ endpoint: str
41
+ timeout_seconds: int = Field(default=120, ge=1)
File without changes
evalkit/core/access.py ADDED
@@ -0,0 +1,75 @@
1
+ from collections.abc import Mapping
2
+ from dataclasses import dataclass
3
+
4
+ from evalkit.contracts.enums import WorkspaceRole
5
+
6
+
7
+ @dataclass(frozen=True)
8
+ class WorkspaceRef:
9
+ id: int
10
+ name: str
11
+ owner_user_id: int | None
12
+ archived: bool
13
+ promotes_to: int | None = None
14
+ promoted_from: int | None = None
15
+
16
+ @property
17
+ def is_shared(self) -> bool:
18
+ return self.owner_user_id is None
19
+
20
+ @property
21
+ def is_target(self) -> bool:
22
+ return self.promoted_from is not None
23
+
24
+
25
+ @dataclass(frozen=True)
26
+ class Actor:
27
+ id: int
28
+ username: str
29
+ is_admin: bool
30
+ roles: Mapping[int, frozenset[WorkspaceRole]]
31
+
32
+ def has(self, ws: WorkspaceRef, role: WorkspaceRole) -> bool:
33
+ return role in self.roles.get(ws.id, frozenset())
34
+
35
+
36
+ def can_read(actor: Actor, ws: WorkspaceRef) -> bool:
37
+ return True
38
+
39
+
40
+ def can_write(actor: Actor, ws: WorkspaceRef) -> bool:
41
+ if not ws.is_shared:
42
+ return ws.owner_user_id == actor.id
43
+ return actor.is_admin or actor.has(ws, WorkspaceRole.WRITER)
44
+
45
+
46
+ def can_review(actor: Actor, ws: WorkspaceRef) -> bool:
47
+ return ws.is_shared and actor.has(ws, WorkspaceRole.REVIEWER)
48
+
49
+
50
+ def can_use_agent(actor: Actor, agent_ws: WorkspaceRef) -> bool:
51
+ if not agent_ws.is_shared:
52
+ return agent_ws.owner_user_id == actor.id
53
+ return True
54
+
55
+
56
+ def can_change_experiment(actor: Actor, exp_ws: WorkspaceRef, created_by: int | None) -> bool:
57
+ return can_write(actor, exp_ws) or created_by == actor.id
58
+
59
+
60
+ def can_create_experiment(actor: Actor, exp_ws: WorkspaceRef) -> bool:
61
+ if not exp_ws.is_shared:
62
+ return exp_ws.owner_user_id == actor.id
63
+ return (
64
+ actor.is_admin
65
+ or actor.has(exp_ws, WorkspaceRole.WRITER)
66
+ or actor.has(exp_ws, WorkspaceRole.REVIEWER)
67
+ )
68
+
69
+
70
+ def experiment_workspace(
71
+ runner_personal: WorkspaceRef, dataset_ws: WorkspaceRef, agent_ws: WorkspaceRef
72
+ ) -> WorkspaceRef:
73
+ if dataset_ws.is_shared and agent_ws.is_shared:
74
+ return dataset_ws
75
+ return runner_personal
evalkit/core/agents.py ADDED
@@ -0,0 +1,65 @@
1
+ from enum import StrEnum
2
+ from typing import Protocol
3
+
4
+ from pydantic import BaseModel
5
+
6
+ from evalkit.contracts.enums import TrajectorySource
7
+ from evalkit.contracts.settings import AgentSettings
8
+ from evalkit.core.workspace import Reference
9
+
10
+
11
+ class AgentDefinition(BaseModel):
12
+ id: int
13
+ name: str
14
+ workspace: str
15
+ workspace_is_shared: bool
16
+ harness_url: str | None
17
+ trajectory_source: TrajectorySource
18
+ repo_url: str | None
19
+ settings: AgentSettings
20
+ archived: bool = False
21
+ has_api_key: bool = False
22
+
23
+ @property
24
+ def ref(self) -> str:
25
+ return f"{self.name}@{self.workspace}"
26
+
27
+ @property
28
+ def reference(self) -> Reference:
29
+ return Reference(self.name, self.workspace)
30
+
31
+ @property
32
+ def is_otel(self) -> bool:
33
+ return self.trajectory_source is TrajectorySource.OTEL
34
+
35
+
36
+ class Driver(StrEnum):
37
+ SERVER = "server"
38
+ CLI = "cli"
39
+ SDK = "sdk"
40
+
41
+
42
+ def driver(agent: AgentDefinition) -> Driver:
43
+ if agent.harness_url is None:
44
+ return Driver.SDK
45
+ if agent.workspace_is_shared:
46
+ return Driver.SERVER
47
+ return Driver.CLI
48
+
49
+
50
+ def server_may_drive(agent: AgentDefinition) -> bool:
51
+ return driver(agent) is Driver.SERVER
52
+
53
+
54
+ class UnknownAgent(KeyError):
55
+ def __init__(self, ref: str):
56
+ super().__init__(f"unknown agent: {ref} — create it with evalkit agents create")
57
+ self.ref = ref
58
+
59
+
60
+ class AgentLookup(Protocol):
61
+ async def get(self, ref: str) -> AgentDefinition: ...
62
+
63
+
64
+ def explicit(ref: Reference) -> str:
65
+ return f"{ref.name}@{ref.workspace}"
evalkit/core/blob.py ADDED
@@ -0,0 +1,46 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import AsyncIterator
4
+ from typing import Protocol
5
+
6
+ from evalkit.core.paths import require_safe_path, require_safe_run_id
7
+ from evalkit.store.base import BlobStream
8
+
9
+
10
+ class BlobStore(Protocol):
11
+ async def put_object(self, key: str, data: bytes) -> None: ...
12
+
13
+ async def put_stream(self, key: str, chunks: AsyncIterator[bytes]) -> None: ...
14
+
15
+ async def get_object(self, key: str) -> bytes: ...
16
+
17
+ async def get_stream(self, key: str) -> BlobStream: ...
18
+
19
+ async def object_exists(self, key: str) -> bool: ...
20
+
21
+ async def list_objects(self, prefix: str) -> list[tuple[str, int]]: ...
22
+
23
+ async def delete_object(self, key: str) -> None: ...
24
+
25
+ async def copy_object(self, source: str, destination: str) -> None: ...
26
+
27
+ async def object_url(self, key: str) -> str: ...
28
+
29
+
30
+ def trajectory_key(agent_id: int, harness_run_id: str) -> str:
31
+ return f"trajectories/{agent_id}/{harness_run_id}.json"
32
+
33
+
34
+ def artifact_key(agent_id: int, harness_run_id: str, path: str) -> str:
35
+ require_safe_run_id(harness_run_id)
36
+ if path:
37
+ path = require_safe_path(path)
38
+ return f"artifacts/{agent_id}/{harness_run_id}/{path}"
39
+
40
+
41
+ def workspace_key(agent_id: int, harness_run_id: str) -> str:
42
+ return f"workspaces/{agent_id}/{harness_run_id}.json"
43
+
44
+
45
+ def fixture_blob_key(sha256: str) -> str:
46
+ return f"fixtures/sha256/{sha256}"
@@ -0,0 +1,36 @@
1
+ from evalkit.contracts.harness import Accepts, Capabilities, Limits, Provides
2
+ from evalkit.core.agents import AgentDefinition
3
+
4
+ SDK_CONTRACT_VERSION = "sdk"
5
+ TRACED_FLAGS = ("trajectory", "tool_calls", "token_usage")
6
+
7
+
8
+ def sdk_capabilities(agent: AgentDefinition) -> Capabilities:
9
+ provides = {
10
+ "trajectory": True,
11
+ "tool_calls": True,
12
+ "token_usage": True,
13
+ "thinking": False,
14
+ "artifacts": False,
15
+ "workspace": False,
16
+ "run_listing": True,
17
+ **(agent.settings.provides or {}),
18
+ }
19
+ return Capabilities(
20
+ contract_version=SDK_CONTRACT_VERSION,
21
+ agent=agent.name,
22
+ provides=Provides(**provides),
23
+ accepts=Accepts(files=True, model_override=True, file_urls=False, stop=False),
24
+ limits=Limits(max_concurrent_runs=agent.settings.max_concurrent_runs),
25
+ )
26
+
27
+
28
+ def traced_capabilities(caps: Capabilities, agent: AgentDefinition) -> Capabilities:
29
+ if not agent.is_otel:
30
+ return caps
31
+ provides = {
32
+ **caps.provides.model_dump(),
33
+ **dict.fromkeys(TRACED_FLAGS, True),
34
+ **(agent.settings.provides or {}),
35
+ }
36
+ return caps.model_copy(update={"provides": Provides(**provides)})