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,706 @@
|
|
|
1
|
+
# Copyright (c) 2025 Lakshya A Agrawal and the GEPA contributors
|
|
2
|
+
# https://github.com/gepa-ai/gepa
|
|
3
|
+
|
|
4
|
+
import hashlib
|
|
5
|
+
import json
|
|
6
|
+
import os
|
|
7
|
+
from collections import defaultdict
|
|
8
|
+
from collections.abc import Callable, Sequence
|
|
9
|
+
from dataclasses import dataclass, field
|
|
10
|
+
from typing import Any, ClassVar, Generic, Literal, TypeAlias
|
|
11
|
+
|
|
12
|
+
from gepa.core.adapter import RolloutOutput
|
|
13
|
+
from gepa.core.data_loader import DataId
|
|
14
|
+
from gepa.gepa_utils import json_default
|
|
15
|
+
from gepa.logging.logger import LoggerProtocol
|
|
16
|
+
|
|
17
|
+
# Types for GEPAState
|
|
18
|
+
ProgramIdx = int
|
|
19
|
+
|
|
20
|
+
# Type aliases
|
|
21
|
+
ObjectiveScores: TypeAlias = dict[str, float]
|
|
22
|
+
FrontierType: TypeAlias = Literal["instance", "objective", "hybrid", "cartesian"]
|
|
23
|
+
"""Strategy for tracking Pareto frontiers: 'instance' (per validation example), 'objective' (per objective metric), 'hybrid' (both), or 'cartesian' (per example x objective)."""
|
|
24
|
+
FrontierKey: TypeAlias = DataId | str | tuple[str, DataId] | tuple[str, DataId, str]
|
|
25
|
+
"""Key type for frontier mappings depending on frontier_type."""
|
|
26
|
+
|
|
27
|
+
CandidateHash: TypeAlias = str
|
|
28
|
+
CacheKey: TypeAlias = tuple[CandidateHash, DataId]
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _candidate_hash(candidate: dict[str, str]) -> CandidateHash:
|
|
32
|
+
"""Compute a deterministic hash of a candidate dictionary."""
|
|
33
|
+
return hashlib.sha256(json.dumps(sorted(candidate.items())).encode()).hexdigest()
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@dataclass
|
|
37
|
+
class CachedEvaluation(Generic[RolloutOutput]):
|
|
38
|
+
"""Cached evaluation result for a (candidate, example) pair."""
|
|
39
|
+
|
|
40
|
+
output: RolloutOutput
|
|
41
|
+
score: float
|
|
42
|
+
objective_scores: ObjectiveScores | None
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@dataclass
|
|
46
|
+
class EvaluationCache(Generic[RolloutOutput, DataId]):
|
|
47
|
+
"""Cache for storing evaluation results of (candidate, example) pairs."""
|
|
48
|
+
|
|
49
|
+
_cache: dict[CacheKey, CachedEvaluation[RolloutOutput]] = field(default_factory=dict)
|
|
50
|
+
|
|
51
|
+
def get(self, candidate: dict[str, str], example_id: DataId) -> CachedEvaluation[RolloutOutput] | None:
|
|
52
|
+
"""Retrieve cached evaluation result if it exists."""
|
|
53
|
+
return self._cache.get((_candidate_hash(candidate), example_id))
|
|
54
|
+
|
|
55
|
+
def put(
|
|
56
|
+
self,
|
|
57
|
+
candidate: dict[str, str],
|
|
58
|
+
example_id: DataId,
|
|
59
|
+
output: RolloutOutput,
|
|
60
|
+
score: float,
|
|
61
|
+
objective_scores: ObjectiveScores | None = None,
|
|
62
|
+
) -> None:
|
|
63
|
+
"""Store an evaluation result in the cache."""
|
|
64
|
+
self._cache[(_candidate_hash(candidate), example_id)] = CachedEvaluation(output, score, objective_scores)
|
|
65
|
+
|
|
66
|
+
def get_batch(
|
|
67
|
+
self, candidate: dict[str, str], example_ids: list[DataId]
|
|
68
|
+
) -> tuple[dict[DataId, CachedEvaluation[RolloutOutput]], list[DataId]]:
|
|
69
|
+
"""Look up cached results for a batch. Returns (cached_results, uncached_ids)."""
|
|
70
|
+
h = _candidate_hash(candidate)
|
|
71
|
+
cached, uncached = {}, []
|
|
72
|
+
for eid in example_ids:
|
|
73
|
+
if entry := self._cache.get((h, eid)):
|
|
74
|
+
cached[eid] = entry
|
|
75
|
+
else:
|
|
76
|
+
uncached.append(eid)
|
|
77
|
+
return cached, uncached
|
|
78
|
+
|
|
79
|
+
def put_batch(
|
|
80
|
+
self,
|
|
81
|
+
candidate: dict[str, str],
|
|
82
|
+
example_ids: list[DataId],
|
|
83
|
+
outputs: list[RolloutOutput],
|
|
84
|
+
scores: list[float],
|
|
85
|
+
objective_scores_list: Sequence[ObjectiveScores] | None = None,
|
|
86
|
+
) -> None:
|
|
87
|
+
"""Store evaluation results for a batch of examples."""
|
|
88
|
+
h = _candidate_hash(candidate)
|
|
89
|
+
for i, eid in enumerate(example_ids):
|
|
90
|
+
self._cache[(h, eid)] = CachedEvaluation(
|
|
91
|
+
outputs[i], scores[i], objective_scores_list[i] if objective_scores_list else None
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
def evaluate_with_cache_full(
|
|
95
|
+
self,
|
|
96
|
+
candidate: dict[str, str],
|
|
97
|
+
example_ids: list[DataId],
|
|
98
|
+
fetcher: Callable[[list[DataId]], Any],
|
|
99
|
+
evaluator: Callable[[Any, dict[str, str]], tuple[Any, list[float], Sequence[ObjectiveScores] | None]],
|
|
100
|
+
) -> tuple[dict[DataId, RolloutOutput], dict[DataId, float], dict[DataId, ObjectiveScores] | None, int]:
|
|
101
|
+
"""
|
|
102
|
+
Evaluate using cache, returning full results.
|
|
103
|
+
|
|
104
|
+
Returns (outputs_by_id, scores_by_id, objective_scores_by_id, num_actual_evals).
|
|
105
|
+
"""
|
|
106
|
+
cached, uncached_ids = self.get_batch(candidate, example_ids)
|
|
107
|
+
|
|
108
|
+
outputs_by_id: dict[DataId, RolloutOutput] = {eid: c.output for eid, c in cached.items()}
|
|
109
|
+
scores_by_id: dict[DataId, float] = {eid: c.score for eid, c in cached.items()}
|
|
110
|
+
objective_by_id: dict[DataId, ObjectiveScores] | None = None
|
|
111
|
+
|
|
112
|
+
# Populate objective scores from cache
|
|
113
|
+
for eid, c in cached.items():
|
|
114
|
+
if c.objective_scores is not None:
|
|
115
|
+
objective_by_id = objective_by_id or {}
|
|
116
|
+
objective_by_id[eid] = c.objective_scores
|
|
117
|
+
|
|
118
|
+
# Evaluate uncached examples
|
|
119
|
+
if uncached_ids:
|
|
120
|
+
batch = fetcher(uncached_ids)
|
|
121
|
+
outputs, scores, obj_scores = evaluator(batch, candidate)
|
|
122
|
+
for idx, eid in enumerate(uncached_ids):
|
|
123
|
+
outputs_by_id[eid] = outputs[idx]
|
|
124
|
+
scores_by_id[eid] = scores[idx]
|
|
125
|
+
if obj_scores is not None:
|
|
126
|
+
objective_by_id = objective_by_id or {}
|
|
127
|
+
objective_by_id[eid] = obj_scores[idx]
|
|
128
|
+
self.put_batch(candidate, uncached_ids, outputs, scores, obj_scores)
|
|
129
|
+
|
|
130
|
+
return outputs_by_id, scores_by_id, objective_by_id, len(uncached_ids)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
@dataclass(slots=True)
|
|
134
|
+
class ValsetEvaluation(Generic[RolloutOutput, DataId]):
|
|
135
|
+
"""Container for evaluation results on a validation set batch."""
|
|
136
|
+
|
|
137
|
+
outputs_by_val_id: dict[DataId, RolloutOutput]
|
|
138
|
+
scores_by_val_id: dict[DataId, float]
|
|
139
|
+
objective_scores_by_val_id: dict[DataId, ObjectiveScores] | None = None
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
class GEPAState(Generic[RolloutOutput, DataId]):
|
|
143
|
+
"""Internal persistent state of a GEPA optimization run.
|
|
144
|
+
|
|
145
|
+
Tracks all explored candidates, their per-example and per-objective scores,
|
|
146
|
+
Pareto frontiers, evaluation budget, and optional evaluation cache.
|
|
147
|
+
Saved/loaded automatically when ``EngineConfig.run_dir`` is set.
|
|
148
|
+
|
|
149
|
+
Users interact with this indirectly via :class:`~gepa.core.result.GEPAResult`
|
|
150
|
+
returned by :func:`~gepa.optimize_anything.optimize_anything`.
|
|
151
|
+
"""
|
|
152
|
+
|
|
153
|
+
_VALIDATION_SCHEMA_VERSION: ClassVar[int] = 5
|
|
154
|
+
# Attributes that are runtime-only and should not be serialized (e.g., callback hooks, caches)
|
|
155
|
+
_EXCLUDED_FROM_SERIALIZATION: ClassVar[frozenset[str]] = frozenset({"_budget_hooks"})
|
|
156
|
+
|
|
157
|
+
program_candidates: list[dict[str, str]]
|
|
158
|
+
parent_program_for_candidate: list[list[ProgramIdx | None]]
|
|
159
|
+
prog_candidate_val_subscores: list[dict[DataId, float]]
|
|
160
|
+
prog_candidate_objective_scores: list[ObjectiveScores]
|
|
161
|
+
|
|
162
|
+
pareto_front_valset: dict[DataId, float]
|
|
163
|
+
program_at_pareto_front_valset: dict[DataId, set[ProgramIdx]]
|
|
164
|
+
objective_pareto_front: ObjectiveScores
|
|
165
|
+
program_at_pareto_front_objectives: dict[str, set[ProgramIdx]]
|
|
166
|
+
pareto_front_cartesian: dict[tuple[DataId, str], float]
|
|
167
|
+
program_at_pareto_front_cartesian: dict[tuple[DataId, str], set[ProgramIdx]]
|
|
168
|
+
|
|
169
|
+
list_of_named_predictors: list[str]
|
|
170
|
+
named_predictor_id_to_update_next_for_program_candidate: list[int]
|
|
171
|
+
|
|
172
|
+
i: int
|
|
173
|
+
num_full_ds_evals: int
|
|
174
|
+
|
|
175
|
+
total_num_evals: int
|
|
176
|
+
|
|
177
|
+
num_metric_calls_by_discovery: list[int]
|
|
178
|
+
|
|
179
|
+
full_program_trace: list[dict[str, Any]]
|
|
180
|
+
best_outputs_valset: dict[DataId, list[tuple[ProgramIdx, RolloutOutput]]] | None
|
|
181
|
+
|
|
182
|
+
validation_schema_version: int
|
|
183
|
+
|
|
184
|
+
# Optional evaluation cache for (candidate, example) pairs
|
|
185
|
+
evaluation_cache: "EvaluationCache[RolloutOutput, DataId] | None"
|
|
186
|
+
|
|
187
|
+
# Opaque bag for adapter-specific persistent state.
|
|
188
|
+
# Core GEPA never inspects this; adapters read/write via get_adapter_state()/set_adapter_state().
|
|
189
|
+
adapter_state: dict[str, Any]
|
|
190
|
+
|
|
191
|
+
def __init__(
|
|
192
|
+
self,
|
|
193
|
+
seed_candidate: dict[str, str],
|
|
194
|
+
base_evaluation: ValsetEvaluation[RolloutOutput, DataId],
|
|
195
|
+
track_best_outputs: bool = False,
|
|
196
|
+
frontier_type: FrontierType = "instance",
|
|
197
|
+
evaluation_cache: "EvaluationCache[RolloutOutput, DataId] | None" = None,
|
|
198
|
+
):
|
|
199
|
+
self.program_candidates = [dict(seed_candidate)]
|
|
200
|
+
self.prog_candidate_val_subscores = [dict(base_evaluation.scores_by_val_id)]
|
|
201
|
+
|
|
202
|
+
base_objective_aggregates = self._aggregate_objective_scores(base_evaluation.objective_scores_by_val_id)
|
|
203
|
+
self.prog_candidate_objective_scores = [base_objective_aggregates]
|
|
204
|
+
|
|
205
|
+
self.parent_program_for_candidate = [[None]]
|
|
206
|
+
|
|
207
|
+
self.frontier_type: FrontierType = frontier_type
|
|
208
|
+
self.pareto_front_valset = dict(base_evaluation.scores_by_val_id)
|
|
209
|
+
self.program_at_pareto_front_valset = {val_id: {0} for val_id in base_evaluation.scores_by_val_id.keys()}
|
|
210
|
+
self.objective_pareto_front = dict(base_objective_aggregates)
|
|
211
|
+
self.program_at_pareto_front_objectives = {objective: {0} for objective in base_objective_aggregates.keys()}
|
|
212
|
+
|
|
213
|
+
# Validate that objective scores are provided for frontier types that require them
|
|
214
|
+
if frontier_type in ("objective", "hybrid", "cartesian"):
|
|
215
|
+
if not base_evaluation.objective_scores_by_val_id:
|
|
216
|
+
raise ValueError(
|
|
217
|
+
f"frontier_type='{frontier_type}' requires objective_scores to be provided by the evaluator, "
|
|
218
|
+
f"but none were found. Use an evaluator that returns objective_scores or use frontier_type='instance'."
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
# Cartesian frontier will be base_evaluation.objective_scores_by_val_id
|
|
222
|
+
if frontier_type == "cartesian":
|
|
223
|
+
assert base_evaluation.objective_scores_by_val_id is not None # Already validated above
|
|
224
|
+
self.pareto_front_cartesian = {
|
|
225
|
+
(val_id, objective): objective_score
|
|
226
|
+
for val_id, objective_scores in base_evaluation.objective_scores_by_val_id.items()
|
|
227
|
+
for objective, objective_score in objective_scores.items()
|
|
228
|
+
}
|
|
229
|
+
self.program_at_pareto_front_cartesian = {
|
|
230
|
+
(val_id, objective): {0}
|
|
231
|
+
for val_id, objective_scores in base_evaluation.objective_scores_by_val_id.items()
|
|
232
|
+
for objective in objective_scores.keys()
|
|
233
|
+
}
|
|
234
|
+
else:
|
|
235
|
+
self.pareto_front_cartesian = {}
|
|
236
|
+
self.program_at_pareto_front_cartesian = {}
|
|
237
|
+
|
|
238
|
+
self.list_of_named_predictors = list(seed_candidate.keys())
|
|
239
|
+
self.named_predictor_id_to_update_next_for_program_candidate = [0]
|
|
240
|
+
self.i = -1
|
|
241
|
+
|
|
242
|
+
self.num_metric_calls_by_discovery = [0]
|
|
243
|
+
|
|
244
|
+
if track_best_outputs:
|
|
245
|
+
self.best_outputs_valset = {
|
|
246
|
+
val_id: [(0, output)] for val_id, output in base_evaluation.outputs_by_val_id.items()
|
|
247
|
+
}
|
|
248
|
+
else:
|
|
249
|
+
self.best_outputs_valset = None
|
|
250
|
+
|
|
251
|
+
self.full_program_trace = []
|
|
252
|
+
self.validation_schema_version = self._VALIDATION_SCHEMA_VERSION
|
|
253
|
+
self.evaluation_cache = evaluation_cache
|
|
254
|
+
self.adapter_state: dict[str, Any] = {}
|
|
255
|
+
|
|
256
|
+
def is_consistent(self) -> bool:
|
|
257
|
+
assert len(self.program_candidates) == len(self.parent_program_for_candidate)
|
|
258
|
+
assert len(self.program_candidates) == len(self.named_predictor_id_to_update_next_for_program_candidate)
|
|
259
|
+
assert len(self.program_candidates) == len(self.prog_candidate_val_subscores)
|
|
260
|
+
assert len(self.program_candidates) == len(self.prog_candidate_objective_scores)
|
|
261
|
+
assert len(self.program_candidates) == len(self.num_metric_calls_by_discovery)
|
|
262
|
+
|
|
263
|
+
assert len(self.pareto_front_valset) == len(self.program_at_pareto_front_valset)
|
|
264
|
+
assert set(self.pareto_front_valset.keys()) == set(self.program_at_pareto_front_valset.keys())
|
|
265
|
+
assert set(self.objective_pareto_front.keys()) == set(self.program_at_pareto_front_objectives.keys())
|
|
266
|
+
|
|
267
|
+
for front in self.program_at_pareto_front_valset.values():
|
|
268
|
+
for prog_idx in front:
|
|
269
|
+
assert prog_idx < len(self.program_candidates), (
|
|
270
|
+
"Program index in valset pareto front exceeds number of program candidates"
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
return True
|
|
274
|
+
|
|
275
|
+
# Budget Hook Mechanism
|
|
276
|
+
def add_budget_hook(self, hook: Callable[[int, int], None]) -> None:
|
|
277
|
+
"""Register a callback to be called whenever total_num_evals changes.
|
|
278
|
+
|
|
279
|
+
Args:
|
|
280
|
+
hook: A callable that receives (new_total, delta) when evals are incremented.
|
|
281
|
+
"""
|
|
282
|
+
if not hasattr(self, "_budget_hooks"):
|
|
283
|
+
self._budget_hooks: list[Callable[[int, int], None]] = []
|
|
284
|
+
self._budget_hooks.append(hook)
|
|
285
|
+
|
|
286
|
+
def increment_evals(self, count: int) -> None:
|
|
287
|
+
"""Increment total_num_evals and notify all registered hooks.
|
|
288
|
+
|
|
289
|
+
Args:
|
|
290
|
+
count: Number of evaluations to add.
|
|
291
|
+
"""
|
|
292
|
+
self.total_num_evals += count
|
|
293
|
+
# Lazy init handles states loaded from disk (which won't have _budget_hooks)
|
|
294
|
+
hooks = getattr(self, "_budget_hooks", None)
|
|
295
|
+
if hooks:
|
|
296
|
+
for hook in hooks:
|
|
297
|
+
hook(self.total_num_evals, count)
|
|
298
|
+
|
|
299
|
+
def _atomic_write_json(self, run_dir: str, filename: str, data: Any) -> None:
|
|
300
|
+
target_path = os.path.join(run_dir, filename)
|
|
301
|
+
tmp_path = target_path + ".tmp"
|
|
302
|
+
with open(tmp_path, "w") as f:
|
|
303
|
+
json.dump(data, f, indent=2, default=json_default)
|
|
304
|
+
os.replace(tmp_path, target_path)
|
|
305
|
+
|
|
306
|
+
def save(self, run_dir: str | None, *, use_cloudpickle: bool = False) -> None:
|
|
307
|
+
if run_dir is None:
|
|
308
|
+
return
|
|
309
|
+
if use_cloudpickle:
|
|
310
|
+
try:
|
|
311
|
+
import cloudpickle as pickle # type: ignore[import-not-found]
|
|
312
|
+
except ModuleNotFoundError:
|
|
313
|
+
import pickle
|
|
314
|
+
import warnings
|
|
315
|
+
|
|
316
|
+
warnings.warn(
|
|
317
|
+
"cloudpickle is not installed; falling back to standard pickle. "
|
|
318
|
+
"Install it with: pip install gepa[full] or pip install cloudpickle",
|
|
319
|
+
stacklevel=2,
|
|
320
|
+
)
|
|
321
|
+
else:
|
|
322
|
+
import pickle
|
|
323
|
+
# Exclude runtime-only attributes that can't be serialized (e.g., callback hooks)
|
|
324
|
+
serialized = {k: v for k, v in self.__dict__.items() if k not in self._EXCLUDED_FROM_SERIALIZATION}
|
|
325
|
+
serialized["validation_schema_version"] = GEPAState._VALIDATION_SCHEMA_VERSION
|
|
326
|
+
target_path = os.path.join(run_dir, "gepa_state.bin")
|
|
327
|
+
tmp_path = target_path + ".tmp"
|
|
328
|
+
try:
|
|
329
|
+
with open(tmp_path, "wb") as f:
|
|
330
|
+
pickle.dump(serialized, f)
|
|
331
|
+
except Exception as e:
|
|
332
|
+
if not use_cloudpickle:
|
|
333
|
+
raise type(e)(
|
|
334
|
+
f"{e}\n\nHint: standard pickle failed to serialize the GEPA state. "
|
|
335
|
+
"Try setting use_cloudpickle=True in EngineConfig, which can serialize "
|
|
336
|
+
"more object types (lambdas, closures, etc.). "
|
|
337
|
+
"Install it with: pip install gepa[full] or pip install cloudpickle"
|
|
338
|
+
) from e
|
|
339
|
+
raise
|
|
340
|
+
os.replace(tmp_path, target_path)
|
|
341
|
+
|
|
342
|
+
# Save run log and candidates as human-readable JSON
|
|
343
|
+
if self.full_program_trace:
|
|
344
|
+
self._atomic_write_json(run_dir, "run_log.json", self.full_program_trace)
|
|
345
|
+
if self.program_candidates:
|
|
346
|
+
self._atomic_write_json(run_dir, "candidates.json", self.program_candidates)
|
|
347
|
+
|
|
348
|
+
@staticmethod
|
|
349
|
+
def load(run_dir: str) -> "GEPAState[RolloutOutput, DataId]":
|
|
350
|
+
with open(os.path.join(run_dir, "gepa_state.bin"), "rb") as f:
|
|
351
|
+
import pickle
|
|
352
|
+
|
|
353
|
+
data = pickle.load(f)
|
|
354
|
+
|
|
355
|
+
# handle schema migration
|
|
356
|
+
version = data.get("validation_schema_version")
|
|
357
|
+
if version is None or version < 2:
|
|
358
|
+
GEPAState._migrate_from_legacy_state_v0(data)
|
|
359
|
+
version = data.get("validation_schema_version")
|
|
360
|
+
if version is None or version < GEPAState._VALIDATION_SCHEMA_VERSION:
|
|
361
|
+
GEPAState._upgrade_state_dict(data)
|
|
362
|
+
|
|
363
|
+
state = GEPAState.__new__(GEPAState)
|
|
364
|
+
state.__dict__.update(data)
|
|
365
|
+
|
|
366
|
+
state.validation_schema_version = GEPAState._VALIDATION_SCHEMA_VERSION
|
|
367
|
+
assert len(state.program_candidates) == len(state.prog_candidate_val_subscores)
|
|
368
|
+
assert len(state.program_candidates) == len(state.prog_candidate_objective_scores)
|
|
369
|
+
assert len(state.program_candidates) == len(state.num_metric_calls_by_discovery)
|
|
370
|
+
assert len(state.program_candidates) == len(state.parent_program_for_candidate)
|
|
371
|
+
assert len(state.program_candidates) == len(state.named_predictor_id_to_update_next_for_program_candidate)
|
|
372
|
+
assert len(state.pareto_front_valset) == len(state.program_at_pareto_front_valset)
|
|
373
|
+
assert set(state.pareto_front_valset.keys()) == set(state.program_at_pareto_front_valset.keys())
|
|
374
|
+
assert set(state.objective_pareto_front.keys()) == set(state.program_at_pareto_front_objectives.keys())
|
|
375
|
+
assert isinstance(state.adapter_state, dict)
|
|
376
|
+
return state
|
|
377
|
+
|
|
378
|
+
@staticmethod
|
|
379
|
+
def _migrate_from_legacy_state_v0(d: dict[str, Any]) -> None:
|
|
380
|
+
assert isinstance(d, dict)
|
|
381
|
+
assert "prog_candidate_val_subscores" in d
|
|
382
|
+
assert isinstance(d["prog_candidate_val_subscores"], list)
|
|
383
|
+
assert all(isinstance(scores, list) for scores in d["prog_candidate_val_subscores"])
|
|
384
|
+
legacy_scores: list[list[float]] = d.pop("prog_candidate_val_subscores", [])
|
|
385
|
+
d["prog_candidate_val_subscores"] = [dict(enumerate(scores)) for scores in legacy_scores]
|
|
386
|
+
|
|
387
|
+
pareto_front = d.get("pareto_front_valset")
|
|
388
|
+
if isinstance(pareto_front, list):
|
|
389
|
+
d["pareto_front_valset"] = dict(enumerate(pareto_front))
|
|
390
|
+
|
|
391
|
+
program_at_front = d.get("program_at_pareto_front_valset")
|
|
392
|
+
if isinstance(program_at_front, list):
|
|
393
|
+
d["program_at_pareto_front_valset"] = {idx: set(front) for idx, front in enumerate(program_at_front)}
|
|
394
|
+
|
|
395
|
+
best_outputs = d.get("best_outputs_valset")
|
|
396
|
+
if isinstance(best_outputs, list):
|
|
397
|
+
d["best_outputs_valset"] = {idx: list(outputs) for idx, outputs in enumerate(best_outputs)}
|
|
398
|
+
|
|
399
|
+
d["validation_schema_version"] = 2
|
|
400
|
+
|
|
401
|
+
@staticmethod
|
|
402
|
+
def _upgrade_state_dict(d: dict[str, Any]) -> None:
|
|
403
|
+
num_candidates = len(d.get("program_candidates", []))
|
|
404
|
+
if "prog_candidate_objective_scores" not in d:
|
|
405
|
+
d["prog_candidate_objective_scores"] = [{} for _ in range(num_candidates)]
|
|
406
|
+
if "objective_pareto_front" not in d:
|
|
407
|
+
d["objective_pareto_front"] = {}
|
|
408
|
+
if "program_at_pareto_front_objectives" not in d:
|
|
409
|
+
d["program_at_pareto_front_objectives"] = {}
|
|
410
|
+
if "frontier_type" not in d:
|
|
411
|
+
d["frontier_type"] = "instance"
|
|
412
|
+
# Since frontier_type instance does not require "pareto_front_cartesian" and "program_at_pareto_front_cartesian", we can safely set them to empty dicts.
|
|
413
|
+
d["pareto_front_cartesian"] = {}
|
|
414
|
+
d["program_at_pareto_front_cartesian"] = {}
|
|
415
|
+
# evaluation_cache is not persisted across runs by default; initialize to None if missing
|
|
416
|
+
if "evaluation_cache" not in d:
|
|
417
|
+
d["evaluation_cache"] = None
|
|
418
|
+
if "adapter_state" not in d:
|
|
419
|
+
d["adapter_state"] = {}
|
|
420
|
+
d["validation_schema_version"] = GEPAState._VALIDATION_SCHEMA_VERSION
|
|
421
|
+
|
|
422
|
+
@staticmethod
|
|
423
|
+
def _aggregate_objective_scores(
|
|
424
|
+
val_objective_scores: dict[DataId, ObjectiveScores] | None,
|
|
425
|
+
) -> ObjectiveScores:
|
|
426
|
+
if not val_objective_scores:
|
|
427
|
+
return {}
|
|
428
|
+
totals: dict[str, float] = {}
|
|
429
|
+
counts: dict[str, int] = {}
|
|
430
|
+
for objective_dict in val_objective_scores.values():
|
|
431
|
+
for objective, score in objective_dict.items():
|
|
432
|
+
totals[objective] = totals.get(objective, 0.0) + score
|
|
433
|
+
counts[objective] = counts.get(objective, 0) + 1
|
|
434
|
+
return {
|
|
435
|
+
objective: totals[objective] / counts[objective] for objective in totals.keys() if counts[objective] > 0
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
def get_program_average_val_subset(self, program_idx: int) -> tuple[float, int]:
|
|
439
|
+
# TODO: This should be only used/handled by the val_evaluation_policy, and never used directly.
|
|
440
|
+
scores = self.prog_candidate_val_subscores[program_idx]
|
|
441
|
+
if not scores:
|
|
442
|
+
return float("-inf"), 0
|
|
443
|
+
num_samples = len(scores)
|
|
444
|
+
avg = sum(scores.values()) / num_samples
|
|
445
|
+
return avg, num_samples
|
|
446
|
+
|
|
447
|
+
@property
|
|
448
|
+
def valset_evaluations(self) -> dict[DataId, list[ProgramIdx]]:
|
|
449
|
+
"""
|
|
450
|
+
Valset examples by id and programs that have evaluated them. Keys include only validation
|
|
451
|
+
ids that have been scored at least once.
|
|
452
|
+
"""
|
|
453
|
+
result: dict[DataId, list[ProgramIdx]] = defaultdict(list)
|
|
454
|
+
for program_idx, val_scores in enumerate(self.prog_candidate_val_subscores):
|
|
455
|
+
for val_id in val_scores.keys():
|
|
456
|
+
result[val_id].append(program_idx)
|
|
457
|
+
return result
|
|
458
|
+
|
|
459
|
+
@property
|
|
460
|
+
def program_full_scores_val_set(self) -> list[float]:
|
|
461
|
+
# TODO: This should be using the val_evaluation_policy instead of the get_program_average_val_subset method to calculate the scores.
|
|
462
|
+
return [
|
|
463
|
+
self.get_program_average_val_subset(program_idx)[0]
|
|
464
|
+
for program_idx in range(len(self.prog_candidate_val_subscores))
|
|
465
|
+
]
|
|
466
|
+
|
|
467
|
+
@property
|
|
468
|
+
def per_program_tracked_scores(self) -> list[float]:
|
|
469
|
+
return [
|
|
470
|
+
self.get_program_average_val_subset(program_idx)[0]
|
|
471
|
+
for program_idx in range(len(self.prog_candidate_val_subscores))
|
|
472
|
+
]
|
|
473
|
+
|
|
474
|
+
def _update_objective_pareto_front(self, objective_scores: ObjectiveScores, program_idx: ProgramIdx) -> None:
|
|
475
|
+
if not objective_scores:
|
|
476
|
+
return
|
|
477
|
+
for objective, score in objective_scores.items():
|
|
478
|
+
prev_score = self.objective_pareto_front.get(objective, float("-inf"))
|
|
479
|
+
if score > prev_score:
|
|
480
|
+
self.objective_pareto_front[objective] = score
|
|
481
|
+
self.program_at_pareto_front_objectives[objective] = {program_idx}
|
|
482
|
+
elif score == prev_score:
|
|
483
|
+
front = self.program_at_pareto_front_objectives.setdefault(objective, set())
|
|
484
|
+
front.add(program_idx)
|
|
485
|
+
|
|
486
|
+
def _update_pareto_front_for_val_id(
|
|
487
|
+
self,
|
|
488
|
+
val_id: DataId,
|
|
489
|
+
score: float,
|
|
490
|
+
program_idx: ProgramIdx,
|
|
491
|
+
output: RolloutOutput | None,
|
|
492
|
+
run_dir: str | None,
|
|
493
|
+
iteration: int,
|
|
494
|
+
) -> None:
|
|
495
|
+
prev_score = self.pareto_front_valset.get(val_id, float("-inf"))
|
|
496
|
+
if score > prev_score:
|
|
497
|
+
self.pareto_front_valset[val_id] = score
|
|
498
|
+
self.program_at_pareto_front_valset[val_id] = {program_idx}
|
|
499
|
+
if self.best_outputs_valset is not None and output is not None:
|
|
500
|
+
self.best_outputs_valset[val_id] = [(program_idx, output)]
|
|
501
|
+
if run_dir is not None:
|
|
502
|
+
task_dir = os.path.join(run_dir, "generated_best_outputs_valset", f"task_{val_id}")
|
|
503
|
+
os.makedirs(task_dir, exist_ok=True)
|
|
504
|
+
with open(os.path.join(task_dir, f"iter_{iteration}_prog_{program_idx}.json"), "w") as fout:
|
|
505
|
+
json.dump(output, fout, indent=4, default=json_default)
|
|
506
|
+
elif score == prev_score:
|
|
507
|
+
pareto_front = self.program_at_pareto_front_valset.setdefault(val_id, set())
|
|
508
|
+
pareto_front.add(program_idx)
|
|
509
|
+
if self.best_outputs_valset is not None and output is not None:
|
|
510
|
+
self.best_outputs_valset[val_id].append((program_idx, output))
|
|
511
|
+
|
|
512
|
+
def _update_pareto_front_for_cartesian(
|
|
513
|
+
self,
|
|
514
|
+
val_id: DataId,
|
|
515
|
+
objective: str,
|
|
516
|
+
objective_score: float,
|
|
517
|
+
program_idx: ProgramIdx,
|
|
518
|
+
) -> None:
|
|
519
|
+
prev_score = self.pareto_front_cartesian.get((val_id, objective), float("-inf"))
|
|
520
|
+
if objective_score > prev_score:
|
|
521
|
+
self.pareto_front_cartesian[(val_id, objective)] = objective_score
|
|
522
|
+
self.program_at_pareto_front_cartesian[(val_id, objective)] = {program_idx}
|
|
523
|
+
elif objective_score == prev_score:
|
|
524
|
+
front = self.program_at_pareto_front_cartesian.setdefault((val_id, objective), set())
|
|
525
|
+
front.add(program_idx)
|
|
526
|
+
|
|
527
|
+
def update_state_with_new_program(
|
|
528
|
+
self,
|
|
529
|
+
parent_program_idx: list[ProgramIdx],
|
|
530
|
+
new_program: dict[str, str],
|
|
531
|
+
valset_evaluation: ValsetEvaluation,
|
|
532
|
+
run_dir: str | None,
|
|
533
|
+
num_metric_calls_by_discovery_of_new_program: int,
|
|
534
|
+
) -> ProgramIdx:
|
|
535
|
+
new_program_idx = len(self.program_candidates)
|
|
536
|
+
self.program_candidates.append(dict(new_program))
|
|
537
|
+
self.num_metric_calls_by_discovery.append(num_metric_calls_by_discovery_of_new_program)
|
|
538
|
+
|
|
539
|
+
max_predictor_id = max(
|
|
540
|
+
[self.named_predictor_id_to_update_next_for_program_candidate[p] for p in parent_program_idx],
|
|
541
|
+
default=0,
|
|
542
|
+
)
|
|
543
|
+
self.named_predictor_id_to_update_next_for_program_candidate.append(max_predictor_id)
|
|
544
|
+
self.parent_program_for_candidate.append(list(parent_program_idx))
|
|
545
|
+
|
|
546
|
+
valset_scores = dict(valset_evaluation.scores_by_val_id)
|
|
547
|
+
self.prog_candidate_val_subscores.append(valset_scores)
|
|
548
|
+
objective_scores = self._aggregate_objective_scores(valset_evaluation.objective_scores_by_val_id)
|
|
549
|
+
self.prog_candidate_objective_scores.append(objective_scores)
|
|
550
|
+
|
|
551
|
+
for val_id, score in valset_scores.items():
|
|
552
|
+
output = valset_evaluation.outputs_by_val_id.get(val_id) if valset_evaluation.outputs_by_val_id else None
|
|
553
|
+
self._update_pareto_front_for_val_id(
|
|
554
|
+
val_id,
|
|
555
|
+
score,
|
|
556
|
+
new_program_idx,
|
|
557
|
+
output,
|
|
558
|
+
run_dir,
|
|
559
|
+
self.i + 1,
|
|
560
|
+
)
|
|
561
|
+
|
|
562
|
+
self._update_objective_pareto_front(objective_scores, new_program_idx)
|
|
563
|
+
|
|
564
|
+
if self.frontier_type in ("objective", "hybrid", "cartesian"):
|
|
565
|
+
if not valset_evaluation.objective_scores_by_val_id:
|
|
566
|
+
raise ValueError(
|
|
567
|
+
f"frontier_type='{self.frontier_type}' requires objective_scores to be provided by the evaluator, "
|
|
568
|
+
f"but none were found in the evaluation result."
|
|
569
|
+
)
|
|
570
|
+
|
|
571
|
+
if self.frontier_type == "cartesian":
|
|
572
|
+
assert valset_evaluation.objective_scores_by_val_id is not None # Validated above
|
|
573
|
+
for val_id, objective_scores in valset_evaluation.objective_scores_by_val_id.items():
|
|
574
|
+
for objective, objective_score in objective_scores.items():
|
|
575
|
+
self._update_pareto_front_for_cartesian(
|
|
576
|
+
val_id,
|
|
577
|
+
objective,
|
|
578
|
+
objective_score,
|
|
579
|
+
new_program_idx,
|
|
580
|
+
)
|
|
581
|
+
|
|
582
|
+
return new_program_idx
|
|
583
|
+
|
|
584
|
+
def _get_pareto_front_mapping(self, frontier_type: FrontierType) -> dict[FrontierKey, set[ProgramIdx]]:
|
|
585
|
+
if frontier_type == "instance":
|
|
586
|
+
return {val_id: set(front) for val_id, front in self.program_at_pareto_front_valset.items()}
|
|
587
|
+
if frontier_type == "objective":
|
|
588
|
+
return {objective: set(front) for objective, front in self.program_at_pareto_front_objectives.items()}
|
|
589
|
+
if frontier_type == "hybrid":
|
|
590
|
+
combined: dict[FrontierKey, set[ProgramIdx]] = {
|
|
591
|
+
("val_id", val_id): set(front) for val_id, front in self.program_at_pareto_front_valset.items()
|
|
592
|
+
}
|
|
593
|
+
for objective, front in self.program_at_pareto_front_objectives.items():
|
|
594
|
+
combined[("objective", objective)] = set(front)
|
|
595
|
+
return combined
|
|
596
|
+
if frontier_type == "cartesian":
|
|
597
|
+
return {
|
|
598
|
+
("cartesian", val_id, objective): set(front)
|
|
599
|
+
for (val_id, objective), front in self.program_at_pareto_front_cartesian.items()
|
|
600
|
+
}
|
|
601
|
+
raise ValueError(f"Unknown frontier_type: {frontier_type}")
|
|
602
|
+
|
|
603
|
+
def get_pareto_front_mapping(self) -> dict[FrontierKey, set[ProgramIdx]]:
|
|
604
|
+
"""Return frontier key to best-program-indices mapping based on configured frontier_type."""
|
|
605
|
+
return self._get_pareto_front_mapping(self.frontier_type)
|
|
606
|
+
|
|
607
|
+
def cached_evaluate(
|
|
608
|
+
self,
|
|
609
|
+
candidate: dict[str, str],
|
|
610
|
+
example_ids: list[DataId],
|
|
611
|
+
fetcher: Callable[[list[DataId]], Any],
|
|
612
|
+
evaluator: Callable[[Any, dict[str, str]], tuple[Any, list[float], Sequence[ObjectiveScores] | None]],
|
|
613
|
+
) -> tuple[list[float], int]:
|
|
614
|
+
"""Evaluate with optional caching. Returns (scores, num_actual_evals)."""
|
|
615
|
+
_, scores_by_id, _, num_actual_evals = self.cached_evaluate_full(candidate, example_ids, fetcher, evaluator)
|
|
616
|
+
return [scores_by_id[eid] for eid in example_ids], num_actual_evals
|
|
617
|
+
|
|
618
|
+
def cached_evaluate_full(
|
|
619
|
+
self,
|
|
620
|
+
candidate: dict[str, str],
|
|
621
|
+
example_ids: list[DataId],
|
|
622
|
+
fetcher: Callable[[list[DataId]], Any],
|
|
623
|
+
evaluator: Callable[[Any, dict[str, str]], tuple[Any, list[float], Sequence[ObjectiveScores] | None]],
|
|
624
|
+
) -> tuple[dict[DataId, RolloutOutput], dict[DataId, float], dict[DataId, ObjectiveScores] | None, int]:
|
|
625
|
+
"""Evaluate with optional caching, returning full results."""
|
|
626
|
+
if self.evaluation_cache is not None:
|
|
627
|
+
return self.evaluation_cache.evaluate_with_cache_full(candidate, example_ids, fetcher, evaluator)
|
|
628
|
+
batch = fetcher(example_ids)
|
|
629
|
+
outputs, scores, objective_scores = evaluator(batch, candidate)
|
|
630
|
+
outputs_by_id = dict(zip(example_ids, outputs, strict=False))
|
|
631
|
+
scores_by_id = dict(zip(example_ids, scores, strict=False))
|
|
632
|
+
objective_by_id = dict(zip(example_ids, objective_scores, strict=False)) if objective_scores else None
|
|
633
|
+
return outputs_by_id, scores_by_id, objective_by_id, len(example_ids)
|
|
634
|
+
|
|
635
|
+
|
|
636
|
+
def write_eval_scores_to_directory(scores: dict[DataId, float], output_dir: str) -> None:
|
|
637
|
+
for val_id, score in scores.items():
|
|
638
|
+
task_dir = os.path.join(output_dir, f"task_{val_id}")
|
|
639
|
+
os.makedirs(task_dir, exist_ok=True)
|
|
640
|
+
with open(os.path.join(task_dir, f"iter_{0}_prog_0.json"), "w") as f:
|
|
641
|
+
json.dump(score, f, indent=4, default=json_default)
|
|
642
|
+
|
|
643
|
+
|
|
644
|
+
def write_eval_outputs_to_directory(outputs, output_dir: str) -> None:
|
|
645
|
+
"""
|
|
646
|
+
Write generated rollout outputs (not scalar scores) to disk.
|
|
647
|
+
|
|
648
|
+
Structure:
|
|
649
|
+
{output_dir}/task_{val_id}/iter_0_prog_0.json
|
|
650
|
+
|
|
651
|
+
This directory is used to store best outputs for inspection/reuse.
|
|
652
|
+
"""
|
|
653
|
+
for val_id, output in outputs.items():
|
|
654
|
+
task_dir = os.path.join(output_dir, f"task_{val_id}")
|
|
655
|
+
os.makedirs(task_dir, exist_ok=True)
|
|
656
|
+
with open(os.path.join(task_dir, "iter_0_prog_0.json"), "w") as f:
|
|
657
|
+
json.dump(output, f, indent=4, default=json_default)
|
|
658
|
+
|
|
659
|
+
|
|
660
|
+
def initialize_gepa_state(
|
|
661
|
+
run_dir: str | None,
|
|
662
|
+
logger: LoggerProtocol,
|
|
663
|
+
seed_candidate: dict[str, str],
|
|
664
|
+
seed_valset_evaluation: ValsetEvaluation[RolloutOutput, DataId],
|
|
665
|
+
track_best_outputs: bool = False,
|
|
666
|
+
frontier_type: FrontierType = "instance",
|
|
667
|
+
evaluation_cache: "EvaluationCache[RolloutOutput, DataId] | None" = None,
|
|
668
|
+
) -> GEPAState[RolloutOutput, DataId]:
|
|
669
|
+
if run_dir is not None and os.path.exists(os.path.join(run_dir, "gepa_state.bin")):
|
|
670
|
+
logger.log("Loading gepa state from run dir")
|
|
671
|
+
gepa_state = GEPAState.load(run_dir)
|
|
672
|
+
if gepa_state.frontier_type != frontier_type:
|
|
673
|
+
raise ValueError(
|
|
674
|
+
f"Frontier type mismatch: requested '{frontier_type}' but loaded state has '{gepa_state.frontier_type}'. "
|
|
675
|
+
f"Use a different run_dir or match the frontier_type parameter."
|
|
676
|
+
)
|
|
677
|
+
# Sync cache with current run's cache_evaluation setting:
|
|
678
|
+
# - If caching is disabled (evaluation_cache is None), clear any loaded cache
|
|
679
|
+
# to respect the current run's cache_evaluation=False setting
|
|
680
|
+
# - If caching is enabled and the loaded state has a cache, preserve it
|
|
681
|
+
# (allows resuming with cached results from previous run)
|
|
682
|
+
# - If caching is enabled but no cache exists in loaded state, use the new empty cache
|
|
683
|
+
if evaluation_cache is None:
|
|
684
|
+
gepa_state.evaluation_cache = None
|
|
685
|
+
elif gepa_state.evaluation_cache is None:
|
|
686
|
+
gepa_state.evaluation_cache = evaluation_cache
|
|
687
|
+
# else: keep the loaded cache (gepa_state.evaluation_cache is already set)
|
|
688
|
+
else:
|
|
689
|
+
if run_dir is not None:
|
|
690
|
+
write_eval_outputs_to_directory(
|
|
691
|
+
seed_valset_evaluation.outputs_by_val_id,
|
|
692
|
+
os.path.join(run_dir, "generated_best_outputs_valset"),
|
|
693
|
+
)
|
|
694
|
+
|
|
695
|
+
gepa_state = GEPAState(
|
|
696
|
+
seed_candidate,
|
|
697
|
+
seed_valset_evaluation,
|
|
698
|
+
track_best_outputs=track_best_outputs,
|
|
699
|
+
frontier_type=frontier_type,
|
|
700
|
+
evaluation_cache=evaluation_cache,
|
|
701
|
+
)
|
|
702
|
+
|
|
703
|
+
gepa_state.num_full_ds_evals = 1
|
|
704
|
+
gepa_state.total_num_evals = len(seed_valset_evaluation.scores_by_val_id)
|
|
705
|
+
|
|
706
|
+
return gepa_state
|