Auto select Copilot Response API for GPT-5 Codex models (#2808)
This commit is contained in:
@@ -6,6 +6,11 @@
|
||||
|
||||
local Utils = require("avante.utils")
|
||||
|
||||
local function copilot_use_response_api(opts)
|
||||
local model = opts and opts.model
|
||||
return type(model) == "string" and model:match("gpt%-5%-codex") ~= nil
|
||||
end
|
||||
|
||||
---@class avante.file_selector.IParams
|
||||
---@field public title string
|
||||
---@field public filepaths string[]
|
||||
@@ -286,7 +291,7 @@ M._defaults = {
|
||||
model = "gpt-4o",
|
||||
timeout = 30000, -- Timeout in milliseconds, increase this for reasoning models
|
||||
context_window = 128000, -- Number of tokens to send to the model for context
|
||||
use_response_api = false, -- Set to true to use OpenAI's new Response API (/responses) instead of Chat Completions API (/chat/completions)
|
||||
use_response_api = copilot_use_response_api, -- Automatically switch to Response API for GPT-5 Codex models
|
||||
support_previous_response_id = true, -- OpenAI Response API supports previous_response_id for stateful conversations
|
||||
-- NOTE: Response API automatically manages conversation state using previous_response_id for tool calling
|
||||
extra_request_body = {
|
||||
@@ -305,7 +310,7 @@ M._defaults = {
|
||||
allow_insecure = false, -- Allow insecure server connections
|
||||
timeout = 30000, -- Timeout in milliseconds
|
||||
context_window = 64000, -- Number of tokens to send to the model for context
|
||||
use_response_api = true, -- Copilot uses Response API input format
|
||||
use_response_api = copilot_use_response_api, -- Automatically switch to Response API for GPT-5 Codex models
|
||||
support_previous_response_id = false, -- Copilot doesn't support previous_response_id, must send full history
|
||||
-- NOTE: Copilot doesn't support previous_response_id, always sends full conversation history including tool_calls
|
||||
-- NOTE: Response API doesn't support some parameters like top_p, frequency_penalty, presence_penalty
|
||||
|
||||
@@ -282,6 +282,7 @@ function M:parse_curl_args(prompt_opts)
|
||||
H.refresh_token(false, false)
|
||||
|
||||
local provider_conf, request_body = Providers.parse_config(self)
|
||||
local use_response_api = Providers.resolve_use_response_api(provider_conf, prompt_opts)
|
||||
local disable_tools = provider_conf.disable_tools or false
|
||||
|
||||
-- Apply OpenAI's set_allowed_params for Response API compatibility
|
||||
@@ -295,7 +296,7 @@ function M:parse_curl_args(prompt_opts)
|
||||
for _, tool in ipairs(prompt_opts.tools) do
|
||||
local transformed_tool = OpenAI:transform_tool(tool)
|
||||
-- Response API uses flattened tool structure
|
||||
if provider_conf.use_response_api then
|
||||
if use_response_api then
|
||||
if transformed_tool.type == "function" and transformed_tool["function"] then
|
||||
transformed_tool = {
|
||||
type = "function",
|
||||
@@ -328,7 +329,7 @@ function M:parse_curl_args(prompt_opts)
|
||||
|
||||
-- Response API uses 'input' instead of 'messages'
|
||||
-- NOTE: Copilot doesn't support previous_response_id, always send full history
|
||||
if provider_conf.use_response_api then
|
||||
if use_response_api then
|
||||
base_body.input = parsed_messages
|
||||
|
||||
-- Response API uses max_output_tokens instead of max_tokens/max_completion_tokens
|
||||
@@ -354,8 +355,11 @@ function M:parse_curl_args(prompt_opts)
|
||||
}
|
||||
end
|
||||
|
||||
local base_url = M.state.github_token.endpoints.api or provider_conf.endpoint
|
||||
local build_url = use_response_api and H.response_url or H.chat_completion_url
|
||||
|
||||
return {
|
||||
url = H.response_url(M.state.github_token.endpoints.api or provider_conf.endpoint),
|
||||
url = build_url(base_url),
|
||||
timeout = provider_conf.timeout,
|
||||
proxy = provider_conf.proxy,
|
||||
insecure = provider_conf.allow_insecure,
|
||||
|
||||
@@ -256,6 +256,22 @@ function M.parse_config(opts)
|
||||
return provider_opts, request_body
|
||||
end
|
||||
|
||||
---@param provider_conf table | nil
|
||||
---@param ctx any
|
||||
---@return boolean
|
||||
function M.resolve_use_response_api(provider_conf, ctx)
|
||||
if not provider_conf then return false end
|
||||
local value = provider_conf.use_response_api
|
||||
if type(value) ~= "function" then value = provider_conf._use_response_api_resolver or value end
|
||||
if type(value) == "function" then
|
||||
provider_conf._use_response_api_resolver = value
|
||||
local ok, result = pcall(value, provider_conf, ctx)
|
||||
if not ok then error("Failed to evaluate use_response_api: " .. result, 2) end
|
||||
return result == true
|
||||
end
|
||||
return value == true
|
||||
end
|
||||
|
||||
---@param provider_name avante.ProviderName
|
||||
function M.get_config(provider_name)
|
||||
provider_name = provider_name or Config.provider
|
||||
|
||||
@@ -71,11 +71,12 @@ function M.is_reasoning_model(model)
|
||||
end
|
||||
|
||||
function M.set_allowed_params(provider_conf, request_body)
|
||||
local use_response_api = Providers.resolve_use_response_api(provider_conf, nil)
|
||||
if M.is_reasoning_model(provider_conf.model) then
|
||||
-- Reasoning models have specific parameter requirements
|
||||
request_body.temperature = 1
|
||||
-- Response API doesn't support temperature for reasoning models
|
||||
if provider_conf.use_response_api then request_body.temperature = nil end
|
||||
if use_response_api then request_body.temperature = nil end
|
||||
else
|
||||
request_body.reasoning_effort = nil
|
||||
request_body.reasoning = nil
|
||||
@@ -84,7 +85,7 @@ function M.set_allowed_params(provider_conf, request_body)
|
||||
if request_body.max_tokens then request_body.max_completion_tokens = nil end
|
||||
|
||||
-- Handle Response API specific parameters
|
||||
if provider_conf.use_response_api then
|
||||
if use_response_api then
|
||||
-- Convert reasoning_effort to reasoning object for Response API
|
||||
if request_body.reasoning_effort then
|
||||
request_body.reasoning = {
|
||||
@@ -113,6 +114,7 @@ end
|
||||
function M:parse_messages(opts)
|
||||
local messages = {}
|
||||
local provider_conf, _ = Providers.parse_config(self)
|
||||
local use_response_api = Providers.resolve_use_response_api(provider_conf, opts)
|
||||
|
||||
local use_ReAct_prompt = provider_conf.use_ReAct_prompt == true
|
||||
local system_prompt = opts.system_prompt
|
||||
@@ -209,13 +211,12 @@ function M:parse_messages(opts)
|
||||
if #tool_calls > 0 then
|
||||
-- Only skip tool_calls if using Response API with previous_response_id support
|
||||
-- Copilot uses Response API format but doesn't support previous_response_id
|
||||
local should_include_tool_calls = not provider_conf.use_response_api
|
||||
or not provider_conf.support_previous_response_id
|
||||
local should_include_tool_calls = not use_response_api or not provider_conf.support_previous_response_id
|
||||
|
||||
if should_include_tool_calls then
|
||||
-- For Response API without previous_response_id support (like Copilot),
|
||||
-- convert tool_calls to function_call items in input
|
||||
if provider_conf.use_response_api then
|
||||
if use_response_api then
|
||||
for _, tool_call in ipairs(tool_calls) do
|
||||
table.insert(messages, {
|
||||
type = "function_call",
|
||||
@@ -242,7 +243,7 @@ function M:parse_messages(opts)
|
||||
if #tool_results > 0 then
|
||||
for _, tool_result in ipairs(tool_results) do
|
||||
-- Response API uses different format for function outputs
|
||||
if provider_conf.use_response_api then
|
||||
if use_response_api then
|
||||
table.insert(messages, {
|
||||
type = "function_call_output",
|
||||
call_id = tool_result.tool_call_id,
|
||||
@@ -741,6 +742,7 @@ function M:parse_curl_args(prompt_opts)
|
||||
end
|
||||
|
||||
self.set_allowed_params(provider_conf, request_body)
|
||||
local use_response_api = Providers.resolve_use_response_api(provider_conf, prompt_opts)
|
||||
|
||||
local use_ReAct_prompt = provider_conf.use_ReAct_prompt == true
|
||||
|
||||
@@ -750,7 +752,7 @@ function M:parse_curl_args(prompt_opts)
|
||||
for _, tool in ipairs(prompt_opts.tools) do
|
||||
local transformed_tool = self:transform_tool(tool)
|
||||
-- Response API uses flattened tool structure
|
||||
if provider_conf.use_response_api then
|
||||
if use_response_api then
|
||||
-- Convert from {type: "function", function: {name, description, parameters}}
|
||||
-- to {type: "function", name, description, parameters}
|
||||
if transformed_tool.type == "function" and transformed_tool["function"] then
|
||||
@@ -773,7 +775,7 @@ function M:parse_curl_args(prompt_opts)
|
||||
if use_ReAct_prompt then stop = { "</tool_use>" } end
|
||||
|
||||
-- Determine endpoint path based on use_response_api
|
||||
local endpoint_path = provider_conf.use_response_api and "/responses" or "/chat/completions"
|
||||
local endpoint_path = use_response_api and "/responses" or "/chat/completions"
|
||||
|
||||
local parsed_messages = self:parse_messages(prompt_opts)
|
||||
|
||||
@@ -786,7 +788,7 @@ function M:parse_curl_args(prompt_opts)
|
||||
}
|
||||
|
||||
-- Response API uses 'input' instead of 'messages'
|
||||
if provider_conf.use_response_api then
|
||||
if use_response_api then
|
||||
-- Check if we have tool results - if so, use previous_response_id
|
||||
local has_function_outputs = false
|
||||
for _, msg in ipairs(parsed_messages) do
|
||||
|
||||
@@ -2527,8 +2527,10 @@ function Sidebar:get_history_messages_for_api(opts)
|
||||
end
|
||||
|
||||
if not Config.acp_providers[Config.provider] then
|
||||
local provider = Providers[Config.provider]
|
||||
local use_response_api = Providers.resolve_use_response_api(provider, nil)
|
||||
local tool_limit
|
||||
if Providers[Config.provider].use_ReAct_prompt or Providers[Config.provider].use_response_api then
|
||||
if provider.use_ReAct_prompt or use_response_api then
|
||||
tool_limit = nil
|
||||
else
|
||||
tool_limit = 25
|
||||
|
||||
@@ -263,7 +263,7 @@ vim.g.avante_login = vim.g.avante_login
|
||||
---@field hide_in_model_selector? boolean
|
||||
---@field use_ReAct_prompt? boolean
|
||||
---@field context_window? integer
|
||||
---@field use_response_api? boolean
|
||||
---@field use_response_api? boolean | fun(provider: AvanteDefaultBaseProvider, ctx?: any): boolean
|
||||
---@field support_previous_response_id? boolean
|
||||
---
|
||||
---@class AvanteSupportedProvider: AvanteDefaultBaseProvider
|
||||
|
||||
Reference in New Issue
Block a user