Compare commits

..

5 Commits
test ... main

Author SHA1 Message Date
zhangkun9038@dingtalk.com
010b23851b gitignroe 的bug修复
Some checks failed
Lua CI / unit tests (ubuntu-22.04, v0.10.0/nvim-linux64.tar.gz) (push) Has been cancelled
Lua CI / Check Lua style (push) Has been cancelled
Lua CI / Lint Lua (push) Has been cancelled
2025-02-17 00:58:22 +08:00
zhangkun9038@dingtalk.com
24b9daee42 尝试修复 add file 作为上下文时会找不到文件
Some checks are pending
Lua CI / unit tests (ubuntu-22.04, v0.10.0/nvim-linux64.tar.gz) (push) Waiting to run
Lua CI / Check Lua style (push) Waiting to run
Lua CI / Lint Lua (push) Waiting to run
2025-02-17 00:15:56 +08:00
zhangkun9038@dingtalk.com
11c0d13b9a 尝试修复 add file 作为上下文时会找不到文件
Some checks are pending
Lua CI / unit tests (ubuntu-22.04, v0.10.0/nvim-linux64.tar.gz) (push) Waiting to run
Lua CI / Check Lua style (push) Waiting to run
Lua CI / Lint Lua (push) Waiting to run
2025-02-17 00:01:17 +08:00
zhangkun9038@dingtalk.com
a6288b8357 尝试修复 add file 作为上下文时会找不到文件
Some checks are pending
Lua CI / unit tests (ubuntu-22.04, v0.10.0/nvim-linux64.tar.gz) (push) Waiting to run
Lua CI / Check Lua style (push) Waiting to run
Lua CI / Lint Lua (push) Waiting to run
2025-02-16 23:38:46 +08:00
zhangkun9038@dingtalk.com
183bf7d8b0 revert
Some checks failed
Lua CI / unit tests (ubuntu-22.04, v0.10.0/nvim-linux64.tar.gz) (push) Has been cancelled
Lua CI / Check Lua style (push) Has been cancelled
Lua CI / Lint Lua (push) Has been cancelled
2025-02-14 13:04:34 +08:00
24 changed files with 211 additions and 646 deletions

View File

@ -3,6 +3,3 @@ rustflags = ["-C", "link-arg=-undefined", "-C", "link-arg=dynamic_lookup"]
[target.aarch64-apple-darwin]
rustflags = ["-C", "link-arg=-undefined", "-C", "link-arg=dynamic_lookup"]
[target.x86_64-unknown-linux-musl]
rustflags = ["-C", "target-feature=-crt-static"]

View File

