@langchain/langgraph-api 0.0.14 → 0.0.15

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.
@@ -1,4 +1,3 @@
1
- import "../preload.mjs";
2
1
  import { asyncExitHook } from "exit-hook";
3
2
  import * as process from "node:process";
4
3
  import { startServer, StartServerSchema } from "../server.mjs";
@@ -21,6 +21,8 @@ For production use, please use LangGraph Cloud.
21
21
  fileURLToPath(new URL("../../cli.mjs", import.meta.resolve("tsx/esm/api"))),
22
22
  "watch",
23
23
  "--clear-screen=false",
24
+ "--import",
25
+ new URL(import.meta.resolve("../preload.mjs")).toString(),
24
26
  fileURLToPath(new URL(import.meta.resolve("./entrypoint.mjs"))),
25
27
  options.pid.toString(),
26
28
  JSON.stringify({
@@ -1,17 +1,19 @@
1
- // This hook is to ensure that @langchain/langgraph package
2
- // found in /api folder has precendence compared to user-provided package
3
- // found in /deps. Does not attempt to semver check for too old packages.
1
+ // This module hook is used to ensure that @langchain/langgraph package
2
+ // is imported from a consistent location.
3
+ // Accepts `{ parentURL: string }` as argument when registering the hook.
4
4
  const OVERRIDE_RESOLVE = [
5
- "@langchain/langgraph",
6
- "@langchain/langgraph-checkpoint",
5
+ // Override `@langchain/langgraph` or `@langchain/langgraph/prebuilt`,
6
+ // but not `@langchain/langgraph-sdk`
7
+ new RegExp(`^@langchain\/langgraph(\/.+)?$`),
8
+ new RegExp(`^@langchain\/langgraph-checkpoint(\/.+)?$`),
7
9
  ];
10
+ let parentURL;
11
+ export async function initialize(args) {
12
+ parentURL = args.parentURL;
13
+ }
8
14
  export async function resolve(specifier, context, nextResolve) {
9
- if (OVERRIDE_RESOLVE.includes(specifier)) {
10
- const parentURL = new URL("./load.mts", import.meta.url).toString();
11
- return await nextResolve(specifier, {
12
- ...context,
13
- parentURL,
14
- });
15
+ if (OVERRIDE_RESOLVE.some((regex) => regex.test(specifier))) {
16
+ return await nextResolve(specifier, { ...context, parentURL });
15
17
  }
16
18
  return nextResolve(specifier, context);
17
19
  }
@@ -30,7 +30,7 @@ export async function resolveGraph(spec, options) {
30
30
  })();
31
31
  return { sourceFile, exportSymbol, resolved };
32
32
  }
33
- export async function runGraphSchemaWorker(spec) {
33
+ export async function runGraphSchemaWorker(spec, options) {
34
34
  let SCHEMA_RESOLVE_TIMEOUT_MS = 30_000;
35
35
  try {
36
36
  const envTimeout = Number.parseInt(process.env.LANGGRAPH_SCHEMA_RESOLVE_TIMEOUT_MS ?? "0", 10);
@@ -41,8 +41,12 @@ export async function runGraphSchemaWorker(spec) {
41
41
  catch {
42
42
  // ignore
43
43
  }
44
+ if (options?.mainThread) {
45
+ const { SubgraphExtractor } = await import("./parser/parser.mjs");
46
+ return SubgraphExtractor.extractSchemas(spec.sourceFile, spec.exportSymbol, { strict: false });
47
+ }
44
48
  return await new Promise((resolve, reject) => {
45
- const worker = new Worker(fileURLToPath(new URL("./parser/parser.worker.mjs", import.meta.url)));
49
+ const worker = new Worker(fileURLToPath(new URL("./parser/parser.worker.mjs", import.meta.url)), { argv: process.argv.slice(-1) });
46
50
  // Set a timeout to reject if the worker takes too long
47
51
  const timeoutId = setTimeout(() => {
48
52
  worker.terminate();
@@ -5,6 +5,12 @@ import dedent from "dedent";
5
5
  import { buildGenerator } from "./schema/types.mjs";
6
6
  import { fileURLToPath } from "node:url";
7
7
  const __dirname = fileURLToPath(new URL(".", import.meta.url));
8
+ const OVERRIDE_RESOLVE = [
9
+ // Override `@langchain/langgraph` or `@langchain/langgraph/prebuilt`,
10
+ // but not `@langchain/langgraph-sdk`
11
+ new RegExp(`^@langchain\/langgraph(\/.+)?$`),
12
+ new RegExp(`^@langchain\/langgraph-checkpoint(\/.+)?$`),
13
+ ];
8
14
  const compilerOptions = {
9
15
  noEmit: true,
10
16
  strict: true,
@@ -227,12 +233,16 @@ export class SubgraphExtractor {
227
233
  // This API is not well made for Windows, ensure that the paths are UNIX slashes
228
234
  const fsMap = new Map();
229
235
  const system = vfs.createFSBackedSystem(fsMap, dirname, ts);
236
+ // TODO: investigate if we should create a PR in @typescript/vfs
237
+ const oldReadFile = system.readFile.bind(system);
238
+ system.readFile = (fileName) => oldReadFile(fileName) ?? "// Non-existent file";
230
239
  const vfsPath = (inputPath) => {
231
240
  if (process.platform === "win32")
232
241
  return inputPath.replace(/\\/g, "/");
233
242
  return inputPath;
234
243
  };
235
- const host = vfs.createVirtualCompilerHost(system, compilerOptions, ts);
244
+ const vfsHost = vfs.createVirtualCompilerHost(system, compilerOptions, ts);
245
+ const host = vfsHost.compilerHost;
236
246
  const targetPath = typeof target === "string"
237
247
  ? target
238
248
  : path.resolve(dirname, "./__langgraph__target.mts");
@@ -243,38 +253,27 @@ export class SubgraphExtractor {
243
253
  fsMap.set(vfsPath(path.resolve(dirname, name)), contents);
244
254
  }
245
255
  }
246
- // TODO: this is in all instances broken, will need to address in a way
247
- // that allows loading fs-backed modules from different vfs
248
- // host.compilerHost.resolveModuleNames = (moduleNames, containingFile) => {
249
- // const resolvedModules: (ts.ResolvedModule | undefined)[] = [];
250
- // for (const moduleName of moduleNames) {
251
- // let target = containingFile;
252
- // const relative = path.relative(dirname, containingFile);
253
- // logger.debug(`${moduleName} ${containingFile}`);
254
- // if (
255
- // moduleName.startsWith("@langchain/langgraph") &&
256
- // relative &&
257
- // !relative.startsWith("..") &&
258
- // !path.isAbsolute(relative)
259
- // ) {
260
- // target = path.resolve(__dirname, "../", relative);
261
- // process.stderr.write(`${moduleName} ${relative} -> ${target}\n`);
262
- // }
263
- // resolvedModules.push(
264
- // ts.resolveModuleName(
265
- // moduleName,
266
- // target,
267
- // compilerOptions,
268
- // host.compilerHost
269
- // ).resolvedModule
270
- // );
271
- // }
272
- // return resolvedModules;
273
- // };
256
+ const moduleCache = ts.createModuleResolutionCache(dirname, (x) => x);
257
+ host.resolveModuleNameLiterals = (entries, containingFile, redirectedReference, options) => entries.flatMap((entry) => {
258
+ const specifier = entry.text;
259
+ // Force module resolution to use @langchain/langgraph from the local project
260
+ // rather than from API/CLI.
261
+ let targetFile = containingFile;
262
+ if (OVERRIDE_RESOLVE.some((regex) => regex.test(specifier))) {
263
+ // check if we're not already importing from node_modules
264
+ if (!containingFile.split(path.sep).includes("node_modules")) {
265
+ // Doesn't matter if the file exists, only used to nudge `ts.resolveModuleName`
266
+ targetFile = path.resolve(dirname, "__langgraph__resolve.mts");
267
+ }
268
+ }
269
+ return [
270
+ ts.resolveModuleName(specifier, targetFile, options, host, moduleCache, redirectedReference),
271
+ ];
272
+ });
274
273
  const research = ts.createProgram({
275
274
  rootNames: [inferTemplatePath, targetPath],
276
275
  options: compilerOptions,
277
- host: host.compilerHost,
276
+ host,
278
277
  });
279
278
  const extractor = new SubgraphExtractor(research, research.getSourceFile(targetPath), research.getSourceFile(inferTemplatePath), options);
280
279
  const { files, exports } = extractor.getAugmentedSourceFile(name);
@@ -284,7 +283,7 @@ export class SubgraphExtractor {
284
283
  const extract = ts.createProgram({
285
284
  rootNames: [path.resolve(dirname, "./__langraph__infer.mts")],
286
285
  options: compilerOptions,
287
- host: host.compilerHost,
286
+ host,
288
287
  });
289
288
  const schemaGenerator = buildGenerator(extract);
290
289
  const trySymbol = (schema, symbol) => {
@@ -27,17 +27,19 @@ type Defactorify<T> = T extends (...args: any[]) => infer R
27
27
  : Awaited<T>;
28
28
 
29
29
  // @ts-expect-error
30
- type Inspect<T> = T extends unknown
31
- ? {
32
- [K in keyof T]: 0 extends 1 & T[K]
33
- ? T[K]
34
- : Wrap<MatchBaseMessageArray<T[K]>> extends Wrap<BaseMessage[]>
35
- ? BaseMessage[]
36
- : Wrap<MatchBaseMessage<T[K]>> extends Wrap<BaseMessage>
37
- ? BaseMessage
38
- : Inspect<T[K]>;
39
- }
40
- : never;
30
+ type Inspect<T, TDepth extends Array<0> = []> = TDepth extends [0, 0, 0]
31
+ ? any
32
+ : T extends unknown
33
+ ? {
34
+ [K in keyof T]: 0 extends 1 & T[K]
35
+ ? T[K]
36
+ : Wrap<MatchBaseMessageArray<T[K]>> extends Wrap<BaseMessage[]>
37
+ ? BaseMessage[]
38
+ : Wrap<MatchBaseMessage<T[K]>> extends Wrap<BaseMessage>
39
+ ? BaseMessage
40
+ : Inspect<T[K], [0, ...TDepth]>;
41
+ }
42
+ : never;
41
43
 
42
44
  type ReflectCompiled<T> = T extends { RunInput: infer S; RunOutput: infer U }
43
45
  ? { state: S; update: U }
package/dist/preload.mjs CHANGED
@@ -1,3 +1,20 @@
1
1
  import { register } from "node:module";
2
- // enforce API @langchain/langgraph precedence
3
- register("./graph/load.hooks.mjs", import.meta.url);
2
+ import { pathToFileURL } from "node:url";
3
+ import { join } from "node:path";
4
+ // arguments passed to the entrypoint: [ppid, payload]
5
+ // we only care about the payload, which contains the server definition
6
+ const lastArg = process.argv.at(-1);
7
+ const options = JSON.parse(lastArg || "{}");
8
+ // find the first file, as `parentURL` needs to be a valid file URL
9
+ // if no graph found, just assume a dummy default file, which should
10
+ // be working fine as well.
11
+ const firstGraphFile = Object.values(options.graphs)
12
+ .flatMap((i) => i.split(":").at(0))
13
+ .at(0) || "index.mts";
14
+ // enforce API @langchain/langgraph resolution
15
+ register("./graph/load.hooks.mjs", import.meta.url, {
16
+ parentURL: "data:",
17
+ data: {
18
+ parentURL: pathToFileURL(join(options.cwd, firstGraphFile)).toString(),
19
+ },
20
+ });
package/dist/ui/load.mjs CHANGED
@@ -26,11 +26,11 @@ api.post("/ui/:agent", zValidator("json", z.object({ name: z.string() })), async
26
26
  const messageName = JSON.stringify(message.name);
27
27
  const result = [];
28
28
  for (const css of files.filter((i) => path.extname(i.basename) === ".css")) {
29
- result.push(`<link rel="stylesheet" href="http://${host}/ui/${agent}/${css.basename}" />`);
29
+ result.push(`<link rel="stylesheet" href="//${host}/ui/${agent}/${css.basename}" />`);
30
30
  }
31
31
  const js = files.find((i) => path.extname(i.basename) === ".js");
32
32
  if (js) {
33
- result.push(`<script src="http://${host}/ui/${agent}/${js.basename}" onload='__LGUI_${agent}.render(${messageName}, "{{shadowRootId}}")'></script>`);
33
+ result.push(`<script src="//${host}/ui/${agent}/${js.basename}" onload='__LGUI_${agent}.render(${messageName}, "{{shadowRootId}}")'></script>`);
34
34
  }
35
35
  return c.text(result.join("\n"), {
36
36
  headers: { "Content-Type": "text/html" },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@langchain/langgraph-api",
3
- "version": "0.0.14",
3
+ "version": "0.0.15",
4
4
  "type": "module",
5
5
  "engines": {
6
6
  "node": "^18.19.0 || >=20.16.0"