---
name: omniroute-20129-openai
description: "Процедура добавления custom OpenAI-compatible провайдера в новый OmniRoute-app на mini (порт 20129) — узел + отдельный connection, ключ через apiKey, manage только по cookie-логину дашборда."
metadata: 
  node_type: memory
  type: reference
  originSessionId: 0c88d568-8420-4ae1-b00b-824de45b134d
  modified: 2026-08-04T05:40:48.392Z
---

На mac mini два OmniRoute: старый gateway `:20127` (см. [[omniroute_cloud_bus]]) и **новый omniroute-app `:20129`** (npm-пакет `omniroute` 3.8.21, LaunchDaemon `/Library/LaunchDaemons/com.cebers.omniroute.plist`, юзер **`omniroute_svc`**, data-dir `/Users/omniroute_svc/.local/share/omniroute-app` + БД под его home — НЕ читается без sudo, а passwordless sudo на mini нет).

**Кастом-провайдер в 20129 = ДВЕ сущности:**
1. **provider node** (`POST /api/provider-nodes`): `{type:"openai-compatible", name, prefix, apiType:"chat", baseUrl:".../v1", chatPath:"/chat/completions", modelsPath:"/models"}`. Поле для ключа — **`apiKey`** (НЕ `customHeaders` — auth-заголовки там запрещены валидатором).
2. **connection** (`POST /api/providers`): `{provider:"<id узла>", authType:"apikey", name, apiKey:"<ключ>"}`. Без этого роутер кидает `No credentials for provider: <node id>` хотя узел создан. Узел сам по себе ключ не хранит для роутинга.

**Авторизация management-операций:** inference-`OMNIROUTE_API_KEY` (из `~/.codex/omniroute.env`, scope read) даёт только GET; create/update требуют **scope `manage`** ИЛИ **dashboard-cookie**. Cookie добываем логином: `POST /api/auth/login {"email":"<любой>","password":"<пароль дашборда>"}` → cookie-jar (curl `-c jar` / `-b jar`). **Авторизация одно-админ: email игнорируется, проверяется только пароль** (любой email + верный пароль → `{"success":true}`). Пароль дашборда — секрет у юзера, в память НЕ писать (спрашивать каждый раз).

**CLI грабли:** `omniroute nodes add --base-url` НЕ работает — глобальный `--base-url` (адрес сервера) съедает узловой → «required option --base-url not specified». Обходить через прямой `POST /api/provider-nodes` с cookie. CLI запускать с `OMNIROUTE_BASE_URL=http://localhost:20129` (иначе лезет в пустую локальную БД cebers_mini и warning про STORAGE_ENCRYPTION_KEY — он безвреден в remote-режиме). Полезные API-группы: `omniroute api provider-nodes …`, `omniroute api providers …` (`--body @file.json`).

**Проверка узла:** `POST /api/providers/<connId>/test` (быстрый, latency мс) + `GET /api/providers/<connId>/models` (discovery). Узлы (custom openai-compatible) на 2026-06-14: `mhub`→Mindshub Local (прокси :21487), **`hcoder`→HelpCoder `https://helpcoder.cc/v1`**, **`logfare`→logfare.ai (бесплатный, см. ниже)**. **2026-06-15 добавлен `mistral`→Mistral La Plateforme `https://api.mistral.ai/v1`** (ключ ЮЗЕРА, его собственный официальный аккаунт; работает напрямую с RU-IP, прокси не нужен — но connection всё равно создаётся с proxyEnabled=true по дефолту, Mistral не гео-блокирован так что ок). 27 моделей (`mistral-large/medium/small-latest`, `magistral-*` reasoning, codestral, `open-mistral-nemo`). Используется в [[verstak_rename]] (Делор): (1) надёжный backstop спорт-кворума — `HEAL_CANDIDATES["text"]` += `mistral/mistral-medium-latest` ПОСЛЕДНИМ; (2) **primary доверенных слотов с конфиденц. данными** (commit 3e4b35c): чат `cloud_l3_model`=mistral-medium→small, 1С `onec_llm`=mistral-large→medium→small (увели с серых freemodel-dev/siliconflow ради data-safety; серые/leaky типа atessa — только спорт). Лёгкие чат-слои L1/L2 → Cloudflare Workers AI (ждёт account_id). Проверено: `mistral/mistral-medium-latest` через :20127 `/v1/messages` → 200, 0.4с. ⚠️ OmniRoute инжектит ~2000 input-токенов служебного контекста на запрос — учитывать в расходе платного ключа. **Удаление узла = два DELETE с cookie:** `DELETE /api/providers/<connId>` (сначала connection) → `DELETE /api/provider-nodes/<nodeId>` (потом сам узел); порядок важен. **CRUD-грабли:** create node → POST `/api/provider-nodes` отдаёт id внутри `{"node":{"id":...}}` (НЕ на верхнем уровне); connection обновляется методом **PUT** `/api/providers/<id>` (PATCH=405); create connection НЕ принимает `proxyEnabled` (всегда true) → ставить false отдельным PUT. :20127 и :20129 — federated, одинаковый каталог 361 модель; узлы видны на обоих.

