Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 12 additions & 6 deletions djelia/config/settings.py
Original file line number Diff line number Diff line change
@@ -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
6 changes: 2 additions & 4 deletions djelia/models/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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"
Expand Down
12 changes: 5 additions & 7 deletions djelia/src/auth/auth.py
Original file line number Diff line number Diff line change
@@ -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}
39 changes: 35 additions & 4 deletions djelia/src/client/client.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,34 @@
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)
from djelia.utils.errors import api_exception, general_exception


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)
Expand Down Expand Up @@ -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)
Expand Down
18 changes: 9 additions & 9 deletions djelia/src/services/tts.py
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -20,23 +20,23 @@ 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:
if not isinstance(request, TTSRequestV2):
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
)
)

Expand Down Expand Up @@ -105,23 +105,23 @@ 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:
if not isinstance(request, TTSRequestV2):
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
)
)

Expand Down
Empty file added djelia/utils/__init__.py
Empty file.
9 changes: 9 additions & 0 deletions djelia/utils/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import uuid


def is_valid_uuid(val):
try:
uuid.UUID(str(val))
return True
except ValueError:
return False
5 changes: 3 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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={
Expand Down
Loading