@librechat/agents 3.1.67 → 3.1.68-dev.1
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 +23 -3
- package/dist/cjs/agents/AgentContext.cjs.map +1 -1
- package/dist/cjs/common/enum.cjs +16 -1
- package/dist/cjs/common/enum.cjs.map +1 -1
- package/dist/cjs/graphs/Graph.cjs +91 -0
- 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/hooks/HookRegistry.cjs +162 -0
- package/dist/cjs/hooks/HookRegistry.cjs.map +1 -0
- package/dist/cjs/hooks/executeHooks.cjs +276 -0
- package/dist/cjs/hooks/executeHooks.cjs.map +1 -0
- package/dist/cjs/hooks/matchers.cjs +256 -0
- package/dist/cjs/hooks/matchers.cjs.map +1 -0
- package/dist/cjs/hooks/types.cjs +27 -0
- package/dist/cjs/hooks/types.cjs.map +1 -0
- package/dist/cjs/main.cjs +54 -0
- package/dist/cjs/main.cjs.map +1 -1
- package/dist/cjs/messages/format.cjs +74 -12
- package/dist/cjs/messages/format.cjs.map +1 -1
- package/dist/cjs/run.cjs +111 -0
- 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 +165 -19
- package/dist/cjs/summarization/node.cjs.map +1 -1
- package/dist/cjs/tools/BashExecutor.cjs +165 -0
- package/dist/cjs/tools/BashExecutor.cjs.map +1 -0
- package/dist/cjs/tools/BashProgrammaticToolCalling.cjs +287 -0
- package/dist/cjs/tools/BashProgrammaticToolCalling.cjs.map +1 -0
- package/dist/cjs/tools/CodeExecutor.cjs +0 -9
- package/dist/cjs/tools/CodeExecutor.cjs.map +1 -1
- package/dist/cjs/tools/ProgrammaticToolCalling.cjs +7 -23
- package/dist/cjs/tools/ProgrammaticToolCalling.cjs.map +1 -1
- package/dist/cjs/tools/ReadFile.cjs +43 -0
- package/dist/cjs/tools/ReadFile.cjs.map +1 -0
- package/dist/cjs/tools/SkillTool.cjs +50 -0
- package/dist/cjs/tools/SkillTool.cjs.map +1 -0
- package/dist/cjs/tools/SubagentTool.cjs +92 -0
- package/dist/cjs/tools/SubagentTool.cjs.map +1 -0
- package/dist/cjs/tools/ToolNode.cjs +304 -140
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/cjs/tools/ToolSearch.cjs +2 -13
- package/dist/cjs/tools/ToolSearch.cjs.map +1 -1
- package/dist/cjs/tools/skillCatalog.cjs +84 -0
- package/dist/cjs/tools/skillCatalog.cjs.map +1 -0
- package/dist/cjs/tools/subagent/SubagentExecutor.cjs +511 -0
- package/dist/cjs/tools/subagent/SubagentExecutor.cjs.map +1 -0
- package/dist/esm/agents/AgentContext.mjs +23 -3
- package/dist/esm/agents/AgentContext.mjs.map +1 -1
- package/dist/esm/common/enum.mjs +15 -2
- package/dist/esm/common/enum.mjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +91 -0
- 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/hooks/HookRegistry.mjs +160 -0
- package/dist/esm/hooks/HookRegistry.mjs.map +1 -0
- package/dist/esm/hooks/executeHooks.mjs +273 -0
- package/dist/esm/hooks/executeHooks.mjs.map +1 -0
- package/dist/esm/hooks/matchers.mjs +251 -0
- package/dist/esm/hooks/matchers.mjs.map +1 -0
- package/dist/esm/hooks/types.mjs +25 -0
- package/dist/esm/hooks/types.mjs.map +1 -0
- package/dist/esm/main.mjs +13 -2
- package/dist/esm/main.mjs.map +1 -1
- package/dist/esm/messages/format.mjs +66 -4
- package/dist/esm/messages/format.mjs.map +1 -1
- package/dist/esm/run.mjs +111 -0
- 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 +165 -19
- package/dist/esm/summarization/node.mjs.map +1 -1
- package/dist/esm/tools/BashExecutor.mjs +159 -0
- package/dist/esm/tools/BashExecutor.mjs.map +1 -0
- package/dist/esm/tools/BashProgrammaticToolCalling.mjs +278 -0
- package/dist/esm/tools/BashProgrammaticToolCalling.mjs.map +1 -0
- package/dist/esm/tools/CodeExecutor.mjs +0 -9
- package/dist/esm/tools/CodeExecutor.mjs.map +1 -1
- package/dist/esm/tools/ProgrammaticToolCalling.mjs +8 -24
- package/dist/esm/tools/ProgrammaticToolCalling.mjs.map +1 -1
- package/dist/esm/tools/ReadFile.mjs +38 -0
- package/dist/esm/tools/ReadFile.mjs.map +1 -0
- package/dist/esm/tools/SkillTool.mjs +45 -0
- package/dist/esm/tools/SkillTool.mjs.map +1 -0
- package/dist/esm/tools/SubagentTool.mjs +85 -0
- package/dist/esm/tools/SubagentTool.mjs.map +1 -0
- package/dist/esm/tools/ToolNode.mjs +306 -142
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/esm/tools/ToolSearch.mjs +3 -14
- package/dist/esm/tools/ToolSearch.mjs.map +1 -1
- package/dist/esm/tools/skillCatalog.mjs +82 -0
- package/dist/esm/tools/skillCatalog.mjs.map +1 -0
- package/dist/esm/tools/subagent/SubagentExecutor.mjs +505 -0
- package/dist/esm/tools/subagent/SubagentExecutor.mjs.map +1 -0
- package/dist/types/agents/AgentContext.d.ts +6 -0
- package/dist/types/common/enum.d.ts +10 -2
- package/dist/types/graphs/Graph.d.ts +2 -0
- package/dist/types/graphs/MultiAgentGraph.d.ts +12 -0
- package/dist/types/hooks/HookRegistry.d.ts +56 -0
- package/dist/types/hooks/executeHooks.d.ts +79 -0
- package/dist/types/hooks/index.d.ts +6 -0
- package/dist/types/hooks/matchers.d.ts +95 -0
- package/dist/types/hooks/types.d.ts +320 -0
- package/dist/types/index.d.ts +8 -0
- package/dist/types/messages/format.d.ts +2 -1
- package/dist/types/run.d.ts +1 -0
- package/dist/types/summarization/index.d.ts +2 -0
- package/dist/types/summarization/node.d.ts +2 -0
- package/dist/types/tools/BashExecutor.d.ts +45 -0
- package/dist/types/tools/BashProgrammaticToolCalling.d.ts +72 -0
- package/dist/types/tools/ProgrammaticToolCalling.d.ts +4 -9
- package/dist/types/tools/ReadFile.d.ts +28 -0
- package/dist/types/tools/SkillTool.d.ts +40 -0
- package/dist/types/tools/SubagentTool.d.ts +36 -0
- package/dist/types/tools/ToolNode.d.ts +24 -2
- package/dist/types/tools/ToolSearch.d.ts +2 -2
- package/dist/types/tools/skillCatalog.d.ts +19 -0
- package/dist/types/tools/subagent/SubagentExecutor.d.ts +137 -0
- package/dist/types/tools/subagent/index.d.ts +2 -0
- package/dist/types/types/graph.d.ts +61 -2
- package/dist/types/types/index.d.ts +1 -0
- package/dist/types/types/run.d.ts +20 -0
- package/dist/types/types/skill.d.ts +9 -0
- package/dist/types/types/tools.d.ts +38 -10
- package/package.json +5 -1
- package/src/agents/AgentContext.ts +26 -2
- package/src/common/enum.ts +15 -1
- package/src/graphs/Graph.ts +113 -0
- package/src/graphs/MultiAgentGraph.ts +39 -0
- package/src/graphs/__tests__/MultiAgentGraph.test.ts +91 -0
- package/src/hooks/HookRegistry.ts +208 -0
- package/src/hooks/__tests__/HookRegistry.test.ts +190 -0
- package/src/hooks/__tests__/compactHooks.test.ts +214 -0
- package/src/hooks/__tests__/executeHooks.test.ts +1013 -0
- package/src/hooks/__tests__/integration.test.ts +337 -0
- package/src/hooks/__tests__/matchers.test.ts +238 -0
- package/src/hooks/__tests__/toolHooks.test.ts +669 -0
- package/src/hooks/executeHooks.ts +375 -0
- package/src/hooks/index.ts +57 -0
- package/src/hooks/matchers.ts +280 -0
- package/src/hooks/types.ts +404 -0
- package/src/index.ts +10 -0
- package/src/messages/format.ts +74 -4
- package/src/messages/formatAgentMessages.skills.test.ts +334 -0
- package/src/run.ts +126 -0
- package/src/scripts/multi-agent-subagent.ts +246 -0
- package/src/scripts/programmatic_exec.ts +1 -10
- package/src/scripts/subagent-event-driven-debug.ts +190 -0
- package/src/scripts/subagent-tools-debug.ts +160 -0
- package/src/scripts/test_code_api.ts +0 -7
- package/src/scripts/tool_search.ts +1 -10
- package/src/specs/subagent.test.ts +305 -0
- 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 +202 -24
- package/src/tools/BashExecutor.ts +193 -0
- package/src/tools/BashProgrammaticToolCalling.ts +381 -0
- package/src/tools/CodeExecutor.ts +0 -11
- package/src/tools/ProgrammaticToolCalling.ts +4 -29
- package/src/tools/ReadFile.ts +39 -0
- package/src/tools/SkillTool.ts +46 -0
- package/src/tools/SubagentTool.ts +100 -0
- package/src/tools/ToolNode.ts +391 -169
- package/src/tools/ToolSearch.ts +3 -19
- package/src/tools/__tests__/ProgrammaticToolCalling.integration.test.ts +7 -8
- package/src/tools/__tests__/ProgrammaticToolCalling.test.ts +0 -1
- package/src/tools/__tests__/ReadFile.test.ts +44 -0
- package/src/tools/__tests__/SkillTool.test.ts +442 -0
- package/src/tools/__tests__/SubagentExecutor.test.ts +1148 -0
- package/src/tools/__tests__/SubagentTool.test.ts +149 -0
- package/src/tools/__tests__/ToolNode.session.test.ts +12 -12
- package/src/tools/__tests__/ToolSearch.integration.test.ts +7 -8
- package/src/tools/__tests__/skillCatalog.test.ts +161 -0
- package/src/tools/__tests__/subagentHooks.test.ts +215 -0
- package/src/tools/skillCatalog.ts +126 -0
- package/src/tools/subagent/SubagentExecutor.ts +676 -0
- package/src/tools/subagent/index.ts +13 -0
- package/src/types/graph.ts +80 -1
- package/src/types/index.ts +1 -0
- package/src/types/run.ts +20 -0
- package/src/types/skill.ts +11 -0
- package/src/types/tools.ts +41 -10
|
@@ -0,0 +1,246 @@
|
|
|
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, createContentAggregator } from '@/stream';
|
|
8
|
+
import { ToolEndHandler, ModelEndHandler } from '@/events';
|
|
9
|
+
import { Providers, GraphEvents, Constants } from '@/common';
|
|
10
|
+
import { Run } from '@/run';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Manual verification script for the subagent primitive.
|
|
14
|
+
*
|
|
15
|
+
* Configures a supervisor agent with two subagent types (researcher, coder),
|
|
16
|
+
* sends a query, and confirms:
|
|
17
|
+
* 1. The parent agent delegates to a subagent via the `subagent` tool
|
|
18
|
+
* 2. The child executes with isolated context (fresh message history)
|
|
19
|
+
* 3. Only the filtered text result returns to the parent
|
|
20
|
+
* 4. The parent incorporates the result and responds
|
|
21
|
+
*
|
|
22
|
+
* Usage:
|
|
23
|
+
* OPENAI_API_KEY=... npx ts-node -r tsconfig-paths/register src/scripts/multi-agent-subagent.ts
|
|
24
|
+
*
|
|
25
|
+
* Or with Anthropic:
|
|
26
|
+
* ANTHROPIC_API_KEY=... npx ts-node -r tsconfig-paths/register src/scripts/multi-agent-subagent.ts --provider anthropic
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
const useAnthropic =
|
|
30
|
+
process.argv.includes('--provider') &&
|
|
31
|
+
process.argv[process.argv.indexOf('--provider') + 1] === 'anthropic';
|
|
32
|
+
|
|
33
|
+
const provider = useAnthropic ? Providers.ANTHROPIC : Providers.OPENAI;
|
|
34
|
+
const apiKey = useAnthropic
|
|
35
|
+
? process.env.ANTHROPIC_API_KEY
|
|
36
|
+
: process.env.OPENAI_API_KEY;
|
|
37
|
+
const modelName = useAnthropic ? 'claude-sonnet-4-20250514' : 'gpt-5.4';
|
|
38
|
+
|
|
39
|
+
if (!apiKey) {
|
|
40
|
+
console.error(
|
|
41
|
+
`Missing ${useAnthropic ? 'ANTHROPIC_API_KEY' : 'OPENAI_API_KEY'} environment variable`
|
|
42
|
+
);
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async function testSubagentPrimitive() {
|
|
47
|
+
console.log('=== Subagent Primitive Manual Verification ===\n');
|
|
48
|
+
console.log(`Provider: ${provider}`);
|
|
49
|
+
console.log(`Model: ${modelName}\n`);
|
|
50
|
+
|
|
51
|
+
const { aggregateContent } = createContentAggregator();
|
|
52
|
+
|
|
53
|
+
const parentAgent: t.AgentInputs = {
|
|
54
|
+
agentId: 'supervisor',
|
|
55
|
+
provider,
|
|
56
|
+
clientOptions: { modelName, apiKey },
|
|
57
|
+
instructions: `You are a supervisor agent. You have access to specialized subagents.
|
|
58
|
+
|
|
59
|
+
When the user asks a research question, delegate it to the "researcher" subagent.
|
|
60
|
+
When the user asks for code, delegate it to the "coder" subagent.
|
|
61
|
+
|
|
62
|
+
After receiving the subagent's result, synthesize it into a clear final answer for the user.
|
|
63
|
+
Always use a subagent for research or coding tasks — do not answer directly.`,
|
|
64
|
+
maxContextTokens: 16000,
|
|
65
|
+
subagentConfigs: [
|
|
66
|
+
{
|
|
67
|
+
type: 'researcher',
|
|
68
|
+
name: 'Research Specialist',
|
|
69
|
+
description:
|
|
70
|
+
'Researches topics and provides detailed summaries with sources.',
|
|
71
|
+
agentInputs: {
|
|
72
|
+
agentId: 'researcher',
|
|
73
|
+
provider,
|
|
74
|
+
clientOptions: { modelName, apiKey },
|
|
75
|
+
instructions: `You are a research specialist working in an isolated context.
|
|
76
|
+
You receive a single task description and must answer it thoroughly.
|
|
77
|
+
Be concise but comprehensive. Include key facts and details.`,
|
|
78
|
+
maxContextTokens: 8000,
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
type: 'coder',
|
|
83
|
+
name: 'Coding Specialist',
|
|
84
|
+
description:
|
|
85
|
+
'Writes, reviews, and explains code in any programming language.',
|
|
86
|
+
agentInputs: {
|
|
87
|
+
agentId: 'coder',
|
|
88
|
+
provider,
|
|
89
|
+
clientOptions: { modelName, apiKey },
|
|
90
|
+
instructions: `You are a coding specialist working in an isolated context.
|
|
91
|
+
You receive a single task description and must provide working code.
|
|
92
|
+
Include brief explanations. Use clean, idiomatic code.`,
|
|
93
|
+
maxContextTokens: 8000,
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
],
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const customHandlers: Record<string, t.EventHandler> = {
|
|
100
|
+
[GraphEvents.CHAT_MODEL_STREAM]: new ChatModelStreamHandler(),
|
|
101
|
+
[GraphEvents.TOOL_END]: new ToolEndHandler(),
|
|
102
|
+
[GraphEvents.CHAT_MODEL_END]: new ModelEndHandler(),
|
|
103
|
+
[GraphEvents.ON_RUN_STEP_COMPLETED]: {
|
|
104
|
+
handle: (event: string, data: t.StreamEventData): void => {
|
|
105
|
+
aggregateContent({
|
|
106
|
+
event: event as GraphEvents,
|
|
107
|
+
data: data as t.RunStep,
|
|
108
|
+
});
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
[GraphEvents.ON_RUN_STEP]: {
|
|
112
|
+
handle: (event: string, data: t.StreamEventData): void => {
|
|
113
|
+
aggregateContent({
|
|
114
|
+
event: event as GraphEvents,
|
|
115
|
+
data: data as t.RunStep,
|
|
116
|
+
});
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
[GraphEvents.ON_RUN_STEP_DELTA]: {
|
|
120
|
+
handle: (event: string, data: t.StreamEventData): void => {
|
|
121
|
+
aggregateContent({
|
|
122
|
+
event: event as GraphEvents,
|
|
123
|
+
data: data as t.RunStepDeltaEvent,
|
|
124
|
+
});
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
[GraphEvents.ON_MESSAGE_DELTA]: {
|
|
128
|
+
handle: (event: string, data: t.StreamEventData): void => {
|
|
129
|
+
aggregateContent({
|
|
130
|
+
event: event as GraphEvents,
|
|
131
|
+
data: data as t.MessageDeltaEvent,
|
|
132
|
+
});
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
const run = await Run.create<t.IState>({
|
|
138
|
+
runId: `subagent-manual-${Date.now()}`,
|
|
139
|
+
graphConfig: {
|
|
140
|
+
type: 'standard',
|
|
141
|
+
agents: [parentAgent],
|
|
142
|
+
},
|
|
143
|
+
returnContent: true,
|
|
144
|
+
customHandlers,
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
console.log('--- Run created ---');
|
|
148
|
+
console.log(
|
|
149
|
+
`Subagent tool present: ${
|
|
150
|
+
(
|
|
151
|
+
(run.Graph as import('@/graphs/Graph').StandardGraph).agentContexts.get(
|
|
152
|
+
'supervisor'
|
|
153
|
+
)?.graphTools as t.GenericTool[] | undefined
|
|
154
|
+
)?.some((t) => 'name' in t && t.name === Constants.SUBAGENT) ?? false
|
|
155
|
+
}\n`
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
const conversationHistory: BaseMessage[] = [];
|
|
159
|
+
|
|
160
|
+
// Turn 1: Research question (should delegate to researcher subagent)
|
|
161
|
+
console.log('=== Turn 1: Research Question ===\n');
|
|
162
|
+
console.log(
|
|
163
|
+
'User: What are the three laws of thermodynamics? Explain briefly.\n'
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
const userMessage = new HumanMessage(
|
|
167
|
+
'What are the three laws of thermodynamics? Explain briefly.'
|
|
168
|
+
);
|
|
169
|
+
conversationHistory.push(userMessage);
|
|
170
|
+
|
|
171
|
+
const callerConfig = {
|
|
172
|
+
configurable: { thread_id: 'subagent-verify' },
|
|
173
|
+
streamMode: 'values' as const,
|
|
174
|
+
version: 'v2' as const,
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
console.log('--- Streaming response ---\n');
|
|
178
|
+
const result = await run.processStream(
|
|
179
|
+
{ messages: conversationHistory },
|
|
180
|
+
callerConfig
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
const runMessages = run.getRunMessages();
|
|
184
|
+
console.log('\n\n--- Run Messages ---\n');
|
|
185
|
+
|
|
186
|
+
if (runMessages) {
|
|
187
|
+
for (const msg of runMessages) {
|
|
188
|
+
const type = msg._getType();
|
|
189
|
+
if (type === 'tool') {
|
|
190
|
+
const name = 'name' in msg ? msg.name : 'unknown';
|
|
191
|
+
const rawContent =
|
|
192
|
+
typeof msg.content === 'string'
|
|
193
|
+
? msg.content
|
|
194
|
+
: JSON.stringify(msg.content);
|
|
195
|
+
const content = rawContent.slice(0, 200);
|
|
196
|
+
const truncated = rawContent.length > 200 ? '...' : '';
|
|
197
|
+
console.log(`[ToolMessage] name=${name}`);
|
|
198
|
+
console.log(` content: ${content}${truncated}\n`);
|
|
199
|
+
} else if (type === 'ai') {
|
|
200
|
+
const content =
|
|
201
|
+
typeof msg.content === 'string'
|
|
202
|
+
? msg.content.slice(0, 300)
|
|
203
|
+
: JSON.stringify(msg.content).slice(0, 300);
|
|
204
|
+
const toolCalls = 'tool_calls' in msg ? msg.tool_calls : undefined;
|
|
205
|
+
console.log(`[AIMessage]`);
|
|
206
|
+
if (toolCalls && Array.isArray(toolCalls) && toolCalls.length > 0) {
|
|
207
|
+
for (const tc of toolCalls) {
|
|
208
|
+
console.log(
|
|
209
|
+
` tool_call: ${tc.name}(${JSON.stringify(tc.args).slice(0, 100)}...)`
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
console.log(
|
|
214
|
+
` content: ${content}${content.length >= 300 ? '...' : ''}\n`
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const subagentToolMessages = runMessages.filter(
|
|
220
|
+
(msg) =>
|
|
221
|
+
msg._getType() === 'tool' &&
|
|
222
|
+
'name' in msg &&
|
|
223
|
+
msg.name === Constants.SUBAGENT
|
|
224
|
+
);
|
|
225
|
+
console.log(`\n--- Verification ---`);
|
|
226
|
+
console.log(`Subagent tool calls found: ${subagentToolMessages.length}`);
|
|
227
|
+
console.log(`Total run messages: ${runMessages.length}`);
|
|
228
|
+
console.log(`Result content parts: ${result?.length ?? 0}`);
|
|
229
|
+
|
|
230
|
+
if (subagentToolMessages.length > 0) {
|
|
231
|
+
console.log(
|
|
232
|
+
'\nSUCCESS: Subagent was invoked and returned a filtered result.'
|
|
233
|
+
);
|
|
234
|
+
console.log(
|
|
235
|
+
'The child context was isolated — only the final text came back.'
|
|
236
|
+
);
|
|
237
|
+
} else {
|
|
238
|
+
console.log('\nNOTE: No subagent tool calls detected.');
|
|
239
|
+
console.log('The LLM may have answered directly without delegating.');
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
console.log('\n=== Done ===');
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
testSubagentPrimitive().catch(console.error);
|
|
@@ -94,15 +94,6 @@ async function main(): Promise<void> {
|
|
|
94
94
|
console.log('==============================================');
|
|
95
95
|
console.log('Demonstrating runtime toolMap injection\n');
|
|
96
96
|
|
|
97
|
-
const apiKey = process.env.LIBRECHAT_CODE_API_KEY;
|
|
98
|
-
if (!apiKey) {
|
|
99
|
-
console.error(
|
|
100
|
-
'Error: LIBRECHAT_CODE_API_KEY environment variable is not set.'
|
|
101
|
-
);
|
|
102
|
-
console.log('Please set it in your .env file or environment.');
|
|
103
|
-
process.exit(1);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
97
|
console.log('Creating mock tools...');
|
|
107
98
|
const mockTools: StructuredToolInterface[] = [
|
|
108
99
|
createGetTeamMembersTool(),
|
|
@@ -119,7 +110,7 @@ async function main(): Promise<void> {
|
|
|
119
110
|
);
|
|
120
111
|
|
|
121
112
|
console.log('\nCreating PTC tool (without toolMap)...');
|
|
122
|
-
const ptcTool = createProgrammaticToolCallingTool(
|
|
113
|
+
const ptcTool = createProgrammaticToolCallingTool();
|
|
123
114
|
console.log('PTC tool created successfully!');
|
|
124
115
|
console.log(
|
|
125
116
|
'Note: toolMap will be passed at runtime with each invocation.\n'
|
|
@@ -0,0 +1,190 @@
|
|
|
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
|
+
});
|
|
@@ -0,0 +1,160 @@
|
|
|
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
|
+
});
|
|
@@ -11,16 +11,10 @@ config();
|
|
|
11
11
|
import fetch, { RequestInit } from 'node-fetch';
|
|
12
12
|
import { HttpsProxyAgent } from 'https-proxy-agent';
|
|
13
13
|
|
|
14
|
-
const API_KEY = process.env.LIBRECHAT_CODE_API_KEY ?? '';
|
|
15
14
|
const BASE_URL =
|
|
16
15
|
process.env.LIBRECHAT_CODE_BASEURL ?? 'https://api.librechat.ai/v1';
|
|
17
16
|
const PROXY = process.env.PROXY;
|
|
18
17
|
|
|
19
|
-
if (!API_KEY) {
|
|
20
|
-
console.error('LIBRECHAT_CODE_API_KEY not set');
|
|
21
|
-
process.exit(1);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
18
|
interface FileRef {
|
|
25
19
|
id: string;
|
|
26
20
|
name: string;
|
|
@@ -53,7 +47,6 @@ async function makeRequest(
|
|
|
53
47
|
headers: {
|
|
54
48
|
'Content-Type': 'application/json',
|
|
55
49
|
'User-Agent': 'LibreChat/1.0',
|
|
56
|
-
'X-API-Key': API_KEY,
|
|
57
50
|
},
|
|
58
51
|
};
|
|
59
52
|
|
|
@@ -66,15 +66,6 @@ async function main(): Promise<void> {
|
|
|
66
66
|
console.log('================================');
|
|
67
67
|
console.log('Demonstrating runtime tool registry injection\n');
|
|
68
68
|
|
|
69
|
-
const apiKey = process.env.LIBRECHAT_CODE_API_KEY;
|
|
70
|
-
if (!apiKey) {
|
|
71
|
-
console.error(
|
|
72
|
-
'Error: LIBRECHAT_CODE_API_KEY environment variable is not set.'
|
|
73
|
-
);
|
|
74
|
-
console.log('Please set it in your .env file or environment.');
|
|
75
|
-
process.exit(1);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
69
|
console.log('Creating sample tool registry...');
|
|
79
70
|
const toolRegistry = createToolSearchToolRegistry();
|
|
80
71
|
console.log(
|
|
@@ -82,7 +73,7 @@ async function main(): Promise<void> {
|
|
|
82
73
|
);
|
|
83
74
|
|
|
84
75
|
console.log('\nCreating Tool Search Regex tool WITH registry for testing...');
|
|
85
|
-
const searchTool = createToolSearch({
|
|
76
|
+
const searchTool = createToolSearch({ toolRegistry });
|
|
86
77
|
console.log('Tool created successfully!');
|
|
87
78
|
console.log(
|
|
88
79
|
'Note: In production, ToolNode injects toolRegistry via params when invoked through the graph.\n'
|