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.
Files changed (2) hide show
  1. package/dist/create.js +231 -87
  2. 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.1.48",
76
- "@langchain/langgraph": "^1.3.2",
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.8.5",
81
- langchain: "^1.3.0",
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 { BaseChatModel } from "@langchain/core/language_models/chat_models";
223
- import type { StructuredToolInterface } from "@langchain/core/tools";
224
- import { createReactAgent, type CreateReactAgentParams } from "@langchain/langgraph/prebuilt";
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?: StructuredToolInterface[];
227
+ tools?: CreateAgentParams["tools"];
230
228
  system?: string;
231
- responseFormat?: CreateReactAgentParams["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
- export function makeAgent({ name, llm, tools = [], system, responseFormat }: MakeAgentParams) {
235
- return createReactAgent({
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 ? { prompt: 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 type { BaseChatModel } from "@langchain/core/language_models/chat_models";
247
- import type { BaseCheckpointSaver, BaseStore } from "@langchain/langgraph-checkpoint";
248
- import { createSupervisor } from "@langchain/langgraph-supervisor";
249
- import { MemorySaver, InMemoryStore } from "@langchain/langgraph";
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
- type SupervisorParams = Parameters<typeof createSupervisor>[0];
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
- export interface MakeSupervisorParams extends SupervisorParams {
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({ checkpointer, store, ...params }: MakeSupervisorParams) {
259
- return createSupervisor(params).compile({
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
- agents: [math, writer], llm,
307
- outputMode: "last_message",
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 type { BaseCheckpointSaver, BaseStore } from "@langchain/langgraph-checkpoint";
326
- import { MessagesAnnotation, Annotation, MemorySaver } from "@langchain/langgraph";
327
- import { createSwarm } from "@langchain/langgraph-swarm";
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 = Annotation.Root({
330
- ...MessagesAnnotation.spec,
331
- activeAgent: Annotation<string>(),
420
+ export const SwarmState = new StateSchema({
421
+ messages: MessagesValue,
422
+ activeAgent: z.string().optional(),
332
423
  });
333
424
 
334
- type SwarmParams = Parameters<typeof createSwarm>[0];
425
+ export type SwarmStateType = typeof SwarmState.State;
335
426
 
336
- export function makeSwarm({
337
- agents, defaultActiveAgent, checkpointer,
338
- }: {
339
- agents: SwarmParams["agents"];
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
- store?: BaseStore;
343
- }) {
344
- return createSwarm({ agents, defaultActiveAgent, stateSchema: SwarmState })
345
- .compile({ checkpointer: checkpointer ?? new MemorySaver() });
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 { ToolMessage } from "@langchain/core/messages";
353
- import { tool } from "@langchain/core/tools";
354
- import { Command, MessagesAnnotation, getCurrentTaskInput } from "@langchain/langgraph";
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 (_args, cfg) => {
361
- const state = getCurrentTaskInput() as (typeof MessagesAnnotation)["State"];
362
- const messages = state.messages ?? [];
363
- const tm = new ToolMessage({
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
- name: toolName,
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
- { name: toolName, description: description ?? \`Ask \${agentName} for help\`, schema: z.object({}) }
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, type SwarmState } from "../agents/swarm";
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: [alice, bob] as any,
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
- const dbAdmin = makeAgent({
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
- const analyst = makeAgent({
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
- console.log("Result:", analysis.messages.at(-1)?.content);`);
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 { getLlm } from "../config/llm";
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
- const ragAgent = makeAgent({
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
  `,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-langgraph-app",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "Scaffold a new LangGraph multi-agent project in seconds",
5
5
  "type": "module",
6
6
  "bin": {