brief-nest-38726
02/14/2026, 7:16 AMtall-airline-16756
02/14/2026, 7:16 AMtall-airline-16756
02/14/2026, 7:17 AMlivekit-plugins-langchain pattern so each node returns text which is spoken via TTS. After the example I’ll list required deps, env vars, and quick notes on extending it.
If you want this for JavaScript/TypeScript instead, or want nodes to call external tools, memory, or multimodal inputs, tell me which and I’ll provide that variant.
1) Minimal working example (Python)
# requirements: livekit-agents[langchain], langgraph, langchain_core, openai, deepgram (or STT/TTS providers you prefer)
from langgraph.graph import StateGraph, MessagesState
from langchain_core.messages import AIMessage
from livekit.agents import AgentSession, JobContext, cli
from livekit.plugins import langchain as lk_langchain
from livekit.plugins import openai, deepgram, silero
# Define state that holds convo messages (LangGraph helpers)
class AgentState(MessagesState):
pass
# Build a small LangGraph workflow with nodes that output natural-language replies
def create_workflow():
graph = StateGraph(AgentState)
# Node: greet - inspects last user message and replies
def greet_node(state: AgentState):
messages = state["messages"]
user_text = messages[-1].content if messages else ""
resp = f"Hello! I heard: '{user_text}'. What can I do for you today?"
return {"messages": [AIMessage(content=resp)]}
# Node: assist - provides follow-up natural text
def assist_node(state: AgentState):
last = state["messages"][-1].content.lower() if state["messages"] else ""
if "book" in last or "reserve" in last:
resp = "I can help with bookings. Which date and time do you prefer?"
elif "help" in last:
resp = "I can help with support or product info. Which would you like?"
else:
resp = "Could you clarify that a bit more?"
return {"messages": [AIMessage(content=resp)]}
graph.add_node("greet", greet_node)
graph.add_node("assist", assist_node)
graph.set_entry_point("greet")
graph.add_edge("greet", "assist")
return graph.compile()
# LiveKit Agent entrypoint: wire STT, LLM adapter (LangGraph), TTS, VAD
async def entrypoint(ctx: JobContext):
# STT provider
stt = deepgram.STT(model="nova-2", language="en")
# TTS provider
tts = openai.TTS(model="tts-1", voice="alloy")
# Voice activity detection (optional)
vad = silero.VAD.load()
# Wrap the LangGraph workflow into LiveKit's LLM adapter
langgraph_workflow = create_workflow()
llm_adapter = lk_langchain.LLMAdapter(graph=langgraph_workflow)
# Create agent session using LangGraph as the LLM
session = AgentSession(
stt=stt,
llm=llm_adapter,
tts=tts,
vad=vad,
)
await session.start(agent=None, room=ctx.room)
await ctx.connect()
if __name__ == "__main__":
cli.run_app(JobContext, entrypoint_fnc=entrypoint)
2) Install / environment
• Install:
◦ pip install "livekit-agents[langchain]" langgraph langchain_core openai deepgram
• Environment variables (examples you must set):
◦ LIVEKIT_URL, LIVEKIT_API_KEY, LIVEKIT_API_SECRET
◦ OPENAI_API_KEY (for TTS or LLMs if using OpenAI)
◦ DEEPGRAM_API_KEY (if using Deepgram STT)
3) How it works (short)
• LiveKit captures audio → STT transcribes → transcribed text becomes the newest message passed into the LangGraph workflow via LLMAdapter → the active LangGraph node returns AIMessage(content=...) → LiveKit converts that text to speech via TTS and plays it in the room.
• Every node returns natural-language text in its messages, so each node’s output is spoken.
4) Ways to extend
• Add memory (LangChain memory passed into the graph) for longer context.
• Allow nodes to call external tools or APIs (e.g., booking system) and then generate a natural response summarizing results.
• Add multimodal inputs (images, video) by building nodes that accept tool outputs and craft language responses.
• Switch STT/TTS providers (Deepgram, Whisper, OpenAI TTS) depending on latency/cost/quality requirements.
• Expose session.say("text") inside node logic if you want immediate/forced TTS output (control timing).
Quick question to tailor the next step:
• Which language do you want the example in (Python or JS/TS)?
• Do you want nodes to call external tools (APIs) or use memory?
Tell me your preference and I’ll provide the adapted example (e.g., JS/TS code, tool-calling node, or memory-enabled workflow).
Sources: LangChain integration guide | LiveKit Documentation | Modifying LLM output before TTS | LiveKit Documentation