orizu 0.5.19 → 0.5.21

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 (378) hide show
  1. package/README.md +23 -0
  2. package/dist/archive-artifacts-cli.d.ts +50 -0
  3. package/dist/archive-artifacts-cli.d.ts.map +1 -0
  4. package/dist/artifact-archive.d.ts +48 -0
  5. package/dist/artifact-archive.d.ts.map +1 -0
  6. package/dist/artifact-pull.d.ts +22 -0
  7. package/dist/artifact-pull.d.ts.map +1 -0
  8. package/dist/banner.d.ts +3 -0
  9. package/dist/banner.d.ts.map +1 -0
  10. package/dist/connectors-cli.d.ts +31 -0
  11. package/dist/connectors-cli.d.ts.map +1 -0
  12. package/dist/credentials.d.ts +46 -0
  13. package/dist/credentials.d.ts.map +1 -0
  14. package/dist/csv.d.ts +49 -0
  15. package/dist/csv.d.ts.map +1 -0
  16. package/dist/dataset-download.d.ts +6 -0
  17. package/dist/dataset-download.d.ts.map +1 -0
  18. package/dist/dataset-edit-rows.d.ts +10 -0
  19. package/dist/dataset-edit-rows.d.ts.map +1 -0
  20. package/dist/dataset-upload-snapshot.d.ts +13 -0
  21. package/dist/dataset-upload-snapshot.d.ts.map +1 -0
  22. package/dist/daytona-slice-rehearsal.d.ts +89 -0
  23. package/dist/daytona-slice-rehearsal.d.ts.map +1 -0
  24. package/dist/daytona-slice.d.ts +106 -0
  25. package/dist/daytona-slice.d.ts.map +1 -0
  26. package/dist/deploy-key-merge-core.d.ts +121 -0
  27. package/dist/deploy-key-merge-core.d.ts.map +1 -0
  28. package/dist/diff-comments-cli.d.ts +12 -0
  29. package/dist/diff-comments-cli.d.ts.map +1 -0
  30. package/dist/diff-comments-cli.js +21 -2
  31. package/dist/egress-allowlist-cli.d.ts +23 -0
  32. package/dist/egress-allowlist-cli.d.ts.map +1 -0
  33. package/dist/egress-policy.d.ts +108 -0
  34. package/dist/egress-policy.d.ts.map +1 -0
  35. package/dist/error-response.d.ts +9 -0
  36. package/dist/error-response.d.ts.map +1 -0
  37. package/dist/file-parser.d.ts +5 -0
  38. package/dist/file-parser.d.ts.map +1 -0
  39. package/dist/gepa-engine-dispatch.d.ts +9 -0
  40. package/dist/gepa-engine-dispatch.d.ts.map +1 -0
  41. package/dist/gepa-engine-dispatch.js +307 -0
  42. package/dist/gepa-python-command.d.ts +6 -0
  43. package/dist/gepa-python-command.d.ts.map +1 -0
  44. package/dist/gepa-python-command.js +26 -0
  45. package/dist/gepa-python-paths.d.ts +6 -0
  46. package/dist/gepa-python-paths.d.ts.map +1 -0
  47. package/dist/gepa-python-paths.js +42 -0
  48. package/dist/git-credential.d.ts +69 -0
  49. package/dist/git-credential.d.ts.map +1 -0
  50. package/dist/git-recovery-environment.d.ts +31 -0
  51. package/dist/git-recovery-environment.d.ts.map +1 -0
  52. package/dist/github-setup.d.ts +122 -0
  53. package/dist/github-setup.d.ts.map +1 -0
  54. package/dist/global-flags.d.ts +12 -0
  55. package/dist/global-flags.d.ts.map +1 -0
  56. package/dist/help.d.ts +46 -0
  57. package/dist/help.d.ts.map +1 -0
  58. package/dist/help.js +93 -25
  59. package/dist/hosted-boot.d.ts +273 -0
  60. package/dist/hosted-boot.d.ts.map +1 -0
  61. package/dist/hosted-bootstrap-rehearsal.d.ts +86 -0
  62. package/dist/hosted-bootstrap-rehearsal.d.ts.map +1 -0
  63. package/dist/hosted-bootstrap.d.ts +188 -0
  64. package/dist/hosted-bootstrap.d.ts.map +1 -0
  65. package/dist/hosted-broker-server.d.ts +50 -0
  66. package/dist/hosted-broker-server.d.ts.map +1 -0
  67. package/dist/hosted-git-http-server.d.ts +36 -0
  68. package/dist/hosted-git-http-server.d.ts.map +1 -0
  69. package/dist/hosted-harness-claude.d.ts +195 -0
  70. package/dist/hosted-harness-claude.d.ts.map +1 -0
  71. package/dist/hosted-harness-opencode.d.ts +227 -0
  72. package/dist/hosted-harness-opencode.d.ts.map +1 -0
  73. package/dist/hosted-harness.d.ts +118 -0
  74. package/dist/hosted-harness.d.ts.map +1 -0
  75. package/dist/hosted-harvest.d.ts +68 -0
  76. package/dist/hosted-harvest.d.ts.map +1 -0
  77. package/dist/hosted-headless.d.ts +72 -0
  78. package/dist/hosted-headless.d.ts.map +1 -0
  79. package/dist/hosted-loop-lifecycle.d.ts +206 -0
  80. package/dist/hosted-loop-lifecycle.d.ts.map +1 -0
  81. package/dist/hosted-loop.d.ts +170 -0
  82. package/dist/hosted-loop.d.ts.map +1 -0
  83. package/dist/hosted-run-event-sink.d.ts +222 -0
  84. package/dist/hosted-run-event-sink.d.ts.map +1 -0
  85. package/dist/hosted-runtime-assets.d.ts +186 -0
  86. package/dist/hosted-runtime-assets.d.ts.map +1 -0
  87. package/dist/hosted-session-cli.d.ts +258 -0
  88. package/dist/hosted-session-cli.d.ts.map +1 -0
  89. package/dist/hosted-skill-staging.d.ts +73 -0
  90. package/dist/hosted-skill-staging.d.ts.map +1 -0
  91. package/dist/http.d.ts +8 -0
  92. package/dist/http.d.ts.map +1 -0
  93. package/dist/index.d.ts +22 -0
  94. package/dist/index.d.ts.map +1 -0
  95. package/dist/index.js +119 -129
  96. package/dist/instruction-set-gepa-launch.d.ts +3 -0
  97. package/dist/instruction-set-gepa-launch.d.ts.map +1 -0
  98. package/dist/instruction-set-gepa-launch.js +15 -0
  99. package/dist/instruction-set-loader.d.ts +6 -0
  100. package/dist/instruction-set-loader.d.ts.map +1 -0
  101. package/dist/instruction-set-loader.js +59 -0
  102. package/dist/instruction-set-manifest.d.ts +14 -0
  103. package/dist/instruction-set-manifest.d.ts.map +1 -0
  104. package/dist/instruction-set-manifest.js +162 -0
  105. package/dist/instruction-sets-cli.d.ts +40 -0
  106. package/dist/instruction-sets-cli.d.ts.map +1 -0
  107. package/dist/instruction-sets-cli.js +361 -0
  108. package/dist/json-response.d.ts +4 -0
  109. package/dist/json-response.d.ts.map +1 -0
  110. package/dist/jsonl-stream.d.ts +8 -0
  111. package/dist/jsonl-stream.d.ts.map +1 -0
  112. package/dist/kill-switch-cli.d.ts +30 -0
  113. package/dist/kill-switch-cli.d.ts.map +1 -0
  114. package/dist/loopback-serve.d.ts +25 -0
  115. package/dist/loopback-serve.d.ts.map +1 -0
  116. package/dist/manifests-cli.d.ts +22 -0
  117. package/dist/manifests-cli.d.ts.map +1 -0
  118. package/dist/markdown-report-input.d.ts +7 -0
  119. package/dist/markdown-report-input.d.ts.map +1 -0
  120. package/dist/markdown-report-input.js +15 -1
  121. package/dist/merge-job.d.ts +155 -0
  122. package/dist/merge-job.d.ts.map +1 -0
  123. package/dist/model-configs-cli.d.ts +19 -0
  124. package/dist/model-configs-cli.d.ts.map +1 -0
  125. package/dist/model-configs-cli.js +104 -0
  126. package/dist/optimization-export-cli.d.ts +19 -0
  127. package/dist/optimization-export-cli.d.ts.map +1 -0
  128. package/dist/optimizations-list-cli.d.ts +20 -0
  129. package/dist/optimizations-list-cli.d.ts.map +1 -0
  130. package/dist/optimizations-promote-cli.d.ts +8 -0
  131. package/dist/optimizations-promote-cli.d.ts.map +1 -0
  132. package/dist/optimizations-promote-cli.js +34 -0
  133. package/dist/option-validation.d.ts +15 -0
  134. package/dist/option-validation.d.ts.map +1 -0
  135. package/dist/preview-runtime.d.ts +63 -0
  136. package/dist/preview-runtime.d.ts.map +1 -0
  137. package/dist/prompt-draft-push.d.ts +60 -0
  138. package/dist/prompt-draft-push.d.ts.map +1 -0
  139. package/dist/prompt-label-cli.d.ts +11 -0
  140. package/dist/prompt-label-cli.d.ts.map +1 -0
  141. package/dist/prompt-label-cli.js +29 -0
  142. package/dist/prompt-length-wire.d.ts +28 -0
  143. package/dist/prompt-length-wire.d.ts.map +1 -0
  144. package/dist/prompt-length-wire.js +2 -2
  145. package/dist/prompt-report-cli.d.ts +10 -0
  146. package/dist/prompt-report-cli.d.ts.map +1 -0
  147. package/dist/prompt-report-cli.js +2 -2
  148. package/dist/prompt-summary-table.d.ts +15 -0
  149. package/dist/prompt-summary-table.d.ts.map +1 -0
  150. package/dist/prompt-summary-table.js +6 -0
  151. package/dist/registration-tag-warning.d.ts +32 -0
  152. package/dist/registration-tag-warning.d.ts.map +1 -0
  153. package/dist/report-comments-cli.d.ts +16 -0
  154. package/dist/report-comments-cli.d.ts.map +1 -0
  155. package/dist/report-markdown-blankness.d.ts +2 -0
  156. package/dist/report-markdown-blankness.d.ts.map +1 -0
  157. package/dist/report-markdown-blankness.js +6 -0
  158. package/dist/report-replacement-warning.d.ts +17 -0
  159. package/dist/report-replacement-warning.d.ts.map +1 -0
  160. package/dist/report-replacement-warning.js +31 -0
  161. package/dist/repository-backup-core.d.ts +225 -0
  162. package/dist/repository-backup-core.d.ts.map +1 -0
  163. package/dist/repository-recovery-manifest.d.ts +83 -0
  164. package/dist/repository-recovery-manifest.d.ts.map +1 -0
  165. package/dist/run-event-digest.d.ts +74 -0
  166. package/dist/run-event-digest.d.ts.map +1 -0
  167. package/dist/runner-dir-verify.d.ts +66 -0
  168. package/dist/runner-dir-verify.d.ts.map +1 -0
  169. package/dist/runner-instruction-set-contract.d.ts +24 -0
  170. package/dist/runner-instruction-set-contract.d.ts.map +1 -0
  171. package/dist/runner-instruction-set-contract.js +50 -0
  172. package/dist/sandbox-hygiene.d.ts +45 -0
  173. package/dist/sandbox-hygiene.d.ts.map +1 -0
  174. package/dist/sandbox-provider.d.ts +193 -0
  175. package/dist/sandbox-provider.d.ts.map +1 -0
  176. package/dist/scorer-draft-push.d.ts +50 -0
  177. package/dist/scorer-draft-push.d.ts.map +1 -0
  178. package/dist/scorers-cli.d.ts +20 -0
  179. package/dist/scorers-cli.d.ts.map +1 -0
  180. package/dist/scorers-cli.js +22 -0
  181. package/dist/scorers-verify-parity-bridge.d.ts +16 -0
  182. package/dist/scorers-verify-parity-bridge.d.ts.map +1 -0
  183. package/dist/scorers-verify-parity-bridge.js +90 -0
  184. package/dist/scorers-verify-parity.d.ts +81 -0
  185. package/dist/scorers-verify-parity.d.ts.map +1 -0
  186. package/dist/scorers-verify-parity.js +502 -0
  187. package/dist/scores-accept-cli.d.ts +21 -0
  188. package/dist/scores-accept-cli.d.ts.map +1 -0
  189. package/dist/secret-redaction.d.ts +47 -0
  190. package/dist/secret-redaction.d.ts.map +1 -0
  191. package/dist/session-timeline.d.ts +54 -0
  192. package/dist/session-timeline.d.ts.map +1 -0
  193. package/dist/setup-prompt.d.ts +5 -0
  194. package/dist/setup-prompt.d.ts.map +1 -0
  195. package/dist/skill-installer.d.ts +68 -0
  196. package/dist/skill-installer.d.ts.map +1 -0
  197. package/dist/skilled-proposer-launch.d.ts +7 -0
  198. package/dist/skilled-proposer-launch.d.ts.map +1 -0
  199. package/dist/skilled-proposer-launch.js +60 -0
  200. package/dist/smoke.d.ts +102 -0
  201. package/dist/smoke.d.ts.map +1 -0
  202. package/dist/start-intent.d.ts +12 -0
  203. package/dist/start-intent.d.ts.map +1 -0
  204. package/dist/task-assignment-manifest.d.ts +15 -0
  205. package/dist/task-assignment-manifest.d.ts.map +1 -0
  206. package/dist/task-create-error.d.ts +18 -0
  207. package/dist/task-create-error.d.ts.map +1 -0
  208. package/dist/team-connectors-cli.d.ts +29 -0
  209. package/dist/team-connectors-cli.d.ts.map +1 -0
  210. package/dist/types.d.ts +39 -0
  211. package/dist/types.d.ts.map +1 -0
  212. package/dist/vercel-sandbox-provider.d.ts +156 -0
  213. package/dist/vercel-sandbox-provider.d.ts.map +1 -0
  214. package/dist/workbench-cli.d.ts +233 -0
  215. package/dist/workbench-cli.d.ts.map +1 -0
  216. package/dist/workspace-sync.d.ts +207 -0
  217. package/dist/workspace-sync.d.ts.map +1 -0
  218. package/dist/workspace.d.ts +92 -0
  219. package/dist/workspace.d.ts.map +1 -0
  220. package/dist/zip-draft-push.d.ts +55 -0
  221. package/dist/zip-draft-push.d.ts.map +1 -0
  222. package/package.json +15 -2
  223. package/requirements/README.md +138 -0
  224. package/requirements/skilled-proposer.in +66 -0
  225. package/requirements/skilled-proposer.lock +265 -0
  226. package/scripts/build-cli-dist.mjs +30 -0
  227. package/scripts/ensure-skilled-proposer-venv.mjs +5 -0
  228. package/scripts/orizu-source-checkout.d.mts +1 -0
  229. package/scripts/orizu-source-checkout.mjs +10 -0
  230. package/scripts/refresh-skilled-proposer-lock.mjs +256 -0
  231. package/scripts/vendor-gepa-python.mjs +187 -4
  232. package/src/diff-comments-cli.ts +41 -3
  233. package/src/gepa-engine-dispatch.ts +340 -0
  234. package/src/gepa-python-command.ts +31 -0
  235. package/src/gepa-python-paths.ts +56 -0
  236. package/src/help.ts +95 -25
  237. package/src/index.ts +131 -166
  238. package/src/instruction-set-gepa-launch.ts +28 -0
  239. package/src/instruction-set-loader.ts +37 -0
  240. package/src/instruction-set-manifest.ts +120 -0
  241. package/src/instruction-sets-cli.ts +306 -0
  242. package/src/markdown-report-input.ts +19 -1
  243. package/src/model-configs-cli.ts +119 -0
  244. package/src/optimizations-promote-cli.ts +41 -0
  245. package/src/prompt-label-cli.ts +55 -0
  246. package/src/prompt-length-wire.ts +1 -1
  247. package/src/prompt-report-cli.ts +2 -2
  248. package/src/prompt-summary-table.ts +12 -0
  249. package/src/report-markdown-blankness.ts +6 -0
  250. package/src/report-replacement-warning.ts +35 -0
  251. package/src/runner-instruction-set-contract.ts +64 -0
  252. package/src/scorers-cli.ts +40 -0
  253. package/src/scorers-verify-parity-bridge.ts +90 -0
  254. package/src/scorers-verify-parity.ts +564 -0
  255. package/src/skilled-proposer-launch.ts +89 -0
  256. package/src/skilled-proposer-venv-manager.mjs +757 -0
  257. package/tsconfig.json +2 -0
  258. package/vendor/gepa-python/LICENSE +7 -0
  259. package/vendor/gepa-python/manifest.json +107 -0
  260. package/vendor/gepa-python/src/gepa/__init__.py +22 -0
  261. package/vendor/gepa-python/src/gepa/adapters/__init__.py +0 -0
  262. package/vendor/gepa-python/src/gepa/adapters/anymaths_adapter/__init__.py +1 -0
  263. package/vendor/gepa-python/src/gepa/adapters/anymaths_adapter/anymaths_adapter.py +174 -0
  264. package/vendor/gepa-python/src/gepa/adapters/confidence_adapter/__init__.py +26 -0
  265. package/vendor/gepa-python/src/gepa/adapters/confidence_adapter/confidence_adapter.py +618 -0
  266. package/vendor/gepa-python/src/gepa/adapters/confidence_adapter/scoring.py +206 -0
  267. package/vendor/gepa-python/src/gepa/adapters/default_adapter/__init__.py +0 -0
  268. package/vendor/gepa-python/src/gepa/adapters/default_adapter/default_adapter.py +206 -0
  269. package/vendor/gepa-python/src/gepa/adapters/dspy_adapter/__init__.py +0 -0
  270. package/vendor/gepa-python/src/gepa/adapters/dspy_adapter/dspy_adapter.py +525 -0
  271. package/vendor/gepa-python/src/gepa/adapters/dspy_adapter/instruction_proposal.py +500 -0
  272. package/vendor/gepa-python/src/gepa/adapters/dspy_full_program_adapter/__init__.py +6 -0
  273. package/vendor/gepa-python/src/gepa/adapters/dspy_full_program_adapter/dspy_program_proposal_signature.py +137 -0
  274. package/vendor/gepa-python/src/gepa/adapters/dspy_full_program_adapter/full_program_adapter.py +269 -0
  275. package/vendor/gepa-python/src/gepa/adapters/generic_rag_adapter/__init__.py +56 -0
  276. package/vendor/gepa-python/src/gepa/adapters/generic_rag_adapter/evaluation_metrics.py +226 -0
  277. package/vendor/gepa-python/src/gepa/adapters/generic_rag_adapter/generic_rag_adapter.py +496 -0
  278. package/vendor/gepa-python/src/gepa/adapters/generic_rag_adapter/rag_pipeline.py +238 -0
  279. package/vendor/gepa-python/src/gepa/adapters/generic_rag_adapter/vector_store_interface.py +212 -0
  280. package/vendor/gepa-python/src/gepa/adapters/generic_rag_adapter/vector_stores/__init__.py +2 -0
  281. package/vendor/gepa-python/src/gepa/adapters/generic_rag_adapter/vector_stores/chroma_store.py +196 -0
  282. package/vendor/gepa-python/src/gepa/adapters/generic_rag_adapter/vector_stores/lancedb_store.py +422 -0
  283. package/vendor/gepa-python/src/gepa/adapters/generic_rag_adapter/vector_stores/milvus_store.py +409 -0
  284. package/vendor/gepa-python/src/gepa/adapters/generic_rag_adapter/vector_stores/qdrant_store.py +368 -0
  285. package/vendor/gepa-python/src/gepa/adapters/generic_rag_adapter/vector_stores/weaviate_store.py +418 -0
  286. package/vendor/gepa-python/src/gepa/adapters/langchain_adapter/__init__.py +11 -0
  287. package/vendor/gepa-python/src/gepa/adapters/langchain_adapter/langchain_adapter.py +261 -0
  288. package/vendor/gepa-python/src/gepa/adapters/mcp_adapter/__init__.py +37 -0
  289. package/vendor/gepa-python/src/gepa/adapters/mcp_adapter/mcp_adapter.py +693 -0
  290. package/vendor/gepa-python/src/gepa/adapters/mcp_adapter/mcp_client.py +364 -0
  291. package/vendor/gepa-python/src/gepa/adapters/optimize_anything_adapter/optimize_anything_adapter.py +879 -0
  292. package/vendor/gepa-python/src/gepa/adapters/terminal_bench_adapter/__init__.py +0 -0
  293. package/vendor/gepa-python/src/gepa/adapters/terminal_bench_adapter/terminal_bench_adapter.py +217 -0
  294. package/vendor/gepa-python/src/gepa/api.py +468 -0
  295. package/vendor/gepa-python/src/gepa/core/__init__.py +0 -0
  296. package/vendor/gepa-python/src/gepa/core/adapter.py +227 -0
  297. package/vendor/gepa-python/src/gepa/core/callbacks.py +551 -0
  298. package/vendor/gepa-python/src/gepa/core/data_loader.py +74 -0
  299. package/vendor/gepa-python/src/gepa/core/engine.py +1025 -0
  300. package/vendor/gepa-python/src/gepa/core/result.py +271 -0
  301. package/vendor/gepa-python/src/gepa/core/state.py +706 -0
  302. package/vendor/gepa-python/src/gepa/examples/__init__.py +0 -0
  303. package/vendor/gepa-python/src/gepa/examples/aime.py +24 -0
  304. package/vendor/gepa-python/src/gepa/examples/anymaths-bench/eval_default.py +111 -0
  305. package/vendor/gepa-python/src/gepa/examples/anymaths-bench/train_anymaths.py +177 -0
  306. package/vendor/gepa-python/src/gepa/examples/mcp_adapter/__init__.py +4 -0
  307. package/vendor/gepa-python/src/gepa/examples/mcp_adapter/mcp_optimization_example.py +456 -0
  308. package/vendor/gepa-python/src/gepa/examples/rag_adapter/__init__.py +9 -0
  309. package/vendor/gepa-python/src/gepa/examples/rag_adapter/rag_optimization.py +824 -0
  310. package/vendor/gepa-python/src/gepa/examples/terminal-bench/train_terminus.py +161 -0
  311. package/vendor/gepa-python/src/gepa/gepa_utils.py +117 -0
  312. package/vendor/gepa-python/src/gepa/gskill/__init__.py +1 -0
  313. package/vendor/gepa-python/src/gepa/gskill/gskill/__init__.py +0 -0
  314. package/vendor/gepa-python/src/gepa/gskill/gskill/cost_tracker.py +194 -0
  315. package/vendor/gepa-python/src/gepa/gskill/gskill/evaluate/__init__.py +0 -0
  316. package/vendor/gepa-python/src/gepa/gskill/gskill/evaluate/claude_code.py +1117 -0
  317. package/vendor/gepa-python/src/gepa/gskill/gskill/evaluate/claude_code_skills.py +613 -0
  318. package/vendor/gepa-python/src/gepa/gskill/gskill/evaluate/mini_swe_agent.py +440 -0
  319. package/vendor/gepa-python/src/gepa/gskill/gskill/experiment_logger.py +367 -0
  320. package/vendor/gepa-python/src/gepa/gskill/gskill/swe_fitness_fn.py +209 -0
  321. package/vendor/gepa-python/src/gepa/gskill/gskill/swe_harness.py +352 -0
  322. package/vendor/gepa-python/src/gepa/gskill/gskill/train_optimize_anything.py +830 -0
  323. package/vendor/gepa-python/src/gepa/image.py +104 -0
  324. package/vendor/gepa-python/src/gepa/lm.py +261 -0
  325. package/vendor/gepa-python/src/gepa/logging/__init__.py +0 -0
  326. package/vendor/gepa-python/src/gepa/logging/experiment_tracker.py +457 -0
  327. package/vendor/gepa-python/src/gepa/logging/logger.py +75 -0
  328. package/vendor/gepa-python/src/gepa/logging/utils.py +130 -0
  329. package/vendor/gepa-python/src/gepa/optimize_anything.py +1701 -0
  330. package/vendor/gepa-python/src/gepa/proposer/__init__.py +0 -0
  331. package/vendor/gepa-python/src/gepa/proposer/base.py +53 -0
  332. package/vendor/gepa-python/src/gepa/proposer/merge.py +403 -0
  333. package/vendor/gepa-python/src/gepa/proposer/reflective_mutation/__init__.py +0 -0
  334. package/vendor/gepa-python/src/gepa/proposer/reflective_mutation/base.py +64 -0
  335. package/vendor/gepa-python/src/gepa/proposer/reflective_mutation/reflection_lm.py +178 -0
  336. package/vendor/gepa-python/src/gepa/proposer/reflective_mutation/reflective_mutation.py +641 -0
  337. package/vendor/gepa-python/src/gepa/py.typed +0 -0
  338. package/vendor/gepa-python/src/gepa/strategies/__init__.py +0 -0
  339. package/vendor/gepa-python/src/gepa/strategies/acceptance.py +66 -0
  340. package/vendor/gepa-python/src/gepa/strategies/batch_sampler.py +103 -0
  341. package/vendor/gepa-python/src/gepa/strategies/candidate_selector.py +82 -0
  342. package/vendor/gepa-python/src/gepa/strategies/component_selector.py +36 -0
  343. package/vendor/gepa-python/src/gepa/strategies/eval_policy.py +64 -0
  344. package/vendor/gepa-python/src/gepa/strategies/instruction_proposal.py +153 -0
  345. package/vendor/gepa-python/src/gepa/strategies/proposal_sampling.py +128 -0
  346. package/vendor/gepa-python/src/gepa/strategies/proposal_selection.py +84 -0
  347. package/vendor/gepa-python/src/gepa/utils/__init__.py +63 -0
  348. package/vendor/gepa-python/src/gepa/utils/code_execution.py +626 -0
  349. package/vendor/gepa-python/src/gepa/utils/stdio_capture.py +145 -0
  350. package/vendor/gepa-python/src/gepa/utils/stop_condition.py +230 -0
  351. package/vendor/gepa-python/src/gepa/visualization.py +359 -0
  352. package/vendor/orizu-gepa/pyproject.toml +18 -0
  353. package/vendor/orizu-gepa/src/orizu_gepa_connector/__init__.py +9 -0
  354. package/vendor/orizu-gepa/src/orizu_gepa_connector/__main__.py +5 -0
  355. package/vendor/orizu-gepa/src/orizu_gepa_connector/adapter.py +138 -0
  356. package/vendor/orizu-gepa/src/orizu_gepa_connector/callbacks.py +666 -0
  357. package/vendor/orizu-gepa/src/orizu_gepa_connector/cli.py +7 -0
  358. package/vendor/orizu-gepa/src/orizu_gepa_connector/engine.py +269 -0
  359. package/vendor/orizu-gepa/src/orizu_gepa_connector/instruction_set_loader.py +52 -0
  360. package/vendor/orizu-gepa/src/orizu_gepa_connector/preflight.py +64 -0
  361. package/vendor/orizu-gepa/src/orizu_gepa_connector/reflection.py +186 -0
  362. package/vendor/orizu-gepa/src/orizu_gepa_connector/runtime.py +721 -0
  363. package/vendor/orizu-gepa/src/orizu_gepa_connector/skilled_proposer_bridge.py +381 -0
  364. package/vendor/orizu-gepa/src/orizu_gepa_connector/stop_conditions.py +55 -0
  365. package/vendor/orizu-gepa/src/orizu_gepa_connector/translator.py +203 -0
  366. package/vendor/orizu-gepa-python/pyproject.toml +1 -1
  367. package/vendor/orizu-gepa-python/src/orizu_gepa/cli.py +1 -0
  368. package/vendor/orizu-gepa-python/src/orizu_gepa/client.py +52 -7
  369. package/vendor/orizu-gepa-python/src/orizu_gepa/local_log.py +52 -0
  370. package/vendor/orizu-gepa-python/src/orizu_gepa/optimizer.py +10 -5
  371. package/vendor/orizu-gepa-python/src/orizu_gepa/reflection.py +323 -68
  372. package/vendor/orizu-gepa-python/src/orizu_gepa/runner.py +207 -13
  373. package/vendor/skills/orizu-cli/SKILL.md +3 -1
  374. package/vendor/skills/orizu-cli/references/cli-reference.md +85 -1
  375. package/vendor/skills/orizu-cli/references/migrate-existing-gepa-setup.md +303 -0
  376. package/vendor/skills/orizu-cli/references/optimization-reports.md +1 -1
  377. package/vendor/skills/orizu-cli/references/optimization-with-gepa.md +26 -1
  378. package/vendor/skills/orizu-cli/references/prompt-control-plane.md +113 -10
