illuma-agents 1.0.7 → 1.0.9
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/LICENSE +1 -5
- package/dist/cjs/common/enum.cjs +1 -2
- package/dist/cjs/common/enum.cjs.map +1 -1
- package/dist/cjs/instrumentation.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/types.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/utils/message_inputs.cjs +79 -2
- package/dist/cjs/llm/anthropic/utils/message_inputs.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/utils/tools.cjs.map +1 -1
- package/dist/cjs/llm/bedrock/index.cjs +99 -0
- package/dist/cjs/llm/bedrock/index.cjs.map +1 -0
- package/dist/cjs/llm/fake.cjs.map +1 -1
- package/dist/cjs/llm/google/index.cjs +78 -9
- package/dist/cjs/llm/google/index.cjs.map +1 -1
- package/dist/cjs/llm/google/utils/common.cjs +185 -28
- package/dist/cjs/llm/google/utils/common.cjs.map +1 -1
- package/dist/cjs/llm/providers.cjs +13 -16
- package/dist/cjs/llm/providers.cjs.map +1 -1
- package/dist/cjs/llm/text.cjs.map +1 -1
- package/dist/cjs/messages/core.cjs +14 -14
- package/dist/cjs/messages/core.cjs.map +1 -1
- package/dist/cjs/messages/ids.cjs.map +1 -1
- package/dist/cjs/messages/prune.cjs.map +1 -1
- package/dist/cjs/run.cjs +10 -1
- package/dist/cjs/run.cjs.map +1 -1
- package/dist/cjs/splitStream.cjs.map +1 -1
- package/dist/cjs/stream.cjs +4 -1
- package/dist/cjs/stream.cjs.map +1 -1
- package/dist/cjs/tools/ToolNode.cjs +163 -55
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/cjs/tools/handlers.cjs +29 -25
- package/dist/cjs/tools/handlers.cjs.map +1 -1
- package/dist/cjs/tools/search/anthropic.cjs.map +1 -1
- package/dist/cjs/tools/search/content.cjs.map +1 -1
- package/dist/cjs/tools/search/firecrawl.cjs.map +1 -1
- package/dist/cjs/tools/search/format.cjs.map +1 -1
- package/dist/cjs/tools/search/highlights.cjs.map +1 -1
- package/dist/cjs/tools/search/rerankers.cjs.map +1 -1
- package/dist/cjs/tools/search/schema.cjs +25 -25
- package/dist/cjs/tools/search/schema.cjs.map +1 -1
- package/dist/cjs/tools/search/search.cjs +6 -1
- package/dist/cjs/tools/search/search.cjs.map +1 -1
- package/dist/cjs/tools/search/serper-scraper.cjs.map +1 -1
- package/dist/cjs/tools/search/tool.cjs +162 -35
- package/dist/cjs/tools/search/tool.cjs.map +1 -1
- package/dist/cjs/tools/search/utils.cjs.map +1 -1
- package/dist/cjs/utils/graph.cjs.map +1 -1
- package/dist/cjs/utils/llm.cjs +0 -1
- package/dist/cjs/utils/llm.cjs.map +1 -1
- package/dist/cjs/utils/misc.cjs.map +1 -1
- package/dist/cjs/utils/run.cjs.map +1 -1
- package/dist/cjs/utils/title.cjs +7 -7
- package/dist/cjs/utils/title.cjs.map +1 -1
- package/dist/esm/common/enum.mjs +1 -2
- package/dist/esm/common/enum.mjs.map +1 -1
- package/dist/esm/instrumentation.mjs.map +1 -1
- package/dist/esm/llm/anthropic/types.mjs.map +1 -1
- package/dist/esm/llm/anthropic/utils/message_inputs.mjs +79 -2
- package/dist/esm/llm/anthropic/utils/message_inputs.mjs.map +1 -1
- package/dist/esm/llm/anthropic/utils/tools.mjs.map +1 -1
- package/dist/esm/llm/bedrock/index.mjs +97 -0
- package/dist/esm/llm/bedrock/index.mjs.map +1 -0
- package/dist/esm/llm/fake.mjs.map +1 -1
- package/dist/esm/llm/google/index.mjs +79 -10
- package/dist/esm/llm/google/index.mjs.map +1 -1
- package/dist/esm/llm/google/utils/common.mjs +184 -30
- package/dist/esm/llm/google/utils/common.mjs.map +1 -1
- package/dist/esm/llm/providers.mjs +2 -5
- package/dist/esm/llm/providers.mjs.map +1 -1
- package/dist/esm/llm/text.mjs.map +1 -1
- package/dist/esm/messages/core.mjs +14 -14
- package/dist/esm/messages/core.mjs.map +1 -1
- package/dist/esm/messages/ids.mjs.map +1 -1
- package/dist/esm/messages/prune.mjs.map +1 -1
- package/dist/esm/run.mjs +10 -1
- package/dist/esm/run.mjs.map +1 -1
- package/dist/esm/splitStream.mjs.map +1 -1
- package/dist/esm/stream.mjs +4 -1
- package/dist/esm/stream.mjs.map +1 -1
- package/dist/esm/tools/ToolNode.mjs +164 -56
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/esm/tools/handlers.mjs +30 -26
- package/dist/esm/tools/handlers.mjs.map +1 -1
- package/dist/esm/tools/search/anthropic.mjs.map +1 -1
- package/dist/esm/tools/search/content.mjs.map +1 -1
- package/dist/esm/tools/search/firecrawl.mjs.map +1 -1
- package/dist/esm/tools/search/format.mjs.map +1 -1
- package/dist/esm/tools/search/highlights.mjs.map +1 -1
- package/dist/esm/tools/search/rerankers.mjs.map +1 -1
- package/dist/esm/tools/search/schema.mjs +25 -25
- package/dist/esm/tools/search/schema.mjs.map +1 -1
- package/dist/esm/tools/search/search.mjs +6 -1
- package/dist/esm/tools/search/search.mjs.map +1 -1
- package/dist/esm/tools/search/serper-scraper.mjs.map +1 -1
- package/dist/esm/tools/search/tool.mjs +162 -35
- package/dist/esm/tools/search/tool.mjs.map +1 -1
- package/dist/esm/tools/search/utils.mjs.map +1 -1
- package/dist/esm/utils/graph.mjs.map +1 -1
- package/dist/esm/utils/llm.mjs +0 -1
- package/dist/esm/utils/llm.mjs.map +1 -1
- package/dist/esm/utils/misc.mjs.map +1 -1
- package/dist/esm/utils/run.mjs.map +1 -1
- package/dist/esm/utils/title.mjs +7 -7
- package/dist/esm/utils/title.mjs.map +1 -1
- package/dist/types/common/enum.d.ts +1 -2
- package/dist/types/llm/bedrock/index.d.ts +36 -0
- package/dist/types/llm/google/index.d.ts +10 -0
- package/dist/types/llm/google/types.d.ts +11 -1
- package/dist/types/llm/google/utils/common.d.ts +17 -2
- package/dist/types/tools/ToolNode.d.ts +9 -1
- package/dist/types/tools/search/types.d.ts +2 -0
- package/dist/types/types/llm.d.ts +3 -8
- package/dist/types/types/tools.d.ts +1 -1
- package/package.json +15 -11
- package/src/common/enum.ts +1 -2
- package/src/common/index.ts +1 -1
- package/src/instrumentation.ts +22 -22
- package/src/llm/anthropic/llm.spec.ts +1442 -1442
- package/src/llm/anthropic/types.ts +140 -140
- package/src/llm/anthropic/utils/message_inputs.ts +757 -660
- package/src/llm/anthropic/utils/output_parsers.ts +133 -133
- package/src/llm/anthropic/utils/tools.ts +29 -29
- package/src/llm/bedrock/index.ts +128 -0
- package/src/llm/fake.ts +133 -133
- package/src/llm/google/data/gettysburg10.wav +0 -0
- package/src/llm/google/data/hotdog.jpg +0 -0
- package/src/llm/google/index.ts +129 -14
- package/src/llm/google/llm.spec.ts +932 -0
- package/src/llm/google/types.ts +56 -43
- package/src/llm/google/utils/common.ts +873 -660
- package/src/llm/google/utils/tools.ts +160 -160
- package/src/llm/openai/types.ts +24 -24
- package/src/llm/openai/utils/isReasoningModel.test.ts +90 -90
- package/src/llm/providers.ts +2 -7
- package/src/llm/text.ts +94 -94
- package/src/messages/core.ts +463 -463
- package/src/messages/formatAgentMessages.tools.test.ts +400 -400
- package/src/messages/formatMessage.test.ts +693 -693
- package/src/messages/ids.ts +26 -26
- package/src/messages/prune.ts +567 -567
- package/src/messages/shiftIndexTokenCountMap.test.ts +81 -81
- package/src/mockStream.ts +98 -98
- package/src/prompts/collab.ts +5 -5
- package/src/prompts/index.ts +1 -1
- package/src/prompts/taskmanager.ts +61 -61
- package/src/run.ts +13 -4
- package/src/scripts/ant_web_search_edge_case.ts +162 -0
- package/src/scripts/ant_web_search_error_edge_case.ts +148 -0
- package/src/scripts/args.ts +48 -48
- package/src/scripts/caching.ts +123 -123
- package/src/scripts/code_exec_files.ts +193 -193
- package/src/scripts/empty_input.ts +137 -137
- package/src/scripts/image.ts +178 -178
- package/src/scripts/memory.ts +97 -97
- package/src/scripts/thinking.ts +149 -149
- package/src/specs/anthropic.simple.test.ts +67 -0
- package/src/specs/spec.utils.ts +3 -3
- package/src/specs/token-distribution-edge-case.test.ts +316 -316
- package/src/specs/tool-error.test.ts +193 -193
- package/src/splitStream.test.ts +691 -691
- package/src/splitStream.ts +234 -234
- package/src/stream.test.ts +94 -94
- package/src/stream.ts +4 -1
- package/src/tools/ToolNode.ts +206 -64
- package/src/tools/handlers.ts +32 -28
- package/src/tools/search/anthropic.ts +51 -51
- package/src/tools/search/content.test.ts +173 -173
- package/src/tools/search/content.ts +147 -147
- package/src/tools/search/direct-url.test.ts +530 -0
- package/src/tools/search/firecrawl.ts +210 -210
- package/src/tools/search/format.ts +250 -250
- package/src/tools/search/highlights.ts +320 -320
- package/src/tools/search/index.ts +2 -2
- package/src/tools/search/jina-reranker.test.ts +126 -126
- package/src/tools/search/output.md +2775 -2775
- package/src/tools/search/rerankers.ts +242 -242
- package/src/tools/search/schema.ts +63 -63
- package/src/tools/search/search.ts +766 -759
- package/src/tools/search/serper-scraper.ts +155 -155
- package/src/tools/search/test.html +883 -883
- package/src/tools/search/test.md +642 -642
- package/src/tools/search/test.ts +159 -159
- package/src/tools/search/tool.ts +619 -471
- package/src/tools/search/types.ts +689 -687
- package/src/tools/search/utils.ts +79 -79
- package/src/types/index.ts +6 -6
- package/src/types/llm.ts +2 -8
- package/src/types/tools.ts +80 -80
- package/src/utils/graph.ts +10 -10
- package/src/utils/llm.ts +26 -27
- package/src/utils/llmConfig.ts +5 -3
- package/src/utils/logging.ts +48 -48
- package/src/utils/misc.ts +57 -57
- package/src/utils/run.ts +100 -100
- package/src/utils/title.ts +165 -165
- package/dist/cjs/llm/ollama/index.cjs +0 -70
- package/dist/cjs/llm/ollama/index.cjs.map +0 -1
- package/dist/cjs/llm/ollama/utils.cjs +0 -158
- package/dist/cjs/llm/ollama/utils.cjs.map +0 -1
- package/dist/esm/llm/ollama/index.mjs +0 -68
- package/dist/esm/llm/ollama/index.mjs.map +0 -1
- package/dist/esm/llm/ollama/utils.mjs +0 -155
- package/dist/esm/llm/ollama/utils.mjs.map +0 -1
- package/dist/types/llm/ollama/index.d.ts +0 -8
- package/dist/types/llm/ollama/utils.d.ts +0 -7
- package/src/llm/ollama/index.ts +0 -92
- package/src/llm/ollama/utils.ts +0 -193
- package/src/proto/CollabGraph.ts +0 -269
- package/src/proto/TaskManager.ts +0 -243
- package/src/proto/collab.ts +0 -200
- package/src/proto/collab_design.ts +0 -184
- package/src/proto/collab_design_v2.ts +0 -224
- package/src/proto/collab_design_v3.ts +0 -255
- package/src/proto/collab_design_v4.ts +0 -220
- package/src/proto/collab_design_v5.ts +0 -251
- package/src/proto/collab_graph.ts +0 -181
- package/src/proto/collab_original.ts +0 -123
- package/src/proto/example.ts +0 -93
- package/src/proto/example_new.ts +0 -68
- package/src/proto/example_old.ts +0 -201
- package/src/proto/example_test.ts +0 -152
- package/src/proto/example_test_anthropic.ts +0 -100
- package/src/proto/log_stream.ts +0 -202
- package/src/proto/main_collab_community_event.ts +0 -133
- package/src/proto/main_collab_design_v2.ts +0 -96
- package/src/proto/main_collab_design_v4.ts +0 -100
- package/src/proto/main_collab_design_v5.ts +0 -135
- package/src/proto/main_collab_global_analysis.ts +0 -122
- package/src/proto/main_collab_hackathon_event.ts +0 -153
- package/src/proto/main_collab_space_mission.ts +0 -153
- package/src/proto/main_philosophy.ts +0 -210
- package/src/proto/original_script.ts +0 -126
- package/src/proto/standard.ts +0 -100
- package/src/proto/stream.ts +0 -56
- package/src/proto/tasks.ts +0 -118
- package/src/proto/tools/global_analysis_tools.ts +0 -86
- package/src/proto/tools/space_mission_tools.ts +0 -60
- package/src/proto/vertexai.ts +0 -54
package/src/tools/ToolNode.ts
CHANGED
|
@@ -1,10 +1,17 @@
|
|
|
1
|
+
import { ToolCall } from '@langchain/core/messages/tool';
|
|
2
|
+
import {
|
|
3
|
+
ToolMessage,
|
|
4
|
+
isAIMessage,
|
|
5
|
+
isBaseMessage,
|
|
6
|
+
} from '@langchain/core/messages';
|
|
1
7
|
import {
|
|
2
8
|
END,
|
|
3
|
-
|
|
9
|
+
Send,
|
|
10
|
+
Command,
|
|
4
11
|
isCommand,
|
|
5
12
|
isGraphInterrupt,
|
|
13
|
+
MessagesAnnotation,
|
|
6
14
|
} from '@langchain/langgraph';
|
|
7
|
-
import { ToolMessage, isBaseMessage } from '@langchain/core/messages';
|
|
8
15
|
import type {
|
|
9
16
|
RunnableConfig,
|
|
10
17
|
RunnableToolLike,
|
|
@@ -14,12 +21,20 @@ import type { StructuredToolInterface } from '@langchain/core/tools';
|
|
|
14
21
|
import type * as t from '@/types';
|
|
15
22
|
import { RunnableCallable } from '@/utils';
|
|
16
23
|
|
|
24
|
+
/**
|
|
25
|
+
* Helper to check if a value is a Send object
|
|
26
|
+
*/
|
|
27
|
+
function isSend(value: unknown): value is Send {
|
|
28
|
+
return value instanceof Send;
|
|
29
|
+
}
|
|
30
|
+
|
|
17
31
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
18
32
|
export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
19
33
|
tools: t.GenericTool[];
|
|
20
34
|
private toolMap: Map<string, StructuredToolInterface | RunnableToolLike>;
|
|
21
35
|
private loadRuntimeTools?: t.ToolRefGenerator;
|
|
22
36
|
handleToolErrors = true;
|
|
37
|
+
trace = false;
|
|
23
38
|
toolCallStepIds?: Map<string, string>;
|
|
24
39
|
errorHandler?: t.ToolNodeConstructorParams['errorHandler'];
|
|
25
40
|
private toolUsageCount: Map<string, number>;
|
|
@@ -52,60 +67,50 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
52
67
|
return new Map(this.toolUsageCount); // Return a copy
|
|
53
68
|
}
|
|
54
69
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
70
|
+
/**
|
|
71
|
+
* Runs a single tool call with error handling
|
|
72
|
+
*/
|
|
73
|
+
protected async runTool(
|
|
74
|
+
call: ToolCall,
|
|
75
|
+
config: RunnableConfig
|
|
76
|
+
): Promise<BaseMessage | Command> {
|
|
77
|
+
const tool = this.toolMap.get(call.name);
|
|
78
|
+
try {
|
|
79
|
+
if (tool === undefined) {
|
|
80
|
+
throw new Error(`Tool "${call.name}" not found.`);
|
|
81
|
+
}
|
|
82
|
+
const turn = this.toolUsageCount.get(call.name) ?? 0;
|
|
83
|
+
this.toolUsageCount.set(call.name, turn + 1);
|
|
84
|
+
const args = call.args;
|
|
85
|
+
const stepId = this.toolCallStepIds?.get(call.id!);
|
|
86
|
+
const output = await tool.invoke(
|
|
87
|
+
{ ...call, args, type: 'tool_call', stepId, turn },
|
|
88
|
+
config
|
|
68
89
|
);
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
90
|
+
if (
|
|
91
|
+
(isBaseMessage(output) && output._getType() === 'tool') ||
|
|
92
|
+
isCommand(output)
|
|
93
|
+
) {
|
|
94
|
+
return output;
|
|
95
|
+
} else {
|
|
96
|
+
return new ToolMessage({
|
|
97
|
+
status: 'success',
|
|
98
|
+
name: tool.name,
|
|
99
|
+
content: typeof output === 'string' ? output : JSON.stringify(output),
|
|
100
|
+
tool_call_id: call.id!,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
} catch (_e: unknown) {
|
|
104
|
+
const e = _e as Error;
|
|
105
|
+
if (!this.handleToolErrors) {
|
|
106
|
+
throw e;
|
|
107
|
+
}
|
|
108
|
+
if (isGraphInterrupt(e)) {
|
|
109
|
+
throw e;
|
|
110
|
+
}
|
|
111
|
+
if (this.errorHandler) {
|
|
75
112
|
try {
|
|
76
|
-
|
|
77
|
-
throw new Error(`Tool "${call.name}" not found.`);
|
|
78
|
-
}
|
|
79
|
-
const turn = this.toolUsageCount.get(call.name) ?? 0;
|
|
80
|
-
this.toolUsageCount.set(call.name, turn + 1);
|
|
81
|
-
const args = call.args;
|
|
82
|
-
const stepId = this.toolCallStepIds?.get(call.id!);
|
|
83
|
-
const output = await tool.invoke(
|
|
84
|
-
{ ...call, args, type: 'tool_call', stepId, turn },
|
|
85
|
-
config
|
|
86
|
-
);
|
|
87
|
-
if (
|
|
88
|
-
(isBaseMessage(output) && output._getType() === 'tool') ||
|
|
89
|
-
isCommand(output)
|
|
90
|
-
) {
|
|
91
|
-
return output;
|
|
92
|
-
} else {
|
|
93
|
-
return new ToolMessage({
|
|
94
|
-
name: tool.name,
|
|
95
|
-
content:
|
|
96
|
-
typeof output === 'string' ? output : JSON.stringify(output),
|
|
97
|
-
tool_call_id: call.id!,
|
|
98
|
-
});
|
|
99
|
-
}
|
|
100
|
-
} catch (_e: unknown) {
|
|
101
|
-
const e = _e as Error;
|
|
102
|
-
if (!this.handleToolErrors) {
|
|
103
|
-
throw e;
|
|
104
|
-
}
|
|
105
|
-
if (isGraphInterrupt(e)) {
|
|
106
|
-
throw e;
|
|
107
|
-
}
|
|
108
|
-
this.errorHandler?.(
|
|
113
|
+
await this.errorHandler(
|
|
109
114
|
{
|
|
110
115
|
error: e,
|
|
111
116
|
id: call.id!,
|
|
@@ -114,27 +119,164 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
114
119
|
},
|
|
115
120
|
config.metadata
|
|
116
121
|
);
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
122
|
+
} catch (handlerError) {
|
|
123
|
+
// eslint-disable-next-line no-console
|
|
124
|
+
console.error('Error in errorHandler:', {
|
|
125
|
+
toolName: call.name,
|
|
126
|
+
toolCallId: call.id,
|
|
127
|
+
toolArgs: call.args,
|
|
128
|
+
stepId: this.toolCallStepIds?.get(call.id!),
|
|
129
|
+
turn: this.toolUsageCount.get(call.name),
|
|
130
|
+
originalError: {
|
|
131
|
+
message: e.message,
|
|
132
|
+
stack: e.stack ?? undefined,
|
|
133
|
+
},
|
|
134
|
+
handlerError:
|
|
135
|
+
handlerError instanceof Error
|
|
136
|
+
? {
|
|
137
|
+
message: handlerError.message,
|
|
138
|
+
stack: handlerError.stack ?? undefined,
|
|
139
|
+
}
|
|
140
|
+
: {
|
|
141
|
+
message: String(handlerError),
|
|
142
|
+
stack: undefined,
|
|
143
|
+
},
|
|
121
144
|
});
|
|
122
145
|
}
|
|
123
|
-
}
|
|
124
|
-
|
|
146
|
+
}
|
|
147
|
+
return new ToolMessage({
|
|
148
|
+
status: 'error',
|
|
149
|
+
content: `Error: ${e.message}\n Please fix your mistakes.`,
|
|
150
|
+
name: call.name,
|
|
151
|
+
tool_call_id: call.id ?? '',
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
157
|
+
protected async run(input: any, config: RunnableConfig): Promise<T> {
|
|
158
|
+
let outputs: (BaseMessage | Command)[];
|
|
159
|
+
|
|
160
|
+
if (this.isSendInput(input)) {
|
|
161
|
+
outputs = [await this.runTool(input.lg_tool_call, config)];
|
|
162
|
+
} else {
|
|
163
|
+
let messages: BaseMessage[];
|
|
164
|
+
if (Array.isArray(input)) {
|
|
165
|
+
messages = input;
|
|
166
|
+
} else if (this.isMessagesState(input)) {
|
|
167
|
+
messages = input.messages;
|
|
168
|
+
} else {
|
|
169
|
+
throw new Error(
|
|
170
|
+
'ToolNode only accepts BaseMessage[] or { messages: BaseMessage[] } as input.'
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const toolMessageIds: Set<string> = new Set(
|
|
175
|
+
messages
|
|
176
|
+
.filter((msg) => msg._getType() === 'tool')
|
|
177
|
+
.map((msg) => (msg as ToolMessage).tool_call_id)
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
let aiMessage: AIMessage | undefined;
|
|
181
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
182
|
+
const message = messages[i];
|
|
183
|
+
if (isAIMessage(message)) {
|
|
184
|
+
aiMessage = message;
|
|
185
|
+
break;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (aiMessage == null || !isAIMessage(aiMessage)) {
|
|
190
|
+
throw new Error('ToolNode only accepts AIMessages as input.');
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (this.loadRuntimeTools) {
|
|
194
|
+
const { tools, toolMap } = this.loadRuntimeTools(
|
|
195
|
+
aiMessage.tool_calls ?? []
|
|
196
|
+
);
|
|
197
|
+
this.tools = tools;
|
|
198
|
+
this.toolMap =
|
|
199
|
+
toolMap ?? new Map(tools.map((tool) => [tool.name, tool]));
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
outputs = await Promise.all(
|
|
203
|
+
aiMessage.tool_calls
|
|
204
|
+
?.filter((call) => {
|
|
205
|
+
/**
|
|
206
|
+
* Filter out:
|
|
207
|
+
* 1. Already processed tool calls (present in toolMessageIds)
|
|
208
|
+
* 2. Server tool calls (e.g., web_search with IDs starting with 'srvtoolu_')
|
|
209
|
+
* which are executed by the provider's API and don't require invocation
|
|
210
|
+
*/
|
|
211
|
+
return (
|
|
212
|
+
(call.id == null || !toolMessageIds.has(call.id)) &&
|
|
213
|
+
!(call.id?.startsWith('srvtoolu_') ?? false)
|
|
214
|
+
);
|
|
215
|
+
})
|
|
216
|
+
.map((call) => this.runTool(call, config)) ?? []
|
|
217
|
+
);
|
|
218
|
+
}
|
|
125
219
|
|
|
126
220
|
if (!outputs.some(isCommand)) {
|
|
127
221
|
return (Array.isArray(input) ? outputs : { messages: outputs }) as T;
|
|
128
222
|
}
|
|
129
223
|
|
|
130
|
-
const combinedOutputs
|
|
224
|
+
const combinedOutputs: (
|
|
225
|
+
| { messages: BaseMessage[] }
|
|
226
|
+
| BaseMessage[]
|
|
227
|
+
| Command
|
|
228
|
+
)[] = [];
|
|
229
|
+
let parentCommand: Command | null = null;
|
|
230
|
+
|
|
231
|
+
for (const output of outputs) {
|
|
131
232
|
if (isCommand(output)) {
|
|
132
|
-
|
|
233
|
+
if (
|
|
234
|
+
output.graph === Command.PARENT &&
|
|
235
|
+
Array.isArray(output.goto) &&
|
|
236
|
+
output.goto.every((send): send is Send => isSend(send))
|
|
237
|
+
) {
|
|
238
|
+
if (parentCommand) {
|
|
239
|
+
(parentCommand.goto as Send[]).push(...(output.goto as Send[]));
|
|
240
|
+
} else {
|
|
241
|
+
parentCommand = new Command({
|
|
242
|
+
graph: Command.PARENT,
|
|
243
|
+
goto: output.goto,
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
} else {
|
|
247
|
+
combinedOutputs.push(output);
|
|
248
|
+
}
|
|
249
|
+
} else {
|
|
250
|
+
combinedOutputs.push(
|
|
251
|
+
Array.isArray(input) ? [output] : { messages: [output] }
|
|
252
|
+
);
|
|
133
253
|
}
|
|
134
|
-
|
|
135
|
-
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
if (parentCommand) {
|
|
257
|
+
combinedOutputs.push(parentCommand);
|
|
258
|
+
}
|
|
259
|
+
|
|
136
260
|
return combinedOutputs as T;
|
|
137
261
|
}
|
|
262
|
+
|
|
263
|
+
private isSendInput(input: unknown): input is { lg_tool_call: ToolCall } {
|
|
264
|
+
return (
|
|
265
|
+
typeof input === 'object' && input != null && 'lg_tool_call' in input
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
private isMessagesState(
|
|
270
|
+
input: unknown
|
|
271
|
+
): input is { messages: BaseMessage[] } {
|
|
272
|
+
return (
|
|
273
|
+
typeof input === 'object' &&
|
|
274
|
+
input != null &&
|
|
275
|
+
'messages' in input &&
|
|
276
|
+
Array.isArray((input as { messages: unknown }).messages) &&
|
|
277
|
+
(input as { messages: unknown[] }).messages.every(isBaseMessage)
|
|
278
|
+
);
|
|
279
|
+
}
|
|
138
280
|
}
|
|
139
281
|
|
|
140
282
|
function areToolCallsInvoked(
|
package/src/tools/handlers.ts
CHANGED
|
@@ -9,7 +9,6 @@ import type { AgentContext } from '@/agents/AgentContext';
|
|
|
9
9
|
import type * as t from '@/types';
|
|
10
10
|
import {
|
|
11
11
|
ToolCallTypes,
|
|
12
|
-
ContentTypes,
|
|
13
12
|
GraphEvents,
|
|
14
13
|
StepTypes,
|
|
15
14
|
Providers,
|
|
@@ -87,22 +86,33 @@ export async function handleToolCallChunks({
|
|
|
87
86
|
const alreadyDispatched =
|
|
88
87
|
prevRunStep?.type === StepTypes.MESSAGE_CREATION &&
|
|
89
88
|
graph.messageStepHasToolCalls.has(prevStepId);
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
89
|
+
|
|
90
|
+
if (prevRunStep?.type === StepTypes.TOOL_CALLS) {
|
|
91
|
+
/**
|
|
92
|
+
* If previous step is already a tool_calls step, use that step ID
|
|
93
|
+
* This ensures tool call deltas are dispatched to the correct step
|
|
94
|
+
*/
|
|
95
|
+
stepId = prevStepId;
|
|
96
|
+
} else if (
|
|
97
|
+
!alreadyDispatched &&
|
|
98
|
+
prevRunStep?.type === StepTypes.MESSAGE_CREATION
|
|
99
|
+
) {
|
|
100
|
+
/**
|
|
101
|
+
* Create tool_calls step as soon as we receive the first tool call chunk
|
|
102
|
+
* This ensures deltas are always associated with the correct step
|
|
103
|
+
*
|
|
104
|
+
* NOTE: We do NOT dispatch an empty text block here because:
|
|
105
|
+
* - Empty text blocks cause providers (Anthropic, Bedrock) to reject messages
|
|
106
|
+
* - The tool_calls themselves are sufficient for the step
|
|
107
|
+
* - Empty content with tool_call_ids gets stored in conversation history
|
|
108
|
+
* and causes "messages must have non-empty content" errors on replay
|
|
109
|
+
*/
|
|
100
110
|
graph.messageStepHasToolCalls.set(prevStepId, true);
|
|
101
111
|
stepId = await graph.dispatchRunStep(
|
|
102
112
|
stepKey,
|
|
103
113
|
{
|
|
104
114
|
type: StepTypes.TOOL_CALLS,
|
|
105
|
-
tool_calls,
|
|
115
|
+
tool_calls: tool_calls ?? [],
|
|
106
116
|
},
|
|
107
117
|
metadata
|
|
108
118
|
);
|
|
@@ -149,26 +159,21 @@ export const handleToolCalls = async (
|
|
|
149
159
|
// no previous step
|
|
150
160
|
}
|
|
151
161
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
},
|
|
162
|
-
],
|
|
163
|
-
});
|
|
164
|
-
};
|
|
162
|
+
/**
|
|
163
|
+
* NOTE: We do NOT dispatch empty text blocks with tool_call_ids because:
|
|
164
|
+
* - Empty text blocks cause providers (Anthropic, Bedrock) to reject messages
|
|
165
|
+
* - They get stored in conversation history and cause errors on replay:
|
|
166
|
+
* "messages must have non-empty content" (Anthropic)
|
|
167
|
+
* "The content field in the Message object is empty" (Bedrock)
|
|
168
|
+
* - The tool_calls themselves are sufficient
|
|
169
|
+
*/
|
|
170
|
+
|
|
165
171
|
/* If the previous step exists and is a message creation */
|
|
166
172
|
if (
|
|
167
173
|
prevStepId &&
|
|
168
174
|
prevRunStep &&
|
|
169
175
|
prevRunStep.type === StepTypes.MESSAGE_CREATION
|
|
170
176
|
) {
|
|
171
|
-
await dispatchToolCallIds(prevStepId);
|
|
172
177
|
graph.messageStepHasToolCalls.set(prevStepId, true);
|
|
173
178
|
/* If the previous step doesn't exist or is not a message creation */
|
|
174
179
|
} else if (
|
|
@@ -186,8 +191,7 @@ export const handleToolCalls = async (
|
|
|
186
191
|
},
|
|
187
192
|
metadata
|
|
188
193
|
);
|
|
189
|
-
|
|
190
|
-
graph.messageStepHasToolCalls.set(prevStepId, true);
|
|
194
|
+
graph.messageStepHasToolCalls.set(stepId, true);
|
|
191
195
|
}
|
|
192
196
|
|
|
193
197
|
await graph.dispatchRunStep(
|
|
@@ -1,51 +1,51 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
AnthropicTextBlockParam,
|
|
3
|
-
AnthropicWebSearchResultBlockParam,
|
|
4
|
-
} from '@/llm/anthropic/types';
|
|
5
|
-
import type { SearchResultData, ProcessedOrganic } from './types';
|
|
6
|
-
import { getAttribution } from './utils';
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Coerces Anthropic web search results to the SearchResultData format
|
|
10
|
-
* @param results - Array of Anthropic web search results
|
|
11
|
-
* @param turn - The turn number to associate with these results
|
|
12
|
-
* @returns SearchResultData with minimal ProcessedOrganic items
|
|
13
|
-
*/
|
|
14
|
-
export function coerceAnthropicSearchResults({
|
|
15
|
-
results,
|
|
16
|
-
turn = 0,
|
|
17
|
-
}: {
|
|
18
|
-
results: (AnthropicTextBlockParam | AnthropicWebSearchResultBlockParam)[];
|
|
19
|
-
turn?: number;
|
|
20
|
-
}): SearchResultData {
|
|
21
|
-
const organic: ProcessedOrganic[] = results
|
|
22
|
-
.filter((result) => result.type === 'web_search_result')
|
|
23
|
-
.map((result, index) => ({
|
|
24
|
-
link: result.url,
|
|
25
|
-
position: index + 1,
|
|
26
|
-
title: result.title,
|
|
27
|
-
date: result.page_age ?? undefined,
|
|
28
|
-
attribution: getAttribution(result.url),
|
|
29
|
-
}));
|
|
30
|
-
|
|
31
|
-
return {
|
|
32
|
-
turn,
|
|
33
|
-
organic,
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Helper function to check if an object is an Anthropic web search result
|
|
39
|
-
*/
|
|
40
|
-
export function isAnthropicWebSearchResult(
|
|
41
|
-
obj: unknown
|
|
42
|
-
): obj is AnthropicWebSearchResultBlockParam {
|
|
43
|
-
return (
|
|
44
|
-
typeof obj === 'object' &&
|
|
45
|
-
obj !== null &&
|
|
46
|
-
'type' in obj &&
|
|
47
|
-
obj.type === 'web_search_result' &&
|
|
48
|
-
'url' in obj &&
|
|
49
|
-
typeof (obj as Record<string, unknown>).url === 'string'
|
|
50
|
-
);
|
|
51
|
-
}
|
|
1
|
+
import type {
|
|
2
|
+
AnthropicTextBlockParam,
|
|
3
|
+
AnthropicWebSearchResultBlockParam,
|
|
4
|
+
} from '@/llm/anthropic/types';
|
|
5
|
+
import type { SearchResultData, ProcessedOrganic } from './types';
|
|
6
|
+
import { getAttribution } from './utils';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Coerces Anthropic web search results to the SearchResultData format
|
|
10
|
+
* @param results - Array of Anthropic web search results
|
|
11
|
+
* @param turn - The turn number to associate with these results
|
|
12
|
+
* @returns SearchResultData with minimal ProcessedOrganic items
|
|
13
|
+
*/
|
|
14
|
+
export function coerceAnthropicSearchResults({
|
|
15
|
+
results,
|
|
16
|
+
turn = 0,
|
|
17
|
+
}: {
|
|
18
|
+
results: (AnthropicTextBlockParam | AnthropicWebSearchResultBlockParam)[];
|
|
19
|
+
turn?: number;
|
|
20
|
+
}): SearchResultData {
|
|
21
|
+
const organic: ProcessedOrganic[] = results
|
|
22
|
+
.filter((result) => result.type === 'web_search_result')
|
|
23
|
+
.map((result, index) => ({
|
|
24
|
+
link: result.url,
|
|
25
|
+
position: index + 1,
|
|
26
|
+
title: result.title,
|
|
27
|
+
date: result.page_age ?? undefined,
|
|
28
|
+
attribution: getAttribution(result.url),
|
|
29
|
+
}));
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
turn,
|
|
33
|
+
organic,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Helper function to check if an object is an Anthropic web search result
|
|
39
|
+
*/
|
|
40
|
+
export function isAnthropicWebSearchResult(
|
|
41
|
+
obj: unknown
|
|
42
|
+
): obj is AnthropicWebSearchResultBlockParam {
|
|
43
|
+
return (
|
|
44
|
+
typeof obj === 'object' &&
|
|
45
|
+
obj !== null &&
|
|
46
|
+
'type' in obj &&
|
|
47
|
+
obj.type === 'web_search_result' &&
|
|
48
|
+
'url' in obj &&
|
|
49
|
+
typeof (obj as Record<string, unknown>).url === 'string'
|
|
50
|
+
);
|
|
51
|
+
}
|