Добавьте кастомного OpenAI-совместимого провайдера в OpenCode.
Updated 2026-07-29
OpenCode читает кастомных провайдеров прямо из opencode.json. Объявите блок provider с пакетом @ai-sdk/openai-compatible, укажите в options.baseURL https://api.apisrouter.com/v1 — и каждая модель, которую вы перечислите, станет доступной в пикере /models под одним ключом.
Короткий ответ: один блок provider в opencode.json.
OpenCode нативно поддерживает кастомных OpenAI-совместимых провайдеров. Добавьте запись provider в opencode.json с npm, установленным в «@ai-sdk/openai-compatible», задайте options.baseURL как https://api.apisrouter.com/v1, читайте ключ из переменной окружения через шаблон {env:...} и перечислите нужные ID моделей под models. Затем установите верхнеуровневое поле model в «apisrouter/<model-id>», и OpenCode проведёт весь цикл агента через шлюз. Это задокументированный путь кастомного провайдера в документации OpenCode, а не обёртка или форк. Конфиг-файл живёт либо в корне вашего проекта (opencode.json), либо глобально в ~/.config/opencode/opencode.json, и оба сливаются, так что блок provider можно объявить один раз и переиспользовать в каждом репозитории.
{
"$schema": "https://opencode.ai/config.json",
"provider": {
"apisrouter": {
"npm": "@ai-sdk/openai-compatible",
"name": "APIsRouter",
"options": {
"baseURL": "https://api.apisrouter.com/v1",
"apiKey": "{env:APISROUTER_API_KEY}"
},
"models": {
"claude-sonnet-4-6": { "name": "Claude Sonnet 4.6" }
}
}
},
"model": "apisrouter/claude-sonnet-4-6"
}Как OpenCode резолвит провайдеров и модели.
OpenCode (anomalyco на GitHub, один из самых отмеченных звёздами терминальных агентов для кода — около 186K звёзд) строит свой слой провайдеров на Vercel AI SDK. Поле npm в блоке provider называет, какой пакет SDK OpenCode загружает, чтобы говорить с этим провайдером: «@ai-sdk/openai-compatible» говорит по стандартному протоколу /v1/chat/completions, а «@ai-sdk/openai» — по протоколу /v1/responses от OpenAI. Мультивендорный шлюз обслуживает chat completions, так что правильный пакет — openai-compatible; выбор «@ai-sdk/openai» против эндпоинта chat-completions — самая частая причина, почему эта настройка ломается. Модели адресуются как пары provider/model. ID провайдера — это тот ключ, который вы выбрали в блоке provider («apisrouter» выше), а ID модели — это ключ внутри карты models, так что модель по умолчанию становится «apisrouter/claude-sonnet-4-6». Всё, что вы объявите, появится в пикере /models внутри TUI, переключаемое прямо посреди сессии. Одна особенность, которую стоит усвоить: для кастомных провайдеров карта models — это allowlist. Встроенные провайдеры поставляются с известным каталогом, но OpenCode не может самостоятельно перечислить модели кастомного эндпоинта, так что доступны только те ID, которые вы явно объявили. Когда эндпоинт за baseURL обслуживает ID Claude, GPT, DeepSeek и Kimi бок о бок, объявление по одной записи на модель превращает пикер в кросс-вендорный коммутатор за одним ключом.
Полная настройка: глобальный конфиг, конфиг проекта, лимиты по моделям.
Чистая раскладка — объявить провайдера один раз в глобальном конфиге ~/.config/opencode/opencode.json и держать в opencode.json каждого проекта только выборы на конкретный репозиторий (какая модель, какие агенты). OpenCode сливает конфиг-файлы, а не заменяет их, так что файл проекта остаётся крошечным, а блок provider никогда не дублируется. Шаблон {env:APISROUTER_API_KEY} резолвится при загрузке из окружения, что держит ключ вне любого файла, который может попасть в коммит. Экспортируйте его из профиля вашей оболочки, чтобы каждая терминальная сессия, запускающая OpenCode, могла его увидеть. Каждая запись модели также принимает объект limit с потолками токенов контекста и выхода. Их объявление важнее, чем кажется: OpenCode использует цифру контекста, чтобы решить, когда сессии нужна суммаризация, так что модель с длинным контекстом, объявленная без лимитов, обрабатывается более консервативно, чем следовало бы. Задайте limit.context равным тому, что модель реально поддерживает, и длинные сессии будут сжиматься позже, а не раньше.
{
"$schema": "https://opencode.ai/config.json",
"provider": {
"apisrouter": {
"npm": "@ai-sdk/openai-compatible",
"name": "APIsRouter",
"options": {
"baseURL": "https://api.apisrouter.com/v1",
"apiKey": "{env:APISROUTER_API_KEY}"
},
"models": {
"claude-opus-4-7": { "name": "Claude Opus 4.7", "limit": { "context": 200000, "output": 32000 } },
"claude-sonnet-4-6": { "name": "Claude Sonnet 4.6", "limit": { "context": 200000, "output": 64000 } },
"gpt-5.5": { "name": "GPT-5.5" },
"gpt-5.6-sol": { "name": "GPT-5.6 Sol" },
"kimi-k2.7-code": { "name": "Kimi K2.7 Code" }
}
}
},
"model": "apisrouter/claude-sonnet-4-6",
"small_model": "apisrouter/kimi-k2.7-code"
}Выбор model и small_model.
Практический рабочий процесс — держать основной слот на модели, которой доверяете для правок, и вращать кандидатов через реальные сессии, а не бенчмарки: вечер реальных диффов на вашей собственной кодовой базе говорит больше, чем любой лидерборд. Маршрутизация через один эндпоинт делает каждого кандидата правкой в одну строку, а вид использования по ключу показывает, во что реально обошёлся каждый эксперимент.
- model ведёт основной цикл агента: чтение файлов, планирование правок, написание диффов, запуск инструментов. Этот слот видит самые длинные контексты и делает реальную инженерную работу, так что здесь место флагманской модели для кода (claude-sonnet-4-6, claude-opus-4-7, gpt-5.5).
- small_model обрабатывает лёгкие задачи вроде генерации заголовков сессии. Он срабатывает часто, но никогда не несёт работу с кодом, так что правильная форма — быстрый, недорогой ID; нет причин сжигать флагманские токены на заголовки.
- ID, настроенные под код, вроде gpt-5.6-sol и kimi-k2.7-code, стоит объявить, даже если они не ваш дефолт: переключение на них ради насыщенной рефакторингом сессии — это один выбор в /models, а не правка конфига.
- Поскольку оба слота принимают строки provider/model против одного и того же блока provider, main и small слоты могут идти от разных вендоров в одной и той же сессии — то, что ни один однопровайдерный ключ выразить не может.
Оплата по факту · дешевле официальных цен
Selected models are priced below official list prices. Exact input, output, cache, and per-request prices are shown for each model.
| Модель | Официальная цена | Наша цена |
|---|---|---|
| Claude Sonnet 4.6 | $3.00 / $15.00 per M | $2.40 / $12.00 per M |
| Claude Opus 4.7 | $5.00 / $25.00 per M | $4.00 / $20.00 per M |
| GPT-5.5 | $5.00 / $30.00 per M | $4.00 / $24.00 per M |
| GPT-5.6 Sol | $5.00 / $30.00 per M | $4.00 / $24.00 per M |
| Kimi K2.7 Code | $0.95 / $4.00 per M | $1.00 / $4.00 per M |
Сбои, специфичные именно для кастомных провайдеров OpenCode.
Неверный пакет SDK. «@ai-sdk/openai» отправляет запросы на /v1/responses; шлюз с chat-completions отвечает на этот маршрут ошибкой. Если первый запрос падает с ошибкой в форме протокола или маршрута, а не ошибкой аутентификации, проверьте, что поле npm говорит именно «@ai-sdk/openai-compatible». Модель отсутствует в пикере. Модели кастомного провайдера существуют только если объявлены; опечатка в ключе models или ID, который вы предполагали, но так и не добавили, просто не появится в /models. ID — это точные строки, включая версионные суффиксы, а листинг /v1/models шлюза — источник истины, откуда их копировать. Неразрешённый {env:...}. Шаблон резолвится из окружения процесса, запустившего OpenCode. Ключ, экспортированный в одном терминале, не достигает инстанса OpenCode, запущенного из другого терминала или из десктопного лаунчера, который никогда не подгружал ваш профиль. Поместите export в профиль оболочки, а не в разовую сессию. Сюрпризы слияния конфигов. Поскольку глобальный и проектный конфиги сливаются, opencode.json проекта, устанавливающий model для другого провайдера, тихо переопределяет ваш глобальный дефолт, а забытый блок provider в старом проекте может маскировать ожидания. Когда маршрутизация выглядит неправильно, читайте оба файла, прежде чем предполагать, что шлюз ведёт себя неверно. baseURL без /v1. SDK дописывает пути маршрутов вроде /chat/completions к тому base, который вы дали, так что https://api.apisrouter.com/v1 верен, а голый хост — нет. Сбой соединения или в форме 404 при в остальном корректной конфигурации почти всегда именно это.
Кто направляет OpenCode через шлюз.
- Разработчики, живущие в TUI весь день и желающие видеть Claude, GPT и Kimi в одном пикере /models вместо поддержания отдельных учётных данных провайдера на каждого вендора.
- Инженеры, сравнивающие модели для кода на реальной работе. Каждый кандидат — это одна объявленная запись и один выбор в пикере; сравнение сессия за сессией не требует новых аккаунтов.
- Команды, стандартизирующие один секрет. Единый APISROUTER_API_KEY в документации онбординга заменяет чек-лист вендорских ключей, а использование по ключу показывает, кто сколько тратит.
- Пользователи, комбинирующие флагманскую main-модель с недорогой small_model от другого вендора — то, что однопровайдерные конфиги выразить не могут.
- Разработчики без доступа к биллингу конкретного вендора. Доступ на основе пополнения без требования карты убирает зависимость от регистрации у каждого провайдера.
Проверьте эндпоинт и отладьте первую сессию.
Перед началом сессии посмотрите, что обслуживает шлюз. ID, возвращаемые /v1/models, — это ровно те строки, с которыми должны совпадать ключи вашей карты models. Сбои первой сессии постоянны. 401 означает, что APISROUTER_API_KEY не был виден процессу OpenCode; выведите переменную через echo в том же терминале, из которого запускаете. Ошибка model-not-found от шлюза означает, что объявленный ключ не совпадает с обслуживаемым ID, включая версионные суффиксы. Если провайдер вообще не появляется, проверьте JSON: висящая запятая или неуместная скобка делают весь файл нечитаемым, и OpenCode откатывается к умолчаниям. Как только запросы пойдут, консоль APIsRouter покажет модель на запрос, счётчики токенов и расходы. Агенты для кода — это долгоконтекстные, многоходовые нагрузки, и наблюдение за тем, какие сессии и какие модели потребляют токены, — это способ решить, оправдывает ли основной слот свою цену.
curl -s https://api.apisrouter.com/v1/models \
-H "Authorization: Bearer $APISROUTER_API_KEY" | head -50Частые вопросы
Может ли OpenCode использовать модели Claude, GPT и Kimi через одного кастомного провайдера?
Да. Кастомный провайдер — это просто baseURL плюс allowlist моделей. Когда эндпоинт обслуживает несколько вендоров, объявите по одной записи на ID, и каждая объявленная модель появится в пикере /models под тем же провайдером и ключом, переключаемая прямо посреди сессии.
Куда вставлять API-ключ в opencode.json?
В options.apiKey с использованием шаблона окружения, например «{env:APISROUTER_API_KEY}». Шаблон резолвится при загрузке, так что буквальный ключ никогда не оказывается в конфиг-файле. Экспортируйте переменную из профиля оболочки, чтобы каждый терминал, запускающий OpenCode, унаследовал её.
Должен ли блок provider жить в глобальном или проектном конфиге?
Глобально, в ~/.config/opencode/opencode.json. OpenCode сливает конфиг-файлы, так что объявление провайдера один раз глобально и установка только выбора модели на проект избавляет репозитории от возни с учётными данными и не даёт дублированным блокам расходиться.
Почему моя модель не появляется в пикере /models?
Модели кастомного провайдера должны быть объявлены явно; OpenCode не может перечислить кастомный эндпоинт. Проверьте, что карта models содержит точную строку ID, включая версионные суффиксы, и копируйте ID из ответа /v1/models шлюза, а не набирайте по памяти.
В чём разница между @ai-sdk/openai-compatible и @ai-sdk/openai здесь?
@ai-sdk/openai-compatible говорит по /v1/chat/completions — протоколу, который обслуживают мультивендорные шлюзы. @ai-sdk/openai говорит по протоколу /v1/responses от OpenAI. Для APIsRouter используйте @ai-sdk/openai-compatible; другой пакет будет отправлять запросы на маршрут, который шлюз для этой цели не обслуживает.
Действительно ли важны объявленные лимиты контекста?
Да. OpenCode использует limit.context, чтобы решить, когда сессии нужна компактификация. Необъявленные лимиты на модели с длинным контекстом означают, что сессии суммаризируются раньше, чем необходимо, так что задайте limit.context и limit.output равными тому, что модель реально поддерживает.