create-langgraph-app 1.0.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 +308 -183
  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.39",
76
- "@langchain/langgraph": "^1.2.7",
77
- "@langchain/langgraph-supervisor": "^1.0.1",
75
+ "@langchain/core": "^1.2.3",
76
+ "@langchain/langgraph": "^1.4.8",
78
77
  "@langchain/mcp-adapters": "^1.1.3",
79
- dotenv: "^17.4.0",
80
- fastify: "^5.8.4",
81
- langchain: "^1.3.0",
82
- zod: "^4.3.6",
78
+ dotenv: "^17.4.2",
79
+ fastify: "^5.10.0",
80
+ langchain: "^1.5.3",
81
+ zod: "^4.4.3",
83
82
  };
84
- if (config.patterns.includes("swarm")) {
85
- deps["@langchain/langgraph-swarm"] = "^1.0.1";
86
- }
87
83
  if (config.patterns.includes("rag")) {
88
84
  deps["@langchain/textsplitters"] = "^1.0.1";
89
85
  }
@@ -109,10 +105,10 @@ function generatePackageJson(config) {
109
105
  },
110
106
  dependencies: Object.fromEntries(Object.entries(deps).sort(([a], [b]) => a.localeCompare(b))),
111
107
  devDependencies: {
112
- "@types/node": "^25.5.2",
113
- tsx: "^4.21.0",
114
- typescript: "^6.0.2",
115
- vitest: "^4.1.2",
108
+ "@types/node": "^25.9.1",
109
+ tsx: "^4.22.4",
110
+ typescript: "^6.0.3",
111
+ vitest: "^4.1.8",
116
112
  },
117
113
  };
118
114
  return JSON.stringify(pkg, null, 2) + "\n";
