Spaces:
Running
Running
Download solar_eval/core/pipeline_compose.py from dev-strender/proofread-demo: direct link, hf CLI and curl.
- Browser
- Download file 8.81 kB
-
https://huggingface.co/spaces/dev-strender/proofread-demo/resolve/main/solar_eval/core/pipeline_compose.py
- Command line
-
hf download hf://spaces/dev-strender/proofread-demo/solar_eval/core/pipeline_compose.py
-
curl -L -o pipeline_compose.py https://huggingface.co/spaces/dev-strender/proofread-demo/resolve/main/solar_eval/core/pipeline_compose.py
8.81 kB
| """νμ΄νλΌμΈ 쑰립 β `extends` μμ + dot-path override. | |
| μ: ν μΆλ§ λ°κΎΌ μ€νμ΄ νμ΄νλΌμΈ νμΌ **μ 체 μ¬λ³Έ**μΌλ‘ μμ¬ μλ€. | |
| `pipeline_dev_v24a/b/c` λ 109μ€μ§λ¦¬ v24 μ¬λ³ΈμΈλ° μ€μ μ°¨μ΄λ | |
| repeat_temperatures: [0.0, 0.2] β [0.0, 0.0] / [0.0, 0.3] / [0.0, 0.5] | |
| ν μ€λΏμ΄λ€. μ¬λ³ΈμΌλ‘ λλ©΄ (1) μ€νμ μ€μ λ³μκ° diff λ₯Ό λ μΌλ§ 보μ΄κ³ , | |
| (2) base λ₯Ό κ³ μΉ λ μ¬λ³Έλ€μ΄ μ‘°μ©ν κ°λΌμ§λ€. μ€νμ μ μλ μ¬λ³Έμ΄ μλλΌ | |
| **diff** μ¬μΌ νλ€. | |
| λ κ°μ§ ννμ μ§μνλ€. λ λ€ κ°μ dot-path λ¬Έλ²μ μ΄λ€: | |
| # νμΌλ‘ λ¨κΈ°λ μ€ν (μ¬μ¬μ©Β·μΈμ© λμ) | |
| name: pipeline_dev_v24a | |
| extends: pipeline_dev_v24 | |
| overrides: | |
| steps.basic_correction.repeat_temperatures: [0.0, 0.0] | |
| # νμΌ μμ΄ ν λ² λλ €λ³΄λ μ€ν | |
| solar-eval runs start ... -P pipeline_dev_v24 \\ | |
| --set steps.basic_correction.repeat_temperatures='[0.0, 0.5]' | |
| 쑰립μ **λ‘λ μμ μ λλλ€** β μ€ν μμ§(`STEP_REGISTRY`)μ `extends` λ₯Ό | |
| μμ λͺ¨λ₯΄κ³ , μμ±λ steps λͺ©λ‘λ§ λ°λλ€. κ·Έλμ μ΄ κΈ°λ₯μ μμ§μ μΌλ°μ±μ | |
| μν₯μ μ£Όμ§ μλλ€(λ²μΒ·μμ½ λ± λ€λ₯Έ νλ‘μ νΈλ κ·Έλλ‘ μΈ μ μλ€). | |
| μμμ **1λ¨κ³λ‘ μ ν**νλ€. base κ° λ base λ₯Ό κ°λ 체μΈμ hydra κ³μ΄μμ | |
| λ°λ³΅μ μΌλ‘ λ¬Έμ κ° λ μ§μ μ΄κ³ ("μ΄ κ°μ΄ μ΄λμ μλκ°"λ₯Ό μΆμ λΆκ°), PoC κ·λͺ¨μμ | |
| μ»λ κ² μλ€. | |
| """ | |
| import copy | |
| import logging | |
| from collections.abc import Callable, Mapping | |
| from typing import Any | |
| import yaml | |
| logger = logging.getLogger(__name__) | |
| #: dot-path μμ μ€ν λͺ©λ‘μ κ°λ¦¬ν€λ μ΅μμ ν€. | |
| STEPS_KEY = "steps" | |
| class PipelineCompositionError(ValueError): | |
| """`extends` ν΄μμ΄λ override μ μ©μ΄ μ€ν¨νμ λ. | |
| μ‘°μ©ν λμ΄κ°μ§ μλλ€ β μ€ν λ override λ "μ무 ν¨κ³Ό μλ μ€ν"μ λ§λ€κ³ , | |
| 그건 μλͺ»λ κ²°λ‘ μΌλ‘ λ°λ‘ μ΄μ΄μ§λ€ (μ΄ μμ§μ΄ κ³Όκ±° μ‘°μ©ν no-op μ€ν μΌλ‘ | |
| κ°μ λ°©μμΌλ‘ λ€μ³€λ€: `pipelines/errors.py` μ°Έκ³ ). | |
| """ | |
| def compose_pipeline( | |
| raw: Mapping[str, Any], | |
| *, | |
| load_base: Callable[[str], Mapping[str, Any]] | None = None, | |
| overrides: Mapping[str, Any] | None = None, | |
| ) -> dict[str, Any]: | |
| """νμ΄νλΌμΈ YAML ν μ₯μ μ€ν κ°λ₯ν μ€μ μΌλ‘ 쑰립νλ€. | |
| Args: | |
| raw: νμ΄νλΌμΈ YAML μ νμ±ν dict. | |
| load_base: `extends` λμ μ΄λ¦μ λ°μ κ·Έ YAML dict λ₯Ό λλ €μ£Όλ ν¨μ. | |
| `extends` λ₯Ό μ΄ νμ΄νλΌμΈμ 쑰립νλ €λ©΄ λ°λμ νμνλ€. | |
| overrides: νμΌ λ°κΉ₯μμ λ€μ΄μ¨ override (CLI `--set`). YAML μ | |
| `overrides` λ³΄λ€ **λμ€μ** μ μ©λλ€. | |
| Returns: | |
| `extends`/`overrides` ν€κ° κ±·ν, ν΄μμ΄ λλ μ€μ dict. | |
| Raises: | |
| PipelineCompositionError: base λ₯Ό λͺ» μ°Ύκ±°λ, μμμ΄ 2λ¨κ³ μ΄μμ΄κ±°λ, | |
| override κ²½λ‘κ° μ€μ¬νμ§ μλ μ€ν μ κ°λ¦¬ν¬ λ. | |
| """ | |
| base_name = raw.get("extends") | |
| if base_name is not None and not isinstance(base_name, str): | |
| raise PipelineCompositionError(f"`extends` must be a pipeline name, got {base_name!r}") | |
| if base_name: | |
| if load_base is None: | |
| raise PipelineCompositionError( | |
| f"Pipeline extends {base_name!r} but no loader was provided" | |
| ) | |
| base = dict(load_base(base_name)) | |
| if base.get("extends"): | |
| raise PipelineCompositionError( | |
| f"Multi-level inheritance is not supported: {base_name!r} itself extends " | |
| f"{base['extends']!r}. Flatten it or extend the root pipeline directly." | |
| ) | |
| composed = _merge_base(base, raw) | |
| else: | |
| composed = copy.deepcopy({k: v for k, v in raw.items() if k != "extends"}) | |
| file_overrides = composed.pop("overrides", None) or raw.get("overrides") or {} | |
| _apply_overrides(composed, file_overrides, origin="pipeline `overrides`") | |
| _apply_overrides(composed, overrides or {}, origin="--set") | |
| return composed | |
| def _merge_base(base: dict[str, Any], child: Mapping[str, Any]) -> dict[str, Any]: | |
| """base μμ child μ μ΅μμ ν€λ₯Ό μΉλλ€. | |
| `steps` λ child κ° ν΅μ§Έλ‘ μ μΈνμ λλ§ κ΅μ²΄νλ€ β λΆλΆ λ³ν©μ override μ | |
| μΌμ΄κ³ , μ¬κΈ°μκΉμ§ νλ©΄ "steps κ° μ΄λμ μλκ°"κ° νλ €μ§λ€. | |
| **κΉμ 볡μ¬κ° νμλ€**: μκ² λ³΅μ¬νλ©΄ μμμ override κ° base μ step dict λ₯Ό | |
| μ μ리μμ κ³ μ³, κ°μ base λ₯Ό μ°λ λ€λ₯Έ μμκΉμ§ μ€μΌλλ€. | |
| """ | |
| merged = copy.deepcopy(base) | |
| for key, value in child.items(): | |
| if key in ("extends", "overrides"): | |
| continue | |
| merged[key] = copy.deepcopy(value) | |
| return merged | |
| def _apply_overrides(config: dict[str, Any], overrides: Mapping[str, Any], *, origin: str) -> None: | |
| """dot-path override λ₯Ό μ μ리μ μ μ©νλ€.""" | |
| if not isinstance(overrides, Mapping): | |
| raise PipelineCompositionError( | |
| f"{origin} must be a mapping of dot-path β value, got {type(overrides).__name__}" | |
| ) | |
| for path, value in overrides.items(): | |
| try: | |
| _set_path(config, str(path), value) | |
| except PipelineCompositionError as e: | |
| raise PipelineCompositionError(f"{origin}: {e}") from None | |
| def _set_path(config: dict[str, Any], path: str, value: Any) -> None: | |
| """`steps.<step>.<field>...` λλ `<field>...` κ²½λ‘μ κ°μ μ΄λ€.""" | |
| parts = [p for p in path.split(".") if p] | |
| if not parts: | |
| raise PipelineCompositionError("empty override path") | |
| if parts[0] == STEPS_KEY: | |
| if len(parts) < 3: | |
| raise PipelineCompositionError( | |
| f"{path!r} β step override needs at least " | |
| f"'{STEPS_KEY}.<step_name>.<field>' (e.g. " | |
| f"'{STEPS_KEY}.basic_correction.reasoning_effort')" | |
| ) | |
| target = _find_step(config, parts[1], path) | |
| _assign(target, parts[2:], value, path) | |
| return | |
| _assign(config, parts, value, path) | |
| def _find_step(config: Mapping[str, Any], step_name: str, path: str) -> dict[str, Any]: | |
| """μ΄λ¦μΌλ‘ μ€ν μ μ°Ύλλ€. μμΌλ©΄ ν보λ₯Ό μ€μ΄ raise (μ€νλ₯Ό μ‘°μ©ν λκΈ°μ§ μλλ€).""" | |
| steps = config.get(STEPS_KEY) | |
| if not isinstance(steps, list): | |
| raise PipelineCompositionError(f"{path!r} β pipeline has no `{STEPS_KEY}` list") | |
| matches = [s for s in steps if isinstance(s, Mapping) and s.get("name") == step_name] | |
| if not matches: | |
| names = [str(s.get("name")) for s in steps if isinstance(s, Mapping)] | |
| raise PipelineCompositionError( | |
| f"{path!r} β no step named {step_name!r} (steps: {', '.join(names)})" | |
| ) | |
| if len(matches) > 1: | |
| raise PipelineCompositionError( | |
| f"{path!r} β {step_name!r} matches {len(matches)} steps; step names must be unique " | |
| f"to be addressable by override" | |
| ) | |
| return matches[0] # type: ignore[return-value] | |
| def _assign(target: dict[str, Any], parts: list[str], value: Any, path: str) -> None: | |
| """μ€μ²© dict λ₯Ό λ°λΌ λ΄λ €κ°λ©° λ§μ§λ§ ν€μ κ°μ μ΄λ€. μ€κ° dict λ λ§λ€μ΄ μ€λ€.""" | |
| cursor: Any = target | |
| for key in parts[:-1]: | |
| nxt = cursor.get(key) | |
| if nxt is None: | |
| nxt = {} | |
| cursor[key] = nxt | |
| elif not isinstance(nxt, dict): | |
| raise PipelineCompositionError( | |
| f"{path!r} β {key!r} holds {type(nxt).__name__}, cannot descend into it" | |
| ) | |
| cursor = nxt | |
| cursor[parts[-1]] = value | |
| def parse_set_option(raw_values: tuple[str, ...] | list[str]) -> dict[str, Any]: | |
| """CLI `--set key=value` λͺ©λ‘μ override λ§΅μΌλ‘ λ°κΎΌλ€. | |
| κ°μ YAML λ‘ νμ±ν΄ νμ μ μ΄λ¦°λ€ β `high` λ λ¬Έμμ΄, `2` λ μ μ, | |
| `[0.0, 0.5]` λ 리μ€νΈκ° λλ€. νμ±μ μ€ν¨νλ©΄ λ¬Έμμ΄ κ·Έλλ‘ λλ€. | |
| Raises: | |
| PipelineCompositionError: `=` κ° μλ νλͺ©μ΄ μμ λ. | |
| """ | |
| overrides: dict[str, Any] = {} | |
| for item in raw_values: | |
| if "=" not in item: | |
| raise PipelineCompositionError( | |
| f"--set expects 'path=value', got {item!r} " | |
| f"(e.g. --set steps.basic_correction.reasoning_effort=high)" | |
| ) | |
| key, _, raw_value = item.partition("=") | |
| key = key.strip() | |
| if not key: | |
| raise PipelineCompositionError(f"--set has an empty path: {item!r}") | |
| try: | |
| overrides[key] = yaml.safe_load(raw_value) | |
| except yaml.YAMLError: | |
| overrides[key] = raw_value | |
| return overrides | |