diff --git a/src/admin/pages/MindSearchPage.tsx b/src/admin/pages/MindSearchPage.tsx index 4b3df2c..8fe356a 100644 --- a/src/admin/pages/MindSearchPage.tsx +++ b/src/admin/pages/MindSearchPage.tsx @@ -1,12 +1,23 @@ import { useEffect, useState } from 'react'; -import { getMindSearchConfig, testMindSearchService, updateMindSearchConfig } from '../../api/client'; -import type { MindSearchConfig, MindSearchRoutes, MindSearchService } from '../../types'; +import { + getMindSearchConfig, + listLlmProviderKeys, + testLlmProviderKey, + testMindSearchService, + updateMindSearchConfig, +} from '../../api/client'; +import type { + LlmProviderKeyRow, + MindSearchConfig, + MindSearchRoutes, + MindSearchService, +} from '../../types'; const builtInServices: MindSearchService[] = [ { id: 'searxng', name: 'SearXNG', kind: 'provider', adapter: 'searxng', capabilities: ['search.web', 'search.news'], endpoint: '', healthPath: '/config', enabled: false, timeoutMs: 8000, priority: 100 }, { id: 'github', name: 'GitHub Code', kind: 'provider', adapter: 'github', capabilities: ['search.code'], endpoint: 'https://api.github.com/search/code', healthPath: '', enabled: false, timeoutMs: 8000, priority: 90 }, { id: 'reader', name: 'Safe Reader', kind: 'reader', adapter: 'reader', capabilities: ['search.read'], endpoint: '', healthPath: '', enabled: false, timeoutMs: 8000, priority: 80 }, - { id: 'deep-search', name: 'TKMind Deep Search', kind: 'orchestrator', adapter: 'research-http', capabilities: ['search.web', 'research.plan', 'research.execute'], endpoint: 'http://127.0.0.1:20100', healthPath: '/health', enabled: false, timeoutMs: 120000, priority: 100 }, + { id: 'deep-search', name: 'TKMind Deep Search', kind: 'orchestrator', adapter: 'research-http', capabilities: ['search.web', 'research.plan', 'research.execute'], endpoint: 'http://127.0.0.1:20100', healthPath: '/health', enabled: false, timeoutMs: 120000, priority: 100, llmProviderKeyId: '', llmModel: '' }, ]; const initial: MindSearchConfig = { @@ -38,13 +49,18 @@ function mergeConfig(config: MindSearchConfig): MindSearchConfig { export function MindSearchPage() { const [config, setConfig] = useState(initial); + const [llmKeys, setLlmKeys] = useState([]); const [meta, setMeta] = useState<{ source?: string; updatedAt?: number | null; updatedBy?: string | null }>({}); const [message, setMessage] = useState('加载配置中…'); useEffect(() => { - getMindSearchConfig() - .then((result) => { + Promise.all([ + getMindSearchConfig(), + listLlmProviderKeys().catch(() => [] as LlmProviderKeyRow[]), + ]) + .then(([result, keys]) => { setConfig(mergeConfig(result.config)); + setLlmKeys(keys.filter((key) => key.status === 'active')); setMeta(result); setMessage(''); }) @@ -88,6 +104,8 @@ export function MindSearchPage() { enabled: false, timeoutMs: 30000, priority: 100, + llmProviderKeyId: '', + llmModel: '', }], })); }; @@ -108,6 +126,21 @@ export function MindSearchPage() { setMessage(error instanceof Error ? error.message : '服务测试失败'); } }; + const runLlmTest = async (service: MindSearchService) => { + if (!service.llmProviderKeyId || !service.llmModel) { + setMessage('请先选择 Deep Search 的 LLM 配置和模型'); + return; + } + setMessage(`测试 ${service.llmModel} 中…`); + try { + const result = await testLlmProviderKey(service.llmProviderKeyId, service.llmModel); + setMessage(result.ok + ? `LLM 通过:${result.model ?? service.llmModel}${result.latencyMs == null ? '' : `;${result.latencyMs}ms`}` + : `LLM 失败:${result.message ?? '模型连接失败'}`); + } catch (error) { + setMessage(error instanceof Error ? error.message : 'LLM 测试失败'); + } + }; return (
@@ -145,7 +178,9 @@ export function MindSearchPage() {

服务注册表

服务独立部署;后台只管理连接、路由和健康检查,不直接操作 Docker 或生产进程。测试使用已保存配置。

- {config.services.map((service) => ( + {config.services.map((service) => { + const selectedLlmKey = llmKeys.find((key) => key.id === service.llmProviderKeyId); + return (
{service.name}({service.id}) @@ -173,13 +208,52 @@ export function MindSearchPage() { + {service.adapter === 'research-http' && <> + + +

密钥复用“统一模型中心”,不会保存到 MindSearch 配置或下发给 Deep Search 服务。

+ }
- {!['searxng', 'github', 'reader'].includes(service.id) && } + {service.adapter === 'research-http' && service.llmProviderKeyId && service.llmModel + && } + {!['searxng', 'github', 'reader', 'deep-search'].includes(service.id) && }
- ))} + ); + })} diff --git a/src/index.css b/src/index.css index f193005..fc2614d 100644 --- a/src/index.css +++ b/src/index.css @@ -615,6 +615,13 @@ body, margin-top: 12px; } +.search-llm-hint { + margin: -2px 0 0; + color: var(--color-text-muted); + font-size: 12px; + line-height: 1.55; +} + .form-span-all { grid-column: 1 / -1; } diff --git a/src/types.ts b/src/types.ts index 0da7d6b..5faacbf 100644 --- a/src/types.ts +++ b/src/types.ts @@ -640,6 +640,8 @@ export type MindSearchService = { enabled: boolean; timeoutMs: number; priority: number; + llmProviderKeyId?: string; + llmModel?: string; }; export type MindSearchRoutes = {