from __future__ import annotations from pathlib import Path from typing import Any, Dict, List, Optional from pydantic import BaseModel, ConfigDict from .run_presets import load_overrides_from_file class MultiProjectItemConfig(BaseModel): model_config = ConfigDict(extra="forbid", validate_assignment=True) local_project_id: str remote_project_id: str config_path: Optional[str] = None @property def scope(self) -> str: return f"project_id:{self.local_project_id}" class MultiProjectRunConfig(BaseModel): model_config = ConfigDict(extra="forbid", validate_assignment=True) global_config: str default_project_config: str project_bootstrap_node_types: List[str] = [] projects: List[MultiProjectItemConfig] def is_multi_project_payload(payload: Dict[str, Any]) -> bool: return all(key in payload for key in ("global_config", "default_project_config", "projects")) def build_multi_project_config_from_file(*, project_root: Path, file_path: str | Path) -> MultiProjectRunConfig: path = Path(file_path) overrides = load_overrides_from_file(path, project_root) return MultiProjectRunConfig.model_validate(overrides)