Skip to content

Чаты

Чат-эндпоинты дают доступ к текстовым моделям через единый OpenAI-подобный интерфейс.

Доступные модели (актуальный список и цены — GET /chats/models):

IDСемействоКонтекст
gemini-3-6-flashGoogle Gemini1M токенов
gemini-3-6-flash-openaiGoogle Gemini1M токенов
claude-sonnet-5Anthropic Claude1M токенов
claude-opus-5Anthropic Claude1M токенов
gpt-5-6-lunaOpenAI GPT1.1M токенов
gpt-5-6-terraOpenAI GPT1.1M токенов
gpt-5-6-solOpenAI GPT1.1M токенов

Как работает биллинг

В отличие от генераций (фиксированная цена за задачу), чат тарифицируется за токены:

  1. Оценка при запросе. До обращения к провайдеру с баланса списывается оценка: входные токены оцениваются по объёму текста, выходные — по вашему max_tokens.
  2. Settle после ответа. Когда модель ответила, фактическая стоимость считается по реальному usage провайдера. Разница возвращается или досписывается автоматически. Итоговая сумма в рублях — в поле cost_rub ответа (или в финальном SSE-чанке). Рядом присутствует legacy-поле cost_coins с тем же значением — оно deprecated и оставлено только для совместимости со старыми клиентами.
  3. Ошибка провайдера = полный возврат. Если запрос не состоялся (502/503), оценка возвращается целиком.
  4. Обрыв стрима. Если поток оборвался на середине, списывается оценка (контент частично доставлен), ошибка приходит событием {"error": ...} внутри потока.

Цены — в разделе Цены (рубли за 1 млн токенов, отдельно входные и выходные).

Обычный запрос (stream=false)

bash
curl -X POST "https://api.aihere.ru/api/v1/chats/completions" \
  -H "Authorization: Bearer $AIHERE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "claude-sonnet-5",
    "messages": [
      {"role": "system", "content": "Ты — краткий ассистент."},
      {"role": "user", "content": "Объясни идемпотентность в одном предложении."}
    ],
    "max_tokens": 300
  }'

Ответ:

json
{
  "request_id": "f47ac10b58cc4372a5670e02b2c3d479",
  "model": "claude-sonnet-5",
  "content": "Идемпотентность — это свойство операции...",
  "finish_reason": "stop",
  "usage": {"prompt_tokens": 25, "completion_tokens": 18, "total_tokens": 43},
  "cost_rub": 0.06,
  "cost_coins": 0.06
}

Диалог ведётся передачей истории в messages — сервер состояние между запросами не хранит, каждый запрос самодостаточен (и тарифицируется по полному объёму сообщений).

Потоковый запрос (stream=true, SSE)

При "stream": true ответ имеет тип text/event-stream: текст приходит порциями по мере генерации. Формат чанков OpenAI-совместимый:

data: {"id": "f47a...", "object": "chat.completion.chunk", "model": "gpt-5-6-luna", "choices": [{"index": 0, "delta": {"content": "Идем"}, "finish_reason": null}]}

data: {"id": "f47a...", "object": "chat.completion.chunk", "model": "gpt-5-6-luna", "choices": [{"index": 0, "delta": {"content": "потентность"}, "finish_reason": null}]}

data: {"id": "f47a...", "object": "chat.completion.chunk", "model": "gpt-5-6-luna", "choices": [{"index": 0, "delta": {}, "finish_reason": "stop"}], "usage": {"prompt_tokens": 25, "completion_tokens": 18, "total_tokens": 43}, "cost_rub": 0.06, "cost_coins": 0.06}

data: [DONE]

Правила разбора:

  • каждое событие — строка data: <json>, события разделены пустой строкой;
  • текст дельт — в choices[0].delta.content (может отсутствовать в финальном чанке);
  • финальный чанк: choices[0].finish_reason + usage + cost_rub (+ legacy-алиас cost_coins);
  • конец потока — служебная строка data: [DONE];
  • ошибка после начала потока — событие {"error": {"message": "..."}}, после него поток закрывается.

Если провайдер недоступен ещё до первого токена, вы получите обычный HTTP-ответ 502/503 с JSON-ошибкой (а не SSE) — оценка при этом возвращается полностью.

Примеры использования

python
import json
import requests

resp = requests.post(
    "https://api.aihere.ru/api/v1/chats/completions",
    headers={"Authorization": f"Bearer {API_KEY}"},
    json={
        "model": "gpt-5-6-luna",
        "messages": [{"role": "user", "content": "Расскажи анекдот"}],
        "stream": True,
    },
    stream=True,
    timeout=300,
)
resp.raise_for_status()

for line in resp.iter_lines(decode_unicode=True):
    if not line or not line.startswith("data: "):
        continue
    payload = line[6:]
    if payload == "[DONE]":
        break
    chunk = json.loads(payload)
    if "error" in chunk:
        raise RuntimeError(chunk["error"]["message"])
    delta = chunk["choices"][0]["delta"].get("content", "")
    if delta:
        print(delta, end="", flush=True)
    if chunk["choices"][0].get("finish_reason"):
        print("\n---")
        print("usage:", chunk["usage"], "cost:", chunk["cost_rub"])
javascript
const resp = await fetch("https://api.aihere.ru/api/v1/chats/completions", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    model: "gpt-5-6-luna",
    messages: [{ role: "user", content: "Расскажи анекдот" }],
    stream: true,
  }),
});
if (!resp.ok) throw new Error(`HTTP ${resp.status}: ${await resp.text()}`);

const reader = resp.body.pipeThrough(new TextDecoderStream()).getReader();
let buffer = "";
while (true) {
  const { done, value } = await reader.read();
  if (done) break;
  buffer += value;
  const events = buffer.split("\n\n");
  buffer = events.pop(); // последний кусок может быть неполным
  for (const ev of events) {
    const line = ev.trim();
    if (!line.startsWith("data: ")) continue;
    const payload = line.slice(6);
    if (payload === "[DONE]") return;
    const chunk = JSON.parse(payload);
    if (chunk.error) throw new Error(chunk.error.message);
    const delta = chunk.choices[0].delta.content ?? "";
    if (delta) process.stdout.write(delta);
    if (chunk.choices[0].finish_reason) {
      console.log("\nusage:", chunk.usage, "cost:", chunk.cost_rub);
    }
  }
}
php
$ch = curl_init('https://api.aihere.ru/api/v1/chats/completions');
$buffer = '';
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => [
        "Authorization: Bearer $apiKey",
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'model' => 'gpt-5-6-luna',
        'messages' => [['role' => 'user', 'content' => 'Расскажи анекдот']],
        'stream' => true,
    ], JSON_UNESCAPED_UNICODE),
    CURLOPT_TIMEOUT => 300,
    CURLOPT_WRITEFUNCTION => function ($ch, $data) use (&$buffer) {
        $buffer .= $data;
        while (($pos = strpos($buffer, "\n\n")) !== false) {
            $event = substr($buffer, 0, $pos);
            $buffer = substr($buffer, $pos + 2);
            $line = trim($event);
            if (!str_starts_with($line, 'data: ')) continue;
            $payload = substr($line, 6);
            if ($payload === '[DONE]') return -1; // остановить приём
            $chunk = json_decode($payload, true);
            if (isset($chunk['error'])) throw new RuntimeException($chunk['error']['message']);
            $delta = $chunk['choices'][0]['delta']['content'] ?? '';
            if ($delta !== '') echo $delta;
            if (!empty($chunk['choices'][0]['finish_reason'])) {
                echo PHP_EOL, 'cost: ', $chunk['cost_rub'], PHP_EOL;
            }
        }
        return strlen($data);
    },
]);
curl_exec($ch);
curl_close($ch);
bash
curl -N -X POST "https://api.aihere.ru/api/v1/chats/completions" \
  -H "Authorization: Bearer $AIHERE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-5-6-luna",
    "messages": [{"role": "user", "content": "Расскажи анекдот"}],
    "stream": true
  }'

# (флаг `-N` отключает буферизацию вывода — дельты видны сразу)

Коды ошибок

КодПричинаСписание
200 + error-событиеобрыв потока на серединеоценка (контент частично доставлен)
402недостаточно средств на балансенет
422невалидный запроснет
429превышен лимит 30/миннет
502провайдер отклонил запросполный возврат оценки
503провайдер / сервис баланса недоступенполный возврат оценки

Ошибки 402/422/429/502/503 приходят обычным JSON {"detail": "..."} — до начала SSE-потока.