Skip to content

Conversation

@Mishikasardana
Copy link

@Mishikasardana Mishikasardana commented Jan 16, 2026

feat: Add intelligent interruption handling
Fixes an issue where the agent stops speaking when the user says simple listening words like “yeah” or “ok” during agent speech.

Changes
Added an InterruptionFilter to ignore backchanneling words
Integrated the filter into the agent’s interruption logic
Added configuration options to:
Enable or disable the filter
Customize the list of ignored words
All four test cases are passing
How it works
When the user speaks while the agent is talking, the filter checks:

Whether the agent is currently speaking
Whether the user’s input contains only backchanneling words
If both conditions are true, the interruption is ignored and the agent continues speaking.
Otherwise, the agent is interrupted as usual.

Summary by CodeRabbit

  • New Features

    • Interruption filter added and exposed in the public API to ignore backchannel-like utterances; enabled by default and configurable per session (toggle, custom ignore words, case-sensitivity).
  • Documentation

    • Added comprehensive docs and a proof log with usage examples, config options, env-var support, and performance notes.
  • Tests

    • New unit/integration tests covering detection, config, punctuation/case handling, and runtime updates.

✏️ Tip: You can customize this high-level summary in your review settings.

Added detailed documentation for the interruption filter feature, including usage examples, configuration options, and implementation details.
Added a comprehensive proof of functionality for the interruption filter, including test execution logs, results, scenarios, and evaluation criteria.
Update test script name in README.
This script tests the functionality of the InterruptionFilter class by simulating various user inputs and agent states. It includes multiple test cases to validate the filter's behavior in different scenarios.
Added interruption filter to manage interruptions during speech.
Implements an interruption filter to manage user interruptions based on agent speaking status and predefined ignore words.
Add unit and integration tests for InterruptionFilter class
@CLAassistant
Copy link

CLAassistant commented Jan 16, 2026

CLA assistant check
All committers have signed the CLA.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 16, 2026

📝 Walkthrough

Walkthrough

Adds a new InterruptionFilter class, exposes it in the voice agents public API, wires filter options into AgentSession, consults the filter during audio interruption checks, and adds docs and tests validating backchannel suppression and configuration. (49 words)

Changes

Cohort / File(s) Summary
Documentation
FILTER_README.md, proof.md
New docs describing the interruption filter, usage, configuration (env var), examples, testing proof, and performance notes.
Core Implementation
livekit-agents/livekit/agents/voice/filter.py
New InterruptionFilter class with DEFAULT_IGNORE_WORDS, normalization, punctuation stripping, case-sensitive option, enable/disable, runtime add/remove, and should_ignore_interruption() logic.
Agent Integration
livekit-agents/livekit/agents/voice/agent_activity.py, livekit-agents/livekit/agents/voice/agent_session.py
AgentSession gains interruption_filter_enabled and interruption_ignore_words; agent constructs InterruptionFilter and consults it in _interrupt_by_audio_activity, using agent speaking state and transcription.
API Exposure
livekit-agents/livekit/agents/voice/__init__.py
Exposes InterruptionFilter and reorganizes __all__/__pdoc__ exports (reorders additions like TranscriptSynchronizer, ModelSettings, AgentSession, IO outputs, and events).
Examples
examples/voice_agents/basic_agent.py
Example updated to enable the filter: interruption_filter_enabled=True.
Tests
tests/filter.py, test.py
New unit and integration-style tests covering default/custom ignore words, enable/disable, punctuation handling, case sensitivity, dynamic updates, and speaking vs silent scenarios.

Sequence Diagram(s)

sequenceDiagram
    participant User as User
    participant Agent as VoiceAgent
    participant ASR as AudioRecognition
    participant Filter as InterruptionFilter
    participant Logic as AgentLogic

    User->>Agent: Speak (audio)
    Agent->>ASR: Transcribe audio
    ASR-->>Agent: Transcribed text
    Agent->>Agent: Evaluate agent_is_speaking
    Agent->>Filter: should_ignore_interruption(transcribed_text, agent_is_speaking)
    Filter->>Filter: Normalize / strip punctuation / tokenize
    Filter->>Filter: Compare tokens to ignore set
    Filter-->>Agent: Return ignore (true/false)
    alt ignore == true
        Agent->>Agent: Log debug and ignore interruption
    else
        Agent->>Logic: Proceed to start user activity / interrupt
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • longcw

