@lobehub/chat 1.128.0 → 1.128.1

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 (52) hide show
  1. package/.github/workflows/test.yml +8 -1
  2. package/CHANGELOG.md +25 -0
  3. package/changelog/v1.json +9 -0
  4. package/next.config.ts +8 -1
  5. package/package.json +71 -69
  6. package/packages/context-engine/ARCHITECTURE.md +425 -0
  7. package/packages/context-engine/package.json +40 -0
  8. package/packages/context-engine/src/base/BaseProcessor.ts +87 -0
  9. package/packages/context-engine/src/base/BaseProvider.ts +22 -0
  10. package/packages/context-engine/src/index.ts +32 -0
  11. package/packages/context-engine/src/pipeline.ts +219 -0
  12. package/packages/context-engine/src/processors/HistoryTruncate.ts +76 -0
  13. package/packages/context-engine/src/processors/InputTemplate.ts +83 -0
  14. package/packages/context-engine/src/processors/MessageCleanup.ts +87 -0
  15. package/packages/context-engine/src/processors/MessageContent.ts +298 -0
  16. package/packages/context-engine/src/processors/PlaceholderVariables.ts +196 -0
  17. package/packages/context-engine/src/processors/ToolCall.ts +186 -0
  18. package/packages/context-engine/src/processors/ToolMessageReorder.ts +113 -0
  19. package/packages/context-engine/src/processors/__tests__/HistoryTruncate.test.ts +175 -0
  20. package/packages/context-engine/src/processors/__tests__/InputTemplate.test.ts +243 -0
  21. package/packages/context-engine/src/processors/__tests__/MessageContent.test.ts +394 -0
  22. package/packages/context-engine/src/processors/__tests__/PlaceholderVariables.test.ts +334 -0
  23. package/packages/context-engine/src/processors/__tests__/ToolMessageReorder.test.ts +186 -0
  24. package/packages/context-engine/src/processors/index.ts +15 -0
  25. package/packages/context-engine/src/providers/HistorySummary.ts +102 -0
  26. package/packages/context-engine/src/providers/InboxGuide.ts +102 -0
  27. package/packages/context-engine/src/providers/SystemRoleInjector.ts +64 -0
  28. package/packages/context-engine/src/providers/ToolSystemRole.ts +118 -0
  29. package/packages/context-engine/src/providers/__tests__/HistorySummaryProvider.test.ts +112 -0
  30. package/packages/context-engine/src/providers/__tests__/InboxGuideProvider.test.ts +121 -0
  31. package/packages/context-engine/src/providers/__tests__/SystemRoleInjector.test.ts +200 -0
  32. package/packages/context-engine/src/providers/__tests__/ToolSystemRoleProvider.test.ts +140 -0
  33. package/packages/context-engine/src/providers/index.ts +11 -0
  34. package/packages/context-engine/src/types.ts +201 -0
  35. package/packages/context-engine/vitest.config.mts +10 -0
  36. package/packages/database/package.json +1 -1
  37. package/packages/prompts/src/prompts/systemRole/index.ts +1 -1
  38. package/packages/utils/src/index.ts +2 -0
  39. package/packages/utils/src/uriParser.test.ts +29 -0
  40. package/packages/utils/src/uriParser.ts +24 -0
  41. package/src/services/{__tests__ → chat}/chat.test.ts +22 -1032
  42. package/src/services/chat/clientModelRuntime.test.ts +385 -0
  43. package/src/services/chat/clientModelRuntime.ts +34 -0
  44. package/src/services/chat/contextEngineering.test.ts +848 -0
  45. package/src/services/chat/contextEngineering.ts +123 -0
  46. package/src/services/chat/helper.ts +61 -0
  47. package/src/services/{chat.ts → chat/index.ts} +24 -366
  48. package/src/services/chat/types.ts +9 -0
  49. package/src/services/models.ts +1 -1
  50. package/src/store/aiInfra/slices/aiModel/selectors.ts +2 -2
  51. package/src/store/chat/slices/aiChat/actions/generateAIChat.ts +1 -40
  52. /package/src/services/{__tests__ → chat}/__snapshots__/chat.test.ts.snap +0 -0
