fix(story-summary): unify vector api test actions
This commit is contained in:
@@ -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':
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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":
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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(/\/+$/, '');
|
||||||
return {
|
const response = await fetch(`${baseUrl}/rerank`, {
|
||||||
success: true,
|
method: 'POST',
|
||||||
message: `连接成功,返回 ${results.length} 个结果`,
|
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 {
|
||||||
|
success: true,
|
||||||
|
message: `连接成功:返回 ${results.length} 个结果`,
|
||||||
};
|
};
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new Error(`连接失败: ${e.message}`);
|
throw new Error(`连接失败: ${e.message}`);
|
||||||
|
} finally {
|
||||||
|
clearTimeout(timeoutId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user