Poem

🐰 I hop and listen soft and clear,
Filtering "uh" so voices steer,
Backchannels kept out of the fray,
Letting speakers have their say,
Happy chats hop on today.

🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.71% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'New branch' is generic and vague, failing to convey the actual changeset which implements an interruption filter for LiveKit Agents. Rename the title to something descriptive like 'Add InterruptionFilter to prevent agent interruption on backchanneling' to clearly communicate the main change.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🤖 Fix all issues with AI agents
In `@FILTER_README.md`:
- Around line 90-95: Update the Testing section to run the new pytest-based
tests: change the command that currently runs "python test.py" to invoke pytest
against the new test file (tests/filter.py) — e.g. use "python -m pytest
tests/filter.py -v" so the README runs the correct test runner and path.

In `@livekit-agents/livekit/agents/voice/__init__.py`:
- Around line 26-36: The import in the package initializer is pointing to a
non-existent module; change the import of InterruptionFilter in
livekit.agents.voice.__init__.py from "from .interruption_filter import
InterruptionFilter" to import it from the module where it's actually defined
("from .filter import InterruptionFilter"), leaving the __all__ entry for
"InterruptionFilter" unchanged so the symbol exports still work with
AgentSession, VoiceActivityVideoSampler, Agent, ModelSettings, AgentTask,
SpeechHandle and RunContext.

In `@livekit-agents/livekit/agents/voice/agent_session.py`:
- Around line 165-167: The constructor adds new public parameters
interruption_filter_enabled and interruption_ignore_words but the __init__
docstring (in AgentSession.__init__ or the AgentSession class docstring) wasn’t
updated; add entries to the Args section documenting
interruption_filter_enabled: bool (what it toggles and default) and
interruption_ignore_words: list[str] | None (meaning, default and behavior), and
ensure conn_options is also described if not already, so the public API
docstring matches the signature.

In `@livekit-agents/livekit/agents/voice/filter.py`:
- Around line 133-159: The _is_backchanneling method currently splits the
normalized text into tokens and only checks individual words against
self._ignore_words, causing multi-word ignores like "got it" from
DEFAULT_IGNORE_WORDS to be missed; update _is_backchanneling (and any
normalization logic it uses) to first normalize the full input (trim, lower if
not self._case_sensitive, strip punctuation exactly as done now) and
short‑circuit by checking if the full normalized_text exists in
self._ignore_words before splitting into words, then fall back to the existing
per-token check; keep the existing debug logging (logger.debug) paths but ensure
they reflect whether the match was by full-phrase or per-token.
- Around line 3-88: Update InterruptionFilter to satisfy Ruff: remove "from
typing import Set" and use built-in generics; change DEFAULT_IGNORE_WORDS and
the return type of ignore_words to use set[str] instead of Set[str]; in __init__
replace the generator expression used to build self._ignore_words from env_words
with a set comprehension; and remove the trailing whitespace on the blank
docstring/comment line near the top. Ensure references are updated for
DEFAULT_IGNORE_WORDS, __init__, and the ignore_words property.

In `@proof.md`:
- Around line 12-16: Change the indented lines under the "### Filter
Initialization" section so "Filter created successfully" becomes a parent list
item and the following lines are nested bullets; specifically, replace the bare
line "Filter created successfully" with a list marker (e.g., "- Filter created
successfully") and indent the subsequent lines ("Enabled: True", "Ignore words
count: 18", "Sample ignore words: [...]") as nested list items (e.g., two-space
indent and a "-" before each) so the block forms a proper parent list and child
items.

