Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
ed65b72
wip
longcw May 21, 2026
31f9cec
(perplexity responses): update default model (#5780)
tinalenguyen May 20, 2026
27b4055
fix(interruption): incorrect guard skips true interruptions (#5787)
chenghao-mou May 20, 2026
812305f
docs(perplexity): fix stale default model in responses.LLM example (#…
detail-app[bot] May 21, 2026
8865076
add new bulbul:v3 speaker voices (#5782)
amlesh-dev May 21, 2026
ad2571f
livekit-agents@1.5.12 (#5789)
github-actions[bot] May 21, 2026
1908687
(openai realtime): add gpt-realtime-2 model str (#5791)
tinalenguyen May 21, 2026
f4b60d9
(healthcare example): remove filesearch and pdf (#5795)
tinalenguyen May 21, 2026
dc5c977
chore(worker): update worker warnings AGT-2909 (#5771)
chenghao-mou May 21, 2026
0ad3ab4
fix(voice): race between flush and clear_buffer on interrupt leaks un…
longcw May 21, 2026
58b02f0
Merge remote-tracking branch 'origin/main' into longc/unify-run-context
longcw May 22, 2026
2ede7d7
update prompt for tail
longcw May 22, 2026
a7e2bfd
add async tool prompt and async toolset
longcw May 22, 2026
96a5416
clean
longcw May 22, 2026
b2a8ee2
fix wait for inactive
longcw May 25, 2026
2fa614a
clean up
longcw May 25, 2026
a69bfb7
fix bugs
longcw May 25, 2026
814de51
update tests
longcw May 25, 2026
f361ab3
update comments
longcw May 25, 2026
53570f1
update AsyncToolPrompts
longcw May 25, 2026
50c363e
first update uses the original id
longcw May 25, 2026
bcc8cac
update prompt
longcw May 25, 2026
36c32ee
add todo
longcw May 26, 2026
56d57ba
raise error from .cacnel if allow cancellation is False
longcw May 26, 2026
4f500d6
update test to expect ToolError for non-cancellable tasks
longcw May 26, 2026
b4c94ee
support sync funcs in confirm-duplicate wrapper
longcw May 26, 2026
381b113
log detached tool errors and surface failed cancel
longcw May 26, 2026
685aaf0
Merge remote-tracking branch 'origin/main' into longc/unify-run-context
longcw May 27, 2026
ae1f5cf
add ToolFlag.CANCELLABLE
longcw May 27, 2026
7794923
update prompt
longcw May 27, 2026
9e5c7f7
fix ruff
longcw May 27, 2026
bdd5aae
remove deprecated on_duplicate_call kwarg from AsyncToolset
longcw May 27, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 46 additions & 36 deletions examples/voice_agents/async_tool_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,33 +12,67 @@
"ddgs (duckduckgo search) is required for this example. Install it with: pip install ddgs"
) from e

from livekit.agents import Agent, AgentServer, AgentSession, JobContext, cli, inference, llm
from livekit.agents.llm.async_toolset import AsyncRunContext, AsyncToolset
from livekit.agents import (
Agent,
AgentServer,
AgentSession,
JobContext,
RunContext,
cli,
inference,
llm,
)
from livekit.plugins import silero

logger = logging.getLogger("async-travel-helper")

_annoying_loggers = ["h2", "rustls", "hyper_util", "cookie_store", "primp"]
_annoying_loggers = [
"h2",
"rustls",
"hyper_util",
"cookie_store",
"primp",
"hpack",
"hickory_net",
"hickory_dns",
"hickory_resolver",
"hickory_proto",
"reqwest",
"ddgs.ddgs",
]
for name in _annoying_loggers:
logging.getLogger(name).setLevel(logging.WARNING)

load_dotenv()


class TravelToolset(AsyncToolset):
class TravelAgent(Agent):
def __init__(self) -> None:
super().__init__(id="travel")
super().__init__(
instructions=(
"You are a friendly travel assistant that communicates via voice. "
"Avoid emojis and markdown — speak naturally and concisely. "
"You can help with two things: booking flights and recommending what "
"to see, eat, and do at a destination. "
"Use the book_flight tool when the user wants to book a flight. "
"Use the tour_guide tool when the user asks about places to visit, "
"restaurants, sightseeing, nightlife, or things to do somewhere. "
"Summarize the results in a concise and natural manner that suitable for voice communication. "
f"Today is {datetime.now().strftime('%Y-%m-%d %A')}. "
"When user is not asking, don't repeat the messages you have already said in the conversation. "
"Don't make up flight details or ask for flight preferences — always use the tools. "
),
)
self._thinking_llm = inference.LLM(
"openai/gpt-5.4", extra_kwargs={"reasoning_effort": "medium"}
)
self._ddgs = DDGS()

# -- Tool 1: Mock flight booking (takes ~2 minutes with progress updates) --
async def on_enter(self):
self.session.generate_reply(instructions="Greet the user and introduce yourself.")

@llm.function_tool
async def book_flight(
self, ctx: AsyncRunContext, origin: str, destination: str, date: str
) -> str:
@llm.function_tool(flags=llm.ToolFlag.CANCELLABLE, on_duplicate="confirm")
async def book_flight(self, ctx: RunContext, origin: str, destination: str, date: str) -> str:
"""Called when user wants to book a flight.

Args:
Expand Down Expand Up @@ -87,8 +121,8 @@ async def book_flight(

# -- Tool 2: Tour guide via web search --

@llm.function_tool
async def tour_guide(self, ctx: AsyncRunContext, destination: str, interests: str) -> str:
@llm.function_tool(flags=llm.ToolFlag.CANCELLABLE, on_duplicate="confirm")
async def tour_guide(self, ctx: RunContext, destination: str, interests: str) -> str:
"""Called when user wants to know about a destination, including
sightseeing spots, restaurants, local food, nightlife, or neighborhood tips.

Expand Down Expand Up @@ -160,30 +194,6 @@ async def _summarize(
return response.text


class TravelAgent(Agent):
def __init__(self) -> None:
super().__init__(
instructions=(
"You are a friendly travel assistant that communicates via voice. "
"Avoid emojis and markdown — speak naturally and concisely. "
"You can help with two things: booking flights and recommending what "
"to see, eat, and do at a destination. "
"Use the book_flight tool when the user wants to book a flight. "
"Use the tour_guide tool when the user asks about places to visit, "
"restaurants, sightseeing, nightlife, or things to do somewhere. "
"Summarize the results in a concise and natural manner that suitable for voice communication. "
f"Today is {datetime.now().strftime('%Y-%m-%d %A')}. "
"When user is not asking, don't repeat the messages you have already said in the conversation. "
"Don't make up flight details or ask for flight preferences — always use the tools. "
),
tools=[TravelToolset()],
)
self._llm_count = 0

async def on_enter(self):
self.session.generate_reply(instructions="Greet the user and introduce yourself.")


server = AgentServer()


Expand Down
6 changes: 3 additions & 3 deletions examples/voice_agents/basic_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,10 +105,10 @@ async def entrypoint(ctx: JobContext) -> None:
"resume_false_interruption": True,
"false_interruption_timeout": 1.0,
},
# allow the LLM to generate a response while waiting for the end of turn
# See more at https://docs.livekit.io/agents/build/audio/#preemptive-generation
preemptive_generation={"enabled": True, "max_retries": 3},
),
# allow the LLM to generate a response while waiting for the end of turn
# See more at https://docs.livekit.io/agents/build/audio/#preemptive-generation
preemptive_generation=True,
# blocks interruptions for a few seconds after the agent starts speaking to allow client to calibrate AEC
aec_warmup_duration=3.0,
tts_text_transforms=[
Expand Down
2 changes: 2 additions & 0 deletions livekit-agents/livekit/agents/llm/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
ToolChoice,
ToolContext,
ToolError,
ToolFlag,
Toolset,
find_function_tools,
function_tool,
Expand Down Expand Up @@ -90,6 +91,7 @@
"ProviderTool",
"ToolContext",
"ToolError",
"ToolFlag",
"StopResponse",
"utils",
"remote_chat_context",
Expand Down
Loading
Loading