mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-09-24 16:05:44 +08:00
fix(anthropic): 支持 API Key Bearer 认证方式
This commit is contained in:
@@ -2653,6 +2653,24 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="form.platform === 'anthropic' && accountCategory === 'apikey'"
|
||||
class="border-t border-gray-200 pt-4 dark:border-dark-600"
|
||||
>
|
||||
<div class="flex items-center justify-between gap-4">
|
||||
<div>
|
||||
<label class="input-label mb-0">{{ t('admin.accounts.anthropic.apiKeyAuthScheme') }}</label>
|
||||
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
||||
{{ t('admin.accounts.anthropic.apiKeyAuthSchemeDesc') }}
|
||||
</p>
|
||||
</div>
|
||||
<select v-model="anthropicAPIKeyAuthScheme" class="input w-52 text-sm">
|
||||
<option value="x_api_key">{{ t('admin.accounts.anthropic.apiKeyAuthSchemeXApiKey') }}</option>
|
||||
<option value="authorization_bearer">{{ t('admin.accounts.anthropic.apiKeyAuthSchemeBearer') }}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Anthropic API Key: Web Search Emulation (hidden when global disabled) -->
|
||||
<div
|
||||
v-if="form.platform === 'anthropic' && accountCategory === 'apikey' && webSearchGlobalEnabled"
|
||||
@@ -3503,7 +3521,9 @@ const openaiOAuthResponsesWebSocketV2Mode = ref<OpenAIWSMode>(OPENAI_WS_MODE_OFF
|
||||
const openaiAPIKeyResponsesWebSocketV2Mode = ref<OpenAIWSMode>(OPENAI_WS_MODE_OFF)
|
||||
const codexCLIOnlyEnabled = ref(false)
|
||||
const codexCLIOnlyAppServerEnabled = ref(false)
|
||||
type AnthropicAPIKeyAuthScheme = 'x_api_key' | 'authorization_bearer'
|
||||
const anthropicPassthroughEnabled = ref(false)
|
||||
const anthropicAPIKeyAuthScheme = ref<AnthropicAPIKeyAuthScheme>('x_api_key')
|
||||
const webSearchEmulationMode = ref('default')
|
||||
const webSearchGlobalEnabled = ref(false)
|
||||
const {
|
||||
@@ -3945,6 +3965,7 @@ watch(
|
||||
}
|
||||
if (newPlatform !== 'anthropic') {
|
||||
anthropicPassthroughEnabled.value = false
|
||||
anthropicAPIKeyAuthScheme.value = 'x_api_key'
|
||||
webSearchEmulationMode.value = 'default'
|
||||
}
|
||||
// Reset OAuth states
|
||||
@@ -3967,6 +3988,7 @@ watch(
|
||||
}
|
||||
if (platform !== 'anthropic' || category !== 'apikey') {
|
||||
anthropicPassthroughEnabled.value = false
|
||||
anthropicAPIKeyAuthScheme.value = 'x_api_key'
|
||||
webSearchEmulationMode.value = 'default'
|
||||
}
|
||||
}
|
||||
@@ -4346,6 +4368,7 @@ const resetForm = () => {
|
||||
codexCLIOnlyEnabled.value = false
|
||||
codexCLIOnlyAppServerEnabled.value = false
|
||||
anthropicPassthroughEnabled.value = false
|
||||
anthropicAPIKeyAuthScheme.value = 'x_api_key'
|
||||
webSearchEmulationMode.value = 'default'
|
||||
// Reset quota control state
|
||||
windowCostEnabled.value = false
|
||||
@@ -4465,6 +4488,11 @@ const buildAnthropicExtra = (base?: Record<string, unknown>): Record<string, unk
|
||||
} else {
|
||||
delete extra.anthropic_passthrough
|
||||
}
|
||||
if (anthropicAPIKeyAuthScheme.value === 'authorization_bearer') {
|
||||
extra.anthropic_apikey_auth_scheme = 'authorization_bearer'
|
||||
} else {
|
||||
delete extra.anthropic_apikey_auth_scheme
|
||||
}
|
||||
if (webSearchEmulationMode.value === 'default') {
|
||||
delete extra.web_search_emulation
|
||||
} else {
|
||||
|
||||
@@ -1537,6 +1537,24 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="account?.platform === 'anthropic' && account?.type === 'apikey'"
|
||||
class="border-t border-gray-200 pt-4 dark:border-dark-600"
|
||||
>
|
||||
<div class="flex items-center justify-between gap-4">
|
||||
<div>
|
||||
<label class="input-label mb-0">{{ t('admin.accounts.anthropic.apiKeyAuthScheme') }}</label>
|
||||
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
||||
{{ t('admin.accounts.anthropic.apiKeyAuthSchemeDesc') }}
|
||||
</p>
|
||||
</div>
|
||||
<select v-model="anthropicAPIKeyAuthScheme" class="input w-52 text-sm">
|
||||
<option value="x_api_key">{{ t('admin.accounts.anthropic.apiKeyAuthSchemeXApiKey') }}</option>
|
||||
<option value="authorization_bearer">{{ t('admin.accounts.anthropic.apiKeyAuthSchemeBearer') }}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Anthropic API Key: Web Search Emulation (hidden when global disabled) -->
|
||||
<div
|
||||
v-if="account?.platform === 'anthropic' && account?.type === 'apikey' && webSearchGlobalEnabled"
|
||||
@@ -2610,7 +2628,9 @@ const codexCLIOnlyEnabled = ref(false)
|
||||
const codexCLIOnlyAppServerEnabled = ref(false)
|
||||
type CodexImageGenerationBridgeMode = 'inherit' | 'enabled' | 'disabled'
|
||||
const codexImageGenerationBridgeMode = ref<CodexImageGenerationBridgeMode>('inherit')
|
||||
type AnthropicAPIKeyAuthScheme = 'x_api_key' | 'authorization_bearer'
|
||||
const anthropicPassthroughEnabled = ref(false)
|
||||
const anthropicAPIKeyAuthScheme = ref<AnthropicAPIKeyAuthScheme>('x_api_key')
|
||||
const webSearchEmulationMode = ref('default')
|
||||
const webSearchGlobalEnabled = ref(false)
|
||||
const {
|
||||
@@ -3015,6 +3035,7 @@ const syncFormFromAccount = (newAccount: Account | null) => {
|
||||
codexCLIOnlyAppServerEnabled.value = false
|
||||
codexImageGenerationBridgeMode.value = 'inherit'
|
||||
anthropicPassthroughEnabled.value = false
|
||||
anthropicAPIKeyAuthScheme.value = 'x_api_key'
|
||||
webSearchEmulationMode.value = 'default'
|
||||
if (newAccount.platform === 'openai' && (newAccount.type === 'oauth' || newAccount.type === 'apikey')) {
|
||||
openaiPassthroughEnabled.value = extra?.openai_passthrough === true || extra?.openai_oauth_passthrough === true
|
||||
@@ -3061,6 +3082,9 @@ const syncFormFromAccount = (newAccount: Account | null) => {
|
||||
}
|
||||
if (newAccount.platform === 'anthropic' && newAccount.type === 'apikey') {
|
||||
anthropicPassthroughEnabled.value = extra?.anthropic_passthrough === true
|
||||
anthropicAPIKeyAuthScheme.value = extra?.anthropic_apikey_auth_scheme === 'authorization_bearer'
|
||||
? 'authorization_bearer'
|
||||
: 'x_api_key'
|
||||
// 三态:string "default"/"enabled"/"disabled",向后兼容旧 bool
|
||||
const wsVal = extra?.web_search_emulation
|
||||
if (wsVal === 'enabled' || wsVal === 'disabled') {
|
||||
@@ -4101,6 +4125,11 @@ const handleSubmit = async () => {
|
||||
} else {
|
||||
delete newExtra.anthropic_passthrough
|
||||
}
|
||||
if (anthropicAPIKeyAuthScheme.value === 'authorization_bearer') {
|
||||
newExtra.anthropic_apikey_auth_scheme = 'authorization_bearer'
|
||||
} else {
|
||||
delete newExtra.anthropic_apikey_auth_scheme
|
||||
}
|
||||
if (webSearchEmulationMode.value === 'default') {
|
||||
delete newExtra.web_search_emulation
|
||||
} else {
|
||||
|
||||
@@ -3563,6 +3563,10 @@ export default {
|
||||
apiKeyPassthrough: 'Auto passthrough (auth only)',
|
||||
apiKeyPassthroughDesc:
|
||||
'Only applies to Anthropic API Key accounts. When enabled, messages/count_tokens are forwarded in passthrough mode with auth replacement only, while billing/concurrency/audit and safety filtering are preserved. Disable to roll back immediately.',
|
||||
apiKeyAuthScheme: 'Upstream auth scheme',
|
||||
apiKeyAuthSchemeDesc: 'Choose the API key auth header used when forwarding to an Anthropic-compatible upstream. Ollama Cloud uses Authorization: Bearer.',
|
||||
apiKeyAuthSchemeXApiKey: 'x-api-key',
|
||||
apiKeyAuthSchemeBearer: 'Authorization: Bearer',
|
||||
webSearchEmulation: 'Web Search Emulation',
|
||||
webSearchEmulationDesc:
|
||||
'Enable web search emulation for this API Key account. When a pure web_search request is detected, the gateway calls a third-party search API and constructs the response locally. Default follows channel config.',
|
||||
|
||||
@@ -3732,6 +3732,10 @@ export default {
|
||||
apiKeyPassthrough: '自动透传(仅替换认证)',
|
||||
apiKeyPassthroughDesc:
|
||||
'仅对 Anthropic API Key 生效。开启后,messages/count_tokens 请求将透传上游并仅替换认证,保留计费/并发/审计及必要安全过滤;关闭即可回滚到现有兼容链路。',
|
||||
apiKeyAuthScheme: '上游认证方式',
|
||||
apiKeyAuthSchemeDesc: '选择转发到 Anthropic-compatible 上游时使用的 API Key 认证头。Ollama Cloud 使用 Authorization: Bearer。',
|
||||
apiKeyAuthSchemeXApiKey: 'x-api-key',
|
||||
apiKeyAuthSchemeBearer: 'Authorization: Bearer',
|
||||
webSearchEmulation: 'Web Search 模拟',
|
||||
webSearchEmulationDesc:
|
||||
'为该 API Key 账号启用 web search 模拟。客户端发送纯 web_search 请求时,由网关调用第三方搜索 API 并构造响应返回。默认跟随渠道配置。',
|
||||
|
||||
Reference in New Issue
Block a user