上傳檔案到「modules/story-summary」
This commit is contained in:
@@ -21,6 +21,10 @@
|
||||
padding-right: 4px;
|
||||
}
|
||||
|
||||
.confirm-modal-box {
|
||||
max-width: 440px;
|
||||
}
|
||||
|
||||
.fact-group {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
@@ -358,8 +358,8 @@
|
||||
postMsg('ANCHOR_GENERATE');
|
||||
};
|
||||
|
||||
$('btn-anchor-clear').onclick = () => {
|
||||
if (confirm('清空所有记忆锚点?(L0 向量也会一并清除)')) {
|
||||
$('btn-anchor-clear').onclick = async () => {
|
||||
if (await showConfirm('清空锚点', '清空所有记忆锚点?(L0 向量也会一并清除)')) {
|
||||
postMsg('ANCHOR_CLEAR');
|
||||
}
|
||||
};
|
||||
@@ -375,6 +375,7 @@
|
||||
};
|
||||
|
||||
$('btn-test-vector-api').onclick = () => {
|
||||
saveConfig(); // 先保存新 Key 到 localStorage
|
||||
postMsg('VECTOR_TEST_ONLINE', {
|
||||
provider: 'siliconflow',
|
||||
config: {
|
||||
@@ -391,8 +392,10 @@
|
||||
postMsg('VECTOR_GENERATE', { config: getVectorConfig() });
|
||||
};
|
||||
|
||||
$('btn-clear-vectors').onclick = () => {
|
||||
if (confirm('确定清空所有向量数据?')) postMsg('VECTOR_CLEAR');
|
||||
$('btn-clear-vectors').onclick = async () => {
|
||||
if (await showConfirm('清空向量', '确定清空所有向量数据?')) {
|
||||
postMsg('VECTOR_CLEAR');
|
||||
}
|
||||
};
|
||||
|
||||
$('btn-cancel-vectors').onclick = () => postMsg('VECTOR_CANCEL_GENERATE');
|
||||
@@ -955,6 +958,43 @@
|
||||
postMsg('FULLSCREEN_CLOSED');
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示通用确认弹窗
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
function showConfirm(title, message, okText = '执行', cancelText = '取消') {
|
||||
return new Promise(resolve => {
|
||||
const modal = $('confirm-modal');
|
||||
const titleEl = $('confirm-title');
|
||||
const msgEl = $('confirm-message');
|
||||
const okBtn = $('confirm-ok');
|
||||
const cancelBtn = $('confirm-cancel');
|
||||
const closeBtn = $('confirm-close');
|
||||
const backdrop = $('confirm-backdrop');
|
||||
|
||||
titleEl.textContent = title;
|
||||
msgEl.textContent = message;
|
||||
okBtn.textContent = okText;
|
||||
cancelBtn.textContent = cancelText;
|
||||
|
||||
const close = (result) => {
|
||||
modal.classList.remove('active');
|
||||
okBtn.onclick = null;
|
||||
cancelBtn.onclick = null;
|
||||
closeBtn.onclick = null;
|
||||
backdrop.onclick = null;
|
||||
resolve(result);
|
||||
};
|
||||
|
||||
okBtn.onclick = () => close(true);
|
||||
cancelBtn.onclick = () => close(false);
|
||||
closeBtn.onclick = () => close(false);
|
||||
backdrop.onclick = () => close(false);
|
||||
|
||||
modal.classList.add('active');
|
||||
});
|
||||
}
|
||||
|
||||
function renderArcsEditor(arcs) {
|
||||
const list = arcs?.length ? arcs : [{ name: '', trajectory: '', progress: 0, moments: [] }];
|
||||
const es = $('editor-struct');
|
||||
@@ -1526,7 +1566,11 @@
|
||||
};
|
||||
|
||||
// Main actions
|
||||
$('btn-clear').onclick = () => postMsg('REQUEST_CLEAR');
|
||||
$('btn-clear').onclick = async () => {
|
||||
if (await showConfirm('清空数据', '确定要清空本聊天的所有总结、关键词及人物关系数据吗?此操作不可撤销。')) {
|
||||
postMsg('REQUEST_CLEAR');
|
||||
}
|
||||
};
|
||||
$('btn-generate').onclick = () => {
|
||||
const btn = $('btn-generate');
|
||||
if (!localGenerating) {
|
||||
|
||||
@@ -20,6 +20,10 @@
|
||||
padding-right: 4px;
|
||||
}
|
||||
|
||||
.confirm-modal-box {
|
||||
max-width: 440px;
|
||||
}
|
||||
|
||||
.fact-group {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
@@ -833,6 +833,28 @@
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/echarts@5/dist/echarts.min.js"></script>
|
||||
<script src="story-summary-ui.js"></script>
|
||||
<!-- Confirm Modal -->
|
||||
<div class="modal" id="confirm-modal">
|
||||
<div class="modal-bg" id="confirm-backdrop"></div>
|
||||
<div class="modal-box confirm-modal-box">
|
||||
<div class="modal-head">
|
||||
<h2 id="confirm-title">确认操作</h2>
|
||||
<button class="modal-close" id="confirm-close">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<line x1="18" y1="6" x2="6" y2="18" />
|
||||
<line x1="6" y1="6" x2="18" y2="18" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div id="confirm-message" style="margin: 10px 0; line-height: 1.6; color: var(--fg);">内容</div>
|
||||
</div>
|
||||
<div class="modal-foot">
|
||||
<button class="btn" id="confirm-cancel">取消</button>
|
||||
<button class="btn btn-del" id="confirm-ok">执行</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
@@ -449,6 +449,34 @@ async function handleGenerateVectors(vectorCfg) {
|
||||
await clearStateVectors(chatId);
|
||||
await updateMeta(chatId, { lastChunkFloor: -1, fingerprint });
|
||||
|
||||
// Helper to embed with retry
|
||||
const embedWithRetry = async (texts, phase, currentBatchIdx, totalItems) => {
|
||||
while (true) {
|
||||
if (vectorCancelled) return null;
|
||||
try {
|
||||
return await embed(texts, vectorCfg, { signal: vectorAbortController.signal });
|
||||
} catch (e) {
|
||||
if (e?.name === "AbortError" || vectorCancelled) return null;
|
||||
xbLog.error(MODULE_ID, `${phase} 向量化单次失败`, e);
|
||||
|
||||
// 等待 60 秒重试
|
||||
const waitSec = 60;
|
||||
for (let s = waitSec; s > 0; s--) {
|
||||
if (vectorCancelled) return null;
|
||||
postToFrame({
|
||||
type: "VECTOR_GEN_PROGRESS",
|
||||
phase,
|
||||
current: currentBatchIdx,
|
||||
total: totalItems,
|
||||
message: `触发限流,${s}s 后重试...`
|
||||
});
|
||||
await new Promise(r => setTimeout(r, 1000));
|
||||
}
|
||||
postToFrame({ type: "VECTOR_GEN_PROGRESS", phase, current: currentBatchIdx, total: totalItems, message: "正在重试..." });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const atoms = getStateAtoms();
|
||||
if (!atoms.length) {
|
||||
postToFrame({ type: "VECTOR_GEN_PROGRESS", phase: "L0", current: 0, total: 0, message: "L0 为空,跳过" });
|
||||
@@ -462,29 +490,26 @@ async function handleGenerateVectors(vectorCfg) {
|
||||
const batch = atoms.slice(i, i + batchSize);
|
||||
const semTexts = batch.map(a => a.semantic);
|
||||
const rTexts = batch.map(a => buildRAggregateText(a));
|
||||
try {
|
||||
const vectors = await embed(semTexts.concat(rTexts), vectorCfg, { signal: vectorAbortController.signal });
|
||||
const split = semTexts.length;
|
||||
if (!Array.isArray(vectors) || vectors.length < split * 2) {
|
||||
throw new Error(`embed length mismatch: expect>=${split * 2}, got=${vectors?.length || 0}`);
|
||||
}
|
||||
const semVectors = vectors.slice(0, split);
|
||||
const rVectors = vectors.slice(split, split + split);
|
||||
const items = batch.map((a, j) => ({
|
||||
atomId: a.atomId,
|
||||
floor: a.floor,
|
||||
vector: semVectors[j],
|
||||
rVector: rVectors[j] || semVectors[j],
|
||||
}));
|
||||
await saveStateVectors(chatId, items, fingerprint);
|
||||
l0Completed += batch.length;
|
||||
postToFrame({ type: "VECTOR_GEN_PROGRESS", phase: "L0", current: l0Completed, total: atoms.length });
|
||||
} catch (e) {
|
||||
if (e?.name === "AbortError") break;
|
||||
xbLog.error(MODULE_ID, "L0 向量化失败", e);
|
||||
vectorCancelled = true;
|
||||
break;
|
||||
|
||||
const vectors = await embedWithRetry(semTexts.concat(rTexts), "L0", l0Completed, atoms.length);
|
||||
if (!vectors) break; // cancelled
|
||||
|
||||
const split = semTexts.length;
|
||||
if (!Array.isArray(vectors) || vectors.length < split * 2) {
|
||||
xbLog.error(MODULE_ID, `embed长度不匹配: expect>=${split * 2}, got=${vectors?.length || 0}`);
|
||||
continue;
|
||||
}
|
||||
const semVectors = vectors.slice(0, split);
|
||||
const rVectors = vectors.slice(split, split + split);
|
||||
const items = batch.map((a, j) => ({
|
||||
atomId: a.atomId,
|
||||
floor: a.floor,
|
||||
vector: semVectors[j],
|
||||
rVector: rVectors[j] || semVectors[j],
|
||||
}));
|
||||
await saveStateVectors(chatId, items, fingerprint);
|
||||
l0Completed += batch.length;
|
||||
postToFrame({ type: "VECTOR_GEN_PROGRESS", phase: "L0", current: l0Completed, total: atoms.length });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -516,22 +541,18 @@ async function handleGenerateVectors(vectorCfg) {
|
||||
|
||||
const batch = allChunks.slice(i, i + batchSize);
|
||||
const texts = batch.map(c => c.text);
|
||||
try {
|
||||
const vectors = await embed(texts, vectorCfg, { signal: vectorAbortController.signal });
|
||||
const items = batch.map((c, j) => ({
|
||||
chunkId: c.chunkId,
|
||||
vector: vectors[j],
|
||||
}));
|
||||
await saveChunkVectors(chatId, items, fingerprint);
|
||||
l1Vectors = l1Vectors.concat(items);
|
||||
l1Completed += batch.length;
|
||||
postToFrame({ type: "VECTOR_GEN_PROGRESS", phase: "L1", current: l1Completed, total: allChunks.length });
|
||||
} catch (e) {
|
||||
if (e?.name === "AbortError") break;
|
||||
xbLog.error(MODULE_ID, "L1 向量化失败", e);
|
||||
vectorCancelled = true;
|
||||
break;
|
||||
}
|
||||
|
||||
const vectors = await embedWithRetry(texts, "L1", l1Completed, allChunks.length);
|
||||
if (!vectors) break; // cancelled
|
||||
|
||||
const items = batch.map((c, j) => ({
|
||||
chunkId: c.chunkId,
|
||||
vector: vectors[j],
|
||||
}));
|
||||
await saveChunkVectors(chatId, items, fingerprint);
|
||||
l1Vectors = l1Vectors.concat(items);
|
||||
l1Completed += batch.length;
|
||||
postToFrame({ type: "VECTOR_GEN_PROGRESS", phase: "L1", current: l1Completed, total: allChunks.length });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -555,21 +576,17 @@ async function handleGenerateVectors(vectorCfg) {
|
||||
|
||||
const batch = l2Pairs.slice(i, i + batchSize);
|
||||
const texts = batch.map(p => p.text);
|
||||
try {
|
||||
const vectors = await embed(texts, vectorCfg, { signal: vectorAbortController.signal });
|
||||
const items = batch.map((p, idx) => ({
|
||||
eventId: p.id,
|
||||
vector: vectors[idx],
|
||||
}));
|
||||
await saveEventVectorsToDb(chatId, items, fingerprint);
|
||||
l2Completed += batch.length;
|
||||
postToFrame({ type: "VECTOR_GEN_PROGRESS", phase: "L2", current: l2Completed, total: l2Pairs.length });
|
||||
} catch (e) {
|
||||
if (e?.name === "AbortError") break;
|
||||
xbLog.error(MODULE_ID, "L2 向量化失败", e);
|
||||
vectorCancelled = true;
|
||||
break;
|
||||
}
|
||||
|
||||
const vectors = await embedWithRetry(texts, "L2", l2Completed, l2Pairs.length);
|
||||
if (!vectors) break; // cancelled
|
||||
|
||||
const items = batch.map((p, idx) => ({
|
||||
eventId: p.id,
|
||||
vector: vectors[idx],
|
||||
}));
|
||||
await saveEventVectorsToDb(chatId, items, fingerprint);
|
||||
l2Completed += batch.length;
|
||||
postToFrame({ type: "VECTOR_GEN_PROGRESS", phase: "L2", current: l2Completed, total: l2Pairs.length });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user