Pārlūkot izejas kodu

Honor preferred transcription languages in speech-to-text.

When Preferred Language 1/2 locales are provided, try them in order and only fall back on empty results instead of letting the English-biased scoring heuristic win.

Co-authored-by: Cursor <cursoragent@cursor.com>
huzaifahayat12 2 mēneši atpakaļ
vecāks
revīzija
e9fef52500
1 mainītis faili ar 35 papildinājumiem un 2 dzēšanām
  1. 35 2
      zoom_app/MeetingTranscriptionService.swift

+ 35 - 2
zoom_app/MeetingTranscriptionService.swift

@@ -125,6 +125,10 @@ final class MeetingTranscriptionService {
         let counter = ProgressCounter(total: totalChunks, onProgress: onProgress)
         await counter.emitInitial()
 
+        // If the caller passes explicit locales, treat them as the user's preferred transcription languages.
+        // In that case we should try locales in the given order and only fall back when a locale
+        // produces an empty transcript for the chunk.
+        let userProvidedLocales = locales.isEmpty == false
         let effectiveLocales = locales.isEmpty ? preferredLocalesForGlobalRecognition() : locales
 
         async let micSegments: [TranscriptSegment] = {
@@ -134,6 +138,7 @@ final class MeetingTranscriptionService {
                 chunks: plan.1,
                 speaker: .microphone,
                 locales: effectiveLocales,
+                preferLocaleOrder: userProvidedLocales,
                 counter: counter
             )
         }()
@@ -144,6 +149,7 @@ final class MeetingTranscriptionService {
                 chunks: plan.1,
                 speaker: .system,
                 locales: effectiveLocales,
+                preferLocaleOrder: userProvidedLocales,
                 counter: counter
             )
         }()
@@ -196,6 +202,7 @@ final class MeetingTranscriptionService {
         chunks: [ChunkPlan],
         speaker: TranscriptSpeaker,
         locales: [Locale],
+        preferLocaleOrder: Bool,
         counter: ProgressCounter
     ) async throws -> [TranscriptSegment] {
         var segments: [TranscriptSegment] = []
@@ -208,7 +215,8 @@ final class MeetingTranscriptionService {
             let result = await transcribeBufferWithLocaleFallback(
                 buffer: buffer,
                 locales: locales,
-                preferredLocale: preferredLocale
+                preferredLocale: preferredLocale,
+                preferLocaleOrder: preferLocaleOrder
             )
             await counter.increment()
             preferredLocale = result.locale ?? preferredLocale
@@ -245,7 +253,8 @@ final class MeetingTranscriptionService {
     private func transcribeBufferWithLocaleFallback(
         buffer: AVAudioPCMBuffer,
         locales: [Locale],
-        preferredLocale: Locale?
+        preferredLocale: Locale?,
+        preferLocaleOrder: Bool
     ) async -> (text: String, locale: Locale?) {
         var orderedLocales: [Locale] = []
         if let preferredLocale {
@@ -255,6 +264,30 @@ final class MeetingTranscriptionService {
             orderedLocales.append(locale)
         }
 
+        // When the caller provides explicit locales (Preferred Language 1/2), prioritize that order.
+        // We only use the heuristic "bestCandidate" scoring for auto locale selection.
+        if preferLocaleOrder {
+            for locale in orderedLocales {
+                guard let recognizer = SFSpeechRecognizer(locale: locale), recognizer.isAvailable else { continue }
+                do {
+                    let text = try await transcribeBuffer(buffer: buffer, recognizer: recognizer)
+                    let trimmed = text.trimmingCharacters(in: .whitespacesAndNewlines)
+                    guard trimmed.isEmpty == false else { continue }
+                    return (trimmed, locale)
+                } catch {
+                    // One transient retry before moving on to the next locale.
+                    try? await Task.sleep(nanoseconds: 500_000_000)
+                    if let text = try? await transcribeBuffer(buffer: buffer, recognizer: recognizer) {
+                        let trimmed = text.trimmingCharacters(in: .whitespacesAndNewlines)
+                        guard trimmed.isEmpty == false else { continue }
+                        return (trimmed, locale)
+                    }
+                    continue
+                }
+            }
+            return ("", nil)
+        }
+
         var bestCandidate: (text: String, locale: Locale, score: Double)?
         for locale in orderedLocales {
             guard let recognizer = SFSpeechRecognizer(locale: locale), recognizer.isAvailable else { continue }