feat(channel-monitor): 增加模板协议管理界面

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
benjamin 2026-05-19 22:04:56 +08:00
parent 89d4b0db54
commit e2831b3291

View File

@ -65,6 +65,13 @@
>
{{ modeLabel(tpl.body_override_mode) }}
</span>
<span
v-if="tpl.provider === PROVIDER_OPENAI"
class="inline-flex items-center rounded-md px-1.5 py-0.5 text-xs"
:class="apiModeBadgeClass(tpl.api_mode)"
>
{{ apiModeLabel(tpl.api_mode) }}
</span>
<span
v-if="tpl.associated_monitors > 0"
class="text-xs text-gray-500 dark:text-gray-400"
@ -137,6 +144,23 @@
</div>
</div>
<div v-if="form.provider === PROVIDER_OPENAI" class="rounded-lg border border-blue-100 bg-blue-50/50 p-3 dark:border-blue-500/20 dark:bg-blue-500/10">
<label class="input-label">{{ t('admin.channelMonitor.form.apiMode') }}</label>
<div class="grid gap-3 sm:grid-cols-2">
<button
v-for="opt in apiModeOptions"
:key="opt.value"
type="button"
class="rounded-lg border-2 px-3 py-2 text-left transition-colors"
:class="apiModeButtonClass(opt.value)"
@click="form.api_mode = opt.value"
>
<span class="block text-sm font-semibold">{{ opt.label }}</span>
<span class="mt-0.5 block text-xs opacity-80">{{ opt.hint }}</span>
</button>
</div>
</div>
<div>
<label class="input-label">
{{ t('admin.channelMonitor.template.form.description') }}
@ -150,6 +174,8 @@
</div>
<MonitorAdvancedRequestConfig
:provider="form.provider"
:api-mode="form.api_mode"
:extra-headers="form.extra_headers"
:body-override-mode="form.body_override_mode"
:body-override="form.body_override"
@ -207,6 +233,7 @@ import { useAppStore } from '@/stores/app'
import { extractApiErrorMessage } from '@/utils/apiError'
import { adminAPI } from '@/api/admin'
import type {
APIMode,
BodyOverrideMode,
Provider,
} from '@/api/admin/channelMonitor'
@ -221,6 +248,8 @@ import {
PROVIDER_ANTHROPIC,
PROVIDER_OPENAI,
PROVIDER_GEMINI,
API_MODE_CHAT_COMPLETIONS,
API_MODE_RESPONSES,
} from '@/constants/channelMonitor'
const props = defineProps<{ show: boolean }>()
@ -263,6 +292,7 @@ interface TemplateForm {
id: number | null
name: string
provider: Provider
api_mode: APIMode
description: string
extra_headers: Record<string, string>
body_override_mode: BodyOverrideMode
@ -278,6 +308,7 @@ function emptyForm(provider: Provider): TemplateForm {
id: null,
name: '',
provider,
api_mode: API_MODE_CHAT_COMPLETIONS,
description: '',
extra_headers: {},
body_override_mode: 'off',
@ -289,6 +320,7 @@ function loadForm(tpl: ChannelMonitorTemplate) {
form.id = tpl.id
form.name = tpl.name
form.provider = tpl.provider
form.api_mode = normalizeAPIMode(tpl.api_mode)
form.description = tpl.description
form.extra_headers = { ...(tpl.extra_headers || {}) }
form.body_override_mode = tpl.body_override_mode
@ -346,6 +378,7 @@ async function handleSubmit() {
await adminAPI.channelMonitorTemplate.create({
name: form.name.trim(),
provider: form.provider,
api_mode: form.provider === PROVIDER_OPENAI ? form.api_mode : API_MODE_CHAT_COMPLETIONS,
description: form.description.trim(),
extra_headers: form.extra_headers,
body_override_mode: form.body_override_mode,
@ -355,6 +388,7 @@ async function handleSubmit() {
} else if (typeof editing.value === 'number') {
await adminAPI.channelMonitorTemplate.update(editing.value, {
name: form.name.trim(),
api_mode: form.provider === PROVIDER_OPENAI ? form.api_mode : API_MODE_CHAT_COMPLETIONS,
description: form.description.trim(),
extra_headers: form.extra_headers,
body_override_mode: form.body_override_mode,
@ -444,4 +478,48 @@ function modeBadgeClass(mode: BodyOverrideMode): string {
function modeLabel(mode: BodyOverrideMode): string {
return t(`admin.channelMonitor.advanced.bodyMode${mode.charAt(0).toUpperCase()}${mode.slice(1)}`)
}
const apiModeOptions = computed<{ value: APIMode; label: string; hint: string }[]>(() => [
{
value: API_MODE_CHAT_COMPLETIONS,
label: t('admin.channelMonitor.form.apiModeChatCompletions'),
hint: t('admin.channelMonitor.form.apiModeChatCompletionsHint'),
},
{
value: API_MODE_RESPONSES,
label: t('admin.channelMonitor.form.apiModeResponses'),
hint: t('admin.channelMonitor.form.apiModeResponsesHint'),
},
])
watch(() => form.provider, (provider) => {
if (provider !== PROVIDER_OPENAI) {
form.api_mode = API_MODE_CHAT_COMPLETIONS
}
})
function normalizeAPIMode(mode: APIMode | undefined | null): APIMode {
return mode === API_MODE_RESPONSES ? API_MODE_RESPONSES : API_MODE_CHAT_COMPLETIONS
}
function apiModeButtonClass(mode: APIMode): string {
const active = form.api_mode === mode
if (active) {
return 'border-primary-500 bg-white text-primary-700 shadow-sm dark:border-primary-400 dark:bg-primary-500/15 dark:text-primary-300'
}
return 'border-blue-100 bg-white/70 text-gray-600 hover:border-primary-300 dark:border-dark-700 dark:bg-dark-800 dark:text-gray-400'
}
function apiModeLabel(mode: APIMode): string {
return normalizeAPIMode(mode) === API_MODE_RESPONSES
? t('admin.channelMonitor.form.apiModeResponses')
: t('admin.channelMonitor.form.apiModeChatCompletions')
}
function apiModeBadgeClass(mode: APIMode): string {
if (normalizeAPIMode(mode) === API_MODE_RESPONSES) {
return 'bg-blue-100 text-blue-700 dark:bg-blue-500/15 dark:text-blue-300'
}
return 'bg-emerald-100 text-emerald-700 dark:bg-emerald-500/15 dark:text-emerald-300'
}
</script>