minuet-ai.nvim

GitHub
1.1k 50 简单 1 次阅读 今天GPL-3.0语言模型插件开发框架Agent图像
AI 解读 由 AI 自动生成,仅供参考

minuet-ai.nvim 是一款专为 Neovim 打造的智能代码补全插件,旨在让开发者在编码时如同与智能伙伴共舞般流畅。它解决了传统静态补全无法理解复杂上下文的问题,通过实时连接 OpenAI、Claude、Gemini、Ollama 及本地 Llama.cpp 等主流大语言模型,提供精准的“边写边补”体验。

该工具特别适合追求高效工作流的软件开发者和技术极客。其核心亮点在于灵活的架构设计:既支持基于聊天的通用模型进行专用提示词优化,也兼容 DeepSeek、Codestral 等模型的“中间填充”(FIM)模式。minuet-ai.nvim 无需后台运行专有二进制文件,仅依赖轻量级网络请求即可工作,并具备流式传输能力,即使在使用较慢的模型时也能保持响应流畅。

此外,它深度集成了 nvim-cmp、blink-cmp 及 Neovim 原生补全前端,甚至可作为进程内 LSP 服务器运行。独特的“逐行接受”功能允许用户按自己的节奏采纳多行建议,而当用户输入与建议开头匹配时,插件会自动同步而非丢弃结果,有效减少了不必要的 API 调用和资源消耗。无论是云端强大模型还是本地私有部署,minuet-ai.nvim 都能帮助开发者提升编码效率,享受更智能的编写体验。

使用场景

一位后端工程师正在 Neovim 中紧急重构一个复杂的异步数据处理模块,需要频繁编写重复的样板代码和调用新的第三方 API。

没有 minuet-ai.nvim 时

  • 上下文切换频繁:遇到不熟悉的 API 参数或复杂逻辑时,必须离开编辑器去浏览器搜索文档或询问聊天机器人,打断心流。
  • 样板代码手写低效:大量的错误处理、日志记录和类型定义需要逐行手动敲击,不仅耗时且容易因疲劳产生拼写错误。
  • 模型适配困难:想要尝试本地运行的 Qwen 模型以保护隐私,却苦于缺乏便捷的插件将 Ollama 或 Llama.cpp 直接集成到补全工作流中。
  • 长建议难以采纳:面对多行代码建议,传统工具要么一次性全部接受(可能包含不需要的部分),要么完全拒绝,无法按需逐行吸收。

使用 minuet-ai.nvim 后

  • 智能随写随补:minuet-ai.nvim 利用 FIM(中间填充)技术,根据前后文实时预测并生成完整的函数实现,工程师无需离开键盘即可获取精准的 API 调用示例。
  • 流式输出零等待:即使连接较慢的本地大模型,流式传输功能也能让代码逐个字符显现,大幅降低感知延迟,保持编码节奏流畅。
  • 灵活的多模型支持:通过简单配置即可在 OpenAI、Claude 和本地 Ollama 之间无缝切换,既享受云端模型的强大推理,又能在敏感项目中启用本地私有模型。
  • 增量式建议采纳:面对多行重构建议,工程师可以逐行确认接受,完美匹配自己的思考速度,避免引入多余代码,同时已输入内容若与建议重合会自动同步,减少无效请求。

minuet-ai.nvim 将大模型的智能深度融入 Neovim 编辑体验,让开发者在“人机共舞”中显著提升编码效率与专注度。

运行环境要求

操作系统
  • Linux
  • macOS
  • Windows
GPU
  • 非必需(取决于所选 LLM 提供商
  • 若使用本地 Ollama 或 llama.cpp 则需相应 GPU 支持,插件本身无特定显卡要求)
内存

未说明(取决于所选模型大小,插件本身无特定内存要求)

依赖
notes该工具是纯 Lua 编写的 Neovim 插件,无需后台二进制程序。核心依赖为 Neovim 0.10+ 和 curl。若使用内置补全功能需 Neovim 0.11+,行内补全需 0.12+。需配置至少一个 AI 提供商的 API Key(如 OpenAI, Claude, Ollama 等)。不再需要 plenary.nvim。
python未说明
Neovim 0.10+
curl
nvim-cmp (可选)
blink.cmp (可选)
minuet-ai.nvim hero image

快速开始

小步舞曲

Minuet:在代码中与智能共舞 💃。

Minuet 将小步舞曲的优雅与和谐带入你的编码过程。 正如舞者在小步舞曲中翩翩起舞一样。

特性

  • 基于 AI 的代码补全,支持双模式:
    • 针对基于聊天的 LLM 在代码补全任务上的专用提示及多种增强功能。
    • 适用于兼容模型(DeepSeek、Codestral、Qwen 等)的中间填充(FIM)补全。
  • 支持多个 AI 提供商(OpenAI、Claude、Gemini、Codestral、Ollama、Llama-cpp 以及兼容 OpenAI 的服务)。
  • 可自定义的配置选项。
  • 流式传输支持,即使在较慢的 LLM 上也能实现补全。
  • 不需要后台运行专有二进制文件。只需使用 curl 和你偏好的 LLM 提供商即可。
  • 支持 virtual-textnvim-cmpblink-cmpbuilt-inmini.completion 前端。
  • 可作为进程内 LSP 服务器提供补全功能(可选功能)。
  • 支持逐行接收多行建议,以便你可以按照自己的节奏逐步获取较长的建议。
  • 当你输入的文本与建议的开头匹配时,Minuet 会保持补全内容与你输入的文本同步,而不是丢弃它,从而减少不必要的 LLM 请求并节省资源。
  • 通过 Minuet duet 命令支持下一次编辑预测(NES)。此功能目前仍处于高度实验阶段。

使用 nvim-cmp / blink-cmp 前端时

example-cmp

使用内置补全前端时(需 NVIM 0.11+):

example-builtin-completion

使用虚拟文本前端时

example-virtual-text

https://github.com/user-attachments/assets/e0c4f2bd-0361-45b4-8eb4-0f49356bd7d9

使用 Duet(下一次编辑预测)时

https://github.com/user-attachments/assets/b98699d5-b81d-4061-a1b3-d6f581c6b9b0

要求

  • Neovim 0.10+。
  • 可选:nvim-cmp
  • 可选:blink.cmp
  • 至少一个受支持的 AI 提供商的 API 密钥
  • plenary.nvim Minuet 现在使用内置的 vim.system,不再需要 plenary。

安装

Lazy.nvim

specs = {
    {
        'milanglacier/minuet-ai.nvim',
        config = function()
            require('minuet').setup {
                -- 您的配置选项在此处
            }
        end,
    },
    -- 可选,如果您使用虚拟文本前端,则不需要 nvim-cmp。
    { 'hrsh7th/nvim-cmp' },
    -- 可选,如果您使用虚拟文本前端,则不需要 blink。
    { 'Saghen/blink.cmp' },
}

Rocks.nvim

Minuet 已在 luarocks.org 上发布。只需运行 Rocks install minuet-ai.nvim,即可像安装其他 luarocks 包一样进行安装。

快速入门

虚拟文本设置

require('minuet').setup {
    virtualtext = {
        auto_trigger_ft = {},
        keymap = {
            -- 接受整个补全
            accept = '<A-A>',
            -- 接受一行
            accept_line = '<A-a>',
            -- 接受 n 行(会提示输入数字)
            -- 例如,“A-z 2 CR”将接受 2 行
            accept_n_lines = '<A-z>',
            -- 切换到上一个补全项,或手动触发补全
            prev = '<A-[>',
            -- 切换到下一个补全项,或手动触发补全
            next = '<A-]>',
            dismiss = '<A-e>',
        },
    },
}

Nvim-cmp 设置

