Skip to content

feat(google): add Cloud TTS API and migrate speech generation#90

Merged
Kamilbenkirane merged 5 commits into
mainfrom
api/google_cloud_tts
Dec 19, 2025
Merged

feat(google): add Cloud TTS API and migrate speech generation#90
Kamilbenkirane merged 5 commits into
mainfrom
api/google_cloud_tts

Conversation

@Kamilbenkirane

@Kamilbenkirane Kamilbenkirane commented Dec 19, 2025

Copy link
Copy Markdown
Member

Summary

  • New authentication mechanism: Add GoogleADC class for OAuth/Application Default Credentials
  • New API package: Add Google Cloud TTS API client with text:synthesize endpoint
  • Enhanced TTS capabilities: 30 voices, 20 languages, 4 output formats for Google speech generation
  • API migration: Migrate from Gemini content generation API to dedicated Cloud TTS API
  • Fix: Add missing httpx import in Gradium client

Provider Package (celeste-google)

New Authentication (GoogleADC)

  • OAuth 2.0 authentication using Google Application Default Credentials
  • Automatic credential discovery from:
    1. GOOGLE_APPLICATION_CREDENTIALS environment variable
    2. gcloud auth application-default login
    3. Attached service account (on GCP)
  • Token refresh via google-auth library with requests transport
  • Quota project header support (x-goog-user-project)

Cloud TTS API (GoogleCloudTTSClient)

  • HTTP POST to https://texttospeech.googleapis.com/v1/text:synthesize
  • Response format: Base64-encoded audio in audioContent field
  • Automatic auth override to use GoogleADC (Cloud TTS requires OAuth, not API key)
  • Endpoints enum for future expansion (LIST_VOICES, CREATE_LONG_AUDIO, etc.)

Parameter Mappers

  • VoiceMapper: Maps voice ID to voice.name field
  • LanguageMapper: Maps Language enum to BCP-47 locale codes (voice.languageCode)
  • PromptMapper: Maps prompt to input.prompt for style control
  • AudioEncodingMapper: Maps AudioMimeType to Cloud TTS encoding strings

Configuration

  • API base URL: https://texttospeech.googleapis.com
  • Endpoint: /v1/text:synthesize
  • Entry point: celeste.providers for auth type registration

Dependencies

  • Add requests for google-auth transport layer

Capability Integration (celeste-speech-generation)

Client Migration

  • GoogleSpeechGenerationClient now inherits from GoogleCloudTTSClient mixin
  • Uses Cloud TTS request format (input.text, voice.modelName, audioConfig)
  • Response parsing extracts base64 audio and decodes to bytes

Model Updates

  • Model IDs changed: gemini-2.5-flash-preview-ttsgemini-2.5-flash-tts
  • Model IDs changed: gemini-2.5-pro-preview-ttsgemini-2.5-pro-tts
  • Added LANGUAGE parameter constraint with 20 supported languages
  • Added OUTPUT_FORMAT parameter constraint (MP3, WAV, OGG, PCM)

Voice Definitions (30 voices)

All voices now include personality descriptions:

  • Zephyr (Bright), Puck (Upbeat), Charon (Informative)
  • Kore (Firm), Fenrir (Excitable), Leda (Youthful)
  • Orus (Firm), Aoede (Breezy), Callirrhoe (Easy-going)
  • ... and 21 more with unique characteristics

Language Support (20 languages)

Uses Language enum (ISO 639-1) with BCP-47 locale mapping:

Language Locale
Arabic ar-EG
German de-DE
English en-US
Spanish es-US
French fr-FR
Hindi hi-IN
Indonesian id-ID
Italian it-IT
Japanese ja-JP
Korean ko-KR
Portuguese pt-BR
Russian ru-RU
Dutch nl-NL
Polish pl-PL
Thai th-TH
Turkish tr-TR
Vietnamese vi-VN
Romanian ro-RO
Ukrainian uk-UA
Tamil ta-IN

Output Formats

AudioMimeType Cloud TTS Encoding
audio/mpeg MP3
audio/wav LINEAR16
audio/ogg OGG_OPUS
audio/pcm PCM

Fixes

Gradium Client

  • Add missing httpx import required for _make_request return type annotation

Provider Package Cleanup

  • Remove empty __all__ from celeste_openai/__init__.py
  • Remove empty __all__ from celeste_xai/__init__.py

Code Statistics

  • Files changed: 16
  • Lines added: 540
  • Lines removed: 220
  • Net change: +320 lines

Breaking Changes

  • Model IDs renamed: Applications using gemini-2.5-flash-preview-tts or gemini-2.5-pro-preview-tts must update to gemini-2.5-flash-tts / gemini-2.5-pro-tts
  • Authentication: Google TTS now requires OAuth via ADC instead of API key. Run gcloud auth application-default login to authenticate.

Test Plan

  • make ci passes (linting, formatting, type checking, security, unit tests)
  • Integration tests pass for all 4 speech generation providers:
    • OpenAI (tts-1)
    • Google (gemini-2.5-flash-tts)
    • ElevenLabs (eleven_flash_v2_5)
    • Gradium (default)
  • Streaming test passes for ElevenLabs

- Add celeste.providers entry point for auth type registration
- Add register_provider() to register GoogleADC auth type
- Add requests dependency for google-auth transport
- Remove unused type ignore comments in cloud_tts client
- Switch from Gemini content generation to Cloud TTS synthesize endpoint
- Update model IDs to gemini-2.5-flash-tts and gemini-2.5-pro-tts
- Add language and output_format parameter support
- Update voice definitions with personality descriptions
- Use Language enum instead of locale strings for multilingual support
- Add GoogleADC authentication class for OAuth/ADC
- Add Cloud TTS API config and endpoints
- Add parameter mappers for voice, language, prompt, and audio encoding
@claude

claude Bot commented Dec 19, 2025

Copy link
Copy Markdown

PR Review: Google Cloud TTS API Migration

Summary

This PR successfully migrates Google speech generation from the Gemini content API to the Cloud TTS API, adding OAuth/ADC authentication and expanding parameter support. The implementation is well-structured with good separation of concerns.

Code Quality & Best Practices ✅

Strengths

  • Clean architecture: The GoogleCloudTTSClient mixin pattern is well-designed, providing shared implementation while allowing capability-specific extensions
  • Good separation of concerns: Authentication, parameter mapping, and API clients are properly separated
  • Entry point registration: Good use of Python entry points for provider registration
  • Type hints: Comprehensive type annotations throughout
  • Documentation: Clear docstrings explaining the mixin pattern and auth flow

Minor Issues

  1. Hardcoded default in LanguageMapper (packages/providers/google/src/celeste_google/cloud_tts/parameters.py:42)

    if validated_value is None:
        request.setdefault("voice", {})["languageCode"] = "fr-FR"  # Why French?
        return request
    • The hardcoded "fr-FR" default seems arbitrary. Should this be "en-US" or configurable?
  2. Inconsistent fallback behavior (packages/providers/google/src/celeste_google/cloud_tts/parameters.py:95)

    encoding = self.encoding_map.get(validated_value, "MP3")  # Silent fallback
    • Silent fallback to MP3 could mask issues. Consider logging or raising an error for unexpected values.
  3. Missing validation in _get_mime_type (packages/capabilities/speech-generation/src/celeste_speech_generation/providers/google/client.py:63-67)

    def _get_mime_type(self, output_format: str | None) -> AudioMimeType:
        if output_format is None:
            return AudioMimeType.MP3
        return AudioMimeType(output_format)  # Could raise ValueError
    • No try/except for invalid AudioMimeType conversion. The AudioMimeType constructor could raise ValueError for invalid formats.

Potential Bugs 🐛

  1. Token refresh race condition (packages/providers/google/src/celeste_google/auth.py:45-46)

    if not self._credentials.valid:
        self._credentials.refresh(self._auth_request)
    • Not thread-safe. If multiple requests occur simultaneously, token refresh could race. Consider adding a lock.
    • The _credentials, _auth_request, and _project are instance attributes that get mutated, which could cause issues in concurrent environments.
  2. Mutable class variable (packages/providers/google/src/celeste_google/auth.py:19-26)

    scopes: ClassVar[list[str]] = ["https://www.googleapis.com/auth/cloud-platform"]
    project_id: str | None = None
    
    model_config: ClassVar[ConfigDict] = ConfigDict(arbitrary_types_allowed=True)
    
    _credentials: Any = None
    _auth_request: Any = None
    _project: str | None = None
    • _credentials, _auth_request, _project are defined as class-level defaults. While they get set per-instance, this pattern could cause confusion. Use __init__ or field defaults instead.
  3. Missing language support check (packages/capabilities/speech-generation/src/celeste_speech_generation/providers/google/parameters.py:27-28)

    class LanguageMapper(_LanguageMapper):
        name = SpeechGenerationParameter.LANGUAGE
        locale_map = LOCALE_MAP
    • If a user passes a supported Language enum value that's not in LOCALE_MAP, the request will silently fail or use the fallback. Should validate or raise an error.

