152 lines
5.1 KiB
Lua
152 lines
5.1 KiB
Lua
local adapters = require("codecompanion.adapters")
|
|
local client = require("codecompanion.http")
|
|
local config = require("codecompanion.config")
|
|
local log = require("codecompanion.utils.log")
|
|
|
|
local fmt = string.format
|
|
|
|
---@class CodeCompanion.Tool.FetchWebpage: CodeCompanion.Tools.Tool
|
|
return {
|
|
name = "fetch_webpage",
|
|
cmds = {
|
|
---Execute the fetch_webpage tool
|
|
---@param self CodeCompanion.Tools
|
|
---@param args table The arguments from the LLM's tool call
|
|
---@param cb function Async callback for completion
|
|
---@return nil
|
|
function(self, args, _, cb)
|
|
local opts = self.tool.opts
|
|
local url = args.url
|
|
|
|
if not opts or not opts.adapter then
|
|
log:error("[Fetch Webpage Tool] No adapter set for `fetch_webpage`")
|
|
return cb({ status = "error", data = "No adapter for `fetch_webpage`" })
|
|
end
|
|
if not args then
|
|
log:error("[Fetch Webpage Tool] No args for `fetch_webpage`")
|
|
return cb({ status = "error", data = "No args for `fetch_webpage`" })
|
|
end
|
|
|
|
if not url or type(url) ~= "string" or url == "" then
|
|
return cb({ status = "error", data = fmt("No URL for `fetch_webpage`") })
|
|
end
|
|
|
|
local tool_adapter = config.strategies.chat.tools.fetch_webpage.opts.adapter
|
|
local adapter = vim.deepcopy(adapters.resolve(tool_adapter))
|
|
adapter.methods.tools.fetch_webpage.setup(adapter, args)
|
|
|
|
if not url:match("^https?://") then
|
|
log:error("[Fetch Webpage Tool] Invalid URL: `%s`", url)
|
|
return cb({ status = "error", data = fmt("Invalid URL: `%s`", url) })
|
|
end
|
|
|
|
client
|
|
.new({
|
|
adapter = adapter,
|
|
})
|
|
:request(_, {
|
|
callback = function(err, data)
|
|
if err then
|
|
log:error("[Fetch Webpage Tool] Error fetching `%s`: %s", url, err)
|
|
return cb({ status = "error", data = fmt("Error fetching `%s`\n%s", url, err) })
|
|
end
|
|
|
|
if data then
|
|
local output = adapter.methods.tools.fetch_webpage.callback(adapter, data)
|
|
if output.status == "error" then
|
|
log:error("[Fetch Webpage Tool] Error processing data for `%s`: %s", url, output.content)
|
|
return cb({ status = "error", data = fmt("Error processing `%s`\n%s", url, output.content) })
|
|
end
|
|
|
|
return cb({ status = "success", data = output.content })
|
|
end
|
|
end,
|
|
})
|
|
end,
|
|
},
|
|
schema = {
|
|
type = "function",
|
|
["function"] = {
|
|
name = "fetch_webpage",
|
|
description = "Fetches the main content from a web page. You should use this tool when you think the user is looking for information from a specific webpage.",
|
|
parameters = {
|
|
type = "object",
|
|
properties = {
|
|
url = {
|
|
type = "string",
|
|
description = "The URL of the webpage to fetch content from",
|
|
},
|
|
},
|
|
required = { "url" },
|
|
},
|
|
},
|
|
},
|
|
output = {
|
|
---@param self CodeCompanion.Tool.FetchWebpage
|
|
---@param tools CodeCompanion.Tools
|
|
---@param cmd table The command that was executed
|
|
---@param stdout table The output from the command
|
|
success = function(self, tools, cmd, stdout)
|
|
local args = self.args
|
|
local chat = tools.chat
|
|
|
|
local content
|
|
if type(stdout) == "table" then
|
|
if #stdout == 1 and type(stdout[1]) == "string" then
|
|
content = stdout[1]
|
|
elseif #stdout == 1 and type(stdout[1]) == "table" then
|
|
-- If stdout[1] is a table, try to extract content
|
|
local first_item = stdout[1]
|
|
if type(first_item) == "table" and first_item.content then
|
|
content = first_item.content
|
|
else
|
|
-- Fallback: convert to string representation
|
|
content = vim.inspect(first_item)
|
|
end
|
|
else
|
|
-- Multiple items or other structure
|
|
content = vim
|
|
.iter(stdout)
|
|
:map(function(item)
|
|
if type(item) == "string" then
|
|
return item
|
|
elseif type(item) == "table" and item.content then
|
|
return item.content
|
|
else
|
|
return vim.inspect(item)
|
|
end
|
|
end)
|
|
:join("\n")
|
|
end
|
|
else
|
|
content = tostring(stdout)
|
|
end
|
|
|
|
local llm_output = fmt([[<attachment url="%s">%s</attachment>]], args.url, content)
|
|
local user_output = fmt("Fetched content from `%s`", args.url)
|
|
|
|
chat:add_tool_output(self, llm_output, user_output)
|
|
end,
|
|
|
|
---@param self CodeCompanion.Tool.FetchWebpage
|
|
---@param tools CodeCompanion.Tools
|
|
---@param cmd table
|
|
---@param stderr table The error output from the command
|
|
error = function(self, tools, cmd, stderr)
|
|
local args = self.args
|
|
local chat = tools.chat
|
|
local errors = vim.iter(stderr):flatten():join("\n")
|
|
log:debug("[Fetch Webpage Tool] Error output: %s", stderr)
|
|
|
|
local error_output = fmt(
|
|
[[Error fetching content from `%s`:
|
|
```txt
|
|
%s
|
|
```]],
|
|
args.url,
|
|
errors
|
|
)
|
|
chat:add_tool_output(self, error_output)
|
|
end,
|
|
},
|
|
}
|