В AI Gateway доступны модели для синтеза и распознавания речи. С их помощью можно озвучивать тексты, добавлять голосовые ответы в приложения, расшифровывать звонки и преобразовывать другие аудиозаписи в текст.
openai/gpt-4o-mini-tts. Для распознавания речи, или STT (Speech-to-Text), доступны модели openai/gpt-4o-mini-transcribe и openai/gpt-4o-transcribe
Возможности синтеза речи
Модель openai/gpt-4o-mini-tts создает аудио на основе переданного текста. Модель поддерживает русский и другие языки, а также позволяет управлять звучанием речи с помощью инструкции: например, задать темп, интонацию, эмоциональную окраску или тон.
Для генерации можно выбрать один из голосов:
alloy;ash;ballad;coral;echo;fable;nova;onyx;sage;shimmer;verse;marin;cedar.
Послушать примеры голосов можно на сайте OpenAI.fm.
Поддерживаются следующие форматы аудио:
mp3— используется по умолчанию;opus— подходит для потоковой передачи и голосовой связи;aac— формат сжатия, который используется, например, на мобильных устройствах;flac— формат сжатия без потери качества;wav— несжатое аудио;pcm— необработанные аудиоданные без заголовка файла.
Форматы wav и pcm подходят для сценариев, в которых важна минимальная задержка. AI Gateway также поддерживает потоковую передачу: воспроизведение можно начать до завершения генерации всего аудио.
Возможности транскрибации
Модели openai/gpt-4o-mini-transcribe и openai/gpt-4o-transcribe преобразуют речь из аудиофайла в текст. Язык записи можно определить автоматически или указать в запросе явно.
Для транскрибации можно использовать файлы следующих форматов: mp3, mp4, mpeg, mpga, m4a, wav и webm. Результат можно получить в формате json или обычного текста.
В запросе также можно передать параметр prompt с дополнительным контекстом. Он помогает модели правильно распознавать:
- имена и фамилии;
- названия продуктов и компаний;
- аббревиатуры;
- профессиональные термины;
- слова, написание которых сложно определить только по произношению.
Если запись разделена на несколько фрагментов, в prompt можно передавать текст предыдущего фрагмента. Это помогает модели сохранять контекст между запросами.
Как считаются токены
В аудиомоделях учитываются текстовые токены и аудиотокены. Текстовые токены зависят от объема текста, а аудиотокены — от продолжительности аудио.
При синтезе речи (TTS-модели):
-
input_tokens— текстовые токены переданного текста; -
output_tokens— аудиотокены созданной записи. Одна секунда аудио считается за один аудиотокен.
Например, если модель создала аудио продолжительностью четыре секунды, будет учтено четыре выходных аудиотокена. Количество входных текстовых токенов зависит от длины исходного текста.
При транскрибации (STT-модели):
-
input_tokens— аудиотокены исходной записи: примерно 10 токенов за одну секунду; -
output_tokens— текстовые токены расшифровки.
Например, для записи продолжительностью 10 секунд будет потрачено примерно 100 входных аудиотокенов. Количество выходных текстовых токенов зависит от длины расшифровки.
Ограничения и особенности
-
Аудиомодели доступны только в AI Gateway.
-
Потоковая генерация и воспроизведение речи поддерживаются только для TTS.
-
Транскрибация звука с микрофона в реальном времени не поддерживается.
-
Для транскрибации доступны форматы ответа
jsonиplain text.
Подключение
Для работы с аудиомоделями нужен API-ключ AI Gateway. Создать его можно в разделе «AI-агенты» во вкладке «AI Gateway» → «API-ключи».
В примерах используется переменная окружения TIMEWEB_AI_TOKEN, чтобы не указывать ключ непосредственно в коде. В Linux и macOS ее можно задать командой:
export TIMEWEB_AI_TOKEN="ваш_API-ключ"
Установите библиотеку OpenAI:
pip install openai
Создайте клиент и укажите базовый URL AI Gateway:
import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ["TIMEWEB_AI_TOKEN"],
base_url="https://api.timeweb.ai/v1",
)
Синтез речи
Для синтеза речи используется метод audio.speech.create().
Создание аудиофайла
В следующем примере модель озвучивает текст и сохраняет результат в файл speech.mp3:
import os
from pathlib import Path
from openai import OpenAI
client = OpenAI(
api_key=os.environ["TIMEWEB_AI_TOKEN"],
base_url="https://api.timeweb.ai/v1",
)
speech_file = Path("speech.mp3")
with client.audio.speech.with_streaming_response.create(
model="openai/gpt-4o-mini-tts",
voice="shimmer",
input="Привет! Это проверка синтеза речи.",
instructions="Говори спокойно и доброжелательно.",
response_format="mp3",
) as response:
response.stream_to_file(speech_file)
Параметры:
-
model— модель для синтеза речи; -
voice— голос, которым будет озвучен текст; -
input— текст для озвучивания; -
instructions— инструкция, определяющая манеру речи; -
response_format— формат созданного аудио.
Потоковое воспроизведение
Чтобы начать воспроизведение до завершения генерации, используйте асинхронный клиент и LocalAudioPlayer.
Для работы LocalAudioPlayer установите библиотеки numpy и sounddevice:
pip install numpy sounddevice
Пример потокового воспроизведения:
import asyncio
import os
from openai import AsyncOpenAI
from openai.helpers import LocalAudioPlayer
client = AsyncOpenAI(
api_key=os.environ["TIMEWEB_AI_TOKEN"],
base_url="https://api.timeweb.ai/v1",
)
async def main() -> None:
async with client.audio.speech.with_streaming_response.create(
model="openai/gpt-4o-mini-tts",
voice="shimmer",
input="Привет! Это проверка потокового синтеза речи.",
response_format="pcm",
) as response:
await LocalAudioPlayer().play(response)
if __name__ == "__main__":
asyncio.run(main())
В примере используется формат pcm, поэтому аудио можно воспроизводить по мере получения данных от модели.
Транскрибация аудио
Для преобразования речи в текст используется метод audio.transcriptions.create().
Транскрибация из файла
В следующем примере модель расшифровывает русскую речь из файла audio.mp3:
import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ["TIMEWEB_AI_TOKEN"],
base_url="https://api.timeweb.ai/v1",
)
with open("audio.mp3", "rb") as audio_file:
transcription = client.audio.transcriptions.create(
model="openai/gpt-4o-mini-transcribe",
file=audio_file,
language="ru",
response_format="json",
)
print(transcription.text)
Параметры:
-
model— модель для транскрибации; -
file— аудиофайл, который нужно преобразовать в текст; -
language— язык аудио. Если параметр не указан, модель определит язык автоматически; -
response_format— формат ответа:jsonилиtext.
Передача контекста
С помощью параметра prompt можно передать модели термины, дополнительный контекст и требования к результату. Также, можно попросить ее удалить слова-паразиты:
import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ["TIMEWEB_AI_TOKEN"],
base_url="https://api.timeweb.ai/v1",
)
with open("meeting.mp3", "rb") as audio_file:
transcription = client.audio.transcriptions.create(
model="openai/gpt-4o-transcribe",
file=audio_file,
language="ru",
response_format="json",
prompt=(
"Запись посвящена Kubernetes и облачной инфраструктуре. "
"В разговоре упоминаются Timeweb Cloud, kubectl и Ingress. "
"Удали из расшифровки слова-паразиты и междометия. "
"Сохрани смысл и не перефразируй остальную речь."
),
)
print(transcription.text)
В примере prompt помогает модели распознать технические термины и задает требования к готовому тексту. Параметр не заменяет содержимое аудио, а дополняет его контекстом и инструкциями.
Транскрибация звука с микрофона
AI Gateway не поддерживает транскрибацию в реальном времени. Для приближенного к реальному времени результата можно записывать звук короткими фрагментами и отправлять каждый фрагмент в API отдельно.
Установите дополнительные библиотеки:
pip install numpy sounddevice
Пример ниже записывает звук фрагментами по три секунды. Между соседними фрагментами добавляется небольшое перекрытие, чтобы модель не потеряла слова на границе записи:
import io
import os
import queue
import threading
import time
import wave
from dataclasses import dataclass
import numpy as np
import sounddevice as sd
from openai import OpenAI
MODEL = "openai/gpt-4o-mini-transcribe"
SAMPLE_RATE = 16_000
CHANNELS = 1
SAMPLE_WIDTH_BYTES = 2
CHUNK_SECONDS = 3.0
OVERLAP_SECONDS = 0.4
FRAMES_PER_BLOCK = 1_600
client = OpenAI(
api_key=os.environ["TIMEWEB_AI_TOKEN"],
base_url="https://api.timeweb.ai/v1",
)
@dataclass
class AudioChunk:
index: int
pcm_data: bytes
audio_queue: queue.Queue[AudioChunk | None] = queue.Queue(maxsize=10)
stop_event = threading.Event()
def pcm_to_wav_bytes(pcm_data: bytes) -> io.BytesIO:
wav_buffer = io.BytesIO()
with wave.open(wav_buffer, "wb") as wav_file:
wav_file.setnchannels(CHANNELS)
wav_file.setsampwidth(SAMPLE_WIDTH_BYTES)
wav_file.setframerate(SAMPLE_RATE)
wav_file.writeframes(pcm_data)
wav_buffer.seek(0)
wav_buffer.name = "chunk.wav"
return wav_buffer
def transcription_worker() -> None:
while True:
chunk = audio_queue.get()
if chunk is None:
audio_queue.task_done()
break
try:
wav_file = pcm_to_wav_bytes(chunk.pcm_data)
started_at = time.perf_counter()
transcription = client.audio.transcriptions.create(
model=MODEL,
file=wav_file,
language="ru",
response_format="json",
)
elapsed = time.perf_counter() - started_at
text = getattr(transcription, "text", "").strip()
if text:
print(
f"\n[{chunk.index:04d}] "
f"({elapsed:.2f} сек.) {text}",
flush=True,
)
else:
print(
f"\n[{chunk.index:04d}] "
f"({elapsed:.2f} сек.) [тишина]",
flush=True,
)
except Exception as exc:
print(
f"\nОшибка транскрибации фрагмента "
f"{chunk.index}: {exc}",
flush=True,
)
finally:
audio_queue.task_done()
def record_microphone() -> None:
chunk_samples = int(SAMPLE_RATE * CHUNK_SECONDS)
overlap_samples = int(SAMPLE_RATE * OVERLAP_SECONDS)
accumulated = np.empty(0, dtype=np.int16)
previous_tail = np.empty(0, dtype=np.int16)
chunk_index = 1
print("Говорите. Для остановки нажмите Ctrl+C.")
with sd.InputStream(
samplerate=SAMPLE_RATE,
channels=CHANNELS,
dtype="int16",
blocksize=FRAMES_PER_BLOCK,
) as stream:
while not stop_event.is_set():
block, overflowed = stream.read(FRAMES_PER_BLOCK)
if overflowed:
print("\nПредупреждение: переполнение аудиобуфера.")
accumulated = np.concatenate((accumulated, block[:, 0]))
while len(accumulated) >= chunk_samples:
current_samples = accumulated[:chunk_samples]
accumulated = accumulated[chunk_samples:]
if len(previous_tail) > 0:
samples_to_send = np.concatenate(
(previous_tail, current_samples)
)
else:
samples_to_send = current_samples
if overlap_samples > 0:
previous_tail = current_samples[-overlap_samples:].copy()
else:
previous_tail = np.empty(0, dtype=np.int16)
audio_chunk = AudioChunk(
index=chunk_index,
pcm_data=samples_to_send.astype(
"<i2",
copy=False,
).tobytes(),
)
try:
audio_queue.put(audio_chunk, timeout=1)
print(".", end="", flush=True)
except queue.Full:
print(
"\nОчередь заполнена: фрагмент пропущен. "
"Увеличьте CHUNK_SECONDS или проверьте скорость API."
)
chunk_index += 1
if len(accumulated) >= SAMPLE_RATE // 2:
if len(previous_tail) > 0:
accumulated = np.concatenate(
(previous_tail, accumulated)
)
audio_queue.put(
AudioChunk(
index=chunk_index,
pcm_data=accumulated.astype(
"<i2",
copy=False,
).tobytes(),
)
)
def main() -> None:
worker = threading.Thread(
target=transcription_worker,
daemon=True,
)
worker.start()
try:
record_microphone()
except KeyboardInterrupt:
print("\nОстанавливаю запись...")
stop_event.set()
finally:
audio_queue.put(None)
audio_queue.join()
worker.join(timeout=5)
print("Готово.")
if __name__ == "__main__":
main()
Значения CHUNK_SECONDS и OVERLAP_SECONDS можно изменить. Более короткие фрагменты уменьшают задержку, но увеличивают количество запросов. Из-за перекрытия в результате могут повторяться отдельные слова — при необходимости удаляйте такие повторы при дальнейшей обработке текста.