boundless-author-7811
05/08/2026, 10:10 AMtall-airline-16756
05/08/2026, 10:10 AMtall-airline-16756
05/08/2026, 10:10 AMfrom livekit.agents.beta.tools import EndCallTool
from livekit.agents import Agent
class SupportAgent(Agent):
def __init__(self):
end_call_tool = EndCallTool(
extra_description="Only end the call after confirming the customer's issue is resolved.",
delete_room=True,
end_instructions="Thank the customer and close the session."
)
super().__init__(
instructions="You are a technical support agent.",
tools=end_call_tool.tools
)
• Automatic termination after silence/inactivity:
◦ Implement a background monitor that tracks last user speech and disconnects the room when silence exceeds a threshold.
◦ Example pattern:
SILENCE_THRESHOLD = 5 # seconds
async def entrypoint(ctx: JobContext):
user_last_spoke_time = time.time()
async def monitor_interaction():
while True:
if time.time() - user_last_spoke_time > SILENCE_THRESHOLD:
await ctx.room.disconnect()
await asyncio.sleep(1)
@agent.on("user_started_speaking")
def on_user_started_speaking(_msg):
nonlocal user_last_spoke_time
user_last_spoke_time = time.time()
asyncio.create_task(monitor_interaction())
Notes about Gemini 3.1 compatibility
• Gemini 3.1 has known compatibility limits with some LiveKit agent LLM-specific features; however, EndCallTool operates at the agent/tool level and should function regardless of the underlying LLM model.
If you tell me which option you prefer (manual, automatic silence-based, or agent-triggered on resolution), I’ll give the exact code and integration steps for your setup.
Sources: EndCallTool | LiveKit Documentation | Agent speech and audio | LiveKit Documentation | How to detect and handle silence during calls, with interactive elementsboundless-author-7811
05/11/2026, 5:31 PMtall-airline-16756
05/11/2026, 5:31 PMboundless-author-7811
05/11/2026, 5:33 PMtall-airline-16756
05/11/2026, 5:33 PMfrom livekit.agents import Agent, function_tool
from livekit.agents.beta.toolsets import ToolProxyToolset, Toolset
@function_tool()
async def get_weather(ctx, location: str) -> str:
return f"Sunny, 72F in {location}"
class WeatherToolset(Toolset):
def __init__(self):
super().__init__(id="weather")
self._tools = [get_weather]
class TravelAgent(Agent):
def __init__(self):
super().__init__(
instructions="You are a travel assistant. Use tool_search to find tools.",
tools=[
ToolProxyToolset(
id="travel_tools",
tools=[WeatherToolset()], # register toolsets but don't dump all schemas each turn
max_results=3,
)
],
)
• Benefit: The model uses `tool_search`/`call_tool` to discover and invoke only relevant tools, reducing tokens compared to sending all 15 schemas every turn.
B. Dynamic creation / update of tools at runtime
from livekit.agents import Agent, function_tool
def create_db_tool(table: str, op: str):
schema = {
"name": f"{op}_{table}",
"description": f"{op} on {table}",
"parameters": { "type": "object", "properties": { "id": {"type": "string"} }, "required": ["id"] }
}
async def handler(raw_args, ctx):
return f"Performed {op} on {table} id={raw_args['id']}"
return function_tool(handler, raw_schema=schema)
class DBAgent(Agent):
async def add_user_tools(self, tables_ops):
tools = [create_db_tool(t, o) for t,o in tables_ops]
await self.update_tools(tools)
• Benefit: Only the necessary tools are added to the agent when needed, so you avoid sending unused tool schemas.
C. Simple caching example (pre-synthesize and reuse TTS audio)
HOLD_FRAMES = []
async def preload_hold_message(tts):
global HOLD_FRAMES
async for event in tts.synthesize("Let me check that for you."):
HOLD_FRAMES.append(event.frame)
@function_tool()
async def long_lookup(ctx, order_id: str) -> str:
# play cached audio while doing work
async def cached_audio():
for f in HOLD_FRAMES:
yield f
hold_handle = ctx.session.say("Please wait...", audio=cached_audio(), add_to_chat_ctx=False)
result = await fetch_order(order_id)
if not hold_handle.done(): hold_handle.interrupt()
return result
• Benefit: Avoid repeated TTS token/latency for fixed phrases.
Practical tips
• Prefer ToolProxyToolset or ToolSearchToolset when you have many tools.
• Keep tool schemas compact (short descriptions, minimal parameter metadata).
• Measure token usage and latency after implementing; iterate (cache heavy schemas or use runtime loading if needed).
If you want, paste one or two tool schemas and I’ll show a concrete before/after token estimate and a tailored ToolProxy setup.
Sources: Toolsets | LiveKit Documentation | Function tool definition | LiveKit Documentation | Audio customization | LiveKit Documentation | The ReAct Pattern for Voice Agents and How AI Agents Think, Act, and Respon..., with interactive elementsboundless-author-7811
05/11/2026, 5:37 PMtall-airline-16756
05/11/2026, 5:38 PMboundless-author-7811
05/11/2026, 5:38 PMtall-airline-16756
05/11/2026, 5:39 PMtotal_tokens calculation. Here's a concise breakdown.
What counts toward total_tokens
• Input tokens:
◦ System/user/assistant prompt text per turn.
◦ STT transcripts (transcribed user speech sent as text).
◦ Tool schemas/function definitions sent in context.
◦ Tool invocation arguments passed to the LLM.
◦ Inputs sent to provider tools (e.g., web search) when those are routed through the LLM.
◦ Cached input tokens are tracked separately as cached_tokens but factor into metrics and efficiency.
• Output tokens:
◦ LLM-generated responses (text or structured outputs).
◦ Text sent to TTS (unless using a realtime model where audio/text may be bundled).
• Tool-related tokens:
◦ Tool results/outputs returned to the LLM (JSON/text) count as tokens.
◦ Provider tool input/output (e.g., CodeInterpreter, WebSearch) are included.
Notes
• For realtime models, metrics break down into input_tokens (including audio_tokens for STT, text_tokens, cached_tokens) and `output_tokens`; total_tokens is the sum of those.
• Large tool schemas or many tools increase input tokens even when not invoked.
• LiveKit exposes per-turn token metrics (RealtimeModelMetrics / per-message metrics) so you can see input/output/cached token breakdowns for optimization.
If you want, share a sample turn (prompt + a tool schema + a tool response) and I’ll show a concrete token breakdown.
Sources: Mistral AI LLM plugin guide | LiveKit Documentation | https://livekit.com/pricing.md| | Agent Console | LiveKit Documentation, with interactive elements