refactor: summarize memory (#1508)

This commit is contained in:
yetone
2025-03-07 00:12:57 +08:00
committed by GitHub
parent 2b0e7e09ae
commit 8620ea3e12
18 changed files with 434 additions and 217 deletions

View File

@@ -2073,11 +2073,11 @@ function Sidebar:get_layout()
return vim.tbl_contains({ "left", "right" }, calculate_config_window_position()) and "vertical" or "horizontal"
end
---@param history avante.ChatHistoryEntry[]
---@param history avante.ChatHistory
---@return string
function Sidebar:render_history_content(history)
local content = ""
for idx, entry in ipairs(history) do
for idx, entry in ipairs(history.entries) do
if entry.reset_memory then
content = content .. "***MEMORY RESET***\n\n"
if idx < #history then content = content .. "-------\n\n" end
@@ -2146,11 +2146,11 @@ end
function Sidebar:clear_history(args, cb)
local chat_history = Path.history.load(self.code.bufnr)
if next(chat_history) ~= nil then
chat_history = {}
chat_history.entries = {}
Path.history.save(self.code.bufnr, chat_history)
self:update_content(
"Chat history cleared",
{ focus = false, scroll = false, callback = function() self:focus_input() end }
{ ignore_history = true, focus = false, scroll = false, callback = function() self:focus_input() end }
)
if cb then cb(args) end
else
@@ -2161,6 +2161,16 @@ function Sidebar:clear_history(args, cb)
end
end
function Sidebar:new_chat(args, cb)
Path.history.new(self.code.bufnr)
Sidebar.reload_chat_history()
self:update_content(
"New chat",
{ ignore_history = true, focus = false, scroll = false, callback = function() self:focus_input() end }
)
if cb then cb(args) end
end
function Sidebar:reset_memory(args, cb)
local chat_history = Path.history.load(self.code.bufnr)
if next(chat_history) ~= nil then
@@ -2176,6 +2186,7 @@ function Sidebar:reset_memory(args, cb)
reset_memory = true,
})
Path.history.save(self.code.bufnr, chat_history)
Sidebar.reload_chat_history()
local history_content = self:render_history_content(chat_history)
self:update_content(history_content, {
focus = false,
@@ -2184,6 +2195,7 @@ function Sidebar:reset_memory(args, cb)
})
if cb then cb(args) end
else
Sidebar.reload_chat_history()
self:update_content(
"Chat history is already empty",
{ focus = false, scroll = false, callback = function() self:focus_input() end }
@@ -2191,7 +2203,7 @@ function Sidebar:reset_memory(args, cb)
end
end
---@alias AvanteSlashCommandType "clear" | "help" | "lines" | "reset" | "commit"
---@alias AvanteSlashCommandType "clear" | "help" | "lines" | "reset" | "commit" | "new"
---@alias AvanteSlashCommandCallback fun(args: string, cb?: fun(args: string): nil): nil
---@alias AvanteSlashCommand {description: string, command: AvanteSlashCommandType, details: string, shorthelp?: string, callback?: AvanteSlashCommandCallback}
---@return AvanteSlashCommand[]
@@ -2211,6 +2223,7 @@ function Sidebar:get_commands()
{ description = "Show help message", command = "help" },
{ description = "Clear chat history", command = "clear" },
{ description = "Reset memory", command = "reset" },
{ description = "New chat", command = "new" },
{
shorthelp = "Ask a question about specific lines",
description = "/lines <start>-<end> <question>",
@@ -2228,6 +2241,7 @@ function Sidebar:get_commands()
end,
clear = function(args, cb) self:clear_history(args, cb) end,
reset = function(args, cb) self:reset_memory(args, cb) end,
new = function(args, cb) self:new_chat(args, cb) end,
lines = function(args, cb)
if cb then cb(args) end
end,
@@ -2298,6 +2312,8 @@ function Sidebar:create_input_container(opts)
local chat_history = Path.history.load(self.code.bufnr)
Sidebar.reload_chat_history = function() chat_history = Path.history.load(self.code.bufnr) end
local tools = vim.deepcopy(LLMTools.get_tools())
table.insert(tools, {
name = "add_file_to_context",
@@ -2330,8 +2346,9 @@ function Sidebar:create_input_container(opts)
})
---@param request string
---@return AvanteGeneratePromptsOptions
local function get_generate_prompts_options(request)
---@param summarize_memory boolean
---@param cb fun(opts: AvanteGeneratePromptsOptions): nil
local function get_generate_prompts_options(request, summarize_memory, cb)
local filetype = api.nvim_get_option_value("filetype", { buf = self.code.bufnr })
local selected_code_content = nil
@@ -2355,40 +2372,19 @@ function Sidebar:create_input_container(opts)
end
end
local history_messages = {}
for i = #chat_history, 1, -1 do
local entry = chat_history[i]
if entry.reset_memory then break end
if
entry.request == nil
or entry.original_response == nil
or entry.request == ""
or entry.original_response == ""
then
break
end
table.insert(
history_messages,
1,
{ role = "assistant", content = Utils.trim_think_content(entry.original_response) }
)
local user_content = ""
if entry.selected_file ~= nil then
user_content = user_content .. "SELECTED FILE: " .. entry.selected_file.filepath .. "\n\n"
end
if entry.selected_code ~= nil then
user_content = user_content
.. "SELECTED CODE:\n\n```"
.. entry.selected_code.filetype
.. "\n"
.. entry.selected_code.content
.. "\n```\n\n"
end
user_content = user_content .. "USER PROMPT:\n\n" .. entry.request
table.insert(history_messages, 1, { role = "user", content = user_content })
local entries = Utils.history.filter_active_entries(chat_history.entries)
if chat_history.memory then
entries = vim
.iter(entries)
:filter(function(entry) return entry.timestamp > chat_history.memory.last_summarized_timestamp end)
:totable()
end
return {
local history_messages = Utils.history.entries_to_llm_messages(entries)
---@type AvanteGeneratePromptsOptions
local prompts_opts = {
ask = opts.ask or true,
project_context = vim.json.encode(project_context),
selected_files = selected_files_contents,
@@ -2400,6 +2396,20 @@ function Sidebar:create_input_container(opts)
mode = Config.behaviour.enable_cursor_planning_mode and "cursor-planning" or "planning",
tools = tools,
}
if chat_history.memory then prompts_opts.memory = chat_history.memory.content end
if not summarize_memory or #history_messages < 8 then
cb(prompts_opts)
return
end
prompts_opts.history_messages = vim.list_slice(prompts_opts.history_messages, 5)
Llm.summarize_memory(self.code.bufnr, chat_history, function(memory)
if memory then prompts_opts.memory = memory.content end
cb(prompts_opts)
end)
end
---@param request string
@@ -2584,7 +2594,8 @@ function Sidebar:create_input_container(opts)
end, 0)
-- Save chat history
table.insert(chat_history or {}, {
chat_history.entries = chat_history.entries or {}
table.insert(chat_history.entries, {
timestamp = timestamp,
provider = Config.provider,
model = model,
@@ -2597,17 +2608,18 @@ function Sidebar:create_input_container(opts)
Path.history.save(self.code.bufnr, chat_history)
end
local generate_prompts_options = get_generate_prompts_options(request)
---@type AvanteLLMStreamOptions
---@diagnostic disable-next-line: assign-type-mismatch
local stream_options = vim.tbl_deep_extend("force", generate_prompts_options, {
on_start = on_start,
on_chunk = on_chunk,
on_stop = on_stop,
on_tool_log = on_tool_log,
})
get_generate_prompts_options(request, true, function(generate_prompts_options)
---@type AvanteLLMStreamOptions
---@diagnostic disable-next-line: assign-type-mismatch
local stream_options = vim.tbl_deep_extend("force", generate_prompts_options, {
on_start = on_start,
on_chunk = on_chunk,
on_stop = on_stop,
on_tool_log = on_tool_log,
})
Llm.stream(stream_options)
Llm.stream(stream_options)
end)
end
local function get_position()
@@ -2762,37 +2774,43 @@ function Sidebar:create_input_container(opts)
local hint_text = (fn.mode() ~= "i" and Config.mappings.submit.normal or Config.mappings.submit.insert)
.. ": submit"
if Config.behaviour.enable_token_counting then
local input_value = table.concat(api.nvim_buf_get_lines(self.input_container.bufnr, 0, -1, false), "\n")
local generate_prompts_options = get_generate_prompts_options(input_value)
local tokens = Llm.calculate_tokens(generate_prompts_options)
hint_text = "Tokens: " .. tostring(tokens) .. "; " .. hint_text
local function show()
local buf = api.nvim_create_buf(false, true)
api.nvim_buf_set_lines(buf, 0, -1, false, { hint_text })
api.nvim_buf_add_highlight(buf, 0, "AvantePopupHint", 0, 0, -1)
-- Get the current window size
local win_width = api.nvim_win_get_width(self.input_container.winid)
local width = #hint_text
-- Set the floating window options
local win_opts = {
relative = "win",
win = self.input_container.winid,
width = width,
height = 1,
row = get_float_window_row(),
col = math.max(win_width - width, 0), -- Display in the bottom right corner
style = "minimal",
border = "none",
focusable = false,
zindex = 100,
}
-- Create the floating window
hint_window = api.nvim_open_win(buf, false, win_opts)
end
local buf = api.nvim_create_buf(false, true)
api.nvim_buf_set_lines(buf, 0, -1, false, { hint_text })
api.nvim_buf_add_highlight(buf, 0, "AvantePopupHint", 0, 0, -1)
-- Get the current window size
local win_width = api.nvim_win_get_width(self.input_container.winid)
local width = #hint_text
-- Set the floating window options
local win_opts = {
relative = "win",
win = self.input_container.winid,
width = width,
height = 1,
row = get_float_window_row(),
col = math.max(win_width - width, 0), -- Display in the bottom right corner
style = "minimal",
border = "none",
focusable = false,
zindex = 100,
}
-- Create the floating window
hint_window = api.nvim_open_win(buf, false, win_opts)
if Config.behaviour.enable_token_counting then
local input_value = table.concat(api.nvim_buf_get_lines(self.input_container.bufnr, 0, -1, false), "\n")
get_generate_prompts_options(input_value, false, function(generate_prompts_options)
local tokens = Llm.calculate_tokens(generate_prompts_options)
hint_text = "Tokens: " .. tostring(tokens) .. "; " .. hint_text
show()
end)
else
show()
end
end
api.nvim_create_autocmd({ "TextChanged", "TextChangedI", "VimResized" }, {