Skip to content

Feat/OpenAI style api - #13

Merged
sudoping01 merged 22 commits into
mainfrom
feat/openai-style-api
Jul 25, 2026
Merged

Feat/OpenAI style api#13
sudoping01 merged 22 commits into
mainfrom
feat/openai-style-api

Conversation

@sudoping01

@sudoping01 sudoping01 commented Jul 25, 2026

Copy link
Copy Markdown
Collaborator

Pull Request

Description

Migrates the SDK to an OpenAI-style resource API (client.audio.speech.create(...), client.audio.transcriptions.create(...), client.translations.create(...)) alongside the existing service-based classes, which are now deprecated but still exported for backward compatibility. Also adds a CLI (typer + rich) for running the cookbook examples with a proper performance view instead of plain stdout.

This is a breaking-ish change at the API surface level even though old classes still work: Versions.from_value normalizer and new error messages change how invalid inputs and API errors surface, and APIError now always carries status_code and message.

Related Issue(s)

None linked.

Changes Made

  • Added Versions.from_value normalizer and new error messages for invalid version input.
  • APIError construction now passes status_code and message instead of a bare exception.
  • Added Translations resource (OpenAI-style), deprecated Translation.
  • Added Transcriptions resource (OpenAI-style), deprecated Transcription.
  • Added Speech resource (OpenAI-style), deprecated TTS.
  • Added Audio namespace grouping transcriptions and speech.
  • Exported the new OpenAI-style resources alongside the deprecated services so both import paths work.
  • Wired the OpenAI-style resources into the client; Settings is now always built regardless of which API style is used.
  • Updated API reference docs and README examples to the OpenAI-style methods.
  • Migrated the cookbook to the OpenAI-style API.
  • Bumped major version to reflect the API surface change.
  • Added typer + rich as dependencies; new CLI entrypoint for running the cookbook with a richer performance/output view.
  • Updated the cookbook run command and docs to match the new CLI entrypoint.
  • README updated for the new CLI usage.

Testing

  • Ran the cookbook through the new CLI entrypoint to confirm both OpenAI-style and legacy service calls still return successfully.
  • No dedicated new unit tests added for the resource migration in this PR — flagging this in case reviewer wants coverage on Versions.from_value and the new APIError construction path before merge.

Checklist

  • Code follows style guidelines (ruff, isort, and optionally black)
  • Tests added or updated in the cookbook module
  • Documentation updated (README.md, API reference, cookbook run instructions)
  • All tests pass (python -m cookbook.main)
  • No linting errors (ruff check . and isort . --check-only)

Additional Notes

Deprecated classes (Translation, Transcription, TTS) are still exported, so this shouldn't break existing integrations immediately, but the major version bump signals the intent to remove them in a future release. Worth deciding a deprecation timeline before that happens.

The last several commits (ignore update, entrypoint, cli entrypoint definition, repeated update cookbook run command) were iteration on the CLI design as it was being redefined — only the latest file state reflects the intended CLI/cookbook behavior, not each intermediate commit.

Tag @sudoping01 for review. Thanks for contributing to Djelia Python SDK! ❤️🇲🇱

Summary by CodeRabbit

  • New Features

    • Added an OpenAI-style API for translations, transcriptions, and speech generation, including synchronous and asynchronous support.
    • Added streaming options for transcription and text-to-speech, with optional audio-file output.
    • Added an interactive Cookbook test-suite command with selectable test groups and execution modes.
    • Added version-value validation and improved API error handling.
  • Documentation

    • Updated README and API documentation with current usage examples and migration guidance.
  • Chores

    • Updated release metadata and added Cookbook setup requirements.

