Kling 4.0 через API: модель сама выбирала 1080p и выставляла счёт за него

Мы делаем агрегатор нейросетей Aigena, поэтому новые видеомодели подключаем в первые дни после выхода. Kling 4.0 от Kuaishou открыли через API в конце октября 2026 года, и на ней мы поймали неприятную ошибку: при оживлении фото модель без спроса рендерила 1080p и брала деньги за 1080p, хотя пользователь выбирал 720p. Ниже разбор: как мы это нашли, что поменяли в коде и как теперь проверяем сгенерированные ролики, не открывая их в плеере.
Ошибки валидации вместо документации
Описание модели у провайдера (мы ходим через Runware) появилось раньше, чем точная спецификация параметров. Самый дешёвый способ узнать правду: отправить заведомо неверный запрос. Ошибку валидации API возвращает сразу, генерация не запускается, и денег за неё не берут.
const base = {
taskType: "videoInference",
taskUUID: crypto.randomUUID(),
model: "klingai:kling-video@4.0",
positivePrompt: "A red fox walks through fresh snow",
deliveryMethod: "async",
outputFormat: "MP4",
};
for (const extra of [
{ duration: 3, width: 1000, height: 1000 },
{ duration: 31, width: 1280, height: 720 },
{ duration: 3, width: 1280, height: 720, providerSettings: { klingai: { sound: true } } },
{ duration: 3, resolution: "720p" },
]) {
const res = await runware([{ ...base, ...extra }]);
console.log(JSON.stringify(extra), "=>", res.errors?.[0]?.message);
}Что ответил API:
Запрос | Ответ |
|---|---|
1000×1000 |
|
duration 31 |
|
|
|
|
|
По четырём ответам стало понятно почти всё:
качества два, 720p и 1080p, в четырёх пропорциях: 16:9, 9:16, 1:1 и 21:9;
длительность от 3 до 30 секунд с шагом в секунду;
выключателя звука нет: у Kling 3.0 звук включался через
providerSettings.klingai.sound, у 4.0 он генерируется всегда, а сам параметр запрещён;параметра
resolutionнет вообще, разрешение задаётся только черезwidthиheight.
Последний пункт и оказался ловушкой.
Как модель выбирала 1080p сама
У Kling 3.0 качество задаётся отдельными моделями: @3-standard, @3-pro, @3-4k. Когда пользователь оживляет фото, мы передаём кадр в inputs.frameImages, а размеры не передаём: пропорции берутся из снимка, и модель не обрезает его под чужой формат. Эта логика жила в общем коде для всех видеомоделей.
У Kling 4.0 одна модель на все качества. Мы запустили три пробы по 3 секунды:
Проба | Что передали | Стоимость | Что получили |
|---|---|---|---|
Текст → видео | 1280×720 | $0.4004 | 1280×720 |
Фото → видео | кадр, без размеров | $0.5993 | 1440×1440 |
Фото → видео | кадр + 1280×720 | $0.4004 | 1280×720 |
Без размеров модель решила, что квадратное фото надо рендерить в максимальном качестве, и выставила счёт за 1080p. Пользователю при этом показана и списана цена 720p. Каждый такой ролик стоил бы нам в полтора раза больше, чем мы за него берём, и узнали бы мы об этом только из счёта в конце месяца.
Заодно выяснилось, что реальная цена ниже прайса: $0.1335 за секунду 720p против $0.157 на странице модели, то есть на 15%. Поэтому себестоимость мы всегда сверяем по полю cost в ответе API (его возвращает includeCost: true), а не по прайсу.
Что поменяли в коде
Параметры моделей у нас лежат в базе, а не в коде: какие качества доступны, таблица размеров, как включать звук. Общая функция собирает задачу для провайдера по этим параметрам. Чинить логику для всех моделей было нельзя: у Seedance, Wan и Kling 3.0 размеры вместе с кадром действительно ломают пропорции. Поэтому появился флаг модели:
// Kling 4.0: одна модель на все качества и параметра resolution не знает:
// без width/height по кадру она сама берёт 1080p. Флаг dimensions_with_frames
// отправляет размеры из таблицы и вместе с кадрами.
const framesTakeDimensions = runware.dimensions_with_frames === true;
if (ownDimensions && (!inputs.frameImages || framesTakeDimensions) && !inputs.video) {
task.width = ownDimensions.width;
task.height = ownDimensions.height;
}А в параметрах модели лежит таблица размеров по качеству и пропорциям:
{
"model": "klingai:kling-video@4.0",
"dimensions": {
"std": { "16:9": [1280, 720], "9:16": [720, 1280], "1:1": [940, 940] },
"pro": { "16:9": [1920, 1080], "9:16": [1080, 1920], "1:1": [1440, 1440] }
},
"dimensions_with_frames": true
}Обратите внимание на 940×940: квадрат в 720p у этой модели не 960×960, как у большинства, а 940×940. С 960 запрос просто не пройдёт.
Перед включением модели для пользователей мы собрали задачи для всех сочетаний (текст и фото, 720p и 1080p, три пропорции) тем же кодом, что работает на сервере, и сравнили с запросами, которые прошли в пробах. Модель включили только после деплоя: если бы строка в базе появилась раньше кода, первые пользователи получили бы старое поведение.
Проверяем ролики, не открывая плеер
Для статьи с примерами мы сгенерировали четыре ролика по 5 и 10 секунд. Смотреть каждый вручную долго, а главное, глазами плохо проверяется то, что нам было важно: совпадает ли речь, встали ли склейки там, где просили, и попадает ли звук в действие. Всё это проверяется из консоли.
Размер, длительность, звук
ffprobe -v error -show_entries stream=codec_type,width,height,channels,duration -of csv=p=0 v.mp4audio,2,10.030998
video,720,1280,10.041667Два канала, то есть стерео, как и обещано. Размер совпал с запрошенным.
Склейки по таймкодам
В Kling 4.0 промпт можно разбить на кадры с таймкодами, и модель сама склеивает сцены. Мы попросили рекламный ролик кроссовка из трёх сцен: «Кадр 1, 0–3 с … Кадр 2, 3–6 с … Кадр 3, 6–10 с». Чтобы проверить, где встали склейки, достаточно одного кадра в секунду, собранного в полосу:
ffmpeg -v error -i v.mp4 -vf "fps=1,scale=144:256,tile=10x1" -frames:v 1 strip.pngНа полосе из десяти кадров смена сцены видна между вторым и третьим и между пятым и шестым кадрами, то есть ровно на 3-й и 6-й секунде.
Совпадает ли звук с действием
Один ролик классический: кот сталкивает кружку со стола. Хотелось проверить, что звон звучит в момент падения, а не просто где-то в ролике. Пики громкости по полусекундам:
ffmpeg -i v.mp4 -af "asetnsamples=24000,astats=metadata=1:reset=1,ametadata=print:key=lavfi.astats.Overall.Peak_level" -f null - 2>&1 | grep -o "Peak_level=[-0-9.]*"Peak_level=-36.53 Peak_level=-34.27 Peak_level=-30.87 Peak_level=-24.17
Peak_level=-9.20 Peak_level=-14.72 Peak_level=0.81 Peak_level=0.71 ...asetnsamples=24000 при 48 кГц даёт окно в полсекунды. До трёх секунд почти тишина, на 3–4 секунде пик выше нуля. На раскадровке кружка уходит за край стола как раз там.
Говорит ли модель по-русски
В официальном гайде Kling 4.0 перечислены английский, китайский, японский, корейский, испанский, португальский, немецкий, французский и хинди. Русского нет. Мы попросили персонажа произнести фразу, указав в промпте «говорит по-русски» и взяв реплику в кавычки, и прогнали звук через распознавание:
ffmpeg -v error -i v.mp4 -vn -ac 1 -ar 16000 -b:a 48k speech.mp3
curl -s https://api.openai.com/v1/audio/transcriptions \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-F file=@speech.mp3 -F model=whisper-1 -F response_format=verbose_jsonrussian | Я думала, это займёт весь вечер, а получилось за 5 минут.Фраза совпала дословно, язык определился как русский. Распознавание стоит доли цента и закрывает вопрос быстрее, чем прослушивание с субтитрами.
Сколько стоили тесты
Что | Параметры | Стоимость |
|---|---|---|
Три пробы параметров | 3 с, 720p и 1080p | $1.40 |
Четыре ролика для примеров | 720×1280, 10 + 10 + 5 + 5 с | $4.00 |
Исходные картинки | GPT Image 2.5, 5 штук | $0.07 |
Все четыре ролика получились с первой попытки: речь, склейки и физика совпали с промптом.
Что мы теперь делаем с каждой новой видеомоделью
Отправляем заведомо неверные параметры и собираем допустимые значения из ошибок валидации. Это бесплатно.
Делаем короткие пробы на минимальной длительности во всех режимах: текст, фото, фото с последним кадром.
Сравниваем поле
costв ответе с ценой, которую видит пользователь, и размер готового файла с запрошенным.Прогоняем готовые ролики через ffprobe, раскадровку и распознавание речи.
Включаем модель в каталоге только после деплоя кода, который её поддерживает.
Самое полезное здесь третий пункт. Ошибку с 1080p не видно ни в интерфейсе, ни в логах: генерация проходит успешно, пользователь доволен качеством. Видно её только в сравнении стоимости одной пробы с другой.
KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.