# Аутентификация (/docs/authentication)
> API-ключи Hubris в формате sk-gw-, заголовок Bearer, безопасное хранение и ротация.
import { EndpointBadge } from '@/components/docs/endpoint-badge';
import { EnvVar } from '@/components/docs/env-var';
Все запросы к Hubris авторизуются API-ключом в заголовке `Authorization: Bearer sk-gw-<32-hex>`. Это совместимо с OpenAI SDK и большинством ML-инструментов.
## Создание ключа
1. Войдите в [/keys](/keys).
2. Нажмите «Новый ключ», задайте описательное имя (например, «production-bot», «dev-laptop»).
3. **Скопируйте ключ сразу** — он показывается один раз. Hubris хранит только хеш ключа (sha256), восстановить полное значение невозможно.
4. Сохраните в менеджер паролей или переменную окружения .
## Формат ключа
`sk-gw-` + 32 шестнадцатеричных символа (16 байт случайных данных). Пример:
```
sk-gw-a1b2c3d4e5f6789012345678901234ab
```
В UI показывается префикс `sk-gw-a1b2...34ab` — этого достаточно, чтобы отличить ключи между собой, но недостаточно для использования.
## Использование в запросах
Заголовок: `Authorization: Bearer sk-gw-...`. Например, через cURL:
```bash
curl https://api.hubris.pw/v1/models \
-H "Authorization: Bearer sk-gw-..."
```
В OpenAI SDK ключ передаётся через параметр `api_key` или env-переменную:
```python
from openai import OpenAI
client = OpenAI(api_key="sk-gw-...", base_url="https://api.hubris.pw/v1")
```
```ts
import OpenAI from "openai";
const client = new OpenAI({ apiKey: "sk-gw-...", baseURL: "https://api.hubris.pw/v1" });
```
## Что делать, если ключ скомпрометирован
1. Откройте [/keys](/keys).
2. Найдите подозрительный ключ по префиксу или имени.
3. Нажмите «Отозвать» — ключ немедленно перестаёт работать.
4. Создайте новый ключ и обновите его во всех местах использования.
## Безопасное хранение
* **Никогда не коммитьте ключи в git** — добавьте `.env` файлы в `.gitignore`.
* **Не отправляйте ключи** в логи, аналитику или error-tracking. Hubris не логирует содержимое запросов, но сторонние сервисы могут.
* **Ротируйте ключи** при смене работы, выходе сотрудника, передаче кода подрядчику. Это секунда работы — старый отозвать, новый создать.
* **Один ключ — одна цель.** Отдельные ключи на dev / staging / production упрощают аудит и быструю ревокацию при инциденте.
## Лимиты на ключе
В стандартном тарифе у пользовательских ключей **нет искусственных лимитов** — расход ограничен только балансом аккаунта. Служебные ключи (CI, скрипты) могут получить дневной лимит в копейках при создании — это страховка от утечки. Подробнее — в разделе про rate limits (готовится).
## Ошибки аутентификации
Без заголовка или с невалидным ключом возвращается **401**:
```json
{
"error": {
"message": "Missing API key",
"type": "invalid_request_error",
"code": "invalid_api_key"
}
}
```
Если ключ был отозван — то же самое: 401 с `code: "invalid_api_key"`.
## Что дальше
* [Быстрый старт](/docs/quickstart) — первый запрос с вашим новым ключом.
* [Каталог моделей](/models) — выбрать модель.
* [Биллинг](/billing) — пополнение баланса, минимальный остаток.
---
# Changelog (/docs/changelog)
> Записи о breaking changes API, новых моделях, обновлениях платформы Hubris.
Записи о значимых изменениях платформы, документации и SDK. Полная история коммитов — внутренняя.
## 2026-05-28 — OAuth 2.0 + PKCE для сторонних приложений (Phase A)
Сторонние приложения могут подключаться к Hubris через стандартный OAuth flow с тремя scopes (`chat:write`, `models:read`, `balance:read`) и опциональным дневным лимитом на токен. Claude Desktop, Claude.ai, Cursor и другие MCP-клиенты теперь подключаются нативно через «Add custom connector» UI — без `mcp-remote` шима. Подробности и Python-пример: [/docs/integrations/oauth](/docs/integrations/oauth).
## 2026-05-26 — MCP-сервер для Claude Desktop / Claude Code / Cline (Beta)
Подключите Hubris к AI-агенту по протоколу [Model Context Protocol](/docs/integrations/mcp): URL `https://api.hubris.pw/mcp`, тот же Bearer-ключ `sk-gw-...`. Агент получает каталог моделей, баланс и запросы к LLM (`chat/complete` — полный паритет с `/v1/chat/completions`) через одно подключение. Биллинг — как обычно. Фича в Beta — поведение и набор инструментов могут поменяться.
## 2026-05-25 — Чат в личном кабинете (Beta)
В ЛК появился раздел [**Чат**](https://hubris.pw/chat) — браузерный интерфейс к любой из 300+ моделей каталога. Альтернатива внешним чат-клиентам, когда нужно «быстро спросить» без curl и API-ключей.
* **История хранится только в этом браузере.** Чаты живут в localStorage — сервер Hubris их не видит и не может восстановить. В журнале запросов остаются только метаданные (модель, токены, стоимость), как и для API-вызовов. При очистке данных браузера или смене устройства чаты пропадут — для бэкапа есть «Скачать всё» и импорт из JSON в настройках чата.
* **Одна модель на чат.** Выбирается при создании, в шапке отображается название и провайдер. Чтобы попробовать другую модель — создаётся новый чат.
* **Edit и regen, без веток.** Любое сообщение пользователя можно отредактировать — хвост чата перепрогоняется заново (с подтверждением, если удаляется ≥2 сообщений). Под последним ответом ассистента — кнопка «Перегенерировать».
* **Лимиты.** До 50 сообщений в одном чате (мягкое предупреждение с 40), до 30 чатов в сайдбаре. При достижении — баннер «Создайте новый чат» с кнопкой «Скачать этот». Защита от тормозов DOM и переплаты за input-replay (каждый новый запрос отправляет всю историю в модель).
* **Кликабельные источники для Perplexity Sonar.** Маркеры `[1]`, `[2]` в ответах web-search моделей превращаются в маленькие фиолетовые pill-ссылки на оригинальные источники. Под ответом — полный список URL с hostname и путём.
* **Поддержка LaTeX-формул.** Inline `\(...\)` и блочные `\[...\]` рендерятся через KaTeX — нужно для reasoning-моделей (Sonar Reasoning, Sonar Deep Research), которые форматируют числа и формулы в математической нотации.
* **Stop с честным биллингом.** Кнопка «Остановить» во время стрима абортит чтение, но сервер дочитывает апстрим до конца и списывает полную стоимость (соответствие SPEC §10). В подсказке у кнопки — явное «не отменяет списание».
* **Списание как обычно, без прогноза.** Каждое сообщение оплачивается из основного баланса так же, как `/v1/*` запрос. При нулевом балансе — баннер «Пополнить», следующее сообщение не отправляется до пополнения.
* **Privacy-онбординг.** При первом открытии — диалог с явным объяснением, что чаты не покидают браузер. Один клик «Понятно» — больше не показывается.
* **Полный экран на десктопе.** Раздел занимает всю ширину/высоту окна — без узкой колонки и шапки дашборда. Колонка сообщений ограничена 760px для комфортного чтения.
Доступ — пункт «Чат» в сайдбаре ЛК (бейдж Beta).
## 2026-05-25 — Активированы Perplexity Sonar и другие web-search модели
5 моделей семейства **Perplexity Sonar** (`sonar`, `sonar-pro`, `sonar-pro-search`, `sonar-reasoning-pro`, `sonar-deep-research`) теперь активны в [каталоге](/models) и доступны через `/v1/*` и встроенный чат. Раньше были деактивированы — пока не было уверенности, что биллинг корректно покрывает web-search surcharge.
* **Биллинг через `usage.cost` от провайдера.** Стоимость каждого запроса (включая web-search per-request surcharge $0.005) приходит готовой от апстрима и попадает в `usage_logs.cost_kopecks` ровно так же, как для обычных моделей. Никаких отдельных формул на нашей стороне.
* **Sentinel-warning в логах.** Если апстрим вдруг вернёт `usage.cost=0` при `web_search_requests > 0` — пишем warn-лог, чтобы поймать проблему до того, как её заметят пользователи.
* **В чате `/chat` — кликабельные `[N]`.** Sonar возвращает массив `citations[]` URL'ов; чат превращает маркеры в тексте в маленькие фиолетовые pill-ссылки и показывает полный список под ответом.
## 2026-05-24 — Все ошибки `/v1/*` и `/api/internal/*` теперь на русском
`error.message` во всех публичных эндпоинтах переведены на формальный русский язык:
* `Недостаточно средств на балансе.`
* `Модель не найдена: `
* `Неверный API-ключ` / `API-ключ не передан`
* `Курс ЦБ РФ временно недоступен`
* `Сервис провайдера временно недоступен` (раньше — `Upstream returned an error`)
* `Превышено время ожидания апстрима` / `Превышен дневной лимит расхода для этого API-ключа`
* и другие.
`error.code` (`insufficient_balance`, `model_not_found`, `upstream_error`, `daily_limit_exceeded`, и т.д.) **остаётся стабильным машинно-читаемым контрактом** — английский. Если ваш код матчит ошибки по `code` — изменений не требуется. Если по `message` — обновите матч на русский (либо мигрируйте на `code`).
В песочнице на странице модели также убрали захардкоженные русские подсказки на фронте — теперь рендерится ровно то, что отдал бэкенд. При ошибке «недостаточно средств» рядом с сообщением появляется ссылка «Пополнить баланс» → `/billing`.
## 2026-05-23 — `usage.cost` теперь в копейках
В ответах всех `/v1/*` эндпоинтов (`/messages`, `/chat/completions`, `/responses`, `/embeddings`) поле `usage.cost` теперь содержит **итоговую цену запроса в копейках** (integer) — ту же сумму, что списывается с баланса и попадает в [/v1/usage](/docs/api/usage). Раньше там приходил raw cost в долларах. Применяется и к потоковому режиму — копейки попадают в финальный `message_delta` / `chat.completion.chunk` / `response.completed`.
## 2026-05-23 — Поддержка Claude Code: эндпоинт `/v1/messages`
* **Новый эндпоинт `POST /v1/messages`** — совместимый с [Anthropic Messages API](https://docs.anthropic.com/claude/reference/messages_post). Через него работают [Claude Code](/docs/integrations/claude-code), официальные SDK Anthropic для Python и TypeScript, любые сторонние клиенты, ожидающие Anthropic-формат.
* **Аутентификация: и Bearer, и `x-api-key`.** Поддержаны оба заголовка одновременно — Claude Code использует `ANTHROPIC_AUTH_TOKEN` через `Authorization: Bearer`, а Anthropic-SDK по умолчанию шлёт `x-api-key`. Hubris принимает обе формы для любого `/v1/*` эндпоинта.
* **Маппинг имён моделей.** Принимаются и Anthropic-стиль (`claude-sonnet-4-5`, `claude-3-5-sonnet-20241022`, `claude-haiku-4-5-latest`), и каноничные Hubris-имена (`anthropic/claude-sonnet-4.6`). В ответе поле `model` восстанавливается в то имя, что прислал клиент — для согласованности логов.
* **Полный набор Anthropic-фич: vision, tool use, prompt caching, extended thinking.** Все content-блоки (`text`, `image`, `tool_use`, `tool_result`, `document`, `thinking`) и параметры (`cache_control`, `tool_choice`, `thinking`, `stop_sequences`) проходят без изменений. Биллинг учитывает `usage.cost`, который провайдер возвращает с поправкой на cache hit / cache write.
* **Anthropic-нативный SSE-стриминг.** События `message_start` / `content_block_*` / `message_delta` / `message_stop` приходят в нативном формате. На разрыве соединения Hubris дочитывает апстрим до конца и списывает стоимость, без бесплатных токенов.
* **Anthropic-формат ошибок.** Вместо OpenAI-стиля `{error:{...}}` возвращаем `{type:"error",error:{type,message}}` с корректным `error.type` (`invalid_request_error`, `authentication_error`, `rate_limit_error`, `api_error`, `not_found_error`, `billing_error`, `timeout_error`). SDK Anthropic не падают.
* **Statusline-скрипт для Claude Code.** Bash (`https://hubris.pw/scripts/claude-statusline.sh`) и кросс-платформенный Node-вариант (`https://hubris.pw/scripts/claude-statusline.mjs`) — выводит активную модель и расход за сегодня в нижней строке TUI. Подробности — в [гиде по Claude Code](/docs/integrations/claude-code#statusline-с-балансом).
* **Документация.** [Гид «Подключение Claude Code»](/docs/integrations/claude-code) — установка, конфигурация, выбор моделей, statusline, FAQ. [API-референс POST /v1/messages](/docs/api/messages) — параметры, форматы, примеры.
* **Ограничение MVP.** Privacy Mode (маскирование PII) на `/v1/messages` **пока не работает** — фича сложнее в реализации для Anthropic-формата content-блоков. Если нужна маскировка, используйте `/v1/chat/completions` с заголовком `X-Hubris-Privacy-Mask`. Поддержка появится отдельно.
## 2026-05-23 — Каталог моделей: +23 модели генерации изображений
* **Подключены image-модели из расширенного каталога:** Flux 2 (pro/max/flex/klein-4b), Recraft v3/v4/v4.1 (11 вариантов: pro, vector, utility), Sourceful Riverflow v2 (pro, fast, preview-серии), xAI Grok Imagine, ByteDance Seedream 4.5, gemini-2.5-flash-image-preview. Полный список — в [каталоге](/models) с фильтром «Output: image».
* **Корректное отображение цены за изображение в каталоге.** Раньше unit-priced модели показывали «0 ₽ за 1М токенов» (UX-баг). Теперь — «X ₽ за изображение» / «X ₽ за мегапиксель» в зависимости от тарификации модели. То же поле `pricing.per_unit[]` доступно через `GET /v1/models` для разработчиков.
* **Документация: расширение [Генерации изображений](/docs/features/image-generation).** Появилась справка по `image_config` параметрам конкретных провайдеров: Recraft `style` / `text_layout` / `rgb_colors`, Sourceful `font_inputs` / `super_resolution_references`. Раздел «Какую модель когда выбирать».
## 2026-05-22 — Песочница умеет генерировать картинки и уважает Privacy Mode
* **Image-gen в песочнице.** На страницах моделей `google/gemini-2.5-flash-image`, `openai/gpt-image-*`, `google/gemini-3.1-flash-image-preview` и других моделей с картиночным выходом песочница теперь возвращает сгенерированное изображение прямо в чат-пузырь. Стоимость и токены считаются как для обычной генерации — без скрытых наценок.
* **Превью и сохранение.** Клик по сгенерированной картинке открывает полноразмерный лайтбокс. Кнопка «Сохранить» скачивает PNG (или WEBP/JPEG — расширение берётся из data URL) с именем `hubris-.`. ESC или клик мимо — закрытие. Файл существует только у вас: на серверах Hubris ничего не сохраняется, в логах остаются только метаданные биллинга.
* **Песочница для image-only моделей.** Модели, которые отдают только картинку без текста (Flux / SDXL-семейство), теперь тоже доступны в песочнице.
* **Privacy Mode теперь работает и в песочнице.** Раньше включённая в `/security` маска применялась только к запросам через API-ключ — а отправки прямо со страницы модели уходили без маски. Теперь оба пути идентичны: ФИО, телефоны, email, ИНН, паспорта и т.д. скрываются до отправки модели, а в ответе восстанавливаются обратно. На stream-ответах работает та же построчная подмена, что и в `/v1/chat/completions`.
## 2026-05-22 — Server-tools, новая секция «Интеграции», доки фич
* **API: новый namespace `hubris:*` для server-tools.** В `tools[]` запроса `/v1/chat/completions` и `/v1/responses` появилась поддержка `{ "type": "hubris:web_search" }` — это канонический способ включить встроенный веб-поиск. Гид: [Поиск в интернете](/docs/features/web-search). Старые namespace-варианты продолжают работать ради обратной совместимости.
* **Новая секция документации «Интеграции» (9 страниц).** Подключение Hubris к популярным AI-инструментам: [Cline](/docs/integrations/cline), [Roo Code](/docs/integrations/roo-code), [Kilo Code](/docs/integrations/kilo-code), [OpenCode](/docs/integrations/opencode), [Qwen Code CLI](/docs/integrations/qwen-code), [Hermes Agent](/docs/integrations/hermes-agent), [OpenClaw](/docs/integrations/openclaw), [Dify](/docs/integrations/dify), [n8n](/docs/integrations/n8n). У каждого инструмента — точные шаги настройки base URL и API-ключа, советы по выбору модели и решение типовых проблем.
* **Новые гиды в разделе «Возможности»:**
* [Вызов инструментов (tool calling)](/docs/features/tool-calling) — жизненный цикл tool-сессии, `tool_choice`, параллельные вызовы, стриминг, SDK-примеры.
* [Структурированный вывод](/docs/features/structured-output) — `json_object` и `json_schema` строгий режим, Pydantic / Zod helper'ы, обработка refusal'ов.
* [Поиск в интернете](/docs/features/web-search) — встроенный server-side инструмент, аннотации источников, стриминг.
* [Изображения на вход (Vision)](/docs/features/vision) — multimodal-ввод через `image_url` content-parts, URL и base64, параметр `detail`, биллинг.
* [Выбор модели программно](/docs/features/model-selection) — фильтрация каталога через `GET /v1/models`, обработка `404 model_not_found`, версионированные slug'и.
* [Стриминг ответов](/docs/features/streaming) — когда включать `stream: true`, парсинг в Python / Node / Vercel AI SDK / голом fetch, UX-паттерны (типографический эффект, прогресс на reasoning-моделях, отмена).
* [Мониторинг расходов](/docs/features/usage-tracking) — `GET /v1/usage` для cron-отчётов, бюджет-алёртов в коде агента, разбивки по проектам, `BigInt` для копеечной точности.
* [Обработка ошибок](/docs/features/error-handling) — стратегия retry по кодам (что ретраить, что нет), `tenacity` пример на Python, factory на TS, маппинг внутренних кодов в user-facing сообщения.
* **Боковое меню документации согласовано с дашбордом.** Шрифт, отступы и компоновка строк сайдбара `/docs` теперь матчат сайдбар личного кабинета (`13.5px` / `6×10` / иконки `15px`). До этого fumadocs-дефолты раздували сайдбар, и переходы между ЛК и доками выглядели визуально несогласованно.
## 2026-05-12 — Документация под общим хедером, AI-friendly выгрузка
* **Документация теперь под общей навигацией сайта.** В шапке `/docs` появилось основное меню Hubris (Главная, Модели, Использование, API-ключи, Биллинг). Для авторизованных — текущий баланс и меню пользователя. Из ЛК больше не нужно открывать документацию «в отрыве».
* **Русский интерфейс fumadocs.** `«На этой странице»`, `«Следующая»`, `«Предыдущая»`, `«Редактировать на GitHub»`, плейсхолдер поиска — теперь по-русски.
* **AI-friendly выгрузка документации.**
* Любую страницу `/docs/` можно открыть как plain-markdown: добавьте `.md` или `.mdx` к URL (например `https://hubris.pw/docs/quickstart.md`).
* В шапке каждой страницы — кнопки **«Скопировать как Markdown»** и **«Открыть в…»** (ChatGPT / Claude / .md-версия).
* `https://hubris.pw/llms.txt` — индекс всей документации в формате [llmstxt.org](https://llmstxt.org), для AI-агентов.
* `https://hubris.pw/llms-full.txt` — полный дамп всех страниц одним файлом, удобно скармливать в context-окно модели.
## 2026-05 — Запуск публичной документации
Опубликованы первые версии разделов:
* С чего начать: Quickstart, Аутентификация, Миграция с OpenAI.
* Базовые концепции: Модели, Цены, Биллинг, Ошибки, Rate limits, Конфиденциальность.
* API Reference: `/v1/models`, `/v1/chat/completions`, `/v1/responses` (BETA), Streaming.
* Возможности: Reasoning-токены, кеширование промптов.
* Фреймворки: OpenAI SDK Python/Node, Vercel AI SDK, LangChain Python/JS.
API сам по себе — без изменений с релизной версии 1.0. Все стабильные эндпоинты под `/v1/*` остаются совместимыми.
## Подписка на обновления
Пока что — следите за этой страницей. RSS / email-рассылка в работе.
Для критических изменений (breaking change в API, отзыв модели из каталога) — будем дополнительно слать на email вашего аккаунта.
## Если нужно, чтобы конкретная модель не пропала
Если вы строите интеграцию вокруг конкретной модели и боитесь, что её уберут — напишите на [support@hubris.pw](mailto:support@hubris.pw), мы согласуем, как минимум, заранее предупредим о выводе.
---
# FAQ (/docs/faq)
> Часто задаваемые вопросы про Hubris API, биллинг, ключи, поддерживаемые модели.
## Что такое Hubris?
Hubris — единый OpenAI-совместимый API ко всем ведущим LLM. Один ключ, оплата в рублях через СБП, OpenAI-совместимый формат запросов и ответов. Подробнее — на [главной](/docs).
## Через какие модели вы работаете?
Мы агрегатор. Маршрутизируем запросы к лучшим моделям мира — OpenAI, Anthropic, Google, DeepSeek, Mistral и другим — через единое API. Конкретные торговые отношения с поставщиками — наша внутренняя коммерческая информация. Полный список доступных моделей с ценами — в [каталоге](/models).
## Сколько стоит?
Pay-as-you-go без подписок. Платите только за фактически использованные токены, в рублях. Для каждой модели — своя цена за 1М входных и выходных токенов, она видна в каталоге. Подробности расчёта — на странице [Цены](/docs/concepts/pricing).
## Как пополнить баланс?
Через СБП на странице [Биллинг](/billing). Минимально 100 ₽, максимально 10 000 ₽ за одну операцию. Зачисление автоматическое в течение 30 секунд. Подробнее — на странице [Биллинг](/docs/concepts/billing).
## Что если баланс ушёл в минус?
Стоимость запроса известна только после ответа модели. Поэтому возможна ситуация: на балансе было 50 ₽, запрос стоил 60 ₽, баланс ушёл в −10 ₽. Это допустимо на одну операцию. Следующий запрос с отрицательным балансом вернёт 402 — пополнение разблокирует следующие запросы.
## Можно ли получить чек / счёт-фактуру?
Для физических лиц — нет, оплата через СБП с подтверждением банка достаточна. Для юридических лиц — пишите на [support@hubris.pw](mailto:support@hubris.pw), обсудим выставление счёта и закрывающие документы.
## Сохраняете ли вы мои запросы?
Нет. Содержимое запросов и ответов не пишется ни в логи, ни в БД. Передаётся провайдеру модели по TLS и сразу забывается. Подробнее — на странице [Конфиденциальность](/docs/concepts/privacy).
## Какие провайдеры моделей видят мои данные?
Перечень провайдеров, которым потенциально передаётся содержимое запроса, — в [Политике конфиденциальности](/legal/privacy), раздел 8. Кратко: модели Anthropic — Anthropic, OpenAI — OpenAI, Google — Google, и так далее. Каждый имеет свою политику обработки.
## Какие лимиты на ключе?
В стандартном тарифе у пользовательских ключей нет искусственных лимитов — расход ограничен только балансом. Дневной лимит на конкретном ключе можно задать или поменять прямо на странице [API-ключи](/keys): поле «Дневной лимит, ₽» в форме создания и кнопка «изменить» рядом с активным ключом. Подробнее — [Rate limits](/docs/concepts/rate-limits).
## Что делать, если ключ скомпрометирован?
Откройте [/keys](/keys), найдите подозрительный ключ, нажмите «Отозвать». Создайте новый и обновите его во всех местах. Подробнее — [Аутентификация](/docs/authentication).
## Поддерживаете ли вы embeddings? Generation картинок? Audio?
**Embeddings** — да, через [`POST /v1/embeddings`](/docs/api/embeddings). Доступны OpenAI `text-embedding-3-*`, Google Gemini, BGE, MiniLM и другие embedding-модели в каталоге.
**Image generation** и **Audio (Whisper / TTS)** — пока нет. Появятся в ближайших обновлениях. Если нужно срочно — напишите на [support@hubris.pw](mailto:support@hubris.pw), приоритезируем.
## Где changelog?
[/docs/changelog](/docs/changelog) — записи о breaking changes API, новых моделях, важных обновлениях.
## Куда писать про баги?
[support@hubris.pw](mailto:support@hubris.pw) — приложите модель, время запроса в UTC и e-mail аккаунта. Найти конкретный запрос потом поможет [/usage](/usage).
## Можно ли запустить self-hosted?
Сейчас нет. Если у вас регулируемые данные (медицинские, финансовые, гос. тайна) — пишите на [support@hubris.pw](mailto:support@hubris.pw), обсудим архитектуру под ваши требования.
---
# Документация Hubris (/docs)
> OpenAI-совместимый LLM-агрегатор с оплатой в рублях. Один ключ ко всем ведущим моделям.
import { Cards, Card } from 'fumadocs-ui/components/card';
import { Rocket, KeyRound, Boxes, BookOpen, GitCompare, Receipt } from 'lucide-react';
import { ModelLink } from '@/components/docs/model-link';
Hubris — единый OpenAI-совместимый API ко всем ведущим языковым моделям. Маршрутизируем запросы к лучшим моделям мира, с автоматическим переключением на запасную модель при сбоях. Оплата — в рублях через СБП.
## С чего начать
} title="Быстрый старт" href="/docs/quickstart" description="Первый запрос за 60 секунд: cURL, Python, TypeScript." />
} title="Аутентификация" href="/docs/authentication" description="API-ключи в формате sk-gw-, ротация, безопасное хранение." />
} title="Каталог моделей" href="/models" description="400+ моделей, актуальные цены в ₽ за 1M токенов." />
} title="Миграция с OpenAI" href="/docs/migration-from-openai" description="Один base URL — и существующий код работает." />
} title="API Reference" href="/docs/api/overview" description="/v1/chat/completions, /v1/responses, /v1/embeddings, /v1/models." />
} title="Биллинг и цены" href="/docs/concepts/pricing" description="Как считается стоимость, СБП-пополнения, free-модели." />
## Что вы получаете
* **Один API-ключ** для доступа к моделям OpenAI, Anthropic, Google, DeepSeek, Mistral и других.
* **Оплата в рублях** через Систему быстрых платежей. Без валютных карт и зарубежных платёжных провайдеров.
* **OpenAI-совместимый формат** — меняете один URL и подключаете существующий код без переписывания.
* **Прозрачные цены** — итоговая стоимость в ₽ за 1 миллион токенов сразу видна в каталоге.
* **Стриминг ответов** — Server-Sent Events работают как в OpenAI API.
## Популярные модели
* — быстрая и дешёвая Claude от Anthropic, для большинства задач.
* — баланс качества и стоимости от OpenAI.
* — большой контекст и мультимодальность от Google.
Полный список с ценами — в [каталоге моделей](/models).
## Поддержка
Не нашли нужную модель или нужна помощь с интеграцией — напишите на [support@hubris.pw](mailto:support@hubris.pw).
---
# Документация для AI (/docs/llms-txt)
> Скормите всю документацию Hubris в ChatGPT, Claude или любого AI-агента одной ссылкой — и спрашивайте на естественном языке.
import { Cards, Card } from 'fumadocs-ui/components/card';
import { FileText, ListTree, Link2, Sparkles } from 'lucide-react';
Если вы не хотите читать документацию глазами — отдайте её AI-модели и задавайте вопросы голосом или текстом. Мы публикуем всю документацию в специальном формате, который понимают ChatGPT, Claude, Gemini и любые другие LLM.
Это работает без регистрации, без VPN и без копирования вручную. Достаточно одной ссылки.
## Самый быстрый способ — за 30 секунд
1. Откройте [ChatGPT](https://chatgpt.com) или [Claude](https://claude.ai).
2. Скопируйте промпт ниже и вставьте в окно чата.
3. Спрашивайте всё, что хотите узнать о Hubris.
```
Прочитай документацию Hubris по ссылке https://hubris.pw/llms-full.txt
и ответь на мои вопросы. Это OpenAI-совместимый LLM-шлюз с оплатой в рублях.
```
Модель сама загрузит файл, разберёт его и будет отвечать со ссылками на нужные разделы. Дальше можно спрашивать что угодно: «как мне мигрировать с OpenAI?», «сколько стоит Claude Haiku в рублях?», «как включить стриминг в Python?» — модель ответит, опираясь на актуальную документацию.
## Три формата на выбор
} title="llms-full.txt — вся документация одним файлом" href="https://hubris.pw/llms-full.txt" description="Полный дамп всех страниц одним plain-text файлом. Скормите целиком в контекст модели." />
} title="llms.txt — индекс документации" href="https://hubris.pw/llms.txt" description="Краткий список всех страниц со ссылками. Стандарт llmstxt.org для AI-агентов." />
} title=".md-версия любой страницы" href="https://hubris.pw/docs/quickstart.md" description="Добавьте .md или .mdx к URL любой страницы — получите её в чистом Markdown." />
### Когда какой формат использовать
| Сценарий | Что давать модели |
| ----------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- |
| Хотите задавать вопросы по всей документации сразу | [`llms-full.txt`](https://hubris.pw/llms-full.txt) |
| Делаете своего AI-агента и хотите, чтобы он сам выбирал нужные страницы | [`llms.txt`](https://hubris.pw/llms.txt) (индекс) — агент сам подтянет нужное |
| Нужна конкретная одна страница | URL с суффиксом `.md`, например [`/docs/quickstart.md`](https://hubris.pw/docs/quickstart.md) |
| Работаете в Cursor / Windsurf / Claude Code | Добавьте `https://hubris.pw/llms-full.txt` как контекст-документ |
## Готовые промпты
Скопируйте и вставьте — модель сделает остальное.
### Помощник по интеграции
```
Прочитай https://hubris.pw/llms-full.txt — это документация Hubris.
Я хочу подключить Hubris к своему проекту на [Python/Node.js/...].
Расскажи пошагово, что нужно сделать. Дай работающий код для первого запроса.
```
### Миграция с OpenAI
```
Загрузи https://hubris.pw/llms-full.txt и помоги мне мигрировать с OpenAI API на Hubris.
У меня код на [язык]. Что именно нужно поменять?
Объясни, что останется идентичным, а где могут быть отличия.
```
### Выбор модели под задачу
```
Изучи https://hubris.pw/llms-full.txt и каталог моделей https://hubris.pw/models.
Мне нужна модель для [задача — например «короткие классификации текста»,
«генерация длинных статей», «работа с картинками»]. Бюджет — [X ₽ в месяц].
Что посоветуешь? Покажи плюсы-минусы и итоговую цену в рублях за 1М токенов.
```
### Отладка ошибок
```
Я работаю с Hubris API и получаю такую ошибку: [вставьте текст ошибки].
Документация: https://hubris.pw/llms-full.txt
Что это значит и как починить?
```
## Как скормить документацию AI-агенту
### ChatGPT (chatgpt.com)
1. Откройте новый чат.
2. Вставьте: `Прочитай https://hubris.pw/llms-full.txt и жди вопросов.`
3. ChatGPT автоматически загрузит файл через встроенный браузер. Дальше задавайте вопросы как обычно.
Можно ещё надёжнее: скачайте [`llms-full.txt`](https://hubris.pw/llms-full.txt) себе на компьютер и **перетащите файл в окно чата** как вложение. Так модель точно прочитает весь текст без зависимости от веб-поиска.
### Claude.ai (claude.ai)
1. Откройте новый чат на [claude.ai](https://claude.ai).
2. Нажмите на иконку скрепки внизу окна → «Add from URL» → вставьте `https://hubris.pw/llms-full.txt`.
3. Claude добавит документ в Project Knowledge и будет ссылаться на него во всех ответах.
Альтернатива: создайте Project, загрузите файл туда один раз — и все чаты внутри проекта будут видеть документацию.
### Google Gemini / AI Studio
В [Gemini](https://gemini.google.com) или [aistudio.google.com](https://aistudio.google.com) — просто вставьте ссылку `https://hubris.pw/llms-full.txt` в чат с фразой «прочитай и помоги по этой документации». У Gemini 2.5 контекст 1M токенов — вся документация поместится с большим запасом.
### Cursor
1. Откройте Cursor → Settings (⌘,) → **Features** → **Docs**.
2. Нажмите **Add new doc**.
3. Вставьте URL: `https://hubris.pw/llms-full.txt`, дайте имя `Hubris`.
4. В любом чате внутри Cursor напишите `@Hubris` — и документация подтянется в контекст.
### Claude Code
В диалоге Claude Code (терминал или IDE) просто упомяните URL:
```
Прочитай https://hubris.pw/llms-full.txt и помоги интегрировать Hubris в этот проект.
```
Claude Code сам загрузит файл через WebFetch. Чтобы это работало всегда — добавьте ссылку в `CLAUDE.md` вашего проекта:
```md
## Документация по Hubris API
Полная документация: https://hubris.pw/llms-full.txt
Индекс: https://hubris.pw/llms.txt
```
### Cline / Roo-Code / Kilo-Code
В настройках расширения найдите раздел **Custom Instructions** или **Documentation** и добавьте:
```
При работе с Hubris API всегда сверяйся с документацией:
https://hubris.pw/llms-full.txt
```
Агент будет подгружать страницу при необходимости через свой web-fetch tool.
### Continue.dev
В `~/.continue/config.json` добавьте раздел `docs`:
```json
{
"docs": [
{
"title": "Hubris",
"startUrl": "https://hubris.pw/llms-full.txt",
"rootUrl": "https://hubris.pw/docs"
}
]
}
```
После перезапуска Continue в чате будет доступна команда `@Hubris`.
### Любой другой агент
Если ваш агент умеет ходить в web (большинство современных умеют — Perplexity, Phind, Poe, MetaGPT, AutoGen), просто скажите ему:
```
Документация Hubris: https://hubris.pw/llms-full.txt
Прочитай её перед ответами на вопросы про Hubris.
```
## Кнопки на каждой странице
Если вам нужна не вся документация, а одна конкретная страница — в шапке каждой страницы есть кнопка **«Открыть в ChatGPT / Claude»**. Она автоматически передаёт текущую страницу в выбранную AI-модель с готовым промптом.
Рядом — кнопка **«Скопировать как Markdown»**: содержимое страницы в чистом виде, без меню и навигации, готово к вставке в любую модель или ваш редактор.
## Для разработчиков AI-агентов
Если вы делаете чат-бот, RAG-систему, или плагин для IDE — используйте `llms.txt` и `.md`-суффиксы как обычные документы для индексации.
* Индекс [`llms.txt`](https://hubris.pw/llms.txt) соответствует [стандарту llmstxt.org](https://llmstxt.org) — содержит список всех страниц со ссылками на их `.mdx`-версии.
* Любую страницу `/docs/` можно получить как plain Markdown, добавив `.md` или `.mdx` к URL.
* Файл `llms-full.txt` кэшируется на CDN и обновляется при каждом деплое документации.
Все три эндпоинта отдаются с `Content-Type: text/plain; charset=utf-8` и кэшируются на 5 минут на клиенте и 1 час на CDN.
## Часто задаваемые вопросы
**Это бесплатно?** Да. Эндпоинты `llms.txt`, `llms-full.txt` и `.md`-версии страниц доступны без регистрации и без API-ключа.
**Документация всегда свежая?** Да. Файлы пересобираются автоматически при каждом обновлении сайта.
**Можно использовать в своём продукте?** Да. Это публичная документация, ссылайтесь и цитируйте свободно.
**Модель путается или галлюцинирует?** Попробуйте более крупную модель (GPT-5, Claude Sonnet 4.6) или сократите контекст до конкретной страницы через `.md`-суффикс. Полный файл — около 200 КБ, помещается в контекст всех современных моделей.
## Что дальше
* [Быстрый старт](/docs/quickstart) — первый запрос за 60 секунд.
* [Каталог моделей](/models) — выбрать модель по цене и задаче.
* [FAQ](/docs/faq) — короткие ответы на популярные вопросы.
---
# Миграция с OpenAI (/docs/migration-from-openai)
> Как переключиться с OpenAI на Hubris — что меняется, что 1-в-1.
import { EnvVar } from '@/components/docs/env-var';
Если у вас уже есть код, работающий через OpenAI API, для перехода на Hubris достаточно изменить **базовый URL и API-ключ**. Остальное — формат запросов, имена параметров, структура ответа — идентично.
## Что меняется
### 1. Базовый URL
Замените базовый URL OpenAI на `https://api.hubris.pw/v1`. Например, если у вас был стандартный OpenAI base URL — замените его на адрес выше.
### 2. API-ключ
OpenAI-ключ (`sk-...`) → Hubris-ключ (`sk-gw-...`). Получите на [/keys](/keys).
```diff
- api_key = os.environ["OPENAI_API_KEY"]
+ api_key = os.environ["HUBRIS_API_KEY"]
```
### 3. Имена моделей
OpenAI принимает короткие имена (например, `gpt-4o-mini`). Hubris использует полные идентификаторы вида `provider/model` — например, `openai/gpt-4o-mini`. Замените короткое имя на полный идентификатор провайдера.
Полный список — в [каталоге моделей](/models).
## Что НЕ меняется
* Формат тела запроса (`messages`, `tools`, `temperature`, и т. д.).
* Формат ответа (`choices[0].message.content`, `usage.prompt_tokens`, `id`, `created`).
* Стриминг через `stream: true` и Server-Sent Events.
* Инструменты (tool calling) — `tools`, `tool_choice`, `tool_calls`.
* JSON-режим — `response_format: { type: "json_object" }` или `json_schema`.
* Vision (multimodal входы) — `image_url` в `content`.
* OpenAI SDK работает без изменений — нужно только два параметра.
## Минимальный diff кода
### Python (с OpenAI SDK)
```python
from openai import OpenAI
client = OpenAI(
base_url="https://api.hubris.pw/v1", # was OpenAI base URL
api_key=os.environ["HUBRIS_API_KEY"], # was OPENAI_API_KEY
)
response = client.chat.completions.create(
model="openai/gpt-4o-mini", # was "gpt-4o-mini"
messages=[{"role": "user", "content": "Привет"}],
)
```
### Node.js (с OpenAI SDK)
```typescript
import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://api.hubris.pw/v1", // was OpenAI base URL
apiKey: process.env.HUBRIS_API_KEY, // was OPENAI_API_KEY
});
const response = await client.chat.completions.create({
model: "openai/gpt-4o-mini", // was "gpt-4o-mini"
messages: [{ role: "user", content: "Привет" }],
});
```
## Что доступно дополнительно
В Hubris вы получаете **модели не только OpenAI**, через тот же API:
* **Anthropic Claude** — например, `anthropic/claude-haiku-4.5`.
* **Google Gemini** — например, `google/gemini-2.5-flash`.
* **OpenAI GPT** — например, `openai/gpt-4o-mini`.
Полный актуальный список — в [каталоге моделей](/models). Все они отвечают в том же OpenAI-формате `chat.completion`, поэтому код менять не нужно.
## Различия в биллинге
* **Цены в ₽**, не в USD. Списание происходит за каждый успешный запрос; стоимость возвращается в `usage` и логируется на [/usage](/usage).
* **Минимальный баланс** для запуска запроса — 1 ₽. Реальная стоимость списывается после успешного ответа и зависит от модели и числа токенов.
* **Без подписок и тарифов** — pay-as-you-go. Подробнее — на странице [Биллинг](/docs/concepts/billing).
## Что поддерживается на старте
Сейчас Hubris реализует основной OpenAI-эндпоинт `POST /v1/chat/completions` (с инструментами, JSON-режимом, vision, стримингом) и `GET /v1/models`. Этого достаточно для подавляющего большинства интеграций — чат-боты, агенты, RAG, классификация, извлечение данных.
Дополнительные эндпоинты (генерация эмбеддингов, картинок, аудио, batch-обработка) находятся в работе — следите за обновлениями. Для них пока используйте провайдера напрямую: когда Hubris их выпустит, переключение будет такой же сменой URL.
Assistants API (`/v1/assistants`, `/v1/threads`) — не планируется: Stateless API лучше для большинства интеграций.
## Что дальше
* [Быстрый старт](/docs/quickstart) — рабочий пример сразу.
* [Каталог моделей](/models) — все доступные `provider/model`.
* [Аутентификация](/docs/authentication) — детали по ключам.
---
# Быстрый старт (/docs/quickstart)
> Первый запрос к Hubris за 60 секунд. Примеры на cURL, Python, TypeScript и OpenAI SDK.
import { CodeTabs } from '@/components/docs/code-tabs';
import { EnvVar } from '@/components/docs/env-var';
import { EndpointBadge } from '@/components/docs/endpoint-badge';
import { TryItButton } from '@/components/docs/try-it-button';
Подключение к Hubris — это смена одного URL. Если у вас уже есть код под OpenAI API, замените базовый URL на `https://api.hubris.pw/v1` и подставьте ключ Hubris. Всё остальное работает как было.
## 1. Получите API-ключ
[Зарегистрируйтесь](/sign-in) (нужен email и одноразовый код), затем создайте ключ в разделе [API-ключи](/keys). Ключ показывается **один раз** — сохраните его в менеджер паролей или передайте в переменную окружения сразу.
## 2. Пополните баланс
В разделе [Биллинг](/billing) пополните баланс через СБП — минимально 100 ₽. Этого хватит на тысячи запросов к мелким моделям.
## 3. Сделайте первый запрос
Замените `sk-gw-...` на ваш ключ:
## С OpenAI SDK
Если у вас уже OpenAI SDK — поменяйте только `base_url` и ключ.
## Ожидаемый ответ
Стандартный OpenAI-формат:
```json
{
"id": "chatcmpl-...",
"object": "chat.completion",
"created": 1714000000,
"model": "anthropic/claude-haiku-4.5",
"choices": [
{
"index": 0,
"message": { "role": "assistant", "content": "OK" },
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 12,
"completion_tokens": 1,
"total_tokens": 13
}
}
```
Если получили `401` — ключ неверный или отсутствует заголовок `Authorization`. См. [Аутентификацию](/docs/authentication). Если `402` — мало денег на балансе, см. [Биллинг](/billing).
## Что дальше
* **[Аутентификация](/docs/authentication)** — управление ключами, безопасное хранение, ротация.
* **[Каталог моделей](/models)** — все доступные модели с актуальными ценами.
* **[Биллинг](/billing)** — пополнение, минимальный остаток.
---
# Статус сервиса (/docs/status)
> Текущая доступность Hubris API и историческая аптайм-статистика.
Hubris работает 24/7. Если у вас что-то не отвечает — сначала проверьте этот раздел.
## Текущий статус
Проверка живости — `GET /healthz` без авторизации:
```bash
curl https://api.hubris.pw/healthz
# {"status":"ok"}
```
Если получили `200 OK` с `{"status":"ok"}` — сервис в норме, проблема, скорее всего, у вас (баланс, ключ, формат запроса).
Если 5xx или таймаут — проблема на нашей стороне. Подождите 1–2 минуты и попробуйте снова.
## Что мониторим внутри
* Задержки `p50`, `p95`, `p99` на `/v1/chat/completions` для каждой активной модели.
* Доступность каталога `GET /v1/models`.
* Курс ЦБ РФ — обновляется раз в день в 12:00 МСК. Если ЦБ временно недоступен и кеш истёк — каталог отдаст `503` с `code: "exchange_rate_unavailable"`.
* Баланс БД, очереди в Redis, дисковое пространство.
## Если что-то сломалось
1. Посмотрите `/healthz`.
2. Если `/healthz` ок, а конкретная модель отвечает 502 — попробуйте другую модель из той же группы (например, `openai/gpt-4o-mini` вместо `openai/gpt-4o`). Часто это локальная проблема апстрима, не Hubris.
3. Если несколько разных моделей дают 502 одновременно — это инфра. Напишите на [support@hubris.pw](mailto:support@hubris.pw) с временем и request\_id из заголовка ответа.
## Публичный uptime-дашборд
В работе — будет на отдельном поддомене с историей 90 дней. Пока — пишите на support, ответим оперативно.
---
# Поддержка (/docs/support)
> Как связаться с командой Hubris — баги, вопросы, фичи, биллинг.
## Email
Главный канал — **[support@hubris.pw](mailto:support@hubris.pw)**. Отвечаем в рабочие часы по Москве, обычно в течение нескольких часов; критические инциденты — быстрее.
## Что приложить к письму
В зависимости от темы:
**Баг в API**:
* Время запроса (UTC или МСК) и используемую модель.
* Email аккаунта (по нему мы найдём запрос в логах через [/usage](/usage)).
* Тело запроса — без полного содержимого `messages`, достаточно структуры.
* Ответ, который пришёл — HTTP-статус и JSON ошибки.
**Биллинг**:
* Email вашего аккаунта.
* Дата и сумма операции.
* Если не пришло пополнение — ID СБП-перевода (виден в банке).
**Запрос новой модели или фичи**:
* Какая модель / провайдер.
* Юзкейс (что вы делаете — это помогает приоритезировать).
**Юр. лицо / договор**:
* Реквизиты компании.
* Объём планируемого использования (для оценки тарифа).
## Чего НЕ присылать
* **Не присылайте API-ключи** ни в каком виде. Если ключ скомпрометирован — отзывайте на [/keys](/keys), это не требует общения с поддержкой.
* **Не присылайте полное содержимое запросов с персональными данными**. Для воспроизведения бага достаточно структуры и метаданных.
## SLA
Стандартный pay-as-you-go — best-effort, ответ в течение рабочего дня.
Для бизнес-интеграций с гарантированным SLA (часовое реагирование, доступ 24/7) — пишите про enterprise-условия. Согласуем индивидуально.
## Что не делаем
* Не помогаем с написанием prompt-ов и интеграцией под конкретную задачу — это работа разработчика. Документация на сайте + примеры в [API Reference](/docs/api/overview) должны хватить для большинства случаев.
* Не комментируем содержимое ответов моделей — это поведение провайдера, не Hubris.
## Социальные сети
Пока единственный канал — email. Корпоративный аккаунт в Telegram / VK — в работе.
---
# Hello world (/docs/_sandbox/hello)
> Демо-страница со всеми компонентами документации.
import { ModelSampleCode } from '@/components/docs/model-sample-code';
import { EndpointBadge } from '@/components/docs/endpoint-badge';
import { EnvVar } from '@/components/docs/env-var';
import { ModelLink } from '@/components/docs/model-link';
Это sandbox-страница для Phase 1. Если вы её видите — Fumadocs стартанул на Tailwind 4 + React 19 RC.
Установите в окружение.
Попробуйте модель .
## cURL пример
```bash
curl https://api.hubris.pw/v1/models \
-H "Authorization: Bearer sk-gw-..."
```
## Code tabs (cURL / Python / TypeScript)
## Чек-лист
* [x] Маршрут `/docs/_sandbox/hello` отдаёт 200 публично
* [x] Sidebar и breadcrumbs рендерятся
* [x] Cmd+K открывает поиск
* [x] og-image генерится
---
# POST /v1/chat/completions (/docs/api/chat-completions)
> Создать chat completion. OpenAI-совместимый формат, поддержка стриминга, tool calling, structured outputs, vision.
import { CodeTabs } from '@/components/docs/code-tabs';
import { EndpointBadge } from '@/components/docs/endpoint-badge';
Главный эндпоинт Hubris — создаёт ответ языковой модели на цепочку сообщений. OpenAI-совместимый формат — без переписывания работают `openai` Python SDK, OpenAI TypeScript SDK, LangChain, Vercel AI SDK и любые другие клиенты под OpenAI Chat Completions API.
## Эндпоинт
Заголовки:
| Header | Значение |
| --------------- | ---------------------------------------------------------------------------------- |
| `Authorization` | `Bearer sk-gw-...` (обязательно) |
| `Content-Type` | `application/json` (обязательно для тела запроса) |
| `Accept` | `application/json` или `text/event-stream` (опционально; при `stream: true` — SSE) |
## Тело запроса
Обязательные поля помечены **жирным**. Остальное — опциональное.
| Поле | Тип | По умолчанию | Описание |
| ----------------------- | ----------------------------- | --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`model`** | `string` | — | Идентификатор модели, например `anthropic/claude-haiku-4.5`. Список доступных — [`GET /v1/models`](/docs/api/models) или [каталог](/models). |
| **`messages`** | `array` | — | Цепочка сообщений диалога. Минимум 1. Структура — [ниже](#сообщения). |
| `models` | `array` | — | До 10 fallback-моделей. Если основная недоступна — пробует следующие по порядку. Биллинг по фактически использованной (см. `model` в ответе). См. [Model fallbacks](/docs/features/model-fallbacks). |
| `temperature` | `number` 0–2 | `1` | Температура сэмплирования. 0 — детерминизм, 2 — максимум разнообразия. |
| `top_p` | `number` 0–1 | `1` | Nucleus sampling. Альтернатива `temperature` — обычно меняют что-то одно. |
| `n` | `integer` ≥ 1 | `1` | Сколько вариантов ответа сгенерировать. Каждый вариант оплачивается отдельно. |
| `stop` | `string \| string[]` | — | До 4 стоп-последовательностей. Модель прекратит генерацию при их встрече. |
| `max_tokens` | `integer` > 0 | — | Лимит токенов в ответе. OpenAI-совместимое имя. |
| `max_completion_tokens` | `integer` > 0 | — | Новое имя `max_tokens` (OpenAI deprecated старое для reasoning-моделей). Можно использовать любой из двух. |
| `frequency_penalty` | `number` -2…2 | `0` | Штраф за повторение токенов. |
| `presence_penalty` | `number` -2…2 | `0` | Штраф за повторение тем. |
| `logit_bias` | `object` | — | Карта `token_id → bias` (от -100 до 100). Управление вероятностью отдельных токенов. |
| `seed` | `integer` | — | Сид для best-effort детерминизма. Не гарантирует идентичности на разных прогонах. |
| `logprobs` | `boolean` | `false` | Вернуть вероятности выбранных токенов. |
| `top_logprobs` | `integer` 0–20 | — | Сколько top-альтернатив возвращать вместе с каждым выбранным токеном. Требует `logprobs: true`. |
| `tools` | `array` | — | Определения функций, которые модель может вызвать. Структура — [Tools](#tools). |
| `tool_choice` | `string \| object` | `"auto"` | Принудительный выбор инструмента. `"none"` / `"auto"` / `"required"` или `{type:"function", function:{name:"..."}}`. |
| `parallel_tool_calls` | `boolean` | `true` | Разрешить модели вызвать несколько tools в одном ответе. |
| `response_format` | `object` | `{type:"text"}` | Формат ответа. `text`, `json_object` или `json_schema`. См. [Structured outputs](#structured-outputs). |
| `modalities` | `array` | `["text"]` | Какие модальности возвращать. `["image","text"]` для image-gen моделей. |
| `image_config` | `object` | — | Параметры image-gen: `aspect_ratio`, `image_size` и model-specific. См. [Image generation](/docs/features/image-generation). |
| `stream` | `boolean` | `false` | Стриминг ответа через SSE. См. [Стриминг](#стриминг). |
| `stream_options` | `object` | — | Hubris автоматически выставляет `include_usage: true` для всех стримов. Указывать не обязательно. |
| `user` | `string` | — | Идентификатор конечного пользователя для трекинга абьюза на стороне провайдера. |
| `reasoning_effort` | `"low" \| "medium" \| "high"` | — | Для reasoning-моделей: бюджет «думания». Подробности — [Reasoning](/docs/features/reasoning). |
## Сообщения
Каждый элемент `messages` — объект с обязательным полем `role` и зависящими от роли остальными полями.
### system
```json
{ "role": "system", "content": "Ты — лаконичный ассистент." }
```
| Поле | Тип | Описание |
| ------------- | ---------- | ------------------------ |
| **`role`** | `"system"` | Роль. |
| **`content`** | `string` | Системная инструкция. |
| `name` | `string` | Опциональное имя автора. |
### user
```json
{ "role": "user", "content": "Привет! Как дела?" }
```
Content может быть строкой ИЛИ массивом мультимодальных частей (текст + картинки):
```json
{
"role": "user",
"content": [
{ "type": "text", "text": "Что на картинке?" },
{ "type": "image_url", "image_url": { "url": "https://example.com/photo.jpg" } }
]
}
```
Поддерживаемые `type`: `text`, `image_url`. URL может быть `https://...` или `data:image/;base64,...`. Подробности — [Vision](/docs/features/vision).
### assistant
```json
{ "role": "assistant", "content": "Привет! Хорошо, спасибо." }
```
С tool calls:
```json
{
"role": "assistant",
"content": null,
"tool_calls": [
{
"id": "call_abc",
"type": "function",
"function": { "name": "get_weather", "arguments": "{\"city\":\"Москва\"}" }
}
]
}
```
### tool
Результат вызова функции (отправляется после `assistant`-сообщения с `tool_calls`):
```json
{
"role": "tool",
"tool_call_id": "call_abc",
"content": "{\"temp\": -5, \"condition\": \"snow\"}"
}
```
## Tools
Каждый элемент `tools` — `function` (определение пользовательской функции) или `server` (server-side tool провайдера: web search, code interpreter и т.д.; зависит от модели).
### Function tool
```json
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Получить текущую погоду по городу",
"parameters": {
"type": "object",
"properties": { "city": { "type": "string" } },
"required": ["city"]
},
"strict": true
}
}
```
| Поле | Тип | Описание |
| ---------------------- | ------------ | ----------------------------------------------------------------------- |
| **`type`** | `"function"` | Дискриминатор. |
| **`function.name`** | `string` | Имя функции. Только латиница, цифры, `_` и `-`. |
| `function.description` | `string` | Описание для модели — что делает функция и когда её вызывать. |
| `function.parameters` | `object` | JSON Schema аргументов функции. |
| `function.strict` | `boolean` | Если `true`, модель гарантированно вернёт arguments, валидные по схеме. |
Полный гайд с примерами обработки — [Tool calling](/docs/features/tool-calling).
## Structured outputs
Поле `response_format` принимает три формы:
```json
{ "type": "text" }
```
```json
{ "type": "json_object" }
```
```json
{
"type": "json_schema",
"json_schema": {
"name": "city_info",
"schema": {
"type": "object",
"properties": {
"name": { "type": "string" },
"country": { "type": "string" },
"population": { "type": "number" }
},
"required": ["name", "country", "population"]
},
"strict": true
}
}
```
При `json_schema` модель гарантированно вернёт валидный JSON по вашей схеме. Подробности и примеры — [Structured output](/docs/features/structured-output).
## Минимальный пример
## Ответ
```json
{
"id": "chatcmpl-abc123",
"object": "chat.completion",
"created": 1714000000,
"model": "anthropic/claude-haiku-4.5",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "Привет! Чем могу помочь?"
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 12,
"completion_tokens": 7,
"total_tokens": 19,
"cost": 24
}
}
```
### Поля ответа
| Поле | Тип | Описание |
| ----------------------------- | ------------------- | ---------------------------------------------------------------------------------------------------- |
| **`id`** | `string` | Уникальный идентификатор completion. Префикс `chatcmpl-`. |
| **`object`** | `"chat.completion"` | Тип объекта. Для стрима — `"chat.completion.chunk"`. |
| **`created`** | `integer` | Unix-timestamp (секунды). |
| **`model`** | `string` | Фактически использованная модель (отличается от запрошенной при срабатывании `models` fallback). |
| **`choices[]`** | `array` | Варианты ответа. Длина равна `n` из запроса (по умолчанию 1). |
| **`choices[].index`** | `integer` | Индекс варианта (0-based). |
| **`choices[].message`** | `object` | Сообщение модели с `role: "assistant"`, `content` и/или `tool_calls`. |
| **`choices[].finish_reason`** | `string` | Почему генерация завершилась: `stop` / `length` / `tool_calls` / `content_filter` / `function_call`. |
| `choices[].logprobs` | `object` | Если в запросе был `logprobs: true`. |
| **`usage.prompt_tokens`** | `integer` | Токены ввода. |
| **`usage.completion_tokens`** | `integer` | Токены вывода. |
| **`usage.total_tokens`** | `integer` | Сумма. |
| **`usage.cost`** | `integer` | Стоимость запроса **в копейках**. Hubris-расширение поверх стандартного OpenAI ответа. |
| `system_fingerprint` | `string` | Метка конфигурации модели у провайдера (для отладки воспроизводимости). |
## Стриминг
С `stream: true` ответ приходит chunk-ами через Server-Sent Events:
```bash
curl -N -s https://api.hubris.pw/v1/chat/completions \
-H "Authorization: Bearer sk-gw-..." \
-H "Content-Type: application/json" \
-d '{
"model": "anthropic/claude-haiku-4.5",
"messages": [{"role": "user", "content": "Расскажи короткую историю"}],
"stream": true
}'
```
Формат каждого chunk, отслеживание usage и обработка `[DONE]` — на странице [Стриминг](/docs/features/streaming).
## Tool calling
```bash
curl -s https://api.hubris.pw/v1/chat/completions \
-H "Authorization: Bearer sk-gw-..." \
-H "Content-Type: application/json" \
-d '{
"model": "anthropic/claude-haiku-4.5",
"messages": [{"role": "user", "content": "Какая погода в Москве?"}],
"tools": [{
"type": "function",
"function": {
"name": "get_weather",
"description": "Получить текущую погоду по городу",
"parameters": {
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"]
}
}
}]
}'
```
Модель вернёт `tool_calls` вместо обычного `content` — выполните функцию у себя и отправьте результат вторым запросом с `role: "tool"`. Полный сценарий — [Tool calling](/docs/features/tool-calling).
## Vision
```bash
curl -s https://api.hubris.pw/v1/chat/completions \
-H "Authorization: Bearer sk-gw-..." \
-H "Content-Type: application/json" \
-d '{
"model": "anthropic/claude-haiku-4.5",
"messages": [{
"role": "user",
"content": [
{"type": "text", "text": "Что на картинке?"},
{"type": "image_url", "image_url": {"url": "https://example.com/photo.jpg"}}
]
}]
}'
```
Поддерживается только моделями с `image` в `input_modalities` — см. [GET /v1/models](/docs/api/models) и страницу [Vision](/docs/features/vision).
## HTTP-коды
| Код | Когда | Полный список — |
| ----------------- | ------------------------------------------- | ------------------------------------------------- |
| `200` | Успешный ответ | — |
| `400` | Тело запроса не соответствует схеме | [Ошибки](/docs/concepts/errors) |
| `401` | Ключ отсутствует / отозван / невалиден | [Ошибки](/docs/concepts/errors) |
| `402` | Недостаточно средств на балансе | [Биллинг](/docs/concepts/billing) |
| `404` | Модели с таким `model` не существует | [Каталог](/models) |
| `429` | Превышен дневной лимит на ключе | [Rate limits](/docs/concepts/rate-limits) |
| `502 / 503 / 504` | Транзиентные сбои у провайдера или курса ЦБ | [Обработка ошибок](/docs/features/error-handling) |
Формат тела ошибки и стратегия retry — [Ошибки](/docs/concepts/errors).
## Что дальше
* [Стриминг](/docs/features/streaming) — формат SSE и обработка в клиенте.
* [Tool calling](/docs/features/tool-calling) — пошаговый сценарий с многократными итерациями.
* [Structured output](/docs/features/structured-output) — JSON Schema validation.
* [Ошибки](/docs/concepts/errors) — таблица всех кодов и retry-стратегий.
* [Цены](/docs/concepts/pricing) — как считается `usage.cost`.
---
# POST /v1/embeddings (/docs/api/embeddings)
> Создать эмбеддинги — вектор фиксированной длины из текста. OpenAI-совместимый формат.
import { CodeTabs } from '@/components/docs/code-tabs';
import { EndpointBadge } from '@/components/docs/endpoint-badge';
Превращает текст в вектор чисел фиксированной длины — для семантического поиска, RAG, классификации, кластеризации. OpenAI-совместимый формат — без переписывания работают `openai` Python/TypeScript SDK, LangChain, LlamaIndex и любые клиенты под OpenAI Embeddings API.
Стрима нет: один POST → один JSON-ответ. Полный список embedding-моделей — в [каталоге](/models) (фильтр «embeddings»).
## Эндпоинт
Заголовки:
| Header | Значение |
| --------------- | -------------------------------- |
| `Authorization` | `Bearer sk-gw-...` (обязательно) |
| `Content-Type` | `application/json` (обязательно) |
## Тело запроса
| Поле | Тип | По умолчанию | Описание |
| ----------------- | ---------------------------------------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------- |
| **`model`** | `string` | — | ID embedding-модели, например `openai/text-embedding-3-small`. |
| **`input`** | `string \| string[] \| number[] \| number[][]` | — | Текст. Поддерживает: одну строку, массив строк (батч), массив токен-ID или массив массивов токен-ID. Максимум 2048 элементов в батче. |
| `encoding_format` | `"float" \| "base64"` | `"float"` | `float` — массив чисел в `embedding`. `base64` — base64-строка (\~30% меньше трафика). |
| `dimensions` | `integer` > 0 | — | Уменьшенная размерность вектора. Поддерживается только моделями `openai/text-embedding-3-*`. |
| `user` | `string` | — | Идентификатор конечного пользователя для трекинга абьюза на стороне провайдера. |
## Минимальный пример
## Ответ
```json
{
"object": "list",
"data": [
{
"object": "embedding",
"index": 0,
"embedding": [-0.012, 0.034, 0.089, ...]
}
],
"model": "openai/text-embedding-3-small",
"usage": {
"prompt_tokens": 4,
"total_tokens": 4,
"cost": 1
}
}
```
### Поля ответа
| Поле | Тип | Описание |
| ------------------------- | -------------------- | -------------------------------------------------------------------------- |
| **`object`** | `"list"` | Тип контейнера. |
| **`data[]`** | `array` | Векторы в том же порядке что и `input`. |
| **`data[].object`** | `"embedding"` | Тип элемента. |
| **`data[].index`** | `integer` | Позиция в исходном `input` (0-based). Полезно при батче. |
| **`data[].embedding`** | `number[] \| string` | Сам вектор. `number[]` при `float`, base64-строка при `base64`. |
| **`model`** | `string` | Фактически использованная модель. |
| **`usage.prompt_tokens`** | `integer` | Токены ввода. |
| **`usage.total_tokens`** | `integer` | Сумма. У embeddings всегда равна `prompt_tokens` — completion-токенов нет. |
| **`usage.cost`** | `integer` | Стоимость **в копейках**. Hubris-расширение. |
## Батч
`input` принимает массив строк — за один запрос до 2048 эмбеддингов. Это значительно быстрее и дешевле, чем 2048 одиночных запросов.
```bash
curl -s https://api.hubris.pw/v1/embeddings \
-H "Authorization: Bearer sk-gw-..." \
-H "Content-Type: application/json" \
-d '{
"model": "openai/text-embedding-3-small",
"input": ["первый текст", "второй текст", "третий текст"]
}'
```
В ответе `data` — массив с тем же порядком, что и `input`. Поле `index` указывает позицию.
## Уменьшенная размерность
Параметр `dimensions` поддерживается моделями `openai/text-embedding-3-small` (по умолчанию 1536) и `openai/text-embedding-3-large` (по умолчанию 3072). Меньшая размерность = меньше места в vector store и быстрее cosine similarity, но падает качество поиска.
```bash
curl -s https://api.hubris.pw/v1/embeddings \
-H "Authorization: Bearer sk-gw-..." \
-H "Content-Type: application/json" \
-d '{
"model": "openai/text-embedding-3-small",
"input": "Привет",
"dimensions": 256
}'
```
## Base64-encoding
`encoding_format: "base64"` возвращает вектор как base64-строку вместо массива чисел — экономит \~30% трафика. Многие SDK сами декодируют обратно в float-массив.
```bash
curl -s https://api.hubris.pw/v1/embeddings \
-H "Authorization: Bearer sk-gw-..." \
-H "Content-Type: application/json" \
-d '{
"model": "openai/text-embedding-3-small",
"input": "Привет",
"encoding_format": "base64"
}'
```
## HTTP-коды
| Код | Когда |
| ----------------- | --------------------------------------------------------------------------------------------------------- |
| `200` | Успешный ответ. |
| `400` | Тело запроса не соответствует схеме (`input` пустой, размер батча > 2048, неподдерживаемый `dimensions`). |
| `401` | Ключ отсутствует / отозван / невалиден. |
| `402` | Недостаточно средств на балансе. |
| `404` | Модели с таким `model` не существует или это не embedding-модель. |
| `429` | Превышен дневной лимит на ключе. |
| `502 / 503 / 504` | Транзиентные сбои у провайдера или курса ЦБ. |
Формат тела ошибки и стратегия retry — [Ошибки](/docs/concepts/errors).
## Биллинг
Embeddings тарифицируются только по входным токенам — completion-токенов нет. Точная формула — на странице [Цены](/docs/concepts/pricing). Цены в ₽ за 1 миллион токенов и весь список embedding-моделей — в [каталоге](/models) с фильтром по выходной модальности «embeddings».
## Что дальше
* [Модели](/docs/concepts/models) — как выбрать embedding-модель.
* [Ошибки](/docs/concepts/errors) — что делать на 429/502.
* [Цены](/docs/concepts/pricing) — формула расчёта.
---
# POST /v1/messages (/docs/api/messages)
> Эндпоинт, совместимый с Anthropic Messages API. Используется Claude Code и любым SDK Anthropic.
import { CodeTabs } from '@/components/docs/code-tabs';
import { EndpointBadge } from '@/components/docs/endpoint-badge';
`/v1/messages` — эндпоинт, совместимый с [Anthropic Messages API](https://docs.anthropic.com/claude/reference/messages_post). Через него работают [Claude Code](/docs/integrations/claude-code), официальные SDK Anthropic для Python/TypeScript и любые инструменты, которые ожидают именно этот формат запросов.
## Когда использовать
* Вы подключаете Claude Code или другой инструмент, поддерживающий только Anthropic-формат.
* Вам нужно нативное `prompt caching` (`cache_control: { "type": "ephemeral" }`).
* Вам удобнее работать с Anthropic-style блоками контента (`text`, `image`, `tool_use`, `tool_result`).
Для типичных задач — [POST /v1/chat/completions](/docs/api/chat-completions) проще и поддерживает больше моделей.
## Эндпоинт
## Аутентификация
Принимаются оба заголовка:
| Заголовок | Когда используется |
| --------------------------------- | ---------------------------------------------------------------- |
| `Authorization: Bearer sk-gw-...` | Claude Code (`ANTHROPIC_AUTH_TOKEN`), произвольные cURL-запросы. |
| `x-api-key: sk-gw-...` | Anthropic Python/TypeScript SDK по умолчанию. |
Если переданы оба — приоритет у `Authorization`. Подойдут API-ключи Hubris формата `sk-gw-…`, созданные на [/keys](https://hubris.pw/keys).
Также нужен `anthropic-version: 2023-06-01` (Anthropic-спека) и `Content-Type: application/json`.
## Тело запроса
| Поле | Тип | По умолчанию | Описание |
| ------------------ | ------------------- | --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`model`** | `string` | — | Идентификатор модели. Принимает Anthropic-имена (`claude-sonnet-4-5`) и каноничные Hubris-имена (`anthropic/claude-sonnet-4.6`). См. [Имена моделей](#имена-моделей). |
| **`max_tokens`** | `integer` > 0 | — | Лимит токенов в ответе. Обязательное (Anthropic-спека). |
| **`messages`** | `array` | — | Цепочка сообщений. Минимум 1. `role: "user" \| "assistant"`. |
| `system` | `string \| Block[]` | — | Системная инструкция (отдельно от `messages`, как в Anthropic). |
| `tools` | `array` | — | Anthropic Tool Use. См. [ниже](#использование-инструментов-tools). |
| `tool_choice` | `object` | `{type:"auto"}` | `{type:"auto" \| "any" \| "tool", name?}`. |
| `temperature` | `number` 0–1 | `1` | Температура сэмплирования. |
| `top_p` | `number` 0–1 | — | Nucleus sampling. |
| `top_k` | `integer` ≥ 0 | — | Top-K sampling. |
| `stop_sequences` | `string[]` | — | До 4 стоп-последовательностей. |
| `stream` | `boolean` | `false` | Anthropic SSE. См. [Потоковая передача](#потоковая-передача). |
| `metadata.user_id` | `string` | — | Anthropic-метка пользователя для трекинга абьюза. |
## Минимальный запрос
`max_tokens` — обязательное поле (Anthropic-спека). Если не указать, вернётся `400 invalid_request_error`.
## Имена моделей
Эндпоинт принимает три формы записи имени модели:
| Форма | Пример | Куда маппится |
| ----------------------------------- | ------------------------------------------------------- | ----------------------------------------------------------- |
| Каноничная (с префиксом провайдера) | `anthropic/claude-sonnet-4.6` | без изменений |
| Стабильная (без даты) | `claude-sonnet-4-5`, `claude-haiku-4-5` | `anthropic/claude-sonnet-4.5`, `anthropic/claude-haiku-4.5` |
| С суффиксом даты или `-latest` | `claude-3-5-sonnet-20241022`, `claude-haiku-4-5-latest` | соответствующая каноничная версия |
Список активных Claude-моделей доступен через [/v1/models](/docs/api/models) с фильтром `provider=anthropic`.
## Ответ
Anthropic-нативный формат:
```json
{
"id": "msg_01XYZ",
"type": "message",
"role": "assistant",
"content": [
{ "type": "text", "text": "Привет!" }
],
"model": "claude-sonnet-4-5",
"stop_reason": "end_turn",
"stop_sequence": null,
"usage": {
"input_tokens": 17,
"output_tokens": 5,
"cache_creation_input_tokens": 0,
"cache_read_input_tokens": 0
}
}
```
Поле `model` в ответе содержит то имя, которое вы прислали в запросе (а не каноничное), — это удобно для согласованности логов и совместимости со старыми SDK.
## Потоковая передача
Параметр `stream: true` включает Anthropic SSE в нативном формате:
```
event: message_start
data: {"type":"message_start","message":{...,"usage":{"input_tokens":17}}}
event: content_block_start
data: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""}}
event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"Привет"}}
event: content_block_stop
data: {"type":"content_block_stop","index":0}
event: message_delta
data: {"type":"message_delta","delta":{"stop_reason":"end_turn"},"usage":{"output_tokens":5}}
event: message_stop
data: {"type":"message_stop"}
event: data
data: [DONE]
```
Финальное `usage` приходит в событии `message_delta`. Если клиент разорвал соединение раньше — Hubris всё равно дочитывает апстрим до конца и списывает стоимость с баланса.
## Использование инструментов (tools)
Поддерживается полный набор Anthropic Tool Use: `tools`, `tool_choice`, блоки `tool_use` в ответе, блоки `tool_result` во входе. Параметры передаются без изменений на провайдера:
```bash
curl https://api.hubris.pw/v1/messages \
-H "Authorization: Bearer sk-gw-..." \
-d '{
"model": "claude-sonnet-4-5",
"max_tokens": 1024,
"tools": [{
"name": "get_weather",
"description": "Возвращает погоду в указанном городе",
"input_schema": {
"type": "object",
"properties": { "city": { "type": "string" } },
"required": ["city"]
}
}],
"messages": [
{ "role": "user", "content": "Какая погода в Москве?" }
]
}'
```
## Vision (изображения на вход)
Блоки `image` в `content[]` принимают и base64, и URL-источник:
```json
{
"role": "user",
"content": [
{ "type": "text", "text": "Что на картинке?" },
{
"type": "image",
"source": {
"type": "base64",
"media_type": "image/png",
"data": "iVBORw0KGgoAAAA..."
}
}
]
}
```
## Кэширование промптов
`cache_control: { "type": "ephemeral" }` ставится на блок контента или инструмента так же, как и в нативном Anthropic API. Hubris передаёт поле без изменений; биллинг учитывает `cache_creation_input_tokens` и `cache_read_input_tokens` по фактической стоимости, которую вернул провайдер.
```json
{
"role": "user",
"content": [
{
"type": "text",
"text": "<длинный системный контекст>",
"cache_control": { "type": "ephemeral" }
},
{ "type": "text", "text": "Вопрос пользователя" }
]
}
```
## Ошибки
Возвращаются в нативном Anthropic-формате:
```json
{
"type": "error",
"error": {
"type": "invalid_request_error",
"message": "max_tokens: Required"
}
}
```
| HTTP | error.type | Что значит |
| --------- | ----------------------- | ------------------------------------------------------------------------- |
| 400 | `invalid_request_error` | Неверное тело запроса (нет `max_tokens`, пустые `messages` и т.д.). |
| 401 | `authentication_error` | Невалидный API-ключ или проблемы у апстрима. |
| 402 | `billing_error` | Баланс ниже минимума. Пополните на [/billing](https://hubris.pw/billing). |
| 404 | `not_found_error` | Модель не найдена в каталоге или имя не парсится. |
| 429 | `rate_limit_error` | Превышен дневной лимит ключа или лимит апстрима. |
| 500 / 502 | `api_error` | Внутренняя ошибка апстрима. |
| 504 | `timeout_error` | Апстрим не ответил в отведённое время. |
## Privacy Mode
На этом эндпоинте Privacy Mode (маскирование PII) **пока не поддерживается** — для запросов с маскированием используйте [POST /v1/chat/completions](/docs/api/chat-completions). Это ограничение MVP, поддержка планируется.
## Тарификация
Считаем по `usage.cost`, который провайдер возвращает в ответе (включая поправку на cache hit / cache write). В редком случае, когда `cost` отсутствует, переключаемся на расчёт по токенам и каталожной цене модели. Подробности — в [Биллинге](/docs/concepts/billing).
## Что дальше
* [Подключение Claude Code](/docs/integrations/claude-code) — подробный гид с настройкой и statusline-скриптом.
* [POST /v1/chat/completions](/docs/api/chat-completions) — основной эндпоинт, поддерживает больше моделей.
* [Каталог моделей](/models) — фильтр `provider:anthropic` для списка Claude-моделей.
* [Ошибки](/docs/concepts/errors) — полная таблица кодов.
---
# GET /v1/models (/docs/api/models)
> Список доступных моделей с актуальными ценами в рублях.
import { CodeTabs } from '@/components/docs/code-tabs';
import { EndpointBadge } from '@/components/docs/endpoint-badge';
Возвращает список всех активных моделей с актуальными ценами в ₽. Стандартный OpenAI-формат с расширениями Hubris: `display_name`, `description`, `context_window`, `modalities`, `pricing`.
Полный [каталог в UI](/models) — там удобнее сравнивать цены и фильтровать.
## Эндпоинт
Заголовки:
| Header | Значение |
| --------------- | -------------------------------- |
| `Authorization` | `Bearer sk-gw-...` (обязательно) |
Тело запроса не передаётся. Параметры query-string не поддерживаются — для фильтрации обрабатывайте ответ на стороне клиента.
## Пример
console.log(m.id));`
},
]}
/>
## Ответ
```json
{
"object": "list",
"data": [
{
"id": "anthropic/claude-haiku-4.5",
"object": "model",
"created": 1714000000,
"owned_by": "anthropic",
"display_name": "Claude Haiku 4.5",
"description": "Быстрая и дешёвая модель Anthropic. Подходит для большинства задач.",
"context_window": 200000,
"max_output_tokens": 8192,
"modalities": {
"input": ["text", "image"],
"output": ["text"]
},
"pricing": {
"prompt_per_1m_rub": 96.50,
"completion_per_1m_rub": 482.50,
"image_input_per_1k_rub": 14.48
}
}
]
}
```
### Поля ответа
| Поле | Тип | Описание |
| ------------------------------------------ | -------------- | -------------------------------------------------------------------------------- |
| **`object`** | `"list"` | Тип контейнера. |
| **`data[]`** | `array` | Массив моделей. |
| **`data[].id`** | `string` | Идентификатор для использования в `model` запроса (формат `/`). |
| **`data[].object`** | `"model"` | Тип элемента. |
| **`data[].created`** | `integer` | Unix-timestamp когда модель появилась в каталоге Hubris. |
| **`data[].owned_by`** | `string` | Провайдер модели (`anthropic`, `openai`, `google`, ...). |
| **`data[].display_name`** | `string` | Человеко-читаемое имя для UI. |
| **`data[].description`** | `string` | Краткое описание модели. |
| **`data[].context_window`** | `integer` | Размер контекста в токенах. |
| **`data[].max_output_tokens`** | `integer` | Максимум токенов в ответе. |
| **`data[].modalities.input`** | `string[]` | Что принимает: `text`, `image`, `audio`. |
| **`data[].modalities.output`** | `string[]` | Что возвращает: `text`, `image`, `embeddings`. |
| **`data[].pricing.prompt_per_1m_rub`** | `number` | Цена входных токенов за 1 миллион, в ₽. |
| **`data[].pricing.completion_per_1m_rub`** | `number` | Цена выходных токенов за 1 миллион, в ₽. |
| `data[].pricing.image_input_per_1k_rub` | `number` | Цена 1000 входных изображений (для vision-моделей). |
Цены пересчитываются каждые несколько часов по курсу ЦБ РФ + наш markup. Точная формула — [Цены](/docs/concepts/pricing).
## Фильтрация
API не поддерживает query-параметры. Фильтруйте локально:
```python
models = client.models.list().data
# Только embedding-модели
embed_models = [m for m in models if 'embeddings' in m.modalities['output']]
# Vision-модели Anthropic дешевле 100₽/1M токенов
cheap_vision = [
m for m in models
if m.owned_by == 'anthropic'
and 'image' in m.modalities['input']
and m.pricing['prompt_per_1m_rub'] < 100
]
```
## HTTP-коды
| Код | Когда |
| ----- | ----------------------------------------------------------------------------- |
| `200` | Успешный ответ. |
| `401` | Ключ отсутствует / отозван / невалиден. |
| `429` | Превышен дневной лимит на ключе. |
| `503` | Курс ЦБ временно недоступен — каталог не отдаёт цены. Повторите через минуту. |
## Что дальше
* [Модели](/docs/concepts/models) — обзор каталога и как выбрать модель.
* [Цены](/docs/concepts/pricing) — формула расчёта стоимости.
* [Каталог в UI](/models) — удобнее искать и сравнивать.
---
# Обзор API (/docs/api/overview)
> Базовый URL, заголовки, аутентификация и общие соглашения Hubris API.
Hubris API совместим с OpenAI Chat Completions API. Все запросы — поверх HTTPS, авторизация через Bearer-токен, ответы — JSON с UTF-8.
## Базовый URL
```
https://api.hubris.pw/v1
```
Все эндпоинты идут от этого префикса. Например, `https://api.hubris.pw/v1/chat/completions` или `https://api.hubris.pw/v1/models`.
## Заголовки
Обязательные:
* `Authorization: Bearer sk-gw-<32-hex>` — ваш API-ключ. См. [Аутентификацию](/docs/authentication).
* `Content-Type: application/json` — для всех `POST`-запросов.
Опциональные:
* `Accept: application/json` — явно ожидать JSON-ответ. По умолчанию мы и так отдаём JSON.
* `Accept: text/event-stream` — при `stream: true` сервер автоматически переключается в SSE-режим.
## Эндпоинты
| Метод | Путь | Описание |
| ----- | ---------------------- | ---------------------------------------------------- |
| GET | `/v1/models` | Список доступных моделей с ценами в ₽ |
| POST | `/v1/chat/completions` | Создать ответ модели (с потоковой передачей или без) |
| POST | `/v1/responses` | Создать ответ через Responses API (бета) |
## Версионирование
Префикс `/v1` — стабильный. Все несовместимые изменения эндпоинтов будут идти под `/v2/...` (или новее), не ломая существующие интеграции.
Метка **бета** у `/v1/responses` относится только к составу внутренних блоков ответа (`output[]`) — туда со временем добавляются новые типы (рассуждения, вызовы инструментов и т.д.) по мере того, как их вводят провайдеры моделей. Авторизация, тарификация и потоковая передача — стабильны и подходят для продакшена. Если ваш клиент строго разбирает конкретные типы блоков, будьте готовы расширять обработку по мере их появления.
## Идемпотентность
`POST /v1/chat/completions` и `POST /v1/responses` не идемпотентны — каждый запрос порождает новый ответ модели, независимо от тела. Если у вас на стороне клиента включены повторы при сбоях, помните: каждая повторная попытка спишет деньги, если первая прошла успешно (что иногда бывает при кратковременных сбоях сети).
`GET /v1/models` идемпотентен — ответ кешируется на стороне Hubris \~60 секунд.
## Потоковая передача
При `stream: true` ответ приходит через Server-Sent Events. Подробности формата — на отдельной странице [Потоковая передача](/docs/api/streaming).
## Ошибки
Все ошибки в OpenAI-формате `{ error: { message, type, code } }`. Полный список кодов — на странице [Ошибки](/docs/concepts/errors).
## Что дальше
* [POST /v1/chat/completions](/docs/api/chat-completions) — основной эндпоинт.
* [GET /v1/models](/docs/api/models) — каталог.
* [Потоковая передача](/docs/api/streaming) — формат SSE.
* [POST /v1/responses](/docs/api/responses) — бета-эндпоинт для Responses API (рассуждающие модели, Codex CLI и т.п.).
---
# POST /v1/responses (бета) (/docs/api/responses)
> Эндпоинт OpenAI Responses API. Подходит для рассуждающих моделей и агентов.
import { CodeTabs } from '@/components/docs/code-tabs';
import { EndpointBadge } from '@/components/docs/endpoint-badge';
> **Бета.** Авторизация, тарификация и потоковая передача стабильны и подходят для продакшена. Метка беты относится к составу внутренних блоков `output[]` (текст, рассуждения, вызовы инструментов) — туда со временем добавляются новые типы по мере появления у провайдеров моделей. Если ваш клиент строго разбирает конкретные блоки — будьте готовы расширять обработку.
`/v1/responses` — альтернативный эндпоинт OpenAI Responses API (без сохранения состояния на стороне сервера). Подходит для рассуждающих моделей (o-серия, Claude с расширенным рассуждением), для агентских CLI вроде Codex и для абстракций, где нужен более гибкий контейнер ответа, чем `chat.completion`.
## Когда использовать
Чаще всего вам нужен [POST /v1/chat/completions](/docs/api/chat-completions) — он совместим с большинством OpenAI-инструментов. `/v1/responses` стоит выбрать только если:
* Вы работаете с рассуждающими моделями (o1, o3, Claude с расширенным рассуждением) и хотите явно управлять блоками рассуждения.
* Вы строите агентский фреймворк, которому нужен единый контейнер для разных типов выходных блоков.
* Используете агентский CLI вроде [Codex](/docs/frameworks/codex-cli), который ходит только в Responses API.
* Конкретный клиент или провайдер требует именно этот формат.
## Эндпоинт
Заголовки:
| Header | Значение |
| --------------- | ---------------------------------------------------------------------------------- |
| `Authorization` | `Bearer sk-gw-...` (обязательно) |
| `Content-Type` | `application/json` (обязательно) |
| `Accept` | `application/json` или `text/event-stream` (опционально; при `stream: true` — SSE) |
## Тело запроса
| Поле | Тип | По умолчанию | Описание |
| ----------- | -------------------- | ------------ | -------------------------------------------------------------------------------------------------------------------------------------- |
| **`model`** | `string` | — | Идентификатор модели, например `openai/o1-mini`. |
| **`input`** | `string \| object[]` | — | Запрос. Простая строка ИЛИ массив input-блоков (см. [OpenAI Responses API](https://platform.openai.com/docs/api-reference/responses)). |
| `stream` | `boolean` | `false` | Стриминг ответа через SSE. |
Дополнительные параметры (instructions, tools, temperature и т.д.) передаются модели как есть — Hubris их не нормализует. Полная схема — в [OpenAI Responses API docs](https://platform.openai.com/docs/api-reference/responses).
## Минимальный пример
`input` может быть строкой или массивом блоков — передаваемый формат зависит от модели.
## Ответ
```json
{
"id": "resp_abc123",
"object": "response",
"created_at": 1714000000,
"model": "openai/gpt-4o-mini",
"output": [
{
"type": "message",
"role": "assistant",
"content": [
{ "type": "output_text", "text": "Привет! Хорошо, спасибо." }
]
}
],
"usage": {
"input_tokens": 8,
"output_tokens": 12,
"total_tokens": 20,
"cost": 24
},
"status": "completed"
}
```
### Поля ответа
| Поле | Тип | Описание |
| ------------------------- | ------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`id`** | `string` | Уникальный идентификатор response. Префикс `resp_`. |
| **`object`** | `"response"` | Тип объекта. |
| **`created_at`** | `integer` | Unix-timestamp (секунды). |
| **`model`** | `string` | Фактически использованная модель. |
| **`output[]`** | `array` | Выходные блоки. Структура зависит от модели и версии — может содержать `message`, `reasoning`, `tool_call` и другие типы. Hubris передаёт как есть, без нормализации. |
| **`usage.input_tokens`** | `integer` | Токены ввода. |
| **`usage.output_tokens`** | `integer` | Токены вывода (включая reasoning-токены, если модель их использует). |
| **`usage.total_tokens`** | `integer` | Сумма. |
| **`usage.cost`** | `integer` | Стоимость **в копейках**. Hubris-расширение. |
| **`status`** | `"completed" \| "in_progress" \| "failed" \| ...` | Состояние генерации. |
## Стриминг
`stream: true` включает SSE. Завершающее событие `response.completed` содержит полный объект `response` вместе с `usage`. Hubris списывает стоимость из этого события (или, в редком случае обрыва до его прихода, — из оценки по длине ввода).
```bash
curl -N -s https://api.hubris.pw/v1/responses \
-H "Authorization: Bearer sk-gw-..." \
-H "Content-Type: application/json" \
-d '{
"model": "openai/gpt-4o-mini",
"input": "Объясни квантовую запутанность",
"stream": true
}'
```
Формат событий и их обработка — на странице [Стриминг](/docs/features/streaming).
## HTTP-коды
| Код | Когда |
| ----------------- | ----------------------------------------------------------------------- |
| `200` | Успешный ответ. |
| `400` | Тело запроса не соответствует схеме. |
| `401` | Ключ отсутствует / отозван / невалиден. |
| `402` | Недостаточно средств на балансе. |
| `404` | Модели с таким `model` не существует или не поддерживает Responses API. |
| `429` | Превышен дневной лимит на ключе. |
| `502 / 503 / 504` | Транзиентные сбои у провайдера или курса ЦБ. |
Формат тела ошибки и стратегия retry — [Ошибки](/docs/concepts/errors).
## Тарификация
Считаем так же, как и для `/v1/chat/completions`: входные + выходные токены × цены модели в ₽. Токены рассуждения (если модель их использует) учитываются в `output_tokens` и тарифицируются как обычные выходные токены.
## Что дальше
* [POST /v1/chat/completions](/docs/api/chat-completions) — основной эндпоинт, подходит большинству задач.
* [Codex CLI](/docs/frameworks/codex-cli) — как подключить агентский CLI к Hubris через Responses API.
* [Стриминг](/docs/features/streaming) — формат событий SSE.
* [Ошибки](/docs/concepts/errors) — таблица кодов.
---
# Потоковая передача (/docs/api/streaming)
> Формат Server-Sent Events для потокового ответа /v1/chat/completions.
При `stream: true` ответ от `/v1/chat/completions` приходит как Server-Sent Events: каждый chunk — отдельная строка `data: ` с разделителем `\n\n`. Поток завершается строкой `data: [DONE]`.
## Заголовки ответа
```
HTTP/1.1 200 OK
Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive
X-Accel-Buffering: no
```
## Формат chunk-а
Каждый chunk — JSON-объект `chat.completion.chunk`:
```json
{
"id": "chatcmpl-abc123",
"object": "chat.completion.chunk",
"created": 1714000000,
"model": "anthropic/claude-haiku-4.5",
"choices": [
{
"index": 0,
"delta": { "role": "assistant", "content": "О" },
"finish_reason": null
}
]
}
```
В первом chunk-е `delta` обычно содержит `role: "assistant"`. Следующие — только инкремент `content`. Последний chunk перед `[DONE]` имеет `finish_reason: "stop"` (или `length`, `tool_calls`, и т.д.) и поле `usage` с итоговыми токенами:
```json
{
"id": "chatcmpl-abc123",
"object": "chat.completion.chunk",
"created": 1714000000,
"model": "anthropic/claude-haiku-4.5",
"choices": [
{
"index": 0,
"delta": {},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 12,
"completion_tokens": 1,
"total_tokens": 13
}
}
```
После `usage`-chunk-а поток завершается:
```
data: [DONE]
```
Hubris автоматически добавляет `stream_options: { include_usage: true }` ко всем stream-запросам, поэтому `usage` приходит всегда.
## Парсинг
Парсите построчно с буфером: один chunk может прийти разорванным на несколько TCP-сегментов. Псевдокод:
```python
buffer = ""
for raw_bytes in response.iter_content():
buffer += raw_bytes.decode("utf-8")
while "\n\n" in buffer:
line, buffer = buffer.split("\n\n", 1)
if not line.startswith("data: "):
continue
payload = line[len("data: "):]
if payload == "[DONE]":
return
chunk = json.loads(payload)
# обработка delta
```
Не парсьте `[DONE]` как JSON — это литерал-маркер, не JSON.
## Дисконнект
Если клиент закрыл соединение до окончания стрима, Hubris всё равно дочитывает ответ от провайдера и списывает деньги — это защищает от «бесплатных токенов» через abort. Если вы намеренно прерываете запрос (например, по timeout-у), ожидайте полное списание стоимости.
## Что дальше
* [POST /v1/chat/completions](/docs/api/chat-completions) — параметры запроса.
* [Ошибки](/docs/concepts/errors) — что бывает при сбое стрима.
---
# GET /v1/usage (/docs/api/usage)
> Расход по API-ключу за выбранный период. Удобно для дашбордов и алертов.
import { CodeTabs } from '@/components/docs/code-tabs';
import { EndpointBadge } from '@/components/docs/endpoint-badge';
Возвращает итоговый расход (рубли, копейки, токены, число запросов) **по тому API-ключу, которым сделан запрос**. Расход других ключей того же аккаунта сюда не попадёт — это специально, чтобы один скомпрометированный ключ не показывал общий бюджет.
По умолчанию — последние 24 часа. Период задаётся либо шорткатом `?period=`, либо явными `?from=&to=` (ISO 8601, UTC). Опциональный `?granularity=hour|day` добавит массив `buckets` для построения графика.
## Эндпоинт
Заголовки:
| Header | Значение |
| --------------- | -------------------------------- |
| `Authorization` | `Bearer sk-gw-...` (обязательно) |
## Query-параметры
| Параметр | Тип | По умолчанию | Описание |
| ------------- | -------------------------------------------- | ------------ | ---------------------------------------------------------------------------------------------------------- |
| `period` | `"today" \| "24h" \| "7d" \| "30d" \| "mtd"` | `"24h"` | Шорткат периода. Взаимоисключающий с `from`/`to`. |
| `from` | `string` (ISO 8601, UTC) | — | Начало периода включительно. Требует `to`. |
| `to` | `string` (ISO 8601, UTC) | — | Конец периода включительно. Требует `from`. |
| `granularity` | `"hour" \| "day"` | — | Если задано — в ответе появится `buckets[]` с поминутной разбивкой. Без `granularity` ответ только totals. |
`period` и `from`/`to` нельзя передавать одновременно — будет `400 invalid_request`. Максимальный диапазон при явных датах — 365 дней.
### Шорткаты периода
| `period` | Что считается |
| -------- | ------------------------------------ |
| `today` | С 00:00 UTC текущих суток до сейчас. |
| `24h` | Последние 24 часа (по умолчанию). |
| `7d` | Последние 7 суток. |
| `30d` | Последние 30 суток. |
| `mtd` | С 1-го числа текущего месяца (UTC). |
## Минимальный пример
## Ответ
```json
{
"object": "usage",
"period": {
"from": "2026-05-13T09:29:31.192Z",
"to": "2026-05-14T09:29:31.192Z",
"shortcut": null
},
"scope": {
"key_id": "31fb0d0d-ac86-4f72-815b-bdefd5978747",
"key_prefix": "sk-gw-758f...d4e3"
},
"totals": {
"requests": 2,
"prompt_tokens": 300,
"completion_tokens": 130,
"total_tokens": 430,
"cost_rub": 54.76,
"cost_kopecks": "5476"
},
"granularity": null,
"buckets": null
}
```
### Поля ответа
| Поле | Тип | Описание |
| ------------------------------ | ------------------------- | ---------------------------------------------------------------------------------------------- |
| **`object`** | `"usage"` | Тип объекта. |
| **`period.from`** | `string` (ISO 8601 UTC) | Начало периода включительно. |
| **`period.to`** | `string` (ISO 8601 UTC) | Конец периода включительно. |
| **`period.shortcut`** | `string \| null` | Если использовался шорткат — его имя; иначе `null`. |
| **`scope.key_id`** | `string` (UUID) | ID ключа, по которому собирается расход. |
| **`scope.key_prefix`** | `string` | Видимый префикс ключа для логов. |
| **`totals.requests`** | `integer` | Кол-во запросов за период. |
| **`totals.prompt_tokens`** | `integer` | Сумма входных токенов. |
| **`totals.completion_tokens`** | `integer` | Сумма выходных токенов. |
| **`totals.total_tokens`** | `integer` | Сумма обоих. |
| **`totals.cost_rub`** | `number` | Итоговая стоимость в рублях. Эквивалент `cost_kopecks / 100`. |
| **`totals.cost_kopecks`** | `string` | Итоговая стоимость в копейках. Строка — чтобы не терять точность на больших суммах (`BigInt`). |
| `granularity` | `"hour" \| "day" \| null` | Если в запросе был задан — повторяется здесь. |
| `buckets[]` | `array \| null` | Если `granularity` задан — массив значений по интервалам. |
| `buckets[].bucket` | `string` (ISO 8601) | Начало интервала. |
| `buckets[].cost_rub` | `number` | Стоимость интервала. |
| `buckets[].cost_kopecks` | `string` | Стоимость в копейках. |
| `buckets[].requests` | `integer` | Кол-во запросов в интервале. |
## С разбивкой по дням
С `granularity` в ответе появится `buckets`:
```bash
curl -s -H "Authorization: Bearer $HUBRIS_API_KEY" \
"https://api.hubris.pw/v1/usage?period=7d&granularity=day"
```
```json
{
"granularity": "day",
"buckets": [
{ "bucket": "2026-05-07T00:00:00.000Z", "cost_rub": 0, "cost_kopecks": "0", "requests": 0 },
{ "bucket": "2026-05-08T00:00:00.000Z", "cost_rub": 0, "cost_kopecks": "0", "requests": 0 },
{ "bucket": "2026-05-14T00:00:00.000Z", "cost_rub": 54.76, "cost_kopecks": "5476", "requests": 2 }
]
}
```
## Произвольный диапазон
```bash
curl -s -H "Authorization: Bearer $HUBRIS_API_KEY" \
"https://api.hubris.pw/v1/usage?from=2026-04-01T00:00:00Z&to=2026-05-01T00:00:00Z"
```
## Точность
* `cost_kopecks` — **строка**, чтобы не терять точность на больших суммах (JavaScript `Number` не выдержит, если когда-нибудь биллим сильно дорогих агентов). Парсить через `BigInt`.
* `cost_rub` — то же значение, делённое на 100, для удобства отображения.
* Время в UTC. Если нужно «сутки по Москве» — пришлите явные `from`/`to`, конвертированные на стороне клиента.
## HTTP-коды
| Код | Когда |
| ----- | --------------------------------------------------------------------------- |
| `200` | Успешный ответ. |
| `400` | Конфликт `period` + `from`/`to`, диапазон > 365 дней, неверный формат даты. |
| `401` | Ключ отсутствует / отозван / невалиден. |
| `429` | Превышен дневной лимит на ключе. |
## Что дальше
* [Биллинг](/docs/concepts/billing) — как формируется итоговая стоимость и из чего складываются токены.
* [Цены](/docs/concepts/pricing) — формула расчёта.
* В UI: страница [/usage](/usage) в дашборде с фильтрами по модели, ключу и статусу + CSV-экспорт.
---
# Биллинг (/docs/concepts/billing)
> Баланс, минимальный остаток, пополнение через СБП, поведение при нехватке средств.
Hubris работает по предоплатной модели: вы пополняете баланс через СБП, и каждый успешный запрос списывает с него стоимость. Подписок и тарифов нет.
## Баланс
Текущий баланс отображается на странице [/billing](/billing) в режиме реального времени. Там же — история пополнений и история расходов.
Также баланс виден в шапке дашборда (рядом с меню профиля).
## Пополнение через СБП
1. На странице [/billing](/billing) нажмите «Пополнить».
2. Введите сумму (минимум 100 ₽, максимум 10 000 ₽ за одну операцию).
3. Сканируйте QR-код в банковском приложении или нажмите «Открыть в приложении» (если на телефоне).
4. Подтвердите перевод в банке.
5. Зачисление происходит автоматически в течение 10–30 секунд.
Если зачисление не пришло за 5 минут — напишите на [support@hubris.pw](mailto:support@hubris.pw) и приложите ID операции СБП (его видно в банковском приложении).
## Минимальный баланс
Hubris требует минимум **100 копеек (1 ₽)** на балансе для обработки запроса. При меньшем балансе любой запрос вернёт **402 Payment Required** ДО обращения к модели:
```json
{
"error": {
"message": "Insufficient balance",
"type": "invalid_request_error",
"code": "insufficient_balance"
}
}
```
Это не значит, что 1 ₽ хватит на весь день. Это значит, что Hubris не запустит запрос, если баланса заведомо мало. Реальная стоимость запроса может быть от 1 копейки до нескольких рублей в зависимости от модели и размера сообщений.
## Уход в минус
Стоимость запроса известна **только после** ответа модели — мы не можем угадать заранее, сколько токенов сгенерирует модель. Поэтому возможна ситуация: на балансе было 50 ₽, запрос стоил 60 ₽, баланс ушёл в **−10 ₽**.
Это допустимо, но только на одну операцию. Следующий запрос с отрицательным балансом вернёт `402 Insufficient balance` — пополнение разблокирует следующие запросы.
## История
В разделе [/billing](/billing) видно:
* Каждое пополнение (дата, сумма, источник СБП).
* Каждый расход — со ссылкой на запрос в `/usage`.
* Корректировки от поддержки (если вы запрашивали возврат).
## Возврат средств
Hubris работает по pay-as-you-go — фактически использованные токены не возвращаются. Однако если вас списали ошибочно (некорректный billing у провайдера, баг на нашей стороне) — напишите на [support@hubris.pw](mailto:support@hubris.pw) с request\_id из ответа или временем запроса. Мы вернём деньги.
Возврат неиспользованного остатка с баланса — по запросу через support, обычно в течение 3 рабочих дней через тот же СБП-перевод.
## Лимиты
В стандартном тарифе у пользовательских ключей **нет искусственных лимитов** запросов в секунду или в день. Расход ограничен только балансом.
Служебным ключам (CI, скрипты) можно задавать дневной лимит — это страховка от утечки ключа. Подробнее — в разделе [Rate limits](/docs/concepts/rate-limits).
## Что дальше
* [Цены](/docs/concepts/pricing) — формула расчёта стоимости запроса.
* [Аутентификация](/docs/authentication) — как создать и отозвать ключ.
* [/billing](/billing) — текущий баланс и история.
---
# Ошибки (/docs/concepts/errors)
> Формат ответа об ошибке, таблица всех кодов, что делать в каждом случае.
Все ошибки Hubris API возвращаются в OpenAI-совместимом формате. Это значит — стандартные SDK обрабатывают их без специальной адаптации.
## Формат
```json
{
"error": {
"message": "Описание ошибки на английском",
"type": "<категория ошибки>",
"code": "<машинно-читаемый код>"
}
}
```
* **`message`** — человекочитаемое сообщение (на английском, для совместимости с OpenAI-инструментами).
* **`type`** — категория. Возможные значения: `invalid_request_error`, `rate_limit_error`, `api_error`.
* **`code`** — машинный код для программной обработки. Список ниже.
## Таблица кодов
| HTTP | code | Что значит | Что делать |
| ---- | --------------------------- | ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ |
| 400 | `invalid_request` | Тело запроса не соответствует схеме (пропущенное поле, неверный тип, выход за допустимые границы) | Проверить JSON по [Быстрому старту](/docs/quickstart) |
| 401 | `invalid_api_key` | Ключ отсутствует, отозван, или невалиден | Проверить заголовок `Authorization`, создать новый ключ на [/keys](/keys) |
| 402 | `insufficient_balance` | На балансе меньше минимального остатка (100 копеек) | Пополнить через [/billing](/billing) |
| 404 | `model_not_found` | Модель с таким `id` не существует или неактивна | Проверить написание, посмотреть [/models](/models) |
| 429 | `daily_limit_exceeded` | Превышен дневной лимит на конкретном ключе (только для служебных) | Дождаться сброса лимита или использовать ключ без лимита |
| 502 | `upstream_error` | Провайдер модели вернул ошибку (5xx, контент-фильтр, internal error) | Повторить через несколько секунд; если повторяется — попробовать другую модель |
| 503 | `exchange_rate_unavailable` | Курс ЦБ РФ временно недоступен — каталог не отдаёт цены | Повторить через минуту |
| 504 | `upstream_timeout` | Провайдер модели не ответил за 120 секунд | Повторить; если повторяется — модель перегружена, попробовать другую |
## Примеры
### 401 (нет ключа)
Запрос: `curl https://api.hubris.pw/v1/models`
```json
{
"error": {
"message": "Missing API key",
"type": "invalid_request_error",
"code": "invalid_api_key"
}
}
```
### 402 (баланс ноль)
```json
{
"error": {
"message": "Insufficient balance",
"type": "invalid_request_error",
"code": "insufficient_balance"
}
}
```
### 404 (модели не существует)
Запрос с несуществующим идентификатором модели:
```json
{
"error": {
"message": "Model not found: fake-provider/nonexistent",
"type": "invalid_request_error",
"code": "model_not_found"
}
}
```
### 502 (апстрим лёг)
```json
{
"error": {
"message": "Upstream returned an error",
"type": "api_error",
"code": "upstream_error"
}
}
```
## Стратегия обработки
В вашем коде имеет смысл:
1. **На 401, 402, 404** — не повторять. Это ошибки конфигурации, retry не поможет.
2. **На 502, 503, 504** — повторить с экспоненциальным backoff (1с, 2с, 4с, до 3 попыток). Это транзиентные сбои.
3. **На 429** — посмотреть на ключ: если у него выставлен дневной лимит, дождаться сброса (окно скользящее, считается от каждого списания); если лимит не выставлен — проверьте [/keys](/keys).
4. **На 400** — почти всегда баг в коде. Логируйте полный запрос (без ключа) для разбора.
## Стриминг
При `stream: true` и ошибке ДО начала стрима возвращается стандартный JSON-ответ выше.
Если ошибка случается **во время** стрима (модель упала на середине) — стрим обрывается на серверной стороне. Клиент получает событие `[DONE]` с пустым `delta` и `finish_reason: "stop"`. Точное поведение зависит от провайдера. Hubris записывает в логи ваш конкретный сценарий, чтобы вы могли разобраться через [/usage](/usage).
## Что дальше
* [Быстрый старт](/docs/quickstart) — рабочий пример без ошибок.
* [Аутентификация](/docs/authentication) — про 401.
* [Биллинг](/docs/concepts/billing) — про 402.
---
# Бесплатные модели (/docs/concepts/free-models)
> 25+ моделей с нулевой ценой — Llama, Gemma, Liquid, BGE и другие. Лимит 100 запросов в сутки на пользователя.
В каталоге Hubris есть **25+ моделей с нулевой ценой**: open-source LLM (Llama, Gemma, Liquid), embedding-модели (BGE, MiniLM, GTE) и другие. Они отмечены значком «Бесплатно» в [каталоге моделей](/models).
Бесплатные модели — это:
* **0 ₽ за 1М токенов** — ничего не списывается с баланса.
* **Доступны без депозита** — даже с балансом 0 ₽ запрос проходит.
* **Лимит 100 запросов в сутки** на пользователя — скользящее окно 24 ч.
Идеально подходят для:
* Знакомства с API без оплаты.
* Тестов и прототипов в проектах с маленьким бюджетом.
* Embedding-задач (BGE, MiniLM, GTE — отличные модели для русского/английского текста).
## Как использовать
Просто передайте ID бесплатной модели — никаких особых заголовков или флагов:
```bash
curl -s https://api.hubris.pw/v1/chat/completions \
-H "Authorization: Bearer sk-gw-..." \
-H "Content-Type: application/json" \
-d '{
"model": "meta-llama/llama-3.2-3b-instruct:free",
"messages": [{"role": "user", "content": "Привет"}]
}'
```
Список бесплатных моделей в каталоге доступен через API:
```bash
curl -s 'https://api.hubris.pw/v1/models' \
-H "Authorization: Bearer sk-gw-..." \
| jq '.data[] | select(.pricing.input_rub_per_million == 0 and .pricing.output_rub_per_million == 0) | .id'
```
## Что считается «бесплатной» моделью
Любая модель из каталога, у которой **обе цены в `pricing` равны 0**:
```json
{
"id": "meta-llama/llama-3.2-3b-instruct:free",
"pricing": {
"input_rub_per_million": 0,
"output_rub_per_million": 0,
"currency": "RUB"
}
}
```
Большинство таких моделей имеют суффикс `:free`, но это не строгое правило — Hubris проверяет именно цены, а не имя. Если когда-нибудь модель станет платной, она автоматически перестанет считаться бесплатной.
## Лимит и его правила
**100 запросов на пользователя в сутки** (скользящее окно 24 ч). Считаются только запросы к бесплатным моделям — платные не учитываются. При превышении возвращается **`429 daily_limit_exceeded`**:
```json
{
"error": {
"message": "Free-tier daily limit reached: 100 requests per 24h. Upgrade by using a paid model or wait for the rolling window to reset.",
"type": "rate_limit_error",
"code": "daily_limit_exceeded"
}
}
```
Когда лимит сбросится — зависит от того, когда был сделан первый из 100 запросов: окно скользит. Через 24 часа после первого запроса один слот освобождается; и так далее.
Лимит **общий для всех бесплатных моделей** — нельзя «накопить» по 100 на каждой модели. Это сделано чтобы:
1. Защитить наш общий ключ к провайдеру от 429 от него самого.
2. Не дать ботам жечь бесплатный пул и блокировать его для реальных пользователей.
Лимит сейчас в Hubris не настраивается на стороне пользователя. Если вам нужен больше — переходите на платные модели.
**Связь с лимитом ключа.** Поле «Дневной лимит, ₽» на странице [API-ключи](/keys) считает только сумму `cost_kopecks` за 24 ч. Бесплатные модели стоят 0 ₽, поэтому в этот лимит не входят и не «съедают» его. Это два независимых счётчика.
## Лимиты на стороне провайдера
Помимо нашего лимита 100 запросов в сутки, **у самого провайдера модели есть свои лимиты на бесплатный пул**, которые мы не контролируем и не публикуем:
* лимиты RPM/RPD выставляет провайдер модели, не Hubris;
* конкретные значения провайдер не публикует — мы их тоже не знаем заранее;
* бесплатный пул общий для всех клиентов провайдера, поэтому в часы пиковой нагрузки он быстро переполняется;
* в этот момент Hubris транслирует от провайдера **HTTP 429** с сообщением вида:
```json
{
"error": {
"message": ":free is temporarily rate-limited upstream. Please retry shortly...",
"type": "invalid_request_error",
"code": "invalid_request"
}
}
```
Это не сбой Hubris и не превышение вашего собственного дневного лимита — провайдер просто временно отказывает всему бесплатному трафику.
**Что делать при 429 от провайдера:**
1. **Повторить запрос с экспоненциальной задержкой** (1 с → 2 с → 4 с → 8 с, с разумным потолком). Часто бесплатный пул освобождается за несколько секунд.
2. **Указать платный fallback** через [Model Fallback](/docs/features/model-fallbacks) — в массиве `models` поставить платную версию той же модели последним элементом:
```bash
curl -s https://api.hubris.pw/v1/chat/completions \
-H "Authorization: Bearer sk-gw-..." \
-H "Content-Type: application/json" \
-d '{
"model": "deepseek/deepseek-v4-flash:free",
"models": [
"deepseek/deepseek-v4-flash:free",
"deepseek/deepseek-v4-flash"
],
"messages": [{"role": "user", "content": "..."}]
}'
```
Hubris автоматически переключится на следующий элемент при 429 от бесплатного. Тарифицируется тот, кто реально ответил.
3. **Для боевой нагрузки** — использовать платную версию модели изначально. У платных нет общего бесплатного пула и нет таких всплесков 429.
## Чем бесплатные отличаются от платных
| Параметр | Бесплатные | Платные |
| ------------------ | ---------------------------------------------- | ---------------------------------------------- |
| Цена | 0 ₽ | По каталогу |
| Минимальный баланс | Не требуется | 1 ₽ |
| Дневной лимит | 100 запросов / 24 ч | Не применяется (или ваш custom-лимит на ключе) |
| Качество | Мелкие/средние LLM | Любые, включая флагманы |
| Латентность | Часто выше — провайдеры приоритезируют платных | Стандартная |
| SLA | Best-effort | Соответствует провайдеру |
**Внимание:** бесплатные модели на стороне провайдера часто ограничены гораздо жёстче платных. Может прийти **429 от провайдера**, даже если ваш собственный дневной лимит не исчерпан. Подробнее — в разделе [Лимиты на стороне провайдера](#лимиты-на-стороне-провайдера) выше.
## Сочетание с Model Fallback
Хороший паттерн — дешёвая основа, платная подстраховка:
```bash
curl -s https://api.hubris.pw/v1/chat/completions \
-H "Authorization: Bearer sk-gw-..." \
-H "Content-Type: application/json" \
-d '{
"model": "meta-llama/llama-3.2-3b-instruct:free",
"models": [
"meta-llama/llama-3.2-3b-instruct:free",
"anthropic/claude-haiku-4.5"
],
"messages": [{"role": "user", "content": "..."}]
}'
```
Если бесплатная Llama упадёт от провайдерского 429 — Hubris автоматически переключится на платный Haiku. Биллится тот, кто реально ответил — то есть в большинстве случаев это будет 0 ₽, и только в редких сбоях — копейки за Haiku.
## Что дальше
* [Модели](/docs/concepts/models) — обзор каталога.
* [Model Fallback](/docs/features/model-fallbacks) — про автоматическое переключение.
* [Цены](/docs/concepts/pricing) — формула расчёта стоимости платных моделей.
---
# Модели (/docs/concepts/models)
> Каталог моделей Hubris, формат GET /v1/models, как выбрать модель под задачу.
import { EndpointBadge } from '@/components/docs/endpoint-badge';
import { ModelLink } from '@/components/docs/model-link';
Hubris предоставляет доступ к моделям ведущих провайдеров через единый API. Все модели отвечают в OpenAI-совместимом формате — код не нужно адаптировать под каждого провайдера.
## Каталог
Полный список — на странице [/models](/models). Там же можно посмотреть актуальные цены в ₽, размер контекста и поддерживаемые модальности (текст, изображения, аудио).
Программно список доступен через API:
```bash
curl -H "Authorization: Bearer sk-gw-..." https://api.hubris.pw/v1/models
```
Ответ — стандартный OpenAI-список с расширениями Hubris:
```json
{
"object": "list",
"data": [
{
"id": "anthropic/claude-haiku-4.5",
"object": "model",
"created": 1714000000,
"owned_by": "anthropic",
"display_name": "Claude Haiku 4.5",
"description": "Быстрая и дешёвая модель Anthropic для большинства задач.",
"context_window": 200000,
"modalities": ["text", "image"],
"pricing": {
"input_rub_per_million": 30.45,
"output_rub_per_million": 152.25,
"currency": "RUB"
}
}
]
}
```
## Формат идентификатора
Все модели используют формат `provider/model`:
* — Anthropic, модель Claude Haiku 4.5.
* — OpenAI, модель GPT-4o mini.
* — Google, модель Gemini 2.5 Flash.
В каталоге доступны модели от Anthropic, OpenAI, Google и других ведущих провайдеров. Все обращения идут через единый эндпоинт `https://api.hubris.pw/v1/chat/completions` — без необходимости подключать SDK каждого провайдера.
## Как выбрать модель
Краткие ориентиры:
* **Для простых задач** (классификация, извлечение, короткие ответы) — самые дешёвые «mini» / «haiku» модели вроде или . Стоят копейки за запрос.
* **Для сложных рассуждений и кода** — флагманские модели (Opus, GPT-4o, Pro). Дороже, но качество выше.
* **Для большого контекста** (длинные документы, кодбазы) — модели с `context_window` от 200K. У ряда моделей Gemini есть варианты до 2M токенов.
* **Для мультимодальных задач** (анализ изображений) — модели с `"image"` в `modalities`.
В каталоге есть фильтры по этим параметрам. Если не уверены — начните с , она работает быстро и дёшево для большинства задач.
## Какие модели в каталоге
В `data` попадают только активные модели с явно заданной фиксированной ценой. Модели с переменной/динамической ценой и устаревшие — фильтруются автоматически.
Каталог обновляется примерно раз в сутки — новые модели появляются в течение дня после релиза провайдером.
## Что дальше
* [Цены](/docs/concepts/pricing) — как считаются деньги за запрос.
* [Быстрый старт](/docs/quickstart) — отправьте первый запрос к выбранной модели.
* [/models](/models) — открыть каталог.
---
# Цены (/docs/concepts/pricing)
> Как считается стоимость запроса в рублях. Таблицы цен — в каталоге моделей.
import { ModelLink } from '@/components/docs/model-link';
Hubris работает по модели **pay-as-you-go**: вы платите только за фактически использованные токены. Никаких подписок, минимальных платежей или скрытых комиссий. Цены — в ₽ за 1 миллион токенов.
## Что такое токен
Токен — это единица обработки текста для модели. Грубо: одно английское слово — это примерно 1.3 токена; русское слово — примерно 2 токена (кириллица токенизируется агрессивнее). Точное число зависит от языка и токенизатора модели. Один абзац обычно — 50–100 токенов.
При расчёте стоимости запроса различаются:
* **prompt\_tokens** (входные) — то, что вы отправили в `messages`.
* **completion\_tokens** (выходные) — то, что модель вернула в `choices[0].message.content`.
Цены за вход и выход разные — выход обычно в 3–5 раз дороже.
## Формула расчёта
```
стоимость_запроса = prompt_tokens × цена_входа + completion_tokens × цена_выхода
```
Где цены — в ₽ за токен (то есть `input_rub_per_million / 1_000_000`). Округление — в большую сторону до копейки, чтобы Hubris не накапливал долг на дробных копейках.
## Цены в каталоге
В каталоге моделей `/models` для каждой модели видно:
* **Вход** — ₽ за 1М входных токенов.
* **Выход** — ₽ за 1М выходных токенов.
Программно те же значения возвращает `GET /v1/models` в поле `pricing`:
```json
{
"id": "anthropic/claude-haiku-4.5",
"pricing": {
"input_rub_per_million": 30.45,
"output_rub_per_million": 152.25,
"currency": "RUB"
}
}
```
## Пример расчёта
Запрос к с 1000 prompt-токенов и 500 completion-токенов при цене 30.45 ₽ / 152.25 ₽ за 1М:
```
стоимость = 1000 × (30.45 / 1_000_000) + 500 × (152.25 / 1_000_000)
= 0.03045 + 0.0761
= 0.10655 ₽
≈ 11 копеек (округление вверх)
```
## Как узнать стоимость конкретного запроса
В ответе любого запроса к `/v1/chat/completions` есть поле `usage`:
```json
{
"usage": {
"prompt_tokens": 1000,
"completion_tokens": 500,
"total_tokens": 1500
}
}
```
В разделе [/usage](/usage) дашборда отображается полный лог запросов с фактической стоимостью каждого. Баланс в реальном времени — на странице [/billing](/billing).
## Стоимость стриминга
При `stream: true` цена считается так же — по `usage` из последнего chunk-а перед `[DONE]`. Hubris автоматически добавляет `stream_options: { include_usage: true }` к стрим-запросам, чтобы вы (и мы) знали окончательное число токенов.
Если соединение разорвалось до окончания стрима — мы всё равно дочитываем ответ от провайдера и списываем деньги (защита от «бесплатных токенов» через abort).
## Что входит в счёт
* Успешные запросы (`200 OK`).
* Запросы со стримингом до получения `usage` (включая прерванные клиентом).
## Что НЕ входит в счёт
* Запросы с ошибкой авторизации (`401`, `403`).
* Запросы с невалидным телом (`400`).
* Запросы к несуществующим моделям (`404`).
* Ошибки апстрима (`502`, `503`, `504`) — модель не отработала, денег не списываем.
* Запросы при недостаточном балансе (`402`) — отвергаются ДО запроса к модели.
## Что дальше
* [Биллинг](/docs/concepts/billing) — пополнение баланса, минимальный остаток.
* [Каталог моделей](/models) — посмотреть все цены.
* [/usage](/usage) — лог ваших запросов с расходами.
---
# Конфиденциальность (/docs/concepts/privacy)
> Что Hubris хранит и не хранит о ваших запросах. Передача данных провайдерам моделей.
Hubris работает по принципу **минимального хранения данных**. Содержимое ваших запросов (`messages`, `tools`, `input`) никогда не записывается на сервере и не используется для обучения моделей.
## Что мы НЕ храним
* Тексты ваших сообщений (`messages[*].content`).
* Системные промпты.
* Описание инструментов (`tools[*].function.parameters`).
* Содержимое ответов от моделей.
* Изображения, аудио, файлы (когда поддержим мультимодальные эндпоинты).
Эти данные проходят через наш сервер исключительно в момент проксирования запроса в провайдера и не сохраняются ни в логах, ни в БД.
## Что мы храним
В таблице `usage_logs` для каждого запроса:
* ID пользователя и API-ключа (для биллинга).
* ID модели (для отчётности).
* Число входных и выходных токенов (для биллинга).
* Стоимость в копейках.
* Латентность ответа.
* Статус (успех / ошибка / тайм-аут).
* Текст ошибки (если была), без содержимого запроса.
Этого достаточно для биллинга, аналитики и поддержки. Содержимое разговоров мы не видим — даже наша команда не имеет к нему доступа.
## Передача данных провайдерам моделей
Чтобы модель ответила на ваш запрос, его содержимое физически передаётся провайдеру модели (Anthropic, OpenAI, Google и т. д.). Передача происходит по защищённому соединению (TLS).
Каждый провайдер имеет собственную политику обработки данных:
* Большинство **не используют API-данные для обучения** по умолчанию.
* Многие хранят логи 30 дней для безопасности и контроля злоупотреблений.
* Часть провайдеров предоставляет режим Zero Data Retention за дополнительную плату или на enterprise-тарифе — на момент 2026-05 Hubris это не пробрасывает; следите за обновлениями.
Полный перечень провайдеров и юридическая обвязка — на странице [Политика конфиденциальности](/legal/privacy), раздел 8.
## Логи на стороне Hubris
Сервер Hubris пишет два класса логов:
1. **Метаданные запросов** (выше): хранятся в БД `usage_logs`, доступны вам в [/usage](/usage).
2. **Сервисные логи** (Pino → systemd journal): только метаданные — req\_id, userId, keyId, modelId, токены, статусы. Никакого содержимого. Хранятся 30 дней, доступ есть у нашей DevOps-команды для отладки инцидентов.
## Файлы и обработка изображений
Когда мы добавим поддержку генерации изображений и обработки аудио — содержимое (промпты для генерации картинок, голосовые файлы) будет передаваться провайдерам по тем же правилам. В нашей БД эти файлы оставаться не будут.
## GDPR / 152-ФЗ
Hubris соответствует требованиям 152-ФЗ для обработки персональных данных. Полная политика — на странице [/legal/privacy](/legal/privacy). Кратко:
* Юр. лицо: ИП Романкова А. В. (ИНН и реквизиты — там же).
* Персональные данные собираются только для биллинга (email, при необходимости — банковские реквизиты для возврата).
* Согласие на обработку даётся через акцепт оферты при регистрации.
* Удаление аккаунта по запросу на [support@hubris.pw](mailto:support@hubris.pw) — все данные стираются в течение 30 дней.
## Если у вас высокие требования
Если ваши данные регулируются (медицинские, финансовые, гос. тайна) — напишите на [support@hubris.pw](mailto:support@hubris.pw). Мы обсудим:
* Какие провайдеры подходят под ваше регулирование.
* Возможен ли self-hosted режим (отдельный inference на вашем железе).
* Дополнительные гарантии в DPA.
## Что дальше
* [Политика конфиденциальности](/legal/privacy) — полные юридические условия.
* [Условия использования](/legal/terms) — публичная оферта.
* [Аутентификация](/docs/authentication) — про безопасное хранение ключей.
---
# Rate limits (/docs/concepts/rate-limits)
> Что ограничивает запросы в Hubris — баланс, апстрим, дневные лимиты на служебных ключах.
Hubris **не применяет искусственных rate-limit-ов** на пользовательские API-ключи. Стандартный тариф даёт неограниченное число запросов в секунду — расход ограничен только балансом.
## Что реально ограничивает запросы
### 1. Баланс
Каждый запрос списывает с баланса фактическую стоимость. При недостаточном балансе (\< 100 копеек) запрос отклоняется с `402 insufficient_balance` ДО обращения к модели. Подробнее — на странице [Биллинг](/docs/concepts/billing).
### 2. Лимиты провайдера
Каждая модель имеет собственные ограничения от провайдера: requests-per-minute, tokens-per-minute, concurrent connections. При превышении провайдер возвращает 429, мы транслируем это как `502 upstream_error`.
Что делать: добавить retry с экспоненциальным backoff. Hubris агрегирует трафик многих клиентов через свой сервисный ключ — вероятность упереться в лимит провайдера ниже, чем при работе напрямую.
### 3. Дневной лимит на служебных ключах
Опционально — при создании ключа можно задать `daily_limit_kopecks`. Например, 5000 копеек = 50 ₽ в сутки. Превышение возвращает **429 daily\_limit\_exceeded**:
```json
{
"error": {
"message": "Daily spending limit exceeded for this API key",
"type": "rate_limit_error",
"code": "daily_limit_exceeded"
}
}
```
Это используется для:
* **CI-runner-ов**, чтобы утечка ключа не разорила баланс.
* **Внутренних скриптов** с ограниченным бюджетом.
* **Sandbox-аккаунтов** для разработчиков.
Окно — скользящее (rolling) 24 часа, а не календарный день. Старые расходы выпадают из счётчика по мере того как проходят сутки с момента списания, поэтому после превышения лимит постепенно «отпускает». Расходы за последние 24ч кешируются в Redis с TTL 60 секунд, то есть UI и middleware могут отставать от точного значения на минуту.
Задать или поменять лимит — на странице [API-ключи](/keys): поле «Дневной лимит, ₽» в форме создания и кнопка «изменить» рядом с каждым активным ключом. Очистите поле, чтобы снять лимит.
Генерация изображений (`modalities: ["image"]` / `["image","text"]`) учитывается в счётчике 24-часового расхода ключа на общих основаниях: одна сгенерированная картинка добавляется к расходу по итоговой стоимости (см. [Генерация изображений](/docs/features/image-generation)).
## Стратегия retry
Если получили 429 от Hubris (`daily_limit_exceeded`) или 502 (от провайдера):
```python
import time
def with_retry(fn, max_attempts=3):
delay = 1
for attempt in range(max_attempts):
try:
return fn()
except APIError as e:
if e.code in ("upstream_error", "upstream_timeout"):
if attempt < max_attempts - 1:
time.sleep(delay)
delay *= 2
continue
raise
```
Не делайте retry на:
* `invalid_api_key` (401) — ключ не починится сам.
* `insufficient_balance` (402) — пополните баланс сначала.
* `model_not_found` (404) — название не изменится.
* `invalid_request` (400) — баг в коде.
## Concurrent requests
Параллельные запросы на одном ключе — без ограничений. Если ваш сервис делает 100 запросов одновременно, они все пойдут к моделям параллельно. Узкое горлышко — провайдер модели, не Hubris.
## Что дальше
* [Ошибки](/docs/concepts/errors) — все коды и стратегия retry.
* [Биллинг](/docs/concepts/billing) — про баланс.
* [Аутентификация](/docs/authentication) — про создание ключей.
---
# Обработка ошибок (/docs/features/error-handling)
> Стратегия retry, backoff, комбинация с model fallbacks и что показывать конечному пользователю.
Гид про практическую работу с ошибками Hubris API — где имеет смысл ретраить, где не имеет, как комбинировать с [Model Fallbacks](/docs/features/model-fallbacks), и как не показать сырое сообщение API конечному пользователю.
Полная таблица кодов и форматов ответа — на странице [Ошибки](/docs/concepts/errors).
## Стратегия по кодам
| HTTP | code | Ретраить? | Почему |
| ---- | --------------------------- | ---------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| 400 | `invalid_request` | ❌ нет | Это баг в коде. Ретрай → та же ошибка. Логируйте и чините. |
| 401 | `invalid_api_key` | ❌ нет | Ключ невалиден. Ротация на лету не предусмотрена. |
| 402 | `insufficient_balance` | ❌ нет (но можно ждать) | Кончился баланс. Если есть авто-пополнение — после крупного топ-апа ретрай поможет. Иначе — алёрт. |
| 404 | `model_not_found` | ❌ нет | Модель архивирована или ID опечатан. Лучше fallback на актуальную ([model-selection](/docs/features/model-selection)). |
| 429 | `daily_limit_exceeded` | ❌ не имеет смысла | Лимит ключа исчерпан. До конца окна — отказ. Используйте другой ключ или ждите. |
| 502 | `upstream_error` | ✅ да | Апстрим вернул 5xx или контент-фильтр. Транзиентно. |
| 503 | `exchange_rate_unavailable` | ✅ да | Курс ЦБ временно не доступен — каталог не отдаёт цены. Обычно минуту. |
| 504 | `upstream_timeout` | ✅ да | Модель перегружена. Часто помогает другая модель из каталога. |
Главное правило: **ретраить транзиентные сбои (5xx) с backoff, остальное — нет**.
## Где ретрай встроен на стороне Hubris
Hubris уже автоматически переключается на запасную модель, если вы передали массив `models`:
```bash
curl -s https://api.hubris.pw/v1/chat/completions \
-d '{
"model": "anthropic/claude-haiku-4.5",
"models": [
"anthropic/claude-haiku-4.5",
"openai/gpt-4o-mini"
],
"messages": [{"role": "user", "content": "..."}]
}'
```
Если основная модель ответила 429 или 5xx — клиент видит успешный ответ от запасной. Подробнее: [Model Fallbacks](/docs/features/model-fallbacks).
**Это снимает большую часть транзиентных ошибок без вашего ретрая.** Перед тем как писать сложную ретрай-логику в клиенте — попробуйте `models: [...]`. На практике она закрывает 80% сценариев.
## Когда нужен клиентский ретрай поверх
Кейсы, где Hubris-fallback не спасает:
* **Нет резервной модели в массиве `models` или нужна та же самая модель.** Если основная модель уникальна (например, специфичный image-gen ID, который никто не дублирует) — клиентский ретрай.
* **503 `exchange_rate_unavailable`.** Это не ошибка модели — это про каталог. Fallback моделей не сработает. Ретрай через минуту.
* **Сетевые таймауты до Hubris.** До нас даже не дошёл запрос — fallback не запустится. Клиентский ретрай.
## Python: exponential backoff
```python
import time
import openai
from openai import OpenAI
client = OpenAI(
base_url="https://api.hubris.pw/v1",
api_key="sk-gw-...",
)
def call_with_retry(messages, max_attempts=3):
for attempt in range(max_attempts):
try:
return client.chat.completions.create(
model="anthropic/claude-haiku-4.5",
models=[
"anthropic/claude-haiku-4.5",
"openai/gpt-4o-mini",
],
messages=messages,
)
except openai.APIStatusError as e:
# Ретраим только транзиентные коды
if e.status_code in (502, 503, 504):
if attempt == max_attempts - 1:
raise
# Exponential backoff с jitter
wait = (2 ** attempt) + (0.5 * attempt)
time.sleep(wait)
continue
# 4xx — баг или конфигурация, ретрай не поможет
raise
resp = call_with_retry([{"role": "user", "content": "Hello"}])
```
Использование `models: [...]` параллельно с retry — это нормально: они работают на разных уровнях (модель vs сеть/каталог). Двойного списания не будет — биллинг по фактически ответившему запросу.
### Готовая библиотека
Если не хотите писать backoff руками — есть [`tenacity`](https://tenacity.readthedocs.io/):
```python
from tenacity import retry, stop_after_attempt, wait_exponential, retry_if_exception_type
import openai
@retry(
retry=retry_if_exception_type((openai.APITimeoutError, openai.APIConnectionError)),
stop=stop_after_attempt(3),
wait=wait_exponential(multiplier=1, min=1, max=10),
)
def call_llm(messages):
return client.chat.completions.create(
model="anthropic/claude-haiku-4.5",
messages=messages,
)
```
## TypeScript: фабрика с retry
```ts
import OpenAI from 'openai';
const client = new OpenAI({
baseURL: 'https://api.hubris.pw/v1',
apiKey: 'sk-gw-...',
});
async function callWithRetry(
messages: Array<{ role: string; content: string }>,
maxAttempts = 3,
) {
for (let attempt = 0; attempt < maxAttempts; attempt++) {
try {
return await client.chat.completions.create({
model: 'anthropic/claude-haiku-4.5',
models: [
'anthropic/claude-haiku-4.5',
'openai/gpt-4o-mini',
],
messages,
} as any);
} catch (e: any) {
const transient = [502, 503, 504].includes(e?.status);
const lastAttempt = attempt === maxAttempts - 1;
if (!transient || lastAttempt) throw e;
const wait = (2 ** attempt) * 1000 + Math.random() * 500;
await new Promise((r) => setTimeout(r, wait));
}
}
throw new Error('unreachable');
}
```
## Что показывать пользователю
Сырые сообщения API не подходят для production UI:
| Внутреннее | Внешнее (пользователю) |
| -------------------------- | --------------------------------------------------------------------------------------------------------------- |
| `401 invalid_api_key` | «Внутренняя ошибка авторизации» (никогда не показывайте «неверный ключ» — это намекает, что у вас есть рабочий) |
| `402 insufficient_balance` | «Сервис временно недоступен» — это **ваша** проблема, не пользователя |
| `404 model_not_found` | «Сервис обновляет каталог» — подставьте альтернативную модель |
| `429 daily_limit_exceeded` | «Лимит запросов на сегодня исчерпан, попробуйте завтра» |
| `502 / 503 / 504` | «Модель временно недоступна, повторим попытку» (если уже идёт retry) |
Никогда не показывайте `error.message` модели напрямую — он на английском и может содержать технические детали (имена провайдеров, ID моделей).
## Как НЕ надо
```python
# Плохо: ретрай 401 / 402 / 404
for i in range(5):
try:
return client.chat.completions.create(...)
except Exception:
time.sleep(2 ** i)
```
Слепой ретрай на любую ошибку:
* На `401` (неверный ключ) — крутит цикл с экспоненциальной задержкой, прежде чем сдаться. UI замораживается.
* На `402` (нет денег) — то же самое, плюс ваш сервис атакует наш API без шансов на успех.
* На `400` (баг в коде) — никогда не починится сама.
```python
# Плохо: показ сырого error.message
except openai.APIError as e:
return jsonify({"error": str(e)}), 500
```
Сырая ошибка попадёт в браузер пользователя. Безопаснее:
```python
except openai.APIError as e:
log.error("LLM error", extra={"status": getattr(e, "status_code", None)})
return jsonify({"error": "Сервис временно недоступен"}), 503
```
## Стриминг и ошибки
При `stream: true` ошибка ДО первого чанка возвращается как обычный JSON `{ error: {...} }` с соответствующим HTTP-кодом. После начала стрима — клиент видит обрыв соединения или преждевременный `finish_reason`. Подробности — на [странице стриминга](/docs/features/streaming) и в [концепции ошибок](/docs/concepts/errors#стриминг).
## Что важно знать
* **Hubris не списывает деньги за неуспешные запросы.** 4xx и 5xx — без биллинга. Streaming-обрыв после первого чанка — частичное списание по тому, что успело сгенерироваться.
* **Не ретраите structured-output ответы внутри одного контекста.** Если модель упала на структуре — лучше переключиться на другую модель (`models: [...]`), а не повторять с той же.
* **Лимиты по таймауту** — ваш HTTP-клиент должен иметь reasonable timeout (90–120 сек), иначе соединение зависнет на perpetually-slow модели.
## Что дальше
* [Ошибки](/docs/concepts/errors) — полная таблица кодов с примерами JSON-ответов.
* [Model Fallbacks](/docs/features/model-fallbacks) — встроенный fallback на запасную модель.
* [Стриминг ответов](/docs/features/streaming) — что происходит с ошибками во время стрима.
* [Управление ключами](/keys) — дневные лимиты на стороне сервера.
---
# Генерация изображений (/docs/features/image-generation)
> Генерация изображений через /v1/chat/completions. Передайте `modalities: ["image","text"]` и модель с поддержкой image-output.
Hubris умеет генерировать изображения через тот же эндпоинт `/v1/chat/completions`, что и для текста — нужно лишь передать `modalities` и выбрать модель с поддержкой image-output. В каталоге сейчас 29 image-моделей: Google Gemini \*-image, OpenAI GPT-5 Image, Black Forest Labs Flux 2 (pro/max/flex/klein), Recraft v3-v4.1 (11 вариантов с поддержкой текста на изображении, vector и utility-режимов), Sourceful Riverflow v2, xAI Grok Imagine, ByteDance Seedream 4.5. Полный актуальный список — фильтр «Output: image» в [каталоге](/models).
## Минимальный пример
```bash
curl -s https://api.hubris.pw/v1/chat/completions \
-H "Authorization: Bearer sk-gw-..." \
-H "Content-Type: application/json" \
-d '{
"model": "google/gemini-2.5-flash-image",
"modalities": ["image", "text"],
"messages": [{"role": "user", "content": "Закат над горами в стиле Гибли"}]
}'
```
Картинка возвращается как base64 data URL в `choices[0].message.images[0].image_url.url`:
```json
{
"id": "chatcmpl-...",
"model": "google/gemini-2.5-flash-image",
"choices": [
{
"message": {
"role": "assistant",
"content": "Готово!",
"images": [
{
"type": "image_url",
"image_url": { "url": "data:image/png;base64,iVBORw0KGgo..." }
}
]
}
}
],
"usage": { "prompt_tokens": 18, "completion_tokens": 0, "total_tokens": 18 }
}
```
## `modalities`
* `["image", "text"]` — модели с гибридным выходом (Google Gemini image, OpenAI GPT-5 Image): отдают и текст-комментарий, и картинку.
* `["image"]` — image-only модели (Flux 2, Recraft, Sourceful, SDXL-семейство): только картинка.
Если передадите `modalities` для не-image модели — вернётся ошибка от провайдера.
## OpenAI SDK
В официальных SDK OpenAI (Python `openai`, TypeScript `openai`) поле `modalities` не объявлено в Chat Completions. Передавайте через `extra_body` / приведением типов.
**Python:**
```python
from openai import OpenAI
client = OpenAI(
base_url="https://api.hubris.pw/v1",
api_key="sk-gw-...",
)
resp = client.chat.completions.create(
model="google/gemini-2.5-flash-image",
messages=[{"role": "user", "content": "Закат над горами"}],
extra_body={"modalities": ["image", "text"]},
)
# images приходит как extra поле — доступ через model_extra
images = resp.choices[0].message.model_extra.get("images", [])
for img in images:
data_url = img["image_url"]["url"]
# data_url начинается с "data:image/png;base64,..." — декодировать и сохранить
```
**TypeScript:**
```ts
import OpenAI from 'openai';
const client = new OpenAI({
baseURL: 'https://api.hubris.pw/v1',
apiKey: 'sk-gw-...',
});
const resp = await client.chat.completions.create({
model: 'google/gemini-2.5-flash-image',
messages: [{ role: 'user', content: 'Закат над горами' }],
modalities: ['image', 'text'],
} as any);
const images = (resp.choices[0].message as any).images ?? [];
for (const img of images) {
const dataUrl = img.image_url.url;
// ...
}
```
## `image_config`
Базовые поля (поддерживаются большинством моделей):
* `aspect_ratio` — `"1:1"` (default), `"16:9"`, `"9:16"`, `"4:3"`, `"3:4"`, `"21:9"` и др.
* `image_size` — `"1K"` (default), `"2K"`, `"4K"`, `"0.5K"`.
### Recraft (v3 / v4 / v4.1)
* `style` — стилевая пресета. Полный список — на карточке модели в [каталоге](/models).
* `text_layout` — массив объектов `{ text, bbox: [[x,y], ...] }` для размещения текста на картинке. Только Recraft V3.
* `rgb_colors` — массив `[r, g, b]` для палитры (Recraft v3+).
* `background_rgb_color` — `[r, g, b]` для фона.
* `strength` — для image-to-image (0..1, default 0.2). Меньше — ближе к оригиналу, больше — креативнее.
### Sourceful Riverflow
* `font_inputs` — массив `{ font_url, text }` для кастомных шрифтов. Максимум 2.
* `super_resolution_references` — массив URL-референсов для image-to-image enhance. Максимум 4. Только при image-to-image.
### Google Gemini \*-image
* Расширенный набор `aspect_ratio` для `gemini-3.1-flash-image-preview`: `"1:4"`, `"4:1"`, `"1:8"`, `"8:1"` (для вертикальных и панорамных layouts).
* `image_size: "0.5K"` — оптимизированный по скорости вариант (только эта модель).
### Black Forest Labs Flux 2
* Стандартный набор `aspect_ratio` + `image_size`. Без model-specific параметров.
```bash
curl -s https://api.hubris.pw/v1/chat/completions \
-H "Authorization: Bearer sk-gw-..." \
-d '{
"model": "google/gemini-3-pro-image-preview",
"modalities": ["image", "text"],
"messages": [{"role": "user", "content": "Морской пейзаж"}],
"image_config": { "aspect_ratio": "16:9", "image_size": "2K" }
}'
```
## Какую модель когда выбирать
* **Текст на изображении** (баннер, постер с надписями) → Recraft V3 (`text_layout`).
* **Брендовые шрифты** → Sourceful Riverflow (`font_inputs`).
* **Photorealistic / frontier quality** → Flux 2 Pro / Max.
* **Быстро + дёшево** → Gemini 2.5 Flash Image, Flux 2 Klein 4B.
* **Гибрид «текст + картинка»** в одном ответе → Gemini \*-image, GPT-5 Image.
## Image-to-image и редактирование
Некоторые модели (Recraft, Sourceful, Flux 2 Pro) поддерживают входное изображение через content-blocks в `messages`. Формат пока разный между провайдерами, единого OpenAI-shape ещё нет — смотрите карточку конкретной модели. Hubris прозрачно пропускает содержимое `messages` к провайдеру.
## Стриминг
`stream: true` работает. Картинка приходит в `delta.images[]` одним или несколькими чанками. Финальный чанк содержит `usage` (как в обычном стриме).
```bash
curl -N https://api.hubris.pw/v1/chat/completions \
-H "Authorization: Bearer sk-gw-..." \
-d '{
"model": "google/gemini-2.5-flash-image",
"modalities": ["image", "text"],
"messages": [{"role": "user", "content": "Космический корабль"}],
"stream": true
}'
```
## Биллинг
Image generation тарифицируется **за изображение** (не за токены). В каталоге у каждой image-модели видна цена в рублях за одну картинку — это итоговая цифра, которую Hubris спишет с баланса. Если модель умеет возвращать несколько изображений за запрос — спишется n × цена.
Дополнительные параметры некоторых моделей могут увеличивать стоимость запроса — итоговая сумма списания в рублях видна в разделе [«Расходы»](/usage).
Списание происходит после успешного ответа. Транзакция атомарна — баланс, usage\_log и движение по балансу пишутся одной БД-транзакцией.
## Лимиты
* **Баланс** — стандартный. Если баланс ниже минимального порога (100 копеек по умолчанию), запрос блокируется с `402 insufficient_balance` (см. [Лимиты и баланс](/docs/concepts/rate-limits)).
* **Дневной лимит ключа** — действует. Image-запросы добавляются в счётчик 24-часового расхода ключа на общих основаниях. Превышение — `429 daily_limit_exceeded`.
## Чего пока нет в MVP
* Отдельный image-generations endpoint OpenAI-shape — пока используем `/v1/chat/completions` с `modalities`.
* Edits (изменение существующего изображения по маске).
* Variations (создание вариаций существующего изображения).
* Загрузка картинки из URL/файла как input (image-to-image поддерживается некоторыми моделями через content blocks в `messages`, но единого формата ещё нет).
Если что-то из этого критично — напишите в [support@hubris.pw](mailto:support@hubris.pw), расскажите use case.
---
# Model Fallbacks (/docs/features/model-fallbacks)
> Запасные модели на случай 429/5xx/контент-фильтра. Передайте массив `models` — Hubris сам переключится при сбое.
Если основная модель временно недоступна — упёрлась в rate limit провайдера, упала с 5xx или ответ зарезали контент-фильтром — Hubris автоматически попробует следующие модели из массива `models`. Биллинг по фактически использованной модели (она возвращается в поле `model` ответа).
Поддерживается только в [`POST /v1/chat/completions`](/docs/api/chat-completions). На `/v1/responses` пока нет.
## Как работает
Передайте массив `models` рядом с обычным `model`. Первый элемент — основной; остальные пробуются по порядку при ошибке:
```bash
curl -s https://api.hubris.pw/v1/chat/completions \
-H "Authorization: Bearer sk-gw-..." \
-H "Content-Type: application/json" \
-d '{
"model": "anthropic/claude-opus-4.7",
"models": [
"anthropic/claude-opus-4.7",
"anthropic/claude-sonnet-4.6",
"openai/gpt-4o-mini"
],
"messages": [{"role": "user", "content": "Привет"}]
}'
```
Если Opus 4.7 вернёт 429 от провайдера — сразу пойдёт Sonnet 4.6; если и тот недоступен — GPT-4o mini. Клиент получит обычный успешный ответ, как если бы fallback не было.
## Когда срабатывает fallback
* **429** — rate limit провайдера.
* **5xx** — апстрим упал.
* **Контент-фильтр** — провайдер заблокировал ответ модерацией (вы получите fallback-результат, а не блок).
* **Network timeout** между Hubris и провайдером.
## Биллинг
Списывается стоимость **только той модели, которая реально ответила**. Поле `model` в JSON-ответе — это победитель (не первый из массива). На странице [/usage](/usage) запрос будет залогирован под этой моделью.
Если в `models` несколько моделей разной цены и срабатывает fallback на самую дорогую — да, спишется по её тарифу. Поэтому имеет смысл располагать модели по возрастанию цены или ставить в конец «дешёвый, но всегда живой» вариант:
```json
{
"model": "anthropic/claude-opus-4.7",
"models": [
"anthropic/claude-opus-4.7",
"anthropic/claude-sonnet-4.6",
"anthropic/claude-haiku-4.5"
],
"messages": [{"role": "user", "content": "..."}]
}
```
## Стриминг
`stream: true` работает с fallback так же. Если основная модель упала ДО первого SSE-чанка, Hubris молча переключится на следующую, и клиент получит чанки уже от неё. Поле `model` в каждом чанке — это уже final-модель. После начала успешного стрима fallback больше не сработает (это технически невозможно: байты пошли клиенту).
**Edge-case:** если клиент дисконнектнется до того, как пришёл первый чанк с полем `model`, биллинг спишется по тарифу основной модели — мы просто не успеем узнать, какая ответила. Это безопасно (over-bill вместо undelivered), но может слегка завысить стоимость в редких сетевых сбоях.
## Ограничения
* **Максимум 10 моделей** в массиве (включая основную).
* **`/v1/responses`** — не поддерживается (используйте `/v1/chat/completions`).
* **`/v1/embeddings`** — не поддерживается (embedding-модели обычно стабильны и не требуют fallback).
* **Структурные ошибки запроса** (400 invalid\_request) не триггерят fallback — это значит ваш запрос неверен, и пробовать другую модель бессмысленно.
## Что дальше
* [POST /v1/chat/completions](/docs/api/chat-completions) — полный список параметров.
* [Ошибки](/docs/concepts/errors) — какие коды и в каких случаях возвращаются.
---
# Выбор модели программно (/docs/features/model-selection)
> Как фильтровать каталог через GET /v1/models, подбирать модель под задачу и обрабатывать переименования.
В Hubris доступны десятки моделей с разными возможностями и ценами. Чтобы не хардкодить ID, а подбирать модель программно — есть `GET /v1/models`. Этот гайд про то, как фильтровать каталог под конкретные задачи и обрабатывать случаи, когда «нужной» модели не оказалось.
## Что возвращает `/v1/models`
```bash
curl -s https://api.hubris.pw/v1/models \
-H "Authorization: Bearer sk-gw-..."
```
Ответ — OpenAI-совместимый список:
```json
{
"object": "list",
"data": [
{
"id": "anthropic/claude-haiku-4.5",
"object": "model",
"created": 1714000000,
"owned_by": "anthropic",
"display_name": "Claude Haiku 4.5",
"description": "Быстрая и недорогая модель Anthropic...",
"context_window": 200000,
"modalities": ["text"],
"input_modalities": ["text", "image"],
"output_modalities": ["text"],
"pricing": {
"input_rub_per_million": 88,
"output_rub_per_million": 440,
"currency": "RUB"
}
}
// ...
]
}
```
Поля каждой модели:
| Поле | Что |
| -------------------------------- | ---------------------------------------------------------------------- |
| `id` | идентификатор для использования в `model` запроса |
| `owned_by` | провайдер модели — `anthropic`, `openai`, `google`, `deepseek`, и т.д. |
| `display_name` | человекочитаемое имя (для UI каталога) |
| `description` | короткое описание возможностей |
| `context_window` | размер контекста в токенах |
| `input_modalities` | что принимает на вход: `text`, `image` |
| `output_modalities` | что выдаёт: `text`, `image` |
| `pricing.input_rub_per_million` | цена в рублях за 1М prompt-токенов |
| `pricing.output_rub_per_million` | цена в рублях за 1М completion-токенов |
В ответ попадают только активные модели — снятые с публикации не приходят.
## Сценарии фильтрации
### «Хочу самую дешёвую модель с поддержкой изображений на вход»
```python
import httpx
resp = httpx.get(
"https://api.hubris.pw/v1/models",
headers={"Authorization": "Bearer sk-gw-..."},
).json()
vision_models = [
m for m in resp["data"]
if "image" in m["input_modalities"]
]
cheapest = min(
vision_models,
key=lambda m: m["pricing"]["input_rub_per_million"],
)
print(cheapest["id"], cheapest["pricing"])
```
### «Нужен большой контекст — от 500k токенов»
```python
big_context = [m for m in resp["data"] if m["context_window"] >= 500_000]
```
### «Только модели определённого провайдера»
```python
anthropic_only = [m for m in resp["data"] if m["owned_by"] == "anthropic"]
```
### «Сортирую по цене prompt-токенов»
```python
by_input_price = sorted(
resp["data"],
key=lambda m: m["pricing"]["input_rub_per_million"],
)
```
## TypeScript
```ts
type Model = {
id: string;
owned_by: string;
context_window: number;
input_modalities: string[];
output_modalities: string[];
pricing: {
input_rub_per_million: number;
output_rub_per_million: number;
currency: 'RUB';
};
};
const resp = await fetch('https://api.hubris.pw/v1/models', {
headers: { Authorization: 'Bearer sk-gw-...' },
}).then((r) => r.json() as Promise<{ data: Model[] }>);
const visionModels = resp.data.filter((m) =>
m.input_modalities.includes('image'),
);
```
## Стабильность ID
ID модели — стабильный идентификатор. Мы не меняем его молча. При больших изменениях:
* **Новая мажорная версия** — публикуется как **новый ID**. Например, `anthropic/claude-haiku-4.5` отдельная сущность от `anthropic/claude-haiku-5.0`. Старая модель может ещё работать или быть архивирована (см. ниже).
* **Архивация** — модель снимается с активного каталога. Запросы с её ID возвращают `404 model_not_found`. Архивированные модели **не возвращаются** в `GET /v1/models`.
* **Мелкие обновления провайдера** (контекст ↑, скорость ↑, цена ↓) — id остаётся прежним, поля карточки обновляются.
## Что делать с `404 model_not_found`
Если в коде захардкожен ID и модель уехала из каталога:
1. Поймайте `404` с `error.code: "model_not_found"`.
2. Либо используйте [model fallbacks](/docs/features/model-fallbacks): передайте `models: ["primary-id", "backup-id"]` — Hubris сам переключится.
3. Либо подберите замену через `GET /v1/models` и обновите ID у себя.
```python
configured_model_id = "anthropic/claude-haiku-4.5" # ID, который вы используете
try:
resp = client.chat.completions.create(model=configured_model_id, messages=[...])
except openai.NotFoundError:
# модель архивирована — fallback на актуальный каталог
models = httpx.get("https://api.hubris.pw/v1/models", headers={...}).json()
new_model = pick_similar(models["data"], owned_by="anthropic")
resp = client.chat.completions.create(model=new_model["id"], messages=[...])
```
Самое надёжное решение в продакшене — `models: [...]` параметр запроса. Тогда выбор fallback'а делается атомарно внутри запроса, без отдельного round-trip к каталогу.
## Версионированные slug'и
Некоторые провайдеры в ответе возвращают модель с дат-версионированным slug'ом — например, запрос ушёл на `anthropic/claude-haiku-4.5`, а в `response.model` приходит `anthropic/claude-haiku-4.5-20251101`. Это нормально: мы биллим по канонической модели (`anthropic/claude-haiku-4.5`), а слаг просто говорит, какая именно дата-версия ответила.
В Hubris ответ на запрос всегда содержит реально использованный ID в поле `model` — удобно для логов и аналитики, чтобы видеть, какая именно версия модели работала на ваших запросах.
## Что важно знать
* **Не кешируйте каталог дольше нескольких часов.** Цены пересчитываются при обновлении курса ЦБ. Раз в час — нормальная частота обновления.
* **Caps по провайдеру меняются.** Иногда у Anthropic / OpenAI заканчиваются мощности — модель временно отвечает 502/503. На уровне каталога модель остаётся, но провайдер недоступен. [Model fallbacks](/docs/features/model-fallbacks) спасают.
* **Цены `0` — это [бесплатные модели](/docs/concepts/free-models).** У них свои лимиты на стороне провайдера, не публикуемые.
## Что дальше
* [GET /v1/models](/docs/api/models) — полная схема ответа.
* [Model fallbacks](/docs/features/model-fallbacks) — `models: [...]` для авто-замены.
* [Каталог моделей](/models) — то же самое в UI.
* [Ошибки](/docs/concepts/errors) — обработка `404 model_not_found`.
---
# Privacy Mode (/docs/features/privacy)
> Автоматическое маскирование персональных данных перед отправкой к провайдеру.
## Зачем это нужно
Когда вы отправляете запрос к зарубежной модели, текст уходит за пределы РФ. По 152-ФЗ это требует обоснования трансграничной передачи персональных данных или согласия субъекта. **Privacy Mode** автоматически удаляет ПД из запроса, а в ответе восстанавливает их обратно — модель работает с обезличенным текстом, вы получаете осмысленный ответ.
Фича **бесплатна** для всех моделей каталога.
## Что маскируется
| Тип | Примеры |
| ---------------- | ------------------------------------------------ |
| ФИО | Иван Петрович Сидоров |
| Email | [user@example.com](mailto:user@example.com) |
| Телефоны | +7 903 158-22-00, 8(495)555-12-34 |
| Банковские карты | 4276 1600 1234 5678 |
| IBAN / Счета | 40817810099910004312 |
| IP-адреса | 192.168.1.1, 2001:db8::1 |
| Адреса | Москва, Тверская 13 |
| Даты / Время | 12.04.1985, 2024-08-15T10:30 |
| URL | [https://example.com/me](https://example.com/me) |
| Национальность | русский, татарка |
| Паспорта РФ | 4506 № 123456 |
| СНИЛС | 123-456-789 01 |
| ИНН | 7707083893, 770708389312 |
| ОГРН | 1027700132195 |
| Мед. лицензии | LO-77-01-009999 |
## Как настроить
Откройте раздел **«Безопасность»** в личном кабинете (`/security`):
1. Включите тумблер **«PII-маскирование»**.
2. В блоке **«Скрываемые сущности»** выберите типы, которые хотите маскировать.
3. Настройте три опции:
* **Восстанавливать в ответе** — модель возвращает ответ с подставленными оригиналами вместо placeholders. По умолчанию включено.
* **Маскировать system prompt** — применять маскирование к сообщениям с `role: 'system'`. По умолчанию выключено.
* **Блокировать при ошибке** — если сервис маскирования временно недоступен или восстановление данных в ответе не удалось — вернуть ошибку 503/502 вместо тихого пропуска без маски. По умолчанию выключено.
4. Нажмите **«Сохранить политику»**.
## Переопределение для одного ключа
На странице **«Ключи»** (`/keys`) кликните на чип Privacy у нужного ключа и выберите один из четырёх режимов:
* **По политике организации** — следует основной политике (значение по умолчанию).
* **Принудительно выключить** — даже если организация включила.
* **Принудительно включить** — даже если организация выключила.
* **Обязательно** — включено + блокировать при ошибке.
## Переопределение в одном запросе
Передайте заголовок `X-Hubris-Privacy-Mask` или поле `privacy_mask` в теле запроса:
```bash
curl https://api.hubris.pw/v1/chat/completions \
-H "Authorization: Bearer sk-gw-..." \
-H "X-Hubris-Privacy-Mask: on" \
-d '{"model": "openai/gpt-4o", "messages": [{"role":"user","content":"Иван +79031582200"}]}'
```
Возможные значения: `on`, `off`, `required`. Приоритет (сильнее → слабее): header > body > per-key > политика организации.
## Пример: было → стало
**Запрос пользователя:**
> «Иван Петрович, тел +79031582200, запиши на 14:00»
**Уходит к провайдеру:**
> «\[PERSON\_1], тел \[PHONE\_1], запиши на \[DATE\_TIME\_1]»
**Ответ провайдера:**
> «Записал \[PERSON\_1] на \[DATE\_TIME\_1], позвоню за час по \[PHONE\_1]»
**Вы получаете:**
> «Записал Ивана Петровича на 14:00, позвоню за час по +79031582200»
## Стриминг
В режиме `stream: true` Hubris разбирает SSE-чанки на лету. Когда в потоке появляется placeholder вида `[TYPE_N]`, его текст буферизуется (не более 50 символов или 200 мс) и при появлении закрывающей скобки заменяется на оригинал.
Если placeholder не нашёлся в карте подстановок — буфер выдаётся клиенту как есть, а счётчик `X-Hubris-Restore-Mismatch` увеличивается на 1.
## Заголовки ответа
| Заголовок | Значение |
| --------------------------------- | ----------------------------------------------------- |
| `X-Hubris-Privacy-Mode` | `on`, `required`, или `failed` |
| `X-Hubris-Masked-Count` | сколько сущностей скрыто |
| `X-Hubris-Masked-Types` | пример: `person:1,phone:1,email:2` |
| `X-Hubris-Mask-Latency-Ms` | задержка анализа в миллисекундах |
| `X-Hubris-Privacy-Policy-Version` | версия применённой политики |
| `X-Hubris-Restore-Mismatch` | сколько placeholders в ответе не удалось восстановить |
| `X-Hubris-Restore-Skipped` | `1`, если в политике выключено восстановление |
## Что НЕ маскируется
* Изображения в vision-запросах (OCR появится в будущем).
* Содержимое аудио-запросов (когда появится audio-транскрипция).
* Сообщения с `role: 'system'`, если в политике отключено «Маскировать system prompt».
* Произвольная обфускация (например, `п@@@ел Иван`, «4-5-0-6 123456») — это сделано намеренно, чтобы не ломать релевантные пользовательские маркеры.
* Очень нестандартные ФИО без контекстных слов — есть редкие промахи NER.
## Защитные ограничения
* **Размер запроса**: при `privacy_mask ≠ off` суммарный prompt не может превышать 30 000 токенов — иначе HTTP 413. Это защита от перегрузки сервиса маскирования; для длинных юридических документов значение может быть увеличено в будущих версиях.
* **Лимит запросов**: 100 privacy-запросов в минуту на один API-ключ. Превышение — HTTP 429. Лимит мягкий, рассчитан на обычное использование.
## Лучше всего работает на коротких сообщениях
Маскирование рассчитано на одиночные запросы или короткие диалоги (1–3 хода). На длинных multi-turn-сессиях возможен такой эффект:
* В каждом запросе анализатор маскирует **всю историю** (включая прошлые ответы ассистента), присваивая токены вида `[PERSON_1]`, `[LOCATION_5]` и т. д.
* К пятому-десятому ходу контекст модели состоит на 30–70% из таких токенов.
* Модель учит этот паттерн в контексте и начинает **сама генерировать новые токены** (`[LOCATION_29]`, `[PERSON_47]`) для придуманных ею сущностей, которых не было в исходном запросе.
* В нашем `anonymizer map` этих токенов нет — мы не можем подставить «оригинал», потому что оригинала и не существует. Такие токены остаются в ответе как есть.
Признаки утечки токенов:
* Видимые в ответе строки вида `[ТИП_число]` (например, `[LOCATION_29]`).
* В `usage_logs.mask_metadata.restore_mismatch_count` будет ненулевое значение.
* В ответе есть response-заголовок `X-Hubris-Restore-Mismatch: N`.
Что делать:
* Для PII-чувствительных задач используйте **одиночные запросы** без длинной истории.
* Если важна жёсткая гарантия, включите **«Блокировать при ошибке»** в политике. Тогда при `restore_mismatch_count > 0` запрос вернёт HTTP 502 `privacy_mask_restore_failed`, и можно повторить запрос без истории.
* Альтернатива — детектировать утечку клиентом: regex `/\[[A-Z_]+_\d+\]/` по ответу, и при срабатывании очищать историю / начинать новую сессию.
## Технические детали
Под капотом — **Microsoft Presidio** (open-source PII detection engine от Microsoft) с моделью **GLiNER multi-PII** (мультиязычная NER, ONNX-оптимизирована), spaCy `ru_core_news_sm` для русской токенизации и собственные распознаватели СНИЛС, ИНН, паспортов и ОГРН с проверкой контрольной суммы.
Сервис маскирования живёт на том же сервере, что и API, и общается через localhost — данные не покидают периметр Hubris для целей анализа.
## Условия использования
Фича бесплатна. При значительном изменении нагрузки мы можем уточнить лимиты — с уведомлением за 30 дней.
---
# Кеширование промптов (Anthropic) (/docs/features/prompt-caching)
> Кеширование длинных промптов для удешевления повторных вызовов с тем же контекстом.
Anthropic-модели (Claude) поддерживают кеширование частей промпта на стороне провайдера. Это даёт значительную экономию, когда вы много раз отправляете один и тот же длинный системный промпт или контекст с разными user-сообщениями.
## Когда это работает
* **Длинный системный промпт** (≥1024 токена) — описание роли ассистента, гайдлайны, документация.
* **Большой контекст в начале** — RAG-сниппеты, долгая история диалога.
* **Повторные вызовы в течение 5 минут** — Anthropic кешит на \~5-минутный TTL.
Если у вас короткий промпт или вы дёргаете API раз в час — кеширование не сэкономит ничего.
## Как включить
Передайте `cache_control` в content-блоке сообщения:
```bash
curl -s https://api.hubris.pw/v1/chat/completions \
-H "Authorization: Bearer sk-gw-..." \
-H "Content-Type: application/json" \
-d '{
"model": "anthropic/claude-haiku-4.5",
"messages": [
{
"role": "system",
"content": [
{
"type": "text",
"text": "Очень длинная инструкция на 5000 токенов...",
"cache_control": {"type": "ephemeral"}
}
]
},
{
"role": "user",
"content": "Ваш вопрос"
}
]
}'
```
Маркер `cache_control: { type: "ephemeral" }` ставится на content-блок, который вы хотите закешировать. Часто это весь системный промпт или большой документ в начале первого user-сообщения.
## Биллинг
В ответе `usage` появятся дополнительные поля от Anthropic:
```json
{
"usage": {
"prompt_tokens": 5050,
"completion_tokens": 100,
"prompt_tokens_details": {
"cached_tokens": 5000
}
}
}
```
`cached_tokens` — то, что было прочитано из кеша. У Anthropic стоимость cache-read обычно в 10× дешевле обычных prompt-токенов; cache-write — в 1.25× дороже.
**Важная оговорка по биллингу Hubris.** Сейчас (на 2026-05) текущий биллинг считает все prompt-токены по полной ставке каталога — без отдельной формулы для cache-read. То есть даже если 90% prompt-токенов прочитаны из кеша, скидки за это не будет. Когда биллинг переделают на формулу с учётом `cached_tokens`, цены в каталоге пересмотрят. Следите за [Changelog](/docs/changelog).
Если вам важна экономия от кеширования и текущий биллинг не подходит — пишите в [support@hubris.pw](mailto:support@hubris.pw), обсудим.
## Что закешировать
Эффективные кандидаты:
* **System prompt с гайдлайнами и примерами** (5–20k токенов) — основная экономия.
* **RAG-контекст** — если шлёте те же документы много раз в течение 5 минут.
* **Длинный one-shot пример** — для классификаторов или агентов.
Не имеет смысла кешировать:
* Каждое user-сообщение (они уникальны).
* Маленькие промпты (\< 1024 токена).
* Запросы реже раза в 5 минут (cache TTL истечёт).
## Что дальше
* [POST /v1/chat/completions](/docs/api/chat-completions) — полная схема.
* [Каталог моделей](/models) — какие Anthropic-модели доступны.
* [Цены](/docs/concepts/pricing) — про токенный биллинг.
---
# Reasoning токены (/docs/features/reasoning)
> Контроль над reasoning-моделями (o1, R1, Claude thinking) через параметр reasoning_effort.
Серия reasoning-моделей (OpenAI o1, DeepSeek R1, Claude с включённым thinking) перед основным ответом тратит токены на «внутреннее размышление». Эти reasoning-токены не показываются в `choices[0].message.content`, но учитываются в счёте.
## Когда использовать
Reasoning-модели лучше справляются с задачами, требующими многошагового рассуждения:
* Математика, логика, доказательства.
* Анализ кода и поиск багов.
* Сложные стратегические решения с несколькими переменными.
* Юридические и научные тексты с цепочкой выводов.
Для коротких ответов (классификация, переформулирование, простые вопросы) reasoning-модели избыточны: тратят больше токенов и дают тот же результат, что и обычные.
## Управление
Передайте параметр `reasoning_effort` в запросе:
```bash
curl -s https://api.hubris.pw/v1/chat/completions \
-H "Authorization: Bearer sk-gw-..." \
-H "Content-Type: application/json" \
-d '{
"model": "openai/gpt-4o-mini",
"messages": [{"role": "user", "content": "Если есть число, простое и сумма цифр которого тоже простое — какое наименьшее?"}],
"reasoning_effort": "high"
}'
```
Допустимые значения:
* `"low"` — минимум reasoning, быстрее и дешевле.
* `"medium"` — баланс (default для большинства reasoning-моделей).
* `"high"` — максимум, для сложных задач.
Не все модели поддерживают `reasoning_effort` — если модель его не понимает, апстрим вернёт ошибку или просто проигнорирует.
## Биллинг
Reasoning-токены входят в `completion_tokens` и тарифицируются по обычной цене выходных токенов модели:
```json
{
"usage": {
"prompt_tokens": 50,
"completion_tokens": 1200,
"total_tokens": 1250,
"completion_tokens_details": {
"reasoning_tokens": 1100
}
}
}
```
В примере выше из 1200 completion-токенов 1100 ушли на reasoning, и только 100 — на финальный ответ. Это нормально для сложных задач: модель «обдумывает» дольше, чем пишет.
Поле `completion_tokens_details.reasoning_tokens` информативное — показывает, сколько именно ушло на размышления. Денежная стоимость такая же, как для обычных completion-токенов.
## Как читать ответ
Сам процесс рассуждения скрыт — его не видно в `message.content`. Видно только финальный ответ:
```json
{
"choices": [{
"index": 0,
"message": {
"role": "assistant",
"content": "Ответ: 23 (простое, сумма цифр 5 — простое)"
},
"finish_reason": "stop"
}]
}
```
У некоторых моделей в перспективе появится поле `reasoning` рядом с `content` — мы пробросим его как есть от провайдера, без нормализации.
## Стриминг
Стриминг работает как обычно: chunk-и приходят по мере генерации финального ответа. Reasoning-фаза происходит ДО первого chunk-а — значит, для задач с высоким `reasoning_effort` первый chunk может прийти через 10–30 секунд молчания, а не сразу.
## Что дальше
* [POST /v1/chat/completions](/docs/api/chat-completions) — полная схема параметров.
* [Цены](/docs/concepts/pricing) — формула счёта.
---
# Стриминг ответов (/docs/features/streaming)
> Когда включать `stream: true`, как корректно собирать ответ модели и показывать прогресс пользователю.
При `stream: true` ответ модели приходит по частям через Server-Sent Events: вы видите текст по мере того, как он генерируется. Это даёт ощущение «живого» интерфейса: пользователь сразу видит первые слова и не ждёт полной завершённости запроса.
Технический формат SSE-стрима (chunk-структура, `[DONE]` маркер, заголовки) — на странице [POST /v1/chat/completions: Стриминг](/docs/api/streaming). Этот гид — про use-cases и практику использования в клиенте.
## Когда включать стриминг
Подходящие сценарии:
* **Чат-интерфейсы** — пользователь видит ответ по мере появления, как в ChatGPT.
* **Длинные ответы** (reasoning-модели, агенты) — без стрима пользователь смотрит 10–30 секунд в крутилку.
* **Прогресс-индикация** — показать, что модель уже отвечает (даже если первые слова — это reasoning-токены).
Не нужен:
* **Однострочные ответы** — классификация, экстракция данных, structured output. Накладные расходы на SSE-парсинг не оправданы.
* **Batch-сценарии** — обработка тысяч запросов в parallelизме, где UI не показывается.
* **Когда нужен сразу полный JSON для парсинга** — собирать stream и парсить «накопленный буфер» работает, но проще получить готовый ответ одним запросом.
## Python (синхронный)
OpenAI SDK скрывает работу с SSE — итерация по объекту `Stream` отдаёт уже распарсенные чанки.
```python
from openai import OpenAI
client = OpenAI(
base_url="https://api.hubris.pw/v1",
api_key="sk-gw-...",
)
stream = client.chat.completions.create(
model="anthropic/claude-haiku-4.5",
messages=[{"role": "user", "content": "Расскажи о трёх океанах планеты."}],
stream=True,
)
for chunk in stream:
if chunk.choices and chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="", flush=True)
# В последнем чанке Hubris отдаёт usage с итоговыми токенами:
# (доступен через stream.response.usage после завершения итерации)
```
Итерация автоматически останавливается на `[DONE]`. SDK обрабатывает чанк-буфер за вас — рисковать с ручным парсингом не нужно.
## Python (асинхронный)
```python
from openai import AsyncOpenAI
client = AsyncOpenAI(
base_url="https://api.hubris.pw/v1",
api_key="sk-gw-...",
)
async def stream_answer(prompt: str):
stream = await client.chat.completions.create(
model="anthropic/claude-haiku-4.5",
messages=[{"role": "user", "content": prompt}],
stream=True,
)
async for chunk in stream:
delta = chunk.choices[0].delta.content if chunk.choices else None
if delta:
yield delta
```
Удобно для FastAPI / aiohttp-эндпоинтов, которые транслируют ответ дальше клиенту.
## Node.js / TypeScript
```ts
import OpenAI from 'openai';
const client = new OpenAI({
baseURL: 'https://api.hubris.pw/v1',
apiKey: 'sk-gw-...',
});
const stream = await client.chat.completions.create({
model: 'anthropic/claude-haiku-4.5',
messages: [{ role: 'user', content: 'Опиши закат.' }],
stream: true,
});
for await (const chunk of stream) {
const delta = chunk.choices[0]?.delta?.content;
if (delta) process.stdout.write(delta);
}
```
`for await...of` итерирует асинхронно — каждый цикл это уже распарсенный chunk.
## Vercel AI SDK
Если вы пишете на Next.js и используете Vercel AI SDK — он работает с Hubris из коробки:
```ts
import { openai } from '@ai-sdk/openai';
import { streamText } from 'ai';
const result = await streamText({
model: openai.chat('anthropic/claude-haiku-4.5'),
messages: [{ role: 'user', content: 'Hello' }],
});
return result.toAIStreamResponse();
```
Конфигурация base URL — через переменную окружения `OPENAI_BASE_URL=https://api.hubris.pw/v1`, либо явно в `openai({ baseURL: ... })`. См. гид [Vercel AI SDK](/docs/frameworks/vercel-ai-sdk).
## Браузер (fetch + ReadableStream)
Если SDK для вас избыточен — можно стримить голыми руками:
```js
const resp = await fetch('https://api.hubris.pw/v1/chat/completions', {
method: 'POST',
headers: {
'Authorization': 'Bearer sk-gw-...',
'Content-Type': 'application/json',
},
body: JSON.stringify({
model: 'anthropic/claude-haiku-4.5',
messages: [{ role: 'user', content: 'Привет' }],
stream: true,
}),
});
const reader = resp.body.getReader();
const decoder = new TextDecoder();
let buffer = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
// Делим буфер по \n\n — каждый блок это SSE-событие
const events = buffer.split('\n\n');
buffer = events.pop() ?? ''; // последний может быть неполным — оставляем
for (const event of events) {
if (!event.startsWith('data: ')) continue;
const payload = event.slice('data: '.length);
if (payload === '[DONE]') return;
const chunk = JSON.parse(payload);
const delta = chunk.choices[0]?.delta?.content;
if (delta) appendToUI(delta);
}
}
```
Ключевые правила:
* **Буфер обязателен.** TCP может разбить SSE-блок на середине — не делайте `JSON.parse` сразу.
* **`[DONE]` не парсить как JSON.** Это литерал-маркер.
* **Используйте `decoder.decode(value, { stream: true })`** — чтобы UTF-8 multi-byte символы не ломались на границе чанков.
## Cancellation (отмена запроса)
Стандартный `AbortController` работает:
```ts
const ac = new AbortController();
const stream = await client.chat.completions.create(
{
model: 'anthropic/claude-haiku-4.5',
messages: [{ role: 'user', content: '...' }],
stream: true,
},
{ signal: ac.signal },
);
// Через 5 секунд отменяем
setTimeout(() => ac.abort(), 5000);
try {
for await (const chunk of stream) {
// ...
}
} catch (e) {
if (e.name === 'AbortError') {
console.log('Запрос отменён пользователем');
}
}
```
**Важно:** Hubris продолжает читать ответ модели до конца, даже если клиент закрыл соединение. Это защита от «бесплатных токенов». Иными словами, при отмене вы получите полное списание стоимости — токены уже сгенерированы апстримом, мы просто не доставили их вам.
## Прогресс в UI: типичные паттерны
### Типографический эффект (как в ChatGPT)
```ts
let buffer = '';
for await (const chunk of stream) {
const delta = chunk.choices[0]?.delta?.content ?? '';
buffer += delta;
setMessage(buffer); // React state setter
}
```
### Индикатор «модель думает»
Reasoning-модели могут несколько секунд молчать перед первым content-чанком (см. [Reasoning токены](/docs/features/reasoning)). Покажите spinner, пока `delta.content` не пришёл хотя бы один раз:
```ts
let firstChunk = true;
for await (const chunk of stream) {
const delta = chunk.choices[0]?.delta?.content;
if (!delta) continue;
if (firstChunk) {
hideSpinner();
firstChunk = false;
}
appendText(delta);
}
```
### Кнопка «Stop» с откатом UI
При отмене иногда нужно показать частичный ответ + label «прервано». Сохраняйте накопленный буфер в state и при `AbortError` дополняйте его пометкой — не очищайте.
## Tool calls и стриминг
Если модель вызывает функцию, `delta.tool_calls[]` приходит частями (имя функции и аргументы могут разорваться по чанкам). Собирайте по `index`. Подробности — в гиде [Вызов инструментов](/docs/features/tool-calling#стриминг).
## Биллинг
Стриминг не меняет тарификацию: списываем по тем же prompt + completion токенам, что и в обычном (не-стрим) запросе. Hubris всегда добавляет `stream_options: { include_usage: true }` к стрим-запросам, поэтому в последнем чанке вы видите итоговые токены.
Дисконнект клиента до завершения стрима — полное списание (см. выше). Это не баг, это анти-абьюз.
## Что важно знать
* **OpenAI SDK почти всегда правильный выбор.** Ручной SSE-парсинг — только если есть конкретная причина (другой стек, экстремальная оптимизация).
* **Чанк-границы непредсказуемы.** Не пытайтесь парсить `delta.content` как JSON — это просто кусок строки.
* **Headers commit before bytes.** Если стрим уже начался — мы уже отдали `200 OK` и SSE-заголовки. Ошибки ПОСЛЕ старта стрима не возвращаются как HTTP 5xx — клиент видит обрыв или `finish_reason: "stop"` на пустом delta.
## Что дальше
* [POST /v1/chat/completions: Стриминг](/docs/api/streaming) — технический формат чанков.
* [Вызов инструментов](/docs/features/tool-calling) — стрим с tool calls.
* [Reasoning токены](/docs/features/reasoning) — почему первый чанк может прийти не сразу.
* [Vercel AI SDK](/docs/frameworks/vercel-ai-sdk) — React/Next-стрим из коробки.
---
# Структурированный вывод (/docs/features/structured-output)
> Получение ответа строго в формате JSON по заданной схеме — без парсинга текста и без шанса получить «почти-JSON».
Структурированный вывод гарантирует, что модель вернёт валидный JSON и, при необходимости, соответствующий конкретной JSON Schema. Это убирает целый класс ошибок: «модель прислала JSON с лишним текстом», «закрывающая скобка съехала», «вместо числа — строка с числом».
Поддерживается большинством современных моделей. Жёсткое следование схеме (`strict: true`) — у OpenAI gpt-4o и новее, Claude 3.5 Sonnet и новее, Gemini 2.0+. Карточка модели в [каталоге](/models) подскажет.
## Два режима
Параметр `response_format` принимает один из трёх вариантов:
| Тип | Что гарантирует |
| -------------------------------- | ----------------------------------------------------------------- |
| `{ "type": "text" }` | Свободный текст. То же, что не передавать `response_format` вовсе |
| `{ "type": "json_object" }` | Ответ — синтаксически валидный JSON. Структура произвольная |
| `{ "type": "json_schema", ... }` | Ответ — JSON, соответствующий вашей схеме |
Режим `json_object` достаточен, если вы сами в системном промпте описали ожидаемые поля. Режим `json_schema` — строже: апстрим валидирует ответ модели и при `strict: true` гарантирует точное соответствие схеме.
## json\_object — простой случай
```bash
curl -s https://api.hubris.pw/v1/chat/completions \
-H "Authorization: Bearer sk-gw-..." \
-H "Content-Type: application/json" \
-d '{
"model": "openai/gpt-4o-mini",
"messages": [
{"role": "system", "content": "Верни поля: name (строка), age (число)."},
{"role": "user", "content": "Меня зовут Алексей, мне 34 года."}
],
"response_format": { "type": "json_object" }
}'
```
Ответ — гарантированно валидный JSON, но конкретные поля зависят от модели:
```json
{
"choices": [{
"message": {
"role": "assistant",
"content": "{\"name\": \"Алексей\", \"age\": 34}"
}
}]
}
```
Контент по-прежнему приходит строкой — `JSON.parse(...)` обязателен.
## json\_schema — строгий режим
Передайте схему в `response_format.json_schema`. Поле `strict: true` включает жёсткую валидацию: апстрим заранее ограничивает токены, которые модель может выдать, чтобы получить именно соответствующий вашей схеме JSON.
```bash
curl -s https://api.hubris.pw/v1/chat/completions \
-H "Authorization: Bearer sk-gw-..." \
-d '{
"model": "openai/gpt-4o-mini",
"messages": [
{"role": "user", "content": "Извлеки данные из квитанции: пицца 750₽, кола 150₽, чаевые 100₽."}
],
"response_format": {
"type": "json_schema",
"json_schema": {
"name": "receipt",
"strict": true,
"schema": {
"type": "object",
"properties": {
"items": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {"type": "string"},
"price_rub": {"type": "number"}
},
"required": ["name", "price_rub"],
"additionalProperties": false
}
},
"tip_rub": {"type": "number"},
"total_rub": {"type": "number"}
},
"required": ["items", "tip_rub", "total_rub"],
"additionalProperties": false
}
}
}
}'
```
Ответ:
```json
{
"choices": [{
"message": {
"content": "{\"items\":[{\"name\":\"пицца\",\"price_rub\":750},{\"name\":\"кола\",\"price_rub\":150}],\"tip_rub\":100,\"total_rub\":1000}"
},
"finish_reason": "stop"
}]
}
```
### Требования к схеме при `strict: true`
* Корневой объект — обязательно `"type": "object"`.
* Все поля, которые могут присутствовать в ответе, должны быть в `required[]`. Опциональные поля нужно делать union с `null`: `{"type": ["string", "null"]}`.
* На каждом уровне объекта обязателен `"additionalProperties": false`.
* Нельзя использовать `oneOf`, `allOf`, `not`, регулярные выражения в pattern, рекурсивные ссылки.
Если схема не проходит этим требованиям — апстрим вернёт ошибку валидации до старта генерации.
## OpenAI SDK
**Python (`pydantic` + helper `parse`):**
```python
from openai import OpenAI
from pydantic import BaseModel
client = OpenAI(
base_url="https://api.hubris.pw/v1",
api_key="sk-gw-...",
)
class ReceiptItem(BaseModel):
name: str
price_rub: float
class Receipt(BaseModel):
items: list[ReceiptItem]
tip_rub: float
total_rub: float
resp = client.chat.completions.parse(
model="openai/gpt-4o-mini",
messages=[{"role": "user", "content": "Пицца 750, кола 150, чаевые 100."}],
response_format=Receipt,
)
receipt = resp.choices[0].message.parsed
print(receipt.total_rub) # 1000.0
```
**TypeScript (`zod` + helper `parse`):**
```ts
import OpenAI from 'openai';
import { z } from 'zod';
import { zodResponseFormat } from 'openai/helpers/zod';
const client = new OpenAI({
baseURL: 'https://api.hubris.pw/v1',
apiKey: 'sk-gw-...',
});
const Receipt = z.object({
items: z.array(z.object({
name: z.string(),
price_rub: z.number(),
})),
tip_rub: z.number(),
total_rub: z.number(),
});
const resp = await client.chat.completions.parse({
model: 'openai/gpt-4o-mini',
messages: [{ role: 'user', content: 'Пицца 750, кола 150, чаевые 100.' }],
response_format: zodResponseFormat(Receipt, 'receipt'),
});
const receipt = resp.choices[0].message.parsed;
console.log(receipt?.total_rub); // 1000
```
Хелперы `parse` сами добавят правильный `response_format` и при успешном ответе вернут уже распарсенный и провалидированный объект в `.parsed`.
## Что делать с отказами
Если модель решит, что задача нерешаема (например, в квитанции совсем нет цен), она вернёт `refusal` вместо контента:
```json
{
"choices": [{
"message": {
"role": "assistant",
"content": null,
"refusal": "Не могу извлечь цены — в тексте нет числовых данных."
},
"finish_reason": "stop"
}]
}
```
В коде проверяйте оба поля — иначе при `refusal` ваш `JSON.parse(content)` упадёт на `null`.
## Стриминг
`stream: true` работает. Чанки в `delta.content` приходят кусками всё ещё валидной JSON-строки — собирайте, как обычный текст, парсите финал. Для прогрессивного парсинга (показывать ответ по мере прихода) понадобится партиальный JSON-парсер на клиенте.
## Биллинг
Структурированный вывод не имеет отдельного тарифа. Платите за обычные prompt- и completion-токены:
* Описание схемы попадает в prompt-токены каждого запроса.
* Финальный JSON-ответ — в completion-токенах.
Длинные схемы (десятки полей) могут заметно раздувать prompt-расход — учитывайте при тарификации.
## Что дальше
* [Вызов инструментов](/docs/features/tool-calling) — если вместо одного JSON-ответа нужно вызвать функцию и продолжить диалог.
* [POST /v1/chat/completions](/docs/api/chat-completions) — полная схема параметров.
* [Каталог моделей](/models) — поиск моделей с поддержкой `json_schema`.
---
# Вызов инструментов (tool calling) (/docs/features/tool-calling)
> Подключение функций к моделям — модель просит вызвать вашу функцию, вы исполняете её локально, отдаёте результат и получаете финальный ответ.
Tool calling позволяет модели запросить у вас выполнение функции (получить погоду, прочитать БД, отправить письмо), получить результат и продолжить диалог с учётом этого результата. Сама модель не исполняет код — она только описывает, что и с какими аргументами надо вызвать. Исполняет — ваш клиент.
Поддерживается большинством современных моделей: Claude (Sonnet, Opus, Haiku), GPT-4o и новее, Gemini Pro/Flash, Llama 3.1+, Mistral Large. Конкретная карточка модели в [каталоге](/models) показывает поддержку.
## Как это работает
Жизненный цикл одного «раунда» вызова инструмента:
1. Вы шлёте запрос с `messages` и `tools` (описанием доступных функций).
2. Модель решает: ответить текстом или попросить вызвать функцию. В случае второго — возвращает `finish_reason: "tool_calls"` и массив `tool_calls[]` в assistant-сообщении.
3. Вы локально исполняете эти функции, формируете ответы.
4. Шлёте новый запрос: добавляете assistant-сообщение с `tool_calls` и одно или несколько `role: "tool"` сообщений с результатами.
5. Модель формирует финальный ответ для пользователя.
При параллельных вызовах модель может попросить вызвать несколько функций за один шаг.
## Минимальный пример
Шаг 1 — запрос с описанием функции:
```bash
curl -s https://api.hubris.pw/v1/chat/completions \
-H "Authorization: Bearer sk-gw-..." \
-H "Content-Type: application/json" \
-d '{
"model": "anthropic/claude-haiku-4.5",
"messages": [
{"role": "user", "content": "Какая погода в Москве?"}
],
"tools": [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Возвращает текущую погоду в указанном городе.",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "Название города"},
"units": {"type": "string", "enum": ["celsius", "fahrenheit"]}
},
"required": ["city"]
}
}
}
]
}'
```
Ответ модели — она просит вызвать `get_weather`:
```json
{
"id": "chatcmpl-...",
"choices": [{
"index": 0,
"message": {
"role": "assistant",
"content": null,
"tool_calls": [
{
"id": "call_abc123",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"city\":\"Москва\",\"units\":\"celsius\"}"
}
}
]
},
"finish_reason": "tool_calls"
}]
}
```
Шаг 2 — исполняете функцию у себя и шлёте результат. Важно: сохраните `id` вызова из `tool_calls[].id` и проставьте его в `tool_call_id`.
```bash
curl -s https://api.hubris.pw/v1/chat/completions \
-H "Authorization: Bearer sk-gw-..." \
-d '{
"model": "anthropic/claude-haiku-4.5",
"messages": [
{"role": "user", "content": "Какая погода в Москве?"},
{
"role": "assistant",
"content": null,
"tool_calls": [{
"id": "call_abc123",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"city\":\"Москва\",\"units\":\"celsius\"}"
}
}]
},
{
"role": "tool",
"tool_call_id": "call_abc123",
"content": "{\"temperature\": -3, \"conditions\": \"снег\"}"
}
],
"tools": [ /* те же tools, что и в первом запросе */ ]
}'
```
Модель ответит текстом с учётом результата:
```json
{
"choices": [{
"message": {
"role": "assistant",
"content": "В Москве сейчас −3 °C и идёт снег. Одевайтесь теплее."
},
"finish_reason": "stop"
}]
}
```
## `tool_choice` — как заставить или запретить вызовы
По умолчанию (`auto`) модель сама решает, нужен ли инструмент. Можно переопределить:
| Значение | Эффект |
| ----------------------------------------------------- | ----------------------------------------------- |
| `"auto"` (по умолчанию) | Модель сама решает: текст или вызов |
| `"none"` | Запретить вызовы, модель ответит только текстом |
| `"required"` | Заставить вызвать какой-нибудь инструмент |
| `{ "type": "function", "function": { "name": "X" } }` | Заставить вызвать именно функцию `X` |
```json
"tool_choice": { "type": "function", "function": { "name": "get_weather" } }
```
## Параллельные вызовы
Если в одном шаге модель решит вызвать несколько функций сразу (например, погоду в трёх городах) — в `tool_calls[]` придёт несколько элементов. Вы исполняете их параллельно, шлёте обратно столько же `role: "tool"` сообщений с соответствующими `tool_call_id`.
Отключить параллельные вызовы (модель будет звать функции по одной):
```json
"parallel_tool_calls": false
```
## Стриминг
При `stream: true` поля `tool_calls` приходят в `delta.tool_calls[]` по частям — имя функции и аргументы могут разбиться на несколько чанков. Собирайте по `index`:
```json
data: {"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"id":"call_abc","type":"function","function":{"name":"get_weather"}}]}}]}
data: {"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\"city\":"}}]}}]}
data: {"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"Москва\"}"}}]}}]}
data: {"choices":[{"index":0,"finish_reason":"tool_calls"}]}
```
Финальный чанк с `finish_reason: "tool_calls"` означает, что модель закончила формировать вызов и ждёт результата.
## OpenAI SDK
Официальные SDK OpenAI поддерживают tools «как есть».
**Python:**
```python
from openai import OpenAI
client = OpenAI(
base_url="https://api.hubris.pw/v1",
api_key="sk-gw-...",
)
tools = [{
"type": "function",
"function": {
"name": "get_weather",
"description": "Возвращает текущую погоду в указанном городе.",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string"},
"units": {"type": "string", "enum": ["celsius", "fahrenheit"]},
},
"required": ["city"],
},
},
}]
resp = client.chat.completions.create(
model="anthropic/claude-haiku-4.5",
messages=[{"role": "user", "content": "Какая погода в Москве?"}],
tools=tools,
)
msg = resp.choices[0].message
if msg.tool_calls:
for call in msg.tool_calls:
args = json.loads(call.function.arguments)
result = get_weather(**args) # ваша локальная функция
# ... добавьте assistant + tool сообщения и сделайте второй вызов
```
**TypeScript:**
```ts
import OpenAI from 'openai';
const client = new OpenAI({
baseURL: 'https://api.hubris.pw/v1',
apiKey: 'sk-gw-...',
});
const resp = await client.chat.completions.create({
model: 'anthropic/claude-haiku-4.5',
messages: [{ role: 'user', content: 'Какая погода в Москве?' }],
tools: [
{
type: 'function',
function: {
name: 'get_weather',
description: 'Возвращает текущую погоду в указанном городе.',
parameters: {
type: 'object',
properties: {
city: { type: 'string' },
units: { type: 'string', enum: ['celsius', 'fahrenheit'] },
},
required: ['city'],
},
},
},
],
});
const msg = resp.choices[0].message;
if (msg.tool_calls?.length) {
for (const call of msg.tool_calls) {
const args = JSON.parse(call.function.arguments);
const result = await getWeather(args);
// ... добавьте assistant + tool сообщения и сделайте второй вызов
}
}
```
## Биллинг
Tool-calling не имеет отдельного тарифа. Списываются обычные prompt- и completion-токены за каждый шаг диалога:
* Шаг 1 (модель просит вызвать функцию) — `prompt_tokens` за описание инструментов и сообщения, `completion_tokens` за сгенерированный `tool_calls[]`.
* Шаг 2 (вы шлёте результат) — `prompt_tokens` за весь диалог (включая `role: "tool"` сообщения), `completion_tokens` за финальный ответ.
Описания инструментов считаются как часть промпта на каждом шаге, где они переданы. Если у вас 10 функций по 200 токенов каждая — это +2000 prompt-токенов за вызов.
## Что дальше
* [POST /v1/chat/completions](/docs/api/chat-completions) — полная схема параметров и ответа.
* [Структурированный вывод](/docs/features/structured-output) — если вам нужен не вызов функции, а строго типизированный JSON-ответ.
* [Поиск в интернете](/docs/features/web-search) — встроенный server-side инструмент, который мы исполняем за вас.
* [Каталог моделей](/models) — какие модели поддерживают tool-calling.
---
# Мониторинг расходов (/docs/features/usage-tracking)
> Программный учёт трат через GET /v1/usage — отслеживание расхода по ключу, бюджет-алёрты, отчёты.
Hubris отдаёт расход по API-ключу через `GET /v1/usage` — отдельные запросы и агрегированные суммы. Используется для автоматического мониторинга: алёрты по порогам, отчёты, экспорт в свою аналитику.
Endpoint-референс — на [GET /v1/usage](/docs/api/usage). Этот гид — про практические паттерны.
## Базовый запрос
```bash
curl -s -H "Authorization: Bearer $HUBRIS_API_KEY" \
https://api.hubris.pw/v1/usage
```
По умолчанию — расход за последние 24 часа по тому ключу, которым сделан запрос. Ответ:
```json
{
"object": "usage",
"period": { "from": "2026-05-21T00:00Z", "to": "2026-05-22T00:00Z" },
"scope": { "key_id": "...", "key_prefix": "sk-gw-758f...d4e3" },
"totals": {
"requests": 142,
"prompt_tokens": 18500,
"completion_tokens": 3200,
"total_tokens": 21700,
"cost_rub": 187.45,
"cost_kopecks": "18745"
},
"granularity": null,
"buckets": null
}
```
**Важно:** виден расход **только этого ключа**, а не всего аккаунта. Это намеренно — скомпрометированный ключ не должен раскрывать общий бюджет.
## Паттерн 1: ежедневный отчёт
Cron-задача, которая раз в день берёт суммарный расход за вчера и шлёт письмо/слак/телеграм:
```python
import os
import requests
from datetime import datetime, timedelta, timezone
key = os.environ["HUBRIS_API_KEY"]
yesterday = datetime.now(timezone.utc) - timedelta(days=1)
day_start = yesterday.replace(hour=0, minute=0, second=0, microsecond=0)
day_end = day_start + timedelta(days=1)
resp = requests.get(
"https://api.hubris.pw/v1/usage",
params={"from": day_start.isoformat(), "to": day_end.isoformat()},
headers={"Authorization": f"Bearer {key}"},
).json()
totals = resp["totals"]
print(f"Вчера: {totals['requests']} запросов, {totals['cost_rub']} ₽")
# Отправить в Slack, если расход за день выше нормы:
if float(totals["cost_rub"]) > 500:
notify_slack(f"⚠️ Вчера ушло {totals['cost_rub']} ₽")
```
## Паттерн 2: бюджет-алёрт после каждого запроса
Если у вас агент, который может за сессию «уйти в космос» — проверяйте расход после каждого запроса и останавливайтесь при превышении лимита:
```python
DAILY_BUDGET_RUB = 1000
def check_budget():
resp = requests.get(
"https://api.hubris.pw/v1/usage",
params={"period": "today"},
headers={"Authorization": f"Bearer {key}"},
).json()
return float(resp["totals"]["cost_rub"])
# В цикле агента
while task_not_done:
if check_budget() > DAILY_BUDGET_RUB:
raise BudgetExceeded(f"Превышен дневной лимит {DAILY_BUDGET_RUB} ₽")
response = run_step()
process(response)
```
В большинстве сценариев лучше использовать **встроенный дневной лимит ключа** (см. [/keys](/keys)) — он гарантированно прерывает на стороне сервера и возвращает `429 daily_limit_exceeded`. Бюджет в коде — мягкая защита поверх него, для случаев когда нужен ранний алёрт «потратили 80% бюджета — переключаемся на дешёвую модель».
## Паттерн 3: график по дням
Для дашборда — берите данные с почасовой/посуточной разбивкой:
```bash
curl -s -H "Authorization: Bearer $HUBRIS_API_KEY" \
"https://api.hubris.pw/v1/usage?period=30d&granularity=day"
```
В ответе появится массив `buckets`:
```json
{
"totals": { "cost_rub": 5432.10, ... },
"granularity": "day",
"buckets": [
{ "bucket": "2026-04-22T00:00:00Z", "cost_rub": 180.30, "requests": 142 },
{ "bucket": "2026-04-23T00:00:00Z", "cost_rub": 213.45, "requests": 167 },
{ "bucket": "2026-04-24T00:00:00Z", "cost_rub": 0, "requests": 0 }
]
}
```
Дни без запросов всё равно присутствуют (с `cost_rub: 0`) — удобно для линейного графика без пропусков.
## Паттерн 4: разделение по проектам
Один аккаунт — несколько ключей под разные проекты. Дашборд `/usage` агрегирует общий расход; API даёт расход **только запрашиваемого ключа**.
Чтобы получить разбивку по проектам — храните ключи (или их `key_id`) на стороне ваших систем и запрашивайте `/v1/usage` каждым ключом отдельно:
```python
PROJECT_KEYS = {
"production": os.environ["HUBRIS_KEY_PROD"],
"staging": os.environ["HUBRIS_KEY_STG"],
"research": os.environ["HUBRIS_KEY_RES"],
}
for project, key in PROJECT_KEYS.items():
resp = requests.get(
"https://api.hubris.pw/v1/usage",
params={"period": "7d"},
headers={"Authorization": f"Bearer {key}"},
).json()
print(f"{project}: {resp['totals']['cost_rub']} ₽ / {resp['totals']['requests']} req")
```
## TypeScript
```ts
type UsageResp = {
totals: {
requests: number;
prompt_tokens: number;
completion_tokens: number;
cost_rub: number;
cost_kopecks: string;
};
buckets: Array<{
bucket: string;
cost_rub: number;
cost_kopecks: string;
requests: number;
}> | null;
};
async function fetchUsage(period = '7d', granularity?: 'hour' | 'day') {
const url = new URL('https://api.hubris.pw/v1/usage');
url.searchParams.set('period', period);
if (granularity) url.searchParams.set('granularity', granularity);
const resp = await fetch(url, {
headers: { Authorization: `Bearer ${process.env.HUBRIS_API_KEY}` },
});
return (await resp.json()) as UsageResp;
}
const week = await fetchUsage('7d', 'day');
console.log(`За 7 дней: ${week.totals.cost_rub} ₽`);
for (const day of week.buckets ?? []) {
console.log(` ${day.bucket}: ${day.cost_rub} ₽`);
}
```
## Точность сумм при больших расходах
`cost_kopecks` приходит как строка — чтобы при сериализации в JavaScript не терялась точность на больших суммах. Если вам важна копейка-в-копейку точность (бухучёт, биллинг клиентов поверх Hubris) — парсите `cost_kopecks` через `BigInt`, не через `Number`:
```ts
const kopecks = BigInt(resp.totals.cost_kopecks);
const rubFormatted = (Number(kopecks) / 100).toFixed(2);
```
Поле `cost_rub` — это уже `cost_kopecks / 100`, удобно для отображения, но не для арифметики.
## Часовые пояса
Время в API — UTC. Если нужно «расход за сегодня по Москве» — конвертируйте границы суток у себя и передавайте явные `from` / `to` (ISO 8601 UTC):
```python
from datetime import datetime, timezone, timedelta
MSK = timezone(timedelta(hours=3))
now_msk = datetime.now(MSK)
day_start_msk = now_msk.replace(hour=0, minute=0, second=0, microsecond=0)
day_end_msk = day_start_msk + timedelta(days=1)
resp = requests.get(
"https://api.hubris.pw/v1/usage",
params={
"from": day_start_msk.astimezone(timezone.utc).isoformat(),
"to": day_end_msk.astimezone(timezone.utc).isoformat(),
},
headers={"Authorization": f"Bearer {key}"},
).json()
```
## Дашбордная альтернатива
Если ручной мониторинг — overkill, на странице [/usage](/usage) дашборда есть готовые фильтры по модели, ключу и статусу, графики по дням и CSV-экспорт. Многие сценарии решаются прямо в UI без написания кода.
## Что важно знать
* **Кешируйте.** Не дёргайте `/v1/usage` чаще, чем раз в минуту — для большинства сценариев достаточно раз в 5–10 минут. Избыточные запросы влияют только на ваш rate-budget.
* **Расход обновляется в течение нескольких секунд** после успешного списания. Свежий запрос → пара секунд → его стоимость в `/v1/usage`.
* **Запросы на free-модели** добавляются к `requests`, но не к `cost_rub`. Если у вас активны бесплатные модели, число запросов в дашборде будет выше, чем «сколько денег ушло».
* **Только успешные запросы** входят в totals. Запросы с ошибками (4xx/5xx) не списываются и не попадают в счётчик расхода.
## Что дальше
* [GET /v1/usage](/docs/api/usage) — полная схема параметров и ответа.
* [Управление ключами](/keys) — настроить дневной лимит на уровне ключа.
* [Биллинг](/docs/concepts/billing) — как формируется стоимость токенов.
* [/usage](/usage) — дашборд расходов с фильтрами и CSV-экспортом.
---
# Изображения на вход (Vision) (/docs/features/vision)
> Отправка изображений в модель для анализа — описание содержимого, OCR, классификация, ответы на вопросы по картинке.
Vision (мультимодальный ввод) — это когда модель получает на вход не только текст, но и изображения. Подходит для описания содержимого, OCR, классификации, ответов на вопросы по картинке, разбора скриншотов и диаграмм.
Не путайте с [генерацией изображений](/docs/features/image-generation): vision — это **картинка на вход**, image-generation — **картинка на выход**.
## Поддерживающие модели
Vision поддерживает любая модель, у которой в карточке (`GET /v1/models`) поле `input_modalities` содержит `"image"`. Это, как правило, актуальные версии Claude (Sonnet/Opus), GPT-4o, Gemini, а также часть open-source моделей (Llama Vision, Qwen-VL). Точный актуальный список — в [каталоге](/models) с фильтром «принимает изображения».
## Минимальный пример
Передайте картинку как content-part типа `image_url` внутри user-сообщения:
```bash
curl -s https://api.hubris.pw/v1/chat/completions \
-H "Authorization: Bearer sk-gw-..." \
-H "Content-Type: application/json" \
-d '{
"model": "anthropic/claude-haiku-4.5",
"messages": [
{
"role": "user",
"content": [
{ "type": "text", "text": "Что изображено на картинке?" },
{
"type": "image_url",
"image_url": {
"url": "https://example.com/photo.jpg"
}
}
]
}
]
}'
```
Контент user-сообщения — это массив content-part'ов. Каждая часть — либо `{ "type": "text", "text": "..." }`, либо `{ "type": "image_url", "image_url": { "url": "..." } }`. Порядок свободный.
## Два способа передать картинку
### URL
```json
{
"type": "image_url",
"image_url": {
"url": "https://example.com/photo.jpg"
}
}
```
Должен быть публично-доступный HTTPS URL. Провайдер модели скачает картинку сам. Размер и формат — на усмотрение провайдера; типичный лимит 5–20 МБ, поддерживаются JPEG, PNG, WebP, GIF.
### Base64 data URL
Когда картинка не лежит в публичном интернете (генерится на лету, приходит от пользователя), кодируйте её в base64 и шлите как data URL:
```json
{
"type": "image_url",
"image_url": {
"url": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAAAAAAA..."
}
}
```
Формат — `data:image/;base64,`. Mime может быть `image/jpeg`, `image/png`, `image/webp`, `image/gif`. Base64-сам по себе делает строку на \~33 % длиннее, поэтому общий размер запроса может быть значительным — учитывайте при тарификации (см. ниже).
## Несколько изображений в одном запросе
Можно передать сколько угодно картинок в одном сообщении — добавляйте content-part'ы по одной:
```json
{
"role": "user",
"content": [
{ "type": "text", "text": "Сравни эти два скриншота интерфейса." },
{ "type": "image_url", "image_url": { "url": "https://.../before.png" } },
{ "type": "image_url", "image_url": { "url": "https://.../after.png" } }
]
}
```
Модель увидит обе картинки и сможет ссылаться на них в ответе («на первом скриншоте...»).
## Параметр `detail`
Часть моделей (gpt-4o-семейство) принимают подсказку о требуемой детализации:
```json
{
"type": "image_url",
"image_url": {
"url": "https://...",
"detail": "high"
}
}
```
| Значение | Что значит |
| ----------------------- | ------------------------------------------------------------------------------------ |
| `"low"` | модель смотрит на картинку в низком разрешении — дёшево, годится для общего описания |
| `"high"` | детальное чтение — мелкий текст, цифры, диаграммы |
| `"auto"` (по умолчанию) | модель решает сама |
Не все модели поддерживают `detail` — он пробрасывается как есть; если модель его не понимает, пара значение/поле просто игнорируется апстримом.
## OpenAI SDK
Vision работает в официальных SDK OpenAI без дополнительных манипуляций.
**Python:**
```python
from openai import OpenAI
client = OpenAI(
base_url="https://api.hubris.pw/v1",
api_key="sk-gw-...",
)
resp = client.chat.completions.create(
model="openai/gpt-4o-mini",
messages=[
{
"role": "user",
"content": [
{"type": "text", "text": "Распознай текст на этом скриншоте."},
{
"type": "image_url",
"image_url": {
"url": "https://example.com/screenshot.png",
"detail": "high",
},
},
],
}
],
)
print(resp.choices[0].message.content)
```
**TypeScript:**
```ts
import OpenAI from 'openai';
const client = new OpenAI({
baseURL: 'https://api.hubris.pw/v1',
apiKey: 'sk-gw-...',
});
const resp = await client.chat.completions.create({
model: 'openai/gpt-4o-mini',
messages: [
{
role: 'user',
content: [
{ type: 'text', text: 'Опиши, что на фото.' },
{
type: 'image_url',
image_url: { url: 'https://example.com/photo.jpg' },
},
],
},
],
});
console.log(resp.choices[0].message.content);
```
### Base64 в Python из файла
```python
import base64
with open("photo.jpg", "rb") as f:
encoded = base64.b64encode(f.read()).decode()
data_url = f"data:image/jpeg;base64,{encoded}"
resp = client.chat.completions.create(
model="openai/gpt-4o-mini",
messages=[
{
"role": "user",
"content": [
{"type": "text", "text": "Опиши фото."},
{"type": "image_url", "image_url": {"url": data_url}},
],
}
],
)
```
## Стриминг
`stream: true` работает с vision-запросами как с обычными — ответ модели приходит чанками, prompt с изображениями просто длиннее. Никаких особенностей.
## Биллинг
Изображение на вход тарифицируется как **дополнительные prompt-токены**. Количество токенов на одну картинку зависит от модели и от её размера/разрешения. Грубо:
* Маленькая картинка в `detail: "low"` — порядка сотни prompt-токенов.
* Стандартное разрешение в `detail: "auto"`/`"high"` — от нескольких сотен до нескольких тысяч prompt-токенов.
Точное количество видно в `usage.prompt_tokens` в ответе. Списание с баланса — стандартная формула: prompt + completion токены умножаются на цену модели из [каталога](/models).
Base64 в URL **не** добавляет токенов — это просто способ доставки байтов. Тарифицируется только распарсенная картинка, не размер JSON-запроса.
## Что важно знать
* **Большие картинки = большой prompt.** Если шлёте 2K-скриншоты, ожидайте по 1000–3000 prompt-токенов на каждую. На сессии с десятком картинок это заметные деньги.
* **Картинка в URL должна быть публично-доступной.** Авторизованные URL (`Authorization: ...` заголовок при загрузке) не подойдут — провайдер скачивает анонимно. Если картинка приватная — шлите как base64.
* **Vision и structured output совмещаются.** Можно передать картинку и попросить вернуть JSON по схеме — например, извлечь поля из чека.
* **Vision и tool calling тоже совмещаются.** Модель может смотреть на картинку и вызывать функции на основе того, что увидела.
## Что дальше
* [Генерация изображений](/docs/features/image-generation) — обратная задача: получить картинку на выходе.
* [Структурированный вывод](/docs/features/structured-output) — извлечение данных из картинки в JSON-схему.
* [POST /v1/chat/completions](/docs/api/chat-completions) — полная схема параметров.
* [Каталог моделей](/models) — фильтр по поддержке Vision.
---
# Поиск в интернете (/docs/features/web-search)
> Server-side инструмент — модель сама ищет в интернете и пишет ответ с источниками. Никакого ручного исполнения у вас на стороне.
В отличие от обычных функций ([Вызов инструментов](/docs/features/tool-calling)), которые исполняете вы, веб-поиск — это **server-side инструмент**: вы добавляете его в `tools[]`, модель сама вызывает поиск, получает результаты, формирует ответ и присылает его одним финальным сообщением. Вашему клиенту ничего исполнять не нужно — никаких лишних round-trip'ов и `role: "tool"` сообщений.
Работает с большинством моделей. Под капотом наш слой маршрутизации выбирает поисковый движок — у части моделей он встроен в саму модель, у части подключается отдельно. Для вас разница только в наборе источников, которые попадут в ответ.
## Минимальный пример
Передайте в `tools[]` элемент с типом `hubris:web_search` (это наш namespace для server-side инструментов — отличает их от обычных function-tool):
```bash
curl -s https://api.hubris.pw/v1/chat/completions \
-H "Authorization: Bearer sk-gw-..." \
-H "Content-Type: application/json" \
-d '{
"model": "openai/gpt-4o-mini",
"messages": [
{"role": "user", "content": "Что произошло на рынке российских облигаций на прошлой неделе?"}
],
"tools": [
{ "type": "hubris:web_search" }
]
}'
```
Ответ — обычный chat-ответ, но с дополнительным полем `annotations[]`, где перечислены источники:
```json
{
"id": "chatcmpl-...",
"choices": [{
"index": 0,
"message": {
"role": "assistant",
"content": "На прошлой неделе ставка ОФЗ выросла до 16.3% на длинных бумагах...",
"annotations": [
{
"type": "url_citation",
"url_citation": {
"url": "https://www.cbr.ru/press/event/?id=12345",
"title": "ЦБ РФ — Информация о денежно-кредитной политике",
"content": "Совет директоров Банка России 17 мая 2026 года...",
"start_index": 42,
"end_index": 87
}
}
]
},
"finish_reason": "stop"
}],
"usage": {
"prompt_tokens": 4521,
"completion_tokens": 318,
"total_tokens": 4839
}
}
```
`tool_calls` остаётся пустым — поиск отрабатывается полностью на стороне нашего слоя маршрутизации, наружу не вылезает.
## Параметры поиска
Можно тонко настроить поведение:
```json
{
"type": "hubris:web_search",
"parameters": {
"max_results": 5,
"search_context_size": "medium"
}
}
```
| Параметр | Значения | Описание |
| --------------------- | ------------------------------- | -------------------------------------------------------------------------------------------- |
| `max_results` | 1–10 (по умолчанию 5) | Сколько источников использовать для составления ответа |
| `search_context_size` | `"low"` / `"medium"` / `"high"` | Сколько контекста из каждого источника подгружать в промпт. Больше — точнее ответ, но дороже |
Все параметры опциональны.
## Аннотации
`annotations[]` приходит на сообщении в формате:
```json
{
"type": "url_citation",
"url_citation": {
"url": "https://...",
"title": "Заголовок страницы",
"content": "Релевантный фрагмент текста источника",
"start_index": 42,
"end_index": 87
}
}
```
`start_index` / `end_index` — позиция (в символах) в `message.content`, к которой относится цитата. Удобно для подсветки источников рядом с текстом в UI.
## Стриминг
`stream: true` работает. Особенность: **`annotations[]` приходит полным массивом сразу в первом чанке** (внутри `delta.annotations`), ещё до самого текста. Дальше идут обычные текстовые `delta.content` чанки.
```
data: {"choices":[{"index":0,"delta":{"role":"assistant","annotations":[{"type":"url_citation","url_citation":{...}}]}}]}
data: {"choices":[{"index":0,"delta":{"content":"На прошлой "}}]}
data: {"choices":[{"index":0,"delta":{"content":"неделе ставка ОФЗ "}}]}
...
data: {"choices":[{"index":0,"finish_reason":"stop"}]}
data: [DONE]
```
Это удобно: в UI можно сразу показать «по N источникам» — а текст подгружать по мере прихода.
## OpenAI SDK
В официальных SDK OpenAI поле `tools` поддерживается, но проверки типа стандартные `function` — поэтому server-tool придётся передавать через `extra_body` (Python) или приведением типов (TypeScript).
**Python:**
```python
from openai import OpenAI
client = OpenAI(
base_url="https://api.hubris.pw/v1",
api_key="sk-gw-...",
)
resp = client.chat.completions.create(
model="openai/gpt-4o-mini",
messages=[{"role": "user", "content": "Курс доллара ЦБ на сегодня?"}],
extra_body={
"tools": [
{"type": "hubris:web_search", "parameters": {"max_results": 3}}
]
},
)
msg = resp.choices[0].message
print(msg.content)
annotations = msg.model_extra.get("annotations", [])
for ann in annotations:
cit = ann["url_citation"]
print(f" - {cit['title']}: {cit['url']}")
```
**TypeScript:**
```ts
import OpenAI from 'openai';
const client = new OpenAI({
baseURL: 'https://api.hubris.pw/v1',
apiKey: 'sk-gw-...',
});
const resp = await client.chat.completions.create({
model: 'openai/gpt-4o-mini',
messages: [{ role: 'user', content: 'Курс доллара ЦБ на сегодня?' }],
tools: [
{ type: 'hubris:web_search', parameters: { max_results: 3 } },
],
} as any);
const msg = resp.choices[0].message;
console.log(msg.content);
const annotations = (msg as any).annotations ?? [];
for (const ann of annotations) {
console.log(` - ${ann.url_citation.title}: ${ann.url_citation.url}`);
}
```
## Что важно знать
* **`prompt_tokens` будут раздуты.** Результаты поиска инжектятся в промпт — это нормально, что на коротком запросе вернутся 3–10 тысяч prompt-токенов. Ответ модели опирается на эти источники.
* **`tool_calls` пустой.** Не пытайтесь обработать веб-поиск как обычный function-call — поиск отрабатывает полностью на нашей стороне.
* **Совмещение с обычными функциями работает.** В одном `tools[]` можно передать и server-tool, и свои function-tools — модель выберет, что вызывать.
* **`/v1/responses` со стримингом — известное ограничение.** В non-stream ответе `/v1/responses` элемент в `output[]` приходит с типом `hubris:web_search`. В streaming-режиме на этом эндпоинте имя пока не нормализуется обратно — в SSE-событиях `response.output_item.added` / `response.output_item.done` префикс типа отличается от того, что вы отправили в запросе. Будет исправлено. На `/v1/chat/completions` (любой режим) и на non-stream `/v1/responses` всё корректно.
## Биллинг
Веб-поиск тарифицируется не только токенами:
* **Токены** — обычным образом (prompt + completion). С учётом раздутого prompt после инжекта результатов.
* **Плата за поиск** — фиксированная сумма за запрос (зависит от модели и `search_context_size`).
В большинстве случаев бо́льшая часть стоимости запроса — это именно плата за поиск, не токены. Итоговая сумма по запросу видна в разделе [«Расходы»](/usage).
Списание происходит после успешного ответа. Транзакция атомарна — баланс, usage\_log и движение по балансу пишутся одной БД-транзакцией.
## Что дальше
* [Вызов инструментов](/docs/features/tool-calling) — для функций, которые исполняете вы сами.
* [Структурированный вывод](/docs/features/structured-output) — заставить ответ соответствовать JSON-схеме (работает вместе с веб-поиском).
* [POST /v1/chat/completions](/docs/api/chat-completions) — полная схема.
* [Расходы](/usage) — детализация запросов и стоимости.
---
# Codex CLI (/docs/frameworks/codex-cli)
> Подключить агентский CLI от OpenAI к Hubris через Responses API.
[Codex CLI](https://github.com/openai/codex) — это локальный агент для написания и правки кода от OpenAI. По умолчанию он ходит в OpenAI Responses API (`/v1/responses`), а не в Chat Completions, и его можно перенаправить на Hubris через файл конфигурации.
## Установка
```bash
npm install -g @openai/codex
```
Минимум — Node.js 20. На Windows работает через WSL2 или обычный PowerShell с глобальным npm.
## Подключение к Hubris
Codex CLI читает настройки из `~/.codex/config.toml` (на Windows — `%USERPROFILE%\.codex\config.toml`). Добавьте туда блок с провайдером Hubris и выберите его по умолчанию:
```toml
[model_providers.hubris]
name = "Hubris"
base_url = "https://api.hubris.pw/v1"
env_key = "HUBRIS_API_KEY"
wire_api = "responses"
requires_openai_auth = false
model_provider = "hubris"
model = "openai/gpt-4o-mini"
```
Что в этих полях:
* `base_url` — корень API Hubris вместе с префиксом `/v1`.
* `env_key` — имя переменной окружения, из которой Codex CLI возьмёт ваш API-ключ Hubris. Сам ключ держите в переменной, а не в файле:
```bash
export HUBRIS_API_KEY="sk-gw-..." # macOS / Linux / WSL
```
```powershell
$env:HUBRIS_API_KEY = "sk-gw-..." # PowerShell
```
* `wire_api = "responses"` — Codex CLI поддерживает только Responses API, поэтому значение тут единственное.
* `requires_openai_auth = false` — отключает встроенный вход через ChatGPT (он не нужен, ключ берётся из `HUBRIS_API_KEY`).
* `model` — модель из [нашего каталога](/models). Любая активная модель в формате `provider/model` подойдёт.
## Запуск
```bash
codex
```
При первом запуске Codex CLI проверит конфигурацию и сделает тестовый запрос. Если ключ корректный — увидите интерактивную сессию. Если нет — ошибка авторизации, проверьте `HUBRIS_API_KEY` и сам ключ на странице [Ключи](https://hubris.pw/keys) в дашборде.
## Выбор модели
Codex CLI хорошо работает с любой моделью, у которой стабильный Responses API. Рекомендации по задачам:
* **Быстрые правки, рутина** — `openai/gpt-4o-mini`, `anthropic/claude-haiku-4.5`.
* **Сложные изменения, рефакторинг** — `openai/gpt-4o`, `anthropic/claude-sonnet-4.5`.
* **Когда нужны рассуждения** (планирование больших изменений, поиск багов) — `openai/o1`, `openai/o3-mini`.
Сменить модель можно прямо в `config.toml` или флагом `--model`:
```bash
codex --model anthropic/claude-sonnet-4.5
```
Полный список — на странице [Каталог моделей](/models). Цены указаны там же в рублях за 1 миллион токенов.
## Тарификация
Codex CLI ничем не отличается от прямых запросов к `/v1/responses` — каждое сообщение списывается с баланса по тарифу выбранной модели. Токены рассуждения (если модель их использует) учитываются в выходных токенах. Историю запросов и затраты смотрите в дашборде на странице [Использование](https://hubris.pw/usage).
## Что дальше
* [POST /v1/responses](/docs/api/responses) — описание самого эндпоинта, который дёргает Codex CLI.
* [Аутентификация](/docs/authentication) — как создать ключ и где хранить его в проекте.
* [Каталог моделей](/models) — какие модели сейчас активны и сколько стоят.
---
# LangChain.js (/docs/frameworks/langchain-js)
> Подключить Hubris к LangChain.js через ChatOpenAI.
LangChain.js работает с Hubris через `ChatOpenAI` из пакета `@langchain/openai`.
## Установка
```bash
npm install langchain @langchain/openai @langchain/core
```
## Подключение
```ts
import { ChatOpenAI } from "@langchain/openai";
const llm = new ChatOpenAI({
model: "anthropic/claude-haiku-4.5",
apiKey: process.env.HUBRIS_API_KEY,
configuration: {
baseURL: "https://api.hubris.pw/v1",
},
});
```
**Замечание про `configuration.baseURL`.** В LangChain.js есть несколько способов задать base URL — самый надёжный для стриминга — через объект `configuration` (он передаётся в OpenAI SDK внутри). Не используйте устаревший `basePath` или `apiBaseUrl` — они ломают стриминг в некоторых версиях.
## Базовый вызов
```ts
import { HumanMessage } from "@langchain/core/messages";
const response = await llm.invoke([new HumanMessage("Привет")]);
console.log(response.content);
```
## С шаблоном промпта
```ts
import { ChatPromptTemplate } from "@langchain/core/prompts";
import { StringOutputParser } from "@langchain/core/output_parsers";
const prompt = ChatPromptTemplate.fromMessages([
["system", "Вы — лаконичный ассистент."],
["user", "{question}"],
]);
const chain = prompt.pipe(llm).pipe(new StringOutputParser());
const result = await chain.invoke({ question: "Что такое pi?" });
console.log(result);
```
## Стриминг
```ts
const stream = await llm.stream([new HumanMessage("Расскажи историю")]);
for await (const chunk of stream) {
process.stdout.write(chunk.content as string);
}
```
## С tool calling
```ts
import { tool } from "@langchain/core/tools";
import { z } from "zod";
const weatherTool = tool(
async ({ city }) => ({ city, temp: 5, condition: "sunny" }),
{
name: "get_weather",
description: "Текущая погода в городе",
schema: z.object({ city: z.string() }),
},
);
const llmWithTools = llm.bindTools([weatherTool]);
const response = await llmWithTools.invoke([new HumanMessage("Погода в Москве?")]);
console.log(response.tool_calls);
```
## Что дальше
* [Каталог моделей](/models) — все доступные `provider/model`.
* [POST /v1/chat/completions](/docs/api/chat-completions) — полная схема параметров.
---
# LangChain (Python) (/docs/frameworks/langchain-python)
> Подключить Hubris к LangChain через ChatOpenAI.
LangChain работает с Hubris через стандартный класс `ChatOpenAI` из пакета `langchain-openai` — задаёте `base_url` на наш endpoint.
## Установка
```bash
pip install langchain langchain-openai
```
## Подключение
```python
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(
model="anthropic/claude-haiku-4.5",
base_url="https://api.hubris.pw/v1",
api_key="sk-gw-...",
)
```
## Базовый вызов
```python
from langchain_core.messages import HumanMessage
response = llm.invoke([HumanMessage(content="Привет")])
print(response.content)
```
## С системным промптом и chain
```python
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
prompt = ChatPromptTemplate.from_messages([
("system", "Вы — лаконичный ассистент."),
("user", "{question}"),
])
chain = prompt | llm | StrOutputParser()
result = chain.invoke({"question": "Что такое pi с точностью до 5 знаков?"})
print(result)
```
## Стриминг
```python
for chunk in llm.stream([HumanMessage(content="Расскажи историю")]):
print(chunk.content, end="", flush=True)
```
## С tool calling
```python
from langchain_core.tools import tool
@tool
def get_weather(city: str) -> dict:
"""Текущая погода в городе."""
return {"city": city, "temp": 5, "condition": "sunny"}
llm_with_tools = llm.bind_tools([get_weather])
response = llm_with_tools.invoke([HumanMessage(content="Какая погода в Москве?")])
print(response.tool_calls)
```
## С агентами (LangGraph)
`ChatOpenAI` напрямую совместим с `langgraph` агентами — указываете тот же клиент в `create_react_agent`:
```python
from langgraph.prebuilt import create_react_agent
agent = create_react_agent(llm, tools=[get_weather])
result = agent.invoke({"messages": [HumanMessage(content="Какая погода в Москве?")]})
```
## Что дальше
* [Каталог моделей](/models) — все доступные `provider/model`.
* [POST /v1/chat/completions](/docs/api/chat-completions) — полная схема.
---
# OpenAI SDK для Node.js (/docs/frameworks/openai-node)
> Использовать официальный openai пакет с Hubris — поменяйте baseURL и apiKey.
Официальный пакет `openai` от OpenAI работает с Hubris как прямая замена — меняете два параметра при инициализации клиента, остальной код не трогаете.
## Установка
```bash
npm install openai
# или
pnpm add openai
# или
yarn add openai
```
## Подключение
```ts
import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://api.hubris.pw/v1",
apiKey: process.env.HUBRIS_API_KEY,
});
```
## Базовый запрос
```ts
const response = await client.chat.completions.create({
model: "anthropic/claude-haiku-4.5",
messages: [{ role: "user", content: "Привет" }],
});
console.log(response.choices[0].message.content);
```
## Стриминг
```ts
const stream = await client.chat.completions.create({
model: "anthropic/claude-haiku-4.5",
messages: [{ role: "user", content: "Расскажи историю" }],
stream: true,
});
for await (const chunk of stream) {
const delta = chunk.choices[0]?.delta?.content;
if (delta) process.stdout.write(delta);
}
```
## Tool calling
```ts
const tools = [{
type: "function" as const,
function: {
name: "get_weather",
description: "Текущая погода в городе",
parameters: {
type: "object",
properties: { city: { type: "string" } },
required: ["city"],
},
},
}];
const response = await client.chat.completions.create({
model: "anthropic/claude-haiku-4.5",
messages: [{ role: "user", content: "Какая погода в Москве?" }],
tools,
});
console.log(response.choices[0].message.tool_calls);
```
## Что не работает
`client.embeddings.create()`, `client.images.generate()`, `client.audio.*`, `client.files.*`, `client.batches.*` — соответствующих эндпоинтов нет, появятся в ближайших обновлениях.
## Что дальше
* [Каталог моделей](/models) — все идентификаторы.
* [POST /v1/chat/completions](/docs/api/chat-completions) — полная схема.
---
# OpenAI SDK для Python (/docs/frameworks/openai-python)
> Использовать официальный openai пакет с Hubris — поменяйте base_url и api_key.
Официальный пакет `openai` от OpenAI работает с Hubris как прямая замена: меняете два параметра при инициализации клиента, остальной код не трогаете.
## Установка
```bash
pip install openai
```
Минимальная версия — 1.0+ (новый API клиента). Старый stub-стиль (`openai.api_key = ...`) не поддерживаем.
## Подключение
```python
from openai import OpenAI
client = OpenAI(
base_url="https://api.hubris.pw/v1",
api_key="sk-gw-...",
)
```
Лучше через переменную окружения:
```python
import os
from openai import OpenAI
client = OpenAI(
base_url="https://api.hubris.pw/v1",
api_key=os.environ["HUBRIS_API_KEY"],
)
```
## Базовый запрос
```python
response = client.chat.completions.create(
model="anthropic/claude-haiku-4.5",
messages=[{"role": "user", "content": "Привет"}],
)
print(response.choices[0].message.content)
```
## Стриминг
```python
stream = client.chat.completions.create(
model="anthropic/claude-haiku-4.5",
messages=[{"role": "user", "content": "Расскажи историю"}],
stream=True,
)
for chunk in stream:
delta = chunk.choices[0].delta.content
if delta:
print(delta, end="", flush=True)
```
## Tool calling
Обычный OpenAI-API:
```python
tools = [{
"type": "function",
"function": {
"name": "get_weather",
"description": "Текущая погода в городе",
"parameters": {
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"],
},
},
}]
response = client.chat.completions.create(
model="anthropic/claude-haiku-4.5",
messages=[{"role": "user", "content": "Какая погода в Москве?"}],
tools=tools,
)
print(response.choices[0].message.tool_calls)
```
## Async-клиент
```python
from openai import AsyncOpenAI
client = AsyncOpenAI(
base_url="https://api.hubris.pw/v1",
api_key="sk-gw-...",
)
async def main():
response = await client.chat.completions.create(
model="anthropic/claude-haiku-4.5",
messages=[{"role": "user", "content": "Привет"}],
)
print(response.choices[0].message.content)
```
## Что не работает
`client.embeddings.create()`, `client.images.generate()`, `client.audio.*`, `client.files.*`, `client.batches.*` — соответствующих эндпоинтов в Hubris сейчас нет, эти вызовы упадут с 404. Появятся в ближайших обновлениях.
## Что дальше
* [Каталог моделей](/models) — все доступные `provider/model` идентификаторы.
* [POST /v1/chat/completions](/docs/api/chat-completions) — полная схема параметров.
* [Аутентификация](/docs/authentication) — про ключи.
---
# Vercel AI SDK (/docs/frameworks/vercel-ai-sdk)
> Подключить Hubris к Vercel AI SDK через @ai-sdk/openai-compatible.
Vercel AI SDK подключается к Hubris через провайдер `@ai-sdk/openai-compatible` — он принимает любой OpenAI-совместимый endpoint.
## Установка
```bash
npm install ai @ai-sdk/openai-compatible
```
## Подключение
```ts
import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
const hubris = createOpenAICompatible({
name: "hubris",
baseURL: "https://api.hubris.pw/v1",
apiKey: process.env.HUBRIS_API_KEY,
});
```
## generateText (одиночный запрос)
```ts
import { generateText } from "ai";
const { text } = await generateText({
model: hubris.chatModel("anthropic/claude-haiku-4.5"),
prompt: "Привет, как дела?",
});
console.log(text);
```
## streamText (стриминг)
```ts
import { streamText } from "ai";
const { textStream } = await streamText({
model: hubris.chatModel("anthropic/claude-haiku-4.5"),
prompt: "Расскажи короткую историю",
});
for await (const chunk of textStream) {
process.stdout.write(chunk);
}
```
## С tool calling
```ts
import { generateText, tool } from "ai";
import { z } from "zod";
const result = await generateText({
model: hubris.chatModel("anthropic/claude-haiku-4.5"),
prompt: "Какая погода в Москве?",
tools: {
getWeather: tool({
description: "Текущая погода в городе",
parameters: z.object({ city: z.string() }),
execute: async ({ city }) => ({ city, temp: 5, condition: "sunny" }),
}),
},
});
```
## Использование в Next.js Route Handler
```ts
// app/api/chat/route.ts
import { streamText } from "ai";
import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
const hubris = createOpenAICompatible({
name: "hubris",
baseURL: "https://api.hubris.pw/v1",
apiKey: process.env.HUBRIS_API_KEY,
});
export async function POST(req: Request) {
const { messages } = await req.json();
const result = await streamText({
model: hubris.chatModel("anthropic/claude-haiku-4.5"),
messages,
});
return result.toDataStreamResponse();
}
```
## Что дальше
* [Каталог моделей](/models) — все идентификаторы.
* [POST /v1/chat/completions](/docs/api/chat-completions) — полная схема параметров.
---
# Claude Code (/docs/integrations/claude-code)
> Подключение официального CLI-агента Claude Code к Hubris через эндпоинт /v1/messages — поддержка Sonnet, Opus, Haiku, кэширования, vision, статусной строки.
[Claude Code](https://docs.anthropic.com/claude/docs/claude-code) — официальный CLI-агент Anthropic. Запускается в терминале, понимает контекст проекта, умеет редактировать файлы, исполнять команды, читать скриншоты, держит долгую сессию с кэшированием промптов. Поддерживает любой Anthropic-совместимый бэкенд через переменные окружения. Hubris подходит из коробки.
## Что вы получите
* Доступ ко всему семейству Claude (Sonnet 4.5/4.6, Opus 4.5/4.6/4.7, Haiku 4.5) **с оплатой в рублях** и без VPN.
* Нативное `prompt caching` для длинных сессий — экономит до десятков рублей на каждом турне.
* Чтение скриншотов и других изображений (vision).
* Использование инструментов (tool use) для редактирования файлов и запуска команд.
* Опциональная statusline в TUI с балансом, расходом сессии и текущей моделью.
## Требования
* macOS / Linux / WSL / современный терминал на Windows.
* Аккаунт на [hubris.pw](https://hubris.pw/dashboard) и API-ключ формата `sk-gw-…` (создать на [/keys](https://hubris.pw/keys)).
* Положительный баланс — пополнить можно [здесь](https://hubris.pw/billing).
## Установка Claude Code
Подробная инструкция — в [официальной документации](https://docs.anthropic.com/claude/docs/claude-code-quickstart). Самый быстрый путь:
```bash
npm install -g @anthropic-ai/claude-code
```
Альтернатива — однострочный установщик:
```bash
curl -fsSL https://claude.ai/install.sh | bash
```
Убедитесь, что Claude Code запускается:
```bash
claude --version
```
## Подключение к Hubris
Достаточно двух переменных окружения. Поставьте их перед запуском Claude Code:
```bash
export ANTHROPIC_BASE_URL="https://api.hubris.pw"
export ANTHROPIC_AUTH_TOKEN="sk-gw-..."
# опционально — пустая ANTHROPIC_API_KEY гарантирует, что SDK не подставит
# случайно настоящий ключ Anthropic, если он есть в среде:
unset ANTHROPIC_API_KEY
```
Чтобы не вводить руками каждый раз — добавьте в `~/.bashrc` / `~/.zshrc` / `~/.config/fish/config.fish`. Под Windows — Set-`HUBRIS_API_KEY` в профиле PowerShell.
Запустите агента из папки проекта:
```bash
cd ~/projects/my-project
claude
```
В первом турне Claude Code сходит к Hubris вместо `api.anthropic.com`, выберет указанную модель и спишет стоимость с вашего баланса в рублях.
## Выбор модели
В рабочей сессии переключайте модель командой `/model`. Hubris принимает и короткие Anthropic-имена (`claude-sonnet-4-5`, `claude-haiku-4-5`), и каноничные (`anthropic/claude-sonnet-4.6`). Полный список — в [каталоге](https://hubris.pw/models?provider=anthropic).
Чтобы зафиксировать модель по умолчанию, передайте её при запуске:
```bash
claude --model claude-sonnet-4-5
```
Или укажите в `~/.claude/settings.json`:
```json
{
"defaultModel": "claude-sonnet-4-5"
}
```
### Какая модель когда подходит
| Модель | Когда брать |
| ----------------------------------------- | ----------------------------------------------------------------------------- |
| `claude-haiku-4-5` | быстрые ответы, поиск по коду, простые правки, дёшево. |
| `claude-sonnet-4-5` / `claude-sonnet-4-6` | основной рабочий вариант: качественные правки, рефакторинги, длинные сессии. |
| `claude-opus-4-7` | сложные архитектурные задачи, миграции, разбор багов в больших кодовых базах. |
## Statusline с балансом
В нижней строке TUI Claude Code можно выводить текущий баланс Hubris, расход сессии и активную модель. Скрипт обновляется на каждом турне и не блокирует ответ модели.
### Установка (Linux / macOS / WSL)
```bash
curl -fsSL https://hubris.pw/scripts/claude-statusline.sh \
-o ~/.claude/hubris-statusline.sh
chmod +x ~/.claude/hubris-statusline.sh
```
Добавьте в `~/.claude/settings.json`:
```json
{
"statusLine": {
"type": "command",
"command": "~/.claude/hubris-statusline.sh"
}
}
```
### Установка (Windows / кросс-платформа на Node.js)
```powershell
Invoke-WebRequest -Uri https://hubris.pw/scripts/claude-statusline.mjs `
-OutFile $env:USERPROFILE\.claude\hubris-statusline.mjs
```
В `~/.claude/settings.json`:
```json
{
"statusLine": {
"type": "command",
"command": "node ~/.claude/hubris-statusline.mjs"
}
}
```
Скрипт читает `ANTHROPIC_AUTH_TOKEN` из среды (тот же ключ, что использует CC) и обращается к [/v1/usage](/docs/api/usage) за расходом и балансом. Запросы на статус — бесплатны (не списываются с баланса).
Пример вывода:
```
hubris │ Sonnet 4.5 │ session 0,42 ₽ · today 12,18 ₽ · balance 1 287 ₽
```
## Прочая конфигурация
| Переменная окружения | Что делает |
| ---------------------------- | ------------------------------------------------------------------------------------------------------------ |
| `ANTHROPIC_BASE_URL` | URL шлюза. Для Hubris — `https://api.hubris.pw`. |
| `ANTHROPIC_AUTH_TOKEN` | API-ключ `sk-gw-…`. Используется как `Authorization: Bearer`. |
| `ANTHROPIC_MODEL` | модель по умолчанию (если не задана в settings.json). |
| `ANTHROPIC_SMALL_FAST_MODEL` | модель для лёгких подзадач (резюмирования, выбор имени). По умолчанию Haiku — оставляйте `claude-haiku-4-5`. |
## Дневные лимиты
Если на ключе включён дневной лимит трат (можно поставить на [/keys](https://hubris.pw/keys)), Hubris вернёт `429 rate_limit_error`. Claude Code отобразит ошибку в TUI и предложит остановить сессию. Лимит обнуляется ежедневно в 00:00 UTC.
## Кэширование промптов
Claude Code сам управляет `cache_control` для длинных системных промптов и расшаренного контекста. Hubris передаёт это поле в Anthropic без изменений и **списывает по фактической стоимости** с учётом cache hit (0,1× от обычной цены) или cache write (1,25×). Подробности — в [API-референсе /v1/messages](/docs/api/messages).
## Часто задаваемые вопросы
**Можно ли использовать через настоящий ключ Anthropic параллельно?**
Да: переменные `ANTHROPIC_*` локальные. Достаточно открыть второй терминал без них — Claude Code пойдёт напрямую.
**Где увидеть детальный расход?**
Все запросы появляются на [странице Использование](https://hubris.pw/usage) с разбивкой по моделям и времени. Через API — [GET /v1/usage](/docs/api/usage).
**Поддерживается ли Privacy Mode для маскирования PII?**
В первой версии — нет. Если нужно маскирование, делайте запросы через [/v1/chat/completions](/docs/api/chat-completions) с заголовком `X-Hubris-Privacy-Mask`. Поддержка на `/v1/messages` появится позже.
**Что делать, если CC просит модель, которой нет в каталоге?**
Hubris вернёт `404 not_found_error` с именем недоступной модели. Сверьтесь со [списком активных моделей](https://hubris.pw/models?provider=anthropic) и переключитесь командой `/model` на одну из них.
## Что дальше
* [POST /v1/messages](/docs/api/messages) — полный API-референс по эндпоинту.
* [Каталог моделей](https://hubris.pw/models?provider=anthropic) — активные Claude-модели и цены в рублях.
* [Использование](https://hubris.pw/usage) — детальный расход по запросам.
* [Биллинг](https://hubris.pw/billing) — пополнение баланса через СБП.
---
# Cline (VS Code / Cursor) (/docs/integrations/cline)
> Подключение AI-ассистента Cline к Hubris — кодит, читает и пишет файлы, запускает терминал из вашего редактора.
[Cline](https://cline.bot) — расширение для VS Code и Cursor, которое превращает редактор в полноценного AI-агента: умеет читать и редактировать файлы, запускать команды в терминале, работать с git, выполнять многошаговые задачи. Подключается к любому OpenAI-совместимому провайдеру — в том числе к Hubris.
## Требования
* VS Code 1.85+ или Cursor
* Установленный аккаунт на [hubris.pw](https://hubris.pw/dashboard) и API-ключ
## Установка Cline
1. Откройте панель расширений в VS Code (`Ctrl+Shift+X`).
2. Найдите **Cline** (издатель — saoudrizwan).
3. Нажмите **Install**.
4. После установки в боковой панели появится иконка Cline.
## Подключение к Hubris
В панели Cline нажмите на селектор модели под полем ввода. Откроется список API-провайдеров.
1. Выберите **OpenAI Compatible**.
2. Заполните поля:
| Поле | Значение |
| ------------ | ------------------------------------------------------ |
| **Base URL** | `https://api.hubris.pw/v1` |
| **API Key** | ваш ключ из [личного кабинета](https://hubris.pw/keys) |
| **Model ID** | например `anthropic/claude-haiku-4.5` |
3. Сохраните настройки. Cline готов к работе — можно сразу писать задачу.
## Рекомендуемые модели
Cline — это многошаговый агент: длинный контекст, много вызовов инструментов, чтение/запись файлов. Под такой профиль подходят:
| Назначение | Модель |
| ----------------------------------------- | --------------------------------------------------- |
| Сложные многошаговые задачи, рефакторинги | Claude Opus (`anthropic/claude-opus-*` из каталога) |
| Основная рабочая лошадка для кодинга | Claude Sonnet (`anthropic/claude-sonnet-*`) |
| Быстрые правки, экономичный режим | `anthropic/claude-haiku-4.5` |
| Универсальная альтернатива | `openai/gpt-4o-mini` |
Полный актуальный список — в [каталоге моделей](/models). Подойдёт любая модель с поддержкой tool calling (фильтр в каталоге).
## Стоимость и контроль расходов
Cline — агент, а значит делает много вызовов на одну задачу: чтение файлов, изменения, проверка результата. Расход токенов на одну сессию может оказаться выше, чем ожидаешь.
Что советуем:
* **Дневной лимит на ключ** — задайте лимит расхода в [настройках ключа](/keys). При превышении ответы вернутся с `429 daily_limit_exceeded`, баланс не пострадает.
* **Раздельные ключи** — отдельный ключ для каждого проекта или клиента, чтобы видеть расходы в [«Расходах»](/usage) разнесённо.
* **Дешёвая модель для рутины** — Haiku или GPT-4o-mini для типовых правок, переключаться на Opus только когда задача реально сложная.
## Решение проблем
### Cline не подключается, ошибка 401
* Проверьте, что в поле **API Key** именно ключ Hubris (формат `sk-gw-` + hex).
* Если копировали из браузера — убедитесь, что не попали пробелы по краям.
* В случае сомнений — создайте новый ключ в [личном кабинете](/keys).
### `model not found` при попытке генерации
* Проверьте написание Model ID. Формат — `provider/model-name`, например `anthropic/claude-haiku-4.5`.
* Убедитесь, что модель есть в нашем [каталоге](/models) — Hubris проксирует не все модели всех провайдеров.
### Cline работает, но «теряет» инструменты или зацикливается
* Это типичная история со слабыми моделями на сложных tool-сценариях. Попробуйте более сильную модель — Claude Sonnet или Opus.
* Уменьшите размер задачи: дайте Cline более узкую цель за раз.
### Сильно вырос баланс — почему?
* Каждое действие Cline (открыть файл, прочитать, изменить, проверить) — отдельный запрос с собственным `prompt_tokens` и `completion_tokens`. Длинные файлы → большой prompt.
* Откройте [«Расходы»](/usage) и отсортируйте по дате — будут видны все запросы за сессию.
## Что дальше
* [Каталог моделей](/models) — выбрать подходящую модель.
* [Управление API-ключами](/keys) — создать отдельный ключ и задать дневной лимит.
* [Расходы](/usage) — детализация всех запросов и потраченных рублей.
* [Вызов инструментов](/docs/features/tool-calling) — как работают tool calls под капотом.
---
# Dify (/docs/integrations/dify)
> Подключение Hubris к Dify — низкокодовой LLM-платформе для построения чат-приложений, агентов и workflow.
[Dify](https://dify.ai) — open-source платформа для построения LLM-приложений: чат-боты, AI-агенты, RAG-workflow. Поддерживает self-hosted и облачный (`cloud.dify.ai`) режимы. Hubris подключается как провайдер типа OpenAI-API-compatible — модели Hubris становятся доступны во всех приложениях.
## Требования
* Аккаунт на [cloud.dify.ai](https://cloud.dify.ai) **или** self-hosted Dify
* Аккаунт на [hubris.pw](https://hubris.pw/dashboard) и API-ключ
## Подключение
1. В Dify откройте **Settings** → **Model Providers**.
2. Найдите в списке **OpenAI-API-compatible** и нажмите **Add Model**.
3. Заполните поля:
| Поле | Значение |
| ---------------------- | ------------------------------------------------------------------- |
| **Model Type** | `LLM` (для chat-моделей) |
| **Model Name** | ID модели из нашего каталога, например `anthropic/claude-haiku-4.5` |
| **API Key** | ваш ключ из [личного кабинета Hubris](https://hubris.pw/keys) |
| **API endpoint URL** | `https://api.hubris.pw/v1` |
| **Completion mode** | `Chat` |
| **Model context size** | размер контекста модели (см. карточку в [каталоге](/models)) |
| **Maximum chunks** | 1 |
4. Сохраните. Модель появится в списке доступных в Dify.
Чтобы добавить ещё модели — повторите для каждой. Один ключ Hubris работает на все.
## Использование в приложениях
После подключения модель Hubris доступна везде, где Dify предлагает выбор LLM:
* **AI Chatflow** — собранный из блоков чат-агент.
* **LLM-блок в Workflow** — точка вызова модели в произвольном workflow.
* **Agent app** — приложение с инструментами и многошаговыми сценариями.
* **Knowledge / RAG** — встраивание модели в retrieval-цепочки.
В каждом блоке выбираете подключённого провайдера (`OpenAI-API-compatible`) и нужную модель.
## Рекомендуемые модели
| ID | Когда подходит |
| ---------------------------- | ---------------------------------------------------------- |
| `anthropic/claude-haiku-4.5` | быстрые чат-боты, классификация |
| `openai/gpt-4o-mini` | универсальный недорогой выбор, поддержка structured output |
| `google/gemini-2.0-flash` | мультимодальные приложения |
Для агентов и сложных reasoning-задач — Claude Sonnet/Opus из [каталога](/models). Используйте фильтр «tool calling» для агентских сценариев.
## Streaming и Function Calling
Dify работает с обоими по умолчанию — `stream: true` идёт в наш `/v1/chat/completions`, tools прокидываются без дополнительной настройки. Никаких отдельных флагов в Dify включать не надо.
## Embeddings для RAG
Для retrieval-сценариев в Dify нужен отдельный provider типа **Text Embedding**. Добавьте ещё одну запись OpenAI-API-compatible, но с **Model Type** = `Text Embedding`, указав модель-эмбеддер из нашего [каталога](/models). API endpoint и ключ — те же, что и для LLM-провайдера.
## Решение проблем
### Provider добавлен, но Dify не видит модели
* Перепроверьте API endpoint URL — должен быть `https://api.hubris.pw/v1` (с `/v1` в конце, без trailing slash).
* Убедитесь, что Model Name точно совпадает с ID из каталога Hubris.
### `Invalid API key` при тестовом запросе
* API-ключ должен начинаться с `sk-gw-` и не содержать пробелов.
### Модель работает, но контекст «обрывается» / ответы короткие
* Проверьте поле **Model context size** — оно должно соответствовать реальному контексту модели (карточка в каталоге).
* Поле **Maximum chunks** относится к чанкам контекста в RAG — оставьте `1` для обычных чат-сценариев.
### Streaming не работает в Workflow
* В настройках LLM-блока должно быть включено **Stream Mode**. Если выключено — ответ приходит одним куском в конце.
## Что дальше
* [Каталог моделей](/models) — IDs для подключения в Dify.
* [Управление API-ключами](/keys) — отдельный ключ под Dify-приложение.
* [Расходы](/usage) — детализация по запросам.
* [Структурированный вывод](/docs/features/structured-output) — стабильные JSON-ответы в workflow.
---
# Hermes Agent (/docs/integrations/hermes-agent)
> Подключение Hermes Agent от Nous Research — самообучающегося AI-агента с Telegram, памятью и веб-дашбордом — к Hubris.
[Hermes Agent](https://github.com/NousResearch/hermes-agent) — открытый AI-агент от Nous Research. Работает с файлами и терминалом, сам формирует «навыки» из опыта, хранит память между сессиями, поддерживает чат через Telegram. Подключается к любому OpenAI-совместимому провайдеру.
## Требования
* Linux / macOS / WSL2 / Android (Termux)
* Python 3.11+
* Аккаунт на [hubris.pw](https://hubris.pw/dashboard) и API-ключ
## Установка
```bash
curl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh | bash
```
Перезагрузите shell:
```bash
source ~/.bashrc # или source ~/.zshrc
```
## Подключение к Hubris
### Через мастер настройки (рекомендуется)
```bash
hermes setup
```
В меню выберите **Custom Endpoint** и заполните:
| Поле | Значение |
| ------------------ | ------------------------------------------------------ |
| **Base URL** | `https://api.hubris.pw/v1` |
| **API Key** | ваш ключ из [личного кабинета](https://hubris.pw/keys) |
| **Model** | например `anthropic/claude-haiku-4.5` |
| **Context window** | размер контекста модели (см. [каталог](/models)) |
Остальные параметры можно оставить по умолчанию.
### Через файлы конфигурации
`~/.hermes/.env`:
```bash
OPENAI_API_KEY=sk-gw-...
OPENAI_BASE_URL=https://api.hubris.pw/v1
```
`~/.hermes/config.yaml`:
```yaml
model:
default: "anthropic/claude-haiku-4.5"
provider: "custom"
base_url: "https://api.hubris.pw/v1"
```
## Запуск
```bash
hermes
```
Откроется интерактивный терминал. Можно сразу писать задачу.
## Веб-дашборд
Hermes поставляется со встроенным веб-интерфейсом для управления сессиями, памятью и навыками:
```bash
hermes dashboard
```
По умолчанию открывается на `http://localhost:9119`. **Не открывайте на публичном IP без защиты** — дашборд хранит API-ключи. Для удалённого доступа используйте SSH-туннель:
```bash
ssh -L 9119:localhost:9119 user@ваш-сервер
```
## Подключение Telegram-бота
Hermes умеет принимать команды через Telegram.
1. У [@BotFather](https://t.me/BotFather) создайте бота (`/newbot`), скопируйте токен.
2. У [@userinfobot](https://t.me/userinfobot) узнайте свой Telegram ID.
3. Добавьте в `~/.hermes/.env`:
```bash
TELEGRAM_BOT_TOKEN=ваш_токен_от_BotFather
TELEGRAM_ALLOWED_USERS=ваш_telegram_id
```
4. Запустите gateway:
```bash
hermes gateway start
```
Теперь бот отвечает в Telegram через выбранную модель Hubris.
## Рекомендуемые модели
Hermes — агент с длинной сессией и многократными вызовами инструментов. Подойдут модели с хорошим tool calling:
| ID | Когда уместна |
| ---------------------------- | ------------------------------------------ |
| `anthropic/claude-haiku-4.5` | быстрые типовые задачи |
| `openai/gpt-4o-mini` | универсальный выбор |
| `google/gemini-2.0-flash` | мультимодальные сценарии, большой контекст |
Для сложных автономных задач (рефакторинг, длинные сессии) — Claude Sonnet/Opus из [каталога](/models). Фильтр «tool calling» поможет отобрать подходящие.
## Решение проблем
### `hermes: command not found` после установки
```bash
source ~/.bashrc # или ~/.zshrc
```
Проверьте, что `~/.local/bin` в PATH: `echo $PATH`.
### `Invalid API key`
* Перепроверьте ключ в `~/.hermes/.env` — без пробелов, начинается с `sk-gw-`.
### Бот в Telegram не отвечает
* Проверьте gateway: `hermes gateway status`.
* Убедитесь, что Telegram ID правильно указан в `TELEGRAM_ALLOWED_USERS`, без пробелов и комментариев в той же строке.
### `No messaging platforms enabled` в логах
* Проверьте, что `TELEGRAM_BOT_TOKEN` указан без inline-комментариев в .env (`TOKEN=xyz # comment` ломает парсинг — комментарий выносите на отдельную строку).
## Что дальше
* [Каталог моделей](/models) — выбор по поддержке tool calling.
* [Управление API-ключами](/keys) — отдельный ключ под Hermes.
* [Расходы](/usage) — детализация сессий.
* [OpenClaw](/docs/integrations/openclaw) — альтернативный multi-messenger агент.
---
# Kilo Code (VS Code / PyCharm) (/docs/integrations/kilo-code)
> Подключение Kilo Code — AI-агента для VS Code и PyCharm — к Hubris.
[Kilo Code](https://kilocode.ai) — AI-агент для разработчиков, доступный и в VS Code, и в PyCharm. Автономно выполняет многошаговые задачи: пишет, рефакторит, отлаживает, добавляет тесты, при необходимости открывает браузер для проверки результата.
## Требования
* VS Code 1.85+ **или** PyCharm 2024.1+
* Аккаунт на [hubris.pw](https://hubris.pw/dashboard) и API-ключ
## Установка и подключение в VS Code
1. Откройте панель расширений (`Ctrl+Shift+X`), найдите **Kilo Code**, нажмите **Install**.
2. В боковой панели появится иконка Kilo — откройте её → **Settings** (шестерёнка) → вкладка **Providers**.
3. Нажмите **Custom provider** → **Connect**.
4. Заполните поля:
| Поле | Значение |
| --------------- | ------------------------------------------------------ |
| **Provider ID** | `hubris` (или любое имя — это локальный идентификатор) |
| **Base URL** | `https://api.hubris.pw/v1` |
| **API Key** | ваш ключ из [личного кабинета](https://hubris.pw/keys) |
5. Сохраните настройки. Kilo автоматически подтянет список моделей.
## Установка и подключение в PyCharm
1. Откройте **Settings** → **Plugins** → **Marketplace**, найдите **Kilo Code**, установите. Перезапустите IDE.
2. В правой панели появится иконка Kilo — откройте, нажмите **Settings**.
3. В разделе **Configuration Profile** создайте новый профиль.
4. В поле **API Provider** выберите **OpenAI Compatible**.
5. Заполните **Base URL** = `https://api.hubris.pw/v1` и **API Key**. Список моделей подтянется автоматически.
## Рекомендуемые модели
Kilo автономно делает много шагов (план → код → тест → правка), поэтому имеет смысл выбирать модели с поддержкой tool calling и нормальным reasoning.
| ID | Когда подходит |
| ---------------------------- | ------------------------------------ |
| `anthropic/claude-haiku-4.5` | быстрая работа над типовыми правками |
| `openai/gpt-4o-mini` | универсальный недорогой выбор |
| `google/gemini-2.0-flash` | мультимодальные сценарии |
Для тяжёлых рефакторингов и многошаговых задач — берите Claude Opus/Sonnet из [каталога](/models). Фильтр «tool calling» поможет отсеять модели, не поддерживающие агентский сценарий.
## Контроль расходов
* Заведите [отдельный ключ](/keys) под Kilo, поставьте суточный лимит — на случай если автономный агент уйдёт «копать» дольше ожидаемого.
* Для рутины используйте недорогую модель, на тяжёлые задачи переключайте Opus.
* Расход по сессиям виден в [«Расходах»](/usage) — фильтруйте по ключу.
## Решение проблем
### Список моделей пустой / Kilo не подтягивает каталог
* Это обычно про невалидный API-ключ или неправильный Base URL. Перепроверьте оба.
* В VS Code иногда нужно перезагрузить окно: `Ctrl+Shift+P` → **Developer: Reload Window**.
* В PyCharm — закройте и заново откройте панель Kilo.
### 401 при первом запросе
* Ключ должен быть в формате `sk-gw-` + hex, без пробелов и переводов строк.
### Kilo не справляется с задачей / зацикливается
* Слабая модель плохо ведёт себя как агент. Попробуйте более сильную из каталога с явной поддержкой tool calling.
* Уменьшите размер задачи: дайте Kilo более узкую цель.
## Что дальше
* [Каталог моделей](/models) — выбор по поддержке tool calling.
* [Управление API-ключами](/keys) — отдельный ключ под Kilo с дневным лимитом.
* [Расходы](/usage) — детализация сессий.
* [Cline](/docs/integrations/cline), [Roo Code](/docs/integrations/roo-code) — альтернативные VS Code агенты.
---
# MCP-сервер (/docs/integrations/mcp)
> Подключите Hubris к AI-агентам через Model Context Protocol — без хардкода API.
> **Beta.** Фича стабильна, но набор инструментов и формат ответов могут уточняться по мере фидбэка.
Model Context Protocol — открытый стандарт от Anthropic. Через MCP агенты (Claude Desktop, Claude Code, Cline, Cursor) получают доступ к внешним сервисам единообразным способом: каталог моделей, баланс, отправка запросов в LLM — всё через одно подключение.
Hubris-MCP даёт агенту:
* **Каталог моделей** с ценой в рублях, фильтрами по capability, цене и длине контекста.
* **Баланс аккаунта** — чтобы агент знал, сколько ему доступно.
* **Запросы к LLM** через `chat_complete` (полный паритет с `/v1/chat/completions`).
* **Готовый prompt** `compare-models` для сравнения моделей под конкретную задачу.
## Что такое MCP
MCP позволяет AI-агентам вызывать инструменты и читать ресурсы внешних сервисов без необходимости хранить API-ключ напрямую в промпте или прописывать кастомные интеграции. Агент узнаёт о доступных инструментах автоматически при подключении к серверу.
Hubris реализует MCP через **Streamable HTTP transport** — единственный URL без дополнительной инфраструктуры.
## Требования
* Аккаунт на [hubris.pw](https://hubris.pw/dashboard) и API-ключ формата `sk-gw-…` (создать на [/keys](https://hubris.pw/keys)).
* Положительный баланс — пополнить можно [здесь](https://hubris.pw/billing).
* MCP-клиент: Claude Desktop, Claude Code, Cline, Cursor, любой другой совместимый.
* Для Claude Desktop дополнительно — установленный Node.js (нужен `npx` для запуска шима `mcp-remote`).
## Подключение
### Claude Desktop
> Claude Desktop пока не умеет напрямую подключаться к удалённым HTTP-MCP-серверам — он работает только со stdio-серверами. Для подключения к Hubris нужен локальный шим [`mcp-remote`](https://github.com/geelen/mcp-remote): он стартует как stdio-процесс и проксирует все вызовы на `https://api.hubris.pw/mcp`.
> **Не используйте окно «Add custom connector (BETA)»** в claude.ai / Claude Desktop. Эта форма рассчитана на сервера с OAuth 2.1 (у Hubris пока Bearer-авторизация), и для произвольных HTTP-MCP-серверов известно падает с ошибкой `Couldn't reach the MCP server. … ofid_…` ещё до отправки запроса нам — это [баг на стороне Anthropic](https://github.com/anthropics/claude-ai-mcp/issues). Подключайтесь только через JSON-конфиг ниже.
Откройте файл конфигурации:
* **macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`
* **Windows:** `%APPDATA%\Claude\claude_desktop_config.json`
* **Linux:** `~/.config/Claude/claude_desktop_config.json`
Добавьте секцию `mcpServers`:
```json
{
"mcpServers": {
"hubris": {
"command": "npx",
"args": [
"-y",
"mcp-remote",
"https://api.hubris.pw/mcp",
"--transport",
"http-only",
"--header",
"Authorization:${AUTH_TOKEN}"
],
"env": {
"AUTH_TOKEN": "Bearer sk-gw-..."
}
}
}
}
```
Подставьте свой ключ в `AUTH_TOKEN` (формат: `Bearer sk-gw-…`). Заголовок в `--header` передаётся как `Authorization:${AUTH_TOKEN}` без пробела вокруг двоеточия — это рекомендация `mcp-remote` из-за особенностей экранирования в некоторых клиентах.
Перезапустите Claude Desktop. В чате появится индикатор подключённого MCP-сервера и список доступных инструментов.
### Claude Code
```bash
claude mcp add --transport http hubris https://api.hubris.pw/mcp \
--header "Authorization: Bearer sk-gw-..."
```
Проверьте, что сервер добавлен:
```bash
claude mcp list
```
### Cline / Cursor / другие клиенты
Любой MCP-клиент с поддержкой Streamable HTTP transport подключается тем же URL и Bearer-ключом. Параметры те же:
| Поле | Значение |
| --------------------- | ------------------------------- |
| Transport | Streamable HTTP |
| URL | `https://api.hubris.pw/mcp` |
| Заголовок авторизации | `Authorization: Bearer sk-gw-…` |
Подробности — в документации вашего клиента.
## Инструменты
| Инструмент | Что делает |
| -------------------- | ----------------------------------------------------------------------------------------------------------- |
| `models_list` | Полный каталог активных моделей с курсорной пагинацией. |
| `models_search` | Поиск по capability (vision / reasoning / tools / web\_search / audio / image\_gen), цене, длине контекста. |
| `models_get_pricing` | Тариф конкретной модели в рублях. |
| `balance_get` | Текущий баланс аккаунта. |
| `chat_complete` | LLM-запрос к выбранной модели. Полный паритет с `/v1/chat/completions`. |
## Ресурсы
| URI | Содержимое |
| -------------------------- | -------------------------------------- |
| `hubris://catalog/models` | Каталог моделей одним JSON-документом. |
| `hubris://docs/quickstart` | Главная страница быстрого старта. |
## Безопасность
Bearer-ключ открывает доступ к балансу аккаунта. Любой, кто получит ключ, сможет отправлять запросы от вашего имени через MCP. Рекомендуем:
* Создать **отдельный ключ** для MCP в [/keys](https://hubris.pw/keys) — это упростит отзыв без затрагивания других интеграций.
* При компрометации — немедленно отозвать ключ в дашборде.
* Не публиковать конфиг MCP с ключом в открытых репозиториях.
OAuth-авторизация (без копирования ключа в конфиг) — на дорожной карте.
## Что дальше
* [Каталог моделей](https://hubris.pw/models) — все активные модели и цены в рублях.
* [POST /v1/chat/completions](/docs/api/chat-completions) — API-референс LLM-запросов.
* [/keys](https://hubris.pw/keys) — управление API-ключами.
* [Биллинг](https://hubris.pw/billing) — пополнение баланса через СБП.
---
# n8n (/docs/integrations/n8n)
> Подключение Hubris к n8n — низкокодовая платформа автоматизации, в которой можно собирать workflow с участием LLM.
[n8n](https://n8n.io) — низкокодовая платформа автоматизации с тысячью готовых интеграций и поддержкой собственных нод. В workflow можно дёргать LLM — обрабатывать письма, классифицировать обращения, переводить, суммаризировать. Hubris подключается как обычный OpenAI-совместимый провайдер.
## Требования
* n8n self-hosted или n8n Cloud
* Аккаунт на [hubris.pw](https://hubris.pw/dashboard) и API-ключ
## Создание Credentials
В n8n настройка апстрима живёт не в самой ноде, а в общих Credentials — один раз создал, потом используешь во всех нодах.
1. Откройте раздел **Credentials** (боковое меню слева).
2. Нажмите **Add Credential** → найдите **OpenAI API**.
3. Заполните поля:
| Поле | Значение |
| ------------------- | ---------------------------------------------------------------------------------------- |
| **API Key** | ваш ключ Hubris (формат `sk-gw-...`) — взять в [личном кабинете](https://hubris.pw/keys) |
| **Base URL** | `https://api.hubris.pw/v1` |
| **Organization ID** | оставьте пустым |
4. Дайте credential осмысленное имя, например `Hubris`. Сохраните.
## Использование в workflow
Дальше credential `Hubris` доступен во всех нодах, которые умеют работать с OpenAI-совместимым API.
### OpenAI Chat Model (для AI Agent / Chains)
В AI-нодах (LangChain-агенты, цепочки):
1. Добавьте ноду **OpenAI Chat Model**.
2. В поле **Credential to connect with** выберите свой `Hubris`.
3. В поле **Model** введите идентификатор модели из нашего [каталога](/models) — например `anthropic/claude-haiku-4.5`.
Эта нода соединяется с другими AI-нодами (Tool, Memory, Output Parser) — n8n собирает агент из ваших шагов.
### OpenAI (Message a model)
Если нужен простой запрос без агентной обвязки:
1. Добавьте ноду **OpenAI** → действие **Message a model**.
2. **Credential** → `Hubris`.
3. **Model** — введите ID вручную (n8n обычно подгружает список из апстрима; у нас это работает, но проверьте, что нода показывает наши модели).
4. Соберите промпт: можно через **Messages** (multi-turn) или **Simple** (один user-prompt).
### HTTP Request (на всякий случай)
Если хочется полного контроля над запросом или прокинуть кастомный параметр:
```
Method: POST
URL: https://api.hubris.pw/v1/chat/completions
Authentication: Generic Credential Type → Header Auth
Name: Authorization
Value: Bearer sk-gw-...
Body (JSON):
{
"model": "anthropic/claude-haiku-4.5",
"messages": [
{"role": "user", "content": "{{ $json.prompt }}"}
]
}
```
Удобно когда нужен `response_format: { type: "json_schema" }` или другие параметры, которых нет в UI ноды OpenAI.
## Рекомендуемые модели
n8n-workflow обычно — серия однотипных запросов: классифицировать, перевести, суммаризировать. Здесь главное — цена и скорость, не максимальное качество reasoning.
| Модель | Когда уместна |
| ---------------------------- | ---------------------------------------------------------- |
| `anthropic/claude-haiku-4.5` | быстрая классификация, простое извлечение данных |
| `openai/gpt-4o-mini` | универсальный недорогой выбор, поддержка structured output |
| `google/gemini-2.0-flash` | мультимодальные сценарии (картинки + текст) |
Полный список с фильтрами по цене и возможностям — в [каталоге](/models).
## Тяжёлые / агентные сценарии
Если workflow строит долгого AI-агента с многошаговыми инструментами — типа «найди в письме компанию → сходи в CRM → собери ответ» — модели с поддержкой tool calling показывают себя гораздо лучше. Возьмите Claude Sonnet/Opus или GPT-4o из каталога, и не забудьте включить **tool calling** в настройках ноды.
См. также наш гид по [вызову инструментов](/docs/features/tool-calling) — там разобран жизненный цикл tool-сессии.
## Решение проблем
### Список моделей в OpenAI-ноде пустой
* Это значит, нода не смогла дёрнуть `GET /v1/models` с вашим credential. Проверьте Base URL (`https://api.hubris.pw/v1`, обязательно с `/v1` в конце) и валидность ключа.
* Можно временно ввести Model ID вручную в режиме **Manual** — работает даже без подгрузки списка.
### 401 при первом запросе
* API-ключ не прошёл. Перепроверьте, что начинается с `sk-gw-` и не содержит пробелов.
### 402 / `insufficient_balance`
* Закончился баланс. Пополните в [личном кабинете](/billing).
### 429 / `daily_limit_exceeded`
* Сработал суточный лимит ключа. Можно увеличить в [настройках ключа](/keys) или дождаться сброса (24 часа от первого запроса дня).
### Workflow «зависает» на AI-ноде
* LLM-запросы могут идти до нескольких минут (длинный контекст, reasoning-модели). В настройках ноды поднимите **Timeout** (по умолчанию 5 минут).
## Что дальше
* [Каталог моделей](/models) — какие именно ID использовать в нодах.
* [Управление API-ключами](/keys) — отдельный ключ под n8n с лимитом.
* [Расходы](/usage) — посмотреть, во сколько обошёлся прогон workflow.
* [Структурированный вывод](/docs/features/structured-output) — стабильные JSON-ответы для дальнейших шагов workflow.
---
# OAuth 2.0 + PKCE (/docs/integrations/oauth)
> Стандартный OAuth flow для подключения сторонних приложений и MCP-клиентов (Claude Desktop, Cursor, Cline) без копирования root-ключа.
Hubris реализует **OAuth 2.0 Authorization Code + PKCE** (RFC 6749 + 7636 + 9700 BCP) для авторизации сторонних приложений. Это даёт два преимущества:
1. **Sandboxed-доступ**: токен выдаётся под выбранные scopes и опциональный дневной лимит. Если приложение попросит чат — оно не сможет читать ваш баланс.
2. **MCP-connector совместимость**: Claude Desktop, Claude.ai, Cursor и любой MCP-клиент с native «Add custom connector» UI проходят discovery + DCR + авторизацию автоматически, без `mcp-remote` шима.
## Discovery (RFC 8414 + RFC 9728)
Клиент начинает с одного из:
```http
GET https://api.hubris.pw/.well-known/oauth-protected-resource
GET https://hubris.pw/.well-known/oauth-authorization-server
```
Второй — основной, содержит все endpoints. Пример ответа:
```json
{
"issuer": "https://hubris.pw",
"authorization_endpoint": "https://hubris.pw/oauth/authorize",
"token_endpoint": "https://hubris.pw/oauth/token",
"registration_endpoint": "https://hubris.pw/oauth/clients",
"revocation_endpoint": "https://hubris.pw/oauth/revoke",
"scopes_supported": ["chat:write", "models:read", "balance:read"],
"response_types_supported": ["code"],
"grant_types_supported": ["authorization_code", "refresh_token"],
"code_challenge_methods_supported": ["S256"],
"token_endpoint_auth_methods_supported": ["none"]
}
```
`token_endpoint_auth_methods_supported: ["none"]` означает что мы поддерживаем только public clients (PKCE-only, без client\_secret).
## Scopes
| Scope | Доступ |
| -------------- | ------------------------------------------------------------------------------ |
| `chat:write` | `/v1/chat/completions`, `/v1/responses`, `/v1/messages`, `/mcp` chat\_complete |
| `models:read` | `/v1/models`, `/mcp` models\_list+search+get\_pricing |
| `balance:read` | чтение баланса (`/api/internal/me`, `/mcp` balance\_get) |
Приложение запрашивает подмножество в `?scope=...` (space-separated). Юзер видит и подтверждает на consent-странице.
## Токены
* **Access token**: формат `hbr-at-<32 hex>`. TTL **1 час**. Идёт как `Authorization: Bearer ...` на `/v1/*` и `/mcp`.
* **Refresh token**: формат `hbr-rt-<48 hex>`. TTL **90 дней**. Принимается **только** на `POST /oauth/token`.
* **Rotation**: каждый refresh выпускает новый refresh + новый access. Старый refresh имеет 30-секундное grace-окно (защита от race-conditions при параллельных запросах). После окна — обнаружение reuse и revoke всей семьи токенов.
## Поток (Python пример)
```python
import requests, hashlib, base64, secrets
# 1. Регистрация client (one-time per app)
reg = requests.post('https://hubris.pw/oauth/clients', json={
'client_name': 'My App',
'redirect_uris': ['http://localhost:8765/callback'],
'scope': 'chat:write models:read',
})
client_id = reg.json()['client_id']
# 2. PKCE
verifier = secrets.token_urlsafe(32)
challenge = base64.urlsafe_b64encode(
hashlib.sha256(verifier.encode()).digest()
).rstrip(b'=').decode()
# 3. Браузерный flow — открыть в браузере
auth_url = (
'https://hubris.pw/oauth/authorize'
f'?client_id={client_id}'
'&redirect_uri=http://localhost:8765/callback'
'&response_type=code'
f'&code_challenge={challenge}&code_challenge_method=S256'
'&scope=chat:write+models:read'
'&state=somestate'
)
# Юзер видит consent, кликает «Разрешить» → редирект на callback?code=...&state=...
# 4. Обмен кода на токены
tok = requests.post('https://hubris.pw/oauth/token', data={
'grant_type': 'authorization_code',
'code': received_code,
'redirect_uri': 'http://localhost:8765/callback',
'client_id': client_id,
'code_verifier': verifier,
}).json()
access_token = tok['access_token']
# 5. Используем
resp = requests.post(
'https://api.hubris.pw/v1/chat/completions',
headers={'Authorization': f'Bearer {access_token}'},
json={
'model': 'openai/gpt-4o-mini',
'messages': [{'role': 'user', 'content': 'привет'}],
},
)
```
## Дневной лимит
Приложение может передать `daily_limit_kopecks=50000` в query к `/oauth/authorize`. Юзер видит запрошенную сумму на consent-странице и может **снизить** перед подтверждением. Если приложение не передало — default **500 ₽/день**.
После выдачи токена лимит работает идентично api\_keys daily-limit'у — 429 при превышении с метаданными в `/usage`.
## Безопасность
Hubris следует RFC 9700 BCP:
* **PKCE S256 обязательно** — `plain` метод не поддерживается.
* **Refresh rotation + reuse detection** — обнаруженный replay старого refresh после grace-окна revoke'ит всю семью токенов.
* **redirect\_uri exact-match** после нормализации (lowercase scheme/host, case-sensitive path) — без prefix-wildcards.
* **Reserved-word check** на client\_name (`hubris`, `official`, `admin`, `support` запрещены как substring) — anti-phishing baseline.
* **DCR rate-limit**: 5 регистраций/IP/час + 100/день глобально.
* **Все токены — sha256-хэши** в БД. Plain text никогда не хранится.
## MCP integration
Совместимость с native «Add custom connector» UI в Claude Desktop / Claude.ai:
1. В клиенте указываешь URL: `https://api.hubris.pw/mcp`
2. Клиент сам идёт по discovery → DCR → /authorize (открывает браузер)
3. Юзер видит consent: «Claude Desktop запрашивает доступ chat:write, models:read, balance:read»
4. После «Разрешить» — обратно в клиент, токены выпущены, инструменты подключены
Legacy `sk-gw-` ключи продолжают работать на `/mcp` без изменений (back-compat).
## Phase B (deferred)
* `/profile/connected-apps` UI — список авторизованных приложений + revoke
* Per-scope deny (сейчас all-or-nothing на consent-странице)
* Step-up auth (re-OTP при выдаче sensitive scopes если auth >7д назад)
* Domain verification для verified-badge при DCR
* `embeddings:write`, `usage:read` scopes
* OAuth для /v1/embeddings + /v1/usage routes
## Ссылки
* RFC 6749 — OAuth 2.0
* RFC 7009 — Token Revocation
* RFC 7591 — Dynamic Client Registration
* RFC 7636 — PKCE
* RFC 8414 — Authorization Server Metadata
* RFC 9700 — OAuth 2.0 Security Best Current Practice
* RFC 9728 — Protected Resource Metadata
* MCP Authorization: [https://modelcontextprotocol.io/specification/draft/basic/authorization](https://modelcontextprotocol.io/specification/draft/basic/authorization)
---
# OpenClaw (/docs/integrations/openclaw)
> Подключение Hubris к OpenClaw — open-source AI-ассистенту для Telegram, WhatsApp, Discord и Slack.
[OpenClaw](https://openclaw-ai.com) — open-source платформа для AI-ассистента в мессенджерах. Поднимает локальный gateway, маршрутизирует сообщения из Telegram, WhatsApp, Discord, Slack к LLM-провайдеру. Hubris подключается как custom provider через OpenAI-совместимый API.
## Требования
* Установленный OpenClaw (см. [инструкцию по установке](https://openclaw-ai.com/en/install))
* Node.js 22+
* Аккаунт на [hubris.pw](https://hubris.pw/dashboard) и API-ключ
## Подключение к Hubris
Конфиг OpenClaw — `~/.openclaw/openclaw.json` (JSON5, поддерживает комментарии и trailing commas).
Откройте файл:
```bash
nano ~/.openclaw/openclaw.json
```
или через встроенный инструмент:
```bash
openclaw configure
```
В секции `models.providers` добавьте провайдер `hubris`:
```json5
{
models: {
mode: "merge",
providers: {
hubris: {
baseUrl: "https://api.hubris.pw/v1",
apiKey: "${HUBRIS_API_KEY}",
api: "openai-completions",
models: [
{ id: "anthropic/claude-haiku-4.5", name: "Claude Haiku" },
{ id: "openai/gpt-4o-mini", name: "GPT-4o mini" },
{ id: "google/gemini-2.0-flash", name: "Gemini Flash" },
],
},
},
},
}
```
Поле `api: "openai-completions"` обязательно — без него OpenClaw не определит тип API.
Ключ положите в `~/.openclaw/.env`:
```
HUBRIS_API_KEY=sk-gw-...
```
## Модель по умолчанию
В секции `agents.defaults` укажите основную модель и опционально fallback'и:
```json5
{
agents: {
defaults: {
model: {
primary: "hubris/anthropic/claude-haiku-4.5",
fallbacks: ["hubris/openai/gpt-4o-mini"],
},
models: {
"hubris/anthropic/claude-haiku-4.5": { alias: "Claude" },
"hubris/openai/gpt-4o-mini": { alias: "GPT" },
"hubris/google/gemini-2.0-flash": { alias: "Gemini" },
},
},
},
}
```
Формат ссылки: `/`, где `` — имя из секции `providers` (у нас `hubris`).
## Применение конфига
Если включён hot-reload (по умолчанию) — изменения подхватятся автоматически. Если нет:
```bash
openclaw gateway restart
```
## Переключение моделей в чате
Прямо в любом канале:
```
/model hubris/openai/gpt-4o-mini
```
Или через CLI:
```bash
openclaw models set hubris/google/gemini-2.0-flash
```
Список доступных:
```bash
openclaw models list
```
## Подключение каналов
В `channels` секции укажите параметры мессенджеров. Пример для Telegram:
```json5
{
channels: {
telegram: {
dmPolicy: "allowlist",
botToken: "<токен бота от @BotFather>",
allowFrom: ["<ваш Telegram ID>"],
groupPolicy: "allowlist",
},
},
plugins: {
entries: {
telegram: { enabled: true },
},
},
}
```
Аналогично для WhatsApp, Discord, Slack — детали в [документации OpenClaw](https://openclaw-ai.com/en/docs).
## Рекомендуемые модели
OpenClaw как чат-агент в мессенджерах хорошо работает на быстрых моделях. Для агентного сценария с инструментами — более крупная модель из каталога.
| ID | Когда уместна |
| ---------------------------- | --------------------------------------------- |
| `anthropic/claude-haiku-4.5` | быстрые типовые ответы |
| `openai/gpt-4o-mini` | универсальный недорогой выбор |
| `google/gemini-2.0-flash` | мультимодальные сценарии (изображения в чате) |
Полный список — в [каталоге моделей](/models).
## Решение проблем
### `'No API provider registered for api: undefined'`
В секции провайдера должно быть поле `api: "openai-completions"` — без него OpenClaw не понимает тип API.
### `Invalid API key` (401)
* Проверьте формат ключа (`sk-gw-` + hex).
* Если используете `${HUBRIS_API_KEY}`, убедитесь, что переменная действительно установлена в `~/.openclaw/.env`.
### Модель не находится
* Проверьте, что ID в `models[]` точно совпадает с одним из [каталога](/models).
* Проверьте, что та же модель есть в `agents.defaults.models` — это allowlist.
* Формат: `hubris/`.
### Gateway не запускается после правки конфига
OpenClaw строго валидирует конфиг:
```bash
openclaw doctor
openclaw doctor --fix
```
### Медленные ответы
* Переключитесь на более быструю модель — Haiku или Flash.
* В настройках канала включите `streamMode: "partial"` для стриминговых ответов.
## Что дальше
* [Каталог моделей](/models) — список ID для конфига.
* [Управление API-ключами](/keys) — отдельный ключ под OpenClaw.
* [Расходы](/usage) — детализация сессий.
* [Hermes Agent](/docs/integrations/hermes-agent) — альтернативный агент с Telegram.
---
# OpenCode (/docs/integrations/opencode)
> Подключение терминального AI-агента OpenCode (sst.dev) к Hubris — Plan Mode, file-context, image drag-and-drop прямо в TUI.
[OpenCode](https://opencode.ai) — open-source AI-агент, работающий прямо в терминале как полноценный TUI. Понимает контекст проекта, помогает писать и править код, держит историю команд. Подключается к любому OpenAI-совместимому провайдеру через `opencode.json` в корне проекта.
## Требования
* macOS / Linux / WSL / Windows + современный терминал
* `curl` и `jq` в PATH
* Аккаунт на [hubris.pw](https://hubris.pw/dashboard) и API-ключ
## Установка
Самый простой способ — curl-инсталлятор:
```bash
curl -fsSL https://opencode.ai/install | bash
```
Альтернативы: `npm i -g opencode-ai`, Homebrew, Chocolatey, Docker-образ. Полный список — в [официальной документации OpenCode](https://opencode.ai/docs/install).
## Подключение к Hubris
OpenCode читает конфиг из `opencode.json` в корне проекта (или `~/.config/opencode/opencode.json` глобально).
Создайте файл со следующим содержимым:
```json
{
"$schema": "https://opencode.ai/config.json",
"provider": {
"hubris": {
"npm": "@ai-sdk/openai-compatible",
"options": {
"baseURL": "https://api.hubris.pw/v1",
"apiKey": "{env:HUBRIS_API_KEY}"
},
"models": {
"anthropic/claude-haiku-4.5": { "name": "Claude Haiku" },
"openai/gpt-4o-mini": { "name": "GPT-4o mini" },
"google/gemini-2.0-flash": { "name": "Gemini Flash" }
}
}
}
}
```
Ключ кладите в переменную окружения (безопаснее, чем в файл):
```bash
export HUBRIS_API_KEY="sk-gw-..."
```
Запустите агента из папки проекта:
```bash
cd /path/to/your/project
opencode
```
## Команды и режимы
| Команда | Что делает |
| ------------------------- | ----------------------------------------------------- |
| `Tab` | переключение Plan Mode ↔ Execute Mode |
| `@filename` | подключить файл в контекст текущего запроса |
| drag-and-drop изображения | передать картинку как вход для мультимодальной модели |
| `/init` | сгенерировать стартовый план проекта |
| `/models` | переключить активную модель |
| `/undo` / `/redo` | откатить или повторить последнее изменение |
| `/share` | поделиться сессией через ссылку |
## Рекомендуемые модели
| ID | Когда подходит |
| ---------------------------- | ---------------------------------------------------- |
| `anthropic/claude-haiku-4.5` | быстрая работа над типовыми задачами |
| `openai/gpt-4o-mini` | универсальный выбор, недорого |
| `google/gemini-2.0-flash` | мультимодальные сценарии (drag-and-drop изображений) |
Для сложных рефакторингов и архитектурных задач — Claude Sonnet или Opus из [каталога](/models). С тяжёлыми моделями стоит работать в Plan Mode, чтобы проверить план перед исполнением.
## Контроль расходов
* Plan Mode (через `Tab`) — модель только описывает, что собирается делать, не исполняет. Хороший способ оценить сложность задачи и стоимость до запуска.
* Отдельный API-ключ для OpenCode с дневным лимитом в [настройках](/keys).
* Расход за сессию виден в [«Расходах»](/usage).
## Решение проблем
### `Invalid API key` при первом запуске
* Перепроверьте, что переменная `HUBRIS_API_KEY` экспортирована в текущей shell-сессии: `echo $HUBRIS_API_KEY`.
* Ключ должен начинаться с `sk-gw-`.
### Модели не показываются в `/models`
* Проверьте, что секция `models` в `opencode.json` содержит ID, существующие в нашем [каталоге](/models).
* Перезапустите `opencode` после правки конфига.
### Запрос «висит» / нет ответа
* Тяжёлые модели на сложных задачах могут думать минуты. Если зависло дольше — `Ctrl+C` и попробуйте более быструю модель.
## Что дальше
* [Каталог моделей](/models) — список доступных IDs.
* [Управление API-ключами](/keys) — отдельный ключ под OpenCode.
* [Расходы](/usage) — детализация сессий.
* [Cline](/docs/integrations/cline), [Roo Code](/docs/integrations/roo-code) — агенты для VS Code.
---
# Qwen Code CLI (/docs/integrations/qwen-code)
> Подключение Qwen Code — терминального AI-агента от Alibaba (форк gemini-cli) — к Hubris.
[Qwen Code](https://github.com/QwenLM/qwen-code) — терминальный AI-агент от Alibaba, форк `gemini-cli` от Google. Несмотря на название, поддерживает любые OpenAI-совместимые провайдеры — не только модели семейства Qwen. Минималистичный TUI, удобен для CLI-сценариев и автоматизации.
## Требования
* macOS / Linux / WSL / Windows
* Node.js 20+
* Аккаунт на [hubris.pw](https://hubris.pw/dashboard) и API-ключ
## Установка
```bash
npm install -g @qwen-code/qwen-code
```
После установки команда `qwen` должна быть доступна в PATH:
```bash
qwen --version
```
## Подключение к Hubris
Конфиг живёт в `~/.qwen/settings.json`. Создайте файл или дополните существующий:
```json
{
"selectedAuthType": "openai",
"openai": {
"baseUrl": "https://api.hubris.pw/v1",
"apiKey": "sk-gw-...",
"models": [
{
"id": "anthropic/claude-haiku-4.5",
"displayName": "Claude Haiku",
"contextWindowSize": 200000
},
{
"id": "openai/gpt-4o-mini",
"displayName": "GPT-4o mini",
"contextWindowSize": 128000
},
{
"id": "google/gemini-2.0-flash",
"displayName": "Gemini Flash",
"contextWindowSize": 1000000
}
]
}
}
```
`contextWindowSize` для каждой модели подсмотрите в [каталоге](/models) — это размер контекстного окна, на основе которого Qwen Code решает, когда обрезать историю диалога.
Альтернатива — переменные окружения:
```bash
export OPENAI_BASE_URL="https://api.hubris.pw/v1"
export OPENAI_API_KEY="sk-gw-..."
```
Запустите агента в папке проекта:
```bash
cd /path/to/your/project
qwen
```
## Переключение моделей
В рантайме:
```
/model anthropic/claude-haiku-4.5
```
Или при запуске:
```bash
qwen --model openai/gpt-4o-mini
```
## Рекомендуемые модели
| ID | Когда подходит |
| ---------------------------- | ------------------------------------------ |
| `anthropic/claude-haiku-4.5` | быстрая работа |
| `openai/gpt-4o-mini` | универсальный выбор |
| `google/gemini-2.0-flash` | мультимодальные сценарии, большой контекст |
Для сложных задач — Claude Sonnet/Opus или Qwen Coder из [каталога](/models).
## Решение проблем
### `qwen: command not found` после установки
* Проверьте, что глобальный bin-каталог npm в PATH: `npm bin -g` покажет, где. Добавьте этот путь в `$PATH`.
### `Invalid API key`
* Ключ в `settings.json` должен быть в кавычках, начинаться с `sk-gw-`.
* Если используете env vars, перезапустите терминал.
### Список моделей пустой / Qwen Code не видит модели
* Проверьте, что в `settings.json` секция `models` непустая и ID моделей корректные.
* При работе через env vars Qwen Code попытается дёрнуть `GET /v1/models` — убедитесь, что Base URL правильный.
### Длинный контекст вылетает с ошибкой
* Поле `contextWindowSize` в конфиге должно соответствовать реальному контексту модели. Загляните в карточку модели в [каталоге](/models).
## Что дальше
* [Каталог моделей](/models) — IDs и contextWindowSize для всех моделей.
* [Управление API-ключами](/keys) — отдельный ключ под Qwen Code.
* [Расходы](/usage) — детализация сессий.
* [OpenCode](/docs/integrations/opencode) — альтернативный терминальный агент.
---
# Roo Code (/docs/integrations/roo-code)
> Подключение Roo Code — AI-агента для VS Code с режимами Architect / Code / Ask / Debug — к Hubris.
[Roo Code](https://roo-code.com) — расширение для VS Code (форк Cline), которое умеет работать в нескольких режимах: Architect для проектирования, Code для написания и правок, Ask для вопросов, Debug для отладки. Каждому режиму можно назначить свою модель — например, более мощную для Architect и более дешёвую для рутинных правок в Code.
## Требования
* VS Code 1.85+
* Аккаунт на [hubris.pw](https://hubris.pw/dashboard) и API-ключ
## Установка Roo Code
1. Откройте панель расширений в VS Code (`Ctrl+Shift+X`).
2. Найдите **Roo Code** (издатель — RooVeterinaryInc).
3. Нажмите **Install**.
4. После установки в боковой панели появится иконка Roo Code.
## Подключение к Hubris
1. Откройте параметры расширения через иконку шестерёнки в боковой панели Roo Code.
2. Перейдите на вкладку **Providers**.
3. В выпадающем меню провайдера выберите **OpenAI Compatible**.
4. Заполните поля:
| Поле | Значение |
| ------------ | ------------------------------------------------------ |
| **Base URL** | `https://api.hubris.pw/v1` |
| **API Key** | ваш ключ из [личного кабинета](https://hubris.pw/keys) |
| **Model** | например `anthropic/claude-haiku-4.5` |
5. Сохраните настройки (кнопка **Save** в правом верхнем углу).
## Разные модели для разных режимов
Главная фишка Roo Code — разные модели на каждый режим. Это удобно: тяжёлая модель только там, где она реально нужна.
В тех же настройках провайдера у Roo Code есть отдельные слоты для каждого режима. Типовая раскладка:
| Режим | Что делает | Какая модель уместна |
| ------------- | ------------------------------------------------- | ------------------------------------------- |
| **Architect** | проектирует архитектуру, разбивает задачу на шаги | мощная — Claude Opus / Sonnet |
| **Code** | пишет и правит код по чёткому плану | средняя или быстрая — Claude Sonnet, GPT-4o |
| **Ask** | отвечает на вопросы о коде | быстрая и дешёвая — Haiku, GPT-4o-mini |
| **Debug** | анализирует логи, ищет причины ошибок | средняя — Claude Sonnet |
Полный актуальный список моделей — в [каталоге](/models). В коде режим называется `apiModelId`, конкретное значение зависит от модели в нашем каталоге.
## Что обычно ставят
| ID | Когда уместна |
| ---------------------------- | -------------------------------- |
| `anthropic/claude-haiku-4.5` | быстрая работа, недорого |
| `openai/gpt-4o-mini` | универсальный недорогой выбор |
| `google/gemini-2.0-flash` | мультимодальные сценарии, быстро |
Для тяжёлых режимов (Architect, сложный Code) подойдёт более крупная Claude или GPT — посмотрите [каталог](/models) с фильтром по поддержке tool calling.
## Решение проблем
### `Failed to fetch models` или 401 при первом подключении
* Перепроверьте ключ: должен начинаться с `sk-gw-`, без пробелов.
* В Roo Code иногда нужно полностью перезагрузить окно VS Code (`Ctrl+Shift+P` → **Developer: Reload Window**) после первой настройки.
### Модель отвечает «не вижу инструментов» или зацикливается
* Не все модели одинаково хорошо справляются с tool-агентами Roo Code. Попробуйте модель, у которой в [каталоге](/models) явно указана поддержка tool calling.
* Уменьшите контекст: закройте лишние открытые файлы — Roo передаёт активные вкладки в каждый запрос.
### Сессия Architect вдруг очень дорогая
* Architect-режим часто пишет длинные планы — это много `completion_tokens`. Используйте модель попроще для Code и Ask, а Architect зовите только когда правда нужен.
### Дневной лимит ключа сработал в середине задачи
* В [настройках ключа](/keys) можно увеличить лимит. Альтернатива — заведите отдельный ключ под Roo Code и для каждого проекта.
## Что дальше
* [Каталог моделей](/models) — подобрать модели под каждый из четырёх режимов.
* [Управление API-ключами](/keys) — отдельный ключ под Roo Code с лимитом.
* [Расходы](/usage) — детализация запросов.
* [Cline](/docs/integrations/cline) — родитель Roo Code, простой одиночный режим.