+ {{ t('admin.accounts.openai.responsesModeTextDisabledHint') }}
+
@@ -2568,10 +2579,29 @@ const openAIResponsesModeOptions = computed(() => [
{ value: 'force_responses', label: t('admin.accounts.openai.responsesModeForceResponses') },
{ value: 'force_chat_completions', label: t('admin.accounts.openai.responsesModeForceChatCompletions') }
])
+const openAITextEndpointCapabilityLabel = computed(() => {
+ if (openAIResponsesMode.value === 'force_responses') {
+ return t('admin.accounts.openai.capabilityResponses')
+ }
+ if (openAIResponsesMode.value === 'force_chat_completions') {
+ return t('admin.accounts.openai.capabilityChatCompletions')
+ }
+ const extra = props.account?.extra as Record | undefined
+ if (extra?.openai_responses_supported === true) {
+ return t('admin.accounts.openai.capabilityResponsesAuto')
+ }
+ if (extra?.openai_responses_supported === false) {
+ return t('admin.accounts.openai.capabilityChatCompletionsAuto')
+ }
+ return t('admin.accounts.openai.capabilityTextAuto')
+})
const openAIEndpointCapabilityOptions = computed<{ value: OpenAIEndpointCapability; label: string }[]>(() => [
- { value: 'chat_completions', label: t('admin.accounts.openai.capabilityChatCompletions') },
+ { value: 'chat_completions', label: openAITextEndpointCapabilityLabel.value },
{ value: 'embeddings', label: t('admin.accounts.openai.capabilityEmbeddings') }
])
+const openAITextGenerationCapabilityEnabled = computed(() =>
+ openAIEndpointCapabilities.value.includes('chat_completions')
+)
const normalizeOpenAIEndpointCapabilities = (values: OpenAIEndpointCapability[]) => {
const allowed: OpenAIEndpointCapability[] = ['chat_completions', 'embeddings']
@@ -2609,6 +2639,9 @@ const toggleOpenAIEndpointCapability = (capability: OpenAIEndpointCapability, ev
openAIEndpointCapabilities.value = openAIEndpointCapabilities.value.filter(
(value) => value !== capability
)
+ if (!openAITextGenerationCapabilityEnabled.value) {
+ openAIResponsesMode.value = 'auto'
+ }
return
}
openAIEndpointCapabilities.value = normalizeOpenAIEndpointCapabilities([
@@ -2826,6 +2859,9 @@ const syncFormFromAccount = (newAccount: Account | null) => {
openAIEndpointCapabilities.value = readOpenAIEndpointCapabilities(
newAccount.credentials as Record | undefined
)
+ if (!openAITextGenerationCapabilityEnabled.value) {
+ openAIResponsesMode.value = 'auto'
+ }
}
const codexImageGenerationBridgeValue = typeof extra?.codex_image_generation_bridge === 'boolean'
? extra.codex_image_generation_bridge
@@ -3945,7 +3981,7 @@ const handleSubmit = async () => {
newExtra.openai_compact_mode = openAICompactMode.value
}
if (props.account.type === 'apikey') {
- if (openAIResponsesMode.value === 'auto') {
+ if (!openAITextGenerationCapabilityEnabled.value || openAIResponsesMode.value === 'auto') {
delete newExtra.openai_responses_mode
} else {
newExtra.openai_responses_mode = openAIResponsesMode.value
diff --git a/frontend/src/components/account/__tests__/EditAccountModal.spec.ts b/frontend/src/components/account/__tests__/EditAccountModal.spec.ts
index db012a30..4561924f 100644
--- a/frontend/src/components/account/__tests__/EditAccountModal.spec.ts
+++ b/frontend/src/components/account/__tests__/EditAccountModal.spec.ts
@@ -367,6 +367,37 @@ describe('EditAccountModal', () => {
])
})
+ it('disables text generation protocol when only embeddings requests are accepted', async () => {
+ const account = buildAccount()
+ account.credentials.openai_capabilities = ['embeddings']
+ account.extra = {
+ openai_responses_mode: 'force_responses',
+ openai_responses_supported: true
+ }
+ updateAccountMock.mockReset()
+ checkMixedChannelRiskMock.mockReset()
+ checkMixedChannelRiskMock.mockResolvedValue({ has_risk: false })
+ updateAccountMock.mockResolvedValue(account)
+
+ const wrapper = mountModal(account)
+
+ const responsesModeSelect = wrapper.get(
+ '[data-testid="openai-responses-mode-select"]'
+ )
+
+ expect(responsesModeSelect.element.disabled).toBe(true)
+ expect(wrapper.find('[data-testid="openai-responses-mode-not-applicable"]').exists()).toBe(true)
+
+ await wrapper.get('form#edit-account-form').trigger('submit.prevent')
+
+ expect(updateAccountMock).toHaveBeenCalledTimes(1)
+ expect(updateAccountMock.mock.calls[0]?.[1]?.credentials?.openai_capabilities).toEqual([
+ 'embeddings'
+ ])
+ expect(updateAccountMock.mock.calls[0]?.[1]?.extra).not.toHaveProperty('openai_responses_mode')
+ expect(updateAccountMock.mock.calls[0]?.[1]?.extra?.openai_responses_supported).toBe(true)
+ })
+
it('submits account-level Codex image generation bridge override', async () => {
const account = buildAccount()
account.extra = {
diff --git a/frontend/src/i18n/locales/en.ts b/frontend/src/i18n/locales/en.ts
index ec659dd4..168e7129 100644
--- a/frontend/src/i18n/locales/en.ts
+++ b/frontend/src/i18n/locales/en.ts
@@ -3349,14 +3349,20 @@ export default {
'Automatic passthrough is currently enabled: it only affects HTTP passthrough and does not disable WS mode.',
responsesMode: 'Responses API support',
responsesModeDesc:
- 'Only applies to OpenAI API Key accounts. Auto follows probe results; force modes override probing.',
+ 'Only applies to the OpenAI API Key text forwarding path. Auto follows probe results; force modes override probing.',
responsesModeAuto: 'Auto',
responsesModeForceResponses: 'Force Responses',
responsesModeForceChatCompletions: 'Force Chat Completions',
+ responsesModeTextDisabledHint:
+ 'Not applicable when the Responses / Chat Completions endpoint is not enabled.',
endpointCapabilities: 'Endpoint capabilities',
endpointCapabilitiesDesc:
- 'Used by account routing. Both endpoints are allowed by default; if the upstream only supports one, select only the supported endpoint.',
+ 'Used by account routing. The text endpoint follows the Responses API support setting above and is shown as Responses, Chat Completions, or auto mode; Embeddings independently controls /v1/embeddings.',
+ capabilityResponses: 'Responses',
+ capabilityTextAuto: 'Responses / Chat Completions (Auto)',
+ capabilityResponsesAuto: 'Responses (auto probe)',
capabilityChatCompletions: 'Chat Completions',
+ capabilityChatCompletionsAuto: 'Chat Completions (auto probe)',
capabilityEmbeddings: 'Embeddings',
responsesStatusAutoSupported: 'Auto probe: Responses',
responsesStatusAutoUnsupported: 'Auto probe: Chat Completions',
diff --git a/frontend/src/i18n/locales/zh.ts b/frontend/src/i18n/locales/zh.ts
index 36b0d8c3..adf3dcaf 100644
--- a/frontend/src/i18n/locales/zh.ts
+++ b/frontend/src/i18n/locales/zh.ts
@@ -3495,14 +3495,19 @@ export default {
responsesWebsocketsV2PassthroughHint: '当前已开启自动透传:仅影响 HTTP 透传链路,不影响 WS mode。',
responsesMode: 'Responses API 支持',
responsesModeDesc:
- '仅对 OpenAI API Key 生效。自动跟随探测结果,强制模式会覆盖自动探测。',
+ '仅对 OpenAI API Key 的文本转发链路生效。自动跟随探测结果,强制模式会覆盖自动探测。',
responsesModeAuto: '自动',
responsesModeForceResponses: '强制 Responses',
responsesModeForceChatCompletions: '强制 Chat Completions',
+ responsesModeTextDisabledHint: '未启用 Responses / Chat Completions 端点时,此设置不适用。',
endpointCapabilities: '端点能力',
endpointCapabilitiesDesc:
- '用于调度筛选。默认两个端点都可用;如果上游只支持其中一个,请只勾选实际支持的端点。',
+ '用于调度筛选。文本端点会跟随上方 Responses API 支持显示为 Responses、Chat Completions 或自动模式;Embeddings 独立控制 /v1/embeddings。',
+ capabilityResponses: 'Responses',
+ capabilityTextAuto: 'Responses / Chat Completions(自动)',
+ capabilityResponsesAuto: 'Responses(自动探测)',
capabilityChatCompletions: 'Chat Completions',
+ capabilityChatCompletionsAuto: 'Chat Completions(自动探测)',
capabilityEmbeddings: 'Embeddings',
responsesStatusAutoSupported: '自动探测:Responses',
responsesStatusAutoUnsupported: '自动探测:Chat Completions',