opencode-jev-router — opencode сам выбирает глубину размышлений

· 3 мин чтения
opencode routing llm tokens proxy
📂 Исходный код на GitHub

Плагин для opencode и отдельный HTTP-прокси (MIT), которые спрашивают у модели Jev, какой уровень размышлений нужен на каждый запрос, и подставляют его в запрос к GPT-6 или Claude. Простые правки уходят модели с низким уровнем, сложные — с высоким, и качество ответов не падает

opencode-jev-router — opencode сам выбирает глубину размышлений

Плагин для opencode, который снимает с вас ручной выбор глубины размышлений. Перед каждым запросом он спрашивает у модели Jev, сколько размышлений нужно именно на эту задачу, и подставляет выбранный уровень в запрос к основной модели. Переименование переменной получит low, разбор падающего теста — high.

Проект написан на TypeScript, распространяется под лицензией MIT и опубликован в npm как @robertn702/opencode-jev-router. Открытый код: репозиторий — robertn702/opencode-jev-router.

Зачем это нужно

У reasoning-моделей есть уровень размышлений. Он напрямую влияет на скорость ответа и на расход токенов. Обычно его задают один раз в настройках модели — и живут с этим.

Но задачи в opencode очень разные по сложности. Переименовать переменную и разобраться, почему падает тест, — это разный объём работы. На простой задаче высокий уровень сжигает токены впустую. На сложной низкий уровень не даёт добраться до причины. Авторы проекта предлагают решать это не вручную, а отдельной дешёвой моделью.

Как это работает

Jev — не обычная LLM. Это специализированная модель класса System One от TypeSafe. Вместо развёрнутого текста она возвращает типизированный выбор с откалиброванными вероятностями за доли секунды. Здесь её используют ровно для одной задачи: выбрать уровень размышлений.

Дальше по шагам:

  1. Плагин отправляет Jev ограниченную сводку последних сообщений диалога. Jev возвращает уровень: например low для переименования и high для падающего теста.
  2. Выбор добавляется в запрос отдельным элементом configuration_update от OpenAI. Для Claude используется системное сообщение, которое меняет только уровень. Старые обновления остаются на месте, чтобы начало промпта не ломалось о кеш.
  3. Запрос уходит на ваш endpoint, ответ возвращается без изменений.

Если Jev не ответил или ответил медленно, запрос уходит с резервным уровнем. По умолчанию это high.

Что нужно для запуска

  • opencode V2 версии 2.0.4 или новее. Проверено на 2.0.4 и 2.0.18. Для opencode V1 есть отдельная версия @robertn702/opencode-jev-router@0.5, либо можно использовать прокси (см. ниже).
  • Ключ Jev. Либо напрямую от TypeSafe, либо через Vercel AI Gateway. Каждый запрос к модели даёт один вызов классификатора, и он тарифицируется отдельно.
  • Endpoint, который говорит на Responses API. Он должен отдавать GPT-6 Astra, Luna или Sol и принимать элементы configuration_update, плюс нужен ключ к нему. Официальный API OpenAI подходит. Подойдёт и любой шлюз с тем же интерфейсом POST /v1/responses, например CLIProxyAPI, который работает по подписке ChatGPT.
  • Для Claude вместо GPT-6: endpoint Anthropic Messages API с одной из пяти перечисленных ниже моделей и доступом к бета-возможности смены настроек в середине диалога.

Node.js версии 24.x нужен только для отдельного прокси и для разработки. Сам плагин запускается вместе с opencode.

Быстрый старт

1. Экспортируйте ключи в том терминале, где запускается opencode:

export JEV_API_KEY=...   # TypeSafe key or Vercel AI Gateway key
export OPENAI_API_KEY=... # key for your Responses API endpoint

2. Добавьте плагин в конфиг opencode. Это либо ~/.config/opencode/opencode.json для всех проектов, либо opencode.json в корне проекта:

{
  "$schema": "https://opencode.ai/config.json",
  "plugins": [{ "package": "@robertn702/opencode-jev-router", "options": {
    "jevApiKey": "{env:JEV_API_KEY}",
    "jevBaseUrl": "https://ai-gateway.vercel.sh/typesafe",
    "wrap": { "openai": ["openai/gpt-6-astra"] },
    "decisionsLogPath": "/tmp/jev-decisions.jsonl"
  }}],
  "model": "jev-router/gpt-6-astra"
}

Если у вас ключ от TypeSafe напрямую, удалите строку jevBaseUrl. Ключ работает только со своим endpoint.

{env:NAME} читает переменную окружения, а {file:~/path} читает файл — если удобнее держать ключи на диске.

Готовый пример конфига лежит в репозитории: examples/opencode.jsonc.

3. Перезапустите opencode и убедитесь, что модель появилась как jev-router/gpt-6-astra. Список моделей можно открыть командой /models.

4. Проверьте, что маршрутизация работает. Отправьте любой запрос, затем выполните:

tail -n 1 /tmp/jev-decisions.jsonl

В выводе должно быть событие JevDecision с заполненным полем "effort" и значением "fallback": null. Непустое fallback означает, что до Jev не дошли. Строку decisionsLogPath можно убрать, когда всё заработало, либо оставить и собирать метрики.

Свой шлюз подключается так же: описываете его как провайдер в opencode и оборачиваете его модель. Обычный http:// допустим только для адресов на локальной машине.

Поддерживаемые модели

Плагин регистрирует псевдонимы вида jev-router/<профиль> только для тех моделей, которые перечислены в wrap. Исходные модели при этом не меняются. Псевдонимы наследуют маршрут, настройки, заголовки, лимиты и стоимость исходной модели, но ручных вариантов уровня у них нет.

Модель в opencode Уровни, которые может выбрать Jev
jev-router/gpt-6-astra low, medium, high, xhigh, max
jev-router/gpt-6-luna none, low, medium, high, xhigh, max
jev-router/gpt-6-sol none, low, medium, high, xhigh, max
jev-router/gpt-6.1-sol low, medium, high, xhigh, max
jev-router/claude-fable-5-1 low, medium, high, xhigh, max
jev-router/claude-mythos-5-1 low, medium, high, xhigh, max
jev-router/claude-opus-5-5 low, medium, high, xhigh, max
jev-router/claude-opus-5 low, medium, high, xhigh, max
jev-router/claude-sonnet-5-5 low, medium, high, xhigh, max

Что уходит в Jev, а что остаётся у вас

Это главный вопрос при подключении любого внешнего классификатора, и авторы проекта отвечают на него прямо.

  • В Jev уходят ограниченные фрагменты последних сообщений пользователя и ассистента, до восьми последних результатов инструментов с именами и признаком ошибки, короткая сводка о сбое и идентификатор модели. Данные от размещённых на стороне инструментов и от computer use не отправляются.
  • На ваш endpoint уходит полный запрос opencode с добавленным обновлением уровня.
  • Локально по умолчанию ничего не пишется. Если задан decisionsLogPath, туда попадает только метаинформация: идентификаторы, модель, уровень, задержка и счётчики токенов. Промпты, вывод инструментов, ключи и сырые ошибки не логируются.

Настройки

Параметры плагина живут в поле options у записи plugins.

Параметр По умолчанию Зачем
jevApiKey переменная JEV_API_KEY Ключ классификатора Jev. Обязателен
jevBaseUrl https://api.typesafe.ai Для ключа Vercel поменять на https://ai-gateway.vercel.sh/typesafe. Другие значения не принимаются
wrap нет Обязательный непустой объект с массивами openai и/или anthropic
decisionsLogPath выключено Абсолютный путь для файла JevDecision в формате JSONL
baseEffort medium Уровень, который отдаётся в ответе на уровне всего запроса
jevTimeoutMs 4000 Общий бюджет времени на классификацию вместе с повторами
maxRetries 1 Сколько дополнительных попыток при временных ошибках Jev
fallbackMode fixed fixed, previous или error — последнее означает отказ запроса
fallbackEffort high Уровень, который используется, если классификация не удалась
maxRequestBytes 1048576 Максимальный размер тела запроса
maxInFlight 32 Сколько запросов может идти одновременно
upstreamHeaderTimeoutMs 10000 Сколько ждать заголовки ответа от основной модели
upstreamIdleTimeoutMs 60000 Максимальная пауза между кусками потока

Опции maxRetries, fallbackMode и fallbackEffort появились в версии 0.3.0. Подробности про повторы и резервное поведение описаны в документе о политике классификации.

Если Jev недоступен

Резервный путь срабатывает автоматически, и это самое частое, что стоит проверить в первую очередь.

