orizu 0.5.20 → 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/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 +3 -0
- 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 +48 -2
- package/dist/gepa-python-command.d.ts +6 -0
- package/dist/gepa-python-command.d.ts.map +1 -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 +6 -3
- 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 +91 -23
- 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 +88 -92
- 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/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-report-cli.d.ts +10 -0
- package/dist/prompt-report-cli.d.ts.map +1 -0
- 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-replacement-warning.d.ts +17 -0
- package/dist/report-replacement-warning.d.ts.map +1 -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 +14 -1
- package/requirements/README.md +138 -0
- package/requirements/skilled-proposer.in +66 -0
- package/requirements/skilled-proposer.lock +265 -0
- package/scripts/ensure-skilled-proposer-venv.mjs +5 -0
- package/scripts/refresh-skilled-proposer-lock.mjs +256 -0
- package/src/diff-comments-cli.ts +4 -0
- package/src/gepa-engine-dispatch.ts +42 -2
- package/src/gepa-python-paths.ts +6 -3
- package/src/help.ts +93 -23
- package/src/index.ts +99 -126
- 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/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-summary-table.ts +12 -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/orizu-gepa/src/orizu_gepa_connector/__init__.py +2 -1
- package/vendor/orizu-gepa/src/orizu_gepa_connector/adapter.py +8 -2
- package/vendor/orizu-gepa/src/orizu_gepa_connector/callbacks.py +17 -3
- package/vendor/orizu-gepa/src/orizu_gepa_connector/engine.py +100 -8
- package/vendor/orizu-gepa/src/orizu_gepa_connector/instruction_set_loader.py +52 -0
- package/vendor/orizu-gepa/src/orizu_gepa_connector/reflection.py +92 -12
- package/vendor/orizu-gepa/src/orizu_gepa_connector/runtime.py +100 -22
- 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 +17 -0
- package/vendor/orizu-gepa/src/orizu_gepa_connector/translator.py +2 -1
- package/vendor/orizu-gepa-python/src/orizu_gepa/client.py +52 -7
- package/vendor/orizu-gepa-python/src/orizu_gepa/local_log.py +4 -1
- 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 +2 -0
- 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-with-gepa.md +25 -0
- package/vendor/skills/orizu-cli/references/prompt-control-plane.md +108 -8
package/tsconfig.json
CHANGED
|
@@ -4,5 +4,6 @@ from .adapter import RunnerEvaluationAdapter
|
|
|
4
4
|
from .callbacks import LifecycleHooks, OrizuCallback
|
|
5
5
|
from .engine import SeedValidationRefused, run_official_gepa
|
|
6
6
|
from .cli import main
|
|
7
|
+
from .instruction_set_loader import load_instruction_set
|
|
7
8
|
|
|
8
|
-
__all__ = ["LifecycleHooks", "OrizuCallback", "RunnerEvaluationAdapter", "SeedValidationRefused", "main", "run_official_gepa"]
|
|
9
|
+
__all__ = ["LifecycleHooks", "OrizuCallback", "RunnerEvaluationAdapter", "SeedValidationRefused", "load_instruction_set", "main", "run_official_gepa"]
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import json
|
|
5
6
|
from typing import Any
|
|
6
7
|
|
|
7
8
|
from gepa.core.adapter import EvaluationBatch
|
|
@@ -59,8 +60,13 @@ class RunnerEvaluationAdapter:
|
|
|
59
60
|
self.last_candidate: dict[str, str] | None = None
|
|
60
61
|
|
|
61
62
|
def evaluate(self, batch: list[Any], candidate: dict[str, str], capture_traces: bool = False) -> EvaluationBatch:
|
|
63
|
+
# The exec-context can describe a multi-component wrapper for a
|
|
64
|
+
# legacy prompt run. The candidate is the execution contract: only a
|
|
65
|
+
# map with more than one value is a tuple rewrite.
|
|
66
|
+
is_multi_component_tuple = len(candidate) > 1
|
|
62
67
|
component = next(iter(candidate))
|
|
63
|
-
candidate_text = candidate[component]
|
|
68
|
+
candidate_text: str | dict[str, str] = dict(candidate) if is_multi_component_tuple else candidate[component]
|
|
69
|
+
candidate_id = json.dumps(candidate, sort_keys=True, separators=(",", ":")) if is_multi_component_tuple else component
|
|
64
70
|
outputs: list[dict[str, Any]] = []
|
|
65
71
|
scores: list[float] = []
|
|
66
72
|
trajectories: list[dict[str, Any]] = []
|
|
@@ -74,7 +80,7 @@ class RunnerEvaluationAdapter:
|
|
|
74
80
|
try:
|
|
75
81
|
row_evaluations = evaluate_candidate(
|
|
76
82
|
candidate_text=candidate_text,
|
|
77
|
-
candidate_id=
|
|
83
|
+
candidate_id=candidate_id,
|
|
78
84
|
rows=batch,
|
|
79
85
|
split="official_gepa",
|
|
80
86
|
prompt_context=self.prompt_context,
|
|
@@ -248,9 +248,12 @@ class OrizuCallback:
|
|
|
248
248
|
def on_proposal_start(self, event: dict[str, Any]) -> None:
|
|
249
249
|
iteration = int(event["iteration"])
|
|
250
250
|
self._flush_pending_parent_minibatches(iteration)
|
|
251
|
+
parent_candidate = event.get("parent_candidate")
|
|
251
252
|
self._reflection_contexts[iteration] = {
|
|
252
253
|
"iteration": iteration,
|
|
253
254
|
"parent_candidate_id": self._selected_parent_by_iteration.get(iteration),
|
|
255
|
+
"components_to_update": list(event.get("components") or []),
|
|
256
|
+
"parent_candidate": dict(parent_candidate) if isinstance(parent_candidate, dict) else None,
|
|
254
257
|
}
|
|
255
258
|
self._emit("on_proposal_start", event)
|
|
256
259
|
|
|
@@ -495,18 +498,29 @@ class OrizuCallback:
|
|
|
495
498
|
def _emit_buffered_proposal(self, event: dict[str, Any], candidate_id: str) -> None:
|
|
496
499
|
iteration = int(event["iteration"])
|
|
497
500
|
parent_id = self._selected_parent_by_iteration.get(iteration)
|
|
498
|
-
|
|
501
|
+
context = self._reflection_contexts.get(iteration, {})
|
|
502
|
+
parent_candidate = context.get("parent_candidate")
|
|
503
|
+
new_instructions = event.get("new_instructions") or {}
|
|
504
|
+
components = {
|
|
505
|
+
**(parent_candidate if isinstance(parent_candidate, dict) else {}),
|
|
506
|
+
**(new_instructions if isinstance(new_instructions, dict) else {}),
|
|
507
|
+
}
|
|
499
508
|
bounded_components = {}
|
|
500
509
|
truncated = False
|
|
501
510
|
for key, value in components.items():
|
|
502
511
|
bounded, did_truncate = _bounded(value, self.max_payload_chars)
|
|
503
512
|
bounded_components[key] = bounded
|
|
504
513
|
truncated = truncated or did_truncate
|
|
514
|
+
selected = list(context.get("components_to_update") or [])
|
|
505
515
|
body, body_truncated = _bounded(next(iter(components.values()), None), self.max_payload_chars)
|
|
516
|
+
payload = {"components": bounded_components, "components_to_update": selected,
|
|
517
|
+
"payload_truncated": truncated or body_truncated}
|
|
518
|
+
if ((isinstance(parent_candidate, dict) and len(parent_candidate) == 1)
|
|
519
|
+
or (parent_candidate is None and len(components) == 1)):
|
|
520
|
+
payload["body"] = body
|
|
506
521
|
self.sink.emit(
|
|
507
522
|
"candidate_proposed",
|
|
508
|
-
|
|
509
|
-
"payload_truncated": truncated or body_truncated},
|
|
523
|
+
payload,
|
|
510
524
|
iteration=iteration,
|
|
511
525
|
candidate_id=candidate_id,
|
|
512
526
|
parent_candidate_id=parent_id,
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
from collections.abc import Callable, Sequence
|
|
6
|
+
import dataclasses
|
|
6
7
|
import json
|
|
7
8
|
import random
|
|
8
9
|
from typing import Any
|
|
@@ -13,6 +14,7 @@ from orizu_gepa.optimizer import TextGepaConfig
|
|
|
13
14
|
|
|
14
15
|
from .callbacks import LifecycleHooks, OrizuCallback
|
|
15
16
|
from .preflight import validate_seed_and_scorer
|
|
17
|
+
from .stop_conditions import ProposalBudgetStopper
|
|
16
18
|
|
|
17
19
|
|
|
18
20
|
def _row_id(row: Any, index: int) -> str:
|
|
@@ -84,8 +86,8 @@ def _preflight_row_evidence(rows: list[Any], scores: list[float], outputs: list[
|
|
|
84
86
|
return evidence
|
|
85
87
|
|
|
86
88
|
|
|
87
|
-
def
|
|
88
|
-
|
|
89
|
+
def _validate_seed_before_run(*, seed_candidate: dict[str, str], valset: list[Any], adapter: Any,
|
|
90
|
+
hooks: LifecycleHooks, allow_degenerate_seed: bool) -> tuple[dict[str, Any], Any, list[Any]]:
|
|
89
91
|
"""Cheap scorer preflight; GEPA owns the single full seed validation."""
|
|
90
92
|
# The approved cap bounds preflight spend. With a binary scorer and a seed
|
|
91
93
|
# that is correct 70% of the time, all three sampled rows can still score
|
|
@@ -103,23 +105,60 @@ def validate_seed_before_run(*, seed_candidate: dict[str, str], valset: list[Any
|
|
|
103
105
|
hooks.emit("seed_validated", verdict)
|
|
104
106
|
if not verdict["allowed"]:
|
|
105
107
|
raise SeedValidationRefused(verdict)
|
|
108
|
+
return verdict, seed_evaluation, preflight_rows
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def validate_seed_before_run(*, seed_candidate: dict[str, str], valset: list[Any], adapter: Any,
|
|
112
|
+
hooks: LifecycleHooks, allow_degenerate_seed: bool) -> dict[str, Any]:
|
|
113
|
+
"""Validate the seed and return the durable public preflight verdict."""
|
|
114
|
+
verdict, _evaluation, _rows = _validate_seed_before_run(
|
|
115
|
+
seed_candidate=seed_candidate,
|
|
116
|
+
valset=valset,
|
|
117
|
+
adapter=adapter,
|
|
118
|
+
hooks=hooks,
|
|
119
|
+
allow_degenerate_seed=allow_degenerate_seed,
|
|
120
|
+
)
|
|
106
121
|
return verdict
|
|
107
122
|
|
|
108
123
|
|
|
124
|
+
class _PreflightReusingAdapter:
|
|
125
|
+
"""Serve GEPA's immediate seed evaluation from the completed preflight."""
|
|
126
|
+
|
|
127
|
+
def __init__(self, adapter: Any, seed_candidate: dict[str, str], rows: list[Any], evaluation: Any):
|
|
128
|
+
self._adapter = adapter
|
|
129
|
+
self._seed_candidate = seed_candidate
|
|
130
|
+
self._rows = rows
|
|
131
|
+
self._evaluation = evaluation
|
|
132
|
+
self._served_seed = False
|
|
133
|
+
|
|
134
|
+
def evaluate(self, batch: list[Any], candidate: dict[str, str], capture_traces: bool = False):
|
|
135
|
+
if (not self._served_seed and not capture_traces and candidate == self._seed_candidate
|
|
136
|
+
and list(batch) == self._rows):
|
|
137
|
+
self._served_seed = True
|
|
138
|
+
return self._evaluation
|
|
139
|
+
return self._adapter.evaluate(batch, candidate, capture_traces=capture_traces)
|
|
140
|
+
|
|
141
|
+
def __getattr__(self, name: str) -> Any:
|
|
142
|
+
return getattr(self._adapter, name)
|
|
143
|
+
|
|
144
|
+
|
|
109
145
|
def run_official_gepa(
|
|
110
146
|
*,
|
|
111
147
|
seed_candidate: dict[str, str],
|
|
112
148
|
trainset: list[Any],
|
|
113
149
|
valset: list[Any],
|
|
114
150
|
adapter: Any,
|
|
115
|
-
callback:
|
|
151
|
+
callback: Any,
|
|
116
152
|
hooks: LifecycleHooks,
|
|
117
153
|
max_metric_calls: int | None = None,
|
|
118
154
|
stop_callbacks: Sequence[Callable[[Any], bool]] = (),
|
|
119
155
|
allow_degenerate_seed: bool = False,
|
|
120
156
|
reflection_lm: Callable[[str], str] | None = None,
|
|
157
|
+
custom_candidate_proposer: Callable[..., dict[str, str]] | None = None,
|
|
158
|
+
proposal_budget: Any | None = None,
|
|
121
159
|
seed_already_validated: bool = False,
|
|
122
160
|
config: TextGepaConfig | None = None,
|
|
161
|
+
module_selector: str = "round_robin",
|
|
123
162
|
) -> Any:
|
|
124
163
|
"""Run GEPA after the launch-time seed check and emit named lifecycle hooks.
|
|
125
164
|
|
|
@@ -135,8 +174,10 @@ def run_official_gepa(
|
|
|
135
174
|
callback.evaluation_adapter = adapter
|
|
136
175
|
callback.validation_row_ids = [_row_id(row, index) for index, row in enumerate(valset)]
|
|
137
176
|
|
|
177
|
+
preflight_evaluation = None
|
|
178
|
+
preflight_rows: list[Any] = []
|
|
138
179
|
if not seed_already_validated:
|
|
139
|
-
|
|
180
|
+
_verdict, preflight_evaluation, preflight_rows = _validate_seed_before_run(
|
|
140
181
|
seed_candidate=seed_candidate,
|
|
141
182
|
valset=valset,
|
|
142
183
|
adapter=adapter,
|
|
@@ -144,6 +185,27 @@ def run_official_gepa(
|
|
|
144
185
|
allow_degenerate_seed=allow_degenerate_seed,
|
|
145
186
|
)
|
|
146
187
|
|
|
188
|
+
# GEPA 0.1.4 reads this optional protocol attribute directly in its
|
|
189
|
+
# reflective proposer, even when a custom proposer owns the call. Our
|
|
190
|
+
# runner adapter supplies it; lightweight adapters used by the real GEPA
|
|
191
|
+
# boundary do not. Normalize the vendor's optional slot without replacing
|
|
192
|
+
# an adapter-owned proposer.
|
|
193
|
+
if custom_candidate_proposer is not None and not hasattr(adapter, "propose_new_texts"):
|
|
194
|
+
adapter.propose_new_texts = None
|
|
195
|
+
|
|
196
|
+
gepa_adapter = adapter
|
|
197
|
+
if custom_candidate_proposer is not None and preflight_evaluation is not None:
|
|
198
|
+
# A one-row low metric ceiling otherwise pays for this same seed row a
|
|
199
|
+
# second time inside GEPA before it reaches the custom proposal. The
|
|
200
|
+
# cached result is already a real evaluator result from this call's
|
|
201
|
+
# mandatory preflight; only the duplicate evaluator invocation is
|
|
202
|
+
# removed, never the separate proposal ledger.
|
|
203
|
+
gepa_adapter = _PreflightReusingAdapter(adapter, seed_candidate, preflight_rows, preflight_evaluation)
|
|
204
|
+
if custom_candidate_proposer is not None:
|
|
205
|
+
# GEPA refuses an adapter-owned proposer alongside its explicit custom
|
|
206
|
+
# proposer. The latter is the selected skilled-proposer path.
|
|
207
|
+
gepa_adapter.propose_new_texts = None
|
|
208
|
+
|
|
147
209
|
config = config or TextGepaConfig()
|
|
148
210
|
candidate_selection_strategy: Any = config.candidate_selection_strategy
|
|
149
211
|
# Official GEPA's string shortcut fixes epsilon at 0.1. Use its public
|
|
@@ -154,24 +216,54 @@ def run_official_gepa(
|
|
|
154
216
|
rng=random.Random(config.seed),
|
|
155
217
|
)
|
|
156
218
|
|
|
219
|
+
owned_stop_callbacks = list(stop_callbacks)
|
|
220
|
+
if proposal_budget is not None:
|
|
221
|
+
owned_stop_callbacks.append(ProposalBudgetStopper(proposal_budget))
|
|
222
|
+
|
|
223
|
+
reflection_minibatch_size = config.minibatch_size
|
|
224
|
+
if custom_candidate_proposer is not None and max_metric_calls is not None:
|
|
225
|
+
# GEPA's metric stopper is evaluated at iteration boundaries. Do not
|
|
226
|
+
# let its default three-row reflection batch overshoot a deliberately
|
|
227
|
+
# smaller evaluator-only ceiling before that boundary is observed.
|
|
228
|
+
reflection_minibatch_size = min(
|
|
229
|
+
reflection_minibatch_size,
|
|
230
|
+
max(1, max_metric_calls - len(valset)),
|
|
231
|
+
)
|
|
232
|
+
|
|
157
233
|
kwargs: dict[str, Any] = {
|
|
158
234
|
"seed_candidate": seed_candidate,
|
|
159
235
|
"trainset": trainset,
|
|
160
236
|
"valset": valset,
|
|
161
|
-
"adapter":
|
|
237
|
+
"adapter": gepa_adapter,
|
|
162
238
|
"callbacks": [callback],
|
|
163
|
-
"stop_callbacks":
|
|
239
|
+
"stop_callbacks": owned_stop_callbacks or None,
|
|
164
240
|
"max_metric_calls": max_metric_calls,
|
|
165
241
|
"candidate_selection_strategy": candidate_selection_strategy,
|
|
166
242
|
"skip_perfect_score": config.skip_perfect_parent_reflection,
|
|
167
|
-
"reflection_minibatch_size":
|
|
243
|
+
"reflection_minibatch_size": reflection_minibatch_size,
|
|
168
244
|
"cache_evaluation": config.cache_evaluations,
|
|
169
245
|
"seed": config.seed,
|
|
170
246
|
"display_progress_bar": False,
|
|
171
247
|
"raise_on_exception": True,
|
|
248
|
+
"module_selector": module_selector,
|
|
172
249
|
}
|
|
173
250
|
if reflection_lm is not None:
|
|
174
251
|
kwargs["reflection_lm"] = reflection_lm
|
|
252
|
+
# The connector implements GEPA's component-aware strategy protocol;
|
|
253
|
+
# passing it as a bare callable would discard the selected component.
|
|
254
|
+
kwargs["reflection_strategy"] = reflection_lm
|
|
255
|
+
if custom_candidate_proposer is not None:
|
|
256
|
+
kwargs["custom_candidate_proposer"] = custom_candidate_proposer
|
|
175
257
|
kwargs["trainset"] = _with_row_identities(trainset)
|
|
176
258
|
kwargs["valset"] = _with_row_identities(valset)
|
|
177
|
-
|
|
259
|
+
result = optimize(**kwargs)
|
|
260
|
+
if custom_candidate_proposer is not None and preflight_evaluation is not None:
|
|
261
|
+
# GEPA's state correctly counts its seed evaluation, but this run
|
|
262
|
+
# served that evaluation from the already-accounted launch preflight.
|
|
263
|
+
# Its public metric result must consequently retain M1's convention:
|
|
264
|
+
# GEPA result counts exclude the one caller preflight, while the
|
|
265
|
+
# adapter's physical count is result + one.
|
|
266
|
+
total = getattr(result, "total_metric_calls", None)
|
|
267
|
+
if isinstance(total, int):
|
|
268
|
+
result = dataclasses.replace(result, total_metric_calls=max(0, total - len(preflight_rows)))
|
|
269
|
+
return result
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"""Offline reader for the ALI-1532 synced instruction-set layout."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class InstructionSetLoaderError(Exception):
|
|
8
|
+
def __init__(self, code: str):
|
|
9
|
+
self.code = code
|
|
10
|
+
super().__init__(code)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _inside(root: Path, candidate: Path) -> bool:
|
|
14
|
+
try:
|
|
15
|
+
candidate.relative_to(root)
|
|
16
|
+
return True
|
|
17
|
+
except ValueError:
|
|
18
|
+
return False
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def load_instruction_set(directory: str, name: str, model_config_identity: str) -> dict[str, str]:
|
|
22
|
+
root = (Path(directory) / name).resolve()
|
|
23
|
+
if not root.is_dir():
|
|
24
|
+
raise InstructionSetLoaderError("instruction_set_not_synced")
|
|
25
|
+
manifest_path = root / "manifest.json"
|
|
26
|
+
if not manifest_path.is_file():
|
|
27
|
+
raise InstructionSetLoaderError("instruction_set_manifest_missing")
|
|
28
|
+
try:
|
|
29
|
+
manifest = json.loads(manifest_path.read_text())
|
|
30
|
+
except (OSError, json.JSONDecodeError) as error:
|
|
31
|
+
raise InstructionSetLoaderError("instruction_set_manifest_missing") from error
|
|
32
|
+
if manifest.get("manifestVersion") != 1:
|
|
33
|
+
raise InstructionSetLoaderError("instruction_set_manifest_version_unsupported")
|
|
34
|
+
if manifest.get("filteredTo") and model_config_identity not in manifest["filteredTo"]:
|
|
35
|
+
raise InstructionSetLoaderError("instruction_set_profile_not_synced")
|
|
36
|
+
production = next((profile.get("production") for profile in manifest.get("profiles", []) if profile.get("modelConfigIdentity") == model_config_identity), None)
|
|
37
|
+
material = production or manifest.get("default", {})
|
|
38
|
+
values: dict[str, str] = {}
|
|
39
|
+
for key in manifest.get("shape", []):
|
|
40
|
+
if key in material.get("pinnedComponents", {}):
|
|
41
|
+
raise InstructionSetLoaderError("instruction_set_component_unavailable")
|
|
42
|
+
relative = material.get("files", {}).get(key)
|
|
43
|
+
if not relative:
|
|
44
|
+
raise InstructionSetLoaderError("instruction_set_profile_key_missing")
|
|
45
|
+
path = (root / relative).resolve()
|
|
46
|
+
if not _inside(root, path):
|
|
47
|
+
raise InstructionSetLoaderError("instruction_set_path_unsafe")
|
|
48
|
+
try:
|
|
49
|
+
values[key] = path.read_text()
|
|
50
|
+
except OSError as error:
|
|
51
|
+
raise InstructionSetLoaderError("instruction_set_component_unreadable") from error
|
|
52
|
+
return values
|
|
@@ -3,9 +3,11 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
from collections.abc import Callable
|
|
6
|
+
from dataclasses import replace
|
|
7
|
+
import json
|
|
6
8
|
from typing import Any
|
|
7
9
|
|
|
8
|
-
from orizu_gepa.optimizer import build_reflection_prompt
|
|
10
|
+
from orizu_gepa.optimizer import DEFAULT_REFLECTION_PROMPT_TEMPLATE, build_reflection_prompt
|
|
9
11
|
from orizu_gepa.reflection import reflect_with_provider
|
|
10
12
|
|
|
11
13
|
|
|
@@ -26,16 +28,14 @@ class GepaReflectionLM:
|
|
|
26
28
|
GEPA intentionally catches reflection failures per proposal. That keeps an
|
|
27
29
|
optimization alive, but means the connector is the final durable boundary
|
|
28
30
|
for the actual exception. This wrapper records it *before* re-raising to
|
|
29
|
-
GEPA's safe helper and exposes
|
|
30
|
-
``TrackingLM
|
|
31
|
+
GEPA's safe helper and exposes provider-measured token usage in GEPA's
|
|
32
|
+
``TrackingLM``-shaped fields.
|
|
31
33
|
"""
|
|
32
34
|
|
|
33
|
-
_CHARS_PER_TOKEN = 4
|
|
34
|
-
|
|
35
35
|
def __init__(
|
|
36
36
|
self,
|
|
37
37
|
*,
|
|
38
|
-
context_supplier: Callable[[], tuple[str, list[Any]]],
|
|
38
|
+
context_supplier: Callable[[], tuple[str | dict[str, str], list[Any]]],
|
|
39
39
|
config: Any,
|
|
40
40
|
failure_reporter: Callable[..., None] | None,
|
|
41
41
|
success_reporter: Callable[[str], None] | None,
|
|
@@ -46,6 +46,7 @@ class GepaReflectionLM:
|
|
|
46
46
|
self._success_reporter = success_reporter
|
|
47
47
|
self._total_tokens_in = 0
|
|
48
48
|
self._total_tokens_out = 0
|
|
49
|
+
self._total_tokens = 0
|
|
49
50
|
|
|
50
51
|
@property
|
|
51
52
|
def total_cost(self) -> float:
|
|
@@ -59,15 +60,46 @@ class GepaReflectionLM:
|
|
|
59
60
|
def total_tokens_out(self) -> int:
|
|
60
61
|
return self._total_tokens_out
|
|
61
62
|
|
|
63
|
+
@property
|
|
64
|
+
def total_tokens(self) -> int:
|
|
65
|
+
return self._total_tokens
|
|
66
|
+
|
|
67
|
+
def _account_usage(self, usage: Any) -> None:
|
|
68
|
+
if not isinstance(usage, dict):
|
|
69
|
+
raise RuntimeError("ALI_1505_PROVIDER_USAGE_MISSING: reflection transport returned no provider usage")
|
|
70
|
+
try:
|
|
71
|
+
input_tokens = int(usage["input_tokens"])
|
|
72
|
+
output_tokens = int(usage["output_tokens"])
|
|
73
|
+
total_tokens = int(usage["total_tokens"])
|
|
74
|
+
except (KeyError, TypeError, ValueError) as error:
|
|
75
|
+
raise RuntimeError("ALI_1505_PROVIDER_USAGE_INVALID: reflection transport returned invalid provider usage") from error
|
|
76
|
+
if min(input_tokens, output_tokens, total_tokens) < 0:
|
|
77
|
+
raise RuntimeError("ALI_1505_PROVIDER_USAGE_INVALID: reflection transport returned negative provider usage")
|
|
78
|
+
self._total_tokens_in += input_tokens
|
|
79
|
+
self._total_tokens_out += output_tokens
|
|
80
|
+
self._total_tokens += total_tokens
|
|
81
|
+
|
|
62
82
|
def __call__(self, gepa_prompt: str) -> str:
|
|
63
|
-
|
|
83
|
+
parent_candidate, parent_results = self._context_supplier()
|
|
84
|
+
parent_text = (
|
|
85
|
+
json.dumps(parent_candidate, sort_keys=True, separators=(",", ":"))
|
|
86
|
+
if isinstance(parent_candidate, dict) and len(parent_candidate) > 1
|
|
87
|
+
else next(iter(parent_candidate.values()))
|
|
88
|
+
if isinstance(parent_candidate, dict)
|
|
89
|
+
else parent_candidate
|
|
90
|
+
)
|
|
64
91
|
try:
|
|
65
92
|
# Prompt validation is part of the provider attempt. Keep it
|
|
66
93
|
# inside this boundary so GEPA cannot silently swallow its error.
|
|
67
94
|
provider_prompt = build_reflection_prompt(parent_text, parent_results, self._config)
|
|
68
|
-
self._total_tokens_in += max(1, len(provider_prompt) // self._CHARS_PER_TOKEN)
|
|
69
95
|
result = reflect_with_provider(parent_text, parent_results, self._config)
|
|
96
|
+
response = result.response
|
|
97
|
+
provider_prompt = getattr(result, "prompt", provider_prompt)
|
|
98
|
+
self._account_usage(getattr(result, "usage", None))
|
|
70
99
|
except Exception as error:
|
|
100
|
+
billable_usage = getattr(error, "usage", None)
|
|
101
|
+
if billable_usage is not None:
|
|
102
|
+
self._account_usage(billable_usage)
|
|
71
103
|
if self._failure_reporter is not None:
|
|
72
104
|
self._failure_reporter(
|
|
73
105
|
error=error,
|
|
@@ -76,17 +108,65 @@ class GepaReflectionLM:
|
|
|
76
108
|
parent_results=parent_results,
|
|
77
109
|
)
|
|
78
110
|
raise
|
|
79
|
-
response = result.response
|
|
80
|
-
provider_prompt = getattr(result, "prompt", provider_prompt)
|
|
81
|
-
self._total_tokens_out += max(1, len(response) // self._CHARS_PER_TOKEN)
|
|
82
111
|
if self._success_reporter is not None:
|
|
83
112
|
self._success_reporter(str(provider_prompt))
|
|
84
113
|
return response
|
|
85
114
|
|
|
115
|
+
def reflect(
|
|
116
|
+
self,
|
|
117
|
+
candidate: dict[str, str],
|
|
118
|
+
reflective_dataset: Any,
|
|
119
|
+
components_to_update: list[str],
|
|
120
|
+
) -> tuple[Any, "GepaReflectionLM"]:
|
|
121
|
+
"""Implement GEPA's component-aware ReflectionLM protocol.
|
|
122
|
+
|
|
123
|
+
The callable protocol has no selected-component information. GEPA's
|
|
124
|
+
newer protocol does, so tuples must use it rather than reverse-engineer
|
|
125
|
+
a key from a rendered vendor prompt.
|
|
126
|
+
"""
|
|
127
|
+
from gepa.proposer.reflective_mutation.reflection_lm import ReflectionProposal
|
|
128
|
+
|
|
129
|
+
_, parent_results = self._context_supplier()
|
|
130
|
+
full_candidate = candidate
|
|
131
|
+
proposal = ReflectionProposal(new_texts={}, prompts={}, raw_lm_outputs={})
|
|
132
|
+
for component in components_to_update:
|
|
133
|
+
if component not in candidate:
|
|
134
|
+
continue
|
|
135
|
+
parent_text = candidate[component]
|
|
136
|
+
others = {key: value for key, value in full_candidate.items() if key != component}
|
|
137
|
+
config = self._config
|
|
138
|
+
if others:
|
|
139
|
+
template = config.reflection_prompt_template or DEFAULT_REFLECTION_PROMPT_TEMPLATE
|
|
140
|
+
config = replace(
|
|
141
|
+
config,
|
|
142
|
+
reflection_prompt_template=(
|
|
143
|
+
f"{template}\n\nOther instruction-set components (read-only): "
|
|
144
|
+
f"{json.dumps(others, sort_keys=True, ensure_ascii=False)}"
|
|
145
|
+
),
|
|
146
|
+
)
|
|
147
|
+
provider_prompt = ""
|
|
148
|
+
try:
|
|
149
|
+
provider_prompt = build_reflection_prompt(parent_text, parent_results, config)
|
|
150
|
+
result = reflect_with_provider(parent_text, parent_results, config)
|
|
151
|
+
self._account_usage(getattr(result, "usage", None))
|
|
152
|
+
except Exception as error:
|
|
153
|
+
billable_usage = getattr(error, "usage", None)
|
|
154
|
+
if billable_usage is not None:
|
|
155
|
+
self._account_usage(billable_usage)
|
|
156
|
+
if self._failure_reporter is not None:
|
|
157
|
+
self._failure_reporter(error=error, gepa_prompt=provider_prompt, parent_text=parent_text, parent_results=parent_results)
|
|
158
|
+
raise
|
|
159
|
+
proposal.new_texts[component] = result.response
|
|
160
|
+
proposal.prompts[component] = getattr(result, "prompt", provider_prompt)
|
|
161
|
+
proposal.raw_lm_outputs[component] = result.response
|
|
162
|
+
if self._success_reporter is not None:
|
|
163
|
+
self._success_reporter(str(proposal.prompts[component]))
|
|
164
|
+
return proposal, self
|
|
165
|
+
|
|
86
166
|
|
|
87
167
|
def make_gepa_reflection_lm(
|
|
88
168
|
*,
|
|
89
|
-
context_supplier: Callable[[], tuple[str, list[Any]]],
|
|
169
|
+
context_supplier: Callable[[], tuple[str | dict[str, str], list[Any]]],
|
|
90
170
|
config: Any,
|
|
91
171
|
failure_reporter: Callable[..., None] | None = None,
|
|
92
172
|
success_reporter: Callable[[str], None] | None = None,
|