@@ -0,0 +1,706 @@
1
+ # Copyright (c) 2025 Lakshya A Agrawal and the GEPA contributors
2
+ # https://github.com/gepa-ai/gepa
3
+
4
+ import hashlib
5
+ import json
6
+ import os
7
+ from collections import defaultdict
8
+ from collections.abc import Callable, Sequence
9
+ from dataclasses import dataclass, field
10
+ from typing import Any, ClassVar, Generic, Literal, TypeAlias
11
+
12
+ from gepa.core.adapter import RolloutOutput
13
+ from gepa.core.data_loader import DataId
14
+ from gepa.gepa_utils import json_default
15
+ from gepa.logging.logger import LoggerProtocol
16
+
17
+ # Types for GEPAState
18
+ ProgramIdx = int
19
+
20
+ # Type aliases
21
+ ObjectiveScores: TypeAlias = dict[str, float]
22
+ FrontierType: TypeAlias = Literal["instance", "objective", "hybrid", "cartesian"]
23
+ """Strategy for tracking Pareto frontiers: 'instance' (per validation example), 'objective' (per objective metric), 'hybrid' (both), or 'cartesian' (per example x objective)."""
24
+ FrontierKey: TypeAlias = DataId | str | tuple[str, DataId] | tuple[str, DataId, str]
25
+ """Key type for frontier mappings depending on frontier_type."""
26
+
27
+ CandidateHash: TypeAlias = str
28
+ CacheKey: TypeAlias = tuple[CandidateHash, DataId]
29
+
30
+
31
+ def _candidate_hash(candidate: dict[str, str]) -> CandidateHash:
32
+ """Compute a deterministic hash of a candidate dictionary."""
33
+ return hashlib.sha256(json.dumps(sorted(candidate.items())).encode()).hexdigest()
34
+
35
+
36
+ @dataclass
37
+ class CachedEvaluation(Generic[RolloutOutput]):
38
+ """Cached evaluation result for a (candidate, example) pair."""
39
+
40
+ output: RolloutOutput
41
+ score: float
42
+ objective_scores: ObjectiveScores | None
43
+
44
+
45
+ @dataclass
46
+ class EvaluationCache(Generic[RolloutOutput, DataId]):
47
+ """Cache for storing evaluation results of (candidate, example) pairs."""
48
+
49
+ _cache: dict[CacheKey, CachedEvaluation[RolloutOutput]] = field(default_factory=dict)
50
+
51
+ def get(self, candidate: dict[str, str], example_id: DataId) -> CachedEvaluation[RolloutOutput] | None:
52
+ """Retrieve cached evaluation result if it exists."""
53
+ return self._cache.get((_candidate_hash(candidate), example_id))
54
+
55
+ def put(
56
+ self,
57
+ candidate: dict[str, str],
58
+ example_id: DataId,
59
+ output: RolloutOutput,
60
+ score: float,
61
+ objective_scores: ObjectiveScores | None = None,
62
+ ) -> None:
63
+ """Store an evaluation result in the cache."""
64
+ self._cache[(_candidate_hash(candidate), example_id)] = CachedEvaluation(output, score, objective_scores)
65
+
66
+ def get_batch(
67
+ self, candidate: dict[str, str], example_ids: list[DataId]
68
+ ) -> tuple[dict[DataId, CachedEvaluation[RolloutOutput]], list[DataId]]:
69
+ """Look up cached results for a batch. Returns (cached_results, uncached_ids)."""
70
+ h = _candidate_hash(candidate)
71
+ cached, uncached = {}, []
72
+ for eid in example_ids:
73
+ if entry := self._cache.get((h, eid)):
74
+ cached[eid] = entry
75
+ else:
76
+ uncached.append(eid)
77
+ return cached, uncached
78
+
79
+ def put_batch(
80
+ self,
81
+ candidate: dict[str, str],
82
+ example_ids: list[DataId],
83
+ outputs: list[RolloutOutput],
84
+ scores: list[float],
85
+ objective_scores_list: Sequence[ObjectiveScores] | None = None,
86
+ ) -> None:
87
+ """Store evaluation results for a batch of examples."""
88
+ h = _candidate_hash(candidate)
89
+ for i, eid in enumerate(example_ids):
90
+ self._cache[(h, eid)] = CachedEvaluation(
91
+ outputs[i], scores[i], objective_scores_list[i] if objective_scores_list else None
92
+ )
93
+
94
+ def evaluate_with_cache_full(
95
+ self,
96
+ candidate: dict[str, str],
97
+ example_ids: list[DataId],
98
+ fetcher: Callable[[list[DataId]], Any],
99
+ evaluator: Callable[[Any, dict[str, str]], tuple[Any, list[float], Sequence[ObjectiveScores] | None]],
100
+ ) -> tuple[dict[DataId, RolloutOutput], dict[DataId, float], dict[DataId, ObjectiveScores] | None, int]:
101
+ """
102
+ Evaluate using cache, returning full results.
103
+
104
+ Returns (outputs_by_id, scores_by_id, objective_scores_by_id, num_actual_evals).
105
+ """
106
+ cached, uncached_ids = self.get_batch(candidate, example_ids)
107
+
108
+ outputs_by_id: dict[DataId, RolloutOutput] = {eid: c.output for eid, c in cached.items()}
109
+ scores_by_id: dict[DataId, float] = {eid: c.score for eid, c in cached.items()}
110
+ objective_by_id: dict[DataId, ObjectiveScores] | None = None
111
+
112
+ # Populate objective scores from cache
113
+ for eid, c in cached.items():
114
+ if c.objective_scores is not None:
115
+ objective_by_id = objective_by_id or {}
116
+ objective_by_id[eid] = c.objective_scores
117
+
118
+ # Evaluate uncached examples
119
+ if uncached_ids:
120
+ batch = fetcher(uncached_ids)
121
+ outputs, scores, obj_scores = evaluator(batch, candidate)
122
+ for idx, eid in enumerate(uncached_ids):
123
+ outputs_by_id[eid] = outputs[idx]
124
+ scores_by_id[eid] = scores[idx]
125
+ if obj_scores is not None:
126
+ objective_by_id = objective_by_id or {}
127
+ objective_by_id[eid] = obj_scores[idx]
128
+ self.put_batch(candidate, uncached_ids, outputs, scores, obj_scores)
129
+
130
+ return outputs_by_id, scores_by_id, objective_by_id, len(uncached_ids)
131
+
132
+
133
+ @dataclass(slots=True)
134
+ class ValsetEvaluation(Generic[RolloutOutput, DataId]):
135
+ """Container for evaluation results on a validation set batch."""
136
+
137
+ outputs_by_val_id: dict[DataId, RolloutOutput]
138
+ scores_by_val_id: dict[DataId, float]
139
+ objective_scores_by_val_id: dict[DataId, ObjectiveScores] | None = None
140
+
141
+
142
+ class GEPAState(Generic[RolloutOutput, DataId]):
143
+ """Internal persistent state of a GEPA optimization run.
144
+
145
+ Tracks all explored candidates, their per-example and per-objective scores,
146
+ Pareto frontiers, evaluation budget, and optional evaluation cache.
147
+ Saved/loaded automatically when ``EngineConfig.run_dir`` is set.
148
+
149
+ Users interact with this indirectly via :class:`~gepa.core.result.GEPAResult`
150
+ returned by :func:`~gepa.optimize_anything.optimize_anything`.
151
+ """
152
+
153
+ _VALIDATION_SCHEMA_VERSION: ClassVar[int] = 5
154
+ # Attributes that are runtime-only and should not be serialized (e.g., callback hooks, caches)
155
+ _EXCLUDED_FROM_SERIALIZATION: ClassVar[frozenset[str]] = frozenset({"_budget_hooks"})
156
+
157
+ program_candidates: list[dict[str, str]]
158
+ parent_program_for_candidate: list[list[ProgramIdx | None]]
159
+ prog_candidate_val_subscores: list[dict[DataId, float]]
160
+ prog_candidate_objective_scores: list[ObjectiveScores]
161
+
162
+ pareto_front_valset: dict[DataId, float]
163
+ program_at_pareto_front_valset: dict[DataId, set[ProgramIdx]]
164
+ objective_pareto_front: ObjectiveScores
165
+ program_at_pareto_front_objectives: dict[str, set[ProgramIdx]]
166
+ pareto_front_cartesian: dict[tuple[DataId, str], float]
167
+ program_at_pareto_front_cartesian: dict[tuple[DataId, str], set[ProgramIdx]]
168
+
169
+ list_of_named_predictors: list[str]
170
+ named_predictor_id_to_update_next_for_program_candidate: list[int]
171
+
172
+ i: int
173
+ num_full_ds_evals: int
174
+
175
+ total_num_evals: int
176
+
177
+ num_metric_calls_by_discovery: list[int]
178
+
179
+ full_program_trace: list[dict[str, Any]]
180
+ best_outputs_valset: dict[DataId, list[tuple[ProgramIdx, RolloutOutput]]] | None
181
+
182
+ validation_schema_version: int
183
+
184
+ # Optional evaluation cache for (candidate, example) pairs
185
+ evaluation_cache: "EvaluationCache[RolloutOutput, DataId] | None"
186
+
187
+ # Opaque bag for adapter-specific persistent state.
188
+ # Core GEPA never inspects this; adapters read/write via get_adapter_state()/set_adapter_state().
189
+ adapter_state: dict[str, Any]
190
+
191
+ def __init__(
192
+ self,
193
+ seed_candidate: dict[str, str],
194
+ base_evaluation: ValsetEvaluation[RolloutOutput, DataId],
195
+ track_best_outputs: bool = False,
196
+ frontier_type: FrontierType = "instance",
197
+ evaluation_cache: "EvaluationCache[RolloutOutput, DataId] | None" = None,
198
+ ):
199
+ self.program_candidates = [dict(seed_candidate)]
200
+ self.prog_candidate_val_subscores = [dict(base_evaluation.scores_by_val_id)]
201
+
202
+ base_objective_aggregates = self._aggregate_objective_scores(base_evaluation.objective_scores_by_val_id)
203
+ self.prog_candidate_objective_scores = [base_objective_aggregates]
204
+
205
+ self.parent_program_for_candidate = [[None]]
206
+
207
+ self.frontier_type: FrontierType = frontier_type
208
+ self.pareto_front_valset = dict(base_evaluation.scores_by_val_id)
209
+ self.program_at_pareto_front_valset = {val_id: {0} for val_id in base_evaluation.scores_by_val_id.keys()}
210
+ self.objective_pareto_front = dict(base_objective_aggregates)
211
+ self.program_at_pareto_front_objectives = {objective: {0} for objective in base_objective_aggregates.keys()}
212
+
213
+ # Validate that objective scores are provided for frontier types that require them
214
+ if frontier_type in ("objective", "hybrid", "cartesian"):
215
+ if not base_evaluation.objective_scores_by_val_id:
216
+ raise ValueError(
217
+ f"frontier_type='{frontier_type}' requires objective_scores to be provided by the evaluator, "
218
+ f"but none were found. Use an evaluator that returns objective_scores or use frontier_type='instance'."
219
+ )
220
+
221
+ # Cartesian frontier will be base_evaluation.objective_scores_by_val_id
222
+ if frontier_type == "cartesian":
223
+ assert base_evaluation.objective_scores_by_val_id is not None # Already validated above
224
+ self.pareto_front_cartesian = {
225
+ (val_id, objective): objective_score
226
+ for val_id, objective_scores in base_evaluation.objective_scores_by_val_id.items()
227
+ for objective, objective_score in objective_scores.items()
228
+ }
229
+ self.program_at_pareto_front_cartesian = {
230
+ (val_id, objective): {0}
231
+ for val_id, objective_scores in base_evaluation.objective_scores_by_val_id.items()
232
+ for objective in objective_scores.keys()
233
+ }
234
+ else:
235
+ self.pareto_front_cartesian = {}
236
+ self.program_at_pareto_front_cartesian = {}
237
+
238
+ self.list_of_named_predictors = list(seed_candidate.keys())
239
+ self.named_predictor_id_to_update_next_for_program_candidate = [0]
240
+ self.i = -1
241
+
242
+ self.num_metric_calls_by_discovery = [0]
243
+
244
+ if track_best_outputs:
245
+ self.best_outputs_valset = {
246
+ val_id: [(0, output)] for val_id, output in base_evaluation.outputs_by_val_id.items()
247
+ }
248
+ else:
249
+ self.best_outputs_valset = None
250
+
251
+ self.full_program_trace = []
252
+ self.validation_schema_version = self._VALIDATION_SCHEMA_VERSION
253
+ self.evaluation_cache = evaluation_cache
254
+ self.adapter_state: dict[str, Any] = {}
255
+
256
+ def is_consistent(self) -> bool:
257
+ assert len(self.program_candidates) == len(self.parent_program_for_candidate)
258
+ assert len(self.program_candidates) == len(self.named_predictor_id_to_update_next_for_program_candidate)
259
+ assert len(self.program_candidates) == len(self.prog_candidate_val_subscores)
260
+ assert len(self.program_candidates) == len(self.prog_candidate_objective_scores)
261
+ assert len(self.program_candidates) == len(self.num_metric_calls_by_discovery)
262
+
263
+ assert len(self.pareto_front_valset) == len(self.program_at_pareto_front_valset)
264
+ assert set(self.pareto_front_valset.keys()) == set(self.program_at_pareto_front_valset.keys())
265
+ assert set(self.objective_pareto_front.keys()) == set(self.program_at_pareto_front_objectives.keys())
266
+
267
+ for front in self.program_at_pareto_front_valset.values():
268
+ for prog_idx in front:
269
+ assert prog_idx < len(self.program_candidates), (
270
+ "Program index in valset pareto front exceeds number of program candidates"
271
+ )
272
+
273
+ return True
274
+
275
+ # Budget Hook Mechanism
276
+ def add_budget_hook(self, hook: Callable[[int, int], None]) -> None:
277
+ """Register a callback to be called whenever total_num_evals changes.
278
+
279
+ Args:
280
+ hook: A callable that receives (new_total, delta) when evals are incremented.
281
+ """
282
+ if not hasattr(self, "_budget_hooks"):
283
+ self._budget_hooks: list[Callable[[int, int], None]] = []
284
+ self._budget_hooks.append(hook)
285
+
286
+ def increment_evals(self, count: int) -> None:
287
+ """Increment total_num_evals and notify all registered hooks.
288
+
289
+ Args:
290
+ count: Number of evaluations to add.
291
+ """
292
+ self.total_num_evals += count
293
+ # Lazy init handles states loaded from disk (which won't have _budget_hooks)
294
+ hooks = getattr(self, "_budget_hooks", None)
295
+ if hooks:
296
+ for hook in hooks:
297
+ hook(self.total_num_evals, count)
298
+
299
+ def _atomic_write_json(self, run_dir: str, filename: str, data: Any) -> None:
300
+ target_path = os.path.join(run_dir, filename)
301
+ tmp_path = target_path + ".tmp"
302
+ with open(tmp_path, "w") as f:
303
+ json.dump(data, f, indent=2, default=json_default)
304
+ os.replace(tmp_path, target_path)
305
+
306
+ def save(self, run_dir: str | None, *, use_cloudpickle: bool = False) -> None:
307
+ if run_dir is None:
308
+ return
309
+ if use_cloudpickle:
310
+ try:
311
+ import cloudpickle as pickle # type: ignore[import-not-found]
312
+ except ModuleNotFoundError:
313
+ import pickle
314
+ import warnings
315
+
316
+ warnings.warn(
317
+ "cloudpickle is not installed; falling back to standard pickle. "
318
+ "Install it with: pip install gepa[full] or pip install cloudpickle",
319
+ stacklevel=2,
320
+ )
321
+ else:
322
+ import pickle
323
+ # Exclude runtime-only attributes that can't be serialized (e.g., callback hooks)
324
+ serialized = {k: v for k, v in self.__dict__.items() if k not in self._EXCLUDED_FROM_SERIALIZATION}
325
+ serialized["validation_schema_version"] = GEPAState._VALIDATION_SCHEMA_VERSION
326
+ target_path = os.path.join(run_dir, "gepa_state.bin")
327
+ tmp_path = target_path + ".tmp"
328
+ try:
329
+ with open(tmp_path, "wb") as f:
330
+ pickle.dump(serialized, f)
331
+ except Exception as e:
332
+ if not use_cloudpickle:
333
+ raise type(e)(
334
+ f"{e}\n\nHint: standard pickle failed to serialize the GEPA state. "
335
+ "Try setting use_cloudpickle=True in EngineConfig, which can serialize "
336
+ "more object types (lambdas, closures, etc.). "
337
+ "Install it with: pip install gepa[full] or pip install cloudpickle"
338
+ ) from e
339
+ raise
340
+ os.replace(tmp_path, target_path)
341
+
342
+ # Save run log and candidates as human-readable JSON
343
+ if self.full_program_trace:
344
+ self._atomic_write_json(run_dir, "run_log.json", self.full_program_trace)
345
+ if self.program_candidates:
346
+ self._atomic_write_json(run_dir, "candidates.json", self.program_candidates)
347
+
348
+ @staticmethod
349
+ def load(run_dir: str) -> "GEPAState[RolloutOutput, DataId]":
350
+ with open(os.path.join(run_dir, "gepa_state.bin"), "rb") as f:
351
+ import pickle
352
+
353
+ data = pickle.load(f)
354
+
355
+ # handle schema migration
356
+ version = data.get("validation_schema_version")
357
+ if version is None or version < 2:
358
+ GEPAState._migrate_from_legacy_state_v0(data)
359
+ version = data.get("validation_schema_version")
360
+ if version is None or version < GEPAState._VALIDATION_SCHEMA_VERSION:
361
+ GEPAState._upgrade_state_dict(data)
362
+
363
+ state = GEPAState.__new__(GEPAState)
364
+ state.__dict__.update(data)
365
+
366
+ state.validation_schema_version = GEPAState._VALIDATION_SCHEMA_VERSION
367
+ assert len(state.program_candidates) == len(state.prog_candidate_val_subscores)
368
+ assert len(state.program_candidates) == len(state.prog_candidate_objective_scores)
369
+ assert len(state.program_candidates) == len(state.num_metric_calls_by_discovery)
370
+ assert len(state.program_candidates) == len(state.parent_program_for_candidate)
371
+ assert len(state.program_candidates) == len(state.named_predictor_id_to_update_next_for_program_candidate)
372
+ assert len(state.pareto_front_valset) == len(state.program_at_pareto_front_valset)
373
+ assert set(state.pareto_front_valset.keys()) == set(state.program_at_pareto_front_valset.keys())
374
+ assert set(state.objective_pareto_front.keys()) == set(state.program_at_pareto_front_objectives.keys())
375
+ assert isinstance(state.adapter_state, dict)
376
+ return state
377
+
378
+ @staticmethod
379
+ def _migrate_from_legacy_state_v0(d: dict[str, Any]) -> None:
380
+ assert isinstance(d, dict)
381
+ assert "prog_candidate_val_subscores" in d
382
+ assert isinstance(d["prog_candidate_val_subscores"], list)
383
+ assert all(isinstance(scores, list) for scores in d["prog_candidate_val_subscores"])
384
+ legacy_scores: list[list[float]] = d.pop("prog_candidate_val_subscores", [])
385
+ d["prog_candidate_val_subscores"] = [dict(enumerate(scores)) for scores in legacy_scores]
386
+
387
+ pareto_front = d.get("pareto_front_valset")
388
+ if isinstance(pareto_front, list):
389
+ d["pareto_front_valset"] = dict(enumerate(pareto_front))
390
+
391
+ program_at_front = d.get("program_at_pareto_front_valset")
392
+ if isinstance(program_at_front, list):
393
+ d["program_at_pareto_front_valset"] = {idx: set(front) for idx, front in enumerate(program_at_front)}
394
+
395
+ best_outputs = d.get("best_outputs_valset")
396
+ if isinstance(best_outputs, list):
397
+ d["best_outputs_valset"] = {idx: list(outputs) for idx, outputs in enumerate(best_outputs)}
398
+
399
+ d["validation_schema_version"] = 2
400
+
401
+ @staticmethod
402
+ def _upgrade_state_dict(d: dict[str, Any]) -> None:
403
+ num_candidates = len(d.get("program_candidates", []))
404
+ if "prog_candidate_objective_scores" not in d:
405
+ d["prog_candidate_objective_scores"] = [{} for _ in range(num_candidates)]
406
+ if "objective_pareto_front" not in d:
407
+ d["objective_pareto_front"] = {}
408
+ if "program_at_pareto_front_objectives" not in d:
409
+ d["program_at_pareto_front_objectives"] = {}
410
+ if "frontier_type" not in d:
411
+ d["frontier_type"] = "instance"
412
+ # Since frontier_type instance does not require "pareto_front_cartesian" and "program_at_pareto_front_cartesian", we can safely set them to empty dicts.
413
+ d["pareto_front_cartesian"] = {}
414
+ d["program_at_pareto_front_cartesian"] = {}
415
+ # evaluation_cache is not persisted across runs by default; initialize to None if missing
416
+ if "evaluation_cache" not in d:
417
+ d["evaluation_cache"] = None
418
+ if "adapter_state" not in d:
419
+ d["adapter_state"] = {}
420
+ d["validation_schema_version"] = GEPAState._VALIDATION_SCHEMA_VERSION
421
+
422
+ @staticmethod
423
+ def _aggregate_objective_scores(
424
+ val_objective_scores: dict[DataId, ObjectiveScores] | None,
425
+ ) -> ObjectiveScores:
426
+ if not val_objective_scores:
427
+ return {}
428
+ totals: dict[str, float] = {}
429
+ counts: dict[str, int] = {}
430
+ for objective_dict in val_objective_scores.values():
431
+ for objective, score in objective_dict.items():
432
+ totals[objective] = totals.get(objective, 0.0) + score
433
+ counts[objective] = counts.get(objective, 0) + 1
434
+ return {
435
+ objective: totals[objective] / counts[objective] for objective in totals.keys() if counts[objective] > 0
436
+ }
437
+
438
+ def get_program_average_val_subset(self, program_idx: int) -> tuple[float, int]:
439
+ # TODO: This should be only used/handled by the val_evaluation_policy, and never used directly.
440
+ scores = self.prog_candidate_val_subscores[program_idx]
441
+ if not scores:
442
+ return float("-inf"), 0
443
+ num_samples = len(scores)
444
+ avg = sum(scores.values()) / num_samples
445
+ return avg, num_samples
446
+
447
+ @property
448
+ def valset_evaluations(self) -> dict[DataId, list[ProgramIdx]]:
449
+ """
450
+ Valset examples by id and programs that have evaluated them. Keys include only validation
451
+ ids that have been scored at least once.
452
+ """
453
+ result: dict[DataId, list[ProgramIdx]] = defaultdict(list)
454
+ for program_idx, val_scores in enumerate(self.prog_candidate_val_subscores):
455
+ for val_id in val_scores.keys():
456
+ result[val_id].append(program_idx)
457
+ return result
458
+
459
+ @property
460
+ def program_full_scores_val_set(self) -> list[float]:
461
+ # TODO: This should be using the val_evaluation_policy instead of the get_program_average_val_subset method to calculate the scores.
462
+ return [
463
+ self.get_program_average_val_subset(program_idx)[0]
464
+ for program_idx in range(len(self.prog_candidate_val_subscores))
465
+ ]
466
+
467
+ @property
468
+ def per_program_tracked_scores(self) -> list[float]:
469
+ return [
470
+ self.get_program_average_val_subset(program_idx)[0]
471
+ for program_idx in range(len(self.prog_candidate_val_subscores))
472
+ ]
473
+
474
+ def _update_objective_pareto_front(self, objective_scores: ObjectiveScores, program_idx: ProgramIdx) -> None:
475
+ if not objective_scores:
476
+ return
477
+ for objective, score in objective_scores.items():
478
+ prev_score = self.objective_pareto_front.get(objective, float("-inf"))
479
+ if score > prev_score:
480
+ self.objective_pareto_front[objective] = score
481
+ self.program_at_pareto_front_objectives[objective] = {program_idx}
482
+ elif score == prev_score:
483
+ front = self.program_at_pareto_front_objectives.setdefault(objective, set())
484
+ front.add(program_idx)
485
+
486
+ def _update_pareto_front_for_val_id(
487
+ self,
488
+ val_id: DataId,
489
+ score: float,
490
+ program_idx: ProgramIdx,
491
+ output: RolloutOutput | None,
492
+ run_dir: str | None,
493
+ iteration: int,
494
+ ) -> None:
495
+ prev_score = self.pareto_front_valset.get(val_id, float("-inf"))
496
+ if score > prev_score:
497
+ self.pareto_front_valset[val_id] = score
498
+ self.program_at_pareto_front_valset[val_id] = {program_idx}
499
+ if self.best_outputs_valset is not None and output is not None:
500
+ self.best_outputs_valset[val_id] = [(program_idx, output)]
501
+ if run_dir is not None:
502
+ task_dir = os.path.join(run_dir, "generated_best_outputs_valset", f"task_{val_id}")
503
+ os.makedirs(task_dir, exist_ok=True)
504
+ with open(os.path.join(task_dir, f"iter_{iteration}_prog_{program_idx}.json"), "w") as fout:
505
+ json.dump(output, fout, indent=4, default=json_default)
506
+ elif score == prev_score:
507
+ pareto_front = self.program_at_pareto_front_valset.setdefault(val_id, set())
508
+ pareto_front.add(program_idx)
509
+ if self.best_outputs_valset is not None and output is not None:
510
+ self.best_outputs_valset[val_id].append((program_idx, output))
511
+
512
+ def _update_pareto_front_for_cartesian(
513
+ self,
514
+ val_id: DataId,
515
+ objective: str,
516
+ objective_score: float,
517
+ program_idx: ProgramIdx,
518
+ ) -> None:
519
+ prev_score = self.pareto_front_cartesian.get((val_id, objective), float("-inf"))
520
+ if objective_score > prev_score:
521
+ self.pareto_front_cartesian[(val_id, objective)] = objective_score
522
+ self.program_at_pareto_front_cartesian[(val_id, objective)] = {program_idx}
523
+ elif objective_score == prev_score:
524
+ front = self.program_at_pareto_front_cartesian.setdefault((val_id, objective), set())
525
+ front.add(program_idx)
526
+
527
+ def update_state_with_new_program(
528
+ self,
529
+ parent_program_idx: list[ProgramIdx],
530
+ new_program: dict[str, str],
531
+ valset_evaluation: ValsetEvaluation,
532
+ run_dir: str | None,
533
+ num_metric_calls_by_discovery_of_new_program: int,
534
+ ) -> ProgramIdx:
535
+ new_program_idx = len(self.program_candidates)
536
+ self.program_candidates.append(dict(new_program))
537
+ self.num_metric_calls_by_discovery.append(num_metric_calls_by_discovery_of_new_program)
538
+
539
+ max_predictor_id = max(
540
+ [self.named_predictor_id_to_update_next_for_program_candidate[p] for p in parent_program_idx],
541
+ default=0,
542
+ )
543
+ self.named_predictor_id_to_update_next_for_program_candidate.append(max_predictor_id)
544
+ self.parent_program_for_candidate.append(list(parent_program_idx))
545
+
546
+ valset_scores = dict(valset_evaluation.scores_by_val_id)
547
+ self.prog_candidate_val_subscores.append(valset_scores)
548
+ objective_scores = self._aggregate_objective_scores(valset_evaluation.objective_scores_by_val_id)
549
+ self.prog_candidate_objective_scores.append(objective_scores)
550
+
551
+ for val_id, score in valset_scores.items():
552
+ output = valset_evaluation.outputs_by_val_id.get(val_id) if valset_evaluation.outputs_by_val_id else None
553
+ self._update_pareto_front_for_val_id(
554
+ val_id,
555
+ score,
556
+ new_program_idx,
557
+ output,
558
+ run_dir,
559
+ self.i + 1,
560
+ )
561
+
562
+ self._update_objective_pareto_front(objective_scores, new_program_idx)
563
+
564
+ if self.frontier_type in ("objective", "hybrid", "cartesian"):
565
+ if not valset_evaluation.objective_scores_by_val_id:
566
+ raise ValueError(
567
+ f"frontier_type='{self.frontier_type}' requires objective_scores to be provided by the evaluator, "
568
+ f"but none were found in the evaluation result."
569
+ )
570
+
571
+ if self.frontier_type == "cartesian":
572
+ assert valset_evaluation.objective_scores_by_val_id is not None # Validated above
573
+ for val_id, objective_scores in valset_evaluation.objective_scores_by_val_id.items():
574
+ for objective, objective_score in objective_scores.items():
575
+ self._update_pareto_front_for_cartesian(
576
+ val_id,
577
+ objective,
578
+ objective_score,
579
+ new_program_idx,
580
+ )
581
+
582
+ return new_program_idx
583
+
584
+ def _get_pareto_front_mapping(self, frontier_type: FrontierType) -> dict[FrontierKey, set[ProgramIdx]]:
585
+ if frontier_type == "instance":
586
+ return {val_id: set(front) for val_id, front in self.program_at_pareto_front_valset.items()}
587
+ if frontier_type == "objective":
588
+ return {objective: set(front) for objective, front in self.program_at_pareto_front_objectives.items()}
589
+ if frontier_type == "hybrid":
590
+ combined: dict[FrontierKey, set[ProgramIdx]] = {
591
+ ("val_id", val_id): set(front) for val_id, front in self.program_at_pareto_front_valset.items()
592
+ }
593
+ for objective, front in self.program_at_pareto_front_objectives.items():
594
+ combined[("objective", objective)] = set(front)
595
+ return combined
596
+ if frontier_type == "cartesian":
597
+ return {
598
+ ("cartesian", val_id, objective): set(front)
599
+ for (val_id, objective), front in self.program_at_pareto_front_cartesian.items()
600
+ }
601
+ raise ValueError(f"Unknown frontier_type: {frontier_type}")
602
+
603
+ def get_pareto_front_mapping(self) -> dict[FrontierKey, set[ProgramIdx]]:
604
+ """Return frontier key to best-program-indices mapping based on configured frontier_type."""
605
+ return self._get_pareto_front_mapping(self.frontier_type)
606
+
607
+ def cached_evaluate(
608
+ self,
609
+ candidate: dict[str, str],
610
+ example_ids: list[DataId],
611
+ fetcher: Callable[[list[DataId]], Any],
612
+ evaluator: Callable[[Any, dict[str, str]], tuple[Any, list[float], Sequence[ObjectiveScores] | None]],
613
+ ) -> tuple[list[float], int]:
614
+ """Evaluate with optional caching. Returns (scores, num_actual_evals)."""
615
+ _, scores_by_id, _, num_actual_evals = self.cached_evaluate_full(candidate, example_ids, fetcher, evaluator)
616
+ return [scores_by_id[eid] for eid in example_ids], num_actual_evals
617
+
618
+ def cached_evaluate_full(
619
+ self,
620
+ candidate: dict[str, str],
621
+ example_ids: list[DataId],
622
+ fetcher: Callable[[list[DataId]], Any],
623
+ evaluator: Callable[[Any, dict[str, str]], tuple[Any, list[float], Sequence[ObjectiveScores] | None]],
624
+ ) -> tuple[dict[DataId, RolloutOutput], dict[DataId, float], dict[DataId, ObjectiveScores] | None, int]:
625
+ """Evaluate with optional caching, returning full results."""
626
+ if self.evaluation_cache is not None:
627
+ return self.evaluation_cache.evaluate_with_cache_full(candidate, example_ids, fetcher, evaluator)
628
+ batch = fetcher(example_ids)
629
+ outputs, scores, objective_scores = evaluator(batch, candidate)
630
+ outputs_by_id = dict(zip(example_ids, outputs, strict=False))
631
+ scores_by_id = dict(zip(example_ids, scores, strict=False))
632
+ objective_by_id = dict(zip(example_ids, objective_scores, strict=False)) if objective_scores else None
633
+ return outputs_by_id, scores_by_id, objective_by_id, len(example_ids)
634
+
635
+
636
+ def write_eval_scores_to_directory(scores: dict[DataId, float], output_dir: str) -> None:
637
+ for val_id, score in scores.items():
638
+ task_dir = os.path.join(output_dir, f"task_{val_id}")
639
+ os.makedirs(task_dir, exist_ok=True)
640
+ with open(os.path.join(task_dir, f"iter_{0}_prog_0.json"), "w") as f:
641
+ json.dump(score, f, indent=4, default=json_default)
642
+
643
+
644
+ def write_eval_outputs_to_directory(outputs, output_dir: str) -> None:
645
+ """
646
+ Write generated rollout outputs (not scalar scores) to disk.
647
+
648
+ Structure:
649
+ {output_dir}/task_{val_id}/iter_0_prog_0.json
650
+
651
+ This directory is used to store best outputs for inspection/reuse.
652
+ """
653
+ for val_id, output in outputs.items():
654
+ task_dir = os.path.join(output_dir, f"task_{val_id}")
655
+ os.makedirs(task_dir, exist_ok=True)
656
+ with open(os.path.join(task_dir, "iter_0_prog_0.json"), "w") as f:
657
+ json.dump(output, f, indent=4, default=json_default)
658
+
659
+
660
+ def initialize_gepa_state(
661
+ run_dir: str | None,
662
+ logger: LoggerProtocol,
663
+ seed_candidate: dict[str, str],
664
+ seed_valset_evaluation: ValsetEvaluation[RolloutOutput, DataId],
665
+ track_best_outputs: bool = False,
666
+ frontier_type: FrontierType = "instance",
667
+ evaluation_cache: "EvaluationCache[RolloutOutput, DataId] | None" = None,
668
+ ) -> GEPAState[RolloutOutput, DataId]:
669
+ if run_dir is not None and os.path.exists(os.path.join(run_dir, "gepa_state.bin")):
670
+ logger.log("Loading gepa state from run dir")
671
+ gepa_state = GEPAState.load(run_dir)
672
+ if gepa_state.frontier_type != frontier_type:
673
+ raise ValueError(
674
+ f"Frontier type mismatch: requested '{frontier_type}' but loaded state has '{gepa_state.frontier_type}'. "
675
+ f"Use a different run_dir or match the frontier_type parameter."
676
+ )
677
+ # Sync cache with current run's cache_evaluation setting:
678
+ # - If caching is disabled (evaluation_cache is None), clear any loaded cache
679
+ # to respect the current run's cache_evaluation=False setting
680
+ # - If caching is enabled and the loaded state has a cache, preserve it
681
+ # (allows resuming with cached results from previous run)
682
+ # - If caching is enabled but no cache exists in loaded state, use the new empty cache
683
+ if evaluation_cache is None:
684
+ gepa_state.evaluation_cache = None
685
+ elif gepa_state.evaluation_cache is None:
686
+ gepa_state.evaluation_cache = evaluation_cache
687
+ # else: keep the loaded cache (gepa_state.evaluation_cache is already set)
688
+ else:
689
+ if run_dir is not None:
690
+ write_eval_outputs_to_directory(
691
+ seed_valset_evaluation.outputs_by_val_id,
692
+ os.path.join(run_dir, "generated_best_outputs_valset"),
693
+ )
694
+
695
+ gepa_state = GEPAState(
696
+ seed_candidate,
697
+ seed_valset_evaluation,
698
+ track_best_outputs=track_best_outputs,
699
+ frontier_type=frontier_type,
700
+ evaluation_cache=evaluation_cache,
701
+ )
702
+
703
+ gepa_state.num_full_ds_evals = 1
704
+ gepa_state.total_num_evals = len(seed_valset_evaluation.scores_by_val_id)
705
+
706
+ return gepa_state