Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

weaveffi-whisper-everywhere

weaveffi-whisper-everywhere is the next entry in WeaveFoundry's *-everywhere demo series, built to prove — in the most concrete, shareable way possible — that a single WeaveFFI interface definition can deliver one native library to every cross-platform framework at once. The goal: take whisper.cpp (Georgi Gerganov's MIT-licensed C/C++ engine that runs OpenAI's Whisper speech-to-text models) and expose a focused slice of its C API through one WeaveFFI IDL, then run identical on-device transcription side-by-side in three apps — React Native, NativeScript, and Flutter — all calling the exact same native core. The architecture: a thin Rust shim links whisper.cpp and implements WeaveFFI's stable C ABI, compiling to one cdylib plus a generated N-API addon; Flutter consumes the C ABI via dart:ffi, while NativeScript (via its new N-API runtime) and React Native (via react-native-node-api) load the same generated N-API addon — the same module that also runs under Node. The thesis worth proving is that React Native's New Architecture (JSI/C++), NativeScript's N-API runtime, and Flutter's dart:ffi are all independently converging on the same C/N-API native boundary that WeaveFFI already targets — so "define once, run everywhere" stops being aspirational, and the same IDL additionally emits Swift, Python, Go, and .NET packages for free.


The one picture

                        idl/whisper.yml          ← the single source of truth
                              │
              weaveffi generate (v0.7.0)
                              │
        ┌──────────┬──────────┼──────────┬──────────┬───────┐
        ▼          ▼          ▼          ▼          ▼       ▼
     generated/  generated/ generated/ generated/  …swift  …go
        c/        dart/      node/      python/     dotnet

   rust/crates/whisper-core  ──implements──▶  the WeaveFFI C ABI
        │  links whisper.cpp (via whisper-rs)
        ▼
   libweaveffi.{dylib,so,dll}   +   weaveffi N-API addon (index.node)
        │                                   │
        │ dart:ffi                          │ require('…/index.node')
        ▼                                   ▼
   ┌─────────┐                  ┌───────────────────────────────────┐
   │ Flutter │                  │  @weaveffi-whisper/core (one JS)   │
   └─────────┘                  │   ├── Node CLI                     │
                                │   ├── React Native (node-api)      │
                                │   └── NativeScript (N-API runtime) │
                                └───────────────────────────────────┘

One IDL → one Rust core → four runtimes reaching the identical weaveffi_whisper_* symbols. The three JS runtimes don't just share an ABI — they share the same JavaScript (packages/whisper-weave-node).

What's verified here

Everything below was run end-to-end on this machine and produces the same transcript of assets/jfk.wav ("And so my fellow Americans…"):

Runtime Path to the core Status
Rust direct C ABI call (cargo run --example) ✅ verified
Node.js WeaveFFI N-API addon ✅ verified
Dart / Flutter WeaveFFI dart:ffi binding ✅ verified (headless FFI run; macOS desktop ready)
React Native @weaveffi-whisper/core via react-native-node-api 🟡 wired — device build needs the core cross-compiled (see app README)
NativeScript @weaveffi-whisper/core via the N-API runtime 🟡 wired — device build needs the core cross-compiled (see app README)

The RN and NativeScript apps run the exact same @weaveffi-whisper/core JavaScript that the verified Node path exercises; what remains for a physical device is building the Rust core for the device architecture and bundling the .node addon (documented per app).

Repository layout

idl/whisper.yml              The interface. Edit this, regenerate everything.
rust/crates/whisper-core/    Rust shim: links whisper.cpp, implements the C ABI.
                             Builds as `libweaveffi` (cdylib) + an rlib.
generated/                   WeaveFFI output: c, dart, node, swift, python, go, dotnet.
packages/whisper-weave-node/ Shared JS package (@weaveffi-whisper/core) wrapping
                             the N-API addon — used by Node, RN, and NativeScript.
apps/flutter/                Flutter app (dart:ffi → generated/dart).
apps/react-native/           Expo app (react-native-node-api → the addon).
apps/nativescript/           NativeScript app (N-API runtime → the addon).
tools/dart-smoke/            Minimal headless Dart FFI proof.
scripts/                     fetch-model, build-core, gen-bindings, build-node-addon.
Makefile                     Orchestration (`make help`).

Quickstart

Prerequisites: Rust (stable), Node 18+, and for the apps, Flutter / the Expo CLI / the NativeScript CLI as needed. CMake + a C/C++ toolchain are required to build whisper.cpp the first time.

# 1. Fetch a small model + the sample clip, build the core, regenerate all
#    bindings, and build the N-API addon.
make setup            # MODEL=tiny.en by default

# 2. Prove the pipeline three ways (all print the same transcript):
make rust-smoke       # Rust → C ABI
make node-smoke       # Node → N-API
make dart-smoke       # Dart → dart:ffi

# 3. Launch an app:
make run-flutter      # macOS desktop, loads the host dylib
make run-rn           # Expo dev server
make run-ns           # NativeScript

make help lists every target.

How each framework reaches the one core

  • Flutter imports package:weaveffi (verbatim generated/dart) and calls the weaveffi_whisper_* C symbols over dart:ffi. See apps/flutter/README.md.
  • React Native imports @weaveffi-whisper/core; react-native-node-api's Babel plugin rewrites the require('*.node') so the addon is bundled and the N-API host resolves it on device. See apps/react-native/README.md.
  • NativeScript imports the same @weaveffi-whisper/core; its built-in N-API runtime loads the addon. See apps/nativescript/README.md.

Because the three JS runtimes share packages/whisper-weave-node, the app code is identical: Model.load(path) → model.transcribeFile(wav, 'en') → transcript.toJSON().

Changing the interface

Edit idl/whisper.yml, then:

make bindings    # regenerate all language bindings
make addon       # rebuild the N-API addon C glue + restage

Implement any new functions in rust/crates/whisper-core/src/lib.rs (the generator can scaffold matching stubs with weaveffi generate … --target rust).

A note on the NativeScript scaffold + min-release-age

If you scaffold NativeScript yourself and have min-release-age set in your ~/.npmrc, ns create can crash with this.before.toISOString is not a function — NativeScript's bundled pacote passes npm's computed before string where a Date is expected. Bypass it for just that one command without touching your global config:

npm_config_userconfig=/dev/null ns create nativescript \
  --template @nativescript/template-blank-ts --appid com.weavefoundry.whisperns

License

Demo code: MIT. whisper.cpp and the ggml models are MIT-licensed by their respective authors.

About

No description, website, or topics provided.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages