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/dspy_full_program_adapter/full_program_adapter.py
ADDED
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import random
|
|
2
|
+
from typing import Any, Callable
|
|
3
|
+
|
|
4
|
+
import dspy
|
|
5
|
+
from dspy.adapters.types import History
|
|
6
|
+
from dspy.evaluate import Evaluate
|
|
7
|
+
from dspy.primitives import Example, Prediction
|
|
8
|
+
from dspy.teleprompt.bootstrap_trace import TraceData
|
|
9
|
+
|
|
10
|
+
from gepa import EvaluationBatch, GEPAAdapter
|
|
11
|
+
from gepa.proposer.reflective_mutation.base import LanguageModel
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class DspyAdapter(GEPAAdapter[Example, TraceData, Prediction]):
|
|
15
|
+
def __init__(
|
|
16
|
+
self,
|
|
17
|
+
task_lm: dspy.LM,
|
|
18
|
+
metric_fn: Callable,
|
|
19
|
+
reflection_lm: LanguageModel,
|
|
20
|
+
failure_score=0.0,
|
|
21
|
+
num_threads: int | None = None,
|
|
22
|
+
add_format_failure_as_feedback: bool = False,
|
|
23
|
+
rng: random.Random | None = None,
|
|
24
|
+
):
|
|
25
|
+
self.task_lm = task_lm
|
|
26
|
+
self.metric_fn = metric_fn
|
|
27
|
+
assert reflection_lm is not None, (
|
|
28
|
+
"DspyAdapter for full-program evolution requires a reflection_lm to be provided"
|
|
29
|
+
)
|
|
30
|
+
self.reflection_lm = reflection_lm
|
|
31
|
+
self.failure_score = failure_score
|
|
32
|
+
self.num_threads = num_threads
|
|
33
|
+
self.add_format_failure_as_feedback = add_format_failure_as_feedback
|
|
34
|
+
self.rng = rng or random.Random(0)
|
|
35
|
+
|
|
36
|
+
def build_program(self, candidate: dict[str, str]) -> tuple[dspy.Module, None] | tuple[None, str]:
|
|
37
|
+
candidate_src = candidate["program"]
|
|
38
|
+
context = {}
|
|
39
|
+
o = self.load_dspy_program_from_code(candidate_src, context)
|
|
40
|
+
return o
|
|
41
|
+
|
|
42
|
+
def load_dspy_program_from_code(
|
|
43
|
+
self,
|
|
44
|
+
candidate_src: str,
|
|
45
|
+
context: dict,
|
|
46
|
+
):
|
|
47
|
+
try:
|
|
48
|
+
compile(candidate_src, "<string>", "exec")
|
|
49
|
+
except SyntaxError as e:
|
|
50
|
+
# print(f"Syntax Error in original code {e}")
|
|
51
|
+
# return None
|
|
52
|
+
import traceback
|
|
53
|
+
|
|
54
|
+
tb = traceback.format_exc()
|
|
55
|
+
return None, f"Syntax Error in code: {e}\n{tb}"
|
|
56
|
+
|
|
57
|
+
try:
|
|
58
|
+
exec(candidate_src, context) # expose to current namespace
|
|
59
|
+
except Exception as e:
|
|
60
|
+
import traceback
|
|
61
|
+
|
|
62
|
+
tb = traceback.format_exc()
|
|
63
|
+
return None, f"Error in executing code: {e}\n{tb}"
|
|
64
|
+
|
|
65
|
+
dspy_program = context.get("program")
|
|
66
|
+
|
|
67
|
+
if dspy_program is None:
|
|
68
|
+
return (
|
|
69
|
+
None,
|
|
70
|
+
"Your code did not define a `program` object. Please define a `program` object which is an instance of `dspy.Module`, either directly by dspy.Predict or dspy.ChainOfThought, or by instantiating a class that inherits from `dspy.Module`.",
|
|
71
|
+
)
|
|
72
|
+
else:
|
|
73
|
+
if not isinstance(dspy_program, dspy.Module):
|
|
74
|
+
return (
|
|
75
|
+
None,
|
|
76
|
+
f"Your code defined a `program` object, but it is an instance of {type(dspy_program)}, not `dspy.Module`. Please define a `program` object which is an instance of `dspy.Module`, either directly by dspy.Predict or dspy.ChainOfThought, or by instantiating a class that inherits from `dspy.Module`.",
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
dspy_program.set_lm(self.task_lm)
|
|
80
|
+
|
|
81
|
+
return dspy_program, None
|
|
82
|
+
|
|
83
|
+
def evaluate(self, batch, candidate, capture_traces=False):
|
|
84
|
+
program, feedback = self.build_program(candidate)
|
|
85
|
+
|
|
86
|
+
if program is None:
|
|
87
|
+
return EvaluationBatch(
|
|
88
|
+
outputs=[None for _ in batch], scores=[self.failure_score for _ in batch], trajectories=feedback
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
if capture_traces:
|
|
92
|
+
# bootstrap_trace_data-like flow with trace capture
|
|
93
|
+
from dspy.teleprompt.bootstrap_trace import bootstrap_trace_data
|
|
94
|
+
|
|
95
|
+
trajs = bootstrap_trace_data(
|
|
96
|
+
program=program,
|
|
97
|
+
dataset=batch,
|
|
98
|
+
metric=self.metric_fn,
|
|
99
|
+
num_threads=self.num_threads,
|
|
100
|
+
raise_on_error=False,
|
|
101
|
+
capture_failed_parses=True,
|
|
102
|
+
failure_score=self.failure_score,
|
|
103
|
+
format_failure_score=self.failure_score,
|
|
104
|
+
)
|
|
105
|
+
scores = []
|
|
106
|
+
outputs = []
|
|
107
|
+
for t in trajs:
|
|
108
|
+
outputs.append(t["prediction"])
|
|
109
|
+
if hasattr(t["prediction"], "__class__") and t.get("score") is None:
|
|
110
|
+
scores.append(self.failure_score)
|
|
111
|
+
else:
|
|
112
|
+
score = t["score"]
|
|
113
|
+
if hasattr(score, "score"):
|
|
114
|
+
score = score["score"]
|
|
115
|
+
scores.append(score)
|
|
116
|
+
return EvaluationBatch(outputs=outputs, scores=scores, trajectories=trajs)
|
|
117
|
+
else:
|
|
118
|
+
evaluator = Evaluate(
|
|
119
|
+
devset=batch,
|
|
120
|
+
metric=self.metric_fn,
|
|
121
|
+
num_threads=self.num_threads,
|
|
122
|
+
return_all_scores=True,
|
|
123
|
+
failure_score=self.failure_score,
|
|
124
|
+
provide_traceback=True,
|
|
125
|
+
max_errors=len(batch) * 100,
|
|
126
|
+
)
|
|
127
|
+
res = evaluator(program)
|
|
128
|
+
outputs = [r[1] for r in res.results]
|
|
129
|
+
scores = [r[2] for r in res.results]
|
|
130
|
+
scores = [s["score"] if hasattr(s, "score") else s for s in scores]
|
|
131
|
+
return EvaluationBatch(outputs=outputs, scores=scores, trajectories=None)
|
|
132
|
+
|
|
133
|
+
def make_reflective_dataset(self, candidate, eval_batch, components_to_update):
|
|
134
|
+
proposed_program, _ = self.build_program(candidate)
|
|
135
|
+
|
|
136
|
+
assert set(components_to_update) == {"program"}, f"set(components_to_update) = {set(components_to_update)}"
|
|
137
|
+
from dspy.teleprompt.bootstrap_trace import FailedPrediction
|
|
138
|
+
|
|
139
|
+
ret_d: dict[str, list[dict[str, Any]]] = {}
|
|
140
|
+
|
|
141
|
+
if isinstance(eval_batch.trajectories, str):
|
|
142
|
+
feedback = eval_batch.trajectories
|
|
143
|
+
return {"program": {"Feedback": feedback}}
|
|
144
|
+
|
|
145
|
+
########
|
|
146
|
+
items: list[dict[str, Any]] = []
|
|
147
|
+
for data in eval_batch.trajectories or []:
|
|
148
|
+
example_data = {}
|
|
149
|
+
trace = data["trace"]
|
|
150
|
+
example = data["example"]
|
|
151
|
+
example_data["Program Inputs"] = {**example.inputs()}
|
|
152
|
+
prediction = data["prediction"]
|
|
153
|
+
example_data["Program Outputs"] = {**prediction}
|
|
154
|
+
module_score = data["score"]
|
|
155
|
+
|
|
156
|
+
if hasattr(module_score, "feedback"):
|
|
157
|
+
feedback_text = module_score["feedback"]
|
|
158
|
+
else:
|
|
159
|
+
feedback_text = None
|
|
160
|
+
|
|
161
|
+
if hasattr(module_score, "score"):
|
|
162
|
+
module_score = module_score["score"]
|
|
163
|
+
|
|
164
|
+
trace_instances = trace
|
|
165
|
+
|
|
166
|
+
if len(trace_instances) == 0:
|
|
167
|
+
continue
|
|
168
|
+
|
|
169
|
+
selected = None
|
|
170
|
+
for t in trace_instances:
|
|
171
|
+
if isinstance(t[2], FailedPrediction):
|
|
172
|
+
selected = t
|
|
173
|
+
break
|
|
174
|
+
|
|
175
|
+
if selected is not None:
|
|
176
|
+
trace_instances = [selected]
|
|
177
|
+
|
|
178
|
+
trace_d = []
|
|
179
|
+
example_data["Program Trace"] = trace_d
|
|
180
|
+
for selected in trace_instances:
|
|
181
|
+
inputs = selected[1]
|
|
182
|
+
outputs = selected[2]
|
|
183
|
+
|
|
184
|
+
pred_name = None
|
|
185
|
+
for name, predictor in proposed_program.named_predictors():
|
|
186
|
+
if predictor.signature.equals(selected[0].signature):
|
|
187
|
+
pred_name = name
|
|
188
|
+
break
|
|
189
|
+
assert pred_name is not None, f"Could not find predictor for {selected[0].signature}"
|
|
190
|
+
|
|
191
|
+
new_inputs = {}
|
|
192
|
+
new_outputs = {}
|
|
193
|
+
|
|
194
|
+
contains_history = False
|
|
195
|
+
history_key_name = None
|
|
196
|
+
for input_key, input_val in inputs.items():
|
|
197
|
+
if isinstance(input_val, History):
|
|
198
|
+
contains_history = True
|
|
199
|
+
assert history_key_name is None
|
|
200
|
+
history_key_name = input_key
|
|
201
|
+
|
|
202
|
+
if contains_history:
|
|
203
|
+
s = "```json\n"
|
|
204
|
+
for i, message in enumerate(inputs[history_key_name].messages):
|
|
205
|
+
s += f" {i}: {message}\n"
|
|
206
|
+
s += "```"
|
|
207
|
+
new_inputs["Context"] = s
|
|
208
|
+
|
|
209
|
+
for input_key, input_val in inputs.items():
|
|
210
|
+
if contains_history and input_key == history_key_name:
|
|
211
|
+
continue
|
|
212
|
+
new_inputs[input_key] = str(input_val)
|
|
213
|
+
|
|
214
|
+
if isinstance(outputs, FailedPrediction):
|
|
215
|
+
s = "Couldn't parse the output as per the expected output format. The model's raw response was:\n"
|
|
216
|
+
s += "```\n"
|
|
217
|
+
s += outputs.completion_text + "\n"
|
|
218
|
+
s += "```\n\n"
|
|
219
|
+
new_outputs = s
|
|
220
|
+
else:
|
|
221
|
+
for output_key, output_val in outputs.items():
|
|
222
|
+
new_outputs[output_key] = str(output_val)
|
|
223
|
+
|
|
224
|
+
d = {"Called Module": pred_name, "Inputs": new_inputs, "Generated Outputs": new_outputs}
|
|
225
|
+
# if isinstance(outputs, FailedPrediction):
|
|
226
|
+
# adapter = ChatAdapter()
|
|
227
|
+
# structure_instruction = ""
|
|
228
|
+
# for dd in adapter.format(module.signature, [], {}):
|
|
229
|
+
# structure_instruction += dd["role"] + ": " + dd["content"] + "\n"
|
|
230
|
+
# d["Feedback"] = "Your output failed to parse. Follow this structure:\n" + structure_instruction
|
|
231
|
+
# # d['score'] = self.failure_score
|
|
232
|
+
# else:
|
|
233
|
+
# assert fb["score"] == module_score, f"Currently, GEPA only supports feedback functions that return the same score as the module's score. However, the module-level score is {module_score} and the feedback score is {fb.score}."
|
|
234
|
+
# d['score'] = fb.score
|
|
235
|
+
trace_d.append(d)
|
|
236
|
+
|
|
237
|
+
if feedback_text is not None:
|
|
238
|
+
example_data["Feedback"] = feedback_text
|
|
239
|
+
|
|
240
|
+
items.append(example_data)
|
|
241
|
+
|
|
242
|
+
if len(items) == 0:
|
|
243
|
+
raise Exception("No valid predictions found for program.")
|
|
244
|
+
|
|
245
|
+
ret_d["program"] = items
|
|
246
|
+
|
|
247
|
+
########
|
|
248
|
+
if len(ret_d) == 0:
|
|
249
|
+
raise Exception("No valid predictions found for any module.")
|
|
250
|
+
|
|
251
|
+
return ret_d
|
|
252
|
+
|
|
253
|
+
def propose_new_texts(
|
|
254
|
+
self,
|
|
255
|
+
candidate: dict[str, str],
|
|
256
|
+
reflective_dataset: dict[str, list[dict[str, Any]]],
|
|
257
|
+
components_to_update: list[str],
|
|
258
|
+
) -> dict[str, str]:
|
|
259
|
+
from gepa.adapters.dspy_full_program_adapter.dspy_program_proposal_signature import DSPyProgramProposalSignature
|
|
260
|
+
|
|
261
|
+
new_texts: dict[str, str] = {}
|
|
262
|
+
for name in components_to_update:
|
|
263
|
+
base_instruction = candidate[name]
|
|
264
|
+
dataset_with_feedback = reflective_dataset[name]
|
|
265
|
+
new_texts[name] = DSPyProgramProposalSignature.run(
|
|
266
|
+
lm=self.reflection_lm,
|
|
267
|
+
input_dict={"curr_program": base_instruction, "dataset_with_feedback": dataset_with_feedback},
|
|
268
|
+
)["new_program"]
|
|
269
|
+
return new_texts
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Copyright (c) 2025 Lakshya A Agrawal and the GEPA contributors
|
|
2
|
+
# https://github.com/gepa-ai/gepa
|
|
3
|
+
|
|
4
|
+
from .evaluation_metrics import RAGEvaluationMetrics
|
|
5
|
+
from .generic_rag_adapter import (
|
|
6
|
+
GenericRAGAdapter,
|
|
7
|
+
RAGDataInst,
|
|
8
|
+
RAGOutput,
|
|
9
|
+
RAGTrajectory,
|
|
10
|
+
)
|
|
11
|
+
from .rag_pipeline import RAGPipeline
|
|
12
|
+
from .vector_store_interface import VectorStoreInterface
|
|
13
|
+
from .vector_stores.chroma_store import ChromaVectorStore
|
|
14
|
+
from .vector_stores.weaviate_store import WeaviateVectorStore
|
|
15
|
+
|
|
16
|
+
# Optional vector stores - import only if dependencies are available
|
|
17
|
+
try:
|
|
18
|
+
from .vector_stores.qdrant_store import QdrantVectorStore
|
|
19
|
+
|
|
20
|
+
_QDRANT_AVAILABLE = True
|
|
21
|
+
except ImportError:
|
|
22
|
+
_QDRANT_AVAILABLE = False
|
|
23
|
+
|
|
24
|
+
try:
|
|
25
|
+
from .vector_stores.milvus_store import MilvusVectorStore
|
|
26
|
+
|
|
27
|
+
_MILVUS_AVAILABLE = True
|
|
28
|
+
except ImportError:
|
|
29
|
+
_MILVUS_AVAILABLE = False
|
|
30
|
+
|
|
31
|
+
try:
|
|
32
|
+
from .vector_stores.lancedb_store import LanceDBVectorStore
|
|
33
|
+
|
|
34
|
+
_LANCEDB_AVAILABLE = True
|
|
35
|
+
except ImportError:
|
|
36
|
+
_LANCEDB_AVAILABLE = False
|
|
37
|
+
|
|
38
|
+
__all__ = [
|
|
39
|
+
"GenericRAGAdapter",
|
|
40
|
+
"RAGDataInst",
|
|
41
|
+
"RAGOutput",
|
|
42
|
+
"RAGTrajectory",
|
|
43
|
+
"VectorStoreInterface",
|
|
44
|
+
"ChromaVectorStore",
|
|
45
|
+
"WeaviateVectorStore",
|
|
46
|
+
"RAGPipeline",
|
|
47
|
+
"RAGEvaluationMetrics",
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
# Add optional vector stores to __all__ if available
|
|
51
|
+
if _QDRANT_AVAILABLE:
|
|
52
|
+
__all__.append("QdrantVectorStore")
|
|
53
|
+
if _MILVUS_AVAILABLE:
|
|
54
|
+
__all__.append("MilvusVectorStore")
|
|
55
|
+
if _LANCEDB_AVAILABLE:
|
|
56
|
+
__all__.append("LanceDBVectorStore")
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
# Copyright (c) 2025 Lakshya A Agrawal and the GEPA contributors
|
|
2
|
+
# https://github.com/gepa-ai/gepa
|
|
3
|
+
|
|
4
|
+
import re
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class RAGEvaluationMetrics:
|
|
9
|
+
"""
|
|
10
|
+
Evaluation metrics for RAG systems.
|
|
11
|
+
|
|
12
|
+
Provides both retrieval and generation quality metrics
|
|
13
|
+
for comprehensive RAG system evaluation.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
def evaluate_retrieval(self, retrieved_docs: list[dict[str, Any]], relevant_doc_ids: list[str]) -> dict[str, float]:
|
|
17
|
+
"""
|
|
18
|
+
Evaluate retrieval quality metrics.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
retrieved_docs: List of retrieved documents with metadata
|
|
22
|
+
relevant_doc_ids: List of ground truth relevant document IDs
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
Dictionary with retrieval metrics (precision, recall, f1, mrr)
|
|
26
|
+
"""
|
|
27
|
+
if not retrieved_docs or not relevant_doc_ids:
|
|
28
|
+
return {"retrieval_precision": 0.0, "retrieval_recall": 0.0, "retrieval_f1": 0.0, "retrieval_mrr": 0.0}
|
|
29
|
+
|
|
30
|
+
# Extract document IDs from retrieved docs
|
|
31
|
+
retrieved_ids = []
|
|
32
|
+
for doc in retrieved_docs:
|
|
33
|
+
doc_id = doc.get("metadata", {}).get("doc_id") or doc.get("metadata", {}).get("id")
|
|
34
|
+
if doc_id:
|
|
35
|
+
retrieved_ids.append(str(doc_id))
|
|
36
|
+
|
|
37
|
+
relevant_set = set(relevant_doc_ids)
|
|
38
|
+
retrieved_set = set(retrieved_ids)
|
|
39
|
+
|
|
40
|
+
# Calculate precision and recall
|
|
41
|
+
if len(retrieved_set) == 0:
|
|
42
|
+
precision = 0.0
|
|
43
|
+
else:
|
|
44
|
+
precision = len(relevant_set.intersection(retrieved_set)) / len(retrieved_set)
|
|
45
|
+
|
|
46
|
+
if len(relevant_set) == 0:
|
|
47
|
+
recall = 0.0
|
|
48
|
+
else:
|
|
49
|
+
recall = len(relevant_set.intersection(retrieved_set)) / len(relevant_set)
|
|
50
|
+
|
|
51
|
+
# Calculate F1
|
|
52
|
+
if precision + recall == 0:
|
|
53
|
+
f1 = 0.0
|
|
54
|
+
else:
|
|
55
|
+
f1 = 2 * (precision * recall) / (precision + recall)
|
|
56
|
+
|
|
57
|
+
# Calculate Mean Reciprocal Rank (MRR)
|
|
58
|
+
mrr = 0.0
|
|
59
|
+
for i, retrieved_id in enumerate(retrieved_ids):
|
|
60
|
+
if retrieved_id in relevant_set:
|
|
61
|
+
mrr = 1.0 / (i + 1)
|
|
62
|
+
break
|
|
63
|
+
|
|
64
|
+
return {"retrieval_precision": precision, "retrieval_recall": recall, "retrieval_f1": f1, "retrieval_mrr": mrr}
|
|
65
|
+
|
|
66
|
+
def evaluate_generation(self, generated_answer: str, ground_truth: str, context: str) -> dict[str, float]:
|
|
67
|
+
"""
|
|
68
|
+
Evaluate generation quality metrics.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
generated_answer: Generated answer text
|
|
72
|
+
ground_truth: Ground truth answer
|
|
73
|
+
context: Retrieved context used for generation
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
Dictionary with generation metrics
|
|
77
|
+
"""
|
|
78
|
+
# Exact match (case-insensitive)
|
|
79
|
+
exact_match = self._exact_match(generated_answer, ground_truth)
|
|
80
|
+
|
|
81
|
+
# F1 score based on token overlap
|
|
82
|
+
f1_score = self._token_f1(generated_answer, ground_truth)
|
|
83
|
+
|
|
84
|
+
# BLEU-like score
|
|
85
|
+
bleu_score = self._simple_bleu(generated_answer, ground_truth)
|
|
86
|
+
|
|
87
|
+
# Answer relevance (simple keyword overlap with context)
|
|
88
|
+
relevance_score = self._answer_relevance(generated_answer, context)
|
|
89
|
+
|
|
90
|
+
# Faithfulness (how well the answer is supported by context)
|
|
91
|
+
faithfulness_score = self._faithfulness_score(generated_answer, context)
|
|
92
|
+
|
|
93
|
+
return {
|
|
94
|
+
"exact_match": float(exact_match),
|
|
95
|
+
"token_f1": f1_score,
|
|
96
|
+
"bleu_score": bleu_score,
|
|
97
|
+
"answer_relevance": relevance_score,
|
|
98
|
+
"faithfulness": faithfulness_score,
|
|
99
|
+
"answer_confidence": (f1_score + relevance_score + faithfulness_score) / 3.0,
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
def combined_rag_score(
|
|
103
|
+
self,
|
|
104
|
+
retrieval_metrics: dict[str, float],
|
|
105
|
+
generation_metrics: dict[str, float],
|
|
106
|
+
retrieval_weight: float = 0.3,
|
|
107
|
+
generation_weight: float = 0.7,
|
|
108
|
+
) -> float:
|
|
109
|
+
"""
|
|
110
|
+
Combine retrieval and generation metrics into a single score.
|
|
111
|
+
|
|
112
|
+
Args:
|
|
113
|
+
retrieval_metrics: Output from evaluate_retrieval
|
|
114
|
+
generation_metrics: Output from evaluate_generation
|
|
115
|
+
retrieval_weight: Weight for retrieval score
|
|
116
|
+
generation_weight: Weight for generation score
|
|
117
|
+
|
|
118
|
+
Returns:
|
|
119
|
+
Combined score between 0 and 1
|
|
120
|
+
"""
|
|
121
|
+
# Primary retrieval metric: F1 score
|
|
122
|
+
retrieval_score = retrieval_metrics.get("retrieval_f1", 0.0)
|
|
123
|
+
|
|
124
|
+
# Primary generation metric: weighted combination
|
|
125
|
+
generation_score = (
|
|
126
|
+
generation_metrics.get("token_f1", 0.0) * 0.4
|
|
127
|
+
+ generation_metrics.get("answer_relevance", 0.0) * 0.3
|
|
128
|
+
+ generation_metrics.get("faithfulness", 0.0) * 0.3
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
return retrieval_weight * retrieval_score + generation_weight * generation_score
|
|
132
|
+
|
|
133
|
+
def _exact_match(self, prediction: str, ground_truth: str) -> bool:
|
|
134
|
+
"""Check if prediction exactly matches ground truth (case-insensitive)."""
|
|
135
|
+
return prediction.strip().lower() == ground_truth.strip().lower()
|
|
136
|
+
|
|
137
|
+
def _token_f1(self, prediction: str, ground_truth: str) -> float:
|
|
138
|
+
"""Calculate F1 score based on token overlap."""
|
|
139
|
+
pred_tokens = set(self._normalize_text(prediction).split())
|
|
140
|
+
truth_tokens = set(self._normalize_text(ground_truth).split())
|
|
141
|
+
|
|
142
|
+
if len(pred_tokens) == 0 and len(truth_tokens) == 0:
|
|
143
|
+
return 1.0
|
|
144
|
+
if len(pred_tokens) == 0 or len(truth_tokens) == 0:
|
|
145
|
+
return 0.0
|
|
146
|
+
|
|
147
|
+
intersection = pred_tokens.intersection(truth_tokens)
|
|
148
|
+
precision = len(intersection) / len(pred_tokens)
|
|
149
|
+
recall = len(intersection) / len(truth_tokens)
|
|
150
|
+
|
|
151
|
+
if precision + recall == 0:
|
|
152
|
+
return 0.0
|
|
153
|
+
|
|
154
|
+
return 2 * (precision * recall) / (precision + recall)
|
|
155
|
+
|
|
156
|
+
def _simple_bleu(self, prediction: str, ground_truth: str, n: int = 2) -> float:
|
|
157
|
+
"""Simple BLEU-like score for n-gram overlap."""
|
|
158
|
+
pred_words = self._normalize_text(prediction).split()
|
|
159
|
+
truth_words = self._normalize_text(ground_truth).split()
|
|
160
|
+
|
|
161
|
+
if len(pred_words) < n or len(truth_words) < n:
|
|
162
|
+
return self._token_f1(prediction, ground_truth)
|
|
163
|
+
|
|
164
|
+
pred_ngrams = {tuple(pred_words[i : i + n]) for i in range(len(pred_words) - n + 1)}
|
|
165
|
+
truth_ngrams = {tuple(truth_words[i : i + n]) for i in range(len(truth_words) - n + 1)}
|
|
166
|
+
|
|
167
|
+
if len(pred_ngrams) == 0 or len(truth_ngrams) == 0:
|
|
168
|
+
return 0.0
|
|
169
|
+
|
|
170
|
+
intersection = pred_ngrams.intersection(truth_ngrams)
|
|
171
|
+
return len(intersection) / len(pred_ngrams)
|
|
172
|
+
|
|
173
|
+
def _answer_relevance(self, answer: str, context: str) -> float:
|
|
174
|
+
"""Measure how well the answer relates to the provided context."""
|
|
175
|
+
answer_words = set(self._normalize_text(answer).split())
|
|
176
|
+
context_words = set(self._normalize_text(context).split())
|
|
177
|
+
|
|
178
|
+
if len(answer_words) == 0:
|
|
179
|
+
return 0.0
|
|
180
|
+
|
|
181
|
+
overlap = answer_words.intersection(context_words)
|
|
182
|
+
return len(overlap) / len(answer_words)
|
|
183
|
+
|
|
184
|
+
def _faithfulness_score(self, answer: str, context: str) -> float:
|
|
185
|
+
"""
|
|
186
|
+
Measure how well the answer is supported by the context.
|
|
187
|
+
Simple implementation based on shared key phrases.
|
|
188
|
+
"""
|
|
189
|
+
# Extract key phrases (sequences of 2+ words)
|
|
190
|
+
answer_phrases = self._extract_phrases(answer)
|
|
191
|
+
context_phrases = self._extract_phrases(context)
|
|
192
|
+
|
|
193
|
+
if len(answer_phrases) == 0:
|
|
194
|
+
return 1.0 # Empty answer is technically faithful
|
|
195
|
+
|
|
196
|
+
supported_phrases = answer_phrases.intersection(context_phrases)
|
|
197
|
+
return len(supported_phrases) / len(answer_phrases)
|
|
198
|
+
|
|
199
|
+
def _extract_phrases(self, text: str, min_length: int = 2) -> set[str]:
|
|
200
|
+
"""Extract meaningful phrases from text."""
|
|
201
|
+
words = self._normalize_text(text).split()
|
|
202
|
+
phrases = set()
|
|
203
|
+
|
|
204
|
+
# Add individual significant words (length > 3)
|
|
205
|
+
for word in words:
|
|
206
|
+
if len(word) > 3:
|
|
207
|
+
phrases.add(word)
|
|
208
|
+
|
|
209
|
+
# Add bi-grams and tri-grams
|
|
210
|
+
for n in range(min_length, min(4, len(words) + 1)):
|
|
211
|
+
for i in range(len(words) - n + 1):
|
|
212
|
+
phrase = " ".join(words[i : i + n])
|
|
213
|
+
if len(phrase) > 5: # Only meaningful phrases
|
|
214
|
+
phrases.add(phrase)
|
|
215
|
+
|
|
216
|
+
return phrases
|
|
217
|
+
|
|
218
|
+
def _normalize_text(self, text: str) -> str:
|
|
219
|
+
"""Normalize text for comparison."""
|
|
220
|
+
# Convert to lowercase and remove extra whitespace
|
|
221
|
+
text = text.lower().strip()
|
|
222
|
+
# Remove punctuation and special characters
|
|
223
|
+
text = re.sub(r"[^\w\s]", " ", text)
|
|
224
|
+
# Normalize whitespace
|
|
225
|
+
text = re.sub(r"\s+", " ", text)
|
|
226
|
+
return text
|