@agentforge/patterns 0.7.0 → 0.8.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/index.cjs +144 -305
- package/dist/index.d.cts +117 -26
- package/dist/index.d.ts +117 -26
- package/dist/index.js +144 -305
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -283,8 +283,8 @@ function generateToolCallCacheKey(toolName, args) {
|
|
|
283
283
|
return `${toolName}:${sortedArgs}`;
|
|
284
284
|
}
|
|
285
285
|
function createPatternLogger(name, defaultLevel = "info") {
|
|
286
|
-
const
|
|
287
|
-
return (0, import_core2.createLogger)(name, { level:
|
|
286
|
+
const logLevel4 = process.env.LOG_LEVEL?.toLowerCase() || defaultLevel;
|
|
287
|
+
return (0, import_core2.createLogger)(name, { level: logLevel4 });
|
|
288
288
|
}
|
|
289
289
|
function calculateDeduplicationSavings(duplicatesSkipped, toolsExecuted) {
|
|
290
290
|
if (duplicatesSkipped === 0) {
|
|
@@ -604,7 +604,8 @@ function createReActAgent(config, options) {
|
|
|
604
604
|
return ACTION_NODE;
|
|
605
605
|
};
|
|
606
606
|
const workflow = new import_langgraph.StateGraph(ReActState).addNode(REASONING_NODE, reasoningNode).addNode(ACTION_NODE, actionNode).addNode(OBSERVATION_NODE, observationNode).addEdge("__start__", REASONING_NODE).addConditionalEdges(REASONING_NODE, shouldContinue).addEdge(ACTION_NODE, OBSERVATION_NODE).addEdge(OBSERVATION_NODE, REASONING_NODE);
|
|
607
|
-
|
|
607
|
+
const checkpointerConfig = checkpointer === true ? { checkpointer: true } : checkpointer ? { checkpointer } : void 0;
|
|
608
|
+
return workflow.compile(checkpointerConfig);
|
|
608
609
|
}
|
|
609
610
|
|
|
610
611
|
// src/react/builder.ts
|
|
@@ -689,6 +690,43 @@ var ReActAgentBuilder = class {
|
|
|
689
690
|
this.options.nodeNames = nodeNames;
|
|
690
691
|
return this;
|
|
691
692
|
}
|
|
693
|
+
/**
|
|
694
|
+
* Set the checkpointer for state persistence (optional)
|
|
695
|
+
*
|
|
696
|
+
* Can be:
|
|
697
|
+
* - A BaseCheckpointSaver instance (e.g., MemorySaver) for standalone agents
|
|
698
|
+
* - `true` to use the parent graph's checkpointer with a separate namespace (for nested graphs)
|
|
699
|
+
*
|
|
700
|
+
* Required for human-in-the-loop workflows (askHuman tool) and conversation continuity.
|
|
701
|
+
*
|
|
702
|
+
* @param checkpointer - Checkpointer instance or `true` for nested graphs
|
|
703
|
+
*
|
|
704
|
+
* @example
|
|
705
|
+
* Standalone agent with its own checkpointer:
|
|
706
|
+
* ```typescript
|
|
707
|
+
* import { MemorySaver } from '@langchain/langgraph';
|
|
708
|
+
*
|
|
709
|
+
* const agent = new ReActAgentBuilder()
|
|
710
|
+
* .withModel(model)
|
|
711
|
+
* .withTools(tools)
|
|
712
|
+
* .withCheckpointer(new MemorySaver())
|
|
713
|
+
* .build();
|
|
714
|
+
* ```
|
|
715
|
+
*
|
|
716
|
+
* @example
|
|
717
|
+
* Nested agent using parent's checkpointer (for multi-agent systems):
|
|
718
|
+
* ```typescript
|
|
719
|
+
* const agent = new ReActAgentBuilder()
|
|
720
|
+
* .withModel(model)
|
|
721
|
+
* .withTools(tools)
|
|
722
|
+
* .withCheckpointer(true) // Use parent's checkpointer with separate namespace
|
|
723
|
+
* .build();
|
|
724
|
+
* ```
|
|
725
|
+
*/
|
|
726
|
+
withCheckpointer(checkpointer) {
|
|
727
|
+
this.config.checkpointer = checkpointer;
|
|
728
|
+
return this;
|
|
729
|
+
}
|
|
692
730
|
/**
|
|
693
731
|
* Build the ReAct agent
|
|
694
732
|
*
|
|
@@ -708,7 +746,8 @@ var ReActAgentBuilder = class {
|
|
|
708
746
|
systemPrompt: this.config.systemPrompt || DEFAULT_REACT_SYSTEM_PROMPT,
|
|
709
747
|
maxIterations: this.config.maxIterations ?? 10,
|
|
710
748
|
returnIntermediateSteps: this.config.returnIntermediateSteps ?? false,
|
|
711
|
-
stopCondition: this.config.stopCondition
|
|
749
|
+
stopCondition: this.config.stopCondition,
|
|
750
|
+
checkpointer: this.config.checkpointer
|
|
712
751
|
};
|
|
713
752
|
return createReActAgent(finalConfig, this.options);
|
|
714
753
|
}
|
|
@@ -2164,66 +2203,6 @@ var MultiAgentState = (0, import_core7.createStateAnnotation)(MultiAgentStateCon
|
|
|
2164
2203
|
|
|
2165
2204
|
// src/multi-agent/routing.ts
|
|
2166
2205
|
var import_messages4 = require("@langchain/core/messages");
|
|
2167
|
-
var import_core8 = require("@agentforge/core");
|
|
2168
|
-
var logLevel = process.env.LOG_LEVEL?.toLowerCase() || import_core8.LogLevel.INFO;
|
|
2169
|
-
var logger = (0, import_core8.createLogger)("multi-agent:routing", { level: logLevel });
|
|
2170
|
-
async function executeTools(toolCalls, tools) {
|
|
2171
|
-
const results = [];
|
|
2172
|
-
logger.debug("Executing tools", {
|
|
2173
|
-
toolCallCount: toolCalls.length,
|
|
2174
|
-
toolNames: toolCalls.map((tc) => tc.name)
|
|
2175
|
-
});
|
|
2176
|
-
for (const toolCall of toolCalls) {
|
|
2177
|
-
const tool = tools.find((t) => t.metadata.name === toolCall.name);
|
|
2178
|
-
if (!tool) {
|
|
2179
|
-
logger.warn("Tool not found", {
|
|
2180
|
-
toolName: toolCall.name,
|
|
2181
|
-
availableTools: tools.map((t) => t.metadata.name)
|
|
2182
|
-
});
|
|
2183
|
-
results.push(new import_messages4.ToolMessage({
|
|
2184
|
-
content: `Error: Tool '${toolCall.name}' not found`,
|
|
2185
|
-
tool_call_id: toolCall.id
|
|
2186
|
-
}));
|
|
2187
|
-
continue;
|
|
2188
|
-
}
|
|
2189
|
-
try {
|
|
2190
|
-
logger.debug("Executing tool", {
|
|
2191
|
-
toolName: toolCall.name,
|
|
2192
|
-
args: toolCall.args
|
|
2193
|
-
});
|
|
2194
|
-
const result = await tool.execute(toolCall.args);
|
|
2195
|
-
const content = typeof result === "string" ? result : JSON.stringify(result);
|
|
2196
|
-
logger.debug("Tool execution successful", {
|
|
2197
|
-
toolName: toolCall.name,
|
|
2198
|
-
resultLength: content.length
|
|
2199
|
-
});
|
|
2200
|
-
results.push(new import_messages4.ToolMessage({
|
|
2201
|
-
content,
|
|
2202
|
-
tool_call_id: toolCall.id
|
|
2203
|
-
}));
|
|
2204
|
-
} catch (error) {
|
|
2205
|
-
logger.error("Tool execution failed", {
|
|
2206
|
-
toolName: toolCall.name,
|
|
2207
|
-
error: error.message
|
|
2208
|
-
});
|
|
2209
|
-
results.push(new import_messages4.ToolMessage({
|
|
2210
|
-
content: `Error executing tool: ${error.message}`,
|
|
2211
|
-
tool_call_id: toolCall.id
|
|
2212
|
-
}));
|
|
2213
|
-
}
|
|
2214
|
-
}
|
|
2215
|
-
logger.debug("Tool execution complete", {
|
|
2216
|
-
successCount: results.filter((r) => {
|
|
2217
|
-
const content = typeof r.content === "string" ? r.content : JSON.stringify(r.content);
|
|
2218
|
-
return !content.startsWith("Error");
|
|
2219
|
-
}).length,
|
|
2220
|
-
errorCount: results.filter((r) => {
|
|
2221
|
-
const content = typeof r.content === "string" ? r.content : JSON.stringify(r.content);
|
|
2222
|
-
return content.startsWith("Error");
|
|
2223
|
-
}).length
|
|
2224
|
-
});
|
|
2225
|
-
return results;
|
|
2226
|
-
}
|
|
2227
2206
|
var DEFAULT_SUPERVISOR_SYSTEM_PROMPT = `You are a supervisor agent responsible for routing tasks to specialized worker agents.
|
|
2228
2207
|
|
|
2229
2208
|
Your job is to:
|
|
@@ -2259,151 +2238,48 @@ Choose parallel routing when the task benefits from multiple perspectives or dat
|
|
|
2259
2238
|
var llmBasedRouting = {
|
|
2260
2239
|
name: "llm-based",
|
|
2261
2240
|
async route(state, config) {
|
|
2262
|
-
logger.info("Starting LLM-based routing", {
|
|
2263
|
-
iteration: state.iteration,
|
|
2264
|
-
availableWorkers: Object.keys(state.workers).length
|
|
2265
|
-
});
|
|
2266
2241
|
if (!config.model) {
|
|
2267
2242
|
throw new Error("LLM-based routing requires a model to be configured");
|
|
2268
2243
|
}
|
|
2269
2244
|
const systemPrompt = config.systemPrompt || DEFAULT_SUPERVISOR_SYSTEM_PROMPT;
|
|
2270
|
-
const maxRetries = config.maxToolRetries || 3;
|
|
2271
|
-
const tools = config.tools || [];
|
|
2272
2245
|
const workerInfo = Object.entries(state.workers).map(([id, caps]) => {
|
|
2273
2246
|
const skills = caps.skills.join(", ");
|
|
2274
|
-
const
|
|
2247
|
+
const tools = caps.tools.join(", ");
|
|
2275
2248
|
const available = caps.available ? "available" : "busy";
|
|
2276
|
-
return `- ${id}: Skills: [${skills}], Tools: [${
|
|
2249
|
+
return `- ${id}: Skills: [${skills}], Tools: [${tools}], Status: ${available}, Workload: ${caps.currentWorkload}`;
|
|
2277
2250
|
}).join("\n");
|
|
2278
|
-
logger.debug("Worker capabilities", {
|
|
2279
|
-
workers: Object.entries(state.workers).map(([id, caps]) => ({
|
|
2280
|
-
id,
|
|
2281
|
-
skills: caps.skills,
|
|
2282
|
-
available: caps.available,
|
|
2283
|
-
workload: caps.currentWorkload
|
|
2284
|
-
}))
|
|
2285
|
-
});
|
|
2286
2251
|
const lastMessage = state.messages[state.messages.length - 1];
|
|
2287
2252
|
const taskContext = lastMessage?.content || state.input;
|
|
2288
|
-
logger.debug("Task context", {
|
|
2289
|
-
taskLength: taskContext.length,
|
|
2290
|
-
taskPreview: taskContext.substring(0, 100)
|
|
2291
|
-
});
|
|
2292
2253
|
const userPrompt = `Current task: ${taskContext}
|
|
2293
2254
|
|
|
2294
2255
|
Available workers:
|
|
2295
2256
|
${workerInfo}
|
|
2296
2257
|
|
|
2297
2258
|
Select the best worker(s) for this task and explain your reasoning.`;
|
|
2298
|
-
const
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
const response = await config.model.invoke(messages);
|
|
2312
|
-
if (response.tool_calls && response.tool_calls.length > 0) {
|
|
2313
|
-
logger.info("LLM requested tool calls", {
|
|
2314
|
-
toolCount: response.tool_calls.length,
|
|
2315
|
-
toolNames: response.tool_calls.map((tc) => tc.name)
|
|
2316
|
-
});
|
|
2317
|
-
if (tools.length === 0) {
|
|
2318
|
-
throw new Error("LLM requested tool calls but no tools are configured");
|
|
2319
|
-
}
|
|
2320
|
-
const toolResults = await executeTools(response.tool_calls, tools);
|
|
2321
|
-
conversationHistory.push(
|
|
2322
|
-
new import_messages4.AIMessage({ content: response.content || "", tool_calls: response.tool_calls }),
|
|
2323
|
-
...toolResults
|
|
2324
|
-
);
|
|
2325
|
-
attempt++;
|
|
2326
|
-
logger.debug("Retrying routing with tool results", { attempt });
|
|
2327
|
-
continue;
|
|
2328
|
-
}
|
|
2329
|
-
logger.debug("Parsing routing decision from LLM response");
|
|
2330
|
-
let decision;
|
|
2331
|
-
if (response && typeof response === "object" && ("targetAgent" in response || "targetAgents" in response)) {
|
|
2332
|
-
logger.debug("Response is structured output", {
|
|
2333
|
-
hasTargetAgent: "targetAgent" in response,
|
|
2334
|
-
hasTargetAgents: "targetAgents" in response
|
|
2335
|
-
});
|
|
2336
|
-
decision = response;
|
|
2337
|
-
} else if (response.content) {
|
|
2338
|
-
if (typeof response.content === "string") {
|
|
2339
|
-
try {
|
|
2340
|
-
decision = JSON.parse(response.content);
|
|
2341
|
-
logger.debug("Parsed JSON from string response");
|
|
2342
|
-
} catch (error) {
|
|
2343
|
-
logger.error("Failed to parse routing decision", {
|
|
2344
|
-
content: response.content,
|
|
2345
|
-
error: error instanceof Error ? error.message : String(error)
|
|
2346
|
-
});
|
|
2347
|
-
throw new Error(`Failed to parse routing decision from LLM. Expected JSON but got: ${response.content}`);
|
|
2348
|
-
}
|
|
2349
|
-
} else if (typeof response.content === "object") {
|
|
2350
|
-
logger.debug("Response content is already an object");
|
|
2351
|
-
decision = response.content;
|
|
2352
|
-
} else {
|
|
2353
|
-
logger.error("Unexpected response content type", {
|
|
2354
|
-
type: typeof response.content
|
|
2355
|
-
});
|
|
2356
|
-
throw new Error(`Unexpected response content type: ${typeof response.content}`);
|
|
2357
|
-
}
|
|
2358
|
-
} else {
|
|
2359
|
-
logger.error("Unexpected response format", {
|
|
2360
|
-
response: JSON.stringify(response)
|
|
2361
|
-
});
|
|
2362
|
-
throw new Error(`Unexpected response format: ${JSON.stringify(response)}`);
|
|
2363
|
-
}
|
|
2364
|
-
const result = {
|
|
2365
|
-
targetAgent: decision.targetAgent,
|
|
2366
|
-
targetAgents: decision.targetAgents,
|
|
2367
|
-
reasoning: decision.reasoning,
|
|
2368
|
-
confidence: decision.confidence,
|
|
2369
|
-
strategy: "llm-based",
|
|
2370
|
-
timestamp: Date.now()
|
|
2371
|
-
};
|
|
2372
|
-
logger.info("LLM routing decision made", {
|
|
2373
|
-
targetAgent: result.targetAgent,
|
|
2374
|
-
targetAgents: result.targetAgents,
|
|
2375
|
-
isParallel: result.targetAgents && result.targetAgents.length > 1,
|
|
2376
|
-
confidence: result.confidence,
|
|
2377
|
-
reasoning: result.reasoning
|
|
2378
|
-
});
|
|
2379
|
-
return result;
|
|
2380
|
-
}
|
|
2381
|
-
logger.error("Max tool retries exceeded", { maxRetries });
|
|
2382
|
-
throw new Error(`Max tool retries (${maxRetries}) exceeded without routing decision`);
|
|
2259
|
+
const messages = [
|
|
2260
|
+
new import_messages4.SystemMessage(systemPrompt),
|
|
2261
|
+
new import_messages4.HumanMessage(userPrompt)
|
|
2262
|
+
];
|
|
2263
|
+
const decision = await config.model.invoke(messages);
|
|
2264
|
+
return {
|
|
2265
|
+
targetAgent: decision.targetAgent,
|
|
2266
|
+
targetAgents: decision.targetAgents,
|
|
2267
|
+
reasoning: decision.reasoning,
|
|
2268
|
+
confidence: decision.confidence,
|
|
2269
|
+
strategy: "llm-based",
|
|
2270
|
+
timestamp: Date.now()
|
|
2271
|
+
};
|
|
2383
2272
|
}
|
|
2384
2273
|
};
|
|
2385
2274
|
var roundRobinRouting = {
|
|
2386
2275
|
name: "round-robin",
|
|
2387
2276
|
async route(state, config) {
|
|
2388
|
-
logger.info("Starting round-robin routing", {
|
|
2389
|
-
iteration: state.iteration
|
|
2390
|
-
});
|
|
2391
2277
|
const availableWorkers = Object.entries(state.workers).filter(([_, caps]) => caps.available).map(([id]) => id);
|
|
2392
|
-
logger.debug("Available workers for round-robin", {
|
|
2393
|
-
count: availableWorkers.length,
|
|
2394
|
-
workers: availableWorkers
|
|
2395
|
-
});
|
|
2396
2278
|
if (availableWorkers.length === 0) {
|
|
2397
|
-
logger.error("No available workers for round-robin routing");
|
|
2398
2279
|
throw new Error("No available workers for round-robin routing");
|
|
2399
2280
|
}
|
|
2400
2281
|
const lastRoutingIndex = state.routingHistory.length % availableWorkers.length;
|
|
2401
2282
|
const targetAgent = availableWorkers[lastRoutingIndex];
|
|
2402
|
-
logger.info("Round-robin routing decision", {
|
|
2403
|
-
targetAgent,
|
|
2404
|
-
index: lastRoutingIndex + 1,
|
|
2405
|
-
totalWorkers: availableWorkers.length
|
|
2406
|
-
});
|
|
2407
2283
|
return {
|
|
2408
2284
|
targetAgent,
|
|
2409
2285
|
targetAgents: null,
|
|
@@ -2417,15 +2293,8 @@ var roundRobinRouting = {
|
|
|
2417
2293
|
var skillBasedRouting = {
|
|
2418
2294
|
name: "skill-based",
|
|
2419
2295
|
async route(state, config) {
|
|
2420
|
-
logger.info("Starting skill-based routing", {
|
|
2421
|
-
iteration: state.iteration
|
|
2422
|
-
});
|
|
2423
2296
|
const lastMessage = state.messages[state.messages.length - 1];
|
|
2424
2297
|
const taskContent = (lastMessage?.content || state.input).toLowerCase();
|
|
2425
|
-
logger.debug("Task content for skill matching", {
|
|
2426
|
-
taskLength: taskContent.length,
|
|
2427
|
-
taskPreview: taskContent.substring(0, 100)
|
|
2428
|
-
});
|
|
2429
2298
|
const workerScores = Object.entries(state.workers).filter(([_, caps]) => caps.available).map(([id, caps]) => {
|
|
2430
2299
|
const skillMatches = caps.skills.filter(
|
|
2431
2300
|
(skill) => taskContent.includes(skill.toLowerCase())
|
|
@@ -2436,20 +2305,11 @@ var skillBasedRouting = {
|
|
|
2436
2305
|
const score = skillMatches * 2 + toolMatches;
|
|
2437
2306
|
return { id, score, skills: caps.skills, tools: caps.tools };
|
|
2438
2307
|
}).filter((w) => w.score > 0).sort((a, b) => b.score - a.score);
|
|
2439
|
-
logger.debug("Worker skill scores", {
|
|
2440
|
-
scoredWorkers: workerScores.map((w) => ({ id: w.id, score: w.score }))
|
|
2441
|
-
});
|
|
2442
2308
|
if (workerScores.length === 0) {
|
|
2443
|
-
logger.warn("No skill matches found, using fallback");
|
|
2444
2309
|
const firstAvailable = Object.entries(state.workers).find(([_, caps]) => caps.available);
|
|
2445
2310
|
if (!firstAvailable) {
|
|
2446
|
-
logger.error("No available workers for skill-based routing");
|
|
2447
2311
|
throw new Error("No available workers for skill-based routing");
|
|
2448
2312
|
}
|
|
2449
|
-
logger.info("Skill-based routing fallback decision", {
|
|
2450
|
-
targetAgent: firstAvailable[0],
|
|
2451
|
-
confidence: 0.5
|
|
2452
|
-
});
|
|
2453
2313
|
return {
|
|
2454
2314
|
targetAgent: firstAvailable[0],
|
|
2455
2315
|
targetAgents: null,
|
|
@@ -2461,12 +2321,6 @@ var skillBasedRouting = {
|
|
|
2461
2321
|
}
|
|
2462
2322
|
const best = workerScores[0];
|
|
2463
2323
|
const confidence = Math.min(best.score / 5, 1);
|
|
2464
|
-
logger.info("Skill-based routing decision", {
|
|
2465
|
-
targetAgent: best.id,
|
|
2466
|
-
score: best.score,
|
|
2467
|
-
confidence,
|
|
2468
|
-
matchedSkills: best.skills
|
|
2469
|
-
});
|
|
2470
2324
|
return {
|
|
2471
2325
|
targetAgent: best.id,
|
|
2472
2326
|
targetAgents: null,
|
|
@@ -2480,26 +2334,13 @@ var skillBasedRouting = {
|
|
|
2480
2334
|
var loadBalancedRouting = {
|
|
2481
2335
|
name: "load-balanced",
|
|
2482
2336
|
async route(state, config) {
|
|
2483
|
-
logger.info("Starting load-balanced routing", {
|
|
2484
|
-
iteration: state.iteration
|
|
2485
|
-
});
|
|
2486
2337
|
const availableWorkers = Object.entries(state.workers).filter(([_, caps]) => caps.available).map(([id, caps]) => ({ id, workload: caps.currentWorkload })).sort((a, b) => a.workload - b.workload);
|
|
2487
|
-
logger.debug("Worker workloads", {
|
|
2488
|
-
workers: availableWorkers.map((w) => ({ id: w.id, workload: w.workload }))
|
|
2489
|
-
});
|
|
2490
2338
|
if (availableWorkers.length === 0) {
|
|
2491
|
-
logger.error("No available workers for load-balanced routing");
|
|
2492
2339
|
throw new Error("No available workers for load-balanced routing");
|
|
2493
2340
|
}
|
|
2494
2341
|
const targetWorker = availableWorkers[0];
|
|
2495
2342
|
const avgWorkload = availableWorkers.reduce((sum, w) => sum + w.workload, 0) / availableWorkers.length;
|
|
2496
2343
|
const confidence = targetWorker.workload === 0 ? 1 : Math.max(0.5, 1 - targetWorker.workload / (avgWorkload * 2));
|
|
2497
|
-
logger.info("Load-balanced routing decision", {
|
|
2498
|
-
targetAgent: targetWorker.id,
|
|
2499
|
-
workload: targetWorker.workload,
|
|
2500
|
-
avgWorkload: avgWorkload.toFixed(1),
|
|
2501
|
-
confidence
|
|
2502
|
-
});
|
|
2503
2344
|
return {
|
|
2504
2345
|
targetAgent: targetWorker.id,
|
|
2505
2346
|
targetAgents: null,
|
|
@@ -2513,24 +2354,13 @@ var loadBalancedRouting = {
|
|
|
2513
2354
|
var ruleBasedRouting = {
|
|
2514
2355
|
name: "rule-based",
|
|
2515
2356
|
async route(state, config) {
|
|
2516
|
-
logger.info("Starting rule-based routing", {
|
|
2517
|
-
iteration: state.iteration
|
|
2518
|
-
});
|
|
2519
2357
|
if (!config.routingFn) {
|
|
2520
|
-
logger.error("Rule-based routing requires a custom routing function");
|
|
2521
2358
|
throw new Error("Rule-based routing requires a custom routing function");
|
|
2522
2359
|
}
|
|
2523
|
-
|
|
2524
|
-
logger.info("Rule-based routing decision", {
|
|
2525
|
-
targetAgent: decision.targetAgent,
|
|
2526
|
-
targetAgents: decision.targetAgents,
|
|
2527
|
-
confidence: decision.confidence
|
|
2528
|
-
});
|
|
2529
|
-
return decision;
|
|
2360
|
+
return await config.routingFn(state);
|
|
2530
2361
|
}
|
|
2531
2362
|
};
|
|
2532
2363
|
function getRoutingStrategy(name) {
|
|
2533
|
-
logger.debug("Getting routing strategy", { name });
|
|
2534
2364
|
switch (name) {
|
|
2535
2365
|
case "llm-based":
|
|
2536
2366
|
return llmBasedRouting;
|
|
@@ -2543,15 +2373,14 @@ function getRoutingStrategy(name) {
|
|
|
2543
2373
|
case "rule-based":
|
|
2544
2374
|
return ruleBasedRouting;
|
|
2545
2375
|
default:
|
|
2546
|
-
logger.error("Unknown routing strategy", { name });
|
|
2547
2376
|
throw new Error(`Unknown routing strategy: ${name}`);
|
|
2548
2377
|
}
|
|
2549
2378
|
}
|
|
2550
2379
|
|
|
2551
2380
|
// src/multi-agent/utils.ts
|
|
2552
|
-
var
|
|
2553
|
-
var
|
|
2554
|
-
var
|
|
2381
|
+
var import_core8 = require("@agentforge/core");
|
|
2382
|
+
var logLevel = process.env.LOG_LEVEL?.toLowerCase() || import_core8.LogLevel.INFO;
|
|
2383
|
+
var logger = (0, import_core8.createLogger)("multi-agent", { level: logLevel });
|
|
2555
2384
|
function isReActAgent(obj) {
|
|
2556
2385
|
return obj && typeof obj === "object" && typeof obj.invoke === "function" && typeof obj.stream === "function" && // Additional check to ensure it's not just any object with invoke/stream
|
|
2557
2386
|
(obj.constructor?.name === "CompiledGraph" || obj.constructor?.name === "CompiledStateGraph");
|
|
@@ -2559,9 +2388,9 @@ function isReActAgent(obj) {
|
|
|
2559
2388
|
function wrapReActAgent(workerId, agent, verbose = false) {
|
|
2560
2389
|
return async (state, config) => {
|
|
2561
2390
|
try {
|
|
2562
|
-
|
|
2391
|
+
logger.debug("Wrapping ReAct agent execution", { workerId });
|
|
2563
2392
|
const task = state.messages[state.messages.length - 1]?.content || state.input;
|
|
2564
|
-
|
|
2393
|
+
logger.debug("Extracted task", {
|
|
2565
2394
|
workerId,
|
|
2566
2395
|
taskPreview: task.substring(0, 100) + (task.length > 100 ? "..." : "")
|
|
2567
2396
|
});
|
|
@@ -2569,25 +2398,39 @@ function wrapReActAgent(workerId, agent, verbose = false) {
|
|
|
2569
2398
|
(assignment) => assignment.workerId === workerId && !state.completedTasks.some((task2) => task2.assignmentId === assignment.id)
|
|
2570
2399
|
);
|
|
2571
2400
|
if (!currentAssignment) {
|
|
2572
|
-
|
|
2401
|
+
logger.debug("No active assignment found", { workerId });
|
|
2573
2402
|
return {};
|
|
2574
2403
|
}
|
|
2404
|
+
const workerThreadId = config?.configurable?.thread_id ? `${config.configurable.thread_id}:worker:${workerId}` : void 0;
|
|
2405
|
+
const workerConfig = workerThreadId ? {
|
|
2406
|
+
...config,
|
|
2407
|
+
configurable: {
|
|
2408
|
+
...config.configurable,
|
|
2409
|
+
thread_id: workerThreadId
|
|
2410
|
+
}
|
|
2411
|
+
} : config;
|
|
2412
|
+
logger.debug("Invoking ReAct agent with worker-specific config", {
|
|
2413
|
+
workerId,
|
|
2414
|
+
parentThreadId: config?.configurable?.thread_id,
|
|
2415
|
+
workerThreadId,
|
|
2416
|
+
hasConfig: !!workerConfig
|
|
2417
|
+
});
|
|
2575
2418
|
const result = await agent.invoke(
|
|
2576
2419
|
{
|
|
2577
2420
|
messages: [{ role: "user", content: task }]
|
|
2578
2421
|
},
|
|
2579
|
-
|
|
2580
|
-
//
|
|
2422
|
+
workerConfig
|
|
2423
|
+
// Worker-specific config with unique thread_id
|
|
2581
2424
|
);
|
|
2582
2425
|
const response = result.messages?.[result.messages.length - 1]?.content || "No response";
|
|
2583
|
-
|
|
2426
|
+
logger.debug("Received response from ReAct agent", {
|
|
2584
2427
|
workerId,
|
|
2585
2428
|
responsePreview: response.substring(0, 100) + (response.length > 100 ? "..." : "")
|
|
2586
2429
|
});
|
|
2587
2430
|
const toolsUsed = result.actions?.map((action) => action.name).filter(Boolean) || [];
|
|
2588
2431
|
const uniqueTools = [...new Set(toolsUsed)];
|
|
2589
2432
|
if (uniqueTools.length > 0) {
|
|
2590
|
-
|
|
2433
|
+
logger.debug("Tools used by ReAct agent", { workerId, tools: uniqueTools });
|
|
2591
2434
|
}
|
|
2592
2435
|
const taskResult = {
|
|
2593
2436
|
assignmentId: currentAssignment.id,
|
|
@@ -2606,7 +2449,7 @@ function wrapReActAgent(workerId, agent, verbose = false) {
|
|
|
2606
2449
|
};
|
|
2607
2450
|
} catch (error) {
|
|
2608
2451
|
const errorMessage = handleNodeError(error, `react-agent:${workerId}`, false);
|
|
2609
|
-
|
|
2452
|
+
logger.error("Error in ReAct agent execution", {
|
|
2610
2453
|
workerId,
|
|
2611
2454
|
error: errorMessage
|
|
2612
2455
|
});
|
|
@@ -2638,9 +2481,9 @@ function wrapReActAgent(workerId, agent, verbose = false) {
|
|
|
2638
2481
|
|
|
2639
2482
|
// src/multi-agent/nodes.ts
|
|
2640
2483
|
var import_messages5 = require("@langchain/core/messages");
|
|
2641
|
-
var
|
|
2642
|
-
var
|
|
2643
|
-
var
|
|
2484
|
+
var import_core9 = require("@agentforge/core");
|
|
2485
|
+
var logLevel2 = process.env.LOG_LEVEL?.toLowerCase() || import_core9.LogLevel.INFO;
|
|
2486
|
+
var logger2 = (0, import_core9.createLogger)("multi-agent:nodes", { level: logLevel2 });
|
|
2644
2487
|
var DEFAULT_AGGREGATOR_SYSTEM_PROMPT = `You are an aggregator agent responsible for combining results from multiple worker agents.
|
|
2645
2488
|
|
|
2646
2489
|
Your job is to:
|
|
@@ -2658,19 +2501,19 @@ function createSupervisorNode(config) {
|
|
|
2658
2501
|
} = config;
|
|
2659
2502
|
return async (state) => {
|
|
2660
2503
|
try {
|
|
2661
|
-
|
|
2504
|
+
logger2.info("Supervisor node executing", {
|
|
2662
2505
|
iteration: state.iteration,
|
|
2663
2506
|
maxIterations,
|
|
2664
2507
|
activeAssignments: state.activeAssignments.length,
|
|
2665
2508
|
completedTasks: state.completedTasks.length
|
|
2666
2509
|
});
|
|
2667
|
-
|
|
2510
|
+
logger2.debug(`Routing iteration ${state.iteration}/${maxIterations}`);
|
|
2668
2511
|
if (state.iteration >= maxIterations) {
|
|
2669
|
-
|
|
2512
|
+
logger2.warn("Max iterations reached", {
|
|
2670
2513
|
iteration: state.iteration,
|
|
2671
2514
|
maxIterations
|
|
2672
2515
|
});
|
|
2673
|
-
|
|
2516
|
+
logger2.debug("Max iterations reached, moving to aggregation");
|
|
2674
2517
|
return {
|
|
2675
2518
|
status: "aggregating",
|
|
2676
2519
|
currentAgent: "aggregator"
|
|
@@ -2679,26 +2522,26 @@ function createSupervisorNode(config) {
|
|
|
2679
2522
|
const allCompleted = state.activeAssignments.every(
|
|
2680
2523
|
(assignment) => state.completedTasks.some((task2) => task2.assignmentId === assignment.id)
|
|
2681
2524
|
);
|
|
2682
|
-
|
|
2525
|
+
logger2.debug("Checking task completion", {
|
|
2683
2526
|
activeAssignments: state.activeAssignments.length,
|
|
2684
2527
|
completedTasks: state.completedTasks.length,
|
|
2685
2528
|
allCompleted
|
|
2686
2529
|
});
|
|
2687
2530
|
if (allCompleted && state.activeAssignments.length > 0) {
|
|
2688
|
-
|
|
2531
|
+
logger2.info("All tasks completed, moving to aggregation", {
|
|
2689
2532
|
completedCount: state.completedTasks.length
|
|
2690
2533
|
});
|
|
2691
|
-
|
|
2534
|
+
logger2.debug("All tasks completed, moving to aggregation");
|
|
2692
2535
|
return {
|
|
2693
2536
|
status: "aggregating",
|
|
2694
2537
|
currentAgent: "aggregator"
|
|
2695
2538
|
};
|
|
2696
2539
|
}
|
|
2697
|
-
|
|
2540
|
+
logger2.debug("Getting routing strategy", { strategy });
|
|
2698
2541
|
const routingImpl = getRoutingStrategy(strategy);
|
|
2699
2542
|
const decision = await routingImpl.route(state, config);
|
|
2700
2543
|
const targetAgents = decision.targetAgents && decision.targetAgents.length > 0 ? decision.targetAgents : decision.targetAgent ? [decision.targetAgent] : [];
|
|
2701
|
-
|
|
2544
|
+
logger2.debug("Target agents determined", {
|
|
2702
2545
|
targetAgents,
|
|
2703
2546
|
isParallel: targetAgents.length > 1,
|
|
2704
2547
|
decision: {
|
|
@@ -2707,17 +2550,17 @@ function createSupervisorNode(config) {
|
|
|
2707
2550
|
}
|
|
2708
2551
|
});
|
|
2709
2552
|
if (targetAgents.length === 0) {
|
|
2710
|
-
|
|
2553
|
+
logger2.error("No target agents specified in routing decision");
|
|
2711
2554
|
throw new Error("Routing decision must specify at least one target agent");
|
|
2712
2555
|
}
|
|
2713
2556
|
if (targetAgents.length === 1) {
|
|
2714
|
-
|
|
2557
|
+
logger2.info("Routing to single agent", {
|
|
2715
2558
|
targetAgent: targetAgents[0],
|
|
2716
2559
|
reasoning: decision.reasoning,
|
|
2717
2560
|
confidence: decision.confidence
|
|
2718
2561
|
});
|
|
2719
2562
|
} else {
|
|
2720
|
-
|
|
2563
|
+
logger2.info("Routing to multiple agents in parallel", {
|
|
2721
2564
|
targetAgents,
|
|
2722
2565
|
count: targetAgents.length,
|
|
2723
2566
|
reasoning: decision.reasoning,
|
|
@@ -2725,9 +2568,9 @@ function createSupervisorNode(config) {
|
|
|
2725
2568
|
});
|
|
2726
2569
|
}
|
|
2727
2570
|
if (targetAgents.length === 1) {
|
|
2728
|
-
|
|
2571
|
+
logger2.debug(`Routing to ${targetAgents[0]}: ${decision.reasoning}`);
|
|
2729
2572
|
} else {
|
|
2730
|
-
|
|
2573
|
+
logger2.debug(`Routing to ${targetAgents.length} agents in parallel [${targetAgents.join(", ")}]: ${decision.reasoning}`);
|
|
2731
2574
|
}
|
|
2732
2575
|
const task = state.messages[state.messages.length - 1]?.content || state.input;
|
|
2733
2576
|
const assignments = targetAgents.map((workerId) => ({
|
|
@@ -2737,7 +2580,7 @@ function createSupervisorNode(config) {
|
|
|
2737
2580
|
priority: 5,
|
|
2738
2581
|
assignedAt: Date.now()
|
|
2739
2582
|
}));
|
|
2740
|
-
|
|
2583
|
+
logger2.debug("Created task assignments", {
|
|
2741
2584
|
assignmentCount: assignments.length,
|
|
2742
2585
|
assignments: assignments.map((a) => ({
|
|
2743
2586
|
id: a.id,
|
|
@@ -2757,7 +2600,7 @@ function createSupervisorNode(config) {
|
|
|
2757
2600
|
priority: assignment.priority
|
|
2758
2601
|
}
|
|
2759
2602
|
}));
|
|
2760
|
-
|
|
2603
|
+
logger2.info("Supervisor routing complete", {
|
|
2761
2604
|
currentAgent: targetAgents.join(","),
|
|
2762
2605
|
status: "executing",
|
|
2763
2606
|
assignmentCount: assignments.length,
|
|
@@ -2774,7 +2617,7 @@ function createSupervisorNode(config) {
|
|
|
2774
2617
|
iteration: state.iteration + 1
|
|
2775
2618
|
};
|
|
2776
2619
|
} catch (error) {
|
|
2777
|
-
|
|
2620
|
+
logger2.error("Supervisor node error", {
|
|
2778
2621
|
error: error instanceof Error ? error.message : String(error),
|
|
2779
2622
|
stack: error instanceof Error ? error.stack : void 0,
|
|
2780
2623
|
iteration: state.iteration
|
|
@@ -2799,7 +2642,7 @@ function createWorkerNode(config) {
|
|
|
2799
2642
|
} = config;
|
|
2800
2643
|
return async (state, runConfig) => {
|
|
2801
2644
|
try {
|
|
2802
|
-
|
|
2645
|
+
logger2.info("Worker node executing", {
|
|
2803
2646
|
workerId: id,
|
|
2804
2647
|
iteration: state.iteration,
|
|
2805
2648
|
activeAssignments: state.activeAssignments.length
|
|
@@ -2808,39 +2651,39 @@ function createWorkerNode(config) {
|
|
|
2808
2651
|
(assignment) => assignment.workerId === id && !state.completedTasks.some((task) => task.assignmentId === assignment.id)
|
|
2809
2652
|
);
|
|
2810
2653
|
if (!currentAssignment) {
|
|
2811
|
-
|
|
2654
|
+
logger2.debug("No active assignment found for worker", {
|
|
2812
2655
|
workerId: id,
|
|
2813
2656
|
totalActiveAssignments: state.activeAssignments.length,
|
|
2814
2657
|
completedTasks: state.completedTasks.length
|
|
2815
2658
|
});
|
|
2816
2659
|
return {};
|
|
2817
2660
|
}
|
|
2818
|
-
|
|
2661
|
+
logger2.info("Worker processing assignment", {
|
|
2819
2662
|
workerId: id,
|
|
2820
2663
|
assignmentId: currentAssignment.id,
|
|
2821
2664
|
taskLength: currentAssignment.task.length,
|
|
2822
2665
|
taskPreview: currentAssignment.task.substring(0, 100)
|
|
2823
2666
|
});
|
|
2824
2667
|
if (executeFn) {
|
|
2825
|
-
|
|
2668
|
+
logger2.debug("Using custom execution function", { workerId: id });
|
|
2826
2669
|
return await executeFn(state, runConfig);
|
|
2827
2670
|
}
|
|
2828
2671
|
if (agent) {
|
|
2829
2672
|
if (isReActAgent(agent)) {
|
|
2830
|
-
|
|
2673
|
+
logger2.debug("Using ReAct agent", { workerId: id });
|
|
2831
2674
|
const wrappedFn = wrapReActAgent(id, agent, verbose);
|
|
2832
2675
|
return await wrappedFn(state, runConfig);
|
|
2833
2676
|
} else {
|
|
2834
|
-
|
|
2677
|
+
logger2.warn("Agent provided but not a ReAct agent, falling back", { workerId: id });
|
|
2835
2678
|
}
|
|
2836
2679
|
}
|
|
2837
2680
|
if (!model) {
|
|
2838
|
-
|
|
2681
|
+
logger2.error("Worker missing required configuration", { workerId: id });
|
|
2839
2682
|
throw new Error(
|
|
2840
2683
|
`Worker ${id} requires either a model, an agent, or a custom execution function. Provide one of: config.model, config.agent, or config.executeFn`
|
|
2841
2684
|
);
|
|
2842
2685
|
}
|
|
2843
|
-
|
|
2686
|
+
logger2.debug("Using default LLM execution", {
|
|
2844
2687
|
workerId: id,
|
|
2845
2688
|
hasTools: tools.length > 0,
|
|
2846
2689
|
toolCount: tools.length
|
|
@@ -2856,18 +2699,18 @@ Execute the assigned task using your skills and tools. Provide a clear, actionab
|
|
|
2856
2699
|
];
|
|
2857
2700
|
let modelToUse = model;
|
|
2858
2701
|
if (tools.length > 0 && model.bindTools) {
|
|
2859
|
-
|
|
2702
|
+
logger2.debug("Binding tools to model", {
|
|
2860
2703
|
workerId: id,
|
|
2861
2704
|
toolCount: tools.length,
|
|
2862
2705
|
toolNames: tools.map((t) => t.metadata.name)
|
|
2863
2706
|
});
|
|
2864
|
-
const langchainTools = (0,
|
|
2707
|
+
const langchainTools = (0, import_core9.toLangChainTools)(tools);
|
|
2865
2708
|
modelToUse = model.bindTools(langchainTools);
|
|
2866
2709
|
}
|
|
2867
|
-
|
|
2710
|
+
logger2.debug("Invoking LLM", { workerId: id });
|
|
2868
2711
|
const response = await modelToUse.invoke(messages);
|
|
2869
2712
|
const result = typeof response.content === "string" ? response.content : JSON.stringify(response.content);
|
|
2870
|
-
|
|
2713
|
+
logger2.info("Worker task completed", {
|
|
2871
2714
|
workerId: id,
|
|
2872
2715
|
assignmentId: currentAssignment.id,
|
|
2873
2716
|
resultLength: result.length,
|
|
@@ -2902,7 +2745,7 @@ Execute the assigned task using your skills and tools. Provide a clear, actionab
|
|
|
2902
2745
|
currentWorkload: Math.max(0, capabilities.currentWorkload - 1)
|
|
2903
2746
|
}
|
|
2904
2747
|
};
|
|
2905
|
-
|
|
2748
|
+
logger2.debug("Worker state update", {
|
|
2906
2749
|
workerId: id,
|
|
2907
2750
|
newWorkload: updatedWorkers[id].currentWorkload
|
|
2908
2751
|
});
|
|
@@ -2913,7 +2756,7 @@ Execute the assigned task using your skills and tools. Provide a clear, actionab
|
|
|
2913
2756
|
};
|
|
2914
2757
|
} catch (error) {
|
|
2915
2758
|
const errorMessage = handleNodeError(error, `worker:${id}`, false);
|
|
2916
|
-
|
|
2759
|
+
logger2.error("Worker node error", {
|
|
2917
2760
|
workerId: id,
|
|
2918
2761
|
error: errorMessage
|
|
2919
2762
|
});
|
|
@@ -2921,7 +2764,7 @@ Execute the assigned task using your skills and tools. Provide a clear, actionab
|
|
|
2921
2764
|
(assignment) => assignment.workerId === id
|
|
2922
2765
|
);
|
|
2923
2766
|
if (currentAssignment) {
|
|
2924
|
-
|
|
2767
|
+
logger2.warn("Creating error result for assignment", {
|
|
2925
2768
|
workerId: id,
|
|
2926
2769
|
assignmentId: currentAssignment.id
|
|
2927
2770
|
});
|
|
@@ -2939,7 +2782,7 @@ Execute the assigned task using your skills and tools. Provide a clear, actionab
|
|
|
2939
2782
|
status: "routing"
|
|
2940
2783
|
};
|
|
2941
2784
|
}
|
|
2942
|
-
|
|
2785
|
+
logger2.error("No assignment found for error handling", { workerId: id });
|
|
2943
2786
|
return {
|
|
2944
2787
|
status: "failed",
|
|
2945
2788
|
error: errorMessage
|
|
@@ -2956,16 +2799,16 @@ function createAggregatorNode(config = {}) {
|
|
|
2956
2799
|
} = config;
|
|
2957
2800
|
return async (state) => {
|
|
2958
2801
|
try {
|
|
2959
|
-
|
|
2802
|
+
logger2.info("Aggregator node executing", {
|
|
2960
2803
|
completedTasks: state.completedTasks.length,
|
|
2961
2804
|
successfulTasks: state.completedTasks.filter((t) => t.success).length,
|
|
2962
2805
|
failedTasks: state.completedTasks.filter((t) => !t.success).length
|
|
2963
2806
|
});
|
|
2964
|
-
|
|
2807
|
+
logger2.debug("Combining results from workers");
|
|
2965
2808
|
if (aggregateFn) {
|
|
2966
|
-
|
|
2809
|
+
logger2.debug("Using custom aggregation function");
|
|
2967
2810
|
const response2 = await aggregateFn(state);
|
|
2968
|
-
|
|
2811
|
+
logger2.info("Custom aggregation complete", {
|
|
2969
2812
|
responseLength: response2.length
|
|
2970
2813
|
});
|
|
2971
2814
|
return {
|
|
@@ -2974,16 +2817,16 @@ function createAggregatorNode(config = {}) {
|
|
|
2974
2817
|
};
|
|
2975
2818
|
}
|
|
2976
2819
|
if (state.completedTasks.length === 0) {
|
|
2977
|
-
|
|
2820
|
+
logger2.warn("No completed tasks to aggregate");
|
|
2978
2821
|
return {
|
|
2979
2822
|
response: "No tasks were completed.",
|
|
2980
2823
|
status: "completed"
|
|
2981
2824
|
};
|
|
2982
2825
|
}
|
|
2983
2826
|
if (!model) {
|
|
2984
|
-
|
|
2827
|
+
logger2.debug("No model provided, concatenating results");
|
|
2985
2828
|
const combinedResults = state.completedTasks.filter((task) => task.success).map((task) => task.result).join("\n\n");
|
|
2986
|
-
|
|
2829
|
+
logger2.info("Simple concatenation complete", {
|
|
2987
2830
|
resultLength: combinedResults.length
|
|
2988
2831
|
});
|
|
2989
2832
|
return {
|
|
@@ -2991,7 +2834,7 @@ function createAggregatorNode(config = {}) {
|
|
|
2991
2834
|
status: "completed"
|
|
2992
2835
|
};
|
|
2993
2836
|
}
|
|
2994
|
-
|
|
2837
|
+
logger2.debug("Using LLM for intelligent aggregation", {
|
|
2995
2838
|
taskCount: state.completedTasks.length
|
|
2996
2839
|
});
|
|
2997
2840
|
const taskResults = state.completedTasks.map((task, idx) => {
|
|
@@ -3010,20 +2853,20 @@ Please synthesize these results into a comprehensive response that addresses the
|
|
|
3010
2853
|
new import_messages5.SystemMessage(systemPrompt),
|
|
3011
2854
|
new import_messages5.HumanMessage(userPrompt)
|
|
3012
2855
|
];
|
|
3013
|
-
|
|
2856
|
+
logger2.debug("Invoking aggregation LLM");
|
|
3014
2857
|
const response = await model.invoke(messages);
|
|
3015
2858
|
const aggregatedResponse = typeof response.content === "string" ? response.content : JSON.stringify(response.content);
|
|
3016
|
-
|
|
2859
|
+
logger2.info("Aggregation complete", {
|
|
3017
2860
|
responseLength: aggregatedResponse.length,
|
|
3018
2861
|
responsePreview: aggregatedResponse.substring(0, 100)
|
|
3019
2862
|
});
|
|
3020
|
-
|
|
2863
|
+
logger2.debug("Aggregation complete");
|
|
3021
2864
|
return {
|
|
3022
2865
|
response: aggregatedResponse,
|
|
3023
2866
|
status: "completed"
|
|
3024
2867
|
};
|
|
3025
2868
|
} catch (error) {
|
|
3026
|
-
|
|
2869
|
+
logger2.error("Aggregator node error", {
|
|
3027
2870
|
error: error instanceof Error ? error.message : String(error),
|
|
3028
2871
|
stack: error instanceof Error ? error.stack : void 0,
|
|
3029
2872
|
completedTasks: state.completedTasks.length
|
|
@@ -3038,9 +2881,9 @@ Please synthesize these results into a comprehensive response that addresses the
|
|
|
3038
2881
|
|
|
3039
2882
|
// src/multi-agent/agent.ts
|
|
3040
2883
|
var import_langgraph4 = require("@langchain/langgraph");
|
|
3041
|
-
var
|
|
3042
|
-
var
|
|
3043
|
-
var
|
|
2884
|
+
var import_core10 = require("@agentforge/core");
|
|
2885
|
+
var logLevel3 = process.env.LOG_LEVEL?.toLowerCase() || import_core10.LogLevel.INFO;
|
|
2886
|
+
var logger3 = (0, import_core10.createLogger)("multi-agent:system", { level: logLevel3 });
|
|
3044
2887
|
function createMultiAgentSystem(config) {
|
|
3045
2888
|
const {
|
|
3046
2889
|
supervisor,
|
|
@@ -3057,10 +2900,6 @@ function createMultiAgentSystem(config) {
|
|
|
3057
2900
|
if (supervisor.strategy === "llm-based") {
|
|
3058
2901
|
configuredModel = configuredModel.withStructuredOutput(RoutingDecisionSchema);
|
|
3059
2902
|
}
|
|
3060
|
-
if (supervisor.tools && supervisor.tools.length > 0) {
|
|
3061
|
-
const langchainTools = (0, import_core11.toLangChainTools)(supervisor.tools);
|
|
3062
|
-
configuredModel = configuredModel.bindTools(langchainTools);
|
|
3063
|
-
}
|
|
3064
2903
|
supervisorConfig.model = configuredModel;
|
|
3065
2904
|
}
|
|
3066
2905
|
const supervisorNode = createSupervisorNode(supervisorConfig);
|
|
@@ -3082,46 +2921,46 @@ function createMultiAgentSystem(config) {
|
|
|
3082
2921
|
});
|
|
3083
2922
|
workflow.addNode("aggregator", aggregatorNode);
|
|
3084
2923
|
const supervisorRouter = (state) => {
|
|
3085
|
-
|
|
2924
|
+
logger3.debug("Supervisor router executing", {
|
|
3086
2925
|
status: state.status,
|
|
3087
2926
|
currentAgent: state.currentAgent,
|
|
3088
2927
|
iteration: state.iteration
|
|
3089
2928
|
});
|
|
3090
2929
|
if (state.status === "completed" || state.status === "failed") {
|
|
3091
|
-
|
|
2930
|
+
logger3.info("Supervisor router: ending workflow", { status: state.status });
|
|
3092
2931
|
return import_langgraph4.END;
|
|
3093
2932
|
}
|
|
3094
2933
|
if (state.status === "aggregating") {
|
|
3095
|
-
|
|
2934
|
+
logger3.info("Supervisor router: routing to aggregator");
|
|
3096
2935
|
return "aggregator";
|
|
3097
2936
|
}
|
|
3098
2937
|
if (state.currentAgent && state.currentAgent !== "supervisor") {
|
|
3099
2938
|
if (state.currentAgent.includes(",")) {
|
|
3100
2939
|
const agents = state.currentAgent.split(",").map((a) => a.trim());
|
|
3101
|
-
|
|
2940
|
+
logger3.info("Supervisor router: parallel routing", {
|
|
3102
2941
|
agents,
|
|
3103
2942
|
count: agents.length
|
|
3104
2943
|
});
|
|
3105
2944
|
return agents;
|
|
3106
2945
|
}
|
|
3107
|
-
|
|
2946
|
+
logger3.info("Supervisor router: single agent routing", {
|
|
3108
2947
|
targetAgent: state.currentAgent
|
|
3109
2948
|
});
|
|
3110
2949
|
return state.currentAgent;
|
|
3111
2950
|
}
|
|
3112
|
-
|
|
2951
|
+
logger3.debug("Supervisor router: staying at supervisor");
|
|
3113
2952
|
return "supervisor";
|
|
3114
2953
|
};
|
|
3115
2954
|
const workerRouter = (state) => {
|
|
3116
|
-
|
|
2955
|
+
logger3.debug("Worker router executing", {
|
|
3117
2956
|
iteration: state.iteration,
|
|
3118
2957
|
completedTasks: state.completedTasks.length
|
|
3119
2958
|
});
|
|
3120
|
-
|
|
2959
|
+
logger3.debug("Worker router: returning to supervisor");
|
|
3121
2960
|
return "supervisor";
|
|
3122
2961
|
};
|
|
3123
2962
|
const aggregatorRouter = (state) => {
|
|
3124
|
-
|
|
2963
|
+
logger3.info("Aggregator router: ending workflow", {
|
|
3125
2964
|
completedTasks: state.completedTasks.length,
|
|
3126
2965
|
status: state.status
|
|
3127
2966
|
});
|