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,721 @@
1
+ """Production lifecycle for an official-GEPA optimization run."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import os
7
+ from pathlib import Path
8
+ from typing import Any
9
+ from uuid import uuid4
10
+
11
+ # Deliberately import—not copy—the frozen M1 transport, local artifacts, and
12
+ # runner contract. The connector owns only the official-GEPA adaptation.
13
+ from orizu_gepa.client import OrizuClient, OrizuEventSink
14
+ from orizu_gepa.local_log import LocalOptimizationLogger
15
+ from orizu_gepa.optimizer import TextGepaConfig, TextGepaResult
16
+ from orizu_gepa.runner import resolve_scorer_input_contract
17
+
18
+ from .adapter import RunnerEvaluationAdapter, ScorerContractError
19
+ from .callbacks import LifecycleHooks, OrizuCallback
20
+ from .engine import SeedValidationRefused, run_official_gepa, validate_seed_before_run
21
+ from .reflection import make_gepa_reflection_lm
22
+ from .stop_conditions import (
23
+ IterationBoundaryStopper,
24
+ MandatoryLoggingStopper,
25
+ MaxCandidateProposalsStopper,
26
+ )
27
+
28
+
29
+ class MandatoryEventSink:
30
+ """Write locally before POSTing; a failed POST makes the run fail honestly."""
31
+
32
+ def __init__(self, client: OrizuClient, run_id: str, local_logger: LocalOptimizationLogger | None):
33
+ self.client = client
34
+ self.run_id = run_id
35
+ self.local_logger = local_logger
36
+ # Connector events occupy odd slots. A promotion/status system event
37
+ # written by the server can take the even slot between any two GEPA
38
+ # callbacks without colliding with the next local event.
39
+ self.sequence = -1
40
+ self.failed = False
41
+ self._failed_status_reason: str | None = None
42
+ # Reuse the frozen sink's measured retry/backoff policy. It owns the
43
+ # local-first write and three total POST attempts; this adapter only
44
+ # reserves the even server sequence slot for a live promotion.
45
+ self._retry_sink = OrizuEventSink(
46
+ client,
47
+ run_id,
48
+ fail_on_log_error=True,
49
+ max_log_retries=2,
50
+ local_logger=local_logger,
51
+ )
52
+
53
+ def emit(self, event_type: str, payload: dict[str, Any], *, iteration: int | None = None,
54
+ candidate_id: str | None = None, parent_candidate_id: str | None = None) -> None:
55
+ next_sequence = self.sequence + 2
56
+ self._retry_sink.sequence = next_sequence - 1
57
+ try:
58
+ self._retry_sink.log_event(
59
+ event_type,
60
+ payload,
61
+ event_layer="core",
62
+ optimizer_family="gepa",
63
+ iteration=iteration,
64
+ candidate_id=candidate_id,
65
+ parent_candidate_id=parent_candidate_id,
66
+ )
67
+ self.sequence = next_sequence
68
+ except Exception as error:
69
+ self.sequence = next_sequence
70
+ self.failed = True
71
+ self._failed_status_reason = f"API logging failed: {error}"
72
+ # ``OrizuEventSink.log_event`` has already written this exact
73
+ # event locally before it attempted the POST. Do not duplicate its
74
+ # sequence record on the failure path.
75
+ self._record_failed_status()
76
+ raise
77
+
78
+ def _record_failed_status(self) -> None:
79
+ if self._failed_status_reason is None:
80
+ return
81
+ try:
82
+ self.client.update_run(
83
+ self.run_id,
84
+ status="failed",
85
+ metadata={"failure_reason": self._failed_status_reason},
86
+ )
87
+ except Exception:
88
+ # Keep the reason for the one later retry at the terminal boundary.
89
+ return
90
+ self._failed_status_reason = None
91
+
92
+ def retry_failed_status(self) -> None:
93
+ """Give a recovered API one terminal opportunity to learn failure."""
94
+ self._record_failed_status()
95
+
96
+ def run_note(self, note: dict[str, Any]) -> None:
97
+ """Production sink used by the merge translator, never a test callback."""
98
+ self.emit("run_note", note["payload"])
99
+
100
+ def promote_candidate(self, **kwargs: Any) -> str:
101
+ """Delegate promotion to the frozen client; its route owns sequence 2n."""
102
+ return self.client.promote_candidate(self.run_id, **kwargs)
103
+
104
+
105
+ def _required_environment() -> dict[str, str]:
106
+ names = [
107
+ "ORIZU_PROJECT", "ORIZU_OPTIMIZER_VERSION_ID",
108
+ "ORIZU_DATASET_VERSION_ID", "ORIZU_SPLIT_SET_ID", "ORIZU_SCORER_VERSION_ID",
109
+ "ORIZU_RUNNER_VERSION_ID", "ORIZU_CANDIDATE_RUNNER_DIR", "ORIZU_SCORER_RUNNER_DIR",
110
+ ]
111
+ if not os.environ.get("ORIZU_INSTRUCTION_SET_PROFILE_VERSION_ID"):
112
+ names.append("ORIZU_PROMPT_VERSION_ID")
113
+ missing = [name for name in names if not os.environ.get(name)]
114
+ if missing:
115
+ raise RuntimeError("missing connector environment: " + ", ".join(missing))
116
+ return {name: os.environ[name] for name in names}
117
+
118
+
119
+ def _optional_int(name: str, *, allow_zero: bool = False) -> int | None:
120
+ raw = os.environ.get(name)
121
+ if raw is None:
122
+ return None
123
+ try:
124
+ value = int(raw)
125
+ except ValueError as error:
126
+ raise RuntimeError(f"{name} must be an integer") from error
127
+ if value < 0 or (value == 0 and not allow_zero):
128
+ raise RuntimeError(f"{name} must be greater than zero")
129
+ return value
130
+
131
+
132
+ def _signed_int(name: str) -> int | None:
133
+ raw = os.environ.get(name)
134
+ if raw is None:
135
+ return None
136
+ try:
137
+ return int(raw)
138
+ except ValueError as error:
139
+ raise RuntimeError(f"{name} must be an integer") from error
140
+
141
+
142
+ def _optional_float(name: str) -> float | None:
143
+ raw = os.environ.get(name)
144
+ if raw is None:
145
+ return None
146
+ try:
147
+ return float(raw)
148
+ except ValueError as error:
149
+ raise RuntimeError(f"{name} must be a number") from error
150
+
151
+
152
+ def _enabled(name: str) -> bool:
153
+ return os.environ.get(name, "").strip().lower() in {"1", "true", "yes"}
154
+
155
+
156
+ def _num_threads_from_environment() -> int | str:
157
+ raw = os.environ.get("ORIZU_NUM_THREADS", "auto").strip()
158
+ if raw.lower() == "auto":
159
+ return "auto"
160
+ try:
161
+ value = int(raw)
162
+ except ValueError as error:
163
+ raise RuntimeError("ORIZU_NUM_THREADS must be a positive integer or 'auto'") from error
164
+ if value <= 0:
165
+ raise RuntimeError("ORIZU_NUM_THREADS must be a positive integer or 'auto'")
166
+ return value
167
+
168
+
169
+ def _metadata_from_environment() -> dict[str, Any]:
170
+ raw = os.environ.get("ORIZU_METADATA", "{}")
171
+ try:
172
+ value = json.loads(raw)
173
+ except json.JSONDecodeError as error:
174
+ raise RuntimeError("ORIZU_METADATA must be a JSON object") from error
175
+ if not isinstance(value, dict):
176
+ raise RuntimeError("ORIZU_METADATA must be a JSON object")
177
+ return value
178
+
179
+
180
+ def _json_object_environment(name: str) -> dict[str, Any]:
181
+ raw = os.environ.get(name, "{}")
182
+ try:
183
+ value = json.loads(raw)
184
+ except json.JSONDecodeError as error:
185
+ raise RuntimeError(f"{name} must be a JSON object") from error
186
+ if not isinstance(value, dict):
187
+ raise RuntimeError(f"{name} must be a JSON object")
188
+ return value
189
+
190
+
191
+ def build_config_from_environment() -> TextGepaConfig:
192
+ """Parse the ALI-1502 wrapper contract before client creation or spend."""
193
+ reflection_model = os.environ.get("ORIZU_REFLECTION_MODEL", TextGepaConfig.reflection_model)
194
+ reflection_max_tokens = _optional_int("ORIZU_REFLECTION_MAX_TOKENS")
195
+ if not reflection_model.startswith("openai/") and reflection_max_tokens is None:
196
+ raise RuntimeError("--reflection-max-tokens is required for Anthropic reflection models")
197
+ return TextGepaConfig(
198
+ budget=os.environ.get("ORIZU_BUDGET", "auto"),
199
+ max_metric_calls=_optional_int("ORIZU_MAX_METRIC_CALLS"),
200
+ max_iterations=_optional_int("ORIZU_MAX_ITERATIONS"),
201
+ max_full_evals=_optional_int("ORIZU_MAX_FULL_EVALS"),
202
+ max_candidate_proposals=_optional_int("ORIZU_MAX_CANDIDATE_PROPOSALS"),
203
+ minibatch_size=_optional_int("ORIZU_MINIBATCH_SIZE") or TextGepaConfig.minibatch_size,
204
+ num_threads=_num_threads_from_environment(),
205
+ reflection_model=reflection_model,
206
+ reflection_temperature=_optional_float("ORIZU_REFLECTION_TEMPERATURE"),
207
+ reflection_max_tokens=reflection_max_tokens,
208
+ reflection_retry_attempts=_optional_int("ORIZU_REFLECTION_RETRY_ATTEMPTS") or TextGepaConfig.reflection_retry_attempts,
209
+ reflection_http_timeout_seconds=_optional_int("ORIZU_REFLECTION_HTTP_TIMEOUT_SECONDS") or TextGepaConfig.reflection_http_timeout_seconds,
210
+ reflection_prompt_template=os.environ.get("ORIZU_REFLECTION_PROMPT_TEMPLATE"),
211
+ reflection_provider_settings=_json_object_environment("ORIZU_REFLECTION_PROVIDER_SETTINGS"),
212
+ epsilon=_optional_float("ORIZU_EPSILON") if os.environ.get("ORIZU_EPSILON") else TextGepaConfig.epsilon,
213
+ candidate_selection_strategy=os.environ.get("ORIZU_CANDIDATE_SELECTION_STRATEGY", TextGepaConfig.candidate_selection_strategy),
214
+ objective=os.environ.get("ORIZU_OBJECTIVE", TextGepaConfig.objective),
215
+ seed=_signed_int("ORIZU_SEED") if "ORIZU_SEED" in os.environ else TextGepaConfig.seed,
216
+ auto_promote=_enabled("ORIZU_AUTO_PROMOTE"),
217
+ promotion_label=os.environ.get("ORIZU_PROMOTION_LABEL"),
218
+ log_row_snapshots=_enabled("ORIZU_LOG_ROW_SNAPSHOTS"),
219
+ cache_evaluations=not _enabled("ORIZU_DISABLE_EVALUATION_CACHE"),
220
+ allow_degenerate_seed=_enabled("ORIZU_ALLOW_DEGENERATE_SEED"),
221
+ skip_perfect_parent_reflection=os.environ.get("ORIZU_SKIP_PERFECT_PARENT_REFLECTION", "1").strip().lower() not in {"0", "false", "no"},
222
+ )
223
+
224
+
225
+ def resolved_budget(config: TextGepaConfig, *, trainset_size: int, valset_size: int, num_components: int = 1):
226
+ """Resolve the frozen legacy budget once for display *and* enforcement."""
227
+ from orizu_gepa.optimizer import Budget
228
+
229
+ return Budget.from_config(
230
+ config,
231
+ trainset_size=trainset_size,
232
+ valset_size=valset_size,
233
+ num_components=num_components,
234
+ )
235
+
236
+
237
+ def selected_budget(config: TextGepaConfig, *, trainset_size: int, valset_size: int) -> tuple[str, int]:
238
+ """Return the display unit and exact limit that this launch selected."""
239
+ budget = resolved_budget(config, trainset_size=trainset_size, valset_size=valset_size)
240
+ return budget.budget_kind, budget.limit
241
+
242
+
243
+ def validate_launch_contract(
244
+ seed_candidate: dict[str, str],
245
+ *,
246
+ candidate_runner_dir: str | None = None,
247
+ scorer_runner_dir: str | None = None,
248
+ pinned_components: dict[str, Any] | None = None,
249
+ instruction_set_shape: list[str] | None = None,
250
+ auto_promote: bool = False,
251
+ ) -> None:
252
+ """Refuse M1-unsupported topology before creating a run row."""
253
+ if _enabled("ORIZU_USE_MERGE") or os.environ.get("ORIZU_MAX_MERGE_INVOCATIONS"):
254
+ raise RuntimeError("merge is unsupported by the official GEPA connector (ALI-1506)")
255
+ pinned = pinned_components or {}
256
+ if pinned:
257
+ raise RuntimeError("instruction_set_pinned_components_unsupported: " + ", ".join(sorted(pinned)))
258
+ if instruction_set_shape is not None:
259
+ missing = [key for key in instruction_set_shape if key not in seed_candidate]
260
+ extra = [key for key in seed_candidate if key not in instruction_set_shape]
261
+ if missing or extra:
262
+ raise RuntimeError("instruction_set_tuple_shape_mismatch: " + ", ".join(missing or extra))
263
+ if auto_promote and len(seed_candidate) != 1:
264
+ raise RuntimeError("instruction_set_candidate_promotion_unsupported")
265
+ if os.environ.get("ORIZU_SAMPLING_STRATEGY") or os.environ.get("ORIZU_SELECTION_STRATEGY"):
266
+ raise RuntimeError("P×N sampling/selection is unsupported by the official GEPA connector (ALI-1507)")
267
+ if candidate_runner_dir is None or scorer_runner_dir is None:
268
+ return
269
+ try:
270
+ verified_dirs = json.loads(os.environ.get("ORIZU_VERIFIED_RUNNER_DIRS", "[]"))
271
+ except json.JSONDecodeError as error:
272
+ raise RuntimeError("ORIZU_VERIFIED_RUNNER_DIRS must be a JSON array") from error
273
+ if not isinstance(verified_dirs, list) or not all(isinstance(value, str) for value in verified_dirs):
274
+ raise RuntimeError("ORIZU_VERIFIED_RUNNER_DIRS must be a JSON array")
275
+ for label, directory in (("candidate", candidate_runner_dir), ("scorer", scorer_runner_dir)):
276
+ if directory not in verified_dirs:
277
+ raise RuntimeError(
278
+ f"{label} runner directory {directory!r} was not verified against a registered runner version; "
279
+ "launch through `orizu optimizations run-gepa` (ADR-007 / ALI-1159)"
280
+ )
281
+
282
+
283
+ def require_durable_logging(sink: MandatoryEventSink, callback: OrizuCallback) -> None:
284
+ """Prevent GEPA's observational callback catcher from masking a POST failure."""
285
+ if sink.failed or callback.logging_failed:
286
+ raise RuntimeError("mandatory event logging failed during official GEPA execution")
287
+
288
+
289
+ def create_local_logger_from_environment(
290
+ *,
291
+ run_id: str,
292
+ env: dict[str, str],
293
+ prompt_context: Any,
294
+ scorer_context: Any,
295
+ trainset: list[Any],
296
+ valset: list[Any],
297
+ metadata: dict[str, Any],
298
+ ) -> LocalOptimizationLogger | None:
299
+ """Use the frozen logger exactly as legacy ``--no-local-log`` does."""
300
+ if _enabled("ORIZU_NO_LOCAL_LOG"):
301
+ return None
302
+ local_logger = LocalOptimizationLogger.create(os.environ.get("ORIZU_LOCAL_LOG_DIR", "logs"), run_id)
303
+ local_logger.write_context(project=env["ORIZU_PROJECT"], run_id=run_id, args=dict(env),
304
+ prompt_context=prompt_context, scorer_context=scorer_context,
305
+ trainset=trainset, valset=valset, metadata=metadata)
306
+ return local_logger
307
+
308
+
309
+ def _preflight_record_verdict(verdict: dict[str, Any], *, include_row_content: bool) -> dict[str, Any]:
310
+ """Keep refusal diagnosis without writing scorer/dataset content by default."""
311
+ if include_row_content:
312
+ return verdict
313
+ record = {**verdict, "row_evidence": []}
314
+ for original in verdict.get("row_evidence", []):
315
+ row = {"row_id": original.get("row_id"), "score": original.get("score")}
316
+ if original.get("scorer_error"):
317
+ row["error_class"] = "scorer_execution_failed"
318
+ elif original.get("execution_error"):
319
+ source = original.get("execution_error_source")
320
+ row["error_class"] = f"{source}_execution_failed" if source in {"candidate", "scorer"} else "runner_execution_failed"
321
+ else:
322
+ row["error_class"] = None
323
+ record["row_evidence"].append(row)
324
+ return record
325
+
326
+
327
+ def write_preflight_refusal_record(*, env: dict[str, str], verdict: dict[str, Any],
328
+ include_row_content: bool = False) -> str | None:
329
+ """Persist the evidence for a refusal that intentionally has no run row."""
330
+ # A refused launch has no remote event stream, but it still follows legacy
331
+ # --no-local-log semantics and never writes scorer/dataset content unless
332
+ # row snapshots were explicitly requested.
333
+ if _enabled("ORIZU_NO_LOCAL_LOG"):
334
+ return None
335
+ record_id = f"preflight-refused-{uuid4()}"
336
+ logger = LocalOptimizationLogger.create(os.environ.get("ORIZU_LOCAL_LOG_DIR", "logs"), record_id)
337
+ path = logger.write_preflight(
338
+ project=env["ORIZU_PROJECT"],
339
+ verdict=_preflight_record_verdict(verdict, include_row_content=include_row_content),
340
+ )
341
+ return str(path)
342
+
343
+
344
+ def _record_result_budget(*, budget: Any, result: Any, callback: OrizuCallback,
345
+ preflight_metric_calls: int) -> None:
346
+ """Reconcile GEPA's result counters with the pre-launch seed work."""
347
+ budget.used_metric_calls = preflight_metric_calls + int(result.total_metric_calls or 0)
348
+ # Official GEPA returns full-valset and metric counters in its result. Its
349
+ # completed iteration and proposal counts are emitted only through the
350
+ # callback trace we observed for this run.
351
+ budget.used_full_evals = int(result.num_full_val_evals or 0)
352
+ budget.used_candidate_proposals = callback.candidate_proposals
353
+ budget.used_iterations = callback.completed_iterations
354
+
355
+
356
+ def _is_budget_exhausted(*, config: TextGepaConfig, budget: Any,
357
+ metric_calls_used: int | None = None,
358
+ candidate_proposals: int | None = None) -> bool:
359
+ """Match legacy terminal semantics, not merely a zero remaining display.
360
+
361
+ ``--max-iterations`` is an intentional completion boundary in the frozen
362
+ loop. Every metric/candidate budget that reaches zero while more work was
363
+ otherwise possible is a pause, which must never auto-promote.
364
+ """
365
+ if config.max_iterations is not None:
366
+ return False
367
+ if budget.budget_kind == "max_metric_calls":
368
+ used = budget.metric_budget_used if metric_calls_used is None else metric_calls_used
369
+ return used >= budget.limit
370
+ if budget.budget_kind == "max_candidate_proposals":
371
+ used = budget.used_candidate_proposals if candidate_proposals is None else candidate_proposals
372
+ return used >= budget.limit
373
+ return budget.remaining == 0
374
+
375
+
376
+ def _terminal_budget_exhausted(event: dict[str, Any], *, config: TextGepaConfig, budget: Any,
377
+ preflight_metric_calls: int, candidate_proposals: int) -> bool:
378
+ """Classify the measured official-GEPA terminal callback before completion emits."""
379
+ metric_calls = event.get("total_metric_calls")
380
+ if not isinstance(metric_calls, int):
381
+ return False
382
+ return _is_budget_exhausted(
383
+ config=config,
384
+ budget=budget,
385
+ metric_calls_used=preflight_metric_calls + metric_calls,
386
+ candidate_proposals=candidate_proposals,
387
+ )
388
+
389
+
390
+ def _result_summary(*, run_id: str, best_id: str, best_score: float, seed_score: float,
391
+ promoted_prompt_version_id: str | None, budget: Any,
392
+ local_logger: LocalOptimizationLogger | None) -> dict[str, Any]:
393
+ return {
394
+ "optimization_run_id": run_id,
395
+ "best_candidate_id": best_id,
396
+ "best_score": best_score,
397
+ "seed_score": seed_score,
398
+ "promoted_prompt_version_id": promoted_prompt_version_id,
399
+ "budget": budget.to_payload(),
400
+ "local_log_dir": str(local_logger.path) if local_logger is not None else None,
401
+ }
402
+
403
+
404
+ def run_from_environment() -> dict[str, Any]:
405
+ """Run official GEPA over the verified legacy runner subprocess contract.
406
+
407
+ ALI-1502 supplies these variables after verifying runner bytes. Scorer
408
+ contract resolution and seed validation intentionally happen before
409
+ ``start_run`` so either refusal leaves no stranded running record.
410
+ """
411
+ env = _required_environment()
412
+ train_split = os.environ.get("ORIZU_TRAIN_SPLIT", "train")
413
+ validation_split = os.environ.get("ORIZU_VALIDATION_SPLIT", "validation")
414
+ config = build_config_from_environment()
415
+ client = OrizuClient.from_env()
416
+ profile_version_id = os.environ.get("ORIZU_INSTRUCTION_SET_PROFILE_VERSION_ID")
417
+ prompt_context, trainset = client.fetch_exec_context(
418
+ prompt_version_id=os.environ.get("ORIZU_PROMPT_VERSION_ID"), runner_version_id=env["ORIZU_RUNNER_VERSION_ID"],
419
+ dataset_version_id=env["ORIZU_DATASET_VERSION_ID"], split_set_id=env["ORIZU_SPLIT_SET_ID"], split=train_split,
420
+ instruction_set_profile_version_id=profile_version_id,
421
+ )
422
+ _, valset = client.fetch_exec_context(
423
+ prompt_version_id=os.environ.get("ORIZU_PROMPT_VERSION_ID"), runner_version_id=env["ORIZU_RUNNER_VERSION_ID"],
424
+ dataset_version_id=env["ORIZU_DATASET_VERSION_ID"], split_set_id=env["ORIZU_SPLIT_SET_ID"], split=validation_split,
425
+ instruction_set_profile_version_id=profile_version_id,
426
+ )
427
+ scorer_context, _ = client.fetch_scorer_exec_context(
428
+ scorer_version_id=env["ORIZU_SCORER_VERSION_ID"], runner_version_id=os.environ.get("ORIZU_SCORER_RUNNER_VERSION_ID"),
429
+ dataset_version_id=env["ORIZU_DATASET_VERSION_ID"], split_set_id=env["ORIZU_SPLIT_SET_ID"], split=validation_split,
430
+ )
431
+ raw_scorer_candidate_field = os.environ.get("ORIZU_SCORER_CANDIDATE_FIELD")
432
+ try:
433
+ scorer_input_contract, scorer_candidate_output_field = resolve_scorer_input_contract(
434
+ env["ORIZU_SCORER_RUNNER_DIR"],
435
+ input_contract=os.environ.get("ORIZU_SCORER_INPUT_CONTRACT"),
436
+ candidate_field=raw_scorer_candidate_field,
437
+ )
438
+ except RuntimeError as error:
439
+ raise RuntimeError(
440
+ f"scorer-contract failure before launch for {env['ORIZU_SCORER_VERSION_ID']}: {error}"
441
+ ) from error
442
+ hooks = LifecycleHooks()
443
+ instruction_set = prompt_context.instruction_set or {}
444
+ shape = instruction_set.get("shape") if isinstance(instruction_set.get("shape"), list) else None
445
+ components = instruction_set.get("components") if isinstance(instruction_set.get("components"), dict) else None
446
+ if profile_version_id and (not shape or not components):
447
+ raise RuntimeError("instruction_set_tuple_incomplete")
448
+ seed_candidate = ({key: components[key] for key in shape if isinstance(components.get(key), str)}
449
+ if profile_version_id and shape and components else {"prompt": prompt_context.body or ""})
450
+ validate_launch_contract(
451
+ seed_candidate,
452
+ candidate_runner_dir=env["ORIZU_CANDIDATE_RUNNER_DIR"],
453
+ scorer_runner_dir=env["ORIZU_SCORER_RUNNER_DIR"],
454
+ pinned_components=instruction_set.get("pinned_components") if profile_version_id and isinstance(instruction_set, dict) else None,
455
+ instruction_set_shape=shape if profile_version_id else None,
456
+ auto_promote=config.auto_promote,
457
+ )
458
+ preflight_adapter = RunnerEvaluationAdapter(
459
+ candidate_runner_dir=env["ORIZU_CANDIDATE_RUNNER_DIR"], scorer_runner_dir=env["ORIZU_SCORER_RUNNER_DIR"],
460
+ run_id=None, prompt_context=prompt_context, scorer_context=scorer_context,
461
+ scorer_input_contract=scorer_input_contract,
462
+ scorer_candidate_field=raw_scorer_candidate_field,
463
+ num_threads=config.num_threads, minibatch_size=config.minibatch_size, validation_count=len(valset),
464
+ )
465
+ try:
466
+ preflight_verdict = validate_seed_before_run(
467
+ seed_candidate=seed_candidate, valset=valset, adapter=preflight_adapter,
468
+ hooks=hooks, allow_degenerate_seed=config.allow_degenerate_seed,
469
+ )
470
+ except SeedValidationRefused as error:
471
+ record_path = write_preflight_refusal_record(
472
+ env=env, verdict=error.verdict, include_row_content=config.log_row_snapshots,
473
+ )
474
+ raise SeedValidationRefused(error.verdict, preflight_record_path=record_path) from error
475
+ except ScorerContractError as error:
476
+ raise RuntimeError(f"scorer-contract failure before launch for {env['ORIZU_SCORER_VERSION_ID']}: {error}") from error
477
+ preflight_metric_calls = int(preflight_verdict.get("preflight_metric_calls", 0))
478
+ budget = resolved_budget(config, trainset_size=len(trainset), valset_size=len(valset), num_components=len(seed_candidate))
479
+ budget_kind, budget_limit = budget.budget_kind, budget.limit
480
+ metadata = {
481
+ **_metadata_from_environment(),
482
+ "optimizer_family": "gepa", "optimizer_package": "orizu-gepa",
483
+ "engine": "official",
484
+ "scorer_input_contract": scorer_input_contract,
485
+ "scorer_candidate_output_field": scorer_candidate_output_field,
486
+ "reflection_lm": config.reflection_model,
487
+ "inference_lm": prompt_context.provider_settings.get("model"),
488
+ "scorer_lm": scorer_context.provider_settings.get("model"),
489
+ "dataset_size": len(trainset) + len(valset), "train_count": len(trainset), "validation_count": len(valset),
490
+ "num_threads": preflight_adapter.num_threads_plan.to_payload(),
491
+ }
492
+ is_multi_component_instruction_set = bool(
493
+ profile_version_id and isinstance(shape, list) and len(shape) > 1
494
+ )
495
+ run_id = client.start_run(
496
+ project=env["ORIZU_PROJECT"], optimizer_version_id=env["ORIZU_OPTIMIZER_VERSION_ID"],
497
+ # A set of one is the legacy prompt subject. Only a genuine component
498
+ # map is attributed to the profile version instead of a prompt version.
499
+ prompt_version_id=(
500
+ None if is_multi_component_instruction_set else prompt_context.prompt_version_id
501
+ ),
502
+ instruction_set_profile_version_id=(
503
+ profile_version_id if is_multi_component_instruction_set else None
504
+ ),
505
+ scorer_version_id=scorer_context.scorer_version_id or env["ORIZU_SCORER_VERSION_ID"],
506
+ dataset_version_id=env["ORIZU_DATASET_VERSION_ID"], split_set_id=env["ORIZU_SPLIT_SET_ID"],
507
+ train_split=train_split, validation_split=validation_split, metadata=metadata,
508
+ model_config_settings_version_id=instruction_set.get("model_config_settings_version_id") if isinstance(instruction_set, dict) else None,
509
+ )
510
+ print(json.dumps({"optimization_run_id": run_id}), flush=True)
511
+ # The frozen sink treats ``None`` as no local artifact writer. Keep that
512
+ # legacy behavior rather than maintaining a connector-local no-op logger.
513
+ local_logger = create_local_logger_from_environment(
514
+ run_id=run_id, env=env, prompt_context=prompt_context, scorer_context=scorer_context,
515
+ trainset=trainset, valset=valset, metadata=metadata,
516
+ )
517
+ if local_logger is not None:
518
+ print(f"[orizu-gepa] local log: {local_logger.path}", flush=True)
519
+ if local_logger is not None and preflight_verdict.get("warnings"):
520
+ local_logger.write_preflight(
521
+ project=env["ORIZU_PROJECT"],
522
+ verdict=_preflight_record_verdict(
523
+ preflight_verdict, include_row_content=config.log_row_snapshots,
524
+ ),
525
+ kind="preflight_warning",
526
+ )
527
+ sink = MandatoryEventSink(client, run_id, local_logger)
528
+ def terminal_budget_exhausted(event: dict[str, Any]) -> bool:
529
+ return _terminal_budget_exhausted(
530
+ event,
531
+ config=config,
532
+ budget=budget,
533
+ preflight_metric_calls=preflight_metric_calls,
534
+ candidate_proposals=callback.candidate_proposals,
535
+ )
536
+
537
+ callback = OrizuCallback(
538
+ sink,
539
+ run_id,
540
+ hooks,
541
+ higher_is_better=scorer_context.higher_is_better,
542
+ budget_kind=budget_kind,
543
+ budget_limit=budget_limit,
544
+ approx_metric_call_limit=budget.approx_metric_call_limit,
545
+ max_payload_chars=_optional_int("ORIZU_MAX_PAYLOAD_CHARS") or 16000,
546
+ log_row_snapshots=config.log_row_snapshots,
547
+ terminal_budget_exhausted=terminal_budget_exhausted,
548
+ )
549
+ adapter = RunnerEvaluationAdapter(
550
+ candidate_runner_dir=env["ORIZU_CANDIDATE_RUNNER_DIR"], scorer_runner_dir=env["ORIZU_SCORER_RUNNER_DIR"],
551
+ run_id=run_id, prompt_context=prompt_context, scorer_context=scorer_context,
552
+ scorer_input_contract=scorer_input_contract,
553
+ scorer_candidate_field=raw_scorer_candidate_field,
554
+ num_threads=config.num_threads, minibatch_size=config.minibatch_size, validation_count=len(valset),
555
+ )
556
+ stoppers = [MandatoryLoggingStopper(lambda: sink.failed or callback.logging_failed)]
557
+ if config.max_iterations is not None:
558
+ stoppers.append(IterationBoundaryStopper(config.max_iterations))
559
+ if config.max_candidate_proposals is not None:
560
+ stoppers.append(MaxCandidateProposalsStopper(
561
+ config.max_candidate_proposals,
562
+ proposal_count=lambda: callback.candidate_proposals,
563
+ ))
564
+ try:
565
+ proposal_observability: Any | None = None
566
+ proposal_budget: Any | None = None
567
+ custom_candidate_proposer = None
568
+ if os.environ.get("ORIZU_CANDIDATE_PROPOSER") == "skilled-proposer":
569
+ # DSPy and skilled-proposer are installed only in the manager
570
+ # published opt-in venv. Keeping this import inside the exact
571
+ # selection branch preserves the established no-flag process.
572
+ from .skilled_proposer_bridge import (
573
+ ProposalCallBudget,
574
+ ProposalObservability,
575
+ make_skilled_proposer_from_environment,
576
+ )
577
+ artifact_root = (local_logger.directory if local_logger is not None
578
+ else Path.cwd() / ".orizu" / "proposal-observability" / run_id)
579
+ proposal_budget = ProposalCallBudget(
580
+ max_calls=_optional_int("ORIZU_PROPOSAL_MAX_CALLS"),
581
+ max_tokens=_optional_int("ORIZU_PROPOSAL_MAX_TOKENS"),
582
+ )
583
+ proposal_observability = ProposalObservability(
584
+ event_log_root=artifact_root / "proposal-observability",
585
+ durable_failure_root=artifact_root / "proposal-failures",
586
+ budget=proposal_budget,
587
+ )
588
+ custom_candidate_proposer = make_skilled_proposer_from_environment(
589
+ config=config,
590
+ observability=proposal_observability,
591
+ budget=proposal_budget,
592
+ )
593
+ if custom_candidate_proposer is None:
594
+ raise RuntimeError("ALI_1505_PROPOSER_FACTORY_REFUSED_SELECTED_VALUE")
595
+
596
+ reflection_lm = None if custom_candidate_proposer is not None else make_gepa_reflection_lm(
597
+ context_supplier=lambda: (
598
+ adapter.last_candidate or seed_candidate,
599
+ adapter.last_row_evaluations,
600
+ ),
601
+ config=config,
602
+ failure_reporter=callback.record_reflection_failure,
603
+ success_reporter=callback.record_reflection_prompt,
604
+ )
605
+ # ``Budget.from_config`` turns every default/preset into a concrete
606
+ # metric limit. Passing that limit is the spend-safety boundary; it is
607
+ # never merely display metadata.
608
+ effective_metric_limit = budget.limit if budget.budget_kind == "max_metric_calls" else None
609
+ result = run_official_gepa(seed_candidate=seed_candidate, trainset=trainset, valset=valset, adapter=adapter,
610
+ callback=callback, hooks=hooks, max_metric_calls=effective_metric_limit,
611
+ stop_callbacks=stoppers, allow_degenerate_seed=config.allow_degenerate_seed,
612
+ reflection_lm=reflection_lm, seed_already_validated=True,
613
+ custom_candidate_proposer=custom_candidate_proposer,
614
+ proposal_budget=proposal_budget,
615
+ config=config,
616
+ module_selector=os.environ.get("ORIZU_COMPONENT_SELECTOR", "round_robin").replace("-", "_"))
617
+ require_durable_logging(sink, callback)
618
+ best_id = str(result.best_idx)
619
+ best_score = float(result.val_aggregate_scores[result.best_idx])
620
+ if not scorer_context.higher_is_better:
621
+ best_score = 1.0 - best_score
622
+ seed_score = (float(result.val_aggregate_scores[0]) if scorer_context.higher_is_better
623
+ else 1.0 - float(result.val_aggregate_scores[0]))
624
+ promoted_prompt_version_id = None
625
+ _record_result_budget(
626
+ budget=budget, result=result, callback=callback,
627
+ preflight_metric_calls=preflight_metric_calls,
628
+ )
629
+ budget_exhausted = _is_budget_exhausted(config=config, budget=budget)
630
+ if budget_exhausted:
631
+ sink.emit(
632
+ "budget_exhausted",
633
+ {
634
+ "stage": "optimization_completed",
635
+ "budget": budget.to_payload(),
636
+ "decision_rule": "budget is checked only between iterations",
637
+ },
638
+ iteration=None,
639
+ candidate_id=best_id,
640
+ parent_candidate_id=None,
641
+ )
642
+ sink.emit(
643
+ "run_paused",
644
+ {
645
+ "reason": "budget_exhausted",
646
+ "best_candidate_id": best_id,
647
+ "best_validation_score": best_score,
648
+ "seed_validation_score": seed_score,
649
+ "budget": budget.to_payload(),
650
+ },
651
+ iteration=None,
652
+ candidate_id=best_id,
653
+ parent_candidate_id=None,
654
+ )
655
+ elif config.auto_promote and best_id != "0":
656
+ prompt_id = getattr(prompt_context, "prompt_id", None)
657
+ if not prompt_id:
658
+ raise RuntimeError("Cannot auto-promote without prompt_id in prompt context")
659
+ best_candidate = result.candidates[result.best_idx]
660
+ promoted_prompt_version_id = sink.promote_candidate(
661
+ candidate_id=best_id,
662
+ prompt_id=prompt_id,
663
+ parent_prompt_version_id=prompt_context.prompt_version_id,
664
+ body=next(iter(best_candidate.values())),
665
+ body_kind=prompt_context.body_kind,
666
+ provider_settings=prompt_context.provider_settings,
667
+ runner_version_id=env["ORIZU_RUNNER_VERSION_ID"],
668
+ label=config.promotion_label,
669
+ )
670
+ if local_logger is not None:
671
+ best_candidate = result.candidates[result.best_idx]
672
+ local_logger.write_result(TextGepaResult(
673
+ run_id=run_id,
674
+ best_candidate_id=best_id,
675
+ best_candidate_text=(best_candidate if len(best_candidate) > 1
676
+ else next(iter(best_candidate.values()))),
677
+ best_score=best_score,
678
+ seed_score=seed_score,
679
+ promoted_prompt_version_id=promoted_prompt_version_id,
680
+ budget=budget,
681
+ ))
682
+ if proposal_observability is not None:
683
+ proposal_observability.write_terminal_lm_stats_artifact()
684
+ else:
685
+ local_logger.write_lm_stats(
686
+ total_cost=float(getattr(reflection_lm, "total_cost", 0.0)),
687
+ total_tokens_in=int(getattr(reflection_lm, "total_tokens_in", 0)),
688
+ total_tokens_out=int(getattr(reflection_lm, "total_tokens_out", 0)),
689
+ total_tokens=int(getattr(reflection_lm, "total_tokens", 0)),
690
+ )
691
+ if budget_exhausted:
692
+ client.update_run(
693
+ run_id,
694
+ status="paused",
695
+ best_score=best_score,
696
+ best_candidate_id=best_id,
697
+ metadata={
698
+ "seed_score": seed_score,
699
+ "budget": budget.to_payload(),
700
+ "pause_reason": "budget_exhausted",
701
+ },
702
+ )
703
+ else:
704
+ client.update_run(run_id, status="succeeded", best_score=best_score, best_candidate_id=best_id,
705
+ result_prompt_version_id=promoted_prompt_version_id)
706
+ summary = _result_summary(
707
+ run_id=run_id, best_id=best_id, best_score=best_score, seed_score=seed_score,
708
+ promoted_prompt_version_id=promoted_prompt_version_id, budget=budget,
709
+ local_logger=local_logger,
710
+ )
711
+ return summary
712
+ except Exception as error:
713
+ if sink.failed:
714
+ sink.retry_failed_status()
715
+ else:
716
+ client.update_run(
717
+ run_id,
718
+ status="failed",
719
+ metadata={"failure_reason": str(error)},
720
+ )
721
+ raise