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

View File

@@ -528,6 +528,10 @@ func (h *AccountHandler) Create(c *gin.Context) {
// 确定是否跳过混合渠道检查
skipCheck := req.ConfirmMixedChannelRisk != nil && *req.ConfirmMixedChannelRisk
// 捕获闭包内创建的账号引用,用于创建成功后触发异步探测。
// 幂等重放时闭包不会执行 → createdAccount 为 nil → 不重复调度。
var createdAccount *service.Account
result, err := executeAdminIdempotent(c, "admin.accounts.create", req, service.DefaultWriteIdempotencyTTL(), func(ctx context.Context) (any, error) {
account, execErr := h.adminService.CreateAccount(ctx, &service.CreateAccountInput{
Name: req.Name,
@@ -549,6 +553,7 @@ func (h *AccountHandler) Create(c *gin.Context) {
if execErr != nil {
return nil, execErr
}
createdAccount = account
// Antigravity OAuth: 新账号直接设置隐私
h.adminService.ForceAntigravityPrivacy(ctx, account)
// OpenAI OAuth: 新账号直接设置隐私
@@ -577,6 +582,9 @@ func (h *AccountHandler) Create(c *gin.Context) {
if result != nil && result.Replayed {
c.Header("X-Idempotency-Replayed", "true")
}
// OpenAI APIKey 账号创建后异步探测上游 /v1/responses 能力。
// 探测失败不影响账号创建响应。
h.scheduleOpenAIResponsesProbe(createdAccount)
response.Success(c, result.Data)
}
@@ -637,9 +645,39 @@ func (h *AccountHandler) Update(c *gin.Context) {
return
}
// OpenAI APIKey: credentials 修改后重新探测上游能力base_url/api_key 可能变更)。
// 异步执行,探测失败不影响账号更新响应。
if len(req.Credentials) > 0 {
h.scheduleOpenAIResponsesProbe(account)
}
response.Success(c, h.buildAccountResponseWithRuntime(c.Request.Context(), account))
}
// scheduleOpenAIResponsesProbe 异步触发 OpenAI APIKey 账号的 Responses API 能力探测。
//
// 仅对 platform=openai && type=apikey 账号生效;其他账号无操作。
// 探测本身在 goroutine 中执行(会发一次 HTTP 请求到上游),不会阻塞
// 当前请求。探测错误仅记录日志,不向上下文传播:探测失败时标记保持缺失,
// 网关会按"现状即证据"默认走 Responses。
func (h *AccountHandler) scheduleOpenAIResponsesProbe(account *service.Account) {
if account == nil || account.Platform != service.PlatformOpenAI || account.Type != service.AccountTypeAPIKey {
return
}
if h.accountTestService == nil {
return
}
accountID := account.ID
go func() {
defer func() {
if r := recover(); r != nil {
slog.Error("openai_responses_probe_panic", "account_id", accountID, "recover", r)
}
}()
h.accountTestService.ProbeOpenAIAPIKeyResponsesSupport(context.Background(), accountID)
}()
}
// Delete handles deleting an account
// DELETE /api/v1/admin/accounts/:id
func (h *AccountHandler) Delete(c *gin.Context) {
@@ -1231,6 +1269,8 @@ func (h *AccountHandler) BatchCreate(c *gin.Context) {
openaiPrivacyAccounts = append(openaiPrivacyAccounts, account)
}
}
// OpenAI APIKey 账号异步探测 /v1/responses 能力。
h.scheduleOpenAIResponsesProbe(account)
success++
results = append(results, gin.H{
"name": item.Name,