@librechat/agents 3.1.75-dev.1 → 3.1.76
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/llm/openai/index.cjs +43 -0
- package/dist/cjs/llm/openai/index.cjs.map +1 -1
- package/dist/cjs/llm/openai/utils/index.cjs +19 -10
- package/dist/cjs/llm/openai/utils/index.cjs.map +1 -1
- package/dist/cjs/messages/format.cjs +67 -10
- package/dist/cjs/messages/format.cjs.map +1 -1
- package/dist/cjs/tools/search/search.cjs +55 -66
- package/dist/cjs/tools/search/search.cjs.map +1 -1
- package/dist/cjs/tools/search/tavily-scraper.cjs +189 -0
- package/dist/cjs/tools/search/tavily-scraper.cjs.map +1 -0
- package/dist/cjs/tools/search/tavily-search.cjs +372 -0
- package/dist/cjs/tools/search/tavily-search.cjs.map +1 -0
- package/dist/cjs/tools/search/tool.cjs +26 -4
- package/dist/cjs/tools/search/tool.cjs.map +1 -1
- package/dist/cjs/tools/search/utils.cjs +10 -3
- package/dist/cjs/tools/search/utils.cjs.map +1 -1
- package/dist/esm/llm/openai/index.mjs +43 -0
- package/dist/esm/llm/openai/index.mjs.map +1 -1
- package/dist/esm/llm/openai/utils/index.mjs +19 -10
- package/dist/esm/llm/openai/utils/index.mjs.map +1 -1
- package/dist/esm/messages/format.mjs +67 -10
- package/dist/esm/messages/format.mjs.map +1 -1
- package/dist/esm/tools/search/search.mjs +55 -66
- package/dist/esm/tools/search/search.mjs.map +1 -1
- package/dist/esm/tools/search/tavily-scraper.mjs +186 -0
- package/dist/esm/tools/search/tavily-scraper.mjs.map +1 -0
- package/dist/esm/tools/search/tavily-search.mjs +370 -0
- package/dist/esm/tools/search/tavily-search.mjs.map +1 -0
- package/dist/esm/tools/search/tool.mjs +26 -4
- package/dist/esm/tools/search/tool.mjs.map +1 -1
- package/dist/esm/tools/search/utils.mjs +10 -3
- package/dist/esm/tools/search/utils.mjs.map +1 -1
- package/dist/types/messages/format.d.ts +4 -1
- package/dist/types/tools/search/tavily-scraper.d.ts +19 -0
- package/dist/types/tools/search/tavily-search.d.ts +4 -0
- package/dist/types/tools/search/types.d.ts +99 -5
- package/dist/types/tools/search/utils.d.ts +2 -2
- package/package.json +1 -1
- package/src/llm/custom-chat-models.smoke.test.ts +175 -1
- package/src/llm/openai/index.ts +124 -0
- package/src/llm/openai/utils/index.ts +23 -14
- package/src/llm/openai/utils/messages.test.ts +159 -0
- package/src/messages/format.ts +90 -13
- package/src/messages/formatAgentMessages.test.ts +166 -1
- package/src/tools/search/search.ts +83 -73
- package/src/tools/search/tavily-scraper.ts +235 -0
- package/src/tools/search/tavily-search.ts +424 -0
- package/src/tools/search/tavily.test.ts +965 -0
- package/src/tools/search/tool.ts +36 -26
- package/src/tools/search/types.ts +134 -11
- package/src/tools/search/utils.ts +13 -5
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { AIMessage, HumanMessage, ToolMessage } from '@langchain/core/messages';
|
|
2
|
+
import { _convertMessagesToOpenAIParams } from './index';
|
|
3
|
+
|
|
4
|
+
describe('_convertMessagesToOpenAIParams', () => {
|
|
5
|
+
it('includes reasoning_content for assistant messages in tool-call context when requested', () => {
|
|
6
|
+
const messages = [
|
|
7
|
+
new AIMessage({
|
|
8
|
+
content: '',
|
|
9
|
+
tool_calls: [
|
|
10
|
+
{
|
|
11
|
+
id: 'call_1',
|
|
12
|
+
name: 'calculator',
|
|
13
|
+
args: { input: '127 * 453' },
|
|
14
|
+
type: 'tool_call',
|
|
15
|
+
},
|
|
16
|
+
],
|
|
17
|
+
additional_kwargs: {
|
|
18
|
+
reasoning_content: 'Need calculator.',
|
|
19
|
+
},
|
|
20
|
+
}),
|
|
21
|
+
new ToolMessage({
|
|
22
|
+
content: '57531',
|
|
23
|
+
tool_call_id: 'call_1',
|
|
24
|
+
}),
|
|
25
|
+
new AIMessage({
|
|
26
|
+
content: '127 * 453 = 57531.',
|
|
27
|
+
additional_kwargs: {
|
|
28
|
+
reasoning_content: 'Calculator returned 57531.',
|
|
29
|
+
},
|
|
30
|
+
}),
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
const params = _convertMessagesToOpenAIParams(messages, 'deepseek-v4-pro', {
|
|
34
|
+
includeReasoningContent: true,
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
expect(params).toHaveLength(3);
|
|
38
|
+
expect(params[0]).toEqual(
|
|
39
|
+
expect.objectContaining({
|
|
40
|
+
role: 'assistant',
|
|
41
|
+
content: '',
|
|
42
|
+
reasoning_content: 'Need calculator.',
|
|
43
|
+
})
|
|
44
|
+
);
|
|
45
|
+
expect(params[2]).toEqual(
|
|
46
|
+
expect.objectContaining({
|
|
47
|
+
role: 'assistant',
|
|
48
|
+
reasoning_content: 'Calculator returned 57531.',
|
|
49
|
+
})
|
|
50
|
+
);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('does not include reasoning_content for no-tool assistant messages', () => {
|
|
54
|
+
const messages = [
|
|
55
|
+
new AIMessage({
|
|
56
|
+
content: '127 * 453 = 57531.',
|
|
57
|
+
additional_kwargs: {
|
|
58
|
+
reasoning_content: 'Mental calculation.',
|
|
59
|
+
},
|
|
60
|
+
}),
|
|
61
|
+
];
|
|
62
|
+
|
|
63
|
+
const params = _convertMessagesToOpenAIParams(messages, 'deepseek-v4-pro', {
|
|
64
|
+
includeReasoningContent: true,
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
expect(params).toHaveLength(1);
|
|
68
|
+
expect(params[0]).not.toHaveProperty('reasoning_content');
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('does not include reasoning_content unless explicitly requested', () => {
|
|
72
|
+
const messages = [
|
|
73
|
+
new AIMessage({
|
|
74
|
+
content: '',
|
|
75
|
+
tool_calls: [
|
|
76
|
+
{
|
|
77
|
+
id: 'call_1',
|
|
78
|
+
name: 'calculator',
|
|
79
|
+
args: { input: '127 * 453' },
|
|
80
|
+
type: 'tool_call',
|
|
81
|
+
},
|
|
82
|
+
],
|
|
83
|
+
additional_kwargs: {
|
|
84
|
+
reasoning_content: 'Need calculator.',
|
|
85
|
+
},
|
|
86
|
+
}),
|
|
87
|
+
];
|
|
88
|
+
|
|
89
|
+
const params = _convertMessagesToOpenAIParams(messages, 'deepseek-v4-pro');
|
|
90
|
+
|
|
91
|
+
expect(params).toHaveLength(1);
|
|
92
|
+
expect(params[0]).not.toHaveProperty('reasoning_content');
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('keeps reasoning_content latched after tool-call context is established', () => {
|
|
96
|
+
const messages = [
|
|
97
|
+
new AIMessage({
|
|
98
|
+
content: 'No tool was needed.',
|
|
99
|
+
additional_kwargs: {
|
|
100
|
+
reasoning_content: 'Initial no-tool reasoning.',
|
|
101
|
+
},
|
|
102
|
+
}),
|
|
103
|
+
new HumanMessage('Use the calculator.'),
|
|
104
|
+
new AIMessage({
|
|
105
|
+
content: '',
|
|
106
|
+
tool_calls: [
|
|
107
|
+
{
|
|
108
|
+
id: 'call_1',
|
|
109
|
+
name: 'calculator',
|
|
110
|
+
args: { input: '127 * 453' },
|
|
111
|
+
type: 'tool_call',
|
|
112
|
+
},
|
|
113
|
+
],
|
|
114
|
+
additional_kwargs: {
|
|
115
|
+
reasoning_content: 'Need calculator.',
|
|
116
|
+
},
|
|
117
|
+
}),
|
|
118
|
+
new ToolMessage({
|
|
119
|
+
content: '57531',
|
|
120
|
+
tool_call_id: 'call_1',
|
|
121
|
+
}),
|
|
122
|
+
new AIMessage({
|
|
123
|
+
content: '127 * 453 = 57531.',
|
|
124
|
+
additional_kwargs: {
|
|
125
|
+
reasoning_content: 'Calculator returned 57531.',
|
|
126
|
+
},
|
|
127
|
+
}),
|
|
128
|
+
new HumanMessage('Was that correct?'),
|
|
129
|
+
new AIMessage({
|
|
130
|
+
content: 'Yes.',
|
|
131
|
+
additional_kwargs: {
|
|
132
|
+
reasoning_content: 'The prior calculator result is available.',
|
|
133
|
+
},
|
|
134
|
+
}),
|
|
135
|
+
];
|
|
136
|
+
|
|
137
|
+
const params = _convertMessagesToOpenAIParams(messages, 'deepseek-v4-pro', {
|
|
138
|
+
includeReasoningContent: true,
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
expect(params).toHaveLength(7);
|
|
142
|
+
expect(params[0]).not.toHaveProperty('reasoning_content');
|
|
143
|
+
expect(params[2]).toEqual(
|
|
144
|
+
expect.objectContaining({
|
|
145
|
+
reasoning_content: 'Need calculator.',
|
|
146
|
+
})
|
|
147
|
+
);
|
|
148
|
+
expect(params[4]).toEqual(
|
|
149
|
+
expect.objectContaining({
|
|
150
|
+
reasoning_content: 'Calculator returned 57531.',
|
|
151
|
+
})
|
|
152
|
+
);
|
|
153
|
+
expect(params[6]).toEqual(
|
|
154
|
+
expect.objectContaining({
|
|
155
|
+
reasoning_content: 'The prior calculator result is available.',
|
|
156
|
+
})
|
|
157
|
+
);
|
|
158
|
+
});
|
|
159
|
+
});
|
package/src/messages/format.ts
CHANGED
|
@@ -7,13 +7,19 @@ import {
|
|
|
7
7
|
HumanMessage,
|
|
8
8
|
SystemMessage,
|
|
9
9
|
} from '@langchain/core/messages';
|
|
10
|
-
import type {
|
|
10
|
+
import type {
|
|
11
|
+
MessageContent,
|
|
12
|
+
MessageContentImageUrl,
|
|
13
|
+
} from '@langchain/core/messages';
|
|
11
14
|
import type { ToolCall } from '@langchain/core/messages/tool';
|
|
12
15
|
import type {
|
|
16
|
+
BedrockReasoningContentText,
|
|
13
17
|
ExtendedMessageContent,
|
|
18
|
+
GoogleReasoningContentText,
|
|
14
19
|
MessageContentComplex,
|
|
15
20
|
ReasoningContentText,
|
|
16
21
|
SummaryContentBlock,
|
|
22
|
+
ThinkingContentText,
|
|
17
23
|
ToolCallContent,
|
|
18
24
|
ToolCallPart,
|
|
19
25
|
TPayload,
|
|
@@ -277,18 +283,86 @@ export const formatFromLangChain = (
|
|
|
277
283
|
};
|
|
278
284
|
};
|
|
279
285
|
|
|
286
|
+
interface FormatAssistantMessageOptions {
|
|
287
|
+
preserveReasoningContent?: boolean;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
interface FormatAgentMessagesOptions {
|
|
291
|
+
provider?: Providers;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
function extractReasoningContent(
|
|
295
|
+
part: MessageContentComplex | undefined | null
|
|
296
|
+
): string {
|
|
297
|
+
if (part == null || typeof part !== 'object') {
|
|
298
|
+
return '';
|
|
299
|
+
}
|
|
300
|
+
if (part.type === ContentTypes.THINK) {
|
|
301
|
+
const think = (part as ReasoningContentText).think;
|
|
302
|
+
return typeof think === 'string' ? think : '';
|
|
303
|
+
}
|
|
304
|
+
if (part.type === ContentTypes.THINKING) {
|
|
305
|
+
const thinking = (part as ThinkingContentText).thinking;
|
|
306
|
+
return typeof thinking === 'string' ? thinking : '';
|
|
307
|
+
}
|
|
308
|
+
if (part.type === ContentTypes.REASONING) {
|
|
309
|
+
const reasoning = (part as GoogleReasoningContentText).reasoning;
|
|
310
|
+
return typeof reasoning === 'string' ? reasoning : '';
|
|
311
|
+
}
|
|
312
|
+
if (part.type === ContentTypes.REASONING_CONTENT) {
|
|
313
|
+
const reasoningText = (part as BedrockReasoningContentText).reasoningText;
|
|
314
|
+
return typeof reasoningText.text === 'string' ? reasoningText.text : '';
|
|
315
|
+
}
|
|
316
|
+
return '';
|
|
317
|
+
}
|
|
318
|
+
|
|
280
319
|
/**
|
|
281
320
|
* Helper function to format an assistant message
|
|
282
321
|
* @param message The message to format
|
|
322
|
+
* @param options Optional formatting options
|
|
283
323
|
* @returns Array of formatted messages
|
|
284
324
|
*/
|
|
285
325
|
function formatAssistantMessage(
|
|
286
|
-
message: Partial<TMessage
|
|
326
|
+
message: Partial<TMessage>,
|
|
327
|
+
options?: FormatAssistantMessageOptions
|
|
287
328
|
): Array<AIMessage | ToolMessage> {
|
|
288
329
|
const formattedMessages: Array<AIMessage | ToolMessage> = [];
|
|
289
330
|
let currentContent: MessageContentComplex[] = [];
|
|
290
331
|
let lastAIMessage: AIMessage | null = null;
|
|
291
332
|
let hasReasoning = false;
|
|
333
|
+
let pendingReasoningContent = '';
|
|
334
|
+
const shouldPreserveReasoningContent =
|
|
335
|
+
options?.preserveReasoningContent === true;
|
|
336
|
+
|
|
337
|
+
const takePendingReasoningContent = (): string | undefined => {
|
|
338
|
+
if (!shouldPreserveReasoningContent || !pendingReasoningContent) {
|
|
339
|
+
return undefined;
|
|
340
|
+
}
|
|
341
|
+
const reasoningContent = pendingReasoningContent;
|
|
342
|
+
pendingReasoningContent = '';
|
|
343
|
+
return reasoningContent;
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
const createAIMessage = (content: MessageContent): AIMessage => {
|
|
347
|
+
const reasoningContent = takePendingReasoningContent();
|
|
348
|
+
return new AIMessage({
|
|
349
|
+
content,
|
|
350
|
+
...(reasoningContent != null && {
|
|
351
|
+
additional_kwargs: { reasoning_content: reasoningContent },
|
|
352
|
+
}),
|
|
353
|
+
});
|
|
354
|
+
};
|
|
355
|
+
|
|
356
|
+
const attachPendingReasoningContent = (aiMessage: AIMessage): void => {
|
|
357
|
+
const reasoningContent = takePendingReasoningContent();
|
|
358
|
+
if (reasoningContent == null) {
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
aiMessage.additional_kwargs.reasoning_content =
|
|
362
|
+
typeof aiMessage.additional_kwargs.reasoning_content === 'string'
|
|
363
|
+
? `${aiMessage.additional_kwargs.reasoning_content}${reasoningContent}`
|
|
364
|
+
: reasoningContent;
|
|
365
|
+
};
|
|
292
366
|
|
|
293
367
|
if (Array.isArray(message.content)) {
|
|
294
368
|
for (const part of message.content as Array<
|
|
@@ -311,15 +385,13 @@ function formatAssistantMessage(
|
|
|
311
385
|
}, '');
|
|
312
386
|
content =
|
|
313
387
|
`${content}\n${part[ContentTypes.TEXT] ?? part.text ?? ''}`.trim();
|
|
314
|
-
lastAIMessage =
|
|
388
|
+
lastAIMessage = createAIMessage(content);
|
|
315
389
|
formattedMessages.push(lastAIMessage);
|
|
316
390
|
currentContent = [];
|
|
317
391
|
continue;
|
|
318
392
|
}
|
|
319
393
|
// Create a new AIMessage with this text and prepare for tool calls
|
|
320
|
-
lastAIMessage =
|
|
321
|
-
content: part.text != null ? part.text : '',
|
|
322
|
-
});
|
|
394
|
+
lastAIMessage = createAIMessage(part.text != null ? part.text : '');
|
|
323
395
|
formattedMessages.push(lastAIMessage);
|
|
324
396
|
} else if (part.type === ContentTypes.TOOL_CALL) {
|
|
325
397
|
// Skip malformed tool call entries without tool_call property
|
|
@@ -344,8 +416,10 @@ function formatAssistantMessage(
|
|
|
344
416
|
|
|
345
417
|
if (!lastAIMessage) {
|
|
346
418
|
// "Heal" the payload by creating an AIMessage to precede the tool call
|
|
347
|
-
lastAIMessage =
|
|
419
|
+
lastAIMessage = createAIMessage('');
|
|
348
420
|
formattedMessages.push(lastAIMessage);
|
|
421
|
+
} else {
|
|
422
|
+
attachPendingReasoningContent(lastAIMessage);
|
|
349
423
|
}
|
|
350
424
|
|
|
351
425
|
const tool_call: ToolCallPart = _tool_call;
|
|
@@ -377,10 +451,12 @@ function formatAssistantMessage(
|
|
|
377
451
|
} else if (
|
|
378
452
|
part.type === ContentTypes.THINK ||
|
|
379
453
|
part.type === ContentTypes.THINKING ||
|
|
454
|
+
part.type === ContentTypes.REASONING ||
|
|
380
455
|
part.type === ContentTypes.REASONING_CONTENT ||
|
|
381
456
|
part.type === 'redacted_thinking'
|
|
382
457
|
) {
|
|
383
458
|
hasReasoning = true;
|
|
459
|
+
pendingReasoningContent += extractReasoningContent(part);
|
|
384
460
|
continue;
|
|
385
461
|
} else if (
|
|
386
462
|
part.type === ContentTypes.ERROR ||
|
|
@@ -411,12 +487,10 @@ function formatAssistantMessage(
|
|
|
411
487
|
.trim();
|
|
412
488
|
|
|
413
489
|
if (content) {
|
|
414
|
-
formattedMessages.push(
|
|
490
|
+
formattedMessages.push(createAIMessage(content));
|
|
415
491
|
}
|
|
416
492
|
} else if (currentContent.length > 0) {
|
|
417
|
-
formattedMessages.push(
|
|
418
|
-
new AIMessage({ content: toLangChainContent(currentContent) })
|
|
419
|
-
);
|
|
493
|
+
formattedMessages.push(createAIMessage(toLangChainContent(currentContent)));
|
|
420
494
|
}
|
|
421
495
|
|
|
422
496
|
return formattedMessages;
|
|
@@ -832,7 +906,8 @@ export const formatAgentMessages = (
|
|
|
832
906
|
/** Pre-resolved skill bodies keyed by skill name. When present, HumanMessages
|
|
833
907
|
* are reconstructed after skill ToolMessages to restore skill instructions
|
|
834
908
|
* that were only in LangGraph state during the original run. */
|
|
835
|
-
skills?: Map<string, string
|
|
909
|
+
skills?: Map<string, string>,
|
|
910
|
+
options?: FormatAgentMessagesOptions
|
|
836
911
|
): {
|
|
837
912
|
messages: Array<HumanMessage | AIMessage | SystemMessage | ToolMessage>;
|
|
838
913
|
indexTokenCountMap?: Record<number, number>;
|
|
@@ -1080,7 +1155,9 @@ export const formatAgentMessages = (
|
|
|
1080
1155
|
}
|
|
1081
1156
|
}
|
|
1082
1157
|
|
|
1083
|
-
const formattedMessages = formatAssistantMessage(processedMessage
|
|
1158
|
+
const formattedMessages = formatAssistantMessage(processedMessage, {
|
|
1159
|
+
preserveReasoningContent: options?.provider === Providers.DEEPSEEK,
|
|
1160
|
+
});
|
|
1084
1161
|
if (sourceMessageId != null && sourceMessageId !== '') {
|
|
1085
1162
|
for (const formattedMessage of formattedMessages) {
|
|
1086
1163
|
formattedMessage.id = sourceMessageId;
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
} from '@langchain/core/messages';
|
|
7
7
|
import type { MessageContentComplex, TPayload } from '@/types';
|
|
8
8
|
import { formatAgentMessages } from './format';
|
|
9
|
-
import { ContentTypes } from '@/common';
|
|
9
|
+
import { ContentTypes, Providers } from '@/common';
|
|
10
10
|
|
|
11
11
|
describe('formatAgentMessages', () => {
|
|
12
12
|
it('should format simple user and AI messages', () => {
|
|
@@ -967,6 +967,171 @@ describe('formatAgentMessages', () => {
|
|
|
967
967
|
);
|
|
968
968
|
});
|
|
969
969
|
|
|
970
|
+
it('should preserve hidden reasoning_content for DeepSeek assistant messages', () => {
|
|
971
|
+
const payload: TPayload = [
|
|
972
|
+
{
|
|
973
|
+
role: 'assistant',
|
|
974
|
+
content: [
|
|
975
|
+
{
|
|
976
|
+
type: ContentTypes.THINK,
|
|
977
|
+
[ContentTypes.THINK]: 'Need calculator.',
|
|
978
|
+
},
|
|
979
|
+
{
|
|
980
|
+
type: ContentTypes.TEXT,
|
|
981
|
+
[ContentTypes.TEXT]: 'Using calculator.',
|
|
982
|
+
tool_call_ids: ['call_1'],
|
|
983
|
+
},
|
|
984
|
+
{
|
|
985
|
+
type: ContentTypes.TOOL_CALL,
|
|
986
|
+
tool_call: {
|
|
987
|
+
id: 'call_1',
|
|
988
|
+
name: 'calculator',
|
|
989
|
+
args: '{"input":"127 * 453"}',
|
|
990
|
+
output: '57531',
|
|
991
|
+
},
|
|
992
|
+
},
|
|
993
|
+
{
|
|
994
|
+
type: ContentTypes.THINK,
|
|
995
|
+
[ContentTypes.THINK]: 'Calculator returned 57531.',
|
|
996
|
+
},
|
|
997
|
+
{
|
|
998
|
+
type: ContentTypes.TEXT,
|
|
999
|
+
[ContentTypes.TEXT]: '127 * 453 = 57531.',
|
|
1000
|
+
},
|
|
1001
|
+
],
|
|
1002
|
+
},
|
|
1003
|
+
];
|
|
1004
|
+
|
|
1005
|
+
const defaultResult = formatAgentMessages(payload);
|
|
1006
|
+
expect(
|
|
1007
|
+
(defaultResult.messages[0] as AIMessage).additional_kwargs
|
|
1008
|
+
.reasoning_content
|
|
1009
|
+
).toBeUndefined();
|
|
1010
|
+
|
|
1011
|
+
const result = formatAgentMessages(
|
|
1012
|
+
payload,
|
|
1013
|
+
undefined,
|
|
1014
|
+
undefined,
|
|
1015
|
+
undefined,
|
|
1016
|
+
{ provider: Providers.DEEPSEEK }
|
|
1017
|
+
);
|
|
1018
|
+
|
|
1019
|
+
expect(result.messages).toHaveLength(3);
|
|
1020
|
+
expect(result.messages[0]).toBeInstanceOf(AIMessage);
|
|
1021
|
+
expect(result.messages[1]).toBeInstanceOf(ToolMessage);
|
|
1022
|
+
expect(result.messages[2]).toBeInstanceOf(AIMessage);
|
|
1023
|
+
|
|
1024
|
+
const toolCallMessage = result.messages[0] as AIMessage;
|
|
1025
|
+
const finalMessage = result.messages[2] as AIMessage;
|
|
1026
|
+
|
|
1027
|
+
expect(toolCallMessage.content).toBe('Using calculator.');
|
|
1028
|
+
expect(toolCallMessage.tool_calls).toHaveLength(1);
|
|
1029
|
+
expect(toolCallMessage.additional_kwargs.reasoning_content).toBe(
|
|
1030
|
+
'Need calculator.'
|
|
1031
|
+
);
|
|
1032
|
+
expect(finalMessage.content).toBe('127 * 453 = 57531.');
|
|
1033
|
+
expect(finalMessage.additional_kwargs.reasoning_content).toBe(
|
|
1034
|
+
'Calculator returned 57531.'
|
|
1035
|
+
);
|
|
1036
|
+
});
|
|
1037
|
+
|
|
1038
|
+
it('should preserve DeepSeek reasoning from supported hidden content blocks', () => {
|
|
1039
|
+
const payload: TPayload = [
|
|
1040
|
+
{
|
|
1041
|
+
role: 'assistant',
|
|
1042
|
+
content: [
|
|
1043
|
+
{
|
|
1044
|
+
type: ContentTypes.THINK,
|
|
1045
|
+
[ContentTypes.THINK]: 'Think. ',
|
|
1046
|
+
},
|
|
1047
|
+
{
|
|
1048
|
+
type: ContentTypes.THINKING,
|
|
1049
|
+
thinking: 'Thinking. ',
|
|
1050
|
+
},
|
|
1051
|
+
{
|
|
1052
|
+
type: ContentTypes.REASONING,
|
|
1053
|
+
reasoning: 'Reasoning. ',
|
|
1054
|
+
},
|
|
1055
|
+
{
|
|
1056
|
+
type: ContentTypes.REASONING_CONTENT,
|
|
1057
|
+
reasoningText: { text: 'Reasoning content.' },
|
|
1058
|
+
},
|
|
1059
|
+
{
|
|
1060
|
+
type: ContentTypes.TEXT,
|
|
1061
|
+
[ContentTypes.TEXT]: 'Done.',
|
|
1062
|
+
},
|
|
1063
|
+
],
|
|
1064
|
+
},
|
|
1065
|
+
];
|
|
1066
|
+
|
|
1067
|
+
const result = formatAgentMessages(
|
|
1068
|
+
payload,
|
|
1069
|
+
undefined,
|
|
1070
|
+
undefined,
|
|
1071
|
+
undefined,
|
|
1072
|
+
{ provider: Providers.DEEPSEEK }
|
|
1073
|
+
);
|
|
1074
|
+
|
|
1075
|
+
expect(result.messages).toHaveLength(1);
|
|
1076
|
+
expect(result.messages[0]).toBeInstanceOf(AIMessage);
|
|
1077
|
+
expect(result.messages[0].content).toBe('Done.');
|
|
1078
|
+
expect(
|
|
1079
|
+
(result.messages[0] as AIMessage).additional_kwargs.reasoning_content
|
|
1080
|
+
).toBe('Think. Thinking. Reasoning. Reasoning content.');
|
|
1081
|
+
});
|
|
1082
|
+
|
|
1083
|
+
it('should attach later DeepSeek reasoning to an existing tool-call assistant message', () => {
|
|
1084
|
+
const payload: TPayload = [
|
|
1085
|
+
{
|
|
1086
|
+
role: 'assistant',
|
|
1087
|
+
content: [
|
|
1088
|
+
{
|
|
1089
|
+
type: ContentTypes.THINK,
|
|
1090
|
+
[ContentTypes.THINK]: 'Need calculator. ',
|
|
1091
|
+
},
|
|
1092
|
+
{
|
|
1093
|
+
type: ContentTypes.TEXT,
|
|
1094
|
+
[ContentTypes.TEXT]: 'Using calculator.',
|
|
1095
|
+
tool_call_ids: ['call_1'],
|
|
1096
|
+
},
|
|
1097
|
+
{
|
|
1098
|
+
type: ContentTypes.THINK,
|
|
1099
|
+
[ContentTypes.THINK]: 'Preparing tool call.',
|
|
1100
|
+
},
|
|
1101
|
+
{
|
|
1102
|
+
type: ContentTypes.TOOL_CALL,
|
|
1103
|
+
tool_call: {
|
|
1104
|
+
id: 'call_1',
|
|
1105
|
+
name: 'calculator',
|
|
1106
|
+
args: '{"input":"127 * 453"}',
|
|
1107
|
+
output: '57531',
|
|
1108
|
+
},
|
|
1109
|
+
},
|
|
1110
|
+
],
|
|
1111
|
+
},
|
|
1112
|
+
];
|
|
1113
|
+
|
|
1114
|
+
const result = formatAgentMessages(
|
|
1115
|
+
payload,
|
|
1116
|
+
undefined,
|
|
1117
|
+
undefined,
|
|
1118
|
+
undefined,
|
|
1119
|
+
{ provider: Providers.DEEPSEEK }
|
|
1120
|
+
);
|
|
1121
|
+
|
|
1122
|
+
expect(result.messages).toHaveLength(2);
|
|
1123
|
+
expect(result.messages[0]).toBeInstanceOf(AIMessage);
|
|
1124
|
+
expect(result.messages[1]).toBeInstanceOf(ToolMessage);
|
|
1125
|
+
|
|
1126
|
+
const toolCallMessage = result.messages[0] as AIMessage;
|
|
1127
|
+
|
|
1128
|
+
expect(toolCallMessage.content).toBe('Using calculator.');
|
|
1129
|
+
expect(toolCallMessage.tool_calls).toHaveLength(1);
|
|
1130
|
+
expect(toolCallMessage.additional_kwargs.reasoning_content).toBe(
|
|
1131
|
+
'Need calculator. Preparing tool call.'
|
|
1132
|
+
);
|
|
1133
|
+
});
|
|
1134
|
+
|
|
970
1135
|
it('should strip thinking blocks and join TEXT parts as string', () => {
|
|
971
1136
|
const payload = [
|
|
972
1137
|
{
|