# # 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())