mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-11-01 16:29:29 +00:00
feat(frontend): show Grok monitoring and Free plan badge
This commit is contained in:
@@ -109,6 +109,8 @@ import { useI18n } from 'vue-i18n'
|
||||
import type { APIMode, BodyOverrideMode, Provider } from '@/api/admin/channelMonitor'
|
||||
import {
|
||||
API_MODE_RESPONSES,
|
||||
DEFAULT_GROK_MODEL,
|
||||
PROVIDER_GROK,
|
||||
PROVIDER_OPENAI,
|
||||
} from '@/constants/channelMonitor'
|
||||
|
||||
@@ -305,11 +307,12 @@ const bodyPlaceholder = computed(() => {
|
||||
}
|
||||
return '{\n "model": "gpt-4o-mini",\n "instructions": "You are a health check endpoint. Reply briefly.",\n "input": "Reply with exactly: ok",\n "max_output_tokens": 20,\n "stream": false\n}'
|
||||
}
|
||||
if (props.provider === PROVIDER_OPENAI) {
|
||||
if (props.provider === PROVIDER_OPENAI || props.provider === PROVIDER_GROK) {
|
||||
if (props.bodyOverrideMode === 'merge') {
|
||||
return '{\n "max_tokens": 20\n}'
|
||||
}
|
||||
return '{\n "model": "gpt-4o-mini",\n "messages": [{"role":"user","content":"Reply with exactly: ok"}],\n "max_tokens": 20,\n "stream": false\n}'
|
||||
const model = props.provider === PROVIDER_GROK ? DEFAULT_GROK_MODEL : 'gpt-4o-mini'
|
||||
return `{\n "model": "${model}",\n "messages": [{"role":"user","content":"Reply with exactly: ok"}],\n "max_tokens": 20,\n "stream": false\n}`
|
||||
}
|
||||
if (props.bodyOverrideMode === 'merge') {
|
||||
return '{\n "system": "You are Claude Code..."\n}'
|
||||
|
||||
@@ -70,6 +70,7 @@ import {
|
||||
PROVIDER_OPENAI,
|
||||
PROVIDER_ANTHROPIC,
|
||||
PROVIDER_GEMINI,
|
||||
PROVIDER_GROK,
|
||||
} from '@/constants/channelMonitor'
|
||||
|
||||
defineProps<{
|
||||
@@ -94,6 +95,7 @@ const providerFilterOptions = computed(() => [
|
||||
{ value: PROVIDER_OPENAI, label: t('monitorCommon.providers.openai') },
|
||||
{ value: PROVIDER_ANTHROPIC, label: t('monitorCommon.providers.anthropic') },
|
||||
{ value: PROVIDER_GEMINI, label: t('monitorCommon.providers.gemini') },
|
||||
{ value: PROVIDER_GROK, label: t('monitorCommon.providers.grok') },
|
||||
])
|
||||
|
||||
const enabledFilterOptions = computed(() => [
|
||||
|
||||
@@ -13,15 +13,16 @@
|
||||
|
||||
<div>
|
||||
<label class="input-label">{{ t('admin.channelMonitor.form.provider') }} <span class="text-red-500">*</span></label>
|
||||
<div class="grid grid-cols-3 gap-3">
|
||||
<div class="grid grid-cols-2 gap-3 sm:grid-cols-4">
|
||||
<button
|
||||
v-for="opt in providerOptions"
|
||||
:key="opt.value"
|
||||
type="button"
|
||||
:data-testid="`monitor-provider-${opt.value}`"
|
||||
:aria-pressed="form.provider === opt.value"
|
||||
class="flex items-center justify-center gap-2 rounded-lg border-2 px-3 py-2.5 text-sm font-medium transition-colors"
|
||||
:class="providerPickerClass(opt.value, form.provider === opt.value)"
|
||||
@click="form.provider = opt.value"
|
||||
@click="selectProvider(opt.value)"
|
||||
>
|
||||
<ProviderIcon :provider="opt.value" :size="18" />
|
||||
<span>{{ opt.label }}</span>
|
||||
@@ -50,7 +51,7 @@
|
||||
<div>
|
||||
<label class="input-label">{{ t('admin.channelMonitor.form.endpoint') }} <span class="text-red-500">*</span></label>
|
||||
<div class="flex gap-2">
|
||||
<input v-model="form.endpoint" type="text" required class="input flex-1" :placeholder="t('admin.channelMonitor.form.endpointPlaceholder')" />
|
||||
<input v-model="form.endpoint" data-testid="monitor-endpoint" type="text" required class="input flex-1" :placeholder="t('admin.channelMonitor.form.endpointPlaceholder')" />
|
||||
<button type="button" @click="useCurrentDomain" class="btn btn-secondary whitespace-nowrap">
|
||||
{{ t('admin.channelMonitor.form.useCurrentDomain') }}
|
||||
</button>
|
||||
@@ -80,6 +81,7 @@
|
||||
<label class="input-label">{{ t('admin.channelMonitor.form.primaryModel') }} <span class="text-red-500">*</span></label>
|
||||
<input
|
||||
v-model="form.primary_model"
|
||||
data-testid="monitor-primary-model"
|
||||
type="text"
|
||||
required
|
||||
class="input font-medium"
|
||||
@@ -213,8 +215,11 @@ import {
|
||||
PROVIDER_OPENAI,
|
||||
PROVIDER_ANTHROPIC,
|
||||
PROVIDER_GEMINI,
|
||||
PROVIDER_GROK,
|
||||
API_MODE_CHAT_COMPLETIONS,
|
||||
API_MODE_RESPONSES,
|
||||
DEFAULT_GROK_ENDPOINT,
|
||||
DEFAULT_GROK_MODEL,
|
||||
DEFAULT_INTERVAL_SECONDS,
|
||||
} from '@/constants/channelMonitor'
|
||||
|
||||
@@ -396,8 +401,26 @@ const providerOptions = computed<ProviderOption[]>(() => [
|
||||
{ value: PROVIDER_ANTHROPIC, label: t('monitorCommon.providers.anthropic') },
|
||||
{ value: PROVIDER_OPENAI, label: t('monitorCommon.providers.openai') },
|
||||
{ value: PROVIDER_GEMINI, label: t('monitorCommon.providers.gemini') },
|
||||
{ value: PROVIDER_GROK, label: t('monitorCommon.providers.grok') },
|
||||
])
|
||||
|
||||
function selectProvider(provider: Provider) {
|
||||
if (form.provider === provider) return
|
||||
const previousProvider = form.provider
|
||||
const clearGrokEndpoint =
|
||||
previousProvider === PROVIDER_GROK && form.endpoint === DEFAULT_GROK_ENDPOINT
|
||||
const clearGrokModel =
|
||||
previousProvider === PROVIDER_GROK && form.primary_model === DEFAULT_GROK_MODEL
|
||||
form.provider = provider
|
||||
if (provider === PROVIDER_GROK) {
|
||||
if (!form.endpoint.trim()) form.endpoint = DEFAULT_GROK_ENDPOINT
|
||||
if (!form.primary_model.trim()) form.primary_model = DEFAULT_GROK_MODEL
|
||||
return
|
||||
}
|
||||
if (clearGrokEndpoint) form.endpoint = ''
|
||||
if (clearGrokModel) form.primary_model = ''
|
||||
}
|
||||
|
||||
// Clear api_key whenever provider changes to avoid cross-provider key mismatch.
|
||||
// Editing mode loads api_key='' via loadFromMonitor and only sets it on user
|
||||
// typing, so clearing on provider change is always a safe no-op until the user
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
>
|
||||
<!-- provider tabs -->
|
||||
<div class="mb-4 border-b border-gray-200 dark:border-dark-700">
|
||||
<div role="tablist" class="flex gap-1">
|
||||
<div role="tablist" class="flex flex-wrap gap-1">
|
||||
<button
|
||||
v-for="tab in providerTabs"
|
||||
:key="tab.value"
|
||||
@@ -130,7 +130,7 @@
|
||||
{{ t('admin.channelMonitor.form.provider') }}
|
||||
<span class="text-red-500">*</span>
|
||||
</label>
|
||||
<div class="grid grid-cols-3 gap-3">
|
||||
<div class="grid grid-cols-2 gap-3 sm:grid-cols-4">
|
||||
<button
|
||||
v-for="opt in providerTabs"
|
||||
:key="opt.value"
|
||||
@@ -248,6 +248,7 @@ import {
|
||||
PROVIDER_ANTHROPIC,
|
||||
PROVIDER_OPENAI,
|
||||
PROVIDER_GEMINI,
|
||||
PROVIDER_GROK,
|
||||
API_MODE_CHAT_COMPLETIONS,
|
||||
API_MODE_RESPONSES,
|
||||
} from '@/constants/channelMonitor'
|
||||
@@ -267,6 +268,7 @@ const providerTabs = computed<{ value: Provider; label: string }[]>(() => [
|
||||
{ value: PROVIDER_ANTHROPIC, label: t('monitorCommon.providers.anthropic') },
|
||||
{ value: PROVIDER_OPENAI, label: t('monitorCommon.providers.openai') },
|
||||
{ value: PROVIDER_GEMINI, label: t('monitorCommon.providers.gemini') },
|
||||
{ value: PROVIDER_GROK, label: t('monitorCommon.providers.grok') },
|
||||
])
|
||||
|
||||
const activeProvider = ref<Provider>(PROVIDER_ANTHROPIC)
|
||||
@@ -282,6 +284,7 @@ const countByProvider = computed<Record<Provider, number>>(() => {
|
||||
anthropic: 0,
|
||||
openai: 0,
|
||||
gemini: 0,
|
||||
grok: 0,
|
||||
}
|
||||
for (const t of templates.value) out[t.provider]++
|
||||
return out
|
||||
|
||||
Reference in New Issue
Block a user