Spaces:
Sleeping
Sleeping
| from __future__ import annotations | |
| import importlib | |
| import time | |
| from pathlib import Path | |
| from typing import Any, cast | |
| import typer | |
| import yaml | |
| from redstack.config.determinism import apply_determinism | |
| from redstack.config.loader import ConfigLoadError, load_config | |
| from redstack.config.schema import Profile, RunMode | |
| from redstack.observability.timing import format_duration | |
| __all__: tuple[str, ...] = ("build",) | |
| #: Default online ``ScoringPolicy.neutral_prior`` (config/schema.py), reused | |
| #: when packaging the locked-heuristics scoring_weights artifact. | |
| _DEFAULT_NEUTRAL_PRIOR: float = 0.5 | |
| def _load_seed_weights(configs_root: Path) -> tuple[dict[str, float], float]: | |
| """Read ``weights/scoring_weights.yaml`` -> (component weights, neutral prior). | |
| This is the locked-heuristics default: the human-authored seed weights, | |
| used verbatim (no gold-label search) when ``--golden-labels`` is not given. | |
| """ | |
| path = configs_root / "weights" / "scoring_weights.yaml" | |
| doc = yaml.safe_load(path.read_text(encoding="utf-8")) | |
| weights = {str(k): float(v) for k, v in doc["weights"].items()} | |
| neutral_prior = float(doc.get("neutral_prior", _DEFAULT_NEUTRAL_PRIOR)) | |
| return weights, neutral_prior | |
| def _resolve_configs_root(config_arg: Path) -> Path: | |
| """Resolve a ``--config`` argument (file or directory) to the configs root. | |
| Accepts the root directory itself, ``<root>/base.yaml``, or | |
| ``<root>/runtime/<mode>.yaml`` — every form used by the Makefile / CLAUDE.md | |
| invocations — by walking up to find the directory holding ``base.yaml``. | |
| Raises: | |
| typer.BadParameter: no ``base.yaml`` found within two levels up. | |
| """ | |
| start = config_arg if config_arg.is_dir() else config_arg.parent | |
| for probe in (start, start.parent): | |
| if (probe / "base.yaml").is_file(): | |
| return probe | |
| raise typer.BadParameter( | |
| f"cannot locate a configs root (base.yaml) from {config_arg}" | |
| ) | |
| def _code_version() -> str: | |
| """Return the build's code provenance for the report (package version).""" | |
| import redstack | |
| return redstack.__version__ | |
| def build( | |
| config: Path = typer.Option( | |
| Path("configs"), | |
| "--config", | |
| help="Configs root directory, or a YAML file inside it.", | |
| ), | |
| profile: str | None = typer.Option( | |
| None, "--profile", help="Optional override layer: ci | local." | |
| ), | |
| force: str | None = typer.Option( | |
| None, | |
| "--force", | |
| help="Comma-separated stage ids to force-recompute (e.g. 'O9,O15').", | |
| ), | |
| golden_labels: bool = typer.Option( | |
| True, | |
| "--golden-labels/--no-golden-labels", | |
| help=( | |
| "Run the real O8-O10 gold-label calibration search against the " | |
| "committed data/golden/golden_labels.csv (default — fails loudly " | |
| "via GoldLabelSeedMissingError if it isn't committed). Pass " | |
| "--no-golden-labels to skip O8-O10's search and use the locked " | |
| "heuristic weights from configs/weights/scoring_weights.yaml " | |
| "as-is; this is an uncalibrated dev shortcut, not a substitute " | |
| "for the real search." | |
| ), | |
| ), | |
| ) -> None: | |
| """Run the offline O0-O18 build: artifacts/ + MANIFEST.json.""" | |
| cumulative_started = time.perf_counter() | |
| configs_root = _resolve_configs_root(config) | |
| profile_enum = Profile(profile) if profile else None | |
| try: | |
| resolved = load_config(configs_root, RunMode.OFFLINE, profile_enum) | |
| except ConfigLoadError as exc: | |
| typer.secho(str(exc), fg=typer.colors.RED, err=True) | |
| raise typer.Exit(code=1) from exc | |
| apply_determinism(resolved.determinism) | |
| # Dynamic on purpose — see module docstring (layering seam + import order). | |
| compose = importlib.import_module("redstack.pipelines.offline.compose") | |
| force_ids = tuple(s.strip() for s in force.split(",")) if force else None | |
| pass_started = time.perf_counter() | |
| if golden_labels: | |
| run_offline_build = cast("Any", compose.run_offline_build) | |
| report = run_offline_build( | |
| resolved, | |
| configs_root=configs_root, | |
| code_version=_code_version(), | |
| force=force_ids, | |
| ) | |
| else: | |
| component_weights, neutral_prior = _load_seed_weights(configs_root) | |
| run_offline_build_with_locked_heuristics = cast( | |
| "Any", compose.run_offline_build_with_locked_heuristics | |
| ) | |
| report = run_offline_build_with_locked_heuristics( | |
| resolved, | |
| configs_root=configs_root, | |
| code_version=_code_version(), | |
| component_weights=component_weights, | |
| neutral_prior=neutral_prior, | |
| force=force_ids, | |
| ) | |
| pass_elapsed = time.perf_counter() - pass_started | |
| cumulative_elapsed = time.perf_counter() - cumulative_started | |
| typer.echo( | |
| "offline build complete: " | |
| f"executed={len(report.executed)} " | |
| f"skipped={len(report.skipped)} " | |
| f"failed={len(report.failed)}" | |
| ) | |
| typer.echo(f"offline pass duration: {format_duration(pass_elapsed)} (MM:SS.ms)") | |
| typer.echo( | |
| f"cumulative wall-clock: {format_duration(cumulative_elapsed)} (MM:SS.ms)" | |
| ) | |
| if report.failed: | |
| raise typer.Exit(code=1) | |