Skip to content

Commit 35a99bf

Browse files
committed
fix(examples): isolate physical inputs and prompt state
1 parent a5d0280 commit 35a99bf

10 files changed

Lines changed: 777 additions & 82 deletions

File tree

examples/optimization/eval_optimize_loop/eval_loop/artifacts.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
from __future__ import annotations
44

55
import re
6+
from collections.abc import Mapping
7+
from pathlib import Path
68
from typing import Any
79

810

@@ -33,3 +35,67 @@ def validate_artifact_component(value: Any, *, context: str) -> str:
3335
if windows_stem in WINDOWS_RESERVED_NAMES:
3436
raise ValueError(f"unsafe {context}: {value!r} is reserved on Windows")
3537
return value
38+
39+
40+
def validate_distinct_file_paths(
41+
paths: Mapping[str, str | Path],
42+
*,
43+
context: str,
44+
) -> None:
45+
"""Reject aliases using portable case-folding and physical file identity."""
46+
47+
resolved_keys: dict[str, str] = {}
48+
physical_keys: dict[tuple[int, int], str] = {}
49+
observed_paths: list[tuple[str, Path, bool]] = []
50+
for label, raw_path in paths.items():
51+
path = Path(raw_path)
52+
try:
53+
resolved = path.resolve(strict=False)
54+
except OSError as error:
55+
raise ValueError(f"{context} {label!r} cannot be resolved: {error}") from error
56+
57+
portable_key = str(resolved).casefold()
58+
previous_label = resolved_keys.get(portable_key)
59+
if previous_label is not None:
60+
raise ValueError(
61+
f"{context} must be different physical files; "
62+
f"{previous_label!r} and {label!r} collide case-insensitively"
63+
)
64+
65+
try:
66+
metadata = path.stat()
67+
except (FileNotFoundError, NotADirectoryError):
68+
metadata = None
69+
except OSError as error:
70+
raise ValueError(f"{context} {label!r} is unavailable: {error}") from error
71+
72+
physical_key = (
73+
(int(metadata.st_dev), int(metadata.st_ino))
74+
if metadata is not None and int(metadata.st_ino) != 0
75+
else None
76+
)
77+
if physical_key is not None and physical_key in physical_keys:
78+
raise ValueError(
79+
f"{context} must be different physical files; "
80+
f"{physical_keys[physical_key]!r} and {label!r} are aliases"
81+
)
82+
83+
for observed_label, observed_path, observed_exists in observed_paths:
84+
if metadata is None or not observed_exists:
85+
continue
86+
try:
87+
same_file = path.samefile(observed_path)
88+
except (FileNotFoundError, NotADirectoryError):
89+
same_file = False
90+
except OSError as error:
91+
raise ValueError(f"{context} {label!r} is unavailable: {error}") from error
92+
if same_file:
93+
raise ValueError(
94+
f"{context} must be different physical files; "
95+
f"{observed_label!r} and {label!r} are aliases"
96+
)
97+
98+
resolved_keys[portable_key] = label
99+
if physical_key is not None:
100+
physical_keys[physical_key] = label
101+
observed_paths.append((label, path, metadata is not None))

examples/optimization/eval_optimize_loop/eval_loop/backends.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from typing import Protocol
1414

1515
from .diffing import make_unified_diff
16+
from .artifacts import validate_distinct_file_paths
1617
from .evaluator import ExampleEvaluator
1718
from .fake_judge import FakeJudge
1819
from .fake_model import FakeModel
@@ -532,9 +533,13 @@ def _load_required_call_agent(self, *, for_evaluation: bool):
532533
return _load_call_agent(self.call_agent_path)
533534

534535
def _target_prompt_paths(self) -> dict[str, str | Path]:
535-
if self.target_prompt_paths:
536-
return dict(self.target_prompt_paths)
537-
return {"system_prompt": self.prompt_path}
536+
paths = (
537+
dict(self.target_prompt_paths)
538+
if self.target_prompt_paths
539+
else {"system_prompt": self.prompt_path}
540+
)
541+
validate_distinct_file_paths(paths, context="SDK target prompt fields")
542+
return paths
538543

539544

540545
def _required_system_prompt(prompts: dict[str, str], *, context: str) -> str:

examples/optimization/eval_optimize_loop/eval_loop/config.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from pathlib import Path
99
from typing import Any
1010

11+
from .artifacts import validate_distinct_file_paths
1112
from .schemas import EvalCase
1213

1314

@@ -141,10 +142,10 @@ def validate_inputs(
141142
validation_cases: list[EvalCase],
142143
config: OptimizerConfig,
143144
) -> None:
144-
train_resolved = Path(train_path).resolve()
145-
val_resolved = Path(val_path).resolve()
146-
if train_resolved == val_resolved:
147-
raise ValueError(f"{train_path}: train and validation evalset paths must be different")
145+
validate_distinct_file_paths(
146+
{"train": train_path, "validation": val_path},
147+
context="train and validation evalset paths",
148+
)
148149

149150
_validate_cases(train_cases, split="train", path=train_path)
150151
_validate_cases(validation_cases, split="validation", path=val_path)

0 commit comments

Comments
 (0)