From 0831739f4b1610d706fc576829d979778b5b0f1c Mon Sep 17 00:00:00 2001
From: X-Zero-L
Date: Sun, 1 Mar 2026 19:48:17 +0800
Subject: [PATCH] feat: add configurable provider endpoint for codex-api
accounts
Add providerEndpoint field (responses/completions/auto) to openai-responses
accounts, allowing admins to specify which API endpoint format the provider
supports. The relay service normalizes request paths based on this config:
- responses (default): routes all requests to /v1/responses
- completions: routes all requests to /v1/chat/completions
- auto: preserves the original request path
Co-Authored-By: Claude Opus 4.6
---
.../account/openaiResponsesAccountService.js | 6 ++-
.../relay/openaiResponsesRelayService.js | 18 ++++++++-
.../src/components/accounts/AccountForm.vue | 40 +++++++++++++++++++
3 files changed, 60 insertions(+), 4 deletions(-)
diff --git a/src/services/account/openaiResponsesAccountService.js b/src/services/account/openaiResponsesAccountService.js
index e16c7129..dc1517e0 100644
--- a/src/services/account/openaiResponsesAccountService.js
+++ b/src/services/account/openaiResponsesAccountService.js
@@ -51,7 +51,8 @@ class OpenAIResponsesAccountService {
dailyQuota = 0, // 每日额度限制(美元),0表示不限制
quotaResetTime = '00:00', // 额度重置时间(HH:mm格式)
rateLimitDuration = 60, // 限流时间(分钟)
- disableAutoProtection = false // 是否关闭自动防护(429/401/400/529 不自动禁用)
+ disableAutoProtection = false, // 是否关闭自动防护(429/401/400/529 不自动禁用)
+ providerEndpoint = 'responses' // Provider 端点类型:responses | completions | auto
} = options
// 验证必填字段
@@ -96,7 +97,8 @@ class OpenAIResponsesAccountService {
lastResetDate: redis.getDateStringInTimezone(),
quotaResetTime,
quotaStoppedAt: '',
- disableAutoProtection: disableAutoProtection.toString() // 关闭自动防护
+ disableAutoProtection: disableAutoProtection.toString(), // 关闭自动防护
+ providerEndpoint // Provider 端点类型:responses(默认) | completions | auto
}
// 保存到 Redis
diff --git a/src/services/relay/openaiResponsesRelayService.js b/src/services/relay/openaiResponsesRelayService.js
index 1cb06e13..f1a6f8fc 100644
--- a/src/services/relay/openaiResponsesRelayService.js
+++ b/src/services/relay/openaiResponsesRelayService.js
@@ -91,8 +91,22 @@ class OpenAIResponsesRelayService {
req.once('close', handleClientDisconnect)
res.once('close', handleClientDisconnect)
- // 构建目标 URL
- const targetUrl = `${fullAccount.baseApi}${req.path}`
+ // 构建目标 URL(根据 providerEndpoint 配置决定端点路径)
+ const providerEndpoint = fullAccount.providerEndpoint || 'responses'
+ let targetPath = req.path
+ if (providerEndpoint === 'responses' && targetPath.includes('/completions')) {
+ // Provider 仅支持 Responses 端点,将 completions 路径归一化
+ targetPath = '/v1/responses'
+ logger.info(`📝 Normalized path (${req.path}) → /v1/responses (providerEndpoint=responses)`)
+ } else if (providerEndpoint === 'completions' && targetPath.includes('/responses')) {
+ // Provider 仅支持 Completions 端点,将 responses 路径归一化
+ targetPath = '/v1/chat/completions'
+ logger.info(
+ `📝 Normalized path (${req.path}) → /v1/chat/completions (providerEndpoint=completions)`
+ )
+ }
+ // providerEndpoint === 'auto' 时保持原始路径不变
+ const targetUrl = `${fullAccount.baseApi}${targetPath}`
logger.info(`🎯 Forwarding to: ${targetUrl}`)
// 构建请求头 - 使用统一的 headerFilter 移除 CDN headers
diff --git a/web/admin-spa/src/components/accounts/AccountForm.vue b/web/admin-spa/src/components/accounts/AccountForm.vue
index ce2eb71e..dddcf150 100644
--- a/web/admin-spa/src/components/accounts/AccountForm.vue
+++ b/web/admin-spa/src/components/accounts/AccountForm.vue
@@ -1692,6 +1692,24 @@
+
+
+
+
+ 指定 Provider 支持的端点类型。Responses 会将所有请求路由到 /v1/responses;Chat
+ Completions 路由到 /v1/chat/completions;自动则保持客户端请求的原始路径
+
+
+
@@ -3440,6 +3458,24 @@
+
+
+
+
+ 指定 Provider 支持的端点类型。Responses 路由到 /v1/responses;Chat Completions
+ 路由到 /v1/chat/completions;自动则保持原始路径
+
+
+
@@ -4272,6 +4308,7 @@ const form = ref({
endpointType: props.account?.endpointType || 'anthropic',
// OpenAI-Responses 特定字段
baseApi: props.account?.baseApi || '',
+ providerEndpoint: props.account?.providerEndpoint || 'responses',
// Gemini-API 特定字段
baseUrl: props.account?.baseUrl || 'https://generativelanguage.googleapis.com',
rateLimitDuration: props.account?.rateLimitDuration || 60,
@@ -5434,6 +5471,7 @@ const createAccount = async () => {
data.baseApi = form.value.baseApi
data.apiKey = form.value.apiKey
data.userAgent = form.value.userAgent || ''
+ data.providerEndpoint = form.value.providerEndpoint || 'responses'
data.priority = form.value.priority || 50
data.rateLimitDuration = 60 // 默认值60,不从用户输入获取
data.dailyQuota = form.value.dailyQuota || 0
@@ -5784,6 +5822,7 @@ const updateAccount = async () => {
data.apiKey = form.value.apiKey
}
data.userAgent = form.value.userAgent || ''
+ data.providerEndpoint = form.value.providerEndpoint || 'responses'
data.priority = form.value.priority || 50
// 编辑时不上传 rateLimitDuration,保持原值
data.dailyQuota = form.value.dailyQuota || 0
@@ -6406,6 +6445,7 @@ watch(
deploymentName: newAccount.deploymentName || '',
// OpenAI-Responses 特定字段
baseApi: newAccount.baseApi || '',
+ providerEndpoint: newAccount.providerEndpoint || 'responses',
// Gemini-API 特定字段
baseUrl: newAccount.baseUrl || 'https://generativelanguage.googleapis.com',
// 额度管理字段