Содержание
Разберёмся, как генерировать изображения и аудио через OpenRouter одним ключом, а заодно распознавать речь и запускать генерацию видео. Идея та же, что и с текстом: один базовый адрес, один ключ, разные модальности. Не нужно поднимать отдельный Whisper-сервер, плодить SDK и держать по счёту у каждого поставщика. В статье пройдём по картинкам, аудио и видео с реальным кодом и настоящим выводом со стенда.
ИИ-агенты для оптимизации бизнес-процессов
Код курса
AGENT
Ближайшая дата курса
26 октября, 2026
Продолжительность
24 ак.часов
Стоимость обучения
66 000
Один ключ на все модальности
OpenRouter отдаёт мультимодальность через привычный OpenAI-совместимый интерфейс. Картинки приходят прямо в ответе чата, если попросить их через поле modalities. Аудио живёт на отдельных эндпоинтах: распознавание на /audio/transcriptions, синтез речи через потоковый ответ. Видео устроено асинхронно: вы отправляете задание и забираете результат по готовности. Ключ и базовый адрес при этом одни и те же.
Выгода такой единой точки простая. Не нужно держать отдельный SDK под каждую модальность и отдельный счёт у каждого поставщика. Авторизация одна, биллинг общий, а расход по всем модальностям виден в одном месте. Для команды это меньше секретов, меньше интеграций и меньше поводов что-то сломать.
Если вы ещё не выпустили ключ, начните со статьи про старт с OpenRouter. Как те же вызовы собираются без кода в n8n, показано в статье про low-code. Здесь идём глубже и по каждой модальности даём рабочий скрипт.
Дальше по каждой ветке этой схемы отдельный скрипт и живой результат.
Обнаружение возможностей моделей
Перед работой полезно понять, какая модель что умеет. Каталог по адресу /models отдаёт у каждой модели поля input_modalities и output_modalities. По ним фильтруем тех, кто выдаёт картинки, принимает аудио или синтезирует речь.
# протестировано на EU-ноде (AWS Stockholm) 2026-08-04: Python 3.12.3, httpx 0.28.1, OpenRouter API
# Обнаружение возможностей: какие модели умеют картинки, аудио на вход и аудио на выход.
import os
import httpx
# Ключ читаем из окружения, в код не вписываем.
key = os.environ["OPENROUTER_API_KEY"]
# Эндпоинт /models отдаёт весь каталог с полями модальностей в architecture.
models = httpx.get(
"https://openrouter.ai/api/v1/models",
headers={"Authorization": f"Bearer {key}"},
timeout=60,
).json()["data"]
def out(x, mod):
# True, если модель выдаёт указанную модальность на выход.
return mod in (x.get("architecture", {}).get("output_modalities") or [])
def inp(x, mod):
# True, если модель принимает указанную модальность на вход.
return mod in (x.get("architecture", {}).get("input_modalities") or [])
images = [x["id"] for x in models if out(x, "image")] # генерация картинок
audio_in = [x["id"] for x in models if inp(x, "audio")] # распознавание и понимание речи
audio_out = [x["id"] for x in models if out(x, "audio")] # синтез речи
print("картинки на выход:", len(images))
print("аудио на вход:", len(audio_in))
print("аудио на выход:", len(audio_out))
картинки на выход: 11 аудио на вход: 26 аудио на выход: 4
На момент проверки картинки выдавали 11 моделей, аудио на вход принимали 26, а синтез речи умели 4. Так вы не гадаете, а берёте модель под задачу по факту. Полный сценарий в файле detect_modalities.py. По традиции весь код используемый в статье выкладываем на наш GitHub репозиторий
Проверять возможности по каталогу важно по двум причинам. Модели появляются и исчезают, и жёстко зашитый в код идентификатор однажды перестанет работать. А ещё рядом лежит поле supported_parameters, по нему видно, кто умеет строгий JSON, tool calling или потоковый вывод. Это удобно подцепить в свой дашборд и обновлять список автоматически.
Картинки
Генерация картинки идёт через обычный chat, только с полем modalities. Модель кладёт изображение прямо в ответ как data-URL с base64 внутри. Остаётся раскодировать и сохранить.
# протестировано на EU-ноде (AWS Stockholm) 2026-08-04: Python 3.12.3, httpx 0.28.1, OpenRouter API
# Генерация картинки: chat с modalities возвращает изображение прямо в ответе.
import os, base64, httpx
key = os.environ["OPENROUTER_API_KEY"]
r = httpx.post(
"https://openrouter.ai/api/v1/chat/completions",
headers={"Authorization": f"Bearer {key}"},
timeout=120,
json={
"model": "google/gemini-2.5-flash-image", # модель с картинками на выход
"messages": [{"role": "user", "content": "Нарисуй простой логотип: синий круг на белом фоне"}],
"modalities": ["image", "text"],
},
).json()
# Картинка приходит в message.images как data-URL, отрезаем префикс и декодируем base64.
url = r["choices"][0]["message"]["images"][0]["image_url"]["url"]
png = base64.b64decode(url.split(",", 1)[1])
open("logo.png", "wb").write(png)
print(f"картинка сохранена: logo.png, {len(png)} байт, cost=${r['usage']['cost']:.6f}")
картинка сохранена: logo.png, 165799 байт, cost=$0.038719
Одним вызовом мы получили готовый PNG и увидели его стоимость. Отдельный сервис генерации не понадобился. Полный сценарий в файле image_generate.py.
Пара практических деталей. Картинку возвращают разные модели, у каждой свой стиль и своя цена за изображение, так что модель подбирают под задачу и бюджет. Промпт лучше держать конкретным, с одним смысловым центром, тогда результат стабильнее. Ответ приходит base64-строкой прямо в JSON, отдельного скачивания по ссылке не требуется.
Аудио
Со звуком работают две задачи: распознать речь в текст и синтезировать речь из текста. Обе идут через тот же ключ.
Транскрипция речи в текст
Распознавание живёт на эндпоинте /audio/transcriptions. Аудио отправляется как файл, модель распознавания указывается в поле model. В ответ приходит готовый текст.
# протестировано на EU-ноде (AWS Stockholm) 2026-08-04: Python 3.12.3, httpx 0.28.1, OpenRouter API
# Транскрипция: тот же ключ, эндпоинт /audio/transcriptions, файл speech.wav.
import os, httpx
key = os.environ["OPENROUTER_API_KEY"]
# Аудио отправляем как multipart-файл, модель распознавания задаём в data.
r = httpx.post(
"https://openrouter.ai/api/v1/audio/transcriptions",
headers={"Authorization": f"Bearer {key}"},
timeout=120,
files={"file": ("speech.wav", open("speech.wav", "rb"), "audio/wav")},
data={"model": "openai/gpt-4o-transcribe"},
).json()
# В ответе поле text содержит распознанную речь.
print("транскрипция:", r["text"])
транскрипция: Мультимодальность через один ключ.
Никакого отдельного Whisper-сервера поднимать не пришлось. Файл ушёл на общий эндпоинт и вернулся текстом. Полный сценарий в файле audio_transcribe.py.
Модель распознавания выбирается полем model. Мы взяли gpt-4o-transcribe, но на том же эндпоинте работает и whisper-1, они различаются точностью и ценой. Эндпоинт принимает обычные аудиоформаты, так что подойдёт файл из микрофона, звонка или из шага синтеза ниже. Для длинных записей разумно резать их на части и склеивать текст.
Синтез речи из текста
Синтез идёт обратной дорогой. Аудио-выход приходит потоком, поэтому запрос идёт со stream, а куски PCM16 мы собираем и заворачиваем в WAV. Файл speech.wav из этого шага и подаём потом на распознавание.
# протестировано на EU-ноде (AWS Stockholm) 2026-08-04: Python 3.12.3, httpx 0.28.1, OpenRouter API
# Синтез речи: аудио-выход идёт стримом (PCM16), собираем WAV.
import os, base64, json, wave, httpx
key = os.environ["OPENROUTER_API_KEY"]
pcm = bytearray() # буфер под сырые PCM16-семплы из стрима
# Аудио-выход требует stream=True. Читаем поток и копим аудио-куски.
with httpx.stream(
"POST",
"https://openrouter.ai/api/v1/chat/completions",
headers={"Authorization": f"Bearer {key}"},
timeout=180,
json={
"model": "openai/gpt-audio",
"modalities": ["text", "audio"],
"audio": {"voice": "alloy", "format": "pcm16"}, # голос и сырой формат
"stream": True,
"messages": [{"role": "user", "content": "Скажи ровно фразу: Мультимодальность через один ключ"}],
},
) as r:
for line in r.iter_lines():
if not line.startswith("data: "):
continue
chunk = line[6:]
if chunk.strip() == "[DONE]":
break
# Аудио-байты лежат в delta.audio.data как base64, накапливаем их.
au = json.loads(chunk)["choices"][0]["delta"].get("audio")
if au and au.get("data"):
pcm += base64.b64decode(au["data"])
# Оборачиваем сырой PCM16 в WAV: моно, 16 бит, 24 кГц.
with wave.open("speech.wav", "wb") as w:
w.setnchannels(1)
w.setsampwidth(2)
w.setframerate(24000)
w.writeframes(bytes(pcm))
print(f"речь сохранена: speech.wav, {len(pcm)} байт PCM16")
речь сохранена: speech.wav, 148800 байт PCM16
Так текст превращается в звук тем же ключом. Полный сценарий в файле speech_synthesize.py.
Голос и формат задаются в поле audio. Мы взяли голос alloy и сырой PCM16, потому что его удобно обернуть в WAV на лету. Стрим здесь не прихоть, а требование: аудио-выход приходит только потоком, поэтому куски приходится собирать по мере поступления. Если нужен готовый mp3, можно попросить его форматом и сохранить как есть.
Видео
Видео дороже и не мгновенно, поэтому эндпоинт /videos работает асинхронно. Вы отправляете задание и сразу получаете идентификатор и статус pending. Дальше опрашиваете статус, пока задание не завершится, и забираете ссылку на готовый ролик. Важно помнить, что генерация видео платная, в нашем прогоне короткий клип на модели wan обошёлся в полдоллара.
# протестировано на EU-ноде (AWS Stockholm) 2026-08-04: Python 3.12.3, httpx 0.28.1, OpenRouter API
# Видео: асинхронный поток. Отправляем задание, поллим статус, забираем ссылку на результат.
import os, time, httpx
key = os.environ["OPENROUTER_API_KEY"]
H = {"Authorization": f"Bearer {key}", "Content-Type": "application/json"}
# 1. Отправляем задание, сразу получаем job id и статус pending.
job = httpx.post(
"https://openrouter.ai/api/v1/videos",
headers=H,
timeout=30,
json={"model": "alibaba/wan-2.7", "prompt": "a calm blue circle on white background, 2 seconds"},
).json()
jid = job["id"]
print("submit:", job["status"], "| id:", jid)
# 2. Поллим статус, пока задание не завершится.
while True:
st = httpx.get(f"https://openrouter.ai/api/v1/videos/{jid}", headers={"Authorization": f"Bearer {key}"}, timeout=30).json()
print("status:", st["status"])
if st["status"] not in ("pending", "processing", "queued", "running"):
break
time.sleep(15)
# 3. Забираем ссылку на готовое видео и стоимость.
print("ссылка на видео:", st["unsigned_urls"][0])
print(f"стоимость: ${st['usage']['cost']}")
submit: pending | id: dih87y7RXNTD47sE4VEp status: pending status: completed ссылка на видео: https://openrouter.ai/api/v1/videos/dih87y7RXNTD47sE4VEp/content?index=0 стоимость: $0.5
Схема одинакова для любой видео-модели, меняется только идентификатор в запросе. Полный сценарий в файле video_generate.py.
С видео есть о чём помнить заранее. Оно платное и небыстрое, ценник у моделей разный, так что для проб берут короткий клип и модель подешевле. Опрос статуса не стоит делать слишком частым, хватает паузы в несколько секунд. Готовый ролик отдаётся по временной ссылке, которую нужно скачать или сразу переложить в своё хранилище.
Где это удобно на практике
Одна точка входа на все модальности упрощает и код, и эксплуатацию. Не нужно держать отдельный сервис распознавания, отдельный генератор картинок и отдельные ключи. Особенно это заметно в связке приёмов.
- Контент-конвейер. Текст пишет одна модель, обложку рисует другая, озвучку делает третья, и всё под одним ключом.
- Разбор звонков. Аудио уходит на транскрипцию, дальше текст обрабатывает обычный chat, без своего Whisper.
- Low-code сценарии. В n8n те же вызовы собираются узлами, что мы показывали в статье про low-code.
Общий знаменатель один: смена модальности это смена эндпоинта или поля, а не всей обвязки.
ИИ-агенты для оптимизации бизнес-процессов
Код курса
AGENT
Ближайшая дата курса
26 октября, 2026
Продолжительность
24 ак.часов
Стоимость обучения
66 000
Что дальше в серии
Итог такой. Через один ключ OpenRouter вы генерируете картинки, распознаёте и синтезируете речь и запускаете видео. Возможности модели проверяются по каталогу, а не наугад. Дальше встаёт вопрос, который особенно волнует команды и бизнес: приватность данных и контроль доступа. В следующей статье серии разберём ZDR и политики данных, свой ключ по схеме BYOK, рабочие пространства и ключи с ограничением по IP.



