feat: tool logs (#1190)
This commit is contained in:
parent
252a9e7818
commit
4f41154e83
@ -146,7 +146,7 @@ M._stream = function(opts)
|
|||||||
on_chunk = opts.on_chunk,
|
on_chunk = opts.on_chunk,
|
||||||
on_stop = function(stop_opts)
|
on_stop = function(stop_opts)
|
||||||
if stop_opts.reason == "tool_use" and stop_opts.tool_use then
|
if stop_opts.reason == "tool_use" and stop_opts.tool_use then
|
||||||
local result, error = LLMTools.process_tool_use(stop_opts.tool_use)
|
local result, error = LLMTools.process_tool_use(stop_opts.tool_use, opts.on_tool_log)
|
||||||
local tool_result = {
|
local tool_result = {
|
||||||
tool_use_id = stop_opts.tool_use.id,
|
tool_use_id = stop_opts.tool_use.id,
|
||||||
content = error ~= nil and error or result,
|
content = error ~= nil and error or result,
|
||||||
@ -424,10 +424,18 @@ end
|
|||||||
---@field on_start AvanteLLMStartCallback
|
---@field on_start AvanteLLMStartCallback
|
||||||
---@field on_chunk AvanteLLMChunkCallback
|
---@field on_chunk AvanteLLMChunkCallback
|
||||||
---@field on_stop AvanteLLMStopCallback
|
---@field on_stop AvanteLLMStopCallback
|
||||||
|
---@field on_tool_log? function(tool_name: string, log: string): nil
|
||||||
|
|
||||||
---@param opts StreamOptions
|
---@param opts StreamOptions
|
||||||
M.stream = function(opts)
|
M.stream = function(opts)
|
||||||
local is_completed = false
|
local is_completed = false
|
||||||
|
if opts.on_tool_log ~= nil then
|
||||||
|
local original_on_tool_log = opts.on_tool_log
|
||||||
|
opts.on_tool_log = vim.schedule_wrap(function(tool_name, log)
|
||||||
|
if not original_on_tool_log then return end
|
||||||
|
return original_on_tool_log(tool_name, log)
|
||||||
|
end)
|
||||||
|
end
|
||||||
if opts.on_chunk ~= nil then
|
if opts.on_chunk ~= nil then
|
||||||
local original_on_chunk = opts.on_chunk
|
local original_on_chunk = opts.on_chunk
|
||||||
opts.on_chunk = vim.schedule_wrap(function(chunk)
|
opts.on_chunk = vim.schedule_wrap(function(chunk)
|
||||||
|
@ -28,11 +28,14 @@ local function has_permission_to_access(abs_path)
|
|||||||
end
|
end
|
||||||
|
|
||||||
---@param opts { rel_path: string, depth?: integer }
|
---@param opts { rel_path: string, depth?: integer }
|
||||||
|
---@param on_log? fun(log: string): nil
|
||||||
---@return string files
|
---@return string files
|
||||||
---@return string|nil error
|
---@return string|nil error
|
||||||
function M.list_files(opts)
|
function M.list_files(opts, on_log)
|
||||||
local abs_path = get_abs_path(opts.rel_path)
|
local abs_path = get_abs_path(opts.rel_path)
|
||||||
if not has_permission_to_access(abs_path) then return "", "No permission to access path: " .. abs_path end
|
if not has_permission_to_access(abs_path) then return "", "No permission to access path: " .. abs_path end
|
||||||
|
if on_log then on_log("path: " .. abs_path) end
|
||||||
|
if on_log then on_log("depth: " .. tostring(opts.depth)) end
|
||||||
local files = Utils.scan_directory_respect_gitignore({
|
local files = Utils.scan_directory_respect_gitignore({
|
||||||
directory = abs_path,
|
directory = abs_path,
|
||||||
add_dirs = true,
|
add_dirs = true,
|
||||||
@ -48,11 +51,14 @@ function M.list_files(opts)
|
|||||||
end
|
end
|
||||||
|
|
||||||
---@param opts { rel_path: string, keyword: string }
|
---@param opts { rel_path: string, keyword: string }
|
||||||
|
---@param on_log? fun(log: string): nil
|
||||||
---@return string files
|
---@return string files
|
||||||
---@return string|nil error
|
---@return string|nil error
|
||||||
function M.search_files(opts)
|
function M.search_files(opts, on_log)
|
||||||
local abs_path = get_abs_path(opts.rel_path)
|
local abs_path = get_abs_path(opts.rel_path)
|
||||||
if not has_permission_to_access(abs_path) then return "", "No permission to access path: " .. abs_path end
|
if not has_permission_to_access(abs_path) then return "", "No permission to access path: " .. abs_path end
|
||||||
|
if on_log then on_log("path: " .. abs_path) end
|
||||||
|
if on_log then on_log("keyword: " .. opts.keyword) end
|
||||||
local files = Utils.scan_directory_respect_gitignore({
|
local files = Utils.scan_directory_respect_gitignore({
|
||||||
directory = abs_path,
|
directory = abs_path,
|
||||||
})
|
})
|
||||||
@ -65,9 +71,10 @@ function M.search_files(opts)
|
|||||||
end
|
end
|
||||||
|
|
||||||
---@param opts { rel_path: string, keyword: string }
|
---@param opts { rel_path: string, keyword: string }
|
||||||
|
---@param on_log? fun(log: string): nil
|
||||||
---@return string result
|
---@return string result
|
||||||
---@return string|nil error
|
---@return string|nil error
|
||||||
function M.search(opts)
|
function M.search(opts, on_log)
|
||||||
local abs_path = get_abs_path(opts.rel_path)
|
local abs_path = get_abs_path(opts.rel_path)
|
||||||
if not has_permission_to_access(abs_path) then return "", "No permission to access path: " .. abs_path end
|
if not has_permission_to_access(abs_path) then return "", "No permission to access path: " .. abs_path end
|
||||||
if not Path:new(abs_path):exists() then return "", "No such file or directory: " .. abs_path end
|
if not Path:new(abs_path):exists() then return "", "No such file or directory: " .. abs_path end
|
||||||
@ -94,18 +101,22 @@ function M.search(opts)
|
|||||||
end
|
end
|
||||||
|
|
||||||
Utils.debug("cmd", cmd)
|
Utils.debug("cmd", cmd)
|
||||||
|
if on_log then on_log("Running command: " .. cmd) end
|
||||||
local result = vim.fn.system(cmd)
|
local result = vim.fn.system(cmd)
|
||||||
|
|
||||||
return result or "", nil
|
return result or "", nil
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param opts { rel_path: string }
|
---@param opts { rel_path: string }
|
||||||
|
---@param on_log? fun(log: string): nil
|
||||||
---@return string definitions
|
---@return string definitions
|
||||||
---@return string|nil error
|
---@return string|nil error
|
||||||
function M.read_file_toplevel_symbols(opts)
|
function M.read_file_toplevel_symbols(opts, on_log)
|
||||||
local RepoMap = require("avante.repo_map")
|
local RepoMap = require("avante.repo_map")
|
||||||
local abs_path = get_abs_path(opts.rel_path)
|
local abs_path = get_abs_path(opts.rel_path)
|
||||||
if not has_permission_to_access(abs_path) then return "", "No permission to access path: " .. abs_path end
|
if not has_permission_to_access(abs_path) then return "", "No permission to access path: " .. abs_path end
|
||||||
|
if on_log then on_log("path: " .. abs_path) end
|
||||||
|
if not Path:new(abs_path):exists() then return "", "File does not exists: " .. abs_path end
|
||||||
local filetype = RepoMap.get_ts_lang(abs_path)
|
local filetype = RepoMap.get_ts_lang(abs_path)
|
||||||
local repo_map_lib = RepoMap._init_repo_map_lib()
|
local repo_map_lib = RepoMap._init_repo_map_lib()
|
||||||
if not repo_map_lib then return "", "Failed to load avante_repo_map" end
|
if not repo_map_lib then return "", "Failed to load avante_repo_map" end
|
||||||
@ -116,11 +127,13 @@ function M.read_file_toplevel_symbols(opts)
|
|||||||
end
|
end
|
||||||
|
|
||||||
---@param opts { rel_path: string }
|
---@param opts { rel_path: string }
|
||||||
|
---@param on_log? fun(log: string): nil
|
||||||
---@return string content
|
---@return string content
|
||||||
---@return string|nil error
|
---@return string|nil error
|
||||||
function M.read_file(opts)
|
function M.read_file(opts, on_log)
|
||||||
local abs_path = get_abs_path(opts.rel_path)
|
local abs_path = get_abs_path(opts.rel_path)
|
||||||
if not has_permission_to_access(abs_path) then return "", "No permission to access path: " .. abs_path end
|
if not has_permission_to_access(abs_path) then return "", "No permission to access path: " .. abs_path end
|
||||||
|
if on_log then on_log("path: " .. abs_path) end
|
||||||
local file = io.open(abs_path, "r")
|
local file = io.open(abs_path, "r")
|
||||||
if not file then return "", "file not found: " .. abs_path end
|
if not file then return "", "file not found: " .. abs_path end
|
||||||
local content = file:read("*a")
|
local content = file:read("*a")
|
||||||
@ -129,11 +142,13 @@ function M.read_file(opts)
|
|||||||
end
|
end
|
||||||
|
|
||||||
---@param opts { rel_path: string }
|
---@param opts { rel_path: string }
|
||||||
|
---@param on_log? fun(log: string): nil
|
||||||
---@return boolean success
|
---@return boolean success
|
||||||
---@return string|nil error
|
---@return string|nil error
|
||||||
function M.create_file(opts)
|
function M.create_file(opts, on_log)
|
||||||
local abs_path = get_abs_path(opts.rel_path)
|
local abs_path = get_abs_path(opts.rel_path)
|
||||||
if not has_permission_to_access(abs_path) then return false, "No permission to access path: " .. abs_path end
|
if not has_permission_to_access(abs_path) then return false, "No permission to access path: " .. abs_path end
|
||||||
|
if on_log then on_log("path: " .. abs_path) end
|
||||||
---create directory if it doesn't exist
|
---create directory if it doesn't exist
|
||||||
local dir = Path:new(abs_path):parent()
|
local dir = Path:new(abs_path):parent()
|
||||||
if not dir:exists() then dir:mkdir({ parents = true }) end
|
if not dir:exists() then dir:mkdir({ parents = true }) end
|
||||||
@ -148,14 +163,16 @@ function M.create_file(opts)
|
|||||||
end
|
end
|
||||||
|
|
||||||
---@param opts { rel_path: string, new_rel_path: string }
|
---@param opts { rel_path: string, new_rel_path: string }
|
||||||
|
---@param on_log? fun(log: string): nil
|
||||||
---@return boolean success
|
---@return boolean success
|
||||||
---@return string|nil error
|
---@return string|nil error
|
||||||
function M.rename_file(opts)
|
function M.rename_file(opts, on_log)
|
||||||
local abs_path = get_abs_path(opts.rel_path)
|
local abs_path = get_abs_path(opts.rel_path)
|
||||||
if not has_permission_to_access(abs_path) then return false, "No permission to access path: " .. abs_path end
|
if not has_permission_to_access(abs_path) then return false, "No permission to access path: " .. abs_path end
|
||||||
if not Path:new(abs_path):exists() then return false, "File not found: " .. abs_path end
|
if not Path:new(abs_path):exists() then return false, "File not found: " .. abs_path end
|
||||||
if not Path:new(abs_path):is_file() then return false, "Path is not a file: " .. abs_path end
|
if not Path:new(abs_path):is_file() then return false, "Path is not a file: " .. abs_path end
|
||||||
local new_abs_path = get_abs_path(opts.new_rel_path)
|
local new_abs_path = get_abs_path(opts.new_rel_path)
|
||||||
|
if on_log then on_log(abs_path .. " -> " .. new_abs_path) end
|
||||||
if not has_permission_to_access(new_abs_path) then return false, "No permission to access path: " .. new_abs_path end
|
if not has_permission_to_access(new_abs_path) then return false, "No permission to access path: " .. new_abs_path end
|
||||||
if Path:new(new_abs_path):exists() then return false, "File already exists: " .. new_abs_path end
|
if Path:new(new_abs_path):exists() then return false, "File already exists: " .. new_abs_path end
|
||||||
if not M.confirm("Are you sure you want to rename the file: " .. abs_path .. " to: " .. new_abs_path) then
|
if not M.confirm("Are you sure you want to rename the file: " .. abs_path .. " to: " .. new_abs_path) then
|
||||||
@ -238,12 +255,14 @@ function M.delete_dir(opts)
|
|||||||
end
|
end
|
||||||
|
|
||||||
---@param opts { rel_path: string, command: string }
|
---@param opts { rel_path: string, command: string }
|
||||||
|
---@param on_log? fun(log: string): nil
|
||||||
---@return string|boolean result
|
---@return string|boolean result
|
||||||
---@return string|nil error
|
---@return string|nil error
|
||||||
function M.run_command(opts)
|
function M.run_command(opts, on_log)
|
||||||
local abs_path = get_abs_path(opts.rel_path)
|
local abs_path = get_abs_path(opts.rel_path)
|
||||||
if not has_permission_to_access(abs_path) then return false, "No permission to access path: " .. abs_path end
|
if not has_permission_to_access(abs_path) then return false, "No permission to access path: " .. abs_path end
|
||||||
if not Path:new(abs_path):exists() then return false, "Path not found: " .. abs_path end
|
if not Path:new(abs_path):exists() then return false, "Path not found: " .. abs_path end
|
||||||
|
if on_log then on_log("command: " .. opts.command) end
|
||||||
if
|
if
|
||||||
not M.confirm("Are you sure you want to run the command: `" .. opts.command .. "` in the directory: " .. abs_path)
|
not M.confirm("Are you sure you want to run the command: `" .. opts.command .. "` in the directory: " .. abs_path)
|
||||||
then
|
then
|
||||||
@ -262,9 +281,11 @@ function M.run_command(opts)
|
|||||||
end
|
end
|
||||||
|
|
||||||
---@param opts { query: string }
|
---@param opts { query: string }
|
||||||
|
---@param on_log? fun(log: string): nil
|
||||||
---@return string|nil result
|
---@return string|nil result
|
||||||
---@return string|nil error
|
---@return string|nil error
|
||||||
function M.web_search(opts)
|
function M.web_search(opts, on_log)
|
||||||
|
if on_log then on_log("query: " .. opts.query) end
|
||||||
local search_engine = Config.web_search_engine
|
local search_engine = Config.web_search_engine
|
||||||
if search_engine.provider == "tavily" then
|
if search_engine.provider == "tavily" then
|
||||||
if search_engine.api_key_name == "" then return nil, "No API key provided" end
|
if search_engine.api_key_name == "" then return nil, "No API key provided" end
|
||||||
@ -696,19 +717,34 @@ M.tools = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
---@param tool_use AvanteLLMToolUse
|
---@param tool_use AvanteLLMToolUse
|
||||||
|
---@param on_log? fun(tool_name: string, log: string): nil
|
||||||
---@return string | nil result
|
---@return string | nil result
|
||||||
---@return string | nil error
|
---@return string | nil error
|
||||||
function M.process_tool_use(tool_use)
|
function M.process_tool_use(tool_use, on_log)
|
||||||
Utils.debug("use tool", tool_use.name, tool_use.input_json)
|
Utils.debug("use tool", tool_use.name, tool_use.input_json)
|
||||||
local tool = vim.iter(M.tools):find(function(tool) return tool.name == tool_use.name end)
|
local tool = vim.iter(M.tools):find(function(tool) return tool.name == tool_use.name end)
|
||||||
if tool == nil then return end
|
if tool == nil then return end
|
||||||
local input_json = vim.json.decode(tool_use.input_json)
|
local input_json = vim.json.decode(tool_use.input_json)
|
||||||
local func = M[tool.name]
|
local func = M[tool.name]
|
||||||
local result, error = func(input_json)
|
if on_log then on_log(tool_use.name, "running tool") end
|
||||||
|
local result, error = func(input_json, function(log)
|
||||||
|
if on_log then on_log(tool_use.name, log) end
|
||||||
|
end)
|
||||||
|
if on_log then on_log(tool_use.name, "tool finished") end
|
||||||
-- Utils.debug("result", result)
|
-- Utils.debug("result", result)
|
||||||
-- Utils.debug("error", error)
|
-- Utils.debug("error", error)
|
||||||
|
if error ~= nil then
|
||||||
|
if on_log then on_log(tool_use.name, "Error: " .. error) end
|
||||||
|
end
|
||||||
if result ~= nil and type(result) ~= "string" then result = vim.json.encode(result) end
|
if result ~= nil and type(result) ~= "string" then result = vim.json.encode(result) end
|
||||||
return result, error
|
return result, error
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---@param tool_use AvanteLLMToolUse
|
||||||
|
---@return string
|
||||||
|
function M.stringify_tool_use(tool_use)
|
||||||
|
local s = string.format("`%s`", tool_use.name)
|
||||||
|
return s
|
||||||
|
end
|
||||||
|
|
||||||
return M
|
return M
|
||||||
|
@ -1810,6 +1810,17 @@ function Sidebar:create_input_container(opts)
|
|||||||
displayed_response = cur_displayed_response
|
displayed_response = cur_displayed_response
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local on_tool_log = function(tool_name, log)
|
||||||
|
if transformed_response:sub(-1) ~= "\n" then transformed_response = transformed_response .. "\n" end
|
||||||
|
transformed_response = transformed_response .. "[" .. tool_name .. "]: " .. log
|
||||||
|
local breakline = ""
|
||||||
|
if displayed_response:sub(-1) ~= "\n" then breakline = "\n" end
|
||||||
|
displayed_response = displayed_response .. breakline .. "[" .. tool_name .. "]: " .. log
|
||||||
|
self:update_content(content_prefix .. displayed_response .. "\n", {
|
||||||
|
scroll = scroll,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
---@type AvanteLLMStopCallback
|
---@type AvanteLLMStopCallback
|
||||||
local on_stop = function(stop_opts)
|
local on_stop = function(stop_opts)
|
||||||
pcall(function()
|
pcall(function()
|
||||||
@ -1870,6 +1881,7 @@ function Sidebar:create_input_container(opts)
|
|||||||
on_start = on_start,
|
on_start = on_start,
|
||||||
on_chunk = on_chunk,
|
on_chunk = on_chunk,
|
||||||
on_stop = on_stop,
|
on_stop = on_stop,
|
||||||
|
on_tool_log = on_tool_log,
|
||||||
})
|
})
|
||||||
|
|
||||||
Llm.stream(stream_options)
|
Llm.stream(stream_options)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user