@librechat/agents 3.1.67-dev.4 → 3.1.68
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/agents/AgentContext.cjs +3 -23
- package/dist/cjs/agents/AgentContext.cjs.map +1 -1
- package/dist/cjs/common/enum.cjs +0 -16
- package/dist/cjs/common/enum.cjs.map +1 -1
- package/dist/cjs/graphs/Graph.cjs +0 -91
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/graphs/MultiAgentGraph.cjs +36 -0
- package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
- package/dist/cjs/main.cjs +1 -53
- package/dist/cjs/main.cjs.map +1 -1
- package/dist/cjs/messages/format.cjs +12 -74
- package/dist/cjs/messages/format.cjs.map +1 -1
- package/dist/cjs/run.cjs +0 -111
- package/dist/cjs/run.cjs.map +1 -1
- package/dist/cjs/summarization/index.cjs +41 -0
- package/dist/cjs/summarization/index.cjs.map +1 -1
- package/dist/cjs/summarization/node.cjs +121 -63
- package/dist/cjs/summarization/node.cjs.map +1 -1
- package/dist/cjs/tools/ToolNode.cjs +140 -304
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/esm/agents/AgentContext.mjs +3 -23
- package/dist/esm/agents/AgentContext.mjs.map +1 -1
- package/dist/esm/common/enum.mjs +1 -15
- package/dist/esm/common/enum.mjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +0 -91
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/graphs/MultiAgentGraph.mjs +36 -0
- package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
- package/dist/esm/main.mjs +2 -13
- package/dist/esm/main.mjs.map +1 -1
- package/dist/esm/messages/format.mjs +4 -66
- package/dist/esm/messages/format.mjs.map +1 -1
- package/dist/esm/run.mjs +0 -111
- package/dist/esm/run.mjs.map +1 -1
- package/dist/esm/summarization/index.mjs +41 -1
- package/dist/esm/summarization/index.mjs.map +1 -1
- package/dist/esm/summarization/node.mjs +121 -63
- package/dist/esm/summarization/node.mjs.map +1 -1
- package/dist/esm/tools/ToolNode.mjs +142 -306
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/types/agents/AgentContext.d.ts +0 -6
- package/dist/types/common/enum.d.ts +1 -10
- package/dist/types/graphs/Graph.d.ts +0 -2
- package/dist/types/graphs/MultiAgentGraph.d.ts +12 -0
- package/dist/types/index.d.ts +0 -8
- package/dist/types/messages/format.d.ts +1 -2
- package/dist/types/run.d.ts +0 -1
- package/dist/types/summarization/index.d.ts +2 -0
- package/dist/types/summarization/node.d.ts +0 -2
- package/dist/types/tools/ToolNode.d.ts +2 -24
- package/dist/types/types/graph.d.ts +2 -61
- package/dist/types/types/index.d.ts +0 -1
- package/dist/types/types/run.d.ts +0 -20
- package/dist/types/types/tools.d.ts +1 -38
- package/package.json +1 -5
- package/src/agents/AgentContext.ts +2 -26
- package/src/common/enum.ts +0 -15
- package/src/graphs/Graph.ts +0 -113
- package/src/graphs/MultiAgentGraph.ts +39 -0
- package/src/graphs/__tests__/MultiAgentGraph.test.ts +91 -0
- package/src/index.ts +0 -10
- package/src/messages/format.ts +4 -74
- package/src/run.ts +0 -126
- package/src/summarization/__tests__/node.test.ts +42 -0
- package/src/summarization/__tests__/trigger.test.ts +100 -1
- package/src/summarization/index.ts +47 -0
- package/src/summarization/node.ts +149 -77
- package/src/tools/ToolNode.ts +169 -391
- package/src/tools/__tests__/ToolNode.session.test.ts +12 -12
- package/src/types/graph.ts +1 -80
- package/src/types/index.ts +0 -1
- package/src/types/run.ts +0 -20
- package/src/types/tools.ts +1 -41
- package/dist/cjs/hooks/HookRegistry.cjs +0 -162
- package/dist/cjs/hooks/HookRegistry.cjs.map +0 -1
- package/dist/cjs/hooks/executeHooks.cjs +0 -276
- package/dist/cjs/hooks/executeHooks.cjs.map +0 -1
- package/dist/cjs/hooks/matchers.cjs +0 -256
- package/dist/cjs/hooks/matchers.cjs.map +0 -1
- package/dist/cjs/hooks/types.cjs +0 -27
- package/dist/cjs/hooks/types.cjs.map +0 -1
- package/dist/cjs/tools/BashExecutor.cjs +0 -175
- package/dist/cjs/tools/BashExecutor.cjs.map +0 -1
- package/dist/cjs/tools/BashProgrammaticToolCalling.cjs +0 -296
- package/dist/cjs/tools/BashProgrammaticToolCalling.cjs.map +0 -1
- package/dist/cjs/tools/ReadFile.cjs +0 -43
- package/dist/cjs/tools/ReadFile.cjs.map +0 -1
- package/dist/cjs/tools/SkillTool.cjs +0 -50
- package/dist/cjs/tools/SkillTool.cjs.map +0 -1
- package/dist/cjs/tools/SubagentTool.cjs +0 -92
- package/dist/cjs/tools/SubagentTool.cjs.map +0 -1
- package/dist/cjs/tools/skillCatalog.cjs +0 -84
- package/dist/cjs/tools/skillCatalog.cjs.map +0 -1
- package/dist/cjs/tools/subagent/SubagentExecutor.cjs +0 -511
- package/dist/cjs/tools/subagent/SubagentExecutor.cjs.map +0 -1
- package/dist/esm/hooks/HookRegistry.mjs +0 -160
- package/dist/esm/hooks/HookRegistry.mjs.map +0 -1
- package/dist/esm/hooks/executeHooks.mjs +0 -273
- package/dist/esm/hooks/executeHooks.mjs.map +0 -1
- package/dist/esm/hooks/matchers.mjs +0 -251
- package/dist/esm/hooks/matchers.mjs.map +0 -1
- package/dist/esm/hooks/types.mjs +0 -25
- package/dist/esm/hooks/types.mjs.map +0 -1
- package/dist/esm/tools/BashExecutor.mjs +0 -169
- package/dist/esm/tools/BashExecutor.mjs.map +0 -1
- package/dist/esm/tools/BashProgrammaticToolCalling.mjs +0 -287
- package/dist/esm/tools/BashProgrammaticToolCalling.mjs.map +0 -1
- package/dist/esm/tools/ReadFile.mjs +0 -38
- package/dist/esm/tools/ReadFile.mjs.map +0 -1
- package/dist/esm/tools/SkillTool.mjs +0 -45
- package/dist/esm/tools/SkillTool.mjs.map +0 -1
- package/dist/esm/tools/SubagentTool.mjs +0 -85
- package/dist/esm/tools/SubagentTool.mjs.map +0 -1
- package/dist/esm/tools/skillCatalog.mjs +0 -82
- package/dist/esm/tools/skillCatalog.mjs.map +0 -1
- package/dist/esm/tools/subagent/SubagentExecutor.mjs +0 -505
- package/dist/esm/tools/subagent/SubagentExecutor.mjs.map +0 -1
- package/dist/types/hooks/HookRegistry.d.ts +0 -56
- package/dist/types/hooks/executeHooks.d.ts +0 -79
- package/dist/types/hooks/index.d.ts +0 -6
- package/dist/types/hooks/matchers.d.ts +0 -95
- package/dist/types/hooks/types.d.ts +0 -320
- package/dist/types/tools/BashExecutor.d.ts +0 -45
- package/dist/types/tools/BashProgrammaticToolCalling.d.ts +0 -72
- package/dist/types/tools/ReadFile.d.ts +0 -28
- package/dist/types/tools/SkillTool.d.ts +0 -40
- package/dist/types/tools/SubagentTool.d.ts +0 -36
- package/dist/types/tools/skillCatalog.d.ts +0 -19
- package/dist/types/tools/subagent/SubagentExecutor.d.ts +0 -137
- package/dist/types/tools/subagent/index.d.ts +0 -2
- package/dist/types/types/skill.d.ts +0 -9
- package/src/hooks/HookRegistry.ts +0 -208
- package/src/hooks/__tests__/HookRegistry.test.ts +0 -190
- package/src/hooks/__tests__/compactHooks.test.ts +0 -214
- package/src/hooks/__tests__/executeHooks.test.ts +0 -1013
- package/src/hooks/__tests__/integration.test.ts +0 -337
- package/src/hooks/__tests__/matchers.test.ts +0 -238
- package/src/hooks/__tests__/toolHooks.test.ts +0 -669
- package/src/hooks/executeHooks.ts +0 -375
- package/src/hooks/index.ts +0 -57
- package/src/hooks/matchers.ts +0 -280
- package/src/hooks/types.ts +0 -404
- package/src/messages/formatAgentMessages.skills.test.ts +0 -334
- package/src/scripts/multi-agent-subagent.ts +0 -246
- package/src/scripts/subagent-event-driven-debug.ts +0 -190
- package/src/scripts/subagent-tools-debug.ts +0 -160
- package/src/specs/subagent.test.ts +0 -305
- package/src/tools/BashExecutor.ts +0 -205
- package/src/tools/BashProgrammaticToolCalling.ts +0 -397
- package/src/tools/ReadFile.ts +0 -39
- package/src/tools/SkillTool.ts +0 -46
- package/src/tools/SubagentTool.ts +0 -100
- package/src/tools/__tests__/ReadFile.test.ts +0 -44
- package/src/tools/__tests__/SkillTool.test.ts +0 -442
- package/src/tools/__tests__/SubagentExecutor.test.ts +0 -1148
- package/src/tools/__tests__/SubagentTool.test.ts +0 -149
- package/src/tools/__tests__/skillCatalog.test.ts +0 -161
- package/src/tools/__tests__/subagentHooks.test.ts +0 -215
- package/src/tools/skillCatalog.ts +0 -126
- package/src/tools/subagent/SubagentExecutor.ts +0 -676
- package/src/tools/subagent/index.ts +0 -13
- package/src/types/skill.ts +0 -11
|
@@ -1,190 +0,0 @@
|
|
|
1
|
-
import { config } from 'dotenv';
|
|
2
|
-
config();
|
|
3
|
-
|
|
4
|
-
import { HumanMessage } from '@langchain/core/messages';
|
|
5
|
-
import type { BaseMessage } from '@langchain/core/messages';
|
|
6
|
-
import type * as t from '@/types';
|
|
7
|
-
import { ChatModelStreamHandler } from '@/stream';
|
|
8
|
-
import { ToolEndHandler, ModelEndHandler } from '@/events';
|
|
9
|
-
import { Providers, GraphEvents, Constants } from '@/common';
|
|
10
|
-
import { Run } from '@/run';
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Repro for LibreChat's actual setup: event-driven tools via `toolDefinitions`
|
|
14
|
-
* + an ON_TOOL_EXECUTE handler that runs the tool. Self-spawn subagent must
|
|
15
|
-
* be able to drive the SAME tool pipeline.
|
|
16
|
-
*/
|
|
17
|
-
const apiKey = process.env.OPENAI_API_KEY!;
|
|
18
|
-
if (!apiKey) {
|
|
19
|
-
console.error('Missing OPENAI_API_KEY');
|
|
20
|
-
process.exit(1);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
// Simulate LibreChat: tool definitions only, execution routed via event.
|
|
24
|
-
const calculatorDef: t.LCTool = {
|
|
25
|
-
name: 'calculator',
|
|
26
|
-
description: 'Evaluate a math expression. Use for any arithmetic.',
|
|
27
|
-
parameters: {
|
|
28
|
-
type: 'object',
|
|
29
|
-
properties: {
|
|
30
|
-
expression: {
|
|
31
|
-
type: 'string',
|
|
32
|
-
description: "A JS math expression, e.g. '42 * 58'",
|
|
33
|
-
},
|
|
34
|
-
},
|
|
35
|
-
required: ['expression'],
|
|
36
|
-
},
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
async function main() {
|
|
40
|
-
console.log('=== Subagent Event-Driven Tool Diagnostic ===\n');
|
|
41
|
-
|
|
42
|
-
const parentAgent: t.AgentInputs = {
|
|
43
|
-
agentId: 'supervisor',
|
|
44
|
-
provider: Providers.OPENAI,
|
|
45
|
-
clientOptions: { modelName: 'gpt-4o-mini', apiKey },
|
|
46
|
-
instructions: `You have calculator AND can spawn a "self" subagent in an isolated context.
|
|
47
|
-
For any arithmetic question, spawn the "self" subagent with the math task.
|
|
48
|
-
The subagent MUST use the calculator tool — never estimate.`,
|
|
49
|
-
maxContextTokens: 8000,
|
|
50
|
-
toolDefinitions: [calculatorDef],
|
|
51
|
-
subagentConfigs: [
|
|
52
|
-
{
|
|
53
|
-
type: 'self',
|
|
54
|
-
self: true,
|
|
55
|
-
name: 'supervisor',
|
|
56
|
-
description:
|
|
57
|
-
'Spawn a copy of this agent in an isolated context for a focused math subtask.',
|
|
58
|
-
},
|
|
59
|
-
],
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
let toolCallCount = 0;
|
|
63
|
-
const customHandlers: Record<string, t.EventHandler> = {
|
|
64
|
-
[GraphEvents.CHAT_MODEL_STREAM]: new ChatModelStreamHandler(),
|
|
65
|
-
[GraphEvents.TOOL_END]: new ToolEndHandler(),
|
|
66
|
-
[GraphEvents.CHAT_MODEL_END]: new ModelEndHandler(),
|
|
67
|
-
[GraphEvents.ON_TOOL_EXECUTE]: {
|
|
68
|
-
handle: (_event, rawData): void => {
|
|
69
|
-
const data = rawData as t.ToolExecuteBatchRequest;
|
|
70
|
-
console.log(
|
|
71
|
-
`[PARENT ON_TOOL_EXECUTE] agentId=${data.agentId} calls=${data.toolCalls
|
|
72
|
-
.map((c) => c.name)
|
|
73
|
-
.join(',')}`
|
|
74
|
-
);
|
|
75
|
-
const results: t.ToolExecuteResult[] = data.toolCalls.map((call) => {
|
|
76
|
-
toolCallCount += 1;
|
|
77
|
-
const args = call.args as { expression?: string };
|
|
78
|
-
const expression = args.expression ?? '';
|
|
79
|
-
let content: string;
|
|
80
|
-
try {
|
|
81
|
-
// eslint-disable-next-line no-eval
|
|
82
|
-
const result = eval(expression);
|
|
83
|
-
content = `${expression} = ${result}`;
|
|
84
|
-
} catch (err) {
|
|
85
|
-
content = `Error: ${String(err)}`;
|
|
86
|
-
}
|
|
87
|
-
return {
|
|
88
|
-
toolCallId: call.id!,
|
|
89
|
-
status: 'success',
|
|
90
|
-
content,
|
|
91
|
-
};
|
|
92
|
-
});
|
|
93
|
-
data.resolve(results);
|
|
94
|
-
},
|
|
95
|
-
},
|
|
96
|
-
[GraphEvents.ON_RUN_STEP]: {
|
|
97
|
-
handle: (event, data): void => {
|
|
98
|
-
const d = data as { type?: string; runId?: string; agentId?: string };
|
|
99
|
-
console.log(
|
|
100
|
-
`[PARENT ${event}] type=${d.type} agentId=${d.agentId ?? '-'} runId=${d.runId ?? '-'}`
|
|
101
|
-
);
|
|
102
|
-
},
|
|
103
|
-
},
|
|
104
|
-
[GraphEvents.ON_RUN_STEP_COMPLETED]: {
|
|
105
|
-
handle: (event, data): void => {
|
|
106
|
-
const r = (
|
|
107
|
-
data as { result: { type: string; tool_call?: { name?: string } } }
|
|
108
|
-
).result;
|
|
109
|
-
console.log(
|
|
110
|
-
`[PARENT ${event}] type=${r.type} tool=${r.tool_call?.name ?? '-'}`
|
|
111
|
-
);
|
|
112
|
-
},
|
|
113
|
-
},
|
|
114
|
-
[GraphEvents.ON_SUBAGENT_UPDATE]: {
|
|
115
|
-
handle: (_event, rawData): void => {
|
|
116
|
-
const d = rawData as t.SubagentUpdateEvent;
|
|
117
|
-
console.log(
|
|
118
|
-
`[SUBAGENT ${d.phase}] [${d.subagentType}] tool_call_id=${d.parentToolCallId ?? '-'} ${d.label ?? ''}`
|
|
119
|
-
);
|
|
120
|
-
},
|
|
121
|
-
},
|
|
122
|
-
};
|
|
123
|
-
|
|
124
|
-
const run = await Run.create<t.IState>({
|
|
125
|
-
runId: `sub-evt-${Date.now()}`,
|
|
126
|
-
graphConfig: { type: 'standard', agents: [parentAgent] },
|
|
127
|
-
customHandlers,
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
const question = new HumanMessage(
|
|
131
|
-
'Compute (42 * 58) + (13 ** 3). Use the self subagent, and have it use the calculator.'
|
|
132
|
-
);
|
|
133
|
-
|
|
134
|
-
console.log('User:', question.content, '\n');
|
|
135
|
-
|
|
136
|
-
await run.processStream(
|
|
137
|
-
{ messages: [question] },
|
|
138
|
-
{
|
|
139
|
-
configurable: { thread_id: `sub-evt` },
|
|
140
|
-
version: 'v2' as const,
|
|
141
|
-
}
|
|
142
|
-
);
|
|
143
|
-
|
|
144
|
-
const msgs = (run.getRunMessages() ?? []) as BaseMessage[];
|
|
145
|
-
console.log('\n--- Run messages ---\n');
|
|
146
|
-
for (const msg of msgs) {
|
|
147
|
-
const type = msg._getType();
|
|
148
|
-
const name = 'name' in msg ? (msg as { name?: string }).name : undefined;
|
|
149
|
-
const content =
|
|
150
|
-
typeof msg.content === 'string'
|
|
151
|
-
? msg.content.slice(0, 400)
|
|
152
|
-
: JSON.stringify(msg.content).slice(0, 400);
|
|
153
|
-
const toolCalls =
|
|
154
|
-
'tool_calls' in msg
|
|
155
|
-
? (msg as { tool_calls?: Array<{ name: string; args: unknown }> })
|
|
156
|
-
.tool_calls
|
|
157
|
-
: undefined;
|
|
158
|
-
console.log(`[${type}]${name ? ` name=${name}` : ''}`);
|
|
159
|
-
if (toolCalls?.length) {
|
|
160
|
-
for (const tc of toolCalls) {
|
|
161
|
-
console.log(
|
|
162
|
-
` tool_call: ${tc.name}(${JSON.stringify(tc.args).slice(0, 150)})`
|
|
163
|
-
);
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
console.log(` content: ${content}\n`);
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
const subagentMsgs = msgs.filter(
|
|
170
|
-
(m) =>
|
|
171
|
-
m._getType() === 'tool' &&
|
|
172
|
-
(m as { name?: string }).name === Constants.SUBAGENT
|
|
173
|
-
);
|
|
174
|
-
|
|
175
|
-
console.log('--- Verification ---');
|
|
176
|
-
console.log(`subagent tool calls seen (parent): ${subagentMsgs.length}`);
|
|
177
|
-
console.log(
|
|
178
|
-
`ON_TOOL_EXECUTE dispatched (parent saw): ${toolCallCount} (expected >= 1 if subagent used calculator)`
|
|
179
|
-
);
|
|
180
|
-
if (subagentMsgs[0]) {
|
|
181
|
-
console.log(
|
|
182
|
-
`\nsubagent result:\n${(subagentMsgs[0].content as string).slice(0, 600)}`
|
|
183
|
-
);
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
main().catch((err) => {
|
|
188
|
-
console.error('Script error:', err);
|
|
189
|
-
process.exit(1);
|
|
190
|
-
});
|
|
@@ -1,160 +0,0 @@
|
|
|
1
|
-
import { config } from 'dotenv';
|
|
2
|
-
config();
|
|
3
|
-
|
|
4
|
-
import { HumanMessage } from '@langchain/core/messages';
|
|
5
|
-
import type { BaseMessage } from '@langchain/core/messages';
|
|
6
|
-
import { tool } from '@langchain/core/tools';
|
|
7
|
-
import { z } from 'zod';
|
|
8
|
-
import type * as t from '@/types';
|
|
9
|
-
import { ChatModelStreamHandler } from '@/stream';
|
|
10
|
-
import { ToolEndHandler, ModelEndHandler } from '@/events';
|
|
11
|
-
import { Providers, GraphEvents, Constants } from '@/common';
|
|
12
|
-
import { Run } from '@/run';
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Diagnostic: verify a self-spawned subagent can call parent's real tools.
|
|
16
|
-
* Expected before-fix: parent delegates to self; child cannot invoke calculator.
|
|
17
|
-
*/
|
|
18
|
-
const apiKey = process.env.OPENAI_API_KEY!;
|
|
19
|
-
if (!apiKey) {
|
|
20
|
-
console.error('Missing OPENAI_API_KEY');
|
|
21
|
-
process.exit(1);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const calculator = tool(
|
|
25
|
-
async ({ expression }) => {
|
|
26
|
-
const result = eval(expression); // don't do this in prod
|
|
27
|
-
return `${expression} = ${result}`;
|
|
28
|
-
},
|
|
29
|
-
{
|
|
30
|
-
name: 'calculator',
|
|
31
|
-
description: 'Evaluate a math expression. Use for any arithmetic.',
|
|
32
|
-
schema: z.object({
|
|
33
|
-
expression: z.string().describe("A JS math expression, e.g. '42 * 58'"),
|
|
34
|
-
}),
|
|
35
|
-
}
|
|
36
|
-
);
|
|
37
|
-
|
|
38
|
-
async function main() {
|
|
39
|
-
console.log('=== Subagent Tool-Access Diagnostic ===\n');
|
|
40
|
-
|
|
41
|
-
const parentAgent: t.AgentInputs = {
|
|
42
|
-
agentId: 'supervisor',
|
|
43
|
-
provider: Providers.OPENAI,
|
|
44
|
-
clientOptions: { modelName: 'gpt-4o-mini', apiKey },
|
|
45
|
-
instructions: `You have calculator AND can spawn a "self" subagent in an isolated context.
|
|
46
|
-
For any arithmetic question that would bloat your context, spawn the "self" subagent with the math task.
|
|
47
|
-
The subagent must use the calculator tool — never estimate.`,
|
|
48
|
-
maxContextTokens: 8000,
|
|
49
|
-
tools: [calculator],
|
|
50
|
-
subagentConfigs: [
|
|
51
|
-
{
|
|
52
|
-
type: 'self',
|
|
53
|
-
self: true,
|
|
54
|
-
name: 'supervisor',
|
|
55
|
-
description:
|
|
56
|
-
'Spawn a copy of this agent in an isolated context for a focused math subtask.',
|
|
57
|
-
},
|
|
58
|
-
],
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
const customHandlers: Record<string, t.EventHandler> = {
|
|
62
|
-
[GraphEvents.CHAT_MODEL_STREAM]: new ChatModelStreamHandler(),
|
|
63
|
-
[GraphEvents.TOOL_END]: new ToolEndHandler(),
|
|
64
|
-
[GraphEvents.CHAT_MODEL_END]: new ModelEndHandler(),
|
|
65
|
-
[GraphEvents.ON_RUN_STEP]: {
|
|
66
|
-
handle: (event, data): void => {
|
|
67
|
-
console.log(
|
|
68
|
-
`[PARENT EVENT] ${event}`,
|
|
69
|
-
JSON.stringify(data).slice(0, 200)
|
|
70
|
-
);
|
|
71
|
-
},
|
|
72
|
-
},
|
|
73
|
-
[GraphEvents.ON_RUN_STEP_COMPLETED]: {
|
|
74
|
-
handle: (event, data): void => {
|
|
75
|
-
console.log(
|
|
76
|
-
`[PARENT EVENT] ${event}`,
|
|
77
|
-
JSON.stringify(data).slice(0, 200)
|
|
78
|
-
);
|
|
79
|
-
},
|
|
80
|
-
},
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
const run = await Run.create<t.IState>({
|
|
84
|
-
runId: `subagent-debug-${Date.now()}`,
|
|
85
|
-
graphConfig: { type: 'standard', agents: [parentAgent] },
|
|
86
|
-
customHandlers,
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
const question = new HumanMessage(
|
|
90
|
-
'Compute (42 * 58) + (13 ^ 3). Spawn the self subagent to do this, and have IT use calculator.'
|
|
91
|
-
);
|
|
92
|
-
|
|
93
|
-
console.log('User:', question.content, '\n');
|
|
94
|
-
|
|
95
|
-
await run.processStream(
|
|
96
|
-
{ messages: [question] },
|
|
97
|
-
{
|
|
98
|
-
configurable: { thread_id: `subagent-debug` },
|
|
99
|
-
version: 'v2' as const,
|
|
100
|
-
}
|
|
101
|
-
);
|
|
102
|
-
|
|
103
|
-
const msgs = (run.getRunMessages() ?? []) as BaseMessage[];
|
|
104
|
-
console.log('\n--- Run messages ---\n');
|
|
105
|
-
for (const msg of msgs) {
|
|
106
|
-
const type = msg._getType();
|
|
107
|
-
const name = 'name' in msg ? (msg as { name?: string }).name : undefined;
|
|
108
|
-
const content =
|
|
109
|
-
typeof msg.content === 'string'
|
|
110
|
-
? msg.content.slice(0, 400)
|
|
111
|
-
: JSON.stringify(msg.content).slice(0, 400);
|
|
112
|
-
const toolCalls =
|
|
113
|
-
'tool_calls' in msg
|
|
114
|
-
? (msg as { tool_calls?: Array<{ name: string; args: unknown }> })
|
|
115
|
-
.tool_calls
|
|
116
|
-
: undefined;
|
|
117
|
-
console.log(`[${type}]${name ? ` name=${name}` : ''}`);
|
|
118
|
-
if (toolCalls?.length) {
|
|
119
|
-
for (const tc of toolCalls) {
|
|
120
|
-
console.log(
|
|
121
|
-
` tool_call: ${tc.name}(${JSON.stringify(tc.args).slice(0, 150)})`
|
|
122
|
-
);
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
console.log(` content: ${content}\n`);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
const subagentCalls = msgs.filter(
|
|
129
|
-
(m) =>
|
|
130
|
-
m._getType() === 'tool' &&
|
|
131
|
-
'name' in m &&
|
|
132
|
-
(m as { name?: string }).name === Constants.SUBAGENT
|
|
133
|
-
);
|
|
134
|
-
const calculatorCalls = msgs.filter(
|
|
135
|
-
(m) =>
|
|
136
|
-
m._getType() === 'tool' &&
|
|
137
|
-
'name' in m &&
|
|
138
|
-
(m as { name?: string }).name === 'calculator'
|
|
139
|
-
);
|
|
140
|
-
|
|
141
|
-
console.log('--- Verification ---');
|
|
142
|
-
console.log(`subagent tool calls seen (parent): ${subagentCalls.length}`);
|
|
143
|
-
console.log(
|
|
144
|
-
`calculator tool calls seen (parent): ${calculatorCalls.length} (expected: 0 if subagent did the math)`
|
|
145
|
-
);
|
|
146
|
-
if (subagentCalls.length > 0) {
|
|
147
|
-
const subResult = subagentCalls[0].content as string;
|
|
148
|
-
console.log(`\nsubagent result snippet:\n${subResult.slice(0, 600)}\n`);
|
|
149
|
-
if (/\berror\b/i.test(subResult) && /tool/i.test(subResult)) {
|
|
150
|
-
console.log('⚠️ BUG CONFIRMED: subagent result mentions tool error');
|
|
151
|
-
} else if (!/\d/.test(subResult)) {
|
|
152
|
-
console.log('⚠️ POSSIBLY BUGGY: subagent result has no numbers');
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
main().catch((err) => {
|
|
158
|
-
console.error('Script error:', err);
|
|
159
|
-
process.exit(1);
|
|
160
|
-
});
|
|
@@ -1,305 +0,0 @@
|
|
|
1
|
-
import { HumanMessage } from '@langchain/core/messages';
|
|
2
|
-
import { FakeListChatModel } from '@langchain/core/utils/testing';
|
|
3
|
-
import type { ToolCall } from '@langchain/core/messages/tool';
|
|
4
|
-
import type { RunnableConfig } from '@langchain/core/runnables';
|
|
5
|
-
import type * as t from '@/types';
|
|
6
|
-
import { Run } from '@/run';
|
|
7
|
-
import {
|
|
8
|
-
Constants,
|
|
9
|
-
GraphEvents,
|
|
10
|
-
Providers,
|
|
11
|
-
ToolEndHandler,
|
|
12
|
-
ModelEndHandler,
|
|
13
|
-
StandardGraph,
|
|
14
|
-
} from '@/index';
|
|
15
|
-
import * as providers from '@/llm/providers';
|
|
16
|
-
|
|
17
|
-
const CHILD_RESPONSE = 'Research result: Paris is the capital of France.';
|
|
18
|
-
|
|
19
|
-
const callerConfig: Partial<RunnableConfig> & {
|
|
20
|
-
version: 'v1' | 'v2';
|
|
21
|
-
streamMode: string;
|
|
22
|
-
} = {
|
|
23
|
-
configurable: { thread_id: 'subagent-test-thread' },
|
|
24
|
-
streamMode: 'values',
|
|
25
|
-
version: 'v2' as const,
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
const createParentAgent = (): t.AgentInputs => ({
|
|
29
|
-
agentId: 'parent',
|
|
30
|
-
provider: Providers.OPENAI,
|
|
31
|
-
clientOptions: { modelName: 'gpt-4o-mini', apiKey: 'test-key' },
|
|
32
|
-
instructions:
|
|
33
|
-
'You are a supervisor. Delegate research tasks using the subagent tool.',
|
|
34
|
-
maxContextTokens: 8000,
|
|
35
|
-
subagentConfigs: [
|
|
36
|
-
{
|
|
37
|
-
type: 'researcher',
|
|
38
|
-
name: 'Research Agent',
|
|
39
|
-
description: 'Researches and summarizes information',
|
|
40
|
-
agentInputs: {
|
|
41
|
-
agentId: 'researcher',
|
|
42
|
-
provider: Providers.OPENAI,
|
|
43
|
-
clientOptions: { modelName: 'gpt-4o-mini', apiKey: 'test-key' },
|
|
44
|
-
instructions: 'You are a research agent. Answer concisely.',
|
|
45
|
-
maxContextTokens: 8000,
|
|
46
|
-
},
|
|
47
|
-
},
|
|
48
|
-
],
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
describe('Subagent Integration', () => {
|
|
52
|
-
jest.setTimeout(30000);
|
|
53
|
-
|
|
54
|
-
let getChatModelClassSpy: jest.SpyInstance;
|
|
55
|
-
const originalGetChatModelClass = providers.getChatModelClass;
|
|
56
|
-
|
|
57
|
-
beforeEach(() => {
|
|
58
|
-
getChatModelClassSpy = jest
|
|
59
|
-
.spyOn(providers, 'getChatModelClass')
|
|
60
|
-
.mockImplementation(((provider: Providers) => {
|
|
61
|
-
if (provider === Providers.OPENAI) {
|
|
62
|
-
return class extends FakeListChatModel {
|
|
63
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
64
|
-
constructor(_options: any) {
|
|
65
|
-
super({ responses: [CHILD_RESPONSE] });
|
|
66
|
-
}
|
|
67
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
68
|
-
} as any;
|
|
69
|
-
}
|
|
70
|
-
return originalGetChatModelClass(provider);
|
|
71
|
-
}) as typeof providers.getChatModelClass);
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
afterEach(() => {
|
|
75
|
-
getChatModelClassSpy.mockRestore();
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it('should create subagent tool on agent context', async () => {
|
|
79
|
-
const run = await Run.create<t.IState>({
|
|
80
|
-
runId: `subagent-test-${Date.now()}`,
|
|
81
|
-
graphConfig: {
|
|
82
|
-
type: 'standard',
|
|
83
|
-
agents: [createParentAgent()],
|
|
84
|
-
},
|
|
85
|
-
returnContent: true,
|
|
86
|
-
skipCleanup: true,
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
expect(run.Graph).toBeDefined();
|
|
90
|
-
const parentContext = (run.Graph as StandardGraph).agentContexts.get(
|
|
91
|
-
'parent'
|
|
92
|
-
);
|
|
93
|
-
expect(parentContext).toBeDefined();
|
|
94
|
-
expect(parentContext?.graphTools).toBeDefined();
|
|
95
|
-
|
|
96
|
-
const subagentTool = (parentContext?.graphTools as t.GenericTool[]).find(
|
|
97
|
-
(t) => 'name' in t && t.name === Constants.SUBAGENT
|
|
98
|
-
);
|
|
99
|
-
expect(subagentTool).toBeDefined();
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
it('should execute subagent and return filtered result to parent', async () => {
|
|
103
|
-
const customHandlers: Record<string, t.EventHandler> = {
|
|
104
|
-
[GraphEvents.TOOL_END]: new ToolEndHandler(),
|
|
105
|
-
[GraphEvents.CHAT_MODEL_END]: new ModelEndHandler(),
|
|
106
|
-
};
|
|
107
|
-
|
|
108
|
-
const run = await Run.create<t.IState>({
|
|
109
|
-
runId: `subagent-exec-${Date.now()}`,
|
|
110
|
-
graphConfig: {
|
|
111
|
-
type: 'standard',
|
|
112
|
-
agents: [createParentAgent()],
|
|
113
|
-
},
|
|
114
|
-
returnContent: true,
|
|
115
|
-
skipCleanup: true,
|
|
116
|
-
customHandlers,
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
const subagentToolCall: ToolCall = {
|
|
120
|
-
id: 'call_subagent_1',
|
|
121
|
-
name: Constants.SUBAGENT,
|
|
122
|
-
args: {
|
|
123
|
-
description: 'What is the capital of France?',
|
|
124
|
-
subagent_type: 'researcher',
|
|
125
|
-
},
|
|
126
|
-
type: 'tool_call',
|
|
127
|
-
};
|
|
128
|
-
|
|
129
|
-
run.Graph?.overrideTestModel(
|
|
130
|
-
[
|
|
131
|
-
'Let me delegate this research task.',
|
|
132
|
-
`Based on the research: ${CHILD_RESPONSE}`,
|
|
133
|
-
],
|
|
134
|
-
10,
|
|
135
|
-
[subagentToolCall]
|
|
136
|
-
);
|
|
137
|
-
|
|
138
|
-
const result = await run.processStream(
|
|
139
|
-
{ messages: [new HumanMessage('What is the capital of France?')] },
|
|
140
|
-
callerConfig
|
|
141
|
-
);
|
|
142
|
-
|
|
143
|
-
expect(result).toBeDefined();
|
|
144
|
-
|
|
145
|
-
const runMessages = run.getRunMessages();
|
|
146
|
-
expect(runMessages).toBeDefined();
|
|
147
|
-
expect(runMessages!.length).toBeGreaterThan(0);
|
|
148
|
-
|
|
149
|
-
const toolMessages = runMessages!.filter(
|
|
150
|
-
(msg) => msg._getType() === 'tool'
|
|
151
|
-
);
|
|
152
|
-
const subagentResult = toolMessages.find(
|
|
153
|
-
(msg) => 'name' in msg && msg.name === Constants.SUBAGENT
|
|
154
|
-
);
|
|
155
|
-
expect(subagentResult).toBeDefined();
|
|
156
|
-
expect(String(subagentResult!.content)).toContain('Paris');
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
it('should not create subagent tool when no subagentConfigs', async () => {
|
|
160
|
-
const agentWithoutSubagents: t.AgentInputs = {
|
|
161
|
-
agentId: 'plain',
|
|
162
|
-
provider: Providers.OPENAI,
|
|
163
|
-
clientOptions: { modelName: 'gpt-4o-mini', apiKey: 'test-key' },
|
|
164
|
-
instructions: 'Plain agent without subagents.',
|
|
165
|
-
maxContextTokens: 8000,
|
|
166
|
-
};
|
|
167
|
-
|
|
168
|
-
const run = await Run.create<t.IState>({
|
|
169
|
-
runId: `no-subagent-${Date.now()}`,
|
|
170
|
-
graphConfig: {
|
|
171
|
-
type: 'standard',
|
|
172
|
-
agents: [agentWithoutSubagents],
|
|
173
|
-
},
|
|
174
|
-
returnContent: true,
|
|
175
|
-
skipCleanup: true,
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
const context = (run.Graph as StandardGraph).agentContexts.get('plain');
|
|
179
|
-
const tools = context?.graphTools as t.GenericTool[] | undefined;
|
|
180
|
-
const subagentTool = tools?.find(
|
|
181
|
-
(t) => 'name' in t && t.name === Constants.SUBAGENT
|
|
182
|
-
);
|
|
183
|
-
expect(subagentTool).toBeUndefined();
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
it('should handle self-spawn subagent config', async () => {
|
|
187
|
-
const agentWithSelfSpawn: t.AgentInputs = {
|
|
188
|
-
agentId: 'self-parent',
|
|
189
|
-
provider: Providers.OPENAI,
|
|
190
|
-
clientOptions: { modelName: 'gpt-4o-mini', apiKey: 'test-key' },
|
|
191
|
-
instructions: 'Agent with self-spawn for context isolation.',
|
|
192
|
-
maxContextTokens: 8000,
|
|
193
|
-
subagentConfigs: [
|
|
194
|
-
{
|
|
195
|
-
type: 'isolated',
|
|
196
|
-
name: 'Isolated Worker',
|
|
197
|
-
description: 'Runs a task with isolated context',
|
|
198
|
-
self: true,
|
|
199
|
-
},
|
|
200
|
-
],
|
|
201
|
-
};
|
|
202
|
-
|
|
203
|
-
const run = await Run.create<t.IState>({
|
|
204
|
-
runId: `self-spawn-${Date.now()}`,
|
|
205
|
-
graphConfig: {
|
|
206
|
-
type: 'standard',
|
|
207
|
-
agents: [agentWithSelfSpawn],
|
|
208
|
-
},
|
|
209
|
-
returnContent: true,
|
|
210
|
-
skipCleanup: true,
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
const context = (run.Graph as StandardGraph).agentContexts.get(
|
|
214
|
-
'self-parent'
|
|
215
|
-
);
|
|
216
|
-
const tools = context?.graphTools as t.GenericTool[] | undefined;
|
|
217
|
-
const subagentTool = tools?.find(
|
|
218
|
-
(t) => 'name' in t && t.name === Constants.SUBAGENT
|
|
219
|
-
);
|
|
220
|
-
expect(subagentTool).toBeDefined();
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
it('should not create subagent tool when maxSubagentDepth is 0', async () => {
|
|
224
|
-
const agentWithZeroDepth: t.AgentInputs = {
|
|
225
|
-
...createParentAgent(),
|
|
226
|
-
agentId: 'zero-depth',
|
|
227
|
-
maxSubagentDepth: 0,
|
|
228
|
-
};
|
|
229
|
-
|
|
230
|
-
const run = await Run.create<t.IState>({
|
|
231
|
-
runId: `zero-depth-${Date.now()}`,
|
|
232
|
-
graphConfig: {
|
|
233
|
-
type: 'standard',
|
|
234
|
-
agents: [agentWithZeroDepth],
|
|
235
|
-
},
|
|
236
|
-
returnContent: true,
|
|
237
|
-
skipCleanup: true,
|
|
238
|
-
});
|
|
239
|
-
|
|
240
|
-
const context = (run.Graph as StandardGraph).agentContexts.get(
|
|
241
|
-
'zero-depth'
|
|
242
|
-
);
|
|
243
|
-
const tools = context?.graphTools as t.GenericTool[] | undefined;
|
|
244
|
-
const subagentTool = tools?.find(
|
|
245
|
-
(t) => 'name' in t && t.name === Constants.SUBAGENT
|
|
246
|
-
);
|
|
247
|
-
expect(subagentTool).toBeUndefined();
|
|
248
|
-
});
|
|
249
|
-
|
|
250
|
-
it('should account for subagent tool schema in toolSchemaTokens', async () => {
|
|
251
|
-
/** Simple char-count tokenizer — deterministic, lets us assert presence. */
|
|
252
|
-
const tokenCounter: t.TokenCounter = (message) => {
|
|
253
|
-
const content = message.content;
|
|
254
|
-
if (typeof content === 'string') return content.length;
|
|
255
|
-
if (Array.isArray(content)) return JSON.stringify(content).length;
|
|
256
|
-
return JSON.stringify(content).length;
|
|
257
|
-
};
|
|
258
|
-
|
|
259
|
-
const agentWithSubagent = createParentAgent();
|
|
260
|
-
const runWith = await Run.create<t.IState>({
|
|
261
|
-
runId: `with-sub-${Date.now()}`,
|
|
262
|
-
graphConfig: {
|
|
263
|
-
type: 'standard',
|
|
264
|
-
agents: [agentWithSubagent],
|
|
265
|
-
},
|
|
266
|
-
tokenCounter,
|
|
267
|
-
returnContent: true,
|
|
268
|
-
skipCleanup: true,
|
|
269
|
-
});
|
|
270
|
-
|
|
271
|
-
const agentWithoutSubagent: t.AgentInputs = {
|
|
272
|
-
agentId: 'plain',
|
|
273
|
-
provider: Providers.OPENAI,
|
|
274
|
-
clientOptions: { modelName: 'gpt-4o-mini', apiKey: 'test-key' },
|
|
275
|
-
instructions:
|
|
276
|
-
'You are a supervisor. Delegate research tasks using the subagent tool.',
|
|
277
|
-
maxContextTokens: 8000,
|
|
278
|
-
};
|
|
279
|
-
const runWithout = await Run.create<t.IState>({
|
|
280
|
-
runId: `without-sub-${Date.now()}`,
|
|
281
|
-
graphConfig: {
|
|
282
|
-
type: 'standard',
|
|
283
|
-
agents: [agentWithoutSubagent],
|
|
284
|
-
},
|
|
285
|
-
tokenCounter,
|
|
286
|
-
returnContent: true,
|
|
287
|
-
skipCleanup: true,
|
|
288
|
-
});
|
|
289
|
-
|
|
290
|
-
const contextWith = (runWith.Graph as StandardGraph).agentContexts.get(
|
|
291
|
-
'parent'
|
|
292
|
-
);
|
|
293
|
-
const contextWithout = (
|
|
294
|
-
runWithout.Graph as StandardGraph
|
|
295
|
-
).agentContexts.get('plain');
|
|
296
|
-
|
|
297
|
-
await contextWith?.tokenCalculationPromise;
|
|
298
|
-
await contextWithout?.tokenCalculationPromise;
|
|
299
|
-
|
|
300
|
-
/** Subagent tool schema is ~600 chars; expect measurable difference. */
|
|
301
|
-
expect(contextWith!.toolSchemaTokens).toBeGreaterThan(
|
|
302
|
-
contextWithout!.toolSchemaTokens
|
|
303
|
-
);
|
|
304
|
-
});
|
|
305
|
-
});
|