feat: migrate on-device Qwen3 ASR to CoreML for background Flow dictation
Replace MLX GPU inference with CoreML bundles so transcription continues while the host app is backgrounded. Adds model download and warm-up, vendored Qwen3Speech, and updates onboarding, settings, and copy for the ~1.6 GB CoreML package (iOS 18+).
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
// LocalASRBackend.swift
|
||||
// OSGKeyboard · Shared
|
||||
//
|
||||
// Identifies which on-device speech recognition engine to use when the
|
||||
// user picks the "local" engine (no cloud LLM polish). The shared
|
||||
// factory `ASRServiceFactory` dispatches on this enum; the settings UI
|
||||
// renders it as a picker.
|
||||
//
|
||||
// Why an enum in `Shared` rather than living next to the concrete
|
||||
// `ASRService` implementations: the value must be serialisable into
|
||||
// the App Group store (so the keyboard extension can observe the
|
||||
// selection), exposed via `ProviderConfig` (UI binding) and consumed
|
||||
// by every layer that asks for an ASR backend.
|
||||
|
||||
import Foundation
|
||||
|
||||
public enum LocalASRBackend: String, CaseIterable, Identifiable, Sendable, Codable {
|
||||
/// iOS 26 `SpeechAnalyzer` + `DictationTranscriber`. Always
|
||||
/// on-device, no asset download, ships with iOS. Default for every
|
||||
/// fresh install — anything else is opt-in.
|
||||
case speechAnalyzer
|
||||
|
||||
/// Qwen3-ASR-0.6B via CoreML (Neural Engine + CPU). Stronger on Chinese
|
||||
/// dialects and noisy audio than `SpeechAnalyzer`, works in Flow while
|
||||
/// the host app is backgrounded, but requires a ~1.6 GB download on first
|
||||
/// use and iOS 18+.
|
||||
case qwen3ASR
|
||||
|
||||
public var id: String { rawValue }
|
||||
|
||||
/// Localisation key for the human label in the settings picker.
|
||||
public var labelKey: String {
|
||||
switch self {
|
||||
case .speechAnalyzer: return "asr.backend.speechAnalyzer.label"
|
||||
case .qwen3ASR: return "asr.backend.qwen3.label"
|
||||
}
|
||||
}
|
||||
|
||||
/// Localisation key for the one-line subtitle shown under the label.
|
||||
public var blurbKey: String {
|
||||
switch self {
|
||||
case .speechAnalyzer: return "asr.backend.speechAnalyzer.blurb"
|
||||
case .qwen3ASR: return "asr.backend.qwen3.blurb"
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether this backend needs the user to download a model file
|
||||
/// before it can run. Used to gate the "Downloading Qwen3-ASR" UI
|
||||
/// in a follow-up; for now we just expose the flag.
|
||||
public var requiresModelDownload: Bool {
|
||||
switch self {
|
||||
case .speechAnalyzer: return false
|
||||
case .qwen3ASR: return true
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user