Запуск генерации

Обновлено

Скачать Markdown

Генерация запускается одним запросом и выполняется в очереди: ответ на запуск несёт идентификатор, по которому забирают результат. Ниже — весь путь, от выбора модели до готового файла.

Каталог моделей

Список семейств отдаёт GET /api/v1/families под правом catalog:read. Фильтры — task (что делает модель) и outputType (что получается на выходе):

curl "https://strophe.app/api/v1/families?outputType=image" \
  -H "Authorization: Bearer <ваш ключ>"

Подробности одного семейства — GET /api/v1/families/{id}. В ответе варианты модели с их задачами и медиа-входами, настраиваемые параметры с типами и допустимыми значениями, цены и ожидаемое время работы.

Там же оси выбора варианта — axes: чем семейство отличает свои варианты помимо формы входа. Чаще всего это уровень качества (version), реже размер (size), тир (tier), режим (mode) или формат вывода (format). У каждой оси перечислены допустимые значения и значение по умолчанию, а у каждого варианта — его значения осей: по ним видно, во что обходится каждый уровень.

Недоступные вам семейства из выдачи не исчезают: они приходят с available: false и причиной в lockReason. Пропустить их молча значило бы соврать, что такой модели нет.

Неизвестное имя параметра запроса — отказ 400, а не полная выдача. Опечатка вроде ?tasks= иначе выглядела бы как применённый фильтр.

Общее устройство каталога описано в статье Каталог моделей.

Входные файлы

Медиа задаётся только нашими идентификаторами. Внешняя ссылка в генерацию не попадает: путь чужого файла внутрь один — POST /api/v1/files под правом files:write.

Файл принимается тремя способами: телом multipart/form-data (поле file), ссылкой или строкой base64.

curl https://strophe.app/api/v1/files \
  -H "Authorization: Bearer <ваш ключ>" \
  -F file=@reference.png
curl https://strophe.app/api/v1/files \
  -H "Authorization: Bearer <ваш ключ>" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com/reference.png"}'

Предел файла — 25 МБ, одинаково для всех трёх способов.

Тип решает подпись содержимого, а не заявление вызывающего: mimeType в запросе можно передать, но если он противоречит содержимому, файл будет отвергнут.

В ответе — fileId, который и подставляется во входы генерации. Состояние processing не отказ: идентификатор уже действителен, а тяжёлое видео дообработается само. Запуск с ещё не готовым файлом отвечает file_not_ready — тот же запрос через несколько секунд пройдёт.

Результат прошлой генерации годится входом наравне с загруженным файлом: у готовой генерации есть поле reusableAs со списком полей запроса, куда её можно подать, а значением служит generationId.

Оценка стоимости

Сколько будет стоить запрос, отвечает POST /api/v1/generations/estimate под правом generations:read. Тело — то же, что у запуска. Ничего не создаётся и не тратится:

curl https://strophe.app/api/v1/generations/estimate \
  -H "Authorization: Bearer <ваш ключ>" \
  -H "Content-Type: application/json" \
  -d '{"familyId": "nano-banana-pro", "prompt": "a red bicycle on a wet street"}'

В ответе — стоимость в кредитах, вариант модели, который будет запущен, доступные кредиты и признак sufficient: хватает ли счёта на эту работу прямо сейчас. Оценка считается так же, как окончательное списание, поэтому расхождения между ними нет.

Запуск

POST /api/v1/generations под правом generations:write:

curl https://strophe.app/api/v1/generations \
  -H "Authorization: Bearer <ваш ключ>" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-4821-render-1" \
  -d '{
        "familyId": "nano-banana-pro",
        "prompt": "a red bicycle on a wet street, evening light",
        "imageIds": ["3f2a…"],
        "maxCredits": 40
      }'