Performance Considerations ⚡

  1. Token caching is good: The authentication caches tokens and only refreshes when invalid, which is efficient.

  2. Base64 encoding overhead: Audio is returned as base64 and then decoded. This is standard for JSON APIs but adds ~33% overhead. Not much you can do here, but worth noting.

  3. No streaming support: The models correctly indicate streaming=False. Users should be aware that entire audio must be generated before returning.

Security Concerns 🔒

Good Security Practices

  • ✅ Uses official google-auth library for ADC
  • ✅ Follows OAuth2 best practices
  • ✅ Scopes are properly defined
  • ✅ No hardcoded credentials

Concerns

  1. Credential storage in memory (packages/providers/google/src/celeste_google/auth.py:24-26)

    _credentials: Any = None
    _auth_request: Any = None
    • Credentials are stored in plain Python objects. While necessary for operation, ensure these objects are not logged or serialized.
    • The use of Any type masks what's actually being stored. Consider being more specific.
  2. Error messages could leak info: Ensure that error messages from the API don't expose sensitive project IDs or credentials. The current implementation looks safe, but worth monitoring.

  3. No timeout configuration: HTTP requests don't specify timeouts. Consider adding configurable timeouts to prevent hanging connections.

Test Coverage 📊

Current Coverage

  • ✅ Integration test updated with new model IDs
  • ✅ Test includes speed parameter

Missing Coverage

  1. No unit tests for new code: The Cloud TTS client, auth class, and parameter mappers lack unit tests
  2. No error handling tests: What happens when:
    • OAuth token refresh fails?
    • Invalid language is provided?
    • API returns error response?
    • Network timeout occurs?
  3. No tests for parameter mappers: Should verify LOCALE_MAP and ENCODING_MAP translations
  4. No tests for edge cases:
    • Empty text input
    • Very long text input
    • Unsupported language/format combinations
    • Invalid voice names

Recommendations

# Example test cases to add:
- test_google_adc_token_refresh()
- test_google_adc_invalid_credentials()
- test_language_mapper_unsupported_language()
- test_output_format_mapper_invalid_format()
- test_voice_mapper_with_invalid_voice()
- test_concurrent_token_refresh()  # For race condition

Additional Observations

Positive Changes

  1. Better parameter support: Added language and output_format support
  2. Voice descriptions: Voice names now include personality traits (e.g., "Zephyr (Bright)")
  3. Cleaner API: Migrated from the content generation API to the dedicated TTS API

Breaking Changes

⚠️ Model ID changes:

  • gemini-2.5-flash-preview-ttsgemini-2.5-flash-tts
  • gemini-2.5-pro-preview-ttsgemini-2.5-pro-tts

Users will need to update their code. Consider documenting this in release notes.

Documentation Needed

  1. Migration guide for users on the old API
  2. How to set up ADC credentials
  3. Supported language/format combinations
  4. Voice personality guide

Recommendations

High Priority

  1. Fix the hardcoded "fr-FR" default to something more sensible (e.g., "en-US")
  2. Add thread-safety to token refresh with asyncio.Lock
  3. Add error handling for invalid AudioMimeType conversion
  4. Add unit tests for new auth and parameter mapping code

Medium Priority

  1. Add logging for parameter fallbacks
  2. Add timeout configuration for HTTP requests
  3. Add validation for language support in LOCALE_MAP
  4. Improve type hints for _credentials (avoid Any)

Low Priority

  1. Add comprehensive error handling tests
  2. Document breaking changes and migration path
  3. Consider adding a deprecation warning for old model IDs

Verdict

This is a solid PR with clean architecture and good separation of concerns. The migration is well-executed, but needs:

  • Unit tests for the new code
  • A few bug fixes (threading, hardcoded defaults, error handling)
  • Better validation and error messages

Once the high-priority items are addressed, this will be ready to merge. Great work overall! 🚀

@Kamilbenkirane Kamilbenkirane merged commit f0e952d into main Dec 19, 2025
11 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