From 00224ea378be52bb524fd3a3b8ee20fe2c25e5b4 Mon Sep 17 00:00:00 2001 From: yetone Date: Thu, 11 Sep 2025 18:39:00 +0800 Subject: [PATCH] fix: use acp to editing (#2701) --- lua/avante/llm.lua | 169 ++++++++++++++++++++++++--------------- lua/avante/selection.lua | 4 +- tests/llm_spec.lua | 1 + 3 files changed, 109 insertions(+), 65 deletions(-) diff --git a/lua/avante/llm.lua b/lua/avante/llm.lua index e41ad8a..c445f6f 100644 --- a/lua/avante/llm.lua +++ b/lua/avante/llm.lua @@ -269,7 +269,6 @@ function M.generate_prompts(opts) if instruction_content then opts.instructions = (opts.instructions or "") .. "\n" .. instruction_content end end - local provider = opts.provider or Providers[Config.provider] local mode = opts.mode or Config.mode -- Check if the instructions contains an image path @@ -309,7 +308,18 @@ function M.generate_prompts(opts) selected_files = vim.iter(selected_files):filter(function(file) return viewed_files[file.path] == nil end):totable() - local provider_conf = Providers.parse_config(provider) + local is_acp_provider = false + if not opts.provider then is_acp_provider = Config.acp_providers[Config.provider] ~= nil end + local model_name = "unknown" + local context_window = nil + local use_react_prompt = false + if not is_acp_provider then + local provider = opts.provider or Providers[Config.provider] + model_name = provider.model or "unknown" + local provider_conf = Providers.parse_config(provider) + use_react_prompt = provider_conf.use_ReAct_prompt + context_window = provider.context_window + end local template_opts = { ask = opts.ask, -- TODO: add mode without ask instruction @@ -320,10 +330,10 @@ function M.generate_prompts(opts) project_context = opts.project_context, diagnostics = opts.diagnostics, system_info = system_info, - model_name = provider.model or "unknown", + model_name = model_name, memory = opts.memory, enable_fastapply = Config.behaviour.enable_fastapply, - use_react_prompt = provider_conf.use_ReAct_prompt, + use_react_prompt = use_react_prompt, } -- Removed the original todos processing logic, now handled in context_messages @@ -384,8 +394,6 @@ function M.generate_prompts(opts) vim.list_extend(pending_compaction_history_messages, opts.prompt_opts.pending_compaction_history_messages) end - local context_window = provider.context_window - if context_window and context_window > 0 then Utils.debug("Context window", context_window) if opts.get_tokens_usage then @@ -806,9 +814,21 @@ function M._stream_acp(opts) ---@type avante.HistoryMessage local last_tool_call_message = nil local acp_provider = Config.acp_providers[Config.provider] + local prev_text_message_content = "" local on_messages_add = function(messages) - if opts.on_messages_add then opts.on_messages_add(messages) end - vim.schedule(function() vim.cmd("redraw") end) + if opts.on_chunk then + for _, message in ipairs(messages) do + if message.message.role == "assistant" and type(message.message.content) == "string" then + local chunk = message.message.content:sub(#prev_text_message_content + 1) + opts.on_chunk(chunk) + prev_text_message_content = message.message.content + end + end + end + if opts.on_messages_add then + opts.on_messages_add(messages) + vim.schedule(function() vim.cmd("redraw") end) + end end local function add_tool_call_message(update) local message = History.Message:new("assistant", { @@ -859,29 +879,31 @@ function M._stream_acp(opts) end if update.sessionUpdate == "agent_message_chunk" then if update.content.type == "text" then - local messages = opts.get_history_messages() - local last_message = messages[#messages] - if last_message and last_message.message.role == "assistant" then - local has_text = false - local content = last_message.message.content - if type(content) == "string" then - last_message.message.content = last_message.message.content .. update.content.text - has_text = true - elseif type(content) == "table" then - for idx, item in ipairs(content) do - if type(item) == "string" then - content[idx] = item .. update.content.text - has_text = true - end - if type(item) == "table" and item.type == "text" then - item.text = item.text .. update.content.text - has_text = true + if opts.get_history_messages then + local messages = opts.get_history_messages() + local last_message = messages[#messages] + if last_message and last_message.message.role == "assistant" then + local has_text = false + local content = last_message.message.content + if type(content) == "string" then + last_message.message.content = last_message.message.content .. update.content.text + has_text = true + elseif type(content) == "table" then + for idx, item in ipairs(content) do + if type(item) == "string" then + content[idx] = item .. update.content.text + has_text = true + end + if type(item) == "table" and item.type == "text" then + item.text = item.text .. update.content.text + has_text = true + end end end - end - if has_text then - on_messages_add({ last_message }) - return + if has_text then + on_messages_add({ last_message }) + return + end end end local message = History.Message:new("assistant", update.content.text) @@ -1033,7 +1055,7 @@ function M._stream_acp(opts) }) acp_client = ACPClient:new(acp_config) acp_client:connect() - opts.on_save_acp_client(acp_client) + if opts.on_save_acp_client then opts.on_save_acp_client(acp_client) end end local session_id = opts.acp_session_id if not session_id then @@ -1048,28 +1070,31 @@ function M._stream_acp(opts) return end session_id = session_id_ - opts.on_save_acp_session_id(session_id) + if opts.on_save_acp_session_id then opts.on_save_acp_session_id(session_id) end end local prompt = {} - if opts.selected_filepaths then - for _, filepath in ipairs(opts.selected_filepaths) do - local abs_path = Utils.to_absolute_path(filepath) - local file_name = vim.fn.fnamemodify(abs_path, ":t") - local prompt_item = acp_client:create_resource_link_content("file://" .. abs_path, file_name) + local donot_use_builtin_system_prompt = opts.history_messages ~= nil and #opts.history_messages > 0 + if donot_use_builtin_system_prompt then + if opts.selected_filepaths then + for _, filepath in ipairs(opts.selected_filepaths) do + local abs_path = Utils.to_absolute_path(filepath) + local file_name = vim.fn.fnamemodify(abs_path, ":t") + local prompt_item = acp_client:create_resource_link_content("file://" .. abs_path, file_name) + table.insert(prompt, prompt_item) + end + end + if opts.selected_code then + local prompt_item = { + type = "text", + text = string.format( + "\n%s\n%s\n", + opts.selected_code.path, + opts.selected_code.content + ), + } table.insert(prompt, prompt_item) end end - if opts.selected_code then - local prompt_item = { - type = "text", - text = string.format( - "\n%s\n%s\n", - opts.selected_code.path, - opts.selected_code.content - ), - } - table.insert(prompt, prompt_item) - end local history_messages = opts.history_messages or {} if opts.acp_session_id then for i = #history_messages, 1, -1 do @@ -1100,27 +1125,43 @@ function M._stream_acp(opts) end end else - for _, message in ipairs(history_messages) do - if message.message.role == "user" then - local content = message.message.content - if type(content) == "table" then - for _, item in ipairs(content) do - if type(item) == "string" then - table.insert(prompt, { - type = "text", - text = item, - }) - elseif type(item) == "table" and item.type == "text" then - table.insert(prompt, { - type = "text", - text = item.text, - }) + if donot_use_builtin_system_prompt then + for _, message in ipairs(history_messages) do + if message.message.role == "user" then + local content = message.message.content + if type(content) == "table" then + for _, item in ipairs(content) do + if type(item) == "string" then + table.insert(prompt, { + type = "text", + text = item, + }) + elseif type(item) == "table" and item.type == "text" then + table.insert(prompt, { + type = "text", + text = item.text, + }) + end end + else + table.insert(prompt, { + type = "text", + text = content, + }) end - else + end + end + else + local prompt_opts = M.generate_prompts(opts) + table.insert(prompt, { + type = "text", + text = prompt_opts.system_prompt, + }) + for _, message in ipairs(prompt_opts.messages) do + if message.role == "user" then table.insert(prompt, { type = "text", - text = content, + text = message.content, }) end end diff --git a/lua/avante/selection.lua b/lua/avante/selection.lua index 13cfcfa..4819124 100644 --- a/lua/avante/selection.lua +++ b/lua/avante/selection.lua @@ -213,6 +213,8 @@ function Selection:submit_input(input) } end + local instructions = "Do not call any tools and just response the request: " .. input + Llm.stream({ ask = true, project_context = vim.json.encode(project_context), @@ -220,7 +222,7 @@ function Selection:submit_input(input) selected_files = { { content = code_content, file_type = filetype, path = "" } }, code_lang = filetype, selected_code = selected_code, - instructions = input, + instructions = instructions, mode = "editing", on_start = on_start, on_chunk = on_chunk, diff --git a/tests/llm_spec.lua b/tests/llm_spec.lua index 435532a..d3e1e2b 100644 --- a/tests/llm_spec.lua +++ b/tests/llm_spec.lua @@ -21,6 +21,7 @@ describe("generate_prompts", function() local Config = require("avante.config") Config.instructions_file = "avante.md" Config.provider = "openai" + Config.acp_providers = {} Config.providers = { openai = { endpoint = "https://api.mock.com/v1",