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,367 @@
1
+ """
2
+ Structured experiment logging for GEPA + SWE-smith experiments.
3
+
4
+ Logs everything needed for analysis:
5
+ - Per-iteration metrics (tokens, steps, scores)
6
+ - Proposer inputs (prompts + traces sent to reflection LLM)
7
+ - Generated prompts at each iteration
8
+ - Aggregated statistics
9
+ """
10
+
11
+ import json
12
+ import uuid
13
+ from dataclasses import asdict, dataclass, field
14
+ from datetime import datetime
15
+ from pathlib import Path
16
+ from typing import Any
17
+
18
+
19
+ @dataclass
20
+ class TaskMetrics:
21
+ """Metrics for a single task execution."""
22
+
23
+ instance_id: str
24
+ success: bool
25
+ score: float
26
+ steps: int
27
+ estimated_tokens: int
28
+ has_patch: bool
29
+
30
+
31
+ @dataclass
32
+ class EvalBatchMetrics:
33
+ """
34
+ Metrics for one adapter.evaluate() call.
35
+
36
+ Note: GEPA calls evaluate() multiple times:
37
+ - Different candidates (prompts) on same tasks
38
+ - Train set sampling for reflection
39
+ - Val set for Pareto selection
40
+
41
+ Use prompt_hash to identify which candidate/prompt was used.
42
+ Same prompt_hash = same prompt being evaluated.
43
+ """
44
+
45
+ eval_id: int # Sequential counter of evaluate() calls
46
+ timestamp: str
47
+ prompt_hash: str # Unique identifier for this prompt/candidate
48
+ prompt_preview: str # First 100 chars for quick reference
49
+
50
+ # Task-level data
51
+ num_tasks: int
52
+ num_passed: int
53
+ pass_rate: float
54
+
55
+ # Aggregated metrics
56
+ avg_steps: float # Avg agent steps per task
57
+ avg_tokens: float # Avg agent tokens per task (estimated)
58
+ total_steps: int
59
+ total_tokens: int
60
+
61
+ # Per-task breakdown (instance_id tells you which task)
62
+ task_metrics: list[TaskMetrics] = field(default_factory=list)
63
+
64
+
65
+ @dataclass
66
+ class ProposerInput:
67
+ """What gets sent to the proposer/reflection LLM."""
68
+
69
+ iteration: int
70
+ timestamp: str
71
+ current_prompt: str
72
+ reflection_records: list[dict[str, Any]] # From make_reflective_dataset
73
+
74
+
75
+ class ExperimentLogger:
76
+ """
77
+ Comprehensive logging for GEPA experiments.
78
+
79
+ Creates structured JSON files for later analysis:
80
+ - iterations.jsonl: One line per iteration with all metrics
81
+ - proposer_inputs.jsonl: What the proposer sees each iteration
82
+ - prompts/: Directory with each prompt version
83
+ - summary.json: Final experiment summary
84
+
85
+ Each run gets its own folder: logs/run_YYYYMMDD_HHMMSS_<short_uuid>/
86
+ """
87
+
88
+ def __init__(self, log_dir: str = "gepa_results/logs", run_name: str | None = None, repo: str | None = None):
89
+ # Generate unique run ID
90
+ self.start_time = datetime.now()
91
+ timestamp = self.start_time.strftime("%Y%m%d_%H%M%S")
92
+ short_id = uuid.uuid4().hex[:6]
93
+
94
+ if run_name:
95
+ self.run_id = run_name
96
+ elif repo:
97
+ # Extract short repo name (e.g., "pygments" from "pygments__pygments")
98
+ short_repo = repo.split("__")[1]
99
+ self.run_id = f"run_{short_repo}_{timestamp}_{short_id}"
100
+ else:
101
+ self.run_id = f"run_{timestamp}_{short_id}"
102
+
103
+ # Create run-specific directory
104
+ self.base_log_dir = Path(log_dir)
105
+ self.log_dir = self.base_log_dir / self.run_id
106
+ self.log_dir.mkdir(parents=True, exist_ok=True)
107
+
108
+ # Create subdirectories
109
+ self.prompts_dir = self.log_dir / "prompts"
110
+ self.prompts_dir.mkdir(exist_ok=True)
111
+
112
+ # Initialize log files
113
+ self.iterations_file = self.log_dir / "iterations.jsonl"
114
+ self.proposer_file = self.log_dir / "proposer_inputs.jsonl"
115
+ self.summary_file = self.log_dir / "summary.json"
116
+ self.config_file = self.log_dir / "config.json"
117
+
118
+ # Track experiment state
119
+ self.eval_count = 0 # Number of evaluate() calls
120
+ self.all_evals: list[EvalBatchMetrics] = []
121
+ self.baseline_metrics: EvalBatchMetrics | None = None
122
+ self.prompts_seen: dict[str, str] = {} # prompt_hash -> full prompt
123
+
124
+ # Create symlink to latest run for easy access
125
+ latest_link = self.base_log_dir / "latest"
126
+ if latest_link.is_symlink():
127
+ latest_link.unlink()
128
+ if not latest_link.exists():
129
+ latest_link.symlink_to(self.run_id)
130
+
131
+ print("📊 Experiment logger initialized")
132
+ print(f" Run ID: {self.run_id}")
133
+ print(f" Run dir: {self.log_dir}")
134
+ print(f" Iterations: {self.iterations_file.name}")
135
+ print(f" Proposer inputs: {self.proposer_file.name}")
136
+ print(f" Prompts: {self.prompts_dir.name}/")
137
+ print(f" Latest symlink: {latest_link}")
138
+
139
+ def save_config(self, config: dict[str, Any]):
140
+ """Save experiment configuration at the start of the run."""
141
+ config_with_meta = {"run_id": self.run_id, "start_time": self.start_time.isoformat(), **config}
142
+ with open(self.config_file, "w") as f:
143
+ json.dump(config_with_meta, f, indent=2)
144
+ print(f" Config saved: {self.config_file.name}")
145
+
146
+ def _prompt_hash(self, prompt: str) -> str:
147
+ """Short hash for prompt identification."""
148
+ import hashlib
149
+
150
+ return hashlib.md5(prompt.encode()).hexdigest()[:8]
151
+
152
+ def log_eval_batch(
153
+ self,
154
+ prompt: str,
155
+ outputs: list[dict[str, Any]],
156
+ scores: list[float],
157
+ task_ids: list[str],
158
+ is_baseline: bool = False,
159
+ ) -> EvalBatchMetrics:
160
+ """
161
+ Log metrics for one adapter.evaluate() call.
162
+
163
+ Args:
164
+ prompt: The system prompt used for this evaluation
165
+ outputs: List of output dicts from adapter (with steps, tokens, etc.)
166
+ scores: List of scores (0.0 or 1.0)
167
+ task_ids: List of instance_ids
168
+ is_baseline: Whether this is the baseline evaluation
169
+ """
170
+ timestamp = datetime.now().isoformat()
171
+ prompt_hash = self._prompt_hash(prompt)
172
+ eval_id = self.eval_count
173
+
174
+ # Track unique prompts
175
+ if prompt_hash not in self.prompts_seen:
176
+ self.prompts_seen[prompt_hash] = prompt
177
+
178
+ # Build per-task metrics
179
+ task_metrics = []
180
+ for _i, (output, score, task_id) in enumerate(zip(outputs, scores, task_ids, strict=False)):
181
+ tm = TaskMetrics(
182
+ instance_id=task_id,
183
+ success=score == 1.0,
184
+ score=score,
185
+ steps=output.get("steps", 0),
186
+ estimated_tokens=output.get("estimated_tokens", 0),
187
+ has_patch=bool(output.get("patch", "").strip()),
188
+ )
189
+ task_metrics.append(tm)
190
+
191
+ # Aggregate metrics
192
+ num_passed = sum(1 for s in scores if s == 1.0)
193
+ total_steps = sum(tm.steps for tm in task_metrics)
194
+ total_tokens = sum(tm.estimated_tokens for tm in task_metrics)
195
+
196
+ # Create prompt preview (first 100 chars, single line)
197
+ prompt_preview = prompt.replace("\n", " ")[:100] + "..." if len(prompt) > 100 else prompt.replace("\n", " ")
198
+
199
+ metrics = EvalBatchMetrics(
200
+ eval_id=eval_id,
201
+ timestamp=timestamp,
202
+ prompt_hash=prompt_hash,
203
+ prompt_preview=prompt_preview,
204
+ num_tasks=len(scores),
205
+ num_passed=num_passed,
206
+ pass_rate=num_passed / len(scores) if scores else 0.0,
207
+ avg_steps=total_steps / len(scores) if scores else 0.0,
208
+ avg_tokens=total_tokens / len(scores) if scores else 0.0,
209
+ total_steps=total_steps,
210
+ total_tokens=total_tokens,
211
+ task_metrics=task_metrics,
212
+ )
213
+
214
+ # Track baseline for comparison
215
+ if is_baseline or eval_id == 0:
216
+ self.baseline_metrics = metrics
217
+
218
+ self.all_evals.append(metrics)
219
+ self.eval_count += 1
220
+
221
+ # Write to JSONL (one line per eval batch)
222
+ with open(self.iterations_file, "a") as f:
223
+ # Convert dataclass to dict, handling nested dataclasses
224
+ data = asdict(metrics)
225
+ f.write(json.dumps(data) + "\n")
226
+
227
+ # Save prompt to file (only if new prompt)
228
+ prompt_file = self.prompts_dir / f"prompt_{prompt_hash}.txt"
229
+ if not prompt_file.exists():
230
+ with open(prompt_file, "w") as f:
231
+ f.write(prompt)
232
+
233
+ # Print summary
234
+ is_new_prompt = len(self.prompts_seen) > 1 and prompt_hash == list(self.prompts_seen.keys())[-1]
235
+ label = "BASELINE" if is_baseline else f"Eval #{eval_id}"
236
+ if is_new_prompt:
237
+ label += " (NEW PROMPT)"
238
+ print(f"\n📈 [{label}] {num_passed}/{len(scores)} passed ({metrics.pass_rate * 100:.1f}%)")
239
+ print(f" Avg steps: {metrics.avg_steps:.1f}, Avg tokens: {metrics.avg_tokens:.0f}")
240
+ print(f" Prompt: {prompt_hash} ({len(self.prompts_seen)} unique prompts seen)")
241
+
242
+ return metrics
243
+
244
+ def log_proposer_input(self, iteration: int, current_prompt: str, reflection_records: list[dict[str, Any]]):
245
+ """
246
+ Log what gets sent to the proposer/reflection LLM.
247
+ """
248
+ timestamp = datetime.now().isoformat()
249
+
250
+ proposer_input = ProposerInput(
251
+ iteration=iteration,
252
+ timestamp=timestamp,
253
+ current_prompt=current_prompt,
254
+ reflection_records=reflection_records,
255
+ )
256
+
257
+ # Write to JSONL
258
+ with open(self.proposer_file, "a") as f:
259
+ data = asdict(proposer_input)
260
+ f.write(json.dumps(data) + "\n")
261
+
262
+ # Also save a readable version
263
+ readable_file = self.prompts_dir / f"proposer_input_{iteration:03d}.txt"
264
+ with open(readable_file, "w") as f:
265
+ f.write(f"=== PROPOSER INPUT FOR ITERATION {iteration} ===\n")
266
+ f.write(f"Timestamp: {timestamp}\n\n")
267
+ f.write(f"=== CURRENT PROMPT ===\n{current_prompt}\n\n")
268
+ f.write(f"=== REFLECTION RECORDS ({len(reflection_records)} tasks) ===\n")
269
+ for i, record in enumerate(reflection_records):
270
+ f.write(f"\n--- Task {i + 1} ---\n")
271
+ f.write(json.dumps(record, indent=2))
272
+ f.write("\n")
273
+
274
+ print(f" 📝 Proposer input logged ({len(reflection_records)} reflection records)")
275
+
276
+ def get_comparison(self) -> dict[str, Any]:
277
+ """Get before/after comparison metrics."""
278
+ if not self.all_evals:
279
+ return {}
280
+
281
+ baseline = self.baseline_metrics or self.all_evals[0]
282
+ latest = self.all_evals[-1]
283
+
284
+ return {
285
+ "baseline": {
286
+ "eval_id": baseline.eval_id,
287
+ "prompt_hash": baseline.prompt_hash,
288
+ "pass_rate": baseline.pass_rate,
289
+ "avg_steps": baseline.avg_steps,
290
+ "avg_tokens": baseline.avg_tokens,
291
+ },
292
+ "latest": {
293
+ "eval_id": latest.eval_id,
294
+ "prompt_hash": latest.prompt_hash,
295
+ "pass_rate": latest.pass_rate,
296
+ "avg_steps": latest.avg_steps,
297
+ "avg_tokens": latest.avg_tokens,
298
+ },
299
+ "improvement": {
300
+ "pass_rate_delta": latest.pass_rate - baseline.pass_rate,
301
+ "pass_rate_pct_change": ((latest.pass_rate - baseline.pass_rate) / baseline.pass_rate * 100)
302
+ if baseline.pass_rate > 0
303
+ else 0,
304
+ "steps_delta": latest.avg_steps - baseline.avg_steps,
305
+ "tokens_delta": latest.avg_tokens - baseline.avg_tokens,
306
+ },
307
+ "unique_prompts_tested": len(self.prompts_seen),
308
+ "total_eval_batches": len(self.all_evals),
309
+ }
310
+
311
+ def save_summary(self, best_prompt: str | None = None, extra_info: dict | None = None):
312
+ """Save final experiment summary."""
313
+ comparison = self.get_comparison()
314
+
315
+ summary = {
316
+ "experiment_start": self.start_time.isoformat(),
317
+ "experiment_end": datetime.now().isoformat(),
318
+ "total_eval_batches": self.eval_count,
319
+ "unique_prompts_tested": len(self.prompts_seen),
320
+ "comparison": comparison,
321
+ "best_prompt_hash": self._prompt_hash(best_prompt) if best_prompt else None,
322
+ "prompts_seen": list(self.prompts_seen.keys()), # List of all prompt hashes
323
+ "extra_info": extra_info or {},
324
+ }
325
+
326
+ with open(self.summary_file, "w") as f:
327
+ json.dump(summary, f, indent=2)
328
+
329
+ print(f"\n{'=' * 60}")
330
+ print("EXPERIMENT SUMMARY")
331
+ print(f"{'=' * 60}")
332
+ if comparison:
333
+ print(f"Total eval batches: {comparison.get('total_eval_batches', self.eval_count)}")
334
+ print(f"Unique prompts tested: {comparison.get('unique_prompts_tested', len(self.prompts_seen))}")
335
+ print()
336
+ print(f"Baseline prompt: {comparison['baseline']['prompt_hash']}")
337
+ print(f"Final prompt: {comparison['latest']['prompt_hash']}")
338
+ print()
339
+ print(f"Baseline pass rate: {comparison['baseline']['pass_rate'] * 100:.1f}%")
340
+ print(f"Final pass rate: {comparison['latest']['pass_rate'] * 100:.1f}%")
341
+ print(f"Improvement: {comparison['improvement']['pass_rate_delta'] * 100:+.1f}%")
342
+ print()
343
+ print(f"Baseline avg steps: {comparison['baseline']['avg_steps']:.1f}")
344
+ print(f"Final avg steps: {comparison['latest']['avg_steps']:.1f}")
345
+ print(f"Steps delta: {comparison['improvement']['steps_delta']:+.1f}")
346
+ print()
347
+ print(f"Baseline avg tokens: {comparison['baseline']['avg_tokens']:.0f}")
348
+ print(f"Final avg tokens: {comparison['latest']['avg_tokens']:.0f}")
349
+ print(f"Tokens delta: {comparison['improvement']['tokens_delta']:+.0f}")
350
+ print(f"\nSummary saved to: {self.summary_file}")
351
+
352
+ return summary
353
+
354
+
355
+ # Global logger instance (set during experiment)
356
+ _experiment_logger: ExperimentLogger | None = None
357
+
358
+
359
+ def get_logger() -> ExperimentLogger | None:
360
+ """Get the global experiment logger."""
361
+ return _experiment_logger
362
+
363
+
364
+ def set_logger(logger: ExperimentLogger):
365
+ """Set the global experiment logger."""
366
+ global _experiment_logger
367
+ _experiment_logger = logger
@@ -0,0 +1,209 @@
1
+ """
2
+ Fitness function for GEPA optimize_anything API.
3
+
4
+ This wraps the SWE harness logic into a simple fitness function that:
5
+ - Takes a candidate (prompt) and batch of tasks
6
+ - Returns (score, output, side_info) tuples for each task
7
+ """
8
+
9
+ import threading
10
+ from typing import Any
11
+
12
+ from gskill.swe_harness import SWEHarness
13
+
14
+
15
+ def create_swe_fitness_fn(model_name: str = "gpt-5-mini", n_workers: int = 6, config_path: str | None = None):
16
+ """Create a fitness function for SWE tasks with Docker containers.
17
+
18
+ Args:
19
+ model_name: LiteLLM model name
20
+ n_workers: Number of parallel workers
21
+ config_path: Optional custom config path for the agent (defaults to mini.yaml)
22
+
23
+ Returns:
24
+ fitness_fn: Function that evaluates candidates on batches
25
+ """
26
+
27
+ # Create harness pool - one per worker
28
+ harness_pool = [SWEHarness() for _ in range(n_workers)]
29
+ harness_available = [True] * n_workers
30
+ harness_lock = threading.Lock()
31
+
32
+ def get_harness() -> tuple[int, SWEHarness]:
33
+ """Get an available harness from the pool."""
34
+ with harness_lock:
35
+ for i, available in enumerate(harness_available):
36
+ if available:
37
+ harness_available[i] = False
38
+ return i, harness_pool[i]
39
+ raise RuntimeError("No harness available - pool exhausted")
40
+
41
+ def release_harness(idx: int):
42
+ """Release a harness back to the pool."""
43
+ with harness_lock:
44
+ harness_available[idx] = True
45
+
46
+ def process_single_task(
47
+ task: dict[str, Any], skills: str, task_idx: int, total_tasks: int
48
+ ) -> tuple[float, dict[str, Any], dict[str, Any]]:
49
+ """Process a single task and return (score, output, side_info)."""
50
+
51
+ # Get a harness from the pool
52
+ harness_idx, harness = get_harness()
53
+
54
+ instance_id = task.get("instance_id", "unknown")[:50]
55
+
56
+ try:
57
+ print(f"[Task {task_idx + 1}/{total_tasks}] {instance_id}...", flush=True)
58
+
59
+ # 1. Setup Docker Container
60
+ harness.setup_task(task_instance=task)
61
+
62
+ # 2. Run Agent
63
+ problem = task["problem_statement"]
64
+ patch, agent_trace, agent_metrics = harness.run_agent(
65
+ problem, skills, model_name=model_name, config_path=config_path
66
+ )
67
+
68
+ # 3. Verify with tests
69
+ has_patch = len(patch.strip()) > 0
70
+ passed = False
71
+ feedback_msg = ""
72
+ test_output = ""
73
+
74
+ if not has_patch:
75
+ passed = False
76
+ feedback_msg = "no_patch"
77
+ test_output = "No patch to test."
78
+ else:
79
+ # Use SWE-smith's run_patch_in_container for proper verification
80
+ # This creates a fresh container, checks out HEAD~1 (with tests),
81
+ # applies the patch, and runs tests
82
+ # First test FAIL_TO_PASS only
83
+ f2p_passed, f2p_output = harness.verify_with_patch(patch, f2p_only=True)
84
+ test_output = f"=== FAIL_TO_PASS TESTS ===\n{f2p_output}"
85
+
86
+ if not f2p_passed:
87
+ passed = False
88
+ feedback_msg = "f2p_failed"
89
+ else:
90
+ # PASS_TO_PASS Tests (Regression Check) - run full test suite
91
+ pass_to_pass = task.get("PASS_TO_PASS", [])
92
+
93
+ if pass_to_pass:
94
+ # Run full test (includes both f2p and p2p)
95
+ p2p_passed, p2p_output = harness.verify_with_patch(patch, f2p_only=False)
96
+ test_output += f"\n\n=== FULL TEST SUITE ===\n{p2p_output}"
97
+
98
+ if not p2p_passed:
99
+ passed = False
100
+ feedback_msg = "p2p_regression"
101
+ else:
102
+ passed = True
103
+ feedback_msg = "all_passed"
104
+ else:
105
+ passed = True
106
+ feedback_msg = "f2p_passed"
107
+
108
+ score = 1.0 if passed else 0.0
109
+
110
+ # Rollout output
111
+ output = {
112
+ "patch": patch,
113
+ "success": passed,
114
+ "steps": agent_metrics.get("steps", 0),
115
+ "estimated_tokens": agent_metrics.get("estimated_tokens", 0),
116
+ }
117
+
118
+ # Side info for reflection
119
+ side_info = {
120
+ "Input": {
121
+ "Task ID": instance_id,
122
+ "Problem": problem[:200] + "..." if len(problem) > 200 else problem,
123
+ },
124
+ "Generated Outputs": {
125
+ "Patch": patch[:500] + "..." if len(patch) > 500 else patch,
126
+ "Agent Trace": agent_trace,
127
+ },
128
+ "Feedback": {
129
+ "Status": feedback_msg,
130
+ "Test Output": test_output[:500] + "..." + test_output[-500:]
131
+ if len(test_output) > 1000
132
+ else test_output,
133
+ },
134
+ "scores": {
135
+ "correctness": score,
136
+ },
137
+ }
138
+
139
+ status = "✓ PASS" if passed else f"✗ FAIL ({feedback_msg})"
140
+ print(f" [{instance_id}] {status} [steps: {agent_metrics.get('steps', 0)}]", flush=True)
141
+
142
+ # Show brief debug info on failure
143
+ if not passed:
144
+ # Extract last few lines of test output for quick debugging
145
+ output_lines = test_output.strip().split("\n")
146
+ # Get last 5 non-empty lines
147
+ relevant_lines = [line for line in output_lines if line.strip()][-5:]
148
+ if relevant_lines:
149
+ print(f" Last output: {relevant_lines[-1][:100]}", flush=True)
150
+
151
+ # Cleanup
152
+ harness.cleanup()
153
+
154
+ return score, output, side_info
155
+
156
+ except Exception as e:
157
+ # Handle setup failures (e.g., git checkout failed, Docker issues)
158
+ error_msg = str(e)
159
+ print(f" [{instance_id[:30]}] ✗ FAIL (setup_error)", flush=True)
160
+ print(f" Error: {error_msg[:100]}", flush=True)
161
+
162
+ # Return failure with error info
163
+ output = {"patch": "", "success": False, "steps": 0, "estimated_tokens": 0, "error": error_msg}
164
+ side_info = {
165
+ "Input": {
166
+ "Task ID": instance_id,
167
+ "Problem": task.get("problem_statement", "")[:200],
168
+ },
169
+ "Generated Outputs": {
170
+ "Patch": "",
171
+ "Agent Trace": "",
172
+ },
173
+ "Feedback": {
174
+ "Status": "setup_error",
175
+ "Test Output": f"Setup failed: {error_msg}",
176
+ },
177
+ "scores": {
178
+ "correctness": 0.0,
179
+ },
180
+ }
181
+
182
+ try:
183
+ harness.cleanup()
184
+ except Exception:
185
+ pass
186
+
187
+ return 0.0, output, side_info
188
+
189
+ finally:
190
+ release_harness(harness_idx)
191
+
192
+ def fitness_fn(
193
+ candidate: dict[str, str],
194
+ example: dict[str, Any],
195
+ ) -> tuple[float, dict[str, Any]]:
196
+ """Evaluate candidate on a single task (called per-example by GEPA).
197
+
198
+ Args:
199
+ candidate: Dict with 'skills' key
200
+ example: A single task instance dict
201
+
202
+ Returns:
203
+ (score, side_info) tuple
204
+ """
205
+ skills = candidate["skills"]
206
+ score, _output, side_info = process_single_task(example, skills, 0, 1)
207
+ return score, side_info
208
+
209
+ return fitness_fn