"""项目路径解析与输入范围校验。""" from __future__ import annotations from datetime import datetime from pathlib import Path import uuid PROJECT_ROOT = Path(__file__).resolve().parents[2] TASKS_ROOT = PROJECT_ROOT / "data" / "skills-bench" / "tasks" RESULTS_ROOT = PROJECT_ROOT / "results" / "compile-pipeline" def task_directory(value: str) -> Path: requested = Path(value).expanduser() if requested.is_absolute(): candidate = requested.resolve() elif len(requested.parts) == 1: candidate = (TASKS_ROOT / requested).resolve() else: candidate = (PROJECT_ROOT / requested).resolve() try: candidate.relative_to(TASKS_ROOT.resolve()) except ValueError as exc: raise ValueError(f"--task must resolve below {TASKS_ROOT}") from exc if not (candidate / "task.md").is_file(): raise ValueError(f"not a SkillsBench task directory: {candidate}") skills = candidate / "environment" / "skills" if not skills.is_dir(): raise ValueError(f"SkillsBench task has no environment/skills directory: {candidate}") return candidate def single_skill_source(task_dir: Path) -> Path: skills_root = task_dir / "environment" / "skills" skill_files = sorted(skills_root.rglob("SKILL.md")) if len(skill_files) != 1: raise ValueError( "the complete pipeline currently requires exactly one task Skill because " f"Fast and Deep accept one Skill package; found {len(skill_files)} under {skills_root}" ) return skills_root def requested_run_root(value: str | Path | None) -> Path: if value is None: run_id = datetime.now().strftime("%Y%m%d-%H%M%S") + "-" + uuid.uuid4().hex[:6] return (RESULTS_ROOT / run_id).resolve() requested = Path(value).expanduser() candidate = ( requested.resolve() if requested.is_absolute() else (PROJECT_ROOT / requested).resolve() ) try: candidate.relative_to(RESULTS_ROOT.resolve()) except ValueError as exc: raise ValueError(f"--run-dir must resolve below {RESULTS_ROOT}") from exc return candidate