tabby-ai-assistant 1.0.13 → 1.0.15

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 (41) hide show
  1. package/.editorconfig +18 -0
  2. package/dist/index.js +1 -1
  3. package/package.json +6 -4
  4. package/src/components/chat/ai-sidebar.component.scss +220 -9
  5. package/src/components/chat/ai-sidebar.component.ts +364 -29
  6. package/src/components/chat/chat-input.component.ts +36 -4
  7. package/src/components/chat/chat-interface.component.ts +225 -5
  8. package/src/components/chat/chat-message.component.ts +6 -1
  9. package/src/components/settings/context-settings.component.ts +91 -91
  10. package/src/components/terminal/ai-toolbar-button.component.ts +4 -2
  11. package/src/components/terminal/command-suggestion.component.ts +148 -6
  12. package/src/index.ts +0 -6
  13. package/src/providers/tabby/ai-toolbar-button.provider.ts +7 -3
  14. package/src/services/chat/ai-sidebar.service.ts +414 -410
  15. package/src/services/chat/chat-session.service.ts +36 -12
  16. package/src/services/context/compaction.ts +110 -134
  17. package/src/services/context/manager.ts +27 -7
  18. package/src/services/context/memory.ts +17 -33
  19. package/src/services/context/summary.service.ts +136 -0
  20. package/src/services/core/ai-assistant.service.ts +1060 -37
  21. package/src/services/core/ai-provider-manager.service.ts +154 -25
  22. package/src/services/core/checkpoint.service.ts +218 -18
  23. package/src/services/core/toast.service.ts +106 -106
  24. package/src/services/providers/anthropic-provider.service.ts +126 -30
  25. package/src/services/providers/base-provider.service.ts +90 -7
  26. package/src/services/providers/glm-provider.service.ts +151 -38
  27. package/src/services/providers/minimax-provider.service.ts +55 -40
  28. package/src/services/providers/ollama-provider.service.ts +117 -28
  29. package/src/services/providers/openai-compatible.service.ts +164 -34
  30. package/src/services/providers/openai-provider.service.ts +169 -34
  31. package/src/services/providers/vllm-provider.service.ts +116 -28
  32. package/src/services/terminal/terminal-context.service.ts +265 -5
  33. package/src/services/terminal/terminal-manager.service.ts +748 -748
  34. package/src/services/terminal/terminal-tools.service.ts +612 -441
  35. package/src/types/ai.types.ts +156 -3
  36. package/src/utils/cost.utils.ts +249 -0
  37. package/src/utils/validation.utils.ts +306 -2
  38. package/dist/index.js.LICENSE.txt +0 -18
  39. package/src/services/terminal/command-analyzer.service.ts +0 -43
  40. package/src/services/terminal/context-menu.service.ts +0 -45
  41. package/src/services/terminal/hotkey.service.ts +0 -53
@@ -6,6 +6,7 @@ import { LoggerService } from '../core/logger.service';
6
6
  import { ConfigProviderService } from '../core/config-provider.service';
7
7
  import { ChatHistoryService } from './chat-history.service';
8
8
  import { ContextManager } from '../context/manager';
9
+ import { CheckpointManager } from '../core/checkpoint.service';
9
10
 
10
11
  /**
11
12
  * 聊天会话服务
@@ -31,7 +32,8 @@ export class ChatSessionService {
31
32
  private logger: LoggerService,
32
33
  private chatHistoryService: ChatHistoryService,
33
34
  private contextManager: ContextManager,
34
- private configService: ConfigProviderService
35
+ private configService: ConfigProviderService,
36
+ private checkpointManager: CheckpointManager
35
37
  ) { }
36
38
 
37
39
  /**
@@ -46,10 +48,34 @@ export class ChatSessionService {
46
48
 
47
49
  /**
48
50
  * 切换到指定会话
51
+ * 从存储中加载会话历史
49
52
  */
