From d9dcbc4fb3c289c9acf76ee25403939c7734663d Mon Sep 17 00:00:00 2001 From: Sakurasan <26715255+Sakurasan@users.noreply.github.com> Date: Sat, 15 Aug 2026 16:55:48 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B8=A0=E9=81=93:=20=E7=A7=BB=E9=99=A4?= =?UTF-8?q?=E4=BE=9B=E5=BA=94=E5=95=86=E4=B8=8B=E6=8B=89,=20=E6=A0=BC?= =?UTF-8?q?=E5=BC=8F=E5=A4=9A=E9=80=89=E4=B8=BA=E5=94=AF=E4=B8=80=E9=85=8D?= =?UTF-8?q?=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 前端渠道表单去掉"供应商"下拉, 只保留"支持的 API 格式"多选(默认 chat) - 后端 provider 改为可选字段: 为空时按 formats 推断(仅 messages→anthropic, 含 responses→openai, 否则 compatible), 兼容旧数据 - 保存时校验至少选择一种格式 Co-Authored-By: Claude --- server/internal/api/admin_channels.go | 30 +++++++++++++++++++-- web/src/views/admin/ChannelsView.vue | 38 ++++++--------------------- 2 files changed, 36 insertions(+), 32 deletions(-) diff --git a/server/internal/api/admin_channels.go b/server/internal/api/admin_channels.go index ea192d4..83436bd 100644 --- a/server/internal/api/admin_channels.go +++ b/server/internal/api/admin_channels.go @@ -45,8 +45,8 @@ func (h *Handler) AdminChannels(c *gin.Context) { type channelBody struct { Name string `json:"name" binding:"required,min=1,max=64"` - Provider string `json:"provider" binding:"required"` - Formats []string `json:"formats"` // 原生支持的协议 chat|responses|messages,空则按 provider 推断 + Provider string `json:"provider"` // 可选:为空时按 formats 推断(兼容旧数据) + Formats []string `json:"formats"` // 原生支持的协议 chat|responses|messages(主配置) BaseURL string `json:"base_url" binding:"required"` APIKey string `json:"api_key"` Weight *int `json:"weight"` @@ -64,6 +64,29 @@ var validFormats = map[string]bool{ store.FormatChat: true, store.FormatResponses: true, store.FormatMessages: true, } +// deriveProvider 按格式推断供应商(仅作内部字段/兼容用途,不参与路由)。 +func deriveProvider(formats []string) string { + if len(formats) == 0 { + return store.ChannelProviderCompatible + } + messagesOnly, hasResponses := true, false + for _, f := range formats { + if f != store.FormatMessages { + messagesOnly = false + } + if f == store.FormatResponses { + hasResponses = true + } + } + if messagesOnly { + return store.ChannelProviderAnthropic + } + if hasResponses { + return store.ChannelProviderOpenAI + } + return store.ChannelProviderCompatible +} + // resolveFormats 渠道协议格式:显式给出则校验去重;空则按 provider 推断默认。 func resolveFormats(provider string, formats []string) ([]string, error) { if len(formats) == 0 { @@ -97,6 +120,9 @@ func (h *Handler) AdminCreateChannel(c *gin.Context) { resp.Fail(c, http.StatusBadRequest, "invalid input: "+err.Error()) return } + if req.Provider == "" { + req.Provider = deriveProvider(req.Formats) + } if !validateProvider(req.Provider) { resp.Fail(c, http.StatusBadRequest, "provider must be openai, anthropic or compatible") return diff --git a/web/src/views/admin/ChannelsView.vue b/web/src/views/admin/ChannelsView.vue index ad71e08..40074d2 100644 --- a/web/src/views/admin/ChannelsView.vue +++ b/web/src/views/admin/ChannelsView.vue @@ -18,8 +18,7 @@ const busyId = ref(null) const form = reactive({ name: '', - provider: 'openai' as 'openai' | 'anthropic' | 'compatible', - formats: [] as string[], + formats: ['chat'] as string[], base_url: '', api_key: '', weight: 1, @@ -29,17 +28,6 @@ const form = reactive({ enabled: true, }) -// 按供应商推断默认支持的协议格式 -function defaultFormats(p: string): string[] { - if (p === 'anthropic') return ['messages'] - if (p === 'openai') return ['chat', 'responses'] - return ['chat'] -} - -function onProviderChange() { - form.formats = defaultFormats(form.provider) -} - async function load() { try { const { data } = await http.get('/admin/channels') @@ -52,7 +40,7 @@ async function load() { function openCreate() { editing.value = null Object.assign(form, { - name: '', provider: 'openai', formats: defaultFormats('openai'), base_url: '', api_key: '', + name: '', formats: ['chat'], base_url: '', api_key: '', weight: 1, priority: 0, timeout_ms: 120000, max_concurrency: 16, enabled: true, }) editOpen.value = true @@ -61,7 +49,7 @@ function openCreate() { function openEdit(ch: Channel) { editing.value = ch Object.assign(form, { - name: ch.name, provider: ch.provider, formats: [...(ch.formats || defaultFormats(ch.provider))], + name: ch.name, formats: [...(ch.formats?.length ? ch.formats : ['chat'])], base_url: ch.base_url, api_key: '', weight: ch.weight, priority: ch.priority, timeout_ms: ch.timeout_ms, max_concurrency: ch.max_concurrency, enabled: ch.enabled, @@ -70,6 +58,10 @@ function openEdit(ch: Channel) { } async function save() { + if (form.formats.length === 0) { + toast.err('请至少选择一种 API 格式') + return + } saving.value = true const payload = { ...form, @@ -199,21 +191,7 @@ onMounted(load)
-
- - -
+
支持的 API 格式