GNU General Public License v3.0

技术解读

以下内容由 GitRead 中文社区 编辑团队基于项目 README 与官方资料整理, 结合中文开发者的常见使用场景补充技术选型建议。具体以项目官方文档为准。

深度解析 Kurama622/llm.nvim: 一款强大的 Neovim 语言模型插件

项目背景与痛点

在现代编程环境中,集成语言模型(LLM)的能力已成为提升开发效率的一项重要功能。Neovim 作为一种高度可定制的高级终端编辑器,其提供了一系列强大的基本特性,因此集成语言模型显得尤为重要。Kurama622/llm.nvim 应运而生,它通过一系列命令与各种语言模型(如 ChatGPT、Copilot、ChatGLM、kimi、deepseek、openrouter 和本地模型)进行交互。此插件不仅解决了复杂代码生成和自动化编码问题,还实现了与 GitHub 模型的兼容性,极大提升了开发者的工作效率。

核心技术揭秘

架构与实现原理

项目背后的设计思想是提供一套非常通用且强大的接口,使用户能够与多种语言模型无缝交互。这一目标通过以下技术架构实现:

  1. 插件 API 架构
    采用模块化的 API 来处理语言模型请求,支持各种语言模型实现。为了高效管理和通信,其内部实现了一个 Command Bus 模式,以确保不同命令间的解耦与独立性。

  2. 通信与数据交互
    利用异步通信机制,与外界语言模型进行数据交换。针对不同的外部接口(如 RESTful API 和 WebSocket),llm.nvim 内部分布了适配层,实现了灵活的对接架构。不妨通过一张架构图来进一步描述其实现原理:

graph LR A[Neovim 命令] --> B[Command Handler] B -->|"参数解析"| C{"模型选择"} C -->|"本地模型"| D[调用本地模型] C -->|"外部模型"| E[调用外部API] D --> F[执行本地推理] E --> G[外部模型响应] F --> H[结果展示] G --> H H --> I[返回用户]
  1. 配置与扩展性
    提供高度可配置选项,使得用户可以指定不同模型的具体参数及行为。同时,开放插件 API,便于其他开发者扩展和适配更多模型。

功能亮点与差异

亮点
  • 广泛模型支持:支持多种语言模型,如 ChatGPT、Copilot、ChatGLM 等。
  • 灵活配置:提供丰富且易于配置的选项。
  • 命令直观:高度可读且易用的 Neovim 命令,让用户在编辑过程中轻松实现集成帮助。
差异

相较于其他 Neovim 插件,如仅支持某个特定语言服务的插件,Kurama622/llm.nvim 以其广泛的语言模型支持能力脱颖而出。其核心差异还在于对本地和外部语言模型的兼容性和高效性,使得用户能够灵活地根据实际需求灵活选择合适的服务。

应用场景与落地建议

此插件非常适合需要大量代码生成、自动代码补全、代码分析等自动化任务的场景。开发团队可以借助它来加速内部代码生成和审核流程,提高代码复用性。同时,基于其支持 GitHub 模型,还可以在内网环境下进行高效开发,尤其适合金融、科研等需要数据持续交付环境的领域。建议在试用过程中充分利用配置灵活性及适配外部模型的能力,同时注意控制权限和密钥安全管理,避免敏感数据泄露。

综合评价

Kurama622/llm.nvim 开发了一款在 Neovim 编辑环境中强大的语言模型插件,实现了与多种流行模型高效通信的目标。其丰富的功能和高度可配置的特性,提升了开发者的生产力,而模块化设计使得最终用户和开发者能够灵活选择和扩展支持的语言模型。然而,使用过程中也需注意模型选择和配置的优化,以及对外界API访问的安全管理,以避免不必要的风险。

关于

GitHub 原始描述(英文)

A large language model (LLM) plugin for Neovim, provides commands to interact with LLM (like ChatGPT, Copilot, ChatGLM, kimi, deepseek, openrouter and local llms). Support Github models.

描述取自 GitHub 官方 API,保持仓库作者书写的原文。

简要分析

热度分
552
价值分
299
活跃状态
活跃

语言占比

Lua
Vim Script

Release

README

llm.nvim

English | 简体中文


Important 重要

