diff --git a/djelia/config/settings.py b/djelia/config/settings.py index 6339cff..2fdc217 100644 --- a/djelia/config/settings.py +++ b/djelia/config/settings.py @@ -1,8 +1,14 @@ -BASE_URL = "https://djelia.cloud" -API_KEY_HEADER = "x-api-key" -ENV_API_KEY = "DJELIA_API_KEY" +from typing import List -VALID_SPEAKER_IDS = [0, 1, 2, 3, 4] -DEFAULT_SPEAKER_ID = 1 +from pydantic import Field +from pydantic_settings import BaseSettings -VALID_TTS_V2_SPEAKERS = ["Moussa", "Sekou", "Seydou"] + +class Settings(BaseSettings): + base_url: str = Field(validation_alias="BASE_URL", default="https://djelia.cloud") + djelia_api_key: str = Field(validation_alias="DJELIA_API_KEY") + valid_speaker_ids: List[int] = Field(default_factory=lambda: [0, 1, 2, 3, 4]) + valid_tts_v2_speakers: List[str] = Field( + default_factory=lambda: ["Moussa", "Sekou", "Seydou"] + ) + default_speaker_id: int = 1 diff --git a/djelia/models/models.py b/djelia/models/models.py index 2580188..8bfde9b 100644 --- a/djelia/models/models.py +++ b/djelia/models/models.py @@ -3,8 +3,6 @@ from pydantic import BaseModel, Field -from djelia.config.settings import BASE_URL, ENV_API_KEY - class Language(str, Enum): FRENCH = "fra_Latn" @@ -35,7 +33,7 @@ class HttpRequestInfo: class DjeliaRequest: - endpoint_prefix = f"{BASE_URL}/api/v{{}}/models/" + endpoint_prefix = "https://djelia.cloud/api/v{}/models/" get_supported_languages: HttpRequestInfo = HttpRequestInfo( endpoint=endpoint_prefix + "translate/supported-languages", method="GET" @@ -111,7 +109,7 @@ class ErrorsMessage: ) speaker_id_error: str = "Speaker ID must be one of {}, got {}" api_key_missing: str = ( - f"API key must be provided via parameter or {ENV_API_KEY} environment variable" + "API key must be provided via parameter or environment variable" ) tts_v1_request_error: str = "TTSRequest required for V1" tts_v2_request_error: str = "TTSRequestV2 required for V2" diff --git a/djelia/src/auth/auth.py b/djelia/src/auth/auth.py index 864055d..3bd6723 100644 --- a/djelia/src/auth/auth.py +++ b/djelia/src/auth/auth.py @@ -1,15 +1,13 @@ -# from djelia.config.settings import API_KEY_HEADER, ENV_API_KEY -import os - -from djelia.models.models import API_KEY_HEADER, ENV_API_KEY, ErrorsMessage +from djelia.models.models import ErrorsMessage +from djelia.utils.utils import is_valid_uuid class Auth: def __init__(self, api_key: str = None): - self.api_key = api_key or os.environ.get(ENV_API_KEY) + self.api_key = api_key - if not self.api_key: + if not is_valid_uuid(self.api_key): raise ValueError(ErrorsMessage.api_key_missing) def get_headers(self): - return {API_KEY_HEADER: self.api_key} + return {"x-api-key": self.api_key} diff --git a/djelia/src/client/client.py b/djelia/src/client/client.py index e02344e..2c68fd0 100644 --- a/djelia/src/client/client.py +++ b/djelia/src/client/client.py @@ -1,8 +1,11 @@ +from typing import Union + import aiohttp import requests from tenacity import (retry, retry_if_exception_type, stop_after_attempt, wait_random_exponential) +from djelia.config.settings import Settings from djelia.src.auth import Auth from djelia.src.services import (TTS, AsyncTranscription, AsyncTranslation, AsyncTTS, Transcription, Translation) @@ -10,8 +13,22 @@ class Djelia: - def __init__(self, api_key: str = None): - self.auth = Auth(api_key) + def __init__( + self, api_key: Union[str, None] = None, base_url: Union[str, None] = None + ): + self.settings = None + if base_url is None: + self.settings = Settings() + self.base_url = self.settings.base_url + else: + self.base_url = self.base_url + + if api_key is None: + self.settings = Settings() + self.auth = Auth(self.settings.djelia_api_key) + else: + self.auth = Auth(api_key=api_key) + self.translation = Translation(self) self.transcription = Transcription(self) self.tts = TTS(self) @@ -41,8 +58,22 @@ def _make_request(self, method: str, endpoint: str, **kwargs): class DjeliaAsync: - def __init__(self, api_key: str = None): - self.auth = Auth(api_key) + def __init__( + self, api_key: Union[str, None] = None, base_url: Union[str, None] = None + ): + self.settings = None + if base_url is None: + self.settings = Settings() + self.base_url = self.settings.base_url + else: + self.base_url = self.base_url + + if api_key is None: + self.settings = Settings() + self.auth = Auth(self.settings.djelia_api_key) + else: + self.auth = Auth(api_key=api_key) + self.translation = AsyncTranslation(self) self.transcription = AsyncTranscription(self) self.tts = AsyncTTS(self) diff --git a/djelia/src/services/tts.py b/djelia/src/services/tts.py index e6de0d2..03e9981 100644 --- a/djelia/src/services/tts.py +++ b/djelia/src/services/tts.py @@ -1,6 +1,6 @@ from collections.abc import AsyncGenerator, Generator -from djelia.config.settings import VALID_SPEAKER_IDS, VALID_TTS_V2_SPEAKERS +# from djelia.config.settings import VALID_SPEAKER_IDS, VALID_TTS_V2_SPEAKERS from djelia.models import (DjeliaRequest, ErrorsMessage, TTSRequest, TTSRequestV2, Versions) from djelia.utils.exceptions import SpeakerError @@ -20,10 +20,10 @@ def text_to_speech( if version == Versions.v1: if not isinstance(request, TTSRequest): raise ValueError(ErrorsMessage.tts_v1_request_error) - if request.speaker not in VALID_SPEAKER_IDS: + if request.speaker not in self.client.settings.valid_speaker_ids: raise SpeakerError( ErrorsMessage.speaker_id_error.format( - VALID_SPEAKER_IDS, request.speaker + self.client.settings.valid_speaker_ids, request.speaker ) ) else: @@ -31,12 +31,12 @@ def text_to_speech( raise ValueError(ErrorsMessage.tts_v2_request_error) speaker_found = any( speaker.lower() in request.description.lower() - for speaker in VALID_TTS_V2_SPEAKERS + for speaker in self.client.settings.valid_tts_v2_speakers ) if not speaker_found: raise SpeakerError( ErrorsMessage.speaker_description_error.format( - VALID_TTS_V2_SPEAKERS + self.client.settings.valid_tts_v2_speakers ) ) @@ -105,10 +105,10 @@ async def text_to_speech( if version == Versions.v1: if not isinstance(request, TTSRequest): raise ValueError(ErrorsMessage.tts_v1_request_error) - if request.speaker not in VALID_SPEAKER_IDS: + if request.speaker not in self.client.settings.valid_speaker_ids: raise SpeakerError( ErrorsMessage.speaker_id_error.format( - VALID_SPEAKER_IDS, request.speaker + self.client.settings.valid_speaker_ids, request.speaker ) ) else: @@ -116,12 +116,12 @@ async def text_to_speech( raise ValueError(ErrorsMessage.tts_v2_request_error) speaker_found = any( speaker.lower() in request.description.lower() - for speaker in VALID_TTS_V2_SPEAKERS + for speaker in self.client.settings.valid_tts_v2_speakers ) if not speaker_found: raise SpeakerError( ErrorsMessage.speaker_description_error.format( - VALID_TTS_V2_SPEAKERS + self.client.settings.valid_tts_v2_speakers ) ) diff --git a/djelia/utils/__init__.py b/djelia/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/djelia/utils/utils.py b/djelia/utils/utils.py new file mode 100644 index 0000000..21a2f40 --- /dev/null +++ b/djelia/utils/utils.py @@ -0,0 +1,9 @@ +import uuid + + +def is_valid_uuid(val): + try: + uuid.UUID(str(val)) + return True + except ValueError: + return False diff --git a/setup.py b/setup.py index 587814d..e15ec21 100644 --- a/setup.py +++ b/setup.py @@ -8,7 +8,7 @@ setup( name="djelia", - version="1.0.1", + version="1.1.1", author="Djelia", author_email="support@djelia.cloud", description="Djelia Python SDK - Advanced AI for African Languages", @@ -43,7 +43,8 @@ install_requires=[ "requests>=2.25.0", "aiohttp>=3.8.0", - "pydantic>=1.8.0,<2.0.0", + "pydantic-settings>=2.10.1", + "pydantic>=2.7.0", "tenacity>=9.1.2", ], extras_require={