refactor: history messages (#1934)

This commit is contained in:
yetone
2025-04-30 03:07:18 +08:00
committed by GitHub
parent f9aa75459d
commit f10b8383e3
36 changed files with 1699 additions and 1462 deletions

View File

@@ -1,87 +0,0 @@
local Utils = require("avante.utils")
local Config = require("avante.config")
---@class avante.utils.history
local M = {}
---@param entries avante.ChatHistoryEntry[]
---@return avante.ChatHistoryEntry[]
function M.filter_active_entries(entries)
local entries_ = {}
for i = #entries, 1, -1 do
local entry = entries[i]
if entry.reset_memory then break end
table.insert(entries_, 1, entry)
end
return entries_
end
---@param entries avante.ChatHistoryEntry[]
---@return AvanteLLMMessage[]
function M.entries_to_llm_messages(entries)
local current_provider_name = Config.provider
local messages = {}
for _, entry in ipairs(entries) do
if entry.selected_filepaths ~= nil and #entry.selected_filepaths > 0 then
local user_content = "SELECTED FILES:\n\n"
for _, filepath in ipairs(entry.selected_filepaths) do
user_content = user_content .. filepath .. "\n"
end
table.insert(messages, { role = "user", content = user_content })
end
if entry.selected_code ~= nil then
local user_content_ = "SELECTED CODE:\n\n```"
.. (entry.selected_code.file_type or "")
.. (entry.selected_code.path and ":" .. entry.selected_code.path or "")
.. "\n"
.. entry.selected_code.content
.. "\n```\n\n"
table.insert(messages, { role = "user", content = user_content_ })
end
if entry.request ~= nil and entry.request ~= "" then
table.insert(messages, { role = "user", content = entry.request })
end
if entry.tool_histories ~= nil and #entry.tool_histories > 0 and entry.provider == current_provider_name then
for _, tool_history in ipairs(entry.tool_histories) do
local assistant_content = {}
if tool_history.tool_use ~= nil then
if tool_history.tool_use.response_contents ~= nil then
for _, response_content in ipairs(tool_history.tool_use.response_contents) do
table.insert(assistant_content, { type = "text", text = response_content })
end
end
table.insert(assistant_content, {
type = "tool_use",
name = tool_history.tool_use.name,
id = tool_history.tool_use.id,
input = vim.json.decode(tool_history.tool_use.input_json),
})
end
table.insert(messages, {
role = "assistant",
content = assistant_content,
})
local user_content = {}
if tool_history.tool_result ~= nil and tool_history.tool_result.content ~= nil then
table.insert(user_content, {
type = "tool_result",
tool_use_id = tool_history.tool_result.tool_use_id,
content = tool_history.tool_result.content,
is_error = tool_history.tool_result.is_error,
})
end
table.insert(messages, {
role = "user",
content = user_content,
})
end
end
local assistant_content = Utils.trim_think_content(entry.original_response or "")
if assistant_content ~= "" then table.insert(messages, { role = "assistant", content = assistant_content }) end
end
return messages
end
return M

View File

@@ -6,7 +6,6 @@ local lsp = vim.lsp
---@field tokens avante.utils.tokens
---@field root avante.utils.root
---@field file avante.utils.file
---@field history avante.utils.history
---@field environment avante.utils.environment
---@field lsp avante.utils.lsp
local M = {}
@@ -415,7 +414,7 @@ function M.debug(...)
local caller_source = info.source:match("@(.+)$") or "unknown"
local caller_module = caller_source:gsub("^.*/lua/", ""):gsub("%.lua$", ""):gsub("/", ".")
local timestamp = os.date("%Y-%m-%d %H:%M:%S")
local timestamp = M.get_timestamp()
local formated_args = {
"[" .. timestamp .. "] [AVANTE] [DEBUG] [" .. caller_module .. ":" .. info.currentline .. "]",
}
@@ -1263,7 +1262,6 @@ function M.get_commands()
local builtin_items = {
{ description = "Show help message", name = "help" },
{ description = "Clear chat history", name = "clear" },
{ description = "Reset memory", name = "reset" },
{ description = "New chat", name = "new" },
{
shorthelp = "Ask a question about specific lines",
@@ -1281,7 +1279,6 @@ function M.get_commands()
if cb then cb(args) end
end,
clear = function(sidebar, args, cb) sidebar:clear_history(args, cb) end,
reset = function(sidebar, args, cb) sidebar:reset_memory(args, cb) end,
new = function(sidebar, args, cb) sidebar:new_chat(args, cb) end,
lines = function(_, args, cb)
if cb then cb(args) end
@@ -1310,4 +1307,97 @@ function M.get_commands()
return vim.list_extend(builtin_commands, Config.slash_commands)
end
---@param history avante.ChatHistory
---@return avante.HistoryMessage[]
function M.get_history_messages(history)
local HistoryMessage = require("avante.history_message")
if history.messages then return history.messages end
local messages = {}
for _, entry in ipairs(history.entries or {}) do
if entry.request and entry.request ~= "" then
local message = HistoryMessage:new({
role = "user",
content = entry.request,
}, {
timestamp = entry.timestamp,
is_user_submission = true,
visible = entry.visible,
selected_filepaths = entry.selected_filepaths,
selected_code = entry.selected_code,
})
table.insert(messages, message)
end
if entry.response and entry.response ~= "" then
local message = HistoryMessage:new({
role = "assistant",
content = entry.response,
}, {
timestamp = entry.timestamp,
visible = entry.visible,
})
table.insert(messages, message)
end
end
history.messages = messages
return messages
end
function M.get_timestamp() return tostring(os.date("%Y-%m-%d %H:%M:%S")) end
---@param history_messages avante.HistoryMessage[]
---@return AvanteLLMMessage[]
function M.history_messages_to_messages(history_messages)
local messages = {}
for _, history_message in ipairs(history_messages) do
if history_message.just_for_display then goto continue end
table.insert(messages, history_message.message)
::continue::
end
return messages
end
function M.uuid()
local template = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx"
return string.gsub(template, "[xy]", function(c)
local v = (c == "x") and math.random(0, 0xf) or math.random(8, 0xb)
return string.format("%x", v)
end)
end
---@param item AvanteLLMMessageContentItem
---@param message avante.HistoryMessage
---@return string
function M.message_content_item_to_text(item, message)
if type(item) == "string" then return item end
if type(item) == "table" then
if item.type == "text" then return item.text end
if item.type == "image" then return "![image](" .. item.source.media_type .. ": " .. item.source.data .. ")" end
if item.type == "tool_use" then
local pieces = {}
table.insert(pieces, string.format("[%s]: calling", item.name))
for _, log in ipairs(message.tool_use_logs or {}) do
table.insert(pieces, log)
end
return table.concat(pieces, "\n")
end
end
return ""
end
---@param message avante.HistoryMessage
---@return string
function M.message_to_text(message)
local content = message.message.content
if type(content) == "string" then return content end
if vim.islist(content) then
local pieces = {}
for _, item in ipairs(content) do
local text = M.message_content_item_to_text(item, message)
if text ~= "" then table.insert(pieces, text) end
end
return table.concat(pieces, "\n")
end
return ""
end
return M

View File

@@ -77,10 +77,24 @@ function StreamingJSONParser:parse(chunk)
-- Handle strings specially (they can contain JSON control characters)
if self.state.inString then
if self.state.escaping then
self.state.stringBuffer = self.state.stringBuffer .. char
local escapeMap = {
['"'] = '"',
["\\"] = "\\",
["/"] = "/",
["b"] = "\b",
["f"] = "\f",
["n"] = "\n",
["r"] = "\r",
["t"] = "\t",
}
local escapedChar = escapeMap[char]
if escapedChar then
self.state.stringBuffer = self.state.stringBuffer .. escapedChar
else
self.state.stringBuffer = self.state.stringBuffer .. char
end
self.state.escaping = false
elseif char == "\\" then
self.state.stringBuffer = self.state.stringBuffer .. char
self.state.escaping = true
elseif char == '"' then
-- End of string