@librechat/agents 2.3.1 → 2.3.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 +6 -6
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/llm.cjs +7 -7
- package/dist/cjs/llm/anthropic/llm.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/utils/message_inputs.cjs +6 -6
- package/dist/cjs/llm/anthropic/utils/message_inputs.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/utils/message_outputs.cjs +24 -24
- package/dist/cjs/llm/anthropic/utils/message_outputs.cjs.map +1 -1
- package/dist/cjs/llm/fake.cjs.map +1 -1
- package/dist/cjs/llm/text.cjs.map +1 -1
- package/dist/cjs/main.cjs +3 -0
- package/dist/cjs/main.cjs.map +1 -1
- package/dist/cjs/messages/core.cjs +5 -5
- package/dist/cjs/messages/core.cjs.map +1 -1
- package/dist/cjs/messages/format.cjs +11 -9
- package/dist/cjs/messages/format.cjs.map +1 -1
- package/dist/cjs/messages/prune.cjs +155 -205
- package/dist/cjs/messages/prune.cjs.map +1 -1
- package/dist/cjs/run.cjs.map +1 -1
- package/dist/cjs/stream.cjs +3 -4
- package/dist/cjs/stream.cjs.map +1 -1
- package/dist/cjs/tools/ToolNode.cjs +1 -1
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/cjs/utils/tokens.cjs +3 -3
- package/dist/cjs/utils/tokens.cjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +6 -6
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/llm/anthropic/llm.mjs +7 -7
- package/dist/esm/llm/anthropic/llm.mjs.map +1 -1
- package/dist/esm/llm/anthropic/utils/message_inputs.mjs +6 -6
- package/dist/esm/llm/anthropic/utils/message_inputs.mjs.map +1 -1
- package/dist/esm/llm/anthropic/utils/message_outputs.mjs +24 -24
- package/dist/esm/llm/anthropic/utils/message_outputs.mjs.map +1 -1
- package/dist/esm/llm/fake.mjs.map +1 -1
- package/dist/esm/llm/text.mjs.map +1 -1
- package/dist/esm/main.mjs +1 -1
- package/dist/esm/messages/core.mjs +5 -5
- package/dist/esm/messages/core.mjs.map +1 -1
- package/dist/esm/messages/format.mjs +11 -9
- package/dist/esm/messages/format.mjs.map +1 -1
- package/dist/esm/messages/prune.mjs +153 -206
- package/dist/esm/messages/prune.mjs.map +1 -1
- package/dist/esm/run.mjs.map +1 -1
- package/dist/esm/stream.mjs +3 -4
- package/dist/esm/stream.mjs.map +1 -1
- package/dist/esm/tools/ToolNode.mjs +1 -1
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/esm/utils/tokens.mjs +3 -3
- package/dist/esm/utils/tokens.mjs.map +1 -1
- package/dist/types/messages/format.d.ts +1 -2
- package/dist/types/messages/prune.d.ts +31 -2
- package/dist/types/types/stream.d.ts +2 -2
- package/dist/types/utils/tokens.d.ts +1 -1
- package/package.json +4 -3
- package/src/graphs/Graph.ts +8 -8
- package/src/llm/anthropic/llm.ts +7 -8
- package/src/llm/anthropic/types.ts +4 -4
- package/src/llm/anthropic/utils/message_inputs.ts +6 -6
- package/src/llm/anthropic/utils/message_outputs.ts +39 -39
- package/src/llm/fake.ts +2 -2
- package/src/llm/text.ts +1 -1
- package/src/messages/core.ts +6 -6
- package/src/messages/format.ts +43 -42
- package/src/messages/formatAgentMessages.test.ts +35 -35
- package/src/messages/formatAgentMessages.tools.test.ts +30 -30
- package/src/messages/prune.ts +182 -255
- package/src/messages/shiftIndexTokenCountMap.test.ts +18 -18
- package/src/mockStream.ts +1 -1
- package/src/run.ts +2 -2
- package/src/specs/prune.test.ts +89 -89
- package/src/specs/reasoning.test.ts +1 -1
- package/src/specs/thinking-prune.test.ts +265 -261
- package/src/specs/tool-error.test.ts +16 -17
- package/src/stream.ts +13 -14
- package/src/tools/ToolNode.ts +1 -1
- package/src/types/stream.ts +4 -3
- package/src/utils/tokens.ts +12 -12
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
UsageMetadata,
|
|
8
8
|
AIMessageChunk,
|
|
9
9
|
} from '@langchain/core/messages';
|
|
10
|
-
import type { ToolCallChunk } from
|
|
10
|
+
import type { ToolCallChunk } from '@langchain/core/messages/tool';
|
|
11
11
|
import { ToolCall } from '@langchain/core/messages/tool';
|
|
12
12
|
import { ChatGeneration } from '@langchain/core/outputs';
|
|
13
13
|
import { AnthropicMessageResponse } from '../types.js';
|
|
@@ -92,18 +92,18 @@ export function _makeMessageChunkFromAnthropicEvent(
|
|
|
92
92
|
}),
|
|
93
93
|
};
|
|
94
94
|
} else if (
|
|
95
|
-
data.type ===
|
|
96
|
-
[
|
|
95
|
+
data.type === 'content_block_start' &&
|
|
96
|
+
['tool_use', 'document'].includes(data.content_block.type)
|
|
97
97
|
) {
|
|
98
98
|
const contentBlock = data.content_block;
|
|
99
99
|
let toolCallChunks: ToolCallChunk[];
|
|
100
|
-
if (contentBlock.type ===
|
|
100
|
+
if (contentBlock.type === 'tool_use') {
|
|
101
101
|
toolCallChunks = [
|
|
102
102
|
{
|
|
103
103
|
id: contentBlock.id,
|
|
104
104
|
index: data.index,
|
|
105
105
|
name: contentBlock.name,
|
|
106
|
-
args:
|
|
106
|
+
args: '',
|
|
107
107
|
},
|
|
108
108
|
];
|
|
109
109
|
} else {
|
|
@@ -112,28 +112,28 @@ export function _makeMessageChunkFromAnthropicEvent(
|
|
|
112
112
|
return {
|
|
113
113
|
chunk: new AIMessageChunk({
|
|
114
114
|
content: fields.coerceContentToString
|
|
115
|
-
?
|
|
115
|
+
? ''
|
|
116
116
|
: [
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
117
|
+
{
|
|
118
|
+
index: data.index,
|
|
119
|
+
...data.content_block,
|
|
120
|
+
input: '',
|
|
121
|
+
},
|
|
122
|
+
],
|
|
123
123
|
additional_kwargs: {},
|
|
124
124
|
tool_call_chunks: toolCallChunks,
|
|
125
125
|
}),
|
|
126
126
|
};
|
|
127
127
|
} else if (
|
|
128
|
-
data.type ===
|
|
128
|
+
data.type === 'content_block_delta' &&
|
|
129
129
|
[
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
130
|
+
'text_delta',
|
|
131
|
+
'citations_delta',
|
|
132
|
+
'thinking_delta',
|
|
133
|
+
'signature_delta',
|
|
134
134
|
].includes(data.delta.type)
|
|
135
135
|
) {
|
|
136
|
-
if (fields.coerceContentToString &&
|
|
136
|
+
if (fields.coerceContentToString && 'text' in data.delta) {
|
|
137
137
|
return {
|
|
138
138
|
chunk: new AIMessageChunk({
|
|
139
139
|
content: data.delta.text,
|
|
@@ -142,30 +142,30 @@ export function _makeMessageChunkFromAnthropicEvent(
|
|
|
142
142
|
} else {
|
|
143
143
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
144
144
|
const contentBlock: Record<string, any> = data.delta;
|
|
145
|
-
if (
|
|
145
|
+
if ('citation' in contentBlock) {
|
|
146
146
|
contentBlock.citations = [contentBlock.citation];
|
|
147
147
|
delete contentBlock.citation;
|
|
148
148
|
}
|
|
149
149
|
if (
|
|
150
|
-
contentBlock.type ===
|
|
151
|
-
contentBlock.type ===
|
|
150
|
+
contentBlock.type === 'thinking_delta' ||
|
|
151
|
+
contentBlock.type === 'signature_delta'
|
|
152
152
|
) {
|
|
153
153
|
return {
|
|
154
154
|
chunk: new AIMessageChunk({
|
|
155
|
-
content: [{ index: data.index, ...contentBlock, type:
|
|
155
|
+
content: [{ index: data.index, ...contentBlock, type: 'thinking' }],
|
|
156
156
|
}),
|
|
157
157
|
};
|
|
158
158
|
}
|
|
159
159
|
|
|
160
160
|
return {
|
|
161
161
|
chunk: new AIMessageChunk({
|
|
162
|
-
content: [{ index: data.index, ...contentBlock, type:
|
|
162
|
+
content: [{ index: data.index, ...contentBlock, type: 'text' }],
|
|
163
163
|
}),
|
|
164
164
|
};
|
|
165
165
|
}
|
|
166
166
|
} else if (
|
|
167
|
-
data.type ===
|
|
168
|
-
data.delta.type ===
|
|
167
|
+
data.type === 'content_block_delta' &&
|
|
168
|
+
data.delta.type === 'input_json_delta'
|
|
169
169
|
) {
|
|
170
170
|
return {
|
|
171
171
|
chunk: new AIMessageChunk({
|
|
@@ -208,19 +208,19 @@ export function _makeMessageChunkFromAnthropicEvent(
|
|
|
208
208
|
};
|
|
209
209
|
}
|
|
210
210
|
} else if (
|
|
211
|
-
data.type ===
|
|
212
|
-
data.content_block.type ===
|
|
211
|
+
data.type === 'content_block_start' &&
|
|
212
|
+
data.content_block.type === 'redacted_thinking'
|
|
213
213
|
) {
|
|
214
214
|
return {
|
|
215
215
|
chunk: new AIMessageChunk({
|
|
216
216
|
content: fields.coerceContentToString
|
|
217
|
-
?
|
|
217
|
+
? ''
|
|
218
218
|
: [{ index: data.index, ...data.content_block }],
|
|
219
219
|
}),
|
|
220
220
|
};
|
|
221
221
|
} else if (
|
|
222
|
-
data.type ===
|
|
223
|
-
data.content_block.type ===
|
|
222
|
+
data.type === 'content_block_start' &&
|
|
223
|
+
data.content_block.type === 'thinking'
|
|
224
224
|
) {
|
|
225
225
|
const content = data.content_block.thinking;
|
|
226
226
|
return {
|
|
@@ -241,17 +241,17 @@ export function anthropicResponseToChatMessages(
|
|
|
241
241
|
): ChatGeneration[] {
|
|
242
242
|
const usage: Record<string, number> | null | undefined =
|
|
243
243
|
additionalKwargs.usage as Record<string, number> | null | undefined;
|
|
244
|
-
|
|
244
|
+
const usageMetadata =
|
|
245
245
|
usage != null
|
|
246
246
|
? {
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
247
|
+
input_tokens: usage.input_tokens ?? 0,
|
|
248
|
+
output_tokens: usage.output_tokens ?? 0,
|
|
249
|
+
total_tokens: (usage.input_tokens ?? 0) + (usage.output_tokens ?? 0),
|
|
250
|
+
input_token_details: {
|
|
251
|
+
cache_creation: usage.cache_creation_input_tokens,
|
|
252
|
+
cache_read: usage.cache_read_input_tokens,
|
|
253
|
+
},
|
|
254
|
+
}
|
|
255
255
|
: undefined;
|
|
256
256
|
if (messages.length === 1 && messages[0].type === 'text') {
|
|
257
257
|
return [
|
package/src/llm/fake.ts
CHANGED
|
@@ -63,7 +63,7 @@ export class FakeChatModel extends FakeListChatModel {
|
|
|
63
63
|
},
|
|
64
64
|
})),
|
|
65
65
|
} : undefined,
|
|
66
|
-
|
|
66
|
+
})});
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
async *_streamResponseChunks(
|
|
@@ -102,7 +102,7 @@ export class FakeChatModel extends FakeListChatModel {
|
|
|
102
102
|
args: JSON.stringify(toolCall.args),
|
|
103
103
|
id: toolCall.id,
|
|
104
104
|
type: 'tool_call_chunk',
|
|
105
|
-
|
|
105
|
+
} as ToolCallChunk;
|
|
106
106
|
});
|
|
107
107
|
const responseChunk = this._createResponseChunk('', toolCallChunks);
|
|
108
108
|
yield responseChunk;
|
package/src/llm/text.ts
CHANGED
package/src/messages/core.ts
CHANGED
|
@@ -78,20 +78,20 @@ function reduceBlocks(blocks: ContentBlock[]): ContentBlock[] {
|
|
|
78
78
|
const lastBlock = reduced[reduced.length - 1];
|
|
79
79
|
|
|
80
80
|
// Merge consecutive 'reasoning_content'
|
|
81
|
-
if (block.type === 'reasoning_content' && lastBlock
|
|
81
|
+
if (block.type === 'reasoning_content' && lastBlock.type === 'reasoning_content') {
|
|
82
82
|
// append text if exists
|
|
83
|
-
if (block.reasoningText
|
|
83
|
+
if (block.reasoningText.text) {
|
|
84
84
|
lastBlock.reasoningText.text = (lastBlock.reasoningText.text || '') + block.reasoningText.text;
|
|
85
85
|
}
|
|
86
86
|
// preserve the signature if exists
|
|
87
|
-
if (block.reasoningText
|
|
87
|
+
if (block.reasoningText.signature) {
|
|
88
88
|
lastBlock.reasoningText.signature = block.reasoningText.signature;
|
|
89
89
|
}
|
|
90
90
|
}
|
|
91
91
|
// Merge consecutive 'text'
|
|
92
|
-
else if (block.type === 'text' && lastBlock
|
|
92
|
+
else if (block.type === 'text' && lastBlock.type === 'text') {
|
|
93
93
|
lastBlock.text += block.text;
|
|
94
|
-
}
|
|
94
|
+
}
|
|
95
95
|
// add a new block as it's a different type or first element
|
|
96
96
|
else {
|
|
97
97
|
// deep copy to avoid mutation of original
|
|
@@ -155,7 +155,7 @@ export function formatAnthropicMessage(message: AIMessageChunk): AIMessage {
|
|
|
155
155
|
input: toolCall.args as unknown as string
|
|
156
156
|
});
|
|
157
157
|
}
|
|
158
|
-
} catch
|
|
158
|
+
} catch {
|
|
159
159
|
if (extendedItem.input) {
|
|
160
160
|
acc.push({ type: 'text', text: extendedItem.input });
|
|
161
161
|
}
|
package/src/messages/format.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
import { HumanMessage, AIMessage, SystemMessage, getBufferString } from '@langchain/core/messages';
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
import { ToolMessage, BaseMessage , HumanMessage, AIMessage, SystemMessage, getBufferString } from '@langchain/core/messages';
|
|
3
3
|
import type { MessageContentImageUrl } from '@langchain/core/messages';
|
|
4
4
|
import type { ToolCall } from '@langchain/core/messages/tool';
|
|
5
5
|
import type { MessageContentComplex, ToolCallPart, TPayload, TMessage } from '@/types';
|
|
@@ -38,17 +38,17 @@ export const formatVisionMessage = ({ message, image_urls, endpoint }: VisionMes
|
|
|
38
38
|
...message,
|
|
39
39
|
content: [] as MessageContentComplex[]
|
|
40
40
|
};
|
|
41
|
-
|
|
41
|
+
|
|
42
42
|
if (endpoint === Providers.ANTHROPIC) {
|
|
43
43
|
result.content = [
|
|
44
|
-
...image_urls,
|
|
44
|
+
...image_urls,
|
|
45
45
|
{ type: ContentTypes.TEXT, text: message.content }
|
|
46
46
|
] as MessageContentComplex[];
|
|
47
47
|
return result;
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
result.content = [
|
|
51
|
-
{ type: ContentTypes.TEXT, text: message.content },
|
|
51
|
+
{ type: ContentTypes.TEXT, text: message.content },
|
|
52
52
|
...image_urls
|
|
53
53
|
] as MessageContentComplex[];
|
|
54
54
|
|
|
@@ -87,13 +87,14 @@ interface FormattedMessage {
|
|
|
87
87
|
* @param {FormatMessageParams} params - The parameters for formatting.
|
|
88
88
|
* @returns {FormattedMessage | HumanMessage | AIMessage | SystemMessage} - The formatted message.
|
|
89
89
|
*/
|
|
90
|
-
export const formatMessage = ({
|
|
91
|
-
message,
|
|
92
|
-
userName,
|
|
93
|
-
assistantName,
|
|
94
|
-
endpoint,
|
|
95
|
-
langChain = false
|
|
90
|
+
export const formatMessage = ({
|
|
91
|
+
message,
|
|
92
|
+
userName,
|
|
93
|
+
assistantName,
|
|
94
|
+
endpoint,
|
|
95
|
+
langChain = false
|
|
96
96
|
}: FormatMessageParams): FormattedMessage | HumanMessage | AIMessage | SystemMessage => {
|
|
97
|
+
// eslint-disable-next-line prefer-const
|
|
97
98
|
let { role: _role, _name, sender, text, content: _content, lc_id } = message;
|
|
98
99
|
if (lc_id && lc_id[2] && !langChain) {
|
|
99
100
|
const roleMapping: Record<string, string> = {
|
|
@@ -103,7 +104,7 @@ export const formatMessage = ({
|
|
|
103
104
|
};
|
|
104
105
|
_role = roleMapping[lc_id[2]] || _role;
|
|
105
106
|
}
|
|
106
|
-
const role = _role ?? (sender && sender
|
|
107
|
+
const role = _role ?? (sender != null && sender && sender.toLowerCase() === 'user' ? 'user' : 'assistant');
|
|
107
108
|
const content = _content ?? text ?? '';
|
|
108
109
|
const formattedMessage: FormattedMessage = {
|
|
109
110
|
role,
|
|
@@ -122,19 +123,19 @@ export const formatMessage = ({
|
|
|
122
123
|
});
|
|
123
124
|
}
|
|
124
125
|
|
|
125
|
-
if (_name) {
|
|
126
|
+
if (_name != null && _name) {
|
|
126
127
|
formattedMessage.name = _name;
|
|
127
128
|
}
|
|
128
129
|
|
|
129
|
-
if (userName && formattedMessage.role === 'user') {
|
|
130
|
+
if (userName != null && userName && formattedMessage.role === 'user') {
|
|
130
131
|
formattedMessage.name = userName;
|
|
131
132
|
}
|
|
132
133
|
|
|
133
|
-
if (assistantName && formattedMessage.role === 'assistant') {
|
|
134
|
+
if (assistantName != null && assistantName && formattedMessage.role === 'assistant') {
|
|
134
135
|
formattedMessage.name = assistantName;
|
|
135
136
|
}
|
|
136
137
|
|
|
137
|
-
if (formattedMessage.name) {
|
|
138
|
+
if (formattedMessage.name != null && formattedMessage.name) {
|
|
138
139
|
// Conform to API regex: ^[a-zA-Z0-9_-]{1,64}$
|
|
139
140
|
// https://community.openai.com/t/the-format-of-the-name-field-in-the-documentation-is-incorrect/175684/2
|
|
140
141
|
formattedMessage.name = formattedMessage.name.replace(/[^a-zA-Z0-9_-]/g, '_');
|
|
@@ -165,7 +166,7 @@ export const formatMessage = ({
|
|
|
165
166
|
* @returns {Array<HumanMessage | AIMessage | SystemMessage>} - The array of formatted LangChain messages.
|
|
166
167
|
*/
|
|
167
168
|
export const formatLangChainMessages = (
|
|
168
|
-
messages: Array<MessageInput>,
|
|
169
|
+
messages: Array<MessageInput>,
|
|
169
170
|
formatOptions: Omit<FormatMessageParams, 'message' | 'langChain'>
|
|
170
171
|
): Array<HumanMessage | AIMessage | SystemMessage> => {
|
|
171
172
|
return messages.map((msg) => {
|
|
@@ -237,7 +238,7 @@ function formatAssistantMessage(message: Partial<TMessage>): Array<AIMessage | T
|
|
|
237
238
|
content: part.text || '',
|
|
238
239
|
});
|
|
239
240
|
formattedMessages.push(lastAIMessage);
|
|
240
|
-
} else if (part
|
|
241
|
+
} else if (part.type === ContentTypes.TOOL_CALL) {
|
|
241
242
|
if (!lastAIMessage) {
|
|
242
243
|
throw new Error('Invalid tool call structure: No preceding AIMessage with tool_call_ids');
|
|
243
244
|
}
|
|
@@ -251,7 +252,7 @@ function formatAssistantMessage(message: Partial<TMessage>): Array<AIMessage | T
|
|
|
251
252
|
if (typeof _args === 'string') {
|
|
252
253
|
args = JSON.parse(_args);
|
|
253
254
|
}
|
|
254
|
-
} catch
|
|
255
|
+
} catch {
|
|
255
256
|
if (typeof _args === 'string') {
|
|
256
257
|
args = { input: _args };
|
|
257
258
|
}
|
|
@@ -290,7 +291,7 @@ function formatAssistantMessage(message: Partial<TMessage>): Array<AIMessage | T
|
|
|
290
291
|
return acc;
|
|
291
292
|
}, '')
|
|
292
293
|
.trim();
|
|
293
|
-
|
|
294
|
+
|
|
294
295
|
if (content) {
|
|
295
296
|
formattedMessages.push(new AIMessage({ content }));
|
|
296
297
|
}
|
|
@@ -310,7 +311,7 @@ function formatAssistantMessage(message: Partial<TMessage>): Array<AIMessage | T
|
|
|
310
311
|
* @returns {Object} - Object containing formatted messages and updated indexTokenCountMap if provided.
|
|
311
312
|
*/
|
|
312
313
|
export const formatAgentMessages = (
|
|
313
|
-
payload: TPayload,
|
|
314
|
+
payload: TPayload,
|
|
314
315
|
indexTokenCountMap?: Record<number, number>,
|
|
315
316
|
tools?: Set<string>
|
|
316
317
|
): {
|
|
@@ -332,11 +333,11 @@ export const formatAgentMessages = (
|
|
|
332
333
|
message.content = [{ type: ContentTypes.TEXT, [ContentTypes.TEXT]: message.content }];
|
|
333
334
|
}
|
|
334
335
|
if (message.role !== 'assistant') {
|
|
335
|
-
messages.push(formatMessage({
|
|
336
|
-
message: message as MessageInput,
|
|
337
|
-
langChain: true
|
|
336
|
+
messages.push(formatMessage({
|
|
337
|
+
message: message as MessageInput,
|
|
338
|
+
langChain: true
|
|
338
339
|
}) as HumanMessage | AIMessage | SystemMessage);
|
|
339
|
-
|
|
340
|
+
|
|
340
341
|
// Update the index mapping for this message
|
|
341
342
|
indexMapping[i] = [messages.length - 1];
|
|
342
343
|
continue;
|
|
@@ -350,12 +351,12 @@ export const formatAgentMessages = (
|
|
|
350
351
|
// First, check if this message contains tool calls
|
|
351
352
|
let hasToolCalls = false;
|
|
352
353
|
let hasInvalidTool = false;
|
|
353
|
-
|
|
354
|
-
|
|
354
|
+
const toolNames: string[] = [];
|
|
355
|
+
|
|
355
356
|
const content = message.content;
|
|
356
357
|
if (content && Array.isArray(content)) {
|
|
357
358
|
for (const part of content) {
|
|
358
|
-
if (part
|
|
359
|
+
if (part.type === ContentTypes.TOOL_CALL) {
|
|
359
360
|
hasToolCalls = true;
|
|
360
361
|
if (tools.size === 0) {
|
|
361
362
|
hasInvalidTool = true;
|
|
@@ -369,17 +370,17 @@ export const formatAgentMessages = (
|
|
|
369
370
|
}
|
|
370
371
|
}
|
|
371
372
|
}
|
|
372
|
-
|
|
373
|
+
|
|
373
374
|
// If this message has tool calls and at least one is invalid, we need to convert it
|
|
374
375
|
if (hasToolCalls && hasInvalidTool) {
|
|
375
376
|
// We need to collect all related messages (this message and any subsequent tool messages)
|
|
376
377
|
const toolSequence: BaseMessage[] = [];
|
|
377
378
|
let sequenceEndIndex = i;
|
|
378
|
-
|
|
379
|
+
|
|
379
380
|
// Process the current assistant message to get the AIMessage with tool calls
|
|
380
381
|
const formattedMessages = formatAssistantMessage(message);
|
|
381
382
|
toolSequence.push(...formattedMessages);
|
|
382
|
-
|
|
383
|
+
|
|
383
384
|
// Look ahead for any subsequent assistant messages that might be part of this tool sequence
|
|
384
385
|
let j = i + 1;
|
|
385
386
|
while (j < payload.length && payload[j].role === 'assistant') {
|
|
@@ -388,13 +389,13 @@ export const formatAgentMessages = (
|
|
|
388
389
|
const content = payload[j].content;
|
|
389
390
|
if (content && Array.isArray(content)) {
|
|
390
391
|
for (const part of content) {
|
|
391
|
-
if (part
|
|
392
|
+
if (part.type === ContentTypes.TOOL_CALL) {
|
|
392
393
|
isToolResponse = true;
|
|
393
394
|
break;
|
|
394
395
|
}
|
|
395
396
|
}
|
|
396
397
|
}
|
|
397
|
-
|
|
398
|
+
|
|
398
399
|
if (isToolResponse) {
|
|
399
400
|
// This is part of the tool sequence, add it
|
|
400
401
|
const nextMessages = formatAssistantMessage(payload[j]);
|
|
@@ -406,20 +407,20 @@ export const formatAgentMessages = (
|
|
|
406
407
|
break;
|
|
407
408
|
}
|
|
408
409
|
}
|
|
409
|
-
|
|
410
|
+
|
|
410
411
|
// Convert the sequence to a string
|
|
411
412
|
const bufferString = getBufferString(toolSequence);
|
|
412
413
|
messages.push(new AIMessage({ content: bufferString }));
|
|
413
|
-
|
|
414
|
+
|
|
414
415
|
// Skip the messages we've already processed
|
|
415
416
|
i = sequenceEndIndex;
|
|
416
|
-
|
|
417
|
+
|
|
417
418
|
// Update the index mapping for this sequence
|
|
418
419
|
const resultIndices = [messages.length - 1];
|
|
419
420
|
for (let k = i; k >= i && k <= sequenceEndIndex; k++) {
|
|
420
421
|
indexMapping[k] = resultIndices;
|
|
421
422
|
}
|
|
422
|
-
|
|
423
|
+
|
|
423
424
|
continue;
|
|
424
425
|
}
|
|
425
426
|
}
|
|
@@ -427,7 +428,7 @@ export const formatAgentMessages = (
|
|
|
427
428
|
// Process the assistant message using the helper function
|
|
428
429
|
const formattedMessages = formatAssistantMessage(message);
|
|
429
430
|
messages.push(...formattedMessages);
|
|
430
|
-
|
|
431
|
+
|
|
431
432
|
// Update the index mapping for this assistant message
|
|
432
433
|
// Store all indices that were created from this original message
|
|
433
434
|
const endMessageIndex = messages.length;
|
|
@@ -443,7 +444,7 @@ export const formatAgentMessages = (
|
|
|
443
444
|
for (let originalIndex = 0; originalIndex < payload.length; originalIndex++) {
|
|
444
445
|
const resultIndices = indexMapping[originalIndex] || [];
|
|
445
446
|
const tokenCount = indexTokenCountMap[originalIndex];
|
|
446
|
-
|
|
447
|
+
|
|
447
448
|
if (tokenCount !== undefined) {
|
|
448
449
|
if (resultIndices.length === 1) {
|
|
449
450
|
// Simple 1:1 mapping
|
|
@@ -506,7 +507,7 @@ export const formatContentStrings = (payload: Array<BaseMessage>): Array<BaseMes
|
|
|
506
507
|
/**
|
|
507
508
|
* Adds a value at key 0 for system messages and shifts all key indices by one in an indexTokenCountMap.
|
|
508
509
|
* This is useful when adding a system message at the beginning of a conversation.
|
|
509
|
-
*
|
|
510
|
+
*
|
|
510
511
|
* @param indexTokenCountMap - The original map of message indices to token counts
|
|
511
512
|
* @param instructionsTokenCount - The token count for the system message to add at index 0
|
|
512
513
|
* @returns A new map with the system message at index 0 and all other indices shifted by 1
|
|
@@ -518,12 +519,12 @@ export function shiftIndexTokenCountMap(
|
|
|
518
519
|
// Create a new map to avoid modifying the original
|
|
519
520
|
const shiftedMap: Record<number, number> = {};
|
|
520
521
|
shiftedMap[0] = instructionsTokenCount;
|
|
521
|
-
|
|
522
|
+
|
|
522
523
|
// Shift all existing indices by 1
|
|
523
524
|
for (const [indexStr, tokenCount] of Object.entries(indexTokenCountMap)) {
|
|
524
525
|
const index = Number(indexStr);
|
|
525
526
|
shiftedMap[index + 1] = tokenCount;
|
|
526
527
|
}
|
|
527
|
-
|
|
528
|
+
|
|
528
529
|
return shiftedMap;
|
|
529
530
|
}
|
|
@@ -362,14 +362,14 @@ describe('formatAgentMessages', () => {
|
|
|
362
362
|
{ role: 'user', content: 'Hello' },
|
|
363
363
|
{ role: 'assistant', content: 'Hi there!' },
|
|
364
364
|
];
|
|
365
|
-
|
|
365
|
+
|
|
366
366
|
const indexTokenCountMap = {
|
|
367
367
|
0: 5, // 5 tokens for "Hello"
|
|
368
368
|
1: 10, // 10 tokens for "Hi there!"
|
|
369
369
|
};
|
|
370
|
-
|
|
370
|
+
|
|
371
371
|
const result = formatAgentMessages(payload, indexTokenCountMap);
|
|
372
|
-
|
|
372
|
+
|
|
373
373
|
expect(result.messages).toHaveLength(2);
|
|
374
374
|
expect(result.indexTokenCountMap).toBeDefined();
|
|
375
375
|
expect(result.indexTokenCountMap?.[0]).toBe(5);
|
|
@@ -399,23 +399,23 @@ describe('formatAgentMessages', () => {
|
|
|
399
399
|
],
|
|
400
400
|
},
|
|
401
401
|
];
|
|
402
|
-
|
|
402
|
+
|
|
403
403
|
const indexTokenCountMap = {
|
|
404
404
|
0: 10, // 10 tokens for "What's the weather?"
|
|
405
405
|
1: 50, // 50 tokens for the assistant message with tool call
|
|
406
406
|
};
|
|
407
|
-
|
|
407
|
+
|
|
408
408
|
const result = formatAgentMessages(payload, indexTokenCountMap);
|
|
409
|
-
|
|
409
|
+
|
|
410
410
|
// The original message at index 1 should be split into two messages
|
|
411
411
|
expect(result.messages).toHaveLength(3);
|
|
412
412
|
expect(result.indexTokenCountMap).toBeDefined();
|
|
413
413
|
expect(result.indexTokenCountMap?.[0]).toBe(10); // User message stays the same
|
|
414
|
-
|
|
414
|
+
|
|
415
415
|
// The assistant message tokens should be distributed across the resulting messages
|
|
416
416
|
const totalAssistantTokens = Object.values(result.indexTokenCountMap || {})
|
|
417
417
|
.reduce((sum, count) => sum + count, 0) - 10; // Subtract user message tokens
|
|
418
|
-
|
|
418
|
+
|
|
419
419
|
expect(totalAssistantTokens).toBe(50); // Should match the original token count
|
|
420
420
|
});
|
|
421
421
|
|
|
@@ -460,23 +460,23 @@ describe('formatAgentMessages', () => {
|
|
|
460
460
|
],
|
|
461
461
|
},
|
|
462
462
|
];
|
|
463
|
-
|
|
463
|
+
|
|
464
464
|
const indexTokenCountMap = {
|
|
465
465
|
0: 100, // 100 tokens for the complex assistant message
|
|
466
466
|
};
|
|
467
|
-
|
|
467
|
+
|
|
468
468
|
const result = formatAgentMessages(payload, indexTokenCountMap);
|
|
469
|
-
|
|
469
|
+
|
|
470
470
|
// One message expands to 5 messages (2 tool calls + text before, between, and after)
|
|
471
471
|
expect(result.messages).toHaveLength(5);
|
|
472
472
|
expect(result.indexTokenCountMap).toBeDefined();
|
|
473
|
-
|
|
473
|
+
|
|
474
474
|
// The sum of all token counts should equal the original
|
|
475
475
|
const totalTokens = Object.values(result.indexTokenCountMap || {})
|
|
476
476
|
.reduce((sum, count) => sum + count, 0);
|
|
477
|
-
|
|
477
|
+
|
|
478
478
|
expect(totalTokens).toBe(100);
|
|
479
|
-
|
|
479
|
+
|
|
480
480
|
// Check that each resulting message has a token count
|
|
481
481
|
for (let i = 0; i < result.messages.length; i++) {
|
|
482
482
|
expect(result.indexTokenCountMap?.[i]).toBeDefined();
|
|
@@ -495,17 +495,17 @@ describe('formatAgentMessages', () => {
|
|
|
495
495
|
],
|
|
496
496
|
},
|
|
497
497
|
];
|
|
498
|
-
|
|
498
|
+
|
|
499
499
|
const indexTokenCountMap = {
|
|
500
500
|
0: 60, // 60 tokens for the message with filtered content
|
|
501
501
|
};
|
|
502
|
-
|
|
502
|
+
|
|
503
503
|
const result = formatAgentMessages(payload, indexTokenCountMap);
|
|
504
|
-
|
|
504
|
+
|
|
505
505
|
// Only one message should remain after filtering
|
|
506
506
|
expect(result.messages).toHaveLength(1);
|
|
507
507
|
expect(result.indexTokenCountMap).toBeDefined();
|
|
508
|
-
|
|
508
|
+
|
|
509
509
|
// All tokens should be assigned to the remaining message
|
|
510
510
|
expect(result.indexTokenCountMap?.[0]).toBe(60);
|
|
511
511
|
});
|
|
@@ -522,17 +522,17 @@ describe('formatAgentMessages', () => {
|
|
|
522
522
|
],
|
|
523
523
|
},
|
|
524
524
|
];
|
|
525
|
-
|
|
525
|
+
|
|
526
526
|
const indexTokenCountMap = {
|
|
527
527
|
0: 40, // 40 tokens for the message with filtered content
|
|
528
528
|
};
|
|
529
|
-
|
|
529
|
+
|
|
530
530
|
const result = formatAgentMessages(payload, indexTokenCountMap);
|
|
531
|
-
|
|
531
|
+
|
|
532
532
|
// No messages should remain after filtering
|
|
533
533
|
expect(result.messages).toHaveLength(0);
|
|
534
534
|
expect(result.indexTokenCountMap).toBeDefined();
|
|
535
|
-
|
|
535
|
+
|
|
536
536
|
// The token count map should be empty since there are no messages
|
|
537
537
|
expect(Object.keys(result.indexTokenCountMap || {})).toHaveLength(0);
|
|
538
538
|
});
|
|
@@ -561,29 +561,29 @@ describe('formatAgentMessages', () => {
|
|
|
561
561
|
],
|
|
562
562
|
},
|
|
563
563
|
];
|
|
564
|
-
|
|
564
|
+
|
|
565
565
|
const indexTokenCountMap = {
|
|
566
566
|
0: 15, // 15 tokens for the user message
|
|
567
567
|
1: 45, // 45 tokens for the assistant message with tool call
|
|
568
568
|
};
|
|
569
|
-
|
|
569
|
+
|
|
570
570
|
const result = formatAgentMessages(payload, indexTokenCountMap);
|
|
571
|
-
|
|
571
|
+
|
|
572
572
|
// 2 input messages become 3 output messages (user + assistant + tool)
|
|
573
573
|
expect(payload).toHaveLength(2);
|
|
574
574
|
expect(result.messages).toHaveLength(3);
|
|
575
575
|
expect(result.indexTokenCountMap).toBeDefined();
|
|
576
576
|
expect(Object.keys(result.indexTokenCountMap ?? {}).length).toBe(3);
|
|
577
|
-
|
|
577
|
+
|
|
578
578
|
// Check message types
|
|
579
579
|
expect(result.messages[0]).toBeInstanceOf(HumanMessage);
|
|
580
580
|
expect(result.messages[1]).toBeInstanceOf(AIMessage);
|
|
581
581
|
expect(result.messages[2]).toBeInstanceOf(ToolMessage);
|
|
582
|
-
|
|
582
|
+
|
|
583
583
|
// The sum of all token counts should equal the original total
|
|
584
584
|
const totalTokens = Object.values(result.indexTokenCountMap || {})
|
|
585
585
|
.reduce((sum, count) => sum + count, 0);
|
|
586
|
-
|
|
586
|
+
|
|
587
587
|
expect(totalTokens).toBe(60); // 15 + 45
|
|
588
588
|
});
|
|
589
589
|
|
|
@@ -600,30 +600,30 @@ describe('formatAgentMessages', () => {
|
|
|
600
600
|
],
|
|
601
601
|
},
|
|
602
602
|
];
|
|
603
|
-
|
|
603
|
+
|
|
604
604
|
const indexTokenCountMap = {
|
|
605
605
|
0: 10, // 10 tokens for the user message
|
|
606
606
|
1: 30, // 30 tokens for the assistant message that will be filtered out
|
|
607
607
|
};
|
|
608
|
-
|
|
608
|
+
|
|
609
609
|
const result = formatAgentMessages(payload, indexTokenCountMap);
|
|
610
|
-
|
|
610
|
+
|
|
611
611
|
// 2 input messages become 1 output message (only the user message remains)
|
|
612
612
|
expect(payload).toHaveLength(2);
|
|
613
613
|
expect(result.messages).toHaveLength(1);
|
|
614
614
|
expect(result.indexTokenCountMap).toBeDefined();
|
|
615
615
|
expect(Object.keys(result.indexTokenCountMap ?? {}).length).toBe(1);
|
|
616
|
-
|
|
616
|
+
|
|
617
617
|
// Check message type
|
|
618
618
|
expect(result.messages[0]).toBeInstanceOf(HumanMessage);
|
|
619
|
-
|
|
619
|
+
|
|
620
620
|
// Only the user message tokens should remain
|
|
621
621
|
expect(result.indexTokenCountMap?.[0]).toBe(10);
|
|
622
|
-
|
|
622
|
+
|
|
623
623
|
// The total tokens should be just the user message tokens
|
|
624
624
|
const totalTokens = Object.values(result.indexTokenCountMap || {})
|
|
625
625
|
.reduce((sum, count) => sum + count, 0);
|
|
626
|
-
|
|
626
|
+
|
|
627
627
|
expect(totalTokens).toBe(10);
|
|
628
628
|
});
|
|
629
629
|
});
|