Signed-off-by: Aaron Pham <contact@aarnphm.xyz>
This commit is contained in:
@@ -18,6 +18,13 @@ M.defaults = {
|
||||
-- For most providers that we support we will determine this automatically.
|
||||
-- If you wish to use a given implementation, then you can override it here.
|
||||
tokenizer = "tiktoken",
|
||||
---@alias AvanteSystemPrompt string
|
||||
-- Default system prompt. Users can override this with their own prompt
|
||||
-- You can use `require('avante.config').override({system_prompt = "MY_SYSTEM_PROMPT"}) conditionally
|
||||
-- in your own autocmds to do it per directory, or that fit your needs.
|
||||
system_prompt = [[
|
||||
You are an excellent programming expert.
|
||||
]],
|
||||
---@type AvanteSupportedProvider
|
||||
openai = {
|
||||
endpoint = "https://api.openai.com/v1",
|
||||
@@ -309,6 +316,7 @@ M.BASE_PROVIDER_KEYS = {
|
||||
"local",
|
||||
"_shellenv",
|
||||
"tokenizer_id",
|
||||
"use_xml_format",
|
||||
}
|
||||
|
||||
---@return {width: integer, height: integer}
|
||||
|
||||
@@ -234,6 +234,15 @@ H.autocmds = function()
|
||||
|
||||
-- automatically setup Avante filetype to markdown
|
||||
vim.treesitter.language.register("markdown", "Avante")
|
||||
|
||||
vim.filetype.add({
|
||||
extension = {
|
||||
["avanterules"] = "jinja",
|
||||
},
|
||||
pattern = {
|
||||
["%.avanterules%.[%w_.-]+"] = "jinja",
|
||||
},
|
||||
})
|
||||
end
|
||||
|
||||
---@param current boolean? false to disable setting current, otherwise use this to track across tabs.
|
||||
@@ -359,16 +368,6 @@ end)
|
||||
|
||||
---@param opts? avante.Config
|
||||
function M.setup(opts)
|
||||
if vim.fn.has("nvim-0.10") == 0 then
|
||||
vim.api.nvim_echo({
|
||||
{ "Avante requires at least nvim-0.10", "ErrorMsg" },
|
||||
{ "Please upgrade your neovim version", "WarningMsg" },
|
||||
{ "Press any key to exit", "ErrorMsg" },
|
||||
}, true, {})
|
||||
vim.fn.getchar()
|
||||
vim.cmd([[quit]])
|
||||
end
|
||||
|
||||
---PERF: we can still allow running require("avante").setup() multiple times to override config if users wish to
|
||||
---but most of the other functionality will only be called once from lazy.nvim
|
||||
Config.setup(opts)
|
||||
|
||||
@@ -4,6 +4,7 @@ local curl = require("plenary.curl")
|
||||
|
||||
local Utils = require("avante.utils")
|
||||
local Config = require("avante.config")
|
||||
local Path = require("avante.path")
|
||||
local P = require("avante.providers")
|
||||
|
||||
---@class avante.LLM
|
||||
@@ -13,136 +14,32 @@ M.CANCEL_PATTERN = "AvanteLLMEscape"
|
||||
|
||||
------------------------------Prompt and type------------------------------
|
||||
|
||||
---@alias AvanteSystemPrompt string
|
||||
local system_prompt = [[
|
||||
You are an excellent programming expert.
|
||||
]]
|
||||
|
||||
-- Copy from: https://github.com/Doriandarko/claude-engineer/blob/15c94963cbf9d01b8ae7bbb5d42d7025aa0555d5/main.py#L276
|
||||
---@alias AvanteBasePrompt string
|
||||
local planning_mode_user_prompt_tpl = [[
|
||||
Your primary task is to suggest code modifications with precise line number ranges. Follow these instructions meticulously:
|
||||
|
||||
1. Carefully analyze the original code, paying close attention to its structure and line numbers. Line numbers start from 1 and include ALL lines, even empty ones.
|
||||
|
||||
2. When suggesting modifications:
|
||||
a. Use the language in the question to reply. If there are non-English parts in the question, use the language of those parts.
|
||||
b. Explain why the change is necessary or beneficial.
|
||||
c. If an image is provided, make sure to use the image in conjunction with the code snippet.
|
||||
d. Provide the exact code snippet to be replaced using this format:
|
||||
|
||||
Replace lines: {{start_line}}-{{end_line}}
|
||||
```{{language}}
|
||||
{{suggested_code}}
|
||||
```
|
||||
|
||||
3. Crucial guidelines for suggested code snippets:
|
||||
- The content regarding line numbers MUST strictly follow the format "Replace lines: {{start_line}}-{{end_line}}". Do not be lazy!
|
||||
- Only apply the change(s) suggested by the most recent assistant message (before your generation).
|
||||
- Do not make any unrelated changes to the code.
|
||||
- Produce a valid full rewrite of the entire original file without skipping any lines. Do not be lazy!
|
||||
- Do not arbitrarily delete pre-existing comments/empty Lines.
|
||||
- Do not omit large parts of the original file for no reason.
|
||||
- Do not omit any needed changes from the requisite messages/code blocks.
|
||||
- If there is a clicked code block, bias towards just applying that (and applying other changes implied).
|
||||
- Please keep your suggested code changes minimal, and do not include irrelevant lines in the code snippet.
|
||||
- Maintain the SAME indentation in the returned code as in the source code
|
||||
|
||||
4. Crucial guidelines for line numbers:
|
||||
- The range {{start_line}}-{{end_line}} is INCLUSIVE. Both start_line and end_line are included in the replacement.
|
||||
- Count EVERY line, including empty lines and comments lines, comments. Do not be lazy!
|
||||
- Use the same number for start and end lines for single-line changes.
|
||||
- For multi-line changes, ensure the range covers ALL affected lines, from first to last.
|
||||
- Double-check that your line numbers align perfectly with the original code structure.
|
||||
|
||||
5. Final check:
|
||||
- Review all suggestions, ensuring each line number is correct, especially the start_line and end_line.
|
||||
- Confirm that no unrelated code is accidentally modified or deleted.
|
||||
- Verify that the start_line and end_line correctly include all intended lines for replacement.
|
||||
- Perform a final alignment check to ensure your line numbers haven't shifted, especially the start_line.
|
||||
- Double-check that your line numbers align perfectly with the original code structure.
|
||||
- DO NOT return the complete modified code with applied changes!
|
||||
|
||||
Remember: Accurate line numbers are CRITICAL. The range start_line to end_line must include ALL lines to be replaced, from the very first to the very last. Double-check every range before finalizing your response, paying special attention to the start_line to ensure it hasn't shifted down. Ensure your line numbers match the original code structure without any overall shift.
|
||||
]]
|
||||
|
||||
local editing_mode_user_prompt_tpl = [[
|
||||
Your task is to modify the provided code according to the user's request. Follow these instructions precisely:
|
||||
|
||||
1. Return ONLY the complete modified code.
|
||||
|
||||
2. Do not include any explanations, comments, or line numbers in your response.
|
||||
|
||||
3. Ensure the returned code is complete and can be directly used as a replacement for the original code.
|
||||
|
||||
4. Preserve the original structure, indentation, and formatting of the code as much as possible.
|
||||
|
||||
5. Do not omit any parts of the code, even if they are unchanged.
|
||||
|
||||
6. Maintain the SAME indentation in the returned code as in the source code
|
||||
|
||||
7. Do NOT include three backticks: ```
|
||||
|
||||
8. Only return the new code snippets to be updated, DO NOT return the entire file content.
|
||||
|
||||
Remember: Your response should contain ONLY the modified code, ready to be used as a direct replacement for the original file.
|
||||
]]
|
||||
|
||||
local suggesting_mode_user_prompt_tpl = [[
|
||||
Your task is to suggest code modifications at the cursor position. Follow these instructions meticulously:
|
||||
|
||||
1. Carefully analyze the original code, paying close attention to its structure and the cursor position.
|
||||
|
||||
2. You must follow this json format when suggesting modifications:
|
||||
|
||||
[
|
||||
{
|
||||
"row": ${row},
|
||||
"col": ${column},
|
||||
"content": "Your suggested code here"
|
||||
}
|
||||
]
|
||||
|
||||
3. When suggesting suggested code:
|
||||
- Each element in the returned list is a COMPLETE and INDEPENDENT code snippet.
|
||||
- MUST be a valid json format. Don't be lazy!
|
||||
- Only return the new code to be inserted.
|
||||
- Your returned code should not overlap with the original code in any way. Don't be lazy!
|
||||
- Please strictly check the code around the position and ensure that the complete code after insertion is correct. Don't be lazy!
|
||||
- Do not return the entire file content or any surrounding code.
|
||||
- Do not include any explanations, comments, or line numbers in your response.
|
||||
- Ensure the suggested code fits seamlessly with the existing code structure and indentation.
|
||||
- If there are no recommended modifications, return an empty list.
|
||||
|
||||
Remember: Return ONLY the suggested code snippet, without any additional formatting or explanation.
|
||||
]]
|
||||
|
||||
local group = api.nvim_create_augroup("avante_llm", { clear = true })
|
||||
|
||||
---@class StreamOptions
|
||||
---@field file_content string
|
||||
---@alias LlmMode "planning" | "editing" | "suggesting"
|
||||
---
|
||||
---@class TemplateOptions
|
||||
---@field use_xml_format boolean
|
||||
---@field ask boolean
|
||||
---@field question string
|
||||
---@field code_lang string
|
||||
---@field file_content string
|
||||
---@field selected_code string | nil
|
||||
---@field instructions string
|
||||
---@field project_context string | nil
|
||||
---@field memory_context string | nil
|
||||
---@field full_file_contents_context string | nil
|
||||
---@field mode "planning" | "editing" | "suggesting"
|
||||
---
|
||||
---@class StreamOptions: TemplateOptions
|
||||
---@field bufnr integer
|
||||
---@field instructions string
|
||||
---@field mode LlmMode
|
||||
---@field on_chunk AvanteChunkParser
|
||||
---@field on_complete AvanteCompleteParser
|
||||
|
||||
---@param opts StreamOptions
|
||||
M.stream = function(opts)
|
||||
local mode = opts.mode or "planning"
|
||||
local provider = Config.provider
|
||||
|
||||
local user_prompt_tpl = planning_mode_user_prompt_tpl
|
||||
|
||||
if mode == "editing" then
|
||||
user_prompt_tpl = editing_mode_user_prompt_tpl
|
||||
elseif mode == "suggesting" then
|
||||
user_prompt_tpl = suggesting_mode_user_prompt_tpl
|
||||
end
|
||||
---@type AvanteProviderFunctor
|
||||
local Provider = P[Config.provider]
|
||||
|
||||
-- Check if the instructions contains an image path
|
||||
local image_paths = {}
|
||||
@@ -159,52 +56,30 @@ M.stream = function(opts)
|
||||
original_instructions = table.concat(lines, "\n")
|
||||
end
|
||||
|
||||
local user_prompts = {}
|
||||
Path.prompts.initialize(Path.prompts.get(opts.bufnr))
|
||||
local user_prompt = Path.prompts.render(mode, {
|
||||
use_xml_format = Provider.use_xml_format,
|
||||
ask = true, -- TODO: add mode without ask instruction
|
||||
question = original_instructions,
|
||||
code_lang = opts.code_lang,
|
||||
file_content = opts.file_content,
|
||||
selected_code = opts.selected_code,
|
||||
project_context = opts.project_context,
|
||||
memory_context = opts.memory_context,
|
||||
})
|
||||
|
||||
if opts.selected_code and opts.selected_code ~= "" then
|
||||
table.insert(
|
||||
user_prompts,
|
||||
string.format("<code_context>```%s\n%s\n```</code_context>", opts.code_lang, opts.file_content)
|
||||
)
|
||||
table.insert(user_prompts, string.format("<code>```%s\n%s\n```</code>", opts.code_lang, opts.selected_code))
|
||||
else
|
||||
table.insert(user_prompts, string.format("<code>```%s\n%s\n```</code>", opts.code_lang, opts.file_content))
|
||||
end
|
||||
|
||||
if opts.project_context then
|
||||
table.insert(user_prompts, string.format("<project_context>%s</project_context>", opts.project_context))
|
||||
end
|
||||
|
||||
if opts.memory_context then
|
||||
table.insert(user_prompts, string.format("<memory_context>%s</memory_context>", opts.memory_context))
|
||||
end
|
||||
|
||||
if opts.full_file_contents_context then
|
||||
table.insert(
|
||||
user_prompts,
|
||||
string.format("<full_file_contents_context>%s</full_file_contents_context>", opts.full_file_contents_context)
|
||||
)
|
||||
end
|
||||
|
||||
table.insert(user_prompts, "<question>" .. original_instructions .. "</question>")
|
||||
|
||||
local user_prompt = user_prompt_tpl:gsub("%${(.-)}", opts)
|
||||
|
||||
table.insert(user_prompts, user_prompt)
|
||||
Utils.debug(user_prompt)
|
||||
|
||||
---@type AvantePromptOptions
|
||||
local code_opts = {
|
||||
system_prompt = system_prompt,
|
||||
user_prompts = user_prompts,
|
||||
system_prompt = Config.system_prompt,
|
||||
user_prompt = user_prompt,
|
||||
image_paths = image_paths,
|
||||
}
|
||||
|
||||
---@type string
|
||||
local current_event_state = nil
|
||||
|
||||
---@type AvanteProviderFunctor
|
||||
local Provider = P[provider]
|
||||
|
||||
---@type AvanteHandlerOptions
|
||||
local handler_opts = { on_chunk = opts.on_chunk, on_complete = opts.on_complete }
|
||||
---@type AvanteCurlOutput
|
||||
@@ -244,7 +119,7 @@ M.stream = function(opts)
|
||||
return
|
||||
end
|
||||
vim.schedule(function()
|
||||
if Config.options[provider] == nil and Provider.parse_stream_data ~= nil then
|
||||
if Config.options[Config.provider] == nil and Provider.parse_stream_data ~= nil then
|
||||
if Provider.parse_response ~= nil then
|
||||
Utils.warn(
|
||||
"parse_stream_data and parse_response_data are mutually exclusive, and thus parse_response_data will be ignored. Make sure that you handle the incoming data correctly.",
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
local fn, api = vim.fn, vim.api
|
||||
local Utils = require("avante.utils")
|
||||
local Path = require("plenary.path")
|
||||
local Scan = require("plenary.scandir")
|
||||
local Config = require("avante.config")
|
||||
|
||||
---@class avante.Path
|
||||
@@ -7,9 +9,10 @@ local Config = require("avante.config")
|
||||
---@field cache_path Path
|
||||
local P = {}
|
||||
|
||||
local M = {}
|
||||
|
||||
-- Helpers
|
||||
local H = {}
|
||||
|
||||
-- Get a chat history file name given a buffer
|
||||
---@param bufnr integer
|
||||
---@return string
|
||||
H.filename = function(bufnr)
|
||||
@@ -20,12 +23,23 @@ H.filename = function(bufnr)
|
||||
return fn.substitute(path_with_separators, "[^A-Za-z0-9._]", "_", "g") .. ".json"
|
||||
end
|
||||
|
||||
-- Given a mode, return the file name for the custom prompt.
|
||||
---@param mode LlmMode
|
||||
H.get_mode_file = function(mode)
|
||||
return string.format("custom.%s.avanterules", mode)
|
||||
end
|
||||
|
||||
-- History path
|
||||
local M = {}
|
||||
|
||||
-- Returns the Path to the chat history file for the given buffer.
|
||||
---@param bufnr integer
|
||||
---@return Path
|
||||
M.get = function(bufnr)
|
||||
return Path:new(Config.history.storage_path):joinpath(H.filename(bufnr))
|
||||
end
|
||||
|
||||
-- Loads the chat history for the given buffer.
|
||||
---@param bufnr integer
|
||||
M.load = function(bufnr)
|
||||
local history_file = M.get(bufnr)
|
||||
@@ -36,6 +50,7 @@ M.load = function(bufnr)
|
||||
return {}
|
||||
end
|
||||
|
||||
-- Saves the chat history for the given buffer.
|
||||
---@param bufnr integer
|
||||
---@param history table
|
||||
M.save = function(bufnr, history)
|
||||
@@ -45,6 +60,83 @@ end
|
||||
|
||||
P.history = M
|
||||
|
||||
-- Prompt path
|
||||
local N = {}
|
||||
|
||||
---@class AvanteTemplates
|
||||
---@field initialize fun(directory: string): nil
|
||||
---@field render fun(template: string, context: TemplateOptions): string
|
||||
local templates = nil
|
||||
|
||||
N.templates = { planning = nil, editing = nil, suggesting = nil }
|
||||
|
||||
-- Creates a directory in the cache path for the given buffer and copies the custom prompts to it.
|
||||
-- We need to do this beacuse the prompt template engine requires a given directory to load all required files.
|
||||
-- PERF: Hmm instead of copy to cache, we can also load in globals context, but it requires some work on bindings. (eh maybe?)
|
||||
---@param bufnr number
|
||||
---@return string the resulted cache_directory to be loaded with avante_templates
|
||||
N.get = function(bufnr)
|
||||
if not P.available() then
|
||||
error("Make sure to build avante (missing avante_templates)", 2)
|
||||
end
|
||||
|
||||
-- get root directory of given bufnr
|
||||
local directory = Path:new(Utils.root.get({ buf = bufnr }))
|
||||
---@cast directory Path
|
||||
---@type Path
|
||||
local cache_prompt_dir = P.cache_path:joinpath(directory)
|
||||
if not cache_prompt_dir:exists() then
|
||||
cache_prompt_dir:mkdir({ parents = true })
|
||||
end
|
||||
|
||||
local scanner = Scan.scan_dir(directory:absolute(), { depth = 1, add_dirs = true })
|
||||
for _, entry in ipairs(scanner) do
|
||||
local file = Path:new(entry)
|
||||
if entry:find("planning") and N.templates.planning == nil then
|
||||
N.templates.planning = file:read()
|
||||
elseif entry:find("editing") and N.templates.editing == nil then
|
||||
N.templates.editing = file:read()
|
||||
elseif entry:find("suggesting") and N.templates.suggesting == nil then
|
||||
N.templates.suggesting = file:read()
|
||||
end
|
||||
end
|
||||
|
||||
Path:new(debug.getinfo(1).source:match("@?(.*/)"):gsub("/lua/avante/path.lua$", "") .. "templates")
|
||||
:copy({ destination = cache_prompt_dir, recursive = true })
|
||||
|
||||
vim
|
||||
.iter(N.templates)
|
||||
:filter(function(_, v)
|
||||
return v ~= nil
|
||||
end)
|
||||
:each(function(k, v)
|
||||
local f = cache_prompt_dir:joinpath(H.get_mode_file(k))
|
||||
f:write(v, "w")
|
||||
end)
|
||||
|
||||
return cache_prompt_dir:absolute()
|
||||
end
|
||||
|
||||
---@param mode LlmMode
|
||||
N.get_file = function(mode)
|
||||
if N.templates[mode] ~= nil then
|
||||
return H.get_mode_file(mode)
|
||||
end
|
||||
return string.format("%s.avanterules", mode)
|
||||
end
|
||||
|
||||
---@param mode LlmMode
|
||||
---@param opts TemplateOptions
|
||||
N.render = function(mode, opts)
|
||||
return templates.render(N.get_file(mode), opts)
|
||||
end
|
||||
|
||||
N.initialize = function(directory)
|
||||
templates.initialize(directory)
|
||||
end
|
||||
|
||||
P.prompts = N
|
||||
|
||||
P.setup = function()
|
||||
local history_path = Path:new(Config.history.storage_path)
|
||||
if not history_path:exists() then
|
||||
@@ -57,6 +149,26 @@ P.setup = function()
|
||||
cache_path:mkdir({ parents = true })
|
||||
end
|
||||
P.cache_path = cache_path
|
||||
|
||||
vim.defer_fn(function()
|
||||
local ok, module = pcall(require, "avante_templates")
|
||||
---@cast module AvanteTemplates
|
||||
---@cast ok boolean
|
||||
if not ok then
|
||||
return
|
||||
end
|
||||
if templates == nil then
|
||||
templates = module
|
||||
end
|
||||
end, 1000)
|
||||
end
|
||||
|
||||
P.available = function()
|
||||
return templates ~= nil
|
||||
end
|
||||
|
||||
P.clear = function()
|
||||
P.cache_path:rm({ recursive = true })
|
||||
end
|
||||
|
||||
return P
|
||||
|
||||
@@ -7,13 +7,13 @@ local M = {}
|
||||
|
||||
M.api_key_name = "ANTHROPIC_API_KEY"
|
||||
M.tokenizer_id = "gpt-4o"
|
||||
M.use_xml_format = true
|
||||
|
||||
---@param prompt_opts AvantePromptOptions
|
||||
M.parse_message = function(prompt_opts)
|
||||
M.parse_message = function(opts)
|
||||
local message_content = {}
|
||||
|
||||
if Clipboard.support_paste_image() and prompt_opts.image_paths then
|
||||
for _, image_path in ipairs(prompt_opts.image_paths) do
|
||||
if Clipboard.support_paste_image() and opts.image_paths then
|
||||
for _, image_path in ipairs(opts.image_paths) do
|
||||
table.insert(message_content, {
|
||||
type = "image",
|
||||
source = {
|
||||
@@ -25,32 +25,15 @@ M.parse_message = function(prompt_opts)
|
||||
end
|
||||
end
|
||||
|
||||
local user_prompts_with_length = {}
|
||||
for idx, user_prompt in ipairs(prompt_opts.user_prompts) do
|
||||
table.insert(user_prompts_with_length, { idx = idx, length = Utils.tokens.calculate_tokens(user_prompt) })
|
||||
local user_prompt_obj = {
|
||||
type = "text",
|
||||
text = opts.user_prompt,
|
||||
}
|
||||
if Utils.tokens.calculate_tokens(opts.user_prompt) then
|
||||
user_prompt_obj.cache_control = { type = "ephemeral" }
|
||||
end
|
||||
|
||||
table.sort(user_prompts_with_length, function(a, b)
|
||||
return a.length > b.length
|
||||
end)
|
||||
|
||||
local top_three = {}
|
||||
for i = 1, math.min(3, #user_prompts_with_length) do
|
||||
top_three[user_prompts_with_length[i].idx] = true
|
||||
end
|
||||
|
||||
for idx, prompt_data in ipairs(prompt_opts.user_prompts) do
|
||||
local user_prompt_obj = {
|
||||
type = "text",
|
||||
text = prompt_data,
|
||||
}
|
||||
|
||||
if top_three[idx] then
|
||||
user_prompt_obj.cache_control = { type = "ephemeral" }
|
||||
end
|
||||
|
||||
table.insert(message_content, user_prompt_obj)
|
||||
end
|
||||
table.insert(message_content, user_prompt_obj)
|
||||
|
||||
return {
|
||||
{
|
||||
|
||||
@@ -32,11 +32,9 @@ M.api_key_name = "CO_API_KEY"
|
||||
M.tokenizer_id = "CohereForAI/c4ai-command-r-plus-08-2024"
|
||||
|
||||
M.parse_message = function(opts)
|
||||
local user_prompt = table.concat(opts.user_prompts, "\n\n")
|
||||
|
||||
return {
|
||||
preamble = opts.system_prompt,
|
||||
message = user_prompt,
|
||||
message = opts.user_prompt,
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
@@ -132,7 +132,7 @@ M.tokenizer_id = "gpt-4o"
|
||||
M.parse_message = function(opts)
|
||||
return {
|
||||
{ role = "system", content = opts.system_prompt },
|
||||
{ role = "user", content = table.concat(opts.user_prompts, "\n\n") },
|
||||
{ role = "user", content = opts.user_prompt },
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
@@ -25,11 +25,7 @@ M.parse_message = function(opts)
|
||||
end
|
||||
|
||||
-- insert a part into parts
|
||||
for _, user_prompt in ipairs(opts.user_prompts) do
|
||||
table.insert(message_content, {
|
||||
text = user_prompt,
|
||||
})
|
||||
end
|
||||
table.insert(message_content, { text = opts.user_prompt })
|
||||
|
||||
return {
|
||||
systemInstruction = {
|
||||
|
||||
@@ -10,7 +10,7 @@ local Dressing = require("avante.ui.dressing")
|
||||
---
|
||||
---@class AvantePromptOptions: table<[string], string>
|
||||
---@field system_prompt string
|
||||
---@field user_prompts string[]
|
||||
---@field user_prompt string
|
||||
---@field image_paths? string[]
|
||||
---
|
||||
---@class AvanteBaseMessage
|
||||
@@ -70,6 +70,7 @@ local Dressing = require("avante.ui.dressing")
|
||||
---@field has fun(): boolean
|
||||
---@field api_key_name string
|
||||
---@field tokenizer_id string | "gpt-4o"
|
||||
---@field use_xml_format boolean
|
||||
---@field model? string
|
||||
---@field parse_api_key fun(): string | nil
|
||||
---@field parse_stream_data? AvanteStreamParser
|
||||
@@ -275,6 +276,10 @@ M = setmetatable(M, {
|
||||
t[k].tokenizer_id = "gpt-4o"
|
||||
end
|
||||
|
||||
if t[k].use_xml_format == nil then
|
||||
t[k].use_xml_format = false
|
||||
end
|
||||
|
||||
if t[k].has == nil then
|
||||
t[k].has = function()
|
||||
return E.parse_envvar(t[k]) ~= nil
|
||||
|
||||
@@ -30,12 +30,10 @@ M.tokenizer_id = "gpt-4o"
|
||||
|
||||
---@param opts AvantePromptOptions
|
||||
M.get_user_message = function(opts)
|
||||
return table.concat(opts.user_prompts, "\n\n")
|
||||
return opts.user_prompt
|
||||
end
|
||||
|
||||
M.parse_message = function(opts)
|
||||
local user_prompt = table.concat(opts.user_prompts, "\n\n")
|
||||
|
||||
---@type string | OpenAIMessage[]
|
||||
local user_content
|
||||
if Config.behaviour.support_paste_from_clipboard and opts.image_paths and #opts.image_paths > 0 then
|
||||
@@ -48,9 +46,9 @@ M.parse_message = function(opts)
|
||||
},
|
||||
})
|
||||
end
|
||||
table.insert(user_content, { type = "text", text = user_prompt })
|
||||
table.insert(user_content, { type = "text", text = opts.user_prompt })
|
||||
else
|
||||
user_content = user_prompt
|
||||
user_content = opts.user_prompt
|
||||
end
|
||||
|
||||
return {
|
||||
|
||||
@@ -414,6 +414,7 @@ function Selection:create_editing_input()
|
||||
local filetype = api.nvim_get_option_value("filetype", { buf = code_bufnr })
|
||||
|
||||
Llm.stream({
|
||||
bufnr = code_bufnr,
|
||||
file_content = code_content,
|
||||
code_lang = filetype,
|
||||
selected_code = self.selection.content,
|
||||
|
||||
@@ -1346,6 +1346,7 @@ function Sidebar:create_input()
|
||||
end
|
||||
|
||||
Llm.stream({
|
||||
bufnr = self.code.bufnr,
|
||||
file_content = content_with_line_numbers,
|
||||
code_lang = filetype,
|
||||
selected_code = selected_code_content_with_line_numbers,
|
||||
|
||||
@@ -111,6 +111,7 @@ function Suggestion:suggest()
|
||||
local full_response = ""
|
||||
|
||||
Llm.stream({
|
||||
bufnr = bufnr,
|
||||
file_content = code_content,
|
||||
code_lang = filetype,
|
||||
instructions = vim.json.encode(doc),
|
||||
|
||||
22
lua/avante/templates/editing.avanterules
Normal file
22
lua/avante/templates/editing.avanterules
Normal file
@@ -0,0 +1,22 @@
|
||||
{% extends "planning.avanterules" %}
|
||||
{% block user_prompt %}
|
||||
Your task is to modify the provided code according to the user's request. Follow these instructions precisely:
|
||||
|
||||
1. Return ONLY the complete modified code.
|
||||
|
||||
2. Do not include any explanations, comments, or line numbers in your response.
|
||||
|
||||
3. Ensure the returned code is complete and can be directly used as a replacement for the original code.
|
||||
|
||||
4. Preserve the original structure, indentation, and formatting of the code as much as possible.
|
||||
|
||||
5. Do not omit any parts of the code, even if they are unchanged.
|
||||
|
||||
6. Maintain the SAME indentation in the returned code as in the source code
|
||||
|
||||
7. DO NOT include three backticks: {%raw%}```{%endraw%} in your suggestion. Treat the suggested code AS IS.
|
||||
|
||||
8. Only return the new code snippets to be updated, DO NOT return the entire file content.
|
||||
|
||||
Remember that Your response SHOULD CONTAIN ONLY THE MODIFIED CODE to be used as DIRECT REPLACEMENT to the original file.
|
||||
{% endblock %}
|
||||
125
lua/avante/templates/planning.avanterules
Normal file
125
lua/avante/templates/planning.avanterules
Normal file
@@ -0,0 +1,125 @@
|
||||
{# Uses https://mitsuhiko.github.io/minijinja-playground/ for testing:
|
||||
{
|
||||
"ask": true,
|
||||
"use_xml_format": true,
|
||||
"question": "Refactor to include tab flow",
|
||||
"code_lang": "lua",
|
||||
"file_content": "local Config = require('avante.config')"
|
||||
}
|
||||
#}
|
||||
{%- if use_xml_format -%}
|
||||
{%- if selected_code -%}
|
||||
<context>
|
||||
```{{code_lang}}
|
||||
{{file_content}}
|
||||
```
|
||||
</context>
|
||||
|
||||
<code>
|
||||
```{{code_lang}}
|
||||
{{selected_code}}
|
||||
```
|
||||
</code>
|
||||
{%- else -%}
|
||||
<code>
|
||||
```{{code_lang}}
|
||||
{{file_content}}
|
||||
```
|
||||
</code>
|
||||
{%- endif %}{%- if project_context -%}
|
||||
<project_context>
|
||||
{{project_context}}
|
||||
</project_context>
|
||||
{%- endif %}{%- if memory_context -%}
|
||||
<memory_context>
|
||||
{{memory_context}}
|
||||
</memory_context>
|
||||
{%- endif %}
|
||||
{% else %}
|
||||
{%- if selected_code -%}
|
||||
CONTEXT:
|
||||
```{{code_lang}}
|
||||
{{file_content}}
|
||||
```
|
||||
|
||||
CODE:
|
||||
```{{code_lang}}
|
||||
{{selected_code}}
|
||||
```
|
||||
{%- else -%}
|
||||
CODE:
|
||||
```{{code_lang}}
|
||||
{{file_content}}
|
||||
```
|
||||
{%- endif %}{%- if project_context -%}
|
||||
PROJECT CONTEXT:
|
||||
{{project_context}}
|
||||
{%- endif %}{%- if memory_context -%}
|
||||
MEMORY CONTEXT:
|
||||
{{memory_context}}
|
||||
{%- endif %}{%- endif %}{%- if ask %}
|
||||
{%- if not use_xml_format %}
|
||||
|
||||
INSTRUCTION: {% else %}
|
||||
<instruction>{% endif -%}
|
||||
{% block user_prompt %}
|
||||
Your primary task is to suggest code modifications with precise line number ranges. Follow these instructions meticulously:
|
||||
|
||||
1. Carefully analyze the original code, paying close attention to its structure and line numbers. Line numbers start from 1 and include ALL lines, even empty ones.
|
||||
|
||||
2. When suggesting modifications:
|
||||
a. Use the language in the question to reply. If there are non-English parts in the question, use the language of those parts.
|
||||
b. Explain why the change is necessary or beneficial.
|
||||
c. If an image is provided, make sure to use the image in conjunction with the code snippet.
|
||||
d. Provide the exact code snippet to be replaced using this format:
|
||||
{% raw %}
|
||||
Replace lines: {{start_line}}-{{end_line}}
|
||||
```{{language}}
|
||||
{{suggested_code}}
|
||||
```
|
||||
{% endraw %}
|
||||
3. Crucial guidelines for suggested code snippets:
|
||||
- The content regarding line numbers MUST strictly follow the format "Replace lines: {{start_line}}-{{end_line}}". Do not be lazy!
|
||||
- Only apply the change(s) suggested by the most recent assistant message (before your generation).
|
||||
- Do not make any unrelated changes to the code.
|
||||
- Produce a valid full rewrite of the entire original file without skipping any lines. Do not be lazy!
|
||||
- Do not arbitrarily delete pre-existing comments/empty Lines.
|
||||
- Do not omit large parts of the original file for no reason.
|
||||
- Do not omit any needed changes from the requisite messages/code blocks.
|
||||
- If there is a clicked code block, bias towards just applying that (and applying other changes implied).
|
||||
- Please keep your suggested code changes minimal, and do not include irrelevant lines in the code snippet.
|
||||
- Maintain the SAME indentation in the returned code as in the source code
|
||||
|
||||
4. Crucial guidelines for line numbers:
|
||||
- The range {{start_line}}-{{end_line}} is INCLUSIVE. Both start_line and end_line are included in the replacement.
|
||||
- Count EVERY line, including empty lines and comments lines, comments. Do not be lazy!
|
||||
- Use the same number for start and end lines for single-line changes.
|
||||
- For multi-line changes, ensure the range covers ALL affected lines, from first to last.
|
||||
- Double-check that your line numbers align perfectly with the original code structure.
|
||||
|
||||
5. Final check:
|
||||
- Review all suggestions, ensuring each line number is correct, especially the start_line and end_line.
|
||||
- Confirm that no unrelated code is accidentally modified or deleted.
|
||||
- Verify that the start_line and end_line correctly include all intended lines for replacement.
|
||||
- Perform a final alignment check to ensure your line numbers haven't shifted, especially the start_line.
|
||||
- Double-check that your line numbers align perfectly with the original code structure.
|
||||
- DO NOT return the complete modified code with applied changes!
|
||||
|
||||
Remember that ACCURATE line numbers are CRITICAL. The range {%raw%}{{start_line}}{%endraw%} to {%raw%}{{end_line}}{%endraw%} must include ALL LINES to be replaced. Double-check ALL RANGES before finalizing your response, and MAKE SURE THAT {%raw%}{{start_line}}{%endraw%} hasn't been shifted down. ENSURE line numbers MATCH the original code structure and indentation ARE PRESERVED.
|
||||
{% endblock %}
|
||||
{%- if use_xml_format -%}
|
||||
</instruction>
|
||||
|
||||
<question>{{question}}</question>
|
||||
{%- else %}
|
||||
QUESTION:
|
||||
{{question}}
|
||||
{%- endif %}
|
||||
{% else %}
|
||||
{% if use_xml_format -%}
|
||||
<question>{{question}}</question>
|
||||
{% else %}
|
||||
QUESTION:
|
||||
{{question}}
|
||||
{%- endif %}
|
||||
{%- endif %}
|
||||
32
lua/avante/templates/suggesting.avanterules
Normal file
32
lua/avante/templates/suggesting.avanterules
Normal file
@@ -0,0 +1,32 @@
|
||||
{% extends "planning.avanterules" %}
|
||||
{% block user_prompt %}
|
||||
Your task is to suggest code modifications at the cursor position. Follow these instructions meticulously:
|
||||
|
||||
1. Carefully analyze the original code, paying close attention to its structure and the cursor position.
|
||||
|
||||
2. You must follow this json format when suggesting modifications:
|
||||
|
||||
{% raw %}
|
||||
[
|
||||
{
|
||||
"row": ${row},
|
||||
"col": ${column},
|
||||
"content": "Your suggested code here"
|
||||
}
|
||||
]
|
||||
{% endraw %}
|
||||
|
||||
3. When suggesting suggested code:
|
||||
- Each element in the returned list is a COMPLETE and INDEPENDENT code snippet.
|
||||
- MUST be a valid json format. Don't be lazy!
|
||||
- Only return the new code to be inserted.
|
||||
- Your returned code should not overlap with the original code in any way. Don't be lazy!
|
||||
- Please strictly check the code around the position and ensure that the complete code after insertion is correct. Don't be lazy!
|
||||
- Do not return the entire file content or any surrounding code.
|
||||
- Do not include any explanations, comments, or line numbers in your response.
|
||||
- Ensure the suggested code fits seamlessly with the existing code structure and indentation.
|
||||
- If there are no recommended modifications, return an empty list.
|
||||
|
||||
Remember to ONLY RETURN the suggested code snippet, without any additional formatting or explanation.
|
||||
{% endblock %}
|
||||
|
||||
@@ -9,14 +9,19 @@ local M = {}
|
||||
|
||||
---@param model "gpt-4o" | string
|
||||
M.setup = function(model)
|
||||
local ok, core = pcall(require, "avante_tokenizers")
|
||||
if not ok then
|
||||
return
|
||||
end
|
||||
---@cast core AvanteTokenizer
|
||||
if tokenizers == nil then
|
||||
tokenizers = core
|
||||
end
|
||||
vim.defer_fn(function()
|
||||
local ok, core = pcall(require, "avante_tokenizers")
|
||||
if not ok then
|
||||
return
|
||||
end
|
||||
|
||||
---@cast core AvanteTokenizer
|
||||
if tokenizers == nil then
|
||||
tokenizers = core
|
||||
end
|
||||
|
||||
core.from_pretrained(model)
|
||||
end, 1000)
|
||||
|
||||
local HF_TOKEN = os.getenv("HF_TOKEN")
|
||||
if HF_TOKEN == nil and model ~= "gpt-4o" then
|
||||
@@ -26,9 +31,6 @@ M.setup = function(model)
|
||||
)
|
||||
end
|
||||
vim.env.HF_HUB_DISABLE_PROGRESS_BARS = 1
|
||||
|
||||
---@cast core AvanteTokenizer
|
||||
core.from_pretrained(model)
|
||||
end
|
||||
|
||||
M.available = function()
|
||||
|
||||
@@ -4,6 +4,7 @@ local lsp = vim.lsp
|
||||
|
||||
---@class avante.utils: LazyUtilCore
|
||||
---@field tokens avante.utils.tokens
|
||||
---@field root avante.utils.root
|
||||
local M = {}
|
||||
|
||||
setmetatable(M, {
|
||||
@@ -30,6 +31,10 @@ M.has = function(plugin)
|
||||
return package.loaded[plugin] ~= nil
|
||||
end
|
||||
|
||||
M.is_win = function()
|
||||
return jit.os:find("Windows") ~= nil
|
||||
end
|
||||
|
||||
---@return "linux" | "darwin" | "windows"
|
||||
M.get_os_name = function()
|
||||
local os_name = vim.uv.os_uname().sysname
|
||||
@@ -254,8 +259,34 @@ function M.get_hl(name)
|
||||
return api.nvim_get_hl(0, { name = name })
|
||||
end
|
||||
|
||||
M.lsp = {}
|
||||
|
||||
---@alias vim.lsp.Client.filter {id?: number, bufnr?: number, name?: string, method?: string, filter?:fun(client: vim.lsp.Client):boolean}
|
||||
|
||||
---@param opts? vim.lsp.Client.filter
|
||||
---@return vim.lsp.Client[]
|
||||
M.lsp.get_clients = function(opts)
|
||||
---@type vim.lsp.Client[]
|
||||
local ret = vim.lsp.get_clients(opts)
|
||||
return (opts and opts.filter) and vim.tbl_filter(opts.filter, ret) or ret
|
||||
end
|
||||
|
||||
--- vendor from lazy.nvim for early access and override
|
||||
|
||||
---@param path string
|
||||
---@return string
|
||||
function M.norm(path)
|
||||
if path:sub(1, 1) == "~" then
|
||||
local home = vim.uv.os_homedir()
|
||||
if home:sub(-1) == "\\" or home:sub(-1) == "/" then
|
||||
home = home:sub(1, -2)
|
||||
end
|
||||
path = home .. path:sub(2)
|
||||
end
|
||||
path = path:gsub("\\", "/"):gsub("/+", "/")
|
||||
return path:sub(-1) == "/" and path:sub(1, -2) or path
|
||||
end
|
||||
|
||||
---@param msg string|string[]
|
||||
---@param opts? LazyNotifyOpts
|
||||
function M.notify(msg, opts)
|
||||
|
||||
161
lua/avante/utils/root.lua
Normal file
161
lua/avante/utils/root.lua
Normal file
@@ -0,0 +1,161 @@
|
||||
-- COPIED and MODIFIED from https://github.com/LazyVim/LazyVim/blob/main/lua/lazyvim/util/root.lua
|
||||
local Utils = require("avante.utils")
|
||||
|
||||
---@class avante.utils.root
|
||||
---@overload fun(): string
|
||||
local M = setmetatable({}, {
|
||||
__call = function(m)
|
||||
return m.get()
|
||||
end,
|
||||
})
|
||||
|
||||
---@class AvanteRoot
|
||||
---@field paths string[]
|
||||
---@field spec AvanteRootSpec
|
||||
|
||||
---@alias AvanteRootFn fun(buf: number): (string|string[])
|
||||
|
||||
---@alias AvanteRootSpec string|string[]|AvanteRootFn
|
||||
|
||||
---@type AvanteRootSpec[]
|
||||
M.spec = { "lsp", { ".git", "lua" }, "cwd" }
|
||||
|
||||
M.detectors = {}
|
||||
|
||||
function M.detectors.cwd()
|
||||
return { vim.uv.cwd() }
|
||||
end
|
||||
|
||||
---@param buf number
|
||||
function M.detectors.lsp(buf)
|
||||
local bufpath = M.bufpath(buf)
|
||||
if not bufpath then
|
||||
return {}
|
||||
end
|
||||
local roots = {} ---@type string[]
|
||||
for _, client in pairs(Utils.lsp.get_clients({ bufnr = buf })) do
|
||||
local workspace = client.config.workspace_folders
|
||||
for _, ws in pairs(workspace or {}) do
|
||||
roots[#roots + 1] = vim.uri_to_fname(ws.uri)
|
||||
end
|
||||
if client.root_dir then
|
||||
roots[#roots + 1] = client.root_dir
|
||||
end
|
||||
end
|
||||
return vim.tbl_filter(function(path)
|
||||
path = Utils.norm(path)
|
||||
return path and bufpath:find(path, 1, true) == 1
|
||||
end, roots)
|
||||
end
|
||||
|
||||
---@param patterns string[]|string
|
||||
function M.detectors.pattern(buf, patterns)
|
||||
patterns = type(patterns) == "string" and { patterns } or patterns
|
||||
local path = M.bufpath(buf) or vim.uv.cwd()
|
||||
local pattern = vim.fs.find(function(name)
|
||||
for _, p in ipairs(patterns) do
|
||||
if name == p then
|
||||
return true
|
||||
end
|
||||
if p:sub(1, 1) == "*" and name:find(vim.pesc(p:sub(2)) .. "$") then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end, { path = path, upward = true })[1]
|
||||
return pattern and { vim.fs.dirname(pattern) } or {}
|
||||
end
|
||||
|
||||
function M.bufpath(buf)
|
||||
return M.realpath(vim.api.nvim_buf_get_name(assert(buf)))
|
||||
end
|
||||
|
||||
function M.cwd()
|
||||
return M.realpath(vim.uv.cwd()) or ""
|
||||
end
|
||||
|
||||
function M.realpath(path)
|
||||
if path == "" or path == nil then
|
||||
return nil
|
||||
end
|
||||
path = vim.uv.fs_realpath(path) or path
|
||||
return Utils.norm(path)
|
||||
end
|
||||
|
||||
---@param spec AvanteRootSpec
|
||||
---@return AvanteRootFn
|
||||
function M.resolve(spec)
|
||||
if M.detectors[spec] then
|
||||
return M.detectors[spec]
|
||||
elseif type(spec) == "function" then
|
||||
return spec
|
||||
end
|
||||
return function(buf)
|
||||
return M.detectors.pattern(buf, spec)
|
||||
end
|
||||
end
|
||||
|
||||
---@param opts? { buf?: number, spec?: AvanteRootSpec[], all?: boolean }
|
||||
function M.detect(opts)
|
||||
opts = opts or {}
|
||||
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[]
|
||||
for _, p in ipairs(paths) do
|
||||
local pp = M.realpath(p)
|
||||
if pp and not vim.tbl_contains(roots, pp) then
|
||||
roots[#roots + 1] = pp
|
||||
end
|
||||
end
|
||||
table.sort(roots, function(a, b)
|
||||
return #a > #b
|
||||
end)
|
||||
if #roots > 0 then
|
||||
ret[#ret + 1] = { spec = spec, paths = roots }
|
||||
if opts.all == false then
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
---@type table<number, string>
|
||||
M.cache = {}
|
||||
|
||||
-- returns the root directory based on:
|
||||
-- * lsp workspace folders
|
||||
-- * lsp root_dir
|
||||
-- * root pattern of filename of the current buffer
|
||||
-- * root pattern of cwd
|
||||
---@param opts? {normalize?:boolean, buf?:number}
|
||||
---@return string
|
||||
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
|
||||
end
|
||||
|
||||
function M.git()
|
||||
local root = M.get()
|
||||
local git_root = vim.fs.find(".git", { path = root, upward = true })[1]
|
||||
local ret = git_root and vim.fn.fnamemodify(git_root, ":h") or root
|
||||
return ret
|
||||
end
|
||||
|
||||
return M
|
||||
Reference in New Issue
Block a user