orizu 0.3.6 → 0.4.0
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 +55 -6
- package/dist/banner.js +20 -0
- package/dist/csv.js +117 -9
- package/dist/help.js +109 -10
- package/dist/index.js +736 -44
- package/dist/setup-prompt.js +22 -0
- package/dist/skill-installer.js +243 -17
- package/dist/workspace.js +76 -0
- package/package.json +5 -1
- package/src/banner.ts +21 -0
- package/src/csv.ts +140 -10
- package/src/help.ts +109 -10
- package/src/index.ts +815 -46
- package/src/setup-prompt.ts +27 -0
- package/src/skill-installer.ts +343 -17
- package/src/workspace.ts +112 -0
- package/vendor/orizu-gepa-python/src/orizu_gepa/__init__.py +6 -0
- package/vendor/orizu-gepa-python/src/orizu_gepa/cli.py +66 -3
- package/vendor/orizu-gepa-python/src/orizu_gepa/client.py +37 -1
- package/vendor/orizu-gepa-python/src/orizu_gepa/optimizer.py +645 -27
- package/vendor/orizu-gepa-python/src/orizu_gepa/reflection.py +98 -17
- package/vendor/skills/orizu-cli/SKILL.md +8 -2
- package/vendor/skills/orizu-cli/references/cli-reference.md +25 -3
- package/vendor/skills/orizu-cli/references/optimization-reports.md +3 -3
- package/vendor/skills/orizu-cli/references/optimization-with-gepa.md +1 -0
- package/vendor/skills/orizu-cli/references/prompt-control-plane.md +19 -5
|
@@ -2,6 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import argparse
|
|
4
4
|
import json
|
|
5
|
+
import sys
|
|
5
6
|
from pathlib import Path
|
|
6
7
|
from typing import Any
|
|
7
8
|
|
|
@@ -11,6 +12,8 @@ from .optimizer import TextGepaConfig, optimize_loaded_text_candidate
|
|
|
11
12
|
from .reflection import reflect_with_provider
|
|
12
13
|
from .runner import make_candidate_runner, make_scorer_runner
|
|
13
14
|
|
|
15
|
+
BUDGET_OPTION_FLAGS = ("--budget", "--max-metric-calls", "--max-full-evals", "--max-iterations")
|
|
16
|
+
|
|
14
17
|
|
|
15
18
|
def positive_int(value: str) -> int:
|
|
16
19
|
parsed = int(value)
|
|
@@ -19,6 +22,12 @@ def positive_int(value: str) -> int:
|
|
|
19
22
|
return parsed
|
|
20
23
|
|
|
21
24
|
|
|
25
|
+
def num_threads_arg(value: str) -> int | str:
|
|
26
|
+
if value.strip().lower() == "auto":
|
|
27
|
+
return "auto"
|
|
28
|
+
return positive_int(value)
|
|
29
|
+
|
|
30
|
+
|
|
22
31
|
def read_text_arg(value: str | None) -> str | None:
|
|
23
32
|
if value is None:
|
|
24
33
|
return None
|
|
@@ -40,6 +49,38 @@ def read_json_object_arg(value: str | None, flag_name: str) -> dict[str, Any] |
|
|
|
40
49
|
return parsed
|
|
41
50
|
|
|
42
51
|
|
|
52
|
+
def _flag_occurrences(raw_args: list[str], flag: str) -> int:
|
|
53
|
+
return sum(1 for arg in raw_args if arg == flag or arg.startswith(f"{flag}="))
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _format_budget_option(flag: str, count: int) -> str:
|
|
57
|
+
return flag if count == 1 else f"{flag} ({count} times)"
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def apply_budget_defaults(args: argparse.Namespace, raw_args: list[str]) -> None:
|
|
61
|
+
occurrences = {
|
|
62
|
+
flag: _flag_occurrences(raw_args, flag)
|
|
63
|
+
for flag in BUDGET_OPTION_FLAGS
|
|
64
|
+
}
|
|
65
|
+
provided = [
|
|
66
|
+
_format_budget_option(flag, count)
|
|
67
|
+
for flag, count in occurrences.items()
|
|
68
|
+
if count > 0
|
|
69
|
+
]
|
|
70
|
+
total = sum(occurrences.values())
|
|
71
|
+
|
|
72
|
+
if total > 1:
|
|
73
|
+
allowed = ", ".join(BUDGET_OPTION_FLAGS)
|
|
74
|
+
received = ", ".join(provided)
|
|
75
|
+
raise ValueError(
|
|
76
|
+
f"Budget options are mutually exclusive; choose at most one of {allowed}. "
|
|
77
|
+
f"Received: {received}."
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
if total == 0:
|
|
81
|
+
args.budget = TextGepaConfig.budget
|
|
82
|
+
|
|
83
|
+
|
|
43
84
|
def main() -> None:
|
|
44
85
|
parser = argparse.ArgumentParser(prog="orizu-gepa")
|
|
45
86
|
parser.add_argument("--project", required=True)
|
|
@@ -54,9 +95,10 @@ def main() -> None:
|
|
|
54
95
|
parser.add_argument("--split-set-id", required=True)
|
|
55
96
|
parser.add_argument("--train-split", default="train")
|
|
56
97
|
parser.add_argument("--val-split", default="validation")
|
|
57
|
-
parser.add_argument("--budget",
|
|
58
|
-
parser.add_argument("--max-iterations", type=positive_int
|
|
98
|
+
parser.add_argument("--budget", choices=["auto", "light", "medium", "heavy"])
|
|
99
|
+
parser.add_argument("--max-iterations", type=positive_int)
|
|
59
100
|
parser.add_argument("--minibatch-size", type=positive_int, default=3)
|
|
101
|
+
parser.add_argument("--num-threads", type=num_threads_arg, default=TextGepaConfig.num_threads)
|
|
60
102
|
parser.add_argument(
|
|
61
103
|
"--candidate-selection-strategy",
|
|
62
104
|
default=TextGepaConfig.candidate_selection_strategy,
|
|
@@ -69,6 +111,18 @@ def main() -> None:
|
|
|
69
111
|
parser.add_argument("--reflection-model", default=TextGepaConfig.reflection_model)
|
|
70
112
|
parser.add_argument("--reflection-temperature", type=float)
|
|
71
113
|
parser.add_argument("--reflection-max-tokens", type=positive_int)
|
|
114
|
+
parser.add_argument(
|
|
115
|
+
"--reflection-retry-attempts",
|
|
116
|
+
type=positive_int,
|
|
117
|
+
default=TextGepaConfig.reflection_retry_attempts,
|
|
118
|
+
help="Total attempts for retryable reflection HTTP failures.",
|
|
119
|
+
)
|
|
120
|
+
parser.add_argument(
|
|
121
|
+
"--reflection-http-timeout-seconds",
|
|
122
|
+
type=positive_int,
|
|
123
|
+
default=TextGepaConfig.reflection_http_timeout_seconds,
|
|
124
|
+
help="Per-attempt timeout for reflection HTTP calls.",
|
|
125
|
+
)
|
|
72
126
|
parser.add_argument("--reflection-prompt-template")
|
|
73
127
|
parser.add_argument("--reflection-provider-settings")
|
|
74
128
|
parser.add_argument(
|
|
@@ -92,7 +146,12 @@ def main() -> None:
|
|
|
92
146
|
parser.add_argument("--log-dir", default="logs")
|
|
93
147
|
parser.add_argument("--no-local-log", action="store_true")
|
|
94
148
|
parser.add_argument("--metadata", default="{}")
|
|
95
|
-
|
|
149
|
+
raw_args = sys.argv[1:]
|
|
150
|
+
args = parser.parse_args(raw_args)
|
|
151
|
+
try:
|
|
152
|
+
apply_budget_defaults(args, raw_args)
|
|
153
|
+
except ValueError as error:
|
|
154
|
+
parser.error(str(error))
|
|
96
155
|
|
|
97
156
|
metadata = json.loads(args.metadata)
|
|
98
157
|
if not isinstance(metadata, dict):
|
|
@@ -102,6 +161,7 @@ def main() -> None:
|
|
|
102
161
|
budget=args.budget,
|
|
103
162
|
max_iterations=args.max_iterations,
|
|
104
163
|
minibatch_size=args.minibatch_size,
|
|
164
|
+
num_threads=args.num_threads,
|
|
105
165
|
candidate_selection_strategy=args.candidate_selection_strategy,
|
|
106
166
|
epsilon=args.epsilon,
|
|
107
167
|
max_metric_calls=args.max_metric_calls,
|
|
@@ -109,6 +169,8 @@ def main() -> None:
|
|
|
109
169
|
reflection_model=args.reflection_model,
|
|
110
170
|
reflection_temperature=args.reflection_temperature,
|
|
111
171
|
reflection_max_tokens=args.reflection_max_tokens,
|
|
172
|
+
reflection_retry_attempts=args.reflection_retry_attempts,
|
|
173
|
+
reflection_http_timeout_seconds=args.reflection_http_timeout_seconds,
|
|
112
174
|
reflection_prompt_template=read_text_arg(args.reflection_prompt_template),
|
|
113
175
|
reflection_provider_settings=read_json_object_arg(args.reflection_provider_settings, "--reflection-provider-settings") or {},
|
|
114
176
|
skip_perfect_parent_reflection=args.skip_perfect_parent_reflection,
|
|
@@ -153,6 +215,7 @@ def main() -> None:
|
|
|
153
215
|
"dataset_size": len(trainset) + len(valset),
|
|
154
216
|
"train_count": len(trainset),
|
|
155
217
|
"validation_count": len(valset),
|
|
218
|
+
"num_threads": args.num_threads,
|
|
156
219
|
}
|
|
157
220
|
|
|
158
221
|
run_id = client.start_run(
|
|
@@ -12,6 +12,39 @@ from typing import Any
|
|
|
12
12
|
from .optimizer import DatasetRow, PromptContext
|
|
13
13
|
|
|
14
14
|
|
|
15
|
+
def _format_number(value: Any) -> str | None:
|
|
16
|
+
if isinstance(value, bool):
|
|
17
|
+
return None
|
|
18
|
+
if isinstance(value, int):
|
|
19
|
+
return str(value)
|
|
20
|
+
if isinstance(value, float):
|
|
21
|
+
return f"{value:g}"
|
|
22
|
+
return None
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _progress_log_suffix(event_type: str, payload: dict[str, Any] | None) -> str:
|
|
26
|
+
if event_type != "optimization_progress" or not isinstance(payload, dict):
|
|
27
|
+
return ""
|
|
28
|
+
|
|
29
|
+
percent_value = payload.get("percent")
|
|
30
|
+
if percent_value is None:
|
|
31
|
+
percent_value = payload.get("progress_percent")
|
|
32
|
+
percent = _format_number(percent_value)
|
|
33
|
+
metric_calls_remaining = _format_number(payload.get("metric_calls_remaining"))
|
|
34
|
+
metric_call_budget_value = payload.get("metric_call_budget")
|
|
35
|
+
if metric_call_budget_value is None:
|
|
36
|
+
metric_call_budget_value = payload.get("approx_metric_call_budget")
|
|
37
|
+
metric_call_budget = _format_number(metric_call_budget_value)
|
|
38
|
+
parts: list[str] = []
|
|
39
|
+
if percent is not None:
|
|
40
|
+
parts.append(f"{percent}%")
|
|
41
|
+
if metric_calls_remaining is not None:
|
|
42
|
+
budget_suffix = f" / {metric_call_budget}" if metric_call_budget is not None else ""
|
|
43
|
+
parts.append(f"{metric_calls_remaining}{budget_suffix} metric calls left")
|
|
44
|
+
|
|
45
|
+
return f" {'; '.join(parts)}" if parts else ""
|
|
46
|
+
|
|
47
|
+
|
|
15
48
|
@dataclass(frozen=True)
|
|
16
49
|
class OrizuClient:
|
|
17
50
|
api_url: str
|
|
@@ -306,7 +339,10 @@ class OrizuEventSink:
|
|
|
306
339
|
parent_candidate_id=parent_candidate_id,
|
|
307
340
|
child_candidate_id=child_candidate_id,
|
|
308
341
|
)
|
|
309
|
-
print(
|
|
342
|
+
print(
|
|
343
|
+
f"[orizu-gepa] {self.sequence:03d} {event_type}{_progress_log_suffix(event_type, payload)}",
|
|
344
|
+
flush=True,
|
|
345
|
+
)
|
|
310
346
|
return
|
|
311
347
|
except Exception as exc:
|
|
312
348
|
last_error = exc
|