Skip to content

feat: LiteRT engine consolidation + on-device STT (flutter_gemma_speech, moonshine) - #389

Merged
DenisovAV merged 23 commits into
mainfrom
feat/litert-base-speech
Jul 26, 2026
Merged

feat: LiteRT engine consolidation + on-device STT (flutter_gemma_speech, moonshine)#389
DenisovAV merged 23 commits into
mainfrom
feat/litert-base-speech

Conversation

@DenisovAV

Copy link
Copy Markdown
Owner

Summary

Consolidates the shared LiteRT runtime into flutter_gemma_litertlm (now the full LiteRT engine) and adds on-device Speech-to-Text via a new flutter_gemma_speech package. First STT model: moonshine-tiny (raw PCM, no mel frontend). The model is selectable like inference models (SttModelType on the spec) — one generic backend + recognizer driven by a per-model profile.

Phase 1 — engine consolidation + core STT contracts

  • flutter_gemma_litertlm absorbs the LiteRt interpreter FFI (LiteRtBindings, moved in from embeddings) and the web litert.js/tensorflow.js runtime (moved in from core) → it is now the sole owner of libLiteRtLm; the stale-owner coordination marker is retired.
  • flutter_gemma_embeddings becomes thin logic depending on litertlm (drops its own hook + FFI + engine web JS). Public API (LiteRtEmbeddingBackend) unchanged.
  • flutter_gemma (core) loses the engine web JS → truly engine-free.
  • Core STT contracts (runtime-agnostic, mirror the embedding probe-chain): ModelManagementType.stt, SttModelSpec + SttModelType, SttBackendProvider/SttRegistry, SpeechRecognizer, FlutterGemma.initialize(sttBackends:), installStt()/getActiveStt() + createSttModel dispatch (mobile/web/desktop) + active-model persistence.

Phase 2 — flutter_gemma_speech (STT, moonshine)

  • New package depends on core + litertlm (shared libLiteRtLm, no own hook).
  • LiteRtSttBackend (sole .tflite STT backend; canHandle => true; returns the core SpeechRecognizer interface). Concrete pipeline (LiteRtSpeechRecognizer, SttModelProfile, SttWorker, SttCore, HfTokenizer) is internal to lib/src/, not exported.
  • Generic pipeline: raw-PCM → encode → autoregressive greedy decode → HF-tokenizer detokenize, driven by SttModelProfile.forType(sttModelType). moonshine profile filled; whisper/parakeet (log-mel) arms throw UnimplementedError (follow-on).
  • Example: STT model catalog + selection screen + transcribe screen.

Validation (on-device, macOS)

  • Phase 1: standard desktop_embedding_test green — embeddings still produce a 768-dim vector through the moved FFI on libLiteRtLm 0.14.0.
  • Phase 2: end-to-end through the public API (installSttgetActiveStttranscribe) transcribes a known clip → "Ever tried ever failed, no matter."

Follow-ups (not in this PR)

  • A committed on-device STT integration test (validated here via a throwaway harness).
  • A mobile-platform gate (iOS/Android) for STT + the consolidated embeddings.
  • Hook robustness: invalidate/refresh the in-repo native prebuilt when the pinned native version changes (a stale local prebuilt silently runs the old libLiteRtLm after a version bump).
  • whisper/parakeet profiles (need a Dart log-mel frontend); streaming STT; STT web arm; TTS + voice loop.

DenisovAV added 23 commits July 23, 2026 11:51
Expose LiteRtBindings via a public native-only litertlm/lib/litert_bindings.dart
(avoids an implementation_imports lint on embeddings' native-only file), plus
trivial prefer_initializing_formals / unnecessary_library_name fixes so
flutter_gemma_embeddings analyzes clean again.
…r-path leak, NaN decode diagnostic, hiddenBufPtr leak, unlock status checks
…core to 1.4.0 (+CHANGELOG); speech requires flutter_gemma ^1.4.0
…repo.ModelType.stt (was silently bucketed into embedding in getInstalledModels/isAnyModelInstalled)
…dow (unconditional in-progress completer guard, mirrors desktop)
… the consolidated single-hook engine + wired moonshine STT
…port-leak guard, drop dead crypto dep, fix path-import shadow, RuntimeConfig.tokenizerPath doc, stt_screen reentrancy guards
…twork install + bundled clip via public API)
…LAPPDATA × Task.split)

