fix: react prompts (#2537)

This commit is contained in:
yetone
2025-07-26 16:06:56 +08:00
committed by GitHub
parent bd69ae14f6
commit 8bc149ccd8
14 changed files with 448 additions and 173 deletions

View File

@@ -44,6 +44,7 @@ struct TemplateContext {
model_name: Option<String>,
memory: Option<String>,
todos: Option<String>,
enable_fastapply: Option<bool>,
}
// Given the file name registered after add, the context table in Lua, resulted in a formatted
@@ -72,6 +73,7 @@ fn render(state: &State, template: &str, context: TemplateContext) -> LuaResult<
model_name => context.model_name,
memory => context.memory,
todos => context.todos,
enable_fastapply => context.enable_fastapply,
})
.map_err(LuaError::external)
.unwrap())

View File

@@ -1,14 +1,3 @@
---@class avante.TextContent
---@field type "text"
---@field text string
---@field partial boolean
---
---@class avante.ToolUseContent
---@field type "tool_use"
---@field tool_name string
---@field tool_input table
---@field partial boolean
local M = {}
-- Helper function to parse a parameter tag like <param_name>value</param_name>

View File

@@ -0,0 +1,261 @@
local JsonParser = require("avante.libs.jsonparser")
---@class avante.TextContent
---@field type "text"
---@field text string
---@field partial boolean
---
---@class avante.ToolUseContent
---@field type "tool_use"
---@field tool_name string
---@field tool_input table
---@field partial boolean
local M = {}
--- Parse the text into a list of TextContent and ToolUseContent
--- The text is a string.
--- For example:
--- parse([[Hello, world!]])
--- returns
--- {
--- {
--- type = "text",
--- text = "Hello, world!",
--- partial = false,
--- },
--- }
---
--- parse([[Hello, world! I am a tool.<tool_use>{"name": "write", "input": {"path": "path/to/file.txt", "content": "foo"}}</tool_use>]])
--- returns
--- {
--- {
--- type = "text",
--- text = "Hello, world! I am a tool.",
--- partial = false,
--- },
--- {
--- type = "tool_use",
--- tool_name = "write",
--- tool_input = {
--- path = "path/to/file.txt",
--- content = "foo",
--- },
--- partial = false,
--- },
--- }
---
--- parse([[Hello, world! I am a tool.<tool_use>{"name": "write", "input": {"path": "path/to/file.txt", "content": "foo"}}</tool_use>I am another tool.<tool_use>{"name": "write", "input": {"path": "path/to/file.txt", "content": "bar"}}</tool_use>hello]])
--- returns
--- {
--- {
--- type = "text",
--- text = "Hello, world! I am a tool.",
--- partial = false,
--- },
--- {
--- type = "tool_use",
--- tool_name = "write",
--- tool_input = {
--- path = "path/to/file.txt",
--- content = "foo",
--- },
--- partial = false,
--- },
--- {
--- type = "text",
--- text = "I am another tool.",
--- partial = false,
--- },
--- {
--- type = "tool_use",
--- tool_name = "write",
--- tool_input = {
--- path = "path/to/file.txt",
--- content = "bar",
--- },
--- partial = false,
--- },
--- {
--- type = "text",
--- text = "hello",
--- partial = false,
--- },
--- }
---
--- parse([[Hello, world! I am a tool.<tool_use>{"name"]])
--- returns
--- {
--- {
--- type = "text",
--- text = "Hello, world! I am a tool.",
--- partial = false,
--- }
--- }
---
--- parse([[Hello, world! I am a tool.<tool_use>{"name": "write"]])
--- returns
--- {
--- {
--- type = "text",
--- text = "Hello, world! I am a tool.",
--- partial = false,
--- },
--- {
--- type = "tool_use",
--- tool_name = "write",
--- tool_input = {},
--- partial = true,
--- },
--- }
---
--- parse([[Hello, world! I am a tool.<tool_use>{"name": "write", "input": {"path": "path/to/file.txt"]])
--- returns
--- {
--- {
--- type = "text",
--- text = "Hello, world! I am a tool.",
--- partial = false,
--- },
--- {
--- type = "tool_use",
--- tool_name = "write",
--- tool_input = {
--- path = "path/to/file.txt",
--- },
--- partial = true,
--- },
--- }
---
--- parse([[Hello, world! I am a tool.<tool_use>{"name": "write", "input": {"path": "path/to/file.txt", "content": "foo bar]])
--- returns
--- {
--- {
--- type = "text",
--- text = "Hello, world! I am a tool.",
--- partial = false,
--- },
--- {
--- type = "tool_use",
--- tool_name = "write",
--- tool_input = {
--- path = "path/to/file.txt",
--- content = "foo bar",
--- },
--- partial = true,
--- },
--- }
---
--- parse([[Hello, world! I am a tool.{"name": "write", "input": {"path": "path/to/file.txt", "content": foo bar]])
--- returns
--- {
--- {
--- type = "text",
--- text = [[Hello, world! I am a tool.{"name": "write", "input": {"path": "path/to/file.txt", "content": foo bar]],
--- partial = false,
--- }
--- }
---
---@param text string
---@return (avante.TextContent|avante.ToolUseContent)[]
function M.parse(text)
local result = {}
local pos = 1
local len = #text
while pos <= len do
local tool_start = text:find("<tool_use>", pos, true)
if not tool_start then
-- No more tool_use tags, add remaining text if any
if pos <= len then
local remaining_text = text:sub(pos)
if remaining_text ~= "" then
table.insert(result, {
type = "text",
text = remaining_text,
partial = false,
})
end
end
break
end
-- Add text before tool_use tag if any
if tool_start > pos then
local text_content = text:sub(pos, tool_start - 1)
if text_content ~= "" then
table.insert(result, {
type = "text",
text = text_content,
partial = false,
})
end
end
-- Find the closing tag
local json_start = tool_start + 10 -- length of "<tool_use>"
local tool_end = text:find("</tool_use>", json_start, true)
if not tool_end then
-- No closing tag found, treat as partial tool_use
local json_text = text:sub(json_start)
json_text = json_text:gsub("^\n+", "")
json_text = json_text:gsub("\n+$", "")
json_text = json_text:gsub("^%s+", "")
json_text = json_text:gsub("%s+$", "")
-- Try to parse complete JSON first
local success, json_data = pcall(function() return vim.json.decode(json_text) end)
if success and json_data and json_data.name then
table.insert(result, {
type = "tool_use",
tool_name = json_data.name,
tool_input = json_data.input or {},
partial = true,
})
else
local jsn = JsonParser.parse(json_text)
if jsn and jsn.name then
table.insert(result, {
type = "tool_use",
tool_name = jsn.name,
tool_input = jsn.input or {},
partial = true,
})
end
end
break
end
-- Extract JSON content
local json_text = text:sub(json_start, tool_end - 1)
local success, json_data = pcall(function() return vim.json.decode(json_text) end)
if success and json_data and json_data.name then
table.insert(result, {
type = "tool_use",
tool_name = json_data.name,
tool_input = json_data.input or {},
partial = false,
})
pos = tool_end + 11 -- length of "</tool_use>"
else
-- Invalid JSON, treat the whole thing as text
local invalid_text = text:sub(tool_start, tool_end + 10)
table.insert(result, {
type = "text",
text = invalid_text,
partial = false,
})
pos = tool_end + 11
end
end
return result
end
return M

View File

@@ -307,6 +307,7 @@ function M.generate_prompts(opts)
system_info = system_info,
model_name = provider.model or "unknown",
memory = opts.memory,
enable_fastapply = Config.behaviour.enable_fastapply,
}
-- Removed the original todos processing logic, now handled in context_messages
@@ -804,7 +805,7 @@ function M._stream(opts)
return
end
end
M._stream(new_opts)
if not streaming_tool_use then M._stream(new_opts) end
return
end
local partial_tool_use = tool_uses[tool_use_index]
@@ -849,15 +850,14 @@ function M._stream(opts)
streaming = partial_tool_use.state == "generating",
on_complete = function() end,
}
if partial_tool_use.state == "generating" and not is_edit_tool_use and not support_streaming then return end
if partial_tool_use.state == "generating" then
if not is_edit_tool_use and not support_streaming then return end
if type(partial_tool_use.input) == "table" then
LLMTools.process_tool_use(prompt_opts.tools, partial_tool_use, tool_use_opts)
end
return
else
if streaming_tool_use then return end
end
if streaming_tool_use then return end
partial_tool_use_message.is_calling = true
if opts.on_messages_add then opts.on_messages_add({ partial_tool_use_message }) end
-- Either on_complete handles the tool result asynchronously or we receive the result and error synchronously when either is not nil
@@ -887,12 +887,11 @@ function M._stream(opts)
local completed_attempt_completion_tool_use = nil
for idx = #history_messages, 1, -1 do
local message = history_messages[idx]
if not message.is_user_submission then
local use = History.Helpers.get_tool_use_data(message)
if use and use.name == "attempt_completion" then
completed_attempt_completion_tool_use = message
break
end
if message.is_user_submission then break end
local use = History.Helpers.get_tool_use_data(message)
if use and use.name == "attempt_completion" then
completed_attempt_completion_tool_use = message
break
end
end
local unfinished_todos = {}
@@ -915,7 +914,7 @@ function M._stream(opts)
if #unfinished_todos > 0 then
message = History.Message:new(
"user",
"<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>",
"<system-reminder>You should use tool calls to answer the question, for example, use update_todo_status if the task step is done or cancelled.</system-reminder>",
{
visible = false,
}
@@ -923,7 +922,7 @@ function M._stream(opts)
else
message = History.Message:new(
"user",
"<user-reminder>You should use tool calls to answer the question, for example, use attempt_completion if the job is done.</user-reminder>",
"<system-reminder>You should use tool calls to answer the question, for example, use attempt_completion if the job is done.</system-reminder>",
{
visible = false,
}

View File

@@ -53,6 +53,10 @@ M.returns = {
---@type AvanteLLMToolFunc<{ path: string, instructions: string, code_edit: string }>
M.func = vim.schedule_wrap(function(input, opts)
if opts.streaming then return false, "streaming not supported" end
if not input.path then return false, "path not provided" end
if not input.instructions then input.instructions = "" end
if not input.code_edit then return false, "code_edit not provided" end
local on_complete = opts.on_complete
if not on_complete then return false, "on_complete not provided" end
local provider = Providers["morph"]

View File

@@ -1274,7 +1274,7 @@ function M.process_tool_use(tools, tool_use, opts)
else
---@type AvanteLLMTool?
local tool = vim.iter(tools):find(function(tool) return tool.name == tool_use.name end) ---@param tool AvanteLLMTool
if tool == nil then return nil, "This tool is not provided: " .. tool_use.name end
if tool == nil then return nil, "This tool is not provided: " .. vim.inspect(tool_use.name) end
func = tool.func or M[tool.name]
end
local input_json = tool_use.input

View File

@@ -50,7 +50,7 @@ function M.func(input, opts)
local todos = sidebar.chat_history.todos
if not todos or #todos == 0 then return false, "No todos found" end
for _, todo in ipairs(todos) do
if todo.id == input.id then
if tostring(todo.id) == tostring(input.id) then
todo.status = input.status
break
end

View File

@@ -10,6 +10,8 @@ M.name = "write_to_file"
M.description =
"Request to write content to a file at the specified path. If the file exists, it will be overwritten with the provided content. If the file doesn't exist, it will be created. This tool will automatically create any directories needed to write the file."
M.support_streaming = false
function M.enabled()
return require("avante.config").mode == "agentic" and not require("avante.config").behaviour.enable_fastapply
end

View File

@@ -113,29 +113,25 @@ function M:parse_messages(opts)
end
if not provider_conf.disable_tools and use_ReAct_prompt then
if content_items[1].type == "tool_result" then
local tool_use = nil
local tool_use_msg = nil
for _, msg_ in ipairs(opts.messages) do
if type(msg_.content) == "table" and #msg_.content > 0 then
if msg_.content[1].type == "tool_use" and msg_.content[1].id == content_items[1].tool_use_id then
tool_use = msg_
tool_use_msg = msg_
break
end
end
end
if tool_use then
if tool_use_msg then
table.insert(contents, {
role = "model",
parts = {
{ text = Utils.tool_use_to_xml(tool_use.content[1]) },
{ text = Utils.tool_use_to_xml(tool_use_msg.content[1]) },
},
})
role = "user"
table.insert(parts, {
text = "["
.. tool_use.content[1].name
.. " for '"
.. (tool_use.content[1].input.path or tool_use.content[1].input.rel_path or "")
.. "'] Result:",
text = "The result of tool use " .. Utils.tool_use_to_xml(tool_use_msg.content[1]) .. " is:\n",
})
table.insert(parts, {
text = content_items[1].content,
@@ -189,6 +185,8 @@ function M.prepare_request_body(provider_instance, prompt_opts, provider_conf, r
local use_ReAct_prompt = provider_conf.use_ReAct_prompt == true
if use_ReAct_prompt then request_body.generationConfig.stopSequences = { "</tool_use>" } end
local disable_tools = provider_conf.disable_tools or false
if not use_ReAct_prompt and not disable_tools and prompt_opts.tools then

View File

@@ -3,7 +3,7 @@ local Config = require("avante.config")
local Clipboard = require("avante.clipboard")
local Providers = require("avante.providers")
local HistoryMessage = require("avante.history.message")
local ReActParser = require("avante.libs.ReAct_parser")
local ReActParser = require("avante.libs.ReAct_parser2")
local JsonParser = require("avante.libs.jsonparser")
local Prompts = require("avante.utils.prompts")
local LlmTools = require("avante.llm_tools")
@@ -130,24 +130,20 @@ function M:parse_messages(opts)
end
if not provider_conf.disable_tools and use_ReAct_prompt then
if msg.content[1].type == "tool_result" then
local tool_use = nil
local tool_use_msg = nil
for _, msg_ in ipairs(opts.messages) do
if type(msg_.content) == "table" and #msg_.content > 0 then
if msg_.content[1].type == "tool_use" and msg_.content[1].id == msg.content[1].tool_use_id then
tool_use = msg_
tool_use_msg = msg_
break
end
end
end
if tool_use then
if tool_use_msg then
msg.role = "user"
table.insert(content, {
type = "text",
text = "["
.. tool_use.content[1].name
.. " for '"
.. (tool_use.content[1].input.path or tool_use.content[1].input.rel_path or "")
.. "'] Result:",
text = "The result of tool use " .. Utils.tool_use_to_xml(tool_use_msg.content[1]) .. " is:\n",
})
table.insert(content, {
type = "text",
@@ -258,7 +254,6 @@ function M:add_text_message(ctx, text, state, opts)
end
local cleaned_xml_content = table.concat(cleaned_xml_lines, "\n")
local xml = ReActParser.parse(cleaned_xml_content)
local has_tool_use = false
if xml and #xml > 0 then
local new_content_list = {}
local xml_md_openned = false
@@ -293,42 +288,45 @@ function M:add_text_message(ctx, text, state, opts)
end
end
if next(input) ~= nil then
has_tool_use = true
local msg_uuid = ctx.content_uuid .. "-" .. idx
local tool_use_id = msg_uuid
local tool_message_state = item.partial and "generating" or "generated"
local msg_ = HistoryMessage:new("assistant", {
type = "tool_use",
name = item.tool_name,
id = tool_use_id,
input = input,
}, {
state = state,
state = tool_message_state,
uuid = msg_uuid,
turn_id = ctx.turn_id,
})
msgs[#msgs + 1] = msg_
ctx.tool_use_list = ctx.tool_use_list or {}
local input_json = type(input) == "string" and input or vim.json.encode(input)
local exists = false
for _, tool_use in ipairs(ctx.tool_use_list) do
if tool_use.id == tool_use_id then
tool_use.input_json = input
tool_use.input_json = input_json
exists = true
end
end
if not exists then
ctx.tool_use_list[#ctx.tool_use_list + 1] = {
uuid = tool_use_id,
id = tool_use_id,
name = item.tool_name,
input_json = input,
input_json = input_json,
state = "generating",
}
end
opts.on_stop({ reason = "tool_use", streaming_tool_use = item.partial })
end
::continue::
end
msg.message.content = table.concat(new_content_list, "\n")
msg.message.content = table.concat(new_content_list, "\n"):gsub("\n+$", "\n")
end
if opts.on_messages_add then opts.on_messages_add(msgs) end
if has_tool_use and state == "generating" then opts.on_stop({ reason = "tool_use", streaming_tool_use = true }) end
end
function M:add_thinking_message(ctx, text, state, opts)
@@ -537,6 +535,9 @@ function M:parse_curl_args(prompt_opts)
Utils.debug("endpoint", provider_conf.endpoint)
Utils.debug("model", provider_conf.model)
local stop = nil
if use_ReAct_prompt then stop = { "</tool_use>" } end
return {
url = Utils.url_join(provider_conf.endpoint, "/chat/completions"),
proxy = provider_conf.proxy,
@@ -545,6 +546,7 @@ function M:parse_curl_args(prompt_opts)
body = vim.tbl_deep_extend("force", {
model = provider_conf.model,
messages = self:parse_messages(prompt_opts),
stop = stop,
stream = true,
stream_options = not M.is_mistral(provider_conf.endpoint) and {
include_usage = true,

View File

@@ -3147,6 +3147,10 @@ function Sidebar:create_todos_container()
local total_count = #history.todos
local focused_idx = 1
local todos_content_lines = {}
if type(history.todos) ~= "table" then
Utils.debug("Invalid todos type", history.todos)
history.todos = {}
end
for idx, todo in ipairs(history.todos) do
local status_content = "[ ]"
if todo.status == "done" then
@@ -3163,7 +3167,7 @@ function Sidebar:create_todos_container()
local todos_buf = api.nvim_win_get_buf(self.containers.todos.winid)
Utils.unlock_buf(todos_buf)
api.nvim_buf_set_lines(todos_buf, 0, -1, false, todos_content_lines)
api.nvim_win_set_cursor(self.containers.todos.winid, { focused_idx, 0 })
pcall(function() api.nvim_win_set_cursor(self.containers.todos.winid, { focused_idx, 0 }) end)
Utils.lock_buf(todos_buf)
self:render_header(
self.containers.todos.winid,

View File

@@ -10,6 +10,82 @@
{%- include "_task-guidelines.avanterules" %}
{% if not enable_fastapply -%}
====
EDITING FILES
You have access to two tools for working with files: **write_to_file** and **replace_in_file**. Understanding their roles and selecting the right one for the job will help ensure efficient and accurate modifications.
# write_to_file
## Purpose
- Create a new file, or overwrite the entire contents of an existing file.
## When to Use
- Initial file creation, such as when scaffolding a new project.
- Overwriting large boilerplate files where you want to replace the entire content at once.
- When the complexity or number of changes would make replace_in_file unwieldy or error-prone.
- When you need to completely restructure a file's content or change its fundamental organization.
## Important Considerations
- Using write_to_file requires providing the file's complete final content.
- If you only need to make small changes to an existing file, consider using replace_in_file instead to avoid unnecessarily rewriting the entire file.
- While write_to_file should not be your default choice, don't hesitate to use it when the situation truly calls for it.
# replace_in_file
## Purpose
- Make targeted edits to specific parts of an existing file without overwriting the entire file.
## When to Use
- Small, localized changes like updating a few lines, function implementations, changing variable names, modifying a section of text, etc.
- Targeted improvements where only specific portions of the file's content needs to be altered.
- Especially useful for long files where much of the file will remain unchanged.
## Advantages
- More efficient for minor edits, since you don't need to supply the entire file content.
- Reduces the chance of errors that can occur when overwriting large files.
# Choosing the Appropriate Tool
- **Default to replace_in_file** for most changes. It's the safer, more precise option that minimizes potential issues.
- **Use write_to_file** when:
- Creating new files
- The changes are so extensive that using replace_in_file would be more complex or risky
- You need to completely reorganize or restructure a file
- The file is relatively small and the changes affect most of its content
- You're generating boilerplate or template files
# Auto-formatting Considerations
- After using either write_to_file or replace_in_file, the user's editor may automatically format the file
- This auto-formatting may modify the file contents, for example:
- Breaking single lines into multiple lines
- Adjusting indentation to match project style (e.g. 2 spaces vs 4 spaces vs tabs)
- Converting single quotes to double quotes (or vice versa based on project preferences)
- Organizing imports (e.g. sorting, grouping by type)
- Adding/removing trailing commas in objects and arrays
- Enforcing consistent brace style (e.g. same-line vs new-line)
- Standardizing semicolon usage (adding or removing based on style)
- The write_to_file and replace_in_file tool responses will include the final state of the file after any auto-formatting
- Use this final state as your reference point for any subsequent edits. This is ESPECIALLY important when crafting SEARCH blocks for replace_in_file which require the content to match what's in the file exactly.
# Workflow Tips
1. Before editing, assess the scope of your changes and decide which tool to use.
2. For targeted edits, apply replace_in_file with carefully crafted SEARCH/REPLACE blocks. If you need multiple changes, you can stack multiple SEARCH/REPLACE blocks within a single replace_in_file call.
3. For major overhauls or initial file creation, rely on write_to_file.
4. Once the file has been edited with either write_to_file or replace_in_file, the system will provide you with the final state of the modified file. Use this updated content as the reference point for any subsequent SEARCH/REPLACE operations, since it reflects any auto-formatting or user-applied changes.
By thoughtfully selecting between write_to_file and replace_in_file, you can make your file editing process smoother, safer, and more efficient.
{% endif %}
====
RULES

View File

@@ -1579,11 +1579,11 @@ end
---@param tool_use AvanteLLMToolUse
function M.tool_use_to_xml(tool_use)
local xml = string.format("<tool_use>\n<%s>\n", tool_use.name)
for k, v in pairs(tool_use.input or {}) do
xml = xml .. string.format("<%s>%s</%s>\n", k, tostring(v), k)
end
xml = xml .. "</" .. tool_use.name .. ">\n</tool_use>"
local tool_use_json = vim.json.encode({
name = tool_use.name,
input = tool_use.input,
})
local xml = string.format("<tool_use>%s</tool_use>", tool_use_json)
return xml
end

View File

@@ -1,3 +1,4 @@
local Config = require("avante.config")
local M = {}
---@param provider_conf AvanteDefaultBaseProvider
@@ -16,35 +17,37 @@ You have access to a set of tools that are executed upon the user's approval. Yo
# Tool Use Formatting
Tool use is formatted using XML-style tags. Each tool use is wrapped in a <tool_use> tag. The tool name is enclosed in opening and closing tags, and each parameter is similarly enclosed within its own set of tags. Here's the structure:
Tool use is formatted using XML-style tags. Each tool use is wrapped in a <tool_use> tag. The tool use content is a valid JSON with tool name and tool input. Here's the structure:
<tool_use>
<tool_name>
<parameter1_name>value1</parameter1_name>
<parameter2_name>value2</parameter2_name>
...
</tool_name>
{
"name": "tool_name",
"input": {
"parameter1_name": "value1",
"parameter2_name": "value2",
...
}
}
</tool_use>
For example:
<tool_use>
<attempt_completion>
<result>
I have completed the task...
</result>
</attempt_completion>
{"name": "attempt_completion", "input": {"result": "I have completed the task..."}}
</tool_use>
<tool_use>
<bash>
<path>./src</path>
<command>npm run dev</command>
</bash>
{"name": "bash", "input": {"path": "./src", "command": "npm run dev"}}
</tool_use>
ALWAYS ADHERE TO this format for the tool use to ensure proper parsing and execution.
## RULES
- When outputting the JSON for tool_use, you MUST first output the "name" field and then the "input" field.
- The value of "input" MUST be VALID JSON.
- If the "input" JSON object contains a "path" field, you MUST output the "path" field before any other fields.
## OUTPUT FORMAT
Please remember you are not allowed to use any format related to function calling or fc or tool_code.
@@ -71,15 +74,23 @@ Parameters:
field.get_description and field.get_description() or (field.description or "")
)
end
if field.choices then
tool_prompt = tool_prompt .. " - Choices: "
for i, choice in ipairs(field.choices) do
tool_prompt = tool_prompt .. string.format("%s", choice)
if i ~= #field.choices then tool_prompt = tool_prompt .. ", " end
end
tool_prompt = tool_prompt .. "\n"
end
end
if tool.param.usage then
tool_prompt = tool_prompt
.. ("Usage:\n<tool_use>\n<{{name}}>\n"):gsub("{{([%w_]+)}}", function(name) return tool[name] end)
for k, v in pairs(tool.param.usage) do
tool_prompt = tool_prompt .. "<" .. k .. ">" .. tostring(v) .. "</" .. k .. ">\n"
end
tool_prompt = tool_prompt
.. ("</{{name}}>\n</tool_use>\n"):gsub("{{([%w_]+)}}", function(name) return tool[name] end)
tool_prompt = tool_prompt .. "Usage:\n<tool_use>"
local tool_use = {
name = tool.name,
input = tool.param.usage,
}
local tool_use_json = vim.json.encode(tool_use)
tool_prompt = tool_prompt .. tool_use_json .. "</tool_use>\n"
end
tools_prompts = tools_prompts .. tool_prompt .. "\n"
end
@@ -92,119 +103,46 @@ Parameters:
## Example 1: Requesting to execute a command
<tool_use>
<bash>
<path>./src</path>
<command>npm run dev</command>
</bash>
</tool_use>
<tool_use>{"name": "bash", "input": {"path": "./src", "command": "npm run dev"}}</tool_use>
]]
if Config.behaviour.enable_fastapply then
system_prompt = system_prompt
.. [[
## Example 2: Requesting to create a new file
<tool_use>
<write_to_file>
<path>src/frontend-config.json</path>
<content>
{
"apiEndpoint": "https://api.example.com",
"theme": {
"primaryColor": "#007bff",
"secondaryColor": "#6c757d",
"fontFamily": "Arial, sans-serif"
},
"features": {
"darkMode": true,
"notifications": true,
"analytics": false
},
"version": "1.0.0"
}
</content>
</write_to_file>
</tool_use>
<tool_use>{"name": "edit_file", "input": {"path": "src/frontend-config.json", "instructions": "write the following content to the file", "code_edit": "// ... existing code ...\nFIRST_EDIT\n// ... existing code ...\nSECOND_EDIT\n// ... existing code ...\nTHIRD_EDIT\n// ... existing code ...\n\n"}}</tool_use>
## Example 3: Requesting to make targeted edits to a file
<tool_use>
<replace_in_file>
<path>src/components/App.tsx</path>
<diff>
------- SEARCH
import React from 'react';
=======
import React, { useState } from 'react';
+++++++ REPLACE
<tool_use>{"name": "edit_file", "input": {"path": "src/frontend-config.json", "instructions": "write the following content to the file", "code_edit": "// ... existing code ...\nFIRST_EDIT\n// ... existing code ...\nSECOND_EDIT\n// ... existing code ...\nTHIRD_EDIT\n// ... existing code ...\n\n"}}</tool_use>
]]
else
system_prompt = system_prompt
.. [[
## Example 2: Requesting to create a new file
------- SEARCH
function handleSubmit() {
saveData();
setLoading(false);
}
<tool_use>{"name": "write_to_file", "input": {"path": "src/frontend-config.json", "the_content": "{\n \"apiEndpoint\": \"https://api.example.com\",\n \"theme\": {\n \"primaryColor\": \"#007bff\",\n \"secondaryColor\": \"#6c757d\",\n \"fontFamily\": \"Arial, sans-serif\"\n },\n \"features\": {\n \"darkMode\": true,\n \"notifications\": true,\n \"analytics\": false\n },\n \"version\": \"1.0.0\"\n}"}}</tool_use>
=======
+++++++ REPLACE
## Example 3: Requesting to make targeted edits to a file
------- SEARCH
return (
<div>
=======
function handleSubmit() {
saveData();
setLoading(false);
}
return (
<div>
+++++++ REPLACE
</diff>
</replace_in_file>
</tool_use>
<tool_use>{"name": "replace_in_file", "input": {"path": "src/components/App.tsx", "the_diff": "------- SEARCH\nimport React from 'react';\n=======\nimport React, { useState } from 'react';\n+++++++ REPLACE\n\n------- SEARCH\nfunction handleSubmit() {\n saveData();\n setLoading(false);\n}\n\n=======\n+++++++ REPLACE\n\n------- SEARCH\nreturn (\n <div>\n=======\nfunction handleSubmit() {\n saveData();\n setLoading(false);\n}\n\nreturn (\n <div>\n+++++++ REPLACE\n"}}}</tool_use>
]]
end
system_prompt = system_prompt
.. [[
## Example 4: Complete current task
<tool_use>
<attempt_completion>
<result>
I've successfully created the requested React component with the following features:
- Responsive layout
- Dark/light mode toggle
- Form validation
- API integration
</result>
</attempt_completion>
</tool_use>
<tool_use>{"name": "attempt_completion", "input": {"result": "I've successfully created the requested React component with the following features:\n- Responsive layout\n- Dark/light mode toggle\n- Form validation\n- API integration"}}</tool_use>
## Example 5: Add todos
<tool_use>
<add_todos>
<todos>
[
{
"id": "1",
"content": "Implement a responsive layout",
"status": "todo",
"priority": "low"
},
{
"id": "2",
"content": "Add dark/light mode toggle",
"status": "todo",
"priority": "medium"
},
]
</todos>
</add_todos>
</tool_use>
<tool_use>{"name": "add_todos", "input": {"todos": [{"id": "1", "content": "Implement a responsive layout", "status": "todo", "priority": "low"}, {"id": "2", "content": "Add dark/light mode toggle", "status": "todo", "priority": "medium"}]}}</tool_use>
## Example 6: Update todo status
<tool_use>
<update_todo_status>
<id>1</id>
<status>done</status>
</update_todo_status>
</tool_use>
<tool_use>{"name": "update_todo_status", "input": {"id": "1", "status": "done"}}</tool_use>
]]
end
return system_prompt