NC_OPT_URL set. Flipping a toggle in the dashboard is not the same as the filter running.
AIC filter dead
What it is. An "AI noise cancellation" input filter that was meant to sit at audio-in alongside the WebRTC processor. The toggle still exists in the UI and the zod schema, and the agent still reads the value at boot.
Runtime. Read at base_service.py:1395-1408 — but at :1407-1408 the code logs that AIC is hard disabled due to crate instability and never instantiates the filter. The boolean has no effect: checked or unchecked, the audio path is identical.
aicEnabled: true, no AIC filter is ever placed in the chain. The dashboard switch is decorative. Use WebRTC APM (Step 30) for cheap CPU-side cleanup, or DeepFilterNet (Step 29) where a GPU noise-cancel service exists.
Symptom it does NOT fix: "there's background noise" — flipping AIC will appear to do something in the UI and change nothing on the call. Reach for WebRTC APM or DeepFilterNet instead.
DeepFilterNet noise cancellation env-gated
What it is. A neural denoiser that strips steady and transient background noise. Critically, it is not an audio-processor-chain filter — it does not sit between transport-in and STT. Instead it wraps the VAD analyzer: the Silero VAD is swapped for a DeepFilterNetVADAnalyzer that denoises internally before deciding speech/no-speech.
Runtime. Built in base_service.py:1557-1567. The toggle alone is not enough: the code checks for the NC_OPT_URL environment variable. If it is unset it logs nc_opt_url_set=False and falls back to plain SileroVADAnalyzer — the denoiser never runs. Only when both the toggle is on and NC_OPT_URL points at a running nc-opt service does the VAD become a DeepFilterNetVADAnalyzer.
NC_OPT_URL is set
deepFilterEnabled: true on a deployment with no NC_OPT_URL = plain Silero VAD, zero denoising. It is an env+toggle pair, not a toggle alone. This is the single most common "I turned it on and nothing changed" trap (see the checkpoint at the end of this part).
- When to change: heavy, persistent background noise (call-center floor, traffic) and the GPU nc-opt service is deployed. Otherwise prefer the cheap WebRTC APM first.
Symptom it fixes: "the noise behind me makes it mishear / cuts me off" — but only when the env var is wired up.
WebRTC Audio Processing (APM) live
What it is. The WebRTC Audio Processing Module: noise suppression (NS), automatic gain control (AGC), acoustic echo cancellation (AEC), and a high-pass filter — a cheap, robust, CPU-side cleanup that sits right at audio-in.
Runtime. The filter class is wired at boot_steps.py:158, placed on the transport's audio-in. The AEC reverse tap — feeding the bot's own TTS output back as the echo reference signal — is added only when echo cancellation is active, at boot_steps.py:2542-2554.
- When to change: a sensible first filter for almost any noisy or echoey telephony deployment — far cheaper than DeepFilterNet. AGC also lifts quiet callers.
Symptom it fixes: "quiet callers aren't heard" (AGC), "there's echo" (AEC), "constant hiss" (NS) — WebRTC APM addresses all three at once.
Smart turn (AI end-of-turn) live
What it is. Replaces pure-silence endpointing with an AI model that predicts when the caller has actually finished, from prosody (intonation, trailing pitch) rather than from a fixed silence timer.
Runtime. base_service.py:1525-1535 swaps the turn-stop strategy to TurnAnalyzerUserTurnStopStrategy(LocalSmartTurnAnalyzerV3()) instead of the timeout-based SpeechTimeoutUserTurnStopStrategy. It changes when the turn ends, not the audio content.
- When to change: callers who pause mid-thought (reading numbers off a card, hesitating) where pure-silence endpointing cuts them off. Adds inference latency per turn, so leave it off when latency is critical — raise
vadStopSecs/waitSeconds(Part 7) first as the cheaper fix.
Symptom it fixes: "it interrupts when I pause to think / read a number off my card."
base_service.py:1395-1408 and confirm whether aicEnabled ever instantiates a filter, and show me base_service.py:1557-1567 and confirm deepFilterEnabled falls back to plain Silero when NC_OPT_URL is unset."Order in the chain & cost/latency tradeoffs
These four toggles do not all live in the same place. WebRTC APM is a true audio-in filter; DeepFilterNet lives inside the VAD; smart turn changes the turn-stop decision after STT; AIC is nowhere (hard-disabled). The effective runtime order is:
| Filter | Cost | Enable when |
|---|---|---|
| WebRTC APM | low (CPU) | almost always for telephony noise/echo — start here |
| DeepFilterNet | high (GPU, WebSocket) | heavy noise and the nc-opt service (NC_OPT_URL) is deployed |
| AIC | n/a | dead — skip (hard-disabled in code) |
| Smart turn | medium (per-turn inference) | pause-heavy callers, clean-ish audio; off when latency-critical |
Try it — filter chain visualizer
NC_OPT_URL).Noisy-call-center recipe
For a loud bank call center, layer cheap-to-expensive:
- Enable WebRTC APM first — cheap, CPU-side, fixes most of it (NS + AGC + AEC).
- Add DeepFilterNet only if noise is still bleeding into the VAD and the GPU nc-opt service exists (
NC_OPT_URLset). - Raise
numberOfWordsto 2–3 (Part 3) so a stray noise burst doesn't trigger barge-in. - Set
botSpeechGraceSecs(Part 7) to kill echo-driven false interrupts at the start of the bot's utterance. - Don't bother with AIC — it's hard-disabled.
Try it — cost/latency picker
NC_OPT_URL set in the agent env, and is deepFilterEnabled true in the published STT config? If the toggle is on but the env var is missing, DeepFilterNet is silently a no-op."Checkpoint: you enable DeepFilterNet for a noisy customer and nothing improves. First thing to check?
Whether NC_OPT_URL is set on that deployment. The toggle is a no-op without it (base_service.py:1557-1567 logs nc_opt_url_set=False and falls back to plain Silero VAD). It is an env+toggle pair, not a toggle alone.