bitnet_flutter_ai
Run Microsoft BitNet b1.58 2B-4T locally on Android, iOS, desktop, and Web (WASM).
0.2.0-beta.1
IbrahimElmourchidi/bitnet_flutter_ai
What this package is like to depend on
Last release 13 days ago
11 Aug 2026
Too new to tell
only 2 dated releases
Unknown
no stable releases
Nothing withdrawn
no release was ever pulled
4 months old
2 releases · first in 2026
2 releases in the last 12 months
see the full history below
Release timeline
2 releases · Apr 2026 to Aug 2026Releases
latest 2-
0.2.0-beta.111 Aug 2026 pre-releaseRelease notes
Open source →Bumps to 0.2.0-beta.1 and finalizes the CHANGELOG Unreleased section
covering Web WASM inference, the PrismML Q2_0 backend, the 10-model
catalog, and iOS support (dual-backend xcframeworks + SPM layout).
Also refreshes README.md, which still described iOS as Planned and
had stale build instructions.Release notes
Open source →Added
- iOS support (device + simulator), both backends. Ships prebuilt
bitnet_bridge.xcframework(static archive merged with llama/ggml) andprism_bridge.xcframework(self-contained dynamic framework) inios/Frameworks/, coveringarm64device andarm64/x86_64simulator slices. The two frameworks export the samebn_*names over incompatible ggml copies, so they resolve differently at runtime: bitnet throughDynamicLibrary.process(), prism through.open(path). The plugin moved to the SwiftPM layout (ios/bitnet_flutter_ai/Sources/) so CocoaPods and SPM build the same sources, withPackage.swiftadded.bitnet_force_link.mkeeps the vendored static archive'sbn_*symbols — reached only via FFIdlsym, so nothing references them at link time — from being dead-stripped, without resorting to-force_load(which breaks clean Xcode builds because the linker path is validated before the Copy-XCFrameworks phase stages it). Verified end-to-end in the simulator with both the bitnet (I2_S) and prism (Q2_0) backends. ModelCachemodel-picker helpers. AddedisCached,hasPartialDownload, andcachedStatusso a model-picker UI can show download state without triggering a load. A.verifiedstamp is written after a successful SHA-256 check so later loads skip re-hashing a multi-GB file, hashing now runs on a background isolate instead of the UI thread, andonProgressis throttled to 0.5% steps.- Expanded model catalog (10 models). Added six TII models that ship
pre-built I2_S GGUFs and run on the existing bitnet.cpp native stack with no
rebuild —
Falcon3-{1B,3B,7B,10B}-Instruct-1.58bitandFalcon-E-{1B,3B}-Instruct— plus three PrismMLTernary-Bonsai-{1.7B,4B,8B}models on a new second backend (below). All are SHA-256 pinned inBitNetCatalog.all;compatibleWith(ram)now surfaces a real size ladder.contextLengthis capped at 4096 per entry (the native layer allocates the full KV cache up-front, and history is trimmed separately). - Per-model chat templates. New
lib/src/session/chat_templates.dartmaps a model'sChatTemplateFamily(bitnetLabeled/falconTagged/chatml/chatmlQwen3) to its prompt formatter and end-of-turn stop markers. The wrong template produces degenerate output (the "runaway!" failure), soBitNetSessionand both engines now dispatch per model instead of hardcoding BitNet's format. Verified: Falcon3-1B coherent viafalconTagged. - Second native backend: PrismML Q2_0 (Ternary Bonsai). PrismML's models use
a
qwen3/Q2_0ggml format incompatible with bitnet.cpp, so they route to a new self-containedlibprism_bridge.so(native/prism_bridge.ccover the PrismML llama.cpp fork, built bytool/build_{linux,android}_prism.sh). It exposes the samebn_*C ABI as the bitnet bridge (zero Dart FFI changes) and a version script exports onlybn_*so its modern ggml symbols can't clash with the bitnet stack loaded in the same process.BitNetModelInfo.engineBackendselects the library at load time. Verified: Bonsai-1.7B coherent viachatmlQwen3. Not available on web yet (no WASM build of the fork). - Working Web (WASM) inference. The web engine is now backed by a real
WebAssembly build of bitnet.cpp (ggml + llama.cpp) + the embind bridge
(
native/bitnet_bridge_wasm.cc), produced bytool/build_web.sh. The module runs inside a Web Worker (web/bitnet_worker.js) — the analogue of the native inference isolate — so model loading and token generation never block the UI thread;web/bitnet_glue.jsis a main-thread async proxy that exposeswindow.BitNetWasm. The worker downloads the GGUF (provisionModel, Cache-API persisted, streamed into the in-memory FS) and frees it afterbn_init. Verified end-to-end against the real 1.1 GB I2_S model (coherent output, clean EOS stop). Single-threaded scalar (~1–2 tok/s for the 2B model); threading and awasm_simd128kernel are documented speed follow-ups. Desktop browsers only — the 1.1 GB model needs multi-GB per-tab memory. - Quiet WASM logging. The bridge installs a
llama_log_setfilter on web that drops everything belowERROR. llama.cpp's INFO/WARN chatter (the full GGUF metadata dump, a per-special-token "not marked as EOG" line, …) otherwise crosses the WASM→JS boundary as thousands of stack-tracedconsole.errorcalls during load, which froze the tab/devtools on its own. DeviceProfilecompiles on web. Platform detection moved behind a conditional import (platform_info_impl.dart/platform_info_web.dart) so the file no longer importsdart:ffi/dart:ioon web (which failed to compile). Web readsnavigator.hardwareConcurrencyfor the core count.- Mobile-first SDK surface. Added the public types
BitNetCatalog,BitNetModelInfo,BitNetCapability,BitNetQuantization,DeviceProfile,BitNetSession,ChatRole,ChatTurn,BitNetRag,RagAnswer,KnowledgeSource,KnowledgeChunk,RetrievedChunk,AssetKnowledgeSource,InMemoryKnowledgeSource,Bm25Retriever, andTextChunker. - Catalog & compatibility.
BitNetCatalog.all,BitNetCatalog.byId,BitNetCatalog.withCapability, andBitNetCatalog.compatibleWith(ramBytes)for picking a model that fits the device. - Device profile & speed estimate.
DeviceProfile.current()returns OS / ABI / cores / RAM, andDeviceProfile.estimateTokensPerSecond(model)produces a heuristic tok/s anchored at 12.5 tok/s on an 8-core x86_64 reference. - Chat session.
BitNetSessionprovides streamingchat(),setSystemPrompt(),clearHistory(), plus convenience helperssummarize(text, {style})(paragraph/bullets/tldr) andassist(question, {context}). - On-device RAG.
BitNetRagindexesAssetKnowledgeSource(assetPaths: [...])(orInMemoryKnowledgeSource) withBm25Retriever+TextChunkerand exposesask()returningRagAnswer{answer, citations}. - Android prebuilts.
android/src/main/jniLibs/{arm64-v8a,x86_64}/shipslibbitnet_bridge.so,libllama.so,libggml.so. Reproducible viatool/build_android.sh(NDK 28). - Example app.
example/lib/main.dartnow demos every public API across four tabs: Device, Chat, Summarise, Ask (RAG) — backed byexample/assets/knowledge/bitnet_faq.md. - End-to-end Linux verification. The full pipeline (bitnet.cpp →
libllama.so→libbitnet_bridge.so→ Dart FFI → token stream) is now validated on x86_64 Linux. A Ryzen 7 5700U produces ~12 tok/s for the BitNet b1.58 2B-4T I2_S model with 749 ms cold model load. tool/dart_smoke.dart— Dart-only CLI integration test that drivesNativeLibrarydirectly against a local GGUF, useful for iterating on the FFI bridge without going through Flutter.native/smoke_test.c— standalone C harness for verifying the bridge before Dart is in the loop.@visibleForTesting BitNetModel.forTest— lets unit tests build models with arbitrary (or computed) SHA-256 fixtures now that the canonical hash is pinned.
Changed
- C bridge updated for bitnet.cpp's pinned llama.cpp. Renamed
llama_model_load_from_file→llama_load_model_from_file,llama_model_free→llama_free_model,llama_model_n_ctx_train→llama_n_ctx_train. Switchedllama_batch_get_oneto its 4-argument form and adaptedllama_model_meta_val_strto the buffer-output variant (the bridge now caches the result in a per-context scratch buffer). BitNetModel.bitnet2B4T.ggufSha256is now pinned to4221b252fdd5fd25e15847adfeb5ee88886506ba50b8a34548374492884c2162, enabling real download integrity checks.ggufSizeBytesupdated to the exact 1,187,801,280-byte upload.native/CMakeLists.txt: the Linux/macOS shared library now keeps the canonicallibprefix (libbitnet_bridge.so/.dylib) so it matches whatDynamicLibrary.openexpects. ThePREFIX ""strip is now Windows-only.- pubspec no longer declares the web/ JS files as Flutter assets. A
package asset resolves under
assets/packages/..., which can never claim the service-worker scopeindex.htmlneeds, and it would otherwise be bundled into every Android/iOS build for nothing. See "Web Setup" in the README for the copy-into-your-own-web/-directory workflow.
Fixed
- Degenerate logits on arm64 (NaN/garbage output, iOS device + Android
arm64-v8a). The classic tree's scalar
ggml_vec_dot_i2_i8_sblock layout was wrong, and its buggyggml-cpu-i2s.cGEMM/GEMV fast path was reachable on arm64, which has no AVX2. Patch0002-i2s-arm-scalar-and-fastpath-gatefixes the scalar layout and gates that fast path to AVX2/AVX512 only; the x86_64 ABI already used the verified AVX2 kernels either way. Applied bytool/build_android.sh. - Prefill SIGBUS/SIGSEGV past ~130 tokens (classic-tree I2_S gemm). The
prefill path sized a stack VLA as
rows-per-thread * n_ubatchfloats, but ggml worker threads only get the pthread default stack (1 MB on Android, 512 KB on Darwin) — a prompt long enough to fill the micro-batch overran it and crashed insideggml_vec_dot_i2_i8_s. Patch0003-i2s-gemm-no-stack-vlaaccumulates straight intodstinstead, so prefill stack use no longer scales with the prompt. - Runaway
!output on Android (and any non-AVX2 target). The BitNet I2_S matmul kernels in bitnet.cpp shipped only AVX2 and NEON implementations with no fallback, so on the Android x86_64 emulator (NDK baseline has no AVX2) they compiled to empty functions: every matmul wrote nothing, logits came out flat, and greedy sampling emitted an endless stream of token 0 ("!"). Added a portable scalar fallback (validated bit-exact against the AVX2 kernel) plus a canonical-layout NEON path, distributed asnative/patches/0001-i2s-portable-kernels.patchand applied bytool/build_android.sh. Also fixes an arm64PARALLEL_SIZEmis-dispatch that corrupted 4-row prefill tail blocks, and a wrong-layoutquantize_i2_sNEON path that produced corrupt GGUFs when quantizing on ARM. - Multi-piece end-of-turn markers not detected.
<|eot_id|>/<|end_of_text|>arrive as literal text spread over several BPE pieces; the old per-piece equality check never matched, so the marker leaked into replies and generation ran on into hallucinated turns. AddedTurnStopDetector(streaming, rolling-tail match) and wired it into both the native and Web engines. - Degenerate-logits guard.
bn_promptnow detects flat/non-finite logits after prefill and fails with a clear error instead of streaming garbage — surfaces a broken-kernel build immediately rather than as"!"spam. - Prefill stack overflow on long prompts.
bn_initcapsn_ubatchat 128 to bound the stack VLA in the I2_S gemm path, which could otherwise exceed a mobile thread's ~1 MB stack for wide FFN tensors. - Stop button /
cancelGeneration()had no effect. The native inference isolate ran generation as a synchronous loop over the blocking FFInextToken(), which starved its own message port — the_CmdCancelsent byBitNetSession.stop()was queued but never delivered until generation had already finished. The worker now yields to the event loop between tokens, so a cancel is honoured at the next token boundary. - RAM gate was model-agnostic. Both engines gated
load()on a hardcoded 3072 MB constant (DeviceInspector.meetsMinimumRam) instead of the selected model'sminimumRamBytes— wrong in both directions now that models range 0.6–4 GB on disk: a small model was needlessly blocked on a <3 GB device, and a large one passed the gate then OOM'd insidebn_init. The gate now checks the specific model, andBitNetUnsupportedDeviceExceptionreports the actual required RAM and model name. - i2_s gemm tail-block out-of-bounds (crash on WASM, silent corruption
elsewhere). Under
ACT_PARALLEL,ggml_gemm_i2_i8_scalls the kernel withnrc = cur_r(activation rows) meaning "one weight row · nrc columns" —_Nx1semantics. The dispatcher routed anynrcnot a multiple ofPARALLEL_SIZE(e.g. a 23-token prefill → tail of 3) to_1x1, which treatsnrcas weight rows and reads the weight pointer out of bounds. On bounds-checked WebAssembly this traps (memory access out of bounds); on native it reads adjacent heap and corrupts the tail rows' logits. The scalar and NEON-dotprod_Nx1handle anynrc, so the tail now routes to_Nx1; the AVX2 path (which blocks byPARALLEL_SIZE) is left byte-identical. Folded intonative/patches/0001-i2s-portable-kernels.patch.
Documentation
- BUILD_GUIDE.md rewritten with the verified Linux recipe (gcc-only requirement, kernel codegen step, staging symlinks, and bridge smoke tests).
- iOS support (device + simulator), both backends. Ships prebuilt
-
0.1.0-beta.109 Apr 2026 pre-releaseRelease notes
Open source →Beta release. The public API is functional but may change before the stable
1.0.0release. SHA-256 pinning, pre-built native binaries, and the Web WASM artefact are not yet distributed via pub.dev — see the README for build instructions.Added
BitNetEngine— unifiedload / generate / cancelGeneration / disposeinterface backed by a dedicatedIsolateon native anddart:js_interopon Web.BitNetModel.bitnet2B4T— immutable const descriptor for Microsoft BitNet b1.58 2B-4T (I2_S GGUF, 4096-token context, ~745 MiB on-disk).ModelCache— downloads the GGUF from HuggingFace with resumable HTTP (Rangeheader), SHA-256 post-download verification, and atomic rename to final path. UsesgetApplicationSupportDirectory()viapath_provider.DeviceInspector— platform-agnostic RAM gate (≥ 3072 MB required).- Android:
ActivityManager.MemoryInfo.totalMemviaMethodChannel. - iOS:
ProcessInfo.processInfo.physicalMemoryviaMethodChannel. - macOS / Linux / Windows:
system_info2(SysInfo.getTotalPhysicalMemory()). - Web:
navigator.deviceMemory(fails closed to 0 if unavailable).
- Android:
- C bridge (
native/bitnet_bridge.h+native/bitnet_bridge.cc) — thin C ABI wrapping the llama.cpp new C API:llama_model_load_from_file,llama_new_context_with_model,llama_decode,llama_sampler_chain_init,llama_token_to_piece,llama_token_is_eog. - FFI bindings (
lib/src/ffi/) — hand-authored ffigen-compatible bindings andNativeLibrarywrapper withusing()/ Arena pointer lifecycle management. web/coi_service_worker.js— COI shim that injectsCross-Origin-Opener-Policy: same-originandCross-Origin-Embedder-Policy: require-corpheaders, enablingSharedArrayBufferfor WASM threads in Chrome and Firefox.- Sealed exception hierarchy —
BitNetExceptionbase with typed subclasses for every failure mode (device, platform, library, init, inference, hash mismatch, download, not-loaded, isolate). - Unit tests — 12 tests covering
BitNetModelmetadata,DeviceInspectorRAM thresholds, exception messages, andModelCachedownload/resume/clear flows.
Known Limitations (beta)
- Pre-built native binaries (
libbitnet_bridge.so,.dylib,.dll) are not yet bundled. Consumers must compilenative/bitnet_bridge.ccagainst llama.cpp themselves. See Building Native Libraries. - The WASM artefact (
BitNetWasmJS object) is not yet published. Web support requires a custom Emscripten build. ggufSha256is a placeholder sentinel — post-download hash verification is skipped until the hash is pinned after the first CI download.- Windows and Linux desktop are untested in this release. Android and iOS are the primary targets.
cancelGeneration()on Web cancels at the nextyieldboundary only (no interrupt signal to the synchronous WASM loop).