Обязателен только familyId. Подо что берётся вход, сервер читает по форме запроса. Уровень качества он угадать не может: его задают объектом axes, например {"version": "pro"}; пропущенная ось берёт значение по умолчанию. Параметры семейства идут объектом parameters, медиа — списками идентификаторов (imageIds, videoId, videoIds, model3dIds, audioIds), и порядок в списках значим: входы моделей позиционные.

modelId называет вариант целиком — и сценарий, и уровень; его передают, когда вариант нужен конкретный. Вместе с axes он не задаётся: два разных выбора в одном запросе — отказ, а не приоритет одного над другим. Имя оси или значение, которых у семейства нет, тоже отказ; значение, которого нет ни у одного варианта под эту форму входа, — отдельный отказ со списком доступных здесь.

maxCredits — потолок этого запроса: дороже — отказ до траты денег. Он необязателен, потому что точную цену даёт оценка, но в автоматической работе его стоит ставить всегда.

Тело проверяется строго: неизвестное поле — отказ, а не молчаливое отбрасывание. Опечатка в имени параметра иначе запустила бы не то, что вы просили, а счёт пришёл бы настоящий. То же внутри parameters: имя, которого нет в схеме семейства, — отказ, и медиа туда не кладут, даже если параметр модели называется ссылкой. Входы задаются только списками идентификаторов.

Ответ — 201 с идентификатором, состоянием queued, удержанной суммой и заголовком Location на ручку состояния.

Защита от повтора

Заголовок Idempotency-Key делает запуск безопасным при обрыве связи. Повтор с тем же ключом и тем же телом не запускает вторую генерацию: приходит тот же ответ 201, но с replayed: true.

Тот же ключ с другими параметрами — отказ idempotency_conflict. Повтор, пока первый запрос ещё выполняется, — idempotency_in_progress: дождитесь его, а не запускайте новый.

Без заголовка повтор ничем не защищён. Оборвавшийся ответ в этом случае означает, что генерация могла запуститься, — и деньги за неё будут списаны.

Ожидание исхода

Состояние генерации отдаёт GET /api/v1/generations/{id} под правом generations:read. Параметр wait до 60 секунд задерживает ответ до конца работы или до истечения этого времени:

curl "https://strophe.app/api/v1/generations/<id>?wait=30" \
  -H "Authorization: Bearer <ваш ключ>"

Состояний шесть: queued, running, finalizing и три конечных — ready, failed, cancelled. У незавершённой генерации есть pollAfterSeconds — через сколько имеет смысл спросить снова; у конечных его нет, потому что спрашивать больше не о чем.

Опрос — не единственный способ: исход умеет приходить сам. Как это настроить, описано в статье Вебхуки.

Провалившаяся генерация — это ответ 200, а не отказ запроса: состояние failed, а причина лежит в error — класс code, причина внутри класса reason, признак retryable, подсказка hint и разобранные числа в details. Как устроен этот разбор — в статье Ошибки и лимиты.

Результат

У готовой генерации в result лежит ссылка на файл, его тип и размеры, а у видео и аудио — длительность. Ссылка ведёт на нашу выдачу, а не в хранилище: она ничего не раскрывает и живёт не меньше суток, но умирает вместе с ключом, которым выдана. Если ключ отозвали, выданные им ссылки перестают работать.

Поле text в результате занято текстовой генерацией. Каталог внешнего API таких семейств не предлагает — текст через него не запускается, — но в истории они встречаются.

История

GET /api/v1/generations возвращает историю страницами: до 50 записей за раз, метка следующей страницы — в nextCursor. Фильтр status принимает active, ready и failed.

Курсор ключевой, а не смещение: генерации, запущенные во время обхода, не сдвигают окно и не приводят к пропускам.

История общая с подключением агента по MCP: это один внешний доступ с двумя способами обращения, и делить их незачем.

Отмена

POST /api/v1/generations/{id}/cancel под правом generations:write. В ответе — состояние, признак cancelled (отменил ли генерацию именно этот вызов) и creditsCharged.

Деньги зависят от момента: отмена до старта возвращает удержанное целиком, отмена уже начатой работы её оплачивает.