On Windows getWriteTargetPath resolves to %LOCALAPPDATA%\flutter_gemma, which is
not one of background_downloader's recognized BaseDirectories (path_provider maps
applicationSupport -> Roaming, applicationDocuments -> Documents). Task.split
therefore falls back to BaseDirectory.root with the drive STRIPPED, and on Windows
the root base reconstructs against '' (= $CWD), so a fresh download landed at
<cwd>\Users\..\AppData\Local\flutter_gemma\ while getReadTargetPath /
validateModelFiles look at the absolute path. install() "succeeded" but
isModelInstalled() stayed false, and getActiveModel/getActiveStt threw "model file
paths not found".

Affects every fresh network download (inference/embedding/STT) since the 0.15.1
LOCALAPPDATA switch (#179); latent because Windows tests use Documents-staged
models and no test exercised a real download landing. macOS/Linux are unaffected
(applicationSupport is a recognized base).

- smart_downloader: resolveDownloadDirectory() hands the DownloadTask the absolute
  directory for the root-fallback case so the file lands exactly at targetPath;
  taskId still derives from the split triple, so resume/reclaim identity is
  unchanged.
- network_source_handler: install()/installWithProgress() now fail loudly when no
  file is present at targetPath after a "successful" download instead of saving a
  0-byte "installed" record.
- test: download_directory_resolution_test guards the download-landing invariant
  the path-resolution round-trip test could not.

Verified end-to-end: Windows STT green (file at %LOCALAPPDATA%\flutter_gemma,
transcript ok), macOS STT green (no regression), core suite 481/481.
…ec/CLAUDE versions

New in 1.4: on-device speech-to-text via the new flutter_gemma_speech package
(selectable ASR, moonshine today) + the Windows fresh-download fix.

- Core README: STT feature bullet, "What's new in 1.4", install snippet +
  pick-by-need, initialize() param table, and a Speech-to-Text row in the
  platform feature matrix; modular-packages bullet now lists speech.
- CHANGELOG 1.4.0: user-facing STT line + the Windows fresh-download fix.
- Website: new /docs/speech guide; sidebar, footer, and features-card entries;
  installation/packages/models/getting-started/genkit/migration/troubleshooting
  updated for STT; version refs bumped (flutter_gemma ^1.4.0, litertlm ^1.3.0,
  embeddings ^1.0.4).
- Sync ios/macos flutter_gemma.podspec s.version and CLAUDE.md Current Version
  to 1.4.0 (+ speech 0.1.0).
- Doc audit fixes: CONTRIBUTING SDK floor (Flutter 3.44 / Dart 3.12),
  native-v0.14.0, FFI-client path; flutter_gemma_embeddings README web-asset
  path (packages/flutter_gemma_embeddings/web/); flutter_gemma_agent CHANGELOG
  8 starter skills; "litertlm 1.2.0+" Windows discrete-GPU regression labels;
  litertlm README notes it exposes LiteRtBindings for embeddings + speech.
…en download-path tests

Follow-up to the Windows download-path fix, from the final PR review:

- Extract `assertInstalledFilePresent(sizeBytes, targetPath)` and apply it in
  NetworkSourceHandler (was inline), AssetSourceHandler, and FileSourceHandler —
  the same "download/copy succeeded but no usable file landed → silently save a
  broken install record" defect the network fix closed. BundledSourceHandler is
  intentionally excluded (a bundled native resource is not a stat-able file, so
  getFileSize can be 0 legitimately). Message now distinguishes 0-byte/missing.
- resolveDownloadDirectory: add an injectable `p.Context` (defaults to the host
  context) so the Windows landing behaviour is deterministically testable on a
  POSIX CI host; correct the doc/inline comments — the stored taskId is derived
  from the split triple regardless, and the reclaim recompute only matters on
  the Android-only reclaim path (which never takes the root fallback).
- Tests: add a host-independent Windows round-trip that reproduces the
  drive-strip landing bug and FAILS if the fix is reverted; add 0-byte guard
  tests for the network/asset/file handlers (throws + never persists metadata).

Core suite 486/486; analyze clean.
@DenisovAV
DenisovAV merged commit ff669fb into main Jul 26, 2026
5 checks 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