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,381 @@
|
|
|
1
|
+
"""Typed DSPy bridge for the opt-in skilled-proposer integration.
|
|
2
|
+
|
|
3
|
+
This module deliberately owns proposal-LM observability. It does not use
|
|
4
|
+
``dspy.LM`` or LiteLLM: every completion reaches the frozen provider transport
|
|
5
|
+
through ``complete_reflection_messages`` and is recorded before DSPy sees its
|
|
6
|
+
response.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import atexit
|
|
12
|
+
import dataclasses
|
|
13
|
+
import json
|
|
14
|
+
import os
|
|
15
|
+
import shutil
|
|
16
|
+
import tempfile
|
|
17
|
+
import uuid
|
|
18
|
+
from dataclasses import dataclass, field
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
from typing import TYPE_CHECKING, Any
|
|
21
|
+
from urllib.parse import urlparse
|
|
22
|
+
|
|
23
|
+
try:
|
|
24
|
+
import dspy
|
|
25
|
+
except ModuleNotFoundError as error:
|
|
26
|
+
if error.name != "dspy":
|
|
27
|
+
raise
|
|
28
|
+
dspy = None
|
|
29
|
+
|
|
30
|
+
if TYPE_CHECKING:
|
|
31
|
+
import dspy as dspy_types
|
|
32
|
+
|
|
33
|
+
from orizu_gepa.reflection import ProviderCompletion, complete_reflection_messages
|
|
34
|
+
from orizu_gepa.optimizer import TextGepaConfig
|
|
35
|
+
|
|
36
|
+
_SELECTION_ENV = "ORIZU_CANDIDATE_PROPOSER"
|
|
37
|
+
_SELECTION_VALUE = "skilled-proposer"
|
|
38
|
+
_SUPPORTED_ROLES = {"system", "user", "assistant"}
|
|
39
|
+
_ALLOWED_DSPY_REQUEST_FIELDS = frozenset({"temperature", "max_tokens", "cache"})
|
|
40
|
+
_DspyBaseLM = dspy.BaseLM if dspy is not None else object
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _require_dspy() -> Any:
|
|
44
|
+
if dspy is None:
|
|
45
|
+
raise RuntimeError("ALI_1505_DSPY_UNAVAILABLE")
|
|
46
|
+
return dspy
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _usage_totals() -> dict[str, int]:
|
|
50
|
+
return {"input_tokens": 0, "output_tokens": 0, "total_tokens": 0}
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _require_usage(usage: dict[str, int]) -> dict[str, int]:
|
|
54
|
+
try:
|
|
55
|
+
normalized = {name: int(usage[name]) for name in _usage_totals()}
|
|
56
|
+
except (KeyError, TypeError, ValueError) as error:
|
|
57
|
+
raise RuntimeError("ALI_1505_PROPOSAL_USAGE_INVALID") from error
|
|
58
|
+
if any(value < 0 for value in normalized.values()):
|
|
59
|
+
raise RuntimeError("ALI_1505_PROPOSAL_USAGE_INVALID")
|
|
60
|
+
return normalized
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@dataclass
|
|
64
|
+
class ProposalCallBudget:
|
|
65
|
+
"""The connector-owned proposal ledger; GEPA metric accounting is separate."""
|
|
66
|
+
|
|
67
|
+
max_calls: int | None = None
|
|
68
|
+
max_tokens: int | None = None
|
|
69
|
+
call_count: int = 0
|
|
70
|
+
usage: dict[str, int] = field(default_factory=_usage_totals)
|
|
71
|
+
|
|
72
|
+
def record(self, usage: dict[str, int] | None = None) -> None:
|
|
73
|
+
self.call_count += 1
|
|
74
|
+
if usage is None:
|
|
75
|
+
return
|
|
76
|
+
normalized = _require_usage(usage)
|
|
77
|
+
for name, value in normalized.items():
|
|
78
|
+
self.usage[name] += value
|
|
79
|
+
|
|
80
|
+
def has_safe_boundary_stop(self) -> bool:
|
|
81
|
+
return ((self.max_calls is not None and self.call_count >= self.max_calls) or
|
|
82
|
+
(self.max_tokens is not None and self.usage["total_tokens"] >= self.max_tokens))
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class ProposalObservability:
|
|
86
|
+
"""Durable per-provider-call proposal evidence and aggregate raw usage."""
|
|
87
|
+
|
|
88
|
+
def __init__(self, *, event_log_root: Path, durable_failure_root: Path, budget: ProposalCallBudget | None = None):
|
|
89
|
+
self.event_log_root = event_log_root
|
|
90
|
+
self.durable_failure_root = durable_failure_root
|
|
91
|
+
self.events: list[dict[str, Any]] = []
|
|
92
|
+
self.budget = budget or ProposalCallBudget()
|
|
93
|
+
self._temporary_root: Path | None = None
|
|
94
|
+
|
|
95
|
+
@classmethod
|
|
96
|
+
def local_for_test(
|
|
97
|
+
cls,
|
|
98
|
+
*,
|
|
99
|
+
event_log_root: Path | None = None,
|
|
100
|
+
durable_failure_root: Path | None = None,
|
|
101
|
+
) -> "ProposalObservability":
|
|
102
|
+
root = Path(tempfile.mkdtemp(prefix="orizu-ali1505-proposal-events-"))
|
|
103
|
+
result = cls(
|
|
104
|
+
event_log_root=event_log_root or root / "events",
|
|
105
|
+
durable_failure_root=durable_failure_root or root / "durable-failures",
|
|
106
|
+
)
|
|
107
|
+
result._temporary_root = root
|
|
108
|
+
atexit.register(shutil.rmtree, root, ignore_errors=True)
|
|
109
|
+
return result
|
|
110
|
+
|
|
111
|
+
def bind_budget(self, budget: ProposalCallBudget) -> None:
|
|
112
|
+
self.budget = budget
|
|
113
|
+
|
|
114
|
+
def close(self) -> None:
|
|
115
|
+
if self._temporary_root is not None:
|
|
116
|
+
shutil.rmtree(self._temporary_root, ignore_errors=True)
|
|
117
|
+
self._temporary_root = None
|
|
118
|
+
|
|
119
|
+
def __del__(self) -> None:
|
|
120
|
+
self.close()
|
|
121
|
+
|
|
122
|
+
def _append_event(self, event: dict[str, Any]) -> None:
|
|
123
|
+
self.event_log_root.mkdir(parents=True, exist_ok=True)
|
|
124
|
+
with (self.event_log_root / "events.jsonl").open("a", encoding="utf-8") as handle:
|
|
125
|
+
handle.write(json.dumps(event, sort_keys=True) + "\n")
|
|
126
|
+
self.events.append(event)
|
|
127
|
+
|
|
128
|
+
def _write_durable_failure(self, *, code: str, detail: str, correlation_id: str | None = None) -> None:
|
|
129
|
+
try:
|
|
130
|
+
self.durable_failure_root.mkdir(parents=True, exist_ok=True)
|
|
131
|
+
payload: dict[str, Any] = {"source": "skilled_proposer", "code": code, "detail": detail}
|
|
132
|
+
if correlation_id is not None:
|
|
133
|
+
payload["correlation_id"] = correlation_id
|
|
134
|
+
(self.durable_failure_root / "latest.json").write_text(
|
|
135
|
+
json.dumps(payload, sort_keys=True) + "\n", encoding="utf-8",
|
|
136
|
+
)
|
|
137
|
+
except Exception:
|
|
138
|
+
# The original delivery failure remains the actionable error. A
|
|
139
|
+
# second durable-store failure cannot be made durable by retrying it.
|
|
140
|
+
return
|
|
141
|
+
|
|
142
|
+
def record_success(self, event: dict[str, Any], usage: dict[str, int]) -> None:
|
|
143
|
+
self.budget.record(usage)
|
|
144
|
+
self._append_event(event)
|
|
145
|
+
|
|
146
|
+
def record_provider_failure(
|
|
147
|
+
self,
|
|
148
|
+
event: dict[str, Any],
|
|
149
|
+
*,
|
|
150
|
+
code: str,
|
|
151
|
+
detail: str,
|
|
152
|
+
usage: dict[str, int] | None = None,
|
|
153
|
+
) -> None:
|
|
154
|
+
self.budget.record(usage)
|
|
155
|
+
self._write_durable_failure(code=code, detail=detail, correlation_id=event.get("correlation_id"))
|
|
156
|
+
self._append_event(event)
|
|
157
|
+
|
|
158
|
+
def record_event_failure(self, error: Exception, *, correlation_id: str) -> None:
|
|
159
|
+
self._write_durable_failure(
|
|
160
|
+
code="proposal_observability_event_failed", detail=str(error), correlation_id=correlation_id,
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
def terminal_stats(self) -> dict[str, Any]:
|
|
164
|
+
return {"call_count": self.budget.call_count, "usage": dict(self.budget.usage)}
|
|
165
|
+
|
|
166
|
+
def terminal_lm_stats(self) -> dict[str, int]:
|
|
167
|
+
return {
|
|
168
|
+
"total_tokens_in": self.budget.usage["input_tokens"],
|
|
169
|
+
"total_tokens_out": self.budget.usage["output_tokens"],
|
|
170
|
+
"total_tokens": self.budget.usage["total_tokens"],
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
def write_terminal_lm_stats_artifact(self) -> str:
|
|
174
|
+
self.event_log_root.mkdir(parents=True, exist_ok=True)
|
|
175
|
+
path = self.event_log_root / "lm_stats.json"
|
|
176
|
+
path.write_text(json.dumps(self.terminal_lm_stats(), sort_keys=True) + "\n", encoding="utf-8")
|
|
177
|
+
return str(path)
|
|
178
|
+
|
|
179
|
+
def read_durable_failure(self) -> dict[str, Any]:
|
|
180
|
+
return json.loads((self.durable_failure_root / "latest.json").read_text(encoding="utf-8"))
|
|
181
|
+
|
|
182
|
+
def has_safe_boundary_stop(self) -> bool:
|
|
183
|
+
return self.budget.has_safe_boundary_stop()
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def _reject(message: str, *, code: str = "proposal_dspy_request_rejected") -> Any:
|
|
187
|
+
return _require_dspy().LMError(message, code=code)
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def _provider_messages(request: "dspy_types.LMRequest") -> list[dict[str, str]]:
|
|
191
|
+
if request.tools:
|
|
192
|
+
raise _reject("proposal_dspy_request_rejected: tools are not supported")
|
|
193
|
+
messages: list[dict[str, str]] = []
|
|
194
|
+
for message in request.messages:
|
|
195
|
+
dumped = message.model_dump()
|
|
196
|
+
role = dumped.get("role")
|
|
197
|
+
if role not in _SUPPORTED_ROLES:
|
|
198
|
+
raise _reject(f"proposal_dspy_request_rejected: unsupported message role {role!r}")
|
|
199
|
+
text_parts: list[str] = []
|
|
200
|
+
for part in dumped.get("parts", []):
|
|
201
|
+
if part.get("type") != "text" or not isinstance(part.get("text"), str):
|
|
202
|
+
raise _reject("proposal_dspy_request_rejected: only text message parts are supported")
|
|
203
|
+
text_parts.append(part["text"])
|
|
204
|
+
if not text_parts:
|
|
205
|
+
raise _reject("proposal_dspy_request_rejected: text messages must contain at least one text part")
|
|
206
|
+
messages.append({"role": role, "content": "".join(text_parts)})
|
|
207
|
+
return messages
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def _request_config(request: "dspy_types.LMRequest", base: TextGepaConfig) -> TextGepaConfig:
|
|
211
|
+
config = request.config
|
|
212
|
+
unsupported = {
|
|
213
|
+
name: value
|
|
214
|
+
for name, value in config.model_dump().items()
|
|
215
|
+
if name not in _ALLOWED_DSPY_REQUEST_FIELDS and value not in (None, {})
|
|
216
|
+
}
|
|
217
|
+
if unsupported:
|
|
218
|
+
raise _reject("proposal_dspy_request_rejected: unsupported DSPy generation configuration")
|
|
219
|
+
cache = config.cache
|
|
220
|
+
if cache is not None and (getattr(cache, "enabled", False) or getattr(cache, "rollout_id", None) is not None):
|
|
221
|
+
raise _reject("proposal_dspy_cache_rejected: proposal response caching is disabled", code="proposal_dspy_cache_rejected")
|
|
222
|
+
changes: dict[str, Any] = {}
|
|
223
|
+
if config.temperature is not None:
|
|
224
|
+
changes["reflection_temperature"] = config.temperature
|
|
225
|
+
if config.max_tokens is not None:
|
|
226
|
+
if config.max_tokens <= 0:
|
|
227
|
+
raise _reject("proposal_dspy_request_rejected: max_tokens must be positive")
|
|
228
|
+
changes["reflection_max_tokens"] = config.max_tokens
|
|
229
|
+
return dataclasses.replace(base, **changes) if changes else base
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def _loopback_endpoint(endpoint: str) -> str:
|
|
233
|
+
parsed = urlparse(endpoint)
|
|
234
|
+
if parsed.scheme not in {"http", "https"} or parsed.hostname not in {"127.0.0.1", "::1", "localhost"}:
|
|
235
|
+
raise ValueError("ALI_1505_BRIDGE_ENDPOINT_OVERRIDE_NOT_LOOPBACK")
|
|
236
|
+
if parsed.username or parsed.password:
|
|
237
|
+
raise ValueError("ALI_1505_BRIDGE_ENDPOINT_OVERRIDE_INVALID")
|
|
238
|
+
return endpoint
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
class OrizuSkilledProposerLM(_DspyBaseLM):
|
|
242
|
+
"""A typed DSPy LM whose only completion path is Orizu's provider transport."""
|
|
243
|
+
|
|
244
|
+
forward_contract = "typed_lm"
|
|
245
|
+
|
|
246
|
+
def __init__(
|
|
247
|
+
self,
|
|
248
|
+
*,
|
|
249
|
+
config: TextGepaConfig,
|
|
250
|
+
observability: ProposalObservability,
|
|
251
|
+
endpoint_override: str | None = None,
|
|
252
|
+
ssl_cert_file: str | None = None,
|
|
253
|
+
) -> None:
|
|
254
|
+
# Provider retries belong exclusively to the frozen transport. DSPy
|
|
255
|
+
# must not retry after a durable-event failure and create unrecorded
|
|
256
|
+
# duplicate proposal attempts.
|
|
257
|
+
_require_dspy().BaseLM.__init__(
|
|
258
|
+
self,
|
|
259
|
+
config.reflection_model,
|
|
260
|
+
temperature=config.reflection_temperature,
|
|
261
|
+
max_tokens=config.reflection_max_tokens,
|
|
262
|
+
cache=False,
|
|
263
|
+
num_retries=0,
|
|
264
|
+
)
|
|
265
|
+
self._config = config
|
|
266
|
+
self._observability = observability
|
|
267
|
+
self._endpoint_override = _loopback_endpoint(endpoint_override) if endpoint_override else None
|
|
268
|
+
self._ssl_cert_file = ssl_cert_file
|
|
269
|
+
|
|
270
|
+
def forward(self, request: "dspy_types.LMRequest") -> "dspy_types.LMResponse":
|
|
271
|
+
dspy_module = _require_dspy()
|
|
272
|
+
correlation_id = uuid.uuid4().hex
|
|
273
|
+
if request.model != self.model:
|
|
274
|
+
raise _reject(
|
|
275
|
+
"proposal_dspy_model_rejected: requested model differs from the configured reflection model",
|
|
276
|
+
code="proposal_dspy_model_rejected",
|
|
277
|
+
)
|
|
278
|
+
messages = _provider_messages(request)
|
|
279
|
+
config = _request_config(request, self._config)
|
|
280
|
+
try:
|
|
281
|
+
completion: ProviderCompletion = complete_reflection_messages(
|
|
282
|
+
model=self.model,
|
|
283
|
+
messages=messages,
|
|
284
|
+
config=config,
|
|
285
|
+
endpoint_override=self._endpoint_override,
|
|
286
|
+
ssl_cert_file=self._ssl_cert_file,
|
|
287
|
+
# The explicit loopback harness must never receive a real
|
|
288
|
+
# provider credential from this process.
|
|
289
|
+
api_key_override="loopback-test-key" if self._endpoint_override else None,
|
|
290
|
+
)
|
|
291
|
+
except Exception as error:
|
|
292
|
+
provider_code = getattr(error, "code", None)
|
|
293
|
+
failure_usage = getattr(error, "usage", None)
|
|
294
|
+
failure_event = {
|
|
295
|
+
"source": "skilled_proposer",
|
|
296
|
+
"status": "failure",
|
|
297
|
+
"attempt": 1,
|
|
298
|
+
"correlation_id": correlation_id,
|
|
299
|
+
"provider": "openai" if self.model.startswith("openai/") else "anthropic",
|
|
300
|
+
"cache_state": "disabled",
|
|
301
|
+
"failure_code": provider_code or "ALI_1505_PROPOSAL_TRANSPORT_FAILURE",
|
|
302
|
+
}
|
|
303
|
+
if failure_usage is not None:
|
|
304
|
+
failure_event["usage"] = failure_usage
|
|
305
|
+
try:
|
|
306
|
+
self._observability.record_provider_failure(
|
|
307
|
+
failure_event,
|
|
308
|
+
code="proposal_provider_transport_failed",
|
|
309
|
+
detail=str(error),
|
|
310
|
+
usage=failure_usage,
|
|
311
|
+
)
|
|
312
|
+
except Exception as event_error:
|
|
313
|
+
self._observability.record_event_failure(event_error, correlation_id=correlation_id)
|
|
314
|
+
raise RuntimeError("proposal_observability_event_failed") from event_error
|
|
315
|
+
raise dspy_module.LMError(
|
|
316
|
+
f"proposal_provider_transport_failed: {error}",
|
|
317
|
+
code="proposal_provider_transport_failed",
|
|
318
|
+
model=self.model,
|
|
319
|
+
provider="orizu",
|
|
320
|
+
provider_code=provider_code,
|
|
321
|
+
) from error
|
|
322
|
+
|
|
323
|
+
usage = _require_usage(completion.usage)
|
|
324
|
+
success_event = {
|
|
325
|
+
"source": "skilled_proposer",
|
|
326
|
+
"status": "success",
|
|
327
|
+
"attempt": 1,
|
|
328
|
+
"correlation_id": correlation_id,
|
|
329
|
+
"provider": completion.provider,
|
|
330
|
+
"model": self.model,
|
|
331
|
+
"request_id": completion.request_id,
|
|
332
|
+
"latency_ms": completion.latency_ms,
|
|
333
|
+
"usage": usage,
|
|
334
|
+
"cache_state": "disabled",
|
|
335
|
+
}
|
|
336
|
+
try:
|
|
337
|
+
self._observability.record_success(success_event, usage)
|
|
338
|
+
except Exception as error:
|
|
339
|
+
self._observability.record_event_failure(error, correlation_id=correlation_id)
|
|
340
|
+
raise RuntimeError("proposal_observability_event_failed") from error
|
|
341
|
+
return dspy_module.LMResponse.from_text(completion.text, model=self.model, usage=usage, cache_hit=False)
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
def make_skilled_proposer_bridge(
|
|
345
|
+
*,
|
|
346
|
+
config: TextGepaConfig,
|
|
347
|
+
observability: ProposalObservability,
|
|
348
|
+
budget: ProposalCallBudget,
|
|
349
|
+
endpoint_override: str | None = None,
|
|
350
|
+
ssl_cert_file: str | None = None,
|
|
351
|
+
) -> OrizuSkilledProposerLM:
|
|
352
|
+
"""Create the cache-disabled typed LM passed to unmodified SkilledProposer."""
|
|
353
|
+
observability.bind_budget(budget)
|
|
354
|
+
return OrizuSkilledProposerLM(
|
|
355
|
+
config=config,
|
|
356
|
+
observability=observability,
|
|
357
|
+
endpoint_override=endpoint_override,
|
|
358
|
+
ssl_cert_file=ssl_cert_file,
|
|
359
|
+
)
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
def make_skilled_proposer_from_environment(
|
|
363
|
+
*,
|
|
364
|
+
config: TextGepaConfig,
|
|
365
|
+
observability: ProposalObservability,
|
|
366
|
+
budget: ProposalCallBudget | None = None,
|
|
367
|
+
endpoint_override: str | None = None,
|
|
368
|
+
) -> Any | None:
|
|
369
|
+
"""Create the opt-in proposer only for the exact selected environment value."""
|
|
370
|
+
if os.environ.get(_SELECTION_ENV) != _SELECTION_VALUE:
|
|
371
|
+
return None
|
|
372
|
+
from skilled_proposer import SkilledProposer
|
|
373
|
+
|
|
374
|
+
bridge = make_skilled_proposer_bridge(
|
|
375
|
+
config=config,
|
|
376
|
+
observability=observability,
|
|
377
|
+
budget=budget or ProposalCallBudget(),
|
|
378
|
+
endpoint_override=endpoint_override,
|
|
379
|
+
ssl_cert_file=os.environ.get("SSL_CERT_FILE"),
|
|
380
|
+
)
|
|
381
|
+
return SkilledProposer(prompt_model=bridge)
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"""GEPA stop conditions that preserve Orizu's iteration boundaries."""
|
|
2
|
+
|
|
3
|
+
from collections.abc import Callable
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class IterationBoundaryStopper:
|
|
7
|
+
def __init__(self, max_iterations: int):
|
|
8
|
+
self.max_iterations = max_iterations
|
|
9
|
+
|
|
10
|
+
def __call__(self, state):
|
|
11
|
+
# GEPA's real loop starts at i=-1. Let it enter and complete the first
|
|
12
|
+
# iteration so this red is raised from the engine boundary, not test setup.
|
|
13
|
+
return state.i >= self.max_iterations - 1
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class MandatoryLoggingStopper:
|
|
17
|
+
"""Stop at GEPA's next safe boundary after a durable-log failure."""
|
|
18
|
+
|
|
19
|
+
def __init__(self, failure_flag):
|
|
20
|
+
self.failure_flag = failure_flag
|
|
21
|
+
|
|
22
|
+
def __call__(self, state):
|
|
23
|
+
return bool(self.failure_flag()) and state.i >= 0
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class MaxCandidateProposalsStopper:
|
|
27
|
+
"""Bound M1's candidate proposals at a safe GEPA boundary.
|
|
28
|
+
|
|
29
|
+
A completed iteration can skip reflection and produce no candidate. The
|
|
30
|
+
callback trace is therefore authoritative; rejected proposals still count.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
def __init__(self, max_candidate_proposals: int, proposal_count: Callable[[], int]):
|
|
34
|
+
self.max_candidate_proposals = max_candidate_proposals
|
|
35
|
+
self.proposal_count = proposal_count
|
|
36
|
+
|
|
37
|
+
def __call__(self, state):
|
|
38
|
+
return self.proposal_count() >= self.max_candidate_proposals
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class ProposalBudgetStopper:
|
|
42
|
+
"""Stop only between GEPA iterations after an owned proposal budget trips.
|
|
43
|
+
|
|
44
|
+
A custom candidate proposer does not pass through GEPA's ``reflection_lm``
|
|
45
|
+
or its cost tracking. Its typed DSPy bridge instead records raw provider
|
|
46
|
+
usage in an Orizu-owned ledger. This stopper deliberately reads only that
|
|
47
|
+
ledger and never changes ``state.total_num_evals``: metric-call budgeting
|
|
48
|
+
remains evaluator-only.
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
def __init__(self, proposal_budget):
|
|
52
|
+
self.proposal_budget = proposal_budget
|
|
53
|
+
|
|
54
|
+
def __call__(self, state):
|
|
55
|
+
return self.proposal_budget.has_safe_boundary_stop() and state.i >= 0
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
"""Translate official-GEPA callbacks into Orizu's established event envelope."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Callable
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
TRUNCATION_MARKER = "…[truncated]"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _candidate_id(value: Any) -> str | None:
|
|
13
|
+
if value is None:
|
|
14
|
+
return None
|
|
15
|
+
return str(value)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _truncate(value: str, limit: int | None) -> tuple[str, bool]:
|
|
19
|
+
if limit is None or len(value) <= limit:
|
|
20
|
+
return value, False
|
|
21
|
+
return value[:limit] + TRUNCATION_MARKER, True
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _components(candidate: Any, limit: int | None) -> tuple[dict[str, str], dict[str, int]]:
|
|
25
|
+
if not isinstance(candidate, dict):
|
|
26
|
+
return {}, {}
|
|
27
|
+
result: dict[str, str] = {}
|
|
28
|
+
truncated: dict[str, int] = {}
|
|
29
|
+
for key, value in candidate.items():
|
|
30
|
+
if not isinstance(key, str) or not isinstance(value, str):
|
|
31
|
+
continue
|
|
32
|
+
text, did_truncate = _truncate(value, limit)
|
|
33
|
+
result[key] = text
|
|
34
|
+
if did_truncate:
|
|
35
|
+
truncated[f"components.{key}"] = len(value.encode("utf-8"))
|
|
36
|
+
return result, truncated
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _row_results(event: dict[str, Any]) -> list[dict[str, Any]]:
|
|
40
|
+
scores = event.get("scores_by_val_id") or event.get("scores_by_id") or {}
|
|
41
|
+
outputs = event.get("outputs_by_val_id") or event.get("outputs_by_id") or {}
|
|
42
|
+
feedbacks = event.get("feedbacks_by_val_id") or {}
|
|
43
|
+
errors = event.get("errors_by_val_id") or {}
|
|
44
|
+
if not isinstance(scores, dict):
|
|
45
|
+
return []
|
|
46
|
+
rows = []
|
|
47
|
+
for row_id, score in scores.items():
|
|
48
|
+
output = outputs.get(row_id) if isinstance(outputs, dict) else None
|
|
49
|
+
side_info = output if isinstance(output, dict) else {}
|
|
50
|
+
adapter_row_id = side_info.get("row_id")
|
|
51
|
+
if adapter_row_id is None and str(row_id).isdigit():
|
|
52
|
+
raise ValueError(
|
|
53
|
+
"official GEPA callback carried positional row id without adapter row identity"
|
|
54
|
+
)
|
|
55
|
+
feedback = feedbacks.get(row_id) if isinstance(feedbacks, dict) else None
|
|
56
|
+
error = errors.get(row_id) if isinstance(errors, dict) else None
|
|
57
|
+
rows.append({
|
|
58
|
+
"row_id": str(adapter_row_id if adapter_row_id is not None else row_id),
|
|
59
|
+
"score": score if isinstance(score, (int, float)) else None,
|
|
60
|
+
# Official GEPA's typed callback has no feedback fields. The
|
|
61
|
+
# adapter therefore carries them in its rollout side-info; accept
|
|
62
|
+
# explicit callback maps too for forward compatibility.
|
|
63
|
+
"feedback": feedback if feedback is not None else side_info.get("feedback"),
|
|
64
|
+
"error": error if error is not None else side_info.get("error"),
|
|
65
|
+
"output": side_info.get("output", output),
|
|
66
|
+
})
|
|
67
|
+
return rows
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def translate_callback(event_name: str, event: dict[str, Any], *, run_id: str, **options: Any) -> dict[str, Any]:
|
|
71
|
+
"""Return an Orizu event for one official callback.
|
|
72
|
+
|
|
73
|
+
``max_payload_chars`` is deliberately characters, not bytes: the payload
|
|
74
|
+
disclosure retains original UTF-8 byte lengths for auditability.
|
|
75
|
+
"""
|
|
76
|
+
max_chars = options.get("max_payload_chars")
|
|
77
|
+
if max_chars is not None and (not isinstance(max_chars, int) or max_chars <= 0):
|
|
78
|
+
raise ValueError("max_payload_chars must be a positive integer")
|
|
79
|
+
|
|
80
|
+
iteration = event.get("iteration")
|
|
81
|
+
candidate_id = _candidate_id(event.get("candidate_idx", event.get("new_candidate_idx")))
|
|
82
|
+
parent_ids = event.get("parent_ids")
|
|
83
|
+
parent_candidate_id = (
|
|
84
|
+
_candidate_id(parent_ids[0])
|
|
85
|
+
if isinstance(parent_ids, (list, tuple)) and parent_ids else None
|
|
86
|
+
)
|
|
87
|
+
candidate = (
|
|
88
|
+
event.get("candidate")
|
|
89
|
+
or event.get("new_instructions")
|
|
90
|
+
or event.get("seed_candidate")
|
|
91
|
+
or event.get("parent_candidate")
|
|
92
|
+
or {}
|
|
93
|
+
)
|
|
94
|
+
components, truncation = _components(candidate, max_chars)
|
|
95
|
+
payload: dict[str, Any] = {"components": components} if components else {}
|
|
96
|
+
translated_name = event_name.removeprefix("on_")
|
|
97
|
+
event_type = translated_name
|
|
98
|
+
|
|
99
|
+
if event_name == "on_optimization_start":
|
|
100
|
+
event_type = "run_started"
|
|
101
|
+
payload.pop("components", None)
|
|
102
|
+
payload.update({
|
|
103
|
+
"seed_candidate_text": next(iter(components.values()), None),
|
|
104
|
+
"seed_components": components,
|
|
105
|
+
"trainset_size": event.get("trainset_size"),
|
|
106
|
+
"valset_size": event.get("valset_size"),
|
|
107
|
+
})
|
|
108
|
+
elif event_name == "on_iteration_start":
|
|
109
|
+
event_type = "iteration_started"
|
|
110
|
+
elif event_name == "on_iteration_end":
|
|
111
|
+
event_type = "iteration_completed"
|
|
112
|
+
payload.update({
|
|
113
|
+
"accepted": event.get("proposal_accepted"),
|
|
114
|
+
"skipped_reflection": event.get("skipped_reflection", False),
|
|
115
|
+
})
|
|
116
|
+
elif event_name == "on_optimization_end":
|
|
117
|
+
event_type = "run_completed"
|
|
118
|
+
payload.update({
|
|
119
|
+
"best_candidate_id": _candidate_id(event.get("best_candidate_idx")),
|
|
120
|
+
"total_iterations": event.get("total_iterations"),
|
|
121
|
+
"total_metric_calls": event.get("total_metric_calls"),
|
|
122
|
+
})
|
|
123
|
+
elif event_name == "on_candidate_accepted":
|
|
124
|
+
event_type = "acceptance_decision_made"
|
|
125
|
+
payload.update({
|
|
126
|
+
"accepted": True,
|
|
127
|
+
"proceed_to_valset": True,
|
|
128
|
+
"parent_score_total": event.get("parent_score_total"),
|
|
129
|
+
"child_score_total": event.get("child_score_total", event.get("new_score")),
|
|
130
|
+
})
|
|
131
|
+
elif event_name == "on_candidate_rejected":
|
|
132
|
+
event_type = "acceptance_decision_made"
|
|
133
|
+
payload.update({
|
|
134
|
+
"accepted": False,
|
|
135
|
+
"proceed_to_valset": False,
|
|
136
|
+
"parent_score_total": event.get("parent_score_total", event.get("old_score")),
|
|
137
|
+
"child_score_total": event.get("child_score_total", event.get("new_score")),
|
|
138
|
+
"reason": event.get("reason"),
|
|
139
|
+
})
|
|
140
|
+
elif event_name == "on_evaluation_skipped":
|
|
141
|
+
event_type = "reflection_skipped"
|
|
142
|
+
skipped_scores = event.get("scores") or []
|
|
143
|
+
payload.update({"reason": event.get("reason"), "parent_score_mean": (sum(skipped_scores) / len(skipped_scores)) if skipped_scores else None})
|
|
144
|
+
elif event_name == "on_valset_evaluated":
|
|
145
|
+
is_seed = iteration == 0 and candidate_id in {"0", "seed"}
|
|
146
|
+
event_type = "seed_val_set_completed" if is_seed else "child_val_set_completed"
|
|
147
|
+
rows = _row_results(event)
|
|
148
|
+
payload.update({
|
|
149
|
+
"candidate_id": "seed" if is_seed else candidate_id,
|
|
150
|
+
"split": "validation",
|
|
151
|
+
"score_mean": event.get("average_score"),
|
|
152
|
+
"row_results": rows,
|
|
153
|
+
"is_new_best": bool(event.get("is_best_program")),
|
|
154
|
+
})
|
|
155
|
+
elif event_name == "on_proposal_end":
|
|
156
|
+
event_type = "reflection_completed"
|
|
157
|
+
raw = event.get("raw_lm_outputs")
|
|
158
|
+
response = next(iter(raw.values()), None) if isinstance(raw, dict) else raw
|
|
159
|
+
prompts = event.get("prompts")
|
|
160
|
+
prompt = next(iter(prompts.values()), None) if isinstance(prompts, dict) else prompts
|
|
161
|
+
payload.update({"prompt": prompt, "response": response, "candidate_text": next(iter(components.values()), None)})
|
|
162
|
+
elif event_name == "on_budget_updated":
|
|
163
|
+
budget_kind = options.get("budget_kind", "max_metric_calls")
|
|
164
|
+
budget_limit = options.get("budget_limit")
|
|
165
|
+
if budget_kind == "max_metric_calls":
|
|
166
|
+
used = event.get("metric_calls_used")
|
|
167
|
+
remaining = event.get("metric_calls_remaining")
|
|
168
|
+
elif budget_kind == "max_iterations":
|
|
169
|
+
used = event.get("iteration", 0)
|
|
170
|
+
remaining = max(0, budget_limit - used) if isinstance(budget_limit, int) else None
|
|
171
|
+
else:
|
|
172
|
+
used = event.get("num_full_ds_evals", event.get("iteration", 0))
|
|
173
|
+
remaining = max(0, budget_limit - used) if isinstance(budget_limit, int) else None
|
|
174
|
+
event_type = "optimization_progress"
|
|
175
|
+
payload = {
|
|
176
|
+
"budget_kind": budget_kind,
|
|
177
|
+
"used": used,
|
|
178
|
+
"metric_calls_used": event.get("metric_calls_used"),
|
|
179
|
+
"metric_calls_remaining": event.get("metric_calls_remaining"),
|
|
180
|
+
"limit": budget_limit,
|
|
181
|
+
"budget_limit": budget_limit,
|
|
182
|
+
"metric_call_budget": options.get("approx_metric_call_limit") if budget_kind != "max_iterations" else None,
|
|
183
|
+
"remaining": remaining,
|
|
184
|
+
}
|
|
185
|
+
elif event_name == "on_merge_attempted":
|
|
186
|
+
note = {
|
|
187
|
+
"eventType": "run_note", "eventLayer": "extension", "optimizerFamily": "gepa",
|
|
188
|
+
"payload": {"code": "unsupported_gepa_merge", "message": "unsupported GEPA merge: two-parent candidates cannot be rendered"},
|
|
189
|
+
}
|
|
190
|
+
sink = options.get("run_note_sink")
|
|
191
|
+
if isinstance(sink, Callable):
|
|
192
|
+
sink(note)
|
|
193
|
+
raise ValueError("unsupported GEPA merge: two-parent candidates cannot be rendered")
|
|
194
|
+
|
|
195
|
+
if truncation:
|
|
196
|
+
payload["payload_truncated"] = True
|
|
197
|
+
payload["truncation"] = {"fields": truncation}
|
|
198
|
+
return {
|
|
199
|
+
"runId": run_id, "eventType": event_type, "optimizerFamily": "gepa", "iteration": iteration,
|
|
200
|
+
"candidateId": "seed" if event_name == "on_valset_evaluated" and iteration == 0 and candidate_id in {"0", "seed"} else candidate_id,
|
|
201
|
+
"parentCandidateId": parent_candidate_id, "childCandidateId": None,
|
|
202
|
+
"payload": payload,
|
|
203
|
+
}
|
|
@@ -3,7 +3,7 @@ requires = ["setuptools>=68"]
|
|
|
3
3
|
build-backend = "setuptools.build_meta"
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
|
-
name = "orizu-gepa"
|
|
6
|
+
name = "orizu-gepa-legacy"
|
|
7
7
|
version = "0.1.0"
|
|
8
8
|
description = "Orizu-owned text prompt optimizer with GEPA-style reflection and mandatory live logging."
|
|
9
9
|
requires-python = ">=3.10"
|