@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,81 @@
1
+ import type { BaseMessage } from "@langchain/core/messages";
2
+ import type {
3
+ StateType,
4
+ UpdateType,
5
+ StateDefinition,
6
+ } from "@langchain/langgraph";
7
+ import type { Graph } from "@langchain/langgraph";
8
+ import type { Pregel } from "@langchain/langgraph/pregel";
9
+
10
+ // @ts-expect-error
11
+ type AnyPregel = Pregel<any, any>;
12
+
13
+ // @ts-expect-error
14
+ type AnyGraph = Graph<any, any, any, any, any>;
15
+
16
+ type Wrap<T> = (a: T) => void;
17
+ type MatchBaseMessage<T> = T extends BaseMessage ? BaseMessage : never;
18
+ type MatchBaseMessageArray<T> =
19
+ T extends Array<infer C>
20
+ ? Wrap<MatchBaseMessage<C>> extends Wrap<BaseMessage>
21
+ ? BaseMessage[]
22
+ : never
23
+ : never;
24
+
25
+ type Defactorify<T> = T extends (...args: any[]) => infer R
26
+ ? Awaited<R>
27
+ : Awaited<T>;
28
+
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;
41
+
42
+ type ReflectCompiled<T> = T extends { RunInput: infer S; RunOutput: infer U }
43
+ ? { state: S; update: U }
44
+ : never;
45
+
46
+ // @ts-expect-error
47
+ type Reflect<T> =
48
+ Defactorify<T> extends infer DT
49
+ ? DT extends {
50
+ compile(...args: any[]): infer Compiled;
51
+ }
52
+ ? ReflectCompiled<Compiled>
53
+ : ReflectCompiled<DT>
54
+ : never;
55
+
56
+ type BuilderReflectCompiled<T> = T extends {
57
+ builder: {
58
+ _inputDefinition: infer I extends StateDefinition;
59
+ _outputDefinition: infer O extends StateDefinition;
60
+ _configSchema?: infer C extends StateDefinition | undefined;
61
+ };
62
+ }
63
+ ? {
64
+ input: UpdateType<I>;
65
+ output: StateType<O>;
66
+ config: UpdateType<Exclude<C, undefined>>;
67
+ }
68
+ : never;
69
+
70
+ // @ts-expect-error
71
+ type BuilderReflect<T> =
72
+ Defactorify<T> extends infer DT
73
+ ? DT extends {
74
+ compile(...args: any[]): infer Compiled;
75
+ }
76
+ ? BuilderReflectCompiled<Compiled>
77
+ : BuilderReflectCompiled<DT>
78
+ : never;
79
+
80
+ // @ts-expect-error
81
+ type FilterAny<T> = 0 extends 1 & T ? never : T;
@@ -0,0 +1,50 @@
1
+ import { createLogger, format, transports } from "winston";
2
+ import { logger as honoLogger } from "hono/logger";
3
+ import { consoleFormat } from "winston-console-format";
4
+ const LOG_JSON = process.env.LOG_JSON === "true";
5
+ const LOG_LEVEL = process.env.LOG_LEVEL || "debug";
6
+ export const logger = createLogger({
7
+ level: LOG_LEVEL,
8
+ format: format.combine(format.errors({ stack: true }), format.timestamp(), format.json(), ...(!LOG_JSON
9
+ ? [
10
+ format.colorize({ all: true }),
11
+ format.padLevels(),
12
+ consoleFormat({
13
+ showMeta: true,
14
+ metaStrip: ["timestamp"],
15
+ inspectOptions: {
16
+ depth: Infinity,
17
+ colors: true,
18
+ maxArrayLength: Infinity,
19
+ breakLength: 120,
20
+ compact: Infinity,
21
+ },
22
+ }),
23
+ ]
24
+ : [
25
+ format.printf((info) => {
26
+ const { timestamp, level, message, ...rest } = info;
27
+ let event;
28
+ if (typeof message === "string") {
29
+ event = message;
30
+ }
31
+ else {
32
+ event = JSON.stringify(message);
33
+ }
34
+ if (rest.stack) {
35
+ rest.message = event;
36
+ event = rest.stack;
37
+ }
38
+ return JSON.stringify({ timestamp, level, event, ...rest });
39
+ }),
40
+ ])),
41
+ transports: [
42
+ new transports.Console({
43
+ handleExceptions: true,
44
+ handleRejections: true,
45
+ }),
46
+ ],
47
+ });
48
+ export const requestLogger = () => honoLogger((message, ...rest) => {
49
+ logger.info(message, ...rest);
50
+ });
@@ -0,0 +1,3 @@
1
+ import { register } from "node:module";
2
+ // enforce API @langchain/langgraph precedence
3
+ register("./graph/load.hooks.mjs", import.meta.url);
package/dist/queue.mjs ADDED
@@ -0,0 +1,91 @@
1
+ import { Runs, Threads } from "./storage/ops.mjs";
2
+ import { streamState, } from "./stream.mjs";
3
+ import { logger } from "./logging.mjs";
4
+ import { serializeError } from "./utils/serde.mjs";
5
+ const MAX_RETRY_ATTEMPTS = 3;
6
+ const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
7
+ export const queue = async () => {
8
+ while (true) {
9
+ for await (const { run, attempt, signal } of Runs.next()) {
10
+ await worker(run, attempt, signal);
11
+ }
12
+ // TODO: this is very suboptimal, we should implement subscription to the run
13
+ await sleep(1000 * Math.random());
14
+ }
15
+ };
16
+ const worker = async (run, attempt, abortSignal) => {
17
+ const startedAt = new Date();
18
+ let checkpoint = undefined;
19
+ let exception = undefined;
20
+ const temporary = run.kwargs.temporary;
21
+ logger.info("Starting background run", {
22
+ run_id: run.run_id,
23
+ run_attempt: attempt,
24
+ run_created_at: run.created_at,
25
+ run_started_at: startedAt,
26
+ run_queue_ms: startedAt.valueOf() - run.created_at.valueOf(),
27
+ });
28
+ const onCheckpoint = (value) => {
29
+ checkpoint = value;
30
+ };
31
+ const onTaskResult = (result) => {
32
+ if (checkpoint == null)
33
+ return;
34
+ const index = checkpoint.tasks.findIndex((task) => task.id === result.id);
35
+ checkpoint.tasks[index] = {
36
+ ...checkpoint.tasks[index],
37
+ ...result,
38
+ };
39
+ };
40
+ try {
41
+ if (attempt > MAX_RETRY_ATTEMPTS) {
42
+ throw new Error(`Run ${run.run_id} exceeded max attempts`);
43
+ }
44
+ try {
45
+ const stream = streamState(run, attempt, {
46
+ signal: abortSignal,
47
+ ...(!temporary ? { onCheckpoint, onTaskResult } : undefined),
48
+ });
49
+ for await (const { event, data } of stream) {
50
+ await Runs.Stream.publish(run.run_id, event, data);
51
+ }
52
+ }
53
+ catch (error) {
54
+ await Runs.Stream.publish(run.run_id, "error", serializeError(error));
55
+ throw error;
56
+ }
57
+ const endedAt = new Date();
58
+ logger.info("Background run succeeded", {
59
+ run_id: run.run_id,
60
+ run_attempt: attempt,
61
+ run_created_at: run.created_at,
62
+ run_started_at: startedAt,
63
+ run_ended_at: endedAt,
64
+ run_exec_ms: endedAt.valueOf() - startedAt.valueOf(),
65
+ });
66
+ await Runs.setStatus(run.run_id, "success");
67
+ }
68
+ catch (error) {
69
+ const endedAt = new Date();
70
+ if (error instanceof Error)
71
+ exception = error;
72
+ logger.info("Background run failed", {
73
+ exc_info: error,
74
+ run_id: run.run_id,
75
+ run_attempt: attempt,
76
+ run_created_at: run.created_at,
77
+ run_started_at: startedAt,
78
+ run_ended_at: endedAt,
79
+ run_exec_ms: endedAt.valueOf() - startedAt.valueOf(),
80
+ });
81
+ await Runs.setStatus(run.run_id, "error");
82
+ }
83
+ finally {
84
+ if (temporary) {
85
+ await Threads.delete(run.thread_id);
86
+ }
87
+ else {
88
+ await Threads.setStatus(run.thread_id, { checkpoint, exception });
89
+ }
90
+ }
91
+ };
@@ -0,0 +1,399 @@
1
+ import { z } from "zod";
2
+ export const AssistantConfigurable = z
3
+ .object({
4
+ thread_id: z.string().optional(),
5
+ thread_ts: z.string().optional(),
6
+ })
7
+ .catchall(z.unknown());
8
+ export const AssistantConfig = z
9
+ .object({
10
+ tags: z.array(z.string()).optional(),
11
+ recursion_limit: z.number().int().optional(),
12
+ configurable: AssistantConfigurable.optional(),
13
+ })
14
+ .catchall(z.unknown())
15
+ .describe("The configuration of an assistant.");
16
+ export const Assistant = z.object({
17
+ assistant_id: z.string().uuid(),
18
+ graph_id: z.string(),
19
+ config: AssistantConfig,
20
+ created_at: z.string(),
21
+ updated_at: z.string(),
22
+ metadata: z.object({}).catchall(z.any()),
23
+ });
24
+ export const AssistantCreate = z
25
+ .object({
26
+ assistant_id: z
27
+ .string()
28
+ .uuid()
29
+ .describe("The ID of the assistant. If not provided, an ID is generated.")
30
+ .optional(),
31
+ graph_id: z.string().describe("The graph to use."),
32
+ config: AssistantConfig.optional(),
33
+ metadata: z
34
+ .object({})
35
+ .catchall(z.unknown())
36
+ .describe("Metadata for the assistant.")
37
+ .optional(),
38
+ if_exists: z
39
+ .union([z.literal("raise"), z.literal("do_nothing")])
40
+ .optional(),
41
+ name: z.string().optional(),
42
+ })
43
+ .describe("Payload for creating an assistant.");
44
+ export const AssistantPatch = z
45
+ .object({
46
+ graph_id: z.string().describe("The graph to use.").optional(),
47
+ config: AssistantConfig.optional(),
48
+ metadata: z
49
+ .object({})
50
+ .catchall(z.any())
51
+ .describe("Metadata to merge with existing assistant metadata.")
52
+ .optional(),
53
+ })
54
+ .describe("Payload for updating an assistant.");
55
+ export const Config = z.object({
56
+ tags: z.array(z.string()).optional(),
57
+ recursion_limit: z.number().int().optional(),
58
+ configurable: z.object({}).catchall(z.any()).optional(),
59
+ });
60
+ export const Cron = z.object({
61
+ cron_id: z.string().uuid(),
62
+ thread_id: z.string().uuid(),
63
+ end_time: z.string(),
64
+ schedule: z.string(),
65
+ created_at: z.string(),
66
+ updated_at: z.string(),
67
+ payload: z.object({}).catchall(z.any()),
68
+ });
69
+ export const CheckpointSchema = z.object({
70
+ checkpoint_id: z.string(),
71
+ checkpoint_ns: z.string().nullish(),
72
+ checkpoint_map: z.record(z.unknown()).nullish(),
73
+ });
74
+ export const CronCreate = z
75
+ .object({
76
+ thread_id: z.string().uuid(),
77
+ assistant_id: z.string().uuid(),
78
+ checkpoint_id: z.string().optional(),
79
+ input: z
80
+ .union([
81
+ z.array(z.object({}).catchall(z.any())),
82
+ z.object({}).catchall(z.any()),
83
+ ])
84
+ .optional(),
85
+ metadata: z
86
+ .object({})
87
+ .catchall(z.any())
88
+ .describe("Metadata for the run.")
89
+ .optional(),
90
+ config: AssistantConfig.optional(),
91
+ webhook: z.string().url().optional(),
92
+ interrupt_before: z.union([z.enum(["*"]), z.array(z.string())]).optional(),
93
+ interrupt_after: z.union([z.enum(["*"]), z.array(z.string())]).optional(),
94
+ multitask_strategy: z
95
+ .enum(["reject", "rollback", "interrupt", "enqueue"])
96
+ .optional(),
97
+ })
98
+ .describe("Payload for creating a cron.");
99
+ export const CronSearch = z
100
+ .object({
101
+ assistant_id: z.string().uuid().optional(),
102
+ thread_id: z.string().uuid().optional(),
103
+ limit: z
104
+ .number()
105
+ .int()
106
+ .gte(1)
107
+ .lte(1000)
108
+ .describe("Maximum number to return.")
109
+ .optional(),
110
+ offset: z
111
+ .number()
112
+ .int()
113
+ .gte(0)
114
+ .describe("Offset to start from.")
115
+ .optional(),
116
+ })
117
+ .describe("Payload for listing crons");
118
+ export const GraphSchema = z.object({
119
+ graph_id: z.string(),
120
+ input_schema: z.object({}).catchall(z.any()).optional(),
121
+ output_schema: z.object({}).catchall(z.any()).optional(),
122
+ state_schema: z.object({}).catchall(z.any()),
123
+ config_schema: z.object({}).catchall(z.any()),
124
+ });
125
+ export const Run = z.object({
126
+ run_id: z.string().uuid(),
127
+ thread_id: z.string().uuid(),
128
+ assistant_id: z.string().uuid(),
129
+ created_at: z.string(),
130
+ updated_at: z.string(),
131
+ status: z.enum([
132
+ "pending",
133
+ "running",
134
+ "error",
135
+ "success",
136
+ "timeout",
137
+ "interrupted",
138
+ ]),
139
+ metadata: z.object({}).catchall(z.any()),
140
+ kwargs: z.object({}).catchall(z.any()),
141
+ multitask_strategy: z.enum(["reject", "rollback", "interrupt", "enqueue"]),
142
+ });
143
+ export const RunCreate = z
144
+ .object({
145
+ assistant_id: z.union([z.string().uuid(), z.string()]),
146
+ checkpoint_id: z.string().optional(),
147
+ checkpoint: CheckpointSchema.optional(),
148
+ input: z
149
+ .union([
150
+ z.array(z.object({}).catchall(z.any())),
151
+ z.object({}).catchall(z.any()),
152
+ z.null(),
153
+ ])
154
+ .optional(),
155
+ command: z
156
+ .object({
157
+ goto: z
158
+ .union([
159
+ z.union([
160
+ z.string(),
161
+ z.object({ node: z.string(), input: z.unknown().optional() }),
162
+ ]),
163
+ z.array(z.union([
164
+ z.string(),
165
+ z.object({ node: z.string(), input: z.unknown().optional() }),
166
+ ])),
167
+ ])
168
+ .optional(),
169
+ update: z
170
+ .union([
171
+ z.record(z.unknown()),
172
+ z.array(z.tuple([z.string(), z.unknown()])),
173
+ ])
174
+ .optional(),
175
+ resume: z.unknown().optional(),
176
+ })
177
+ .optional(),
178
+ metadata: z
179
+ .object({})
180
+ .catchall(z.any())
181
+ .describe("Metadata for the run.")
182
+ .optional(),
183
+ config: AssistantConfig.optional(),
184
+ webhook: z.string().url().optional(),
185
+ interrupt_before: z.union([z.enum(["*"]), z.array(z.string())]).optional(),
186
+ interrupt_after: z.union([z.enum(["*"]), z.array(z.string())]).optional(),
187
+ on_disconnect: z
188
+ .enum(["cancel", "continue"])
189
+ .optional()
190
+ .default("continue"),
191
+ multitask_strategy: z
192
+ .enum(["reject", "rollback", "interrupt", "enqueue"])
193
+ .optional(),
194
+ stream_mode: z
195
+ .union([
196
+ z.array(z.enum(["values", "messages", "updates", "events", "debug"])),
197
+ z.enum(["values", "messages", "updates", "events", "debug"]),
198
+ ])
199
+ .optional(),
200
+ stream_subgraphs: z.boolean().optional(),
201
+ after_seconds: z.number().optional(),
202
+ if_not_exists: z.enum(["reject", "create"]).optional(),
203
+ on_completion: z.enum(["delete", "keep"]).optional(),
204
+ feedback_keys: z.array(z.string()).optional(),
205
+ })
206
+ .describe("Payload for creating a stateful run.");
207
+ export const RunBatchCreate = z
208
+ .array(RunCreate)
209
+ .min(1)
210
+ .describe("Payload for creating a batch of runs.");
211
+ export const SearchResult = z
212
+ .object({
213
+ metadata: z
214
+ .object({})
215
+ .catchall(z.any())
216
+ .describe("Metadata to search for.")
217
+ .optional(),
218
+ limit: z
219
+ .number()
220
+ .int()
221
+ .gte(1)
222
+ .lte(1000)
223
+ .describe("Maximum number to return.")
224
+ .optional(),
225
+ offset: z
226
+ .number()
227
+ .int()
228
+ .gte(0)
229
+ .describe("Offset to start from.")
230
+ .optional(),
231
+ })
232
+ .describe("Payload for listing runs.");
233
+ export const AssistantSearchRequest = z
234
+ .object({
235
+ metadata: z
236
+ .object({})
237
+ .catchall(z.any())
238
+ .describe("Metadata to search for.")
239
+ .optional(),
240
+ graph_id: z.string().describe("Filter by graph ID.").optional(),
241
+ limit: z
242
+ .number()
243
+ .int()
244
+ .gte(1)
245
+ .lte(1000)
246
+ .describe("Maximum number to return.")
247
+ .optional(),
248
+ offset: z
249
+ .number()
250
+ .int()
251
+ .gte(0)
252
+ .describe("Offset to start from.")
253
+ .optional(),
254
+ })
255
+ .describe("Payload for listing assistants.");
256
+ export const ThreadSearchRequest = z
257
+ .object({
258
+ metadata: z
259
+ .record(z.unknown())
260
+ .describe("Metadata to search for.")
261
+ .optional(),
262
+ status: z
263
+ .enum(["idle", "busy", "interrupted"])
264
+ .describe("Filter by thread status.")
265
+ .optional(),
266
+ values: z
267
+ .record(z.unknown())
268
+ .describe("Filter by thread values.")
269
+ .optional(),
270
+ limit: z
271
+ .number()
272
+ .int()
273
+ .gte(1)
274
+ .lte(1000)
275
+ .describe("Maximum number to return.")
276
+ .optional(),
277
+ offset: z
278
+ .number()
279
+ .int()
280
+ .gte(0)
281
+ .describe("Offset to start from.")
282
+ .optional(),
283
+ })
284
+ .describe("Payload for listing threads.");
285
+ export const Thread = z.object({
286
+ thread_id: z.string().uuid(),
287
+ created_at: z.string(),
288
+ updated_at: z.string(),
289
+ metadata: z.record(z.unknown()).optional(),
290
+ status: z.enum(["idle", "busy", "interrupted"]).optional(),
291
+ });
292
+ export const ThreadCreate = z
293
+ .object({
294
+ thread_id: z
295
+ .string()
296
+ .uuid()
297
+ .describe("The ID of the thread. If not provided, an ID is generated.")
298
+ .optional(),
299
+ metadata: z
300
+ .object({})
301
+ .catchall(z.any())
302
+ .describe("Metadata for the thread.")
303
+ .optional(),
304
+ if_exists: z
305
+ .union([z.literal("raise"), z.literal("do_nothing")])
306
+ .optional(),
307
+ })
308
+ .describe("Payload for creating a thread.");
309
+ export const ThreadPatch = z
310
+ .object({
311
+ metadata: z
312
+ .object({})
313
+ .catchall(z.any())
314
+ .describe("Metadata to merge with existing thread metadata.")
315
+ .optional(),
316
+ })
317
+ .describe("Payload for patching a thread.");
318
+ export const ThreadState = z.object({
319
+ values: z.union([
320
+ z.array(z.object({}).catchall(z.any())),
321
+ z.object({}).catchall(z.any()),
322
+ ]),
323
+ next: z.array(z.string()),
324
+ checkpoint_id: z.string(),
325
+ metadata: z.object({}).catchall(z.any()),
326
+ created_at: z.string(),
327
+ parent_checkpoint_id: z.string(),
328
+ });
329
+ export const ThreadStatePatch = z
330
+ .object({ metadata: z.object({}).catchall(z.any()) })
331
+ .describe("Payload for patching state of a thread.");
332
+ export const ThreadStateSearch = z.object({
333
+ limit: z
334
+ .number()
335
+ .int()
336
+ .gte(1)
337
+ .lte(1000)
338
+ .describe("The maximum number of states to return.")
339
+ .optional(),
340
+ before: z
341
+ .string()
342
+ .describe("Return states before this checkpoint ID.")
343
+ .optional(),
344
+ metadata: z
345
+ .object({})
346
+ .catchall(z.any())
347
+ .describe("Filter states by metadata key-value pairs.")
348
+ .optional(),
349
+ });
350
+ export const ThreadStateUpdate = z
351
+ .object({
352
+ values: z
353
+ .union([
354
+ z.array(z.object({}).catchall(z.any())),
355
+ z.object({}).catchall(z.any()),
356
+ z.null(),
357
+ ])
358
+ .optional(),
359
+ checkpoint_id: z.string().optional(),
360
+ as_node: z.string().optional(),
361
+ })
362
+ .describe("Payload for adding state to a thread.");
363
+ export const AssistantLatestVersion = z.object({
364
+ version: z.number(),
365
+ });
366
+ export const StoreListNamespaces = z.object({
367
+ prefix: z.array(z.string()).optional(),
368
+ suffix: z.array(z.string()).optional(),
369
+ max_depth: z.number().optional(),
370
+ limit: z.number().default(100).optional(),
371
+ offset: z.number().default(0).optional(),
372
+ });
373
+ export const StoreSearchItems = z.object({
374
+ namespace_prefix: z.array(z.string()),
375
+ filter: z.record(z.unknown()).optional(),
376
+ limit: z.number().default(10).optional(),
377
+ offset: z.number().default(0).optional(),
378
+ query: z.string().optional(),
379
+ });
380
+ export const StorePutItem = z.object({
381
+ namespace: z.array(z.string()),
382
+ key: z.string(),
383
+ value: z.record(z.unknown()),
384
+ });
385
+ export const StoreDeleteItem = z.object({
386
+ namespace: z.array(z.string()).optional(),
387
+ key: z.string(),
388
+ });
389
+ export const StoreGetItem = z.object({
390
+ namespace: z
391
+ .string()
392
+ .optional()
393
+ .transform((value) => value?.split(".") ?? []),
394
+ key: z.string(),
395
+ });
396
+ export const coercedBoolean = z.string().transform((val) => {
397
+ const lower = val.toLowerCase();
398
+ return lower === "true" || lower === "1" || lower === "yes";
399
+ });
@@ -0,0 +1,63 @@
1
+ import { serve } from "@hono/node-server";
2
+ import { Hono } from "hono";
3
+ import { cors } from "hono/cors";
4
+ import { registerFromEnv } from "./graph/load.mjs";
5
+ import runs from "./api/runs.mjs";
6
+ import threads from "./api/threads.mjs";
7
+ import assistants from "./api/assistants.mjs";
8
+ import store from "./api/store.mjs";
9
+ import { truncate, conn as opsConn } from "./storage/ops.mjs";
10
+ import { zValidator } from "@hono/zod-validator";
11
+ import { z } from "zod";
12
+ import { queue } from "./queue.mjs";
13
+ import { logger, requestLogger } from "./logging.mjs";
14
+ import { checkpointer } from "./storage/checkpoint.mjs";
15
+ import { store as graphStore } from "./storage/store.mjs";
16
+ const app = new Hono();
17
+ app.use(cors());
18
+ app.use(requestLogger());
19
+ app.route("/", assistants);
20
+ app.route("/", runs);
21
+ app.route("/", threads);
22
+ app.route("/", store);
23
+ app.get("/info", (c) => c.json({ flags: { assistants: true, crons: false } }));
24
+ app.post("/internal/truncate", zValidator("json", z.object({
25
+ runs: z.boolean().optional(),
26
+ threads: z.boolean().optional(),
27
+ assistants: z.boolean().optional(),
28
+ checkpointer: z.boolean().optional(),
29
+ store: z.boolean().optional(),
30
+ })), (c) => {
31
+ const { runs, threads, assistants, checkpointer, store } = c.req.valid("json");
32
+ truncate({ runs, threads, assistants, checkpointer, store });
33
+ return c.json({ ok: true });
34
+ });
35
+ export const StartServerSchema = z.object({
36
+ port: z.number(),
37
+ nWorkers: z.number(),
38
+ host: z.string(),
39
+ cwd: z.string(),
40
+ graphs: z.record(z.string()),
41
+ });
42
+ export async function startServer(options) {
43
+ logger.info(`Initializing storage...`);
44
+ const callbacks = await Promise.all([
45
+ opsConn.initialize(options.cwd),
46
+ checkpointer.initialize(options.cwd),
47
+ graphStore.initialize(options.cwd),
48
+ ]);
49
+ const cleanup = async () => {
50
+ logger.info(`Flushing to persistent storage, exiting...`);
51
+ await Promise.all(callbacks.map((c) => c.flush()));
52
+ };
53
+ logger.info(`Registering graphs from ${options.cwd}`);
54
+ await registerFromEnv(options.graphs, { cwd: options.cwd });
55
+ logger.info(`Starting ${options.nWorkers} workers`);
56
+ for (let i = 0; i < options.nWorkers; i++)
57
+ queue();
58
+ return new Promise((resolve) => {
59
+ serve({ fetch: app.fetch, port: options.port, hostname: options.host }, (c) => {
60
+ resolve({ host: `${c.address}:${c.port}`, cleanup });
61
+ });
62
+ });
63
+ }