tabby-ai-assistant 1.0.5 → 1.0.6

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.
Files changed (116) hide show
  1. package/dist/components/chat/ai-sidebar.component.d.ts +147 -0
  2. package/dist/components/chat/chat-interface.component.d.ts +38 -6
  3. package/dist/components/settings/general-settings.component.d.ts +6 -3
  4. package/dist/components/settings/provider-config.component.d.ts +25 -12
  5. package/dist/components/terminal/command-preview.component.d.ts +38 -0
  6. package/dist/index-full.d.ts +8 -0
  7. package/dist/index-minimal.d.ts +3 -0
  8. package/dist/index.d.ts +7 -3
  9. package/dist/index.js +1 -2
  10. package/dist/providers/tabby/ai-config.provider.d.ts +57 -5
  11. package/dist/providers/tabby/ai-hotkey.provider.d.ts +8 -14
  12. package/dist/providers/tabby/ai-toolbar-button.provider.d.ts +8 -9
  13. package/dist/services/chat/ai-sidebar.service.d.ts +89 -0
  14. package/dist/services/chat/chat-history.service.d.ts +78 -0
  15. package/dist/services/chat/chat-session.service.d.ts +57 -2
  16. package/dist/services/context/compaction.d.ts +90 -0
  17. package/dist/services/context/manager.d.ts +69 -0
  18. package/dist/services/context/memory.d.ts +116 -0
  19. package/dist/services/context/token-budget.d.ts +105 -0
  20. package/dist/services/core/ai-assistant.service.d.ts +40 -1
  21. package/dist/services/core/checkpoint.service.d.ts +130 -0
  22. package/dist/services/platform/escape-sequence.service.d.ts +132 -0
  23. package/dist/services/platform/platform-detection.service.d.ts +146 -0
  24. package/dist/services/providers/anthropic-provider.service.d.ts +5 -0
  25. package/dist/services/providers/base-provider.service.d.ts +6 -1
  26. package/dist/services/providers/glm-provider.service.d.ts +5 -0
  27. package/dist/services/providers/minimax-provider.service.d.ts +10 -1
  28. package/dist/services/providers/ollama-provider.service.d.ts +76 -0
  29. package/dist/services/providers/openai-compatible.service.d.ts +5 -0
  30. package/dist/services/providers/openai-provider.service.d.ts +5 -0
  31. package/dist/services/providers/vllm-provider.service.d.ts +82 -0
  32. package/dist/services/terminal/buffer-analyzer.service.d.ts +128 -0
  33. package/dist/services/terminal/terminal-manager.service.d.ts +185 -0
  34. package/dist/services/terminal/terminal-tools.service.d.ts +79 -0
  35. package/dist/types/ai.types.d.ts +92 -0
  36. package/dist/types/provider.types.d.ts +1 -1
  37. package/package.json +7 -10
  38. package/src/components/chat/ai-sidebar.component.ts +945 -0
  39. package/src/components/chat/chat-input.component.html +9 -24
  40. package/src/components/chat/chat-input.component.scss +3 -2
  41. package/src/components/chat/chat-interface.component.html +77 -69
  42. package/src/components/chat/chat-interface.component.scss +54 -4
  43. package/src/components/chat/chat-interface.component.ts +250 -34
  44. package/src/components/chat/chat-settings.component.scss +4 -4
  45. package/src/components/chat/chat-settings.component.ts +22 -11
  46. package/src/components/common/error-message.component.html +15 -0
  47. package/src/components/common/error-message.component.scss +77 -0
  48. package/src/components/common/error-message.component.ts +2 -96
  49. package/src/components/common/loading-spinner.component.html +4 -0
  50. package/src/components/common/loading-spinner.component.scss +57 -0
  51. package/src/components/common/loading-spinner.component.ts +2 -63
  52. package/src/components/security/consent-dialog.component.html +22 -0
  53. package/src/components/security/consent-dialog.component.scss +34 -0
  54. package/src/components/security/consent-dialog.component.ts +2 -55
  55. package/src/components/security/password-prompt.component.html +19 -0
  56. package/src/components/security/password-prompt.component.scss +30 -0
  57. package/src/components/security/password-prompt.component.ts +2 -54
  58. package/src/components/security/risk-confirm-dialog.component.html +8 -12
  59. package/src/components/security/risk-confirm-dialog.component.scss +8 -5
  60. package/src/components/security/risk-confirm-dialog.component.ts +6 -6
  61. package/src/components/settings/ai-settings-tab.component.html +16 -20
  62. package/src/components/settings/ai-settings-tab.component.scss +8 -5
  63. package/src/components/settings/ai-settings-tab.component.ts +12 -12
  64. package/src/components/settings/general-settings.component.html +8 -17
  65. package/src/components/settings/general-settings.component.scss +6 -3
  66. package/src/components/settings/general-settings.component.ts +62 -22
  67. package/src/components/settings/provider-config.component.html +19 -39
  68. package/src/components/settings/provider-config.component.scss +182 -39
  69. package/src/components/settings/provider-config.component.ts +119 -7
  70. package/src/components/settings/security-settings.component.scss +1 -1
  71. package/src/components/terminal/ai-toolbar-button.component.html +8 -0
  72. package/src/components/terminal/ai-toolbar-button.component.scss +20 -0
  73. package/src/components/terminal/ai-toolbar-button.component.ts +2 -30
  74. package/src/components/terminal/command-preview.component.html +61 -0
  75. package/src/components/terminal/command-preview.component.scss +72 -0
  76. package/src/components/terminal/command-preview.component.ts +127 -140
  77. package/src/components/terminal/command-suggestion.component.html +23 -0
  78. package/src/components/terminal/command-suggestion.component.scss +55 -0
  79. package/src/components/terminal/command-suggestion.component.ts +2 -77
  80. package/src/index-minimal.ts +32 -0
  81. package/src/index.ts +94 -11
  82. package/src/index.ts.backup +165 -0
  83. package/src/providers/tabby/ai-config.provider.ts +60 -51
  84. package/src/providers/tabby/ai-hotkey.provider.ts +23 -39
  85. package/src/providers/tabby/ai-settings-tab.provider.ts +2 -2
  86. package/src/providers/tabby/ai-toolbar-button.provider.ts +29 -24
  87. package/src/services/chat/ai-sidebar.service.ts +258 -0
  88. package/src/services/chat/chat-history.service.ts +308 -0
  89. package/src/services/chat/chat-history.service.ts.backup +239 -0
  90. package/src/services/chat/chat-session.service.ts +276 -3
  91. package/src/services/context/compaction.ts +483 -0
  92. package/src/services/context/manager.ts +442 -0
  93. package/src/services/context/memory.ts +519 -0
  94. package/src/services/context/token-budget.ts +422 -0
  95. package/src/services/core/ai-assistant.service.ts +280 -5
  96. package/src/services/core/ai-provider-manager.service.ts +2 -2
  97. package/src/services/core/checkpoint.service.ts +619 -0
  98. package/src/services/platform/escape-sequence.service.ts +499 -0
  99. package/src/services/platform/platform-detection.service.ts +494 -0
  100. package/src/services/providers/anthropic-provider.service.ts +28 -1
  101. package/src/services/providers/base-provider.service.ts +7 -1
  102. package/src/services/providers/glm-provider.service.ts +28 -1
  103. package/src/services/providers/minimax-provider.service.ts +209 -11
  104. package/src/services/providers/ollama-provider.service.ts +445 -0
  105. package/src/services/providers/openai-compatible.service.ts +9 -0
  106. package/src/services/providers/openai-provider.service.ts +9 -0
  107. package/src/services/providers/vllm-provider.service.ts +463 -0
  108. package/src/services/security/risk-assessment.service.ts +6 -2
  109. package/src/services/terminal/buffer-analyzer.service.ts +594 -0
  110. package/src/services/terminal/terminal-manager.service.ts +748 -0
  111. package/src/services/terminal/terminal-tools.service.ts +441 -0
  112. package/src/styles/ai-assistant.scss +78 -6
  113. package/src/types/ai.types.ts +144 -0
  114. package/src/types/provider.types.ts +1 -1
  115. package/tsconfig.json +9 -9
  116. package/webpack.config.js +28 -6
