Skip to content

Fix Cohere long-form transcription - #763

Open
altic-dev wants to merge 9 commits into
mainfrom
B/replace-cohere-coreml-with-gguf
Open

Fix Cohere long-form transcription#763
altic-dev wants to merge 9 commits into
mainfrom
B/replace-cohere-coreml-with-gguf

Conversation

@altic-dev

@altic-dev altic-dev commented Aug 2, 2026

Copy link
Copy Markdown
Owner

Description

Replaces Cohere's Core ML backend with the existing transcribe.cpp integration and the validated Q4_K_M GGUF. Cohere audio is decoded in bounded 30-second windows with 2-second overlap and multilingual seam merging, preventing silent long-form truncation while leaving other speech models unchanged.

Type of Change

  • 🐞 Bug fix
  • ✨ New feature
  • 💥 Breaking change
  • 🧹 Chore
  • 📝 Documentation update

Related Issue or Discussion

Closes #736

Testing

  • Tested on Intel Mac
  • Tested on Apple Silicon Mac
  • Tested on macOS version: 27.0
  • Ran linter locally: swiftlint --strict --config .swiftlint.yml Sources
  • Ran formatter locally: swiftformat --config .swiftformat Sources
  • Ran tests locally: 14 focused chunking, seam-merging, streaming-policy, and transcription-lock tests
  • Tested a 299.8-second fixture through the installed app; all 829 words and the final sentence were retained
  • Tested a 299.8-second mixed Mandarin/English fixture through the installed release app; all 4,796,800 samples completed
  • Tested Cohere live previews through the installed release app at a 1-second cadence; preview decodes completed in 0.10-0.27 seconds
  • Built, installed, launched, and code-sign verified with Fluid Intelligence enabled

Screenshots / Video

  • No UI/visual changes; screenshots/video are not applicable.

Notes

The Q4_K_M artifact is 1.45 GiB and reports comparable LibriSpeech WER to BF16 (1.25% versus 1.26%). Existing Cohere Core ML artifacts are removed only after the GGUF loads successfully.

Cohere uses total physical RAM for its 8 GB compatibility gate. Live previews decode only the latest 30 seconds, keeping preview work bounded while final transcription still processes the complete recording.

Voice-model actions and microphone dictation are disabled during meeting transcription, and the job remains pinned to the model loaded when it started.

@greptile-apps

greptile-apps Bot commented Aug 2, 2026

Copy link
Copy Markdown

Greptile Summary

Replaces Cohere’s Core ML backend with the existing transcribe.cpp integration and adds bounded long-form decoding with overlapping seam merging.

  • Downloads and validates the Cohere Q4_K_M GGUF model.
  • Processes Cohere audio in overlapping 30-second windows while bounding live previews to the latest window.
  • Coordinates file-transcription state with dictation and model-management operations.
  • Adds focused coverage for chunk ranges, concurrency gating, punctuation preservation, and multilingual seam handling.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains, and the four previously reported seam-merging defects are addressed by the current alignment and boundary-joining logic.

Reviews (8): Last reviewed commit: "preserve cohere seam punctuation" | Re-trigger Greptile

Comment thread Sources/Fluid/Services/WhisperProvider.swift Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 529f9b6cc1

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".


var bestRightCount = 0
var bestMatchLength = 0
for leftCount in 2...maximum {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Guard the phrase loop when only one token is available

When either nonempty chunk has only one whitespace-delimited token and the exact-overlap pass does not accept it, maximum is 1 and constructing the closed range 2...maximum traps with Range requires lowerBound <= upperBound. Thus sparse long-form audio such as two separated one-word chunks can crash the app during seam merging; return zero overlap before these phrase loops when maximum < 2.

Useful? React with 👍 / 👎.

Comment on lines 355 to +356
case .cohereTranscribeSixBit:
return self.getExternalCoreMLProvider()
return self.getWhisperProvider()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Avoid rejecting Cohere based on eight currently free GB

Routing Cohere through WhisperProvider also applies that provider's availableMemoryGB < requiredMemoryGB guard, while Cohere's requirement is 8 GB and availableMemoryGB() counts only free, inactive, and purgeable pages. An 8-GB Apple Silicon Mac can never report the full 8 GB as available after macOS is running, so users on an otherwise supported configuration now download the GGUF and then always receive an insufficient-memory error; use total physical memory or a realistic free-memory threshold for this model.

Useful? React with 👍 / 👎.

Comment on lines +528 to +531
let ranges = CohereTranscribeCppLongFormProcessor.ranges(
sampleCount: samples.count,
sampleRate: 16_000
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep Cohere streaming previews bounded

During timer-based Cohere streaming, ASRService passes the complete growing recording prefix to the provider, and the default transcribeStreaming now reaches this long-form loop. After 30 seconds every preview therefore re-decodes all windows from the beginning rather than the bounded latest window used by the previous Cohere provider, making work grow with recording length and causing stop to wait for an increasingly expensive in-flight preview; override the streaming path to decode only a bounded suffix or disable previews for this model.

Useful? React with 👍 / 👎.

@github-actions

github-actions Bot commented Aug 2, 2026

Copy link
Copy Markdown

FluidVoice PR build ready

Download FluidVoice-PR-763-18f536f2c631

The artifact contains the ad-hoc-signed app ZIP, Xcode archive, build manifest, and installation instructions. It expires 5 days after the build.

Install the app

  1. Extract the downloaded artifact, then extract FluidVoice-PR-763.app.zip.

  2. Move FluidVoice Fix Cohere long-form transcription #763.app into the /Applications folder.

  3. Open Terminal and remove the download quarantine marker:

    xattr -dr com.apple.quarantine "/Applications/FluidVoice #763.app"
    
  4. In Applications, Control-click FluidVoice Fix Cohere long-form transcription #763.app and choose Open.

  5. If macOS still blocks it, open System Settings → Privacy & Security, click Open Anyway, and confirm.

This build has its own app identity, so its permissions are separate from the release version of FluidVoice.

View workflow run

Comment thread Sources/Fluid/Services/WhisperProvider.swift Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1c337be8a0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

return left + String(String.UnicodeScalarView(rightScalars.dropFirst(endIndex)))
}
}
return left + right

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve separators when CJK chunks have no overlap