@@ -157,6 +153,7 @@ const API_KEY_MAP: Record<LlmProvider, string> = {
157
153
 
158
154
  export const LLM_PROVIDER = resolveProvider();
159
155
 
156
+ // Fail fast: validate the selected provider's key at startup.
160
157
  const requiredKey = API_KEY_MAP[LLM_PROVIDER];
161
158
  if (requiredKey && !process.env[requiredKey]) {
162
159
  throw new Error(\`\${requiredKey} is required for provider "\${LLM_PROVIDER}" but not set in .env\`);
@@ -167,127 +164,173 @@ export const LLM_TEMPERATURE = Number(process.env.LLM_TEMPERATURE ?? 0);
167
164
  export const PORT = Number(process.env.PORT ?? 3000);
168
165
  `;
169
166
  }
170
- function generateLlmConfig() {
171
- return `import type { BaseChatModel } from "@langchain/core/language_models/chat_models";
172
- import { LLM_PROVIDER, LLM_MODEL, LLM_TEMPERATURE } from "./env";
173
-
174
- const DEFAULTS: Record<string, string> = {
175
- openai: "gpt-4o-mini",
176
- anthropic: "claude-sonnet-4-20250514",
177
- google: "gemini-2.0-flash",
178
- groq: "llama-3.3-70b-versatile",
179
- ollama: "llama3.2",
167
+ // Per-provider class + constructor shape. The generated llm.ts only imports the
168
+ // provider that was selected, so the scaffold depends on a single LLM SDK.
169
+ const PROVIDER_LLM = {
170
+ openai: { pkg: "@langchain/openai", className: "ChatOpenAI", modelArg: "modelName", defaultModel: "gpt-4o-mini" },
171
+ anthropic: { pkg: "@langchain/anthropic", className: "ChatAnthropic", modelArg: "modelName", defaultModel: "claude-sonnet-4-20250514" },
172
+ google: { pkg: "@langchain/google-genai", className: "ChatGoogleGenerativeAI", modelArg: "model", defaultModel: "gemini-2.0-flash" },
173
+ groq: { pkg: "@langchain/groq", className: "ChatGroq", modelArg: "model", defaultModel: "llama-3.3-70b-versatile" },
174
+ ollama: { pkg: "@langchain/ollama", className: "ChatOllama", modelArg: "model", defaultModel: "llama3.2" },
180
175
  };
176
+ function generateLlmConfig(config) {
177
+ const p = PROVIDER_LLM[config.provider] ?? PROVIDER_LLM.openai;
178
+ return `import type { BaseChatModel } from "@langchain/core/language_models/chat_models";
179
+ import { LLM_MODEL, LLM_TEMPERATURE } from "./env";
181
180
 
182
- function createLlm(): BaseChatModel {
183
- const model = LLM_MODEL ?? DEFAULTS[LLM_PROVIDER] ?? DEFAULTS.openai;
184
- const temperature = LLM_TEMPERATURE;
181
+ const DEFAULT_MODEL = "${p.defaultModel}";
185
182
 
186
- switch (LLM_PROVIDER) {
187
- case "anthropic": {
188
- const { ChatAnthropic } = require("@langchain/anthropic");
189
- return new ChatAnthropic({ modelName: model, temperature });
190
- }
191
- case "google": {
192
- const { ChatGoogleGenerativeAI } = require("@langchain/google-genai");
193
- return new ChatGoogleGenerativeAI({ modelName: model, temperature });
194
- }
195
- case "groq": {
196
- const { ChatGroq } = require("@langchain/groq");
197
- return new ChatGroq({ modelName: model, temperature });
198
- }
199
- case "ollama": {
200
- const { ChatOllama } = require("@langchain/ollama");
201
- return new ChatOllama({ model, temperature });
202
- }
203
- default: {
204
- const { ChatOpenAI } = require("@langchain/openai");
205
- return new ChatOpenAI({ modelName: model, temperature });
206
- }
207
- }
183
+ export interface LlmOptions {
184
+ model?: string;
185
+ temperature?: number;
186
+ }
187
+
188
+ async function createLlm(opts: LlmOptions = {}): Promise<BaseChatModel> {
189
+ const model = opts.model ?? LLM_MODEL ?? DEFAULT_MODEL;
190
+ const temperature = opts.temperature ?? LLM_TEMPERATURE;
191
+
192
+ const { ${p.className} } = await import("${p.pkg}");
193
+ return new ${p.className}({ ${p.modelArg}: model, temperature });
208
194
  }
209
195
 
210
- export const llm = createLlm();
196
+ let _default: Promise<BaseChatModel> | undefined;
197
+
198
+ /**
199
+ * Returns a chat model.
200
+ *
201
+ * Called with no options it returns a memoized, shared instance (nothing is
202
+ * constructed until first use). Pass options to build a distinct model, e.g. a
203
+ * cheap router and a strong worker:
204
+ *
205
+ * const router = await getLlm({ model: "${p.defaultModel}" });
206
+ * const worker = await getLlm({ model: "<a-stronger-model>" });
207
+ */
208
+ export function getLlm(opts?: LlmOptions): Promise<BaseChatModel> {
209
+ if (!opts || Object.keys(opts).length === 0) {
210
+ _default ??= createLlm();
211
+ return _default;
212
+ }
213
+ return createLlm(opts);
214
+ }
211
215
  `;
212
216
  }
213
217
  function generateAgentFactory() {
214
- return `import type { BaseChatModel } from "@langchain/core/language_models/chat_models";
215
- import type { StructuredToolInterface } from "@langchain/core/tools";
216
- 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];
217
223
 
218
224
  export interface MakeAgentParams {
219
225
  name: string;
220
226
  llm: BaseChatModel;
221
- tools?: StructuredToolInterface[];
227
+ tools?: CreateAgentParams["tools"];
222
228
  system?: string;
223
- 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;
224
236
  }
225
237
 
226
- export function makeAgent({ name, llm, tools = [], system, responseFormat }: MakeAgentParams) {
227
- 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({
228
248
  name,
229
- llm,
249
+ model: llm,
230
250
  tools,
231
- ...(system ? { prompt: system } : {}),
251
+ ...(system ? { systemPrompt: system } : {}),
232
252
  ...(responseFormat ? { responseFormat } : {}),
253
+ ...(checkpointer ? { checkpointer } : {}),
233
254
  });
234
255
  }
256
+
257
+ export type AgentGraph = ReturnType<typeof makeAgent>;
235
258
  `;
236
259
  }
237
260
  function generateSupervisorHelper() {
238
- return `import type { BaseChatModel } from "@langchain/core/language_models/chat_models";
239
- import type { BaseCheckpointSaver, BaseStore } from "@langchain/langgraph-checkpoint";
240
- import { createSupervisor } from "@langchain/langgraph-supervisor";
241
- 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
+ */
274
+
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
+ }
242
282
 
243
- type SupervisorParams = Parameters<typeof createSupervisor>[0];
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
+ }
244
307
 
245
- export interface MakeSupervisorParams extends SupervisorParams {
308
+ export interface MakeSupervisorParams {
309
+ subagents: SubagentSpec[];
310
+ llm: BaseChatModel;
311
+ supervisorName?: string;
312
+ prompt?: string;
246
313
  checkpointer?: BaseCheckpointSaver;
247
- store?: BaseStore;
248
314
  }
249
315
 
250
- export function makeSupervisor({ checkpointer, store, ...params }: MakeSupervisorParams) {
251
- 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,
252
333
  checkpointer: checkpointer ?? new MemorySaver(),
253
- store: store ?? new InMemoryStore(),
254
- });
255
- }
256
- `;
257
- }
258
- function generateSupervisorApp() {
259
- return `import { z } from "zod";
260
- import { tool } from "@langchain/core/tools";
261
- import { llm } from "./config/env";
262
- import { makeAgent } from "./agents/factory";
263
- import { makeSupervisor } from "./agents/supervisor";
264
-
265
- const add = tool(async ({ a, b }) => String(a + b), {
266
- name: "add", description: "Add two numbers",
267
- schema: z.object({ a: z.number(), b: z.number() }),
268
- });
269
-
270
- const multiply = tool(async ({ a, b }) => String(a * b), {
271
- name: "multiply", description: "Multiply two numbers",
272
- schema: z.object({ a: z.number(), b: z.number() }),
273
- });
274
-
275
- export function createApp() {
276
- const math = makeAgent({
277
- name: "math_expert", llm,
278
- tools: [add, multiply],
279
- system: "You are a math expert. Use tools to compute answers.",
280
- });
281
-
282
- const writer = makeAgent({
283
- name: "writer", llm, tools: [],
284
- system: "You write crisp, structured answers.",
285
- });
286
-
287
- return makeSupervisor({
288
- agents: [math, writer], llm,
289
- outputMode: "last_message",
290
- supervisorName: "supervisor",
291
334
  });
292
335
  }
