From fe8198c8cdeb32a33f3b26999225eefa72314759 Mon Sep 17 00:00:00 2001 From: ianshaw Date: Mon, 12 Jan 2026 10:14:50 -0800 Subject: [PATCH 1/6] =?UTF-8?q?fix(frontend):=20=E5=90=8C=E6=AD=A5=20OpenA?= =?UTF-8?q?I=20GPT-5=20=E7=B3=BB=E5=88=97=E6=A8=A1=E5=9E=8B=E5=88=97?= =?UTF-8?q?=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修复编辑账号页面 GPT-5 模型只显示 3 个的问题: - 原来只有: gpt-5, gpt-5-mini, gpt-5-nano - 现在添加完整的 22 个模型,包括: * GPT-5 系列: gpt-5, gpt-5-codex, gpt-5-chat, gpt-5-pro, gpt-5-mini, gpt-5-nano 及各时间戳版本 * GPT-5.1 系列: gpt-5.1, gpt-5.1-codex, gpt-5.1-codex-max, gpt-5.1-codex-mini 及各版本 * GPT-5.2 系列: gpt-5.2, gpt-5.2-codex, gpt-5.2-pro 及各版本 - 更新快捷预设按钮,新增 GPT-5.1, GPT-5.2, GPT-5.1 Codex 选项 与后端定价文件 (model_prices_and_context_window.json) 保持一致。 Fixes issue introduced in fb86002 (feat: 添加模型白名单选择器组件) Related-to: fb86002ef90dda16d8460c06cfc4488419bf2a10 --- frontend/src/composables/useModelWhitelist.ts | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/frontend/src/composables/useModelWhitelist.ts b/frontend/src/composables/useModelWhitelist.ts index d18bdc5f..79900c6e 100644 --- a/frontend/src/composables/useModelWhitelist.ts +++ b/frontend/src/composables/useModelWhitelist.ts @@ -13,7 +13,17 @@ const openaiModels = [ 'o1', 'o1-preview', 'o1-mini', 'o1-pro', 'o3', 'o3-mini', 'o3-pro', 'o4-mini', - 'gpt-5', 'gpt-5-mini', 'gpt-5-nano', + // GPT-5 系列(同步后端定价文件) + 'gpt-5', 'gpt-5-2025-08-07', 'gpt-5-chat', 'gpt-5-chat-latest', + 'gpt-5-codex', 'gpt-5-pro', 'gpt-5-pro-2025-10-06', + 'gpt-5-mini', 'gpt-5-mini-2025-08-07', + 'gpt-5-nano', 'gpt-5-nano-2025-08-07', + // GPT-5.1 系列 + 'gpt-5.1', 'gpt-5.1-2025-11-13', 'gpt-5.1-chat-latest', + 'gpt-5.1-codex', 'gpt-5.1-codex-max', 'gpt-5.1-codex-mini', + // GPT-5.2 系列 + 'gpt-5.2', 'gpt-5.2-2025-12-11', 'gpt-5.2-chat-latest', + 'gpt-5.2-codex', 'gpt-5.2-pro', 'gpt-5.2-pro-2025-12-11', 'chatgpt-4o-latest', 'gpt-4o-audio-preview', 'gpt-4o-realtime-preview' ] @@ -211,7 +221,10 @@ const openaiPresetMappings = [ { label: 'GPT-4.1', from: 'gpt-4.1', to: 'gpt-4.1', color: 'bg-indigo-100 text-indigo-700 hover:bg-indigo-200 dark:bg-indigo-900/30 dark:text-indigo-400' }, { label: 'o1', from: 'o1', to: 'o1', color: 'bg-purple-100 text-purple-700 hover:bg-purple-200 dark:bg-purple-900/30 dark:text-purple-400' }, { label: 'o3', from: 'o3', to: 'o3', color: 'bg-emerald-100 text-emerald-700 hover:bg-emerald-200 dark:bg-emerald-900/30 dark:text-emerald-400' }, - { label: 'GPT-5', from: 'gpt-5', to: 'gpt-5', color: 'bg-amber-100 text-amber-700 hover:bg-amber-200 dark:bg-amber-900/30 dark:text-amber-400' } + { label: 'GPT-5', from: 'gpt-5', to: 'gpt-5', color: 'bg-amber-100 text-amber-700 hover:bg-amber-200 dark:bg-amber-900/30 dark:text-amber-400' }, + { label: 'GPT-5.1', from: 'gpt-5.1', to: 'gpt-5.1', color: 'bg-orange-100 text-orange-700 hover:bg-orange-200 dark:bg-orange-900/30 dark:text-orange-400' }, + { label: 'GPT-5.2', from: 'gpt-5.2', to: 'gpt-5.2', color: 'bg-red-100 text-red-700 hover:bg-red-200 dark:bg-red-900/30 dark:text-red-400' }, + { label: 'GPT-5.1 Codex', from: 'gpt-5.1-codex', to: 'gpt-5.1-codex', color: 'bg-cyan-100 text-cyan-700 hover:bg-cyan-200 dark:bg-cyan-900/30 dark:text-cyan-400' } ] const geminiPresetMappings = [ From fe6a3f4267f60c5aaedf57a00f751a3a62ae0b10 Mon Sep 17 00:00:00 2001 From: ianshaw Date: Mon, 12 Jan 2026 11:08:28 -0800 Subject: [PATCH 2/6] =?UTF-8?q?fix(gateway):=20=E5=AE=8C=E5=96=84=20max=5F?= =?UTF-8?q?output=5Ftokens=20=E5=8F=82=E6=95=B0=E5=A4=84=E7=90=86=E9=80=BB?= =?UTF-8?q?=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 根据不同平台和账号类型处理 max_output_tokens 参数: - OpenAI OAuth (Responses API): 保留 max_output_tokens(支持) - OpenAI API Key: 删除 max_output_tokens(不支持) - Anthropic (Claude): 转换 max_output_tokens 为 max_tokens - Gemini: 删除 max_output_tokens(由 Gemini 专用转换处理) - 其他平台: 删除(安全起见) 同时处理 max_completion_tokens 参数,仅在 OpenAI OAuth 时保留。 修复客户端(如 OpenCode)发送不支持参数导致上游返回 400 错误的问题。 Related-to: #231 --- .../service/openai_gateway_service.go | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/backend/internal/service/openai_gateway_service.go b/backend/internal/service/openai_gateway_service.go index f9078959..214da20a 100644 --- a/backend/internal/service/openai_gateway_service.go +++ b/backend/internal/service/openai_gateway_service.go @@ -568,6 +568,44 @@ func (s *OpenAIGatewayService) Forward(ctx context.Context, c *gin.Context, acco } } + // Handle max_output_tokens based on platform and account type + if !isCodexCLI { + if maxOutputTokens, hasMaxOutputTokens := reqBody["max_output_tokens"]; hasMaxOutputTokens { + switch account.Platform { + case PlatformOpenAI: + // For OpenAI API Key, remove max_output_tokens (not supported) + // For OpenAI OAuth (Responses API), keep it (supported) + if account.Type == AccountTypeAPIKey { + delete(reqBody, "max_output_tokens") + bodyModified = true + } + case PlatformAnthropic: + // For Anthropic (Claude), convert to max_tokens + delete(reqBody, "max_output_tokens") + if _, hasMaxTokens := reqBody["max_tokens"]; !hasMaxTokens { + reqBody["max_tokens"] = maxOutputTokens + } + bodyModified = true + case PlatformGemini: + // For Gemini, remove (will be handled by Gemini-specific transform) + delete(reqBody, "max_output_tokens") + bodyModified = true + default: + // For unknown platforms, remove to be safe + delete(reqBody, "max_output_tokens") + bodyModified = true + } + } + + // Also handle max_completion_tokens (similar logic) + if _, hasMaxCompletionTokens := reqBody["max_completion_tokens"]; hasMaxCompletionTokens { + if account.Type == AccountTypeAPIKey || account.Platform != PlatformOpenAI { + delete(reqBody, "max_completion_tokens") + bodyModified = true + } + } + } + // Re-serialize body only if modified if bodyModified { var err error From ea699cbdc2a4a24d4d0c8651c538759e037b6f68 Mon Sep 17 00:00:00 2001 From: ianshaw Date: Mon, 12 Jan 2026 11:17:47 -0800 Subject: [PATCH 3/6] =?UTF-8?q?docs(frontend):=20=E5=AE=8C=E5=96=84=20Open?= =?UTF-8?q?Code=20=E9=85=8D=E7=BD=AE=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 更新 API 密钥页面 OpenCode 配置提示信息: - 补充支持 opencode.jsonc 后缀名 - 说明可使用默认 provider(openai/anthropic/google)或自定义 provider_id - 说明 API Key 支持直接配置或通过 /connect 命令配置 - 保留"示例仅供参考,模型与选项可按需调整"的提示 配置文件路径:~/.config/opencode/opencode.json(或 opencode.jsonc) --- frontend/src/i18n/locales/en.ts | 2 +- frontend/src/i18n/locales/zh.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/i18n/locales/en.ts b/frontend/src/i18n/locales/en.ts index 7482e018..629ae6d6 100644 --- a/frontend/src/i18n/locales/en.ts +++ b/frontend/src/i18n/locales/en.ts @@ -390,7 +390,7 @@ export default { opencode: { title: 'OpenCode Example', subtitle: 'opencode.json', - hint: 'Config path: ~/.config/opencode/opencode.json (create if not exists). This is an example, adjust model and options as needed.', + hint: 'Config path: ~/.config/opencode/opencode.json (or opencode.jsonc), create if not exists. Use default providers (openai/anthropic/google) or custom provider_id. API Key can be configured directly or via /connect command. This is an example, adjust models and options as needed.', }, }, customKeyLabel: 'Custom Key', diff --git a/frontend/src/i18n/locales/zh.ts b/frontend/src/i18n/locales/zh.ts index 56827a76..e27915ca 100644 --- a/frontend/src/i18n/locales/zh.ts +++ b/frontend/src/i18n/locales/zh.ts @@ -387,7 +387,7 @@ export default { opencode: { title: 'OpenCode 配置示例', subtitle: 'opencode.json', - hint: '配置文件路径:~/.config/opencode/opencode.json,不存在需手动创建。示例仅供参考,模型与选项可按需调整。', + hint: '配置文件路径:~/.config/opencode/opencode.json(或 opencode.jsonc),不存在需手动创建。可使用默认 provider(openai/anthropic/google)或自定义 provider_id。API Key 支持直接配置或通过客户端 /connect 命令配置。示例仅供参考,模型与选项可按需调整。', }, }, customKeyLabel: '自定义密钥', From 7fdc25df3cd391bd1899eaec2771d7143a1fe8dc Mon Sep 17 00:00:00 2001 From: ianshaw Date: Mon, 12 Jan 2026 11:39:45 -0800 Subject: [PATCH 4/6] =?UTF-8?q?fix(gateway):=20=E4=BF=AE=E5=A4=8D=20base?= =?UTF-8?q?=5Furl=20=E5=8C=85=E5=90=AB=20/chat/completions=20=E6=97=B6?= =?UTF-8?q?=E8=B7=AF=E5=BE=84=E6=8B=BC=E6=8E=A5=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 问题: - 当账号的 base_url 配置为 https://example.com/v1/chat/completions 时 - 代码直接追加 /responses,导致路径变成 /v1/chat/completions/responses - 上游返回 404 错误 修复: - 在追加 /responses 前,先移除 base_url 中的 /chat/completions 后缀 - 确保最终路径为 https://example.com/v1/responses 影响范围: - OpenAI API Key 账号的测试接口 - OpenAI API Key 账号的实际网关请求 Related-to: #231 --- backend/internal/service/account_test_service.go | 5 ++++- backend/internal/service/openai_gateway_service.go | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/backend/internal/service/account_test_service.go b/backend/internal/service/account_test_service.go index 8419c2b4..c17aa3ff 100644 --- a/backend/internal/service/account_test_service.go +++ b/backend/internal/service/account_test_service.go @@ -332,7 +332,10 @@ func (s *AccountTestService) testOpenAIAccountConnection(c *gin.Context, account if err != nil { return s.sendErrorAndEnd(c, fmt.Sprintf("Invalid base URL: %s", err.Error())) } - apiURL = strings.TrimSuffix(normalizedBaseURL, "/") + "/responses" + // Remove /chat/completions suffix if present, then add /responses + normalizedBaseURL = strings.TrimSuffix(normalizedBaseURL, "/") + normalizedBaseURL = strings.TrimSuffix(normalizedBaseURL, "/chat/completions") + apiURL = normalizedBaseURL + "/responses" } else { return s.sendErrorAndEnd(c, fmt.Sprintf("Unsupported account type: %s", account.Type)) } diff --git a/backend/internal/service/openai_gateway_service.go b/backend/internal/service/openai_gateway_service.go index 214da20a..0e228fb1 100644 --- a/backend/internal/service/openai_gateway_service.go +++ b/backend/internal/service/openai_gateway_service.go @@ -740,6 +740,9 @@ func (s *OpenAIGatewayService) buildUpstreamRequest(ctx context.Context, c *gin. if err != nil { return nil, err } + // Remove /chat/completions suffix if present, then add /responses + validatedURL = strings.TrimSuffix(validatedURL, "/") + validatedURL = strings.TrimSuffix(validatedURL, "/chat/completions") targetURL = validatedURL + "/responses" } default: From 3402acb6067a911308f1555ce0588e60728dca4a Mon Sep 17 00:00:00 2001 From: ianshaw Date: Mon, 12 Jan 2026 13:23:05 -0800 Subject: [PATCH 5/6] =?UTF-8?q?feat(gateway):=20=E5=AF=B9=E6=89=80?= =?UTF-8?q?=E6=9C=89=E8=AF=B7=E6=B1=82=EF=BC=88=E5=8C=85=E6=8B=AC=20Codex?= =?UTF-8?q?=20CLI=EF=BC=89=E5=BA=94=E7=94=A8=E6=A8=A1=E5=9E=8B=E6=98=A0?= =?UTF-8?q?=E5=B0=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 移除 Codex CLI 的模型映射跳过逻辑 - 添加详细的模型映射日志,包含账号名称和请求类型 - 确保所有 OpenAI 请求都能正确应用账号配置的模型映射 --- backend/internal/service/openai_gateway_service.go | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/backend/internal/service/openai_gateway_service.go b/backend/internal/service/openai_gateway_service.go index 0e228fb1..1104a3a5 100644 --- a/backend/internal/service/openai_gateway_service.go +++ b/backend/internal/service/openai_gateway_service.go @@ -545,14 +545,12 @@ func (s *OpenAIGatewayService) Forward(ctx context.Context, c *gin.Context, acco isCodexCLI := openai.IsCodexCLIRequest(c.GetHeader("User-Agent")) - // Apply model mapping (skip for Codex CLI for transparent forwarding) - mappedModel := reqModel - if !isCodexCLI { - mappedModel = account.GetMappedModel(reqModel) - if mappedModel != reqModel { - reqBody["model"] = mappedModel - bodyModified = true - } + // Apply model mapping for all requests (including Codex CLI) + mappedModel := account.GetMappedModel(reqModel) + if mappedModel != reqModel { + log.Printf("[OpenAI] Model mapping applied: %s -> %s (account: %s, isCodexCLI: %v)", reqModel, mappedModel, account.Name, isCodexCLI) + reqBody["model"] = mappedModel + bodyModified = true } if account.Type == AccountTypeOAuth && !isCodexCLI { From d85288a6c02433f5abb2ccd0d2734c0bcaddbacb Mon Sep 17 00:00:00 2001 From: ianshaw Date: Mon, 12 Jan 2026 13:29:04 -0800 Subject: [PATCH 6/6] =?UTF-8?q?Revert=20"fix(gateway):=20=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=20base=5Furl=20=E5=8C=85=E5=90=AB=20/chat/completions?= =?UTF-8?q?=20=E6=97=B6=E8=B7=AF=E5=BE=84=E6=8B=BC=E6=8E=A5=E9=94=99?= =?UTF-8?q?=E8=AF=AF"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 7fdc25df3cd391bd1899eaec2771d7143a1fe8dc. --- backend/internal/service/account_test_service.go | 5 +---- backend/internal/service/openai_gateway_service.go | 3 --- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/backend/internal/service/account_test_service.go b/backend/internal/service/account_test_service.go index c17aa3ff..8419c2b4 100644 --- a/backend/internal/service/account_test_service.go +++ b/backend/internal/service/account_test_service.go @@ -332,10 +332,7 @@ func (s *AccountTestService) testOpenAIAccountConnection(c *gin.Context, account if err != nil { return s.sendErrorAndEnd(c, fmt.Sprintf("Invalid base URL: %s", err.Error())) } - // Remove /chat/completions suffix if present, then add /responses - normalizedBaseURL = strings.TrimSuffix(normalizedBaseURL, "/") - normalizedBaseURL = strings.TrimSuffix(normalizedBaseURL, "/chat/completions") - apiURL = normalizedBaseURL + "/responses" + apiURL = strings.TrimSuffix(normalizedBaseURL, "/") + "/responses" } else { return s.sendErrorAndEnd(c, fmt.Sprintf("Unsupported account type: %s", account.Type)) } diff --git a/backend/internal/service/openai_gateway_service.go b/backend/internal/service/openai_gateway_service.go index 1104a3a5..e86aa2d3 100644 --- a/backend/internal/service/openai_gateway_service.go +++ b/backend/internal/service/openai_gateway_service.go @@ -738,9 +738,6 @@ func (s *OpenAIGatewayService) buildUpstreamRequest(ctx context.Context, c *gin. if err != nil { return nil, err } - // Remove /chat/completions suffix if present, then add /responses - validatedURL = strings.TrimSuffix(validatedURL, "/") - validatedURL = strings.TrimSuffix(validatedURL, "/chat/completions") targetURL = validatedURL + "/responses" } default: