Модели решений
Модель решений получает данные и вопросы с вариантами ответа, а возвращает выбранный вариант и
вероятность каждого. Вызывается она одним запросом на адрес
/v1/decisions: ответ приходит на этот же запрос, задачу ставить и
опрашивать не нужно. Поставщик — TypeSafe AI.
Модели
Попробовать
Войдите и получите 50 кредитов на тесты и любое использование сервиса. Без регистрации доступно 10 запусков в сутки.
Нажмите «Запустить» — ответы модели появятся здесь
{ }
Вызов
Адрес один, и ответ приходит на этот же запрос. Ключ передаётся заголовком
Authorization: Bearer sk-… и выпускается
в кабинете. На один ключ шлюз пропускает
600 запросов в минуту.
Счёт идёт по входным токенам из поля usage ответа: это данные и вопросы
вместе. Ответ модели бесплатный. Кредиты списываются после ответа, по факту; за один запрос
списывается не меньше 0,02 кредита. Сколько списано, приходит в ответе полем
credits_consumed. Запрос, не дошедший до ответа, не тарифицируется.
Поля запроса
Формат тот же, что у разработчика модели: три поля. Данные и вопросы вместе — до 262 144 токенов. В одном запросе можно задать несколько вопросов об одних данных: модель отвечает на каждый отдельно.
| Поле | Тип | Обяз. | Что в нём |
|---|---|---|---|
| model | текст | обязательное | jev/1-14 |
| state | текст, объект или список | обязательное |
Данные, о которых модель выносит решение: письмо, отзыв, запись из базы; картинки — частью image_url, как в
документации
|
| questions | объект | обязательное | Вопросы: имя вопроса и его описание. Имя вы выбираете сами, под ним придёт ответ |
| questions.*.type | текст | обязательное |
Вид вопроса: choice — один из вариантов,
noul — да или нет, score — место на шкале, box — где на картинке предмет
(вопрос box)
|
| questions.*.instructions | текст, объект или список | обязательное | Сам вопрос. Текстом — когда хватает одной фразы. Объектом или списком — когда к вопросу нужны пояснения или данные, на которые он ссылается |
| questions.*.criteria | объект | обязательное |
У choice — варианты ответа: имя варианта и его описание. Описание —
текст, объект, список или null, когда пояснять нечего. До
255 вариантов
|
| questions.*.criteria | список | обязательное |
У score — ступени шкалы от низшей к высшей. Ступень — текст, объект
или список. До 10 ступеней
|
| questions.*.criteria | объект | необязательное |
У noul — что считать ответом «да» и что ответом «нет»: поля
true и false, оба сразу
|
Вопрос объектом — пример из справки разработчика: сам вопрос лежит в одном поле, данные — в соседнем, и вопрос ссылается на них по имени в обратных кавычках.
"instructions": {
"potential_duplicate": {
"name": "John Smith",
"location": "Oakland, California",
"last_employer": "Google"
},
"question": "Is the resume for the same person as `potential_duplicate`?"
}
Вызов
Запрос показан целиком, с именем этой модели и тремя вопросами разного вида: подставить остаётся только свой ключ.
curl https://api.gen202.com/v1/decisions \
-H "Authorization: Bearer sk-ваш-ключ" \
-H "Content-Type: application/json" \
-d '{
"model": "jev/1-14",
"state": "Help! My payouts have been failing for 3 days.",
"questions": {
"is_urgent": {
"type": "noul",
"instructions": "Does this convey urgency?",
"criteria": {
"true": "Explicitly time-sensitive",
"false": "No urgency expressed"
}
},
"department": {
"type": "choice",
"instructions": "Which team should handle this?",
"criteria": {
"billing": "Payments, invoicing, refunds",
"technical": "Bugs, outages, integrations",
"sales": "Pricing, upgrades, new accounts"
}
},
"frustration": {
"type": "score",
"instructions": "How frustrated is the customer?",
"criteria": ["Calm", "Frustrated", "Very angry"]
}
}
}'
import requests
answer = requests.post(
"https://api.gen202.com/v1/decisions",
headers={"Authorization": "Bearer sk-ваш-ключ"},
json={
"model": "jev/1-14",
"state": "Help! My payouts have been failing for 3 days.",
"questions": {
"is_urgent": {
"type": "noul",
"instructions": "Does this convey urgency?",
"criteria": {
"true": "Explicitly time-sensitive",
"false": "No urgency expressed",
},
},
"department": {
"type": "choice",
"instructions": "Which team should handle this?",
"criteria": {
"billing": "Payments, invoicing, refunds",
"technical": "Bugs, outages, integrations",
"sales": "Pricing, upgrades, new accounts",
},
},
"frustration": {
"type": "score",
"instructions": "How frustrated is the customer?",
"criteria": ["Calm", "Frustrated", "Very angry"],
},
},
},
).json()
print(answer["answers"]["department"]["choice"]) # billing
print(answer["usage"]) # по нему выставлен счёт
Ответ
Ответ на запрос выше, каким он пришёл от модели: под именем каждого вопроса — его ответ.
{
"model": "jev/1-14",
"answers": {
"is_urgent": { "type": "noul", "noul": 0.95 },
"department": {
"type": "choice",
"choice": "billing",
"probabilities": { "billing": 0.86, "technical": 0.14, "sales": 0 },
"confidence": 0.79
},
"frustration": {
"type": "score",
"score": 1.05,
"legend": { "0": "Calm", "1": "Frustrated", "2": "Very angry" },
"probabilities": { "0": 0, "1": 0.95, "2": 0.05 },
"confidence": 0.93
}
},
"usage": { "input_tokens": 426, "output_tokens": 73 },
"credits_consumed": 0.02
}
| Поле | Тип | Что в нём |
|---|---|---|
| model | текст | Имя модели, как в запросе |
| answers | объект | Ответы: под именем каждого вопроса — его ответ |
| answers.*.type | текст |
Вид вопроса: noul, choice, score или box
|
| answers.*.noul | число | У noul — вероятность ответа «да», от 0 до 1 |
| answers.*.choice | текст | У choice — вариант с наибольшей вероятностью |
| answers.*.score | число |
У score — место на шкале, взвешенное по вероятностям ступеней; может
лежать между ступенями
|
| answers.*.legend | объект | У score — номер ступени и её описание из запроса |
| answers.*.probabilities | объект |
У choice и score — вероятность каждого
варианта или ступени, в сумме 1
|
| answers.*.confidence | число |
У choice и score — уверенность модели, от 0
до 1; считается из вероятностей. У box — уверенность в рамке, от 0 до 1
|
| answers.*.box | объект |
У box — края рамки top,
bottom, left,
right: доли высоты и ширины картинки от 0 до 1, считая от левого
верхнего угла
|
| usage.input_tokens | целое | Токены данных и вопросов. По ним выставлен счёт |
| usage.output_tokens | целое | Токены ответа. Не тарифицируются |
| credits_consumed | число | Сколько кредитов списано за запрос |
Отказы
Тело отказа — объект error, как у OpenAI:
{"error":{"message":…,"type":…,"code":…,"param":null}}. Ни один из этих отказов не тарифицируется:
деньги списываются только после ответа модели.
| Код | code | Когда |
|---|---|---|
| 400 | model_not_found | Имени из поля model нет среди моделей решений. В сообщении перечислено, что принимается. |
| 400 | invalid_state | Поле state не передано или пустое. Данные принимаются строкой, объектом или списком. |
| 400 | invalid_questions | Поле questions не передано или пустое, либо вопрос задан не по схеме: не назван вид (choice, noul, score, box), пустое поле instructions, у choice нет объекта вариантов или их больше 255, у score нет списка ступеней или их больше 10, у noul в criteria нет одного из полей true и false. Тот же отказ приходит на вопрос box к модели, которая на него не отвечает: box отвечает только jev/1-14. В сообщении назван сам вопрос. |
| 400 | invalid_image | Картинка передана не так: ссылкой http(s), в формате кроме PNG и JPEG, строкой base64 без приставки data:image/…;base64, частью input_image или полями assets и images. В сообщении названо место в запросе и показано, как передать картинку. |
| 400 | key_required | Названный заголовком учёта ключ не принадлежит аккаунту или отключён. При вызове по ключу Bearer такого отказа не бывает. |
| 400 | invalid_request | Модель отказала по самому запросу. В сообщении — её слова. Запрос не тарифицируется. |
| 401 | invalid_api_key | Ключа нет в заголовке Authorization, либо он неверный или отключён. |
| 402 | insufficient_credits | На балансе не осталось свободных кредитов. Перед вызовом шлюз проверяет только это — сумма списывается после ответа, по факту. |
| 403 | forbidden | Аккаунт заблокирован: вызовы модели, пополнение и загрузка файлов ему закрыты. Ключи при этом не отключены — запрет лежит на аккаунте. |
| 413 | invalid_request | Запрос слишком велик для модели. Запрос не тарифицируется. |
| 422 | invalid_request | Модель не смогла обработать запрос в таком виде. В сообщении — её слова. Запрос не тарифицируется. |
| 429 | concurrent_request | Другой запрос этого аккаунта ещё считается. Пока на балансе 50 кредитов или меньше, текстовые запросы выполняются по одному: списание приходит после ответа, и одновременные запросы увели бы счёт в минус. Дождитесь ответа или пополните баланс. |
| 502 | upstream_error | Поставщик не ответил или ответил ошибкой. Запрос не тарифицируется: списывать нечего, ответа не было. |
| 503 | service_overloaded | Поставщик перегружен или отбил запрос по своему лимиту. Запрос не тарифицируется, повторить можно через несколько секунд — срок назван в заголовке Retry-After. |
Сверх 600 запросов в минуту с одного ключа шлюз отвечает
429 в своём виде:
{"statusCode":429,"error":…,"message":…} — объекта
error в таком ответе нет.
Картинки
Картинки принимают обе модели решений — jev/1-14 и jev/1-13. Картинку кодируют в base64
и кладут в state частью image_url рядом с
текстом. Отвечают по картинке вопросы всех видов, а вопрос box
находит на ней нужный предмет.
Запрос с картинкой
Фотография читается с диска, кодируется в base64 и подставляется в поле url.
Вопросы ссылаются на картинку по имени поля в обратных кавычках.
IMAGE=$(base64 < photo.jpg | tr -d '\n')
curl https://api.gen202.com/v1/decisions \
-H "Authorization: Bearer sk-ваш-ключ" \
-H "Content-Type: application/json" \
-d @- <<EOF
{
"model": "jev/1-14",
"state": {
"listing": "Photo from a listing.",
"photo": { "type": "image_url", "image_url": { "url": "data:image/jpeg;base64,$IMAGE" } }
},
"questions": {
"animal": { "type": "noul", "instructions": "There is a cat in \`photo\`." },
"face": { "type": "box", "instructions": "the cat's face in \`photo\`" }
}
}
EOF
Ответ — под именем каждого вопроса. Поля usage и
credits_consumed приходят те же, что в ответе выше, и здесь опущены.
{
"model": "jev/1-14",
"answers": {
"animal": { "type": "noul", "noul": 0.95 },
"face": {
"type": "box",
"box": { "top": 0.29, "bottom": 0.47, "left": 0.45, "right": 0.61 },
"confidence": 0.86
}
}
}
Числа — из нашего запроса 17 октября 2026 года с фотографией 900 × 900 пикселей в JPEG. В том запросе
стоял ещё вопрос choice, и весь запрос — фотография, подпись и три вопроса —
занял 2 597 входных токенов.
Как передать картинку
-
Картинка — объект
{"type": "image_url", "image_url": {"url": "data:image/jpeg;base64,…"}}. -
В
urlстоит строка base64 с приставкойdata:image/png;base64,илиdata:image/jpeg;base64,. Принимаются PNG и JPEG. Ссылки http(s) не скачиваются. -
Картинка лежит в
stateна любой глубине: элементом списка или значением поля объекта. Её можно положить и в вопрос — вinstructionsили в описание варианта вcriteria. -
Картинка в
instructionsуходит модели с каждым вопросом, в котором стоит, и каждый раз считается в токены. Картинку, нужную нескольким вопросам, кладут вstateодин раз и называют по имени поля. -
Текст рядом с картинкой — обычные строки, а не части
{"type": "text"}. - Вопрос
boxотвечает только jev/1-14.
Картинкой не считаются:
- поле
imagesна верхнем уровне запроса; - поле
assets; - часть
input_image; -
строка base64 без приставки
data:image/…;base64,— прямо вstateмодель прочтёт её как текст; - картинка WebP, GIF или в другом формате, кроме PNG и JPEG.
На поля images и assets, на часть
input_image и на часть image_url со ссылкой,
другим форматом или строкой без приставки шлюз отвечает 400 invalid_image.
В сообщении названо место в запросе.
Пределы картинок
| Предел | Значение |
|---|---|
| Формат | PNG или JPEG, только неподвижные: анимированный PNG модель отклоняет |
| Ширина и высота | От 280 до 1568 пикселей каждая |
| Пикселей всего | От 78 400 до 2 458 624 — площадь квадрата от 280 × 280 до 1568 × 1568 |
| Файл картинки | До 16 МиБ после раскодирования base64 |
| Тело запроса | До 20 МиБ вместе со строками base64 |
| Окно | Картинки считаются входными токенами и вместе с данными и вопросами входят в 262 144 токенов |
-
Картинку за этими пределами модель отклоняет, а не уменьшает, и тогда не проходит весь запрос: меньше
нижней границы —
400 invalid_request, больше верхней —413 invalid_request, оба со словами модели. - Поворот из EXIF модель не применяет: фотографию поворачивают до кодирования.
-
Картинка оплачивается входными токенами по ставке модели, отдельной платы за неё нет. Сколько токенов
ушло, показывает
usage.input_tokens.
Каждый запрос с картинкой модель обрабатывает в режиме длинного контекста, а этот режим, по словам разработчика, медленнее и менее точен.
Картинки и вопрос box разработчик помечает как раннюю возможность: формат запроса, ответы и пределы могут меняться от выпуска к выпуску.
Вопрос box
Вопрос box находит на картинке один предмет и отвечает рамкой вокруг него —
четырьмя краями в долях ширины и высоты картинки — и уверенностью. Отвечает на него только
jev/1-14: такой вопрос к jev/1-13 шлюз отклоняет сам, отказом
400 invalid_questions. В одном запросе box стоит рядом с вопросами других видов, и все они относятся к одним данным.
Поля вопроса
| Поле | Тип | Обяз. | Что в нём |
|---|---|---|---|
| questions.*.type | текст | обязательное | box |
| questions.*.instructions | текст, объект или список | обязательное |
Что найти и на какой картинке. Пишется описанием предмета, а не командой:
the cat's face in `photo`
|
| questions.*.criteria | — | не задаётся | Вариантов у box нет, и это поле модель не читает |
{
"model": "jev/1-14",
"state": {
"listing": "Photo from a listing.",
"photo": { "type": "image_url", "image_url": { "url": "data:image/jpeg;base64,/9j/4AAQ..." } }
},
"questions": {
"face": { "type": "box", "instructions": "the cat's face in `photo`" }
}
}
Ответ
Края рамки лежат в поле box. Ответ — из того же нашего запроса.
"face": {
"type": "box",
"box": { "top": 0.29, "bottom": 0.47, "left": 0.45, "right": 0.61 },
"confidence": 0.86
}
| Поле | Тип | Что в нём |
|---|---|---|
| answers.*.type | текст | box |
| answers.*.box.top, bottom | число | Верхний и нижний края — доли высоты картинки от 0 до 1, считая сверху. top меньше bottom |
| answers.*.box.left, right | число | Левый и правый края — доли ширины картинки от 0 до 1, считая слева. left меньше right |
| answers.*.confidence | число | Уверенность модели в рамке, от 0 до 1 |
Как прочитать рамку
Чтобы обрезать картинку по рамке или нарисовать её, каждый край умножают на ширину или высоту картинки в пикселях. На фотографии 900 × 900 рамка из ответа выше — это прямоугольник от 405 до 549 пикселей по ширине и от 261 до 423 по высоте.
from PIL import Image
image = Image.open("photo.jpg")
width, height = image.size
edges = answer["answers"]["face"]["box"]
box = (
round(edges["left"] * width),
round(edges["top"] * height),
round(edges["right"] * width),
round(edges["bottom"] * height),
)
image.crop(box).save("face.jpg")
Ответа «не найдено» у box нет: рамка приходит, даже когда предмета на картинке нет. В нашем запросе
без картинки box вернул рамку с уверенностью 0,03. Поэтому в тот же запрос ставят вопрос
noul — есть ли предмет на картинке — и рамку не берут, когда вероятность
«да» низкая.
- Один вопрос box — одна рамка. Несколько предметов ищут несколькими вопросами в одном запросе.
-
Если похожих предметов несколько, нужный описывают так, чтобы подходил только он:
the mug nearest the sink in `photo`. -
Мелкий предмет на большой картинке модель может пропустить. Сначала находят нужную часть картинки —
например, вопросом
choice, где варианты — части картинки, — и задают box уже по ней. - Порог уверенности разработчик советует подбирать на своих картинках. При низкой уверенности спрашивают снова по картинке крупнее или расширяют рамку перед обрезкой.
Сколько стоит Jev 1.14
Jev 1.14. Цена за миллион токенов — за входные токены: данные и вопросы. Ответ модели бесплатный.
| Токены | Наша цена |
|---|---|
| Вход | 18.59 кр. $0.08459,29 ₽ |
| Выход | бесплатно |
кр. — кредиты, покупаются за рубли.
Jev 1.13. Цена за миллион токенов — за входные токены: данные и вопросы. Ответ модели бесплатный.
| Токены | Наша цена |
|---|---|
| Вход | 18.59 кр. $0.08459,29 ₽ |
| Выход | бесплатно |
кр. — кредиты, покупаются за рубли.
Подробнее: отказы — коды остальных адресов; лимиты — пределы частоты по адресам; баланс и расход — как спросить списание по ключу.