Skip to content
Open
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
11 changes: 10 additions & 1 deletion livekit-agents/livekit/agents/llm/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -606,7 +606,16 @@ def _shallow_model_dump(model: BaseModel, *, by_alias: bool = False) -> dict[str
return result


def strip_thinking_tokens(content: str | None, thinking: asyncio.Event) -> str | None:
def strip_thinking_tokens(
content: str | list | None, thinking: asyncio.Event
) -> str | None:
if isinstance(content, list):
# OpenAI-compatible providers (e.g. Mistral) may stream delta.content as a
# list of typed content parts instead of a plain string.
content = (
"".join(part.get("text", "") for part in content if isinstance(part, dict))
or None
)
Comment on lines +612 to +618

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.

🚩 Joining list parts may increase likelihood of thinking token leakage

The thinking token state machine in strip_thinking_tokens processes only one tag transition per call — it finds either <think> or </think> but not both. When content was always a plain string from streaming, each chunk was typically small enough that both tags rarely appeared in the same chunk. With the new list joining (livekit-agents/livekit/agents/llm/utils.py:616), multiple content parts are concatenated into a single string, making it more likely that both <think> and </think> appear in one call. In that case, only the first tag is processed: if <think>reasoning</think>actual arrives as a single joined string, the function sets the thinking flag and returns "reasoning</think>actual", leaking the model's internal reasoning to the user. This is a pre-existing design limitation, but the list-joining makes it a more plausible scenario.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

if content is None:
return None

Expand Down
Loading