Refine story summary prompts and vector sync
This commit is contained in:
@@ -36,7 +36,7 @@ Incremental_Summary_Requirements:
|
|||||||
- 转折: 改变某条线走向
|
- 转折: 改变某条线走向
|
||||||
- 点睛: 有细节不影响主线
|
- 点睛: 有细节不影响主线
|
||||||
- 氛围: 纯粹氛围片段
|
- 氛围: 纯粹氛围片段
|
||||||
- Causal_Chain: 为每个新事件标注直接前因事件ID(causedBy),0-2个。只填 evt-数字 形式,必须指向“已存在事件”或“本次新输出事件”。不要写解释文字。
|
- Causal_Chain: 为每个新事件标注直接前因事件ID(causedBy)。仅在因果关系明确(直接导致/明确动机/承接后果)时填写;不明确时填[]完全正常。0-2个,只填 evt-数字,指向已存在或本次新输出事件。
|
||||||
- Character_Dynamics: 识别新角色,追踪关系趋势(破裂/厌恶/反感/陌生/投缘/亲密/交融)
|
- Character_Dynamics: 识别新角色,追踪关系趋势(破裂/厌恶/反感/陌生/投缘/亲密/交融)
|
||||||
- Arc_Tracking: 更新角色弧光轨迹与成长进度(0.0-1.0)
|
- Arc_Tracking: 更新角色弧光轨迹与成长进度(0.0-1.0)
|
||||||
- World_State_Tracking: 维护当前世界的硬性约束。解决"什么不能违反"。采用 KV 覆盖模型,追踪生死、物品归属、秘密知情、关系状态、环境规则等不可违背的事实。(覆盖式更新)
|
- World_State_Tracking: 维护当前世界的硬性约束。解决"什么不能违反"。采用 KV 覆盖模型,追踪生死、物品归属、秘密知情、关系状态、环境规则等不可违背的事实。(覆盖式更新)
|
||||||
@@ -215,10 +215,7 @@ Before generating, observe the USER and analyze carefully:
|
|||||||
- events.id 从 evt-{nextEventId} 开始编号
|
- events.id 从 evt-{nextEventId} 开始编号
|
||||||
- 仅输出【增量】内容,已有事件绝不重复
|
- 仅输出【增量】内容,已有事件绝不重复
|
||||||
- keywords 是全局关键词,综合已有+新增
|
- keywords 是全局关键词,综合已有+新增
|
||||||
- causedBy 规则:
|
- causedBy 仅在因果明确时填写,允许为[],0-2个,详见上方 Causal_Chain 规则
|
||||||
- 数组,最多2个;无前因则 []
|
|
||||||
- 只能填 evt-数字(例如 evt-12)
|
|
||||||
- 必须引用“已存在事件”或“本次新输出事件”(允许引用本次 JSON 内较早出现的事件)
|
|
||||||
- worldUpdate 可为空数组
|
- worldUpdate 可为空数组
|
||||||
- 合法JSON,字符串值内部避免英文双引号
|
- 合法JSON,字符串值内部避免英文双引号
|
||||||
- 用朴实、白描、有烟火气的笔触记录,避免比喻和意象
|
- 用朴实、白描、有烟火气的笔触记录,避免比喻和意象
|
||||||
|
|||||||
@@ -91,11 +91,11 @@ function cleanSummary(summary) {
|
|||||||
function buildSystemPreamble() {
|
function buildSystemPreamble() {
|
||||||
return [
|
return [
|
||||||
"以上内容为因上下文窗口限制保留的可见历史",
|
"以上内容为因上下文窗口限制保留的可见历史",
|
||||||
"【剧情记忆】为对以上可见、不可见历史的总结",
|
"以下【剧情记忆】是对可见与不可见历史的总结:",
|
||||||
"1) 【世界状态】属于硬约束",
|
"• 【世界约束】记录着已确立的事实",
|
||||||
"2) 【事件/证据/碎片/人物弧光】可用于补全上下文与动机。",
|
"• 其余部分是过往经历的回忆碎片",
|
||||||
"",
|
"",
|
||||||
"请阅读并内化以下剧情记忆:",
|
"请内化这些记忆:",
|
||||||
].join("\n");
|
].join("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -275,7 +275,7 @@ function buildNonVectorPrompt(store) {
|
|||||||
|
|
||||||
if (data.world?.length) {
|
if (data.world?.length) {
|
||||||
const lines = formatWorldLines(data.world);
|
const lines = formatWorldLines(data.world);
|
||||||
sections.push(`[世界约束] 规则手册,请严格遵守\n${lines.join("\n")}`);
|
sections.push(`[世界约束] 已确立的事实\n${lines.join("\n")}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.events?.length) {
|
if (data.events?.length) {
|
||||||
@@ -602,7 +602,7 @@ async function buildVectorPrompt(store, recallResult, causalById, queryEntities
|
|||||||
|
|
||||||
// 1. 世界约束
|
// 1. 世界约束
|
||||||
if (assembled.world.lines.length) {
|
if (assembled.world.lines.length) {
|
||||||
sections.push(`[世界约束] 规则手册,请严格遵守\n${assembled.world.lines.join("\n")}`);
|
sections.push(`[世界约束] 已确立的事实\n${assembled.world.lines.join("\n")}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 核心经历
|
// 2. 核心经历
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// story-summary-ui.js
|
// story-summary-ui.js
|
||||||
// iframe 内 UI 逻辑
|
// iframe 内 UI 逻辑
|
||||||
|
|
||||||
(function() {
|
(function () {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
// ═══════════════════════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
@@ -154,7 +154,7 @@
|
|||||||
saveConfig();
|
saveConfig();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch {}
|
} catch { }
|
||||||
}
|
}
|
||||||
|
|
||||||
function applyConfig(cfg) {
|
function applyConfig(cfg) {
|
||||||
@@ -299,16 +299,18 @@
|
|||||||
const items = rules?.length ? rules : [];
|
const items = rules?.length ? rules : [];
|
||||||
|
|
||||||
setHtml(list, items.map((r, i) => `
|
setHtml(list, items.map((r, i) => `
|
||||||
<div class="filter-rule-item" data-idx="${i}" style="display:flex;gap:6px;align-items:center">
|
<div class="filter-rule-item" data-idx="${i}">
|
||||||
<input type="text" class="filter-rule-start" placeholder="起始(可空)" value="${h(r.start || '')}" style="flex:1;padding:6px 8px;font-size:.8125rem">
|
<div class="filter-rule-inputs">
|
||||||
<span style="color:var(--txt3)">→</span>
|
<input type="text" class="filter-rule-start" placeholder="起始(可空)" value="${h(r.start || '')}">
|
||||||
<input type="text" class="filter-rule-end" placeholder="结束(可空)" value="${h(r.end || '')}" style="flex:1;padding:6px 8px;font-size:.8125rem">
|
<span class="rule-arrow">⬇</span>
|
||||||
<button class="btn btn-sm btn-del filter-rule-del" style="padding:4px 8px">✕</button>
|
<input type="text" class="filter-rule-end" placeholder="结束(可空)" value="${h(r.end || '')}">
|
||||||
|
</div>
|
||||||
|
<button class="btn-del-rule">✕</button>
|
||||||
</div>
|
</div>
|
||||||
`).join(''));
|
`).join(''));
|
||||||
|
|
||||||
// 绑定删除
|
// 绑定删除
|
||||||
list.querySelectorAll('.filter-rule-del').forEach(btn => {
|
list.querySelectorAll('.btn-del-rule').forEach(btn => {
|
||||||
btn.onclick = () => {
|
btn.onclick = () => {
|
||||||
btn.closest('.filter-rule-item')?.remove();
|
btn.closest('.filter-rule-item')?.remove();
|
||||||
};
|
};
|
||||||
@@ -338,14 +340,15 @@
|
|||||||
const div = document.createElement('div');
|
const div = document.createElement('div');
|
||||||
div.className = 'filter-rule-item';
|
div.className = 'filter-rule-item';
|
||||||
div.dataset.idx = idx;
|
div.dataset.idx = idx;
|
||||||
div.style.cssText = 'display:flex;gap:6px;align-items:center';
|
|
||||||
setHtml(div, `
|
setHtml(div, `
|
||||||
<input type="text" class="filter-rule-start" placeholder="起始(可空)" value="" style="flex:1;padding:6px 8px;font-size:.8125rem">
|
<div class="filter-rule-inputs">
|
||||||
<span style="color:var(--txt3)">→</span>
|
<input type="text" class="filter-rule-start" placeholder="起始(可空)" value="">
|
||||||
<input type="text" class="filter-rule-end" placeholder="结束(可空)" value="" style="flex:1;padding:6px 8px;font-size:.8125rem">
|
<span class="rule-arrow">⬇</span>
|
||||||
<button class="btn btn-sm btn-del filter-rule-del" style="padding:4px 8px">✕</button>
|
<input type="text" class="filter-rule-end" placeholder="结束(可空)" value="">
|
||||||
|
</div>
|
||||||
|
<button class="btn-del-rule">✕</button>
|
||||||
`);
|
`);
|
||||||
div.querySelector('.filter-rule-del').onclick = () => div.remove();
|
div.querySelector('.btn-del-rule').onclick = () => div.remove();
|
||||||
list.appendChild(div);
|
list.appendChild(div);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -550,7 +553,24 @@
|
|||||||
updateProviderUI(config.api.provider);
|
updateProviderUI(config.api.provider);
|
||||||
if (config.vector) loadVectorConfig(config.vector);
|
if (config.vector) loadVectorConfig(config.vector);
|
||||||
|
|
||||||
|
// Initialize sub-options visibility
|
||||||
|
const autoSummaryOptions = $('auto-summary-options');
|
||||||
|
if (autoSummaryOptions) {
|
||||||
|
autoSummaryOptions.classList.toggle('hidden', !config.trigger.enabled);
|
||||||
|
}
|
||||||
|
const insertWrapperOptions = $('insert-wrapper-options');
|
||||||
|
if (insertWrapperOptions) {
|
||||||
|
insertWrapperOptions.classList.toggle('hidden', !config.trigger.forceInsertAtEnd);
|
||||||
|
}
|
||||||
|
|
||||||
$('settings-modal').classList.add('active');
|
$('settings-modal').classList.add('active');
|
||||||
|
|
||||||
|
// Default to first tab
|
||||||
|
$$('.settings-tab').forEach(t => t.classList.remove('active'));
|
||||||
|
$$('.settings-tab[data-tab="tab-summary"]').forEach(t => t.classList.add('active'));
|
||||||
|
$$('.tab-pane').forEach(p => p.classList.remove('active'));
|
||||||
|
$('tab-summary').classList.add('active');
|
||||||
|
|
||||||
postMsg('SETTINGS_OPENED');
|
postMsg('SETTINGS_OPENED');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1202,17 +1222,6 @@ CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860", "--workers", "
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function openRecallLog() {
|
|
||||||
updateRecallLogDisplay();
|
|
||||||
$('recall-log-modal').classList.add('active');
|
|
||||||
postMsg('FULLSCREEN_OPENED');
|
|
||||||
}
|
|
||||||
|
|
||||||
function closeRecallLog() {
|
|
||||||
$('recall-log-modal').classList.remove('active');
|
|
||||||
postMsg('FULLSCREEN_CLOSED');
|
|
||||||
}
|
|
||||||
|
|
||||||
// ═══════════════════════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
// Editor
|
// Editor
|
||||||
// ═══════════════════════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
@@ -1666,6 +1675,27 @@ CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860", "--workers", "
|
|||||||
$('settings-cancel').onclick = () => closeSettings(false);
|
$('settings-cancel').onclick = () => closeSettings(false);
|
||||||
$('settings-save').onclick = () => closeSettings(true);
|
$('settings-save').onclick = () => closeSettings(true);
|
||||||
|
|
||||||
|
// Settings tabs
|
||||||
|
$$('.settings-tab').forEach(tab => {
|
||||||
|
tab.onclick = () => {
|
||||||
|
const targetId = tab.dataset.tab;
|
||||||
|
if (!targetId) return;
|
||||||
|
|
||||||
|
// Update tab active state
|
||||||
|
$$('.settings-tab').forEach(t => t.classList.remove('active'));
|
||||||
|
tab.classList.add('active');
|
||||||
|
|
||||||
|
// Update pane active state
|
||||||
|
$$('.tab-pane').forEach(p => p.classList.remove('active'));
|
||||||
|
$(targetId).classList.add('active');
|
||||||
|
|
||||||
|
// If switching to debug tab, refresh log
|
||||||
|
if (targetId === 'tab-debug') {
|
||||||
|
postMsg('REQUEST_RECALL_LOG');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
// API provider change
|
// API provider change
|
||||||
$('api-provider').onchange = e => {
|
$('api-provider').onchange = e => {
|
||||||
const pv = PROVIDER_DEFAULTS[e.target.value];
|
const pv = PROVIDER_DEFAULTS[e.target.value];
|
||||||
@@ -1729,11 +1759,6 @@ CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860", "--workers", "
|
|||||||
$('hf-guide-backdrop').onclick = closeHfGuide;
|
$('hf-guide-backdrop').onclick = closeHfGuide;
|
||||||
$('hf-guide-close').onclick = closeHfGuide;
|
$('hf-guide-close').onclick = closeHfGuide;
|
||||||
|
|
||||||
// Recall log
|
|
||||||
$('btn-recall').onclick = openRecallLog;
|
|
||||||
$('recall-log-backdrop').onclick = closeRecallLog;
|
|
||||||
$('recall-log-close').onclick = closeRecallLog;
|
|
||||||
|
|
||||||
// Character selector
|
// Character selector
|
||||||
$('char-sel-trigger').onclick = e => {
|
$('char-sel-trigger').onclick = e => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
@@ -1748,6 +1773,36 @@ CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860", "--workers", "
|
|||||||
// Vector UI
|
// Vector UI
|
||||||
initVectorUI();
|
initVectorUI();
|
||||||
|
|
||||||
|
// Gen params collapsible
|
||||||
|
const genParamsToggle = $('gen-params-toggle');
|
||||||
|
const genParamsContent = $('gen-params-content');
|
||||||
|
if (genParamsToggle && genParamsContent) {
|
||||||
|
genParamsToggle.onclick = () => {
|
||||||
|
const collapse = genParamsToggle.closest('.settings-collapse');
|
||||||
|
collapse.classList.toggle('open');
|
||||||
|
genParamsContent.classList.toggle('hidden');
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Auto summary sub-options toggle
|
||||||
|
const triggerEnabled = $('trigger-enabled');
|
||||||
|
const autoSummaryOptions = $('auto-summary-options');
|
||||||
|
if (triggerEnabled && autoSummaryOptions) {
|
||||||
|
triggerEnabled.onchange = () => {
|
||||||
|
autoSummaryOptions.classList.toggle('hidden', !triggerEnabled.checked);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Force insert sub-options toggle
|
||||||
|
const triggerInsertAtEnd = $('trigger-insert-at-end');
|
||||||
|
const insertWrapperOptions = $('insert-wrapper-options');
|
||||||
|
if (triggerInsertAtEnd && insertWrapperOptions) {
|
||||||
|
triggerInsertAtEnd.onchange = () => {
|
||||||
|
insertWrapperOptions.classList.toggle('hidden', !triggerInsertAtEnd.checked);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Resize
|
// Resize
|
||||||
window.onresize = () => {
|
window.onresize = () => {
|
||||||
relationChart?.resize();
|
relationChart?.resize();
|
||||||
|
|||||||
@@ -59,7 +59,8 @@ main {
|
|||||||
min-height: 0;
|
min-height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.left, .right {
|
.left,
|
||||||
|
.right {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 24px;
|
gap: 24px;
|
||||||
@@ -67,9 +68,11 @@ main {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* 关键词卡片:固定高度 */
|
/* 关键词卡片:固定高度 */
|
||||||
.left > .card:first-child {
|
.left>.card:first-child {
|
||||||
flex: 0 0 auto; /* 关键词:不伸缩 */
|
flex: 0 0 auto;
|
||||||
|
/* 关键词:不伸缩 */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ═══════════════════════════════════════════════════════════════════════════
|
/* ═══════════════════════════════════════════════════════════════════════════
|
||||||
Typography
|
Typography
|
||||||
═══════════════════════════════════════════════════════════════════════════ */
|
═══════════════════════════════════════════════════════════════════════════ */
|
||||||
@@ -153,9 +156,9 @@ h1 span {
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
padding: 8px 16px;
|
padding: 4px 0;
|
||||||
background: var(--bg3);
|
background: transparent;
|
||||||
border: 1px solid var(--bdr);
|
border: none;
|
||||||
font-size: .8125rem;
|
font-size: .8125rem;
|
||||||
color: var(--txt2);
|
color: var(--txt2);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
@@ -163,7 +166,7 @@ h1 span {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.chk-label:hover {
|
.chk-label:hover {
|
||||||
border-color: var(--acc);
|
color: var(--txt);
|
||||||
}
|
}
|
||||||
|
|
||||||
.chk-label input {
|
.chk-label input {
|
||||||
@@ -290,33 +293,25 @@ h1 span {
|
|||||||
padding: 10px 14px;
|
padding: 10px 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-recall {
|
.btn-debug {
|
||||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
background: var(--bg2);
|
||||||
color: #fff;
|
color: var(--txt2);
|
||||||
border-color: #667eea;
|
border: 1px solid var(--bdr);
|
||||||
position: relative;
|
display: flex;
|
||||||
overflow: hidden;
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-recall:hover {
|
.btn-debug svg {
|
||||||
background: linear-gradient(135deg, #5a67d8 0%, #6b46a1 100%);
|
width: 14px;
|
||||||
border-color: #5a67d8;
|
height: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-recall::after {
|
.btn-debug:hover {
|
||||||
content: '';
|
background: var(--bg3);
|
||||||
position: absolute;
|
border-color: var(--acc);
|
||||||
top: -50%;
|
color: var(--txt);
|
||||||
left: -50%;
|
|
||||||
width: 200%;
|
|
||||||
height: 200%;
|
|
||||||
background: linear-gradient(45deg, transparent 40%, rgba(255,255,255,.15) 50%, transparent 60%);
|
|
||||||
animation: shimmer 3s infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes shimmer {
|
|
||||||
0% { transform: translateX(-100%) rotate(45deg); }
|
|
||||||
100% { transform: translateX(100%) rotate(45deg); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ═══════════════════════════════════════════════════════════════════════════
|
/* ═══════════════════════════════════════════════════════════════════════════
|
||||||
@@ -700,13 +695,40 @@ h1 span {
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.trend-broken { background: rgba(68, 68, 68, .15); color: #444; }
|
.trend-broken {
|
||||||
.trend-hate { background: rgba(139, 0, 0, .15); color: #8b0000; }
|
background: rgba(68, 68, 68, .15);
|
||||||
.trend-dislike { background: rgba(205, 92, 92, .15); color: #cd5c5c; }
|
color: #444;
|
||||||
.trend-stranger { background: rgba(136, 136, 136, .15); color: #888; }
|
}
|
||||||
.trend-click { background: rgba(102, 205, 170, .15); color: #4a9a7e; }
|
|
||||||
.trend-close { background: rgba(235, 106, 106, .15); color: var(--hl); }
|
.trend-hate {
|
||||||
.trend-merge { background: rgba(199, 21, 133, .2); color: #c71585; }
|
background: rgba(139, 0, 0, .15);
|
||||||
|
color: #8b0000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.trend-dislike {
|
||||||
|
background: rgba(205, 92, 92, .15);
|
||||||
|
color: #cd5c5c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.trend-stranger {
|
||||||
|
background: rgba(136, 136, 136, .15);
|
||||||
|
color: #888;
|
||||||
|
}
|
||||||
|
|
||||||
|
.trend-click {
|
||||||
|
background: rgba(102, 205, 170, .15);
|
||||||
|
color: #4a9a7e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.trend-close {
|
||||||
|
background: rgba(235, 106, 106, .15);
|
||||||
|
color: var(--hl);
|
||||||
|
}
|
||||||
|
|
||||||
|
.trend-merge {
|
||||||
|
background: rgba(199, 21, 133, .2);
|
||||||
|
color: #c71585;
|
||||||
|
}
|
||||||
|
|
||||||
/* ═══════════════════════════════════════════════════════════════════════════
|
/* ═══════════════════════════════════════════════════════════════════════════
|
||||||
Custom Select
|
Custom Select
|
||||||
@@ -787,8 +809,15 @@ h1 span {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@keyframes fadeIn {
|
@keyframes fadeIn {
|
||||||
from { opacity: 0; transform: translateY(-4px); }
|
from {
|
||||||
to { opacity: 1; transform: translateY(0); }
|
opacity: 0;
|
||||||
|
transform: translateY(-4px);
|
||||||
|
}
|
||||||
|
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ═══════════════════════════════════════════════════════════════════════════
|
/* ═══════════════════════════════════════════════════════════════════════════
|
||||||
@@ -1041,8 +1070,10 @@ h1 span {
|
|||||||
letter-spacing: .05em;
|
letter-spacing: .05em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.settings-field input,
|
.settings-field input:not([type="checkbox"]):not([type="radio"]),
|
||||||
.settings-field select {
|
.settings-field select {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 100%;
|
||||||
padding: 10px 14px;
|
padding: 10px 14px;
|
||||||
background: var(--bg3);
|
background: var(--bg3);
|
||||||
border: 1px solid var(--bdr);
|
border: 1px solid var(--bdr);
|
||||||
@@ -1050,6 +1081,22 @@ h1 span {
|
|||||||
color: var(--txt);
|
color: var(--txt);
|
||||||
outline: none;
|
outline: none;
|
||||||
transition: border-color .2s;
|
transition: border-color .2s;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-field input[type="checkbox"],
|
||||||
|
.settings-field input[type="radio"] {
|
||||||
|
width: auto;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-field select {
|
||||||
|
appearance: none;
|
||||||
|
-webkit-appearance: none;
|
||||||
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23666' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='3 4.5 6 7.5 9 4.5'%3E%3C/polyline%3E%3C/svg%3E");
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: right 12px center;
|
||||||
|
padding-right: 32px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.settings-field input:focus,
|
.settings-field input:focus,
|
||||||
@@ -1113,6 +1160,10 @@ h1 span {
|
|||||||
|
|
||||||
.engine-option input {
|
.engine-option input {
|
||||||
accent-color: var(--hl);
|
accent-color: var(--hl);
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
margin: 0;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.engine-area {
|
.engine-area {
|
||||||
@@ -1132,19 +1183,35 @@ h1 span {
|
|||||||
margin-bottom: 4px;
|
margin-bottom: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.engine-card-desc {
|
.engine-status-row {
|
||||||
font-size: .75rem;
|
display: flex;
|
||||||
color: var(--txt3);
|
justify-content: space-between;
|
||||||
margin-bottom: 12px;
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
margin-top: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.engine-status {
|
.engine-status {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
|
||||||
gap: 6px;
|
gap: 6px;
|
||||||
font-size: .8125rem;
|
font-size: .8125rem;
|
||||||
margin-bottom: 12px;
|
color: var(--txt3);
|
||||||
|
flex: 1;
|
||||||
|
/* 占 1/3 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.engine-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
justify-content: flex-end;
|
||||||
|
flex: 2;
|
||||||
|
/* 占 2/3 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 针对在线测试连接按钮的特殊处理 */
|
||||||
|
#btn-test-vector-api {
|
||||||
|
flex: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-dot {
|
.status-dot {
|
||||||
@@ -1154,15 +1221,37 @@ h1 span {
|
|||||||
background: var(--txt3);
|
background: var(--txt3);
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-dot.ready { background: #22c55e; }
|
.status-dot.ready {
|
||||||
.status-dot.cached { background: #3b82f6; }
|
background: #22c55e;
|
||||||
.status-dot.downloading { background: #f59e0b; animation: pulse 1s infinite; }
|
}
|
||||||
.status-dot.error { background: #ef4444; }
|
|
||||||
.status-dot.success { background: #22c55e; }
|
.status-dot.cached {
|
||||||
|
background: #3b82f6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-dot.downloading {
|
||||||
|
background: #f59e0b;
|
||||||
|
animation: pulse 1s infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-dot.error {
|
||||||
|
background: #ef4444;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-dot.success {
|
||||||
|
background: #22c55e;
|
||||||
|
}
|
||||||
|
|
||||||
@keyframes pulse {
|
@keyframes pulse {
|
||||||
0%, 100% { opacity: 1; }
|
|
||||||
50% { opacity: .5; }
|
0%,
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
50% {
|
||||||
|
opacity: .5;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.engine-progress {
|
.engine-progress {
|
||||||
@@ -1195,8 +1284,8 @@ h1 span {
|
|||||||
.engine-actions {
|
.engine-actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
justify-content: center;
|
justify-content: flex-end;
|
||||||
flex-wrap: wrap;
|
flex: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.model-select-row {
|
.model-select-row {
|
||||||
@@ -1218,8 +1307,8 @@ h1 span {
|
|||||||
.model-desc {
|
.model-desc {
|
||||||
font-size: .75rem;
|
font-size: .75rem;
|
||||||
color: var(--txt3);
|
color: var(--txt3);
|
||||||
text-align: center;
|
text-align: left;
|
||||||
margin-bottom: 12px;
|
margin-bottom: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.vector-stats {
|
.vector-stats {
|
||||||
@@ -1343,8 +1432,8 @@ h1 span {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.hf-intro {
|
.hf-intro {
|
||||||
background: linear-gradient(135deg, rgba(102,126,234,.08), rgba(118,75,162,.08));
|
background: linear-gradient(135deg, rgba(102, 126, 234, .08), rgba(118, 75, 162, .08));
|
||||||
border: 1px solid rgba(102,126,234,.2);
|
border: 1px solid rgba(102, 126, 234, .2);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@@ -1495,8 +1584,8 @@ h1 span {
|
|||||||
right: 8px;
|
right: 8px;
|
||||||
top: 8px;
|
top: 8px;
|
||||||
padding: 4px 10px;
|
padding: 4px 10px;
|
||||||
background: rgba(255,255,255,.1);
|
background: rgba(255, 255, 255, .1);
|
||||||
border: 1px solid rgba(255,255,255,.2);
|
border: 1px solid rgba(255, 255, 255, .2);
|
||||||
color: #999;
|
color: #999;
|
||||||
font-size: .6875rem;
|
font-size: .6875rem;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
@@ -1505,14 +1594,14 @@ h1 span {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.hf-code .copy-btn:hover {
|
.hf-code .copy-btn:hover {
|
||||||
background: rgba(255,255,255,.2);
|
background: rgba(255, 255, 255, .2);
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hf-status-badge {
|
.hf-status-badge {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding: 2px 10px;
|
padding: 2px 10px;
|
||||||
background: rgba(34,197,94,.15);
|
background: rgba(34, 197, 94, .15);
|
||||||
color: #22c55e;
|
color: #22c55e;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
font-size: .75rem;
|
font-size: .75rem;
|
||||||
@@ -1684,8 +1773,7 @@ h1 span {
|
|||||||
|
|
||||||
.btn-group {
|
.btn-group {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: grid;
|
display: flex;
|
||||||
grid-template-columns: repeat(4, 1fr);
|
|
||||||
gap: 6px;
|
gap: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1714,7 +1802,8 @@ h1 span {
|
|||||||
gap: 16px;
|
gap: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.left, .right {
|
.left,
|
||||||
|
.right {
|
||||||
gap: 16px;
|
gap: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1882,7 +1971,9 @@ h1 span {
|
|||||||
font-size: .6875rem;
|
font-size: .6875rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
main, .left, .right {
|
main,
|
||||||
|
.left,
|
||||||
|
.right {
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2177,3 +2268,412 @@ h1 span {
|
|||||||
padding: 6px 8px;
|
padding: 6px 8px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
New Settings Styles
|
||||||
|
═══════════════════════════════════════════════════════════════════════════ */
|
||||||
|
|
||||||
|
.settings-modal-box {
|
||||||
|
max-width: 680px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Collapsible Section */
|
||||||
|
.settings-collapse {
|
||||||
|
margin-top: 20px;
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-collapse-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 12px 16px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: .8125rem;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--txt2);
|
||||||
|
transition: all .2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-collapse-header:hover {
|
||||||
|
background: var(--bdr);
|
||||||
|
}
|
||||||
|
|
||||||
|
.collapse-icon {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
transition: transform .2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-collapse.open .collapse-icon {
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-collapse-content {
|
||||||
|
padding: 16px;
|
||||||
|
border-top: 1px solid var(--bdr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Checkbox Group */
|
||||||
|
.settings-checkbox-group {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
padding: 0;
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-checkbox-group:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-checkbox {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-checkbox input[type="checkbox"] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-mark {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
border: 2px solid var(--bdr);
|
||||||
|
border-radius: 4px;
|
||||||
|
background: var(--bg2);
|
||||||
|
position: relative;
|
||||||
|
transition: all .2s;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-checkbox input:checked+.checkbox-mark {
|
||||||
|
background: var(--acc);
|
||||||
|
border-color: var(--acc);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-checkbox input:checked+.checkbox-mark::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
left: 6px;
|
||||||
|
top: 2px;
|
||||||
|
width: 5px;
|
||||||
|
height: 10px;
|
||||||
|
border: solid #fff;
|
||||||
|
border-width: 0 2px 2px 0;
|
||||||
|
transform: rotate(45deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-label {
|
||||||
|
font-size: .875rem;
|
||||||
|
color: var(--txt);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-checkbox-group .settings-hint {
|
||||||
|
margin-left: 30px;
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sub Options */
|
||||||
|
.settings-sub-options {
|
||||||
|
margin-top: 12px;
|
||||||
|
padding-top: 12px;
|
||||||
|
border-top: 1px dashed var(--bdr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Filter Rules */
|
||||||
|
.filter-rules-section {
|
||||||
|
margin-top: 20px;
|
||||||
|
padding: 16px;
|
||||||
|
background: var(--bg3);
|
||||||
|
border: 1px solid var(--bdr);
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-rules-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-rules-header label {
|
||||||
|
font-size: .75rem;
|
||||||
|
color: var(--txt3);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: .05em;
|
||||||
|
font-weight: 600;
|
||||||
|
flex: 1;
|
||||||
|
/* 1/3 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-add {
|
||||||
|
flex: 2;
|
||||||
|
/* 2/3 */
|
||||||
|
justify-content: center;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
padding: 6px 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-rules-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
margin-top: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-rule-item {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
align-items: flex-start;
|
||||||
|
padding: 10px 12px;
|
||||||
|
background: var(--bg2);
|
||||||
|
border: 1px solid var(--bdr2);
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-rule-inputs {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-rule-item input {
|
||||||
|
width: 100%;
|
||||||
|
padding: 8px 10px;
|
||||||
|
background: var(--bg3);
|
||||||
|
border: 1px solid var(--bdr);
|
||||||
|
font-size: .8125rem;
|
||||||
|
color: var(--txt);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-rule-item input:focus {
|
||||||
|
border-color: var(--acc);
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-rule-item .rule-arrow {
|
||||||
|
color: var(--txt3);
|
||||||
|
font-size: .875rem;
|
||||||
|
flex-shrink: 0;
|
||||||
|
padding: 2px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-rule-item .btn-del-rule {
|
||||||
|
padding: 6px 10px;
|
||||||
|
background: transparent;
|
||||||
|
border: 1px solid var(--hl);
|
||||||
|
color: var(--hl);
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: .75rem;
|
||||||
|
transition: all .2s;
|
||||||
|
flex-shrink: 0;
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-rule-item .btn-del-rule:hover {
|
||||||
|
background: var(--hl-soft);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Vector Stats - Original horizontal layout */
|
||||||
|
.vector-stats {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 16px;
|
||||||
|
font-size: .875rem;
|
||||||
|
color: var(--txt2);
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vector-stat-col {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vector-stat-label {
|
||||||
|
font-size: .75rem;
|
||||||
|
color: var(--txt3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vector-stat-value {
|
||||||
|
color: var(--txt2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vector-stat-value strong {
|
||||||
|
color: var(--hl);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vector-stat-sep {
|
||||||
|
color: var(--txt3);
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vector-io-section {
|
||||||
|
border-top: 1px solid var(--bdr);
|
||||||
|
padding-top: 16px;
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mobile Settings Responsive */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.settings-modal-box {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-collapse-header {
|
||||||
|
padding: 14px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-checkbox-group {
|
||||||
|
padding: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-label {
|
||||||
|
font-size: .8125rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vector-stats {
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vector-stat-sep {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vector-stat-col {
|
||||||
|
flex-direction: row;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-field {
|
||||||
|
min-width: 100px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
.settings-checkbox-group {
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-mark {
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-checkbox input:checked+.checkbox-mark::after {
|
||||||
|
left: 5px;
|
||||||
|
top: 1px;
|
||||||
|
width: 4px;
|
||||||
|
height: 9px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-rules-section {
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-rule-item {
|
||||||
|
padding: 8px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-rule-item .btn-del-rule {
|
||||||
|
padding: 4px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-sub-options .settings-row {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Settings Tabs */
|
||||||
|
.settings-tabs {
|
||||||
|
display: flex;
|
||||||
|
gap: 24px;
|
||||||
|
align-self: flex-end;
|
||||||
|
/* 使底部边框与 header 底部对齐 */
|
||||||
|
margin-bottom: -20px;
|
||||||
|
/* 抵消 modal-head 的 padding,让边框贴合底部 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-tab {
|
||||||
|
font-size: .875rem;
|
||||||
|
color: var(--txt3);
|
||||||
|
cursor: pointer;
|
||||||
|
padding-bottom: 20px;
|
||||||
|
/* 增加内边距使点击区域更大且贴合底部 */
|
||||||
|
border-bottom: 2px solid transparent;
|
||||||
|
transition: all .2s;
|
||||||
|
user-select: none;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: .1em;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-tab:hover {
|
||||||
|
color: var(--txt);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-tab.active {
|
||||||
|
color: var(--hl);
|
||||||
|
border-bottom-color: var(--hl);
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-pane {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-pane.active {
|
||||||
|
display: block;
|
||||||
|
animation: fadeIn .3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.debug-log-header {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
padding-bottom: 8px;
|
||||||
|
border-bottom: 1px dashed var(--bdr2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.debug-title {
|
||||||
|
font-size: .875rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--txt);
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.debug-log-viewer {
|
||||||
|
width: 100%;
|
||||||
|
height: 400px;
|
||||||
|
background: var(--bg3);
|
||||||
|
border: 1px solid var(--bdr);
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 12px;
|
||||||
|
font-family: 'SF Mono', Monaco, Consolas, 'Courier New', monospace;
|
||||||
|
font-size: 11px;
|
||||||
|
line-height: 1.5;
|
||||||
|
color: var(--txt2);
|
||||||
|
overflow-y: auto;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recall-empty {
|
||||||
|
color: var(--txt3);
|
||||||
|
text-align: center;
|
||||||
|
padding: 40px;
|
||||||
|
font-style: italic;
|
||||||
|
font-size: .8125rem;
|
||||||
|
line-height: 1.8;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<!-- story-summary.html -->
|
<!-- story-summary.html -->
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="zh-CN">
|
<html lang="zh-CN">
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||||
@@ -8,6 +9,7 @@
|
|||||||
<title>剧情总结 · Story Summary</title>
|
<title>剧情总结 · Story Summary</title>
|
||||||
<link rel="stylesheet" href="story-summary.css">
|
<link rel="stylesheet" href="story-summary.css">
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<!-- Header -->
|
<!-- Header -->
|
||||||
@@ -37,18 +39,19 @@
|
|||||||
<div class="controls">
|
<div class="controls">
|
||||||
<label class="chk-label">
|
<label class="chk-label">
|
||||||
<input type="checkbox" id="hide-summarized">
|
<input type="checkbox" id="hide-summarized">
|
||||||
<span>聊天时隐藏已总结 · <strong id="summarized-count">0</strong> 楼(保留<input type="number" id="keep-visible-count" min="0" max="50" value="3">楼)</span>
|
<span>隐藏已总结 · <strong id="summarized-count">0</strong> 楼(保留<input type="number" id="keep-visible-count"
|
||||||
|
min="0" max="50" value="3">楼)</span>
|
||||||
</label>
|
</label>
|
||||||
<span class="spacer"></span>
|
<span class="spacer"></span>
|
||||||
<div class="btn-group">
|
<div class="btn-group">
|
||||||
<button class="btn btn-icon" id="btn-settings">
|
<button class="btn btn-icon" id="btn-settings">
|
||||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
<circle cx="12" cy="12" r="3"/>
|
<circle cx="12" cy="12" r="3" />
|
||||||
<path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/>
|
<path
|
||||||
|
d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z" />
|
||||||
</svg>
|
</svg>
|
||||||
<span>设置</span>
|
<span>设置</span>
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-recall" id="btn-recall">涌现</button>
|
|
||||||
<button class="btn" id="btn-clear">清空</button>
|
<button class="btn" id="btn-clear">清空</button>
|
||||||
<button class="btn btn-p" id="btn-generate">总结</button>
|
<button class="btn btn-p" id="btn-generate">总结</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -92,8 +95,10 @@
|
|||||||
<div class="sec-title">人物关系</div>
|
<div class="sec-title">人物关系</div>
|
||||||
<div class="sec-actions">
|
<div class="sec-actions">
|
||||||
<button class="sec-btn sec-icon" id="btn-fullscreen-relations" title="全屏查看">
|
<button class="sec-btn sec-icon" id="btn-fullscreen-relations" title="全屏查看">
|
||||||
<svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2">
|
<svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor"
|
||||||
<path d="M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3"/>
|
stroke-width="2">
|
||||||
|
<path
|
||||||
|
d="M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3" />
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
<button class="sec-btn" data-section="characters">编辑</button>
|
<button class="sec-btn" data-section="characters">编辑</button>
|
||||||
@@ -132,7 +137,8 @@
|
|||||||
<h2 id="editor-title">编辑</h2>
|
<h2 id="editor-title">编辑</h2>
|
||||||
<button class="modal-close" id="editor-close">
|
<button class="modal-close" id="editor-close">
|
||||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
<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"/>
|
<line x1="18" y1="6" x2="6" y2="18" />
|
||||||
|
<line x1="6" y1="6" x2="18" y2="18" />
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -152,17 +158,24 @@
|
|||||||
<!-- Settings Modal -->
|
<!-- Settings Modal -->
|
||||||
<div class="modal" id="settings-modal">
|
<div class="modal" id="settings-modal">
|
||||||
<div class="modal-bg" id="settings-backdrop"></div>
|
<div class="modal-bg" id="settings-backdrop"></div>
|
||||||
<div class="modal-box">
|
<div class="modal-box settings-modal-box">
|
||||||
<div class="modal-head">
|
<div class="modal-head">
|
||||||
<h2>设置</h2>
|
<div class="settings-tabs">
|
||||||
|
<div class="settings-tab active" data-tab="tab-summary">总结设置</div>
|
||||||
|
<div class="settings-tab" data-tab="tab-vector">向量设置</div>
|
||||||
|
<div class="settings-tab" data-tab="tab-debug">调试</div>
|
||||||
|
</div>
|
||||||
<button class="modal-close" id="settings-close">
|
<button class="modal-close" id="settings-close">
|
||||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
<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"/>
|
<line x1="18" y1="6" x2="6" y2="18" />
|
||||||
|
<line x1="6" y1="6" x2="18" y2="18" />
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<!-- API Config -->
|
<!-- Tab 1: Summary Settings -->
|
||||||
|
<div class="tab-pane active" id="tab-summary">
|
||||||
|
<!-- API Config & Gen Params Combined -->
|
||||||
<div class="settings-section">
|
<div class="settings-section">
|
||||||
<div class="settings-section-title">API 配置</div>
|
<div class="settings-section-title">API 配置</div>
|
||||||
<div class="settings-row">
|
<div class="settings-row">
|
||||||
@@ -181,7 +194,7 @@
|
|||||||
<div class="settings-field full">
|
<div class="settings-field full">
|
||||||
<label>API URL</label>
|
<label>API URL</label>
|
||||||
<input type="text" id="api-url" placeholder="https://api.openai.com 或代理地址">
|
<input type="text" id="api-url" placeholder="https://api.openai.com 或代理地址">
|
||||||
<div class="settings-hint">不同渠道默认端点:OpenAI 用 /v1,Gemini 用 /v1beta,Claude 用 /v1</div>
|
<div class="settings-hint">默认端点:OpenAI:/v1,Gemini:/v1beta,Claude:/v1</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="settings-row hidden" id="api-key-row">
|
<div class="settings-row hidden" id="api-key-row">
|
||||||
@@ -204,22 +217,36 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="settings-btn-row hidden" id="api-connect-row">
|
<div class="settings-btn-row hidden" id="api-connect-row"
|
||||||
<button class="btn btn-sm btn-p" id="btn-connect">连接 / 拉取模型列表</button>
|
style="display: flex; gap: 12px; align-items: center; justify-content: space-between;">
|
||||||
</div>
|
<button class="btn btn-sm btn-p" id="btn-connect" style="flex: 4;">连接 / 拉取模型列表</button>
|
||||||
|
<label class="chk-label compact"
|
||||||
|
style="margin: 0; flex: 1; display: flex; align-items: center; gap: 6px; white-space: nowrap; justify-content: center;">
|
||||||
|
<input type="checkbox" id="trigger-stream" checked>
|
||||||
|
<span>流式</span>
|
||||||
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Gen Params -->
|
<!-- Collapsible Gen Params -->
|
||||||
<div class="settings-section">
|
<div class="settings-collapse">
|
||||||
<div class="settings-section-title">生成参数</div>
|
<div class="settings-collapse-header" id="gen-params-toggle">
|
||||||
|
<span>生成参数</span>
|
||||||
|
<svg class="collapse-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
||||||
|
stroke-width="2">
|
||||||
|
<polyline points="6 9 12 15 18 9"></polyline>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div class="settings-collapse-content hidden" id="gen-params-content">
|
||||||
<div class="settings-row">
|
<div class="settings-row">
|
||||||
<div class="settings-field">
|
<div class="settings-field">
|
||||||
<label>Temperature</label>
|
<label>Temperature</label>
|
||||||
<input type="number" id="gen-temp" step="0.01" min="0" max="2" placeholder="未设置">
|
<input type="number" id="gen-temp" step="0.01" min="0" max="2"
|
||||||
|
placeholder="未设置">
|
||||||
</div>
|
</div>
|
||||||
<div class="settings-field">
|
<div class="settings-field">
|
||||||
<label>Top P</label>
|
<label>Top P</label>
|
||||||
<input type="number" id="gen-top-p" step="0.01" min="0" max="1" placeholder="未设置">
|
<input type="number" id="gen-top-p" step="0.01" min="0" max="1"
|
||||||
|
placeholder="未设置">
|
||||||
</div>
|
</div>
|
||||||
<div class="settings-field">
|
<div class="settings-field">
|
||||||
<label>Top K</label>
|
<label>Top K</label>
|
||||||
@@ -229,11 +256,15 @@
|
|||||||
<div class="settings-row">
|
<div class="settings-row">
|
||||||
<div class="settings-field">
|
<div class="settings-field">
|
||||||
<label>存在惩罚</label>
|
<label>存在惩罚</label>
|
||||||
<input type="number" id="gen-presence" step="0.01" min="-2" max="2" placeholder="未设置">
|
<input type="number" id="gen-presence" step="0.01" min="-2" max="2"
|
||||||
|
placeholder="未设置">
|
||||||
</div>
|
</div>
|
||||||
<div class="settings-field">
|
<div class="settings-field">
|
||||||
<label>频率惩罚</label>
|
<label>频率惩罚</label>
|
||||||
<input type="number" id="gen-frequency" step="0.01" min="-2" max="2" placeholder="未设置">
|
<input type="number" id="gen-frequency" step="0.01" min="-2" max="2"
|
||||||
|
placeholder="未设置">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -250,7 +281,25 @@
|
|||||||
<option value="assistant">Assistant</option>
|
<option value="assistant">Assistant</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="settings-field">
|
||||||
|
<label>单次最大总结(楼)</label>
|
||||||
|
<select id="trigger-max-per-run">
|
||||||
|
<option value="50">50</option>
|
||||||
|
<option value="100" selected>100</option>
|
||||||
|
<option value="150">150</option>
|
||||||
|
<option value="200">200</option>
|
||||||
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Auto Summary with sub-options -->
|
||||||
|
<div class="settings-checkbox-group">
|
||||||
|
<label class="settings-checkbox">
|
||||||
|
<input type="checkbox" id="trigger-enabled">
|
||||||
|
<span class="checkbox-mark"></span>
|
||||||
|
<span class="checkbox-label">启用自动总结</span>
|
||||||
|
</label>
|
||||||
|
<div class="settings-sub-options hidden" id="auto-summary-options">
|
||||||
<div class="settings-row">
|
<div class="settings-row">
|
||||||
<div class="settings-field">
|
<div class="settings-field">
|
||||||
<label>自动总结间隔(楼)</label>
|
<label>自动总结间隔(楼)</label>
|
||||||
@@ -264,53 +313,45 @@
|
|||||||
<option value="manual">仅手动</option>
|
<option value="manual">仅手动</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="settings-field">
|
|
||||||
<label>单次最大总结(楼)</label>
|
|
||||||
<select id="trigger-max-per-run">
|
|
||||||
<option value="50">50</option>
|
|
||||||
<option value="100" selected>100</option>
|
|
||||||
<option value="150">150</option>
|
|
||||||
<option value="200">200</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="settings-row">
|
|
||||||
<div class="settings-field full">
|
|
||||||
<label>头部包裹词(应对NoAss配置)</label>
|
|
||||||
<input type="text" id="trigger-wrapper-head" placeholder="添加到开头">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="settings-row">
|
|
||||||
<div class="settings-field full">
|
|
||||||
<label>尾部包裹词(应对NoAss配置)</label>
|
|
||||||
<input type="text" id="trigger-wrapper-tail" placeholder="添加到结尾">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="settings-row">
|
|
||||||
<div class="settings-field-inline">
|
|
||||||
<input type="checkbox" id="trigger-enabled">
|
|
||||||
<label for="trigger-enabled">启用自动总结</label>
|
|
||||||
</div>
|
|
||||||
<div class="settings-field-inline">
|
|
||||||
<input type="checkbox" id="trigger-stream" checked>
|
|
||||||
<label for="trigger-stream">启用流式生成</label>
|
|
||||||
</div>
|
|
||||||
<div class="settings-field-inline">
|
|
||||||
<input type="checkbox" id="trigger-insert-at-end">
|
|
||||||
<label for="trigger-insert-at-end">强制插入到聊天最后</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="settings-hint" style="margin-top:8px">若 API 不支持非流式请求,请勾选"启用流式生成"</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Vector Settings -->
|
<!-- Force Insert with wrapper options -->
|
||||||
|
<div class="settings-checkbox-group">
|
||||||
|
<label class="settings-checkbox">
|
||||||
|
<input type="checkbox" id="trigger-insert-at-end">
|
||||||
|
<span class="checkbox-mark"></span>
|
||||||
|
<span class="checkbox-label">强制插入到聊天最后(插件冲突用)</span>
|
||||||
|
</label>
|
||||||
|
<div class="settings-sub-options hidden" id="insert-wrapper-options">
|
||||||
|
<div class="settings-row">
|
||||||
|
<div class="settings-field full">
|
||||||
|
<label>头部包裹词</label>
|
||||||
|
<input type="text" id="trigger-wrapper-head" placeholder="添加到开头(应对NoAss配置)">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="settings-row">
|
||||||
|
<div class="settings-field full">
|
||||||
|
<label>尾部包裹词</label>
|
||||||
|
<input type="text" id="trigger-wrapper-tail" placeholder="添加到结尾(应对NoAss配置)">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Tab 2: Vector Settings -->
|
||||||
|
<div class="tab-pane" id="tab-vector">
|
||||||
<div class="settings-section">
|
<div class="settings-section">
|
||||||
<div class="settings-section-title">智能记忆(向量检索)</div>
|
<div class="settings-section-title">智能记忆(向量检索)</div>
|
||||||
<div class="settings-row">
|
<div class="settings-checkbox-group">
|
||||||
<div class="settings-field-inline">
|
<label class="settings-checkbox">
|
||||||
<input type="checkbox" id="vector-enabled">
|
<input type="checkbox" id="vector-enabled">
|
||||||
<label for="vector-enabled">启用向量检索</label>
|
<span class="checkbox-mark"></span>
|
||||||
</div>
|
<span class="checkbox-label">启用向量检索</span>
|
||||||
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div id="vector-config-area" class="hidden">
|
<div id="vector-config-area" class="hidden">
|
||||||
<div class="settings-row" style="margin-top:16px">
|
<div class="settings-row" style="margin-top:16px">
|
||||||
@@ -339,18 +380,24 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="model-desc" id="local-model-desc">手机/低配适用</div>
|
<div class="model-desc" id="local-model-desc">手机/低配适用</div>
|
||||||
|
<div class="engine-status-row">
|
||||||
<div class="engine-status" id="local-model-status">
|
<div class="engine-status" id="local-model-status">
|
||||||
<span class="status-dot"></span>
|
<span class="status-dot"></span>
|
||||||
<span class="status-text">检查中...</span>
|
<span class="status-text">检查中...</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="engine-progress hidden" id="local-model-progress">
|
|
||||||
<div class="progress-bar"><div class="progress-inner"></div></div>
|
|
||||||
<span class="progress-text">0%</span>
|
|
||||||
</div>
|
|
||||||
<div class="engine-actions" id="local-model-actions">
|
<div class="engine-actions" id="local-model-actions">
|
||||||
<button class="btn btn-sm btn-p" id="btn-download-model">下载模型</button>
|
<button class="btn btn-sm btn-p" id="btn-download-model">下载</button>
|
||||||
<button class="btn btn-sm" id="btn-cancel-download" style="display:none">取消下载</button>
|
<button class="btn btn-sm" id="btn-cancel-download"
|
||||||
<button class="btn btn-sm btn-del" id="btn-delete-model" style="display:none">删除缓存</button>
|
style="display:none">取消</button>
|
||||||
|
<button class="btn btn-sm btn-del" id="btn-delete-model"
|
||||||
|
style="display:none">删除</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="engine-progress hidden" id="local-model-progress" style="margin-top: 8px;">
|
||||||
|
<div class="progress-bar">
|
||||||
|
<div class="progress-inner"></div>
|
||||||
|
</div>
|
||||||
|
<span class="progress-text">0%</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -385,32 +432,39 @@
|
|||||||
<select id="vector-model-select" style="flex:1">
|
<select id="vector-model-select" style="flex:1">
|
||||||
<option value="">请选择模型</option>
|
<option value="">请选择模型</option>
|
||||||
</select>
|
</select>
|
||||||
<button class="btn btn-sm" id="btn-fetch-models" style="display:none">拉取</button>
|
<button class="btn btn-sm" id="btn-fetch-models"
|
||||||
|
style="display:none">拉取</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="engine-status-row">
|
||||||
<div class="engine-status" id="online-api-status">
|
<div class="engine-status" id="online-api-status">
|
||||||
<span class="status-dot"></span>
|
<span class="status-dot"></span>
|
||||||
<span class="status-text">未测试</span>
|
<span class="status-text">未测试</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="settings-btn-row" style="justify-content:center">
|
|
||||||
<button class="btn btn-sm" id="btn-test-vector-api">测试连接</button>
|
<button class="btn btn-sm" id="btn-test-vector-api">测试连接</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="provider-hint" id="provider-hint">
|
<div class="provider-hint" id="provider-hint">
|
||||||
💡 <a href="https://siliconflow.cn" target="_blank">硅基流动</a> 免费、速度快、质量好,推荐 BAAI/bge-m3
|
💡 <a href="https://siliconflow.cn" target="_blank">硅基流动</a> 免费、速度快、质量好,推荐
|
||||||
|
BAAI/bge-m3
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 文本过滤规则 -->
|
<!-- 文本过滤规则 - Redesigned for mobile -->
|
||||||
<div class="settings-row" style="margin-top:16px">
|
<div class="filter-rules-section">
|
||||||
<div class="settings-field full">
|
<div class="filter-rules-header">
|
||||||
<label>文本过滤规则</label>
|
<label>文本过滤规则</label>
|
||||||
<p class="settings-hint" style="margin-bottom:8px">
|
<button class="btn btn-sm btn-add" id="btn-add-filter-rule">
|
||||||
遇到「起始」后跳过,直到「结束」。起始或结束可单独留空。用于过滤思考标签等干扰内容。
|
<svg viewBox="0 0 24 24" width="14" height="14" fill="none"
|
||||||
</p>
|
stroke="currentColor" stroke-width="2">
|
||||||
<div id="filter-rules-list" style="display:flex;flex-direction:column;gap:6px"></div>
|
<line x1="12" y1="5" x2="12" y2="19"></line>
|
||||||
<button class="btn btn-sm" id="btn-add-filter-rule" style="margin-top:8px">+ 添加规则</button>
|
<line x1="5" y1="12" x2="19" y2="12"></line>
|
||||||
|
</svg>
|
||||||
|
添加
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
<p class="settings-hint">过滤干扰内容(如思考标签):遇到「起始」跳过直到「结束」</p>
|
||||||
|
<div id="filter-rules-list" class="filter-rules-list"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Vector Stats -->
|
<!-- Vector Stats -->
|
||||||
@@ -419,11 +473,26 @@
|
|||||||
<div class="settings-field full">
|
<div class="settings-field full">
|
||||||
<label>当前聊天向量</label>
|
<label>当前聊天向量</label>
|
||||||
<div class="vector-stats" id="vector-stats">
|
<div class="vector-stats" id="vector-stats">
|
||||||
<span>事件向量: <strong id="vector-event-count">0</strong>/<strong id="vector-event-total">0</strong></span>
|
<div class="vector-stat-col">
|
||||||
<span>·</span>
|
<span class="vector-stat-label">事件向量:</span>
|
||||||
<span>Chunks: <strong id="vector-chunk-count">0</strong> 个(<span id="vector-chunk-floors">0</span>/<span id="vector-chunk-total">0</span> 层)</span>
|
<span class="vector-stat-value"><strong
|
||||||
<span>·</span>
|
id="vector-event-count">0</strong>/<strong
|
||||||
<span>消息: <strong id="vector-message-count">0</strong></span>
|
id="vector-event-total">0</strong></span>
|
||||||
|
</div>
|
||||||
|
<span class="vector-stat-sep">·</span>
|
||||||
|
<div class="vector-stat-col">
|
||||||
|
<span class="vector-stat-label">Chunks:</span>
|
||||||
|
<span class="vector-stat-value"><strong
|
||||||
|
id="vector-chunk-count">0</strong>
|
||||||
|
个(<span id="vector-chunk-floors">0</span>/<span
|
||||||
|
id="vector-chunk-total">0</span> 层)</span>
|
||||||
|
</div>
|
||||||
|
<span class="vector-stat-sep">·</span>
|
||||||
|
<div class="vector-stat-col">
|
||||||
|
<span class="vector-stat-label">消息:</span>
|
||||||
|
<span class="vector-stat-value"><strong
|
||||||
|
id="vector-message-count">0</strong></span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="vector-mismatch-warning hidden" id="vector-mismatch-warning">
|
<div class="vector-mismatch-warning hidden" id="vector-mismatch-warning">
|
||||||
⚠ 引擎/模型已变更,需重新生成向量
|
⚠ 引擎/模型已变更,需重新生成向量
|
||||||
@@ -432,12 +501,16 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="engine-progress hidden" id="vector-gen-progress-l1">
|
<div class="engine-progress hidden" id="vector-gen-progress-l1">
|
||||||
<div style="font-size:.75rem;color:var(--txt3);margin-bottom:4px">L1 片段</div>
|
<div style="font-size:.75rem;color:var(--txt3);margin-bottom:4px">L1 片段</div>
|
||||||
<div class="progress-bar"><div class="progress-inner"></div></div>
|
<div class="progress-bar">
|
||||||
|
<div class="progress-inner"></div>
|
||||||
|
</div>
|
||||||
<span class="progress-text">0/0</span>
|
<span class="progress-text">0/0</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="engine-progress hidden" id="vector-gen-progress-l2">
|
<div class="engine-progress hidden" id="vector-gen-progress-l2">
|
||||||
<div style="font-size:.75rem;color:var(--txt3);margin-bottom:4px">L2 事件</div>
|
<div style="font-size:.75rem;color:var(--txt3);margin-bottom:4px">L2 事件</div>
|
||||||
<div class="progress-bar"><div class="progress-inner"></div></div>
|
<div class="progress-bar">
|
||||||
|
<div class="progress-inner"></div>
|
||||||
|
</div>
|
||||||
<span class="progress-text">0/0</span>
|
<span class="progress-text">0/0</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="settings-hint" id="vector-perf-l1"></div>
|
<div class="settings-hint" id="vector-perf-l1"></div>
|
||||||
@@ -447,14 +520,16 @@
|
|||||||
<button class="btn btn-sm btn-del" id="btn-clear-vectors">清除向量</button>
|
<button class="btn btn-sm btn-del" id="btn-clear-vectors">清除向量</button>
|
||||||
<button class="btn btn-sm hidden" id="btn-cancel-vectors">取消</button>
|
<button class="btn btn-sm hidden" id="btn-cancel-vectors">取消</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="settings-hint" style="margin-top:8px">首次生成向量可能耗时较久,页面短暂卡顿属正常。若本地模型重进酒馆后需重下。</div>
|
<div class="settings-hint" style="margin-top:8px">首次生成向量可能耗时较久,页面短暂卡顿属正常。若本地模型重进酒馆后需重下。
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- 向量导入导出 -->
|
<!-- 向量导入导出 -->
|
||||||
<div class="vector-io-section" style="border-top:1px solid var(--bdr);padding-top:16px;margin-top:16px">
|
<div class="vector-io-section">
|
||||||
<div class="settings-row">
|
<div class="settings-row">
|
||||||
<div class="settings-field full">
|
<div class="settings-field full">
|
||||||
<label>向量迁移(跨设备 / 防清缓存)</label>
|
<label>向量迁移(跨设备 / 防清缓存)</label>
|
||||||
<div class="settings-hint" style="margin-bottom:8px">导出/导入均为 zip 格式,勿解压</div>
|
<div class="settings-hint" style="margin-bottom:8px">导出/导入均为 zip 格式,勿解压
|
||||||
|
</div>
|
||||||
<div class="settings-btn-row" id="vector-io-row" style="margin-top:8px">
|
<div class="settings-btn-row" id="vector-io-row" style="margin-top:8px">
|
||||||
<button class="btn btn-sm" id="btn-export-vectors">导出向量</button>
|
<button class="btn btn-sm" id="btn-export-vectors">导出向量</button>
|
||||||
<button class="btn btn-sm" id="btn-import-vectors">导入向量</button>
|
<button class="btn btn-sm" id="btn-import-vectors">导入向量</button>
|
||||||
@@ -467,6 +542,16 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Tab 3: Debug -->
|
||||||
|
<div class="tab-pane" id="tab-debug">
|
||||||
|
<div class="debug-log-header">
|
||||||
|
<div class="debug-title">🔧 记忆召回日志</div>
|
||||||
|
<div class="settings-hint">显示最近一次 AI 生成时的向量检索详情</div>
|
||||||
|
</div>
|
||||||
|
<pre id="recall-log-content" class="debug-log-viewer"></pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="modal-foot">
|
<div class="modal-foot">
|
||||||
<button class="btn" id="settings-cancel">取消</button>
|
<button class="btn" id="settings-cancel">取消</button>
|
||||||
<button class="btn btn-p" id="settings-save">保存</button>
|
<button class="btn btn-p" id="settings-save">保存</button>
|
||||||
@@ -482,7 +567,8 @@
|
|||||||
<h2>人物关系图</h2>
|
<h2>人物关系图</h2>
|
||||||
<button class="modal-close" id="rel-fs-close">
|
<button class="modal-close" id="rel-fs-close">
|
||||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
<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"/>
|
<line x1="18" y1="6" x2="6" y2="18" />
|
||||||
|
<line x1="6" y1="6" x2="18" y2="18" />
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -500,7 +586,8 @@
|
|||||||
<h2>🤗 Hugging Face Space 部署指南</h2>
|
<h2>🤗 Hugging Face Space 部署指南</h2>
|
||||||
<button class="modal-close" id="hf-guide-close">
|
<button class="modal-close" id="hf-guide-close">
|
||||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
<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"/>
|
<line x1="18" y1="6" x2="6" y2="18" />
|
||||||
|
<line x1="6" y1="6" x2="18" y2="18" />
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -508,25 +595,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Recall Log Modal -->
|
|
||||||
<div class="modal" id="recall-log-modal">
|
|
||||||
<div class="modal-bg" id="recall-log-backdrop"></div>
|
|
||||||
<div class="modal-box" style="max-width:900px">
|
|
||||||
<div class="modal-head">
|
|
||||||
<h2>✨ 涌现 · 记忆召回日志</h2>
|
|
||||||
<button class="modal-close" id="recall-log-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" style="padding:0">
|
|
||||||
<pre id="recall-log-content"></pre>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/echarts@5/dist/echarts.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/echarts@5/dist/echarts.min.js"></script>
|
||||||
<script src="story-summary-ui.js"></script>
|
<script src="story-summary-ui.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ import {
|
|||||||
updateMeta,
|
updateMeta,
|
||||||
saveEventVectors as saveEventVectorsToDb,
|
saveEventVectors as saveEventVectorsToDb,
|
||||||
clearEventVectors,
|
clearEventVectors,
|
||||||
|
deleteEventVectorsByIds,
|
||||||
clearAllChunks,
|
clearAllChunks,
|
||||||
saveChunks,
|
saveChunks,
|
||||||
saveChunkVectors,
|
saveChunkVectors,
|
||||||
@@ -506,6 +507,91 @@ async function handleGenerateVectors(vectorCfg) {
|
|||||||
xbLog.info(MODULE_ID, `向量生成完成: L1=${l1Vectors.filter(Boolean).length}, L2=${l2VectorItems.length}`);
|
xbLog.info(MODULE_ID, `向量生成完成: L1=${l1Vectors.filter(Boolean).length}, L2=${l2VectorItems.length}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
// L2 自动增量向量化(总结完成后调用)
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
async function autoVectorizeNewEvents(newEventIds) {
|
||||||
|
if (!newEventIds?.length) return;
|
||||||
|
|
||||||
|
const vectorCfg = getVectorConfig();
|
||||||
|
if (!vectorCfg?.enabled) return;
|
||||||
|
|
||||||
|
const { chatId } = getContext();
|
||||||
|
if (!chatId) return;
|
||||||
|
|
||||||
|
// 本地模型未加载时跳过(不阻塞总结流程)
|
||||||
|
if (vectorCfg.engine === "local") {
|
||||||
|
const modelId = vectorCfg.local?.modelId || DEFAULT_LOCAL_MODEL;
|
||||||
|
if (!isLocalModelLoaded(modelId)) {
|
||||||
|
xbLog.warn(MODULE_ID, "L2 自动向量化跳过:本地模型未加载");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const store = getSummaryStore();
|
||||||
|
const events = store?.json?.events || [];
|
||||||
|
const newEventIdSet = new Set(newEventIds);
|
||||||
|
|
||||||
|
// 只取本次新增的 events
|
||||||
|
const newEvents = events.filter((e) => newEventIdSet.has(e.id));
|
||||||
|
if (!newEvents.length) return;
|
||||||
|
|
||||||
|
const pairs = newEvents
|
||||||
|
.map((e) => ({ id: e.id, text: `${e.title || ""} ${e.summary || ""}`.trim() }))
|
||||||
|
.filter((p) => p.text);
|
||||||
|
|
||||||
|
if (!pairs.length) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const fingerprint = getEngineFingerprint(vectorCfg);
|
||||||
|
const batchSize = vectorCfg.engine === "local" ? 5 : 25;
|
||||||
|
|
||||||
|
for (let i = 0; i < pairs.length; i += batchSize) {
|
||||||
|
const batch = pairs.slice(i, i + batchSize);
|
||||||
|
const texts = batch.map((p) => p.text);
|
||||||
|
|
||||||
|
const vectors = await embed(texts, vectorCfg);
|
||||||
|
const items = batch.map((p, idx) => ({
|
||||||
|
eventId: p.id,
|
||||||
|
vector: vectors[idx],
|
||||||
|
}));
|
||||||
|
|
||||||
|
await saveEventVectorsToDb(chatId, items, fingerprint);
|
||||||
|
}
|
||||||
|
|
||||||
|
xbLog.info(MODULE_ID, `L2 自动增量完成: ${pairs.length} 个事件`);
|
||||||
|
await sendVectorStatsToFrame();
|
||||||
|
} catch (e) {
|
||||||
|
xbLog.error(MODULE_ID, "L2 自动向量化失败", e);
|
||||||
|
// 不抛出,不阻塞总结流程
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
// L2 跟随编辑同步(用户编辑 events 时调用)
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
async function syncEventVectorsOnEdit(oldEvents, newEvents) {
|
||||||
|
const vectorCfg = getVectorConfig();
|
||||||
|
if (!vectorCfg?.enabled) return;
|
||||||
|
|
||||||
|
const { chatId } = getContext();
|
||||||
|
if (!chatId) return;
|
||||||
|
|
||||||
|
const oldIds = new Set((oldEvents || []).map((e) => e.id).filter(Boolean));
|
||||||
|
const newIds = new Set((newEvents || []).map((e) => e.id).filter(Boolean));
|
||||||
|
|
||||||
|
// 找出被删除的 eventIds
|
||||||
|
const deletedIds = [...oldIds].filter((id) => !newIds.has(id));
|
||||||
|
|
||||||
|
if (deletedIds.length > 0) {
|
||||||
|
await deleteEventVectorsByIds(chatId, deletedIds);
|
||||||
|
xbLog.info(MODULE_ID, `L2 同步删除: ${deletedIds.length} 个事件向量`);
|
||||||
|
await sendVectorStatsToFrame();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ═══════════════════════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
// 向量完整性检测(仅提醒,不自动操作)
|
// 向量完整性检测(仅提醒,不自动操作)
|
||||||
// ═══════════════════════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
@@ -565,6 +651,7 @@ async function handleClearVectors() {
|
|||||||
await clearAllChunks(chatId);
|
await clearAllChunks(chatId);
|
||||||
await updateMeta(chatId, { lastChunkFloor: -1 });
|
await updateMeta(chatId, { lastChunkFloor: -1 });
|
||||||
await sendVectorStatsToFrame();
|
await sendVectorStatsToFrame();
|
||||||
|
await executeSlashCommand('/echo severity=info 向量数据已清除。如需恢复召回功能,请重新点击"生成向量"。');
|
||||||
xbLog.info(MODULE_ID, "向量数据已清除");
|
xbLog.info(MODULE_ID, "向量数据已清除");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -769,6 +856,11 @@ function openPanelForMessage(mesId) {
|
|||||||
// ═══════════════════════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
async function getHideBoundaryFloor(store) {
|
async function getHideBoundaryFloor(store) {
|
||||||
|
// 没有总结时,不隐藏
|
||||||
|
if (store?.lastSummarizedMesId == null || store.lastSummarizedMesId < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
const vectorCfg = getVectorConfig();
|
const vectorCfg = getVectorConfig();
|
||||||
if (!vectorCfg?.enabled) {
|
if (!vectorCfg?.enabled) {
|
||||||
return store?.lastSummarizedMesId ?? -1;
|
return store?.lastSummarizedMesId ?? -1;
|
||||||
@@ -845,7 +937,7 @@ async function autoRunSummaryWithRetry(targetMesId, configForRun) {
|
|||||||
const result = await runSummaryGeneration(targetMesId, configForRun, {
|
const result = await runSummaryGeneration(targetMesId, configForRun, {
|
||||||
onStatus: (text) => postToFrame({ type: "SUMMARY_STATUS", statusText: text }),
|
onStatus: (text) => postToFrame({ type: "SUMMARY_STATUS", statusText: text }),
|
||||||
onError: (msg) => postToFrame({ type: "SUMMARY_ERROR", message: msg }),
|
onError: (msg) => postToFrame({ type: "SUMMARY_ERROR", message: msg }),
|
||||||
onComplete: ({ merged, endMesId }) => {
|
onComplete: async ({ merged, endMesId, newEventIds }) => {
|
||||||
postToFrame({
|
postToFrame({
|
||||||
type: "SUMMARY_FULL_DATA",
|
type: "SUMMARY_FULL_DATA",
|
||||||
payload: {
|
payload: {
|
||||||
@@ -860,6 +952,9 @@ async function autoRunSummaryWithRetry(targetMesId, configForRun) {
|
|||||||
|
|
||||||
applyHideStateDebounced();
|
applyHideStateDebounced();
|
||||||
updateFrameStatsAfterSummary(endMesId, merged);
|
updateFrameStatsAfterSummary(endMesId, merged);
|
||||||
|
|
||||||
|
// L2 自动增量向量化
|
||||||
|
await autoVectorizeNewEvents(newEventIds);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1060,11 +1155,20 @@ function handleFrameMessage(event) {
|
|||||||
const store = getSummaryStore();
|
const store = getSummaryStore();
|
||||||
if (!store) break;
|
if (!store) break;
|
||||||
store.json ||= {};
|
store.json ||= {};
|
||||||
|
|
||||||
|
// 如果是 events,先记录旧数据用于同步向量
|
||||||
|
const oldEvents = data.section === "events" ? [...(store.json.events || [])] : null;
|
||||||
|
|
||||||
if (VALID_SECTIONS.includes(data.section)) {
|
if (VALID_SECTIONS.includes(data.section)) {
|
||||||
store.json[data.section] = data.data;
|
store.json[data.section] = data.data;
|
||||||
}
|
}
|
||||||
store.updatedAt = Date.now();
|
store.updatedAt = Date.now();
|
||||||
saveSummaryStore();
|
saveSummaryStore();
|
||||||
|
|
||||||
|
// 同步 L2 向量(删除被移除的事件)
|
||||||
|
if (data.section === "events" && oldEvents) {
|
||||||
|
syncEventVectorsOnEdit(oldEvents, data.data);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1133,7 +1237,7 @@ async function handleManualGenerate(mesId, config) {
|
|||||||
await runSummaryGeneration(mesId, config, {
|
await runSummaryGeneration(mesId, config, {
|
||||||
onStatus: (text) => postToFrame({ type: "SUMMARY_STATUS", statusText: text }),
|
onStatus: (text) => postToFrame({ type: "SUMMARY_STATUS", statusText: text }),
|
||||||
onError: (msg) => postToFrame({ type: "SUMMARY_ERROR", message: msg }),
|
onError: (msg) => postToFrame({ type: "SUMMARY_ERROR", message: msg }),
|
||||||
onComplete: ({ merged, endMesId }) => {
|
onComplete: async ({ merged, endMesId, newEventIds }) => {
|
||||||
postToFrame({
|
postToFrame({
|
||||||
type: "SUMMARY_FULL_DATA",
|
type: "SUMMARY_FULL_DATA",
|
||||||
payload: {
|
payload: {
|
||||||
@@ -1148,6 +1252,9 @@ async function handleManualGenerate(mesId, config) {
|
|||||||
|
|
||||||
applyHideStateDebounced();
|
applyHideStateDebounced();
|
||||||
updateFrameStatsAfterSummary(endMesId, merged);
|
updateFrameStatsAfterSummary(endMesId, merged);
|
||||||
|
|
||||||
|
// L2 自动增量向量化
|
||||||
|
await autoVectorizeNewEvents(newEventIds);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1206,6 +1313,9 @@ async function handleMessageReceived() {
|
|||||||
|
|
||||||
initButtonsForAll();
|
initButtonsForAll();
|
||||||
|
|
||||||
|
// 向量全量生成中时跳过 L1 sync(避免竞争写入)
|
||||||
|
if (vectorGenerating) return;
|
||||||
|
|
||||||
await syncOnMessageReceived(chatId, lastFloor, message, vectorConfig);
|
await syncOnMessageReceived(chatId, lastFloor, message, vectorConfig);
|
||||||
await maybeAutoBuildChunks();
|
await maybeAutoBuildChunks();
|
||||||
|
|
||||||
@@ -1289,7 +1399,8 @@ async function handleGenerationStarted(type, _params, isDryRun) {
|
|||||||
if (boundary < 0) return;
|
if (boundary < 0) return;
|
||||||
|
|
||||||
// 2) depth:倒序插入,从末尾往前数
|
// 2) depth:倒序插入,从末尾往前数
|
||||||
const depth = chatLen - boundary - 1;
|
// 最小为 1,避免插入到最底部导致 AI 看到的最后是总结
|
||||||
|
const depth = Math.max(1, chatLen - boundary - 1);
|
||||||
if (depth < 0) return;
|
if (depth < 0) return;
|
||||||
|
|
||||||
// 3) 构建注入文本(保持原逻辑)
|
// 3) 构建注入文本(保持原逻辑)
|
||||||
|
|||||||
@@ -335,6 +335,13 @@ export async function syncOnMessageReceived(chatId, lastFloor, message, vectorCo
|
|||||||
if (!chatId || lastFloor < 0 || !message) return;
|
if (!chatId || lastFloor < 0 || !message) return;
|
||||||
if (!vectorConfig?.enabled) return;
|
if (!vectorConfig?.enabled) return;
|
||||||
|
|
||||||
|
// 本地模型未加载时跳过(避免意外触发下载或报错)
|
||||||
|
if (vectorConfig.engine === "local") {
|
||||||
|
const { isLocalModelLoaded, DEFAULT_LOCAL_MODEL } = await import("./embedder.js");
|
||||||
|
const modelId = vectorConfig.local?.modelId || DEFAULT_LOCAL_MODEL;
|
||||||
|
if (!isLocalModelLoaded(modelId)) return;
|
||||||
|
}
|
||||||
|
|
||||||
// 删除该楼层旧的
|
// 删除该楼层旧的
|
||||||
await deleteChunksAtFloor(chatId, lastFloor);
|
await deleteChunksAtFloor(chatId, lastFloor);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user