Что видите Что делать
В логе fallback равно jev_error с jev_error_category: "http_auth" Ключ Jev не подходит к endpoint. Для ключа Vercel нужен jevBaseUrl, для ключа TypeSafe его надо убрать
В логе fallback равно jev_timeout Jev отвечал медленно или был недоступен. Запросы всё равно ушли с резервным уровнем. Если это происходит часто, поднимите jevTimeoutMs
В логе opencode failed to load plugin Проверьте синтаксис wrap и уберите опции, удалённые в новых версиях
Model unavailable: jev-router/... Плагин не загрузился. Смотрите запись plugins и лог opencode
Ответ модели 401, 403 или 404 Ответ endpoint передаётся без изменений. Проверяйте ключ провайдера и имя модели
Локальная ошибка 400 про reasoning.mode или truncation Запрос использует неподдерживаемый режим или модель
uses OAuth или has no API key Встроенный OAuth по подписке не поддерживается. Нужен ключ API либо шлюз с ключевой авторизацией, например CLIProxyAPI
Изменения конфига ничего не дали Перезапустите opencode. Плагины загружаются только при старте

Прокси вместо плагина

Тот же роутер можно запустить как локальный HTTP-прокси для любого клиента Responses API. Это нужно Node.js 24.x.

Создайте файл .env в папке, из которой будете запускать, или экспортируйте переменные:

JEV_API_KEY=your-jev-key
# JEV_BASE_URL=https://ai-gateway.vercel.sh/typesafe   # Vercel keys only
JEV_ROUTER_UPSTREAM_BASE_URL=https://api.openai.com/v1
JEV_ROUTER_UPSTREAM_AUTH=bearer
JEV_ROUTER_UPSTREAM_API_KEY=your-endpoint-key

Запуск и проверка:

npx --yes @robertn702/opencode-jev-router

curl --fail http://127.0.0.1:4320/ready
curl http://127.0.0.1:4320/v1/responses \
  -H 'content-type: application/json' -H 'authorization: Bearer unused' \
  -d '{"model":"gpt-6-astra","input":[{"role":"user","content":"Say hi"}]}'

Прокси слушает http://127.0.0.1:4320. Для Anthropic задайте JEV_ROUTER_ANTHROPIC_UPSTREAM_BASE_URL и JEV_ROUTER_UPSTREAM_API_KEY для режима bearer, а запросы отправляйте на POST /v1/messages. Каждый запрос печатает в stdout запись с метаинформацией. Полный список переменных — по npx @robertn702/opencode-jev-router --help, значения по умолчанию — в .env.example.

До версии 0.3.0 переменные назывались без префикса JEV_ROUTER_. Разница описана в CHANGELOG.

Что показали замеры

Замеры авторов приведены вместе с их оговорками — приводим их без изменений.

  • На задаче pytest #5262 Jev потратил на 42% меньше выходных токенов и на 40% меньше времени, чем фиксированный high. Обе настройки решили задачу в 6 случаях из 6.
  • На отдельно выбранной сложной задаче Jev решил 5 случаев из 5, а фиксированный medium — только 1 из 5. Авторы прямо пишут, что такой разовый результат не доказывает общую пользу.
  • На наборе задач GPT-6 Astra Jev и фиксированный high решили по 44 попытки из 44. Jev при этом использовал на 14% меньше выходных токенов вместе с размышлениями и в среднем заканчивал на 9% быстрее.

На других задачах результат может отличаться — это написано в README проекта. Подробные цифры и методика лежат в описании замеров.

Ограничения

Для GPT-6 поддерживается только обычный режим с одним агентом. Pro-модели, другие значения reasoning.mode и режим truncation: "auto" отклоняются на месте. Собственные варианты уровня размышлений в opencode для этого провайдера игнорируются, потому что уровень выбирает Jev. В ответе при этом указывается базовый уровень medium, а не выбранный — смотреть нужно в журнал решений.

Встроенный OAuth по подписке ChatGPT и Claude не поддерживается. Нужен ключ API либо шлюз с ключевой авторизацией.

Запросы к Anthropic не следуют редиректам: ответ с кодом 3xx возвращается вызывающему, чтобы ключи не ушли на другой домен. Данные о том, как меняется уровень и что происходит с кешем промпта, собраны в документе о поведении.

Плагину нужен транспорт HTTP. Если вручную переопределить его на websocket, плагин не станет работать молча — он честно откажется обрабатывать запрос.

Лицензия и предшественники

Код распространяется под лицензией MIT. Идея подсмотрена в jev-codex-router и pi-jev-router — авторы указывают, что код оттуда не копировали. Пакет лежит в npm: @robertn702/opencode-jev-router.

Источник: https://github.com/robertn702/opencode-jev-router