Merge pull request #2143 from alfadb/fix/openai-apikey-cc-default-routing

修复:APIKey 账户上游不支持 OpenAI Responses API 时的 Chat Completions 路由回退
This commit is contained in:
Wesley Liddick
2026-05-03 22:58:26 +08:00
committed by GitHub
9 changed files with 830 additions and 5 deletions
@@ -15,6 +15,7 @@ import (
"github.com/Wei-Shaw/sub2api/internal/pkg/apicompat"
"github.com/Wei-Shaw/sub2api/internal/pkg/logger"
"github.com/Wei-Shaw/sub2api/internal/pkg/openai_compat"
"github.com/Wei-Shaw/sub2api/internal/util/responseheaders"
"github.com/gin-gonic/gin"
"github.com/tidwall/gjson"
@@ -40,9 +41,18 @@ var cursorResponsesUnsupportedFields = []string{
// ForwardAsChatCompletions accepts a Chat Completions request body, converts it
// to OpenAI Responses API format, forwards to the OpenAI upstream, and converts
// the response back to Chat Completions format. All account types (OAuth and API
// Key) go through the Responses API conversion path since the upstream only
// exposes the /v1/responses endpoint.
// the response back to Chat Completions format.
//
// 历史背景:该函数原本对所有 OpenAI 账号无差别走 CC→Responses 转换 + /v1/responses
// 端点——这在 OAuthChatGPT 内部 API 仅支持 Responses)和官方 APIKey 账号上是
// 正确的,但 sub2api 接入 DeepSeek/Kimi/GLM 等第三方 OpenAI 兼容上游后假设破裂:
// 这些上游普遍只支持 /v1/chat/completions,无 /v1/responses 端点。
//
// 当前路由策略(基于账号探测标记,详见 openai_compat.ShouldUseResponsesAPI):
// - APIKey 账号 + 探测确认不支持 Responses → 走 forwardAsRawChatCompletions
// 直转上游 /v1/chat/completions,不做协议转换
// - 其他所有情况(OAuth、APIKey 探测确认支持、未探测)→ 走原有 CC→Responses
// 转换路径(保留旧行为,存量未探测账号零兼容破坏)
func (s *OpenAIGatewayService) ForwardAsChatCompletions(
ctx context.Context,
c *gin.Context,
@@ -51,6 +61,12 @@ func (s *OpenAIGatewayService) ForwardAsChatCompletions(
promptCacheKey string,
defaultMappedModel string,
) (*OpenAIForwardResult, error) {
// 入口分流:APIKey 账号 + 已探测且确认上游不支持 Responses,走 CC 直转。
// 标记缺失(未探测)按"现状即证据"原则继续走下方原 Responses 转换路径。
if account.Type == AccountTypeAPIKey && !openai_compat.ShouldUseResponsesAPI(account.Extra) {
return s.forwardAsRawChatCompletions(ctx, c, account, body, defaultMappedModel)
}
startTime := time.Now()
// 1. Parse Chat Completions request