Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

Murf BVC for LiveKit Agents

Background voice cancellation for voice agents. Murf BVC keeps the person talking to your agent and removes everyone and everything else (TV, other people in the room, street and office noise) before your agent's VAD, turn detection and speech-to-text see the audio. Fewer false interruptions, fewer transcripts of someone else's words.

It runs on CPU inside your agent process, like LiveKit's own noise-cancellation plugin. No GPU and no extra services; audio never leaves your process.

Requirements

  • Python 3.10+ and livekit-agents 1.2+
  • Linux (glibc 2.28+: Ubuntu 20.04+, Debian 10+, RHEL/Rocky/Alma 8+, Amazon Linux 2023) on x86_64 or aarch64, or macOS 11+ (Apple Silicon) / 10.15+ (Intel)
  • x86_64 CPUs need AVX2 and FMA (Intel Haswell / 2013 or newer, any AMD Zen); on ARM, AWS Graviton2 or newer, or any Apple Silicon Mac
  • A Murf API key with BVC enabled, or an offline license key for on-premise and air-gapped deployments (contact Murf)

Install

pip install livekit-plugins-murf-bvc

To test the 0.3.0 pre-release, pin it: plain pip install skips pre-releases and installs 0.2.2.

pip install --pre livekit-plugins-murf-bvc==0.3.0rc2

Both versions work against Murf. 0.3.0 seals the model key to each agent process and signs its usage reports.

Quick start

Set MURF_API_KEY (the same Murf API key the Murf TTS plugin uses; in .env.local like your other keys), call murf_bvc.prewarm() in the server's setup function, and pass murf_bvc.BVC() as the noise cancellation for the room's audio input:

from livekit.agents import Agent, AgentServer, AgentSession, JobContext, JobProcess, cli, room_io
from livekit.plugins import murf_bvc

server = AgentServer()


def prewarm(proc: JobProcess):
    murf_bvc.prewarm()  # license + models, before any call is assigned


server.setup_fnc = prewarm


@server.rtc_session()
async def entrypoint(ctx: JobContext):
    session = AgentSession(...)  # your STT, LLM, TTS, VAD, turn detection
    await session.start(
        room=ctx.room,
        agent=Agent(instructions="You are a helpful assistant."),
        room_options=room_io.RoomOptions(
            audio_input=room_io.AudioInputOptions(noise_cancellation=murf_bvc.BVC()),
        ),
    )


if __name__ == "__main__":
    cli.run_app(server)

That's all most agents need. The defaults are what we recommend for production. On-premise or air-gapped, set MURF_BVC_LICENSE="mbvc1...." (an offline license from Murf) instead of MURF_API_KEY.

Why prewarm

BVC() never blocks your agent. Without prewarm(), though, the first call in a process waits for work that prewarm() does up front: fetching the first lease for an API key (a request to Murf), and decrypting and loading the models (about 0.3 s, which would otherwise happen inside LiveKit's audio pipeline when the first participant's audio arrives). Until the lease arrives, audio passes through unprocessed.

LiveKit runs the setup function once per worker process while the process waits in the idle pool, so the time it takes is never on a call. It is the same place agents load their VAD model. prewarm() takes the same arguments as BVC(), returns True when ready, and returns False (logging why) if there is no valid license within timeout seconds (default 5); it never raises.

Keys

Like LiveKit's other plugins, BVC reads its key from the environment unless you pass one:

Key Environment variable How it is checked
api_key: Murf API key MURF_API_KEY The plugin exchanges it with Murf for a short-lived license (a lease, valid for an hour) and renews every 15 minutes, reporting how many minutes of audio it processed. A key you revoke on Murf stops working within the hour. Needs outbound HTTPS to api.murf.ai.
license_key: offline license (mbvc1....) MURF_BVC_LICENSE Checked locally (signature and expiry); no network access. For on-premise and air-gapped deployments. Takes precedence over an API key.

If you'd rather not put the key in the environment, pass it in code to both calls, for example murf_bvc.prewarm(api_key=key) and murf_bvc.BVC(api_key=key) with the key from your secrets manager. A key passed in code applies only to that BVC value and the streams it creates (a BVC() without a key is not licensed by it), never enters LiveKit's options, and is not inherited by subprocesses or visible in the process environment, where crash reporters and debugging tools often collect it.

Any code running in your agent process (other plugins included) can read memory it shares with the agent, so no way of passing a key hides it from them. Treat the BVC key like the agent's other API keys.

