@langchain/langgraph-cli 0.0.0-preview.4

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 (45) hide show
  1. package/README.md +24 -0
  2. package/dist/api/assistants.mjs +144 -0
  3. package/dist/api/runs.mjs +239 -0
  4. package/dist/api/store.mjs +83 -0
  5. package/dist/api/threads.mjs +145 -0
  6. package/dist/cli/build.mjs +44 -0
  7. package/dist/cli/cli.mjs +7 -0
  8. package/dist/cli/dev.entrypoint.mjs +35 -0
  9. package/dist/cli/dev.mjs +133 -0
  10. package/dist/cli/dockerfile.mjs +35 -0
  11. package/dist/cli/utils/builder.mjs +16 -0
  12. package/dist/cli/utils/ipc/client.mjs +25 -0
  13. package/dist/cli/utils/ipc/server.mjs +71 -0
  14. package/dist/cli/utils/ipc/utils/get-pipe-path.mjs +7 -0
  15. package/dist/cli/utils/ipc/utils/temporary-directory.mjs +18 -0
  16. package/dist/cli/utils/project.mjs +18 -0
  17. package/dist/docker/compose.mjs +185 -0
  18. package/dist/docker/dockerfile.mjs +390 -0
  19. package/dist/docker/shell.mjs +62 -0
  20. package/dist/graph/load.hooks.mjs +17 -0
  21. package/dist/graph/load.mjs +71 -0
  22. package/dist/graph/load.utils.mjs +50 -0
  23. package/dist/graph/parser/parser.mjs +308 -0
  24. package/dist/graph/parser/parser.worker.mjs +7 -0
  25. package/dist/graph/parser/schema/types.mjs +1607 -0
  26. package/dist/graph/parser/schema/types.template.mts +81 -0
  27. package/dist/logging.mjs +50 -0
  28. package/dist/preload.mjs +3 -0
  29. package/dist/queue.mjs +91 -0
  30. package/dist/schemas.mjs +399 -0
  31. package/dist/server.mjs +63 -0
  32. package/dist/state.mjs +32 -0
  33. package/dist/storage/checkpoint.mjs +123 -0
  34. package/dist/storage/ops.mjs +786 -0
  35. package/dist/storage/persist.mjs +69 -0
  36. package/dist/storage/store.mjs +37 -0
  37. package/dist/stream.mjs +215 -0
  38. package/dist/utils/abort.mjs +8 -0
  39. package/dist/utils/config.mjs +35 -0
  40. package/dist/utils/error.mjs +1 -0
  41. package/dist/utils/hono.mjs +27 -0
  42. package/dist/utils/importMap.mjs +55 -0
  43. package/dist/utils/runnableConfig.mjs +45 -0
  44. package/dist/utils/serde.mjs +20 -0
  45. package/package.json +62 -0
