fix(story-summary): unify vector api test actions

This commit is contained in:
2026-04-04 01:06:18 +08:00
parent 485016abdd
commit 37b2f15d05
5 changed files with 105 additions and 30 deletions

View File

@@ -916,16 +916,6 @@ All checks passed. Beginning incremental extraction...
$('vector-config-area').classList.toggle('hidden', !e.target.checked); $('vector-config-area').classList.toggle('hidden', !e.target.checked);
}; };
$('btn-test-vector-api').onclick = () => {
$('btn-test-vector-api').disabled = true;
setStatusText($('embedding-api-connect-status'), '测试中...', 'loading');
saveConfig(); // 先保存新 Key 到 localStorage
postMsg('VECTOR_TEST_ONLINE', {
provider: getVectorConfig().embeddingApi.provider,
config: getVectorConfig().embeddingApi
});
};
['l0', 'embedding', 'rerank'].forEach(prefix => { ['l0', 'embedding', 'rerank'].forEach(prefix => {
$(`${prefix}-api-provider`).onchange = e => { $(`${prefix}-api-provider`).onchange = e => {
saveCurrentVectorApiProfile(prefix); saveCurrentVectorApiProfile(prefix);
@@ -941,6 +931,18 @@ All checks passed. Beginning incremental extraction...
}; };
$(`${prefix}-btn-connect`).onclick = () => fetchVectorModels(prefix); $(`${prefix}-btn-connect`).onclick = () => fetchVectorModels(prefix);
$(`${prefix}-btn-test`).onclick = () => {
const btn = $(`${prefix}-btn-test`);
if (btn) btn.disabled = true;
setStatusText($(`${prefix}-api-connect-status`), '测试中...', 'loading');
saveConfig();
const cfg = getVectorConfig();
postMsg('VECTOR_TEST_ONLINE', {
target: prefix,
provider: cfg[`${prefix}Api`].provider,
config: cfg[`${prefix}Api`],
});
};
}); });
$('btn-add-filter-rule').onclick = addFilterRule; $('btn-add-filter-rule').onclick = addFilterRule;
@@ -987,11 +989,12 @@ All checks passed. Beginning incremental extraction...
postMsg('REQUEST_ANCHOR_STATS'); postMsg('REQUEST_ANCHOR_STATS');
} }
function updateVectorOnlineStatus(status, message) { function updateVectorOnlineStatus(target, status, message) {
const btn = $('btn-test-vector-api'); const prefix = target || 'embedding';
const btn = $(`${prefix}-btn-test`);
if (btn) btn.disabled = false; if (btn) btn.disabled = false;
setStatusText( setStatusText(
$('embedding-api-connect-status'), $(`${prefix}-api-connect-status`),
message || '', message || '',
status === 'error' ? 'error' : status === 'success' ? 'success' : 'loading' status === 'error' ? 'error' : status === 'success' ? 'success' : 'loading'
); );
@@ -2099,7 +2102,7 @@ All checks passed. Beginning incremental extraction...
break; break;
case 'VECTOR_ONLINE_STATUS': case 'VECTOR_ONLINE_STATUS':
updateVectorOnlineStatus(d.status, d.message); updateVectorOnlineStatus(d.target, d.status, d.message);
break; break;
case 'VECTOR_STATS': case 'VECTOR_STATS':

View File

@@ -472,6 +472,7 @@
</div> </div>
<div class="settings-btn-row" id="l0-api-connect-row" style="margin-bottom:8px;"> <div class="settings-btn-row" id="l0-api-connect-row" style="margin-bottom:8px;">
<button class="btn btn-sm" id="l0-btn-connect" style="flex:1">连接 / 拉取模型列表</button> <button class="btn btn-sm" id="l0-btn-connect" style="flex:1">连接 / 拉取模型列表</button>
<button class="btn btn-sm" id="l0-btn-test" style="flex:1">测试</button>
</div> </div>
<div class="settings-hint" id="l0-api-connect-status" style="margin-bottom:16px;"></div> <div class="settings-hint" id="l0-api-connect-status" style="margin-bottom:16px;"></div>
</div> </div>
@@ -520,7 +521,7 @@
</div> </div>
<div class="settings-btn-row" id="embedding-api-connect-row" style="margin-bottom:8px;"> <div class="settings-btn-row" id="embedding-api-connect-row" style="margin-bottom:8px;">
<button class="btn btn-sm" id="embedding-btn-connect" style="flex:1">连接 / 拉取模型列表</button> <button class="btn btn-sm" id="embedding-btn-connect" style="flex:1">连接 / 拉取模型列表</button>
<button class="btn btn-sm" id="btn-test-vector-api" style="flex:1">测试 Embedding</button> <button class="btn btn-sm" id="embedding-btn-test" style="flex:1">测试</button>
</div> </div>
<div class="settings-hint" id="embedding-api-connect-status" style="margin-bottom:16px;"></div> <div class="settings-hint" id="embedding-api-connect-status" style="margin-bottom:16px;"></div>
</div> </div>
@@ -569,6 +570,7 @@
</div> </div>
<div class="settings-btn-row" id="rerank-api-connect-row"> <div class="settings-btn-row" id="rerank-api-connect-row">
<button class="btn btn-sm" id="rerank-btn-connect" style="flex:1">连接 / 拉取模型列表</button> <button class="btn btn-sm" id="rerank-btn-connect" style="flex:1">连接 / 拉取模型列表</button>
<button class="btn btn-sm" id="rerank-btn-test" style="flex:1">测试</button>
</div> </div>
<div class="settings-hint" id="rerank-api-connect-status"></div> <div class="settings-hint" id="rerank-api-connect-status"></div>
</div> </div>

View File

@@ -43,6 +43,8 @@ import { runSummaryGeneration } from "./generate/generator.js";
// vector service // vector service
import { embed, getEngineFingerprint, testOnlineService } from "./vector/utils/embedder.js"; import { embed, getEngineFingerprint, testOnlineService } from "./vector/utils/embedder.js";
import { testL0Service } from "./vector/llm/llm-service.js";
import { testRerankService } from "./vector/llm/reranker.js";
// tokenizer // tokenizer
import { preload as preloadTokenizer, injectEntities, isReady as isTokenizerReady } from "./vector/utils/tokenizer.js"; import { preload as preloadTokenizer, injectEntities, isReady as isTokenizerReady } from "./vector/utils/tokenizer.js";
@@ -421,17 +423,23 @@ function handleAnchorCancel() {
postToFrame({ type: "ANCHOR_GEN_PROGRESS", current: -1, total: 0 }); postToFrame({ type: "ANCHOR_GEN_PROGRESS", current: -1, total: 0 });
} }
async function handleTestOnlineService(provider, config) { async function handleTestOnlineService(provider, config, target = "embedding") {
try { try {
postToFrame({ type: "VECTOR_ONLINE_STATUS", status: "downloading", message: "连接中..." }); postToFrame({ type: "VECTOR_ONLINE_STATUS", target, status: "downloading", message: "连接中..." });
const result = await testOnlineService(provider, config); let result;
if (target === "l0") result = await testL0Service(config);
else if (target === "rerank") result = await testRerankService(config);
else result = await testOnlineService(provider, config);
postToFrame({ postToFrame({
type: "VECTOR_ONLINE_STATUS", type: "VECTOR_ONLINE_STATUS",
target,
status: "success", status: "success",
message: `连接成功 (${result.dims}维)`, message: target === "embedding"
? `连接成功 (${result.dims}维)`
: (result.message || "连接成功"),
}); });
} catch (e) { } catch (e) {
postToFrame({ type: "VECTOR_ONLINE_STATUS", status: "error", message: e.message }); postToFrame({ type: "VECTOR_ONLINE_STATUS", target, status: "error", message: e.message });
} }
} }
@@ -1631,7 +1639,7 @@ async function handleFrameMessage(event) {
break; break;
case "VECTOR_TEST_ONLINE": case "VECTOR_TEST_ONLINE":
handleTestOnlineService(data.provider, data.config); handleTestOnlineService(data.provider, data.config, data.target || "embedding");
break; break;
case "VECTOR_GENERATE": case "VECTOR_GENERATE":

View File

@@ -39,6 +39,17 @@ function getL0ApiConfig() {
}; };
} }
function normalizeL0ApiConfig(apiConfig = null) {
const fallback = getL0ApiConfig();
const next = apiConfig || {};
return {
provider: String(next.provider || fallback.provider || 'siliconflow').trim(),
url: String(next.url || fallback.url || DEFAULT_L0_API_URL).trim(),
key: String(next.key || fallback.key || '').trim(),
model: String(next.model || fallback.model || DEFAULT_L0_MODEL).trim(),
};
}
function getNextKey(rawKey) { function getNextKey(rawKey) {
const keys = String(rawKey || '') const keys = String(rawKey || '')
.split(/[,;|\n]+/) .split(/[,;|\n]+/)
@@ -60,12 +71,13 @@ export async function callLLM(messages, options = {}) {
temperature = 0.2, temperature = 0.2,
max_tokens = 500, max_tokens = 500,
timeout = 40000, timeout = 40000,
apiConfig = null,
} = options; } = options;
const mod = getStreamingModule(); const mod = getStreamingModule();
if (!mod) throw new Error('Streaming module not ready'); if (!mod) throw new Error('Streaming module not ready');
const apiCfg = getL0ApiConfig(); const apiCfg = normalizeL0ApiConfig(apiConfig);
const apiKey = getNextKey(apiCfg.key); const apiKey = getNextKey(apiCfg.key);
if (!apiKey) { if (!apiKey) {
throw new Error('L0 requires siliconflow API key'); throw new Error('L0 requires siliconflow API key');
@@ -111,6 +123,24 @@ export async function callLLM(messages, options = {}) {
} }
} }
export async function testL0Service(apiConfig = {}) {
if (!apiConfig?.key) {
throw new Error('请配置 L0 API Key');
}
const result = await callLLM([
{ role: 'system', content: '你是一个测试助手。请只输出 OK。' },
{ role: 'user', content: '只输出 OK' },
], {
apiConfig,
temperature: 0,
max_tokens: 16,
timeout: 15000,
});
const text = String(result || '').trim();
if (!text) throw new Error('返回为空');
return { success: true, message: `连接成功:${text.slice(0, 60)}` };
}
export function cancelAllL0Requests() { export function cancelAllL0Requests() {
const mod = getStreamingModule(); const mod = getStreamingModule();
if (!mod?.cancel) return; if (!mod?.cancel) return;

View File

@@ -273,19 +273,51 @@ export async function rerankChunks(query, chunks, options = {}) {
/** /**
* 测试 Rerank 服务连接 * 测试 Rerank 服务连接
*/ */
export async function testRerankService() { export async function testRerankService(apiConfig = {}) {
const key = getApiKey(); const next = {
if (!key) { provider: String(apiConfig.provider || 'siliconflow').trim(),
throw new Error('请配置硅基 API Key'); url: String(apiConfig.url || DEFAULT_RERANK_URL).trim(),
key: String(apiConfig.key || '').trim(),
model: String(apiConfig.model || RERANK_MODEL).trim(),
};
if (!next.key) {
throw new Error('请配置 Rerank API Key');
} }
const key = getNextRerankKey(next.key);
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 15000);
try { try {
const { results } = await rerank('测试查询', ['测试文档1', '测试文档2'], { topN: 2 }); const baseUrl = String(next.url || DEFAULT_RERANK_URL).replace(/\/+$/, '');
const response = await fetch(`${baseUrl}/rerank`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${key}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
model: next.model,
query: '测试查询',
documents: ['测试文档1', '测试文档2'],
top_n: 2,
return_documents: false,
}),
signal: controller.signal,
});
clearTimeout(timeoutId);
if (!response.ok) {
const errorText = await response.text().catch(() => '');
throw new Error(`Rerank API ${response.status}: ${errorText.slice(0, 200)}`);
}
const data = await response.json();
const results = Array.isArray(data.results) ? data.results : [];
return { return {
success: true, success: true,
message: `连接成功返回 ${results.length} 个结果`, message: `连接成功返回 ${results.length} 个结果`,
}; };
} catch (e) { } catch (e) {
throw new Error(`连接失败: ${e.message}`); throw new Error(`连接失败: ${e.message}`);
} finally {
clearTimeout(timeoutId);
} }
} }