Neovim: подключение avante.nvim и codecompanion

У avante.nvim и codecompanion.nvim разная логика адресов под капотом — один провайдер ждёт /v1, другой нет. Разбираем обе конфигурации построчно.

4 мин · · Обновлено · IDE · терминал

У avante.nvim и codecompanion.nvim разная внутренняя логика для провайдеров, и это напрямую влияет на то, что писать в поле адреса. Провайдер claude в avante собран под Anthropic-совместимый протокол и сам достраивает путь /v1/messages — базовый адрес нужен без суффикса. Провайдер openai в avante, наоборот, ждёт готовый префикс /v1 в самом адресе, а адаптер openai_compatible в codecompanion сам дописывает /v1/chat/completions — ему нужен адрес без суффикса. Перепутать эти случаи — самый быстрый способ получить 404 сразу после установки.

Переменная окружения для ключа

Оба плагина читают ключ из переменной окружения, а не хранят его в конфиге прямым текстом. В .zshrc или .bashrc:

bash
export FETCH_API_KEY="fetch_ваш_ключ"

После этого в Lua-конфиге ключ достаётся через os.getenv("FETCH_API_KEY") или vim.env.FETCH_API_KEY.

avante.nvim: провайдер claude

bash
require("avante").setup({
  provider = "claude",
  providers = {
    claude = {
      endpoint = "https://api.zukko.pro",
      model = "claude-sonnet-5",
      api_key_name = "FETCH_API_KEY",
      extra_request_body = {
        max_tokens = 4096,
      },
    },
  },
})

Обратите внимание: endpoint — без /v1. Провайдер claude в avante сам обращается к /v1/messages и сам добавляет заголовок anthropic-version: 2023-06-01. Если сюда по привычке дописать /v1, каждый запрос уйдёт на .../v1/v1/messages и вернётся 404.

avante.nvim: провайдер openai (если нужна модель не из линейки Claude)

bash
require("avante").setup({
  provider = "openai",
  providers = {
    openai = {
      endpoint = "https://api.zukko.pro/v1",
      model = "gpt-5.6-sol",
      api_key_name = "FETCH_API_KEY",
    },
  },
})

Здесь наоборот — /v1 в адресе обязателен, потому что провайдер openai бьёт напрямую в /chat/completions без собственной логики достраивания пути.

codecompanion.nvim: адаптер openai_compatible

bash
require("codecompanion").setup({
  adapters = {
    zukko = function()
      return require("codecompanion.adapters").extend("openai_compatible", {
        env = {
          url = "https://api.zukko.pro",
          api_key = "FETCH_API_KEY",
        },
        schema = {
          model = {
            default = "glm-5.3",
          },
        },
      })
    end,
  },
  strategies = {
    chat = { adapter = "zukko" },
    inline = { adapter = "zukko" },
  },
})

Адаптер openai_compatible в codecompanion — общий для всех совместимых с OpenAI бэкендов и сам добавляет к url путь /v1/chat/completions, поэтому url указывается без /v1. Если дописать суффикс, запросы уйдут на .../v1/v1/chat/completions и вернутся 404.

Проверка ключа до правки Lua-конфига

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

bash
curl https://api.zukko.pro/v1/messages \
  -H "Authorization: Bearer $FETCH_API_KEY" \
  -H "Content-Type: application/json" \
  -H "anthropic-version: 2023-06-01" \
  -d '{"model":"claude-sonnet-5","max_tokens":64,"messages":[{"role":"user","content":"ping"}]}'

Если curl отвечает, а плагин нет — ошибка в конфиге Lua, а не в ключе или балансе.

Какую модель под какую задачу в редакторе

Для разбора незнакомого репозитория и вопросов уровня «как тут в целом всё устроено» подходит claude-opus-5-5 — контекст 1,000,000 токенов у моделей Claude и GPT (у GLM — 128K) позволяет закинуть в запрос десятки файлов сразу, а extended thinking у Opus 5.5 полезен именно для архитектурных вопросов, а не построчных правок. Для рутинных задач вроде «добавь тип в эту функцию» или автодополнения внутри codecompanion разумнее glm-5.3-flash — она заметно дешевле ($0.04 против $0.3 за миллион токенов); устраивает ли качество на коротких точечных запросах, проверьте на своих задачах.

Кэш при повторных запросах без изменений в файле

Если вы гоняете один и тот же промпт на одном и том же куске кода несколько раз подряд (например, повторно запускаете генерацию докстринга, не меняя ни файл, ни формулировку задания), одинаковые запросы кэшируются на стороне шлюза и возвращаются мгновенно и бесплатно — в ответе будет X-Zukko-Cache: hit. Основной способ включить кэш — заголовок X-Zukko-Cache: true, он работает для любой модели; temperature: 0 тоже включает кэш, но только для моделей, которые принимают это значение (новые модели Claude — claude-sonnet-5, claude-opus-5-5 — могут отклонить temperature, отличную от 1, с ошибкой 400). Кэш срабатывает только при полном совпадении тела запроса: изменённое слово в задании или строка в файле — это уже новый запрос. Попадания в кэш не пишутся в /v1/usage. Ни avante, ни codecompanion не показывают этот заголовок в интерфейсе, но если правите Lua-обвязку сами и логируете сырой ответ, это легко проверить.

Идемпотентность при обрыве SSH-сессии

Если Neovim работает по SSH и сессия рвётся посреди запроса, повторная отправка того же промпта — обычное дело. Заголовок Idempotency-Key с уникальным значением на каждый логический запрос защищает от двойного списания: повтор с тем же ключом идемпотентности в течение 24 часов вернёт тот же ответ, если исходный запрос на самом деле уже выполнился на сервере. Ни один из двух плагинов не генерирует такой заголовок из коробки — добавить его можно, только если вы пишете собственную тонкую обвязку вокруг API вместо штатного адаптера.

Отдельный ключ для рабочей машины

Разумно завести для Neovim свой ключ с дневным лимитом расхода и списком разрешённых моделей — настраивается на странице ключа в кабинете. Если в конфиге случайно останется цикл автодополнения или скрипт для батч-рефакторинга по всему проекту, лимит остановит расход раньше, чем это будет замечено в конце месяца.

Если модель временно отключена

Ответ 503 provider_unavailable или model_disabled означает, что конкретная модель на время выключена в каталоге — ни avante, ни codecompanion не отличают эту ситуацию от прочих ошибок сети и просто покажут сообщение об ошибке в окне чата. Быстрее всего временно поменять значение model в Lua-конфиге на соседнюю модель из того же семейства и перезагрузить конфигурацию плагина (:so % или перезапуск Neovim), а не тратить время на разбор логов в поисках несуществующей проблемы с сетью.

Формат тела ответа отличается у двух протоколов

Ещё один источник путаницы при отладке через curl: /v1/messages возвращает тело в формате Anthropic (поле content — массив блоков), а /v1/chat/completions — в формате OpenAI (поле choices с вложенным message.content). Если пишете собственный парсер ответа поверх стандартного адаптера — например, чтобы вытащить только текст без метаданных — эти два формата не взаимозаменяемы, и код, писанный под один, упадёт на другом с ошибкой доступа к несуществующему полю.

zukko

Платите в 25×
меньше

за Claude Code & ChatGPT

Один ключ fetch_* и base URL api.zukko.pro — в Cursor, Claude Code, Codex и любом SDK.

Neovim + свой API-ключ: avante.nvim и codecompanion · Zukko