In `@test.py`:
- Around line 1-27: Replace the exec-based file loading in test.py with a normal
import: remove imports of sys and os and the open/exec block, and instead import
the class with from livekit.agents.voice import InterruptionFilter; then
instantiate InterruptionFilter as before. Also fix minor lint issues by not
using redundant file mode flags (remove any unnecessary 'r' mention if present)
and convert f-strings that have no placeholders (e.g., print(f"\n Filter created
successfully")) to plain string literals to address F541 and remove unused
imports (F401).

In `@tests/filter.py`:
- Around line 205-213: Remove the stray footer text that was accidentally
appended after the module bottom; keep only the valid __main__ block that runs
pytest (the if __name__ == "__main__": pytest.main([__file__, "-v"]) block) and
delete the trailing lines "Footer", "© 2026 GitHub, Inc.", "Footer navigation",
"Terms", "Privacy", "Se" so the file parses as valid Python.
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7c3250d and c03d079.

📒 Files selected for processing (9)
  • FILTER_README.md
  • examples/voice_agents/basic_agent.py
  • livekit-agents/livekit/agents/voice/__init__.py
  • livekit-agents/livekit/agents/voice/agent_activity.py
  • livekit-agents/livekit/agents/voice/agent_session.py
  • livekit-agents/livekit/agents/voice/filter.py
  • proof.md
  • test.py
  • tests/filter.py
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Format code with ruff
Run ruff linter and auto-fix issues
Run mypy type checker in strict mode
Maintain line length of 100 characters maximum
Ensure Python 3.9+ compatibility
Use Google-style docstrings

Files:

  • livekit-agents/livekit/agents/voice/agent_session.py
  • livekit-agents/livekit/agents/voice/agent_activity.py
  • test.py
  • tests/filter.py
  • livekit-agents/livekit/agents/voice/__init__.py
  • examples/voice_agents/basic_agent.py
  • livekit-agents/livekit/agents/voice/filter.py
🧬 Code graph analysis (4)
livekit-agents/livekit/agents/voice/agent_activity.py (3)
livekit-agents/livekit/agents/voice/filter.py (3)
  • InterruptionFilter (10-195)
  • ignore_words (85-87)
  • should_ignore_interruption (89-118)
livekit-agents/livekit/agents/voice/audio_recognition.py (1)
  • current_transcript (323-329)
livekit-agents/livekit/agents/tokenize/_basic_word.py (1)
  • split_words (6-70)
test.py (1)
livekit-agents/livekit/agents/voice/filter.py (3)
  • InterruptionFilter (10-195)
  • ignore_words (85-87)
  • should_ignore_interruption (89-118)
tests/filter.py (1)
livekit-agents/livekit/agents/voice/filter.py (5)
  • InterruptionFilter (10-195)
  • ignore_words (85-87)
  • should_ignore_interruption (89-118)
  • add_ignore_word (161-169)
  • remove_ignore_word (171-179)
livekit-agents/livekit/agents/voice/__init__.py (1)
livekit-agents/livekit/agents/voice/filter.py (1)
  • InterruptionFilter (10-195)
🪛 GitHub Check: ruff
test.py

[failure] 23-23: Ruff (F541)
test.py:23:7: F541 f-string without any placeholders


[failure] 7-7: Ruff (UP015)
test.py:7:24: UP015 Unnecessary mode argument


[failure] 1-1: Ruff (F401)
test.py:1:8: F401 sys imported but unused


[failure] 1-2: Ruff (I001)
test.py:1:1: I001 Import block is un-sorted or un-formatted

livekit-agents/livekit/agents/voice/filter.py

[failure] 85-85: Ruff (UP006)
livekit-agents/livekit/agents/voice/filter.py:85:31: UP006 Use set instead of Set for type annotation


[failure] 63-63: Ruff (C401)
livekit-agents/livekit/agents/voice/filter.py:63:38: C401 Unnecessary generator (rewrite as a set comprehension)


[failure] 19-19: Ruff (UP006)
livekit-agents/livekit/agents/voice/filter.py:19:27: UP006 Use set instead of Set for type annotation


[failure] 13-13: Ruff (W293)
livekit-agents/livekit/agents/voice/filter.py:13:1: W293 Blank line contains whitespace


[failure] 5-5: Ruff (UP035)
livekit-agents/livekit/agents/voice/filter.py:5:1: UP035 typing.Set is deprecated, use set instead

🪛 LanguageTool
FILTER_README.md

[grammar] ~111-~111: Ensure spelling is correct
Context: ... Performance The filter adds less than 1ms of latency. It just does simple string ...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🪛 markdownlint-cli2 (0.18.1)
proof.md

14-14: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


15-15: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


16-16: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: type-check (3.9)
  • GitHub Check: type-check (3.13)
🔇 Additional comments (3)
examples/voice_agents/basic_agent.py (1)

84-105: LGTM: example opts into the interruption filter.

Line 105 makes the feature visible in the basic agent example, which is helpful for discoverability.

livekit-agents/livekit/agents/voice/agent_activity.py (2)

138-141: LGTM: Filter wired from session options.

Cleanly initializes the filter with session-configured ignore words and enable flag.


1176-1202: LGTM: Backchannel suppression gate is clear and safe.

The transcript is computed once and the filter short-circuits interruptions without altering existing paths.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment on lines 26 to 36
from .interruption_filter import InterruptionFilter

__all__ = [
"AgentSession",
"VoiceActivityVideoSampler",
"Agent",
"ModelSettings",
"AgentTask",
"AgentTask",\
"SpeechHandle",
"RunContext",
"InterruptionFilter",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check for interruption_filter.py file
echo "=== Checking for interruption_filter.py ==="
fd -a 'interruption_filter.py'

# Check for filter.py files
echo -e "\n=== Checking for filter.py files in voice directory ==="
fd -a 'filter.py' 'livekit-agents/livekit/agents/voice'

# Search for InterruptionFilter class definition
echo -e "\n=== Searching for InterruptionFilter class definition ==="
rg -n "class InterruptionFilter" --type=py

# Check what's in the __init__.py file
echo -e "\n=== Checking __init__.py imports ==="
cat -n "livekit-agents/livekit/agents/voice/__init__.py"

Repository: livekit/agents

Length of output: 2476


Fix import path: InterruptionFilter is defined in filter.py, not interruption_filter.py

Line 26 imports from .interruption_filter, but interruption_filter.py does not exist in the repository. The class is defined in filter.py. This will raise ImportError when the package is imported.

🔧 Fix
-from .interruption_filter import InterruptionFilter
+from .filter import InterruptionFilter
🤖 Prompt for AI Agents
In `@livekit-agents/livekit/agents/voice/__init__.py` around lines 26 - 36, The
import in the package initializer is pointing to a non-existent module; change
the import of InterruptionFilter in livekit.agents.voice.__init__.py from "from
.interruption_filter import InterruptionFilter" to import it from the module
where it's actually defined ("from .filter import InterruptionFilter"), leaving
the __all__ entry for "InterruptionFilter" unchanged so the symbol exports still
work with AgentSession, VoiceActivityVideoSampler, Agent, ModelSettings,
AgentTask, SpeechHandle and RunContext.

Comment on lines +165 to 167
interruption_filter_enabled: bool = True,
interruption_ignore_words: list[str] | None = None,
conn_options: NotGivenOr[SessionConnectOptions] = NOT_GIVEN,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Document the new interruption filter options in the constructor docstring.

The new parameters are public API but aren’t described in the Args section, which makes them easy to miss.

📝 Docstring addition
             ivr_detection (bool): Whether to detect if the agent is interacting with an IVR system.
                 Default ``False``.
+            interruption_filter_enabled (bool): Whether to enable the backchannel
+                interruption filter. Default ``True``.
+            interruption_ignore_words (list[str] | None): Custom ignore words for
+                the interruption filter. ``None`` uses the default set.
             conn_options (SessionConnectOptions, optional): Connection options for
                 stt, llm, and tts.

As per coding guidelines, keep docstrings in sync with public parameters.

🤖 Prompt for AI Agents
In `@livekit-agents/livekit/agents/voice/agent_session.py` around lines 165 - 167,
The constructor adds new public parameters interruption_filter_enabled and
interruption_ignore_words but the __init__ docstring (in AgentSession.__init__
or the AgentSession class docstring) wasn’t updated; add entries to the Args
section documenting interruption_filter_enabled: bool (what it toggles and
default) and interruption_ignore_words: list[str] | None (meaning, default and
behavior), and ensure conn_options is also described if not already, so the
public API docstring matches the signature.

Comment on lines 133 to 159
# Normalize the text
normalized_text = text.strip()
if not self._case_sensitive:
normalized_text = normalized_text.lower()

# Remove common punctuation
normalized_text = normalized_text.replace(".", "").replace(",", "").replace("!", "").replace("?", "")

# Split into words
words = normalized_text.split()

if not words:
return False

# Check if ALL words are in the ignore list
# If even one word is NOT in the ignore list, it's not pure backchanneling
for word in words:
if word not in self._ignore_words:
# Found a word that's not in ignore list - this is a real interruption
logger.debug(
f"Detected real interruption: '{text}' contains non-backchannel word '{word}'"
)
return False

# All words are in the ignore list - this is backchanneling
logger.debug(f"Detected backchanneling: '{text}' - ignoring interruption")
return True
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Backchannel phrases with spaces never match the ignore list.

DEFAULT_IGNORE_WORDS includes multi-word phrases like "got it", but _is_backchanneling() splits on whitespace and checks tokens individually. As a result, "got it" will be treated as a real interruption even though it’s in the ignore list. Consider short‑circuiting on full-phrase matches before token checks.

✅ Proposed fix
-        # Remove common punctuation
-        normalized_text = normalized_text.replace(".", "").replace(",", "").replace("!", "").replace("?", "")
+        # Remove common punctuation
+        normalized_text = normalized_text.replace(".", "").replace(",", "").replace("!", "").replace("?", "")
+
+        # Full-phrase match (e.g., "got it")
+        if normalized_text in self._ignore_words:
+            return True
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# Normalize the text
normalized_text = text.strip()
if not self._case_sensitive:
normalized_text = normalized_text.lower()
# Remove common punctuation
normalized_text = normalized_text.replace(".", "").replace(",", "").replace("!", "").replace("?", "")
# Split into words
words = normalized_text.split()
if not words:
return False
# Check if ALL words are in the ignore list
# If even one word is NOT in the ignore list, it's not pure backchanneling
for word in words:
if word not in self._ignore_words:
# Found a word that's not in ignore list - this is a real interruption
logger.debug(
f"Detected real interruption: '{text}' contains non-backchannel word '{word}'"
)
return False
# All words are in the ignore list - this is backchanneling
logger.debug(f"Detected backchanneling: '{text}' - ignoring interruption")
return True
# Normalize the text
normalized_text = text.strip()
if not self._case_sensitive:
normalized_text = normalized_text.lower()
# Remove common punctuation
normalized_text = normalized_text.replace(".", "").replace(",", "").replace("!", "").replace("?", "")
# Full-phrase match (e.g., "got it")
if normalized_text in self._ignore_words:
return True
# Split into words
words = normalized_text.split()
if not words:
return False
# Check if ALL words are in the ignore list
# If even one word is NOT in the ignore list, it's not pure backchanneling
for word in words:
if word not in self._ignore_words:
# Found a word that's not in ignore list - this is a real interruption
logger.debug(
f"Detected real interruption: '{text}' contains non-backchannel word '{word}'"
)
return False
# All words are in the ignore list - this is backchanneling
logger.debug(f"Detected backchanneling: '{text}' - ignoring interruption")
return True
🤖 Prompt for AI Agents
In `@livekit-agents/livekit/agents/voice/filter.py` around lines 133 - 159, The
_is_backchanneling method currently splits the normalized text into tokens and
only checks individual words against self._ignore_words, causing multi-word
ignores like "got it" from DEFAULT_IGNORE_WORDS to be missed; update
_is_backchanneling (and any normalization logic it uses) to first normalize the
full input (trim, lower if not self._case_sensitive, strip punctuation exactly
as done now) and short‑circuit by checking if the full normalized_text exists in
self._ignore_words before splitting into words, then fall back to the existing
per-token check; keep the existing debug logging (logger.debug) paths but ensure
they reflect whether the match was by full-phrase or per-token.

Comment on lines +12 to +16
### Filter Initialization
✓ Filter created successfully
- Enabled: True
- Ignore words count: 18
- Sample ignore words: ['yeah', 'ok', 'hmm', 'right', 'uh-huh']
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix unordered list indentation (markdownlint MD007).

Lines 14–16 are indented list items without a parent list item. This triggers MD007. Consider turning “Filter created successfully” into a parent list item and nesting the details.

✍️ Proposed fix
-✓ Filter created successfully
-  - Enabled: True
-  - Ignore words count: 18
-  - Sample ignore words: ['yeah', 'ok', 'hmm', 'right', 'uh-huh']
+- ✓ Filter created successfully
+  - Enabled: True
+  - Ignore words count: 18
+  - Sample ignore words: ['yeah', 'ok', 'hmm', 'right', 'uh-huh']
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
### Filter Initialization
✓ Filter created successfully
- Enabled: True
- Ignore words count: 18
- Sample ignore words: ['yeah', 'ok', 'hmm', 'right', 'uh-huh']
### Filter Initialization
- ✓ Filter created successfully
- Enabled: True
- Ignore words count: 18
- Sample ignore words: ['yeah', 'ok', 'hmm', 'right', 'uh-huh']
🧰 Tools
🪛 markdownlint-cli2 (0.18.1)

14-14: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


15-15: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


16-16: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)

🤖 Prompt for AI Agents
In `@proof.md` around lines 12 - 16, Change the indented lines under the "###
Filter Initialization" section so "Filter created successfully" becomes a parent
list item and the following lines are nested bullets; specifically, replace the
bare line "Filter created successfully" with a list marker (e.g., "- Filter
created successfully") and indent the subsequent lines ("Enabled: True", "Ignore
words count: 18", "Sample ignore words: [...]") as nested list items (e.g.,
two-space indent and a "-" before each) so the block forms a proper parent list
and child items.

Refactor tests for InterruptionFilter class to use pytest framework and remove direct execution of interruption_filter.py.
Refactor InterruptionFilter to use set instead of Set for ignore words and improve logging.
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@test.py`:
- Line 5: Update the import in the package initializer to import the correct
module: replace the import of InterruptionFilter from .interruption_filter with
an import from .filter so that InterruptionFilter is imported from the actual
module name (change the statement referencing .interruption_filter to reference
.filter in the __init__.py that exposes InterruptionFilter).
♻️ Duplicate comments (1)
test.py (1)

1-1: Remove unused import os.

The os module is imported but never used in this file.

🔧 Suggested fix
-import os
-
 import pytest
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c03d079 and da062db.

📒 Files selected for processing (1)
  • test.py
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Format code with ruff
Run ruff linter and auto-fix issues
Run mypy type checker in strict mode
Maintain line length of 100 characters maximum
Ensure Python 3.9+ compatibility
Use Google-style docstrings

Files:

  • test.py
🧬 Code graph analysis (1)
test.py (1)
livekit-agents/livekit/agents/voice/filter.py (5)
  • InterruptionFilter (10-195)
  • ignore_words (85-87)
  • should_ignore_interruption (89-118)
  • add_ignore_word (161-169)
  • remove_ignore_word (171-179)
🪛 GitHub Actions: tests
test.py

[error] 1-1: ImportError during test collection: failed to import module due to missing dependency (livekit.agents.voice.interruption_filter).

🪛 GitHub Check: ruff
test.py

[failure] 1-1: Ruff (F401)
test.py:1:8: F401 os imported but unused

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: type-check (3.13)
  • GitHub Check: type-check (3.9)
🔇 Additional comments (1)
test.py (1)

8-99: LGTM! Comprehensive test coverage for InterruptionFilter.

The test suite thoroughly covers:

  • Default and custom initialization
  • Enabled/disabled states
  • Backchanneling detection (speaking vs. silent)
  • Real interruptions and mixed input
  • Case sensitivity modes
  • Edge cases (empty input, punctuation)
  • Dynamic modification via add_ignore_word/remove_ignore_word

All assertions correctly align with the InterruptionFilter implementation.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.


import pytest

from livekit.agents.voice import InterruptionFilter
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for any references to the incorrect module path
rg -n "interruption_filter" --type py -g '!test.py'

Repository: livekit/agents

Length of output: 1046


🏁 Script executed:

#!/bin/bash
# Also check the actual module structure in livekit.agents.voice
find . -path "*/livekit/agents/voice*" -type f -name "*.py" | head -20

Repository: livekit/agents

Length of output: 1160


🏁 Script executed:

#!/bin/bash
# Check what's actually exported from livekit.agents.voice
rg -n "InterruptionFilter" --type py | head -20

Repository: livekit/agents

Length of output: 1269


🏁 Script executed:

#!/bin/bash
# Search for the actual interruption_filter.py file
find . -name "interruption_filter.py" -type f

Repository: livekit/agents

Length of output: 40


🏁 Script executed:

#!/bin/bash
# Also check the full directory listing of livekit/agents/voice
ls -la livekit-agents/livekit/agents/voice/ | grep -E "\.py$|^d"

Repository: livekit/agents

Length of output: 1387


🏁 Script executed:

#!/bin/bash
# Check the __init__.py imports
cat -n livekit-agents/livekit/agents/voice/__init__.py

Repository: livekit/agents

Length of output: 2138


Fix the import statement in __init__.py.

The import in test.py is correct, but the pipeline error originates from livekit-agents/livekit/agents/voice/__init__.py line 26. It imports from .interruption_filter, but the module file is named filter.py. Change line 26 from from .interruption_filter import InterruptionFilter to from .filter import InterruptionFilter.

🤖 Prompt for AI Agents
In `@test.py` at line 5, Update the import in the package initializer to import
the correct module: replace the import of InterruptionFilter from
.interruption_filter with an import from .filter so that InterruptionFilter is
imported from the actual module name (change the statement referencing
.interruption_filter to reference .filter in the __init__.py that exposes
InterruptionFilter).

Refactor InterruptionFilter class and its tests to improve structure and logging.
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@tests/filter.py`:
- Around line 99-123: The per-word check splits normalized_text and therefore
never matches multi-word ignore phrases like "got it"; to fix, after creating
normalized_text (after lowercasing and punctuation removal) but before
splitting, check if normalized_text is in self._ignore_words (or if any phrase
in self._ignore_words equals normalized_text) and return True/skip accordingly;
keep the existing per-word loop as fallback for single-word ignores. Use the
existing symbols normalized_text and self._ignore_words to implement this
phrase-first check so multi-word backchannel phrases are correctly recognized.
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c1efebb and 88d0bfa.

📒 Files selected for processing (1)
  • tests/filter.py
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Format code with ruff
Run ruff linter and auto-fix issues
Run mypy type checker in strict mode
Maintain line length of 100 characters maximum
Ensure Python 3.9+ compatibility
Use Google-style docstrings

Files:

  • tests/filter.py
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: type-check (3.9)
  • GitHub Check: type-check (3.13)
🔇 Additional comments (1)
tests/filter.py (1)

82-93: Clear early-exit guards.
Line 87–93: the enabled/agent_speaking checks keep the filter predictable and side‑effect free.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment on lines +99 to +123
normalized_text = text.strip()
if not self._case_sensitive:
normalized_text = normalized_text.lower()

normalized_text = (
normalized_text.replace(".", "")
.replace(",", "")
.replace("!", "")
.replace("?", "")
)

words = normalized_text.split()
if not words:
return False

for word in words:
if word not in self._ignore_words:
logger.debug(
"Detected real interruption: '%s' contains non-backchannel word '%s'",
text,
word,
)
return False

logger.debug("Detected backchanneling: '%s' - ignoring interruption", text)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Multi‑word ignore phrases never match.
Line 110–121: entries like "got it" are split into "got"/"it" and will always fail the per‑word check, so common backchannel phrases won’t be ignored.

💡 Suggested fix (phrase check before per‑word loop)
         words = normalized_text.split()
         if not words:
             return False
+
+        normalized_phrase = " ".join(words)
+        if normalized_phrase in self._ignore_words:
+            logger.debug("Detected backchanneling: '%s' - ignoring interruption", text)
+            return True
 
         for word in words:
             if word not in self._ignore_words:
                 logger.debug(
                     "Detected real interruption: '%s' contains non-backchannel word '%s'",
                     text,
                     word,
                 )
                 return False
🤖 Prompt for AI Agents
In `@tests/filter.py` around lines 99 - 123, The per-word check splits
normalized_text and therefore never matches multi-word ignore phrases like "got
it"; to fix, after creating normalized_text (after lowercasing and punctuation
removal) but before splitting, check if normalized_text is in self._ignore_words
(or if any phrase in self._ignore_words equals normalized_text) and return
True/skip accordingly; keep the existing per-word loop as fallback for
single-word ignores. Use the existing symbols normalized_text and
self._ignore_words to implement this phrase-first check so multi-word
backchannel phrases are correctly recognized.

@davidzhao davidzhao closed this Jan 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants