From f599e160de2d18967ea2d0c80b618224a3c4a00c Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Mon, 25 Nov 2024 13:42:08 -0500 Subject: [PATCH] Make Pipecat Flows an independent package --- CHANGELOG.md | 6 - README.md | 3 + examples/foundational/25-conversation-flow.py | 266 ------------------ pyproject.toml | 1 - 4 files changed, 3 insertions(+), 273 deletions(-) delete mode 100644 examples/foundational/25-conversation-flow.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 45ee6fa2b..e4d4986db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,12 +12,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added a new RTVI message called `disconnect-bot`, which when handled pushes an `EndFrame` to trigger the pipeline to stop. -- Added support for the new Pipecat Flows package (pipecat-ai-flows). Learn - more at: https://github.com/pipecat-ai/pipecat-flows. - -- Added foundational example `25-conversation-flow.py` showing how to use - Pipecat Flows. - ### Changed - Expanded the transcriptions.language module to support a superset of diff --git a/README.md b/README.md index 04d1da3ea..cb12f4086 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ Pipecat is an open source Python framework for building voice and multimodal con - **Multimodal Apps**: Combine voice, video, images, and text - **Creative Tools**: [Story-telling experiences](https://storytelling-chatbot.fly.dev/) and social companions - **Business Solutions**: [Customer intake flows](https://www.youtube.com/watch?v=lDevgsp9vn0) and support bots +- **Complex conversational flows**: [Refer to Pipecat Flows](https://github.com/pipecat-ai/pipecat-flows) to learn more ## See it in action @@ -32,6 +33,8 @@ Pipecat is an open source Python framework for building voice and multimodal con - **Real-time Processing**: Frame-based pipeline architecture for fluid interactions - **Production Ready**: Enterprise-grade WebRTC and Websocket support +๐Ÿ’ก Looking to build structured conversations? Check out [Pipecat Flows](https://github.com/pipecat-ai/pipecat-flows) for managing complex conversational states and transitions. + ## Getting started You can get started with Pipecat running on your local machine, then move your agent processes to the cloud when youโ€™re ready. You can also add a ๐Ÿ“ž telephone number, ๐Ÿ–ผ๏ธ image output, ๐Ÿ“บ video input, use different LLMs, and more. diff --git a/examples/foundational/25-conversation-flow.py b/examples/foundational/25-conversation-flow.py deleted file mode 100644 index 2b43deb98..000000000 --- a/examples/foundational/25-conversation-flow.py +++ /dev/null @@ -1,266 +0,0 @@ -# -# 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 -# -# 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": { - "start": { - "messages": [ - { - "role": "system", - "content": "You are an order-taking assistant. You must ALWAYS use one of the available functions to progress the conversation. For this step, ask the user if they want pizza or sushi, and wait for them to use a function to choose. Start off by greeting them. Be friendly and casual; you're taking an order for food over the phone.", - } - ], - "functions": [ - { - "type": "function", - "function": { - "name": "choose_pizza", - "description": "User wants to order pizza", - "parameters": {"type": "object", "properties": {}}, - }, - }, - { - "type": "function", - "function": { - "name": "choose_sushi", - "description": "User wants to order sushi", - "parameters": {"type": "object", "properties": {}}, - }, - }, - ], - }, - "choose_pizza": { - "messages": [ - { - "role": "system", - "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. - - Start off by acknowledging the user's choice. Once they've chosen a size, ask if they'd like anything else. - Remember to be friendly and casual.""", - } - ], - "functions": [ - { - "type": "function", - "function": { - "name": "select_pizza_size", - "description": "Record the selected pizza size", - "parameters": { - "type": "object", - "properties": { - "size": { - "type": "string", - "enum": ["small", "medium", "large"], - "description": "Size of the pizza", - } - }, - "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..."} - ], - }, - "choose_sushi": { - "messages": [ - { - "role": "system", - "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. - - Start off by acknowledging the user's choice. Once they've chosen a size, ask if they'd like anything else. - Remember to be friendly and casual.""", - } - ], - "functions": [ - { - "type": "function", - "function": { - "name": "select_roll_count", - "description": "Record the number of sushi rolls", - "parameters": { - "type": "object", - "properties": { - "count": { - "type": "integer", - "minimum": 1, - "maximum": 10, - "description": "Number of rolls to order", - } - }, - "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, let me help you with your sushi order..."} - ], - }, - "end": { - "messages": [ - { - "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"}], - }, - }, -} - - -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-4") - - # Get initial tools from the first node - initial_tools = flow_config["nodes"]["start"]["functions"] - - # Create initial context - messages = [ - { - "role": "system", - "content": "You are an order-taking assistant. You must ALWAYS use the available functions to progress the conversation. Never assume an order is complete without the proper function calls. Your responses will be converted to audio so avoid special characters.", - } - ] - - 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()) diff --git a/pyproject.toml b/pyproject.toml index a362f6aad..d4ffee4ef 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -51,7 +51,6 @@ gladia = [ "websockets~=13.1" ] google = [ "google-generativeai~=0.8.3", "google-cloud-texttospeech~=2.17.2" ] gstreamer = [ "pygobject~=3.48.2" ] fireworks = [ "openai~=1.37.2" ] -flows = [ "pipecat-ai-flows~=0.0.2" ] krisp = [ "pipecat-ai-krisp~=0.3.0" ] langchain = [ "langchain~=0.2.14", "langchain-community~=0.2.12", "langchain-openai~=0.1.20" ] livekit = [ "livekit~=0.17.5", "livekit-api~=0.7.1", "tenacity~=8.5.0" ]