[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"similar-milanglacier--minuet-ai.nvim":3,"tool-milanglacier--minuet-ai.nvim":62},[4,18,26,36,46,54],{"id":5,"name":6,"github_repo":7,"description_zh":8,"stars":9,"difficulty_score":10,"last_commit_at":11,"category_tags":12,"status":17},4358,"openclaw","openclaw\u002Fopenclaw","OpenClaw 是一款专为个人打造的本地化 AI 助手，旨在让你在自己的设备上拥有完全可控的智能伙伴。它打破了传统 AI 助手局限于特定网页或应用的束缚，能够直接接入你日常使用的各类通讯渠道，包括微信、WhatsApp、Telegram、Discord、iMessage 等数十种平台。无论你在哪个聊天软件中发送消息，OpenClaw 都能即时响应，甚至支持在 macOS、iOS 和 Android 设备上进行语音交互，并提供实时的画布渲染功能供你操控。\n\n这款工具主要解决了用户对数据隐私、响应速度以及“始终在线”体验的需求。通过将 AI 部署在本地，用户无需依赖云端服务即可享受快速、私密的智能辅助，真正实现了“你的数据，你做主”。其独特的技术亮点在于强大的网关架构，将控制平面与核心助手分离，确保跨平台通信的流畅性与扩展性。\n\nOpenClaw 非常适合希望构建个性化工作流的技术爱好者、开发者，以及注重隐私保护且不愿被单一生态绑定的普通用户。只要具备基础的终端操作能力（支持 macOS、Linux 及 Windows WSL2），即可通过简单的命令行引导完成部署。如果你渴望拥有一个懂你",349277,3,"2026-04-06T06:32:30",[13,14,15,16],"Agent","开发框架","图像","数据工具","ready",{"id":19,"name":20,"github_repo":21,"description_zh":22,"stars":23,"difficulty_score":10,"last_commit_at":24,"category_tags":25,"status":17},3808,"stable-diffusion-webui","AUTOMATIC1111\u002Fstable-diffusion-webui","stable-diffusion-webui 是一个基于 Gradio 构建的网页版操作界面，旨在让用户能够轻松地在本地运行和使用强大的 Stable Diffusion 图像生成模型。它解决了原始模型依赖命令行、操作门槛高且功能分散的痛点，将复杂的 AI 绘图流程整合进一个直观易用的图形化平台。\n\n无论是希望快速上手的普通创作者、需要精细控制画面细节的设计师，还是想要深入探索模型潜力的开发者与研究人员，都能从中获益。其核心亮点在于极高的功能丰富度：不仅支持文生图、图生图、局部重绘（Inpainting）和外绘（Outpainting）等基础模式，还独创了注意力机制调整、提示词矩阵、负向提示词以及“高清修复”等高级功能。此外，它内置了 GFPGAN 和 CodeFormer 等人脸修复工具，支持多种神经网络放大算法，并允许用户通过插件系统无限扩展能力。即使是显存有限的设备，stable-diffusion-webui 也提供了相应的优化选项，让高质量的 AI 艺术创作变得触手可及。",162132,"2026-04-05T11:01:52",[14,15,13],{"id":27,"name":28,"github_repo":29,"description_zh":30,"stars":31,"difficulty_score":32,"last_commit_at":33,"category_tags":34,"status":17},1381,"everything-claude-code","affaan-m\u002Feverything-claude-code","everything-claude-code 是一套专为 AI 编程助手（如 Claude Code、Codex、Cursor 等）打造的高性能优化系统。它不仅仅是一组配置文件，而是一个经过长期实战打磨的完整框架，旨在解决 AI 代理在实际开发中面临的效率低下、记忆丢失、安全隐患及缺乏持续学习能力等核心痛点。\n\n通过引入技能模块化、直觉增强、记忆持久化机制以及内置的安全扫描功能，everything-claude-code 能显著提升 AI 在复杂任务中的表现，帮助开发者构建更稳定、更智能的生产级 AI 代理。其独特的“研究优先”开发理念和针对 Token 消耗的优化策略，使得模型响应更快、成本更低，同时有效防御潜在的攻击向量。\n\n这套工具特别适合软件开发者、AI 研究人员以及希望深度定制 AI 工作流的技术团队使用。无论您是在构建大型代码库，还是需要 AI 协助进行安全审计与自动化测试，everything-claude-code 都能提供强大的底层支持。作为一个曾荣获 Anthropic 黑客大奖的开源项目，它融合了多语言支持与丰富的实战钩子（hooks），让 AI 真正成长为懂上",159636,2,"2026-04-17T23:33:34",[14,13,35],"语言模型",{"id":37,"name":38,"github_repo":39,"description_zh":40,"stars":41,"difficulty_score":42,"last_commit_at":43,"category_tags":44,"status":17},8272,"opencode","anomalyco\u002Fopencode","OpenCode 是一款开源的 AI 编程助手（Coding Agent），旨在像一位智能搭档一样融入您的开发流程。它不仅仅是一个代码补全插件，而是一个能够理解项目上下文、自主规划任务并执行复杂编码操作的智能体。无论是生成全新功能、重构现有代码，还是排查难以定位的 Bug，OpenCode 都能通过自然语言交互高效完成，显著减少开发者在重复性劳动和上下文切换上的时间消耗。\n\n这款工具专为软件开发者、工程师及技术研究人员设计，特别适合希望利用大模型能力来提升编码效率、加速原型开发或处理遗留代码维护的专业人群。其核心亮点在于完全开源的架构，这意味着用户可以审查代码逻辑、自定义行为策略，甚至私有化部署以保障数据安全，彻底打破了传统闭源 AI 助手的“黑盒”限制。\n\n在技术体验上，OpenCode 提供了灵活的终端界面（Terminal UI）和正在测试中的桌面应用程序，支持 macOS、Windows 及 Linux 全平台。它兼容多种包管理工具，安装便捷，并能无缝集成到现有的开发环境中。无论您是追求极致控制权的资深极客，还是渴望提升产出的独立开发者，OpenCode 都提供了一个透明、可信",144296,1,"2026-04-16T14:50:03",[13,45],"插件",{"id":47,"name":48,"github_repo":49,"description_zh":50,"stars":51,"difficulty_score":32,"last_commit_at":52,"category_tags":53,"status":17},2271,"ComfyUI","Comfy-Org\u002FComfyUI","ComfyUI 是一款功能强大且高度模块化的视觉 AI 引擎，专为设计和执行复杂的 Stable Diffusion 图像生成流程而打造。它摒弃了传统的代码编写模式，采用直观的节点式流程图界面，让用户通过连接不同的功能模块即可构建个性化的生成管线。\n\n这一设计巧妙解决了高级 AI 绘图工作流配置复杂、灵活性不足的痛点。用户无需具备编程背景，也能自由组合模型、调整参数并实时预览效果，轻松实现从基础文生图到多步骤高清修复等各类复杂任务。ComfyUI 拥有极佳的兼容性，不仅支持 Windows、macOS 和 Linux 全平台，还广泛适配 NVIDIA、AMD、Intel 及苹果 Silicon 等多种硬件架构，并率先支持 SDXL、Flux、SD3 等前沿模型。\n\n无论是希望深入探索算法潜力的研究人员和开发者，还是追求极致创作自由度的设计师与资深 AI 绘画爱好者，ComfyUI 都能提供强大的支持。其独特的模块化架构允许社区不断扩展新功能，使其成为当前最灵活、生态最丰富的开源扩散模型工具之一，帮助用户将创意高效转化为现实。",108322,"2026-04-10T11:39:34",[14,15,13],{"id":55,"name":56,"github_repo":57,"description_zh":58,"stars":59,"difficulty_score":32,"last_commit_at":60,"category_tags":61,"status":17},6121,"gemini-cli","google-gemini\u002Fgemini-cli","gemini-cli 是一款由谷歌推出的开源 AI 命令行工具，它将强大的 Gemini 大模型能力直接集成到用户的终端环境中。对于习惯在命令行工作的开发者而言，它提供了一条从输入提示词到获取模型响应的最短路径，无需切换窗口即可享受智能辅助。\n\n这款工具主要解决了开发过程中频繁上下文切换的痛点，让用户能在熟悉的终端界面内直接完成代码理解、生成、调试以及自动化运维任务。无论是查询大型代码库、根据草图生成应用，还是执行复杂的 Git 操作，gemini-cli 都能通过自然语言指令高效处理。\n\n它特别适合广大软件工程师、DevOps 人员及技术研究人员使用。其核心亮点包括支持高达 100 万 token 的超长上下文窗口，具备出色的逻辑推理能力；内置 Google 搜索、文件操作及 Shell 命令执行等实用工具；更独特的是，它支持 MCP（模型上下文协议），允许用户灵活扩展自定义集成，连接如图像生成等外部能力。此外，个人谷歌账号即可享受免费的额度支持，且项目基于 Apache 2.0 协议完全开源，是提升终端工作效率的理想助手。",100752,"2026-04-10T01:20:03",[45,13,15,14],{"id":63,"github_repo":64,"name":65,"description_en":66,"description_zh":67,"ai_summary_zh":68,"readme_en":69,"readme_zh":70,"quickstart_zh":71,"use_case_zh":72,"hero_image_url":73,"owner_login":74,"owner_name":75,"owner_avatar_url":76,"owner_bio":75,"owner_company":75,"owner_location":75,"owner_email":75,"owner_twitter":75,"owner_website":75,"owner_url":77,"languages":78,"stars":91,"forks":92,"last_commit_at":93,"license":94,"difficulty_score":32,"env_os":95,"env_gpu":96,"env_ram":97,"env_deps":98,"category_tags":106,"github_topics":107,"view_count":32,"oss_zip_url":75,"oss_zip_packed_at":75,"status":17,"created_at":113,"updated_at":114,"faqs":115,"releases":144},8907,"milanglacier\u002Fminuet-ai.nvim","minuet-ai.nvim","💃 Dance with Intelligence in Your Code. Minuet offers code completion as-you-type from popular LLMs including OpenAI, Gemini, Claude, Ollama, Llama.cpp, Codestral, and more.","minuet-ai.nvim 是一款专为 Neovim 打造的智能代码补全插件，旨在让开发者在编码时如同与智能伙伴共舞般流畅。它解决了传统静态补全无法理解复杂上下文的问题，通过实时连接 OpenAI、Claude、Gemini、Ollama 及本地 Llama.cpp 等主流大语言模型，提供精准的“边写边补”体验。\n\n该工具特别适合追求高效工作流的软件开发者和技术极客。其核心亮点在于灵活的架构设计：既支持基于聊天的通用模型进行专用提示词优化，也兼容 DeepSeek、Codestral 等模型的“中间填充”（FIM）模式。minuet-ai.nvim 无需后台运行专有二进制文件，仅依赖轻量级网络请求即可工作，并具备流式传输能力，即使在使用较慢的模型时也能保持响应流畅。\n\n此外，它深度集成了 nvim-cmp、blink-cmp 及 Neovim 原生补全前端，甚至可作为进程内 LSP 服务器运行。独特的“逐行接受”功能允许用户按自己的节奏采纳多行建议，而当用户输入与建议开头匹配时，插件会自动同步而非丢弃结果，有效减少了不必要的 API 调用和资源消耗。无论是云端强大模型还是本地私有部署","minuet-ai.nvim 是一款专为 Neovim 打造的智能代码补全插件，旨在让开发者在编码时如同与智能伙伴共舞般流畅。它解决了传统静态补全无法理解复杂上下文的问题，通过实时连接 OpenAI、Claude、Gemini、Ollama 及本地 Llama.cpp 等主流大语言模型，提供精准的“边写边补”体验。\n\n该工具特别适合追求高效工作流的软件开发者和技术极客。其核心亮点在于灵活的架构设计：既支持基于聊天的通用模型进行专用提示词优化，也兼容 DeepSeek、Codestral 等模型的“中间填充”（FIM）模式。minuet-ai.nvim 无需后台运行专有二进制文件，仅依赖轻量级网络请求即可工作，并具备流式传输能力，即使在使用较慢的模型时也能保持响应流畅。\n\n此外，它深度集成了 nvim-cmp、blink-cmp 及 Neovim 原生补全前端，甚至可作为进程内 LSP 服务器运行。独特的“逐行接受”功能允许用户按自己的节奏采纳多行建议，而当用户输入与建议开头匹配时，插件会自动同步而非丢弃结果，有效减少了不必要的 API 调用和资源消耗。无论是云端强大模型还是本地私有部署，minuet-ai.nvim 都能帮助开发者提升编码效率，享受更智能的编写体验。","- [Minuet](#minuet)\n- [Features](#features)\n- [Requirements](#requirements)\n- [Installation](#installation)\n- [Quick Start](#quick-start)\n  - [Virtual Text Setup](#virtual-text-setup)\n  - [Nvim-cmp setup](#nvim-cmp-setup)\n  - [Blink-cmp Setup](#blink-cmp-setup)\n  - [In-Process LSP for Built-in Completion and Inline Completion](#in-process-lsp-for-built-in-completion-and-inline-completion)\n    - [Completion](#completion)\n    - [Inline completion](#inline-completion)\n  - [LLM Provider Examples](#llm-provider-examples)\n    - [Openrouter Kimi-K2](#openrouter-kimi-k2)\n    - [Deepseek](#deepseek)\n    - [Ollama Qwen-2.5-coder:7b](#ollama-qwen-25-coder7b)\n    - [Llama.cpp Qwen-2.5-coder:1.5b](#llamacpp-qwen-25-coder15b)\n- [Selecting a Provider or Model](#selecting-a-provider-or-model)\n  - [Understanding Model Speed](#understanding-model-speed)\n- [Configuration](#configuration)\n- [API Keys](#api-keys)\n- [Prompt](#prompt)\n  - [Prefix-First vs. Suffix-First](#prefix-first-vs-suffix-first)\n- [Providers](#providers)\n  - [OpenAI](#openai)\n  - [Claude](#claude)\n  - [Codestral](#codestral)\n  - [Mercury Coder](#mercury-coder)\n  - [Gemini](#gemini)\n  - [OpenAI-compatible](#openai-compatible)\n  - [OpenAI-FIM-compatible](#openai-fim-compatible)\n    - [Non-OpenAI-FIM-Compatible APIs](#non-openai-fim-compatible-apis)\n- [Commands](#commands)\n  - [`Minuet change_provider`, `Minuet change_model`](#minuet-change_provider-minuet-change_model)\n  - [`Minuet change_preset`](#minuet-change_preset)\n  - [`Minuet blink`, `Minuet cmp`](#minuet-blink-minuet-cmp)\n  - [`Minuet virtualtext`](#minuet-virtualtext)\n  - [`Minuet duet`](#minuet-duet)\n  - [`Minuet lsp`](#minuet-lsp)\n- [Duet (Next Edit Prediction)](#duet-next-edit-prediction)\n  - [TODO](#todo)\n  - [Default Config](#default-config)\n- [API](#api)\n  - [Virtual Text](#virtual-text)\n  - [Lualine](#lualine)\n  - [Minuet Event](#minuet-event)\n- [FAQ](#faq)\n  - [Customize `cmp` ui for source icon and kind icon](#customize-cmp-ui-for-source-icon-and-kind-icon)\n  - [Customize `blink` ui for source icon and kind icon](#customize-blink-ui-for-source-icon-and-kind-icon)\n  - [Significant Input Delay When Moving to a New Line with `nvim-cmp`](#significant-input-delay-when-moving-to-a-new-line-with-nvim-cmp)\n  - [Integration with `lazyvim`](#integration-with-lazyvim)\n- [Enhancement](#enhancement)\n  - [RAG (Experimental)](#rag-experimental)\n- [Troubleshooting](#troubleshooting)\n- [Contributing](#contributing)\n- [Acknowledgement](#acknowledgement)\n\n# Minuet\n\nMinuet: Dance with Intelligence in Your Code 💃.\n\n`Minuet` brings the grace and harmony of a minuet to your coding process.\nJust as dancers move during a minuet.\n\n# Features\n\n- AI-powered code completion with dual modes:\n  - Specialized prompts and various enhancements for chat-based LLMs on code completion tasks.\n  - Fill-in-the-middle (FIM) completion for compatible models (DeepSeek,\n    Codestral, Qwen, and others).\n- Support for multiple AI providers (OpenAI, Claude, Gemini, Codestral, Ollama,\n  Llama-cpp, and OpenAI-compatible services).\n- Customizable configuration options.\n- Streaming support to enable completion delivery even with slower LLMs.\n- No proprietary binary running in the background. Just curl and your preferred LLM provider.\n- Support `virtual-text`, `nvim-cmp`, `blink-cmp`, `built-in`,\n  `mini.completion` frontend.\n- Act as an **in-process LSP** server to provide completions (opt-in feature).\n- Accept multi-line suggestions line-by-line, so longer suggestions can be\n  pulled in incrementally in your own pace.\n- When your typed text matches the start of a suggestion, Minuet keeps the\n  completion in sync of your typed text rather than discarding it, to reduce\n  unnecessary LLM requests and conserving resources.\n- Support next-edit prediction (NES) via `Minuet duet` commands. This feature\n  is highly experimental.\n\n**With nvim-cmp \u002F blink-cmp frontend**:\n\n![example-cmp](https:\u002F\u002Foss.gittoolsai.com\u002Fimages\u002Fmilanglacier_minuet-ai.nvim_readme_b90b830929e1.png)\n\n**With builtin completion frontend** (requires nvim 0.11+):\n\n![example-builtin-completion](https:\u002F\u002Foss.gittoolsai.com\u002Fimages\u002Fmilanglacier_minuet-ai.nvim_readme_9c2e01383476.jpg)\n\n**With virtual text frontend**:\n\n![example-virtual-text](https:\u002F\u002Foss.gittoolsai.com\u002Fimages\u002Fmilanglacier_minuet-ai.nvim_readme_4a672921709a.png)\n\nhttps:\u002F\u002Fgithub.com\u002Fuser-attachments\u002Fassets\u002Fe0c4f2bd-0361-45b4-8eb4-0f49356bd7d9\n\n**With duet (next-edit prediction)**:\n\nhttps:\u002F\u002Fgithub.com\u002Fuser-attachments\u002Fassets\u002Fb98699d5-b81d-4061-a1b3-d6f581c6b9b0\n\n\u003C!-- The link above is a showcase video for the virtual text feature, hosted -->\n\u003C!-- externally on GitHub. -->\n\n# Requirements\n\n- Neovim 0.10+.\n- optional: [nvim-cmp](https:\u002F\u002Fgithub.com\u002Fhrsh7th\u002Fnvim-cmp)\n- optional: [blink.cmp](https:\u002F\u002Fgithub.com\u002FSaghen\u002Fblink.cmp)\n- An API key for at least one of the supported AI providers\n- ~~[plenary.nvim](https:\u002F\u002Fgithub.com\u002Fnvim-lua\u002Fplenary.nvim)~~ Minuet now uses\n  the builtin `vim.system` and no longer requires plenary.\n\n# Installation\n\n**Lazy.nvim**:\n\n```lua\nspecs = {\n    {\n        'milanglacier\u002Fminuet-ai.nvim',\n        config = function()\n            require('minuet').setup {\n                -- Your configuration options here\n            }\n        end,\n    },\n    -- optional, if you are using virtual-text frontend, nvim-cmp is not\n    -- required.\n    { 'hrsh7th\u002Fnvim-cmp' },\n    -- optional, if you are using virtual-text frontend, blink is not required.\n    { 'Saghen\u002Fblink.cmp' },\n}\n```\n\n**Rocks.nvim**:\n\n`Minuet` is available on luarocks.org. Simply run `Rocks install minuet-ai.nvim` to install it like any other luarocks package.\n\n# Quick Start\n\n## Virtual Text Setup\n\n```lua\nrequire('minuet').setup {\n    virtualtext = {\n        auto_trigger_ft = {},\n        keymap = {\n            -- accept whole completion\n            accept = '\u003CA-A>',\n            -- accept one line\n            accept_line = '\u003CA-a>',\n            -- accept n lines (prompts for number)\n            -- e.g. \"A-z 2 CR\" will accept 2 lines\n            accept_n_lines = '\u003CA-z>',\n            -- Cycle to prev completion item, or manually invoke completion\n            prev = '\u003CA-[>',\n            -- Cycle to next completion item, or manually invoke completion\n            next = '\u003CA-]>',\n            dismiss = '\u003CA-e>',\n        },\n    },\n}\n```\n\n## Nvim-cmp setup\n\n\u003Cdetails>\n\n```lua\nrequire('cmp').setup {\n    sources = {\n        {\n             -- Include minuet as a source to enable autocompletion\n            { name = 'minuet' },\n            -- and your other sources\n        }\n    },\n    performance = {\n        -- It is recommended to increase the timeout duration due to\n        -- the typically slower response speed of LLMs compared to\n        -- other completion sources. This is not needed when you only\n        -- need manual completion.\n        fetching_timeout = 2000,\n    },\n}\n\n\n-- If you wish to invoke completion manually,\n-- The following configuration binds `A-y` key\n-- to invoke the configuration manually.\nrequire('cmp').setup {\n    mapping = {\n        [\"\u003CA-y>\"] = require('minuet').make_cmp_map()\n        -- and your other keymappings\n    },\n}\n```\n\n\u003C\u002Fdetails>\n\n## Blink-cmp Setup\n\n\u003Cdetails>\n\n```lua\nrequire('blink-cmp').setup {\n    keymap = {\n        -- Manually invoke minuet completion.\n        ['\u003CA-y>'] = require('minuet').make_blink_map(),\n    },\n    sources = {\n         -- Enable minuet for autocomplete\n        default = { 'lsp', 'path', 'buffer', 'snippets', 'minuet' },\n        -- For manual completion only, remove 'minuet' from default\n        providers = {\n            minuet = {\n                name = 'minuet',\n                module = 'minuet.blink',\n                async = true,\n                -- Should match minuet.config.request_timeout * 1000,\n                -- since minuet.config.request_timeout is in seconds\n                timeout_ms = 3000,\n                score_offset = 50, -- Gives minuet higher priority among suggestions\n            },\n        },\n    },\n    -- Recommended to avoid unnecessary request\n    completion = { trigger = { prefetch_on_insert = false } },\n}\n```\n\n\u003C\u002Fdetails>\n\n## In-Process LSP for Built-in Completion and Inline Completion\n\n\u003Cdetails>\n\n**Requirements:**\n\n- Neovim version 0.11 or higher is necessary for built-in completion.\n- Neovim version 0.12 or higher is necessary for `vim.lsp.inline_completion`.\n\n**Note:**\n\n`config.lsp.completion.enable` and `config.lsp.inline_completion.enable` are\nsetup-time options. Minuet decides which LSP capabilities to expose when\n`require('minuet').setup()` runs, so changing either option later will not\nenable the feature for an already-running Minuet LSP server.\n\nIf you might want to use one of these features later in the same session,\nenable it during setup first, then control only its per-buffer auto-trigger\nbehavior at runtime.\n\n### Completion\n\n```lua\nrequire('minuet').setup {\n    lsp = {\n        enabled_ft = { 'toml', 'lua', 'cpp' },\n        completion = {\n            -- Enables automatic completion triggering using `vim.lsp.completion.enable`\n            enabled_auto_trigger_ft = { 'cpp', 'lua' },\n        },\n    }\n}\n```\n\nThe `completion.enabled_auto_trigger_ft` setting is relevant only for built-in\ncompletion (`vim.lsp.completion`). `Mini.Completion` users can ignore this\noption, as Mini.Completion uses **all** available LSPs for **auto-triggered**\ncompletion.\n\nFor manually triggered completion, ensure `vim.bo.omnifunc` is set to\n`v:lua.vim.lsp.omnifunc` and use `\u003CC-x>\u003CC-o>` in Insert mode.\n\n**Recommendation:**\n\nFor users of `blink-cmp` and `nvim-cmp`, it is recommended to use the native\nsource rather than through LSP for two main reasons:\n\n1. `blink-cmp` and `nvim-cmp` offer better sorting and async management when\n   Minuet is utilized as a separate source rather than alongside a regular LSP\n   such as `clangd`.\n2. With `blink-cmp` and `nvim-cmp` native sources, it's possible to configure\n   Minuet for manual completion only, disabling automatic completion. However,\n   when Minuet operates as an LSP server, it is impossible to determine whether\n   completion is triggered automatically or manually.\n\n   The LSP protocol specification defines three `triggerKind` values:\n   `Invoked`, `TriggerCharacter`, and `TriggerForIncompleteCompletions`.\n   However, none of these specifically differentiates between manual and\n   automatic completion requests.\n\n**Note**:\n\n- An upstream issue ([tracked\n  here](https:\u002F\u002Fgithub.com\u002Fneovim\u002Fneovim\u002Fissues\u002F32972)) may cause unexpected\n  indentation behavior when accepting multi-line completions.\n\n  Currently, Minuet offers the config option `config.lsp.completion.adjust_indentation`\n  (enabled by default) as a temporary workaround. However, the author\n  acknowledges that this solution is incomplete and may introduce additional edge\n  cases when enabled.\n\n  Therefore, consider the following practices when using built-in completion:\n  - Ensure `config.add_single_line_entry = true` and only accept single-line completions.\n  - Avoid using Minuet and built-in completion with languages where indentation\n    affects semantics, such as Python.\n\n- Users might call `vim.lsp.completion.enable {autotrigger = true}` during\n  the `LspAttach` event when the client supports completion. However, this is\n  not the desired behavior for Minuet. As an LLM completion source, Minuet can\n  face significant rate limits during automatic triggering.\n\n  Therefore, it's recommended to enable Minuet for automatic triggering using\n  the `config.lsp.completion.enabled_auto_trigger_ft` setting.\n\n  For users who uses `LspAttach` event, it is recommeded to verify that the\n  server is not the Minuet server before enabling autotrigger. An example\n  configuration is shown below:\n\n```lua\nvim.api.nvim_create_autocmd('LspAttach', {\n    callback = function(args)\n        local client_id = args.data.client_id\n        local bufnr = args.buf\n        local client = vim.lsp.get_client_by_id(client_id)\n        if not client then\n            return\n        end\n\n        if client.server_capabilities.completionProvider and client.name ~= 'minuet' then\n            vim.lsp.completion.enable(true, client_id, bufnr, { autotrigger = true })\n        end\n    end,\n    desc = 'Enable built-in auto completion',\n})\n```\n\n### Inline completion\n\nMinuet can also expose suggestions through Neovim's built-in\n`vim.lsp.inline_completion` interface:\n\n```lua\nrequire('minuet').setup {\n    lsp = {\n        enabled_ft = { 'toml', 'lua', 'cpp' },\n        inline_completion = {\n            enable = true,\n            enabled_auto_trigger_ft = { 'cpp', 'lua' },\n        },\n    },\n}\n\nvim.keymap.set('i', '\u003CA-x>', function()\n    vim.lsp.inline_completion.get()\nend, { desc = 'accept' })\nvim.keymap.set('i', '\u003CA-c>', function()\n    vim.lsp.inline_completion.select { count = 1 }\nend, { desc = 'cycle to next' })\nvim.keymap.set('i', '\u003CA-v>', function()\n    vim.lsp.inline_completion.select { count = -1 }\nend, { desc = 'cycle to prev' })\n```\n\nIf you prefer not to use inline completion at startup but still want the option\nto enable it for specific buffers later, set\n`config.lsp.inline_completion.enable = true` during setup and leave\n`config.lsp.inline_completion.enabled_auto_trigger_ft` empty. You can then\nenable it at runtime for the current buffer with: `:Minuet lsp\ninline_completion enable_auto_trigger`.\n\n**Recommendation:**\n\nIf you want inline suggestions, Minuet's own `virtualtext` frontend is still\nthe **recommended** choice. Neovim's built-in `inline_completion` support is a\nuseful baseline, but in practice it only covers automatic triggering. Minuet's\n`virtualtext` frontend supports a much more comprehensive workflow: it supports\nboth manual invocation and automatic triggering, keeps suggestions in sync as\nyou continue typing, and lets you accept longer suggestions incrementally,\nincluding accepting only part of a completion instead of committing the entire\nsuggestion at once.\n\nWhen using LSP inline completion, avoid enabling Minuet `virtualtext` at the\nsame time.\n\n\u003C\u002Fdetails>\n\n## LLM Provider Examples\n\n### Openrouter Kimi-K2\n\n\u003Cdetails>\n\n```lua\nrequire('minuet').setup {\n    provider = 'openai_compatible',\n    request_timeout = 2.5,\n    throttle = 1500, -- Increase to reduce costs and avoid rate limits\n    debounce = 600, -- Increase to reduce costs and avoid rate limits\n    provider_options = {\n        openai_compatible = {\n            api_key = 'OPENROUTER_API_KEY',\n            end_point = 'https:\u002F\u002Fopenrouter.ai\u002Fapi\u002Fv1\u002Fchat\u002Fcompletions',\n            model = 'moonshotai\u002Fkimi-k2',\n            name = 'Openrouter',\n            optional = {\n                max_tokens = 56,\n                top_p = 0.9,\n                provider = {\n                     -- Prioritize throughput for faster completion\n                    sort = 'throughput',\n                },\n            },\n        },\n    },\n}\n```\n\n\u003C\u002Fdetails>\n\n### Deepseek\n\n\u003Cdetails>\n\n```lua\n-- you can use deepseek with both openai_fim_compatible or openai_compatible provider\nrequire('minuet').setup {\n    provider = 'openai_fim_compatible',\n    provider_options = {\n        openai_fim_compatible = {\n            api_key = 'DEEPSEEK_API_KEY',\n            name = 'deepseek',\n            optional = {\n                max_tokens = 256,\n                top_p = 0.9,\n            },\n        },\n    },\n}\n\n\n-- or\nrequire('minuet').setup {\n    provider = 'openai_compatible',\n    provider_options = {\n        openai_compatible = {\n            end_point = 'https:\u002F\u002Fapi.deepseek.com\u002Fchat\u002Fcompletions',\n            api_key = 'DEEPSEEK_API_KEY',\n            name = 'deepseek',\n            optional = {\n                max_tokens = 256,\n                top_p = 0.9,\n            },\n        },\n    },\n}\n```\n\n\u003C\u002Fdetails>\n\n### Ollama Qwen-2.5-coder:7b\n\n\u003Cdetails>\n\n```lua\nrequire('minuet').setup {\n    provider = 'openai_fim_compatible',\n    n_completions = 1, -- recommend for local model for resource saving\n    -- I recommend beginning with a small context window size and incrementally\n    -- expanding it, depending on your local computing power. A context window\n    -- of 512, serves as an good starting point to estimate your computing\n    -- power. Once you have a reliable estimate of your local computing power,\n    -- you should adjust the context window to a larger value.\n    context_window = 512,\n    provider_options = {\n        openai_fim_compatible = {\n            -- For Windows users, TERM may not be present in environment variables.\n            -- Consider using APPDATA instead.\n            api_key = 'TERM',\n            name = 'Ollama',\n            end_point = 'http:\u002F\u002Flocalhost:11434\u002Fv1\u002Fcompletions',\n            model = 'qwen2.5-coder:7b',\n            optional = {\n                max_tokens = 56,\n                top_p = 0.9,\n            },\n        },\n    },\n}\n```\n\n\u003C\u002Fdetails>\n\n### Llama.cpp Qwen-2.5-coder:1.5b\n\n\u003Cdetails>\n\nFirst, launch the `llama-server` with your chosen model.\n\nHere's an example of a bash script to start the server if your system has less\nthan 8GB of VRAM:\n\n```bash\nllama-server \\\n    -hf ggml-org\u002FQwen2.5-Coder-1.5B-Q8_0-GGUF \\\n    --port 8012 -ngl 99 -fa -ub 1024 -b 1024 \\\n    --ctx-size 0 --cache-reuse 256\n```\n\n```lua\nrequire('minuet').setup {\n    provider = 'openai_fim_compatible',\n    n_completions = 1, -- recommend for local model for resource saving\n    -- I recommend beginning with a small context window size and incrementally\n    -- expanding it, depending on your local computing power. A context window\n    -- of 512, serves as an good starting point to estimate your computing\n    -- power. Once you have a reliable estimate of your local computing power,\n    -- you should adjust the context window to a larger value.\n    context_window = 512,\n    provider_options = {\n        openai_fim_compatible = {\n            -- For Windows users, TERM may not be present in environment variables.\n            -- Consider using APPDATA instead.\n            api_key = 'TERM',\n            name = 'Llama.cpp',\n            end_point = 'http:\u002F\u002Flocalhost:8012\u002Fv1\u002Fcompletions',\n            -- The model is set by the llama-cpp server and cannot be altered\n            -- post-launch.\n            model = 'PLACEHOLDER',\n            optional = {\n                max_tokens = 56,\n                top_p = 0.9,\n            },\n            -- Llama.cpp does not support the `suffix` option in FIM completion.\n            -- Therefore, we must disable it and manually populate the special\n            -- tokens required for FIM completion.\n            template = {\n                prompt = function(context_before_cursor, context_after_cursor, _)\n                    return '\u003C|fim_prefix|>'\n                        .. context_before_cursor\n                        .. '\u003C|fim_suffix|>'\n                        .. context_after_cursor\n                        .. '\u003C|fim_middle|>'\n                end,\n                suffix = false,\n            },\n        },\n    },\n}\n```\n\n**NOTE**: Special tokens such as `\u003C|fim_prefix|>` vary across different models.\nThe example code provided uses the tokens specific to `Qwen-2.5-coder`. If you\nintend to use a different model, ensure the `llama-cpp` template is updated to\nreflect the corresponding special tokens for your chosen model.\n\nFor additional example bash scripts to run llama.cpp based on your local\ncomputing power, please refer to [recipes.md](.\u002Frecipes.md).\n\n\u003C\u002Fdetails>\n\n# Selecting a Provider or Model\n\nThe `gemini-2.0-flash` and `codestral` models offer high-quality output with\nfree and fast processing. For optimal quality, though with significantly slower\ngeneration speed, consider using the `deepseek-chat` model, which is compatible\nwith both `openai-fim-compatible` and `openai-compatible` providers. For local\nLLM inference, you can deploy either `qwen-2.5-coder` or `deepseek-coder-v2`\nthrough Ollama using the `openai-fim-compatible` provider.\n\nAs of January 28, 2025: Due to high server demand, Deepseek users may\nexperience significant response delays or timeout. We recommend trying\nalternative providers instead.\n\nWe **do not** recommend using thinking models, as this mode significantly\nincreases latency—even with the fastest models. However, if you choose to use\nthinking models, please ensure that their thinking capabilities are disabled.\nRefer to the following examples for guidance on how to disable the thinking\nfeature.\n\n## Understanding Model Speed\n\nFor cloud-based providers,\n[Openrouter](https:\u002F\u002Fopenrouter.ai\u002Fgoogle\u002Fgemini-2.0-flash-001\u002Fproviders)\noffers a valuable resource for comparing the speed of both closed-source and\nopen-source models hosted by various cloud inference providers.\n\nWhen assessing model speed, two key metrics are latency (time to first token)\nand throughput (tokens per second). Latency is often a more critical factor\nthan throughput.\n\nIdeally, one would aim for a latency of less than 1 second and a throughput\nexceeding 100 tokens per second.\n\nFor local LLM,\n[llama.cpp#4167](https:\u002F\u002Fgithub.com\u002Fggml-org\u002Fllama.cpp\u002Fdiscussions\u002F4167)\nprovides valuable data on model speed for 7B models running on Apple M-series\nchips. The two crucial metrics are `Q4_0 PP [t\u002Fs]`, which measures latency\n(tokens per second to process the KV cache, equivalent to the time to generate\nthe first token), and `Q4_0 TG [t\u002Fs]`, which indicates the tokens per second\ngeneration speed.\n\n# Configuration\n\nMinuet AI comes with the following defaults:\n\n```lua\ndefault_config = {\n    -- Enable or disable auto-completion. Note that you still need to add\n    -- Minuet to your cmp\u002Fblink sources. This option controls whether cmp\u002Fblink\n    -- will attempt to invoke minuet when minuet is included in cmp\u002Fblink\n    -- sources. This setting has no effect on manual completion; Minuet will\n    -- always be enabled when invoked manually. You can use the command\n    -- `Minuet cmp\u002Fblink toggle` to toggle this option.\n    cmp = {\n        enable_auto_complete = true,\n    },\n    blink = {\n        enable_auto_complete = true,\n    },\n    -- LSP is recommended only for built-in completion. If you are using\n    -- `cmp` or `blink`, utilizing LSP for code completion from Minuet is *not*\n    -- recommended.\n    lsp = {\n        enabled_ft = {},\n        -- Filetypes excluded from LSP activation. Useful when `enabled_ft` = { '*' }\n        disabled_ft = {},\n        completion = {\n            enable = true,\n            -- if true, warn the user that they should use the native source\n            -- instead when the user is using blink or nvim-cmp.\n            warn_on_blink_or_cmp = true,\n            -- See README In-Process LSP section for more details on this option.\n            adjust_indentation = true,\n            -- Enables automatic completion triggering using `vim.lsp.completion.enable`\n            enabled_auto_trigger_ft = {},\n            -- Filetypes excluded from autotriggering. Useful when `enabled_auto_trigger_ft` = { '*' }\n            disabled_auto_trigger_ft = {},\n        },\n        inline_completion = {\n            enable = false,\n            -- if true, warn when LSP inline completion is enabled while\n            -- Minuet virtual text is also configured for use.\n            warn_on_virtualtext = true,\n            -- Enables automatic inline completion using `vim.lsp.inline_completion.enable`\n            -- for these filetypes.\n            enabled_auto_trigger_ft = {},\n            -- Filetypes excluded from inline completion autotriggering.\n            disabled_auto_trigger_ft = {},\n        },\n    },\n    virtualtext = {\n        -- Specify the filetypes to enable automatic virtual text completion,\n        -- e.g., { 'python', 'lua' }. Note that you can still invoke manual\n        -- completion even if the filetype is not on your auto_trigger_ft list.\n        auto_trigger_ft = {},\n        -- specify file types where automatic virtual text completion should be\n        -- disabled. This option is useful when auto-completion is enabled for\n        -- all file types i.e., when auto_trigger_ft = { '*' }\n        auto_trigger_ignore_ft = {},\n        keymap = {\n            accept = nil,\n            accept_line = nil,\n            accept_n_lines = nil,\n            -- Cycle to next completion item, or manually invoke completion\n            next = nil,\n            -- Cycle to prev completion item, or manually invoke completion\n            prev = nil,\n            dismiss = nil,\n        },\n        -- Whether show virtual text suggestion when the completion menu\n        -- (nvim-cmp or blink-cmp) is visible.\n        show_on_completion_menu = false,\n    },\n    provider = 'codestral',\n    -- the maximum total characters of the context before and after the cursor\n    -- 16000 characters typically equate to approximately 4,000 tokens for\n    -- LLMs.\n    context_window = 16000,\n    -- when the total characters exceed the context window, the ratio of\n    -- context before cursor and after cursor, the larger the ratio the more\n    -- context before cursor will be used. This option should be between 0 and\n    -- 1, context_ratio = 0.75 means the ratio will be 3:1.\n    context_ratio = 0.75,\n    throttle = 1000, -- only send the request every x milliseconds, use 0 to disable throttle.\n    -- debounce the request in x milliseconds, set to 0 to disable debounce\n    debounce = 400,\n    -- Control notification display for request status\n    -- Notification options:\n    -- false: Disable all notifications (use boolean false, not string \"false\")\n    -- \"debug\": Display all notifications (comprehensive debugging)\n    -- \"verbose\": Display most notifications\n    -- \"warn\": Display warnings and errors only\n    -- \"error\": Display errors only\n    notify = 'warn',\n    -- The request timeout, measured in seconds. When streaming is enabled\n    -- (stream = true), setting a shorter request_timeout allows for faster\n    -- retrieval of completion items, albeit potentially incomplete.\n    -- Conversely, with streaming disabled (stream = false), a timeout\n    -- occurring before the LLM returns results will yield no completion items.\n    request_timeout = 3,\n    -- Command used to make HTTP requests.\n    curl_cmd = 'curl',\n    -- Extra arguments passed to curl (list of strings).\n    curl_extra_args = {},\n    -- If completion item has multiple lines, create another completion item\n    -- only containing its first line. This option only has impact for cmp and\n    -- blink. For virtualtext, no single line entry will be added.\n    add_single_line_entry = true,\n    -- The number of completion items encoded as part of the prompt for the\n    -- chat LLM. For FIM model, this is the number of requests to send. It's\n    -- important to note that when 'add_single_line_entry' is set to true, the\n    -- actual number of returned items may exceed this value. Additionally, the\n    -- LLM cannot guarantee the exact number of completion items specified, as\n    -- this parameter serves only as a prompt guideline.\n    n_completions = 3,\n    --  Length of context after cursor used to filter completion text.\n    --\n    -- This setting helps prevent the language model from generating redundant\n    -- text.  When filtering completions, the system compares the suffix of a\n    -- completion candidate with the text immediately following the cursor.\n    --\n    -- If the length of the longest common substring between the end of the\n    -- candidate and the beginning of the post-cursor context exceeds this\n    -- value, that common portion is trimmed from the candidate.\n    --\n    -- For example, if the value is 15, and a completion candidate ends with a\n    -- 20-character string that exactly matches the 20 characters following the\n    -- cursor, the candidate will be truncated by those 20 characters before\n    -- being delivered.\n    after_cursor_filter_length = 15,\n    -- Similar to after_cursor_filter_length but trim the completion item from\n    -- prefix instead of suffix.\n    before_cursor_filter_length = 2,\n    -- proxy port to use\n    proxy = nil,\n    -- **List** of functions to execute. If any function returns `false`, Minuet\n    -- will not trigger auto-completion. Manual completion can still be invoked,\n    -- even if these functions evaluate to `false`, when using `nvim-cmp`,\n    -- `blink-cmp`, or virtual text (excluding LSP).\n    -- When this list is empty (the default), it always evaluates to `true`.\n    -- Note that this is called each time Minuet attempts to trigger\n    -- auto-completion, so ensure the functions in this list are highly efficient.\n    enable_predicates = {},\n    provider_options = {\n        -- see the documentation in each provider in the following part.\n    },\n    -- see the documentation in the `Prompt` section\n    default_system = {\n        template = '...',\n        prompt = '...',\n        guidelines = '...',\n        n_completion_template = '...',\n    },\n    default_system_prefix_first = {\n        template = '...',\n        prompt = '...',\n        guidelines = '...',\n        n_completion_template = '...',\n    },\n    default_fim_template = {\n        prompt = '...',\n        suffix = '...',\n    },\n    default_few_shots = { '...' },\n    default_chat_input = { '...' },\n    default_few_shots_prefix_first = { '...' },\n    default_chat_input_prefix_first = { '...' },\n    -- Config options for `Minuet change_preset` command\n    presets = {}\n}\n```\n\n# API Keys\n\nMinuet AI requires API keys to function. Set the following environment variables:\n\n- `OPENAI_API_KEY` for OpenAI\n- `GEMINI_API_KEY` for Gemini\n- `ANTHROPIC_API_KEY` for Claude\n- `CODESTRAL_API_KEY` for Codestral\n- Custom environment variable for OpenAI-compatible services (as specified in your configuration)\n\n**Note:** Provide the name of the environment variable to Minuet, not the\nactual value. For instance, pass `OPENAI_API_KEY` to Minuet, not the value\nitself (e.g., `sk-xxxx`).\n\nIf using Ollama, you need to assign an arbitrary, non-null environment variable\nas a placeholder for it to function.\n\nAlternatively, you can provide a function that returns the API key. This\nfunction should return the result instantly as it will be called for each\ncompletion request.\n\n```lua\nrequire('minuet').setup {\n    provider_options = {\n        openai_compatible = {\n            -- good\n            api_key = 'FIREWORKS_API_KEY', -- will read the environment variable FIREWORKS_API_KEY\n            -- good\n            api_key = function() return 'sk-xxxx' end,\n            -- bad\n            api_key = 'sk-xxxx',\n        }\n    }\n}\n```\n\n# Prompt\n\nSee [prompt](.\u002Fprompt.md) for the default prompt used by `minuet` and\ninstructions on customization.\n\nNote that `minuet` employs two distinct prompt systems:\n\n1. A system designed for chat-based LLMs (OpenAI, OpenAI-Compatible, Claude,\n   and Gemini)\n2. A separate system designed for Codestral and OpenAI-FIM-compatible models\n\n## Prefix-First vs. Suffix-First\n\nWhen use chat-based LLMs, there are two ways for constructing the prompt:\nplacing the prefix (context before the cursor) before the suffix (context after\nthe cursor), or placing the suffix before the prefix.\n\nBy default, `minuet` uses the **prefix-first** style for the OpenAI and Gemini\nproviders, and the **suffix-first** style for OpenAI-Compatible and Claude\nproviders. It is recommended that you experiment with both strategies to\ndetermine which yields the best results, particularly if you are using an\nOpenAI-compatible provider with various models.\n\nBelow is an example code snippet demonstrating how to switch between these two\nprompt construction methods:\n\n\u003Cdetails>\n\n```lua\nlocal mc = require 'minuet.config'\n\n-- Prefix-first style\nrequire('minuet').setup {\n    provider_options = {\n        openai_compatible = {\n            system = mc.default_system_prefix_first,\n            chat_input = mc.default_chat_input_prefix_first,\n            few_shots = mc.default_few_shots_prefix_first,\n        },\n    },\n}\n\n-- Suffix-first style\nrequire('minuet').setup {\n    provider_options = {\n        openai_compatible = {\n            system = mc.default_system,\n            few_shots = mc.default_few_shots,\n            chat_input = mc.default_chat_input,\n        },\n    },\n}\n```\n\n\u003C\u002Fdetails>\n\n# Providers\n\nYou need to set the field `provider` in the config, the default provider is\n`codestral`. For example:\n\n```lua\nrequire('minuet').setup {\n    provider = 'gemini'\n}\n```\n\n## OpenAI\n\n\u003Cdetails>\n\nthe following is the default configuration for OpenAI:\n\n```lua\nprovider_options = {\n    openai = {\n        model = 'gpt-5.4-nano',\n        end_point = 'https:\u002F\u002Fapi.openai.com\u002Fv1\u002Fchat\u002Fcompletions',\n        system = \"see [Prompt] section for the default value\",\n        few_shots = \"see [Prompt] section for the default value\",\n        chat_input = \"See [Prompt Section for default value]\",\n        stream = true,\n        api_key = 'OPENAI_API_KEY',\n        optional = {\n            -- pass any additional parameters you want to send to OpenAI request,\n            -- e.g.\n            -- stop = { 'end' },\n            -- max_completion_tokens = 256,\n            -- top_p = 0.9,\n            -- reasoning_effort = 'minimal'\n            -- reasoning_effort = 'none'\n        },\n        -- a list of functions to transform the endpoint, header, and request body\n        transform = {},\n    },\n}\n```\n\nThe following configuration is not the default, but recommended to prevent\nrequest timeout from outputing too many tokens.\n\n```lua\nprovider_options = {\n\topenai = {\n\t\toptional = {\n\t\t\tmax_completion_tokens = 128,\n\t\t\t-- for thinking models\n\t\t\treasoning_effort = 'none'\n\t\t\t-- reasoning_effort = \"minimal\",\n\t\t\t-- Set to \"minimal\" if your chosen model doesn't support \"none\"\n\t\t},\n\t},\n}\n```\n\nNote: If you intend to use GPT-5 series models (e.g., `gpt-5-mini` or\n`gpt-5.4-nano`), keep the following points in mind:\n\n1. Use `max_completion_tokens` instead of `max_tokens`.\n2. These models do not support `top_p` or `temperature` adjustments.\n3. Disable thinking by setting `reasoning_effort` to `none`, or use `minimal`\n   if your chosen model does not support `none`.\n\n\u003C\u002Fdetails>\n\n## Claude\n\n\u003Cdetails>\n\nthe following is the default configuration for Claude:\n\n```lua\nprovider_options = {\n    claude = {\n        max_tokens = 256,\n        model = 'claude-haiku-4.5',\n        system = \"see [Prompt] section for the default value\",\n        few_shots = \"see [Prompt] section for the default value\",\n        chat_input = \"See [Prompt Section for default value]\",\n        stream = true,\n        api_key = 'ANTHROPIC_API_KEY',\n        end_point = 'https:\u002F\u002Fapi.anthropic.com\u002Fv1\u002Fmessages',\n        optional = {\n            -- pass any additional parameters you want to send to claude request,\n            -- e.g.\n            -- stop_sequences = nil,\n        },\n        -- a list of functions to transform the endpoint, header, and request body\n        transform = {},\n    },\n}\n```\n\n\u003C\u002Fdetails>\n\n## Codestral\n\n\u003Cdetails>\n\nCodestral is a text completion model, not a chat model, so the system prompt\nand few shot examples does not apply. Note that you should use the\n`CODESTRAL_API_KEY`, not the `MISTRAL_API_KEY`, as they are using different\nendpoint. To use the Mistral endpoint, simply modify the `end_point` and\n`api_key` parameters in the configuration.\n\nthe following is the default configuration for Codestral:\n\n```lua\nprovider_options = {\n    codestral = {\n        model = 'codestral-latest',\n        end_point = 'https:\u002F\u002Fcodestral.mistral.ai\u002Fv1\u002Ffim\u002Fcompletions',\n        api_key = 'CODESTRAL_API_KEY',\n        stream = true,\n        template = {\n            prompt = \"See [Prompt Section for default value]\",\n            suffix = \"See [Prompt Section for default value]\",\n        },\n        optional = {\n            stop = nil, -- the identifier to stop the completion generation\n            max_tokens = nil,\n        },\n    },\n}\n```\n\nThe following configuration is not the default, but recommended to prevent\nrequest timeout from outputing too many tokens.\n\n```lua\nprovider_options = {\n    codestral = {\n        optional = {\n            max_tokens = 256,\n            stop = { '\\n\\n' },\n        },\n    },\n}\n```\n\n\u003C\u002Fdetails>\n\n## Mercury Coder\n\nDeveloped by Inception, Mercury Coder is described as a diffusion-based large\nlanguage model that accelerates code generation through iterative refinement\nrather than autoregressive token prediction. According to the claim, this\napproach is intended to deliver faster and more efficient code completions. To\nbegin, obtain an API key from the Inception Platform and configure it as the\n`INCEPTION_API_KEY` environment variable.\n\n\u003Cdetails>\n\nYou can access Mercury Coder via the OpenAI compatible FIM endpoint using the\nfollowing configuration:\n\n```lua\nprovider_options = {\n    openai_fim_compatible = {\n        model = \"mercury-coder\",\n        end_point = \"https:\u002F\u002Fapi.inceptionlabs.ai\u002Fv1\u002Ffim\u002Fcompletions\",\n        api_key = \"INCEPTION_API_KEY\", -- environment variable name\n        stream = true,\n    },\n}\n```\n\n\u003C\u002Fdetails>\n\n## Gemini\n\nYou should register the account and use the service from Google AI Studio\ninstead of Google Cloud. You can get an API key via their\n[Google API page](https:\u002F\u002Fmakersuite.google.com\u002Fapp\u002Fapikey).\n\n\u003Cdetails>\n\nThe following config is the default.\n\n```lua\nprovider_options = {\n    gemini = {\n        model = 'gemini-2.0-flash',\n        system = \"see [Prompt] section for the default value\",\n        few_shots = \"see [Prompt] section for the default value\",\n        chat_input = \"See [Prompt Section for default value]\",\n        stream = true,\n        api_key = 'GEMINI_API_KEY',\n        end_point = 'https:\u002F\u002Fgenerativelanguage.googleapis.com\u002Fv1beta\u002Fmodels',\n        optional = {},\n        -- a list of functions to transform the endpoint, header, and request body\n        transform = {},\n    },\n}\n```\n\nThe following configuration is not the default, but recommended to prevent\nrequest timeout from outputing too many tokens. You can also adjust the safety\nsettings following the example:\n\n```lua\nprovider_options = {\n    gemini = {\n        optional = {\n            generationConfig = {\n                maxOutputTokens = 256,\n                thinkingConfig = {\n                    -- Disable thinking for gemini 2.5 models\n                    thinkingBudget = 0,\n                    -- Disable thinking for gemini 3.x models\n                    thinkingLevel = 'minimal',\n                    -- Setting only one of the above options is sufficient.\n                },\n            },\n            safetySettings = {\n                {\n                    -- HARM_CATEGORY_HATE_SPEECH,\n                    -- HARM_CATEGORY_HARASSMENT\n                    -- HARM_CATEGORY_SEXUALLY_EXPLICIT\n                    category = 'HARM_CATEGORY_DANGEROUS_CONTENT',\n                    -- BLOCK_NONE\n                    threshold = 'BLOCK_ONLY_HIGH',\n                },\n            },\n        },\n    },\n}\n```\n\nWe recommend using `gemini-2.0-flash` over `gemini-2.5-flash`, as the 2.0\nversion offers significantly lower costs with comparable performance. The\nprimary improvement in version 2.5 lies in its extended thinking mode, which\nprovides minimal value for code completion scenarios. Furthermore, the thinking\nmode substantially increases latency, so we recommend disabling it entirely.\n\n\u003C\u002Fdetails>\n\n## OpenAI-compatible\n\nUse any providers compatible with OpenAI's chat completion API.\n\nFor example, you can set the `end_point` to\n`http:\u002F\u002Flocalhost:11434\u002Fv1\u002Fchat\u002Fcompletions` to use `ollama`.\n\n\u003Cdetails>\n\nNote that not all openAI compatible services has streaming support, you should\nchange `stream=false` to disable streaming in case your services do not support\nit.\n\nThe following config is the default.\n\n```lua\nprovider_options = {\n    openai_compatible = {\n        model = 'mistralai\u002Fdevstral-small',\n        system = \"see [Prompt] section for the default value\",\n        few_shots = \"see [Prompt] section for the default value\",\n        chat_input = \"See [Prompt Section for default value]\",\n        stream = true,\n        end_point = 'https:\u002F\u002Fopenrouter.ai\u002Fapi\u002Fv1\u002Fchat\u002Fcompletions',\n        api_key = 'OPENROUTER_API_KEY',\n        name = 'Openrouter',\n        optional = {\n            stop = nil,\n            max_tokens = nil,\n        },\n        -- a list of functions to transform the endpoint, header, and request body\n        transform = {},\n    }\n}\n```\n\n\u003C\u002Fdetails>\n\n## OpenAI-FIM-compatible\n\nUse any provider compatible with OpenAI's completion API. This request uses the\ntext `\u002Fcompletions` endpoint, **not** `\u002Fchat\u002Fcompletions` endpoint, so system\nprompts and few-shot examples are not applicable.\n\nFor example, you can set the `end_point` to\n`http:\u002F\u002Flocalhost:11434\u002Fv1\u002Fcompletions` to use `ollama`,\n`http:\u002F\u002Flocalhost:8012\u002Fv1\u002Fcompletions` to use `llama.cpp`.\n\nCmdline completion is available for models supported by these providers:\n`deepseek`, `ollama`, and `siliconflow`.\n\n\u003Cdetails>\n\nRefer to the [Completions\nLegacy](https:\u002F\u002Fplatform.openai.com\u002Fdocs\u002Fapi-reference\u002Fcompletions) section of\nthe OpenAI documentation for details.\n\nPlease note that not all OpenAI-compatible services support streaming. If your\nservice does not support streaming, you should set `stream=false` to disable\nit.\n\nAdditionally, for Ollama users, it is essential to verify whether the model's\ntemplate supports FIM completion. For example, qwen2.5-coder offers FIM\nsupport, as suggested in its\n[template](https:\u002F\u002Follama.com\u002Flibrary\u002Fqwen2.5-coder\u002Fblobs\u002Fe94a8ecb9327).\nHowever it may come as a surprise to some users that, `deepseek-coder` does not\nsupport the FIM template, and you should use `deepseek-coder-v2` instead.\n\nFor example bash scripts to run llama.cpp based on your local\ncomputing power, please refer to [recipes.md](.\u002Frecipes.md). Note\nthat the model for `llama.cpp` must be determined when you launch the\n`llama.cpp` server and cannot be changed thereafter.\n\n```lua\nprovider_options = {\n    openai_fim_compatible = {\n        model = 'deepseek-chat',\n        end_point = 'https:\u002F\u002Fapi.deepseek.com\u002Fbeta\u002Fcompletions',\n        api_key = 'DEEPSEEK_API_KEY',\n        name = 'Deepseek',\n        stream = true,\n        template = {\n            prompt = \"See [Prompt Section for default value]\",\n            suffix = \"See [Prompt Section for default value]\",\n        },\n        -- a list of functions to transform the endpoint, header, and request body\n        transform = {},\n        -- Custom function to extract LLM-generated text from JSON output\n        get_text_fn = {}\n        optional = {\n            stop = nil,\n            max_tokens = nil,\n        },\n    }\n}\n```\n\nThe following configuration is not the default, but recommended to prevent\nrequest timeout from outputing too many tokens.\n\n```lua\nprovider_options = {\n    openai_fim_compatible = {\n        optional = {\n            max_tokens = 256,\n            stop = { '\\n\\n' },\n        },\n    },\n}\n```\n\n\u003C\u002Fdetails>\n\n### Non-OpenAI-FIM-Compatible APIs\n\nFor providers like **DeepInfra FIM**\n(`https:\u002F\u002Fapi.deepinfra.com\u002Fv1\u002Finference\u002F`), refer to\n[recipes.md](.\u002Frecipes.md) for advanced configuration instructions.\n\n# Commands\n\n## `Minuet change_provider`, `Minuet change_model`\n\nThe `change_provider` command allows you to change the provider after `Minuet`\nhas been setup.\n\nExample usage: `Minuet change_provider claude`\n\nThe `change_model` command allows you to change both the provider and model in\none command. When called without arguments, it will open an interactive\nselection menu using `vim.ui.select` to choose from available models. When\ncalled with an argument, the format is `provider:model`.\n\nExample usage:\n\n- `Minuet change_model` - Opens interactive model selection\n- `Minuet change_model gemini:gemini-1.5-pro-latest` - Directly sets the model\n\nNote: For `openai_compatible` and `openai_fim_compatible` providers, the model\ncompletions in cmdline are determined by the `name` field in your\nconfiguration. For example, if you configured:\n\n```lua\nprovider_options.openai_compatible.name = 'Fireworks'\n```\n\nWhen entering `Minuet change_model openai_compatible:` in the cmdline,\nyou'll see model completions specific to the Fireworks provider.\n\n## `Minuet change_preset`\n\nThe `change_preset` command allows you to switch between config presets that\nwere defined during initial setup. Presets provide a convenient way to toggle\nbetween different config sets. This is particularly useful when you need to:\n\n- Switch between different cloud providers (such as Fireworks or Groq) for the\n  `openai_compatible` provider\n- Apply different throttle and debounce settings for different providers\n\nWhen called, the command merges the selected preset with the current config\ntable to create an updated configuration.\n\nUsage syntax: `Minuet change_preset preset_1`\n\nPresets can be configured during the initial setup process.\n\n\u003Cdetails>\n\n```lua\nrequire('minuet').setup {\n    presets = {\n        preset_1 = {\n            -- Configuration for cloud-based requests with large context window\n            context_window = 20000,\n            request_timeout = 4,\n            throttle = 3000,\n            debounce = 1000,\n            provider = 'openai_compatible',\n            provider_options = {\n                openai_compatible = {\n                    model = 'llama-3.3-70b-versatile',\n                    api_key = 'GROQ_API_KEY',\n                    name = 'Groq'\n                }\n            }\n        },\n        preset_2 = {\n            -- Configuration for local model with smaller context window\n            provider = 'openai_fim_compatible',\n            context_window = 2000,\n            throttle = 400,\n            debounce = 100,\n            provider_options = {\n                openai_fim_compatible = {\n                    api_key = 'TERM',\n                    name = 'Ollama',\n                    end_point = 'http:\u002F\u002Flocalhost:11434\u002Fv1\u002Fcompletions',\n                    model = 'qwen2.5-coder:7b',\n                    optional = {\n                        max_tokens = 256,\n                        top_p = 0.9\n                    }\n                }\n            }\n        }\n    }\n}\n```\n\n\u003C\u002Fdetails>\n\n## `Minuet blink`, `Minuet cmp`\n\nEnable or disable autocompletion for `nvim-cmp` or `blink.cmp`. While Minuet\nmust be added to your cmp\u002Fblink sources, this command only controls whether\nMinuet is triggered during autocompletion. The command does not affect manual\ncompletion behavior - Minuet remains active and available when manually\ninvoked.\n\nExample usage: `Minuet blink toggle`, `Minuet blink enable`, `Minuet blink disable`\n\n## `Minuet virtualtext`\n\nEnable or disable the automatic display of `virtual-text` completion in the\n**current buffer**.\n\nExample usage: `Minuet virtualtext toggle`, `Minuet virtualtext enable`,\n`Minuet virtualtext disable`.\n\n## `Minuet duet`\n\nThe Minuet duet command provides manual next-edit prediction controls:\n\n- `:Minuet duet predict`: Request an NES prediction for the current editable\n  region and show it as a preview.\n- `:Minuet duet apply`: Apply the current duet prediction.\n- `:Minuet duet dismiss`: Dismiss the current duet prediction preview.\n\n## `Minuet lsp`\n\nThe Minuet LSP command provides commands for managing the in-process LSP server:\n\n- `:Minuet lsp attach`: Attach the Minuet LSP server to the **current buffer**.\n- `:Minuet lsp detach`: Detach the Minuet LSP server from the **current buffer**.\n- `:Minuet lsp completion enable_auto_trigger`: Enable auto-triggered `vim.lsp.completion` for the **current buffer**.\n- `:Minuet lsp completion disable_auto_trigger`: Disable auto-triggered `vim.lsp.completion` for the **current buffer**.\n- `:Minuet lsp inline_completion enable_auto_trigger`: Enable `vim.lsp.inline_completion` auto-triggering for the **current buffer**.\n- `:Minuet lsp inline_completion disable_auto_trigger`: Disable `vim.lsp.inline_completion` auto-triggering for the **current buffer**.\n\n# Duet (Next Edit Prediction)\n\n`Minuet duet` is Minuet's highly experimental next-edit prediction (NES)\nfeature.\n\nBasic usage is manual. Bind the duet commands to your preferred keymaps, then:\n\n1. Trigger `:Minuet duet predict` to request a prediction for the current edit.\n2. Review the preview rendered in the buffer.\n3. Apply it with `:Minuet duet apply` or discard it with\n   `:Minuet duet dismiss`.\n\nExample keymaps:\n\n```lua\nvim.keymap.set('n', '\u003Cleader>mp', '\u003Ccmd>Minuet duet predict\u003Ccr>', { desc = 'Minuet duet predict' })\nvim.keymap.set('n', '\u003Cleader>ma', '\u003Ccmd>Minuet duet apply\u003Ccr>', { desc = 'Minuet duet apply' })\nvim.keymap.set('n', '\u003Cleader>md', '\u003Ccmd>Minuet duet dismiss\u003Ccr>', { desc = 'Minuet duet dismiss' })\nvim.keymap.set('i', '\u003CA-z>', '\u003Ccmd>Minuet duet predict\u003Ccr>', { desc = 'Minuet duet predict' })\nvim.keymap.set('i', '\u003CA-a>', '\u003Ccmd>Minuet duet apply\u003Ccr>', { desc = 'Minuet duet apply' })\nvim.keymap.set('i', '\u003CA-x>', '\u003Ccmd>Minuet duet dismiss\u003Ccr>', { desc = 'Minuet duet dismiss' })\n```\n\nThe recommended model at the moment is `gemini-3-flash-preview`.\n\n```lua\nrequire('minuet').setup {\n    duet = {\n        provider = 'gemini',\n        provider_options = {\n            gemini = {\n                model = 'gemini-3-flash-preview',\n                optional = {\n                    generationConfig = {\n                        thinkingConfig = {\n                            -- Disable thinking is recommended\n                            thinkingLevel = 'minimal',\n                        },\n                    },\n                },\n            },\n            openai_compatible = {\n                model = 'minimax\u002Fminimax-m2.7',\n                optional = {\n                    reasoning_effort = 'minimal',\n                    -- prioritize throughput for faster completion\n                    provider = {\n                        sort = 'throughput',\n                    },\n                },\n            },\n        },\n    },\n}\n```\n\nThis feature is highly experimental:\n\n- It only targets general-purpose LLMs rather than NES-specialized models, as I\n  lack local GPU resources for testing.\n- Comparable small models from competitors of Google—`claude-haiku-4.5` and\n  `gpt-5.4-mini`—perform poorly.\n- Given completion latency constraints, automatic duet prediction is not\n  implemented.\n\nIt is recommended to configure the thinking levels of the models; refer to the\n[provider sections](#providers) for guidance on managing thinking settings for\neach provider.\n\nAvoid setting a small `max_tokens` or `max_completion_tokens` limit for duet\nrequests. Duet expects the model to return the complete rewritten editable\nregion, including the cursor marker; if the response is truncated, the parser\nwill reject it. Leave the limit unset when the provider allows that, or set it\nlarge enough to cover the full rewritten region.\n\n## TODO\n\n- [ ] Implement a proper diff mechanism to include recent edit changes in prompts.\n- [ ] Add support for specialized NES models (Zeta, Sweep).\n- [ ] Integrate with Inception's hosted API.\n- [ ] Implement automatically triggered duet prediction.\n\n## Default Config\n\n```lua\nrequire('minuet').setup {\n    duet = {\n        provider = 'gemini', -- Provider used by `:Minuet duet predict`.\n        request_timeout = 15, -- Timeout in seconds for a single duet request.\n        editable_region = {\n            lines_before = 8, -- Number of editable lines included before the cursor.\n            lines_after = 15, -- Number of editable lines included after the cursor.\n            before_region_filter_length = 30, -- Trim duplicated text from the start of the model output when it repeats non-editable text before the region.\n            after_region_filter_length = 30, -- Trim duplicated text from the end of the model output when it repeats non-editable text after the region.\n        },\n        markers = {\n            editable_region_start = '\u003Ceditable_region>', -- Marker that wraps the start of the editable region in prompts and responses.\n            editable_region_end = '\u003C\u002Feditable_region>', -- Marker that wraps the end of the editable region in prompts and responses.\n            cursor_position = '\u003Ccursor_position\u002F>', -- Marker the model must preserve exactly once to indicate the final cursor position.\n        },\n        preview = {\n            cursor = '', -- Virtual marker shown at the predicted cursor location in the preview.\n        },\n        provider_options = {\n            openai = {\n                model = 'gpt-5.4-mini', -- Default OpenAI model for duet requests.\n                api_key = 'OPENAI_API_KEY', -- Environment variable name, or a function that returns the API key.\n                end_point = 'https:\u002F\u002Fapi.openai.com\u002Fv1\u002Fchat\u002Fcompletions', -- OpenAI chat completions endpoint.\n                system = { ... }, -- Duet system prompt config; keep the default unless you need a custom rewrite prompt.\n                few_shots = { ... }, -- Example user\u002Fassistant turns used to steer the rewrite.\n                chat_input = { ... }, -- Template that serializes editable and non-editable buffer regions.\n                optional = {}, -- Extra request body fields passed through to the OpenAI API.\n                transform = {}, -- Optional endpoint\u002Fheader\u002Fbody transforms applied before sending the request.\n            },\n            claude = {\n                model = 'claude-haiku-4-5', -- Default Claude model for duet requests.\n                api_key = 'ANTHROPIC_API_KEY', -- Environment variable name, or a function that returns the API key.\n                end_point = 'https:\u002F\u002Fapi.anthropic.com\u002Fv1\u002Fmessages', -- Claude messages endpoint.\n                system = { ... }, -- Same duet system prompt structure as other chat providers.\n                system = { ... }, -- Duet system prompt config; keep the default unless you need a custom rewrite prompt.\n                few_shots = { ... }, -- Example user\u002Fassistant turns used to steer the rewrite.\n                chat_input = { ... }, -- Template that serializes editable and non-editable buffer regions.\n                max_tokens = 8192, -- Keep this large; if Claude truncates the rewritten editable region, duet parsing will fail.\n                optional = {}, -- Extra request body fields passed through to the Claude API.\n                transform = {}, -- Optional endpoint\u002Fheader\u002Fbody transforms applied before sending the request.\n            },\n            gemini = {\n                model = 'gemini-3-flash-preview', -- Recommended duet model at the moment.\n                api_key = 'GEMINI_API_KEY', -- Environment variable name, or a function that returns the API key.\n                end_point = 'https:\u002F\u002Fgenerativelanguage.googleapis.com\u002Fv1beta\u002Fmodels', -- Base Gemini models endpoint.\n                system = { ... }, -- Duet system prompt config; keep the default unless you need a custom rewrite prompt.\n                few_shots = { ... }, -- Example user\u002Fassistant turns used to steer the rewrite.\n                chat_input = { ... }, -- Template that serializes editable and non-editable buffer regions.\n                optional = {}, -- Extra request body fields passed through to the OpenAI API.\n                transform = {}, -- Optional endpoint\u002Fheader\u002Fbody transforms applied before sending the request.\n            },\n            openai_compatible = {\n                model = 'minimax\u002Fminimax-m2.7', -- Default model for OpenAI-compatible chat providers.\n                api_key = 'OPENROUTER_API_KEY', -- Environment variable name, or a function that returns the API key.\n                end_point = 'https:\u002F\u002Fopenrouter.ai\u002Fapi\u002Fv1\u002Fchat\u002Fcompletions', -- Must be a chat-completions-compatible endpoint.\n                name = 'Openrouter', -- Provider label used in events and notifications.\n                system = { ... }, -- Duet system prompt config; keep the default unless you need a custom rewrite prompt.\n                few_shots = { ... }, -- Example user\u002Fassistant turns used to steer the rewrite.\n                chat_input = { ... }, -- Template that serializes editable and non-editable buffer regions.\n                optional = {}, -- Extra request body fields passed through to the OpenAI API.\n                transform = {}, -- Optional endpoint\u002Fheader\u002Fbody transforms applied before sending the request.\n            },\n        },\n    },\n}\n```\n\n# API\n\n## Virtual Text\n\n`minuet-ai.nvim` offers the following functions to customize your key mappings:\n\n```lua\n{\n    -- accept whole completion\n    require('minuet.virtualtext').action.accept,\n    -- accept by line\n    require('minuet.virtualtext').action.accept_line,\n    -- accept n lines (prompts for number)\n    require('minuet.virtualtext').action.accept_n_lines,\n    require('minuet.virtualtext').action.next,\n    require('minuet.virtualtext').action.prev,\n    require('minuet.virtualtext').action.dismiss,\n    -- whether the virtual text is visible in current buffer\n    require('minuet.virtualtext').action.is_visible,\n}\n```\n\n## Duet\n\nThe duet module provides functions to programmatically control duet prediction:\n\n```lua\n{\n    require('minuet.duet').action.predict,\n    require('minuet.duet').action.apply,\n    require('minuet.duet').action.dismiss,\n    -- Check if a duet preview is currently visible in the current buffer\n    require('minuet.duet').action.is_visible,\n}\n```\n\n## Lualine\n\nMinuet provides a Lualine component that displays the current status of Minuet requests. This component shows:\n\n- The name of the active provider and model\n- The current request count (e.g., \"1\u002F3\")\n- An animated spinner while processing\n\nTo use the Minuet Lualine component, add it to your Lualine configuration:\n\n\u003Cdetails>\n\n```lua\nrequire('lualine').setup {\n    sections = {\n        lualine_x = {\n            {\n                require 'minuet.lualine',\n                -- the follwing is the default configuration\n                -- the name displayed in the lualine. Set to \"provider\", \"model\" or \"both\"\n                -- display_name = 'both',\n                -- separator between provider and model name for option \"both\"\n                -- provider_model_separator = ':',\n                -- whether show display_name when no completion requests are active\n                -- display_on_idle = false,\n            },\n            'encoding',\n            'fileformat',\n            'filetype',\n        },\n    },\n}\n```\n\n\u003C\u002Fdetails>\n\n## Minuet Event\n\n### Standard Completion Events\n\n- **MinuetRequestStartedPre**: Triggered before a completion request is\n  initiated. This allows for pre-request operations, such as logging or updating\n  the user interface.\n- **MinuetRequestStarted**: Triggered immediately after the completion request\n  is dispatched, signaling that the request is in progress.\n- **MinuetRequestFinished**: Triggered upon completion of the request.\n\n### Duet Events\n\n- **MinuetDuetRequestStartedPre**: Triggered before a duet request is initiated.\n- **MinuetDuetRequestStarted**: Triggered immediately after the duet request\n  is dispatched.\n- **MinuetDuetRequestFinished**: Triggered upon completion of the duet request.\n\n### Event Data\n\nEach event includes a `data` field containing the following properties:\n\n- `provider`: A string indicating the provider type (e.g.,\n  'openai_compatible').\n- `name`: A string specifying the provider's name (e.g., 'OpenAI', 'Groq',\n  'Ollama').\n- `model`: A string containing the model name (e.g., 'gemini-2.0-flash').\n- `n_requests`: The number of requests encompassed in this completion cycle.\n- `request_idx` (optional): The index of the current request, applicable when\n  providers make multiple requests.\n- `timestamp`: A Unix timestamp representing the start of the request cycle\n  (corresponding to the `MinuetRequestStartedPre` event).\n\n# FAQ\n\n## Customize `cmp` ui for source icon and kind icon\n\nYou can configure the icons of completion items returned by `minuet` by using\nthe following snippet (referenced from [cmp's\nwiki](https:\u002F\u002Fgithub.com\u002Fhrsh7th\u002Fnvim-cmp\u002Fwiki\u002FMenu-Appearance#basic-customisations)):\n\n\u003Cdetails>\n\n```lua\nlocal kind_icons = {\n    Number = '󰎠',\n    Array = '',\n    Variable = '',\n    -- and other icons\n    -- LLM Provider icons\n    claude = '󰋦',\n    openai = '󱢆',\n    codestral = '󱎥',\n    gemini = '',\n    Groq = '',\n    Openrouter = '󱂇',\n    Ollama = '󰳆',\n    ['Llama.cpp'] = '󰳆',\n    Deepseek = ''\n    -- FALLBACK\n    fallback = '',\n}\n\nlocal source_icons = {\n    minuet = '󱗻',\n    nvim_lsp = '',\n    lsp = '',\n    buffer = '',\n    luasnip = '',\n    snippets = '',\n    path = '',\n    git = '',\n    tags = '',\n    -- FALLBACK\n    fallback = '󰜚',\n}\n\nlocal cmp = require 'cmp'\ncmp.setup {\n    formatting = {\n        format = function(entry, vim_item)\n            -- Kind icons\n            -- This concatenates the icons with the name of the item kind\n            vim_item.kind = string.format('%s %s', kind_icons[vim_item.kind] or kind_icons.fallback, vim_item.kind)\n            -- Source\n            vim_item.menu = source_icons[entry.source.name] or source_icons.fallback\n            return vim_item\n        end,\n    },\n}\n```\n\n\u003C\u002Fdetails>\n\n## Customize `blink` ui for source icon and kind icon\n\nYou can configure the icons of completion items returned by `minuet` by the following snippet:\n\n\u003Cdetails>\n\nTo customize the kind icons:\n\n```lua\nlocal kind_icons = {\n    -- LLM Provider icons\n    claude = '󰋦',\n    openai = '󱢆',\n    codestral = '󱎥',\n    gemini = '',\n    Groq = '',\n    Openrouter = '󱂇',\n    Ollama = '󰳆',\n    ['Llama.cpp'] = '󰳆',\n    Deepseek = ''\n}\n\nrequire('blink-cmp').setup {\n    appearance = {\n        use_nvim_cmp_as_default = true,\n        nerd_font_variant = 'normal',\n        kind_icons = kind_icons\n    },\n}\n\n```\n\nTo customize the source icons:\n\n```lua\nlocal source_icons = {\n    minuet = '󱗻',\n    orgmode = '',\n    otter = '󰼁',\n    nvim_lsp = '',\n    lsp = '',\n    buffer = '',\n    luasnip = '',\n    snippets = '',\n    path = '',\n    git = '',\n    tags = '',\n    cmdline = '󰘳',\n    latex_symbols = '',\n    cmp_nvim_r = '󰟔',\n    codeium = '󰩂',\n    -- FALLBACK\n    fallback = '󰜚',\n}\n\nrequire('blink-cmp').setup {\n    appearance = {\n        use_nvim_cmp_as_default = true,\n        nerd_font_variant = 'normal',\n        kind_icons = kind_icons\n    },\n    completion = {\n        menu = {\n            draw = {\n                columns = {\n                    { 'label', 'label_description', gap = 1 },\n                    { 'kind_icon', 'kind' },\n                    { 'source_icon' },\n                },\n                components = {\n                    source_icon = {\n                        -- don't truncate source_icon\n                        ellipsis = false,\n                        text = function(ctx)\n                            return source_icons[ctx.source_name:lower()] or source_icons.fallback\n                        end,\n                        highlight = 'BlinkCmpSource',\n                    },\n                },\n            },\n        },\n    }\n}\n```\n\n\u003C\u002Fdetails>\n\n## Significant Input Delay When Moving to a New Line with `nvim-cmp`\n\nWhen using Minuet with auto-complete enabled, you may occasionally experience a\nnoticeable delay when pressing `\u003CCR>` to move to the next line. This occurs\nbecause Minuet triggers autocompletion at the start of a new line, while cmp\nblocks the `\u003CCR>` key, awaiting Minuet's response.\n\nTo address this issue, consider the following solutions:\n\n1. Unbind the `\u003CCR>` key from your cmp keymap.\n2. Utilize cmp's internal API to avoid blocking calls, though be aware that\n   this API may change without prior notice.\n\nHere's an example of the second approach using Lua:\n\n```lua\nlocal cmp = require 'cmp'\nopts.mapping = {\n    ['\u003CCR>'] = cmp.mapping(function(fallback)\n        -- use the internal non-blocking call to check if cmp is visible\n        if cmp.core.view:visible() then\n            cmp.confirm { select = true }\n        else\n            fallback()\n        end\n    end),\n}\n```\n\n## Integration with `lazyvim`\n\n\u003Cdetails>\n\n**With nvim-cmp**:\n\n```lua\n{\n    'milanglacier\u002Fminuet-ai.nvim',\n    config = function()\n        require('minuet').setup {\n            -- Your configuration options here\n        }\n    end\n},\n{\n    'nvim-cmp',\n    optional = true,\n    opts = function(_, opts)\n        -- if you wish to use autocomplete\n        table.insert(opts.sources, 1, {\n            name = 'minuet',\n            group_index = 1,\n            priority = 100,\n        })\n\n        opts.performance = {\n            -- It is recommended to increase the timeout duration due to\n            -- the typically slower response speed of LLMs compared to\n            -- other completion sources. This is not needed when you only\n            -- need manual completion.\n            fetching_timeout = 2000,\n        }\n\n        opts.mapping = vim.tbl_deep_extend('force', opts.mapping or {}, {\n            -- if you wish to use manual complete\n            ['\u003CA-y>'] = require('minuet').make_cmp_map(),\n        })\n    end,\n}\n```\n\n**With blink-cmp**:\n\n```lua\n-- set the following line in your config\u002Foptions.lua\nvim.g.lazyvim_blink_main = true\n\n{\n    'milanglacier\u002Fminuet-ai.nvim',\n    config = function()\n        require('minuet').setup {\n            -- Your configuration options here\n        }\n    end,\n},\n{\n    'saghen\u002Fblink.cmp',\n    optional = true,\n    opts = {\n        keymap = {\n            ['\u003CA-y>'] = {\n                function(cmp)\n                    cmp.show { providers = { 'minuet' } }\n                end,\n            },\n        },\n        sources = {\n            -- if you want to use auto-complete\n            default =  { 'minuet' },\n            providers = {\n                minuet = {\n                    name = 'minuet',\n                    module = 'minuet.blink',\n                    score_offset = 100,\n                },\n            },\n        },\n    },\n}\n```\n\n\u003C\u002Fdetails>\n\n# Enhancement\n\n## RAG (Experimental)\n\nYou can enhance the content sent to the LLM for code completion by leveraging\nRAG support through the [VectorCode](https:\u002F\u002Fgithub.com\u002FDavidyz\u002FVectorCode)\npackage.\n\nVectorCode contains two main components. The first is a standalone CLI program\nwritten in Python, available for installation via PyPI. This program is\nresponsible for creating the vector database and processing RAG queries. The\nsecond component is a Neovim plugin that provides utility functions to send\nqueries and manage buffer-related RAG information within Neovim.\n\nWe offer two example recipes demonstrating VectorCode integration: one for\nchat-based LLMs (Gemini) and another for the FIM model (Qwen-2.5-Coder),\navailable in [recipes.md](.\u002Frecipes.md).\n\nFor detailed instructions on setting up and using VectorCode, please refer to the\n[official VectorCode\ndocumentation](https:\u002F\u002Fgithub.com\u002FDavidyz\u002FVectorCode\u002Ftree\u002Fmain\u002Fdocs\u002Fneovim).\n\n# Troubleshooting\n\nIf your setup failed, there are two most likely reasons:\n\n1. You may set the API key incorrectly. Checkout the [API Key](#api-keys)\n   section to see how to correctly specify the API key.\n2. You are using a model or a context window that is too large, causing\n   completion items to timeout before returning any tokens. This is\n   particularly common with local LLM. It is recommended to start with the\n   following settings to have a better understanding of your provider's inference\n   speed.\n   - Begin by testing with manual completions.\n   - Use a smaller context window (e.g., `config.context_window = 768`)\n   - Use a smaller model\n   - Set a longer request timeout (e.g., `config.request_timeout = 5`)\n\nTo diagnose issues, set `config.notify = debug` and examine the output.\n\n# Contributing\n\nContributions are welcome! Please feel free to submit a Pull Request.\n\n# Acknowledgement\n\n- [cmp-ai](https:\u002F\u002Fgithub.com\u002Ftzachar\u002Fcmp-ai): Reference for the integration with `nvim-cmp`.\n- [continue.dev](https:\u002F\u002Fwww.continue.dev): not a neovim plugin, but I find a lot LLM models from here.\n- [copilot.lua](https:\u002F\u002Fgithub.com\u002Fzbirenbaum\u002Fcopilot.lua): Reference for the virtual text frontend.\n- [llama.vim](https:\u002F\u002Fgithub.com\u002Fggml-org\u002Fllama.vim): Reference for CLI parameters used to launch the llama-cpp server.\n- [crates.nvim](https:\u002F\u002Fgithub.com\u002Fsaecki\u002Fcrates.nvim): Reference for in-process LSP implemtation to provide completion.\n","- [小步舞曲](#minuet)\n- [特性](#features)\n- [要求](#requirements)\n- [安装](#installation)\n- [快速入门](#quick-start)\n  - [虚拟文本设置](#virtual-text-setup)\n  - [Nvim-cmp 设置](#nvim-cmp-setup)\n  - [Blink-cmp 设置](#blink-cmp-setup)\n  - [内置补全与内联补全的进程内 LSP](#in-process-lsp-for-built-in-completion-and-inline-completion)\n    - [补全](#completion)\n    - [内联补全](#inline-completion)\n  - [LLM 提供商示例](#llm-provider-examples)\n    - [Openrouter Kimi-K2](#openrouter-kimi-k2)\n    - [Deepseek](#deepseek)\n    - [Ollama Qwen-2.5-coder:7b](#ollama-qwen-25-coder7b)\n    - [Llama.cpp Qwen-2.5-coder:1.5b](#llamacpp-qwen-25-coder15b)\n- [选择提供商或模型](#selecting-a-provider-or-model)\n  - [理解模型速度](#understanding-model-speed)\n- [配置](#configuration)\n- [API 密钥](#api-keys)\n- [提示](#prompt)\n  - [前缀优先 vs 后缀优先](#prefix-first-vs-suffix-first)\n- [提供商](#providers)\n  - [OpenAI](#openai)\n  - [Claude](#claude)\n  - [Codestral](#codestral)\n  - [Mercury Coder](#mercury-coder)\n  - [Gemini](#gemini)\n  - [兼容 OpenAI 的提供商](#openai-compatible)\n  - [兼容 OpenAI-FIM 的提供商](#openai-fim-compatible)\n    - [不兼容 OpenAI-FIM 的 API](#non-openai-fim-compatible-apis)\n- [命令](#commands)\n  - [`Minuet change_provider`, `Minuet change_model`](#minuet-change_provider-minuet-change_model)\n  - [`Minuet change_preset`](#minuet-change_preset)\n  - [`Minuet blink`, `Minuet cmp`](#minuet-blink-minuet-cmp)\n  - [`Minuet virtualtext`](#minuet-virtualtext)\n  - [`Minuet duet`](#minuet-duet)\n  - [`Minuet lsp`](#minuet-lsp)\n- [Duet（下一次编辑预测）](#duet-next-edit-prediction)\n  - [待办事项](#todo)\n  - [默认配置](#default-config)\n- [API](#api)\n  - [虚拟文本](#virtual-text)\n  - [Lualine](#lualine)\n  - [Minuet 事件](#minuet-event)\n- [常见问题解答](#faq)\n  - [自定义 `cmp` 界面以显示源图标和种类图标](#customize-cmp-ui-for-source-icon-and-kind-icon)\n  - [自定义 `blink` 界面以显示源图标和种类图标](#customize-blink-ui-for-source-icon-and-kind-icon)\n  - [使用 `nvim-cmp` 切换到新行时出现显著输入延迟](#significant-input-delay-when-moving-to-a-new-line-with-nvim-cmp)\n  - [与 `lazyvim` 集成](#integration-with-lazyvim)\n- [增强功能](#enhancement)\n  - [RAG（实验性）](#rag-experimental)\n- [故障排除](#troubleshooting)\n- [贡献](#contributing)\n- [致谢](#acknowledgement)\n\n# 小步舞曲\n\nMinuet：在代码中与智能共舞 💃。\n\n`Minuet` 将小步舞曲的优雅与和谐带入你的编码过程。\n正如舞者在小步舞曲中翩翩起舞一样。\n\n# 特性\n\n- 基于 AI 的代码补全，支持双模式：\n  - 针对基于聊天的 LLM 在代码补全任务上的专用提示及多种增强功能。\n  - 适用于兼容模型（DeepSeek、Codestral、Qwen 等）的中间填充（FIM）补全。\n- 支持多个 AI 提供商（OpenAI、Claude、Gemini、Codestral、Ollama、Llama-cpp 以及兼容 OpenAI 的服务）。\n- 可自定义的配置选项。\n- 流式传输支持，即使在较慢的 LLM 上也能实现补全。\n- 不需要后台运行专有二进制文件。只需使用 curl 和你偏好的 LLM 提供商即可。\n- 支持 `virtual-text`、`nvim-cmp`、`blink-cmp`、`built-in`、`mini.completion` 前端。\n- 可作为**进程内 LSP** 服务器提供补全功能（可选功能）。\n- 支持逐行接收多行建议，以便你可以按照自己的节奏逐步获取较长的建议。\n- 当你输入的文本与建议的开头匹配时，Minuet 会保持补全内容与你输入的文本同步，而不是丢弃它，从而减少不必要的 LLM 请求并节省资源。\n- 通过 `Minuet duet` 命令支持下一次编辑预测（NES）。此功能目前仍处于高度实验阶段。\n\n**使用 nvim-cmp \u002F blink-cmp 前端时**：\n\n![example-cmp](https:\u002F\u002Foss.gittoolsai.com\u002Fimages\u002Fmilanglacier_minuet-ai.nvim_readme_b90b830929e1.png)\n\n**使用内置补全前端时**（需 NVIM 0.11+）：\n\n![example-builtin-completion](https:\u002F\u002Foss.gittoolsai.com\u002Fimages\u002Fmilanglacier_minuet-ai.nvim_readme_9c2e01383476.jpg)\n\n**使用虚拟文本前端时**：\n\n![example-virtual-text](https:\u002F\u002Foss.gittoolsai.com\u002Fimages\u002Fmilanglacier_minuet-ai.nvim_readme_4a672921709a.png)\n\nhttps:\u002F\u002Fgithub.com\u002Fuser-attachments\u002Fassets\u002Fe0c4f2bd-0361-45b4-8eb4-0f49356bd7d9\n\n**使用 Duet（下一次编辑预测）时**：\n\nhttps:\u002F\u002Fgithub.com\u002Fuser-attachments\u002Fassets\u002Fb98699d5-b81d-4061-a1b3-d6f581c6b9b0\n\n\u003C!-- 上述链接是一个展示虚拟文本功能的视频，托管在 GitHub 外部。 -->\n\n# 要求\n\n- Neovim 0.10+。\n- 可选：[nvim-cmp](https:\u002F\u002Fgithub.com\u002Fhrsh7th\u002Fnvim-cmp)\n- 可选：[blink.cmp](https:\u002F\u002Fgithub.com\u002FSaghen\u002Fblink.cmp)\n- 至少一个受支持的 AI 提供商的 API 密钥\n- ~~[plenary.nvim](https:\u002F\u002Fgithub.com\u002Fnvim-lua\u002Fplenary.nvim)~~ Minuet 现在使用内置的 `vim.system`，不再需要 plenary。\n\n# 安装\n\n**Lazy.nvim**：\n\n```lua\nspecs = {\n    {\n        'milanglacier\u002Fminuet-ai.nvim',\n        config = function()\n            require('minuet').setup {\n                -- 您的配置选项在此处\n            }\n        end,\n    },\n    -- 可选，如果您使用虚拟文本前端，则不需要 nvim-cmp。\n    { 'hrsh7th\u002Fnvim-cmp' },\n    -- 可选，如果您使用虚拟文本前端，则不需要 blink。\n    { 'Saghen\u002Fblink.cmp' },\n}\n```\n\n**Rocks.nvim**：\n\n`Minuet` 已在 luarocks.org 上发布。只需运行 `Rocks install minuet-ai.nvim`，即可像安装其他 luarocks 包一样进行安装。\n\n# 快速入门\n\n## 虚拟文本设置\n\n```lua\nrequire('minuet').setup {\n    virtualtext = {\n        auto_trigger_ft = {},\n        keymap = {\n            -- 接受整个补全\n            accept = '\u003CA-A>',\n            -- 接受一行\n            accept_line = '\u003CA-a>',\n            -- 接受 n 行（会提示输入数字）\n            -- 例如，“A-z 2 CR”将接受 2 行\n            accept_n_lines = '\u003CA-z>',\n            -- 切换到上一个补全项，或手动触发补全\n            prev = '\u003CA-[>',\n            -- 切换到下一个补全项，或手动触发补全\n            next = '\u003CA-]>',\n            dismiss = '\u003CA-e>',\n        },\n    },\n}\n```\n\n## Nvim-cmp 设置\n\n\u003Cdetails>\n\n```lua\nrequire('cmp').setup {\n    sources = {\n        {\n             -- 将 minuet 作为源之一，以启用自动补全\n            { name = 'minuet' },\n            -- 以及其他来源\n        }\n    },\n    performance = {\n        -- 由于 LLM 的响应速度通常比其他补全源慢，建议增加超时时间。如果仅需手动补全，则无需此设置。\n        fetching_timeout = 2000,\n    },\n\n\n-- 如果您希望手动触发补全，\n-- 下列配置将 `A-y` 键绑定为手动触发补全的快捷键。\nrequire('cmp').setup {\n    mapping = {\n        [\"\u003CA-y>\"] = require('minuet').make_cmp_map()\n        -- 以及其他按键映射\n    },\n}\n```\n\n\u003C\u002Fdetails>\n\n## Blink-cmp 设置\n\n\u003Cdetails>\n\n```lua\nrequire('blink-cmp').setup {\n    keymap = {\n        -- 手动调用 minuet 补全。\n        ['\u003CA-y>'] = require('minuet').make_blink_map(),\n    },\n    sources = {\n         -- 启用 minuet 用于自动补全\n        default = { 'lsp', 'path', 'buffer', 'snippets', 'minuet' },\n        -- 仅用于手动补全时，从 default 中移除 'minuet'\n        providers = {\n            minuet = {\n                name = 'minuet',\n                module = 'minuet.blink',\n                async = true,\n                -- 应与 minuet.config.request_timeout * 1000 匹配，\n                -- 因为 minuet.config.request_timeout 的单位是秒\n                timeout_ms = 3000,\n                score_offset = 50, -- 使 minuet 在建议中具有更高优先级\n            },\n        },\n    },\n    -- 建议避免不必要的请求\n    completion = { trigger = { prefetch_on_insert = false } },\n}\n```\n\n\u003C\u002Fdetails>\n\n## 内置补全与内联补全的进程内 LSP\n\n\u003Cdetails>\n\n**要求：**\n\n- 需要 Neovim 0.11 或更高版本才能使用内置补全。\n- 需要 Neovim 0.12 或更高版本才能使用 `vim.lsp.inline_completion`。\n\n**注意：**\n\n`config.lsp.completion.enable` 和 `config.lsp.inline_completion.enable` 是设置时的选项。Minuet 会在运行 `require('minuet').setup()` 时决定暴露哪些 LSP 功能，因此之后更改这些选项并不会为已经运行的 Minuet LSP 服务器启用该功能。\n\n如果在同一个会话中可能稍后使用其中一项功能，请先在设置时启用它，然后在运行时仅控制其每缓冲区的自动触发行为。\n\n### 补全\n\n```lua\nrequire('minuet').setup {\n    lsp = {\n        enabled_ft = { 'toml', 'lua', 'cpp' },\n        completion = {\n            -- 使用 `vim.lsp.completion.enable` 启用自动补全触发\n            enabled_auto_trigger_ft = { 'cpp', 'lua' },\n        },\n    }\n}\n```\n\n`completion.enabled_auto_trigger_ft` 设置仅适用于内置补全（`vim.lsp.completion`）。使用 Mini.Completion 的用户可以忽略此选项，因为 Mini.Completion 会使用所有可用的 LSP 进行自动触发的补全。\n\n对于手动触发的补全，请确保将 `vim.bo.omnifunc` 设置为 `v:lua.vim.lsp.omnifunc`，并在插入模式下使用 `\u003CC-x>\u003CC-o>`。\n\n**建议：**\n\n对于使用 `blink-cmp` 和 `nvim-cmp` 的用户，推荐直接使用原生源而不是通过 LSP，主要有两个原因：\n\n1. 当 Minuet 作为独立源使用时，`blink-cmp` 和 `nvim-cmp` 能提供更好的排序和异步管理，而将其与常规 LSP（如 `clangd`）一起使用则无法达到同样的效果。\n2. 使用 `blink-cmp` 和 `nvim-cmp` 的原生源时，可以配置 Minuet 仅用于手动补全，从而禁用自动补全。然而，当 Minuet 以 LSP 服务器身份运行时，无法区分补全是自动触发还是手动触发。\n\nLSP 协议规范定义了三种 `triggerKind` 值：`Invoked`、`TriggerCharacter` 和 `TriggerForIncompleteCompletions`。但这些值均不能明确区分手动和自动补全请求。\n\n**注意**：\n\n- 上游问题（[在此跟踪](https:\u002F\u002Fgithub.com\u002Fneovim\u002Fneovim\u002Fissues\u002F32972)）可能导致在接受多行补全时出现意外的缩进行为。\n\n目前，Minuet 提供了配置选项 `config.lsp.completion.adjust_indentation`（默认启用）作为临时解决方案。然而，作者承认这一解决方案并不完善，并且启用后可能会引入额外的边缘情况。\n\n因此，在使用内置补全时，请考虑以下做法：\n- 确保 `config.add_single_line_entry = true`，只接受单行补全。\n- 避免在缩进影响语义的语言（如 Python）中使用 Minuet 和内置补全。\n\n- 用户可能会在客户端支持补全的情况下，在 `LspAttach` 事件中调用 `vim.lsp.completion.enable {autotrigger = true}`。但这并不是 Minuet 的理想行为。作为 LLM 补全源，Minuet 在自动触发时可能会面临严重的速率限制。\n\n因此，建议使用 `config.lsp.completion.enabled_auto_trigger_ft` 设置来启用 Minuet 的自动触发。\n\n对于使用 `LspAttach` 事件的用户，建议在启用自动触发前先确认服务器是否为 Minuet 服务器。示例如下：\n\n```lua\nvim.api.nvim_create_autocmd('LspAttach', {\n    callback = function(args)\n        local client_id = args.data.client_id\n        local bufnr = args.buf\n        local client = vim.lsp.get_client_by_id(client_id)\n        if not client then\n            return\n        end\n\n        if client.server_capabilities.completionProvider and client.name ~= 'minuet' then\n            vim.lsp.completion.enable(true, client_id, bufnr, { autotrigger = true })\n        end\n    end,\n    desc = '启用内置自动补全',\n})\n```\n\n### 内联补全\n\nMinuet 也可以通过 Neovim 内置的 `vim.lsp.inline_completion` 接口展示建议：\n\n```lua\nrequire('minuet').setup {\n    lsp = {\n        enabled_ft = { 'toml', 'lua', 'cpp' },\n        inline_completion = {\n            enable = true,\n            enabled_auto_trigger_ft = { 'cpp', 'lua' },\n        },\n    },\n}\n\nvim.keymap.set('i', '\u003CA-x>', function()\n    vim.lsp.inline_completion.get()\nend, { desc = '接受' })\nvim.keymap.set('i', '\u003CA-c>', function()\n    vim.lsp.inline_completion.select { count = 1 }\nend, { desc = '切换到下一个' })\nvim.keymap.set('i', '\u003CA-v>', function()\n    vim.lsp.inline_completion.select { count = -1 }\nend, { desc = '切换到上一个' })\n```\n\n如果您不希望在启动时使用内联补全，但仍想在后续为特定缓冲区启用它，请在设置时将 `config.lsp.inline_completion.enable = true`，并将 `config.lsp.inline_completion.enabled_auto_trigger_ft` 留空。之后可以在运行时为当前缓冲区启用：`:Minuet lsp inline_completion enable_auto_trigger`。\n\n**建议：**\n\n如果您需要内联建议，Minuet 自带的 `virtualtext` 前端仍然是**推荐**的选择。Neovim 内置的 `inline_completion` 支持虽然实用，但在实际应用中仅覆盖自动触发场景。而 Minuet 的 `virtualtext` 前端支持更全面的工作流程：既支持手动调用也支持自动触发，能够在您继续输入时保持建议同步更新，并允许您逐步接受较长的建议，甚至可以选择只接受部分补全内容而非一次性提交整个建议。\n\n使用 LSP 内联补全时，请避免同时启用 Minuet 的 `virtualtext`。\n\n\u003C\u002Fdetails>\n\n## LLM 提供者示例\n\n### Openrouter Kimi-K2\n\n\u003Cdetails>\n\n```lua\nrequire('minuet').setup {\n    provider = 'openai_compatible',\n    request_timeout = 2.5,\n    throttle = 1500, -- 增加此值以降低成本并避免速率限制\n    debounce = 600, -- 增加此值以降低成本并避免速率限制\n    provider_options = {\n        openai_compatible = {\n            api_key = 'OPENROUTER_API_KEY',\n            end_point = 'https:\u002F\u002Fopenrouter.ai\u002Fapi\u002Fv1\u002Fchat\u002Fcompletions',\n            model = 'moonshotai\u002Fkimi-k2',\n            name = 'Openrouter',\n            optional = {\n                max_tokens = 56,\n                top_p = 0.9,\n                provider = {\n                     -- 优先考虑吞吐量以加快完成速度\n                    sort = 'throughput',\n                },\n            },\n        },\n    },\n}\n```\n\n\u003C\u002Fdetails>\n\n### Deepseek\n\n\u003Cdetails>\n\n```lua\n-- 你可以使用 deepseek，无论是通过 openai_fim_compatible 还是 openai_compatible 提供者\nrequire('minuet').setup {\n    provider = 'openai_fim_compatible',\n    provider_options = {\n        openai_fim_compatible = {\n            api_key = 'DEEPSEEK_API_KEY',\n            name = 'deepseek',\n            optional = {\n                max_tokens = 256,\n                top_p = 0.9,\n            },\n        },\n    },\n}\n\n\n-- 或者\nrequire('minuet').setup {\n    provider = 'openai_compatible',\n    provider_options = {\n        openai_compatible = {\n            end_point = 'https:\u002F\u002Fapi.deepseek.com\u002Fchat\u002Fcompletions',\n            api_key = 'DEEPSEEK_API_KEY',\n            name = 'deepseek',\n            optional = {\n                max_tokens = 256,\n                top_p = 0.9,\n            },\n        },\n    },\n}\n```\n\n\u003C\u002Fdetails>\n\n### Ollama Qwen-2.5-coder:7b\n\n\u003Cdetails>\n\n```lua\nrequire('minuet').setup {\n    provider = 'openai_fim_compatible',\n    n_completions = 1, -- 建议在本地模型中使用以节省资源\n    -- 我建议从较小的上下文窗口大小开始，并根据你的本地计算能力逐步扩大。\n    -- 上下文窗口为512，可以作为一个良好的起点来估算你的计算能力。\n    -- 一旦你对本地计算能力有了可靠的估计，就应该将上下文窗口调整到更大的值。\n    context_window = 512,\n    provider_options = {\n        openai_fim_compatible = {\n            -- 对于 Windows 用户，环境变量中可能没有 TERM。可以考虑使用 APPDATA。\n            api_key = 'TERM',\n            name = 'Ollama',\n            end_point = 'http:\u002F\u002Flocalhost:11434\u002Fv1\u002Fcompletions',\n            model = 'qwen2.5-coder:7b',\n            optional = {\n                max_tokens = 56,\n                top_p = 0.9,\n            },\n        },\n    },\n}\n```\n\n\u003C\u002Fdetails>\n\n### Llama.cpp Qwen-2.5-coder:1.5b\n\n\u003Cdetails>\n\n首先，使用你选择的模型启动 `llama-server`。\n\n如果你的系统显存不足8GB，以下是一个启动服务器的bash脚本示例：\n\n```bash\nllama-server \\\n    -hf ggml-org\u002FQwen2.5-Coder-1.5B-Q8_0-GGUF \\\n    --port 8012 -ngl 99 -fa -ub 1024 -b 1024 \\\n    --ctx-size 0 --cache-reuse 256\n```\n\n```lua\nrequire('minuet').setup {\n    provider = 'openai_fim_compatible',\n    n_completions = 1, -- 建议在本地模型中使用以节省资源\n    -- 我建议从较小的上下文窗口大小开始，并根据你的本地计算能力逐步扩大。\n    -- 上下文窗口为512，可以作为一个良好的起点来估算你的计算能力。\n    -- 一旦你对本地计算能力有了可靠的估计，就应该将上下文窗口调整到更大的值。\n    context_window = 512,\n    provider_options = {\n        openai_fim_compatible = {\n            -- 对于 Windows 用户，环境变量中可能没有 TERM。可以考虑使用 APPDATA。\n            api_key = 'TERM',\n            name = 'Llama.cpp',\n            end_point = 'http:\u002F\u002Flocalhost:8012\u002Fv1\u002Fcompletions',\n            -- 模型由 llama-cpp 服务器设置，无法在启动后更改。\n            model = 'PLACEHOLDER',\n            optional = {\n                max_tokens = 56,\n                top_p = 0.9,\n            },\n            -- Llama.cpp 不支持 FIM 补全中的 `suffix` 选项。\n            -- 因此，我们必须禁用它，并手动填充 FIM 补全所需的特殊标记。\n            template = {\n                prompt = function(context_before_cursor, context_after_cursor, _)\n                    return '\u003C|num_start|>'\n                        .. context_before_cursor\n                        .. '\u003C|num_end|>'\n                        .. context_after_cursor\n                        .. '\n\n## 理解模型速度\n\n对于云服务提供商而言，\n[Openrouter](https:\u002F\u002Fopenrouter.ai\u002Fgoogle\u002Fgemini-2.0-flash-001\u002Fproviders)\n提供了一个宝贵的资源，用于比较由不同云推理提供商托管的闭源和开源模型的速度。\n\n在评估模型速度时，有两个关键指标：延迟（生成第一个 token 所需的时间）和吞吐量（每秒处理的 token 数）。通常情况下，延迟比吞吐量更为重要。\n\n理想情况下，应将延迟控制在 1 秒以下，同时确保吞吐量超过每秒 100 个 token。\n\n对于本地运行的大语言模型，\n[llama.cpp#4167](https:\u002F\u002Fgithub.com\u002Fggml-org\u002Fllama.cpp\u002Fdiscussions\u002F4167)\n提供了关于在 Apple M 系列芯片上运行的 7B 参数模型速度的宝贵数据。其中两个关键指标是 `Q4_0 PP [t\u002Fs]`，它衡量的是延迟（即处理 KV 缓存的每秒 token 数，相当于生成第一个 token 所需的时间），以及 `Q4_0 TG [t\u002Fs]`，它表示每秒的生成速度。\n\n# 配置\n\nMinuet AI 的默认配置如下：\n\n```lua\ndefault_config = {\n    -- 启用或禁用自动补全。请注意，您仍然需要将 Minuet 添加到 cmp\u002Fblink 的源中。此选项控制当 Minuet 包含在 cmp\u002Fblink 源中时，cmp\u002Fblink 是否会尝试调用 Minuet。该设置对手动补全没有影响；手动调用时，Minuet 始终启用。您可以使用命令 `Minuet cmp\u002Fblink toggle` 来切换此选项。\n    cmp = {\n        enable_auto_complete = true,\n    },\n    blink = {\n        enable_auto_complete = true,\n    },\n    -- 推荐仅在内置补全中使用 LSP。如果您正在使用 `cmp` 或 `blink`，则不建议使用 LSP 通过 Minuet 进行代码补全。\n    lsp = {\n        enabled_ft = {},\n        -- 排除在 LSP 激活之外的文件类型。当 `enabled_ft` = { '*' } 时很有用。\n        disabled_ft = {},\n        completion = {\n            enable = true,\n            -- 如果为真，当用户使用 blink 或 nvim-cmp 时，会警告用户应改用原生源。\n            warn_on_blink_or_cmp = true,\n            -- 有关此选项的更多详细信息，请参阅 README 中的“进程内 LSP”部分。\n            adjust_indentation = true,\n            -- 启用使用 `vim.lsp.completion.enable` 的自动补全触发功能。\n            enabled_auto_trigger_ft = {},\n            -- 排除在自动触发之外的文件类型。当 `enabled_auto_trigger_ft` = { '*' } 时很有用。\n            disabled_auto_trigger_ft = {},\n        },\n        inline_completion = {\n            enable = false,\n            -- 如果为真，当启用 LSP 内联补全且同时配置了 Minuet 虚拟文本使用时，会发出警告。\n            warn_on_virtualtext = true，\n            -- 对这些文件类型启用使用 `vim.lsp.inline_completion.enable` 的自动内联补全功能。\n            enabled_auto_trigger_ft = {},\n            -- 排除在内联补全自动触发之外的文件类型。\n            disabled_auto_trigger_ft = {},\n        },\n    },\n    virtualtext = {\n        -- 指定要启用自动虚拟文本补全的文件类型，例如 { 'python', 'lua' }。请注意，即使文件类型不在您的 auto_trigger_ft 列表中，您仍然可以手动调用补全。\n        auto_trigger_ft = {},\n        -- 指定应禁用自动虚拟文本补全的文件类型。此选项在启用了所有文件类型的自动补全时很有用，即当 auto_trigger_ft = { '*' } 时。\n        auto_trigger_ignore_ft = {},\n        keymap = {\n            accept = nil,\n            accept_line = nil,\n            accept_n_lines = nil,\n            -- 循环到下一个补全项，或手动调用补全。\n            next = nil,\n            -- 循环到上一个补全项，或手动调用补全。\n            prev = nil,\n            dismiss = nil,\n        },\n        -- 当补全菜单（nvim-cmp 或 blink-cmp）可见时，是否显示虚拟文本建议。\n        show_on_completion_menu = false,\n    },\n    provider = 'codestral',\n    -- 光标前后上下文的最大总字符数。16000 个字符通常相当于 LLM 的约 4000 个 token。\n    context_window = 16000,\n    -- 当总字符数超过上下文窗口时，光标前后的上下文比例。比例越大，光标前的上下文使用越多。此选项应在 0 到 1 之间，context_ratio = 0.75 表示比例为 3:1。\n    context_ratio = 0.75,\n    throttle = 1000, -- 每 x 毫秒才发送一次请求，使用 0 可禁用节流。\n    -- 在 x 毫秒内对请求进行防抖处理，设置为 0 可禁用防抖。\n    debounce = 400,\n    -- 控制请求状态的通知显示。\n    -- 通知选项：\n    -- false：禁用所有通知（使用布尔值 false，而不是字符串 \"false\"）\n    -- \"debug\"：显示所有通知（全面调试）\n    -- \"verbose\"：显示大多数通知\n    -- \"warn\"：仅显示警告和错误\n    -- \"error\"：仅显示错误\n    notify = 'warn',\n    -- 请求超时时间，以秒为单位。当启用流式传输（stream = true）时，设置较短的 request_timeout 可以更快地获取补全项，但结果可能不完整。\n    -- 相反，当流式传输禁用（stream = false）时，如果在 LLM 返回结果之前发生超时，则不会获得任何补全项。\n    request_timeout = 3,\n    -- 用于发起 HTTP 请求的命令。\n    curl_cmd = 'curl',\n    -- 传递给 curl 的额外参数（字符串列表）。\n    curl_extra_args = {},\n    -- 如果补全项有多行，只创建包含其第一行的另一个补全项。此选项仅对 cmp 和 blink 有效。对于虚拟文本，不会添加单行条目。\n    add_single_line_entry = true,\n    -- 编码到聊天 LLM 提示中的补全项数量。对于 FIM 模型，这是要发送的请求数量。需要注意的是，当 'add_single_line_entry' 设置为 true 时，实际返回的补全项数量可能会超过此值。此外，LLM 无法保证指定的确切补全项数量，因为此参数仅作为提示指南。\n    n_completions = 3,\n    -- 光标后上下文长度，用于过滤补全文本。\n    --\n    -- 此设置有助于防止语言模型生成冗余文本。在过滤补全时，系统会将补全候选的后缀与光标后紧跟的文本进行比较。\n    --\n    -- 如果候选文本末尾与光标后上下文开头之间的最长公共子串长度超过此值，那么这部分公共内容将从候选文本中截去。\n    --\n    -- 例如，如果该值为 15，而某个补全候选以一段 20 个字符的字符串结尾，恰好与光标后的 20 个字符完全匹配，则该候选将在交付前被截去这 20 个字符。\n    after_cursor_filter_length = 15,\n    -- 类似于 after_cursor_filter_length，但不是从后缀而是从前缀修剪补全项。\n    before_cursor_filter_length = 2,\n    -- 要使用的代理端口。\n    proxy = nil,\n    -- 要执行的函数列表。如果任何函数返回 `false`, Minuet 将不会触发自动补全。即使这些函数评估为 `false`，使用 `nvim-cmp`、`blink-cmp` 或虚拟文本（不包括 LSP）时，仍可手动调用补全。\n    -- 当此列表为空（默认）时，它始终评估为 `true`。\n    -- 请注意，每次 Minuet 尝试触发自动补全时都会调用此列表，因此请确保此列表中的函数效率极高。\n    enable_predicates = {},\n    provider_options = {\n        -- 请参阅下一部分中各提供商的文档。\n    },\n    -- 请参阅“提示”部分的文档。\n    default_system = {\n        template = '...',\n        prompt = '...',\n        guidelines = '...',\n        n_completion_template = '...',\n    },\n    default_system_prefix_first = {\n        template = '...',\n        prompt = '...',\n        guidelines = '...',\n        n_completion_template = '...',\n    },\n    default_fim_template = {\n        prompt = '...',\n        suffix = '...',\n    },\n    default_few_shots = { '...' },\n    default_chat_input = { '...' },\n    default_few_shots_prefix_first = { '...' },\n    default_chat_input_prefix_first = { '...' },\n    -- 用于 `Minuet change_preset` 命令的配置选项。\n    presets = {}\n}\n```\n\n# API 密钥\n\nMinuet AI 需要 API 密钥才能运行。请设置以下环境变量：\n\n- `OPENAI_API_KEY` 用于 OpenAI\n- `GEMINI_API_KEY` 用于 Gemini\n- `ANTHROPIC_API_KEY` 用于 Claude\n- `CODESTRAL_API_KEY` 用于 Codestral\n- 自定义环境变量，用于与 OpenAI 兼容的服务（如您的配置中所指定）\n\n**注意：** 请将环境变量的名称传递给 Minuet，而不是实际的值。例如，应传递 `OPENAI_API_KEY`，而不是其具体值（如 `sk-xxxx`）。\n\n如果使用 Ollama，您需要分配一个任意的非空环境变量作为占位符，以便其正常工作。\n\n此外，您也可以提供一个返回 API 密钥的函数。该函数应立即返回结果，因为它会在每次完成请求时被调用。\n\n```lua\nrequire('minuet').setup {\n    provider_options = {\n        openai_compatible = {\n            -- 好的做法\n            api_key = 'FIREWORKS_API_KEY', -- 将读取名为 FIREWORKS_API_KEY 的环境变量\n            -- 好的做法\n            api_key = function() return 'sk-xxxx' end,\n            -- 不好的做法\n            api_key = 'sk-xxxx',\n        }\n    }\n}\n```\n\n# 提示词\n\n请参阅 [提示词](.\u002Fprompt.md)，了解 `minuet` 使用的默认提示词及自定义说明。\n\n需要注意的是，`minuet` 采用了两种不同的提示词系统：\n\n1. 专为聊天型大模型设计的系统（OpenAI、与 OpenAI 兼容的模型、Claude 和 Gemini）\n2. 另一种专为 Codestral 和与 OpenAI-FIM 兼容的模型设计的系统\n\n## 前缀优先 vs. 后缀优先\n\n在使用聊天型大模型时，构建提示词有两种方式：将前缀（光标前的上下文）置于后缀（光标后的上下文）之前，或者将后缀置于前缀之前。\n\n默认情况下，`minuet` 对 OpenAI 和 Gemini 提供者采用 **前缀优先** 风格，而对与 OpenAI 兼容的提供者和 Claude 提供者则采用 **后缀优先** 风格。建议您尝试这两种策略，以确定哪种效果更好，尤其是在使用与 OpenAI 兼容的提供者及其不同模型时。\n\n以下是一个示例代码片段，展示了如何在这两种提示词构建方法之间切换：\n\n\u003Cdetails>\n\n```lua\nlocal mc = require 'minuet.config'\n\n-- 前缀优先风格\nrequire('minuet').setup {\n    provider_options = {\n        openai_compatible = {\n            system = mc.default_system_prefix_first,\n            chat_input = mc.default_chat_input_prefix_first,\n            few_shots = mc.default_few_shots_prefix_first,\n        },\n    },\n}\n\n-- 后缀优先风格\nrequire('minuet').setup {\n    provider_options = {\n        openai_compatible = {\n            system = mc.default_system,\n            few_shots = mc.default_few_shots,\n            chat_input = mc.default_chat_input,\n        },\n    },\n}\n```\n\n\u003C\u002Fdetails>\n\n# 提供者\n\n您需要在配置中设置 `provider` 字段，默认提供者是 `codestral`。例如：\n\n```lua\nrequire('minuet').setup {\n    provider = 'gemini'\n}\n```\n\n## OpenAI\n\n\u003Cdetails>\n\n以下是 OpenAI 的默认配置：\n\n```lua\nprovider_options = {\n    openai = {\n        model = 'gpt-5.4-nano',\n        end_point = 'https:\u002F\u002Fapi.openai.com\u002Fv1\u002Fchat\u002Fcompletions',\n        system = \"默认值请参见[提示词]部分\",\n        few_shots = \"默认值请参见[提示词]部分\",\n        chat_input = \"默认值请参见[提示词部分]\",\n        stream = true,\n        api_key = 'OPENAI_API_KEY',\n        optional = {\n            -- 您可以传递任何想要发送到 OpenAI 请求的额外参数，\n            -- 例如：\n            -- stop = { 'end' },\n            -- max_completion_tokens = 256,\n            -- top_p = 0.9,\n            -- reasoning_effort = 'minimal'\n            -- reasoning_effort = 'none'\n        },\n        -- 用于转换端点、头部和请求体的一系列函数\n        transform = {},\n    },\n}\n```\n\n以下配置并非默认，但推荐使用，以防止因输出过多 token 而导致请求超时。\n\n```lua\nprovider_options = {\n\topenai = {\n\t\toptional = {\n\t\t\tmax_completion_tokens = 128,\n\t\t\t-- 对于思考型模型\n\t\t\treasoning_effort = 'none'\n\t\t\t-- reasoning_effort = \"minimal\",\n\t\t\t-- 如果您选择的模型不支持 'none'，则设置为 'minimal'\n\t\t},\n\t},\n}\n```\n\n注意：如果您打算使用 GPT-5 系列模型（例如 `gpt-5-mini` 或 `gpt-5.4-nano`），请记住以下几点：\n\n1. 使用 `max_completion_tokens` 而不是 `max_tokens`。\n2. 这些模型不支持调整 `top_p` 或 `temperature`。\n3. 通过将 `reasoning_effort` 设置为 `none` 来禁用思考模式；如果所选模型不支持 `none`，则使用 `minimal`。\n\n\u003C\u002Fdetails>\n\n## Claude\n\n\u003Cdetails>\n\n以下是 Claude 的默认配置：\n\n```lua\nprovider_options = {\n    claude = {\n        max_tokens = 256,\n        model = 'claude-haiku-4.5',\n        system = \"默认值请参见[提示词]部分\",\n        few_shots = \"默认值请参见[提示词]部分\",\n        chat_input = \"默认值请参见[提示词部分]\",\n        stream = true,\n        api_key = 'ANTHROPIC_API_KEY',\n        end_point = 'https:\u002F\u002Fapi.anthropic.com\u002Fv1\u002Fmessages',\n        optional = {\n            -- 您可以传递任何想要发送到 Claude 请求的额外参数，\n            -- 例如：\n            -- stop_sequences = nil,\n        },\n        -- 用于转换端点、头部和请求体的一系列函数\n        transform = {},\n    },\n}\n```\n\n\u003C\u002Fdetails>\n\n## Codestral\n\n\u003Cdetails>\n\nCodestral 是一个文本补全模型，而非聊天模型，因此系统提示词和少量示例并不适用。请注意，您应使用 `CODESTRAL_API_KEY`，而不是 `MISTRAL_API_KEY`，因为它们使用不同的端点。若要使用 Mistral 的端点，只需在配置中修改 `end_point` 和 `api_key` 参数即可。\n\n以下是 Codestral 的默认配置：\n\n```lua\nprovider_options = {\n    codestral = {\n        model = 'codestral-latest',\n        end_point = 'https:\u002F\u002Fcodestral.mistral.ai\u002Fv1\u002Ffim\u002Fcompletions',\n        api_key = 'CODESTRAL_API_KEY',\n        stream = true,\n        template = {\n            prompt = \"默认值请参见[提示词部分]\",\n            suffix = \"默认值请参见[提示词部分]\",\n        },\n        optional = {\n            stop = nil, -- 用于停止补全生成的标识符\n            max_tokens = nil,\n        },\n    },\n}\n```\n\n以下配置并非默认，但推荐使用，以防止因输出过多 token 而导致请求超时。\n\n```lua\nprovider_options = {\n    codestral = {\n        optional = {\n            max_tokens = 256,\n            stop = { '\\n\\n' },\n        },\n    },\n}\n```\n\n\u003C\u002Fdetails>\n\n## 水星编码器\n\n由 Inception 开发的水星编码器被描述为一种基于扩散的大语言模型，它通过迭代优化而非自回归式标记预测来加速代码生成。据称，这种方法旨在提供更快、更高效的代码补全功能。首先，您需要从 Inception 平台获取 API 密钥，并将其配置为 `INCEPTION_API_KEY` 环境变量。\n\n\u003Cdetails>\n\n您可以通过 OpenAI 兼容的 FIM 端点访问水星编码器，使用如下配置：\n\n```lua\nprovider_options = {\n    openai_fim_compatible = {\n        model = \"mercury-coder\",\n        end_point = \"https:\u002F\u002Fapi.inceptionlabs.ai\u002Fv1\u002Ffim\u002Fcompletions\",\n        api_key = \"INCEPTION_API_KEY\", -- 环境变量名称\n        stream = true,\n    },\n}\n```\n\n\u003C\u002Fdetails>\n\n## 雅典娜\n\n您应该注册账号并使用 Google AI Studio 的服务，而不是 Google Cloud。您可以通过他们的 [Google API 页面](https:\u002F\u002Fmakersuite.google.com\u002Fapp\u002Fapikey) 获取 API 密钥。\n\n\u003Cdetails>\n\n以下配置为默认设置。\n\n```lua\nprovider_options = {\n    gemini = {\n        model = 'gemini-2.0-flash',\n        system = \"请参阅[提示]部分以了解默认值\",\n        few_shots = \"请参阅[提示]部分以了解默认值\",\n        chat_input = \"请参阅[提示部分]以了解默认值\",\n        stream = true,\n        api_key = 'GEMINI_API_KEY',\n        end_point = 'https:\u002F\u002Fgenerativelanguage.googleapis.com\u002Fv1beta\u002Fmodels',\n        optional = {},\n        -- 用于转换端点、头部和请求体的一系列函数\n        transform = {},\n    },\n}\n```\n\n以下配置并非默认设置，但建议采用，以防止因输出过多标记而导致请求超时。您还可以按照示例调整安全设置：\n\n```lua\nprovider_options = {\n    gemini = {\n        optional = {\n            generationConfig = {\n                maxOutputTokens = 256,\n                thinkingConfig = {\n                    -- 对于 Gemini 2.5 型号，禁用思考模式\n                    thinkingBudget = 0，\n                    -- 对于 Gemini 3.x 型号，将思考级别设为“最低”\n                    thinkingLevel = 'minimal'，\n                    -- 只需设置上述选项之一即可。\n                },\n            },\n            safetySettings = {\n                {\n                    -- HARM_CATEGORY_HATE_SPEECH，\n                    -- HARM_CATEGORY_HARASSMENT，\n                    -- HARM_CATEGORY_SEXUALLY_EXPLICIT\n                    category = 'HARM_CATEGORY_DANGEROUS_CONTENT'，\n                    -- BLOCK_NONE，\n                    threshold = 'BLOCK_ONLY_HIGH'，\n                },\n            },\n        },\n    },\n}\n```\n\n我们建议使用 `gemini-2.0-flash` 而不是 `gemini-2.5-flash`，因为 2.0 版本在性能相当的情况下成本显著更低。2.5 版的主要改进在于其扩展的思考模式，但对于代码补全场景而言，这种模式的价值微乎其微。此外，思考模式会大幅增加延迟，因此我们建议完全禁用它。\n\n\u003C\u002Fdetails>\n\n## OpenAI 兼容\n\n使用任何与 OpenAI 的聊天补全 API 兼容的提供商。\n\n例如，您可以将 `end_point` 设置为 `http:\u002F\u002Flocalhost:11434\u002Fv1\u002Fchat\u002Fcompletions` 来使用 `ollama`。\n\n\u003Cdetails>\n\n请注意，不是所有 OpenAI 兼容的服务都支持流式传输，如果您的服务不支持流式传输，您应将 `stream=false` 以禁用流式传输。\n\n以下配置为默认设置。\n\n```lua\nprovider_options = {\n    openai_compatible = {\n        model = 'mistralai\u002Fdevstral-small',\n        system = \"请参阅[提示]部分以了解默认值\",\n        few_shots = \"请参阅[提示]部分以了解默认值\",\n        chat_input = \"请参阅[提示部分]以了解默认值\",\n        stream = true，\n        end_point = 'https:\u002F\u002Fopenrouter.ai\u002Fapi\u002Fv1\u002Fchat\u002Fcompletions'，\n        api_key = 'OPENROUTER_API_KEY'，\n        name = 'Openrouter'，\n        optional = {\n            stop = nil，\n            max_tokens = nil，\n        }，\n        -- 用于转换端点、头部和请求体的一系列函数\n        transform = {},\n    }\n}\n```\n\n\u003C\u002Fdetails>\n\n## OpenAI-FIM 兼容\n\n使用任何与 OpenAI 补全 API 兼容的提供商。此请求使用 `\u002Fcompletions` 文本端点，**而非** `\u002Fchat\u002Fcompletions` 端点，因此系统提示和少量示例不适用。\n\n例如，您可以将 `end_point` 设置为 `http:\u002F\u002Flocalhost:11434\u002Fv1\u002Fcompletions` 来使用 `ollama`，或设置为 `http:\u002F\u002Flocalhost:8012\u002Fv1\u002Fcompletions` 来使用 `llama.cpp`。\n\n命令行补全功能适用于这些提供商支持的模型：`deepseek`、`ollama` 和 `siliconflow`。\n\n\u003Cdetails>\n\n有关详细信息，请参阅 OpenAI 文档中的 [Completions Legacy](https:\u002F\u002Fplatform.openai.com\u002Fdocs\u002Fapi-reference\u002Fcompletions) 部分。\n\n请注意，不是所有 OpenAI 兼容的服务都支持流式传输。如果您的服务不支持流式传输，您应将 `stream=false` 以禁用流式传输。\n\n此外，对于 Ollama 用户来说，务必确认模型模板是否支持 FIM 补全。例如，qwen2.5-coder 支持 FIM，正如其 [模板](https:\u002F\u002Follama.com\u002Flibrary\u002Fqwen2.5-coder\u002Fblobs\u002Fe94a8ecb9327) 所示。然而，一些用户可能会惊讶地发现，`deepseek-coder` 不支持 FIM 模板，而应改用 `deepseek-coder-v2`。\n\n例如，根据您的本地计算能力运行 llama.cpp 的 Bash 脚本，请参阅 [recipes.md](.\u002Frecipes.md)。请注意，`llama.cpp` 的模型必须在启动 `llama.cpp` 服务器时确定，此后无法更改。\n\n```lua\nprovider_options = {\n    openai_fim_compatible = {\n        model = 'deepseek-chat',\n        end_point = 'https:\u002F\u002Fapi.deepseek.com\u002Fbeta\u002Fcompletions'，\n        api_key = 'DEEPSEEK_API_KEY'，\n        name = 'Deepseek'，\n        stream = true，\n        template = {\n            prompt = \"请参阅[提示部分]以了解默认值\"，\n            suffix = \"请参阅[提示部分]以了解默认值\"，\n        }，\n        -- 用于转换端点、头部和请求体的一系列函数\n        transform = {},\n        -- 自定义函数，用于从 JSON 输出中提取 LLM 生成的文本\n        get_text_fn = {}\n        optional = {\n            stop = nil，\n            max_tokens = nil，\n        }，\n    }\n}\n```\n\n以下配置并非默认设置，但建议采用，以防止因输出过多标记而导致请求超时。\n\n```lua\nprovider_options = {\n    openai_fim_compatible = {\n        optional = {\n            max_tokens = 256，\n            stop = { '\\n\\n' }，\n        }，\n    }\n}\n```\n\n\u003C\u002Fdetails>\n\n### 非 OpenAI-FIM 兼容的 API\n\n对于像 **DeepInfra FIM**（`https:\u002F\u002Fapi.deepinfra.com\u002Fv1\u002Finference\u002F`）这样的提供商，请参阅 [recipes.md](.\u002Frecipes.md) 以获取高级配置说明。\n\n# 命令\n\n## `Minuet change_provider`、`Minuet change_model`\n\n`change_provider` 命令允许您在 `Minuet` 设置完成后更改提供者。\n\n使用示例：`Minuet change_provider claude`\n\n`change_model` 命令允许您在一个命令中同时更改提供者和模型。当不带参数调用时，它会使用 `vim.ui.select` 打开一个交互式选择菜单，供您从可用模型中选择。当带参数调用时，格式为 `provider:model`。\n\n使用示例：\n\n- `Minuet change_model` - 打开交互式模型选择\n- `Minuet change_model gemini:gemini-1.5-pro-latest` - 直接设置模型\n\n注意：对于 `openai_compatible` 和 `openai_fim_compatible` 提供者，命令行中的模型补全由您的配置中的 `name` 字段决定。例如，如果您配置了：\n\n```lua\nprovider_options.openai_compatible.name = 'Fireworks'\n```\n\n那么在命令行中输入 `Minuet change_model openai_compatible:` 时，您将看到特定于 Fireworks 提供者的模型补全选项。\n\n## `Minuet change_preset`\n\n`change_preset` 命令允许您在初始设置期间定义的配置预设之间切换。预设提供了一种便捷的方式来在不同的配置集之间切换。这在您需要以下情况时特别有用：\n\n- 在不同的云提供商（如 Fireworks 或 Groq）之间切换 `openai_compatible` 提供者\n- 为不同提供者应用不同的限流和防抖设置\n\n调用该命令时，它会将所选预设与当前配置表合并，以创建更新后的配置。\n\n使用语法：`Minuet change_preset preset_1`\n\n预设可以在初始设置过程中进行配置。\n\n\u003Cdetails>\n\n```lua\nrequire('minuet').setup {\n    presets = {\n        preset_1 = {\n            -- 面向具有大上下文窗口的云端请求的配置\n            context_window = 20000,\n            request_timeout = 4,\n            throttle = 3000,\n            debounce = 1000,\n            provider = 'openai_compatible',\n            provider_options = {\n                openai_compatible = {\n                    model = 'llama-3.3-70b-versatile',\n                    api_key = 'GROQ_API_KEY',\n                    name = 'Groq'\n                }\n            }\n        },\n        preset_2 = {\n            -- 面向具有较小上下文窗口的本地模型的配置\n            provider = 'openai_fim_compatible',\n            context_window = 2000,\n            throttle = 400,\n            debounce = 100,\n            provider_options = {\n                openai_fim_compatible = {\n                    api_key = 'TERM',\n                    name = 'Ollama',\n                    end_point = 'http:\u002F\u002Flocalhost:11434\u002Fv1\u002Fcompletions',\n                    model = 'qwen2.5-coder:7b',\n                    optional = {\n                        max_tokens = 256,\n                        top_p = 0.9\n                    }\n                }\n            }\n        }\n    }\n}\n```\n\n\u003C\u002Fdetails>\n\n## `Minuet blink`、`Minuet cmp`\n\n启用或禁用 `nvim-cmp` 或 `blink.cmp` 的自动补全功能。虽然 Minuet 必须添加到您的 cmp\u002Fblink 源中，但此命令仅控制 Minuet 是否会在自动补全过程中被触发。该命令不会影响手动补全行为——Minuet 在手动调用时仍然处于活动状态并可用。\n\n使用示例：`Minuet blink toggle`、`Minuet blink enable`、`Minuet blink disable`\n\n## `Minuet virtualtext`\n\n启用或禁用在**当前缓冲区**中自动显示 `virtual-text` 补全。\n\n使用示例：`Minuet virtualtext toggle`、`Minuet virtualtext enable`、`Minuet virtualtext disable`。\n\n## `Minuet duet`\n\nMinuet duet 命令提供了手动下一次编辑预测控制：\n\n- `:Minuet duet predict`：为当前可编辑区域请求 NES 预测，并将其作为预览显示。\n- `:Minuet duet apply`：应用当前的 duet 预测。\n- `:Minuet duet dismiss`：取消当前的 duet 预测预览。\n\n## `Minuet lsp`\n\nMinuet LSP 命令提供了用于管理进程内 LSP 服务器的命令：\n\n- `:Minuet lsp attach`：将 Minuet LSP 服务器附加到**当前缓冲区**。\n- `:Minuet lsp detach`：从**当前缓冲区**分离 Minuet LSP 服务器。\n- `:Minuet lsp completion enable_auto_trigger`：为**当前缓冲区**启用自动触发的 `vim.lsp.completion`。\n- `:Minuet lsp completion disable_auto_trigger`：禁用**当前缓冲区**的自动触发 `vim.lsp.completion`。\n- `:Minuet lsp inline_completion enable_auto_trigger`：为**当前缓冲区**启用 `vim.lsp.inline_completion` 的自动触发。\n- `:Minuet lsp inline_completion disable_auto_trigger`：禁用**当前缓冲区**的 `vim.lsp.inline_completion` 自动触发。\n\n# 二重奏（下一次编辑预测）\n\n`Minuet duet` 是 Minuet 的一项高度实验性的下一次编辑预测（NES）功能。\n\n基本用法为手动操作。将二重奏命令绑定到您偏好的键位映射中，然后：\n\n1. 触发 `:Minuet duet predict` 来请求当前编辑的预测。\n2. 查看缓冲区中渲染的预览。\n3. 使用 `:Minuet duet apply` 应用预测，或使用 `:Minuet duet dismiss` 丢弃它。\n\n示例键位映射：\n\n```lua\nvim.keymap.set('n', '\u003Cleader>mp', '\u003Ccmd>Minuet duet predict\u003Ccr>', { desc = 'Minuet duet predict' })\nvim.keymap.set('n', '\u003Cleader>ma', '\u003Ccmd>Minuet duet apply\u003Ccr>', { desc = 'Minuet duet apply' })\nvim.keymap.set('n', '\u003Cleader>md', '\u003Ccmd>Minuet duet dismiss\u003Ccr>', { desc = 'Minuet duet dismiss' })\nvim.keymap.set('i', '\u003CA-z>', '\u003Ccmd>Minuet duet predict\u003Ccr>', { desc = 'Minuet duet predict' })\nvim.keymap.set('i', '\u003CA-a>', '\u003Ccmd>Minuet duet apply\u003Ccr>', { desc = 'Minuet duet apply' })\nvim.keymap.set('i', '\u003CA-x>', '\u003Ccmd>Minuet duet dismiss\u003Ccr>', { desc = 'Minuet duet dismiss' })\n```\n\n目前推荐使用的模型是 `gemini-3-flash-preview`。\n\n```lua\nrequire('minuet').setup {\n    duet = {\n        provider = 'gemini',\n        provider_options = {\n            gemini = {\n                model = 'gemini-3-flash-preview',\n                optional = {\n                    generationConfig = {\n                        thinkingConfig = {\n                            -- 建议关闭思考模式\n                            thinkingLevel = 'minimal',\n                        },\n                    },\n                },\n            },\n            openai_compatible = {\n                model = 'minimax\u002Fminimax-m2.7',\n                optional = {\n                    reasoning_effort = 'minimal',\n                    -- 优先考虑吞吐量以加快完成速度\n                    provider = {\n                        sort = 'throughput',\n                    },\n                },\n            },\n        },\n    },\n}\n```\n\n此功能仍处于高度实验阶段：\n\n- 它仅针对通用型大语言模型，而非专门用于 NES 的模型，因为我缺乏本地 GPU 资源来进行测试。\n- 来自谷歌竞争对手的类似小型模型——`claude-haiku-4.5` 和 `gpt-5.4-mini`——表现较差。\n- 由于生成延迟的限制，尚未实现自动化的二重奏预测。\n\n建议配置模型的思考级别；请参阅[提供商章节](#providers)，以获取关于如何管理各提供商思考设置的指导。\n\n避免为二重奏请求设置过小的 `max_tokens` 或 `max_completion_tokens` 限制。二重奏期望模型返回完整的重写可编辑区域，包括光标标记；如果响应被截断，解析器将会拒绝该响应。当提供商允许时，请保持该限制未设置，或者将其设置得足够大，以覆盖整个重写区域。\n\n## 待办事项\n\n- [ ] 实现一个合适的差异机制，以便在提示中包含最近的编辑更改。\n- [ ] 添加对专用 NES 模型（Zeta、Sweep）的支持。\n- [ ] 与 Inception 的托管 API 集成。\n- [ ] 实现自动触发的二重奏预测。\n\n## 默认配置\n\n```lua\nrequire('minuet').setup {\n    duet = {\n        provider = 'gemini', -- `:Minuet duet predict` 使用的提供者。\n        request_timeout = 15, -- 单次 duet 请求的超时时间，单位为秒。\n        editable_region = {\n            lines_before = 8, -- 光标前包含的可编辑行数。\n            lines_after = 15, -- 光标后包含的可编辑行数。\n            before_region_filter_length = 30, -- 当模型输出重复区域前的不可编辑文本时，从开头裁剪掉重复部分。\n            after_region_filter_length = 30, -- 当模型输出重复区域后的不可编辑文本时，从结尾裁剪掉重复部分。\n        },\n        markers = {\n            editable_region_start = '\u003Ceditable_region>', -- 在提示和响应中包裹可编辑区域起始位置的标记。\n            editable_region_end = '\u003C\u002Feditable_region>', -- 在提示和响应中包裹可编辑区域结束位置的标记。\n            cursor_position = '\u003Ccursor_position\u002F>', -- 模型必须精确保留一次的标记，用于指示最终光标位置。\n        },\n        preview = {\n            cursor = '', -- 预览中显示在预测光标位置的虚拟标记。\n        },\n        provider_options = {\n            openai = {\n                model = 'gpt-5.4-mini', -- duet 请求的默认 OpenAI 模型。\n                api_key = 'OPENAI_API_KEY', -- 环境变量名称，或返回 API 密钥的函数。\n                end_point = 'https:\u002F\u002Fapi.openai.com\u002Fv1\u002Fchat\u002Fcompletions', -- OpenAI 的聊天完成端点。\n                system = { ... }, -- Duet 系统提示配置；除非需要自定义改写提示，否则保持默认。\n                few_shots = { ... }, -- 用于引导改写的用户\u002F助手对话示例。\n                chat_input = { ... }, -- 序列化可编辑和不可编辑缓冲区区域的模板。\n                optional = {}, -- 传递给 OpenAI API 的额外请求体字段。\n                transform = {}, -- 发送请求前应用的可选端点\u002F头部\u002F请求体转换。\n            },\n            claude = {\n                model = 'claude-haiku-4-5', -- duet 请求的默认 Claude 模型。\n                api_key = 'ANTHROPIC_API_KEY', -- 环境变量名称，或返回 API 密钥的函数。\n                end_point = 'https:\u002F\u002Fapi.anthropic.com\u002Fv1\u002Fmessages', -- Claude 的消息端点。\n                system = { ... }, -- 与其他聊天提供者的 Duet 系统提示结构相同。\n                system = { ... }, -- Duet 系统提示配置；除非需要自定义改写提示，否则保持默认。\n                few_shots = { ... }, -- 用于引导改写的用户\u002F助手对话示例。\n                chat_input = { ... }, -- 序列化可编辑和不可编辑缓冲区区域的模板。\n                max_tokens = 8192, -- 请保持此值较大；如果 Claude 截断了改写的可编辑区域，duet 解析将会失败。\n                optional = {}, -- 传递给 Claude API 的额外请求体字段。\n                transform = {}, -- 发送请求前应用的可选端点\u002F头部\u002F请求体转换。\n            },\n            gemini = {\n                model = 'gemini-3-flash-preview', -- 目前推荐的 duet 模型。\n                api_key = 'GEMINI_API_KEY', -- 环境变量名称，或返回 API 密钥的函数。\n                end_point = 'https:\u002F\u002Fgenerativelanguage.googleapis.com\u002Fv1beta\u002Fmodels', -- 基础 Gemini 模型端点。\n                system = { ... }, -- Duet 系统提示配置；除非需要自定义改写提示，否则保持默认。\n                few_shots = { ... }, -- 用于引导改写的用户\u002F助手对话示例。\n                chat_input = { ... }, -- 序列化可编辑和不可编辑缓冲区区域的模板。\n                optional = {}, -- 传递给 OpenAI API 的额外请求体字段。\n                transform = {}, -- 发送请求前应用的可选端点\u002F头部\u002F请求体转换。\n            },\n            openai_compatible = {\n                model = 'minimax\u002Fminimax-m2.7', -- 兼容 OpenAI 的聊天提供者的默认模型。\n                api_key = 'OPENROUTER_API_KEY', -- 环境变量名称，或返回 API 密钥的函数。\n                end_point = 'https:\u002F\u002Fopenrouter.ai\u002Fapi\u002Fv1\u002Fchat\u002Fcompletions', -- 必须是兼容聊天完成的端点。\n                name = 'Openrouter', -- 事件和通知中使用的提供者标签。\n                system = { ... }, -- Duet 系统提示配置；除非需要自定义改写提示，否则保持默认。\n                few_shots = { ... }, -- 用于引导改写的用户\u002F助手对话示例。\n                chat_input = { ... }, -- 序列化可编辑和不可编辑缓冲区区域的模板。\n                optional = {}, -- 传递给 OpenAI API 的额外请求体字段。\n                transform = {}, -- 发送请求前应用的可选端点\u002F头部\u002F请求体转换。\n            },\n        },\n    },\n}\n```\n\n# API\n\n## 虚拟文本\n\n`minuet-ai.nvim` 提供以下函数来定制你的键映射：\n\n```lua\n{\n    -- 接受整个补全\n    require('minuet.virtualtext').action.accept,\n    -- 按行接受\n    require('minuet.virtualtext').action.accept_line,\n    -- 接受 n 行（会提示输入数字）\n    require('minuet.virtualtext').action.accept_n_lines,\n    require('minuet.virtualtext').action.next,\n    require('minuet.virtualtext').action.prev,\n    require('minuet.virtualtext').action.dismiss,\n    -- 检查当前缓冲区中是否可见虚拟文本\n    require('minuet.virtualtext').action.is_visible,\n}\n```\n\n## Duet\n\nDuet 模块提供了用于以编程方式控制 duet 预测的函数：\n\n```lua\n{\n    require('minuet.duet').action.predict,\n    require('minuet.duet').action.apply,\n    require('minuet.duet').action.dismiss,\n    -- 检查当前缓冲区中是否正在显示 duet 预览\n    require('minuet.duet').action.is_visible,\n}\n```\n\n## Lualine\n\nMinuet 提供了一个 Lualine 组件，用于显示当前 Minuet 请求的状态。该组件会展示：\n\n- 当前激活的提供者和模型名称\n- 当前请求计数（例如：“1\u002F3”）\n- 处理过程中显示的动画旋转图标\n\n要使用 Minuet 的 Lualine 组件，只需将其添加到你的 Lualine 配置中：\n\n\u003Cdetails>\n\n```lua\nrequire('lualine').setup {\n    sections = {\n        lualine_x = {\n            {\n                require 'minuet.lualine',\n                -- 以下为默认配置\n                -- 在 Lualine 中显示的名称。可设置为 \"provider\"、\"model\" 或 \"both\"\n                -- display_name = 'both',\n                -- 当设置为 \"both\" 时，提供者与模型名称之间的分隔符\n                -- provider_model_separator = ':',\n                -- 当没有活跃的补全请求时是否显示 display_name\n                -- display_on_idle = false,\n            },\n            'encoding',\n            'fileformat',\n            'filetype',\n        },\n    },\n}\n```\n\n\u003C\u002Fdetails>\n\n## Minuet 事件\n\n### 标准补全事件\n\n- **MinuetRequestStartedPre**：在补全请求开始之前触发。这允许执行请求前的操作，比如记录日志或更新用户界面。\n- **MinuetRequestStarted**：补全请求被发送后立即触发，表示请求正在处理中。\n- **MinuetRequestFinished**：请求完成后触发。\n\n### 双人协作事件\n\n- **MinuetDuetRequestStartedPre**：在双人协作请求开始之前触发。\n- **MinuetDuetRequestStarted**：双人协作请求被发送后立即触发。\n- **MinuetDuetRequestFinished**：双人协作请求完成后触发。\n\n### 事件数据\n\n每个事件都包含一个 `data` 字段，其中包含以下属性：\n\n- `provider`：字符串，表示提供者类型（例如，“openai_compatible”）。\n- `name`：字符串，指定提供者的名称（例如，“OpenAI”、“Groq”、“Ollama”）。\n- `model`：字符串，包含模型名称（例如，“gemini-2.0-flash”）。\n- `n_requests`：本次补全周期内包含的请求数量。\n- `request_idx`（可选）：当前请求的索引，适用于提供者发出多个请求的情况。\n- `timestamp`：Unix 时间戳，表示请求周期的开始时间（对应于 `MinuetRequestStartedPre` 事件）。\n\n# 常见问题解答\n\n## 自定义 `cmp` 界面中的源图标和种类图标\n\n你可以通过以下代码片段来配置由 `minuet` 返回的补全项图标（参考自 [cmp 的维基](https:\u002F\u002Fgithub.com\u002Fhrsh7th\u002Fnvim-cmp\u002Fwiki\u002FMenu-Appearance#basic-customisations))：\n\n\u003Cdetails>\n\n```lua\nlocal kind_icons = {\n    Number = '󰎠',\n    Array = '',\n    Variable = '',\n    -- 其他图标\n    -- LLM 提供者图标\n    claude = '󰋦',\n    openai = '󱢆',\n    codestral = '󱎥',\n    gemini = '',\n    Groq = '',\n    Openrouter = '󱂇',\n    Ollama = '󰳆',\n    ['Llama.cpp'] = '󰳆',\n    Deepseek = ''\n    -- 默认图标\n    fallback = '',\n}\n\nlocal source_icons = {\n    minuet = '󱗻',\n    nvim_lsp = '',\n    lsp = '',\n    buffer = '',\n    luasnip = '',\n    snippets = '',\n    path = '',\n    git = '',\n    tags = '',\n    -- 默认图标\n    fallback = '󰜚',\n}\n\nlocal cmp = require 'cmp'\ncmp.setup {\n    formatting = {\n        format = function(entry, vim_item)\n            -- 种类图标\n            -- 将图标与项目种类名称拼接在一起\n            vim_item.kind = string.format('%s %s', kind_icons[vim_item.kind] or kind_icons.fallback, vim_item.kind)\n            -- 源\n            vim_item.menu = source_icons[entry.source.name] or source_icons.fallback\n            return vim_item\n        end,\n    },\n}\n```\n\n\u003C\u002Fdetails>\n\n## 自定义 `blink` 界面中的源图标和种类图标\n\n你可以通过以下代码片段来配置由 `minuet` 返回的补全项图标：\n\n\u003Cdetails>\n\n自定义种类图标：\n\n```lua\nlocal kind_icons = {\n    -- LLM 提供者图标\n    claude = '󰋦',\n    openai = '󱢆',\n    codestral = '󱎥',\n    gemini = '',\n    Groq = '',\n    Openrouter = '󱂇',\n    Ollama = '󰳆',\n    ['Llama.cpp'] = '󰳆',\n    Deepseek = ''\n}\n\nrequire('blink-cmp').setup {\n    appearance = {\n        use_nvim_cmp_as_default = true,\n        nerd_font_variant = 'normal',\n        kind_icons = kind_icons\n    },\n}\n```\n\n自定义源图标：\n\n```lua\nlocal source_icons = {\n    minuet = '󱗻',\n    orgmode = '',\n    otter = '󰼁',\n    nvim_lsp = '',\n    lsp = '',\n    buffer = '',\n    luasnip = '',\n    snippets = '',\n    path = '',\n    git = '',\n    tags = '',\n    cmdline = '󰘳',\n    latex_symbols = '',\n    cmp_nvim_r = '󰟔',\n    codeium = '󰩂',\n    -- 默认图标\n    fallback = '󰜚',\n}\n\nrequire('blink-cmp').setup {\n    appearance = {\n        use_nvim_cmp_as_default = true,\n        nerd_font_variant = 'normal',\n        kind_icons = kind_icons\n    },\n    completion = {\n        menu = {\n            draw = {\n                columns = {\n                    { 'label', 'label_description', gap = 1 },\n                    { 'kind_icon', 'kind' },\n                    { 'source_icon' },\n                },\n                components = {\n                    source_icon = {\n                        -- 不截断源图标\n                        ellipsis = false,\n                        text = function(ctx)\n                            return source_icons[ctx.source_name:lower()] or source_icons.fallback\n                        end,\n                        highlight = 'BlinkCmpSource',\n                    },\n                },\n            },\n        },\n    }\n}\n```\n\n\u003C\u002Fdetails>\n\n## 使用 `nvim-cmp` 时换行输入延迟较大\n\n当使用 Minuet 并启用自动补全功能时，按下 `\u003CCR>` 键换行可能会偶尔出现明显的延迟。这是因为 Minuet 会在新行开始时触发自动补全，而 cmp 则会阻塞 `\u003CCR>` 键，等待 Minuet 的响应。\n\n为了解决这个问题，可以考虑以下方案：\n\n1. 解绑 `cmp` 键映射中的 `\u003CCR>` 键。\n2. 使用 cmp 的内部 API 来避免阻塞调用，但请注意，该 API 可能会在未提前通知的情况下发生变化。\n\n以下是使用 Lua 实现第二种方法的示例：\n\n```lua\nlocal cmp = require 'cmp'\nopts.mapping = {\n    ['\u003CCR>'] = cmp.mapping(function(fallback)\n        -- 使用内部非阻塞调用来检查 cmp 是否可见\n        if cmp.core.view:visible() then\n            cmp.confirm { select = true }\n        else\n            fallback()\n        end\n    end),\n}\n```\n\n## 与 `lazyvim` 的集成\n\n\u003Cdetails>\n\n**使用 nvim-cmp**：\n\n```lua\n{\n    'milanglacier\u002Fminuet-ai.nvim',\n    config = function()\n        require('minuet').setup {\n            -- 您的配置选项在此处\n        }\n    end,\n},\n{\n    'nvim-cmp',\n    optional = true,\n    opts = function(_, opts)\n        -- 如果您希望使用自动补全功能\n        table.insert(opts.sources, 1, {\n            name = 'minuet',\n            group_index = 1,\n            priority = 100,\n        })\n\n        opts.performance = {\n            -- 建议增加超时时间，因为与其他补全源相比，LLM 的响应速度通常较慢。如果您只需要手动补全，则无需此设置。\n            fetching_timeout = 2000,\n        }\n\n        opts.mapping = vim.tbl_deep_extend('force', opts.mapping or {}, {\n            -- 如果您希望使用手动补全功能\n            ['\u003CA-y>'] = require('minuet').make_cmp_map(),\n        })\n    end,\n}\n```\n\n**使用 blink-cmp**：\n\n```lua\n-- 在您的 config\u002Foptions.lua 中设置以下行\nvim.g.lazyvim_blink_main = true\n\n{\n    'milanglacier\u002Fminuet-ai.nvim',\n    config = function()\n        require('minuet').setup {\n            -- 您的配置选项在此处\n        }\n    end,\n},\n{\n    'saghen\u002Fblink.cmp',\n    optional = true,\n    opts = {\n        keymap = {\n            ['\u003CA-y>'] = {\n                function(cmp)\n                    cmp.show { providers = { 'minuet' } }\n                end,\n            },\n        },\n        sources = {\n            -- 如果您想使用自动补全功能\n            default =  { 'minuet' },\n            providers = {\n                minuet = {\n                    name = 'minuet',\n                    module = 'minuet.blink',\n                    score_offset = 100,\n                },\n            },\n        },\n    },\n}\n```\n\n\u003C\u002Fdetails>\n\n# 增强功能\n\n## RAG（实验性）\n\n您可以通过利用 [VectorCode](https:\u002F\u002Fgithub.com\u002FDavidyz\u002FVectorCode) 包提供的 RAG 支持，增强发送给 LLM 用于代码补全的内容。\n\nVectorCode 包含两个主要组件。第一个是用 Python 编写的独立 CLI 程序，可通过 PyPI 安装。该程序负责创建向量数据库并处理 RAG 查询。第二个组件是 Neovim 插件，它提供了在 Neovim 内发送查询和管理与缓冲区相关的 RAG 信息的实用函数。\n\n我们提供了两个示例配方来演示 VectorCode 的集成：一个适用于基于聊天的 LLM（Gemini），另一个适用于 FIM 模型（Qwen-2.5-Coder），这些配方可在 [recipes.md](.\u002Frecipes.md) 中找到。\n\n有关设置和使用 VectorCode 的详细说明，请参阅 [官方 VectorCode 文档](https:\u002F\u002Fgithub.com\u002FDavidyz\u002FVectorCode\u002Ftree\u002Fmain\u002Fdocs\u002Fneovim)。\n\n# 故障排除\n\n如果您的设置失败，最可能的原因有两个：\n\n1. 您可能错误地设置了 API 密钥。请查看 [API 密钥](#api-keys) 部分，了解如何正确指定 API 密钥。\n2. 您正在使用模型或上下文窗口过大，导致补全项在返回任何标记之前就超时了。这种情况在本地 LLM 中尤为常见。建议从以下设置开始，以便更好地了解您的提供商的推理速度：\n   - 首先尝试手动补全。\n   - 使用较小的上下文窗口（例如 `config.context_window = 768`）。\n   - 使用较小的模型。\n   - 设置较长的请求超时时间（例如 `config.request_timeout = 5`）。\n\n要诊断问题，请将 `config.notify = debug` 设置为启用，并检查输出。\n\n# 贡献\n\n欢迎贡献！请随时提交 Pull Request。\n\n# 致谢\n\n- [cmp-ai](https:\u002F\u002Fgithub.com\u002Ftzachar\u002Fcmp-ai)：与 `nvim-cmp` 集成的参考。\n- [continue.dev](https:\u002F\u002Fwww.continue.dev)：虽然不是 Neovim 插件，但我从中找到了许多 LLM 模型。\n- [copilot.lua](https:\u002F\u002Fgithub.com\u002Fzbirenbaum\u002Fcopilot.lua)：虚拟文本前端的参考。\n- [llama.vim](https:\u002F\u002Fgithub.com\u002Fggml-org\u002Fllama.vim)：用于启动 llama-cpp 服务器的 CLI 参数参考。\n- [crates.nvim](https:\u002F\u002Fgithub.com\u002Fsaecki\u002Fcrates.nvim)：用于提供补全的进程内 LSP 实现参考。","# Minuet-ai.nvim 快速上手指南\n\nMinuet 是一款为 Neovim 打造的 AI 代码补全插件，支持多种大模型提供商（如 DeepSeek、OpenAI、Ollama 等），提供流式补全、虚拟文本提示及多行建议逐行接受等功能。\n\n## 环境准备\n\n在开始之前，请确保满足以下要求：\n\n*   **Neovim 版本**：0.10 或更高（内置 LSP 补全需 0.11+）。\n*   **依赖项**（可选）：\n    *   `nvim-cmp`：如果你习惯使用 cmp 前端。\n    *   `blink.cmp`：如果你习惯使用 blink 前端。\n    *   若仅使用虚拟文本（Virtual Text）模式，上述两者均非必需。\n*   **API Key**：至少需要一个支持的 AI 服务提供商的 API Key（如 DeepSeek、OpenAI、Ollama 本地服务等）。\n*   **系统工具**：无需后台二进制进程，仅需系统自带 `curl`。\n\n> **注意**：Minuet 现已使用 Neovim 内置的 `vim.system`，不再需要 `plenary.nvim`。\n\n## 安装步骤\n\n推荐使用 **lazy.nvim** 进行包管理。在你的插件配置文件中添加以下内容：\n\n```lua\nspecs = {\n    {\n        'milanglacier\u002Fminuet-ai.nvim',\n        config = function()\n            require('minuet').setup {\n                -- 在此处填写你的配置选项\n                -- 例如配置 API Key 和默认模型\n                provider = {\n                    name = 'openai_compatible',\n                    api_key = 'YOUR_API_KEY_HERE', -- 替换为你的 Key\n                    base_url = 'https:\u002F\u002Fapi.deepseek.com', -- 以 DeepSeek 为例\n                    model = 'deepseek-coder',\n                },\n            }\n        end,\n    },\n    -- 可选：如果使用虚拟文本前端，不需要安装 nvim-cmp\n    { 'hrsh7th\u002Fnvim-cmp' },\n    -- 可选：如果使用虚拟文本前端，不需要安装 blink.cmp\n    { 'Saghen\u002Fblink.cmp' },\n}\n```\n\n如果你使用 **rocks.nvim**，可以直接运行命令安装：\n```bash\nRocks install minuet-ai.nvim\n```\n\n## 基本使用\n\nMinuet 支持多种前端模式，以下是三种最常用的快速配置方案。\n\n### 方案一：虚拟文本模式 (Virtual Text)\n无需额外插件，直接在代码行后显示灰色建议内容。\n\n```lua\nrequire('minuet').setup {\n    virtualtext = {\n        auto_trigger_ft = {}, -- 空表表示手动触发，或填入 {'lua', 'python'} 自动触发\n        keymap = {\n            accept = '\u003CA-A>',        -- 接受整段建议\n            accept_line = '\u003CA-a>',   -- 接受一行\n            accept_n_lines = '\u003CA-z>',-- 接受指定行数\n            prev = '\u003CA-[>',          -- 上一条建议\n            next = '\u003CA-]>',          -- 下一条建议\n            dismiss = '\u003CA-e>',       -- 关闭建议\n        },\n    },\n}\n```\n\n### 方案二：集成 nvim-cmp\n将 Minuet 作为 cmp 的一个源，与其他补全源混合使用。\n\n```lua\n-- 1. 配置 cmp 源\nrequire('cmp').setup {\n    sources = {\n        { name = 'minuet' }, -- 启用 minuet\n        -- 其他源...\n    },\n    performance = {\n        -- LLM 响应较慢，建议增加超时时间\n        fetching_timeout = 2000,\n    },\n}\n\n-- 2. 配置手动触发快捷键 (可选)\nrequire('cmp').setup {\n    mapping = {\n        [\"\u003CA-y>\"] = require('minuet').make_cmp_map(),\n        -- 其他映射...\n    },\n}\n```\n\n### 方案三：集成 blink.cmp\n适用于使用 blink.cmp 的用户。\n\n```lua\nrequire('blink-cmp').setup {\n    keymap = {\n        ['\u003CA-y>'] = require('minuet').make_blink_map(), -- 手动触发\n    },\n    sources = {\n        default = { 'lsp', 'path', 'buffer', 'snippets', 'minuet' },\n        providers = {\n            minuet = {\n                name = 'minuet',\n                module = 'minuet.blink',\n                async = true,\n                timeout_ms = 3000, -- 需匹配 minuet.config.request_timeout * 1000\n                score_offset = 50, -- 提高优先级\n            },\n        },\n    },\n    completion = { \n        trigger = { prefetch_on_insert = false } -- 避免不必要的请求\n    },\n}\n```\n\n### 常用命令\n安装完成后，你可以在 Neovim 中使用以下命令切换模型或提供商：\n\n*   `:Minuet change_provider` - 切换 AI 提供商\n*   `:Minuet change_model` - 切换具体模型\n*   `:Minuet virtualtext` - 切换虚拟文本模式\n*   `:Minuet duet` - 开启实验性的“下一编辑预测”功能\n\n> **国内开发者提示**：配置 `provider` 时，若使用国内模型（如 DeepSeek、通义千问等），请将 `base_url` 指向对应的国内 API 地址，以获得更低的延迟和更好的连接稳定性。","一位后端工程师正在 Neovim 中紧急重构一个复杂的异步数据处理模块，需要频繁编写重复的样板代码和调用新的第三方 API。\n\n### 没有 minuet-ai.nvim 时\n- **上下文切换频繁**：遇到不熟悉的 API 参数或复杂逻辑时，必须离开编辑器去浏览器搜索文档或询问聊天机器人，打断心流。\n- **样板代码手写低效**：大量的错误处理、日志记录和类型定义需要逐行手动敲击，不仅耗时且容易因疲劳产生拼写错误。\n- **模型适配困难**：想要尝试本地运行的 Qwen 模型以保护隐私，却苦于缺乏便捷的插件将 Ollama 或 Llama.cpp 直接集成到补全工作流中。\n- **长建议难以采纳**：面对多行代码建议，传统工具要么一次性全部接受（可能包含不需要的部分），要么完全拒绝，无法按需逐行吸收。\n\n### 使用 minuet-ai.nvim 后\n- **智能随写随补**：minuet-ai.nvim 利用 FIM（中间填充）技术，根据前后文实时预测并生成完整的函数实现，工程师无需离开键盘即可获取精准的 API 调用示例。\n- **流式输出零等待**：即使连接较慢的本地大模型，流式传输功能也能让代码逐个字符显现，大幅降低感知延迟，保持编码节奏流畅。\n- **灵活的多模型支持**：通过简单配置即可在 OpenAI、Claude 和本地 Ollama 之间无缝切换，既享受云端模型的强大推理，又能在敏感项目中启用本地私有模型。\n- **增量式建议采纳**：面对多行重构建议，工程师可以逐行确认接受，完美匹配自己的思考速度，避免引入多余代码，同时已输入内容若与建议重合会自动同步，减少无效请求。\n\nminuet-ai.nvim 将大模型的智能深度融入 Neovim 编辑体验，让开发者在“人机共舞”中显著提升编码效率与专注度。","https:\u002F\u002Foss.gittoolsai.com\u002Fimages\u002Fmilanglacier_minuet-ai.nvim_b90b8309.png","milanglacier",null,"https:\u002F\u002Foss.gittoolsai.com\u002Favatars\u002Fmilanglacier_bc2cab86.png","https:\u002F\u002Fgithub.com\u002Fmilanglacier",[79,83,87],{"name":80,"color":81,"percentage":82},"Lua","#000080",99.7,{"name":84,"color":85,"percentage":86},"Shell","#89e051",0.2,{"name":88,"color":89,"percentage":90},"Makefile","#427819",0.1,1102,50,"2026-04-17T23:18:20","GPL-3.0","Linux, macOS, Windows","非必需（取决于所选 LLM 提供商；若使用本地 Ollama 或 llama.cpp 则需相应 GPU 支持，插件本身无特定显卡要求）","未说明（取决于所选模型大小，插件本身无特定内存要求）",{"notes":99,"python":100,"dependencies":101},"该工具是纯 Lua 编写的 Neovim 插件，无需后台二进制程序。核心依赖为 Neovim 0.10+ 和 curl。若使用内置补全功能需 Neovim 0.11+，行内补全需 0.12+。需配置至少一个 AI 提供商的 API Key（如 OpenAI, Claude, Ollama 等）。不再需要 plenary.nvim。","未说明",[102,103,104,105],"Neovim 0.10+","curl","nvim-cmp (可选)","blink.cmp (可选)",[35,45,14,13,15],[108,109,110,111,112],"ai","code-completion","llm","neovim","neovim-plugin","2026-03-27T02:49:30.150509","2026-04-18T14:30:59.568739",[116,121,126,131,136,140],{"id":117,"question_zh":118,"answer_zh":119,"source_url":120},39927,"如何配置 Minuet 以便仅在手动触发时使用，而不是自动完成？","如果您希望仅手动触发完成而不参与自动完成循环，请执行以下步骤：\n1. 不要将 `minuet` 添加到 `nvim-cmp` 的 sources 列表中。\n2. 在 `cmp.setup` 的 mapping 配置中绑定一个按键（例如 `Alt+Y`）到 `require('minuet').make_cmp_map()`。\n这样，只有当您按下绑定的按键时才会触发 Minuet 补全。注意：`config.cmp.enable_auto_complete` 设置仅控制自动触发事件，手动触发（如按 Ctrl+Space）时 Minuet 始终启用。","https:\u002F\u002Fgithub.com\u002Fmilanglacier\u002Fminuet-ai.nvim\u002Fissues\u002F102",{"id":122,"question_zh":123,"answer_zh":124,"source_url":125},39928,"如何使用 \u003CCR> (Enter 键) 接受虚拟文本建议，同时保留插入换行符的功能？","由于直接绑定 \u003CCR> 会覆盖默认的换行功能，您需要利用补全前端（如 nvim-cmp 或 blink-cmp）的回退逻辑来实现条件绑定。以下是一个基于 nvim-cmp 的解决方案示例：\n\n```lua\n[\"\u003CCR>\"] = cmp.mapping(function(fallback)\n  local minuet = require(\"minuet.virtualtext\").action\n  if minuet.is_visible() then\n    minuet.accept()\n  else\n    -- 如果没有虚拟文本建议，则回退到常规的 cmp 确认或默认行为\n    cmp.mapping.confirm({\n      behavior = cmp.ConfirmBehavior.Replace,\n      select = false,\n    })(fallback)\n  end\nend, { \"i\", \"s\" })\n```\n\n对于 blink-cmp 用户，逻辑类似：检查 `require('minuet.virtualtext').action.is_visible()`，如果为真则接受建议，否则返回 nil 以触发默认行为或后续映射。","https:\u002F\u002Fgithub.com\u002Fmilanglacier\u002Fminuet-ai.nvim\u002Fissues\u002F44",{"id":127,"question_zh":128,"answer_zh":129,"source_url":130},39929,"是否支持根据不同的文件类型使用不同的模型或提示词？","是的，您可以通过在配置中传递函数来动态调整提示词（prompts）和少量示例（few_shots）。例如，根据文件类型返回不同的 system prompt：\n\n```lua\nrequire('minuet').setup {\n    provider_options = {\n        openai = {\n            system = {\n                prompt = function()\n                    if vim.bo.ft == 'tex' then\n                        return [[您的 LaTeX 专用提示词]]\n                    elseif vim.bo.ft == 'mail' then\n                        return [[您的邮件专用提示词]]\n                    else\n                        return require('minuet.config').default_template.prompt\n                    end\n                end,\n            },\n        },\n    },\n}\n```\n\n关于基于 Treesitter 上下文的调整，目前维护者暂无计划实现，因为需要为每种文件类型编写特定查询且难以控制上下文长度。未来的开发重点将放在虚拟文本和 RAG 功能上。","https:\u002F\u002Fgithub.com\u002Fmilanglacier\u002Fminuet-ai.nvim\u002Fissues\u002F6",{"id":132,"question_zh":133,"answer_zh":134,"source_url":135},39930,"Minuet 是否支持类似 Cursor 的“下一步编辑预测”（Next Edit Prediction）功能？","是的，Minuet 已经实现了名为 **Minuet duet** 的实验性功能来支持下一步编辑预测。该模块完全用 Lua 编写。\n\n目前的使用限制和说明：\n1. 该功能处于高度实验阶段，主要针对通用大语言模型（LLM），而非专门的 NES 模型。\n2. 测试表明，小模型中只有 `gemini-3-flash-preview` 表现尚可，而 `claude-haiku-4.5` 和 `gpt-5.4-mini` 表现较差。\n3. 由于延迟限制，目前不支持自动触发，必须手动运行命令：\n   - 预测：`Minuet duet predict`\n   - 应用：`Minuet duet apply`\n\n未来计划包括实现差异机制以包含最近的编辑变更、支持专用 NES 模型（如 Zeta, Sweep）以及集成 Inception 的托管 API。","https:\u002F\u002Fgithub.com\u002Fmilanglacier\u002Fminuet-ai.nvim\u002Fissues\u002F70",{"id":137,"question_zh":138,"answer_zh":139,"source_url":135},39931,"是否有其他插件推荐用于实现类似 Cursor Tab 的多行部分建议或跳转功能？","如果您正在寻找支持 Sweep 模型或其他类似 Cursor Tab 功能的插件，社区推荐尝试 `cursortab.nvim`。该插件支持包括 Sweep 在内的多种模型，可能为您提供更成熟的多行建议和跳转体验。\n\n项目地址：https:\u002F\u002Fgithub.com\u002Fleonardcser\u002Fcursortab.nvim",{"id":141,"question_zh":142,"answer_zh":143,"source_url":120},39932,"为什么执行 `Minuet cmp disable` 后，手动触发补全时仍然会发送请求？","这是预期行为。`Minuet cmp disable`（或配置项 `config.cmp.enable_auto_complete`）仅控制 Minuet 是否在**自动触发**的补全事件中被调用。\n\n当您手动触发补全（例如按下 `Ctrl+Space`）时，无论该开关状态如何，Minuet 都会保持启用并响应请求。如果您希望彻底禁用自动请求并仅通过特定按键手动触发，请参考“如何配置 Minuet 以便仅在手动触发时使用”的相关解答，即不从 sources 中添加 minuet，而是通过 keymap 手动绑定触发。",[145,150,155,160,165,170,175,180,185],{"id":146,"version":147,"summary_zh":148,"released_at":149},323418,"v0.8.0","# 版本 0.8.0（2025-12-13）\n\n## 破坏性变更\n\n- 将 `config.enabled` 重命名为 `config.enable_predicates`\n\n## 功能特性\n\n- 虚拟文本：\n  - 连续逐行接受：`accept_line` 命令现在会保留部分已接受的建议状态。这使得用户可以通过多次调用该命令，逐步接受多行建议。\n  - 多行输入时建议保持不变：即使用户正在输入多行文本，前缀匹配的建议也会持续显示，从而避免不必要的 LLM 请求并节省资源。\n\n","2025-12-14T03:56:59",{"id":151,"version":152,"summary_zh":153,"released_at":154},323419,"v0.7.0","# 版本 0.7.0（2025-12-09）\n\n## 功能\n\n- defaults：将默认的 `before_cursor_filter_length` 设置为 2。\n- claude：将默认模型更改为 claude-haiku-4.5。\n- claude：将默认的 `max_tokens` 更改为 256。\n- 添加 `enabled` 选项，支持在运行时动态启用。\n- virtualtext：当用户输入与现有补全项匹配时，保留补全项（#90）。\n\n## 新贡献者\n* @celso 在 https:\u002F\u002Fgithub.com\u002Fmilanglacier\u002Fminuet-ai.nvim\u002Fpull\u002F114 中完成了首次贡献。\n* @ermonste 在 https:\u002F\u002Fgithub.com\u002Fmilanglacier\u002Fminuet-ai.nvim\u002Fpull\u002F116 中完成了首次贡献。\n\n**完整变更日志**：https:\u002F\u002Fgithub.com\u002Fmilanglacier\u002Fminuet-ai.nvim\u002Fcompare\u002Fv0.6.0...v0.7.0","2025-12-09T22:24:07",{"id":156,"version":157,"summary_zh":158,"released_at":159},323420,"v0.6.0","# 版本 0.6.0（2025-08-11）\n\n## 破坏性变更\n\n- 改进基于光标前后上下文的补全过滤：\n  - 重构了补全过滤逻辑，使其基于最长公共匹配。\n  - 新增 `before-cursor-filter-length` 配置选项，可根据光标前的文本修剪补全结果中的重复前缀。\n- 更改默认少样本示例：默认的少样本示例已更新，要求 AI 结合光标前后的信息来生成正确的逻辑。\n- 更新默认系统提示：系统提示经过优化，更加简洁，并为 AI 提供了更清晰的指令，以应对代码、注释和字符串等多种补全场景。\n\n## 功能\n\n- 使 Gemini 和 Claude 的端点可配置：用户现在可以为 Gemini 和 Claude 提供商指定自定义的 API 端点。\n\n## 修复\n\n- 处理非流式请求中的空字符串：确保在非流式请求中，API 返回的空字符串能够被正确处理。\n\n## 其他\n\n- 重构 Gemini 请求：更新了 Gemini 请求，使用 `x-goog-api-key` 头部，以适应上游的变化。\n- 按照 luv 手册的建议，使用小写字符串而非数字来发送信号。\n\n## 新贡献者\n* @baronrabban 在 https:\u002F\u002Fgithub.com\u002Fmilanglacier\u002Fminuet-ai.nvim\u002Fpull\u002F94 中做出了首次贡献。\n* @catherinetcai 在 https:\u002F\u002Fgithub.com\u002Fmilanglacier\u002Fminuet-ai.nvim\u002Fpull\u002F96 中做出了首次贡献。\n* @bdchik 在 https:\u002F\u002Fgithub.com\u002Fmilanglacier\u002Fminuet-ai.nvim\u002Fpull\u002F104 中做出了首次贡献。\n\n**完整变更日志**：https:\u002F\u002Fgithub.com\u002Fmilanglacier\u002Fminuet-ai.nvim\u002Fcompare\u002Fv0.5.2...v0.6.0","2025-08-11T20:44:17",{"id":161,"version":162,"summary_zh":163,"released_at":164},323421,"v0.5.2","## 功能特性\n\n- lsp：为补全项添加 `adjust_indentation` 选项。\n- lualine：显示提供者和模型名称。\n- 聊天输入模板可以是字符串列表。\n\n## 修复\n\n- 光标位置：在接受单行虚拟文本补全时，将光标置于正确位置。\n\n## 新贡献者\n* @Jari27 在 https:\u002F\u002Fgithub.com\u002Fmilanglacier\u002Fminuet-ai.nvim\u002Fpull\u002F82 中做出了首次贡献。\n* @BohdaR 在 https:\u002F\u002Fgithub.com\u002Fmilanglacier\u002Fminuet-ai.nvim\u002Fpull\u002F89 中做出了首次贡献。\n\n**完整变更日志**：https:\u002F\u002Fgithub.com\u002Fmilanglacier\u002Fminuet-ai.nvim\u002Fcompare\u002Fv0.5.1...v0.5.2","2025-05-12T03:48:18",{"id":166,"version":167,"summary_zh":168,"released_at":169},323422,"v0.5.1","## 功能特性\n\n- 为兼容 OpenAI-FIM 的提供商新增了 `transform` 选项。\n\n  此功能使得非 OpenAI-FIM 兼容的 API 能够通过兼容 OpenAI-FIM 的提供商使用，例如 DeepInfra FIM API。示例配置可在 [recipes.md](.\u002Frecipes.md) 中找到。\n\n## 新贡献者\n* @ilan-schemoul 在 https:\u002F\u002Fgithub.com\u002Fmilanglacier\u002Fminuet-ai.nvim\u002Fpull\u002F69 中完成了首次贡献。\n* @taiwbi 在 https:\u002F\u002Fgithub.com\u002Fmilanglacier\u002Fminuet-ai.nvim\u002Fpull\u002F66 中完成了首次贡献。\n\n**完整变更日志**: https:\u002F\u002Fgithub.com\u002Fmilanglacier\u002Fminuet-ai.nvim\u002Fcompare\u002Fv0.5.0...v0.5.1","2025-04-08T19:16:46",{"id":171,"version":172,"summary_zh":173,"released_at":174},323423,"v0.5.0","# 版本 0.5.0（2025-03-28）\n\n## 破坏性变更\n\n- 修改了 Gemini 提供者的默认提示策略，改为使用新的\n  **前缀优先** 结构。\n- 其他提供者将继续使用其先前的默认提示配置。\n\n## 功能\n\n- lsp：始终启用 `prepend_to_complete_word`，无需检查 `TriggerCharacter`\n- 为聊天型大模型新增“前缀优先”提示结构。\n\n## 新贡献者\n* @Abizrh 在 https:\u002F\u002Fgithub.com\u002Fmilanglacier\u002Fminuet-ai.nvim\u002Fpull\u002F67 中做出了首次贡献\n\n**完整变更日志**：https:\u002F\u002Fgithub.com\u002Fmilanglacier\u002Fminuet-ai.nvim\u002Fcompare\u002Fv0.4.2...v0.5.0","2025-03-28T07:22:28",{"id":176,"version":177,"summary_zh":178,"released_at":179},323424,"v0.4.2","# 版本 0.4.2（2025-03-21）\n\n## 破坏性变更\n\n- 将多行指示符改为 Unicode 字符 `⏎`。\n\n## 功能\n\n- **lsp**: 将多行指示符改为 Unicode 字符 `⏎`。\n- Lualine 集成：新增加载动画组件。\n- **lsp**: 增加 detail 字段，用于显示提供者名称。\n- **Minuet Event**: 在其请求流程中新增三个用户事件。\n\n## 修复\n\n- **lsp**: 处理 params.context 未提供的请求（适用于 mini.completion）。\n\n## 新贡献者\n* @nguquen 在 https:\u002F\u002Fgithub.com\u002Fmilanglacier\u002Fminuet-ai.nvim\u002Fpull\u002F62 中完成了首次贡献。\n\n**完整更新日志**: https:\u002F\u002Fgithub.com\u002Fmilanglacier\u002Fminuet-ai.nvim\u002Fcompare\u002Fv0.4.1...v0.4.2","2025-03-21T18:06:39",{"id":181,"version":182,"summary_zh":183,"released_at":184},323425,"v0.4.1","# 版本 0.4.1（2025-03-19）\n\n## 破坏性变更\n\n- **lsp**: 不再显式为不在 `enabled_auto_trigger_ft` 中的文件类型禁用自动触发。\n\n## 修复\n\n- **lsp**: 修复在尝试获取当前行内容时光标列位置不正确的问题。\n- **lsp**: 在节流时提前返回。\n- \n**完整更新日志**: https:\u002F\u002Fgithub.com\u002Fmilanglacier\u002Fminuet-ai.nvim\u002Fcompare\u002Fv0.4.0...v0.4.1","2025-03-19T19:05:46",{"id":186,"version":187,"summary_zh":188,"released_at":189},323426,"v0.4.0","# 版本 0.4.0（2025-03-18）\n\n## 功能特性\n\n- 引入了内置补全的进程内 LSP 支持。\n- 对于 `blink-cmp`，补全项的 `kind_name` 现在会显示 LLM 提供商的名称。\n- 改进了对流式与非流式 JSON 解码的错误处理，提供了更具信息量的错误提示。\n\n**完整变更日志**：https:\u002F\u002Fgithub.com\u002Fmilanglacier\u002Fminuet-ai.nvim\u002Fcompare\u002Fv0.3.3...v0.4.0","2025-03-19T04:03:22"]