From 8fad3ccceb00fb7cbf0fe78cccf7c8950bcab834 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Wed, 3 Apr 2024 15:09:12 -0700 Subject: [PATCH] use conditional imports and show help errors if modules not found --- examples/starter-apps/patient-intake.py | 2 +- src/dailyai/pipeline/frames.py | 11 --------- .../pipeline/opeanai_llm_aggregator.py | 10 ++++++-- src/dailyai/pipeline/openai_frames.py | 12 ++++++++++ src/dailyai/services/anthropic_llm_service.py | 9 +++++++- src/dailyai/services/azure_ai_services.py | 18 ++++++++++----- src/dailyai/services/fal_ai_services.py | 9 ++++++-- src/dailyai/services/open_ai_services.py | 10 +++++++- .../services/openai_api_llm_service.py | 21 +++++++++++------ src/dailyai/services/openai_llm_context.py | 19 ++++++++++----- src/dailyai/services/playht_ai_service.py | 13 ++++++++--- src/dailyai/services/whisper_ai_services.py | 10 +++++++- src/dailyai/transports/daily_transport.py | 23 ++++++++++++------- src/dailyai/transports/local_transport.py | 15 ++++++------ src/dailyai/transports/threaded_transport.py | 1 - src/dailyai/transports/websocket_transport.py | 9 +++++++- tests/integration/integration_azure_llm.py | 4 +--- tests/integration/integration_ollama_llm.py | 4 +--- tests/integration/integration_openai_llm.py | 4 +--- 19 files changed, 137 insertions(+), 67 deletions(-) create mode 100644 src/dailyai/pipeline/openai_frames.py diff --git a/examples/starter-apps/patient-intake.py b/examples/starter-apps/patient-intake.py index 9409d894a..553aacde5 100644 --- a/examples/starter-apps/patient-intake.py +++ b/examples/starter-apps/patient-intake.py @@ -19,12 +19,12 @@ # from dailyai.services.deepgram_ai_services import DeepgramTTSService from dailyai.services.elevenlabs_ai_service import ElevenLabsTTSService from dailyai.pipeline.frames import ( - OpenAILLMContextFrame, Frame, LLMFunctionCallFrame, LLMFunctionStartFrame, AudioFrame, ) +from dailyai.pipeline.openai_frames import OpenAILLMContextFrame from dailyai.services.ai_services import FrameLogger, AIService from openai._types import NotGiven, NOT_GIVEN diff --git a/src/dailyai/pipeline/frames.py b/src/dailyai/pipeline/frames.py index 658f3c357..4942747b8 100644 --- a/src/dailyai/pipeline/frames.py +++ b/src/dailyai/pipeline/frames.py @@ -1,9 +1,6 @@ from dataclasses import dataclass from typing import Any, List -from dailyai.services.openai_llm_context import OpenAILLMContext -import dailyai.pipeline.protobufs.frames_pb2 as frame_protos - class Frame: def __str__(self): @@ -135,14 +132,6 @@ class LLMMessagesFrame(Frame): messages: List[dict] -@dataclass() -class OpenAILLMContextFrame(Frame): - """Like an LLMMessagesFrame, but with extra context specific to the - OpenAI API. The context in this message is also mutable, and will be - changed by the OpenAIContextAggregator frame processor.""" - context: OpenAILLMContext - - @dataclass() class ReceivedAppMessageFrame(Frame): message: Any diff --git a/src/dailyai/pipeline/opeanai_llm_aggregator.py b/src/dailyai/pipeline/opeanai_llm_aggregator.py index 38c0f4566..b4b254087 100644 --- a/src/dailyai/pipeline/opeanai_llm_aggregator.py +++ b/src/dailyai/pipeline/opeanai_llm_aggregator.py @@ -4,15 +4,21 @@ Frame, LLMResponseEndFrame, LLMResponseStartFrame, - OpenAILLMContextFrame, TextFrame, TranscriptionFrame, UserStartedSpeakingFrame, UserStoppedSpeakingFrame, ) +from dailyai.pipeline.openai_frames import OpenAILLMContextFrame from dailyai.services.openai_llm_context import OpenAILLMContext -from openai.types.chat import ChatCompletionRole +try: + from openai.types.chat import ChatCompletionRole +except ModuleNotFoundError as e: + print(f"Exception: {e}") + print( + "In order to use OpenAI, you need to `pip install dailyai[openai]`. Also, set `OPENAI_API_KEY` environment variable.") + raise Exception(f"Missing module: {e}") class OpenAIContextAggregator(FrameProcessor): diff --git a/src/dailyai/pipeline/openai_frames.py b/src/dailyai/pipeline/openai_frames.py new file mode 100644 index 000000000..2a14c670e --- /dev/null +++ b/src/dailyai/pipeline/openai_frames.py @@ -0,0 +1,12 @@ +from dataclasses import dataclass + +from dailyai.pipeline.frames import Frame +from dailyai.services.openai_llm_context import OpenAILLMContext + + +@dataclass() +class OpenAILLMContextFrame(Frame): + """Like an LLMMessagesFrame, but with extra context specific to the + OpenAI API. The context in this message is also mutable, and will be + changed by the OpenAIContextAggregator frame processor.""" + context: OpenAILLMContext diff --git a/src/dailyai/services/anthropic_llm_service.py b/src/dailyai/services/anthropic_llm_service.py index ea48950e7..44c045992 100644 --- a/src/dailyai/services/anthropic_llm_service.py +++ b/src/dailyai/services/anthropic_llm_service.py @@ -1,9 +1,16 @@ from typing import AsyncGenerator -from anthropic import AsyncAnthropic from dailyai.pipeline.frames import Frame, LLMMessagesFrame, TextFrame from dailyai.services.ai_services import LLMService +try: + from anthropic import AsyncAnthropic +except ModuleNotFoundError as e: + print(f"Exception: {e}") + print( + "In order to use Anthropic, you need to `pip install dailyai[anthropic]`. Also, set `ANTHROPIC_API_KEY` environment variable.") + raise Exception(f"Missing module: {e}") + class AnthropicLLMService(LLMService): diff --git a/src/dailyai/services/azure_ai_services.py b/src/dailyai/services/azure_ai_services.py index b6ca1e4fd..0f0151144 100644 --- a/src/dailyai/services/azure_ai_services.py +++ b/src/dailyai/services/azure_ai_services.py @@ -9,12 +9,18 @@ from PIL import Image # See .env.example for Azure configuration needed -from azure.cognitiveservices.speech import ( - SpeechSynthesizer, - SpeechConfig, - ResultReason, - CancellationReason, -) +try: + from azure.cognitiveservices.speech import ( + SpeechSynthesizer, + SpeechConfig, + ResultReason, + CancellationReason, + ) +except ModuleNotFoundError as e: + print(f"Exception: {e}") + print( + "In order to use Azure TTS, you need to `pip install dailyai[azure]`. Also, set `SPEECH_KEY` and `SPEECH_REGION` environment variables.") + raise Exception(f"Missing module: {e}") from dailyai.services.openai_api_llm_service import BaseOpenAILLMService diff --git a/src/dailyai/services/fal_ai_services.py b/src/dailyai/services/fal_ai_services.py index 10343f97c..9130b062b 100644 --- a/src/dailyai/services/fal_ai_services.py +++ b/src/dailyai/services/fal_ai_services.py @@ -1,4 +1,3 @@ -import fal import aiohttp import asyncio import io @@ -10,7 +9,13 @@ from dailyai.services.ai_services import ImageGenService -# Fal expects FAL_KEY_ID and FAL_KEY_SECRET to be set in the env +try: + import fal +except ModuleNotFoundError as e: + print(f"Exception: {e}") + print( + "In order to use Fal, you need to `pip install dailyai[fal]`. Also, set `FAL_KEY_ID` and `FAL_KEY_SECRET` environment variables.") + raise Exception(f"Missing module: {e}") class FalImageGenService(ImageGenService): diff --git a/src/dailyai/services/open_ai_services.py b/src/dailyai/services/open_ai_services.py index 61963769a..9d177f8b7 100644 --- a/src/dailyai/services/open_ai_services.py +++ b/src/dailyai/services/open_ai_services.py @@ -1,12 +1,20 @@ import aiohttp from PIL import Image import io -from openai import AsyncOpenAI from dailyai.services.ai_services import ImageGenService from dailyai.services.openai_api_llm_service import BaseOpenAILLMService +try: + from openai import AsyncOpenAI +except ModuleNotFoundError as e: + print(f"Exception: {e}") + print( + "In order to use OpenAI, you need to `pip install dailyai[openai]`. Also, set `OPENAI_API_KEY` environment variable.") + raise Exception(f"Missing module: {e}") + + class OpenAILLMService(BaseOpenAILLMService): def __init__(self, model="gpt-4", * args, **kwargs): diff --git a/src/dailyai/services/openai_api_llm_service.py b/src/dailyai/services/openai_api_llm_service.py index d36e878e2..2ddfc7796 100644 --- a/src/dailyai/services/openai_api_llm_service.py +++ b/src/dailyai/services/openai_api_llm_service.py @@ -1,7 +1,6 @@ import json import time from typing import AsyncGenerator, List -from openai import AsyncOpenAI, AsyncStream from dailyai.pipeline.frames import ( Frame, LLMFunctionCallFrame, @@ -9,17 +8,25 @@ LLMMessagesFrame, LLMResponseEndFrame, LLMResponseStartFrame, - OpenAILLMContextFrame, TextFrame, ) from dailyai.services.ai_services import LLMService +from dailyai.pipeline.openai_frames import OpenAILLMContextFrame from dailyai.services.openai_llm_context import OpenAILLMContext -from openai.types.chat import ( - ChatCompletion, - ChatCompletionChunk, - ChatCompletionMessageParam, -) +try: + from openai import AsyncOpenAI, AsyncStream + + from openai.types.chat import ( + ChatCompletion, + ChatCompletionChunk, + ChatCompletionMessageParam, + ) +except ModuleNotFoundError as e: + print(f"Exception: {e}") + print( + "In order to use OpenAI, you need to `pip install dailyai[openai]`. Also, set `OPENAI_API_KEY` environment variable.") + raise Exception(f"Missing module: {e}") class BaseOpenAILLMService(LLMService): diff --git a/src/dailyai/services/openai_llm_context.py b/src/dailyai/services/openai_llm_context.py index ce705f547..2d16c3cb6 100644 --- a/src/dailyai/services/openai_llm_context.py +++ b/src/dailyai/services/openai_llm_context.py @@ -1,11 +1,18 @@ from typing import List -from openai._types import NOT_GIVEN, NotGiven -from openai.types.chat import ( - ChatCompletionToolParam, - ChatCompletionToolChoiceOptionParam, - ChatCompletionMessageParam, -) +try: + from openai._types import NOT_GIVEN, NotGiven + + from openai.types.chat import ( + ChatCompletionToolParam, + ChatCompletionToolChoiceOptionParam, + ChatCompletionMessageParam, + ) +except ModuleNotFoundError as e: + print(f"Exception: {e}") + print( + "In order to use OpenAI, you need to `pip install dailyai[openai]`. Also, set `OPENAI_API_KEY` environment variable.") + raise Exception(f"Missing module: {e}") class OpenAILLMContext: diff --git a/src/dailyai/services/playht_ai_service.py b/src/dailyai/services/playht_ai_service.py index 350e5cb88..291855264 100644 --- a/src/dailyai/services/playht_ai_service.py +++ b/src/dailyai/services/playht_ai_service.py @@ -1,11 +1,18 @@ import io import struct -from pyht import Client -from pyht.client import TTSOptions -from pyht.protos.api_pb2 import Format from dailyai.services.ai_services import TTSService +try: + from pyht import Client + from pyht.client import TTSOptions + from pyht.protos.api_pb2 import Format +except ModuleNotFoundError as e: + print(f"Exception: {e}") + print( + "In order to use PlayHT, you need to `pip install dailyai[playht]`. Also, set `PLAY_HT_USER_ID` and `PLAY_HT_API_KEY` environment variables.") + raise Exception(f"Missing module: {e}") + class PlayHTAIService(TTSService): diff --git a/src/dailyai/services/whisper_ai_services.py b/src/dailyai/services/whisper_ai_services.py index cc657e6cf..ddddb4f98 100644 --- a/src/dailyai/services/whisper_ai_services.py +++ b/src/dailyai/services/whisper_ai_services.py @@ -3,10 +3,18 @@ from enum import Enum import logging from typing import BinaryIO -from faster_whisper import WhisperModel from dailyai.services.local_stt_service import LocalSTTService +try: + from faster_whisper import WhisperModel +except ModuleNotFoundError as e: + print(f"Exception: {e}") + print( + "In order to use Whisper, you need to `pip install dailyai[whisper]`.") + raise Exception(f"Missing module: {e}") + + class Model(Enum): """Class of basic Whisper model selection options""" TINY = "tiny" diff --git a/src/dailyai/transports/daily_transport.py b/src/dailyai/transports/daily_transport.py index d510cadbb..66798782f 100644 --- a/src/dailyai/transports/daily_transport.py +++ b/src/dailyai/transports/daily_transport.py @@ -15,14 +15,21 @@ from threading import Event -from daily import ( - EventHandler, - CallClient, - Daily, - VirtualCameraDevice, - VirtualMicrophoneDevice, - VirtualSpeakerDevice, -) +try: + from daily import ( + EventHandler, + CallClient, + Daily, + VirtualCameraDevice, + VirtualMicrophoneDevice, + VirtualSpeakerDevice, + ) +except ModuleNotFoundError as e: + print(f"Exception: {e}") + print( + "In order to use the Daily transport, you need to `pip install dailyai[daily]`.") + raise Exception(f"Missing module: {e}") + from dailyai.transports.threaded_transport import ThreadedTransport diff --git a/src/dailyai/transports/local_transport.py b/src/dailyai/transports/local_transport.py index 4b24fb535..e79b147a8 100644 --- a/src/dailyai/transports/local_transport.py +++ b/src/dailyai/transports/local_transport.py @@ -4,17 +4,18 @@ from dailyai.transports.threaded_transport import ThreadedTransport +try: + import pyaudio +except ModuleNotFoundError as e: + print(f"Exception: {e}") + print( + "In order to use the local transport, you need to `pip install dailyai[local]`. On MacOS, you also need to `brew install portaudio`.") + raise Exception(f"Missing module: {e}") + class LocalTransport(ThreadedTransport): def __init__(self, **kwargs): super().__init__(**kwargs) - try: - global pyaudio - import pyaudio - except ModuleNotFoundError as e: - print(f"Exception: {e}") - print("In order to use the local transport, you'll need to `pip install pyaudio`. On MacOS, you'll also need to `brew install portaudio`.") - raise Exception(f"Missing module: {e}") self._sample_width = kwargs.get("sample_width") or 2 self._n_channels = kwargs.get("n_channels") or 1 self._tk_root = kwargs.get("tk_root") or None diff --git a/src/dailyai/transports/threaded_transport.py b/src/dailyai/transports/threaded_transport.py index 033cb97a2..9adb496d4 100644 --- a/src/dailyai/transports/threaded_transport.py +++ b/src/dailyai/transports/threaded_transport.py @@ -1,7 +1,6 @@ from abc import abstractmethod import asyncio import itertools -import logging import numpy as np import queue diff --git a/src/dailyai/transports/websocket_transport.py b/src/dailyai/transports/websocket_transport.py index 0784eb89f..f5009a02e 100644 --- a/src/dailyai/transports/websocket_transport.py +++ b/src/dailyai/transports/websocket_transport.py @@ -1,7 +1,6 @@ import asyncio import time from typing import AsyncGenerator, List -import websockets from dailyai.pipeline.frame_processor import FrameProcessor from dailyai.pipeline.frames import AudioFrame, ControlFrame, EndFrame, Frame, TTSEndFrame, TTSStartFrame, TextFrame @@ -10,6 +9,14 @@ from dailyai.transports.abstract_transport import AbstractTransport from dailyai.transports.threaded_transport import ThreadedTransport +try: + import websockets +except ModuleNotFoundError as e: + print(f"Exception: {e}") + print( + "In order to use the websocket transport, you need to `pip install dailyai[websocket]`.") + raise Exception(f"Missing module: {e}") + class WebSocketFrameProcessor(FrameProcessor): """This FrameProcessor filters and mutates frames before they're sent over the websocket. diff --git a/tests/integration/integration_azure_llm.py b/tests/integration/integration_azure_llm.py index 41cb8ee6c..d4744bd8b 100644 --- a/tests/integration/integration_azure_llm.py +++ b/tests/integration/integration_azure_llm.py @@ -1,8 +1,6 @@ import asyncio import os -from dailyai.pipeline.frames import ( - OpenAILLMContextFrame, -) +from dailyai.pipeline.openai_frames import OpenAILLMContextFrame from dailyai.services.azure_ai_services import AzureLLMService from dailyai.services.openai_llm_context import OpenAILLMContext diff --git a/tests/integration/integration_ollama_llm.py b/tests/integration/integration_ollama_llm.py index 527a20e98..2ac90ce65 100644 --- a/tests/integration/integration_ollama_llm.py +++ b/tests/integration/integration_ollama_llm.py @@ -1,7 +1,5 @@ import asyncio -from dailyai.pipeline.frames import ( - OpenAILLMContextFrame, -) +from dailyai.pipeline.openai_frames import OpenAILLMContextFrame from dailyai.services.openai_llm_context import OpenAILLMContext from openai.types.chat import ( diff --git a/tests/integration/integration_openai_llm.py b/tests/integration/integration_openai_llm.py index baea80d00..fa4a449ec 100644 --- a/tests/integration/integration_openai_llm.py +++ b/tests/integration/integration_openai_llm.py @@ -1,8 +1,6 @@ import asyncio import os -from dailyai.pipeline.frames import ( - OpenAILLMContextFrame, -) +from dailyai.pipeline.openai_frames import OpenAILLMContextFrame from dailyai.services.openai_llm_context import OpenAILLMContext from openai.types.chat import (