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
package/vendor/gepa-python/src/gepa/adapters/generic_rag_adapter/vector_stores/weaviate_store.py
ADDED
|
@@ -0,0 +1,418 @@
|
|
|
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
|
+
from gepa.adapters.generic_rag_adapter.vector_store_interface import VectorStoreInterface
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class WeaviateVectorStore(VectorStoreInterface):
|
|
10
|
+
"""
|
|
11
|
+
Weaviate implementation of the VectorStoreInterface.
|
|
12
|
+
|
|
13
|
+
Weaviate is a cloud-native, modular, real-time vector database
|
|
14
|
+
with powerful search capabilities including hybrid search.
|
|
15
|
+
Supports both Weaviate Cloud Services and self-hosted deployments.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def __init__(self, client, collection_name: str, embedding_function=None):
|
|
19
|
+
"""
|
|
20
|
+
Initialize WeaviateVectorStore.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
client: Weaviate client instance (WeaviateClient from weaviate-client)
|
|
24
|
+
collection_name: Name of the collection/class to use
|
|
25
|
+
embedding_function: Optional function to compute embeddings for queries
|
|
26
|
+
"""
|
|
27
|
+
import importlib.util
|
|
28
|
+
|
|
29
|
+
if importlib.util.find_spec("weaviate") is None:
|
|
30
|
+
raise ImportError(
|
|
31
|
+
"Weaviate client is required for WeaviateVectorStore. Install with: pip install litellm weaviate-client"
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
import weaviate.classes as wvc
|
|
35
|
+
|
|
36
|
+
self.client = client
|
|
37
|
+
self.collection_name = collection_name
|
|
38
|
+
self.wvc = wvc
|
|
39
|
+
self.embedding_function = embedding_function
|
|
40
|
+
|
|
41
|
+
# Get the collection
|
|
42
|
+
try:
|
|
43
|
+
self.collection = self.client.collections.get(collection_name)
|
|
44
|
+
except Exception as e:
|
|
45
|
+
raise ValueError(
|
|
46
|
+
f"Collection '{collection_name}' not found. Please create the collection first. Error: {e!s}"
|
|
47
|
+
) from e
|
|
48
|
+
|
|
49
|
+
def similarity_search(
|
|
50
|
+
self,
|
|
51
|
+
query: str,
|
|
52
|
+
k: int = 5,
|
|
53
|
+
filters: dict[str, Any] | None = None,
|
|
54
|
+
) -> list[dict[str, Any]]:
|
|
55
|
+
"""Search for documents similar to the query text using embeddings."""
|
|
56
|
+
if self.embedding_function is None:
|
|
57
|
+
raise ValueError("No embedding function provided for similarity search")
|
|
58
|
+
|
|
59
|
+
# Compute embeddings for the query
|
|
60
|
+
try:
|
|
61
|
+
query_vector = self.embedding_function(query)
|
|
62
|
+
if hasattr(query_vector, "tolist"):
|
|
63
|
+
query_vector = query_vector.tolist()
|
|
64
|
+
except Exception as e:
|
|
65
|
+
raise RuntimeError(f"Failed to compute embeddings for query: {e!s}") from e
|
|
66
|
+
|
|
67
|
+
# Use vector search with computed embeddings
|
|
68
|
+
return self.vector_search(query_vector, k, filters)
|
|
69
|
+
|
|
70
|
+
def vector_search(
|
|
71
|
+
self,
|
|
72
|
+
query_vector: list[float],
|
|
73
|
+
k: int = 5,
|
|
74
|
+
filters: dict[str, Any] | None = None,
|
|
75
|
+
) -> list[dict[str, Any]]:
|
|
76
|
+
"""Search using a pre-computed query vector."""
|
|
77
|
+
weaviate_filters = self._convert_filters(filters) if filters else None
|
|
78
|
+
|
|
79
|
+
try:
|
|
80
|
+
# Execute query
|
|
81
|
+
if weaviate_filters:
|
|
82
|
+
response = self.collection.query.near_vector(near_vector=query_vector, limit=k).where(weaviate_filters)
|
|
83
|
+
else:
|
|
84
|
+
response = self.collection.query.near_vector(near_vector=query_vector, limit=k)
|
|
85
|
+
|
|
86
|
+
# Handle GenerativeReturn object - access .objects attribute
|
|
87
|
+
if hasattr(response, "objects"):
|
|
88
|
+
results = response.objects
|
|
89
|
+
else:
|
|
90
|
+
results = response
|
|
91
|
+
|
|
92
|
+
return self._format_results(results)
|
|
93
|
+
|
|
94
|
+
except Exception as e:
|
|
95
|
+
raise RuntimeError(f"Weaviate vector search failed: {e!s}") from e
|
|
96
|
+
|
|
97
|
+
def hybrid_search(
|
|
98
|
+
self,
|
|
99
|
+
query: str,
|
|
100
|
+
k: int = 5,
|
|
101
|
+
alpha: float = 0.5,
|
|
102
|
+
filters: dict[str, Any] | None = None,
|
|
103
|
+
) -> list[dict[str, Any]]:
|
|
104
|
+
"""
|
|
105
|
+
Hybrid semantic + keyword search using Weaviate's native hybrid search.
|
|
106
|
+
|
|
107
|
+
Args:
|
|
108
|
+
query: Text query to search for
|
|
109
|
+
k: Number of documents to return
|
|
110
|
+
alpha: Weight for semantic vs keyword search (0.0 = pure keyword, 1.0 = pure semantic)
|
|
111
|
+
filters: Optional metadata filters
|
|
112
|
+
"""
|
|
113
|
+
weaviate_filters = self._convert_filters(filters) if filters else None
|
|
114
|
+
|
|
115
|
+
try:
|
|
116
|
+
results = self.collection.query.hybrid(
|
|
117
|
+
query=query,
|
|
118
|
+
alpha=alpha,
|
|
119
|
+
limit=k,
|
|
120
|
+
where=weaviate_filters,
|
|
121
|
+
return_metadata=self.wvc.query.MetadataQuery(score=True, explain_score=True),
|
|
122
|
+
return_properties=["content", "*"],
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
return self._format_results(results)
|
|
126
|
+
|
|
127
|
+
except Exception as e:
|
|
128
|
+
raise RuntimeError(f"Weaviate hybrid search failed: {e!s}") from e
|
|
129
|
+
|
|
130
|
+
def get_collection_info(self) -> dict[str, Any]:
|
|
131
|
+
"""Get metadata about the Weaviate collection."""
|
|
132
|
+
try:
|
|
133
|
+
# Get collection configuration
|
|
134
|
+
config = self.collection.config.get()
|
|
135
|
+
|
|
136
|
+
# Count objects in collection
|
|
137
|
+
count_result = self.collection.aggregate.over_all(total_count=True)
|
|
138
|
+
total_count = count_result.total_count
|
|
139
|
+
|
|
140
|
+
# Try to determine vector dimensions
|
|
141
|
+
dimension = 0
|
|
142
|
+
if hasattr(config, "vector_config") and config.vector_config:
|
|
143
|
+
# For collections with vector config
|
|
144
|
+
if hasattr(config.vector_config, "vector_index_config"):
|
|
145
|
+
vector_index = config.vector_index_config
|
|
146
|
+
if hasattr(vector_index, "distance"):
|
|
147
|
+
# This indicates vectors are configured
|
|
148
|
+
# Try to get dimension from a sample object
|
|
149
|
+
try:
|
|
150
|
+
sample = self.collection.query.fetch_objects(limit=1, include_vector=True)
|
|
151
|
+
if sample.objects and hasattr(sample.objects[0], "vector") and sample.objects[0].vector:
|
|
152
|
+
# Handle both named vectors and default vector
|
|
153
|
+
vector_data = sample.objects[0].vector
|
|
154
|
+
if isinstance(vector_data, dict):
|
|
155
|
+
# Named vectors case
|
|
156
|
+
for _vector_name, vector_values in vector_data.items():
|
|
157
|
+
if vector_values:
|
|
158
|
+
dimension = len(vector_values)
|
|
159
|
+
break
|
|
160
|
+
elif isinstance(vector_data, list):
|
|
161
|
+
# Default vector case
|
|
162
|
+
dimension = len(vector_data)
|
|
163
|
+
except Exception:
|
|
164
|
+
# If we can't determine dimension from sample, that's ok
|
|
165
|
+
pass
|
|
166
|
+
|
|
167
|
+
# Determine if hybrid search is supported
|
|
168
|
+
supports_hybrid = True # Weaviate generally supports hybrid search
|
|
169
|
+
try:
|
|
170
|
+
# Test if hybrid search works by doing a minimal query
|
|
171
|
+
self.collection.query.hybrid(query="test", limit=1)
|
|
172
|
+
supports_hybrid = True
|
|
173
|
+
except Exception:
|
|
174
|
+
supports_hybrid = False
|
|
175
|
+
|
|
176
|
+
return {
|
|
177
|
+
"name": self.collection_name,
|
|
178
|
+
"document_count": total_count,
|
|
179
|
+
"dimension": dimension,
|
|
180
|
+
"vector_store_type": "weaviate",
|
|
181
|
+
"supports_hybrid_search": supports_hybrid,
|
|
182
|
+
"vectorizer": getattr(config, "vectorizer_config", None),
|
|
183
|
+
"properties": [prop.name for prop in getattr(config, "properties", [])],
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
except Exception as e:
|
|
187
|
+
# Fallback info if detailed info fails
|
|
188
|
+
return {
|
|
189
|
+
"name": self.collection_name,
|
|
190
|
+
"document_count": 0,
|
|
191
|
+
"dimension": 0,
|
|
192
|
+
"vector_store_type": "weaviate",
|
|
193
|
+
"error": str(e),
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
def supports_hybrid_search(self) -> bool:
|
|
197
|
+
"""Weaviate supports hybrid search."""
|
|
198
|
+
try:
|
|
199
|
+
# Test if hybrid search works
|
|
200
|
+
self.collection.query.hybrid(query="test", limit=1)
|
|
201
|
+
return True
|
|
202
|
+
except Exception:
|
|
203
|
+
return False
|
|
204
|
+
|
|
205
|
+
def supports_metadata_filtering(self) -> bool:
|
|
206
|
+
"""Weaviate supports metadata filtering with where clauses."""
|
|
207
|
+
return True
|
|
208
|
+
|
|
209
|
+
def _convert_filters(self, filters: dict[str, Any]) -> Any:
|
|
210
|
+
"""
|
|
211
|
+
Convert generic filters to Weaviate where clause format.
|
|
212
|
+
|
|
213
|
+
Generic format: {"key": "value", "key2": {"$gt": 5}}
|
|
214
|
+
Weaviate format uses Filter class
|
|
215
|
+
"""
|
|
216
|
+
if not filters:
|
|
217
|
+
return None
|
|
218
|
+
|
|
219
|
+
try:
|
|
220
|
+
from weaviate.collections.classes.filters import Filter
|
|
221
|
+
except ImportError:
|
|
222
|
+
# Fallback if Filter class not available
|
|
223
|
+
return None
|
|
224
|
+
|
|
225
|
+
filter_conditions = []
|
|
226
|
+
|
|
227
|
+
for key, value in filters.items():
|
|
228
|
+
if isinstance(value, dict):
|
|
229
|
+
# Handle operator-based filters
|
|
230
|
+
for op, op_value in value.items():
|
|
231
|
+
if op == "$eq" or op == "equal":
|
|
232
|
+
filter_conditions.append(Filter.by_property(key).equal(op_value))
|
|
233
|
+
elif op == "$ne" or op == "not_equal":
|
|
234
|
+
filter_conditions.append(Filter.by_property(key).not_equal(op_value))
|
|
235
|
+
elif op == "$gt" or op == "greater_than":
|
|
236
|
+
filter_conditions.append(Filter.by_property(key).greater_than(op_value))
|
|
237
|
+
elif op == "$gte" or op == "greater_equal":
|
|
238
|
+
filter_conditions.append(Filter.by_property(key).greater_or_equal(op_value))
|
|
239
|
+
elif op == "$lt" or op == "less_than":
|
|
240
|
+
filter_conditions.append(Filter.by_property(key).less_than(op_value))
|
|
241
|
+
elif op == "$lte" or op == "less_equal":
|
|
242
|
+
filter_conditions.append(Filter.by_property(key).less_or_equal(op_value))
|
|
243
|
+
elif op == "$in" or op == "contains_any":
|
|
244
|
+
if isinstance(op_value, list):
|
|
245
|
+
filter_conditions.append(Filter.by_property(key).contains_any(op_value))
|
|
246
|
+
elif op == "$like" or op == "like":
|
|
247
|
+
filter_conditions.append(Filter.by_property(key).like(op_value))
|
|
248
|
+
else:
|
|
249
|
+
# Simple equality filter
|
|
250
|
+
filter_conditions.append(Filter.by_property(key).equal(value))
|
|
251
|
+
|
|
252
|
+
# Combine all conditions with AND
|
|
253
|
+
if len(filter_conditions) == 1:
|
|
254
|
+
return filter_conditions[0]
|
|
255
|
+
elif len(filter_conditions) > 1:
|
|
256
|
+
combined_filter = filter_conditions[0]
|
|
257
|
+
for condition in filter_conditions[1:]:
|
|
258
|
+
combined_filter = combined_filter & condition
|
|
259
|
+
return combined_filter
|
|
260
|
+
|
|
261
|
+
return None
|
|
262
|
+
|
|
263
|
+
def _format_results(self, results) -> list[dict[str, Any]]:
|
|
264
|
+
"""Convert Weaviate results to standardized format."""
|
|
265
|
+
documents = []
|
|
266
|
+
|
|
267
|
+
# Handle both GenerativeReturn objects and direct lists
|
|
268
|
+
if hasattr(results, "objects"):
|
|
269
|
+
objects_list = results.objects
|
|
270
|
+
else:
|
|
271
|
+
objects_list = results
|
|
272
|
+
|
|
273
|
+
if not objects_list:
|
|
274
|
+
return documents
|
|
275
|
+
|
|
276
|
+
for obj in objects_list:
|
|
277
|
+
# Get the content - try different property names
|
|
278
|
+
content = ""
|
|
279
|
+
properties = obj.properties
|
|
280
|
+
|
|
281
|
+
# Common content field names
|
|
282
|
+
content_fields = ["content", "text", "document", "body", "description"]
|
|
283
|
+
for field in content_fields:
|
|
284
|
+
if field in properties:
|
|
285
|
+
content = properties[field]
|
|
286
|
+
break
|
|
287
|
+
|
|
288
|
+
# If no content field found, use all text properties
|
|
289
|
+
if not content:
|
|
290
|
+
text_properties = []
|
|
291
|
+
for key, value in properties.items():
|
|
292
|
+
if isinstance(value, str) and value.strip():
|
|
293
|
+
text_properties.append(f"{key}: {value}")
|
|
294
|
+
content = " | ".join(text_properties)
|
|
295
|
+
|
|
296
|
+
# Extract metadata (all properties except content)
|
|
297
|
+
metadata = {}
|
|
298
|
+
for key, value in properties.items():
|
|
299
|
+
if key not in ["content", "text", "document", "body"] or not content:
|
|
300
|
+
metadata[key] = value
|
|
301
|
+
|
|
302
|
+
# Add UUID as doc_id in metadata
|
|
303
|
+
metadata["doc_id"] = str(obj.uuid)
|
|
304
|
+
|
|
305
|
+
# Calculate similarity score from distance or use provided score
|
|
306
|
+
score = 0.0
|
|
307
|
+
if hasattr(obj.metadata, "score") and obj.metadata.score is not None:
|
|
308
|
+
score = float(obj.metadata.score)
|
|
309
|
+
elif hasattr(obj.metadata, "distance") and obj.metadata.distance is not None:
|
|
310
|
+
# Convert distance to similarity (assuming cosine distance)
|
|
311
|
+
# Weaviate cosine distance is between 0 and 2, similarity = 1 - (distance/2)
|
|
312
|
+
distance = float(obj.metadata.distance)
|
|
313
|
+
score = max(0.0, 1.0 - (distance / 2.0))
|
|
314
|
+
|
|
315
|
+
documents.append({"content": content, "metadata": metadata, "score": score})
|
|
316
|
+
|
|
317
|
+
return documents
|
|
318
|
+
|
|
319
|
+
@classmethod
|
|
320
|
+
def create_local(
|
|
321
|
+
cls,
|
|
322
|
+
host: str = "localhost",
|
|
323
|
+
port: int = 8080,
|
|
324
|
+
grpc_port: int = 50051,
|
|
325
|
+
collection_name: str = "Documents",
|
|
326
|
+
headers: dict[str, str] | None = None,
|
|
327
|
+
) -> "WeaviateVectorStore":
|
|
328
|
+
"""
|
|
329
|
+
Create a WeaviateVectorStore connected to local Weaviate instance.
|
|
330
|
+
|
|
331
|
+
Args:
|
|
332
|
+
host: Weaviate host
|
|
333
|
+
port: HTTP port
|
|
334
|
+
grpc_port: gRPC port
|
|
335
|
+
collection_name: Name of the collection
|
|
336
|
+
headers: Optional headers for authentication
|
|
337
|
+
|
|
338
|
+
Returns:
|
|
339
|
+
WeaviateVectorStore instance
|
|
340
|
+
"""
|
|
341
|
+
try:
|
|
342
|
+
import weaviate
|
|
343
|
+
except ImportError as e:
|
|
344
|
+
raise ImportError("Weaviate client is required. Install with: pip install litellm weaviate-client") from e
|
|
345
|
+
|
|
346
|
+
client = weaviate.connect_to_local(host=host, port=port, grpc_port=grpc_port, headers=headers)
|
|
347
|
+
|
|
348
|
+
return cls(client, collection_name)
|
|
349
|
+
|
|
350
|
+
@classmethod
|
|
351
|
+
def create_cloud(
|
|
352
|
+
cls,
|
|
353
|
+
cluster_url: str,
|
|
354
|
+
auth_credentials,
|
|
355
|
+
collection_name: str = "Documents",
|
|
356
|
+
headers: dict[str, str] | None = None,
|
|
357
|
+
) -> "WeaviateVectorStore":
|
|
358
|
+
"""
|
|
359
|
+
Create a WeaviateVectorStore connected to Weaviate Cloud Services.
|
|
360
|
+
|
|
361
|
+
Args:
|
|
362
|
+
cluster_url: Weaviate cluster URL
|
|
363
|
+
auth_credentials: Authentication credentials (API key or other auth)
|
|
364
|
+
collection_name: Name of the collection
|
|
365
|
+
headers: Optional headers
|
|
366
|
+
|
|
367
|
+
Returns:
|
|
368
|
+
WeaviateVectorStore instance
|
|
369
|
+
"""
|
|
370
|
+
try:
|
|
371
|
+
import weaviate
|
|
372
|
+
except ImportError as e:
|
|
373
|
+
raise ImportError("Weaviate client is required. Install with: pip install litellm weaviate-client") from e
|
|
374
|
+
|
|
375
|
+
client = weaviate.connect_to_weaviate_cloud(
|
|
376
|
+
cluster_url=cluster_url, auth_credentials=auth_credentials, headers=headers
|
|
377
|
+
)
|
|
378
|
+
|
|
379
|
+
return cls(client, collection_name)
|
|
380
|
+
|
|
381
|
+
@classmethod
|
|
382
|
+
def create_custom(
|
|
383
|
+
cls,
|
|
384
|
+
url: str,
|
|
385
|
+
auth_credentials=None,
|
|
386
|
+
collection_name: str = "Documents",
|
|
387
|
+
headers: dict[str, str] | None = None,
|
|
388
|
+
grpc_port: int | None = None,
|
|
389
|
+
) -> "WeaviateVectorStore":
|
|
390
|
+
"""
|
|
391
|
+
Create a WeaviateVectorStore with custom connection parameters.
|
|
392
|
+
|
|
393
|
+
Args:
|
|
394
|
+
url: Weaviate instance URL
|
|
395
|
+
auth_credentials: Authentication credentials
|
|
396
|
+
collection_name: Name of the collection
|
|
397
|
+
headers: Optional headers
|
|
398
|
+
grpc_port: Optional gRPC port
|
|
399
|
+
|
|
400
|
+
Returns:
|
|
401
|
+
WeaviateVectorStore instance
|
|
402
|
+
"""
|
|
403
|
+
try:
|
|
404
|
+
import weaviate
|
|
405
|
+
except ImportError as e:
|
|
406
|
+
raise ImportError("Weaviate client is required. Install with: pip install litellm weaviate-client") from e
|
|
407
|
+
|
|
408
|
+
client = weaviate.connect_to_custom(
|
|
409
|
+
http_host=url,
|
|
410
|
+
http_port=443 if "https" in url else 80,
|
|
411
|
+
http_secure="https" in url,
|
|
412
|
+
grpc_port=grpc_port,
|
|
413
|
+
grpc_secure="https" in url,
|
|
414
|
+
auth_credentials=auth_credentials,
|
|
415
|
+
headers=headers,
|
|
416
|
+
)
|
|
417
|
+
|
|
418
|
+
return cls(client, collection_name)
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
import time
|
|
5
|
+
from collections import Counter
|
|
6
|
+
from collections.abc import Callable, Mapping, Sequence
|
|
7
|
+
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
from langchain_core.language_models import BaseChatModel
|
|
11
|
+
from langchain_core.messages import AIMessage, HumanMessage
|
|
12
|
+
|
|
13
|
+
from gepa.core.adapter import EvaluationBatch, GEPAAdapter, ProposalFn
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
DataInst = Mapping[str, Any]
|
|
18
|
+
RolloutState = dict[str, Any]
|
|
19
|
+
RolloutFn = Callable[[dict[str, str], DataInst], RolloutState]
|
|
20
|
+
EvalFn = Callable[[DataInst, RolloutState], tuple[float, str]]
|
|
21
|
+
ReflectiveRecordFn = Callable[[DataInst, RolloutState, float, str], Mapping[str, Any]]
|
|
22
|
+
ReflectionLM = Callable[[str], str]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def make_reflection_lm(model: BaseChatModel | Mapping[str, Any] | str) -> ReflectionLM:
|
|
26
|
+
"""Wrap a LangChain chat model into the `(prompt: str) -> str` callable GEPA expects.
|
|
27
|
+
|
|
28
|
+
`model` accepts either:
|
|
29
|
+
- a `BaseChatModel` instance,
|
|
30
|
+
- a `provider:model` string (passed to `init_chat_model`), or
|
|
31
|
+
- a kwargs mapping (passed via `init_chat_model(**model)`).
|
|
32
|
+
"""
|
|
33
|
+
if not isinstance(model, BaseChatModel):
|
|
34
|
+
from langchain.chat_models import init_chat_model
|
|
35
|
+
|
|
36
|
+
model = init_chat_model(model) if isinstance(model, str) else init_chat_model(**dict(model))
|
|
37
|
+
|
|
38
|
+
def reflection_lm(prompt: str) -> str:
|
|
39
|
+
logger.debug("reflection prompt (%d chars):\n%s", len(prompt), prompt)
|
|
40
|
+
result = model.invoke([HumanMessage(content=prompt)])
|
|
41
|
+
content = result.content
|
|
42
|
+
if isinstance(content, list):
|
|
43
|
+
text = "".join(part.get("text", "") if isinstance(part, dict) else str(part) for part in content)
|
|
44
|
+
else:
|
|
45
|
+
text = str(content)
|
|
46
|
+
logger.debug("reflection response (%d chars):\n%s", len(text), text)
|
|
47
|
+
return text
|
|
48
|
+
|
|
49
|
+
return reflection_lm
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def last_message_text(state: RolloutState) -> str:
|
|
53
|
+
"""Return the text content of the last message in a rollout state.
|
|
54
|
+
|
|
55
|
+
Works for both single-turn rollouts (`{"messages": [AIMessage(...)]}`) and
|
|
56
|
+
agent rollouts that follow LangGraph's standard state shape.
|
|
57
|
+
"""
|
|
58
|
+
messages = state.get("messages") if isinstance(state, dict) else None
|
|
59
|
+
if not messages:
|
|
60
|
+
return ""
|
|
61
|
+
msg = messages[-1]
|
|
62
|
+
content = getattr(msg, "content", msg)
|
|
63
|
+
if isinstance(content, list):
|
|
64
|
+
return "".join(part.get("text", "") if isinstance(part, dict) else str(part) for part in content)
|
|
65
|
+
return str(content)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _default_reflective_record(data: DataInst, state: RolloutState, score: float, feedback: str) -> Mapping[str, Any]:
|
|
69
|
+
inputs = data.get("input", data)
|
|
70
|
+
return {
|
|
71
|
+
"Inputs": inputs,
|
|
72
|
+
"Generated Outputs": last_message_text(state),
|
|
73
|
+
"Feedback": feedback,
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _truncate(text: str, limit: int = 800) -> str:
|
|
78
|
+
text = text.replace("\n", " ⏎ ")
|
|
79
|
+
if len(text) <= limit:
|
|
80
|
+
return text
|
|
81
|
+
return text[:limit] + f"... [+{len(text) - limit} chars]"
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class LangChainAdapter(GEPAAdapter):
|
|
85
|
+
"""GEPA adapter for arbitrary LangChain rollouts.
|
|
86
|
+
|
|
87
|
+
Caller supplies:
|
|
88
|
+
- `rollout_fn(candidate, example) -> state dict`: any LangChain pipeline —
|
|
89
|
+
a single chat-model invocation, an agent built with `create_agent`, a
|
|
90
|
+
custom LangGraph graph, RAG, etc. Must return a state dict; for single-turn
|
|
91
|
+
cases return a dict with the messages key
|
|
92
|
+
`{"messages": messages + [AIMessage("llm response")]}`.
|
|
93
|
+
For agents, return the full agent state directly (e.g. `agent.invoke(...)`).
|
|
94
|
+
- `eval_fn(example, state) -> (score, feedback)`: scores the rollout state.
|
|
95
|
+
Use `last_message_text(state)` if you only need the final assistant text;
|
|
96
|
+
agents can inspect tool calls in `state["messages"]` directly.
|
|
97
|
+
Note: if `rollout_fn` raises, the adapter substitutes a stand-in state
|
|
98
|
+
of the form `{"messages": [AIMessage("ERROR: <type>: <msg>")], "error": e}`
|
|
99
|
+
and still calls `eval_fn` with it. Check `state.get("error")` to detect
|
|
100
|
+
rollout failures and score them appropriately (e.g. return 0.0 with a
|
|
101
|
+
feedback string explaining the failure to the reflection LM).
|
|
102
|
+
- `reflective_record_fn(example, state, score, feedback) -> mapping`
|
|
103
|
+
(optional): builds the per-example record passed to the reflection LM.
|
|
104
|
+
Defaults to `{"Inputs", "Generated Outputs", "Feedback"}` derived from
|
|
105
|
+
`example["input"]` and `last_message_text(state)`. Override to surface
|
|
106
|
+
tool-call traces, intermediate steps, or domain-specific context.
|
|
107
|
+
- `num_threads`: parallelism for `evaluate` (default 32).
|
|
108
|
+
- `custom_proposer`: optional `ProposalFn` to override GEPA's default
|
|
109
|
+
text-proposal behavior.
|
|
110
|
+
- `show_progress`: whether to render a tqdm progress bar during
|
|
111
|
+
`evaluate` (default True).
|
|
112
|
+
|
|
113
|
+
The candidate is a `dict[str, str]` of named text components; `rollout_fn`
|
|
114
|
+
decides how those components are wired into the call.
|
|
115
|
+
"""
|
|
116
|
+
|
|
117
|
+
def __init__(
|
|
118
|
+
self,
|
|
119
|
+
rollout_fn: RolloutFn,
|
|
120
|
+
eval_fn: EvalFn,
|
|
121
|
+
num_threads: int = 32,
|
|
122
|
+
custom_proposer: ProposalFn | None = None,
|
|
123
|
+
reflective_record_fn: ReflectiveRecordFn = _default_reflective_record,
|
|
124
|
+
show_progress: bool = True,
|
|
125
|
+
):
|
|
126
|
+
self.rollout_fn = rollout_fn
|
|
127
|
+
self.eval_fn = eval_fn
|
|
128
|
+
self.num_threads = num_threads
|
|
129
|
+
self.reflective_record_fn = reflective_record_fn
|
|
130
|
+
self.propose_new_texts = custom_proposer
|
|
131
|
+
self.show_progress = show_progress
|
|
132
|
+
|
|
133
|
+
def evaluate(
|
|
134
|
+
self,
|
|
135
|
+
batch: list[DataInst],
|
|
136
|
+
candidate: dict[str, str],
|
|
137
|
+
capture_traces: bool = False,
|
|
138
|
+
) -> EvaluationBatch:
|
|
139
|
+
logger.info(
|
|
140
|
+
"evaluate: batch=%d capture_traces=%s components=%s",
|
|
141
|
+
len(batch),
|
|
142
|
+
capture_traces,
|
|
143
|
+
list(candidate.keys()),
|
|
144
|
+
)
|
|
145
|
+
for name, text in candidate.items():
|
|
146
|
+
logger.debug("candidate[%s]: %s", name, _truncate(text, limit=400))
|
|
147
|
+
|
|
148
|
+
outputs: list[Any] = [None] * len(batch)
|
|
149
|
+
scores: list[float] = [0.0] * len(batch)
|
|
150
|
+
trajectories: list[Any] | None = [None] * len(batch) if capture_traces else None
|
|
151
|
+
|
|
152
|
+
def run_one(idx: int, example: DataInst):
|
|
153
|
+
t0 = time.perf_counter()
|
|
154
|
+
error: Exception | None = None
|
|
155
|
+
try:
|
|
156
|
+
state: RolloutState = self.rollout_fn(candidate, example)
|
|
157
|
+
if not isinstance(state, dict):
|
|
158
|
+
raise TypeError(
|
|
159
|
+
f"rollout_fn must return a dict, got {type(state).__name__}. "
|
|
160
|
+
f"For single-turn chat use {{'messages': [SystemMessage(...), HumanMessage(...), AIMessage(...)]}}; "
|
|
161
|
+
f"for LangGraph agents return the agent state dict directly."
|
|
162
|
+
)
|
|
163
|
+
except Exception as e:
|
|
164
|
+
state = {"messages": [AIMessage(content=f"ERROR: {type(e).__name__}: {e}")], "error": e}
|
|
165
|
+
error = e
|
|
166
|
+
elapsed_ms = (time.perf_counter() - t0) * 1000
|
|
167
|
+
score, feedback = self.eval_fn(example, state)
|
|
168
|
+
if error is not None:
|
|
169
|
+
input_preview = _truncate(str(example.get("input", example)), limit=120)
|
|
170
|
+
logger.warning(
|
|
171
|
+
"example %d FAILED after %.0fms: %s: %s | input=%s",
|
|
172
|
+
idx,
|
|
173
|
+
elapsed_ms,
|
|
174
|
+
type(error).__name__,
|
|
175
|
+
error,
|
|
176
|
+
input_preview,
|
|
177
|
+
exc_info=logger.isEnabledFor(logging.DEBUG),
|
|
178
|
+
)
|
|
179
|
+
else:
|
|
180
|
+
logger.debug("example %d ok in %.0fms (score=%.2f)", idx, elapsed_ms, score)
|
|
181
|
+
return idx, state, score, feedback
|
|
182
|
+
|
|
183
|
+
start = time.perf_counter()
|
|
184
|
+
with ThreadPoolExecutor(max_workers=self.num_threads) as pool:
|
|
185
|
+
futures = [pool.submit(run_one, i, ex) for i, ex in enumerate(batch)]
|
|
186
|
+
iterator: Any = as_completed(futures)
|
|
187
|
+
if self.show_progress:
|
|
188
|
+
try:
|
|
189
|
+
from tqdm.auto import tqdm
|
|
190
|
+
|
|
191
|
+
iterator = tqdm(iterator, total=len(futures), desc="evaluate", leave=False)
|
|
192
|
+
except ImportError:
|
|
193
|
+
pass
|
|
194
|
+
|
|
195
|
+
running_correct = 0
|
|
196
|
+
for f in iterator:
|
|
197
|
+
idx, state, score, feedback = f.result()
|
|
198
|
+
outputs[idx] = {"state": state, "response": last_message_text(state)}
|
|
199
|
+
scores[idx] = score
|
|
200
|
+
if trajectories is not None:
|
|
201
|
+
trajectories[idx] = {
|
|
202
|
+
"data": batch[idx],
|
|
203
|
+
"state": state,
|
|
204
|
+
"score": score,
|
|
205
|
+
"feedback": feedback,
|
|
206
|
+
}
|
|
207
|
+
if score == 1.0:
|
|
208
|
+
running_correct += 1
|
|
209
|
+
if self.show_progress and hasattr(iterator, "set_postfix"):
|
|
210
|
+
iterator.set_postfix(correct=running_correct, refresh=False)
|
|
211
|
+
logger.debug(
|
|
212
|
+
" example %d: score=%.3f feedback=%s",
|
|
213
|
+
idx,
|
|
214
|
+
score,
|
|
215
|
+
_truncate(feedback, limit=200),
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
elapsed = time.perf_counter() - start
|
|
219
|
+
total = sum(scores)
|
|
220
|
+
mean = total / len(scores) if scores else 0.0
|
|
221
|
+
|
|
222
|
+
failure_counts: Counter[str] = Counter()
|
|
223
|
+
for o in outputs:
|
|
224
|
+
if isinstance(o, dict) and isinstance(o.get("state"), dict) and o["state"].get("error") is not None:
|
|
225
|
+
err_type = type(o["state"]["error"]).__name__
|
|
226
|
+
failure_counts[err_type] += 1
|
|
227
|
+
if failure_counts:
|
|
228
|
+
logger.warning(
|
|
229
|
+
"evaluate failures: %d/%d (by type: %s)",
|
|
230
|
+
sum(failure_counts.values()),
|
|
231
|
+
len(batch),
|
|
232
|
+
dict(failure_counts),
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
logger.info(
|
|
236
|
+
"evaluate done: mean=%.3f sum=%.3f n=%d in %.2fs",
|
|
237
|
+
mean,
|
|
238
|
+
total,
|
|
239
|
+
len(scores),
|
|
240
|
+
elapsed,
|
|
241
|
+
)
|
|
242
|
+
return EvaluationBatch(outputs=outputs, scores=scores, trajectories=trajectories)
|
|
243
|
+
|
|
244
|
+
def make_reflective_dataset(
|
|
245
|
+
self,
|
|
246
|
+
candidate: dict[str, str],
|
|
247
|
+
eval_batch: EvaluationBatch,
|
|
248
|
+
components_to_update: list[str],
|
|
249
|
+
) -> Mapping[str, Sequence[Mapping[str, Any]]]:
|
|
250
|
+
records = [
|
|
251
|
+
self.reflective_record_fn(traj["data"], traj["state"], traj["score"], traj["feedback"])
|
|
252
|
+
for traj in (eval_batch.trajectories or [])
|
|
253
|
+
]
|
|
254
|
+
if not records:
|
|
255
|
+
raise RuntimeError("No trajectories captured; cannot build reflective dataset.")
|
|
256
|
+
logger.info(
|
|
257
|
+
"reflective dataset: %d records for components=%s",
|
|
258
|
+
len(records),
|
|
259
|
+
components_to_update,
|
|
260
|
+
)
|
|
261
|
+
return dict.fromkeys(components_to_update, records)
|