50
53
  switchToSession(sessionId: string): void {
51
54
  this.currentSessionId = sessionId;
52
- // TODO: 从存储中加载会话历史
55
+
56
+ // 从 ChatHistoryService 加载会话历史
57
+ const sessionData = this.chatHistoryService.loadSession(sessionId);
58
+
59
+ if (sessionData && sessionData.messages && sessionData.messages.length > 0) {
60
+ // 恢复消息历史
61
+ const chatMessages: ChatMessage[] = sessionData.messages.map((msg, index) => ({
62
+ id: msg.id || `msg_${sessionId}_${index}`,
63
+ role: msg.role,
64
+ content: msg.content,
65
+ timestamp: msg.timestamp instanceof Date ? msg.timestamp : new Date(msg.timestamp || Date.now())
66
+ }));
67
+
68
+ this.messagesSubject.next(chatMessages);
69
+ this.logger.info('Loaded session history', {
70
+ sessionId,
71
+ messageCount: chatMessages.length
72
+ });
73
+ } else {
74
+ // 新会话,清空当前消息
75
+ this.messagesSubject.next([]);
76
+ this.logger.info('Started new session', { sessionId });
77
+ }
78
+
53
79
  this.logger.info('Switched to session', { sessionId });
54
80
  }
55
81
 
