使用 deepseek, claude, gemini,ollama 或者流行大模型进行代码补全,欢迎大家试用 minuet-ai.el

大家好!我很高兴向大家正式发布 minuet-ai.el 插件。此前我曾在帖子中分享过直接调用大模型进行代码补全的方案,现在这个项目终于与大家见面了。

minuet-ai.el 是 Emacs 代码补全插件,其主要特点是:

  1. 直接通过 curl 调用大语言模型进行代码补全
  2. 支持两种补全模式:
    • 基于 chat LLM 的 prompt building 方式,解析 LLM 的回答并构建补全方案。
    • 通过 openai completion API 实现的 fill-in-the-middle (FIM) 方式

目前支持的模型包括:

  • OpenAI
  • Claude
  • Gemini
  • Codestral
  • 所有兼容 OpenAI chat API 的模型(如 Fireworks、DeepSeek、Ollama)
  • 所有兼容 OpenAI completion API 的模型(如 DeepSeek、Ollama)

插件提供了简单的配置方案,默认使用 DeepSeek 模型(需要提前设置 DEEPSEEK_API_KEY 环境变量)。此外,还有 Ollama 和 Gemini 的实例配置

默认使用 deepseek:

(straight-use-package '(minuet :host github :repo "milanglacier/minuet-ai.el"))

(use-package minuet
    :init
    (general-define-key
     ;; use completion-in-region for completion
     "M-y" #'minuet-completion-region
     ;; use overlay for completion
     "M-p" #'minuet-previous-suggestion ;; invoke completion or cycle to next completion
     "M-n" #'minuet-next-suggestion ;; invoke completion or cycle to previous completion
     "M-A" #'minuet-accept-suggestion ;; accept whole completion
     "M-a" #'minuet-accept-suggestion-line ;; accept current line completion
     "M-e" #'minuet-dismiss-suggestion)

       ;; 如需启用自动补全
       ;; 注意:即使不启用 minuet-auto-suggestion-mode,也可以手动触发补全
     (add-hook 'prog-mode-hook #'minuet-auto-suggestion-mode)

    :config
    (setq minuet-provider 'openai-fim-compatible)
    )

Ollama 版本

(use-package minuet
    :init
    (general-define-key
     ;; use completion-in-region for completion
     "M-y" #'minuet-completion-region
     ;; use overlay for completion
     "M-p" #'minuet-previous-suggestion ;; invoke completion or cycle to next completion
     "M-n" #'minuet-next-suggestion ;; invoke completion or cycle to previous completion
     "M-A" #'minuet-accept-suggestion ;; accept whole completion
     "M-a" #'minuet-accept-suggestion-line ;; accept current line completion
     "M-e" #'minuet-dismiss-suggestion)

     ;; 如需启用自动补全
     ;; 注意:即使不启用 minuet-auto-suggestion-mode,也可以手动触发补全
     (add-hook 'prog-mode-hook #'minuet-auto-suggestion-mode)

    :config
    (setq minuet-provider 'openai-fim-compatible)
    (plist-put minuet-openai-fim-compatible-options :end-point "http://localhost:11434/v1/completions")
    ;; an arbitrary non-null environment variable as placeholder
    (plist-put minuet-openai-fim-compatible-options :name "Ollama")
    (plist-put minuet-openai-fim-compatible-options :api-key "TERM")
    (plist-put minuet-openai-fim-compatible-options :model "qwen2.5-coder:3b")
    )

Gemini 版本

(use-package minuet
    :init
    (general-define-key
     ;; use completion-in-region for completion
     "M-y" #'minuet-completion-region
     ;; use overlay for completion
     "M-p" #'minuet-previous-suggestion ;; invoke completion or cycle to next completion
     "M-n" #'minuet-next-suggestion ;; invoke completion or cycle to previous completion
     "M-A" #'minuet-accept-suggestion ;; accept whole completion
     "M-a" #'minuet-accept-suggestion-line ;; accept current line completion
     "M-e" #'minuet-dismiss-suggestion)

     ;; 如需启用自动补全
     ;; 注意:即使不启用 minuet-auto-suggestion-mode,也可以手动触发补全
     (add-hook 'prog-mode-hook #'minuet-auto-suggestion-mode)

    :config
    (setq minuet-provider 'gemini)
    (minuet-set-optional-options minuet-gemini-options
                                :generationConfig
                                '(:maxOutputTokens 256
                                :topP 0.9))
    (minuet-set-optional-options minuet-gemini-options
                                :safetySettings
                                [(:category "HARM_CATEGORY_DANGEROUS_CONTENT"
                                :threshold "BLOCK_NONE")
                                (:category "HARM_CATEGORY_HATE_SPEECH"
                                :threshold "BLOCK_NONE")
                                (:category "HARM_CATEGORY_HARASSMENT"
                                :threshold "BLOCK_NONE")
                                (:category "HARM_CATEGORY_SEXUALLY_EXPLICIT"
                                :threshold "BLOCK_NONE")])

    )

最后上效果图:

欢迎大家试用,并感谢一切反馈和建议!

12 个赞

哈哈, 佬, 你终于把它释放出来当一个包了…

codestral 今日发布了最新版 2025-01 模型,据称 FIM 补全效果超过 deepseek v3。目前仍处于免费试用阶段。感兴趣的可以注册一个 codestral api 账号并体验 (注意使用 CODESTRAL_API_KEY 不要使用 MISTRAL_API_KEY)。

请问是否需要科学上网?有可能出离线版本吗?

mistral 应该是没有锁 IP,应该可以直接用的。codestral v1 是开源了的,可以在 ollama 上使用,不过尺寸较大 (22B)一般在本地部署没办法达到满意的速度。最新版 2501 没有开源,只能调 API。

执行以下语句:

  (use-package minuet
 
    :init
    (general-define-key

     ;; use completion-in-region for completion
     "M-y" #'minuet-completion-region
     ;; use overlay for completion
     "M-i" #'minuet-show-suggestion)

    ;; if you want to enable auto suggestion.
    ;; Note that you can manually invoke completions without enable minuet-auto-suggestion-mode
    (add-hook 'prog-mode-hook #'minuet-auto-suggestion-mode)

    :config
    (setq minuet-provider 'openai-fim-compatible)

    ;; Required when defining minuet-ative-mode-map in insert/normal states.
    ;; Not required when defining minuet-active-mode-map without evil state.
    (add-hook 'minuet-active-mode-hook #'evil-normalize-keymaps)

    (general-define-key
     :keymaps 'minuet-active-mode-map
     "M-p" #'minuet-previous-suggestion ;; invoke completion or cycle to next completion
     "M-n" #'minuet-next-suggestion ;; invoke completion or cycle to previous completion
     "M-A" #'minuet-accept-suggestion ;; accept whole completion
     "M-a" #'minuet-accept-suggestion-line ;; accept current line completion
     "M-e" #'minuet-dismiss-suggestion)

    (minuet-set-optional-options minuet-openai-fim-compatible-options :max_tokens 256))

报以下错误:

■ Error (use-package): minuet/:init: Symbol’s function definition is void: general-define-key

■ Error (use-package): minuet/:catch: Cannot open load file: No such file or directory, plz

■ Error (use-package): minuet/:init: Symbol’s function definition is void: general-define-key

■ Error (use-package): minuet/:catch: Cannot open load file: No such file or directory, plz

■ Error (use-package): minuet/:init: Symbol’s function definition is void: general-define-key

■ Error (use-package): minuet/:catch: Cannot open load file: No such file or directory, plz

背景介绍: 我用的是spacemacs, emacs版本是29.4,已经成功安装了straight包。 看执行结果已经成功下载了minuet在~/.emacs.d/straight/repos下。

是在这个包里,要安装它

1 个赞

定义快捷键可以用系统的默认函数 define-keyglobal-set-key。使用 general 来定义快捷键只是因为我的个人习惯而已。和核心功能无关。

请问和lsp-bridge有没有冲突呢?平时我是使用lsp-bridge来智能提示、代码补全。我也想在emacs上用上ai补全,不知道可以同时用吗?或者是否可以集成。

一个是类似 copilot 的那种 overlay 的方式,一个是弹出选项框补全,不冲突的。

弹窗补全的后端,一般 0.1秒内 lsp 就返回结果了,其他的后端就更快了。然后用 deepseek 或者 local llm 这种,再快也要 1-2s 左右,时间差距太大了,基本上等 lsp 的结果都渲染上屏了,ai 的结果还没到达。1-2s 这种用 overlay 的话就还好,就相当于你打完字稍微停一下等结果出来就行了,但是就不适合作为弹窗补全的后端使用。

1 个赞

十分感谢!我后面试试看。

请问这个后端可以直接用 llm 库吗 GitHub - ahyatt/llm: A package abstracting llm capabilities for emacs.

我有几个其他的工具是直接用这个库,这样 provider 只需要声明一遍就可以直接传过来了。

目前还是没办法做到的。

我一开始也想在 llm.el 的基础上开发。但是 llm.el 只支持 chat API,是不支持 completion API ,也就是没办法用 deepseek,codestral,还有一些 ollama 模型。 所以没办法,我只能从头从 curl request 开始写。既然 completion API 已经是自己写的 curl request 了,那么 chat API 也没必要调用 llm 了,干脆也自己写得了。

但是 provider 的声明语法写一个非常简单的函数应该就可以迁移 provider 的声明完成复用。

2 个赞

确实,搜索了一下看到你们的讨论了

如果是这样的话,可以实现一个更好的 llm.el 了。 就叫 better-llm.el :smile: