alias3-micro-1026 · LiteRT.js (WebGPU int4, int8, int4-b64)

LiteRT.js @litertjs/core 2.6.0 builds of aliasrobotics/alias3-micro-1026 with the weights streamed into WebGPU buffers and the hidden state exposed for System 1 heads. Browser: litert/int4/ (b32, default) — graph + 2.10 GB of int4 weights streamed into WebGPU, 3.66 GB in all. Four quantizations of the same graph, each in its own folder: litert/int4/, litert/int8/, litert/int4-b64/, and litert/fp16/ (native CPU; its 8.43 GB graph is above the 4 GiB WebGPU buffer limit measured here, so it is not a browser build). About 5.3 s per decision in Chrome for the default int4 build. Loaded by ct0-edge with runtime: "litert". Base model: aliasrobotics/alias3-micro-1026 (Alias Robotics), whose card labels the weights a “research preview”. Paper: arXiv:2610.16550. System 1 / System 2 and the bilinear pointer-head interface come from that paper, which draws on Kahneman's dual-process framing and adapts the typed-decision head from the Jev / System One lineage of TypeSafe AI (pointer networks, Vinyals et al., 2015).

Limits. The browser builds (int4, int8, int4-b64) are WebGPU only (no WASM / CPU path for the 4B in the browser) and were validated in Chrome 156 on an Apple M2 Pro. Not picked by ct0-edge's "auto": the GenAI build is about 10× faster (~0.5 s against ~5.3 s per decision).

Speed, fidelity and size in the browser

ONNX · transformers.js | ONNX · GenAI | ONNX · decode-only | MLC q4f32_1 | MLC q4f16_1 | LiteRT.js

Score vs size

Score vs size

Produced by: litert-torch f61d0b7 + our patches (GQA head-repeat as CONCATENATION; the hidden output), the Gated-DeltaNet decomposed, FullyConnected weights quantized with ai_edge_quantizer (int4 block 32, int4 block 64, int8 per channel, or fp16 cast) and, for the integer builds, externalized so they stream as external buffers, packaged by our route-B rebundle. Composite accuracy is yes/no (head) · multiple choice (head) · severity ±1 (head), as in the sibling cards: int4 83.8, int8 87.2, int4-b64 83.1, fp16 85.9.

System 1 per file

System 1 per file

Accuracy is not statistically distinguishable from the original (0 of 32 paired tests, McNemar, before or after Holm correction); individual decisions change on 2.3–26.2% of questions; the test set (50–65 questions per mode) only detects large losses. The fp16 bundle sits within one answer of the original on every read; the 4-bit builds lose multiple choice (the logits read most: 32/57 vs 39/57 for the original), the choice-mode drop that the head read partly recovers.

Dark segment: accuracy when the answer is read straight from the model's logits (the Yes/No, option or digit tokens). Lighter segment: the extra accuracy when the readout head (head.bin, or head_score.bin for severity) reads the hidden state instead. Severity (head_score.bin) is a proof of concept: fitted on 30 synthetic cases; using it as a severity scorer calls for a larger, independently labelled set.

System 1 table on the decision test set (logits / head per mode)

Status vs the original model: 🟢 within 2 answers of the original (or better) · 🟡 3+ below the original, not significant · 🔴 significantly below the original (McNemar p < 0.05) · ⚪ original model (PyTorch bf16), the reference.

file yes/no (65) · logits yes/no (65) · head.bin multiple choice (57) · logits multiple choice (57) · head.bin severity (50) exact · logits severity (50) exact · head_score.bin severity (50) ±1 · head_score.bin changed answers vs original (default read)
Original (PyTorch bf16) ⚪ 54/65 ⚪ 54/65 ⚪ 39/57 ⚪ 46/57 ⚪ 20/50 ⚪ 26/50 ⚪ 47/50 —
litert/int4 🟢 56/65 🟢 53/65 🟡 32/57 🟡 41/57 🟢 19/50 🟢 26/50 🟢 49/50 21.5%
litert/int8 🟢 54/65 🟢 54/65 🟢 41/57 🟢 47/57 🟢 21/50 🟡 21/50 🟢 48/50 8.7%
litert/int4-b64 🟢 57/65 🟢 53/65 🟡 32/57 🟡 42/57 🟢 20/50 🟡 20/50 🟢 47/50 26.2%
litert/fp16 🟢 54/65 🟢 54/65 🟢 38/57 🟢 46/57 🟢 20/50 🟢 26/50 🟢 47/50 2.3%

