Skip to content

Commit 227827c

Browse files
committed
Harden SDK audit reproducibility
1 parent 46b0774 commit 227827c

5 files changed

Lines changed: 135 additions & 31 deletions

File tree

examples/optimization/eval_optimize_loop/README.md

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,10 @@ python examples/optimization/eval_optimize_loop/run_pipeline.py \
8484

8585
`--sdk-call-agent` must point to an async callable compatible with
8686
`AgentOptimizer.optimize(call_agent=...)`. Configure any real model credentials
87-
needed by that callable. SDK mode never silently falls back to fake mode.
87+
needed by that callable. SDK mode never silently falls back to fake mode. The
88+
generated reproducibility command records the actual `--sdk-call-agent`
89+
`module:function` target and file/config paths, but it does not record API keys
90+
or other provider secrets.
8891

8992
SDK optimizer config and wrapper gate config are intentionally separate.
9093
`--optimizer-config` is passed unchanged to `AgentOptimizer.optimize`, so it must
@@ -113,9 +116,10 @@ exposes full per-case validation scores; they are listed in
113116

114117
Fake mode uses a deterministic run id (`eval_optimize_loop_seed_<seed>`) so the
115118
example outputs are byte-stable. SDK mode is append-only by default: the wrapper
116-
derives `run.run_id` from the SDK result `started_at` when available, otherwise
117-
from the current UTC timestamp. Pass `--run-id` only when a fixed audit path is
118-
useful for tests or local smoke runs.
119+
derives a compact UTC `run.run_id` from the SDK result `started_at` when
120+
available, otherwise from the current UTC timestamp. Pass `--run-id` only when
121+
a fixed audit path is useful for tests or local smoke runs; only explicit
122+
`--run-id` values are included in the reproducibility command.
119123

120124
## Source Prompt Writes
121125

@@ -174,7 +178,8 @@ diffs, and the reproducibility command.
174178
`target_prompts.<field>`, and optional `gate_config` hashes;
175179
- fake mode: `candidate_prompts/<candidate_id>/system_prompt.txt`;
176180
- SDK mode: `candidate_prompts/<candidate_id>/<field_name>.txt` for every
177-
returned `best_prompts` field;
181+
returned `best_prompts` field, plus `bundle.txt` with the combined prompt
182+
shown in the wrapper report;
178183
- `case_results/<candidate_id>_<split>.json`;
179184
- `prompt_diffs/<candidate_id>.diff`.
180185

