Skip to content

fix(stt): define and validate the direct STT audio contract (OSS-30) - #38

Merged
corasan merged 2 commits into
mainfrom
feature/oss-30-define-and-validate-the-mlx-direct-stt-audio-contract
Aug 11, 2026
Merged

fix(stt): define and validate the direct STT audio contract (OSS-30)#38
corasan merged 2 commits into
mainfrom
feature/oss-30-define-and-validate-the-mlx-direct-stt-audio-contract

Conversation

@corasan

@corasan corasan commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

Summary

Direct STT accepted any nonempty ArrayBuffer as 16 kHz mono Float32 and forced English. Encoded files, Int16 buffers, and the library's own 24 kHz TTS output silently produced incorrect transcriptions.

This PR makes the audio contract explicit and enforces it:

  • transcribe/transcribeStream reject encoded containers and misaligned buffers with errors that say what to fix.
  • New STTTranscribeOptions.sampleRate resamples 8–48 kHz PCM to the model rate natively; rates outside that range reject.
  • language is now selectable per call and per listening session; when omitted, the model auto-detects instead of forcing English.
  • README documents the audio format, language behavior, and the NSMicrophoneUsageDescription requirement.

Review notes

  • Validation rules live in a pure STTAudioContract (no MLX import) with swiftc-run tests; TS guards mirror them. Design notes: docs/superpowers/specs/2026-08-11-oss-30-stt-audio-contract-design.md.
  • Sample copying now occurs synchronously in the bridge call. Before, the JS ArrayBuffer was read inside a detached Task, after its guaranteed lifetime.
  • On-device inference is not testable in CI; only the contract layer is unit-tested.

Closes OSS-30.

STT.transcribe/transcribeStream previously treated every nonempty
ArrayBuffer as native-endian mono Float32 at 16 kHz and forced English.
Encoded containers, misaligned buffers, and non-16-kHz PCM (including
the library's own 24 kHz TTS output) silently produced wrong text.

The buffer contract is now explicit and enforced on both sides of the
bridge: recognizable encoded containers (WAV, ID3-tagged MP3, FLAC, Ogg,
AIFF, CAF, MP4/M4A) and non-Float32-aligned byte lengths reject with
actionable errors. A new STTTranscribeOptions makes sampleRate explicit:
rates in 8-48 kHz are linearly resampled to 16 kHz natively before
inference, others reject. Language is now selectable per call (and per
listening session via STTListeningOptions); when omitted, Qwen3ASR
auto-detects instead of forcing English.

Contract rules live in a pure Foundation-only STTAudioContract covered
by swiftc-run unit tests; TS-side guards mirror them before crossing the
bridge. Sample copying now happens synchronously in the bridge call, so
the JS ArrayBuffer is no longer read from a detached Task. README
documents the audio format, language behavior, and the
NSMicrophoneUsageDescription requirement for live transcription.
@linear

linear Bot commented Aug 11, 2026

Copy link
Copy Markdown

OSS-30

Section comments that restated adjacent expect messages are gone; bare
validate calls in tests get named values instead. Remaining comments
state only what code cannot show: cross-language mirroring, the
deliberate MP3 frame-sync omission, the ArrayBuffer lifetime rule, and
NaN equality semantics.
@corasan
corasan merged commit ce8ec9b into main Aug 11, 2026
3 checks passed
@corasan
corasan deleted the feature/oss-30-define-and-validate-the-mlx-direct-stt-audio-contract branch August 11, 2026 23:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant