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:
101
lua/avante/history/helpers.lua
Normal file
101
lua/avante/history/helpers.lua
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
local Utils = require("avante.utils")
|
||||||
|
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
---@param message avante.HistoryMessage
|
||||||
|
---@return boolean
|
||||||
|
function M.is_tool_use_message(message)
|
||||||
|
local content = message.message.content
|
||||||
|
if type(content) == "string" then return false end
|
||||||
|
if vim.islist(content) then
|
||||||
|
for _, item in ipairs(content) do
|
||||||
|
if item.type == "tool_use" then return true end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param message avante.HistoryMessage
|
||||||
|
---@return boolean
|
||||||
|
function M.is_tool_result_message(message)
|
||||||
|
local content = message.message.content
|
||||||
|
if type(content) == "string" then return false end
|
||||||
|
if vim.islist(content) then
|
||||||
|
for _, item in ipairs(content) do
|
||||||
|
if item.type == "tool_result" then return true end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param message avante.HistoryMessage
|
||||||
|
---@param messages avante.HistoryMessage[]
|
||||||
|
---@return avante.HistoryMessage | nil
|
||||||
|
function M.get_tool_use_message(message, messages)
|
||||||
|
local content = message.message.content
|
||||||
|
if type(content) == "string" then return nil end
|
||||||
|
if vim.islist(content) then
|
||||||
|
local tool_id = nil
|
||||||
|
for _, item in ipairs(content) do
|
||||||
|
if item.type == "tool_result" then
|
||||||
|
tool_id = item.tool_use_id
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if not tool_id then return nil end
|
||||||
|
for idx_ = #messages, 1, -1 do
|
||||||
|
local message_ = messages[idx_]
|
||||||
|
local content_ = message_.message.content
|
||||||
|
if type(content_) == "table" then
|
||||||
|
for _, item in ipairs(content_) do
|
||||||
|
if item.type == "tool_use" and item.id == tool_id then return message_ end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param tool_use_message avante.HistoryMessage | nil
|
||||||
|
function M.is_edit_func_call_message(tool_use_message)
|
||||||
|
local is_replace_func_call = false
|
||||||
|
local is_str_replace_editor_func_call = false
|
||||||
|
local is_str_replace_based_edit_tool_func_call = false
|
||||||
|
local path = nil
|
||||||
|
if tool_use_message and M.is_tool_use_message(tool_use_message) then
|
||||||
|
local tool_use = tool_use_message.message.content[1]
|
||||||
|
---@cast tool_use AvanteLLMToolUse
|
||||||
|
return Utils.is_edit_func_call_tool_use(tool_use)
|
||||||
|
end
|
||||||
|
return is_replace_func_call, is_str_replace_editor_func_call, is_str_replace_based_edit_tool_func_call, path
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param message avante.HistoryMessage
|
||||||
|
---@param messages avante.HistoryMessage[]
|
||||||
|
---@return avante.HistoryMessage | nil
|
||||||
|
function M.get_tool_result_message(message, messages)
|
||||||
|
local content = message.message.content
|
||||||
|
if type(content) == "string" then return nil end
|
||||||
|
if vim.islist(content) then
|
||||||
|
local tool_id = nil
|
||||||
|
for _, item in ipairs(content) do
|
||||||
|
if item.type == "tool_use" then
|
||||||
|
tool_id = item.id
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if not tool_id then return nil end
|
||||||
|
for idx_ = #messages, 1, -1 do
|
||||||
|
local message_ = messages[idx_]
|
||||||
|
local content_ = message_.message.content
|
||||||
|
if type(content_) == "table" then
|
||||||
|
for _, item in ipairs(content_) do
|
||||||
|
if item.type == "tool_result" and item.tool_use_id == tool_id then return message_ end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
||||||
303
lua/avante/history/init.lua
Normal file
303
lua/avante/history/init.lua
Normal file
@@ -0,0 +1,303 @@
|
|||||||
|
local Helpers = require("avante.history.helpers")
|
||||||
|
local Message = require("avante.history.message")
|
||||||
|
local Utils = require("avante.utils")
|
||||||
|
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
M.Helpers = Helpers
|
||||||
|
M.Message = Message
|
||||||
|
|
||||||
|
---@param history avante.ChatHistory
|
||||||
|
---@return avante.HistoryMessage[]
|
||||||
|
function M.get_history_messages(history)
|
||||||
|
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 = Message: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 = Message:new({
|
||||||
|
role = "assistant",
|
||||||
|
content = entry.response,
|
||||||
|
}, {
|
||||||
|
timestamp = entry.timestamp,
|
||||||
|
visible = entry.visible,
|
||||||
|
})
|
||||||
|
table.insert(messages, message)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
history.messages = messages
|
||||||
|
return messages
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param messages avante.HistoryMessage[]
|
||||||
|
---@param using_ReAct_prompt boolean
|
||||||
|
---@param add_diagnostic boolean Mix in LSP diagnostic info for affected files
|
||||||
|
---@return avante.HistoryMessage[]
|
||||||
|
M.update_history_messages = function(messages, using_ReAct_prompt, add_diagnostic)
|
||||||
|
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(messages) do
|
||||||
|
if Helpers.is_tool_result_message(message) then
|
||||||
|
local tool_use_message = Helpers.get_tool_use_message(message, messages)
|
||||||
|
|
||||||
|
local is_edit_func_call, _, _, path = Helpers.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(messages) do
|
||||||
|
table.insert(history_messages, message)
|
||||||
|
if Helpers.is_tool_result_message(message) then
|
||||||
|
local tool_use_message = Helpers.get_tool_use_message(message, messages)
|
||||||
|
local is_edit_func_call, is_str_replace_editor_func_call, is_str_replace_based_edit_tool_func_call, path =
|
||||||
|
Helpers.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, {
|
||||||
|
Message:new({
|
||||||
|
role = "assistant",
|
||||||
|
content = string.format("Viewing file %s to get the latest content", path),
|
||||||
|
}, {
|
||||||
|
is_dummy = true,
|
||||||
|
}),
|
||||||
|
Message:new({
|
||||||
|
role = "assistant",
|
||||||
|
content = {
|
||||||
|
{
|
||||||
|
type = "tool_use",
|
||||||
|
id = view_tool_use_id,
|
||||||
|
name = view_tool_name,
|
||||||
|
input = view_tool_input,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
is_dummy = true,
|
||||||
|
}),
|
||||||
|
Message: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 add_diagnostic then
|
||||||
|
local diagnostics = Utils.lsp.get_diagnostics_from_filepath(path)
|
||||||
|
history_messages = vim.list_extend(history_messages, {
|
||||||
|
Message: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,
|
||||||
|
}),
|
||||||
|
Message:new({
|
||||||
|
role = "assistant",
|
||||||
|
content = {
|
||||||
|
{
|
||||||
|
type = "tool_use",
|
||||||
|
id = get_diagnostics_tool_use_id,
|
||||||
|
name = "get_diagnostics",
|
||||||
|
input = { path = path },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
is_dummy = true,
|
||||||
|
}),
|
||||||
|
Message: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 using_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 Helpers.is_tool_result_message(msg) then
|
||||||
|
local tool_use_message = Helpers.get_tool_use_message(msg, history_messages)
|
||||||
|
if tool_use_message then
|
||||||
|
table.insert(
|
||||||
|
picked_messages,
|
||||||
|
1,
|
||||||
|
Message: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,
|
||||||
|
Message:new({ role = "assistant", content = msg_content }, { is_dummy = true })
|
||||||
|
)
|
||||||
|
end
|
||||||
|
elseif Helpers.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 Helpers.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 Helpers.is_tool_use_message(msg) then
|
||||||
|
tool_result_message = Helpers.get_tool_result_message(msg, history_messages)
|
||||||
|
if not tool_result_message then goto continue end
|
||||||
|
end
|
||||||
|
if Helpers.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
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
local History = require("avante.history")
|
||||||
local Utils = require("avante.utils")
|
local Utils = require("avante.utils")
|
||||||
local Path = require("avante.path")
|
local Path = require("avante.path")
|
||||||
local Config = require("avante.config")
|
local Config = require("avante.config")
|
||||||
@@ -9,7 +10,7 @@ local M = {}
|
|||||||
---@param history avante.ChatHistory
|
---@param history avante.ChatHistory
|
||||||
---@return table?
|
---@return table?
|
||||||
local function to_selector_item(history)
|
local function to_selector_item(history)
|
||||||
local messages = Utils.get_history_messages(history)
|
local messages = History.get_history_messages(history)
|
||||||
local timestamp = #messages > 0 and messages[#messages].timestamp or history.timestamp
|
local timestamp = #messages > 0 and messages[#messages].timestamp or history.timestamp
|
||||||
local name = history.title .. " - " .. timestamp .. " (" .. #messages .. ")"
|
local name = history.title .. " - " .. timestamp .. " (" .. #messages .. ")"
|
||||||
name = name:gsub("\n", "\\n")
|
name = name:gsub("\n", "\\n")
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ local Path = require("avante.path")
|
|||||||
local Providers = require("avante.providers")
|
local Providers = require("avante.providers")
|
||||||
local LLMToolHelpers = require("avante.llm_tools.helpers")
|
local LLMToolHelpers = require("avante.llm_tools.helpers")
|
||||||
local LLMTools = require("avante.llm_tools")
|
local LLMTools = require("avante.llm_tools")
|
||||||
local HistoryMessage = require("avante.history_message")
|
local History = require("avante.history")
|
||||||
|
|
||||||
---@class avante.LLM
|
---@class avante.LLM
|
||||||
local M = {}
|
local M = {}
|
||||||
@@ -771,7 +771,7 @@ function M._stream(opts)
|
|||||||
---@type avante.HistoryMessage[]
|
---@type avante.HistoryMessage[]
|
||||||
local messages = {}
|
local messages = {}
|
||||||
for _, tool_result in ipairs(tool_results) do
|
for _, tool_result in ipairs(tool_results) do
|
||||||
messages[#messages + 1] = HistoryMessage:new({
|
messages[#messages + 1] = History.Message:new({
|
||||||
role = "user",
|
role = "user",
|
||||||
content = {
|
content = {
|
||||||
{
|
{
|
||||||
@@ -816,7 +816,7 @@ function M._stream(opts)
|
|||||||
Utils.debug("Tool execution was cancelled by user")
|
Utils.debug("Tool execution was cancelled by user")
|
||||||
if opts.on_chunk then opts.on_chunk("\n*[Request cancelled by user during tool execution.]*\n") end
|
if opts.on_chunk then opts.on_chunk("\n*[Request cancelled by user during tool execution.]*\n") end
|
||||||
if opts.on_messages_add then
|
if opts.on_messages_add then
|
||||||
local message = HistoryMessage:new({
|
local message = History.Message:new({
|
||||||
role = "assistant",
|
role = "assistant",
|
||||||
content = "\n*[Request cancelled by user during tool execution.]*\n",
|
content = "\n*[Request cancelled by user during tool execution.]*\n",
|
||||||
}, {
|
}, {
|
||||||
@@ -868,7 +868,7 @@ function M._stream(opts)
|
|||||||
if stop_opts.reason == "cancelled" then
|
if stop_opts.reason == "cancelled" then
|
||||||
if opts.on_chunk then opts.on_chunk("\n*[Request cancelled by user.]*\n") end
|
if opts.on_chunk then opts.on_chunk("\n*[Request cancelled by user.]*\n") end
|
||||||
if opts.on_messages_add then
|
if opts.on_messages_add then
|
||||||
local message = HistoryMessage:new({
|
local message = History.Message:new({
|
||||||
role = "assistant",
|
role = "assistant",
|
||||||
content = "\n*[Request cancelled by user.]*\n",
|
content = "\n*[Request cancelled by user.]*\n",
|
||||||
}, {
|
}, {
|
||||||
@@ -885,7 +885,7 @@ function M._stream(opts)
|
|||||||
for idx = #history_messages, 1, -1 do
|
for idx = #history_messages, 1, -1 do
|
||||||
local message = history_messages[idx]
|
local message = history_messages[idx]
|
||||||
if message.is_user_submission then break end
|
if message.is_user_submission then break end
|
||||||
if not Utils.is_tool_use_message(message) then goto continue end
|
if not History.Helpers.is_tool_use_message(message) then goto continue end
|
||||||
if message.message.content[1].name ~= "attempt_completion" then break end
|
if message.message.content[1].name ~= "attempt_completion" then break end
|
||||||
completed_attempt_completion_tool_use = message
|
completed_attempt_completion_tool_use = message
|
||||||
if message then break end
|
if message then break end
|
||||||
@@ -909,14 +909,14 @@ function M._stream(opts)
|
|||||||
Utils.debug("user reminder count", user_reminder_count)
|
Utils.debug("user reminder count", user_reminder_count)
|
||||||
local message
|
local message
|
||||||
if #unfinished_todos > 0 then
|
if #unfinished_todos > 0 then
|
||||||
message = HistoryMessage:new({
|
message = History.Message:new({
|
||||||
role = "user",
|
role = "user",
|
||||||
content = "<user-reminder>You should use tool calls to answer the question, for example, use update_todo_status if the task step is done or cancelled.</user-reminder>",
|
content = "<user-reminder>You should use tool calls to answer the question, for example, use update_todo_status if the task step is done or cancelled.</user-reminder>",
|
||||||
}, {
|
}, {
|
||||||
visible = false,
|
visible = false,
|
||||||
})
|
})
|
||||||
else
|
else
|
||||||
message = HistoryMessage:new({
|
message = History.Message:new({
|
||||||
role = "user",
|
role = "user",
|
||||||
content = "<user-reminder>You should use tool calls to answer the question, for example, use attempt_completion if the job is done.</user-reminder>",
|
content = "<user-reminder>You should use tool calls to answer the question, for example, use attempt_completion if the job is done.</user-reminder>",
|
||||||
}, {
|
}, {
|
||||||
@@ -954,7 +954,7 @@ function M._stream(opts)
|
|||||||
if opts.on_chunk then opts.on_chunk("\n" .. msg_content .. "\n") end
|
if opts.on_chunk then opts.on_chunk("\n" .. msg_content .. "\n") end
|
||||||
local message
|
local message
|
||||||
if opts.on_messages_add then
|
if opts.on_messages_add then
|
||||||
message = HistoryMessage:new({
|
message = History.Message:new({
|
||||||
role = "assistant",
|
role = "assistant",
|
||||||
content = "\n\n" .. msg_content,
|
content = "\n\n" .. msg_content,
|
||||||
}, {
|
}, {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
local Base = require("avante.llm_tools.base")
|
local Base = require("avante.llm_tools.base")
|
||||||
local Utils = require("avante.utils")
|
local History = require("avante.history")
|
||||||
|
|
||||||
---@class AvanteLLMTool
|
---@class AvanteLLMTool
|
||||||
local M = setmetatable({}, Base)
|
local M = setmetatable({}, Base)
|
||||||
@@ -43,10 +43,10 @@ M.returns = {
|
|||||||
function M.func(input, opts)
|
function M.func(input, opts)
|
||||||
local sidebar = require("avante").get()
|
local sidebar = require("avante").get()
|
||||||
if not sidebar then return false, "Avante sidebar not found" end
|
if not sidebar then return false, "Avante sidebar not found" end
|
||||||
local history_messages = Utils.get_history_messages(sidebar.chat_history)
|
local history_messages = History.get_history_messages(sidebar.chat_history)
|
||||||
local the_deleted_message_uuids = {}
|
local the_deleted_message_uuids = {}
|
||||||
for _, msg in ipairs(history_messages) do
|
for _, msg in ipairs(history_messages) do
|
||||||
if Utils.is_tool_use_message(msg) then
|
if History.Helpers.is_tool_use_message(msg) then
|
||||||
local content = msg.message.content
|
local content = msg.message.content
|
||||||
if type(content) == "table" then
|
if type(content) == "table" then
|
||||||
for _, item in ipairs(content) do
|
for _, item in ipairs(content) do
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ local Providers = require("avante.providers")
|
|||||||
local Config = require("avante.config")
|
local Config = require("avante.config")
|
||||||
local Utils = require("avante.utils")
|
local Utils = require("avante.utils")
|
||||||
local Base = require("avante.llm_tools.base")
|
local Base = require("avante.llm_tools.base")
|
||||||
local HistoryMessage = require("avante.history_message")
|
local History = require("avante.history")
|
||||||
local Line = require("avante.ui.line")
|
local Line = require("avante.ui.line")
|
||||||
local Highlights = require("avante.highlights")
|
local Highlights = require("avante.highlights")
|
||||||
|
|
||||||
@@ -94,7 +94,7 @@ function M.on_render(input, opts)
|
|||||||
local content = msg.message.content
|
local content = msg.message.content
|
||||||
local summary
|
local summary
|
||||||
if type(content) == "table" and #content > 0 and content[1].type == "tool_use" then
|
if type(content) == "table" and #content > 0 and content[1].type == "tool_use" then
|
||||||
local tool_result_message = Utils.get_tool_result_message(msg, messages)
|
local tool_result_message = History.Helpers.get_tool_result_message(msg, messages)
|
||||||
if tool_result_message then
|
if tool_result_message then
|
||||||
local tool_name = msg.message.content[1].name
|
local tool_name = msg.message.content[1].name
|
||||||
if tool_name == "ls" then
|
if tool_name == "ls" then
|
||||||
@@ -267,7 +267,7 @@ When you're done, provide a clear and concise summary of what you found.]]):gsub
|
|||||||
.. elapsed_time
|
.. elapsed_time
|
||||||
.. "s)"
|
.. "s)"
|
||||||
if session_ctx.on_messages_add then
|
if session_ctx.on_messages_add then
|
||||||
local message = HistoryMessage:new({
|
local message = History.Message:new({
|
||||||
role = "assistant",
|
role = "assistant",
|
||||||
content = "\n\n" .. summary,
|
content = "\n\n" .. summary,
|
||||||
}, {
|
}, {
|
||||||
|
|||||||
@@ -53,10 +53,11 @@ function History.list(bufnr)
|
|||||||
--- sort by timestamp
|
--- sort by timestamp
|
||||||
--- sort by latest_filename
|
--- sort by latest_filename
|
||||||
table.sort(res, function(a, b)
|
table.sort(res, function(a, b)
|
||||||
|
local H = require("avante.history")
|
||||||
if a.filename == latest_filename then return true end
|
if a.filename == latest_filename then return true end
|
||||||
if b.filename == latest_filename then return false end
|
if b.filename == latest_filename then return false end
|
||||||
local a_messages = Utils.get_history_messages(a)
|
local a_messages = H.get_history_messages(a)
|
||||||
local b_messages = Utils.get_history_messages(b)
|
local b_messages = H.get_history_messages(b)
|
||||||
local timestamp_a = #a_messages > 0 and a_messages[#a_messages].timestamp or a.timestamp
|
local timestamp_a = #a_messages > 0 and a_messages[#a_messages].timestamp or a.timestamp
|
||||||
local timestamp_b = #b_messages > 0 and b_messages[#b_messages].timestamp or b.timestamp
|
local timestamp_b = #b_messages > 0 and b_messages[#b_messages].timestamp or b.timestamp
|
||||||
return timestamp_a > timestamp_b
|
return timestamp_a > timestamp_b
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
local Utils = require("avante.utils")
|
local Utils = require("avante.utils")
|
||||||
local Clipboard = require("avante.clipboard")
|
local Clipboard = require("avante.clipboard")
|
||||||
local P = require("avante.providers")
|
local P = require("avante.providers")
|
||||||
local HistoryMessage = require("avante.history_message")
|
local HistoryMessage = require("avante.history.message")
|
||||||
local JsonParser = require("avante.libs.jsonparser")
|
local JsonParser = require("avante.libs.jsonparser")
|
||||||
|
|
||||||
---@class AvanteProviderFunctor
|
---@class AvanteProviderFunctor
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ local Utils = require("avante.utils")
|
|||||||
local Providers = require("avante.providers")
|
local Providers = require("avante.providers")
|
||||||
local Config = require("avante.config")
|
local Config = require("avante.config")
|
||||||
local Clipboard = require("avante.clipboard")
|
local Clipboard = require("avante.clipboard")
|
||||||
local HistoryMessage = require("avante.history_message")
|
local HistoryMessage = require("avante.history.message")
|
||||||
local Prompts = require("avante.utils.prompts")
|
local Prompts = require("avante.utils.prompts")
|
||||||
|
|
||||||
---@class AvanteProviderFunctor
|
---@class AvanteProviderFunctor
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ local Utils = require("avante.utils")
|
|||||||
local Config = require("avante.config")
|
local Config = require("avante.config")
|
||||||
local Clipboard = require("avante.clipboard")
|
local Clipboard = require("avante.clipboard")
|
||||||
local Providers = require("avante.providers")
|
local Providers = require("avante.providers")
|
||||||
local HistoryMessage = require("avante.history_message")
|
local HistoryMessage = require("avante.history.message")
|
||||||
local ReActParser = require("avante.libs.ReAct_parser")
|
local ReActParser = require("avante.libs.ReAct_parser")
|
||||||
local JsonParser = require("avante.libs.jsonparser")
|
local JsonParser = require("avante.libs.jsonparser")
|
||||||
local Prompts = require("avante.utils.prompts")
|
local Prompts = require("avante.utils.prompts")
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ local Highlights = require("avante.highlights")
|
|||||||
local RepoMap = require("avante.repo_map")
|
local RepoMap = require("avante.repo_map")
|
||||||
local FileSelector = require("avante.file_selector")
|
local FileSelector = require("avante.file_selector")
|
||||||
local LLMTools = require("avante.llm_tools")
|
local LLMTools = require("avante.llm_tools")
|
||||||
local HistoryMessage = require("avante.history_message")
|
local History = require("avante.history")
|
||||||
local Line = require("avante.ui.line")
|
local Line = require("avante.ui.line")
|
||||||
local LRUCache = require("avante.utils.lru_cache")
|
local LRUCache = require("avante.utils.lru_cache")
|
||||||
|
|
||||||
@@ -1755,7 +1755,7 @@ end
|
|||||||
---@param history avante.ChatHistory
|
---@param history avante.ChatHistory
|
||||||
---@return avante.ui.Line[]
|
---@return avante.ui.Line[]
|
||||||
function Sidebar.get_history_lines(history)
|
function Sidebar.get_history_lines(history)
|
||||||
local history_messages = Utils.get_history_messages(history)
|
local history_messages = History.get_history_messages(history)
|
||||||
local ctx = {}
|
local ctx = {}
|
||||||
---@type avante.ui.Line[][]
|
---@type avante.ui.Line[][]
|
||||||
local group = {}
|
local group = {}
|
||||||
@@ -1825,7 +1825,7 @@ end
|
|||||||
---@param history avante.ChatHistory
|
---@param history avante.ChatHistory
|
||||||
---@return string
|
---@return string
|
||||||
function Sidebar.render_history_content(history)
|
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 ctx = {}
|
||||||
local group = {}
|
local group = {}
|
||||||
for _, message in ipairs(history_messages) do
|
for _, message in ipairs(history_messages) do
|
||||||
@@ -1985,7 +1985,7 @@ end
|
|||||||
|
|
||||||
function Sidebar:compact_history_messages(args, cb)
|
function Sidebar:compact_history_messages(args, cb)
|
||||||
local history_memory = self.chat_history.memory
|
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.current_state = "compacting"
|
||||||
self:render_state()
|
self:render_state()
|
||||||
self:update_content(
|
self:update_content(
|
||||||
@@ -2023,7 +2023,7 @@ function Sidebar:save_history() debounced_save_history(self) end
|
|||||||
|
|
||||||
---@param uuids string[]
|
---@param uuids string[]
|
||||||
function Sidebar:delete_history_messages(uuids)
|
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
|
for _, msg in ipairs(history_messages) do
|
||||||
if vim.list_contains(uuids, msg.uuid) then msg.is_deleted = true end
|
if vim.list_contains(uuids, msg.uuid) then msg.is_deleted = true end
|
||||||
end
|
end
|
||||||
@@ -2041,7 +2041,7 @@ end
|
|||||||
|
|
||||||
---@param messages avante.HistoryMessage | avante.HistoryMessage[]
|
---@param messages avante.HistoryMessage | avante.HistoryMessage[]
|
||||||
function Sidebar:add_history_messages(messages)
|
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 }
|
messages = vim.islist(messages) and messages or { messages }
|
||||||
for _, message in ipairs(messages) do
|
for _, message in ipairs(messages) do
|
||||||
if message.is_user_submission then
|
if message.is_user_submission then
|
||||||
@@ -2110,7 +2110,7 @@ function Sidebar:add_chat_history(messages, options)
|
|||||||
self.chat_history.system_prompt = content
|
self.chat_history.system_prompt = content
|
||||||
goto continue
|
goto continue
|
||||||
end
|
end
|
||||||
local history_message = HistoryMessage:new(message)
|
local history_message = History.Message:new(message)
|
||||||
if message.role == "user" and is_first_user then
|
if message.role == "user" and is_first_user then
|
||||||
is_first_user = false
|
is_first_user = false
|
||||||
history_message.is_user_submission = true
|
history_message.is_user_submission = true
|
||||||
@@ -2254,8 +2254,7 @@ end
|
|||||||
---@return avante.HistoryMessage[]
|
---@return avante.HistoryMessage[]
|
||||||
function Sidebar:get_history_messages_for_api(opts)
|
function Sidebar:get_history_messages_for_api(opts)
|
||||||
opts = opts or {}
|
opts = opts or {}
|
||||||
local history_messages0 = Utils.get_history_messages(self.chat_history)
|
local history_messages0 = History.get_history_messages(self.chat_history)
|
||||||
self.chat_history.messages = history_messages0
|
|
||||||
|
|
||||||
history_messages0 = vim
|
history_messages0 = vim
|
||||||
.iter(history_messages0)
|
.iter(history_messages0)
|
||||||
@@ -2264,8 +2263,6 @@ function Sidebar:get_history_messages_for_api(opts)
|
|||||||
|
|
||||||
if opts.all then return history_messages0 end
|
if opts.all then return history_messages0 end
|
||||||
|
|
||||||
local use_ReAct_prompt = Providers[Config.provider].use_ReAct_prompt ~= nil
|
|
||||||
|
|
||||||
history_messages0 = vim
|
history_messages0 = vim
|
||||||
.iter(history_messages0)
|
.iter(history_messages0)
|
||||||
:filter(function(message) return message.state ~= "generating" end)
|
:filter(function(message) return message.state ~= "generating" end)
|
||||||
@@ -2281,258 +2278,11 @@ function Sidebar:get_history_messages_for_api(opts)
|
|||||||
history_messages0 = picked_messages
|
history_messages0 = picked_messages
|
||||||
end
|
end
|
||||||
|
|
||||||
local tool_id_to_tool_name = {}
|
return History.update_history_messages(
|
||||||
local tool_id_to_path = {}
|
history_messages0,
|
||||||
local tool_id_to_start_line = {}
|
Providers[Config.provider].use_ReAct_prompt ~= nil,
|
||||||
local tool_id_to_end_line = {}
|
Config.behaviour.auto_check_diagnostics
|
||||||
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
|
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param request string
|
---@param request string
|
||||||
@@ -2651,7 +2401,7 @@ function Sidebar:create_input_container()
|
|||||||
|
|
||||||
if self.is_generating then
|
if self.is_generating then
|
||||||
self:add_history_messages({
|
self:add_history_messages({
|
||||||
HistoryMessage:new({ role = "user", content = request }),
|
History.Message:new({ role = "user", content = request }),
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
@@ -2802,7 +2552,7 @@ function Sidebar:create_input_container()
|
|||||||
local msg_content = stop_opts.error
|
local msg_content = stop_opts.error
|
||||||
if type(msg_content) ~= "string" then msg_content = vim.inspect(msg_content) end
|
if type(msg_content) ~= "string" then msg_content = vim.inspect(msg_content) end
|
||||||
self:add_history_messages({
|
self:add_history_messages({
|
||||||
HistoryMessage:new({
|
History.Message:new({
|
||||||
role = "assistant",
|
role = "assistant",
|
||||||
content = "\n\nError: " .. msg_content,
|
content = "\n\nError: " .. msg_content,
|
||||||
}, {
|
}, {
|
||||||
@@ -2831,7 +2581,7 @@ function Sidebar:create_input_container()
|
|||||||
|
|
||||||
if request and request ~= "" then
|
if request and request ~= "" then
|
||||||
self:add_history_messages({
|
self:add_history_messages({
|
||||||
HistoryMessage:new({
|
History.Message:new({
|
||||||
role = "user",
|
role = "user",
|
||||||
content = request,
|
content = request,
|
||||||
}, {
|
}, {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ local Llm = require("avante.llm")
|
|||||||
local Highlights = require("avante.highlights")
|
local Highlights = require("avante.highlights")
|
||||||
local Config = require("avante.config")
|
local Config = require("avante.config")
|
||||||
local Providers = require("avante.providers")
|
local Providers = require("avante.providers")
|
||||||
local HistoryMessage = require("avante.history_message")
|
local HistoryMessage = require("avante.history.message")
|
||||||
local api = vim.api
|
local api = vim.api
|
||||||
local fn = vim.fn
|
local fn = vim.fn
|
||||||
|
|
||||||
|
|||||||
@@ -1473,41 +1473,6 @@ function M.get_commands()
|
|||||||
return vim.list_extend(builtin_commands, Config.slash_commands)
|
return vim.list_extend(builtin_commands, Config.slash_commands)
|
||||||
end
|
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
|
function M.get_timestamp() return tostring(os.date("%Y-%m-%d %H:%M:%S")) end
|
||||||
|
|
||||||
---@param history_messages avante.HistoryMessage[]
|
---@param history_messages avante.HistoryMessage[]
|
||||||
@@ -1530,60 +1495,6 @@ function M.uuid()
|
|||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param message avante.HistoryMessage
|
|
||||||
---@return boolean
|
|
||||||
function M.is_tool_use_message(message)
|
|
||||||
local content = message.message.content
|
|
||||||
if type(content) == "string" then return false end
|
|
||||||
if vim.islist(content) then
|
|
||||||
for _, item in ipairs(content) do
|
|
||||||
if item.type == "tool_use" then return true end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
---@param message avante.HistoryMessage
|
|
||||||
---@return boolean
|
|
||||||
function M.is_tool_result_message(message)
|
|
||||||
local content = message.message.content
|
|
||||||
if type(content) == "string" then return false end
|
|
||||||
if vim.islist(content) then
|
|
||||||
for _, item in ipairs(content) do
|
|
||||||
if item.type == "tool_result" then return true end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
---@param message avante.HistoryMessage
|
|
||||||
---@param messages avante.HistoryMessage[]
|
|
||||||
---@return avante.HistoryMessage | nil
|
|
||||||
function M.get_tool_use_message(message, messages)
|
|
||||||
local content = message.message.content
|
|
||||||
if type(content) == "string" then return nil end
|
|
||||||
if vim.islist(content) then
|
|
||||||
local tool_id = nil
|
|
||||||
for _, item in ipairs(content) do
|
|
||||||
if item.type == "tool_result" then
|
|
||||||
tool_id = item.tool_use_id
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if not tool_id then return nil end
|
|
||||||
for idx_ = #messages, 1, -1 do
|
|
||||||
local message_ = messages[idx_]
|
|
||||||
local content_ = message_.message.content
|
|
||||||
if type(content_) == "table" then
|
|
||||||
for _, item in ipairs(content_) do
|
|
||||||
if item.type == "tool_use" and item.id == tool_id then return message_ end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
---@param tool_use AvanteLLMToolUse
|
---@param tool_use AvanteLLMToolUse
|
||||||
function M.tool_use_to_xml(tool_use)
|
function M.tool_use_to_xml(tool_use)
|
||||||
local xml = string.format("<tool_use>\n<%s>\n", tool_use.name)
|
local xml = string.format("<tool_use>\n<%s>\n", tool_use.name)
|
||||||
@@ -1621,48 +1532,6 @@ function M.is_edit_func_call_tool_use(tool_use)
|
|||||||
return is_replace_func_call, is_str_replace_editor_func_call, is_str_replace_based_edit_tool_func_call, path
|
return is_replace_func_call, is_str_replace_editor_func_call, is_str_replace_based_edit_tool_func_call, path
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param tool_use_message avante.HistoryMessage | nil
|
|
||||||
function M.is_edit_func_call_message(tool_use_message)
|
|
||||||
local is_replace_func_call = false
|
|
||||||
local is_str_replace_editor_func_call = false
|
|
||||||
local is_str_replace_based_edit_tool_func_call = false
|
|
||||||
local path = nil
|
|
||||||
if tool_use_message and M.is_tool_use_message(tool_use_message) then
|
|
||||||
local tool_use = tool_use_message.message.content[1]
|
|
||||||
---@cast tool_use AvanteLLMToolUse
|
|
||||||
return M.is_edit_func_call_tool_use(tool_use)
|
|
||||||
end
|
|
||||||
return is_replace_func_call, is_str_replace_editor_func_call, is_str_replace_based_edit_tool_func_call, path
|
|
||||||
end
|
|
||||||
|
|
||||||
---@param message avante.HistoryMessage
|
|
||||||
---@param messages avante.HistoryMessage[]
|
|
||||||
---@return avante.HistoryMessage | nil
|
|
||||||
function M.get_tool_result_message(message, messages)
|
|
||||||
local content = message.message.content
|
|
||||||
if type(content) == "string" then return nil end
|
|
||||||
if vim.islist(content) then
|
|
||||||
local tool_id = nil
|
|
||||||
for _, item in ipairs(content) do
|
|
||||||
if item.type == "tool_use" then
|
|
||||||
tool_id = item.id
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if not tool_id then return nil end
|
|
||||||
for idx_ = #messages, 1, -1 do
|
|
||||||
local message_ = messages[idx_]
|
|
||||||
local content_ = message_.message.content
|
|
||||||
if type(content_) == "table" then
|
|
||||||
for _, item in ipairs(content_) do
|
|
||||||
if item.type == "tool_result" and item.tool_use_id == tool_id then return message_ end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
---@param text string
|
---@param text string
|
||||||
---@param hl string | nil
|
---@param hl string | nil
|
||||||
---@return avante.ui.Line[]
|
---@return avante.ui.Line[]
|
||||||
@@ -1701,6 +1570,7 @@ end
|
|||||||
---@return avante.ui.Line[]
|
---@return avante.ui.Line[]
|
||||||
function M.message_content_item_to_lines(item, message, messages)
|
function M.message_content_item_to_lines(item, message, messages)
|
||||||
local Line = require("avante.ui.line")
|
local Line = require("avante.ui.line")
|
||||||
|
local HistoryHelpers = require("avante.history.helpers")
|
||||||
if type(item) == "string" then return M.text_to_lines(item) end
|
if type(item) == "string" then return M.text_to_lines(item) end
|
||||||
if type(item) == "table" then
|
if type(item) == "table" then
|
||||||
if item.type == "thinking" or item.type == "redacted_thinking" then
|
if item.type == "thinking" or item.type == "redacted_thinking" then
|
||||||
@@ -1711,7 +1581,7 @@ function M.message_content_item_to_lines(item, message, messages)
|
|||||||
return { Line:new({ { "" } }) }
|
return { Line:new({ { "" } }) }
|
||||||
end
|
end
|
||||||
if item.type == "tool_use" then
|
if item.type == "tool_use" then
|
||||||
local tool_result_message = M.get_tool_result_message(message, messages)
|
local tool_result_message = HistoryHelpers.get_tool_result_message(message, messages)
|
||||||
local lines = {}
|
local lines = {}
|
||||||
local state = "generating"
|
local state = "generating"
|
||||||
local hl = "AvanteStateSpinnerToolCalling"
|
local hl = "AvanteStateSpinnerToolCalling"
|
||||||
@@ -1832,6 +1702,7 @@ end
|
|||||||
---@return AvantePartialLLMToolUse[]
|
---@return AvantePartialLLMToolUse[]
|
||||||
---@return avante.HistoryMessage[]
|
---@return avante.HistoryMessage[]
|
||||||
function M.get_uncalled_tool_uses(history_messages)
|
function M.get_uncalled_tool_uses(history_messages)
|
||||||
|
local HistoryHelpers = require("avante.history.helpers")
|
||||||
local last_turn_id = nil
|
local last_turn_id = nil
|
||||||
if #history_messages > 0 then last_turn_id = history_messages[#history_messages].turn_id end
|
if #history_messages > 0 then last_turn_id = history_messages[#history_messages].turn_id end
|
||||||
local uncalled_tool_uses = {} ---@type AvantePartialLLMToolUse[]
|
local uncalled_tool_uses = {} ---@type AvantePartialLLMToolUse[]
|
||||||
@@ -1841,8 +1712,8 @@ function M.get_uncalled_tool_uses(history_messages)
|
|||||||
local message = history_messages[idx]
|
local message = history_messages[idx]
|
||||||
if last_turn_id then
|
if last_turn_id then
|
||||||
if message.turn_id ~= last_turn_id then break end
|
if message.turn_id ~= last_turn_id then break end
|
||||||
else
|
elseif not HistoryHelpers.is_tool_use_message(message) and not HistoryHelpers.is_tool_result_message(message) then
|
||||||
if not M.is_tool_use_message(message) and not M.is_tool_result_message(message) then break end
|
break
|
||||||
end
|
end
|
||||||
local content = message.message.content
|
local content = message.message.content
|
||||||
if type(content) ~= "table" or #content == 0 then goto continue end
|
if type(content) ~= "table" or #content == 0 then goto continue end
|
||||||
|
|||||||
Reference in New Issue
Block a user