create-langgraph-app 1.1.0 → 1.2.0
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/create.js +231 -87
- package/package.json +1 -1
package/dist/create.js
CHANGED
|
@@ -72,18 +72,14 @@ function generateEnv(config) {
|
|
|
72
72
|
}
|
|
73
73
|
function generatePackageJson(config) {
|
|
74
74
|
const deps = {
|
|
75
|
-
"@langchain/core": "^1.
|
|
76
|
-
"@langchain/langgraph": "^1.
|
|
77
|
-
"@langchain/langgraph-supervisor": "^1.0.3",
|
|
75
|
+
"@langchain/core": "^1.2.3",
|
|
76
|
+
"@langchain/langgraph": "^1.4.8",
|
|
78
77
|
"@langchain/mcp-adapters": "^1.1.3",
|
|
79
78
|
dotenv: "^17.4.2",
|
|
80
|
-
fastify: "^5.
|
|
81
|
-
langchain: "^1.3
|
|
79
|
+
fastify: "^5.10.0",
|
|
80
|
+
langchain: "^1.5.3",
|
|
82
81
|
zod: "^4.4.3",
|
|
83
82
|
};
|
|
84
|
-
if (config.patterns.includes("swarm")) {
|
|
85
|
-
deps["@langchain/langgraph-swarm"] = "^1.0.2";
|
|
86
|
-
}
|
|
87
83
|
if (config.patterns.includes("rag")) {
|
|
88
84
|
deps["@langchain/textsplitters"] = "^1.0.1";
|
|
89
85
|
}
|
|
@@ -219,46 +215,122 @@ export function getLlm(opts?: LlmOptions): Promise<BaseChatModel> {
|
|
|
219
215
|
`;
|
|
220
216
|
}
|
|
221
217
|
function generateAgentFactory() {
|
|
222
|
-
return `import type
|
|
223
|
-
import type {
|
|
224
|
-
import {
|
|
218
|
+
return `import { createAgent, type ResponseFormat, type TypedToolStrategy } from "langchain";
|
|
219
|
+
import type { BaseChatModel } from "@langchain/core/language_models/chat_models";
|
|
220
|
+
import type { BaseCheckpointSaver } from "@langchain/langgraph-checkpoint";
|
|
221
|
+
|
|
222
|
+
type CreateAgentParams = Parameters<typeof createAgent>[0];
|
|
225
223
|
|
|
226
224
|
export interface MakeAgentParams {
|
|
227
225
|
name: string;
|
|
228
226
|
llm: BaseChatModel;
|
|
229
|
-
tools?:
|
|
227
|
+
tools?: CreateAgentParams["tools"];
|
|
230
228
|
system?: string;
|
|
231
|
-
responseFormat?:
|
|
229
|
+
responseFormat?: ResponseFormat | TypedToolStrategy<Record<string, unknown>>;
|
|
230
|
+
/**
|
|
231
|
+
* Only the outermost agent of a multi-agent setup should get a
|
|
232
|
+
* checkpointer — subagents inherit it at runtime, which is what lets
|
|
233
|
+
* interrupt() calls bubble up to the top-level graph.
|
|
234
|
+
*/
|
|
235
|
+
checkpointer?: BaseCheckpointSaver;
|
|
232
236
|
}
|
|
233
237
|
|
|
234
|
-
|
|
235
|
-
|
|
238
|
+
/** Wraps LangChain's createAgent with a simpler interface. */
|
|
239
|
+
export function makeAgent({
|
|
240
|
+
name,
|
|
241
|
+
llm,
|
|
242
|
+
tools = [],
|
|
243
|
+
system,
|
|
244
|
+
responseFormat,
|
|
245
|
+
checkpointer,
|
|
246
|
+
}: MakeAgentParams) {
|
|
247
|
+
return createAgent({
|
|
236
248
|
name,
|
|
237
|
-
llm,
|
|
249
|
+
model: llm,
|
|
238
250
|
tools,
|
|
239
|
-
...(system ? {
|
|
251
|
+
...(system ? { systemPrompt: system } : {}),
|
|
240
252
|
...(responseFormat ? { responseFormat } : {}),
|
|
253
|
+
...(checkpointer ? { checkpointer } : {}),
|
|
241
254
|
});
|
|
242
255
|
}
|
|
256
|
+
|
|
257
|
+
export type AgentGraph = ReturnType<typeof makeAgent>;
|
|
243
258
|
`;
|
|
244
259
|
}
|
|
245
260
|
function generateSupervisorHelper() {
|
|
246
|
-
return `import
|
|
247
|
-
import
|
|
248
|
-
import {
|
|
249
|
-
import {
|
|
261
|
+
return `import { z } from "zod";
|
|
262
|
+
import { tool } from "@langchain/core/tools";
|
|
263
|
+
import type { BaseChatModel } from "@langchain/core/language_models/chat_models";
|
|
264
|
+
import type { BaseCheckpointSaver } from "@langchain/langgraph-checkpoint";
|
|
265
|
+
import { MemorySaver } from "@langchain/langgraph";
|
|
266
|
+
import { makeAgent, type AgentGraph } from "./factory";
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Supervisor via the "subagents" pattern: a main agent coordinates workers
|
|
270
|
+
* by calling them as tools. Subagents are stateless and run in isolated
|
|
271
|
+
* context windows; only the supervisor gets a checkpointer, so interrupt()
|
|
272
|
+
* inside a subagent tool pauses the whole graph.
|
|
273
|
+
*/
|
|
250
274
|
|
|
251
|
-
|
|
275
|
+
export interface SubagentSpec {
|
|
276
|
+
/** Tool name the supervisor calls, e.g. "math_expert". */
|
|
277
|
+
name: string;
|
|
278
|
+
/** Tells the supervisor's model when to delegate to this agent. */
|
|
279
|
+
description: string;
|
|
280
|
+
agent: AgentGraph;
|
|
281
|
+
}
|
|
252
282
|
|
|
253
|
-
|
|
283
|
+
/** Wraps a compiled agent as a tool the supervisor can call. */
|
|
284
|
+
export function subagentTool({ name, description, agent }: SubagentSpec) {
|
|
285
|
+
return tool(
|
|
286
|
+
async ({ task }) => {
|
|
287
|
+
const result = await agent.invoke({
|
|
288
|
+
messages: [{ role: "user", content: task }],
|
|
289
|
+
});
|
|
290
|
+
const last = result.messages.at(-1);
|
|
291
|
+
if (!last) return "(no response)";
|
|
292
|
+
return typeof last.content === "string"
|
|
293
|
+
? last.content
|
|
294
|
+
: JSON.stringify(last.content);
|
|
295
|
+
},
|
|
296
|
+
{
|
|
297
|
+
name,
|
|
298
|
+
description,
|
|
299
|
+
schema: z.object({
|
|
300
|
+
task: z
|
|
301
|
+
.string()
|
|
302
|
+
.describe("A self-contained task with all context the agent needs"),
|
|
303
|
+
}),
|
|
304
|
+
}
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
export interface MakeSupervisorParams {
|
|
309
|
+
subagents: SubagentSpec[];
|
|
310
|
+
llm: BaseChatModel;
|
|
311
|
+
supervisorName?: string;
|
|
312
|
+
prompt?: string;
|
|
254
313
|
checkpointer?: BaseCheckpointSaver;
|
|
255
|
-
store?: BaseStore;
|
|
256
314
|
}
|
|
257
315
|
|
|
258
|
-
export function makeSupervisor({
|
|
259
|
-
|
|
316
|
+
export function makeSupervisor({
|
|
317
|
+
subagents,
|
|
318
|
+
llm,
|
|
319
|
+
supervisorName = "supervisor",
|
|
320
|
+
prompt,
|
|
321
|
+
checkpointer,
|
|
322
|
+
}: MakeSupervisorParams) {
|
|
323
|
+
const defaultPrompt =
|
|
324
|
+
"You coordinate a team of specialists. Delegate work to them via " +
|
|
325
|
+
\`your tools (\${subagents.map((s) => s.name).join(", ")}) and answer \` +
|
|
326
|
+
"the user only once the delegated work is done.";
|
|
327
|
+
|
|
328
|
+
return makeAgent({
|
|
329
|
+
name: supervisorName,
|
|
330
|
+
llm,
|
|
331
|
+
tools: subagents.map(subagentTool),
|
|
332
|
+
system: prompt ?? defaultPrompt,
|
|
260
333
|
checkpointer: checkpointer ?? new MemorySaver(),
|
|
261
|
-
store: store ?? new InMemoryStore(),
|
|
262
334
|
});
|
|
263
335
|
}
|
|
264
336
|
`;
|
|
@@ -303,8 +375,11 @@ export async function createSupervisorApp() {
|
|
|
303
375
|
});
|
|
304
376
|
|
|
305
377
|
return makeSupervisor({
|
|
306
|
-
|
|
307
|
-
|
|
378
|
+
subagents: [
|
|
379
|
+
{ name: "math_expert", description: "Delegate calculations to the math expert.", agent: math },
|
|
380
|
+
{ name: "writer", description: "Delegate writing. Include all facts the text should contain.", agent: writer },
|
|
381
|
+
],
|
|
382
|
+
llm,
|
|
308
383
|
supervisorName: "supervisor",
|
|
309
384
|
});
|
|
310
385
|
}
|
|
@@ -322,56 +397,131 @@ export async function createSupervisorApp() {
|
|
|
322
397
|
if (config.patterns.includes("swarm")) {
|
|
323
398
|
files.push({
|
|
324
399
|
path: "src/agents/swarm.ts",
|
|
325
|
-
content: `import
|
|
326
|
-
import {
|
|
327
|
-
import {
|
|
400
|
+
content: `import { z } from "zod";
|
|
401
|
+
import { AIMessage } from "@langchain/core/messages";
|
|
402
|
+
import {
|
|
403
|
+
StateGraph,
|
|
404
|
+
StateSchema,
|
|
405
|
+
MessagesValue,
|
|
406
|
+
MemorySaver,
|
|
407
|
+
START,
|
|
408
|
+
END,
|
|
409
|
+
} from "@langchain/langgraph";
|
|
410
|
+
import type { BaseCheckpointSaver } from "@langchain/langgraph-checkpoint";
|
|
411
|
+
import type { AgentGraph } from "./factory";
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* Swarm via the "handoffs" pattern: each agent is a graph node, and handoff
|
|
415
|
+
* tools (see ./handoff.ts) jump between nodes while flipping activeAgent.
|
|
416
|
+
* activeAgent is checkpointed, so the conversation resumes with whichever
|
|
417
|
+
* agent last held it.
|
|
418
|
+
*/
|
|
328
419
|
|
|
329
|
-
export const SwarmState =
|
|
330
|
-
|
|
331
|
-
activeAgent:
|
|
420
|
+
export const SwarmState = new StateSchema({
|
|
421
|
+
messages: MessagesValue,
|
|
422
|
+
activeAgent: z.string().optional(),
|
|
332
423
|
});
|
|
333
424
|
|
|
334
|
-
type
|
|
425
|
+
export type SwarmStateType = typeof SwarmState.State;
|
|
335
426
|
|
|
336
|
-
export
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
427
|
+
export interface SwarmAgentSpec {
|
|
428
|
+
/** Node name; handoff tools reference it as transfer_to_<name>. */
|
|
429
|
+
name: string;
|
|
430
|
+
agent: AgentGraph;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
export interface MakeSwarmParams {
|
|
434
|
+
agents: SwarmAgentSpec[];
|
|
340
435
|
defaultActiveAgent: string;
|
|
341
436
|
checkpointer?: BaseCheckpointSaver;
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
export function makeSwarm({
|
|
440
|
+
agents,
|
|
441
|
+
defaultActiveAgent,
|
|
442
|
+
checkpointer,
|
|
443
|
+
}: MakeSwarmParams) {
|
|
444
|
+
const names = agents.map((a) => a.name);
|
|
445
|
+
|
|
446
|
+
const routeToActive = (state: SwarmStateType) =>
|
|
447
|
+
state.activeAgent ?? defaultActiveAgent;
|
|
448
|
+
|
|
449
|
+
// After an agent runs: done if it answered (no pending tool calls);
|
|
450
|
+
// otherwise a handoff moved activeAgent, so continue there.
|
|
451
|
+
const routeAfterAgent = (state: SwarmStateType) => {
|
|
452
|
+
const last = state.messages.at(-1);
|
|
453
|
+
if (last && AIMessage.isInstance(last) && !last.tool_calls?.length) {
|
|
454
|
+
return END;
|
|
455
|
+
}
|
|
456
|
+
return state.activeAgent ?? defaultActiveAgent;
|
|
457
|
+
};
|
|
458
|
+
|
|
459
|
+
// Node names are only known at runtime, so the graph is built with
|
|
460
|
+
// string keys and the routers are cast to the node-name union.
|
|
461
|
+
let builder = new StateGraph(SwarmState) as StateGraph<
|
|
462
|
+
typeof SwarmState,
|
|
463
|
+
SwarmStateType
|
|
464
|
+
>;
|
|
465
|
+
for (const { name, agent } of agents) {
|
|
466
|
+
builder = builder.addNode(name, async (state: SwarmStateType) =>
|
|
467
|
+
agent.invoke(state)
|
|
468
|
+
) as typeof builder;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
builder.addConditionalEdges(START, routeToActive as never, names as never);
|
|
472
|
+
for (const name of names) {
|
|
473
|
+
builder.addConditionalEdges(
|
|
474
|
+
name as never,
|
|
475
|
+
routeAfterAgent as never,
|
|
476
|
+
[...names, END] as never
|
|
477
|
+
);
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
return builder.compile({
|
|
481
|
+
checkpointer: checkpointer ?? new MemorySaver(),
|
|
482
|
+
});
|
|
346
483
|
}
|
|
347
484
|
`,
|
|
348
485
|
});
|
|
349
486
|
files.push({
|
|
350
487
|
path: "src/agents/handoff.ts",
|
|
351
488
|
content: `import { z } from "zod";
|
|
352
|
-
import {
|
|
353
|
-
import {
|
|
354
|
-
import { Command
|
|
489
|
+
import { tool, type ToolRuntime } from "@langchain/core/tools";
|
|
490
|
+
import { AIMessage, ToolMessage } from "@langchain/core/messages";
|
|
491
|
+
import { Command } from "@langchain/langgraph";
|
|
492
|
+
import type { SwarmStateType } from "./swarm";
|
|
355
493
|
|
|
494
|
+
/**
|
|
495
|
+
* Handoff tool for the handoffs pattern. Returns a Command targeting the
|
|
496
|
+
* PARENT graph: it flips activeAgent and jumps to the target agent's node.
|
|
497
|
+
* The calling agent's last AI message (the one containing this tool call)
|
|
498
|
+
* plus a ToolMessage are copied into parent state so the conversation
|
|
499
|
+
* history stays well-formed.
|
|
500
|
+
*/
|
|
356
501
|
export function createHandoffTool({ agentName, description }: { agentName: string; description?: string }) {
|
|
357
|
-
const toolName = \`transfer_to_\${agentName.replace(/\\s+/g, "_").toLowerCase()}\`;
|
|
358
|
-
|
|
359
502
|
return tool(
|
|
360
|
-
async (
|
|
361
|
-
const
|
|
362
|
-
|
|
363
|
-
|
|
503
|
+
async (_, runtime: ToolRuntime<SwarmStateType>) => {
|
|
504
|
+
const lastAiMessage = [...(runtime.state.messages ?? [])]
|
|
505
|
+
.reverse()
|
|
506
|
+
.find(AIMessage.isInstance);
|
|
507
|
+
const transferMessage = new ToolMessage({
|
|
364
508
|
content: \`Transferred to \${agentName}\`,
|
|
365
|
-
|
|
366
|
-
tool_call_id: cfg.toolCall.id,
|
|
509
|
+
tool_call_id: runtime.toolCallId ?? "",
|
|
367
510
|
});
|
|
368
511
|
return new Command({
|
|
369
512
|
goto: agentName,
|
|
513
|
+
update: {
|
|
514
|
+
activeAgent: agentName,
|
|
515
|
+
messages: [lastAiMessage, transferMessage].filter(Boolean),
|
|
516
|
+
},
|
|
370
517
|
graph: Command.PARENT,
|
|
371
|
-
update: { messages: messages.concat(tm), activeAgent: agentName },
|
|
372
518
|
});
|
|
373
519
|
},
|
|
374
|
-
{
|
|
520
|
+
{
|
|
521
|
+
name: \`transfer_to_\${agentName}\`,
|
|
522
|
+
description: description ?? \`Transfer the conversation to \${agentName}.\`,
|
|
523
|
+
schema: z.object({}),
|
|
524
|
+
}
|
|
375
525
|
);
|
|
376
526
|
}
|
|
377
527
|
`,
|
|
@@ -383,7 +533,7 @@ import { z } from "zod";
|
|
|
383
533
|
import { tool } from "@langchain/core/tools";
|
|
384
534
|
import { makeAgent } from "../agents/factory";
|
|
385
535
|
import { createHandoffTool } from "../agents/handoff";
|
|
386
|
-
import { makeSwarm
|
|
536
|
+
import { makeSwarm } from "../agents/swarm";
|
|
387
537
|
|
|
388
538
|
const add = tool(async ({ a, b }) => String(a + b), {
|
|
389
539
|
name: "add", description: "Add two numbers",
|
|
@@ -411,7 +561,10 @@ export async function createSwarmApp() {
|
|
|
411
561
|
});
|
|
412
562
|
|
|
413
563
|
return makeSwarm({
|
|
414
|
-
agents: [
|
|
564
|
+
agents: [
|
|
565
|
+
{ name: "alice", agent: alice },
|
|
566
|
+
{ name: "bob", agent: bob },
|
|
567
|
+
],
|
|
415
568
|
defaultActiveAgent: "alice",
|
|
416
569
|
});
|
|
417
570
|
}
|
|
@@ -431,10 +584,9 @@ export async function createSwarmApp() {
|
|
|
431
584
|
path: "src/apps/interrupt.ts",
|
|
432
585
|
content: `import { z } from "zod";
|
|
433
586
|
import { tool } from "@langchain/core/tools";
|
|
434
|
-
import { interrupt } from "@langchain/langgraph";
|
|
587
|
+
import { interrupt, MemorySaver } from "@langchain/langgraph";
|
|
435
588
|
import { getLlm } from "../config/llm";
|
|
436
589
|
import { makeAgent } from "../agents/factory";
|
|
437
|
-
import { makeSupervisor } from "../agents/supervisor";
|
|
438
590
|
|
|
439
591
|
const deleteRecord = tool(
|
|
440
592
|
async (args) => {
|
|
@@ -457,16 +609,13 @@ const deleteRecord = tool(
|
|
|
457
609
|
export async function createInterruptApp() {
|
|
458
610
|
const llm = await getLlm();
|
|
459
611
|
|
|
460
|
-
|
|
612
|
+
// A single agent with a checkpointer — interrupt() inside delete_record
|
|
613
|
+
// pauses the graph; resume with Command({ resume: "yes" }) on the thread.
|
|
614
|
+
return makeAgent({
|
|
461
615
|
name: "db_admin", llm,
|
|
462
616
|
tools: [deleteRecord],
|
|
463
617
|
system: "You are a database administrator.",
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
return makeSupervisor({
|
|
467
|
-
agents: [dbAdmin], llm,
|
|
468
|
-
outputMode: "last_message",
|
|
469
|
-
supervisorName: "interrupt_supervisor",
|
|
618
|
+
checkpointer: new MemorySaver(),
|
|
470
619
|
});
|
|
471
620
|
}
|
|
472
621
|
`,
|
|
@@ -491,9 +640,10 @@ export async function createInterruptApp() {
|
|
|
491
640
|
files.push({
|
|
492
641
|
path: "src/apps/analyst.ts",
|
|
493
642
|
content: `import { z } from "zod";
|
|
643
|
+
import { toolStrategy } from "langchain";
|
|
644
|
+
import { MemorySaver } from "@langchain/langgraph";
|
|
494
645
|
import { getLlm } from "../config/llm";
|
|
495
646
|
import { makeAgent } from "../agents/factory";
|
|
496
|
-
import { makeSupervisor } from "../agents/supervisor";
|
|
497
647
|
|
|
498
648
|
const SummarySchema = z.object({
|
|
499
649
|
title: z.string(),
|
|
@@ -504,16 +654,14 @@ const SummarySchema = z.object({
|
|
|
504
654
|
export async function createAnalystApp() {
|
|
505
655
|
const llm = await getLlm();
|
|
506
656
|
|
|
507
|
-
|
|
657
|
+
// The structured result is returned on the structuredResponse key of the
|
|
658
|
+
// final state. toolStrategy works with every provider; models with native
|
|
659
|
+
// structured output support could use providerStrategy instead.
|
|
660
|
+
return makeAgent({
|
|
508
661
|
name: "analyst", llm, tools: [],
|
|
509
662
|
system: "Analyze text and produce structured summaries.",
|
|
510
|
-
responseFormat: SummarySchema,
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
return makeSupervisor({
|
|
514
|
-
agents: [analyst], llm,
|
|
515
|
-
outputMode: "last_message",
|
|
516
|
-
supervisorName: "analyst_supervisor",
|
|
663
|
+
responseFormat: toolStrategy(SummarySchema),
|
|
664
|
+
checkpointer: new MemorySaver(),
|
|
517
665
|
});
|
|
518
666
|
}
|
|
519
667
|
`,
|
|
@@ -525,14 +673,15 @@ export async function createAnalystApp() {
|
|
|
525
673
|
{ messages: [{ role: "user", content: "Analyze: Revenue grew 25% but churn increased 8%." }] },
|
|
526
674
|
{ configurable: { thread_id: "analyst-demo" } }
|
|
527
675
|
);
|
|
528
|
-
|
|
676
|
+
const structured = (analysis as Record<string, unknown>).structuredResponse;
|
|
677
|
+
console.log("Result:", JSON.stringify(structured ?? analysis.messages.at(-1)?.content));`);
|
|
529
678
|
}
|
|
530
679
|
if (config.patterns.includes("rag")) {
|
|
531
680
|
files.push({
|
|
532
681
|
path: "src/apps/rag.ts",
|
|
533
|
-
content: `import {
|
|
682
|
+
content: `import { MemorySaver } from "@langchain/langgraph";
|
|
683
|
+
import { getLlm } from "../config/llm";
|
|
534
684
|
import { makeAgent } from "../agents/factory";
|
|
535
|
-
import { makeSupervisor } from "../agents/supervisor";
|
|
536
685
|
// TODO: Add your vector store, embeddings, and retrieval tool here.
|
|
537
686
|
// See the full starter kit for a complete RAG implementation:
|
|
538
687
|
// https://github.com/ac12644/langgraph-starter-kit
|
|
@@ -540,15 +689,10 @@ import { makeSupervisor } from "../agents/supervisor";
|
|
|
540
689
|
export async function createRagApp() {
|
|
541
690
|
const llm = await getLlm();
|
|
542
691
|
|
|
543
|
-
|
|
692
|
+
return makeAgent({
|
|
544
693
|
name: "rag_agent", llm, tools: [],
|
|
545
694
|
system: "You are a knowledgeable assistant. Answer questions based on your knowledge.",
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
return makeSupervisor({
|
|
549
|
-
agents: [ragAgent], llm,
|
|
550
|
-
outputMode: "last_message",
|
|
551
|
-
supervisorName: "rag_supervisor",
|
|
695
|
+
checkpointer: new MemorySaver(),
|
|
552
696
|
});
|
|
553
697
|
}
|
|
554
698
|
`,
|