From 2838e6b5d417e505f4dfdb9947ae3c34cadf15bf Mon Sep 17 00:00:00 2001 From: Beingpax Date: Tue, 8 Jul 2025 14:00:09 +0545 Subject: [PATCH] Improve transcription failure error handling. --- .../Services/LocalTranscriptionService.swift | 8 +++---- VoiceInk/Whisper/LibWhisper.swift | 5 +---- VoiceInk/Whisper/WhisperError.swift | 22 +------------------ VoiceInk/Whisper/WhisperState.swift | 6 ++++- 4 files changed, 11 insertions(+), 30 deletions(-) diff --git a/VoiceInk/Services/LocalTranscriptionService.swift b/VoiceInk/Services/LocalTranscriptionService.swift index 0e5b912..8c61860 100644 --- a/VoiceInk/Services/LocalTranscriptionService.swift +++ b/VoiceInk/Services/LocalTranscriptionService.swift @@ -16,7 +16,7 @@ class LocalTranscriptionService: TranscriptionService { func transcribe(audioURL: URL, model: any TranscriptionModel) async throws -> String { guard let localModel = model as? LocalModel else { - throw WhisperError.couldNotInitializeContext + throw WhisperStateError.modelLoadFailed } logger.notice("Initiating local transcription for model: \(localModel.displayName)") @@ -37,7 +37,7 @@ class LocalTranscriptionService: TranscriptionService { guard FileManager.default.fileExists(atPath: modelURL.path) else { logger.error("Model file not found at path: \(modelURL.path)") - throw WhisperError.couldNotInitializeContext + throw WhisperStateError.modelLoadFailed } logger.notice("Loading model: \(localModel.name)") @@ -45,13 +45,13 @@ class LocalTranscriptionService: TranscriptionService { whisperContext = try await WhisperContext.createContext(path: modelURL.path) } catch { logger.error("Failed to load model: \(localModel.name) - \(error.localizedDescription)") - throw WhisperError.couldNotInitializeContext + throw WhisperStateError.modelLoadFailed } } guard let whisperContext = whisperContext else { logger.error("Cannot transcribe: Model could not be loaded") - throw WhisperError.couldNotInitializeContext + throw WhisperStateError.modelLoadFailed } // Read audio data diff --git a/VoiceInk/Whisper/LibWhisper.swift b/VoiceInk/Whisper/LibWhisper.swift index 2703152..c472c78 100644 --- a/VoiceInk/Whisper/LibWhisper.swift +++ b/VoiceInk/Whisper/LibWhisper.swift @@ -6,9 +6,6 @@ import whisper #endif import os -enum WhisperError: Error { - case couldNotInitializeContext -} // Meet Whisper C++ constraint: Don't access from more than one thread at a time. actor WhisperContext { @@ -126,7 +123,7 @@ actor WhisperContext { self.context = context } else { logger.error("❌ Couldn't load model at \(path)") - throw WhisperError.couldNotInitializeContext + throw WhisperStateError.modelLoadFailed } } diff --git a/VoiceInk/Whisper/WhisperError.swift b/VoiceInk/Whisper/WhisperError.swift index 9fdfdf6..34fc74b 100644 --- a/VoiceInk/Whisper/WhisperError.swift +++ b/VoiceInk/Whisper/WhisperError.swift @@ -3,10 +3,6 @@ import Foundation enum WhisperStateError: Error, Identifiable { case modelLoadFailed case transcriptionFailed - case recordingFailed - case accessibilityPermissionDenied - case modelDownloadFailed - case modelDeletionFailed case unzipFailed case unknownError @@ -20,14 +16,6 @@ extension WhisperStateError: LocalizedError { return "Failed to load the transcription model." case .transcriptionFailed: return "Failed to transcribe the audio." - case .recordingFailed: - return "Failed to start or stop recording." - case .accessibilityPermissionDenied: - return "Accessibility permission is required for automatic pasting." - case .modelDownloadFailed: - return "Failed to download the model." - case .modelDeletionFailed: - return "Failed to delete the model." case .unzipFailed: return "Failed to unzip the downloaded Core ML model." case .unknownError: @@ -40,15 +28,7 @@ extension WhisperStateError: LocalizedError { case .modelLoadFailed: return "Try selecting a different model or redownloading the current model." case .transcriptionFailed: - return "Check your audio input and try again. If the problem persists, try a different model." - case .recordingFailed: - return "Check your microphone permissions and try again." - case .accessibilityPermissionDenied: - return "Go to System Preferences > Security & Privacy > Privacy > Accessibility and allow VoiceInk." - case .modelDownloadFailed: - return "Check your internet connection and try again. If the problem persists, try a different model." - case .modelDeletionFailed: - return "Restart the application and try again. If the problem persists, you may need to manually delete the model file." + return "Check the default model try again. If the problem persists, try a different model." case .unzipFailed: return "The downloaded Core ML model archive might be corrupted. Try deleting the model and downloading it again. Check available disk space." case .unknownError: diff --git a/VoiceInk/Whisper/WhisperState.swift b/VoiceInk/Whisper/WhisperState.swift index 749af5b..9b30acc 100644 --- a/VoiceInk/Whisper/WhisperState.swift +++ b/VoiceInk/Whisper/WhisperState.swift @@ -413,8 +413,12 @@ class WhisperState: NSObject, ObservableObject, AVAudioRecorderDelegate { let duration = CMTimeGetSeconds(try await audioAsset.load(.duration)) await MainActor.run { + let errorDescription = (error as? LocalizedError)?.errorDescription ?? error.localizedDescription + let recoverySuggestion = (error as? LocalizedError)?.recoverySuggestion ?? "" + let fullErrorText = recoverySuggestion.isEmpty ? errorDescription : "\(errorDescription) \(recoverySuggestion)" + let failedTranscription = Transcription( - text: "Transcription Failed: \(error.localizedDescription)", + text: "Transcription Failed: \(fullErrorText)", duration: duration, enhancedText: nil, audioFileURL: permanentURL.absoluteString