# Аутентификация (/docs/authentication) > API-ключи Hubris в формате sk-gw-, заголовок Bearer, безопасное хранение и ротация. import { EndpointBadge } from '@/components/docs/endpoint-badge'; import { EnvVar } from '@/components/docs/env-var'; Все запросы к Hubris авторизуются API-ключом в заголовке `Authorization: Bearer sk-gw-<32-hex>`. Это совместимо с OpenAI SDK и большинством ML-инструментов. ## Создание ключа 1. Войдите в [/keys](/keys). 2. Нажмите «Новый ключ», задайте описательное имя (например, «production-bot», «dev-laptop»). 3. **Скопируйте ключ сразу** — он показывается один раз. Hubris хранит только хеш ключа (sha256), восстановить полное значение невозможно. 4. Сохраните в менеджер паролей или переменную окружения . ## Формат ключа `sk-gw-` + 32 шестнадцатеричных символа (16 байт случайных данных). Пример: ``` sk-gw-a1b2c3d4e5f6789012345678901234ab ``` В UI показывается префикс `sk-gw-a1b2...34ab` — этого достаточно, чтобы отличить ключи между собой, но недостаточно для использования. ## Использование в запросах Заголовок: `Authorization: Bearer sk-gw-...`. Например, через cURL: ```bash curl https://api.hubris.pw/v1/models \ -H "Authorization: Bearer sk-gw-..." ``` В OpenAI SDK ключ передаётся через параметр `api_key` или env-переменную: ```python from openai import OpenAI client = OpenAI(api_key="sk-gw-...", base_url="https://api.hubris.pw/v1") ``` ```ts import OpenAI from "openai"; const client = new OpenAI({ apiKey: "sk-gw-...", baseURL: "https://api.hubris.pw/v1" }); ``` ## Что делать, если ключ скомпрометирован 1. Откройте [/keys](/keys). 2. Найдите подозрительный ключ по префиксу или имени. 3. Нажмите «Отозвать» — ключ немедленно перестаёт работать. 4. Создайте новый ключ и обновите его во всех местах использования. ## Безопасное хранение * **Никогда не коммитьте ключи в git** — добавьте `.env` файлы в `.gitignore`. * **Не отправляйте ключи** в логи, аналитику или error-tracking. Hubris не логирует содержимое запросов, но сторонние сервисы могут. * **Ротируйте ключи** при смене работы, выходе сотрудника, передаче кода подрядчику. Это секунда работы — старый отозвать, новый создать. * **Один ключ — одна цель.** Отдельные ключи на dev / staging / production упрощают аудит и быструю ревокацию при инциденте. ## Лимиты на ключе В стандартном тарифе у пользовательских ключей **нет искусственных лимитов** — расход ограничен только балансом аккаунта. Служебные ключи (CI, скрипты) могут получить дневной лимит в копейках при создании — это страховка от утечки. Подробнее — в разделе про rate limits (готовится). ## Ошибки аутентификации Без заголовка или с невалидным ключом возвращается **401**: ```json { "error": { "message": "Missing API key", "type": "invalid_request_error", "code": "invalid_api_key" } } ``` Если ключ был отозван — то же самое: 401 с `code: "invalid_api_key"`. ## Что дальше * [Быстрый старт](/docs/quickstart) — первый запрос с вашим новым ключом. * [Каталог моделей](/models) — выбрать модель. * [Биллинг](/billing) — пополнение баланса, минимальный остаток. --- # Changelog (/docs/changelog) > Записи о breaking changes API, новых моделях, обновлениях платформы Hubris. Записи о значимых изменениях платформы, документации и SDK. Полная история коммитов — внутренняя. ## 2026-05-28 — OAuth 2.0 + PKCE для сторонних приложений (Phase A) Сторонние приложения могут подключаться к Hubris через стандартный OAuth flow с тремя scopes (`chat:write`, `models:read`, `balance:read`) и опциональным дневным лимитом на токен. Claude Desktop, Claude.ai, Cursor и другие MCP-клиенты теперь подключаются нативно через «Add custom connector» UI — без `mcp-remote` шима. Подробности и Python-пример: [/docs/integrations/oauth](/docs/integrations/oauth). ## 2026-05-26 — MCP-сервер для Claude Desktop / Claude Code / Cline (Beta) Подключите Hubris к AI-агенту по протоколу [Model Context Protocol](/docs/integrations/mcp): URL `https://api.hubris.pw/mcp`, тот же Bearer-ключ `sk-gw-...`. Агент получает каталог моделей, баланс и запросы к LLM (`chat/complete` — полный паритет с `/v1/chat/completions`) через одно подключение. Биллинг — как обычно. Фича в Beta — поведение и набор инструментов могут поменяться. ## 2026-05-25 — Чат в личном кабинете (Beta) В ЛК появился раздел [**Чат**](https://hubris.pw/chat) — браузерный интерфейс к любой из 300+ моделей каталога. Альтернатива внешним чат-клиентам, когда нужно «быстро спросить» без curl и API-ключей. * **История хранится только в этом браузере.** Чаты живут в localStorage — сервер Hubris их не видит и не может восстановить. В журнале запросов остаются только метаданные (модель, токены, стоимость), как и для API-вызовов. При очистке данных браузера или смене устройства чаты пропадут — для бэкапа есть «Скачать всё» и импорт из JSON в настройках чата. * **Одна модель на чат.** Выбирается при создании, в шапке отображается название и провайдер. Чтобы попробовать другую модель — создаётся новый чат. * **Edit и regen, без веток.** Любое сообщение пользователя можно отредактировать — хвост чата перепрогоняется заново (с подтверждением, если удаляется ≥2 сообщений). Под последним ответом ассистента — кнопка «Перегенерировать». * **Лимиты.** До 50 сообщений в одном чате (мягкое предупреждение с 40), до 30 чатов в сайдбаре. При достижении — баннер «Создайте новый чат» с кнопкой «Скачать этот». Защита от тормозов DOM и переплаты за input-replay (каждый новый запрос отправляет всю историю в модель). * **Кликабельные источники для Perplexity Sonar.** Маркеры `[1]`, `[2]` в ответах web-search моделей превращаются в маленькие фиолетовые pill-ссылки на оригинальные источники. Под ответом — полный список URL с hostname и путём. * **Поддержка LaTeX-формул.** Inline `\(...\)` и блочные `\[...\]` рендерятся через KaTeX — нужно для reasoning-моделей (Sonar Reasoning, Sonar Deep Research), которые форматируют числа и формулы в математической нотации. * **Stop с честным биллингом.** Кнопка «Остановить» во время стрима абортит чтение, но сервер дочитывает апстрим до конца и списывает полную стоимость (соответствие SPEC §10). В подсказке у кнопки — явное «не отменяет списание». * **Списание как обычно, без прогноза.** Каждое сообщение оплачивается из основного баланса так же, как `/v1/*` запрос. При нулевом балансе — баннер «Пополнить», следующее сообщение не отправляется до пополнения. * **Privacy-онбординг.** При первом открытии — диалог с явным объяснением, что чаты не покидают браузер. Один клик «Понятно» — больше не показывается. * **Полный экран на десктопе.** Раздел занимает всю ширину/высоту окна — без узкой колонки и шапки дашборда. Колонка сообщений ограничена 760px для комфортного чтения. Доступ — пункт «Чат» в сайдбаре ЛК (бейдж Beta). ## 2026-05-25 — Активированы Perplexity Sonar и другие web-search модели 5 моделей семейства **Perplexity Sonar** (`sonar`, `sonar-pro`, `sonar-pro-search`, `sonar-reasoning-pro`, `sonar-deep-research`) теперь активны в [каталоге](/models) и доступны через `/v1/*` и встроенный чат. Раньше были деактивированы — пока не было уверенности, что биллинг корректно покрывает web-search surcharge. * **Биллинг через `usage.cost` от провайдера.** Стоимость каждого запроса (включая web-search per-request surcharge $0.005) приходит готовой от апстрима и попадает в `usage_logs.cost_kopecks` ровно так же, как для обычных моделей. Никаких отдельных формул на нашей стороне. * **Sentinel-warning в логах.** Если апстрим вдруг вернёт `usage.cost=0` при `web_search_requests > 0` — пишем warn-лог, чтобы поймать проблему до того, как её заметят пользователи. * **В чате `/chat` — кликабельные `[N]`.** Sonar возвращает массив `citations[]` URL'ов; чат превращает маркеры в тексте в маленькие фиолетовые pill-ссылки и показывает полный список под ответом. ## 2026-05-24 — Все ошибки `/v1/*` и `/api/internal/*` теперь на русском `error.message` во всех публичных эндпоинтах переведены на формальный русский язык: * `Недостаточно средств на балансе.` * `Модель не найдена: ` * `Неверный API-ключ` / `API-ключ не передан` * `Курс ЦБ РФ временно недоступен` * `Сервис провайдера временно недоступен` (раньше — `Upstream returned an error`) * `Превышено время ожидания апстрима` / `Превышен дневной лимит расхода для этого API-ключа` * и другие. `error.code` (`insufficient_balance`, `model_not_found`, `upstream_error`, `daily_limit_exceeded`, и т.д.) **остаётся стабильным машинно-читаемым контрактом** — английский. Если ваш код матчит ошибки по `code` — изменений не требуется. Если по `message` — обновите матч на русский (либо мигрируйте на `code`). В песочнице на странице модели также убрали захардкоженные русские подсказки на фронте — теперь рендерится ровно то, что отдал бэкенд. При ошибке «недостаточно средств» рядом с сообщением появляется ссылка «Пополнить баланс» → `/billing`. ## 2026-05-23 — `usage.cost` теперь в копейках В ответах всех `/v1/*` эндпоинтов (`/messages`, `/chat/completions`, `/responses`, `/embeddings`) поле `usage.cost` теперь содержит **итоговую цену запроса в копейках** (integer) — ту же сумму, что списывается с баланса и попадает в [/v1/usage](/docs/api/usage). Раньше там приходил raw cost в долларах. Применяется и к потоковому режиму — копейки попадают в финальный `message_delta` / `chat.completion.chunk` / `response.completed`. ## 2026-05-23 — Поддержка Claude Code: эндпоинт `/v1/messages` * **Новый эндпоинт `POST /v1/messages`** — совместимый с [Anthropic Messages API](https://docs.anthropic.com/claude/reference/messages_post). Через него работают [Claude Code](/docs/integrations/claude-code), официальные SDK Anthropic для Python и TypeScript, любые сторонние клиенты, ожидающие Anthropic-формат. * **Аутентификация: и Bearer, и `x-api-key`.** Поддержаны оба заголовка одновременно — Claude Code использует `ANTHROPIC_AUTH_TOKEN` через `Authorization: Bearer`, а Anthropic-SDK по умолчанию шлёт `x-api-key`. Hubris принимает обе формы для любого `/v1/*` эндпоинта. * **Маппинг имён моделей.** Принимаются и Anthropic-стиль (`claude-sonnet-4-5`, `claude-3-5-sonnet-20241022`, `claude-haiku-4-5-latest`), и каноничные Hubris-имена (`anthropic/claude-sonnet-4.6`). В ответе поле `model` восстанавливается в то имя, что прислал клиент — для согласованности логов. * **Полный набор Anthropic-фич: vision, tool use, prompt caching, extended thinking.** Все content-блоки (`text`, `image`, `tool_use`, `tool_result`, `document`, `thinking`) и параметры (`cache_control`, `tool_choice`, `thinking`, `stop_sequences`) проходят без изменений. Биллинг учитывает `usage.cost`, который провайдер возвращает с поправкой на cache hit / cache write. * **Anthropic-нативный SSE-стриминг.** События `message_start` / `content_block_*` / `message_delta` / `message_stop` приходят в нативном формате. На разрыве соединения Hubris дочитывает апстрим до конца и списывает стоимость, без бесплатных токенов. * **Anthropic-формат ошибок.** Вместо OpenAI-стиля `{error:{...}}` возвращаем `{type:"error",error:{type,message}}` с корректным `error.type` (`invalid_request_error`, `authentication_error`, `rate_limit_error`, `api_error`, `not_found_error`, `billing_error`, `timeout_error`). SDK Anthropic не падают. * **Statusline-скрипт для Claude Code.** Bash (`https://hubris.pw/scripts/claude-statusline.sh`) и кросс-платформенный Node-вариант (`https://hubris.pw/scripts/claude-statusline.mjs`) — выводит активную модель и расход за сегодня в нижней строке TUI. Подробности — в [гиде по Claude Code](/docs/integrations/claude-code#statusline-с-балансом). * **Документация.** [Гид «Подключение Claude Code»](/docs/integrations/claude-code) — установка, конфигурация, выбор моделей, statusline, FAQ. [API-референс POST /v1/messages](/docs/api/messages) — параметры, форматы, примеры. * **Ограничение MVP.** Privacy Mode (маскирование PII) на `/v1/messages` **пока не работает** — фича сложнее в реализации для Anthropic-формата content-блоков. Если нужна маскировка, используйте `/v1/chat/completions` с заголовком `X-Hubris-Privacy-Mask`. Поддержка появится отдельно. ## 2026-05-23 — Каталог моделей: +23 модели генерации изображений * **Подключены image-модели из расширенного каталога:** Flux 2 (pro/max/flex/klein-4b), Recraft v3/v4/v4.1 (11 вариантов: pro, vector, utility), Sourceful Riverflow v2 (pro, fast, preview-серии), xAI Grok Imagine, ByteDance Seedream 4.5, gemini-2.5-flash-image-preview. Полный список — в [каталоге](/models) с фильтром «Output: image». * **Корректное отображение цены за изображение в каталоге.** Раньше unit-priced модели показывали «0 ₽ за 1М токенов» (UX-баг). Теперь — «X ₽ за изображение» / «X ₽ за мегапиксель» в зависимости от тарификации модели. То же поле `pricing.per_unit[]` доступно через `GET /v1/models` для разработчиков. * **Документация: расширение [Генерации изображений](/docs/features/image-generation).** Появилась справка по `image_config` параметрам конкретных провайдеров: Recraft `style` / `text_layout` / `rgb_colors`, Sourceful `font_inputs` / `super_resolution_references`. Раздел «Какую модель когда выбирать». ## 2026-05-22 — Песочница умеет генерировать картинки и уважает Privacy Mode * **Image-gen в песочнице.** На страницах моделей `google/gemini-2.5-flash-image`, `openai/gpt-image-*`, `google/gemini-3.1-flash-image-preview` и других моделей с картиночным выходом песочница теперь возвращает сгенерированное изображение прямо в чат-пузырь. Стоимость и токены считаются как для обычной генерации — без скрытых наценок. * **Превью и сохранение.** Клик по сгенерированной картинке открывает полноразмерный лайтбокс. Кнопка «Сохранить» скачивает PNG (или WEBP/JPEG — расширение берётся из data URL) с именем `hubris-.`. ESC или клик мимо — закрытие. Файл существует только у вас: на серверах Hubris ничего не сохраняется, в логах остаются только метаданные биллинга. * **Песочница для image-only моделей.** Модели, которые отдают только картинку без текста (Flux / SDXL-семейство), теперь тоже доступны в песочнице. * **Privacy Mode теперь работает и в песочнице.** Раньше включённая в `/security` маска применялась только к запросам через API-ключ — а отправки прямо со страницы модели уходили без маски. Теперь оба пути идентичны: ФИО, телефоны, email, ИНН, паспорта и т.д. скрываются до отправки модели, а в ответе восстанавливаются обратно. На stream-ответах работает та же построчная подмена, что и в `/v1/chat/completions`. ## 2026-05-22 — Server-tools, новая секция «Интеграции», доки фич * **API: новый namespace `hubris:*` для server-tools.** В `tools[]` запроса `/v1/chat/completions` и `/v1/responses` появилась поддержка `{ "type": "hubris:web_search" }` — это канонический способ включить встроенный веб-поиск. Гид: [Поиск в интернете](/docs/features/web-search). Старые namespace-варианты продолжают работать ради обратной совместимости. * **Новая секция документации «Интеграции» (9 страниц).** Подключение Hubris к популярным AI-инструментам: [Cline](/docs/integrations/cline), [Roo Code](/docs/integrations/roo-code), [Kilo Code](/docs/integrations/kilo-code), [OpenCode](/docs/integrations/opencode), [Qwen Code CLI](/docs/integrations/qwen-code), [Hermes Agent](/docs/integrations/hermes-agent), [OpenClaw](/docs/integrations/openclaw), [Dify](/docs/integrations/dify), [n8n](/docs/integrations/n8n). У каждого инструмента — точные шаги настройки base URL и API-ключа, советы по выбору модели и решение типовых проблем. * **Новые гиды в разделе «Возможности»:** * [Вызов инструментов (tool calling)](/docs/features/tool-calling) — жизненный цикл tool-сессии, `tool_choice`, параллельные вызовы, стриминг, SDK-примеры. * [Структурированный вывод](/docs/features/structured-output) — `json_object` и `json_schema` строгий режим, Pydantic / Zod helper'ы, обработка refusal'ов. * [Поиск в интернете](/docs/features/web-search) — встроенный server-side инструмент, аннотации источников, стриминг. * [Изображения на вход (Vision)](/docs/features/vision) — multimodal-ввод через `image_url` content-parts, URL и base64, параметр `detail`, биллинг. * [Выбор модели программно](/docs/features/model-selection) — фильтрация каталога через `GET /v1/models`, обработка `404 model_not_found`, версионированные slug'и. * [Стриминг ответов](/docs/features/streaming) — когда включать `stream: true`, парсинг в Python / Node / Vercel AI SDK / голом fetch, UX-паттерны (типографический эффект, прогресс на reasoning-моделях, отмена). * [Мониторинг расходов](/docs/features/usage-tracking) — `GET /v1/usage` для cron-отчётов, бюджет-алёртов в коде агента, разбивки по проектам, `BigInt` для копеечной точности. * [Обработка ошибок](/docs/features/error-handling) — стратегия retry по кодам (что ретраить, что нет), `tenacity` пример на Python, factory на TS, маппинг внутренних кодов в user-facing сообщения. * **Боковое меню документации согласовано с дашбордом.** Шрифт, отступы и компоновка строк сайдбара `/docs` теперь матчат сайдбар личного кабинета (`13.5px` / `6×10` / иконки `15px`). До этого fumadocs-дефолты раздували сайдбар, и переходы между ЛК и доками выглядели визуально несогласованно. ## 2026-05-12 — Документация под общим хедером, AI-friendly выгрузка * **Документация теперь под общей навигацией сайта.** В шапке `/docs` появилось основное меню Hubris (Главная, Модели, Использование, API-ключи, Биллинг). Для авторизованных — текущий баланс и меню пользователя. Из ЛК больше не нужно открывать документацию «в отрыве». * **Русский интерфейс fumadocs.** `«На этой странице»`, `«Следующая»`, `«Предыдущая»`, `«Редактировать на GitHub»`, плейсхолдер поиска — теперь по-русски. * **AI-friendly выгрузка документации.** * Любую страницу `/docs/` можно открыть как plain-markdown: добавьте `.md` или `.mdx` к URL (например `https://hubris.pw/docs/quickstart.md`). * В шапке каждой страницы — кнопки **«Скопировать как Markdown»** и **«Открыть в…»** (ChatGPT / Claude / .md-версия). * `https://hubris.pw/llms.txt` — индекс всей документации в формате [llmstxt.org](https://llmstxt.org), для AI-агентов. * `https://hubris.pw/llms-full.txt` — полный дамп всех страниц одним файлом, удобно скармливать в context-окно модели. ## 2026-05 — Запуск публичной документации Опубликованы первые версии разделов: * С чего начать: Quickstart, Аутентификация, Миграция с OpenAI. * Базовые концепции: Модели, Цены, Биллинг, Ошибки, Rate limits, Конфиденциальность. * API Reference: `/v1/models`, `/v1/chat/completions`, `/v1/responses` (BETA), Streaming. * Возможности: Reasoning-токены, кеширование промптов. * Фреймворки: OpenAI SDK Python/Node, Vercel AI SDK, LangChain Python/JS. API сам по себе — без изменений с релизной версии 1.0. Все стабильные эндпоинты под `/v1/*` остаются совместимыми. ## Подписка на обновления Пока что — следите за этой страницей. RSS / email-рассылка в работе. Для критических изменений (breaking change в API, отзыв модели из каталога) — будем дополнительно слать на email вашего аккаунта. ## Если нужно, чтобы конкретная модель не пропала Если вы строите интеграцию вокруг конкретной модели и боитесь, что её уберут — напишите на [support@hubris.pw](mailto:support@hubris.pw), мы согласуем, как минимум, заранее предупредим о выводе. --- # FAQ (/docs/faq) > Часто задаваемые вопросы про Hubris API, биллинг, ключи, поддерживаемые модели. ## Что такое Hubris? Hubris — единый OpenAI-совместимый API ко всем ведущим LLM. Один ключ, оплата в рублях через СБП, OpenAI-совместимый формат запросов и ответов. Подробнее — на [главной](/docs). ## Через какие модели вы работаете? Мы агрегатор. Маршрутизируем запросы к лучшим моделям мира — OpenAI, Anthropic, Google, DeepSeek, Mistral и другим — через единое API. Конкретные торговые отношения с поставщиками — наша внутренняя коммерческая информация. Полный список доступных моделей с ценами — в [каталоге](/models). ## Сколько стоит? Pay-as-you-go без подписок. Платите только за фактически использованные токены, в рублях. Для каждой модели — своя цена за 1М входных и выходных токенов, она видна в каталоге. Подробности расчёта — на странице [Цены](/docs/concepts/pricing). ## Как пополнить баланс? Через СБП на странице [Биллинг](/billing). Минимально 100 ₽, максимально 10 000 ₽ за одну операцию. Зачисление автоматическое в течение 30 секунд. Подробнее — на странице [Биллинг](/docs/concepts/billing). ## Что если баланс ушёл в минус? Стоимость запроса известна только после ответа модели. Поэтому возможна ситуация: на балансе было 50 ₽, запрос стоил 60 ₽, баланс ушёл в −10 ₽. Это допустимо на одну операцию. Следующий запрос с отрицательным балансом вернёт 402 — пополнение разблокирует следующие запросы. ## Можно ли получить чек / счёт-фактуру? Для физических лиц — нет, оплата через СБП с подтверждением банка достаточна. Для юридических лиц — пишите на [support@hubris.pw](mailto:support@hubris.pw), обсудим выставление счёта и закрывающие документы. ## Сохраняете ли вы мои запросы? Нет. Содержимое запросов и ответов не пишется ни в логи, ни в БД. Передаётся провайдеру модели по TLS и сразу забывается. Подробнее — на странице [Конфиденциальность](/docs/concepts/privacy). ## Какие провайдеры моделей видят мои данные? Перечень провайдеров, которым потенциально передаётся содержимое запроса, — в [Политике конфиденциальности](/legal/privacy), раздел 8. Кратко: модели Anthropic — Anthropic, OpenAI — OpenAI, Google — Google, и так далее. Каждый имеет свою политику обработки. ## Какие лимиты на ключе? В стандартном тарифе у пользовательских ключей нет искусственных лимитов — расход ограничен только балансом. Дневной лимит на конкретном ключе можно задать или поменять прямо на странице [API-ключи](/keys): поле «Дневной лимит, ₽» в форме создания и кнопка «изменить» рядом с активным ключом. Подробнее — [Rate limits](/docs/concepts/rate-limits). ## Что делать, если ключ скомпрометирован? Откройте [/keys](/keys), найдите подозрительный ключ, нажмите «Отозвать». Создайте новый и обновите его во всех местах. Подробнее — [Аутентификация](/docs/authentication). ## Поддерживаете ли вы embeddings? Generation картинок? Audio? **Embeddings** — да, через [`POST /v1/embeddings`](/docs/api/embeddings). Доступны OpenAI `text-embedding-3-*`, Google Gemini, BGE, MiniLM и другие embedding-модели в каталоге. **Image generation** и **Audio (Whisper / TTS)** — пока нет. Появятся в ближайших обновлениях. Если нужно срочно — напишите на [support@hubris.pw](mailto:support@hubris.pw), приоритезируем. ## Где changelog? [/docs/changelog](/docs/changelog) — записи о breaking changes API, новых моделях, важных обновлениях. ## Куда писать про баги? [support@hubris.pw](mailto:support@hubris.pw) — приложите модель, время запроса в UTC и e-mail аккаунта. Найти конкретный запрос потом поможет [/usage](/usage). ## Можно ли запустить self-hosted? Сейчас нет. Если у вас регулируемые данные (медицинские, финансовые, гос. тайна) — пишите на [support@hubris.pw](mailto:support@hubris.pw), обсудим архитектуру под ваши требования. --- # Документация Hubris (/docs) > OpenAI-совместимый LLM-агрегатор с оплатой в рублях. Один ключ ко всем ведущим моделям. import { Cards, Card } from 'fumadocs-ui/components/card'; import { Rocket, KeyRound, Boxes, BookOpen, GitCompare, Receipt } from 'lucide-react'; import { ModelLink } from '@/components/docs/model-link'; Hubris — единый OpenAI-совместимый API ко всем ведущим языковым моделям. Маршрутизируем запросы к лучшим моделям мира, с автоматическим переключением на запасную модель при сбоях. Оплата — в рублях через СБП. ## С чего начать } title="Быстрый старт" href="/docs/quickstart" description="Первый запрос за 60 секунд: cURL, Python, TypeScript." /> } title="Аутентификация" href="/docs/authentication" description="API-ключи в формате sk-gw-, ротация, безопасное хранение." /> } title="Каталог моделей" href="/models" description="400+ моделей, актуальные цены в ₽ за 1M токенов." /> } title="Миграция с OpenAI" href="/docs/migration-from-openai" description="Один base URL — и существующий код работает." /> } title="API Reference" href="/docs/api/overview" description="/v1/chat/completions, /v1/responses, /v1/embeddings, /v1/models." /> } title="Биллинг и цены" href="/docs/concepts/pricing" description="Как считается стоимость, СБП-пополнения, free-модели." /> ## Что вы получаете * **Один API-ключ** для доступа к моделям OpenAI, Anthropic, Google, DeepSeek, Mistral и других. * **Оплата в рублях** через Систему быстрых платежей. Без валютных карт и зарубежных платёжных провайдеров. * **OpenAI-совместимый формат** — меняете один URL и подключаете существующий код без переписывания. * **Прозрачные цены** — итоговая стоимость в ₽ за 1 миллион токенов сразу видна в каталоге. * **Стриминг ответов** — Server-Sent Events работают как в OpenAI API. ## Популярные модели * — быстрая и дешёвая Claude от Anthropic, для большинства задач. * — баланс качества и стоимости от OpenAI. * — большой контекст и мультимодальность от Google. Полный список с ценами — в [каталоге моделей](/models). ## Поддержка Не нашли нужную модель или нужна помощь с интеграцией — напишите на [support@hubris.pw](mailto:support@hubris.pw). --- # Документация для AI (/docs/llms-txt) > Скормите всю документацию Hubris в ChatGPT, Claude или любого AI-агента одной ссылкой — и спрашивайте на естественном языке. import { Cards, Card } from 'fumadocs-ui/components/card'; import { FileText, ListTree, Link2, Sparkles } from 'lucide-react'; Если вы не хотите читать документацию глазами — отдайте её AI-модели и задавайте вопросы голосом или текстом. Мы публикуем всю документацию в специальном формате, который понимают ChatGPT, Claude, Gemini и любые другие LLM. Это работает без регистрации, без VPN и без копирования вручную. Достаточно одной ссылки. ## Самый быстрый способ — за 30 секунд 1. Откройте [ChatGPT](https://chatgpt.com) или [Claude](https://claude.ai). 2. Скопируйте промпт ниже и вставьте в окно чата. 3. Спрашивайте всё, что хотите узнать о Hubris. ``` Прочитай документацию Hubris по ссылке https://hubris.pw/llms-full.txt и ответь на мои вопросы. Это OpenAI-совместимый LLM-шлюз с оплатой в рублях. ``` Модель сама загрузит файл, разберёт его и будет отвечать со ссылками на нужные разделы. Дальше можно спрашивать что угодно: «как мне мигрировать с OpenAI?», «сколько стоит Claude Haiku в рублях?», «как включить стриминг в Python?» — модель ответит, опираясь на актуальную документацию. ## Три формата на выбор } title="llms-full.txt — вся документация одним файлом" href="https://hubris.pw/llms-full.txt" description="Полный дамп всех страниц одним plain-text файлом. Скормите целиком в контекст модели." /> } title="llms.txt — индекс документации" href="https://hubris.pw/llms.txt" description="Краткий список всех страниц со ссылками. Стандарт llmstxt.org для AI-агентов." /> } title=".md-версия любой страницы" href="https://hubris.pw/docs/quickstart.md" description="Добавьте .md или .mdx к URL любой страницы — получите её в чистом Markdown." /> ### Когда какой формат использовать | Сценарий | Что давать модели | | ----------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- | | Хотите задавать вопросы по всей документации сразу | [`llms-full.txt`](https://hubris.pw/llms-full.txt) | | Делаете своего AI-агента и хотите, чтобы он сам выбирал нужные страницы | [`llms.txt`](https://hubris.pw/llms.txt) (индекс) — агент сам подтянет нужное | | Нужна конкретная одна страница | URL с суффиксом `.md`, например [`/docs/quickstart.md`](https://hubris.pw/docs/quickstart.md) | | Работаете в Cursor / Windsurf / Claude Code | Добавьте `https://hubris.pw/llms-full.txt` как контекст-документ | ## Готовые промпты Скопируйте и вставьте — модель сделает остальное. ### Помощник по интеграции ``` Прочитай https://hubris.pw/llms-full.txt — это документация Hubris. Я хочу подключить Hubris к своему проекту на [Python/Node.js/...]. Расскажи пошагово, что нужно сделать. Дай работающий код для первого запроса. ``` ### Миграция с OpenAI ``` Загрузи https://hubris.pw/llms-full.txt и помоги мне мигрировать с OpenAI API на Hubris. У меня код на [язык]. Что именно нужно поменять? Объясни, что останется идентичным, а где могут быть отличия. ``` ### Выбор модели под задачу ``` Изучи https://hubris.pw/llms-full.txt и каталог моделей https://hubris.pw/models. Мне нужна модель для [задача — например «короткие классификации текста», «генерация длинных статей», «работа с картинками»]. Бюджет — [X ₽ в месяц]. Что посоветуешь? Покажи плюсы-минусы и итоговую цену в рублях за 1М токенов. ``` ### Отладка ошибок ``` Я работаю с Hubris API и получаю такую ошибку: [вставьте текст ошибки]. Документация: https://hubris.pw/llms-full.txt Что это значит и как починить? ``` ## Как скормить документацию AI-агенту ### ChatGPT (chatgpt.com) 1. Откройте новый чат. 2. Вставьте: `Прочитай https://hubris.pw/llms-full.txt и жди вопросов.` 3. ChatGPT автоматически загрузит файл через встроенный браузер. Дальше задавайте вопросы как обычно. Можно ещё надёжнее: скачайте [`llms-full.txt`](https://hubris.pw/llms-full.txt) себе на компьютер и **перетащите файл в окно чата** как вложение. Так модель точно прочитает весь текст без зависимости от веб-поиска. ### Claude.ai (claude.ai) 1. Откройте новый чат на [claude.ai](https://claude.ai). 2. Нажмите на иконку скрепки внизу окна → «Add from URL» → вставьте `https://hubris.pw/llms-full.txt`. 3. Claude добавит документ в Project Knowledge и будет ссылаться на него во всех ответах. Альтернатива: создайте Project, загрузите файл туда один раз — и все чаты внутри проекта будут видеть документацию. ### Google Gemini / AI Studio В [Gemini](https://gemini.google.com) или [aistudio.google.com](https://aistudio.google.com) — просто вставьте ссылку `https://hubris.pw/llms-full.txt` в чат с фразой «прочитай и помоги по этой документации». У Gemini 2.5 контекст 1M токенов — вся документация поместится с большим запасом. ### Cursor 1. Откройте Cursor → Settings (⌘,) → **Features** → **Docs**. 2. Нажмите **Add new doc**. 3. Вставьте URL: `https://hubris.pw/llms-full.txt`, дайте имя `Hubris`. 4. В любом чате внутри Cursor напишите `@Hubris` — и документация подтянется в контекст. ### Claude Code В диалоге Claude Code (терминал или IDE) просто упомяните URL: ``` Прочитай https://hubris.pw/llms-full.txt и помоги интегрировать Hubris в этот проект. ``` Claude Code сам загрузит файл через WebFetch. Чтобы это работало всегда — добавьте ссылку в `CLAUDE.md` вашего проекта: ```md ## Документация по Hubris API Полная документация: https://hubris.pw/llms-full.txt Индекс: https://hubris.pw/llms.txt ``` ### Cline / Roo-Code / Kilo-Code В настройках расширения найдите раздел **Custom Instructions** или **Documentation** и добавьте: ``` При работе с Hubris API всегда сверяйся с документацией: https://hubris.pw/llms-full.txt ``` Агент будет подгружать страницу при необходимости через свой web-fetch tool. ### Continue.dev В `~/.continue/config.json` добавьте раздел `docs`: ```json { "docs": [ { "title": "Hubris", "startUrl": "https://hubris.pw/llms-full.txt", "rootUrl": "https://hubris.pw/docs" } ] } ``` После перезапуска Continue в чате будет доступна команда `@Hubris`. ### Любой другой агент Если ваш агент умеет ходить в web (большинство современных умеют — Perplexity, Phind, Poe, MetaGPT, AutoGen), просто скажите ему: ``` Документация Hubris: https://hubris.pw/llms-full.txt Прочитай её перед ответами на вопросы про Hubris. ``` ## Кнопки на каждой странице Если вам нужна не вся документация, а одна конкретная страница — в шапке каждой страницы есть кнопка **«Открыть в ChatGPT / Claude»**. Она автоматически передаёт текущую страницу в выбранную AI-модель с готовым промптом. Рядом — кнопка **«Скопировать как Markdown»**: содержимое страницы в чистом виде, без меню и навигации, готово к вставке в любую модель или ваш редактор. ## Для разработчиков AI-агентов Если вы делаете чат-бот, RAG-систему, или плагин для IDE — используйте `llms.txt` и `.md`-суффиксы как обычные документы для индексации. * Индекс [`llms.txt`](https://hubris.pw/llms.txt) соответствует [стандарту llmstxt.org](https://llmstxt.org) — содержит список всех страниц со ссылками на их `.mdx`-версии. * Любую страницу `/docs/` можно получить как plain Markdown, добавив `.md` или `.mdx` к URL. * Файл `llms-full.txt` кэшируется на CDN и обновляется при каждом деплое документации. Все три эндпоинта отдаются с `Content-Type: text/plain; charset=utf-8` и кэшируются на 5 минут на клиенте и 1 час на CDN. ## Часто задаваемые вопросы **Это бесплатно?** Да. Эндпоинты `llms.txt`, `llms-full.txt` и `.md`-версии страниц доступны без регистрации и без API-ключа. **Документация всегда свежая?** Да. Файлы пересобираются автоматически при каждом обновлении сайта. **Можно использовать в своём продукте?** Да. Это публичная документация, ссылайтесь и цитируйте свободно. **Модель путается или галлюцинирует?** Попробуйте более крупную модель (GPT-5, Claude Sonnet 4.6) или сократите контекст до конкретной страницы через `.md`-суффикс. Полный файл — около 200 КБ, помещается в контекст всех современных моделей. ## Что дальше * [Быстрый старт](/docs/quickstart) — первый запрос за 60 секунд. * [Каталог моделей](/models) — выбрать модель по цене и задаче. * [FAQ](/docs/faq) — короткие ответы на популярные вопросы. --- # Миграция с OpenAI (/docs/migration-from-openai) > Как переключиться с OpenAI на Hubris — что меняется, что 1-в-1. import { EnvVar } from '@/components/docs/env-var'; Если у вас уже есть код, работающий через OpenAI API, для перехода на Hubris достаточно изменить **базовый URL и API-ключ**. Остальное — формат запросов, имена параметров, структура ответа — идентично. ## Что меняется ### 1. Базовый URL Замените базовый URL OpenAI на `https://api.hubris.pw/v1`. Например, если у вас был стандартный OpenAI base URL — замените его на адрес выше. ### 2. API-ключ OpenAI-ключ (`sk-...`) → Hubris-ключ (`sk-gw-...`). Получите на [/keys](/keys). ```diff - api_key = os.environ["OPENAI_API_KEY"] + api_key = os.environ["HUBRIS_API_KEY"] ``` ### 3. Имена моделей OpenAI принимает короткие имена (например, `gpt-4o-mini`). Hubris использует полные идентификаторы вида `provider/model` — например, `openai/gpt-4o-mini`. Замените короткое имя на полный идентификатор провайдера. Полный список — в [каталоге моделей](/models). ## Что НЕ меняется * Формат тела запроса (`messages`, `tools`, `temperature`, и т. д.). * Формат ответа (`choices[0].message.content`, `usage.prompt_tokens`, `id`, `created`). * Стриминг через `stream: true` и Server-Sent Events. * Инструменты (tool calling) — `tools`, `tool_choice`, `tool_calls`. * JSON-режим — `response_format: { type: "json_object" }` или `json_schema`. * Vision (multimodal входы) — `image_url` в `content`. * OpenAI SDK работает без изменений — нужно только два параметра. ## Минимальный diff кода ### Python (с OpenAI SDK) ```python from openai import OpenAI client = OpenAI( base_url="https://api.hubris.pw/v1", # was OpenAI base URL api_key=os.environ["HUBRIS_API_KEY"], # was OPENAI_API_KEY ) response = client.chat.completions.create( model="openai/gpt-4o-mini", # was "gpt-4o-mini" messages=[{"role": "user", "content": "Привет"}], ) ``` ### Node.js (с OpenAI SDK) ```typescript import OpenAI from "openai"; const client = new OpenAI({ baseURL: "https://api.hubris.pw/v1", // was OpenAI base URL apiKey: process.env.HUBRIS_API_KEY, // was OPENAI_API_KEY }); const response = await client.chat.completions.create({ model: "openai/gpt-4o-mini", // was "gpt-4o-mini" messages: [{ role: "user", content: "Привет" }], }); ``` ## Что доступно дополнительно В Hubris вы получаете **модели не только OpenAI**, через тот же API: * **Anthropic Claude** — например, `anthropic/claude-haiku-4.5`. * **Google Gemini** — например, `google/gemini-2.5-flash`. * **OpenAI GPT** — например, `openai/gpt-4o-mini`. Полный актуальный список — в [каталоге моделей](/models). Все они отвечают в том же OpenAI-формате `chat.completion`, поэтому код менять не нужно. ## Различия в биллинге * **Цены в ₽**, не в USD. Списание происходит за каждый успешный запрос; стоимость возвращается в `usage` и логируется на [/usage](/usage). * **Минимальный баланс** для запуска запроса — 1 ₽. Реальная стоимость списывается после успешного ответа и зависит от модели и числа токенов. * **Без подписок и тарифов** — pay-as-you-go. Подробнее — на странице [Биллинг](/docs/concepts/billing). ## Что поддерживается на старте Сейчас Hubris реализует основной OpenAI-эндпоинт `POST /v1/chat/completions` (с инструментами, JSON-режимом, vision, стримингом) и `GET /v1/models`. Этого достаточно для подавляющего большинства интеграций — чат-боты, агенты, RAG, классификация, извлечение данных. Дополнительные эндпоинты (генерация эмбеддингов, картинок, аудио, batch-обработка) находятся в работе — следите за обновлениями. Для них пока используйте провайдера напрямую: когда Hubris их выпустит, переключение будет такой же сменой URL. Assistants API (`/v1/assistants`, `/v1/threads`) — не планируется: Stateless API лучше для большинства интеграций. ## Что дальше * [Быстрый старт](/docs/quickstart) — рабочий пример сразу. * [Каталог моделей](/models) — все доступные `provider/model`. * [Аутентификация](/docs/authentication) — детали по ключам. --- # Быстрый старт (/docs/quickstart) > Первый запрос к Hubris за 60 секунд. Примеры на cURL, Python, TypeScript и OpenAI SDK. import { CodeTabs } from '@/components/docs/code-tabs'; import { EnvVar } from '@/components/docs/env-var'; import { EndpointBadge } from '@/components/docs/endpoint-badge'; import { TryItButton } from '@/components/docs/try-it-button'; Подключение к Hubris — это смена одного URL. Если у вас уже есть код под OpenAI API, замените базовый URL на `https://api.hubris.pw/v1` и подставьте ключ Hubris. Всё остальное работает как было. ## 1. Получите API-ключ [Зарегистрируйтесь](/sign-in) (нужен email и одноразовый код), затем создайте ключ в разделе [API-ключи](/keys). Ключ показывается **один раз** — сохраните его в менеджер паролей или передайте в переменную окружения сразу. ## 2. Пополните баланс В разделе [Биллинг](/billing) пополните баланс через СБП — минимально 100 ₽. Этого хватит на тысячи запросов к мелким моделям. ## 3. Сделайте первый запрос Замените `sk-gw-...` на ваш ключ: ## С OpenAI SDK Если у вас уже OpenAI SDK — поменяйте только `base_url` и ключ. ## Ожидаемый ответ Стандартный OpenAI-формат: ```json { "id": "chatcmpl-...", "object": "chat.completion", "created": 1714000000, "model": "anthropic/claude-haiku-4.5", "choices": [ { "index": 0, "message": { "role": "assistant", "content": "OK" }, "finish_reason": "stop" } ], "usage": { "prompt_tokens": 12, "completion_tokens": 1, "total_tokens": 13 } } ``` Если получили `401` — ключ неверный или отсутствует заголовок `Authorization`. См. [Аутентификацию](/docs/authentication). Если `402` — мало денег на балансе, см. [Биллинг](/billing). ## Что дальше * **[Аутентификация](/docs/authentication)** — управление ключами, безопасное хранение, ротация. * **[Каталог моделей](/models)** — все доступные модели с актуальными ценами. * **[Биллинг](/billing)** — пополнение, минимальный остаток. --- # Статус сервиса (/docs/status) > Текущая доступность Hubris API и историческая аптайм-статистика. Hubris работает 24/7. Если у вас что-то не отвечает — сначала проверьте этот раздел. ## Текущий статус Проверка живости — `GET /healthz` без авторизации: ```bash curl https://api.hubris.pw/healthz # {"status":"ok"} ``` Если получили `200 OK` с `{"status":"ok"}` — сервис в норме, проблема, скорее всего, у вас (баланс, ключ, формат запроса). Если 5xx или таймаут — проблема на нашей стороне. Подождите 1–2 минуты и попробуйте снова. ## Что мониторим внутри * Задержки `p50`, `p95`, `p99` на `/v1/chat/completions` для каждой активной модели. * Доступность каталога `GET /v1/models`. * Курс ЦБ РФ — обновляется раз в день в 12:00 МСК. Если ЦБ временно недоступен и кеш истёк — каталог отдаст `503` с `code: "exchange_rate_unavailable"`. * Баланс БД, очереди в Redis, дисковое пространство. ## Если что-то сломалось 1. Посмотрите `/healthz`. 2. Если `/healthz` ок, а конкретная модель отвечает 502 — попробуйте другую модель из той же группы (например, `openai/gpt-4o-mini` вместо `openai/gpt-4o`). Часто это локальная проблема апстрима, не Hubris. 3. Если несколько разных моделей дают 502 одновременно — это инфра. Напишите на [support@hubris.pw](mailto:support@hubris.pw) с временем и request\_id из заголовка ответа. ## Публичный uptime-дашборд В работе — будет на отдельном поддомене с историей 90 дней. Пока — пишите на support, ответим оперативно. --- # Поддержка (/docs/support) > Как связаться с командой Hubris — баги, вопросы, фичи, биллинг. ## Email Главный канал — **[support@hubris.pw](mailto:support@hubris.pw)**. Отвечаем в рабочие часы по Москве, обычно в течение нескольких часов; критические инциденты — быстрее. ## Что приложить к письму В зависимости от темы: **Баг в API**: * Время запроса (UTC или МСК) и используемую модель. * Email аккаунта (по нему мы найдём запрос в логах через [/usage](/usage)). * Тело запроса — без полного содержимого `messages`, достаточно структуры. * Ответ, который пришёл — HTTP-статус и JSON ошибки. **Биллинг**: * Email вашего аккаунта. * Дата и сумма операции. * Если не пришло пополнение — ID СБП-перевода (виден в банке). **Запрос новой модели или фичи**: * Какая модель / провайдер. * Юзкейс (что вы делаете — это помогает приоритезировать). **Юр. лицо / договор**: * Реквизиты компании. * Объём планируемого использования (для оценки тарифа). ## Чего НЕ присылать * **Не присылайте API-ключи** ни в каком виде. Если ключ скомпрометирован — отзывайте на [/keys](/keys), это не требует общения с поддержкой. * **Не присылайте полное содержимое запросов с персональными данными**. Для воспроизведения бага достаточно структуры и метаданных. ## SLA Стандартный pay-as-you-go — best-effort, ответ в течение рабочего дня. Для бизнес-интеграций с гарантированным SLA (часовое реагирование, доступ 24/7) — пишите про enterprise-условия. Согласуем индивидуально. ## Что не делаем * Не помогаем с написанием prompt-ов и интеграцией под конкретную задачу — это работа разработчика. Документация на сайте + примеры в [API Reference](/docs/api/overview) должны хватить для большинства случаев. * Не комментируем содержимое ответов моделей — это поведение провайдера, не Hubris. ## Социальные сети Пока единственный канал — email. Корпоративный аккаунт в Telegram / VK — в работе. --- # Hello world (/docs/_sandbox/hello) > Демо-страница со всеми компонентами документации. import { ModelSampleCode } from '@/components/docs/model-sample-code'; import { EndpointBadge } from '@/components/docs/endpoint-badge'; import { EnvVar } from '@/components/docs/env-var'; import { ModelLink } from '@/components/docs/model-link'; Это sandbox-страница для Phase 1. Если вы её видите — Fumadocs стартанул на Tailwind 4 + React 19 RC. Установите в окружение. Попробуйте модель . ## cURL пример ```bash curl https://api.hubris.pw/v1/models \ -H "Authorization: Bearer sk-gw-..." ``` ## Code tabs (cURL / Python / TypeScript) ## Чек-лист * [x] Маршрут `/docs/_sandbox/hello` отдаёт 200 публично * [x] Sidebar и breadcrumbs рендерятся * [x] Cmd+K открывает поиск * [x] og-image генерится --- # POST /v1/chat/completions (/docs/api/chat-completions) > Создать chat completion. OpenAI-совместимый формат, поддержка стриминга, tool calling, structured outputs, vision. import { CodeTabs } from '@/components/docs/code-tabs'; import { EndpointBadge } from '@/components/docs/endpoint-badge'; Главный эндпоинт Hubris — создаёт ответ языковой модели на цепочку сообщений. OpenAI-совместимый формат — без переписывания работают `openai` Python SDK, OpenAI TypeScript SDK, LangChain, Vercel AI SDK и любые другие клиенты под OpenAI Chat Completions API. ## Эндпоинт Заголовки: | Header | Значение | | --------------- | ---------------------------------------------------------------------------------- | | `Authorization` | `Bearer sk-gw-...` (обязательно) | | `Content-Type` | `application/json` (обязательно для тела запроса) | | `Accept` | `application/json` или `text/event-stream` (опционально; при `stream: true` — SSE) | ## Тело запроса Обязательные поля помечены **жирным**. Остальное — опциональное. | Поле | Тип | По умолчанию | Описание | | ----------------------- | ----------------------------- | --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **`model`** | `string` | — | Идентификатор модели, например `anthropic/claude-haiku-4.5`. Список доступных — [`GET /v1/models`](/docs/api/models) или [каталог](/models). | | **`messages`** | `array` | — | Цепочка сообщений диалога. Минимум 1. Структура — [ниже](#сообщения). | | `models` | `array` | — | До 10 fallback-моделей. Если основная недоступна — пробует следующие по порядку. Биллинг по фактически использованной (см. `model` в ответе). См. [Model fallbacks](/docs/features/model-fallbacks). | | `temperature` | `number` 0–2 | `1` | Температура сэмплирования. 0 — детерминизм, 2 — максимум разнообразия. | | `top_p` | `number` 0–1 | `1` | Nucleus sampling. Альтернатива `temperature` — обычно меняют что-то одно. | | `n` | `integer` ≥ 1 | `1` | Сколько вариантов ответа сгенерировать. Каждый вариант оплачивается отдельно. | | `stop` | `string \| string[]` | — | До 4 стоп-последовательностей. Модель прекратит генерацию при их встрече. | | `max_tokens` | `integer` > 0 | — | Лимит токенов в ответе. OpenAI-совместимое имя. | | `max_completion_tokens` | `integer` > 0 | — | Новое имя `max_tokens` (OpenAI deprecated старое для reasoning-моделей). Можно использовать любой из двух. | | `frequency_penalty` | `number` -2…2 | `0` | Штраф за повторение токенов. | | `presence_penalty` | `number` -2…2 | `0` | Штраф за повторение тем. | | `logit_bias` | `object` | — | Карта `token_id → bias` (от -100 до 100). Управление вероятностью отдельных токенов. | | `seed` | `integer` | — | Сид для best-effort детерминизма. Не гарантирует идентичности на разных прогонах. | | `logprobs` | `boolean` | `false` | Вернуть вероятности выбранных токенов. | | `top_logprobs` | `integer` 0–20 | — | Сколько top-альтернатив возвращать вместе с каждым выбранным токеном. Требует `logprobs: true`. | | `tools` | `array` | — | Определения функций, которые модель может вызвать. Структура — [Tools](#tools). | | `tool_choice` | `string \| object` | `"auto"` | Принудительный выбор инструмента. `"none"` / `"auto"` / `"required"` или `{type:"function", function:{name:"..."}}`. | | `parallel_tool_calls` | `boolean` | `true` | Разрешить модели вызвать несколько tools в одном ответе. | | `response_format` | `object` | `{type:"text"}` | Формат ответа. `text`, `json_object` или `json_schema`. См. [Structured outputs](#structured-outputs). | | `modalities` | `array` | `["text"]` | Какие модальности возвращать. `["image","text"]` для image-gen моделей. | | `image_config` | `object` | — | Параметры image-gen: `aspect_ratio`, `image_size` и model-specific. См. [Image generation](/docs/features/image-generation). | | `stream` | `boolean` | `false` | Стриминг ответа через SSE. См. [Стриминг](#стриминг). | | `stream_options` | `object` | — | Hubris автоматически выставляет `include_usage: true` для всех стримов. Указывать не обязательно. | | `user` | `string` | — | Идентификатор конечного пользователя для трекинга абьюза на стороне провайдера. | | `reasoning_effort` | `"low" \| "medium" \| "high"` | — | Для reasoning-моделей: бюджет «думания». Подробности — [Reasoning](/docs/features/reasoning). | ## Сообщения Каждый элемент `messages` — объект с обязательным полем `role` и зависящими от роли остальными полями. ### system ```json { "role": "system", "content": "Ты — лаконичный ассистент." } ``` | Поле | Тип | Описание | | ------------- | ---------- | ------------------------ | | **`role`** | `"system"` | Роль. | | **`content`** | `string` | Системная инструкция. | | `name` | `string` | Опциональное имя автора. | ### user ```json { "role": "user", "content": "Привет! Как дела?" } ``` Content может быть строкой ИЛИ массивом мультимодальных частей (текст + картинки): ```json { "role": "user", "content": [ { "type": "text", "text": "Что на картинке?" }, { "type": "image_url", "image_url": { "url": "https://example.com/photo.jpg" } } ] } ``` Поддерживаемые `type`: `text`, `image_url`. URL может быть `https://...` или `data:image/;base64,...`. Подробности — [Vision](/docs/features/vision). ### assistant ```json { "role": "assistant", "content": "Привет! Хорошо, спасибо." } ``` С tool calls: ```json { "role": "assistant", "content": null, "tool_calls": [ { "id": "call_abc", "type": "function", "function": { "name": "get_weather", "arguments": "{\"city\":\"Москва\"}" } } ] } ``` ### tool Результат вызова функции (отправляется после `assistant`-сообщения с `tool_calls`): ```json { "role": "tool", "tool_call_id": "call_abc", "content": "{\"temp\": -5, \"condition\": \"snow\"}" } ``` ## Tools Каждый элемент `tools` — `function` (определение пользовательской функции) или `server` (server-side tool провайдера: web search, code interpreter и т.д.; зависит от модели). ### Function tool ```json { "type": "function", "function": { "name": "get_weather", "description": "Получить текущую погоду по городу", "parameters": { "type": "object", "properties": { "city": { "type": "string" } }, "required": ["city"] }, "strict": true } } ``` | Поле | Тип | Описание | | ---------------------- | ------------ | ----------------------------------------------------------------------- | | **`type`** | `"function"` | Дискриминатор. | | **`function.name`** | `string` | Имя функции. Только латиница, цифры, `_` и `-`. | | `function.description` | `string` | Описание для модели — что делает функция и когда её вызывать. | | `function.parameters` | `object` | JSON Schema аргументов функции. | | `function.strict` | `boolean` | Если `true`, модель гарантированно вернёт arguments, валидные по схеме. | Полный гайд с примерами обработки — [Tool calling](/docs/features/tool-calling). ## Structured outputs Поле `response_format` принимает три формы: ```json { "type": "text" } ``` ```json { "type": "json_object" } ``` ```json { "type": "json_schema", "json_schema": { "name": "city_info", "schema": { "type": "object", "properties": { "name": { "type": "string" }, "country": { "type": "string" }, "population": { "type": "number" } }, "required": ["name", "country", "population"] }, "strict": true } } ``` При `json_schema` модель гарантированно вернёт валидный JSON по вашей схеме. Подробности и примеры — [Structured output](/docs/features/structured-output). ## Минимальный пример ## Ответ ```json { "id": "chatcmpl-abc123", "object": "chat.completion", "created": 1714000000, "model": "anthropic/claude-haiku-4.5", "choices": [ { "index": 0, "message": { "role": "assistant", "content": "Привет! Чем могу помочь?" }, "finish_reason": "stop" } ], "usage": { "prompt_tokens": 12, "completion_tokens": 7, "total_tokens": 19, "cost": 24 } } ``` ### Поля ответа | Поле | Тип | Описание | | ----------------------------- | ------------------- | ---------------------------------------------------------------------------------------------------- | | **`id`** | `string` | Уникальный идентификатор completion. Префикс `chatcmpl-`. | | **`object`** | `"chat.completion"` | Тип объекта. Для стрима — `"chat.completion.chunk"`. | | **`created`** | `integer` | Unix-timestamp (секунды). | | **`model`** | `string` | Фактически использованная модель (отличается от запрошенной при срабатывании `models` fallback). | | **`choices[]`** | `array` | Варианты ответа. Длина равна `n` из запроса (по умолчанию 1). | | **`choices[].index`** | `integer` | Индекс варианта (0-based). | | **`choices[].message`** | `object` | Сообщение модели с `role: "assistant"`, `content` и/или `tool_calls`. | | **`choices[].finish_reason`** | `string` | Почему генерация завершилась: `stop` / `length` / `tool_calls` / `content_filter` / `function_call`. | | `choices[].logprobs` | `object` | Если в запросе был `logprobs: true`. | | **`usage.prompt_tokens`** | `integer` | Токены ввода. | | **`usage.completion_tokens`** | `integer` | Токены вывода. | | **`usage.total_tokens`** | `integer` | Сумма. | | **`usage.cost`** | `integer` | Стоимость запроса **в копейках**. Hubris-расширение поверх стандартного OpenAI ответа. | | `system_fingerprint` | `string` | Метка конфигурации модели у провайдера (для отладки воспроизводимости). | ## Стриминг С `stream: true` ответ приходит chunk-ами через Server-Sent Events: ```bash curl -N -s https://api.hubris.pw/v1/chat/completions \ -H "Authorization: Bearer sk-gw-..." \ -H "Content-Type: application/json" \ -d '{ "model": "anthropic/claude-haiku-4.5", "messages": [{"role": "user", "content": "Расскажи короткую историю"}], "stream": true }' ``` Формат каждого chunk, отслеживание usage и обработка `[DONE]` — на странице [Стриминг](/docs/features/streaming). ## Tool calling ```bash curl -s https://api.hubris.pw/v1/chat/completions \ -H "Authorization: Bearer sk-gw-..." \ -H "Content-Type: application/json" \ -d '{ "model": "anthropic/claude-haiku-4.5", "messages": [{"role": "user", "content": "Какая погода в Москве?"}], "tools": [{ "type": "function", "function": { "name": "get_weather", "description": "Получить текущую погоду по городу", "parameters": { "type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"] } } }] }' ``` Модель вернёт `tool_calls` вместо обычного `content` — выполните функцию у себя и отправьте результат вторым запросом с `role: "tool"`. Полный сценарий — [Tool calling](/docs/features/tool-calling). ## Vision ```bash curl -s https://api.hubris.pw/v1/chat/completions \ -H "Authorization: Bearer sk-gw-..." \ -H "Content-Type: application/json" \ -d '{ "model": "anthropic/claude-haiku-4.5", "messages": [{ "role": "user", "content": [ {"type": "text", "text": "Что на картинке?"}, {"type": "image_url", "image_url": {"url": "https://example.com/photo.jpg"}} ] }] }' ``` Поддерживается только моделями с `image` в `input_modalities` — см. [GET /v1/models](/docs/api/models) и страницу [Vision](/docs/features/vision). ## HTTP-коды | Код | Когда | Полный список — | | ----------------- | ------------------------------------------- | ------------------------------------------------- | | `200` | Успешный ответ | — | | `400` | Тело запроса не соответствует схеме | [Ошибки](/docs/concepts/errors) | | `401` | Ключ отсутствует / отозван / невалиден | [Ошибки](/docs/concepts/errors) | | `402` | Недостаточно средств на балансе | [Биллинг](/docs/concepts/billing) | | `404` | Модели с таким `model` не существует | [Каталог](/models) | | `429` | Превышен дневной лимит на ключе | [Rate limits](/docs/concepts/rate-limits) | | `502 / 503 / 504` | Транзиентные сбои у провайдера или курса ЦБ | [Обработка ошибок](/docs/features/error-handling) | Формат тела ошибки и стратегия retry — [Ошибки](/docs/concepts/errors). ## Что дальше * [Стриминг](/docs/features/streaming) — формат SSE и обработка в клиенте. * [Tool calling](/docs/features/tool-calling) — пошаговый сценарий с многократными итерациями. * [Structured output](/docs/features/structured-output) — JSON Schema validation. * [Ошибки](/docs/concepts/errors) — таблица всех кодов и retry-стратегий. * [Цены](/docs/concepts/pricing) — как считается `usage.cost`. --- # POST /v1/embeddings (/docs/api/embeddings) > Создать эмбеддинги — вектор фиксированной длины из текста. OpenAI-совместимый формат. import { CodeTabs } from '@/components/docs/code-tabs'; import { EndpointBadge } from '@/components/docs/endpoint-badge'; Превращает текст в вектор чисел фиксированной длины — для семантического поиска, RAG, классификации, кластеризации. OpenAI-совместимый формат — без переписывания работают `openai` Python/TypeScript SDK, LangChain, LlamaIndex и любые клиенты под OpenAI Embeddings API. Стрима нет: один POST → один JSON-ответ. Полный список embedding-моделей — в [каталоге](/models) (фильтр «embeddings»). ## Эндпоинт Заголовки: | Header | Значение | | --------------- | -------------------------------- | | `Authorization` | `Bearer sk-gw-...` (обязательно) | | `Content-Type` | `application/json` (обязательно) | ## Тело запроса | Поле | Тип | По умолчанию | Описание | | ----------------- | ---------------------------------------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------- | | **`model`** | `string` | — | ID embedding-модели, например `openai/text-embedding-3-small`. | | **`input`** | `string \| string[] \| number[] \| number[][]` | — | Текст. Поддерживает: одну строку, массив строк (батч), массив токен-ID или массив массивов токен-ID. Максимум 2048 элементов в батче. | | `encoding_format` | `"float" \| "base64"` | `"float"` | `float` — массив чисел в `embedding`. `base64` — base64-строка (\~30% меньше трафика). | | `dimensions` | `integer` > 0 | — | Уменьшенная размерность вектора. Поддерживается только моделями `openai/text-embedding-3-*`. | | `user` | `string` | — | Идентификатор конечного пользователя для трекинга абьюза на стороне провайдера. | ## Минимальный пример ## Ответ ```json { "object": "list", "data": [ { "object": "embedding", "index": 0, "embedding": [-0.012, 0.034, 0.089, ...] } ], "model": "openai/text-embedding-3-small", "usage": { "prompt_tokens": 4, "total_tokens": 4, "cost": 1 } } ``` ### Поля ответа | Поле | Тип | Описание | | ------------------------- | -------------------- | -------------------------------------------------------------------------- | | **`object`** | `"list"` | Тип контейнера. | | **`data[]`** | `array` | Векторы в том же порядке что и `input`. | | **`data[].object`** | `"embedding"` | Тип элемента. | | **`data[].index`** | `integer` | Позиция в исходном `input` (0-based). Полезно при батче. | | **`data[].embedding`** | `number[] \| string` | Сам вектор. `number[]` при `float`, base64-строка при `base64`. | | **`model`** | `string` | Фактически использованная модель. | | **`usage.prompt_tokens`** | `integer` | Токены ввода. | | **`usage.total_tokens`** | `integer` | Сумма. У embeddings всегда равна `prompt_tokens` — completion-токенов нет. | | **`usage.cost`** | `integer` | Стоимость **в копейках**. Hubris-расширение. | ## Батч `input` принимает массив строк — за один запрос до 2048 эмбеддингов. Это значительно быстрее и дешевле, чем 2048 одиночных запросов. ```bash curl -s https://api.hubris.pw/v1/embeddings \ -H "Authorization: Bearer sk-gw-..." \ -H "Content-Type: application/json" \ -d '{ "model": "openai/text-embedding-3-small", "input": ["первый текст", "второй текст", "третий текст"] }' ``` В ответе `data` — массив с тем же порядком, что и `input`. Поле `index` указывает позицию. ## Уменьшенная размерность Параметр `dimensions` поддерживается моделями `openai/text-embedding-3-small` (по умолчанию 1536) и `openai/text-embedding-3-large` (по умолчанию 3072). Меньшая размерность = меньше места в vector store и быстрее cosine similarity, но падает качество поиска. ```bash curl -s https://api.hubris.pw/v1/embeddings \ -H "Authorization: Bearer sk-gw-..." \ -H "Content-Type: application/json" \ -d '{ "model": "openai/text-embedding-3-small", "input": "Привет", "dimensions": 256 }' ``` ## Base64-encoding `encoding_format: "base64"` возвращает вектор как base64-строку вместо массива чисел — экономит \~30% трафика. Многие SDK сами декодируют обратно в float-массив. ```bash curl -s https://api.hubris.pw/v1/embeddings \ -H "Authorization: Bearer sk-gw-..." \ -H "Content-Type: application/json" \ -d '{ "model": "openai/text-embedding-3-small", "input": "Привет", "encoding_format": "base64" }' ``` ## HTTP-коды | Код | Когда | | ----------------- | --------------------------------------------------------------------------------------------------------- | | `200` | Успешный ответ. | | `400` | Тело запроса не соответствует схеме (`input` пустой, размер батча > 2048, неподдерживаемый `dimensions`). | | `401` | Ключ отсутствует / отозван / невалиден. | | `402` | Недостаточно средств на балансе. | | `404` | Модели с таким `model` не существует или это не embedding-модель. | | `429` | Превышен дневной лимит на ключе. | | `502 / 503 / 504` | Транзиентные сбои у провайдера или курса ЦБ. | Формат тела ошибки и стратегия retry — [Ошибки](/docs/concepts/errors). ## Биллинг Embeddings тарифицируются только по входным токенам — completion-токенов нет. Точная формула — на странице [Цены](/docs/concepts/pricing). Цены в ₽ за 1 миллион токенов и весь список embedding-моделей — в [каталоге](/models) с фильтром по выходной модальности «embeddings». ## Что дальше * [Модели](/docs/concepts/models) — как выбрать embedding-модель. * [Ошибки](/docs/concepts/errors) — что делать на 429/502. * [Цены](/docs/concepts/pricing) — формула расчёта. --- # POST /v1/messages (/docs/api/messages) > Эндпоинт, совместимый с Anthropic Messages API. Используется Claude Code и любым SDK Anthropic. import { CodeTabs } from '@/components/docs/code-tabs'; import { EndpointBadge } from '@/components/docs/endpoint-badge'; `/v1/messages` — эндпоинт, совместимый с [Anthropic Messages API](https://docs.anthropic.com/claude/reference/messages_post). Через него работают [Claude Code](/docs/integrations/claude-code), официальные SDK Anthropic для Python/TypeScript и любые инструменты, которые ожидают именно этот формат запросов. ## Когда использовать * Вы подключаете Claude Code или другой инструмент, поддерживающий только Anthropic-формат. * Вам нужно нативное `prompt caching` (`cache_control: { "type": "ephemeral" }`). * Вам удобнее работать с Anthropic-style блоками контента (`text`, `image`, `tool_use`, `tool_result`). Для типичных задач — [POST /v1/chat/completions](/docs/api/chat-completions) проще и поддерживает больше моделей. ## Эндпоинт ## Аутентификация Принимаются оба заголовка: | Заголовок | Когда используется | | --------------------------------- | ---------------------------------------------------------------- | | `Authorization: Bearer sk-gw-...` | Claude Code (`ANTHROPIC_AUTH_TOKEN`), произвольные cURL-запросы. | | `x-api-key: sk-gw-...` | Anthropic Python/TypeScript SDK по умолчанию. | Если переданы оба — приоритет у `Authorization`. Подойдут API-ключи Hubris формата `sk-gw-…`, созданные на [/keys](https://hubris.pw/keys). Также нужен `anthropic-version: 2023-06-01` (Anthropic-спека) и `Content-Type: application/json`. ## Тело запроса | Поле | Тип | По умолчанию | Описание | | ------------------ | ------------------- | --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **`model`** | `string` | — | Идентификатор модели. Принимает Anthropic-имена (`claude-sonnet-4-5`) и каноничные Hubris-имена (`anthropic/claude-sonnet-4.6`). См. [Имена моделей](#имена-моделей). | | **`max_tokens`** | `integer` > 0 | — | Лимит токенов в ответе. Обязательное (Anthropic-спека). | | **`messages`** | `array` | — | Цепочка сообщений. Минимум 1. `role: "user" \| "assistant"`. | | `system` | `string \| Block[]` | — | Системная инструкция (отдельно от `messages`, как в Anthropic). | | `tools` | `array` | — | Anthropic Tool Use. См. [ниже](#использование-инструментов-tools). | | `tool_choice` | `object` | `{type:"auto"}` | `{type:"auto" \| "any" \| "tool", name?}`. | | `temperature` | `number` 0–1 | `1` | Температура сэмплирования. | | `top_p` | `number` 0–1 | — | Nucleus sampling. | | `top_k` | `integer` ≥ 0 | — | Top-K sampling. | | `stop_sequences` | `string[]` | — | До 4 стоп-последовательностей. | | `stream` | `boolean` | `false` | Anthropic SSE. См. [Потоковая передача](#потоковая-передача). | | `metadata.user_id` | `string` | — | Anthropic-метка пользователя для трекинга абьюза. | ## Минимальный запрос `max_tokens` — обязательное поле (Anthropic-спека). Если не указать, вернётся `400 invalid_request_error`. ## Имена моделей Эндпоинт принимает три формы записи имени модели: | Форма | Пример | Куда маппится | | ----------------------------------- | ------------------------------------------------------- | ----------------------------------------------------------- | | Каноничная (с префиксом провайдера) | `anthropic/claude-sonnet-4.6` | без изменений | | Стабильная (без даты) | `claude-sonnet-4-5`, `claude-haiku-4-5` | `anthropic/claude-sonnet-4.5`, `anthropic/claude-haiku-4.5` | | С суффиксом даты или `-latest` | `claude-3-5-sonnet-20241022`, `claude-haiku-4-5-latest` | соответствующая каноничная версия | Список активных Claude-моделей доступен через [/v1/models](/docs/api/models) с фильтром `provider=anthropic`. ## Ответ Anthropic-нативный формат: ```json { "id": "msg_01XYZ", "type": "message", "role": "assistant", "content": [ { "type": "text", "text": "Привет!" } ], "model": "claude-sonnet-4-5", "stop_reason": "end_turn", "stop_sequence": null, "usage": { "input_tokens": 17, "output_tokens": 5, "cache_creation_input_tokens": 0, "cache_read_input_tokens": 0 } } ``` Поле `model` в ответе содержит то имя, которое вы прислали в запросе (а не каноничное), — это удобно для согласованности логов и совместимости со старыми SDK. ## Потоковая передача Параметр `stream: true` включает Anthropic SSE в нативном формате: ``` event: message_start data: {"type":"message_start","message":{...,"usage":{"input_tokens":17}}} event: content_block_start data: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""}} event: content_block_delta data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"Привет"}} event: content_block_stop data: {"type":"content_block_stop","index":0} event: message_delta data: {"type":"message_delta","delta":{"stop_reason":"end_turn"},"usage":{"output_tokens":5}} event: message_stop data: {"type":"message_stop"} event: data data: [DONE] ``` Финальное `usage` приходит в событии `message_delta`. Если клиент разорвал соединение раньше — Hubris всё равно дочитывает апстрим до конца и списывает стоимость с баланса. ## Использование инструментов (tools) Поддерживается полный набор Anthropic Tool Use: `tools`, `tool_choice`, блоки `tool_use` в ответе, блоки `tool_result` во входе. Параметры передаются без изменений на провайдера: ```bash curl https://api.hubris.pw/v1/messages \ -H "Authorization: Bearer sk-gw-..." \ -d '{ "model": "claude-sonnet-4-5", "max_tokens": 1024, "tools": [{ "name": "get_weather", "description": "Возвращает погоду в указанном городе", "input_schema": { "type": "object", "properties": { "city": { "type": "string" } }, "required": ["city"] } }], "messages": [ { "role": "user", "content": "Какая погода в Москве?" } ] }' ``` ## Vision (изображения на вход) Блоки `image` в `content[]` принимают и base64, и URL-источник: ```json { "role": "user", "content": [ { "type": "text", "text": "Что на картинке?" }, { "type": "image", "source": { "type": "base64", "media_type": "image/png", "data": "iVBORw0KGgoAAAA..." } } ] } ``` ## Кэширование промптов `cache_control: { "type": "ephemeral" }` ставится на блок контента или инструмента так же, как и в нативном Anthropic API. Hubris передаёт поле без изменений; биллинг учитывает `cache_creation_input_tokens` и `cache_read_input_tokens` по фактической стоимости, которую вернул провайдер. ```json { "role": "user", "content": [ { "type": "text", "text": "<длинный системный контекст>", "cache_control": { "type": "ephemeral" } }, { "type": "text", "text": "Вопрос пользователя" } ] } ``` ## Ошибки Возвращаются в нативном Anthropic-формате: ```json { "type": "error", "error": { "type": "invalid_request_error", "message": "max_tokens: Required" } } ``` | HTTP | error.type | Что значит | | --------- | ----------------------- | ------------------------------------------------------------------------- | | 400 | `invalid_request_error` | Неверное тело запроса (нет `max_tokens`, пустые `messages` и т.д.). | | 401 | `authentication_error` | Невалидный API-ключ или проблемы у апстрима. | | 402 | `billing_error` | Баланс ниже минимума. Пополните на [/billing](https://hubris.pw/billing). | | 404 | `not_found_error` | Модель не найдена в каталоге или имя не парсится. | | 429 | `rate_limit_error` | Превышен дневной лимит ключа или лимит апстрима. | | 500 / 502 | `api_error` | Внутренняя ошибка апстрима. | | 504 | `timeout_error` | Апстрим не ответил в отведённое время. | ## Privacy Mode На этом эндпоинте Privacy Mode (маскирование PII) **пока не поддерживается** — для запросов с маскированием используйте [POST /v1/chat/completions](/docs/api/chat-completions). Это ограничение MVP, поддержка планируется. ## Тарификация Считаем по `usage.cost`, который провайдер возвращает в ответе (включая поправку на cache hit / cache write). В редком случае, когда `cost` отсутствует, переключаемся на расчёт по токенам и каталожной цене модели. Подробности — в [Биллинге](/docs/concepts/billing). ## Что дальше * [Подключение Claude Code](/docs/integrations/claude-code) — подробный гид с настройкой и statusline-скриптом. * [POST /v1/chat/completions](/docs/api/chat-completions) — основной эндпоинт, поддерживает больше моделей. * [Каталог моделей](/models) — фильтр `provider:anthropic` для списка Claude-моделей. * [Ошибки](/docs/concepts/errors) — полная таблица кодов. --- # GET /v1/models (/docs/api/models) > Список доступных моделей с актуальными ценами в рублях. import { CodeTabs } from '@/components/docs/code-tabs'; import { EndpointBadge } from '@/components/docs/endpoint-badge'; Возвращает список всех активных моделей с актуальными ценами в ₽. Стандартный OpenAI-формат с расширениями Hubris: `display_name`, `description`, `context_window`, `modalities`, `pricing`. Полный [каталог в UI](/models) — там удобнее сравнивать цены и фильтровать. ## Эндпоинт Заголовки: | Header | Значение | | --------------- | -------------------------------- | | `Authorization` | `Bearer sk-gw-...` (обязательно) | Тело запроса не передаётся. Параметры query-string не поддерживаются — для фильтрации обрабатывайте ответ на стороне клиента. ## Пример console.log(m.id));` }, ]} /> ## Ответ ```json { "object": "list", "data": [ { "id": "anthropic/claude-haiku-4.5", "object": "model", "created": 1714000000, "owned_by": "anthropic", "display_name": "Claude Haiku 4.5", "description": "Быстрая и дешёвая модель Anthropic. Подходит для большинства задач.", "context_window": 200000, "max_output_tokens": 8192, "modalities": { "input": ["text", "image"], "output": ["text"] }, "pricing": { "prompt_per_1m_rub": 96.50, "completion_per_1m_rub": 482.50, "image_input_per_1k_rub": 14.48 } } ] } ``` ### Поля ответа | Поле | Тип | Описание | | ------------------------------------------ | -------------- | -------------------------------------------------------------------------------- | | **`object`** | `"list"` | Тип контейнера. | | **`data[]`** | `array` | Массив моделей. | | **`data[].id`** | `string` | Идентификатор для использования в `model` запроса (формат `/`). | | **`data[].object`** | `"model"` | Тип элемента. | | **`data[].created`** | `integer` | Unix-timestamp когда модель появилась в каталоге Hubris. | | **`data[].owned_by`** | `string` | Провайдер модели (`anthropic`, `openai`, `google`, ...). | | **`data[].display_name`** | `string` | Человеко-читаемое имя для UI. | | **`data[].description`** | `string` | Краткое описание модели. | | **`data[].context_window`** | `integer` | Размер контекста в токенах. | | **`data[].max_output_tokens`** | `integer` | Максимум токенов в ответе. | | **`data[].modalities.input`** | `string[]` | Что принимает: `text`, `image`, `audio`. | | **`data[].modalities.output`** | `string[]` | Что возвращает: `text`, `image`, `embeddings`. | | **`data[].pricing.prompt_per_1m_rub`** | `number` | Цена входных токенов за 1 миллион, в ₽. | | **`data[].pricing.completion_per_1m_rub`** | `number` | Цена выходных токенов за 1 миллион, в ₽. | | `data[].pricing.image_input_per_1k_rub` | `number` | Цена 1000 входных изображений (для vision-моделей). | Цены пересчитываются каждые несколько часов по курсу ЦБ РФ + наш markup. Точная формула — [Цены](/docs/concepts/pricing). ## Фильтрация API не поддерживает query-параметры. Фильтруйте локально: ```python models = client.models.list().data # Только embedding-модели embed_models = [m for m in models if 'embeddings' in m.modalities['output']] # Vision-модели Anthropic дешевле 100₽/1M токенов cheap_vision = [ m for m in models if m.owned_by == 'anthropic' and 'image' in m.modalities['input'] and m.pricing['prompt_per_1m_rub'] < 100 ] ``` ## HTTP-коды | Код | Когда | | ----- | ----------------------------------------------------------------------------- | | `200` | Успешный ответ. | | `401` | Ключ отсутствует / отозван / невалиден. | | `429` | Превышен дневной лимит на ключе. | | `503` | Курс ЦБ временно недоступен — каталог не отдаёт цены. Повторите через минуту. | ## Что дальше * [Модели](/docs/concepts/models) — обзор каталога и как выбрать модель. * [Цены](/docs/concepts/pricing) — формула расчёта стоимости. * [Каталог в UI](/models) — удобнее искать и сравнивать. --- # Обзор API (/docs/api/overview) > Базовый URL, заголовки, аутентификация и общие соглашения Hubris API. Hubris API совместим с OpenAI Chat Completions API. Все запросы — поверх HTTPS, авторизация через Bearer-токен, ответы — JSON с UTF-8. ## Базовый URL ``` https://api.hubris.pw/v1 ``` Все эндпоинты идут от этого префикса. Например, `https://api.hubris.pw/v1/chat/completions` или `https://api.hubris.pw/v1/models`. ## Заголовки Обязательные: * `Authorization: Bearer sk-gw-<32-hex>` — ваш API-ключ. См. [Аутентификацию](/docs/authentication). * `Content-Type: application/json` — для всех `POST`-запросов. Опциональные: * `Accept: application/json` — явно ожидать JSON-ответ. По умолчанию мы и так отдаём JSON. * `Accept: text/event-stream` — при `stream: true` сервер автоматически переключается в SSE-режим. ## Эндпоинты | Метод | Путь | Описание | | ----- | ---------------------- | ---------------------------------------------------- | | GET | `/v1/models` | Список доступных моделей с ценами в ₽ | | POST | `/v1/chat/completions` | Создать ответ модели (с потоковой передачей или без) | | POST | `/v1/responses` | Создать ответ через Responses API (бета) | ## Версионирование Префикс `/v1` — стабильный. Все несовместимые изменения эндпоинтов будут идти под `/v2/...` (или новее), не ломая существующие интеграции. Метка **бета** у `/v1/responses` относится только к составу внутренних блоков ответа (`output[]`) — туда со временем добавляются новые типы (рассуждения, вызовы инструментов и т.д.) по мере того, как их вводят провайдеры моделей. Авторизация, тарификация и потоковая передача — стабильны и подходят для продакшена. Если ваш клиент строго разбирает конкретные типы блоков, будьте готовы расширять обработку по мере их появления. ## Идемпотентность `POST /v1/chat/completions` и `POST /v1/responses` не идемпотентны — каждый запрос порождает новый ответ модели, независимо от тела. Если у вас на стороне клиента включены повторы при сбоях, помните: каждая повторная попытка спишет деньги, если первая прошла успешно (что иногда бывает при кратковременных сбоях сети). `GET /v1/models` идемпотентен — ответ кешируется на стороне Hubris \~60 секунд. ## Потоковая передача При `stream: true` ответ приходит через Server-Sent Events. Подробности формата — на отдельной странице [Потоковая передача](/docs/api/streaming). ## Ошибки Все ошибки в OpenAI-формате `{ error: { message, type, code } }`. Полный список кодов — на странице [Ошибки](/docs/concepts/errors). ## Что дальше * [POST /v1/chat/completions](/docs/api/chat-completions) — основной эндпоинт. * [GET /v1/models](/docs/api/models) — каталог. * [Потоковая передача](/docs/api/streaming) — формат SSE. * [POST /v1/responses](/docs/api/responses) — бета-эндпоинт для Responses API (рассуждающие модели, Codex CLI и т.п.). --- # POST /v1/responses (бета) (/docs/api/responses) > Эндпоинт OpenAI Responses API. Подходит для рассуждающих моделей и агентов. import { CodeTabs } from '@/components/docs/code-tabs'; import { EndpointBadge } from '@/components/docs/endpoint-badge'; > **Бета.** Авторизация, тарификация и потоковая передача стабильны и подходят для продакшена. Метка беты относится к составу внутренних блоков `output[]` (текст, рассуждения, вызовы инструментов) — туда со временем добавляются новые типы по мере появления у провайдеров моделей. Если ваш клиент строго разбирает конкретные блоки — будьте готовы расширять обработку. `/v1/responses` — альтернативный эндпоинт OpenAI Responses API (без сохранения состояния на стороне сервера). Подходит для рассуждающих моделей (o-серия, Claude с расширенным рассуждением), для агентских CLI вроде Codex и для абстракций, где нужен более гибкий контейнер ответа, чем `chat.completion`. ## Когда использовать Чаще всего вам нужен [POST /v1/chat/completions](/docs/api/chat-completions) — он совместим с большинством OpenAI-инструментов. `/v1/responses` стоит выбрать только если: * Вы работаете с рассуждающими моделями (o1, o3, Claude с расширенным рассуждением) и хотите явно управлять блоками рассуждения. * Вы строите агентский фреймворк, которому нужен единый контейнер для разных типов выходных блоков. * Используете агентский CLI вроде [Codex](/docs/frameworks/codex-cli), который ходит только в Responses API. * Конкретный клиент или провайдер требует именно этот формат. ## Эндпоинт Заголовки: | Header | Значение | | --------------- | ---------------------------------------------------------------------------------- | | `Authorization` | `Bearer sk-gw-...` (обязательно) | | `Content-Type` | `application/json` (обязательно) | | `Accept` | `application/json` или `text/event-stream` (опционально; при `stream: true` — SSE) | ## Тело запроса | Поле | Тип | По умолчанию | Описание | | ----------- | -------------------- | ------------ | -------------------------------------------------------------------------------------------------------------------------------------- | | **`model`** | `string` | — | Идентификатор модели, например `openai/o1-mini`. | | **`input`** | `string \| object[]` | — | Запрос. Простая строка ИЛИ массив input-блоков (см. [OpenAI Responses API](https://platform.openai.com/docs/api-reference/responses)). | | `stream` | `boolean` | `false` | Стриминг ответа через SSE. | Дополнительные параметры (instructions, tools, temperature и т.д.) передаются модели как есть — Hubris их не нормализует. Полная схема — в [OpenAI Responses API docs](https://platform.openai.com/docs/api-reference/responses). ## Минимальный пример `input` может быть строкой или массивом блоков — передаваемый формат зависит от модели. ## Ответ ```json { "id": "resp_abc123", "object": "response", "created_at": 1714000000, "model": "openai/gpt-4o-mini", "output": [ { "type": "message", "role": "assistant", "content": [ { "type": "output_text", "text": "Привет! Хорошо, спасибо." } ] } ], "usage": { "input_tokens": 8, "output_tokens": 12, "total_tokens": 20, "cost": 24 }, "status": "completed" } ``` ### Поля ответа | Поле | Тип | Описание | | ------------------------- | ------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **`id`** | `string` | Уникальный идентификатор response. Префикс `resp_`. | | **`object`** | `"response"` | Тип объекта. | | **`created_at`** | `integer` | Unix-timestamp (секунды). | | **`model`** | `string` | Фактически использованная модель. | | **`output[]`** | `array` | Выходные блоки. Структура зависит от модели и версии — может содержать `message`, `reasoning`, `tool_call` и другие типы. Hubris передаёт как есть, без нормализации. | | **`usage.input_tokens`** | `integer` | Токены ввода. | | **`usage.output_tokens`** | `integer` | Токены вывода (включая reasoning-токены, если модель их использует). | | **`usage.total_tokens`** | `integer` | Сумма. | | **`usage.cost`** | `integer` | Стоимость **в копейках**. Hubris-расширение. | | **`status`** | `"completed" \| "in_progress" \| "failed" \| ...` | Состояние генерации. | ## Стриминг `stream: true` включает SSE. Завершающее событие `response.completed` содержит полный объект `response` вместе с `usage`. Hubris списывает стоимость из этого события (или, в редком случае обрыва до его прихода, — из оценки по длине ввода). ```bash curl -N -s https://api.hubris.pw/v1/responses \ -H "Authorization: Bearer sk-gw-..." \ -H "Content-Type: application/json" \ -d '{ "model": "openai/gpt-4o-mini", "input": "Объясни квантовую запутанность", "stream": true }' ``` Формат событий и их обработка — на странице [Стриминг](/docs/features/streaming). ## HTTP-коды | Код | Когда | | ----------------- | ----------------------------------------------------------------------- | | `200` | Успешный ответ. | | `400` | Тело запроса не соответствует схеме. | | `401` | Ключ отсутствует / отозван / невалиден. | | `402` | Недостаточно средств на балансе. | | `404` | Модели с таким `model` не существует или не поддерживает Responses API. | | `429` | Превышен дневной лимит на ключе. | | `502 / 503 / 504` | Транзиентные сбои у провайдера или курса ЦБ. | Формат тела ошибки и стратегия retry — [Ошибки](/docs/concepts/errors). ## Тарификация Считаем так же, как и для `/v1/chat/completions`: входные + выходные токены × цены модели в ₽. Токены рассуждения (если модель их использует) учитываются в `output_tokens` и тарифицируются как обычные выходные токены. ## Что дальше * [POST /v1/chat/completions](/docs/api/chat-completions) — основной эндпоинт, подходит большинству задач. * [Codex CLI](/docs/frameworks/codex-cli) — как подключить агентский CLI к Hubris через Responses API. * [Стриминг](/docs/features/streaming) — формат событий SSE. * [Ошибки](/docs/concepts/errors) — таблица кодов. --- # Потоковая передача (/docs/api/streaming) > Формат Server-Sent Events для потокового ответа /v1/chat/completions. При `stream: true` ответ от `/v1/chat/completions` приходит как Server-Sent Events: каждый chunk — отдельная строка `data: ` с разделителем `\n\n`. Поток завершается строкой `data: [DONE]`. ## Заголовки ответа ``` HTTP/1.1 200 OK Content-Type: text/event-stream Cache-Control: no-cache Connection: keep-alive X-Accel-Buffering: no ``` ## Формат chunk-а Каждый chunk — JSON-объект `chat.completion.chunk`: ```json { "id": "chatcmpl-abc123", "object": "chat.completion.chunk", "created": 1714000000, "model": "anthropic/claude-haiku-4.5", "choices": [ { "index": 0, "delta": { "role": "assistant", "content": "О" }, "finish_reason": null } ] } ``` В первом chunk-е `delta` обычно содержит `role: "assistant"`. Следующие — только инкремент `content`. Последний chunk перед `[DONE]` имеет `finish_reason: "stop"` (или `length`, `tool_calls`, и т.д.) и поле `usage` с итоговыми токенами: ```json { "id": "chatcmpl-abc123", "object": "chat.completion.chunk", "created": 1714000000, "model": "anthropic/claude-haiku-4.5", "choices": [ { "index": 0, "delta": {}, "finish_reason": "stop" } ], "usage": { "prompt_tokens": 12, "completion_tokens": 1, "total_tokens": 13 } } ``` После `usage`-chunk-а поток завершается: ``` data: [DONE] ``` Hubris автоматически добавляет `stream_options: { include_usage: true }` ко всем stream-запросам, поэтому `usage` приходит всегда. ## Парсинг Парсите построчно с буфером: один chunk может прийти разорванным на несколько TCP-сегментов. Псевдокод: ```python buffer = "" for raw_bytes in response.iter_content(): buffer += raw_bytes.decode("utf-8") while "\n\n" in buffer: line, buffer = buffer.split("\n\n", 1) if not line.startswith("data: "): continue payload = line[len("data: "):] if payload == "[DONE]": return chunk = json.loads(payload) # обработка delta ``` Не парсьте `[DONE]` как JSON — это литерал-маркер, не JSON. ## Дисконнект Если клиент закрыл соединение до окончания стрима, Hubris всё равно дочитывает ответ от провайдера и списывает деньги — это защищает от «бесплатных токенов» через abort. Если вы намеренно прерываете запрос (например, по timeout-у), ожидайте полное списание стоимости. ## Что дальше * [POST /v1/chat/completions](/docs/api/chat-completions) — параметры запроса. * [Ошибки](/docs/concepts/errors) — что бывает при сбое стрима. --- # GET /v1/usage (/docs/api/usage) > Расход по API-ключу за выбранный период. Удобно для дашбордов и алертов. import { CodeTabs } from '@/components/docs/code-tabs'; import { EndpointBadge } from '@/components/docs/endpoint-badge'; Возвращает итоговый расход (рубли, копейки, токены, число запросов) **по тому API-ключу, которым сделан запрос**. Расход других ключей того же аккаунта сюда не попадёт — это специально, чтобы один скомпрометированный ключ не показывал общий бюджет. По умолчанию — последние 24 часа. Период задаётся либо шорткатом `?period=`, либо явными `?from=&to=` (ISO 8601, UTC). Опциональный `?granularity=hour|day` добавит массив `buckets` для построения графика. ## Эндпоинт Заголовки: | Header | Значение | | --------------- | -------------------------------- | | `Authorization` | `Bearer sk-gw-...` (обязательно) | ## Query-параметры | Параметр | Тип | По умолчанию | Описание | | ------------- | -------------------------------------------- | ------------ | ---------------------------------------------------------------------------------------------------------- | | `period` | `"today" \| "24h" \| "7d" \| "30d" \| "mtd"` | `"24h"` | Шорткат периода. Взаимоисключающий с `from`/`to`. | | `from` | `string` (ISO 8601, UTC) | — | Начало периода включительно. Требует `to`. | | `to` | `string` (ISO 8601, UTC) | — | Конец периода включительно. Требует `from`. | | `granularity` | `"hour" \| "day"` | — | Если задано — в ответе появится `buckets[]` с поминутной разбивкой. Без `granularity` ответ только totals. | `period` и `from`/`to` нельзя передавать одновременно — будет `400 invalid_request`. Максимальный диапазон при явных датах — 365 дней. ### Шорткаты периода | `period` | Что считается | | -------- | ------------------------------------ | | `today` | С 00:00 UTC текущих суток до сейчас. | | `24h` | Последние 24 часа (по умолчанию). | | `7d` | Последние 7 суток. | | `30d` | Последние 30 суток. | | `mtd` | С 1-го числа текущего месяца (UTC). | ## Минимальный пример ## Ответ ```json { "object": "usage", "period": { "from": "2026-05-13T09:29:31.192Z", "to": "2026-05-14T09:29:31.192Z", "shortcut": null }, "scope": { "key_id": "31fb0d0d-ac86-4f72-815b-bdefd5978747", "key_prefix": "sk-gw-758f...d4e3" }, "totals": { "requests": 2, "prompt_tokens": 300, "completion_tokens": 130, "total_tokens": 430, "cost_rub": 54.76, "cost_kopecks": "5476" }, "granularity": null, "buckets": null } ``` ### Поля ответа | Поле | Тип | Описание | | ------------------------------ | ------------------------- | ---------------------------------------------------------------------------------------------- | | **`object`** | `"usage"` | Тип объекта. | | **`period.from`** | `string` (ISO 8601 UTC) | Начало периода включительно. | | **`period.to`** | `string` (ISO 8601 UTC) | Конец периода включительно. | | **`period.shortcut`** | `string \| null` | Если использовался шорткат — его имя; иначе `null`. | | **`scope.key_id`** | `string` (UUID) | ID ключа, по которому собирается расход. | | **`scope.key_prefix`** | `string` | Видимый префикс ключа для логов. | | **`totals.requests`** | `integer` | Кол-во запросов за период. | | **`totals.prompt_tokens`** | `integer` | Сумма входных токенов. | | **`totals.completion_tokens`** | `integer` | Сумма выходных токенов. | | **`totals.total_tokens`** | `integer` | Сумма обоих. | | **`totals.cost_rub`** | `number` | Итоговая стоимость в рублях. Эквивалент `cost_kopecks / 100`. | | **`totals.cost_kopecks`** | `string` | Итоговая стоимость в копейках. Строка — чтобы не терять точность на больших суммах (`BigInt`). | | `granularity` | `"hour" \| "day" \| null` | Если в запросе был задан — повторяется здесь. | | `buckets[]` | `array \| null` | Если `granularity` задан — массив значений по интервалам. | | `buckets[].bucket` | `string` (ISO 8601) | Начало интервала. | | `buckets[].cost_rub` | `number` | Стоимость интервала. | | `buckets[].cost_kopecks` | `string` | Стоимость в копейках. | | `buckets[].requests` | `integer` | Кол-во запросов в интервале. | ## С разбивкой по дням С `granularity` в ответе появится `buckets`: ```bash curl -s -H "Authorization: Bearer $HUBRIS_API_KEY" \ "https://api.hubris.pw/v1/usage?period=7d&granularity=day" ``` ```json { "granularity": "day", "buckets": [ { "bucket": "2026-05-07T00:00:00.000Z", "cost_rub": 0, "cost_kopecks": "0", "requests": 0 }, { "bucket": "2026-05-08T00:00:00.000Z", "cost_rub": 0, "cost_kopecks": "0", "requests": 0 }, { "bucket": "2026-05-14T00:00:00.000Z", "cost_rub": 54.76, "cost_kopecks": "5476", "requests": 2 } ] } ``` ## Произвольный диапазон ```bash curl -s -H "Authorization: Bearer $HUBRIS_API_KEY" \ "https://api.hubris.pw/v1/usage?from=2026-04-01T00:00:00Z&to=2026-05-01T00:00:00Z" ``` ## Точность * `cost_kopecks` — **строка**, чтобы не терять точность на больших суммах (JavaScript `Number` не выдержит, если когда-нибудь биллим сильно дорогих агентов). Парсить через `BigInt`. * `cost_rub` — то же значение, делённое на 100, для удобства отображения. * Время в UTC. Если нужно «сутки по Москве» — пришлите явные `from`/`to`, конвертированные на стороне клиента. ## HTTP-коды | Код | Когда | | ----- | --------------------------------------------------------------------------- | | `200` | Успешный ответ. | | `400` | Конфликт `period` + `from`/`to`, диапазон > 365 дней, неверный формат даты. | | `401` | Ключ отсутствует / отозван / невалиден. | | `429` | Превышен дневной лимит на ключе. | ## Что дальше * [Биллинг](/docs/concepts/billing) — как формируется итоговая стоимость и из чего складываются токены. * [Цены](/docs/concepts/pricing) — формула расчёта. * В UI: страница [/usage](/usage) в дашборде с фильтрами по модели, ключу и статусу + CSV-экспорт. --- # Биллинг (/docs/concepts/billing) > Баланс, минимальный остаток, пополнение через СБП, поведение при нехватке средств. Hubris работает по предоплатной модели: вы пополняете баланс через СБП, и каждый успешный запрос списывает с него стоимость. Подписок и тарифов нет. ## Баланс Текущий баланс отображается на странице [/billing](/billing) в режиме реального времени. Там же — история пополнений и история расходов. Также баланс виден в шапке дашборда (рядом с меню профиля). ## Пополнение через СБП 1. На странице [/billing](/billing) нажмите «Пополнить». 2. Введите сумму (минимум 100 ₽, максимум 10 000 ₽ за одну операцию). 3. Сканируйте QR-код в банковском приложении или нажмите «Открыть в приложении» (если на телефоне). 4. Подтвердите перевод в банке. 5. Зачисление происходит автоматически в течение 10–30 секунд. Если зачисление не пришло за 5 минут — напишите на [support@hubris.pw](mailto:support@hubris.pw) и приложите ID операции СБП (его видно в банковском приложении). ## Минимальный баланс Hubris требует минимум **100 копеек (1 ₽)** на балансе для обработки запроса. При меньшем балансе любой запрос вернёт **402 Payment Required** ДО обращения к модели: ```json { "error": { "message": "Insufficient balance", "type": "invalid_request_error", "code": "insufficient_balance" } } ``` Это не значит, что 1 ₽ хватит на весь день. Это значит, что Hubris не запустит запрос, если баланса заведомо мало. Реальная стоимость запроса может быть от 1 копейки до нескольких рублей в зависимости от модели и размера сообщений. ## Уход в минус Стоимость запроса известна **только после** ответа модели — мы не можем угадать заранее, сколько токенов сгенерирует модель. Поэтому возможна ситуация: на балансе было 50 ₽, запрос стоил 60 ₽, баланс ушёл в **−10 ₽**. Это допустимо, но только на одну операцию. Следующий запрос с отрицательным балансом вернёт `402 Insufficient balance` — пополнение разблокирует следующие запросы. ## История В разделе [/billing](/billing) видно: * Каждое пополнение (дата, сумма, источник СБП). * Каждый расход — со ссылкой на запрос в `/usage`. * Корректировки от поддержки (если вы запрашивали возврат). ## Возврат средств Hubris работает по pay-as-you-go — фактически использованные токены не возвращаются. Однако если вас списали ошибочно (некорректный billing у провайдера, баг на нашей стороне) — напишите на [support@hubris.pw](mailto:support@hubris.pw) с request\_id из ответа или временем запроса. Мы вернём деньги. Возврат неиспользованного остатка с баланса — по запросу через support, обычно в течение 3 рабочих дней через тот же СБП-перевод. ## Лимиты В стандартном тарифе у пользовательских ключей **нет искусственных лимитов** запросов в секунду или в день. Расход ограничен только балансом. Служебным ключам (CI, скрипты) можно задавать дневной лимит — это страховка от утечки ключа. Подробнее — в разделе [Rate limits](/docs/concepts/rate-limits). ## Что дальше * [Цены](/docs/concepts/pricing) — формула расчёта стоимости запроса. * [Аутентификация](/docs/authentication) — как создать и отозвать ключ. * [/billing](/billing) — текущий баланс и история. --- # Ошибки (/docs/concepts/errors) > Формат ответа об ошибке, таблица всех кодов, что делать в каждом случае. Все ошибки Hubris API возвращаются в OpenAI-совместимом формате. Это значит — стандартные SDK обрабатывают их без специальной адаптации. ## Формат ```json { "error": { "message": "Описание ошибки на английском", "type": "<категория ошибки>", "code": "<машинно-читаемый код>" } } ``` * **`message`** — человекочитаемое сообщение (на английском, для совместимости с OpenAI-инструментами). * **`type`** — категория. Возможные значения: `invalid_request_error`, `rate_limit_error`, `api_error`. * **`code`** — машинный код для программной обработки. Список ниже. ## Таблица кодов | HTTP | code | Что значит | Что делать | | ---- | --------------------------- | ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ | | 400 | `invalid_request` | Тело запроса не соответствует схеме (пропущенное поле, неверный тип, выход за допустимые границы) | Проверить JSON по [Быстрому старту](/docs/quickstart) | | 401 | `invalid_api_key` | Ключ отсутствует, отозван, или невалиден | Проверить заголовок `Authorization`, создать новый ключ на [/keys](/keys) | | 402 | `insufficient_balance` | На балансе меньше минимального остатка (100 копеек) | Пополнить через [/billing](/billing) | | 404 | `model_not_found` | Модель с таким `id` не существует или неактивна | Проверить написание, посмотреть [/models](/models) | | 429 | `daily_limit_exceeded` | Превышен дневной лимит на конкретном ключе (только для служебных) | Дождаться сброса лимита или использовать ключ без лимита | | 502 | `upstream_error` | Провайдер модели вернул ошибку (5xx, контент-фильтр, internal error) | Повторить через несколько секунд; если повторяется — попробовать другую модель | | 503 | `exchange_rate_unavailable` | Курс ЦБ РФ временно недоступен — каталог не отдаёт цены | Повторить через минуту | | 504 | `upstream_timeout` | Провайдер модели не ответил за 120 секунд | Повторить; если повторяется — модель перегружена, попробовать другую | ## Примеры ### 401 (нет ключа) Запрос: `curl https://api.hubris.pw/v1/models` ```json { "error": { "message": "Missing API key", "type": "invalid_request_error", "code": "invalid_api_key" } } ``` ### 402 (баланс ноль) ```json { "error": { "message": "Insufficient balance", "type": "invalid_request_error", "code": "insufficient_balance" } } ``` ### 404 (модели не существует) Запрос с несуществующим идентификатором модели: ```json { "error": { "message": "Model not found: fake-provider/nonexistent", "type": "invalid_request_error", "code": "model_not_found" } } ``` ### 502 (апстрим лёг) ```json { "error": { "message": "Upstream returned an error", "type": "api_error", "code": "upstream_error" } } ``` ## Стратегия обработки В вашем коде имеет смысл: 1. **На 401, 402, 404** — не повторять. Это ошибки конфигурации, retry не поможет. 2. **На 502, 503, 504** — повторить с экспоненциальным backoff (1с, 2с, 4с, до 3 попыток). Это транзиентные сбои. 3. **На 429** — посмотреть на ключ: если у него выставлен дневной лимит, дождаться сброса (окно скользящее, считается от каждого списания); если лимит не выставлен — проверьте [/keys](/keys). 4. **На 400** — почти всегда баг в коде. Логируйте полный запрос (без ключа) для разбора. ## Стриминг При `stream: true` и ошибке ДО начала стрима возвращается стандартный JSON-ответ выше. Если ошибка случается **во время** стрима (модель упала на середине) — стрим обрывается на серверной стороне. Клиент получает событие `[DONE]` с пустым `delta` и `finish_reason: "stop"`. Точное поведение зависит от провайдера. Hubris записывает в логи ваш конкретный сценарий, чтобы вы могли разобраться через [/usage](/usage). ## Что дальше * [Быстрый старт](/docs/quickstart) — рабочий пример без ошибок. * [Аутентификация](/docs/authentication) — про 401. * [Биллинг](/docs/concepts/billing) — про 402. --- # Бесплатные модели (/docs/concepts/free-models) > 25+ моделей с нулевой ценой — Llama, Gemma, Liquid, BGE и другие. Лимит 100 запросов в сутки на пользователя. В каталоге Hubris есть **25+ моделей с нулевой ценой**: open-source LLM (Llama, Gemma, Liquid), embedding-модели (BGE, MiniLM, GTE) и другие. Они отмечены значком «Бесплатно» в [каталоге моделей](/models). Бесплатные модели — это: * **0 ₽ за 1М токенов** — ничего не списывается с баланса. * **Доступны без депозита** — даже с балансом 0 ₽ запрос проходит. * **Лимит 100 запросов в сутки** на пользователя — скользящее окно 24 ч. Идеально подходят для: * Знакомства с API без оплаты. * Тестов и прототипов в проектах с маленьким бюджетом. * Embedding-задач (BGE, MiniLM, GTE — отличные модели для русского/английского текста). ## Как использовать Просто передайте ID бесплатной модели — никаких особых заголовков или флагов: ```bash curl -s https://api.hubris.pw/v1/chat/completions \ -H "Authorization: Bearer sk-gw-..." \ -H "Content-Type: application/json" \ -d '{ "model": "meta-llama/llama-3.2-3b-instruct:free", "messages": [{"role": "user", "content": "Привет"}] }' ``` Список бесплатных моделей в каталоге доступен через API: ```bash curl -s 'https://api.hubris.pw/v1/models' \ -H "Authorization: Bearer sk-gw-..." \ | jq '.data[] | select(.pricing.input_rub_per_million == 0 and .pricing.output_rub_per_million == 0) | .id' ``` ## Что считается «бесплатной» моделью Любая модель из каталога, у которой **обе цены в `pricing` равны 0**: ```json { "id": "meta-llama/llama-3.2-3b-instruct:free", "pricing": { "input_rub_per_million": 0, "output_rub_per_million": 0, "currency": "RUB" } } ``` Большинство таких моделей имеют суффикс `:free`, но это не строгое правило — Hubris проверяет именно цены, а не имя. Если когда-нибудь модель станет платной, она автоматически перестанет считаться бесплатной. ## Лимит и его правила **100 запросов на пользователя в сутки** (скользящее окно 24 ч). Считаются только запросы к бесплатным моделям — платные не учитываются. При превышении возвращается **`429 daily_limit_exceeded`**: ```json { "error": { "message": "Free-tier daily limit reached: 100 requests per 24h. Upgrade by using a paid model or wait for the rolling window to reset.", "type": "rate_limit_error", "code": "daily_limit_exceeded" } } ``` Когда лимит сбросится — зависит от того, когда был сделан первый из 100 запросов: окно скользит. Через 24 часа после первого запроса один слот освобождается; и так далее. Лимит **общий для всех бесплатных моделей** — нельзя «накопить» по 100 на каждой модели. Это сделано чтобы: 1. Защитить наш общий ключ к провайдеру от 429 от него самого. 2. Не дать ботам жечь бесплатный пул и блокировать его для реальных пользователей. Лимит сейчас в Hubris не настраивается на стороне пользователя. Если вам нужен больше — переходите на платные модели. **Связь с лимитом ключа.** Поле «Дневной лимит, ₽» на странице [API-ключи](/keys) считает только сумму `cost_kopecks` за 24 ч. Бесплатные модели стоят 0 ₽, поэтому в этот лимит не входят и не «съедают» его. Это два независимых счётчика. ## Лимиты на стороне провайдера Помимо нашего лимита 100 запросов в сутки, **у самого провайдера модели есть свои лимиты на бесплатный пул**, которые мы не контролируем и не публикуем: * лимиты RPM/RPD выставляет провайдер модели, не Hubris; * конкретные значения провайдер не публикует — мы их тоже не знаем заранее; * бесплатный пул общий для всех клиентов провайдера, поэтому в часы пиковой нагрузки он быстро переполняется; * в этот момент Hubris транслирует от провайдера **HTTP 429** с сообщением вида: ```json { "error": { "message": ":free is temporarily rate-limited upstream. Please retry shortly...", "type": "invalid_request_error", "code": "invalid_request" } } ``` Это не сбой Hubris и не превышение вашего собственного дневного лимита — провайдер просто временно отказывает всему бесплатному трафику. **Что делать при 429 от провайдера:** 1. **Повторить запрос с экспоненциальной задержкой** (1 с → 2 с → 4 с → 8 с, с разумным потолком). Часто бесплатный пул освобождается за несколько секунд. 2. **Указать платный fallback** через [Model Fallback](/docs/features/model-fallbacks) — в массиве `models` поставить платную версию той же модели последним элементом: ```bash curl -s https://api.hubris.pw/v1/chat/completions \ -H "Authorization: Bearer sk-gw-..." \ -H "Content-Type: application/json" \ -d '{ "model": "deepseek/deepseek-v4-flash:free", "models": [ "deepseek/deepseek-v4-flash:free", "deepseek/deepseek-v4-flash" ], "messages": [{"role": "user", "content": "..."}] }' ``` Hubris автоматически переключится на следующий элемент при 429 от бесплатного. Тарифицируется тот, кто реально ответил. 3. **Для боевой нагрузки** — использовать платную версию модели изначально. У платных нет общего бесплатного пула и нет таких всплесков 429. ## Чем бесплатные отличаются от платных | Параметр | Бесплатные | Платные | | ------------------ | ---------------------------------------------- | ---------------------------------------------- | | Цена | 0 ₽ | По каталогу | | Минимальный баланс | Не требуется | 1 ₽ | | Дневной лимит | 100 запросов / 24 ч | Не применяется (или ваш custom-лимит на ключе) | | Качество | Мелкие/средние LLM | Любые, включая флагманы | | Латентность | Часто выше — провайдеры приоритезируют платных | Стандартная | | SLA | Best-effort | Соответствует провайдеру | **Внимание:** бесплатные модели на стороне провайдера часто ограничены гораздо жёстче платных. Может прийти **429 от провайдера**, даже если ваш собственный дневной лимит не исчерпан. Подробнее — в разделе [Лимиты на стороне провайдера](#лимиты-на-стороне-провайдера) выше. ## Сочетание с Model Fallback Хороший паттерн — дешёвая основа, платная подстраховка: ```bash curl -s https://api.hubris.pw/v1/chat/completions \ -H "Authorization: Bearer sk-gw-..." \ -H "Content-Type: application/json" \ -d '{ "model": "meta-llama/llama-3.2-3b-instruct:free", "models": [ "meta-llama/llama-3.2-3b-instruct:free", "anthropic/claude-haiku-4.5" ], "messages": [{"role": "user", "content": "..."}] }' ``` Если бесплатная Llama упадёт от провайдерского 429 — Hubris автоматически переключится на платный Haiku. Биллится тот, кто реально ответил — то есть в большинстве случаев это будет 0 ₽, и только в редких сбоях — копейки за Haiku. ## Что дальше * [Модели](/docs/concepts/models) — обзор каталога. * [Model Fallback](/docs/features/model-fallbacks) — про автоматическое переключение. * [Цены](/docs/concepts/pricing) — формула расчёта стоимости платных моделей. --- # Модели (/docs/concepts/models) > Каталог моделей Hubris, формат GET /v1/models, как выбрать модель под задачу. import { EndpointBadge } from '@/components/docs/endpoint-badge'; import { ModelLink } from '@/components/docs/model-link'; Hubris предоставляет доступ к моделям ведущих провайдеров через единый API. Все модели отвечают в OpenAI-совместимом формате — код не нужно адаптировать под каждого провайдера. ## Каталог Полный список — на странице [/models](/models). Там же можно посмотреть актуальные цены в ₽, размер контекста и поддерживаемые модальности (текст, изображения, аудио). Программно список доступен через API: ```bash curl -H "Authorization: Bearer sk-gw-..." https://api.hubris.pw/v1/models ``` Ответ — стандартный OpenAI-список с расширениями Hubris: ```json { "object": "list", "data": [ { "id": "anthropic/claude-haiku-4.5", "object": "model", "created": 1714000000, "owned_by": "anthropic", "display_name": "Claude Haiku 4.5", "description": "Быстрая и дешёвая модель Anthropic для большинства задач.", "context_window": 200000, "modalities": ["text", "image"], "pricing": { "input_rub_per_million": 30.45, "output_rub_per_million": 152.25, "currency": "RUB" } } ] } ``` ## Формат идентификатора Все модели используют формат `provider/model`: * — Anthropic, модель Claude Haiku 4.5. * — OpenAI, модель GPT-4o mini. * — Google, модель Gemini 2.5 Flash. В каталоге доступны модели от Anthropic, OpenAI, Google и других ведущих провайдеров. Все обращения идут через единый эндпоинт `https://api.hubris.pw/v1/chat/completions` — без необходимости подключать SDK каждого провайдера. ## Как выбрать модель Краткие ориентиры: * **Для простых задач** (классификация, извлечение, короткие ответы) — самые дешёвые «mini» / «haiku» модели вроде или . Стоят копейки за запрос. * **Для сложных рассуждений и кода** — флагманские модели (Opus, GPT-4o, Pro). Дороже, но качество выше. * **Для большого контекста** (длинные документы, кодбазы) — модели с `context_window` от 200K. У ряда моделей Gemini есть варианты до 2M токенов. * **Для мультимодальных задач** (анализ изображений) — модели с `"image"` в `modalities`. В каталоге есть фильтры по этим параметрам. Если не уверены — начните с , она работает быстро и дёшево для большинства задач. ## Какие модели в каталоге В `data` попадают только активные модели с явно заданной фиксированной ценой. Модели с переменной/динамической ценой и устаревшие — фильтруются автоматически. Каталог обновляется примерно раз в сутки — новые модели появляются в течение дня после релиза провайдером. ## Что дальше * [Цены](/docs/concepts/pricing) — как считаются деньги за запрос. * [Быстрый старт](/docs/quickstart) — отправьте первый запрос к выбранной модели. * [/models](/models) — открыть каталог. --- # Цены (/docs/concepts/pricing) > Как считается стоимость запроса в рублях. Таблицы цен — в каталоге моделей. import { ModelLink } from '@/components/docs/model-link'; Hubris работает по модели **pay-as-you-go**: вы платите только за фактически использованные токены. Никаких подписок, минимальных платежей или скрытых комиссий. Цены — в ₽ за 1 миллион токенов. ## Что такое токен Токен — это единица обработки текста для модели. Грубо: одно английское слово — это примерно 1.3 токена; русское слово — примерно 2 токена (кириллица токенизируется агрессивнее). Точное число зависит от языка и токенизатора модели. Один абзац обычно — 50–100 токенов. При расчёте стоимости запроса различаются: * **prompt\_tokens** (входные) — то, что вы отправили в `messages`. * **completion\_tokens** (выходные) — то, что модель вернула в `choices[0].message.content`. Цены за вход и выход разные — выход обычно в 3–5 раз дороже. ## Формула расчёта ``` стоимость_запроса = prompt_tokens × цена_входа + completion_tokens × цена_выхода ``` Где цены — в ₽ за токен (то есть `input_rub_per_million / 1_000_000`). Округление — в большую сторону до копейки, чтобы Hubris не накапливал долг на дробных копейках. ## Цены в каталоге В каталоге моделей `/models` для каждой модели видно: * **Вход** — ₽ за 1М входных токенов. * **Выход** — ₽ за 1М выходных токенов. Программно те же значения возвращает `GET /v1/models` в поле `pricing`: ```json { "id": "anthropic/claude-haiku-4.5", "pricing": { "input_rub_per_million": 30.45, "output_rub_per_million": 152.25, "currency": "RUB" } } ``` ## Пример расчёта Запрос к с 1000 prompt-токенов и 500 completion-токенов при цене 30.45 ₽ / 152.25 ₽ за 1М: ``` стоимость = 1000 × (30.45 / 1_000_000) + 500 × (152.25 / 1_000_000) = 0.03045 + 0.0761 = 0.10655 ₽ ≈ 11 копеек (округление вверх) ``` ## Как узнать стоимость конкретного запроса В ответе любого запроса к `/v1/chat/completions` есть поле `usage`: ```json { "usage": { "prompt_tokens": 1000, "completion_tokens": 500, "total_tokens": 1500 } } ``` В разделе [/usage](/usage) дашборда отображается полный лог запросов с фактической стоимостью каждого. Баланс в реальном времени — на странице [/billing](/billing). ## Стоимость стриминга При `stream: true` цена считается так же — по `usage` из последнего chunk-а перед `[DONE]`. Hubris автоматически добавляет `stream_options: { include_usage: true }` к стрим-запросам, чтобы вы (и мы) знали окончательное число токенов. Если соединение разорвалось до окончания стрима — мы всё равно дочитываем ответ от провайдера и списываем деньги (защита от «бесплатных токенов» через abort). ## Что входит в счёт * Успешные запросы (`200 OK`). * Запросы со стримингом до получения `usage` (включая прерванные клиентом). ## Что НЕ входит в счёт * Запросы с ошибкой авторизации (`401`, `403`). * Запросы с невалидным телом (`400`). * Запросы к несуществующим моделям (`404`). * Ошибки апстрима (`502`, `503`, `504`) — модель не отработала, денег не списываем. * Запросы при недостаточном балансе (`402`) — отвергаются ДО запроса к модели. ## Что дальше * [Биллинг](/docs/concepts/billing) — пополнение баланса, минимальный остаток. * [Каталог моделей](/models) — посмотреть все цены. * [/usage](/usage) — лог ваших запросов с расходами. --- # Конфиденциальность (/docs/concepts/privacy) > Что Hubris хранит и не хранит о ваших запросах. Передача данных провайдерам моделей. Hubris работает по принципу **минимального хранения данных**. Содержимое ваших запросов (`messages`, `tools`, `input`) никогда не записывается на сервере и не используется для обучения моделей. ## Что мы НЕ храним * Тексты ваших сообщений (`messages[*].content`). * Системные промпты. * Описание инструментов (`tools[*].function.parameters`). * Содержимое ответов от моделей. * Изображения, аудио, файлы (когда поддержим мультимодальные эндпоинты). Эти данные проходят через наш сервер исключительно в момент проксирования запроса в провайдера и не сохраняются ни в логах, ни в БД. ## Что мы храним В таблице `usage_logs` для каждого запроса: * ID пользователя и API-ключа (для биллинга). * ID модели (для отчётности). * Число входных и выходных токенов (для биллинга). * Стоимость в копейках. * Латентность ответа. * Статус (успех / ошибка / тайм-аут). * Текст ошибки (если была), без содержимого запроса. Этого достаточно для биллинга, аналитики и поддержки. Содержимое разговоров мы не видим — даже наша команда не имеет к нему доступа. ## Передача данных провайдерам моделей Чтобы модель ответила на ваш запрос, его содержимое физически передаётся провайдеру модели (Anthropic, OpenAI, Google и т. д.). Передача происходит по защищённому соединению (TLS). Каждый провайдер имеет собственную политику обработки данных: * Большинство **не используют API-данные для обучения** по умолчанию. * Многие хранят логи 30 дней для безопасности и контроля злоупотреблений. * Часть провайдеров предоставляет режим Zero Data Retention за дополнительную плату или на enterprise-тарифе — на момент 2026-05 Hubris это не пробрасывает; следите за обновлениями. Полный перечень провайдеров и юридическая обвязка — на странице [Политика конфиденциальности](/legal/privacy), раздел 8. ## Логи на стороне Hubris Сервер Hubris пишет два класса логов: 1. **Метаданные запросов** (выше): хранятся в БД `usage_logs`, доступны вам в [/usage](/usage). 2. **Сервисные логи** (Pino → systemd journal): только метаданные — req\_id, userId, keyId, modelId, токены, статусы. Никакого содержимого. Хранятся 30 дней, доступ есть у нашей DevOps-команды для отладки инцидентов. ## Файлы и обработка изображений Когда мы добавим поддержку генерации изображений и обработки аудио — содержимое (промпты для генерации картинок, голосовые файлы) будет передаваться провайдерам по тем же правилам. В нашей БД эти файлы оставаться не будут. ## GDPR / 152-ФЗ Hubris соответствует требованиям 152-ФЗ для обработки персональных данных. Полная политика — на странице [/legal/privacy](/legal/privacy). Кратко: * Юр. лицо: ИП Романкова А. В. (ИНН и реквизиты — там же). * Персональные данные собираются только для биллинга (email, при необходимости — банковские реквизиты для возврата). * Согласие на обработку даётся через акцепт оферты при регистрации. * Удаление аккаунта по запросу на [support@hubris.pw](mailto:support@hubris.pw) — все данные стираются в течение 30 дней. ## Если у вас высокие требования Если ваши данные регулируются (медицинские, финансовые, гос. тайна) — напишите на [support@hubris.pw](mailto:support@hubris.pw). Мы обсудим: * Какие провайдеры подходят под ваше регулирование. * Возможен ли self-hosted режим (отдельный inference на вашем железе). * Дополнительные гарантии в DPA. ## Что дальше * [Политика конфиденциальности](/legal/privacy) — полные юридические условия. * [Условия использования](/legal/terms) — публичная оферта. * [Аутентификация](/docs/authentication) — про безопасное хранение ключей. --- # Rate limits (/docs/concepts/rate-limits) > Что ограничивает запросы в Hubris — баланс, апстрим, дневные лимиты на служебных ключах. Hubris **не применяет искусственных rate-limit-ов** на пользовательские API-ключи. Стандартный тариф даёт неограниченное число запросов в секунду — расход ограничен только балансом. ## Что реально ограничивает запросы ### 1. Баланс Каждый запрос списывает с баланса фактическую стоимость. При недостаточном балансе (\< 100 копеек) запрос отклоняется с `402 insufficient_balance` ДО обращения к модели. Подробнее — на странице [Биллинг](/docs/concepts/billing). ### 2. Лимиты провайдера Каждая модель имеет собственные ограничения от провайдера: requests-per-minute, tokens-per-minute, concurrent connections. При превышении провайдер возвращает 429, мы транслируем это как `502 upstream_error`. Что делать: добавить retry с экспоненциальным backoff. Hubris агрегирует трафик многих клиентов через свой сервисный ключ — вероятность упереться в лимит провайдера ниже, чем при работе напрямую. ### 3. Дневной лимит на служебных ключах Опционально — при создании ключа можно задать `daily_limit_kopecks`. Например, 5000 копеек = 50 ₽ в сутки. Превышение возвращает **429 daily\_limit\_exceeded**: ```json { "error": { "message": "Daily spending limit exceeded for this API key", "type": "rate_limit_error", "code": "daily_limit_exceeded" } } ``` Это используется для: * **CI-runner-ов**, чтобы утечка ключа не разорила баланс. * **Внутренних скриптов** с ограниченным бюджетом. * **Sandbox-аккаунтов** для разработчиков. Окно — скользящее (rolling) 24 часа, а не календарный день. Старые расходы выпадают из счётчика по мере того как проходят сутки с момента списания, поэтому после превышения лимит постепенно «отпускает». Расходы за последние 24ч кешируются в Redis с TTL 60 секунд, то есть UI и middleware могут отставать от точного значения на минуту. Задать или поменять лимит — на странице [API-ключи](/keys): поле «Дневной лимит, ₽» в форме создания и кнопка «изменить» рядом с каждым активным ключом. Очистите поле, чтобы снять лимит. Генерация изображений (`modalities: ["image"]` / `["image","text"]`) учитывается в счётчике 24-часового расхода ключа на общих основаниях: одна сгенерированная картинка добавляется к расходу по итоговой стоимости (см. [Генерация изображений](/docs/features/image-generation)). ## Стратегия retry Если получили 429 от Hubris (`daily_limit_exceeded`) или 502 (от провайдера): ```python import time def with_retry(fn, max_attempts=3): delay = 1 for attempt in range(max_attempts): try: return fn() except APIError as e: if e.code in ("upstream_error", "upstream_timeout"): if attempt < max_attempts - 1: time.sleep(delay) delay *= 2 continue raise ``` Не делайте retry на: * `invalid_api_key` (401) — ключ не починится сам. * `insufficient_balance` (402) — пополните баланс сначала. * `model_not_found` (404) — название не изменится. * `invalid_request` (400) — баг в коде. ## Concurrent requests Параллельные запросы на одном ключе — без ограничений. Если ваш сервис делает 100 запросов одновременно, они все пойдут к моделям параллельно. Узкое горлышко — провайдер модели, не Hubris. ## Что дальше * [Ошибки](/docs/concepts/errors) — все коды и стратегия retry. * [Биллинг](/docs/concepts/billing) — про баланс. * [Аутентификация](/docs/authentication) — про создание ключей. --- # Обработка ошибок (/docs/features/error-handling) > Стратегия retry, backoff, комбинация с model fallbacks и что показывать конечному пользователю. Гид про практическую работу с ошибками Hubris API — где имеет смысл ретраить, где не имеет, как комбинировать с [Model Fallbacks](/docs/features/model-fallbacks), и как не показать сырое сообщение API конечному пользователю. Полная таблица кодов и форматов ответа — на странице [Ошибки](/docs/concepts/errors). ## Стратегия по кодам | HTTP | code | Ретраить? | Почему | | ---- | --------------------------- | ---------------------- | ---------------------------------------------------------------------------------------------------------------------- | | 400 | `invalid_request` | ❌ нет | Это баг в коде. Ретрай → та же ошибка. Логируйте и чините. | | 401 | `invalid_api_key` | ❌ нет | Ключ невалиден. Ротация на лету не предусмотрена. | | 402 | `insufficient_balance` | ❌ нет (но можно ждать) | Кончился баланс. Если есть авто-пополнение — после крупного топ-апа ретрай поможет. Иначе — алёрт. | | 404 | `model_not_found` | ❌ нет | Модель архивирована или ID опечатан. Лучше fallback на актуальную ([model-selection](/docs/features/model-selection)). | | 429 | `daily_limit_exceeded` | ❌ не имеет смысла | Лимит ключа исчерпан. До конца окна — отказ. Используйте другой ключ или ждите. | | 502 | `upstream_error` | ✅ да | Апстрим вернул 5xx или контент-фильтр. Транзиентно. | | 503 | `exchange_rate_unavailable` | ✅ да | Курс ЦБ временно не доступен — каталог не отдаёт цены. Обычно минуту. | | 504 | `upstream_timeout` | ✅ да | Модель перегружена. Часто помогает другая модель из каталога. | Главное правило: **ретраить транзиентные сбои (5xx) с backoff, остальное — нет**. ## Где ретрай встроен на стороне Hubris Hubris уже автоматически переключается на запасную модель, если вы передали массив `models`: ```bash curl -s https://api.hubris.pw/v1/chat/completions \ -d '{ "model": "anthropic/claude-haiku-4.5", "models": [ "anthropic/claude-haiku-4.5", "openai/gpt-4o-mini" ], "messages": [{"role": "user", "content": "..."}] }' ``` Если основная модель ответила 429 или 5xx — клиент видит успешный ответ от запасной. Подробнее: [Model Fallbacks](/docs/features/model-fallbacks). **Это снимает большую часть транзиентных ошибок без вашего ретрая.** Перед тем как писать сложную ретрай-логику в клиенте — попробуйте `models: [...]`. На практике она закрывает 80% сценариев. ## Когда нужен клиентский ретрай поверх Кейсы, где Hubris-fallback не спасает: * **Нет резервной модели в массиве `models` или нужна та же самая модель.** Если основная модель уникальна (например, специфичный image-gen ID, который никто не дублирует) — клиентский ретрай. * **503 `exchange_rate_unavailable`.** Это не ошибка модели — это про каталог. Fallback моделей не сработает. Ретрай через минуту. * **Сетевые таймауты до Hubris.** До нас даже не дошёл запрос — fallback не запустится. Клиентский ретрай. ## Python: exponential backoff ```python import time import openai from openai import OpenAI client = OpenAI( base_url="https://api.hubris.pw/v1", api_key="sk-gw-...", ) def call_with_retry(messages, max_attempts=3): for attempt in range(max_attempts): try: return client.chat.completions.create( model="anthropic/claude-haiku-4.5", models=[ "anthropic/claude-haiku-4.5", "openai/gpt-4o-mini", ], messages=messages, ) except openai.APIStatusError as e: # Ретраим только транзиентные коды if e.status_code in (502, 503, 504): if attempt == max_attempts - 1: raise # Exponential backoff с jitter wait = (2 ** attempt) + (0.5 * attempt) time.sleep(wait) continue # 4xx — баг или конфигурация, ретрай не поможет raise resp = call_with_retry([{"role": "user", "content": "Hello"}]) ``` Использование `models: [...]` параллельно с retry — это нормально: они работают на разных уровнях (модель vs сеть/каталог). Двойного списания не будет — биллинг по фактически ответившему запросу. ### Готовая библиотека Если не хотите писать backoff руками — есть [`tenacity`](https://tenacity.readthedocs.io/): ```python from tenacity import retry, stop_after_attempt, wait_exponential, retry_if_exception_type import openai @retry( retry=retry_if_exception_type((openai.APITimeoutError, openai.APIConnectionError)), stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=1, max=10), ) def call_llm(messages): return client.chat.completions.create( model="anthropic/claude-haiku-4.5", messages=messages, ) ``` ## TypeScript: фабрика с retry ```ts import OpenAI from 'openai'; const client = new OpenAI({ baseURL: 'https://api.hubris.pw/v1', apiKey: 'sk-gw-...', }); async function callWithRetry( messages: Array<{ role: string; content: string }>, maxAttempts = 3, ) { for (let attempt = 0; attempt < maxAttempts; attempt++) { try { return await client.chat.completions.create({ model: 'anthropic/claude-haiku-4.5', models: [ 'anthropic/claude-haiku-4.5', 'openai/gpt-4o-mini', ], messages, } as any); } catch (e: any) { const transient = [502, 503, 504].includes(e?.status); const lastAttempt = attempt === maxAttempts - 1; if (!transient || lastAttempt) throw e; const wait = (2 ** attempt) * 1000 + Math.random() * 500; await new Promise((r) => setTimeout(r, wait)); } } throw new Error('unreachable'); } ``` ## Что показывать пользователю Сырые сообщения API не подходят для production UI: | Внутреннее | Внешнее (пользователю) | | -------------------------- | --------------------------------------------------------------------------------------------------------------- | | `401 invalid_api_key` | «Внутренняя ошибка авторизации» (никогда не показывайте «неверный ключ» — это намекает, что у вас есть рабочий) | | `402 insufficient_balance` | «Сервис временно недоступен» — это **ваша** проблема, не пользователя | | `404 model_not_found` | «Сервис обновляет каталог» — подставьте альтернативную модель | | `429 daily_limit_exceeded` | «Лимит запросов на сегодня исчерпан, попробуйте завтра» | | `502 / 503 / 504` | «Модель временно недоступна, повторим попытку» (если уже идёт retry) | Никогда не показывайте `error.message` модели напрямую — он на английском и может содержать технические детали (имена провайдеров, ID моделей). ## Как НЕ надо ```python # Плохо: ретрай 401 / 402 / 404 for i in range(5): try: return client.chat.completions.create(...) except Exception: time.sleep(2 ** i) ``` Слепой ретрай на любую ошибку: * На `401` (неверный ключ) — крутит цикл с экспоненциальной задержкой, прежде чем сдаться. UI замораживается. * На `402` (нет денег) — то же самое, плюс ваш сервис атакует наш API без шансов на успех. * На `400` (баг в коде) — никогда не починится сама. ```python # Плохо: показ сырого error.message except openai.APIError as e: return jsonify({"error": str(e)}), 500 ``` Сырая ошибка попадёт в браузер пользователя. Безопаснее: ```python except openai.APIError as e: log.error("LLM error", extra={"status": getattr(e, "status_code", None)}) return jsonify({"error": "Сервис временно недоступен"}), 503 ``` ## Стриминг и ошибки При `stream: true` ошибка ДО первого чанка возвращается как обычный JSON `{ error: {...} }` с соответствующим HTTP-кодом. После начала стрима — клиент видит обрыв соединения или преждевременный `finish_reason`. Подробности — на [странице стриминга](/docs/features/streaming) и в [концепции ошибок](/docs/concepts/errors#стриминг). ## Что важно знать * **Hubris не списывает деньги за неуспешные запросы.** 4xx и 5xx — без биллинга. Streaming-обрыв после первого чанка — частичное списание по тому, что успело сгенерироваться. * **Не ретраите structured-output ответы внутри одного контекста.** Если модель упала на структуре — лучше переключиться на другую модель (`models: [...]`), а не повторять с той же. * **Лимиты по таймауту** — ваш HTTP-клиент должен иметь reasonable timeout (90–120 сек), иначе соединение зависнет на perpetually-slow модели. ## Что дальше * [Ошибки](/docs/concepts/errors) — полная таблица кодов с примерами JSON-ответов. * [Model Fallbacks](/docs/features/model-fallbacks) — встроенный fallback на запасную модель. * [Стриминг ответов](/docs/features/streaming) — что происходит с ошибками во время стрима. * [Управление ключами](/keys) — дневные лимиты на стороне сервера. --- # Генерация изображений (/docs/features/image-generation) > Генерация изображений через /v1/chat/completions. Передайте `modalities: ["image","text"]` и модель с поддержкой image-output. Hubris умеет генерировать изображения через тот же эндпоинт `/v1/chat/completions`, что и для текста — нужно лишь передать `modalities` и выбрать модель с поддержкой image-output. В каталоге сейчас 29 image-моделей: Google Gemini \*-image, OpenAI GPT-5 Image, Black Forest Labs Flux 2 (pro/max/flex/klein), Recraft v3-v4.1 (11 вариантов с поддержкой текста на изображении, vector и utility-режимов), Sourceful Riverflow v2, xAI Grok Imagine, ByteDance Seedream 4.5. Полный актуальный список — фильтр «Output: image» в [каталоге](/models). ## Минимальный пример ```bash curl -s https://api.hubris.pw/v1/chat/completions \ -H "Authorization: Bearer sk-gw-..." \ -H "Content-Type: application/json" \ -d '{ "model": "google/gemini-2.5-flash-image", "modalities": ["image", "text"], "messages": [{"role": "user", "content": "Закат над горами в стиле Гибли"}] }' ``` Картинка возвращается как base64 data URL в `choices[0].message.images[0].image_url.url`: ```json { "id": "chatcmpl-...", "model": "google/gemini-2.5-flash-image", "choices": [ { "message": { "role": "assistant", "content": "Готово!", "images": [ { "type": "image_url", "image_url": { "url": "data:image/png;base64,iVBORw0KGgo..." } } ] } } ], "usage": { "prompt_tokens": 18, "completion_tokens": 0, "total_tokens": 18 } } ``` ## `modalities` * `["image", "text"]` — модели с гибридным выходом (Google Gemini image, OpenAI GPT-5 Image): отдают и текст-комментарий, и картинку. * `["image"]` — image-only модели (Flux 2, Recraft, Sourceful, SDXL-семейство): только картинка. Если передадите `modalities` для не-image модели — вернётся ошибка от провайдера. ## OpenAI SDK В официальных SDK OpenAI (Python `openai`, TypeScript `openai`) поле `modalities` не объявлено в Chat Completions. Передавайте через `extra_body` / приведением типов. **Python:** ```python from openai import OpenAI client = OpenAI( base_url="https://api.hubris.pw/v1", api_key="sk-gw-...", ) resp = client.chat.completions.create( model="google/gemini-2.5-flash-image", messages=[{"role": "user", "content": "Закат над горами"}], extra_body={"modalities": ["image", "text"]}, ) # images приходит как extra поле — доступ через model_extra images = resp.choices[0].message.model_extra.get("images", []) for img in images: data_url = img["image_url"]["url"] # data_url начинается с "data:image/png;base64,..." — декодировать и сохранить ``` **TypeScript:** ```ts import OpenAI from 'openai'; const client = new OpenAI({ baseURL: 'https://api.hubris.pw/v1', apiKey: 'sk-gw-...', }); const resp = await client.chat.completions.create({ model: 'google/gemini-2.5-flash-image', messages: [{ role: 'user', content: 'Закат над горами' }], modalities: ['image', 'text'], } as any); const images = (resp.choices[0].message as any).images ?? []; for (const img of images) { const dataUrl = img.image_url.url; // ... } ``` ## `image_config` Базовые поля (поддерживаются большинством моделей): * `aspect_ratio` — `"1:1"` (default), `"16:9"`, `"9:16"`, `"4:3"`, `"3:4"`, `"21:9"` и др. * `image_size` — `"1K"` (default), `"2K"`, `"4K"`, `"0.5K"`. ### Recraft (v3 / v4 / v4.1) * `style` — стилевая пресета. Полный список — на карточке модели в [каталоге](/models). * `text_layout` — массив объектов `{ text, bbox: [[x,y], ...] }` для размещения текста на картинке. Только Recraft V3. * `rgb_colors` — массив `[r, g, b]` для палитры (Recraft v3+). * `background_rgb_color` — `[r, g, b]` для фона. * `strength` — для image-to-image (0..1, default 0.2). Меньше — ближе к оригиналу, больше — креативнее. ### Sourceful Riverflow * `font_inputs` — массив `{ font_url, text }` для кастомных шрифтов. Максимум 2. * `super_resolution_references` — массив URL-референсов для image-to-image enhance. Максимум 4. Только при image-to-image. ### Google Gemini \*-image * Расширенный набор `aspect_ratio` для `gemini-3.1-flash-image-preview`: `"1:4"`, `"4:1"`, `"1:8"`, `"8:1"` (для вертикальных и панорамных layouts). * `image_size: "0.5K"` — оптимизированный по скорости вариант (только эта модель). ### Black Forest Labs Flux 2 * Стандартный набор `aspect_ratio` + `image_size`. Без model-specific параметров. ```bash curl -s https://api.hubris.pw/v1/chat/completions \ -H "Authorization: Bearer sk-gw-..." \ -d '{ "model": "google/gemini-3-pro-image-preview", "modalities": ["image", "text"], "messages": [{"role": "user", "content": "Морской пейзаж"}], "image_config": { "aspect_ratio": "16:9", "image_size": "2K" } }' ``` ## Какую модель когда выбирать * **Текст на изображении** (баннер, постер с надписями) → Recraft V3 (`text_layout`). * **Брендовые шрифты** → Sourceful Riverflow (`font_inputs`). * **Photorealistic / frontier quality** → Flux 2 Pro / Max. * **Быстро + дёшево** → Gemini 2.5 Flash Image, Flux 2 Klein 4B. * **Гибрид «текст + картинка»** в одном ответе → Gemini \*-image, GPT-5 Image. ## Image-to-image и редактирование Некоторые модели (Recraft, Sourceful, Flux 2 Pro) поддерживают входное изображение через content-blocks в `messages`. Формат пока разный между провайдерами, единого OpenAI-shape ещё нет — смотрите карточку конкретной модели. Hubris прозрачно пропускает содержимое `messages` к провайдеру. ## Стриминг `stream: true` работает. Картинка приходит в `delta.images[]` одним или несколькими чанками. Финальный чанк содержит `usage` (как в обычном стриме). ```bash curl -N https://api.hubris.pw/v1/chat/completions \ -H "Authorization: Bearer sk-gw-..." \ -d '{ "model": "google/gemini-2.5-flash-image", "modalities": ["image", "text"], "messages": [{"role": "user", "content": "Космический корабль"}], "stream": true }' ``` ## Биллинг Image generation тарифицируется **за изображение** (не за токены). В каталоге у каждой image-модели видна цена в рублях за одну картинку — это итоговая цифра, которую Hubris спишет с баланса. Если модель умеет возвращать несколько изображений за запрос — спишется n × цена. Дополнительные параметры некоторых моделей могут увеличивать стоимость запроса — итоговая сумма списания в рублях видна в разделе [«Расходы»](/usage). Списание происходит после успешного ответа. Транзакция атомарна — баланс, usage\_log и движение по балансу пишутся одной БД-транзакцией. ## Лимиты * **Баланс** — стандартный. Если баланс ниже минимального порога (100 копеек по умолчанию), запрос блокируется с `402 insufficient_balance` (см. [Лимиты и баланс](/docs/concepts/rate-limits)). * **Дневной лимит ключа** — действует. Image-запросы добавляются в счётчик 24-часового расхода ключа на общих основаниях. Превышение — `429 daily_limit_exceeded`. ## Чего пока нет в MVP * Отдельный image-generations endpoint OpenAI-shape — пока используем `/v1/chat/completions` с `modalities`. * Edits (изменение существующего изображения по маске). * Variations (создание вариаций существующего изображения). * Загрузка картинки из URL/файла как input (image-to-image поддерживается некоторыми моделями через content blocks в `messages`, но единого формата ещё нет). Если что-то из этого критично — напишите в [support@hubris.pw](mailto:support@hubris.pw), расскажите use case. --- # Model Fallbacks (/docs/features/model-fallbacks) > Запасные модели на случай 429/5xx/контент-фильтра. Передайте массив `models` — Hubris сам переключится при сбое. Если основная модель временно недоступна — упёрлась в rate limit провайдера, упала с 5xx или ответ зарезали контент-фильтром — Hubris автоматически попробует следующие модели из массива `models`. Биллинг по фактически использованной модели (она возвращается в поле `model` ответа). Поддерживается только в [`POST /v1/chat/completions`](/docs/api/chat-completions). На `/v1/responses` пока нет. ## Как работает Передайте массив `models` рядом с обычным `model`. Первый элемент — основной; остальные пробуются по порядку при ошибке: ```bash curl -s https://api.hubris.pw/v1/chat/completions \ -H "Authorization: Bearer sk-gw-..." \ -H "Content-Type: application/json" \ -d '{ "model": "anthropic/claude-opus-4.7", "models": [ "anthropic/claude-opus-4.7", "anthropic/claude-sonnet-4.6", "openai/gpt-4o-mini" ], "messages": [{"role": "user", "content": "Привет"}] }' ``` Если Opus 4.7 вернёт 429 от провайдера — сразу пойдёт Sonnet 4.6; если и тот недоступен — GPT-4o mini. Клиент получит обычный успешный ответ, как если бы fallback не было. ## Когда срабатывает fallback * **429** — rate limit провайдера. * **5xx** — апстрим упал. * **Контент-фильтр** — провайдер заблокировал ответ модерацией (вы получите fallback-результат, а не блок). * **Network timeout** между Hubris и провайдером. ## Биллинг Списывается стоимость **только той модели, которая реально ответила**. Поле `model` в JSON-ответе — это победитель (не первый из массива). На странице [/usage](/usage) запрос будет залогирован под этой моделью. Если в `models` несколько моделей разной цены и срабатывает fallback на самую дорогую — да, спишется по её тарифу. Поэтому имеет смысл располагать модели по возрастанию цены или ставить в конец «дешёвый, но всегда живой» вариант: ```json { "model": "anthropic/claude-opus-4.7", "models": [ "anthropic/claude-opus-4.7", "anthropic/claude-sonnet-4.6", "anthropic/claude-haiku-4.5" ], "messages": [{"role": "user", "content": "..."}] } ``` ## Стриминг `stream: true` работает с fallback так же. Если основная модель упала ДО первого SSE-чанка, Hubris молча переключится на следующую, и клиент получит чанки уже от неё. Поле `model` в каждом чанке — это уже final-модель. После начала успешного стрима fallback больше не сработает (это технически невозможно: байты пошли клиенту). **Edge-case:** если клиент дисконнектнется до того, как пришёл первый чанк с полем `model`, биллинг спишется по тарифу основной модели — мы просто не успеем узнать, какая ответила. Это безопасно (over-bill вместо undelivered), но может слегка завысить стоимость в редких сетевых сбоях. ## Ограничения * **Максимум 10 моделей** в массиве (включая основную). * **`/v1/responses`** — не поддерживается (используйте `/v1/chat/completions`). * **`/v1/embeddings`** — не поддерживается (embedding-модели обычно стабильны и не требуют fallback). * **Структурные ошибки запроса** (400 invalid\_request) не триггерят fallback — это значит ваш запрос неверен, и пробовать другую модель бессмысленно. ## Что дальше * [POST /v1/chat/completions](/docs/api/chat-completions) — полный список параметров. * [Ошибки](/docs/concepts/errors) — какие коды и в каких случаях возвращаются. --- # Выбор модели программно (/docs/features/model-selection) > Как фильтровать каталог через GET /v1/models, подбирать модель под задачу и обрабатывать переименования. В Hubris доступны десятки моделей с разными возможностями и ценами. Чтобы не хардкодить ID, а подбирать модель программно — есть `GET /v1/models`. Этот гайд про то, как фильтровать каталог под конкретные задачи и обрабатывать случаи, когда «нужной» модели не оказалось. ## Что возвращает `/v1/models` ```bash curl -s https://api.hubris.pw/v1/models \ -H "Authorization: Bearer sk-gw-..." ``` Ответ — OpenAI-совместимый список: ```json { "object": "list", "data": [ { "id": "anthropic/claude-haiku-4.5", "object": "model", "created": 1714000000, "owned_by": "anthropic", "display_name": "Claude Haiku 4.5", "description": "Быстрая и недорогая модель Anthropic...", "context_window": 200000, "modalities": ["text"], "input_modalities": ["text", "image"], "output_modalities": ["text"], "pricing": { "input_rub_per_million": 88, "output_rub_per_million": 440, "currency": "RUB" } } // ... ] } ``` Поля каждой модели: | Поле | Что | | -------------------------------- | ---------------------------------------------------------------------- | | `id` | идентификатор для использования в `model` запроса | | `owned_by` | провайдер модели — `anthropic`, `openai`, `google`, `deepseek`, и т.д. | | `display_name` | человекочитаемое имя (для UI каталога) | | `description` | короткое описание возможностей | | `context_window` | размер контекста в токенах | | `input_modalities` | что принимает на вход: `text`, `image` | | `output_modalities` | что выдаёт: `text`, `image` | | `pricing.input_rub_per_million` | цена в рублях за 1М prompt-токенов | | `pricing.output_rub_per_million` | цена в рублях за 1М completion-токенов | В ответ попадают только активные модели — снятые с публикации не приходят. ## Сценарии фильтрации ### «Хочу самую дешёвую модель с поддержкой изображений на вход» ```python import httpx resp = httpx.get( "https://api.hubris.pw/v1/models", headers={"Authorization": "Bearer sk-gw-..."}, ).json() vision_models = [ m for m in resp["data"] if "image" in m["input_modalities"] ] cheapest = min( vision_models, key=lambda m: m["pricing"]["input_rub_per_million"], ) print(cheapest["id"], cheapest["pricing"]) ``` ### «Нужен большой контекст — от 500k токенов» ```python big_context = [m for m in resp["data"] if m["context_window"] >= 500_000] ``` ### «Только модели определённого провайдера» ```python anthropic_only = [m for m in resp["data"] if m["owned_by"] == "anthropic"] ``` ### «Сортирую по цене prompt-токенов» ```python by_input_price = sorted( resp["data"], key=lambda m: m["pricing"]["input_rub_per_million"], ) ``` ## TypeScript ```ts type Model = { id: string; owned_by: string; context_window: number; input_modalities: string[]; output_modalities: string[]; pricing: { input_rub_per_million: number; output_rub_per_million: number; currency: 'RUB'; }; }; const resp = await fetch('https://api.hubris.pw/v1/models', { headers: { Authorization: 'Bearer sk-gw-...' }, }).then((r) => r.json() as Promise<{ data: Model[] }>); const visionModels = resp.data.filter((m) => m.input_modalities.includes('image'), ); ``` ## Стабильность ID ID модели — стабильный идентификатор. Мы не меняем его молча. При больших изменениях: * **Новая мажорная версия** — публикуется как **новый ID**. Например, `anthropic/claude-haiku-4.5` отдельная сущность от `anthropic/claude-haiku-5.0`. Старая модель может ещё работать или быть архивирована (см. ниже). * **Архивация** — модель снимается с активного каталога. Запросы с её ID возвращают `404 model_not_found`. Архивированные модели **не возвращаются** в `GET /v1/models`. * **Мелкие обновления провайдера** (контекст ↑, скорость ↑, цена ↓) — id остаётся прежним, поля карточки обновляются. ## Что делать с `404 model_not_found` Если в коде захардкожен ID и модель уехала из каталога: 1. Поймайте `404` с `error.code: "model_not_found"`. 2. Либо используйте [model fallbacks](/docs/features/model-fallbacks): передайте `models: ["primary-id", "backup-id"]` — Hubris сам переключится. 3. Либо подберите замену через `GET /v1/models` и обновите ID у себя. ```python configured_model_id = "anthropic/claude-haiku-4.5" # ID, который вы используете try: resp = client.chat.completions.create(model=configured_model_id, messages=[...]) except openai.NotFoundError: # модель архивирована — fallback на актуальный каталог models = httpx.get("https://api.hubris.pw/v1/models", headers={...}).json() new_model = pick_similar(models["data"], owned_by="anthropic") resp = client.chat.completions.create(model=new_model["id"], messages=[...]) ``` Самое надёжное решение в продакшене — `models: [...]` параметр запроса. Тогда выбор fallback'а делается атомарно внутри запроса, без отдельного round-trip к каталогу. ## Версионированные slug'и Некоторые провайдеры в ответе возвращают модель с дат-версионированным slug'ом — например, запрос ушёл на `anthropic/claude-haiku-4.5`, а в `response.model` приходит `anthropic/claude-haiku-4.5-20251101`. Это нормально: мы биллим по канонической модели (`anthropic/claude-haiku-4.5`), а слаг просто говорит, какая именно дата-версия ответила. В Hubris ответ на запрос всегда содержит реально использованный ID в поле `model` — удобно для логов и аналитики, чтобы видеть, какая именно версия модели работала на ваших запросах. ## Что важно знать * **Не кешируйте каталог дольше нескольких часов.** Цены пересчитываются при обновлении курса ЦБ. Раз в час — нормальная частота обновления. * **Caps по провайдеру меняются.** Иногда у Anthropic / OpenAI заканчиваются мощности — модель временно отвечает 502/503. На уровне каталога модель остаётся, но провайдер недоступен. [Model fallbacks](/docs/features/model-fallbacks) спасают. * **Цены `0` — это [бесплатные модели](/docs/concepts/free-models).** У них свои лимиты на стороне провайдера, не публикуемые. ## Что дальше * [GET /v1/models](/docs/api/models) — полная схема ответа. * [Model fallbacks](/docs/features/model-fallbacks) — `models: [...]` для авто-замены. * [Каталог моделей](/models) — то же самое в UI. * [Ошибки](/docs/concepts/errors) — обработка `404 model_not_found`. --- # Privacy Mode (/docs/features/privacy) > Автоматическое маскирование персональных данных перед отправкой к провайдеру. ## Зачем это нужно Когда вы отправляете запрос к зарубежной модели, текст уходит за пределы РФ. По 152-ФЗ это требует обоснования трансграничной передачи персональных данных или согласия субъекта. **Privacy Mode** автоматически удаляет ПД из запроса, а в ответе восстанавливает их обратно — модель работает с обезличенным текстом, вы получаете осмысленный ответ. Фича **бесплатна** для всех моделей каталога. ## Что маскируется | Тип | Примеры | | ---------------- | ------------------------------------------------ | | ФИО | Иван Петрович Сидоров | | Email | [user@example.com](mailto:user@example.com) | | Телефоны | +7 903 158-22-00, 8(495)555-12-34 | | Банковские карты | 4276 1600 1234 5678 | | IBAN / Счета | 40817810099910004312 | | IP-адреса | 192.168.1.1, 2001:db8::1 | | Адреса | Москва, Тверская 13 | | Даты / Время | 12.04.1985, 2024-08-15T10:30 | | URL | [https://example.com/me](https://example.com/me) | | Национальность | русский, татарка | | Паспорта РФ | 4506 № 123456 | | СНИЛС | 123-456-789 01 | | ИНН | 7707083893, 770708389312 | | ОГРН | 1027700132195 | | Мед. лицензии | LO-77-01-009999 | ## Как настроить Откройте раздел **«Безопасность»** в личном кабинете (`/security`): 1. Включите тумблер **«PII-маскирование»**. 2. В блоке **«Скрываемые сущности»** выберите типы, которые хотите маскировать. 3. Настройте три опции: * **Восстанавливать в ответе** — модель возвращает ответ с подставленными оригиналами вместо placeholders. По умолчанию включено. * **Маскировать system prompt** — применять маскирование к сообщениям с `role: 'system'`. По умолчанию выключено. * **Блокировать при ошибке** — если сервис маскирования временно недоступен или восстановление данных в ответе не удалось — вернуть ошибку 503/502 вместо тихого пропуска без маски. По умолчанию выключено. 4. Нажмите **«Сохранить политику»**. ## Переопределение для одного ключа На странице **«Ключи»** (`/keys`) кликните на чип Privacy у нужного ключа и выберите один из четырёх режимов: * **По политике организации** — следует основной политике (значение по умолчанию). * **Принудительно выключить** — даже если организация включила. * **Принудительно включить** — даже если организация выключила. * **Обязательно** — включено + блокировать при ошибке. ## Переопределение в одном запросе Передайте заголовок `X-Hubris-Privacy-Mask` или поле `privacy_mask` в теле запроса: ```bash curl https://api.hubris.pw/v1/chat/completions \ -H "Authorization: Bearer sk-gw-..." \ -H "X-Hubris-Privacy-Mask: on" \ -d '{"model": "openai/gpt-4o", "messages": [{"role":"user","content":"Иван +79031582200"}]}' ``` Возможные значения: `on`, `off`, `required`. Приоритет (сильнее → слабее): header > body > per-key > политика организации. ## Пример: было → стало **Запрос пользователя:** > «Иван Петрович, тел +79031582200, запиши на 14:00» **Уходит к провайдеру:** > «\[PERSON\_1], тел \[PHONE\_1], запиши на \[DATE\_TIME\_1]» **Ответ провайдера:** > «Записал \[PERSON\_1] на \[DATE\_TIME\_1], позвоню за час по \[PHONE\_1]» **Вы получаете:** > «Записал Ивана Петровича на 14:00, позвоню за час по +79031582200» ## Стриминг В режиме `stream: true` Hubris разбирает SSE-чанки на лету. Когда в потоке появляется placeholder вида `[TYPE_N]`, его текст буферизуется (не более 50 символов или 200 мс) и при появлении закрывающей скобки заменяется на оригинал. Если placeholder не нашёлся в карте подстановок — буфер выдаётся клиенту как есть, а счётчик `X-Hubris-Restore-Mismatch` увеличивается на 1. ## Заголовки ответа | Заголовок | Значение | | --------------------------------- | ----------------------------------------------------- | | `X-Hubris-Privacy-Mode` | `on`, `required`, или `failed` | | `X-Hubris-Masked-Count` | сколько сущностей скрыто | | `X-Hubris-Masked-Types` | пример: `person:1,phone:1,email:2` | | `X-Hubris-Mask-Latency-Ms` | задержка анализа в миллисекундах | | `X-Hubris-Privacy-Policy-Version` | версия применённой политики | | `X-Hubris-Restore-Mismatch` | сколько placeholders в ответе не удалось восстановить | | `X-Hubris-Restore-Skipped` | `1`, если в политике выключено восстановление | ## Что НЕ маскируется * Изображения в vision-запросах (OCR появится в будущем). * Содержимое аудио-запросов (когда появится audio-транскрипция). * Сообщения с `role: 'system'`, если в политике отключено «Маскировать system prompt». * Произвольная обфускация (например, `п@@@ел Иван`, «4-5-0-6 123456») — это сделано намеренно, чтобы не ломать релевантные пользовательские маркеры. * Очень нестандартные ФИО без контекстных слов — есть редкие промахи NER. ## Защитные ограничения * **Размер запроса**: при `privacy_mask ≠ off` суммарный prompt не может превышать 30 000 токенов — иначе HTTP 413. Это защита от перегрузки сервиса маскирования; для длинных юридических документов значение может быть увеличено в будущих версиях. * **Лимит запросов**: 100 privacy-запросов в минуту на один API-ключ. Превышение — HTTP 429. Лимит мягкий, рассчитан на обычное использование. ## Лучше всего работает на коротких сообщениях Маскирование рассчитано на одиночные запросы или короткие диалоги (1–3 хода). На длинных multi-turn-сессиях возможен такой эффект: * В каждом запросе анализатор маскирует **всю историю** (включая прошлые ответы ассистента), присваивая токены вида `[PERSON_1]`, `[LOCATION_5]` и т. д. * К пятому-десятому ходу контекст модели состоит на 30–70% из таких токенов. * Модель учит этот паттерн в контексте и начинает **сама генерировать новые токены** (`[LOCATION_29]`, `[PERSON_47]`) для придуманных ею сущностей, которых не было в исходном запросе. * В нашем `anonymizer map` этих токенов нет — мы не можем подставить «оригинал», потому что оригинала и не существует. Такие токены остаются в ответе как есть. Признаки утечки токенов: * Видимые в ответе строки вида `[ТИП_число]` (например, `[LOCATION_29]`). * В `usage_logs.mask_metadata.restore_mismatch_count` будет ненулевое значение. * В ответе есть response-заголовок `X-Hubris-Restore-Mismatch: N`. Что делать: * Для PII-чувствительных задач используйте **одиночные запросы** без длинной истории. * Если важна жёсткая гарантия, включите **«Блокировать при ошибке»** в политике. Тогда при `restore_mismatch_count > 0` запрос вернёт HTTP 502 `privacy_mask_restore_failed`, и можно повторить запрос без истории. * Альтернатива — детектировать утечку клиентом: regex `/\[[A-Z_]+_\d+\]/` по ответу, и при срабатывании очищать историю / начинать новую сессию. ## Технические детали Под капотом — **Microsoft Presidio** (open-source PII detection engine от Microsoft) с моделью **GLiNER multi-PII** (мультиязычная NER, ONNX-оптимизирована), spaCy `ru_core_news_sm` для русской токенизации и собственные распознаватели СНИЛС, ИНН, паспортов и ОГРН с проверкой контрольной суммы. Сервис маскирования живёт на том же сервере, что и API, и общается через localhost — данные не покидают периметр Hubris для целей анализа. ## Условия использования Фича бесплатна. При значительном изменении нагрузки мы можем уточнить лимиты — с уведомлением за 30 дней. --- # Кеширование промптов (Anthropic) (/docs/features/prompt-caching) > Кеширование длинных промптов для удешевления повторных вызовов с тем же контекстом. Anthropic-модели (Claude) поддерживают кеширование частей промпта на стороне провайдера. Это даёт значительную экономию, когда вы много раз отправляете один и тот же длинный системный промпт или контекст с разными user-сообщениями. ## Когда это работает * **Длинный системный промпт** (≥1024 токена) — описание роли ассистента, гайдлайны, документация. * **Большой контекст в начале** — RAG-сниппеты, долгая история диалога. * **Повторные вызовы в течение 5 минут** — Anthropic кешит на \~5-минутный TTL. Если у вас короткий промпт или вы дёргаете API раз в час — кеширование не сэкономит ничего. ## Как включить Передайте `cache_control` в content-блоке сообщения: ```bash curl -s https://api.hubris.pw/v1/chat/completions \ -H "Authorization: Bearer sk-gw-..." \ -H "Content-Type: application/json" \ -d '{ "model": "anthropic/claude-haiku-4.5", "messages": [ { "role": "system", "content": [ { "type": "text", "text": "Очень длинная инструкция на 5000 токенов...", "cache_control": {"type": "ephemeral"} } ] }, { "role": "user", "content": "Ваш вопрос" } ] }' ``` Маркер `cache_control: { type: "ephemeral" }` ставится на content-блок, который вы хотите закешировать. Часто это весь системный промпт или большой документ в начале первого user-сообщения. ## Биллинг В ответе `usage` появятся дополнительные поля от Anthropic: ```json { "usage": { "prompt_tokens": 5050, "completion_tokens": 100, "prompt_tokens_details": { "cached_tokens": 5000 } } } ``` `cached_tokens` — то, что было прочитано из кеша. У Anthropic стоимость cache-read обычно в 10× дешевле обычных prompt-токенов; cache-write — в 1.25× дороже. **Важная оговорка по биллингу Hubris.** Сейчас (на 2026-05) текущий биллинг считает все prompt-токены по полной ставке каталога — без отдельной формулы для cache-read. То есть даже если 90% prompt-токенов прочитаны из кеша, скидки за это не будет. Когда биллинг переделают на формулу с учётом `cached_tokens`, цены в каталоге пересмотрят. Следите за [Changelog](/docs/changelog). Если вам важна экономия от кеширования и текущий биллинг не подходит — пишите в [support@hubris.pw](mailto:support@hubris.pw), обсудим. ## Что закешировать Эффективные кандидаты: * **System prompt с гайдлайнами и примерами** (5–20k токенов) — основная экономия. * **RAG-контекст** — если шлёте те же документы много раз в течение 5 минут. * **Длинный one-shot пример** — для классификаторов или агентов. Не имеет смысла кешировать: * Каждое user-сообщение (они уникальны). * Маленькие промпты (\< 1024 токена). * Запросы реже раза в 5 минут (cache TTL истечёт). ## Что дальше * [POST /v1/chat/completions](/docs/api/chat-completions) — полная схема. * [Каталог моделей](/models) — какие Anthropic-модели доступны. * [Цены](/docs/concepts/pricing) — про токенный биллинг. --- # Reasoning токены (/docs/features/reasoning) > Контроль над reasoning-моделями (o1, R1, Claude thinking) через параметр reasoning_effort. Серия reasoning-моделей (OpenAI o1, DeepSeek R1, Claude с включённым thinking) перед основным ответом тратит токены на «внутреннее размышление». Эти reasoning-токены не показываются в `choices[0].message.content`, но учитываются в счёте. ## Когда использовать Reasoning-модели лучше справляются с задачами, требующими многошагового рассуждения: * Математика, логика, доказательства. * Анализ кода и поиск багов. * Сложные стратегические решения с несколькими переменными. * Юридические и научные тексты с цепочкой выводов. Для коротких ответов (классификация, переформулирование, простые вопросы) reasoning-модели избыточны: тратят больше токенов и дают тот же результат, что и обычные. ## Управление Передайте параметр `reasoning_effort` в запросе: ```bash curl -s https://api.hubris.pw/v1/chat/completions \ -H "Authorization: Bearer sk-gw-..." \ -H "Content-Type: application/json" \ -d '{ "model": "openai/gpt-4o-mini", "messages": [{"role": "user", "content": "Если есть число, простое и сумма цифр которого тоже простое — какое наименьшее?"}], "reasoning_effort": "high" }' ``` Допустимые значения: * `"low"` — минимум reasoning, быстрее и дешевле. * `"medium"` — баланс (default для большинства reasoning-моделей). * `"high"` — максимум, для сложных задач. Не все модели поддерживают `reasoning_effort` — если модель его не понимает, апстрим вернёт ошибку или просто проигнорирует. ## Биллинг Reasoning-токены входят в `completion_tokens` и тарифицируются по обычной цене выходных токенов модели: ```json { "usage": { "prompt_tokens": 50, "completion_tokens": 1200, "total_tokens": 1250, "completion_tokens_details": { "reasoning_tokens": 1100 } } } ``` В примере выше из 1200 completion-токенов 1100 ушли на reasoning, и только 100 — на финальный ответ. Это нормально для сложных задач: модель «обдумывает» дольше, чем пишет. Поле `completion_tokens_details.reasoning_tokens` информативное — показывает, сколько именно ушло на размышления. Денежная стоимость такая же, как для обычных completion-токенов. ## Как читать ответ Сам процесс рассуждения скрыт — его не видно в `message.content`. Видно только финальный ответ: ```json { "choices": [{ "index": 0, "message": { "role": "assistant", "content": "Ответ: 23 (простое, сумма цифр 5 — простое)" }, "finish_reason": "stop" }] } ``` У некоторых моделей в перспективе появится поле `reasoning` рядом с `content` — мы пробросим его как есть от провайдера, без нормализации. ## Стриминг Стриминг работает как обычно: chunk-и приходят по мере генерации финального ответа. Reasoning-фаза происходит ДО первого chunk-а — значит, для задач с высоким `reasoning_effort` первый chunk может прийти через 10–30 секунд молчания, а не сразу. ## Что дальше * [POST /v1/chat/completions](/docs/api/chat-completions) — полная схема параметров. * [Цены](/docs/concepts/pricing) — формула счёта. --- # Стриминг ответов (/docs/features/streaming) > Когда включать `stream: true`, как корректно собирать ответ модели и показывать прогресс пользователю. При `stream: true` ответ модели приходит по частям через Server-Sent Events: вы видите текст по мере того, как он генерируется. Это даёт ощущение «живого» интерфейса: пользователь сразу видит первые слова и не ждёт полной завершённости запроса. Технический формат SSE-стрима (chunk-структура, `[DONE]` маркер, заголовки) — на странице [POST /v1/chat/completions: Стриминг](/docs/api/streaming). Этот гид — про use-cases и практику использования в клиенте. ## Когда включать стриминг Подходящие сценарии: * **Чат-интерфейсы** — пользователь видит ответ по мере появления, как в ChatGPT. * **Длинные ответы** (reasoning-модели, агенты) — без стрима пользователь смотрит 10–30 секунд в крутилку. * **Прогресс-индикация** — показать, что модель уже отвечает (даже если первые слова — это reasoning-токены). Не нужен: * **Однострочные ответы** — классификация, экстракция данных, structured output. Накладные расходы на SSE-парсинг не оправданы. * **Batch-сценарии** — обработка тысяч запросов в parallelизме, где UI не показывается. * **Когда нужен сразу полный JSON для парсинга** — собирать stream и парсить «накопленный буфер» работает, но проще получить готовый ответ одним запросом. ## Python (синхронный) OpenAI SDK скрывает работу с SSE — итерация по объекту `Stream` отдаёт уже распарсенные чанки. ```python from openai import OpenAI client = OpenAI( base_url="https://api.hubris.pw/v1", api_key="sk-gw-...", ) stream = client.chat.completions.create( model="anthropic/claude-haiku-4.5", messages=[{"role": "user", "content": "Расскажи о трёх океанах планеты."}], stream=True, ) for chunk in stream: if chunk.choices and chunk.choices[0].delta.content: print(chunk.choices[0].delta.content, end="", flush=True) # В последнем чанке Hubris отдаёт usage с итоговыми токенами: # (доступен через stream.response.usage после завершения итерации) ``` Итерация автоматически останавливается на `[DONE]`. SDK обрабатывает чанк-буфер за вас — рисковать с ручным парсингом не нужно. ## Python (асинхронный) ```python from openai import AsyncOpenAI client = AsyncOpenAI( base_url="https://api.hubris.pw/v1", api_key="sk-gw-...", ) async def stream_answer(prompt: str): stream = await client.chat.completions.create( model="anthropic/claude-haiku-4.5", messages=[{"role": "user", "content": prompt}], stream=True, ) async for chunk in stream: delta = chunk.choices[0].delta.content if chunk.choices else None if delta: yield delta ``` Удобно для FastAPI / aiohttp-эндпоинтов, которые транслируют ответ дальше клиенту. ## Node.js / TypeScript ```ts import OpenAI from 'openai'; const client = new OpenAI({ baseURL: 'https://api.hubris.pw/v1', apiKey: 'sk-gw-...', }); const stream = await client.chat.completions.create({ model: 'anthropic/claude-haiku-4.5', messages: [{ role: 'user', content: 'Опиши закат.' }], stream: true, }); for await (const chunk of stream) { const delta = chunk.choices[0]?.delta?.content; if (delta) process.stdout.write(delta); } ``` `for await...of` итерирует асинхронно — каждый цикл это уже распарсенный chunk. ## Vercel AI SDK Если вы пишете на Next.js и используете Vercel AI SDK — он работает с Hubris из коробки: ```ts import { openai } from '@ai-sdk/openai'; import { streamText } from 'ai'; const result = await streamText({ model: openai.chat('anthropic/claude-haiku-4.5'), messages: [{ role: 'user', content: 'Hello' }], }); return result.toAIStreamResponse(); ``` Конфигурация base URL — через переменную окружения `OPENAI_BASE_URL=https://api.hubris.pw/v1`, либо явно в `openai({ baseURL: ... })`. См. гид [Vercel AI SDK](/docs/frameworks/vercel-ai-sdk). ## Браузер (fetch + ReadableStream) Если SDK для вас избыточен — можно стримить голыми руками: ```js const resp = await fetch('https://api.hubris.pw/v1/chat/completions', { method: 'POST', headers: { 'Authorization': 'Bearer sk-gw-...', 'Content-Type': 'application/json', }, body: JSON.stringify({ model: 'anthropic/claude-haiku-4.5', messages: [{ role: 'user', content: 'Привет' }], stream: true, }), }); const reader = resp.body.getReader(); const decoder = new TextDecoder(); let buffer = ''; while (true) { const { done, value } = await reader.read(); if (done) break; buffer += decoder.decode(value, { stream: true }); // Делим буфер по \n\n — каждый блок это SSE-событие const events = buffer.split('\n\n'); buffer = events.pop() ?? ''; // последний может быть неполным — оставляем for (const event of events) { if (!event.startsWith('data: ')) continue; const payload = event.slice('data: '.length); if (payload === '[DONE]') return; const chunk = JSON.parse(payload); const delta = chunk.choices[0]?.delta?.content; if (delta) appendToUI(delta); } } ``` Ключевые правила: * **Буфер обязателен.** TCP может разбить SSE-блок на середине — не делайте `JSON.parse` сразу. * **`[DONE]` не парсить как JSON.** Это литерал-маркер. * **Используйте `decoder.decode(value, { stream: true })`** — чтобы UTF-8 multi-byte символы не ломались на границе чанков. ## Cancellation (отмена запроса) Стандартный `AbortController` работает: ```ts const ac = new AbortController(); const stream = await client.chat.completions.create( { model: 'anthropic/claude-haiku-4.5', messages: [{ role: 'user', content: '...' }], stream: true, }, { signal: ac.signal }, ); // Через 5 секунд отменяем setTimeout(() => ac.abort(), 5000); try { for await (const chunk of stream) { // ... } } catch (e) { if (e.name === 'AbortError') { console.log('Запрос отменён пользователем'); } } ``` **Важно:** Hubris продолжает читать ответ модели до конца, даже если клиент закрыл соединение. Это защита от «бесплатных токенов». Иными словами, при отмене вы получите полное списание стоимости — токены уже сгенерированы апстримом, мы просто не доставили их вам. ## Прогресс в UI: типичные паттерны ### Типографический эффект (как в ChatGPT) ```ts let buffer = ''; for await (const chunk of stream) { const delta = chunk.choices[0]?.delta?.content ?? ''; buffer += delta; setMessage(buffer); // React state setter } ``` ### Индикатор «модель думает» Reasoning-модели могут несколько секунд молчать перед первым content-чанком (см. [Reasoning токены](/docs/features/reasoning)). Покажите spinner, пока `delta.content` не пришёл хотя бы один раз: ```ts let firstChunk = true; for await (const chunk of stream) { const delta = chunk.choices[0]?.delta?.content; if (!delta) continue; if (firstChunk) { hideSpinner(); firstChunk = false; } appendText(delta); } ``` ### Кнопка «Stop» с откатом UI При отмене иногда нужно показать частичный ответ + label «прервано». Сохраняйте накопленный буфер в state и при `AbortError` дополняйте его пометкой — не очищайте. ## Tool calls и стриминг Если модель вызывает функцию, `delta.tool_calls[]` приходит частями (имя функции и аргументы могут разорваться по чанкам). Собирайте по `index`. Подробности — в гиде [Вызов инструментов](/docs/features/tool-calling#стриминг). ## Биллинг Стриминг не меняет тарификацию: списываем по тем же prompt + completion токенам, что и в обычном (не-стрим) запросе. Hubris всегда добавляет `stream_options: { include_usage: true }` к стрим-запросам, поэтому в последнем чанке вы видите итоговые токены. Дисконнект клиента до завершения стрима — полное списание (см. выше). Это не баг, это анти-абьюз. ## Что важно знать * **OpenAI SDK почти всегда правильный выбор.** Ручной SSE-парсинг — только если есть конкретная причина (другой стек, экстремальная оптимизация). * **Чанк-границы непредсказуемы.** Не пытайтесь парсить `delta.content` как JSON — это просто кусок строки. * **Headers commit before bytes.** Если стрим уже начался — мы уже отдали `200 OK` и SSE-заголовки. Ошибки ПОСЛЕ старта стрима не возвращаются как HTTP 5xx — клиент видит обрыв или `finish_reason: "stop"` на пустом delta. ## Что дальше * [POST /v1/chat/completions: Стриминг](/docs/api/streaming) — технический формат чанков. * [Вызов инструментов](/docs/features/tool-calling) — стрим с tool calls. * [Reasoning токены](/docs/features/reasoning) — почему первый чанк может прийти не сразу. * [Vercel AI SDK](/docs/frameworks/vercel-ai-sdk) — React/Next-стрим из коробки. --- # Структурированный вывод (/docs/features/structured-output) > Получение ответа строго в формате JSON по заданной схеме — без парсинга текста и без шанса получить «почти-JSON». Структурированный вывод гарантирует, что модель вернёт валидный JSON и, при необходимости, соответствующий конкретной JSON Schema. Это убирает целый класс ошибок: «модель прислала JSON с лишним текстом», «закрывающая скобка съехала», «вместо числа — строка с числом». Поддерживается большинством современных моделей. Жёсткое следование схеме (`strict: true`) — у OpenAI gpt-4o и новее, Claude 3.5 Sonnet и новее, Gemini 2.0+. Карточка модели в [каталоге](/models) подскажет. ## Два режима Параметр `response_format` принимает один из трёх вариантов: | Тип | Что гарантирует | | -------------------------------- | ----------------------------------------------------------------- | | `{ "type": "text" }` | Свободный текст. То же, что не передавать `response_format` вовсе | | `{ "type": "json_object" }` | Ответ — синтаксически валидный JSON. Структура произвольная | | `{ "type": "json_schema", ... }` | Ответ — JSON, соответствующий вашей схеме | Режим `json_object` достаточен, если вы сами в системном промпте описали ожидаемые поля. Режим `json_schema` — строже: апстрим валидирует ответ модели и при `strict: true` гарантирует точное соответствие схеме. ## json\_object — простой случай ```bash curl -s https://api.hubris.pw/v1/chat/completions \ -H "Authorization: Bearer sk-gw-..." \ -H "Content-Type: application/json" \ -d '{ "model": "openai/gpt-4o-mini", "messages": [ {"role": "system", "content": "Верни поля: name (строка), age (число)."}, {"role": "user", "content": "Меня зовут Алексей, мне 34 года."} ], "response_format": { "type": "json_object" } }' ``` Ответ — гарантированно валидный JSON, но конкретные поля зависят от модели: ```json { "choices": [{ "message": { "role": "assistant", "content": "{\"name\": \"Алексей\", \"age\": 34}" } }] } ``` Контент по-прежнему приходит строкой — `JSON.parse(...)` обязателен. ## json\_schema — строгий режим Передайте схему в `response_format.json_schema`. Поле `strict: true` включает жёсткую валидацию: апстрим заранее ограничивает токены, которые модель может выдать, чтобы получить именно соответствующий вашей схеме JSON. ```bash curl -s https://api.hubris.pw/v1/chat/completions \ -H "Authorization: Bearer sk-gw-..." \ -d '{ "model": "openai/gpt-4o-mini", "messages": [ {"role": "user", "content": "Извлеки данные из квитанции: пицца 750₽, кола 150₽, чаевые 100₽."} ], "response_format": { "type": "json_schema", "json_schema": { "name": "receipt", "strict": true, "schema": { "type": "object", "properties": { "items": { "type": "array", "items": { "type": "object", "properties": { "name": {"type": "string"}, "price_rub": {"type": "number"} }, "required": ["name", "price_rub"], "additionalProperties": false } }, "tip_rub": {"type": "number"}, "total_rub": {"type": "number"} }, "required": ["items", "tip_rub", "total_rub"], "additionalProperties": false } } } }' ``` Ответ: ```json { "choices": [{ "message": { "content": "{\"items\":[{\"name\":\"пицца\",\"price_rub\":750},{\"name\":\"кола\",\"price_rub\":150}],\"tip_rub\":100,\"total_rub\":1000}" }, "finish_reason": "stop" }] } ``` ### Требования к схеме при `strict: true` * Корневой объект — обязательно `"type": "object"`. * Все поля, которые могут присутствовать в ответе, должны быть в `required[]`. Опциональные поля нужно делать union с `null`: `{"type": ["string", "null"]}`. * На каждом уровне объекта обязателен `"additionalProperties": false`. * Нельзя использовать `oneOf`, `allOf`, `not`, регулярные выражения в pattern, рекурсивные ссылки. Если схема не проходит этим требованиям — апстрим вернёт ошибку валидации до старта генерации. ## OpenAI SDK **Python (`pydantic` + helper `parse`):** ```python from openai import OpenAI from pydantic import BaseModel client = OpenAI( base_url="https://api.hubris.pw/v1", api_key="sk-gw-...", ) class ReceiptItem(BaseModel): name: str price_rub: float class Receipt(BaseModel): items: list[ReceiptItem] tip_rub: float total_rub: float resp = client.chat.completions.parse( model="openai/gpt-4o-mini", messages=[{"role": "user", "content": "Пицца 750, кола 150, чаевые 100."}], response_format=Receipt, ) receipt = resp.choices[0].message.parsed print(receipt.total_rub) # 1000.0 ``` **TypeScript (`zod` + helper `parse`):** ```ts import OpenAI from 'openai'; import { z } from 'zod'; import { zodResponseFormat } from 'openai/helpers/zod'; const client = new OpenAI({ baseURL: 'https://api.hubris.pw/v1', apiKey: 'sk-gw-...', }); const Receipt = z.object({ items: z.array(z.object({ name: z.string(), price_rub: z.number(), })), tip_rub: z.number(), total_rub: z.number(), }); const resp = await client.chat.completions.parse({ model: 'openai/gpt-4o-mini', messages: [{ role: 'user', content: 'Пицца 750, кола 150, чаевые 100.' }], response_format: zodResponseFormat(Receipt, 'receipt'), }); const receipt = resp.choices[0].message.parsed; console.log(receipt?.total_rub); // 1000 ``` Хелперы `parse` сами добавят правильный `response_format` и при успешном ответе вернут уже распарсенный и провалидированный объект в `.parsed`. ## Что делать с отказами Если модель решит, что задача нерешаема (например, в квитанции совсем нет цен), она вернёт `refusal` вместо контента: ```json { "choices": [{ "message": { "role": "assistant", "content": null, "refusal": "Не могу извлечь цены — в тексте нет числовых данных." }, "finish_reason": "stop" }] } ``` В коде проверяйте оба поля — иначе при `refusal` ваш `JSON.parse(content)` упадёт на `null`. ## Стриминг `stream: true` работает. Чанки в `delta.content` приходят кусками всё ещё валидной JSON-строки — собирайте, как обычный текст, парсите финал. Для прогрессивного парсинга (показывать ответ по мере прихода) понадобится партиальный JSON-парсер на клиенте. ## Биллинг Структурированный вывод не имеет отдельного тарифа. Платите за обычные prompt- и completion-токены: * Описание схемы попадает в prompt-токены каждого запроса. * Финальный JSON-ответ — в completion-токенах. Длинные схемы (десятки полей) могут заметно раздувать prompt-расход — учитывайте при тарификации. ## Что дальше * [Вызов инструментов](/docs/features/tool-calling) — если вместо одного JSON-ответа нужно вызвать функцию и продолжить диалог. * [POST /v1/chat/completions](/docs/api/chat-completions) — полная схема параметров. * [Каталог моделей](/models) — поиск моделей с поддержкой `json_schema`. --- # Вызов инструментов (tool calling) (/docs/features/tool-calling) > Подключение функций к моделям — модель просит вызвать вашу функцию, вы исполняете её локально, отдаёте результат и получаете финальный ответ. Tool calling позволяет модели запросить у вас выполнение функции (получить погоду, прочитать БД, отправить письмо), получить результат и продолжить диалог с учётом этого результата. Сама модель не исполняет код — она только описывает, что и с какими аргументами надо вызвать. Исполняет — ваш клиент. Поддерживается большинством современных моделей: Claude (Sonnet, Opus, Haiku), GPT-4o и новее, Gemini Pro/Flash, Llama 3.1+, Mistral Large. Конкретная карточка модели в [каталоге](/models) показывает поддержку. ## Как это работает Жизненный цикл одного «раунда» вызова инструмента: 1. Вы шлёте запрос с `messages` и `tools` (описанием доступных функций). 2. Модель решает: ответить текстом или попросить вызвать функцию. В случае второго — возвращает `finish_reason: "tool_calls"` и массив `tool_calls[]` в assistant-сообщении. 3. Вы локально исполняете эти функции, формируете ответы. 4. Шлёте новый запрос: добавляете assistant-сообщение с `tool_calls` и одно или несколько `role: "tool"` сообщений с результатами. 5. Модель формирует финальный ответ для пользователя. При параллельных вызовах модель может попросить вызвать несколько функций за один шаг. ## Минимальный пример Шаг 1 — запрос с описанием функции: ```bash curl -s https://api.hubris.pw/v1/chat/completions \ -H "Authorization: Bearer sk-gw-..." \ -H "Content-Type: application/json" \ -d '{ "model": "anthropic/claude-haiku-4.5", "messages": [ {"role": "user", "content": "Какая погода в Москве?"} ], "tools": [ { "type": "function", "function": { "name": "get_weather", "description": "Возвращает текущую погоду в указанном городе.", "parameters": { "type": "object", "properties": { "city": {"type": "string", "description": "Название города"}, "units": {"type": "string", "enum": ["celsius", "fahrenheit"]} }, "required": ["city"] } } } ] }' ``` Ответ модели — она просит вызвать `get_weather`: ```json { "id": "chatcmpl-...", "choices": [{ "index": 0, "message": { "role": "assistant", "content": null, "tool_calls": [ { "id": "call_abc123", "type": "function", "function": { "name": "get_weather", "arguments": "{\"city\":\"Москва\",\"units\":\"celsius\"}" } } ] }, "finish_reason": "tool_calls" }] } ``` Шаг 2 — исполняете функцию у себя и шлёте результат. Важно: сохраните `id` вызова из `tool_calls[].id` и проставьте его в `tool_call_id`. ```bash curl -s https://api.hubris.pw/v1/chat/completions \ -H "Authorization: Bearer sk-gw-..." \ -d '{ "model": "anthropic/claude-haiku-4.5", "messages": [ {"role": "user", "content": "Какая погода в Москве?"}, { "role": "assistant", "content": null, "tool_calls": [{ "id": "call_abc123", "type": "function", "function": { "name": "get_weather", "arguments": "{\"city\":\"Москва\",\"units\":\"celsius\"}" } }] }, { "role": "tool", "tool_call_id": "call_abc123", "content": "{\"temperature\": -3, \"conditions\": \"снег\"}" } ], "tools": [ /* те же tools, что и в первом запросе */ ] }' ``` Модель ответит текстом с учётом результата: ```json { "choices": [{ "message": { "role": "assistant", "content": "В Москве сейчас −3 °C и идёт снег. Одевайтесь теплее." }, "finish_reason": "stop" }] } ``` ## `tool_choice` — как заставить или запретить вызовы По умолчанию (`auto`) модель сама решает, нужен ли инструмент. Можно переопределить: | Значение | Эффект | | ----------------------------------------------------- | ----------------------------------------------- | | `"auto"` (по умолчанию) | Модель сама решает: текст или вызов | | `"none"` | Запретить вызовы, модель ответит только текстом | | `"required"` | Заставить вызвать какой-нибудь инструмент | | `{ "type": "function", "function": { "name": "X" } }` | Заставить вызвать именно функцию `X` | ```json "tool_choice": { "type": "function", "function": { "name": "get_weather" } } ``` ## Параллельные вызовы Если в одном шаге модель решит вызвать несколько функций сразу (например, погоду в трёх городах) — в `tool_calls[]` придёт несколько элементов. Вы исполняете их параллельно, шлёте обратно столько же `role: "tool"` сообщений с соответствующими `tool_call_id`. Отключить параллельные вызовы (модель будет звать функции по одной): ```json "parallel_tool_calls": false ``` ## Стриминг При `stream: true` поля `tool_calls` приходят в `delta.tool_calls[]` по частям — имя функции и аргументы могут разбиться на несколько чанков. Собирайте по `index`: ```json data: {"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"id":"call_abc","type":"function","function":{"name":"get_weather"}}]}}]} data: {"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\"city\":"}}]}}]} data: {"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"Москва\"}"}}]}}]} data: {"choices":[{"index":0,"finish_reason":"tool_calls"}]} ``` Финальный чанк с `finish_reason: "tool_calls"` означает, что модель закончила формировать вызов и ждёт результата. ## OpenAI SDK Официальные SDK OpenAI поддерживают tools «как есть». **Python:** ```python from openai import OpenAI client = OpenAI( base_url="https://api.hubris.pw/v1", api_key="sk-gw-...", ) tools = [{ "type": "function", "function": { "name": "get_weather", "description": "Возвращает текущую погоду в указанном городе.", "parameters": { "type": "object", "properties": { "city": {"type": "string"}, "units": {"type": "string", "enum": ["celsius", "fahrenheit"]}, }, "required": ["city"], }, }, }] resp = client.chat.completions.create( model="anthropic/claude-haiku-4.5", messages=[{"role": "user", "content": "Какая погода в Москве?"}], tools=tools, ) msg = resp.choices[0].message if msg.tool_calls: for call in msg.tool_calls: args = json.loads(call.function.arguments) result = get_weather(**args) # ваша локальная функция # ... добавьте assistant + tool сообщения и сделайте второй вызов ``` **TypeScript:** ```ts import OpenAI from 'openai'; const client = new OpenAI({ baseURL: 'https://api.hubris.pw/v1', apiKey: 'sk-gw-...', }); const resp = await client.chat.completions.create({ model: 'anthropic/claude-haiku-4.5', messages: [{ role: 'user', content: 'Какая погода в Москве?' }], tools: [ { type: 'function', function: { name: 'get_weather', description: 'Возвращает текущую погоду в указанном городе.', parameters: { type: 'object', properties: { city: { type: 'string' }, units: { type: 'string', enum: ['celsius', 'fahrenheit'] }, }, required: ['city'], }, }, }, ], }); const msg = resp.choices[0].message; if (msg.tool_calls?.length) { for (const call of msg.tool_calls) { const args = JSON.parse(call.function.arguments); const result = await getWeather(args); // ... добавьте assistant + tool сообщения и сделайте второй вызов } } ``` ## Биллинг Tool-calling не имеет отдельного тарифа. Списываются обычные prompt- и completion-токены за каждый шаг диалога: * Шаг 1 (модель просит вызвать функцию) — `prompt_tokens` за описание инструментов и сообщения, `completion_tokens` за сгенерированный `tool_calls[]`. * Шаг 2 (вы шлёте результат) — `prompt_tokens` за весь диалог (включая `role: "tool"` сообщения), `completion_tokens` за финальный ответ. Описания инструментов считаются как часть промпта на каждом шаге, где они переданы. Если у вас 10 функций по 200 токенов каждая — это +2000 prompt-токенов за вызов. ## Что дальше * [POST /v1/chat/completions](/docs/api/chat-completions) — полная схема параметров и ответа. * [Структурированный вывод](/docs/features/structured-output) — если вам нужен не вызов функции, а строго типизированный JSON-ответ. * [Поиск в интернете](/docs/features/web-search) — встроенный server-side инструмент, который мы исполняем за вас. * [Каталог моделей](/models) — какие модели поддерживают tool-calling. --- # Мониторинг расходов (/docs/features/usage-tracking) > Программный учёт трат через GET /v1/usage — отслеживание расхода по ключу, бюджет-алёрты, отчёты. Hubris отдаёт расход по API-ключу через `GET /v1/usage` — отдельные запросы и агрегированные суммы. Используется для автоматического мониторинга: алёрты по порогам, отчёты, экспорт в свою аналитику. Endpoint-референс — на [GET /v1/usage](/docs/api/usage). Этот гид — про практические паттерны. ## Базовый запрос ```bash curl -s -H "Authorization: Bearer $HUBRIS_API_KEY" \ https://api.hubris.pw/v1/usage ``` По умолчанию — расход за последние 24 часа по тому ключу, которым сделан запрос. Ответ: ```json { "object": "usage", "period": { "from": "2026-05-21T00:00Z", "to": "2026-05-22T00:00Z" }, "scope": { "key_id": "...", "key_prefix": "sk-gw-758f...d4e3" }, "totals": { "requests": 142, "prompt_tokens": 18500, "completion_tokens": 3200, "total_tokens": 21700, "cost_rub": 187.45, "cost_kopecks": "18745" }, "granularity": null, "buckets": null } ``` **Важно:** виден расход **только этого ключа**, а не всего аккаунта. Это намеренно — скомпрометированный ключ не должен раскрывать общий бюджет. ## Паттерн 1: ежедневный отчёт Cron-задача, которая раз в день берёт суммарный расход за вчера и шлёт письмо/слак/телеграм: ```python import os import requests from datetime import datetime, timedelta, timezone key = os.environ["HUBRIS_API_KEY"] yesterday = datetime.now(timezone.utc) - timedelta(days=1) day_start = yesterday.replace(hour=0, minute=0, second=0, microsecond=0) day_end = day_start + timedelta(days=1) resp = requests.get( "https://api.hubris.pw/v1/usage", params={"from": day_start.isoformat(), "to": day_end.isoformat()}, headers={"Authorization": f"Bearer {key}"}, ).json() totals = resp["totals"] print(f"Вчера: {totals['requests']} запросов, {totals['cost_rub']} ₽") # Отправить в Slack, если расход за день выше нормы: if float(totals["cost_rub"]) > 500: notify_slack(f"⚠️ Вчера ушло {totals['cost_rub']} ₽") ``` ## Паттерн 2: бюджет-алёрт после каждого запроса Если у вас агент, который может за сессию «уйти в космос» — проверяйте расход после каждого запроса и останавливайтесь при превышении лимита: ```python DAILY_BUDGET_RUB = 1000 def check_budget(): resp = requests.get( "https://api.hubris.pw/v1/usage", params={"period": "today"}, headers={"Authorization": f"Bearer {key}"}, ).json() return float(resp["totals"]["cost_rub"]) # В цикле агента while task_not_done: if check_budget() > DAILY_BUDGET_RUB: raise BudgetExceeded(f"Превышен дневной лимит {DAILY_BUDGET_RUB} ₽") response = run_step() process(response) ``` В большинстве сценариев лучше использовать **встроенный дневной лимит ключа** (см. [/keys](/keys)) — он гарантированно прерывает на стороне сервера и возвращает `429 daily_limit_exceeded`. Бюджет в коде — мягкая защита поверх него, для случаев когда нужен ранний алёрт «потратили 80% бюджета — переключаемся на дешёвую модель». ## Паттерн 3: график по дням Для дашборда — берите данные с почасовой/посуточной разбивкой: ```bash curl -s -H "Authorization: Bearer $HUBRIS_API_KEY" \ "https://api.hubris.pw/v1/usage?period=30d&granularity=day" ``` В ответе появится массив `buckets`: ```json { "totals": { "cost_rub": 5432.10, ... }, "granularity": "day", "buckets": [ { "bucket": "2026-04-22T00:00:00Z", "cost_rub": 180.30, "requests": 142 }, { "bucket": "2026-04-23T00:00:00Z", "cost_rub": 213.45, "requests": 167 }, { "bucket": "2026-04-24T00:00:00Z", "cost_rub": 0, "requests": 0 } ] } ``` Дни без запросов всё равно присутствуют (с `cost_rub: 0`) — удобно для линейного графика без пропусков. ## Паттерн 4: разделение по проектам Один аккаунт — несколько ключей под разные проекты. Дашборд `/usage` агрегирует общий расход; API даёт расход **только запрашиваемого ключа**. Чтобы получить разбивку по проектам — храните ключи (или их `key_id`) на стороне ваших систем и запрашивайте `/v1/usage` каждым ключом отдельно: ```python PROJECT_KEYS = { "production": os.environ["HUBRIS_KEY_PROD"], "staging": os.environ["HUBRIS_KEY_STG"], "research": os.environ["HUBRIS_KEY_RES"], } for project, key in PROJECT_KEYS.items(): resp = requests.get( "https://api.hubris.pw/v1/usage", params={"period": "7d"}, headers={"Authorization": f"Bearer {key}"}, ).json() print(f"{project}: {resp['totals']['cost_rub']} ₽ / {resp['totals']['requests']} req") ``` ## TypeScript ```ts type UsageResp = { totals: { requests: number; prompt_tokens: number; completion_tokens: number; cost_rub: number; cost_kopecks: string; }; buckets: Array<{ bucket: string; cost_rub: number; cost_kopecks: string; requests: number; }> | null; }; async function fetchUsage(period = '7d', granularity?: 'hour' | 'day') { const url = new URL('https://api.hubris.pw/v1/usage'); url.searchParams.set('period', period); if (granularity) url.searchParams.set('granularity', granularity); const resp = await fetch(url, { headers: { Authorization: `Bearer ${process.env.HUBRIS_API_KEY}` }, }); return (await resp.json()) as UsageResp; } const week = await fetchUsage('7d', 'day'); console.log(`За 7 дней: ${week.totals.cost_rub} ₽`); for (const day of week.buckets ?? []) { console.log(` ${day.bucket}: ${day.cost_rub} ₽`); } ``` ## Точность сумм при больших расходах `cost_kopecks` приходит как строка — чтобы при сериализации в JavaScript не терялась точность на больших суммах. Если вам важна копейка-в-копейку точность (бухучёт, биллинг клиентов поверх Hubris) — парсите `cost_kopecks` через `BigInt`, не через `Number`: ```ts const kopecks = BigInt(resp.totals.cost_kopecks); const rubFormatted = (Number(kopecks) / 100).toFixed(2); ``` Поле `cost_rub` — это уже `cost_kopecks / 100`, удобно для отображения, но не для арифметики. ## Часовые пояса Время в API — UTC. Если нужно «расход за сегодня по Москве» — конвертируйте границы суток у себя и передавайте явные `from` / `to` (ISO 8601 UTC): ```python from datetime import datetime, timezone, timedelta MSK = timezone(timedelta(hours=3)) now_msk = datetime.now(MSK) day_start_msk = now_msk.replace(hour=0, minute=0, second=0, microsecond=0) day_end_msk = day_start_msk + timedelta(days=1) resp = requests.get( "https://api.hubris.pw/v1/usage", params={ "from": day_start_msk.astimezone(timezone.utc).isoformat(), "to": day_end_msk.astimezone(timezone.utc).isoformat(), }, headers={"Authorization": f"Bearer {key}"}, ).json() ``` ## Дашбордная альтернатива Если ручной мониторинг — overkill, на странице [/usage](/usage) дашборда есть готовые фильтры по модели, ключу и статусу, графики по дням и CSV-экспорт. Многие сценарии решаются прямо в UI без написания кода. ## Что важно знать * **Кешируйте.** Не дёргайте `/v1/usage` чаще, чем раз в минуту — для большинства сценариев достаточно раз в 5–10 минут. Избыточные запросы влияют только на ваш rate-budget. * **Расход обновляется в течение нескольких секунд** после успешного списания. Свежий запрос → пара секунд → его стоимость в `/v1/usage`. * **Запросы на free-модели** добавляются к `requests`, но не к `cost_rub`. Если у вас активны бесплатные модели, число запросов в дашборде будет выше, чем «сколько денег ушло». * **Только успешные запросы** входят в totals. Запросы с ошибками (4xx/5xx) не списываются и не попадают в счётчик расхода. ## Что дальше * [GET /v1/usage](/docs/api/usage) — полная схема параметров и ответа. * [Управление ключами](/keys) — настроить дневной лимит на уровне ключа. * [Биллинг](/docs/concepts/billing) — как формируется стоимость токенов. * [/usage](/usage) — дашборд расходов с фильтрами и CSV-экспортом. --- # Изображения на вход (Vision) (/docs/features/vision) > Отправка изображений в модель для анализа — описание содержимого, OCR, классификация, ответы на вопросы по картинке. Vision (мультимодальный ввод) — это когда модель получает на вход не только текст, но и изображения. Подходит для описания содержимого, OCR, классификации, ответов на вопросы по картинке, разбора скриншотов и диаграмм. Не путайте с [генерацией изображений](/docs/features/image-generation): vision — это **картинка на вход**, image-generation — **картинка на выход**. ## Поддерживающие модели Vision поддерживает любая модель, у которой в карточке (`GET /v1/models`) поле `input_modalities` содержит `"image"`. Это, как правило, актуальные версии Claude (Sonnet/Opus), GPT-4o, Gemini, а также часть open-source моделей (Llama Vision, Qwen-VL). Точный актуальный список — в [каталоге](/models) с фильтром «принимает изображения». ## Минимальный пример Передайте картинку как content-part типа `image_url` внутри user-сообщения: ```bash curl -s https://api.hubris.pw/v1/chat/completions \ -H "Authorization: Bearer sk-gw-..." \ -H "Content-Type: application/json" \ -d '{ "model": "anthropic/claude-haiku-4.5", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Что изображено на картинке?" }, { "type": "image_url", "image_url": { "url": "https://example.com/photo.jpg" } } ] } ] }' ``` Контент user-сообщения — это массив content-part'ов. Каждая часть — либо `{ "type": "text", "text": "..." }`, либо `{ "type": "image_url", "image_url": { "url": "..." } }`. Порядок свободный. ## Два способа передать картинку ### URL ```json { "type": "image_url", "image_url": { "url": "https://example.com/photo.jpg" } } ``` Должен быть публично-доступный HTTPS URL. Провайдер модели скачает картинку сам. Размер и формат — на усмотрение провайдера; типичный лимит 5–20 МБ, поддерживаются JPEG, PNG, WebP, GIF. ### Base64 data URL Когда картинка не лежит в публичном интернете (генерится на лету, приходит от пользователя), кодируйте её в base64 и шлите как data URL: ```json { "type": "image_url", "image_url": { "url": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAAAAAAA..." } } ``` Формат — `data:image/;base64,`. Mime может быть `image/jpeg`, `image/png`, `image/webp`, `image/gif`. Base64-сам по себе делает строку на \~33 % длиннее, поэтому общий размер запроса может быть значительным — учитывайте при тарификации (см. ниже). ## Несколько изображений в одном запросе Можно передать сколько угодно картинок в одном сообщении — добавляйте content-part'ы по одной: ```json { "role": "user", "content": [ { "type": "text", "text": "Сравни эти два скриншота интерфейса." }, { "type": "image_url", "image_url": { "url": "https://.../before.png" } }, { "type": "image_url", "image_url": { "url": "https://.../after.png" } } ] } ``` Модель увидит обе картинки и сможет ссылаться на них в ответе («на первом скриншоте...»). ## Параметр `detail` Часть моделей (gpt-4o-семейство) принимают подсказку о требуемой детализации: ```json { "type": "image_url", "image_url": { "url": "https://...", "detail": "high" } } ``` | Значение | Что значит | | ----------------------- | ------------------------------------------------------------------------------------ | | `"low"` | модель смотрит на картинку в низком разрешении — дёшево, годится для общего описания | | `"high"` | детальное чтение — мелкий текст, цифры, диаграммы | | `"auto"` (по умолчанию) | модель решает сама | Не все модели поддерживают `detail` — он пробрасывается как есть; если модель его не понимает, пара значение/поле просто игнорируется апстримом. ## OpenAI SDK Vision работает в официальных SDK OpenAI без дополнительных манипуляций. **Python:** ```python from openai import OpenAI client = OpenAI( base_url="https://api.hubris.pw/v1", api_key="sk-gw-...", ) resp = client.chat.completions.create( model="openai/gpt-4o-mini", messages=[ { "role": "user", "content": [ {"type": "text", "text": "Распознай текст на этом скриншоте."}, { "type": "image_url", "image_url": { "url": "https://example.com/screenshot.png", "detail": "high", }, }, ], } ], ) print(resp.choices[0].message.content) ``` **TypeScript:** ```ts import OpenAI from 'openai'; const client = new OpenAI({ baseURL: 'https://api.hubris.pw/v1', apiKey: 'sk-gw-...', }); const resp = await client.chat.completions.create({ model: 'openai/gpt-4o-mini', messages: [ { role: 'user', content: [ { type: 'text', text: 'Опиши, что на фото.' }, { type: 'image_url', image_url: { url: 'https://example.com/photo.jpg' }, }, ], }, ], }); console.log(resp.choices[0].message.content); ``` ### Base64 в Python из файла ```python import base64 with open("photo.jpg", "rb") as f: encoded = base64.b64encode(f.read()).decode() data_url = f"data:image/jpeg;base64,{encoded}" resp = client.chat.completions.create( model="openai/gpt-4o-mini", messages=[ { "role": "user", "content": [ {"type": "text", "text": "Опиши фото."}, {"type": "image_url", "image_url": {"url": data_url}}, ], } ], ) ``` ## Стриминг `stream: true` работает с vision-запросами как с обычными — ответ модели приходит чанками, prompt с изображениями просто длиннее. Никаких особенностей. ## Биллинг Изображение на вход тарифицируется как **дополнительные prompt-токены**. Количество токенов на одну картинку зависит от модели и от её размера/разрешения. Грубо: * Маленькая картинка в `detail: "low"` — порядка сотни prompt-токенов. * Стандартное разрешение в `detail: "auto"`/`"high"` — от нескольких сотен до нескольких тысяч prompt-токенов. Точное количество видно в `usage.prompt_tokens` в ответе. Списание с баланса — стандартная формула: prompt + completion токены умножаются на цену модели из [каталога](/models). Base64 в URL **не** добавляет токенов — это просто способ доставки байтов. Тарифицируется только распарсенная картинка, не размер JSON-запроса. ## Что важно знать * **Большие картинки = большой prompt.** Если шлёте 2K-скриншоты, ожидайте по 1000–3000 prompt-токенов на каждую. На сессии с десятком картинок это заметные деньги. * **Картинка в URL должна быть публично-доступной.** Авторизованные URL (`Authorization: ...` заголовок при загрузке) не подойдут — провайдер скачивает анонимно. Если картинка приватная — шлите как base64. * **Vision и structured output совмещаются.** Можно передать картинку и попросить вернуть JSON по схеме — например, извлечь поля из чека. * **Vision и tool calling тоже совмещаются.** Модель может смотреть на картинку и вызывать функции на основе того, что увидела. ## Что дальше * [Генерация изображений](/docs/features/image-generation) — обратная задача: получить картинку на выходе. * [Структурированный вывод](/docs/features/structured-output) — извлечение данных из картинки в JSON-схему. * [POST /v1/chat/completions](/docs/api/chat-completions) — полная схема параметров. * [Каталог моделей](/models) — фильтр по поддержке Vision. --- # Поиск в интернете (/docs/features/web-search) > Server-side инструмент — модель сама ищет в интернете и пишет ответ с источниками. Никакого ручного исполнения у вас на стороне. В отличие от обычных функций ([Вызов инструментов](/docs/features/tool-calling)), которые исполняете вы, веб-поиск — это **server-side инструмент**: вы добавляете его в `tools[]`, модель сама вызывает поиск, получает результаты, формирует ответ и присылает его одним финальным сообщением. Вашему клиенту ничего исполнять не нужно — никаких лишних round-trip'ов и `role: "tool"` сообщений. Работает с большинством моделей. Под капотом наш слой маршрутизации выбирает поисковый движок — у части моделей он встроен в саму модель, у части подключается отдельно. Для вас разница только в наборе источников, которые попадут в ответ. ## Минимальный пример Передайте в `tools[]` элемент с типом `hubris:web_search` (это наш namespace для server-side инструментов — отличает их от обычных function-tool): ```bash curl -s https://api.hubris.pw/v1/chat/completions \ -H "Authorization: Bearer sk-gw-..." \ -H "Content-Type: application/json" \ -d '{ "model": "openai/gpt-4o-mini", "messages": [ {"role": "user", "content": "Что произошло на рынке российских облигаций на прошлой неделе?"} ], "tools": [ { "type": "hubris:web_search" } ] }' ``` Ответ — обычный chat-ответ, но с дополнительным полем `annotations[]`, где перечислены источники: ```json { "id": "chatcmpl-...", "choices": [{ "index": 0, "message": { "role": "assistant", "content": "На прошлой неделе ставка ОФЗ выросла до 16.3% на длинных бумагах...", "annotations": [ { "type": "url_citation", "url_citation": { "url": "https://www.cbr.ru/press/event/?id=12345", "title": "ЦБ РФ — Информация о денежно-кредитной политике", "content": "Совет директоров Банка России 17 мая 2026 года...", "start_index": 42, "end_index": 87 } } ] }, "finish_reason": "stop" }], "usage": { "prompt_tokens": 4521, "completion_tokens": 318, "total_tokens": 4839 } } ``` `tool_calls` остаётся пустым — поиск отрабатывается полностью на стороне нашего слоя маршрутизации, наружу не вылезает. ## Параметры поиска Можно тонко настроить поведение: ```json { "type": "hubris:web_search", "parameters": { "max_results": 5, "search_context_size": "medium" } } ``` | Параметр | Значения | Описание | | --------------------- | ------------------------------- | -------------------------------------------------------------------------------------------- | | `max_results` | 1–10 (по умолчанию 5) | Сколько источников использовать для составления ответа | | `search_context_size` | `"low"` / `"medium"` / `"high"` | Сколько контекста из каждого источника подгружать в промпт. Больше — точнее ответ, но дороже | Все параметры опциональны. ## Аннотации `annotations[]` приходит на сообщении в формате: ```json { "type": "url_citation", "url_citation": { "url": "https://...", "title": "Заголовок страницы", "content": "Релевантный фрагмент текста источника", "start_index": 42, "end_index": 87 } } ``` `start_index` / `end_index` — позиция (в символах) в `message.content`, к которой относится цитата. Удобно для подсветки источников рядом с текстом в UI. ## Стриминг `stream: true` работает. Особенность: **`annotations[]` приходит полным массивом сразу в первом чанке** (внутри `delta.annotations`), ещё до самого текста. Дальше идут обычные текстовые `delta.content` чанки. ``` data: {"choices":[{"index":0,"delta":{"role":"assistant","annotations":[{"type":"url_citation","url_citation":{...}}]}}]} data: {"choices":[{"index":0,"delta":{"content":"На прошлой "}}]} data: {"choices":[{"index":0,"delta":{"content":"неделе ставка ОФЗ "}}]} ... data: {"choices":[{"index":0,"finish_reason":"stop"}]} data: [DONE] ``` Это удобно: в UI можно сразу показать «по N источникам» — а текст подгружать по мере прихода. ## OpenAI SDK В официальных SDK OpenAI поле `tools` поддерживается, но проверки типа стандартные `function` — поэтому server-tool придётся передавать через `extra_body` (Python) или приведением типов (TypeScript). **Python:** ```python from openai import OpenAI client = OpenAI( base_url="https://api.hubris.pw/v1", api_key="sk-gw-...", ) resp = client.chat.completions.create( model="openai/gpt-4o-mini", messages=[{"role": "user", "content": "Курс доллара ЦБ на сегодня?"}], extra_body={ "tools": [ {"type": "hubris:web_search", "parameters": {"max_results": 3}} ] }, ) msg = resp.choices[0].message print(msg.content) annotations = msg.model_extra.get("annotations", []) for ann in annotations: cit = ann["url_citation"] print(f" - {cit['title']}: {cit['url']}") ``` **TypeScript:** ```ts import OpenAI from 'openai'; const client = new OpenAI({ baseURL: 'https://api.hubris.pw/v1', apiKey: 'sk-gw-...', }); const resp = await client.chat.completions.create({ model: 'openai/gpt-4o-mini', messages: [{ role: 'user', content: 'Курс доллара ЦБ на сегодня?' }], tools: [ { type: 'hubris:web_search', parameters: { max_results: 3 } }, ], } as any); const msg = resp.choices[0].message; console.log(msg.content); const annotations = (msg as any).annotations ?? []; for (const ann of annotations) { console.log(` - ${ann.url_citation.title}: ${ann.url_citation.url}`); } ``` ## Что важно знать * **`prompt_tokens` будут раздуты.** Результаты поиска инжектятся в промпт — это нормально, что на коротком запросе вернутся 3–10 тысяч prompt-токенов. Ответ модели опирается на эти источники. * **`tool_calls` пустой.** Не пытайтесь обработать веб-поиск как обычный function-call — поиск отрабатывает полностью на нашей стороне. * **Совмещение с обычными функциями работает.** В одном `tools[]` можно передать и server-tool, и свои function-tools — модель выберет, что вызывать. * **`/v1/responses` со стримингом — известное ограничение.** В non-stream ответе `/v1/responses` элемент в `output[]` приходит с типом `hubris:web_search`. В streaming-режиме на этом эндпоинте имя пока не нормализуется обратно — в SSE-событиях `response.output_item.added` / `response.output_item.done` префикс типа отличается от того, что вы отправили в запросе. Будет исправлено. На `/v1/chat/completions` (любой режим) и на non-stream `/v1/responses` всё корректно. ## Биллинг Веб-поиск тарифицируется не только токенами: * **Токены** — обычным образом (prompt + completion). С учётом раздутого prompt после инжекта результатов. * **Плата за поиск** — фиксированная сумма за запрос (зависит от модели и `search_context_size`). В большинстве случаев бо́льшая часть стоимости запроса — это именно плата за поиск, не токены. Итоговая сумма по запросу видна в разделе [«Расходы»](/usage). Списание происходит после успешного ответа. Транзакция атомарна — баланс, usage\_log и движение по балансу пишутся одной БД-транзакцией. ## Что дальше * [Вызов инструментов](/docs/features/tool-calling) — для функций, которые исполняете вы сами. * [Структурированный вывод](/docs/features/structured-output) — заставить ответ соответствовать JSON-схеме (работает вместе с веб-поиском). * [POST /v1/chat/completions](/docs/api/chat-completions) — полная схема. * [Расходы](/usage) — детализация запросов и стоимости. --- # Codex CLI (/docs/frameworks/codex-cli) > Подключить агентский CLI от OpenAI к Hubris через Responses API. [Codex CLI](https://github.com/openai/codex) — это локальный агент для написания и правки кода от OpenAI. По умолчанию он ходит в OpenAI Responses API (`/v1/responses`), а не в Chat Completions, и его можно перенаправить на Hubris через файл конфигурации. ## Установка ```bash npm install -g @openai/codex ``` Минимум — Node.js 20. На Windows работает через WSL2 или обычный PowerShell с глобальным npm. ## Подключение к Hubris Codex CLI читает настройки из `~/.codex/config.toml` (на Windows — `%USERPROFILE%\.codex\config.toml`). Добавьте туда блок с провайдером Hubris и выберите его по умолчанию: ```toml [model_providers.hubris] name = "Hubris" base_url = "https://api.hubris.pw/v1" env_key = "HUBRIS_API_KEY" wire_api = "responses" requires_openai_auth = false model_provider = "hubris" model = "openai/gpt-4o-mini" ``` Что в этих полях: * `base_url` — корень API Hubris вместе с префиксом `/v1`. * `env_key` — имя переменной окружения, из которой Codex CLI возьмёт ваш API-ключ Hubris. Сам ключ держите в переменной, а не в файле: ```bash export HUBRIS_API_KEY="sk-gw-..." # macOS / Linux / WSL ``` ```powershell $env:HUBRIS_API_KEY = "sk-gw-..." # PowerShell ``` * `wire_api = "responses"` — Codex CLI поддерживает только Responses API, поэтому значение тут единственное. * `requires_openai_auth = false` — отключает встроенный вход через ChatGPT (он не нужен, ключ берётся из `HUBRIS_API_KEY`). * `model` — модель из [нашего каталога](/models). Любая активная модель в формате `provider/model` подойдёт. ## Запуск ```bash codex ``` При первом запуске Codex CLI проверит конфигурацию и сделает тестовый запрос. Если ключ корректный — увидите интерактивную сессию. Если нет — ошибка авторизации, проверьте `HUBRIS_API_KEY` и сам ключ на странице [Ключи](https://hubris.pw/keys) в дашборде. ## Выбор модели Codex CLI хорошо работает с любой моделью, у которой стабильный Responses API. Рекомендации по задачам: * **Быстрые правки, рутина** — `openai/gpt-4o-mini`, `anthropic/claude-haiku-4.5`. * **Сложные изменения, рефакторинг** — `openai/gpt-4o`, `anthropic/claude-sonnet-4.5`. * **Когда нужны рассуждения** (планирование больших изменений, поиск багов) — `openai/o1`, `openai/o3-mini`. Сменить модель можно прямо в `config.toml` или флагом `--model`: ```bash codex --model anthropic/claude-sonnet-4.5 ``` Полный список — на странице [Каталог моделей](/models). Цены указаны там же в рублях за 1 миллион токенов. ## Тарификация Codex CLI ничем не отличается от прямых запросов к `/v1/responses` — каждое сообщение списывается с баланса по тарифу выбранной модели. Токены рассуждения (если модель их использует) учитываются в выходных токенах. Историю запросов и затраты смотрите в дашборде на странице [Использование](https://hubris.pw/usage). ## Что дальше * [POST /v1/responses](/docs/api/responses) — описание самого эндпоинта, который дёргает Codex CLI. * [Аутентификация](/docs/authentication) — как создать ключ и где хранить его в проекте. * [Каталог моделей](/models) — какие модели сейчас активны и сколько стоят. --- # LangChain.js (/docs/frameworks/langchain-js) > Подключить Hubris к LangChain.js через ChatOpenAI. LangChain.js работает с Hubris через `ChatOpenAI` из пакета `@langchain/openai`. ## Установка ```bash npm install langchain @langchain/openai @langchain/core ``` ## Подключение ```ts import { ChatOpenAI } from "@langchain/openai"; const llm = new ChatOpenAI({ model: "anthropic/claude-haiku-4.5", apiKey: process.env.HUBRIS_API_KEY, configuration: { baseURL: "https://api.hubris.pw/v1", }, }); ``` **Замечание про `configuration.baseURL`.** В LangChain.js есть несколько способов задать base URL — самый надёжный для стриминга — через объект `configuration` (он передаётся в OpenAI SDK внутри). Не используйте устаревший `basePath` или `apiBaseUrl` — они ломают стриминг в некоторых версиях. ## Базовый вызов ```ts import { HumanMessage } from "@langchain/core/messages"; const response = await llm.invoke([new HumanMessage("Привет")]); console.log(response.content); ``` ## С шаблоном промпта ```ts import { ChatPromptTemplate } from "@langchain/core/prompts"; import { StringOutputParser } from "@langchain/core/output_parsers"; const prompt = ChatPromptTemplate.fromMessages([ ["system", "Вы — лаконичный ассистент."], ["user", "{question}"], ]); const chain = prompt.pipe(llm).pipe(new StringOutputParser()); const result = await chain.invoke({ question: "Что такое pi?" }); console.log(result); ``` ## Стриминг ```ts const stream = await llm.stream([new HumanMessage("Расскажи историю")]); for await (const chunk of stream) { process.stdout.write(chunk.content as string); } ``` ## С tool calling ```ts import { tool } from "@langchain/core/tools"; import { z } from "zod"; const weatherTool = tool( async ({ city }) => ({ city, temp: 5, condition: "sunny" }), { name: "get_weather", description: "Текущая погода в городе", schema: z.object({ city: z.string() }), }, ); const llmWithTools = llm.bindTools([weatherTool]); const response = await llmWithTools.invoke([new HumanMessage("Погода в Москве?")]); console.log(response.tool_calls); ``` ## Что дальше * [Каталог моделей](/models) — все доступные `provider/model`. * [POST /v1/chat/completions](/docs/api/chat-completions) — полная схема параметров. --- # LangChain (Python) (/docs/frameworks/langchain-python) > Подключить Hubris к LangChain через ChatOpenAI. LangChain работает с Hubris через стандартный класс `ChatOpenAI` из пакета `langchain-openai` — задаёте `base_url` на наш endpoint. ## Установка ```bash pip install langchain langchain-openai ``` ## Подключение ```python from langchain_openai import ChatOpenAI llm = ChatOpenAI( model="anthropic/claude-haiku-4.5", base_url="https://api.hubris.pw/v1", api_key="sk-gw-...", ) ``` ## Базовый вызов ```python from langchain_core.messages import HumanMessage response = llm.invoke([HumanMessage(content="Привет")]) print(response.content) ``` ## С системным промптом и chain ```python from langchain_core.prompts import ChatPromptTemplate from langchain_core.output_parsers import StrOutputParser prompt = ChatPromptTemplate.from_messages([ ("system", "Вы — лаконичный ассистент."), ("user", "{question}"), ]) chain = prompt | llm | StrOutputParser() result = chain.invoke({"question": "Что такое pi с точностью до 5 знаков?"}) print(result) ``` ## Стриминг ```python for chunk in llm.stream([HumanMessage(content="Расскажи историю")]): print(chunk.content, end="", flush=True) ``` ## С tool calling ```python from langchain_core.tools import tool @tool def get_weather(city: str) -> dict: """Текущая погода в городе.""" return {"city": city, "temp": 5, "condition": "sunny"} llm_with_tools = llm.bind_tools([get_weather]) response = llm_with_tools.invoke([HumanMessage(content="Какая погода в Москве?")]) print(response.tool_calls) ``` ## С агентами (LangGraph) `ChatOpenAI` напрямую совместим с `langgraph` агентами — указываете тот же клиент в `create_react_agent`: ```python from langgraph.prebuilt import create_react_agent agent = create_react_agent(llm, tools=[get_weather]) result = agent.invoke({"messages": [HumanMessage(content="Какая погода в Москве?")]}) ``` ## Что дальше * [Каталог моделей](/models) — все доступные `provider/model`. * [POST /v1/chat/completions](/docs/api/chat-completions) — полная схема. --- # OpenAI SDK для Node.js (/docs/frameworks/openai-node) > Использовать официальный openai пакет с Hubris — поменяйте baseURL и apiKey. Официальный пакет `openai` от OpenAI работает с Hubris как прямая замена — меняете два параметра при инициализации клиента, остальной код не трогаете. ## Установка ```bash npm install openai # или pnpm add openai # или yarn add openai ``` ## Подключение ```ts import OpenAI from "openai"; const client = new OpenAI({ baseURL: "https://api.hubris.pw/v1", apiKey: process.env.HUBRIS_API_KEY, }); ``` ## Базовый запрос ```ts const response = await client.chat.completions.create({ model: "anthropic/claude-haiku-4.5", messages: [{ role: "user", content: "Привет" }], }); console.log(response.choices[0].message.content); ``` ## Стриминг ```ts const stream = await client.chat.completions.create({ model: "anthropic/claude-haiku-4.5", messages: [{ role: "user", content: "Расскажи историю" }], stream: true, }); for await (const chunk of stream) { const delta = chunk.choices[0]?.delta?.content; if (delta) process.stdout.write(delta); } ``` ## Tool calling ```ts const tools = [{ type: "function" as const, function: { name: "get_weather", description: "Текущая погода в городе", parameters: { type: "object", properties: { city: { type: "string" } }, required: ["city"], }, }, }]; const response = await client.chat.completions.create({ model: "anthropic/claude-haiku-4.5", messages: [{ role: "user", content: "Какая погода в Москве?" }], tools, }); console.log(response.choices[0].message.tool_calls); ``` ## Что не работает `client.embeddings.create()`, `client.images.generate()`, `client.audio.*`, `client.files.*`, `client.batches.*` — соответствующих эндпоинтов нет, появятся в ближайших обновлениях. ## Что дальше * [Каталог моделей](/models) — все идентификаторы. * [POST /v1/chat/completions](/docs/api/chat-completions) — полная схема. --- # OpenAI SDK для Python (/docs/frameworks/openai-python) > Использовать официальный openai пакет с Hubris — поменяйте base_url и api_key. Официальный пакет `openai` от OpenAI работает с Hubris как прямая замена: меняете два параметра при инициализации клиента, остальной код не трогаете. ## Установка ```bash pip install openai ``` Минимальная версия — 1.0+ (новый API клиента). Старый stub-стиль (`openai.api_key = ...`) не поддерживаем. ## Подключение ```python from openai import OpenAI client = OpenAI( base_url="https://api.hubris.pw/v1", api_key="sk-gw-...", ) ``` Лучше через переменную окружения: ```python import os from openai import OpenAI client = OpenAI( base_url="https://api.hubris.pw/v1", api_key=os.environ["HUBRIS_API_KEY"], ) ``` ## Базовый запрос ```python response = client.chat.completions.create( model="anthropic/claude-haiku-4.5", messages=[{"role": "user", "content": "Привет"}], ) print(response.choices[0].message.content) ``` ## Стриминг ```python stream = client.chat.completions.create( model="anthropic/claude-haiku-4.5", messages=[{"role": "user", "content": "Расскажи историю"}], stream=True, ) for chunk in stream: delta = chunk.choices[0].delta.content if delta: print(delta, end="", flush=True) ``` ## Tool calling Обычный OpenAI-API: ```python tools = [{ "type": "function", "function": { "name": "get_weather", "description": "Текущая погода в городе", "parameters": { "type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"], }, }, }] response = client.chat.completions.create( model="anthropic/claude-haiku-4.5", messages=[{"role": "user", "content": "Какая погода в Москве?"}], tools=tools, ) print(response.choices[0].message.tool_calls) ``` ## Async-клиент ```python from openai import AsyncOpenAI client = AsyncOpenAI( base_url="https://api.hubris.pw/v1", api_key="sk-gw-...", ) async def main(): response = await client.chat.completions.create( model="anthropic/claude-haiku-4.5", messages=[{"role": "user", "content": "Привет"}], ) print(response.choices[0].message.content) ``` ## Что не работает `client.embeddings.create()`, `client.images.generate()`, `client.audio.*`, `client.files.*`, `client.batches.*` — соответствующих эндпоинтов в Hubris сейчас нет, эти вызовы упадут с 404. Появятся в ближайших обновлениях. ## Что дальше * [Каталог моделей](/models) — все доступные `provider/model` идентификаторы. * [POST /v1/chat/completions](/docs/api/chat-completions) — полная схема параметров. * [Аутентификация](/docs/authentication) — про ключи. --- # Vercel AI SDK (/docs/frameworks/vercel-ai-sdk) > Подключить Hubris к Vercel AI SDK через @ai-sdk/openai-compatible. Vercel AI SDK подключается к Hubris через провайдер `@ai-sdk/openai-compatible` — он принимает любой OpenAI-совместимый endpoint. ## Установка ```bash npm install ai @ai-sdk/openai-compatible ``` ## Подключение ```ts import { createOpenAICompatible } from "@ai-sdk/openai-compatible"; const hubris = createOpenAICompatible({ name: "hubris", baseURL: "https://api.hubris.pw/v1", apiKey: process.env.HUBRIS_API_KEY, }); ``` ## generateText (одиночный запрос) ```ts import { generateText } from "ai"; const { text } = await generateText({ model: hubris.chatModel("anthropic/claude-haiku-4.5"), prompt: "Привет, как дела?", }); console.log(text); ``` ## streamText (стриминг) ```ts import { streamText } from "ai"; const { textStream } = await streamText({ model: hubris.chatModel("anthropic/claude-haiku-4.5"), prompt: "Расскажи короткую историю", }); for await (const chunk of textStream) { process.stdout.write(chunk); } ``` ## С tool calling ```ts import { generateText, tool } from "ai"; import { z } from "zod"; const result = await generateText({ model: hubris.chatModel("anthropic/claude-haiku-4.5"), prompt: "Какая погода в Москве?", tools: { getWeather: tool({ description: "Текущая погода в городе", parameters: z.object({ city: z.string() }), execute: async ({ city }) => ({ city, temp: 5, condition: "sunny" }), }), }, }); ``` ## Использование в Next.js Route Handler ```ts // app/api/chat/route.ts import { streamText } from "ai"; import { createOpenAICompatible } from "@ai-sdk/openai-compatible"; const hubris = createOpenAICompatible({ name: "hubris", baseURL: "https://api.hubris.pw/v1", apiKey: process.env.HUBRIS_API_KEY, }); export async function POST(req: Request) { const { messages } = await req.json(); const result = await streamText({ model: hubris.chatModel("anthropic/claude-haiku-4.5"), messages, }); return result.toDataStreamResponse(); } ``` ## Что дальше * [Каталог моделей](/models) — все идентификаторы. * [POST /v1/chat/completions](/docs/api/chat-completions) — полная схема параметров. --- # Claude Code (/docs/integrations/claude-code) > Подключение официального CLI-агента Claude Code к Hubris через эндпоинт /v1/messages — поддержка Sonnet, Opus, Haiku, кэширования, vision, статусной строки. [Claude Code](https://docs.anthropic.com/claude/docs/claude-code) — официальный CLI-агент Anthropic. Запускается в терминале, понимает контекст проекта, умеет редактировать файлы, исполнять команды, читать скриншоты, держит долгую сессию с кэшированием промптов. Поддерживает любой Anthropic-совместимый бэкенд через переменные окружения. Hubris подходит из коробки. ## Что вы получите * Доступ ко всему семейству Claude (Sonnet 4.5/4.6, Opus 4.5/4.6/4.7, Haiku 4.5) **с оплатой в рублях** и без VPN. * Нативное `prompt caching` для длинных сессий — экономит до десятков рублей на каждом турне. * Чтение скриншотов и других изображений (vision). * Использование инструментов (tool use) для редактирования файлов и запуска команд. * Опциональная statusline в TUI с балансом, расходом сессии и текущей моделью. ## Требования * macOS / Linux / WSL / современный терминал на Windows. * Аккаунт на [hubris.pw](https://hubris.pw/dashboard) и API-ключ формата `sk-gw-…` (создать на [/keys](https://hubris.pw/keys)). * Положительный баланс — пополнить можно [здесь](https://hubris.pw/billing). ## Установка Claude Code Подробная инструкция — в [официальной документации](https://docs.anthropic.com/claude/docs/claude-code-quickstart). Самый быстрый путь: ```bash npm install -g @anthropic-ai/claude-code ``` Альтернатива — однострочный установщик: ```bash curl -fsSL https://claude.ai/install.sh | bash ``` Убедитесь, что Claude Code запускается: ```bash claude --version ``` ## Подключение к Hubris Достаточно двух переменных окружения. Поставьте их перед запуском Claude Code: ```bash export ANTHROPIC_BASE_URL="https://api.hubris.pw" export ANTHROPIC_AUTH_TOKEN="sk-gw-..." # опционально — пустая ANTHROPIC_API_KEY гарантирует, что SDK не подставит # случайно настоящий ключ Anthropic, если он есть в среде: unset ANTHROPIC_API_KEY ``` Чтобы не вводить руками каждый раз — добавьте в `~/.bashrc` / `~/.zshrc` / `~/.config/fish/config.fish`. Под Windows — Set-`HUBRIS_API_KEY` в профиле PowerShell. Запустите агента из папки проекта: ```bash cd ~/projects/my-project claude ``` В первом турне Claude Code сходит к Hubris вместо `api.anthropic.com`, выберет указанную модель и спишет стоимость с вашего баланса в рублях. ## Выбор модели В рабочей сессии переключайте модель командой `/model`. Hubris принимает и короткие Anthropic-имена (`claude-sonnet-4-5`, `claude-haiku-4-5`), и каноничные (`anthropic/claude-sonnet-4.6`). Полный список — в [каталоге](https://hubris.pw/models?provider=anthropic). Чтобы зафиксировать модель по умолчанию, передайте её при запуске: ```bash claude --model claude-sonnet-4-5 ``` Или укажите в `~/.claude/settings.json`: ```json { "defaultModel": "claude-sonnet-4-5" } ``` ### Какая модель когда подходит | Модель | Когда брать | | ----------------------------------------- | ----------------------------------------------------------------------------- | | `claude-haiku-4-5` | быстрые ответы, поиск по коду, простые правки, дёшево. | | `claude-sonnet-4-5` / `claude-sonnet-4-6` | основной рабочий вариант: качественные правки, рефакторинги, длинные сессии. | | `claude-opus-4-7` | сложные архитектурные задачи, миграции, разбор багов в больших кодовых базах. | ## Statusline с балансом В нижней строке TUI Claude Code можно выводить текущий баланс Hubris, расход сессии и активную модель. Скрипт обновляется на каждом турне и не блокирует ответ модели. ### Установка (Linux / macOS / WSL) ```bash curl -fsSL https://hubris.pw/scripts/claude-statusline.sh \ -o ~/.claude/hubris-statusline.sh chmod +x ~/.claude/hubris-statusline.sh ``` Добавьте в `~/.claude/settings.json`: ```json { "statusLine": { "type": "command", "command": "~/.claude/hubris-statusline.sh" } } ``` ### Установка (Windows / кросс-платформа на Node.js) ```powershell Invoke-WebRequest -Uri https://hubris.pw/scripts/claude-statusline.mjs ` -OutFile $env:USERPROFILE\.claude\hubris-statusline.mjs ``` В `~/.claude/settings.json`: ```json { "statusLine": { "type": "command", "command": "node ~/.claude/hubris-statusline.mjs" } } ``` Скрипт читает `ANTHROPIC_AUTH_TOKEN` из среды (тот же ключ, что использует CC) и обращается к [/v1/usage](/docs/api/usage) за расходом и балансом. Запросы на статус — бесплатны (не списываются с баланса). Пример вывода: ``` hubris │ Sonnet 4.5 │ session 0,42 ₽ · today 12,18 ₽ · balance 1 287 ₽ ``` ## Прочая конфигурация | Переменная окружения | Что делает | | ---------------------------- | ------------------------------------------------------------------------------------------------------------ | | `ANTHROPIC_BASE_URL` | URL шлюза. Для Hubris — `https://api.hubris.pw`. | | `ANTHROPIC_AUTH_TOKEN` | API-ключ `sk-gw-…`. Используется как `Authorization: Bearer`. | | `ANTHROPIC_MODEL` | модель по умолчанию (если не задана в settings.json). | | `ANTHROPIC_SMALL_FAST_MODEL` | модель для лёгких подзадач (резюмирования, выбор имени). По умолчанию Haiku — оставляйте `claude-haiku-4-5`. | ## Дневные лимиты Если на ключе включён дневной лимит трат (можно поставить на [/keys](https://hubris.pw/keys)), Hubris вернёт `429 rate_limit_error`. Claude Code отобразит ошибку в TUI и предложит остановить сессию. Лимит обнуляется ежедневно в 00:00 UTC. ## Кэширование промптов Claude Code сам управляет `cache_control` для длинных системных промптов и расшаренного контекста. Hubris передаёт это поле в Anthropic без изменений и **списывает по фактической стоимости** с учётом cache hit (0,1× от обычной цены) или cache write (1,25×). Подробности — в [API-референсе /v1/messages](/docs/api/messages). ## Часто задаваемые вопросы **Можно ли использовать через настоящий ключ Anthropic параллельно?** Да: переменные `ANTHROPIC_*` локальные. Достаточно открыть второй терминал без них — Claude Code пойдёт напрямую. **Где увидеть детальный расход?** Все запросы появляются на [странице Использование](https://hubris.pw/usage) с разбивкой по моделям и времени. Через API — [GET /v1/usage](/docs/api/usage). **Поддерживается ли Privacy Mode для маскирования PII?** В первой версии — нет. Если нужно маскирование, делайте запросы через [/v1/chat/completions](/docs/api/chat-completions) с заголовком `X-Hubris-Privacy-Mask`. Поддержка на `/v1/messages` появится позже. **Что делать, если CC просит модель, которой нет в каталоге?** Hubris вернёт `404 not_found_error` с именем недоступной модели. Сверьтесь со [списком активных моделей](https://hubris.pw/models?provider=anthropic) и переключитесь командой `/model` на одну из них. ## Что дальше * [POST /v1/messages](/docs/api/messages) — полный API-референс по эндпоинту. * [Каталог моделей](https://hubris.pw/models?provider=anthropic) — активные Claude-модели и цены в рублях. * [Использование](https://hubris.pw/usage) — детальный расход по запросам. * [Биллинг](https://hubris.pw/billing) — пополнение баланса через СБП. --- # Cline (VS Code / Cursor) (/docs/integrations/cline) > Подключение AI-ассистента Cline к Hubris — кодит, читает и пишет файлы, запускает терминал из вашего редактора. [Cline](https://cline.bot) — расширение для VS Code и Cursor, которое превращает редактор в полноценного AI-агента: умеет читать и редактировать файлы, запускать команды в терминале, работать с git, выполнять многошаговые задачи. Подключается к любому OpenAI-совместимому провайдеру — в том числе к Hubris. ## Требования * VS Code 1.85+ или Cursor * Установленный аккаунт на [hubris.pw](https://hubris.pw/dashboard) и API-ключ ## Установка Cline 1. Откройте панель расширений в VS Code (`Ctrl+Shift+X`). 2. Найдите **Cline** (издатель — saoudrizwan). 3. Нажмите **Install**. 4. После установки в боковой панели появится иконка Cline. ## Подключение к Hubris В панели Cline нажмите на селектор модели под полем ввода. Откроется список API-провайдеров. 1. Выберите **OpenAI Compatible**. 2. Заполните поля: | Поле | Значение | | ------------ | ------------------------------------------------------ | | **Base URL** | `https://api.hubris.pw/v1` | | **API Key** | ваш ключ из [личного кабинета](https://hubris.pw/keys) | | **Model ID** | например `anthropic/claude-haiku-4.5` | 3. Сохраните настройки. Cline готов к работе — можно сразу писать задачу. ## Рекомендуемые модели Cline — это многошаговый агент: длинный контекст, много вызовов инструментов, чтение/запись файлов. Под такой профиль подходят: | Назначение | Модель | | ----------------------------------------- | --------------------------------------------------- | | Сложные многошаговые задачи, рефакторинги | Claude Opus (`anthropic/claude-opus-*` из каталога) | | Основная рабочая лошадка для кодинга | Claude Sonnet (`anthropic/claude-sonnet-*`) | | Быстрые правки, экономичный режим | `anthropic/claude-haiku-4.5` | | Универсальная альтернатива | `openai/gpt-4o-mini` | Полный актуальный список — в [каталоге моделей](/models). Подойдёт любая модель с поддержкой tool calling (фильтр в каталоге). ## Стоимость и контроль расходов Cline — агент, а значит делает много вызовов на одну задачу: чтение файлов, изменения, проверка результата. Расход токенов на одну сессию может оказаться выше, чем ожидаешь. Что советуем: * **Дневной лимит на ключ** — задайте лимит расхода в [настройках ключа](/keys). При превышении ответы вернутся с `429 daily_limit_exceeded`, баланс не пострадает. * **Раздельные ключи** — отдельный ключ для каждого проекта или клиента, чтобы видеть расходы в [«Расходах»](/usage) разнесённо. * **Дешёвая модель для рутины** — Haiku или GPT-4o-mini для типовых правок, переключаться на Opus только когда задача реально сложная. ## Решение проблем ### Cline не подключается, ошибка 401 * Проверьте, что в поле **API Key** именно ключ Hubris (формат `sk-gw-` + hex). * Если копировали из браузера — убедитесь, что не попали пробелы по краям. * В случае сомнений — создайте новый ключ в [личном кабинете](/keys). ### `model not found` при попытке генерации * Проверьте написание Model ID. Формат — `provider/model-name`, например `anthropic/claude-haiku-4.5`. * Убедитесь, что модель есть в нашем [каталоге](/models) — Hubris проксирует не все модели всех провайдеров. ### Cline работает, но «теряет» инструменты или зацикливается * Это типичная история со слабыми моделями на сложных tool-сценариях. Попробуйте более сильную модель — Claude Sonnet или Opus. * Уменьшите размер задачи: дайте Cline более узкую цель за раз. ### Сильно вырос баланс — почему? * Каждое действие Cline (открыть файл, прочитать, изменить, проверить) — отдельный запрос с собственным `prompt_tokens` и `completion_tokens`. Длинные файлы → большой prompt. * Откройте [«Расходы»](/usage) и отсортируйте по дате — будут видны все запросы за сессию. ## Что дальше * [Каталог моделей](/models) — выбрать подходящую модель. * [Управление API-ключами](/keys) — создать отдельный ключ и задать дневной лимит. * [Расходы](/usage) — детализация всех запросов и потраченных рублей. * [Вызов инструментов](/docs/features/tool-calling) — как работают tool calls под капотом. --- # Dify (/docs/integrations/dify) > Подключение Hubris к Dify — низкокодовой LLM-платформе для построения чат-приложений, агентов и workflow. [Dify](https://dify.ai) — open-source платформа для построения LLM-приложений: чат-боты, AI-агенты, RAG-workflow. Поддерживает self-hosted и облачный (`cloud.dify.ai`) режимы. Hubris подключается как провайдер типа OpenAI-API-compatible — модели Hubris становятся доступны во всех приложениях. ## Требования * Аккаунт на [cloud.dify.ai](https://cloud.dify.ai) **или** self-hosted Dify * Аккаунт на [hubris.pw](https://hubris.pw/dashboard) и API-ключ ## Подключение 1. В Dify откройте **Settings** → **Model Providers**. 2. Найдите в списке **OpenAI-API-compatible** и нажмите **Add Model**. 3. Заполните поля: | Поле | Значение | | ---------------------- | ------------------------------------------------------------------- | | **Model Type** | `LLM` (для chat-моделей) | | **Model Name** | ID модели из нашего каталога, например `anthropic/claude-haiku-4.5` | | **API Key** | ваш ключ из [личного кабинета Hubris](https://hubris.pw/keys) | | **API endpoint URL** | `https://api.hubris.pw/v1` | | **Completion mode** | `Chat` | | **Model context size** | размер контекста модели (см. карточку в [каталоге](/models)) | | **Maximum chunks** | 1 | 4. Сохраните. Модель появится в списке доступных в Dify. Чтобы добавить ещё модели — повторите для каждой. Один ключ Hubris работает на все. ## Использование в приложениях После подключения модель Hubris доступна везде, где Dify предлагает выбор LLM: * **AI Chatflow** — собранный из блоков чат-агент. * **LLM-блок в Workflow** — точка вызова модели в произвольном workflow. * **Agent app** — приложение с инструментами и многошаговыми сценариями. * **Knowledge / RAG** — встраивание модели в retrieval-цепочки. В каждом блоке выбираете подключённого провайдера (`OpenAI-API-compatible`) и нужную модель. ## Рекомендуемые модели | ID | Когда подходит | | ---------------------------- | ---------------------------------------------------------- | | `anthropic/claude-haiku-4.5` | быстрые чат-боты, классификация | | `openai/gpt-4o-mini` | универсальный недорогой выбор, поддержка structured output | | `google/gemini-2.0-flash` | мультимодальные приложения | Для агентов и сложных reasoning-задач — Claude Sonnet/Opus из [каталога](/models). Используйте фильтр «tool calling» для агентских сценариев. ## Streaming и Function Calling Dify работает с обоими по умолчанию — `stream: true` идёт в наш `/v1/chat/completions`, tools прокидываются без дополнительной настройки. Никаких отдельных флагов в Dify включать не надо. ## Embeddings для RAG Для retrieval-сценариев в Dify нужен отдельный provider типа **Text Embedding**. Добавьте ещё одну запись OpenAI-API-compatible, но с **Model Type** = `Text Embedding`, указав модель-эмбеддер из нашего [каталога](/models). API endpoint и ключ — те же, что и для LLM-провайдера. ## Решение проблем ### Provider добавлен, но Dify не видит модели * Перепроверьте API endpoint URL — должен быть `https://api.hubris.pw/v1` (с `/v1` в конце, без trailing slash). * Убедитесь, что Model Name точно совпадает с ID из каталога Hubris. ### `Invalid API key` при тестовом запросе * API-ключ должен начинаться с `sk-gw-` и не содержать пробелов. ### Модель работает, но контекст «обрывается» / ответы короткие * Проверьте поле **Model context size** — оно должно соответствовать реальному контексту модели (карточка в каталоге). * Поле **Maximum chunks** относится к чанкам контекста в RAG — оставьте `1` для обычных чат-сценариев. ### Streaming не работает в Workflow * В настройках LLM-блока должно быть включено **Stream Mode**. Если выключено — ответ приходит одним куском в конце. ## Что дальше * [Каталог моделей](/models) — IDs для подключения в Dify. * [Управление API-ключами](/keys) — отдельный ключ под Dify-приложение. * [Расходы](/usage) — детализация по запросам. * [Структурированный вывод](/docs/features/structured-output) — стабильные JSON-ответы в workflow. --- # Hermes Agent (/docs/integrations/hermes-agent) > Подключение Hermes Agent от Nous Research — самообучающегося AI-агента с Telegram, памятью и веб-дашбордом — к Hubris. [Hermes Agent](https://github.com/NousResearch/hermes-agent) — открытый AI-агент от Nous Research. Работает с файлами и терминалом, сам формирует «навыки» из опыта, хранит память между сессиями, поддерживает чат через Telegram. Подключается к любому OpenAI-совместимому провайдеру. ## Требования * Linux / macOS / WSL2 / Android (Termux) * Python 3.11+ * Аккаунт на [hubris.pw](https://hubris.pw/dashboard) и API-ключ ## Установка ```bash curl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh | bash ``` Перезагрузите shell: ```bash source ~/.bashrc # или source ~/.zshrc ``` ## Подключение к Hubris ### Через мастер настройки (рекомендуется) ```bash hermes setup ``` В меню выберите **Custom Endpoint** и заполните: | Поле | Значение | | ------------------ | ------------------------------------------------------ | | **Base URL** | `https://api.hubris.pw/v1` | | **API Key** | ваш ключ из [личного кабинета](https://hubris.pw/keys) | | **Model** | например `anthropic/claude-haiku-4.5` | | **Context window** | размер контекста модели (см. [каталог](/models)) | Остальные параметры можно оставить по умолчанию. ### Через файлы конфигурации `~/.hermes/.env`: ```bash OPENAI_API_KEY=sk-gw-... OPENAI_BASE_URL=https://api.hubris.pw/v1 ``` `~/.hermes/config.yaml`: ```yaml model: default: "anthropic/claude-haiku-4.5" provider: "custom" base_url: "https://api.hubris.pw/v1" ``` ## Запуск ```bash hermes ``` Откроется интерактивный терминал. Можно сразу писать задачу. ## Веб-дашборд Hermes поставляется со встроенным веб-интерфейсом для управления сессиями, памятью и навыками: ```bash hermes dashboard ``` По умолчанию открывается на `http://localhost:9119`. **Не открывайте на публичном IP без защиты** — дашборд хранит API-ключи. Для удалённого доступа используйте SSH-туннель: ```bash ssh -L 9119:localhost:9119 user@ваш-сервер ``` ## Подключение Telegram-бота Hermes умеет принимать команды через Telegram. 1. У [@BotFather](https://t.me/BotFather) создайте бота (`/newbot`), скопируйте токен. 2. У [@userinfobot](https://t.me/userinfobot) узнайте свой Telegram ID. 3. Добавьте в `~/.hermes/.env`: ```bash TELEGRAM_BOT_TOKEN=ваш_токен_от_BotFather TELEGRAM_ALLOWED_USERS=ваш_telegram_id ``` 4. Запустите gateway: ```bash hermes gateway start ``` Теперь бот отвечает в Telegram через выбранную модель Hubris. ## Рекомендуемые модели Hermes — агент с длинной сессией и многократными вызовами инструментов. Подойдут модели с хорошим tool calling: | ID | Когда уместна | | ---------------------------- | ------------------------------------------ | | `anthropic/claude-haiku-4.5` | быстрые типовые задачи | | `openai/gpt-4o-mini` | универсальный выбор | | `google/gemini-2.0-flash` | мультимодальные сценарии, большой контекст | Для сложных автономных задач (рефакторинг, длинные сессии) — Claude Sonnet/Opus из [каталога](/models). Фильтр «tool calling» поможет отобрать подходящие. ## Решение проблем ### `hermes: command not found` после установки ```bash source ~/.bashrc # или ~/.zshrc ``` Проверьте, что `~/.local/bin` в PATH: `echo $PATH`. ### `Invalid API key` * Перепроверьте ключ в `~/.hermes/.env` — без пробелов, начинается с `sk-gw-`. ### Бот в Telegram не отвечает * Проверьте gateway: `hermes gateway status`. * Убедитесь, что Telegram ID правильно указан в `TELEGRAM_ALLOWED_USERS`, без пробелов и комментариев в той же строке. ### `No messaging platforms enabled` в логах * Проверьте, что `TELEGRAM_BOT_TOKEN` указан без inline-комментариев в .env (`TOKEN=xyz # comment` ломает парсинг — комментарий выносите на отдельную строку). ## Что дальше * [Каталог моделей](/models) — выбор по поддержке tool calling. * [Управление API-ключами](/keys) — отдельный ключ под Hermes. * [Расходы](/usage) — детализация сессий. * [OpenClaw](/docs/integrations/openclaw) — альтернативный multi-messenger агент. --- # Kilo Code (VS Code / PyCharm) (/docs/integrations/kilo-code) > Подключение Kilo Code — AI-агента для VS Code и PyCharm — к Hubris. [Kilo Code](https://kilocode.ai) — AI-агент для разработчиков, доступный и в VS Code, и в PyCharm. Автономно выполняет многошаговые задачи: пишет, рефакторит, отлаживает, добавляет тесты, при необходимости открывает браузер для проверки результата. ## Требования * VS Code 1.85+ **или** PyCharm 2024.1+ * Аккаунт на [hubris.pw](https://hubris.pw/dashboard) и API-ключ ## Установка и подключение в VS Code 1. Откройте панель расширений (`Ctrl+Shift+X`), найдите **Kilo Code**, нажмите **Install**. 2. В боковой панели появится иконка Kilo — откройте её → **Settings** (шестерёнка) → вкладка **Providers**. 3. Нажмите **Custom provider** → **Connect**. 4. Заполните поля: | Поле | Значение | | --------------- | ------------------------------------------------------ | | **Provider ID** | `hubris` (или любое имя — это локальный идентификатор) | | **Base URL** | `https://api.hubris.pw/v1` | | **API Key** | ваш ключ из [личного кабинета](https://hubris.pw/keys) | 5. Сохраните настройки. Kilo автоматически подтянет список моделей. ## Установка и подключение в PyCharm 1. Откройте **Settings** → **Plugins** → **Marketplace**, найдите **Kilo Code**, установите. Перезапустите IDE. 2. В правой панели появится иконка Kilo — откройте, нажмите **Settings**. 3. В разделе **Configuration Profile** создайте новый профиль. 4. В поле **API Provider** выберите **OpenAI Compatible**. 5. Заполните **Base URL** = `https://api.hubris.pw/v1` и **API Key**. Список моделей подтянется автоматически. ## Рекомендуемые модели Kilo автономно делает много шагов (план → код → тест → правка), поэтому имеет смысл выбирать модели с поддержкой tool calling и нормальным reasoning. | ID | Когда подходит | | ---------------------------- | ------------------------------------ | | `anthropic/claude-haiku-4.5` | быстрая работа над типовыми правками | | `openai/gpt-4o-mini` | универсальный недорогой выбор | | `google/gemini-2.0-flash` | мультимодальные сценарии | Для тяжёлых рефакторингов и многошаговых задач — берите Claude Opus/Sonnet из [каталога](/models). Фильтр «tool calling» поможет отсеять модели, не поддерживающие агентский сценарий. ## Контроль расходов * Заведите [отдельный ключ](/keys) под Kilo, поставьте суточный лимит — на случай если автономный агент уйдёт «копать» дольше ожидаемого. * Для рутины используйте недорогую модель, на тяжёлые задачи переключайте Opus. * Расход по сессиям виден в [«Расходах»](/usage) — фильтруйте по ключу. ## Решение проблем ### Список моделей пустой / Kilo не подтягивает каталог * Это обычно про невалидный API-ключ или неправильный Base URL. Перепроверьте оба. * В VS Code иногда нужно перезагрузить окно: `Ctrl+Shift+P` → **Developer: Reload Window**. * В PyCharm — закройте и заново откройте панель Kilo. ### 401 при первом запросе * Ключ должен быть в формате `sk-gw-` + hex, без пробелов и переводов строк. ### Kilo не справляется с задачей / зацикливается * Слабая модель плохо ведёт себя как агент. Попробуйте более сильную из каталога с явной поддержкой tool calling. * Уменьшите размер задачи: дайте Kilo более узкую цель. ## Что дальше * [Каталог моделей](/models) — выбор по поддержке tool calling. * [Управление API-ключами](/keys) — отдельный ключ под Kilo с дневным лимитом. * [Расходы](/usage) — детализация сессий. * [Cline](/docs/integrations/cline), [Roo Code](/docs/integrations/roo-code) — альтернативные VS Code агенты. --- # MCP-сервер (/docs/integrations/mcp) > Подключите Hubris к AI-агентам через Model Context Protocol — без хардкода API. > **Beta.** Фича стабильна, но набор инструментов и формат ответов могут уточняться по мере фидбэка. Model Context Protocol — открытый стандарт от Anthropic. Через MCP агенты (Claude Desktop, Claude Code, Cline, Cursor) получают доступ к внешним сервисам единообразным способом: каталог моделей, баланс, отправка запросов в LLM — всё через одно подключение. Hubris-MCP даёт агенту: * **Каталог моделей** с ценой в рублях, фильтрами по capability, цене и длине контекста. * **Баланс аккаунта** — чтобы агент знал, сколько ему доступно. * **Запросы к LLM** через `chat_complete` (полный паритет с `/v1/chat/completions`). * **Готовый prompt** `compare-models` для сравнения моделей под конкретную задачу. ## Что такое MCP MCP позволяет AI-агентам вызывать инструменты и читать ресурсы внешних сервисов без необходимости хранить API-ключ напрямую в промпте или прописывать кастомные интеграции. Агент узнаёт о доступных инструментах автоматически при подключении к серверу. Hubris реализует MCP через **Streamable HTTP transport** — единственный URL без дополнительной инфраструктуры. ## Требования * Аккаунт на [hubris.pw](https://hubris.pw/dashboard) и API-ключ формата `sk-gw-…` (создать на [/keys](https://hubris.pw/keys)). * Положительный баланс — пополнить можно [здесь](https://hubris.pw/billing). * MCP-клиент: Claude Desktop, Claude Code, Cline, Cursor, любой другой совместимый. * Для Claude Desktop дополнительно — установленный Node.js (нужен `npx` для запуска шима `mcp-remote`). ## Подключение ### Claude Desktop > Claude Desktop пока не умеет напрямую подключаться к удалённым HTTP-MCP-серверам — он работает только со stdio-серверами. Для подключения к Hubris нужен локальный шим [`mcp-remote`](https://github.com/geelen/mcp-remote): он стартует как stdio-процесс и проксирует все вызовы на `https://api.hubris.pw/mcp`. > **Не используйте окно «Add custom connector (BETA)»** в claude.ai / Claude Desktop. Эта форма рассчитана на сервера с OAuth 2.1 (у Hubris пока Bearer-авторизация), и для произвольных HTTP-MCP-серверов известно падает с ошибкой `Couldn't reach the MCP server. … ofid_…` ещё до отправки запроса нам — это [баг на стороне Anthropic](https://github.com/anthropics/claude-ai-mcp/issues). Подключайтесь только через JSON-конфиг ниже. Откройте файл конфигурации: * **macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json` * **Windows:** `%APPDATA%\Claude\claude_desktop_config.json` * **Linux:** `~/.config/Claude/claude_desktop_config.json` Добавьте секцию `mcpServers`: ```json { "mcpServers": { "hubris": { "command": "npx", "args": [ "-y", "mcp-remote", "https://api.hubris.pw/mcp", "--transport", "http-only", "--header", "Authorization:${AUTH_TOKEN}" ], "env": { "AUTH_TOKEN": "Bearer sk-gw-..." } } } } ``` Подставьте свой ключ в `AUTH_TOKEN` (формат: `Bearer sk-gw-…`). Заголовок в `--header` передаётся как `Authorization:${AUTH_TOKEN}` без пробела вокруг двоеточия — это рекомендация `mcp-remote` из-за особенностей экранирования в некоторых клиентах. Перезапустите Claude Desktop. В чате появится индикатор подключённого MCP-сервера и список доступных инструментов. ### Claude Code ```bash claude mcp add --transport http hubris https://api.hubris.pw/mcp \ --header "Authorization: Bearer sk-gw-..." ``` Проверьте, что сервер добавлен: ```bash claude mcp list ``` ### Cline / Cursor / другие клиенты Любой MCP-клиент с поддержкой Streamable HTTP transport подключается тем же URL и Bearer-ключом. Параметры те же: | Поле | Значение | | --------------------- | ------------------------------- | | Transport | Streamable HTTP | | URL | `https://api.hubris.pw/mcp` | | Заголовок авторизации | `Authorization: Bearer sk-gw-…` | Подробности — в документации вашего клиента. ## Инструменты | Инструмент | Что делает | | -------------------- | ----------------------------------------------------------------------------------------------------------- | | `models_list` | Полный каталог активных моделей с курсорной пагинацией. | | `models_search` | Поиск по capability (vision / reasoning / tools / web\_search / audio / image\_gen), цене, длине контекста. | | `models_get_pricing` | Тариф конкретной модели в рублях. | | `balance_get` | Текущий баланс аккаунта. | | `chat_complete` | LLM-запрос к выбранной модели. Полный паритет с `/v1/chat/completions`. | ## Ресурсы | URI | Содержимое | | -------------------------- | -------------------------------------- | | `hubris://catalog/models` | Каталог моделей одним JSON-документом. | | `hubris://docs/quickstart` | Главная страница быстрого старта. | ## Безопасность Bearer-ключ открывает доступ к балансу аккаунта. Любой, кто получит ключ, сможет отправлять запросы от вашего имени через MCP. Рекомендуем: * Создать **отдельный ключ** для MCP в [/keys](https://hubris.pw/keys) — это упростит отзыв без затрагивания других интеграций. * При компрометации — немедленно отозвать ключ в дашборде. * Не публиковать конфиг MCP с ключом в открытых репозиториях. OAuth-авторизация (без копирования ключа в конфиг) — на дорожной карте. ## Что дальше * [Каталог моделей](https://hubris.pw/models) — все активные модели и цены в рублях. * [POST /v1/chat/completions](/docs/api/chat-completions) — API-референс LLM-запросов. * [/keys](https://hubris.pw/keys) — управление API-ключами. * [Биллинг](https://hubris.pw/billing) — пополнение баланса через СБП. --- # n8n (/docs/integrations/n8n) > Подключение Hubris к n8n — низкокодовая платформа автоматизации, в которой можно собирать workflow с участием LLM. [n8n](https://n8n.io) — низкокодовая платформа автоматизации с тысячью готовых интеграций и поддержкой собственных нод. В workflow можно дёргать LLM — обрабатывать письма, классифицировать обращения, переводить, суммаризировать. Hubris подключается как обычный OpenAI-совместимый провайдер. ## Требования * n8n self-hosted или n8n Cloud * Аккаунт на [hubris.pw](https://hubris.pw/dashboard) и API-ключ ## Создание Credentials В n8n настройка апстрима живёт не в самой ноде, а в общих Credentials — один раз создал, потом используешь во всех нодах. 1. Откройте раздел **Credentials** (боковое меню слева). 2. Нажмите **Add Credential** → найдите **OpenAI API**. 3. Заполните поля: | Поле | Значение | | ------------------- | ---------------------------------------------------------------------------------------- | | **API Key** | ваш ключ Hubris (формат `sk-gw-...`) — взять в [личном кабинете](https://hubris.pw/keys) | | **Base URL** | `https://api.hubris.pw/v1` | | **Organization ID** | оставьте пустым | 4. Дайте credential осмысленное имя, например `Hubris`. Сохраните. ## Использование в workflow Дальше credential `Hubris` доступен во всех нодах, которые умеют работать с OpenAI-совместимым API. ### OpenAI Chat Model (для AI Agent / Chains) В AI-нодах (LangChain-агенты, цепочки): 1. Добавьте ноду **OpenAI Chat Model**. 2. В поле **Credential to connect with** выберите свой `Hubris`. 3. В поле **Model** введите идентификатор модели из нашего [каталога](/models) — например `anthropic/claude-haiku-4.5`. Эта нода соединяется с другими AI-нодами (Tool, Memory, Output Parser) — n8n собирает агент из ваших шагов. ### OpenAI (Message a model) Если нужен простой запрос без агентной обвязки: 1. Добавьте ноду **OpenAI** → действие **Message a model**. 2. **Credential** → `Hubris`. 3. **Model** — введите ID вручную (n8n обычно подгружает список из апстрима; у нас это работает, но проверьте, что нода показывает наши модели). 4. Соберите промпт: можно через **Messages** (multi-turn) или **Simple** (один user-prompt). ### HTTP Request (на всякий случай) Если хочется полного контроля над запросом или прокинуть кастомный параметр: ``` Method: POST URL: https://api.hubris.pw/v1/chat/completions Authentication: Generic Credential Type → Header Auth Name: Authorization Value: Bearer sk-gw-... Body (JSON): { "model": "anthropic/claude-haiku-4.5", "messages": [ {"role": "user", "content": "{{ $json.prompt }}"} ] } ``` Удобно когда нужен `response_format: { type: "json_schema" }` или другие параметры, которых нет в UI ноды OpenAI. ## Рекомендуемые модели n8n-workflow обычно — серия однотипных запросов: классифицировать, перевести, суммаризировать. Здесь главное — цена и скорость, не максимальное качество reasoning. | Модель | Когда уместна | | ---------------------------- | ---------------------------------------------------------- | | `anthropic/claude-haiku-4.5` | быстрая классификация, простое извлечение данных | | `openai/gpt-4o-mini` | универсальный недорогой выбор, поддержка structured output | | `google/gemini-2.0-flash` | мультимодальные сценарии (картинки + текст) | Полный список с фильтрами по цене и возможностям — в [каталоге](/models). ## Тяжёлые / агентные сценарии Если workflow строит долгого AI-агента с многошаговыми инструментами — типа «найди в письме компанию → сходи в CRM → собери ответ» — модели с поддержкой tool calling показывают себя гораздо лучше. Возьмите Claude Sonnet/Opus или GPT-4o из каталога, и не забудьте включить **tool calling** в настройках ноды. См. также наш гид по [вызову инструментов](/docs/features/tool-calling) — там разобран жизненный цикл tool-сессии. ## Решение проблем ### Список моделей в OpenAI-ноде пустой * Это значит, нода не смогла дёрнуть `GET /v1/models` с вашим credential. Проверьте Base URL (`https://api.hubris.pw/v1`, обязательно с `/v1` в конце) и валидность ключа. * Можно временно ввести Model ID вручную в режиме **Manual** — работает даже без подгрузки списка. ### 401 при первом запросе * API-ключ не прошёл. Перепроверьте, что начинается с `sk-gw-` и не содержит пробелов. ### 402 / `insufficient_balance` * Закончился баланс. Пополните в [личном кабинете](/billing). ### 429 / `daily_limit_exceeded` * Сработал суточный лимит ключа. Можно увеличить в [настройках ключа](/keys) или дождаться сброса (24 часа от первого запроса дня). ### Workflow «зависает» на AI-ноде * LLM-запросы могут идти до нескольких минут (длинный контекст, reasoning-модели). В настройках ноды поднимите **Timeout** (по умолчанию 5 минут). ## Что дальше * [Каталог моделей](/models) — какие именно ID использовать в нодах. * [Управление API-ключами](/keys) — отдельный ключ под n8n с лимитом. * [Расходы](/usage) — посмотреть, во сколько обошёлся прогон workflow. * [Структурированный вывод](/docs/features/structured-output) — стабильные JSON-ответы для дальнейших шагов workflow. --- # OAuth 2.0 + PKCE (/docs/integrations/oauth) > Стандартный OAuth flow для подключения сторонних приложений и MCP-клиентов (Claude Desktop, Cursor, Cline) без копирования root-ключа. Hubris реализует **OAuth 2.0 Authorization Code + PKCE** (RFC 6749 + 7636 + 9700 BCP) для авторизации сторонних приложений. Это даёт два преимущества: 1. **Sandboxed-доступ**: токен выдаётся под выбранные scopes и опциональный дневной лимит. Если приложение попросит чат — оно не сможет читать ваш баланс. 2. **MCP-connector совместимость**: Claude Desktop, Claude.ai, Cursor и любой MCP-клиент с native «Add custom connector» UI проходят discovery + DCR + авторизацию автоматически, без `mcp-remote` шима. ## Discovery (RFC 8414 + RFC 9728) Клиент начинает с одного из: ```http GET https://api.hubris.pw/.well-known/oauth-protected-resource GET https://hubris.pw/.well-known/oauth-authorization-server ``` Второй — основной, содержит все endpoints. Пример ответа: ```json { "issuer": "https://hubris.pw", "authorization_endpoint": "https://hubris.pw/oauth/authorize", "token_endpoint": "https://hubris.pw/oauth/token", "registration_endpoint": "https://hubris.pw/oauth/clients", "revocation_endpoint": "https://hubris.pw/oauth/revoke", "scopes_supported": ["chat:write", "models:read", "balance:read"], "response_types_supported": ["code"], "grant_types_supported": ["authorization_code", "refresh_token"], "code_challenge_methods_supported": ["S256"], "token_endpoint_auth_methods_supported": ["none"] } ``` `token_endpoint_auth_methods_supported: ["none"]` означает что мы поддерживаем только public clients (PKCE-only, без client\_secret). ## Scopes | Scope | Доступ | | -------------- | ------------------------------------------------------------------------------ | | `chat:write` | `/v1/chat/completions`, `/v1/responses`, `/v1/messages`, `/mcp` chat\_complete | | `models:read` | `/v1/models`, `/mcp` models\_list+search+get\_pricing | | `balance:read` | чтение баланса (`/api/internal/me`, `/mcp` balance\_get) | Приложение запрашивает подмножество в `?scope=...` (space-separated). Юзер видит и подтверждает на consent-странице. ## Токены * **Access token**: формат `hbr-at-<32 hex>`. TTL **1 час**. Идёт как `Authorization: Bearer ...` на `/v1/*` и `/mcp`. * **Refresh token**: формат `hbr-rt-<48 hex>`. TTL **90 дней**. Принимается **только** на `POST /oauth/token`. * **Rotation**: каждый refresh выпускает новый refresh + новый access. Старый refresh имеет 30-секундное grace-окно (защита от race-conditions при параллельных запросах). После окна — обнаружение reuse и revoke всей семьи токенов. ## Поток (Python пример) ```python import requests, hashlib, base64, secrets # 1. Регистрация client (one-time per app) reg = requests.post('https://hubris.pw/oauth/clients', json={ 'client_name': 'My App', 'redirect_uris': ['http://localhost:8765/callback'], 'scope': 'chat:write models:read', }) client_id = reg.json()['client_id'] # 2. PKCE verifier = secrets.token_urlsafe(32) challenge = base64.urlsafe_b64encode( hashlib.sha256(verifier.encode()).digest() ).rstrip(b'=').decode() # 3. Браузерный flow — открыть в браузере auth_url = ( 'https://hubris.pw/oauth/authorize' f'?client_id={client_id}' '&redirect_uri=http://localhost:8765/callback' '&response_type=code' f'&code_challenge={challenge}&code_challenge_method=S256' '&scope=chat:write+models:read' '&state=somestate' ) # Юзер видит consent, кликает «Разрешить» → редирект на callback?code=...&state=... # 4. Обмен кода на токены tok = requests.post('https://hubris.pw/oauth/token', data={ 'grant_type': 'authorization_code', 'code': received_code, 'redirect_uri': 'http://localhost:8765/callback', 'client_id': client_id, 'code_verifier': verifier, }).json() access_token = tok['access_token'] # 5. Используем resp = requests.post( 'https://api.hubris.pw/v1/chat/completions', headers={'Authorization': f'Bearer {access_token}'}, json={ 'model': 'openai/gpt-4o-mini', 'messages': [{'role': 'user', 'content': 'привет'}], }, ) ``` ## Дневной лимит Приложение может передать `daily_limit_kopecks=50000` в query к `/oauth/authorize`. Юзер видит запрошенную сумму на consent-странице и может **снизить** перед подтверждением. Если приложение не передало — default **500 ₽/день**. После выдачи токена лимит работает идентично api\_keys daily-limit'у — 429 при превышении с метаданными в `/usage`. ## Безопасность Hubris следует RFC 9700 BCP: * **PKCE S256 обязательно** — `plain` метод не поддерживается. * **Refresh rotation + reuse detection** — обнаруженный replay старого refresh после grace-окна revoke'ит всю семью токенов. * **redirect\_uri exact-match** после нормализации (lowercase scheme/host, case-sensitive path) — без prefix-wildcards. * **Reserved-word check** на client\_name (`hubris`, `official`, `admin`, `support` запрещены как substring) — anti-phishing baseline. * **DCR rate-limit**: 5 регистраций/IP/час + 100/день глобально. * **Все токены — sha256-хэши** в БД. Plain text никогда не хранится. ## MCP integration Совместимость с native «Add custom connector» UI в Claude Desktop / Claude.ai: 1. В клиенте указываешь URL: `https://api.hubris.pw/mcp` 2. Клиент сам идёт по discovery → DCR → /authorize (открывает браузер) 3. Юзер видит consent: «Claude Desktop запрашивает доступ chat:write, models:read, balance:read» 4. После «Разрешить» — обратно в клиент, токены выпущены, инструменты подключены Legacy `sk-gw-` ключи продолжают работать на `/mcp` без изменений (back-compat). ## Phase B (deferred) * `/profile/connected-apps` UI — список авторизованных приложений + revoke * Per-scope deny (сейчас all-or-nothing на consent-странице) * Step-up auth (re-OTP при выдаче sensitive scopes если auth >7д назад) * Domain verification для verified-badge при DCR * `embeddings:write`, `usage:read` scopes * OAuth для /v1/embeddings + /v1/usage routes ## Ссылки * RFC 6749 — OAuth 2.0 * RFC 7009 — Token Revocation * RFC 7591 — Dynamic Client Registration * RFC 7636 — PKCE * RFC 8414 — Authorization Server Metadata * RFC 9700 — OAuth 2.0 Security Best Current Practice * RFC 9728 — Protected Resource Metadata * MCP Authorization: [https://modelcontextprotocol.io/specification/draft/basic/authorization](https://modelcontextprotocol.io/specification/draft/basic/authorization) --- # OpenClaw (/docs/integrations/openclaw) > Подключение Hubris к OpenClaw — open-source AI-ассистенту для Telegram, WhatsApp, Discord и Slack. [OpenClaw](https://openclaw-ai.com) — open-source платформа для AI-ассистента в мессенджерах. Поднимает локальный gateway, маршрутизирует сообщения из Telegram, WhatsApp, Discord, Slack к LLM-провайдеру. Hubris подключается как custom provider через OpenAI-совместимый API. ## Требования * Установленный OpenClaw (см. [инструкцию по установке](https://openclaw-ai.com/en/install)) * Node.js 22+ * Аккаунт на [hubris.pw](https://hubris.pw/dashboard) и API-ключ ## Подключение к Hubris Конфиг OpenClaw — `~/.openclaw/openclaw.json` (JSON5, поддерживает комментарии и trailing commas). Откройте файл: ```bash nano ~/.openclaw/openclaw.json ``` или через встроенный инструмент: ```bash openclaw configure ``` В секции `models.providers` добавьте провайдер `hubris`: ```json5 { models: { mode: "merge", providers: { hubris: { baseUrl: "https://api.hubris.pw/v1", apiKey: "${HUBRIS_API_KEY}", api: "openai-completions", models: [ { id: "anthropic/claude-haiku-4.5", name: "Claude Haiku" }, { id: "openai/gpt-4o-mini", name: "GPT-4o mini" }, { id: "google/gemini-2.0-flash", name: "Gemini Flash" }, ], }, }, }, } ``` Поле `api: "openai-completions"` обязательно — без него OpenClaw не определит тип API. Ключ положите в `~/.openclaw/.env`: ``` HUBRIS_API_KEY=sk-gw-... ``` ## Модель по умолчанию В секции `agents.defaults` укажите основную модель и опционально fallback'и: ```json5 { agents: { defaults: { model: { primary: "hubris/anthropic/claude-haiku-4.5", fallbacks: ["hubris/openai/gpt-4o-mini"], }, models: { "hubris/anthropic/claude-haiku-4.5": { alias: "Claude" }, "hubris/openai/gpt-4o-mini": { alias: "GPT" }, "hubris/google/gemini-2.0-flash": { alias: "Gemini" }, }, }, }, } ``` Формат ссылки: `/`, где `` — имя из секции `providers` (у нас `hubris`). ## Применение конфига Если включён hot-reload (по умолчанию) — изменения подхватятся автоматически. Если нет: ```bash openclaw gateway restart ``` ## Переключение моделей в чате Прямо в любом канале: ``` /model hubris/openai/gpt-4o-mini ``` Или через CLI: ```bash openclaw models set hubris/google/gemini-2.0-flash ``` Список доступных: ```bash openclaw models list ``` ## Подключение каналов В `channels` секции укажите параметры мессенджеров. Пример для Telegram: ```json5 { channels: { telegram: { dmPolicy: "allowlist", botToken: "<токен бота от @BotFather>", allowFrom: ["<ваш Telegram ID>"], groupPolicy: "allowlist", }, }, plugins: { entries: { telegram: { enabled: true }, }, }, } ``` Аналогично для WhatsApp, Discord, Slack — детали в [документации OpenClaw](https://openclaw-ai.com/en/docs). ## Рекомендуемые модели OpenClaw как чат-агент в мессенджерах хорошо работает на быстрых моделях. Для агентного сценария с инструментами — более крупная модель из каталога. | ID | Когда уместна | | ---------------------------- | --------------------------------------------- | | `anthropic/claude-haiku-4.5` | быстрые типовые ответы | | `openai/gpt-4o-mini` | универсальный недорогой выбор | | `google/gemini-2.0-flash` | мультимодальные сценарии (изображения в чате) | Полный список — в [каталоге моделей](/models). ## Решение проблем ### `'No API provider registered for api: undefined'` В секции провайдера должно быть поле `api: "openai-completions"` — без него OpenClaw не понимает тип API. ### `Invalid API key` (401) * Проверьте формат ключа (`sk-gw-` + hex). * Если используете `${HUBRIS_API_KEY}`, убедитесь, что переменная действительно установлена в `~/.openclaw/.env`. ### Модель не находится * Проверьте, что ID в `models[]` точно совпадает с одним из [каталога](/models). * Проверьте, что та же модель есть в `agents.defaults.models` — это allowlist. * Формат: `hubris/`. ### Gateway не запускается после правки конфига OpenClaw строго валидирует конфиг: ```bash openclaw doctor openclaw doctor --fix ``` ### Медленные ответы * Переключитесь на более быструю модель — Haiku или Flash. * В настройках канала включите `streamMode: "partial"` для стриминговых ответов. ## Что дальше * [Каталог моделей](/models) — список ID для конфига. * [Управление API-ключами](/keys) — отдельный ключ под OpenClaw. * [Расходы](/usage) — детализация сессий. * [Hermes Agent](/docs/integrations/hermes-agent) — альтернативный агент с Telegram. --- # OpenCode (/docs/integrations/opencode) > Подключение терминального AI-агента OpenCode (sst.dev) к Hubris — Plan Mode, file-context, image drag-and-drop прямо в TUI. [OpenCode](https://opencode.ai) — open-source AI-агент, работающий прямо в терминале как полноценный TUI. Понимает контекст проекта, помогает писать и править код, держит историю команд. Подключается к любому OpenAI-совместимому провайдеру через `opencode.json` в корне проекта. ## Требования * macOS / Linux / WSL / Windows + современный терминал * `curl` и `jq` в PATH * Аккаунт на [hubris.pw](https://hubris.pw/dashboard) и API-ключ ## Установка Самый простой способ — curl-инсталлятор: ```bash curl -fsSL https://opencode.ai/install | bash ``` Альтернативы: `npm i -g opencode-ai`, Homebrew, Chocolatey, Docker-образ. Полный список — в [официальной документации OpenCode](https://opencode.ai/docs/install). ## Подключение к Hubris OpenCode читает конфиг из `opencode.json` в корне проекта (или `~/.config/opencode/opencode.json` глобально). Создайте файл со следующим содержимым: ```json { "$schema": "https://opencode.ai/config.json", "provider": { "hubris": { "npm": "@ai-sdk/openai-compatible", "options": { "baseURL": "https://api.hubris.pw/v1", "apiKey": "{env:HUBRIS_API_KEY}" }, "models": { "anthropic/claude-haiku-4.5": { "name": "Claude Haiku" }, "openai/gpt-4o-mini": { "name": "GPT-4o mini" }, "google/gemini-2.0-flash": { "name": "Gemini Flash" } } } } } ``` Ключ кладите в переменную окружения (безопаснее, чем в файл): ```bash export HUBRIS_API_KEY="sk-gw-..." ``` Запустите агента из папки проекта: ```bash cd /path/to/your/project opencode ``` ## Команды и режимы | Команда | Что делает | | ------------------------- | ----------------------------------------------------- | | `Tab` | переключение Plan Mode ↔ Execute Mode | | `@filename` | подключить файл в контекст текущего запроса | | drag-and-drop изображения | передать картинку как вход для мультимодальной модели | | `/init` | сгенерировать стартовый план проекта | | `/models` | переключить активную модель | | `/undo` / `/redo` | откатить или повторить последнее изменение | | `/share` | поделиться сессией через ссылку | ## Рекомендуемые модели | ID | Когда подходит | | ---------------------------- | ---------------------------------------------------- | | `anthropic/claude-haiku-4.5` | быстрая работа над типовыми задачами | | `openai/gpt-4o-mini` | универсальный выбор, недорого | | `google/gemini-2.0-flash` | мультимодальные сценарии (drag-and-drop изображений) | Для сложных рефакторингов и архитектурных задач — Claude Sonnet или Opus из [каталога](/models). С тяжёлыми моделями стоит работать в Plan Mode, чтобы проверить план перед исполнением. ## Контроль расходов * Plan Mode (через `Tab`) — модель только описывает, что собирается делать, не исполняет. Хороший способ оценить сложность задачи и стоимость до запуска. * Отдельный API-ключ для OpenCode с дневным лимитом в [настройках](/keys). * Расход за сессию виден в [«Расходах»](/usage). ## Решение проблем ### `Invalid API key` при первом запуске * Перепроверьте, что переменная `HUBRIS_API_KEY` экспортирована в текущей shell-сессии: `echo $HUBRIS_API_KEY`. * Ключ должен начинаться с `sk-gw-`. ### Модели не показываются в `/models` * Проверьте, что секция `models` в `opencode.json` содержит ID, существующие в нашем [каталоге](/models). * Перезапустите `opencode` после правки конфига. ### Запрос «висит» / нет ответа * Тяжёлые модели на сложных задачах могут думать минуты. Если зависло дольше — `Ctrl+C` и попробуйте более быструю модель. ## Что дальше * [Каталог моделей](/models) — список доступных IDs. * [Управление API-ключами](/keys) — отдельный ключ под OpenCode. * [Расходы](/usage) — детализация сессий. * [Cline](/docs/integrations/cline), [Roo Code](/docs/integrations/roo-code) — агенты для VS Code. --- # Qwen Code CLI (/docs/integrations/qwen-code) > Подключение Qwen Code — терминального AI-агента от Alibaba (форк gemini-cli) — к Hubris. [Qwen Code](https://github.com/QwenLM/qwen-code) — терминальный AI-агент от Alibaba, форк `gemini-cli` от Google. Несмотря на название, поддерживает любые OpenAI-совместимые провайдеры — не только модели семейства Qwen. Минималистичный TUI, удобен для CLI-сценариев и автоматизации. ## Требования * macOS / Linux / WSL / Windows * Node.js 20+ * Аккаунт на [hubris.pw](https://hubris.pw/dashboard) и API-ключ ## Установка ```bash npm install -g @qwen-code/qwen-code ``` После установки команда `qwen` должна быть доступна в PATH: ```bash qwen --version ``` ## Подключение к Hubris Конфиг живёт в `~/.qwen/settings.json`. Создайте файл или дополните существующий: ```json { "selectedAuthType": "openai", "openai": { "baseUrl": "https://api.hubris.pw/v1", "apiKey": "sk-gw-...", "models": [ { "id": "anthropic/claude-haiku-4.5", "displayName": "Claude Haiku", "contextWindowSize": 200000 }, { "id": "openai/gpt-4o-mini", "displayName": "GPT-4o mini", "contextWindowSize": 128000 }, { "id": "google/gemini-2.0-flash", "displayName": "Gemini Flash", "contextWindowSize": 1000000 } ] } } ``` `contextWindowSize` для каждой модели подсмотрите в [каталоге](/models) — это размер контекстного окна, на основе которого Qwen Code решает, когда обрезать историю диалога. Альтернатива — переменные окружения: ```bash export OPENAI_BASE_URL="https://api.hubris.pw/v1" export OPENAI_API_KEY="sk-gw-..." ``` Запустите агента в папке проекта: ```bash cd /path/to/your/project qwen ``` ## Переключение моделей В рантайме: ``` /model anthropic/claude-haiku-4.5 ``` Или при запуске: ```bash qwen --model openai/gpt-4o-mini ``` ## Рекомендуемые модели | ID | Когда подходит | | ---------------------------- | ------------------------------------------ | | `anthropic/claude-haiku-4.5` | быстрая работа | | `openai/gpt-4o-mini` | универсальный выбор | | `google/gemini-2.0-flash` | мультимодальные сценарии, большой контекст | Для сложных задач — Claude Sonnet/Opus или Qwen Coder из [каталога](/models). ## Решение проблем ### `qwen: command not found` после установки * Проверьте, что глобальный bin-каталог npm в PATH: `npm bin -g` покажет, где. Добавьте этот путь в `$PATH`. ### `Invalid API key` * Ключ в `settings.json` должен быть в кавычках, начинаться с `sk-gw-`. * Если используете env vars, перезапустите терминал. ### Список моделей пустой / Qwen Code не видит модели * Проверьте, что в `settings.json` секция `models` непустая и ID моделей корректные. * При работе через env vars Qwen Code попытается дёрнуть `GET /v1/models` — убедитесь, что Base URL правильный. ### Длинный контекст вылетает с ошибкой * Поле `contextWindowSize` в конфиге должно соответствовать реальному контексту модели. Загляните в карточку модели в [каталоге](/models). ## Что дальше * [Каталог моделей](/models) — IDs и contextWindowSize для всех моделей. * [Управление API-ключами](/keys) — отдельный ключ под Qwen Code. * [Расходы](/usage) — детализация сессий. * [OpenCode](/docs/integrations/opencode) — альтернативный терминальный агент. --- # Roo Code (/docs/integrations/roo-code) > Подключение Roo Code — AI-агента для VS Code с режимами Architect / Code / Ask / Debug — к Hubris. [Roo Code](https://roo-code.com) — расширение для VS Code (форк Cline), которое умеет работать в нескольких режимах: Architect для проектирования, Code для написания и правок, Ask для вопросов, Debug для отладки. Каждому режиму можно назначить свою модель — например, более мощную для Architect и более дешёвую для рутинных правок в Code. ## Требования * VS Code 1.85+ * Аккаунт на [hubris.pw](https://hubris.pw/dashboard) и API-ключ ## Установка Roo Code 1. Откройте панель расширений в VS Code (`Ctrl+Shift+X`). 2. Найдите **Roo Code** (издатель — RooVeterinaryInc). 3. Нажмите **Install**. 4. После установки в боковой панели появится иконка Roo Code. ## Подключение к Hubris 1. Откройте параметры расширения через иконку шестерёнки в боковой панели Roo Code. 2. Перейдите на вкладку **Providers**. 3. В выпадающем меню провайдера выберите **OpenAI Compatible**. 4. Заполните поля: | Поле | Значение | | ------------ | ------------------------------------------------------ | | **Base URL** | `https://api.hubris.pw/v1` | | **API Key** | ваш ключ из [личного кабинета](https://hubris.pw/keys) | | **Model** | например `anthropic/claude-haiku-4.5` | 5. Сохраните настройки (кнопка **Save** в правом верхнем углу). ## Разные модели для разных режимов Главная фишка Roo Code — разные модели на каждый режим. Это удобно: тяжёлая модель только там, где она реально нужна. В тех же настройках провайдера у Roo Code есть отдельные слоты для каждого режима. Типовая раскладка: | Режим | Что делает | Какая модель уместна | | ------------- | ------------------------------------------------- | ------------------------------------------- | | **Architect** | проектирует архитектуру, разбивает задачу на шаги | мощная — Claude Opus / Sonnet | | **Code** | пишет и правит код по чёткому плану | средняя или быстрая — Claude Sonnet, GPT-4o | | **Ask** | отвечает на вопросы о коде | быстрая и дешёвая — Haiku, GPT-4o-mini | | **Debug** | анализирует логи, ищет причины ошибок | средняя — Claude Sonnet | Полный актуальный список моделей — в [каталоге](/models). В коде режим называется `apiModelId`, конкретное значение зависит от модели в нашем каталоге. ## Что обычно ставят | ID | Когда уместна | | ---------------------------- | -------------------------------- | | `anthropic/claude-haiku-4.5` | быстрая работа, недорого | | `openai/gpt-4o-mini` | универсальный недорогой выбор | | `google/gemini-2.0-flash` | мультимодальные сценарии, быстро | Для тяжёлых режимов (Architect, сложный Code) подойдёт более крупная Claude или GPT — посмотрите [каталог](/models) с фильтром по поддержке tool calling. ## Решение проблем ### `Failed to fetch models` или 401 при первом подключении * Перепроверьте ключ: должен начинаться с `sk-gw-`, без пробелов. * В Roo Code иногда нужно полностью перезагрузить окно VS Code (`Ctrl+Shift+P` → **Developer: Reload Window**) после первой настройки. ### Модель отвечает «не вижу инструментов» или зацикливается * Не все модели одинаково хорошо справляются с tool-агентами Roo Code. Попробуйте модель, у которой в [каталоге](/models) явно указана поддержка tool calling. * Уменьшите контекст: закройте лишние открытые файлы — Roo передаёт активные вкладки в каждый запрос. ### Сессия Architect вдруг очень дорогая * Architect-режим часто пишет длинные планы — это много `completion_tokens`. Используйте модель попроще для Code и Ask, а Architect зовите только когда правда нужен. ### Дневной лимит ключа сработал в середине задачи * В [настройках ключа](/keys) можно увеличить лимит. Альтернатива — заведите отдельный ключ под Roo Code и для каждого проекта. ## Что дальше * [Каталог моделей](/models) — подобрать модели под каждый из четырёх режимов. * [Управление API-ключами](/keys) — отдельный ключ под Roo Code с лимитом. * [Расходы](/usage) — детализация запросов. * [Cline](/docs/integrations/cline) — родитель Roo Code, простой одиночный режим.