66import hashlib
77import json
88import math
9+ import re
910import shlex
1011import sys
1112import tempfile
4243DEFAULT_OPTIMIZER_CONFIG = HERE / "data" / "optimizer.json"
4344DEFAULT_PROMPT = HERE / "prompts" / "baseline_system_prompt.txt"
4445DEFAULT_OUTPUT_DIR = Path (tempfile .gettempdir ()) / "eval-optimize-loop"
46+ TARGET_PROMPT_FIELD_RE = re .compile (r"^[A-Za-z_][A-Za-z0-9_]*$" )
47+ RUN_ID_RE = re .compile (r"^[A-Za-z0-9_.-]+$" )
4548
4649
4750def run_pipeline (
@@ -65,6 +68,8 @@ def run_pipeline(
6568
6669 if mode not in {"fake" , "sdk" }:
6770 raise ValueError ("field 'mode' must be one of: fake, sdk" )
71+ if run_id is not None :
72+ run_id = validate_run_id (run_id )
6873 if mode == "fake" and (not fake_model or not fake_judge ):
6974 raise ValueError (
7075 "fake mode requires fake_model=True and fake_judge=True. Pass --fake-model --fake-judge "
@@ -109,6 +114,8 @@ def run_pipeline(
109114 sdk_call_agent = sdk_call_agent ,
110115 run_id = run_id ,
111116 )
117+ if run_id is None :
118+ _resolve_default_sdk_run_id_collision (report , output_dir )
112119 write_reports (report , output_dir )
113120 return report
114121
@@ -351,14 +358,15 @@ def _build_sdk_report(
351358 prompt_path = prompt_path ,
352359 )
353360 sdk_summary = sdk_backend .last_result_summary or {}
354- baseline_pass_rate = _summary_float (sdk_summary , "baseline_pass_rate" , 0.0 )
355- best_pass_rate = _summary_float (sdk_summary , "best_pass_rate" , baseline_pass_rate )
361+ baseline_pass_rate = _summary_float (sdk_summary , "baseline_pass_rate" , 0.0 , required = True )
362+ best_pass_rate = _summary_float (sdk_summary , "best_pass_rate" , baseline_pass_rate , required = True )
356363 pass_rate_improvement = _summary_float (
357364 sdk_summary ,
358365 "pass_rate_improvement" ,
359366 best_pass_rate - baseline_pass_rate ,
367+ required = True ,
360368 )
361- total_llm_cost = _summary_float (sdk_summary , "total_llm_cost" , 0.0 )
369+ total_llm_cost = _summary_float (sdk_summary , "total_llm_cost" , 0.0 , required = True )
362370 duration_seconds = _summary_float (sdk_summary , "duration_seconds" , 0.0 )
363371 effective_run_id = run_id or _default_sdk_run_id (sdk_summary )
364372 target_prompt_hashes = {
@@ -562,8 +570,8 @@ def _sdk_gate_decision(
562570 gate_config : dict [str , float ],
563571) -> GateDecision :
564572 status = str (sdk_summary .get ("status" ) or "UNKNOWN" )
565- improvement = _summary_float (sdk_summary , "pass_rate_improvement" , 0.0 )
566- total_cost = _summary_float (sdk_summary , "total_llm_cost" , 0.0 )
573+ improvement = _summary_float (sdk_summary , "pass_rate_improvement" , 0.0 , required = True )
574+ total_cost = _summary_float (sdk_summary , "total_llm_cost" , 0.0 , required = True )
567575 min_improvement = gate_config ["min_val_score_improvement" ]
568576 max_cost = gate_config ["max_total_cost" ]
569577
@@ -659,14 +667,25 @@ def _is_non_negative_finite_number(value: Any) -> bool:
659667 )
660668
661669
662- def _summary_float (summary : dict [str , Any ], key : str , default : float ) -> float :
670+ def _summary_float (summary : dict [str , Any ], key : str , default : float , * , required : bool = False ) -> float :
663671 value = summary .get (key , default )
664672 if value is None :
665673 return default
674+ if isinstance (value , bool ):
675+ if required :
676+ raise ValueError (f"SDK OptimizeResult field { key } must be a finite number" )
677+ return default
666678 try :
667- return float (value )
679+ parsed = float (value )
668680 except (TypeError , ValueError ):
681+ if required :
682+ raise ValueError (f"SDK OptimizeResult field { key } must be a finite number" )
683+ return default
684+ if not math .isfinite (parsed ):
685+ if required :
686+ raise ValueError (f"SDK OptimizeResult field { key } must be a finite number" )
669687 return default
688+ return parsed
670689
671690
672691def _default_sdk_run_id (sdk_summary : dict [str , Any ]) -> str :
@@ -704,16 +723,39 @@ def _parse_target_prompt_paths(
704723 if "=" not in item :
705724 raise ValueError ("--target-prompt must use name=path format" )
706725 name , path = item .split ("=" , 1 )
707- name = name .strip ()
708726 path = path .strip ()
709- if not name or not path :
727+ if not TARGET_PROMPT_FIELD_RE .fullmatch (name ):
728+ raise ValueError (
729+ f"--target-prompt field name { name !r} is invalid; use /^[A-Za-z_][A-Za-z0-9_]*$/"
730+ )
731+ if not path :
710732 raise ValueError ("--target-prompt must use non-empty name=path values" )
711733 if name in parsed :
712734 raise ValueError (f"--target-prompt duplicate field name { name !r} " )
713735 parsed [name ] = Path (path )
714736 return parsed
715737
716738
739+ def validate_run_id (run_id : str ) -> str :
740+ if not isinstance (run_id , str ):
741+ raise ValueError (f"--run-id value { run_id !r} must be a string" )
742+ if run_id in {"" , "." , ".." } or not RUN_ID_RE .fullmatch (run_id ):
743+ raise ValueError (f"--run-id value { run_id !r} is invalid" )
744+ return run_id
745+
746+
747+ def _resolve_default_sdk_run_id_collision (report : OptimizationReport , output_dir : str | Path ) -> None :
748+ base_run_id = str (report .run .get ("run_id" ) or "" )
749+ validate_run_id (base_run_id )
750+ run_root = Path (output_dir ) / "runs"
751+ candidate = base_run_id
752+ suffix = 1
753+ while (run_root / candidate ).exists ():
754+ candidate = f"{ base_run_id } -{ suffix } "
755+ suffix += 1
756+ report .run ["run_id" ] = candidate
757+
758+
717759def _candidate_prompt_hashes_by_field (
718760 candidates : list [CandidatePrompt ],
719761 sdk_summary : dict [str , Any ],
0 commit comments