Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 27 additions & 1 deletion Sources/parrot/Transcription/WhisperKitTranscriber.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@ import Foundation
import WhisperKit

actor WhisperKitTranscriber: Transcriber {
/// Where model weights live. WhisperKit defaults to `Documents`, which iCloud
/// replicates and evicts to dataless stubs — CoreML's mmap of an evicted
/// weight file then blocks forever and the daemon never finishes loading.
private static let modelStore = URL.applicationSupportDirectory.appending(path: "parrot/huggingface")

/// Earlier versions downloaded into WhisperKit's `Documents` default. Say so
/// once rather than silently re-downloading gigabytes behind the user's back.
private static let legacyModelStore = URL.documentsDirectory.appending(path: "huggingface")

let modelID: String
private let model: TranscriptionModel
private var pipeline: WhisperKit?
Expand All @@ -19,12 +28,29 @@ actor WhisperKitTranscriber: Transcriber {
guard let whisperKitID = model.whisperKitID else {
throw TranscriberError.missingEngineID
}
Self.noteLegacyStore()
FileHandle.standardError.write(Data("loading \(model.id)...\n".utf8))
let config = WhisperKitConfig(model: whisperKitID, verbose: false, prewarm: true, load: true)
let config = WhisperKitConfig(
model: whisperKitID,
downloadBase: Self.modelStore,
verbose: false,
prewarm: true,
load: true
)
pipeline = try await WhisperKit(config)
FileHandle.standardError.write(Data("✓ \(model.id) ready\n".utf8))
}

/// Point at leftover models from the old `Documents` location so the disk
/// space is reclaimable instead of just abandoned.
private static func noteLegacyStore() {
let path = legacyModelStore.path(percentEncoded: false)
guard FileManager.default.fileExists(atPath: path) else { return }
FileHandle.standardError.write(Data(
"models now live in \(modelStore.path(percentEncoded: false)) — \(path) is unused, delete it to reclaim space\n".utf8
))
}

func transcribe(_ audio: [Float]) async throws -> String {
if pipeline == nil { try await warmUp() }
guard let pipeline else { throw TranscriberError.notLoaded }
Expand Down