acoustic-lizard-86122
02/25/2025, 9:18 PMgray-monkey-83525
02/25/2025, 9:18 PMbefore_llm_cb
callback in the VoicePipelineAgent. This callback allows you to intercept and modify the LLM's response before it is synthesized into speech. By returning False
from this callback, you can cancel the synthesis of the reply, effectively blocking the LLM from saying anything. You can then take your desired action before allowing the LLM to proceed.
Here's a basic example of how you might set this up:
from livekit.agents.pipeline import VoicePipelineAgent
def before_llm_callback(agent, chat_ctx):
# Your custom logic here
# For example, log the LLM's intended response
print("LLM is about to say:", chat_ctx.messages[-1].text)
# Take your action here
# ...
# Return False to block the LLM from speaking
return False
agent = VoicePipelineAgent(
vad=vad_instance,
stt=stt_instance,
llm=llm_instance,
tts=tts_instance,
before_llm_cb=before_llm_callback
)
Would you like more details on implementing this or any specific part of the process?
Sources:
- VoicePipelineAgent 👍 0 button 👎 0 button 💡 Hint: Mention @runllm in the thread for followups.acoustic-lizard-86122
02/25/2025, 9:19 PMgray-monkey-83525
02/25/2025, 9:19 PMagent_speech_committed
event to trigger the hang-up action after the bot has spoken. Here's a basic example of how you might implement this:
1. Define a Callback: Use the agent_speech_committed
event to detect when the bot has finished speaking.
2. Hang Up: Call a function to hang up the call after the speech is committed.
Here's a code snippet to illustrate this:
import asyncio
from livekit import api
from livekit.agents.pipeline import VoicePipelineAgent
async def hang_up(ctx, participant):
lk_api = api.LiveKitAPI()
await lk_api.room.remove_participant(
api.RoomParticipantIdentity(
room=ctx.room.name, identity=participant.identity
)
)
def on_speech_committed(ctx, participant):
asyncio.create_task(hang_up(ctx, participant))
agent = VoicePipelineAgent(
vad=vad_instance,
stt=stt_instance,
llm=llm_instance,
tts=tts_instance
)
agent.on("agent_speech_committed", lambda msg: on_speech_committed(ctx, participant))
This setup will ensure that once the bot finishes speaking, the call is terminated. Would you like more details on any part of this process?
Sources:
- LiveKit Slack Discussion 👍 0 button 👎 0 button 💡 Hint: Mention @runllm in the thread for followups.acoustic-lizard-86122
02/25/2025, 9:21 PMgray-monkey-83525
02/25/2025, 9:21 PMsay
method of the VoicePipelineAgent
to play the phrase, and then use the agent_speech_committed
event to trigger the hang-up action once the phrase has been spoken. Here's how you can implement this:
1. Use say
Method: Call the say
method to have the bot speak the specific phrase.
2. Listen for agent_speech_committed
: Register a callback for the agent_speech_committed
event to hang up the call after the phrase is spoken.
Here's a code example:
import asyncio
from livekit import api
from livekit.agents.pipeline import VoicePipelineAgent
async def hang_up(ctx, participant):
lk_api = api.LiveKitAPI()
await lk_api.room.remove_participant(
api.RoomParticipantIdentity(
room=ctx.room.name, identity=participant.identity
)
)
def on_speech_committed(ctx, participant):
asyncio.create_task(hang_up(ctx, participant))
async def main(ctx, participant):
agent = VoicePipelineAgent(
vad=vad_instance,
stt=stt_instance,
llm=llm_instance,
tts=tts_instance
)
# Say the specific phrase
await agent.say("This is the specific phrase I want to say.", allow_interruptions=False)
# Register the callback to hang up after speaking
agent.on("agent_speech_committed", lambda msg: on_speech_committed(ctx, participant))
# Assuming `ctx` and `participant` are defined in your context
# asyncio.run(main(ctx, participant))
This code will ensure that the bot says the specified phrase and then hangs up the call. Would you like more details on any part of this process?
Sources:
- LiveKit Slack Discussion 👍 0 button 👎 0 button 💡 Hint: Mention @runllm in the thread for followups.acoustic-lizard-86122
02/25/2025, 10:26 PMgray-monkey-83525
02/25/2025, 10:26 PMuser_stopped_speaking
event to detect when the user stops speaking.
2. Start a Timer: When the user stops speaking, start a timer for a specified duration (e.g., 5 seconds).
3. Prompt the User: If the timer completes without detecting any new speech activity, use the say
method to prompt the user to speak again.
Here's a code example:
import asyncio
from livekit.agents.pipeline import VoicePipelineAgent
timeout_task = None
TIMEOUT_SECONDS = 5
async def prompt_user(agent):
await agent.say("Please say something to continue.", allow_interruptions=False)
def reset_timeout(agent):
global timeout_task
if timeout_task and not timeout_task.done():
timeout_task.cancel()
timeout_task = asyncio.create_task(start_timeout(agent))
async def start_timeout(agent):
try:
await asyncio.sleep(TIMEOUT_SECONDS)
await prompt_user(agent)
except asyncio.CancelledError:
pass
agent = VoicePipelineAgent(
vad=vad_instance,
stt=stt_instance,
llm=llm_instance,
tts=tts_instance
)
agent.on("user_stopped_speaking", lambda *args: reset_timeout(agent))
agent.on("user_started_speaking", lambda *args: reset_timeout(agent))
This setup will prompt the user to speak again if there is no activity for the specified timeout period. Would you like more details on any part of this process?
Sources:
- LiveKit Slack Discussion 👍 0 button 👎 0 button 💡 Hint: Mention @runllm in the thread for followups.acoustic-lizard-86122
02/25/2025, 10:29 PMgray-monkey-83525
02/25/2025, 10:29 PMuser_stopped_speaking
and agent_stopped_speaking
events to determine when both the user and the bot are silent.
2. Start Timer on Silence: Only start the timer when both events have been triggered, indicating silence from both parties.
3. Prompt on Timer Completion: If the timer completes without any new speech activity, prompt the user.
Here's a code example:
import asyncio
from livekit.agents.pipeline import VoicePipelineAgent
timeout_task = None
TIMEOUT_SECONDS = 5
user_speaking = False
agent_speaking = False
async def prompt_user(agent):
await agent.say("Please say something to continue.", allow_interruptions=False)
def reset_timeout(agent):
global timeout_task
if timeout_task and not timeout_task.done():
timeout_task.cancel()
if not user_speaking and not agent_speaking:
timeout_task = asyncio.create_task(start_timeout(agent))
async def start_timeout(agent):
try:
await asyncio.sleep(TIMEOUT_SECONDS)
await prompt_user(agent)
except asyncio.CancelledError:
pass
def on_user_stopped_speaking(*args):
global user_speaking
user_speaking = False
reset_timeout(agent)
def on_user_started_speaking(*args):
global user_speaking
user_speaking = True
reset_timeout(agent)
def on_agent_stopped_speaking(*args):
global agent_speaking
agent_speaking = False
reset_timeout(agent)
def on_agent_started_speaking(*args):
global agent_speaking
agent_speaking = True
reset_timeout(agent)
agent = VoicePipelineAgent(
vad=vad_instance,
stt=stt_instance,
llm=llm_instance,
tts=tts_instance
)
agent.on("user_stopped_speaking", on_user_stopped_speaking)
agent.on("user_started_speaking", on_user_started_speaking)
agent.on("agent_stopped_speaking", on_agent_stopped_speaking)
agent.on("agent_started_speaking", on_agent_started_speaking)
This setup ensures that the timer only starts when both the user and the bot are silent, and it will prompt the user if no activity is detected for 5 seconds. Would you like more details on any part of this process?
Sources:
- https://docs.livekit.io/agents/voice-agent/voice-pipeline/
- https://docs.livekit.io/agents/voice-agent/ 👍 0 button 👎 0 button 💡 Hint: Mention @runllm in the thread for followups.acoustic-lizard-86122
02/25/2025, 11:02 PM@llm.ai_callable()
async def detected_answering_machine(
self,
):
"""
Use this when the call reaches voicemail, AFTER you hear the voicemail greeting intro.
"""
if isinstance(self.agent, VoicePipelineAgent):
if self.prompt_data.is_last_call:
await self.agent.say(self.prompt_data.last_call_message)
self.agent.on("agent_speech_committed", lambda msg: asyncio.create_task(self.end_call()))
return
await self.end_call()
else:
return "I just heard the user's voicemail. I should handle this according to my instructions."
@llm.ai_callable()
if this tool gets interrupted, do not hang up since we are not in voicemail, how do I do that?gray-monkey-83525
02/25/2025, 11:02 PMinterrupted
attribute of the speech handle to determine if the speech was interrupted. Here's how you can modify your code:
1. Check for Interruptions: Before calling end_call
, check if the speech was interrupted.
2. Proceed Based on Interruption: Only proceed with the hang-up if the speech was not interrupted.
Here's a modified version of your code:
@llm.ai_callable()
async def detected_answering_machine(self):
"""
Use this when the call reaches voicemail, AFTER you hear the voicemail greeting intro.
"""
if isinstance(self.agent, VoicePipelineAgent):
if self.prompt_data.is_last_call:
await self.agent.say(self.prompt_data.last_call_message)
def on_speech_committed(msg):
if not msg.interrupted: # Check if the speech was not interrupted
asyncio.create_task(self.end_call())
self.agent.on("agent_speech_committed", on_speech_committed)
return
await self.end_call()
else:
return "I just heard the user's voicemail. I should handle this according to my instructions."
This setup ensures that the call is only ended if the speech was not interrupted, indicating that it was indeed a voicemail. Would you like more details on any part of this process?
Sources:
- https://docs.livekit.io/agents-js/interfaces/agents.pipeline.VPAOptions.html
- https://livekit-users.slack.com/archives/C07FY8WHGPM/p1733820422.673179
Note: I wasn’t able to find highly relevant data sources, but above are a few potentially relevant links. 👍 0 button 👎 0 button 💡 Hint: Mention @runllm in the thread for followups.acoustic-lizard-86122
02/25/2025, 11:05 PMgray-monkey-83525
02/25/2025, 11:05 PMinterrupted
attribute on a ChatMessage
object, which does not exist. Instead, you should be checking the interrupted
status on the SpeechHandle
object associated with the speech event.
To resolve this, ensure that you are accessing the interrupted
attribute from the correct object. Here's how you can modify your code to check for interruptions correctly:
1. Access the Correct Object: Ensure you are using the SpeechHandle
object to check for interruptions.
Here's an example of how you might adjust your code:
@llm.ai_callable()
async def detected_answering_machine(self):
"""
Use this when the call reaches voicemail, AFTER you hear the voicemail greeting intro.
"""
if isinstance(self.agent, VoicePipelineAgent):
if self.prompt_data.is_last_call:
speech_handle = await self.agent.say(self.prompt_data.last_call_message)
def on_speech_committed(handle):
if not handle.interrupted: # Check if the speech was not interrupted
asyncio.create_task(self.end_call())
self.agent.on("agent_speech_committed", on_speech_committed)
return
await self.end_call()
else:
return "I just heard the user's voicemail. I should handle this according to my instructions."
In this example, speech_handle
is used to track the speech event, and its interrupted
attribute is checked to determine if the speech was interrupted.
Would you like more details on any part of this process?
Sources:
- SpeechHandle Documentation 👍 0 button 👎 0 button 💡 Hint: Mention @runllm in the thread for followups.