Spaces:
Sleeping
Sleeping
Download solar_eval/core/project_loader.py from dev-strender/proofread-demo: direct link, hf CLI and curl.
- Browser
- Download file 9.4 kB
-
https://huggingface.co/spaces/dev-strender/proofread-demo/resolve/dd41633552f36d3c639676ca2e005abe50eb72af/solar_eval/core/project_loader.py
- Command line
-
hf download hf://spaces/dev-strender/proofread-demo@dd41633552f36d3c639676ca2e005abe50eb72af/solar_eval/core/project_loader.py
-
curl -L -o project_loader.py https://huggingface.co/spaces/dev-strender/proofread-demo/resolve/dd41633552f36d3c639676ca2e005abe50eb72af/solar_eval/core/project_loader.py
9.4 kB
| """Load project configurations from YAML files.""" | |
| import logging | |
| from collections.abc import Mapping | |
| from pathlib import Path | |
| from typing import Any | |
| import yaml | |
| from solar_eval.core.pipeline_compose import PipelineCompositionError, compose_pipeline | |
| from solar_eval.core.task_discovery import discover_tasks | |
| logger = logging.getLogger(__name__) | |
| def load_all_project_configs( | |
| data_dir: str | Path, config_dirs: Mapping[str, Path] | None = None | |
| ) -> list[dict[str, Any]]: | |
| """λ ν¬ κ΄λ¦¬ config λλ ν 리 + λ°μ΄ν° 루νΈλ₯Ό λ³ν©ν΄ λ‘λνλ€. | |
| config_dirs (νλ‘μ νΈλͺ β accounts/<a>/<c>/03-evaluation) κ° μ°μ μ΄κ³ , | |
| λ°μ΄ν° 루νΈμμλ§ λ°κ²¬λλ νλ‘μ νΈ(λ ν¬ κ΄λ¦¬ μ μΈ νΈλ)λ λ€μ λΆλλ€. | |
| νμ΄νλΌμΈ μ°Έμ‘°λ κ° config κ° λμΈ λλ ν 리 κΈ°μ€μΌλ‘ ν΄μλλ€. | |
| """ | |
| configs: list[dict[str, Any]] = [] | |
| seen: set[str] = set() | |
| for _, config_dir in sorted((config_dirs or {}).items()): | |
| config = _load_yaml(Path(config_dir) / "project.yaml") | |
| if config is None or config.get("name") in seen: | |
| continue | |
| _resolve_defaults(config, Path(config_dir), data_root=Path(data_dir)) | |
| configs.append(config) | |
| seen.add(config["name"]) | |
| for config in load_project_configs(data_dir): | |
| if config["name"] not in seen: | |
| configs.append(config) | |
| return configs | |
| def load_project_configs(projects_dir: str | Path) -> list[dict[str, Any]]: | |
| """Load all YAML project configs from a directory. | |
| Searches two locations (both supported, no duplicates): | |
| 1. projects_dir/<name>/project.yaml (preferred β config inside project folder) | |
| 2. projects_dir/<name>.yaml (legacy β config at root level) | |
| Args: | |
| projects_dir: Path to directory containing project configs. | |
| Returns: | |
| List of parsed project config dicts. | |
| """ | |
| configs = [] | |
| seen_names: set[str] = set() | |
| projects_path = Path(projects_dir) | |
| if not projects_path.exists(): | |
| logger.warning(f"Projects directory not found: {projects_path}") | |
| return configs | |
| # 1. project.yaml inside project folders (preferred) | |
| for project_yaml in sorted(projects_path.glob("*/project.yaml")): | |
| config = _load_yaml(project_yaml) | |
| if config and config.get("name") not in seen_names: | |
| project_dir = project_yaml.parent | |
| _resolve_defaults(config, project_dir, data_root=projects_path) | |
| configs.append(config) | |
| seen_names.add(config["name"]) | |
| # 2. Legacy: <name>.yaml at root level | |
| for yaml_file in sorted(projects_path.glob("*.yaml")): | |
| config = _load_yaml(yaml_file) | |
| if config and config.get("name") not in seen_names: | |
| project_dir = yaml_file.parent / config["name"] | |
| _resolve_defaults(config, project_dir, data_root=projects_path) | |
| configs.append(config) | |
| seen_names.add(config["name"]) | |
| return configs | |
| def _load_yaml(path: Path) -> dict[str, Any] | None: | |
| """Load and validate a single project YAML file.""" | |
| try: | |
| with open(path) as f: | |
| config = yaml.safe_load(f) | |
| if config and "name" in config: | |
| logger.debug(f"Loaded project config: {config['name']} from {path}") | |
| return config | |
| except Exception as e: | |
| logger.warning(f"Failed to load {path}: {e}") | |
| return None | |
| def _resolve_defaults( | |
| config: dict[str, Any], project_dir: Path, data_root: Path | None = None | |
| ) -> None: | |
| """Merge top-level `defaults` into each task, then resolve derived fields. | |
| Also expands `discover:` rules into tasks, resolves string `pipeline_config` | |
| references to actual pipeline YAML files (following `extends`), and attaches a | |
| resolved `field_map` (see `resolve_field_map`). Mutates config in place. | |
| """ | |
| # λ‘λκ° μ±μ°λ μ λ νλ β μ€ν μ΄ config μ λ³Έ κΈ°μ€ μμ°(μΉν μ¬μ λ±)μ | |
| # ν λ μ΄λ€. project.yaml μ μ λ κ°μ΄ μλλ€. | |
| config["config_dir"] = str(project_dir) | |
| _expand_discovered_tasks(config, data_root) | |
| tasks = config.get("tasks", []) | |
| defaults = config.get("defaults", {}) | |
| for task in tasks: | |
| # Merge each default key into task if not already set | |
| for key, value in defaults.items(): | |
| if key not in task: | |
| task[key] = value | |
| # Resolve string pipeline_config β load from pipelines/ directory | |
| _resolve_pipeline_ref(task, project_dir) | |
| # EvalSample field_map β explicit if declared, else derived from the | |
| # legacy input_fields/golden_field/golden_fields trio (no-op today: nothing | |
| # consumes task["field_map"] yet, this only prepares stage C/D wiring). | |
| task["field_map"] = resolve_field_map(task) | |
| def _expand_discovered_tasks(config: dict[str, Any], data_root: Path | None) -> None: | |
| """`discover:` κ·μΉμ΄ μ°Ύμ νμ€ν¬λ₯Ό `tasks` λ€μ λΆμΈλ€ (λͺ μ νμ€ν¬κ° μ°μ ). | |
| λ°κ²¬ μ€ν¨λ μΌν€μ§ μκ³ λ‘κ·Έλ‘ λ¨κΈ΄λ€ β λ°μ΄ν° 루νΈκ° μλ νκ²½(CI λ±)μμλ | |
| λΉ λͺ©λ‘μ΄ μ μμ΄μ§λ§, κ·μΉ μμ²΄κ° μλͺ»λ κ²½μ°μλ ꡬλΆλΌμΌ νλ€. | |
| """ | |
| if not config.get("discover"): | |
| return | |
| try: | |
| found = discover_tasks(config, data_root) | |
| except ValueError as e: | |
| logger.warning("Task discovery failed for %s: %s", config.get("name"), e) | |
| return | |
| if found: | |
| config["tasks"] = [*(config.get("tasks") or []), *found] | |
| logger.debug("Discovered %d tasks for %s", len(found), config.get("name")) | |
| def load_pipeline_file(pipelines_dir: Path, name: str) -> dict[str, Any]: | |
| """νμ΄νλΌμΈ YAML ν μ₯μ μ½μ΄ `extends`/override λ₯Ό ν΄μν dict λ‘ λλ €μ€λ€. | |
| Args: | |
| pipelines_dir: `pipelines/` λλ ν 리. | |
| name: νμ₯μ μλ νμ΄νλΌμΈ μ΄λ¦. | |
| Raises: | |
| FileNotFoundError: νμΌμ΄ μμ λ. | |
| PipelineCompositionError: μμΒ·override ν΄μμ΄ μ€ν¨νμ λ. | |
| """ | |
| path = pipelines_dir / f"{name}.yaml" | |
| if not path.exists(): | |
| raise FileNotFoundError(f"Pipeline file not found: {path}") | |
| with open(path) as f: | |
| raw = yaml.safe_load(f) | |
| if not isinstance(raw, dict): | |
| raise PipelineCompositionError(f"Pipeline {name!r} is not a mapping: {path}") | |
| return compose_pipeline(raw, load_base=lambda base: _load_raw_pipeline(pipelines_dir, base)) | |
| def _load_raw_pipeline(pipelines_dir: Path, name: str) -> dict[str, Any]: | |
| """`extends` λμμ 쑰립νμ§ μμ μνλ‘ μ½λλ€ (λ€λ¨ μμ κ²μΆμ©).""" | |
| path = pipelines_dir / f"{name}.yaml" | |
| if not path.exists(): | |
| raise PipelineCompositionError(f"Base pipeline {name!r} not found: {path}") | |
| with open(path) as f: | |
| raw = yaml.safe_load(f) | |
| if not isinstance(raw, dict): | |
| raise PipelineCompositionError(f"Base pipeline {name!r} is not a mapping: {path}") | |
| return raw | |
| def _resolve_pipeline_ref(task: dict[str, Any], project_dir: Path) -> None: | |
| """If pipeline_config is a string reference, load the pipeline YAML file.""" | |
| pipeline_ref = task.get("pipeline_config") | |
| if not isinstance(pipeline_ref, str): | |
| return | |
| try: | |
| task["pipeline_config"] = load_pipeline_file(project_dir / "pipelines", pipeline_ref) | |
| logger.debug(f"Resolved pipeline_config '{pipeline_ref}' from {project_dir}") | |
| except (FileNotFoundError, PipelineCompositionError, OSError, yaml.YAMLError) as e: | |
| logger.warning(f"Failed to load pipeline '{pipeline_ref}': {e}") | |
| def resolve_field_map(task_config: dict[str, Any]) -> dict[str, Any]: | |
| """task μ€μ μμ `EvalSample.from_row` μ© field_map μ κ²°μ νλ€. | |
| μμ ν¨μ β task_config λ₯Ό λ³ννμ§ μκ³ μ dict λ₯Ό λ°ννλ€. | |
| μ°μ μμ: | |
| 1. task_config μ λͺ μμ `field_map` μ΄ μμΌλ©΄ κ·Έλλ‘(μ¬λ³Έ) λ°ννλ€. | |
| 2. μμΌλ©΄ λ κ±°μ `input_fields`/`golden_field`/`golden_fields` μμ μ λνλ€: | |
| - `input_fields` μ **첫 λ²μ§Έ** νλ β `input` | |
| (λλ¨Έμ§ νλλ field_map μ΄ λ΄μ§ λͺ»νλ€ β λ€μ€ μ λ ₯ νλ νμ€ν¬λ | |
| λͺ μμ field_map μ μ μΈν΄μΌ νλ€) | |
| - `golden_field` β `reference` (λ¬Έμμ΄ μ»¬λΌλͺ ) | |
| - `golden_field` κ° μκ³ `golden_fields` κ° μμΌλ©΄ β `reference` μ | |
| κ·Έ dict λ₯Ό κ·Έλλ‘ λ£λλ€ (`EvalSample.from_row` κ° μ¬λ¬ 컬λΌμ λ¬Άμ΄ | |
| ν©μ± μ λ΅μ λ§λ λ€). `runner.py` μ `golden_fields` μ²λ¦¬μ λμΉ. | |
| Args: | |
| task_config: `defaults` λ³ν©μ΄ λλ task μ€μ dict. | |
| Returns: | |
| EvalSample νλλͺ -> 컬λΌλͺ (str) λλ {μλΈν€: 컬λΌλͺ }(dict) λ§€ν. | |
| μ΄λ μμ€λ μμΌλ©΄ λΉ dict. | |
| """ | |
| explicit = task_config.get("field_map") | |
| if explicit: | |
| return dict(explicit) | |
| field_map: dict[str, Any] = {} | |
| input_fields = task_config.get("input_fields") or [] | |
| if input_fields: | |
| field_map["input"] = input_fields[0] | |
| golden_field = task_config.get("golden_field") | |
| golden_fields = task_config.get("golden_fields") | |
| if golden_field: | |
| field_map["reference"] = golden_field | |
| elif golden_fields: | |
| field_map["reference"] = dict(golden_fields) | |
| return field_map | |