ambitious-refrigerator-86200
09/17/2025, 3:45 AMtall-airline-16756
09/17/2025, 3:45 AMtall-airline-16756
09/17/2025, 3:46 AM@dataclass
class UserData:
customer_name: Optional[str] = None
customer_phone: Optional[str] = None
2. Create Function Tools: Next, use function tools to collect inputs and update the data class through the RunContext. For instance:
@function_tool()
async def update_name(name: str, context: RunContext[UserData]) -> str:
context.userdata.customer_name = name
return f"The name is updated to {name}"
3. Initialize Agent Session: Then, start an agent session, making sure to specify your data class in the userdata parameter:
session = AgentSession(
llm=aws.realtime.RealtimeModel(),
userdata=UserData(),
)
4. Integrate with Nova Sonic: To use the Nova Sonic model, simply specify aws.realtime.RealtimeModel when initializing your session.
5. Optional - Use Tasks: If you want to handle operations like form filling in a structured way, tasks can be very helpful. For example:
class CollectConsent(AgentTask[bool]):
...
This setup is designed to make real-time, voice-driven data collection smooth and organized, keeping your data structured and persistent throughout agent sessions. If anything feels unclear or you’d like me to help you with a specific part, please don’t hesitate to ask—I’m here to help!
Sources: livekit-agents/livekit/agents/voice/generation.py | complex-agents/nova-sonic/nova-sonic-form-agent/components/ui/select.tsx | Amazon Nova Sonic integration guide | LiveKit Docs | Workflows | LiveKit Docsambitious-refrigerator-86200
09/17/2025, 3:58 AMtall-airline-16756
09/17/2025, 3:59 AMaws_sdk_bedrock_runtime.models.ValidationException: Tool Response parsing error, which usually happens when the tool response isn't in a valid JSON format. Don’t worry, I’m here to help you work through this! Here are some friendly steps to get things back on track:
1. Make Sure Your Tool Returns JSON-Serializable Output: Your tool functions should always return a dictionary or something that can be easily converted to JSON. For example:
@function_tool()
async def update_name(name: str, context: RunContext[UserData]) -> dict:
context.userdata.customer_name = name
return {"status": "success", "name": name}
2. Check JSON Serialization: If your tool returns a string, double-check that it’s valid JSON. You can use json.dumps to safely serialize more complex data:
import json
return json.dumps({"result": "your string"})
3. Turn on Debug Logging: Setting the environment variable LK_BEDROCK_DEBUG=1 can really help you see what’s being sent to AWS Bedrock and spot any formatting hiccups.
4. Review Tool Execution: Make sure your tool function is actually running, not throwing any exceptions, and returning something that can be serialized without issues.
5. Verify Tool Schema: It’s important your tool function has clear type hints so the right schema is generated for both input and output.
Following these tips should smooth out the response formatting and prevent those pesky parsing errors. If you run into any bumps or want me to walk through this with you, just let me know—I’m happy to help!
Sources: livekit-agents/livekit/agents/llm/_provider_format/aws.py | agents/src/voice/generation.tsambitious-refrigerator-86200
09/17/2025, 4:04 AMget_user_profile returned an invalid output {"call_id": "95c2b06c-0bac-4345-be98-7317bb5737c2", "output": {"status": "Profile Updated", "name": "<__mp_main__.PolicyAgent object at 0x000001A0F9137980>"}}
here "get_user_profile" is the name of my tool and "PolicyAgent" is the name of my agenttall-airline-16756
09/17/2025, 4:05 AMget_user_profile is returning a non-serializable object (like PolicyAgent), which LiveKit doesn’t support. No worries though! To fix this, you’ll want to use the agent handoff pattern to smoothly transfer control to another agent.
Here’s a friendly step-by-step on how to do it:
1. Return a Structured Handoff Object: Instead of sending back the agent object directly, wrap it in a structured way. For example:
@function_tool()
async def transfer_to_policy_agent(context: RunContext):
return PolicyAgent(), "Transferring to the policy specialist to assist with your inquiry"
2. How It Works: LiveKit recognizes this handoff pattern and handles it properly. The agent is transferred, and the accompanying message is shown as the tool output.
3. Key Things to Keep in Mind:
◦ Avoid returning raw agent objects or any non-serializable data directly.
◦ Make sure to return only one agent instance per function call.
Following these steps should help you avoid those pesky invalid output errors and make sure your agent transitions are seamless. If anything’s unclear or you want to dive deeper, just let me know—I’m here to help!
Sources: agents/src/llm/chat_context.ts | livekit-agents/livekit/agents/llm/_provider_format/utils.pyambitious-refrigerator-86200
09/17/2025, 4:08 AMtall-airline-16756
09/17/2025, 4:09 AMget_user_profile is returning a dictionary that contains a non-serializable object reference (like PolicyAgent). LiveKit needs all function outputs to be JSON-serializable, so it can’t handle direct object references in the output.
Here’s a friendly step-by-step on how to fix this:
1. Update the Return Value: Make sure the function returns only JSON-serializable data. For example:
@function_tool()
async def get_user_profile(name: str, context: RunContext[UserProfile]) -> dict:
context.userdata.name = name
return {"status": "Profile Updated", "name": name}
2. Debug the Output: It’s helpful to add some logging to check what’s being returned before sending it out:
<http://logger.info|logger.info>(f"Returning from get_user_profile: {type(output)} = {output}")
3. Validate JSON Serialization: Double-check that everything you return can be serialized into JSON. Avoid including objects like PolicyAgent directly in the output.
By making sure your output is strictly JSON-serializable, you’ll be able to prevent this error from popping up. If you’d like, I’m here to help you walk through this or answer any other questions you might have!
Sources: livekit-agents/livekit/agents/voice/agent.py | Tool definition and use | LiveKit Docsambitious-refrigerator-86200
09/17/2025, 4:25 AMtall-airline-16756
09/17/2025, 4:26 AMfrom dataclasses import dataclass
from typing import Optional
from livekit.agents import Agent, AgentSession, AgentTask, function_tool, RunContext
from livekit.aws.realtime import RealtimeModel
from livekit.core import AutoSubscribe
# Define the UserProfile data class
@dataclass
class UserProfile:
name: Optional[str] = None
# Define the PolicyAgent class
class PolicyAgent(Agent):
def __init__(self, chat_ctx=None):
super().__init__(
instructions=(
"You are a policy help agent. You must collect the user profile data with the help of the 'get_user_profile' tool at the start of the session. Without that, don't answer any questions."
),
chat_ctx=chat_ctx,
)
@function_tool()
async def get_user_profile(self, name: str, context: RunContext[UserProfile]) -> dict:
"""Collects the user's name and updates the profile."""
context.userdata.name = name
return {"status": "Profile Updated", "name": name}
# Define the AgentTask to collect the name
class CollectNameTask(AgentTask[bool]):
async def run(self, context: RunContext[UserProfile]) -> bool:
"""Runs the task to collect the user's name."""
if not context.userdata.name:
await context.agent.get_user_profile(name="John Doe", context=context) # Example name
return True
# Define the entrypoint function
async def entrypoint(ctx):
await ctx.connect(auto_subscribe=AutoSubscribe.AUDIO_ONLY)
# Initialize the agent session with Nova Sonic and the user profile
session = AgentSession(
llm=RealtimeModel(),
userdata=UserProfile(),
)
# Start the session with the PolicyAgent and run the CollectNameTask
await session.start(agent=PolicyAgent(), room=ctx.room)
await session.run_task(CollectNameTask())
# Main entrypoint
if __name__ == "__main__":
from livekit.agents import cli
cli.run_app(cli.WorkerOptions(entrypoint_fnc=entrypoint))
This code:
• Defines a UserProfile data class to store the user’s name.
• Implements the PolicyAgent with a get_user_profile tool to collect and update the name.
• Uses an AgentTask (CollectNameTask) to ensure the name is collected at the start of the session.
• Integrates with Nova Sonic using RealtimeModel.
If you have any questions or would like me to walk you through any part of this, please don’t hesitate to ask. I’m here to help you get everything working just the way you need!ambitious-refrigerator-86200
09/17/2025, 4:50 AMtall-airline-16756
09/17/2025, 4:51 AMAgentSession class doesn’t have a run_task method. Instead, AgentTask is an experimental feature that runs automatically within the context of an existing agent session.
To help you fix this, you can either integrate the task logic directly into the agent or make sure the task is triggered as part of the agent’s workflow. Here’s an example:
# Inside the PolicyAgent class
class PolicyAgent(Agent):
...
async def on_start(self, context: RunContext[UserProfile]):
# Run the task logic here
if not context.userdata.name:
await self.get_user_profile(name="John Doe", context=context)
This way, the task logic runs as soon as the agent session starts. If anything’s unclear or you’d like a hand with this, please don’t hesitate to ask—I’m here to help!
Sources: Workflows | LiveKit Docs | livekit-agents/livekit/agents/voice/agent_activity.py