@langgraph-js/pure-graph 1.2.0 → 1.4.0

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 (79) hide show
  1. package/README.md +22 -14
  2. package/dist/adapter/hono/runs.js +18 -1
  3. package/dist/adapter/nextjs/router.js +18 -7
  4. package/dist/adapter/zod.d.ts +15 -8
  5. package/dist/adapter/zod.js +8 -0
  6. package/dist/global.d.ts +3 -2
  7. package/dist/storage/index.d.ts +4 -3
  8. package/dist/storage/index.js +13 -1
  9. package/dist/storage/memory/threads.d.ts +3 -2
  10. package/dist/storage/memory/threads.js +29 -2
  11. package/dist/storage/pg/checkpoint.d.ts +2 -0
  12. package/dist/storage/pg/checkpoint.js +9 -0
  13. package/dist/storage/pg/threads.d.ts +43 -0
  14. package/dist/storage/pg/threads.js +304 -0
  15. package/dist/storage/sqlite/threads.d.ts +3 -2
  16. package/dist/storage/sqlite/threads.js +37 -3
  17. package/dist/threads/index.d.ts +3 -2
  18. package/dist/threads/index.js +1 -26
  19. package/dist/tsconfig.tsbuildinfo +1 -0
  20. package/dist/types.d.ts +2 -1
  21. package/package.json +38 -5
  22. package/.prettierrc +0 -11
  23. package/bun.lock +0 -209
  24. package/dist/adapter/nextjs/zod.d.ts +0 -203
  25. package/dist/adapter/nextjs/zod.js +0 -60
  26. package/examples/nextjs/README.md +0 -36
  27. package/examples/nextjs/app/api/langgraph/[...path]/route.ts +0 -10
  28. package/examples/nextjs/app/favicon.ico +0 -0
  29. package/examples/nextjs/app/globals.css +0 -26
  30. package/examples/nextjs/app/layout.tsx +0 -34
  31. package/examples/nextjs/app/page.tsx +0 -211
  32. package/examples/nextjs/next.config.ts +0 -26
  33. package/examples/nextjs/package.json +0 -24
  34. package/examples/nextjs/postcss.config.mjs +0 -5
  35. package/examples/nextjs/tsconfig.json +0 -27
  36. package/packages/agent-graph/demo.json +0 -35
  37. package/packages/agent-graph/package.json +0 -18
  38. package/packages/agent-graph/src/index.ts +0 -47
  39. package/packages/agent-graph/src/tools/tavily.ts +0 -9
  40. package/packages/agent-graph/src/tools.ts +0 -38
  41. package/packages/agent-graph/src/types.ts +0 -42
  42. package/pnpm-workspace.yaml +0 -4
  43. package/src/adapter/hono/assistants.ts +0 -24
  44. package/src/adapter/hono/endpoint.ts +0 -3
  45. package/src/adapter/hono/index.ts +0 -14
  46. package/src/adapter/hono/runs.ts +0 -65
  47. package/src/adapter/hono/threads.ts +0 -37
  48. package/src/adapter/nextjs/endpoint.ts +0 -2
  49. package/src/adapter/nextjs/index.ts +0 -2
  50. package/src/adapter/nextjs/router.ts +0 -193
  51. package/src/adapter/nextjs/zod.ts +0 -66
  52. package/src/adapter/zod.ts +0 -135
  53. package/src/createEndpoint.ts +0 -116
  54. package/src/e.d.ts +0 -3
  55. package/src/global.ts +0 -11
  56. package/src/graph/stream.ts +0 -263
  57. package/src/graph/stringify.ts +0 -219
  58. package/src/index.ts +0 -6
  59. package/src/queue/JsonPlusSerializer.ts +0 -143
  60. package/src/queue/event_message.ts +0 -30
  61. package/src/queue/stream_queue.ts +0 -237
  62. package/src/storage/index.ts +0 -52
  63. package/src/storage/memory/checkpoint.ts +0 -2
  64. package/src/storage/memory/queue.ts +0 -91
  65. package/src/storage/memory/threads.ts +0 -154
  66. package/src/storage/redis/queue.ts +0 -148
  67. package/src/storage/sqlite/DB.ts +0 -16
  68. package/src/storage/sqlite/checkpoint.ts +0 -503
  69. package/src/storage/sqlite/threads.ts +0 -366
  70. package/src/storage/sqlite/type.ts +0 -12
  71. package/src/threads/index.ts +0 -51
  72. package/src/types.ts +0 -116
  73. package/src/utils/createEntrypointGraph.ts +0 -20
  74. package/src/utils/getGraph.ts +0 -44
  75. package/src/utils/getLangGraphCommand.ts +0 -21
  76. package/test/graph/entrypoint.ts +0 -21
  77. package/test/graph/index.ts +0 -60
  78. package/test/hono.ts +0 -15
  79. package/tsconfig.json +0 -20