Accept model/version as enum, int, or string (e.g. 'v2'). Add messages for invalid version and required TTS v2 description.
APIError requires (status_code, message); the factory previously passed a single argument, raising TypeError on 403/404 responses.
New create()/list_languages() with flat kwargs. Old Translation is kept as a deprecated shim that delegates to the new resource.
New create() with flat kwargs. Fixes the streaming path to use the transcribe/stream endpoint and method. Old Transcription is kept as a deprecated shim.
New create() with flat kwargs (voice for v1, description for v2). Fixes the streaming-compatibility error attribute typo. Old TTS is kept as a deprecated shim.
Groups audio.transcriptions and audio.speech, mirroring the OpenAI client layout.
Expose client.translations and client.audio, keep translation/transcription/tts as deprecated aliases. Always construct Settings so it is never None when both api_key and base_url are passed, and fix the async base_url self-assignment.
@coderabbitai

coderabbitai Bot commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The SDK introduces OpenAI-style translation, transcription, and speech resources with deprecated legacy wrappers. The cookbook becomes a configurable Typer/Rich test suite, while examples, documentation, package metadata, and cookbook execution instructions are updated.

Changes

SDK API modernization

Layer / File(s) Summary
Version and error contracts
djelia/models/models.py, djelia/utils/errors.py
Version inputs are normalized through Versions.from_value, with standardized invalid-version and TTS validation messages.
Resource wiring
djelia/src/client/client.py, djelia/src/services/*
Sync and async clients expose translations and audio.transcriptions/audio.speech namespaces while retaining deprecated service exports.
Service implementations
djelia/src/services/translation.py, djelia/src/services/transcription.py, djelia/src/services/tts.py
New create-based translation, transcription, streaming, and speech APIs are implemented; legacy methods delegate with deprecation warnings.

Cookbook test suite

Layer / File(s) Summary
Configurable runner and reporting
cookbook/config.py, cookbook/utils.py, cookbook/cookbook.py, cookbook/__main__.py
The cookbook gains grouped sync/async execution, runtime configuration, Rich reporting, audio cleanup, validation, and Typer command options.

Documentation and packaging

Layer / File(s) Summary
Examples and project instructions
README.md, docs/api.md, CONTRIBUTING.md, .github/pull_request_template.md, .gitignore, setup.py
Examples and API references use the new namespaces, cookbook commands use python -m cookbook, generated output is ignored, and the cookbook extra plus package metadata are updated.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant Translations
  participant Audio
  participant Transcriptions
  participant Speech
  participant API
  Client->>Translations: create translation
  Translations->>API: submit translation request
  Client->>Audio: access audio namespace
  Audio->>Transcriptions: create transcription
  Transcriptions->>API: submit transcription request
  Audio->>Speech: create speech
  Speech->>API: submit speech request
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 7.41% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and matches the main change: an OpenAI-style API migration.
Description check ✅ Passed The description follows the template and covers the required sections, including changes, testing, checklist, and notes.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/openai-style-api

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
djelia/src/services/transcription.py (1)

32-52: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Make retryable transcription uploads idempotent.

Djelia._make_request is decorated with tenacity.retry, but the synchronous transcribe path passes a file handle directly in files=. Retrying can re-send a handle whose stream position has moved, causing retry uploads to send a truncated/empty body or fail due to an unrewindable body. Read the bytes once and pass bytes to files= before calling _make_request.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@djelia/src/services/transcription.py` around lines 32 - 52, Update the
synchronous transcription flow around Djelia._make_request to read file inputs
into bytes before constructing files, including string paths and file-like
objects. Pass the buffered bytes in files= so tenacity retries resend the
complete upload from the beginning without relying on a rewindable handle.
🧹 Nitpick comments (4)
djelia/src/services/translation.py (1)

30-37: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

All new resource create paths serialize with pydantic v1's deprecated .dict(). With pydantic>=2.7.0 declared in setup.py, each call emits a DeprecationWarning; replace with model_dump().

  • djelia/src/services/translation.py#L30-L37: json=request.model_dump() in Translations.create.
  • djelia/src/services/translation.py#L63-L70: same in AsyncTranslations.create.
  • djelia/src/services/tts.py#L73-L77: same in Speech.create (and the adjacent _stream call).
  • djelia/src/services/tts.py#L142-L146: same in AsyncSpeech.create (and its _stream call).
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@djelia/src/services/translation.py` around lines 30 - 37, Replace deprecated
Pydantic .dict() serialization with model_dump() in Translations.create at
djelia/src/services/translation.py:30-37, AsyncTranslations.create at
djelia/src/services/translation.py:63-70, Speech.create and adjacent _stream
call at djelia/src/services/tts.py:73-77, and AsyncSpeech.create and its _stream
call at djelia/src/services/tts.py:142-146; preserve the existing request
payload behavior.
cookbook/cookbook.py (2)

453-469: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Run cleanup in a finally block.

cleanup() is skipped if a test phase raises or the user interrupts (__main__.py catches KeyboardInterrupt at line 171), leaving generated audio behind despite the documented auto-cleanup behaviour.

♻️ Proposed fix
     def run(self, groups: List[str], mode: str) -> None:
         selected = [g for g in GROUP_ORDER if g in groups]
-
-        for key in selected:
-            method = GROUPS[key]["sync"]
-            if mode in ("sync", "both") and method:
-                getattr(self, method)()
-
-        async_methods = [
-            GROUPS[key]["async"]
-            for key in selected
-            if mode in ("async", "both") and GROUPS[key]["async"]
-        ]
-        if async_methods:
-            asyncio.run(self._run_async(async_methods))
-
-        self.cleanup()
+        try:
+            if mode in ("sync", "both"):
+                for key in selected:
+                    method = GROUPS[key]["sync"]
+                    if method:
+                        getattr(self, method)()
+
+            if mode in ("async", "both"):
+                async_methods = [
+                    GROUPS[key]["async"] for key in selected if GROUPS[key]["async"]
+                ]
+                if async_methods:
+                    asyncio.run(self._run_async(async_methods))
+        finally:
+            self.cleanup()
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cookbook/cookbook.py` around lines 453 - 469, Update run so all sync and
async execution, including _run_async, is enclosed in a try/finally structure,
and invoke cleanup from the finally block. Preserve the existing selection and
mode behavior while ensuring cleanup runs when execution raises or is
interrupted.

233-243: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Streaming iterators are abandoned at early break instead of closed. Every bounded streaming test exits its loop with break, leaving the SDK generator and its underlying HTTP response open until GC; the async variants additionally risk "async generator ignored GeneratorExit" warnings.

  • cookbook/cookbook.py#L233-L243: wrap the sync transcription stream in contextlib.closing (and await generator.aclose() after the break in streaming_transcription_async, lines 255-266).
  • cookbook/cookbook.py#L328-L339: wrap the sync TTS chunk stream in contextlib.closing (and await generator.aclose() after the break in streaming_tts_async, lines 350-362).
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cookbook/cookbook.py` around lines 233 - 243, Ensure all bounded streaming
examples explicitly close their iterators: wrap the synchronous transcription
stream in streaming_transcription and the synchronous TTS chunk stream in
streaming_tts with contextlib.closing, and await generator.aclose() after early
termination in streaming_transcription_async and streaming_tts_async. Apply
these changes at cookbook/cookbook.py lines 233-243 and 328-339; the async sites
at lines 255-266 and 350-362 also require cleanup.
cookbook/__main__.py (1)

3-16: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Drop the sys.path manipulation.

The relative imports on lines 14-16 only resolve when the package is executed as python -m cookbook, in which case the repo root is already importable — the sys.path.append is dead weight and forces imports below top-of-file (an E402 candidate for the repo's ruff gate).

♻️ Proposed fix
 import enum
-import os
-import sys
 from typing import List, Optional, Tuple
 
 import typer
 from rich.prompt import Confirm, Prompt
 from rich.table import Table
 
-sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
-
 from .config import Config
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cookbook/__main__.py` around lines 3 - 16, Remove the os and sys imports and
the sys.path.append call from the module-level setup in cookbook/__main__.py;
keep the existing relative imports of Config,
GROUP_ORDER/GROUPS/DjeliaTestSuite, and Reporter/banner/console/render_summary
unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@CONTRIBUTING.md`:
- Around line 64-68: Update the cookbook setup instructions in CONTRIBUTING.md
to install the cookbook extra by changing the editable install command to use
the project’s cookbook extra, and update the dependency note near
extras_require["test"] to reference the cookbook extra as well. Ensure both
locations direct users to install the dependencies required by python -m
cookbook.

In `@djelia/src/services/translation.py`:
- Around line 88-104: Deprecated wrappers must normalize explicitly passed None
versions before forwarding them. In djelia/src/services/translation.py lines
88-104 and 122-138, use the default Versions.v1 when version is None in
Translation.translate and AsyncTranslation.translate; in
djelia/src/services/transcription.py lines 246-264 and 273-291, apply the
default Versions.v2 in Transcription.transcribe and
AsyncTranscription.transcribe; in djelia/src/services/tts.py lines 205-205 and
247-247, normalize version with the existing Versions.from_value using
Versions.v1 as the fallback in TTS.text_to_speech and AsyncTTS.text_to_speech.

In `@setup.py`:
- Around line 70-74: Update the “cookbook” dependency extras in setup.py to
constrain rich to versions compatible with Python 3.7, changing its requirement
from only a lower bound to a range below 13.9.0 while preserving the existing
minimum version.

---

Outside diff comments:
In `@djelia/src/services/transcription.py`:
- Around line 32-52: Update the synchronous transcription flow around
Djelia._make_request to read file inputs into bytes before constructing files,
including string paths and file-like objects. Pass the buffered bytes in files=
so tenacity retries resend the complete upload from the beginning without
relying on a rewindable handle.

---

Nitpick comments:
In `@cookbook/__main__.py`:
- Around line 3-16: Remove the os and sys imports and the sys.path.append call
from the module-level setup in cookbook/__main__.py; keep the existing relative
imports of Config, GROUP_ORDER/GROUPS/DjeliaTestSuite, and
Reporter/banner/console/render_summary unchanged.

In `@cookbook/cookbook.py`:
- Around line 453-469: Update run so all sync and async execution, including
_run_async, is enclosed in a try/finally structure, and invoke cleanup from the
finally block. Preserve the existing selection and mode behavior while ensuring
cleanup runs when execution raises or is interrupted.
- Around line 233-243: Ensure all bounded streaming examples explicitly close
their iterators: wrap the synchronous transcription stream in
streaming_transcription and the synchronous TTS chunk stream in streaming_tts
with contextlib.closing, and await generator.aclose() after early termination in
streaming_transcription_async and streaming_tts_async. Apply these changes at
cookbook/cookbook.py lines 233-243 and 328-339; the async sites at lines 255-266
and 350-362 also require cleanup.

In `@djelia/src/services/translation.py`:
- Around line 30-37: Replace deprecated Pydantic .dict() serialization with
model_dump() in Translations.create at djelia/src/services/translation.py:30-37,
AsyncTranslations.create at djelia/src/services/translation.py:63-70,
Speech.create and adjacent _stream call at djelia/src/services/tts.py:73-77, and
AsyncSpeech.create and its _stream call at djelia/src/services/tts.py:142-146;
preserve the existing request payload behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 682511f6-b56e-4a5b-b62b-5d4ffdcc081f

📥 Commits

Reviewing files that changed from the base of the PR and between f21981c and 33c20d7.

📒 Files selected for processing (19)
  • .github/pull_request_template.md
  • .gitignore
  • CONTRIBUTING.md
  • README.md
  • cookbook/__main__.py
  • cookbook/config.py
  • cookbook/cookbook.py
  • cookbook/main.py
  • cookbook/utils.py
  • djelia/models/models.py
  • djelia/src/client/client.py
  • djelia/src/services/__init__.py
  • djelia/src/services/audio.py
  • djelia/src/services/transcription.py
  • djelia/src/services/translation.py
  • djelia/src/services/tts.py
  • djelia/utils/errors.py
  • docs/api.md
  • setup.py
💤 Files with no reviewable changes (1)
  • cookbook/main.py

Comment thread CONTRIBUTING.md
Comment thread djelia/src/services/translation.py
Comment thread setup.py
@sudoping01
sudoping01 merged commit be0fe40 into main Jul 25, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant