Skip to main content
Stub — a human will curate the wording before publishing.

What it does

Every workflow worker that calls cw.models.load("<id>") needs the weight file on disk before the worker container starts. Edge Core’s Model Manager resolves each ID into the local cache at ~/.cyberwave/models/{model_id}/ and serves it read-only into the worker. Sources are tried in priority order; the first one that yields a checksum-verified file wins.

Resolution order

For each required model:
  1. Pre-staged on disk. A file already at ~/.cyberwave/models/{model_id}/ is registered as downloaded_from: prestaged and short-circuits the catalog entirely. Use this for air-gapped sites.
  2. Cyberwave-hosted signed URL (GET /api/v1/mlmodels/{uuid}/weights). Authenticated artefact in our private GCS bucket — preferred when we have mirrored the checkpoint.
  3. Upstream weights URL (download_url / metadata.upstream_weights_url). Public community mirror — used for ONNX models we publish to static.cyberwave.com/ml_models/.
  4. Runtime-managed download (new): when the catalog entry has no URL of any kind but its edge_runtime ships its own weight resolver (today: ultralytics against its GitHub releases hub), Edge Core invokes that resolver in a subprocess and caches the result as downloaded_from: runtime_managed. This is what lets .pt catalog entries (YOLO26, YOLOE-26) work without a mirrored upstream URL.
Bit-rot detection (SHA-256) runs on every ensure_model call. Pre-staged files are never auto-overwritten by catalog updates.

ML runtime extras

The default install keeps the runtime surface lean — no Torch, no Ultralytics, no ONNX. Catalog entries that rely on the runtime-managed download fallback need the matching runtime installed on the edge host:
# Pull in Ultralytics so YOLO / YOLOE .pt entries can self-download
pip install 'cyberwave-edge-core[ml]'

# Other available extras (mirror the cyberwave SDK extras 1:1):
pip install 'cyberwave-edge-core[ml-onnx]'    # onnxruntime
pip install 'cyberwave-edge-core[ml-tflite]'  # tflite-runtime
pip install 'cyberwave-edge-core[ml-stt]'          # whisper.cpp catalog models only
pip install 'cyberwave-edge-core[ml-stt-faster]'   # faster-whisper catalog models only
pip install 'cyberwave-edge-core[ml-all-audio]'    # all speech extras (avoid on Pi)
pip install 'cyberwave-edge-core[ml-all]'          # CI/dev fat bundle only
Voice workflow nodes (Audio Assistant, Wake Word, Fuzzy Matcher) use separate extras — see Edge workflow dependencies. When the required runtime is missing, the resolver raises a RuntimeError listing three workarounds: drop the file under ~/.cyberwave/models/{model_id}/, upload to /api/v1/mlmodels/{uuid}/weights, or set metadata.download_url on the catalog entry.

Pre-staging weights (air-gapped sites)

mkdir -p ~/.cyberwave/models/yoloe-26s-seg
cp /usb-stick/yoloe-26s-seg.pt ~/.cyberwave/models/yoloe-26s-seg/
Edge Core picks up the file on the next sync, infers the runtime from the extension (.ptultralytics, .onnxonnxruntime, …), and writes a sidecar metadata.json so subsequent runs are deterministic.

Self-healing the cache

A failed download leaves an empty cache_dir/{model_id}/ behind — mkdir runs before the network fetch, and any error in the downloader skips the cleanup. Left alone, the SDK in the worker container would later route that directory into torch.load and crash with IsADirectoryError on every restart. Edge Core handles this in three layers:
  • Per-download cleanup (conservative). A failed _download_model / _download_runtime_managed removes the staging directory before the exception propagates, provided it contains only an .dl_*.part partial download, a metadata.json sidecar, or nothing at all. Operator-staged content blocks the prune.
  • Startup sweep (conservative). ModelManager() walks cache_dir/ once at construction time and prunes any per-model subdirectory that is not in the manifest and contains only orphan cruft. This is what unsticks hosts that were already wedged before this code shipped. Operator-staged content blocks the prune.
  • evict_model('foo') (decisive). When there is no manifest entry but cache_dir/foo/ exists on disk, the directory is removed unconditionally. This is the recovery hook for a remote eviction API to call without shell access to the host; an explicit evict is treated as a stronger operator-intent signal than the conservative auto-sweeps.
The SDK has matching logic in ModelManager._resolve_model_path so an orphan dir that survives Edge Core (e.g., on a host that hasn’t picked up this release yet but mounts a fixed SDK) is also recovered inside the worker container. The Ultralytics runtime is the defensive backstop: if a directory ever reaches it (a hand-built UltralyticsRuntime().load(...) call bypassing the manager), it raises a clear FileNotFoundError instead of letting Ultralytics call torch.load(<dir>).

Sidecar fields

Every cached weight has a metadata.json next to it. Fields you’ll see:
FieldMeaning
downloaded_fromartifact_url, download_url, runtime_managed, or prestaged
source_urlPublic URL we fetched (or null for artifact_url and runtime_managed — neither is a re-fetchable persistent URL)
upstream_urlProvenance hint
checksum_sha256SHA-256 of the on-disk bytes; re-verified on every load
runtimeultralytics, onnxruntime, tflite, tensorrt, torch, opencv
See the full module docstring in cyberwave-edge-core/cyberwave_edge_core/model_manager.py.