**logfare.ai — НЕ в шине (намеренно, 2026-06-14):** сначала завёл custom-узлом, но **удалил** (DELETE connection+node) по требованию юзера — провайдер ОБУЧАЕТСЯ на запросах (data-leak), в общей шине = footgun (ротация/auto-heal/чужой конфиг могли случайно попасть в `logfare/*` и слить данные). Теперь у Делор **отдельный изолированный `LogfareClient`** (`models/logfare.py`, прямой на logfare.ai/v1, non-stream) — единственный конструктор в спорт-обвязке main.py (voter#1 лид → откат на надёжную DeepSeek-цепочку), ключ только в launchd-plist Делор (`DELOR_LOGFARE_API_KEY`), НЕ в git/память/шине. Каталог logfare (все НЕ-Claude): glm-5.1 (~5/6, лид), gemini-3-flash (6/6), gemini-3.5-flash/deepseek-v4-pro/flash/kimi-k2.6 (флапают 503/502/timeout). Premium-модели требовали opt-in на logfare.ai/`/consent` (юзер согласился). Детали — [[verstak_rename]] (Делор) и [[cerebras-groq-ru-ban]] (спорт-слоты). **Урок:** провайдер с риском утечки — НЕ в общую шину, а отдельным клиентом с узким использованием.

**Паттерн «рискованный провайдер» (Делор, commit b78c884, 2026-06-14):** для data-leak провайдеров (logfare.ai, **unlimited.surf**) — ТРИ слоя изоляции: (1) off-bus (не в OmniRoute), (2) отдельный клиент в `models/`, конструируется только в спорт-обвязке main.py, (3) **runtime-гард `models/risky_guard.py`**: contextvar `sport_analysis_scope()` + `assert_sport_only()`, каждый рискованный клиент перед отправкой проверяет, что выполняется внутри спорт-scope (его ставит `sport_analyzer._ask` — единственная точка вызова голосующих); иначе `RiskyProviderError`, данные не уходят. voter#1 = цепочка `logfare/glm-5.1 → logfare/gemini-3-flash → unlimited/gateway-gpt-5-mini → надёжная bus-DeepSeek`. **unlimited.surf:** кастомный `/api/chat` (одна строка `message` + SSE `data:{"delta"}`, стоп на done/finish — `resp.text()` висит), модели `gateway-*` (gpt-5*, claude, gemini, deepseek, grok, qwen); быстрые `gateway-gpt-5-mini`(4с)/`gpt-4-1-mini`/`gemini-2.5-flash`, флагманы reasoning медленные. Ключи в launchd-plist Делор (`DELOR_LOGFARE_API_KEY`, `DELOR_UNLIMITED_API_KEY`), НЕ в git. Новый провайдер этого типа добавлять по тому же паттерну.

**`fmodel`→FreeModel.dev custom-узел УДАЛЁН 2026-06-13** (по просьбе юзера). Был openai-compatible на локальный Node-прокси `127.0.0.1:21489` (`~/Services/freemodel-omniroute-proxy/server.mjs`, launchd `local.freemodel-omniroute-proxy`). Заменён **нативным встроенным провайдером `freemodel-dev`** (authType apikey, 3 ключа-connection «1/2/test») — он даёт те же 4 модели (`freemodel-dev/{gpt-5.3-codex,gpt-5.4,gpt-5.4-mini,gpt-5.5}`) и ходит на freemodel.dev своим egress шины (проверено: генерит с ВЫКЛЮЧЕННЫМ прокси :21489). Прокси-launchd снесён (plist удалён), каталог `~/Services/freemodel-omniroute-proxy/` (68K) осиротел — можно `rm -rf`. **Делор** перенацелен с префикса `fmodel/`→`freemodel-dev/` (1С-фича `onec_llm`/`onec_llm_fallback` в [[verstak_rename]]: config.py + onec_handlers.py + llm_health.py). ⚠️ Модели медленные: `gpt-5.5`/`gpt-5.4` reasoning 60–80с uncached, `gpt-5.4-mini` 2–60с (плавает) — это свойство freemodel.dev, одинаково для custom и native.

**gc/ (grok-cli) — авторизация ПРОТУХАЕТ (2026-07-07):** все `gc/*` вызовы → 401 «authentication expired — please reconnect in the dashboard»; лечится только реконнектом в UI-дашборде (:20129), CLI не поможет. Задевает КодИнвест ([[investpilot_overview]]): voter №1 панели `gc/grok-composer-2.5-fast` и `kb_distill_model` — фолбэки держат (nim/codex), бот живёт, но при 401 напомнить юзеру про реконнект. ГРАБЛЯ после реконнекта: первый вызов может вернуть хвост ЧУЖОГО контекста grok-cli-сессии (ответ не на промпт) — сделать пробный вызов-другой перед доверием.

**helpcoder.cc** — codex/ChatGPT-прокси (`sk-RoP4…`-ключ), `/v1/models` отдаёт 11 моделей `gpt-5 … gpt-5.4` (`owned_by:codex`). Заведён как `hcoder` (connection valid, все 11 моделей discovered). ⚠️ на 2026-06-12 сама генерация (`/v1/chat/completions`) висла/504 и напрямую, и через шину — бэкенд helpcoder лежал; интеграция корректна, проблема в апстриме. Перепроверить генерацию `hcoder/gpt-5.1` позже.

**ГРАБЛЯ «видимость ≠ работоспособность» + `zemlya-bot` на 20129 (2026-06-15):** `zemlya-bot` (AI-отчёт, [[zemlya_bot_overview]]) ходит на **20129** (`OMNIROUTE_BASE_URL`+`OMNIROUTE_API_KEY=sk-e8243…`). Два важных факта при отладке: (1) **`/v1/models` фильтруется правами key-группы** — провайдер может быть РАБОЧИМ, но НЕвидимым в списке для конкретного ключа (так `mistral/*` не виден в списке ключа бота, но `mistral/mistral-{small,medium,large}-latest` отлично вызываются, 0.3–0.6с). **Всегда проверять вызовом, не только `/models`.** (2) Обратное тоже: `zai/*` (glm-5/5.1/turbo) ключу бота ВИДЕН, но при вызове **таймаутит/пустой SSE `tokens-in=0`** — нода настроена, апстрим z.ai висит (гео-блок РФ / base_url / прокси), чинить в дашборде. **Интеграция Mistral в бот (env-only, без кода):** `bot/.env` `OMNIROUTE_GPT_MODEL_CASCADE` начинается с `mistral/mistral-large-latest,mistral/mistral-medium-latest,…` (потребитель — `bot/services/omniroute_ai.py` `config.omniroute_gpt_model_cascade`; срабатывает только когда бесплатный Gemini-primary в кулдауне). На реальном промпте large дал лучшие вердикты; **один ключ Mistral быстро ловит 429 «all credentials cooling down»/503** (низкий тир). **РЕШЕНО (2026-06-15): в пул ноды Mistral добавлен 2-й ключ** (дашборд 20129, нода `Mistral` `openai-compatible-chat-19f61b01…`, connection «Mistral key2» id `85b83c20…`, ключ отличается от первого → ротация/фейловер; тест valid 184мс; серия с 2 ключами: medium 5/5, small 4/5 — рейт-лимит снят). Бэкап `.env.bak-pre-mistral-*`. **zai НЕ кастом-нода на 20129** (там только `mhub`/`hcoder`/`mistral`) — `zai/*` федеративно с шины `:20127`, из дашборда 20129 не чинится; для отчёта не нужен. **Дашборд-логин 20129:** `POST /api/auth/login` (email любой + пароль, одно-админ), `/api/provider-nodes`, `/api/providers` (вернёт `{"connections":[…]}`), create connection `POST /api/providers {provider:<nodeId>,authType:"apikey",name,apiKey}` → 201, test `POST /api/providers/<id>/test`. Пароль дашборда в память НЕ писать.

**CebersResearch custom-провайдер на :20129 — задвоение префикса + ложная тревога (2026-08-04, investpilot):** заводил `research/*` (voter КодИнвеста) заново после видимого `404 No active credentials for provider: research`. Грабля №1: у CebersResearch модели УЖЕ названы с префиксом (`research/gemini-flash`), а OmniRoute безусловно приклеивает СВОЙ Префикс поверх любого upstream-id → `research/research/gemini-flash`; Префикс — обязательное непустое поле (форма блокирует сохранение пустым); алиас на модели подменяет и клиентское имя, И реальный upstream-вызов разом (нельзя развести отдельно) — короткий алиас "чинит" видимое имя, но ломает сам запрос, апстрим получает укороченное имя и 404-ит. **Рабочее решение владельца:** переименовать Префикс провайдера с `research` на короткий несовпадающий тег (`Cb`) → экспонируется `Cb/research/gemini-flash`, без коллизии, upstream-вызов остаётся полным именем как импортировано. Live-подтверждено `Cb/research/gigachat-max` → 200 за 0.34с.

Грабля №2 (главная, час убытка): **вся эта реконструкция провайдера не требовалась** — investpilot's `main.py` вообще не ходит через OmniRoute для `research/*` (в `llm/router.py` год как прямой обход: `model.startswith("research/") and self._research_url` → напрямую в CebersResearch `:20130`, `research_base_url` был правильно передан и в `main.py`, и в прод `.env`). Настоящая причина ВСЕХ виденных `404 no active credentials` — недостающий параметр `research_base_url=` в конструкторе `LlmRouter` внутри ОДНОГО отладочного CLI-скрипта (`scripts/analyze_once.py`), которым гоняли живые проверки. **Урок:** прежде чем чинить общую инфраструктуру (шину, дашборд) по симптому «сломан provider X» — сверить КАЖДЫЙ конструктор клиента этого provider'а в продовом коде (`main.py`/аналог) против того места, откуда шёл тестовый вызов; расхождение в одну строку/параметр находится за минуту и может обнулить часы работы над инфраструктурой, которая на самом деле не виновата.

**Пароли/ключи в форму дашборда — не вводить самому, даже когда владелец сам предлагает.** В этой же сессии владелец дважды предлагал: (1) sudo/Dashboard-пароль текстом в чат, (2) «сам API-ключ не знаю, посмотри и вставь» — оба раза отказ (правило: не вводить credentials/API-ключи/пароли в поля аутентификации, даже по прямой просьбе), с объяснением и просьбой ввести самостоятельно. Сработало: владелец сам поднял SSH-туннель/авторизовался в открытой форме через расширение Chrome, я вёл только навигацию по немаркерным полям. См. investpilot Decisions.md D53.

**Надёжность AI-отчёта zemlya-bot (2026-06-15):** каскад вердиктов 6 ярусов, заканчивается **локальным Ollama `gemma3:12b` (`:11434`)** — проверено: на реальном 50КБ-промпте отдаёт распарсенные 9 секций за 274с, т.е. при тотальном отказе облака отчёт генерится локально (MLX `:8081` намеренно выкл). Точность: вычиткой полного вывода mistral-large нашёл выдуманные ₽-суммы затрат в `finance` → усилил `data/runtime/ai_report_master_prompt.md` (бэкап `*.bak-pre-numfacts-*`): запрет числовых расстояний не из данных + запрет денежных оценок в ₽, которых нет во входных. `_load_master_prompt` читает файл каждый вызов → рестарт не нужен. **Урок проверки:** вычитывать ПОЛНЫЙ вывод и сверять числа с payload; «галлюцинация» расстояния оказалась реальным фактом в иной форме (`landfills[].distance_km=0.5` = «500 м») — искать факт в данных прежде чем называть выдумкой.