The default read per mode is yes/no logits, multiple choice head.bin, severity head_score.bin.

Browser details (load, timing, same answers as native)
build fits load time per decision, median · max same answers: browser vs native CPU (default read per mode)
litert/int4 (b32) ✅ 19.6 s 5.3 s · 22.5 s 159/172 (yes/no 64/65 · multiple choice 53/57 · severity 42/50)
litert/int8 ✅ 46.4 s 5.6 s · 30.1 s 161/172 (65/65 · 53/57 · 43/50)
litert/int4-b64 ✅ 23.5 s 4.7 s · 25.4 s 154/172 (63/65 · 46/57 · 45/50)
litert/fp16 ❌ — — graph 8.43 GB > the 4 GiB WebGPU buffer limit; native CPU only

Chrome 156 (WebGPU, Apple M2 Pro, Metal-3 adapter; maxBufferSize 4,294,967,292), @litertjs/core through ct0-edge, the same 172 questions as above, one run per build. Time per decision is the wall time of one prefill per question, including the token-embedding reads. All 23,790 embedding reads per run were served as HTTP 206 (Range), so the full-file fallback was never used. "Same answers" compares the browser's decisions with the native CPU run of the same file; the cause of the differing answers was not isolated.

Files: size, tool, runtime

Folder layout (the baseUrl for ct0-edge is the folder): litert/int4/ (b32, default), litert/int8/, litert/int4-b64/, litert/fp16/ (graph + manifest + embed only; edge-only).

file bytes sha256 (first 16) notes
litert/int4/model.graph.tflite 282,002,344 2f39fb1433640020 the graph (logits and hidden outputs)
litert/int4/model.weights.bin 2,102,394,880 09ca2af6c8c100d8 external int4 FullyConnected weights (group tflite_weights), streamed into WebGPU via loadModelAndWeights
litert/int4/embed.f16.bin 1,271,398,400 084b44680de857f1 token-embedding table (248,320 × 2,560, fp16), read per row via HTTP Range where the host supports it; falls back to fetching the whole file once and slicing if the server ignores Range
litert/int4/manifest.json 34,169 60335f9667f52ea1 shapes, signatures, tokenizer ids
litert/int8/model.graph.tflite 48,766,672 4ee0cae6cfe6e0b3 same layout
litert/int8/model.weights.bin 4,204,789,760 a68375bc45696bcf external int8 FullyConnected weights (group tflite_weights), streamed into WebGPU via loadModelAndWeights
litert/int8/embed.f16.bin 1,271,398,400 084b44680de857f1 token-embedding table, identical to litert/int4/embed.f16.bin
litert/int8/manifest.json 34,168 2cb5aa80acbadf74 same layout
litert/int4-b64/model.graph.tflite 150,602,664 8ab2f7f827fd59d8 same layout
litert/int4-b64/model.weights.bin 2,102,394,880 b92b67ceb005a5a3 external int4 FullyConnected weights (group tflite_weights), streamed into WebGPU via loadModelAndWeights
litert/int4-b64/embed.f16.bin 1,271,398,400 084b44680de857f1 token-embedding table, identical to litert/int4/embed.f16.bin
litert/int4-b64/manifest.json 34,169 6d14f8a5cbc46357 same layout
litert/fp16/model.graph.tflite 8,428,761,008 dce6591d25799ba7 same layout; carries the fp16 weights, so there is no weights file
litert/fp16/embed.f16.bin 1,271,398,400 084b44680de857f1 token-embedding table, identical to litert/int4/embed.f16.bin
litert/fp16/manifest.json 34,161 344ad724cdb4bf71 same layout

Runtime: LiteRT.js (@litertjs/core 2.6.0) in the browser (WebGPU), loaded by ct0-edge; the builds also run on native LiteRT CPU, where the System 1 accuracy table was measured.

folder total dtype produced by runtime
litert/int4/ ⭐ 3,655,829,793 B (3.66 GB) int4, block 32 O+P · litert-torch f61d0b7 + patches 0001/0002, ai_edge_quantizer int4 block 32, LiteRT externalize tool restricted to FullyConnected by a local edit, S route-B rebundle browser WebGPU · native CPU
litert/int8/ 5,524,989,000 B (5.52 GB) int8, per channel same, ai_edge_quantizer int8 per channel browser WebGPU · native CPU
litert/int4-b64/ 3,524,430,113 B (3.52 GB) int4, block 64 same, ai_edge_quantizer int4 block 64 browser WebGPU · native CPU
litert/fp16/ 9,700,193,569 B (9.70 GB) fp16 same, ai_edge_quantizer fp16 cast; no weights file native CPU only

Provenance: O = official tool as-is · O+P = official tool + our patch or wrapper · S = our script (what the official tool does not do). ⭐ = recommended for the browser.

Provenance and export recipe
  • Source checkpoint: aliasrobotics/alias3-micro-1026, revision 61c0e7100c052a252563ceb0711d15dc58bda2d0 (model.safetensors sha256 062756404947caffee60cb1cc399b5c0ff33782abbb4ce3fc100d23e8020a6c1).
  • Exported with litert-torch at f61d0b7 plus local patches: 0001 GQA head-repeat as CONCATENATION (no BROADCAST_TO, required for full WebGPU delegation), 0002 the hidden-state output (post final norm). Gated-DeltaNet decomposed (use_fused_gdn=False): LiteRT.js does not register the fused gated_delta_update op. Short-conv composite off. Signatures prefill_16 and decode; cache length 2,048; pad token <|vision_pad|>.
  • Quantization recipes (ai_edge_quantizer, min_max_uniform_quantize, applied to FullyConnected ops only, all other tensors as exported; the embedding table is kept fp16 in its own file):
    • litert/int4/: 4-bit, symmetric, BLOCKWISE_32
    • litert/int8/: 8-bit, symmetric, CHANNELWISE
    • litert/int4-b64/: 4-bit, symmetric, BLOCKWISE_64
    • litert/fp16/: float_casting to 16-bit float
  • Packaging: the integer builds have their FullyConnected weights moved out of the graph into model.weights.bin (LiteRT external buffers, group tflite_weights) so loadModelAndWeights can stream them into WebGPU buffers; the fp16 build has no integer weights to move, so its weights file is empty and the graph carries the weights (8.4 GB graph, hence edge-only).

Generation (System 2)

System 1 = single-forward typed decisions through the bilinear pointer head (sections above); System 2 = generation, the usual decode loop from the same weights. Terms as in the base model card and the paper.

Generation (System 2)

96-step greedy, 22-token prompt, run natively on LiteRT CPU (Linux x86). Reference = the decode-only fp32 ONNX tokens on onnxruntime CPU (method of record; there is no bf16 original generation run).

Generation details per file
file test free-running = fp32 first divergence teacher-forced notes
litert/fp16 96-step greedy 96/96 — 96/96 free-running cos min 1.00000
litert/int8 96-step greedy 96/96 — 96/96 free-running cos min 0.84839
litert/int4 96-step greedy 3/96 step 2 90/96 teacher-forced cos mean 0.959
litert/int4-b64 96-step greedy 3/96 step 2 90/96 teacher-forced cos mean 0.949

Every 4-bit file diverges at step 2 when free-running (a near-tie after <think>).

