Skip to content

Commit

Permalink
New way to get openai api key (tests included) (#41)
Browse files Browse the repository at this point in the history
* First draft with tests

* Fix formating in config.lua

* Add gh actions, fix tests, minor changes

* Test badge

* Fix badges

* Change badge to main branch of Bryley repo

* Comment out api-key.get function from custom config
  • Loading branch information
mandos authored Jul 12, 2023
1 parent b90180e commit 248c200
Show file tree
Hide file tree
Showing 15 changed files with 424 additions and 39 deletions.
28 changes: 28 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: CI
on:
push:
pull_request:

jobs:
tests:
strategy:
matrix:
# os: [ubuntu-latest, windows-latest]
os: [ubuntu-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- name: Install Neovim
shell: bash
run: |
mkdir -p /tmp/nvim
wget -q https://github.com/neovim/neovim/releases/download/nightly/nvim.appimage -O /tmp/nvim/nvim.appimage
cd /tmp/nvim
chmod a+x ./nvim.appimage
./nvim.appimage --appimage-extract
echo "/tmp/nvim/squashfs-root/usr/bin/" >> $GITHUB_PATH
- name: Run Tests
run: |
nvim --version
[ ! -d tests ] && exit 0
nvim --headless -u tests/init.lua -c "PlenaryBustedDirectory tests/ {minimal_init = 'tests/init.lua', sequential = true}"
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/.tests
60 changes: 47 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
![NeoAI tests](https://github.com/Bryley/neoai.nvim/actions/workflows/main.yml/badge.svg)
# NeoAI
NeoAI is a Neovim plugin that brings the power of OpenAI's GPT-4 directly to
your editor. It helps you generate code, rewrite text, and even get suggestions
Expand Down Expand Up @@ -60,6 +61,24 @@ For packer:
use ({
"Bryley/neoai.nvim",
require = { "MunifTanjim/nui.nvim" },
cmd = {
"NeoAI",
"NeoAIOpen",
"NeoAIClose",
"NeoAIToggle",
"NeoAIContext",
"NeoAIContextOpen",
"NeoAIContextClose",
"NeoAIInject",
"NeoAIInjectCode",
"NeoAIInjectContext",
"NeoAIInjectContextCode",
},
config = function()
require("neoai").setup({
-- Options go here
})
end,
})


Expand Down Expand Up @@ -146,24 +165,23 @@ monitored through [this link](https://platform.openai.com/account/usage)


## Setup
To set up the plugin, add the following to your `init.vim` or `.vimrc` (or put
under the `config` option if using lazy.nvim:
To set up the plugin, add the following code with default values to your `init.lua` (or put
under the `config` option if using lazy.nvim or packer.nvim.

```lua
lua << EOF
require('neoai').setup{
require("neoai").setup({
-- Below are the default options, feel free to override what you would like changed
ui = {
output_popup_text = "NeoAI",
input_popup_text = "Prompt",
width = 30, -- As percentage eg. 30%
width = 30, -- As percentage eg. 30%
output_popup_height = 80, -- As percentage eg. 80%
submit = "<Enter>", -- Key binding to submit the prompt
},
models = {
{
name = "openai",
model = "gpt-3.5-turbo"
model = "gpt-3.5-turbo",
params = nil,
},
},
Expand All @@ -188,7 +206,22 @@ require('neoai').setup{
["select_up"] = "<C-k>",
["select_down"] = "<C-j>",
},
open_api_key_env = "OPENAI_API_KEY",
open_ai = {
api_key = {
env = "OPENAI_API_KEY",
value = nil,
-- `get` is is a function that retrieves an API key, can be used to override the default method.
-- get = function() ... end

-- Here is some code for a function that retrieves an API key. You can use it with
-- the Linux 'pass' application.
-- get = function()
-- local key = vim.fn.system("pass show openai/mytestkey")
-- key = string.gsub(key, "\n", "")
-- return key
-- end,
},
},
shortcuts = {
{
name = "textify",
Expand All @@ -208,7 +241,7 @@ require('neoai').setup{
key = "<leader>ag",
desc = "generate git commit message",
use_context = false,
prompt = function ()
prompt = function()
return [[
Using the following git diff generate a consise and
clear git commit message, with a short title summary
Expand All @@ -219,8 +252,7 @@ require('neoai').setup{
strip_function = nil,
},
},
}
EOF
})
```

### Options
Expand Down Expand Up @@ -267,9 +299,11 @@ context_prompt = function(context)
end
```

### OpenAI API Key
- `open_api_key_env`: The environment variable that contains the OpenAI API key (default: "OPENAI_API_KEY").

### OpenAI Options:
- `open_api_key_env` (deprecated, use `api_key.env` instead): The environment variable containing the OpenAI API key. The default value is "OPENAI_API_KEY ".
- `api_key.env`: The environment variable containing the OpenAI API key. The default value is "OPENAI_API_KEY".
- `api_key.value`: The OpenAI API key, which takes precedence over `api_key .env`.
- `api_key.get`: A function that retrieves the OpenAI API key. For an example implementation, refer to the [Setup](#Setup) section. It has the higher precedence.

### Mappings
- `mappings`: A table containing the following actions that can be keys:
Expand Down
9 changes: 5 additions & 4 deletions lua/neoai/chat/models/openai.lua
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@ local M = {}

M.name = "OpenAI"

local chunks = {}
M._chunks = {}
local raw_chunks = {}

M.get_current_output = function()
return table.concat(chunks, "")
return table.concat(M._chunks, "")
end

---@param chunk string
Expand Down Expand Up @@ -42,7 +43,7 @@ M._recieve_chunk = function(chunk, on_stdout_chunk)
end
on_stdout_chunk(path)
-- append_to_output(path, 0)
table.insert(chunks, path)
table.insert(M._chunks, path)
::continue::
end
end
Expand All @@ -51,7 +52,7 @@ end
---@param on_stdout_chunk fun(chunk: string) Function to call whenever a stdout chunk occurs
---@param on_complete fun(err?: string, output?: string) Function to call when model has finished
M.send_to_model = function(chat_history, on_stdout_chunk, on_complete)
local api_key = os.getenv(config.options.open_api_key_env)
local api_key = config.options.open_ai.api_key.get()

local data = {
model = chat_history.model,
Expand Down
48 changes: 46 additions & 2 deletions lua/neoai/config.lua
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
local logger = require("neoai.logger")
---@class Config
---@field options Options
---@field setup function
---@field get_defaults function
local M = {}

---Get default options
Expand All @@ -7,7 +12,7 @@ M.get_defaults = function()
ui = {
output_popup_text = "NeoAI",
input_popup_text = "Prompt",
width = 30, -- As percentage eg. 30%
width = 30, -- As percentage eg. 30%
output_popup_height = 80, -- As percentage eg. 80%
submit = "<Enter>",
},
Expand Down Expand Up @@ -39,7 +44,35 @@ M.get_defaults = function()
["select_up"] = "<C-k>",
["select_down"] = "<C-j>",
},
open_api_key_env = "OPENAI_API_KEY",
open_ai = {
api_key = {
env = "OPENAI_API_KEY",
value = nil,
get = function()
local open_api_key = nil
if M.options.open_ai.api_key.value then
open_api_key = M.options.open_ai.api_key.value
else
local env_name
if M.options.open_api_key_env then
env_name = M.options.open_api_key_env
logger.deprecation("config.open_api_key_env", "config.open_ai.api_key.env")
else
env_name = M.options.open_ai.api_key.env
end
open_api_key = os.getenv(env_name)
end

if open_api_key then
return open_api_key
end
local msg = M.options.open_ai.api_key.env
.. " environment variable is not set, and open_api_key.value is empty"
logger.error(msg)
error(msg)
end,
},
},
shortcuts = {
{
name = "textify",
Expand Down Expand Up @@ -100,6 +133,14 @@ end
---@field modes ("n" | "v")[] A list of modes to set the keybind up for "n" for normal, "v" for visual
---@field strip_function (fun(output: string): string) | nil The strip function to use

---@class Open_AI_Options
---@field api_key Open_AI_Key_Options The open api key options

---@class Open_AI_Key_Options
---@field env string The environment variable to get the open api key from
---@field value string | nil The value of the open api key to use, if nil then use the environment variable
---@field get fun(): string The function to get the open api key

---@class Options
---@field ui UI_Options UI configurations
---@field model string The OpenAI model to use by default @depricated
Expand All @@ -108,15 +149,18 @@ end
---@field inject Inject_Options The inject options
---@field prompts Prompt_Options The custom prompt options
---@field open_api_key_env string The environment variable that contains the openai api key
---@field open_ai Open_AI_Options The open api key options
---@field mappings table<"select_up" | "select_down", nil|string|string[]> A table of actions with it's mapping(s)
---@field shortcuts Shortcut[] Array of shortcuts
M.options = {}

---Setup options
---@param options Options | nil
---@return Config
M.setup = function(options)
options = options or {}
M.options = vim.tbl_deep_extend("force", {}, M.get_defaults(), options)
return M
end

return M
36 changes: 36 additions & 0 deletions lua/neoai/logger.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
--- Logger module for NeoAI
-- @module neoai.logger

local M = {}

M.deprecation = function(what, instead)
vim.notify(what .. " is deprecated, use " .. instead, vim.log.levels.WARN, {
title = "NeoAI",
})
end

M.debug = function(message)
vim.notify(message, vim.log.levels.DEBUG, {
title = "NeoAI",
})
end

M.info = function(message)
vim.notify(message, vim.log.levels.INFO, {
title = "NeoAI",
})
end

M.warning = function(message)
vim.notify(message, vim.log.levels.WARN, {
title = "NeoAI",
})
end

M.error = function(message)
vim.notify(message, vim.log.levels.ERROR, {
title = "NeoAI",
})
end

return M
6 changes: 3 additions & 3 deletions lua/neoai/utils.lua
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ end

---@param output string
M.save_to_registers = function(output)
for register, strip_func in pairs(config.options.register_output) do
vim.fn.setreg(register, strip_func(output))
end
for register, strip_func in pairs(config.options.register_output) do
vim.fn.setreg(register, strip_func(output))
end
end

---Executes command getting stdout chunks
Expand Down
Loading

0 comments on commit 248c200

Please sign in to comment.