Chat Completions

POST https://router.1-lab.online/v1/chat/completions — основной эндпоинт: модель получает список сообщений и возвращает ответ. Формат совпадает с OpenAI, поэтому подойдут официальные SDK openai для Python и TypeScript и любые программы, которые умеют работать с OpenAI API.

Примеры ниже на Python; клиент создаётся так:

import os
from openai import OpenAI

client = OpenAI(base_url="https://router.1-lab.online/v1", api_key=os.environ["ROUTER_API_KEY"])

Основные параметры

Параметр Что задаёт
model ID модели, например openai/gpt-6.1-sol (обязательный)
messages список сообщений диалога (обязательный)
max_tokens предел длины ответа в токенах
temperature случайность ответа: 0 — предсказуемо, 1 и выше — разнообразнее
top_p альтернатива temperature; обычно меняют что-то одно
stop строка или список строк, на которых модель остановится
stream true — ответ приходит по частям (см. ниже)
tools, tool_choice вызов инструментов (см. ниже)
response_format ответ в формате JSON (см. ниже)

Роли сообщений:

  • system — инструкция для модели: роль, стиль, ограничения;
  • user — сообщения пользователя;
  • assistant — прошлые ответы модели (для продолжения диалога);
  • tool — результат вызова инструмента.

Модель не помнит прошлые запросы: чтобы продолжить диалог, передавайте историю целиком.

resp = client.chat.completions.create(
    model="anthropic/claude-sonnet-5.5",
    messages=[
        {"role": "system", "content": "Ты помощник юриста. Отвечай кратко."},
        {"role": "user", "content": "Чем договор оказания услуг отличается от подряда?"},
    ],
    temperature=0.3,
    max_tokens=800,
)
print(resp.choices[0].message.content)
print(resp.usage)  # prompt_tokens, completion_tokens, total_tokens

Поле finish_reason в ответе объясняет, почему модель остановилась: stop — закончила мысль, length — упёрлась в max_tokens, tool_calls — хочет вызвать инструмент.

Потоковый ответ (stream)

С "stream": true ответ приходит по частям, пока модель его пишет, — в формате Server-Sent Events. Пользователь видит текст сразу, не дожидаясь конца.

Python

stream = client.chat.completions.create(
    model="openai/gpt-6.1-sol",
    messages=[{"role": "user", "content": "Напиши короткое поздравление коллеге."}],
    stream=True,
    stream_options={"include_usage": True},
)

for chunk in stream:
    if chunk.choices and chunk.choices[0].delta.content:
        print(chunk.choices[0].delta.content, end="", flush=True)
    if chunk.usage:  # приходит в последнем фрагменте
        print("\nТокены:", chunk.usage.prompt_tokens, "+", chunk.usage.completion_tokens)

TypeScript

const stream = await client.chat.completions.create({
  model: "openai/gpt-6.1-sol",
  messages: [{ role: "user", content: "Напиши короткое поздравление коллеге." }],
  stream: true,
  stream_options: { include_usage: true },
});

for await (const chunk of stream) {
  const text = chunk.choices[0]?.delta?.content;
  if (text) process.stdout.write(text);
}

Если читаете поток без SDK: каждое событие — строка data: {...} с фрагментом в choices[0].delta, поток заканчивается строкой data: [DONE]. Строки, которые начинаются с двоеточия, — служебные, их нужно пропускать.

curl -N https://router.1-lab.online/v1/chat/completions \
  -H "Authorization: Bearer $ROUTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model": "openai/gpt-6-luna", "stream": true,
       "messages": [{"role": "user", "content": "Посчитай от 1 до 5"}]}'

Ошибка может прийти и посреди потока, когда HTTP-статус уже 200. Обрабатывайте ошибки на протяжении всего чтения, а не только при отправке запроса.

Вызов инструментов (function calling)

Модель может вернуть не текст, а просьбу вызвать вашу функцию с аргументами. Вы выполняете функцию у себя и отправляете результат обратно — модель формулирует ответ.

import json

tools = [{
    "type": "function",
    "function": {
        "name": "get_order_status",
        "description": "Статус заказа по номеру",
        "parameters": {
            "type": "object",
            "properties": {"order_id": {"type": "string", "description": "Номер заказа"}},
            "required": ["order_id"],
        },
    },
}]

messages = [{"role": "user", "content": "Где мой заказ 48213?"}]
resp = client.chat.completions.create(model="openai/gpt-6.1-sol", messages=messages, tools=tools)
msg = resp.choices[0].message

if msg.tool_calls:
    messages.append(msg)  # ответ модели с вызовом — обратно в историю
    for call in msg.tool_calls:
        args = json.loads(call.function.arguments)
        result = {"order_id": args["order_id"], "status": "передан в доставку"}  # здесь ваш код
        messages.append({
            "role": "tool",
            "tool_call_id": call.id,
            "content": json.dumps(result, ensure_ascii=False),
        })
    final = client.chat.completions.create(model="openai/gpt-6.1-sol", messages=messages, tools=tools)
    print(final.choices[0].message.content)
else:
    print(msg.content)
  • tool_choice: "auto" (по умолчанию) — модель сама решает, вызывать ли инструмент; "none" — запретить; {"type": "function", "function": {"name": "..."}} — обязать вызвать конкретный.
  • Модель может запросить несколько вызовов сразу — обработайте все tool_calls и верните результат на каждый tool_call_id.
  • Описания инструментов оплачиваются как входные токены в каждом запросе.

Ответ в формате JSON

Параметр response_format просит модель вернуть JSON.

По схеме — ответ строго соответствует вашей JSON Schema:

resp = client.chat.completions.create(
    model="openai/gpt-6.1-sol",
    messages=[{"role": "user", "content": "Извлеки данные: Иван Петров, 34 года, Казань"}],
    response_format={
        "type": "json_schema",
        "json_schema": {
            "name": "person",
            "strict": True,
            "schema": {
                "type": "object",
                "properties": {
                    "name": {"type": "string"},
                    "age": {"type": "integer"},
                    "city": {"type": "string"},
                },
                "required": ["name", "age", "city"],
                "additionalProperties": False,
            },
        },
    },
)
data = json.loads(resp.choices[0].message.content)

Любой JSON — response_format={"type": "json_object"}. В этом режиме обязательно попросите в промпте ответить в JSON и опишите нужные поля.

Надёжнее всего ответ по схеме работает у моделей OpenAI, Anthropic и Google. С открытыми моделями (DeepSeek, Qwen, GLM, Kimi и др.) дополнительно описывайте формат в промпте и проверяйте ответ в коде.

Изображения на входе

Большинство моделей принимают картинки вместе с текстом — например, openai/gpt-6.1-sol, anthropic/claude-sonnet-5.5, google/gemini-4-argon. Только текст понимают deepseek/deepseek-v4-pro и z-ai/glm-5.3. Сообщение в этом случае — список частей:

resp = client.chat.completions.create(
    model="openai/gpt-6.1-sol",
    messages=[{
        "role": "user",
        "content": [
            {"type": "text", "text": "Что на этом фото? Ответь одним предложением."},
            {"type": "image_url", "image_url": {"url": "https://example.com/photo.jpg"}},
        ],
    }],
)

Локальный файл передаётся в формате base64:

import base64

with open("scan.png", "rb") as f:
    b64 = base64.b64encode(f.read()).decode()

image_part = {"type": "image_url", "image_url": {"url": f"data:image/png;base64,{b64}"}}

Изображения пересчитываются во входные токены: чем больше разрешение, тем дороже запрос. Если модель не принимает изображения, запрос вернёт ошибку.