little-king-41579
12/15/2025, 6:02 AMtall-airline-16756
12/15/2025, 6:02 AMtall-airline-16756
12/15/2025, 6:02 AMasync def on_user_turn_completed(self, turn_ctx: ChatContext, new_message: ChatMessage) -> None:
if new_message.text_content:
new_message.content = [f"Modified: {new_message.text_content}"]
await self.update_chat_ctx(turn_ctx)
Node.js example:
async onUserTurnCompleted(turnCtx: llm.ChatContext, newMessage: llm.ChatMessage): Promise<void> {
if (newMessage.textContent) {
newMessage.content = [`Modified: ${newMessage.textContent}`];
await this.updateChatCtx(turnCtx);
}
}
Notes:
• Place this logic in a base agent or an early pipeline node so every agent/task sees the modified message.
• If you rely on explicit chat context passing between agents, ensure you call the context update (e.g., update_chat_ctx() / updateChatCtx) after modifying the message.
Would you like a version that strips PII, applies templating, or conditionally modifies messages based on metadata?
Sources: Pipeline nodes and hooks | LiveKit docslittle-king-41579
12/15/2025, 6:14 AMtall-airline-16756
12/15/2025, 6:14 AMon_user_turn_completed in a base Agent class that all your agents inherit. The modification runs for every agent automatically.
• Common middleware / pipeline node: Implement the hook as a middleware or early pipeline component that runs before agents process the turn; this ensures all downstream agents see the modified message.
• Shared utility + explicit call: Keep a single utility function that updates the message and call it from each agent’s on_user_turn_completed if you prefer explicit wiring.
If you want, I can provide a short example showing the base-class approach in Python or Node.js. Which language would you like?little-king-41579
12/15/2025, 6:15 AMtall-airline-16756
12/15/2025, 6:15 AMon_user_turn_completed to modify the user message for the entire session, and a derived agent that inherits that behavior.
# base_agent.py
from typing import Any
from your_llm_framework import Agent, ChatContext, ChatMessage
class BaseAgent(Agent):
async def on_user_turn_completed(self, turn_ctx: ChatContext, new_message: ChatMessage) -> None:
# Example modification: prepend a tag and strip PII-like pattern (simple example)
if new_message.text_content:
modified = f"[SessionModified] {new_message.text_content.strip()}"
new_message.content = [modified]
# Persist the updated chat context if required by your framework
if hasattr(self, "update_chat_ctx"):
await self.update_chat_ctx(turn_ctx)
# custom_agent.py
from base_agent import BaseAgent
from your_llm_framework import ChatContext, ChatMessage
class SupportAgent(BaseAgent):
async def on_user_turn_completed(self, turn_ctx: ChatContext, new_message: ChatMessage) -> None:
# Optionally call base behavior to keep shared modifications
await super().on_user_turn_completed(turn_ctx, new_message)
# Agent-specific additional modification (optional)
if new_message.text_content and "billing" in new_message.text_content.lower():
new_message.content = [f"{new_message.content[0]} — routed: billing"]
# No need to call update_chat_ctx again if base already did it
Key points:
• Put shared logic in BaseAgent.on_user_turn_completed. All agents that inherit BaseAgent will run the modification automatically.
• Call await super().on_user_turn_completed(...) in derived agents if you add agent-specific changes.
• Call update_chat_ctx once (in the base) to persist the change for downstream agents/tasks.
Would you like this adapted to a specific framework API or an example that strips emails/phone numbers?little-king-41579
12/15/2025, 6:21 AMtall-airline-16756
12/15/2025, 6:21 AMnew_message (not yet added to the context), and lets you modify new_message so all downstream agents/tasks see the changed content.
Would you like a short Python snippet showing how to do a simple pre-processing change in on_user_turn_completed?
Sources: Events and error handling | LiveKit docs | Pipeline nodes and hooks | LiveKit docslittle-king-41579
12/15/2025, 6:22 AMtall-airline-16756
12/15/2025, 6:23 AMon_user_turn_completed cannot be used on AgentTask instances.
Why: on_user_turn_completed is an Agent lifecycle hook invoked by the AgentSession when a user turn ends (before LLM inference). AgentTask runs as a self-contained unit with its own control flow while it holds the session, so agent lifecycle hooks are not invoked during task execution.
What to do instead:
• Implement on_user_turn_completed in a shared base Agent class so every agent in the session applies the same preprocessing before the LLM runs.
• If a task needs further tweaks, let the base agent do session-wide preprocessing, then perform task-specific adjustments inside the task (e.g., in on_enter or task logic) after the task starts.
If you want, I can provide a concise Python example showing: 1) a BaseAgent with on_user_turn_completed, and 2) an AgentTask that does optional task-specific adjustments. Which would you prefer?
Sources: Pipeline nodes and hooks | LiveKit docs | Tasks and task groups | LiveKit docs