require('cmp').setup {
    sources = {
        {
             -- 将 minuet 作为源之一,以启用自动补全
            { name = 'minuet' },
            -- 以及其他来源
        }
    },
    performance = {
        -- 由于 LLM 的响应速度通常比其他补全源慢,建议增加超时时间。如果仅需手动补全,则无需此设置。
        fetching_timeout = 2000,
    },


-- 如果您希望手动触发补全,
-- 下列配置将 `A-y` 键绑定为手动触发补全的快捷键。
require('cmp').setup {
    mapping = {
        ["<A-y>"] = require('minuet').make_cmp_map()
        -- 以及其他按键映射
    },
}

Blink-cmp 设置

require('blink-cmp').setup {
    keymap = {
        -- 手动调用 minuet 补全。
        ['<A-y>'] = require('minuet').make_blink_map(),
    },
    sources = {
         -- 启用 minuet 用于自动补全
        default = { 'lsp', 'path', 'buffer', 'snippets', 'minuet' },
        -- 仅用于手动补全时,从 default 中移除 'minuet'
        providers = {
            minuet = {
                name = 'minuet',
                module = 'minuet.blink',
                async = true,
                -- 应与 minuet.config.request_timeout * 1000 匹配,
                -- 因为 minuet.config.request_timeout 的单位是秒
                timeout_ms = 3000,
                score_offset = 50, -- 使 minuet 在建议中具有更高优先级
            },
        },
    },
    -- 建议避免不必要的请求
    completion = { trigger = { prefetch_on_insert = false } },
}

内置补全与内联补全的进程内 LSP

要求:

  • 需要 Neovim 0.11 或更高版本才能使用内置补全。
  • 需要 Neovim 0.12 或更高版本才能使用 vim.lsp.inline_completion

注意:

config.lsp.completion.enableconfig.lsp.inline_completion.enable 是设置时的选项。Minuet 会在运行 require('minuet').setup() 时决定暴露哪些 LSP 功能,因此之后更改这些选项并不会为已经运行的 Minuet LSP 服务器启用该功能。

如果在同一个会话中可能稍后使用其中一项功能,请先在设置时启用它,然后在运行时仅控制其每缓冲区的自动触发行为。

补全

require('minuet').setup {
    lsp = {
        enabled_ft = { 'toml', 'lua', 'cpp' },
        completion = {
            -- 使用 `vim.lsp.completion.enable` 启用自动补全触发
            enabled_auto_trigger_ft = { 'cpp', 'lua' },
        },
    }
}

completion.enabled_auto_trigger_ft 设置仅适用于内置补全(vim.lsp.completion)。使用 Mini.Completion 的用户可以忽略此选项,因为 Mini.Completion 会使用所有可用的 LSP 进行自动触发的补全。

对于手动触发的补全,请确保将 vim.bo.omnifunc 设置为 v:lua.vim.lsp.omnifunc,并在插入模式下使用 <C-x><C-o>

建议:

对于使用 blink-cmpnvim-cmp 的用户,推荐直接使用原生源而不是通过 LSP,主要有两个原因:

  1. 当 Minuet 作为独立源使用时,blink-cmpnvim-cmp 能提供更好的排序和异步管理,而将其与常规 LSP(如 clangd)一起使用则无法达到同样的效果。
  2. 使用 blink-cmpnvim-cmp 的原生源时,可以配置 Minuet 仅用于手动补全,从而禁用自动补全。然而,当 Minuet 以 LSP 服务器身份运行时,无法区分补全是自动触发还是手动触发。

LSP 协议规范定义了三种 triggerKind 值:InvokedTriggerCharacterTriggerForIncompleteCompletions。但这些值均不能明确区分手动和自动补全请求。

注意

  • 上游问题(在此跟踪)可能导致在接受多行补全时出现意外的缩进行为。

目前,Minuet 提供了配置选项 config.lsp.completion.adjust_indentation(默认启用)作为临时解决方案。然而,作者承认这一解决方案并不完善,并且启用后可能会引入额外的边缘情况。

因此,在使用内置补全时,请考虑以下做法:

  • 确保 config.add_single_line_entry = true,只接受单行补全。

  • 避免在缩进影响语义的语言(如 Python)中使用 Minuet 和内置补全。

  • 用户可能会在客户端支持补全的情况下,在 LspAttach 事件中调用 vim.lsp.completion.enable {autotrigger = true}。但这并不是 Minuet 的理想行为。作为 LLM 补全源,Minuet 在自动触发时可能会面临严重的速率限制。

因此,建议使用 config.lsp.completion.enabled_auto_trigger_ft 设置来启用 Minuet 的自动触发。

对于使用 LspAttach 事件的用户,建议在启用自动触发前先确认服务器是否为 Minuet 服务器。示例如下:

vim.api.nvim_create_autocmd('LspAttach', {
    callback = function(args)
        local client_id = args.data.client_id
        local bufnr = args.buf
        local client = vim.lsp.get_client_by_id(client_id)
        if not client then
            return
        end

        if client.server_capabilities.completionProvider and client.name ~= 'minuet' then
            vim.lsp.completion.enable(true, client_id, bufnr, { autotrigger = true })
        end
    end,
    desc = '启用内置自动补全',
})

内联补全

Minuet 也可以通过 Neovim 内置的 vim.lsp.inline_completion 接口展示建议:

require('minuet').setup {
    lsp = {
        enabled_ft = { 'toml', 'lua', 'cpp' },
        inline_completion = {
            enable = true,
            enabled_auto_trigger_ft = { 'cpp', 'lua' },
        },
    },
}

vim.keymap.set('i', '<A-x>', function()
    vim.lsp.inline_completion.get()
end, { desc = '接受' })
vim.keymap.set('i', '<A-c>', function()
    vim.lsp.inline_completion.select { count = 1 }
end, { desc = '切换到下一个' })
vim.keymap.set('i', '<A-v>', function()
    vim.lsp.inline_completion.select { count = -1 }
end, { desc = '切换到上一个' })

如果您不希望在启动时使用内联补全,但仍想在后续为特定缓冲区启用它,请在设置时将 config.lsp.inline_completion.enable = true,并将 config.lsp.inline_completion.enabled_auto_trigger_ft 留空。之后可以在运行时为当前缓冲区启用::Minuet lsp inline_completion enable_auto_trigger

建议:

如果您需要内联建议,Minuet 自带的 virtualtext 前端仍然是推荐的选择。Neovim 内置的 inline_completion 支持虽然实用,但在实际应用中仅覆盖自动触发场景。而 Minuet 的 virtualtext 前端支持更全面的工作流程:既支持手动调用也支持自动触发,能够在您继续输入时保持建议同步更新,并允许您逐步接受较长的建议,甚至可以选择只接受部分补全内容而非一次性提交整个建议。

使用 LSP 内联补全时,请避免同时启用 Minuet 的 virtualtext

LLM 提供者示例

Openrouter Kimi-K2

require('minuet').setup {
    provider = 'openai_compatible',
    request_timeout = 2.5,
    throttle = 1500, -- 增加此值以降低成本并避免速率限制
    debounce = 600, -- 增加此值以降低成本并避免速率限制
    provider_options = {
        openai_compatible = {
            api_key = 'OPENROUTER_API_KEY',
            end_point = 'https://openrouter.ai/api/v1/chat/completions',
            model = 'moonshotai/kimi-k2',
            name = 'Openrouter',
            optional = {
                max_tokens = 56,
                top_p = 0.9,
                provider = {
                     -- 优先考虑吞吐量以加快完成速度
                    sort = 'throughput',
                },
            },
        },
    },
}

Deepseek

-- 你可以使用 deepseek,无论是通过 openai_fim_compatible 还是 openai_compatible 提供者
require('minuet').setup {
    provider = 'openai_fim_compatible',
    provider_options = {
        openai_fim_compatible = {
            api_key = 'DEEPSEEK_API_KEY',
            name = 'deepseek',
            optional = {
                max_tokens = 256,
                top_p = 0.9,
            },
        },
    },
}


-- 或者
require('minuet').setup {
    provider = 'openai_compatible',
    provider_options = {
        openai_compatible = {
            end_point = 'https://api.deepseek.com/chat/completions',
            api_key = 'DEEPSEEK_API_KEY',
            name = 'deepseek',
            optional = {
                max_tokens = 256,
                top_p = 0.9,
            },
        },
    },
}

Ollama Qwen-2.5-coder:7b

require('minuet').setup {
    provider = 'openai_fim_compatible',
    n_completions = 1, -- 建议在本地模型中使用以节省资源
    -- 我建议从较小的上下文窗口大小开始,并根据你的本地计算能力逐步扩大。
    -- 上下文窗口为512,可以作为一个良好的起点来估算你的计算能力。
    -- 一旦你对本地计算能力有了可靠的估计,就应该将上下文窗口调整到更大的值。
    context_window = 512,
    provider_options = {
        openai_fim_compatible = {
            -- 对于 Windows 用户,环境变量中可能没有 TERM。可以考虑使用 APPDATA。
            api_key = 'TERM',
            name = 'Ollama',
            end_point = 'http://localhost:11434/v1/completions',
            model = 'qwen2.5-coder:7b',
            optional = {
                max_tokens = 56,
                top_p = 0.9,
            },
        },
    },
}

Llama.cpp Qwen-2.5-coder:1.5b

首先,使用你选择的模型启动 llama-server

如果你的系统显存不足8GB,以下是一个启动服务器的bash脚本示例:

llama-server \
    -hf ggml-org/Qwen2.5-Coder-1.5B-Q8_0-GGUF \
    --port 8012 -ngl 99 -fa -ub 1024 -b 1024 \
    --ctx-size 0 --cache-reuse 256
require('minuet').setup {
    provider = 'openai_fim_compatible',
    n_completions = 1, -- 建议在本地模型中使用以节省资源
    -- 我建议从较小的上下文窗口大小开始,并根据你的本地计算能力逐步扩大。
    -- 上下文窗口为512,可以作为一个良好的起点来估算你的计算能力。
    -- 一旦你对本地计算能力有了可靠的估计,就应该将上下文窗口调整到更大的值。
    context_window = 512,
    provider_options = {
        openai_fim_compatible = {
            -- 对于 Windows 用户,环境变量中可能没有 TERM。可以考虑使用 APPDATA。
            api_key = 'TERM',
            name = 'Llama.cpp',
            end_point = 'http://localhost:8012/v1/completions',
            -- 模型由 llama-cpp 服务器设置,无法在启动后更改。
            model = 'PLACEHOLDER',
            optional = {
                max_tokens = 56,
                top_p = 0.9,
            },
            -- Llama.cpp 不支持 FIM 补全中的 `suffix` 选项。
            -- 因此,我们必须禁用它,并手动填充 FIM 补全所需的特殊标记。
            template = {
                prompt = function(context_before_cursor, context_after_cursor, _)
                    return '<|num_start|>'
                        .. context_before_cursor
                        .. '<|num_end|>'
                        .. context_after_cursor
                        .. '

## 理解模型速度

对于云服务提供商而言,
[Openrouter](https://openrouter.ai/google/gemini-2.0-flash-001/providers)
提供了一个宝贵的资源,用于比较由不同云推理提供商托管的闭源和开源模型的速度。

在评估模型速度时,有两个关键指标:延迟(生成第一个 token 所需的时间)和吞吐量(每秒处理的 token 数)。通常情况下,延迟比吞吐量更为重要。

理想情况下,应将延迟控制在 1 秒以下,同时确保吞吐量超过每秒 100 个 token。

对于本地运行的大语言模型,
[llama.cpp#4167](https://github.com/ggml-org/llama.cpp/discussions/4167)
提供了关于在 Apple M 系列芯片上运行的 7B 参数模型速度的宝贵数据。其中两个关键指标是 `Q4_0 PP [t/s]`,它衡量的是延迟(即处理 KV 缓存的每秒 token 数,相当于生成第一个 token 所需的时间),以及 `Q4_0 TG [t/s]`,它表示每秒的生成速度。

# 配置

Minuet AI 的默认配置如下:

```lua
default_config = {
    -- 启用或禁用自动补全。请注意,您仍然需要将 Minuet 添加到 cmp/blink 的源中。此选项控制当 Minuet 包含在 cmp/blink 源中时,cmp/blink 是否会尝试调用 Minuet。该设置对手动补全没有影响;手动调用时,Minuet 始终启用。您可以使用命令 `Minuet cmp/blink toggle` 来切换此选项。
    cmp = {
        enable_auto_complete = true,
    },
    blink = {
        enable_auto_complete = true,
    },
    -- 推荐仅在内置补全中使用 LSP。如果您正在使用 `cmp` 或 `blink`,则不建议使用 LSP 通过 Minuet 进行代码补全。
    lsp = {
        enabled_ft = {},
        -- 排除在 LSP 激活之外的文件类型。当 `enabled_ft` = { '*' } 时很有用。
        disabled_ft = {},
        completion = {
            enable = true,
            -- 如果为真,当用户使用 blink 或 nvim-cmp 时,会警告用户应改用原生源。
            warn_on_blink_or_cmp = true,
            -- 有关此选项的更多详细信息,请参阅 README 中的“进程内 LSP”部分。
            adjust_indentation = true,
            -- 启用使用 `vim.lsp.completion.enable` 的自动补全触发功能。
            enabled_auto_trigger_ft = {},
            -- 排除在自动触发之外的文件类型。当 `enabled_auto_trigger_ft` = { '*' } 时很有用。
            disabled_auto_trigger_ft = {},
        },
        inline_completion = {
            enable = false,
            -- 如果为真,当启用 LSP 内联补全且同时配置了 Minuet 虚拟文本使用时,会发出警告。
            warn_on_virtualtext = true,
            -- 对这些文件类型启用使用 `vim.lsp.inline_completion.enable` 的自动内联补全功能。
            enabled_auto_trigger_ft = {},
            -- 排除在内联补全自动触发之外的文件类型。
            disabled_auto_trigger_ft = {},
        },
    },
    virtualtext = {
        -- 指定要启用自动虚拟文本补全的文件类型,例如 { 'python', 'lua' }。请注意,即使文件类型不在您的 auto_trigger_ft 列表中,您仍然可以手动调用补全。
        auto_trigger_ft = {},
        -- 指定应禁用自动虚拟文本补全的文件类型。此选项在启用了所有文件类型的自动补全时很有用,即当 auto_trigger_ft = { '*' } 时。
        auto_trigger_ignore_ft = {},
        keymap = {
            accept = nil,
            accept_line = nil,
            accept_n_lines = nil,
            -- 循环到下一个补全项,或手动调用补全。
            next = nil,
            -- 循环到上一个补全项,或手动调用补全。
            prev = nil,
            dismiss = nil,
        },
        -- 当补全菜单(nvim-cmp 或 blink-cmp)可见时,是否显示虚拟文本建议。
        show_on_completion_menu = false,
    },
    provider = 'codestral',
    -- 光标前后上下文的最大总字符数。16000 个字符通常相当于 LLM 的约 4000 个 token。
    context_window = 16000,
    -- 当总字符数超过上下文窗口时,光标前后的上下文比例。比例越大,光标前的上下文使用越多。此选项应在 0 到 1 之间,context_ratio = 0.75 表示比例为 3:1。
    context_ratio = 0.75,
    throttle = 1000, -- 每 x 毫秒才发送一次请求,使用 0 可禁用节流。
    -- 在 x 毫秒内对请求进行防抖处理,设置为 0 可禁用防抖。
    debounce = 400,
    -- 控制请求状态的通知显示。
    -- 通知选项:
    -- false:禁用所有通知(使用布尔值 false,而不是字符串 "false")
    -- "debug":显示所有通知(全面调试)
    -- "verbose":显示大多数通知
    -- "warn":仅显示警告和错误
    -- "error":仅显示错误
    notify = 'warn',
    -- 请求超时时间,以秒为单位。当启用流式传输(stream = true)时,设置较短的 request_timeout 可以更快地获取补全项,但结果可能不完整。
    -- 相反,当流式传输禁用(stream = false)时,如果在 LLM 返回结果之前发生超时,则不会获得任何补全项。
    request_timeout = 3,
    -- 用于发起 HTTP 请求的命令。
    curl_cmd = 'curl',
    -- 传递给 curl 的额外参数(字符串列表)。
    curl_extra_args = {},
    -- 如果补全项有多行,只创建包含其第一行的另一个补全项。此选项仅对 cmp 和 blink 有效。对于虚拟文本,不会添加单行条目。
    add_single_line_entry = true,
    -- 编码到聊天 LLM 提示中的补全项数量。对于 FIM 模型,这是要发送的请求数量。需要注意的是,当 'add_single_line_entry' 设置为 true 时,实际返回的补全项数量可能会超过此值。此外,LLM 无法保证指定的确切补全项数量,因为此参数仅作为提示指南。
    n_completions = 3,
    -- 光标后上下文长度,用于过滤补全文本。
    --
    -- 此设置有助于防止语言模型生成冗余文本。在过滤补全时,系统会将补全候选的后缀与光标后紧跟的文本进行比较。
    --
    -- 如果候选文本末尾与光标后上下文开头之间的最长公共子串长度超过此值,那么这部分公共内容将从候选文本中截去。
    --
    -- 例如,如果该值为 15,而某个补全候选以一段 20 个字符的字符串结尾,恰好与光标后的 20 个字符完全匹配,则该候选将在交付前被截去这 20 个字符。
    after_cursor_filter_length = 15,
    -- 类似于 after_cursor_filter_length,但不是从后缀而是从前缀修剪补全项。
    before_cursor_filter_length = 2,
    -- 要使用的代理端口。
    proxy = nil,
    -- 要执行的函数列表。如果任何函数返回 `false`, Minuet 将不会触发自动补全。即使这些函数评估为 `false`,使用 `nvim-cmp`、`blink-cmp` 或虚拟文本(不包括 LSP)时,仍可手动调用补全。
    -- 当此列表为空(默认)时,它始终评估为 `true`。
    -- 请注意,每次 Minuet 尝试触发自动补全时都会调用此列表,因此请确保此列表中的函数效率极高。
    enable_predicates = {},
    provider_options = {
        -- 请参阅下一部分中各提供商的文档。
    },
    -- 请参阅“提示”部分的文档。
    default_system = {
        template = '...',
        prompt = '...',
        guidelines = '...',
        n_completion_template = '...',
    },
    default_system_prefix_first = {
        template = '...',
        prompt = '...',
        guidelines = '...',
        n_completion_template = '...',
    },
    default_fim_template = {
        prompt = '...',
        suffix = '...',
    },
    default_few_shots = { '...' },
    default_chat_input = { '...' },
    default_few_shots_prefix_first = { '...' },
    default_chat_input_prefix_first = { '...' },
    -- 用于 `Minuet change_preset` 命令的配置选项。
    presets = {}
}

API 密钥

Minuet AI 需要 API 密钥才能运行。请设置以下环境变量:

  • OPENAI_API_KEY 用于 OpenAI
  • GEMINI_API_KEY 用于 Gemini
  • ANTHROPIC_API_KEY 用于 Claude
  • CODESTRAL_API_KEY 用于 Codestral
  • 自定义环境变量,用于与 OpenAI 兼容的服务(如您的配置中所指定)

注意: 请将环境变量的名称传递给 Minuet,而不是实际的值。例如,应传递 OPENAI_API_KEY,而不是其具体值(如 sk-xxxx)。

如果使用 Ollama,您需要分配一个任意的非空环境变量作为占位符,以便其正常工作。

此外,您也可以提供一个返回 API 密钥的函数。该函数应立即返回结果,因为它会在每次完成请求时被调用。

require('minuet').setup {
    provider_options = {
        openai_compatible = {
            -- 好的做法
            api_key = 'FIREWORKS_API_KEY', -- 将读取名为 FIREWORKS_API_KEY 的环境变量
            -- 好的做法
            api_key = function() return 'sk-xxxx' end,
            -- 不好的做法
            api_key = 'sk-xxxx',
        }
    }
}

提示词

请参阅 提示词,了解 minuet 使用的默认提示词及自定义说明。

需要注意的是,minuet 采用了两种不同的提示词系统:

  1. 专为聊天型大模型设计的系统(OpenAI、与 OpenAI 兼容的模型、Claude 和 Gemini)
  2. 另一种专为 Codestral 和与 OpenAI-FIM 兼容的模型设计的系统

前缀优先 vs. 后缀优先

在使用聊天型大模型时,构建提示词有两种方式:将前缀(光标前的上下文)置于后缀(光标后的上下文)之前,或者将后缀置于前缀之前。

默认情况下,minuet 对 OpenAI 和 Gemini 提供者采用 前缀优先 风格,而对与 OpenAI 兼容的提供者和 Claude 提供者则采用 后缀优先 风格。建议您尝试这两种策略,以确定哪种效果更好,尤其是在使用与 OpenAI 兼容的提供者及其不同模型时。

以下是一个示例代码片段,展示了如何在这两种提示词构建方法之间切换:

local mc = require 'minuet.config'

-- 前缀优先风格
require('minuet').setup {
    provider_options = {
        openai_compatible = {
            system = mc.default_system_prefix_first,
            chat_input = mc.default_chat_input_prefix_first,
            few_shots = mc.default_few_shots_prefix_first,
        },
    },
}

-- 后缀优先风格
require('minuet').setup {
    provider_options = {
        openai_compatible = {
            system = mc.default_system,
            few_shots = mc.default_few_shots,
            chat_input = mc.default_chat_input,
        },
    },
}

提供者

您需要在配置中设置 provider 字段,默认提供者是 codestral。例如:

require('minuet').setup {
    provider = 'gemini'
}

OpenAI

以下是 OpenAI 的默认配置:

provider_options = {
    openai = {
        model = 'gpt-5.4-nano',
        end_point = 'https://api.openai.com/v1/chat/completions',
        system = "默认值请参见[提示词]部分",
        few_shots = "默认值请参见[提示词]部分",
        chat_input = "默认值请参见[提示词部分]",
        stream = true,
        api_key = 'OPENAI_API_KEY',
        optional = {
            -- 您可以传递任何想要发送到 OpenAI 请求的额外参数,
            -- 例如:
            -- stop = { 'end' },
            -- max_completion_tokens = 256,
            -- top_p = 0.9,
            -- reasoning_effort = 'minimal'
            -- reasoning_effort = 'none'
        },
        -- 用于转换端点、头部和请求体的一系列函数
        transform = {},
    },
}

以下配置并非默认,但推荐使用,以防止因输出过多 token 而导致请求超时。

provider_options = {
	openai = {
		optional = {
			max_completion_tokens = 128,
			-- 对于思考型模型
			reasoning_effort = 'none'
			-- reasoning_effort = "minimal",
			-- 如果您选择的模型不支持 'none',则设置为 'minimal'
		},
	},
}

注意:如果您打算使用 GPT-5 系列模型(例如 gpt-5-minigpt-5.4-nano),请记住以下几点:

  1. 使用 max_completion_tokens 而不是 max_tokens
  2. 这些模型不支持调整 top_ptemperature
  3. 通过将 reasoning_effort 设置为 none 来禁用思考模式;如果所选模型不支持 none,则使用 minimal

Claude

以下是 Claude 的默认配置:

provider_options = {
    claude = {
        max_tokens = 256,
        model = 'claude-haiku-4.5',
        system = "默认值请参见[提示词]部分",
        few_shots = "默认值请参见[提示词]部分",
        chat_input = "默认值请参见[提示词部分]",
        stream = true,
        api_key = 'ANTHROPIC_API_KEY',
        end_point = 'https://api.anthropic.com/v1/messages',
        optional = {
            -- 您可以传递任何想要发送到 Claude 请求的额外参数,
            -- 例如:
            -- stop_sequences = nil,
        },
        -- 用于转换端点、头部和请求体的一系列函数
        transform = {},
    },
}

Codestral

Codestral 是一个文本补全模型,而非聊天模型,因此系统提示词和少量示例并不适用。请注意,您应使用 CODESTRAL_API_KEY,而不是 MISTRAL_API_KEY,因为它们使用不同的端点。若要使用 Mistral 的端点,只需在配置中修改 end_pointapi_key 参数即可。

以下是 Codestral 的默认配置:

provider_options = {
    codestral = {
        model = 'codestral-latest',
        end_point = 'https://codestral.mistral.ai/v1/fim/completions',
        api_key = 'CODESTRAL_API_KEY',
        stream = true,
        template = {
            prompt = "默认值请参见[提示词部分]",
            suffix = "默认值请参见[提示词部分]",
        },
        optional = {
            stop = nil, -- 用于停止补全生成的标识符
            max_tokens = nil,
        },
    },
}

以下配置并非默认,但推荐使用,以防止因输出过多 token 而导致请求超时。

provider_options = {
    codestral = {
        optional = {
            max_tokens = 256,
            stop = { '\n\n' },
        },
    },
}

水星编码器

由 Inception 开发的水星编码器被描述为一种基于扩散的大语言模型,它通过迭代优化而非自回归式标记预测来加速代码生成。据称,这种方法旨在提供更快、更高效的代码补全功能。首先,您需要从 Inception 平台获取 API 密钥,并将其配置为 INCEPTION_API_KEY 环境变量。

您可以通过 OpenAI 兼容的 FIM 端点访问水星编码器,使用如下配置:

provider_options = {
    openai_fim_compatible = {
        model = "mercury-coder",
        end_point = "https://api.inceptionlabs.ai/v1/fim/completions",
        api_key = "INCEPTION_API_KEY", -- 环境变量名称
        stream = true,
    },
}

雅典娜

您应该注册账号并使用 Google AI Studio 的服务,而不是 Google Cloud。您可以通过他们的 Google API 页面 获取 API 密钥。

以下配置为默认设置。

provider_options = {
    gemini = {
        model = 'gemini-2.0-flash',
        system = "请参阅[提示]部分以了解默认值",
        few_shots = "请参阅[提示]部分以了解默认值",
        chat_input = "请参阅[提示部分]以了解默认值",
        stream = true,
        api_key = 'GEMINI_API_KEY',
        end_point = 'https://generativelanguage.googleapis.com/v1beta/models',
        optional = {},
        -- 用于转换端点、头部和请求体的一系列函数
        transform = {},
    },
}

以下配置并非默认设置,但建议采用,以防止因输出过多标记而导致请求超时。您还可以按照示例调整安全设置:

provider_options = {
    gemini = {
        optional = {
            generationConfig = {
                maxOutputTokens = 256,
                thinkingConfig = {
                    -- 对于 Gemini 2.5 型号,禁用思考模式
                    thinkingBudget = 0,
                    -- 对于 Gemini 3.x 型号,将思考级别设为“最低”
                    thinkingLevel = 'minimal',
                    -- 只需设置上述选项之一即可。
                },
            },
            safetySettings = {
                {
                    -- HARM_CATEGORY_HATE_SPEECH,
                    -- HARM_CATEGORY_HARASSMENT,
                    -- HARM_CATEGORY_SEXUALLY_EXPLICIT
                    category = 'HARM_CATEGORY_DANGEROUS_CONTENT',
                    -- BLOCK_NONE,
                    threshold = 'BLOCK_ONLY_HIGH',
                },
            },
        },
    },
}

我们建议使用 gemini-2.0-flash 而不是 gemini-2.5-flash,因为 2.0 版本在性能相当的情况下成本显著更低。2.5 版的主要改进在于其扩展的思考模式,但对于代码补全场景而言,这种模式的价值微乎其微。此外,思考模式会大幅增加延迟,因此我们建议完全禁用它。

OpenAI 兼容

使用任何与 OpenAI 的聊天补全 API 兼容的提供商。

例如,您可以将 end_point 设置为 http://localhost:11434/v1/chat/completions 来使用 ollama

请注意,不是所有 OpenAI 兼容的服务都支持流式传输,如果您的服务不支持流式传输,您应将 stream=false 以禁用流式传输。

以下配置为默认设置。

provider_options = {
    openai_compatible = {
        model = 'mistralai/devstral-small',
        system = "请参阅[提示]部分以了解默认值",
        few_shots = "请参阅[提示]部分以了解默认值",
        chat_input = "请参阅[提示部分]以了解默认值",
        stream = true,
        end_point = 'https://openrouter.ai/api/v1/chat/completions',
        api_key = 'OPENROUTER_API_KEY',
        name = 'Openrouter',
        optional = {
            stop = nil,
            max_tokens = nil,
        },
        -- 用于转换端点、头部和请求体的一系列函数
        transform = {},
    }
}

OpenAI-FIM 兼容

使用任何与 OpenAI 补全 API 兼容的提供商。此请求使用 /completions 文本端点,而非 /chat/completions 端点,因此系统提示和少量示例不适用。

例如,您可以将 end_point 设置为 http://localhost:11434/v1/completions 来使用 ollama,或设置为 http://localhost:8012/v1/completions 来使用 llama.cpp

命令行补全功能适用于这些提供商支持的模型:deepseekollamasiliconflow

有关详细信息,请参阅 OpenAI 文档中的 Completions Legacy 部分。

请注意,不是所有 OpenAI 兼容的服务都支持流式传输。如果您的服务不支持流式传输,您应将 stream=false 以禁用流式传输。

此外,对于 Ollama 用户来说,务必确认模型模板是否支持 FIM 补全。例如,qwen2.5-coder 支持 FIM,正如其 模板 所示。然而,一些用户可能会惊讶地发现,deepseek-coder 不支持 FIM 模板,而应改用 deepseek-coder-v2

例如,根据您的本地计算能力运行 llama.cpp 的 Bash 脚本,请参阅 recipes.md。请注意,llama.cpp 的模型必须在启动 llama.cpp 服务器时确定,此后无法更改。

provider_options = {
    openai_fim_compatible = {
        model = 'deepseek-chat',
        end_point = 'https://api.deepseek.com/beta/completions',
        api_key = 'DEEPSEEK_API_KEY',
        name = 'Deepseek',
        stream = true,
        template = {
            prompt = "请参阅[提示部分]以了解默认值",
            suffix = "请参阅[提示部分]以了解默认值",
        },
        -- 用于转换端点、头部和请求体的一系列函数
        transform = {},
        -- 自定义函数,用于从 JSON 输出中提取 LLM 生成的文本
        get_text_fn = {}
        optional = {
            stop = nil,
            max_tokens = nil,
        },
    }
}

以下配置并非默认设置,但建议采用,以防止因输出过多标记而导致请求超时。

provider_options = {
    openai_fim_compatible = {
        optional = {
            max_tokens = 256,
            stop = { '\n\n' },
        },
    }
}

非 OpenAI-FIM 兼容的 API

对于像 DeepInfra FIMhttps://api.deepinfra.com/v1/inference/)这样的提供商,请参阅 recipes.md 以获取高级配置说明。

命令

Minuet change_providerMinuet change_model

change_provider 命令允许您在 Minuet 设置完成后更改提供者。

使用示例:Minuet change_provider claude

change_model 命令允许您在一个命令中同时更改提供者和模型。当不带参数调用时,它会使用 vim.ui.select 打开一个交互式选择菜单,供您从可用模型中选择。当带参数调用时,格式为 provider:model

使用示例:

  • Minuet change_model - 打开交互式模型选择
  • Minuet change_model gemini:gemini-1.5-pro-latest - 直接设置模型

注意:对于 openai_compatibleopenai_fim_compatible 提供者,命令行中的模型补全由您的配置中的 name 字段决定。例如,如果您配置了:

provider_options.openai_compatible.name = 'Fireworks'

那么在命令行中输入 Minuet change_model openai_compatible: 时,您将看到特定于 Fireworks 提供者的模型补全选项。

Minuet change_preset

change_preset 命令允许您在初始设置期间定义的配置预设之间切换。预设提供了一种便捷的方式来在不同的配置集之间切换。这在您需要以下情况时特别有用:

  • 在不同的云提供商(如 Fireworks 或 Groq)之间切换 openai_compatible 提供者
  • 为不同提供者应用不同的限流和防抖设置

调用该命令时,它会将所选预设与当前配置表合并,以创建更新后的配置。

使用语法:Minuet change_preset preset_1

预设可以在初始设置过程中进行配置。

require('minuet').setup {
    presets = {
        preset_1 = {
            -- 面向具有大上下文窗口的云端请求的配置
            context_window = 20000,
            request_timeout = 4,
            throttle = 3000,
            debounce = 1000,
            provider = 'openai_compatible',
            provider_options = {
                openai_compatible = {
                    model = 'llama-3.3-70b-versatile',
                    api_key = 'GROQ_API_KEY',
                    name = 'Groq'
                }
            }
        },
        preset_2 = {
            -- 面向具有较小上下文窗口的本地模型的配置
            provider = 'openai_fim_compatible',
            context_window = 2000,
            throttle = 400,
            debounce = 100,
            provider_options = {
                openai_fim_compatible = {
                    api_key = 'TERM',
                    name = 'Ollama',
                    end_point = 'http://localhost:11434/v1/completions',
                    model = 'qwen2.5-coder:7b',
                    optional = {
                        max_tokens = 256,
                        top_p = 0.9
                    }
                }
            }
        }
    }
}

Minuet blinkMinuet cmp

启用或禁用 nvim-cmpblink.cmp 的自动补全功能。虽然 Minuet 必须添加到您的 cmp/blink 源中,但此命令仅控制 Minuet 是否会在自动补全过程中被触发。该命令不会影响手动补全行为——Minuet 在手动调用时仍然处于活动状态并可用。

使用示例:Minuet blink toggleMinuet blink enableMinuet blink disable

Minuet virtualtext

启用或禁用在当前缓冲区中自动显示 virtual-text 补全。

使用示例:Minuet virtualtext toggleMinuet virtualtext enableMinuet virtualtext disable

Minuet duet

Minuet duet 命令提供了手动下一次编辑预测控制:

  • :Minuet duet predict:为当前可编辑区域请求 NES 预测,并将其作为预览显示。
  • :Minuet duet apply:应用当前的 duet 预测。
  • :Minuet duet dismiss:取消当前的 duet 预测预览。

Minuet lsp

Minuet LSP 命令提供了用于管理进程内 LSP 服务器的命令:

  • :Minuet lsp attach:将 Minuet LSP 服务器附加到当前缓冲区
  • :Minuet lsp detach:从当前缓冲区分离 Minuet LSP 服务器。
  • :Minuet lsp completion enable_auto_trigger:为当前缓冲区启用自动触发的 vim.lsp.completion
  • :Minuet lsp completion disable_auto_trigger:禁用当前缓冲区的自动触发 vim.lsp.completion
  • :Minuet lsp inline_completion enable_auto_trigger:为当前缓冲区启用 vim.lsp.inline_completion 的自动触发。
  • :Minuet lsp inline_completion disable_auto_trigger:禁用当前缓冲区vim.lsp.inline_completion 自动触发。

二重奏(下一次编辑预测)

Minuet duet 是 Minuet 的一项高度实验性的下一次编辑预测(NES)功能。

基本用法为手动操作。将二重奏命令绑定到您偏好的键位映射中,然后:

  1. 触发 :Minuet duet predict 来请求当前编辑的预测。
  2. 查看缓冲区中渲染的预览。
  3. 使用 :Minuet duet apply 应用预测,或使用 :Minuet duet dismiss 丢弃它。

示例键位映射:

vim.keymap.set('n', '<leader>mp', '<cmd>Minuet duet predict<cr>', { desc = 'Minuet duet predict' })
vim.keymap.set('n', '<leader>ma', '<cmd>Minuet duet apply<cr>', { desc = 'Minuet duet apply' })
vim.keymap.set('n', '<leader>md', '<cmd>Minuet duet dismiss<cr>', { desc = 'Minuet duet dismiss' })
vim.keymap.set('i', '<A-z>', '<cmd>Minuet duet predict<cr>', { desc = 'Minuet duet predict' })
vim.keymap.set('i', '<A-a>', '<cmd>Minuet duet apply<cr>', { desc = 'Minuet duet apply' })
vim.keymap.set('i', '<A-x>', '<cmd>Minuet duet dismiss<cr>', { desc = 'Minuet duet dismiss' })

目前推荐使用的模型是 gemini-3-flash-preview

require('minuet').setup {
    duet = {
        provider = 'gemini',
        provider_options = {
            gemini = {
                model = 'gemini-3-flash-preview',
                optional = {
                    generationConfig = {
                        thinkingConfig = {
                            -- 建议关闭思考模式
                            thinkingLevel = 'minimal',
                        },
                    },
                },
            },
            openai_compatible = {
                model = 'minimax/minimax-m2.7',
                optional = {
                    reasoning_effort = 'minimal',
                    -- 优先考虑吞吐量以加快完成速度
                    provider = {
                        sort = 'throughput',
                    },
                },
            },
        },
    },
}

此功能仍处于高度实验阶段:

  • 它仅针对通用型大语言模型,而非专门用于 NES 的模型,因为我缺乏本地 GPU 资源来进行测试。
  • 来自谷歌竞争对手的类似小型模型——claude-haiku-4.5gpt-5.4-mini——表现较差。
  • 由于生成延迟的限制,尚未实现自动化的二重奏预测。

建议配置模型的思考级别;请参阅提供商章节,以获取关于如何管理各提供商思考设置的指导。

避免为二重奏请求设置过小的 max_tokensmax_completion_tokens 限制。二重奏期望模型返回完整的重写可编辑区域,包括光标标记;如果响应被截断,解析器将会拒绝该响应。当提供商允许时,请保持该限制未设置,或者将其设置得足够大,以覆盖整个重写区域。

待办事项

  • 实现一个合适的差异机制,以便在提示中包含最近的编辑更改。
  • 添加对专用 NES 模型(Zeta、Sweep)的支持。
  • 与 Inception 的托管 API 集成。
  • 实现自动触发的二重奏预测。

默认配置

require('minuet').setup {
    duet = {
        provider = 'gemini', -- `:Minuet duet predict` 使用的提供者。
        request_timeout = 15, -- 单次 duet 请求的超时时间,单位为秒。
        editable_region = {
            lines_before = 8, -- 光标前包含的可编辑行数。
            lines_after = 15, -- 光标后包含的可编辑行数。
            before_region_filter_length = 30, -- 当模型输出重复区域前的不可编辑文本时,从开头裁剪掉重复部分。
            after_region_filter_length = 30, -- 当模型输出重复区域后的不可编辑文本时,从结尾裁剪掉重复部分。
        },
        markers = {
            editable_region_start = '<editable_region>', -- 在提示和响应中包裹可编辑区域起始位置的标记。
            editable_region_end = '</editable_region>', -- 在提示和响应中包裹可编辑区域结束位置的标记。
            cursor_position = '<cursor_position/>', -- 模型必须精确保留一次的标记,用于指示最终光标位置。
        },
        preview = {
            cursor = '', -- 预览中显示在预测光标位置的虚拟标记。
        },
        provider_options = {
            openai = {
                model = 'gpt-5.4-mini', -- duet 请求的默认 OpenAI 模型。
                api_key = 'OPENAI_API_KEY', -- 环境变量名称,或返回 API 密钥的函数。
                end_point = 'https://api.openai.com/v1/chat/completions', -- OpenAI 的聊天完成端点。
                system = { ... }, -- Duet 系统提示配置;除非需要自定义改写提示,否则保持默认。
                few_shots = { ... }, -- 用于引导改写的用户/助手对话示例。
                chat_input = { ... }, -- 序列化可编辑和不可编辑缓冲区区域的模板。
                optional = {}, -- 传递给 OpenAI API 的额外请求体字段。
                transform = {}, -- 发送请求前应用的可选端点/头部/请求体转换。
            },
            claude = {
                model = 'claude-haiku-4-5', -- duet 请求的默认 Claude 模型。
                api_key = 'ANTHROPIC_API_KEY', -- 环境变量名称,或返回 API 密钥的函数。
                end_point = 'https://api.anthropic.com/v1/messages', -- Claude 的消息端点。
                system = { ... }, -- 与其他聊天提供者的 Duet 系统提示结构相同。
                system = { ... }, -- Duet 系统提示配置;除非需要自定义改写提示,否则保持默认。
                few_shots = { ... }, -- 用于引导改写的用户/助手对话示例。
                chat_input = { ... }, -- 序列化可编辑和不可编辑缓冲区区域的模板。
                max_tokens = 8192, -- 请保持此值较大;如果 Claude 截断了改写的可编辑区域,duet 解析将会失败。
                optional = {}, -- 传递给 Claude API 的额外请求体字段。
                transform = {}, -- 发送请求前应用的可选端点/头部/请求体转换。
            },
            gemini = {
                model = 'gemini-3-flash-preview', -- 目前推荐的 duet 模型。
                api_key = 'GEMINI_API_KEY', -- 环境变量名称,或返回 API 密钥的函数。
                end_point = 'https://generativelanguage.googleapis.com/v1beta/models', -- 基础 Gemini 模型端点。
                system = { ... }, -- Duet 系统提示配置;除非需要自定义改写提示,否则保持默认。
                few_shots = { ... }, -- 用于引导改写的用户/助手对话示例。
                chat_input = { ... }, -- 序列化可编辑和不可编辑缓冲区区域的模板。
                optional = {}, -- 传递给 OpenAI API 的额外请求体字段。
                transform = {}, -- 发送请求前应用的可选端点/头部/请求体转换。
            },
            openai_compatible = {
                model = 'minimax/minimax-m2.7', -- 兼容 OpenAI 的聊天提供者的默认模型。
                api_key = 'OPENROUTER_API_KEY', -- 环境变量名称,或返回 API 密钥的函数。
                end_point = 'https://openrouter.ai/api/v1/chat/completions', -- 必须是兼容聊天完成的端点。
                name = 'Openrouter', -- 事件和通知中使用的提供者标签。
                system = { ... }, -- Duet 系统提示配置;除非需要自定义改写提示,否则保持默认。
                few_shots = { ... }, -- 用于引导改写的用户/助手对话示例。
                chat_input = { ... }, -- 序列化可编辑和不可编辑缓冲区区域的模板。
                optional = {}, -- 传递给 OpenAI API 的额外请求体字段。
                transform = {}, -- 发送请求前应用的可选端点/头部/请求体转换。
            },
        },
    },
}

API

虚拟文本

minuet-ai.nvim 提供以下函数来定制你的键映射:

{
    -- 接受整个补全
    require('minuet.virtualtext').action.accept,
    -- 按行接受
    require('minuet.virtualtext').action.accept_line,
    -- 接受 n 行(会提示输入数字)
    require('minuet.virtualtext').action.accept_n_lines,
    require('minuet.virtualtext').action.next,
    require('minuet.virtualtext').action.prev,
    require('minuet.virtualtext').action.dismiss,
    -- 检查当前缓冲区中是否可见虚拟文本
    require('minuet.virtualtext').action.is_visible,
}

Duet

Duet 模块提供了用于以编程方式控制 duet 预测的函数:

{
    require('minuet.duet').action.predict,
    require('minuet.duet').action.apply,
    require('minuet.duet').action.dismiss,
    -- 检查当前缓冲区中是否正在显示 duet 预览
    require('minuet.duet').action.is_visible,
}

Lualine

Minuet 提供了一个 Lualine 组件,用于显示当前 Minuet 请求的状态。该组件会展示:

  • 当前激活的提供者和模型名称
  • 当前请求计数(例如:“1/3”)
  • 处理过程中显示的动画旋转图标

要使用 Minuet 的 Lualine 组件,只需将其添加到你的 Lualine 配置中:

require('lualine').setup {
    sections = {
        lualine_x = {
            {
                require 'minuet.lualine',
                -- 以下为默认配置
                -- 在 Lualine 中显示的名称。可设置为 "provider"、"model" 或 "both"
                -- display_name = 'both',
                -- 当设置为 "both" 时,提供者与模型名称之间的分隔符
                -- provider_model_separator = ':',
                -- 当没有活跃的补全请求时是否显示 display_name
                -- display_on_idle = false,
            },
            'encoding',
            'fileformat',
            'filetype',
        },
    },
}

Minuet 事件

标准补全事件

  • MinuetRequestStartedPre:在补全请求开始之前触发。这允许执行请求前的操作,比如记录日志或更新用户界面。
  • MinuetRequestStarted:补全请求被发送后立即触发,表示请求正在处理中。
  • MinuetRequestFinished:请求完成后触发。

双人协作事件

  • MinuetDuetRequestStartedPre:在双人协作请求开始之前触发。
  • MinuetDuetRequestStarted:双人协作请求被发送后立即触发。
  • MinuetDuetRequestFinished:双人协作请求完成后触发。

事件数据

每个事件都包含一个 data 字段,其中包含以下属性:

  • provider:字符串,表示提供者类型(例如,“openai_compatible”)。
  • name:字符串,指定提供者的名称(例如,“OpenAI”、“Groq”、“Ollama”)。
  • model:字符串,包含模型名称(例如,“gemini-2.0-flash”)。
  • n_requests:本次补全周期内包含的请求数量。
  • request_idx(可选):当前请求的索引,适用于提供者发出多个请求的情况。
  • timestamp:Unix 时间戳,表示请求周期的开始时间(对应于 MinuetRequestStartedPre 事件)。

常见问题解答

自定义 cmp 界面中的源图标和种类图标

你可以通过以下代码片段来配置由 minuet 返回的补全项图标(参考自 cmp 的维基):

local kind_icons = {
    Number = '󰎠',
    Array = '',
    Variable = '',
    -- 其他图标
    -- LLM 提供者图标
    claude = '󰋦',
    openai = '󱢆',
    codestral = '󱎥',
    gemini = '',
    Groq = '',
    Openrouter = '󱂇',
    Ollama = '󰳆',
    ['Llama.cpp'] = '󰳆',
    Deepseek = ''
    -- 默认图标
    fallback = '',
}

local source_icons = {
    minuet = '󱗻',
    nvim_lsp = '',
    lsp = '',
    buffer = '',
    luasnip = '',
    snippets = '',
    path = '',
    git = '',
    tags = '',
    -- 默认图标
    fallback = '󰜚',
}

local cmp = require 'cmp'
cmp.setup {
    formatting = {
        format = function(entry, vim_item)
            -- 种类图标
            -- 将图标与项目种类名称拼接在一起
            vim_item.kind = string.format('%s %s', kind_icons[vim_item.kind] or kind_icons.fallback, vim_item.kind)
            -- 源
            vim_item.menu = source_icons[entry.source.name] or source_icons.fallback
            return vim_item
        end,
    },
}

自定义 blink 界面中的源图标和种类图标

你可以通过以下代码片段来配置由 minuet 返回的补全项图标:

自定义种类图标:

local kind_icons = {
    -- LLM 提供者图标
    claude = '󰋦',
    openai = '󱢆',
    codestral = '󱎥',
    gemini = '',
    Groq = '',
    Openrouter = '󱂇',
    Ollama = '󰳆',
    ['Llama.cpp'] = '󰳆',
    Deepseek = ''
}

require('blink-cmp').setup {
    appearance = {
        use_nvim_cmp_as_default = true,
        nerd_font_variant = 'normal',
        kind_icons = kind_icons
    },
}

自定义源图标:

local source_icons = {
    minuet = '󱗻',
    orgmode = '',
    otter = '󰼁',
    nvim_lsp = '',
    lsp = '',
    buffer = '',
    luasnip = '',
    snippets = '',
    path = '',
    git = '',
    tags = '',
    cmdline = '󰘳',
    latex_symbols = '',
    cmp_nvim_r = '󰟔',
    codeium = '󰩂',
    -- 默认图标
    fallback = '󰜚',
}

require('blink-cmp').setup {
    appearance = {
        use_nvim_cmp_as_default = true,
        nerd_font_variant = 'normal',
        kind_icons = kind_icons
    },
    completion = {
        menu = {
            draw = {
                columns = {
                    { 'label', 'label_description', gap = 1 },
                    { 'kind_icon', 'kind' },
                    { 'source_icon' },
                },
                components = {
                    source_icon = {
                        -- 不截断源图标
                        ellipsis = false,
                        text = function(ctx)
                            return source_icons[ctx.source_name:lower()] or source_icons.fallback
                        end,
                        highlight = 'BlinkCmpSource',
                    },
                },
            },
        },
    }
}

使用 nvim-cmp 时换行输入延迟较大

当使用 Minuet 并启用自动补全功能时,按下 <CR> 键换行可能会偶尔出现明显的延迟。这是因为 Minuet 会在新行开始时触发自动补全,而 cmp 则会阻塞 <CR> 键,等待 Minuet 的响应。

为了解决这个问题,可以考虑以下方案:

  1. 解绑 cmp 键映射中的 <CR> 键。
  2. 使用 cmp 的内部 API 来避免阻塞调用,但请注意,该 API 可能会在未提前通知的情况下发生变化。

以下是使用 Lua 实现第二种方法的示例:

local cmp = require 'cmp'
opts.mapping = {
    ['<CR>'] = cmp.mapping(function(fallback)
        -- 使用内部非阻塞调用来检查 cmp 是否可见
        if cmp.core.view:visible() then
            cmp.confirm { select = true }
        else
            fallback()
        end
    end),
}

lazyvim 的集成

使用 nvim-cmp

{
    'milanglacier/minuet-ai.nvim',
    config = function()
        require('minuet').setup {
            -- 您的配置选项在此处
        }
    end,
},
{
    'nvim-cmp',
    optional = true,
    opts = function(_, opts)
        -- 如果您希望使用自动补全功能
        table.insert(opts.sources, 1, {
            name = 'minuet',
            group_index = 1,
            priority = 100,
        })

        opts.performance = {
            -- 建议增加超时时间,因为与其他补全源相比,LLM 的响应速度通常较慢。如果您只需要手动补全,则无需此设置。
            fetching_timeout = 2000,
        }

        opts.mapping = vim.tbl_deep_extend('force', opts.mapping or {}, {
            -- 如果您希望使用手动补全功能
            ['<A-y>'] = require('minuet').make_cmp_map(),
        })
    end,
}

使用 blink-cmp

-- 在您的 config/options.lua 中设置以下行
vim.g.lazyvim_blink_main = true

{
    'milanglacier/minuet-ai.nvim',
    config = function()
        require('minuet').setup {
            -- 您的配置选项在此处
        }
    end,
},
{
    'saghen/blink.cmp',
    optional = true,
    opts = {
        keymap = {
            ['<A-y>'] = {
                function(cmp)
                    cmp.show { providers = { 'minuet' } }
                end,
            },
        },
        sources = {
            -- 如果您想使用自动补全功能
            default =  { 'minuet' },
            providers = {
                minuet = {
                    name = 'minuet',
                    module = 'minuet.blink',
                    score_offset = 100,
                },
            },
        },
    },
}

增强功能

RAG(实验性)

您可以通过利用 VectorCode 包提供的 RAG 支持,增强发送给 LLM 用于代码补全的内容。

VectorCode 包含两个主要组件。第一个是用 Python 编写的独立 CLI 程序,可通过 PyPI 安装。该程序负责创建向量数据库并处理 RAG 查询。第二个组件是 Neovim 插件,它提供了在 Neovim 内发送查询和管理与缓冲区相关的 RAG 信息的实用函数。

我们提供了两个示例配方来演示 VectorCode 的集成:一个适用于基于聊天的 LLM(Gemini),另一个适用于 FIM 模型(Qwen-2.5-Coder),这些配方可在 recipes.md 中找到。

有关设置和使用 VectorCode 的详细说明,请参阅 官方 VectorCode 文档

故障排除

如果您的设置失败,最可能的原因有两个:

  1. 您可能错误地设置了 API 密钥。请查看 API 密钥 部分,了解如何正确指定 API 密钥。
  2. 您正在使用模型或上下文窗口过大,导致补全项在返回任何标记之前就超时了。这种情况在本地 LLM 中尤为常见。建议从以下设置开始,以便更好地了解您的提供商的推理速度:
    • 首先尝试手动补全。
    • 使用较小的上下文窗口(例如 config.context_window = 768)。
    • 使用较小的模型。
    • 设置较长的请求超时时间(例如 config.request_timeout = 5)。

要诊断问题,请将 config.notify = debug 设置为启用,并检查输出。

贡献

欢迎贡献!请随时提交 Pull Request。

致谢

  • cmp-ai:与 nvim-cmp 集成的参考。
  • continue.dev:虽然不是 Neovim 插件,但我从中找到了许多 LLM 模型。
  • copilot.lua:虚拟文本前端的参考。
  • llama.vim:用于启动 llama-cpp 服务器的 CLI 参数参考。
  • crates.nvim:用于提供补全的进程内 LSP 实现参考。

版本历史

v0.8.02025/12/14
v0.7.02025/12/09
v0.6.02025/08/11
v0.5.22025/05/12
v0.5.12025/04/08
v0.5.02025/03/28
v0.4.22025/03/21
v0.4.12025/03/19
v0.4.02025/03/19

常见问题

相似工具推荐

openclaw

OpenClaw 是一款专为个人打造的本地化 AI 助手,旨在让你在自己的设备上拥有完全可控的智能伙伴。它打破了传统 AI 助手局限于特定网页或应用的束缚,能够直接接入你日常使用的各类通讯渠道,包括微信、WhatsApp、Telegram、Discord、iMessage 等数十种平台。无论你在哪个聊天软件中发送消息,OpenClaw 都能即时响应,甚至支持在 macOS、iOS 和 Android 设备上进行语音交互,并提供实时的画布渲染功能供你操控。 这款工具主要解决了用户对数据隐私、响应速度以及“始终在线”体验的需求。通过将 AI 部署在本地,用户无需依赖云端服务即可享受快速、私密的智能辅助,真正实现了“你的数据,你做主”。其独特的技术亮点在于强大的网关架构,将控制平面与核心助手分离,确保跨平台通信的流畅性与扩展性。 OpenClaw 非常适合希望构建个性化工作流的技术爱好者、开发者,以及注重隐私保护且不愿被单一生态绑定的普通用户。只要具备基础的终端操作能力(支持 macOS、Linux 及 Windows WSL2),即可通过简单的命令行引导完成部署。如果你渴望拥有一个懂你

349.3k|★★★☆☆|1周前
Agent开发框架图像

stable-diffusion-webui

stable-diffusion-webui 是一个基于 Gradio 构建的网页版操作界面,旨在让用户能够轻松地在本地运行和使用强大的 Stable Diffusion 图像生成模型。它解决了原始模型依赖命令行、操作门槛高且功能分散的痛点,将复杂的 AI 绘图流程整合进一个直观易用的图形化平台。 无论是希望快速上手的普通创作者、需要精细控制画面细节的设计师,还是想要深入探索模型潜力的开发者与研究人员,都能从中获益。其核心亮点在于极高的功能丰富度:不仅支持文生图、图生图、局部重绘(Inpainting)和外绘(Outpainting)等基础模式,还独创了注意力机制调整、提示词矩阵、负向提示词以及“高清修复”等高级功能。此外,它内置了 GFPGAN 和 CodeFormer 等人脸修复工具,支持多种神经网络放大算法,并允许用户通过插件系统无限扩展能力。即使是显存有限的设备,stable-diffusion-webui 也提供了相应的优化选项,让高质量的 AI 艺术创作变得触手可及。

162.1k|★★★☆☆|1周前
开发框架图像Agent

everything-claude-code

everything-claude-code 是一套专为 AI 编程助手(如 Claude Code、Codex、Cursor 等)打造的高性能优化系统。它不仅仅是一组配置文件,而是一个经过长期实战打磨的完整框架,旨在解决 AI 代理在实际开发中面临的效率低下、记忆丢失、安全隐患及缺乏持续学习能力等核心痛点。 通过引入技能模块化、直觉增强、记忆持久化机制以及内置的安全扫描功能,everything-claude-code 能显著提升 AI 在复杂任务中的表现,帮助开发者构建更稳定、更智能的生产级 AI 代理。其独特的“研究优先”开发理念和针对 Token 消耗的优化策略,使得模型响应更快、成本更低,同时有效防御潜在的攻击向量。 这套工具特别适合软件开发者、AI 研究人员以及希望深度定制 AI 工作流的技术团队使用。无论您是在构建大型代码库,还是需要 AI 协助进行安全审计与自动化测试,everything-claude-code 都能提供强大的底层支持。作为一个曾荣获 Anthropic 黑客大奖的开源项目,它融合了多语言支持与丰富的实战钩子(hooks),让 AI 真正成长为懂上

159.6k|★★☆☆☆|今天
开发框架Agent语言模型

opencode

OpenCode 是一款开源的 AI 编程助手(Coding Agent),旨在像一位智能搭档一样融入您的开发流程。它不仅仅是一个代码补全插件,而是一个能够理解项目上下文、自主规划任务并执行复杂编码操作的智能体。无论是生成全新功能、重构现有代码,还是排查难以定位的 Bug,OpenCode 都能通过自然语言交互高效完成,显著减少开发者在重复性劳动和上下文切换上的时间消耗。 这款工具专为软件开发者、工程师及技术研究人员设计,特别适合希望利用大模型能力来提升编码效率、加速原型开发或处理遗留代码维护的专业人群。其核心亮点在于完全开源的架构,这意味着用户可以审查代码逻辑、自定义行为策略,甚至私有化部署以保障数据安全,彻底打破了传统闭源 AI 助手的“黑盒”限制。 在技术体验上,OpenCode 提供了灵活的终端界面(Terminal UI)和正在测试中的桌面应用程序,支持 macOS、Windows 及 Linux 全平台。它兼容多种包管理工具,安装便捷,并能无缝集成到现有的开发环境中。无论您是追求极致控制权的资深极客,还是渴望提升产出的独立开发者,OpenCode 都提供了一个透明、可信

144.3k|★☆☆☆☆|昨天
Agent插件

ComfyUI

ComfyUI 是一款功能强大且高度模块化的视觉 AI 引擎,专为设计和执行复杂的 Stable Diffusion 图像生成流程而打造。它摒弃了传统的代码编写模式,采用直观的节点式流程图界面,让用户通过连接不同的功能模块即可构建个性化的生成管线。 这一设计巧妙解决了高级 AI 绘图工作流配置复杂、灵活性不足的痛点。用户无需具备编程背景,也能自由组合模型、调整参数并实时预览效果,轻松实现从基础文生图到多步骤高清修复等各类复杂任务。ComfyUI 拥有极佳的兼容性,不仅支持 Windows、macOS 和 Linux 全平台,还广泛适配 NVIDIA、AMD、Intel 及苹果 Silicon 等多种硬件架构,并率先支持 SDXL、Flux、SD3 等前沿模型。 无论是希望深入探索算法潜力的研究人员和开发者,还是追求极致创作自由度的设计师与资深 AI 绘画爱好者,ComfyUI 都能提供强大的支持。其独特的模块化架构允许社区不断扩展新功能,使其成为当前最灵活、生态最丰富的开源扩散模型工具之一,帮助用户将创意高效转化为现实。

108.3k|★★☆☆☆|1周前
开发框架图像Agent

gemini-cli

gemini-cli 是一款由谷歌推出的开源 AI 命令行工具,它将强大的 Gemini 大模型能力直接集成到用户的终端环境中。对于习惯在命令行工作的开发者而言,它提供了一条从输入提示词到获取模型响应的最短路径,无需切换窗口即可享受智能辅助。 这款工具主要解决了开发过程中频繁上下文切换的痛点,让用户能在熟悉的终端界面内直接完成代码理解、生成、调试以及自动化运维任务。无论是查询大型代码库、根据草图生成应用,还是执行复杂的 Git 操作,gemini-cli 都能通过自然语言指令高效处理。 它特别适合广大软件工程师、DevOps 人员及技术研究人员使用。其核心亮点包括支持高达 100 万 token 的超长上下文窗口,具备出色的逻辑推理能力;内置 Google 搜索、文件操作及 Shell 命令执行等实用工具;更独特的是,它支持 MCP(模型上下文协议),允许用户灵活扩展自定义集成,连接如图像生成等外部能力。此外,个人谷歌账号即可享受免费的额度支持,且项目基于 Apache 2.0 协议完全开源,是提升终端工作效率的理想助手。

100.8k|★★☆☆☆|1周前
插件Agent图像