@librechat/agents 2.2.1 → 2.2.2
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/dist/cjs/graphs/Graph.cjs +8 -7
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/main.cjs +19 -8
- package/dist/cjs/main.cjs.map +1 -1
- package/dist/cjs/{messages.cjs → messages/core.cjs} +2 -2
- package/dist/cjs/messages/core.cjs.map +1 -0
- package/dist/cjs/messages/format.cjs +313 -0
- package/dist/cjs/messages/format.cjs.map +1 -0
- package/dist/cjs/messages/transformers.cjs +318 -0
- package/dist/cjs/messages/transformers.cjs.map +1 -0
- package/dist/cjs/messages/trimMessagesFactory.cjs +129 -0
- package/dist/cjs/messages/trimMessagesFactory.cjs.map +1 -0
- package/dist/esm/graphs/Graph.mjs +2 -1
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/main.mjs +4 -1
- package/dist/esm/main.mjs.map +1 -1
- package/dist/esm/{messages.mjs → messages/core.mjs} +2 -2
- package/dist/esm/messages/core.mjs.map +1 -0
- package/dist/esm/messages/format.mjs +306 -0
- package/dist/esm/messages/format.mjs.map +1 -0
- package/dist/esm/messages/transformers.mjs +316 -0
- package/dist/esm/messages/transformers.mjs.map +1 -0
- package/dist/esm/messages/trimMessagesFactory.mjs +127 -0
- package/dist/esm/messages/trimMessagesFactory.mjs.map +1 -0
- package/dist/types/messages/format.d.ts +111 -0
- package/dist/types/messages/index.d.ts +4 -0
- package/dist/types/messages/transformers.d.ts +320 -0
- package/dist/types/messages/trimMessagesFactory.d.ts +37 -0
- package/package.json +1 -1
- package/src/messages/format.ts +433 -0
- package/src/messages/formatAgentMessages.test.ts +628 -0
- package/src/messages/formatMessage.test.ts +277 -0
- package/src/messages/index.ts +4 -0
- package/src/messages/transformers.ts +786 -0
- package/src/messages/trimMessagesFactory.test.ts +331 -0
- package/src/messages/trimMessagesFactory.ts +140 -0
- package/dist/cjs/messages.cjs.map +0 -1
- package/dist/esm/messages.mjs.map +0 -1
- /package/dist/types/{messages.d.ts → messages/core.d.ts} +0 -0
- /package/src/{messages.ts → messages/core.ts} +0 -0
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
import { HumanMessage, AIMessage, SystemMessage, ToolMessage } from '@langchain/core/messages';
|
|
2
|
+
import { Providers, ContentTypes } from '../common/enum.mjs';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Formats a message to OpenAI Vision API payload format.
|
|
6
|
+
*
|
|
7
|
+
* @param {VisionMessageParams} params - The parameters for formatting.
|
|
8
|
+
* @returns {Object} - The formatted message.
|
|
9
|
+
*/
|
|
10
|
+
const formatVisionMessage = ({ message, image_urls, endpoint }) => {
|
|
11
|
+
// Create a new object to avoid mutating the input
|
|
12
|
+
const result = {
|
|
13
|
+
...message,
|
|
14
|
+
content: []
|
|
15
|
+
};
|
|
16
|
+
if (endpoint === Providers.ANTHROPIC) {
|
|
17
|
+
result.content = [
|
|
18
|
+
...image_urls,
|
|
19
|
+
{ type: ContentTypes.TEXT, text: message.content }
|
|
20
|
+
];
|
|
21
|
+
return result;
|
|
22
|
+
}
|
|
23
|
+
result.content = [
|
|
24
|
+
{ type: ContentTypes.TEXT, text: message.content },
|
|
25
|
+
...image_urls
|
|
26
|
+
];
|
|
27
|
+
return result;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Formats a message to OpenAI payload format based on the provided options.
|
|
31
|
+
*
|
|
32
|
+
* @param {FormatMessageParams} params - The parameters for formatting.
|
|
33
|
+
* @returns {FormattedMessage | HumanMessage | AIMessage | SystemMessage} - The formatted message.
|
|
34
|
+
*/
|
|
35
|
+
const formatMessage = ({ message, userName, assistantName, endpoint, langChain = false }) => {
|
|
36
|
+
let { role: _role, _name, sender, text, content: _content, lc_id } = message;
|
|
37
|
+
if (lc_id && lc_id[2] && !langChain) {
|
|
38
|
+
const roleMapping = {
|
|
39
|
+
SystemMessage: 'system',
|
|
40
|
+
HumanMessage: 'user',
|
|
41
|
+
AIMessage: 'assistant',
|
|
42
|
+
};
|
|
43
|
+
_role = roleMapping[lc_id[2]] || _role;
|
|
44
|
+
}
|
|
45
|
+
const role = _role ?? (sender && sender?.toLowerCase() === 'user' ? 'user' : 'assistant');
|
|
46
|
+
const content = _content ?? text ?? '';
|
|
47
|
+
const formattedMessage = {
|
|
48
|
+
role,
|
|
49
|
+
content,
|
|
50
|
+
};
|
|
51
|
+
const { image_urls } = message;
|
|
52
|
+
if (Array.isArray(image_urls) && image_urls.length > 0 && role === 'user') {
|
|
53
|
+
return formatVisionMessage({
|
|
54
|
+
message: {
|
|
55
|
+
...formattedMessage,
|
|
56
|
+
content: typeof formattedMessage.content === 'string' ? formattedMessage.content : ''
|
|
57
|
+
},
|
|
58
|
+
image_urls,
|
|
59
|
+
endpoint,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
if (_name) {
|
|
63
|
+
formattedMessage.name = _name;
|
|
64
|
+
}
|
|
65
|
+
if (userName && formattedMessage.role === 'user') {
|
|
66
|
+
formattedMessage.name = userName;
|
|
67
|
+
}
|
|
68
|
+
if (assistantName && formattedMessage.role === 'assistant') {
|
|
69
|
+
formattedMessage.name = assistantName;
|
|
70
|
+
}
|
|
71
|
+
if (formattedMessage.name) {
|
|
72
|
+
// Conform to API regex: ^[a-zA-Z0-9_-]{1,64}$
|
|
73
|
+
// https://community.openai.com/t/the-format-of-the-name-field-in-the-documentation-is-incorrect/175684/2
|
|
74
|
+
formattedMessage.name = formattedMessage.name.replace(/[^a-zA-Z0-9_-]/g, '_');
|
|
75
|
+
if (formattedMessage.name.length > 64) {
|
|
76
|
+
formattedMessage.name = formattedMessage.name.substring(0, 64);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (!langChain) {
|
|
80
|
+
return formattedMessage;
|
|
81
|
+
}
|
|
82
|
+
if (role === 'user') {
|
|
83
|
+
return new HumanMessage(formattedMessage);
|
|
84
|
+
}
|
|
85
|
+
else if (role === 'assistant') {
|
|
86
|
+
return new AIMessage(formattedMessage);
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
return new SystemMessage(formattedMessage);
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
/**
|
|
93
|
+
* Formats an array of messages for LangChain.
|
|
94
|
+
*
|
|
95
|
+
* @param {Array<MessageInput>} messages - The array of messages to format.
|
|
96
|
+
* @param {Omit<FormatMessageParams, 'message' | 'langChain'>} formatOptions - The options for formatting each message.
|
|
97
|
+
* @returns {Array<HumanMessage | AIMessage | SystemMessage>} - The array of formatted LangChain messages.
|
|
98
|
+
*/
|
|
99
|
+
const formatLangChainMessages = (messages, formatOptions) => {
|
|
100
|
+
return messages.map((msg) => {
|
|
101
|
+
const formatted = formatMessage({ ...formatOptions, message: msg, langChain: true });
|
|
102
|
+
return formatted;
|
|
103
|
+
});
|
|
104
|
+
};
|
|
105
|
+
/**
|
|
106
|
+
* Formats a LangChain message object by merging properties from `lc_kwargs` or `kwargs` and `additional_kwargs`.
|
|
107
|
+
*
|
|
108
|
+
* @param {LangChainMessage} message - The message object to format.
|
|
109
|
+
* @returns {Record<string, any>} The formatted LangChain message.
|
|
110
|
+
*/
|
|
111
|
+
const formatFromLangChain = (message) => {
|
|
112
|
+
const kwargs = message.lc_kwargs ?? message.kwargs ?? {};
|
|
113
|
+
const { additional_kwargs = {}, ...message_kwargs } = kwargs;
|
|
114
|
+
return {
|
|
115
|
+
...message_kwargs,
|
|
116
|
+
...additional_kwargs,
|
|
117
|
+
};
|
|
118
|
+
};
|
|
119
|
+
/**
|
|
120
|
+
* Formats an array of messages for LangChain, handling tool calls and creating ToolMessage instances.
|
|
121
|
+
*
|
|
122
|
+
* @param {Array<Partial<TMessage>>} payload - The array of messages to format.
|
|
123
|
+
* @param {Record<number, number>} [indexTokenCountMap] - Optional map of message indices to token counts.
|
|
124
|
+
* @returns {Object} - Object containing formatted messages and updated indexTokenCountMap if provided.
|
|
125
|
+
*/
|
|
126
|
+
const formatAgentMessages = (payload, indexTokenCountMap) => {
|
|
127
|
+
const messages = [];
|
|
128
|
+
// If indexTokenCountMap is provided, create a new map to track the updated indices
|
|
129
|
+
const updatedIndexTokenCountMap = {};
|
|
130
|
+
// Keep track of the mapping from original payload indices to result indices
|
|
131
|
+
const indexMapping = {};
|
|
132
|
+
for (let i = 0; i < payload.length; i++) {
|
|
133
|
+
const message = payload[i];
|
|
134
|
+
// Q: Store the current length of messages to track where this payload message starts in the result?
|
|
135
|
+
// const startIndex = messages.length;
|
|
136
|
+
if (typeof message.content === 'string') {
|
|
137
|
+
message.content = [{ type: ContentTypes.TEXT, [ContentTypes.TEXT]: message.content }];
|
|
138
|
+
}
|
|
139
|
+
if (message.role !== 'assistant') {
|
|
140
|
+
messages.push(formatMessage({
|
|
141
|
+
message: message,
|
|
142
|
+
langChain: true
|
|
143
|
+
}));
|
|
144
|
+
// Update the index mapping for this message
|
|
145
|
+
indexMapping[i] = [messages.length - 1];
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
// For assistant messages, track the starting index before processing
|
|
149
|
+
const startMessageIndex = messages.length;
|
|
150
|
+
let currentContent = [];
|
|
151
|
+
let lastAIMessage = null;
|
|
152
|
+
let hasReasoning = false;
|
|
153
|
+
if (Array.isArray(message.content)) {
|
|
154
|
+
for (const part of message.content) {
|
|
155
|
+
if (part.type === ContentTypes.TEXT && part.tool_call_ids) {
|
|
156
|
+
/*
|
|
157
|
+
If there's pending content, it needs to be aggregated as a single string to prepare for tool calls.
|
|
158
|
+
For Anthropic models, the "tool_calls" field on a message is only respected if content is a string.
|
|
159
|
+
*/
|
|
160
|
+
if (currentContent.length > 0) {
|
|
161
|
+
let content = currentContent.reduce((acc, curr) => {
|
|
162
|
+
if (curr.type === ContentTypes.TEXT) {
|
|
163
|
+
return `${acc}${curr[ContentTypes.TEXT] || ''}\n`;
|
|
164
|
+
}
|
|
165
|
+
return acc;
|
|
166
|
+
}, '');
|
|
167
|
+
content = `${content}\n${part[ContentTypes.TEXT] ?? part.text ?? ''}`.trim();
|
|
168
|
+
lastAIMessage = new AIMessage({ content });
|
|
169
|
+
messages.push(lastAIMessage);
|
|
170
|
+
currentContent = [];
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
// Create a new AIMessage with this text and prepare for tool calls
|
|
174
|
+
lastAIMessage = new AIMessage({
|
|
175
|
+
content: part.text || '',
|
|
176
|
+
});
|
|
177
|
+
messages.push(lastAIMessage);
|
|
178
|
+
}
|
|
179
|
+
else if (part.type === ContentTypes.TOOL_CALL) {
|
|
180
|
+
if (!lastAIMessage) {
|
|
181
|
+
throw new Error('Invalid tool call structure: No preceding AIMessage with tool_call_ids');
|
|
182
|
+
}
|
|
183
|
+
// Note: `tool_calls` list is defined when constructed by `AIMessage` class, and outputs should be excluded from it
|
|
184
|
+
const { output, args: _args, ...tool_call } = part.tool_call;
|
|
185
|
+
// TODO: investigate; args as dictionary may need to be providers-or-tool-specific
|
|
186
|
+
let args = _args;
|
|
187
|
+
try {
|
|
188
|
+
if (typeof _args === 'string') {
|
|
189
|
+
args = JSON.parse(_args);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
catch (e) {
|
|
193
|
+
if (typeof _args === 'string') {
|
|
194
|
+
args = { input: _args };
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
tool_call.args = args;
|
|
198
|
+
if (!lastAIMessage.tool_calls) {
|
|
199
|
+
lastAIMessage.tool_calls = [];
|
|
200
|
+
}
|
|
201
|
+
lastAIMessage.tool_calls.push(tool_call);
|
|
202
|
+
// Add the corresponding ToolMessage
|
|
203
|
+
messages.push(new ToolMessage({
|
|
204
|
+
tool_call_id: tool_call.id,
|
|
205
|
+
name: tool_call.name,
|
|
206
|
+
content: output || '',
|
|
207
|
+
}));
|
|
208
|
+
}
|
|
209
|
+
else if (part.type === ContentTypes.THINK) {
|
|
210
|
+
hasReasoning = true;
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
else if (part.type === ContentTypes.ERROR || part.type === ContentTypes.AGENT_UPDATE) {
|
|
214
|
+
continue;
|
|
215
|
+
}
|
|
216
|
+
else {
|
|
217
|
+
currentContent.push(part);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
if (hasReasoning && currentContent.length > 0) {
|
|
222
|
+
const content = currentContent
|
|
223
|
+
.reduce((acc, curr) => {
|
|
224
|
+
if (curr.type === ContentTypes.TEXT) {
|
|
225
|
+
return `${acc}${curr[ContentTypes.TEXT] || ''}\n`;
|
|
226
|
+
}
|
|
227
|
+
return acc;
|
|
228
|
+
}, '')
|
|
229
|
+
.trim();
|
|
230
|
+
if (content) {
|
|
231
|
+
messages.push(new AIMessage({ content }));
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
else if (currentContent.length > 0) {
|
|
235
|
+
messages.push(new AIMessage({ content: currentContent }));
|
|
236
|
+
}
|
|
237
|
+
// Update the index mapping for this assistant message
|
|
238
|
+
// Store all indices that were created from this original message
|
|
239
|
+
const endMessageIndex = messages.length;
|
|
240
|
+
const resultIndices = [];
|
|
241
|
+
for (let j = startMessageIndex; j < endMessageIndex; j++) {
|
|
242
|
+
resultIndices.push(j);
|
|
243
|
+
}
|
|
244
|
+
indexMapping[i] = resultIndices;
|
|
245
|
+
}
|
|
246
|
+
// Update the token count map if it was provided
|
|
247
|
+
if (indexTokenCountMap) {
|
|
248
|
+
for (let originalIndex = 0; originalIndex < payload.length; originalIndex++) {
|
|
249
|
+
const resultIndices = indexMapping[originalIndex] || [];
|
|
250
|
+
const tokenCount = indexTokenCountMap[originalIndex];
|
|
251
|
+
if (tokenCount !== undefined) {
|
|
252
|
+
if (resultIndices.length === 1) {
|
|
253
|
+
// Simple 1:1 mapping
|
|
254
|
+
updatedIndexTokenCountMap[resultIndices[0]] = tokenCount;
|
|
255
|
+
}
|
|
256
|
+
else if (resultIndices.length > 1) {
|
|
257
|
+
// If one message was split into multiple, distribute the token count
|
|
258
|
+
// This is a simplification - in reality, you might want a more sophisticated distribution
|
|
259
|
+
const countPerMessage = Math.floor(tokenCount / resultIndices.length);
|
|
260
|
+
resultIndices.forEach((resultIndex, idx) => {
|
|
261
|
+
if (idx === resultIndices.length - 1) {
|
|
262
|
+
// Give any remainder to the last message
|
|
263
|
+
updatedIndexTokenCountMap[resultIndex] = tokenCount - (countPerMessage * (resultIndices.length - 1));
|
|
264
|
+
}
|
|
265
|
+
else {
|
|
266
|
+
updatedIndexTokenCountMap[resultIndex] = countPerMessage;
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
return {
|
|
274
|
+
messages,
|
|
275
|
+
indexTokenCountMap: indexTokenCountMap ? updatedIndexTokenCountMap : undefined
|
|
276
|
+
};
|
|
277
|
+
};
|
|
278
|
+
/**
|
|
279
|
+
* Formats an array of messages for LangChain, making sure all content fields are strings
|
|
280
|
+
* @param {Array<HumanMessage | AIMessage | SystemMessage | ToolMessage>} payload - The array of messages to format.
|
|
281
|
+
* @returns {Array<HumanMessage | AIMessage | SystemMessage | ToolMessage>} - The array of formatted LangChain messages, including ToolMessages for tool calls.
|
|
282
|
+
*/
|
|
283
|
+
const formatContentStrings = (payload) => {
|
|
284
|
+
// Create a copy of the payload to avoid modifying the original
|
|
285
|
+
const result = [...payload];
|
|
286
|
+
for (const message of result) {
|
|
287
|
+
if (typeof message.content === 'string') {
|
|
288
|
+
continue;
|
|
289
|
+
}
|
|
290
|
+
if (!Array.isArray(message.content)) {
|
|
291
|
+
continue;
|
|
292
|
+
}
|
|
293
|
+
// Reduce text types to a single string, ignore all other types
|
|
294
|
+
const content = message.content.reduce((acc, curr) => {
|
|
295
|
+
if (curr.type === ContentTypes.TEXT) {
|
|
296
|
+
return `${acc}${curr[ContentTypes.TEXT] || ''}\n`;
|
|
297
|
+
}
|
|
298
|
+
return acc;
|
|
299
|
+
}, '');
|
|
300
|
+
message.content = content.trim();
|
|
301
|
+
}
|
|
302
|
+
return result;
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
export { formatAgentMessages, formatContentStrings, formatFromLangChain, formatLangChainMessages, formatMessage, formatVisionMessage };
|
|
306
|
+
//# sourceMappingURL=format.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.mjs","sources":["../../../src/messages/format.ts"],"sourcesContent":["import { ToolMessage, BaseMessage } from '@langchain/core/messages';\nimport { HumanMessage, AIMessage, SystemMessage } from '@langchain/core/messages';\nimport { MessageContentImageUrl } from '@langchain/core/messages';\nimport type { ToolCall } from '@langchain/core/messages/tool';\nimport type { MessageContentComplex } from '@/types';\nimport { Providers, ContentTypes } from '@/common';\n\ninterface VisionMessageParams {\n message: {\n role: string;\n content: string;\n name?: string;\n [key: string]: any;\n };\n image_urls: MessageContentImageUrl[];\n endpoint?: Providers;\n}\n\n/**\n * Formats a message to OpenAI Vision API payload format.\n *\n * @param {VisionMessageParams} params - The parameters for formatting.\n * @returns {Object} - The formatted message.\n */\nexport const formatVisionMessage = ({ message, image_urls, endpoint }: VisionMessageParams): {\n role: string;\n content: MessageContentComplex[];\n name?: string;\n [key: string]: any;\n} => {\n // Create a new object to avoid mutating the input\n const result: {\n role: string;\n content: MessageContentComplex[];\n name?: string;\n [key: string]: any;\n } = {\n ...message,\n content: [] as MessageContentComplex[]\n };\n \n if (endpoint === Providers.ANTHROPIC) {\n result.content = [\n ...image_urls, \n { type: ContentTypes.TEXT, text: message.content }\n ] as MessageContentComplex[];\n return result;\n }\n\n result.content = [\n { type: ContentTypes.TEXT, text: message.content }, \n ...image_urls\n ] as MessageContentComplex[];\n\n return result;\n};\n\ninterface MessageInput {\n role?: string;\n _name?: string;\n sender?: string;\n text?: string;\n content?: string | MessageContentComplex[];\n image_urls?: MessageContentImageUrl[];\n lc_id?: string[];\n [key: string]: any;\n}\n\ninterface FormatMessageParams {\n message: MessageInput;\n userName?: string;\n assistantName?: string;\n endpoint?: Providers;\n langChain?: boolean;\n}\n\ninterface FormattedMessage {\n role: string;\n content: string | MessageContentComplex[];\n name?: string;\n [key: string]: any;\n}\n\n/**\n * Formats a message to OpenAI payload format based on the provided options.\n *\n * @param {FormatMessageParams} params - The parameters for formatting.\n * @returns {FormattedMessage | HumanMessage | AIMessage | SystemMessage} - The formatted message.\n */\nexport const formatMessage = ({ \n message, \n userName, \n assistantName, \n endpoint, \n langChain = false \n}: FormatMessageParams): FormattedMessage | HumanMessage | AIMessage | SystemMessage => {\n let { role: _role, _name, sender, text, content: _content, lc_id } = message;\n if (lc_id && lc_id[2] && !langChain) {\n const roleMapping: Record<string, string> = {\n SystemMessage: 'system',\n HumanMessage: 'user',\n AIMessage: 'assistant',\n };\n _role = roleMapping[lc_id[2]] || _role;\n }\n const role = _role ?? (sender && sender?.toLowerCase() === 'user' ? 'user' : 'assistant');\n const content = _content ?? text ?? '';\n const formattedMessage: FormattedMessage = {\n role,\n content,\n };\n\n const { image_urls } = message;\n if (Array.isArray(image_urls) && image_urls.length > 0 && role === 'user') {\n return formatVisionMessage({\n message: {\n ...formattedMessage,\n content: typeof formattedMessage.content === 'string' ? formattedMessage.content : ''\n },\n image_urls,\n endpoint,\n });\n }\n\n if (_name) {\n formattedMessage.name = _name;\n }\n\n if (userName && formattedMessage.role === 'user') {\n formattedMessage.name = userName;\n }\n\n if (assistantName && formattedMessage.role === 'assistant') {\n formattedMessage.name = assistantName;\n }\n\n if (formattedMessage.name) {\n // Conform to API regex: ^[a-zA-Z0-9_-]{1,64}$\n // https://community.openai.com/t/the-format-of-the-name-field-in-the-documentation-is-incorrect/175684/2\n formattedMessage.name = formattedMessage.name.replace(/[^a-zA-Z0-9_-]/g, '_');\n\n if (formattedMessage.name.length > 64) {\n formattedMessage.name = formattedMessage.name.substring(0, 64);\n }\n }\n\n if (!langChain) {\n return formattedMessage;\n }\n\n if (role === 'user') {\n return new HumanMessage(formattedMessage);\n } else if (role === 'assistant') {\n return new AIMessage(formattedMessage);\n } else {\n return new SystemMessage(formattedMessage);\n }\n};\n\n/**\n * Formats an array of messages for LangChain.\n *\n * @param {Array<MessageInput>} messages - The array of messages to format.\n * @param {Omit<FormatMessageParams, 'message' | 'langChain'>} formatOptions - The options for formatting each message.\n * @returns {Array<HumanMessage | AIMessage | SystemMessage>} - The array of formatted LangChain messages.\n */\nexport const formatLangChainMessages = (\n messages: Array<MessageInput>, \n formatOptions: Omit<FormatMessageParams, 'message' | 'langChain'>\n): Array<HumanMessage | AIMessage | SystemMessage> => {\n return messages.map((msg) => {\n const formatted = formatMessage({ ...formatOptions, message: msg, langChain: true });\n return formatted as HumanMessage | AIMessage | SystemMessage;\n });\n};\n\ninterface LangChainMessage {\n lc_kwargs?: {\n additional_kwargs?: Record<string, any>;\n [key: string]: any;\n };\n kwargs?: {\n additional_kwargs?: Record<string, any>;\n [key: string]: any;\n };\n [key: string]: any;\n}\n\n/**\n * Formats a LangChain message object by merging properties from `lc_kwargs` or `kwargs` and `additional_kwargs`.\n *\n * @param {LangChainMessage} message - The message object to format.\n * @returns {Record<string, any>} The formatted LangChain message.\n */\nexport const formatFromLangChain = (message: LangChainMessage): Record<string, any> => {\n const kwargs = message.lc_kwargs ?? message.kwargs ?? {};\n const { additional_kwargs = {}, ...message_kwargs } = kwargs;\n return {\n ...message_kwargs,\n ...additional_kwargs,\n };\n};\n\ninterface TMessage {\n role?: string;\n content?: string | Array<{\n type: ContentTypes;\n [ContentTypes.TEXT]?: string;\n text?: string;\n tool_call_ids?: string[];\n [key: string]: any;\n }>;\n [key: string]: any;\n}\n\ninterface ToolCallPart {\n type: ContentTypes.TOOL_CALL;\n tool_call: {\n id: string;\n name: string;\n args: string | Record<string, unknown>;\n output?: string;\n [key: string]: any;\n };\n}\n\n/**\n * Formats an array of messages for LangChain, handling tool calls and creating ToolMessage instances.\n *\n * @param {Array<Partial<TMessage>>} payload - The array of messages to format.\n * @param {Record<number, number>} [indexTokenCountMap] - Optional map of message indices to token counts.\n * @returns {Object} - Object containing formatted messages and updated indexTokenCountMap if provided.\n */\nexport const formatAgentMessages = (\n payload: Array<Partial<TMessage>>, \n indexTokenCountMap?: Record<number, number>\n): {\n messages: Array<HumanMessage | AIMessage | SystemMessage | ToolMessage>;\n indexTokenCountMap?: Record<number, number>;\n} => {\n const messages: Array<HumanMessage | AIMessage | SystemMessage | ToolMessage> = [];\n // If indexTokenCountMap is provided, create a new map to track the updated indices\n const updatedIndexTokenCountMap: Record<number, number> = {};\n // Keep track of the mapping from original payload indices to result indices\n const indexMapping: Record<number, number[]> = {};\n\n for (let i = 0; i < payload.length; i++) {\n const message = payload[i];\n // Q: Store the current length of messages to track where this payload message starts in the result?\n // const startIndex = messages.length;\n if (typeof message.content === 'string') {\n message.content = [{ type: ContentTypes.TEXT, [ContentTypes.TEXT]: message.content }];\n }\n if (message.role !== 'assistant') {\n messages.push(formatMessage({ \n message: message as MessageInput, \n langChain: true \n }) as HumanMessage | AIMessage | SystemMessage);\n \n // Update the index mapping for this message\n indexMapping[i] = [messages.length - 1];\n continue;\n }\n\n // For assistant messages, track the starting index before processing\n const startMessageIndex = messages.length;\n\n let currentContent: any[] = [];\n let lastAIMessage: AIMessage | null = null;\n\n let hasReasoning = false;\n if (Array.isArray(message.content)) {\n for (const part of message.content) {\n if (part.type === ContentTypes.TEXT && part.tool_call_ids) {\n /*\n If there's pending content, it needs to be aggregated as a single string to prepare for tool calls.\n For Anthropic models, the \"tool_calls\" field on a message is only respected if content is a string.\n */\n if (currentContent.length > 0) {\n let content = currentContent.reduce((acc, curr) => {\n if (curr.type === ContentTypes.TEXT) {\n return `${acc}${curr[ContentTypes.TEXT] || ''}\\n`;\n }\n return acc;\n }, '');\n content = `${content}\\n${part[ContentTypes.TEXT] ?? part.text ?? ''}`.trim();\n lastAIMessage = new AIMessage({ content });\n messages.push(lastAIMessage);\n currentContent = [];\n continue;\n }\n\n // Create a new AIMessage with this text and prepare for tool calls\n lastAIMessage = new AIMessage({\n content: part.text || '',\n });\n\n messages.push(lastAIMessage);\n } else if (part.type === ContentTypes.TOOL_CALL) {\n if (!lastAIMessage) {\n throw new Error('Invalid tool call structure: No preceding AIMessage with tool_call_ids');\n }\n\n // Note: `tool_calls` list is defined when constructed by `AIMessage` class, and outputs should be excluded from it\n const { output, args: _args, ...tool_call } = (part.tool_call as any);\n // TODO: investigate; args as dictionary may need to be providers-or-tool-specific\n let args: any = _args;\n try {\n if (typeof _args === 'string') {\n args = JSON.parse(_args);\n }\n } catch (e) {\n if (typeof _args === 'string') {\n args = { input: _args };\n }\n }\n\n tool_call.args = args;\n if (!lastAIMessage.tool_calls) {\n lastAIMessage.tool_calls = [];\n }\n lastAIMessage.tool_calls.push(tool_call as ToolCall);\n\n // Add the corresponding ToolMessage\n messages.push(\n new ToolMessage({\n tool_call_id: tool_call.id,\n name: tool_call.name,\n content: output || '',\n }),\n );\n } else if (part.type === ContentTypes.THINK) {\n hasReasoning = true;\n continue;\n } else if (part.type === ContentTypes.ERROR || part.type === ContentTypes.AGENT_UPDATE) {\n continue;\n } else {\n currentContent.push(part);\n }\n }\n }\n\n if (hasReasoning && currentContent.length > 0) {\n const content = currentContent\n .reduce((acc, curr) => {\n if (curr.type === ContentTypes.TEXT) {\n return `${acc}${curr[ContentTypes.TEXT] || ''}\\n`;\n }\n return acc;\n }, '')\n .trim();\n \n if (content) {\n messages.push(new AIMessage({ content }));\n }\n } else if (currentContent.length > 0) {\n messages.push(new AIMessage({ content: currentContent }));\n }\n \n // Update the index mapping for this assistant message\n // Store all indices that were created from this original message\n const endMessageIndex = messages.length;\n const resultIndices = [];\n for (let j = startMessageIndex; j < endMessageIndex; j++) {\n resultIndices.push(j);\n }\n indexMapping[i] = resultIndices;\n }\n\n // Update the token count map if it was provided\n if (indexTokenCountMap) {\n for (let originalIndex = 0; originalIndex < payload.length; originalIndex++) {\n const resultIndices = indexMapping[originalIndex] || [];\n const tokenCount = indexTokenCountMap[originalIndex];\n \n if (tokenCount !== undefined) {\n if (resultIndices.length === 1) {\n // Simple 1:1 mapping\n updatedIndexTokenCountMap[resultIndices[0]] = tokenCount;\n } else if (resultIndices.length > 1) {\n // If one message was split into multiple, distribute the token count\n // This is a simplification - in reality, you might want a more sophisticated distribution\n const countPerMessage = Math.floor(tokenCount / resultIndices.length);\n resultIndices.forEach((resultIndex, idx) => {\n if (idx === resultIndices.length - 1) {\n // Give any remainder to the last message\n updatedIndexTokenCountMap[resultIndex] = tokenCount - (countPerMessage * (resultIndices.length - 1));\n } else {\n updatedIndexTokenCountMap[resultIndex] = countPerMessage;\n }\n });\n }\n }\n }\n }\n\n return {\n messages,\n indexTokenCountMap: indexTokenCountMap ? updatedIndexTokenCountMap : undefined\n };\n};\n\n/**\n * Formats an array of messages for LangChain, making sure all content fields are strings\n * @param {Array<HumanMessage | AIMessage | SystemMessage | ToolMessage>} payload - The array of messages to format.\n * @returns {Array<HumanMessage | AIMessage | SystemMessage | ToolMessage>} - The array of formatted LangChain messages, including ToolMessages for tool calls.\n */\nexport const formatContentStrings = (payload: Array<BaseMessage>): Array<BaseMessage> => {\n // Create a copy of the payload to avoid modifying the original\n const result = [...payload];\n\n for (const message of result) {\n if (typeof message.content === 'string') {\n continue;\n }\n\n if (!Array.isArray(message.content)) {\n continue;\n }\n\n // Reduce text types to a single string, ignore all other types\n const content = message.content.reduce((acc, curr) => {\n if (curr.type === ContentTypes.TEXT) {\n return `${acc}${curr[ContentTypes.TEXT] || ''}\\n`;\n }\n return acc;\n }, '');\n\n message.content = content.trim();\n }\n\n return result;\n};\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
import { RunnableLambda } from '@langchain/core/runnables';
|
|
2
|
+
import { isBaseMessageChunk, ChatMessageChunk, ChatMessage, FunctionMessageChunk, FunctionMessage, ToolMessageChunk, ToolMessage, SystemMessageChunk, SystemMessage, AIMessageChunk, AIMessage, HumanMessageChunk, HumanMessage, defaultTextSplitter } from '@langchain/core/messages';
|
|
3
|
+
|
|
4
|
+
const _isMessageType = (msg, types) => {
|
|
5
|
+
const typesAsStrings = [
|
|
6
|
+
...new Set(types?.map((t) => {
|
|
7
|
+
if (typeof t === "string") {
|
|
8
|
+
return t;
|
|
9
|
+
}
|
|
10
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
11
|
+
const instantiatedMsgClass = new t({});
|
|
12
|
+
if (!("getType" in instantiatedMsgClass) ||
|
|
13
|
+
typeof instantiatedMsgClass.getType !== "function") {
|
|
14
|
+
throw new Error("Invalid type provided.");
|
|
15
|
+
}
|
|
16
|
+
return instantiatedMsgClass.getType();
|
|
17
|
+
})),
|
|
18
|
+
];
|
|
19
|
+
const msgType = msg.getType();
|
|
20
|
+
return typesAsStrings.some((t) => t === msgType);
|
|
21
|
+
};
|
|
22
|
+
function trimMessages(messagesOrOptions, options) {
|
|
23
|
+
if (Array.isArray(messagesOrOptions)) {
|
|
24
|
+
const messages = messagesOrOptions;
|
|
25
|
+
if (!options) {
|
|
26
|
+
throw new Error("Options parameter is required when providing messages.");
|
|
27
|
+
}
|
|
28
|
+
return _trimMessagesHelper(messages, options);
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
const trimmerOptions = messagesOrOptions;
|
|
32
|
+
return RunnableLambda.from((input) => _trimMessagesHelper(input, trimmerOptions)).withConfig({
|
|
33
|
+
runName: "trim_messages",
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
async function _trimMessagesHelper(messages, options) {
|
|
38
|
+
const { maxTokens, tokenCounter, strategy = "last", allowPartial = false, endOn, startOn, includeSystem = false, textSplitter, } = options;
|
|
39
|
+
if (startOn && strategy === "first") {
|
|
40
|
+
throw new Error("`startOn` should only be specified if `strategy` is 'last'.");
|
|
41
|
+
}
|
|
42
|
+
if (includeSystem && strategy === "first") {
|
|
43
|
+
throw new Error("`includeSystem` should only be specified if `strategy` is 'last'.");
|
|
44
|
+
}
|
|
45
|
+
let listTokenCounter;
|
|
46
|
+
if ("getNumTokens" in tokenCounter) {
|
|
47
|
+
listTokenCounter = async (msgs) => {
|
|
48
|
+
const tokenCounts = await Promise.all(msgs.map((msg) => tokenCounter.getNumTokens(msg.content)));
|
|
49
|
+
return tokenCounts.reduce((sum, count) => sum + count, 0);
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
listTokenCounter = async (msgs) => tokenCounter(msgs);
|
|
54
|
+
}
|
|
55
|
+
let textSplitterFunc = defaultTextSplitter;
|
|
56
|
+
if (textSplitter) {
|
|
57
|
+
if ("splitText" in textSplitter) {
|
|
58
|
+
textSplitterFunc = textSplitter.splitText;
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
textSplitterFunc = async (text) => textSplitter(text);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (strategy === "first") {
|
|
65
|
+
return _firstMaxTokens(messages, {
|
|
66
|
+
maxTokens,
|
|
67
|
+
tokenCounter: listTokenCounter,
|
|
68
|
+
textSplitter: textSplitterFunc,
|
|
69
|
+
partialStrategy: allowPartial ? "first" : undefined,
|
|
70
|
+
endOn,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
else if (strategy === "last") {
|
|
74
|
+
return _lastMaxTokens(messages, {
|
|
75
|
+
maxTokens,
|
|
76
|
+
tokenCounter: listTokenCounter,
|
|
77
|
+
textSplitter: textSplitterFunc,
|
|
78
|
+
allowPartial,
|
|
79
|
+
includeSystem,
|
|
80
|
+
startOn,
|
|
81
|
+
endOn,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
throw new Error(`Unrecognized strategy: '${strategy}'. Must be one of 'first' or 'last'.`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
async function _firstMaxTokens(messages, options) {
|
|
89
|
+
const { maxTokens, tokenCounter, textSplitter, partialStrategy, endOn } = options;
|
|
90
|
+
let messagesCopy = [...messages];
|
|
91
|
+
let idx = 0;
|
|
92
|
+
for (let i = 0; i < messagesCopy.length; i += 1) {
|
|
93
|
+
const remainingMessages = i > 0 ? messagesCopy.slice(0, -i) : messagesCopy;
|
|
94
|
+
if ((await tokenCounter(remainingMessages)) <= maxTokens) {
|
|
95
|
+
idx = messagesCopy.length - i;
|
|
96
|
+
break;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (idx < messagesCopy.length - 1 && partialStrategy) {
|
|
100
|
+
let includedPartial = false;
|
|
101
|
+
if (Array.isArray(messagesCopy[idx].content)) {
|
|
102
|
+
const excluded = messagesCopy[idx];
|
|
103
|
+
if (typeof excluded.content === "string") {
|
|
104
|
+
throw new Error("Expected content to be an array.");
|
|
105
|
+
}
|
|
106
|
+
const numBlock = excluded.content.length;
|
|
107
|
+
const reversedContent = partialStrategy === "last"
|
|
108
|
+
? [...excluded.content].reverse()
|
|
109
|
+
: excluded.content;
|
|
110
|
+
for (let i = 1; i <= numBlock; i += 1) {
|
|
111
|
+
const partialContent = partialStrategy === "first"
|
|
112
|
+
? reversedContent.slice(0, i)
|
|
113
|
+
: reversedContent.slice(-i);
|
|
114
|
+
const fields = Object.fromEntries(Object.entries(excluded).filter(([k]) => k !== "type" && !k.startsWith("lc_")));
|
|
115
|
+
const updatedMessage = _switchTypeToMessage(excluded.getType(), {
|
|
116
|
+
...fields,
|
|
117
|
+
content: partialContent,
|
|
118
|
+
});
|
|
119
|
+
const slicedMessages = [...messagesCopy.slice(0, idx), updatedMessage];
|
|
120
|
+
if ((await tokenCounter(slicedMessages)) <= maxTokens) {
|
|
121
|
+
messagesCopy = slicedMessages;
|
|
122
|
+
idx += 1;
|
|
123
|
+
includedPartial = true;
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
break;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
if (includedPartial && partialStrategy === "last") {
|
|
130
|
+
excluded.content = [...reversedContent].reverse();
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
if (!includedPartial) {
|
|
134
|
+
const excluded = messagesCopy[idx];
|
|
135
|
+
let text;
|
|
136
|
+
if (Array.isArray(excluded.content) &&
|
|
137
|
+
excluded.content.some((block) => typeof block === "string" || block.type === "text")) {
|
|
138
|
+
const textBlock = excluded.content.find((block) => block.type === "text" && block.text);
|
|
139
|
+
text = textBlock?.text;
|
|
140
|
+
}
|
|
141
|
+
else if (typeof excluded.content === "string") {
|
|
142
|
+
text = excluded.content;
|
|
143
|
+
}
|
|
144
|
+
if (text) {
|
|
145
|
+
const splitTexts = await textSplitter(text);
|
|
146
|
+
const numSplits = splitTexts.length;
|
|
147
|
+
if (partialStrategy === "last") {
|
|
148
|
+
splitTexts.reverse();
|
|
149
|
+
}
|
|
150
|
+
for (let _ = 0; _ < numSplits - 1; _ += 1) {
|
|
151
|
+
splitTexts.pop();
|
|
152
|
+
excluded.content = splitTexts.join("");
|
|
153
|
+
if ((await tokenCounter([...messagesCopy.slice(0, idx), excluded])) <=
|
|
154
|
+
maxTokens) {
|
|
155
|
+
if (partialStrategy === "last") {
|
|
156
|
+
excluded.content = [...splitTexts].reverse().join("");
|
|
157
|
+
}
|
|
158
|
+
messagesCopy = [...messagesCopy.slice(0, idx), excluded];
|
|
159
|
+
idx += 1;
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
if (endOn) {
|
|
167
|
+
const endOnArr = Array.isArray(endOn) ? endOn : [endOn];
|
|
168
|
+
while (idx > 0 && !_isMessageType(messagesCopy[idx - 1], endOnArr)) {
|
|
169
|
+
idx -= 1;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return messagesCopy.slice(0, idx);
|
|
173
|
+
}
|
|
174
|
+
async function _lastMaxTokens(messages, options) {
|
|
175
|
+
const { allowPartial = false, includeSystem = false, endOn, startOn, ...rest } = options;
|
|
176
|
+
// Create a copy of messages to avoid mutation
|
|
177
|
+
let messagesCopy = messages.map((message) => {
|
|
178
|
+
const fields = Object.fromEntries(Object.entries(message).filter(([k]) => k !== "type" && !k.startsWith("lc_")));
|
|
179
|
+
return _switchTypeToMessage(message.getType(), fields, isBaseMessageChunk(message));
|
|
180
|
+
});
|
|
181
|
+
if (endOn) {
|
|
182
|
+
const endOnArr = Array.isArray(endOn) ? endOn : [endOn];
|
|
183
|
+
while (messagesCopy.length > 0 &&
|
|
184
|
+
!_isMessageType(messagesCopy[messagesCopy.length - 1], endOnArr)) {
|
|
185
|
+
messagesCopy = messagesCopy.slice(0, -1);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
const swappedSystem = includeSystem && messagesCopy[0]?.getType() === "system";
|
|
189
|
+
let reversed_ = swappedSystem
|
|
190
|
+
? messagesCopy.slice(0, 1).concat(messagesCopy.slice(1).reverse())
|
|
191
|
+
: messagesCopy.reverse();
|
|
192
|
+
reversed_ = await _firstMaxTokens(reversed_, {
|
|
193
|
+
...rest,
|
|
194
|
+
partialStrategy: allowPartial ? "last" : undefined,
|
|
195
|
+
endOn: startOn,
|
|
196
|
+
});
|
|
197
|
+
if (swappedSystem) {
|
|
198
|
+
return [reversed_[0], ...reversed_.slice(1).reverse()];
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
return reversed_.reverse();
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
function _switchTypeToMessage(messageType, fields, returnChunk) {
|
|
205
|
+
let chunk;
|
|
206
|
+
let msg;
|
|
207
|
+
switch (messageType) {
|
|
208
|
+
case "human":
|
|
209
|
+
if (returnChunk) {
|
|
210
|
+
chunk = new HumanMessageChunk(fields);
|
|
211
|
+
}
|
|
212
|
+
else {
|
|
213
|
+
msg = new HumanMessage(fields);
|
|
214
|
+
}
|
|
215
|
+
break;
|
|
216
|
+
case "ai":
|
|
217
|
+
if (returnChunk) {
|
|
218
|
+
let aiChunkFields = {
|
|
219
|
+
...fields,
|
|
220
|
+
};
|
|
221
|
+
if ("tool_calls" in aiChunkFields) {
|
|
222
|
+
aiChunkFields = {
|
|
223
|
+
...aiChunkFields,
|
|
224
|
+
tool_call_chunks: aiChunkFields.tool_calls?.map((tc) => ({
|
|
225
|
+
...tc,
|
|
226
|
+
type: "tool_call_chunk",
|
|
227
|
+
index: undefined,
|
|
228
|
+
args: JSON.stringify(tc.args),
|
|
229
|
+
})),
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
chunk = new AIMessageChunk(aiChunkFields);
|
|
233
|
+
}
|
|
234
|
+
else {
|
|
235
|
+
msg = new AIMessage(fields);
|
|
236
|
+
}
|
|
237
|
+
break;
|
|
238
|
+
case "system":
|
|
239
|
+
if (returnChunk) {
|
|
240
|
+
chunk = new SystemMessageChunk(fields);
|
|
241
|
+
}
|
|
242
|
+
else {
|
|
243
|
+
msg = new SystemMessage(fields);
|
|
244
|
+
}
|
|
245
|
+
break;
|
|
246
|
+
case "developer":
|
|
247
|
+
if (returnChunk) {
|
|
248
|
+
chunk = new SystemMessageChunk({
|
|
249
|
+
...fields,
|
|
250
|
+
additional_kwargs: {
|
|
251
|
+
...fields.additional_kwargs,
|
|
252
|
+
__openai_role__: "developer",
|
|
253
|
+
},
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
msg = new SystemMessage({
|
|
258
|
+
...fields,
|
|
259
|
+
additional_kwargs: {
|
|
260
|
+
...fields.additional_kwargs,
|
|
261
|
+
__openai_role__: "developer",
|
|
262
|
+
},
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
break;
|
|
266
|
+
case "tool":
|
|
267
|
+
if ("tool_call_id" in fields) {
|
|
268
|
+
if (returnChunk) {
|
|
269
|
+
chunk = new ToolMessageChunk(fields);
|
|
270
|
+
}
|
|
271
|
+
else {
|
|
272
|
+
msg = new ToolMessage(fields);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
else {
|
|
276
|
+
throw new Error("Can not convert ToolMessage to ToolMessageChunk if 'tool_call_id' field is not defined.");
|
|
277
|
+
}
|
|
278
|
+
break;
|
|
279
|
+
case "function":
|
|
280
|
+
if (returnChunk) {
|
|
281
|
+
chunk = new FunctionMessageChunk(fields);
|
|
282
|
+
}
|
|
283
|
+
else {
|
|
284
|
+
if (!fields.name) {
|
|
285
|
+
throw new Error("FunctionMessage must have a 'name' field");
|
|
286
|
+
}
|
|
287
|
+
msg = new FunctionMessage(fields);
|
|
288
|
+
}
|
|
289
|
+
break;
|
|
290
|
+
case "generic":
|
|
291
|
+
if ("role" in fields) {
|
|
292
|
+
if (returnChunk) {
|
|
293
|
+
chunk = new ChatMessageChunk(fields);
|
|
294
|
+
}
|
|
295
|
+
else {
|
|
296
|
+
msg = new ChatMessage(fields);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
else {
|
|
300
|
+
throw new Error("Can not convert ChatMessage to ChatMessageChunk if 'role' field is not defined.");
|
|
301
|
+
}
|
|
302
|
+
break;
|
|
303
|
+
default:
|
|
304
|
+
throw new Error(`Unrecognized message type ${messageType}`);
|
|
305
|
+
}
|
|
306
|
+
if (returnChunk && chunk) {
|
|
307
|
+
return chunk;
|
|
308
|
+
}
|
|
309
|
+
if (msg) {
|
|
310
|
+
return msg;
|
|
311
|
+
}
|
|
312
|
+
throw new Error(`Unrecognized message type ${messageType}`);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
export { trimMessages };
|
|
316
|
+
//# sourceMappingURL=transformers.mjs.map
|