Spaces:
Sleeping
Sleeping
File size: 5,299 Bytes
761d149 830d137 feb1b1c 830d137 bb77312 830d137 fc87e83 830d137 bb77312 830d137 fc87e83 830d137 fc87e83 830d137 bb77312 830d137 bb77312 fc87e83 bb77312 830d137 bb77312 830d137 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 |
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)
|