@librechat/agents 2.4.30 → 2.4.33
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/common/enum.cjs +1 -0
- package/dist/cjs/common/enum.cjs.map +1 -1
- package/dist/cjs/events.cjs +3 -3
- package/dist/cjs/events.cjs.map +1 -1
- package/dist/cjs/graphs/Graph.cjs +2 -1
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/main.cjs +7 -2
- package/dist/cjs/main.cjs.map +1 -1
- package/dist/cjs/messages/ids.cjs +23 -0
- package/dist/cjs/messages/ids.cjs.map +1 -0
- package/dist/cjs/splitStream.cjs +2 -1
- package/dist/cjs/splitStream.cjs.map +1 -1
- package/dist/cjs/stream.cjs +87 -154
- package/dist/cjs/stream.cjs.map +1 -1
- package/dist/cjs/tools/ToolNode.cjs +14 -3
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/cjs/tools/handlers.cjs +144 -0
- package/dist/cjs/tools/handlers.cjs.map +1 -0
- package/dist/cjs/tools/search/content.cjs +140 -0
- package/dist/cjs/tools/search/content.cjs.map +1 -0
- package/dist/cjs/tools/search/firecrawl.cjs +131 -0
- package/dist/cjs/tools/search/firecrawl.cjs.map +1 -0
- package/dist/cjs/tools/search/format.cjs +203 -0
- package/dist/cjs/tools/search/format.cjs.map +1 -0
- package/dist/cjs/tools/search/highlights.cjs +245 -0
- package/dist/cjs/tools/search/highlights.cjs.map +1 -0
- package/dist/cjs/tools/search/rerankers.cjs +194 -0
- package/dist/cjs/tools/search/rerankers.cjs.map +1 -0
- package/dist/cjs/tools/search/schema.cjs +70 -0
- package/dist/cjs/tools/search/schema.cjs.map +1 -0
- package/dist/cjs/tools/search/search.cjs +491 -0
- package/dist/cjs/tools/search/search.cjs.map +1 -0
- package/dist/cjs/tools/search/tool.cjs +292 -0
- package/dist/cjs/tools/search/tool.cjs.map +1 -0
- package/dist/cjs/tools/search/utils.cjs +66 -0
- package/dist/cjs/tools/search/utils.cjs.map +1 -0
- package/dist/esm/common/enum.mjs +1 -0
- package/dist/esm/common/enum.mjs.map +1 -1
- package/dist/esm/events.mjs +1 -1
- package/dist/esm/events.mjs.map +1 -1
- 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/ids.mjs +21 -0
- package/dist/esm/messages/ids.mjs.map +1 -0
- package/dist/esm/splitStream.mjs +2 -1
- package/dist/esm/splitStream.mjs.map +1 -1
- package/dist/esm/stream.mjs +87 -152
- package/dist/esm/stream.mjs.map +1 -1
- package/dist/esm/tools/ToolNode.mjs +14 -3
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/esm/tools/handlers.mjs +141 -0
- package/dist/esm/tools/handlers.mjs.map +1 -0
- package/dist/esm/tools/search/content.mjs +119 -0
- package/dist/esm/tools/search/content.mjs.map +1 -0
- package/dist/esm/tools/search/firecrawl.mjs +128 -0
- package/dist/esm/tools/search/firecrawl.mjs.map +1 -0
- package/dist/esm/tools/search/format.mjs +201 -0
- package/dist/esm/tools/search/format.mjs.map +1 -0
- package/dist/esm/tools/search/highlights.mjs +243 -0
- package/dist/esm/tools/search/highlights.mjs.map +1 -0
- package/dist/esm/tools/search/rerankers.mjs +188 -0
- package/dist/esm/tools/search/rerankers.mjs.map +1 -0
- package/dist/esm/tools/search/schema.mjs +61 -0
- package/dist/esm/tools/search/schema.mjs.map +1 -0
- package/dist/esm/tools/search/search.mjs +488 -0
- package/dist/esm/tools/search/search.mjs.map +1 -0
- package/dist/esm/tools/search/tool.mjs +290 -0
- package/dist/esm/tools/search/tool.mjs.map +1 -0
- package/dist/esm/tools/search/utils.mjs +61 -0
- package/dist/esm/tools/search/utils.mjs.map +1 -0
- package/dist/types/common/enum.d.ts +1 -0
- package/dist/types/graphs/Graph.d.ts +1 -1
- package/dist/types/index.d.ts +2 -0
- package/dist/types/messages/ids.d.ts +3 -0
- package/dist/types/messages/index.d.ts +1 -0
- package/dist/types/scripts/search.d.ts +1 -0
- package/dist/types/stream.d.ts +0 -8
- package/dist/types/tools/ToolNode.d.ts +6 -0
- package/dist/types/tools/example.d.ts +23 -3
- package/dist/types/tools/handlers.d.ts +8 -0
- package/dist/types/tools/search/content.d.ts +4 -0
- package/dist/types/tools/search/firecrawl.d.ts +38 -0
- package/dist/types/tools/search/format.d.ts +5 -0
- package/dist/types/tools/search/highlights.d.ts +13 -0
- package/dist/types/tools/search/index.d.ts +2 -0
- package/dist/types/tools/search/rerankers.d.ts +36 -0
- package/dist/types/tools/search/schema.d.ts +16 -0
- package/dist/types/tools/search/search.d.ts +9 -0
- package/dist/types/tools/search/test.d.ts +1 -0
- package/dist/types/tools/search/tool.d.ts +33 -0
- package/dist/types/tools/search/types.d.ts +540 -0
- package/dist/types/tools/search/utils.d.ts +10 -0
- package/package.json +10 -7
- package/src/common/enum.ts +1 -0
- package/src/events.ts +49 -15
- package/src/graphs/Graph.ts +6 -2
- package/src/index.ts +2 -0
- package/src/messages/ids.ts +26 -0
- package/src/messages/index.ts +1 -0
- package/src/scripts/search.ts +146 -0
- package/src/splitStream.test.ts +132 -71
- package/src/splitStream.ts +2 -1
- package/src/stream.ts +94 -183
- package/src/tools/ToolNode.ts +37 -14
- package/src/tools/handlers.ts +167 -0
- package/src/tools/search/content.test.ts +173 -0
- package/src/tools/search/content.ts +147 -0
- package/src/tools/search/firecrawl.ts +158 -0
- package/src/tools/search/format.ts +252 -0
- package/src/tools/search/highlights.ts +320 -0
- package/src/tools/search/index.ts +2 -0
- package/src/tools/search/output.md +2775 -0
- package/src/tools/search/rerankers.ts +269 -0
- package/src/tools/search/schema.ts +63 -0
- package/src/tools/search/search.ts +680 -0
- package/src/tools/search/test.html +884 -0
- package/src/tools/search/test.md +643 -0
- package/src/tools/search/test.ts +159 -0
- package/src/tools/search/tool.ts +427 -0
- package/src/tools/search/types.ts +621 -0
- package/src/tools/search/utils.ts +79 -0
- package/src/utils/llmConfig.ts +1 -1
package/src/stream.ts
CHANGED
|
@@ -1,120 +1,71 @@
|
|
|
1
1
|
// src/stream.ts
|
|
2
|
-
import { nanoid } from 'nanoid';
|
|
3
2
|
import type { AIMessageChunk } from '@langchain/core/messages';
|
|
4
|
-
import type { ToolCall
|
|
3
|
+
import type { ToolCall } from '@langchain/core/messages/tool';
|
|
5
4
|
import type { Graph } from '@/graphs';
|
|
6
5
|
import type * as t from '@/types';
|
|
7
6
|
import { StepTypes, ContentTypes, GraphEvents, ToolCallTypes } from '@/common';
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
7
|
+
import { handleToolCalls, handleToolCallChunks } from '@/tools/handlers';
|
|
8
|
+
import { getMessageId } from '@/messages';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Parses content to extract thinking sections enclosed in <think> tags using string operations
|
|
12
|
+
* @param content The content to parse
|
|
13
|
+
* @returns An object with separated text and thinking content
|
|
14
|
+
*/
|
|
15
|
+
function parseThinkingContent(content: string): {
|
|
16
|
+
text: string;
|
|
17
|
+
thinking: string;
|
|
18
|
+
} {
|
|
19
|
+
// If no think tags, return the original content as text
|
|
20
|
+
if (!content.includes('<think>')) {
|
|
21
|
+
return { text: content, thinking: '' };
|
|
14
22
|
}
|
|
15
|
-
return undefined;
|
|
16
|
-
}
|
|
17
23
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
returnExistingId = false
|
|
22
|
-
): string | undefined => {
|
|
23
|
-
const messageId = graph.messageIdsByStepKey.get(stepKey);
|
|
24
|
-
if (messageId != null && messageId) {
|
|
25
|
-
return returnExistingId ? messageId : undefined;
|
|
26
|
-
}
|
|
24
|
+
let textResult = '';
|
|
25
|
+
const thinkingResult: string[] = [];
|
|
26
|
+
let position = 0;
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
graph.prelimMessageIdsByStepKey.delete(stepKey);
|
|
31
|
-
graph.messageIdsByStepKey.set(stepKey, prelimMessageId);
|
|
32
|
-
return prelimMessageId;
|
|
33
|
-
}
|
|
28
|
+
while (position < content.length) {
|
|
29
|
+
const thinkStart = content.indexOf('<think>', position);
|
|
34
30
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
export const handleToolCalls = (
|
|
41
|
-
toolCalls?: ToolCall[],
|
|
42
|
-
metadata?: Record<string, unknown>,
|
|
43
|
-
graph?: Graph
|
|
44
|
-
): void => {
|
|
45
|
-
if (!graph || !metadata) {
|
|
46
|
-
console.warn(`Graph or metadata not found in ${event} event`);
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
31
|
+
if (thinkStart === -1) {
|
|
32
|
+
// No more think tags, add the rest and break
|
|
33
|
+
textResult += content.slice(position);
|
|
34
|
+
break;
|
|
35
|
+
}
|
|
49
36
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
}
|
|
37
|
+
// Add text before the think tag
|
|
38
|
+
textResult += content.slice(position, thinkStart);
|
|
53
39
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
40
|
+
const thinkEnd = content.indexOf('</think>', thinkStart);
|
|
41
|
+
if (thinkEnd === -1) {
|
|
42
|
+
// Malformed input, no closing tag
|
|
43
|
+
textResult += content.slice(thinkStart);
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
57
46
|
|
|
58
|
-
|
|
47
|
+
// Add the thinking content
|
|
48
|
+
const thinkContent = content.slice(thinkStart + 7, thinkEnd);
|
|
49
|
+
thinkingResult.push(thinkContent);
|
|
59
50
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
if (!toolCallId || graph.toolCallStepIds.has(toolCallId)) {
|
|
64
|
-
continue;
|
|
65
|
-
}
|
|
51
|
+
// Move position to after the think tag
|
|
52
|
+
position = thinkEnd + 8; // 8 is the length of '</think>'
|
|
53
|
+
}
|
|
66
54
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
} catch {
|
|
73
|
-
// no previous step
|
|
74
|
-
}
|
|
55
|
+
return {
|
|
56
|
+
text: textResult.trim(),
|
|
57
|
+
thinking: thinkingResult.join('\n').trim(),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
75
60
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
type: 'text',
|
|
81
|
-
text: '',
|
|
82
|
-
tool_call_ids: [toolCallId],
|
|
83
|
-
},
|
|
84
|
-
],
|
|
85
|
-
});
|
|
86
|
-
};
|
|
87
|
-
/* If the previous step exists and is a message creation */
|
|
88
|
-
if (
|
|
89
|
-
prevStepId &&
|
|
90
|
-
prevRunStep &&
|
|
91
|
-
prevRunStep.type === StepTypes.MESSAGE_CREATION
|
|
92
|
-
) {
|
|
93
|
-
dispatchToolCallIds(prevStepId);
|
|
94
|
-
graph.messageStepHasToolCalls.set(prevStepId, true);
|
|
95
|
-
/* If the previous step doesn't exist or is not a message creation */
|
|
96
|
-
} else if (
|
|
97
|
-
!prevRunStep ||
|
|
98
|
-
prevRunStep.type !== StepTypes.MESSAGE_CREATION
|
|
99
|
-
) {
|
|
100
|
-
const messageId = getMessageId(stepKey, graph, true) ?? '';
|
|
101
|
-
const stepId = graph.dispatchRunStep(stepKey, {
|
|
102
|
-
type: StepTypes.MESSAGE_CREATION,
|
|
103
|
-
message_creation: {
|
|
104
|
-
message_id: messageId,
|
|
105
|
-
},
|
|
106
|
-
});
|
|
107
|
-
dispatchToolCallIds(stepId);
|
|
108
|
-
graph.messageStepHasToolCalls.set(prevStepId, true);
|
|
61
|
+
function getNonEmptyValue(possibleValues: string[]): string | undefined {
|
|
62
|
+
for (const value of possibleValues) {
|
|
63
|
+
if (value && value.trim() !== '') {
|
|
64
|
+
return value;
|
|
109
65
|
}
|
|
110
|
-
|
|
111
|
-
graph.dispatchRunStep(stepKey, {
|
|
112
|
-
type: StepTypes.TOOL_CALLS,
|
|
113
|
-
tool_calls: [tool_call],
|
|
114
|
-
});
|
|
115
66
|
}
|
|
116
|
-
|
|
117
|
-
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
118
69
|
export class ChatModelStreamHandler implements t.EventHandler {
|
|
119
70
|
handle(
|
|
120
71
|
event: string,
|
|
@@ -179,7 +130,7 @@ export class ChatModelStreamHandler implements t.EventHandler {
|
|
|
179
130
|
chunk.tool_call_chunks.length &&
|
|
180
131
|
typeof chunk.tool_call_chunks[0]?.index === 'number'
|
|
181
132
|
) {
|
|
182
|
-
|
|
133
|
+
handleToolCallChunks({
|
|
183
134
|
graph,
|
|
184
135
|
stepKey,
|
|
185
136
|
toolCallChunks: chunk.tool_call_chunks,
|
|
@@ -239,6 +190,40 @@ hasToolCallChunks: ${hasToolCallChunks}
|
|
|
239
190
|
},
|
|
240
191
|
],
|
|
241
192
|
});
|
|
193
|
+
} else if (graph.currentTokenType === 'think_and_text') {
|
|
194
|
+
const { text, thinking } = parseThinkingContent(content);
|
|
195
|
+
if (thinking) {
|
|
196
|
+
graph.dispatchReasoningDelta(stepId, {
|
|
197
|
+
content: [
|
|
198
|
+
{
|
|
199
|
+
type: ContentTypes.THINK,
|
|
200
|
+
think: thinking,
|
|
201
|
+
},
|
|
202
|
+
],
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
if (text) {
|
|
206
|
+
graph.currentTokenType = ContentTypes.TEXT;
|
|
207
|
+
graph.tokenTypeSwitch = 'content';
|
|
208
|
+
const newStepKey = graph.getStepKey(metadata);
|
|
209
|
+
const message_id = getMessageId(newStepKey, graph) ?? '';
|
|
210
|
+
graph.dispatchRunStep(newStepKey, {
|
|
211
|
+
type: StepTypes.MESSAGE_CREATION,
|
|
212
|
+
message_creation: {
|
|
213
|
+
message_id,
|
|
214
|
+
},
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
const newStepId = graph.getStepIdByKey(newStepKey);
|
|
218
|
+
graph.dispatchMessageDelta(newStepId, {
|
|
219
|
+
content: [
|
|
220
|
+
{
|
|
221
|
+
type: ContentTypes.TEXT,
|
|
222
|
+
text: text,
|
|
223
|
+
},
|
|
224
|
+
],
|
|
225
|
+
});
|
|
226
|
+
}
|
|
242
227
|
} else {
|
|
243
228
|
graph.dispatchReasoningDelta(stepId, {
|
|
244
229
|
content: [
|
|
@@ -273,88 +258,6 @@ hasToolCallChunks: ${hasToolCallChunks}
|
|
|
273
258
|
});
|
|
274
259
|
}
|
|
275
260
|
}
|
|
276
|
-
handleToolCallChunks = ({
|
|
277
|
-
graph,
|
|
278
|
-
stepKey,
|
|
279
|
-
toolCallChunks,
|
|
280
|
-
}: {
|
|
281
|
-
graph: Graph;
|
|
282
|
-
stepKey: string;
|
|
283
|
-
toolCallChunks: ToolCallChunk[];
|
|
284
|
-
}): void => {
|
|
285
|
-
let prevStepId: string;
|
|
286
|
-
let prevRunStep: t.RunStep | undefined;
|
|
287
|
-
try {
|
|
288
|
-
prevStepId = graph.getStepIdByKey(stepKey, graph.contentData.length - 1);
|
|
289
|
-
prevRunStep = graph.getRunStep(prevStepId);
|
|
290
|
-
} catch {
|
|
291
|
-
/** Edge Case: If no previous step exists, create a new message creation step */
|
|
292
|
-
const message_id = getMessageId(stepKey, graph, true) ?? '';
|
|
293
|
-
prevStepId = graph.dispatchRunStep(stepKey, {
|
|
294
|
-
type: StepTypes.MESSAGE_CREATION,
|
|
295
|
-
message_creation: {
|
|
296
|
-
message_id,
|
|
297
|
-
},
|
|
298
|
-
});
|
|
299
|
-
prevRunStep = graph.getRunStep(prevStepId);
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
const _stepId = graph.getStepIdByKey(stepKey, prevRunStep?.index);
|
|
303
|
-
|
|
304
|
-
/** Edge Case: Tool Call Run Step or `tool_call_ids` never dispatched */
|
|
305
|
-
const tool_calls: ToolCall[] | undefined =
|
|
306
|
-
prevStepId &&
|
|
307
|
-
prevRunStep &&
|
|
308
|
-
prevRunStep.type === StepTypes.MESSAGE_CREATION
|
|
309
|
-
? []
|
|
310
|
-
: undefined;
|
|
311
|
-
|
|
312
|
-
/** Edge Case: `id` and `name` fields cannot be empty strings */
|
|
313
|
-
for (const toolCallChunk of toolCallChunks) {
|
|
314
|
-
if (toolCallChunk.name === '') {
|
|
315
|
-
toolCallChunk.name = undefined;
|
|
316
|
-
}
|
|
317
|
-
if (toolCallChunk.id === '') {
|
|
318
|
-
toolCallChunk.id = undefined;
|
|
319
|
-
} else if (
|
|
320
|
-
tool_calls != null &&
|
|
321
|
-
toolCallChunk.id != null &&
|
|
322
|
-
toolCallChunk.name != null
|
|
323
|
-
) {
|
|
324
|
-
tool_calls.push({
|
|
325
|
-
args: {},
|
|
326
|
-
id: toolCallChunk.id,
|
|
327
|
-
name: toolCallChunk.name,
|
|
328
|
-
type: ToolCallTypes.TOOL_CALL,
|
|
329
|
-
});
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
let stepId: string = _stepId;
|
|
334
|
-
const alreadyDispatched =
|
|
335
|
-
prevRunStep?.type === StepTypes.MESSAGE_CREATION &&
|
|
336
|
-
graph.messageStepHasToolCalls.has(prevStepId);
|
|
337
|
-
if (!alreadyDispatched && tool_calls?.length === toolCallChunks.length) {
|
|
338
|
-
graph.dispatchMessageDelta(prevStepId, {
|
|
339
|
-
content: [
|
|
340
|
-
{
|
|
341
|
-
type: ContentTypes.TEXT,
|
|
342
|
-
text: '',
|
|
343
|
-
tool_call_ids: tool_calls.map((tc) => tc.id ?? ''),
|
|
344
|
-
},
|
|
345
|
-
],
|
|
346
|
-
});
|
|
347
|
-
graph.messageStepHasToolCalls.set(prevStepId, true);
|
|
348
|
-
stepId = graph.dispatchRunStep(stepKey, {
|
|
349
|
-
type: StepTypes.TOOL_CALLS,
|
|
350
|
-
tool_calls,
|
|
351
|
-
});
|
|
352
|
-
}
|
|
353
|
-
graph.dispatchRunStepDelta(stepId, {
|
|
354
|
-
type: StepTypes.TOOL_CALLS,
|
|
355
|
-
tool_calls: toolCallChunks,
|
|
356
|
-
});
|
|
357
|
-
};
|
|
358
261
|
handleReasoning(chunk: Partial<AIMessageChunk>, graph: Graph): void {
|
|
359
262
|
let reasoning_content = chunk.additional_kwargs?.[graph.reasoningKey] as
|
|
360
263
|
| string
|
|
@@ -384,6 +287,14 @@ hasToolCallChunks: ${hasToolCallChunks}
|
|
|
384
287
|
) {
|
|
385
288
|
graph.currentTokenType = ContentTypes.TEXT;
|
|
386
289
|
graph.tokenTypeSwitch = 'content';
|
|
290
|
+
} else if (
|
|
291
|
+
chunk.content != null &&
|
|
292
|
+
typeof chunk.content === 'string' &&
|
|
293
|
+
chunk.content.includes('<think>') &&
|
|
294
|
+
chunk.content.includes('</think>')
|
|
295
|
+
) {
|
|
296
|
+
graph.currentTokenType = 'think_and_text';
|
|
297
|
+
graph.tokenTypeSwitch = 'content';
|
|
387
298
|
} else if (
|
|
388
299
|
chunk.content != null &&
|
|
389
300
|
typeof chunk.content === 'string' &&
|
package/src/tools/ToolNode.ts
CHANGED
|
@@ -1,10 +1,18 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
END,
|
|
3
|
+
MessagesAnnotation,
|
|
4
|
+
isCommand,
|
|
5
|
+
isGraphInterrupt,
|
|
6
|
+
} from '@langchain/langgraph';
|
|
2
7
|
import { ToolMessage, isBaseMessage } from '@langchain/core/messages';
|
|
3
|
-
import type {
|
|
8
|
+
import type {
|
|
9
|
+
RunnableConfig,
|
|
10
|
+
RunnableToolLike,
|
|
11
|
+
} from '@langchain/core/runnables';
|
|
4
12
|
import type { BaseMessage, AIMessage } from '@langchain/core/messages';
|
|
5
13
|
import type { StructuredToolInterface } from '@langchain/core/tools';
|
|
6
14
|
import type * as t from '@/types';
|
|
7
|
-
import{ RunnableCallable } from '@/utils';
|
|
15
|
+
import { RunnableCallable } from '@/utils';
|
|
8
16
|
import { GraphNodeKeys } from '@/common';
|
|
9
17
|
|
|
10
18
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -15,6 +23,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
15
23
|
handleToolErrors = true;
|
|
16
24
|
toolCallStepIds?: Map<string, string>;
|
|
17
25
|
errorHandler?: t.ToolNodeConstructorParams['errorHandler'];
|
|
26
|
+
private toolUsageCount: Map<string, number>;
|
|
18
27
|
|
|
19
28
|
constructor({
|
|
20
29
|
tools,
|
|
@@ -28,11 +37,20 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
28
37
|
}: t.ToolNodeConstructorParams) {
|
|
29
38
|
super({ name, tags, func: (input, config) => this.run(input, config) });
|
|
30
39
|
this.tools = tools;
|
|
31
|
-
this.toolMap = toolMap ?? new Map(tools.map(tool => [tool.name, tool]));
|
|
40
|
+
this.toolMap = toolMap ?? new Map(tools.map((tool) => [tool.name, tool]));
|
|
32
41
|
this.toolCallStepIds = toolCallStepIds;
|
|
33
42
|
this.handleToolErrors = handleToolErrors ?? this.handleToolErrors;
|
|
34
43
|
this.loadRuntimeTools = loadRuntimeTools;
|
|
35
44
|
this.errorHandler = errorHandler;
|
|
45
|
+
this.toolUsageCount = new Map<string, number>();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Returns a snapshot of the current tool usage counts.
|
|
50
|
+
* @returns A ReadonlyMap where keys are tool names and values are their usage counts.
|
|
51
|
+
*/
|
|
52
|
+
public getToolUsageCounts(): ReadonlyMap<string, number> {
|
|
53
|
+
return new Map(this.toolUsageCount); // Return a copy
|
|
36
54
|
}
|
|
37
55
|
|
|
38
56
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -50,7 +68,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
50
68
|
(message as AIMessage).tool_calls ?? []
|
|
51
69
|
);
|
|
52
70
|
this.tools = tools;
|
|
53
|
-
this.toolMap = toolMap ?? new Map(tools.map(tool => [tool.name, tool]));
|
|
71
|
+
this.toolMap = toolMap ?? new Map(tools.map((tool) => [tool.name, tool]));
|
|
54
72
|
}
|
|
55
73
|
const outputs = await Promise.all(
|
|
56
74
|
(message as AIMessage).tool_calls?.map(async (call) => {
|
|
@@ -59,11 +77,13 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
59
77
|
if (tool === undefined) {
|
|
60
78
|
throw new Error(`Tool "${call.name}" not found.`);
|
|
61
79
|
}
|
|
80
|
+
const turn = this.toolUsageCount.get(call.name) ?? 0;
|
|
81
|
+
this.toolUsageCount.set(call.name, turn + 1);
|
|
62
82
|
const args = call.args;
|
|
63
83
|
const stepId = this.toolCallStepIds?.get(call.id!);
|
|
64
84
|
const output = await tool.invoke(
|
|
65
|
-
{ ...call, args, type: 'tool_call', stepId },
|
|
66
|
-
config
|
|
85
|
+
{ ...call, args, type: 'tool_call', stepId, turn },
|
|
86
|
+
config
|
|
67
87
|
);
|
|
68
88
|
if (
|
|
69
89
|
(isBaseMessage(output) && output._getType() === 'tool') ||
|
|
@@ -86,12 +106,15 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
86
106
|
if (isGraphInterrupt(e)) {
|
|
87
107
|
throw e;
|
|
88
108
|
}
|
|
89
|
-
this.errorHandler?.(
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
109
|
+
this.errorHandler?.(
|
|
110
|
+
{
|
|
111
|
+
error: e,
|
|
112
|
+
id: call.id!,
|
|
113
|
+
name: call.name,
|
|
114
|
+
input: call.args,
|
|
115
|
+
},
|
|
116
|
+
config.metadata
|
|
117
|
+
);
|
|
95
118
|
return new ToolMessage({
|
|
96
119
|
content: `Error: ${e.message}\n Please fix your mistakes.`,
|
|
97
120
|
name: call.name,
|
|
@@ -130,4 +153,4 @@ export function toolsCondition(
|
|
|
130
153
|
} else {
|
|
131
154
|
return END;
|
|
132
155
|
}
|
|
133
|
-
}
|
|
156
|
+
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
// src/tools/handlers.ts
|
|
3
|
+
import { nanoid } from 'nanoid';
|
|
4
|
+
import type { ToolCall, ToolCallChunk } from '@langchain/core/messages/tool';
|
|
5
|
+
import type { Graph } from '@/graphs';
|
|
6
|
+
import type * as t from '@/types';
|
|
7
|
+
import { StepTypes, ContentTypes, ToolCallTypes } from '@/common';
|
|
8
|
+
import { getMessageId } from '@/messages';
|
|
9
|
+
|
|
10
|
+
export function handleToolCallChunks({
|
|
11
|
+
graph,
|
|
12
|
+
stepKey,
|
|
13
|
+
toolCallChunks,
|
|
14
|
+
}: {
|
|
15
|
+
graph: Graph;
|
|
16
|
+
stepKey: string;
|
|
17
|
+
toolCallChunks: ToolCallChunk[];
|
|
18
|
+
}): void {
|
|
19
|
+
let prevStepId: string;
|
|
20
|
+
let prevRunStep: t.RunStep | undefined;
|
|
21
|
+
try {
|
|
22
|
+
prevStepId = graph.getStepIdByKey(stepKey, graph.contentData.length - 1);
|
|
23
|
+
prevRunStep = graph.getRunStep(prevStepId);
|
|
24
|
+
} catch {
|
|
25
|
+
/** Edge Case: If no previous step exists, create a new message creation step */
|
|
26
|
+
const message_id = getMessageId(stepKey, graph, true) ?? '';
|
|
27
|
+
prevStepId = graph.dispatchRunStep(stepKey, {
|
|
28
|
+
type: StepTypes.MESSAGE_CREATION,
|
|
29
|
+
message_creation: {
|
|
30
|
+
message_id,
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
prevRunStep = graph.getRunStep(prevStepId);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const _stepId = graph.getStepIdByKey(stepKey, prevRunStep?.index);
|
|
37
|
+
|
|
38
|
+
/** Edge Case: Tool Call Run Step or `tool_call_ids` never dispatched */
|
|
39
|
+
const tool_calls: ToolCall[] | undefined =
|
|
40
|
+
prevStepId && prevRunStep && prevRunStep.type === StepTypes.MESSAGE_CREATION
|
|
41
|
+
? []
|
|
42
|
+
: undefined;
|
|
43
|
+
|
|
44
|
+
/** Edge Case: `id` and `name` fields cannot be empty strings */
|
|
45
|
+
for (const toolCallChunk of toolCallChunks) {
|
|
46
|
+
if (toolCallChunk.name === '') {
|
|
47
|
+
toolCallChunk.name = undefined;
|
|
48
|
+
}
|
|
49
|
+
if (toolCallChunk.id === '') {
|
|
50
|
+
toolCallChunk.id = undefined;
|
|
51
|
+
} else if (
|
|
52
|
+
tool_calls != null &&
|
|
53
|
+
toolCallChunk.id != null &&
|
|
54
|
+
toolCallChunk.name != null
|
|
55
|
+
) {
|
|
56
|
+
tool_calls.push({
|
|
57
|
+
args: {},
|
|
58
|
+
id: toolCallChunk.id,
|
|
59
|
+
name: toolCallChunk.name,
|
|
60
|
+
type: ToolCallTypes.TOOL_CALL,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
let stepId: string = _stepId;
|
|
66
|
+
const alreadyDispatched =
|
|
67
|
+
prevRunStep?.type === StepTypes.MESSAGE_CREATION &&
|
|
68
|
+
graph.messageStepHasToolCalls.has(prevStepId);
|
|
69
|
+
if (!alreadyDispatched && tool_calls?.length === toolCallChunks.length) {
|
|
70
|
+
graph.dispatchMessageDelta(prevStepId, {
|
|
71
|
+
content: [
|
|
72
|
+
{
|
|
73
|
+
type: ContentTypes.TEXT,
|
|
74
|
+
text: '',
|
|
75
|
+
tool_call_ids: tool_calls.map((tc) => tc.id ?? ''),
|
|
76
|
+
},
|
|
77
|
+
],
|
|
78
|
+
});
|
|
79
|
+
graph.messageStepHasToolCalls.set(prevStepId, true);
|
|
80
|
+
stepId = graph.dispatchRunStep(stepKey, {
|
|
81
|
+
type: StepTypes.TOOL_CALLS,
|
|
82
|
+
tool_calls,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
graph.dispatchRunStepDelta(stepId, {
|
|
86
|
+
type: StepTypes.TOOL_CALLS,
|
|
87
|
+
tool_calls: toolCallChunks,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export const handleToolCalls = (
|
|
92
|
+
toolCalls?: ToolCall[],
|
|
93
|
+
metadata?: Record<string, unknown>,
|
|
94
|
+
graph?: Graph
|
|
95
|
+
): void => {
|
|
96
|
+
if (!graph || !metadata) {
|
|
97
|
+
console.warn(`Graph or metadata not found in ${event} event`);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (!toolCalls) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (toolCalls.length === 0) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const stepKey = graph.getStepKey(metadata);
|
|
110
|
+
|
|
111
|
+
for (const tool_call of toolCalls) {
|
|
112
|
+
const toolCallId = tool_call.id ?? `toolu_${nanoid()}`;
|
|
113
|
+
tool_call.id = toolCallId;
|
|
114
|
+
if (!toolCallId || graph.toolCallStepIds.has(toolCallId)) {
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
let prevStepId = '';
|
|
119
|
+
let prevRunStep: t.RunStep | undefined;
|
|
120
|
+
try {
|
|
121
|
+
prevStepId = graph.getStepIdByKey(stepKey, graph.contentData.length - 1);
|
|
122
|
+
prevRunStep = graph.getRunStep(prevStepId);
|
|
123
|
+
} catch {
|
|
124
|
+
// no previous step
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const dispatchToolCallIds = (lastMessageStepId: string): void => {
|
|
128
|
+
graph.dispatchMessageDelta(lastMessageStepId, {
|
|
129
|
+
content: [
|
|
130
|
+
{
|
|
131
|
+
type: 'text',
|
|
132
|
+
text: '',
|
|
133
|
+
tool_call_ids: [toolCallId],
|
|
134
|
+
},
|
|
135
|
+
],
|
|
136
|
+
});
|
|
137
|
+
};
|
|
138
|
+
/* If the previous step exists and is a message creation */
|
|
139
|
+
if (
|
|
140
|
+
prevStepId &&
|
|
141
|
+
prevRunStep &&
|
|
142
|
+
prevRunStep.type === StepTypes.MESSAGE_CREATION
|
|
143
|
+
) {
|
|
144
|
+
dispatchToolCallIds(prevStepId);
|
|
145
|
+
graph.messageStepHasToolCalls.set(prevStepId, true);
|
|
146
|
+
/* If the previous step doesn't exist or is not a message creation */
|
|
147
|
+
} else if (
|
|
148
|
+
!prevRunStep ||
|
|
149
|
+
prevRunStep.type !== StepTypes.MESSAGE_CREATION
|
|
150
|
+
) {
|
|
151
|
+
const messageId = getMessageId(stepKey, graph, true) ?? '';
|
|
152
|
+
const stepId = graph.dispatchRunStep(stepKey, {
|
|
153
|
+
type: StepTypes.MESSAGE_CREATION,
|
|
154
|
+
message_creation: {
|
|
155
|
+
message_id: messageId,
|
|
156
|
+
},
|
|
157
|
+
});
|
|
158
|
+
dispatchToolCallIds(stepId);
|
|
159
|
+
graph.messageStepHasToolCalls.set(prevStepId, true);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
graph.dispatchRunStep(stepKey, {
|
|
163
|
+
type: StepTypes.TOOL_CALLS,
|
|
164
|
+
tool_calls: [tool_call],
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
};
|