@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.
- package/.github/workflows/test.yml +8 -1
- package/CHANGELOG.md +25 -0
- package/changelog/v1.json +9 -0
- package/next.config.ts +8 -1
- package/package.json +71 -69
- package/packages/context-engine/ARCHITECTURE.md +425 -0
- package/packages/context-engine/package.json +40 -0
- package/packages/context-engine/src/base/BaseProcessor.ts +87 -0
- package/packages/context-engine/src/base/BaseProvider.ts +22 -0
- package/packages/context-engine/src/index.ts +32 -0
- package/packages/context-engine/src/pipeline.ts +219 -0
- package/packages/context-engine/src/processors/HistoryTruncate.ts +76 -0
- package/packages/context-engine/src/processors/InputTemplate.ts +83 -0
- package/packages/context-engine/src/processors/MessageCleanup.ts +87 -0
- package/packages/context-engine/src/processors/MessageContent.ts +298 -0
- package/packages/context-engine/src/processors/PlaceholderVariables.ts +196 -0
- package/packages/context-engine/src/processors/ToolCall.ts +186 -0
- package/packages/context-engine/src/processors/ToolMessageReorder.ts +113 -0
- package/packages/context-engine/src/processors/__tests__/HistoryTruncate.test.ts +175 -0
- package/packages/context-engine/src/processors/__tests__/InputTemplate.test.ts +243 -0
- package/packages/context-engine/src/processors/__tests__/MessageContent.test.ts +394 -0
- package/packages/context-engine/src/processors/__tests__/PlaceholderVariables.test.ts +334 -0
- package/packages/context-engine/src/processors/__tests__/ToolMessageReorder.test.ts +186 -0
- package/packages/context-engine/src/processors/index.ts +15 -0
- package/packages/context-engine/src/providers/HistorySummary.ts +102 -0
- package/packages/context-engine/src/providers/InboxGuide.ts +102 -0
- package/packages/context-engine/src/providers/SystemRoleInjector.ts +64 -0
- package/packages/context-engine/src/providers/ToolSystemRole.ts +118 -0
- package/packages/context-engine/src/providers/__tests__/HistorySummaryProvider.test.ts +112 -0
- package/packages/context-engine/src/providers/__tests__/InboxGuideProvider.test.ts +121 -0
- package/packages/context-engine/src/providers/__tests__/SystemRoleInjector.test.ts +200 -0
- package/packages/context-engine/src/providers/__tests__/ToolSystemRoleProvider.test.ts +140 -0
- package/packages/context-engine/src/providers/index.ts +11 -0
- package/packages/context-engine/src/types.ts +201 -0
- package/packages/context-engine/vitest.config.mts +10 -0
- package/packages/database/package.json +1 -1
- package/packages/prompts/src/prompts/systemRole/index.ts +1 -1
- package/packages/utils/src/index.ts +2 -0
- package/packages/utils/src/uriParser.test.ts +29 -0
- package/packages/utils/src/uriParser.ts +24 -0
- package/src/services/{__tests__ → chat}/chat.test.ts +22 -1032
- package/src/services/chat/clientModelRuntime.test.ts +385 -0
- package/src/services/chat/clientModelRuntime.ts +34 -0
- package/src/services/chat/contextEngineering.test.ts +848 -0
- package/src/services/chat/contextEngineering.ts +123 -0
- package/src/services/chat/helper.ts +61 -0
- package/src/services/{chat.ts → chat/index.ts} +24 -366
- package/src/services/chat/types.ts +9 -0
- package/src/services/models.ts +1 -1
- package/src/store/aiInfra/slices/aiModel/selectors.ts +2 -2
- package/src/store/chat/slices/aiChat/actions/generateAIChat.ts +1 -40
- /package/src/services/{__tests__ → chat}/__snapshots__/chat.test.ts.snap +0 -0
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import debug from 'debug';
|
|
2
|
+
|
|
3
|
+
import type {
|
|
4
|
+
AgentState,
|
|
5
|
+
ContextProcessor,
|
|
6
|
+
PipelineContext,
|
|
7
|
+
PipelineResult,
|
|
8
|
+
ProcessorOptions,
|
|
9
|
+
} from './types';
|
|
10
|
+
import { PipelineError } from './types';
|
|
11
|
+
|
|
12
|
+
const log = debug('context-engine:ContextEngine');
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Context Engine Configuration
|
|
16
|
+
*/
|
|
17
|
+
export interface ContextEngineConfig extends ProcessorOptions {
|
|
18
|
+
/** Processor pipeline */
|
|
19
|
+
pipeline: ContextProcessor[];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Context Engine - Core orchestrator that executes processors sequentially
|
|
24
|
+
*/
|
|
25
|
+
export class ContextEngine {
|
|
26
|
+
private processors: ContextProcessor[] = [];
|
|
27
|
+
private options: ProcessorOptions;
|
|
28
|
+
|
|
29
|
+
constructor(config: ContextEngineConfig) {
|
|
30
|
+
const { pipeline, ...options } = config;
|
|
31
|
+
this.processors = [...pipeline];
|
|
32
|
+
this.options = {
|
|
33
|
+
debug: false,
|
|
34
|
+
logger: console.log,
|
|
35
|
+
...options,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Add processor to pipeline
|
|
41
|
+
*/
|
|
42
|
+
addProcessor(processor: ContextProcessor): this {
|
|
43
|
+
this.processors.push(processor);
|
|
44
|
+
return this;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Remove processor
|
|
49
|
+
*/
|
|
50
|
+
removeProcessor(name: string): this {
|
|
51
|
+
this.processors = this.processors.filter((p) => p.name !== name);
|
|
52
|
+
return this;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Get processor list
|
|
57
|
+
*/
|
|
58
|
+
getProcessors(): ContextProcessor[] {
|
|
59
|
+
return [...this.processors];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Clear all processors
|
|
64
|
+
*/
|
|
65
|
+
clear(): this {
|
|
66
|
+
this.processors = [];
|
|
67
|
+
return this;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Execute pipeline processing
|
|
72
|
+
*/
|
|
73
|
+
async process(input: {
|
|
74
|
+
initialState: AgentState;
|
|
75
|
+
maxTokens: number;
|
|
76
|
+
messages?: Array<any>;
|
|
77
|
+
metadata?: Record<string, any>;
|
|
78
|
+
model: string;
|
|
79
|
+
}): Promise<PipelineResult> {
|
|
80
|
+
const startTime = Date.now();
|
|
81
|
+
const processorDurations: Record<string, number> = {};
|
|
82
|
+
|
|
83
|
+
// Create initial pipeline context
|
|
84
|
+
let context: PipelineContext = {
|
|
85
|
+
initialState: input.initialState,
|
|
86
|
+
isAborted: false,
|
|
87
|
+
messages: Array.isArray(input.messages) ? [...input.messages] : [],
|
|
88
|
+
metadata: {
|
|
89
|
+
maxTokens: input.maxTokens,
|
|
90
|
+
model: input.model,
|
|
91
|
+
...input.metadata,
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
log('Starting pipeline processing');
|
|
96
|
+
log('Number of processors:', this.processors.length);
|
|
97
|
+
|
|
98
|
+
let processedCount = 0;
|
|
99
|
+
|
|
100
|
+
try {
|
|
101
|
+
// Execute each processor in sequence
|
|
102
|
+
for (const processor of this.processors) {
|
|
103
|
+
if (context.isAborted) {
|
|
104
|
+
log('Pipeline aborted before processor', processor.name, 'reason:', context.abortReason);
|
|
105
|
+
break;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const processorStartTime = Date.now();
|
|
109
|
+
log('Executing processor:', processor.name);
|
|
110
|
+
|
|
111
|
+
try {
|
|
112
|
+
context = await processor.process(context);
|
|
113
|
+
processedCount++;
|
|
114
|
+
|
|
115
|
+
const duration = Date.now() - processorStartTime;
|
|
116
|
+
processorDurations[processor.name] = duration;
|
|
117
|
+
|
|
118
|
+
log('Processor', processor.name, 'completed in', duration + 'ms');
|
|
119
|
+
|
|
120
|
+
if (context.isAborted) {
|
|
121
|
+
log('Pipeline aborted by processor', processor.name, 'reason:', context.abortReason);
|
|
122
|
+
break;
|
|
123
|
+
}
|
|
124
|
+
} catch (error) {
|
|
125
|
+
const duration = Date.now() - processorStartTime;
|
|
126
|
+
processorDurations[processor.name] = duration;
|
|
127
|
+
|
|
128
|
+
log('Processor', processor.name, 'execution failed:', error);
|
|
129
|
+
throw new PipelineError(
|
|
130
|
+
`Processor [${processor.name}] execution failed`,
|
|
131
|
+
processor.name,
|
|
132
|
+
error instanceof Error ? error : new Error(String(error)),
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const totalDuration = Date.now() - startTime;
|
|
138
|
+
log('Pipeline processing completed in', totalDuration + 'ms');
|
|
139
|
+
|
|
140
|
+
return {
|
|
141
|
+
abortReason: context.abortReason,
|
|
142
|
+
isAborted: context.isAborted,
|
|
143
|
+
messages: context.messages,
|
|
144
|
+
metadata: context.metadata,
|
|
145
|
+
stats: {
|
|
146
|
+
processedCount,
|
|
147
|
+
processorDurations,
|
|
148
|
+
totalDuration,
|
|
149
|
+
},
|
|
150
|
+
};
|
|
151
|
+
} catch (error) {
|
|
152
|
+
log('Pipeline processing failed:', error);
|
|
153
|
+
|
|
154
|
+
if (error instanceof PipelineError) {
|
|
155
|
+
throw error;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
throw new PipelineError(
|
|
159
|
+
'Unknown error occurred during pipeline processing',
|
|
160
|
+
undefined,
|
|
161
|
+
error instanceof Error ? error : new Error(String(error)),
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Get pipeline statistics
|
|
168
|
+
*/
|
|
169
|
+
getStats() {
|
|
170
|
+
return {
|
|
171
|
+
processorCount: this.processors.length,
|
|
172
|
+
processorNames: this.processors.map((p) => p.name),
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Clone pipeline (deep copy processor list)
|
|
178
|
+
*/
|
|
179
|
+
clone(): ContextEngine {
|
|
180
|
+
return new ContextEngine({
|
|
181
|
+
pipeline: [...this.processors],
|
|
182
|
+
...this.options,
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Validate pipeline configuration
|
|
188
|
+
*/
|
|
189
|
+
validate(): { errors: string[]; valid: boolean } {
|
|
190
|
+
const errors: string[] = [];
|
|
191
|
+
|
|
192
|
+
// Check for duplicate processor names
|
|
193
|
+
const names = this.processors.map((p) => p.name);
|
|
194
|
+
const duplicates = names.filter((name, index) => names.indexOf(name) !== index);
|
|
195
|
+
if (duplicates.length > 0) {
|
|
196
|
+
errors.push(`Found duplicate processor names: ${duplicates.join(', ')}`);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Check if processors are empty
|
|
200
|
+
if (this.processors.length === 0) {
|
|
201
|
+
errors.push('No processors in pipeline');
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Check if processors implement required methods
|
|
205
|
+
this.processors.forEach((processor) => {
|
|
206
|
+
if (!processor.name) {
|
|
207
|
+
errors.push('Processor missing name');
|
|
208
|
+
}
|
|
209
|
+
if (typeof processor.process !== 'function') {
|
|
210
|
+
errors.push(`Processor [${processor.name}] missing process method`);
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
return {
|
|
215
|
+
errors,
|
|
216
|
+
valid: errors.length === 0,
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import debug from 'debug';
|
|
2
|
+
|
|
3
|
+
import { BaseProcessor } from '../base/BaseProcessor';
|
|
4
|
+
import type { PipelineContext, ProcessorOptions } from '../types';
|
|
5
|
+
|
|
6
|
+
const log = debug('context-engine:processor:HistoryTruncateProcessor');
|
|
7
|
+
|
|
8
|
+
export interface HistoryTruncateConfig {
|
|
9
|
+
/** Whether to enable history count limit */
|
|
10
|
+
enableHistoryCount?: boolean;
|
|
11
|
+
/** Maximum number of historical messages to keep */
|
|
12
|
+
historyCount?: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Slice messages based on history count configuration
|
|
17
|
+
* @param messages Original messages array
|
|
18
|
+
* @param options Configuration options for slicing
|
|
19
|
+
* @returns Sliced messages array
|
|
20
|
+
*/
|
|
21
|
+
export const getSlicedMessages = (
|
|
22
|
+
messages: any[],
|
|
23
|
+
options: {
|
|
24
|
+
enableHistoryCount?: boolean;
|
|
25
|
+
historyCount?: number;
|
|
26
|
+
},
|
|
27
|
+
): any[] => {
|
|
28
|
+
// if historyCount is not enabled, return all messages
|
|
29
|
+
if (!options.enableHistoryCount || options.historyCount === undefined) return messages;
|
|
30
|
+
|
|
31
|
+
// if historyCount is negative or set to 0, return empty array
|
|
32
|
+
if (options.historyCount <= 0) return [];
|
|
33
|
+
|
|
34
|
+
// if historyCount is positive, return last N messages
|
|
35
|
+
return messages.slice(-options.historyCount);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* History Truncate Processor
|
|
40
|
+
* Responsible for limiting message history based on configuration
|
|
41
|
+
*/
|
|
42
|
+
export class HistoryTruncateProcessor extends BaseProcessor {
|
|
43
|
+
readonly name = 'HistoryTruncateProcessor';
|
|
44
|
+
|
|
45
|
+
constructor(
|
|
46
|
+
private config: HistoryTruncateConfig,
|
|
47
|
+
options: ProcessorOptions = {},
|
|
48
|
+
) {
|
|
49
|
+
super(options);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
protected async doProcess(context: PipelineContext): Promise<PipelineContext> {
|
|
53
|
+
const clonedContext = this.cloneContext(context);
|
|
54
|
+
|
|
55
|
+
const originalCount = clonedContext.messages.length;
|
|
56
|
+
|
|
57
|
+
// Apply history truncation
|
|
58
|
+
clonedContext.messages = getSlicedMessages(clonedContext.messages, {
|
|
59
|
+
enableHistoryCount: this.config.enableHistoryCount,
|
|
60
|
+
historyCount: this.config.historyCount,
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
const finalCount = clonedContext.messages.length;
|
|
64
|
+
const truncatedCount = originalCount - finalCount;
|
|
65
|
+
|
|
66
|
+
// Update metadata
|
|
67
|
+
clonedContext.metadata.historyTruncated = truncatedCount;
|
|
68
|
+
clonedContext.metadata.finalMessageCount = finalCount;
|
|
69
|
+
|
|
70
|
+
log(
|
|
71
|
+
`History truncation completed, truncated ${truncatedCount} messages (${originalCount} → ${finalCount})`,
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
return this.markAsExecuted(clonedContext);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import debug from 'debug';
|
|
2
|
+
import { template } from 'lodash-es';
|
|
3
|
+
|
|
4
|
+
import { BaseProcessor } from '../base/BaseProcessor';
|
|
5
|
+
import type { PipelineContext, ProcessorOptions } from '../types';
|
|
6
|
+
|
|
7
|
+
const log = debug('context-engine:processor:InputTemplateProcessor');
|
|
8
|
+
|
|
9
|
+
export interface InputTemplateConfig {
|
|
10
|
+
/** Input message template string */
|
|
11
|
+
inputTemplate?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Input Template Processor
|
|
16
|
+
* Responsible for applying input message templates to user messages
|
|
17
|
+
*/
|
|
18
|
+
export class InputTemplateProcessor extends BaseProcessor {
|
|
19
|
+
readonly name = 'InputTemplateProcessor';
|
|
20
|
+
|
|
21
|
+
constructor(
|
|
22
|
+
private config: InputTemplateConfig,
|
|
23
|
+
options: ProcessorOptions = {},
|
|
24
|
+
) {
|
|
25
|
+
super(options);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
protected async doProcess(context: PipelineContext): Promise<PipelineContext> {
|
|
29
|
+
const clonedContext = this.cloneContext(context);
|
|
30
|
+
|
|
31
|
+
// Skip processing if no template is configured
|
|
32
|
+
if (!this.config.inputTemplate) {
|
|
33
|
+
log('No input template configured, skipping processing');
|
|
34
|
+
return this.markAsExecuted(clonedContext);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
let processedCount = 0;
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
// Compile the template
|
|
41
|
+
const compiler = template(this.config.inputTemplate, {
|
|
42
|
+
interpolate: /{{\s*(text)\s*}}/g,
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
log(`Applying input template: ${this.config.inputTemplate}`);
|
|
46
|
+
|
|
47
|
+
// Process each message
|
|
48
|
+
for (let i = 0; i < clonedContext.messages.length; i++) {
|
|
49
|
+
const message = clonedContext.messages[i];
|
|
50
|
+
|
|
51
|
+
// Only apply template to user messages
|
|
52
|
+
if (message.role === 'user') {
|
|
53
|
+
try {
|
|
54
|
+
const originalContent = message.content;
|
|
55
|
+
const processedContent = compiler({ text: originalContent });
|
|
56
|
+
|
|
57
|
+
if (processedContent !== originalContent) {
|
|
58
|
+
clonedContext.messages[i] = {
|
|
59
|
+
...message,
|
|
60
|
+
content: processedContent,
|
|
61
|
+
};
|
|
62
|
+
processedCount++;
|
|
63
|
+
log(`Applied template to message ${message.id}`);
|
|
64
|
+
}
|
|
65
|
+
} catch (error) {
|
|
66
|
+
log.extend('error')(`Error applying template to message ${message.id}: ${error}`);
|
|
67
|
+
// Keep original message on error
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
} catch (error) {
|
|
72
|
+
log.extend('error')(`Template compilation failed: ${error}`);
|
|
73
|
+
// Skip processing if template compilation fails
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Update metadata
|
|
77
|
+
clonedContext.metadata.inputTemplateProcessed = processedCount;
|
|
78
|
+
|
|
79
|
+
log(`Input template processing completed, processed ${processedCount} messages`);
|
|
80
|
+
|
|
81
|
+
return this.markAsExecuted(clonedContext);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import debug from 'debug';
|
|
2
|
+
|
|
3
|
+
import { BaseProcessor } from '../base/BaseProcessor';
|
|
4
|
+
import type { PipelineContext, ProcessorOptions } from '../types';
|
|
5
|
+
|
|
6
|
+
const log = debug('context-engine:processor:MessageCleanupProcessor');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* 消息清理处理器
|
|
10
|
+
* 负责清理消息中的多余字段,只保留 OpenAI 格式所需的必要字段
|
|
11
|
+
*/
|
|
12
|
+
export class MessageCleanupProcessor extends BaseProcessor {
|
|
13
|
+
readonly name = 'MessageCleanupProcessor';
|
|
14
|
+
|
|
15
|
+
constructor(options: ProcessorOptions = {}) {
|
|
16
|
+
super(options);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
protected async doProcess(context: PipelineContext): Promise<PipelineContext> {
|
|
20
|
+
const clonedContext = this.cloneContext(context);
|
|
21
|
+
|
|
22
|
+
let cleanedCount = 0;
|
|
23
|
+
|
|
24
|
+
// 清理每条消息,只保留必要字段
|
|
25
|
+
for (let i = 0; i < clonedContext.messages.length; i++) {
|
|
26
|
+
const message = clonedContext.messages[i];
|
|
27
|
+
const cleanedMessage = this.cleanMessage(message);
|
|
28
|
+
|
|
29
|
+
if (cleanedMessage !== message) {
|
|
30
|
+
clonedContext.messages[i] = cleanedMessage;
|
|
31
|
+
cleanedCount++;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// 更新元数据
|
|
36
|
+
clonedContext.metadata.messageCleanup = {
|
|
37
|
+
cleanedCount,
|
|
38
|
+
totalMessages: clonedContext.messages.length,
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
log(`Message cleanup completed, cleaned ${cleanedCount} messages`);
|
|
42
|
+
return this.markAsExecuted(clonedContext);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* 清理单条消息,只保留必要字段
|
|
47
|
+
*/
|
|
48
|
+
private cleanMessage(message: any): any {
|
|
49
|
+
switch (message.role) {
|
|
50
|
+
case 'system': {
|
|
51
|
+
return {
|
|
52
|
+
content: message.content,
|
|
53
|
+
role: message.role,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
case 'user': {
|
|
58
|
+
return {
|
|
59
|
+
content: message.content,
|
|
60
|
+
role: message.role,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
case 'assistant': {
|
|
65
|
+
return {
|
|
66
|
+
content: message.content,
|
|
67
|
+
role: message.role,
|
|
68
|
+
...(message.tool_calls && { tool_calls: message.tool_calls }),
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
case 'tool': {
|
|
73
|
+
return {
|
|
74
|
+
content: message.content,
|
|
75
|
+
role: message.role,
|
|
76
|
+
tool_call_id: message.tool_call_id,
|
|
77
|
+
...(message.name && { name: message.name }),
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
default: {
|
|
82
|
+
// 对于未知角色,保持原样
|
|
83
|
+
return message;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|