Skip to content

Commit 8328938

Browse files
Copilotolimorris
andcommitted
Fix JSON error handling to match original behavior for malformed tool arguments
Co-authored-by: olimorris <[email protected]>
1 parent 7ea5598 commit 8328938

File tree

1 file changed

+36
-32
lines changed
  • lua/codecompanion/strategies/chat/tools

1 file changed

+36
-32
lines changed

lua/codecompanion/strategies/chat/tools/init.lua

Lines changed: 36 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -50,28 +50,6 @@ function Tools:_pattern(tool)
5050
return CONSTANTS.PREFIX .. "{" .. tool .. "}"
5151
end
5252

53-
---Parse and normalize tool arguments
54-
---@param tool table The tool with function and arguments
55-
---@return table|nil args The parsed arguments or nil if parsing failed
56-
---@return string|nil error_msg Error message if parsing failed
57-
function Tools:_parse_tool_arguments(tool)
58-
local args = tool["function"].arguments
59-
if not args then
60-
return nil, nil
61-
end
62-
63-
if type(args) == "string" then
64-
local success, decoded = pcall(vim.json.decode, args)
65-
if success then
66-
return decoded, nil
67-
else
68-
local tool_name = tool["function"].name or "unknown"
69-
return nil, string.format("Couldn't decode JSON arguments for tool '%s': %s", tool_name, args)
70-
end
71-
end
72-
73-
return args, nil
74-
end
7553

7654
---Handle missing or invalid tool errors
7755
---@param tool table The tool that failed
@@ -110,32 +88,53 @@ end
11088
---@param tool table The tool call from the LLM
11189
---@return table|nil resolved_tool The resolved tool or nil if failed
11290
---@return string|nil error_msg Error message if resolution failed
91+
---@return boolean|nil is_json_error Whether this is a JSON parsing error that needs special handling
11392
function Tools:_resolve_and_prepare_tool(tool)
11493
local name = tool["function"].name
11594
local tool_config = self.tools_config[name]
11695

11796
if not tool_config then
118-
return nil, string.format("Couldn't find the tool `%s`", name)
97+
return nil, string.format("Couldn't find the tool `%s`", name), false
11998
end
12099

121100
local ok, resolved_tool = pcall(function()
122101
return Tools.resolve(tool_config)
123102
end)
124103

125104
if not ok or not resolved_tool then
126-
return nil, string.format("Couldn't resolve the tool `%s`", name)
105+
return nil, string.format("Couldn't resolve the tool `%s`", name), false
127106
end
128107

129108
local prepared_tool = vim.deepcopy(resolved_tool)
130109
prepared_tool.name = name
131110
prepared_tool.function_call = tool
132111

133-
-- Parse and set arguments
134-
local args, parse_error = self:_parse_tool_arguments(tool)
135-
if parse_error then
136-
return nil, parse_error
112+
-- Parse and set arguments - handle JSON errors specially like the original code
113+
if tool["function"].arguments then
114+
local args = tool["function"].arguments
115+
-- For some adapter's that aren't streaming, the args are strings rather than tables
116+
if type(args) == "string" then
117+
local decoded
118+
local json_ok = xpcall(function()
119+
decoded = vim.json.decode(args)
120+
end, function(err)
121+
log:error("Couldn't decode the tool arguments: %s", args)
122+
self.chat:add_tool_output(
123+
prepared_tool,
124+
string.format('You made an error in calling the %s tool: "%s"', name, err),
125+
""
126+
)
127+
return util.fire("ToolsFinished", { bufnr = self.bufnr })
128+
end)
129+
130+
if not json_ok then
131+
return nil, "JSON parsing failed", true -- Special flag to indicate this was handled
132+
end
133+
134+
args = decoded
135+
end
136+
prepared_tool.args = args
137137
end
138-
prepared_tool.args = args
139138

140139
-- Merge options
141140
prepared_tool.opts = vim.tbl_extend("force", prepared_tool.opts or {}, tool_config.opts or {})
@@ -146,7 +145,7 @@ function Tools:_resolve_and_prepare_tool(tool)
146145
util.replace_placeholders(prepared_tool.cmds, env)
147146
end
148147

149-
return prepared_tool, nil
148+
return prepared_tool, nil, false
150149
end
151150

152151
---Start edit tracking for all tools
@@ -267,10 +266,15 @@ function Tools:execute(chat, tools)
267266

268267
-- Process each tool
269268
for _, tool in ipairs(tools) do
270-
local resolved_tool, error_msg = self:_resolve_and_prepare_tool(tool)
269+
local resolved_tool, error_msg, is_json_error = self:_resolve_and_prepare_tool(tool)
271270

272271
if not resolved_tool then
273-
return self:_handle_tool_error(tool, error_msg)
272+
if is_json_error then
273+
-- JSON error was already handled by _resolve_and_prepare_tool
274+
return
275+
else
276+
return self:_handle_tool_error(tool, error_msg)
277+
end
274278
end
275279

276280
self.tool = resolved_tool

0 commit comments

Comments
 (0)