A large language model(LLM) plugin that allows you to interact with LLM in Neovim.

  1. Supports any LLM, such as GPT, GLM, Kimi, DeepSeek, Gemini, Qwen or local LLMs (such as ollama).
  2. Allows you to define your own AI tools, with different tools able to use different models.
  3. Most importantly, you can use free models provided by any platform (such as Copilot, GitHub models, SiliconFlow, openrouter, Cloudflare or other platforms).
Note 注意

The configurations of different LLMs (such as ollama, deepseek), UI configurations, and AI tools (including code completion) should be checked in the examples first. Here you will find most of the information you want to know. Additionally, before using the plugin, you should ensure that your LLM_KEY is valid and that the environment variable is in effect.

Additionally, you should also take a look at wiki and docs.

Contents

Screenshots

Chat

models | UI

Press ? can display the shortcut key help window

  • Float-UI

llm-float-ui

  • Split-UI

llm-split-ui

Quick Translation

enable_cword_context = true: Translate the text under the cursor in normal mode.

llm-translate

Explain Code

Streaming output | Non-streaming output

llm-explain-code

Ask

One-time, no history retained.

You can configure inline_assistant to decide whether to display diffs (default: show by pressing ‘d’).

llm-ask

Attach To Chat

You can configure inline_assistant to decide whether to display diffs (default: show by pressing ‘d’).

llm-attach

Optimize Code

llm-optimize-code

llm-optimize-compare-action

Generate Test Cases

test-case

AI Translation

llm-trans

Image Recognition

llm-images

Generate Git Commit Message

llm-git-commit-msg

Generate Doc String

llm-docstring

Web Search

/buffer | /file | @web_search

web-search

Diagnostic

Both disposable_ask_handler, attach_to_chat_handler, side_by_side_handler and action_handler can enable diagnostic features:

diagnostic = { min = vim.diagnostic.severity.HINT },
-- or
-- diagnostic = { vim.diagnostic.severity.WARN, vim.diagnostic.severity.ERROR },
-- see `:h diagnostic-severity`

diagnostic

Lsp

Note 注意

New features, still in continuous iteration.

Both disposable_ask_handler, attach_to_chat_handler, and action_handler can enable lsp features:

lsp = {
  cpp = { methods = { "definition", "declaration" } },
  python = { methods = { "definition" } },
  lua = { methods = { "definition", "declaration" } },

  root_dir = { {'pyproject.toml', 'setup.py' }, ".git" },
},

lsp

⬆ back to top

Installation

Dependencies

  • curl
  • fzf >= 0.37.0: Optional. Split style preview of session history and image recognition tool image selection depends on fzf(The author’s development environment is 0.39.0)
  • render-markdown.nvim: Optional. Better Markdown preview depends on this plugin.
{
    "MeanderingProgrammer/render-markdown.nvim",
    dependencies = {
      {
        "nvim-treesitter/nvim-treesitter",
        branch = "main",
        config = function()
          vim.api.nvim_create_autocmd("FileType", {
            pattern = { "llm", "markdown" },
            callback = function()
              vim.treesitter.start(0, "markdown")
            end,
          })
        end,
      },
      "nvim-mini/mini.icons",
    }, -- if you use standalone mini plugins
    ft = { "markdown", "llm" },

    config = function()
      require("render-markdown").setup({
        restart_highlighter = true,
        heading = {
          enabled = true,
          sign = false,
          position = "overlay", -- inline | overlay
          icons = { "󰎤 ", "󰎧 ", "󰎪 ", "󰎭 ", "󰎱 ", "󰎳 " },
          signs = { "󰫎 " },
          width = "block",
          left_margin = 0,
          left_pad = 0,
          right_pad = 0,
          min_width = 0,
          border = false,
          border_virtual = false,
          border_prefix = false,
          above = "▄",
          below = "▀",
          backgrounds = {},
          foregrounds = {
            "RenderMarkdownH1",
            "RenderMarkdownH2",
            "RenderMarkdownH3",
            "RenderMarkdownH4",
            "RenderMarkdownH5",
            "RenderMarkdownH6",
          },
        },
        dash = {
          enabled = true,
          icon = "─",
          width = 0.5,
          left_margin = 0.5,
          highlight = "RenderMarkdownDash",
        },
        code = { style = "normal" },
      })
    end,
  }

