Contents

June 20, 2026 · View on GitHub

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

/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.
PlatformLink to obtain api keyNote
Cloudflarehttps://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 ModelsGithub Token
siliconflow (硅基流动)siliconflowYou can see all models on Siliconflow here, and select 'Only Free' to see all free models.
Deepseekhttps://platform.deepseek.com/api_keys
Openrouterhttps://openrouter.ai/
Requestyhttps://app.requesty.ai/routerOpenAI-compatible gateway. See examples/chat/requesty.
Chatanywherehttps://api.chatanywhere.org/v1/oauth/free/render200 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

CmdDescription
LLMSessionToggleOpen/hide the Chat UI
LLMSelectedTextHandlerHandle the selected text, the way it is processed depends on the prompt words you input
LLMAppHandlerCall AI tools

Model Parameters

Expand the table.
ParameterDescriptionValue
urlModel entpointString
modelModel nameString
api_typeResult parsing formatworkers-ai | zhipu|
openai | ollama|
deepseek | copilot |
lmstudio
timeoutThe maximum timeout for a response (in seconds)Number
proxyCurl request proxyString (noproxy|<ip>:<port>) | nil
fetch_keyAPI key string or Function that returns the API keyFunction | String
max_tokensLimits the number of tokens generated in a response.Number
temperatureFrom 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_pA 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_thinkingActivate the model's deep thinking ability (The model itself needs to ensure this feature.)Boolean
thinking_budgetThe maximum length of the thinking process only takes effect when enable_thinking is true.Number
schemaFunction-calling required function parameter descriptionTable
functions_tblFunction dict required for Function-callingTable
keep_aliveMaintain connection (usually for ollama)see keep_alive/OLLAMA_KEEP_ALIVE
streaming_handlerCustomize the parsing format of the streaming outputFunction
parse_handlerCustomize the parsing format for non-streaming outputFunction

keymaps

Expand the table.
StyleKeynameDescriptionDefault: [mode] keymapWindow
floatInput:SubmitSubmit your question[i] ctrl+gInput
floatInput:CancelCancel dialog response[i] ctrl+cInput
floatInput:ResendRerespond to the dialog[i] ctrl+rInput
floatInput:HistoryNextSelect the next session history[i] ctrl+jInput
floatInput:HistoryPrevSelect the previous session history[i] ctrl+kInput
floatInput:ModelsNextSelect the next model[i] ctrl+shift+jInput
floatInput:ModelsPrevSelect the previous model[i] ctrl+shift+kInput
splitOutput:AskOpen the input box
In the normal mode of the input box, press Enter to submit your question)
[n] iOutput
splitOutput:CancelCancel dialog response[n] ctrl+cOutput
splitOutput:ResendRerespond to the dialog[n] ctrl+rOutput
float/splitSession:ToggleToggle session[n] <leader>acInput+Output
float/splitSession:CloseClose session[n] <esc>float: Input+Output
split: Output
float/splitSession:NewCreate a new session[n] <C-n>float: Input+Output
split: Output
float/splitSession:ModelsOpen the model-list window[n] ctrl+mfloat: App input window
split: Output
splitSession:HistoryOpen the history window
move: same as fzf configuration
<cr>: select
<esc>: close
[n] ctrl+hOutput
floatFocus:InputJump from the output window to the input window-Output
floatFocus:OutputJump from the input window to the output window-Input
floatPageUpOutput Window page up[n/i] Ctrl+bInput
floatPageDownOutput window page down[n/i] Ctrl+fInput
floatHalfPageUpOutput Window page up (half)[n/i] Ctrl+uInput
floatHalfPageDownOutput window page down (half)[n/i] Ctrl+dInput
floatJumpToTopJump to the top (output window)[n] ggInput
floatJumpToBottomJump to the bottom (output window)[n] GInput

Tool

Handler nameDescription
side_by_side_handlerDisplay results in two windows side by side
action_handlerDisplay results in the source file in the form of a diff
qa_handlerAI for single-round dialogue
flexi_handlerResults will be displayed in a flexible window (window size is automatically calculated based on the amount of output text)
disposable_ask_handlerFlexible 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_handlerAttach the selected content to the context and ask a question.
completion_handlerCode completion
curl_request_handlerThe 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