future-ocean-83972
02/27/2026, 9:16 AMtall-airline-16756
02/27/2026, 9:16 AMtall-airline-16756
02/27/2026, 9:16 AMrole:, remove top-level JSON blocks, or use a regex to remove { ... } tool payloads.
1. Make the agent produce a clear user-facing message
◦ Configure the agent or prompt so it returns a final user-facing reply that excludes tool-call details.
◦ Ask the LLM to summarize tool responses into plain language (e.g., “Found disturbance info: [summary]”) and only send that summary to TTS.
1. Use structured channels / message types (if available)
◦ If your pipeline or SDK supports message metadata or separate channels (e.g., tool, assistant, system), route tool outputs to a non-TTS channel and only route assistant.user_message content to TTS.
1. Detect and strip internal tokens or chain-of-thought
◦ Some models output chain-of-thought or special markers. Detect those markers and remove those segments before TTS.
1. Sanity-check tool outputs before synthesis
◦ If a tool returns JSON, convert it to a human-friendly sentence first (e.g., “I checked disturbance info — no problems found.”) and send that to TTS.
◦ Never send raw serialized objects to your TTS engine.
Questions to help me give exact code or config
• Which LiveKit SDK / language are you using (JS/TS, Python, other)?
• Where in your pipeline do you call TTS (client-side, server-side)?
• Do you receive separate message roles (e.g., tool, assistant) from your LLM/agent or is everything combined in a single string?
• Do you want a quick code example for filtering/parsing and sending only the cleaned text to TTS?
If you tell me your SDK/language and whether TTS is run server- or client-side, I’ll provide a short example to filter out the tool-call JSON and ensure only user-facing text is spoken.future-ocean-83972
02/27/2026, 9:21 AMtall-airline-16756
02/27/2026, 9:21 AMllm_node in your Agent and filter streaming chunks that contain tool-call info.
Example pattern (conceptual)
1. Override llm_node in your Agent subclass.
2. Stream the default LLM output.
3. If a chunk contains tool-call metadata (for example a tool_calls field, role labels, or a JSON payload), do not yield it to the TTS stage.
4. Optionally, convert tool results into a short natural-language summary and yield that instead.
Minimal illustrative code (adapt to your environment)
from livekit.agents import Agent, ChatContext, FunctionTool, ModelSettings
from typing import AsyncIterable
class Assistant(Agent):
async def llm_node(
self,
chat_ctx: ChatContext,
tools: list[FunctionTool],
model_settings: ModelSettings,
) -> AsyncIterable:
# Stream the default LLM output
async for chunk in Agent.default.llm_node(self, chat_ctx, tools, model_settings):
# Detect tool calls or serialized JSON in the chunk and skip them
if hasattr(chunk, "tool_calls") and chunk.tool_calls:
continue # do not send tool-call chunks to TTS
# Optionally: detect large JSON blocks or role lines and skip/clean them
text = getattr(chunk, "delta", None) and getattr(chunk.delta, "content", None)
if text:
# basic JSON / role-line guard (tune regex to your needs)
if text.strip().startswith("{") or text.strip().startswith("role:"):
continue
# If clean, yield chunk to downstream (TTS) node
yield chunk
Additional recommendations
• Instead of simply skipping, you can generate a compact summary of tool outputs (e.g., “Checked disturbance info — no issues found.”) and send that to TTS so users get meaningful feedback.
• If your agent or SDK supports separate channels/metadata for tool outputs, ensure you route those away from TTS and only route assistant/user-facing messages to the speech pipeline.
• Add logging for skipped chunks so you can verify filtering is working and troubleshoot edge cases.
If you’d like, I can:
• Provide a more complete code example tailored to your exact LiveKit SDK version and TTS library.
• Show a small routine to convert tool JSON into a human-readable summary before sending it to TTS.
• Suggest a regex or parser tuned to the specific JSON/tool-call format you’re seeing.
Which would you prefer? If you paste a short real example of the LLM chunk structure (the raw message object you receive before TTS), I’ll produce an exact filter/snippet you can drop into your agent.
Sources: Tool definition and use | LiveKit Documentation | Pipeline nodes and hooks | LiveKit Documentation