can i get any event from livekit directly tht play...
# ask-ai
e
can i get any event from livekit directly tht playback finished You still have cases where the final goodbye’s playback-finished signal is missed (you saw ⏳ TTS playback wait timed out after 20.0s). That’s not a crash, but it means we rely on the log-based playback-finished signal which can be flaky. The system still ends safely due to the timeout, but it can make endings slower. If you want that fully bulletproof, the next step would be to also set tts_playback_finished_event from another reliable signal you already log (e.g. the “avatar audio track unpublished - agent finished” event), so we don’t have to wait for the log hook string. Tell me if you want that additional improvement and I’ll implement it.
t
Thanks for reaching out, Guess. I'm an AI assistant and I'm here to help. Give me a moment while I research this topic.
Thanks — I’m responding about detecting when playback finishes. Yes — LiveKit emits a direct
playback_finished
event from the audio output you can use instead of relying on log hooks. It’s fired when TTS/audio playback completes and is more reliable than the log-based signal. You can handle it like this:
Copy code
from <http://livekit.agents.voice.io|livekit.agents.voice.io> import PlaybackFinishedEvent

@session.output.audio.on("playback_finished")
def _on_playback_finished(ev: PlaybackFinishedEvent):
    <http://logger.info|logger.info>(f"playback finished: {ev.interrupted}")
    # trigger your tts_playback_finished_event here
For extra robustness you can also set
tts_playback_finished_event
when you receive a track-level signal (e.g.,
track_unpublished
for the agent audio) or an RPC from an avatar synthesis server; combining signals prevents timeout-based delays. Would you like me to implement adding the
playback_finished
handler (and optionally the secondary
track_unpublished
hook) in your codebase? If yes, tell me which option you prefer. Sources: How to detect when an agent has finished speaking | Bringing AI avatars to voice agents
e
is this event sent after completing or at tsarting the audio
t
The
playback_finished
event is emitted when playback ends — after the audio has completed (or was interrupted), not at the start.
e
does livekit support tolol calling
openai tool calling
t
Responding to both parts you asked about: • The
playback_finished
event is emitted after audio playback ends (or is interrupted), not at the start. • LiveKit Agents does support OpenAI tool calling (function calling). You can define tools/functions and have the LLM invoke them; support exists in both Python and Node.js (e.g.,
@function_tool
in Python,
llm.tool
in Node), and it works with Realtime models. You can also control tool usage with parameters like
tool_choice
(
auto
,
required
,
none
). Would you like a short example for Python or Node.js showing how to define a tool and handle its invocation? Sources: Agents (Python) | livekit/livekit_composite | DeepWiki | Tool definition and use | LiveKit docs | OpenAI and LiveKit partner to turn Advanced Voice into an API | OpenAI and LiveKit partner to turn Advanced Voice into an API | OpenAI LLM plugin guide | LiveKit Docs
e
yes
t
Below are concise examples showing how to define and use OpenAI tool (function) calling in LiveKit Agents for Python and Node.js. Python (using
@function_tool
)
Copy code
from livekit.agents import Agent, function_tool

agent = Agent(...)

@function_tool(name="lookup_order", description="Find order status by ID")
def lookup_order(order_id: str) -> dict:
    # your implementation: query DB or API
    return {"order_id": order_id, "status": "shipped"}

# Agent will map incoming model tool calls to this function automatically.
# When running with a RealtimeModel, the model can call "lookup_order" and
# the framework will invoke the function and return results to the model.
Node.js (using
llm.tool
)
Copy code
const { Agent } = require("livekit-agents");

const agent = new Agent(...);

agent.llm.tool("lookup_order", {
  description: "Find order status by ID",
  params: {
    type: "object",
    properties: { order_id: { type: "string" } },
    required: ["order_id"],
  },
}, async (args) => {
  // args.order_id -> query DB or API
  return { order_id: args.order_id, status: "shipped" };
});

