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,367 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Structured experiment logging for GEPA + SWE-smith experiments.
|
|
3
|
+
|
|
4
|
+
Logs everything needed for analysis:
|
|
5
|
+
- Per-iteration metrics (tokens, steps, scores)
|
|
6
|
+
- Proposer inputs (prompts + traces sent to reflection LLM)
|
|
7
|
+
- Generated prompts at each iteration
|
|
8
|
+
- Aggregated statistics
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import json
|
|
12
|
+
import uuid
|
|
13
|
+
from dataclasses import asdict, dataclass, field
|
|
14
|
+
from datetime import datetime
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
from typing import Any
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass
|
|
20
|
+
class TaskMetrics:
|
|
21
|
+
"""Metrics for a single task execution."""
|
|
22
|
+
|
|
23
|
+
instance_id: str
|
|
24
|
+
success: bool
|
|
25
|
+
score: float
|
|
26
|
+
steps: int
|
|
27
|
+
estimated_tokens: int
|
|
28
|
+
has_patch: bool
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@dataclass
|
|
32
|
+
class EvalBatchMetrics:
|
|
33
|
+
"""
|
|
34
|
+
Metrics for one adapter.evaluate() call.
|
|
35
|
+
|
|
36
|
+
Note: GEPA calls evaluate() multiple times:
|
|
37
|
+
- Different candidates (prompts) on same tasks
|
|
38
|
+
- Train set sampling for reflection
|
|
39
|
+
- Val set for Pareto selection
|
|
40
|
+
|
|
41
|
+
Use prompt_hash to identify which candidate/prompt was used.
|
|
42
|
+
Same prompt_hash = same prompt being evaluated.
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
eval_id: int # Sequential counter of evaluate() calls
|
|
46
|
+
timestamp: str
|
|
47
|
+
prompt_hash: str # Unique identifier for this prompt/candidate
|
|
48
|
+
prompt_preview: str # First 100 chars for quick reference
|
|
49
|
+
|
|
50
|
+
# Task-level data
|
|
51
|
+
num_tasks: int
|
|
52
|
+
num_passed: int
|
|
53
|
+
pass_rate: float
|
|
54
|
+
|
|
55
|
+
# Aggregated metrics
|
|
56
|
+
avg_steps: float # Avg agent steps per task
|
|
57
|
+
avg_tokens: float # Avg agent tokens per task (estimated)
|
|
58
|
+
total_steps: int
|
|
59
|
+
total_tokens: int
|
|
60
|
+
|
|
61
|
+
# Per-task breakdown (instance_id tells you which task)
|
|
62
|
+
task_metrics: list[TaskMetrics] = field(default_factory=list)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@dataclass
|
|
66
|
+
class ProposerInput:
|
|
67
|
+
"""What gets sent to the proposer/reflection LLM."""
|
|
68
|
+
|
|
69
|
+
iteration: int
|
|
70
|
+
timestamp: str
|
|
71
|
+
current_prompt: str
|
|
72
|
+
reflection_records: list[dict[str, Any]] # From make_reflective_dataset
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class ExperimentLogger:
|
|
76
|
+
"""
|
|
77
|
+
Comprehensive logging for GEPA experiments.
|
|
78
|
+
|
|
79
|
+
Creates structured JSON files for later analysis:
|
|
80
|
+
- iterations.jsonl: One line per iteration with all metrics
|
|
81
|
+
- proposer_inputs.jsonl: What the proposer sees each iteration
|
|
82
|
+
- prompts/: Directory with each prompt version
|
|
83
|
+
- summary.json: Final experiment summary
|
|
84
|
+
|
|
85
|
+
Each run gets its own folder: logs/run_YYYYMMDD_HHMMSS_<short_uuid>/
|
|
86
|
+
"""
|
|
87
|
+
|
|
88
|
+
def __init__(self, log_dir: str = "gepa_results/logs", run_name: str | None = None, repo: str | None = None):
|
|
89
|
+
# Generate unique run ID
|
|
90
|
+
self.start_time = datetime.now()
|
|
91
|
+
timestamp = self.start_time.strftime("%Y%m%d_%H%M%S")
|
|
92
|
+
short_id = uuid.uuid4().hex[:6]
|
|
93
|
+
|
|
94
|
+
if run_name:
|
|
95
|
+
self.run_id = run_name
|
|
96
|
+
elif repo:
|
|
97
|
+
# Extract short repo name (e.g., "pygments" from "pygments__pygments")
|
|
98
|
+
short_repo = repo.split("__")[1]
|
|
99
|
+
self.run_id = f"run_{short_repo}_{timestamp}_{short_id}"
|
|
100
|
+
else:
|
|
101
|
+
self.run_id = f"run_{timestamp}_{short_id}"
|
|
102
|
+
|
|
103
|
+
# Create run-specific directory
|
|
104
|
+
self.base_log_dir = Path(log_dir)
|
|
105
|
+
self.log_dir = self.base_log_dir / self.run_id
|
|
106
|
+
self.log_dir.mkdir(parents=True, exist_ok=True)
|
|
107
|
+
|
|
108
|
+
# Create subdirectories
|
|
109
|
+
self.prompts_dir = self.log_dir / "prompts"
|
|
110
|
+
self.prompts_dir.mkdir(exist_ok=True)
|
|
111
|
+
|
|
112
|
+
# Initialize log files
|
|
113
|
+
self.iterations_file = self.log_dir / "iterations.jsonl"
|
|
114
|
+
self.proposer_file = self.log_dir / "proposer_inputs.jsonl"
|
|
115
|
+
self.summary_file = self.log_dir / "summary.json"
|
|
116
|
+
self.config_file = self.log_dir / "config.json"
|
|
117
|
+
|
|
118
|
+
# Track experiment state
|
|
119
|
+
self.eval_count = 0 # Number of evaluate() calls
|
|
120
|
+
self.all_evals: list[EvalBatchMetrics] = []
|
|
121
|
+
self.baseline_metrics: EvalBatchMetrics | None = None
|
|
122
|
+
self.prompts_seen: dict[str, str] = {} # prompt_hash -> full prompt
|
|
123
|
+
|
|
124
|
+
# Create symlink to latest run for easy access
|
|
125
|
+
latest_link = self.base_log_dir / "latest"
|
|
126
|
+
if latest_link.is_symlink():
|
|
127
|
+
latest_link.unlink()
|
|
128
|
+
if not latest_link.exists():
|
|
129
|
+
latest_link.symlink_to(self.run_id)
|
|
130
|
+
|
|
131
|
+
print("📊 Experiment logger initialized")
|
|
132
|
+
print(f" Run ID: {self.run_id}")
|
|
133
|
+
print(f" Run dir: {self.log_dir}")
|
|
134
|
+
print(f" Iterations: {self.iterations_file.name}")
|
|
135
|
+
print(f" Proposer inputs: {self.proposer_file.name}")
|
|
136
|
+
print(f" Prompts: {self.prompts_dir.name}/")
|
|
137
|
+
print(f" Latest symlink: {latest_link}")
|
|
138
|
+
|
|
139
|
+
def save_config(self, config: dict[str, Any]):
|
|
140
|
+
"""Save experiment configuration at the start of the run."""
|
|
141
|
+
config_with_meta = {"run_id": self.run_id, "start_time": self.start_time.isoformat(), **config}
|
|
142
|
+
with open(self.config_file, "w") as f:
|
|
143
|
+
json.dump(config_with_meta, f, indent=2)
|
|
144
|
+
print(f" Config saved: {self.config_file.name}")
|
|
145
|
+
|
|
146
|
+
def _prompt_hash(self, prompt: str) -> str:
|
|
147
|
+
"""Short hash for prompt identification."""
|
|
148
|
+
import hashlib
|
|
149
|
+
|
|
150
|
+
return hashlib.md5(prompt.encode()).hexdigest()[:8]
|
|
151
|
+
|
|
152
|
+
def log_eval_batch(
|
|
153
|
+
self,
|
|
154
|
+
prompt: str,
|
|
155
|
+
outputs: list[dict[str, Any]],
|
|
156
|
+
scores: list[float],
|
|
157
|
+
task_ids: list[str],
|
|
158
|
+
is_baseline: bool = False,
|
|
159
|
+
) -> EvalBatchMetrics:
|
|
160
|
+
"""
|
|
161
|
+
Log metrics for one adapter.evaluate() call.
|
|
162
|
+
|
|
163
|
+
Args:
|
|
164
|
+
prompt: The system prompt used for this evaluation
|
|
165
|
+
outputs: List of output dicts from adapter (with steps, tokens, etc.)
|
|
166
|
+
scores: List of scores (0.0 or 1.0)
|
|
167
|
+
task_ids: List of instance_ids
|
|
168
|
+
is_baseline: Whether this is the baseline evaluation
|
|
169
|
+
"""
|
|
170
|
+
timestamp = datetime.now().isoformat()
|
|
171
|
+
prompt_hash = self._prompt_hash(prompt)
|
|
172
|
+
eval_id = self.eval_count
|
|
173
|
+
|
|
174
|
+
# Track unique prompts
|
|
175
|
+
if prompt_hash not in self.prompts_seen:
|
|
176
|
+
self.prompts_seen[prompt_hash] = prompt
|
|
177
|
+
|
|
178
|
+
# Build per-task metrics
|
|
179
|
+
task_metrics = []
|
|
180
|
+
for _i, (output, score, task_id) in enumerate(zip(outputs, scores, task_ids, strict=False)):
|
|
181
|
+
tm = TaskMetrics(
|
|
182
|
+
instance_id=task_id,
|
|
183
|
+
success=score == 1.0,
|
|
184
|
+
score=score,
|
|
185
|
+
steps=output.get("steps", 0),
|
|
186
|
+
estimated_tokens=output.get("estimated_tokens", 0),
|
|
187
|
+
has_patch=bool(output.get("patch", "").strip()),
|
|
188
|
+
)
|
|
189
|
+
task_metrics.append(tm)
|
|
190
|
+
|
|
191
|
+
# Aggregate metrics
|
|
192
|
+
num_passed = sum(1 for s in scores if s == 1.0)
|
|
193
|
+
total_steps = sum(tm.steps for tm in task_metrics)
|
|
194
|
+
total_tokens = sum(tm.estimated_tokens for tm in task_metrics)
|
|
195
|
+
|
|
196
|
+
# Create prompt preview (first 100 chars, single line)
|
|
197
|
+
prompt_preview = prompt.replace("\n", " ")[:100] + "..." if len(prompt) > 100 else prompt.replace("\n", " ")
|
|
198
|
+
|
|
199
|
+
metrics = EvalBatchMetrics(
|
|
200
|
+
eval_id=eval_id,
|
|
201
|
+
timestamp=timestamp,
|
|
202
|
+
prompt_hash=prompt_hash,
|
|
203
|
+
prompt_preview=prompt_preview,
|
|
204
|
+
num_tasks=len(scores),
|
|
205
|
+
num_passed=num_passed,
|
|
206
|
+
pass_rate=num_passed / len(scores) if scores else 0.0,
|
|
207
|
+
avg_steps=total_steps / len(scores) if scores else 0.0,
|
|
208
|
+
avg_tokens=total_tokens / len(scores) if scores else 0.0,
|
|
209
|
+
total_steps=total_steps,
|
|
210
|
+
total_tokens=total_tokens,
|
|
211
|
+
task_metrics=task_metrics,
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
# Track baseline for comparison
|
|
215
|
+
if is_baseline or eval_id == 0:
|
|
216
|
+
self.baseline_metrics = metrics
|
|
217
|
+
|
|
218
|
+
self.all_evals.append(metrics)
|
|
219
|
+
self.eval_count += 1
|
|
220
|
+
|
|
221
|
+
# Write to JSONL (one line per eval batch)
|
|
222
|
+
with open(self.iterations_file, "a") as f:
|
|
223
|
+
# Convert dataclass to dict, handling nested dataclasses
|
|
224
|
+
data = asdict(metrics)
|
|
225
|
+
f.write(json.dumps(data) + "\n")
|
|
226
|
+
|
|
227
|
+
# Save prompt to file (only if new prompt)
|
|
228
|
+
prompt_file = self.prompts_dir / f"prompt_{prompt_hash}.txt"
|
|
229
|
+
if not prompt_file.exists():
|
|
230
|
+
with open(prompt_file, "w") as f:
|
|
231
|
+
f.write(prompt)
|
|
232
|
+
|
|
233
|
+
# Print summary
|
|
234
|
+
is_new_prompt = len(self.prompts_seen) > 1 and prompt_hash == list(self.prompts_seen.keys())[-1]
|
|
235
|
+
label = "BASELINE" if is_baseline else f"Eval #{eval_id}"
|
|
236
|
+
if is_new_prompt:
|
|
237
|
+
label += " (NEW PROMPT)"
|
|
238
|
+
print(f"\n📈 [{label}] {num_passed}/{len(scores)} passed ({metrics.pass_rate * 100:.1f}%)")
|
|
239
|
+
print(f" Avg steps: {metrics.avg_steps:.1f}, Avg tokens: {metrics.avg_tokens:.0f}")
|
|
240
|
+
print(f" Prompt: {prompt_hash} ({len(self.prompts_seen)} unique prompts seen)")
|
|
241
|
+
|
|
242
|
+
return metrics
|
|
243
|
+
|
|
244
|
+
def log_proposer_input(self, iteration: int, current_prompt: str, reflection_records: list[dict[str, Any]]):
|
|
245
|
+
"""
|
|
246
|
+
Log what gets sent to the proposer/reflection LLM.
|
|
247
|
+
"""
|
|
248
|
+
timestamp = datetime.now().isoformat()
|
|
249
|
+
|
|
250
|
+
proposer_input = ProposerInput(
|
|
251
|
+
iteration=iteration,
|
|
252
|
+
timestamp=timestamp,
|
|
253
|
+
current_prompt=current_prompt,
|
|
254
|
+
reflection_records=reflection_records,
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
# Write to JSONL
|
|
258
|
+
with open(self.proposer_file, "a") as f:
|
|
259
|
+
data = asdict(proposer_input)
|
|
260
|
+
f.write(json.dumps(data) + "\n")
|
|
261
|
+
|
|
262
|
+
# Also save a readable version
|
|
263
|
+
readable_file = self.prompts_dir / f"proposer_input_{iteration:03d}.txt"
|
|
264
|
+
with open(readable_file, "w") as f:
|
|
265
|
+
f.write(f"=== PROPOSER INPUT FOR ITERATION {iteration} ===\n")
|
|
266
|
+
f.write(f"Timestamp: {timestamp}\n\n")
|
|
267
|
+
f.write(f"=== CURRENT PROMPT ===\n{current_prompt}\n\n")
|
|
268
|
+
f.write(f"=== REFLECTION RECORDS ({len(reflection_records)} tasks) ===\n")
|
|
269
|
+
for i, record in enumerate(reflection_records):
|
|
270
|
+
f.write(f"\n--- Task {i + 1} ---\n")
|
|
271
|
+
f.write(json.dumps(record, indent=2))
|
|
272
|
+
f.write("\n")
|
|
273
|
+
|
|
274
|
+
print(f" 📝 Proposer input logged ({len(reflection_records)} reflection records)")
|
|
275
|
+
|
|
276
|
+
def get_comparison(self) -> dict[str, Any]:
|
|
277
|
+
"""Get before/after comparison metrics."""
|
|
278
|
+
if not self.all_evals:
|
|
279
|
+
return {}
|
|
280
|
+
|
|
281
|
+
baseline = self.baseline_metrics or self.all_evals[0]
|
|
282
|
+
latest = self.all_evals[-1]
|
|
283
|
+
|
|
284
|
+
return {
|
|
285
|
+
"baseline": {
|
|
286
|
+
"eval_id": baseline.eval_id,
|
|
287
|
+
"prompt_hash": baseline.prompt_hash,
|
|
288
|
+
"pass_rate": baseline.pass_rate,
|
|
289
|
+
"avg_steps": baseline.avg_steps,
|
|
290
|
+
"avg_tokens": baseline.avg_tokens,
|
|
291
|
+
},
|
|
292
|
+
"latest": {
|
|
293
|
+
"eval_id": latest.eval_id,
|
|
294
|
+
"prompt_hash": latest.prompt_hash,
|
|
295
|
+
"pass_rate": latest.pass_rate,
|
|
296
|
+
"avg_steps": latest.avg_steps,
|
|
297
|
+
"avg_tokens": latest.avg_tokens,
|
|
298
|
+
},
|
|
299
|
+
"improvement": {
|
|
300
|
+
"pass_rate_delta": latest.pass_rate - baseline.pass_rate,
|
|
301
|
+
"pass_rate_pct_change": ((latest.pass_rate - baseline.pass_rate) / baseline.pass_rate * 100)
|
|
302
|
+
if baseline.pass_rate > 0
|
|
303
|
+
else 0,
|
|
304
|
+
"steps_delta": latest.avg_steps - baseline.avg_steps,
|
|
305
|
+
"tokens_delta": latest.avg_tokens - baseline.avg_tokens,
|
|
306
|
+
},
|
|
307
|
+
"unique_prompts_tested": len(self.prompts_seen),
|
|
308
|
+
"total_eval_batches": len(self.all_evals),
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
def save_summary(self, best_prompt: str | None = None, extra_info: dict | None = None):
|
|
312
|
+
"""Save final experiment summary."""
|
|
313
|
+
comparison = self.get_comparison()
|
|
314
|
+
|
|
315
|
+
summary = {
|
|
316
|
+
"experiment_start": self.start_time.isoformat(),
|
|
317
|
+
"experiment_end": datetime.now().isoformat(),
|
|
318
|
+
"total_eval_batches": self.eval_count,
|
|
319
|
+
"unique_prompts_tested": len(self.prompts_seen),
|
|
320
|
+
"comparison": comparison,
|
|
321
|
+
"best_prompt_hash": self._prompt_hash(best_prompt) if best_prompt else None,
|
|
322
|
+
"prompts_seen": list(self.prompts_seen.keys()), # List of all prompt hashes
|
|
323
|
+
"extra_info": extra_info or {},
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
with open(self.summary_file, "w") as f:
|
|
327
|
+
json.dump(summary, f, indent=2)
|
|
328
|
+
|
|
329
|
+
print(f"\n{'=' * 60}")
|
|
330
|
+
print("EXPERIMENT SUMMARY")
|
|
331
|
+
print(f"{'=' * 60}")
|
|
332
|
+
if comparison:
|
|
333
|
+
print(f"Total eval batches: {comparison.get('total_eval_batches', self.eval_count)}")
|
|
334
|
+
print(f"Unique prompts tested: {comparison.get('unique_prompts_tested', len(self.prompts_seen))}")
|
|
335
|
+
print()
|
|
336
|
+
print(f"Baseline prompt: {comparison['baseline']['prompt_hash']}")
|
|
337
|
+
print(f"Final prompt: {comparison['latest']['prompt_hash']}")
|
|
338
|
+
print()
|
|
339
|
+
print(f"Baseline pass rate: {comparison['baseline']['pass_rate'] * 100:.1f}%")
|
|
340
|
+
print(f"Final pass rate: {comparison['latest']['pass_rate'] * 100:.1f}%")
|
|
341
|
+
print(f"Improvement: {comparison['improvement']['pass_rate_delta'] * 100:+.1f}%")
|
|
342
|
+
print()
|
|
343
|
+
print(f"Baseline avg steps: {comparison['baseline']['avg_steps']:.1f}")
|
|
344
|
+
print(f"Final avg steps: {comparison['latest']['avg_steps']:.1f}")
|
|
345
|
+
print(f"Steps delta: {comparison['improvement']['steps_delta']:+.1f}")
|
|
346
|
+
print()
|
|
347
|
+
print(f"Baseline avg tokens: {comparison['baseline']['avg_tokens']:.0f}")
|
|
348
|
+
print(f"Final avg tokens: {comparison['latest']['avg_tokens']:.0f}")
|
|
349
|
+
print(f"Tokens delta: {comparison['improvement']['tokens_delta']:+.0f}")
|
|
350
|
+
print(f"\nSummary saved to: {self.summary_file}")
|
|
351
|
+
|
|
352
|
+
return summary
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
# Global logger instance (set during experiment)
|
|
356
|
+
_experiment_logger: ExperimentLogger | None = None
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
def get_logger() -> ExperimentLogger | None:
|
|
360
|
+
"""Get the global experiment logger."""
|
|
361
|
+
return _experiment_logger
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
def set_logger(logger: ExperimentLogger):
|
|
365
|
+
"""Set the global experiment logger."""
|
|
366
|
+
global _experiment_logger
|
|
367
|
+
_experiment_logger = logger
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Fitness function for GEPA optimize_anything API.
|
|
3
|
+
|
|
4
|
+
This wraps the SWE harness logic into a simple fitness function that:
|
|
5
|
+
- Takes a candidate (prompt) and batch of tasks
|
|
6
|
+
- Returns (score, output, side_info) tuples for each task
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import threading
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
from gskill.swe_harness import SWEHarness
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def create_swe_fitness_fn(model_name: str = "gpt-5-mini", n_workers: int = 6, config_path: str | None = None):
|
|
16
|
+
"""Create a fitness function for SWE tasks with Docker containers.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
model_name: LiteLLM model name
|
|
20
|
+
n_workers: Number of parallel workers
|
|
21
|
+
config_path: Optional custom config path for the agent (defaults to mini.yaml)
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
fitness_fn: Function that evaluates candidates on batches
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
# Create harness pool - one per worker
|
|
28
|
+
harness_pool = [SWEHarness() for _ in range(n_workers)]
|
|
29
|
+
harness_available = [True] * n_workers
|
|
30
|
+
harness_lock = threading.Lock()
|
|
31
|
+
|
|
32
|
+
def get_harness() -> tuple[int, SWEHarness]:
|
|
33
|
+
"""Get an available harness from the pool."""
|
|
34
|
+
with harness_lock:
|
|
35
|
+
for i, available in enumerate(harness_available):
|
|
36
|
+
if available:
|
|
37
|
+
harness_available[i] = False
|
|
38
|
+
return i, harness_pool[i]
|
|
39
|
+
raise RuntimeError("No harness available - pool exhausted")
|
|
40
|
+
|
|
41
|
+
def release_harness(idx: int):
|
|
42
|
+
"""Release a harness back to the pool."""
|
|
43
|
+
with harness_lock:
|
|
44
|
+
harness_available[idx] = True
|
|
45
|
+
|
|
46
|
+
def process_single_task(
|
|
47
|
+
task: dict[str, Any], skills: str, task_idx: int, total_tasks: int
|
|
48
|
+
) -> tuple[float, dict[str, Any], dict[str, Any]]:
|
|
49
|
+
"""Process a single task and return (score, output, side_info)."""
|
|
50
|
+
|
|
51
|
+
# Get a harness from the pool
|
|
52
|
+
harness_idx, harness = get_harness()
|
|
53
|
+
|
|
54
|
+
instance_id = task.get("instance_id", "unknown")[:50]
|
|
55
|
+
|
|
56
|
+
try:
|
|
57
|
+
print(f"[Task {task_idx + 1}/{total_tasks}] {instance_id}...", flush=True)
|
|
58
|
+
|
|
59
|
+
# 1. Setup Docker Container
|
|
60
|
+
harness.setup_task(task_instance=task)
|
|
61
|
+
|
|
62
|
+
# 2. Run Agent
|
|
63
|
+
problem = task["problem_statement"]
|
|
64
|
+
patch, agent_trace, agent_metrics = harness.run_agent(
|
|
65
|
+
problem, skills, model_name=model_name, config_path=config_path
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
# 3. Verify with tests
|
|
69
|
+
has_patch = len(patch.strip()) > 0
|
|
70
|
+
passed = False
|
|
71
|
+
feedback_msg = ""
|
|
72
|
+
test_output = ""
|
|
73
|
+
|
|
74
|
+
if not has_patch:
|
|
75
|
+
passed = False
|
|
76
|
+
feedback_msg = "no_patch"
|
|
77
|
+
test_output = "No patch to test."
|
|
78
|
+
else:
|
|
79
|
+
# Use SWE-smith's run_patch_in_container for proper verification
|
|
80
|
+
# This creates a fresh container, checks out HEAD~1 (with tests),
|
|
81
|
+
# applies the patch, and runs tests
|
|
82
|
+
# First test FAIL_TO_PASS only
|
|
83
|
+
f2p_passed, f2p_output = harness.verify_with_patch(patch, f2p_only=True)
|
|
84
|
+
test_output = f"=== FAIL_TO_PASS TESTS ===\n{f2p_output}"
|
|
85
|
+
|
|
86
|
+
if not f2p_passed:
|
|
87
|
+
passed = False
|
|
88
|
+
feedback_msg = "f2p_failed"
|
|
89
|
+
else:
|
|
90
|
+
# PASS_TO_PASS Tests (Regression Check) - run full test suite
|
|
91
|
+
pass_to_pass = task.get("PASS_TO_PASS", [])
|
|
92
|
+
|
|
93
|
+
if pass_to_pass:
|
|
94
|
+
# Run full test (includes both f2p and p2p)
|
|
95
|
+
p2p_passed, p2p_output = harness.verify_with_patch(patch, f2p_only=False)
|
|
96
|
+
test_output += f"\n\n=== FULL TEST SUITE ===\n{p2p_output}"
|
|
97
|
+
|
|
98
|
+
if not p2p_passed:
|
|
99
|
+
passed = False
|
|
100
|
+
feedback_msg = "p2p_regression"
|
|
101
|
+
else:
|
|
102
|
+
passed = True
|
|
103
|
+
feedback_msg = "all_passed"
|
|
104
|
+
else:
|
|
105
|
+
passed = True
|
|
106
|
+
feedback_msg = "f2p_passed"
|
|
107
|
+
|
|
108
|
+
score = 1.0 if passed else 0.0
|
|
109
|
+
|
|
110
|
+
# Rollout output
|
|
111
|
+
output = {
|
|
112
|
+
"patch": patch,
|
|
113
|
+
"success": passed,
|
|
114
|
+
"steps": agent_metrics.get("steps", 0),
|
|
115
|
+
"estimated_tokens": agent_metrics.get("estimated_tokens", 0),
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
# Side info for reflection
|
|
119
|
+
side_info = {
|
|
120
|
+
"Input": {
|
|
121
|
+
"Task ID": instance_id,
|
|
122
|
+
"Problem": problem[:200] + "..." if len(problem) > 200 else problem,
|
|
123
|
+
},
|
|
124
|
+
"Generated Outputs": {
|
|
125
|
+
"Patch": patch[:500] + "..." if len(patch) > 500 else patch,
|
|
126
|
+
"Agent Trace": agent_trace,
|
|
127
|
+
},
|
|
128
|
+
"Feedback": {
|
|
129
|
+
"Status": feedback_msg,
|
|
130
|
+
"Test Output": test_output[:500] + "..." + test_output[-500:]
|
|
131
|
+
if len(test_output) > 1000
|
|
132
|
+
else test_output,
|
|
133
|
+
},
|
|
134
|
+
"scores": {
|
|
135
|
+
"correctness": score,
|
|
136
|
+
},
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
status = "✓ PASS" if passed else f"✗ FAIL ({feedback_msg})"
|
|
140
|
+
print(f" [{instance_id}] {status} [steps: {agent_metrics.get('steps', 0)}]", flush=True)
|
|
141
|
+
|
|
142
|
+
# Show brief debug info on failure
|
|
143
|
+
if not passed:
|
|
144
|
+
# Extract last few lines of test output for quick debugging
|
|
145
|
+
output_lines = test_output.strip().split("\n")
|
|
146
|
+
# Get last 5 non-empty lines
|
|
147
|
+
relevant_lines = [line for line in output_lines if line.strip()][-5:]
|
|
148
|
+
if relevant_lines:
|
|
149
|
+
print(f" Last output: {relevant_lines[-1][:100]}", flush=True)
|
|
150
|
+
|
|
151
|
+
# Cleanup
|
|
152
|
+
harness.cleanup()
|
|
153
|
+
|
|
154
|
+
return score, output, side_info
|
|
155
|
+
|
|
156
|
+
except Exception as e:
|
|
157
|
+
# Handle setup failures (e.g., git checkout failed, Docker issues)
|
|
158
|
+
error_msg = str(e)
|
|
159
|
+
print(f" [{instance_id[:30]}] ✗ FAIL (setup_error)", flush=True)
|
|
160
|
+
print(f" Error: {error_msg[:100]}", flush=True)
|
|
161
|
+
|
|
162
|
+
# Return failure with error info
|
|
163
|
+
output = {"patch": "", "success": False, "steps": 0, "estimated_tokens": 0, "error": error_msg}
|
|
164
|
+
side_info = {
|
|
165
|
+
"Input": {
|
|
166
|
+
"Task ID": instance_id,
|
|
167
|
+
"Problem": task.get("problem_statement", "")[:200],
|
|
168
|
+
},
|
|
169
|
+
"Generated Outputs": {
|
|
170
|
+
"Patch": "",
|
|
171
|
+
"Agent Trace": "",
|
|
172
|
+
},
|
|
173
|
+
"Feedback": {
|
|
174
|
+
"Status": "setup_error",
|
|
175
|
+
"Test Output": f"Setup failed: {error_msg}",
|
|
176
|
+
},
|
|
177
|
+
"scores": {
|
|
178
|
+
"correctness": 0.0,
|
|
179
|
+
},
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
try:
|
|
183
|
+
harness.cleanup()
|
|
184
|
+
except Exception:
|
|
185
|
+
pass
|
|
186
|
+
|
|
187
|
+
return 0.0, output, side_info
|
|
188
|
+
|
|
189
|
+
finally:
|
|
190
|
+
release_harness(harness_idx)
|
|
191
|
+
|
|
192
|
+
def fitness_fn(
|
|
193
|
+
candidate: dict[str, str],
|
|
194
|
+
example: dict[str, Any],
|
|
195
|
+
) -> tuple[float, dict[str, Any]]:
|
|
196
|
+
"""Evaluate candidate on a single task (called per-example by GEPA).
|
|
197
|
+
|
|
198
|
+
Args:
|
|
199
|
+
candidate: Dict with 'skills' key
|
|
200
|
+
example: A single task instance dict
|
|
201
|
+
|
|
202
|
+
Returns:
|
|
203
|
+
(score, side_info) tuple
|
|
204
|
+
"""
|
|
205
|
+
skills = candidate["skills"]
|
|
206
|
+
score, _output, side_info = process_single_task(example, skills, 0, 1)
|
|
207
|
+
return score, side_info
|
|
208
|
+
|
|
209
|
+
return fitness_fn
|