multimodalart's picture
multimodalart HF Staff
Upload folder using huggingface_hub
7b1830e verified
Raw History Blame Contribute Delete
12.5 kB
"""Command-line interface for training, inference, preencoding, and data inspection."""
from __future__ import annotations
import argparse
import hashlib
import json
import os
import sys
import tempfile
from collections.abc import Sequence
from pathlib import Path
from typing import Any
from solarwm import __version__
from solarwm.config import load_config
from solarwm.config.routes import supported_routes, validate_route
from solarwm.data.index import inventory, read_index
from solarwm.data.sampling import (
CanonicalSampler,
ReaderIdentity,
SamplingConfig,
plan_fingerprint,
)
from solarwm.errors import SolarWMError
from solarwm.runtime.create_only import write_file_create_only
from solarwm.runtime.images import probe_python_runtime
from solarwm.runtime.output_layout import (
camera_inference_output_layout,
invocation_output_dir,
)
from solarwm.runtime.provenance import build_launch_manifest, reject_inline_secrets
def _common_config(parser: argparse.ArgumentParser) -> None:
parser.add_argument("--config", required=True, type=Path)
parser.add_argument(
"--set",
action="append",
default=[],
metavar="KEY=VALUE",
help="override one dotted config value; may be repeated",
)
def build_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(prog="solarwm")
parser.add_argument("--version", action="version", version=f"%(prog)s {__version__}")
commands = parser.add_subparsers(dest="command", required=True)
for action in ("train", "infer", "preencode"):
child = commands.add_parser(action, help=f"run the unified {action} entrypoint")
_common_config(child)
config = commands.add_parser("config", help="resolve and inspect a run config")
config_commands = config.add_subparsers(dest="config_command", required=True)
resolve = config_commands.add_parser("resolve")
_common_config(resolve)
resolve.add_argument("--output", type=Path)
config_commands.add_parser("routes")
data = commands.add_parser("data", help="inspect canonical index semantics")
data_commands = data.add_subparsers(dest="data_command", required=True)
inspect = data_commands.add_parser("inspect")
inspect.add_argument("index", type=Path)
plan = data_commands.add_parser("plan")
plan.add_argument("index", type=Path)
plan.add_argument("--seed", type=int, required=True)
plan.add_argument("--pixel-frames", type=int, required=True)
plan.add_argument("--rank", type=int, default=0)
plan.add_argument("--world-size", type=int, default=1)
plan.add_argument("--worker-id", type=int, default=0)
plan.add_argument("--num-workers", type=int, default=1)
plan.add_argument("--epoch", type=int, default=0)
plan.add_argument("--limit", type=int, default=16)
plan.add_argument("--shuffle-buffer", type=int, default=4096)
environment = commands.add_parser("environment", help="inspect the current runtime")
environment_commands = environment.add_subparsers(dest="environment_command", required=True)
environment_commands.add_parser("probe")
return parser
def _write_json(value: Any, output: Path | None = None) -> None:
payload = json.dumps(value, indent=2, sort_keys=True, ensure_ascii=False) + "\n"
if output is None:
sys.stdout.write(payload)
return
output.parent.mkdir(parents=True, exist_ok=True)
temporary: str | None = None
try:
with tempfile.NamedTemporaryFile(
mode="w",
encoding="utf-8",
dir=output.parent,
prefix=f".{output.name}.",
suffix=".tmp",
delete=False,
) as handle:
temporary = handle.name
handle.write(payload)
handle.flush()
os.fsync(handle.fileno())
os.replace(temporary, output)
temporary = None
finally:
if temporary is not None:
Path(temporary).unlink(missing_ok=True)
def _run_backend(args: argparse.Namespace) -> int:
resolved = load_config(args.config, args.set)
reject_inline_secrets(resolved.values)
configured_action = str(resolved.values["action"])
if configured_action != args.command:
raise SolarWMError(
f"command {args.command!r} does not match config action {configured_action!r}"
)
route = validate_route(resolved.values)
runtime = resolved.values.get("runtime", {})
from solarwm.backends import load_backend
backend = load_backend(route.family)
backend.validate_config(resolved.values)
if not str(runtime.get("output_dir", "")).strip():
raise SolarWMError("runtime.output_dir is required")
publication_layout = camera_inference_output_layout(resolved.values)
output_dir = invocation_output_dir(resolved.values)
launcher_rank = int(os.environ.get("RANK", "0"))
if launcher_rank == 0:
if publication_layout is not None:
publication_layout.publish_root.mkdir(parents=True, exist_ok=True)
output_dir.parent.mkdir(parents=True, exist_ok=True)
try:
output_dir.mkdir()
except FileExistsError as exc:
raise SolarWMError(f"camera inference run already exists: {output_dir}") from exc
else:
output_dir.mkdir(parents=True, exist_ok=True)
# A terminal result describes only the current invocation. Remove
# a result left by an earlier legacy invocation before work starts.
(output_dir / "run-result.json").unlink(missing_ok=True)
resolved.write_json(output_dir / "resolved-config.json")
manifest = build_launch_manifest(
config=resolved.values,
source_config=resolved.path,
source_digest=resolved.source_digest,
resolved_digest=resolved.resolved_digest,
route=route.key,
repository=Path(__file__).resolve().parents[2],
)
_write_json(manifest, output_dir / "launch-manifest.json")
operation = getattr(backend, args.command)
try:
result = int(operation(resolved.values) or 0)
except Exception as exc:
if launcher_rank == 0:
_write_json(
{
"schema": "solarwm.run-result.v1",
"status": "failed",
"error_type": type(exc).__name__,
"message": str(exc),
},
output_dir / "run-result.json",
)
raise
if launcher_rank == 0:
_write_json(
{"schema": "solarwm.run-result.v1", "status": "complete", "exit_code": result},
output_dir / "run-result.json",
)
if publication_layout is not None and result == 0:
publication_complete = output_dir / "publication" / "COMPLETE.json"
if not publication_complete.is_file():
raise SolarWMError(
"camera inference returned without a publication completion marker"
)
publication_payload = publication_complete.read_bytes()
run_result_payload = (output_dir / "run-result.json").read_bytes()
complete_payload = (
json.dumps(
{
"schema": "solarwm.camera-inference-run-complete.v1",
"layout": publication_layout.layout,
"run_id": publication_layout.run_id,
"resolved_config_digest": resolved.resolved_digest,
"publication_complete_digest": hashlib.blake2s(
publication_payload
).hexdigest(),
"run_result_digest": hashlib.blake2s(run_result_payload).hexdigest(),
},
allow_nan=False,
sort_keys=True,
separators=(",", ":"),
ensure_ascii=False,
)
+ "\n"
).encode("utf-8")
write_file_create_only(
output_dir / "COMPLETE.json",
complete_payload,
error_type=SolarWMError,
label="camera inference run completion marker",
)
return result
def _config_command(args: argparse.Namespace) -> int:
if args.config_command == "routes":
_write_json([route.key for route in supported_routes()])
return 0
resolved = load_config(args.config, args.set)
reject_inline_secrets(resolved.values)
route = validate_route(resolved.values)
from solarwm.backends import load_backend
load_backend(route.family).validate_config(resolved.values)
payload = {
"source": str(resolved.path),
"source_digest": resolved.source_digest,
"resolved_digest": resolved.resolved_digest,
"route": route.key,
"config": resolved.values,
}
_write_json(payload, args.output)
return 0
def _data_command(args: argparse.Namespace) -> int:
rows = read_index(args.index)
report = inventory(args.index, rows)
if args.data_command == "inspect":
_write_json(
{
"index": str(args.index.resolve()),
"rows": report.rows,
"virtual_occurrences": report.virtual_occurrences,
"ordered_sample_id_digest": report.ordered_sample_id_digest,
"ordered_row_digest": report.ordered_row_digest,
"decompressed_digest": report.decompressed_digest,
}
)
return 0
config = SamplingConfig(
seed=args.seed,
pixel_frames=args.pixel_frames,
shuffle_buffer=args.shuffle_buffer,
)
identity = ReaderIdentity(
rank=args.rank,
world_size=args.world_size,
worker_id=args.worker_id,
num_workers=args.num_workers,
)
plans = list(CanonicalSampler(rows, config, identity).iter_epoch(args.epoch))
selected = plans[: max(0, args.limit)]
_write_json(
{
"full_plan_fingerprint": plan_fingerprint(plans),
"full_plan_count": len(plans),
"shown": [
{
"sample_id": plan.sample_id,
"key": plan.key,
"shard": plan.shard,
"repeat_ordinal": plan.repeat_ordinal,
"start_frame": plan.start_frame,
"source_frame_indices": plan.source_frame_indices,
}
for plan in selected
],
}
)
return 0
def _environment_command(args: argparse.Namespace) -> int:
if args.environment_command == "probe":
_write_json(probe_python_runtime())
return 0
raise SolarWMError(f"unhandled environment command {args.environment_command!r}")
def _run_backend_or_leave(args: argparse.Namespace) -> int:
"""Run a backend command; a rank of a distributed launch that fails exits outright.
A rank that returns from a failure still joins its threads at interpreter shutdown, and one
that never ends leaves it alive: the launcher never sees it fail, and every other
rank waits out the collective timeout. Leaving at once hands the failure to the launcher.
"""
if int(os.environ.get("WORLD_SIZE", "1")) <= 1:
return _run_backend(args)
try:
return _run_backend(args)
except (SolarWMError, OSError, ValueError) as exc:
print(f"solarwm: error: {exc}", file=sys.stderr)
status = 2
except BaseException:
import traceback
traceback.print_exc()
status = 1
sys.stdout.flush()
sys.stderr.flush()
os._exit(status)
def main(argv: Sequence[str] | None = None) -> int:
args = build_parser().parse_args(argv)
try:
if args.command in {"train", "infer", "preencode"}:
return _run_backend_or_leave(args)
if args.command == "config":
return _config_command(args)
if args.command == "data":
return _data_command(args)
if args.command == "environment":
return _environment_command(args)
raise SolarWMError(f"unhandled command {args.command!r}")
except (SolarWMError, OSError, ValueError) as exc:
print(f"solarwm: error: {exc}", file=sys.stderr)
return 2
if __name__ == "__main__": # pragma: no cover
raise SystemExit(main())