Repository: ALERTua/styletts2-ukrainian-openai-tts-api
GitHub Docker Registry: https://github.com/ALERTua/styletts2-ukrainian-openai-tts-api/pkgs/container/styletts2-ukrainian-openai-tts-api
Provides OpenAI TTS API endpoints for the patriotyk/styletts2-ukrainian Gradio app, based on the Docker image ALERTua/patriotyk_styletts2_ukrainian_docker
I didn't mean to make this as a full-fledged product. It's just a quick and dirty solution to get the model working with Home Assistant until a better option arrives. By better option I mean a model of at least the same quality but in Piper-compatible format for easy Home Assistant usage.
AUTO_USE_VERBALIZER: automatically use the verbalizer (convert numbers to words) (default: 1)PUID: user ID the API container runs as (default: 1000)PGID: group ID the API container runs as (default: 1000)GRADIO_URL: URL of the Gradio Web UI (default: http://gradio:7860)UVICORN_PORT: port for the OpenAI TTS API (default: 8000)UVICORN_HOST: listen interface for the OpenAI TTS API (default: 0.0.0.0)GRADIO_PORT: port for Gradio Web UI (default: 7860)GRADIO_SERVER_NAME: listen interface for the Gradio App (default: 0.0.0.0)HF_TOKEN: Hugging Face token to raise the download limit for HuggingFace. Optional (default: None)UV_OVERRIDE: path INSIDE the gradio container to a file with package version overrides (e.g. PyTorch builds for older/newer GPUs). Put the file into the data volume (./tts_ukrainian_data/user_requirements.txt→/data/user_requirements.txt). Optional (default: None)UV_EXTRA_INDEX_URL: extra package index for the overrides above, e.g. https://download.pytorch.org/whl/cu126. Optional (default: None)UV_CACHE_DIR: uv download cache directory INSIDE the container. Defaults to/data/uv_cachein the gradio image (persistent, lives in the data volume). To share one uv cache between projects, bind a host directory over it in docker-compose.yml instead of changing this variable
The best way is to use the docker-compose.yml
Don't forget to create .env from .env.example.
You can also deploy the Gradio App the way you like and just point this application to it using the GRADIO_URL environment variable.
- tag
latestuses ~110 MiB of RAM
- A separate endpoint that lists all voices
- a separate verbalization endpoint
- Add https://github.com/sfortis/openai_tts integration to your Home Assistant instance.
- Provide it with the url to this container UVICORN forwarded port.
- You can leave the API key empty, as the endpoint does not check for it.
- Use one of these models: multi, single
- Use one of the voices available: https://huggingface.co/spaces/patriotyk/styletts2-ukrainian/tree/main/voices (filename without .pt). E.g.
Марина Панас - If a stress is wrong use
`symbol before a syllable to stress it, e.g.русн`я. - The model handles short messages poorly so at least end each syntax with a dot.
action: tts.speak
data:
media_player_entity_id: media_player.office_speaker
message: тестове повідомлення 123.
cache: false
target:
entity_id: tts.styletts2- Add the built in OpenAI Text-to-speech provider.
- Set the model to
multito pick a voice per message, or tosinglefor the built in voice. The provider offerstts-1by default, which this API rejects. - Set the API endpoint to this container, including the
/v1path, e.g.http://127.0.0.1:8000/v1. The provider appends/audio/speechand/audio/voicesto it. - You can leave the API key empty, as the endpoint does not check for it.
- The provider always asks for the
mp3response format, which this API supports. - The voices are discovered through
GET /v1/audio/voices, so the voices override in the provider options can stay empty. With thesinglemodel put one name there instead, otherwise the provider shows one entry per voice and every one of them sounds the same.
Endpoint: POST /v1/audio/speech
This endpoint synthesizes speech from the given text using the specified voice and parameters.
Example Request:
#!/bin/bash
curl -X POST "http://127.0.0.1:8000/v1/audio/speech" \
-H "accept: audio/wav" \
-H "Content-Type: application/json" \
-d '{
"input": "Русн+я вже майже вся подохла. Залишилося ще трохи почекати, і перемога буде за нами.",
"voice": "Марина Панас",
"speed": 1.0,
"verbalize": 1
}'# powershell
$headers = @{
"Content-Type" = "application/json"
}
Invoke-RestMethod -Uri 'http://127.0.0.1:8000/v1/audio/speech' `
-Method POST `
-Headers $headers `
-Body '{
"input": "Зустр`іч признач`ена на 15:30 12.05.2025 у конференц-зал`і №3."
}' `
-OutFile 'output.mp3'
explorer.exe output.mp3- model (string): model name: multi or single
- input (string): The text to generate audio for.
- voice (string): The voice to use for synthesis.
- speed (float): The speed of the speech. Default is
1.0. response_format (string): The format of the audio output. Supported formats arewavandmp3. Default iswav.sample_rate (int): The sample rate of the audio. Default is24000.
Endpoint: GET /v1/audio/voices
Lists the voices the multi speaker model accepts. Every name in the response is a valid value for the voice field of the speech endpoint. The single speaker model has one built in voice and ignores that field.
The names are read from the Gradio app description, which the Gradio client fetches once, while this application connects. A voice added to the Gradio app therefore shows up only after this application restarts. When the description cannot be read, the response holds the default voice alone.
Example Request:
curl "http://127.0.0.1:8000/v1/audio/voices"Example Response:
{"voices": ["Інна Гелевера", "Анастасія Павленко", "Марина Панас"]}The example above is shortened. The real response holds every voice the multi speaker model has.
- If the verbalizer is off, the model does not handle numbers(!) so make sure to replace them with words.
# pip install num2words
cases = [
"nominative",
"genitive",
"dative",
"accusative",
"instrumental",
"locative"
]
a = num2words(42, lang='uk', to='ordinal') # сорок другий
c = num2words(42, lang='uk', to='cardinal', gender='feminine', case="genitive") # сорока двох
d = num2words(1442, lang='uk', to='year') # одна тисяча чотириста сорок два
e = num2words(1444.10, lang='uk', to='currency', currency='USD', cents=False, separator='', adjective=True) # одна тисяча чотириста сорок чотири долари 10 центів- The verbalization is tricky and may not work as expected. For example:
це тестове повідомлення номер 2012, яке було видане в 1954 роціwill be verbalized asце тестове повідомлення номер дві тисячі дванадцятого року, яке було видане в тисяча дев'ятсот п'ятдесят четвертому році
Я готовий розширити це readme будь-якою корисною інформацією, лише скажіть, чого вам не вистачає.
