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,457 @@
|
|
|
1
|
+
# Copyright (c) 2025 Lakshya A Agrawal and the GEPA contributors
|
|
2
|
+
# https://github.com/gepa-ai/gepa
|
|
3
|
+
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ExperimentTracker:
|
|
8
|
+
"""
|
|
9
|
+
Unified experiment tracking that supports both wandb and mlflow.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
def __enter__(self):
|
|
13
|
+
"""Context manager entry."""
|
|
14
|
+
self.initialize()
|
|
15
|
+
self.start_run()
|
|
16
|
+
return self
|
|
17
|
+
|
|
18
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
19
|
+
"""Context manager exit - always end the run."""
|
|
20
|
+
self.end_run()
|
|
21
|
+
return False # Don't suppress exceptions
|
|
22
|
+
|
|
23
|
+
def __init__(
|
|
24
|
+
self,
|
|
25
|
+
use_wandb: bool = False,
|
|
26
|
+
wandb_api_key: str | None = None,
|
|
27
|
+
wandb_init_kwargs: dict[str, Any] | None = None,
|
|
28
|
+
wandb_attach_existing: bool = False,
|
|
29
|
+
wandb_step_metric: str | None = None,
|
|
30
|
+
use_mlflow: bool = False,
|
|
31
|
+
mlflow_tracking_uri: str | None = None,
|
|
32
|
+
mlflow_experiment_name: str | None = None,
|
|
33
|
+
mlflow_attach_existing: bool = False,
|
|
34
|
+
key_prefix: str = "",
|
|
35
|
+
):
|
|
36
|
+
self.use_wandb = use_wandb
|
|
37
|
+
self.use_mlflow = use_mlflow
|
|
38
|
+
|
|
39
|
+
self.wandb_api_key = wandb_api_key
|
|
40
|
+
self.wandb_init_kwargs = wandb_init_kwargs or {}
|
|
41
|
+
self.wandb_attach_existing = wandb_attach_existing
|
|
42
|
+
self.wandb_step_metric = wandb_step_metric
|
|
43
|
+
self.mlflow_tracking_uri = mlflow_tracking_uri
|
|
44
|
+
self.mlflow_experiment_name = mlflow_experiment_name
|
|
45
|
+
self.mlflow_attach_existing = mlflow_attach_existing
|
|
46
|
+
self.key_prefix = key_prefix
|
|
47
|
+
|
|
48
|
+
self._created_mlflow_run = False
|
|
49
|
+
self._mlflow_run_id: str | None = None
|
|
50
|
+
self._mlflow_client: Any = None
|
|
51
|
+
self._wandb_step_metric_defined = False
|
|
52
|
+
|
|
53
|
+
# Accumulate table rows so each wandb.log() sends the full growing
|
|
54
|
+
# table, not just the latest row. Without this, commit=False causes
|
|
55
|
+
# the pending dict to overwrite earlier single-row tables with the
|
|
56
|
+
# newest one, and only the last row per commit cycle survives.
|
|
57
|
+
self._wandb_table_rows: dict[str, tuple[list[str], list[list]]] = {}
|
|
58
|
+
|
|
59
|
+
def _p(self, key: str) -> str:
|
|
60
|
+
"""Prepend key_prefix to a key/name, if one is set."""
|
|
61
|
+
return f"{self.key_prefix}{key}" if self.key_prefix else key
|
|
62
|
+
|
|
63
|
+
def initialize(self):
|
|
64
|
+
"""Initialize the logging backends."""
|
|
65
|
+
if self.use_wandb:
|
|
66
|
+
self._initialize_wandb()
|
|
67
|
+
if self.use_mlflow:
|
|
68
|
+
self._initialize_mlflow()
|
|
69
|
+
|
|
70
|
+
def _initialize_wandb(self):
|
|
71
|
+
"""Initialize wandb."""
|
|
72
|
+
try:
|
|
73
|
+
import wandb # type: ignore
|
|
74
|
+
|
|
75
|
+
if self.wandb_api_key:
|
|
76
|
+
wandb.login(key=self.wandb_api_key, verify=True)
|
|
77
|
+
else:
|
|
78
|
+
wandb.login()
|
|
79
|
+
except ImportError:
|
|
80
|
+
raise ImportError("wandb is not installed. Please install it or set backend='mlflow' or 'none'.")
|
|
81
|
+
except Exception as e:
|
|
82
|
+
raise RuntimeError(f"Error logging into wandb: {e}")
|
|
83
|
+
|
|
84
|
+
def _initialize_mlflow(self):
|
|
85
|
+
"""Initialize mlflow."""
|
|
86
|
+
try:
|
|
87
|
+
import mlflow # type: ignore
|
|
88
|
+
|
|
89
|
+
if self.mlflow_tracking_uri:
|
|
90
|
+
mlflow.set_tracking_uri(self.mlflow_tracking_uri)
|
|
91
|
+
if self.mlflow_experiment_name:
|
|
92
|
+
mlflow.set_experiment(self.mlflow_experiment_name)
|
|
93
|
+
except ImportError:
|
|
94
|
+
raise ImportError("mlflow is not installed. Please install it or set backend='wandb' or 'none'.")
|
|
95
|
+
except Exception as e:
|
|
96
|
+
raise RuntimeError(f"Error setting up mlflow: {e}")
|
|
97
|
+
|
|
98
|
+
def _define_wandb_step_metric(self) -> None:
|
|
99
|
+
"""Declare a custom x-axis for all GEPA metrics in wandb.
|
|
100
|
+
|
|
101
|
+
Called once on the first ``log_metrics`` call (not in ``start_run``)
|
|
102
|
+
so that it works regardless of whether GEPA owns the ``wandb.init``
|
|
103
|
+
call or is attaching to an existing run.
|
|
104
|
+
|
|
105
|
+
When ``wandb_step_metric`` is set, all GEPA metrics are plotted
|
|
106
|
+
against this custom step metric instead of wandb's global monotonic
|
|
107
|
+
step counter. This avoids conflicts when GEPA is embedded inside
|
|
108
|
+
a host training loop that uses its own step counter.
|
|
109
|
+
"""
|
|
110
|
+
if self._wandb_step_metric_defined or not self.wandb_step_metric:
|
|
111
|
+
return
|
|
112
|
+
try:
|
|
113
|
+
import wandb # type: ignore
|
|
114
|
+
|
|
115
|
+
if wandb.run is not None:
|
|
116
|
+
wandb.define_metric(self.wandb_step_metric, hidden=False)
|
|
117
|
+
# Scope the custom x-axis to GEPA's prefixed metrics only.
|
|
118
|
+
# Using "*" would override the x-axis for ALL metrics in the
|
|
119
|
+
# run, including the host's metrics (e.g. train/loss), causing
|
|
120
|
+
# wandb to drop the host's data after GEPA runs.
|
|
121
|
+
if self.key_prefix:
|
|
122
|
+
glob = f"{self.key_prefix}*"
|
|
123
|
+
else:
|
|
124
|
+
glob = "*"
|
|
125
|
+
wandb.define_metric(glob, step_metric=self.wandb_step_metric)
|
|
126
|
+
self._wandb_step_metric_defined = True
|
|
127
|
+
except Exception as e:
|
|
128
|
+
print(f"Warning: Failed to define wandb step metric: {e}")
|
|
129
|
+
|
|
130
|
+
def start_run(self):
|
|
131
|
+
"""Start a new run.
|
|
132
|
+
|
|
133
|
+
When ``wandb_attach_existing=True`` the tracker skips ``wandb.init()``
|
|
134
|
+
and logs into whatever run is already active in the process.
|
|
135
|
+
When ``mlflow_attach_existing=True`` the tracker skips
|
|
136
|
+
``mlflow.start_run()`` and logs into the already-active MLflow run.
|
|
137
|
+
In both cases ``end_run()`` will not terminate the run.
|
|
138
|
+
"""
|
|
139
|
+
if self.use_wandb:
|
|
140
|
+
if self.wandb_attach_existing:
|
|
141
|
+
# Attach to the active run — no init, no finish later.
|
|
142
|
+
pass
|
|
143
|
+
else:
|
|
144
|
+
import wandb # type: ignore
|
|
145
|
+
|
|
146
|
+
wandb.init(**self.wandb_init_kwargs)
|
|
147
|
+
if self.use_mlflow:
|
|
148
|
+
import mlflow # type: ignore
|
|
149
|
+
from mlflow import MlflowClient # type: ignore
|
|
150
|
+
|
|
151
|
+
if self.mlflow_attach_existing:
|
|
152
|
+
# Attach to the active run — no start, no end later.
|
|
153
|
+
self._created_mlflow_run = False
|
|
154
|
+
else:
|
|
155
|
+
# Only start a new run if there's no active run
|
|
156
|
+
if mlflow.active_run() is None:
|
|
157
|
+
mlflow.start_run()
|
|
158
|
+
self._created_mlflow_run = True
|
|
159
|
+
else:
|
|
160
|
+
self._created_mlflow_run = False
|
|
161
|
+
|
|
162
|
+
# Capture run_id and create a client for thread-safe logging.
|
|
163
|
+
# mlflow.active_run() is thread-local, so parallel threads
|
|
164
|
+
# (e.g. parallel proposals) would auto-create new runs without this.
|
|
165
|
+
active = mlflow.active_run()
|
|
166
|
+
if active is not None:
|
|
167
|
+
self._mlflow_run_id = active.info.run_id
|
|
168
|
+
try:
|
|
169
|
+
tracking_uri = mlflow.get_tracking_uri()
|
|
170
|
+
self._mlflow_client = MlflowClient(tracking_uri=tracking_uri)
|
|
171
|
+
except Exception:
|
|
172
|
+
# MlflowClient creation can fail in test environments;
|
|
173
|
+
# fall back to fluent API (thread-unsafe but functional).
|
|
174
|
+
self._mlflow_client = None
|
|
175
|
+
|
|
176
|
+
def log_config(self, config: dict[str, Any]) -> None:
|
|
177
|
+
"""Log run configuration/hyperparameters to the active backends.
|
|
178
|
+
|
|
179
|
+
Args:
|
|
180
|
+
config: Flat dict of config key-value pairs. Non-serializable values
|
|
181
|
+
are converted to strings.
|
|
182
|
+
"""
|
|
183
|
+
safe_config = {}
|
|
184
|
+
for k, v in config.items():
|
|
185
|
+
if isinstance(v, bool | int | float | str | type(None)):
|
|
186
|
+
safe_config[k] = v
|
|
187
|
+
else:
|
|
188
|
+
safe_config[k] = str(v)
|
|
189
|
+
|
|
190
|
+
if self.use_wandb:
|
|
191
|
+
try:
|
|
192
|
+
import wandb # type: ignore
|
|
193
|
+
|
|
194
|
+
prefixed = {self._p(k): v for k, v in safe_config.items()}
|
|
195
|
+
wandb.config.update(prefixed, allow_val_change=True)
|
|
196
|
+
except Exception as e:
|
|
197
|
+
print(f"Warning: Failed to log config to wandb: {e}")
|
|
198
|
+
|
|
199
|
+
if self.use_mlflow:
|
|
200
|
+
try:
|
|
201
|
+
str_params = {self._p(k): str(v) for k, v in safe_config.items()}
|
|
202
|
+
if self._mlflow_client and self._mlflow_run_id:
|
|
203
|
+
for k, v in str_params.items():
|
|
204
|
+
self._mlflow_client.log_param(self._mlflow_run_id, k, v)
|
|
205
|
+
else:
|
|
206
|
+
import mlflow # type: ignore
|
|
207
|
+
mlflow.log_params(str_params)
|
|
208
|
+
except Exception as e:
|
|
209
|
+
print(f"Warning: Failed to log config to mlflow: {e}")
|
|
210
|
+
|
|
211
|
+
def log_metrics(self, metrics: dict[str, Any], step: int | None = None):
|
|
212
|
+
"""Log metrics to the active backends."""
|
|
213
|
+
if self.use_wandb:
|
|
214
|
+
try:
|
|
215
|
+
import wandb # type: ignore
|
|
216
|
+
|
|
217
|
+
# Lazily define the custom step metric on the first log call.
|
|
218
|
+
self._define_wandb_step_metric()
|
|
219
|
+
|
|
220
|
+
# Filter to numeric values only — non-numeric data (dicts, strings)
|
|
221
|
+
# is logged via log_table() instead to avoid noisy flat charts
|
|
222
|
+
numeric_metrics = {self._p(k): v for k, v in metrics.items() if isinstance(v, int | float)}
|
|
223
|
+
if numeric_metrics:
|
|
224
|
+
if self.wandb_step_metric and step is not None:
|
|
225
|
+
# Use custom x-axis: inject the step as a metric value
|
|
226
|
+
# and omit the step= arg to avoid conflicting with the
|
|
227
|
+
# host run's global step counter.
|
|
228
|
+
numeric_metrics[self.wandb_step_metric] = step
|
|
229
|
+
wandb.log(numeric_metrics)
|
|
230
|
+
else:
|
|
231
|
+
wandb.log(numeric_metrics, step=step)
|
|
232
|
+
except Exception as e:
|
|
233
|
+
print(f"Warning: Failed to log to wandb: {e}")
|
|
234
|
+
|
|
235
|
+
if self.use_mlflow:
|
|
236
|
+
try:
|
|
237
|
+
numeric_metrics = {self._p(k): float(v) for k, v in metrics.items() if isinstance(v, int | float)}
|
|
238
|
+
if numeric_metrics:
|
|
239
|
+
if self._mlflow_client and self._mlflow_run_id:
|
|
240
|
+
for k, v in numeric_metrics.items():
|
|
241
|
+
self._mlflow_client.log_metric(self._mlflow_run_id, k, v, step=step or 0)
|
|
242
|
+
else:
|
|
243
|
+
import mlflow # type: ignore
|
|
244
|
+
mlflow.log_metrics(numeric_metrics, step=step)
|
|
245
|
+
except Exception as e:
|
|
246
|
+
print(f"Warning: Failed to log to mlflow: {e}")
|
|
247
|
+
|
|
248
|
+
def log_summary(self, summary: dict[str, Any]) -> None:
|
|
249
|
+
"""Log run summary data (visible on the run overview page).
|
|
250
|
+
|
|
251
|
+
Args:
|
|
252
|
+
summary: Key-value pairs for the run summary. Supports strings,
|
|
253
|
+
numbers, and other serializable values.
|
|
254
|
+
"""
|
|
255
|
+
if self.use_wandb:
|
|
256
|
+
try:
|
|
257
|
+
import wandb # type: ignore
|
|
258
|
+
|
|
259
|
+
for k, v in summary.items():
|
|
260
|
+
wandb.run.summary[self._p(k)] = v # type: ignore[union-attr]
|
|
261
|
+
except Exception as e:
|
|
262
|
+
print(f"Warning: Failed to log summary to wandb: {e}")
|
|
263
|
+
|
|
264
|
+
if self.use_mlflow:
|
|
265
|
+
try:
|
|
266
|
+
numeric = {self._p(k): float(v) for k, v in summary.items() if isinstance(v, int | float)}
|
|
267
|
+
text = {self._p(k): str(v) for k, v in summary.items() if isinstance(v, str)}
|
|
268
|
+
if self._mlflow_client and self._mlflow_run_id:
|
|
269
|
+
if numeric:
|
|
270
|
+
for k, v in numeric.items():
|
|
271
|
+
self._mlflow_client.log_metric(self._mlflow_run_id, k, v)
|
|
272
|
+
if text:
|
|
273
|
+
for k, v in text.items():
|
|
274
|
+
self._mlflow_client.log_param(self._mlflow_run_id, f"summary/{k}", v)
|
|
275
|
+
else:
|
|
276
|
+
import mlflow # type: ignore
|
|
277
|
+
if numeric:
|
|
278
|
+
mlflow.log_metrics(numeric)
|
|
279
|
+
if text:
|
|
280
|
+
mlflow.log_params({f"summary/{k}": v for k, v in text.items()})
|
|
281
|
+
except Exception as e:
|
|
282
|
+
print(f"Warning: Failed to log summary to mlflow: {e}")
|
|
283
|
+
|
|
284
|
+
def log_table(self, table_name: str, columns: list[str], data: list[list[Any]]) -> None:
|
|
285
|
+
"""Log a table to the active backends.
|
|
286
|
+
|
|
287
|
+
Args:
|
|
288
|
+
table_name: Name/key for the table.
|
|
289
|
+
columns: Column headers.
|
|
290
|
+
data: Rows of data (each row is a list matching columns).
|
|
291
|
+
"""
|
|
292
|
+
if self.use_wandb:
|
|
293
|
+
try:
|
|
294
|
+
import wandb # type: ignore
|
|
295
|
+
|
|
296
|
+
# Accumulate rows: each call appends to the stored rows for
|
|
297
|
+
# this table, then logs the full growing table. This ensures
|
|
298
|
+
# all rows survive even when multiple log_table calls share
|
|
299
|
+
# the same commit cycle (commit=False overwrites the pending
|
|
300
|
+
# dict, so a single-row table would replace the previous one).
|
|
301
|
+
key = self._p(table_name)
|
|
302
|
+
if key not in self._wandb_table_rows:
|
|
303
|
+
self._wandb_table_rows[key] = (columns, list(data))
|
|
304
|
+
else:
|
|
305
|
+
self._wandb_table_rows[key][1].extend(data)
|
|
306
|
+
all_columns, all_rows = self._wandb_table_rows[key]
|
|
307
|
+
table = wandb.Table(columns=all_columns, data=all_rows)
|
|
308
|
+
wandb.log({key: table}, commit=False)
|
|
309
|
+
except Exception as e:
|
|
310
|
+
print(f"Warning: Failed to log table to wandb: {e}")
|
|
311
|
+
|
|
312
|
+
if self.use_mlflow:
|
|
313
|
+
try:
|
|
314
|
+
import mlflow # type: ignore
|
|
315
|
+
|
|
316
|
+
if self._mlflow_run_id:
|
|
317
|
+
# When we have a run_id, only log from the owning thread to avoid creating new runs
|
|
318
|
+
active = mlflow.active_run()
|
|
319
|
+
if active is None or active.info.run_id != self._mlflow_run_id:
|
|
320
|
+
return
|
|
321
|
+
table_dict = {col: [row[i] for row in data] for i, col in enumerate(columns)}
|
|
322
|
+
mlflow.log_table(data=table_dict, artifact_file=f"{self._p(table_name)}.json")
|
|
323
|
+
except Exception as e:
|
|
324
|
+
print(f"Warning: Failed to log table to mlflow: {e}")
|
|
325
|
+
|
|
326
|
+
def log_html(self, html_content: str, key: str = "candidate_tree") -> None:
|
|
327
|
+
"""Log an HTML string as a rich media artifact.
|
|
328
|
+
|
|
329
|
+
Args:
|
|
330
|
+
html_content: Self-contained HTML string.
|
|
331
|
+
key: Artifact key / name used in the dashboard.
|
|
332
|
+
"""
|
|
333
|
+
if self.use_wandb:
|
|
334
|
+
try:
|
|
335
|
+
import wandb # type: ignore
|
|
336
|
+
|
|
337
|
+
html_obj = wandb.Html(html_content)
|
|
338
|
+
pkey = self._p(key)
|
|
339
|
+
wandb.log({pkey: html_obj}, commit=False)
|
|
340
|
+
# Also write to run summary so the panel always shows the latest tree
|
|
341
|
+
wandb.run.summary[pkey] = html_obj # type: ignore[union-attr]
|
|
342
|
+
except Exception as e:
|
|
343
|
+
print(f"Warning: Failed to log HTML to wandb: {e}")
|
|
344
|
+
|
|
345
|
+
if self.use_mlflow:
|
|
346
|
+
try:
|
|
347
|
+
import tempfile
|
|
348
|
+
|
|
349
|
+
with tempfile.NamedTemporaryFile(mode="w", suffix=".html", delete=False) as f:
|
|
350
|
+
f.write(html_content)
|
|
351
|
+
tmp_path = f.name
|
|
352
|
+
if self._mlflow_client and self._mlflow_run_id:
|
|
353
|
+
self._mlflow_client.log_artifact(self._mlflow_run_id, tmp_path, artifact_path=self._p(key))
|
|
354
|
+
else:
|
|
355
|
+
import mlflow # type: ignore
|
|
356
|
+
mlflow.log_artifact(tmp_path, artifact_path=self._p(key))
|
|
357
|
+
except Exception as e:
|
|
358
|
+
print(f"Warning: Failed to log HTML to mlflow: {e}")
|
|
359
|
+
|
|
360
|
+
def end_run(self):
|
|
361
|
+
"""End the current run.
|
|
362
|
+
|
|
363
|
+
When ``wandb_attach_existing=True`` or ``mlflow_attach_existing=True``
|
|
364
|
+
the respective run is left open — the caller owns its lifecycle.
|
|
365
|
+
"""
|
|
366
|
+
if self.use_wandb and not self.wandb_attach_existing:
|
|
367
|
+
try:
|
|
368
|
+
import wandb # type: ignore
|
|
369
|
+
|
|
370
|
+
if wandb.run is not None:
|
|
371
|
+
wandb.finish()
|
|
372
|
+
except Exception as e:
|
|
373
|
+
print(f"Warning: Failed to end wandb run: {e}")
|
|
374
|
+
|
|
375
|
+
if self.use_mlflow:
|
|
376
|
+
try:
|
|
377
|
+
import mlflow # type: ignore
|
|
378
|
+
|
|
379
|
+
if self._created_mlflow_run and mlflow.active_run() is not None:
|
|
380
|
+
mlflow.end_run()
|
|
381
|
+
self._created_mlflow_run = False
|
|
382
|
+
except Exception as e:
|
|
383
|
+
print(f"Warning: Failed to end mlflow run: {e}")
|
|
384
|
+
|
|
385
|
+
def is_active(self) -> bool:
|
|
386
|
+
"""Check if any backend has an active run."""
|
|
387
|
+
if self.use_wandb:
|
|
388
|
+
try:
|
|
389
|
+
import wandb # type: ignore
|
|
390
|
+
|
|
391
|
+
if wandb.run is not None:
|
|
392
|
+
return True
|
|
393
|
+
except Exception:
|
|
394
|
+
pass
|
|
395
|
+
|
|
396
|
+
if self.use_mlflow:
|
|
397
|
+
try:
|
|
398
|
+
import mlflow # type: ignore
|
|
399
|
+
|
|
400
|
+
if mlflow.active_run() is not None:
|
|
401
|
+
return True
|
|
402
|
+
except Exception:
|
|
403
|
+
pass
|
|
404
|
+
|
|
405
|
+
return False
|
|
406
|
+
|
|
407
|
+
|
|
408
|
+
def create_experiment_tracker(
|
|
409
|
+
use_wandb: bool = False,
|
|
410
|
+
wandb_api_key: str | None = None,
|
|
411
|
+
wandb_init_kwargs: dict[str, Any] | None = None,
|
|
412
|
+
wandb_attach_existing: bool = False,
|
|
413
|
+
wandb_step_metric: str | None = None,
|
|
414
|
+
use_mlflow: bool = False,
|
|
415
|
+
mlflow_tracking_uri: str | None = None,
|
|
416
|
+
mlflow_experiment_name: str | None = None,
|
|
417
|
+
mlflow_attach_existing: bool = False,
|
|
418
|
+
key_prefix: str = "",
|
|
419
|
+
) -> ExperimentTracker:
|
|
420
|
+
"""
|
|
421
|
+
Create an experiment tracker based on the specified backends.
|
|
422
|
+
|
|
423
|
+
Args:
|
|
424
|
+
use_wandb: Whether to use wandb
|
|
425
|
+
use_mlflow: Whether to use mlflow
|
|
426
|
+
wandb_api_key: API key for wandb
|
|
427
|
+
wandb_init_kwargs: Additional kwargs for wandb.init()
|
|
428
|
+
wandb_attach_existing: When True, skip wandb.init() and wandb.finish()
|
|
429
|
+
and log into the already-active run.
|
|
430
|
+
wandb_step_metric: Custom x-axis metric name for wandb. When set,
|
|
431
|
+
GEPA uses ``wandb.define_metric`` to log all metrics against this
|
|
432
|
+
custom step instead of wandb's global monotonic step counter.
|
|
433
|
+
Required when embedding GEPA inside a host training loop that
|
|
434
|
+
manages its own wandb step counter.
|
|
435
|
+
mlflow_tracking_uri: Tracking URI for mlflow
|
|
436
|
+
mlflow_experiment_name: Experiment name for mlflow
|
|
437
|
+
mlflow_attach_existing: When True, skip mlflow.start_run() and
|
|
438
|
+
mlflow.end_run() and log into the already-active run.
|
|
439
|
+
|
|
440
|
+
Returns:
|
|
441
|
+
ExperimentTracker instance
|
|
442
|
+
|
|
443
|
+
Note:
|
|
444
|
+
Both wandb and mlflow can be used simultaneously if desired.
|
|
445
|
+
"""
|
|
446
|
+
return ExperimentTracker(
|
|
447
|
+
use_wandb=use_wandb,
|
|
448
|
+
wandb_api_key=wandb_api_key,
|
|
449
|
+
wandb_init_kwargs=wandb_init_kwargs,
|
|
450
|
+
wandb_attach_existing=wandb_attach_existing,
|
|
451
|
+
wandb_step_metric=wandb_step_metric,
|
|
452
|
+
use_mlflow=use_mlflow,
|
|
453
|
+
mlflow_tracking_uri=mlflow_tracking_uri,
|
|
454
|
+
mlflow_experiment_name=mlflow_experiment_name,
|
|
455
|
+
mlflow_attach_existing=mlflow_attach_existing,
|
|
456
|
+
key_prefix=key_prefix,
|
|
457
|
+
)
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# Copyright (c) 2025 Lakshya A Agrawal and the GEPA contributors
|
|
2
|
+
# https://github.com/gepa-ai/gepa
|
|
3
|
+
|
|
4
|
+
import sys
|
|
5
|
+
from typing import Protocol
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class LoggerProtocol(Protocol):
|
|
9
|
+
def log(self, message: str): ...
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class StdOutLogger(LoggerProtocol):
|
|
13
|
+
def log(self, message: str):
|
|
14
|
+
print(message)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class Tee:
|
|
18
|
+
def __init__(self, *files):
|
|
19
|
+
self.files = files
|
|
20
|
+
|
|
21
|
+
def write(self, obj):
|
|
22
|
+
for f in self.files:
|
|
23
|
+
f.write(obj)
|
|
24
|
+
|
|
25
|
+
def flush(self):
|
|
26
|
+
for f in self.files:
|
|
27
|
+
if hasattr(f, "flush"):
|
|
28
|
+
f.flush()
|
|
29
|
+
|
|
30
|
+
def isatty(self):
|
|
31
|
+
# True if any of the files is a terminal
|
|
32
|
+
return any(hasattr(f, "isatty") and f.isatty() for f in self.files)
|
|
33
|
+
|
|
34
|
+
def close(self):
|
|
35
|
+
for f in self.files:
|
|
36
|
+
if hasattr(f, "close"):
|
|
37
|
+
f.close()
|
|
38
|
+
|
|
39
|
+
def fileno(self):
|
|
40
|
+
for f in self.files:
|
|
41
|
+
if hasattr(f, "fileno"):
|
|
42
|
+
return f.fileno()
|
|
43
|
+
raise OSError("No underlying file object with fileno")
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class Logger(LoggerProtocol):
|
|
47
|
+
def __init__(self, filename, mode="a"):
|
|
48
|
+
self.file_handle = open(filename, mode)
|
|
49
|
+
self.file_handle_stderr = open(filename.replace("run_log.", "run_log_stderr."), mode)
|
|
50
|
+
self.modified_sys = False
|
|
51
|
+
|
|
52
|
+
def __enter__(self):
|
|
53
|
+
self.original_stdout = sys.stdout
|
|
54
|
+
self.original_stderr = sys.stderr
|
|
55
|
+
sys.stdout = Tee(sys.stdout, self.file_handle)
|
|
56
|
+
sys.stderr = Tee(sys.stderr, self.file_handle_stderr)
|
|
57
|
+
self.modified_sys = True
|
|
58
|
+
return self
|
|
59
|
+
|
|
60
|
+
def __exit__(self, exc_type, exc_value, traceback):
|
|
61
|
+
sys.stdout = self.original_stdout
|
|
62
|
+
sys.stderr = self.original_stderr
|
|
63
|
+
self.file_handle.close()
|
|
64
|
+
self.file_handle_stderr.close()
|
|
65
|
+
self.modified_sys = False
|
|
66
|
+
|
|
67
|
+
def log(self, *args, **kwargs):
|
|
68
|
+
if self.modified_sys:
|
|
69
|
+
print(*args, **kwargs)
|
|
70
|
+
else:
|
|
71
|
+
# Emulate print(*args, **kwargs) behavior but write to the file
|
|
72
|
+
print(*args, **kwargs)
|
|
73
|
+
print(*args, file=self.file_handle_stderr, **kwargs)
|
|
74
|
+
self.file_handle.flush()
|
|
75
|
+
self.file_handle_stderr.flush()
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# Copyright (c) 2025 Lakshya A Agrawal and the GEPA contributors
|
|
2
|
+
# https://github.com/gepa-ai/gepa
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
from gepa.core.adapter import DataInst
|
|
6
|
+
from gepa.core.data_loader import DataId
|
|
7
|
+
from gepa.core.state import GEPAState, ValsetEvaluation
|
|
8
|
+
from gepa.strategies.eval_policy import EvaluationPolicy
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def log_detailed_metrics_after_discovering_new_program(
|
|
12
|
+
logger,
|
|
13
|
+
gepa_state: GEPAState,
|
|
14
|
+
new_program_idx,
|
|
15
|
+
valset_evaluation: ValsetEvaluation,
|
|
16
|
+
objective_scores,
|
|
17
|
+
experiment_tracker,
|
|
18
|
+
linear_pareto_front_program_idx,
|
|
19
|
+
valset_size: int,
|
|
20
|
+
val_evaluation_policy: EvaluationPolicy[DataId, DataInst],
|
|
21
|
+
):
|
|
22
|
+
# best_prog_per_agg_val_score = idxmax(gepa_state.program_full_scores_val_set)
|
|
23
|
+
best_prog_per_agg_val_score = val_evaluation_policy.get_best_program(gepa_state)
|
|
24
|
+
best_score_on_valset = val_evaluation_policy.get_valset_score(best_prog_per_agg_val_score, gepa_state)
|
|
25
|
+
|
|
26
|
+
# avg, coverage = gepa_state.get_program_average_val_subset(new_program_idx)
|
|
27
|
+
valset_score = val_evaluation_policy.get_valset_score(new_program_idx, gepa_state)
|
|
28
|
+
valset_scores = valset_evaluation.scores_by_val_id
|
|
29
|
+
coverage = len(valset_scores)
|
|
30
|
+
logger.log(
|
|
31
|
+
f"Iteration {gepa_state.i + 1}: Valset score for new program: {valset_score}"
|
|
32
|
+
f" (coverage {coverage} / {valset_size})"
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
agg_valset_score_new_program = val_evaluation_policy.get_valset_score(new_program_idx, gepa_state)
|
|
36
|
+
|
|
37
|
+
logger.log(f"Iteration {gepa_state.i + 1}: Val aggregate for new program: {agg_valset_score_new_program}")
|
|
38
|
+
logger.log(f"Iteration {gepa_state.i + 1}: Individual valset scores for new program: {valset_scores}")
|
|
39
|
+
if objective_scores:
|
|
40
|
+
logger.log(f"Iteration {gepa_state.i + 1}: Objective aggregate scores for new program: {objective_scores}")
|
|
41
|
+
logger.log(f"Iteration {gepa_state.i + 1}: New valset pareto front scores: {gepa_state.pareto_front_valset}")
|
|
42
|
+
if gepa_state.objective_pareto_front:
|
|
43
|
+
logger.log(f"Iteration {gepa_state.i + 1}: Objective pareto front scores: {gepa_state.objective_pareto_front}")
|
|
44
|
+
|
|
45
|
+
pareto_scores = list(gepa_state.pareto_front_valset.values())
|
|
46
|
+
assert all(score > float("-inf") for score in pareto_scores), (
|
|
47
|
+
"Should have at least one valid score per validation example"
|
|
48
|
+
)
|
|
49
|
+
assert len(pareto_scores) > 0
|
|
50
|
+
pareto_avg = sum(pareto_scores) / len(pareto_scores)
|
|
51
|
+
|
|
52
|
+
logger.log(f"Iteration {gepa_state.i + 1}: Valset pareto front aggregate score: {pareto_avg}")
|
|
53
|
+
logger.log(
|
|
54
|
+
f"Iteration {gepa_state.i + 1}: Updated valset pareto front programs: {gepa_state.program_at_pareto_front_valset}"
|
|
55
|
+
)
|
|
56
|
+
if gepa_state.program_at_pareto_front_objectives:
|
|
57
|
+
logger.log(
|
|
58
|
+
f"Iteration {gepa_state.i + 1}: Updated objective pareto front programs: {gepa_state.program_at_pareto_front_objectives}"
|
|
59
|
+
)
|
|
60
|
+
logger.log(
|
|
61
|
+
f"Iteration {gepa_state.i + 1}: Best valset aggregate score so far: {max(gepa_state.program_full_scores_val_set)}"
|
|
62
|
+
)
|
|
63
|
+
logger.log(
|
|
64
|
+
f"Iteration {gepa_state.i + 1}: Best program as per aggregate score on valset: {best_prog_per_agg_val_score}"
|
|
65
|
+
)
|
|
66
|
+
logger.log(f"Iteration {gepa_state.i + 1}: Best score on valset: {best_score_on_valset}")
|
|
67
|
+
logger.log(f"Iteration {gepa_state.i + 1}: Linear pareto front program index: {linear_pareto_front_program_idx}")
|
|
68
|
+
logger.log(f"Iteration {gepa_state.i + 1}: New program candidate index: {new_program_idx}")
|
|
69
|
+
|
|
70
|
+
# Scalar metrics go to log_metrics (creates wandb/mlflow line charts)
|
|
71
|
+
metrics = {
|
|
72
|
+
"iteration": gepa_state.i + 1,
|
|
73
|
+
"new_program_idx": new_program_idx,
|
|
74
|
+
"valset_pareto_front_agg": pareto_avg,
|
|
75
|
+
"best_score_on_valset": best_score_on_valset,
|
|
76
|
+
"linear_pareto_front_program_idx": linear_pareto_front_program_idx,
|
|
77
|
+
"best_program_as_per_agg_score_valset": best_prog_per_agg_val_score,
|
|
78
|
+
"val_evaluated_count_new_program": coverage,
|
|
79
|
+
"val_total_count": valset_size,
|
|
80
|
+
"val_program_average": valset_score,
|
|
81
|
+
"total_metric_calls": gepa_state.total_num_evals,
|
|
82
|
+
}
|
|
83
|
+
if objective_scores:
|
|
84
|
+
for obj_name, obj_val in objective_scores.items():
|
|
85
|
+
if isinstance(obj_val, int | float):
|
|
86
|
+
metrics[f"objective/{obj_name}"] = obj_val
|
|
87
|
+
experiment_tracker.log_metrics(metrics, step=gepa_state.i + 1)
|
|
88
|
+
|
|
89
|
+
# Structured data goes to log_table (creates wandb Tables / mlflow artifacts)
|
|
90
|
+
# instead of log_metrics, which would flatten nested dicts into hundreds of charts.
|
|
91
|
+
# Only log the new candidate's row to avoid O(candidates * valset) repeated uploads.
|
|
92
|
+
|
|
93
|
+
all_val_ids = sorted(gepa_state.pareto_front_valset.keys(), key=str)
|
|
94
|
+
val_columns = ["candidate_idx", "parent_ids"] + [str(vid) for vid in all_val_ids]
|
|
95
|
+
new_scores_dict = gepa_state.prog_candidate_val_subscores[new_program_idx]
|
|
96
|
+
new_parent = gepa_state.parent_program_for_candidate[new_program_idx]
|
|
97
|
+
val_row = [new_program_idx, str(new_parent)] + [new_scores_dict.get(vid) for vid in all_val_ids]
|
|
98
|
+
experiment_tracker.log_table("valset_scores", columns=val_columns, data=[val_row])
|
|
99
|
+
|
|
100
|
+
# Valset pareto front: which programs are best for which val examples
|
|
101
|
+
pareto_front_rows = [
|
|
102
|
+
[str(val_id), score, str(sorted(gepa_state.program_at_pareto_front_valset[val_id]))]
|
|
103
|
+
for val_id, score in gepa_state.pareto_front_valset.items()
|
|
104
|
+
]
|
|
105
|
+
if pareto_front_rows:
|
|
106
|
+
experiment_tracker.log_table(
|
|
107
|
+
"valset_pareto_front",
|
|
108
|
+
columns=["val_id", "best_score", "program_ids"],
|
|
109
|
+
data=pareto_front_rows,
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
# Objective scores for the new candidate only
|
|
113
|
+
new_obj_scores = gepa_state.prog_candidate_objective_scores[new_program_idx]
|
|
114
|
+
if new_obj_scores:
|
|
115
|
+
all_objectives = sorted(new_obj_scores.keys(), key=str)
|
|
116
|
+
obj_columns = ["candidate_idx", "parent_ids"] + [str(obj) for obj in all_objectives]
|
|
117
|
+
obj_row = [new_program_idx, str(new_parent)] + [new_obj_scores.get(obj) for obj in all_objectives]
|
|
118
|
+
experiment_tracker.log_table("objective_scores", columns=obj_columns, data=[obj_row])
|
|
119
|
+
|
|
120
|
+
# Objective pareto front
|
|
121
|
+
if gepa_state.objective_pareto_front:
|
|
122
|
+
obj_pareto_rows = [
|
|
123
|
+
[str(obj), float(score), str(sorted(gepa_state.program_at_pareto_front_objectives[obj]))]
|
|
124
|
+
for obj, score in gepa_state.objective_pareto_front.items()
|
|
125
|
+
]
|
|
126
|
+
experiment_tracker.log_table(
|
|
127
|
+
"objective_pareto_front",
|
|
128
|
+
columns=["objective", "best_score", "program_ids"],
|
|
129
|
+
data=obj_pareto_rows,
|
|
130
|
+
)
|