-
Notifications
You must be signed in to change notification settings - Fork 2.8k
New branch #4537
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
New branch #4537
Conversation
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
📝 WalkthroughWalkthroughAdds 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
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
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
Comment |
There was a problem hiding this 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
📒 Files selected for processing (9)
FILTER_README.mdexamples/voice_agents/basic_agent.pylivekit-agents/livekit/agents/voice/__init__.pylivekit-agents/livekit/agents/voice/agent_activity.pylivekit-agents/livekit/agents/voice/agent_session.pylivekit-agents/livekit/agents/voice/filter.pyproof.mdtest.pytests/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.pylivekit-agents/livekit/agents/voice/agent_activity.pytest.pytests/filter.pylivekit-agents/livekit/agents/voice/__init__.pyexamples/voice_agents/basic_agent.pylivekit-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.
| from .interruption_filter import InterruptionFilter | ||
|
|
||
| __all__ = [ | ||
| "AgentSession", | ||
| "VoiceActivityVideoSampler", | ||
| "Agent", | ||
| "ModelSettings", | ||
| "AgentTask", | ||
| "AgentTask",\ | ||
| "SpeechHandle", | ||
| "RunContext", | ||
| "InterruptionFilter", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 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.
| interruption_filter_enabled: bool = True, | ||
| interruption_ignore_words: list[str] | None = None, | ||
| conn_options: NotGivenOr[SessionConnectOptions] = NOT_GIVEN, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| # 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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| # 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.
| ### Filter Initialization | ||
| ✓ Filter created successfully | ||
| - Enabled: True | ||
| - Ignore words count: 18 | ||
| - Sample ignore words: ['yeah', 'ok', 'hmm', 'right', 'uh-huh'] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| ### 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.
There was a problem hiding this 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 importos.The
osmodule 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
📒 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_wordAll assertions correctly align with the
InterruptionFilterimplementation.
✏️ 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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 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 -20Repository: 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 -20Repository: livekit/agents
Length of output: 1269
🏁 Script executed:
#!/bin/bash
# Search for the actual interruption_filter.py file
find . -name "interruption_filter.py" -type fRepository: 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__.pyRepository: 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.
There was a problem hiding this 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
📒 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.
| 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) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
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
Documentation
Tests
✏️ Tip: You can customize this high-level summary in your review settings.