refactor(history): start moving history-related code into avante/history
The utils module has grown too big and contains unrelated functionality. Start moving code related to managing history messages comprising chat history into lua/avante/history module to keep the code more manageable.
This commit is contained in:
@@ -16,7 +16,7 @@ local Highlights = require("avante.highlights")
|
||||
local RepoMap = require("avante.repo_map")
|
||||
local FileSelector = require("avante.file_selector")
|
||||
local LLMTools = require("avante.llm_tools")
|
||||
local HistoryMessage = require("avante.history_message")
|
||||
local History = require("avante.history")
|
||||
local Line = require("avante.ui.line")
|
||||
local LRUCache = require("avante.utils.lru_cache")
|
||||
|
||||
@@ -1755,7 +1755,7 @@ end
|
||||
---@param history avante.ChatHistory
|
||||
---@return avante.ui.Line[]
|
||||
function Sidebar.get_history_lines(history)
|
||||
local history_messages = Utils.get_history_messages(history)
|
||||
local history_messages = History.get_history_messages(history)
|
||||
local ctx = {}
|
||||
---@type avante.ui.Line[][]
|
||||
local group = {}
|
||||
@@ -1825,7 +1825,7 @@ end
|
||||
---@param history avante.ChatHistory
|
||||
---@return string
|
||||
function Sidebar.render_history_content(history)
|
||||
local history_messages = Utils.get_history_messages(history)
|
||||
local history_messages = History.get_history_messages(history)
|
||||
local ctx = {}
|
||||
local group = {}
|
||||
for _, message in ipairs(history_messages) do
|
||||
@@ -1985,7 +1985,7 @@ end
|
||||
|
||||
function Sidebar:compact_history_messages(args, cb)
|
||||
local history_memory = self.chat_history.memory
|
||||
local messages = Utils.get_history_messages(self.chat_history)
|
||||
local messages = History.get_history_messages(self.chat_history)
|
||||
self.current_state = "compacting"
|
||||
self:render_state()
|
||||
self:update_content(
|
||||
@@ -2023,7 +2023,7 @@ function Sidebar:save_history() debounced_save_history(self) end
|
||||
|
||||
---@param uuids string[]
|
||||
function Sidebar:delete_history_messages(uuids)
|
||||
local history_messages = Utils.get_history_messages(self.chat_history)
|
||||
local history_messages = History.get_history_messages(self.chat_history)
|
||||
for _, msg in ipairs(history_messages) do
|
||||
if vim.list_contains(uuids, msg.uuid) then msg.is_deleted = true end
|
||||
end
|
||||
@@ -2041,7 +2041,7 @@ end
|
||||
|
||||
---@param messages avante.HistoryMessage | avante.HistoryMessage[]
|
||||
function Sidebar:add_history_messages(messages)
|
||||
local history_messages = Utils.get_history_messages(self.chat_history)
|
||||
local history_messages = History.get_history_messages(self.chat_history)
|
||||
messages = vim.islist(messages) and messages or { messages }
|
||||
for _, message in ipairs(messages) do
|
||||
if message.is_user_submission then
|
||||
@@ -2110,7 +2110,7 @@ function Sidebar:add_chat_history(messages, options)
|
||||
self.chat_history.system_prompt = content
|
||||
goto continue
|
||||
end
|
||||
local history_message = HistoryMessage:new(message)
|
||||
local history_message = History.Message:new(message)
|
||||
if message.role == "user" and is_first_user then
|
||||
is_first_user = false
|
||||
history_message.is_user_submission = true
|
||||
@@ -2254,8 +2254,7 @@ end
|
||||
---@return avante.HistoryMessage[]
|
||||
function Sidebar:get_history_messages_for_api(opts)
|
||||
opts = opts or {}
|
||||
local history_messages0 = Utils.get_history_messages(self.chat_history)
|
||||
self.chat_history.messages = history_messages0
|
||||
local history_messages0 = History.get_history_messages(self.chat_history)
|
||||
|
||||
history_messages0 = vim
|
||||
.iter(history_messages0)
|
||||
@@ -2264,8 +2263,6 @@ function Sidebar:get_history_messages_for_api(opts)
|
||||
|
||||
if opts.all then return history_messages0 end
|
||||
|
||||
local use_ReAct_prompt = Providers[Config.provider].use_ReAct_prompt ~= nil
|
||||
|
||||
history_messages0 = vim
|
||||
.iter(history_messages0)
|
||||
:filter(function(message) return message.state ~= "generating" end)
|
||||
@@ -2281,258 +2278,11 @@ function Sidebar:get_history_messages_for_api(opts)
|
||||
history_messages0 = picked_messages
|
||||
end
|
||||
|
||||
local tool_id_to_tool_name = {}
|
||||
local tool_id_to_path = {}
|
||||
local tool_id_to_start_line = {}
|
||||
local tool_id_to_end_line = {}
|
||||
local viewed_files = {}
|
||||
local last_modified_files = {}
|
||||
local history_messages = {}
|
||||
|
||||
for idx, message in ipairs(history_messages0) do
|
||||
if Utils.is_tool_result_message(message) then
|
||||
local tool_use_message = Utils.get_tool_use_message(message, history_messages0)
|
||||
|
||||
local is_edit_func_call, _, _, path = Utils.is_edit_func_call_message(tool_use_message)
|
||||
|
||||
-- Only track as successful modification if not an error AND not user-declined
|
||||
if
|
||||
is_edit_func_call
|
||||
and path
|
||||
and not message.message.content[1].is_error
|
||||
and not message.message.content[1].is_user_declined
|
||||
then
|
||||
local uniformed_path = Utils.uniform_path(path)
|
||||
last_modified_files[uniformed_path] = idx
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for idx, message in ipairs(history_messages0) do
|
||||
table.insert(history_messages, message)
|
||||
if Utils.is_tool_result_message(message) then
|
||||
local tool_use_message = Utils.get_tool_use_message(message, history_messages0)
|
||||
local is_edit_func_call, is_str_replace_editor_func_call, is_str_replace_based_edit_tool_func_call, path =
|
||||
Utils.is_edit_func_call_message(tool_use_message)
|
||||
--- For models like gpt-4o, the input parameter of replace_in_file is treated as the latest file content, so here we need to insert a fake view tool call to ensure it uses the latest file content
|
||||
if is_edit_func_call and path and not message.message.content[1].is_error then
|
||||
local uniformed_path = Utils.uniform_path(path)
|
||||
local view_result, view_error = require("avante.llm_tools.view").func({ path = path }, {})
|
||||
if view_error then view_result = "Error: " .. view_error end
|
||||
local get_diagnostics_tool_use_id = Utils.uuid()
|
||||
local view_tool_use_id = Utils.uuid()
|
||||
local view_tool_name = "view"
|
||||
local view_tool_input = { path = path }
|
||||
if is_str_replace_editor_func_call then
|
||||
view_tool_name = "str_replace_editor"
|
||||
view_tool_input = { command = "view", path = path }
|
||||
end
|
||||
if is_str_replace_based_edit_tool_func_call then
|
||||
view_tool_name = "str_replace_based_edit_tool"
|
||||
view_tool_input = { command = "view", path = path }
|
||||
end
|
||||
history_messages = vim.list_extend(history_messages, {
|
||||
HistoryMessage:new({
|
||||
role = "assistant",
|
||||
content = string.format("Viewing file %s to get the latest content", path),
|
||||
}, {
|
||||
is_dummy = true,
|
||||
}),
|
||||
HistoryMessage:new({
|
||||
role = "assistant",
|
||||
content = {
|
||||
{
|
||||
type = "tool_use",
|
||||
id = view_tool_use_id,
|
||||
name = view_tool_name,
|
||||
input = view_tool_input,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
is_dummy = true,
|
||||
}),
|
||||
HistoryMessage:new({
|
||||
role = "user",
|
||||
content = {
|
||||
{
|
||||
type = "tool_result",
|
||||
tool_use_id = view_tool_use_id,
|
||||
content = view_result,
|
||||
is_error = view_error ~= nil,
|
||||
is_user_declined = false,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
is_dummy = true,
|
||||
}),
|
||||
})
|
||||
if last_modified_files[uniformed_path] == idx and Config.behaviour.auto_check_diagnostics then
|
||||
local diagnostics = Utils.lsp.get_diagnostics_from_filepath(path)
|
||||
history_messages = vim.list_extend(history_messages, {
|
||||
HistoryMessage:new({
|
||||
role = "assistant",
|
||||
content = string.format(
|
||||
"The file %s has been modified, let me check if there are any errors in the changes.",
|
||||
path
|
||||
),
|
||||
}, {
|
||||
is_dummy = true,
|
||||
}),
|
||||
HistoryMessage:new({
|
||||
role = "assistant",
|
||||
content = {
|
||||
{
|
||||
type = "tool_use",
|
||||
id = get_diagnostics_tool_use_id,
|
||||
name = "get_diagnostics",
|
||||
input = { path = path },
|
||||
},
|
||||
},
|
||||
}, {
|
||||
is_dummy = true,
|
||||
}),
|
||||
HistoryMessage:new({
|
||||
role = "user",
|
||||
content = {
|
||||
{
|
||||
type = "tool_result",
|
||||
tool_use_id = get_diagnostics_tool_use_id,
|
||||
content = vim.json.encode(diagnostics),
|
||||
is_error = false,
|
||||
is_user_declined = false,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
is_dummy = true,
|
||||
}),
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
for _, message in ipairs(history_messages) do
|
||||
local content = message.message.content
|
||||
if type(content) ~= "table" then goto continue end
|
||||
for _, item in ipairs(content) do
|
||||
if type(item) ~= "table" then goto continue1 end
|
||||
if item.type ~= "tool_use" then goto continue1 end
|
||||
local tool_name = item.name
|
||||
if tool_name ~= "view" then goto continue1 end
|
||||
local path = item.input.path
|
||||
tool_id_to_tool_name[item.id] = tool_name
|
||||
if path then
|
||||
local uniform_path = Utils.uniform_path(path)
|
||||
tool_id_to_path[item.id] = uniform_path
|
||||
tool_id_to_start_line[item.id] = item.input.start_line
|
||||
tool_id_to_end_line[item.id] = item.input.end_line
|
||||
viewed_files[uniform_path] = item.id
|
||||
end
|
||||
::continue1::
|
||||
end
|
||||
::continue::
|
||||
end
|
||||
for _, message in ipairs(history_messages) do
|
||||
local content = message.message.content
|
||||
if type(content) == "table" then
|
||||
for _, item in ipairs(content) do
|
||||
if type(item) ~= "table" then goto continue end
|
||||
if item.type ~= "tool_result" then goto continue end
|
||||
local tool_name = tool_id_to_tool_name[item.tool_use_id]
|
||||
if tool_name ~= "view" then goto continue end
|
||||
if item.is_error then goto continue end
|
||||
local path = tool_id_to_path[item.tool_use_id]
|
||||
local latest_tool_id = viewed_files[path]
|
||||
if not latest_tool_id then goto continue end
|
||||
if latest_tool_id ~= item.tool_use_id then
|
||||
item.content = string.format("The file %s has been updated. Please use the latest `view` tool result!", path)
|
||||
else
|
||||
local start_line = tool_id_to_start_line[item.tool_use_id]
|
||||
local end_line = tool_id_to_end_line[item.tool_use_id]
|
||||
local view_result, view_error = require("avante.llm_tools.view").func(
|
||||
{ path = path, start_line = start_line, end_line = end_line },
|
||||
{}
|
||||
)
|
||||
if view_error then view_result = "Error: " .. view_error end
|
||||
item.content = view_result
|
||||
item.is_error = view_error ~= nil
|
||||
end
|
||||
::continue::
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if not use_ReAct_prompt then
|
||||
local picked_messages = {}
|
||||
local max_tool_use_count = 25
|
||||
local tool_use_count = 0
|
||||
for idx = #history_messages, 1, -1 do
|
||||
local msg = history_messages[idx]
|
||||
if tool_use_count > max_tool_use_count then
|
||||
if Utils.is_tool_result_message(msg) then
|
||||
local tool_use_message = Utils.get_tool_use_message(msg, history_messages)
|
||||
if tool_use_message then
|
||||
table.insert(
|
||||
picked_messages,
|
||||
1,
|
||||
HistoryMessage:new({
|
||||
role = "user",
|
||||
content = {
|
||||
{
|
||||
type = "text",
|
||||
text = string.format(
|
||||
"Tool use [%s] is successful: %s",
|
||||
tool_use_message.message.content[1].name,
|
||||
tostring(not msg.message.content[1].is_error)
|
||||
),
|
||||
},
|
||||
},
|
||||
}, { is_dummy = true })
|
||||
)
|
||||
local msg_content = {}
|
||||
table.insert(msg_content, {
|
||||
type = "text",
|
||||
text = string.format(
|
||||
"Tool use %s(%s)",
|
||||
tool_use_message.message.content[1].name,
|
||||
vim.json.encode(tool_use_message.message.content[1].input)
|
||||
),
|
||||
})
|
||||
table.insert(
|
||||
picked_messages,
|
||||
1,
|
||||
HistoryMessage:new({ role = "assistant", content = msg_content }, { is_dummy = true })
|
||||
)
|
||||
end
|
||||
elseif Utils.is_tool_use_message(msg) then
|
||||
tool_use_count = tool_use_count + 1
|
||||
goto continue
|
||||
else
|
||||
table.insert(picked_messages, 1, msg)
|
||||
end
|
||||
else
|
||||
if Utils.is_tool_use_message(msg) then tool_use_count = tool_use_count + 1 end
|
||||
table.insert(picked_messages, 1, msg)
|
||||
end
|
||||
::continue::
|
||||
end
|
||||
|
||||
history_messages = picked_messages
|
||||
end
|
||||
|
||||
local final_history_messages = {}
|
||||
for _, msg in ipairs(history_messages) do
|
||||
local tool_result_message
|
||||
if Utils.is_tool_use_message(msg) then
|
||||
tool_result_message = Utils.get_tool_result_message(msg, history_messages)
|
||||
if not tool_result_message then goto continue end
|
||||
end
|
||||
if Utils.is_tool_result_message(msg) then goto continue end
|
||||
table.insert(final_history_messages, msg)
|
||||
if tool_result_message then table.insert(final_history_messages, tool_result_message) end
|
||||
::continue::
|
||||
end
|
||||
|
||||
return final_history_messages
|
||||
return History.update_history_messages(
|
||||
history_messages0,
|
||||
Providers[Config.provider].use_ReAct_prompt ~= nil,
|
||||
Config.behaviour.auto_check_diagnostics
|
||||
)
|
||||
end
|
||||
|
||||
---@param request string
|
||||
@@ -2651,7 +2401,7 @@ function Sidebar:create_input_container()
|
||||
|
||||
if self.is_generating then
|
||||
self:add_history_messages({
|
||||
HistoryMessage:new({ role = "user", content = request }),
|
||||
History.Message:new({ role = "user", content = request }),
|
||||
})
|
||||
return
|
||||
end
|
||||
@@ -2802,7 +2552,7 @@ function Sidebar:create_input_container()
|
||||
local msg_content = stop_opts.error
|
||||
if type(msg_content) ~= "string" then msg_content = vim.inspect(msg_content) end
|
||||
self:add_history_messages({
|
||||
HistoryMessage:new({
|
||||
History.Message:new({
|
||||
role = "assistant",
|
||||
content = "\n\nError: " .. msg_content,
|
||||
}, {
|
||||
@@ -2831,7 +2581,7 @@ function Sidebar:create_input_container()
|
||||
|
||||
if request and request ~= "" then
|
||||
self:add_history_messages({
|
||||
HistoryMessage:new({
|
||||
History.Message:new({
|
||||
role = "user",
|
||||
content = request,
|
||||
}, {
|
||||
|
||||
Reference in New Issue
Block a user