// When the model emits a tool call for "lookup_order", the handler above runs
// and the tool output is provided back to the model/agent.
If you want, I can: • Provide a full Realtime session example wiring a
RealtimeModel
to these tools. • Show how to control
tool_choice
(
auto
,
required
,
none
) or handle complex parameter schemas. Which would you prefer?
e
yes
t
Below are complete Realtime session examples showing OpenAI tool (function) calling in LiveKit Agents for both Python and Node.js. Each example demonstrates: defining a tool, wiring a Realtime model, and handling tool invocations. Python — Realtime session with
@function_tool
Copy code
# example_python_realtime_tools.py
from livekit.agents import Agent, RealtimeModel, function_tool, SessionConfig

# 1) Define agent and tools
agent = Agent(name="orders-agent")

@function_tool(name="lookup_order", description="Find order status by ID")
def lookup_order(order_id: str) -> dict:
    # replace with your DB/API call
    return {"order_id": order_id, "status": "shipped", "eta": "2025-12-20"}

@function_tool(
    name="cancel_order",
    description="Cancel an order by ID",
)
def cancel_order(order_id: str, reason: str = "") -> dict:
    # replace with cancellation logic
    return {"order_id": order_id, "canceled": True, "reason": reason}

# 2) Configure RealtimeModel (OpenAI) — choose model and tool usage policy
realtime_model = RealtimeModel(
    model="gpt-4o-realtime-preview",  # example realtime model
    tool_choice="auto",               # allow model to call tools when useful
)

# 3) Start a realtime session (SessionConfig holds session params like token, etc.)
config = SessionConfig(session_name="orders-realtime")
session = agent.start_realtime_session(model=realtime_model, config=config)

# 4) Handle session lifecycle and messages (optional hooks)
@session.on("session_started")
def on_started(ev):
    print("Realtime session started")

@session.on("tool_called")
def on_tool_called(ev):
    # optional: inspect tool calls as they happen
    print("Tool called:", ev.tool_name, ev.args)

# Keep running while session operates (depends on your app loop)
# session.run_forever()  # pseudo-call — integrate with your app loop/async runtime
Node.js — Realtime session with
llm.tool
Copy code
// example_node_realtime_tools.js
const { Agent, RealtimeModel } = require("livekit-agents");

// 1) Create agent
const agent = new Agent({ name: "orders-agent-node" });

// 2) Define tools exposed to the LLM
agent.llm.tool(
  "lookup_order",
  {
    description: "Find order status by ID",
    params: {
      type: "object",
      properties: {
        order_id: { type: "string" }
      },
      required: ["order_id"]
    }
  },
  async (args) => {
    // replace with DB/API call
    return { order_id: args.order_id, status: "shipped", eta: "2025-12-20" };
  }
);

agent.llm.tool(
  "cancel_order",
  {
    description: "Cancel an order by ID",
    params: {
      type: "object",
      properties: {
        order_id: { type: "string" },
        reason: { type: "string" }
      },
      required: ["order_id"]
    }
  },
  async (args) => {
    // replace with cancellation logic
    return { order_id: args.order_id, canceled: true, reason: args.reason || "" };
  }
);

// 3) Configure RealtimeModel and start session
const realtimeModel = new RealtimeModel({
  model: "gpt-4o-realtime-preview",
  tool_choice: "auto" // allow model to call tools when useful
});

(async () => {
  const session = await agent.startRealtimeSession({ model: realtimeModel, sessionName: "orders-realtime" });

  session.on("session_started", () => {
    console.log("Realtime session started (Node)");
  });

  session.on("tool_called", (ev) => {
    console.log("Tool called:", ev.toolName, ev.args);
  });

  // keep your process running or integrate with your application loop
})();
Notes and next steps I can help with: • Show how tool outputs are returned to the model and how to surface them to users. • Demonstrate
tool_choice
behaviors:
auto
,
required
,
none
. • Add a frontend example showing RPC or websocket messages to react when a tool result arrives. • Add authentication/session token wiring for your environment. Which follow-up would you like?