Options: when to change them

murf_bvc.BVC(api_key=None, license_key=None, restore=True, normalize=True, native=True)
Option Default Change it when
api_key MURF_API_KEY You keep the key out of the environment: see Keys.
license_key MURF_BVC_LICENSE You run on-premise or air-gapped with an offline license from Murf (or keep it out of the environment). Takes precedence over api_key.
restore True Leave on. While only the caller is talking, it gives back anything the model removed, so the caller's own voice is never thinned and STT accuracy holds up on clean calls. It runs a speaker check every 0.5 s of speech. Set False only if you'd rather have maximum removal at all times, or need to save that bit of CPU.
normalize True Leave on. It levels quiet and loud callers before the model so both get the same quality. There is no production reason to turn it off.
native True Leave on. It runs BVC inside LiveKit's audio pipeline. It falls back to a Python processor on its own if your livekit version predates audio-filter plugins, so you only set False when debugging.

Rooms with several participants

Nothing to change: one BVC() value serves every participant, each with its own independent state.

If you use native=False (or BVCProcessor directly), each processor handles one track, so pass a selector instead:

noise_cancellation=lambda params: murf_bvc.BVC(native=False)

What to expect

  • Latency: 10 ms at 16 kHz, about 13.6 ms at 24 or 48 kHz (resampling included).
  • CPU: about 3% of one core per active participant on an AWS AMD EPYC instance, plus a speaker check every 0.5 s of speech while caller restore is on. Processing is single-threaded per stream, so it scales with the number of concurrent calls.
  • Audio: any sample rate LiveKit delivers (8–96 kHz); stereo input is mixed to mono.

License behaviour

Without a valid license BVC does not stop your agent: it logs an error and passes audio through unchanged, delayed by the same few milliseconds, so calls keep working and a license expiring mid-call never knocks audio out of sync.

With an API key, the first lease is fetched in the background (or by prewarm()); calls that start before it arrives pass audio through, then are processed once it does, without reconnecting. If Murf cannot be reached, processing continues on the current lease for at least 45 minutes; after it expires audio passes through until a renewal succeeds, then processing resumes on its own.

A refused key (HTTP 401, 402 or 403) is retried every 5 minutes, and audio passes through meanwhile. Once the account is fixed, processing starts within about 5 minutes without restarting the agent: for example after BVC is enabled on the account (it can take a few minutes to take effect) or credit is topped up. Restart the workers to pick it up immediately. A key passed in code or in MURF_API_KEY is read when the agent starts, so replacing the key itself needs a restart.

0.2.2 and earlier stop requesting leases after a refusal, so audio stays unprocessed until the workers restart.

Check a key at startup with the result of prewarm():

if not murf_bvc.prewarm():   # the reason is logged
    ...

Troubleshooting

All messages, including those from the native library inside LiveKit's audio pipeline, go to the Python logger livekit.plugins.murf_bvc, so they follow your agent's log level and format (JSON in production). License events carry license_sub and license_expires_at as structured fields.

You see Meaning
no license: pass BVC(api_key=...) or BVC(license_key=...), or set MURF_API_KEY or MURF_BVC_LICENSE No key was passed and neither variable is set.
HTTP 401 invalid_api_key ...; key refused, retrying in 300 s The Murf API key is wrong or revoked. Set the right key and restart the agent workers.
HTTP 402 ...; key refused, retrying in 300 s The key's Murf account has no plan or credit covering BVC; check billing on Murf. Processing resumes within 5 minutes of topping up.
HTTP 403 not_entitled ...; key refused, retrying in 300 s The key's Murf account does not include BVC, or it was enabled only minutes ago; contact Murf. Processing resumes within 5 minutes of it taking effect.
...; lease renewal stopped (0.2.2 and earlier) Same causes as the three rows above, but these versions do not retry: restart the agent workers after fixing the cause.
lease request failed (...); retrying Murf can't be reached (outbound HTTPS blocked, or an outage). Processing continues until the current lease expires.
prewarm: no valid license after 5.0 s The first lease did not arrive in time (or the key was refused: see the line before). Audio passes through until it arrives.
offline license for '...' expires in N days Ask Murf for a new offline license before that date.
license for '...' has expired Renew the key with Murf.
this license does not carry a model key; ask Murf to reissue it An older-format key; ask Murf for a reissued one.
Illegal instruction when the agent starts The CPU lacks AVX2/FMA (very old x86, or some emulated/virtualized CPUs). Use a newer instance type.
pip finds no matching distribution Python older than 3.10, an unsupported platform, or glibc older than 2.28.

