Add end session handler and update example
This commit is contained in:
@@ -28,7 +28,35 @@ load_dotenv(override=True)
|
|||||||
logger.remove(0)
|
logger.remove(0)
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
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 = {
|
flow_config = {
|
||||||
"initial_node": "start",
|
"initial_node": "start",
|
||||||
"nodes": {
|
"nodes": {
|
||||||
@@ -59,14 +87,19 @@ flow_config = {
|
|||||||
"choose_pizza": {
|
"choose_pizza": {
|
||||||
"message": {
|
"message": {
|
||||||
"role": "system",
|
"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": [
|
"functions": [
|
||||||
{
|
{
|
||||||
"type": "function",
|
"type": "function",
|
||||||
"function": {
|
"function": {
|
||||||
"name": "select_pizza_size",
|
"name": "select_pizza_size",
|
||||||
"description": "Select pizza size",
|
"description": "Record the selected pizza size",
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
@@ -79,21 +112,36 @@ flow_config = {
|
|||||||
"required": ["size"],
|
"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": {
|
"choose_sushi": {
|
||||||
"message": {
|
"message": {
|
||||||
"role": "system",
|
"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": [
|
"functions": [
|
||||||
{
|
{
|
||||||
"type": "function",
|
"type": "function",
|
||||||
"function": {
|
"function": {
|
||||||
"name": "select_roll_count",
|
"name": "select_roll_count",
|
||||||
"description": "Select number of sushi rolls",
|
"description": "Record the number of sushi rolls",
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
@@ -107,9 +155,28 @@ flow_config = {
|
|||||||
"required": ["count"],
|
"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 loguru import logger
|
||||||
|
|
||||||
from pipecat.frames.frames import (
|
from pipecat.frames.frames import (
|
||||||
|
EndFrame,
|
||||||
LLMMessagesAppendFrame,
|
LLMMessagesAppendFrame,
|
||||||
LLMMessagesUpdateFrame,
|
LLMMessagesUpdateFrame,
|
||||||
LLMSetToolsFrame,
|
LLMSetToolsFrame,
|
||||||
@@ -51,6 +52,7 @@ class FlowManager:
|
|||||||
|
|
||||||
# Register built-in actions
|
# Register built-in actions
|
||||||
self.register_action("tts_say", self._handle_tts_action)
|
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]):
|
async def initialize(self, initial_messages: List[dict]):
|
||||||
"""Initialize the flow with starting messages and functions.
|
"""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}")
|
logger.warning(f"No handler registered for action type: {action_type}")
|
||||||
|
|
||||||
async def _handle_tts_action(self, action: dict):
|
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:
|
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"])
|
await self.tts.say(action["text"])
|
||||||
else:
|
else:
|
||||||
# Fall back to queued TTS if no direct service available
|
# Fall back to queued TTS if no direct service available
|
||||||
await self.task.queue_frame(TTSSpeakFrame(text=action["text"]))
|
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):
|
async def handle_transition(self, function_name: str):
|
||||||
"""Handle node transition triggered by a function call.
|
"""Handle node transition triggered by a function call.
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user