Skip to content

fix(voice): treat a non-list record_keyterms argument as no change - #7468

Merged
longcw merged 2 commits into
livekit:mainfrom
Lesereingrape:fix/keyterm-non-list-arguments
Sep 30, 2026
Merged

longcw merged 2 commits into
livekit:mainfrom
Lesereingrape:fix/keyterm-non-list-arguments

Conversation

@Lesereingrape

Copy link
Copy Markdown

What

_parse_tool_call assumed each of pending / confirm / remove decodes to a list. A value
that is not a list is now ignored, the way blank and non-string items already were.

Why

The arguments come straight from a provider, through parse_function_arguments, which
deliberately recovers malformed JSON rather than rejecting it — and data.get(key, []) then
iterates whatever the model actually sent:

  • {"pending": null} is valid JSON, so it reaches _terms as None and raises
    TypeError: 'NoneType' object is not iterable. KeytermDetector._run_once is decorated with
    @utils.log_exceptions, which logs and re-raises, so the pass dies and that turn's keyterm
    update is lost.
  • A truncated call such as {"confirm": "Kubernetes is repaired by json_repair into the string
    "Kubernetes", and iterating a string yields its characters: _apply_pass confirms K, u,
    b, …, and _run_once pushes that set to _stt._update_session_keyterms()
    (keyterm_detection.py:386-387). The detector's own instructions treat a wrong spelling as
    unrecoverable for the rest of the call, so ignoring a group the recognizer cannot use fits
    "precision beats coverage" better than biasing on single letters.

Both reproduce on main at 98ed3e8:

null  -> raised TypeError 'NoneType' object is not iterable
trunc -> ([], ['K', 'u', 'b', 'e', 'r', 'n', 'e', 't', 'e', 's'], [])

Tests

tests/test_stt_context.py, which already covers _parse_tool_call:

  • before the fix: 1 failed, 36 passed in 5.47s (the TypeError above)
  • after: 37 passed in 0.60s
  • keyterm-related unit modules (test_keyterm_detection_span, test_stt_context,
    test_agent_session, test_meta_stt, test_session_options_report, test_trace_types_pii,
    --unit -n auto): 1 failed, 155 passed, 1 error. The failure is test_trace_types_pii reading
    a source file with the Windows default codec, and the error is test_meta_stt importing a plugin
    that is not installed here; both reproduce on the untouched base and neither touches a file in
    this diff.
  • ruff check / ruff format --check: clean on both files. mypy in strict mode reports no error
    in keyterm_detection.py.

Environment note: Windows, core package installed editable. uv sync --all-extras cannot resolve
bithuman here (no Windows wheel), so plugin packages are absent and I could not run the full
--unit --audio_eot gate locally.

`_parse_tool_call` assumed `pending`/`confirm`/`remove` decode to a list.
A model that answers with `null` raised TypeError out of the detection pass,
and a bare string — what the json_repair fallback returns for a truncated
call — became one keyterm per character.
@Lesereingrape
Lesereingrape requested a review from a team as a code owner September 25, 2026 09:38
@CLAassistant

CLAassistant commented Sep 25, 2026 •

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no bugs or issues to report.

Devin Review

@longcw longcw left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

lgtm!

@longcw
longcw merged commit 560c8db into livekit:main Sep 30, 2026
17 checks passed
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