@@ -325,17 +351,15 @@ export class ChatSessionService {
325
351
  /**
326
352
  * 压缩存储检查点
327
353
  */
328
- compressCheckpoint(checkpointId: string): Checkpoint {
329
- const checkpoint = this.getCheckpoint(checkpointId);
330
- if (!checkpoint) {
331
- throw new Error(`Checkpoint not found: ${checkpointId}`);
354
+ async compressCheckpoint(checkpointId: string): Promise<void> {
355
+ try {
356
+ // 使用 CheckpointManager 的压缩功能
357
+ await this.checkpointManager.compressForCheckpoint(checkpointId);
358
+ this.logger.info('Checkpoint compressed', { checkpointId });
359
+ } catch (error) {
360
+ this.logger.error('Failed to compress checkpoint', { checkpointId, error });
361
+ throw error;
332
362
  }
333
-
334
- // TODO: 实现检查点压缩逻辑
335
- // 这里先返回原检查点,实际实现中可以使用 ContextManager 进行压缩
336
- this.logger.info('Checkpoint compressed', { checkpointId });
337
-
338
- return checkpoint;
339
363
  }
340
364
 
341
365
  /**
@@ -8,6 +8,7 @@ import {
8
8
  PruneResult,
9
9
  TruncationResult
10
10
  } from '../../types/ai.types';
11
+ import { SummaryService } from './summary.service';
11
12
 
12
13
  /**
13
14
  * 压缩算法实现类
@@ -19,7 +20,10 @@ import {
19
20
  export class Compaction {
20
21
  private config: ContextConfig;
21
22
 
22
- constructor(private logger: LoggerService) {
23
+ constructor(
24
+ private logger: LoggerService,
25
+ private summaryService: SummaryService
26
+ ) {
23
27
  this.config = { ...DEFAULT_CONTEXT_CONFIG };
24
28
  }
25
29
 
@@ -67,20 +71,26 @@ export class Compaction {
67
71
  const condenseId = `compact_${Date.now()}`;
68
72
 
69
73
  try {
70
- // 1. 准备摘要输入
71
- const summaryInput = this.formatMessagesForSummary(messages);
74
+ // 使用SummaryService生成AI摘要
75
+ const summaryResult = await this.summaryService.generateSummary(messages);
76
+ const summary = summaryResult.summary;
72
77
 
73
- // 2. 生成摘要(TODO: 调用AI API)
74
- const summary = await this.generateSummary(summaryInput, messages);
75
-
76
- // 3. 创建摘要消息
78
+ // 创建摘要消息,包含元数据
77
79
  const summaryMessage: ApiMessage = {
78
80
  role: 'system',
79
81
  content: summary,
80
82
  ts: Date.now(),
81
83
  isSummary: true,
82
84
  condenseId,
83
- condenseParent: undefined
85
+ condenseParent: undefined,
86
+ summaryMeta: {
87
+ originalMessageCount: summaryResult.originalMessageCount,
88
+ tokensCost: summaryResult.tokensCost,
89
+ compressionRatio: this.summaryService.calculateCompressionRatio(
90
+ summaryResult.originalMessageCount,
91
+ summary.length
92
+ )
93
+ }
84
94
  };
85
95
 
86
96
  // 4. 标记被压缩的消息
@@ -97,7 +107,8 @@ export class Compaction {
97
107
  condenseId,
98
108
  originalTokens,
99
109
  summaryTokens,
100
- tokensSaved
110
+ tokensSaved,
111
+ compressionRatio: summaryMessage.summaryMeta?.compressionRatio
101
112
  });
102
113
 
103
114
  return {
@@ -106,7 +117,7 @@ export class Compaction {
106
117
  summary,
107
118
  condenseId,
108
119
  tokensSaved,
109
- cost: 0 // TODO: 计算实际API成本
120
+ cost: summaryResult.tokensCost
110
121
  };
111
122
 
112
123
  } catch (error) {
@@ -301,162 +312,127 @@ export class Compaction {
301
312
 
302
313
  /**
303
314
  * 裁剪复杂内容(工具调用结果等)
315
+ * 智能裁剪长文本、JSON、代码块
304
316
  */
305
317
  private pruneComplexContent(message: ApiMessage, content: any[]): ApiMessage & { tokensSaved?: number; partsCompacted?: number } {
306
- // TODO: 实现复杂内容的裁剪逻辑
307
- // 例如:工具调用结果的格式化、JSON数据的精简等
318
+ let tokensSaved = 0;
319
+ let partsCompacted = 0;
320
+ const originalLength = JSON.stringify(content).length;
321
+
322
+ // 处理每个内容块
323
+ const prunedContent = content.map(block => {
324
+ // 如果是文本块,尝试智能裁剪
325
+ if (block.type === 'text' && typeof block.text === 'string') {
326
+ const prunedText = this.pruneText(block.text, 500);
327
+ if (prunedText.length < block.text.length) {
328
+ tokensSaved += this.estimateTokens(block.text) - this.estimateTokens(prunedText);
329
+ partsCompacted++;
330
+ return { ...block, text: prunedText };
331
+ }
332
+ }
333
+ // 如果是工具调用结果,尝试精简
334
+ if (block.type === 'tool_use') {
335
+ const prunedTool = this.pruneToolResult(block);
336
+ if (prunedTool !== block) {
337
+ partsCompacted++;
338
+ return prunedTool;
339
+ }
340
+ }
341
+ return block;
342
+ });
343
+
344
+ // 如果内容类型不对,只返回原内容
345
+ if (typeof message.content === 'string') {
346
+ return {
347
+ ...message,
348
+ tokensSaved: 0,
349
+ partsCompacted: 0
350
+ };
351
+ }
352
+
308
353
  return {
309
354
  ...message,
310
- content,
311
- tokensSaved: 0,
312
- partsCompacted: 0
355
+ content: prunedContent,
356
+ tokensSaved,
357
+ partsCompacted
313
358
  };
314
359
  }
315
360
 
316
361
  /**
317
- * 应用Prune结果到消息
362
+ * 智能裁剪文本内容
318
363
  */
319
- private async applyPruneResult(messages: ApiMessage[], pruneResult: PruneResult): Promise<ApiMessage[]> {
320
- // 简化实现:假设prune已经处理了消息
321
- // 实际实现中需要更复杂的逻辑来处理prune结果
322
- return messages;
323
- }
324
-
325
- /**
326
- * 格式化消息用于摘要
327
- */
328
- private formatMessagesForSummary(messages: ApiMessage[]): string {
329
- const formattedMessages = messages.map(msg => {
330
- const content = this.extractTextContent(msg.content);
331
- return `[${msg.role}] ${content}`;
332
- });
333
-
334
- return formattedMessages.join('\n---\n');
335
- }
336
-
337
- /**
338
- * 提取文本内容
339
- */
340
- private extractTextContent(content: string | any[]): string {
341
- if (typeof content === 'string') {
364
+ private pruneText(content: string, maxLength: number = 500): string {
365
+ // 如果内容不超过限制,直接返回
366
+ if (content.length <= maxLength) {
342
367
  return content;
343
368
  }
344
369
 
345
- if (Array.isArray(content)) {
346
- return content
347
- .filter(block => block.type === 'text' && block.text)
348
- .map(block => block.text)
349
- .join('\n');
350
- }
351
-
352
- return '[复杂内容]';
353
- }
354
-
355
- /**
356
- * 生成摘要(集成AI API)
357
- */
358
- private async generateSummary(input: string, messages: ApiMessage[]): Promise<string> {
359
- try {
360
- // TODO: 实现AI摘要生成
361
- // 实际实现中应该调用AI API
362
-
363
- // 临时实现:基于规则的智能摘要
364
- const messageCount = messages.length;
365
- const estimatedTokens = this.calculateTotalTokens(messages);
366
-
367
- // 如果消息数量较少,使用简单摘要
368
- if (messageCount < 5) {
369
- return this.createSimpleSummary(messages);
370
+ // JSON 内容:保留结构概要
371
+ if (content.trim().startsWith('{') || content.trim().startsWith('[')) {
372
+ try {
373
+ const parsed = JSON.parse(content);
374
+ const keys = Object.keys(parsed);
375
+ return `[JSON对象,包含${keys.length}个字段: ${keys.slice(0, 5).join(', ')}${keys.length > 5 ? '...' : ''}]`;
376
+ } catch {
377
+ // 非有效 JSON,继续处理
370
378
  }
379
+ }
371
380
 
372
- // 如果消息数量较多,使用智能摘要
373
- const intelligentSummary = this.createIntelligentSummary(messages);
374
-
375
- return `【AI摘要】压缩了${messageCount}条消息(约${estimatedTokens}个Token)
376
-
377
- ${intelligentSummary}
378
-
379
- --- 上下文压缩完成 ---`;
380
-
381
- } catch (error) {
382
- this.logger.error('Failed to generate AI summary', { error });
383
- // 降级到本地摘要
384
- return this.createFallbackSummary(messages);
381
+ // 代码块:保留头尾
382
+ if (content.includes('\n') && content.split('\n').length > 10) {
383
+ const lines = content.split('\n');
384
+ const head = lines.slice(0, 5).join('\n');
385
+ const tail = lines.slice(-3).join('\n');
386
+ return `${head}\n[...省略${lines.length - 8}行...]\n${tail}`;
385
387
  }
388
+
389
+ // 普通长文本:截断并添加省略标记
390
+ return content.substring(0, maxLength) + '\n[...内容已裁剪...]';
386
391
  }
387
392
 
388
393
  /**
389
- * 创建简单摘要(适用于短对话)
394
+ * 精简工具调用结果
390
395
  */
391
- private createSimpleSummary(messages: ApiMessage[]): string {
392
- const userMessages = messages.filter(m => m.role === 'user');
393
- const assistantMessages = messages.filter(m => m.role === 'assistant');
394
-
395
- if (userMessages.length === 0) {
396
- return '会话摘要:空对话';
396
+ private pruneToolResult(block: any): any {
397
+ // 如果是工具输出块
398
+ if (block.type === 'tool_result' && block.content) {
399
+ let content = block.content;
400
+ if (typeof content === 'string') {
401
+ // 对长文本结果进行裁剪
402
+ if (content.length > 1000) {
403
+ content = content.substring(0, 500) + '\n[...输出已截断...]';
404
+ }
405
+ }
406
+ return { ...block, content };
397
407
  }
398
-
399
- const firstMessage = this.extractTextContent(userMessages[0].content);
400
- const lastMessage = this.extractTextContent(assistantMessages[assistantMessages.length - 1]?.content || '');
401
-
402
- return `【摘要】简短对话:
403
- 用户问题:${firstMessage.substring(0, 80)}${firstMessage.length > 80 ? '...' : ''}
404
- 助手回复:${lastMessage.substring(0, 80)}${lastMessage.length > 80 ? '...' : ''}
405
- 消息总数:${messages.length}条`;
408
+ return block;
406
409
  }
407
410
 
408
411
  /**
409
- * 创建降级摘要(当AI API不可用时)
412
+ * 应用Prune结果到消息
410
413
  */
411
- private createFallbackSummary(messages: ApiMessage[]): string {
412
- const messageCount = messages.length;
413
- const userCount = messages.filter(m => m.role === 'user').length;
414
- const assistantCount = messages.filter(m => m.role === 'assistant').length;
415
- const totalTokens = this.calculateTotalTokens(messages);
416
-
417
- const summary = [
418
- `【压缩摘要】`,
419
- `消息统计:${messageCount}条消息(用户${userCount}条,助手${assistantCount}条)`,
420
- `Token使用:约${totalTokens}个Token`,
421
- ``,
422
- `关键内容提取:`
423
- ];
424
-
425
- // 提取关键消息
426
- const keyMessages = messages
427
- .filter(m => m.role === 'user' && this.extractTextContent(m.content).length > 10)
428
- .slice(0, 3)
429
- .map((m, i) => ` ${i + 1}. ${this.extractTextContent(m.content).substring(0, 60)}...`);
430
-
431
- summary.push(...keyMessages);
432
-
433
- summary.push('', '--- 压缩完成 ---');
434
-
435
- return summary.join('\n');
414
+ private async applyPruneResult(messages: ApiMessage[], pruneResult: PruneResult): Promise<ApiMessage[]> {
415
+ // 简化实现:假设prune已经处理了消息
416
+ // 实际实现中需要更复杂的逻辑来处理prune结果
417
+ return messages;
436
418
  }
437
419
 
438
420
  /**
439
- * 创建智能摘要(简化版)
421
+ * 提取文本内容
440
422
  */
441
- private createIntelligentSummary(messages: ApiMessage[]): string {
442
- const userMessages = messages.filter(m => m.role === 'user');
443
- const assistantMessages = messages.filter(m => m.role === 'assistant');
444
-
445
- let summary = '';
446
-
447
- if (userMessages.length > 0) {
448
- summary += `用户发送了${userMessages.length}条消息\n`;
449
- summary += `主要问题:${this.extractTextContent(userMessages[0].content).substring(0, 100)}...\n`;
423
+ private extractTextContent(content: string | any[]): string {
424
+ if (typeof content === 'string') {
425
+ return content;
450
426
  }
451
427
 
452
- if (assistantMessages.length > 0) {
453
- summary += `助手回复了${assistantMessages.length}条消息\n`;
428
+ if (Array.isArray(content)) {
429
+ return content
430
+ .filter(block => block.type === 'text' && block.text)
431
+ .map(block => block.text)
432
+ .join('\n');
454
433
  }
455
434
 
456
- summary += `会话主题:基于历史消息推断的主题\n`;
457
- summary += `关键决策:基于历史消息提取的关键操作\n`;
458
-
459
- return summary;
435
+ return '[复杂内容]';
460
436
  }
461
437
 
462
438
  /**
@@ -2,6 +2,8 @@ import { Injectable } from '@angular/core';
2
2
  import { LoggerService } from '../core/logger.service';
3
3
  import { ConfigProviderService } from '../core/config-provider.service';
4
4
  import { ChatHistoryService, SavedSession } from '../chat/chat-history.service';
5
+ import { SummaryService } from './summary.service';
6
+ import { calculateCost, formatCost } from '../../utils/cost.utils';
5
7
  import {
6
8
  ApiMessage,
7
9
  ContextConfig,
@@ -9,7 +11,8 @@ import {
9
11
  TokenUsage,
10
12
  CompactionResult,
11
13
  PruneResult,
12
- TruncationResult
14
+ TruncationResult,
15
+ ChatMessage
13
16
  } from '../../types/ai.types';
14
17
 
15
18
  /**
@@ -25,7 +28,8 @@ export class ContextManager {
25
28
  constructor(
26
29
  private logger: LoggerService,
27
30
  private chatHistoryService: ChatHistoryService,
28
- private configService: ConfigProviderService
31
+ private configService: ConfigProviderService,
32
+ private summaryService: SummaryService
29
33
  ) {
30
34
  this.config = { ...DEFAULT_CONTEXT_CONFIG };
31
35
  // 动态获取当前供应商的上下文限制
@@ -353,9 +357,9 @@ export class ContextManager {
353
357
  // 格式化消息用于摘要
354
358
  const summaryInput = this.formatMessagesForSummary(messagesToSummarize);
355
359
 
356
- // TODO: 调用AI API生成摘要
357
- // 这里先使用占位符实现
358
- const summary = `[摘要: ${messagesToSummarize.length}条消息已压缩为摘要,节省约${messagesToSummarize.length * 50}个Token]`;
360
+ // 调用AI API生成摘要
361
+ const summaryResult = await this.summaryService.generateSummary(messagesToSummarize);
362
+ const summary = summaryResult.summary;
359
363
 
360
364
  // 创建摘要消息
361
365
  const summaryMessage: ApiMessage = {
@@ -364,7 +368,13 @@ export class ContextManager {
364
368
  ts: Date.now(),
365
369
  isSummary: true,
366
370
  condenseId,
367
- condenseParent: undefined
371
+ condenseParent: undefined,
372
+ // 记录摘要元数据
373
+ summaryMeta: {
374
+ originalMessageCount: summaryResult.originalMessageCount,
375
+ tokensCost: summaryResult.tokensCost,
376
+ compressionRatio: this.summaryService.calculateCompressionRatio(summaryResult.originalMessageCount, summary.length)
377
+ }
368
378
  };
369
379
 
370
380
  // 标记被压缩的消息
@@ -387,13 +397,23 @@ export class ContextManager {
387
397
  condenseId
388
398
  );
389
399
 
400
+ // 计算API成本
401
+ const provider = this.configService.getDefaultProvider();
402
+ const providerConfig = this.configService.getProviderConfig(provider);
403
+ const model = providerConfig?.model || 'gpt-4o';
404
+ const costResult = calculateCost(
405
+ provider as any,
406
+ model,
407
+ { inputTokens: summaryResult.tokensCost, outputTokens: Math.floor(summaryResult.tokensCost * 0.05) }
408
+ );
409
+
390
410
  return {
391
411
  success: true,
392
412
  messages: resultMessages,
393
413
  summary,
394
414
  condenseId,
395
415
  tokensSaved,
396
- cost: 0 // TODO: 计算实际API成本
416
+ cost: costResult.totalCost
397
417
  };
398
418
 
399
419
  } catch (error) {
@@ -1,6 +1,7 @@
1
1
  import { Injectable } from '@angular/core';
2
2
  import { LoggerService } from '../core/logger.service';
3
3
  import { ApiMessage } from '../../types/ai.types';
4
+ import { SummaryService } from './summary.service';
4
5
 
5
6
  /**
6
7
  * 记忆层类型
@@ -75,7 +76,10 @@ export class Memory {
75
76
  // 重要性阈值
76
77
  private readonly IMPORTANCE_THRESHOLD = 0.7;
77
78
 
78
- constructor(private logger: LoggerService) {
79
+ constructor(
80
+ private logger: LoggerService,
81
+ private summaryService: SummaryService
82
+ ) {
79
83
  this.logger.info('Memory system initialized');
80
84
  this.loadFromStorage();
81
85
  }
@@ -253,8 +257,16 @@ export class Memory {
253
257
  return '无相关记忆';
254
258
  }
255
259
 
256
- // TODO: 使用AI生成摘要
257
- const summary = this.generateIntelligentSummary(sessionMemories);
260
+ // 将记忆转换为ApiMessage格式供AI摘要生成
261
+ const messages: ApiMessage[] = sessionMemories.map(m => ({
262
+ role: m.content.includes('assistant:') ? 'assistant' as const : 'user' as const,
263
+ content: m.content,
264
+ ts: m.metadata.timestamp
265
+ }));
266
+
267
+ // 使用AI生成摘要
268
+ const summaryResult = await this.summaryService.generateSummary(messages);
269
+ const summary = summaryResult.summary;
258
270
 
259
271
  // 保存为中期记忆
260
272
  const midTermId = this.store(summary, MemoryLayer.MID_TERM, {
@@ -266,6 +278,8 @@ export class Memory {
266
278
  this.logger.info('Created mid-term summary', {
267
279
  sessionId,
268
280
  summaryLength: summary.length,
281
+ originalMessageCount: summaryResult.originalMessageCount,
282
+ tokensCost: summaryResult.tokensCost,
269
283
  memoryId: midTermId
270
284
  });
271
285
 
@@ -442,36 +456,6 @@ export class Memory {
442
456
  toDelete.forEach(([id]) => memories.delete(id));
443
457
  }
444
458
 
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
459
  private calculateSimilarity(text1: string, text2: string): number {
476
460
  // 简化的文本相似性计算
477
461
  const words1 = new Set(text1.toLowerCase().split(/\s+/));