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:
Rocky
2026-06-23 00:46:58 +08:00
parent 5e5122f172
commit df1c5ff32c
160 changed files with 22080 additions and 492 deletions
@@ -9,18 +9,37 @@ public enum EngineServiceLabel {
public static func summary(
engineMode: String,
providerId: String,
model: String
model: String,
localASRBackend: LocalASRBackend = .speechAnalyzer,
language: AppUILanguage? = nil
) -> String {
let isChinese = Locale.preferredLanguages.first?.hasPrefix("zh") == true
let prefix = isChinese ? "当前:" : "Active: "
let lang = language ?? AppGroupStore().uiLanguage
if engineMode == "local" {
return isChinese
? "\(prefix)本地引擎 · Apple SpeechAnalyzer"
: "\(prefix)On-device · Apple SpeechAnalyzer"
let asrName = asrDisplayName(for: localASRBackend, language: lang)
return SharedL10n.format("engine.summary.local", language: lang, asrName)
}
let providerName = ProviderDisplayName.name(for: providerId)
let providerName = ProviderDisplayName.name(for: providerId, language: lang)
let trimmedModel = model.trimmingCharacters(in: .whitespacesAndNewlines)
if trimmedModel.isEmpty { return "\(prefix)\(providerName)" }
return "\(prefix)\(providerName) · \(trimmedModel)"
if trimmedModel.isEmpty {
return SharedL10n.format("engine.summary.cloud", language: lang, providerName)
}
return SharedL10n.format(
"engine.summary.cloudWithModel",
language: lang,
providerName,
trimmedModel
)
}
private static func asrDisplayName(
for backend: LocalASRBackend,
language: AppUILanguage
) -> String {
switch backend {
case .speechAnalyzer:
return SharedL10n.string("engine.asr.appleSpeech", language: language)
case .qwen3ASR:
return SharedL10n.string("model.qwen3asr.name", language: language)
}
}
}