Add vector IO and text filtering

This commit is contained in:
2026-01-29 17:02:51 +08:00
parent fc23781e17
commit ee5f02fff9
10 changed files with 3368 additions and 42 deletions

View File

@@ -102,6 +102,11 @@
}
};
const DEFAULT_FILTER_RULES = [
{ start: '<think>', end: '</think>' },
{ start: '<thinking>', end: '</thinking>' },
];
// ═══════════════════════════════════════════════════════════════════════════
// State
// ═══════════════════════════════════════════════════════════════════════════
@@ -199,7 +204,7 @@
if (opt.value) modelCache.push(opt.value);
}
}
return {
const result = {
enabled: safeVal('vector-enabled', false),
engine: safeRadio('vector-engine', 'online'),
local: { modelId: safeVal('local-model-select', 'bge-small-zh') },
@@ -211,6 +216,10 @@
modelCache
}
};
// 收集过滤规则
result.textFilterRules = collectFilterRules();
return result;
}
function loadVectorConfig(cfg) {
@@ -240,6 +249,9 @@
}
if (cfg.online.model) $('vector-model-select').value = cfg.online.model;
}
// 加载过滤规则
renderFilterRules(cfg?.textFilterRules || DEFAULT_FILTER_RULES);
}
function updateLocalModelDesc(modelId) {
@@ -278,6 +290,67 @@
if (guideBtn) guideBtn.onclick = e => { e.preventDefault(); openHfGuide(); };
}
// ═══════════════════════════════════════════════════════════════════════════
// Filter Rules UI
// ═══════════════════════════════════════════════════════════════════════════
function renderFilterRules(rules) {
const list = $('filter-rules-list');
if (!list) return;
const items = rules?.length ? rules : [];
setHtml(list, items.map((r, i) => `
<div class="filter-rule-item" data-idx="${i}" style="display:flex;gap:6px;align-items:center">
<input type="text" class="filter-rule-start" placeholder="起始(可空)" value="${h(r.start || '')}" style="flex:1;padding:6px 8px;font-size:.8125rem">
<span style="color:var(--txt3)">→</span>
<input type="text" class="filter-rule-end" placeholder="结束(可空)" value="${h(r.end || '')}" style="flex:1;padding:6px 8px;font-size:.8125rem">
<button class="btn btn-sm btn-del filter-rule-del" style="padding:4px 8px">✕</button>
</div>
`).join(''));
// 绑定删除
list.querySelectorAll('.filter-rule-del').forEach(btn => {
btn.onclick = () => {
btn.closest('.filter-rule-item')?.remove();
};
});
}
function collectFilterRules() {
const list = $('filter-rules-list');
if (!list) return [];
const rules = [];
list.querySelectorAll('.filter-rule-item').forEach(item => {
const start = item.querySelector('.filter-rule-start')?.value?.trim() || '';
const end = item.querySelector('.filter-rule-end')?.value?.trim() || '';
if (start || end) {
rules.push({ start, end });
}
});
return rules;
}
function addFilterRule() {
const list = $('filter-rules-list');
if (!list) return;
const idx = list.querySelectorAll('.filter-rule-item').length;
const div = document.createElement('div');
div.className = 'filter-rule-item';
div.dataset.idx = idx;
div.style.cssText = 'display:flex;gap:6px;align-items:center';
setHtml(div, `
<input type="text" class="filter-rule-start" placeholder="起始(可空)" value="" style="flex:1;padding:6px 8px;font-size:.8125rem">
<span style="color:var(--txt3)">→</span>
<input type="text" class="filter-rule-end" placeholder="结束(可空)" value="" style="flex:1;padding:6px 8px;font-size:.8125rem">
<button class="btn btn-sm btn-del filter-rule-del" style="padding:4px 8px">✕</button>
`);
div.querySelector('.filter-rule-del').onclick = () => div.remove();
list.appendChild(div);
}
function updateLocalModelStatus(status, message) {
const dot = $('local-model-status').querySelector('.status-dot');
const text = $('local-model-status').querySelector('.status-text');
@@ -395,6 +468,10 @@
config: { url: $('vector-api-url').value.trim(), key: $('vector-api-key').value.trim(), model: $('vector-model-select').value.trim() }
});
};
// 过滤规则:添加按钮
$('btn-add-filter-rule').onclick = addFilterRule;
$('btn-gen-vectors').onclick = () => {
if (vectorGenerating) return;
postMsg('VECTOR_GENERATE', { config: getVectorConfig() });
@@ -403,6 +480,20 @@
if (confirm('确定清除当前聊天的向量数据?')) postMsg('VECTOR_CLEAR');
};
$('btn-cancel-vectors').onclick = () => postMsg('VECTOR_CANCEL_GENERATE');
// 导入导出
$('btn-export-vectors').onclick = () => {
$('btn-export-vectors').disabled = true;
$('vector-io-status').textContent = '导出中...';
postMsg('VECTOR_EXPORT');
};
$('btn-import-vectors').onclick = () => {
// 让 parent 处理文件选择,避免 iframe 传大文件
$('btn-import-vectors').disabled = true;
$('vector-io-status').textContent = '导入中...';
postMsg('VECTOR_IMPORT_PICK');
};
}
// ═══════════════════════════════════════════════════════════════════════════
// Settings Modal
@@ -1524,6 +1615,30 @@ CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860", "--workers", "
updateVectorGenProgress(d.phase, d.current, d.total);
break;
case 'VECTOR_EXPORT_RESULT':
$('btn-export-vectors').disabled = false;
if (d.success) {
$('vector-io-status').textContent = `导出成功: ${d.filename} (${(d.size / 1024 / 1024).toFixed(2)}MB)`;
} else {
$('vector-io-status').textContent = '导出失败: ' + (d.error || '未知错误');
}
break;
case 'VECTOR_IMPORT_RESULT':
$('btn-import-vectors').disabled = false;
if (d.success) {
let msg = `导入成功: ${d.chunkCount} 片段, ${d.eventCount} 事件`;
if (d.warnings?.length) {
msg += '\n⚠ ' + d.warnings.join('\n⚠ ');
}
$('vector-io-status').textContent = msg;
// 刷新统计
postMsg('REQUEST_VECTOR_STATS');
} else {
$('vector-io-status').textContent = '导入失败: ' + (d.error || '未知错误');
}
break;
case 'RECALL_LOG':
setRecallLog(d.text || '');
break;