Files
pipecat/src/samples/static-sprite/sprite-sample.py
2023-12-30 08:45:25 -05:00

175 lines
5.8 KiB
Python

import argparse
from email.mime import image
import logging
import os
import random
import requests
import time
import urllib.parse
from PIL import Image
from dailyai.async_processor.async_processor import (
ConversationProcessorCollection,
LLMResponse,
OrchestratorResponse
)
from dailyai.orchestrator import OrchestratorConfig, Orchestrator
from dailyai.output_queue import OutputQueueFrame, FrameType
from dailyai.message_handler.message_handler import MessageHandler
from dailyai.services.ai_services import AIServiceConfig
from dailyai.services.azure_ai_services import AzureImageGenService, AzureTTSService, AzureLLMService
class StaticSpriteResponse(OrchestratorResponse):
def __init__(
self,
services,
message_handler,
output_queue
) -> None:
super().__init__(services, message_handler, output_queue)
self.image_bytes:bytes | None = None
self.filenames = None # override this in subclasses
def start_preparation(self) -> None:
full_path = os.path.join(os.path.dirname(__file__), "sprites/", self.filename)
print(full_path)
with Image.open(full_path) as img:
self.image_bytes = img.tobytes()
def do_play(self) -> None:
self.output_queue.put(OutputQueueFrame(FrameType.IMAGE_FRAME, self.image_bytes))
class IntroSpriteResponse(StaticSpriteResponse):
def __init__(self, services, message_handler, output_queue) -> None:
super().__init__(services, message_handler, output_queue)
self.filename = "intro.png"
class WaitingSpriteResponse(StaticSpriteResponse):
def __init__(self, services, message_handler, output_queue) -> None:
super().__init__(services, message_handler, output_queue)
self.filename = "waiting.png"
class AnimatedSpriteLLMResponse(LLMResponse):
def __init__(self, services, message_handler, output_queue) -> None:
super().__init__(services, message_handler, output_queue)
self.filenames = ["talk-1.png", "talk-2.png"]
self.image_bytes = []
def start_preparation(self) -> None:
for filename in self.filenames:
full_path = os.path.join(os.path.dirname(__file__), "sprites/", filename)
print(full_path)
with Image.open(full_path) as img:
self.image_bytes.append(img.tobytes())
def get_frames_from_tts_response(self, audio_frame) -> list[OutputQueueFrame]:
return [
OutputQueueFrame(FrameType.AUDIO_FRAME, audio_frame),
random.choice(self.image_bytes)
]
def add_bot_to_room(room_url, token, expiration) -> None:
# A simple prompt for a simple sample.
message_handler = MessageHandler(
"""
You are a sample bot, meant to demonstrate how to use an LLM with transcription at TTS.
Answer user's questions and be friendly, and if you can, give some ideas about how someone
could use a bot like you in a more in-depth way. Because your responses will be spoken,
try to keep them short and sweet.
"""
)
# Use Azure services for the TTS, image generation, and LLM.
# Note that you'll need to set the following environment variables:
# - AZURE_SPEECH_SERVICE_KEY
# - AZURE_SPEECH_SERVICE_REGION
# - AZURE_CHATGPT_KEY
# - AZURE_CHATGPT_ENDPOINT
# - AZURE_CHATGPT_DEPLOYMENT_ID
#
# This demo doesn't use image generation, but if you extend it to do so,
# you'll also need to set:
# - AZURE_DALLE_KEY
# - AZURE_DALLE_ENDPOINT
# - AZURE_DALLE_DEPLOYMENT_ID
services = AIServiceConfig(
tts=AzureTTSService(), image=AzureImageGenService(), llm=AzureLLMService()
)
sprite_conversation_processors = ConversationProcessorCollection(
introduction=IntroSpriteResponse,
waiting=WaitingSpriteResponse,
response=AnimatedSpriteLLMResponse,
)
orchestrator_config = OrchestratorConfig(
room_url=room_url,
token=token,
bot_name="Simple Bot",
expiration=expiration,
)
FORMAT = f"%(levelno)s %(asctime)s %(message)s"
# Remove any handlers added by imported modules so we can use our formatting
while logging.root.handlers:
logging.root.removeHandler(logging.root.handlers[-1])
logging.basicConfig(format=FORMAT)
logger: logging.Logger = logging.getLogger("dailyai")
logger.setLevel(logging.DEBUG)
logger.info("hello world")
orchestrator = Orchestrator(
orchestrator_config,
services,
message_handler,
sprite_conversation_processors
)
orchestrator.start()
# When the orchestrator's done, we need to shut it down,
# and the various services and handlers we've created.
orchestrator.stop()
message_handler.shutdown()
services.tts.close()
services.image.close()
services.llm.close()
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Simple Daily Bot Sample")
parser.add_argument("-u", "--url", type=str, required=True, help="URL of the Daily room")
parser.add_argument(
"-k", "--apikey", type=str, required=True, help="Daily API Key (needed to create token)"
)
args: argparse.Namespace = parser.parse_args()
# Create a meeting token for the given room with an expiration 1 hour in the future.
room_name: str = urllib.parse.urlparse(args.url).path[1:]
expiration: float = time.time() + 60 * 60
res: requests.Response = requests.post(
f"https://api.daily.co/v1/meeting-tokens",
headers={"Authorization": f"Bearer {args.apikey}"},
json={
"properties": {"room_name": room_name, "is_owner": True, "exp": expiration}
},
)
if res.status_code != 200:
raise Exception(f'Failed to create meeting token: {res.status_code} {res.text}')
token: str = res.json()['token']
add_bot_to_room(args.url, token, expiration)