@@ -0,0 +1,334 @@
1
+ import { describe, expect, it } from 'vitest';
2
+
3
+ import {
4
+ PlaceholderVariablesProcessor,
5
+ parsePlaceholderVariables,
6
+ parsePlaceholderVariablesMessages,
7
+ } from '../PlaceholderVariables';
8
+
9
+ describe('PlaceholderVariablesProcessor', () => {
10
+ const mockVariableGenerators = {
11
+ date: () => '2023-12-25',
12
+ time: () => '14:30:45',
13
+ username: () => 'TestUser',
14
+ random: () => '12345',
15
+ nested: () => 'Value with {{date}} inside',
16
+ };
17
+
18
+ describe('parsePlaceholderVariables', () => {
19
+ it('should replace simple placeholder variables', () => {
20
+ const text = 'Today is {{date}} and the time is {{time}}';
21
+ const result = parsePlaceholderVariables(text, mockVariableGenerators);
22
+ expect(result).toBe('Today is 2023-12-25 and the time is 14:30:45');
23
+ });
24
+
25
+ it('should handle missing variables gracefully', () => {
26
+ const text = 'Hello {{username}}, missing: {{missing}}';
27
+ const result = parsePlaceholderVariables(text, mockVariableGenerators);
28
+ expect(result).toBe('Hello TestUser, missing: {{missing}}');
29
+ });
30
+
31
+ it('should handle nested variables with recursion', () => {
32
+ const text = 'Nested: {{nested}}';
33
+ const result = parsePlaceholderVariables(text, mockVariableGenerators);
34
+ expect(result).toBe('Nested: Value with 2023-12-25 inside');
35
+ });
36
+
37
+ it('should respect depth limit', () => {
38
+ const text = 'Nested: {{nested}}';
39
+ const result = parsePlaceholderVariables(text, mockVariableGenerators, 1);
40
+ expect(result).toBe('Nested: Value with {{date}} inside');
41
+ });
42
+
43
+ it('should handle empty text', () => {
44
+ const text = '';
45
+ const result = parsePlaceholderVariables(text, mockVariableGenerators);
46
+ expect(result).toBe('');
47
+ });
48
+
49
+ it('should handle text without placeholders', () => {
50
+ const text = 'No placeholders here';
51
+ const result = parsePlaceholderVariables(text, mockVariableGenerators);
52
+ expect(result).toBe('No placeholders here');
53
+ });
54
+ });
55
+
56
+ describe('parsePlaceholderVariablesMessages', () => {
57
+ it('should process string content messages', () => {
58
+ const messages = [
59
+ {
60
+ id: '1',
61
+ role: 'user',
62
+ content: 'Hello {{username}}, today is {{date}}',
63
+ },
64
+ {
65
+ id: '2',
66
+ role: 'assistant',
67
+ content: 'Hi there! The time is {{time}}',
68
+ },
69
+ ];
70
+
71
+ const result = parsePlaceholderVariablesMessages(messages, mockVariableGenerators);
72
+
73
+ expect(result).toEqual([
74
+ {
75
+ id: '1',
76
+ role: 'user',
77
+ content: 'Hello TestUser, today is 2023-12-25',
78
+ },
79
+ {
80
+ id: '2',
81
+ role: 'assistant',
82
+ content: 'Hi there! The time is 14:30:45',
83
+ },
84
+ ]);
85
+ });
86
+
87
+ it('should process array content messages with text parts', () => {
88
+ const messages = [
89
+ {
90
+ id: '1',
91
+ role: 'user',
92
+ content: [
93
+ {
94
+ type: 'text',
95
+ text: 'Hello {{username}}, today is {{date}}',
96
+ },
97
+ {
98
+ type: 'image_url',
99
+ image_url: { url: 'data:image/png;base64,abc123' },
100
+ },
101
+ ],
102
+ },
103
+ ];
104
+
105
+ const result = parsePlaceholderVariablesMessages(messages, mockVariableGenerators);
106
+
107
+ expect(result).toEqual([
108
+ {
109
+ id: '1',
110
+ role: 'user',
111
+ content: [
112
+ {
113
+ type: 'text',
114
+ text: 'Hello TestUser, today is 2023-12-25',
115
+ },
116
+ {
117
+ type: 'image_url',
118
+ image_url: { url: 'data:image/png;base64,abc123' },
119
+ },
120
+ ],
121
+ },
122
+ ]);
123
+ });
124
+
125
+ it('should skip messages without content', () => {
126
+ const messages = [
127
+ {
128
+ id: '1',
129
+ role: 'user',
130
+ },
131
+ {
132
+ id: '2',
133
+ role: 'assistant',
134
+ content: null,
135
+ },
136
+ ];
137
+
138
+ const result = parsePlaceholderVariablesMessages(messages, mockVariableGenerators);
139
+
140
+ expect(result).toEqual(messages);
141
+ });
142
+
143
+ it('should handle mixed content types', () => {
144
+ const messages = [
145
+ {
146
+ id: '1',
147
+ role: 'user',
148
+ content: 'Simple {{username}} message',
149
+ },
150
+ {
151
+ id: '2',
152
+ role: 'user',
153
+ content: [{ type: 'text', text: 'Complex {{date}} message' }],
154
+ },
155
+ {
156
+ id: '3',
157
+ role: 'assistant',
158
+ content: { type: 'object', data: 'not processed' },
159
+ },
160
+ ];
161
+
162
+ const result = parsePlaceholderVariablesMessages(messages, mockVariableGenerators);
163
+
164
+ expect(result).toEqual([
165
+ {
166
+ id: '1',
167
+ role: 'user',
168
+ content: 'Simple TestUser message',
169
+ },
170
+ {
171
+ id: '2',
172
+ role: 'user',
173
+ content: [{ type: 'text', text: 'Complex 2023-12-25 message' }],
174
+ },
175
+ {
176
+ id: '3',
177
+ role: 'assistant',
178
+ content: { type: 'object', data: 'not processed' },
179
+ },
180
+ ]);
181
+ });
182
+ });
183
+
184
+ describe('PlaceholderVariablesProcessor', () => {
185
+ it('should process messages through the processor', async () => {
186
+ const processor = new PlaceholderVariablesProcessor({
187
+ variableGenerators: mockVariableGenerators,
188
+ });
189
+
190
+ const context = {
191
+ initialState: {
192
+ messages: [],
193
+ model: 'gpt-4',
194
+ provider: 'openai',
195
+ systemRole: '',
196
+ tools: [],
197
+ },
198
+ messages: [
199
+ {
200
+ id: '1',
201
+ role: 'user',
202
+ content: 'Hello {{username}}, today is {{date}}',
203
+ createdAt: Date.now(),
204
+ updatedAt: Date.now(),
205
+ },
206
+ ],
207
+ metadata: {
208
+ model: 'gpt-4',
209
+ maxTokens: 4096,
210
+ },
211
+ isAborted: false,
212
+ executedProcessors: [],
213
+ };
214
+
215
+ const result = await processor.process(context);
216
+
217
+ expect(result.messages[0].content).toBe('Hello TestUser, today is 2023-12-25');
218
+ expect(result.metadata.placeholderVariablesProcessed).toBe(1);
219
+ });
220
+
221
+ it('should handle processing errors gracefully', async () => {
222
+ const faultyGenerators = {
223
+ error: () => {
224
+ throw new Error('Generator error');
225
+ },
226
+ working: () => 'works',
227
+ };
228
+
229
+ const processor = new PlaceholderVariablesProcessor({
230
+ variableGenerators: faultyGenerators,
231
+ });
232
+
233
+ const context = {
234
+ initialState: {
235
+ messages: [],
236
+ model: 'gpt-4',
237
+ provider: 'openai',
238
+ systemRole: '',
239
+ tools: [],
240
+ },
241
+ messages: [
242
+ {
243
+ id: '1',
244
+ role: 'user',
245
+ content: 'This {{working}} but this {{error}} fails',
246
+ createdAt: Date.now(),
247
+ updatedAt: Date.now(),
248
+ },
249
+ ],
250
+ metadata: {
251
+ model: 'gpt-4',
252
+ maxTokens: 4096,
253
+ },
254
+ isAborted: false,
255
+ executedProcessors: [],
256
+ };
257
+
258
+ // Should not throw, but continue processing
259
+ const result = await processor.process(context);
260
+ expect(result.messages).toHaveLength(1);
261
+ });
262
+
263
+ it('should use custom depth setting', async () => {
264
+ const processor = new PlaceholderVariablesProcessor({
265
+ variableGenerators: mockVariableGenerators,
266
+ depth: 1,
267
+ });
268
+
269
+ const context = {
270
+ initialState: {
271
+ messages: [],
272
+ model: 'gpt-4',
273
+ provider: 'openai',
274
+ systemRole: '',
275
+ tools: [],
276
+ },
277
+ messages: [
278
+ {
279
+ id: '1',
280
+ role: 'user',
281
+ content: 'Nested: {{nested}}',
282
+ createdAt: Date.now(),
283
+ updatedAt: Date.now(),
284
+ },
285
+ ],
286
+ metadata: {
287
+ model: 'gpt-4',
288
+ maxTokens: 4096,
289
+ },
290
+ isAborted: false,
291
+ executedProcessors: [],
292
+ };
293
+
294
+ const result = await processor.process(context);
295
+
296
+ expect(result.messages[0].content).toBe('Nested: Value with {{date}} inside');
297
+ });
298
+
299
+ it('should not modify messages that do not need processing', async () => {
300
+ const processor = new PlaceholderVariablesProcessor({
301
+ variableGenerators: mockVariableGenerators,
302
+ });
303
+
304
+ const context = {
305
+ initialState: {
306
+ messages: [],
307
+ model: 'gpt-4',
308
+ provider: 'openai',
309
+ systemRole: '',
310
+ tools: [],
311
+ },
312
+ messages: [
313
+ {
314
+ id: '1',
315
+ role: 'user',
316
+ content: 'No variables here',
317
+ createdAt: Date.now(),
318
+ updatedAt: Date.now(),
319
+ },
320
+ ],
321
+ metadata: {
322
+ model: 'gpt-4',
323
+ maxTokens: 4096,
324
+ },
325
+ isAborted: false,
326
+ executedProcessors: [],
327
+ };
328
+
329
+ const result = await processor.process(context);
330
+
331
+ expect(result.metadata.placeholderVariablesProcessed).toBe(0);
332
+ });
333
+ });
334
+ });
@@ -0,0 +1,186 @@
1
+ import { describe, expect, it } from 'vitest';
2
+
3
+ import type { PipelineContext } from '../../types';
4
+ import { ToolMessageReorder } from '../ToolMessageReorder';
5
+
6
+ const createContext = (messages: any[]): PipelineContext => ({
7
+ initialState: { messages: [] } as any,
8
+ messages,
9
+ metadata: { model: 'gpt-4', maxTokens: 4096 },
10
+ isAborted: false,
11
+ });
12
+
13
+ describe('ToolMessageReorder', () => {
14
+ it('should place tool messages right after their assistant calls and drop invalid tools', async () => {
15
+ const proc = new ToolMessageReorder();
16
+ const messages = [
17
+ { id: 'u1', role: 'user', content: 'hi' },
18
+ {
19
+ id: 'a1',
20
+ role: 'assistant',
21
+ content: 'calling',
22
+ tool_calls: [
23
+ { id: 'call_1', type: 'function', function: { name: 'test', arguments: '{}' } },
24
+ ],
25
+ },
26
+ { id: 't1', role: 'tool', content: '{"ok":1}', tool_call_id: 'call_1' },
27
+ { id: 't_invalid', role: 'tool', content: '{"ok":0}' },
28
+ ];
29
+
30
+ const ctx = createContext(messages);
31
+ const res = await proc.process(ctx);
32
+
33
+ expect(res.messages.map((m) => m.id)).toEqual(['u1', 'a1', 't1']);
34
+ });
35
+
36
+ it('should reorderToolMessages', async () => {
37
+ const proc = new ToolMessageReorder();
38
+ const messages = [
39
+ {
40
+ content: '## Tools\n\nYou can use these tools',
41
+ role: 'system',
42
+ },
43
+ {
44
+ content: '',
45
+ role: 'assistant',
46
+ tool_calls: [
47
+ {
48
+ function: {
49
+ arguments:
50
+ '{"query":"LobeChat","searchEngines":["brave","google","duckduckgo","qwant"]}',
51
+ name: 'lobe-web-browsing____searchWithSearXNG____builtin',
52
+ },
53
+ id: 'call_6xCmrOtFOyBAcqpqO1TGfw2B',
54
+ type: 'function',
55
+ },
56
+ {
57
+ function: {
58
+ arguments:
59
+ '{"query":"LobeChat","searchEngines":["brave","google","duckduckgo","qwant"]}',
60
+ name: 'lobe-web-browsing____searchWithSearXNG____builtin',
61
+ },
62
+ id: 'tool_call_nXxXHW8Z',
63
+ type: 'function',
64
+ },
65
+ ],
66
+ },
67
+ {
68
+ content: '[]',
69
+ name: 'lobe-web-browsing____searchWithSearXNG____builtin',
70
+ role: 'tool',
71
+ tool_call_id: 'call_6xCmrOtFOyBAcqpqO1TGfw2B',
72
+ },
73
+ {
74
+ content: 'LobeHub 是一个专注于设计和开发现代人工智能生成内容(AIGC)工具和组件的团队。',
75
+ role: 'assistant',
76
+ },
77
+ {
78
+ content: '[]',
79
+ name: 'lobe-web-browsing____searchWithSearXNG____builtin',
80
+ role: 'tool',
81
+ tool_call_id: 'tool_call_nXxXHW8Z',
82
+ },
83
+ {
84
+ content: '[]',
85
+ name: 'lobe-web-browsing____searchWithSearXNG____builtin',
86
+ role: 'tool',
87
+ tool_call_id: 'tool_call_2f3CEKz9',
88
+ },
89
+ {
90
+ content: '### LobeHub 智能AI聚合神器\n\nLobeHub 是一个强大的AI聚合平台',
91
+ role: 'assistant',
92
+ },
93
+ ];
94
+
95
+ const ctx = createContext(messages);
96
+
97
+ const output = await proc.process(ctx);
98
+
99
+ expect(output.messages).toEqual([
100
+ {
101
+ content: '## Tools\n\nYou can use these tools',
102
+ role: 'system',
103
+ },
104
+ {
105
+ content: '',
106
+ role: 'assistant',
107
+ tool_calls: [
108
+ {
109
+ function: {
110
+ arguments:
111
+ '{"query":"LobeChat","searchEngines":["brave","google","duckduckgo","qwant"]}',
112
+ name: 'lobe-web-browsing____searchWithSearXNG____builtin',
113
+ },
114
+ id: 'call_6xCmrOtFOyBAcqpqO1TGfw2B',
115
+ type: 'function',
116
+ },
117
+ {
118
+ function: {
119
+ arguments:
120
+ '{"query":"LobeChat","searchEngines":["brave","google","duckduckgo","qwant"]}',
121
+ name: 'lobe-web-browsing____searchWithSearXNG____builtin',
122
+ },
123
+ id: 'tool_call_nXxXHW8Z',
124
+ type: 'function',
125
+ },
126
+ ],
127
+ },
128
+ {
129
+ content: '[]',
130
+ name: 'lobe-web-browsing____searchWithSearXNG____builtin',
131
+ role: 'tool',
132
+ tool_call_id: 'call_6xCmrOtFOyBAcqpqO1TGfw2B',
133
+ },
134
+ {
135
+ content: '[]',
136
+ name: 'lobe-web-browsing____searchWithSearXNG____builtin',
137
+ role: 'tool',
138
+ tool_call_id: 'tool_call_nXxXHW8Z',
139
+ },
140
+ {
141
+ content: 'LobeHub 是一个专注于设计和开发现代人工智能生成内容(AIGC)工具和组件的团队。',
142
+ role: 'assistant',
143
+ },
144
+ {
145
+ content: '### LobeHub 智能AI聚合神器\n\nLobeHub 是一个强大的AI聚合平台',
146
+ role: 'assistant',
147
+ },
148
+ ]);
149
+ });
150
+
151
+ it('should correctly reorder when a tool message appears before the assistant message', async () => {
152
+ const messages = [
153
+ {
154
+ role: 'system',
155
+ content: 'System message',
156
+ },
157
+ {
158
+ role: 'tool',
159
+ tool_call_id: 'tool_call_1',
160
+ name: 'test-plugin____testApi',
161
+ content: 'Tool result',
162
+ },
163
+ {
164
+ role: 'assistant',
165
+ content: '',
166
+ tool_calls: [
167
+ { id: 'tool_call_1', type: 'function', function: { name: 'testApi', arguments: '{}' } },
168
+ ],
169
+ },
170
+ ];
171
+
172
+ const proc = new ToolMessageReorder();
173
+
174
+ const ctx = createContext(messages);
175
+
176
+ const { messages: output } = await proc.process(ctx);
177
+
178
+ // Verify reordering logic works and covers line 688 hasPushed check
179
+ // In this test, tool messages are duplicated but the second occurrence is skipped
180
+ expect(output.length).toBe(4); // Original has 3, assistant will add corresponding tool message again
181
+ expect(output[0].role).toBe('system');
182
+ expect(output[1].role).toBe('tool');
183
+ expect(output[2].role).toBe('assistant');
184
+ expect(output[3].role).toBe('tool'); // Tool message added by assistant's tool_calls
185
+ });
186
+ });
@@ -0,0 +1,15 @@
1
+ // Transformer processors
2
+ export { HistoryTruncateProcessor } from './HistoryTruncate';
3
+ export { InputTemplateProcessor } from './InputTemplate';
4
+ export { MessageCleanupProcessor } from './MessageCleanup';
5
+ export { MessageContentProcessor } from './MessageContent';
6
+ export { PlaceholderVariablesProcessor } from './PlaceholderVariables';
7
+ export { ToolCallProcessor } from './ToolCall';
8
+ export { ToolMessageReorder } from './ToolMessageReorder';
9
+
10
+ // Re-export types
11
+ export type { HistoryTruncateConfig } from './HistoryTruncate';
12
+ export type { InputTemplateConfig } from './InputTemplate';
13
+ export type { MessageContentConfig, UserMessageContentPart } from './MessageContent';
14
+ export type { PlaceholderVariablesConfig } from './PlaceholderVariables';
15
+ export type { ToolCallConfig } from './ToolCall';
@@ -0,0 +1,102 @@
1
+ import debug from 'debug';
2
+
3
+ import { BaseProvider } from '../base/BaseProvider';
4
+ import type { PipelineContext, ProcessorOptions } from '../types';
5
+
6
+ const log = debug('context-engine:provider:HistorySummaryProvider');
7
+
8
+ /**
9
+ * History Summary Configuration
10
+ */
11
+ export interface HistorySummaryConfig {
12
+ /** History summary template function */
13
+ formatHistorySummary?: (summary: string) => string;
14
+ /** History summary content */
15
+ historySummary?: string;
16
+ }
17
+
18
+ /**
19
+ * Default history summary formatter function
20
+ */
21
+ const defaultHistorySummaryFormatter = (historySummary: string): string => `<chat_history_summary>
22
+ <docstring>Users may have lots of chat messages, here is the summary of the history:</docstring>
23
+ <summary>${historySummary}</summary>
24
+ </chat_history_summary>`;
25
+
26
+ /**
27
+ * History Summary Provider
28
+ * Responsible for injecting history conversation summary into system messages
29
+ */
30
+ export class HistorySummaryProvider extends BaseProvider {
31
+ readonly name = 'HistorySummaryProvider';
32
+
33
+ constructor(
34
+ private config: HistorySummaryConfig,
35
+ options: ProcessorOptions = {},
36
+ ) {
37
+ super(options);
38
+ }
39
+
40
+ protected async doProcess(context: PipelineContext): Promise<PipelineContext> {
41
+ const clonedContext = this.cloneContext(context);
42
+
43
+ // 检查是否有历史摘要
44
+ if (!this.config.historySummary) {
45
+ log('No history summary content, skipping processing');
46
+ return this.markAsExecuted(clonedContext);
47
+ }
48
+
49
+ // 格式化历史摘要
50
+ const formattedSummary = this.formatHistorySummary(this.config.historySummary);
51
+
52
+ // 注入历史摘要
53
+ this.injectHistorySummary(clonedContext, formattedSummary);
54
+
55
+ // 更新元数据
56
+ clonedContext.metadata.historySummary = {
57
+ formattedLength: formattedSummary.length,
58
+ injected: true,
59
+ originalLength: this.config.historySummary.length,
60
+ };
61
+
62
+ log(
63
+ `History summary injection completed, original length: ${this.config.historySummary.length}, formatted length: ${formattedSummary.length}`,
64
+ );
65
+ return this.markAsExecuted(clonedContext);
66
+ }
67
+
68
+ /**
69
+ * Format history summary
70
+ */
71
+ private formatHistorySummary(historySummary: string): string {
72
+ const formatter = this.config.formatHistorySummary || defaultHistorySummaryFormatter;
73
+ return formatter(historySummary);
74
+ }
75
+
76
+ /**
77
+ * Inject history summary to system message
78
+ */
79
+ private injectHistorySummary(context: PipelineContext, formattedSummary: string): void {
80
+ const existingSystemMessage = context.messages.find((msg) => msg.role === 'system');
81
+
82
+ if (existingSystemMessage) {
83
+ // 合并到现有系统消息
84
+ existingSystemMessage.content = [existingSystemMessage.content, formattedSummary]
85
+ .filter(Boolean)
86
+ .join('\n\n');
87
+
88
+ log(
89
+ `History summary merged to existing system message, final length: ${existingSystemMessage.content.length}`,
90
+ );
91
+ } else {
92
+ // 创建新的系统消息
93
+ const systemMessage = {
94
+ content: formattedSummary,
95
+ role: 'system' as const,
96
+ };
97
+
98
+ context.messages.unshift(systemMessage as any);
99
+ log(`New history summary system message created, content length: ${formattedSummary.length}`);
100
+ }
101
+ }
102
+ }