@langchain/langgraph-api 0.0.54 → 0.0.56

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/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # @langchain/langgraph-api
2
2
 
3
+ ## 0.0.56
4
+
5
+ ### Patch Changes
6
+
7
+ - 3c390c9: fix(api): parser: sanitise generated symbol names, honor typescript extension
8
+ - @langchain/langgraph-ui@0.0.56
9
+
10
+ ## 0.0.55
11
+
12
+ ### Patch Changes
13
+
14
+ - ef84039: fix(api): place the schema inference template next to the graph code, use whole path for symbol name
15
+ - 7edf347: exlcude meta routes from auth
16
+ - 77b21d5: add installed langgraph version to info endpoint for js server
17
+ - @langchain/langgraph-ui@0.0.55
18
+
3
19
  ## 0.0.54
4
20
 
5
21
  ### Patch Changes
package/dist/api/meta.mjs CHANGED
@@ -6,6 +6,7 @@ const api = new Hono();
6
6
  // Get the version using the same pattern as semver/index.mts
7
7
  const packageJsonPath = path.resolve(url.fileURLToPath(import.meta.url), "../../../package.json");
8
8
  let version;
9
+ let langgraph_js_version;
9
10
  try {
10
11
  const packageJson = JSON.parse(await fs.readFile(packageJsonPath, "utf-8"));
11
12
  version = packageJson.version;
@@ -13,6 +14,16 @@ try {
13
14
  catch {
14
15
  console.warn("Could not determine version of langgraph-api");
15
16
  }
17
+ // Get the installed version of @langchain/langgraph
18
+ try {
19
+ const langgraphPkg = await import("@langchain/langgraph/package.json");
20
+ if (langgraphPkg?.default?.version) {
21
+ langgraph_js_version = langgraphPkg.default.version;
22
+ }
23
+ }
24
+ catch {
25
+ console.warn("Could not determine version of @langchain/langgraph");
26
+ }
16
27
  // read env variable
17
28
  const env = process.env;
18
29
  api.get("/info", (c) => {
@@ -33,6 +44,7 @@ api.get("/info", (c) => {
33
44
  })();
34
45
  return c.json({
35
46
  version,
47
+ langgraph_js_version,
36
48
  context: "js",
37
49
  flags: {
38
50
  assistants: true,
@@ -43,6 +43,9 @@ export const auth = () => {
43
43
  return async (c, next) => {
44
44
  if (!isAuthRegistered())
45
45
  return next();
46
+ // skip for /info
47
+ if (c.req.path === "/info")
48
+ return next();
46
49
  if (!isStudioAuthDisabled() &&
47
50
  c.req.header("x-auth-scheme") === "langsmith") {
48
51
  c.set("auth", {
@@ -35,7 +35,9 @@ export declare class SubgraphExtractor {
35
35
  node: ts.Node;
36
36
  };
37
37
  }[];
38
- getAugmentedSourceFile: (suffix: string, name: string) => {
38
+ getAugmentedSourceFile: (sourcePath: string, name: string, options: {
39
+ allowImportingTsExtensions: boolean;
40
+ }) => {
39
41
  inferFile: {
40
42
  fileName: string;
41
43
  contents: string;
@@ -62,7 +64,7 @@ export declare class SubgraphExtractor {
62
64
  protected reduceChildren<Acc>(node: ts.Node, fn: (acc: Acc, node: ts.Node) => Acc, initial: Acc): Acc;
63
65
  static extractSchemas(target: {
64
66
  sourceFile: string | {
65
- name: string;
67
+ path: string;
66
68
  contents: string;
67
69
  main?: boolean;
68
70
  }[];
@@ -12,11 +12,6 @@ const OVERRIDE_RESOLVE = [
12
12
  new RegExp(`^@langchain\/langgraph-checkpoint(\/.+)?$`),
13
13
  ];
14
14
  const INFER_TEMPLATE_PATH = path.resolve(__dirname, "./schema/types.template.mts");
15
- const compilerOptions = {
16
- noEmit: true,
17
- strict: true,
18
- allowUnusedLabels: true,
19
- };
20
15
  export class SubgraphExtractor {
21
16
  program;
22
17
  checker;
@@ -145,50 +140,67 @@ export class SubgraphExtractor {
145
140
  return this.findSubgraphs(varDecl.initializer, [name]);
146
141
  });
147
142
  };
148
- getAugmentedSourceFile = (suffix, name) => {
143
+ getAugmentedSourceFile = (sourcePath, name, options) => {
144
+ function sanitize(input) {
145
+ return input.replace(/[^a-zA-Z0-9]/g, "_");
146
+ }
149
147
  const vars = this.getSubgraphsVariables(name);
150
- const typeSuffix = suffix.replace(/[^a-zA-Z0-9]/g, "_");
151
- const typeExports = [
148
+ const ext = path.extname(sourcePath);
149
+ const suffix = sourcePath.slice(0, -ext.length);
150
+ let typeExports = [
152
151
  {
153
- typeName: `__langgraph__${name}_${typeSuffix}`,
152
+ typeName: sanitize(`__langgraph__${name}_${suffix}`),
154
153
  valueName: name,
155
154
  graphName: name,
156
155
  },
157
156
  ];
157
+ const seenTypeName = new Set();
158
158
  for (const { subgraph, node, namespace } of vars) {
159
+ if (seenTypeName.has(subgraph.name))
160
+ continue;
161
+ seenTypeName.add(subgraph.name);
159
162
  typeExports.push({
160
- typeName: `__langgraph__${namespace.join("_")}_${node}_${typeSuffix}`,
163
+ typeName: sanitize(`__langgraph__${namespace.join("_")}_${node}_${suffix}`),
161
164
  valueName: subgraph.name,
162
165
  graphName: [...namespace, node].join("|"),
163
166
  });
164
167
  }
165
- const sourceFilePath = `__langgraph__source_${suffix}.mts`;
168
+ typeExports = typeExports.map(({ typeName, ...rest }) => ({
169
+ ...rest,
170
+ typeName: sanitize(typeName),
171
+ }));
172
+ const sourceFilePath = `__langgraph__source_${sanitize(suffix)}${ext}`;
166
173
  const sourceContents = [
167
174
  this.getText(this.sourceFile),
168
- ...typeExports.map(({ typeName, valueName }) => `export type ${typeName} = typeof ${valueName}`),
169
- ].join("\n\n");
170
- const inferFilePath = `__langgraph__infer_${suffix}.mts`;
175
+ typeExports.map((type) => `export type ${type.typeName} = typeof ${type.valueName}`),
176
+ ];
177
+ const inferFilePath = `__langgraph__infer_${sanitize(suffix)}${ext}`;
178
+ const sourceFileImportPath = options.allowImportingTsExtensions
179
+ ? sourceFilePath
180
+ : sourceFilePath.slice(0, -ext.length) + ext.replace("ts", "js");
171
181
  const inferContents = [
172
- ...typeExports.map(({ typeName }) => `import type { ${typeName}} from "./__langgraph__source_${suffix}.mts"`),
182
+ typeExports.map((type) => `import type { ${type.typeName} } from "./${sourceFileImportPath}"`),
173
183
  this.inferFile.getText(this.inferFile),
174
- ...typeExports.flatMap(({ typeName }) => {
175
- return [
176
- dedent `
177
- type ${typeName}__reflect = Reflect<${typeName}>;
178
- export type ${typeName}__state = Inspect<${typeName}__reflect["state"]>;
179
- export type ${typeName}__update = Inspect<${typeName}__reflect["update"]>;
184
+ typeExports.map((type) => dedent `
185
+ type ${type.typeName}__reflect = Reflect<${type.typeName}>;
186
+ export type ${type.typeName}__state = Inspect<${type.typeName}__reflect["state"]>;
187
+ export type ${type.typeName}__update = Inspect<${type.typeName}__reflect["update"]>;
180
188
 
181
- type ${typeName}__builder = BuilderReflect<${typeName}>;
182
- export type ${typeName}__input = Inspect<FilterAny<${typeName}__builder["input"]>>;
183
- export type ${typeName}__output = Inspect<FilterAny<${typeName}__builder["output"]>>;
184
- export type ${typeName}__config = Inspect<FilterAny<${typeName}__builder["config"]>>;
185
- `,
186
- ];
187
- }),
188
- ].join("\n\n");
189
+ type ${type.typeName}__builder = BuilderReflect<${type.typeName}>;
190
+ export type ${type.typeName}__input = Inspect<FilterAny<${type.typeName}__builder["input"]>>;
191
+ export type ${type.typeName}__output = Inspect<FilterAny<${type.typeName}__builder["output"]>>;
192
+ export type ${type.typeName}__config = Inspect<FilterAny<${type.typeName}__builder["config"]>>;
193
+ `),
194
+ ];
189
195
  return {
190
- inferFile: { fileName: inferFilePath, contents: inferContents },
191
- sourceFile: { fileName: sourceFilePath, contents: sourceContents },
196
+ inferFile: {
197
+ fileName: inferFilePath,
198
+ contents: inferContents.flat(1).join("\n\n"),
199
+ },
200
+ sourceFile: {
201
+ fileName: sourceFilePath,
202
+ contents: sourceContents.flat(1).join("\n\n"),
203
+ },
192
204
  exports: typeExports,
193
205
  };
194
206
  };
@@ -268,6 +280,19 @@ export class SubgraphExtractor {
268
280
  return inputPath.replace(/\\/g, "/");
269
281
  return inputPath;
270
282
  };
283
+ let compilerOptions = {
284
+ noEmit: true,
285
+ strict: true,
286
+ allowUnusedLabels: true,
287
+ };
288
+ // Find tsconfig.json file
289
+ const tsconfigPath = ts.findConfigFile(projectDirname, ts.sys.fileExists, "tsconfig.json");
290
+ // Read tsconfig.json file
291
+ if (tsconfigPath != null) {
292
+ const tsconfigFile = ts.readConfigFile(tsconfigPath, ts.sys.readFile);
293
+ const parsedTsconfig = ts.parseJsonConfigFileContent(tsconfigFile.config, ts.sys, path.dirname(tsconfigPath));
294
+ compilerOptions = { ...parsedTsconfig.options, ...compilerOptions };
295
+ }
271
296
  const vfsHost = vfs.createVirtualCompilerHost(system, compilerOptions, ts);
272
297
  const host = vfsHost.compilerHost;
273
298
  const targetPaths = [];
@@ -276,12 +301,13 @@ export class SubgraphExtractor {
276
301
  targetPaths.push({ ...item, sourceFile: item.sourceFile });
277
302
  }
278
303
  else {
279
- for (const { name, contents, main } of item.sourceFile ?? []) {
280
- fsMap.set(vfsPath(path.resolve(projectDirname, name)), contents);
304
+ for (const { path: sourcePath, contents, main } of item.sourceFile ??
305
+ []) {
306
+ fsMap.set(vfsPath(path.resolve(projectDirname, sourcePath)), contents);
281
307
  if (main) {
282
308
  targetPaths.push({
283
309
  ...item,
284
- sourceFile: path.resolve(projectDirname, name),
310
+ sourceFile: path.resolve(projectDirname, sourcePath),
285
311
  });
286
312
  }
287
313
  }
@@ -312,12 +338,15 @@ export class SubgraphExtractor {
312
338
  const researchTargets = [];
313
339
  for (const targetPath of targetPaths) {
314
340
  const extractor = new SubgraphExtractor(research, research.getSourceFile(targetPath.sourceFile), research.getSourceFile(INFER_TEMPLATE_PATH), options);
315
- const { sourceFile, inferFile, exports } = extractor.getAugmentedSourceFile(path.basename(targetPath.sourceFile), targetPath.exportSymbol);
341
+ const graphDirname = path.dirname(targetPath.sourceFile);
342
+ const { sourceFile, inferFile, exports } = extractor.getAugmentedSourceFile(path.relative(projectDirname, targetPath.sourceFile), targetPath.exportSymbol, {
343
+ allowImportingTsExtensions: compilerOptions.allowImportingTsExtensions ?? false,
344
+ });
316
345
  for (const { fileName, contents } of [sourceFile, inferFile]) {
317
- system.writeFile(vfsPath(path.resolve(projectDirname, fileName)), contents);
346
+ system.writeFile(vfsPath(path.resolve(graphDirname, fileName)), contents);
318
347
  }
319
348
  researchTargets.push({
320
- rootName: path.resolve(projectDirname, inferFile.fileName),
349
+ rootName: path.resolve(graphDirname, inferFile.fileName),
321
350
  exports,
322
351
  });
323
352
  }
@@ -326,23 +355,70 @@ export class SubgraphExtractor {
326
355
  options: compilerOptions,
327
356
  host,
328
357
  });
358
+ // Print out any diagnostics file that were detected before emitting
359
+ // This may explain why sometimes the schema is invalid.
360
+ const allDiagnostics = ts.getPreEmitDiagnostics(extract);
361
+ for (const diagnostic of allDiagnostics) {
362
+ let message = ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n") + "\n";
363
+ if (diagnostic.file) {
364
+ const fileName = diagnostic.file.fileName;
365
+ const { line, character } = ts.getLineAndCharacterOfPosition(diagnostic.file, diagnostic.start);
366
+ const fileLoc = `(${line + 1},${character + 1})`;
367
+ message = `${fileName} ${fileLoc}: ${message}`;
368
+ }
369
+ console.log(message);
370
+ }
329
371
  const schemaGenerator = buildGenerator(extract);
330
- const trySymbol = (schema, symbol) => {
372
+ const trySymbol = (symbol) => {
373
+ let schema = undefined;
331
374
  try {
332
- return schema?.getSchemaForSymbol(symbol) ?? undefined;
375
+ schema = schemaGenerator?.getSchemaForSymbol(symbol) ?? undefined;
333
376
  }
334
377
  catch (e) {
335
378
  console.warn(`Failed to obtain symbol "${symbol}":`, e?.message);
336
379
  }
337
- return undefined;
380
+ if (schema == null)
381
+ return undefined;
382
+ const definitions = schema.definitions;
383
+ if (definitions == null)
384
+ return schema;
385
+ const toReplace = Object.keys(definitions).flatMap((key) => {
386
+ const replacedKey = key.includes("import(")
387
+ ? key.replace(/import\(.+@langchain[\\/]core.+\)\./, "")
388
+ : key;
389
+ if (key !== replacedKey && definitions[replacedKey] == null) {
390
+ return [
391
+ {
392
+ source: key,
393
+ target: replacedKey,
394
+ sourceRef: `#/definitions/${key}`,
395
+ targetRef: `#/definitions/${replacedKey}`,
396
+ },
397
+ ];
398
+ }
399
+ return [];
400
+ });
401
+ for (const { source, target } of toReplace) {
402
+ definitions[target] = definitions[source];
403
+ delete definitions[source];
404
+ }
405
+ const refMap = toReplace.reduce((acc, item) => {
406
+ acc[item.sourceRef] = item.targetRef;
407
+ return acc;
408
+ }, {});
409
+ return JSON.parse(JSON.stringify(schema, (_, value) => {
410
+ if (typeof value === "string" && refMap[value])
411
+ return refMap[value];
412
+ return value;
413
+ }));
338
414
  };
339
415
  return researchTargets.map(({ exports }) => Object.fromEntries(exports.map(({ typeName, graphName }) => [
340
416
  graphName,
341
417
  {
342
- state: trySymbol(schemaGenerator, `${typeName}__update`),
343
- input: trySymbol(schemaGenerator, `${typeName}__input`),
344
- output: trySymbol(schemaGenerator, `${typeName}__output`),
345
- config: trySymbol(schemaGenerator, `${typeName}__config`),
418
+ state: trySymbol(`${typeName}__update`),
419
+ input: trySymbol(`${typeName}__input`),
420
+ output: trySymbol(`${typeName}__output`),
421
+ config: trySymbol(`${typeName}__config`),
346
422
  },
347
423
  ])));
348
424
  }
@@ -5,14 +5,14 @@ import type {
5
5
  StateDefinition,
6
6
  } from "@langchain/langgraph";
7
7
 
8
- // @ts-expect-error
8
+ // @ts-ignore
9
9
  type AnyPregel = {
10
10
  lg_is_pregel: boolean;
11
11
  stream: (...args: any[]) => any;
12
12
  invoke: (...args: any[]) => any;
13
13
  };
14
14
 
15
- // @ts-expect-error
15
+ // @ts-ignore
16
16
  type AnyGraph = {
17
17
  compiled: boolean;
18
18
  compile: (...args: any[]) => any;
@@ -30,7 +30,7 @@ type Defactorify<T> = T extends (...args: any[]) => infer R
30
30
  ? Awaited<R>
31
31
  : Awaited<T>;
32
32
 
33
- // @ts-expect-error
33
+ // @ts-ignore
34
34
  type Inspect<T, TDepth extends Array<0> = []> = TDepth extends [0, 0, 0]
35
35
  ? any
36
36
  : T extends unknown
@@ -51,7 +51,7 @@ type ReflectCompiled<T> = T extends { RunInput: infer S; RunOutput: infer U }
51
51
  ? { state: OutputType; update: InputType }
52
52
  : never;
53
53
 
54
- // @ts-expect-error
54
+ // @ts-ignore
55
55
  type Reflect<T> = Defactorify<T> extends infer DT
56
56
  ? DT extends {
57
57
  compile(...args: any[]): infer Compiled;
@@ -74,7 +74,7 @@ type BuilderReflectCompiled<T> = T extends {
74
74
  }
75
75
  : never;
76
76
 
77
- // @ts-expect-error
77
+ // @ts-ignore
78
78
  type BuilderReflect<T> = Defactorify<T> extends infer DT
79
79
  ? DT extends {
80
80
  compile(...args: any[]): infer Compiled;
@@ -83,5 +83,5 @@ type BuilderReflect<T> = Defactorify<T> extends infer DT
83
83
  : BuilderReflectCompiled<DT>
84
84
  : never;
85
85
 
86
- // @ts-expect-error
86
+ // @ts-ignore
87
87
  type FilterAny<T> = 0 extends 1 & T ? never : T;
@@ -183,7 +183,6 @@ export declare const AssistantCreate: z.ZodObject<{
183
183
  graph_id: string;
184
184
  context?: unknown;
185
185
  metadata?: z.objectOutputType<{}, z.ZodUnknown, "strip"> | undefined;
186
- name?: string | undefined;
187
186
  config?: z.objectOutputType<{
188
187
  tags: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
189
188
  recursion_limit: z.ZodOptional<z.ZodNumber>;
@@ -198,13 +197,13 @@ export declare const AssistantCreate: z.ZodObject<{
198
197
  thread_ts: z.ZodOptional<z.ZodString>;
199
198
  }, z.ZodUnknown, "strip">>>;
200
199
  }, z.ZodUnknown, "strip"> | undefined;
200
+ name?: string | undefined;
201
201
  if_exists?: "do_nothing" | "raise" | undefined;
202
202
  assistant_id?: string | undefined;
203
203
  }, {
204
204
  graph_id: string;
205
205
  context?: unknown;
206
206
  metadata?: z.objectInputType<{}, z.ZodUnknown, "strip"> | undefined;
207
- name?: string | undefined;
208
207
  config?: z.objectInputType<{
209
208
  tags: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
210
209
  recursion_limit: z.ZodOptional<z.ZodNumber>;
@@ -219,6 +218,7 @@ export declare const AssistantCreate: z.ZodObject<{
219
218
  thread_ts: z.ZodOptional<z.ZodString>;
220
219
  }, z.ZodUnknown, "strip">>>;
221
220
  }, z.ZodUnknown, "strip"> | undefined;
221
+ name?: string | undefined;
222
222
  if_exists?: "do_nothing" | "raise" | undefined;
223
223
  assistant_id?: string | undefined;
224
224
  }>;
@@ -270,7 +270,6 @@ export declare const AssistantPatch: z.ZodObject<{
270
270
  }, "strip", z.ZodTypeAny, {
271
271
  context?: unknown;
272
272
  metadata?: z.objectOutputType<{}, z.ZodAny, "strip"> | undefined;
273
- name?: string | undefined;
274
273
  config?: z.objectOutputType<{
275
274
  tags: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
276
275
  recursion_limit: z.ZodOptional<z.ZodNumber>;
@@ -285,11 +284,11 @@ export declare const AssistantPatch: z.ZodObject<{
285
284
  thread_ts: z.ZodOptional<z.ZodString>;
286
285
  }, z.ZodUnknown, "strip">>>;
287
286
  }, z.ZodUnknown, "strip"> | undefined;
287
+ name?: string | undefined;
288
288
  graph_id?: string | undefined;
289
289
  }, {
290
290
  context?: unknown;
291
291
  metadata?: z.objectInputType<{}, z.ZodAny, "strip"> | undefined;
292
- name?: string | undefined;
293
292
  config?: z.objectInputType<{
294
293
  tags: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
295
294
  recursion_limit: z.ZodOptional<z.ZodNumber>;
@@ -304,6 +303,7 @@ export declare const AssistantPatch: z.ZodObject<{
304
303
  thread_ts: z.ZodOptional<z.ZodString>;
305
304
  }, z.ZodUnknown, "strip">>>;
306
305
  }, z.ZodUnknown, "strip"> | undefined;
306
+ name?: string | undefined;
307
307
  graph_id?: string | undefined;
308
308
  }>;
309
309
  export declare const Config: z.ZodObject<{
package/dist/server.mjs CHANGED
@@ -19,6 +19,7 @@ import { registerHttp } from "./http/custom.mjs";
19
19
  import { cors, ensureContentType } from "./http/middleware.mjs";
20
20
  import { bindLoopbackFetch } from "./loopback.mjs";
21
21
  import { checkLangGraphSemver } from "./semver/index.mjs";
22
+ import { getConfig } from "@langchain/langgraph";
22
23
  export const StartServerSchema = z.object({
23
24
  port: z.number(),
24
25
  nWorkers: z.number(),
@@ -79,9 +80,6 @@ export async function startServer(options) {
79
80
  registerSdkLogger();
80
81
  logger.info(`Registering graphs from ${options.cwd}`);
81
82
  await registerFromEnv(options.graphs, { cwd: options.cwd });
82
- // Make sure to register the runtime formatter after we've loaded the graphs
83
- // to ensure that we're not loading `@langchain/langgraph` from different path.
84
- const { getConfig } = await import("@langchain/langgraph");
85
83
  registerRuntimeLogFormatter((info) => {
86
84
  const config = getConfig();
87
85
  if (config == null)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@langchain/langgraph-api",
3
- "version": "0.0.54",
3
+ "version": "0.0.56",
4
4
  "type": "module",
5
5
  "engines": {
6
6
  "node": "^18.19.0 || >=20.16.0"
@@ -52,7 +52,7 @@
52
52
  "@babel/code-frame": "^7.26.2",
53
53
  "@hono/node-server": "^1.12.0",
54
54
  "@hono/zod-validator": "^0.2.2",
55
- "@langchain/langgraph-ui": "0.0.54",
55
+ "@langchain/langgraph-ui": "0.0.56",
56
56
  "@types/json-schema": "^7.0.15",
57
57
  "@typescript/vfs": "^1.6.0",
58
58
  "dedent": "^1.5.3",
@@ -84,9 +84,9 @@
84
84
  },
85
85
  "devDependencies": {
86
86
  "@langchain/core": "^0.3.59",
87
- "@langchain/langgraph": "0.4.0",
87
+ "@langchain/langgraph": "0.4.2",
88
88
  "@langchain/langgraph-checkpoint": "0.1.0",
89
- "@langchain/langgraph-sdk": "0.0.104",
89
+ "@langchain/langgraph-sdk": "0.0.105",
90
90
  "@types/babel__code-frame": "^7.0.6",
91
91
  "@types/node": "^18.15.11",
92
92
  "@types/react": "^19.0.8",