2024.04.08

ChatGPT Inside Neovim

こんにちは、次世代システム研究室のN.M.です。

TLDR

Neovim is a fork of Vim. It uses Lua for configuration, although it still supports Vim Script. There are many plugins and even many pre-configured distributions available.
ChatGPT.nvim is a Neovim plugin that allows you to interact with the OpenAI Chat GPT API from within Neovim. It provides a simple chat interface that allows you to prompt, ask questions, and perform other tasks.
The plugin is designed to be extensible, so you can add custom prompts and responses.
If you are interested only in code generation, I would recommend looking at Aider, which is a CLI tool that is specifically designed for code generation, and handling integration with git. See my previous blog on Aider.
If you are looking for a general, seamless UX with the OpenAI API, from inside your Neovim editor, ChatGPT.nvim is a good option.

Introduction

The plugin is designed to be simple to use and easy to install. It is also extensible, you can add custom prompts.
In this blog, we will cover how to use it, how to upgrade the model, how to write tests, as well as looking at a failing of the plugin.
We will also look at how to extend the plugin with custom prompts.

Help

As with most plugins, ChatGPT.nvim comes with a help file that explains how to use the plugin. You can view the help by running :h chat-gpt-nvim in Neovim.
While using the plugin, Typing ctrl-h, will show a list of ChatGPT.nvim key shortcut commands, useable from inside ChatGPT.nvim.

Upgrading the model

By default, the plugin uses the gpt-3.5 Turbo model.
The plugin allows you to upgrade the model as shown below:
return {
    "jackMort/ChatGPT.nvim",
    enabled = true,
    event = "VeryLazy",
    config = function()
        require("chatgpt").setup({
            api_key_cmd = "pass show work/openai_api_key",
            openai_params = {
                model = "gpt-4",
            },
            openai_edit_params = {
                model = "gpt-4",
            },
            predefined_chat_gpt_prompts = "file:///" .. vim.loop.os_homedir() .. "/awesome-chatgpt-prompts/prompts.csv",
        })
    end,
    dependencies = {
        "MunifTanjim/nui.nvim",
        "nvim-lua/plenary.nvim",
        "folke/trouble.nvim",
        "nvim-telescope/telescope.nvim",
    },
  lazy = false,
}
Note that the above configuration uses the Lazy Plugin Manager, which is a plugin manager for Neovim that allows you to load plugins on demand. If you are using a different plugin manager, you will need to adjust the configuration accordingly.
After changing the configuration, reload the plugin, with the following vim command (if you’re not using the Lazy plugin manager, substitute your plugin manager’s specific reloading method):
:Lazy reload ChatGPT.nvim

The ChatGPT command

The :ChatGPT command is used to interact with the OpenAI API. Use this command to ask questions, as you would normally do in the ChatGPT web interface.

Editing Code

  • I used the :ChatGPTEditWithInstructions command to edit code.
  • The prompt:
The screen-shot below shows the diff between the original and proposed code, the model used, and the available ChatGPT.nvim commands:
  •     the diff is obtained by typing ctl-d
  • the model used is obtained by typing ctl-o
  • the available ChatGPT.nvim pop-up commands are obtained by typing ctl-h
  • Type ctl-y to accept the suggestion, which causes the ChatGPT.nvim popup to close, and the original buffer to be updated with the new code

From the command line, the new code builds, and runs, showing the ordered insert was successful:

➜ cargo run
   Compiling linked-list v0.1.0 (/Users/me/project/linked-list-chat-gpt-nvim)
    Finished dev [unoptimized + debuginfo] target(s) in 0.08s
     Running `target/debug/linked-list`
ll = LinkedList(Some((1, LinkedList(Some((2, LinkedList(Some((3, LinkedList(Some((4, LinkedList(None)))))))))))))

Add a method to reverse the linked list

The prompt:
A screenshot showing the diff between the original and proposed code ctl-d
  • It builds, and runs, but shows the `reverse` logic is incorrect:
❯ cargo run
   Compiling linked-list v0.1.0 (/Users/me/project/linked-list-chat-gpt-nvim)
    Finished dev [unoptimized + debuginfo] target(s) in 0.33s
     Running `target/debug/linked-list`
ll = LinkedList(Some((1, LinkedList(None))))
Aider was able to successfully add the reverse method, also using the OpenAI 4.0 model.
I suspect that the difference may be due to higher quality prompting used in Aider.

Writing tests

Tests can be created with the :ChatGPTRun add_tests command.
#[derive(Debug, PartialEq)]
pub struct LinkedList<T>(Option<(T, Box<LinkedList<T>>)>);


impl<T: PartialOrd> LinkedList<T> {
    pub fn new() -> Self {
        LinkedList(None)
    }


    pub fn push_front(&mut self, data: T) {
        let t = self.0.take();
        self.0 = Some((data, Box::new(LinkedList(t))));
    }


    pub fn push_back(&mut self, data: T) {
        match self.0 {
            Some((_, ref mut child)) => child.push_back(data),
            None => self.push_front(data),
        }
    }


