Feat/OpenAI style api - #13
Conversation
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.
…nd some ops utils
📝 WalkthroughWalkthroughThe 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. ChangesSDK API modernization
Cookbook test suite
Documentation and packaging
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
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
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 winMake retryable transcription uploads idempotent.
Djelia._make_requestis decorated withtenacity.retry, but the synchronous transcribe path passes a file handle directly infiles=. 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 tofiles=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 winAll new resource
createpaths serialize with pydantic v1's deprecated.dict(). Withpydantic>=2.7.0declared insetup.py, each call emits aDeprecationWarning; replace withmodel_dump().
djelia/src/services/translation.py#L30-L37:json=request.model_dump()inTranslations.create.djelia/src/services/translation.py#L63-L70: same inAsyncTranslations.create.djelia/src/services/tts.py#L73-L77: same inSpeech.create(and the adjacent_streamcall).djelia/src/services/tts.py#L142-L146: same inAsyncSpeech.create(and its_streamcall).🤖 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 winRun cleanup in a
finallyblock.
cleanup()is skipped if a test phase raises or the user interrupts (__main__.pycatchesKeyboardInterruptat 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 winStreaming iterators are abandoned at early
breakinstead of closed. Every bounded streaming test exits its loop withbreak, 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 incontextlib.closing(andawait generator.aclose()after the break instreaming_transcription_async, lines 255-266).cookbook/cookbook.py#L328-L339: wrap the sync TTS chunk stream incontextlib.closing(andawait generator.aclose()after the break instreaming_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 winDrop the
sys.pathmanipulation.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 — thesys.path.appendis dead weight and forces imports below top-of-file (an E402 candidate for the repo'sruffgate).♻️ 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
📒 Files selected for processing (19)
.github/pull_request_template.md.gitignoreCONTRIBUTING.mdREADME.mdcookbook/__main__.pycookbook/config.pycookbook/cookbook.pycookbook/main.pycookbook/utils.pydjelia/models/models.pydjelia/src/client/client.pydjelia/src/services/__init__.pydjelia/src/services/audio.pydjelia/src/services/transcription.pydjelia/src/services/translation.pydjelia/src/services/tts.pydjelia/utils/errors.pydocs/api.mdsetup.py
💤 Files with no reviewable changes (1)
- cookbook/main.py
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_valuenormalizer and new error messages change how invalid inputs and API errors surface, andAPIErrornow always carriesstatus_codeandmessage.Related Issue(s)
None linked.
Changes Made
Versions.from_valuenormalizer and new error messages for invalid version input.APIErrorconstruction now passesstatus_codeandmessageinstead of a bare exception.Translationsresource (OpenAI-style), deprecatedTranslation.Transcriptionsresource (OpenAI-style), deprecatedTranscription.Speechresource (OpenAI-style), deprecatedTTS.Audionamespace groupingtranscriptionsandspeech.Settingsis now always built regardless of which API style is used.Testing
Versions.from_valueand the newAPIErrorconstruction path before merge.Checklist
ruff,isort, and optionallyblack)cookbookmoduleREADME.md, API reference, cookbook run instructions)python -m cookbook.main)ruff check .andisort . --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, repeatedupdate 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
@sudoping01for review. Thanks for contributing to Djelia Python SDK! ❤️🇲🇱Summary by CodeRabbit
New Features
Documentation
Chores