Peculiarities

  • graph: two signatures: prefill_16 (16 embedded tokens, outputs hidden) and decode (1 token, outputs logits and hidden); the recurrent / KV cache state is passed in and out as tensors (cache length 2,048)
  • inputs: embeddings (float32, looked up by the runtime from embed.f16.bin), input_pos, the cache tensors
  • head outputs exposed: hidden name: hidden (post final norm, patch 0002)
  • GPU-friendly graph (litert-torch + our patch 0001): the stock qwen3_5 exporter repeats the key heads for grouped-query attention with an op that lowers to BROADCAST_TO, which the GPU delegates do not support, so the graph is split and, with streamed weights, the CPU parts have no weight data; it also ignores the typed use_short_conv_composite flag, and the GPU runtime reads the short-conv composite as its gated variant (prefill logits off by 0.13–0.35 nats; with the composite off it is exact). Patch 0001 repeats the heads by concatenation (numerically identical) and honours the flag; we export with the composite off. Proposed upstream: https://github.com/google-ai-edge/litert-torch/pull/1396.
  • hidden output (our patch 0002): the stock exporter returns logits only. Patch 0002 adds an output_hidden option that returns the post-final-norm hidden state, which prefill and decode emit as a float32 output named hidden; off by default. Checked on a small test model: it equals the PyTorch last hidden state to 1.1e-5, and lm_head(hidden) reproduces the logits exactly.
  • Gated-DeltaNet decomposed: LiteRT.js 2.6.0 does not register the fused gated_delta_update operator (it is unresolved on WASM and fails WebGPU delegation), so we export with the exporter's own decomposed path (use_fused_gdn=False). Effect: the graph uses standard operators only, and many more of them than the fused form (760 operators against 121 in a one-layer test slice).
  • weights streamed from outside the graph: litert-torch writes one flatbuffer with the weights inside, and a model above 2 GiB cannot be loaded that way in the browser. We move the FullyConnected weights out with LiteRT's own externalize tool, restricted by a local edit to FullyConnected only (quantizing and externalizing the depthwise convolution fails with “Unsupported quantization scale size”), and repackage graph, model.weights.bin, embed.f16.bin and manifest with a rebundling script of ours so loadModelAndWeights can stream them. The fp16 build has no integer weights to move, so its graph carries the weights (8.43 GB) and it is not a browser build.
  • quantization recipes (FullyConnected only): ai_edge_quantizer is applied with recipe files of ours that touch FullyConnected ops only (int4 block 32, int4 block 64, int8 per channel, fp16 cast); every other tensor stays as exported. Effect: a ~3.5–5.5 GB browser download, with the accuracy changes in the tables above.
  • embedding table kept outside the graph: the token embedding is a separate fp16 file (embed.f16.bin); the graph takes embeddings as input. ct0-edge reads one row at a time with HTTP Range requests and falls back to downloading the file once if the server ignores Range.
  • weight streaming reader: loadModelAndWeights in LiteRT.js 2.6.0 runs one session per WebGPU partition from offset 0 on a single weight stream and fails with “Stream ended before all weights were loaded”; ct0-edge hands each session its own fresh reader.
  • System 1 head file (head.bin recovered from a truncated release file) and prompt markers: the official head.pt is a truncated streaming zip and cannot be loaded, so head.bin is recovered from it (q exact; k.weight 89% of rows; k.bias lost and estimated as one offset). The marker-to-id mapping is not released, so the prompt markers are plain text, and the official System 1 endpoint needs a fork of SGLang (--systemone-head) that is not in public SGLang. Table and numbers: “Changes to the official head” below.

Our patches and scripts are kept by the authors and are available on request.

Usage

npm i ct0-edge @huggingface/transformers @litertjs/core@2.6.0
import { CT0 } from "ct0-edge";
const ct0 = await CT0.load({
  runtime: "litert",
  litertBaseUrl: "https://huggingface.co/eduardoworrel/alias3-micro-1026-LiteRT/resolve/main/litert/int4/",
});
const p = await ct0.decide(htmlSnippet, "Is this a reflected XSS vulnerability?");

Heads

file size read by shipped
heads/head.bin 2,622,548 B yes/no, multiple choice — bilinear pointer head over the hidden at <decide> and at the last / option positions ✅ in this repo, heads/ (sha256 1138a7745037fb29; the same file as in the other CT0 repos)
heads/head_score.bin 2,622,668 B severity (levels 1–5) — ridge probe on the last-position hidden, same file format (proof of concept: fitted on 30 synthetic cases; using it as a severity scorer calls for a larger, independently labelled set.) ✅ in this repo, heads/ (sha256 82640a99653d49d6; the same file as in the other CT0 repos)
mode ct0-edge default read alternative
yes/no logits Yes vs No (no head) head.bin
multiple choice (1 of K) head.bin logits of each option's first token
severity (1–5) head_score.bin logits of digits 1…5

