Add end session handler and update example
This commit is contained in:
@@ -28,7 +28,35 @@ load_dotenv(override=True)
|
||||
logger.remove(0)
|
||||
logger.add(sys.stderr, level="DEBUG")
|
||||
|
||||
# Define our conversation flow
|
||||
# Flow Configuration
|
||||
#
|
||||
# This configuration defines a simple food ordering system with the following states:
|
||||
#
|
||||
# 1. start
|
||||
# - Initial state where user chooses between pizza or sushi
|
||||
# - Functions: choose_pizza, choose_sushi
|
||||
# - Transitions to: choose_pizza or choose_sushi
|
||||
#
|
||||
# 2. choose_pizza
|
||||
# - Handles pizza size selection and order confirmation
|
||||
# - Functions:
|
||||
# * select_pizza_size (terminal function, can be called multiple times)
|
||||
# * end (transitions to end node after order confirmation)
|
||||
# - Pre-action: Immediate TTS acknowledgment
|
||||
#
|
||||
# 3. choose_sushi
|
||||
# - Handles sushi roll count selection and order confirmation
|
||||
# - Functions:
|
||||
# * select_roll_count (terminal function, can be called multiple times)
|
||||
# * end (transitions to end node after order confirmation)
|
||||
# - Pre-action: Immediate TTS acknowledgment
|
||||
#
|
||||
# 4. end
|
||||
# - Final state that closes the conversation
|
||||
# - No functions available
|
||||
# - Pre-action: Farewell message
|
||||
# - Post-action: Ends conversation
|
||||
|
||||
flow_config = {
|
||||
"initial_node": "start",
|
||||
"nodes": {
|
||||
@@ -59,14 +87,19 @@ flow_config = {
|
||||
"choose_pizza": {
|
||||
"message": {
|
||||
"role": "system",
|
||||
"content": "The user has chosen pizza. You must now ask them to select a size using the select_pizza_size function. Do not proceed until they use this function. Do not assume any selections have been made.",
|
||||
"content": """You are handling a pizza order. Use the available functions:
|
||||
- Use select_pizza_size when the user specifies a size (can be used multiple times if they change their mind)
|
||||
- Use the end function ONLY when the user confirms they are done with their order
|
||||
|
||||
After each size selection, confirm the selection and ask if they want to change it or complete their order.
|
||||
Only use the end function after the user confirms they are satisfied with their order.""",
|
||||
},
|
||||
"functions": [
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "select_pizza_size",
|
||||
"description": "Select pizza size",
|
||||
"description": "Record the selected pizza size",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -79,21 +112,36 @@ flow_config = {
|
||||
"required": ["size"],
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "end",
|
||||
"description": "Complete the order (use only after user confirms)",
|
||||
"parameters": {"type": "object", "properties": {}},
|
||||
},
|
||||
},
|
||||
],
|
||||
"pre_actions": [
|
||||
{"type": "tts_say", "text": "Ok, let me help you with your pizza order..."}
|
||||
],
|
||||
"pre_actions": [{"type": "tts_say", "text": "Ok, let me pull up our pizza menu..."}],
|
||||
},
|
||||
"choose_sushi": {
|
||||
"message": {
|
||||
"role": "system",
|
||||
"content": "The user has chosen sushi. Immediately say: 'How many sushi rolls would you like to order?' If they answer provide to the question of how many rolls, use the select_roll_count function.",
|
||||
"content": """You are handling a sushi order. Use the available functions:
|
||||
- Use select_roll_count when the user specifies how many rolls (can be used multiple times if they change their mind)
|
||||
- Use the end function ONLY when the user confirms they are done with their order
|
||||
|
||||
After each roll count selection, confirm the count and ask if they want to change it or complete their order.
|
||||
Only use the end function after the user confirms they are satisfied with their order.""",
|
||||
},
|
||||
"functions": [
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "select_roll_count",
|
||||
"description": "Select number of sushi rolls",
|
||||
"description": "Record the number of sushi rolls",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -107,9 +155,28 @@ flow_config = {
|
||||
"required": ["count"],
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "end",
|
||||
"description": "Complete the order (use only after user confirms)",
|
||||
"parameters": {"type": "object", "properties": {}},
|
||||
},
|
||||
},
|
||||
],
|
||||
"pre_actions": [{"type": "tts_say", "text": "Ok, one moment..."}],
|
||||
"pre_actions": [
|
||||
{"type": "tts_say", "text": "Ok, let me help you with your sushi order..."}
|
||||
],
|
||||
},
|
||||
"end": {
|
||||
"message": {
|
||||
"role": "system",
|
||||
"content": "The order is complete. Thank the user and end the conversation.",
|
||||
},
|
||||
"functions": [],
|
||||
"pre_actions": [{"type": "tts_say", "text": "Thank you for your order! Goodbye!"}],
|
||||
"post_actions": [{"type": "end_conversation"}],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ from typing import Callable, Dict, List, Optional
|
||||
from loguru import logger
|
||||
|
||||
from pipecat.frames.frames import (
|
||||
EndFrame,
|
||||
LLMMessagesAppendFrame,
|
||||
LLMMessagesUpdateFrame,
|
||||
LLMSetToolsFrame,
|
||||
@@ -51,6 +52,7 @@ class FlowManager:
|
||||
|
||||
# Register built-in actions
|
||||
self.register_action("tts_say", self._handle_tts_action)
|
||||
self.register_action("end_conversation", self._handle_end_action)
|
||||
|
||||
async def initialize(self, initial_messages: List[dict]):
|
||||
"""Initialize the flow with starting messages and functions.
|
||||
@@ -137,14 +139,37 @@ class FlowManager:
|
||||
logger.warning(f"No handler registered for action type: {action_type}")
|
||||
|
||||
async def _handle_tts_action(self, action: dict):
|
||||
"""Built-in handler for TTS actions"""
|
||||
"""Built-in handler for TTS actions that speak immediately.
|
||||
|
||||
This handler attempts to use the TTS service directly to speak the text
|
||||
immediately, bypassing the pipeline queue. If no TTS service is available,
|
||||
it falls back to queueing the text through the pipeline.
|
||||
|
||||
Args:
|
||||
action: Dictionary containing the action configuration.
|
||||
Must include a 'text' key with the text to speak.
|
||||
"""
|
||||
if self.tts:
|
||||
# Direct call to TTS service to speak text
|
||||
# Direct call to TTS service to speak text immediately
|
||||
await self.tts.say(action["text"])
|
||||
else:
|
||||
# Fall back to queued TTS if no direct service available
|
||||
await self.task.queue_frame(TTSSpeakFrame(text=action["text"]))
|
||||
|
||||
async def _handle_end_action(self, action: dict):
|
||||
"""Built-in handler for ending the conversation.
|
||||
|
||||
This handler queues an EndFrame to terminate the conversation. If the action
|
||||
includes a 'text' key, it will queue that text to be spoken before ending.
|
||||
|
||||
Args:
|
||||
action: Dictionary containing the action configuration.
|
||||
Optional 'text' key for a goodbye message.
|
||||
"""
|
||||
if action.get("text"): # Optional goodbye message
|
||||
await self.task.queue_frame(TTSSpeakFrame(text=action["text"]))
|
||||
await self.task.queue_frame(EndFrame())
|
||||
|
||||
async def handle_transition(self, function_name: str):
|
||||
"""Handle node transition triggered by a function call.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user