diff --git a/lua/avante/config.lua b/lua/avante/config.lua
index 2a15385..4545d8b 100644
--- a/lua/avante/config.lua
+++ b/lua/avante/config.lua
@@ -297,6 +297,7 @@ M._defaults = {
model = "gemini-2.0-flash",
timeout = 30000, -- Timeout in milliseconds
context_window = 1048576,
+ use_ReAct_prompt = true,
extra_request_body = {
generationConfig = {
temperature = 0.75,
diff --git a/lua/avante/libs/ReAct_parser.lua b/lua/avante/libs/ReAct_parser.lua
new file mode 100644
index 0000000..a8316c8
--- /dev/null
+++ b/lua/avante/libs/ReAct_parser.lua
@@ -0,0 +1,408 @@
+---@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 value
+-- Returns {name = string, value = string, next_pos = number} or nil if incomplete
+local function parse_parameter(text, start_pos)
+ local i = start_pos
+ local len = #text
+
+ -- Skip whitespace
+ while i <= len and string.match(string.sub(text, i, i), "%s") do
+ i = i + 1
+ end
+
+ if i > len or string.sub(text, i, i) ~= "<" then return nil end
+
+ -- Find parameter name
+ local param_name_start = i + 1
+ local param_name_end = string.find(text, ">", param_name_start)
+
+ if not param_name_end then
+ return nil -- Incomplete parameter tag
+ end
+
+ local param_name = string.sub(text, param_name_start, param_name_end - 1)
+ i = param_name_end + 1
+
+ -- Find parameter value (everything until closing tag)
+ local param_close_tag = "" .. param_name .. ">"
+ local param_value_start = i
+ local param_close_pos = string.find(text, param_close_tag, i, true)
+
+ if not param_close_pos then
+ -- Incomplete parameter value, return what we have
+ local param_value = string.sub(text, param_value_start)
+ return {
+ name = param_name,
+ value = param_value,
+ next_pos = len + 1,
+ }
+ end
+
+ local param_value = string.sub(text, param_value_start, param_close_pos - 1)
+ i = param_close_pos + #param_close_tag
+
+ return {
+ name = param_name,
+ value = param_value,
+ next_pos = i,
+ }
+end
+
+-- Helper function to parse tool use content starting after
+-- Returns {content = ToolUseContent, next_pos = number} or nil if incomplete
+local function parse_tool_use(text, start_pos)
+ local i = start_pos
+ local len = #text
+
+ -- Skip whitespace
+ while i <= len and string.match(string.sub(text, i, i), "%s") do
+ i = i + 1
+ end
+
+ if i > len then
+ return nil -- No content after
+ end
+
+ -- Check if we have opening tag for tool name
+ if string.sub(text, i, i) ~= "<" then
+ return nil -- Invalid format
+ end
+
+ -- Find tool name
+ local tool_name_start = i + 1
+ local tool_name_end = string.find(text, ">", tool_name_start)
+
+ if not tool_name_end then
+ return nil -- Incomplete tool name tag
+ end
+
+ local tool_name = string.sub(text, tool_name_start, tool_name_end - 1)
+ i = tool_name_end + 1
+
+ -- Parse tool parameters
+ local tool_input = {}
+ local partial = false
+
+ -- Look for tool closing tag or
+ local tool_close_tag = "" .. tool_name .. ">"
+ local tool_use_close_tag = ""
+
+ while i <= len do
+ -- Skip whitespace before checking for closing tags
+ while i <= len and string.match(string.sub(text, i, i), "%s") do
+ i = i + 1
+ end
+
+ if i > len then
+ partial = true
+ break
+ end
+
+ -- Check for tool closing tag first
+ local tool_close_pos = string.find(text, tool_close_tag, i, true)
+ local tool_use_close_pos = string.find(text, tool_use_close_tag, i, true)
+
+ if tool_close_pos and tool_close_pos == i then
+ -- Found tool closing tag
+ i = tool_close_pos + #tool_close_tag
+
+ -- Skip whitespace
+ while i <= len and string.match(string.sub(text, i, i), "%s") do
+ i = i + 1
+ end
+
+ -- Check for
+ if i <= len and string.find(text, tool_use_close_tag, i, true) == i then
+ i = i + #tool_use_close_tag
+ partial = false
+ else
+ partial = true
+ end
+ break
+ elseif tool_use_close_pos and tool_use_close_pos == i then
+ -- Found without tool closing tag (malformed, but handle it)
+ i = tool_use_close_pos + #tool_use_close_tag
+ partial = false
+ break
+ else
+ -- Parse parameter tag
+ local param_result = parse_parameter(text, i)
+ if param_result then
+ tool_input[param_result.name] = param_result.value
+ i = param_result.next_pos
+ else
+ -- Incomplete parameter, mark as partial
+ partial = true
+ break
+ end
+ end
+ end
+
+ -- If we reached end of text without proper closing, it's partial
+ if i > len then partial = true end
+
+ return {
+ content = {
+ type = "tool_use",
+ tool_name = tool_name,
+ tool_input = tool_input,
+ partial = partial,
+ },
+ next_pos = i,
+ }
+end
+
+--- 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.path/to/file.txtfoo")
+--- 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.path/to/file.txtfooI am another tool.path/to/file.txtbarhello")
+--- 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.")
+--- 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.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.path/to/file.txtfoo 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.path/to/file.txtfoo bar")
+--- returns
+--- {
+--- {
+--- type = "text",
+--- text = "Hello, world! I am a tool.path/to/file.txtfoo bar",
+--- partial = false,
+--- }
+--- }
+---
+--- parse("Hello, world! I am a tool.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",
+--- content = "",
+--- },
+--- partial = false,
+--- },
+--- }
+---
+--- parse("Hello, world! I am a tool.path/to/file.txt