Hidden positions: <decide> (the final > of its 4 tokens >< dec ide >) and the last prompt position (choice: each option's position).

Changes to the official head

Each decision is read two ways from one prefill: by the logits at the last prompt position (no extra weights) and by the head on the hidden states at <decide> and the key positions (the rows after the first).

official (base card / paper) ours why
logits read not part of the official System 1 (the paper leaves head-vs-logits to future work) compare next-token logits: Yes vs No; first token of each option; digits 1–5 weight-free baseline every runtime supports; the dark segment in the System 1 figure
weights file head.pt on the base repo: truncated streaming zip (no central directory), cut inside k.weight head.bin, recovered from it: q.weight/q.bias 100% (CRC match), k.weight 89% (227/256 rows), k.bias lost → one score offset (+16.27) estimated without labels the official file cannot be loaded
format PyTorch head.pt for the CSI SGLang fork (--systemone-head) head.bin (fp16, JSON header) read by ct0-edge systemone.ts in JS, same math on-device runtimes
yes/no read bilinear pointer head with K = 2 options query at <decide>, key at the last position, plus the offset marker-to-id mapping and prompt row are not published
severity read ordinal scale as a K-option pointer (System 1 severity 44.4% in the paper) new head_score.bin: ridge probe on the last-position hidden (λ = 10), encoded exactly in head.bin's 2-row layout; trained on 30 labelled cases, measured on the 50 severity questions (proof of concept: fitted on 30 synthetic cases; using it as a severity scorer calls for a larger, independently labelled set.) the released head does not order severity
prompt markers reserved ids (mapping unpublished) plain-text markers see the note below the table
temperature / orientation T = 2.0; query at <decide>, keys at </opt> kept as released —

Prompt markers: the paper maps the markers (<state>, <q>, <opt>, </opt>, <decide>) onto reserved token ids. The model has 243 unnamed embedding slots (ids 248077–248319) beyond the tokenizer's last token (248076), but the marker-to-id mapping isn't published, so we write the markers as plain text, which the tokenizer splits into ordinary tokens.

“Extra from the head” in the System 1 figure = how many more questions the head read gets right than the logits read on the same decision test set (ours).

How measured

  • Decision test set (ours): 172 security questions with known answers, written by us (not part of the CT0 release): 65 yes/no, 57 multiple choice (chance 0.272), 50 severity (1–5, held out from head_score.bin); sha256 88e0f6c5…
  • Stats: 95% bootstrap CI (2000 resamples) · McNemar exact, paired per question, vs the original model (PyTorch bf16, revision 61c0e710)
  • Platform: all four bundles on the same platform: native LiteRT CPU on Linux x86, the same 172 questions as the sibling cards, one prefill per question, logits and the hidden output read at the decision positions
  • Browser: the three browser builds were run in Chrome WebGPU on an Apple M2 Pro, one run each, over the same 172 questions (see Browser details); every one loaded and answered all 172
  • Platform control: the same int4 file on a Mac ARM CPU changes 13 of 172 answers (default read per mode), so numerics differ between x86 and ARM on a few close calls
  • Files: eduardoworrel/alias3-micro-1026-LiteRT · base model: aliasrobotics/alias3-micro-1026

Citation

If you use these files, please cite the base model:

@article{ct0_2026,
  title   = {{CT0}: Cybersecurity Transformer},
  author  = {Mayoral-Vilches, V{\'i}ctor and {Alias Robotics}},
  journal = {arXiv preprint arXiv:2610.16550},
  year    = {2026},
  url     = {https://arxiv.org/abs/2610.16550}
}

Base model card: aliasrobotics/alias3-micro-1026.

License: Apache-2.0 (weights, as upstream).

Downloads last month
3
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support

Model tree for eduardoworrel/alias3-micro-1026-LiteRT

Finetuned
Qwen/Qwen3.5-4B
Quantized
(6)
this model

Collection including eduardoworrel/alias3-micro-1026-LiteRT

Papers for eduardoworrel/alias3-micro-1026-LiteRT