model hub plan - Capsize-Games/spikeforge GitHub Wiki
Focused design for workstream A of the
professional_roadmap.md. Read the roadmap first for the vision, cross-workstream interfaces, and the packaging rules.
Design/spec only. Every claim about the current code is grounded in a file/line reference so a Code-mode agent can execute this file-by-file.
Objective. Make the tool able to find, download, inspect, and import SNN models from the wider neuromorphic landscape — in the dashboard, the CLI, and the WebSocket API — with honest availability, reuse of the existing isolated download worker, checksum verification, and an offline cache. A downloaded model is either mapped to a shipped preset (structure + weights) or explicitly rejected; it is never silently loaded wrong.
Bundled curated catalog first, optional live Hugging Face Hub second.
- A local JSON catalog (
spikeforge_hub/models.json) enumerates known models across snnTorch, NIR, SpikingJelly, Norse, and Lava. It renders fully offline and ships only verified entries; the Hugging Face ingestion path (hub/hf_api.py,hub/download_cli.py, and thehubextra) downloads any vetted repo a user chooses to add. - Live Hugging Face search/download is opt-in behind the
hubextra (huggingface_hub), isolated in one module so its absence is reported, not raised. This mirrors thetonic/eventsprecedent (datasets.py) and the isolated SDK probe precedent (probe.py).
Why not live-search-only: network dependence, no offline story, and no way to keep the honesty rule (an uncurated hit is not a verified, runnable SNN). Why not catalog-only: it forecloses the broader landscape the user explicitly wants to reach. The hybrid keeps the default deterministic and the reach open-ended.
Scope boundary: this workstream handles model artifacts (NIR graphs, SNN checkpoints, framework weights in declared formats) and metadata. It does not train downloaded models, and it does not claim any model runs until compatibility is verified (Phase A3).
| Capability the goal requires | Current reality | Anchor |
|---|---|---|
| Model browser | None; only dataset pickers | datasets.py |
| Model download | None; no download path for weights | model_store.py |
| Isolated download worker | Exists for datasets, reusable pattern |
download_cli.py, downloads.py
|
| Progress + cancel UI | Exists for datasets | DownloadProgress.tsx |
| Checksum/size verification | None | n/a |
| Offline cache dir | Only DATA_DIR / MODEL_DIR
|
config.py |
| External NIR import | Partial, file-path only |
ingest.py, serialization.py
|
| Weight import into a preset | None | model_store.py |
| Compatibility validation | None for foreign artifacts | registry.py |
| HF dependency | Absent | setup.py |
- The existing dataset
DownloadManagerand itsdownload_statepayload keys stay unchanged (downloads.py); the hub gets its own manager and its own state type. -
model_storeremains the local registry for trained models; imported foreign models land in a separate hub cache and are only promoted intoMODEL_DIRafter a successful compat check (config.py). - NIR import keeps raising the typed errors from
errors.py— never a silent partial load. - All new WebSocket fields are additive; the
ClientMessage/ServerMessageliterals only grow (client_message.py,server_message.py).
New package spikeforge_hub/ (one class per file, files under 250 lines):
spikeforge_hub/
__init__.py public API: catalog, search, download, inspect, import_model
entry.py HubEntry dataclass: id, name, framework, kind, source...
catalog.py load/validate models.json; list + filter
models.json the bundled curated catalog (data file)
probe.py isolated huggingface_hub probe (hub extra)
hf_api.py isolated huggingface_hub search/download wrappers
cache.py offline cache dirs, path resolution, size accounting
verify.py checksum + size verification
download_cli.py isolated child-process downloader (mirrors data/download_cli.py)
downloads.py async HubDownloadManager: progress + cancel + verify
inspect.py structural report for a downloaded artifact
compat.py map an artifact to a preset or reject it, with a verdict
weight_map.py load compatible weights into a built preset module
import_model.py orchestrate inspect -> compat -> promote into MODEL_DIR
errors.py typed hub errors
cli.py spikeforge-hub entry point
Modified files:
| File | Change |
|---|---|
setup.py |
add hub extra; add spikeforge-hub console script |
config.py |
add HUB_CACHE_DIR (env SPIKEFORGE_HUB_DIR, default DATA_DIR/hub) |
server/protocol_handlers.py |
route hub_* actions to server/hub_handlers.py
|
server/schemas/client_message.py |
add hub_list, hub_search, hub_download, hub_cancel, hub_inspect, hub_import
|
server/schemas/server_message.py |
add hub_list, hub_search, hub_download_state, hub_inspect, hub_import
|
client/src/useWebSocket.ts |
dispatch the new replies |
client/src/App.tsx |
mount the HubPanel
|
New server modules (kept out of handlers.py to respect
the 250-line limit, exactly like target_handlers.py):
server/hub_handlers.py dispatch_hub + per-action handlers
server/hub_payloads.py JSON payload builders (entry cards, inspect, compat)
server/hub_downloads.py hub download manager singleton + state emitter
New client files: client/src/hubTypes.ts,
client/src/components/HubPanel.tsx, HubEntryCard.tsx,
HubDownloadProgress.tsx, HubCompatBadge.tsx, client/src/hooks/useHub.ts,
client/src/styles/hub.css.
hub/models.json is a versioned list of entries. Each entry is validated into
a HubEntry (hub/entry.py). Required fields:
| Field | Type | Meaning |
|---|---|---|
id |
str | stable slug, e.g. nir/conv_net
|
name |
str | human label |
framework |
str |
snntorch, nir, spikingjelly, norse, lava, hf
|
kind |
str |
nir_graph, state_dict, framework_weights
|
source |
str |
bundled, url, or hf_repo
|
url |
str? | for source=url
|
hf_repo |
str? | for source=hf_repo
|
sha256 |
str? | expected checksum (per file) |
size_bytes |
int? | expected size |
topology |
str? | preset it matches, if any |
input_shape |
str? | declared input contract |
license |
str | declared license |
notes |
str | free text, honesty about provenance |
The catalog loader validates every entry and reports unknown frameworks
rather than dropping them (honesty rule). catalog.list() returns JSON-able
dicts with an additive available flag computed from
hub/probe.py, exactly as
datasets.catalog() does.
Proposed seed entries (≥12 across ≥5 frameworks): the four shipped presets as bundled NIR graphs, a small SpikingJelly reference, a Norse reference, a Lava reference, and a handful of allow-listed HF SNN repos.
Reuse the isolated child-process worker pattern verbatim
(download_cli.py,
downloads.py) so a download never blocks the
FastAPI event loop and can be terminated to cancel in flight.
-
hub/download_cli.pydownloads one entry (or HF repo) into the cache, then verifies checksum and size viahub/verify.py. Non-zero exit on verification failure. -
hub/downloads.pymirrorsDownloadManager:ensure,_poll,_finish,snapshot,cancel, with ahub_download_statepayload carrying{id, status, bytes, total_bytes, verified}. Terminal states stay{idle, downloading, done, cancelled, error}for consistency withdownloads.py. - Progress is measured by cache-dir byte delta (same best-effort approach as
_dir_bytes()); whenhuggingface_hubexposes a total,total_bytesis filled, elsenull(honest unknown). - The offline cache dir is
HUB_CACHE_DIRunderDATA_DIR, gitignored likeMODEL_DIR.
Import is a three-gate funnel; a model must pass each gate or be explicitly rejected with a typed reason.
-
Inspect (
hub/inspect.py): detect the artifact kind (nir_graph,state_dict,framework_weights) and describe its structure without committing. NIR artifacts are summarized withgraph_summary; state dicts are described by key/shape. Unknown or unreadable artifacts are rejected withHubArtifactError. -
Compat (
hub/compat.py): compare the inspected structure against the shipped presets intopology/registry.py. Produce aCompatibilityVerdict:exact,mappable(with the stage mapping), orincompatible(with the specific mismatches named). -
Promote (
hub/import_model.py): only onexact/mappable, build the preset viabuild_topology, load weights viahub/weight_map.py, run a drift check withvalidate, and only then save intoMODEL_DIRwith hub provenance recorded inmeta.
This satisfies "reported and either mapped or explicitly rejected, never
silently loaded wrong". The weight loader reuses load_state_dict semantics
and refuses strict mismatches, reporting missing/unexpected keys.
NIR-only models that match no preset are still runnable: they are registered as
imported NIR graphs (ingest.py)
and executed by the reference interpreter, so import is useful even without a
preset match.
Request/response shapes (all payload additive; errors use the existing
error type per target_handlers.py).
| Action | Request fields | Reply | Reply payload |
|---|---|---|---|
hub_list |
{framework?, kind?, available?} |
hub_list |
{entries: [{...entry, available}]} |
hub_search |
{query, limit?} |
hub_search |
{query, available, results: [...]} |
hub_download |
{name: id} |
hub_download_state (streamed) |
download snapshot |
hub_cancel |
{name: id} |
hub_download_state |
terminal cancelled
|
hub_inspect |
{name: id} |
hub_inspect |
{kind, nodes, keys, notes} |
hub_import |
{name: id, topology?} |
hub_import |
{verdict, promoted, meta, validation} |
hub_search reports available: false with a reason when the hub extra is
absent, rather than erroring. hub_import returns a verdict object even when
incompatible, so the client can render why.
hub/cli.py, mirroring the argparse style of
target_cli.py:
spikeforge-hub list [--framework snntorch] [--available]
spikeforge-hub search <query> [--limit 20]
spikeforge-hub download <id> [--no-verify]
spikeforge-hub inspect <id>
spikeforge-hub import <id> [--topology conv_net]
Every command prints JSON. import exits non-zero when the verdict is
incompatible, so it doubles as a CI gate (same convention as
deploy_exit).
HubPanel.tsx is a browser + downloader:
- Filter bar: framework, kind, availability.
- Entry cards (
HubEntryCard.tsx): name, framework badge, kind, size, license,availablestate, and aHubCompatBadge.tsxshowing the compat verdict. - Download row reuses the existing progress UX
(
DownloadProgress.tsx) and adds cancel + verified state. - Import action surfaces the inspect report and the compat verdict inline; an incompatible model shows the named mismatches rather than a generic failure.
Types live in client/src/hubTypes.ts (no any, 80-column).
-
Deliverables:
hub/entry.py,hub/catalog.py,hub/models.json,hub/probe.py,hub/cache.py,hub/errors.py,hub/__init__.py;config.pyHUB_CACHE_DIR;models.jsonschema validation. -
Acceptance:
python -m spikeforge_hub.cli listprints ≥10 entries withavailableflags; a malformed entry is reported (not silently skipped); nohuggingface_hubimport occurs without the extra.
-
Deliverables:
hub/download_cli.py,hub/downloads.py,hub/verify.py. -
Acceptance:
spikeforge-hub download <id>fetches into the cache, verifies sha256 + size, and exits non-zero on a seeded checksum mismatch; cancellation terminates the child and reportscancelled.
-
Deliverables:
hub/inspect.py,hub/compat.py,hub/weight_map.py,hub/import_model.py. -
Acceptance: a shipped NIR artifact inspects and imports with verdict
exact; a structurally perturbed artifact is rejectedincompatiblewith the mismatched stage named; a matching preset artifact loads weights andvalidate(...)reportswithin_tolerance=Truebefore promotion.
-
Deliverables:
server/hub_handlers.py,server/hub_payloads.py,server/hub_downloads.py, schema additions,hub/cli.py, client panel. -
Acceptance: the six WS actions round-trip over a live connection;
spikeforge-hub importgates on the verdict; the client builds and renders the panel from live payloads; existing datasetdownload_statepayloads are unchanged.
- Licensing / redistribution: whether weights may be bundled is a user decision (decision 1 in the roadmap). Default: metadata-only catalog; fetch weights on demand.
-
Network flakiness / HF API drift: isolated in
hub/hf_api.pybehind thehubextra, exactly likeapi.py. -
Checksum unknowns: when a source publishes no checksum,
sha256isnulland the verifier reports "unverified" rather than passing silently (honesty rule). - Deferred: model push/publish, model cards, and any training of downloaded models. Multi-user caches are out of scope.