@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.
Files changed (40) hide show
  1. package/dist/cjs/graphs/Graph.cjs +8 -7
  2. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  3. package/dist/cjs/main.cjs +19 -8
  4. package/dist/cjs/main.cjs.map +1 -1
  5. package/dist/cjs/{messages.cjs → messages/core.cjs} +2 -2
  6. package/dist/cjs/messages/core.cjs.map +1 -0
  7. package/dist/cjs/messages/format.cjs +313 -0
  8. package/dist/cjs/messages/format.cjs.map +1 -0
  9. package/dist/cjs/messages/transformers.cjs +318 -0
  10. package/dist/cjs/messages/transformers.cjs.map +1 -0
  11. package/dist/cjs/messages/trimMessagesFactory.cjs +129 -0
  12. package/dist/cjs/messages/trimMessagesFactory.cjs.map +1 -0
  13. package/dist/esm/graphs/Graph.mjs +2 -1
  14. package/dist/esm/graphs/Graph.mjs.map +1 -1
  15. package/dist/esm/main.mjs +4 -1
  16. package/dist/esm/main.mjs.map +1 -1
  17. package/dist/esm/{messages.mjs → messages/core.mjs} +2 -2
  18. package/dist/esm/messages/core.mjs.map +1 -0
  19. package/dist/esm/messages/format.mjs +306 -0
  20. package/dist/esm/messages/format.mjs.map +1 -0
  21. package/dist/esm/messages/transformers.mjs +316 -0
  22. package/dist/esm/messages/transformers.mjs.map +1 -0
  23. package/dist/esm/messages/trimMessagesFactory.mjs +127 -0
  24. package/dist/esm/messages/trimMessagesFactory.mjs.map +1 -0
  25. package/dist/types/messages/format.d.ts +111 -0
  26. package/dist/types/messages/index.d.ts +4 -0
  27. package/dist/types/messages/transformers.d.ts +320 -0
  28. package/dist/types/messages/trimMessagesFactory.d.ts +37 -0
  29. package/package.json +1 -1
  30. package/src/messages/format.ts +433 -0
  31. package/src/messages/formatAgentMessages.test.ts +628 -0
  32. package/src/messages/formatMessage.test.ts +277 -0
  33. package/src/messages/index.ts +4 -0
  34. package/src/messages/transformers.ts +786 -0
  35. package/src/messages/trimMessagesFactory.test.ts +331 -0
  36. package/src/messages/trimMessagesFactory.ts +140 -0
  37. package/dist/cjs/messages.cjs.map +0 -1
  38. package/dist/esm/messages.mjs.map +0 -1
  39. /package/dist/types/{messages.d.ts → messages/core.d.ts} +0 -0
  40. /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