examples/optimization/eval_optimize_loop/eval_loop/backends.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -122,17 +122,24 @@ async def optimize_async(
122122
best_prompts = dict(getattr(result, "best_prompts", {}) or {})
123123
if not best_prompts:
124124
raise ValueError("sdk mode completed but OptimizeResult.best_prompts was empty")
125-
missing_fields = [
126-
name
127-
for name in target_prompt_paths
128-
if not best_prompts.get(name)
129-
]
125+
missing_fields = [name for name in target_prompt_paths if name not in best_prompts]
130126
if missing_fields:
131127
missing = ", ".join(sorted(missing_fields))
132128
raise ValueError(
133129
"sdk mode completed but OptimizeResult.best_prompts is missing registered target fields: "
134130
f"{missing}"
135131
)
132+
empty_fields = [
133+
name
134+
for name in target_prompt_paths
135+
if not isinstance(best_prompts[name], str) or not best_prompts[name].strip()
136+
]
137+
if empty_fields:
138+
empty = ", ".join(sorted(empty_fields))
139+
raise ValueError(
140+
"sdk mode completed but OptimizeResult.best_prompts contained empty registered target fields: "
141+
f"{empty}"
142+
)
136143
self.last_result = result
137144
self.last_result_summary = _summarize_sdk_result(result)
138145
self.last_artifact_dir = str(output_dir)

examples/optimization/eval_optimize_loop/eval_loop/report.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,7 @@ def write_audit_artifacts(report: OptimizationReport, output_path: Path) -> None
281281
if report.run.get("mode") == "sdk" and isinstance(best_prompts, dict) and best_prompts:
282282
for field_name, prompt_text in best_prompts.items():
283283
(candidate_dir / f"{field_name}.txt").write_text(str(prompt_text), encoding="utf-8")
284+
(candidate_dir / "bundle.txt").write_text(candidate.prompt, encoding="utf-8")
284285
else:
285286
(candidate_dir / "system_prompt.txt").write_text(candidate.prompt, encoding="utf-8")
286287
(diffs_dir / f"{candidate.candidate_id}.diff").write_text(candidate.prompt_diff, encoding="utf-8")

examples/optimization/eval_optimize_loop/run_pipeline.py

Lines changed: 55 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
import argparse
66
import hashlib
77
import json
8+
import math
9+
import shlex
810
import sys
911
import tempfile
1012
from datetime import datetime
@@ -462,7 +464,7 @@ def _build_sdk_report(
462464
gate_config_path=gate_config_path,
463465
target_prompt_paths=target_prompt_paths,
464466
sdk_call_agent=sdk_call_agent,
465-
run_id=effective_run_id,
467+
run_id=run_id,
466468
),
467469
}
468470
run = {
@@ -521,23 +523,36 @@ def _sdk_reproducibility_command(
521523
gate_config_path: str | Path | None,
522524
target_prompt_paths: dict[str, str | Path],
523525
sdk_call_agent: str | None,
524-
run_id: str,
526+
run_id: str | None,
525527
) -> str:
526-
command = (
527-
"python examples/optimization/eval_optimize_loop/run_pipeline.py "
528-
f"--mode sdk --train {train_path} --val {val_path} "
529-
f"--optimizer-config {optimizer_config_path} --prompt {prompt_path} "
530-
f"--output-dir {output_dir} --sdk-call-agent {sdk_call_agent or ''}"
531-
)
528+
parts = [
529+
"python",
530+
"examples/optimization/eval_optimize_loop/run_pipeline.py",
531+
"--mode",
532+
"sdk",
533+
"--train",
534+
str(train_path),
535+
"--val",
536+
str(val_path),
537+
"--optimizer-config",
538+
str(optimizer_config_path),
539+
"--prompt",
540+
str(prompt_path),
541+
"--output-dir",
542+
str(output_dir),
543+
"--sdk-call-agent",
544+
sdk_call_agent or "",
545+
]
532546
for name, path in target_prompt_paths.items():
533547
if not (name == "system_prompt" and Path(path) == Path(prompt_path) and len(target_prompt_paths) == 1):
534-
command += f" --target-prompt {name}={path}"
548+
parts.extend(["--target-prompt", f"{name}={path}"])
535549
if gate_config_path:
536-
command += f" --gate-config {gate_config_path}"
537-
command += f" --run-id {run_id}"
550+
parts.extend(["--gate-config", str(gate_config_path)])
551+
if run_id:
552+
parts.extend(["--run-id", run_id])
538553
if update_source:
539-
command += " --update-source"
540-
return command
554+
parts.append("--update-source")
555+
return " ".join(shlex.quote(part) for part in parts)
541556

542557

543558
def _sdk_gate_decision(
@@ -620,16 +635,30 @@ def _load_sdk_gate_config(gate_config_path: str | Path | None) -> dict[str, floa
620635

621636
min_improvement = gate_payload.get("min_val_score_improvement", 0.01)
622637
max_cost = gate_payload.get("max_total_cost", 1.0)
623-
if not isinstance(min_improvement, (int, float)) or min_improvement < 0:
624-
raise ValueError(f"{path_text}: field 'gate.min_val_score_improvement' must be a non-negative number")
625-
if not isinstance(max_cost, (int, float)) or max_cost < 0:
626-
raise ValueError(f"{path_text}: field 'gate.max_total_cost' must be a non-negative number")
638+
if not _is_non_negative_finite_number(min_improvement):
639+
raise ValueError(
640+
f"--gate-config {path_text}: field 'gate.min_val_score_improvement' "
641+
"must be a non-negative finite number"
642+
)
643+
if not _is_non_negative_finite_number(max_cost):
644+
raise ValueError(
645+
f"--gate-config {path_text}: field 'gate.max_total_cost' must be a non-negative finite number"
646+
)
627647
return {
628648
"min_val_score_improvement": float(min_improvement),
629649
"max_total_cost": float(max_cost),
630650
}
631651

632652

653+
def _is_non_negative_finite_number(value: Any) -> bool:
654+
return (
655+
isinstance(value, (int, float))
656+
and not isinstance(value, bool)
657+
and math.isfinite(float(value))
658+
and float(value) >= 0
659+
)
660+
661+
633662
def _summary_float(summary: dict[str, Any], key: str, default: float) -> float:
634663
value = summary.get(key, default)
635664
if value is None:
@@ -644,8 +673,16 @@ def _default_sdk_run_id(sdk_summary: dict[str, Any]) -> str:
644673
started_at = sdk_summary.get("started_at")
645674
if isinstance(started_at, str) and started_at.strip():
646675
source = started_at.strip()
676+
try:
677+
normalized = source[:-1] + "+00:00" if source.endswith("Z") else source
678+
parsed = datetime.fromisoformat(normalized)
679+
if parsed.tzinfo is None:
680+
parsed = parsed.replace(tzinfo=timezone.utc)
681+
return "eval_optimize_loop_sdk_" + parsed.astimezone(timezone.utc).strftime("%Y%m%dT%H%M%SZ")
682+
except ValueError:
683+
pass
647684
else:
648-
source = datetime.now(timezone.utc).isoformat(timespec="seconds")
685+
return "eval_optimize_loop_sdk_" + datetime.now(timezone.utc).strftime("%Y%m%dT%H%M%SZ")
649686
safe = []
650687
for char in source:
651688
if char.isalnum() or char in {"-", "_"}:

examples/optimization/eval_optimize_loop/tests/test_sdk_backend.py

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,22 @@ def test_sdk_backend_missing_registered_best_prompt_field_is_clear(tmp_path: Pat
139139
)
140140

141141

142+
def test_sdk_backend_empty_best_prompts_dict_error_is_clear(tmp_path: Path, monkeypatch):
143+
_install_fake_sdk(monkeypatch, best_prompts={})
144+
prompt_path = tmp_path / "prompt.txt"
145+
prompt_path.write_text("baseline", encoding="utf-8")
146+
backend = SDKBackend(prompt_path=prompt_path, call_agent_path="fake_call_agent_module:call_agent")
147+
148+
with pytest.raises(ValueError, match="best_prompts was empty"):
149+
backend.optimize(
150+
baseline_prompt="baseline",
151+
train_path=tmp_path / "train.evalset.json",
152+
val_path=tmp_path / "val.evalset.json",
153+
optimizer_config_path=tmp_path / "optimizer.json",
154+
output_dir=tmp_path / "out",
155+
)
156+
157+
142158
def test_sdk_backend_passes_update_source_true(tmp_path: Path, monkeypatch):
143159
calls = _install_fake_sdk(monkeypatch, best_prompt="optimized prompt")
144160
prompt_path = tmp_path / "prompt.txt"
@@ -207,7 +223,7 @@ def test_sdk_backend_empty_best_prompt_error_is_clear(tmp_path: Path, monkeypatc
207223
prompt_path.write_text("baseline", encoding="utf-8")
208224
backend = SDKBackend(prompt_path=prompt_path, call_agent_path="fake_call_agent_module:call_agent")
209225

210-
with pytest.raises(ValueError, match="missing registered target fields.*system_prompt"):
226+
with pytest.raises(ValueError, match="contained empty registered target fields.*system_prompt"):
211227
backend.optimize(
212228
baseline_prompt="baseline",
213229
train_path=tmp_path / "train.evalset.json",
@@ -359,8 +375,9 @@ def test_run_pipeline_mode_sdk_default_run_id_uses_sdk_started_at(tmp_path: Path
359375
sdk_call_agent="fake_call_agent_module:call_agent",
360376
)
361377

362-
assert report.run["run_id"] == "eval_optimize_loop_sdk_2026-07-04T12-34-56-00-00"
378+
assert report.run["run_id"] == "eval_optimize_loop_sdk_20260704T123456Z"
363379
assert (tmp_path / "sdk_run" / "runs" / report.run["run_id"]).is_dir()
380+
assert "--run-id" not in report.run["reproducibility_command"]
364381

365382

366383
def test_run_pipeline_mode_sdk_uses_default_wrapper_gate_when_sdk_config_has_no_gate(
@@ -457,6 +474,39 @@ def test_run_pipeline_mode_sdk_custom_gate_rejects_cost_over_budget(tmp_path: Pa
457474
assert any("cost" in reason for reason in decision.reasons)
458475

459476

477+
@pytest.mark.parametrize(
478+
("field_name", "field_value"),
479+
[
480+
("min_val_score_improvement", True),
481+
("max_total_cost", float("nan")),
482+
("max_total_cost", float("inf")),
483+
],
484+
)
485+
def test_run_pipeline_mode_sdk_rejects_invalid_gate_numbers(
486+
tmp_path: Path,
487+
monkeypatch,
488+
field_name,
489+
field_value,
490+
):
491+
_install_fake_sdk(monkeypatch, best_prompt="optimized prompt")
492+
gate = {"min_val_score_improvement": 0.01, "max_total_cost": 1.0}
493+
gate[field_name] = field_value
494+
gate_path = tmp_path / "bad_gate.json"
495+
gate_path.write_text(json.dumps({"gate": gate}), encoding="utf-8")
496+
497+
with pytest.raises(ValueError, match=f"--gate-config.*{field_name}"):
498+
run_pipeline(
499+
mode="sdk",
500+
train_path=DEFAULT_TRAIN,
501+
val_path=DEFAULT_VAL,
502+
optimizer_config_path=_write_sdk_optimizer_config(tmp_path),
503+
prompt_path=DEFAULT_PROMPT,
504+
output_dir=tmp_path / "sdk_run",
505+
sdk_call_agent="fake_call_agent_module:call_agent",
506+
gate_config_path=gate_path,
507+
)
508+
509+
460510
def test_run_pipeline_mode_sdk_does_not_pass_wrapper_gate_config_to_agent_optimizer(
461511
tmp_path: Path,
462512
monkeypatch,
@@ -540,12 +590,16 @@ def test_run_pipeline_mode_sdk_registers_multiple_target_prompt_paths(tmp_path:
540590
assert (run_dir / "candidate_prompts" / "sdk_best" / "skill_prompt.txt").read_text(
541591
encoding="utf-8"
542592
) == "optimized skill"
593+
assert (run_dir / "candidate_prompts" / "sdk_best" / "bundle.txt").read_text(
594+
encoding="utf-8"
595+
) == report.candidates[0]["candidate"].prompt
543596
input_hashes = json.loads((run_dir / "input_hashes.json").read_text(encoding="utf-8"))
544597
assert set(input_hashes["target_prompts"]) == {"system_prompt", "router_prompt", "skill_prompt"}
545598
assert "gate_config" in input_hashes
546599
command = report.run["reproducibility_command"]
547600
assert "--sdk-call-agent fake_call_agent_module:call_agent" in command
548-
assert f"--target-prompt router_prompt={router_path}" in command
601+
assert "--target-prompt" in command
602+
assert f"router_prompt={router_path}" in command
549603
assert "--gate-config" in command
550604

551605

0 commit comments

Comments
 (0)