Skip to content

GoWithitRoger/fluid-bridge

Repository files navigation

fluid-bridge mark

fluid-bridge

A small, unofficial Python adapter for the FluidAudio macOS command-line interface.

The bridge lets Python code call FluidAudio without loading Swift or Rust into the same process. This is an unaffiliated hobby project, maintained when time allows.

The adapter runs the separate FluidAudio CLI and returns its output and exit status. A small Python API and command-line wrapper cover common calls. See the capability matrix and validation guide for the details.

Important prerequisite

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. To use transcription, diarization, VAD, text-to-speech, or voice cloning, install the FluidAudio CLI or point the bridge at a local FluidAudio checkout. The bridge does not install FluidAudio. Its external process controls model downloads and caching during inference.

Install

git clone https://github.com/GoWithitRoger/fluid-bridge.git
cd fluid-bridge
uv sync --all-extras --locked

Provide FluidAudio

Use 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.

Python usage

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().

CLI usage

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.wav

The named commands accept an upstream option tail after --. This covers common options without hiding the rest of FluidAudio's CLI:

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.7

Use raw -- to run an upstream FluidAudio command with its arguments unchanged. It also covers commands and options that do not have a named 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-sdm

Raw 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.wav

The 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 lists 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 specific findings. The probe recognizes common Swift compiler/SDK incompatibility messages and keeps the original toolchain diagnostics intact.

Relationship to FluidAudio

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 dependency research note documents how macOS, FluidAudio, and fluidaudio-rs fit together.

Prior art

Applications such as Senko use FluidAudio directly. fluid-bridge is only a reusable Python adapter around the official FluidAudio CLI; it does not provide an application pipeline.

Development

uv sync --all-extras --locked
uv run pytest
uv run ruff check .
uv run ty check

Default tests do not download FluidAudio models or run live inference.

Live macOS validation

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 -v

This runs root help and every source-audited safe command help path. The suite checks that unsafe help paths remain 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 -v

Set 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 do not share a reliable dry-run contract. The live suite never starts them. Run them manually only after choosing the dataset, storage cost, model-download policy, and benchmark limits:

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

Releases

Packages

Used by

Contributors

Languages