@@ -0,0 +1,308 @@
1
+ import * as ts from "typescript";
2
+ import * as vfs from "@typescript/vfs";
3
+ import * as path from "node:path";
4
+ import dedent from "dedent";
5
+ import { buildGenerator } from "./schema/types.mjs";
6
+ import { fileURLToPath } from "node:url";
7
+ const __dirname = fileURLToPath(new URL(".", import.meta.url));
8
+ const compilerOptions = {
9
+ noEmit: true,
10
+ strict: true,
11
+ allowUnusedLabels: true,
12
+ };
13
+ export class SubgraphExtractor {
14
+ program;
15
+ checker;
16
+ sourceFile;
17
+ inferFile;
18
+ anyPregelType;
19
+ anyGraphType;
20
+ strict;
21
+ constructor(program, sourceFile, inferFile, options) {
22
+ this.program = program;
23
+ this.sourceFile = sourceFile;
24
+ this.inferFile = inferFile;
25
+ this.checker = program.getTypeChecker();
26
+ this.strict = options?.strict ?? false;
27
+ this.anyPregelType = this.findTypeByName("AnyPregel");
28
+ this.anyGraphType = this.findTypeByName("AnyGraph");
29
+ }
30
+ findTypeByName = (needle) => {
31
+ let result = undefined;
32
+ const visit = (node) => {
33
+ if (ts.isTypeAliasDeclaration(node)) {
34
+ const symbol = node.symbol;
35
+ if (symbol != null) {
36
+ const name = this.checker
37
+ .getFullyQualifiedName(symbol)
38
+ .replace(/".*"\./, "");
39
+ if (name === needle)
40
+ result = this.checker.getTypeAtLocation(node);
41
+ }
42
+ }
43
+ if (result == null)
44
+ ts.forEachChild(node, visit);
45
+ };
46
+ ts.forEachChild(this.inferFile, visit);
47
+ if (!result)
48
+ throw new Error(`Failed to find "${needle}" type`);
49
+ return result;
50
+ };
51
+ find = (root, predicate) => {
52
+ let result = undefined;
53
+ const visit = (node) => {
54
+ if (predicate(node)) {
55
+ result = node;
56
+ }
57
+ else {
58
+ ts.forEachChild(node, visit);
59
+ }
60
+ };
61
+ if (predicate(root))
62
+ return root;
63
+ ts.forEachChild(root, visit);
64
+ return result;
65
+ };
66
+ findSubgraphs = (node, namespace = []) => {
67
+ const findAllAddNodeCalls = (acc, node) => {
68
+ if (ts.isCallExpression(node)) {
69
+ const firstChild = node.getChildAt(0);
70
+ if (ts.isPropertyAccessExpression(firstChild) &&
71
+ this.getText(firstChild.name) === "addNode") {
72
+ let nodeName = "unknown";
73
+ let variables = [];
74
+ const [subgraphNode, callArg] = node.arguments;
75
+ if (subgraphNode && ts.isStringLiteralLike(subgraphNode)) {
76
+ nodeName = this.getText(subgraphNode);
77
+ if ((nodeName.startsWith(`"`) && nodeName.endsWith(`"`)) ||
78
+ (nodeName.startsWith(`'`) && nodeName.endsWith(`'`))) {
79
+ nodeName = nodeName.slice(1, -1);
80
+ }
81
+ }
82
+ if (callArg) {
83
+ if (ts.isFunctionLike(callArg) ||
84
+ ts.isCallLikeExpression(callArg)) {
85
+ variables = this.reduceChildren(callArg, this.findSubgraphIdentifiers, []);
86
+ }
87
+ else if (ts.isIdentifier(callArg)) {
88
+ variables = this.findSubgraphIdentifiers([], callArg);
89
+ }
90
+ }
91
+ if (variables.length > 0) {
92
+ if (variables.length > 1) {
93
+ const targetName = [...namespace, nodeName].join("|");
94
+ const errMsg = `Multiple unique subgraph invocations found for "${targetName}"`;
95
+ if (this.strict)
96
+ throw new Error(errMsg);
97
+ console.warn(errMsg);
98
+ }
99
+ acc.push({
100
+ namespace: namespace,
101
+ node: nodeName,
102
+ subgraph: variables[0],
103
+ });
104
+ }
105
+ }
106
+ }
107
+ return acc;
108
+ };
109
+ let subgraphs = this.reduceChildren(node, findAllAddNodeCalls, []);
110
+ // TODO: make this more strict, only traverse the flow graph only
111
+ // if no `addNode` calls were found
112
+ if (!subgraphs.length) {
113
+ const candidate = this.find(node, (node) => node && "flowNode" in node && node.flowNode);
114
+ if (candidate?.flowNode &&
115
+ this.isGraphOrPregelType(this.checker.getTypeAtLocation(candidate.flowNode.node))) {
116
+ subgraphs = this.findSubgraphs(candidate.flowNode.node, namespace);
117
+ }
118
+ }
119
+ // handle recursive behaviour
120
+ if (subgraphs.length > 0) {
121
+ return [
122
+ ...subgraphs,
123
+ ...subgraphs.map(({ subgraph, node }) => this.findSubgraphs(subgraph.node, [...namespace, node])),
124
+ ].flat();
125
+ }
126
+ return subgraphs;
127
+ };
128
+ getSubgraphsVariables = (name) => {
129
+ const sourceSymbol = this.checker.getSymbolAtLocation(this.sourceFile);
130
+ const exports = this.checker.getExportsOfModule(sourceSymbol);
131
+ const targetExport = exports.find((item) => item.name === name);
132
+ if (!targetExport)
133
+ throw new Error(`Failed to find export "${name}"`);
134
+ const varDecls = (targetExport.declarations ?? []).filter(ts.isVariableDeclaration);
135
+ return varDecls.flatMap((varDecl) => {
136
+ if (!varDecl.initializer)
137
+ return [];
138
+ return this.findSubgraphs(varDecl.initializer, [name]);
139
+ });
140
+ };
141
+ getAugmentedSourceFile = (name) => {
142
+ const vars = this.getSubgraphsVariables(name);
143
+ const typeExports = [
144
+ { typeName: `__langgraph__${name}`, valueName: name, graphName: name },
145
+ ];
146
+ for (const { subgraph, node, namespace } of vars) {
147
+ typeExports.push({
148
+ typeName: `__langgraph__${namespace.join("_")}_${node}`,
149
+ valueName: subgraph.name,
150
+ graphName: [...namespace, node].join("|"),
151
+ });
152
+ }
153
+ const sourceFilePath = "__langgraph__source.mts";
154
+ const sourceContents = [
155
+ this.getText(this.sourceFile),
156
+ ...typeExports.map(({ typeName, valueName }) => `export type ${typeName} = typeof ${valueName}`),
157
+ ].join("\n\n");
158
+ const inferFilePath = "__langraph__infer.mts";
159
+ const inferContents = [
160
+ ...typeExports.map(({ typeName }) => `import type { ${typeName}} from "./__langgraph__source.mts"`),
161
+ this.inferFile.getText(this.inferFile),
162
+ ...typeExports.flatMap(({ typeName }) => {
163
+ return [
164
+ dedent `
165
+ type ${typeName}__reflect = Reflect<${typeName}>;
166
+ export type ${typeName}__state = Inspect<${typeName}__reflect["state"]>;
167
+ export type ${typeName}__update = Inspect<${typeName}__reflect["update"]>;
168
+
169
+ type ${typeName}__builder = BuilderReflect<${typeName}>;
170
+ export type ${typeName}__input = Inspect<FilterAny<${typeName}__builder["input"]>>;
171
+ export type ${typeName}__output = Inspect<FilterAny<${typeName}__builder["output"]>>;
172
+ export type ${typeName}__config = Inspect<FilterAny<${typeName}__builder["config"]>>;
173
+ `,
174
+ ];
175
+ }),
176
+ ].join("\n\n");
177
+ return {
178
+ files: [
179
+ [sourceFilePath, sourceContents],
180
+ [inferFilePath, inferContents],
181
+ ],
182
+ exports: typeExports,
183
+ };
184
+ };
185
+ findSubgraphIdentifiers = (acc, node) => {
186
+ if (ts.isIdentifier(node)) {
187
+ const smb = this.checker.getSymbolAtLocation(node);
188
+ if (smb?.valueDeclaration &&
189
+ ts.isVariableDeclaration(smb.valueDeclaration)) {
190
+ const target = smb.valueDeclaration;
191
+ const targetType = this.checker.getTypeAtLocation(target);
192
+ if (this.isGraphOrPregelType(targetType)) {
193
+ acc.push({ name: this.getText(target.name), node: target });
194
+ }
195
+ }
196
+ if (smb?.declarations) {
197
+ const target = smb.declarations.find(ts.isImportSpecifier);
198
+ if (target) {
199
+ const targetType = this.checker.getTypeAtLocation(target);
200
+ if (this.isGraphOrPregelType(targetType)) {
201
+ acc.push({ name: this.getText(target.name), node: target });
202
+ }
203
+ }
204
+ }
205
+ }
206
+ return acc;
207
+ };
208
+ isGraphOrPregelType = (type) => {
209
+ return (this.checker.isTypeAssignableTo(type, this.anyPregelType) ||
210
+ this.checker.isTypeAssignableTo(type, this.anyGraphType));
211
+ };
212
+ getText(node) {
213
+ return node.getText(this.sourceFile);
214
+ }
215
+ reduceChildren(node, fn, initial) {
216
+ let acc = initial;
217
+ function it(node) {
218
+ acc = fn(acc, node);
219
+ // @ts-expect-error
220
+ ts.forEachChild(node, it.bind(this));
221
+ }
222
+ ts.forEachChild(node, it.bind(this));
223
+ return acc;
224
+ }
225
+ static extractSchemas(target, name, options) {
226
+ const dirname = typeof target === "string" ? path.dirname(target) : __dirname;
227
+ const fsMap = new Map();
228
+ const system = vfs.createFSBackedSystem(fsMap, dirname, ts);
229
+ const host = vfs.createVirtualCompilerHost(system, compilerOptions, ts);
230
+ const targetPath = typeof target === "string"
231
+ ? target
232
+ : path.resolve(dirname, "./__langgraph__target.mts");
233
+ const inferTemplatePath = path.resolve(__dirname, "./schema/types.template.mts");
234
+ if (typeof target !== "string") {
235
+ fsMap.set(targetPath, target.contents);
236
+ for (const [name, contents] of target.files ?? []) {
237
+ let tsFileName = path.resolve(dirname, name);
238
+ // TS for some reason uses UNIX backslashes instead of the Window ones
239
+ if (process.platform === "win32") {
240
+ tsFileName = tsFileName.replace(/\\/g, "/");
241
+ }
242
+ fsMap.set(tsFileName, contents);
243
+ }
244
+ }
245
+ // TODO: this is in all instances broken, will need to address in a way
246
+ // that allows loading fs-backed modules from different vfs
247
+ // host.compilerHost.resolveModuleNames = (moduleNames, containingFile) => {
248
+ // const resolvedModules: (ts.ResolvedModule | undefined)[] = [];
249
+ // for (const moduleName of moduleNames) {
250
+ // let target = containingFile;
251
+ // const relative = path.relative(dirname, containingFile);
252
+ // logger.debug(`${moduleName} ${containingFile}`);
253
+ // if (
254
+ // moduleName.startsWith("@langchain/langgraph") &&
255
+ // relative &&
256
+ // !relative.startsWith("..") &&
257
+ // !path.isAbsolute(relative)
258
+ // ) {
259
+ // target = path.resolve(__dirname, "../", relative);
260
+ // process.stderr.write(`${moduleName} ${relative} -> ${target}\n`);
261
+ // }
262
+ // resolvedModules.push(
263
+ // ts.resolveModuleName(
264
+ // moduleName,
265
+ // target,
266
+ // compilerOptions,
267
+ // host.compilerHost
268
+ // ).resolvedModule
269
+ // );
270
+ // }
271
+ // return resolvedModules;
272
+ // };
273
+ const research = ts.createProgram({
274
+ rootNames: [inferTemplatePath, targetPath],
275
+ options: compilerOptions,
276
+ host: host.compilerHost,
277
+ });
278
+ const extractor = new SubgraphExtractor(research, research.getSourceFile(targetPath), research.getSourceFile(inferTemplatePath), options);
279
+ const { files, exports } = extractor.getAugmentedSourceFile(name);
280
+ for (const [name, source] of files) {
281
+ system.writeFile(path.resolve(dirname, name), source);
282
+ }
283
+ const extract = ts.createProgram({
284
+ rootNames: [path.resolve(dirname, "./__langraph__infer.mts")],
285
+ options: compilerOptions,
286
+ host: host.compilerHost,
287
+ });
288
+ const schemaGenerator = buildGenerator(extract);
289
+ const trySymbol = (schema, symbol) => {
290
+ try {
291
+ return schema?.getSchemaForSymbol(symbol) ?? undefined;
292
+ }
293
+ catch (e) {
294
+ console.warn(`Failed to obtain symbol "${symbol}":`, e?.message);
295
+ }
296
+ return undefined;
297
+ };
298
+ return Object.fromEntries(exports.map(({ typeName, graphName }) => [
299
+ graphName,
300
+ {
301
+ input: trySymbol(schemaGenerator, `${typeName}__input`),
302
+ output: trySymbol(schemaGenerator, `${typeName}__output`),
303
+ state: trySymbol(schemaGenerator, `${typeName}__update`),
304
+ config: trySymbol(schemaGenerator, `${typeName}__config`),
305
+ },
306
+ ]));
307
+ }
308
+ }
@@ -0,0 +1,7 @@
1
+ import { tsImport } from "tsx/esm/api";
2
+ import { parentPort } from "node:worker_threads";
3
+ parentPort?.on("message", async (payload) => {
4
+ const { SubgraphExtractor } = await tsImport("./parser.mjs", import.meta.url);
5
+ const result = SubgraphExtractor.extractSchemas(payload.sourceFile, payload.exportSymbol, { strict: false });
6
+ parentPort?.postMessage(result);
7
+ });