Содержание

Провайдеры

Провайдер — это один апстрим: имя, тип (диалект), адрес и набор ключей. Настраивается в панели, применяется гейтом без перезапуска.

Типы

Тип Кого обслуживает
openai OpenAI и любой OpenAI-совместимый сервер: Ollama, vLLM, llama.cpp, LM Studio, прокси
anthropic Anthropic Messages
google Google Gemini (Generative Language API)
vertex Google Vertex AI

Тип выбирает адаптер, а не поставщика. Для самостоятельно поднятой модели берите openai и укажите свой базовый адрес — отдельного типа для vLLM нет и не нужно.

Имя провайдера

Имя — это одновременно:

  • идентификатор в журнале и на страницах;
  • префикс в адресации моделей: имя/модель;
  • то, с чем сравнивается ограничение ключа по провайдерам.

Поэтому имя лучше короткое и без слэшей. Регистр не важен — см. Адресация моделей.

Базовый адрес

Пусто — адрес по умолчанию для типа. Заполнено — используется как есть.

Адрес может уже содержать версию (http://ollama:11434/v1) — второй раз она не добавится. Ключ провайдера может переопределить адрес для себя: так адресуется отдельный регион или отдельный прокси на тот же тип.

Модели

Список моделей провайдера — это таблица маршрутов: голое имя модели из запроса ищется в ней. Пишутся имена без префикса провайдера.

Модель может вести только к одному провайдеру — это ограничение базы, а не соглашение. Если два провайдера обслуживают одну модель, различайте их префиксом в запросе.

Если провайдер один, список можно оставить пустым: любая модель уйдёт к нему. Это удобно и рискованно одновременно — опечатка в имени модели тоже уйдёт наверх, и ошибку вернёт провайдер, а не гейт.

Кнопка «Спросить /v1/models»

Рядом с полем моделей есть кнопка, которая спрашивает у провайдера его каталог — тем же секретом, адресом и заголовками, с которыми пойдёт трафик.

Зачем: списки моделей набираются руками и тихо расходятся с тем, что провайдер отдаёт на самом деле. Кнопка отвечает на вопрос «что этот ключ реально умеет» до того, как он попадёт в бой.

  • В настройках ключа секрет берётся из формы, ещё до сохранения — так проверяется свежевведённый ключ. Пустое поле секрета у сохранённого ключа означает «использовать сохранённый».
  • У ключа пользователя, рядом с разрешёнными моделями, спрашиваются все провайдеры сразу, каждый своим ключом.
  • Ответ не кэшируется.
  • Vertex AI так не спросить: его каталог выдаётся только по токену OAuth сервисного аккаунта, и об этом честно написано в ответе кнопки.

Для локального сервера без авторизации заголовок с ключом не отправляется вовсе — пустой Bearer некоторые серверы отвергают.

Ключи провайдера

Один провайдер — сколько угодно ключей. Между ними распределяется нагрузка.

Поле Смысл
Метка видна в журнале и метриках; секрет не показывается
Секрет шифруется AES-256-GCM, в базе только шифротекст
Вес доля нагрузки среди ключей провайдера
Модели этого ключа пусто — все модели провайдера
Свой базовый адрес переопределяет адрес провайдера
Заголовки ключа добавляются к заголовкам провайдера

Выбор ключа — smooth weighted round-robin (тот же алгоритм, что у nginx): каждый кандидат накапливает свой вес каждый проход, побеждает наибольшая сумма, потом возвращает пулу общую. В сравнении со взвешенно-случайным он раскладывает нагрузку ровно на любом масштабе, а не только в среднем — что важно, когда связывающее ограничение это лимит провайдера на ключ.

Ключ, получивший 429, 401, 403 или 5xx, уходит в cooldown: 5 минут для отказа авторизации (сам он не починится) и от секунды с экспоненциальным ростом до минуты для остального. Если все подходящие ключи в cooldown, берётся тот, которому осталось меньше всех — отказать запросу хуже, чем попробовать ещё раз.

Ограничение ключа по моделям не мешает перечислять каталог: /v1/models приходит без имени модели, и ограничение к нему не применяется. Оно применяется к результату — модель, которую не обслуживает ни один включённый ключ, из каталога выпадает.

Vertex AI

Для типа vertex у ключа есть проект, регион и JSON сервисного аккаунта. JSON шифруется так же, как секрет. Каталог моделей кнопкой не спрашивается.

Особенности диалектов

Панель показывает только те переключатели, которые относятся к выбранному типу.

OpenAI:

  • Есть эндпоинт /v1/responses — если выключено, запросы Responses транслируются в chat completions.
  • Отправлять max_completion_tokens вместо max_tokens — требуется reasoning-моделям OpenAI и отвергается большинством совместимых серверов. Это тот флаг, из-за которого «всё работало, а потом перестало».

Anthropic: anthropic-version и anthropic-beta.

Нагрузка и повторы

Поле По умолчанию Смысл
Одновременных запросов 256 семафор на провайдера; 0 — без ограничения
Длина очереди 0 0 значит восьмикратная ёмкость; при переполнении сразу 429
Попыток 3 каждая берёт другой ключ
Начальная пауза 200 мс с джиттером
Максимальная пауза 5000 мс

Быстрый 429 при переполнении очереди — намеренно: бесконечное ожидание хуже внятного отказа, по которому клиент сделает бэкофф.

Сеть и заголовки

Заголовки — JSON-объект, отправляется с каждым запросом к провайдеру. Заголовки ключа перебивают заголовки провайдера. Их же отправляет и кнопка «Спросить /v1/models»: иначе она задавала бы не тот вопрос, что трафик.

Транспорт — необязательный JSON: dial_timeout_ms, response_header_timeout_ms, request_timeout_ms, max_idle_conns_per_host, idle_conn_timeout_ms, disable_http2.

Фолбэки

Запрос может нести цепочку provider/model. Гейт идёт по ней, когда ошибка того стоит: временные сбои и отсутствие возможности — да, «неверный запрос» — нет, он будет столь же неверным у следующего провайдера, и обход цепочки превратил бы один внятный 400 в несколько путаных.

Доступ проверяется на каждой цели отдельно: ключ, которому запрещён основной провайдер, но разрешён резервный, уедет на резервный, а не получит отказ.