    pub fn ordered_insert(&mut self, data: T) {
        match self.0 {
            Some((ref mut head, ref mut child)) if *head < data => child.ordered_insert(data),
            _ => self.push_front(data),
        }
    }


    pub fn reverse(&mut self) {
        let mut prev = LinkedList(None);
        while let Some((data, mut next)) = self.0.take() {
            next.0 = prev.0.take();
            prev = LinkedList(Some((data, next)));
        }
        *self = prev;
    }
}


fn main() {
    let mut ll = LinkedList::new();
    ll.push_front(1);
    ll.push_back(2);
    ll.push_back(4);
    ll.ordered_insert(3);
    ll.reverse();


    println!("ll = {:?}", ll);
}


#[cfg(test)]
mod tests {
    use super::*;


    #[test]
    fn test_push_front() {
        let mut ll = LinkedList::new();
        ll.push_front(1);
        assert_eq!(ll, LinkedList(Some((1, Box::new(LinkedList(None))))));
    }


    #[test]
    fn test_push_back() {
        let mut ll = LinkedList::new();
        ll.push_back(1);
        assert_eq!(ll, LinkedList(Some((1, Box::new(LinkedList(None))))));
    }


    #[test]
    fn test_ordered_insert() {
        let mut ll = LinkedList::new();
        ll.push_front(1);
        ll.push_back(3);
        ll.ordered_insert(2);
        assert_eq!(ll, LinkedList(Some((1, Box::new(LinkedList(Some((2, Box::new(LinkedList(Some((3, Box::new(LinkedList(None))))))))))))));
    }


    #[test]
    fn test_reverse() {
        let mut ll = LinkedList::new();
        ll.push_front(1);
        ll.push_back(2);
        ll.push_back(3);
        ll.reverse();
        assert_eq!(ll, LinkedList(Some((3, Box::new(LinkedList(Some((2, Box::new(LinkedList(Some((1, Box::new(LinkedList(None))))))))))))))
    }
}

 

These tests work as expected and correctly expose the bug in the reverse method.

Handling Errors

My hope was, that, since there was already a test for the reverse method, the plugin would be able to generate a fix for the method and pass the test.
The plugin provides a ChatGPTRun fix_code command, which I tried using to fix the reverse method. Unfortunately, the plugin was unable to provide a fix for the reverse method.
I could probably fix the reverse method by providing a more detailed prompt, using the ChatGPT command, but I did not try this, because this would be functionally the same as just using the ChatGPT web interface, and I don’t need to prove that GPT 4.0 can solve this, because I have already confirmed it can, in previous tests.

Awesome Chat GPT Prompts

The ChatGPT.nvim plugin uses Awesome Chat GPT Prompts to provide persona description prompts.
The prompts are stored in a CSV file, and loaded into the plugin using the predefined_chat_gpt_prompts configuration option in the plugin configuration.
I customized my prompts by cloning the repository and editing the CSV file. I then updated the predefined_chat_gpt_prompts configuration option to point to the file on my local machine.
predefined_chat_gpt_prompts = "file:///" .. vim.loop.os_homedir() .. "/awesome-chatgpt-prompts/prompts.csv",
This allowed me to use my custom prompts.
ChatGPT.nvim uses these prompts in the :ChatGPTActAs command, which allows you to choose a persona that the AI should imitate when generating responses.
If you wish to customize the way ChatGPT.nvim generates responses, you can edit the CSV file changing existing prompts or adding your own.

Summary

  • While providing a slightly better UX than Aider due to integration with Neovim, the ChatGPT.nvim plugin was unable to provide a fix for the reverse method, which Aider was able to do. This inferior code generation ability, may be due to differences in the prompts used by the two tools, since I tested both using the same OpenAI 4.0 model.
  • While behind Aider in code generation, ChatGPT.nvim provides broader integration with the OpenAI API, allowing for a ChatGPT-like experience within Neovim.
  • Using the ChatGPT command, along with ChatGPTActAs and many others, makes it easy and fun to interact with the OpenAI API from within Neovim.
  • Not having to context switch between browser and editor is a plus point, possibly allowing for more focus on development.
Perhaps ChatGPT.nvim can replace the ChatGPT web interface, during day-to-day code editing. This should allow, for easier switching between ChatGPT, code, and notes, for Neovim users.
ChatGPT code generation may be useful for simple tasks, falling back to Aider or other AI tools for more complex code generation tasks.

Other Recommended AI Tools

References

次世代システム研究室では、グループ全体のインテグレーションを支援してくれるアーキテクトを募集しています。インフラ設計、構築経験者の方、次世代システム研究室にご興味を持って頂ける方がいらっしゃいましたら、ぜひ募集職種一覧からご応募をお願いします。
  • Twitter
  • Facebook
  • はてなブックマークに追加

グループ研究開発本部の最新情報をTwitterで配信中です。ぜひフォローください。

 
  • AI研究開発室
  • 大阪研究開発グループ

関連記事