@lobehub/lobehub 2.0.0-next.49 → 2.0.0-next.50
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/CHANGELOG.md +25 -0
- package/changelog/v1.json +9 -0
- package/package.json +1 -1
- package/packages/context-engine/src/base/BaseProcessor.ts +13 -13
- package/packages/context-engine/src/base/BaseProvider.ts +2 -2
- package/packages/context-engine/src/base/__tests__/BaseProcessor.test.ts +5 -5
- package/packages/context-engine/src/processors/MessageCleanup.ts +6 -6
- package/packages/context-engine/src/processors/MessageContent.ts +17 -17
- package/packages/context-engine/src/processors/PlaceholderVariables.ts +4 -4
- package/packages/context-engine/src/processors/ToolCall.ts +18 -18
- package/packages/context-engine/src/processors/ToolMessageReorder.ts +10 -10
- package/packages/context-engine/src/providers/HistorySummary.ts +6 -6
- package/packages/context-engine/src/providers/ToolSystemRole.ts +7 -7
- package/packages/context-engine/src/tools/ToolsEngine.ts +8 -8
- package/packages/context-engine/src/types.ts +35 -35
- package/packages/utils/src/server/validateRedirectHost.test.ts +352 -0
- package/src/app/[variants]/oauth/consent/[uid]/Consent/index.tsx +0 -2
- package/src/app/[variants]/oauth/consent/[uid]/Login.tsx +0 -2
- package/src/server/services/oidc/index.test.ts +232 -0
- package/src/server/services/oidc/index.ts +24 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,31 @@
|
|
|
2
2
|
|
|
3
3
|
# Changelog
|
|
4
4
|
|
|
5
|
+
## [Version 2.0.0-next.50](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.49...v2.0.0-next.50)
|
|
6
|
+
|
|
7
|
+
<sup>Released on **2025-11-13**</sup>
|
|
8
|
+
|
|
9
|
+
#### 🐛 Bug Fixes
|
|
10
|
+
|
|
11
|
+
- **misc**: Fix oidc accountId mismatch.
|
|
12
|
+
|
|
13
|
+
<br/>
|
|
14
|
+
|
|
15
|
+
<details>
|
|
16
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
17
|
+
|
|
18
|
+
#### What's fixed
|
|
19
|
+
|
|
20
|
+
- **misc**: Fix oidc accountId mismatch, closes [#10058](https://github.com/lobehub/lobe-chat/issues/10058) ([0692ba7](https://github.com/lobehub/lobe-chat/commit/0692ba7))
|
|
21
|
+
|
|
22
|
+
</details>
|
|
23
|
+
|
|
24
|
+
<div align="right">
|
|
25
|
+
|
|
26
|
+
[](#readme-top)
|
|
27
|
+
|
|
28
|
+
</div>
|
|
29
|
+
|
|
5
30
|
## [Version 2.0.0-next.49](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.48...v2.0.0-next.49)
|
|
6
31
|
|
|
7
32
|
<sup>Released on **2025-11-13**</sup>
|
package/changelog/v1.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lobehub/lobehub",
|
|
3
|
-
"version": "2.0.0-next.
|
|
3
|
+
"version": "2.0.0-next.50",
|
|
4
4
|
"description": "LobeHub - an open-source,comprehensive AI Agent framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"framework",
|
|
@@ -2,23 +2,23 @@ import type { ContextProcessor, PipelineContext, ProcessorOptions } from '../typ
|
|
|
2
2
|
import { ProcessorError } from '../types';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
5
|
+
* Base processor abstract class
|
|
6
|
+
* Provides common processor functionality and error handling
|
|
7
7
|
*/
|
|
8
8
|
export abstract class BaseProcessor implements ContextProcessor {
|
|
9
9
|
abstract readonly name: string;
|
|
10
10
|
|
|
11
|
-
//
|
|
11
|
+
// Keep parameters for compatibility with existing subclass constructor signatures, but do no processing
|
|
12
12
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
13
13
|
constructor(_options: ProcessorOptions = {}) {}
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
|
-
*
|
|
16
|
+
* Core processing method - subclasses need to implement
|
|
17
17
|
*/
|
|
18
18
|
protected abstract doProcess(context: PipelineContext): Promise<PipelineContext>;
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
|
-
*
|
|
21
|
+
* Public processing entry point, includes error handling and logging
|
|
22
22
|
*/
|
|
23
23
|
async process(context: PipelineContext): Promise<PipelineContext> {
|
|
24
24
|
try {
|
|
@@ -29,32 +29,32 @@ export abstract class BaseProcessor implements ContextProcessor {
|
|
|
29
29
|
} catch (error) {
|
|
30
30
|
throw new ProcessorError(
|
|
31
31
|
this.name,
|
|
32
|
-
|
|
32
|
+
`Processing failed: ${error}`,
|
|
33
33
|
error instanceof Error ? error : new Error(String(error)),
|
|
34
34
|
);
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
/**
|
|
39
|
-
*
|
|
39
|
+
* Validate input context
|
|
40
40
|
*/
|
|
41
41
|
protected validateInput(context: PipelineContext): void {
|
|
42
42
|
if (!context || !Array.isArray(context.messages)) {
|
|
43
|
-
throw new Error('
|
|
43
|
+
throw new Error('Invalid context');
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
/**
|
|
48
|
-
*
|
|
48
|
+
* Validate output context
|
|
49
49
|
*/
|
|
50
50
|
protected validateOutput(context: PipelineContext): void {
|
|
51
51
|
if (!context || !Array.isArray(context.messages)) {
|
|
52
|
-
throw new Error('
|
|
52
|
+
throw new Error('Invalid output context');
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
/**
|
|
57
|
-
*
|
|
57
|
+
* Safely clone context
|
|
58
58
|
*/
|
|
59
59
|
protected cloneContext(context: PipelineContext): PipelineContext {
|
|
60
60
|
return {
|
|
@@ -65,7 +65,7 @@ export abstract class BaseProcessor implements ContextProcessor {
|
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
/**
|
|
68
|
-
*
|
|
68
|
+
* Abort pipeline processing
|
|
69
69
|
*/
|
|
70
70
|
protected abort(context: PipelineContext, reason: string): PipelineContext {
|
|
71
71
|
return {
|
|
@@ -76,7 +76,7 @@ export abstract class BaseProcessor implements ContextProcessor {
|
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
/**
|
|
79
|
-
*
|
|
79
|
+
* Check if message is empty
|
|
80
80
|
*/
|
|
81
81
|
protected isEmptyMessage(message: string | undefined | null): boolean {
|
|
82
82
|
return !message || message.trim().length === 0;
|
|
@@ -2,10 +2,10 @@ import type { PipelineContext } from '../types';
|
|
|
2
2
|
import { BaseProcessor } from './BaseProcessor';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
5
|
+
* Minimal Provider: constrained to the single responsibility of "injecting system message at the beginning"
|
|
6
6
|
*/
|
|
7
7
|
export abstract class BaseProvider extends BaseProcessor {
|
|
8
|
-
//
|
|
8
|
+
// Subclasses can optionally implement; by default no additional context is built
|
|
9
9
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
10
10
|
protected async buildContext(_context: PipelineContext): Promise<string | null> {
|
|
11
11
|
return null;
|
|
@@ -96,7 +96,7 @@ describe('BaseProcessor', () => {
|
|
|
96
96
|
const invalidContext = { messages: 'not an array' } as any;
|
|
97
97
|
|
|
98
98
|
await expect(processor.process(invalidContext)).rejects.toThrow(ProcessorError);
|
|
99
|
-
await expect(processor.process(invalidContext)).rejects.toThrow('
|
|
99
|
+
await expect(processor.process(invalidContext)).rejects.toThrow('Invalid context');
|
|
100
100
|
});
|
|
101
101
|
|
|
102
102
|
it('should validate output context', async () => {
|
|
@@ -104,7 +104,7 @@ describe('BaseProcessor', () => {
|
|
|
104
104
|
const context = createContext([{ content: 'test', role: 'user' }]);
|
|
105
105
|
|
|
106
106
|
await expect(processor.process(context)).rejects.toThrow(ProcessorError);
|
|
107
|
-
await expect(processor.process(context)).rejects.toThrow('
|
|
107
|
+
await expect(processor.process(context)).rejects.toThrow('Invalid output context');
|
|
108
108
|
});
|
|
109
109
|
|
|
110
110
|
it('should wrap errors in ProcessorError', async () => {
|
|
@@ -145,13 +145,13 @@ describe('BaseProcessor', () => {
|
|
|
145
145
|
it('should reject null context', async () => {
|
|
146
146
|
const processor = new TestProcessor();
|
|
147
147
|
|
|
148
|
-
await expect(processor.process(null as any)).rejects.toThrow('
|
|
148
|
+
await expect(processor.process(null as any)).rejects.toThrow('Invalid context');
|
|
149
149
|
});
|
|
150
150
|
|
|
151
151
|
it('should reject undefined context', async () => {
|
|
152
152
|
const processor = new TestProcessor();
|
|
153
153
|
|
|
154
|
-
await expect(processor.process(undefined as any)).rejects.toThrow('
|
|
154
|
+
await expect(processor.process(undefined as any)).rejects.toThrow('Invalid context');
|
|
155
155
|
});
|
|
156
156
|
|
|
157
157
|
it('should reject context without messages array', async () => {
|
|
@@ -162,7 +162,7 @@ describe('BaseProcessor', () => {
|
|
|
162
162
|
metadata: {},
|
|
163
163
|
} as any;
|
|
164
164
|
|
|
165
|
-
await expect(processor.process(invalidContext)).rejects.toThrow('
|
|
165
|
+
await expect(processor.process(invalidContext)).rejects.toThrow('Invalid context');
|
|
166
166
|
});
|
|
167
167
|
});
|
|
168
168
|
|
|
@@ -6,8 +6,8 @@ import type { PipelineContext, ProcessorOptions } from '../types';
|
|
|
6
6
|
const log = debug('context-engine:processor:MessageCleanupProcessor');
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
|
-
*
|
|
10
|
-
*
|
|
9
|
+
* Message Cleanup Processor
|
|
10
|
+
* Responsible for cleaning up redundant fields in messages, keeping only necessary fields required by OpenAI format
|
|
11
11
|
*/
|
|
12
12
|
export class MessageCleanupProcessor extends BaseProcessor {
|
|
13
13
|
readonly name = 'MessageCleanupProcessor';
|
|
@@ -21,7 +21,7 @@ export class MessageCleanupProcessor extends BaseProcessor {
|
|
|
21
21
|
|
|
22
22
|
let cleanedCount = 0;
|
|
23
23
|
|
|
24
|
-
//
|
|
24
|
+
// Clean each message, keeping only necessary fields
|
|
25
25
|
for (let i = 0; i < clonedContext.messages.length; i++) {
|
|
26
26
|
const message = clonedContext.messages[i];
|
|
27
27
|
const cleanedMessage = this.cleanMessage(message);
|
|
@@ -32,7 +32,7 @@ export class MessageCleanupProcessor extends BaseProcessor {
|
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
//
|
|
35
|
+
// Update metadata
|
|
36
36
|
clonedContext.metadata.messageCleanup = {
|
|
37
37
|
cleanedCount,
|
|
38
38
|
totalMessages: clonedContext.messages.length,
|
|
@@ -43,7 +43,7 @@ export class MessageCleanupProcessor extends BaseProcessor {
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
/**
|
|
46
|
-
*
|
|
46
|
+
* Clean a single message, keeping only necessary fields
|
|
47
47
|
*/
|
|
48
48
|
private cleanMessage(message: any): any {
|
|
49
49
|
switch (message.role) {
|
|
@@ -80,7 +80,7 @@ export class MessageCleanupProcessor extends BaseProcessor {
|
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
default: {
|
|
83
|
-
//
|
|
83
|
+
// For unknown roles, keep as is
|
|
84
84
|
return message;
|
|
85
85
|
}
|
|
86
86
|
}
|
|
@@ -64,7 +64,7 @@ export class MessageContentProcessor extends BaseProcessor {
|
|
|
64
64
|
let userMessagesProcessed = 0;
|
|
65
65
|
let assistantMessagesProcessed = 0;
|
|
66
66
|
|
|
67
|
-
//
|
|
67
|
+
// Process the content of each message
|
|
68
68
|
for (let i = 0; i < clonedContext.messages.length; i++) {
|
|
69
69
|
const message = clonedContext.messages[i];
|
|
70
70
|
|
|
@@ -91,11 +91,11 @@ export class MessageContentProcessor extends BaseProcessor {
|
|
|
91
91
|
}
|
|
92
92
|
} catch (error) {
|
|
93
93
|
log.extend('error')(`Error processing message ${message.id} content: ${error}`);
|
|
94
|
-
//
|
|
94
|
+
// Continue processing other messages
|
|
95
95
|
}
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
-
//
|
|
98
|
+
// Update metadata
|
|
99
99
|
clonedContext.metadata.messageContentProcessed = processedCount;
|
|
100
100
|
clonedContext.metadata.userMessagesProcessed = userMessagesProcessed;
|
|
101
101
|
clonedContext.metadata.assistantMessagesProcessed = assistantMessagesProcessed;
|
|
@@ -163,14 +163,14 @@ export class MessageContentProcessor extends BaseProcessor {
|
|
|
163
163
|
contentParts.push(...videoContentParts);
|
|
164
164
|
}
|
|
165
165
|
|
|
166
|
-
//
|
|
166
|
+
// Explicitly return fields, keeping only necessary message fields
|
|
167
167
|
const hasFileContext = (hasFiles || hasImages || hasVideos) && this.config.fileContext?.enabled;
|
|
168
168
|
const hasVisionContent =
|
|
169
169
|
hasImages && this.config.isCanUseVision?.(this.config.model, this.config.provider);
|
|
170
170
|
const hasVideoContent =
|
|
171
171
|
hasVideos && this.config.isCanUseVideo?.(this.config.model, this.config.provider);
|
|
172
172
|
|
|
173
|
-
//
|
|
173
|
+
// If only text content and no file context added and no vision/video content, return plain text
|
|
174
174
|
if (
|
|
175
175
|
contentParts.length === 1 &&
|
|
176
176
|
contentParts[0].type === 'text' &&
|
|
@@ -185,7 +185,7 @@ export class MessageContentProcessor extends BaseProcessor {
|
|
|
185
185
|
meta: message.meta,
|
|
186
186
|
role: message.role,
|
|
187
187
|
updatedAt: message.updatedAt,
|
|
188
|
-
//
|
|
188
|
+
// Keep other potentially needed fields, but remove processed file-related fields
|
|
189
189
|
...(message.tools && { tools: message.tools }),
|
|
190
190
|
...(message.tool_calls && { tool_calls: message.tool_calls }),
|
|
191
191
|
...(message.tool_call_id && { tool_call_id: message.tool_call_id }),
|
|
@@ -193,7 +193,7 @@ export class MessageContentProcessor extends BaseProcessor {
|
|
|
193
193
|
};
|
|
194
194
|
}
|
|
195
195
|
|
|
196
|
-
//
|
|
196
|
+
// Return structured content
|
|
197
197
|
return {
|
|
198
198
|
content: contentParts,
|
|
199
199
|
createdAt: message.createdAt,
|
|
@@ -201,7 +201,7 @@ export class MessageContentProcessor extends BaseProcessor {
|
|
|
201
201
|
meta: message.meta,
|
|
202
202
|
role: message.role,
|
|
203
203
|
updatedAt: message.updatedAt,
|
|
204
|
-
//
|
|
204
|
+
// Keep other potentially needed fields, but remove processed file-related fields
|
|
205
205
|
...(message.tools && { tools: message.tools }),
|
|
206
206
|
...(message.tool_calls && { tool_calls: message.tool_calls }),
|
|
207
207
|
...(message.tool_call_id && { tool_call_id: message.tool_call_id }),
|
|
@@ -210,10 +210,10 @@ export class MessageContentProcessor extends BaseProcessor {
|
|
|
210
210
|
}
|
|
211
211
|
|
|
212
212
|
/**
|
|
213
|
-
*
|
|
213
|
+
* Process assistant message content
|
|
214
214
|
*/
|
|
215
215
|
private async processAssistantMessage(message: any): Promise<any> {
|
|
216
|
-
//
|
|
216
|
+
// Check if there is reasoning content (thinking mode)
|
|
217
217
|
const shouldIncludeThinking = message.reasoning && !!message.reasoning?.signature;
|
|
218
218
|
|
|
219
219
|
if (shouldIncludeThinking) {
|
|
@@ -235,11 +235,11 @@ export class MessageContentProcessor extends BaseProcessor {
|
|
|
235
235
|
};
|
|
236
236
|
}
|
|
237
237
|
|
|
238
|
-
//
|
|
238
|
+
// Check if there are images (assistant messages may also contain images)
|
|
239
239
|
const hasImages = message.imageList && message.imageList.length > 0;
|
|
240
240
|
|
|
241
241
|
if (hasImages && this.config.isCanUseVision?.(this.config.model, this.config.provider)) {
|
|
242
|
-
//
|
|
242
|
+
// Create structured content
|
|
243
243
|
const contentParts: UserMessageContentPart[] = [];
|
|
244
244
|
|
|
245
245
|
if (message.content) {
|
|
@@ -249,7 +249,7 @@ export class MessageContentProcessor extends BaseProcessor {
|
|
|
249
249
|
});
|
|
250
250
|
}
|
|
251
251
|
|
|
252
|
-
//
|
|
252
|
+
// Process image content
|
|
253
253
|
const imageContentParts = await this.processImageList(message.imageList || []);
|
|
254
254
|
contentParts.push(...imageContentParts);
|
|
255
255
|
|
|
@@ -259,7 +259,7 @@ export class MessageContentProcessor extends BaseProcessor {
|
|
|
259
259
|
};
|
|
260
260
|
}
|
|
261
261
|
|
|
262
|
-
//
|
|
262
|
+
// Regular assistant message, return plain text content
|
|
263
263
|
return {
|
|
264
264
|
...message,
|
|
265
265
|
content: message.content,
|
|
@@ -267,7 +267,7 @@ export class MessageContentProcessor extends BaseProcessor {
|
|
|
267
267
|
}
|
|
268
268
|
|
|
269
269
|
/**
|
|
270
|
-
*
|
|
270
|
+
* Process image list
|
|
271
271
|
*/
|
|
272
272
|
private async processImageList(imageList: any[]): Promise<UserMessageContentPart[]> {
|
|
273
273
|
if (!imageList || imageList.length === 0) {
|
|
@@ -293,7 +293,7 @@ export class MessageContentProcessor extends BaseProcessor {
|
|
|
293
293
|
}
|
|
294
294
|
|
|
295
295
|
/**
|
|
296
|
-
*
|
|
296
|
+
* Process video list
|
|
297
297
|
*/
|
|
298
298
|
private async processVideoList(videoList: any[]): Promise<UserMessageContentPart[]> {
|
|
299
299
|
if (!videoList || videoList.length === 0) {
|
|
@@ -309,7 +309,7 @@ export class MessageContentProcessor extends BaseProcessor {
|
|
|
309
309
|
}
|
|
310
310
|
|
|
311
311
|
/**
|
|
312
|
-
*
|
|
312
|
+
* Validate content part format
|
|
313
313
|
*/
|
|
314
314
|
private validateContentPart(part: UserMessageContentPart): boolean {
|
|
315
315
|
if (!part || !part.type) return false;
|
|
@@ -133,7 +133,7 @@ export class PlaceholderVariablesProcessor extends BaseProcessor {
|
|
|
133
133
|
`Starting placeholder variables processing with ${Object.keys(this.config.variableGenerators).length} generators`,
|
|
134
134
|
);
|
|
135
135
|
|
|
136
|
-
//
|
|
136
|
+
// Process placeholder variables for each message
|
|
137
137
|
for (let i = 0; i < clonedContext.messages.length; i++) {
|
|
138
138
|
const message = clonedContext.messages[i];
|
|
139
139
|
|
|
@@ -148,11 +148,11 @@ export class PlaceholderVariablesProcessor extends BaseProcessor {
|
|
|
148
148
|
}
|
|
149
149
|
} catch (error) {
|
|
150
150
|
log.extend('error')(`Error processing placeholders in message ${message.id}: ${error}`);
|
|
151
|
-
//
|
|
151
|
+
// Continue processing other messages
|
|
152
152
|
}
|
|
153
153
|
}
|
|
154
154
|
|
|
155
|
-
//
|
|
155
|
+
// Update metadata
|
|
156
156
|
clonedContext.metadata.placeholderVariablesProcessed = processedCount;
|
|
157
157
|
|
|
158
158
|
log(`Placeholder variables processing completed, processed ${processedCount} messages`);
|
|
@@ -161,7 +161,7 @@ export class PlaceholderVariablesProcessor extends BaseProcessor {
|
|
|
161
161
|
}
|
|
162
162
|
|
|
163
163
|
/**
|
|
164
|
-
*
|
|
164
|
+
* Process placeholder variables for a single message
|
|
165
165
|
*/
|
|
166
166
|
private processMessagePlaceholders(message: any, depth: number): any {
|
|
167
167
|
if (!message?.content) return message;
|
|
@@ -41,7 +41,7 @@ export class ToolCallProcessor extends BaseProcessor {
|
|
|
41
41
|
let toolCallsConverted = 0;
|
|
42
42
|
let toolMessagesConverted = 0;
|
|
43
43
|
|
|
44
|
-
//
|
|
44
|
+
// Process tool calls for each message
|
|
45
45
|
for (let i = 0; i < clonedContext.messages.length; i++) {
|
|
46
46
|
const message = clonedContext.messages[i];
|
|
47
47
|
|
|
@@ -52,7 +52,7 @@ export class ToolCallProcessor extends BaseProcessor {
|
|
|
52
52
|
processedCount++;
|
|
53
53
|
clonedContext.messages[i] = updatedMessage;
|
|
54
54
|
|
|
55
|
-
//
|
|
55
|
+
// Count converted tool calls and tool messages
|
|
56
56
|
if (message.role === 'assistant' && message.tools) {
|
|
57
57
|
toolCallsConverted += message.tools.length;
|
|
58
58
|
}
|
|
@@ -60,15 +60,15 @@ export class ToolCallProcessor extends BaseProcessor {
|
|
|
60
60
|
toolMessagesConverted++;
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
log(
|
|
63
|
+
log(`Processed message ${message.id}, role: ${message.role}`);
|
|
64
64
|
}
|
|
65
65
|
} catch (error) {
|
|
66
|
-
log.extend('error')(
|
|
67
|
-
//
|
|
66
|
+
log.extend('error')(`Error processing tool call in message ${message.id}: ${error}`);
|
|
67
|
+
// Continue processing other messages
|
|
68
68
|
}
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
//
|
|
71
|
+
// Update metadata
|
|
72
72
|
clonedContext.metadata.toolCallProcessed = processedCount;
|
|
73
73
|
clonedContext.metadata.toolCallsConverted = toolCallsConverted;
|
|
74
74
|
clonedContext.metadata.toolMessagesConverted = toolMessagesConverted;
|
|
@@ -82,7 +82,7 @@ export class ToolCallProcessor extends BaseProcessor {
|
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
/**
|
|
85
|
-
*
|
|
85
|
+
* Process tool calls for a single message
|
|
86
86
|
*/
|
|
87
87
|
private async processMessage(message: any, supportTools: boolean): Promise<any> {
|
|
88
88
|
switch (message.role) {
|
|
@@ -101,26 +101,26 @@ export class ToolCallProcessor extends BaseProcessor {
|
|
|
101
101
|
}
|
|
102
102
|
|
|
103
103
|
/**
|
|
104
|
-
*
|
|
104
|
+
* Process tool calls in assistant message
|
|
105
105
|
*/
|
|
106
106
|
private processAssistantMessage(message: any, supportTools: boolean): any {
|
|
107
|
-
//
|
|
107
|
+
// Check if there are tool calls
|
|
108
108
|
const hasTools = message.tools && message.tools.length > 0;
|
|
109
109
|
const hasEmptyToolCalls = message.tool_calls && message.tool_calls.length === 0;
|
|
110
110
|
|
|
111
111
|
if (!supportTools || (!hasTools && hasEmptyToolCalls)) {
|
|
112
|
-
//
|
|
112
|
+
// If tools not supported or only has empty tool calls, return regular message (remove tool-related properties)
|
|
113
113
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
114
114
|
const { tools, tool_calls, ...messageWithoutTools } = message;
|
|
115
115
|
return messageWithoutTools;
|
|
116
116
|
}
|
|
117
117
|
|
|
118
118
|
if (!hasTools) {
|
|
119
|
-
//
|
|
119
|
+
// If no tools but has other tool call properties, only remove tools
|
|
120
120
|
return message;
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
-
//
|
|
123
|
+
// Convert tools to tool_calls format
|
|
124
124
|
const tool_calls = message.tools.map(
|
|
125
125
|
(tool: any): MessageToolCall => ({
|
|
126
126
|
function: {
|
|
@@ -138,11 +138,11 @@ export class ToolCallProcessor extends BaseProcessor {
|
|
|
138
138
|
}
|
|
139
139
|
|
|
140
140
|
/**
|
|
141
|
-
*
|
|
141
|
+
* Process tool message
|
|
142
142
|
*/
|
|
143
143
|
private processToolMessage(message: any, supportTools: boolean): any {
|
|
144
144
|
if (!supportTools) {
|
|
145
|
-
//
|
|
145
|
+
// If tools not supported, convert tool message to user message
|
|
146
146
|
return {
|
|
147
147
|
...message,
|
|
148
148
|
name: undefined,
|
|
@@ -152,7 +152,7 @@ export class ToolCallProcessor extends BaseProcessor {
|
|
|
152
152
|
};
|
|
153
153
|
}
|
|
154
154
|
|
|
155
|
-
//
|
|
155
|
+
// Generate tool name
|
|
156
156
|
const toolName = message.plugin
|
|
157
157
|
? this.config.genToolCallingName
|
|
158
158
|
? this.config.genToolCallingName(
|
|
@@ -166,19 +166,19 @@ export class ToolCallProcessor extends BaseProcessor {
|
|
|
166
166
|
return {
|
|
167
167
|
...message,
|
|
168
168
|
name: toolName,
|
|
169
|
-
//
|
|
169
|
+
// Keep tool_call_id for association
|
|
170
170
|
};
|
|
171
171
|
}
|
|
172
172
|
|
|
173
173
|
/**
|
|
174
|
-
*
|
|
174
|
+
* Validate tool call format
|
|
175
175
|
*/
|
|
176
176
|
private validateToolCall(tool: any): boolean {
|
|
177
177
|
return !!(tool && tool.id && tool.identifier && tool.apiName && tool.arguments);
|
|
178
178
|
}
|
|
179
179
|
|
|
180
180
|
/**
|
|
181
|
-
*
|
|
181
|
+
* Validate tool message format
|
|
182
182
|
*/
|
|
183
183
|
private validateToolMessage(message: any): boolean {
|
|
184
184
|
return !!(message && message.tool_call_id && message.content !== undefined);
|
|
@@ -19,7 +19,7 @@ export class ToolMessageReorder extends BaseProcessor {
|
|
|
19
19
|
protected async doProcess(context: PipelineContext): Promise<PipelineContext> {
|
|
20
20
|
const clonedContext = this.cloneContext(context);
|
|
21
21
|
|
|
22
|
-
//
|
|
22
|
+
// Reorder messages
|
|
23
23
|
const reorderedMessages = this.reorderToolMessages(clonedContext.messages);
|
|
24
24
|
|
|
25
25
|
const originalCount = clonedContext.messages.length;
|
|
@@ -27,7 +27,7 @@ export class ToolMessageReorder extends BaseProcessor {
|
|
|
27
27
|
|
|
28
28
|
clonedContext.messages = reorderedMessages;
|
|
29
29
|
|
|
30
|
-
//
|
|
30
|
+
// Update metadata
|
|
31
31
|
clonedContext.metadata.toolMessageReorder = {
|
|
32
32
|
originalCount,
|
|
33
33
|
removedInvalidTools: originalCount - reorderedCount,
|
|
@@ -48,10 +48,10 @@ export class ToolMessageReorder extends BaseProcessor {
|
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
/**
|
|
51
|
-
*
|
|
51
|
+
* Reorder tool messages
|
|
52
52
|
*/
|
|
53
53
|
private reorderToolMessages(messages: any[]): any[] {
|
|
54
|
-
// 1.
|
|
54
|
+
// 1. First collect all valid tool_call_ids from assistant messages
|
|
55
55
|
const validToolCallIds = new Set<string>();
|
|
56
56
|
messages.forEach((message) => {
|
|
57
57
|
if (message.role === 'assistant' && message.tool_calls) {
|
|
@@ -61,7 +61,7 @@ export class ToolMessageReorder extends BaseProcessor {
|
|
|
61
61
|
}
|
|
62
62
|
});
|
|
63
63
|
|
|
64
|
-
// 2.
|
|
64
|
+
// 2. Collect all valid tool messages
|
|
65
65
|
const toolMessages: Record<string, any> = {};
|
|
66
66
|
messages.forEach((message) => {
|
|
67
67
|
if (
|
|
@@ -73,10 +73,10 @@ export class ToolMessageReorder extends BaseProcessor {
|
|
|
73
73
|
}
|
|
74
74
|
});
|
|
75
75
|
|
|
76
|
-
// 3.
|
|
76
|
+
// 3. Reorder messages
|
|
77
77
|
const reorderedMessages: any[] = [];
|
|
78
78
|
messages.forEach((message) => {
|
|
79
|
-
//
|
|
79
|
+
// Skip invalid tool messages
|
|
80
80
|
if (
|
|
81
81
|
message.role === 'tool' &&
|
|
82
82
|
(!message.tool_call_id || !validToolCallIds.has(message.tool_call_id))
|
|
@@ -85,7 +85,7 @@ export class ToolMessageReorder extends BaseProcessor {
|
|
|
85
85
|
return;
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
-
//
|
|
88
|
+
// Check if this tool message has already been added
|
|
89
89
|
const hasPushed = reorderedMessages.some(
|
|
90
90
|
(m) => !!message.tool_call_id && m.tool_call_id === message.tool_call_id,
|
|
91
91
|
);
|
|
@@ -94,7 +94,7 @@ export class ToolMessageReorder extends BaseProcessor {
|
|
|
94
94
|
|
|
95
95
|
reorderedMessages.push(message);
|
|
96
96
|
|
|
97
|
-
//
|
|
97
|
+
// If assistant message with tool_calls, add corresponding tool messages
|
|
98
98
|
if (message.role === 'assistant' && message.tool_calls) {
|
|
99
99
|
message.tool_calls.forEach((toolCall: any) => {
|
|
100
100
|
const correspondingToolMessage = toolMessages[toolCall.id];
|
|
@@ -109,5 +109,5 @@ export class ToolMessageReorder extends BaseProcessor {
|
|
|
109
109
|
return reorderedMessages;
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
-
//
|
|
112
|
+
// Simplified: removed validation/statistics helper methods
|
|
113
113
|
}
|
|
@@ -40,19 +40,19 @@ export class HistorySummaryProvider extends BaseProvider {
|
|
|
40
40
|
protected async doProcess(context: PipelineContext): Promise<PipelineContext> {
|
|
41
41
|
const clonedContext = this.cloneContext(context);
|
|
42
42
|
|
|
43
|
-
//
|
|
43
|
+
// Check if history summary exists
|
|
44
44
|
if (!this.config.historySummary) {
|
|
45
45
|
log('No history summary content, skipping processing');
|
|
46
46
|
return this.markAsExecuted(clonedContext);
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
//
|
|
49
|
+
// Format history summary
|
|
50
50
|
const formattedSummary = this.formatHistorySummary(this.config.historySummary);
|
|
51
51
|
|
|
52
|
-
//
|
|
52
|
+
// Inject history summary
|
|
53
53
|
this.injectHistorySummary(clonedContext, formattedSummary);
|
|
54
54
|
|
|
55
|
-
//
|
|
55
|
+
// Update metadata
|
|
56
56
|
clonedContext.metadata.historySummary = {
|
|
57
57
|
formattedLength: formattedSummary.length,
|
|
58
58
|
injected: true,
|
|
@@ -80,7 +80,7 @@ export class HistorySummaryProvider extends BaseProvider {
|
|
|
80
80
|
const existingSystemMessage = context.messages.find((msg) => msg.role === 'system');
|
|
81
81
|
|
|
82
82
|
if (existingSystemMessage) {
|
|
83
|
-
//
|
|
83
|
+
// Merge to existing system message
|
|
84
84
|
existingSystemMessage.content = [existingSystemMessage.content, formattedSummary]
|
|
85
85
|
.filter(Boolean)
|
|
86
86
|
.join('\n\n');
|
|
@@ -89,7 +89,7 @@ export class HistorySummaryProvider extends BaseProvider {
|
|
|
89
89
|
`History summary merged to existing system message, final length: ${existingSystemMessage.content.length}`,
|
|
90
90
|
);
|
|
91
91
|
} else {
|
|
92
|
-
//
|
|
92
|
+
// Create new system message
|
|
93
93
|
const systemMessage = {
|
|
94
94
|
content: formattedSummary,
|
|
95
95
|
role: 'system' as const,
|