Metadata

Release files for livekit-plugins-murf-bvc 0.3.0rc2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Built distributions (wheels)

Table of built distributions (wheels) for livekit-plugins-murf-bvc 0.3.0rc2
File Actions
livekit_plugins_murf_bvc-0.3.0rc2-py3-none-manylinux_2_28_x86_64.whl Python 3 none Linux glibc 2.28+ x86-64 Details for livekit_plugins_murf_bvc-0.3.0rc2-py3-none-manylinux_2_28_x86_64.whl
livekit_plugins_murf_bvc-0.3.0rc2-py3-none-manylinux_2_28_aarch64.whl Python 3 none Linux glibc 2.28+ ARM64 Details for livekit_plugins_murf_bvc-0.3.0rc2-py3-none-manylinux_2_28_aarch64.whl
livekit_plugins_murf_bvc-0.3.0rc2-py3-none-macosx_11_0_arm64.whl Python 3 none macOS 11.0+ ARM64 Details for livekit_plugins_murf_bvc-0.3.0rc2-py3-none-macosx_11_0_arm64.whl
livekit_plugins_murf_bvc-0.3.0rc2-py3-none-macosx_10_15_x86_64.whl Python 3 none macOS 10.15+ x86-64 Details for livekit_plugins_murf_bvc-0.3.0rc2-py3-none-macosx_10_15_x86_64.whl

Total release size: 157.2 MB

Release files / livekit_plugins_murf_bvc-0.3.0rc2-py3-none-manylinux_2_28_x86_64.whl

Download URL livekit_plugins_murf_bvc-0.3.0rc2-py3-none-manylinux_2_28_x86_64.whl
Size 39.2 MB
Tags Linux glibc 2.28+ x86-64 Python 3
SHA-256 checksum
How to use checksums
b3c698b052fb4b558d6caa00b57bb454a796d26c34ed34e1a133aeaf117e593a
BLAKE2b-256 checksum
How to use checksums
9cadfdb7c92614706dae7efdddbcb234da3e77204f185ca6478f53215648b4d7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 10, 2026.

Transparency log

Release files / livekit_plugins_murf_bvc-0.3.0rc2-py3-none-manylinux_2_28_aarch64.whl

Download URL livekit_plugins_murf_bvc-0.3.0rc2-py3-none-manylinux_2_28_aarch64.whl
Size 39.2 MB
Tags Linux glibc 2.28+ ARM64 Python 3
SHA-256 checksum
How to use checksums
ee26bb5af876a84b07ecc6234bb90a72e34f9c328543850a6dd7d8e7f8705a80
BLAKE2b-256 checksum
How to use checksums
381fcebddab49a3872061f6027873addde26f008bd4744a8bcb46f284789154b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 10, 2026.

Transparency log

Release files / livekit_plugins_murf_bvc-0.3.0rc2-py3-none-macosx_11_0_arm64.whl

Download URL livekit_plugins_murf_bvc-0.3.0rc2-py3-none-macosx_11_0_arm64.whl
Size 39.5 MB
Tags Python 3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
e9aecd9b085dc24ee63ec30dc482203ad5665daa65fb0e6ff3531197402df04b
BLAKE2b-256 checksum
How to use checksums
565ed95c07cddbd37493c5a0759345d40c495ea5afbfa398c5f29d30a97cb63b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 10, 2026.

Transparency log

Release files / livekit_plugins_murf_bvc-0.3.0rc2-py3-none-macosx_10_15_x86_64.whl

Download URL livekit_plugins_murf_bvc-0.3.0rc2-py3-none-macosx_10_15_x86_64.whl
Size 39.3 MB
Tags Python 3 macOS 10.15+ x86-64
SHA-256 checksum
How to use checksums
b0838f630da041ba5ccdeacf780e00195c1659b88acdb93f526d8c9e49687355
BLAKE2b-256 checksum
How to use checksums
53653d28e01c28fc2c687ff33384a4748f8ae6f2e1feb795cb7ef815cd94e192
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 10, 2026.

Transparency log

Security for livekit-plugins-murf-bvc 0.3.0rc2

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 4 files in this release have been uploaded with provenance metadata.

Select from 4 files

Attestation for livekit_plugins_murf_bvc-0.3.0rc2-py3-none-manylinux_2_28_x86_64.whl

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 10, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page