Appearance
Чаты
Чат-эндпоинты дают доступ к текстовым моделям через единый OpenAI-подобный интерфейс.
Доступные модели (актуальный список и цены — GET /chats/models):
| ID | Семейство | Контекст |
|---|---|---|
gemini-3-6-flash | Google Gemini | 1M токенов |
gemini-3-6-flash-openai | Google Gemini | 1M токенов |
claude-sonnet-5 | Anthropic Claude | 1M токенов |
claude-opus-5 | Anthropic Claude | 1M токенов |
gpt-5-6-luna | OpenAI GPT | 1.1M токенов |
gpt-5-6-terra | OpenAI GPT | 1.1M токенов |
gpt-5-6-sol | OpenAI GPT | 1.1M токенов |
Как работает биллинг
В отличие от генераций (фиксированная цена за задачу), чат тарифицируется за токены:
- Оценка при запросе. До обращения к провайдеру с баланса списывается оценка: входные токены оцениваются по объёму текста, выходные — по вашему
max_tokens. - Settle после ответа. Когда модель ответила, фактическая стоимость считается по реальному
usageпровайдера. Разница возвращается или досписывается автоматически. Итоговая сумма в рублях — в полеcost_rubответа (или в финальном SSE-чанке). Рядом присутствует legacy-полеcost_coinsс тем же значением — оно deprecated и оставлено только для совместимости со старыми клиентами. - Ошибка провайдера = полный возврат. Если запрос не состоялся (502/503), оценка возвращается целиком.
- Обрыв стрима. Если поток оборвался на середине, списывается оценка (контент частично доставлен), ошибка приходит событием
{"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-потока.