293
336
  `;
@@ -297,7 +340,7 @@ function getPatternFiles(config) {
297
340
  const files = [];
298
341
  // Always include core files
299
342
  files.push({ path: "src/config/env.ts", content: generateEnvConfig(config) });
300
- files.push({ path: "src/config/llm.ts", content: generateLlmConfig() });
343
+ files.push({ path: "src/config/llm.ts", content: generateLlmConfig(config) });
301
344
  files.push({ path: "src/agents/factory.ts", content: generateAgentFactory() });
302
345
  files.push({ path: "src/agents/supervisor.ts", content: generateSupervisorHelper() });
303
346
  // Index file — imports vary by selected patterns
@@ -306,7 +349,7 @@ function getPatternFiles(config) {
306
349
  if (config.patterns.includes("supervisor")) {
307
350
  files.push({
308
351
  path: "src/apps/supervisor.ts",
309
- content: `import { llm } from "../config/llm";
352
+ content: `import { getLlm } from "../config/llm";
310
353
  import { z } from "zod";
311
354
  import { tool } from "@langchain/core/tools";
312
355
  import { makeAgent } from "../agents/factory";
@@ -317,7 +360,9 @@ const add = tool(async ({ a, b }) => String(a + b), {
317
360
  schema: z.object({ a: z.number(), b: z.number() }),
318
361
  });
319
362
 
320
- export function createSupervisorApp() {
363
+ export async function createSupervisorApp() {
364
+ const llm = await getLlm();
365
+
321
366
  const math = makeAgent({
322
367
  name: "math_expert", llm,
323
368
  tools: [add],
@@ -330,8 +375,11 @@ export function createSupervisorApp() {
330
375
  });
331
376
 
332
377
  return makeSupervisor({
333
- agents: [math, writer], llm,
334
- 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,
335
383
  supervisorName: "supervisor",
336
384
  });
337
385
  }
@@ -339,7 +387,7 @@ export function createSupervisorApp() {
339
387
  });
340
388
  imports.push(`import { createSupervisorApp } from "./apps/supervisor";`);
341
389
  demos.push(` console.log("=== Supervisor Demo ===");
