@yun-zero/claw-memory 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +68 -0
- package/README.md +323 -0
- package/dist/config/llm.d.ts +13 -0
- package/dist/config/llm.d.ts.map +1 -0
- package/dist/config/llm.js +96 -0
- package/dist/config/llm.js.map +1 -0
- package/dist/config/plugin.d.ts +15 -0
- package/dist/config/plugin.d.ts.map +1 -0
- package/dist/config/plugin.js +32 -0
- package/dist/config/plugin.js.map +1 -0
- package/dist/db/entityRepository.d.ts +21 -0
- package/dist/db/entityRepository.d.ts.map +1 -0
- package/dist/db/entityRepository.js +55 -0
- package/dist/db/entityRepository.js.map +1 -0
- package/dist/db/repository.d.ts +22 -0
- package/dist/db/repository.d.ts.map +1 -0
- package/dist/db/repository.js +77 -0
- package/dist/db/repository.js.map +1 -0
- package/dist/db/schema.d.ts +5 -0
- package/dist/db/schema.d.ts.map +1 -0
- package/dist/db/schema.js +112 -0
- package/dist/db/schema.js.map +1 -0
- package/dist/db/todoRepository.d.ts +26 -0
- package/dist/db/todoRepository.d.ts.map +1 -0
- package/dist/db/todoRepository.js +54 -0
- package/dist/db/todoRepository.js.map +1 -0
- package/dist/hooks/bootstrap.d.ts +3 -0
- package/dist/hooks/bootstrap.d.ts.map +1 -0
- package/dist/hooks/bootstrap.js +28 -0
- package/dist/hooks/bootstrap.js.map +1 -0
- package/dist/hooks/message.d.ts +18 -0
- package/dist/hooks/message.d.ts.map +1 -0
- package/dist/hooks/message.js +52 -0
- package/dist/hooks/message.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +46 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/tools.d.ts +26 -0
- package/dist/mcp/tools.d.ts.map +1 -0
- package/dist/mcp/tools.js +360 -0
- package/dist/mcp/tools.js.map +1 -0
- package/dist/plugin.d.ts +18 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +62 -0
- package/dist/plugin.js.map +1 -0
- package/dist/services/entityGraphService.d.ts +87 -0
- package/dist/services/entityGraphService.d.ts.map +1 -0
- package/dist/services/entityGraphService.js +271 -0
- package/dist/services/entityGraphService.js.map +1 -0
- package/dist/services/memory.d.ts +26 -0
- package/dist/services/memory.d.ts.map +1 -0
- package/dist/services/memory.js +281 -0
- package/dist/services/memory.js.map +1 -0
- package/dist/services/memoryIndex.d.ts +34 -0
- package/dist/services/memoryIndex.d.ts.map +1 -0
- package/dist/services/memoryIndex.js +100 -0
- package/dist/services/memoryIndex.js.map +1 -0
- package/dist/services/metadataExtractor.d.ts +16 -0
- package/dist/services/metadataExtractor.d.ts.map +1 -0
- package/dist/services/metadataExtractor.js +75 -0
- package/dist/services/metadataExtractor.js.map +1 -0
- package/dist/services/retrieval.d.ts +24 -0
- package/dist/services/retrieval.d.ts.map +1 -0
- package/dist/services/retrieval.js +40 -0
- package/dist/services/retrieval.js.map +1 -0
- package/dist/services/scheduler.d.ts +122 -0
- package/dist/services/scheduler.d.ts.map +1 -0
- package/dist/services/scheduler.js +434 -0
- package/dist/services/scheduler.js.map +1 -0
- package/dist/services/summarizer.d.ts +43 -0
- package/dist/services/summarizer.d.ts.map +1 -0
- package/dist/services/summarizer.js +252 -0
- package/dist/services/summarizer.js.map +1 -0
- package/dist/services/tagService.d.ts +64 -0
- package/dist/services/tagService.d.ts.map +1 -0
- package/dist/services/tagService.js +281 -0
- package/dist/services/tagService.js.map +1 -0
- package/dist/tools/memory.d.ts +3 -0
- package/dist/tools/memory.d.ts.map +1 -0
- package/dist/tools/memory.js +114 -0
- package/dist/tools/memory.js.map +1 -0
- package/dist/types.d.ts +128 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/docs/plans/2026-03-02-claw-memory-design.md +445 -0
- package/docs/plans/2026-03-02-incremental-summary-design.md +157 -0
- package/docs/plans/2026-03-02-incremental-summary-implementation.md +468 -0
- package/docs/plans/2026-03-02-memory-index-design.md +163 -0
- package/docs/plans/2026-03-02-memory-index-implementation.md +836 -0
- package/docs/plans/2026-03-02-mvp-implementation.md +1703 -0
- package/docs/plans/2026-03-02-testing-implementation.md +395 -0
- package/docs/plans/2026-03-02-testing-plan.md +93 -0
- package/docs/plans/2026-03-03-claw-memory-openclaw-plugin-design.md +285 -0
- package/docs/plans/2026-03-03-claw-memory-plugin-implementation.md +642 -0
- package/docs/plans/2026-03-03-entity-graph-design.md +121 -0
- package/docs/plans/2026-03-03-entity-graph-implementation.md +687 -0
- package/docs/plans/2026-03-03-llm-generic-config-design.md +43 -0
- package/docs/plans/2026-03-03-llm-generic-config-implementation.md +186 -0
- package/docs/plans/2026-03-03-memory-e2e-stress-test-design.md +110 -0
- package/docs/plans/2026-03-03-memory-e2e-stress-test-implementation.md +464 -0
- package/docs/plans/2026-03-03-minimax-llm-fix.md +156 -0
- package/docs/plans/2026-03-03-scheduler-design.md +165 -0
- package/docs/plans/2026-03-03-scheduler-implementation.md +777 -0
- package/docs/plans/2026-03-03-tags-visualization-design.md +73 -0
- package/docs/plans/2026-03-03-tags-visualization-implementation.md +539 -0
- package/openclaw.plugin.json +11 -0
- package/package.json +41 -0
- package/src/config/llm.ts +129 -0
- package/src/config/plugin.ts +47 -0
- package/src/db/entityRepository.ts +80 -0
- package/src/db/repository.ts +106 -0
- package/src/db/schema.ts +121 -0
- package/src/db/todoRepository.ts +76 -0
- package/src/hooks/bootstrap.ts +36 -0
- package/src/hooks/message.ts +84 -0
- package/src/index.ts +50 -0
- package/src/plugin.ts +85 -0
- package/src/services/entityGraphService.ts +367 -0
- package/src/services/memory.ts +338 -0
- package/src/services/memoryIndex.ts +140 -0
- package/src/services/metadataExtractor.ts +89 -0
- package/src/services/retrieval.ts +71 -0
- package/src/services/scheduler.ts +529 -0
- package/src/services/summarizer.ts +318 -0
- package/src/services/tagService.ts +335 -0
- package/src/tools/memory.ts +137 -0
- package/src/types.ts +139 -0
- package/tsconfig.json +20 -0
- package/vitest.config.ts +16 -0
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Memory Summarizer Service
|
|
3
|
+
* Aggregates memory data for weekly report generation
|
|
4
|
+
*/
|
|
5
|
+
import { generateSummaryWithLLM } from '../config/llm.js';
|
|
6
|
+
export class SummarizerService {
|
|
7
|
+
db;
|
|
8
|
+
constructor(db) {
|
|
9
|
+
this.db = db;
|
|
10
|
+
}
|
|
11
|
+
async aggregateMemories(startDate, endDate) {
|
|
12
|
+
// 1. 基础统计 - 查询指定日期范围的 memories
|
|
13
|
+
const basicStats = this.getBasicStats(startDate, endDate);
|
|
14
|
+
// 2. 标签分布 - 查询 memory_entities + entities (type='tag')
|
|
15
|
+
const tagStats = this.getTagStats(startDate, endDate);
|
|
16
|
+
// 3. 关键词 - 查询 memory_entities + entities (type='keyword')
|
|
17
|
+
const keywordStats = this.getKeywordStats(startDate, endDate);
|
|
18
|
+
// 4. 重要性分组 - 按 importance 分组
|
|
19
|
+
const importanceStats = this.getImportanceStats(startDate, endDate);
|
|
20
|
+
// 5. 访问模式 - 按 access_count 和时间排序
|
|
21
|
+
const accessStats = this.getAccessStats(startDate, endDate);
|
|
22
|
+
// 6. 实体关系 - 查询 entity_relations
|
|
23
|
+
const entityStats = this.getEntityStats(startDate, endDate);
|
|
24
|
+
return {
|
|
25
|
+
period: { start: startDate, end: endDate },
|
|
26
|
+
basic: basicStats,
|
|
27
|
+
tags: tagStats,
|
|
28
|
+
topics: keywordStats,
|
|
29
|
+
importance: importanceStats,
|
|
30
|
+
access: accessStats,
|
|
31
|
+
entities: entityStats,
|
|
32
|
+
summary: '' // Will be filled by LLM
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
getBasicStats(startDate, endDate) {
|
|
36
|
+
const stmt = this.db.prepare(`
|
|
37
|
+
SELECT
|
|
38
|
+
COUNT(*) as totalMemories,
|
|
39
|
+
COALESCE(SUM(token_count), 0) as totalTokens,
|
|
40
|
+
COALESCE(AVG(importance), 0) as avgImportance
|
|
41
|
+
FROM memories
|
|
42
|
+
WHERE date(created_at) >= date(?)
|
|
43
|
+
AND date(created_at) <= date(?)
|
|
44
|
+
`);
|
|
45
|
+
const result = stmt.get(startDate, endDate);
|
|
46
|
+
return {
|
|
47
|
+
totalMemories: result.totalMemories || 0,
|
|
48
|
+
totalTokens: result.totalTokens || 0,
|
|
49
|
+
avgImportance: parseFloat((result.avgImportance || 0).toFixed(2))
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
getTagStats(startDate, endDate) {
|
|
53
|
+
// Get all tags with their counts in the date range
|
|
54
|
+
const stmt = this.db.prepare(`
|
|
55
|
+
SELECT e.name, COUNT(me.memory_id) as count
|
|
56
|
+
FROM memory_entities me
|
|
57
|
+
JOIN entities e ON me.entity_id = e.id
|
|
58
|
+
JOIN memories m ON me.memory_id = m.id
|
|
59
|
+
WHERE e.type = 'tag'
|
|
60
|
+
AND date(m.created_at) >= date(?)
|
|
61
|
+
AND date(m.created_at) <= date(?)
|
|
62
|
+
GROUP BY e.id, e.name
|
|
63
|
+
ORDER BY count DESC
|
|
64
|
+
LIMIT 20
|
|
65
|
+
`);
|
|
66
|
+
const tags = stmt.all(startDate, endDate);
|
|
67
|
+
// Build distribution map
|
|
68
|
+
const tagDistribution = {};
|
|
69
|
+
for (const tag of tags) {
|
|
70
|
+
tagDistribution[tag.name] = tag.count;
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
topTags: tags.slice(0, 10),
|
|
74
|
+
tagDistribution
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
getKeywordStats(startDate, endDate) {
|
|
78
|
+
const stmt = this.db.prepare(`
|
|
79
|
+
SELECT e.name, COUNT(me.memory_id) as count
|
|
80
|
+
FROM memory_entities me
|
|
81
|
+
JOIN entities e ON me.entity_id = e.id
|
|
82
|
+
JOIN memories m ON me.memory_id = m.id
|
|
83
|
+
WHERE e.type = 'keyword'
|
|
84
|
+
AND date(m.created_at) >= date(?)
|
|
85
|
+
AND date(m.created_at) <= date(?)
|
|
86
|
+
GROUP BY e.id, e.name
|
|
87
|
+
ORDER BY count DESC
|
|
88
|
+
LIMIT 30
|
|
89
|
+
`);
|
|
90
|
+
const keywordsRaw = stmt.all(startDate, endDate);
|
|
91
|
+
// Map to expected format with 'word' field
|
|
92
|
+
const keywords = keywordsRaw.map(k => ({ word: k.name, count: k.count }));
|
|
93
|
+
// Extract key topics (top 5 most frequent)
|
|
94
|
+
const keyTopics = keywordsRaw.slice(0, 5).map(k => k.name);
|
|
95
|
+
return {
|
|
96
|
+
keywords,
|
|
97
|
+
keyTopics
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
getImportanceStats(startDate, endDate) {
|
|
101
|
+
const stmt = this.db.prepare(`
|
|
102
|
+
SELECT id, summary, importance
|
|
103
|
+
FROM memories
|
|
104
|
+
WHERE date(created_at) >= date(?)
|
|
105
|
+
AND date(created_at) <= date(?)
|
|
106
|
+
`);
|
|
107
|
+
const memories = stmt.all(startDate, endDate);
|
|
108
|
+
const highPriority = [];
|
|
109
|
+
const mediumPriority = [];
|
|
110
|
+
const lowPriority = [];
|
|
111
|
+
for (const memory of memories) {
|
|
112
|
+
if (memory.importance > 0.7) {
|
|
113
|
+
highPriority.push(memory);
|
|
114
|
+
}
|
|
115
|
+
else if (memory.importance > 0.3) {
|
|
116
|
+
mediumPriority.push(memory);
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
lowPriority.push(memory);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return { highPriority, mediumPriority, lowPriority };
|
|
123
|
+
}
|
|
124
|
+
getAccessStats(startDate, endDate) {
|
|
125
|
+
// Most accessed
|
|
126
|
+
const mostAccessedStmt = this.db.prepare(`
|
|
127
|
+
SELECT id, summary, access_count
|
|
128
|
+
FROM memories
|
|
129
|
+
WHERE date(created_at) >= date(?)
|
|
130
|
+
AND date(created_at) <= date(?)
|
|
131
|
+
ORDER BY access_count DESC
|
|
132
|
+
LIMIT 5
|
|
133
|
+
`);
|
|
134
|
+
const mostAccessed = mostAccessedStmt.all(startDate, endDate);
|
|
135
|
+
// Recently created
|
|
136
|
+
const recentlyCreatedStmt = this.db.prepare(`
|
|
137
|
+
SELECT id, summary, created_at
|
|
138
|
+
FROM memories
|
|
139
|
+
WHERE date(created_at) >= date(?)
|
|
140
|
+
AND date(created_at) <= date(?)
|
|
141
|
+
ORDER BY created_at DESC
|
|
142
|
+
LIMIT 5
|
|
143
|
+
`);
|
|
144
|
+
const recentlyCreated = recentlyCreatedStmt.all(startDate, endDate);
|
|
145
|
+
// Recently accessed
|
|
146
|
+
const recentlyAccessedStmt = this.db.prepare(`
|
|
147
|
+
SELECT id, summary, last_accessed_at
|
|
148
|
+
FROM memories
|
|
149
|
+
WHERE date(created_at) >= date(?)
|
|
150
|
+
AND date(created_at) <= date(?)
|
|
151
|
+
AND last_accessed_at IS NOT NULL
|
|
152
|
+
ORDER BY last_accessed_at DESC
|
|
153
|
+
LIMIT 5
|
|
154
|
+
`);
|
|
155
|
+
const recentlyAccessed = recentlyAccessedStmt.all(startDate, endDate);
|
|
156
|
+
return { mostAccessed, recentlyCreated, recentlyAccessed };
|
|
157
|
+
}
|
|
158
|
+
getEntityStats(startDate, endDate) {
|
|
159
|
+
// Get related entities from entity_relations
|
|
160
|
+
const relatedStmt = this.db.prepare(`
|
|
161
|
+
SELECT e1.name as entity, GROUP_CONCAT(e2.name) as related
|
|
162
|
+
FROM entity_relations er
|
|
163
|
+
JOIN entities e1 ON er.source_id = e1.id
|
|
164
|
+
JOIN entities e2 ON er.target_id = e2.id
|
|
165
|
+
WHERE er.relation_type IN ('related', 'similar', 'co_occur')
|
|
166
|
+
GROUP BY e1.id, e1.name
|
|
167
|
+
LIMIT 10
|
|
168
|
+
`);
|
|
169
|
+
const relatedResults = relatedStmt.all();
|
|
170
|
+
const relatedGroups = relatedResults.map(r => ({
|
|
171
|
+
entity: r.entity,
|
|
172
|
+
related: r.related ? r.related.split(',') : []
|
|
173
|
+
}));
|
|
174
|
+
// Get co-occurring tags
|
|
175
|
+
const coOccurStmt = this.db.prepare(`
|
|
176
|
+
SELECT e1.name as tag1, e2.name as tag2, COUNT(*) as count
|
|
177
|
+
FROM memory_entities me1
|
|
178
|
+
JOIN memory_entities me2 ON me1.memory_id = me2.memory_id AND me1.entity_id < me2.entity_id
|
|
179
|
+
JOIN entities e1 ON me1.entity_id = e1.id
|
|
180
|
+
JOIN entities e2 ON me2.entity_id = e2.id
|
|
181
|
+
JOIN memories m ON me1.memory_id = m.id
|
|
182
|
+
WHERE e1.type = 'tag' AND e2.type = 'tag'
|
|
183
|
+
AND date(m.created_at) >= date(?)
|
|
184
|
+
AND date(m.created_at) <= date(?)
|
|
185
|
+
GROUP BY e1.id, e2.id
|
|
186
|
+
ORDER BY count DESC
|
|
187
|
+
LIMIT 10
|
|
188
|
+
`);
|
|
189
|
+
const coOccurring = coOccurStmt.all(startDate, endDate);
|
|
190
|
+
const coOccurringTags = coOccurring.map(c => [c.tag1, c.tag2]);
|
|
191
|
+
return { relatedGroups, coOccurringTags };
|
|
192
|
+
}
|
|
193
|
+
reportToString(report) {
|
|
194
|
+
const lines = [];
|
|
195
|
+
lines.push(`📊 周报期间: ${report.period.start} 至 ${report.period.end}`);
|
|
196
|
+
lines.push('');
|
|
197
|
+
lines.push('## 基础统计');
|
|
198
|
+
lines.push(`- 记忆总数: ${report.basic.totalMemories}`);
|
|
199
|
+
lines.push(`- 总 Token 数: ${report.basic.totalTokens}`);
|
|
200
|
+
lines.push(`- 平均重要性: ${report.basic.avgImportance}`);
|
|
201
|
+
lines.push('');
|
|
202
|
+
if (report.tags.topTags.length > 0) {
|
|
203
|
+
lines.push('## 热门标签');
|
|
204
|
+
for (const tag of report.tags.topTags.slice(0, 5)) {
|
|
205
|
+
lines.push(`- ${tag.name}: ${tag.count} 条`);
|
|
206
|
+
}
|
|
207
|
+
lines.push('');
|
|
208
|
+
}
|
|
209
|
+
if (report.topics.keyTopics.length > 0) {
|
|
210
|
+
lines.push('## 关键主题');
|
|
211
|
+
lines.push(report.topics.keyTopics.join(', '));
|
|
212
|
+
lines.push('');
|
|
213
|
+
}
|
|
214
|
+
if (report.importance.highPriority.length > 0) {
|
|
215
|
+
lines.push('## 高优先级记忆');
|
|
216
|
+
for (const m of report.importance.highPriority.slice(0, 3)) {
|
|
217
|
+
lines.push(`- ${m.summary?.substring(0, 50) || '无摘要'}... (重要性: ${m.importance})`);
|
|
218
|
+
}
|
|
219
|
+
lines.push('');
|
|
220
|
+
}
|
|
221
|
+
return lines.join('\n');
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Generate weekly summary using LLM
|
|
225
|
+
*/
|
|
226
|
+
async generateWeeklySummary(report) {
|
|
227
|
+
const stats = await this.aggregateMemories(report.startDate, report.endDate);
|
|
228
|
+
const statsString = this.reportToString(stats);
|
|
229
|
+
let prompt = `请为以下周报数据生成一个简洁的中文摘要(100-200字):\n\n${statsString}`;
|
|
230
|
+
if (report.memories && report.memories.length > 0) {
|
|
231
|
+
prompt += `\n\n## 本周重要记忆内容:\n${report.memories.join('\n---\n')}`;
|
|
232
|
+
}
|
|
233
|
+
return await generateSummaryWithLLM(prompt);
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Generate monthly summary using LLM
|
|
237
|
+
*/
|
|
238
|
+
async generateMonthlySummary(report) {
|
|
239
|
+
const stats = await this.aggregateMemories(report.startDate, report.endDate);
|
|
240
|
+
const statsString = this.reportToString(stats);
|
|
241
|
+
let prompt = `请为以下月报数据生成一个简洁的中文摘要(150-300字):\n\n${statsString}`;
|
|
242
|
+
if (report.memories && report.memories.length > 0) {
|
|
243
|
+
prompt += `\n\n## 本月重要记忆内容:\n${report.memories.join('\n---\n')}`;
|
|
244
|
+
}
|
|
245
|
+
return await generateSummaryWithLLM(prompt);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Alias for SummarizerService for backwards compatibility
|
|
250
|
+
*/
|
|
251
|
+
export { SummarizerService as Summarizer };
|
|
252
|
+
//# sourceMappingURL=summarizer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"summarizer.js","sourceRoot":"","sources":["../../src/services/summarizer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import Database from 'better-sqlite3';
|
|
2
|
+
/**
|
|
3
|
+
* TagNode represents a hierarchical tag structure
|
|
4
|
+
*/
|
|
5
|
+
export interface TagNode {
|
|
6
|
+
name: string;
|
|
7
|
+
level: number;
|
|
8
|
+
memoryCount: number;
|
|
9
|
+
usageCount: number;
|
|
10
|
+
children: TagNode[];
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* TagStats provides aggregated statistics about tags
|
|
14
|
+
*/
|
|
15
|
+
export interface TagStats {
|
|
16
|
+
totalTags: number;
|
|
17
|
+
totalMemories: number;
|
|
18
|
+
usageStats: {
|
|
19
|
+
name: string;
|
|
20
|
+
count: number;
|
|
21
|
+
}[];
|
|
22
|
+
levelDistribution: Record<number, number>;
|
|
23
|
+
recentlyUsed: {
|
|
24
|
+
name: string;
|
|
25
|
+
lastUsedAt: Date;
|
|
26
|
+
}[];
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* TagService provides tag management and statistics functionality
|
|
30
|
+
*/
|
|
31
|
+
export declare class TagService {
|
|
32
|
+
private db;
|
|
33
|
+
private entityRepo;
|
|
34
|
+
constructor(db: Database.Database);
|
|
35
|
+
/**
|
|
36
|
+
* Get all tags as a hierarchical tree structure with statistics
|
|
37
|
+
*/
|
|
38
|
+
getTagTree(): Promise<{
|
|
39
|
+
totalTags: number;
|
|
40
|
+
maxLevel: number;
|
|
41
|
+
tree: TagNode[];
|
|
42
|
+
}>;
|
|
43
|
+
/**
|
|
44
|
+
* Get tag statistics
|
|
45
|
+
*/
|
|
46
|
+
getTagStats(): TagStats;
|
|
47
|
+
/**
|
|
48
|
+
* Generate collapsible tag tree HTML
|
|
49
|
+
*/
|
|
50
|
+
generateTreeHtml(data: {
|
|
51
|
+
totalTags: number;
|
|
52
|
+
maxLevel: number;
|
|
53
|
+
tree: TagNode[];
|
|
54
|
+
}): string;
|
|
55
|
+
/**
|
|
56
|
+
* Generate tag statistics HTML
|
|
57
|
+
*/
|
|
58
|
+
generateStatsHtml(stats: TagStats): string;
|
|
59
|
+
/**
|
|
60
|
+
* Build hierarchical tag tree from flat tag list
|
|
61
|
+
*/
|
|
62
|
+
private buildTagTree;
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=tagService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tagService.d.ts","sourceRoot":"","sources":["../../src/services/tagService.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AAGtC;;GAEG;AACH,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,OAAO,EAAE,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC9C,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1C,YAAY,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,IAAI,CAAA;KAAE,EAAE,CAAC;CACpD;AAED;;GAEG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,EAAE,CAAoB;IAC9B,OAAO,CAAC,UAAU,CAAmB;gBAEzB,EAAE,EAAE,QAAQ,CAAC,QAAQ;IAKjC;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,OAAO,EAAE,CAAA;KAAE,CAAC;IAwCrF;;OAEG;IACH,WAAW,IAAI,QAAQ;IAsEvB;;OAEG;IACH,gBAAgB,CAAC,IAAI,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,OAAO,EAAE,CAAA;KAAE,GAAG,MAAM;IA0DxF;;OAEG;IACH,iBAAiB,CAAC,KAAK,EAAE,QAAQ,GAAG,MAAM;IA0E1C;;OAEG;IACH,OAAO,CAAC,YAAY;CAwCrB"}
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
import { EntityRepository } from '../db/entityRepository.js';
|
|
2
|
+
/**
|
|
3
|
+
* TagService provides tag management and statistics functionality
|
|
4
|
+
*/
|
|
5
|
+
export class TagService {
|
|
6
|
+
db;
|
|
7
|
+
entityRepo;
|
|
8
|
+
constructor(db) {
|
|
9
|
+
this.db = db;
|
|
10
|
+
this.entityRepo = new EntityRepository(db);
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Get all tags as a hierarchical tree structure with statistics
|
|
14
|
+
*/
|
|
15
|
+
async getTagTree() {
|
|
16
|
+
// 1. Get all tags with memory count
|
|
17
|
+
const allTags = this.db.prepare(`
|
|
18
|
+
SELECT e.*, COUNT(me.memory_id) as memory_count
|
|
19
|
+
FROM entities e
|
|
20
|
+
LEFT JOIN memory_entities me ON e.id = me.entity_id
|
|
21
|
+
WHERE e.type = 'tag'
|
|
22
|
+
GROUP BY e.id
|
|
23
|
+
`).all();
|
|
24
|
+
// 2. Build mapping: tagId -> TagNode
|
|
25
|
+
const tagMap = new Map();
|
|
26
|
+
for (const tag of allTags) {
|
|
27
|
+
tagMap.set(tag.id, {
|
|
28
|
+
name: tag.name,
|
|
29
|
+
level: tag.level,
|
|
30
|
+
memoryCount: tag.memory_count || 0,
|
|
31
|
+
usageCount: tag.memory_count || 0,
|
|
32
|
+
children: []
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
// 3. Build tree structure via parent_id
|
|
36
|
+
const rootTags = [];
|
|
37
|
+
let maxLevel = 0;
|
|
38
|
+
for (const tag of allTags) {
|
|
39
|
+
const node = tagMap.get(tag.id);
|
|
40
|
+
maxLevel = Math.max(maxLevel, tag.level || 0);
|
|
41
|
+
if (tag.parent_id && tagMap.has(tag.parent_id)) {
|
|
42
|
+
tagMap.get(tag.parent_id).children.push(node);
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
rootTags.push(node);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return { totalTags: allTags.length, maxLevel, tree: rootTags };
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Get tag statistics
|
|
52
|
+
*/
|
|
53
|
+
getTagStats() {
|
|
54
|
+
// Get all tag entities
|
|
55
|
+
const tags = this.entityRepo.findByType('tag');
|
|
56
|
+
// Get memory count per tag
|
|
57
|
+
const memoryCountStmt = this.db.prepare(`
|
|
58
|
+
SELECT e.name, COUNT(me.memory_id) as memory_count
|
|
59
|
+
FROM entities e
|
|
60
|
+
LEFT JOIN memory_entities me ON e.id = me.entity_id
|
|
61
|
+
WHERE e.type = 'tag'
|
|
62
|
+
GROUP BY e.id
|
|
63
|
+
`);
|
|
64
|
+
const memoryCounts = memoryCountStmt.all();
|
|
65
|
+
// Get usage count (relevance sum)
|
|
66
|
+
const usageStmt = this.db.prepare(`
|
|
67
|
+
SELECT e.name, SUM(me.relevance) as usage_count
|
|
68
|
+
FROM entities e
|
|
69
|
+
LEFT JOIN memory_entities me ON e.id = me.entity_id
|
|
70
|
+
WHERE e.type = 'tag'
|
|
71
|
+
GROUP BY e.id
|
|
72
|
+
`);
|
|
73
|
+
const usageCounts = usageStmt.all();
|
|
74
|
+
// Calculate level distribution
|
|
75
|
+
const levelDistribution = {};
|
|
76
|
+
for (const tag of tags) {
|
|
77
|
+
levelDistribution[tag.level] = (levelDistribution[tag.level] || 0) + 1;
|
|
78
|
+
}
|
|
79
|
+
// Get total memories with tags
|
|
80
|
+
const totalMemoriesStmt = this.db.prepare(`
|
|
81
|
+
SELECT COUNT(DISTINCT memory_id) as count
|
|
82
|
+
FROM memory_entities me
|
|
83
|
+
JOIN entities e ON me.entity_id = e.id
|
|
84
|
+
WHERE e.type = 'tag'
|
|
85
|
+
`);
|
|
86
|
+
const totalMemoriesResult = totalMemoriesStmt.get();
|
|
87
|
+
// Get recently used tags
|
|
88
|
+
const recentStmt = this.db.prepare(`
|
|
89
|
+
SELECT e.name, MAX(me.created_at) as last_used_at
|
|
90
|
+
FROM entities e
|
|
91
|
+
JOIN memory_entities me ON e.id = me.entity_id
|
|
92
|
+
WHERE e.type = 'tag'
|
|
93
|
+
GROUP BY e.id
|
|
94
|
+
ORDER BY last_used_at DESC
|
|
95
|
+
LIMIT 10
|
|
96
|
+
`);
|
|
97
|
+
const recentlyUsed = recentStmt.all();
|
|
98
|
+
// Build usage stats
|
|
99
|
+
const usageStatsMap = new Map(usageCounts.map(u => [u.name, u.usage_count || 0]));
|
|
100
|
+
const usageStats = tags.map(tag => ({
|
|
101
|
+
name: tag.name,
|
|
102
|
+
count: usageStatsMap.get(tag.name) || 0
|
|
103
|
+
})).sort((a, b) => b.count - a.count);
|
|
104
|
+
return {
|
|
105
|
+
totalTags: tags.length,
|
|
106
|
+
totalMemories: totalMemoriesResult.count,
|
|
107
|
+
usageStats,
|
|
108
|
+
levelDistribution,
|
|
109
|
+
recentlyUsed: recentlyUsed.map(r => ({
|
|
110
|
+
name: r.name,
|
|
111
|
+
lastUsedAt: new Date(r.last_used_at)
|
|
112
|
+
}))
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Generate collapsible tag tree HTML
|
|
117
|
+
*/
|
|
118
|
+
generateTreeHtml(data) {
|
|
119
|
+
const renderNode = (node, indent = 0) => {
|
|
120
|
+
const padding = ' '.repeat(indent);
|
|
121
|
+
let html = `${padding}<div class="tag-item" data-level="${node.level}">\n`;
|
|
122
|
+
html += `${padding} <div class="tag-header" onclick="toggle(this)">\n`;
|
|
123
|
+
html += `${padding} <span class="toggle">${node.children.length ? '▶' : '·'}</span>\n`;
|
|
124
|
+
html += `${padding} <span class="tag-name">${node.name}</span>\n`;
|
|
125
|
+
html += `${padding} <span class="tag-count">(${node.memoryCount}条记忆, ${node.usageCount}次使用)</span>\n`;
|
|
126
|
+
html += `${padding} </div>\n`;
|
|
127
|
+
if (node.children.length > 0) {
|
|
128
|
+
html += `${padding} <div class="tag-children" style="display:none;">\n`;
|
|
129
|
+
for (const child of node.children) {
|
|
130
|
+
html += renderNode(child, indent + 2);
|
|
131
|
+
}
|
|
132
|
+
html += `${padding} </div>\n`;
|
|
133
|
+
}
|
|
134
|
+
html += `${padding}</div>\n`;
|
|
135
|
+
return html;
|
|
136
|
+
};
|
|
137
|
+
let html = `<!DOCTYPE html>
|
|
138
|
+
<html>
|
|
139
|
+
<head>
|
|
140
|
+
<meta charset="UTF-8">
|
|
141
|
+
<title>标签树 - ${data.totalTags} 个标签</title>
|
|
142
|
+
<style>
|
|
143
|
+
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; padding: 20px; }
|
|
144
|
+
.tag-item { margin: 4px 0; }
|
|
145
|
+
.tag-header { cursor: pointer; padding: 4px 8px; border-radius: 4px; }
|
|
146
|
+
.tag-header:hover { background: #f0f0f0; }
|
|
147
|
+
.toggle { display: inline-block; width: 20px; color: #666; }
|
|
148
|
+
.tag-name { font-weight: 500; color: #333; }
|
|
149
|
+
.tag-count { color: #999; font-size: 12px; margin-left: 8px; }
|
|
150
|
+
.tag-children { margin-left: 20px; border-left: 1px solid #eee; padding-left: 8px; }
|
|
151
|
+
</style>
|
|
152
|
+
</head>
|
|
153
|
+
<body>
|
|
154
|
+
<h1>标签树 (${data.totalTags} 个标签, 最大层级: ${data.maxLevel})</h1>
|
|
155
|
+
<script>
|
|
156
|
+
function toggle(el) {
|
|
157
|
+
const children = el.nextElementSibling;
|
|
158
|
+
if (children) children.style.display = children.style.display === 'none' ? 'block' : 'none';
|
|
159
|
+
const arrow = el.querySelector('.toggle');
|
|
160
|
+
if (arrow) arrow.textContent = children.style.display === 'none' ? '▶' : '▼';
|
|
161
|
+
}
|
|
162
|
+
</script>
|
|
163
|
+
`;
|
|
164
|
+
for (const node of data.tree) {
|
|
165
|
+
html += renderNode(node);
|
|
166
|
+
}
|
|
167
|
+
html += `</body></html>`;
|
|
168
|
+
return html;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Generate tag statistics HTML
|
|
172
|
+
*/
|
|
173
|
+
generateStatsHtml(stats) {
|
|
174
|
+
const maxUsage = Math.max(...stats.usageStats.map(s => s.count), 1);
|
|
175
|
+
let html = `<!DOCTYPE html>
|
|
176
|
+
<html>
|
|
177
|
+
<head>
|
|
178
|
+
<meta charset="UTF-8">
|
|
179
|
+
<title>标签统计</title>
|
|
180
|
+
<style>
|
|
181
|
+
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; padding: 20px; }
|
|
182
|
+
h1, h2 { color: #333; }
|
|
183
|
+
.stat-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 20px; margin: 20px 0; }
|
|
184
|
+
.stat-card { background: #f9f9f9; padding: 20px; border-radius: 8px; text-align: center; }
|
|
185
|
+
.stat-number { font-size: 36px; font-weight: bold; color: #4a90d9; }
|
|
186
|
+
.stat-label { color: #666; margin-top: 8px; }
|
|
187
|
+
.bar-chart { margin: 20px 0; }
|
|
188
|
+
.bar { background: #4a90d9; color: white; padding: 8px 12px; margin: 4px 0; border-radius: 4px; }
|
|
189
|
+
.recent-list { list-style: none; padding: 0; }
|
|
190
|
+
.recent-list li { padding: 8px; border-bottom: 1px solid #eee; }
|
|
191
|
+
.level-grid { display: flex; gap: 10px; flex-wrap: wrap; }
|
|
192
|
+
.level-badge { background: #e0e0e0; padding: 8px 16px; border-radius: 16px; }
|
|
193
|
+
</style>
|
|
194
|
+
</head>
|
|
195
|
+
<body>
|
|
196
|
+
<h1>标签统计</h1>
|
|
197
|
+
|
|
198
|
+
<div class="stat-grid">
|
|
199
|
+
<div class="stat-card">
|
|
200
|
+
<div class="stat-number">${stats.totalTags}</div>
|
|
201
|
+
<div class="stat-label">总标签数</div>
|
|
202
|
+
</div>
|
|
203
|
+
<div class="stat-card">
|
|
204
|
+
<div class="stat-number">${stats.totalMemories}</div>
|
|
205
|
+
<div class="stat-label">总记忆数</div>
|
|
206
|
+
</div>
|
|
207
|
+
</div>
|
|
208
|
+
|
|
209
|
+
<h2>使用频率排行</h2>
|
|
210
|
+
<div class="bar-chart">
|
|
211
|
+
`;
|
|
212
|
+
for (const s of stats.usageStats) {
|
|
213
|
+
const width = Math.round((s.count / maxUsage) * 100);
|
|
214
|
+
html += ` <div class="bar" style="width: ${width}%">${s.name} (${s.count})</div>\n`;
|
|
215
|
+
}
|
|
216
|
+
html += ` </div>
|
|
217
|
+
|
|
218
|
+
<h2>层级分布</h2>
|
|
219
|
+
<div class="level-grid">
|
|
220
|
+
`;
|
|
221
|
+
for (const [level, count] of Object.entries(stats.levelDistribution)) {
|
|
222
|
+
html += ` <div class="level-badge">Level ${level}: ${count}</div>\n`;
|
|
223
|
+
}
|
|
224
|
+
html += ` </div>
|
|
225
|
+
|
|
226
|
+
<h2>最近使用</h2>
|
|
227
|
+
<ul class="recent-list">
|
|
228
|
+
`;
|
|
229
|
+
for (const r of stats.recentlyUsed) {
|
|
230
|
+
const date = new Date(r.lastUsedAt).toLocaleDateString('zh-CN');
|
|
231
|
+
html += ` <li>${r.name} - ${date}</li>\n`;
|
|
232
|
+
}
|
|
233
|
+
html += ` </ul>
|
|
234
|
+
</body>
|
|
235
|
+
</html>`;
|
|
236
|
+
return html;
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Build hierarchical tag tree from flat tag list
|
|
240
|
+
*/
|
|
241
|
+
buildTagTree(tags) {
|
|
242
|
+
const tagMap = new Map();
|
|
243
|
+
const rootNodes = [];
|
|
244
|
+
// First pass: create all nodes
|
|
245
|
+
for (const tag of tags) {
|
|
246
|
+
tagMap.set(tag.name, {
|
|
247
|
+
name: tag.name,
|
|
248
|
+
level: tag.level,
|
|
249
|
+
memoryCount: 0,
|
|
250
|
+
usageCount: 0,
|
|
251
|
+
children: []
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
// Second pass: build hierarchy
|
|
255
|
+
for (const tag of tags) {
|
|
256
|
+
const node = tagMap.get(tag.name);
|
|
257
|
+
if (tag.level === 0) {
|
|
258
|
+
rootNodes.push(node);
|
|
259
|
+
}
|
|
260
|
+
else {
|
|
261
|
+
// Find parent based on hierarchical naming (e.g., "a/b/c" has parent "a/b")
|
|
262
|
+
const parts = tag.name.split('/');
|
|
263
|
+
if (parts.length > 1) {
|
|
264
|
+
const parentName = parts.slice(0, -1).join('/');
|
|
265
|
+
const parent = tagMap.get(parentName);
|
|
266
|
+
if (parent) {
|
|
267
|
+
parent.children.push(node);
|
|
268
|
+
}
|
|
269
|
+
else {
|
|
270
|
+
rootNodes.push(node);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
else {
|
|
274
|
+
rootNodes.push(node);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
return rootNodes;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
//# sourceMappingURL=tagService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tagService.js","sourceRoot":"","sources":["../../src/services/tagService.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../src/tools/memory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAM/C,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,GAAG,EACV,EAAE,EAAE,QAAQ,EACZ,OAAO,EAAE,MAAM,QA+HhB"}
|