feat(provider): support copilot (#381)
* feat(provider): add back support for copilot Signed-off-by: Aaron Pham <contact@aarnphm.xyz> * docs: add acknowledgement Signed-off-by: Aaron Pham <contact@aarnphm.xyz> --------- Signed-off-by: Aaron Pham <contact@aarnphm.xyz>
This commit is contained in:
parent
d82ef342c4
commit
483f71dba4
10
README.md
10
README.md
@ -41,6 +41,7 @@ https://github.com/user-attachments/assets/86140bfd-08b4-483d-a887-1b701d9e37dd
|
||||
"MunifTanjim/nui.nvim",
|
||||
--- The below dependencies are optional,
|
||||
"nvim-tree/nvim-web-devicons", -- or echasnovski/mini.icons
|
||||
"zbirenbaum/copilot.lua", -- for providers='copilot'
|
||||
{
|
||||
-- support for image pasting
|
||||
"HakonHarnes/img-clip.nvim",
|
||||
@ -86,6 +87,7 @@ Plug 'MunifTanjim/nui.nvim'
|
||||
" Optional deps
|
||||
Plug 'nvim-tree/nvim-web-devicons' "or Plug 'echasnovski/mini.icons'
|
||||
Plug 'HakonHarnes/img-clip.nvim'
|
||||
Plug 'zbirenbaum/copilot.lua'
|
||||
|
||||
" Yay
|
||||
Plug 'yetone/avante.nvim'
|
||||
@ -110,12 +112,14 @@ add({
|
||||
},
|
||||
})
|
||||
--- optional
|
||||
add({ source = 'zbirenbaum/copilot.lua' })
|
||||
add({ source = 'HakonHarnes/img-clip.nvim' })
|
||||
add({ source = 'MeanderingProgrammer/render-markdown.nvim' })
|
||||
|
||||
later(function() require('render-markdown').setup({...}) end)
|
||||
later(function()
|
||||
require('img-clip').setup({...}) -- config img-clip
|
||||
require("copilot").setup({...}) -- setup copilot to your liking
|
||||
require("avante").setup({...}) -- config for avante.nvim
|
||||
end)
|
||||
|
||||
@ -134,6 +138,9 @@ end)
|
||||
require('img-clip').setup ({
|
||||
-- use recommended settings from above
|
||||
})
|
||||
require('copilot').setup ({
|
||||
-- use recommended settings from above
|
||||
})
|
||||
require('render-markdown').setup ({
|
||||
-- use recommended settings from above
|
||||
})
|
||||
@ -171,7 +178,7 @@ _See [config.lua#L9](./lua/avante/config.lua) for the full config_
|
||||
```lua
|
||||
{
|
||||
---@alias Provider "openai" | "claude" | "azure" | "cohere" | [string]
|
||||
provider = "claude", -- Only recommend using Claude
|
||||
provider = "claude", -- Recommend using Claude
|
||||
claude = {
|
||||
endpoint = "https://api.anthropic.com",
|
||||
model = "claude-3-5-sonnet-20240620",
|
||||
@ -343,6 +350,7 @@ We would like to express our heartfelt gratitude to the contributors of the foll
|
||||
| [git-conflict.nvim](https://github.com/akinsho/git-conflict.nvim) | No License | Diff comparison functionality | https://github.com/yetone/avante.nvim/blob/main/lua/avante/diff.lua |
|
||||
| [ChatGPT.nvim](https://github.com/jackMort/ChatGPT.nvim) | Apache 2.0 License | Calculation of tokens count | https://github.com/yetone/avante.nvim/blob/main/lua/avante/utils/tokens.lua |
|
||||
| [img-clip.nvim](https://github.com/HakonHarnes/img-clip.nvim) | MIT License | Clipboard image support | https://github.com/yetone/avante.nvim/blob/main/lua/avante/clipboard.lua |
|
||||
| [copilot.lua](https://github.com/zbirenbaum/copilot.lua) | MIT License | Copilot support | https://github.com/yetone/avante.nvim/blob/main/lua/avante/providers/copilot.lua |
|
||||
|
||||
The high quality and ingenuity of these projects' source code have been immensely beneficial throughout our development process. We extend our sincere thanks and respect to the authors and contributors of these projects. It is the selfless dedication of the open-source community that drives projects like avante.nvim forward.
|
||||
|
||||
|
@ -21,6 +21,16 @@ M.defaults = {
|
||||
max_tokens = 4096,
|
||||
["local"] = false,
|
||||
},
|
||||
---@type AvanteSupportedProvider
|
||||
copilot = {
|
||||
endpoint = "https://api.githubcopilot.com",
|
||||
model = "gpt-4o-2024-05-13",
|
||||
proxy = nil, -- [protocol://]host[:port] Use this proxy
|
||||
allow_insecure = false, -- Allow insecure server connections
|
||||
timeout = 30000, -- Timeout in milliseconds
|
||||
temperature = 0,
|
||||
max_tokens = 4096,
|
||||
},
|
||||
---@type AvanteAzureProvider
|
||||
azure = {
|
||||
endpoint = "", -- example: "https://<your-resource-name>.openai.azure.com"
|
||||
|
156
lua/avante/providers/copilot.lua
Normal file
156
lua/avante/providers/copilot.lua
Normal file
@ -0,0 +1,156 @@
|
||||
---Reference implementation:
|
||||
---https://github.com/zbirenbaum/copilot.lua/blob/master/lua/copilot/auth.lua config file
|
||||
---https://github.com/zed-industries/zed/blob/ad43bbbf5eda59eba65309735472e0be58b4f7dd/crates/copilot/src/copilot_chat.rs#L272 for authorization
|
||||
---
|
||||
---@class CopilotToken
|
||||
---@field annotations_enabled boolean
|
||||
---@field chat_enabled boolean
|
||||
---@field chat_jetbrains_enabled boolean
|
||||
---@field code_quote_enabled boolean
|
||||
---@field codesearch boolean
|
||||
---@field copilotignore_enabled boolean
|
||||
---@field endpoints {api: string, ["origin-tracker"]: string, proxy: string, telemetry: string}
|
||||
---@field expires_at integer
|
||||
---@field individual boolean
|
||||
---@field nes_enabled boolean
|
||||
---@field prompt_8k boolean
|
||||
---@field public_suggestions string
|
||||
---@field refresh_in integer
|
||||
---@field sku string
|
||||
---@field snippy_load_test_enabled boolean
|
||||
---@field telemetry string
|
||||
---@field token string
|
||||
---@field tracking_id string
|
||||
---@field vsc_electron_fetcher boolean
|
||||
---@field xcode boolean
|
||||
---@field xcode_chat boolean
|
||||
|
||||
local curl = require("plenary.curl")
|
||||
|
||||
local Config = require("avante.config")
|
||||
local Path = require("plenary.path")
|
||||
local Utils = require("avante.utils")
|
||||
local P = require("avante.providers")
|
||||
local O = require("avante.providers").openai
|
||||
|
||||
local H = {}
|
||||
|
||||
---@class OAuthToken
|
||||
---@field user string
|
||||
---@field oauth_token string
|
||||
---
|
||||
---@return string
|
||||
H.get_oauth_token = function()
|
||||
local xdg_config = vim.fn.expand("$XDG_CONFIG_HOME")
|
||||
local os_name = Utils.get_os_name()
|
||||
---@type string
|
||||
local config_dir
|
||||
|
||||
if vim.tbl_contains({ "linux", "darwin" }, os_name) then
|
||||
config_dir = vim.fn.isdirectory(xdg_config) and xdg_config or vim.fn.expand("~/.config")
|
||||
else
|
||||
config_dir = vim.fn.expand("~/AppData/Local")
|
||||
end
|
||||
|
||||
local yason = Path:new(config_dir):joinpath("github-copilot", "hosts.json")
|
||||
if not yason:exists() then
|
||||
error("You must setup copilot with either copilot.lua or copilot.vim", 2)
|
||||
end
|
||||
return vim
|
||||
.iter(
|
||||
---@type table<string, OAuthToken>
|
||||
vim.json.decode(yason:read())
|
||||
)
|
||||
:filter(function(k, _)
|
||||
return k:match("github.com")
|
||||
end)
|
||||
---@param acc {oauth_token: string}
|
||||
:fold({}, function(acc, _, v)
|
||||
acc.oauth_token = v.oauth_token
|
||||
return acc
|
||||
end)
|
||||
.oauth_token
|
||||
end
|
||||
|
||||
H.chat_auth_url = "https://api.github.com/copilot_internal/v2/token"
|
||||
H.chat_completion_url = function(base_url)
|
||||
return Utils.trim(base_url, { prefix = "/" }) .. "/chat/completions"
|
||||
end
|
||||
|
||||
---@class AvanteProviderFunctor
|
||||
local M = {}
|
||||
|
||||
H.refresh_token = function()
|
||||
if not M.state then
|
||||
error("internal initialization error")
|
||||
end
|
||||
|
||||
if
|
||||
not M.state.github_token
|
||||
or (M.state.github_token.expires_at and M.state.github_token.expires_at < math.floor(os.time()))
|
||||
then
|
||||
curl.get(H.chat_auth_url, {
|
||||
headers = {
|
||||
["Authorization"] = "token " .. M.state.oauth_token,
|
||||
["Accept"] = "application/json",
|
||||
},
|
||||
timeout = Config.copilot.timeout,
|
||||
proxy = Config.copilot.proxy,
|
||||
insecure = Config.copilot.allow_insecure,
|
||||
on_error = function(err)
|
||||
error("Failed to get response: " .. vim.inspect(err))
|
||||
end,
|
||||
callback = function(output)
|
||||
M.state.github_token = vim.json.decode(output.body)
|
||||
if not vim.g.avante_login then
|
||||
vim.g.avante_login = true
|
||||
end
|
||||
end,
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
---@private
|
||||
---@class AvanteCopilotState
|
||||
---@field oauth_token string
|
||||
---@field github_token CopilotToken?
|
||||
M.state = nil
|
||||
|
||||
M.api_key_name = P.AVANTE_INTERNAL_KEY
|
||||
|
||||
M.parse_message = O.parse_message
|
||||
M.parse_response = O.parse_response
|
||||
|
||||
M.parse_curl_args = function(provider, code_opts)
|
||||
H.refresh_token()
|
||||
|
||||
local base, body_opts = P.parse_config(provider)
|
||||
|
||||
return {
|
||||
url = H.chat_completion_url(base.endpoint),
|
||||
timeout = base.timeout,
|
||||
proxy = base.proxy,
|
||||
insecure = base.allow_insecure,
|
||||
headers = {
|
||||
["Content-Type"] = "application/json",
|
||||
["Authorization"] = "Bearer " .. M.state.github_token.token,
|
||||
["Copilot-Integration-Id"] = "vscode-chat",
|
||||
["Editor-Version"] = ("Neovim/%s.%s.%s"):format(vim.version().major, vim.version().minor, vim.version().patch),
|
||||
},
|
||||
body = vim.tbl_deep_extend("force", {
|
||||
model = base.model,
|
||||
messages = M.parse_message(code_opts),
|
||||
stream = true,
|
||||
}, body_opts),
|
||||
}
|
||||
end
|
||||
|
||||
M.setup = function()
|
||||
if not M.state then
|
||||
M.state = { github_token = nil, oauth_token = H.get_oauth_token() }
|
||||
H.refresh_token()
|
||||
end
|
||||
vim.g.avante_login = true
|
||||
end
|
||||
|
||||
return M
|
@ -370,12 +370,6 @@ end
|
||||
---@return AvanteProviderFunctor
|
||||
M.get_config = function(provider)
|
||||
provider = provider or Config.provider
|
||||
if provider == "copilot" then
|
||||
Utils.error(
|
||||
"Sorry! We no longer support the copilot provider! Please use other providers!",
|
||||
{ once = true, title = "Avante" }
|
||||
)
|
||||
end
|
||||
local cur = Config.get_provider(provider)
|
||||
return type(cur) == "function" and cur() or cur
|
||||
end
|
||||
|
Loading…
x
Reference in New Issue
Block a user