package/README.md CHANGED
@@ -1,6 +1,13 @@
1
1
  # Pure Graph
2
2
 
3
- Pure Graph is a library that provides a standard LangGraph endpoint for integrating into various frameworks like NextJS and Hono.js. This document will guide you on how to use Pure Graph in your projects.
3
+ Pure Graph is a library that provides a standard LangGraph endpoint for integrating into various frameworks like NextJS and Hono.js. It supports multiple storage backends (SQLite, PostgreSQL, Redis) and message queues. This document will guide you on how to use Pure Graph in your projects.
4
+
5
+ ## Features
6
+
7
+ - **Multiple Storage Backends**: Support for SQLite, PostgreSQL, Redis, and in-memory storage
8
+ - **Message Queue**: Redis-based stream queue with TTL support
9
+ - **Thread Management**: Comprehensive thread lifecycle management with status tracking
10
+ - **Framework Integration**: Native support for Next.js and Hono.js frameworks
4
11
 
5
12
  ## Installation
6
13
 
@@ -84,27 +91,28 @@ To integrate Pure Graph into a Hono.js project, follow these steps:
84
91
 
85
92
  Here are the environment variables you need to configure:
86
93
 
87
- - `SQLITE_DATABASE_URI`: Path to your SQLite database.
88
- - `CHECKPOINT_TYPE`: Type of checkpoint storage (e.g., `postgres`, `redis`, `shallow/redis`).
89
- - `REDIS_URL`: URL for Redis (required if using Redis).
94
+ - `SQLITE_DATABASE_URI`: Path to your SQLite database (e.g., `./.langgraph_api/chat.db`).
95
+ - `DATABASE_URL`: PostgreSQL connection string (required for PostgreSQL checkpoint storage).
96
+ - `CHECKPOINT_TYPE`: Type of checkpoint storage (optional, defaults to memory; options: `postgres`, `redis`, `shallow/redis`).
97
+ - `REDIS_URL`: URL for Redis (required if using Redis checkpoint or message queue).
90
98
 
91
99
  ## API Endpoints
92
100
 
93
101
  ### Assistants
94
102
 
95
- - **GET /assistants**: Search for assistants.
96
- - **GET /assistants/{assistantId}**: Retrieve a specific assistant graph.
103
+ - **GET /assistants**: Search for assistants.
104
+ - **GET /assistants/{assistantId}**: Retrieve a specific assistant graph.
97
105
 
98
106
  ### Threads
99
107
 
100
- - **POST /threads**: Create a new thread.
101
- - **GET /threads**: Search for threads.
102
- - **GET /threads/{threadId}**: Retrieve a specific thread.
103
- - **DELETE /threads/{threadId}**: Delete a specific thread.
108
+ - **POST /threads**: Create a new thread.
109
+ - **GET /threads**: Search for threads.
110
+ - **GET /threads/{threadId}**: Retrieve a specific thread.
111
+ - **DELETE /threads/{threadId}**: Delete a specific thread.
104
112
 
105
113
  ### Runs
106
114
 
107
- - **GET /threads/{threadId}/runs**: List runs in a thread.
108
- - **POST /threads/{threadId}/runs**: Create a new run.
109
- - **DELETE /threads/{threadId}/runs/{runId}**: Cancel a specific run.
110
- - **GET /threads/{threadId}/runs/{runId}/stream**: Stream run data.
115
+ - **GET /threads/{threadId}/runs**: List runs in a thread.
116
+ - **POST /threads/{threadId}/runs**: Create a new run.
117
+ - **DELETE /threads/{threadId}/runs/{runId}**: Cancel a specific run.
118
+ - **GET /threads/{threadId}/runs/{runId}/stream**: Stream run data.
@@ -2,8 +2,9 @@ import { zValidator } from '@hono/zod-validator';
2
2
  import { Hono } from 'hono';
3
3
  import { streamSSE } from 'hono/streaming';
4
4
  import { client } from './endpoint';
5
- import { ThreadIdParamSchema, RunIdParamSchema, RunStreamPayloadSchema, RunListQuerySchema, RunCancelQuerySchema, } from '../zod';
5
+ import { ThreadIdParamSchema, RunIdParamSchema, RunStreamPayloadSchema, RunListQuerySchema, RunCancelQuerySchema, ThreadStateUpdate, } from '../zod';
6
6
  import { serialiseAsDict } from '../../graph/stream';
7
+ import z from 'zod';
7
8
  const api = new Hono();
8
9
  // 最常用的对话接口
9
10
  api.post('/threads/:thread_id/runs/stream', zValidator('param', ThreadIdParamSchema), zValidator('json', RunStreamPayloadSchema), async (c) => {
@@ -34,4 +35,20 @@ api.post('/threads/:thread_id/runs/:run_id/cancel', zValidator('param', RunIdPar
34
35
  }
35
36
  return c.body(null, wait ? 204 : 202);
36
37
  });
38
+ api.post('/threads/:thread_id/state', zValidator('param', z.object({ thread_id: z.string().uuid() })), zValidator('json', ThreadStateUpdate), async (c) => {
39
+ // Update Thread State
40
+ const { thread_id } = c.req.valid('param');
41
+ const payload = c.req.valid('json');
42
+ // const config: RunnableConfig = { configurable: { thread_id } };
43
+ // if (payload.checkpoint_id) {
44
+ // config.configurable ??= {};
45
+ // config.configurable.checkpoint_id = payload.checkpoint_id;
46
+ // }
47
+ // if (payload.checkpoint) {
48
+ // config.configurable ??= {};
49
+ // Object.assign(config.configurable, payload.checkpoint);
50
+ // }
51
+ const inserted = await client.threads.updateState(thread_id, payload);
52
+ return c.json(inserted);
53
+ });
37
54
  export default api;
@@ -1,7 +1,7 @@
1
1
  /** @ts-ignore */
2
2
  import { NextResponse } from 'next/server';
3
3
  import { client } from './endpoint';
4
- import { AssistantsSearchSchema, AssistantGraphQuerySchema, RunStreamPayloadSchema, RunListQuerySchema, RunCancelQuerySchema, ThreadCreatePayloadSchema, ThreadSearchPayloadSchema, } from '../zod';
4
+ import { AssistantsSearchSchema, AssistantGraphQuerySchema, RunStreamPayloadSchema, RunListQuerySchema, RunCancelQuerySchema, ThreadCreatePayloadSchema, ThreadSearchPayloadSchema, ThreadStateUpdate, } from '../zod';
5
5
  import { serialiseAsDict } from '../../graph/stream';
6
6
  // Next.js App Router 的 SSE 响应实现
7
7
  async function sseResponse(generator) {
@@ -39,7 +39,7 @@ export async function GET(req) {
39
39
  const match = pathname.match(/\/assistants\/([^/]+)\/graph$/);
40
40
  if (match) {
41
41
  const assistant_id = match[1];
42
- const xrayParam = url.searchParams.get('xray');
42
+ const xrayParam = url.searchParams.get('xray') ?? undefined;
43
43
  const queryParams = { xray: xrayParam };
44
44
  const { xray } = AssistantGraphQuerySchema.parse(queryParams);
45
45
  const data = await client.assistants.getGraph(assistant_id, {
@@ -62,9 +62,9 @@ export async function GET(req) {
62
62
  const match = pathname.match(/\/threads\/([0-9a-fA-F-]{36})\/runs$/);
63
63
  if (match) {
64
64
  const thread_id = match[1];
65
- const limit = url.searchParams.get('limit');
66
- const offset = url.searchParams.get('offset');
67
- const status = url.searchParams.get('status');
65
+ const limit = url.searchParams.get('limit') ?? undefined;
66
+ const offset = url.searchParams.get('offset') ?? undefined;
67
+ const status = url.searchParams.get('status') ?? undefined;
68
68
  const queryParams = { limit, offset, status };
69
69
  const { limit: parsedLimit, offset: parsedOffset, status: parsedStatus, } = RunListQuerySchema.parse(queryParams);
70
70
  const runs = await client.runs.list(thread_id, {
@@ -120,6 +120,17 @@ export async function POST(req) {
120
120
  headers: { 'X-Pagination-Total': String(result.length) },
121
121
  });
122
122
  }
123
+ // Threads state update
124
+ if (pathname.match(/\/threads\/[0-9a-fA-F-]{36}\/state$/)) {
125
+ const match = pathname.match(/\/threads\/([0-9a-fA-F-]{36})\/state$/);
126
+ if (match) {
127
+ const thread_id = match[1];
128
+ const body = await req.json();
129
+ const payload = ThreadStateUpdate.parse(body);
130
+ const result = await client.threads.updateState(thread_id, payload);
131
+ return NextResponse.json(result);
132
+ }
133
+ }
123
134
  // Runs routes - stream
124
135
  if (pathname.match(/\/threads\/[0-9a-fA-F-]{36}\/runs\/stream$/)) {
125
136
  const match = pathname.match(/\/threads\/([0-9a-fA-F-]{36})\/runs\/stream$/);
@@ -137,8 +148,8 @@ export async function POST(req) {
137
148
  if (match) {
138
149
  const thread_id = match[1];
139
150
  const run_id = match[2];
140
- const waitParam = url.searchParams.get('wait');
141
- const actionParam = url.searchParams.get('action');
151
+ const waitParam = url.searchParams.get('wait') ?? undefined;
152
+ const actionParam = url.searchParams.get('action') ?? undefined;
142
153
  const queryParams = {
143
154
  wait: waitParam ? waitParam === 'true' : false,
144
155
  action: actionParam ?? 'interrupt',
@@ -101,6 +101,7 @@ export declare const Assistant: z.ZodObject<{
101
101
  metadata: {} & {
102
102
  [k: string]: any;
103
103
  };
104
+ graph_id: string;
104
105
  assistant_id: string;
105
106
  config: {
106
107
  configurable?: z.objectOutputType<{
@@ -112,13 +113,13 @@ export declare const Assistant: z.ZodObject<{
112
113
  } & {
113
114
  [k: string]: unknown;
114
115
  };
115
- graph_id: string;
116
116
  }, {
117
117
  created_at: string;
118
118
  updated_at: string;
119
119
  metadata: {} & {
120
120
  [k: string]: any;
121
121
  };
122
+ graph_id: string;
122
123
  assistant_id: string;
123
124
  config: {
124
125
  configurable?: z.objectInputType<{
@@ -130,7 +131,6 @@ export declare const Assistant: z.ZodObject<{
130
131
  } & {
131
132
  [k: string]: unknown;
132
133
  };
133
- graph_id: string;
134
134
  }>;
135
135
  export declare const MetadataSchema: z.ZodObject<{
136
136
  source: z.ZodOptional<z.ZodUnion<[z.ZodLiteral<"input">, z.ZodLiteral<"loop">, z.ZodLiteral<"update">, z.ZodString]>>;
@@ -255,9 +255,9 @@ export declare const AssistantsSearchSchema: z.ZodObject<{
255
255
  writes: z.ZodOptional<z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
256
256
  parents: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
257
257
  }, z.ZodUnknown, "strip"> | undefined;
258
+ graph_id?: string | undefined;
258
259
  limit?: number | undefined;
259
260
  offset?: number | undefined;
260
- graph_id?: string | undefined;
261
261
  }, {
262
262
  metadata?: z.objectInputType<{
263
263
  source: z.ZodOptional<z.ZodUnion<[z.ZodLiteral<"input">, z.ZodLiteral<"loop">, z.ZodLiteral<"update">, z.ZodString]>>;
@@ -265,9 +265,9 @@ export declare const AssistantsSearchSchema: z.ZodObject<{
265
265
  writes: z.ZodOptional<z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
266
266
  parents: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
267
267
  }, z.ZodUnknown, "strip"> | undefined;
268
+ graph_id?: string | undefined;
268
269
  limit?: number | undefined;
269
270
  offset?: number | undefined;
270
- graph_id?: string | undefined;
271
271
  }>;
272
272
  export declare const AssistantGraphQuerySchema: z.ZodObject<{
273
273
  xray: z.ZodOptional<z.ZodString>;
@@ -395,13 +395,13 @@ export declare const RunStreamPayloadSchema: z.ZodObject<{
395
395
  }, "strip", z.ZodTypeAny, {
396
396
  assistant_id: string;
397
397
  on_disconnect: "cancel" | "continue";
398
+ input?: any;
398
399
  metadata?: z.objectOutputType<{
399
400
  source: z.ZodOptional<z.ZodUnion<[z.ZodLiteral<"input">, z.ZodLiteral<"loop">, z.ZodLiteral<"update">, z.ZodString]>>;
400
401
  step: z.ZodOptional<z.ZodNumber>;
401
402
  writes: z.ZodOptional<z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
402
403
  parents: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
403
404
  }, z.ZodUnknown, "strip"> | undefined;
404
- input?: any;
405
405
  multitask_strategy?: "reject" | "interrupt" | "rollback" | "enqueue" | undefined;
406
406
  checkpoint_id?: string | undefined;
407
407
  config?: z.objectOutputType<{
@@ -432,7 +432,7 @@ export declare const RunStreamPayloadSchema: z.ZodObject<{
432
432
  webhook?: string | undefined;
433
433
  interrupt_before?: string[] | "*" | undefined;
434
434
  interrupt_after?: string[] | "*" | undefined;
435
- stream_mode?: ("values" | "events" | "messages" | "updates" | "debug" | "custom" | "messages-tuple")[] | undefined;
435
+ stream_mode?: ("values" | "updates" | "debug" | "messages" | "custom" | "events" | "messages-tuple")[] | undefined;
436
436
  stream_subgraphs?: boolean | undefined;
437
437
  stream_resumable?: boolean | undefined;
438
438
  after_seconds?: number | undefined;
@@ -442,13 +442,13 @@ export declare const RunStreamPayloadSchema: z.ZodObject<{
442
442
  langsmith_tracer?: unknown;
443
443
  }, {
444
444
  assistant_id: string;
445
+ input?: any;
445
446
  metadata?: z.objectInputType<{
446
447
  source: z.ZodOptional<z.ZodUnion<[z.ZodLiteral<"input">, z.ZodLiteral<"loop">, z.ZodLiteral<"update">, z.ZodString]>>;
447
448
  step: z.ZodOptional<z.ZodNumber>;
448
449
  writes: z.ZodOptional<z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
449
450
  parents: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
450
451
  }, z.ZodUnknown, "strip"> | undefined;
451
- input?: any;
452
452
  multitask_strategy?: "reject" | "interrupt" | "rollback" | "enqueue" | undefined;
453
453
  checkpoint_id?: string | undefined;
454
454
  config?: z.objectInputType<{
@@ -480,7 +480,7 @@ export declare const RunStreamPayloadSchema: z.ZodObject<{
480
480
  interrupt_before?: string[] | "*" | undefined;
481
481
  interrupt_after?: string[] | "*" | undefined;
482
482
  on_disconnect?: "cancel" | "continue" | undefined;
483
- stream_mode?: ("values" | "events" | "messages" | "updates" | "debug" | "custom" | "messages-tuple")[] | undefined;
483
+ stream_mode?: ("values" | "updates" | "debug" | "messages" | "custom" | "events" | "messages-tuple")[] | undefined;
484
484
  stream_subgraphs?: boolean | undefined;
485
485
  stream_resumable?: boolean | undefined;
486
486
  after_seconds?: number | undefined;
@@ -575,3 +575,10 @@ export declare const ThreadSearchPayloadSchema: z.ZodObject<{
575
575
  sort_by?: "thread_id" | "status" | "created_at" | "updated_at" | undefined;
576
576
  sort_order?: "asc" | "desc" | undefined;
577
577
  }>;
578
+ export declare const ThreadStateUpdate: z.ZodObject<{
579
+ values: z.ZodOptional<z.ZodNullable<z.ZodUnion<[z.ZodRecord<z.ZodString, z.ZodUnknown>, z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>, "many">]>>>;
580
+ }, "strip", z.ZodTypeAny, {
581
+ values?: Record<string, unknown> | Record<string, unknown>[] | null | undefined;
582
+ }, {
583
+ values?: Record<string, unknown> | Record<string, unknown>[] | null | undefined;
584
+ }>;
@@ -117,3 +117,11 @@ export const ThreadSearchPayloadSchema = z
117
117
  sort_order: z.enum(['asc', 'desc']).describe('Sort order.').optional(),
118
118
  })
119
119
  .describe('Payload for listing threads.');
120
+ export const ThreadStateUpdate = z
121
+ .object({
122
+ values: z.union([z.record(z.string(), z.unknown()), z.array(z.record(z.string(), z.unknown()))]).nullish(),
123
+ // as_node: z.string().optional(),
124
+ // checkpoint_id: z.string().optional(),
125
+ // checkpoint: CheckpointSchema.nullish(),
126
+ })
127
+ .describe('Payload for adding state to a thread.');
package/dist/global.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import type { SqliteSaver } from './storage/sqlite/checkpoint.js';
2
+ import type { PostgresSaver } from '@langchain/langgraph-checkpoint-postgres';
2
3
  export declare class LangGraphGlobal {
3
4
  static globalMessageQueue: import("./queue/stream_queue.js").StreamQueueManager<import("./queue/stream_queue.js").BaseStreamQueueInterface>;
4
- static globalCheckPointer: SqliteSaver | import("@langchain/langgraph-checkpoint-redis").RedisSaver | import("@langchain/langgraph-checkpoint-redis/shallow").ShallowRedisSaver | import("@langchain/langgraph-checkpoint").MemorySaver;
5
- static globalThreadsManager: import("./storage/sqlite/threads.js").SQLiteThreadsManager<unknown> | import("./storage/memory/threads.js").MemoryThreadsManager<unknown>;
5
+ static globalCheckPointer: import("@langchain/langgraph-checkpoint-redis").RedisSaver | import("@langchain/langgraph-checkpoint-redis/shallow").ShallowRedisSaver | PostgresSaver | SqliteSaver | import("@langchain/langgraph-checkpoint").MemorySaver;
6
+ static globalThreadsManager: import("./storage/pg/threads.js").PostgresThreadsManager<unknown> | import("./storage/sqlite/threads.js").SQLiteThreadsManager<unknown> | import("./storage/memory/threads.js").MemoryThreadsManager<unknown>;
6
7
  }
@@ -2,9 +2,10 @@ import { BaseStreamQueueInterface, StreamQueueManager } from '../queue/stream_qu
2
2
  import { MemorySaver } from './memory/checkpoint';
3
3
  import { MemoryThreadsManager } from './memory/threads';
4
4
  import type { SqliteSaver as SqliteSaverType } from './sqlite/checkpoint';
5
+ import type { PostgresSaver } from '@langchain/langgraph-checkpoint-postgres';
5
6
  import { SQLiteThreadsManager } from './sqlite/threads';
6
- export declare const createCheckPointer: () => Promise<SqliteSaverType | import("@langchain/langgraph-checkpoint-redis").RedisSaver | import("@langchain/langgraph-checkpoint-redis/shallow").ShallowRedisSaver | MemorySaver>;
7
+ export declare const createCheckPointer: () => Promise<import("@langchain/langgraph-checkpoint-redis").RedisSaver | import("@langchain/langgraph-checkpoint-redis/shallow").ShallowRedisSaver | PostgresSaver | SqliteSaverType | MemorySaver>;
7
8
  export declare const createMessageQueue: () => Promise<StreamQueueManager<BaseStreamQueueInterface>>;
8
9
  export declare const createThreadManager: (config: {
9
- checkpointer?: SqliteSaverType;
10
- }) => SQLiteThreadsManager<unknown> | MemoryThreadsManager<unknown>;
10
+ checkpointer?: SqliteSaverType | PostgresSaver;
11
+ }) => Promise<import("./pg/threads").PostgresThreadsManager<unknown> | SQLiteThreadsManager<unknown> | MemoryThreadsManager<unknown>>;
@@ -8,6 +8,7 @@ export const createCheckPointer = async () => {
8
8
  if ((process.env.REDIS_URL && process.env.CHECKPOINT_TYPE === 'redis') ||
9
9
  process.env.CHECKPOINT_TYPE === 'shallow/redis') {
10
10
  if (process.env.CHECKPOINT_TYPE === 'redis') {
11
+ console.log('Using redis as checkpoint');
11
12
  const { RedisSaver } = await import('@langchain/langgraph-checkpoint-redis');
12
13
  return await RedisSaver.fromUrl(process.env.REDIS_URL, {
13
14
  defaultTTL: 60, // TTL in minutes
@@ -15,11 +16,18 @@ export const createCheckPointer = async () => {
15
16
  });
16
17
  }
17
18
  if (process.env.CHECKPOINT_TYPE === 'shallow/redis') {
19
+ console.log('Using shallow redis as checkpoint');
18
20
  const { ShallowRedisSaver } = await import('@langchain/langgraph-checkpoint-redis/shallow');
19
21
  return await ShallowRedisSaver.fromUrl(process.env.REDIS_URL);
20
22
  }
21
23
  }
24
+ if (process.env.DATABASE_URL) {
25
+ console.log('Using postgres as checkpoint');
26
+ const { createPGCheckpoint } = await import('./pg/checkpoint');
27
+ return createPGCheckpoint();
28
+ }
22
29
  if (process.env.SQLITE_DATABASE_URI) {
30
+ console.log('Using sqlite as checkpoint');
23
31
  const { SqliteSaver } = await import('./sqlite/checkpoint');
24
32
  const db = SqliteSaver.fromConnString(process.env.SQLITE_DATABASE_URI);
25
33
  return db;
@@ -38,7 +46,11 @@ export const createMessageQueue = async () => {
38
46
  }
39
47
  return new StreamQueueManager(q);
40
48
  };
41
- export const createThreadManager = (config) => {
49
+ export const createThreadManager = async (config) => {
50
+ if (process.env.DATABASE_URL && config.checkpointer) {
51
+ const { PostgresThreadsManager } = await import('./pg/threads');
52
+ return new PostgresThreadsManager(config.checkpointer);
53
+ }
42
54
  if (process.env.SQLITE_DATABASE_URI && config.checkpointer) {
43
55
  return new SQLiteThreadsManager(config.checkpointer);
44
56
  }
@@ -1,6 +1,6 @@
1
1
  import { BaseThreadsManager } from '../../threads/index.js';
2
- import { Command, Metadata, OnConflictBehavior, Run, RunStatus, SortOrder, Thread, ThreadSortBy, ThreadStatus } from '@langgraph-js/sdk';
3
- export declare class MemoryThreadsManager<ValuesType = unknown> extends BaseThreadsManager {
2
+ import { Command, Config, Metadata, OnConflictBehavior, Run, RunStatus, SortOrder, Thread, ThreadSortBy, ThreadStatus } from '@langgraph-js/sdk';
3
+ export declare class MemoryThreadsManager<ValuesType = unknown> implements BaseThreadsManager<ValuesType> {
4
4
  private threads;
5
5
  create(payload?: {
6
6
  metadata?: Metadata;
@@ -26,6 +26,7 @@ export declare class MemoryThreadsManager<ValuesType = unknown> extends BaseThre
26
26
  get(threadId: string): Promise<Thread<ValuesType>>;
27
27
  set(threadId: string, thread: Partial<Thread<ValuesType>>): Promise<void>;
28
28
  delete(threadId: string): Promise<void>;
29
+ updateState(threadId: string, thread: Partial<Thread<ValuesType>>): Promise<Pick<Config, 'configurable'>>;
29
30
  runs: Run[];
30
31
  createRun(threadId: string, assistantId: string, payload?: {
31
32
  metadata?: Metadata;
@@ -1,5 +1,6 @@
1
- import { BaseThreadsManager } from '../../threads/index.js';
2
- export class MemoryThreadsManager extends BaseThreadsManager {
1
+ import { getGraph } from '../../utils/getGraph.js';
2
+ import { serialiseAsDict } from '../../graph/stream.js';
3
+ export class MemoryThreadsManager {
3
4
  threads = [];
4
5
  async create(payload) {
5
6
  const threadId = payload?.threadId || crypto.randomUUID();
@@ -79,6 +80,32 @@ export class MemoryThreadsManager extends BaseThreadsManager {
79
80
  throw new Error(`Thread with ID ${threadId} not found.`);
80
81
  }
81
82
  }
83
+ async updateState(threadId, thread) {
84
+ const index = this.threads.findIndex((t) => t.thread_id === threadId);
85
+ if (index === -1) {
86
+ throw new Error(`Thread with ID ${threadId} not found.`);
87
+ }
88
+ const targetThread = this.threads[index];
89
+ if (targetThread.status === 'busy') {
90
+ throw new Error(`Thread with ID ${threadId} is busy, can't update state.`);
91
+ }
92
+ this.threads[index] = { ...targetThread, values: thread.values };
93
+ if (!targetThread.metadata?.graph_id) {
94
+ throw new Error(`Thread with ID ${threadId} has no graph_id.`);
95
+ }
96
+ const graphId = targetThread.metadata?.graph_id;
97
+ const config = {
98
+ configurable: {
99
+ thread_id: threadId,
100
+ graph_id: graphId,
101
+ },
102
+ };
103
+ const graph = await getGraph(graphId, config);
104
+ const nextConfig = await graph.updateState(config, thread.values);
105
+ const graphState = await graph.getState(config);
106
+ await this.set(threadId, { values: JSON.parse(serialiseAsDict(graphState.values)) });
107
+ return nextConfig;
108
+ }
82
109
  runs = [];
83
110
  async createRun(threadId, assistantId, payload) {
84
111
  const runId = crypto.randomUUID();
@@ -0,0 +1,2 @@
1
+ import { PostgresSaver } from '@langchain/langgraph-checkpoint-postgres';
2
+ export declare const createPGCheckpoint: () => Promise<PostgresSaver>;
@@ -0,0 +1,9 @@
1
+ import { PostgresSaver } from '@langchain/langgraph-checkpoint-postgres';
2
+ export const createPGCheckpoint = async () => {
3
+ const checkpointer = PostgresSaver.fromConnString(process.env.DATABASE_URL);
4
+ if (process.env.DATABASE_INIT === 'true') {
5
+ console.log('Initializing postgres checkpoint');
6
+ await checkpointer.setup();
7
+ }
8
+ return checkpointer;
9
+ };
@@ -0,0 +1,43 @@
1
+ import { BaseThreadsManager } from '../../threads/index.js';
2
+ import { Command, Config, Metadata, OnConflictBehavior, Run, RunStatus, SortOrder, Thread, ThreadSortBy, ThreadStatus } from '@langgraph-js/sdk';
3
+ import type { PostgresSaver } from '@langchain/langgraph-checkpoint-postgres';
4
+ export declare class PostgresThreadsManager<ValuesType = unknown> implements BaseThreadsManager<ValuesType> {
5
+ private pool;
6
+ private isSetup;
7
+ constructor(checkpointer: PostgresSaver);
8
+ private setup;
9
+ create(payload?: {
10
+ metadata?: Metadata;
11
+ threadId?: string;
12
+ ifExists?: OnConflictBehavior;
13
+ graphId?: string;
14
+ supersteps?: Array<{
15
+ updates: Array<{
16
+ values: unknown;
17
+ command?: Command;
18
+ asNode: string;
19
+ }>;
20
+ }>;
21
+ }): Promise<Thread<ValuesType>>;
22
+ search(query?: {
23
+ metadata?: Metadata;
24
+ limit?: number;
25
+ offset?: number;
26
+ status?: ThreadStatus;
27
+ sortBy?: ThreadSortBy;
28
+ sortOrder?: SortOrder;
29
+ }): Promise<Thread<ValuesType>[]>;
30
+ get(threadId: string): Promise<Thread<ValuesType>>;
31
+ set(threadId: string, thread: Partial<Thread<ValuesType>>): Promise<void>;
32
+ updateState(threadId: string, thread: Partial<Thread<ValuesType>>): Promise<Pick<Config, 'configurable'>>;
33
+ delete(threadId: string): Promise<void>;
34
+ createRun(threadId: string, assistantId: string, payload?: {
35
+ metadata?: Metadata;
36
+ }): Promise<Run>;
37
+ listRuns(threadId: string, options?: {
38
+ limit?: number;
39
+ offset?: number;
40
+ status?: RunStatus;
41
+ }): Promise<Run[]>;
42
+ updateRun(runId: string, run: Partial<Run>): Promise<void>;
43
+ }