story-summary: facts migration + recall enhancements
This commit is contained in:
@@ -60,7 +60,7 @@
|
||||
events: { title: '编辑事件时间线', hint: '编辑时,每个事件要素都应完整' },
|
||||
characters: { title: '编辑人物关系', hint: '编辑时,每个要素都应完整' },
|
||||
arcs: { title: '编辑角色弧光', hint: '编辑时,每个要素都应完整' },
|
||||
world: { title: '编辑世界状态', hint: '每行一条:category|topic|content。清除用:category|topic|(留空)或 category|topic|cleared' }
|
||||
facts: { title: '编辑事实图谱', hint: '每行一条:主体|谓词|值|趋势(可选)。删除用:主体|谓词|(留空值)' }
|
||||
};
|
||||
|
||||
const TREND_COLORS = {
|
||||
@@ -116,7 +116,7 @@
|
||||
vector: { enabled: false, engine: 'online', local: { modelId: 'bge-small-zh' }, online: { provider: 'siliconflow', url: '', key: '', model: '' } }
|
||||
};
|
||||
|
||||
let summaryData = { keywords: [], events: [], characters: { main: [], relationships: [] }, arcs: [], world: [] };
|
||||
let summaryData = { keywords: [], events: [], characters: { main: [], relationships: [] }, arcs: [], facts: [] };
|
||||
let localGenerating = false;
|
||||
let vectorGenerating = false;
|
||||
let relationChart = null;
|
||||
@@ -1415,9 +1415,14 @@ CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860", "--workers", "
|
||||
|
||||
if (section === 'keywords') {
|
||||
ta.value = summaryData.keywords.map(k => `${k.text}|${k.weight || '一般'}`).join('\n');
|
||||
} else if (section === 'world') {
|
||||
ta.value = (summaryData.world || [])
|
||||
.map(w => `${w.category || ''}|${w.topic || ''}|${w.content || ''}`)
|
||||
} else if (section === 'facts') {
|
||||
ta.value = (summaryData.facts || [])
|
||||
.filter(f => !f.retracted)
|
||||
.map(f => {
|
||||
const parts = [f.s, f.p, f.o];
|
||||
if (f.trend) parts.push(f.trend);
|
||||
return parts.join('|');
|
||||
})
|
||||
.join('\n');
|
||||
} else {
|
||||
ta.classList.add('hidden');
|
||||
@@ -1496,21 +1501,32 @@ CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860", "--workers", "
|
||||
moments
|
||||
}, oldArc);
|
||||
}).filter(a => a.name || a.trajectory || a.moments?.length);
|
||||
} else if (section === 'world') {
|
||||
const oldWorldMap = new Map((summaryData.world || []).map(w => [`${w.category}|${w.topic}`, w]));
|
||||
} else if (section === 'facts') {
|
||||
const oldMap = new Map((summaryData.facts || []).map(f => [`${f.s}::${f.p}`, f]));
|
||||
parsed = ta.value
|
||||
.split('\n')
|
||||
.map(l => l.trim())
|
||||
.filter(Boolean)
|
||||
.map(line => {
|
||||
const parts = line.split('|').map(s => s.trim());
|
||||
const category = parts[0];
|
||||
const topic = parts[1];
|
||||
const content = parts.slice(2).join('|').trim();
|
||||
if (!category || !topic) return null;
|
||||
if (!content || content.toLowerCase() === 'cleared') return null;
|
||||
const key = `${category}|${topic}`;
|
||||
return preserveAddedAt({ category, topic, content }, oldWorldMap.get(key));
|
||||
const s = parts[0];
|
||||
const p = parts[1];
|
||||
const o = parts[2];
|
||||
const trend = parts[3];
|
||||
if (!s || !p) return null;
|
||||
if (!o) return null;
|
||||
const key = `${s}::${p}`;
|
||||
const old = oldMap.get(key);
|
||||
const fact = {
|
||||
id: old?.id || `f-${Date.now()}`,
|
||||
s, p, o,
|
||||
since: old?.since ?? 0,
|
||||
_addedAt: old?._addedAt ?? 0,
|
||||
};
|
||||
if (/^对.+的/.test(p) && trend) {
|
||||
fact.trend = trend;
|
||||
}
|
||||
return fact;
|
||||
})
|
||||
.filter(Boolean);
|
||||
}
|
||||
@@ -1526,7 +1542,7 @@ CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860", "--workers", "
|
||||
else if (section === 'events') { renderTimeline(parsed); $('stat-events').textContent = parsed.length; }
|
||||
else if (section === 'characters') renderRelations(parsed);
|
||||
else if (section === 'arcs') renderArcs(parsed);
|
||||
else if (section === 'world') renderWorldState(parsed);
|
||||
else if (section === 'facts') renderFacts(parsed);
|
||||
|
||||
closeEditor();
|
||||
}
|
||||
@@ -1565,7 +1581,7 @@ CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860", "--workers", "
|
||||
if (p.events) renderTimeline(p.events);
|
||||
if (p.characters) renderRelations(p.characters);
|
||||
if (p.arcs) renderArcs(p.arcs);
|
||||
if (p.world) renderWorldState(p.world);
|
||||
if (p.facts) renderFacts(p.facts);
|
||||
$('stat-events').textContent = p.events?.length || 0;
|
||||
if (p.lastSummarizedMesId != null) $('stat-summarized').textContent = p.lastSummarizedMesId + 1;
|
||||
if (p.stats) updateStats(p.stats);
|
||||
@@ -1582,12 +1598,12 @@ CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860", "--workers", "
|
||||
$('stat-summarized').textContent = 0;
|
||||
$('stat-pending').textContent = t;
|
||||
$('summarized-count').textContent = 0;
|
||||
summaryData = { keywords: [], events: [], characters: { main: [], relationships: [] }, arcs: [], world: [] };
|
||||
summaryData = { keywords: [], events: [], characters: { main: [], relationships: [] }, arcs: [], facts: [] };
|
||||
renderKeywords([]);
|
||||
renderTimeline([]);
|
||||
renderRelations(null);
|
||||
renderArcs([]);
|
||||
renderWorldState([]);
|
||||
renderFacts([]);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1829,7 +1845,7 @@ CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860", "--workers", "
|
||||
renderKeywords([]);
|
||||
renderTimeline([]);
|
||||
renderArcs([]);
|
||||
renderWorldState([]);
|
||||
renderFacts([]);
|
||||
|
||||
bindEvents();
|
||||
|
||||
@@ -1845,51 +1861,53 @@ CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860", "--workers", "
|
||||
}
|
||||
|
||||
|
||||
function renderWorldState(world) {
|
||||
summaryData.world = world || [];
|
||||
function renderFacts(facts) {
|
||||
summaryData.facts = facts || [];
|
||||
|
||||
const container = $('world-state-list');
|
||||
if (!container) return;
|
||||
const container = $('facts-list');
|
||||
if (!container) return;
|
||||
|
||||
if (!world?.length) {
|
||||
setHtml(container, '<div class="empty">暂无世界状态</div>');
|
||||
return;
|
||||
}
|
||||
const activeFacts = (facts || []).filter(f => !f.retracted);
|
||||
|
||||
const labels = {
|
||||
status: '状态',
|
||||
inventory: '物品',
|
||||
knowledge: '认知',
|
||||
relation: '关系',
|
||||
rule: '规则'
|
||||
};
|
||||
|
||||
const categoryOrder = ['status', 'inventory', 'relation', 'knowledge', 'rule'];
|
||||
|
||||
const grouped = {};
|
||||
world.forEach(w => {
|
||||
const cat = w.category || 'other';
|
||||
if (!grouped[cat]) grouped[cat] = [];
|
||||
grouped[cat].push(w);
|
||||
});
|
||||
|
||||
const html = categoryOrder
|
||||
.filter(cat => grouped[cat]?.length)
|
||||
.map(cat => {
|
||||
const items = grouped[cat].sort((a, b) => (b.floor || 0) - (a.floor || 0));
|
||||
return `
|
||||
<div class="world-group">
|
||||
<div class="world-group-title">${labels[cat] || cat}</div>
|
||||
${items.map(w => `
|
||||
<div class="world-item">
|
||||
<span class="world-topic">${h(w.topic)}</span>
|
||||
<span class="world-content">${h(w.content)}</span>
|
||||
</div>
|
||||
`).join('')}
|
||||
</div>
|
||||
`;
|
||||
}).join('');
|
||||
|
||||
setHtml(container, html || '<div class="empty">暂无世界状态</div>');
|
||||
if (!activeFacts.length) {
|
||||
setHtml(container, '<div class="empty">暂无事实记录</div>');
|
||||
return;
|
||||
}
|
||||
|
||||
const relations = activeFacts.filter(f => /^对.+的/.test(f.p));
|
||||
const states = activeFacts.filter(f => !/^对.+的/.test(f.p));
|
||||
|
||||
let html = '';
|
||||
|
||||
if (states.length) {
|
||||
html += `<div class="fact-group">
|
||||
<div class="fact-group-title">状态/属性</div>
|
||||
${states.map(f => `
|
||||
<div class="fact-item">
|
||||
<span class="fact-subject">${h(f.s)}</span>
|
||||
<span class="fact-predicate">${h(f.p)}</span>
|
||||
<span class="fact-object">${h(f.o)}</span>
|
||||
<span class="fact-since">#${(f.since || 0) + 1}</span>
|
||||
</div>
|
||||
`).join('')}
|
||||
</div>`;
|
||||
}
|
||||
|
||||
if (relations.length) {
|
||||
html += `<div class="fact-group">
|
||||
<div class="fact-group-title">人物关系</div>
|
||||
${relations.map(f => `
|
||||
<div class="fact-item">
|
||||
<span class="fact-subject">${h(f.s)}</span>
|
||||
<span class="fact-predicate">${h(f.p)}</span>
|
||||
<span class="fact-object">${h(f.o)}</span>
|
||||
${f.trend ? `<span class="fact-trend ${TREND_CLASS[f.trend] || ''}">${h(f.trend)}</span>` : ''}
|
||||
<span class="fact-since">#${(f.since || 0) + 1}</span>
|
||||
</div>
|
||||
`).join('')}
|
||||
</div>`;
|
||||
}
|
||||
|
||||
setHtml(container, html);
|
||||
}
|
||||
})();
|
||||
|
||||
Reference in New Issue
Block a user