需要 macOS 14.0+ 与 Xcode 16.0+,TTSKit 还要求 macOS 15.0+/iOS 18.0+;首次运行会自动下载模型(TTSKit 默认 0.6B 约 1 GB);本地服务默认端口为 50060,且需用 --model 指定模型。
dependencies: [.package(url: "https://github.com/argmaxinc/argmax-oss-swift.git", from: "0.9.0")]
brew install whisperkit-cliREADME 未说明具体显存或 GPU 型号要求;TTSKit 基于 Core ML 在 Apple silicon 上运行,0.6B 模型约 1 GB 下载、1.7B 模型约 2.2 GB 且仅限 macOS。
在 Apple Silicon 上本地跑语音识别、说话人分离和语音合成的 Swift SDK
面向 Apple Silicon 的端侧语音 AI 工具包,用 Swift 在 iOS 和 macOS 上本地跑语音识别、说话人分离和语音合成,不用联网也能处理音频。
核心能力
- WhisperKit 基于 OpenAI Whisper 做端侧语音转文字
- SpeakerKit 用 Pyannote 做说话人分离
- TTSKit 基于 Qwen3-TTS 做端侧语音合成
- 提供 OpenAI 兼容的本地 HTTP 服务端点
- 支持增量加载长音频以控制内存占用
容易踩的坑
- v1.1.1最新正式版2026-10-08 · 2026-10-08 13:48 UTC
Highlights
v1.1.1is a patch release fixing some crashes identified by the community and a broader set of bug fixes, including removing several force unwraps, TTS text chunking, and improved download efficiency and error handling.HF Download Changes
- Hugging Face tokens are no longer automatically read from the file system, meaning tokens need to be either passed in to the config or set as an
HF_TOKENenvironment variable (macOS or CLI implementations). - Metadata checks on an existing model download no longer re-fetch small config files.
- Authentication errors now include the HTTP status code, for example
Authentication failed with status code: 403. - Canceling a Hub snapshot download now throws
CancellationError. Previously, it could return the partially downloaded folder, which looked like a successful download but was missing files.
Other API Updates
AudioStreamTranscribertakes an optionalinputDeviceIDto stream from a specific input device instead of the system default.WhisperKit.recommendedRemoteModelsandWhisperKit.fetchModelSupportConfigtake an optionaluseBackgroundSession, andWhisperKitnow passesuseBackgroundDownloadSessionthrough when it fetches the support config.TTSKitConfigandAudioOutput.startPlaybacktake an optionalpreserveExistingAudioSessionto reuse the app's current iOSAVAudioSessionsetup instead of reconfiguring it.
All optional with defaults for additional customizability.
Thanks to everyone who sent fixes and reproductions for this release. 🙏
What's Changed
- Add inputDeviceID passthrough to AudioStreamTranscriber by @DTM-7-RX in https://github.com/argmaxinc/argmax-oss-swift/pull/503
- TTSKit: mark the CoreML import in Qwen3SpeechDecoder as @preconcurrency by @daeho-ro in https://github.com/argmaxinc/argmax-oss-swift/pull/524
- Don't log the model variant before it is detected by @atiorh in https://github.com/argmaxinc/argmax-oss-swift/pull/536
- Stop recording when streaming transcription exits on error by @rodrigouroz in https://github.com/argmaxinc/argmax-oss-swift/pull/533
- Throw when a Hub snapshot download is cancelled by @rodrigouroz in https://github.com/argmaxinc/argmax-oss-swift/pull/534
- Reuse compatible iOS audio session in TTSKit playback by @shura-v in https://github.com/argmaxinc/argmax-oss-swift/pull/464
- Keep metadata requests as HEAD across the Hub's relative redirects by @a2they in https://github.com/argmaxinc/argmax-oss-swift/pull/544
- TTSKit : guard sampleFromProbs against zero-sum top-k probabilities (#450) by @achyutbenz19 in https://github.com/argmaxinc/argmax-oss-swift/pull/459
- Test on macOS 27, drop macOS 14, and speed up simulator unit tests by @ZachNagengast in https://github.com/argmaxinc/argmax-oss-swift/pull/545
- Hugging Face tokens env only, report response code on auth errors by @ZachNagengast in https://github.com/argmaxinc/argmax-oss-swift/pull/546
- Replace force-unwraps with typed errors during logits sampling by @ZachNagengast in https://github.com/argmaxinc/argmax-oss-swift/pull/547
- WhisperKit : propagate useBackgroundDownloadSession into config fetch (#337) by @achyutbenz19 in https://github.com/argmaxinc/argmax-oss-swift/pull/461
- TTSKit sentence boundary fix by @ZachNagengast in https://github.com/argmaxinc/argmax-oss-swift/pull/548
New Contributors
- @DTM-7-RX made their first contribution in https://github.com/argmaxinc/argmax-oss-swift/pull/503
- @daeho-ro made their first contribution in https://github.com/argmaxinc/argmax-oss-swift/pull/524
- @rodrigouroz made their first contribution in https://github.com/argmaxinc/argmax-oss-swift/pull/533
- @achyutbenz19 made their first contribution in https://github.com/argmaxinc/argmax-oss-swift/pull/459
Full Changelog: https://github.com/argmaxinc/argmax-oss-swift/compare/v1.1.0...v1.1.1
- Hugging Face tokens are no longer automatically read from the file system, meaning tokens need to be either passed in to the config or set as an
- v1.1.02026-08-06 · 2026-08-06 17:54 UTC
查看更新说明
Highlights
- WhisperKit: Significant peak memory reduction via incremental audio file loading: 70%+ savings for 3-hour audio input.
- TTSKit: ~40% end-to-end speedup with improved inference for Qwen3-TTS.
- SpeakerKit: speaker centroid embeddings are now available from a public interface, useful for matching speakers across separate diarization runs.
- Bug fixes for
promptTokens, Chinese word timestamps,transcribeWithOptions, TTS chunk boundaries, and the model load/cache path.
[!WARNING] TTSKit's default model variants changed (a one-time download on upgrade) and speech generation now requires macOS 15 / iOS 18. The previous assets remain supported - see Model Assets.
WhisperKit: Incremental File Loading
By default, WhisperKit decodes an entire audio file into memory before transcribing, which can lead to OOM situations for multi-hour audio files.
.incrementalstreams it from disk in bounded-memory chunks instead:let pipe = try await WhisperKit() let results = try await pipe.transcribe( audioPath: "path/to/large-audio.wav", audioInputOptions: AudioInputOptions(audioLoadingMode: .incremental) )Chunks are cut at silence (VAD) boundaries, so the transcript matches a full-file run with
chunkingStrategy: .vad- only peak memory differs. Tune with.incremental(chunkDuration:chunkBufferSize:)(defaults: 120s chunks, 2 chunks buffered at a time).Also available via CLI:
swift run argmax-cli transcribe \ --model large-v3-v20240930_626MB \ --audio-path "path/to/large-audio.wav" \ --incremental-loadingThis will become a default option in a future release, so let us know how it works for you!
TTSKit: Faster Models
Both Qwen3-TTS decoders now ship as multifunction Core ML assets, which lets us load slightly different model forward pass configurations using the same set of weights.
SpeechDecoder (
TTSKitConfig.speechDecoderMode):.latencyOptimized(default, 1 audio frame/call, ~80ms in length) for lowest time-to-first-audio, or.throughputOptimized(4 frames/call, ~320ms) to speed up throughput overall at the cost of a slower initial buffer.MultiCodeDecoder (
TTSKitConfig.multiCodeDecoderMode): a talker frame expands into 15 residual codes..stepped(default) does that in 16 Core ML calls with host-side sampling;.fuseddoes the whole frame in one call with sampling and lookups inside the graph.// Defaults: latency-optimized SpeechDecoder, stepped MultiCodeDecoder let tts = try await TTSKit() // Opt in let config = TTSKitConfig( speechDecoderMode: .throughputOptimized, multiCodeDecoderMode: .fused ) let fasterTTS = try await TTSKit(config)Via CLI:
swift run -c release argmax-cli tts --text "Hello there." \ --speech-decoder-mode throughputOptimized \ --multi-code-decoder-mode fused --playThe mode is read once at model load since it's considered an entirely separate model to the OS. Set it before constructing
TTSKit, or reload to switch at runtime. The two modes don't produce byte-identical audio, but it's audibly indistinguishable - fused sampling draws a different but equally valid sequence. The TTSKitExample app gains sidebar pickers for both modes.The layout is detected from the asset at load time, so the legacy single-function
W8A16variants keep working (#520) - see Model Assets.SpeakerKit: Speaker Centroid Embeddings
DiarizationResultnow carriesspeakerCentroidEmbeddings: [Int: [Float]]which can be used to link speakers across separate diarization runs without re-running th - v1.0.02026-05-01 · 2026-05-01 21:42 UTC
查看更新说明
Highlights
v1.0.0marks the graduation of the project from WhisperKit into the Argmax Open-Source SDK, a Swift package that ships WhisperKit, SpeakerKit, TTSKit, and ArgmaxCore together. Alongside the new name, this release brings full Swift 6 concurrency support, vendors theswift-transformersHub and Tokenizers sources directly into ArgmaxCore under their original Apache 2.0 license (see NOTICES), and adds a new dynamic library product for binary distribution.⚠️ This is a breaking release. Deprecated APIs have been removed, and a small number of APIs changed; see API changes below.
New name: Argmax Open-Source SDK
The new name reflects the project's evolution from a single kit (WhisperKit) into a multi-kit open-source SDK, and gives us room to add more models and capabilities in the future. This open-source SDK is the foundation that the Argmax Pro SDK is built on. The Pro SDK extends what's here with additional models and advanced features.
The package is now
argmax-oss-swiftand the CLI isargmax-cli. A new umbrella product,ArgmaxOSS, re-exports all core modules so a single import gives you everything:.package(url: "https://github.com/argmaxinc/argmax-oss-swift", from: "1.0.0")import ArgmaxOSS // WhisperKit + SpeakerKit + TTSKit + ArgmaxCoreSwift 6 Concurrency
The package is now Swift 6 compatible under Swift 6's approachable concurrency model, with
Sendableadopted where appropriate and async boundaries made explicit. CI builds against the latest Swift 6 toolchains alongside Swift 5.10.[!NOTE] The top-level kit classes (
WhisperKit,SpeakerKit,TTSKit) are not yetSendable. If you store an instance of these in aSendabletype or pass it through a@Sendableclosure, you may see warnings. FullSendableconformance for the top-level kit classes is planned for a future release.Vendored Hub and Tokenizers
Huge thanks to the Hugging Face team and the
swift-transformerscommunity. The project has been a foundational dependency for WhisperKit since day one.To give the OSS SDK a self-contained build and avoid version-resolution conflicts for downstream apps that already depend on
swift-transformers, we've vendored the Hub and Tokenizers source files directly intoSources/ArgmaxCore/External/under their original Apache 2.0 license (see NOTICES for full attribution). Every file carries the upstream attribution header, and all Argmax modifications are marked by// Argmax-modification:comments for easy auditing against upstream.The public API surface is exposed through opaque wrappers (
HubApiWrapper,TokenizerWrapper, andAutoTokenizerWrapper), so the embeddedHubandTokenizertypes stay internal toArgmaxCoreand won't collide if you importswift-transformersseparately in the same app.ArgmaxOSS Dynamic Library
A new
ArgmaxOSSDynamicproduct (in[email protected]) builds the SDK as a dynamic library.CLI
The renamed
argmax-clibundles transcription, diarization, TTS, and an OpenAI-compatible local server in one binary.Run the local server with streaming transcription support:
BUILD_ALL=1 swift run argmax-cli serve --model large-v3-v20240930_626MB --host 0.0.0.0 --port 50060The server responds to
POST /v1/audio/transcriptionsandPOST /v1/audio/translations, so existing OpenAI SDK clients work out of the box. See the README for client examples.API Changes
Removed deprecated APIs
All APIs previously marked `@a
- v0.18.02026-04-01 · 2026-04-01 03:25 UTC
查看更新说明
Highlights
This release refactors SpeakerKit around a new shared
ModelManagerbase class, unifying the download → load → unload lifecycle across kits and simplifying the SpeakerKit public API.The top-level entry point is now just:
let speakerKit = try await SpeakerKit()No config object required for the default case.
Architecture Changes
ModelManager(new,ArgmaxCore)A reusable base class for managing the full model lifecycle that all kits can now inherit from. It handles state transitions, error recovery, and concurrent load coalescing via an internal
LoadModelsCoordinator— concurrent callers toensureModelsLoaded()coalesce onto a single in-flight task rather than racing.Backend-specific I/O is delegated to a new
ModelLoaderprotocol:public protocol ModelLoader: AnyObject, Sendable { var modelFolder: String? { get } func resolveModels(downloader: ModelDownloader, progressCallback: ((Progress) -> Void)?) async throws -> String func load(from modelPath: String, prewarm: Bool) async throws func unload() async }SpeakerKitDiarizer(replacesSpeakerKitModelManager)SpeakerKitModelManagerhas been replaced bySpeakerKitDiarizer, which inherits fromModelManagerand conforms to the newDiarizerprotocol. Create it via the static factory:let diarizer = SpeakerKitDiarizer.pyannote(config: config)SpeakerKitModelManageris still available as a deprecated typealias pointing toSpeakerKitDiarizerso existing code compiles with a warning rather than breaking.Diarizerprotocol (new)A clean protocol for plugging in diarization backends:
public protocol Diarizer: Sendable { var modelState: ModelState { get } func downloadModels() async throws func loadModels() async throws func unloadModels() async func diarize(audioArray: [Float], options: (any DiarizationOptions)?, progressCallback: ...) async throws -> DiarizationResult }ModelDownloadConfig(new,ArgmaxCore)All download parameters (endpoint, repo, token, revision, background session flag) are now bundled in a
ModelDownloadConfigstruct rather than passed individually toModelDownloader. The existing convenience init still works unchanged.SpeakerKitConfig(new base class)PyannoteConfignow inherits fromSpeakerKitConfig, which adds aload: Boolflag. Whenfalse(the default), models are loaded lazily on the firstdiarize()call. Setload: trueto eager-load duringSpeakerKitinit.API Changes
SpeakerKitinitialization simplified// Before let config = PyannoteConfig() let speakerKit = try await SpeakerKit(config) // After let speakerKit = try await SpeakerKit()Local models path is now a
Stringinstead ofURL:// Before let config = PyannoteConfig(modelFolder: URL(filePath: "/path/to/models")) // After let config = PyannoteConfig(modelFolder: "/path/to/models")Lazy loading by default
Models are now loaded on the first
diarize()call unlessload: trueis set in config.CLI
diarizeandtranscribe --diarizationnow use the simplifiedSpeakerKit(config)API internally. No functional changes to CLI flags.What's Changed
- Fix SpeakerKit tests on iOS 17 by @a2they in https://github.com/argmaxinc/WhisperKit/pull/444
- Refactor SpeakerKit around a reusable ModelManager base class by @a2they in https://github.com/argmaxinc/WhisperKit/pull/452
Full Changelog: https://github.com/argmaxinc/WhisperKit/compare/v0.17.0...v0.18.0
- v0.17.02026-03-13 · 2026-03-13 15:38 UTC
查看更新说明
Highlights
We're excited to bring our commercial-grade speaker diarization framework, SpeakerKit, to open-source!
With NVIDIA Sortformer now powering real-time speaker diarization in the Argmax Pro SDK, we're open-sourcing our implementation of Pyannote 4 (community-1). Pyannote is well-known for solving the "who spoke when" problem and has shown strong results on datasets such as AMI, DIHARD, and VoxConverse. Read the blog post for architecture details and benchmarks.
Quickstart
Download, load, diarize, and generate RTTM (Rich Transcription Time Marked) output in just a few lines of code:
import SpeakerKit let speakerKit = try await SpeakerKit() let result = try await speakerKit.diarize(audioPath: "audio.wav") let rttm = speakerKit.generateRTTM(result: result)Key features
- End-to-end Pyannote-style diarization pipeline
- Automatically estimate the number of speakers or set it manually
- Utilities to add speaker information to WhisperKit outputs
- Standard RTTM export
Explore the new SpeakerKit README section for API documentation, configuration details, and optimization tips.
CLI
whisperkit-clinow includes a dedicateddiarizesubcommand:swift run -c release whisperkit-cli diarize --audio-path audio.wav --rttm-path output.rttmWith Homebrew:
brew install whisperkit-cli whisperkit-cli diarize --audio-path audio.wav --rttm-path output.rttmYou can also run transcription and diarization together using the new
--diarizationflag:whisperkit-cli transcribe --audio-path audio.wav --diarizationExample output:
---- Speaker Diarization Results ---- SPEAKER audio 1 0.220 7.360 What is RLHF? reinforcement learning with human feedback. What was that little magic ingredient to the dish that made it so much more delicious? <NA> A <NA> <NA> SPEAKER audio 1 7.610 14.850 - So we train these models on a lot of text data. And in that process, they learn something about the underlying representations of what's in here or in there. <NA> B <NA> <NA>Additional flags are available for speaker counts, model variants, clustering algorithms tuning, and more.
WhisperAX example updates
The WhisperAX example app has been updated with SpeakerKit support. It now includes diarization toggles, a flexible pipeline selector, and a Speakers tab for browsing labeled segments.
What's Changed
- Add SpeakerKit with Pyannote speaker diarization support by @a2they in https://github.com/argmaxinc/WhisperKit/pull/440
Full Changelog: https://github.com/argmaxinc/WhisperKit/compare/v0.16.0...v0.17.0
用户点评
登录用户的真实使用记录,可按版本和硬件筛选还没有人点评。第一个说出真实体验的人最有价值。
Argmax Open-Source SDK
Argmax Open-Source SDK Swift is a collection of turn-key on-device inference frameworks:
- WhisperKit for speech-to-text with OpenAI Whisper
- SpeakerKit for speaker diarization with Pyannote
- TTSKit for text-to-speech with Qwen-TTS
[!IMPORTANT] Argmax Pro SDK supports additional models and advanced features such as:
- Real-time transcription with speakers
- Frontier accuracy for your use case with custom vocabulary
- Argmax Local Server for non-native apps
- Android support with Argmax Pro SDK Kotlin
Further resources:
Table of Contents
Installation
Swift Package Manager
WhisperKit, TTSKit, and SpeakerKit are separate library products in the same Swift package. Add the package once and pick the products you need. You can also use the ArgmaxOSS umbrella product to import everything at once.
Prerequisites
- macOS 14.0 or later.
- Xcode 16.0 or later.
Xcode Steps
- Open your Swift project in Xcode.
- Navigate to
File>Add Package Dependencies.... - Enter the package repository URL:
https://github.com/argmaxinc/argmax-oss-swift. - Choose the version range or specific version.
- When prompted to choose library products, select ArgmaxOSS (all kits), or individual kits: WhisperKit, TTSKit, SpeakerKit.
Package.swift
Add the package dependency:
dependencies: [
.package(url: "https://github.com/argmaxinc/argmax-oss-swift.git", from: "0.9.0"),
],
Then add the products you need as target dependencies:
.target(
name: "YourApp",
dependencies: [
// Import everything at once:
.product(name: "ArgmaxOSS", package: "argmax-oss-swift"),
// Or pick individual kits:
// .product(name: "WhisperKit", package: "argmax-oss-swift"), // speech-to-text
// .product(name: "TTSKit", package: "argmax-oss-swift"), // text-to-speech
// .product(name: "SpeakerKit", package: "argmax-oss-swift"), // speaker diarization
]
),
Homebrew
You can install the command line app using Homebrew by running the following command:
brew install whisperkit-cli
WhisperKit
To get started with WhisperKit, you need to initialize it in your project.
Quick Example
This example demonstrates how to transcribe a local audio file:
import WhisperKit
// Initialize WhisperKit with default settings
Task {
let pipe = try? await WhisperKit()
let results = try? await pipe?.transcribe(audioPath: "path/to/your/audio.{wav,mp3,m4a,flac}")
let transcription = results?.map(\.text).joined(separator: " ")
print(transcription ?? "")
}
Memory-Efficient Loading for Large Files
By default WhisperKit loads the whole audio file into memory before transcribing. For long recordings, .incremental streams it from disk in bounded-memory chunks instead:
import WhisperKit
let pipe = try await WhisperKit()
let options = AudioInputOptions(audioLoadingMode: .incremental)
let results = try await pipe.transcribe(
audioPath: "path/to/large-audio.wav",
audioInputOptions: options
)
print(results.map(\.text).joined(separator: " "))
It splits the audio at silence (VAD) boundaries, so the result matches a full-file transcription run with chunkingStrategy: .vad — only peak memory differs. Tune the chunking with .incremental(chunkDuration:chunkBufferSize:), or pick channels with AudioInputOptions(channelMode:).
From the CLI, add --incremental-loading (optionally --incremental-chunk-duration / --incremental-chunk-buffer-size):
swift run argmax-cli transcribe --model large-v3-v20240930_626MB --audio-path "path/to/large-audio.wav" --incremental-loading
Model Selection
[!NOTE] Argmax recommends
large-v3-v20240930_626MBfor maximum multilingual accuracy andtinyfor the fastest debugging workflow.
| Whisper Version | WhisperKit Variant | Description |
|---|---|---|
| Large v3 Turbo (compressed) | large-v3-v20240930_626MB | Recommended across iOS and macOS for maximum accuracy |
| Large v3 Turbo | large-v3-v20240930_turbo | Recommended on macOS for maximum speed and accuracy |
| Base (multilingual) | base | |
| Base (English-only) | base.en | |
| Small (Multilingual) | small | |
| Small (English-only) | small.en | |
| Tiny (Multilingual) | tiny | |
| Tiny (English-only) | tiny.en | Smallest size, lowest accuracy. Only recommended for development & debugging. |
WhisperKit automatically downloads the recommended model for the device if not specified. You can also select a specific model by passing in the model name:
let pipe = try? await WhisperKit(WhisperKitConfig(model: "large-v3-v20240930_626MB"))
This method also supports glob search, so you can use wildcards to select a model:
let pipe = try? await WhisperKit(WhisperKitConfig(model: "large-v3-v20240930_626MB"))
Note that the model search must return a single model from the source repo, otherwise an error will be thrown.
For a list of available models, see our HuggingFace repo.
Generating Models
WhisperKit also comes with the supporting repo whisperkittools which lets you create and deploy your own fine tuned versions of Whisper in CoreML format to HuggingFace. Once generated, they can be loaded by simply changing the repo name to the one used to upload the model:
let config = WhisperKitConfig(model: "large-v3-v20240930_626MB", modelRepo: "username/your-model-repo")
let pipe = try? await WhisperKit(config)
Swift CLI
The Swift CLI allows for quick testing and debugging outside of an Xcode project. To install it, run the following:
git clone https://github.com/argmaxinc/argmax-oss-swift.git
cd argmax-oss-swift
Then, setup the environment and download your desired model.
make setup
make download-model MODEL=large-v3-v20240930_626MB
Note:
- This will download only the model specified by
MODEL(see what's available in our HuggingFace repo, where we use the prefixopenai_whisper-{MODEL}) - Before running
download-model, make sure git-lfs is installed
If you would like download all available models to your local folder, use this command instead:
make download-models
You can then run them via the CLI with:
swift run argmax-cli transcribe --model-path "Models/whisperkit-coreml/openai_whisper-large-v3-v20240930_626MB" --audio-path "path/to/your/audio.{wav,mp3,m4a,flac}"
Which should print a transcription of the audio file. If you would like to stream the audio directly from a microphone, use:
swift run argmax-cli transcribe --model-path "Models/whisperkit-coreml/openai_whisper-large-v3-v20240930_626MB" --stream
Local Server
The Argmax CLI includes a local server that implements the OpenAI Audio API, allowing you to use existing OpenAI SDK clients or generate new ones. The server supports transcription and translation with output streaming capabilities (real-time transcription results as they're generated).
[!NOTE] Argmax Pro Local Server provides a real-time streaming transcription with a WebSocket local server that is API-compatible with cloud-based providers such as Deepgram.
Building the Server
# Build with server support
make build-local-server
# Or manually with the build flag
BUILD_ALL=1 swift build --product argmax-cli
Starting the Server
# Start server with default settings
BUILD_ALL=1 swift run argmax-cli serve
# Custom host and port
BUILD_ALL=1 swift run argmax-cli serve --host 0.0.0.0 --port 8080
# With specific model and verbose logging
BUILD_ALL=1 swift run argmax-cli serve --model tiny --verbose
# See all configurable parameters
BUILD_ALL=1 swift run argmax-cli serve --help
API Endpoints
- POST
/v1/audio/transcriptions- Transcribe audio to text - POST
/v1/audio/translations- Translate audio to English
Supported Parameters
| Parameter | Description | Default |
|---|---|---|
file | Audio file (wav, mp3, m4a, flac) | Required |
model | Model identifier | Server default |
language | Source language code | Auto-detect |
prompt | Text to guide transcription | None |
response_format | Output format (json, verbose_json) | verbose_json |
temperature | Sampling temperature (0.0-1.0) | 0.0 |
timestamp_granularities[] | Timing detail (word, segment) | segment |
stream | Enable streaming | false |
Client Examples
Python Client (OpenAI SDK)
cd Examples/ServeCLIClient/Python
uv sync
python whisperkit_client.py transcribe --file audio.wav --language en
python whisperkit_client.py translate --file audio.wav
Quick Python example:
from openai import OpenAI
client = OpenAI(base_url="http://localhost:50060/v1")
result = client.audio.transcriptions.create(
file=open("audio.wav", "rb"),
model="tiny" # Model parameter is required
)
print(result.text)
Swift Client (Generated from OpenAPI Spec, see ServeCLIClient/Swift/updateClient.sh)
cd Examples/ServeCLIClient/Swift
swift run whisperkit-client transcribe audio.wav --language en
swift run whisperkit-client translate audio.wav
CurlClient (Shell Scripts)
cd Examples/ServeCLIClient/Curl
chmod +x *.sh
./transcribe.sh audio.wav --language en
./translate.sh audio.wav --language es
./test.sh # Run comprehensive test suite
Generating the API Specification
The server's OpenAPI specification and code are generated from the official OpenAI API:
# Generate latest spec and server code
make generate-server
Client Generation
You can generate clients for any language using the OpenAPI specification, for example:
# Generate Python client
swift run swift-openapi-generator generate scripts/specs/localserver_openapi.yaml \
--output-directory python-client \
--mode client \
--mode types
# Generate TypeScript client
npx @openapitools/openapi-generator-cli generate \
-i scripts/specs/localserver_openapi.yaml \
-g typescript-fetch \
-o typescript-client
API Limitations
Compared to the official OpenAI API, the local server has these limitations:
- Response formats: Only
jsonandverbose_jsonsupported (no plain text, SRT, VTT formats) - Model selection: Client must launch server with desired model via
--modelflag
Fully Supported Features
The local server fully supports these OpenAI API features:
- Include parameters:
logprobsparameter for detailed token-level log probabilities - Streaming responses: Server-Sent Events (SSE) for real-time transcription
- Timestamp granularities: Both
wordandsegmentlevel timing - Language detection: Automatic language detection or manual specification
- Temperature control: Sampling temperature for transcription randomness
- Prompt text: Text guidance for transcription style and context
TTSKit
TTSKit is an on-device text-to-speech framework built on Core ML. It runs Qwen3-TTS models entirely on Apple silicon with real-time streaming playback, no server required.
- macOS 15.0 or later.
- iOS 18.0 or later.
Quick Example
This example demonstrates how to generate speech from text:
import TTSKit
Task {
let tts = try await TTSKit()
let result = try await tts.generate(text: "Hello from TTSKit!")
print("Generated \(result.audioDuration)s of audio at \(result.sampleRate)Hz")
}
TTSKit() automatically downloads the default 0.6B model on first run. The tokenizer and CoreML models are loaded lazily on the first generate() call.
Model Selection
TTSKit ships two model sizes. You can select the model by passing a variant to TTSKitConfig:
// Fast, runs on all platforms (~1 GB download)
let tts = try await TTSKit(TTSKitConfig(model: .qwen3TTS_0_6b))
// Higher quality, macOS only (~2.2 GB download, supports style instructions)
let tts = try await TTSKit(TTSKitConfig(model: .qwen3TTS_1_7b))
Models are hosted on HuggingFace and cached locally after the first download.
Custom Voices
You can choose from 9 built-in voices and 10 languages:
let result = try await tts.generate(
text: "こんにちは世界",
speaker: .onoAnna,
language: .japanese
)
Voices: .ryan, .aiden, .onoAnna, .sohee, .eric, .dylan, .serena, .vivian, .uncleFu
Languages: .english, .chinese, .japanese, .korean, .german, .french, .russian, .portuguese, .spanish, .italian
Real-Time Streaming Playback
play streams audio to the device speakers frame-by-frame as it is generated:
try await tts.play(text: "This starts playing before generation finishes.")
You can control how much audio is buffered before playback begins. The default .auto strategy measures the first generation step and pre-buffers just enough to avoid underruns:
try await tts.play(
text: "Long passage...",
playbackStrategy: .auto
)
Other strategies include .stream (immediate, no buffer), .buffered(seconds:) (fixed pre-buffer), and .generateFirst (generate all audio first, then play).
Speech Decoder Mode
TTSKit's default speech decoder bundles two functions, selectable via TTSKitConfig.speechDecoderMode:
| Mode | RVQ frames / call | Audio / call | Use case |
|---|---|---|---|
.latencyOptimized (default) | 1 | ~80 ms | Lowest time-to-first-audio for streaming. |
.throughputOptimized | 4 | ~320 ms | Amortizes decoder overhead for higher throughput, at the cost of a ~4× larger first-buffer latency. |
// Default: latency-optimized (lowest time-to-first-audio)
let tts = try await TTSKit()
// Opt into throughput-optimized generation
let config = TTSKitConfig(speechDecoderMode: .throughputOptimized)
let throughputTTS = try await TTSKit(config)
The mode is read once when models are loaded; set it before constructing TTSKit (or reload the model to switch at runtime).
Generation Options
You can customize sampling, chunking, and concurrency via GenerationOptions:
// Defaults recommended by Qwen
var options = GenerationOptions()
options.temperature = 0.9
options.topK = 50
options.repetitionPenalty = 1.05
options.maxNewTokens = 245
// Long text is automatically split at sentence boundaries
options.chunkingStrategy = .sentence
options.concurrentWorkerCount = nil // nil = all chunks run concurrently with a good default for the device
let result = try await tts.generate(text: longArticle, options: options)
Style Instructions (1.7B only)
The 1.7B model accepts a natural-language style instruction that controls prosody:
var options = GenerationOptions()
options.instruction = "Speak slowly and warmly, like a storyteller."
let result = try await tts.generate(
text: "Once upon a time...",
speaker: .ryan,
options: options
)
Saving Audio
Generated audio can be saved to WAV or M4A:
let result = try await tts.generate(text: "Save me!")
let outputDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
// Save as .wav or .m4a (AAC)
try await AudioOutput.saveAudio(result.audio, toFolder: outputDir, filename: "output", format: .m4a)
Progress Callbacks
You can receive per-step audio during generation. Return false from the callback to cancel early:
let result = try await tts.generate(text: "Hello!") { progress in
print("Audio chunk: \(progress.audio.count) samples")
if let stepTime = progress.stepTime {
print("First step took \(stepTime)s")
}
return true // return false to cancel
}
Swift CLI
The TTS command is available through the argmax-cli tool. You can generate speech and optionally play it back in real time:
swift run argmax-cli tts --text "Hello from the command line" --play
swift run argmax-cli tts --text "Save to file" --output-path output.wav
swift run argmax-cli tts --text "日本語テスト" --speaker ono-anna --language japanese
swift run argmax-cli tts --text-file article.txt --model 1.7b --instruction "Read cheerfully"
swift run argmax-cli tts --help
Demo App
The TTSKitExample example app showcases real-time streaming, model management, waveform visualization, and generation history on macOS and iOS. See the TTSKitExample README for build instructions.
SpeakerKit
SpeakerKit is an on-device speaker diarization framework built on Core ML. It runs Pyannote v4 (community-1) on Apple silicon to label speakers in audio. Read the blog post for architecture details and benchmarks.
- macOS 13.0 or later.
- iOS 16.0 or later.
Quick Example
This example demonstrates how to diarize an audio file:
import SpeakerKit
Task {
let speakerKit = try await SpeakerKit()
let audioArray = try AudioProcessor.loadAudioAsFloatArray(fromPath: "audio.wav")
let result = try await speakerKit.diarize(audioArray: audioArray)
print("Detected \(result.speakerCount) speakers")
for segment in result.segments {
print(segment)
}
}
SpeakerKit() uses PyannoteConfig() defaults, automatically downloading models from HuggingFace on first run. The segmenter and embedder CoreML models are loaded lazily (unless load is set on config) on the first diarize() call.
Diarization Options
You can control speaker detection via PyannoteDiarizationOptions:
let audioArray = try AudioProcessor.loadAudioAsFloatArray(fromPath: "audio.wav")
let options = PyannoteDiarizationOptions(
numberOfSpeakers: 2, // nil = automatic detection
clusterDistanceThreshold: 0.6, // clustering threshold
useExclusiveReconciliation: false // exclusive speaker assignment per frame
)
let result = try await speakerKit.diarize(audioArray: audioArray, options: options)
For local models, skip the download step:
let config = PyannoteConfig(modelFolder: "/path/to/models")
let speakerKit = try await SpeakerKit(config)
Combining with Transcription
SpeakerKit can merge diarization results with WhisperKit transcriptions to produce speaker-attributed segments:
import WhisperKit
import SpeakerKit
let whisperKit = try await WhisperKit()
let speakerKit = try await SpeakerKit()
let audioArray = try AudioProcessor.loadAudioAsFloatArray(fromPath: "audio.wav")
let transcription = try await whisperKit.transcribe(audioArray: audioArray)
let diarization = try await speakerKit.diarize(audioArray: audioArray)
let speakerSegments = diarization.addSpeakerInfo(to: transcription)
for group in speakerSegments {
for segment in group {
print("\(segment.speaker): \(segment.text)")
}
}
Two strategies are available for matching speakers to transcription:
.subsegment(default) -- splits segments at word gaps, then assigns speakers.segment-- assigns a speaker to each transcription segment as a whole
RTTM Output
Generate RTTM output:
let speakerKit = try await SpeakerKit()
let audioArray = try AudioProcessor.loadAudioAsFloatArray(fromPath: "meeting.wav")
let diarization = try await speakerKit.diarize(audioArray: audioArray)
let rttmLines = SpeakerKit.generateRTTM(from: diarization, fileName: "meeting")
for line in rttmLines {
print(line)
}
Swift CLI
The diarization commands are available through the argmax-cli tool:
# Standalone diarization
swift run argmax-cli diarize --audio-path audio.wav --verbose
# Save RTTM output
swift run argmax-cli diarize --audio-path audio.wav --rttm-path output.rttm
# Specify number of speakers
swift run argmax-cli diarize --audio-path audio.wav --num-speakers 3
# Transcription with diarization
swift run argmax-cli transcribe --audio-path audio.wav --diarization
# See all options
swift run argmax-cli diarize --help
Contributing & Roadmap
Our goal is to make this SDK better and better over time and we'd love your help! Just search the code for "TODO" for a variety of features that are yet to be built. Please refer to our contribution guidelines for submitting issues, pull requests, and coding standards, where we also have a public roadmap of features we are looking forward to building in the future.
External dependencies: Sources/ArgmaxCore/External/ contains a copy of swift-transformers (Hub and Tokenizers modules, v1.1.6) with Jinja-dependent code removed. When updating to a newer version, copy the fresh sources over that directory and re-apply the patches marked with // Argmax-modification: (grep -r "Argmax-modification:" Sources/ArgmaxCore/External/). The matching upstream tests are vendored under Tests/ArgmaxCoreTests/External/ using the same convention.
License
Argmax OSS is released under the MIT License. See LICENSE for more details.
This project incorporates third-party software under their own license terms. See NOTICES for attributions.
Citation
If you use this SDK for something cool or just find it useful, please drop us a note at [email protected]!
If you use WhisperKit, SpeakerKit or TTSKit for academic work, please cite the project using the following BibTeX:
@misc{whisperkit-argmax,
title = {Argmax OSS: On-device Speech AI with WhisperKit, SpeakerKit and TTSKit},
author = {Argmax, Inc.},
year = {2024},
URL = {https://github.com/argmaxinc/argmax-oss-swift}
}