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