diff --git a/backend/internal/service/account_header_override.go b/backend/internal/service/account_header_override.go index 506a79084d..078d98d832 100644 --- a/backend/internal/service/account_header_override.go +++ b/backend/internal/service/account_header_override.go @@ -9,8 +9,8 @@ import ( "golang.org/x/net/http/httpguts" ) -// 请求头覆写(header override):对 Anthropic / OpenAI 平台的 api_key 账号 -// 以及 Grok 平台的 api_key / oauth 账号生效。 +// 请求头覆写(header override):对 Anthropic / OpenAI / Kimi / Zhipu / DeepSeek +// 平台的 api_key 账号,以及 Grok 平台的 api_key / oauth 账号生效。 // 管理员在账号上配置一组 header name -> value,转发到上游前用配置值覆盖同名请求头 // (匹配不区分大小写);value 为空的条目视为"未填写",不参与覆盖。 const ( @@ -70,14 +70,15 @@ func isHeaderOverrideBlockedName(lowerName string) bool { } // IsHeaderOverrideEligible 报告账号类型是否支持请求头覆写。 -// Anthropic / OpenAI 仅开放 api_key 账号;Grok 额外开放 oauth 账号—— +// Anthropic / OpenAI / Kimi / Zhipu / DeepSeek 仅开放 api_key 账号; +// Grok 额外开放 oauth 账号—— // 订阅流量改发自定义转发地址时,通常需要补充中间层要求的准入头。 func (a *Account) IsHeaderOverrideEligible() bool { if a == nil { return false } switch a.Platform { - case PlatformAnthropic, PlatformOpenAI: + case PlatformAnthropic, PlatformOpenAI, PlatformKimi, PlatformZhipu, PlatformDeepseek: return a.Type == AccountTypeAPIKey case PlatformGrok: return a.Type == AccountTypeAPIKey || a.Type == AccountTypeOAuth diff --git a/backend/internal/service/account_header_override_test.go b/backend/internal/service/account_header_override_test.go index 998fa543b5..9d99257f28 100644 --- a/backend/internal/service/account_header_override_test.go +++ b/backend/internal/service/account_header_override_test.go @@ -27,8 +27,14 @@ func TestIsHeaderOverrideEligible(t *testing.T) { }{ {"anthropic apikey", PlatformAnthropic, AccountTypeAPIKey, true}, {"openai apikey", PlatformOpenAI, AccountTypeAPIKey, true}, + {"kimi apikey", PlatformKimi, AccountTypeAPIKey, true}, + {"zhipu apikey", PlatformZhipu, AccountTypeAPIKey, true}, + {"deepseek apikey", PlatformDeepseek, AccountTypeAPIKey, true}, {"anthropic oauth", PlatformAnthropic, AccountTypeOAuth, false}, {"openai oauth", PlatformOpenAI, AccountTypeOAuth, false}, + {"kimi oauth", PlatformKimi, AccountTypeOAuth, false}, + {"zhipu oauth", PlatformZhipu, AccountTypeOAuth, false}, + {"deepseek oauth", PlatformDeepseek, AccountTypeOAuth, false}, {"gemini apikey", PlatformGemini, AccountTypeAPIKey, false}, {"grok apikey", PlatformGrok, AccountTypeAPIKey, true}, {"grok oauth", PlatformGrok, AccountTypeOAuth, true}, diff --git a/frontend/src/components/account/BulkEditAccountModal.vue b/frontend/src/components/account/BulkEditAccountModal.vue index e8159c7bfb..0576194248 100644 --- a/frontend/src/components/account/BulkEditAccountModal.vue +++ b/frontend/src/components/account/BulkEditAccountModal.vue @@ -602,7 +602,7 @@ - +
@@ -1599,7 +1599,7 @@ const allBillingProbeCapable = computed(() => { ) }) -// 是否全部为 anthropic/openai 平台的 apikey 账号(请求头覆写仅在此条件下显示) +// 是否全部为支持请求头覆写的平台/账号类型 // 所选平台 × 所选类型的全组合均需具备覆写资格(实际选中账号是该组合的子集, // 按交叉积判定偏保守但绝不放行不合资格的账号) const allHeaderOverrideCapable = computed(() => { diff --git a/frontend/src/components/account/CreateAccountModal.vue b/frontend/src/components/account/CreateAccountModal.vue index 26b4efb2b7..55a0831ae1 100644 --- a/frontend/src/components/account/CreateAccountModal.vue +++ b/frontend/src/components/account/CreateAccountModal.vue @@ -1661,7 +1661,7 @@
- +
{ credentials.custom_error_codes = [...selectedErrorCodes.value] } - // Add header override if enabled (anthropic/openai/grok apikey) + // Add header override if enabled for this API-key platform if (isHeaderOverrideCapable(form.platform, 'apikey')) { if (headerOverrideEnabled.value) { const headerError = validateHeaderOverrideRows(headerOverrideRows.value) diff --git a/frontend/src/components/account/EditAccountModal.vue b/frontend/src/components/account/EditAccountModal.vue index 1a5654bd0f..9369b229aa 100644 --- a/frontend/src/components/account/EditAccountModal.vue +++ b/frontend/src/components/account/EditAccountModal.vue @@ -540,7 +540,7 @@
- +
@@ -3730,7 +3730,7 @@ const syncFormFromAccount = (newAccount: Account | null) => { loadTempUnschedRules(credentials) loadAccountSchedulingThresholdOverride(newAccount.platform, credentials) - // Load header override state (anthropic/openai apikey + grok apikey/oauth) + // Load header override state for eligible account platforms/types headerOverrideEnabled.value = false headerOverrideRows.value = [] if (newAccount.credentials && isHeaderOverrideCapable(newAccount.platform, newAccount.type)) { @@ -4520,7 +4520,7 @@ const handleSubmit = async () => { delete newCredentials.custom_error_codes } - // Add header override if enabled (anthropic/openai/grok apikey) + // Add header override if enabled for this API-key platform if (isHeaderOverrideCapable(props.account.platform, 'apikey')) { if (headerOverrideEnabled.value) { const headerError = validateHeaderOverrideRows(headerOverrideRows.value) diff --git a/frontend/src/components/account/__tests__/BulkEditAccountModal.spec.ts b/frontend/src/components/account/__tests__/BulkEditAccountModal.spec.ts index dec8be054a..b6fa614223 100644 --- a/frontend/src/components/account/__tests__/BulkEditAccountModal.spec.ts +++ b/frontend/src/components/account/__tests__/BulkEditAccountModal.spec.ts @@ -217,6 +217,24 @@ describe('BulkEditAccountModal', () => { expect(wrapper.findAll('[data-testid="grok-base-url-preset"]').length).toBe(0) }) + it.each(['kimi', 'zhipu', 'deepseek'])('全部目标为 %s API Key 时展示请求头覆写', (platform) => { + const wrapper = mountModal({ + selectedPlatforms: [platform], + selectedTypes: ['apikey'] + }) + + expect(wrapper.find('#bulk-edit-header-override-enabled').exists()).toBe(true) + }) + + it.each(['kimi', 'zhipu', 'deepseek'])('目标为 %s OAuth 时不展示请求头覆写', (platform) => { + const wrapper = mountModal({ + selectedPlatforms: [platform], + selectedTypes: ['oauth'] + }) + + expect(wrapper.find('#bulk-edit-header-override-enabled').exists()).toBe(false) + }) + it('全部目标为 Grok OAuth 时,第三方 base_url 正常提交', async () => { const wrapper = mountModal({ selectedPlatforms: ['grok'], diff --git a/frontend/src/components/account/__tests__/credentialsBuilder.spec.ts b/frontend/src/components/account/__tests__/credentialsBuilder.spec.ts index 6d4b59e2a7..e5cee32ad0 100644 --- a/frontend/src/components/account/__tests__/credentialsBuilder.spec.ts +++ b/frontend/src/components/account/__tests__/credentialsBuilder.spec.ts @@ -106,6 +106,13 @@ describe('isHeaderOverrideCapable', () => { expect(isHeaderOverrideCapable('openai', 'oauth')).toBe(false) }) + it('kimi/zhipu/deepseek only support apikey accounts', () => { + for (const platform of ['kimi', 'zhipu', 'deepseek']) { + expect(isHeaderOverrideCapable(platform, 'apikey')).toBe(true) + expect(isHeaderOverrideCapable(platform, 'oauth')).toBe(false) + } + }) + it('grok supports both apikey and oauth accounts', () => { expect(isHeaderOverrideCapable('grok', 'apikey')).toBe(true) expect(isHeaderOverrideCapable('grok', 'oauth')).toBe(true) @@ -468,4 +475,3 @@ describe('plan_type helpers', () => { }) }) }) - diff --git a/frontend/src/components/account/credentialsBuilder.ts b/frontend/src/components/account/credentialsBuilder.ts index 054fd46238..1ba2cb22bf 100644 --- a/frontend/src/components/account/credentialsBuilder.ts +++ b/frontend/src/components/account/credentialsBuilder.ts @@ -25,7 +25,7 @@ export function applyAntigravityProjectID( } } -// ========== 请求头覆写(anthropic/openai 的 api_key 账号 + grok 的 api_key/oauth 账号) ========== +// ========== 请求头覆写(API-key 平台 + grok 的 api_key/oauth 账号) ========== export const HEADER_OVERRIDE_ENABLED_CREDENTIAL_KEY = 'header_override_enabled' export const HEADER_OVERRIDES_CREDENTIAL_KEY = 'header_overrides' @@ -37,7 +37,13 @@ export interface HeaderOverrideRow { /** 请求头覆写资格(与后端 IsHeaderOverrideEligible 保持一致) */ export function isHeaderOverrideCapable(platform: string, type: string): boolean { - if (platform === 'anthropic' || platform === 'openai') { + if ( + platform === 'anthropic' || + platform === 'openai' || + platform === 'kimi' || + platform === 'zhipu' || + platform === 'deepseek' + ) { return type === 'apikey' } if (platform === 'grok') {