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:
export FETCH_API_KEY="fetch_ваш_ключ"После этого в Lua-конфиге ключ достаётся через os.getenv("FETCH_API_KEY") или vim.env.FETCH_API_KEY.
avante.nvim: провайдер claude
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)
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
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-конфига
Если после настройки плагин молчит или падает с непонятной ошибкой, быстрее сначала проверить ключ напрямую:
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.
Похожие статьи
- JetBrains AI Assistant не работает: причины и обходные путиAI Assistant не активируется, молчит или недоступен в отдельных файлах. Разбираем причины по документации JetBrains и подключаем свои модели через шлюз.
- DeepSeek в VS Code: подключение через Cline, Kilo Code и ContinueПодключаем DeepSeek к Cline, Kilo Code и Continue с одним ключом, выбираем между Flash и Pro и считаем, сколько стоит рабочий день с агентом в редакторе.
- Аналоги GitHub Copilot со своим API-ключом: что выбратьCline, Kilo Code, Continue и Cursor работают со своим ключом. Сравниваем, куда они встроены, как подключаются к шлюзу и как считать расход.
- Invalid API key в opencode: почему ключ не принимается и как проверитьОшибка Invalid API key в opencode почти всегда сводится к четырём причинам. Проверяем ключ отдельно от opencode и находим, где ломается цепочка.