mirror of
https://github.com/Wei-Shaw/claude-relay-service.git
synced 2026-01-22 16:43:35 +00:00
[fix] Droid: dynamic x-api-provider and custom User-Agent support
- Dynamic x-api-provider selection for OpenAI endpoint based on model - Models with '-max' suffix use 'openai' provider - Other models use 'azure_openai' provider - Fixes gpt-5.1-codex-max model compatibility issue - Update default User-Agent to factory-cli/0.32.1 - Add custom User-Agent field for Droid accounts - Backend: userAgent field in createAccount and updateAccount - Frontend: User-Agent input in account creation/edit UI - Supports all Droid auth modes: OAuth, Manual, API Key This resolves the issue where gpt-5.1-codex-max failed with 'Azure OpenAI only supports...' error due to incorrect provider header.
This commit is contained in:
@@ -556,7 +556,8 @@ class DroidAccountService {
|
|||||||
tokenType = 'Bearer',
|
tokenType = 'Bearer',
|
||||||
authenticationMethod = '',
|
authenticationMethod = '',
|
||||||
expiresIn = null,
|
expiresIn = null,
|
||||||
apiKeys = []
|
apiKeys = [],
|
||||||
|
userAgent = '' // 自定义 User-Agent
|
||||||
} = options
|
} = options
|
||||||
|
|
||||||
const accountId = uuidv4()
|
const accountId = uuidv4()
|
||||||
@@ -832,7 +833,8 @@ class DroidAccountService {
|
|||||||
: '',
|
: '',
|
||||||
apiKeys: hasApiKeys ? JSON.stringify(apiKeyEntries) : '',
|
apiKeys: hasApiKeys ? JSON.stringify(apiKeyEntries) : '',
|
||||||
apiKeyCount: hasApiKeys ? String(apiKeyEntries.length) : '0',
|
apiKeyCount: hasApiKeys ? String(apiKeyEntries.length) : '0',
|
||||||
apiKeyStrategy: hasApiKeys ? 'random_sticky' : ''
|
apiKeyStrategy: hasApiKeys ? 'random_sticky' : '',
|
||||||
|
userAgent: userAgent || '' // 自定义 User-Agent
|
||||||
}
|
}
|
||||||
|
|
||||||
await redis.setDroidAccount(accountId, accountData)
|
await redis.setDroidAccount(accountId, accountData)
|
||||||
@@ -931,6 +933,11 @@ class DroidAccountService {
|
|||||||
sanitizedUpdates.endpointType = this._sanitizeEndpointType(sanitizedUpdates.endpointType)
|
sanitizedUpdates.endpointType = this._sanitizeEndpointType(sanitizedUpdates.endpointType)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 处理 userAgent 字段
|
||||||
|
if (typeof sanitizedUpdates.userAgent === 'string') {
|
||||||
|
sanitizedUpdates.userAgent = sanitizedUpdates.userAgent.trim()
|
||||||
|
}
|
||||||
|
|
||||||
const parseProxyConfig = (value) => {
|
const parseProxyConfig = (value) => {
|
||||||
if (!value) {
|
if (!value) {
|
||||||
return null
|
return null
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ class DroidRelayService {
|
|||||||
comm: '/o/v1/chat/completions'
|
comm: '/o/v1/chat/completions'
|
||||||
}
|
}
|
||||||
|
|
||||||
this.userAgent = 'factory-cli/0.19.12'
|
this.userAgent = 'factory-cli/0.32.1'
|
||||||
this.systemPrompt = SYSTEM_PROMPT
|
this.systemPrompt = SYSTEM_PROMPT
|
||||||
this.API_KEY_STICKY_PREFIX = 'droid_api_key'
|
this.API_KEY_STICKY_PREFIX = 'droid_api_key'
|
||||||
}
|
}
|
||||||
@@ -241,7 +241,8 @@ class DroidRelayService {
|
|||||||
accessToken,
|
accessToken,
|
||||||
normalizedRequestBody,
|
normalizedRequestBody,
|
||||||
normalizedEndpoint,
|
normalizedEndpoint,
|
||||||
clientHeaders
|
clientHeaders,
|
||||||
|
account
|
||||||
)
|
)
|
||||||
|
|
||||||
if (selectedApiKey) {
|
if (selectedApiKey) {
|
||||||
@@ -982,11 +983,13 @@ class DroidRelayService {
|
|||||||
/**
|
/**
|
||||||
* 构建请求头
|
* 构建请求头
|
||||||
*/
|
*/
|
||||||
_buildHeaders(accessToken, requestBody, endpointType, clientHeaders = {}) {
|
_buildHeaders(accessToken, requestBody, endpointType, clientHeaders = {}, account = null) {
|
||||||
|
// 使用账户配置的 userAgent 或默认值
|
||||||
|
const userAgent = account?.userAgent || this.userAgent
|
||||||
const headers = {
|
const headers = {
|
||||||
'content-type': 'application/json',
|
'content-type': 'application/json',
|
||||||
authorization: `Bearer ${accessToken}`,
|
authorization: `Bearer ${accessToken}`,
|
||||||
'user-agent': this.userAgent,
|
'user-agent': userAgent,
|
||||||
'x-factory-client': 'cli',
|
'x-factory-client': 'cli',
|
||||||
connection: 'keep-alive'
|
connection: 'keep-alive'
|
||||||
}
|
}
|
||||||
@@ -1003,10 +1006,16 @@ class DroidRelayService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// OpenAI 特定头
|
// OpenAI 特定头 - 根据模型动态选择 provider
|
||||||
if (endpointType === 'openai') {
|
if (endpointType === 'openai') {
|
||||||
|
const model = (requestBody?.model || '').toLowerCase()
|
||||||
|
// -max 模型使用 openai provider,其他使用 azure_openai
|
||||||
|
if (model.includes('-max')) {
|
||||||
|
headers['x-api-provider'] = 'openai'
|
||||||
|
} else {
|
||||||
headers['x-api-provider'] = 'azure_openai'
|
headers['x-api-provider'] = 'azure_openai'
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Comm 端点根据模型动态设置 provider
|
// Comm 端点根据模型动态设置 provider
|
||||||
if (endpointType === 'comm') {
|
if (endpointType === 'comm') {
|
||||||
|
|||||||
@@ -1944,6 +1944,22 @@
|
|||||||
rows="4"
|
rows="4"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Droid User-Agent 配置 (OAuth/Manual 模式) -->
|
||||||
|
<div v-if="form.platform === 'droid'">
|
||||||
|
<label class="mb-3 block text-sm font-semibold text-gray-700 dark:text-gray-300"
|
||||||
|
>自定义 User-Agent (可选)</label
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
v-model="form.userAgent"
|
||||||
|
class="form-input w-full border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-200 dark:placeholder-gray-400"
|
||||||
|
placeholder="factory-cli/0.32.1"
|
||||||
|
type="text"
|
||||||
|
/>
|
||||||
|
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
||||||
|
留空使用默认值 factory-cli/0.32.1,可根据需要自定义
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- API Key 模式输入 -->
|
<!-- API Key 模式输入 -->
|
||||||
@@ -1989,6 +2005,22 @@
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Droid User-Agent 配置 -->
|
||||||
|
<div>
|
||||||
|
<label class="mb-3 block text-sm font-semibold text-gray-700 dark:text-gray-300"
|
||||||
|
>自定义 User-Agent (可选)</label
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
v-model="form.userAgent"
|
||||||
|
class="form-input w-full border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-200 dark:placeholder-gray-400"
|
||||||
|
placeholder="factory-cli/0.32.1"
|
||||||
|
type="text"
|
||||||
|
/>
|
||||||
|
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
||||||
|
留空使用默认值 factory-cli/0.32.1,可根据需要自定义
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="rounded-lg border border-purple-200 bg-white/70 p-3 text-xs text-purple-800 dark:border-purple-700 dark:bg-purple-800/20 dark:text-purple-100"
|
class="rounded-lg border border-purple-200 bg-white/70 p-3 text-xs text-purple-800 dark:border-purple-700 dark:bg-purple-800/20 dark:text-purple-100"
|
||||||
>
|
>
|
||||||
@@ -3639,6 +3671,22 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Droid User-Agent 配置 (编辑模式) -->
|
||||||
|
<div v-if="form.platform === 'droid'">
|
||||||
|
<label class="mb-3 block text-sm font-semibold text-gray-700 dark:text-gray-300"
|
||||||
|
>自定义 User-Agent (可选)</label
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
v-model="form.userAgent"
|
||||||
|
class="form-input w-full border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-200 dark:placeholder-gray-400"
|
||||||
|
placeholder="factory-cli/0.32.1"
|
||||||
|
type="text"
|
||||||
|
/>
|
||||||
|
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
||||||
|
留空使用默认值 factory-cli/0.32.1,可根据需要自定义
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- 代理设置 -->
|
<!-- 代理设置 -->
|
||||||
<ProxyConfig v-model="form.proxy" />
|
<ProxyConfig v-model="form.proxy" />
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user