@ -60,7 +60,6 @@ For building binary if you wish to build from source, then `cargo` is required.
timeout = 30000, -- timeout in milliseconds
temperature = 0, -- adjust if needed
max_tokens = 4096,
reasoning_effort = "high" -- only supported for "o" models
},
},
-- if you want to build from source then do `make BUILD_FROM_SOURCE=true`
@ -381,44 +380,12 @@ This is achieved by emulating nvim-cmp using blink.compat
```lua
file_selector = {
--- @alias FileSelectorProvider "native" | "fzf" | "mini.pick" | "snacks" | "telescope" | string | fun(params: avante.file_selector.IParams|nil): nil
--- @alias FileSelectorProvider "native" | "fzf" | "mini.pick" | "snacks" | "telescope" | string
provider = "fzf",
-- Options override for custom providers
provider_opts = {},
}
```
To create a customized file_selector, you can specify a customized function to launch a picker to select items and pass the selected items to the `handler` callback.
```lua
file_selector = {
---@param params avante.file_selector.IParams
provider = function(params)
local filepaths = params.filepaths ---@type string[]
local title = params.title ---@type string
local handler = params.handler ---@type fun(selected_filepaths: string[]|nil): nil
-- Launch your customized picker with the items built from `filepaths`, then in the `on_confirm` callback,
-- pass the selected items (convert back to file paths) to the `handler` function.
local items = __your_items_formatter__(filepaths)
__your_picker__({
items = items,
on_cancel = function()
handler(nil)
end,
on_confirm = function(selected_items)
local selected_filepaths = {}
for _, item in ipairs(selected_items) do
table.insert(selected_filepaths, item.filepath)
end
handler(selected_filepaths)
end
})
end,
}
```
Choose a selector other that native, the default as that currently has an issue
For lazyvim users copy the full config for blink.cmp from the website or extend the options
```lua
@ -504,10 +471,8 @@ Given its early stage, `avante.nvim` currently supports the following basic func
> For Amazon Bedrock:
>
> ```sh
> export BEDROCK_KEYS=aws_access_key_id,aws_secret_access_key,aws_region[,aws_session_token]
>
> export BEDROCK_KEYS=aws_access_key_id,aws_secret_access_key,aws_region
> ```
> Note: The aws_session_token is optional and only needed when using temporary AWS credentials
1. Open a code file in Neovim.
2. Use the `:AvanteAsk` command to query the AI about the code.
@ -583,16 +548,15 @@ For more information, see [Custom Providers](https://github.com/yetone/avante.nv
## Web Search Engines
Avante's tools include some web search engines, currently support [tavily](https://tavily.com/), [serpapi](https://serpapi.com/), [searchapi](https://www.searchapi.io/) and google's [programmable search engine](https://developers.google.com/custom-search/v1/overview). The default is tavily, and can be changed through configuring `Config.web_search_engine.provider`:
Avante's tools include some web search engines, currently support [tavily](https://tavily.com/) and [serpapi](https://serpapi.com/). The default is tavily, and can be changed through configuring `Config.web_search_engine.provider`:
```lua
web_search_engine = {
provider = "tavily", -- tavily, serpapi, searchapi or google
provider = "tavily", -- tavily or serpapi
}
```
You need to set the environment variable `TAVILY_API_KEY` , `SERPAPI_API_KEY`, `SEARCHAPI_API_KEY` to use tavily or serpapi or searchapi.
To use google, set the `GOOGLE_SEARCH_API_KEY` as the [API key](https://developers.google.com/custom-search/v1/overview), and `GOOGLE_SEARCH_ENGINE_ID` as the [search engine](https://programmablesearchengine.google.com) ID.
You need to set the environment variable `TAVILY_API_KEY` or `SERPAPI_API_KEY` to use tavily or serpapi.
## Disable Tools

View File

@ -5,11 +5,6 @@
local Utils = require("avante.utils")
---@class avante.file_selector.IParams
---@field public title string
---@field public filepaths string[]
---@field public handler fun(filepaths: string[]|nil): nil
---@class avante.CoreConfig: avante.Config
local M = {}
---@class avante.Config
@ -33,10 +28,11 @@ M._defaults = {
tavily = {
api_key_name = "TAVILY_API_KEY",
extra_request_body = {
time_range = "d",
include_answer = "basic",
},
---@type WebSearchEngineProviderResponseBodyFormatter
format_response_body = function(body) return body.answer, nil end,
format_response_body = function(body) return body.anwser, nil end,
},
serpapi = {
api_key_name = "SERPAPI_API_KEY",
@ -56,65 +52,11 @@ M._defaults = {
title = result.title,
link = result.link,
snippet = result.snippet,
date = result.date,
}
end
)
:take(10)
:totable()
return vim.json.encode(jsn), nil
end
return "", nil
end,
},
searchapi = {
api_key_name = "SEARCHAPI_API_KEY",
extra_request_body = {
engine = "google",
},
---@type WebSearchEngineProviderResponseBodyFormatter
format_response_body = function(body)
if body.answer_box ~= nil then return body.answer_box.result, nil end
if body.organic_results ~= nil then
local jsn = vim
.iter(body.organic_results)
:map(
function(result)
return {
title = result.title,
link = result.link,
snippet = result.snippet,
date = result.date,
}
end
)
:take(10)
:totable()
return vim.json.encode(jsn), nil
end
return "", nil
end,
},
google = {
api_key_name = "GOOGLE_SEARCH_API_KEY",
engine_id_name = "GOOGLE_SEARCH_ENGINE_ID",
extra_request_body = {},
---@type WebSearchEngineProviderResponseBodyFormatter
format_response_body = function(body)
if body.items ~= nil then
local jsn = vim
.iter(body.items)
:map(
function(result)
return {
title = result.title,
link = result.link,
snippet = result.snippet,
}
end
)
:take(10)
:totable()
if #jsn > 5 then jsn = vim.list_slice(jsn, 1, 5) end
return vim.json.encode(jsn), nil
end
return "", nil
@ -314,12 +256,8 @@ M._defaults = {
files = {
add_current = "<leader>ac", -- Add current buffer to selected files
},
providers = {
show_provides = "<leader>ap", -- Show providers selector
},
},
windows = {
---@alias AvantePosition "right" | "left" | "top" | "bottom" | "smart"
position = "right",
wrap = true, -- similar to vim.o.wrap
@ -365,7 +303,7 @@ M._defaults = {
},
--- @class AvanteFileSelectorConfig
file_selector = {
--- @alias FileSelectorProvider "native" | "fzf" | "mini.pick" | "snacks" | "telescope" | string | fun(params: avante.file_selector.IParams|nil): nil
--- @alias FileSelectorProvider "native" | "fzf" | "mini.pick" | "snacks" | "telescope" | string
provider = "native",
-- Options override for custom providers
provider_opts = {},

View File

@ -330,11 +330,6 @@ function FileSelector:show_select_ui()
self:snacks_picker_ui(handler)
elseif Config.file_selector.provider == "telescope" then
self:telescope_ui(handler)
elseif type(Config.file_selector.provider) == "function" then
local title = string.format("%s:", PROMPT_TITLE) ---@type string
local filepaths = self:get_filepaths() ---@type string[]
local params = { title = title, filepaths = filepaths, handler = handler } ---@type avante.file_selector.IParams
Config.file_selector.provider(params)
else
Utils.error("Unknown file selector provider: " .. Config.file_selector.provider)
end
@ -368,9 +363,9 @@ end
function FileSelector:get_selected_files_contents()
local contents = {}
for _, file_path in ipairs(self.selected_filepaths) do
local lines, error = Utils.read_file_from_buf_or_disk(file_path)
local lines, filetype, error = Utils.read_file_from_buf_or_disk(file_path)
lines = lines or {}
local filetype = Utils.get_filetype(file_path)
filetype = filetype or "unknown"
if error ~= nil then
Utils.error("error reading file: " .. error)
else

View File

@ -23,7 +23,9 @@ M.check = function()
end
-- Optional dependencies
if Utils.icons_enabled() then
local has_devicons = Utils.has("nvim-web-devicons")
local has_mini_icons = Utils.has("mini.icons") or Utils.has("mini.nvim")
if has_devicons or has_mini_icons then
H.ok("Found icons plugin (nvim-web-devicons or mini.icons)")
else
H.warn("No icons plugin found (nvim-web-devicons or mini.icons). Icons will not be displayed")

View File

@ -68,11 +68,6 @@ H.keymaps = function()
vim.keymap.set("n", "<Plug>(AvanteToggleHint)", function() M.toggle.hint() end)
vim.keymap.set("n", "<Plug>(AvanteToggleSuggestion)", function() M.toggle.suggestion() end)
vim.keymap.set("n", "<Plug>(AvanteShowProviders)", function()
local sidebar = M.get()
if sidebar then sidebar:show_providers_selector() end
end, { noremap = true, silent = true })
vim.keymap.set({ "n", "v" }, "<Plug>(AvanteConflictOurs)", function() Diff.choose("ours") end)
vim.keymap.set({ "n", "v" }, "<Plug>(AvanteConflictBoth)", function() Diff.choose("both") end)
vim.keymap.set({ "n", "v" }, "<Plug>(AvanteConflictTheirs)", function() Diff.choose("theirs") end)
@ -126,14 +121,6 @@ H.keymaps = function()
function() M.toggle.suggestion() end,
{ desc = "avante: toggle suggestion" }
)
Utils.safe_keymap_set("n", Config.mappings.show_providers, function()
local sidebar = M.get()
if sidebar then sidebar:show_providers_selector() end
end, {
desc = "avante: show providers selector",
noremap = true,
silent = true,
})
Utils.safe_keymap_set("n", Config.mappings.toggle.repomap, function() require("avante.repo_map").show() end, {
desc = "avante: display repo map",
noremap = true,

View File

@ -25,8 +25,8 @@ M.generate_prompts = function(opts)
local Provider = opts.provider or P[Config.provider]
local mode = opts.mode or "planning"
---@type AvanteProviderFunctor | AvanteBedrockProviderFunctor
local _, request_body = P.parse_config(Provider)
local max_tokens = request_body.max_tokens or 4096
local _, body_opts = P.parse_config(Provider)
local max_tokens = body_opts.max_tokens or 4096
-- Check if the instructions contains an image path
local image_paths = {}
@ -449,7 +449,7 @@ M.stream = function(opts)
return original_on_stop(stop_opts)
end)
end
if Config.dual_boost.enabled and opts.mode == "planning" then
if Config.dual_boost.enabled then
M._dual_boost_stream(opts, P[Config.dual_boost.first_provider], P[Config.dual_boost.second_provider])
else
M._stream(opts)

View File

@ -7,7 +7,6 @@ local M = {}
---@param rel_path string
---@return string
local function get_abs_path(rel_path)
if Path:new(rel_path):is_absolute() then return rel_path end
local project_root = Utils.get_project_root()
return Path:new(project_root):joinpath(rel_path):absolute()
end
@ -42,12 +41,13 @@ function M.list_files(opts, on_log)
add_dirs = true,
depth = opts.depth,
})
local filepaths = {}
local result = ""
for _, file in ipairs(files) do
local uniform_path = Utils.uniform_path(file)
table.insert(filepaths, uniform_path)
result = result .. uniform_path .. "\n"
end
return vim.json.encode(filepaths), nil
result = result:gsub("\n$", "")
return result, nil
end
---@param opts { rel_path: string, keyword: string }
@ -62,11 +62,12 @@ function M.search_files(opts, on_log)
local files = Utils.scan_directory_respect_gitignore({
directory = abs_path,
})
local filepaths = {}
local result = ""
for _, file in ipairs(files) do
if file:find(opts.keyword) then table.insert(filepaths, file) end
if file:find(opts.keyword) then result = result .. file .. "\n" end
end
return vim.json.encode(filepaths), nil
result = result:gsub("\n$", "")
return result, nil
end
---@param opts { rel_path: string, keyword: string }
@ -103,9 +104,7 @@ function M.search(opts, on_log)
if on_log then on_log("Running command: " .. cmd) end
local result = vim.fn.system(cmd)
local filepaths = vim.split(result, "\n")
return vim.json.encode(filepaths), nil
return result or "", nil
end
---@param opts { rel_path: string }
@ -184,10 +183,9 @@ function M.rename_file(opts, on_log)
end
---@param opts { rel_path: string, new_rel_path: string }
---@param on_log? fun(log: string): nil
---@return boolean success
---@return string|nil error
function M.copy_file(opts, on_log)
function M.copy_file(opts)
local abs_path = get_abs_path(opts.rel_path)
if not has_permission_to_access(abs_path) then return false, "No permission to access path: " .. abs_path end
if not Path:new(abs_path):exists() then return false, "File not found: " .. abs_path end
@ -195,47 +193,38 @@ function M.copy_file(opts, on_log)
local new_abs_path = get_abs_path(opts.new_rel_path)
if not has_permission_to_access(new_abs_path) then return false, "No permission to access path: " .. new_abs_path end
if Path:new(new_abs_path):exists() then return false, "File already exists: " .. new_abs_path end
if on_log then on_log("Copying file: " .. abs_path .. " to " .. new_abs_path) end
Path:new(new_abs_path):write(Path:new(abs_path):read())
return true, nil
end
---@param opts { rel_path: string }
---@param on_log? fun(log: string): nil
---@return boolean success
---@return string|nil error
function M.delete_file(opts, on_log)
function M.delete_file(opts)
local abs_path = get_abs_path(opts.rel_path)
if not has_permission_to_access(abs_path) then return false, "No permission to access path: " .. abs_path end
if not Path:new(abs_path):exists() then return false, "File not found: " .. abs_path end
if not Path:new(abs_path):is_file() then return false, "Path is not a file: " .. abs_path end
if not M.confirm("Are you sure you want to delete the file: " .. abs_path) then return false, "User canceled" end
if on_log then on_log("Deleting file: " .. abs_path) end
os.remove(abs_path)
return true, nil
end
---@param opts { rel_path: string }
---@param on_log? fun(log: string): nil
---@return boolean success
---@return string|nil error
function M.create_dir(opts, on_log)
function M.create_dir(opts)
local abs_path = get_abs_path(opts.rel_path)
if not has_permission_to_access(abs_path) then return false, "No permission to access path: " .. abs_path end
if Path:new(abs_path):exists() then return false, "Directory already exists: " .. abs_path end
if not M.confirm("Are you sure you want to create the directory: " .. abs_path) then
return false, "User canceled"
end
if on_log then on_log("Creating directory: " .. abs_path) end
Path:new(abs_path):mkdir({ parents = true })
return true, nil
end
---@param opts { rel_path: string, new_rel_path: string }
---@param on_log? fun(log: string): nil
---@return boolean success
---@return string|nil error
function M.rename_dir(opts, on_log)
function M.rename_dir(opts)
local abs_path = get_abs_path(opts.rel_path)
if not has_permission_to_access(abs_path) then return false, "No permission to access path: " .. abs_path end
if not Path:new(abs_path):exists() then return false, "Directory not found: " .. abs_path end
@ -246,16 +235,14 @@ function M.rename_dir(opts, on_log)
if not M.confirm("Are you sure you want to rename directory " .. abs_path .. " to " .. new_abs_path .. "?") then
return false, "User canceled"
end
if on_log then on_log("Renaming directory: " .. abs_path .. " to " .. new_abs_path) end
os.rename(abs_path, new_abs_path)
return true, nil
end
---@param opts { rel_path: string }
---@param on_log? fun(log: string): nil
---@return boolean success
---@return string|nil error
function M.delete_dir(opts, on_log)
function M.delete_dir(opts)
local abs_path = get_abs_path(opts.rel_path)
if not has_permission_to_access(abs_path) then return false, "No permission to access path: " .. abs_path end
if not Path:new(abs_path):exists() then return false, "Directory not found: " .. abs_path end
@ -263,7 +250,6 @@ function M.delete_dir(opts, on_log)
if not M.confirm("Are you sure you want to delete the directory: " .. abs_path) then
return false, "User canceled"
end
if on_log then on_log("Deleting directory: " .. abs_path) end
os.remove(abs_path)
return true, nil
end
@ -340,45 +326,6 @@ function M.web_search(opts, on_log)
if resp.status ~= 200 then return nil, "Error: " .. resp.body end
local jsn = vim.json.decode(resp.body)
return search_engine.format_response_body(jsn)
elseif provider_type == "searchapi" then
local query_params = vim.tbl_deep_extend("force", {
api_key = api_key,
q = opts.query,
}, search_engine.extra_request_body)
local query_string = ""
for key, value in pairs(query_params) do
query_string = query_string .. key .. "=" .. vim.uri_encode(value) .. "&"
end
local resp = curl.get("https://searchapi.io/api/v1/search?" .. query_string, {
headers = {
["Content-Type"] = "application/json",
},
})
if resp.status ~= 200 then return nil, "Error: " .. resp.body end
local jsn = vim.json.decode(resp.body)
return search_engine.format_response_body(jsn)
elseif provider_type == "google" then
local engine_id = os.getenv(search_engine.engine_id_name)
if engine_id == nil or engine_id == "" then
return nil, "Environment variable " .. search_engine.engine_id_namee .. " is not set"
end
local query_params = vim.tbl_deep_extend("force", {
key = api_key,
cx = engine_id,
q = opts.query,
}, search_engine.extra_request_body)
local query_string = ""
for key, value in pairs(query_params) do
query_string = query_string .. key .. "=" .. vim.uri_encode(value) .. "&"
end
local resp = curl.get("https://www.googleapis.com/customsearch/v1?" .. query_string, {
headers = {
["Content-Type"] = "application/json",
},
})
if resp.status ~= 200 then return nil, "Error: " .. resp.body end
local jsn = vim.json.decode(resp.body)
return search_engine.format_response_body(jsn)
end
end

View File

@ -18,34 +18,31 @@ M.parse_response = O.parse_response
M.parse_response_without_stream = O.parse_response_without_stream
M.parse_curl_args = function(provider, prompt_opts)
local provider_conf, request_body = P.parse_config(provider)
local base, body_opts = P.parse_config(provider)
local headers = {
["Content-Type"] = "application/json",
}
if P.env.require_api_key(provider_conf) then headers["api-key"] = provider.parse_api_key() end
if P.env.require_api_key(base) then headers["api-key"] = provider.parse_api_key() end
-- NOTE: When using "o" series set the supported parameters only
if O.is_o_series_model(provider_conf.model) then
request_body.max_tokens = nil
request_body.temperature = 1
if O.is_o_series_model(base.model) then
body_opts.max_tokens = nil
body_opts.temperature = 1
end
return {
url = Utils.url_join(
provider_conf.endpoint,
"/openai/deployments/"
.. provider_conf.deployment
.. "/chat/completions?api-version="
.. provider_conf.api_version
base.endpoint,
"/openai/deployments/" .. base.deployment .. "/chat/completions?api-version=" .. base.api_version
),
proxy = provider_conf.proxy,
insecure = provider_conf.allow_insecure,
proxy = base.proxy,
insecure = base.allow_insecure,
headers = headers,
body = vim.tbl_deep_extend("force", {
messages = M.parse_messages(prompt_opts),
stream = true,
}, request_body),
}, body_opts),
}
end

View File

@ -1,4 +1,5 @@
local Utils = require("avante.utils")
local Clipboard = require("avante.clipboard")
local P = require("avante.providers")
---@alias AvanteBedrockPayloadBuilder fun(prompt_opts: AvantePromptOptions, body_opts: table<string, any>): table<string, any>
@ -16,14 +17,17 @@ M.api_key_name = "BEDROCK_KEYS"
M.use_xml_format = true
M.load_model_handler = function()
local provider_conf, _ = P.parse_config(P["bedrock"])
local bedrock_model = provider_conf.model
if provider_conf.model:match("anthropic") then bedrock_model = "claude" end
local base, _ = P.parse_config(P["bedrock"])
local bedrock_model = base.model
if base.model:match("anthropic") then bedrock_model = "claude" end
local ok, model_module = pcall(require, "avante.providers.bedrock." .. bedrock_model)
if ok then return model_module end
local error_msg = "Bedrock model handler not found: " .. bedrock_model
error(error_msg)
if ok then
return model_module
else
local error_msg = "Bedrock model handler not found: " .. bedrock_model
Utils.error(error_msg, { once = true, title = "Avante" })
end
end
M.parse_response = function(ctx, data_stream, event_state, opts)
@ -42,8 +46,8 @@ M.parse_stream_data = function(data, opts)
-- The `type` field in the decoded JSON determines how the response is handled.
local bedrock_match = data:gmatch("event(%b{})")
for bedrock_data_match in bedrock_match do
local jsn = vim.json.decode(bedrock_data_match)
local data_stream = vim.base64.decode(jsn.bytes)
local data = vim.json.decode(bedrock_data_match)
local data_stream = vim.base64.decode(data.bytes)
local json = vim.json.decode(data_stream)
M.parse_response({}, data_stream, json.type, opts)
end
@ -56,12 +60,10 @@ M.parse_curl_args = function(provider, prompt_opts)
local base, body_opts = P.parse_config(provider)
local api_key = provider.parse_api_key()
if api_key == nil then error("Cannot get the bedrock api key!") end
local parts = vim.split(api_key, ",")
local aws_access_key_id = parts[1]
local aws_secret_access_key = parts[2]
local aws_region = parts[3]
local aws_session_token = parts[4]
local endpoint = string.format(
"https://bedrock-runtime.%s.amazonaws.com/model/%s/invoke-with-response-stream",
@ -73,8 +75,6 @@ M.parse_curl_args = function(provider, prompt_opts)
["Content-Type"] = "application/json",
}
if aws_session_token and aws_session_token ~= "" then headers["x-amz-security-token"] = aws_session_token end
local body_payload = M.build_bedrock_payload(prompt_opts, body_opts)
local rawArgs = {
@ -105,6 +105,7 @@ M.on_error = function(result)
end
local error_msg = body.error.message
local error_type = body.error.type
Utils.error(error_msg, { once = true, title = "Avante" })
end

View File

@ -226,7 +226,7 @@ end
---@param prompt_opts AvantePromptOptions
---@return table
M.parse_curl_args = function(provider, prompt_opts)
local provider_conf, request_body = P.parse_config(provider)
local base, body_opts = P.parse_config(provider)
local headers = {
["Content-Type"] = "application/json",
@ -234,7 +234,7 @@ M.parse_curl_args = function(provider, prompt_opts)
["anthropic-beta"] = "prompt-caching-2024-07-31",
}
if P.env.require_api_key(provider_conf) then headers["x-api-key"] = provider.parse_api_key() end
if P.env.require_api_key(base) then headers["x-api-key"] = provider.parse_api_key() end
local messages = M.parse_messages(prompt_opts)
@ -246,12 +246,12 @@ M.parse_curl_args = function(provider, prompt_opts)
end
return {
url = Utils.url_join(provider_conf.endpoint, "/v1/messages"),
proxy = provider_conf.proxy,
insecure = provider_conf.allow_insecure,
url = Utils.url_join(base.endpoint, "/v1/messages"),
proxy = base.proxy,
insecure = base.allow_insecure,
headers = headers,
body = vim.tbl_deep_extend("force", {
model = provider_conf.model,
model = base.model,
system = {
{
type = "text",
@ -262,7 +262,7 @@ M.parse_curl_args = function(provider, prompt_opts)
messages = messages,
tools = tools,
stream = true,
}, request_body),
}, body_opts),
}
end

View File

@ -70,7 +70,7 @@ M.parse_stream_data = function(data, opts)
end
M.parse_curl_args = function(provider, prompt_opts)
local provider_conf, request_body = P.parse_config(provider)
local base, body_opts = P.parse_config(provider)
local headers = {
["Accept"] = "application/json",
@ -82,17 +82,17 @@ M.parse_curl_args = function(provider, prompt_opts)
.. "."
.. vim.version().patch,
}
if P.env.require_api_key(provider_conf) then headers["Authorization"] = "Bearer " .. provider.parse_api_key() end
if P.env.require_api_key(base) then headers["Authorization"] = "Bearer " .. provider.parse_api_key() end
return {
url = Utils.url_join(provider_conf.endpoint, "/chat"),
proxy = provider_conf.proxy,
insecure = provider_conf.allow_insecure,
url = Utils.url_join(base.endpoint, "/chat"),
proxy = base.proxy,
insecure = base.allow_insecure,
headers = headers,
body = vim.tbl_deep_extend("force", {
model = provider_conf.model,
model = base.model,
stream = true,
}, M.parse_messages(prompt_opts), request_body),
}, M.parse_messages(prompt_opts), body_opts),
}
end

View File

@ -249,7 +249,7 @@ M.parse_curl_args = function(provider, prompt_opts)
-- (this should rarely happen, as we refresh the token in the background)
H.refresh_token(false, false)
local provider_conf, request_body = P.parse_config(provider)
local base, body_opts = P.parse_config(provider)
local tools = {}
if prompt_opts.tools then
@ -259,10 +259,10 @@ M.parse_curl_args = function(provider, prompt_opts)
end
return {
url = H.chat_completion_url(provider_conf.endpoint),
timeout = provider_conf.timeout,
proxy = provider_conf.proxy,
insecure = provider_conf.allow_insecure,
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,
@ -270,11 +270,11 @@ M.parse_curl_args = function(provider, prompt_opts)
["Editor-Version"] = ("Neovim/%s.%s.%s"):format(vim.version().major, vim.version().minor, vim.version().patch),
},
body = vim.tbl_deep_extend("force", {
model = provider_conf.model,
model = base.model,
messages = M.parse_messages(prompt_opts),
stream = true,
tools = tools,
}, request_body),
}, body_opts),
}
end

View File

@ -82,29 +82,26 @@ M.parse_response = function(ctx, data_stream, _, opts)
end
M.parse_curl_args = function(provider, prompt_opts)
local provider_conf, request_body = P.parse_config(provider)
local base, body_opts = P.parse_config(provider)
request_body = vim.tbl_deep_extend("force", request_body, {
body_opts = vim.tbl_deep_extend("force", body_opts, {
generationConfig = {
temperature = request_body.temperature,
maxOutputTokens = request_body.max_tokens,
temperature = body_opts.temperature,
maxOutputTokens = body_opts.max_tokens,
},
})
request_body.temperature = nil
request_body.max_tokens = nil
body_opts.temperature = nil
body_opts.max_tokens = nil
local api_key = provider.parse_api_key()
if api_key == nil then error("Cannot get the gemini api key!") end
return {
url = Utils.url_join(
provider_conf.endpoint,
provider_conf.model .. ":streamGenerateContent?alt=sse&key=" .. api_key
),
proxy = provider_conf.proxy,
insecure = provider_conf.allow_insecure,
url = Utils.url_join(base.endpoint, base.model .. ":streamGenerateContent?alt=sse&key=" .. api_key),
proxy = base.proxy,
insecure = base.allow_insecure,
headers = { ["Content-Type"] = "application/json" },
body = vim.tbl_deep_extend("force", {}, M.parse_messages(prompt_opts), request_body),
body = vim.tbl_deep_extend("force", {}, M.parse_messages(prompt_opts), body_opts),
}
end

View File

@ -64,7 +64,6 @@ local DressingState = { winid = nil, input_winid = nil, input_bufnr = nil }
---@field __inherited_from? string
---@field temperature? number
---@field max_tokens? number
---@field reasoning_effort? string
---
---@class AvanteLLMUsage
---@field input_tokens number
@ -348,9 +347,9 @@ M = setmetatable(M, {
if t[k].has == nil then t[k].has = function() return E.parse_envvar(t[k]) ~= nil end end
if t[k].setup == nil then
local provider_conf = M.parse_config(t[k])
local base = M.parse_config(t[k])
t[k].setup = function()
if E.require_api_key(provider_conf) then t[k].parse_api_key() end
if E.require_api_key(base) then t[k].parse_api_key() end
require("avante.tokenizers").setup(t[k].tokenizer_id)
end
end

View File

@ -223,7 +223,7 @@ M.parse_response = function(ctx, data_stream, _, opts)
end
ctx.last_think_content = choice.delta.reasoning
opts.on_chunk(choice.delta.reasoning)
elseif choice.delta.tool_calls and choice.delta.tool_calls ~= vim.NIL then
elseif choice.delta.tool_calls then
local tool_call = choice.delta.tool_calls[1]
if not ctx.tool_use_list then ctx.tool_use_list = {} end
if not ctx.tool_use_list[tool_call.index + 1] then
@ -272,14 +272,13 @@ end
local Log = require("avante.utils.log")
M.parse_curl_args = function(provider, prompt_opts)
local provider_conf, request_body = P.parse_config(provider)
local disable_tools = provider_conf.disable_tools or false
local base, body_opts = P.parse_config(provider)
local disable_tools = base.disable_tools or false
local headers = {
["Content-Type"] = "application/json",
}
<<<<<<< HEAD
-- Add appid header for baidu provider
if Config.provider == "baidu" then
local baidu_config = Config.get_provider("baidu")
@ -290,9 +289,6 @@ M.parse_curl_args = function(provider, prompt_opts)
end
if P.env.require_api_key(base) then
=======
if P.env.require_api_key(provider_conf) then
>>>>>>> b6ae4dfe7fe443362f5f31d71797173ec12c2598
local api_key = provider.parse_api_key()
if api_key == nil then
error(Config.provider .. " API key is not set, please set it in your environment variable or config file")
@ -300,18 +296,18 @@ M.parse_curl_args = function(provider, prompt_opts)
headers["Authorization"] = "Bearer " .. api_key
end
if M.is_openrouter(provider_conf.endpoint) then
if M.is_openrouter(base.endpoint) then
headers["HTTP-Referer"] = "https://github.com/yetone/avante.nvim"
headers["X-Title"] = "Avante.nvim"
request_body.include_reasoning = true
body_opts.include_reasoning = true
end
-- NOTE: When using "o" series set the supported parameters only
local stream = true
if M.is_o_series_model(provider_conf.model) then
request_body.max_completion_tokens = request_body.max_tokens
request_body.max_tokens = nil
request_body.temperature = 1
if M.is_o_series_model(base.model) then
body_opts.max_completion_tokens = body_opts.max_tokens
body_opts.max_tokens = nil
body_opts.temperature = 1
end
local tools = nil
@ -322,20 +318,20 @@ M.parse_curl_args = function(provider, prompt_opts)
end
end
Utils.debug("endpoint", provider_conf.endpoint)
Utils.debug("model", provider_conf.model)
Utils.debug("endpoint", base.endpoint)
Utils.debug("model", base.model)
return {
url = Utils.url_join(provider_conf.endpoint, "/chat/completions"),
proxy = provider_conf.proxy,
insecure = provider_conf.allow_insecure,
local request = {
url = Utils.url_join(base.endpoint, "/chat/completions"),
proxy = base.proxy,
insecure = base.allow_insecure,
headers = headers,
body = vim.tbl_deep_extend("force", {
model = provider_conf.model,
model = base.model,
messages = M.parse_messages(prompt_opts),
stream = stream,
tools = tools,
}, request_body),
}, body_opts),
}
-- 记录请求详细信息
Log.log_request(request.url, request.headers, request.body)

View File

@ -32,22 +32,22 @@ M.parse_api_key = function()
end
M.parse_curl_args = function(provider, prompt_opts)
local provider_conf, request_body = P.parse_config(provider)
local base, body_opts = P.parse_config(provider)
local location = vim.fn.getenv("LOCATION") or "default-location"
local project_id = vim.fn.getenv("PROJECT_ID") or "default-project-id"
local model_id = provider_conf.model or "default-model-id"
local url = provider_conf.endpoint:gsub("LOCATION", location):gsub("PROJECT_ID", project_id)
local model_id = base.model or "default-model-id"
local url = base.endpoint:gsub("LOCATION", location):gsub("PROJECT_ID", project_id)
url = string.format("%s/%s:streamGenerateContent?alt=sse", url, model_id)
request_body = vim.tbl_deep_extend("force", request_body, {
body_opts = vim.tbl_deep_extend("force", body_opts, {
generationConfig = {
temperature = request_body.temperature,
maxOutputTokens = request_body.max_tokens,
temperature = body_opts.temperature,
maxOutputTokens = body_opts.max_tokens,
},
})
request_body.temperature = nil
request_body.max_tokens = nil
body_opts.temperature = nil
body_opts.max_tokens = nil
local bearer_token = M.parse_api_key()
return {
@ -56,9 +56,9 @@ M.parse_curl_args = function(provider, prompt_opts)
["Authorization"] = "Bearer " .. bearer_token,
["Content-Type"] = "application/json; charset=utf-8",
},
proxy = provider_conf.proxy,
insecure = provider_conf.allow_insecure,
body = vim.tbl_deep_extend("force", {}, M.parse_messages(prompt_opts), request_body),
proxy = base.proxy,
insecure = base.allow_insecure,
body = vim.tbl_deep_extend("force", {}, M.parse_messages(prompt_opts), body_opts),
}
end

View File

@ -29,10 +29,21 @@ end
function RepoMap.setup() vim.defer_fn(RepoMap._init_repo_map_lib, 1000) end
function RepoMap.get_ts_lang(filepath)
local filetype = Utils.get_filetype(filepath)
local filetype = RepoMap.get_filetype(filepath)
return filetype_map[filetype] or filetype
end
function RepoMap.get_filetype(filepath)
-- Some files are sometimes not detected correctly when buffer is not included
-- https://github.com/neovim/neovim/issues/27265
local buf = vim.api.nvim_create_buf(false, true)
local filetype = vim.filetype.match({ filename = filepath, buf = buf })
vim.api.nvim_buf_delete(buf, { force = true })
return filetype
end
function RepoMap._build_repo_map(project_root, file_ext)
local output = {}
local gitignore_path = project_root .. "/.gitignore"
@ -59,7 +70,7 @@ function RepoMap._build_repo_map(project_root, file_ext)
if definitions == "" then return end
table.insert(output, {
path = Utils.relative_path(filepath),
lang = Utils.get_filetype(filepath),
lang = RepoMap.get_filetype(filepath),
defs = definitions,
})
end)
@ -131,7 +142,7 @@ function RepoMap._get_repo_map(file_ext)
if not found then
table.insert(repo_map, {
path = Utils.relative_path(abs_filepath),
lang = Utils.get_filetype(abs_filepath),
lang = RepoMap.get_filetype(abs_filepath),
defs = definitions,
})
end

View File

@ -57,7 +57,6 @@ function Sidebar:new(id)
selected_files_container = nil,
input_container = nil,
file_selector = FileSelector:new(id),
is_generating = false,
}, { __index = self })
end
@ -67,24 +66,9 @@ function Sidebar:delete_autocmds()
end
function Sidebar:reset()
-- clean up event handlers
if self.augroup then
api.nvim_del_augroup_by_id(self.augroup)
self.augroup = nil
end
-- clean up keymaps
self:unbind_apply_key()
self:unbind_sidebar_keys()
-- clean up file selector events
if self.file_selector then self.file_selector:off("update") end
if self.result_container then self.result_container:unmount() end
if self.selected_code_container then self.selected_code_container:unmount() end
if self.selected_files_container then self.selected_files_container:unmount() end
if self.input_container then self.input_container:unmount() end
self:delete_autocmds()
self.code = { bufnr = 0, winid = 0, selection = nil }
self.winids =
{ result_container = 0, selected_files_container = 0, selected_code_container = 0, input_container = 0 }
@ -216,34 +200,24 @@ local function transform_result_content(selected_files, result_content, prev_fil
local current_filepath
local i = 1
while true do
if i > #result_lines then break end
while i <= #result_lines do
local line_content = result_lines[i]
if line_content:match("<[Ff][Ii][Ll][Ee][Pp][Aa][Tt][Hh]>.+</[Ff][Ii][Ll][Ee][Pp][Aa][Tt][Hh]>") then
local filepath = line_content:match("<[Ff][Ii][Ll][Ee][Pp][Aa][Tt][Hh]>(.+)</[Ff][Ii][Ll][Ee][Pp][Aa][Tt][Hh]>")
if line_content:match("<FILEPATH>.+</FILEPATH>") then
local filepath = line_content:match("<FILEPATH>(.+)</FILEPATH>")
if filepath then
current_filepath = filepath
table.insert(transformed_lines, string.format("Filepath: %s", filepath))
goto continue
end
end
if line_content:match("^%s*<[Ss][Ee][Aa][Rr][Cc][Hh]>") then
if line_content == "<SEARCH>" then
is_searching = true
if not line_content:match("^%s*<[Ss][Ee][Aa][Rr][Cc][Hh]>%s*$") then
local search_start_line = line_content:match("<[Ss][Ee][Aa][Rr][Cc][Hh]>(.+)$")
line_content = "<SEARCH>"
result_lines[i] = line_content
if search_start_line and search_start_line ~= "" then table.insert(result_lines, i + 1, search_start_line) end
end
line_content = "<SEARCH>"
local prev_line = result_lines[i - 1]
if
prev_line
and prev_filepath
and not prev_line:match("Filepath:.+")
and not prev_line:match("<[Ff][Ii][Ll][Ee][Pp][Aa][Tt][Hh]>.+</[Ff][Ii][Ll][Ee][Pp][Aa][Tt][Hh]>")
and not prev_line:match("<FILEPATH>.+</FILEPATH>")
then
table.insert(transformed_lines, string.format("Filepath: %s", prev_filepath))
end
@ -251,23 +225,7 @@ local function transform_result_content(selected_files, result_content, prev_fil
if next_line and next_line:match("^%s*```%w+$") then i = i + 1 end
search_start = i + 1
last_search_tag_start_line = i
elseif line_content:match("</[Ss][Ee][Aa][Rr][Cc][Hh]>%s*$") then
if is_replacing then
result_lines[i] = line_content:gsub("</[Ss][Ee][Aa][Rr][Cc][Hh]>", "</REPLACE>")
goto continue_without_increment
end
-- Handle case where </SEARCH> is a suffix
if not line_content:match("^%s*</[Ss][Ee][Aa][Rr][Cc][Hh]>%s*$") then
local search_end_line = line_content:match("^(.+)</[Ss][Ee][Aa][Rr][Cc][Hh]>")
line_content = "</SEARCH>"
result_lines[i] = line_content
if search_end_line and search_end_line ~= "" then
table.insert(result_lines, i, search_end_line)
goto continue_without_increment
end
end
elseif line_content == "</SEARCH>" then
is_searching = false
local search_end = i
@ -290,28 +248,24 @@ local function transform_result_content(selected_files, result_content, prev_fil
if not the_matched_file then
if not PPath:new(filepath):exists() then
the_matched_file = {
filepath = filepath,
content = "",
file_type = nil,
}
else
if not PPath:new(filepath):is_file() then
Utils.warn("Not a file: " .. filepath)
goto continue
end
local lines = Utils.read_file_from_buf_or_disk(filepath)
if lines == nil then
Utils.warn("Failed to read file: " .. filepath)
goto continue
end
local content = table.concat(lines, "\n")
the_matched_file = {
filepath = filepath,
content = content,
file_type = nil,
}
Utils.warn("File not found: " .. filepath)
goto continue
end
if not PPath:new(filepath):is_file() then
Utils.warn("Not a file: " .. filepath)
goto continue
end
local lines = Utils.read_file_from_buf_or_disk(filepath)
if lines == nil then
Utils.warn("Failed to read file: " .. filepath)
goto continue
end
local content = table.concat(lines, "\n")
the_matched_file = {
filepath = filepath,
content = content,
file_type = nil,
}
end
local file_content = vim.split(the_matched_file.content, "\n")
@ -338,7 +292,8 @@ local function transform_result_content(selected_files, result_content, prev_fil
-- can happen if the llm tries to edit or create a file outside of it's context.
if not match_filetype then
local snippet_file_path = current_filepath or prev_filepath
match_filetype = Utils.get_filetype(snippet_file_path)
local snippet_file_type = vim.filetype.match({ filename = snippet_file_path }) or "unknown"
match_filetype = snippet_file_type
end
local search_start_tag_idx_in_transformed_lines = 0
@ -356,31 +311,13 @@ local function transform_result_content(selected_files, result_content, prev_fil
string.format("```%s", match_filetype),
})
goto continue
elseif line_content:match("^%s*<[Rr][Ee][Pp][Ll][Aa][Cc][Ee]>") then
elseif line_content == "<REPLACE>" then
is_replacing = true
if not line_content:match("^%s*<[Rr][Ee][Pp][Ll][Aa][Cc][Ee]>%s*$") then
local replace_first_line = line_content:match("<[Rr][Ee][Pp][Ll][Aa][Cc][Ee]>(.+)$")
line_content = "<REPLACE>"
result_lines[i] = line_content
if replace_first_line and replace_first_line ~= "" then
table.insert(result_lines, i + 1, replace_first_line)
end
end
local next_line = result_lines[i + 1]
if next_line and next_line:match("^%s*```%w+$") then i = i + 1 end
last_replace_tag_start_line = i
goto continue
elseif line_content:match("</[Rr][Ee][Pp][Ll][Aa][Cc][Ee]>%s*$") then
-- Handle case where </REPLACE> is a suffix
if not line_content:match("^%s*</[Rr][Ee][Pp][Ll][Aa][Cc][Ee]>%s*$") then
local replace_end_line = line_content:match("^(.+)</[Rr][Ee][Pp][Ll][Aa][Cc][Ee]>")
line_content = "</REPLACE>"
result_lines[i] = line_content
if replace_end_line and replace_end_line ~= "" then
table.insert(result_lines, i, replace_end_line)
goto continue_without_increment
end
end
elseif line_content == "</REPLACE>" then
is_replacing = false
local prev_line = result_lines[i - 1]
if not (prev_line and prev_line:match("^%s*```$")) then table.insert(transformed_lines, "```") end
@ -395,7 +332,6 @@ local function transform_result_content(selected_files, result_content, prev_fil
table.insert(transformed_lines, line_content)
::continue::
i = i + 1
::continue_without_increment::
end
return {
@ -461,8 +397,8 @@ local function get_searching_hint()
end
local thinking_spinner_chars = {
Utils.icon("🤯", "?"),
Utils.icon("🙄", "¿"),
"🤯",
"🙄",
}
local thinking_spinner_index = 1
@ -501,10 +437,8 @@ local function generate_display_content(replacement)
return string.format(" > %s", line)
end)
:totable()
local result_lines = vim.list_extend(
vim.list_slice(lines, 1, replacement.last_search_tag_start_line),
{ Utils.icon("🤔 ") .. "Thought content:" }
)
local result_lines =
vim.list_extend(vim.list_slice(lines, 1, replacement.last_search_tag_start_line), { "🤔 Thought content:" })
result_lines = vim.list_extend(result_lines, formatted_thinking_content_lines)
result_lines = vim.list_extend(result_lines, vim.list_slice(lines, last_think_tag_end_line + 1))
return table.concat(result_lines, "\n")
@ -761,22 +695,28 @@ local function minimize_snippet(original_lines, snippet)
return new_snippets
end
---@param filepath string
---@param snippets AvanteCodeSnippet[]
---@param snippets_map table<string, AvanteCodeSnippet[]>
---@return table<string, AvanteCodeSnippet[]>
function Sidebar:minimize_snippets(filepath, snippets)
function Sidebar:minimize_snippets(snippets_map)
local original_lines = {}
local original_lines_ = Utils.read_file_from_buf_or_disk(filepath)
if original_lines_ then original_lines = original_lines_ end
if vim.tbl_count(snippets_map) > 0 then
local filepaths = vim.tbl_keys(snippets_map)
local original_lines_, _, err = Utils.read_file_from_buf_or_disk(filepaths[1])
if err ~= nil then return {} end
if original_lines_ then original_lines = original_lines_ end
end
local results = {}
for _, snippet in ipairs(snippets) do
local new_snippets = minimize_snippet(original_lines, snippet)
if new_snippets then
for _, new_snippet in ipairs(new_snippets) do
table.insert(results, new_snippet)
for filepath, snippets in pairs(snippets_map) do
for _, snippet in ipairs(snippets) do
local new_snippets = minimize_snippet(original_lines, snippet)
if new_snippets then
results[filepath] = results[filepath] or {}
for _, new_snippet in ipairs(new_snippets) do
table.insert(results[filepath], new_snippet)
end
end
end
end
@ -809,13 +749,12 @@ function Sidebar:apply(current_cursor)
selected_snippets_map = all_snippets_map
end
if Config.behaviour.minimize_diff then selected_snippets_map = self:minimize_snippets(selected_snippets_map) end
vim.defer_fn(function()
api.nvim_set_current_win(self.code.winid)
for filepath, snippets in pairs(selected_snippets_map) do
if Config.behaviour.minimize_diff then snippets = self:minimize_snippets(filepath, snippets) end
local bufnr = Utils.get_or_create_buffer_with_filepath(filepath)
local path_ = PPath:new(filepath)
path_:parent():mkdir({ parents = true, exists_ok = true })
insert_conflict_contents(bufnr, snippets)
local process = function(winid)
api.nvim_set_current_win(winid)
@ -906,7 +845,7 @@ function Sidebar:render_result()
then
return
end
local header_text = Utils.icon("󰭻 ") .. "Avante"
local header_text = "󰭻 Avante"
self:render_header(
self.result_container.winid,
self.result_container.bufnr,
@ -928,15 +867,13 @@ function Sidebar:render_input(ask)
end
local header_text = string.format(
"%s%s (" .. Config.mappings.sidebar.switch_windows .. ": switch focus)",
Utils.icon("󱜸 "),
"󱜸 %s (" .. Config.mappings.sidebar.switch_windows .. ": switch focus)",
ask and "Ask" or "Chat with"
)
if self.code.selection ~= nil then
header_text = string.format(
"%s%s (%d:%d) (<Tab>: switch focus)",
Utils.icon("󱜸 "),
"󱜸 %s (%d:%d) (<Tab>: switch focus)",
ask and "Ask" or "Chat with",
self.code.selection.range.start.lnum,
self.code.selection.range.finish.lnum
@ -969,8 +906,7 @@ function Sidebar:render_selected_code()
selected_code_lines_count = #selected_code_lines
end
local header_text = Utils.icon("")
.. "Selected Code"
local header_text = " Selected Code"
.. (
selected_code_lines_count > selected_code_max_lines_count
and " (Show only the first " .. tostring(selected_code_max_lines_count) .. " lines)"
@ -1081,76 +1017,6 @@ function Sidebar:on_mount(opts)
})
end
-- Add keymap to add current buffer while sidebar is open
if Config.mappings.providers and Config.mappings.providers.show_providers then
vim.keymap.set("n", Config.mappings.providers.show_providers, function()
local providers = require("avante.providers")
local current_provider = Config.provider
-- Create a new buffer
local bufnr = api.nvim_create_buf(false, true)
-- Set buffer content
local lines = {}
for provider, _ in pairs(providers) do
if provider == current_provider then
table.insert(lines, "> " .. provider .. " <")
else
table.insert(lines, " " .. provider)
end
end
api.nvim_buf_set_lines(bufnr, 0, -1, false, lines)
-- Create a floating window
local width = 40
local height = #lines
local win_opts = {
relative = "editor",
width = width,
height = height,
col = (vim.o.columns - width) / 2,
row = (vim.o.lines - height) / 2 - 1,
style = "minimal",
border = "rounded",
}
local winid = api.nvim_open_win(bufnr, true, win_opts)
-- Highlight current provider
api.nvim_buf_add_highlight(bufnr, -1, "AvanteProviderCurrent", 0, 0, -1)
-- Key mappings
local function move_selection(direction)
local cursor = api.nvim_win_get_cursor(winid)
local line = cursor[1]
if direction == "up" and line > 1 then
api.nvim_win_set_cursor(winid, { line - 1, 0 })
elseif direction == "down" and line < #lines then
api.nvim_win_set_cursor(winid, { line + 1, 0 })
end
end
local function select_provider()
local cursor = api.nvim_win_get_cursor(winid)
local line = cursor[1]
local selected_provider = lines[line]:match("%S+$")
if selected_provider and selected_provider ~= current_provider then providers.refresh(selected_provider) end
api.nvim_win_close(winid, true)
end
vim.keymap.set("n", "<Up>", function() move_selection("up") end, { buffer = bufnr })
vim.keymap.set("n", "<Down>", function() move_selection("down") end, { buffer = bufnr })
vim.keymap.set("n", "<CR>", select_provider, { buffer = bufnr })
vim.keymap.set("n", "q", function() api.nvim_win_close(winid, true) end, { buffer = bufnr })
end, {
desc = "avante: show providers selector",
noremap = true,
silent = true,
})
end
api.nvim_set_option_value("wrap", Config.windows.wrap, { win = self.result_container.winid })
local current_apply_extmark_id = nil
@ -1353,12 +1219,6 @@ end
--- Initialize the sidebar instance.
--- @return avante.Sidebar The Sidebar instance.
function Sidebar:initialize()
-- Add keymap to show providers selector
vim.keymap.set("n", "<leader>ap", function() self:show_providers_selector() end, { noremap = true, silent = true })
self.code.winid = api.nvim_get_current_win()
self.code.bufnr = api.nvim_get_current_buf()
self.code.selection = Utils.get_visual_selection_and_range()
self.code.winid = api.nvim_get_current_win()
self.code.bufnr = api.nvim_get_current_buf()
self.code.selection = Utils.get_visual_selection_and_range()
@ -1376,20 +1236,6 @@ function Sidebar:initialize()
return self
end
function Sidebar:is_focused()
if not self:is_open() then return false end
local current_winid = api.nvim_get_current_win()
if self.winids.result_container and self.winids.result_container == current_winid then return true end
if self.winids.selected_files_container and self.winids.selected_files_container == current_winid then
return true
end
if self.winids.selected_code_container and self.winids.selected_code_container == current_winid then return true end
if self.winids.input_container and self.winids.input_container == current_winid then return true end
return false
end
function Sidebar:is_focused_on_result()
return self:is_open() and self.result_container and self.result_container.winid == api.nvim_get_current_win()
end
@ -1970,8 +1816,6 @@ function Sidebar:create_input_container(opts)
---@type AvanteLLMChunkCallback
local on_chunk = function(chunk)
self.is_generating = true
original_response = original_response .. chunk
local selected_files = self.file_selector:get_selected_files_contents()
@ -2006,8 +1850,6 @@ function Sidebar:create_input_container(opts)
---@type AvanteLLMStopCallback
local on_stop = function(stop_opts)
self.is_generating = false
pcall(function()
---remove keymaps
vim.keymap.del("n", "j", { buffer = self.result_container.bufnr })
@ -2121,7 +1963,6 @@ function Sidebar:create_input_container(opts)
local request = table.concat(lines, "\n")
if request == "" then return end
api.nvim_buf_set_lines(self.input_container.bufnr, 0, -1, false, {})
api.nvim_win_set_cursor(self.input_container.winid, { 1, 0 })
handle_submit(request)
end
@ -2415,70 +2256,6 @@ function Sidebar:render(opts)
return self
end
function Sidebar:show_providers_selector()
local providers = require("avante.providers")
local current_provider = Config.provider
-- Create a new buffer
local bufnr = api.nvim_create_buf(false, true)
-- Set buffer content
local lines = {}
for provider, _ in pairs(providers) do
if provider == current_provider then
table.insert(lines, "> " .. provider .. " <")
else
table.insert(lines, " " .. provider)
end
end
api.nvim_buf_set_lines(bufnr, 0, -1, false, lines)
-- Create a floating window
local width = 40
local height = #lines
local win_opts = {
relative = "editor",
width = width,
height = height,
col = (vim.o.columns - width) / 2,
row = (vim.o.lines - height) / 2 - 1,
style = "minimal",
border = "rounded",
}
local winid = api.nvim_open_win(bufnr, true, win_opts)
-- Highlight current provider
api.nvim_buf_add_highlight(bufnr, -1, "AvanteProviderCurrent", 0, 0, -1)
-- Key mappings
local function move_selection(direction)
local cursor = api.nvim_win_get_cursor(winid)
local line = cursor[1]
if direction == "up" and line > 1 then
api.nvim_win_set_cursor(winid, { line - 1, 0 })
elseif direction == "down" and line < #lines then
api.nvim_win_set_cursor(winid, { line + 1, 0 })
end
end
local function select_provider()
local cursor = api.nvim_win_get_cursor(winid)
local line = cursor[1]
local selected_provider = lines[line]:match("%S+$")
if selected_provider and selected_provider ~= current_provider then providers.refresh(selected_provider) end
api.nvim_win_close(winid, true)
end
vim.keymap.set("n", "<Up>", function() move_selection("up") end, { buffer = bufnr })
vim.keymap.set("n", "<Down>", function() move_selection("down") end, { buffer = bufnr })
vim.keymap.set("n", "<CR>", select_provider, { buffer = bufnr })
vim.keymap.set("n", "q", function() api.nvim_win_close(winid, true) end, { buffer = bufnr })
end
function Sidebar:create_selected_files_container()
if self.selected_files_container then self.selected_files_container:unmount() end
@ -2537,7 +2314,7 @@ function Sidebar:create_selected_files_container()
self:render_header(
self.selected_files_container.winid,
selected_files_buf,
Utils.icon("") .. "Selected Files",
"Selected Files",
Highlights.SUBTITLE,
Highlights.REVERSED_SUBTITLE
)

View File

@ -84,8 +84,8 @@ function Suggestion:suggest()
L1: def fib
L2:
L3: if __name__ == "__main__":
L4: # just pass
L5: pass
L4: # just pass
L5: pass
</code>
]],
},
@ -95,7 +95,7 @@ L5: pass
},
{
role = "user",
content = '<question>{"insertSpaces":true,"tabSize":4,"indentSize":4,"position":{"row":1,"col":7}}</question>',
content = '<question>{ "indentSize": 4, "position": { "row": 1, "col": 2 } }</question>',
},
{
role = "assistant",

View File

@ -10,17 +10,10 @@
Act as an expert software developer.
Always use best practices when coding.
Respect and use existing conventions, libraries, etc that are already present in the code base.
Don't directly search for code context in historical messages. Instead, prioritize using tools to obtain context first, then use context from historical messages as a secondary source, since context from historical messages is often not up to date.
Tools Usage Guide:
- You have access to tools, but only use them when necessary. If a tool is not required, respond as normal.
- If you encounter a URL, prioritize using the fetch tool to obtain its content.
- If you have information that you don't know, please proactively use the tools provided by users! Especially the web search tool.
- When available tools cannot meet the requirements, please try to use the `run_command` tool to solve the problem whenever possible.
- When attempting to modify a file that is not in the context, please first use the `list_files` tool and `search_files` tool to check if the file you want to modify exists, then use the `read_file` tool to read the file content. Don't modify blindly!
- When generating files, first use `list_files` tool to read the directory structure, don't generate blindly!
- When creating files, first check if the directory exists. If it doesn't exist, create the directory before creating the file.
- After `web_search`, if you don't get detailed enough information, do not continue use `web_search`, just continue using the `fetch` tool to get more information you need from the links in the search results.
You have access to tools, but only use them when necessary. If a tool is not required, respond as normal.
If you encounter a URL, prioritize using the fetch tool to obtain its content.
If you have information that you don't know, please proactively use the tools provided by users! Especially the web search tool.
When available tools cannot meet the requirements, please try to use the `run_command` tool to solve the problem whenever possible.
{% if system_info -%}
Use the appropriate shell based on the user's system info:

View File

@ -903,19 +903,9 @@ function M.is_same_file(filepath_a, filepath_b) return M.uniform_path(filepath_a
function M.trim_think_content(content) return content:gsub("^<think>.-</think>", "", 1) end
function M.get_filetype(filepath)
-- Some files are sometimes not detected correctly when buffer is not included
-- https://github.com/neovim/neovim/issues/27265
local buf = vim.api.nvim_create_buf(false, true)
local filetype = vim.filetype.match({ filename = filepath, buf = buf }) or ""
vim.api.nvim_buf_delete(buf, { force = true })
return filetype
end
---@param file_path string
---@return string[]|nil lines
---@return string|nil file_type
---@return string|nil error
function M.read_file_from_buf_or_disk(file_path)
--- Lookup if the file is loaded in a buffer
@ -923,7 +913,8 @@ function M.read_file_from_buf_or_disk(file_path)
if bufnr ~= -1 and vim.api.nvim_buf_is_loaded(bufnr) then
-- If buffer exists and is loaded, get buffer content
local lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false)
return lines, nil
local file_type = vim.api.nvim_get_option_value("filetype", { buf = bufnr })
return lines, file_type, nil
end
-- Fallback: read file from disk
@ -931,28 +922,12 @@ function M.read_file_from_buf_or_disk(file_path)
if file then
local content = file:read("*all")
file:close()
return vim.split(content, "\n"), nil
-- Detect the file type using the specific file's content
local file_type = vim.filetype.match({ filename = file_path, contents = { content } }) or "unknown"
return vim.split(content, "\n"), file_type, nil
else
-- M.error("failed to open file: " .. file_path .. " with error: " .. open_err)
return {}, open_err
end
end
---Check if an icon plugin is installed
---@return boolean
M.icons_enabled = function() return M.has("nvim-web-devicons") or M.has("mini.icons") or M.has("mini.nvim") end
---Display an string with icon, if an icon plugin is available.
---Dev icons are an optional install for avante, this function prevents ugly chars
---being displayed by displaying fallback options or nothing at all.
---@param string_with_icon string
---@param utf8_fallback string|nil
---@return string
M.icon = function(string_with_icon, utf8_fallback)
if M.icons_enabled() then
return string_with_icon
else
return utf8_fallback or ""
M.error("failed to open file: " .. file_path .. " with error: " .. open_err)
return {}, nil, open_err
end
end

View File

@ -81,9 +81,11 @@ function M.detect(opts)
opts.spec = opts.spec or type(vim.g.root_spec) == "table" and vim.g.root_spec or M.spec
opts.buf = (opts.buf == nil or opts.buf == 0) and vim.api.nvim_get_current_buf() or opts.buf
local ret = {} ---@type AvanteRoot[]
for _, spec in ipairs(opts.spec) do
local paths = M.resolve(spec)(opts.buf)
paths = paths or {}
paths = type(paths) == "table" and paths or { paths }
local roots = {} ---@type string[]
@ -100,6 +102,7 @@ function M.detect(opts)
return ret
end
---@type table<number, string>
M.cache = {}
@ -113,14 +116,22 @@ M.cache = {}
function M.get(opts)
opts = opts or {}
local buf = opts.buf or vim.api.nvim_get_current_buf()
local ret = M.cache[buf]
if not ret then
local roots = M.detect({ all = false, buf = buf })
ret = roots[1] and roots[1].paths[1] or vim.uv.cwd()
M.cache[buf] = ret
end
if opts and opts.normalize then return ret end
return Utils.is_win() and ret:gsub("/", "\\") or ret
-- Ensure a non-empty return value
ret = ret ~= "" and ret or vim.uv.cwd()
--- return Utils.is_win() and ret:gsub("/", "\\") or ret
return ret
end
function M.git()

View File

@ -1,22 +0,0 @@
Failed to run `config` for avante.nvim
...share/nvim/lazy/lazy.nvim/lua/lazy/core/handler/keys.lua:35: attempt to index local 'ret' (a nil value)
# stacktrace:
- /lazy.nvim/lua/lazy/core/util.lua:123 _in_ **__index**
- /lazy.nvim/lua/lazy/core/handler/keys.lua:35 _in_ **parse**
- /lazy.nvim/lua/lazy/core/handler/keys.lua:63 _in_ **have**
- /avante.nvim/lua/avante/utils/init.lua:163 _in_ **func**
- vim/shared.lua:274 _in_ **tbl_filter**
- /avante.nvim/lua/avante/utils/init.lua:163 _in_ **safe_keymap_set**
- /avante.nvim/lua/avante/init.lua:129 _in_ **keymaps**
- /avante.nvim/lua/avante/init.lua:386 _in_ **setup**
- /lazy.nvim/lua/lazy/core/loader.lua:387
- /lazy.nvim/lua/lazy/core/util.lua:135 _in_ **try**
- /lazy.nvim/lua/lazy/core/loader.lua:395 _in_ **config**
- /lazy.nvim/lua/lazy/core/loader.lua:362 _in_ **_load**
- /lazy.nvim/lua/lazy/core/loader.lua:197 _in_ **load**
- /lazy.nvim/lua/lazy/core/loader.lua:127 _in_ **startup**
- /lazy.nvim/lua/lazy/init.lua:112 _in_ **setup**
- ~/.config/nvim/lua/config/lazy.lua:17
- ~/.config/nvim/init.lua:2