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.
- package/README.md +23 -0
- package/dist/archive-artifacts-cli.d.ts +50 -0
- package/dist/archive-artifacts-cli.d.ts.map +1 -0
- package/dist/artifact-archive.d.ts +48 -0
- package/dist/artifact-archive.d.ts.map +1 -0
- package/dist/artifact-pull.d.ts +22 -0
- package/dist/artifact-pull.d.ts.map +1 -0
- package/dist/banner.d.ts +3 -0
- package/dist/banner.d.ts.map +1 -0
- package/dist/connectors-cli.d.ts +31 -0
- package/dist/connectors-cli.d.ts.map +1 -0
- package/dist/credentials.d.ts +46 -0
- package/dist/credentials.d.ts.map +1 -0
- package/dist/csv.d.ts +49 -0
- package/dist/csv.d.ts.map +1 -0
- package/dist/dataset-download.d.ts +6 -0
- package/dist/dataset-download.d.ts.map +1 -0
- package/dist/dataset-edit-rows.d.ts +10 -0
- package/dist/dataset-edit-rows.d.ts.map +1 -0
- package/dist/dataset-upload-snapshot.d.ts +13 -0
- package/dist/dataset-upload-snapshot.d.ts.map +1 -0
- package/dist/daytona-slice-rehearsal.d.ts +89 -0
- package/dist/daytona-slice-rehearsal.d.ts.map +1 -0
- package/dist/daytona-slice.d.ts +106 -0
- package/dist/daytona-slice.d.ts.map +1 -0
- package/dist/deploy-key-merge-core.d.ts +121 -0
- package/dist/deploy-key-merge-core.d.ts.map +1 -0
- package/dist/diff-comments-cli.d.ts +12 -0
- package/dist/diff-comments-cli.d.ts.map +1 -0
- package/dist/diff-comments-cli.js +21 -2
- package/dist/egress-allowlist-cli.d.ts +23 -0
- package/dist/egress-allowlist-cli.d.ts.map +1 -0
- package/dist/egress-policy.d.ts +108 -0
- package/dist/egress-policy.d.ts.map +1 -0
- package/dist/error-response.d.ts +9 -0
- package/dist/error-response.d.ts.map +1 -0
- package/dist/file-parser.d.ts +5 -0
- package/dist/file-parser.d.ts.map +1 -0
- package/dist/gepa-engine-dispatch.d.ts +9 -0
- package/dist/gepa-engine-dispatch.d.ts.map +1 -0
- package/dist/gepa-engine-dispatch.js +307 -0
- package/dist/gepa-python-command.d.ts +6 -0
- package/dist/gepa-python-command.d.ts.map +1 -0
- package/dist/gepa-python-command.js +26 -0
- package/dist/gepa-python-paths.d.ts +6 -0
- package/dist/gepa-python-paths.d.ts.map +1 -0
- package/dist/gepa-python-paths.js +42 -0
- package/dist/git-credential.d.ts +69 -0
- package/dist/git-credential.d.ts.map +1 -0
- package/dist/git-recovery-environment.d.ts +31 -0
- package/dist/git-recovery-environment.d.ts.map +1 -0
- package/dist/github-setup.d.ts +122 -0
- package/dist/github-setup.d.ts.map +1 -0
- package/dist/global-flags.d.ts +12 -0
- package/dist/global-flags.d.ts.map +1 -0
- package/dist/help.d.ts +46 -0
- package/dist/help.d.ts.map +1 -0
- package/dist/help.js +93 -25
- package/dist/hosted-boot.d.ts +273 -0
- package/dist/hosted-boot.d.ts.map +1 -0
- package/dist/hosted-bootstrap-rehearsal.d.ts +86 -0
- package/dist/hosted-bootstrap-rehearsal.d.ts.map +1 -0
- package/dist/hosted-bootstrap.d.ts +188 -0
- package/dist/hosted-bootstrap.d.ts.map +1 -0
- package/dist/hosted-broker-server.d.ts +50 -0
- package/dist/hosted-broker-server.d.ts.map +1 -0
- package/dist/hosted-git-http-server.d.ts +36 -0
- package/dist/hosted-git-http-server.d.ts.map +1 -0
- package/dist/hosted-harness-claude.d.ts +195 -0
- package/dist/hosted-harness-claude.d.ts.map +1 -0
- package/dist/hosted-harness-opencode.d.ts +227 -0
- package/dist/hosted-harness-opencode.d.ts.map +1 -0
- package/dist/hosted-harness.d.ts +118 -0
- package/dist/hosted-harness.d.ts.map +1 -0
- package/dist/hosted-harvest.d.ts +68 -0
- package/dist/hosted-harvest.d.ts.map +1 -0
- package/dist/hosted-headless.d.ts +72 -0
- package/dist/hosted-headless.d.ts.map +1 -0
- package/dist/hosted-loop-lifecycle.d.ts +206 -0
- package/dist/hosted-loop-lifecycle.d.ts.map +1 -0
- package/dist/hosted-loop.d.ts +170 -0
- package/dist/hosted-loop.d.ts.map +1 -0
- package/dist/hosted-run-event-sink.d.ts +222 -0
- package/dist/hosted-run-event-sink.d.ts.map +1 -0
- package/dist/hosted-runtime-assets.d.ts +186 -0
- package/dist/hosted-runtime-assets.d.ts.map +1 -0
- package/dist/hosted-session-cli.d.ts +258 -0
- package/dist/hosted-session-cli.d.ts.map +1 -0
- package/dist/hosted-skill-staging.d.ts +73 -0
- package/dist/hosted-skill-staging.d.ts.map +1 -0
- package/dist/http.d.ts +8 -0
- package/dist/http.d.ts.map +1 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +119 -129
- package/dist/instruction-set-gepa-launch.d.ts +3 -0
- package/dist/instruction-set-gepa-launch.d.ts.map +1 -0
- package/dist/instruction-set-gepa-launch.js +15 -0
- package/dist/instruction-set-loader.d.ts +6 -0
- package/dist/instruction-set-loader.d.ts.map +1 -0
- package/dist/instruction-set-loader.js +59 -0
- package/dist/instruction-set-manifest.d.ts +14 -0
- package/dist/instruction-set-manifest.d.ts.map +1 -0
- package/dist/instruction-set-manifest.js +162 -0
- package/dist/instruction-sets-cli.d.ts +40 -0
- package/dist/instruction-sets-cli.d.ts.map +1 -0
- package/dist/instruction-sets-cli.js +361 -0
- package/dist/json-response.d.ts +4 -0
- package/dist/json-response.d.ts.map +1 -0
- package/dist/jsonl-stream.d.ts +8 -0
- package/dist/jsonl-stream.d.ts.map +1 -0
- package/dist/kill-switch-cli.d.ts +30 -0
- package/dist/kill-switch-cli.d.ts.map +1 -0
- package/dist/loopback-serve.d.ts +25 -0
- package/dist/loopback-serve.d.ts.map +1 -0
- package/dist/manifests-cli.d.ts +22 -0
- package/dist/manifests-cli.d.ts.map +1 -0
- package/dist/markdown-report-input.d.ts +7 -0
- package/dist/markdown-report-input.d.ts.map +1 -0
- package/dist/markdown-report-input.js +15 -1
- package/dist/merge-job.d.ts +155 -0
- package/dist/merge-job.d.ts.map +1 -0
- package/dist/model-configs-cli.d.ts +19 -0
- package/dist/model-configs-cli.d.ts.map +1 -0
- package/dist/model-configs-cli.js +104 -0
- package/dist/optimization-export-cli.d.ts +19 -0
- package/dist/optimization-export-cli.d.ts.map +1 -0
- package/dist/optimizations-list-cli.d.ts +20 -0
- package/dist/optimizations-list-cli.d.ts.map +1 -0
- package/dist/optimizations-promote-cli.d.ts +8 -0
- package/dist/optimizations-promote-cli.d.ts.map +1 -0
- package/dist/optimizations-promote-cli.js +34 -0
- package/dist/option-validation.d.ts +15 -0
- package/dist/option-validation.d.ts.map +1 -0
- package/dist/preview-runtime.d.ts +63 -0
- package/dist/preview-runtime.d.ts.map +1 -0
- package/dist/prompt-draft-push.d.ts +60 -0
- package/dist/prompt-draft-push.d.ts.map +1 -0
- package/dist/prompt-label-cli.d.ts +11 -0
- package/dist/prompt-label-cli.d.ts.map +1 -0
- package/dist/prompt-label-cli.js +29 -0
- package/dist/prompt-length-wire.d.ts +28 -0
- package/dist/prompt-length-wire.d.ts.map +1 -0
- package/dist/prompt-length-wire.js +2 -2
- package/dist/prompt-report-cli.d.ts +10 -0
- package/dist/prompt-report-cli.d.ts.map +1 -0
- package/dist/prompt-report-cli.js +2 -2
- package/dist/prompt-summary-table.d.ts +15 -0
- package/dist/prompt-summary-table.d.ts.map +1 -0
- package/dist/prompt-summary-table.js +6 -0
- package/dist/registration-tag-warning.d.ts +32 -0
- package/dist/registration-tag-warning.d.ts.map +1 -0
- package/dist/report-comments-cli.d.ts +16 -0
- package/dist/report-comments-cli.d.ts.map +1 -0
- package/dist/report-markdown-blankness.d.ts +2 -0
- package/dist/report-markdown-blankness.d.ts.map +1 -0
- package/dist/report-markdown-blankness.js +6 -0
- package/dist/report-replacement-warning.d.ts +17 -0
- package/dist/report-replacement-warning.d.ts.map +1 -0
- package/dist/report-replacement-warning.js +31 -0
- package/dist/repository-backup-core.d.ts +225 -0
- package/dist/repository-backup-core.d.ts.map +1 -0
- package/dist/repository-recovery-manifest.d.ts +83 -0
- package/dist/repository-recovery-manifest.d.ts.map +1 -0
- package/dist/run-event-digest.d.ts +74 -0
- package/dist/run-event-digest.d.ts.map +1 -0
- package/dist/runner-dir-verify.d.ts +66 -0
- package/dist/runner-dir-verify.d.ts.map +1 -0
- package/dist/runner-instruction-set-contract.d.ts +24 -0
- package/dist/runner-instruction-set-contract.d.ts.map +1 -0
- package/dist/runner-instruction-set-contract.js +50 -0
- package/dist/sandbox-hygiene.d.ts +45 -0
- package/dist/sandbox-hygiene.d.ts.map +1 -0
- package/dist/sandbox-provider.d.ts +193 -0
- package/dist/sandbox-provider.d.ts.map +1 -0
- package/dist/scorer-draft-push.d.ts +50 -0
- package/dist/scorer-draft-push.d.ts.map +1 -0
- package/dist/scorers-cli.d.ts +20 -0
- package/dist/scorers-cli.d.ts.map +1 -0
- package/dist/scorers-cli.js +22 -0
- package/dist/scorers-verify-parity-bridge.d.ts +16 -0
- package/dist/scorers-verify-parity-bridge.d.ts.map +1 -0
- package/dist/scorers-verify-parity-bridge.js +90 -0
- package/dist/scorers-verify-parity.d.ts +81 -0
- package/dist/scorers-verify-parity.d.ts.map +1 -0
- package/dist/scorers-verify-parity.js +502 -0
- package/dist/scores-accept-cli.d.ts +21 -0
- package/dist/scores-accept-cli.d.ts.map +1 -0
- package/dist/secret-redaction.d.ts +47 -0
- package/dist/secret-redaction.d.ts.map +1 -0
- package/dist/session-timeline.d.ts +54 -0
- package/dist/session-timeline.d.ts.map +1 -0
- package/dist/setup-prompt.d.ts +5 -0
- package/dist/setup-prompt.d.ts.map +1 -0
- package/dist/skill-installer.d.ts +68 -0
- package/dist/skill-installer.d.ts.map +1 -0
- package/dist/skilled-proposer-launch.d.ts +7 -0
- package/dist/skilled-proposer-launch.d.ts.map +1 -0
- package/dist/skilled-proposer-launch.js +60 -0
- package/dist/smoke.d.ts +102 -0
- package/dist/smoke.d.ts.map +1 -0
- package/dist/start-intent.d.ts +12 -0
- package/dist/start-intent.d.ts.map +1 -0
- package/dist/task-assignment-manifest.d.ts +15 -0
- package/dist/task-assignment-manifest.d.ts.map +1 -0
- package/dist/task-create-error.d.ts +18 -0
- package/dist/task-create-error.d.ts.map +1 -0
- package/dist/team-connectors-cli.d.ts +29 -0
- package/dist/team-connectors-cli.d.ts.map +1 -0
- package/dist/types.d.ts +39 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/vercel-sandbox-provider.d.ts +156 -0
- package/dist/vercel-sandbox-provider.d.ts.map +1 -0
- package/dist/workbench-cli.d.ts +233 -0
- package/dist/workbench-cli.d.ts.map +1 -0
- package/dist/workspace-sync.d.ts +207 -0
- package/dist/workspace-sync.d.ts.map +1 -0
- package/dist/workspace.d.ts +92 -0
- package/dist/workspace.d.ts.map +1 -0
- package/dist/zip-draft-push.d.ts +55 -0
- package/dist/zip-draft-push.d.ts.map +1 -0
- package/package.json +15 -2
- package/requirements/README.md +138 -0
- package/requirements/skilled-proposer.in +66 -0
- package/requirements/skilled-proposer.lock +265 -0
- package/scripts/build-cli-dist.mjs +30 -0
- package/scripts/ensure-skilled-proposer-venv.mjs +5 -0
- package/scripts/orizu-source-checkout.d.mts +1 -0
- package/scripts/orizu-source-checkout.mjs +10 -0
- package/scripts/refresh-skilled-proposer-lock.mjs +256 -0
- package/scripts/vendor-gepa-python.mjs +187 -4
- package/src/diff-comments-cli.ts +41 -3
- package/src/gepa-engine-dispatch.ts +340 -0
- package/src/gepa-python-command.ts +31 -0
- package/src/gepa-python-paths.ts +56 -0
- package/src/help.ts +95 -25
- package/src/index.ts +131 -166
- package/src/instruction-set-gepa-launch.ts +28 -0
- package/src/instruction-set-loader.ts +37 -0
- package/src/instruction-set-manifest.ts +120 -0
- package/src/instruction-sets-cli.ts +306 -0
- package/src/markdown-report-input.ts +19 -1
- package/src/model-configs-cli.ts +119 -0
- package/src/optimizations-promote-cli.ts +41 -0
- package/src/prompt-label-cli.ts +55 -0
- package/src/prompt-length-wire.ts +1 -1
- package/src/prompt-report-cli.ts +2 -2
- package/src/prompt-summary-table.ts +12 -0
- package/src/report-markdown-blankness.ts +6 -0
- package/src/report-replacement-warning.ts +35 -0
- package/src/runner-instruction-set-contract.ts +64 -0
- package/src/scorers-cli.ts +40 -0
- package/src/scorers-verify-parity-bridge.ts +90 -0
- package/src/scorers-verify-parity.ts +564 -0
- package/src/skilled-proposer-launch.ts +89 -0
- package/src/skilled-proposer-venv-manager.mjs +757 -0
- package/tsconfig.json +2 -0
- package/vendor/gepa-python/LICENSE +7 -0
- package/vendor/gepa-python/manifest.json +107 -0
- package/vendor/gepa-python/src/gepa/__init__.py +22 -0
- package/vendor/gepa-python/src/gepa/adapters/__init__.py +0 -0
- package/vendor/gepa-python/src/gepa/adapters/anymaths_adapter/__init__.py +1 -0
- package/vendor/gepa-python/src/gepa/adapters/anymaths_adapter/anymaths_adapter.py +174 -0
- package/vendor/gepa-python/src/gepa/adapters/confidence_adapter/__init__.py +26 -0
- package/vendor/gepa-python/src/gepa/adapters/confidence_adapter/confidence_adapter.py +618 -0
- package/vendor/gepa-python/src/gepa/adapters/confidence_adapter/scoring.py +206 -0
- package/vendor/gepa-python/src/gepa/adapters/default_adapter/__init__.py +0 -0
- package/vendor/gepa-python/src/gepa/adapters/default_adapter/default_adapter.py +206 -0
- package/vendor/gepa-python/src/gepa/adapters/dspy_adapter/__init__.py +0 -0
- package/vendor/gepa-python/src/gepa/adapters/dspy_adapter/dspy_adapter.py +525 -0
- package/vendor/gepa-python/src/gepa/adapters/dspy_adapter/instruction_proposal.py +500 -0
- package/vendor/gepa-python/src/gepa/adapters/dspy_full_program_adapter/__init__.py +6 -0
- package/vendor/gepa-python/src/gepa/adapters/dspy_full_program_adapter/dspy_program_proposal_signature.py +137 -0
- package/vendor/gepa-python/src/gepa/adapters/dspy_full_program_adapter/full_program_adapter.py +269 -0
- package/vendor/gepa-python/src/gepa/adapters/generic_rag_adapter/__init__.py +56 -0
- package/vendor/gepa-python/src/gepa/adapters/generic_rag_adapter/evaluation_metrics.py +226 -0
- package/vendor/gepa-python/src/gepa/adapters/generic_rag_adapter/generic_rag_adapter.py +496 -0
- package/vendor/gepa-python/src/gepa/adapters/generic_rag_adapter/rag_pipeline.py +238 -0
- package/vendor/gepa-python/src/gepa/adapters/generic_rag_adapter/vector_store_interface.py +212 -0
- package/vendor/gepa-python/src/gepa/adapters/generic_rag_adapter/vector_stores/__init__.py +2 -0
- package/vendor/gepa-python/src/gepa/adapters/generic_rag_adapter/vector_stores/chroma_store.py +196 -0
- package/vendor/gepa-python/src/gepa/adapters/generic_rag_adapter/vector_stores/lancedb_store.py +422 -0
- package/vendor/gepa-python/src/gepa/adapters/generic_rag_adapter/vector_stores/milvus_store.py +409 -0
- package/vendor/gepa-python/src/gepa/adapters/generic_rag_adapter/vector_stores/qdrant_store.py +368 -0
- package/vendor/gepa-python/src/gepa/adapters/generic_rag_adapter/vector_stores/weaviate_store.py +418 -0
- package/vendor/gepa-python/src/gepa/adapters/langchain_adapter/__init__.py +11 -0
- package/vendor/gepa-python/src/gepa/adapters/langchain_adapter/langchain_adapter.py +261 -0
- package/vendor/gepa-python/src/gepa/adapters/mcp_adapter/__init__.py +37 -0
- package/vendor/gepa-python/src/gepa/adapters/mcp_adapter/mcp_adapter.py +693 -0
- package/vendor/gepa-python/src/gepa/adapters/mcp_adapter/mcp_client.py +364 -0
- package/vendor/gepa-python/src/gepa/adapters/optimize_anything_adapter/optimize_anything_adapter.py +879 -0
- package/vendor/gepa-python/src/gepa/adapters/terminal_bench_adapter/__init__.py +0 -0
- package/vendor/gepa-python/src/gepa/adapters/terminal_bench_adapter/terminal_bench_adapter.py +217 -0
- package/vendor/gepa-python/src/gepa/api.py +468 -0
- package/vendor/gepa-python/src/gepa/core/__init__.py +0 -0
- package/vendor/gepa-python/src/gepa/core/adapter.py +227 -0
- package/vendor/gepa-python/src/gepa/core/callbacks.py +551 -0
- package/vendor/gepa-python/src/gepa/core/data_loader.py +74 -0
- package/vendor/gepa-python/src/gepa/core/engine.py +1025 -0
- package/vendor/gepa-python/src/gepa/core/result.py +271 -0
- package/vendor/gepa-python/src/gepa/core/state.py +706 -0
- package/vendor/gepa-python/src/gepa/examples/__init__.py +0 -0
- package/vendor/gepa-python/src/gepa/examples/aime.py +24 -0
- package/vendor/gepa-python/src/gepa/examples/anymaths-bench/eval_default.py +111 -0
- package/vendor/gepa-python/src/gepa/examples/anymaths-bench/train_anymaths.py +177 -0
- package/vendor/gepa-python/src/gepa/examples/mcp_adapter/__init__.py +4 -0
- package/vendor/gepa-python/src/gepa/examples/mcp_adapter/mcp_optimization_example.py +456 -0
- package/vendor/gepa-python/src/gepa/examples/rag_adapter/__init__.py +9 -0
- package/vendor/gepa-python/src/gepa/examples/rag_adapter/rag_optimization.py +824 -0
- package/vendor/gepa-python/src/gepa/examples/terminal-bench/train_terminus.py +161 -0
- package/vendor/gepa-python/src/gepa/gepa_utils.py +117 -0
- package/vendor/gepa-python/src/gepa/gskill/__init__.py +1 -0
- package/vendor/gepa-python/src/gepa/gskill/gskill/__init__.py +0 -0
- package/vendor/gepa-python/src/gepa/gskill/gskill/cost_tracker.py +194 -0
- package/vendor/gepa-python/src/gepa/gskill/gskill/evaluate/__init__.py +0 -0
- package/vendor/gepa-python/src/gepa/gskill/gskill/evaluate/claude_code.py +1117 -0
- package/vendor/gepa-python/src/gepa/gskill/gskill/evaluate/claude_code_skills.py +613 -0
- package/vendor/gepa-python/src/gepa/gskill/gskill/evaluate/mini_swe_agent.py +440 -0
- package/vendor/gepa-python/src/gepa/gskill/gskill/experiment_logger.py +367 -0
- package/vendor/gepa-python/src/gepa/gskill/gskill/swe_fitness_fn.py +209 -0
- package/vendor/gepa-python/src/gepa/gskill/gskill/swe_harness.py +352 -0
- package/vendor/gepa-python/src/gepa/gskill/gskill/train_optimize_anything.py +830 -0
- package/vendor/gepa-python/src/gepa/image.py +104 -0
- package/vendor/gepa-python/src/gepa/lm.py +261 -0
- package/vendor/gepa-python/src/gepa/logging/__init__.py +0 -0
- package/vendor/gepa-python/src/gepa/logging/experiment_tracker.py +457 -0
- package/vendor/gepa-python/src/gepa/logging/logger.py +75 -0
- package/vendor/gepa-python/src/gepa/logging/utils.py +130 -0
- package/vendor/gepa-python/src/gepa/optimize_anything.py +1701 -0
- package/vendor/gepa-python/src/gepa/proposer/__init__.py +0 -0
- package/vendor/gepa-python/src/gepa/proposer/base.py +53 -0
- package/vendor/gepa-python/src/gepa/proposer/merge.py +403 -0
- package/vendor/gepa-python/src/gepa/proposer/reflective_mutation/__init__.py +0 -0
- package/vendor/gepa-python/src/gepa/proposer/reflective_mutation/base.py +64 -0
- package/vendor/gepa-python/src/gepa/proposer/reflective_mutation/reflection_lm.py +178 -0
- package/vendor/gepa-python/src/gepa/proposer/reflective_mutation/reflective_mutation.py +641 -0
- package/vendor/gepa-python/src/gepa/py.typed +0 -0
- package/vendor/gepa-python/src/gepa/strategies/__init__.py +0 -0
- package/vendor/gepa-python/src/gepa/strategies/acceptance.py +66 -0
- package/vendor/gepa-python/src/gepa/strategies/batch_sampler.py +103 -0
- package/vendor/gepa-python/src/gepa/strategies/candidate_selector.py +82 -0
- package/vendor/gepa-python/src/gepa/strategies/component_selector.py +36 -0
- package/vendor/gepa-python/src/gepa/strategies/eval_policy.py +64 -0
- package/vendor/gepa-python/src/gepa/strategies/instruction_proposal.py +153 -0
- package/vendor/gepa-python/src/gepa/strategies/proposal_sampling.py +128 -0
- package/vendor/gepa-python/src/gepa/strategies/proposal_selection.py +84 -0
- package/vendor/gepa-python/src/gepa/utils/__init__.py +63 -0
- package/vendor/gepa-python/src/gepa/utils/code_execution.py +626 -0
- package/vendor/gepa-python/src/gepa/utils/stdio_capture.py +145 -0
- package/vendor/gepa-python/src/gepa/utils/stop_condition.py +230 -0
- package/vendor/gepa-python/src/gepa/visualization.py +359 -0
- package/vendor/orizu-gepa/pyproject.toml +18 -0
- package/vendor/orizu-gepa/src/orizu_gepa_connector/__init__.py +9 -0
- package/vendor/orizu-gepa/src/orizu_gepa_connector/__main__.py +5 -0
- package/vendor/orizu-gepa/src/orizu_gepa_connector/adapter.py +138 -0
- package/vendor/orizu-gepa/src/orizu_gepa_connector/callbacks.py +666 -0
- package/vendor/orizu-gepa/src/orizu_gepa_connector/cli.py +7 -0
- package/vendor/orizu-gepa/src/orizu_gepa_connector/engine.py +269 -0
- package/vendor/orizu-gepa/src/orizu_gepa_connector/instruction_set_loader.py +52 -0
- package/vendor/orizu-gepa/src/orizu_gepa_connector/preflight.py +64 -0
- package/vendor/orizu-gepa/src/orizu_gepa_connector/reflection.py +186 -0
- package/vendor/orizu-gepa/src/orizu_gepa_connector/runtime.py +721 -0
- package/vendor/orizu-gepa/src/orizu_gepa_connector/skilled_proposer_bridge.py +381 -0
- package/vendor/orizu-gepa/src/orizu_gepa_connector/stop_conditions.py +55 -0
- package/vendor/orizu-gepa/src/orizu_gepa_connector/translator.py +203 -0
- package/vendor/orizu-gepa-python/pyproject.toml +1 -1
- package/vendor/orizu-gepa-python/src/orizu_gepa/cli.py +1 -0
- package/vendor/orizu-gepa-python/src/orizu_gepa/client.py +52 -7
- package/vendor/orizu-gepa-python/src/orizu_gepa/local_log.py +52 -0
- package/vendor/orizu-gepa-python/src/orizu_gepa/optimizer.py +10 -5
- package/vendor/orizu-gepa-python/src/orizu_gepa/reflection.py +323 -68
- package/vendor/orizu-gepa-python/src/orizu_gepa/runner.py +207 -13
- package/vendor/skills/orizu-cli/SKILL.md +3 -1
- package/vendor/skills/orizu-cli/references/cli-reference.md +85 -1
- package/vendor/skills/orizu-cli/references/migrate-existing-gepa-setup.md +303 -0
- package/vendor/skills/orizu-cli/references/optimization-reports.md +1 -1
- package/vendor/skills/orizu-cli/references/optimization-with-gepa.md +26 -1
- 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)
|