diff --git a/lua/avante/config.lua b/lua/avante/config.lua index 8473311..48d8663 100644 --- a/lua/avante/config.lua +++ b/lua/avante/config.lua @@ -345,6 +345,7 @@ M._defaults = { }, history = { max_tokens = 4096, + carried_entry_count = nil, storage_path = vim.fn.stdpath("state") .. "/avante", paste = { extension = "png", diff --git a/lua/avante/llm.lua b/lua/avante/llm.lua index 6cc1589..652984e 100644 --- a/lua/avante/llm.lua +++ b/lua/avante/llm.lua @@ -60,10 +60,11 @@ end ---@param bufnr integer ---@param history avante.ChatHistory +---@param entries? avante.ChatHistoryEntry[] ---@param cb fun(memory: avante.ChatMemory | nil): nil -function M.summarize_memory(bufnr, history, cb) +function M.summarize_memory(bufnr, history, entries, cb) local system_prompt = [[You are a helpful AI assistant tasked with summarizing conversations.]] - local entries = Utils.history.filter_active_entries(history.entries) + if not entries then entries = Utils.history.filter_active_entries(history.entries) end if #entries == 0 then cb(nil) return @@ -202,9 +203,7 @@ function M.generate_prompts(opts) if memory ~= "" then table.insert(messages, { role = "user", content = memory }) end end - if instructions then - table.insert(messages, { role = "user", content = string.format("%s", instructions) }) - end + if instructions then table.insert(messages, { role = "user", content = instructions }) end local remaining_tokens = max_tokens - Utils.tokens.calculate_tokens(system_prompt) @@ -212,20 +211,29 @@ function M.generate_prompts(opts) remaining_tokens = remaining_tokens - Utils.tokens.calculate_tokens(message.content) end + local dropped_history_messages = {} if opts.history_messages then if Config.history.max_tokens > 0 then remaining_tokens = math.min(Config.history.max_tokens, remaining_tokens) end -- Traverse the history in reverse, keeping only the latest history until the remaining tokens are exhausted and the first message role is "user" local history_messages = {} for i = #opts.history_messages, 1, -1 do local message = opts.history_messages[i] - local tokens = Utils.tokens.calculate_tokens(message.content) - remaining_tokens = remaining_tokens - tokens - if remaining_tokens > 0 then + if Config.history.carried_entry_count ~= nil then + if #history_messages > Config.history.carried_entry_count then break end table.insert(history_messages, message) else - break + local tokens = Utils.tokens.calculate_tokens(message.content) + remaining_tokens = remaining_tokens - tokens + if remaining_tokens > 0 then + table.insert(history_messages, message) + else + break + end end end + + dropped_history_messages = vim.list_slice(opts.history_messages, 1, #opts.history_messages - #history_messages) + -- prepend the history messages to the messages table vim.iter(history_messages):each(function(msg) table.insert(messages, 1, msg) end) if #messages > 0 and messages[1].role == "assistant" then table.remove(messages, 1) end @@ -254,6 +262,7 @@ Merge all changes from the snippet into the below. image_paths = image_paths, tools = opts.tools, tool_histories = opts.tool_histories, + dropped_history_messages = dropped_history_messages, } end @@ -493,6 +502,15 @@ function M._stream(opts) local prompt_opts = M.generate_prompts(opts) + if + prompt_opts.dropped_history_messages + and #prompt_opts.dropped_history_messages > 0 + and opts.on_memory_summarize + then + opts.on_memory_summarize(prompt_opts.dropped_history_messages) + return + end + local resp_headers = {} ---@type AvanteHandlerOptions diff --git a/lua/avante/sidebar.lua b/lua/avante/sidebar.lua index 8bbe142..4bd5fbc 100644 --- a/lua/avante/sidebar.lua +++ b/lua/avante/sidebar.lua @@ -2578,7 +2578,7 @@ function Sidebar:create_input_container(opts) prompts_opts.history_messages = vim.list_slice(prompts_opts.history_messages, 5) - Llm.summarize_memory(self.code.bufnr, self.chat_history, function(memory) + Llm.summarize_memory(self.code.bufnr, self.chat_history, nil, function(memory) if memory then prompts_opts.memory = memory.content end cb(prompts_opts) end) @@ -2815,6 +2815,28 @@ function Sidebar:create_input_container(opts) session_ctx = {}, }) + local function on_memory_summarize(dropped_history_messages) + local entries = Utils.history.filter_active_entries(self.chat_history.entries) + + if self.chat_history.memory then + entries = vim + .iter(entries) + :filter(function(entry) return entry.timestamp > self.chat_history.memory.last_summarized_timestamp end) + :totable() + end + + entries = vim.list_slice(entries, 1, #dropped_history_messages) + + Llm.summarize_memory(self.code.bufnr, self.chat_history, entries, function(memory) + if memory then stream_options.memory = memory.content end + stream_options.history_messages = + vim.list_slice(stream_options.history_messages, #dropped_history_messages + 1) + Llm.stream(stream_options) + end) + end + + stream_options.on_memory_summarize = on_memory_summarize + Llm.stream(stream_options) end) end diff --git a/lua/avante/suggestion.lua b/lua/avante/suggestion.lua index aa3f2d4..9292751 100644 --- a/lua/avante/suggestion.lua +++ b/lua/avante/suggestion.lua @@ -95,7 +95,7 @@ L5: pass }, { role = "user", - content = '{"insertSpaces":true,"tabSize":4,"indentSize":4,"position":{"row":1,"col":7}}', + content = '{"insertSpaces":true,"tabSize":4,"indentSize":4,"position":{"row":1,"col":7}}', }, { role = "assistant", diff --git a/lua/avante/templates/base.avanterules b/lua/avante/templates/base.avanterules index 674e489..a6a6dc8 100644 --- a/lua/avante/templates/base.avanterules +++ b/lua/avante/templates/base.avanterules @@ -4,6 +4,8 @@ Respect and use existing conventions, libraries, etc that are already present in Make sure code comments are in English when generating them. +Memory is crucial, you must follow the instructions in ! + {% include "_tools-guidelines.avanterules" %} {% if system_info -%} diff --git a/lua/avante/types.lua b/lua/avante/types.lua index e87ebd2..ab575ff 100644 --- a/lua/avante/types.lua +++ b/lua/avante/types.lua @@ -96,6 +96,7 @@ vim.g.avante_login = vim.g.avante_login ---@field image_paths? string[] ---@field tools? AvanteLLMTool[] ---@field tool_histories? AvanteLLMToolHistory[] +---@field dropped_history_messages? AvanteLLMMessage[] --- ---@class AvanteGeminiMessage ---@field role "user" @@ -331,11 +332,14 @@ vim.g.avante_login = vim.g.avante_login ---@field tool_result? AvanteLLMToolResult ---@field tool_use? AvanteLLMToolUse --- +---@alias AvanteLLMMemorySummarizeCallback fun(dropped_history_messages: AvanteLLMMessage[]): nil +--- ---@class AvanteLLMStreamOptions: AvanteGeneratePromptsOptions ---@field session_ctx? table ---@field on_start AvanteLLMStartCallback ---@field on_chunk AvanteLLMChunkCallback ---@field on_stop AvanteLLMStopCallback +---@field on_memory_summarize? AvanteLLMMemorySummarizeCallback ---@field on_tool_log? function(tool_name: string, log: string): nil --- ---@alias AvanteLLMToolFunc fun(