@@ -0,0 +1,519 @@
1
+ import { Injectable } from '@angular/core';
2
+ import { LoggerService } from '../core/logger.service';
3
+ import { ApiMessage } from '../../types/ai.types';
4
+
5
+ /**
6
+ * 记忆层类型
7
+ */
8
+ export enum MemoryLayer {
9
+ SHORT_TERM = 'short_term', // 短期记忆:当前会话
10
+ MID_TERM = 'mid_term', // 中期记忆:跨会话摘要
11
+ LONG_TERM = 'long_term' // 长期记忆:项目级知识
12
+ }
13
+
14
+ /**
15
+ * 记忆项接口
16
+ */
17
+ export interface MemoryItem {
18
+ id: string;
19
+ layer: MemoryLayer;
20
+ content: string;
21
+ metadata: {
22
+ timestamp: number;
23
+ sessionId?: string;
24
+ importance: number; // 0-1 重要性评分
25
+ tags?: string[];
26
+ source?: string;
27
+ };
28
+ accessCount: number;
29
+ lastAccessed: number;
30
+ }
31
+
32
+ /**
33
+ * 记忆摘要
34
+ */
35
+ export interface MemorySummary {
36
+ id: string;
37
+ layer: MemoryLayer;
38
+ summary: string;
39
+ keyPoints: string[];
40
+ createdAt: number;
41
+ relatedMemories: string[]; // 相关记忆ID列表
42
+ }
43
+
44
+ /**
45
+ * 记忆统计信息
46
+ */
47
+ export interface MemoryStatistics {
48
+ totalItems: number;
49
+ shortTermCount: number;
50
+ midTermCount: number;
51
+ longTermCount: number;
52
+ totalAccessCount: number;
53
+ averageImportance: number;
54
+ oldestMemory?: Date;
55
+ newestMemory?: Date;
56
+ }
57
+
58
+ /**
59
+ * 三层记忆系统
60
+ * 实现 Claude Code 架构的记忆管理
61
+ */
62
+ @Injectable({
63
+ providedIn: 'root'
64
+ })
65
+ export class Memory {
66
+ private shortTermMemories: Map<string, MemoryItem> = new Map();
67
+ private midTermMemories: Map<string, MemoryItem> = new Map();
68
+ private longTermMemories: Map<string, MemoryItem> = new Map();
69
+
70
+ // LRU缓存管理
71
+ private readonly MAX_SHORT_TERM_ITEMS = 100;
72
+ private readonly MAX_MID_TERM_ITEMS = 50;
73
+ private readonly MAX_LONG_TERM_ITEMS = 20;
74
+
75
+ // 重要性阈值
76
+ private readonly IMPORTANCE_THRESHOLD = 0.7;
77
+
78
+ constructor(private logger: LoggerService) {
79
+ this.logger.info('Memory system initialized');
80
+ this.loadFromStorage();
81
+ }
82
+
83
+ /**
84
+ * 存储记忆
85
+ */
86
+ store(content: string, layer: MemoryLayer, metadata: Partial<MemoryItem['metadata']> = {}): string {
87
+ const memoryId = this.generateMemoryId();
88
+ const memory: MemoryItem = {
89
+ id: memoryId,
90
+ layer,
91
+ content,
92
+ metadata: {
93
+ timestamp: Date.now(),
94
+ importance: 0.5,
95
+ tags: [],
96
+ ...metadata
97
+ },
98
+ accessCount: 0,
99
+ lastAccessed: Date.now()
100
+ };
101
+
102
+ // 根据层级存储
103
+ switch (layer) {
104
+ case MemoryLayer.SHORT_TERM:
105
+ this.shortTermMemories.set(memoryId, memory);
106
+ this.enforceLimit(this.shortTermMemories, this.MAX_SHORT_TERM_ITEMS);
107
+ break;
108
+
109
+ case MemoryLayer.MID_TERM:
110
+ this.midTermMemories.set(memoryId, memory);
111
+ this.enforceLimit(this.midTermMemories, this.MAX_MID_TERM_ITEMS);
112
+ break;
113
+
114
+ case MemoryLayer.LONG_TERM:
115
+ this.longTermMemories.set(memoryId, memory);
116
+ this.enforceLimit(this.longTermMemories, this.MAX_LONG_TERM_ITEMS);
117
+ break;
118
+ }
119
+
120
+ // 持久化存储
121
+ this.saveToStorage();
122
+
123
+ this.logger.info('Memory stored', {
124
+ id: memoryId,
125
+ layer,
126
+ contentLength: content.length
127
+ });
128
+
129
+ return memoryId;
130
+ }
131
+
132
+ /**
133
+ * 检索记忆
134
+ */
135
+ retrieve(memoryId: string): MemoryItem | undefined {
136
+ // 在所有层级中查找
137
+ const memory = this.shortTermMemories.get(memoryId) ||
138
+ this.midTermMemories.get(memoryId) ||
139
+ this.longTermMemories.get(memoryId);
140
+
141
+ if (memory) {
142
+ // 更新访问统计
143
+ memory.accessCount++;
144
+ memory.lastAccessed = Date.now();
145
+
146
+ // 重要性提升(基于访问次数)
147
+ memory.metadata.importance = Math.min(1, memory.metadata.importance + 0.01);
148
+
149
+ this.logger.debug('Memory retrieved', { id: memoryId });
150
+ this.saveToStorage();
151
+ }
152
+
153
+ return memory;
154
+ }
155
+
156
+ /**
157
+ * 搜索记忆
158
+ */
159
+ search(query: string, layer?: MemoryLayer): MemoryItem[] {
160
+ const results: MemoryItem[] = [];
161
+ const searchTerms = query.toLowerCase().split(' ');
162
+
163
+ const searchInMap = (memories: Map<string, MemoryItem>) => {
164
+ memories.forEach(memory => {
165
+ const searchableText = `${memory.content} ${memory.metadata.tags?.join(' ') || ''}`.toLowerCase();
166
+ const matchScore = searchTerms.reduce((score, term) => {
167
+ return score + (searchableText.includes(term) ? 1 : 0);
168
+ }, 0);
169
+
170
+ if (matchScore > 0) {
171
+ results.push({
172
+ ...memory,
173
+ metadata: {
174
+ ...memory.metadata,
175
+ importance: memory.metadata.importance * (matchScore / searchTerms.length)
176
+ }
177
+ });
178
+ }
179
+ });
180
+ };
181
+
182
+ if (layer) {
183
+ switch (layer) {
184
+ case MemoryLayer.SHORT_TERM:
185
+ searchInMap(this.shortTermMemories);
186
+ break;
187
+ case MemoryLayer.MID_TERM:
188
+ searchInMap(this.midTermMemories);
189
+ break;
190
+ case MemoryLayer.LONG_TERM:
191
+ searchInMap(this.longTermMemories);
192
+ break;
193
+ }
194
+ } else {
195
+ // 搜索所有层级
196
+ searchInMap(this.shortTermMemories);
197
+ searchInMap(this.midTermMemories);
198
+ searchInMap(this.longTermMemories);
199
+ }
200
+
201
+ // 按相关性和重要性排序
202
+ return results.sort((a, b) => {
203
+ // 首先按重要性排序
204
+ if (Math.abs(a.metadata.importance - b.metadata.importance) > 0.1) {
205
+ return b.metadata.importance - a.metadata.importance;
206
+ }
207
+ // 然后按访问次数排序
208
+ return b.accessCount - a.accessCount;
209
+ });
210
+ }
211
+
212
+ /**
213
+ * 从消息创建记忆
214
+ */
215
+ createFromMessages(messages: ApiMessage[], layer: MemoryLayer): string[] {
216
+ const memoryIds: string[] = [];
217
+
218
+ for (const message of messages) {
219
+ if (message.role === 'user' || message.role === 'assistant') {
220
+ const content = typeof message.content === 'string'
221
+ ? message.content
222
+ : JSON.stringify(message.content);
223
+
224
+ // 只保存较长的消息作为记忆
225
+ if (content.length > 50) {
226
+ const memoryId = this.store(content, layer, {
227
+ timestamp: message.ts,
228
+ source: 'message'
229
+ });
230
+ memoryIds.push(memoryId);
231
+ }
232
+ }
233
+ }
234
+
235
+ this.logger.info('Created memories from messages', {
236
+ messageCount: messages.length,
237
+ memoryCount: memoryIds.length,
238
+ layer
239
+ });
240
+
241
+ return memoryIds;
242
+ }
243
+
244
+ /**
245
+ * 生成中期记忆摘要
246
+ */
247
+ async createMidTermSummary(sessionId: string): Promise<string> {
248
+ // 获取短期记忆中的相关消息
249
+ const sessionMemories = this.search('', MemoryLayer.SHORT_TERM)
250
+ .filter(m => m.metadata.sessionId === sessionId);
251
+
252
+ if (sessionMemories.length === 0) {
253
+ return '无相关记忆';
254
+ }
255
+
256
+ // TODO: 使用AI生成摘要
257
+ const summary = this.generateIntelligentSummary(sessionMemories);
258
+
259
+ // 保存为中期记忆
260
+ const midTermId = this.store(summary, MemoryLayer.MID_TERM, {
261
+ sessionId,
262
+ importance: 0.8,
263
+ tags: ['summary', 'session']
264
+ });
265
+
266
+ this.logger.info('Created mid-term summary', {
267
+ sessionId,
268
+ summaryLength: summary.length,
269
+ memoryId: midTermId
270
+ });
271
+
272
+ return summary;
273
+ }
274
+
275
+ /**
276
+ * 保存到长期记忆(项目级)
277
+ */
278
+ saveToLongTerm(content: string, metadata: Partial<MemoryItem['metadata']> = {}): string {
279
+ return this.store(content, MemoryLayer.LONG_TERM, {
280
+ importance: 0.9,
281
+ tags: ['project', ...(metadata.tags || [])],
282
+ ...metadata
283
+ });
284
+ }
285
+
286
+ /**
287
+ * 从长期记忆加载
288
+ */
289
+ loadFromLongTerm(query?: string): MemoryItem[] {
290
+ if (query) {
291
+ return this.search(query, MemoryLayer.LONG_TERM);
292
+ }
293
+ return Array.from(this.longTermMemories.values());
294
+ }
295
+
296
+ /**
297
+ * 获取记忆统计
298
+ */
299
+ getStatistics(): MemoryStatistics {
300
+ const allMemories = [
301
+ ...this.shortTermMemories.values(),
302
+ ...this.midTermMemories.values(),
303
+ ...this.longTermMemories.values()
304
+ ];
305
+
306
+ const totalAccessCount = allMemories.reduce((sum, m) => sum + m.accessCount, 0);
307
+ const averageImportance = allMemories.length > 0
308
+ ? allMemories.reduce((sum, m) => sum + m.metadata.importance, 0) / allMemories.length
309
+ : 0;
310
+
311
+ const timestamps = allMemories.map(m => m.metadata.timestamp);
312
+ const oldestMemory = timestamps.length > 0 ? new Date(Math.min(...timestamps)) : undefined;
313
+ const newestMemory = timestamps.length > 0 ? new Date(Math.max(...timestamps)) : undefined;
314
+
315
+ return {
316
+ totalItems: allMemories.length,
317
+ shortTermCount: this.shortTermMemories.size,
318
+ midTermCount: this.midTermMemories.size,
319
+ longTermCount: this.longTermMemories.size,
320
+ totalAccessCount,
321
+ averageImportance,
322
+ oldestMemory,
323
+ newestMemory
324
+ };
325
+ }
326
+
327
+ /**
328
+ * 清理过期记忆
329
+ */
330
+ cleanup(): number {
331
+ const now = Date.now();
332
+ const DAY_IN_MS = 24 * 60 * 60 * 1000;
333
+
334
+ let cleanedCount = 0;
335
+
336
+ // 清理短期记忆(超过7天且低重要性)
337
+ this.shortTermMemories.forEach((memory, id) => {
338
+ const age = now - memory.metadata.timestamp;
339
+ const isOld = age > 7 * DAY_IN_MS;
340
+ const isLowImportance = memory.metadata.importance < 0.3;
341
+ const isRarelyAccessed = memory.accessCount < 2;
342
+
343
+ if (isOld && isLowImportance && isRarelyAccessed) {
344
+ this.shortTermMemories.delete(id);
345
+ cleanedCount++;
346
+ }
347
+ });
348
+
349
+ // 清理中期记忆(超过30天且低重要性)
350
+ this.midTermMemories.forEach((memory, id) => {
351
+ const age = now - memory.metadata.timestamp;
352
+ const isOld = age > 30 * DAY_IN_MS;
353
+ const isLowImportance = memory.metadata.importance < 0.5;
354
+
355
+ if (isOld && isLowImportance) {
356
+ this.midTermMemories.delete(id);
357
+ cleanedCount++;
358
+ }
359
+ });
360
+
361
+ if (cleanedCount > 0) {
362
+ this.saveToStorage();
363
+ this.logger.info('Cleaned up memories', { cleanedCount });
364
+ }
365
+
366
+ return cleanedCount;
367
+ }
368
+
369
+ /**
370
+ * 清空指定层级
371
+ */
372
+ clearLayer(layer: MemoryLayer): void {
373
+ switch (layer) {
374
+ case MemoryLayer.SHORT_TERM:
375
+ this.shortTermMemories.clear();
376
+ break;
377
+ case MemoryLayer.MID_TERM:
378
+ this.midTermMemories.clear();
379
+ break;
380
+ case MemoryLayer.LONG_TERM:
381
+ this.longTermMemories.clear();
382
+ break;
383
+ }
384
+
385
+ this.saveToStorage();
386
+ this.logger.info('Cleared memory layer', { layer });
387
+ }
388
+
389
+ /**
390
+ * 获取相关记忆
391
+ */
392
+ getRelatedMemories(memoryId: string): MemoryItem[] {
393
+ const memory = this.retrieve(memoryId);
394
+ if (!memory) {
395
+ return [];
396
+ }
397
+
398
+ // 基于标签和内容相似性查找相关记忆
399
+ const allMemories = [
400
+ ...this.shortTermMemories.values(),
401
+ ...this.midTermMemories.values(),
402
+ ...this.longTermMemories.values()
403
+ ];
404
+
405
+ return allMemories.filter(m => {
406
+ if (m.id === memoryId) return false;
407
+
408
+ // 标签匹配
409
+ const commonTags = (m.metadata.tags || []).filter(tag =>
410
+ memory.metadata.tags?.includes(tag)
411
+ );
412
+
413
+ if (commonTags.length > 0) {
414
+ return true;
415
+ }
416
+
417
+ // 内容相似性(简化版)
418
+ const similarity = this.calculateSimilarity(memory.content, m.content);
419
+ return similarity > 0.3;
420
+ }).slice(0, 10); // 最多返回10个相关记忆
421
+ }
422
+
423
+ // ==================== 私有方法 ====================
424
+
425
+ private generateMemoryId(): string {
426
+ return `memory_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
427
+ }
428
+
429
+ private enforceLimit(memories: Map<string, MemoryItem>, maxItems: number): void {
430
+ if (memories.size <= maxItems) {
431
+ return;
432
+ }
433
+
434
+ // 按访问时间和重要性排序,删除最不重要的
435
+ const sorted = Array.from(memories.entries()).sort((a, b) => {
436
+ const scoreA = a[1].metadata.importance * (1 / (a[1].accessCount + 1));
437
+ const scoreB = b[1].metadata.importance * (1 / (b[1].accessCount + 1));
438
+ return scoreA - scoreB;
439
+ });
440
+
441
+ const toDelete = sorted.slice(0, memories.size - maxItems);
442
+ toDelete.forEach(([id]) => memories.delete(id));
443
+ }
444
+
445
+ private generateIntelligentSummary(memories: MemoryItem[]): string {
446
+ // 简化版摘要生成
447
+ // 实际实现中应该使用AI
448
+
449
+ const userMessages = memories.filter(m => m.content.includes('user:'));
450
+ const assistantMessages = memories.filter(m => m.content.includes('assistant:'));
451
+
452
+ let summary = `会话摘要(共${memories.length}条记忆):\n\n`;
453
+
454
+ if (userMessages.length > 0) {
455
+ summary += `用户主要问题:\n`;
456
+ summary += `${userMessages[0].content.substring(0, 100)}...\n\n`;
457
+ }
458
+
459
+ if (assistantMessages.length > 0) {
460
+ summary += `助手主要回复:\n`;
461
+ summary += `${assistantMessages[0].content.substring(0, 100)}...\n\n`;
462
+ }
463
+
464
+ summary += `关键要点:\n`;
465
+ memories
466
+ .filter(m => m.metadata.importance > this.IMPORTANCE_THRESHOLD)
467
+ .slice(0, 3)
468
+ .forEach((m, i) => {
469
+ summary += `${i + 1}. ${m.content.substring(0, 80)}...\n`;
470
+ });
471
+
472
+ return summary;
473
+ }
474
+
475
+ private calculateSimilarity(text1: string, text2: string): number {
476
+ // 简化的文本相似性计算
477
+ const words1 = new Set(text1.toLowerCase().split(/\s+/));
478
+ const words2 = new Set(text2.toLowerCase().split(/\s+/));
479
+
480
+ const intersection = new Set([...words1].filter(x => words2.has(x)));
481
+ const union = new Set([...words1, ...words2]);
482
+
483
+ return intersection.size / union.size;
484
+ }
485
+
486
+ private saveToStorage(): void {
487
+ try {
488
+ const data = {
489
+ shortTerm: Array.from(this.shortTermMemories.entries()),
490
+ midTerm: Array.from(this.midTermMemories.entries()),
491
+ longTerm: Array.from(this.longTermMemories.entries())
492
+ };
493
+ localStorage.setItem('tabby-ai-assistant-memories', JSON.stringify(data));
494
+ } catch (error) {
495
+ this.logger.error('Failed to save memories to storage', error);
496
+ }
497
+ }
498
+
499
+ private loadFromStorage(): void {
500
+ try {
501
+ const stored = localStorage.getItem('tabby-ai-assistant-memories');
502
+ if (stored) {
503
+ const data = JSON.parse(stored);
504
+
505
+ this.shortTermMemories = new Map(data.shortTerm || []);
506
+ this.midTermMemories = new Map(data.midTerm || []);
507
+ this.longTermMemories = new Map(data.longTerm || []);
508
+
509
+ this.logger.info('Loaded memories from storage', {
510
+ shortTerm: this.shortTermMemories.size,
511
+ midTerm: this.midTermMemories.size,
512
+ longTerm: this.longTermMemories.size
513
+ });
514
+ }
515
+ } catch (error) {
516
+ this.logger.error('Failed to load memories from storage', error);
517
+ }
518
+ }
519
+ }