Preconditions

  1. Register on the official website and obtain your API Key (Cloudflare needs to obtain an additional account).

  2. Set the LLM_KEY (Cloudflare needs to set an additional ACCOUNT) environment variable in your zshrc or bashrc.

export LLM_KEY=<Your API_KEY>
export ACCOUNT=<Your ACCOUNT> # just for cloudflare
Websites of different AI platforms
Expand the table.
Platform Link to obtain api key Note
Cloudflare https://dash.cloudflare.com/ You can see all of Cloudflare’s models here, with the ones marked as beta being free models.
ChatGLM(智谱清言) https://open.bigmodel.cn/
Kimi(月之暗面) Moonshot AI 开放平台
Github Models Github Token
siliconflow (硅基流动) siliconflow You can see all models on Siliconflow here, and select ‘Only Free’ to see all free models.
Deepseek https://platform.deepseek.com/api_keys
Openrouter https://openrouter.ai/
Requesty https://app.requesty.ai/router OpenAI-compatible gateway. See examples/chat/requesty.
Chatanywhere https://api.chatanywhere.org/v1/oauth/free/render 200 free calls to GPT-4o-mini are available every day.

For local llms, Set LLM_KEY to NONE in your zshrc or bashrc.

⬆ back to top

Minimal installation example

  • lazy.nvim
  {
    "Kurama622/llm.nvim",
    dependencies = { "nvim-lua/plenary.nvim", "MunifTanjim/nui.nvim"},
    cmd = { "LLMSessionToggle", "LLMSelectedTextHandler", "LLMAppHandler" },
    config = function()
      require("llm").setup({
        url = "https://models.inference.ai.azure.com/chat/completions",
        model = "gpt-4o-mini",
        api_type = "openai"
      })
    end,
    keys = {
      { "<leader>ac", mode = "n", "<cmd>LLMSessionToggle<cr>" },
    },
  }
  • Mini.deps
require("mini.deps").setup()
MiniDeps.add({
        source = "Kurama622/llm.nvim",
        depends = { "nvim-lua/plenary.nvim", "MunifTanjim/nui.nvim" },
        cmd = { "LLMSessionToggle", "LLMSelectedTextHandler", "LLMAppHandler" },
})

require("llm").setup({
        url = "https://models.inference.ai.azure.com/chat/completions",
        model = "gpt-4o-mini",
        api_type = "openai"
})

Configure template

Configuration

Commands

Cmd Description
LLMSessionToggle Open/hide the Chat UI
LLMSelectedTextHandler Handle the selected text, the way it is processed depends on the prompt words you input
LLMAppHandler Call AI tools

Model Parameters

Expand the table.
Parameter Description Value
url Model entpoint String
model Model name String
api_type Result parsing format workers-ai | zhipu|
openai | ollama|
deepseek | copilot |
lmstudio
timeout The maximum timeout for a response (in seconds) Number
proxy Curl request proxy String (noproxy|<ip>:<port>) | nil
fetch_key API key string or Function that returns the API key Function | String
max_tokens Limits the number of tokens generated in a response. Number
temperature From 0 to 1.
The lower the number is, the more deterministic the response will be.
The higher the number is the more creative the response will be, but moe likely to go off topic if it’s too high
Number
top_p A threshold(From 0 to 1).
The higher the threshold is the more diverse and the less repetetive the response will be.
(But it could also lead to less likely tokens which also means: off-topic responses.)
Number
enable_thinking Activate the model’s deep thinking ability (The model itself needs to ensure this feature.) Boolean
thinking_budget The maximum length of the thinking process only takes effect when enable_thinking is true. Number
schema Function-calling required function parameter description Table
functions_tbl Function dict required for Function-calling Table
keep_alive Maintain connection (usually for ollama) see keep_alive/OLLAMA_KEEP_ALIVE
streaming_handler Customize the parsing format of the streaming output Function
parse_handler Customize the parsing format for non-streaming output Function

keymaps

