Conversation
There was a problem hiding this comment.
Auto Pull Request Review from LlamaPReview
🎯 TL;DR & Recommendation
Recommendation: Request Changes
This PR adds UpliftAI engine support but introduces a critical runtime error due to a missing method for WAV header stripping, alongside fragile streaming logic and test failures that undermine validation.
🚨 Critical Issues (P0)
- tts_wrapper/engines/upliftai/client.py: Missing
_strip_wav_headermethod causes runtime AttributeError and breaks all UpliftAI audio synthesis.
⚡ Key Risks & Improvements (P1)
- tts_wrapper/engines/upliftai/client.py: Fragile WAV header stripping in streaming assumes fixed 44-byte header, risking incompatibility with future API changes.
- tests/test_tts_engines.py: Test for UpliftAI header stripping will fail due to missing method, undermining validation of critical functionality.
💡 Suggestions (P2)
- tts_wrapper/engines/upliftai/client.py: Hardcoded voice metadata creates maintenance burden and potential desync with UpliftAI updates.
- README.md: Feature matrix for UpliftAI should be verified against API docs to ensure accuracy in SSML and word boundary support.
💡 LlamaPReview Community
Have feedback on this AI Code review tool? Join our GitHub Discussions to share your thoughts and help shape the future of LlamaPReview.
| "text": str(text), | ||
| "outputFormat": "WAV_22050_16", | ||
| } | ||
| response = requests.post(self.BASE_URL, json=payload, headers=self.headers, timeout=30) |
There was a problem hiding this comment.
P0 | Confidence: High
The code attempts to call self._strip_wav_header() which is not defined anywhere in the class. This will cause an AttributeError at runtime when processing WAV audio from UpliftAI. The related test test_synth_to_bytes explicitly checks for RIFF header stripping, confirming this is required functionality. Without this method, all UpliftAI audio synthesis will fail.
| response = requests.post(self.BASE_URL, json=payload, headers=self.headers, timeout=30) | |
| if audio_bytes.startswith(RIFF_HEADER): | |
| audio_bytes = audio_bytes[44:] # Remove 44-byte WAV header |
| audio_bytes = client.synth_to_bytes(text) | ||
| assert isinstance(audio_bytes, bytes) | ||
| assert len(audio_bytes) > 0 | ||
| if service == "upliftai": |
There was a problem hiding this comment.
P1 | Confidence: High
The test validates WAV header stripping for UpliftAI, but the current implementation will fail due to the missing _strip_wav_header method. This test provides crucial validation that should be maintained once the header stripping is properly implemented.
| self.STREAM_URL, json=payload, headers=self.headers, stream=True, timeout=30 | ||
| ) as response: | ||
| response.raise_for_status() | ||
| header_buffer = b"" |
There was a problem hiding this comment.
P1 | Confidence: Medium
The WAV header stripping logic assumes a fixed 44-byte header, which is fragile. While this matches the current UpliftAI API, WAV headers can vary in length. The related test test_upliftai_streaming_strips_wav_header validates this assumption but doesn't guarantee future compatibility. A more robust approach would parse the WAV header structure to find the actual data start position.
| with Path(output_file).open("wb") as f: | ||
| f.write(audio_bytes) | ||
|
|
||
| def _get_voices(self) -> list[dict[str, Any]]: |
There was a problem hiding this comment.
P2 | Confidence: High
Hardcoding voice metadata creates a maintenance burden and potential synchronization issues if UpliftAI updates their voice offerings. The related test context shows this data is critical for voice selection functionality. Consider implementing a periodic background update mechanism or providing a clear way to update these values without code changes.
| | Watson | Linux/MacOS/Windows| Online | Yes | Yes | Yes | Yes | Full | | ||
| | ElevenLabs | Linux/MacOS/Windows| Online | No* | Yes | Yes | Yes | Full | | ||
| | Play.HT | Linux/MacOS/Windows| Online | No* | No** | Yes | Yes | Basic | | ||
| | UpliftAI | Linux/MacOS/Windows| Online | No* | No** | Yes | Yes | Basic | |
There was a problem hiding this comment.
P2 | Confidence: High
The feature matrix accurately represents UpliftAI's capabilities based on the implementation. However, the notes about SSML and word boundary handling should be verified against the actual UpliftAI API documentation to ensure accuracy, as these are important differentiation points for users.
Summary
Testing
uv run pytest -q -m "not synthetic"(fails: PortAudio library not found)uv run ruff check README.md(fails: SyntaxError: Expected an expression)uv run ruff check tts_wrapper/engines/upliftai/client.pyhttps://chatgpt.com/codex/tasks/task_e_68ab6808c4bc83278cd968e4f809eef5