feat: 为openai分组增加messages调度开关和默认映射模型

This commit is contained in:
shaw
2026-03-07 17:02:19 +08:00
parent 351a08f813
commit 92d35409de
27 changed files with 711 additions and 19 deletions

View File

@@ -146,6 +146,7 @@ interface Props {
apiKey: string
baseUrl: string
platform: GroupPlatform | null
allowMessagesDispatch?: boolean
}
interface Emits {
@@ -265,13 +266,17 @@ const SparkleIcon = {
const clientTabs = computed((): TabConfig[] => {
if (!props.platform) return []
switch (props.platform) {
case 'openai':
return [
case 'openai': {
const tabs: TabConfig[] = [
{ id: 'codex', label: t('keys.useKeyModal.cliTabs.codexCli'), icon: TerminalIcon },
{ id: 'codex-ws', label: t('keys.useKeyModal.cliTabs.codexCliWs'), icon: TerminalIcon },
{ id: 'claude', label: t('keys.useKeyModal.cliTabs.claudeCode'), icon: TerminalIcon },
{ id: 'opencode', label: t('keys.useKeyModal.cliTabs.opencode'), icon: TerminalIcon }
]
if (props.allowMessagesDispatch) {
tabs.push({ id: 'claude', label: t('keys.useKeyModal.cliTabs.claudeCode'), icon: TerminalIcon })
}
tabs.push({ id: 'opencode', label: t('keys.useKeyModal.cliTabs.opencode'), icon: TerminalIcon })
return tabs
}
case 'gemini':
return [
{ id: 'gemini', label: t('keys.useKeyModal.cliTabs.geminiCli'), icon: SparkleIcon },

View File

@@ -1443,6 +1443,14 @@ export default {
fallbackHint: 'Non-Claude Code requests will use this group. Leave empty to reject directly.',
noFallback: 'No Fallback (Reject)'
},
openaiMessages: {
title: 'OpenAI Messages Dispatch',
allowDispatch: 'Allow /v1/messages dispatch',
allowDispatchHint: 'When enabled, API keys in this OpenAI group can dispatch requests through /v1/messages endpoint',
defaultModel: 'Default mapped model',
defaultModelPlaceholder: 'e.g., gpt-4.1',
defaultModelHint: 'When account has no model mapping configured, all request models will be mapped to this model'
},
invalidRequestFallback: {
title: 'Invalid Request Fallback Group',
hint: 'Triggered only when upstream explicitly returns prompt too long. Leave empty to disable fallback.',

View File

@@ -1530,6 +1530,14 @@ export default {
fallbackHint: '非 Claude Code 请求将使用此分组,留空则直接拒绝',
noFallback: '不降级(直接拒绝)'
},
openaiMessages: {
title: 'OpenAI Messages 调度配置',
allowDispatch: '允许 /v1/messages 调度',
allowDispatchHint: '启用后,此 OpenAI 分组的 API Key 可以通过 /v1/messages 端点调度请求',
defaultModel: '默认映射模型',
defaultModelPlaceholder: '例如: gpt-4.1',
defaultModelHint: '当账号未配置模型映射时,所有请求模型将映射到此模型'
},
invalidRequestFallback: {
title: '无效请求兜底分组',
hint: '仅当上游明确返回 prompt too long 时才会触发,留空表示不兜底',

View File

@@ -389,6 +389,8 @@ export interface Group {
claude_code_only: boolean
fallback_group_id: number | null
fallback_group_id_on_invalid_request: number | null
// OpenAI Messages 调度开关(用户侧需要此字段判断是否展示 Claude Code 教程)
allow_messages_dispatch?: boolean
created_at: string
updated_at: string
}
@@ -407,6 +409,9 @@ export interface AdminGroup extends Group {
// 分组下账号数量(仅管理员可见)
account_count?: number
// OpenAI Messages 调度配置(仅 openai 平台使用)
default_mapped_model?: string
// 分组排序
sort_order: number
}

View File

@@ -708,6 +708,44 @@
</div>
</div>
<!-- OpenAI Messages 调度配置 openai 平台 -->
<div v-if="createForm.platform === 'openai'" class="border-t border-gray-200 dark:border-dark-400 pt-4 mt-4">
<h4 class="text-sm font-medium text-gray-700 dark:text-gray-300 mb-3">{{ t('admin.groups.openaiMessages.title') }}</h4>
<!-- 允许 Messages 调度开关 -->
<div class="flex items-center justify-between">
<label class="text-sm text-gray-600 dark:text-gray-400">{{ t('admin.groups.openaiMessages.allowDispatch') }}</label>
<button
type="button"
@click="createForm.allow_messages_dispatch = !createForm.allow_messages_dispatch"
class="relative inline-flex h-6 w-12 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none"
:class="
createForm.allow_messages_dispatch ? 'bg-primary-500' : 'bg-gray-300 dark:bg-dark-600'
"
>
<span
class="pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out"
:class="
createForm.allow_messages_dispatch ? 'translate-x-6' : 'translate-x-1'
"
/>
</button>
</div>
<p class="text-xs text-gray-500 dark:text-gray-400 mt-1">{{ t('admin.groups.openaiMessages.allowDispatchHint') }}</p>
<!-- 默认映射模型仅当开关打开时显示 -->
<div v-if="createForm.allow_messages_dispatch" class="mt-3">
<label class="input-label">{{ t('admin.groups.openaiMessages.defaultModel') }}</label>
<input
v-model="createForm.default_mapped_model"
type="text"
:placeholder="t('admin.groups.openaiMessages.defaultModelPlaceholder')"
class="input"
/>
<p class="input-hint">{{ t('admin.groups.openaiMessages.defaultModelHint') }}</p>
</div>
</div>
<!-- 无效请求兜底 anthropic/antigravity 平台且非订阅分组 -->
<div
v-if="['anthropic', 'antigravity'].includes(createForm.platform) && createForm.subscription_type !== 'subscription'"
@@ -1405,6 +1443,44 @@
</div>
</div>
<!-- OpenAI Messages 调度配置 openai 平台 -->
<div v-if="editForm.platform === 'openai'" class="border-t border-gray-200 dark:border-dark-400 pt-4 mt-4">
<h4 class="text-sm font-medium text-gray-700 dark:text-gray-300 mb-3">{{ t('admin.groups.openaiMessages.title') }}</h4>
<!-- 允许 Messages 调度开关 -->
<div class="flex items-center justify-between">
<label class="text-sm text-gray-600 dark:text-gray-400">{{ t('admin.groups.openaiMessages.allowDispatch') }}</label>
<button
type="button"
@click="editForm.allow_messages_dispatch = !editForm.allow_messages_dispatch"
class="relative inline-flex h-6 w-12 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none"
:class="
editForm.allow_messages_dispatch ? 'bg-primary-500' : 'bg-gray-300 dark:bg-dark-600'
"
>
<span
class="pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out"
:class="
editForm.allow_messages_dispatch ? 'translate-x-6' : 'translate-x-1'
"
/>
</button>
</div>
<p class="text-xs text-gray-500 dark:text-gray-400 mt-1">{{ t('admin.groups.openaiMessages.allowDispatchHint') }}</p>
<!-- 默认映射模型仅当开关打开时显示 -->
<div v-if="editForm.allow_messages_dispatch" class="mt-3">
<label class="input-label">{{ t('admin.groups.openaiMessages.defaultModel') }}</label>
<input
v-model="editForm.default_mapped_model"
type="text"
:placeholder="t('admin.groups.openaiMessages.defaultModelPlaceholder')"
class="input"
/>
<p class="input-hint">{{ t('admin.groups.openaiMessages.defaultModelHint') }}</p>
</div>
</div>
<!-- 无效请求兜底 anthropic/antigravity 平台且非订阅分组 -->
<div
v-if="['anthropic', 'antigravity'].includes(editForm.platform) && editForm.subscription_type !== 'subscription'"
@@ -1920,6 +1996,9 @@ const createForm = reactive({
claude_code_only: false,
fallback_group_id: null as number | null,
fallback_group_id_on_invalid_request: null as number | null,
// OpenAI Messages 调度配置(仅 openai 平台使用)
allow_messages_dispatch: false,
default_mapped_model: 'gpt-5.4',
// 模型路由开关
model_routing_enabled: false,
// 支持的模型系列(仅 antigravity 平台)
@@ -2161,6 +2240,9 @@ const editForm = reactive({
claude_code_only: false,
fallback_group_id: null as number | null,
fallback_group_id_on_invalid_request: null as number | null,
// OpenAI Messages 调度配置(仅 openai 平台使用)
allow_messages_dispatch: false,
default_mapped_model: '',
// 模型路由开关
model_routing_enabled: false,
// 支持的模型系列(仅 antigravity 平台)
@@ -2260,6 +2342,8 @@ const closeCreateModal = () => {
createForm.claude_code_only = false
createForm.fallback_group_id = null
createForm.fallback_group_id_on_invalid_request = null
createForm.allow_messages_dispatch = false
createForm.default_mapped_model = 'gpt-5.4'
createForm.supported_model_scopes = ['claude', 'gemini_text', 'gemini_image']
createForm.mcp_xml_inject = true
createForm.copy_accounts_from_group_ids = []
@@ -2320,6 +2404,8 @@ const handleEdit = async (group: AdminGroup) => {
editForm.claude_code_only = group.claude_code_only || false
editForm.fallback_group_id = group.fallback_group_id
editForm.fallback_group_id_on_invalid_request = group.fallback_group_id_on_invalid_request
editForm.allow_messages_dispatch = group.allow_messages_dispatch || false
editForm.default_mapped_model = group.default_mapped_model || ''
editForm.model_routing_enabled = group.model_routing_enabled || false
editForm.supported_model_scopes = group.supported_model_scopes || ['claude', 'gemini_text', 'gemini_image']
editForm.mcp_xml_inject = group.mcp_xml_inject ?? true
@@ -2410,6 +2496,10 @@ watch(
if (!['anthropic', 'antigravity'].includes(newVal)) {
createForm.fallback_group_id_on_invalid_request = null
}
if (newVal !== 'openai') {
createForm.allow_messages_dispatch = false
createForm.default_mapped_model = ''
}
}
)

View File

@@ -899,6 +899,7 @@
:api-key="selectedKey?.key || ''"
:base-url="publicSettings?.api_base_url || ''"
:platform="selectedKey?.group?.platform || null"
:allow-messages-dispatch="selectedKey?.group?.allow_messages_dispatch || false"
@close="closeUseKeyModal"
/>