Expand the table.
Style Keyname Description Default: [mode] keymap Window
float Input:Submit Submit your question [i] ctrl+g Input
float Input:Cancel Cancel dialog response [i] ctrl+c Input
float Input:Resend Rerespond to the dialog [i] ctrl+r Input
float Input:HistoryNext Select the next session history [i] ctrl+j Input
float Input:HistoryPrev Select the previous session history [i] ctrl+k Input
float Input:ModelsNext Select the next model [i] ctrl+shift+j Input
float Input:ModelsPrev Select the previous model [i] ctrl+shift+k Input
split Output:Ask Open the input box
In the normal mode of the input box, press Enter to submit your question)
[n] i Output
split Output:Cancel Cancel dialog response [n] ctrl+c Output
split Output:Resend Rerespond to the dialog [n] ctrl+r Output
float/split Session:Toggle Toggle session [n] <leader>ac Input+Output
float/split Session:Close Close session [n] <esc> float: Input+Output
split: Output
float/split Session:New Create a new session [n] <C-n> float: Input+Output
split: Output
float/split Session:Models Open the model-list window [n] ctrl+m float: App input window
split: Output
split Session:History Open the history window
move: same as fzf configuration
<cr>: select
<esc>: close
[n] ctrl+h Output
float Focus:Input Jump from the output window to the input window - Output
float Focus:Output Jump from the input window to the output window - Input
float PageUp Output Window page up [n/i] Ctrl+b Input
float PageDown Output window page down [n/i] Ctrl+f Input
float HalfPageUp Output Window page up (half) [n/i] Ctrl+u Input
float HalfPageDown Output window page down (half) [n/i] Ctrl+d Input
float JumpToTop Jump to the top (output window) [n] gg Input
float JumpToBottom Jump to the bottom (output window) [n] G Input

Tool

Handler name Description
side_by_side_handler Display results in two windows side by side
action_handler Display results in the source file in the form of a diff
qa_handler AI for single-round dialogue
flexi_handler Results will be displayed in a flexible window (window size is automatically calculated based on the amount of output text)
disposable_ask_handler Flexible questioning, you can choose a piece of code to ask about, or you can ask directly (the current buffer is the context)
attach_to_chat_handler Attach the selected content to the context and ask a question.
completion_handler Code completion
curl_request_handler The simplest interaction between curl and LLM is generally used to query account balance or available model lists, etc.

Each handler’s parameters can be referred to here.

Examples can be seen AI Tools Configuration

UI

See UI Configuration and nui/popup

⬆ back to top

Custom parsing function

For streaming output, we use our custom streaming_handler; for AI tools that return output results in one go, we use our custom parse_handler.

Below is an example of ollama running llama3.2:1b.

Expand the code.
local function local_llm_streaming_handler(chunk, ctx, F)
  if not chunk then
    return ctx.assistant_output
  end
  local tail = chunk:sub(-1, -1)
  if tail:sub(1, 1) ~= "}" then
    ctx.line = ctx.line .. chunk
  else
    ctx.line = ctx.line .. chunk
    local status, data = pcall(vim.json.decode, ctx.line)
    if not status or not data.message.content then
      return ctx.assistant_output
    end
    ctx.assistant_output = ctx.assistant_output .. data.message.content
    F.WriteContent(ctx.bufnr, ctx.winid, data.message.content)
    ctx.line = ""
  end
  return ctx.assistant_output
end

local function local_llm_parse_handler(chunk)
  local assistant_output = chunk.message.content
  return assistant_output
end

return {
  {
    "Kurama622/llm.nvim",
    dependencies = { "nvim-lua/plenary.nvim", "MunifTanjim/nui.nvim" },
    cmd = { "LLMSessionToggle", "LLMSelectedTextHandler" },
    config = function()
      require("llm").setup({
        url = "http://localhost:11434/api/chat", -- your url
        model = "llama3.2:1b",

        streaming_handler = local_llm_streaming_handler,
        app_handler = {
          WordTranslate = {
            handler = tools.flexi_handler,
            prompt = "Translate the following text to Chinese, please only return the translation",
            opts = {
              parse_handler = local_llm_parse_handler,
              exit_on_move = true,
              enter_flexible_window = false,
            },
          },
        }
      })
    end,
    keys = {
      { "<leader>ac", mode = "n", "<cmd>LLMSessionToggle<cr>" },
    },
  }
}

⬆ back to top

TODO List

todo-list

⬆ back to top

Author’s configuration

plugins/llm

Acknowledgments

We would like to express our heartfelt gratitude to the contributors of the following open-source projects, whose code has provided invaluable inspiration and reference for the development of llm.nvim:

Special thanks

ACKNOWLEDGMENTS

评论

暂无评论