feat(aiops): 模型配置参考 aiops/opsxshell — 快捷预设 + 供应商下拉
- 快捷预设:deepseek-flash(推荐)/ deepseek-v4-pro / 自定义 一键填入端点与模型 - 供应商目录(9 家 + 自定义):DeepSeek/通义/智谱/Kimi/MiniMax/硅基流动/混元/OpenAI/本地 Ollama·vLLM,选择后自动填 Base URL 与首个推荐模型 - 模型输入支持 datalist 下拉(随供应商联动)且可手输任意模型名;Base URL 保持可编辑
This commit is contained in:
1 parent
63445bd598
commit
1610988c2e
4 files changed
+157
-9
No files matched your search
@@ -54,6 +54,10 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
config: {
|
config: {
|
||||||
|
quickPresets: 'Quick presets',
|
||||||
|
presetCustom: 'Custom',
|
||||||
|
presetHint: 'DeepSeek official picks; custom accepts any OpenAI-compatible endpoint',
|
||||||
|
providerSelectHint: 'Pick a provider to autofill endpoint and model',
|
||||||
enabled: 'Enable AI analysis',
|
enabled: 'Enable AI analysis',
|
||||||
enabledHint: 'When off, Analyze returns a clear "not enabled" message',
|
enabledHint: 'When off, Analyze returns a clear "not enabled" message',
|
||||||
provider: 'Protocol',
|
provider: 'Protocol',
|
||||||
|
|||||||
@@ -54,6 +54,10 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
config: {
|
config: {
|
||||||
|
quickPresets: '快捷预设',
|
||||||
|
presetCustom: '自定义',
|
||||||
|
presetHint: 'DeepSeek 官方推荐;自定义可手填任意 OpenAI 兼容端点',
|
||||||
|
providerSelectHint: '选择供应商自动填入端点与推荐模型',
|
||||||
enabled: '启用 AI 分析',
|
enabled: '启用 AI 分析',
|
||||||
enabledHint: '关闭后「开始分析」会直接提示未启用,不影响其它功能',
|
enabledHint: '关闭后「开始分析」会直接提示未启用,不影响其它功能',
|
||||||
provider: '协议',
|
provider: '协议',
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { useCallback, useEffect, useMemo, useState } from 'react'
|
import { useCallback, useEffect, useMemo, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { LLM_PROVIDERS, matchProvider } from './llmProviders'
|
||||||
import { Loader2, Play, Save, Sparkles, PlugZap, History, ShieldCheck } from 'lucide-react'
|
import { Loader2, Play, Save, Sparkles, PlugZap, History, ShieldCheck } from 'lucide-react'
|
||||||
import { toast } from 'sonner'
|
import { toast } from 'sonner'
|
||||||
import { PageContainer } from '@/components/PageContainer'
|
import { PageContainer } from '@/components/PageContainer'
|
||||||
@@ -8,6 +9,7 @@ import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
|
|||||||
import { Input } from '@/components/ui/input'
|
import { Input } from '@/components/ui/input'
|
||||||
import { Label } from '@/components/ui/label'
|
import { Label } from '@/components/ui/label'
|
||||||
import { Switch } from '@/components/ui/switch'
|
import { Switch } from '@/components/ui/switch'
|
||||||
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
|
||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
||||||
import { Textarea } from '@/components/ui/textarea'
|
import { Textarea } from '@/components/ui/textarea'
|
||||||
import { Badge } from '@/components/ui/badge'
|
import { Badge } from '@/components/ui/badge'
|
||||||
@@ -479,8 +481,70 @@ export default function AiopsPage() {
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-wrap items-end gap-2">
|
||||||
|
<div className="space-y-1">
|
||||||
|
<Label className="text-xs">{t('config.quickPresets')}</Label>
|
||||||
|
<div className="flex gap-2">
|
||||||
|
{LLM_PROVIDERS.filter(p => p.id === 'deepseek')[0].models.slice(0, 2).reverse().concat([]).map(m => (
|
||||||
|
<Button
|
||||||
|
key={m}
|
||||||
|
type="button"
|
||||||
|
size="sm"
|
||||||
|
variant={config.model === m ? 'default' : 'outline'}
|
||||||
|
onClick={() => setConfig({ ...config, baseUrl: LLM_PROVIDERS.find(p => p.id === 'deepseek')!.endpoint, model: m })}
|
||||||
|
>
|
||||||
|
{m}
|
||||||
|
</Button>
|
||||||
|
))}
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
size="sm"
|
||||||
|
variant={matchProvider(config.baseUrl) === 'custom' ? 'default' : 'outline'}
|
||||||
|
onClick={() => setConfig({ ...config, baseUrl: '', model: '' })}
|
||||||
|
>
|
||||||
|
{t('config.presetCustom')}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<p className="text-[11px] text-muted-foreground">{t('config.presetHint')}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="grid gap-4 sm:grid-cols-2">
|
<div className="grid gap-4 sm:grid-cols-2">
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
|
<Label className="text-xs">{t('config.provider')}</Label>
|
||||||
|
<Select
|
||||||
|
value={matchProvider(config.baseUrl)}
|
||||||
|
onValueChange={value => {
|
||||||
|
const p = LLM_PROVIDERS.find(x => x.id === value)
|
||||||
|
if (!p) return
|
||||||
|
setConfig({ ...config, baseUrl: p.endpoint, model: p.models[0] && value !== 'custom' ? p.models[0] : config.model })
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<SelectTrigger><SelectValue /></SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
{LLM_PROVIDERS.map(p => (
|
||||||
|
<SelectItem key={p.id} value={p.id}>{p.name}</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
<p className="text-[11px] text-muted-foreground">{t('config.providerSelectHint')}</p>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-1">
|
||||||
|
<Label className="text-xs">{t('config.model')}</Label>
|
||||||
|
<Input
|
||||||
|
value={config.model}
|
||||||
|
placeholder="deepseek-flash"
|
||||||
|
list="llm-model-options"
|
||||||
|
onChange={e => setConfig({ ...config, model: e.target.value })}
|
||||||
|
/>
|
||||||
|
<datalist id="llm-model-options">
|
||||||
|
{(LLM_PROVIDERS.find(p => p.id === matchProvider(config.baseUrl))?.models ?? []).map(m => (
|
||||||
|
<option key={m} value={m} />
|
||||||
|
))}
|
||||||
|
</datalist>
|
||||||
|
<p className="text-[11px] text-muted-foreground">{t('config.modelHint')}</p>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-1 sm:col-span-2">
|
||||||
<Label className="text-xs">{t('config.baseUrl')}</Label>
|
<Label className="text-xs">{t('config.baseUrl')}</Label>
|
||||||
<Input
|
<Input
|
||||||
value={config.baseUrl}
|
value={config.baseUrl}
|
||||||
@@ -489,15 +553,6 @@ export default function AiopsPage() {
|
|||||||
/>
|
/>
|
||||||
<p className="text-[11px] text-muted-foreground">{t('config.baseUrlHint')}</p>
|
<p className="text-[11px] text-muted-foreground">{t('config.baseUrlHint')}</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-1">
|
|
||||||
<Label className="text-xs">{t('config.model')}</Label>
|
|
||||||
<Input
|
|
||||||
value={config.model}
|
|
||||||
placeholder="deepseek-chat"
|
|
||||||
onChange={e => setConfig({ ...config, model: e.target.value })}
|
|
||||||
/>
|
|
||||||
<p className="text-[11px] text-muted-foreground">{t('config.modelHint')}</p>
|
|
||||||
</div>
|
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<Label className="text-xs">{t('config.apiKey')}</Label>
|
<Label className="text-xs">{t('config.apiKey')}</Label>
|
||||||
<Input
|
<Input
|
||||||
|
|||||||
@@ -0,0 +1,85 @@
|
|||||||
|
// LLM 供应商目录(参考 aiops llmProviders / opsxshell providerPresets,按 LokiStack 精简):
|
||||||
|
// 全部为 OpenAI 兼容协议端点;models 为该供应商常用模型,均允许手输目录之外的模型名。
|
||||||
|
export interface LLMProvider {
|
||||||
|
id: string
|
||||||
|
name: string
|
||||||
|
endpoint: string
|
||||||
|
models: string[]
|
||||||
|
keyHint?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export const LLM_PROVIDERS: LLMProvider[] = [
|
||||||
|
{
|
||||||
|
id: 'deepseek',
|
||||||
|
name: 'DeepSeek',
|
||||||
|
endpoint: 'https://api.deepseek.com/v1',
|
||||||
|
models: ['deepseek-v4-pro', 'deepseek-flash', 'deepseek-reasoner', 'deepseek-chat'],
|
||||||
|
keyHint: 'platform.deepseek.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'qwen',
|
||||||
|
name: '通义千问',
|
||||||
|
endpoint: 'https://dashscope.aliyuncs.com/compatible-mode/v1',
|
||||||
|
models: ['qwen3.8-max', 'qwen3.8-flash', 'qwen3.7-max', 'qwen3.7-plus'],
|
||||||
|
keyHint: 'bailian.console.aliyun.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'glm',
|
||||||
|
name: '智谱 GLM',
|
||||||
|
endpoint: 'https://open.bigmodel.cn/api/paas/v4',
|
||||||
|
models: ['glm-5.2', 'glm-5.1', 'glm-5'],
|
||||||
|
keyHint: 'open.bigmodel.cn',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'moonshot',
|
||||||
|
name: '月之暗面 Kimi',
|
||||||
|
endpoint: 'https://api.moonshot.cn/v1',
|
||||||
|
models: ['kimi-k3', 'kimi-k2.7-code', 'kimi-k2.6'],
|
||||||
|
keyHint: 'platform.moonshot.cn',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'minimax',
|
||||||
|
name: 'MiniMax',
|
||||||
|
endpoint: 'https://api.minimaxi.com/v1',
|
||||||
|
models: ['MiniMax-M3', 'MiniMax-M2.7'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'siliconflow',
|
||||||
|
name: '硅基流动',
|
||||||
|
endpoint: 'https://api.siliconflow.cn/v1',
|
||||||
|
models: ['deepseek-ai/DeepSeek-V4-Pro', 'deepseek-ai/DeepSeek-V4-Flash', 'Qwen/Qwen3.5-397B-A17B'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'hunyuan',
|
||||||
|
name: '腾讯混元',
|
||||||
|
endpoint: 'https://tokenhub.tencentmaas.com/v1',
|
||||||
|
models: ['hy3', 'hy3-preview'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'openai',
|
||||||
|
name: 'OpenAI',
|
||||||
|
endpoint: 'https://api.openai.com/v1',
|
||||||
|
models: ['gpt-5.5', 'gpt-5.4', 'gpt-4.1'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'ollama',
|
||||||
|
name: '本地 Ollama / vLLM',
|
||||||
|
endpoint: 'http://127.0.0.1:11434/v1',
|
||||||
|
models: ['qwen3:8b', 'deepseek-r1:14b'],
|
||||||
|
keyHint: '本地服务无需 Key 可留空',
|
||||||
|
},
|
||||||
|
{ id: 'custom', name: '自定义', endpoint: '', models: [] },
|
||||||
|
]
|
||||||
|
|
||||||
|
/** 按 baseUrl 反推当前选中的供应商(未匹配归 custom) */
|
||||||
|
export function matchProvider(baseUrl: string): string {
|
||||||
|
const hit = LLM_PROVIDERS.find(p => p.endpoint && p.endpoint === baseUrl)
|
||||||
|
return hit?.id ?? 'custom'
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 推荐预设:一键填入 endpoint + 模型 */
|
||||||
|
export const QUICK_PRESETS = [
|
||||||
|
{ id: 'flash', label: 'DeepSeek Flash(推荐)', provider: 'deepseek', model: 'deepseek-flash' },
|
||||||
|
{ id: 'pro', label: 'DeepSeek V4 Pro', provider: 'deepseek', model: 'deepseek-v4-pro' },
|
||||||
|
{ id: 'custom', label: '自定义', provider: 'custom', model: '' },
|
||||||
|
]
|
||||||
Reference in new issue
Block a user