Unofficial Python bindings for FluidAudio's macOS command-line interface.
FluidAudio is a Swift SDK for local audio AI on Apple devices. Upstream FluidAudio includes ASR/transcription, text-to-speech, voice activity detection, speaker diarization, speaker embeddings/identification, offline and streaming modes, and ANE/CoreML execution.
fluid-bridge is intentionally thin: it lets Python projects call FluidAudio's official CLI without
embedding Swift or Rust in the Python runtime. The first public release focuses on CLI execution and
conservative result handling; future releases may add an optional Rust/PyO3 backend over
fluidaudio-rs.
The support contract is complete CLI transport parity, not a duplicate implementation of the FluidAudio Swift SDK. See the full capability matrix and validation guide.
fluid-bridge is not a built-in macOS speech feature and it does not contain FluidAudio itself.
macOS supplies the Core ML runtime and Apple Silicon hardware that can execute compatible models;
it does not ship the FluidAudio SDK, fluidaudiocli, or FluidAudio's model assets.
This package is a Python adapter around a separate FluidAudio CLI installation. Before it can run transcription, diarization, VAD, text-to-speech, or voice cloning, provide FluidAudio by either installing its CLI or pointing the bridge at a local FluidAudio checkout. The bridge never installs FluidAudio. The external FluidAudio process, not this Python package, controls any model download and cache behavior triggered by an inference command.
git clone https://github.com/GoWithitRoger/fluid-bridge.git
cd fluid-bridge
uv sync --all-extras --lockedUse one of these setup paths for the separate FluidAudio dependency:
# Option 1: put a built FluidAudio CLI on PATH
fluidaudiocli --help
# Option 2: point fluid-bridge at a FluidAudio Swift package checkout
export FLUID_AUDIO_PACKAGE=/path/to/FluidAudio
swift run --package-path "$FLUID_AUDIO_PACKAGE" fluidaudiocli --help
# Option 3: provide an exact command
export FLUID_BRIDGE_CLI='swift run --package-path /path/to/FluidAudio fluidaudiocli'fluid-bridge doctor --probe verifies this connection without loading a model. The first actual
inference command may cause FluidAudio to download and cache the selected third-party model assets;
consult FluidAudio's upstream documentation for model availability, storage, proxy, and offline
controls.
from fluid_bridge import FluidAudioBridge
bridge = FluidAudioBridge()
doctor = bridge.doctor(probe_cli=True)
print(doctor.to_dict())
transcript = bridge.transcribe(
"meeting.wav",
model_version="v3",
streaming=True,
language="en",
output_json="transcript.json",
)
print(transcript.parsed_json)
print(transcript.artifacts["transcript"])
diarization = bridge.diarize(
"meeting.wav",
mode="offline",
threshold=0.6,
output_path="diarization.json",
export_embeddings="embeddings.json",
)
print(diarization.parsed_json)
vad = bridge.vad_analyze(
"meeting.wav",
streaming=True,
threshold=0.65,
export_wav="speech.wav",
)
print(vad.stdout)
tts = bridge.tts("Hello from FluidAudio.", "out.wav", backend="kokoro-ane")
tts.raise_for_error()CommandResult.artifacts contains only files produced by a successful command. When requested JSON
exists but cannot be decoded, parsed_json is None, parse_error describes the problem, and raw
stdout/stderr remain available.
For incremental Python output, start a streaming command and consume its stdout/stderr line events:
running = bridge.stream(["parakeet-eou", "--input", "meeting.wav"])
for event in running:
print(event.stream, event.text, end="")
result = running.wait()
result.raise_for_error()Call running.cancel() from application control flow to stop early. FluidAudioCLIConfig(timeout_s=...)
applies one deadline across event iteration and wait().
fluid-bridge doctor
fluid-bridge doctor --probe
fluid-bridge capabilities
fluid-bridge capabilities --deep
fluid-bridge capabilities --deep --include-additional
fluid-bridge transcribe meeting.wav --model-version v2
fluid-bridge diarize meeting.wav --mode offline --threshold 0.6
fluid-bridge vad meeting.wav --streaming --threshold 0.65
fluid-bridge tts "Hello from FluidAudio." --backend kokoro-ane --output out.wavFriendly commands accept an upstream option tail after --. This keeps common options concise
while leaving every FluidAudio option available:
fluid-bridge transcribe meeting.wav --streaming --output-json result.json -- --custom-vocab terms.txt
fluid-bridge diarize meeting.wav --export-embeddings embeddings.json -- --num-speakers 3
fluid-bridge vad meeting.wav --export-wav speech.wav -- --min-silence-ms 400 --pad-ms 100
fluid-bridge tts "Hello" --backend pocket --clone-voice speaker.wav --output out.wav -- --temperature 0.7Use raw -- to run any upstream FluidAudio command with its arguments unchanged. This is the full
CLI compatibility path, including commands and options that do not yet have a friendly
fluid-bridge subcommand:
fluid-bridge raw -- parakeet-eou --input meeting.wav
fluid-bridge raw -- nemotron-transcribe --input meeting.wav --chunk-ms 160
fluid-bridge raw -- sortformer meeting.wav --offline --output speakers.json
fluid-bridge raw -- download --dataset ami-sdmRaw mode preserves FluidAudio's stdout, stderr, and exit status. The Python equivalent is
FluidAudioBridge.run(["command", "--option", "value"]).
Add --live before the separator for interactive or long-running commands. Live mode inherits
stdin, stdout, and stderr so progress appears immediately, and it mirrors FluidAudio's signal exit:
fluid-bridge raw --live -- multi-stream microphone.wav system-audio.wavThe Python equivalent is FluidAudioBridge.run_live(...).
fluid-bridge capabilities compares the installed CLI's root help with the command baseline audited
from FluidAudio commit 372eb32a. FluidAudio's root help does not list every registered command, so
baseline_not_advertised means only that a command was absent from root help; it is not treated as
proof that the installed CLI cannot run it. additional_commands highlights newly advertised
upstream commands while raw mode keeps them immediately usable. If help cannot be parsed reliably,
probe_ok is false and both delta lists remain empty.
Add --deep to probe every known-safe command help surface and collect its installed long options,
raw help output, diagnostics, and exit status. Five pinned commands do not implement a safe
command --help path: download, unified-benchmark, multi-stream, lseend, and
cohere-transcribe. The report marks them as skipped instead of risking model, corpus, dataset, or
audio work. Their full argument surfaces remain available through raw mode.
Newly advertised commands are reported but not executed unless --include-additional is explicit,
because the bridge cannot yet know whether their help paths are free of side effects.
doctor reports platform, CLI discovery, Swift, and xcode-select state without running FluidAudio.
Add --probe (or probe_cli=True in Python) to execute root help and receive a readiness result plus
the exact command, exit status, stdout, stderr, and actionable findings. The probe recognizes common
Swift compiler/SDK incompatibility messages and keeps the original toolchain diagnostics intact.
This project is an unofficial Python adapter. FluidAudio is upstream-owned by Fluid Inference and
has its own source, model, and third-party licensing. Core ML is Apple technology for packaging and
running compatible models; the models FluidAudio uses are not Apple-provided macOS speech models.
FluidAudio converts or integrates third-party open models for Core ML/ANE execution and may download
their assets on first use. See the upstream documentation for model provenance, registry, proxy, and
offline-mode controls. The source-backed dependency research note
explains the relationship among macOS, FluidAudio, and fluidaudio-rs.
Many projects use FluidAudio directly in apps and pipelines, including Senko and several macOS/iOS
dictation or meeting tools. fluid-bridge is narrower: a small reusable Python adapter around the
official FluidAudio CLI, not an application pipeline.
uv sync --all-extras --locked
uv run pytest
uv run ruff check .
uv run ty checkDefault tests do not download FluidAudio models or run live inference.
Point the bridge at a real CLI or checkout, then explicitly enable the no-download live tier:
export FLUID_AUDIO_PACKAGE=/path/to/FluidAudio
FLUID_BRIDGE_LIVE=1 uv run pytest -m live -vThis runs root help and every source-audited safe command help path. Unsafe upstream help paths are
asserted as skipped. Model-backed smoke tests require both download consent and a capability-specific
input, so setting FLUID_BRIDGE_LIVE=1 alone cannot start inference or download models:
export FLUID_BRIDGE_LIVE=1
export FLUID_BRIDGE_LIVE_ALLOW_DOWNLOADS=1
export FLUID_BRIDGE_LIVE_AUDIO=/absolute/path/to/short.wav
export FLUID_BRIDGE_LIVE_TTS=1
uv run pytest -m live_inference -vSet FLUID_BRIDGE_LIVE_VOICE=/absolute/path/to/reference.wav to include PocketTTS voice cloning.
Use FLUID_BRIDGE_LIVE_TIMEOUT to change the per-command timeout from its 600-second default.
Dataset downloads and full benchmarks have no universal dry-run contract upstream. They are never started by the live suite. Run them manually through raw mode only after choosing the dataset, storage cost, model-download policy, and benchmark limits, for example:
fluid-bridge raw -- download --dataset ami-sdm
fluid-bridge raw -- asr-benchmark --subset test-clean --max-files 10
fluid-bridge raw -- diarization-benchmark --dataset ami-sdm --single-file ES2004a
fluid-bridge raw -- tts-benchmark --backend kokoro-ane --skip-asr