@langgraph-js/pure-graph 1.0.2 → 1.3.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.
- package/.prettierrc +11 -0
- package/README.md +104 -10
- package/bun.lock +209 -0
- package/dist/adapter/hono/assistants.js +3 -9
- package/dist/adapter/hono/endpoint.js +1 -2
- package/dist/adapter/hono/runs.js +23 -39
- package/dist/adapter/hono/threads.js +5 -46
- package/dist/adapter/nextjs/endpoint.d.ts +1 -0
- package/dist/adapter/nextjs/endpoint.js +2 -0
- package/dist/adapter/nextjs/index.d.ts +1 -0
- package/dist/adapter/nextjs/index.js +2 -0
- package/dist/adapter/nextjs/router.d.ts +5 -0
- package/dist/adapter/nextjs/router.js +179 -0
- package/dist/adapter/nextjs/zod.d.ts +203 -0
- package/dist/adapter/nextjs/zod.js +60 -0
- package/dist/adapter/zod.d.ts +584 -0
- package/dist/adapter/zod.js +127 -0
- package/dist/createEndpoint.d.ts +1 -2
- package/dist/createEndpoint.js +4 -3
- package/dist/global.d.ts +6 -4
- package/dist/global.js +10 -5
- package/dist/graph/stream.d.ts +1 -1
- package/dist/graph/stream.js +18 -10
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/queue/stream_queue.d.ts +5 -3
- package/dist/queue/stream_queue.js +4 -2
- package/dist/storage/index.d.ts +9 -4
- package/dist/storage/index.js +38 -3
- package/dist/storage/memory/threads.d.ts +3 -2
- package/dist/storage/memory/threads.js +29 -2
- package/dist/storage/redis/queue.d.ts +39 -0
- package/dist/storage/redis/queue.js +130 -0
- package/dist/storage/sqlite/DB.d.ts +3 -0
- package/dist/storage/sqlite/DB.js +14 -0
- package/dist/storage/sqlite/checkpoint.d.ts +18 -0
- package/dist/storage/sqlite/checkpoint.js +374 -0
- package/dist/storage/sqlite/threads.d.ts +44 -0
- package/dist/storage/sqlite/threads.js +300 -0
- package/dist/storage/sqlite/type.d.ts +15 -0
- package/dist/storage/sqlite/type.js +1 -0
- package/dist/threads/index.d.ts +3 -2
- package/dist/threads/index.js +1 -26
- package/dist/types.d.ts +2 -1
- package/dist/utils/createEntrypointGraph.d.ts +14 -0
- package/dist/utils/createEntrypointGraph.js +11 -0
- package/dist/utils/getGraph.js +3 -3
- package/examples/nextjs/README.md +36 -0
- package/examples/nextjs/app/api/langgraph/[...path]/route.ts +10 -0
- package/examples/nextjs/app/favicon.ico +0 -0
- package/examples/nextjs/app/globals.css +26 -0
- package/examples/nextjs/app/layout.tsx +34 -0
- package/examples/nextjs/app/page.tsx +211 -0
- package/examples/nextjs/next.config.ts +26 -0
- package/examples/nextjs/package.json +24 -0
- package/examples/nextjs/postcss.config.mjs +5 -0
- package/examples/nextjs/tsconfig.json +27 -0
- package/package.json +9 -4
- package/packages/agent-graph/demo.json +35 -0
- package/packages/agent-graph/package.json +18 -0
- package/packages/agent-graph/src/index.ts +47 -0
- package/packages/agent-graph/src/tools/tavily.ts +9 -0
- package/packages/agent-graph/src/tools.ts +38 -0
- package/packages/agent-graph/src/types.ts +42 -0
- package/pnpm-workspace.yaml +4 -0
- package/src/adapter/hono/assistants.ts +16 -33
- package/src/adapter/hono/endpoint.ts +1 -2
- package/src/adapter/hono/runs.ts +42 -51
- package/src/adapter/hono/threads.ts +15 -70
- package/src/adapter/nextjs/endpoint.ts +2 -0
- package/src/adapter/nextjs/index.ts +2 -0
- package/src/adapter/nextjs/router.ts +206 -0
- package/src/adapter/{hono → nextjs}/zod.ts +22 -5
- package/src/adapter/zod.ts +144 -0
- package/src/createEndpoint.ts +12 -5
- package/src/e.d.ts +3 -0
- package/src/global.ts +11 -6
- package/src/graph/stream.ts +20 -10
- package/src/index.ts +1 -0
- package/src/queue/stream_queue.ts +6 -5
- package/src/storage/index.ts +42 -4
- package/src/storage/memory/threads.ts +30 -1
- package/src/storage/redis/queue.ts +148 -0
- package/src/storage/sqlite/DB.ts +16 -0
- package/src/storage/sqlite/checkpoint.ts +502 -0
- package/src/storage/sqlite/threads.ts +405 -0
- package/src/storage/sqlite/type.ts +12 -0
- package/src/threads/index.ts +11 -25
- package/src/types.ts +2 -0
- package/src/utils/createEntrypointGraph.ts +20 -0
- package/src/utils/getGraph.ts +3 -3
- package/test/graph/entrypoint.ts +21 -0
- package/test/graph/index.ts +45 -6
- package/test/hono.ts +5 -0
- package/test/test.ts +0 -10
|
@@ -0,0 +1,127 @@
|
|
|
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 MetadataSchema = z
|
|
25
|
+
.object({
|
|
26
|
+
source: z.union([z.literal('input'), z.literal('loop'), z.literal('update'), z.string()]).optional(),
|
|
27
|
+
step: z.number().optional(),
|
|
28
|
+
writes: z.record(z.unknown()).nullable().optional(),
|
|
29
|
+
parents: z.record(z.string()).optional(),
|
|
30
|
+
})
|
|
31
|
+
.catchall(z.unknown());
|
|
32
|
+
export const SendSchema = z.object({
|
|
33
|
+
node: z.string(),
|
|
34
|
+
input: z.unknown().nullable(),
|
|
35
|
+
});
|
|
36
|
+
export const CommandSchema = z.object({
|
|
37
|
+
update: z
|
|
38
|
+
.union([z.record(z.unknown()), z.array(z.tuple([z.string(), z.unknown()]))])
|
|
39
|
+
.nullable()
|
|
40
|
+
.optional(),
|
|
41
|
+
resume: z.unknown().optional(),
|
|
42
|
+
goto: z.union([SendSchema, z.array(SendSchema), z.string(), z.array(z.string())]).optional(),
|
|
43
|
+
});
|
|
44
|
+
// 公共的查询参数验证 schema
|
|
45
|
+
export const PaginationQuerySchema = z.object({
|
|
46
|
+
limit: z.number().int().optional(),
|
|
47
|
+
offset: z.number().int().optional(),
|
|
48
|
+
});
|
|
49
|
+
export const ThreadIdParamSchema = z.object({
|
|
50
|
+
thread_id: z.string().uuid(),
|
|
51
|
+
});
|
|
52
|
+
export const RunIdParamSchema = z.object({
|
|
53
|
+
thread_id: z.string().uuid(),
|
|
54
|
+
run_id: z.string().uuid(),
|
|
55
|
+
});
|
|
56
|
+
// Assistants 相关的 schema
|
|
57
|
+
export const AssistantsSearchSchema = z.object({
|
|
58
|
+
graph_id: z.string().optional(),
|
|
59
|
+
metadata: MetadataSchema.optional(),
|
|
60
|
+
limit: z.number().int().optional(),
|
|
61
|
+
offset: z.number().int().optional(),
|
|
62
|
+
});
|
|
63
|
+
export const AssistantGraphQuerySchema = z.object({
|
|
64
|
+
xray: z.string().optional(),
|
|
65
|
+
});
|
|
66
|
+
// Runs 相关的 schema
|
|
67
|
+
export const RunStreamPayloadSchema = z
|
|
68
|
+
.object({
|
|
69
|
+
assistant_id: z.union([z.string().uuid(), z.string()]),
|
|
70
|
+
checkpoint_id: z.string().optional(),
|
|
71
|
+
input: z.any().optional(),
|
|
72
|
+
command: CommandSchema.optional(),
|
|
73
|
+
metadata: MetadataSchema.optional(),
|
|
74
|
+
config: AssistantConfig.optional(),
|
|
75
|
+
webhook: z.string().optional(),
|
|
76
|
+
interrupt_before: z.union([z.literal('*'), z.array(z.string())]).optional(),
|
|
77
|
+
interrupt_after: z.union([z.literal('*'), z.array(z.string())]).optional(),
|
|
78
|
+
on_disconnect: z.enum(['cancel', 'continue']).optional().default('continue'),
|
|
79
|
+
multitask_strategy: z.enum(['reject', 'rollback', 'interrupt', 'enqueue']).optional(),
|
|
80
|
+
stream_mode: z
|
|
81
|
+
.array(z.enum(['values', 'messages', 'messages-tuple', 'updates', 'events', 'debug', 'custom']))
|
|
82
|
+
.optional(),
|
|
83
|
+
stream_subgraphs: z.boolean().optional(),
|
|
84
|
+
stream_resumable: z.boolean().optional(),
|
|
85
|
+
after_seconds: z.number().optional(),
|
|
86
|
+
if_not_exists: z.enum(['create', 'reject']).optional(),
|
|
87
|
+
on_completion: z.enum(['complete', 'continue']).optional(),
|
|
88
|
+
feedback_keys: z.array(z.string()).optional(),
|
|
89
|
+
langsmith_tracer: z.unknown().optional(),
|
|
90
|
+
})
|
|
91
|
+
.describe('Payload for creating a stateful run.');
|
|
92
|
+
export const RunListQuerySchema = z.object({
|
|
93
|
+
limit: z.coerce.number().int().optional(),
|
|
94
|
+
offset: z.coerce.number().int().optional(),
|
|
95
|
+
status: z.enum(['pending', 'running', 'error', 'success', 'timeout', 'interrupted']).optional(),
|
|
96
|
+
});
|
|
97
|
+
export const RunCancelQuerySchema = z.object({
|
|
98
|
+
wait: z.coerce.boolean().optional().default(false),
|
|
99
|
+
action: z.enum(['interrupt', 'rollback']).optional().default('interrupt'),
|
|
100
|
+
});
|
|
101
|
+
// Threads 相关的 schema
|
|
102
|
+
export const ThreadCreatePayloadSchema = z
|
|
103
|
+
.object({
|
|
104
|
+
thread_id: z.string().uuid().describe('The ID of the thread. If not provided, an ID is generated.').optional(),
|
|
105
|
+
metadata: MetadataSchema.optional(),
|
|
106
|
+
if_exists: z.union([z.literal('raise'), z.literal('do_nothing')]).optional(),
|
|
107
|
+
})
|
|
108
|
+
.describe('Payload for creating a thread.');
|
|
109
|
+
export const ThreadSearchPayloadSchema = z
|
|
110
|
+
.object({
|
|
111
|
+
metadata: z.record(z.unknown()).describe('Metadata to search for.').optional(),
|
|
112
|
+
status: z.enum(['idle', 'busy', 'interrupted', 'error']).describe('Filter by thread status.').optional(),
|
|
113
|
+
values: z.record(z.unknown()).describe('Filter by thread values.').optional(),
|
|
114
|
+
limit: z.number().int().gte(1).lte(1000).describe('Maximum number to return.').optional(),
|
|
115
|
+
offset: z.number().int().gte(0).describe('Offset to start from.').optional(),
|
|
116
|
+
sort_by: z.enum(['thread_id', 'status', 'created_at', 'updated_at']).describe('Sort by field.').optional(),
|
|
117
|
+
sort_order: z.enum(['asc', 'desc']).describe('Sort order.').optional(),
|
|
118
|
+
})
|
|
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/createEndpoint.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { BaseThreadsManager } from './threads/index.js';
|
|
2
1
|
import { ILangGraphClient } from './types.js';
|
|
3
2
|
export { registerGraph } from './utils/getGraph.js';
|
|
4
3
|
export declare const AssistantEndpoint: ILangGraphClient['assistants'];
|
|
5
|
-
export declare const createEndpoint: (
|
|
4
|
+
export declare const createEndpoint: () => ILangGraphClient;
|
package/dist/createEndpoint.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { streamState } from './graph/stream.js';
|
|
2
2
|
import { getGraph, GRAPHS } from './utils/getGraph.js';
|
|
3
|
-
import {
|
|
3
|
+
import { LangGraphGlobal } from './global.js';
|
|
4
4
|
export { registerGraph } from './utils/getGraph.js';
|
|
5
5
|
export const AssistantEndpoint = {
|
|
6
6
|
async search(query) {
|
|
@@ -41,7 +41,8 @@ export const AssistantEndpoint = {
|
|
|
41
41
|
return drawable.toJSON();
|
|
42
42
|
},
|
|
43
43
|
};
|
|
44
|
-
export const createEndpoint = (
|
|
44
|
+
export const createEndpoint = () => {
|
|
45
|
+
const threads = LangGraphGlobal.globalThreadsManager;
|
|
45
46
|
return {
|
|
46
47
|
assistants: AssistantEndpoint,
|
|
47
48
|
threads,
|
|
@@ -50,7 +51,7 @@ export const createEndpoint = (threads) => {
|
|
|
50
51
|
return threads.listRuns(threadId, options);
|
|
51
52
|
},
|
|
52
53
|
async cancel(threadId, runId, wait, action) {
|
|
53
|
-
return globalMessageQueue.cancelQueue(runId);
|
|
54
|
+
return LangGraphGlobal.globalMessageQueue.cancelQueue(runId);
|
|
54
55
|
},
|
|
55
56
|
async *stream(threadId, assistantId, payload) {
|
|
56
57
|
if (!payload.config) {
|
package/dist/global.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
export declare
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import type { SqliteSaver } from './storage/sqlite/checkpoint.js';
|
|
2
|
+
export declare class LangGraphGlobal {
|
|
3
|
+
static globalMessageQueue: import("./queue/stream_queue.js").StreamQueueManager<import("./queue/stream_queue.js").BaseStreamQueueInterface>;
|
|
4
|
+
static globalCheckPointer: import("@langchain/langgraph-checkpoint-redis").RedisSaver | import("@langchain/langgraph-checkpoint-redis/shallow").ShallowRedisSaver | SqliteSaver | import("@langchain/langgraph-checkpoint").MemorySaver;
|
|
5
|
+
static globalThreadsManager: import("./storage/sqlite/threads.js").SQLiteThreadsManager<unknown> | import("./storage/memory/threads.js").MemoryThreadsManager<unknown>;
|
|
6
|
+
}
|
package/dist/global.js
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
|
-
import { createCheckPointer, createMessageQueue } from './storage/index.js';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import { createCheckPointer, createMessageQueue, createThreadManager } from './storage/index.js';
|
|
2
|
+
const [globalMessageQueue, globalCheckPointer] = await Promise.all([createMessageQueue(), createCheckPointer()]);
|
|
3
|
+
const globalThreadsManager = await createThreadManager({
|
|
4
|
+
checkpointer: globalCheckPointer,
|
|
5
|
+
});
|
|
6
|
+
export class LangGraphGlobal {
|
|
7
|
+
static globalMessageQueue = globalMessageQueue;
|
|
8
|
+
static globalCheckPointer = globalCheckPointer;
|
|
9
|
+
static globalThreadsManager = globalThreadsManager;
|
|
10
|
+
}
|
package/dist/graph/stream.d.ts
CHANGED
|
@@ -23,7 +23,7 @@ export declare function createStreamFromQueue(queueId: string): AsyncGenerator<{
|
|
|
23
23
|
event: string;
|
|
24
24
|
data: unknown;
|
|
25
25
|
}>;
|
|
26
|
-
export declare const serialiseAsDict: (obj: unknown) => string;
|
|
26
|
+
export declare const serialiseAsDict: (obj: unknown, indent?: number) => string;
|
|
27
27
|
/**
|
|
28
28
|
* 兼容性函数:保持原有 API,同时使用队列模式
|
|
29
29
|
* @param run 运行配置
|
package/dist/graph/stream.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { isBaseMessage } from '@langchain/core/messages';
|
|
2
2
|
import { getLangGraphCommand } from '../utils/getLangGraphCommand.js';
|
|
3
|
-
import {
|
|
3
|
+
import { LangGraphGlobal } from '../global.js';
|
|
4
4
|
import { EventMessage, StreamErrorEventMessage, StreamEndEventMessage } from '../queue/event_message.js';
|
|
5
5
|
export async function streamStateWithQueue(threads, run, queue, payload, options) {
|
|
6
6
|
const kwargs = payload;
|
|
@@ -22,7 +22,11 @@ export async function streamStateWithQueue(threads, run, queue, payload, options
|
|
|
22
22
|
if (userStreamMode.includes('messages')) {
|
|
23
23
|
libStreamMode.add('values');
|
|
24
24
|
}
|
|
25
|
-
await queue.push(new EventMessage('metadata', {
|
|
25
|
+
await queue.push(new EventMessage('metadata', {
|
|
26
|
+
run_id: run.run_id,
|
|
27
|
+
attempt: options.attempt,
|
|
28
|
+
graph_id: graphId,
|
|
29
|
+
}));
|
|
26
30
|
const metadata = {
|
|
27
31
|
...payload.config?.metadata,
|
|
28
32
|
run_attempt: options.attempt,
|
|
@@ -65,7 +69,9 @@ export async function streamStateWithQueue(threads, run, queue, payload, options
|
|
|
65
69
|
}
|
|
66
70
|
}
|
|
67
71
|
if (mode === 'values') {
|
|
68
|
-
await threads.set(run.thread_id, {
|
|
72
|
+
await threads.set(run.thread_id, {
|
|
73
|
+
values: data ? JSON.parse(serialiseAsDict(data)) : '',
|
|
74
|
+
});
|
|
69
75
|
}
|
|
70
76
|
}
|
|
71
77
|
else if (userStreamMode.includes('events')) {
|
|
@@ -105,7 +111,9 @@ export async function streamStateWithQueue(threads, run, queue, payload, options
|
|
|
105
111
|
continue;
|
|
106
112
|
if (messages[message.id] == null) {
|
|
107
113
|
messages[message.id] = message;
|
|
108
|
-
await queue.push(new EventMessage('messages/metadata', {
|
|
114
|
+
await queue.push(new EventMessage('messages/metadata', {
|
|
115
|
+
[message.id]: { metadata: event.metadata },
|
|
116
|
+
}));
|
|
109
117
|
}
|
|
110
118
|
else {
|
|
111
119
|
messages[message.id] = messages[message.id].concat(message);
|
|
@@ -127,10 +135,10 @@ export async function streamStateWithQueue(threads, run, queue, payload, options
|
|
|
127
135
|
* @returns 数据流生成器
|
|
128
136
|
*/
|
|
129
137
|
export async function* createStreamFromQueue(queueId) {
|
|
130
|
-
const queue = globalMessageQueue.getQueue(queueId);
|
|
138
|
+
const queue = LangGraphGlobal.globalMessageQueue.getQueue(queueId);
|
|
131
139
|
return queue.onDataReceive();
|
|
132
140
|
}
|
|
133
|
-
export const serialiseAsDict = (obj) => {
|
|
141
|
+
export const serialiseAsDict = (obj, indent = 2) => {
|
|
134
142
|
return JSON.stringify(obj, function (key, value) {
|
|
135
143
|
const rawValue = this[key];
|
|
136
144
|
if (rawValue != null &&
|
|
@@ -142,7 +150,7 @@ export const serialiseAsDict = (obj) => {
|
|
|
142
150
|
return { ...data, type };
|
|
143
151
|
}
|
|
144
152
|
return value;
|
|
145
|
-
},
|
|
153
|
+
}, indent);
|
|
146
154
|
};
|
|
147
155
|
/**
|
|
148
156
|
* 兼容性函数:保持原有 API,同时使用队列模式
|
|
@@ -159,12 +167,12 @@ export async function* streamState(threads, run, payload, options) {
|
|
|
159
167
|
// 启动队列推送任务(在后台异步执行)
|
|
160
168
|
await threads.set(threadId, { status: 'busy' });
|
|
161
169
|
await threads.updateRun(run.run_id, { status: 'running' });
|
|
162
|
-
const queue = globalMessageQueue.createQueue(queueId);
|
|
170
|
+
const queue = LangGraphGlobal.globalMessageQueue.createQueue(queueId);
|
|
163
171
|
const state = queue.onDataReceive();
|
|
164
172
|
streamStateWithQueue(threads, run, queue, payload, options).catch((error) => {
|
|
165
173
|
console.error('Queue task error:', error);
|
|
166
174
|
// 如果生产者出错,向队列推送错误信号
|
|
167
|
-
globalMessageQueue.pushToQueue(queueId, new StreamErrorEventMessage(error));
|
|
175
|
+
LangGraphGlobal.globalMessageQueue.pushToQueue(queueId, new StreamErrorEventMessage(error));
|
|
168
176
|
// TODO 不知道这里需不需要错误处理
|
|
169
177
|
});
|
|
170
178
|
for await (const data of state) {
|
|
@@ -182,6 +190,6 @@ export async function* streamState(threads, run, payload, options) {
|
|
|
182
190
|
finally {
|
|
183
191
|
// 在完成后清理队列
|
|
184
192
|
await threads.set(threadId, { status: 'idle' });
|
|
185
|
-
globalMessageQueue.removeQueue(queueId);
|
|
193
|
+
LangGraphGlobal.globalMessageQueue.removeQueue(queueId);
|
|
186
194
|
}
|
|
187
195
|
}
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -16,6 +16,7 @@ interface StreamQueueEvents<T extends EventMessage> {
|
|
|
16
16
|
* Base stream queue class
|
|
17
17
|
*/
|
|
18
18
|
export declare class BaseStreamQueue extends EventEmitter<StreamQueueEvents<EventMessage>> {
|
|
19
|
+
readonly id: string;
|
|
19
20
|
readonly compressMessages: boolean;
|
|
20
21
|
/** 序列化器实例 / Serializer instance */
|
|
21
22
|
serializer: JsonPlusSerializer;
|
|
@@ -24,7 +25,7 @@ export declare class BaseStreamQueue extends EventEmitter<StreamQueueEvents<Even
|
|
|
24
25
|
* Constructor
|
|
25
26
|
* @param compressMessages 是否压缩消息 / Whether to compress messages
|
|
26
27
|
*/
|
|
27
|
-
constructor(compressMessages?: boolean);
|
|
28
|
+
constructor(id: string, compressMessages?: boolean);
|
|
28
29
|
/**
|
|
29
30
|
* 编码数据为 Uint8Array
|
|
30
31
|
* Encode data to Uint8Array
|
|
@@ -45,6 +46,7 @@ export declare class BaseStreamQueue extends EventEmitter<StreamQueueEvents<Even
|
|
|
45
46
|
* Base stream queue interface
|
|
46
47
|
*/
|
|
47
48
|
export interface BaseStreamQueueInterface {
|
|
49
|
+
id: string;
|
|
48
50
|
/** 是否压缩消息 / Whether to compress messages */
|
|
49
51
|
compressMessages: boolean;
|
|
50
52
|
/**
|
|
@@ -63,7 +65,7 @@ export interface BaseStreamQueueInterface {
|
|
|
63
65
|
* @param listener 数据变化监听器 / Data change listener
|
|
64
66
|
* @returns 取消监听函数 / Unsubscribe function
|
|
65
67
|
*/
|
|
66
|
-
|
|
68
|
+
onDataReceive(): AsyncGenerator<EventMessage, void, unknown>;
|
|
67
69
|
/** 取消信号控制器 / Cancel signal controller */
|
|
68
70
|
cancelSignal: AbortController;
|
|
69
71
|
/** 取消操作 / Cancel operation */
|
|
@@ -86,7 +88,7 @@ export declare class StreamQueueManager<Q extends BaseStreamQueueInterface> {
|
|
|
86
88
|
* @param queueConstructor 队列构造函数 / Queue constructor
|
|
87
89
|
* @param options 配置选项 / Configuration options
|
|
88
90
|
*/
|
|
89
|
-
constructor(queueConstructor: new (
|
|
91
|
+
constructor(queueConstructor: new (id: string) => Q, options?: {
|
|
90
92
|
/** 默认是否压缩消息 / Default compress messages setting */
|
|
91
93
|
defaultCompressMessages?: boolean;
|
|
92
94
|
});
|
|
@@ -5,6 +5,7 @@ import { JsonPlusSerializer } from './JsonPlusSerializer.js';
|
|
|
5
5
|
* Base stream queue class
|
|
6
6
|
*/
|
|
7
7
|
export class BaseStreamQueue extends EventEmitter {
|
|
8
|
+
id;
|
|
8
9
|
compressMessages;
|
|
9
10
|
/** 序列化器实例 / Serializer instance */
|
|
10
11
|
serializer = new JsonPlusSerializer();
|
|
@@ -13,8 +14,9 @@ export class BaseStreamQueue extends EventEmitter {
|
|
|
13
14
|
* Constructor
|
|
14
15
|
* @param compressMessages 是否压缩消息 / Whether to compress messages
|
|
15
16
|
*/
|
|
16
|
-
constructor(compressMessages = true) {
|
|
17
|
+
constructor(id, compressMessages = true) {
|
|
17
18
|
super();
|
|
19
|
+
this.id = id;
|
|
18
20
|
this.compressMessages = compressMessages;
|
|
19
21
|
}
|
|
20
22
|
/**
|
|
@@ -68,7 +70,7 @@ export class StreamQueueManager {
|
|
|
68
70
|
*/
|
|
69
71
|
createQueue(id, compressMessages) {
|
|
70
72
|
const compress = compressMessages ?? this.defaultCompressMessages;
|
|
71
|
-
this.queues.set(id, new this.queueConstructor(
|
|
73
|
+
this.queues.set(id, new this.queueConstructor(id));
|
|
72
74
|
return this.queues.get(id);
|
|
73
75
|
}
|
|
74
76
|
/**
|
package/dist/storage/index.d.ts
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
|
-
import { StreamQueueManager } from '../queue/stream_queue';
|
|
1
|
+
import { BaseStreamQueueInterface, StreamQueueManager } from '../queue/stream_queue';
|
|
2
2
|
import { MemorySaver } from './memory/checkpoint';
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
import { MemoryThreadsManager } from './memory/threads';
|
|
4
|
+
import type { SqliteSaver as SqliteSaverType } from './sqlite/checkpoint';
|
|
5
|
+
import { SQLiteThreadsManager } from './sqlite/threads';
|
|
6
|
+
export declare const createCheckPointer: () => Promise<import("@langchain/langgraph-checkpoint-redis").RedisSaver | import("@langchain/langgraph-checkpoint-redis/shallow").ShallowRedisSaver | SqliteSaverType | MemorySaver>;
|
|
7
|
+
export declare const createMessageQueue: () => Promise<StreamQueueManager<BaseStreamQueueInterface>>;
|
|
8
|
+
export declare const createThreadManager: (config: {
|
|
9
|
+
checkpointer?: SqliteSaverType;
|
|
10
|
+
}) => SQLiteThreadsManager<unknown> | MemoryThreadsManager<unknown>;
|
package/dist/storage/index.js
CHANGED
|
@@ -1,11 +1,46 @@
|
|
|
1
1
|
import { StreamQueueManager } from '../queue/stream_queue';
|
|
2
2
|
import { MemorySaver } from './memory/checkpoint';
|
|
3
3
|
import { MemoryStreamQueue } from './memory/queue';
|
|
4
|
+
import { MemoryThreadsManager } from './memory/threads';
|
|
5
|
+
import { SQLiteThreadsManager } from './sqlite/threads';
|
|
4
6
|
// 所有的适配实现,都请写到这里,通过环境变量进行判断使用哪种方式进行适配
|
|
5
|
-
export const createCheckPointer = () => {
|
|
7
|
+
export const createCheckPointer = async () => {
|
|
8
|
+
if ((process.env.REDIS_URL && process.env.CHECKPOINT_TYPE === 'redis') ||
|
|
9
|
+
process.env.CHECKPOINT_TYPE === 'shallow/redis') {
|
|
10
|
+
if (process.env.CHECKPOINT_TYPE === 'redis') {
|
|
11
|
+
const { RedisSaver } = await import('@langchain/langgraph-checkpoint-redis');
|
|
12
|
+
return await RedisSaver.fromUrl(process.env.REDIS_URL, {
|
|
13
|
+
defaultTTL: 60, // TTL in minutes
|
|
14
|
+
refreshOnRead: true,
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
if (process.env.CHECKPOINT_TYPE === 'shallow/redis') {
|
|
18
|
+
const { ShallowRedisSaver } = await import('@langchain/langgraph-checkpoint-redis/shallow');
|
|
19
|
+
return await ShallowRedisSaver.fromUrl(process.env.REDIS_URL);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
if (process.env.SQLITE_DATABASE_URI) {
|
|
23
|
+
const { SqliteSaver } = await import('./sqlite/checkpoint');
|
|
24
|
+
const db = SqliteSaver.fromConnString(process.env.SQLITE_DATABASE_URI);
|
|
25
|
+
return db;
|
|
26
|
+
}
|
|
6
27
|
return new MemorySaver();
|
|
7
28
|
};
|
|
8
|
-
export const createMessageQueue = () => {
|
|
9
|
-
|
|
29
|
+
export const createMessageQueue = async () => {
|
|
30
|
+
let q;
|
|
31
|
+
if (process.env.REDIS_URL) {
|
|
32
|
+
console.log('Using redis as stream queue');
|
|
33
|
+
const { RedisStreamQueue } = await import('./redis/queue');
|
|
34
|
+
q = RedisStreamQueue;
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
q = MemoryStreamQueue;
|
|
38
|
+
}
|
|
10
39
|
return new StreamQueueManager(q);
|
|
11
40
|
};
|
|
41
|
+
export const createThreadManager = (config) => {
|
|
42
|
+
if (process.env.SQLITE_DATABASE_URI && config.checkpointer) {
|
|
43
|
+
return new SQLiteThreadsManager(config.checkpointer);
|
|
44
|
+
}
|
|
45
|
+
return new MemoryThreadsManager();
|
|
46
|
+
};
|
|
@@ -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>
|
|
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 {
|
|
2
|
-
|
|
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,39 @@
|
|
|
1
|
+
import { EventMessage } from '../../queue/event_message.js';
|
|
2
|
+
import { BaseStreamQueue } from '../../queue/stream_queue.js';
|
|
3
|
+
import { BaseStreamQueueInterface } from '../../queue/stream_queue.js';
|
|
4
|
+
import { RedisClientType } from 'redis';
|
|
5
|
+
/**
|
|
6
|
+
* Redis 实现的消息队列,用于存储消息
|
|
7
|
+
*/
|
|
8
|
+
export declare class RedisStreamQueue extends BaseStreamQueue implements BaseStreamQueueInterface {
|
|
9
|
+
readonly id: string;
|
|
10
|
+
static redis: RedisClientType;
|
|
11
|
+
static subscriberRedis: RedisClientType;
|
|
12
|
+
private redis;
|
|
13
|
+
private subscriberRedis;
|
|
14
|
+
private queueKey;
|
|
15
|
+
private channelKey;
|
|
16
|
+
private isConnected;
|
|
17
|
+
cancelSignal: AbortController;
|
|
18
|
+
constructor(id?: string);
|
|
19
|
+
/**
|
|
20
|
+
* 推送消息到 Redis 队列
|
|
21
|
+
*/
|
|
22
|
+
push(item: EventMessage): Promise<void>;
|
|
23
|
+
/**
|
|
24
|
+
* 异步生成器:支持 for await...of 方式消费队列数据
|
|
25
|
+
*/
|
|
26
|
+
onDataReceive(): AsyncGenerator<EventMessage, void, unknown>;
|
|
27
|
+
/**
|
|
28
|
+
* 获取队列中的所有数据
|
|
29
|
+
*/
|
|
30
|
+
getAll(): Promise<EventMessage[]>;
|
|
31
|
+
/**
|
|
32
|
+
* 清空队列
|
|
33
|
+
*/
|
|
34
|
+
clear(): void;
|
|
35
|
+
/**
|
|
36
|
+
* 取消操作
|
|
37
|
+
*/
|
|
38
|
+
cancel(): void;
|
|
39
|
+
}
|