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,121 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import AsyncIterator
4
+ from datetime import datetime
5
+ from typing import Any
6
+
7
+ import httpx
8
+
9
+ from evalkit.core.traces.raw import RawSpan
10
+ from evalkit.core.traces.sources.base import TracePage
11
+
12
+
13
+ def flatten_attributes(nested: dict[str, Any], prefix: str = "") -> dict[str, Any]:
14
+ flat: dict[str, Any] = {}
15
+ for key, value in nested.items():
16
+ name = f"{prefix}{key}"
17
+ if isinstance(value, dict):
18
+ flat.update(flatten_attributes(value, f"{name}."))
19
+ elif isinstance(value, list) and value and all(isinstance(v, dict) for v in value):
20
+ for i, item in enumerate(value):
21
+ flat.update(flatten_attributes(item, f"{name}.{i}."))
22
+ else:
23
+ flat[name] = value
24
+ return flat
25
+
26
+
27
+ def _to_raw(span: dict[str, Any]) -> RawSpan:
28
+ context = span.get("context") or {}
29
+ end = span.get("end_time")
30
+ return RawSpan(
31
+ trace_id=str(context.get("trace_id") or span.get("trace_id")),
32
+ span_id=str(context.get("span_id") or span.get("span_id")),
33
+ parent_span_id=span.get("parent_id") or None,
34
+ name=str(span.get("name") or ""),
35
+ kind=str(span.get("span_kind") or "INTERNAL"),
36
+ start_time=datetime.fromisoformat(span["start_time"]),
37
+ end_time=datetime.fromisoformat(end) if end else None,
38
+ status_code=str(span.get("status_code") or "UNSET"),
39
+ status_message=span.get("status_message") or None,
40
+ attributes=flatten_attributes(span.get("attributes") or {}),
41
+ events=[
42
+ {
43
+ "name": e.get("name"),
44
+ "time": e.get("timestamp"),
45
+ "attributes": e.get("attributes") or {},
46
+ }
47
+ for e in span.get("events") or []
48
+ ],
49
+ resource={},
50
+ )
51
+
52
+
53
+ SPAN_PAGE = 1000
54
+
55
+
56
+ class PhoenixClient:
57
+ def __init__(
58
+ self,
59
+ url: str,
60
+ api_key: str | None,
61
+ page_traces: int,
62
+ *,
63
+ transport: httpx.AsyncBaseTransport | None = None,
64
+ ):
65
+ self._url = url.rstrip("/")
66
+ self._api_key = api_key
67
+ self._page_traces = page_traces
68
+ self._transport = transport
69
+
70
+ async def _pages(
71
+ self, path: str, params: dict[str, str | list[str]]
72
+ ) -> AsyncIterator[list[dict[str, Any]]]:
73
+ cursor: str | None = None
74
+ async with httpx.AsyncClient(
75
+ base_url=self._url,
76
+ headers={"Authorization": f"Bearer {self._api_key}"} if self._api_key else {},
77
+ timeout=120,
78
+ transport=self._transport,
79
+ ) as client:
80
+ while True:
81
+ resp = await client.get(
82
+ path, params={**params, **({"cursor": cursor} if cursor else {})}
83
+ )
84
+ resp.raise_for_status()
85
+ body = resp.json()
86
+ rows = body.get("data") or []
87
+ if rows:
88
+ yield rows
89
+ cursor = body.get("next_cursor")
90
+ if not cursor or not rows:
91
+ return
92
+
93
+ def _traces(self, project: str, params: dict[str, str]) -> AsyncIterator[list[dict[str, Any]]]:
94
+ return self._pages(
95
+ f"/v1/projects/{project}/traces",
96
+ {"sort": "start_time", "order": "asc", "limit": str(self._page_traces), **params},
97
+ )
98
+
99
+ async def trace_spans(self, project: str, trace_ids: list[str]) -> AsyncIterator[list[RawSpan]]:
100
+ params: dict[str, str | list[str]] = {"trace_id": trace_ids, "limit": str(SPAN_PAGE)}
101
+ async for spans in self._pages(f"/v1/projects/{project}/spans", params):
102
+ yield [_to_raw(span) for span in spans]
103
+
104
+ async def session_spans(self, project: str, session_id: str) -> list[RawSpan]:
105
+ found: list[RawSpan] = []
106
+ async for traces in self._traces(project, {"session_identifier": session_id}):
107
+ async for spans in self.trace_spans(project, [t["trace_id"] for t in traces]):
108
+ found.extend(spans)
109
+ return found
110
+
111
+ async def trace_pages(
112
+ self, project: str, since: datetime, until: datetime
113
+ ) -> AsyncIterator[TracePage]:
114
+ window = {"start_time": since.isoformat(), "end_time": until.isoformat()}
115
+ async for traces in self._traces(project, window):
116
+ yield TracePage(
117
+ traces=[
118
+ (str(trace["trace_id"]), datetime.fromisoformat(trace["start_time"]))
119
+ for trace in traces
120
+ ]
121
+ )
@@ -0,0 +1,248 @@
1
+ import json
2
+
3
+ from pydantic import BaseModel, Field
4
+
5
+ from evalkit.contracts.atif import ContentPart, ObservationResult, Step, ToolCall, Trajectory
6
+ from evalkit.core.costmap import cost_usd, resolve_rates
7
+
8
+
9
+ def _message_text(message: str | list[ContentPart] | None) -> str:
10
+ if message is None:
11
+ return ""
12
+ if isinstance(message, str):
13
+ return message
14
+ return " ".join(part.text for part in message if part.type == "text" and part.text)
15
+
16
+
17
+ def _result_text(result: ObservationResult) -> str:
18
+ if result.content is None:
19
+ return ""
20
+ if isinstance(result.content, str):
21
+ return result.content
22
+ return " ".join(part.text for part in result.content if part.type == "text" and part.text)
23
+
24
+
25
+ class TokenTotals(BaseModel):
26
+ input: int | None = None
27
+ output: int | None = None
28
+ cached: int | None = None
29
+
30
+
31
+ class TrajectoryMetrics(BaseModel):
32
+ agent: str
33
+ input_tokens: int | None = None
34
+ output_tokens: int | None = None
35
+ cached_tokens: int | None = None
36
+ cost_usd: float | None = None
37
+ total_tool_calls: int = 0
38
+ errored_tool_calls: int = 0
39
+ turns: int = 0
40
+
41
+
42
+ class RunMetrics(BaseModel):
43
+ duration_ms: int | None = None
44
+ input_tokens: int | None = None
45
+ output_tokens: int | None = None
46
+ cached_tokens: int | None = None
47
+ cost_usd: float | None = None
48
+ total_tool_calls: int = 0
49
+ errored_tool_calls: int = 0
50
+ subagents: int = 0
51
+ turns: int = 0
52
+ by_trajectory: dict[str, TrajectoryMetrics] = Field(default_factory=dict)
53
+
54
+
55
+ def _add_reported[Num: (int, float)](total: Num | None, value: Num | None) -> Num | None:
56
+ if total is None:
57
+ return value
58
+ if value is None:
59
+ return total
60
+ return total + value
61
+
62
+
63
+ def _errored_results(step: Step) -> int:
64
+ if step.observation is None:
65
+ return 0
66
+ return sum(1 for r in step.observation.results if (r.extra or {}).get("is_error") is True)
67
+
68
+
69
+ def _own_metrics(trajectory: Trajectory, agent: str) -> TrajectoryMetrics:
70
+ metrics = TrajectoryMetrics(agent=agent)
71
+ for step in trajectory.steps:
72
+ if step.source == "agent":
73
+ metrics.turns += 1
74
+ metrics.total_tool_calls += len(step.tool_calls or [])
75
+ metrics.errored_tool_calls += _errored_results(step)
76
+ if step.metrics is not None:
77
+ metrics.input_tokens = _add_reported(metrics.input_tokens, step.metrics.prompt_tokens)
78
+ metrics.output_tokens = _add_reported(
79
+ metrics.output_tokens, step.metrics.completion_tokens
80
+ )
81
+ metrics.cached_tokens = _add_reported(metrics.cached_tokens, step.metrics.cached_tokens)
82
+ metrics.cost_usd = _add_reported(metrics.cost_usd, step.metrics.cost_usd)
83
+ if metrics.cost_usd is not None:
84
+ metrics.cost_usd = round(metrics.cost_usd, 10)
85
+ return metrics
86
+
87
+
88
+ class TrajectoryView:
89
+ def __init__(self, trajectory: Trajectory):
90
+ self.trajectory = trajectory
91
+
92
+ def _all_steps(self) -> list[Step]:
93
+ steps = list(self.trajectory.steps)
94
+ for sub in self.trajectory.subagent_trajectories or []:
95
+ steps.extend(sub.steps)
96
+ return steps
97
+
98
+ def tool_calls(self) -> list[ToolCall]:
99
+ calls: list[ToolCall] = []
100
+ for step in self._all_steps():
101
+ calls.extend(step.tool_calls or [])
102
+ return calls
103
+
104
+ def errored_tool_call_count(self) -> int:
105
+ return sum(_errored_results(step) for step in self._all_steps())
106
+
107
+ def token_totals(self) -> TokenTotals:
108
+ final = self.trajectory.final_metrics
109
+ if final is not None and any(
110
+ v is not None
111
+ for v in (
112
+ final.total_prompt_tokens,
113
+ final.total_completion_tokens,
114
+ final.total_cached_tokens,
115
+ )
116
+ ):
117
+ return TokenTotals(
118
+ input=final.total_prompt_tokens,
119
+ output=final.total_completion_tokens,
120
+ cached=final.total_cached_tokens,
121
+ )
122
+ totals = {"input": None, "output": None, "cached": None}
123
+ keys = {"input": "prompt_tokens", "output": "completion_tokens", "cached": "cached_tokens"}
124
+ for step in self._all_steps():
125
+ if step.metrics is None:
126
+ continue
127
+ for name, attr in keys.items():
128
+ value = getattr(step.metrics, attr)
129
+ if value is not None:
130
+ totals[name] = (totals[name] or 0) + value
131
+ return TokenTotals(**totals)
132
+
133
+ def turn_count(self) -> int:
134
+ return sum(1 for step in self.trajectory.steps if step.source == "agent")
135
+
136
+ def subagent_count(self) -> int:
137
+ return len(self.trajectory.subagent_trajectories or [])
138
+
139
+ def metrics_by_trajectory(self) -> dict[str, TrajectoryMetrics]:
140
+ main = self.trajectory
141
+ split = {"main": _own_metrics(main, main.agent.name if main.agent else "main")}
142
+ for i, sub in enumerate(self.trajectory.subagent_trajectories or []):
143
+ key = sub.trajectory_id or f"subagent_trajectories[{i}]"
144
+ split[key] = _own_metrics(sub, (sub.agent.name if sub.agent else None) or "subagent")
145
+ return split
146
+
147
+ def render_steps(self) -> list[str]:
148
+ lines: list[str] = []
149
+ for step in self._all_steps():
150
+ if step.reasoning_content:
151
+ lines.append(f"[reasoning] {step.reasoning_content}")
152
+ text = _message_text(step.message)
153
+ if text:
154
+ lines.append(f"[{step.source}] {text}")
155
+ results_by_call: dict[str, ObservationResult] = {}
156
+ if step.observation is not None:
157
+ for result in step.observation.results:
158
+ if result.source_call_id:
159
+ results_by_call[result.source_call_id] = result
160
+ for call in step.tool_calls or []:
161
+ args = json.dumps(call.arguments, ensure_ascii=False, default=str)
162
+ line = f"[tool] {call.function_name} args: {args}"
163
+ matched = results_by_call.get(call.tool_call_id)
164
+ if matched is not None:
165
+ line += f" result: {_result_text(matched)}"
166
+ if (matched.extra or {}).get("is_error") is True:
167
+ line += " [ERROR]"
168
+ lines.append(line)
169
+ return [f"{i}. {line}" for i, line in enumerate(lines, 1)]
170
+
171
+ def render(self) -> str:
172
+ numbered = "\n".join(self.render_steps()) or "(none)"
173
+ return f"Recorded steps (in order):\n{numbered}"
174
+
175
+
176
+ def trajectory_model(trajectory: Trajectory) -> str | None:
177
+ if trajectory.agent and trajectory.agent.model_name:
178
+ return trajectory.agent.model_name
179
+ for step in trajectory.steps:
180
+ if step.model_name:
181
+ return step.model_name
182
+ return None
183
+
184
+
185
+ def _price_steps(steps: list[Step], cost_map: dict, default_model: str | None) -> None:
186
+ for step in steps:
187
+ metrics = step.metrics
188
+ # Only fill a gap: a step with token counts but no price. A zero from
189
+ # the harness counts as a gap too: harnesses that lack rates for a
190
+ # model (pi defaults them to 0) report 0.0 rather than omitting it.
191
+ if metrics is None or metrics.cost_usd or metrics.prompt_tokens is None:
192
+ continue
193
+ model = step.model_name or default_model
194
+ if model is None:
195
+ continue
196
+ rates = resolve_rates(cost_map, model)
197
+ if rates is None:
198
+ continue
199
+ metrics.cost_usd = cost_usd(
200
+ rates,
201
+ metrics.prompt_tokens,
202
+ metrics.completion_tokens or 0,
203
+ metrics.cached_tokens or 0,
204
+ )
205
+
206
+
207
+ def backfill_step_costs(trajectory: Trajectory, cost_map: dict, model: str | None = None) -> None:
208
+ # The run's model override is the fallback when the harness didn't record a
209
+ # model on the trajectory; a step's own model_name still wins.
210
+ default_model = model or trajectory_model(trajectory)
211
+ _price_steps(trajectory.steps, cost_map, default_model)
212
+ for sub in trajectory.subagent_trajectories or []:
213
+ _price_steps(sub.steps, cost_map, trajectory_model(sub) or default_model)
214
+
215
+
216
+ def _sum_step_costs(view: TrajectoryView) -> float | None:
217
+ costs = [
218
+ step.metrics.cost_usd
219
+ for step in view._all_steps()
220
+ if step.metrics is not None and step.metrics.cost_usd is not None
221
+ ]
222
+ return round(sum(costs), 10) if costs else None
223
+
224
+
225
+ def derive_metrics(view: TrajectoryView, wall_ms: int | None) -> RunMetrics:
226
+ totals = view.token_totals()
227
+ final = view.trajectory.final_metrics
228
+ # Prefer a harness-reported total; otherwise sum the per-step costs.
229
+ # backfill_step_costs prices any step the harness left unset before the
230
+ # trajectory is stored, so the gauge and the UI cost view read the same
231
+ # per-step numbers.
232
+ cost = (
233
+ final.total_cost_usd
234
+ if (final is not None and final.total_cost_usd)
235
+ else _sum_step_costs(view)
236
+ )
237
+ return RunMetrics(
238
+ duration_ms=wall_ms,
239
+ input_tokens=totals.input,
240
+ output_tokens=totals.output,
241
+ cached_tokens=totals.cached,
242
+ cost_usd=cost,
243
+ total_tool_calls=len(view.tool_calls()),
244
+ errored_tool_calls=view.errored_tool_call_count(),
245
+ subagents=view.subagent_count(),
246
+ turns=view.turn_count(),
247
+ by_trajectory=view.metrics_by_trajectory(),
248
+ )
@@ -0,0 +1,30 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import NamedTuple
4
+
5
+
6
+ class Reference(NamedTuple):
7
+ name: str
8
+ workspace: str
9
+
10
+
11
+ class BareReference(ValueError):
12
+ def __init__(self, text: str):
13
+ super().__init__(f"use name@workspace, not {text!r}")
14
+
15
+
16
+ def parse_reference(text: str, default_workspace: str) -> Reference:
17
+ name, sep, workspace = text.partition("@")
18
+ if not name or (sep and (not workspace or "@" in workspace)):
19
+ raise ValueError(f"malformed reference: {text!r}")
20
+ return Reference(name, workspace if sep else default_workspace)
21
+
22
+
23
+ def parse_slug(value: str) -> Reference:
24
+ if "@" not in value:
25
+ raise BareReference(value)
26
+ return parse_reference(value, "")
27
+
28
+
29
+ def slug(ref: Reference) -> str:
30
+ return f"{ref.name}@{ref.workspace}"
evalkit/credentials.py ADDED
@@ -0,0 +1,119 @@
1
+ import os
2
+ import re
3
+ from collections.abc import Mapping
4
+ from pathlib import Path
5
+ from typing import Literal, Protocol
6
+
7
+ from dotenv import dotenv_values
8
+ from pydantic import BaseModel, ConfigDict, field_validator, model_validator
9
+
10
+ Kind = Literal["agent", "evaluator", "llm_provider"]
11
+
12
+
13
+ class MissingCredential(ValueError):
14
+ pass
15
+
16
+
17
+ ENV_REF = re.compile(r"^env:[A-Za-z_][A-Za-z0-9_]*$")
18
+
19
+
20
+ class CredentialsConfig(BaseModel):
21
+ model_config = ConfigDict(extra="forbid", hide_input_in_errors=True)
22
+
23
+ agents: dict[str, str] = {}
24
+ evaluators: dict[str, str] = {}
25
+ llm_providers: dict[str, str] = {}
26
+
27
+ # TODO: remove in 0.5.0, two minor versions after 0.3.0 renamed credentials.judges
28
+ # to credentials.llm_providers.
29
+ @model_validator(mode="before")
30
+ @classmethod
31
+ def _judges_renamed(cls, data: object) -> object:
32
+ if isinstance(data, dict) and "judges" in data:
33
+ raise ValueError("credentials.judges is now credentials.llm_providers")
34
+ return data
35
+
36
+ @field_validator("agents", "evaluators", "llm_providers")
37
+ @classmethod
38
+ def _env_refs_only(cls, value: dict[str, str]) -> dict[str, str]:
39
+ bad = [k for k, v in value.items() if not ENV_REF.match(v)]
40
+ if bad:
41
+ raise ValueError(f"credentials must be env:NAME references (no defaults): {bad}")
42
+ return value
43
+
44
+ @field_validator("llm_providers")
45
+ @classmethod
46
+ def _provider_keys_are_urls_or_env_refs(cls, value: dict[str, str]) -> dict[str, str]:
47
+ bad = [k for k in value if k.startswith("env:") and not ENV_REF.match(k)]
48
+ if bad:
49
+ raise ValueError(f"a credentials.llm_providers key is a base URL or env:NAME: {bad}")
50
+ return value
51
+
52
+
53
+ class KeySource(Protocol):
54
+ def key_for(self, kind: Kind, ref: str, url: str | None = None) -> str | None: ...
55
+
56
+ def require_judge_key(self, base_url: str) -> str | None: ...
57
+
58
+
59
+ class CredentialRegistry:
60
+ def __init__(
61
+ self, cfg: CredentialsConfig, environ: Mapping[str, str], *, home: str | None = None
62
+ ):
63
+ self._cfg = cfg
64
+ self._env = environ
65
+ self._home = home
66
+
67
+ def _table(self, kind: Kind) -> dict[str, str]:
68
+ if kind == "llm_provider":
69
+ return {
70
+ url: v
71
+ for k, v in self._cfg.llm_providers.items()
72
+ if (url := self._judge_url(k)) is not None
73
+ }
74
+ return {"agent": self._cfg.agents, "evaluator": self._cfg.evaluators}[kind]
75
+
76
+ def _judge_url(self, key: str) -> str | None:
77
+ if not key.startswith("env:"):
78
+ return key
79
+ return self._env.get(key.removeprefix("env:"))
80
+
81
+ def _unset_judge_keys(self) -> list[str]:
82
+ return [
83
+ k
84
+ for k in self._cfg.llm_providers
85
+ if k.startswith("env:") and self._judge_url(k) is None
86
+ ]
87
+
88
+ def _entry(self, kind: Kind, ref: str) -> str | None:
89
+ table = self._table(kind)
90
+ if ref in table:
91
+ return table[ref]
92
+ name, _, workspace = ref.partition("@")
93
+ if kind != "llm_provider" and self._home is not None and workspace == self._home:
94
+ return table.get(name)
95
+ return None
96
+
97
+ def key_for(self, kind: Kind, ref: str, url: str | None = None) -> str | None:
98
+ entry = self._entry(kind, ref)
99
+ if entry is None:
100
+ return None
101
+ name = entry.removeprefix("env:")
102
+ if name not in self._env:
103
+ raise MissingCredential(f"credentials.{kind}s[{ref!r}] needs env var {name}")
104
+ return self._env[name]
105
+
106
+ def require_judge_key(self, base_url: str) -> str:
107
+ key = self.key_for("llm_provider", base_url)
108
+ if key is None:
109
+ unset = self._unset_judge_keys()
110
+ hint = f" (unset: {', '.join(k.removeprefix('env:') for k in unset)})" if unset else ""
111
+ raise MissingCredential(f"no credentials.llm_providers entry for {base_url}{hint}")
112
+ return key
113
+
114
+
115
+ def load_dotenv_beside(config_path: Path) -> dict[str, str]:
116
+ file_values = {
117
+ k: v for k, v in dotenv_values(config_path.parent / ".env").items() if v is not None
118
+ }
119
+ return {**file_values, **os.environ}
evalkit/db/__init__.py ADDED
File without changes