Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion livekit-agents/livekit/agents/voice/keyterm_detection.py
Original file line number Diff line number Diff line change
Expand Up @@ -506,7 +506,12 @@ def _parse_tool_call(
return [], [], []

def _terms(key: str) -> list[str]:
return [t for t in data.get(key, []) if isinstance(t, str) and t.strip()]
value = data.get(key)
if not isinstance(value, list):
# null or a bare string (also what a repaired truncated call decodes to):
# iterating either would raise, or yield one keyterm per character
return []
return [t for t in value if isinstance(t, str) and t.strip()]

return _terms("pending"), _terms("confirm"), _terms("remove")

Expand Down
19 changes: 19 additions & 0 deletions tests/test_stt_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -543,6 +543,25 @@ def test_parse_tool_call_missing() -> None:
assert _parse_tool_call([bad]) == ([], [], [])


def test_parse_tool_call_non_list_term_group() -> None:
# a model can answer with null instead of an empty list; that must not raise out of
# the detection pass and drop the whole turn's keyterm update
nulls = FunctionToolCall(
call_id="1",
name="record_keyterms",
arguments='{"pending": null, "confirm": [], "remove": []}',
)
assert _parse_tool_call([nulls]) == ([], [], [])

# nor may a bare string become one keyterm per character
string = FunctionToolCall(
call_id="2",
name="record_keyterms",
arguments='{"pending": [], "confirm": "Kubernetes", "remove": []}',
)
assert _parse_tool_call([string]) == ([], [], [])


def test_format_input_splits_applied_and_candidate() -> None:
text = _format_input(_ctx("hi"), [("Term1", True), ("Term2", False)])
assert text is not None
Expand Down
Loading