From 77d6296e88656e4e61c6e88c472cac8a88c5acb0 Mon Sep 17 00:00:00 2001 From: sudoping01 Date: Fri, 27 Jun 2025 18:37:34 +0000 Subject: [PATCH 01/15] refactor: replace global constants with Pydantic BaseSettings for env-based config --- djelia/config/settings.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) 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 From 1b0a703719f4a3e98d63461052603c70566f9850 Mon Sep 17 00:00:00 2001 From: sudoping01 Date: Fri, 27 Jun 2025 18:38:23 +0000 Subject: [PATCH 02/15] remove global imported var from settings, directly add base_url [Will make it dynamic later --- djelia/models/models.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) 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" From 0189c5843852b642696606eec8265e18876af483 Mon Sep 17 00:00:00 2001 From: sudoping01 Date: Fri, 27 Jun 2025 18:39:52 +0000 Subject: [PATCH 03/15] add api key validation with uuid validator and avoid loading envar which is directly handled by settings --- djelia/src/auth/auth.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) 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} From c0da827a3f6d2181515d9cf10af9d4d1eae0b901 Mon Sep 17 00:00:00 2001 From: sudoping01 Date: Fri, 27 Jun 2025 18:40:45 +0000 Subject: [PATCH 04/15] refactor based on the centralization of settings and add safety check, add base_url as optional param --- djelia/src/client/client.py | 59 +++++++++++++++++++++++++++++++------ 1 file changed, 50 insertions(+), 9 deletions(-) diff --git a/djelia/src/client/client.py b/djelia/src/client/client.py index e02344e..a798b0b 100644 --- a/djelia/src/client/client.py +++ b/djelia/src/client/client.py @@ -1,17 +1,44 @@ +from typing import Union + import aiohttp import requests -from tenacity import (retry, retry_if_exception_type, stop_after_attempt, - wait_random_exponential) - +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.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) @@ -41,8 +68,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) From 82f9880bed62ab5960856e15c59a90e881c2c47a Mon Sep 17 00:00:00 2001 From: sudoping01 Date: Fri, 27 Jun 2025 18:41:28 +0000 Subject: [PATCH 05/15] avod global var importation from settings, use the central settings for speakers settings --- djelia/src/services/tts.py | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/djelia/src/services/tts.py b/djelia/src/services/tts.py index e6de0d2..1e329b1 100644 --- a/djelia/src/services/tts.py +++ b/djelia/src/services/tts.py @@ -1,8 +1,13 @@ from collections.abc import AsyncGenerator, Generator -from djelia.config.settings import VALID_SPEAKER_IDS, VALID_TTS_V2_SPEAKERS -from djelia.models import (DjeliaRequest, ErrorsMessage, TTSRequest, - TTSRequestV2, Versions) +# 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 +25,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 +36,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 +110,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 +121,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 ) ) From e061d116c31f19b7ec5a2d27ebe3afdb6d266a20 Mon Sep 17 00:00:00 2001 From: sudoping01 Date: Fri, 27 Jun 2025 18:42:50 +0000 Subject: [PATCH 06/15] add pydantic settings and change pydantic version to the compatible one with pydantic settings --- setup.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 587814d..ba92237 100644 --- a/setup.py +++ b/setup.py @@ -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={ From 86f12cff0db0ab2d1a1fda7b3a8581bbc1a2ef38 Mon Sep 17 00:00:00 2001 From: sudoping01 Date: Fri, 27 Jun 2025 18:43:41 +0000 Subject: [PATCH 07/15] update version to a medium based on the changes --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index ba92237..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", From 2227670057dd5deb97336658136db4878bd5578f Mon Sep 17 00:00:00 2001 From: sudoping01 Date: Fri, 27 Jun 2025 18:48:36 +0000 Subject: [PATCH 08/15] code quality: auto formatting with black --- cookbook/cookbook.py | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/cookbook/cookbook.py b/cookbook/cookbook.py index 3e2fc62..ecc426e 100644 --- a/cookbook/cookbook.py +++ b/cookbook/cookbook.py @@ -5,13 +5,26 @@ from uuid import uuid4 from djelia import Djelia, DjeliaAsync -from djelia.models import (Language, SupportedLanguageSchema, - TranslationRequest, TranslationResponse, TTSRequest, - TTSRequestV2, Versions) +from djelia.models import ( + Language, + SupportedLanguageSchema, + TranslationRequest, + TranslationResponse, + TTSRequest, + TTSRequestV2, + Versions, +) from .config import Config -from .utils import (ConsoleColor, handle_transcription_result, print_error, - print_info, print_success, print_summary, process_result) +from .utils import ( + ConsoleColor, + handle_transcription_result, + print_error, + print_info, + print_success, + print_summary, + process_result, +) # ================================================ # Djelia Cookbook From f8b1c794fc977c73ff2997e4d9db988964dfd57e Mon Sep 17 00:00:00 2001 From: sudoping01 Date: Fri, 27 Jun 2025 18:49:09 +0000 Subject: [PATCH 09/15] code quality auto formatting with black --- djelia/models/__init__.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/djelia/models/__init__.py b/djelia/models/__init__.py index bb8d525..5819150 100644 --- a/djelia/models/__init__.py +++ b/djelia/models/__init__.py @@ -1,9 +1,18 @@ from .models import ErrorsMessage # TranscriptionRequest, -from .models import (DjeliaRequest, FrenchTranscriptionResponse, - HttpRequestInfo, Language, Params, - SupportedLanguageSchema, TranscriptionSegment, - TranslationRequest, TranslationResponse, TTSRequest, - TTSRequestV2, Versions) +from .models import ( + DjeliaRequest, + FrenchTranscriptionResponse, + HttpRequestInfo, + Language, + Params, + SupportedLanguageSchema, + TranscriptionSegment, + TranslationRequest, + TranslationResponse, + TTSRequest, + TTSRequestV2, + Versions, +) __all__ = [ "Language", From dfde1272bcef5cf972fe9f7e147f8ac0fd43ab9d Mon Sep 17 00:00:00 2001 From: sudoping01 Date: Fri, 27 Jun 2025 18:49:33 +0000 Subject: [PATCH 10/15] code style, autoformating black --- djelia/src/services/transcription.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/djelia/src/services/transcription.py b/djelia/src/services/transcription.py index df83210..ea9bbdf 100644 --- a/djelia/src/services/transcription.py +++ b/djelia/src/services/transcription.py @@ -5,9 +5,14 @@ import aiohttp -from djelia.models import (DjeliaRequest, ErrorsMessage, - FrenchTranscriptionResponse, Params, - TranscriptionSegment, Versions) +from djelia.models import ( + DjeliaRequest, + ErrorsMessage, + FrenchTranscriptionResponse, + Params, + TranscriptionSegment, + Versions, +) class Transcription: From bf7dca89527f030c32e309399d9b016d03124938 Mon Sep 17 00:00:00 2001 From: sudoping01 Date: Fri, 27 Jun 2025 18:49:57 +0000 Subject: [PATCH 11/15] code style auto formattings: black --- djelia/src/services/translation.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/djelia/src/services/translation.py b/djelia/src/services/translation.py index 5b0a294..5e67906 100644 --- a/djelia/src/services/translation.py +++ b/djelia/src/services/translation.py @@ -1,5 +1,10 @@ -from djelia.models import (DjeliaRequest, SupportedLanguageSchema, - TranslationRequest, TranslationResponse, Versions) +from djelia.models import ( + DjeliaRequest, + SupportedLanguageSchema, + TranslationRequest, + TranslationResponse, + Versions, +) class Translation: From 5422fee427a8af38570f9e2cc8703d859a41fcd0 Mon Sep 17 00:00:00 2001 From: sudoping01 Date: Fri, 27 Jun 2025 18:50:19 +0000 Subject: [PATCH 12/15] code quality auto formating --- djelia/utils/errors.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/djelia/utils/errors.py b/djelia/utils/errors.py index 7ede6c7..8a00594 100644 --- a/djelia/utils/errors.py +++ b/djelia/utils/errors.py @@ -1,7 +1,11 @@ from typing import Any -from djelia.utils.exceptions import (APIError, AuthenticationError, - DjeliaError, ValidationError) +from djelia.utils.exceptions import ( + APIError, + AuthenticationError, + DjeliaError, + ValidationError, +) class ExceptionMessage: From 6a9886e762072780f2f064873f8992976829e2ca Mon Sep 17 00:00:00 2001 From: sudoping01 Date: Fri, 27 Jun 2025 18:52:02 +0000 Subject: [PATCH 13/15] code quality: import formatting with isort --- djelia/utils/__init__.py | 0 djelia/utils/utils.py | 9 +++++++++ 2 files changed, 9 insertions(+) create mode 100644 djelia/utils/__init__.py create mode 100644 djelia/utils/utils.py 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 From 7bc8d2f8a2a53fe0b7d07a5e6456290e4c1113d7 Mon Sep 17 00:00:00 2001 From: sudoping01 Date: Fri, 27 Jun 2025 18:53:17 +0000 Subject: [PATCH 14/15] auto formating: isort --- cookbook/cookbook.py | 23 +++++------------------ 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/cookbook/cookbook.py b/cookbook/cookbook.py index ecc426e..3e2fc62 100644 --- a/cookbook/cookbook.py +++ b/cookbook/cookbook.py @@ -5,26 +5,13 @@ from uuid import uuid4 from djelia import Djelia, DjeliaAsync -from djelia.models import ( - Language, - SupportedLanguageSchema, - TranslationRequest, - TranslationResponse, - TTSRequest, - TTSRequestV2, - Versions, -) +from djelia.models import (Language, SupportedLanguageSchema, + TranslationRequest, TranslationResponse, TTSRequest, + TTSRequestV2, Versions) from .config import Config -from .utils import ( - ConsoleColor, - handle_transcription_result, - print_error, - print_info, - print_success, - print_summary, - process_result, -) +from .utils import (ConsoleColor, handle_transcription_result, print_error, + print_info, print_success, print_summary, process_result) # ================================================ # Djelia Cookbook From a87ec2bad4ae15ce7a3a40d52d0c6936d877c359 Mon Sep 17 00:00:00 2001 From: sudoping01 Date: Fri, 27 Jun 2025 18:53:46 +0000 Subject: [PATCH 15/15] code style auto formating inport with isort --- djelia/models/__init__.py | 19 +++++-------------- djelia/src/client/client.py | 18 ++++-------------- djelia/src/services/transcription.py | 11 +++-------- djelia/src/services/translation.py | 9 ++------- djelia/src/services/tts.py | 9 ++------- djelia/utils/errors.py | 8 ++------ 6 files changed, 18 insertions(+), 56 deletions(-) diff --git a/djelia/models/__init__.py b/djelia/models/__init__.py index 5819150..bb8d525 100644 --- a/djelia/models/__init__.py +++ b/djelia/models/__init__.py @@ -1,18 +1,9 @@ from .models import ErrorsMessage # TranscriptionRequest, -from .models import ( - DjeliaRequest, - FrenchTranscriptionResponse, - HttpRequestInfo, - Language, - Params, - SupportedLanguageSchema, - TranscriptionSegment, - TranslationRequest, - TranslationResponse, - TTSRequest, - TTSRequestV2, - Versions, -) +from .models import (DjeliaRequest, FrenchTranscriptionResponse, + HttpRequestInfo, Language, Params, + SupportedLanguageSchema, TranscriptionSegment, + TranslationRequest, TranslationResponse, TTSRequest, + TTSRequestV2, Versions) __all__ = [ "Language", diff --git a/djelia/src/client/client.py b/djelia/src/client/client.py index a798b0b..2c68fd0 100644 --- a/djelia/src/client/client.py +++ b/djelia/src/client/client.py @@ -2,23 +2,13 @@ import aiohttp import requests -from tenacity import ( - retry, - retry_if_exception_type, - stop_after_attempt, - wait_random_exponential, -) +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.src.services import (TTS, AsyncTranscription, AsyncTranslation, + AsyncTTS, Transcription, Translation) from djelia.utils.errors import api_exception, general_exception diff --git a/djelia/src/services/transcription.py b/djelia/src/services/transcription.py index ea9bbdf..df83210 100644 --- a/djelia/src/services/transcription.py +++ b/djelia/src/services/transcription.py @@ -5,14 +5,9 @@ import aiohttp -from djelia.models import ( - DjeliaRequest, - ErrorsMessage, - FrenchTranscriptionResponse, - Params, - TranscriptionSegment, - Versions, -) +from djelia.models import (DjeliaRequest, ErrorsMessage, + FrenchTranscriptionResponse, Params, + TranscriptionSegment, Versions) class Transcription: diff --git a/djelia/src/services/translation.py b/djelia/src/services/translation.py index 5e67906..5b0a294 100644 --- a/djelia/src/services/translation.py +++ b/djelia/src/services/translation.py @@ -1,10 +1,5 @@ -from djelia.models import ( - DjeliaRequest, - SupportedLanguageSchema, - TranslationRequest, - TranslationResponse, - Versions, -) +from djelia.models import (DjeliaRequest, SupportedLanguageSchema, + TranslationRequest, TranslationResponse, Versions) class Translation: diff --git a/djelia/src/services/tts.py b/djelia/src/services/tts.py index 1e329b1..03e9981 100644 --- a/djelia/src/services/tts.py +++ b/djelia/src/services/tts.py @@ -1,13 +1,8 @@ from collections.abc import AsyncGenerator, Generator # from djelia.config.settings import VALID_SPEAKER_IDS, VALID_TTS_V2_SPEAKERS -from djelia.models import ( - DjeliaRequest, - ErrorsMessage, - TTSRequest, - TTSRequestV2, - Versions, -) +from djelia.models import (DjeliaRequest, ErrorsMessage, TTSRequest, + TTSRequestV2, Versions) from djelia.utils.exceptions import SpeakerError diff --git a/djelia/utils/errors.py b/djelia/utils/errors.py index 8a00594..7ede6c7 100644 --- a/djelia/utils/errors.py +++ b/djelia/utils/errors.py @@ -1,11 +1,7 @@ from typing import Any -from djelia.utils.exceptions import ( - APIError, - AuthenticationError, - DjeliaError, - ValidationError, -) +from djelia.utils.exceptions import (APIError, AuthenticationError, + DjeliaError, ValidationError) class ExceptionMessage: