Instructions to use litert-community/d1-2B-LiteRT with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- LiteRT
How to use litert-community/d1-2B-LiteRT with LiteRT:
# No code snippets available yet for this library. # To use this model, check the repository files and the library's documentation. # Want to help? PRs adding snippets are welcome at: # https://github.com/huggingface/huggingface.js
- Notebooks
- Google Colab
- Kaggle
d1-2B for LiteRT
This repository is a conversion of LiquidAI/d1-2B to LiteRT, made with litert-torch 0.10.0 for the CompiledModel API of ai-edge-litert 2.3.0: on an Apple M4 Max with Metal at float32 precision, one question about the source model card's support ticket takes a median 40.9 ms, and three questions about the same ticket 41.3 ms in one call (2026-11-02).
LiquidAI/d1-2B is a decision model by Liquid AI for text, pictures and video, built on LiquidAI/LFM3-VL-2B. It reads a state (a string, any JSON value, or nothing when pictures are the whole state) and any number of named questions about it, and returns typed answers with their probabilities from one forward pass, without generating tokens: yes or no (noul), one or several of named options (choice), an ordered level (score), a text, number or date read from the state with its span (text, number, date), an estimate with an interval (number with a range), and one object or every instance in a picture (box, boxes). Requests and responses follow system_one() and system_one_batch() of the provider (Liquid AI's model code in the source repository). What it is for and how it scores: the source model card and the blog post.
This repository holds the model as LiteRT files and a Python host for desktops that needs no PyTorch. The decision graph comes as seven files for sequences of up to 128, 256, 512, 1,024, 2,048, 4,096 and 6,144 tokens; as one file that runs eight sequences of up to 128 tokens in one call; and as one file that holds all eight of these graphs over one copy of the weights. Five of the decision graphs also come compiled ahead of time for the NPU of the Qualcomm SM8850, the chip of the Galaxy S26. Seven tower files turn a picture into tokens, one file per canvas shape. A decision graph takes the embeddings of a sequence's tokens and two small tables that say which positions see which, and returns the hidden states after the final norm. The host reads requests with the provider's own code (its api.py, prompt.py and the other files in provider/, unchanged), writes the embeddings, picks the graphs and reads the answers out. The repository also holds the tokenizer, the embedding table, test fixtures and the conversion scripts.
The reference for every check is the provider's own code in float32 on the CPU. On an Apple M4 Max, on the CPU and on the Metal GPU at float32 precision, the files passed every check on the 520 test requests that the reference answers and the host runs: the same most likely option on every question that is not a near tie, and probabilities within 3.68e-5 of the reference.
On two Android phones, the graph files ran through LiteRT 2.2.0 on inputs that the host wrote on a Mac, and the host read the outputs back. One graph call for one question took 70.1 ms on the NPU of a Galaxy S26, with the decision file compiled for its chip, and 736.8 ms on the GPU of a Pixel 11 Pro at float32 precision (see Android).
The source model card's first example, one support ticket and four questions:
questions = {
"refund": {"type": "noul", "instructions": "Is the customer asking for a refund?"},
"team": {"type": "choice", "instructions": "Which team should handle this?",
"criteria": {"billing": "Charges, refunds, invoices", "shipping": "Delivery and parcels",
"technical": "App or site faults"}},
"topics": {"type": "choice", "multiple": True, "instructions": "Which topics does the message raise?",
"criteria": {"billing": "Payments and charges", "delivery": "Shipping and parcels",
"login": "Signing in to the account"}},
"urgency": {"type": "score", "instructions": "How urgent is this ticket?", "criteria": ["low", "medium", "high"]},
}
out = host.system_one("I was charged twice and my parcel never arrived. Please refund one charge.", questions)
The answers' probabilities, rounded to 5 decimals:
| Question | Option | Provider, float32 CPU | This repository, CPU | This repository, Metal (float32) |
|---|---|---|---|---|
refund (noul) |
yes | 0.99690 | 0.99690 | 0.99690 |
team (choice) |
billing | 0.71030 | 0.71030 | 0.71030 |
| shipping | 0.24445 | 0.24446 | 0.24445 | |
| technical | 0.04524 | 0.04524 | 0.04524 | |
topics (several) |
billing | 0.94304 | 0.94304 | 0.94304 |
| delivery | 0.96789 | 0.96789 | 0.96789 | |
| login | 0.00000 | 0.00000 | 0.00000 | |
urgency (score) |
low | 0.23401 | 0.23401 | 0.23401 |
| medium | 0.67318 | 0.67318 | 0.67318 | |
| high | 0.09281 | 0.09281 | 0.09281 |
All three give the card's printed line 1.0 billing ['billing', 'delivery'] medium, and report 189 input tokens and no output tokens. Before rounding, both runs differ from the reference by at most 1.7e-6 on these probabilities.
Files
| File | Bytes | Role |
|---|---|---|
d1-2b_decide_L128_fp16fc.tflite |
2,934,923,056 | Decision graph for sequences of up to 128 tokens. |
d1-2b_decide_L256_fp16fc.tflite |
2,935,382,112 | Up to 256 tokens. |
d1-2b_decide_L512_fp16fc.tflite |
2,938,800,160 | Up to 512 tokens. |
d1-2b_decide_L1024_fp16fc.tflite |
2,940,897,312 | Up to 1,024 tokens. |
d1-2b_decide_L2048_fp16fc.tflite |
2,945,091,616 | Up to 2,048 tokens. |
d1-2b_decide_L4096_qb1024_fp16fc.tflite |
2,953,520,352 | Up to 4,096 tokens, attention in query blocks of 1,024. |
d1-2b_decide_L6144_qb1024_fp16fc.tflite |
2,961,934,864 | Up to 6,144 tokens, attention in query blocks of 1,024. |
d1-2b_decide_b8_L128_fp16fc.tflite |
2,934,942,096 | Eight sequences of up to 128 tokens in one call. |
d1-2b_decide_multi8_fp16fc.tflite |
2,998,830,272 | The eight graphs above as eight signatures of one file, over one copy of the weights. |
d1-2b_tower_G24x24_fp16fc.tflite |
377,919,584 | Picture tower and projector for a patch grid of up to 24 Γ 24. |
d1-2b_tower_G32x48_fp16fc.tflite |
378,411,104 | Up to 32 Γ 48 patches (rows Γ columns). |
d1-2b_tower_G48x32_fp16fc.tflite |
378,411,104 | Up to 48 Γ 32 patches. |
d1-2b_tower_G48x64_fp16fc.tflite |
379,197,536 | Up to 48 Γ 64 patches. |
d1-2b_tower_G64x48_fp16fc.tflite |
379,197,536 | Up to 64 Γ 48 patches. |
d1-2b_tower_G72x96_qb1152_fp16fc.tflite |
381,224,224 | Up to 72 Γ 96 patches, attention in query blocks of 1,152. |
d1-2b_tower_G96x72_qb1152_fp16fc.tflite |
381,224,224 | Up to 96 Γ 72 patches, attention in query blocks of 1,152. |
tables/embed_table.npy |
524,288,128 | The tied token embedding as its bfloat16 bits (uint16), 128,000 Γ 2,048. The host writes its rows into the decision graphs and reads the answers against them. |
provider/ |
The source repository's files that the host reads, unchanged: 12 Python modules (8 imported, constants read from 4) and 6 data files, among them tokenizer.json. |
|
contract.json |
The interface as data: graph inputs and outputs, buckets, the mask, canvases, routing, and the size and SHA-256 of every graph. | |
host/ |
The Python host: d1_litert.py (load_host), d1_host.py (the provider's request handling ported to numpy), d1_batch.py (the eight-sequence graph), multi8.py, d1_layout.py and d1_layout_b.py (sees_q and sees_k), d1_pictures.py, d1_tokenizer.py, lrt_backend.py, and requirements-host.txt. |
|
examples/ |
run_example.py, the source card's examples, and run_example.expected.json, the provider's answers to them. |
|
npu/sm8850/ |
Five decision files compiled ahead of time for the NPU of the Qualcomm SM8850, the chip of the Galaxy S26: for sequences of up to 128, 256, 512 and 1,024 tokens, and for eight sequences of up to 128 tokens (see Galaxy S26 NPU). | |
fixtures/ |
The test requests, the provider's answers to them, and the test pictures (see Provenance). | |
conversion/ |
The export and check scripts, their environments, and a README with the commands in order. | |
REPRODUCE.md |
Sources, environments, steps, and where each number on this card comes from. | |
LICENSE, NOTICE, SHA256SUMS |
The LFM Open License v1.0 (the source repository's file), attribution, and the SHA-256 of every file. |
Each decision file holds the whole text decoder, from the embeddings to the final norm, with its own copy of the weights. A graph computes every position of its length whatever the sequence's length, so the host takes the smallest file that holds a sequence. load_host() (below) compiles only the files a request needs.
Minimal usage
Python: desktop CPU or GPU
hf download litert-community/d1-2B-LiteRT --local-dir d1-2B-LiteRT
cd d1-2B-LiteRT
pip install -r host/requirements-host.txt
python examples/run_example.py --check # CPU (XNNPACK, 8 threads)
python examples/run_example.py --check --accel gpu # GPU at float32 precision (Metal on a Mac)
python examples/run_example.py --check --accel gpu --form multi8 # the eight-signature file
python examples/run_example.py --check --form multi8 --weight-cache multi8.xnnpack_cache # the same on the CPU
The host needs tokenizers, numpy, pillow and ai-edge-litert (2.3.0), pinned with their dependencies in host/requirements-host.txt. It does not need PyTorch or transformers. It was tested with Python 3.12.11 on macOS 27.0.
examples/run_example.py sends the source card's examples: the ticket above, the invoice with a text, a number, a date and an absent value, the photo with a count, one box and every cat, and two tickets in one call. Then it sends a state too long for the largest graph, which the host refuses. The photo (COCO val2017 000000039769.jpg, 640 Γ 480) is not in this repository: the script downloads it into memory and checks its SHA-256 before use. --check compares the answers with examples/run_example.expected.json, the provider's own answers in float32 on the CPU: probabilities, P(found) and P(yes) within 1e-4, box edges within 0.002, box confidence within 0.02, and everything else equal (the picked options, texts, spans, numbers, dates, counts and token counts), and the refusal. All four commands print CHECK PASS on the Apple M4 Max. On the CPU, --form multi8 needs --weight-cache: the first load writes the XNNPACK weight cache there (5,868,852,600 bytes) and later loads read it.
In your own code:
import sys
sys.path.insert(0, "host") # run from the repository root
from d1_litert import load_host
questions = {
"refund": {"type": "noul", "instructions": "Is the customer asking for a refund?"},
"team": {"type": "choice", "instructions": "Which team should handle this?",
"criteria": {"billing": "Charges, refunds, invoices", "shipping": "Delivery and parcels",
"technical": "App or site faults"}},
}
with load_host(".", accelerator="cpu") as host: # accelerator="gpu": the GPU at float32 precision
out = host.system_one("I was charged twice and my parcel never arrived. Please refund one charge.", questions)
print(round(out["answers"]["refund"]["noul"], 2), out["answers"]["team"]["choice"], out["usage"])
It prints 1.0 billing {'input_tokens': 88, 'output_tokens': 0}.
system_one(state, questions, media=None) and system_one_batch([(state, questions, media), ...]) are the provider's calls. media lists pictures: PIL images, file paths or {"type": "image", "path": ...}. load_host() builds the host from the files that are present. With form="files" (the default) it compiles a decision file when a sequence first needs it and keeps at most keep of them compiled, closing the least recently used one; with form="multi8" it compiles the eight-signature file once. On the GPU it sets GpuOptions(enforce_f32=True), and for form="multi8" also constant_tensor_sharing=True.
How the host works
contract.json holds these rules as data, and host/d1_host.py implements them with the provider's own code at the revision below.
- The provider's
prompt.pyrenders a request: the state, then the questions, at most 16 to a sequence (the model'sset_size); a request with more questions runs as several sequences over the same state. The tokenizer is the source repository'stokenizer.json, read withtokenizers. embedsfloat32[1, L, 2048]: the rows oftables/embed_table.npyat the sequence's token ids, widened from bfloat16 to float32 (exact), the tower's tokens at the picture positions, and zeros on the padding.sees_qandsees_kfloat32[1, L, 66]say which positions see which: per position, whether it is in the state, and which question it belongs to. The 6 global-attention layers add (sees_q Β· sees_kα΅ β 1) Β· 30000 to their scores, which is the provider'ssetmask: the state sees the whole state and every question, and a question sees the state and itself. The 5 sliding-window layers (window 256) and the 13 convolution layers are causal, as in the provider's code.- The graph's
decidesignature returnshiddenfloat32[1, L, 2048], the hidden states after the final norm. The positions are constants in the graph, from zero. - The provider's
parsing.pyandcalibration.pyread the answers out: each answer slot's hidden state times the rows of the tied table for its option tokens, the provider's calibration temperature, and its pointer reads for spans and boxes. - The host takes the smallest decision file that holds a sequence. A sequence longer than 6,144 tokens is refused (
ValueError), never cut. A batch of at least 8 sequences of at most 128 tokens each runs on the eight-sequence graph, 8 sequences a call; any other sequence runs alone. - A picture goes through the provider's processor, ported to numpy in
host/d1_pictures.py: its patches go to the smallest tower canvas that holds its patch grid, and the tower's tokens take the picture's positions in the sequence.
Measured agreement and speed
Agreement with the provider's float32 code
The reference is the provider's code at the revision below, in float32 on the CPU (transformers 5.19.0, torch 2.14.1). The test set has 526 requests: 520 that the reference answers and the host runs, five that the reference refuses, and one that the reference answers but no graph holds. They are 220 requests from the transfer-v4 development file of github.com/jaredpalmer/kev, 144 SemIf items, the examples of the d1-2B and d1-3B model cards, and requests written for these conversions, with text, pictures and the speed workloads below. Six control requests each change one fact of a test request, and must move the answer that the fact decides by more than 0.02: this shows that the checks see the input.
The tolerance, set before the runs: (a) for yes/no, choice and score questions, the same most likely option outside near ties, max |Ξp| at most 0.02 and mean |Ξp| at most 0.002; (b) for text, number and date reads, |ΞP(found)| at most 0.02, the same found side, span and value; (c) for estimates, the distribution within 0.02 (mean 0.002) and the same median, p10 and p90; (d) for boxes, P(found) within 0.02, edges within 0.002 and confidence within 0.02, and the same count; (e) every control request moves its answer by more than 0.02, as the reference's does.
| Files | Accelerator | Requests | (a) questions, max |Ξp| | (b) reads, max |ΞP(found)| | (c) estimates, max |Ξp| | (d) boxes, max edge / confidence | (e) controls |
|---|---|---|---|---|---|---|---|
| Single decision files and towers | CPU (XNNPACK, 8 threads) | 520 | 587, 3.4e-5 | 235, 1.8e-6 | 66, 8.8e-5 | 88, 0.0005 / 0.0022 | 6/6 |
| Single decision files and towers | Metal, float32 | 520 | 587, 3.47e-5 | 235, 5.1e-6 | 66, 8.7e-5 | 88, 0.0005 / 0.0022 | 6/6 |
| Eight-sequence file, 8 a call | CPU | 382 | 386, 1.09e-5 | β | β | β | 1/1 |
| Eight-sequence file, 8 a call | Metal, float32 | 382 | 386, 9.08e-6 | β | β | β | 1/1 |
| Seven length graphs in one file, constant tensor sharing | Metal, float32 | 520 | 587, 3.68e-5 | 235, 2.6e-6 | 66, 8.6e-5 | 88, 0.0005 / 0.0022 | 6/6 |
Every question passed (a) to (e), and all 8 near ties (two options within 0.02 of each other in the reference) kept their order. The eight-sequence rows are the 382 requests whose sequences fit 128 tokens. The last row was measured on a seven-signature file of the same graphs: the eight-signature file in this repository gives its outputs bit for bit on Metal with sharing, and its eighth signature the outputs of the eight-sequence file, on Metal and on the CPU.
On the phones, the host on the Mac read the phones' outputs and scored them in the same way, on a device set of 42 requests with every kind of question:
| Phone | Files | Accelerator | Requests | (a) questions, max |Ξp| | (b) reads | (c) estimates | (d) boxes, max edge | (e) controls |
|---|---|---|---|---|---|---|---|---|
| Pixel 11 Pro | Seven length graphs in one file, sharing | GPU, float32 | 42 | 61, 3.77e-5 | 52, 9.9e-7 | 22, 5.7e-5 | 38, 0.0005 | 6/6 |
| Pixel 11 Pro | Single decision files and towers | CPU (XNNPACK, 4 threads) | 42 | 61, 3.39e-5 | 52, 3.6e-6 | 22, 5.7e-5 | 38, 0.0005 | 6/6 |
| Pixel 11 Pro | Eight-signature file, eight-sequence graph | GPU, float32 | 64 | 64, 2.35e-6 | β | β | β | β |
| Galaxy S26 | The NPU files of npu/sm8850/, towers of the Pixel 11 Pro's GPU |
NPU | 35 | 42, 0.00474 | 52, 0.004 | 16, 0.0074 | 18, 0.0010 | 6/6 |
| Galaxy S26 | The NPU eight-sequence file | NPU | 64 | 64, 0.00716 | β | β | β | β |
| Galaxy S26 | d1-2b_decide_L128_fp16fc.tflite, sharing |
GPU, float32 | 16 | 16, 1.72e-6 | β | β | β | 1/1 |
| Galaxy S26 | d1-2b_decide_L128_fp16fc.tflite |
CPU (XNNPACK, 4 threads) | 16 | 16, 1.23e-6 | β | β | β | 1/1 |
Every question on the phones passed (a) to (e) on the questions that the subset holds. On the Pixel 11 Pro's GPU, the 16 sequences of 128 tokens gave the same output bytes on the eight-signature file as on the seven-signature file. The 64 requests are the 64-states workload below. On the Galaxy S26's NPU, the decision files ran the 35 requests of the device set that fit 1,024 tokens (the other 7 need a longer file, which does not run on that NPU: see Galaxy S26 NPU), with the Pixel 11 Pro GPU's tower outputs for the pictures. The NPU runs the graphs in float16: its mean |Ξp| on the questions was 0.000429, against the 0.002 of (a). On the Galaxy S26's GPU and CPU, the subset is the 16 requests of the 128-token file, with one control request; on its GPU, the towers gave the Mac's outputs within 6.7e-6 on the same pictures.
Speed on a Mac (Apple M4 Max)
An Apple M4 Max (macOS 27.0), ai-edge-litert 2.3.0 through the Python CompiledModel API. A time is the median of 20 requests after 5 warm-up requests, through the host from the request to the answers: preprocessing, every graph call (writing the inputs, running, reading the whole output back) and the read-out. The workloads are those of the source card's speed table, on the source card's requests where it has them.
| Request | Files | Metal, float32 | CPU, 8 threads |
|---|---|---|---|
One question (the card's ticket and refund, 43 tokens) |
L128 | 40.9 ms | 154.4 ms |
3 questions (the same ticket and state: refund, team, urgency) |
L128 | 41.3 ms | 154.3 ms |
| 5K-token state and 1 question | L6144 | 1,980.9 ms | 6,837.6 ms |
| 384 px picture and 1 question | tower G24x24, L256 | 96.4 ms | 353.2 ms |
Box (the card's photo and remote) |
tower G32x48, L512 | 203.2 ms | 754.7 ms |
Boxes (the card's photo and every_cat) |
tower G32x48, L512 | 205.5 ms | 755.7 ms |
| 64 states in one call (64 tickets, one question each) | eight-sequence file, 8 calls | 1,989.0 ms | 7,466.9 ms |
The 64 states take 1,989.0 ms in one call on Metal, 32.2 states a second. Compiling a decision file on Metal at float32 took 12.95 to 21.11 s and a tower file 1.67 to 2.19 s, once per process.
Speed on Android phones
On the GPU and the CPU, a test app ran the graph files through the Kotlin CompiledModel API of LiteRT 2.2.0. On the Galaxy S26's NPU, a test runner on the C API of LiteRT 2.2.0 ran the NPU files, outside the app. Both ran on inputs that the host wrote on the Mac and time one graph call the same way: writing the inputs, running and reading the output back. A time is the median of 10 calls after 2 warm-up calls in the app (a tower: 20 after 5; the 5K-token state: 3 calls; the 64 states: the median of 2 requests after 1), and the median of 20 calls after 5 in the runner (the 64 states: the median of 3 requests after 1). The host's own work, which ran on the Mac, is not included. A picture is two calls, the tower and then the decision graph, timed apart and added up; on the Galaxy S26 the tower ran on the GPU, in the app.
| Request | Pixel 11 Pro, GPU (float32) | Pixel 11 Pro, CPU (4 threads) | Galaxy S26, NPU (towers: GPU, float32) |
|---|---|---|---|
| Load (compile) | 25.2 s, all eight signatures | 7.2 s, the L128 file (2.8 s from its weight cache) | 0.7 to 2.1 s, the L128 file |
| One question | 736.8 ms | 1,300.8 ms | 70.1 ms |
| 3 questions | 740.5 ms | 1,297.0 ms | 73.3 ms |
| 384 px picture and 1 question | 1,321.4 ms (274.4 + 1,047.0) | 2,963.5 ms (628.6 + 2,334.9) | 221.8 ms (112.2 + 109.6) |
| Box | 2,531.0 ms (759.1 + 1,771.9) | 701.8 ms (452.8 + 249.0) | |
| Boxes | 3,355.8 ms (819.2 + 2,536.6) | 723.9 ms (473.6 + 250.3) | |
| 5K-token state and 1 question | 38.4 s | does not run (see Galaxy S26 NPU) | |
| 64 states in one call | 27.3 s (8 calls) | 102.0 s (64 calls) | 4.8 s (8 calls) |
Empty cells were not timed: the Pixel 11 Pro's CPU ran those graphs only in the checks above, whose call times are not in this table. On the Pixel 11 Pro's GPU, the calls of up to 6,144 tokens were timed on a file of the seven length graphs with sharing, which gives the same outputs as the eight-signature file (see Measured agreement); the load and the 64 states are the eight-signature file's. On the Galaxy S26, the NPU load creates the compiled model from the file (four runs of the L128 file). Its GPU at float32 loaded the same length in 27.5 s and took 366.4 ms for one question; its CPU (XNNPACK, 4 threads) took 675.9 ms for one call of the L128 file in a check run, with the phone warm and its CPU frequency capped.
Android
The files that run best differ by device. The host picks the GPU and CPU files by form= and accelerator=; it does not run the NPU files:
- Pixel 11 Pro: the eight-signature file with constant tensor sharing on the GPU,
form="multi8". One compile of 25.2 s loads all eight graphs over one copy of the weights, so every request shape is ready after one load. - Galaxy S26: the decision files compiled for its NPU (
npu/sm8850/, see Galaxy S26 NPU), with the towers on the GPU at float32. On its GPU, the eight-signature file did not load: during its compile, the phone's available memory fell to 1,488,000 kB, under the 1,500,000 kB at which our test stops a run. The 6,144-token file did not load on its GPU either: 21.3 s into its compile, the available memory read 1,405,316 kB. - Mac: the single files, with the eight-sequence file for many short requests in one call. A process compiles only the decision files its requests need.
Both phones ran the GPU through LiteRT's OpenCL backend at float32, and the Galaxy S26 its NPU through LiteRT's dispatch delegate, one node holding the whole graph. The Pixel 11 Pro ran Android 17 (API level 37); the Galaxy S26 (SM-S942Q) Android 16 (API level 36). This repository has no Android host: an app needs the steps of the host contract above, in its own language.
Galaxy S26 NPU
npu/sm8850/ holds five decision files compiled ahead of time for the NPU of the Qualcomm SM8850 with ai-edge-litert 2.2.0 and Qualcomm's QAIRT 2.47 (conversion/npu/): for sequences of up to 128, 256, 512 and 1,024 tokens, and the eight-sequence file. These are the files that ran on the Galaxy S26, with the SHA-256 that the phone read:
| File | Bytes | SHA-256 |
|---|---|---|
d1-2b_decide_L128_fp16fc_SM8850.tflite |
2,970,355,376 | de6abc09c34edc1ceeacc6c1164370493c9f8b6e7c13a6ba733d77b06019f596 |
d1-2b_decide_L256_fp16fc_SM8850.tflite |
2,976,356,016 | 873ea1d67bc13971d12580a320a27baf196b2f7b0db7bcc88e9460256287a9e1 |
d1-2b_decide_L512_fp16fc_SM8850.tflite |
3,000,534,704 | c5e1b297b1c7cffe62053d623d9c919dc2dd51a4c857686a6db235d6288efb08 |
d1-2b_decide_L1024_fp16fc_SM8850.tflite |
3,031,398,064 | d08d75fc489f1e9525f48a2767cddea4c3cf393da0df07e1f14516d269d6aade |
d1-2b_decide_b8_L128_fp16fc_SM8850.tflite |
3,087,439,584 | 12ff74c978968b206d5e46be4a6aa5a718a8f1f4efadb4c5c271c8f0f7ef0c4c |
Each file is one dispatch operator that holds the whole graph, and the phone ran every node on the NPU, in float16. The agreement and the times are in Measured agreement and speed above. The longer decision files do not run on this NPU: the phone refused to create the 2,048-token file's context (QNN error 1002, for a context it estimated at 3,883,471,104 bytes, more than one compute-DSP session holds), the 4,096-token file did not compile within 32 GiB of memory, and the 6,144-token file was not compiled. So on the Galaxy S26 a sequence of more than 1,024 tokens needs the GPU, where the 2,048- and 4,096-token files were not run and the 6,144-token file did not load (see Android): the 5K-token state does not run on this phone.
The host of this repository does not run these files. On the Galaxy S26, a test runner on the C API of LiteRT 2.2.0, outside an app, loaded each into a CompiledModel with the NPU accelerator and Qualcomm's dispatch library (libLiteRtDispatch_Qualcomm.so), and ran it on inputs laid out on the Mac by the scripts of this repository: the times and the agreement above come from that runner. An Android app loads them the same way, through LiteRT's CompiledModel with the NPU accelerator and that dispatch library; no app ran them in these checks.
GPU precision
Run the GPU at float32 precision: the host sets GpuOptions(enforce_f32=True) for accelerator="gpu". At the Metal GPU's default precision (float16), the answers move outside the tolerance: max |Ξp| 0.115 on the questions, |ΞP(found)| 0.063 on the reads, 0.2 on the estimates with one estimate question failing its median, and up to 0.27 on box edges and 0.91 on box confidence.
Limits
- The model never generates text. A sequence holds at most 6,144 tokens: the state, its pictures and up to 16 questions. A longer one is refused, never cut. The source model card gives a context length of 128K tokens.
- The host reads pictures only: a video is refused (
NotImplementedError), and so is a picture or clip inside a question's instructions. - A picture's patch grid must fit one of the seven tower canvases; a picture that fits none is refused, and its aspect ratio is never changed.
- Run the GPU at float32 precision. Its default precision is outside the tolerance (see GPU precision).
- The NPU files are compiled for the Qualcomm SM8850 only, and the host of this repository does not run them (see Galaxy S26 NPU). On the Galaxy S26's NPU a sequence holds at most 1,024 tokens; a longer one runs on its GPU, where the 2,048- and 4,096-token decision files were not run, and the 5K-token state does not run on that phone (see Galaxy S26 NPU).
- Each compiled decision file holds its own copy of the weights. On the CPU,
form="multi8"needs a weight cache file (5,868,852,600 bytes). - The agreement numbers measure how closely this conversion follows the provider's code, not task accuracy. Accuracy and calibration were not measured again; see the source model card.
- Measured on one Mac (Apple M4 Max), one Pixel 11 Pro and one Galaxy S26. Other machines and GPUs were not measured.
Provenance, conversion and license
- Source:
LiquidAI/d1-2Bat revision10e7671d44e4b37700020b1eed3f594c5f2af0b4. Base:LiquidAI/LFM3-VL-2B. Both are licensed under the LFM Open License v1.0. - Model: the LFM3 text decoder (24 layers: 13 gated short convolutions, 5 sliding-window attention layers with a window of 256 and 6 global attention layers; hidden size 2,048; 128,000 embedding rows; tied embeddings), and the LFM3-VL picture encoder (14 layers, width 1,024, 16 Γ 16 pixel patches merged 2 Γ 2) with its projector.
- Decision graphs: the provider's LFM3 computation (its
modeling_lfm3.py) written out in plain PyTorch for export, with thesetattention mask built inside the graph fromsees_qandsees_k, exported with litert-torch 0.10.0 from the weights in float32 (transformers 5.14.1, torch 2.14.1), then quantized with ai-edge-quantizer 0.10.0: the decoder's 153 weight matrices are stored in float16 and read through DEQUANTIZE operators, and activations stay in float32. A decision graph has 2,062 to 2,456 operators. None is CUSTOM, no tensor is int64, and no tensor has a rank above 4. - Tower graphs: the picture encoder and projector for a fixed canvas of patches, with the picture's patches at the top left and a mask of the real ones; FULLY_CONNECTED weights in float16.
- Table: the tied embedding's bfloat16 bits as they are in the checkpoint.
- Scripts:
conversion/holds the export and check scripts, with their environments and a README of the commands.REPRODUCE.mdlists the sources, the steps and where each number comes from. One file of each kind, rebuilt on 2026-11-02 from a clean copy of these scripts, has the same SHA-256 as the shipped file, and so does a compile of the 128-token NPU file. - Test data:
fixtures/holds the test requests, the provider's answers and the test pictures. The SemIf records (MIT,fixtures/LICENSE-SemIf-MIT.txt), the examples of the d1-2B and d1-3B model cards and the requests written for these conversions are included with their text. The 223 requests made from the transfer-v4 development file of github.com/jaredpalmer/kev (220 of its lines and 3 requests made from them) quote public datasets under their own licenses, so they are listed by reference, andfixtures/rebuild_requests.pyrestores them from that file at tagkev-1.0. The pictures are 5 CC0 photographs from Wikimedia Commons and 18 synthetic pictures; the COCO photo is listed by URL and SHA-256.
License: d1-2B and LFM3-VL-2B are licensed under the LFM Open License v1.0 (LICENSE, a copy of the source repository's file). This repository is released under the same license, except the SemIf fixture records (MIT). Note the license's limit on commercial use by organizations with annual revenue of US$10 million or more. Attribution is in NOTICE.
Changes from the original work. The weights were converted from the checkpoint's safetensors (bfloat16) to LiteRT flatbuffers: float16 FULLY_CONNECTED weights, float32 for the rest. The decision model was split into graphs of fixed lengths, an eight-sequence graph and a file that holds them all, with the attention mask built inside the graph from two position tables. The picture encoder was split into graphs of fixed canvases. The embedding table ships separately and the host writes its rows. The tokenizer and the provider's request code are unchanged. The host ports the rest of the provider's request handling and picture preprocessing to numpy. This repository is not affiliated with Liquid AI.
- Downloads last month
- 233