Auto select Copilot Response API for GPT-5 Codex models (#2808)

This commit is contained in:
yetone
2025-10-30 22:38:32 +08:00
committed by GitHub
parent 4286ac963a
commit d45b622193
6 changed files with 45 additions and 16 deletions

View File

@@ -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

View File

@@ -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,

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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