orizu 0.3.3 → 0.3.5
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 +19 -0
- package/dist/index.js +359 -20
- package/dist/preview-runtime.js +857 -0
- package/package.json +10 -1
- package/src/index.ts +448 -21
- package/src/preview-runtime.ts +976 -0
- package/vendor/orizu-gepa-python/src/orizu_gepa/cli.py +2 -0
- package/vendor/orizu-gepa-python/src/orizu_gepa/optimizer.py +22 -56
- package/vendor/orizu-gepa-python/src/orizu_gepa/reflection.py +31 -6
|
@@ -68,6 +68,7 @@ def main() -> None:
|
|
|
68
68
|
parser.add_argument("--max-full-evals", type=positive_int)
|
|
69
69
|
parser.add_argument("--reflection-model", default=TextGepaConfig.reflection_model)
|
|
70
70
|
parser.add_argument("--reflection-temperature", type=float)
|
|
71
|
+
parser.add_argument("--reflection-max-tokens", type=positive_int)
|
|
71
72
|
parser.add_argument("--reflection-prompt-template")
|
|
72
73
|
parser.add_argument("--reflection-provider-settings")
|
|
73
74
|
parser.add_argument("--objective", default=TextGepaConfig.objective)
|
|
@@ -94,6 +95,7 @@ def main() -> None:
|
|
|
94
95
|
max_full_evals=args.max_full_evals,
|
|
95
96
|
reflection_model=args.reflection_model,
|
|
96
97
|
reflection_temperature=args.reflection_temperature,
|
|
98
|
+
reflection_max_tokens=args.reflection_max_tokens,
|
|
97
99
|
reflection_prompt_template=read_text_arg(args.reflection_prompt_template),
|
|
98
100
|
reflection_provider_settings=read_json_object_arg(args.reflection_provider_settings, "--reflection-provider-settings") or {},
|
|
99
101
|
objective=args.objective,
|
|
@@ -179,7 +179,7 @@ class TextGepaConfig:
|
|
|
179
179
|
max_candidate_proposals: int | None = None
|
|
180
180
|
reflection_model: str = "anthropic/claude-opus-4-7"
|
|
181
181
|
reflection_temperature: float | None = None
|
|
182
|
-
reflection_max_tokens: int =
|
|
182
|
+
reflection_max_tokens: int | None = None
|
|
183
183
|
reflection_prompt_template: str | None = None
|
|
184
184
|
reflection_provider_settings: dict[str, Any] = field(default_factory=dict)
|
|
185
185
|
objective: str = "Improve this text candidate to maximize evaluator score while preserving intended behavior."
|
|
@@ -420,37 +420,12 @@ def _aggregate_scores(
|
|
|
420
420
|
}
|
|
421
421
|
|
|
422
422
|
|
|
423
|
-
def
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
candidate_id
|
|
430
|
-
for candidate_ids in frontier_by_row_id.values()
|
|
431
|
-
for candidate_id in candidate_ids
|
|
432
|
-
}
|
|
433
|
-
for candidate_id in sorted(
|
|
434
|
-
remaining,
|
|
435
|
-
key=lambda value: aggregate_scores.get(value, 0.0),
|
|
436
|
-
reverse=not higher_is_better,
|
|
437
|
-
):
|
|
438
|
-
owned_rows = [
|
|
439
|
-
row_id
|
|
440
|
-
for row_id, candidate_ids in frontier_by_row_id.items()
|
|
441
|
-
if candidate_id in candidate_ids
|
|
442
|
-
]
|
|
443
|
-
if owned_rows and all(
|
|
444
|
-
any(other_id != candidate_id and other_id in remaining for other_id in frontier_by_row_id[row_id])
|
|
445
|
-
for row_id in owned_rows
|
|
446
|
-
):
|
|
447
|
-
remaining.discard(candidate_id)
|
|
448
|
-
|
|
449
|
-
return {
|
|
450
|
-
row_id: {candidate_id for candidate_id in candidate_ids if candidate_id in remaining}
|
|
451
|
-
for row_id, candidate_ids in frontier_by_row_id.items()
|
|
452
|
-
if any(candidate_id in remaining for candidate_id in candidate_ids)
|
|
453
|
-
}
|
|
423
|
+
def _frontier_counts(frontier_by_row_id: dict[str, set[str]]) -> dict[str, int]:
|
|
424
|
+
counts: dict[str, int] = {}
|
|
425
|
+
for candidate_ids in frontier_by_row_id.values():
|
|
426
|
+
for candidate_id in candidate_ids:
|
|
427
|
+
counts[candidate_id] = counts.get(candidate_id, 0) + 1
|
|
428
|
+
return counts
|
|
454
429
|
|
|
455
430
|
|
|
456
431
|
def _select_parent_candidate_id(
|
|
@@ -479,15 +454,7 @@ def _select_parent_candidate_id(
|
|
|
479
454
|
"candidate_selection_strategy must be one of: pareto, current_best, epsilon_greedy"
|
|
480
455
|
)
|
|
481
456
|
|
|
482
|
-
|
|
483
|
-
_frontier_by_row_id(val_scores_by_candidate, higher_is_better),
|
|
484
|
-
_aggregate_scores(val_scores_by_candidate),
|
|
485
|
-
higher_is_better,
|
|
486
|
-
)
|
|
487
|
-
frontier_counts: dict[str, int] = {}
|
|
488
|
-
for candidate_ids in frontier.values():
|
|
489
|
-
for candidate_id in candidate_ids:
|
|
490
|
-
frontier_counts[candidate_id] = frontier_counts.get(candidate_id, 0) + 1
|
|
457
|
+
frontier_counts = _frontier_counts(_frontier_by_row_id(val_scores_by_candidate, higher_is_better))
|
|
491
458
|
sampling_list: list[str] = []
|
|
492
459
|
for candidate_id, count in sorted(frontier_counts.items()):
|
|
493
460
|
sampling_list.extend([candidate_id] * count)
|
|
@@ -505,15 +472,7 @@ def _parent_selection_payload(
|
|
|
505
472
|
higher_is_better: bool = True,
|
|
506
473
|
) -> dict[str, Any]:
|
|
507
474
|
aggregate_scores = _aggregate_scores(val_scores_by_candidate)
|
|
508
|
-
|
|
509
|
-
_frontier_by_row_id(val_scores_by_candidate, higher_is_better),
|
|
510
|
-
aggregate_scores,
|
|
511
|
-
higher_is_better,
|
|
512
|
-
)
|
|
513
|
-
frontier_counts: dict[str, int] = {}
|
|
514
|
-
for candidate_ids in frontier.values():
|
|
515
|
-
for candidate_id in candidate_ids:
|
|
516
|
-
frontier_counts[candidate_id] = frontier_counts.get(candidate_id, 0) + 1
|
|
475
|
+
frontier_counts = _frontier_counts(_frontier_by_row_id(val_scores_by_candidate, higher_is_better))
|
|
517
476
|
return {
|
|
518
477
|
"strategy": config.candidate_selection_strategy,
|
|
519
478
|
"selected_candidate_id": selected_candidate_id,
|
|
@@ -541,16 +500,23 @@ def _pareto_payload(
|
|
|
541
500
|
}
|
|
542
501
|
|
|
543
502
|
aggregate_scores = _aggregate_scores(val_scores_by_candidate)
|
|
544
|
-
|
|
503
|
+
frontier_counts = _frontier_counts(raw_frontier)
|
|
504
|
+
if frontier_counts:
|
|
505
|
+
best_candidate_id = max(
|
|
506
|
+
sorted(frontier_counts),
|
|
507
|
+
key=lambda candidate_id: (
|
|
508
|
+
frontier_counts[candidate_id],
|
|
509
|
+
aggregate_scores.get(candidate_id, 0.0) if higher_is_better else -aggregate_scores.get(candidate_id, 0.0),
|
|
510
|
+
),
|
|
511
|
+
)
|
|
512
|
+
else:
|
|
513
|
+
best_candidate_id = max(aggregate_scores, key=aggregate_scores.get) if higher_is_better else min(aggregate_scores, key=aggregate_scores.get)
|
|
545
514
|
best_score = aggregate_scores[best_candidate_id]
|
|
546
515
|
return best_candidate_id, best_score, {
|
|
547
516
|
"frontier_by_row_id": frontier_by_row_id,
|
|
548
|
-
"frontier_candidate_ids": sorted(
|
|
549
|
-
candidate_id
|
|
550
|
-
for candidate_ids in raw_frontier.values()
|
|
551
|
-
for candidate_id in candidate_ids
|
|
552
|
-
}),
|
|
517
|
+
"frontier_candidate_ids": sorted(frontier_counts),
|
|
553
518
|
"candidate_scores": aggregate_scores,
|
|
519
|
+
"frontier_counts": frontier_counts,
|
|
554
520
|
"best_candidate_id": best_candidate_id,
|
|
555
521
|
"best_score_mean": best_score,
|
|
556
522
|
"best_candidate_preview": val_text_by_candidate[best_candidate_id][:1200],
|
|
@@ -62,12 +62,30 @@ def _add_temperature(payload: dict[str, Any], config: TextGepaConfig) -> dict[st
|
|
|
62
62
|
return {**payload, "temperature": config.reflection_temperature}
|
|
63
63
|
|
|
64
64
|
|
|
65
|
+
def _validate_reflection_max_tokens(value: int | None) -> None:
|
|
66
|
+
if value is not None and value <= 0:
|
|
67
|
+
raise RuntimeError("reflection_max_tokens must be positive")
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _require_anthropic_reflection_max_tokens(value: int | None) -> int:
|
|
71
|
+
_validate_reflection_max_tokens(value)
|
|
72
|
+
if value is None:
|
|
73
|
+
raise RuntimeError("reflection_max_tokens is required for Anthropic reflection models")
|
|
74
|
+
return value
|
|
75
|
+
|
|
76
|
+
|
|
65
77
|
def build_anthropic_reflection_payload(model: str, prompt: str, config: TextGepaConfig) -> dict[str, Any]:
|
|
66
|
-
|
|
78
|
+
max_tokens = _require_anthropic_reflection_max_tokens(config.reflection_max_tokens)
|
|
79
|
+
base_payload: dict[str, Any] = {
|
|
67
80
|
"model": model,
|
|
68
|
-
"max_tokens":
|
|
81
|
+
"max_tokens": max_tokens,
|
|
69
82
|
"messages": [{"role": "user", "content": prompt}],
|
|
70
|
-
}
|
|
83
|
+
}
|
|
84
|
+
payload = _merge_provider_settings(
|
|
85
|
+
base_payload,
|
|
86
|
+
config.reflection_provider_settings,
|
|
87
|
+
reserved_keys={"model", "max_tokens", "messages"},
|
|
88
|
+
)
|
|
71
89
|
payload = _add_temperature(payload, config)
|
|
72
90
|
if "temperature" in payload and "thinking" in payload:
|
|
73
91
|
raise RuntimeError("reflection_temperature cannot be combined with Anthropic thinking")
|
|
@@ -75,11 +93,18 @@ def build_anthropic_reflection_payload(model: str, prompt: str, config: TextGepa
|
|
|
75
93
|
|
|
76
94
|
|
|
77
95
|
def build_openai_reflection_payload(model: str, prompt: str, config: TextGepaConfig) -> dict[str, Any]:
|
|
78
|
-
|
|
96
|
+
_validate_reflection_max_tokens(config.reflection_max_tokens)
|
|
97
|
+
base_payload: dict[str, Any] = {
|
|
79
98
|
"model": model,
|
|
80
99
|
"input": [{"role": "user", "content": prompt}],
|
|
81
|
-
|
|
82
|
-
|
|
100
|
+
}
|
|
101
|
+
if config.reflection_max_tokens is not None:
|
|
102
|
+
base_payload["max_output_tokens"] = config.reflection_max_tokens
|
|
103
|
+
payload = _merge_provider_settings(
|
|
104
|
+
base_payload,
|
|
105
|
+
config.reflection_provider_settings,
|
|
106
|
+
reserved_keys={"model", "input", "max_output_tokens"},
|
|
107
|
+
)
|
|
83
108
|
return _add_temperature(payload, config)
|
|
84
109
|
|
|
85
110
|
|