Генерация изображений
Генерация и редактирование изображений: гибридные модели через /v1/chat/completions с modalities, премиум-модели через /v1/images/generations с input_references.
Hubris генерирует изображения двумя путями — какой использовать, зависит от модели:
| Линейка моделей | Эндпоинт | Вход для редактирования |
|---|---|---|
| Google Gemini *-image (Nano Banana), OpenAI GPT-5 Image | POST /v1/chat/completions | картинка в messages (как во Vision) |
| FLUX.2, Recraft, Seedream, Riverflow, Grok Imagine | POST /v1/images/generations | поле input_references |
Первый путь — «диалоговый»: модель отвечает и текстом, и картинкой, умеет пошагово дорабатывать результат в переписке. Второй — классический Images API в формате OpenAI: один запрос → готовые изображения, фиксированная цена за штуку. Полный список моделей — фильтр «Output: image» в каталоге; на карточке модели видно, как она тарифицируется: токенами (chat-путь) или за изображение (images-путь).
Генерация через chat completions
Передайте modalities и модель с поддержкой image-output:
curl -s https://api.hubris.pw/v1/chat/completions \
-H "Authorization: Bearer sk-gw-..." \
-H "Content-Type: application/json" \
-d '{
"model": "google/gemini-2.5-flash-image",
"modalities": ["image", "text"],
"messages": [{"role": "user", "content": "Закат над горами в стиле Гибли"}]
}'Картинка возвращается как base64 data URL в choices[0].message.images[0].image_url.url:
{
"id": "chatcmpl-...",
"model": "google/gemini-2.5-flash-image",
"choices": [
{
"message": {
"role": "assistant",
"content": "Готово!",
"images": [
{
"type": "image_url",
"image_url": { "url": "data:image/png;base64,iVBORw0KGgo..." }
}
]
}
}
],
"usage": { "prompt_tokens": 18, "completion_tokens": 1290, "total_tokens": 1308 }
}modalities: ["image", "text"] — модель отдаёт и текст-комментарий, и картинку. Если передать modalities не-image модели, вернётся ошибка провайдера.
Генерация через /v1/images/generations
FLUX.2, Recraft, Seedream, Riverflow и Grok Imagine через chat completions недоступны (404 model_not_found) — они работают только через выделенный эндпоинт в формате OpenAI Images API:
curl -s https://api.hubris.pw/v1/images/generations \
-H "Authorization: Bearer sk-gw-..." \
-H "Content-Type: application/json" \
-d '{
"model": "black-forest-labs/flux.2-pro",
"prompt": "product photo of a ceramic mug on a linen tablecloth, soft window light"
}'Ответ — data[].b64_json (base64 без префикса data:), как у OpenAI. Официальные SDK работают из коробки:
import base64
from openai import OpenAI
client = OpenAI(base_url="https://api.hubris.pw/v1", api_key="sk-gw-...")
resp = client.images.generate(
model="black-forest-labs/flux.2-pro",
prompt="product photo of a ceramic mug on a linen tablecloth",
)
open("mug.png", "wb").write(base64.b64decode(resp.data[0].b64_json))Совет по FLUX: пишите промпт на английском — кириллицу эта линейка норовит отрисовать как текст на самой картинке. Gemini, Seedream и Recraft русский понимают нормально.
Все поля запроса, коды ошибок и биллинг — в референсе POST /v1/images/generations.
Редактирование изображений
Оба пути умеют image-to-image: подаёте исходную картинку + текст, что с ней сделать. Картинка передаётся как data URL с base64 (надёжно у всех провайдеров) или как публичная https-ссылка.
Через chat completions (Gemini, GPT-5 Image)
Исходное изображение кладётся в content-массив сообщения — ровно так же, как во Vision:
IMG=$(base64 -w0 photo.jpg)
curl -s https://api.hubris.pw/v1/chat/completions \
-H "Authorization: Bearer sk-gw-..." \
-H "Content-Type: application/json" \
-d @- <<EOF
{
"model": "google/gemini-2.5-flash-image",
"modalities": ["image", "text"],
"messages": [{
"role": "user",
"content": [
{ "type": "text", "text": "Убери фон, оставь предмет на чистом белом" },
{ "type": "image_url", "image_url": { "url": "data:image/jpeg;base64,$IMG" } }
]
}]
}
EOFPython (в официальном SDK поле modalities не объявлено — передаётся через extra_body, а images ответа читается из model_extra):
import base64
from openai import OpenAI
client = OpenAI(base_url="https://api.hubris.pw/v1", api_key="sk-gw-...")
b64 = base64.b64encode(open("photo.jpg", "rb").read()).decode()
resp = client.chat.completions.create(
model="google/gemini-2.5-flash-image",
messages=[{
"role": "user",
"content": [
{"type": "text", "text": "Сделай из фото акварельную иллюстрацию"},
{"type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{b64}"}},
],
}],
extra_body={"modalities": ["image", "text"]},
)
data_url = resp.choices[0].message.model_extra["images"][0]["image_url"]["url"]
header, b64_out = data_url.split(",", 1)
open("result.png", "wb").write(base64.b64decode(b64_out))TypeScript:
import fs from 'node:fs';
import OpenAI from 'openai';
const client = new OpenAI({ baseURL: 'https://api.hubris.pw/v1', apiKey: 'sk-gw-...' });
const b64 = fs.readFileSync('photo.jpg').toString('base64');
const resp = await client.chat.completions.create({
model: 'google/gemini-2.5-flash-image',
messages: [{
role: 'user',
content: [
{ type: 'text', text: 'Замени фон на вечерний город, предмет не трогай' },
{ type: 'image_url', image_url: { url: `data:image/jpeg;base64,${b64}` } },
],
}],
modalities: ['image', 'text'],
} as any);
const dataUrl = (resp.choices[0].message as any).images[0].image_url.url as string;
fs.writeFileSync('result.png', Buffer.from(dataUrl.split(',')[1]!, 'base64'));Главное преимущество chat-пути — пошаговая доработка в диалоге. Получили картинку → не понравилась деталь → кладёте её в следующее user-сообщение как image_url и пишете, что поправить («сделай небо темнее, остальное не трогай»). Модель редактирует именно ваш результат, а не генерирует заново.
Через /v1/images/generations (FLUX, Recraft, Seedream, Riverflow, Grok)
Референсные изображения передаются полем input_references — массив в том же формате image_url:
IMG=$(base64 -w0 product.jpg)
curl -s https://api.hubris.pw/v1/images/generations \
-H "Authorization: Bearer sk-gw-..." \
-H "Content-Type: application/json" \
-d @- <<EOF
{
"model": "bytedance-seed/seedream-4.5",
"prompt": "Перенеси предмет с фото в интерьер лофта, мягкий дневной свет",
"input_references": [
{ "type": "image_url", "image_url": { "url": "data:image/jpeg;base64,$IMG" } }
]
}
EOFТо же через официальный SDK — extra_body:
resp = client.images.generate(
model="bytedance-seed/seedream-4.5",
prompt="Перенеси предмет с фото в интерьер лофта, мягкий дневной свет",
extra_body={"input_references": [
{"type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{b64}"}}
]},
)Сколько референсов принимает модель (по нашим замерам; проверка на стороне провайдера — лишние он отклонит своей ошибкой):
| Модель | Референсов |
|---|---|
| Seedream 4.5 | до 14 |
| FLUX.2 (pro/max/flex/klein) | до 8 |
| Grok Imagine | до 3 |
| Recraft (v3–v4.1) | 1 |
Лимит Riverflow мы не замеряли — начните с 1–2 референсов. У Recraft дополнительно работает strength (0..1, по умолчанию 0.2) — насколько далеко уходить от оригинала; у Riverflow — super_resolution_references для image-to-image-апскейла (до 4, только при image-to-image). Провайдер-специфичные поля передаются в теле запроса как есть.
Параметры генерации
Chat-путь (Gemini): объект image_config в теле запроса:
aspect_ratio—"1:1"(default),"16:9","9:16","4:3","3:4","21:9"; уgemini-3.1-flash-image-previewдополнительно"1:4","4:1","1:8","8:1".image_size—"1K"(default),"2K","4K";"0.5K"— только уgemini-3.1-flash-image-preview.
curl -s https://api.hubris.pw/v1/chat/completions \
-H "Authorization: Bearer sk-gw-..." \
-d '{
"model": "google/gemini-3-pro-image-preview",
"modalities": ["image", "text"],
"messages": [{"role": "user", "content": "Морской пейзаж"}],
"image_config": { "aspect_ratio": "16:9", "image_size": "2K" }
}'Images-путь: параметры — плоские поля тела запроса, набор у каждой линейки свой (image_config здесь не используется):
n— сколько изображений за запрос (Seedream до 10, Recraft до 6; FLUX и Grok — по одному).aspect_ratio,resolution— понимает Grok Imagine ("1K"/"2K"); FLUX, Recraft и Seedream размер задают сами — эти поля им слать не нужно (в лучшем случае игнор, в худшем400).- Recraft:
style(стилевые пресеты — список на карточке модели),text_layout(текст на картинке, только V3),rgb_colors,background_rgb_color,strength. - Riverflow:
font_inputs— массив{ font_url, text }для фирменных шрифтов, до 2.
Какую модель когда выбирать
- Отредактировать фото по инструкции → Gemini *-image (Nano Banana): диалоговое редактирование, понимает русский.
- Текст на изображении (баннер, постер) → Recraft (
text_layoutу V3). - Фирменные шрифты → Riverflow (
font_inputs). - Максимальное качество / фотореализм → FLUX.2 Pro / Max, Gemini 3 Pro Image.
- Быстро и дёшево → Gemini 2.5 Flash Image, FLUX.2 Klein 4B.
- Скомбинировать несколько референсов в одну сцену → Seedream 4.5 (до 14 входных картинок).
Стриминг
На chat-пути работает stream: true — картинка приходит в delta.images[] одним или несколькими чанками, финальный чанк содержит usage (как в обычном стриме). На /v1/images/generations стриминга нет: один POST → готовый ответ.
curl -N https://api.hubris.pw/v1/chat/completions \
-H "Authorization: Bearer sk-gw-..." \
-d '{
"model": "google/gemini-2.5-flash-image",
"modalities": ["image", "text"],
"messages": [{"role": "user", "content": "Космический корабль"}],
"stream": true
}'Биллинг
- Images-путь — фиксированная цена за изображение: в каталоге у каждой модели видна цена в рублях за штуку, спишется
n × цена. Списание — после успешного ответа; за отказ провайдера или пустой ответ не списывается ничего. - Chat-путь — по токенам модели: сгенерированное изображение засчитывается провайдером как пакет completion-токенов. Итоговая сумма в рублях по каждому запросу — в журнале и разделе «Расходы».
Все списания атомарны: баланс, запись в журнале и движение по балансу пишутся одной транзакцией.
Лимиты
- Баланс — на
/v1/images/generationsзапрос блокируется с402 insufficient_balance, если баланса не хватает на ожидаемую стоимость заказа (n × цена за изображение); на chat-пути действует общий минимальный порог (см. Лимиты и баланс). - Дневной лимит ключа — действует на оба пути. Превышение —
429 daily_limit_exceeded. - Размер тела запроса на chat-пути — 36 МБ: этого хватает на несколько base64-картинок; при больших исходниках сжимайте JPEG перед отправкой.
Чего пока нет
- Edits по маске (inpainting в формате OpenAI
images.editс полемmask). - Variations (
images.createVariation). response_format: "url"— ответ всегдаb64_json.
Если что-то из этого критично — напишите в support@hubris.pw, расскажите use case.
Обновлено: