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,830 @@
1
+ """
2
+ Training script using GEPA optimize_anything API.
3
+ """
4
+
5
+ import argparse
6
+ import json
7
+ import logging
8
+ import os
9
+ import sys
10
+ from pathlib import Path
11
+
12
+ from dotenv import load_dotenv
13
+
14
+ # Suppress verbose LiteLLM logging
15
+ logging.getLogger("LiteLLM").setLevel(logging.WARNING)
16
+ logging.getLogger("litellm").setLevel(logging.WARNING)
17
+
18
+ # Load environment variables
19
+ load_dotenv()
20
+
21
+ import re
22
+
23
+ from datasets import load_dataset
24
+ from gskill.cost_tracker import reset_tracker
25
+ from gskill.experiment_logger import ExperimentLogger, set_logger
26
+ from gskill.swe_fitness_fn import create_swe_fitness_fn
27
+
28
+ from gepa.optimize_anything import EngineConfig, GEPAConfig, ReflectionConfig, TrackingConfig, optimize_anything
29
+ from gepa.utils.stop_condition import TimeoutStopCondition
30
+
31
+
32
+ class TeeOutput:
33
+ """Duplicate stdout/stderr to both terminal and a log file."""
34
+
35
+ def __init__(self, log_path: Path):
36
+ self.log_path = log_path
37
+ self.log_file = None
38
+ self.original_stdout = None
39
+ self.original_stderr = None
40
+
41
+ def start(self):
42
+ """Start capturing output."""
43
+ self.log_file = open(self.log_path, "w", buffering=1) # Line buffered
44
+ self.original_stdout = sys.stdout
45
+ self.original_stderr = sys.stderr
46
+ sys.stdout = self._TeeStream(self.original_stdout, self.log_file)
47
+ sys.stderr = self._TeeStream(self.original_stderr, self.log_file)
48
+ print(f"[Logging terminal output to: {self.log_path}]")
49
+
50
+ def stop(self):
51
+ """Stop capturing and restore original streams."""
52
+ if self.original_stdout:
53
+ sys.stdout = self.original_stdout
54
+ if self.original_stderr:
55
+ sys.stderr = self.original_stderr
56
+ if self.log_file:
57
+ self.log_file.close()
58
+
59
+ class _TeeStream:
60
+ """Stream wrapper that writes to both original stream and file."""
61
+
62
+ def __init__(self, original, log_file):
63
+ self.original = original
64
+ self.log_file = log_file
65
+
66
+ def write(self, data):
67
+ self.original.write(data)
68
+ self.original.flush()
69
+ self.log_file.write(data)
70
+ self.log_file.flush()
71
+
72
+ def flush(self):
73
+ self.original.flush()
74
+ self.log_file.flush()
75
+
76
+ def fileno(self):
77
+ return self.original.fileno()
78
+
79
+
80
+ class LoggingProposer:
81
+ """Base proposer class that logs all inputs/outputs to separate files.
82
+
83
+ Can be inherited to create custom proposers with different strategies.
84
+ """
85
+
86
+ # Custom prompt for learning terminal and repo-specific skills
87
+ DEFAULT_PROMPT_TEMPLATE = """You are helping an AI coding agent learn skills for fixing bugs in a software repository.
88
+
89
+ The agent operates inside a Docker container with:
90
+ - The repository cloned at /testbed
91
+ - Access to bash commands (grep, find, cat, sed, python, pytest, git, etc.)
92
+ - The ability to read, edit, and create files
93
+
94
+ ## Current Skills
95
+ The agent currently has these learned skills:
96
+ ```
97
+ {curr_skills}
98
+ ```
99
+
100
+ ## Evaluation Results
101
+ Below are results from the agent attempting to fix bugs using the current skills. Each entry shows:
102
+ - The problem statement (bug description)
103
+ - The agent's actions and reasoning
104
+ - Whether the fix succeeded or failed
105
+ - Test output and error messages
106
+
107
+ ```
108
+ {evaluation_data}
109
+ ```
110
+
111
+ ## Your Task
112
+ Analyze the evaluation results and propose IMPROVED SKILLS that will help the agent:
113
+
114
+ 1. **Terminal/Bash Skills**: Common command patterns, file navigation, searching code, running tests
115
+ 2. **Repository-Specific Knowledge**: Project structure, key modules, common patterns in this codebase
116
+ 3. **Debugging Strategies**: How to locate bugs, understand test failures, verify fixes
117
+ 4. **Code Editing Patterns**: Safe ways to modify files, handle imports, avoid syntax errors
118
+
119
+ Focus on:
120
+ - Patterns that led to SUCCESS - reinforce these
121
+ - Patterns that led to FAILURE - what should the agent do differently?
122
+ - Missing knowledge that would have helped
123
+ - Common mistakes to avoid
124
+
125
+ Write the improved skills as clear, actionable instructions. Be specific and concrete. Remember the agent will not be evaluated on the same task, but they are expected to use the skills to fix other tasks in the SAME repository.
126
+ If any older skills are still helpful, include them as well. However, always keep the overall skills concise and comprehensive.
127
+ Adding skills blindly will not help the agent, so only add skills that are actually helpful.
128
+
129
+ Provide the new skills within a SINGLE ``` block. Only include one ``` block, if you include multiple ``` blocks, the agent will not be able to parse the response."""
130
+
131
+ def __init__(self, run_dir: Path, reflection_lm: str):
132
+ """Initialize the proposer.
133
+
134
+ Args:
135
+ run_dir: Directory to save logs
136
+ reflection_lm: Model name for reflection LLM
137
+ """
138
+ from gepa.lm import LM
139
+
140
+ self.run_dir = run_dir
141
+ self.reflection_lm = reflection_lm
142
+ self._lm = LM(reflection_lm)
143
+ self.proposer_dir = run_dir / "proposer_calls"
144
+ self.proposer_dir.mkdir(parents=True, exist_ok=True)
145
+ self.call_counter = 0
146
+ self.prompt_template = self.DEFAULT_PROMPT_TEMPLATE
147
+
148
+ def format_prompt(self, curr_skills: str, evaluation_data: str) -> str:
149
+ """Format the prompt with current skills and evaluation data.
150
+
151
+ Override this method to customize prompt formatting.
152
+ """
153
+ return self.prompt_template.format(
154
+ curr_skills=curr_skills if curr_skills else "(No skills learned yet)", evaluation_data=evaluation_data
155
+ )
156
+
157
+ def call_llm(self, prompt: str) -> str:
158
+ """Call the LLM and return the response text.
159
+
160
+ Override this method to customize LLM calling.
161
+ """
162
+ return self._lm(prompt)
163
+
164
+ def extract_skills(self, response_text: str) -> str:
165
+ """Extract skills from LLM response.
166
+
167
+ Override this method to customize extraction logic.
168
+ Matches from the first ``` to the last ``` to handle nested backtick references.
169
+ """
170
+ # Find the first code fence (with optional language tag)
171
+ first = re.search(r"```(?:\w*\n)?", response_text)
172
+ if not first:
173
+ return response_text.strip()
174
+
175
+ # Find the last occurrence of ``` to handle nested backtick references
176
+ last_pos = response_text.rfind("```")
177
+ if last_pos <= first.end():
178
+ return response_text.strip()
179
+
180
+ return response_text[first.end() : last_pos].strip()
181
+
182
+ def log_call(self, call_num: int, log_entry: dict) -> Path:
183
+ """Log the call to a file and return the file path.
184
+
185
+ Override this method to customize logging.
186
+ """
187
+ call_file = self.proposer_dir / f"call_{call_num:03d}.json"
188
+ with open(call_file, "w") as f:
189
+ json.dump(log_entry, f, indent=2, default=str)
190
+ return call_file
191
+
192
+ def __call__(self, candidate: dict, reflective_dataset: dict, components_to_update: list) -> dict:
193
+ """Main proposer logic - processes all side info at once.
194
+
195
+ Args:
196
+ candidate: Current candidate dict with component values
197
+ reflective_dataset: Dict mapping components to their side info lists
198
+ components_to_update: List of component names to update
199
+
200
+ Returns:
201
+ Dict mapping components to their new values
202
+ """
203
+ self.call_counter += 1
204
+ call_num = self.call_counter
205
+
206
+ results = {}
207
+
208
+ for component in components_to_update:
209
+ curr_value = candidate.get(component, "")
210
+ side_info = reflective_dataset.get(component, [])
211
+
212
+ # Format side info as string
213
+ side_info_str = json.dumps(side_info, indent=2, default=str)
214
+
215
+ # Format the prompt
216
+ prompt = self.format_prompt(curr_value, side_info_str)
217
+
218
+ # Prepare log entry
219
+ log_entry = {
220
+ "call_num": call_num,
221
+ "component": component,
222
+ "current_skills": curr_value,
223
+ "evaluation_data": side_info,
224
+ "full_prompt": prompt,
225
+ "type": "batch",
226
+ }
227
+
228
+ # Call the reflection LLM
229
+ try:
230
+ response_text = self.call_llm(prompt)
231
+ new_value = self.extract_skills(response_text)
232
+
233
+ log_entry["llm_response"] = response_text
234
+ log_entry["new_skills"] = new_value
235
+ log_entry["success"] = True
236
+
237
+ results[component] = new_value
238
+
239
+ except Exception as e:
240
+ log_entry["error"] = str(e)
241
+ log_entry["success"] = False
242
+ results[component] = curr_value # Keep current on error
243
+
244
+ # Write to separate file for this call
245
+ call_file = self.log_call(call_num, log_entry)
246
+
247
+ # Also print summary
248
+ print(f"\n[PROPOSER] Call {call_num} for '{component}':")
249
+ print(f" Current skills: {len(curr_value)} chars")
250
+ print(f" Evaluation items: {len(side_info)}")
251
+ print(f" New skills: {len(results.get(component, ''))} chars")
252
+ print(f" Saved to: {call_file}")
253
+
254
+ return results
255
+
256
+
257
+ class LoopProposer(LoggingProposer):
258
+ """Proposer that processes one side info item at a time, then merges all skills."""
259
+
260
+ MERGE_TEMPLATE = """You are helping an AI coding agent consolidate learned skills for fixing bugs.
261
+
262
+ ## New Skills/Insights from Recent Evaluations
263
+ {intermediate_skills}
264
+
265
+ ## Your Task
266
+ Merge all the above skills into a SINGLE, coherent skill set that:
267
+ 1. Eliminates redundancy
268
+ 2. Resolves conflicts (keep the most reliable/general one)
269
+ 3. Prioritizes (most important first)
270
+ 4. Stays concise
271
+
272
+ Provide the merged skills within a SINGLE ``` block."""
273
+
274
+ def _process_single_item(
275
+ self, component: str, curr_skills: str, item: dict, item_idx: int, total_items: int
276
+ ) -> str | None:
277
+ """Process a single evaluation item and return extracted skills, or None on failure."""
278
+ self.call_counter += 1
279
+ call_num = self.call_counter
280
+
281
+ item_str = json.dumps(item, indent=2, default=str)
282
+ prompt = self.format_prompt(curr_skills, item_str)
283
+
284
+ log_entry = {
285
+ "call_num": call_num,
286
+ "component": component,
287
+ "item_index": item_idx,
288
+ "total_items": total_items,
289
+ "current_skills": curr_skills,
290
+ "evaluation_item": item,
291
+ "full_prompt": prompt,
292
+ "type": "single_item",
293
+ }
294
+
295
+ try:
296
+ response_text = self.call_llm(prompt)
297
+ new_skills = self.extract_skills(response_text)
298
+ log_entry.update({"llm_response": response_text, "extracted_skills": new_skills, "success": True})
299
+ result = new_skills
300
+ except Exception as e:
301
+ log_entry.update({"error": str(e), "success": False})
302
+ result = None
303
+
304
+ self.log_call(call_num, log_entry)
305
+ print(
306
+ f"\n[LOOP PROPOSER] Call {call_num} - Item {item_idx + 1}/{total_items} for '{component}': {'OK' if result else 'FAIL'}"
307
+ )
308
+ return result
309
+
310
+ def _merge_skills(self, component: str, curr_skills: str, intermediate_skills: list) -> str:
311
+ """Merge intermediate skills into final skill set."""
312
+ self.call_counter += 1
313
+ call_num = self.call_counter
314
+
315
+ formatted = "\n\n".join(f"### Evaluation {i}\n```\n{s}\n```" for i, s in enumerate(intermediate_skills, 1))
316
+ merge_prompt = self.MERGE_TEMPLATE.format(intermediate_skills=formatted)
317
+
318
+ log_entry = {
319
+ "call_num": call_num,
320
+ "component": component,
321
+ "type": "merge",
322
+ "num_intermediate_skills": len(intermediate_skills),
323
+ "intermediate_skills": intermediate_skills,
324
+ "full_prompt": merge_prompt,
325
+ }
326
+
327
+ try:
328
+ response_text = self.call_llm(merge_prompt)
329
+ final_skills = self.extract_skills(response_text)
330
+ log_entry.update({"llm_response": response_text, "final_skills": final_skills, "success": True})
331
+ except Exception as e:
332
+ log_entry.update({"error": str(e), "success": False})
333
+ final_skills = "\n\n---\n\n".join(intermediate_skills) # Fallback
334
+
335
+ self.log_call(call_num, log_entry)
336
+ print(
337
+ f"\n[LOOP PROPOSER] Merge call {call_num} for '{component}': {len(intermediate_skills)} items -> {len(final_skills)} chars"
338
+ )
339
+ return final_skills
340
+
341
+ def __call__(self, candidate: dict, reflective_dataset: dict, components_to_update: list) -> dict:
342
+ """Process each side info item one at a time, then merge all skills."""
343
+ results = {}
344
+
345
+ for component in components_to_update:
346
+ curr_skills = candidate.get(component, "")
347
+ side_info_items = reflective_dataset.get(component, [])
348
+
349
+ if not side_info_items:
350
+ results[component] = curr_skills
351
+ continue
352
+
353
+ # Process each item
354
+ intermediate_skills = [
355
+ skills
356
+ for i, item in enumerate(side_info_items)
357
+ if (skills := self._process_single_item(component, curr_skills, item, i, len(side_info_items)))
358
+ ]
359
+
360
+ # Merge results
361
+ results[component] = (
362
+ self._merge_skills(component, curr_skills, intermediate_skills) if intermediate_skills else curr_skills
363
+ )
364
+
365
+ return results
366
+
367
+
368
+ def create_logging_proposer(run_dir: Path, reflection_lm: str):
369
+ """Create a LoggingProposer instance (factory function for backward compatibility)."""
370
+ return LoggingProposer(run_dir, reflection_lm)
371
+
372
+
373
+ def create_loop_proposer(run_dir: Path, reflection_lm: str):
374
+ """Create a LoopProposer instance that processes items one at a time then merges."""
375
+ return LoopProposer(run_dir, reflection_lm)
376
+
377
+
378
+ import random
379
+
380
+
381
+ def load_and_split_data(repo: str, train_size: int, val_size: int, test_size: int, seed: int = 42):
382
+ """
383
+ Load data directly from HuggingFace and split into train/val/test.
384
+
385
+ Args:
386
+ repo: Repository name to filter (e.g., "pygments__pygments" or "swesmith/pygments__pygments")
387
+ train_size: Number of training examples
388
+ val_size: Number of validation examples
389
+ test_size: Number of test examples
390
+ seed: Random seed for shuffling
391
+
392
+ Returns:
393
+ (train_data, val_data, test_data) tuple
394
+ """
395
+ total_needed = train_size + val_size + test_size
396
+ print(f"Loading {repo} data from HuggingFace SWE-smith (need {total_needed})...")
397
+ ds = load_dataset("SWE-bench/SWE-smith", split="train")
398
+
399
+ # Normalize repo name for matching
400
+ # Handle both "pygments__pygments" and "swesmith/pygments__pygments"
401
+ repo_pattern = repo if "/" in repo else f"swesmith/{repo}"
402
+
403
+ all_data = []
404
+ for item in ds:
405
+ repo_name = item.get("repo", "")
406
+ if repo_pattern in repo_name:
407
+ all_data.append(dict(item))
408
+ if len(all_data) >= total_needed:
409
+ break
410
+
411
+ print(f"Loaded {len(all_data)} {repo} examples")
412
+
413
+ # Shuffle with seed
414
+ random.seed(seed)
415
+ random.shuffle(all_data)
416
+
417
+ # Calculate split sizes
418
+ total_needed = train_size + val_size + test_size
419
+ if len(all_data) < total_needed:
420
+ print(f"WARNING: Only {len(all_data)} examples available, need {total_needed}")
421
+ # Scale down proportionally
422
+ ratio = len(all_data) / total_needed
423
+ train_size = int(train_size * ratio)
424
+ val_size = int(val_size * ratio)
425
+ test_size = len(all_data) - train_size - val_size
426
+
427
+ # Split
428
+ train_data = all_data[:train_size]
429
+ val_data = all_data[train_size : train_size + val_size]
430
+ test_data = all_data[train_size + val_size : train_size + val_size + test_size]
431
+
432
+ print(f"Split: {len(train_data)} train, {len(val_data)} val, {len(test_data)} test")
433
+
434
+ return train_data, val_data, test_data
435
+
436
+
437
+ def evaluate_on_test(fitness_fn, candidate: dict, test_data: list, name: str = "Test"):
438
+ """Evaluate a candidate on test data and return results."""
439
+ print(f"\n{'=' * 70}")
440
+ print(f"Evaluating {name}...")
441
+ print(f"{'=' * 70}")
442
+
443
+ results = [fitness_fn(candidate, task) for task in test_data]
444
+
445
+ scores = [r[0] for r in results]
446
+ avg_score = sum(scores) / len(scores) if scores else 0.0
447
+ pass_count = sum(1 for s in scores if s == 1.0)
448
+
449
+ print(f"\n{name} Results:")
450
+ print(f" Pass rate: {pass_count}/{len(test_data)} ({avg_score:.1%})")
451
+
452
+ return {"avg_score": avg_score, "pass_count": pass_count, "total": len(test_data), "scores": scores}
453
+
454
+
455
+ def main():
456
+ parser = argparse.ArgumentParser(description="GEPA optimization using optimize_anything API (recommended)")
457
+ parser.add_argument(
458
+ "--repo",
459
+ type=str,
460
+ default="pygments__pygments",
461
+ help="Repository to train on (e.g., 'pygments__pygments', 'django__django')",
462
+ )
463
+ parser.add_argument("--train-size", type=int, default=200, help="Number of training examples")
464
+ parser.add_argument("--val-size", type=int, default=50, help="Number of validation examples for Pareto selection")
465
+ parser.add_argument("--test-size", type=int, default=100, help="Number of test examples for final evaluation")
466
+ parser.add_argument(
467
+ "--model", type=str, default="gpt-5-mini", help="Agent model for running tasks (default: gpt-5-mini)"
468
+ )
469
+ parser.add_argument(
470
+ "--reflection-model",
471
+ type=str,
472
+ default="gpt-5.2-pro",
473
+ help="Model for GEPA reflection/prompt optimization (default: gpt-5.2-pro)",
474
+ )
475
+ parser.add_argument("--smoke-test", action="store_true", help="Run a quick smoke test")
476
+ parser.add_argument(
477
+ "--run-post-optimization-testset", action="store_true", help="Evaluate on test set after optimization"
478
+ )
479
+ parser.add_argument(
480
+ "--resume",
481
+ type=str,
482
+ default=None,
483
+ help="Resume from a previous run directory (e.g., gepa_results/logs/run_XXXXXXXX)",
484
+ )
485
+ parser.add_argument(
486
+ "--run-pre-optimization-testset", action="store_true", help="run the agent on the test set before optimization"
487
+ )
488
+ parser.add_argument(
489
+ "--run-testset", action="store_true", help="run the agent on the test set before and after optimization"
490
+ )
491
+ parser.add_argument("--verbose", action="store_true", help="Enable verbose logging")
492
+ parser.add_argument("--timeout", type=int, default=43200, help="Max seconds to run (default: 12 hours)")
493
+ parser.add_argument("--seed", type=int, default=42, help="Random seed for reproducibility")
494
+ parser.add_argument("--workers", type=int, default=6, help="Number of parallel workers (Docker containers)")
495
+ parser.add_argument("--max-metric-calls", type=int, default=600, help="Max rollouts for GEPA (controls budget)")
496
+ parser.add_argument("--wandb", action="store_true", help="Enable wandb tracking")
497
+ parser.add_argument("--wandb-project", type=str, default="gepa-swesmith", help="Wandb project name")
498
+ parser.add_argument(
499
+ "--proposer",
500
+ type=str,
501
+ default="batch",
502
+ choices=["batch", "loop"],
503
+ help="Proposer type: 'batch' (all at once) or 'loop' (one at a time, then merge)",
504
+ )
505
+ args = parser.parse_args()
506
+
507
+ if args.smoke_test:
508
+ args.train_size = 3
509
+ args.val_size = 2
510
+ args.test_size = 2
511
+ args.max_metric_calls = 20
512
+ print("SMOKE TEST MODE")
513
+
514
+ # Handle resume from previous run
515
+ resumed_from = None
516
+ if args.resume:
517
+ resume_dir = Path(args.resume)
518
+ if not resume_dir.exists():
519
+ print(f"ERROR: Resume directory not found: {resume_dir}")
520
+ sys.exit(1)
521
+
522
+ state_file = resume_dir / "gepa_state.bin"
523
+ if not state_file.exists():
524
+ print(f"ERROR: No gepa_state.bin found in {resume_dir}")
525
+ sys.exit(1)
526
+
527
+ # Load previous config to get consistent parameters
528
+ prev_config_file = resume_dir / "config.json"
529
+ if prev_config_file.exists():
530
+ with open(prev_config_file) as f:
531
+ prev_config = json.load(f)
532
+ # Use same repo and seed for data consistency
533
+ args.repo = prev_config.get("repo", args.repo)
534
+ args.seed = prev_config.get("seed", args.seed)
535
+ print(f"RESUME MODE: Loading state from {resume_dir}")
536
+ print(f" Using repo={args.repo}, seed={args.seed} from previous run")
537
+
538
+ resumed_from = str(resume_dir)
539
+
540
+ # Initialize experiment logger FIRST to get run_id
541
+ exp_logger = ExperimentLogger(log_dir="gepa_results/logs", repo=args.repo)
542
+ set_logger(exp_logger)
543
+ run_dir = exp_logger.log_dir
544
+
545
+ # If resuming, copy the state file to the new run directory
546
+ if resumed_from:
547
+ import shutil
548
+
549
+ src_state = Path(resumed_from) / "gepa_state.bin"
550
+ dst_state = run_dir / "gepa_state.bin"
551
+ shutil.copy2(src_state, dst_state)
552
+ print(f"Copied state file from {src_state} to {dst_state}")
553
+
554
+ # Setup output tee - capture all stdout/stderr to terminal.log
555
+ terminal_log = run_dir / "terminal.log"
556
+ tee = TeeOutput(terminal_log)
557
+ tee.start()
558
+
559
+ # Setup Python logging
560
+ log_file = run_dir / "training.log"
561
+ logging.basicConfig(
562
+ level=logging.INFO if args.verbose else logging.WARNING,
563
+ format="%(asctime)s - %(levelname)s - %(message)s",
564
+ handlers=[logging.FileHandler(log_file), logging.StreamHandler(sys.stdout)],
565
+ )
566
+ logger = logging.getLogger(__name__)
567
+
568
+ # Reset cost tracking
569
+ reset_tracker()
570
+
571
+ print("\n" + "=" * 70)
572
+ print("GEPA + SWE-smith Training (optimize_anything API)")
573
+ print("=" * 70)
574
+ print(f"Repository: {args.repo}")
575
+ print(f"Model: {args.model}")
576
+ print(f"Reflection Model: {args.reflection_model}")
577
+ print(f"Proposer: {args.proposer}")
578
+ print(f"Workers: {args.workers} (Docker containers)")
579
+ print(f"Run directory: {run_dir}")
580
+ if resumed_from:
581
+ print(f"Resumed from: {resumed_from}")
582
+ print("=" * 70 + "\n")
583
+
584
+ # 1. Load Data - directly from HuggingFace, split with shuffle
585
+ train_data, val_data, test_data = load_and_split_data(
586
+ repo=args.repo, train_size=args.train_size, val_size=args.val_size, test_size=args.test_size, seed=args.seed
587
+ )
588
+
589
+ print(f"Data splits: {len(train_data)} train, {len(val_data)} val, {len(test_data)} test")
590
+ logger.info(f"Data: train={len(train_data)}, val={len(val_data)}, test={len(test_data)}")
591
+
592
+ # Determine reflection model
593
+ reflection_model = args.reflection_model or args.model
594
+
595
+ # Save experiment config
596
+ exp_logger.save_config(
597
+ {
598
+ "api": "optimize_anything",
599
+ "repo": args.repo,
600
+ "model": args.model,
601
+ "reflection_model": reflection_model,
602
+ "train_size": len(train_data),
603
+ "val_size": len(val_data),
604
+ "test_size": len(test_data),
605
+ "max_metric_calls": args.max_metric_calls,
606
+ "workers": args.workers,
607
+ "seed": args.seed,
608
+ "timeout": args.timeout,
609
+ "resumed_from": resumed_from,
610
+ "run_pre_optimization_testset": args.run_pre_optimization_testset,
611
+ "run_post_optimization_testset": args.run_post_optimization_testset,
612
+ "execution_mode": "docker",
613
+ "wandb": args.wandb,
614
+ "wandb_project": args.wandb_project if args.wandb else None,
615
+ "proposer": args.proposer,
616
+ }
617
+ )
618
+
619
+ # 2. Create Fitness Function
620
+ print(f"\nCreating fitness function with {args.workers} workers...")
621
+ fitness_fn = create_swe_fitness_fn(model_name=args.model, n_workers=args.workers)
622
+ logger.info(f"Model: {args.model}")
623
+ logger.info("Execution: Docker containers via SWE-smith")
624
+
625
+ # 3. Initial Candidate (Baseline Prompt)
626
+ # Start with empty skills - GEPA will learn and evolve them
627
+ # The system_template in mini.yaml already has base instructions,
628
+ # this just populates the {{ skills }} placeholder
629
+ initial_skills = ""
630
+
631
+ seed_candidate = {"skills": initial_skills}
632
+
633
+ # 4. Setup stop conditions
634
+ stop_callbacks = [
635
+ TimeoutStopCondition(timeout_seconds=args.timeout),
636
+ ]
637
+
638
+ # 5. Configure GEPA with structured config
639
+ wandb_kwargs = (
640
+ {
641
+ "project": args.wandb_project,
642
+ "name": run_dir.name,
643
+ "config": {
644
+ "repo": args.repo,
645
+ "model": args.model,
646
+ "reflection_model": reflection_model,
647
+ "train_size": len(train_data),
648
+ "val_size": len(val_data),
649
+ },
650
+ }
651
+ if args.wandb
652
+ else None
653
+ )
654
+
655
+ # Configure LiteLLM for reflection model (OpenAI regional endpoint)
656
+ if "openai" in reflection_model.lower() or reflection_model.startswith("gpt-"):
657
+ os.environ["OPENAI_API_BASE"] = "https://us.api.openai.com/v1"
658
+
659
+ # Create proposer based on command line choice
660
+ if args.proposer == "loop":
661
+ proposer = LoopProposer(run_dir, reflection_model)
662
+ elif args.proposer == "batch":
663
+ proposer = LoggingProposer(run_dir, reflection_model)
664
+
665
+ config = GEPAConfig(
666
+ engine=EngineConfig(
667
+ run_dir=str(run_dir),
668
+ seed=args.seed,
669
+ display_progress_bar=True,
670
+ max_metric_calls=args.max_metric_calls,
671
+ candidate_selection_strategy="pareto",
672
+ parallel=True,
673
+ max_workers=args.workers,
674
+ ),
675
+ reflection=ReflectionConfig(
676
+ reflection_lm=reflection_model,
677
+ reflection_minibatch_size=3,
678
+ skip_perfect_score=True,
679
+ perfect_score=1.0,
680
+ custom_candidate_proposer=proposer,
681
+ ),
682
+ tracking=TrackingConfig(
683
+ use_wandb=args.wandb,
684
+ wandb_init_kwargs=wandb_kwargs,
685
+ ),
686
+ stop_callbacks=stop_callbacks,
687
+ )
688
+
689
+ # 6. Baseline evaluations (before optimization)
690
+ original_config_results = None
691
+ baseline_test_results = None
692
+ if args.run_pre_optimization_testset or args.run_testset:
693
+ # 6a. Evaluate with original mini-swe-agent config (no skills placeholder)
694
+ original_config_path = Path(__file__).parent / "mini_swe_agent_config" / "original_mini.yaml"
695
+ print(f"\nEvaluating original mini-swe-agent config: {original_config_path}")
696
+ original_fitness_fn = create_swe_fitness_fn(
697
+ model_name=args.model, n_workers=args.workers, config_path=str(original_config_path)
698
+ )
699
+ original_config_results = evaluate_on_test(
700
+ original_fitness_fn, {"skills": ""}, test_data, name="Original mini-swe-agent"
701
+ )
702
+ with open(run_dir / "original_config_test_results.json", "w") as f:
703
+ json.dump(original_config_results, f, indent=2)
704
+
705
+ # 6b. Evaluate with GEPA template + empty skills
706
+ print("\nEvaluating GEPA template with empty skills")
707
+ baseline_test_results = evaluate_on_test(
708
+ fitness_fn, seed_candidate, test_data, name="GEPA template (empty skills)"
709
+ )
710
+ with open(run_dir / "baseline_test_results.json", "w") as f:
711
+ json.dump(baseline_test_results, f, indent=2)
712
+
713
+ # 7. Run GEPA
714
+ print("\n" + "=" * 70)
715
+ print("Starting GEPA Optimization (optimize_anything API)...")
716
+ print("=" * 70 + "\n")
717
+
718
+ result = optimize_anything(
719
+ seed_candidate=seed_candidate,
720
+ evaluator=fitness_fn,
721
+ dataset=train_data,
722
+ valset=val_data,
723
+ config=config,
724
+ )
725
+
726
+ # 7. Results
727
+ print("\n" + "=" * 70)
728
+ print("Optimization Complete!")
729
+ print("=" * 70)
730
+
731
+ # best_candidate is a dict: {"skills": "..."}
732
+ best_candidate_dict = result.best_candidate
733
+ if isinstance(best_candidate_dict, dict):
734
+ # It's already a dict with the prompt
735
+ best_skills = best_candidate_dict.get("skills", str(best_candidate_dict))
736
+ else:
737
+ # It's an object with .candidate attribute
738
+ best_skills = best_candidate_dict.candidate["skills"]
739
+
740
+ # Get best score from result
741
+ best_idx = result.best_idx
742
+ best_score = result.val_aggregate_scores[best_idx] if result.val_aggregate_scores else 0.0
743
+ num_candidates = result.num_candidates
744
+
745
+ print(f"\nBest Prompt (Score: {best_score:.2%}):")
746
+ print("-" * 70)
747
+ print(best_skills)
748
+ print("-" * 70)
749
+
750
+ # Save best skills
751
+ skills_file = run_dir / "prompts" / "best_skills.txt"
752
+ skills_file.parent.mkdir(exist_ok=True)
753
+ with open(skills_file, "w") as f:
754
+ f.write(best_skills)
755
+
756
+ print(f"\nBest skills saved to: {skills_file}")
757
+ print(f"Full results in: {run_dir}")
758
+
759
+ # 9. Post-optimization test evaluation
760
+ optimized_test_results = None
761
+ if args.run_post_optimization_testset or args.run_testset:
762
+ optimized_candidate = {"skills": best_skills}
763
+ optimized_test_results = evaluate_on_test(
764
+ fitness_fn, optimized_candidate, test_data, name="Optimized (after optimization)"
765
+ )
766
+ # Save optimized results
767
+ with open(run_dir / "optimized_test_results.json", "w") as f:
768
+ json.dump(optimized_test_results, f, indent=2)
769
+
770
+ # Print comparison
771
+ print("\n" + "=" * 70)
772
+ print("TEST SET COMPARISON")
773
+ print("=" * 70)
774
+ if original_config_results:
775
+ print(
776
+ f" Original mini-swe-agent: {original_config_results['pass_count']}/{original_config_results['total']} ({original_config_results['avg_score']:.1%})"
777
+ )
778
+ if baseline_test_results:
779
+ print(
780
+ f" GEPA template (empty): {baseline_test_results['pass_count']}/{baseline_test_results['total']} ({baseline_test_results['avg_score']:.1%})"
781
+ )
782
+ print(
783
+ f" GEPA optimized: {optimized_test_results['pass_count']}/{optimized_test_results['total']} ({optimized_test_results['avg_score']:.1%})"
784
+ )
785
+ if original_config_results:
786
+ improvement_from_original = optimized_test_results["avg_score"] - original_config_results["avg_score"]
787
+ print(f" Improvement over original: {improvement_from_original:+.1%}")
788
+ if baseline_test_results:
789
+ improvement_from_baseline = optimized_test_results["avg_score"] - baseline_test_results["avg_score"]
790
+ print(f" Improvement over baseline: {improvement_from_baseline:+.1%}")
791
+ print("=" * 70)
792
+
793
+ # Summary
794
+ summary_data = {
795
+ "best_score": float(best_score),
796
+ "num_candidates": num_candidates,
797
+ "total_metric_calls": result.total_metric_calls,
798
+ "final_skills_length": len(best_skills),
799
+ }
800
+ if original_config_results:
801
+ summary_data["original_config_test_score"] = original_config_results["avg_score"]
802
+ if baseline_test_results:
803
+ summary_data["baseline_test_score"] = baseline_test_results["avg_score"]
804
+ if optimized_test_results:
805
+ summary_data["optimized_test_score"] = optimized_test_results["avg_score"]
806
+
807
+ exp_logger.save_summary(best_prompt=best_skills, extra_info=summary_data)
808
+
809
+ print("\nSummary:")
810
+ print(f" Best Val Score: {best_score:.2%}")
811
+ print(f" Candidates Generated: {num_candidates}")
812
+ print(f" Total Metric Calls: {result.total_metric_calls}")
813
+ print(f" Final Skills Length: {len(best_skills)} chars")
814
+ if original_config_results:
815
+ print(f" Original Config Test Score: {original_config_results['avg_score']:.1%}")
816
+ if baseline_test_results:
817
+ print(f" Baseline Test Score: {baseline_test_results['avg_score']:.1%}")
818
+ if optimized_test_results:
819
+ print(f" Optimized Test Score: {optimized_test_results['avg_score']:.1%}")
820
+
821
+ print("\n" + "=" * 70)
822
+ print("Done!")
823
+ print("=" * 70 + "\n")
824
+
825
+ # Stop tee logging
826
+ tee.stop()
827
+
828
+
829
+ if __name__ == "__main__":
830
+ main()