342
- const supervisorApp = createSupervisorApp();
390
+ const supervisorApp = await createSupervisorApp();
343
391
  const sup = await supervisorApp.invoke(
344
392
  { messages: [{ role: "user", content: "What is 10 + 15?" }] },
345
393
  { configurable: { thread_id: "demo" } }
@@ -349,68 +397,143 @@ export function createSupervisorApp() {
349
397
  if (config.patterns.includes("swarm")) {
350
398
  files.push({
351
399
  path: "src/agents/swarm.ts",
352
- content: `import type { BaseCheckpointSaver, BaseStore } from "@langchain/langgraph-checkpoint";
353
- import { MessagesAnnotation, Annotation, MemorySaver } from "@langchain/langgraph";
354
- import { createSwarm } from "@langchain/langgraph-swarm";
355
-
356
- export const SwarmState = Annotation.Root({
357
- ...MessagesAnnotation.spec,
358
- activeAgent: Annotation<string>(),
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
+ */
419
+
420
+ export const SwarmState = new StateSchema({
421
+ messages: MessagesValue,
422
+ activeAgent: z.string().optional(),
359
423
  });
360
424
 
361
- type SwarmParams = Parameters<typeof createSwarm>[0];
425
+ export type SwarmStateType = typeof SwarmState.State;
362
426
 
363
- export function makeSwarm({
364
- agents, defaultActiveAgent, checkpointer,
365
- }: {
366
- 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[];
367
435
  defaultActiveAgent: string;
368
436
  checkpointer?: BaseCheckpointSaver;
369
- store?: BaseStore;
370
- }) {
371
- return createSwarm({ agents, defaultActiveAgent, stateSchema: SwarmState })
372
- .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
+ });
373
483
  }
374
484
  `,
375
485
  });
376
486
  files.push({
377
487
  path: "src/agents/handoff.ts",
378
488
  content: `import { z } from "zod";
379
- import { ToolMessage } from "@langchain/core/messages";
380
- import { tool } from "@langchain/core/tools";
381
- import { Command, MessagesAnnotation, getCurrentTaskInput } from "@langchain/langgraph";
382
-
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";
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
+ */
383
501
  export function createHandoffTool({ agentName, description }: { agentName: string; description?: string }) {
384
- const toolName = \`transfer_to_\${agentName.replace(/\\s+/g, "_").toLowerCase()}\`;
385
-
386
502
  return tool(
387
- async (_args, cfg) => {
388
- const state = getCurrentTaskInput() as (typeof MessagesAnnotation)["State"];
389
- const messages = state.messages ?? [];
390
- 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({
391
508
  content: \`Transferred to \${agentName}\`,
392
- name: toolName,
393
- tool_call_id: cfg.toolCall.id,
509
+ tool_call_id: runtime.toolCallId ?? "",
394
510
  });
395
511
  return new Command({
396
512
  goto: agentName,
513
+ update: {
514
+ activeAgent: agentName,
515
+ messages: [lastAiMessage, transferMessage].filter(Boolean),
516
+ },
397
517
  graph: Command.PARENT,
398
- update: { messages: messages.concat(tm), activeAgent: agentName },
399
518
  });
400
519
  },
401
- { 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
+ }
402
525
  );
403
526
  }
404
527
  `,
405
528
  });
406
529
  files.push({
407
530
  path: "src/apps/swarm.ts",
408
- content: `import { llm } from "../config/llm";
531
+ content: `import { getLlm } from "../config/llm";
409
532
  import { z } from "zod";
410
533
  import { tool } from "@langchain/core/tools";
411
534
  import { makeAgent } from "../agents/factory";
412
535
  import { createHandoffTool } from "../agents/handoff";
413
- import { makeSwarm, type SwarmState } from "../agents/swarm";
536
+ import { makeSwarm } from "../agents/swarm";
414
537
 
415
538
  const add = tool(async ({ a, b }) => String(a + b), {
416
539
  name: "add", description: "Add two numbers",
@@ -422,7 +545,9 @@ const multiply = tool(async ({ a, b }) => String(a * b), {
422
545
  schema: z.object({ a: z.number(), b: z.number() }),
423
546
  });
424
547
 
425
- export function createSwarmApp() {
548
+ export async function createSwarmApp() {
549
+ const llm = await getLlm();
550
+
426
551
  const alice = makeAgent({
427
552
  name: "alice", llm,
428
553
  tools: [add, createHandoffTool({ agentName: "bob" })],
@@ -436,7 +561,10 @@ export function createSwarmApp() {
436
561
  });
437
562
 
438
563
  return makeSwarm({
439
- agents: [alice, bob] as any,
564
+ agents: [
565
+ { name: "alice", agent: alice },
566
+ { name: "bob", agent: bob },
567
+ ],
440
568
  defaultActiveAgent: "alice",
441
569
  });
442
570
  }
@@ -444,7 +572,7 @@ export function createSwarmApp() {
444
572
  });
445
573
  imports.push(`import { createSwarmApp } from "./apps/swarm";`);
446
574
  demos.push(` console.log("\\n=== Swarm Demo ===");
447
- const swarmApp = createSwarmApp();
575
+ const swarmApp = await createSwarmApp();
448
576
  const swarm = await swarmApp.invoke(
449
577
  { messages: [{ role: "user", content: "add 5 and 7, then talk to bob and multiply by 3" }] },
450
578
  { configurable: { thread_id: "swarm-demo" } }
@@ -456,10 +584,9 @@ export function createSwarmApp() {
456
584
  path: "src/apps/interrupt.ts",
457
585
  content: `import { z } from "zod";
458
586
  import { tool } from "@langchain/core/tools";
459
- import { interrupt } from "@langchain/langgraph";
460
- import { llm } from "../config/llm";
587
+ import { interrupt, MemorySaver } from "@langchain/langgraph";
588
+ import { getLlm } from "../config/llm";
461
589
  import { makeAgent } from "../agents/factory";
462
- import { makeSupervisor } from "../agents/supervisor";
463
590
 
464
591
  const deleteRecord = tool(
465
592
  async (args) => {
@@ -479,17 +606,16 @@ const deleteRecord = tool(
479
606
  }
480
607
  );
481
608
 
482
- export function createInterruptApp() {
483
- const dbAdmin = makeAgent({
609
+ export async function createInterruptApp() {
610
+ const llm = await getLlm();
611
+
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({
484
615
  name: "db_admin", llm,
485
616
  tools: [deleteRecord],
486
617
  system: "You are a database administrator.",
487
- });
488
-
489
- return makeSupervisor({
490
- agents: [dbAdmin], llm,
491
- outputMode: "last_message",
492
- supervisorName: "interrupt_supervisor",
618
+ checkpointer: new MemorySaver(),
493
619
  });
494
620
  }
495
621
  `,
@@ -497,7 +623,7 @@ export function createInterruptApp() {
497
623
  imports.push(`import { Command } from "@langchain/langgraph";`);
498
624
  imports.push(`import { createInterruptApp } from "./apps/interrupt";`);
499
625
  demos.push(` console.log("\\n=== Human-in-the-Loop Demo ===");
500
- const interruptApp = createInterruptApp();
626
+ const interruptApp = await createInterruptApp();
501
627
  const hitlCfg = { configurable: { thread_id: "hitl-demo" } };
502
628
  await interruptApp.invoke(
503
629
  { messages: [{ role: "user", content: "delete record rec_2" }] },
@@ -514,9 +640,10 @@ export function createInterruptApp() {
514
640
  files.push({
515
641
  path: "src/apps/analyst.ts",
516
642
  content: `import { z } from "zod";
517
- import { llm } from "../config/llm";
643
+ import { toolStrategy } from "langchain";
644
+ import { MemorySaver } from "@langchain/langgraph";
645
+ import { getLlm } from "../config/llm";
518
646
  import { makeAgent } from "../agents/factory";
519
- import { makeSupervisor } from "../agents/supervisor";
520
647
 
521
648
  const SummarySchema = z.object({
522
649
  title: z.string(),
@@ -524,57 +651,55 @@ const SummarySchema = z.object({
524
651
  sentiment: z.enum(["positive", "negative", "neutral"]),
525
652
  });
526
653
 
527
- export function createAnalystApp() {
528
- const analyst = makeAgent({
654
+ export async function createAnalystApp() {
655
+ const llm = await getLlm();
656
+
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({
529
661
  name: "analyst", llm, tools: [],
530
662
  system: "Analyze text and produce structured summaries.",
531
- responseFormat: SummarySchema,
532
- });
533
-
534
- return makeSupervisor({
535
- agents: [analyst], llm,
536
- outputMode: "last_message",
537
- supervisorName: "analyst_supervisor",
663
+ responseFormat: toolStrategy(SummarySchema),
664
+ checkpointer: new MemorySaver(),
538
665
  });
539
666
  }
540
667
  `,
541
668
  });
542
669
  imports.push(`import { createAnalystApp } from "./apps/analyst";`);
543
670
  demos.push(` console.log("\\n=== Structured Output Demo ===");
544
- const analystApp = createAnalystApp();
671
+ const analystApp = await createAnalystApp();
545
672
  const analysis = await analystApp.invoke(
546
673
  { messages: [{ role: "user", content: "Analyze: Revenue grew 25% but churn increased 8%." }] },
547
674
  { configurable: { thread_id: "analyst-demo" } }
548
675
  );
549
- 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));`);
550
678
  }
551
679
  if (config.patterns.includes("rag")) {
552
680
  files.push({
553
681
  path: "src/apps/rag.ts",
554
- content: `import { llm } from "../config/llm";
682
+ content: `import { MemorySaver } from "@langchain/langgraph";
683
+ import { getLlm } from "../config/llm";
555
684
  import { makeAgent } from "../agents/factory";
556
- import { makeSupervisor } from "../agents/supervisor";
557
685
  // TODO: Add your vector store, embeddings, and retrieval tool here.
558
686
  // See the full starter kit for a complete RAG implementation:
559
687
  // https://github.com/ac12644/langgraph-starter-kit
560
688
 
561
- export function createRagApp() {
562
- const ragAgent = makeAgent({
689
+ export async function createRagApp() {
690
+ const llm = await getLlm();
691
+
692
+ return makeAgent({
563
693
  name: "rag_agent", llm, tools: [],
564
694
  system: "You are a knowledgeable assistant. Answer questions based on your knowledge.",
565
- });
566
-
567
- return makeSupervisor({
568
- agents: [ragAgent], llm,
569
- outputMode: "last_message",
570
- supervisorName: "rag_supervisor",
695
+ checkpointer: new MemorySaver(),
571
696
  });
572
697
  }
573
698
  `,
574
699
  });
575
700
  imports.push(`import { createRagApp } from "./apps/rag";`);
576
701
  demos.push(` console.log("\\n=== RAG Demo ===");
577
- const ragApp = createRagApp();
702
+ const ragApp = await createRagApp();
578
703
  const rag = await ragApp.invoke(
579
704
  { messages: [{ role: "user", content: "What is RAG and how does it work?" }] },
580
705
  { configurable: { thread_id: "rag-demo" } }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-langgraph-app",
3
- "version": "1.0.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": {