Skip to content

feat: add Apple SpeechAnalyzer (macOS 26+) batch transcription backend - #1

Open
mlaplante wants to merge 14 commits into
mainfrom
feat/apple-speechanalyzer-backend
Open

feat: add Apple SpeechAnalyzer (macOS 26+) batch transcription backend#1
mlaplante wants to merge 14 commits into
mainfrom
feat/apple-speechanalyzer-backend

Conversation

@mlaplante

@mlaplante mlaplante commented Jul 14, 2026

Copy link
Copy Markdown
Owner

Summary

Adds Apple's on-device SpeechAnalyzer / SpeechTranscriber (Speech framework, macOS 26+) as a batch transcription backend, selectable in the model list as "Built-in (Apple)". No downloadable weights — the OS holds the model. macOS/Apple-Silicon only; the feature is compiled out (inert stub) on every other platform.

How it works:

  • Swift↔Rust FFI bridge (src-tauri/swift/speech_analyzer*.swift + apple_speech.rs), mirroring the existing apple_intelligence bridge and compiled by build.rs (links Speech/AVFoundation).
  • New EngineType::AppleSpeech, ModelSource::System, LoadedEngine::AppleSpeech; availability-gated catalog entry.
  • Batch path: 16 kHz mono f32 → AVAudioConverterSpeechAnalyzer.start/finalizeAndFinishThroughEndOfInput → drain transcriber.results.
  • Availability via SpeechTranscriber.isAvailable; locale resolution via Apple's SpeechTranscriber.supportedLocale(equivalentTo:).
  • Language-asset install happens at model-load time only (never inside the transcribe call), with bounded timeouts so a stalled download can't wedge the load slot.

Design spec and implementation plan are committed under docs/superpowers/.

Before Submitting This PR

Please confirm you have done the following:

  • I have searched existing issues and pull requests (including closed ones) to ensure this isn't a duplicate
  • I have read CONTRIBUTING.md

Fixes #
Discussion:

Community Feedback

N/A for this fork PR. Upstream Handy is under a feature freeze; proposing this to cjpais/Handy would require gathering community support via Discussions first.

Testing

  • cargo test --lib: 132 passing, including new unit tests for locale resolution, availability gating, and catalog injection (present-iff-available, no id collision).
  • Standalone Swift smoke tests against the real macOS 26 SDK: word-accurate transcription ("The quick brown fox jumps over the lazy dog."), SpeechTranscriber.isAvailable == true, and locale resolution (enen-US, zh-Hantzh-TW, bogus xx-YY→nil).
  • bun run build (tsc + vite) green; both cargo build (real bridge) and HANDY_FORCE_SPEECH_STUB=1 cargo build (stub config) link.
  • Ran the dev app (bun run tauri dev) and produced a release .app bundle for parallel manual testing.
  • Known gaps: the real language-asset download path wasn't exercised (assets already cached on the test machine); the App Sandbox speech-recognition entitlement is documented but untested (App Sandbox isn't enabled); catalog speed/accuracy scores are placeholders, not benchmarked.

AI Assistance

  • AI was used (please describe below)

If AI was used:

  • Tools used: Claude Code (Opus 4.8), subagent-driven development workflow.
  • How extensively: Extensively. AI drove the full brainstorm → spec → plan → implementation → review cycle (each task got independent spec-compliance and code-quality review passes, plus a final integration review). The human directed scope and made the key decisions (personal build, batch-only, macOS-only). Committed docs/superpowers/specs/ and docs/superpowers/plans/ capture the design and plan.

mlaplante added 10 commits July 14, 2026 08:59
Add the Apple SpeechAnalyzer/SpeechTranscriber Swift<->Rust FFI bridge
scaffold (Task 1 of the apple-speechanalyzer-backend plan), mirroring the
existing apple_intelligence real/stub/header pattern so build.rs stays
uniform.

- speech_analyzer_bridge.h declares the full C surface (available,
  locale_installed, install_locale, transcribe, supported_locales_json,
  free, free_str).
- speech_analyzer.swift implements availability + supported_locales for
  real against the macOS 26 Speech framework; install_locale/transcribe
  are error-returning placeholders wired up in later tasks.
- speech_analyzer_stub.swift mirrors every symbol for toolchains/SDKs
  without Speech.framework, so both build configs link identically.
- build.rs compiles the bridge via swiftc + libtool into a static
  archive and links -framework Speech -framework AVFoundation. Unlike
  the FoundationModels bridge, Speech's SpeechAnalyzer/SpeechTranscriber
  types compile under a Command-Line-Tools-only swiftc, so the real/stub
  gate only checks for Speech.framework's presence in the SDK (plus a
  HANDY_FORCE_SPEECH_STUB escape hatch).
- apple_speech.rs provides the safe Rust wrapper with a cfg-gated real
  impl (macOS/aarch64) and a cross-platform stub, plus availability/
  locales unit tests proving the module is total and never panics.

Verified on this machine (CLT-only Swift 6.4 / SDK 27): the real bridge
compiles and links, apple_speech::available() returns true, and
supported_locales() returns 47 real BCP-47 locales from
SpeechTranscriber.supportedLocales.
…le(equivalentTo:) for availability & locale resolution
@mlaplante
mlaplante force-pushed the feat/apple-speechanalyzer-backend branch from d8baf26 to 0f6c321 Compare July 14, 2026 12:59
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