Чат
POST /v1/chat/completions — контракт OpenAI Chat Completions, в том числе с
"stream": true. Итог вызова (статус и цена, без текста) — GET /v1/jobs/{id} с id
ответа; карточка с токенами и временем — GET /v1/usage/{id} тем же ключом. Поля — схема
ChatCompletionRequest.
curl https://api.souz.ai/v1/chat/completions \
-H "Authorization: Bearer $SOUZ_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model": "gpt-5-nano", "messages": [{"role": "user", "content": "Привет!"}], "routing": "fast"}'chat = client.chat.completions.create(
model="gpt-5-nano",
messages=[{"role": "user", "content": "Привет!"}],
extra_body={"routing": "fast"},
)
print(chat.choices[0].message.content, chat.usage)const chat = await client.chat.completions.create({
model: "gpt-5-nano",
messages: [{ role: "user", content: "Привет!" }],
// @ts-expect-error — поле Souz API сверх контракта OpenAI
routing: "fast",
});
console.log(chat.choices[0].message.content, chat.usage);Поля запроса
-
Поля, которых нет в схеме, уходят модели как есть; что модель читает — её карточка.
-
Значения известных полей проверяются до списания, ошибка —
400 invalid_request, вdetail[]поле и что подойдёт:temperature— от 0 до 2,top_p— от 0 до 1; если вparamsкарточки у поля свой диапазон, действует он;reasoning_effortиreasoning.effort— одна из ступенейnone,minimal,low,medium,high,xhigh,max; ступени модели — вparamsкарточки. Ступень, которой нет в карточке, канал получает ближайшей ступенью карточки не ниже запрошенной, а если такой нет — высшей; канал, который подбирает ступень под модель сам, получает её как есть и применяет своё правило соответствия;max_tokens,max_completion_tokensиn— целые от 1;max_tokensиmax_completion_tokens— не меньшеminизparams(у GPT-6 — 16);- имена полей — с учётом регистра:
Max_TokensилиN—400с{"path": "N", "reason": "unsupported_field"}.
JSON{"path": "temperature", "reason": "out_of_range", "allowed": ["at most 2"]} -
tools, картинка, файл, звук и видео вmessages[].contentиresponse_format: json_schemaуходят только туда, где их понимают; если модель этого не умеет —400 capability_mismatchдо списания, вdetail[]названо поле. -
tool_choice: "required"и{"type": "function", "function": {"name": "…"}}исполняют не все каналы: такой запрос идёт только по тем, что умеют. Не умеет ни один —400 capability_mismatchдо списания,{"path": "tool_choice", "reason": "unsupported_value"}; с"auto"модель решает сама. -
Параметр, который канал не поддерживает, этим каналом игнорируется:
temperature,top_p,top_k,min_p,seed, штрафы,logprobs,logit_bias,stopи подобные; что учитывает канал —capabilities.supported_parameters.tools,tool_choice,response_formatиverbosityидут только к каналам, которые их учитывают; не учитывает ни один —400 capability_mismatchдо списания с полем вdetail[]. -
response_format: {"type": "json_object"}у моделей OpenAI требует слова «json» в сообщениях; без него —400 invalid_requestс{"path": "response_format", "reason": "incompatible_value"}. -
Тип картинки в data URL определяется по её байтам: подпись
image/jpegу PNG не мешает. -
Веб-поиск ни одна модель не исполняет:
web_search_options(и{}) или плагин{"id": "web"}вplugins—400 capability_mismatchдо списания с полем вdetail[]. -
service_tier,provider,models,route,transformsи остальныеplugins(file-parserи другие) игнорируются: канал и запасные каналы выбирают Авто-роутинг иrouting_options, PDF модель получает сама. -
Общие поля Souz API:
routing,max_price_multiplier,routing_options,store.
Claude Haiku 5.5: мышление и кэш
Модель claude-haiku-5.5 принимает reasoning_effort со значениями none,
low, medium, high, xhigh, max; умолчание — medium. none отключает
мышление. tools поддерживает tool_choice: "auto" и "none"; принудительный
или именованный выбор функции у доступных каналов не поддерживается.
Изображения и PDF передаются стандартными частями сообщений.
Промпт можно закэшировать на 5 минут или 1 час. cache_control передаётся
внутри текстового блока без изменений, например:
{
"model": "claude-haiku-5.5",
"reasoning_effort": "medium",
"max_tokens": 4096,
"messages": [{
"role": "user",
"content": [{
"type": "text",
"text": "Длинный документ и вопрос по нему",
"cache_control": {"type": "ephemeral", "ttl": "1h"}
}]
}]
}Повторный запрос с тем же префиксом может прочитать кэш. Запись и чтение
оплачиваются отдельно; для промпта свыше 100 тысяч токенов действует тариф
длинного контекста. Итоговое списание — по фактическому использованию.
stream: true включает потоковый ответ, stream_options: {"include_usage": true}
возвращает итоговый расход токенов в потоке.
Файлы, звук и видео
Части messages[].content — как у OpenAI Chat Completions, SDK работает без правок.
| Часть | Форма | Карточка модели |
|---|---|---|
| файл (PDF) | {"type": "file", "file": {"filename": "doc.pdf", "file_data": "data:application/pdf;base64,…"}} | capabilities.file_input |
| звук | {"type": "input_audio", "input_audio": {"data": "<base64>", "format": "wav"}}, wav или mp3 | capabilities.audio_input |
| видео | {"type": "video_url", "video_url": {"url": "https://…/clip.mp4"}} или data:video/mp4;base64,… | capabilities.video_input |
- Файл и звук — в теле, base64; видео — ссылкой или data URL. Ссылка должна открываться
без авторизации; подойдёт
urlфайла изPOST /v1/files. Если модель не смогла скачать ссылку —400 invalid_inputс{"path": "messages", "reason": "unreachable", "allowed": ["data URL"]}: передайте вложение data URL. - Модель без нужного входа —
400 capability_mismatchдо списания, вdetail[]—{"path": "messages", "reason": "unsupported_value"}. Какие каналы примут вход — превью сfile_input,audio_input,video_input. - Картинка data URL больше предела канала уходит только каналам, которые её примут. Не
примет ни один —
400 capability_mismatchдо списания с{"path": "messages", "reason": "too_large", "allowed": ["at most <N> base64 bytes per image"]}, где N — наибольший предел каналов модели. Уменьшите картинку или передайте её ссылкой. - Файл, звук и кадры видео модель считает во входных токенах; у части моделей токены звука дороже текстовых. Итог — по факту выполнения.
- Тело запроса — до 25 МиБ; больше —
413с{"path": "body", "reason": "too_large", "allowed": ["at most 26214400 bytes"]}. Крупное вложение загрузите черезPOST /v1/filesи передайте егоurlили передайте ссылкойhttps://….
import base64
pdf = base64.b64encode(open("contract.pdf", "rb").read()).decode()
chat = client.chat.completions.create(
model="gemini-3.8-flash",
messages=[{"role": "user", "content": [
{"type": "text", "text": "Перечисли сроки из договора."},
{"type": "file", "file": {"filename": "contract.pdf",
"file_data": f"data:application/pdf;base64,{pdf}"}},
]}],
)Поток
"stream": true — text/event-stream по контракту OpenAI. Заголовки приходят с первым
содержательным чанком; ошибка до него — обычный JSON со статусом. После 15 секунд тишины —
пока модель думает до первого токена или между чанками — поток пишет комментарий
: ping; SDK и разбор SSE его пропускают. Если поток уже открыт пингом, ошибка до первого
чанка приходит событием data: {"error": {…}} с тем же телом, без data: [DONE].
stream_options: {"include_usage": true} — последний чанк с пустым choices, usage и
usage.cost. Без флага usage в потоке нет, у каждого чанка есть choices[0]; списание —
в GET /v1/jobs/{id}. Поток, оборвавшийся без usage, бесплатен.
stream = client.chat.completions.create(
model="gpt-5-nano",
messages=[{"role": "user", "content": "Привет!"}],
stream=True,
stream_options={"include_usage": True},
)
for chunk in stream:
if chunk.choices:
print(chunk.choices[0].delta.content or "", end="")const stream = await client.chat.completions.create({
model: "gpt-5-nano",
messages: [{ role: "user", content: "Привет!" }],
stream: true,
stream_options: { include_usage: true },
});
for await (const chunk of stream) {
process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
}Ответ
usage.cost и usage.currency — списано, channel — канал запроса. Ожидание ответа — до 14
минут; дольше — 502 generation_failed (в потоке — обрыв без usage), без списания.
SDK OpenAI и Anthropic ждут ответа 10 минут и после таймаута сами повторяют запрос. Обрыв
вызов не отменяет: без ключа каждый повтор — новый вызов модели и новое списание. Передавайте
свой Idempotency-Key на каждый вызов: повтор с тем же ключом и тем же телом дожидается первого
вызова и получает его ответ (и поток), списание одно. Тот же ключ с другим телом — 409 idempotency_key_conflict. Ответ под ключом хранится сутки.
chat = client.chat.completions.create(
model="gpt-5-nano",
messages=[{"role": "user", "content": "Привет!"}],
extra_headers={"Idempotency-Key": "order-1842-summary"},
)Вход длиннее, чем принимает модель, или слишком большой max_tokens — 400 с полем в
detail[] (messages с reason: "too_long" или max_tokens); тело больше, чем принимает
любой канал модели, — messages с reason: "too_large"; пустой messages — тоже с полем.
Ответ, остановленный фильтром содержания (finish_reason: "content_filter"), несёт в
refusal нашу фразу, а не текст фильтра; в потоке refusal приходит одним куском в чанке
с finish_reason.