how to modify the user message before the llm proc...
# ask-ai
l
how to modify the user message before the llm processing in a session specific for all agents tasks.
t
Thanks for reaching out, Rajadurai. I'm an AI assistant and I'm here to help. Give me a moment while I research this topic.
You can modify the user message for the whole session by intercepting it in the agent lifecycle before the LLM runs. Use the on_user_turn_completed hook (called after transcription/turn end but before LLM processing), edit the incoming ChatMessage, and persist the change so all downstream agents/tasks see it. Python example:
Copy code
async 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:
Copy code
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 docs
l
Do I write this function in all agent for the same functionality?
t
You don’t need to copy the function into every agent. Implement it once in a shared place and have your agents use it. Options: • Base agent class: Put
on_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?
l
yes provide a short example showing the base-class approach in Python
t
Below is a concise Python example showing a base Agent class that implements
on_user_turn_completed
to modify the user message for the entire session, and a derived agent that inherits that behavior.
Copy code
# 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)
Copy code
# 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?
l
Can I user on_conversation_item_added for this?
t
Good question — regarding modifying user messages before LLM processing: You cannot use on_conversation_item_added for that. on_conversation_item_added fires after a message is committed to the chat history, which is after the LLM has already processed the turn. It’s intended for post-processing tasks (logging, UI updates, persistence). Use on_user_turn_completed instead. That hook runs after transcription/turn end but before LLM inference, receives the
new_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 docs
l
Can I use on_user_turn_completed on AgentTasks?
t
Short answer: No —
on_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