When two adjacent chunks contain CJK characters but share no recognized overlap, this fallback concatenates them without any separator. This corrupts Korean, which is included in containsCJK and normally uses spaces—for example, merging "한국어 테스트" and "새로운 문장" produces "한국어 테스트새로운 문장"; mixed CJK/English boundaries can be joined similarly. Preserve an appropriate boundary separator when no overlap was found instead of unconditionally using left + right.

Useful? React with 👍 / 👎.

Comment on lines +82 to +83
if similarCount >= max(2, count - 1) {
return count

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject fuzzy overlaps between very short words

When an overlapped window actually begins a new phrase, two unrelated short words can each pass the one-edit tolerance and cause content to be discarded. For example, left text ending in "turn on" and right text beginning "burn in slowly" is treated as a two-word overlap (turn/burn and on/in are each one edit apart), producing "turn on slowly" and losing "burn in". Require stronger evidence, such as sufficient token length or aggregate matched characters, before accepting this fuzzy overlap.

Useful? React with 👍 / 👎.

Comment thread Sources/Fluid/Services/WhisperProvider.swift
Comment thread Sources/Fluid/Services/WhisperProvider.swift

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ab571b8f88

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

return self.getParakeetRealtimeProvider()
case .cohereTranscribeSixBit:
return self.getExternalCoreMLProvider()
return self.getWhisperProvider()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep Cohere file transcription seam-aware

When Cohere is used to transcribe a meeting file longer than 20 minutes, routing it to WhisperProvider makes it inherit prefersNativeFileTranscription == false. The fallback in MeetingTranscriptionService.transcribeFile divides the file into disjoint 20-minute chunks and directly joins their transcripts, so the new 30-second overlap processor only handles seams within each outer chunk and cannot recover words split across a 20-minute boundary. The previous Cohere provider opted into whole-file transcription; preserve that behavior or add overlap and seam merging to the outer chunks.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a58b21b1bd

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

let transcript = try await session.run(
samples,
options: RunOptions(timestamps: .segment)
let isCohere = self.selectedModel == .cohereTranscribeSixBit

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep transcription tied to the loaded model

If the user changes speech models while a meeting transcription is running, this provider remains retained by MeetingTranscriptionService, but selectedModel starts returning the new global setting even though session still contains the previously loaded model. On the next outer chunk, a Cohere session can therefore take the non-Cohere branch and receive the entire roughly 20-minute buffer with Whisper timestamp options instead of bounded 30-second chunks, causing incorrect output or excessive resource use. Store the model identity alongside the installed session and use that identity for transcription behavior.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b934e32e52

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +60 to +62
let remaining = rightTokens.dropFirst(overlap)
guard !remaining.isEmpty else { continue }
result += " " + remaining.joined(separator: " ")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve punctuation from the overlapped right chunk

When the earlier window ends before sentence punctuation but the overlapping window recognizes it, dropping all overlapped right tokens also drops that punctuation. For example, merging "The end" with "The end. Next" produces "The end Next", and merging with "The end." produces "The end"; this removes sentence boundaries from Cohere long-form output. Reconcile punctuation on matched boundary tokens before discarding the overlap.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 18f536f2c6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".


var areSpeechModelActionsBlocked: Bool {
self.asr.isRunning
self.asr.isAnyTranscriptionRunning

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Block Cohere language changes during meeting transcription

When a Cohere meeting transcription runs longer than the outer 20-minute chunk, this gate disables model actions but does not disable speechModelLanguagePicker, whose menu still writes selectedCohereLanguage. WhisperProvider.transcribe reads that global setting again for every outer chunk, so changing the language mid-job makes later chunks use a different decoder language and can corrupt the combined transcript. Disable language controls during file transcription or capture and reuse the language selected when the job begins.

Useful? React with 👍 / 👎.

try FileManager.default.removeItem(at: self.modelDirectory)
}
}
self.removeLegacyCohereCachesIfNeeded(for: targetModel)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve legacy Cohere caches when a download is cancelled

When a Cohere GGUF download is cancelled before installation, ASRService.downloadModel observes modelsExistOnDisk() == false and invokes clearCache() as cancellation cleanup. This unconditional legacy cleanup then deletes both existing Core ML cache directories even though the replacement model never loaded, silently discarding the previous model data on a routine cancellation. Keep legacy-cache migration out of incomplete-download cleanup, or customize Cohere's cancellation cleanup behavior.

Useful? React with 👍 / 👎.

@altic-dev altic-dev self-assigned this Aug 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] Cohere Transcribe truncates audio (with fix)

1 participant