feat: add Dual Boost Mode for Enhanced LLM Responses with Multi-Provider Support (#854)
This commit is contained in:
parent
9891b03656
commit
3051bfdf24
16
README.md
16
README.md
@ -208,6 +208,22 @@ _See [config.lua#L9](./lua/avante/config.lua) for the full config_
|
|||||||
temperature = 0,
|
temperature = 0,
|
||||||
max_tokens = 4096,
|
max_tokens = 4096,
|
||||||
},
|
},
|
||||||
|
---Specify the special dual_boost mode
|
||||||
|
---1. enabled: Whether to enable dual_boost mode. Default to false.
|
||||||
|
---2. first_provider: The first provider to generate response. Default to "openai".
|
||||||
|
---3. second_provider: The second provider to generate response. Default to "claude".
|
||||||
|
---4. prompt: The prompt to generate response based on the two reference outputs.
|
||||||
|
---5. timeout: Timeout in milliseconds. Default to 60000.
|
||||||
|
---Whow it works:
|
||||||
|
--- When dual_boost is enabled, avante will generate two responses from the first_provider and second_provider respectively. Then use the response from the first_provider as provider1_output and the response from the second_provider as provider2_output. Finally, avante will generate a response based on the prompt and the two reference outputs, with the default Provider as normal.
|
||||||
|
---Note: This is an experimental feature and may not work as expected.
|
||||||
|
dual_boost = {
|
||||||
|
enabled = false,
|
||||||
|
first_provider = "openai",
|
||||||
|
second_provider = "claude",
|
||||||
|
prompt = "Based on the two reference outputs below, generate a response that incorporates elements from both but reflects your own judgment and unique perspective. Do not provide any explanation, just give the response directly. Reference Output 1: [{{provider1_output}}], Reference Output 2: [{{provider2_output}}]",
|
||||||
|
timeout = 60000, -- Timeout in milliseconds
|
||||||
|
},
|
||||||
behaviour = {
|
behaviour = {
|
||||||
auto_suggestions = false, -- Experimental stage
|
auto_suggestions = false, -- Experimental stage
|
||||||
auto_set_highlight_group = true,
|
auto_set_highlight_group = true,
|
||||||
|
@ -97,6 +97,22 @@ M.defaults = {
|
|||||||
max_tokens = 8000,
|
max_tokens = 8000,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
---Specify the special dual_boost mode
|
||||||
|
---1. enabled: Whether to enable dual_boost mode. Default to false.
|
||||||
|
---2. first_provider: The first provider to generate response. Default to "openai".
|
||||||
|
---3. second_provider: The second provider to generate response. Default to "claude".
|
||||||
|
---4. prompt: The prompt to generate response based on the two reference outputs.
|
||||||
|
---5. timeout: Timeout in milliseconds. Default to 60000.
|
||||||
|
---Whow it works:
|
||||||
|
--- When dual_boost is enabled, avante will generate two responses from the first_provider and second_provider respectively. Then use the response from the first_provider as provider1_output and the response from the second_provider as provider2_output. Finally, avante will generate a response based on the prompt and the two reference outputs, with the default Provider as normal.
|
||||||
|
---Note: This is an experimental feature and may not work as expected.
|
||||||
|
dual_boost = {
|
||||||
|
enabled = false,
|
||||||
|
first_provider = "openai",
|
||||||
|
second_provider = "claude",
|
||||||
|
prompt = "Based on the two reference outputs below, generate a response that incorporates elements from both but reflects your own judgment and unique perspective. Do not provide any explanation, just give the response directly. Reference Output 1: [{{provider1_output}}], Reference Output 2: [{{provider2_output}}]",
|
||||||
|
timeout = 60000, -- Timeout in milliseconds
|
||||||
|
},
|
||||||
---Specify the behaviour of avante.nvim
|
---Specify the behaviour of avante.nvim
|
||||||
---1. auto_apply_diff_after_generation: Whether to automatically apply diff after LLM response.
|
---1. auto_apply_diff_after_generation: Whether to automatically apply diff after LLM response.
|
||||||
--- This would simulate similar behaviour to cursor. Default to false.
|
--- This would simulate similar behaviour to cursor. Default to false.
|
||||||
|
@ -18,32 +18,10 @@ M.CANCEL_PATTERN = "AvanteLLMEscape"
|
|||||||
|
|
||||||
local group = api.nvim_create_augroup("avante_llm", { clear = true })
|
local group = api.nvim_create_augroup("avante_llm", { clear = true })
|
||||||
|
|
||||||
---@alias LlmMode "planning" | "editing" | "suggesting"
|
M._stream = function(opts, Provider)
|
||||||
---
|
-- print opts
|
||||||
---@class TemplateOptions
|
|
||||||
---@field use_xml_format boolean
|
|
||||||
---@field ask boolean
|
|
||||||
---@field question string
|
|
||||||
---@field code_lang string
|
|
||||||
---@field file_content string
|
|
||||||
---@field selected_code string | nil
|
|
||||||
---@field project_context string | nil
|
|
||||||
---@field history_messages AvanteLLMMessage[]
|
|
||||||
---
|
|
||||||
---@class StreamOptions: TemplateOptions
|
|
||||||
---@field ask boolean
|
|
||||||
---@field bufnr integer
|
|
||||||
---@field instructions string
|
|
||||||
---@field mode LlmMode
|
|
||||||
---@field provider AvanteProviderFunctor | nil
|
|
||||||
---@field on_chunk AvanteChunkParser
|
|
||||||
---@field on_complete AvanteCompleteParser
|
|
||||||
|
|
||||||
---@param opts StreamOptions
|
|
||||||
M.stream = function(opts)
|
|
||||||
local mode = opts.mode or "planning"
|
local mode = opts.mode or "planning"
|
||||||
---@type AvanteProviderFunctor
|
---@type AvanteProviderFunctor
|
||||||
local Provider = opts.provider or P[Config.provider]
|
|
||||||
local _, body_opts = P.parse_config(Provider)
|
local _, body_opts = P.parse_config(Provider)
|
||||||
local max_tokens = body_opts.max_tokens or 4096
|
local max_tokens = body_opts.max_tokens or 4096
|
||||||
|
|
||||||
@ -126,7 +104,6 @@ M.stream = function(opts)
|
|||||||
messages = messages,
|
messages = messages,
|
||||||
image_paths = image_paths,
|
image_paths = image_paths,
|
||||||
}
|
}
|
||||||
|
|
||||||
---@type string
|
---@type string
|
||||||
local current_event_state = nil
|
local current_event_state = nil
|
||||||
|
|
||||||
@ -265,6 +242,119 @@ M.stream = function(opts)
|
|||||||
return active_job
|
return active_job
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function _merge_response(first_response, second_response, opts, Provider)
|
||||||
|
local prompt = "\n" .. Config.dual_boost.prompt
|
||||||
|
prompt = prompt
|
||||||
|
:gsub("{{[%s]*provider1_output[%s]*}}", first_response)
|
||||||
|
:gsub("{{[%s]*provider2_output[%s]*}}", second_response)
|
||||||
|
|
||||||
|
prompt = prompt .. "\n"
|
||||||
|
|
||||||
|
-- append this reference prompt to the code_opts messages at last
|
||||||
|
opts.instructions = opts.instructions .. prompt
|
||||||
|
|
||||||
|
M._stream(opts, Provider)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function _collector_process_responses(collector, opts, Provider)
|
||||||
|
if not collector[1] or not collector[2] then
|
||||||
|
Utils.error("One or both responses failed to complete")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
_merge_response(collector[1], collector[2], opts, Provider)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function _collector_add_response(collector, index, response, opts, Provider)
|
||||||
|
collector[index] = response
|
||||||
|
collector.count = collector.count + 1
|
||||||
|
|
||||||
|
if collector.count == 2 then
|
||||||
|
collector.timer:stop()
|
||||||
|
_collector_process_responses(collector, opts, Provider)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
M._dual_boost_stream = function(opts, Provider, Provider1, Provider2)
|
||||||
|
Utils.debug("Starting Dual Boost Stream")
|
||||||
|
|
||||||
|
local collector = {
|
||||||
|
count = 0,
|
||||||
|
responses = {},
|
||||||
|
timer = uv.new_timer(),
|
||||||
|
timeout_ms = Config.dual_boost.timeout,
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Setup timeout
|
||||||
|
collector.timer:start(
|
||||||
|
collector.timeout_ms,
|
||||||
|
0,
|
||||||
|
vim.schedule_wrap(function()
|
||||||
|
if collector.count < 2 then
|
||||||
|
Utils.warn("Dual boost stream timeout reached")
|
||||||
|
collector.timer:stop()
|
||||||
|
-- Process whatever responses we have
|
||||||
|
_collector_process_responses(collector, opts, Provider)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
)
|
||||||
|
|
||||||
|
-- Create options for both streams
|
||||||
|
local function create_stream_opts(index)
|
||||||
|
local response = ""
|
||||||
|
return vim.tbl_extend("force", opts, {
|
||||||
|
on_chunk = function(chunk)
|
||||||
|
if chunk then response = response .. chunk end
|
||||||
|
end,
|
||||||
|
on_complete = function(err)
|
||||||
|
if err then
|
||||||
|
Utils.error(string.format("Stream %d failed: %s", index, err))
|
||||||
|
return
|
||||||
|
end
|
||||||
|
Utils.debug(string.format("Response %d completed", index))
|
||||||
|
_collector_add_response(collector, index, response, opts, Provider)
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Start both streams
|
||||||
|
local success, err = xpcall(function()
|
||||||
|
M._stream(create_stream_opts(1), Provider1)
|
||||||
|
M._stream(create_stream_opts(2), Provider2)
|
||||||
|
end, function(err) return err end)
|
||||||
|
if not success then Utils.error("Failed to start dual_boost streams: " .. tostring(err)) end
|
||||||
|
end
|
||||||
|
|
||||||
|
---@alias LlmMode "planning" | "editing" | "suggesting"
|
||||||
|
---
|
||||||
|
---@class TemplateOptions
|
||||||
|
---@field use_xml_format boolean
|
||||||
|
---@field ask boolean
|
||||||
|
---@field question string
|
||||||
|
---@field code_lang string
|
||||||
|
---@field file_content string
|
||||||
|
---@field selected_code string | nil
|
||||||
|
---@field project_context string | nil
|
||||||
|
---@field history_messages AvanteLLMMessage[]
|
||||||
|
---
|
||||||
|
---@class StreamOptions: TemplateOptions
|
||||||
|
---@field ask boolean
|
||||||
|
---@field bufnr integer
|
||||||
|
---@field instructions string
|
||||||
|
---@field mode LlmMode
|
||||||
|
---@field provider AvanteProviderFunctor | nil
|
||||||
|
---@field on_chunk AvanteChunkParser
|
||||||
|
---@field on_complete AvanteCompleteParser
|
||||||
|
|
||||||
|
---@param opts StreamOptions
|
||||||
|
M.stream = function(opts)
|
||||||
|
local Provider = opts.provider or P[Config.provider]
|
||||||
|
if Config.dual_boost.enabled then
|
||||||
|
M._dual_boost_stream(opts, Provider, P[Config.dual_boost.first_provider], P[Config.dual_boost.second_provider])
|
||||||
|
else
|
||||||
|
M._stream(opts, Provider)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
function M.cancel_inflight_request() api.nvim_exec_autocmds("User", { pattern = M.CANCEL_PATTERN }) end
|
function M.cancel_inflight_request() api.nvim_exec_autocmds("User", { pattern = M.CANCEL_PATTERN }) end
|
||||||
|
|
||||||
return M
|
return M
|
||||||
|
Loading…
x
Reference in New Issue
Block a user