@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.
- package/README.md +24 -0
- package/dist/api/assistants.mjs +144 -0
- package/dist/api/runs.mjs +239 -0
- package/dist/api/store.mjs +83 -0
- package/dist/api/threads.mjs +145 -0
- package/dist/cli/build.mjs +44 -0
- package/dist/cli/cli.mjs +7 -0
- package/dist/cli/dev.entrypoint.mjs +35 -0
- package/dist/cli/dev.mjs +133 -0
- package/dist/cli/dockerfile.mjs +35 -0
- package/dist/cli/utils/builder.mjs +16 -0
- package/dist/cli/utils/ipc/client.mjs +25 -0
- package/dist/cli/utils/ipc/server.mjs +71 -0
- package/dist/cli/utils/ipc/utils/get-pipe-path.mjs +7 -0
- package/dist/cli/utils/ipc/utils/temporary-directory.mjs +18 -0
- package/dist/cli/utils/project.mjs +18 -0
- package/dist/docker/compose.mjs +185 -0
- package/dist/docker/dockerfile.mjs +390 -0
- package/dist/docker/shell.mjs +62 -0
- package/dist/graph/load.hooks.mjs +17 -0
- package/dist/graph/load.mjs +71 -0
- package/dist/graph/load.utils.mjs +50 -0
- package/dist/graph/parser/parser.mjs +308 -0
- package/dist/graph/parser/parser.worker.mjs +7 -0
- package/dist/graph/parser/schema/types.mjs +1607 -0
- package/dist/graph/parser/schema/types.template.mts +81 -0
- package/dist/logging.mjs +50 -0
- package/dist/preload.mjs +3 -0
- package/dist/queue.mjs +91 -0
- package/dist/schemas.mjs +399 -0
- package/dist/server.mjs +63 -0
- package/dist/state.mjs +32 -0
- package/dist/storage/checkpoint.mjs +123 -0
- package/dist/storage/ops.mjs +786 -0
- package/dist/storage/persist.mjs +69 -0
- package/dist/storage/store.mjs +37 -0
- package/dist/stream.mjs +215 -0
- package/dist/utils/abort.mjs +8 -0
- package/dist/utils/config.mjs +35 -0
- package/dist/utils/error.mjs +1 -0
- package/dist/utils/hono.mjs +27 -0
- package/dist/utils/importMap.mjs +55 -0
- package/dist/utils/runnableConfig.mjs +45 -0
- package/dist/utils/serde.mjs +20 -0
- 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
|
+
});
|