Skip to content

Add packed 4-bit SafeTensors weight support - #19

Open
FelixKrall wants to merge 3 commits into
rustnn:mainfrom
FelixKrall:fkrall/packed4-external-weights
Open

FelixKrall wants to merge 3 commits into
rustnn:mainfrom
FelixKrall:fkrall/packed4-external-weights

Conversation

@FelixKrall

@FelixKrall FelixKrall commented Sep 21, 2026

Copy link
Copy Markdown

This PR is used and depended on by RustNN #238 and onnx2webnn #5

Summary

Centralize .webnn external-weight reading and writing in webnn-graph. The resolver now supports versioned packed Int4/Uint4 SafeTensors storage, memory-maps external archives, and shares the same validation rules with a new atomic SafeTensors writer.

Feature additions

  • Support logical Int4 and Uint4 constants stored as packed U8 SafeTensors tensors using the existing rustnn.webnn.packed4=1 metadata contract.
  • Add write_external_weights_safetensors, which validates external declarations and writes ordinary and packed tensors through a same-directory temporary file.
  • Document .webnn, @weights, sidecar discovery, dtype mappings, packed-4-bit storage, naming rules, and compatibility guarantees.

Bugfixes

  • Reject missing or unsupported packed-format markers, invalid storage dtypes, malformed physical shapes and lengths, element-count overflow, missing references, and ambiguous sanitized names.
  • Clean temporary archives when serialization or final installation fails.

Refactors

  • Resolve ordinary and packed tensors during one traversal of one SafeTensors archive.
  • Memory-map SafeTensors and manifest-backed raw weight files instead of reading complete sidecars into heap buffers.
  • Centralize packed-format constants and validation in webnn-graph for reuse by consumers such as RustNN.

Behavioral impact and compatibility

  • The existing resolve_external_weights API remains compatible.
  • Existing archives using rustnn.webnn.packed4=1 and the low-nibble-first layout remain supported.
  • Packed tensors are physically stored as one-dimensional U8 tensors of length ceil(logical_elements / 2); the .webnn declaration remains authoritative for their logical dtype and shape.
  • Ordinary SafeTensors and manifest-backed external weights retain their existing format and discovery behavior.
  • Resolved tensors are copied once into owned InlineBytes; externally backed GraphJson storage is not introduced.

Validation

  • cargo fmt --all -- --check — passed.
  • cargo test external_weights --no-default-features — 13 focused external-weight tests passed.
  • cargo test --all-features — 195 unit tests and the ResNet integration test passed.
  • cargo test --no-default-features — 72 tests passed.
  • cargo clippy --all-targets --no-default-features -- -D warnings — passed.
  • Release builds with all features and with no default features passed.
  • cargo clippy --all-targets --all-features -- -D warnings — reaches a pre-existing clippy::question_mark failure in src/onnx/shape_inference.rs; no changed external-weight code is implicated.
  • git diff --check — passed.

Reviewer focus

  • Confirm the packed-U8 representation and versioned metadata marker are appropriate as the compatibility contract for logical Int4/Uint4 tensors.
  • Review the read-only memory-mapping boundary and atomic temporary-file installation behavior.

Extend the shared external-weight resolver and writer to support packed logical Int4 and Uint4 tensors in versioned SafeTensors archives. Resolve ordinary and packed constants in one memory-mapped traversal, validate names, metadata, storage types, shapes, and lengths, and write archives atomically without an archive-sized output allocation.

Document the .webnn external-weight contract and cover packed, mixed, malformed, sanitized-name, manifest, and raw-weight cases.
## Feature additions

- Add a canonical `.webnn` format reference covering graph versions, shapes, initializers, node options, multi-output operations, output bindings, and `GraphJson` round trips.
- Add a maintained v2 format example and parser → serializer → parser regression coverage.
- Document SafeTensors, packed Int4/Uint4 storage, manifest-backed raw weights, sidecar discovery, mmap ownership, and atomic writer behavior.

## Bugfixes

- Make the example build workflow reconstruct untracked tensor inputs from the checked-in weight archive.
- Update the example workflow to use the current direct JavaScript emitter command.

## Refactors

- Consolidate duplicated format material into canonical documentation pages.
- Rewrite the README, dynamic-dimension guide, ONNX-lowering guide, and example documentation against current source behavior.
- Remove stale model-specific guidance, unsupported quantitative claims, and obsolete agent-oriented files.

## Behavioral impact and compatibility

- No library API, file-format, parser, serializer, or runtime behavior changes are intended.
- The example build script now works without locally retained raw tensor files but produces the same documented artifacts.
- The removed pages are replaced by canonical format and workflow documentation.

## Validation

- `cargo fmt --all -- --check` passed.
- `cargo test --all-features` passed, including 195 library tests and the format-reference and ResNet conversion integration tests.
- `cargo test --no-default-features` passed, including 72 library tests and the format-reference integration test.
- Strict Clippy passed for all targets with all features and without default features.
- Parse, serialize, structural validation, manifest validation, pack, unpack, and JavaScript-emitter smoke checks passed.
- Relative Markdown links, script syntax, line lengths, stale-language searches, and `git diff --check` passed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant