# Аутентификация (/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-08-12 — Пять новых моделей в студии видео В [студии видео](/videos) стало на пять моделей больше: **Seedance 2.5** (сцены до 30 секунд), **FLUX.3 Video** (720p и 1080p, ключевые кадры), **Hailuo 3** (2K), **Runway Gen-4.5** и **Grok Imagine Video 1.5** (до 1080p, форматы 3:2 и 2:3). У каждой проверены звук, доступные форматы и оживление картинки первым кадром — композер предлагает только то, что модель действительно умеет. ## 2026-07-27 — Озвучка текста: 15 моделей и готовый аудиофайл в ответе Запустили [`/v1/audio/speech`](/docs/api/speech): отправляете текст — получаете сразу байты аудио (mp3 или pcm), тем же ключом `sk-gw-...`. Пригодится для голосовых ответов бота, аудиоверсий статей и рассылок, реплик персонажей: 15 моделей озвучки — MAI Voice, Aura-2, MiniMax Speech, Kokoro, Orpheus и другие, у каждой свой набор голосов (от двух до девяноста), список — в карточке модели [каталога](/models). Официальный OpenAI SDK работает после смены `base_url`. Тарификация — за символы отправленного текста, поэтому цена известна заранее; оплата в рублях по факту запроса. ## 2026-07-26 — Аудио прямо в чате: модель слушает запись и отвечает голосом В [`/v1/chat/completions`](/docs/api/chat-completions) появился звук в обе стороны. Приложите запись к сообщению content-частью `input_audio` — и модель ответит на вопросы о ней: о чём говорили, каким тоном, что за шум на фоне; это не расшифровка, а рассуждение, которое можно совмещать с tool calling и структурированным выводом. В обратную сторону работает голосовой ответ: `modalities: ["text","audio"]` вместе со `stream: true`, аудио приходит чанками с текстовой расшифровкой. Оплата в рублях по факту, аудио-токены — по своей ставке модели; форматы, коды ошибок и примеры на cURL/Python/TypeScript — в [гиде](/docs/features/audio). ## 2026-07-26 — Распознавание речи: 12 моделей транскрибации Запустили [`/v1/audio/transcriptions`](/docs/api/transcriptions): Whisper, GPT-4o Transcribe, Chirp 3, Voxtral, Nova-3 и другие — 12 моделей превращают аудио в текст тем же ключом `sk-gw-...`. Официальный OpenAI SDK работает после смены `base_url`, файлы до 25 МБ (WAV/MP3/FLAC/M4A/OGG/WebM/AAC). Цены — по фактическим единицам моделей: от 0,17 ₽ за минуту записи, оплата в рублях по факту; весь список — в [каталоге](/models) по фильтру «Транскрибация». ## 2026-07-25 — Контраст интерфейса по WCAG AA Пересобрали палитру под требования доступности: мелкие подписи, реквизиты в футере и текст на фиолетовых кнопках теперь читаются при слабом зрении, ярком солнце и на плохих экранах. Кнопки стали чуть глубже по цвету, третичный текст — светлее. Все публичные страницы проходят WCAG 2.1 AA (контраст ≥ 4.5:1 для обычного текста). ## 2026-07-24 — Кеширование промптов Claude через `/v1/chat/completions` [Кеширование промптов](/docs/features/prompt-caching) для Anthropic-моделей теперь работает и через OpenAI-совместимый [`/v1/chat/completions`](/docs/api/chat-completions) — маркер `cache_control` доходит до модели из любого OpenAI SDK, не только через `/v1/messages`. Закэшированная часть промпта при повторных запросах тарифицируется примерно в десять раз дешевле, а ответ на длинном контексте начинается заметно быстрее. Если вы передавали `cache_control` по нашей документации — с этого момента он действует, ничего менять не нужно. ## 2026-07-24 — Claude Code через Hubris: Claude 5 и любые чат-модели каталога Официальный агент Anthropic — Claude Code, в терминале и как расширение VS Code, — подключается к Hubris за пару минут: две переменные окружения, оплата в рублях. Доступно всё семейство Claude (Fable 5, Sonnet 5, Opus 4.8, Haiku 4.5) с переключателем глубины рассуждений, а через тот же [`/v1/messages`](/docs/api/messages) — и любые чат-модели каталога: GPT, Gemini, DeepSeek и другие. Готовый конфиг, настройка VS Code и нюансы — в [гиде по подключению](/docs/integrations/claude-code). ## 2026-07-11 — Публичный API генерации видео Запустили асинхронный [`/v1/videos`](/docs/api/videos): создаёте задачу, опрашиваете статус, скачиваете mp4 — тем же ключом `sk-gw-...`, оплата в рублях по факту. Готовый ролик хранится 72 часа. Scopes `videos:write` / `videos:read`, поддержка `Idempotency-Key`. Видео-модели видны в каталоге через `GET /v1/models?output_modalities=video` и `GET /v1/videos/models` (цены в рублях). ## 2026-07-08 — Студия генерации видео Запустили [студию видео](/videos): Veo 3.1, Sora 2 Pro, Kling 3.0, Seedance 2.0 и другие — всего 16 моделей в одном окне. Опишите сцену — получите ролик со звуком, до 4K и до 20 секунд; картинку можно «оживить», сделав её первым кадром. Цена видна до запуска, оплата в рублях по факту, готовые ролики сохраняются в «Мои генерации». ## 2026-07-07 — Предпросмотр результата и ID генерации Клик по готовой картинке в [студии](/images) открывает крупный предпросмотр: полный промпт (копируется кликом), стрелки между результатами и заметная кнопка «Скачать». У каждой генерации теперь виден её ID — скопируйте его кликом и приложите к обращению в поддержку, если что-то пошло не так: найдём запись мгновенно. ## 2026-07-07 — Стили с превью и несколько картинок за раз В [студии картинок](/images) — 24 стиля с живыми превью: наведите, чтобы увидеть готовый промпт, кликните — он окажется в строке ввода. Модели GPT Image теперь честно генерируют до 4 изображений за один запрос и учитывают выбранное качество, а Enter в строке ввода сразу запускает генерацию. ## 2026-07-07 — Премиум-модели картинок в студии Добавили в [генератор](/images) флагманские image-модели: **FLUX.2** (Max/Pro/Flex), **Seedream 4.5** (фотореализм), **Recraft V4.1** (дизайн и иллюстрации, + Pro) и **Grok Imagine** — генерируются прямо в UI, оплата в рублях по факту генерации. Для FLUX русский промпт автоматически переводим на английский (модель не понимает кириллицу) — пишете на английском, оставляем как есть. Результат по центру экрана, промпт — под картинкой. ## 2026-07-06 — Новая студия генерации изображений Переделали [генератор картинок](/images) в полноэкранную студию. Выбираете реальную модель (Nano Banana, GPT Image и другие) — соотношение сторон, разрешение или качество подстраиваются под её возможности прямо в строке ввода. Можно приложить картинки-референсы, а все ваши генерации собираются в ленту с промптами. Оплата в рублях через СБП, первая картинка — в пару кликов. ## 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). Минимально 300 ₽, максимально 100 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) > Скормите документацию и весь каталог моделей с ценами в ChatGPT, Claude или любого AI-агента одной ссылкой — и спрашивайте на естественном языке. import { Cards, Card } from 'fumadocs-ui/components/card'; import { FileText, ListTree, Link2, Sparkles, Coins, Braces } from 'lucide-react'; Если вы не хотите читать документацию глазами — отдайте её AI-модели и задавайте вопросы голосом или текстом. Мы публикуем и документацию, и весь каталог моделей с ценами в специальном формате, который понимают ChatGPT, Claude, Gemini и любые другие LLM. Это работает без регистрации и без копирования вручную. Достаточно одной ссылки. ## Самый быстрый способ — за 30 секунд 1. Откройте [ChatGPT](https://chatgpt.com) или [Claude](https://claude.ai). 2. Скопируйте промпт ниже и вставьте в окно чата. 3. Спрашивайте всё, что хотите узнать о Hubris. ``` Прочитай документацию Hubris по ссылке https://hubris.pw/llms-full.txt и каталог моделей с ценами https://hubris.pw/models.txt. Это OpenAI-совместимый LLM-шлюз с оплатой в рублях. Дальше отвечай на мои вопросы. ``` Модель сама загрузит файлы, разберёт их и будет отвечать со ссылками на нужные разделы. Дальше можно спрашивать что угодно: «как мне мигрировать с OpenAI?», «сколько стоит Claude Haiku в рублях?», «как включить стриминг в Python?» — модель ответит, опираясь на актуальную документацию и актуальные цены. Ссылка на каталог здесь не лишняя: в документации цен нет, они живут в отдельном файле — иначе на вопрос про стоимость модель начнёт гадать. ## Цены и каталог моделей Самый частый вопрос к ассистенту — «сколько это стоит в рублях». Для него есть отдельные файлы: в них весь каталог с актуальными ценами, без документации и лишнего текста. } title="models.txt — все модели и цены текстом" href="https://hubris.pw/models.txt" description="Весь каталог с ценами в рублях одним файлом. Каждая строка — модель, цена, контекст и ссылка на карточку." /> } title="models.json — то же машиночитаемо" href="https://hubris.pw/models.json" description="Тот же каталог структурированными данными: единица тарификации, цены, модальности, контекст." /> } title=".md-версия карточки модели" href="https://hubris.pw/models/openai/gpt-5.1.md" description="Добавьте .md к адресу любой модели — получите её характеристики и цену в чистом Markdown." /> Спросить цену у модели можно так: ``` Открой https://hubris.pw/models.txt — это каталог Hubris с ценами в рублях. Подбери мне самую дешёвую модель, которая понимает картинки, и назови её цену за миллион токенов. ``` ## Документация: три формата на выбор } 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." /> ### Когда какой формат использовать | Сценарий | Что давать модели | | ----------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ | | Нужны цены — «сколько стоит», «что дешевле», «подбери под бюджет» | [`models.txt`](https://hubris.pw/models.txt) | | Пишете код, который читает каталог программно | [`models.json`](https://hubris.pw/models.json) | | Нужна одна конкретная модель | адрес модели с суффиксом `.md`, например [`/models/openai/gpt-5.1.md`](https://hubris.pw/models/openai/gpt-5.1.md) | | Хотите задавать вопросы по всей документации сразу | [`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.txt. Мне нужна модель для [задача — например «короткие классификации текста», «генерация длинных статей», «работа с картинками»]. Бюджет — [X ₽ в месяц]. Что посоветуешь? Покажи плюсы-минусы и итоговую цену в рублях за 1М токенов. ``` ### Сравнить цены нескольких моделей ``` Открой https://hubris.pw/models.txt и сравни цены на [модель А], [модель Б] и [модель В]. Посчитай, во сколько мне обойдётся [N] запросов в месяц, если в среднем на запрос уходит [X] токенов на вход и [Y] на выход. Ответ в рублях, таблицей. ``` ### Отладка ошибок ``` Я работаю с 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 Модели и цены в рублях: https://hubris.pw/models.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) — покрывает документацию, каталог, интеграции и разделы сайта. * Любую страницу `/docs/` и любую карточку модели `/models/` можно получить как plain Markdown, добавив `.md` к URL. * Каталог обновляется автоматически: цены в фидах — те же, что на сайте, ручной синхронизации нет. Текстовые эндпоинты отдаются с `Content-Type: text/plain; charset=utf-8`, `models.json` — с `application/json`. Все кэшируются на 5 минут на клиенте и 1 час на CDN. **У `models.json` нет гарантий обратной совместимости.** Схема может поменяться без предупреждения и без версионирования — это написано и в самом ответе, первым полем `notice`. Если вам нужен стабильный контракт, используйте [`GET /v1/models`](/docs/api/models): он OpenAI-совместим и просто так не меняется. ## Часто задаваемые вопросы **Это бесплатно?** Да. Все перечисленные файлы — `llms.txt`, `llms-full.txt`, `models.txt`, `models.json` и `.md`-версии страниц — доступны без регистрации и без API-ключа. **Документация и цены всегда свежие?** Да. Документация пересобирается при каждом обновлении сайта, каталог и цены подтягиваются из той же базы, что показывает сайт, — расхождения между фидом и карточкой модели быть не может. **Можно использовать в своём продукте?** Да, ссылайтесь и цитируйте свободно. Учтите только, что у `models.json` схема без гарантий совместимости — см. предупреждение выше. **Модель путается или галлюцинирует?** Попробуйте более крупную модель или сократите контекст: вместо `llms-full.txt` дайте конкретную страницу через `.md`-суффикс, а для вопросов про цены — `models.txt` вместо всей документации. Полный дамп документации весит около 600 КБ: он помещается в контекст современных моделей, но занимает его заметную часть. ## Что дальше * [Быстрый старт](/docs/quickstart) — первый запрос за 60 секунд. * [Каталог моделей](/models) — выбрать модель по цене и задаче глазами. * [GET /v1/models](/docs/api/models) — тот же каталог по API, со стабильной схемой. * [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) пополните баланс через СБП — минимально 300 ₽. Этого хватит на тысячи запросов к мелким моделям. ## 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). | | `provider` | `object` | — | Предпочтения по провайдерам инференса: `{"order": ["deepinfra"], "allow_fallbacks": false}`. Полезно для стабильных кеш-хитов — см. [Кеширование промптов](/docs/features/prompt-caching). | | `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) | | `413` | Тело запроса больше 36 МБ | [Ошибки](/docs/concepts/errors) | | `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-модель. | | `413` | Тело запроса больше 36 МБ. | | `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/images/generations (/docs/api/images) > Генерация изображений в формате OpenAI Images API — FLUX.2, Recraft, Seedream, Riverflow, Grok Imagine. Фиксированная цена за изображение, image-to-image через input_references. import { CodeTabs } from '@/components/docs/code-tabs'; import { EndpointBadge } from '@/components/docs/endpoint-badge'; Классический Images API: текстовый промпт на входе, готовые изображения на выходе. OpenAI-совместимый формат — официальные `openai` SDK работают без переписывания, достаточно поменять `base_url`. Через этот эндпоинт работают модели с оплатой **за изображение**: Black Forest Labs FLUX.2, Recraft V3–V4.1, ByteDance Seedream 4.5, Sourceful Riverflow, xAI Grok Imagine. Полный список — фильтр «Output: image» в [каталоге](/models). Гибридные модели с токенной тарификацией (Google Gemini \*-image, OpenAI GPT-5 Image) сюда не ходят — они генерируют через [chat completions с `modalities`](/docs/features/image-generation): здесь такой `model` вернёт `404`. ## Эндпоинт Заголовки: | Header | Значение | | --------------- | -------------------------------- | | `Authorization` | `Bearer sk-gw-...` (обязательно) | | `Content-Type` | `application/json` | ## Тело запроса | Поле | Тип | По умолчанию | Описание | | ----------------- | --------- | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------- | | **`model`** | `string` | — | ID модели из каталога (только модели с ценой за изображение). | | **`prompt`** | `string` | — | Описание изображения. FLUX лучше промптовать на английском — кириллицу рисует как текст на картинке. | | `n` | `integer` | `1` | Сколько изображений, 1–10. Реальный потолок у каждой модели свой (Seedream до 10, Recraft до 6, FLUX и Grok — 1); списание — за фактически возвращённые. | | `size` | `string` | — | Размер, напр. `"1024x1024"`. Большинство моделей задают размер сами — поле им передавать не нужно. | | `response_format` | `string` | `b64_json` | Пока поддерживается только `b64_json`. | Провайдер-специфичные поля схема не отбрасывает и передаёт модели как есть: `input_references` (image-to-image, см. ниже), `aspect_ratio` и `resolution` у Grok Imagine, `style` / `strength` / `text_layout` у Recraft, `font_inputs` у Riverflow. Что понимает конкретная модель — на её карточке в [каталоге](/models); незнакомое поле провайдер игнорирует или отклоняет запрос с `400`. Тело запроса ограничено 36 МБ — хватает на максимальный заказ с base64-референсами; при больших исходниках сжимайте JPEG перед отправкой. ## Минимальный пример ## Ответ ```json { "created": 1753789200, "data": [ { "b64_json": "iVBORw0KGgoAAAANSUhEUg..." } ] } ``` | Поле | Описание | | ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | | `created` | Unix-время (секунды). | | `data[]` | По одному элементу на изображение. | | `data[].b64_json` | Байты изображения в base64 — **без** префикса `data:`. Формат файла определяется по содержимому (обычно PNG или JPEG — как отдал провайдер). | Ссылок (`url`) в ответе нет — изображения приходят телом ответа. Поля `usage` тоже нет: стоимость запроса смотрите в [журнале](/logs). ## Редактирование (image-to-image) Исходные изображения передаются полем `input_references` — data URL с base64 или публичная https-ссылка: ```json { "model": "bytedance-seed/seedream-4.5", "prompt": "Перенеси предмет с фото в интерьер лофта, мягкий дневной свет", "input_references": [ { "type": "image_url", "image_url": { "url": "data:image/jpeg;base64,/9j/4AAQ..." } } ] } ``` В официальных SDK поле не объявлено — передавайте через `extra_body` (Python) или приведение типов (TypeScript). Сколько референсов принимает каждая модель и примеры целиком — в гайде [Генерация изображений](/docs/features/image-generation#редактирование-изображений). ## HTTP-коды | Код | `code` | Когда | | ----------------- | ---------------------- | ------------------------------------------------------------------------------------------------------------------------ | | `200` | — | Успех, изображения в `data[]`. | | `400` | `invalid_request` | Тело не соответствует схеме (нет `model`/`prompt`) либо модель отклонила параметры. | | `401` | — | Ключ отсутствует / отозван / невалиден. | | `402` | `insufficient_balance` | Баланса не хватает на ожидаемую стоимость заказа (`n × цена за изображение`). | | `404` | `model_not_found` | Модели нет в каталоге, она выключена или тарифицируется токенами (Gemini/GPT-5 Image — те ходят через chat completions). | | `413` | `request_too_large` | Тело запроса больше 36 МБ. | | `429` | `daily_limit_exceeded` | Превышен дневной лимит расходов ключа. | | `502` | `no_images_returned` | Провайдер ответил успехом, но без изображений. Списания нет, запрос стоит повторить. | | `502` | `pricing_unavailable` | Не удалось определить стоимость генерации — запрос не выполняется, бесплатных генераций не бывает. | | `502 / 503 / 504` | — | Транзиентные сбои провайдера, курса ЦБ или таймаут генерации. | Формат тела ошибки и стратегия retry — [Ошибки](/docs/concepts/errors). ## Биллинг Тарификация — **за изображение**: цена в рублях за штуку видна на карточке модели в [каталоге](/models), за запрос спишется `n × цена`. Стоимость известна до запроса и не зависит от длины промпта. Списание — после успешного ответа, одной транзакцией; сумма попадает в [журнал запросов](/logs). За отклонённые запросы (`model_not_found`, невалидное тело), отказ провайдера и пустой ответ не списывается ничего. ## Что дальше * [Генерация изображений](/docs/features/image-generation) — гайд целиком: оба пути, редактирование, выбор модели, параметры. * [Vision](/docs/features/vision) — картинка на входе для анализа, а не генерации. * [Модели](/docs/concepts/models) — как выбрать модель и прочитать её карточку. * [Ошибки](/docs/concepts/errors) — что делать на 429/502. * [Цены](/docs/concepts/pricing) — как считается стоимость запроса. --- # Management API (/docs/api/management) > Управление аккаунтом по API — ключи, баланс, расход, транзакции. Для скриптов, сервисов поверх Hubris и агентов. import { CodeTabs } from '@/components/docs/code-tabs'; import { EndpointBadge } from '@/components/docs/endpoint-badge'; Management API позволяет управлять аккаунтом программно, без захода в личный кабинет: создавать и отзывать API-ключи, ставить лимиты, смотреть баланс, расход и историю операций. Типовые сценарии: * **Скрипты и мониторинг** — проверить баланс перед ночным батчем, отправить уведомление «пора пополнить», отозвать утёкший ключ одной командой. * **Свой сервис поверх Hubris** — выдавать по отдельному ключу каждому своему клиенту, ставить дневной лимит в рублях, отключать неплательщиков и сверять расход по каждому ключу. * **Агенты и автоматизации** — агент своим же рабочим ключом узнаёт лимит, потраченное и баланс через `GET /v1/key` и сам решает, хватит ли на задачу. ## Два типа ключей | | Обычный ключ | Management-ключ | | -------------------------------------------- | ------------------------------------ | --------------------------------------------------------- | | Формат | `sk-gw-…` | `sk-gw-mgmt-…` | | Вызов моделей (`/v1/chat/completions` и др.) | ✅ | ❌ (403) | | Управление ключами, баланс, транзакции | ❌ (403) | ✅ | | `GET /v1/key` (самоинтроспекция) | ✅ | ✅ | | Как создаётся | В кабинете или через `POST /v1/keys` | Только в кабинете на [/management-keys](/management-keys) | Разделение сделано намеренно: обычные ключи живут на серверах и в агентах — если такой ключ утёк, злоумышленник может только тратить (и упирается в дневной лимит), но не может создавать ключи или читать историю платежей. Management-ключ храните как пароль: не передавайте в клиентский код, агентам и третьим лицам. **Если management-ключ скомпрометирован:** отзовите его в кабинете на [/management-keys](/management-keys), затем проверьте `GET /v1/keys` — не появились ли созданные без вас ключи (у них в кабинете видно время создания), и отзовите лишние. ## Лимиты | Лимит | Значение | | --------------------------------- | ---------------------- | | Запросы к management-эндпоинтам | 60 в минуту на аккаунт | | Создание ключей (`POST /v1/keys`) | 30 в час | | Активных ключей на аккаунт | 500 | Превышение — `429` с кодом `rate_limit_exceeded` (или `key_quota_exceeded` для потолка ключей). ## Ключи — `/v1/keys` Все эндпоинты этого раздела требуют management-ключ: `Authorization: Bearer sk-gw-mgmt-…`. Обычный ключ получит `403 management_key_required`. ### Список ключей Query-параметры: `offset` (по умолчанию 0), `limit` (1–100, по умолчанию 100). Возвращаются только активные обычные ключи — management-ключи и отозванные в список не попадают. ```json { "object": "list", "data": [ { "id": "3f1c9b2e-…", "name": "Клиент А", "key_prefix": "sk-gw-ab12...cd34", "disabled": false, "daily_limit_kopecks": 20000, "spent_24h_kopecks": 512, "privacy_mask_default": "inherit", "last_used_at": "2026-07-16T09:00:00.000Z", "created_at": "2026-07-01T12:00:00.000Z" } ], "has_more": false } ``` Полное значение ключа не возвращается никогда — только маскированный `key_prefix`. ### Создать ключ | Поле | Тип | Описание | | ---------------------- | ------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------- | | `name` | `string` (1–100) | Название ключа — например, имя вашего клиента или сервиса. | | `daily_limit_kopecks` | `number \| null` | Дневной лимит расхода в копейках (скользящие 24 часа), максимум 10 000 000 (100 000 ₽). Не указан или `null` — без лимита. | | `privacy_mask_default` | `"inherit" \| "off" \| "on" \| "required"` | Режим [маскирования данных](/docs/features/privacy) для запросов этим ключом. | Ответ `201` — та же форма, что в списке, плюс поле `key` с **полным значением ключа. Оно показывается один раз** — сохраните сразу, повторно получить нельзя. ### Один ключ Та же форма, что элемент списка. Чужой, отозванный или management-ключ — `404 not_found`. ### Изменить ключ Частичное обновление — передайте хотя бы одно поле: | Поле | Описание | | ---------------------- | -------------------------------------------------------------------------------------------------------- | | `name` | Переименовать. | | `disabled` | `true` — временно отключить (ключ получает `401` на любой запрос), `false` — включить обратно. Обратимо. | | `daily_limit_kopecks` | Новый дневной лимит; `null` — снять лимит. | | `privacy_mask_default` | Режим маскирования данных. | Ответ — обновлённый ключ. Типовой приём для сервиса поверх Hubris: клиент не оплатил период — `{"disabled": true}`, оплатил — `{"disabled": false}`. ### Отозвать ключ Необратимо. Ответ `{"id": "…", "deleted": true}`; повторный вызов по уже отозванному ключу тоже вернёт `200` (идемпотентно). Для временной блокировки используйте `PATCH` с `disabled: true`. ## Баланс — `GET /v1/credits` Требует management-ключ. ```json { "data": { "balance_kopecks": 152030, "bonus_balance_kopecks": 5000, "currency": "RUB" } } ``` `152030` копеек = 1520,30 ₽. Бонусный баланс — невыводимые кредиты (реферальные начисления и акции). ## Текущий ключ — `GET /v1/key` Самоинтроспекция: работает **обычным** ключом — management-ключ не нужен. Главная ручка для агентов: один запрос своим же рабочим ключом — и агент знает свой лимит, расход за сутки и баланс аккаунта. ```json { "data": { "name": "prod-bot", "key_prefix": "sk-gw-ab12...cd34", "key_type": "inference", "disabled": false, "daily_limit_kopecks": 20000, "spent_24h_kopecks": 512, "balance_kopecks": 152030, "created_at": "2026-07-01T12:00:00.000Z" } } ``` ## Транзакции — `GET /v1/transactions` Требует management-ключ. Пополнения, списания, возвраты и бонусные операции, свежие сверху — удобно для сверки и бухгалтерии. Query-параметры: `offset`, `limit` (1–100, по умолчанию 50), `type` (например, `topup`, `usage`, `refund`), `from`/`to` (ISO 8601). ```json { "object": "list", "data": [ { "id": "9d2e4c11-…", "type": "topup", "status": "succeeded", "amount_kopecks": 100000, "balance_after_kopecks": 152030, "payment_method": "sbp", "created_at": "2026-07-15T10:00:00.000Z" } ], "has_more": true } ``` Положительный `amount_kopecks` — зачисление, отрицательный — списание. `payment_method` (`sbp` / `card` / `invoice`) заполнен только у пополнений. ## Расход по всем ключам — `GET /v1/usage` [Обычный вызов `/v1/usage`](/docs/api/usage) показывает расход только того ключа, которым сделан запрос. **Management-ключом** тот же эндпоинт видит весь аккаунт и получает два дополнительных параметра: | Параметр | Описание | | ---------- | ------------------------------------------------------------------------------------- | | `key_id` | Сузить до одного ключа (UUID из `GET /v1/keys`). | | `group_by` | `key` — добавить массив `by_key` (расход каждого ключа), `model` — массив `by_model`. | В `by_key[]`: `key_id`, `key_prefix`, `name`, `requests`, `total_tokens`, `cost_kopecks` (строка, копейки). В `by_model[]`: `model`, `requests`, `prompt_tokens`, `completion_tokens`, `cost_kopecks`. ## HTTP-коды | Код | Когда | | ----------------------------- | ------------------------------------------------------------------ | | `400 invalid_request` | Невалидное тело или параметры; `key_id`/`group_by` обычным ключом. | | `401 invalid_api_key` | Ключ не передан, неверный, отозван или отключён. | | `403 management_key_required` | Management-эндпоинт вызван обычным ключом. | | `403 inference_key_required` | Модельный эндпоинт вызван management-ключом. | | `404 not_found` | Ключ не найден (или не ваш). | | `429 rate_limit_exceeded` | Превышен лимит 60/мин или 30 созданий/час. | | `429 key_quota_exceeded` | Достигнут потолок 500 активных ключей. | Формат ошибок стандартный: `{"error": {"message", "type", "code"}}` — см. [ошибки](/docs/concepts/errors). ## Что дальше * [GET /v1/usage](/docs/api/usage) — формы ответа расхода, шорткаты периодов. * [Обзор API](/docs/api/overview) — авторизация, базовый URL, совместимость с OpenAI SDK. * [Маскирование данных](/docs/features/privacy) — что такое `privacy_mask_default`. --- # 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` | Модель не найдена в каталоге или имя не парсится. | | 413 | `request_too_large` | Тело запроса больше 36 МБ. | | 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). ### Хранение ответов не поддерживается Hubris не хранит созданные response — поэтому поле `store` всегда трактуется как `false`, каким бы вы его ни прислали, а `previous_response_id` и `conversation` не работают. Историю диалога передавайте целиком в `input`. Практическое следствие: клиенты, которые ставят `store: true` по умолчанию (официальный OpenAI SDK, нода n8n «Message a model»), работают без дополнительной настройки — значение просто игнорируется. ## Минимальный пример `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. | | `413` | Тело запроса больше 36 МБ. | | `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) — таблица кодов. --- # POST /v1/audio/speech (/docs/api/speech) > Озвучка текста (TTS) — текст на входе, готовый аудиофайл на выходе. OpenAI-совместимый формат, работает официальный openai SDK. import { CodeTabs } from '@/components/docs/code-tabs'; import { EndpointBadge } from '@/components/docs/endpoint-badge'; Превращает текст в речь: озвучка статей и рассылок, голосовые ответы бота, аудиоверсии инструкций, реплики персонажей. OpenAI-совместимый формат — без переписывания работают `openai` Python/TypeScript SDK и любые клиенты под OpenAI Audio API: достаточно поменять `base_url`. Ответ — **сами байты аудио**, а не JSON: сохраняйте их в файл или отдавайте плееру. Стрима по флагу нет — один POST возвращает один готовый ответ целиком (его можно читать потоково, но это всё тот же единственный ответ). Модели озвучки — в [каталоге](/models) (фильтр «Озвучка») или запросом `GET /v1/models?output_modalities=speech`: MAI Voice, Aura-2, MiniMax Speech, Kokoro, Orpheus, Zonos, Voxtral TTS и другие. ## Эндпоинт Заголовки: | Header | Значение | | --------------- | -------------------------------- | | `Authorization` | `Bearer sk-gw-...` (обязательно) | | `Content-Type` | `application/json` | ## Тело запроса | Поле | Тип | По умолчанию | Описание | | ----------------- | -------- | ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | | **`model`** | `string` | — | ID модели озвучки из каталога. | | **`input`** | `string` | — | Текст для озвучки, до 50 000 символов. Тарифицируется по количеству символов. | | **`voice`** | `string` | — | Голос модели. Обязателен, у каждой модели свой набор — см. ниже. | | `response_format` | `string` | зависит от модели | `mp3` или `pcm` (16-bit LE). Если поле не передать, формат выбирает модель — как правило, это `pcm`; для файла на диске указывайте `mp3` явно. | Дополнительные поля, которые понимает конкретная модель (например, скорость речи или инструкции по стилю), передаются ей как есть — схема их не отбрасывает. Тело запроса ограничено 1 МБ: это текст, а не медиа, лимита хватает с большим запасом. ## Голоса `voice` обязателен, и наборы голосов у моделей не пересекаются: где-то их два, где-то девяносто. Список голосов конкретной модели — в её карточке в [каталоге](/models). Часть провайдеров список не публикует — тогда берите значение из документации самой модели. Если голос не из списка, который знает каталог, запрос отклоняется **до** обращения к модели — с кодом `400`, `code: "voice_not_supported"`, и в тексте ошибки перечислены доступные значения: ```json { "error": { "message": "Голос «нет-такого» недоступен у модели microsoft/mai-voice-2-flash. Доступные: en-US-Harper:MAI-Voice-2, …", "type": "invalid_request_error", "code": "voice_not_supported" } } ``` Если голосов у модели много, в сообщении перечислены первые восемь и число оставшихся. За отклонённый таким образом запрос ничего не списывается. ## Минимальный пример Модель и голос всегда идут парой: сменили модель — берите голос из её карточки в каталоге. Python-вариант с `with_streaming_response` читает тот же единственный ответ по мере поступления байтов — это удобно для длинного текста, но никакого пофразового стрима, как в чате, здесь нет. ## Ответ `200` — тело целиком состоит из байтов аудио. JSON в успешном ответе не приходит, поэтому и поля `usage` в нём нет: расход по запросу смотрите в [журнале](/logs). | Заголовок | Значение | | ---------------- | ----------------------------------------------------------------- | | `Content-Type` | `audio/mpeg` при `response_format: "mp3"`, `audio/pcm` при `pcm`. | | `Content-Length` | Размер аудио в байтах. | | `Cache-Control` | `no-store` — ответ не кэшируется. | `pcm` — это сырые сэмплы без заголовка WAV: их либо скармливают плееру/микшеру напрямую, либо оборачивают в контейнер. Если нужен файл, который просто откроется двойным кликом, берите `mp3`. Ошибки, наоборот, всегда приходят JSON-ом в обычном формате — по `Content-Type` ответа легко отличить одно от другого. ## HTTP-коды | Код | `code` | Когда | | ----------------- | ---------------------- | -------------------------------------------------------------------------------------------------------------- | | `200` | — | Успех, в теле — аудио. | | `400` | `voice_not_supported` | Голос не входит в набор этой модели (в тексте ошибки — доступные). | | `400` | `invalid_request` | Тело не соответствует схеме: нет `model`, `input` или `voice`, неизвестный `response_format`, невалидный JSON. | | `401` | — | Ключ отсутствует / отозван / невалиден. | | `402` | `insufficient_balance` | Недостаточно средств на балансе. | | `404` | `model_not_found` | Модели с таким `model` не существует или это не модель озвучки. | | `413` | `request_too_large` | Тело запроса больше 1 МБ. | | `429` | — | Превышен дневной лимит на ключе. | | `502` | `pricing_unavailable` | У модели нет ставки в каталоге — считать стоимость нечем, запрос не выполняется. | | `502` | `no_audio_returned` | Модель ответила успехом, но пустым телом. Списания нет, запрос стоит повторить. | | `502 / 503 / 504` | — | Транзиентные сбои провайдера, курса ЦБ или таймаут озвучки. | Формат тела ошибки и стратегия retry — [Ошибки](/docs/concepts/errors). ## Биллинг Озвучка тарифицируется **за символы входного текста** — не за токены и не за длительность получившегося аудио. Стоимость поэтому известна заранее: сколько символов отправили, столько и оплатили, независимо от того, насколько неторопливо модель их проговорила. Ставка каждой модели видна в её карточке в [каталоге](/models). Списание — по факту выполненного запроса, в рублях; сумма попадает в [журнал запросов](/logs) вместе с моделью и временем. За отклонённые до обращения к модели запросы (`voice_not_supported`, `model_not_found`, невалидное тело) и за пустой ответ модели не списывается ничего. ## Что дальше * [POST /v1/audio/transcriptions](/docs/api/transcriptions) — обратная задача: запись в текст. * [Аудио в чате](/docs/features/audio) — модель рассуждает о записи или отвечает голосом внутри диалога. * [Модели](/docs/concepts/models) — как выбрать модель и прочитать её карточку. * [Ошибки](/docs/concepts/errors) — что делать на 429/502. * [Цены](/docs/concepts/pricing) — как считается стоимость запроса. --- # Потоковая передача (/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` приходит всегда — в том числе если вы явно передали `include_usage: false` (значение перезаписывается: по итоговому `usage` считается списание). Учитывайте, что финальный usage-чанк приходит с пустым `choices: []` — читайте `chunk.choices[0]` с проверкой. ## Парсинг Парсите построчно с буфером: один 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) — что бывает при сбое стрима. --- # POST /v1/audio/transcriptions (/docs/api/transcriptions) > Распознавание речи (транскрибация аудио в текст). OpenAI-совместимый формат — работает официальный openai SDK. import { CodeTabs } from '@/components/docs/code-tabs'; import { EndpointBadge } from '@/components/docs/endpoint-badge'; Превращает аудиозапись в текст: голосовые сообщения, звонки, подкасты, интервью, субтитры. OpenAI-совместимый формат — без переписывания работают `openai` Python/TypeScript SDK и любые клиенты под OpenAI Audio API: достаточно поменять `base_url`. Стрима нет: один POST → один JSON-ответ с готовым текстом (`stream: true` отклоняется с кодом 400). Полный список моделей распознавания — в [каталоге](/models) (фильтр «Транскрибация»): Whisper, GPT-4o Transcribe, Voxtral, Chirp и другие. ## Эндпоинт Заголовки: | Header | Значение | | --------------- | ------------------------------------------------------------ | | `Authorization` | `Bearer sk-gw-...` (обязательно) | | `Content-Type` | `multipart/form-data` (файл) или `application/json` (base64) | ## Тело запроса Поддерживаются два формата — результат одинаковый. ### Вариант 1: multipart/form-data (как у OpenAI) | Поле | Тип | По умолчанию | Описание | | --------------------------- | ---------- | ------------ | --------------------------------------------------------------------------------------------------------------------- | | **`file`** | `file` | — | Аудиофайл. Форматы: WAV, MP3, FLAC, M4A, OGG, WebM, AAC. До 25 МБ. | | **`model`** | `string` | — | ID модели распознавания, например `openai/whisper-1`. | | `language` | `string` | авто | Язык записи в ISO-639-1 (`ru`, `en`). Явное указание ускоряет и уточняет распознавание. | | `prompt` | `string` | — | Подсказка: имена, термины, аббревиатуры, которые встречаются в записи. | | `temperature` | `number` | `0` | 0–1. Выше — креативнее, ниже — детерминированнее. | | `response_format` | `string` | `json` | `json` или `verbose_json` (поддержка `verbose_json` зависит от модели). Форматы `text`/`srt`/`vtt` не поддерживаются. | | `timestamp_granularities[]` | `string[]` | — | `segment` и/или `word` — таймстемпы в `verbose_json` (поддерживается не всеми моделями). | ### Вариант 2: application/json (аудио в base64) | Поле | Тип | Описание | | ------------------------ | -------- | ---------------------------------------------------------- | | **`model`** | `string` | ID модели распознавания. | | **`input_audio.data`** | `string` | Аудио в base64 (без `data:`-префикса). | | **`input_audio.format`** | `string` | Формат: `wav`, `mp3`, `flac`, `m4a`, `ogg`, `webm`, `aac`. | Прямые URL на аудио не поддерживаются — только файл или base64. Записи длиннее \~10 минут лучше резать на части: у провайдеров есть таймаут обработки (\~60 секунд на запрос). ## Минимальный пример ## Ответ ```json { "text": "Привет! Это тестовая запись для распознавания речи.", "usage": { "seconds": 4, "input_tokens": 120, "output_tokens": 18, "total_tokens": 138, "cost": 6 } } ``` ### Поля ответа | Поле | Тип | Описание | | --------------------- | --------- | ------------------------------------------------------------------------- | | **`text`** | `string` | Распознанный текст. Пустая строка — валидный результат (тишина в записи). | | `usage.seconds` | `number` | Длительность обработанного аудио в секундах (если модель её сообщает). | | `usage.input_tokens` | `integer` | Токены аудио-входа. | | `usage.output_tokens` | `integer` | Токены текста на выходе. | | `usage.total_tokens` | `integer` | Сумма. | | **`usage.cost`** | `integer` | Стоимость **в копейках**. Hubris-расширение. | При `response_format: "verbose_json"` модели, которые это поддерживают, дополнительно возвращают `language`, `duration`, `segments[]` и `words[]` с таймстемпами. ## HTTP-коды | Код | Когда | | ----------------- | ------------------------------------------------------------------------- | | `200` | Успешное распознавание. | | `400` | Нет `file`/`model` (multipart) или `input_audio` (JSON), невалидное тело. | | `401` | Ключ отсутствует / отозван / невалиден. | | `402` | Недостаточно средств на балансе. | | `404` | Модели с таким `model` не существует или это не модель распознавания. | | `413` | Тело запроса больше лимита (файл до 25 МБ). | | `429` | Превышен дневной лимит на ключе. | | `502 / 503 / 504` | Транзиентные сбои у провайдера или курса ЦБ. | Формат тела ошибки и стратегия retry — [Ошибки](/docs/concepts/errors). ## Биллинг Стоимость считается по факту каждого запроса и возвращается в `usage.cost` в копейках — та же сумма, что списывается с баланса и видна в [логах](/logs). Часть моделей тарифицируется по длительности записи, часть — по токенам; ориентиры цен — в [каталоге](/models) с фильтром «Транскрибация». ## Что дальше * [Модели](/docs/concepts/models) — как выбрать модель распознавания. * [Ошибки](/docs/concepts/errors) — что делать на 429/502. * [Цены](/docs/concepts/pricing) — формула расчёта. --- # 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": 6000, "completion_tokens": 130, "total_tokens": 6130, "cache_read_tokens": 198000, "cache_write_tokens": 0, "reasoning_tokens": 0, "cache_hit_rate": 0.97, "cache_savings_kopecks": "48150", "cache_savings_rub": 481.5, "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.cache_read_tokens`** | `integer` | Токены, прочитанные из кеша (дешевле обычного входа). | | **`totals.cache_write_tokens`** | `integer` | Токены записи кеша (дороже обычного входа). | | **`totals.reasoning_tokens`** | `integer` | Токены рассуждения — подмножество `completion_tokens`, не прибавляйте их отдельно. | | **`totals.cache_hit_rate`** | `number` (0..1) | Доля попаданий в кеш за период. | | **`totals.cache_savings_kopecks`** | `string` | Сколько кеш сэкономил за период (нетто), в копейках. | | **`totals.cache_savings_rub`** | `number` | То же значение в рублях. | | **`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` | Кол-во запросов в интервале. | Всего входных токенов = `prompt_tokens` + `cache_read_tokens` + `cache_write_tokens`. `reasoning_tokens` входят в `completion_tokens`, не прибавляйте их отдельно. ## С разбивкой по дням С `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-экспорт. --- # Генерация видео (API) (/docs/api/videos) > Асинхронный /v1/videos — создание, опрос статуса, скачивание mp4. Оплата в рублях по факту, хранение результата 72 часа. import { CodeTabs } from '@/components/docs/code-tabs'; import { EndpointBadge } from '@/components/docs/endpoint-badge'; `/v1/videos` — асинхронный API генерации видео. Вы создаёте задачу, опрашиваете её статус и, когда видео готово, скачиваете mp4. Тот же Bearer-ключ `sk-gw-...`, что и для остального API. Цена — в рублях, списывается по факту. Идентификатор задачи (`id`), `polling_url` и `unsigned_urls` указывают **только на эндпоинты Hubris** (`https://api.hubris.pw`). ## Scope ключа * `POST /v1/videos` требует scope **`videos:write`** (денежно-мутирующий — создаёт задачу и резервирует средства). * `GET /v1/videos/{id}` и `GET /v1/videos/{id}/content` требуют **`videos:read`**. * `GET /v1/videos/models` требует **`models:read`** — тот же scope, что и обычный каталог [`GET /v1/models`](/docs/api/models), потому что это тоже просто листинг каталога. Полноправные ключи `sk-gw-` (без ограничения scope) покрывают все три. У scoped-ключей и OAuth-токенов проверьте, что нужный scope выдан на [/keys](/keys). ## Жизненный цикл ``` POST /v1/videos → 202 { id, status: "pending", polling_url } │ ▼ (опрос раз в несколько секунд) GET /v1/videos/{id} → 200 { status: "pending" | "in_progress" | "completed" | "failed" } │ ▼ (когда status = "completed") GET /v1/videos/{id}/content?index=0 → video/mp4 (сырые байты) ``` Статусы наружу: `pending`, `in_progress`, `completed`, `failed`. Промежуточные внутренние состояния схлопываются в `in_progress`. ## Создание — `POST /v1/videos` Обязательны `model` (из каталога видео-моделей, см. [`GET /v1/videos/models`](#каталог-видео-моделей--get-v1videosmodels)) и `prompt`. Ответ — **HTTP 202**. ```json { "id": "b0f2c1a4-3e9d-4b7a-9c2e-8f1a6d5c4b3a", "status": "pending", "polling_url": "https://api.hubris.pw/v1/videos/b0f2c1a4-3e9d-4b7a-9c2e-8f1a6d5c4b3a", "created": 1752192000 } ``` Опциональные поля: `size` (`"1280x720"`, шорткат к `resolution`+`aspect_ratio`; при явном `resolution` и/или `aspect_ratio` они перекрывают `size`), `seed` (int, только если модель поддерживает детерминизм), `frame_images[]` — до двух опорных кадров, чтобы оживить картинку первым и/или последним кадром: ```json { "frame_images": [ { "type": "image_url", "image_url": { "url": "https://example.com/frame.png" }, "frame_type": "first_frame" } ] } ``` `image_url.url` принимает `https://`-ссылку (без логина/пароля в URL) или `data:image/png;base64,...` / `data:image/jpeg;base64,...` (до 10 МБ в base64). Поля `callback_url` и `input_references` в v1 не поддерживаются — запрос с ними вернёт `400 invalid_request` (не молчаливый дроп). ### Идемпотентность Передайте заголовок `Idempotency-Key: <ваш-ключ>` — повторный `POST` с тем же ключом от того же аккаунта вернёт **уже созданную** задачу, не создавая вторую и не резервируя средства повторно. Используйте при сетевых ретраях создания. ## Опрос статуса — `GET /v1/videos/{id}` ```bash curl -s https://api.hubris.pw/v1/videos/b0f2c1a4-3e9d-4b7a-9c2e-8f1a6d5c4b3a \ -H "Authorization: Bearer $HUBRIS_API_KEY" ``` ```json { "id": "b0f2c1a4-3e9d-4b7a-9c2e-8f1a6d5c4b3a", "status": "completed", "created": 1752192000, "unsigned_urls": [ "https://api.hubris.pw/v1/videos/b0f2c1a4-3e9d-4b7a-9c2e-8f1a6d5c4b3a/content?index=0" ], "usage": { "cost": 4200 } } ``` * **`usage.cost`** — стоимость **в копейках** (integer), появляется только при `status: "completed"`. `4200` = 42,00 ₽. Это итоговая сумма, списанная с баланса — не оценка. * **`unsigned_urls`** появляются только при `status: "completed"` и, несмотря на имя (унаследовано от формата апстрима), **требуют тот же Bearer-ключ** — это эндпоинты Hubris, не публичные подписанные ссылки. * При `status: "failed"` приходит поле `error` с нейтральным текстом. ### Пример полного цикла (bash) ```bash ID=$(curl -s https://api.hubris.pw/v1/videos \ -H "Authorization: Bearer $HUBRIS_API_KEY" \ -H "Content-Type: application/json" \ -d '{"model":"kwaivgi/kling-v3.0-pro","prompt":"Закат над океаном, дрон"}' \ | python3 -c 'import sys,json;print(json.load(sys.stdin)["id"])') while true; do STATUS=$(curl -s "https://api.hubris.pw/v1/videos/$ID" \ -H "Authorization: Bearer $HUBRIS_API_KEY" \ | python3 -c 'import sys,json;print(json.load(sys.stdin)["status"])') echo "status=$STATUS" [ "$STATUS" = "completed" ] && break [ "$STATUS" = "failed" ] && { echo "generation failed"; exit 1; } sleep 5 done curl -s "https://api.hubris.pw/v1/videos/$ID/content?index=0" \ -H "Authorization: Bearer $HUBRIS_API_KEY" -o result.mp4 ``` Опрашивайте не чаще раза в несколько секунд — частые повторы одного и того же `GET` не ускорят генерацию. ## Скачивание — `GET /v1/videos/{id}/content` Отдаёт сырые байты `video/mp4`. **Требует Bearer-ключ** и доступен только владельцу задачи. В v1 доступен единственный клип — `index=0` (параметр можно не передавать, по умолчанию тот же); `index` больше нуля вернёт `400 index_out_of_range`. Запрос до готовности видео или после истечения хранения — `404 video_not_ready`. ## Хранение результата — 72 часа Готовый ролик хранится у Hubris **72 часа** с момента создания задачи, затем удаляется без возможности восстановления. Скачайте и сохраните файл у себя в течение этого окна. Запрос `/content` после удаления вернёт `404 video_not_ready` — так же, как если бы видео ещё не было готово (Hubris не палит разницу между «протухло» и «не готово»). ## Каталог видео-моделей — `GET /v1/videos/models` ```bash curl -s https://api.hubris.pw/v1/videos/models \ -H "Authorization: Bearer $HUBRIS_API_KEY" ``` Возвращает `{ "object": "list", "data": [...] }` — активные видео-модели с поддерживаемыми разрешениями, соотношениями сторон, длительностями, доступностью звука/seed/опорных кадров и **ценами в рублях** за единицу (секунда или видео). Видео-модели скрыты из обычного [`GET /v1/models`](/docs/api/models) — получить их там тоже можно, но отдельным запросом: `GET /v1/models?output_modalities=video`. ## Коды ошибок | HTTP | code | type | Когда | | --------- | --------------------------- | ----------------------- | -------------------------------------------------------------------------------------------------------------------------------- | | 400 | `invalid_request` | `invalid_request_error` | Кривой запрос, неподдерживаемая ось (длительность/разрешение/пропорции не из каталога модели), `callback_url`/`input_references` | | 400 | `index_out_of_range` | `invalid_request_error` | `index` в `/content` вне диапазона (v1 — только `index=0`) | | 402 | `insufficient_balance` | `invalid_request_error` | Недостаточно средств для резерва под задачу | | 404 | `model_not_found` | `invalid_request_error` | Модель с таким `id` не видео-модель или неактивна | | 404 | `not_found` | `invalid_request_error` | Задачи с таким `id` не существует или она принадлежит другому аккаунту | | 404 | `video_not_ready` | `invalid_request_error` | `/content` до `completed` или после 72-часового TTL | | 413 | `request_too_large` | `invalid_request_error` | Тело запроса (обычно — `frame_images` в base64) превышает лимит | | 429 | `too_many_active_jobs` | `rate_limit_error` | Достигнут лимит одновременных видео-задач аккаунта | | 502 / 504 | `upstream_error` | `api_error` | Провайдер модели не принял задачу (502) или не ответил вовремя (504) | | 503 | `exchange_rate_unavailable` | `api_error` | Курс ЦБ РФ временно недоступен — нельзя посчитать резерв под задачу | Полный формат ошибок и стратегия retry — на странице [Ошибки](/docs/concepts/errors). ## Приватность и хранение данных Видео-генерация выполняется на инфраструктуре внешних провайдеров моделей и **не покрывается режимом Zero Data Retention**: провайдеры временно хранят входные данные (промпт, кадры `frame_images`) и результат на своей стороне на время генерации. У Hubris готовый ролик хранится 72 часа, затем удаляется. **Privacy Mode на видео не распространяется.** Автоматическое маскирование персональных данных ([Privacy Mode](/docs/features/privacy)) работает только для текстовых запросов Chat Completions; промпты и кадры видео уходят провайдеру как есть — не передавайте туда персональные данные третьих лиц. **Трансграничная передача (152-ФЗ).** Если вы вызываете `/v1/videos` от имени своих пользователей, вы отвечаете за наличие их согласия на обработку и трансграничную передачу входов и выходов (промпты, кадры, результат) за пределы РФ, а также на 72-часовое хранение результата у Hubris. Условия использования API фиксируют это в оферте. Hubris **не логирует** содержимое ваших запросов: `prompt`, байты видео и `frame_images` не попадают в логи — только метаданные (модель, стоимость, статус, латентность). ## Что дальше * [Ошибки](/docs/concepts/errors) — таблица всех кодов и retry-стратегий. * [Биллинг](/docs/concepts/billing) — как считается `usage.cost`. * [Privacy Mode](/docs/features/privacy) — что покрывает маскирование, а что нет. * [Студия видео](/videos) в личном кабинете — тот же API под UI, без curl. --- # Биллинг (/docs/concepts/billing) > Баланс, минимальный остаток, пополнение через СБП, поведение при нехватке средств. Hubris работает по предоплатной модели: вы пополняете баланс через СБП, и каждый успешный запрос списывает с него стоимость. Подписок и тарифов нет. ## Баланс Текущий баланс отображается на странице [/billing](/billing) в режиме реального времени. Там же — история пополнений и история расходов. Также баланс виден в шапке дашборда (рядом с меню профиля). ## Пополнение через СБП 1. На странице [/billing](/billing) нажмите «Пополнить». 2. Введите сумму (минимум 300 ₽, максимум 100 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) — текущий баланс и история. --- # Дотратить баланс OpenRouter (BYOK) (/docs/concepts/byok) > Подключите свой ключ OpenRouter — Hubris потратит его баланс вместо рублёвого. Комиссия — 1 копейка за запрос. import { CodeTabs } from '@/components/docs/code-tabs'; Если у вас уже есть аккаунт и оплаченный баланс в OpenRouter — этот раздел для вас. ## Что это OpenRouter не принимает запросы напрямую из России. Если у вас есть аккаунт OpenRouter с оплаченным балансом, он может попросту зависнуть — потратить его штатным способом из РФ не получится. **«Дотратить баланс OpenRouter»** решает эту проблему: вы подключаете свой ключ OpenRouter к Hubris, и дальше расходуете баланс через нашу инфраструктуру. Комиссия Hubris — **1 копейка за успешный запрос**, сама генерация оплачивается напрямую с вашего баланса OpenRouter — по тем же ценам, что там указаны. Такой режим иногда называют BYOK (bring your own key — «принесите свой ключ»). ## Как это работает 1. Вы подключаете свой ключ OpenRouter (`sk-or-v1-...`) на странице [«BYOK»](/byok) в личном кабинете. 2. Hubris автоматически создаёт связанный ключ Hubris (`sk-gw-...`) — пара «один ключ OpenRouter → один ключ Hubris». 3. Запросы этим ключом Hubris форвардятся на OpenRouter по вашему ключу и списывают токены с вашего баланса OpenRouter. 4. Ваши обычные ключи Hubris при этом не меняются и продолжают работать в рублях с баланса Hubris, как и раньше — подключение BYOK-ключа ничего в них не трогает. ## Подключение 1. Откройте страницу [«BYOK»](/byok) в личном кабинете. 2. Вставьте ключ OpenRouter. Ключ создаётся в кабинете OpenRouter → [Keys](https://openrouter.ai/settings/keys) — скопируйте его оттуда. 3. Нажмите «Подключить и создать ключ Hubris». В открывшемся окне появится готовый ключ Hubris (`sk-gw-...`) — он показывается **один раз**, сохраните его сразу. 4. В вашей интеграции замените прежний ключ на этот. Адрес `api.hubris.pw` менять не нужно — меняется только значение заголовка `Authorization`: Больше в коде ничего не меняется — тот же `base_url`, тот же формат запроса, тот же формат ответа. ## Несколько ключей и проекты Можно подключить сразу несколько ключей OpenRouter — например, если у вас несколько аккаунтов OpenRouter или вы хотите развести проекты по разным балансам. Каждому ключу OpenRouter соответствует свой отдельный ключ Hubris, поэтому расход по проектам не смешивается. ## Обновление ключа (rotate) Ключ Hubris показывается один раз — если вы его не сохранили, не нужно подключать ключ OpenRouter заново. На странице [«BYOK»](/byok) у нужной пары нажмите **«Обновить ключ Hubris»**: выдастся новый ключ Hubris, старый сразу перестанет работать. Привязанный ключ OpenRouter при этом остаётся прежним — вписывать его снова не требуется. ## Где работает BYOK-ключ работает на тех же эндпоинтах, что и обычный ключ Hubris, кроме генерации видео: | Эндпоинт | Путь | Комментарий | | --------------------- | ------------------------ | ---------------------- | | Чат | `/v1/chat/completions` | | | Генерация изображений | `/v1/images/generations` | | | Эмбеддинги | `/v1/embeddings` | | | Responses API | `/v1/responses` | | | Messages API | `/v1/messages` | Anthropic-совместимый | | Видео | `/v1/videos` | пока не поддерживается | Видео (`/v1/videos`) для BYOK-ключей добавим позже — пока запрос на этот эндпоинт с BYOK-ключом вернёт ошибку. ## Стоимость и логи За каждый успешный запрос BYOK-ключом с вашего баланса Hubris списывается фиксированная **1 копейка** — это комиссия за форвардинг запроса. Стоимость самих токенов при этом не входит в эту копейку и не проходит через баланс Hubris вообще — она списывается напрямую с вашего баланса OpenRouter по его собственным ценам. В разделе [/logs](/logs) такие запросы помечены значком OpenRouter и подписью «через ваш OpenRouter» — их легко отличить от запросов, оплаченных с обычного баланса Hubris. ## Когда баланс OpenRouter закончится Как только баланс на вашем аккаунте OpenRouter будет исчерпан, запросы BYOK-ключом перестанут проходить — вернётся ошибка: ```json { "error": { "message": "Баланс вашего аккаунта OpenRouter исчерпан. Пополните его на openrouter.ai или переключитесь на оплату в рублях через Hubris.", "type": "insufficient_quota", "code": "byok_insufficient_credits" } } ``` В этом случае можно либо пополнить баланс на стороне OpenRouter, либо просто продолжить работу на обычном балансе Hubris — переключитесь на любой из ваших ключей Hubris без пометки BYOK. Пополнение баланса Hubris — в рублях через СБП, подробнее в разделе [Биллинг](/docs/concepts/billing). ## Безопасность Ключ OpenRouter хранится на сервере Hubris в зашифрованном виде. В интерфейсе и в логах виден только его префикс — полный ключ нигде, кроме момента подключения, не отображается и не логируется. > Использование ключа OpenRouter через посредника может подпадать под условия использования самого OpenRouter — оцените это самостоятельно перед подключением. ## Что дальше * [«BYOK»](/byok) — страница подключения ключей в личном кабинете. * [Биллинг](/docs/concepts/billing) — пополнение обычного баланса Hubris через СБП. * [Ошибки](/docs/concepts/errors) — формат ошибок и таблица кодов. --- # Ошибки (/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` | Превышен дневной лимит на конкретном ключе (только для служебных) | Дождаться сброса лимита или использовать ключ без лимита | | 429 | `too_many_active_jobs` | Достигнут лимит одновременных видео-задач аккаунта — только `/v1/videos` | Опрашивать `GET /v1/videos/{id}`, дождаться завершения текущих задач, затем повторить POST | | 404 | `video_not_ready` | Запрошен `/content` видео-задачи, которая ещё не `completed` (или истёк 72-часовой TTL) — только `/v1/videos` | Опрашивать `GET /v1/videos/{id}` до `status: "completed"`, затем качать `/content` | | 400 | `index_out_of_range` | Параметр `index` в `/content` вне диапазона (v1 отдаёт только `index=0`) — только `/v1/videos` | Использовать `index=0` | | 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) > Модели с нулевой ценой во всех единицах тарификации. Как их найти по флагу is_free и какой действует лимит запросов. В каталоге Hubris есть модели с нулевой ценой: open-source LLM и embedding-модели. Они отмечены значком «Бесплатно» в [каталоге моделей](/models) — актуальный список открывается фильтром [«бесплатные»](/models?free=true), состав со временем меняется. Бесплатные модели — это: * **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.is_free) | .id' ``` ## Что считается «бесплатной» моделью Модель бесплатна, когда **нулевые все единицы тарификации** — за токены, за единицу (изображение, мегапиксель, секунда) и дополнительные компоненты — **и выход не медийный**. Итог этой проверки приходит готовым флагом `pricing.is_free`: ```json { "id": "openai/gpt-oss-20b:free", "pricing": { "input_rub_per_million": 0, "output_rub_per_million": 0, "currency": "RUB", "is_free": true } } ``` **Не определяйте бесплатность по нулевой цене за токены.** У моделей с оплатой в других единицах — генерация изображений, видео, аудио — цена за токены нулевая, а запрос платный: стоимость считается за картинку или за секунду. Такие модели приходят с `is_free: false`, даже если оба поля `*_rub_per_million` равны нулю. Суффикс `: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/dashboard/client-accounts) > Как подрядчику получить доступ к кабинету заказчика, что он может делать, чего не может и как доступ отозвать. Если вы настраиваете нейросети для заказчиков, раньше мешали две вещи: код из письма на каждый вход и невозможность зайти в кабинет клиента. Теперь клиент может подключить вас к своему кабинету, и вы переключаетесь между кабинетами одной кнопкой в шапке. Деньги при этом остаются деньгами клиента: каждый кабинет платит со своего баланса. ## Как подключиться 1. Вы отправляете приглашение на почту клиента — [Профиль](/profile?tab=access) → вкладка «Доступы» → «Пригласить». 2. Клиент открывает письмо и одной кнопкой попадает в свой кабинет — регистрироваться заново или искать код не нужно. 3. Клиент подтверждает доступ на той же вкладке «Доступы». 4. У вас в шапке появляется переключатель кабинетов рядом с меню профиля. Приглашать можно и тех, у кого аккаунта ещё нет: связка привяжется, когда человек зарегистрируется на этот адрес. Пока вы работаете в чужом кабинете, наверху видна янтарная полоса с его названием — чтобы не перепутать, где вы находитесь. ## Что подрядчик может * Входить в кабинет клиента и работать в нём как он сам * Создавать и отзывать API-ключи * Видеть траты, журнал запросов и историю платежей * Работать в чате, генерировать картинки и видео — за счёт баланса клиента ## Чего подрядчик не может * Менять пароль, почту и способы входа * Пополнять баланс, включать автопополнение и привязывать карты * Править реквизиты юридического лица и выставлять счета * Выдавать доступ к кабинету другим Запреты действуют на стороне сервера, а не только в интерфейсе: даже прямой запрос к API вернёт `403 agent_forbidden`. ## Отзыв доступа Клиент в любой момент отзывает доступ на вкладке «Доступы» — подтверждения с вашей стороны не требуется. Вместе с доступом гасятся и ключи, которые подрядчик создал в этом кабинете. Если на них работают интеграции клиента, они перестанут отвечать — ключи придётся выпустить заново. Это защита: иначе выпущенный ключ пережил бы отзыв доступа и продолжал тратить баланс. Ключи, которые клиент создал сам, отзыв не трогает. ## Чем это отличается от общего пароля Совместный пароль означает полный доступ, включая деньги и смену почты, и не оставляет следов, кто что сделал. Подключение кабинета — это именованный доступ с границами: клиент видит, кто подключён, любые действия подрядчика попадают в журнал, а отзыв занимает одно нажатие. ## Что дальше * [Ключи и проекты](/docs/dashboard/keys-and-projects) — разложить ключи клиентов по проектам * [Вход в аккаунт](/docs/dashboard/sign-in) — пароль вместо кода на почту --- # Кабинет (/docs/dashboard) > Что где лежит в личном кабинете Hubris — вход, ключи и проекты, доступ подрядчиков, расход и оплата. Этот раздел — про интерфейс: куда нажимать и что произойдёт. Если вы ищете параметры запросов и коды ошибок, вам в [API](/docs/api/overview) и [Базовые концепции](/docs/concepts/models). ## Разделы | Страница | О чём | | ---------------------------------------------------- | --------------------------------------------------------------------------- | | [Вход в аккаунт](/docs/dashboard/sign-in) | Код на почту, пароль, вход через Яндекс и VK, что делать при потере доступа | | [Ключи и проекты](/docs/dashboard/keys-and-projects) | Создание ключей, дневной лимит, группировка по проектам, расход по каждому | | [Кабинеты клиентов](/docs/dashboard/client-accounts) | Работа в кабинете заказчика: приглашение, границы доступа, отзыв | ## Где что находится Слева в кабинете — разделы, сгруппированные по смыслу. * **Дашборд** — баланс, последние траты, подсказки по началу работы. * **Использование** — графики расхода: по дням, моделям, ключам и проектам. * **Логи** — журнал запросов с фильтрами и выгрузкой в CSV. * **Ключи** — API-ключи и проекты. * **Биллинг** — пополнение, автопополнение, история платежей, документы для юрлиц. * **Профиль** — почта и пароль, доступы, уведомления, юридические данные. Баланс всегда виден слева вверху, рядом — кнопка пополнения. ## Быстрые ответы **Где взять ключ.** [Ключи](/keys) → «Создать ключ». Полное значение показывается один раз — сохраните сразу, в базе остаётся только его хеш. **Почему списалось больше, чем ожидалось.** Откройте [Логи](/logs) и найдите запрос: там видно модель, токены входа и выхода, попадание в кеш и итоговую стоимость. Подробнее — [Цены](/docs/concepts/pricing). **Как разделить расходы по клиентам.** Заведите проекты и разложите ключи по ним — [Ключи и проекты](/docs/dashboard/keys-and-projects). **Как пустить подрядчика в свой кабинет.** Пригласите его на вкладке «Доступы» в профиле — [Кабинеты клиентов](/docs/dashboard/client-accounts). --- # Ключи и проекты (/docs/dashboard/keys-and-projects) > Создание API-ключей, дневной лимит, отключение и отзыв, группировка ключей по проектам и расход по каждому. Ключ — это пропуск к API. Проект — папка для ключей, чтобы считать расход по клиенту или направлению отдельно. Всё живёт на странице [Ключи](/keys). ## Создание ключа 1. Откройте [Ключи](/keys) → «Создать ключ». 2. Дайте понятное имя: по нему вы будете узнавать ключ в журнале и в статистике. Хорошо — «прод-бот», «сайт-виджет»; плохо — «ключ 2». 3. При желании задайте дневной лимит и проект. 4. Скопируйте значение — **оно показывается один раз**. В базе остаётся только хеш, восстановить нельзя. Потеряли ключ — создайте новый и отзовите старый; это нормальная практика, а не авария. ## Дневной лимит Лимит ограничивает траты по ключу за скользящие 24 часа. Превышение — ошибка `429 daily_limit_exceeded`, остальные ключи продолжают работать. Лимит полезен, когда ключ уезжает в чужие руки: подрядчику, в тестовый стенд, в публичное демо. Без лимита один цикл в чужом коде способен съесть весь баланс. Задать или изменить — на странице ключа, поле «Дневной лимит». Пусто = без лимита. ## Отключение и отзыв На странице ключа есть два разных действия: | Действие | Что происходит | Обратимо | | --------- | ---------------------------------------------------- | ---------------------- | | Отключить | Ключ отвечает 401, статистика и значение сохраняются | Да, включается обратно | | Отозвать | Ключ перестаёт работать навсегда | Нет | Отключение удобно, когда нужно быстро остановить подозрительный трафик и разобраться. Отзыв — когда ключ точно скомпрометирован или больше не нужен. История запросов в обоих случаях остаётся в журнале: отзыв ключа не стирает прошлые траты. ## Проекты Проект группирует ключи внутри вашего кабинета. Это удобно, если вы ведёте нескольких клиентов или несколько направлений и хотите видеть, сколько потратил каждый. Своего баланса у проекта нет: деньги общие, проект делит **учёт**, а не кошелёк. ### Как завести 1. На странице [Ключи](/keys) нажмите «+ Проект». 2. Задайте название и цвет метки. 3. Разложите ключи: на странице ключа поле «Проект», либо выберите проект сразу при создании. Над списком ключей появится полоса вкладок: «Все ключи», затем ваши проекты и «Без проекта». Выбранная вкладка фильтрует список, а в подзаголовке показывает сводку — сколько ключей и сколько потрачено за 30 дней. ### Расход по проекту * На странице [Использование](/activity) — карточка «Распределение расхода по проектам». * В [журнале запросов](/logs) — фильтр «Проект» рядом с фильтром «API-ключ». Ключ можно переложить в другой проект в любой момент — история его расходов переезжает вместе с ним. Это сделано специально: первая раскладка ключей по клиентам почти всегда получается неточной, и её нужно уметь исправить задним числом. ### Удаление проекта Удаляется только группировка: ключи возвращаются в «Без проекта» и продолжают работать. Ни одна интеграция от удаления проекта не сломается. ## Что дальше * [Аутентификация](/docs/authentication) — как передавать ключ в запросах * [Кабинеты клиентов](/docs/dashboard/client-accounts) — если ключи заводит подрядчик * [Лимиты частоты](/docs/concepts/rate-limits) — ограничения на стороне API --- # Вход в аккаунт (/docs/dashboard/sign-in) > Код на почту, вход по паролю, Яндекс ID и VK ID — и что делать, если доступ потерян. Аккаунт в Hubris — это почта. Пароль необязателен: по умолчанию вход идёт по одноразовому коду, а пароль можно завести отдельно, если так удобнее. ## Код на почту Основной способ. Он же регистрация: отдельной формы «создать аккаунт» нет. 1. Откройте [страницу входа](/sign-in) и введите почту. 2. Нажмите «Получить код» — письмо приходит в течение нескольких секунд. 3. Введите шестизначный код. Код живёт ограниченное время и сгорает после первого использования. Если письмо не пришло — проверьте папку «Спам»; если и там пусто, напишите в [поддержку](/docs/support). ## Пароль Пароль удобен, когда вы заходите часто или ведёте несколько кабинетов и не хотите каждый раз лезть в почту. 1. Откройте [Профиль](/profile) → карточка «Пароль». 2. Задайте пароль — от 8 символов. 3. Дальше на странице входа переключитесь на «Войти по паролю». Мы храним не сам пароль, а его хеш (argon2id), поэтому подсмотреть или восстановить исходное значение нельзя — только задать новое. При смене пароля все остальные сессии завершаются: на других устройствах придётся войти заново. Это защита на случай, если пароль меняют потому, что доступ к аккаунту мог утечь. Вход по коду продолжает работать и после того, как вы завели пароль — это два способа, а не замена одного другим. ## Яндекс ID и VK ID На странице входа есть кнопки «Войти с Яндекс ID» и «Войти через VK ID». Если почта в социальном профиле совпадает с почтой существующего аккаунта, вход произойдёт в него же — второй аккаунт не создастся. ## Если доступ потерян * **Не приходит письмо с кодом.** Проверьте спам и правильность адреса. Почтовые провайдеры иногда задерживают письма на несколько минут. * **Забыт пароль.** Войдите по коду на почту и задайте новый в профиле — отдельной формы восстановления нет, она и не нужна. * **Нет доступа к почте.** Напишите в [поддержку](/docs/support) с адреса, к которому доступ есть, и опишите ситуацию: мы проверим принадлежность аккаунта. * **Открывается пустой кабинет вместо формы входа.** Обновите страницу: сеанс закончился, и приложение само вернёт вас на форму. ## Что дальше * [Ключи и проекты](/docs/dashboard/keys-and-projects) — создать первый ключ * [Кабинеты клиентов](/docs/dashboard/client-accounts) — пустить подрядчика в свой кабинет * [Аутентификация в API](/docs/authentication) — как ключ передаётся в запросах --- # 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) — полная схема параметров. --- # Аудио в чате (запись на вход, голос на выход) (/docs/features/audio) > Модель слушает аудиозапись и рассуждает о ней — либо отвечает голосом. Content-part input_audio, modalities audio, стриминг delta.audio. import { CodeTabs } from '@/components/docs/code-tabs'; import { EndpointBadge } from '@/components/docs/endpoint-badge'; Обычный [`/v1/chat/completions`](/docs/api/chat-completions) умеет работать со звуком в обе стороны: * **Аудио на вход** — вы прикладываете запись к сообщению, и модель отвечает на вопросы о ней: кто говорит, каким тоном, что за шум на фоне, о чём договорились в звонке. * **Голос на выход** — модель озвучивает свой ответ, аудио приходит в потоке вместе с текстовой расшифровкой. Не путайте с [транскрибацией](/docs/api/transcriptions): `/v1/audio/transcriptions` решает одну задачу — превратить запись в текст, дёшево и предсказуемо. Аудио в чате — это рассуждение о записи (можно совмещать с tool calling и структурированным выводом) и разговорный сценарий. Если нужен просто текст расшифровки, берите транскрибацию — она для этого и сделана. ## Эндпоинт ## Аудио на вход Запись передаётся как content-part типа `input_audio` внутри user-сообщения — рядом с обычным текстом: ```json { "role": "user", "content": [ { "type": "text", "text": "О чём эта запись и каким тоном говорят?" }, { "type": "input_audio", "input_audio": { "data": "UklGRiQAAABXQVZFZm10...", "format": "wav" } } ] } ``` | Поле | Тип | Описание | | ------------------------ | --------------- | ------------------------------------------------------------------------------ | | **`type`** | `"input_audio"` | Дискриминатор content-part. | | **`input_audio.data`** | `string` | Аудио в base64 **без** префикса `data:audio/...;base64,` — только сами данные. | | **`input_audio.format`** | `string` | `wav`, `mp3`, `aiff`, `aac`, `ogg`, `flac`, `m4a`, `pcm16`, `pcm24`. | Ссылки на аудио не поддерживаются — только base64. Base64 раздувает запрос примерно на треть, поэтому длинные записи лучше резать: тело запроса ограничено 36 МБ, минута WAV — это около 2,7 МБ. ### Минимальный пример Официальные `openai` SDK для Python и TypeScript работают без правок — достаточно поменять `base_url` / `baseURL`. ### Сколько это стоит в токенах Аудио превращается в отдельный подвид prompt-токенов. В ответе они видны в `usage.prompt_tokens_details.audio_tokens` и входят в общий `usage.prompt_tokens`: ```json { "usage": { "prompt_tokens": 70, "completion_tokens": 38, "total_tokens": 108, "prompt_tokens_details": { "audio_tokens": 50 }, "cost": 9 } } ``` ### Какие модели принимают аудио Те, у которых в карточке ([`GET /v1/models`](/docs/api/models)) поле `input_modalities` содержит `"audio"` — в [каталоге](/models) это фильтр «принимает аудио». Обратите внимание: под фильтр попадают и модели распознавания речи (Whisper, Voxtral, Nova и другие) — они предназначены для [`/v1/audio/transcriptions`](/docs/api/transcriptions), а не для чата. Для аудио в диалоге берите чат-модели: семейство Gemini, `openai/gpt-audio` и подобные. Если модель аудио не принимает, запрос отклоняется **до** обращения к провайдеру — с кодом `400` и `code: "audio_input_not_supported"`. За такой вызов ничего не списывается. ## Голос на выход Если диалог не нужен и требуется просто озвучить готовый текст, берите [`/v1/audio/speech`](/docs/api/speech): один запрос — один аудиофайл, дешевле и предсказуемее. Всё, что ниже, — про голосовой ответ модели внутри чата. Чтобы модель ответила голосом, нужны три вещи одновременно: 1. `modalities: ["text", "audio"]`; 2. объект `audio` с голосом и форматом; 3. **`stream: true`** — иначе `400` с `code: "audio_output_requires_stream"`. | Поле | Тип | Описание | | ------------------ | --------------- | ----------------------------------------------- | | **`modalities`** | `array` | `["text","audio"]`. | | **`audio.voice`** | `string` | Голос озвучки, например `alloy`. Список — ниже. | | **`audio.format`** | `string` | Формат ответа: `wav`, `mp3`, `pcm16` и т. п. | | **`stream`** | `boolean` | Обязательно `true`. | ```bash curl -N -s https://api.hubris.pw/v1/chat/completions \ -H "Authorization: Bearer sk-gw-..." \ -H "Content-Type: application/json" \ -d '{ "model": "openai/gpt-audio", "modalities": ["text", "audio"], "audio": {"voice": "alloy", "format": "pcm16"}, "stream": true, "messages": [{"role": "user", "content": "Скажи «Привет!»"}] }' ``` Звук приходит частями внутри обычных SSE-чанков, в поле `delta.audio`: ```json { "id": "chatcmpl-abc123", "object": "chat.completion.chunk", "choices": [ { "index": 0, "delta": { "audio": { "id": "audio_abc123", "data": "GAAaABwAHgAgACIAJAAmACgA...", "transcript": "Привет" } } } ] } ``` | Поле | Описание | | ------------------------ | -------------------------------------------------------------------------------------------------------------- | | `delta.audio.id` | Идентификатор аудио-фрагмента ответа. | | `delta.audio.data` | Кусок аудио в base64, в формате из `audio.format`. Склеивайте куски по порядку и декодируйте один раз в конце. | | `delta.audio.transcript` | Текстовая расшифровка того, что модель произносит в этом куске. | Расшифровка удобна, чтобы показывать субтитры в момент воспроизведения и сохранять реплику в историю диалога. Общий формат SSE, `[DONE]` и сбор `usage` — на странице [Стриминг](/docs/features/streaming). Токены озвучки приходят в `completion_tokens_details.audio_tokens` (подмножество `completion_tokens`). ### Какие бывают голоса Для моделей семейства `openai/gpt-audio*` доступны тринадцать голосов: `alloy`, `echo`, `fable`, `onyx`, `nova`, `shimmer`, `coral`, `verse`, `ballad`, `ash`, `sage`, `marin`, `cedar`. Отдельного справочника голосов у провайдеров нет, и в карточке модели они не публикуются. Если голос указан неверно, ответ приходит с кодом `400`, а в тексте ошибки провайдер перечисляет все допустимые значения для этой модели — это и есть способ узнать список для любой новой модели: ```json { "error": { "message": "Invalid value: 'нет-такого'. Supported values are: 'alloy', 'echo', 'fable', ...", "type": "invalid_request_error", "code": "invalid_value" } } ``` ### Модели с голосовым ответом Их сильно меньше, чем принимающих аудио: нужно `"audio"` в `output_modalities` карточки модели — например `openai/gpt-audio` и `openai/gpt-audio-mini`. Актуальный список — в [каталоге](/models). Запрос `modalities: ["text","audio"]` к модели без голосового выхода отклоняется с `400` и `code: "audio_output_not_supported"`. Не путайте с генерацией музыки: у моделей вроде `google/lyria-3-pro-preview` на выходе тоже аудио, но это сочинённый трек по текстовому описанию, а не проговоренный ответ. Они не ведут диалог голосом и оплачиваются за клип или песню — смотрите цену на карточке модели. ### Ограничение: аудио не переиспользуется по `id` Ссылаться в следующем запросе на прошлый ответ по `audio.id` нельзя — идентификатор живёт в пределах одного ответа. Историю мультитёрн-диалога ведите текстом: кладите в `messages` assistant-сообщение с `content` или с `audio.transcript` предыдущей реплики. Сами байты озвучки в историю отправлять не нужно — это только раздует запрос. ## HTTP-коды | Код | `code` | Когда | | ----------------- | ------------------------------ | -------------------------------------------------------------------------------------------- | | `200` | — | Успешный ответ. | | `400` | `audio_input_not_supported` | В сообщениях есть `input_audio`, а модель не принимает аудио. | | `400` | `audio_output_not_supported` | В `modalities` есть `audio`, а модель не умеет отвечать голосом. | | `400` | `audio_output_requires_stream` | Голосовой ответ запрошен без `stream: true`. | | `400` | — | Тело не соответствует схеме (например, `data` с префиксом `data:` или неизвестный `format`). | | `401` | — | Ключ отсутствует / отозван / невалиден. | | `402` | — | Недостаточно средств на балансе. | | `413` | `request_too_large` | Тело запроса больше 36 МБ. | | `429` | — | Превышен дневной лимит на ключе. | | `502 / 503 / 504` | — | Транзиентные сбои у провайдера или курса ЦБ. | Формат тела ошибки и стратегия retry — [Ошибки](/docs/concepts/errors). ## Биллинг Аудио-токены тарифицируются по отдельной ставке модели — обычно она выше текстовой, и на вход и на выход ставки разные. Итоговая стоимость запроса приходит в `usage.cost` в копейках: это ровно та сумма, что списывается с баланса и попадает в [журнал запросов](/logs), с уже учтённой разницей ставок. Списание — по факту завершённого запроса, итог приходит в `usage.cost` **в копейках** (как на всех остальных эндпоинтах) и совпадает с суммой в [логах](/logs). При обрыве соединения во время голосового стрима ответ дочитывается до конца и оплачивается полностью. ## Что дальше * [POST /v1/audio/transcriptions](/docs/api/transcriptions) — просто получить текст записи, без рассуждений модели. * [POST /v1/audio/speech](/docs/api/speech) — обратная задача: озвучить готовый текст одним запросом. * [Стриминг](/docs/features/streaming) — разбор SSE-чанков и сбор `usage`. * [POST /v1/chat/completions](/docs/api/chat-completions) — полная схема параметров. * [Изображения на вход](/docs/features/vision) — та же логика мультимодального ввода, только для картинок. * [Цены](/docs/concepts/pricing) — как считается `usage.cost`. --- # Обработка ошибок (/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, премиум-модели через /v1/images/generations с input_references. Hubris генерирует изображения двумя путями — какой использовать, зависит от модели: | Линейка моделей | Эндпоинт | Вход для редактирования | | -------------------------------------------------------- | --------------------------------------------------------- | -------------------------------------------------------------- | | Google Gemini \*-image (Nano Banana), OpenAI GPT-5 Image | [`POST /v1/chat/completions`](/docs/api/chat-completions) | картинка в `messages` (как во [Vision](/docs/features/vision)) | | FLUX.2, Recraft, Seedream, Riverflow, Grok Imagine | [`POST /v1/images/generations`](/docs/api/images) | поле `input_references` | Первый путь — «диалоговый»: модель отвечает и текстом, и картинкой, умеет пошагово дорабатывать результат в переписке. Второй — классический Images API в формате OpenAI: один запрос → готовые изображения, фиксированная цена за штуку. Полный список моделей — фильтр «Output: image» в [каталоге](/models); на карточке модели видно, как она тарифицируется: токенами (chat-путь) или за изображение (images-путь). ## Генерация через chat completions Передайте `modalities` и модель с поддержкой image-output: ```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": 1290, "total_tokens": 1308 } } ``` `modalities: ["image", "text"]` — модель отдаёт и текст-комментарий, и картинку. Если передать `modalities` не-image модели, вернётся ошибка провайдера. ## Генерация через /v1/images/generations FLUX.2, Recraft, Seedream, Riverflow и Grok Imagine через chat completions недоступны (`404 model_not_found`) — они работают только через выделенный эндпоинт в формате OpenAI Images API: ```bash curl -s https://api.hubris.pw/v1/images/generations \ -H "Authorization: Bearer sk-gw-..." \ -H "Content-Type: application/json" \ -d '{ "model": "black-forest-labs/flux.2-pro", "prompt": "product photo of a ceramic mug on a linen tablecloth, soft window light" }' ``` Ответ — `data[].b64_json` (base64 **без** префикса `data:`), как у OpenAI. Официальные SDK работают из коробки: ```python import base64 from openai import OpenAI client = OpenAI(base_url="https://api.hubris.pw/v1", api_key="sk-gw-...") resp = client.images.generate( model="black-forest-labs/flux.2-pro", prompt="product photo of a ceramic mug on a linen tablecloth", ) open("mug.png", "wb").write(base64.b64decode(resp.data[0].b64_json)) ``` Совет по FLUX: пишите промпт на английском — кириллицу эта линейка норовит отрисовать как текст на самой картинке. Gemini, Seedream и Recraft русский понимают нормально. Все поля запроса, коды ошибок и биллинг — в референсе [POST /v1/images/generations](/docs/api/images). ## Редактирование изображений Оба пути умеют image-to-image: подаёте исходную картинку + текст, что с ней сделать. Картинка передаётся как data URL с base64 (надёжно у всех провайдеров) или как публичная https-ссылка. ### Через chat completions (Gemini, GPT-5 Image) Исходное изображение кладётся в `content`-массив сообщения — ровно так же, как во [Vision](/docs/features/vision): ```bash IMG=$(base64 -w0 photo.jpg) curl -s https://api.hubris.pw/v1/chat/completions \ -H "Authorization: Bearer sk-gw-..." \ -H "Content-Type: application/json" \ -d @- < Запасные модели на случай 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": { "unit": "token", "input_rub_per_million": 88, "output_rub_per_million": 440, "currency": "RUB", "is_free": false } } // ... ] } ``` Поля каждой модели: | Поле | Что | | -------------------------------- | ---------------------------------------------------------------------- | | `id` | идентификатор для использования в `model` запроса | | `owned_by` | провайдер модели — `anthropic`, `openai`, `google`, `deepseek`, и т.д. | | `display_name` | человекочитаемое имя (для UI каталога) | | `description` | короткое описание возможностей | | `context_window` | размер контекста в токенах | | `input_modalities` | что принимает на вход: `text`, `image` | | `output_modalities` | что выдаёт: `text`, `image` | | `pricing.unit` | единица тарификации: `token`, `unit` или `unknown` (см. ниже) | | `pricing.input_rub_per_million` | цена в рублях за 1М prompt-токенов (при `unit: "token"`) | | `pricing.output_rub_per_million` | цена в рублях за 1М completion-токенов (при `unit: "token"`) | | `pricing.per_unit` | цены за другую единицу — изображение, мегапиксель, минуту | | `pricing.is_free` | бесплатна ли модель | В ответ попадают только активные модели — снятые с публикации не приходят. ### Единица тарификации Считать стоимость по `input_rub_per_million` можно, только когда `pricing.unit` равен `token`. Остальные значения: | `pricing.unit` | Что значит | | -------------- | ------------------------------------------------------------------------------------------------------------------------------------ | | `token` | цена за токены, поля `*_rub_per_million` осмысленны | | `unit` | оплата за другую единицу (изображение, мегапиксель, минуту записи); реальные ставки — в `per_unit`, а `*_rub_per_million` равны нулю | | `unknown` | провайдер не публикует тариф; списывается фактическая стоимость запроса | Так, у моделей распознавания речи запрос стоит рублей за минуту записи, а не за миллион токенов, — умножение токенной ставки на число токенов даст ошибку в тысячи раз. ## Сценарии фильтрации ### «Хочу самую дешёвую модель с поддержкой изображений на вход» ```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( # Сравнивать по цене за токены можно только модели с токенной тарификацией. [m for m in vision_models if m["pricing"]["unit"] == "token"], 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( [m for m in resp["data"] if m["pricing"]["unit"] == "token"], 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: { unit: 'token' | 'unit' | 'unknown'; input_rub_per_million: number; output_rub_per_million: number; per_unit?: Array<{ sku: string; price_rub: number; unit: string }>; currency: 'RUB'; is_free: boolean; }; }; 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) спасают. * **Цену за токены считайте только при `pricing.unit === "token"`.** У моделей с оплатой за изображение, секунду видео или минуту записи эти поля равны нулю — реальные цены лежат в `pricing.per_unit`. * **Бесплатность определяйте по `pricing.is_free`, а не по нулевым ценам за токены.** У моделей с оплатой в других единицах (за изображение, за секунду видео) цена за токены нулевая, а запрос платный. Подробности — [бесплатные модели](/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** автоматически удаляет ПД из запроса, а в ответе восстанавливает их обратно — модель работает с обезличенным текстом, вы получаете осмысленный ответ. Фича **бесплатна** для всех моделей каталога. > **Что Privacy Mode не покрывает.** Маскирование работает для текстовых запросов Chat Completions. Генерация изображений и видео (в т.ч. публичный [`/v1/videos`](/docs/api/videos)) уходит провайдеру без маскирования и не под Zero Data Retention — не передавайте туда персональные данные третьих лиц. ## Что маскируется | Тип | Примеры | | ---------------- | ------------------------------------------------ | | ФИО | Иван Петрович Сидоров | | 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 дней. --- # Кеширование промптов (/docs/features/prompt-caching) > Как работает кеш промптов, почему cached_tokens бывает 0 и как добиться стабильной экономии на повторных запросах. Кеширование промптов экономит деньги, когда вы много раз отправляете запросы с одним и тем же длинным началом — системной инструкцией, описанием инструментов, большим документом. Провайдер запоминает обработанное начало промпта, и при повторном запросе эти токены оплачиваются по сниженной ставке — обычно в разы дешевле обычного входа. Скидка применяется автоматически: Hubris списывает стоимость по фактической цене исполнения, в которой кеш уже учтён. Отдельно ничего включать и оплачивать не нужно. ## Два вида кеширования **Автоматическое** — у большинства современных моделей (DeepSeek, GPT, Gemini, Qwen и другие). Провайдер сам находит совпадающее начало промпта и применяет скидку. От вас требуется одно: начало промпта должно повторяться байт в байт. **Явное** — у Anthropic-моделей (Claude). Кешируемый блок надо пометить маркером `cache_control` — [см. ниже](#явное-кеширование-anthropic). ## Как проверить, что кеш сработал Смотрите поле `usage.prompt_tokens_details.cached_tokens` в ответе: ```json { "usage": { "prompt_tokens": 351, "completion_tokens": 16, "prompt_tokens_details": { "cached_tokens": 256 } } } ``` `cached_tokens` — сколько входных токенов прочитано из кеша по сниженной ставке. Ноль — кеш в этом запросе не сработал. ## Почему `cached_tokens` бывает 0 Это самый частый вопрос, поэтому по пунктам. **1. Кеш живёт у конкретного провайдера инференса.** Открытые модели (DeepSeek, Qwen, Llama и т. п.) обслуживают десятки независимых дата-центров, и каждый запрос маршрутизируется на доступный в этот момент. Кеш одного провайдера невидим для другого: если первый запрос обработал один дата-центр, а повторный улетел в другой — кеша там нет. Часть провайдеров кеширование вообще не поддерживает. Какие провайдеры обслуживают модель, видно в её карточке в [каталоге](/models). **2. Минимальная длина и блочность.** Кешируется только достаточно длинное начало промпта, причём блоками фиксированного размера (обычно кратно 64–256 токенам, у некоторых провайдеров минимум — 1024 токена). Промпт на 100–200 токенов может не закешироваться нигде. **3. Начало промпта должно совпадать точно.** Любое изменение в начале — дата в системной инструкции, перестановка инструментов, другой порядок сообщений — сбрасывает совпадение с этой позиции. Всё переменное ставьте в конец промпта. **4. Кеш не вечен.** Время жизни — от минут до часов в зависимости от провайдера. Редкие запросы (раз в час) в кеш обычно не попадают. ## Как добиться стабильных кеш-хитов Главный инструмент — закрепить провайдера полем `provider` в запросе, тогда повторные запросы будут попадать в один и тот же дата-центр: ```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", "provider": {"order": ["deepinfra"], "allow_fallbacks": false}, "messages": [ {"role": "system", "content": "Длинная стабильная инструкция..."}, {"role": "user", "content": "Вопрос"} ] }' ``` * `order` — список провайдеров в порядке предпочтения. Имя — в нижнем регистре, без пробелов (провайдер `DeepInfra` → `deepinfra`); список провайдеров модели есть в её карточке в [каталоге](/models). * `allow_fallbacks: false` — не переключаться на других провайдеров. Надёжно для кеша, но если выбранный провайдер недоступен, запрос вернёт ошибку вместо ответа от другого. Без этого флага `order` задаёт приоритет, но при недоступности запрос уйдёт к следующему доступному. Живой пример: два одинаковых запроса к `deepseek/deepseek-v4-flash` (промпт 351 токен) с закреплённым `deepinfra` — первый вернул `cached_tokens: 64`, повторный — `cached_tokens: 256`. Тот же тест без закрепления провайдера легко даёт 0: запросы разлетаются по разным дата-центрам. И общие правила, независимо от провайдера: * **Стабильное начало.** Системная инструкция и инструменты — в начале и без изменений, всё переменное (данные пользователя, текущая дата) — в конце. * **Частота.** Серии запросов подряд кешируются отлично, одиночные редкие — нет. * **Длина.** Чем длиннее общий префикс, тем больше экономия. ## Явное кеширование (Anthropic) Claude-модели кешируют только помеченные блоки. Поставьте `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": "Ваш вопрос" } ] }' ``` Требования Anthropic: блок от 1024 токенов, время жизни кеша \~5 минут (продлевается при каждом попадании). Чтение из кеша заметно дешевле обычного входа, запись в кеш — немного дороже (примерно +25 %), поэтому кешировать имеет смысл то, что будет переиспользовано. ## Кеш в статистике В ответе `GET /v1/usage` и в кабинете (вкладка «Активность») видна сводка по кешу за период: | Поле | Описание | | -------------------- | ----------------------------------------------------------------- | | `cache_read_tokens` | Токены, прочитанные из кеша. | | `cache_write_tokens` | Токены, записанные в кеш (у моделей с явным кешированием). | | `cache_hit_rate` | Доля входных токенов, пришедшихся на кеш (0..1). | | `cache_savings_rub` | Сколько сэкономлено за период относительно полной входной ставки. | ## Что кешировать Эффективные кандидаты: * **Системная инструкция с правилами и примерами** (1–20 тыс. токенов) — основная экономия. * **RAG-контекст** — одни и те же документы во многих запросах подряд. * **История долгого диалога** — каждый следующий ход переиспользует общий префикс. * **Длинный few-shot пример** — для классификаторов и агентов. Не даст экономии: * Уникальные короткие запросы без общего начала. * Промпты меньше минимального порога кеширования. * Редкие запросы — кеш истекает между ними. ## Что дальше * [POST /v1/chat/completions](/docs/api/chat-completions) — полная схема запроса. * [Каталог моделей](/models) — провайдеры и цены каждой модели, включая ставку кеш-чтения. * [Отслеживание расходов](/docs/features/usage-tracking) — как смотреть статистику. --- # 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, что и в первом запросе */ ] }' ``` В assistant-сообщении истории поле `content` может быть строкой, `null` или массивом текстовых блоков (`[{"type": "text", "text": "..."}]`) — как у OpenAI. Пустой массив `[]` (его шлёт, например, n8n начиная с версии 2.31) тоже принимается и трактуется как `null`. Модель ответит текстом с учётом результата: ```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; total_tokens: number; cache_read_tokens: number; cache_write_tokens: number; reasoning_tokens: number; cache_hit_rate: number; cache_savings_kopecks: string; cache_savings_rub: 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 тоже совмещаются.** Модель может смотреть на картинку и вызывать функции на основе того, что увидела. * **IDE-агенты (Kilo Code, Roo Code, Cline) могут не отправлять картинку.** Такие расширения сами решают, поддерживает ли модель изображения, и для подключений типа «OpenAI Compatible» по умолчанию считают модель текстовой — тогда картинка не доходит до модели, даже если сама модель vision поддерживает. Лечится в настройках агента (в Kilo — поле `modalities` в `kilo.jsonc`, см. [Kilo Code](/docs/integrations/kilo-code)). ## Что дальше * [Генерация изображений](/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) — детализация запросов и стоимости. --- # Claude Code (VS Code) (/docs/integrations/claude-code) > Подключение Claude Code (CLI и расширение VS Code) к Hubris через эндпоинт /v1/messages — Fable, Sonnet, Opus, Haiku, reasoning effort, кэширование, статусная строка. [Claude Code](https://code.claude.com/docs/en/overview) — официальный агент Anthropic. Работает в терминале и как расширение VS Code, понимает контекст проекта, умеет редактировать файлы, исполнять команды, читать скриншоты, держит долгую сессию с кэшированием промптов. Поддерживает любой Anthropic-совместимый бэкенд через переменные окружения. Hubris подходит из коробки — и для CLI, и для VS Code. ## Что вы получите * Доступ ко всему семейству Claude (Fable 5, Sonnet 5, Opus 4.8, Haiku 4.5 и предыдущие поколения) **с оплатой в рублях**. * Не только Claude: [любая чат-модель каталога](#любые-чат-модели-каталога--gpt-gemini-deepseek-и-другие) — GPT, Gemini, DeepSeek и другие. * Нативное `prompt caching` для длинных сессий — экономит до десятков рублей на каждом турне. * Чтение скриншотов и других изображений (vision). * Использование инструментов (tool use) для редактирования файлов и запуска команд. * Опциональная statusline в TUI с балансом, расходом сессии и текущей моделью. ## Требования * macOS / Linux / WSL / современный терминал на Windows — либо VS Code с расширением Claude Code. * Аккаунт на [hubris.pw](https://hubris.pw/dashboard) и API-ключ формата `sk-gw-…` (создать на [/keys](https://hubris.pw/keys)). * Положительный баланс — пополнить можно [здесь](https://hubris.pw/billing). ## Установка Claude Code Подробная инструкция — в [официальной документации](https://code.claude.com/docs/en/quickstart). Варианты: * **VS Code**: Extensions (`Ctrl+Shift+X`) → «Claude Code» (издатель Anthropic) → Install. * **CLI**: ```bash npm install -g @anthropic-ai/claude-code ``` Альтернатива — однострочный установщик: ```bash curl -fsSL https://claude.ai/install.sh | bash ``` Убедитесь, что Claude Code запускается: `claude --version` (для CLI) или иконка Claude в сайдбаре VS Code. ## Подключение к Hubris Рекомендуемый способ — блок `env` в файле `~/.claude/settings.json` (Windows: `C:\Users\<имя>\.claude\settings.json`). Его подхватывают **и CLI, и расширение VS Code**, поэтому конфиг настраивается один раз: ```json { "env": { "ANTHROPIC_AUTH_TOKEN": "sk-gw-ВАШ_КЛЮЧ", "ANTHROPIC_BASE_URL": "https://api.hubris.pw", "ANTHROPIC_API_KEY": "", "ANTHROPIC_DEFAULT_HAIKU_MODEL": "anthropic/claude-haiku-4.5", "ANTHROPIC_DEFAULT_SONNET_MODEL": "anthropic/claude-sonnet-5", "ANTHROPIC_DEFAULT_OPUS_MODEL": "anthropic/claude-opus-4.8", "ANTHROPIC_DEFAULT_OPUS_MODEL_SUPPORTED_CAPABILITIES": "effort,xhigh_effort,max_effort,thinking,adaptive_thinking,interleaved_thinking", "ANTHROPIC_MODEL": "anthropic/claude-opus-4.8", "ANTHROPIC_SMALL_FAST_MODEL": "anthropic/claude-haiku-4.5" } } ``` Пояснения к неочевидным строкам: * `ANTHROPIC_API_KEY: ""` — предохранитель: если в системе уже лежит настоящий ключ Anthropic, пустая строка не даст Claude Code случайно отправить его на шлюз. * `ANTHROPIC_DEFAULT_OPUS_MODEL_SUPPORTED_CAPABILITIES` — возвращает ползунок reasoning effort, см. [ниже](#reasoning-effort-ползунок-lowmediumhigh). * Вход в аккаунт Anthropic не нужен — авторизация идёт по ключу `sk-gw-…`. После сохранения файла перезапустите VS Code (или откройте новый терминал) и запустите агента из папки проекта. Первый же запрос уйдёт в Hubris вместо `api.anthropic.com` и спишется с вашего баланса в рублях — проверить можно на [странице Использование](https://hubris.pw/usage). Альтернатива для CLI — обычные переменные окружения в `~/.bashrc` / `~/.zshrc` (или профиле PowerShell): ```bash export ANTHROPIC_BASE_URL="https://api.hubris.pw" export ANTHROPIC_AUTH_TOKEN="sk-gw-..." ``` ## Выбор модели В рабочей сессии переключайте модель командой `/model`. Полный список — в [каталоге](https://hubris.pw/models?provider=anthropic). Какие имена принимает Hubris: * **Канонические id с провайдером** — `anthropic/claude-opus-4.8`, `anthropic/claude-sonnet-5`, `anthropic/claude-fable-5`. Работают для всех моделей каталога — **рекомендуем именно их**. * **Короткие Anthropic-имена** — `claude-sonnet-4-5`, `claude-opus-4-7`, `claude-haiku-4-5` (в том числе с датой или `-latest` на конце). Поддерживаются для поколений до Sonnet 4.6 / Opus 4.7 включительно. Чтобы зафиксировать модель по умолчанию, задайте `ANTHROPIC_MODEL` в конфиге выше или передайте её при запуске CLI: ```bash claude --model anthropic/claude-sonnet-5 ``` ### Какая модель когда подходит | Модель | Когда брать | | ---------------------------- | ----------------------------------------------------------------------------- | | `anthropic/claude-haiku-4.5` | быстрые ответы, поиск по коду, простые правки, дёшево. | | `anthropic/claude-sonnet-5` | основной рабочий вариант: качественные правки, рефакторинги, длинные сессии. | | `anthropic/claude-opus-4.8` | сложные архитектурные задачи, миграции, разбор багов в больших кодовых базах. | | `anthropic/claude-fable-5` | максимум качества для самых трудных задач; заметно дороже Opus. | ## Reasoning effort: ползунок low/medium/high Claude Code показывает выбор глубины рассуждения (low / medium / high) только для моделей, чьи имена он узнаёт. Канонические id вида `anthropic/claude-opus-4.8` под встроенные шаблоны не подпадают, поэтому без дополнительной настройки ползунок пропадает — хотя сама модель effort полностью поддерживает. Решение — объявить возможности модели явно через переменную `ANTHROPIC_DEFAULT_OPUS_MODEL_SUPPORTED_CAPABILITIES` (уже включена в рекомендуемый конфиг выше). После перезапуска ползунок возвращается в `/model` и в VS Code. Аналогичная переменная с суффиксом `_SUPPORTED_CAPABILITIES` есть и у `ANTHROPIC_DEFAULT_SONNET_MODEL`. Управлять уровнем можно и без ползунка: * команда `/effort high` прямо в сессии; * поле `"effortLevel": "high"` в `~/.claude/settings.json` (вне блока `env`) — сохраняет выбор между сессиями; * флаг CLI `claude --effort high`. ## Любые чат-модели каталога — GPT, Gemini, DeepSeek и другие Claude Code через Hubris не ограничен моделями Anthropic. Эндпоинт [/v1/messages](/docs/api/messages) принимает **любую чат-модель каталога**: формат Anthropic конвертируется на стороне агрегатора, ответ приходит в привычном Claude Code виде — со стримингом, tool use и токен-статистикой. Проверено вживую на `openai/gpt-4o-mini`, `google/gemini-3.6-flash` и `deepseek/deepseek-chat`. Просто укажите канонический id модели из [каталога](https://hubris.pw/models) в тех же переменных: ```json { "env": { "ANTHROPIC_AUTH_TOKEN": "sk-gw-ВАШ_КЛЮЧ", "ANTHROPIC_BASE_URL": "https://api.hubris.pw", "ANTHROPIC_API_KEY": "", "ANTHROPIC_MODEL": "openai/gpt-4o-mini", "ANTHROPIC_DEFAULT_SONNET_MODEL": "google/gemini-3.6-flash", "ANTHROPIC_DEFAULT_OPUS_MODEL": "deepseek/deepseek-chat", "ANTHROPIC_SMALL_FAST_MODEL": "anthropic/claude-haiku-4.5" } } ``` Слоты переключателя `/model` (Haiku / Sonnet / Opus) — это просто «ярлыки»: какую модель подставить в каждый, решаете вы через `ANTHROPIC_DEFAULT_*_MODEL`. В сессии можно указать и произвольную модель напрямую: `/model google/gemini-3.6-flash`. ### Вариант конфигурации: настройки расширения VS Code Вместо `~/.claude/settings.json` те же переменные можно задать прямо в настройках VS Code — поле `claudeCode.environmentVariables` (массив объектов `{"name", "value"}`). Важно: настройка применяется только из **User Settings** (`Ctrl+Shift+P` → «Preferences: Open User Settings (JSON)»), в workspace-файле проекта она игнорируется. ```json { "claudeCode.environmentVariables": [ { "name": "ANTHROPIC_BASE_URL", "value": "https://api.hubris.pw" }, { "name": "ANTHROPIC_AUTH_TOKEN", "value": "sk-gw-ВАШ_КЛЮЧ" }, { "name": "ANTHROPIC_API_KEY", "value": "" }, { "name": "ANTHROPIC_MODEL", "value": "openai/gpt-4o-mini" } ] } ``` Не задавайте одну и ту же переменную и здесь, и в `~/.claude/settings.json` — выберите одно место, чтобы конфигурация оставалась предсказуемой. ### Ограничения * Работают только **чат-модели** (`text`-вывод). Image-, видео- и embedding-модели каталога через Claude Code недоступны. * Ползунок [reasoning effort](#reasoning-effort-ползунок-lowmediumhigh) показывается только для Claude-моделей — у остальных глубина рассуждений управляется самой моделью. * Экономия на [кэшировании промптов](#кэширование-промптов) — сильная сторона именно Claude-моделей; у других провайдеров кэш работает иначе или не работает вовсе. * Claude Code — агент, обученный в первую очередь под Claude. С другими моделями он работает, но качество агентных сценариев (инструменты, длинные сессии) может отличаться. ## 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 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` | модель по умолчанию. | | `ANTHROPIC_SMALL_FAST_MODEL` | модель для лёгких подзадач (резюмирование, выбор имени). Оставляйте `anthropic/claude-haiku-4.5`. | | `ANTHROPIC_DEFAULT_HAIKU_MODEL` / `..._SONNET_MODEL` / `..._OPUS_MODEL` | какие модели подставляются в стандартные пункты переключателя `/model`. | | `ANTHROPIC_DEFAULT_OPUS_MODEL_SUPPORTED_CAPABILITIES` | явное объявление возможностей модели (effort, thinking) — нужно при канонических id, см. раздел про reasoning effort. | | `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC` | `1` — отключает фоновые запросы Claude Code к сервисам Anthropic (телеметрия, проверка обновлений). Необязательно. | ## Дневные лимиты Если на ключе включён дневной лимит трат (можно поставить на [/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` появится позже. **Что делать, если Hubris отвечает `404 not_found_error` на модель?** Чаще всего это короткое имя новой модели (например `claude-opus-4-8`), которое шлюз пока не распознаёт. Используйте канонический id из [каталога](https://hubris.pw/models?provider=anthropic) — `anthropic/claude-opus-4.8` — или переключитесь командой `/model`. **Пропал ползунок low/medium/high у Opus — как вернуть?** Добавьте `ANTHROPIC_DEFAULT_OPUS_MODEL_SUPPORTED_CAPABILITIES` в блок `env` (см. [рекомендуемый конфиг](#подключение-к-hubris)) и перезапустите VS Code. Подробности — в разделе про [reasoning effort](#reasoning-effort-ползунок-lowmediumhigh). ## Что дальше * [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 пишет, что модель не поддерживает image input, **либо** картинка вроде бы отправляется, но модель отвечает что-то вроде «This model does not support image input, please provide details as text» — и так на любой модели, даже заведомо мультимодальной (`openai/gpt-4o`, Claude Sonnet, Gemini). Причина — в самом Kilo, а не в Hubris. Для провайдеров типа **OpenAI Compatible** Kilo определяет поддержку изображений **на своей стороне**, не запрашивая возможности модели у сервера, и по умолчанию считает такие модели текстовыми. В итоге расширение либо блокирует прикрепление, либо кладёт в запрос только имя файла без самой картинки — модель получает текст вроде `233.png`, но не пиксели, и закономерно отвечает, что изображения не видит. Решение — явно объявить модальности модели в файле `kilo.jsonc` (лежит в корне проекта или в `.kilo/kilo.jsonc`): ```jsonc { "provider": { "hubris": { "models": { "openai/gpt-4o": { "name": "openai/gpt-4o", "modalities": { "input": ["text", "image"], "output": ["text"] } } } } } } ``` * `hubris` — тот же **Provider ID**, что вы задали при подключении. * Ключ модели (`openai/gpt-4o`) должен точно совпадать с тем, что выбран в Kilo. * В `input` перечислите реально поддерживаемые модальности — для работы с картинками обязателен `"image"`. Поддерживает ли модель изображения, видно в [каталоге](/models) (фильтр «принимает изображения»). * Пропишите так каждую модель, которой будете отправлять картинки. После правки перезагрузите окно: `Ctrl+Shift+P` → **Developer: Reload Window**. Со стороны Hubris vision работает в полном объёме: `/v1/chat/completions` принимает изображения и публичным URL, и base64 data URL — подробности в [Изображения на вход](/docs/features/vision). Ограничение здесь чисто на стороне Kilo. ### Kilo не справляется с задачей / зацикливается * Слабая модель плохо ведёт себя как агент. Попробуйте более сильную из каталога с явной поддержкой tool calling. * Уменьшите размер задачи: дайте Kilo более узкую цель. ## Что дальше * [Каталог моделей](/models) — выбор по поддержке tool calling. * [Управление API-ключами](/keys) — отдельный ключ под Kilo с дневным лимитом. * [Расходы](/usage) — детализация сессий. * [Cline](/docs/integrations/cline), [Roo Code](/docs/integrations/roo-code) — альтернативные VS Code агенты. --- # Make (/docs/integrations/make) > Подключение Hubris к Make (бывший Integromat) — приложение с модулями «Создать текстовый ответ» и «Сгенерировать изображение», готовый сценарий для блога на WordPress и универсальный путь через HTTP-модуль. [Make](https://www.make.com) (бывший Integromat) — визуальный конструктор автоматизаций: сценарий собирается из модулей мышкой, без кода. Hubris подключается двумя способами: * **Готовое приложение Hubris** — два модуля, «Создать текстовый ответ» и «Сгенерировать изображение». Список моделей подтягивается прямо из вашего аккаунта, изображение приходит готовым файлом. Это основной путь. * **HTTP-модуль** — универсальный запасной вариант: работает с любым нашим эндпоинтом, включая те, которых нет в приложении (озвучка, транскрибация, эмбеддинги, видео). Приложение для Make собрала студия [web-steps.ru](https://web-steps.ru/) — не команда Hubris. Мы отвечаем за API и за то, что описано ниже; вопросы по устройству самого приложения, доработки и поддержка — на стороне автора. ## Требования * Аккаунт [Make](https://www.make.com) * Аккаунт на [hubris.pw](https://hubris.pw/dashboard), пополненный баланс и API-ключ формата `sk-gw-...` Приложение ставится на аккаунт любого тарифа. Учтите только: Make относит сторонние приложения к платным категориям, и на бесплатном тарифе запуск сценария может упереться в сообщение вида «premium app» — это ограничение Make, а не наше. Тогда пригодится HTTP-модуль (см. ниже) — он штатный и такого ограничения не имеет. ## Установка приложения Приложение не опубликовано в общем каталоге Make — оно ставится по ссылке-приглашению. 1. Войдите в свой аккаунт Make. 2. Откройте ссылку: **[Установить приложение Hubris](https://us2.make.com/app/invite/34a849c2d356583d3a4663d59a5c5b13)** 3. Подтвердите установку. Приложение появится в вашей организации — дальше оно ищется в списке модулей по слову `Hubris`. Ссылка ведёт на зону `us2`. Если ваш аккаунт живёт в другой зоне (`eu1`, `eu2`, `us1` — видно в адресной строке Make), сначала войдите в Make, а потом откройте ссылку: установка идёт в ту организацию, которая выбрана в вашей сессии. ## Создание подключения Подключение (Connection) создаётся один раз и потом переиспользуется во всех модулях сценария. 1. Добавьте в сценарий любой модуль Hubris. 2. В поле **Connection** нажмите **Add**. 3. Заполните: | Поле | Значение | | ------------------- | ---------------------------------------------------------------------------------- | | **Connection name** | любое понятное имя, например `Hubris` | | **API Key** | ваш ключ формата `sk-gw-...` — создать в [личном кабинете](https://hubris.pw/keys) | 4. Сохраните. Make сразу проверит ключ запросом к нашему каталогу моделей — если подключение создалось, ключ рабочий. Ключ живёт в хранилище подключений Make и в самом сценарии не показывается. Разумно завести под Make отдельный ключ с [дневным лимитом](/keys) — тогда ошибка в сценарии (например, бесконечный цикл) не съест весь баланс. ## Модуль «Создать текстовый ответ» Отправляет запрос к языковой модели и возвращает текст. Подходит для генерации статей и писем, кратких пересказов, классификации обращений, извлечения данных из текста. ### Параметры | Поле | Что задаёт | | -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Провайдер** | Семейство моделей: ChatGPT, Claude, Gemini, DeepSeek, Llama, Mistral, Qwen, Grok, Kimi, GLM. Это фильтр для следующего поля. | | **Model** | Конкретная модель. Список подгружается из вашего аккаунта Hubris при открытии модуля — то есть всегда актуальный. | | **Prompt** | Основной текст запроса. Поле многострочное; чтобы подставлять данные предыдущих шагов, включите режим сопоставления (иконка **Map**) и вставляйте переменные вида `{{1.text}}`. | | **Prompt (2-е сообщение)** | Второе сообщение с ролью `system`. Спрятано под **Show advanced settings**. Удобно, когда одна часть промпта постоянная (правила, тон, формат), а вторая приходит из сценария. Если поле заполнено, первое сообщение тоже отправляется как `system`. | | **Role** | Роль первого сообщения: `user`, `system` или `assistant`. По умолчанию `user`. | | **Max Tokens** | Потолок длины ответа в токенах. По умолчанию 6000. | | **Temperature** | Разброс ответов, от 0 до 2. Ниже — стабильнее и суше, выше — свободнее. По умолчанию 1. | | **Top P** | Альтернативный способ ограничить разброс, от 0 до 1. По умолчанию 1. Обычно трогают либо Temperature, либо Top P. | | **Parse JSON Response** | Требует от модели ответ строго в JSON и разбирает его в поле `Result`. Подробности ниже. | | **Response Format** | Селектор `Text` / `JSON Object`. Фактически формат задаёт галка **Parse JSON Response**, этот селектор на запрос не влияет — ориентируйтесь на галку. | Переменной подставляются только **Prompt** и **Prompt (2-е сообщение)**. Всё остальное — включая **Провайдер** и **Model** — задаётся руками в панели модуля. Если модель нужно переключать по ходу сценария (например, дешёвая для простых заявок, сильная для сложных), поставьте перед модулями **Router** с двумя ветками или используйте HTTP-модуль. ### Что возвращает | Поле | Значение | | ---------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Text Response** | Ответ модели текстом — то, что нужно в 90 % сценариев. | | **Result** | Разобранный JSON (только при включённом **Parse JSON Response**). | | **Model** | Идентификатор модели, которая реально ответила. | | **ID** | Идентификатор ответа модели (`chatcmpl-...`) — пригодится в переписке с поддержкой. | | **Finish Reason** | Почему модель остановилась: `stop` — договорила сама, `length` — упёрлась в Max Tokens. | | **Usage → Prompt / Completion / Total Tokens** | Сколько токенов ушло на запрос и ответ. | | **Usage → Cost** | Стоимость запроса **в копейках**. `1250` — это 12,50 ₽. Удобно складывать за прогон и писать в таблицу. Изредка поле приходит пустым (модель не отдала стоимость) — списание при этом всё равно происходит, точную сумму смотрите в [логах](/logs). | ### Ответ в формате JSON Когда нужен не связный текст, а структура (заголовок, описание, теги), включите **Parse JSON Response**. Эта галка — единственный переключатель формата: с ней модуль требует от модели строгий JSON и кладёт разобранный ответ в поле `Result`; без неё запрос уходит за обычным текстом, даже если селектор **Response Format** стоит в `JSON Object`. Оговорка про `Result`: в списке для сопоставления у него показаны заранее заданные ключи — `filename`, `title`, `alt`, `caption`, `info`. Их зашил автор приложения под свой сценарий. Если структура другая, поля не появятся в подсказках Make, но данные приходят целиком — обращайтесь к ним выражением вида `{{2.result.my_field}}`. Когда полей много и хочется видеть их в сопоставлении, добавьте после модуля штатный **JSON → Parse JSON** и разберите им `Text Response`. В самом промпте всегда описывайте нужную структуру словами и приводите пример — модель ориентируется на него. Строгий JSON поддерживают не все модели: если выбранная не понимает такой режим, запрос вернётся ошибкой — тогда выключите галку и опишите формат словами либо возьмите другую модель из [каталога](/models). Подробнее — в разделе [Структурированный вывод](/docs/features/structured-output). ## Модуль «Сгенерировать изображение» Рисует картинку по текстовому описанию и отдаёт её **готовым файлом** — дальше его можно сразу положить в Google Drive, загрузить в WordPress, отправить в Telegram или приложить к письму, без промежуточных модулей. ### Параметры | Поле | Что задаёт | | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Провайдер** | Семейство: Gemini Image, GPT Image, FLUX, Recraft, Seedream, Riverflow, Grok Imagine, MiniMax, Qwen Image, Wan. У MiniMax и Wan в каталоге сейчас нет моделей, рисующих картинки, — список моделей там будет пустым. | | **Model** | Конкретная модель, список подгружается из вашего аккаунта. | | **Prompt** | Описание картинки. Поддерживает сопоставление — можно собирать из данных предыдущих шагов. | | **Quality** | `Medium` — размер 1K, `High` — 2K. Влияет на стоимость. | | **Aspect Ratio** | `1:1`, `16:9`, `9:16`, `4:3`, `3:4`, `2:3`, `3:2`. Соотношения `2:3` и `3:2` отправляются как ближайшие поддерживаемые — `3:4` и `4:3`. | Quality и Aspect Ratio рассчитаны на линейку Gemini Image — там они работают предсказуемо. У остальных линеек размер и пропорции задаёт сама модель: FLUX, Recraft, Seedream и прочие эти поля игнорируют, у GPT Image соотношение сторон тоже может не примениться. Когда пропорции критичны, проверьте результат на одном прогоне. ### Что возвращает | Поле | Значение | | ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Data** | Само изображение — двоичный файл. Именно его принимают модули Google Drive, Dropbox, WordPress, Telegram, Email. | | **File Name** | Имя файла — `post-image` с расширением. Одинаковое на каждом прогоне: если складываете картинки в одну папку, переименуйте файл следующим модулем (например, по теме поста). | | **MIME Type** | Тип содержимого. У большинства линеек это всегда `image/png`. | | **Text Comment** | Текстовый комментарий модели к картинке, если она его дала. | | **Model** | Модель, которая рисовала. | | **Endpoint Used** | Каким путём ушёл запрос: `chat` или `images`. Полезно при разборе ошибок. | | **First Image URL** / **Image URLs** | Ссылка на изображение (у части моделей это `data:`-строка, а не веб-адрес). | Модели Gemini Image и GPT Image приложение вызывает через `/v1/chat/completions`, остальные — через `/v1/images/generations`. Это видно в поле **Endpoint Used** и объясняет, почему набор влияющих настроек у линеек разный. Подробнее про оба пути — в разделе [Генерация изображений](/docs/features/image-generation). ## Готовый сценарий: блог на WordPress Чтобы не собирать конвейер с нуля, возьмите готовый: **[скачать wordpress-seo.blueprint.json](/make/wordpress-seo.blueprint.json)**. Сценарий берёт тему из Google Таблицы, пишет статью, рисует обложку, заливает её в медиатеку сайта и создаёт запись в WordPress. Цепочка сценария в Make: тема из Google Таблицы, статья, промпт картинки, генерация изображения, alt-текст, загрузка в медиатеку, Markdown в HTML, запись в WordPress, удаление строки Сценарий и приложение для Make сделала студия [web-steps.ru](https://web-steps.ru/). Цепочка из девяти модулей: 1. **Google Sheets → Get a Cell** — очередная тема из таблицы. 2. **Hubris → Создать текстовый ответ** — статья по теме: заголовки, списки, блок вопросов, финальный призыв со ссылкой. 3. **Hubris → Создать текстовый ответ** — промпт для обложки по той же теме. 4. **Hubris → Сгенерировать изображение** — сама обложка. 5. **Hubris → Создать текстовый ответ** — имя файла, заголовок, alt-текст, подпись и описание картинки (ответ в JSON). 6. **WordPress → Create a Media Item** — обложка попадает в медиатеку с заполненными alt и подписью. 7. **Markdown → Markdown to HTML** — статья переводится из Markdown в HTML. 8. **WordPress → Create a Post** — запись создаётся **черновиком**, обложка привязывается к ней. 9. **Google Sheets → Delete a Row** — отработанная тема вычёркивается из таблицы. ### Как импортировать 1. В Make: **Create a new scenario** → меню `···` в правом нижнем углу → **Import Blueprint** → выберите скачанный файл. 2. Откройте каждый модуль и подставьте свои подключения: Hubris, Google Sheets, WordPress. 3. В обоих модулях Google Sheets выберите свою таблицу — в файле она намеренно не задана — и проверьте лист: первый модуль настроен на лист с именем `Лист1`, последний — на первый лист по счёту. Формат таблицы простой: темы идут списком в столбце `A`, сценарий читает ячейку `A1`, а в конце удаляет первую строку — следующая тема поднимается наверх сама. 4. Во втором модуле (статья) раскройте **Show advanced settings** и в поле **Prompt (2-е сообщение)** замените `https://example.com` и анкор на свои — иначе в конце статьи окажется ссылка-заглушка. 5. В модуле **Create a Post** проверьте автора и рубрику: в файле стоят первый автор и первая рубрика сайта. На чужом WordPress это почти наверняка не то, что нужно. 6. Запустите один прогон и посмотрите черновик в WordPress. Когда результат устроит, поменяйте в модуле **Create a Post** статус с `draft` на `publish`. Модели в файле проставлены рабочие, но их можно менять: для более выразительного текста — модель посильнее из [каталога](/models), для картинок — любая image-модель. Стоимость каждого прогона видна в поле **Usage → Cost** и в [логах](/logs) — там же можно отфильтровать запросы по ключу, если под Make заведён отдельный. ## Универсальный путь: HTTP-модуль Приложение закрывает текст и картинки. Всё остальное — озвучку, транскрибацию, эмбеддинги, видео, вызов инструментов, картинку на входе — можно взять штатным модулем **HTTP → Make a request**: ``` URL: https://api.hubris.pw/v1/chat/completions Method: POST Headers: Authorization: Bearer sk-gw-... Content-Type: application/json Body type: Raw → JSON (application/json) Request content: { "model": "anthropic/claude-haiku-4.5", "messages": [ {"role": "user", "content": "{{1.text}}"} ] } ``` Включите **Parse response**, чтобы Make сам разобрал JSON — тогда ответ доступен как `{{2.choices[1].message.content}}` (в Make нумерация элементов массива начинается с единицы). Таймаут в настройках модуля стоит поднять до 300 секунд: длинные тексты и рассуждающие модели отвечают не мгновенно. Полный список эндпоинтов — в [справочнике API](/docs/api/overview). ## Рекомендуемые модели Сценарии Make — это обычно поток однотипных запросов, где важнее цена и скорость, чем предельное качество рассуждений. | Модель | Когда уместна | | ------------------------------- | ------------------------------------------------- | | `anthropic/claude-haiku-4.5` | классификация, короткие ответы, извлечение данных | | `openai/gpt-4o-mini` | универсальный недорогой выбор | | `anthropic/claude-sonnet-4.5` | статьи, письма, тексты, где виден стиль | | `google/gemini-2.5-flash-image` | быстрые иллюстрации и обложки | | `black-forest-labs/flux.2-pro` | фотореалистичные изображения | Полный каталог с ценами и фильтрами — на странице [Модели](/models). ## Решение проблем ### Список моделей пустой или крутится вечно Приложение запрашивает модели по вашему ключу. Пустой список означает, что запрос не прошёл: проверьте подключение (пересоздайте его с ключом заново) и то, что ключ не отозван в [личном кабинете](/keys). Ещё вариант — у выбранного провайдера просто нет моделей нужного типа: например, у Recraft нет текстовых. ### «Неверный API-ключ Hubris» (401) Ключ не подошёл. Он должен начинаться с `sk-gw-` и не содержать пробелов и переносов строк — при копировании из письма или мессенджера легко прихватить лишнее. ### «Недостаточно баланса» (402) Баланс кончился. Пополнить — в [личном кабинете](/billing), минимум 300 ₽. Полезно включить [автопополнение](/billing), чтобы ночной сценарий не встал на середине. ### «Модель не найдена» (404) Модель убрали из каталога или переименовали — такое случается, когда производитель снимает версию с публикации. Откройте поле **Model** и выберите модель заново. ### «Превышен дневной лимит API-ключа» (429) Обычно это суточный лимит расхода, заданный для ключа: поднять или снять — в [настройках ключа](/keys), лимит считается по скользящим 24 часам. Тот же текст приложение показывает и когда частоту запросов ограничил сам производитель модели (частая история на бесплатных моделях) — если лимита на ключе нет или он далеко не выбран, просто повторите запуск позже или возьмите другую модель. ### Сценарий падает по таймауту Ограничение на один запрос в модулях приложения — 300 секунд, этого хватает почти всегда. Если используете HTTP-модуль, проверьте его собственное поле **Timeout**: по умолчанию там куда меньше. И помните про ограничение Make на длительность всего сценария на вашем тарифе. ### Ответ обрывается на полуслове **Finish Reason** = `length` означает, что упёрлись в **Max Tokens**. Поднимите значение — для длинной статьи 6000 может не хватить. ## Что дальше * [Каталог моделей](/models) — какие модели доступны и сколько стоят. * [Управление ключами](/keys) — отдельный ключ под Make с дневным лимитом. * [Расходы](/usage) — во сколько обошлись прогоны сценариев. * [Структурированный вывод](/docs/features/structured-output) — как получать стабильный JSON. * [Генерация изображений](/docs/features/image-generation) — параметры моделей и оба пути вызова. --- # 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\_in / audio\_out / image\_gen / transcription), цене, длине контекста. | | `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, простой одиночный режим. --- # Кабинет для AI-агентов (/docs/integrations/webmcp) > WebMCP — браузерный агент вызывает инструменты кабинета Hubris напрямую, без скрейпинга интерфейса. > **В разработке.** Стандарт WebMCP ещё формируется, доступность в браузерах ограничена. Набор инструментов может расшириться по мере взросления стандарта. [WebMCP](https://github.com/webmachinelearning/webmcp) — браузерный стандарт в разработке (инкубируется в рамках W3C). Идея простая: страница сама объявляет структурные инструменты через `document.modelContext`, и агент в браузере пользователя вызывает их напрямую, вместо того чтобы читать интерфейс и угадывать, куда кликнуть. На Hubris это работает на публичных страницах и в кабинете одновременно — набор инструментов подстраивается под то, вошли вы или нет. Нативная поддержка есть пока не во всех браузерах. В Chrome стандарт доступен через origin trial (версии 149–156); также WebMCP умеют агентные браузеры и браузерные расширения, реализующие этот стандарт. Если ваш браузер или агент WebMCP не поддерживает, кабинет работает как обычно — ничего не ломается. ## Инструменты Гостю (без входа в кабинет) видны 5 публичных инструментов — они работают на любой странице Hubris. После входа добавляются ещё 13: чтение данных кабинета, действия и тестовый прогон модели. Итого в кабинете доступно 18 инструментов. | Инструмент | Что делает | Где доступен | | ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------ | | `models_search` | Ищет модели в каталоге по названию, провайдеру или описанию задачи своими словами; отдаёт цены в рублях за 1М токенов. | Гость | | `pricing_get` | Возвращает подробные цены и характеристики одной модели по её идентификатору. | Гость | | `cost_estimate` | Оценивает стоимость использования модели в рублях по ожидаемому числу запросов и токенов — ориентир по текущим ценам, не оферта. | Гость | | `docs_search` | Ищет по документации Hubris, возвращает до 10 подходящих страниц. | Гость | | `status_get` | Текущий статус платформы и провайдеров моделей, включая активные (нерешённые) инциденты. | Гость | | `balance_get` | Баланс кабинета (основной и бонусный) и приблизительный прогноз, на сколько дней и запросов хватит при текущем расходе. | Кабинет | | `usage_stats` | Статистика расхода и запросов за период: по дням, по моделям, по API-ключам или общие итоги за 7 дней. | Кабинет | | `logs_query` | Последние записи журнала запросов: время, модель, статус, стоимость, токены — без адресов вызывающих приложений и других чувствительных данных. | Кабинет | | `payment_history` | Лента денежных операций: пополнения, возвраты, корректировки, партнёрские выплаты. | Кабинет | | `usage_export` | Возвращает ссылку на CSV-файл с историей расхода за период. | Кабинет | | `referral_info` | Состояние партнёрской программы: ссылка, число приглашённых, сумма начислений. Доступно после первой оплаты. | Кабинет | | `notification_prefs_get` | Текущие настройки уведомлений: продуктовые, о достижениях, маркетинговые. | Кабинет | | `topup_start` | Начинает пополнение баланса на сумму в рублях: создаёт QR-код СБП или платёжную ссылку для карты. Баланс пополняется только после подтверждения платежа в приложении банка. | Кабинет | | `invoice_create` | Создаёт счёт на оплату для юридического лица на сумму в рублях (нужны заранее заполненные реквизиты организации). | Кабинет | | `documents_get` | Список счетов юридического лица со ссылкой на PDF и, если документ уже сформирован, — на закрывающий УПД. | Кабинет | | `notification_prefs_set` | Меняет настройки email-уведомлений кабинета. | Кабинет | | `feedback_submit` | Публикует идею на доску идей Hubris — заголовок и, опционально, описание. Лимит — не больше трёх идей в сутки. | Кабинет | | `chat_test` | Тестовый прогон промпта по текстовой модели каталога (генерация изображений — в студии на сайте). Списывается с баланса по обычным ценам. | Кабинет | Управление API-ключами агенту намеренно недоступно ни в одном из наборов — ни просмотр, ни выпуск, ни отзыв. Это осознанное ограничение: ключ открывает доступ к балансу за пределами текущей браузерной сессии, и агент не должен уметь его получить. ## Лимиты `chat_test` `chat_test` — это примерка модели, а не рабочий канал. У неё два серверных ограничения, которые нельзя обойти со стороны агента: * **до 2000 токенов ответа** — сервер обрезает ответ до этого значения независимо от того, что попросил агент; * **до 20 прогонов в час** — на пользователя, счётчик скользит от первого прогона в окне; после исчерпания сервер отвечает `429` с `Retry-After`. Ограничения защищают от случайного зацикливания агента, а не от расходов — токены `chat_test` списываются с баланса по тем же ценам, что и обычный запрос. Инструмент работает только с текстовыми моделями: попытка вызвать модель с генерацией изображений получает отказ — сама генерация доступна в студии на сайте, не через кабинет для агентов. При обрезке ответа `chat_test` называет причину: если сработал серверный кап (2000 токенов), в подсказке — ссылка на подключение MCP-сервера, путь к полноценной работе без лимитов; если ответ обрезал собственный `max_tokens` агента (передан меньше 2000), подсказка называет именно это значение и предлагает его поднять — подключать MCP-сервер для этого не нужно. ## Полноценная работа — через MCP-сервер WebMCP в кабинете создан для быстрой проверки и точечных действий прямо в браузере, а не для продуктивной работы с моделями. Если агенту нужны запросы без ограничения токенов и без часового лимита, следующий шаг — подключить [MCP-сервер Hubris](/docs/integrations/mcp): тот же каталог моделей и `chat_complete`, но по API-ключу и без капов, применимых к тестовому прогону. ## Приватность Агент действует под вашей активной сессией в браузере — теми же правами, что и вы сами в открытой вкладке кабинета. Отдельной агентской идентичности или токена WebMCP не создаёт: закрыли вкладку или вышли из аккаунта — доступ у агента пропал вместе с сессией. Из этого следует два ограничения, введённых сознательно: * **Управление API-ключами недоступно** — см. выше. Ключ живёт дольше браузерной сессии и не должен быть доступен через страницу. * **Генерация изображений — только в студии на сайте**, не через инструменты кабинета для агентов. ## Что дальше * [MCP-сервер](/docs/integrations/mcp) — подключение по API-ключу, без лимитов тестового прогона. * [Каталог моделей](https://hubris.pw/models) — все активные модели и цены в рублях. * [Биллинг](https://hubris.pw/billing) — пополнение баланса. * [Кабинет](https://hubris.pw/dashboard) — то же самое можно сделать руками, без агента.