Files
pipecat/examples/foundational/25c-flow-travel-planner.py
2024-11-23 07:57:23 -05:00

397 lines
16 KiB
Python

#
# Copyright (c) 2024, Daily
#
# SPDX-License-Identifier: BSD 2-Clause License
#
import asyncio
import os
import sys
import aiohttp
from dotenv import load_dotenv
from loguru import logger
from pipecat_flows import FlowManager
from runner import configure
from pipecat.audio.vad.silero import SileroVADAnalyzer
from pipecat.pipeline.pipeline import Pipeline
from pipecat.pipeline.runner import PipelineRunner
from pipecat.pipeline.task import PipelineParams, PipelineTask
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
from pipecat.services.deepgram import DeepgramSTTService, DeepgramTTSService
from pipecat.services.openai import OpenAILLMService
from pipecat.transports.services.daily import DailyParams, DailyTransport
load_dotenv(override=True)
logger.remove(0)
logger.add(sys.stderr, level="DEBUG")
# Flow Configuration - Travel Planner
#
# This configuration defines a vacation planning system with the following states:
#
# 1. start
# - Initial state where user chooses between beach or mountain vacation
# - Functions: choose_beach, choose_mountain
# - Pre-action: Welcome message
# - Transitions to: choose_beach or choose_mountain
#
# 2. choose_beach/choose_mountain
# - Handles destination selection for chosen vacation type
# - Functions:
# * select_destination (terminal function with location-specific options)
# * get_dates (transitions to date selection)
# - Pre-action: Destination-specific welcome message
#
# 3. get_dates
# - Handles travel date selection
# - Functions:
# * record_dates (terminal function, can be modified)
# * get_activities (transitions to activity selection)
#
# 4. get_activities
# - Handles activity preference selection
# - Functions:
# * record_activities (terminal function, array-based selection)
# * verify_itinerary (transitions to verification)
#
# 5. verify_itinerary
# - Reviews complete vacation plan
# - Functions:
# * revise_plan (loops back to get_dates)
# * confirm_booking (transitions to confirmation)
#
# 6. confirm_booking
# - Handles final confirmation and tips
# - Functions: end
# - Pre-action: Confirmation message
#
# 7. end
# - Final state that closes the conversation
# - No functions available
# - Post-action: Ends conversation
flow_config = {
"initial_node": "start",
"nodes": {
"start": {
"messages": [
{
"role": "system",
"content": "For this step, ask if they're interested in planning a beach vacation or a mountain retreat, and wait for them to choose. Start with an enthusiastic greeting and be conversational; you're helping them plan their dream vacation.",
}
],
"functions": [
{
"type": "function",
"function": {
"name": "choose_beach",
"description": "User wants to plan a beach vacation",
"parameters": {"type": "object", "properties": {}},
},
},
{
"type": "function",
"function": {
"name": "choose_mountain",
"description": "User wants to plan a mountain retreat",
"parameters": {"type": "object", "properties": {}},
},
},
],
"pre_actions": [
{
"type": "tts_say",
"text": "Welcome to Dream Vacations! I'll help you plan your perfect getaway.",
}
],
},
"choose_beach": {
"messages": [
{
"role": "system",
"content": "You are handling beach vacation planning. Use the available functions:\n - Use select_destination when the user chooses their preferred beach location\n - Use get_dates once they've selected a destination\n\nAvailable beach destinations are: 'Maui', 'Cancun', or 'Maldives'. After they choose, confirm their selection and proceed to dates. Be enthusiastic and paint a picture of each destination.",
}
],
"functions": [
{
"type": "function",
"function": {
"name": "select_destination",
"description": "Record the selected beach destination",
"parameters": {
"type": "object",
"properties": {
"destination": {
"type": "string",
"enum": ["Maui", "Cancun", "Maldives"],
"description": "Selected beach destination",
}
},
"required": ["destination"],
},
},
},
{
"type": "function",
"function": {
"name": "get_dates",
"description": "Proceed to date selection",
"parameters": {"type": "object", "properties": {}},
},
},
],
"pre_actions": [
{"type": "tts_say", "text": "Let's find your perfect beach paradise..."}
],
},
"choose_mountain": {
"messages": [
{
"role": "system",
"content": "You are handling mountain retreat planning. Use the available functions:\n - Use select_destination when the user chooses their preferred mountain location\n - Use get_dates once they've selected a destination\n\nAvailable mountain destinations are: 'Swiss Alps', 'Rocky Mountains', or 'Himalayas'. After they choose, confirm their selection and proceed to dates. Be enthusiastic and paint a picture of each destination.",
}
],
"functions": [
{
"type": "function",
"function": {
"name": "select_destination",
"description": "Record the selected mountain destination",
"parameters": {
"type": "object",
"properties": {
"destination": {
"type": "string",
"enum": ["Swiss Alps", "Rocky Mountains", "Himalayas"],
"description": "Selected mountain destination",
}
},
"required": ["destination"],
},
},
},
{
"type": "function",
"function": {
"name": "get_dates",
"description": "Proceed to date selection",
"parameters": {"type": "object", "properties": {}},
},
},
],
"pre_actions": [
{"type": "tts_say", "text": "Let's find your perfect mountain getaway..."}
],
},
"get_dates": {
"messages": [
{
"role": "system",
"content": "Handle travel date selection. Use the available functions:\n - Use record_dates when the user specifies their travel dates (can be used multiple times if they change their mind)\n - Use get_activities once dates are confirmed\n\nAsk for their preferred travel dates within the next 6 months. After recording dates, confirm the selection and proceed to activities.",
}
],
"functions": [
{
"type": "function",
"function": {
"name": "record_dates",
"description": "Record the selected travel dates",
"parameters": {
"type": "object",
"properties": {
"check_in": {
"type": "string",
"format": "date",
"description": "Check-in date (YYYY-MM-DD)",
},
"check_out": {
"type": "string",
"format": "date",
"description": "Check-out date (YYYY-MM-DD)",
},
},
"required": ["check_in", "check_out"],
},
},
},
{
"type": "function",
"function": {
"name": "get_activities",
"description": "Proceed to activity selection",
"parameters": {"type": "object", "properties": {}},
},
},
],
},
"get_activities": {
"messages": [
{
"role": "system",
"content": "Handle activity preferences. Use the available functions:\n - Use record_activities to save their activity preferences\n - Use verify_itinerary once activities are selected\n\nFor beach destinations, suggest: snorkeling, surfing, sunset cruise\nFor mountain destinations, suggest: hiking, skiing, mountain biking\n\nAfter they choose, confirm their selections and proceed to verification.",
}
],
"functions": [
{
"type": "function",
"function": {
"name": "record_activities",
"description": "Record selected activities",
"parameters": {
"type": "object",
"properties": {
"activities": {
"type": "array",
"items": {"type": "string"},
"minItems": 1,
"maxItems": 3,
"description": "Selected activities",
}
},
"required": ["activities"],
},
},
},
{
"type": "function",
"function": {
"name": "verify_itinerary",
"description": "Proceed to itinerary verification",
"parameters": {"type": "object", "properties": {}},
},
},
],
},
"verify_itinerary": {
"messages": [
{
"role": "system",
"content": "Review the complete itinerary with the user. Summarize their destination, dates, and chosen activities. Use the available functions:\n - Use get_dates if they want to make changes\n - Use confirm_booking if they're happy with everything\n\nBe thorough in reviewing all details and ask for their confirmation.",
}
],
"functions": [
{
"type": "function",
"function": {
"name": "get_dates",
"description": "Return to date selection to revise the plan",
"parameters": {"type": "object", "properties": {}},
},
},
{
"type": "function",
"function": {
"name": "confirm_booking",
"description": "Confirm the booking and proceed to end",
"parameters": {"type": "object", "properties": {}},
},
},
],
},
"confirm_booking": {
"messages": [
{
"role": "system",
"content": "The booking is confirmed. Share some relevant tips about their chosen destination, thank them warmly, and use end to complete the conversation.",
}
],
"functions": [
{
"type": "function",
"function": {
"name": "end",
"description": "End the conversation",
"parameters": {"type": "object", "properties": {}},
},
}
],
"pre_actions": [
{"type": "tts_say", "text": "Fantastic! Your dream vacation is confirmed!"}
],
},
"end": {
"messages": [
{
"role": "system",
"content": "Wish them a wonderful trip and end the conversation.",
}
],
"functions": [],
"post_actions": [{"type": "end_conversation"}],
},
},
}
async def main():
async with aiohttp.ClientSession() as session:
(room_url, _) = await configure(session)
transport = DailyTransport(
room_url,
None,
"Respond bot",
DailyParams(
audio_out_enabled=True,
vad_enabled=True,
vad_analyzer=SileroVADAnalyzer(),
vad_audio_passthrough=True,
),
)
stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
tts = DeepgramTTSService(api_key=os.getenv("DEEPGRAM_API_KEY"), voice="aura-helios-en")
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o")
# Get initial tools from the first node
initial_tools = flow_config["nodes"]["start"]["functions"]
# Create initial context
messages = [
{
"role": "system",
"content": "You are a travel planning assistant. You must ALWAYS use one of the available functions to progress the conversation. This is a phone conversations and your responses will be converted to audio. Avoid outputting special characters and emojis.",
}
]
context = OpenAILLMContext(messages, initial_tools)
context_aggregator = llm.create_context_aggregator(context)
pipeline = Pipeline(
[
transport.input(), # Transport user input
stt, # STT
context_aggregator.user(), # User responses
llm, # LLM
tts, # TTS
transport.output(), # Transport bot output
context_aggregator.assistant(), # Assistant spoken responses
]
)
task = PipelineTask(pipeline, PipelineParams(allow_interruptions=True))
# Initialize flow manager
flow_manager = FlowManager(flow_config, task, tts)
# Register functions with LLM service
await flow_manager.register_functions(llm)
@transport.event_handler("on_first_participant_joined")
async def on_first_participant_joined(transport, participant):
await transport.capture_participant_transcription(participant["id"])
# Initialize the flow processor
await flow_manager.initialize(messages)
# Kick off the conversation using the context aggregator
await task.queue_frames([context_aggregator.user().get_context_frame()])
runner = PipelineRunner()
await runner.run(task)
if __name__ == "__main__":
asyncio.run(main())