Skip to content

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

Open
Lesereingrape wants to merge 2 commits into
livekit:mainfrom
Lesereingrape:fix/keyterm-non-list-arguments
Open

Lesereingrape wants to merge 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

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@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

This branch has not been deployed

No deployments
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.

2 participants