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,666 @@
1
+ """Production GEPA callback sink and lifecycle hooks."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Callable
6
+ import hashlib
7
+ import json
8
+ from typing import Any
9
+
10
+ from orizu_gepa.optimizer import RowEvaluation
11
+
12
+ from .translator import translate_callback
13
+
14
+
15
+ def _bounded(value: Any, limit: int) -> tuple[Any, bool]:
16
+ """Cap disclosure values before they enter the mandatory event transport."""
17
+ if isinstance(value, str):
18
+ if len(value) <= limit:
19
+ return value, False
20
+ return value[:limit] + "…[truncated]", True
21
+ if isinstance(value, (dict, list)):
22
+ rendered = json.dumps(value, ensure_ascii=False, sort_keys=True)
23
+ if len(rendered) <= limit:
24
+ return value, False
25
+ return rendered[:limit] + "…[truncated]", True
26
+ return value, False
27
+
28
+
29
+ def _redacted(name: str, value: Any, *, include_text: bool) -> dict[str, Any]:
30
+ if include_text:
31
+ return {name: value}
32
+ rendered = json.dumps(value, ensure_ascii=False, sort_keys=True, default=str)
33
+ return {f"{name}_sha256": hashlib.sha256(rendered.encode()).hexdigest(), f"{name}_redacted": True}
34
+
35
+
36
+ class LifecycleHooks:
37
+ """Named extension points; callbacks receive immutable observation data."""
38
+
39
+ def __init__(self) -> None:
40
+ self._hooks: dict[str, list[Callable[[dict[str, Any]], None]]] = {}
41
+
42
+ def add(self, name: str, hook: Callable[[dict[str, Any]], None]) -> None:
43
+ self._hooks.setdefault(name, []).append(hook)
44
+
45
+ def emit(self, name: str, payload: dict[str, Any]) -> None:
46
+ for hook in self._hooks.get(name, []):
47
+ hook(payload)
48
+
49
+
50
+ class OrizuCallback:
51
+ """Official callback object wired to the mandatory production event sink."""
52
+
53
+ def __init__(self, sink: Any, run_id: str, hooks: LifecycleHooks | None = None,
54
+ higher_is_better: bool = True, budget_kind: str = "max_metric_calls",
55
+ budget_limit: int | None = None, max_payload_chars: int = 16000,
56
+ log_row_snapshots: bool = False, approx_metric_call_limit: int | None = None,
57
+ terminal_budget_exhausted: Callable[[dict[str, Any]], bool] | None = None,
58
+ evaluation_adapter: Any | None = None):
59
+ self.sink = sink
60
+ self.run_id = run_id
61
+ self.hooks = hooks or LifecycleHooks()
62
+ self.higher_is_better = higher_is_better
63
+ self.budget_kind = budget_kind
64
+ self.budget_limit = budget_limit
65
+ self.max_payload_chars = max_payload_chars
66
+ self.log_row_snapshots = log_row_snapshots
67
+ self.approx_metric_call_limit = approx_metric_call_limit
68
+ self._terminal_budget_exhausted = terminal_budget_exhausted
69
+ # GEPA does not issue evaluation callbacks for full valset runs. Its
70
+ # adapter is therefore the only authoritative row-id source at the
71
+ # following on_valset_evaluated callback.
72
+ self.evaluation_adapter = evaluation_adapter
73
+ self.validation_row_ids: list[str] = []
74
+ self.logging_failed = False
75
+ self._selected_parent_by_iteration: dict[int, str] = {}
76
+ self._proposals_by_iteration: dict[int, list[dict[str, Any]]] = {}
77
+ self._parent_minibatches: dict[int, list[dict[str, Any]]] = {}
78
+ # A candidate-indexed evaluation can be either a parent minibatch or
79
+ # the row evidence preceding GEPA's authoritative valset callback.
80
+ # Hold it until callback identity, never its row count, decides.
81
+ self._pending_candidate_evaluations: dict[int, list[dict[str, Any]]] = {}
82
+ # Child train rows have no GEPA candidate id until its acceptance or
83
+ # rejection callback. Keep both wire payload and durable row evidence
84
+ # together until that authoritative id arrives.
85
+ self._child_minibatches: dict[int, list[dict[str, Any]]] = {}
86
+ self._seed_valset_rows: list[dict[str, Any]] = []
87
+ self._evaluation_row_ids: dict[int, list[list[str]]] = {}
88
+ self._evaluation_inputs: dict[int, list[list[Any]]] = {}
89
+ self._reflection_contexts: dict[int, dict[str, Any]] = {}
90
+ # GEPAResult deliberately exposes full-validation and metric counts,
91
+ # but iteration/proposal counts live only in its callback trace.
92
+ self.completed_iterations = 0
93
+ self.candidate_proposals = 0
94
+
95
+ def _external_score(self, value: Any) -> Any:
96
+ if not isinstance(value, (int, float)):
97
+ return value
98
+ return value if self.higher_is_better else 1.0 - value
99
+
100
+ def _emit(self, callback_name: str, event: dict[str, Any]) -> None:
101
+ try:
102
+ translated = translate_callback(
103
+ callback_name,
104
+ event,
105
+ run_id=self.run_id,
106
+ last_server_sequence=self.sink.sequence,
107
+ max_payload_chars=self.max_payload_chars,
108
+ budget_kind=self.budget_kind,
109
+ budget_limit=self.budget_limit,
110
+ approx_metric_call_limit=self.approx_metric_call_limit,
111
+ )
112
+ self.sink.emit(translated["eventType"], translated["payload"],
113
+ iteration=translated["iteration"], candidate_id=translated["candidateId"],
114
+ parent_candidate_id=translated["parentCandidateId"])
115
+ except Exception:
116
+ self.logging_failed = True
117
+ raise
118
+
119
+ def _write_local(self, write: Any) -> None:
120
+ """Make local-artifact failures visible to the terminal run guard."""
121
+ try:
122
+ write()
123
+ except Exception:
124
+ self.logging_failed = True
125
+ raise
126
+
127
+ def on_optimization_start(self, event: dict[str, Any]) -> None:
128
+ self._emit("on_optimization_start", event)
129
+
130
+ def on_iteration_start(self, event: dict[str, Any]) -> None:
131
+ self._emit("on_iteration_start", event)
132
+
133
+ def _flush_pending_parent_minibatches(self, iteration: int) -> None:
134
+ """Publish candidate-indexed work once it was not identified as valset work."""
135
+ pending = self._pending_candidate_evaluations.pop(iteration, [])
136
+ for item in pending:
137
+ candidate_id = item["candidate_id"]
138
+ payload = item["payload"]
139
+ self._parent_minibatches.setdefault(iteration, []).append(payload)
140
+ self.sink.emit(
141
+ "parent_minibatch_completed",
142
+ payload,
143
+ iteration=iteration,
144
+ candidate_id=self._selected_parent_by_iteration.get(iteration, candidate_id),
145
+ )
146
+ local_logger = getattr(self.sink, "local_logger", None)
147
+ if local_logger is not None:
148
+ self._write_local(lambda: local_logger.append_evaluations(
149
+ stage="parent_minibatch", split="train", iteration=iteration,
150
+ candidate_id=candidate_id, parent_candidate_id=item["parent_candidate_id"],
151
+ results=item["results"],
152
+ ))
153
+
154
+ def on_candidate_selected(self, event: dict[str, Any]) -> None:
155
+ iteration = int(event["iteration"])
156
+ self._selected_parent_by_iteration[iteration] = str(event["candidate_idx"])
157
+
158
+ def on_minibatch_sampled(self, event: dict[str, Any]) -> None:
159
+ self._emit("on_minibatch_sampled", event)
160
+
161
+ def on_evaluation_start(self, event: dict[str, Any]) -> None:
162
+ inputs = list(event.get("inputs") or [])
163
+ row_ids = []
164
+ for index, value in enumerate(inputs):
165
+ row_ids.append(str(getattr(value, "id", value.get("id", index) if isinstance(value, dict) else index)))
166
+ self._evaluation_row_ids.setdefault(int(event["iteration"]), []).append(row_ids)
167
+ self._evaluation_inputs.setdefault(int(event["iteration"]), []).append(inputs)
168
+
169
+ def on_evaluation_end(self, event: dict[str, Any]) -> None:
170
+ iteration = int(event["iteration"])
171
+ raw_scores = event.get("scores") or []
172
+ outputs = event.get("outputs") or []
173
+ trajectories = event.get("trajectories") or []
174
+ row_ids = (self._evaluation_row_ids.get(iteration) or [[]]).pop(0)
175
+ inputs = (self._evaluation_inputs.get(iteration) or [[]]).pop(0)
176
+ rows = []
177
+ local_results: list[RowEvaluation] = []
178
+ scores = []
179
+ for index, score in enumerate(raw_scores):
180
+ output = outputs[index] if index < len(outputs) else None
181
+ trace = trajectories[index] if index < len(trajectories) else {}
182
+ trace = trace if isinstance(trace, dict) else {}
183
+ output_info = output if isinstance(output, dict) else {}
184
+ external_score = output_info.get("raw_score", self._external_score(score))
185
+ scores.append(external_score)
186
+ actual_output = output_info.get("output", output)
187
+ bounded_output, output_truncated = _bounded(actual_output, self.max_payload_chars)
188
+ row = {
189
+ "row_id": output_info.get("row_id", row_ids[index] if index < len(row_ids) else str(index)),
190
+ "score": external_score,
191
+ "feedback": output_info.get("feedback", trace.get("Feedback")),
192
+ "error": output_info.get("error", trace.get("error")),
193
+ "output": bounded_output,
194
+ }
195
+ for field in ("latency_ms", "token_in", "token_out", "cost_usd", "cached", "error_source"):
196
+ if field in output_info:
197
+ row[field] = output_info[field]
198
+ if output_truncated:
199
+ row["output_truncated"] = True
200
+ rows.append(row)
201
+ source = inputs[index] if index < len(inputs) else None
202
+ source_row = getattr(source, "row", source if isinstance(source, dict) else {})
203
+ local_results.append(RowEvaluation(
204
+ row_id=str(row["row_id"]),
205
+ row=source_row if isinstance(source_row, dict) else {},
206
+ output=actual_output,
207
+ score=float(external_score) if isinstance(external_score, (int, float)) else 0.0,
208
+ feedback=row["feedback"],
209
+ scorer_response=output_info.get("scorer_output"),
210
+ latency_ms=output_info.get("latency_ms"),
211
+ token_in=output_info.get("token_in"),
212
+ token_out=output_info.get("token_out"),
213
+ cost_usd=output_info.get("cost_usd"),
214
+ error=row["error"],
215
+ cached=bool(output_info.get("cached", False)),
216
+ error_source=output_info.get("error_source"),
217
+ ))
218
+ payload = {"score_mean": (sum(scores) / len(scores)) if scores else None,
219
+ "score_total": sum(scores), "row_results": rows}
220
+ is_seed_baseline = iteration == 0 and bool(event.get("is_seed_candidate"))
221
+ if is_seed_baseline:
222
+ # The following valset callback is the authoritative full-eval
223
+ # identity; retain this row evidence only to enrich its outputs.
224
+ self._seed_valset_rows = rows
225
+ elif event.get("candidate_idx") is not None:
226
+ parent_ids = event.get("parent_ids") or []
227
+ self._pending_candidate_evaluations.setdefault(iteration, []).append({
228
+ "candidate_id": str(event["candidate_idx"]),
229
+ "payload": payload,
230
+ "results": local_results,
231
+ "parent_candidate_id": str(parent_ids[0]) if parent_ids else None,
232
+ })
233
+ else:
234
+ parent_ids = event.get("parent_ids") or []
235
+ self._child_minibatches.setdefault(iteration, []).append({
236
+ "payload": payload,
237
+ "results": local_results,
238
+ "parent_candidate_id": str(parent_ids[0]) if parent_ids else None,
239
+ })
240
+
241
+ def on_evaluation_skipped(self, event: dict[str, Any]) -> None:
242
+ self._flush_pending_parent_minibatches(int(event["iteration"]))
243
+ self._emit("on_evaluation_skipped", event)
244
+
245
+ def on_reflective_dataset_built(self, event: dict[str, Any]) -> None:
246
+ self._emit("on_reflective_dataset_built", event)
247
+
248
+ def on_proposal_start(self, event: dict[str, Any]) -> None:
249
+ iteration = int(event["iteration"])
250
+ self._flush_pending_parent_minibatches(iteration)
251
+ parent_candidate = event.get("parent_candidate")
252
+ self._reflection_contexts[iteration] = {
253
+ "iteration": iteration,
254
+ "parent_candidate_id": self._selected_parent_by_iteration.get(iteration),
255
+ "components_to_update": list(event.get("components") or []),
256
+ "parent_candidate": dict(parent_candidate) if isinstance(parent_candidate, dict) else None,
257
+ }
258
+ self._emit("on_proposal_start", event)
259
+
260
+ def record_reflection_failure(
261
+ self,
262
+ *,
263
+ error: Exception,
264
+ gepa_prompt: str,
265
+ parent_text: str,
266
+ parent_results: list[Any],
267
+ ) -> None:
268
+ """Persist an exception before GEPA's safe proposal helper can swallow it."""
269
+ latest_iteration = next(reversed(self._reflection_contexts), None)
270
+ context = self._reflection_contexts.get(latest_iteration, {})
271
+ iteration = context.get("iteration")
272
+ parent_candidate_id = context.get("parent_candidate_id")
273
+ row_ids = [str(getattr(result, "row_id", index)) for index, result in enumerate(parent_results)]
274
+ context_chars = len(json.dumps(parent_results, ensure_ascii=False, default=str))
275
+ payload = {
276
+ "error_type": type(error).__name__,
277
+ "message": str(error),
278
+ "gepa_prompt_chars": len(gepa_prompt),
279
+ "parent_text_chars": len(parent_text),
280
+ "parent_result_count": len(parent_results),
281
+ "parent_context_chars": context_chars,
282
+ }
283
+ self.sink.emit(
284
+ "reflection_failed",
285
+ payload,
286
+ iteration=iteration,
287
+ candidate_id=parent_candidate_id,
288
+ parent_candidate_id=parent_candidate_id,
289
+ )
290
+ local_logger = getattr(self.sink, "local_logger", None)
291
+ if local_logger is not None:
292
+ self._write_local(lambda: local_logger.append_reflection_failure(
293
+ iteration=iteration,
294
+ parent_candidate_id=parent_candidate_id,
295
+ row_ids=row_ids,
296
+ error_type=type(error).__name__,
297
+ error_message=str(error),
298
+ gepa_prompt_chars=len(gepa_prompt),
299
+ parent_text_chars=len(parent_text),
300
+ parent_result_count=len(parent_results),
301
+ parent_context_chars=context_chars,
302
+ ))
303
+ self.sink.run_note({"payload": {
304
+ "message": f"reflection failed: {type(error).__name__}: {error}",
305
+ "error_type": type(error).__name__,
306
+ }})
307
+
308
+ @staticmethod
309
+ def _adapter_output(result: RowEvaluation) -> dict[str, Any]:
310
+ """Return the adapter-owned evidence for one authoritative row id."""
311
+ return {
312
+ "row_id": result.row_id,
313
+ "output": result.output,
314
+ "raw_score": result.score,
315
+ "feedback": result.feedback,
316
+ "scorer_output": result.scorer_response,
317
+ "latency_ms": result.latency_ms,
318
+ "token_in": result.token_in,
319
+ "token_out": result.token_out,
320
+ "cost_usd": result.cost_usd,
321
+ "error": result.error,
322
+ "cached": result.cached,
323
+ "error_source": result.error_source,
324
+ }
325
+
326
+ def _rekey_valset_evidence(
327
+ self, event: dict[str, Any], results: list[RowEvaluation]
328
+ ) -> dict[str, Any]:
329
+ """Replace GEPA's positional score keys with runner-owned row identities.
330
+
331
+ GEPA 0.1.4 reports a valset score map keyed by positional indexes. The
332
+ immediately preceding adapter evaluation is the authoritative pairing
333
+ of those scores to dataset rows. Rekey every correlated map here, at
334
+ that callback boundary, so downstream transports never need to guess.
335
+ """
336
+ scores = event.get("scores_by_val_id")
337
+ if not isinstance(scores, dict) or len(scores) != len(results):
338
+ return event
339
+
340
+ outputs = event.get("outputs_by_val_id") or {}
341
+ feedbacks = event.get("feedbacks_by_val_id") or {}
342
+ errors = event.get("errors_by_val_id") or {}
343
+ if not isinstance(outputs, dict):
344
+ outputs = {}
345
+ if not isinstance(feedbacks, dict):
346
+ feedbacks = {}
347
+ if not isinstance(errors, dict):
348
+ errors = {}
349
+
350
+ rekeyed_scores: dict[str, Any] = {}
351
+ rekeyed_outputs: dict[str, Any] = {}
352
+ rekeyed_feedbacks: dict[str, Any] = {}
353
+ rekeyed_errors: dict[str, Any] = {}
354
+ for (source_id, score), result in zip(scores.items(), results, strict=True):
355
+ row_id = str(result.row_id)
356
+ if row_id in rekeyed_scores:
357
+ raise ValueError(f"adapter returned duplicate valset row id {row_id!r}")
358
+ existing = outputs.get(source_id, outputs.get(row_id, {}))
359
+ output_info = dict(existing) if isinstance(existing, dict) else {"output": existing}
360
+ for name, value in self._adapter_output(result).items():
361
+ if output_info.get(name) is None:
362
+ output_info[name] = value
363
+ # The adapter id is canonical even when GEPA happened to include
364
+ # another row_id in side information.
365
+ output_info["row_id"] = row_id
366
+ rekeyed_scores[row_id] = score
367
+ rekeyed_outputs[row_id] = output_info
368
+ if source_id in feedbacks:
369
+ rekeyed_feedbacks[row_id] = feedbacks[source_id]
370
+ if source_id in errors:
371
+ rekeyed_errors[row_id] = errors[source_id]
372
+
373
+ enriched = dict(event)
374
+ enriched["scores_by_val_id"] = rekeyed_scores
375
+ enriched["outputs_by_val_id"] = rekeyed_outputs
376
+ if feedbacks:
377
+ enriched["feedbacks_by_val_id"] = rekeyed_feedbacks
378
+ if errors:
379
+ enriched["errors_by_val_id"] = rekeyed_errors
380
+ return enriched
381
+
382
+ def on_valset_evaluated(self, event: dict[str, Any]) -> None:
383
+ is_seed = int(event.get("iteration", -1)) == 0 and str(event.get("candidate_idx")) in {"0", "seed"}
384
+ iteration = int(event.get("iteration", 0))
385
+ raw_candidate_id = event.get("candidate_idx")
386
+ candidate_id = str(raw_candidate_id) if raw_candidate_id is not None else "unknown"
387
+ # This callback, rather than row count, identifies a child's complete
388
+ # validation. Preserve its staged adapter evidence for the local row
389
+ # artifact before removing it from the pending parent-minibatch queue.
390
+ pending = self._pending_candidate_evaluations.get(iteration, [])
391
+ staged = [item for item in pending if item["candidate_id"] == candidate_id]
392
+ remaining = [item for item in pending if item["candidate_id"] != candidate_id]
393
+ if remaining:
394
+ self._pending_candidate_evaluations[iteration] = remaining
395
+ else:
396
+ self._pending_candidate_evaluations.pop(iteration, None)
397
+ # GEPA's valset callback deliberately omits rollout outputs. Its
398
+ # preceding evaluation callback has the adapter's real row evidence.
399
+ # Reattach that evidence for the seed baseline rather than emitting an
400
+ # empty dashboard baseline.
401
+ adapter_results = list(
402
+ getattr(self.evaluation_adapter, "last_row_evaluations", []) or []
403
+ )
404
+ scores = event.get("scores_by_val_id")
405
+ expected_ids = self.validation_row_ids
406
+ adapter_ids = [str(result.row_id) for result in adapter_results]
407
+ needs_declared_valset_identity = (
408
+ isinstance(scores, dict)
409
+ and len(expected_ids) == len(scores)
410
+ and set(adapter_ids) != set(expected_ids)
411
+ )
412
+ if needs_declared_valset_identity:
413
+ adapter_results = [
414
+ RowEvaluation(row_id=row_id, row={}, output=None, score=0.0, feedback=None)
415
+ for row_id in expected_ids
416
+ ]
417
+ if is_seed and adapter_results:
418
+ event = self._rekey_valset_evidence(event, adapter_results)
419
+ elif is_seed and self._seed_valset_rows:
420
+ seed_results = [
421
+ RowEvaluation(
422
+ row_id=str(row["row_id"]), row={}, output=row.get("output"),
423
+ score=float(row["score"]) if isinstance(row.get("score"), (int, float)) else 0.0,
424
+ feedback=row.get("feedback"), scorer_response=row.get("scorer_output"),
425
+ latency_ms=row.get("latency_ms"), token_in=row.get("token_in"),
426
+ token_out=row.get("token_out"), cost_usd=row.get("cost_usd"),
427
+ error=row.get("error"), cached=bool(row.get("cached", False)),
428
+ error_source=row.get("error_source"),
429
+ )
430
+ for row in self._seed_valset_rows
431
+ ]
432
+ event = self._rekey_valset_evidence(event, seed_results)
433
+ if not is_seed and adapter_results:
434
+ event = self._rekey_valset_evidence(event, adapter_results)
435
+ elif not is_seed and staged:
436
+ # The most recent same-candidate evaluation is the adapter call
437
+ # immediately before this authoritative valset callback. Earlier
438
+ # entries are child minibatches and must never supply valset rows.
439
+ event = self._rekey_valset_evidence(event, staged[-1]["results"])
440
+ local_logger = getattr(self.sink, "local_logger", None)
441
+ if local_logger is not None:
442
+ outputs_by_val_id = event.get("outputs_by_val_id") or {}
443
+ results: list[RowEvaluation] = []
444
+ parent_ids = event.get("parent_ids") or []
445
+ raw_parent_id = parent_ids[0] if parent_ids else None
446
+ parent_candidate_id = str(raw_parent_id) if raw_parent_id is not None else None
447
+ for row_id, score in (event.get("scores_by_val_id") or {}).items():
448
+ output_info = outputs_by_val_id.get(row_id, {}) if isinstance(outputs_by_val_id, dict) else {}
449
+ output_info = output_info if isinstance(output_info, dict) else {"output": output_info}
450
+ external_score = self._external_score(score)
451
+ row_error = output_info.get("error")
452
+ if isinstance(external_score, (int, float)):
453
+ recorded_score = float(external_score)
454
+ else:
455
+ recorded_score = 0.0
456
+ warning = "invalid scorer score; recorded as 0.0"
457
+ row_error = f"{row_error}; {warning}" if row_error else warning
458
+ if raw_candidate_id is None:
459
+ warning = "missing candidate_idx; recorded as unknown"
460
+ row_error = f"{row_error}; {warning}" if row_error else warning
461
+ results.append(RowEvaluation(
462
+ row_id=str(output_info.get("row_id", row_id)), row={}, output=output_info.get("output"),
463
+ score=recorded_score, feedback=output_info.get("feedback"),
464
+ scorer_response=output_info.get("scorer_output"),
465
+ latency_ms=output_info.get("latency_ms"), token_in=output_info.get("token_in"),
466
+ token_out=output_info.get("token_out"), cost_usd=output_info.get("cost_usd"),
467
+ error=row_error, cached=bool(output_info.get("cached", False)),
468
+ error_source=output_info.get("error_source"),
469
+ ))
470
+ if results:
471
+ stage = "seed_val_set" if is_seed else "child_val_set"
472
+ self._write_local(lambda: local_logger.append_evaluations(
473
+ stage=stage, split="validation", iteration=None if is_seed else iteration,
474
+ candidate_id="seed" if is_seed else candidate_id,
475
+ parent_candidate_id=None if is_seed else parent_candidate_id,
476
+ results=results,
477
+ ))
478
+ if self.higher_is_better:
479
+ self._emit("on_valset_evaluated", event)
480
+ return
481
+ external = dict(event)
482
+ external["scores_by_val_id"] = {key: self._external_score(value) for key, value in event.get("scores_by_val_id", {}).items()}
483
+ external["average_score"] = self._external_score(event.get("average_score"))
484
+ self._emit("on_valset_evaluated", external)
485
+
486
+ def on_proposal_end(self, event: dict[str, Any]) -> None:
487
+ # GEPA tells us the child index only at acceptance time. Buffer the
488
+ # reflection so the dashboard never sees an unjoinable child.
489
+ self.candidate_proposals += 1
490
+ self._proposals_by_iteration.setdefault(int(event["iteration"]), []).append(event)
491
+
492
+ def record_reflection_prompt(self, provider_prompt: str) -> None:
493
+ """Remember the frozen provider prompt, not GEPA's discarded renderer input."""
494
+ latest_iteration = next(reversed(self._reflection_contexts), None)
495
+ if latest_iteration is not None:
496
+ self._reflection_contexts[latest_iteration]["provider_prompt"] = provider_prompt
497
+
498
+ def _emit_buffered_proposal(self, event: dict[str, Any], candidate_id: str) -> None:
499
+ iteration = int(event["iteration"])
500
+ parent_id = self._selected_parent_by_iteration.get(iteration)
501
+ context = self._reflection_contexts.get(iteration, {})
502
+ parent_candidate = context.get("parent_candidate")
503
+ new_instructions = event.get("new_instructions") or {}
504
+ components = {
505
+ **(parent_candidate if isinstance(parent_candidate, dict) else {}),
506
+ **(new_instructions if isinstance(new_instructions, dict) else {}),
507
+ }
508
+ bounded_components = {}
509
+ truncated = False
510
+ for key, value in components.items():
511
+ bounded, did_truncate = _bounded(value, self.max_payload_chars)
512
+ bounded_components[key] = bounded
513
+ truncated = truncated or did_truncate
514
+ selected = list(context.get("components_to_update") or [])
515
+ body, body_truncated = _bounded(next(iter(components.values()), None), self.max_payload_chars)
516
+ payload = {"components": bounded_components, "components_to_update": selected,
517
+ "payload_truncated": truncated or body_truncated}
518
+ if ((isinstance(parent_candidate, dict) and len(parent_candidate) == 1)
519
+ or (parent_candidate is None and len(components) == 1)):
520
+ payload["body"] = body
521
+ self.sink.emit(
522
+ "candidate_proposed",
523
+ payload,
524
+ iteration=iteration,
525
+ candidate_id=candidate_id,
526
+ parent_candidate_id=parent_id,
527
+ )
528
+ translated = translate_callback("on_proposal_end", event, run_id=self.run_id,
529
+ max_payload_chars=self.max_payload_chars)
530
+ payload = dict(translated["payload"])
531
+ prompt = payload.pop("prompt", None)
532
+ response = payload.pop("response", None)
533
+ candidate_text = payload.pop("candidate_text", None)
534
+ payload.update(_redacted("prompt", prompt, include_text=self.log_row_snapshots))
535
+ # Frozen legacy behavior deliberately gates only the reflection prompt:
536
+ # the response and extracted candidate are required dashboard evidence.
537
+ payload["response"] = response
538
+ payload["candidate_text"] = candidate_text
539
+ local_logger = getattr(self.sink, "local_logger", None)
540
+ if local_logger is not None:
541
+ parent_rows = (self._parent_minibatches.get(iteration) or [{}])[0].get("row_results", [])
542
+ provider_prompt = self._reflection_contexts.get(iteration, {}).get("provider_prompt", prompt)
543
+ self._write_local(lambda: local_logger.append_reflection(
544
+ iteration=iteration,
545
+ parent_candidate_id=parent_id or "unknown",
546
+ child_candidate_id=candidate_id,
547
+ row_ids=[str(row.get("row_id", index)) for index, row in enumerate(parent_rows)],
548
+ prompt=str(provider_prompt or ""),
549
+ response=str(response or ""),
550
+ candidate_text=str(candidate_text or ""),
551
+ ))
552
+ self.sink.emit(
553
+ "reflection_completed",
554
+ payload,
555
+ iteration=iteration,
556
+ candidate_id=candidate_id,
557
+ parent_candidate_id=parent_id,
558
+ )
559
+
560
+ def on_candidate_accepted(self, event: dict[str, Any]) -> None:
561
+ iteration = int(event["iteration"])
562
+ self._flush_pending_parent_minibatches(iteration)
563
+ candidate_id = str(event["new_candidate_idx"])
564
+ proposals = self._proposals_by_iteration.get(iteration) or []
565
+ proposal = proposals.pop(0) if proposals else None
566
+ if proposal is not None:
567
+ self._emit_buffered_proposal(proposal, candidate_id)
568
+ children = self._child_minibatches.get(iteration) or []
569
+ child = children.pop(0) if children else None
570
+ child_minibatch = child["payload"] if child is not None else None
571
+ if child_minibatch is not None:
572
+ self.sink.emit("child_minibatch_completed", child_minibatch, iteration=iteration,
573
+ candidate_id=candidate_id,
574
+ parent_candidate_id=self._selected_parent_by_iteration.get(iteration))
575
+ local_logger = getattr(self.sink, "local_logger", None)
576
+ if local_logger is not None:
577
+ self._write_local(lambda: local_logger.append_evaluations(
578
+ stage="child_minibatch", split="train", iteration=iteration,
579
+ candidate_id=candidate_id,
580
+ parent_candidate_id=child["parent_candidate_id"] or self._selected_parent_by_iteration.get(iteration),
581
+ results=child["results"],
582
+ ))
583
+ parents = self._parent_minibatches.get(iteration) or [{}]
584
+ parent_minibatch = parents[0]
585
+ acceptance = dict(event)
586
+ acceptance["parent_score_total"] = parent_minibatch.get("score_total")
587
+ acceptance["child_score_total"] = (child_minibatch or {}).get("score_total", event.get("new_score"))
588
+ self._emit("on_candidate_accepted", acceptance)
589
+
590
+ def on_candidate_rejected(self, event: dict[str, Any]) -> None:
591
+ iteration = int(event["iteration"])
592
+ self._flush_pending_parent_minibatches(iteration)
593
+ proposals = self._proposals_by_iteration.get(iteration) or []
594
+ proposal = proposals.pop(0) if proposals else None
595
+ candidate_id = f"rejected-{iteration}"
596
+ if proposal is not None:
597
+ self._emit_buffered_proposal(proposal, candidate_id)
598
+ children = self._child_minibatches.get(iteration) or []
599
+ child = children.pop(0) if children else None
600
+ child_minibatch = child["payload"] if child is not None else None
601
+ if child_minibatch is not None:
602
+ self.sink.emit("child_minibatch_completed", child_minibatch, iteration=iteration,
603
+ candidate_id=candidate_id, parent_candidate_id=self._selected_parent_by_iteration.get(iteration))
604
+ local_logger = getattr(self.sink, "local_logger", None)
605
+ if local_logger is not None:
606
+ self._write_local(lambda: local_logger.append_evaluations(
607
+ stage="child_minibatch", split="train", iteration=iteration,
608
+ candidate_id=candidate_id,
609
+ parent_candidate_id=child["parent_candidate_id"] or self._selected_parent_by_iteration.get(iteration),
610
+ results=child["results"],
611
+ ))
612
+ parents = self._parent_minibatches.get(iteration) or [{}]
613
+ rejection = dict(event)
614
+ rejection["parent_score_total"] = parents[0].get("score_total")
615
+ rejection["child_score_total"] = (child_minibatch or {}).get("score_total", event.get("new_score"))
616
+ rejection["candidate_idx"] = candidate_id
617
+ self._emit("on_candidate_rejected", rejection)
618
+
619
+ def on_merge_accepted(self, event: dict[str, Any]) -> None:
620
+ self._emit("on_merge_accepted", event)
621
+
622
+ def on_merge_rejected(self, event: dict[str, Any]) -> None:
623
+ self._emit("on_merge_rejected", event)
624
+
625
+ def on_pareto_front_updated(self, event: dict[str, Any]) -> None:
626
+ self.sink.emit("pareto_front_updated", {"new_front": event.get("new_front", []), "displaced_candidates": event.get("displaced_candidates", [])}, iteration=event.get("iteration"))
627
+
628
+ def on_state_saved(self, event: dict[str, Any]) -> None:
629
+ self._emit("on_state_saved", event)
630
+
631
+ def on_budget_updated(self, event: dict[str, Any]) -> None:
632
+ self._emit("on_budget_updated", event)
633
+
634
+ def on_error(self, event: dict[str, Any]) -> None:
635
+ error = str(event.get("exception", "unknown GEPA error"))
636
+ event_type = "reflection_failed" if "reflection" in error.lower() else "error"
637
+ self.sink.emit(event_type, {"message": error, "will_continue": bool(event.get("will_continue"))}, iteration=event.get("iteration"))
638
+
639
+ def on_iteration_end(self, event: dict[str, Any]) -> None:
640
+ self._emit("on_iteration_end", event)
641
+ self.completed_iterations += 1
642
+ iteration = int(event["iteration"])
643
+ self._flush_pending_parent_minibatches(iteration)
644
+ self._selected_parent_by_iteration.pop(iteration, None)
645
+ self._proposals_by_iteration.pop(iteration, None)
646
+ self._parent_minibatches.pop(iteration, None)
647
+ self._child_minibatches.pop(iteration, None)
648
+ self._evaluation_row_ids.pop(iteration, None)
649
+ self._evaluation_inputs.pop(iteration, None)
650
+ self._reflection_contexts.pop(iteration, None)
651
+ self.hooks.emit("iteration_completed", event)
652
+
653
+ def on_optimization_end(self, event: dict[str, Any]) -> None:
654
+ # GEPA calls this on every normal exit. A budget-bound normal exit is
655
+ # a paused optimization in Orizu's lifecycle, not a completed one.
656
+ # Runtime emits the paired budget_exhausted/run_paused events once it
657
+ # has the final, reconciled budget ledger.
658
+ if self._terminal_budget_exhausted is not None and self._terminal_budget_exhausted(event):
659
+ return
660
+ self._emit("on_optimization_end", event)
661
+ self.hooks.emit("run_completed", event)
662
+
663
+ def on_merge_attempted(self, event: dict[str, Any]) -> None:
664
+ # This is the production sink, unlike the focused translator unit seam.
665
+ translate_callback("on_merge_attempted", event, run_id=self.run_id,
666
+ run_note_sink=self.sink.run_note)
@@ -0,0 +1,7 @@
1
+ import json
2
+
3
+ from .runtime import run_from_environment
4
+
5
+
6
+ def main() -> None:
7
+ print(json.dumps(run_from_environment()), flush=True)