Can you give me a detailed example about how to ha...
# ask-ai
w
Can you give me a detailed example about how to have a LiveKit backed LangGraph with multiple points of communication where we're waiting for user input? Do we need to have the
interrupt_before=
on the graph.compile step or is that handled automatically? My Agent is not saying anything when I dial in.
t
Thanks for reaching out, John. I'm an AI assistant and I'm here to help. Give me a moment while I research this topic.
I don't have the answer you're looking for. You could also try asking your question: • in one of the other Slack channels or • to https://deepwiki.com/livekit/livekit_composite which is trained on all LiveKit source code If you find the answer, please post it here to help others!
w
As a very minimal example, why wouldn't this be working?
Copy code
import logging
from typing import Optional
from uuid import UUID, uuid4, uuid5

from langchain_core.messages import AIMessage, HumanMessage
from langchain_openai.chat_models import ChatOpenAI
from langgraph.graph import StateGraph, add_messages
from langgraph.types import StreamWriter, interrupt
from livekit.agents import Agent, AgentSession, AutoSubscribe
from livekit.plugins import deepgram, langchain, openai, silero
from typing_extensions import Annotated, TypedDict

from revin_voice_agent.agents.graphs.util.types import TypedLivekit
from revin_voice_agent.utils.config import get_settings

logger = logging.getLogger(__name__)


class AgentState(TypedDict):
    messages: Annotated[list, add_messages]
    user_name: Optional[str]


async def ask_name(state: AgentState, writer: StreamWriter) -> AgentState:
    """Ask for the user's name"""
    livekit = TypedLivekit(writer)
    livekit.say("Hello! What's your name?")

    # Wait for user response
    name, name_msgs = interrupt("user_name")

    <http://logger.info|logger.info>(f"User name received: {name}")
    return {"user_name": name, "messages": name_msgs}


async def write_sonnet(state: AgentState, writer: StreamWriter) -> AgentState:
    """Write a sonnet using the user's name"""
    livekit = TypedLivekit(writer)

    # Generate a sonnet using the user's name
    prompt = f"Write a beautiful sonnet that includes the name '{state['user_name']}'. Make it poetic and personal."
    response = await ChatOpenAI(model="gpt-4o-mini").ainvoke([HumanMessage(content=prompt)])

    # Say the sonnet
    livekit.say(response.content)
    livekit.flush()

    <http://logger.info|logger.info>(f"Generated sonnet for {state['user_name']}")
    return {"messages": [AIMessage(content=f"Sonnet for {state['user_name']}: {response.content}")]}


async def say_goodbye(state: AgentState, writer: StreamWriter) -> AgentState:
    """Say goodbye to the user"""
    livekit = TypedLivekit(writer)
    livekit.say(f"Thank you for listening, {state['user_name']}. Goodbye!")
    livekit.flush()

    <http://logger.info|logger.info>(f"Said goodbye to {state['user_name']}")
    return {"messages": [AIMessage(content=f"Conversation ended with {state['user_name']}")]}


# Build the graph
builder = StateGraph(AgentState)

# Add nodes
builder.add_node("ask_name", ask_name)
builder.add_node("write_sonnet", write_sonnet)
builder.add_node("say_goodbye", say_goodbye)

# Set entry point
builder.set_entry_point("ask_name")

# Add edges to create the flow
builder.add_edge("ask_name", "write_sonnet")
builder.add_edge("write_sonnet", "say_goodbye")

# Compile the graph
graph = builder.compile()


def prewarm(proc):
    """Prewarm function for the minimal voice agent"""
    proc.userdata["vad"] = silero.VAD.load()


async def entrypoint(ctx):
    """Entrypoint function for the minimal voice agent"""

    <http://logger.info|logger.info>(f"connecting to room {ctx.room.name}")
    await ctx.connect(auto_subscribe=AutoSubscribe.AUDIO_ONLY)

    participant = await ctx.wait_for_participant()

    # Generate thread ID
    NAMESPACE = UUID("41010b5d-5447-4df5-baf2-97d69f2e9d06")
    if participant.sid is not None:
        thread_id = str(uuid5(NAMESPACE, participant.sid))
    else:
        thread_id = str(uuid4())

    <http://logger.info|logger.info>(
        f"starting minimal voice assistant for participant {participant.identity} (thread ID: {thread_id})"
    )
    settings = get_settings()

    agent = Agent(
        instructions="",  # Instructions are handled by the LangGraph
        llm=langchain.LLMAdapter(
            graph=graph,
            config={"configurable": {"thread_id": thread_id}},
        ),
    )

    # Create voice pipeline agent with our compiled graph
    session: AgentSession = AgentSession(
        stt=deepgram.STT(model=settings.deepgram.stt_model),
        tts=openai.TTS(
            model=settings.openai.tts_model,
            voice=settings.openai.tts_voice,
        ),
        vad=silero.VAD.load(),
        min_endpointing_delay=0.5,
        max_endpointing_delay=5.0,
    )

    await session.start(room=ctx.room, agent=agent)