feat(compute): add bounded camera job contract
This commit is contained in:
@@ -17,11 +17,13 @@ from rich.table import Table
|
||||
|
||||
from k1link import __version__
|
||||
from k1link.artifacts import write_json_atomic
|
||||
from k1link.compute import prepare_camera_compute_job
|
||||
from k1link.device_plugins.xgrids_k1.analyze import (
|
||||
DEFAULT_STREAM_SUMMARY_MAX_PAYLOAD_BYTES,
|
||||
MAX_STREAM_SUMMARY_PAYLOAD_BYTES,
|
||||
summarize_mqtt_streams,
|
||||
)
|
||||
from k1link.device_plugins.xgrids_k1.archive import discover_legacy_viewer_sessions
|
||||
from k1link.device_plugins.xgrids_k1.ble.gatt import dump_metadata
|
||||
from k1link.device_plugins.xgrids_k1.ble.scanner import scan
|
||||
from k1link.device_plugins.xgrids_k1.ble.wifi_provisioning import (
|
||||
@@ -42,6 +44,7 @@ from k1link.device_plugins.xgrids_k1.protocol.application_authority import (
|
||||
)
|
||||
from k1link.device_plugins.xgrids_k1.usb.snapshot import snapshot as usb_snapshot
|
||||
from k1link.macos_credentials import CredentialDialogError, prompt_wifi_credentials
|
||||
from k1link.sessions import SessionIntegrityError
|
||||
|
||||
app = typer.Typer(
|
||||
name="k1link",
|
||||
@@ -57,11 +60,16 @@ authority_app = typer.Typer(
|
||||
help="One-time local authority administration; never sends a K1 command.",
|
||||
no_args_is_help=True,
|
||||
)
|
||||
compute_app = typer.Typer(
|
||||
help="Bounded, content-addressed handoffs to external compute workers.",
|
||||
no_args_is_help=True,
|
||||
)
|
||||
app.add_typer(ble_app, name="ble")
|
||||
app.add_typer(net_app, name="net")
|
||||
app.add_typer(usb_app, name="usb")
|
||||
app.add_typer(analyze_app, name="analyze")
|
||||
app.add_typer(authority_app, name="authority")
|
||||
app.add_typer(compute_app, name="compute")
|
||||
|
||||
|
||||
class ToolStatus(TypedDict):
|
||||
@@ -579,5 +587,71 @@ def analyze_mqtt_streams(
|
||||
console.print(f"Saved: {out}")
|
||||
|
||||
|
||||
@compute_app.command("prepare-camera-job")
|
||||
def prepare_camera_job(
|
||||
session_root: Annotated[
|
||||
Path,
|
||||
typer.Option(
|
||||
"--session-root",
|
||||
exists=True,
|
||||
file_okay=False,
|
||||
dir_okay=True,
|
||||
readable=True,
|
||||
help="Sealed observation session containing the canonical camera epoch.",
|
||||
),
|
||||
],
|
||||
source_id: Annotated[
|
||||
str,
|
||||
typer.Option("--source", help="Canonical archived camera source id."),
|
||||
],
|
||||
codec_epoch: Annotated[
|
||||
int,
|
||||
typer.Option("--epoch", min=1, help="Physical camera codec epoch number."),
|
||||
],
|
||||
out_root: Annotated[
|
||||
Path,
|
||||
typer.Option(
|
||||
"--out-root",
|
||||
help="Ignored private root where the immutable job directory is published.",
|
||||
),
|
||||
],
|
||||
) -> None:
|
||||
"""Validate and package one archived camera epoch without touching the K1."""
|
||||
|
||||
session = session_root.expanduser().resolve()
|
||||
candidates = {
|
||||
candidate.session_id: candidate
|
||||
for candidate in discover_legacy_viewer_sessions(session.parent)
|
||||
}
|
||||
candidate = candidates.get(session.name)
|
||||
if candidate is None or candidate.session_root != session:
|
||||
console.print("[red]Compute job failed:[/red] session is not a sealed archive")
|
||||
raise typer.Exit(code=2)
|
||||
if all(media.source_id != source_id for media in candidate.media_sources):
|
||||
console.print("[red]Compute job failed:[/red] camera source is not cataloged")
|
||||
raise typer.Exit(code=2)
|
||||
try:
|
||||
job = prepare_camera_compute_job(
|
||||
session_root=session,
|
||||
source_id=source_id,
|
||||
codec_epoch=codec_epoch,
|
||||
origin_epoch_ns=candidate.timeline_origin_epoch_ns,
|
||||
origin_monotonic_ns=candidate.timeline_origin_monotonic_ns,
|
||||
output_root=out_root,
|
||||
)
|
||||
except (OSError, SessionIntegrityError, ValueError) as exc:
|
||||
console.print(f"[red]Compute job failed:[/red] {type(exc).__name__}: {exc}")
|
||||
raise typer.Exit(code=2) from exc
|
||||
|
||||
console.print(f"[green]Compute job ready:[/green] {job.job_id}")
|
||||
console.print(
|
||||
f"Segments: {job.segment_count}; bytes: {job.input_byte_length}; "
|
||||
f"session time: {job.timeline_start_seconds:.6f}–"
|
||||
f"{job.timeline_end_seconds:.6f} s"
|
||||
)
|
||||
console.print(f"Input SHA-256: {job.input_sha256}")
|
||||
console.print(f"Saved: {job.job_root}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app()
|
||||
|
||||
Reference in New Issue
Block a user