@langgraph-js/pure-graph 1.3.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.
- package/README.md +22 -14
- package/dist/adapter/nextjs/router.js +6 -6
- package/dist/global.d.ts +3 -2
- package/dist/storage/index.d.ts +4 -3
- package/dist/storage/index.js +13 -1
- package/dist/storage/pg/checkpoint.d.ts +2 -0
- package/dist/storage/pg/checkpoint.js +9 -0
- package/dist/storage/pg/threads.d.ts +43 -0
- package/dist/storage/pg/threads.js +304 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +38 -5
- package/.prettierrc +0 -11
- package/bun.lock +0 -209
- package/dist/adapter/hono/zod.d.ts +0 -203
- package/dist/adapter/hono/zod.js +0 -43
- package/dist/adapter/nextjs/zod.d.ts +0 -203
- package/dist/adapter/nextjs/zod.js +0 -60
- package/examples/nextjs/README.md +0 -36
- package/examples/nextjs/app/api/langgraph/[...path]/route.ts +0 -10
- package/examples/nextjs/app/favicon.ico +0 -0
- package/examples/nextjs/app/globals.css +0 -26
- package/examples/nextjs/app/layout.tsx +0 -34
- package/examples/nextjs/app/page.tsx +0 -211
- package/examples/nextjs/next.config.ts +0 -26
- package/examples/nextjs/package.json +0 -24
- package/examples/nextjs/postcss.config.mjs +0 -5
- package/examples/nextjs/tsconfig.json +0 -27
- package/packages/agent-graph/demo.json +0 -35
- package/packages/agent-graph/package.json +0 -18
- package/packages/agent-graph/src/index.ts +0 -47
- package/packages/agent-graph/src/tools/tavily.ts +0 -9
- package/packages/agent-graph/src/tools.ts +0 -38
- package/packages/agent-graph/src/types.ts +0 -42
- package/pnpm-workspace.yaml +0 -4
- package/src/adapter/hono/assistants.ts +0 -24
- package/src/adapter/hono/endpoint.ts +0 -3
- package/src/adapter/hono/index.ts +0 -14
- package/src/adapter/hono/runs.ts +0 -92
- package/src/adapter/hono/threads.ts +0 -37
- package/src/adapter/nextjs/endpoint.ts +0 -2
- package/src/adapter/nextjs/index.ts +0 -2
- package/src/adapter/nextjs/router.ts +0 -206
- package/src/adapter/nextjs/zod.ts +0 -66
- package/src/adapter/zod.ts +0 -144
- package/src/createEndpoint.ts +0 -116
- package/src/e.d.ts +0 -3
- package/src/global.ts +0 -11
- package/src/graph/stream.ts +0 -263
- package/src/graph/stringify.ts +0 -219
- package/src/index.ts +0 -6
- package/src/queue/JsonPlusSerializer.ts +0 -143
- package/src/queue/event_message.ts +0 -30
- package/src/queue/stream_queue.ts +0 -237
- package/src/storage/index.ts +0 -52
- package/src/storage/memory/checkpoint.ts +0 -2
- package/src/storage/memory/queue.ts +0 -91
- package/src/storage/memory/threads.ts +0 -183
- package/src/storage/redis/queue.ts +0 -148
- package/src/storage/sqlite/DB.ts +0 -16
- package/src/storage/sqlite/checkpoint.ts +0 -502
- package/src/storage/sqlite/threads.ts +0 -405
- package/src/storage/sqlite/type.ts +0 -12
- package/src/threads/index.ts +0 -37
- package/src/types.ts +0 -118
- package/src/utils/createEntrypointGraph.ts +0 -20
- package/src/utils/getGraph.ts +0 -44
- package/src/utils/getLangGraphCommand.ts +0 -21
- package/test/graph/entrypoint.ts +0 -21
- package/test/graph/index.ts +0 -60
- package/test/hono.ts +0 -15
- package/tsconfig.json +0 -20
package/src/graph/stringify.ts
DELETED
|
@@ -1,219 +0,0 @@
|
|
|
1
|
-
/* eslint-disable */
|
|
2
|
-
// @ts-nocheck
|
|
3
|
-
|
|
4
|
-
// Stringify that can handle circular references.
|
|
5
|
-
// Inlined due to ESM import issues
|
|
6
|
-
// Source: https://www.npmjs.com/package/fast-safe-stringify
|
|
7
|
-
|
|
8
|
-
var LIMIT_REPLACE_NODE = '[...]';
|
|
9
|
-
var CIRCULAR_REPLACE_NODE = '[Circular]';
|
|
10
|
-
|
|
11
|
-
var arr = [];
|
|
12
|
-
var replacerStack = [];
|
|
13
|
-
|
|
14
|
-
function defaultOptions() {
|
|
15
|
-
return {
|
|
16
|
-
depthLimit: Number.MAX_SAFE_INTEGER,
|
|
17
|
-
edgesLimit: Number.MAX_SAFE_INTEGER,
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
// Regular stringify
|
|
22
|
-
export function stringify(obj, replacer?, spacer?, options?) {
|
|
23
|
-
if (typeof options === 'undefined') {
|
|
24
|
-
options = defaultOptions();
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
decirc(obj, '', 0, [], undefined, 0, options);
|
|
28
|
-
var res;
|
|
29
|
-
try {
|
|
30
|
-
if (replacerStack.length === 0) {
|
|
31
|
-
res = JSON.stringify(obj, replacer, spacer);
|
|
32
|
-
} else {
|
|
33
|
-
res = JSON.stringify(obj, replaceGetterValues(replacer), spacer);
|
|
34
|
-
}
|
|
35
|
-
} catch (_) {
|
|
36
|
-
return JSON.stringify('[unable to serialize, circular reference is too complex to analyze]');
|
|
37
|
-
} finally {
|
|
38
|
-
while (arr.length !== 0) {
|
|
39
|
-
var part = arr.pop();
|
|
40
|
-
if (part.length === 4) {
|
|
41
|
-
Object.defineProperty(part[0], part[1], part[3]);
|
|
42
|
-
} else {
|
|
43
|
-
part[0][part[1]] = part[2];
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
return res;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function setReplace(replace, val, k, parent) {
|
|
51
|
-
var propertyDescriptor = Object.getOwnPropertyDescriptor(parent, k);
|
|
52
|
-
if (propertyDescriptor.get !== undefined) {
|
|
53
|
-
if (propertyDescriptor.configurable) {
|
|
54
|
-
Object.defineProperty(parent, k, { value: replace });
|
|
55
|
-
arr.push([parent, k, val, propertyDescriptor]);
|
|
56
|
-
} else {
|
|
57
|
-
replacerStack.push([val, k, replace]);
|
|
58
|
-
}
|
|
59
|
-
} else {
|
|
60
|
-
parent[k] = replace;
|
|
61
|
-
arr.push([parent, k, val]);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function decirc(val, k, edgeIndex, stack, parent, depth, options) {
|
|
66
|
-
depth += 1;
|
|
67
|
-
var i;
|
|
68
|
-
if (typeof val === 'object' && val !== null) {
|
|
69
|
-
for (i = 0; i < stack.length; i++) {
|
|
70
|
-
if (stack[i] === val) {
|
|
71
|
-
setReplace(CIRCULAR_REPLACE_NODE, val, k, parent);
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
if (typeof options.depthLimit !== 'undefined' && depth > options.depthLimit) {
|
|
77
|
-
setReplace(LIMIT_REPLACE_NODE, val, k, parent);
|
|
78
|
-
return;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
if (typeof options.edgesLimit !== 'undefined' && edgeIndex + 1 > options.edgesLimit) {
|
|
82
|
-
setReplace(LIMIT_REPLACE_NODE, val, k, parent);
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
stack.push(val);
|
|
87
|
-
// Optimize for Arrays. Big arrays could kill the performance otherwise!
|
|
88
|
-
if (Array.isArray(val)) {
|
|
89
|
-
for (i = 0; i < val.length; i++) {
|
|
90
|
-
decirc(val[i], i, i, stack, val, depth, options);
|
|
91
|
-
}
|
|
92
|
-
} else {
|
|
93
|
-
var keys = Object.keys(val);
|
|
94
|
-
for (i = 0; i < keys.length; i++) {
|
|
95
|
-
var key = keys[i];
|
|
96
|
-
decirc(val[key], key, i, stack, val, depth, options);
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
stack.pop();
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// Stable-stringify
|
|
104
|
-
function compareFunction(a, b) {
|
|
105
|
-
if (a < b) {
|
|
106
|
-
return -1;
|
|
107
|
-
}
|
|
108
|
-
if (a > b) {
|
|
109
|
-
return 1;
|
|
110
|
-
}
|
|
111
|
-
return 0;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
function deterministicStringify(obj, replacer, spacer, options) {
|
|
115
|
-
if (typeof options === 'undefined') {
|
|
116
|
-
options = defaultOptions();
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
var tmp = deterministicDecirc(obj, '', 0, [], undefined, 0, options) || obj;
|
|
120
|
-
var res;
|
|
121
|
-
try {
|
|
122
|
-
if (replacerStack.length === 0) {
|
|
123
|
-
res = JSON.stringify(tmp, replacer, spacer);
|
|
124
|
-
} else {
|
|
125
|
-
res = JSON.stringify(tmp, replaceGetterValues(replacer), spacer);
|
|
126
|
-
}
|
|
127
|
-
} catch (_) {
|
|
128
|
-
return JSON.stringify('[unable to serialize, circular reference is too complex to analyze]');
|
|
129
|
-
} finally {
|
|
130
|
-
// Ensure that we restore the object as it was.
|
|
131
|
-
while (arr.length !== 0) {
|
|
132
|
-
var part = arr.pop();
|
|
133
|
-
if (part.length === 4) {
|
|
134
|
-
Object.defineProperty(part[0], part[1], part[3]);
|
|
135
|
-
} else {
|
|
136
|
-
part[0][part[1]] = part[2];
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
return res;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
function deterministicDecirc(val, k, edgeIndex, stack, parent, depth, options) {
|
|
144
|
-
depth += 1;
|
|
145
|
-
var i;
|
|
146
|
-
if (typeof val === 'object' && val !== null) {
|
|
147
|
-
for (i = 0; i < stack.length; i++) {
|
|
148
|
-
if (stack[i] === val) {
|
|
149
|
-
setReplace(CIRCULAR_REPLACE_NODE, val, k, parent);
|
|
150
|
-
return;
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
try {
|
|
154
|
-
if (typeof val.toJSON === 'function') {
|
|
155
|
-
return;
|
|
156
|
-
}
|
|
157
|
-
} catch (_) {
|
|
158
|
-
return;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
if (typeof options.depthLimit !== 'undefined' && depth > options.depthLimit) {
|
|
162
|
-
setReplace(LIMIT_REPLACE_NODE, val, k, parent);
|
|
163
|
-
return;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
if (typeof options.edgesLimit !== 'undefined' && edgeIndex + 1 > options.edgesLimit) {
|
|
167
|
-
setReplace(LIMIT_REPLACE_NODE, val, k, parent);
|
|
168
|
-
return;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
stack.push(val);
|
|
172
|
-
// Optimize for Arrays. Big arrays could kill the performance otherwise!
|
|
173
|
-
if (Array.isArray(val)) {
|
|
174
|
-
for (i = 0; i < val.length; i++) {
|
|
175
|
-
deterministicDecirc(val[i], i, i, stack, val, depth, options);
|
|
176
|
-
}
|
|
177
|
-
} else {
|
|
178
|
-
// Create a temporary object in the required way
|
|
179
|
-
var tmp = {};
|
|
180
|
-
var keys = Object.keys(val).sort(compareFunction);
|
|
181
|
-
for (i = 0; i < keys.length; i++) {
|
|
182
|
-
var key = keys[i];
|
|
183
|
-
deterministicDecirc(val[key], key, i, stack, val, depth, options);
|
|
184
|
-
tmp[key] = val[key];
|
|
185
|
-
}
|
|
186
|
-
if (typeof parent !== 'undefined') {
|
|
187
|
-
arr.push([parent, k, val]);
|
|
188
|
-
parent[k] = tmp;
|
|
189
|
-
} else {
|
|
190
|
-
return tmp;
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
stack.pop();
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
// wraps replacer function to handle values we couldn't replace
|
|
198
|
-
// and mark them as replaced value
|
|
199
|
-
function replaceGetterValues(replacer) {
|
|
200
|
-
replacer =
|
|
201
|
-
typeof replacer !== 'undefined'
|
|
202
|
-
? replacer
|
|
203
|
-
: function (k, v) {
|
|
204
|
-
return v;
|
|
205
|
-
};
|
|
206
|
-
return function (key, val) {
|
|
207
|
-
if (replacerStack.length > 0) {
|
|
208
|
-
for (var i = 0; i < replacerStack.length; i++) {
|
|
209
|
-
var part = replacerStack[i];
|
|
210
|
-
if (part[1] === key && part[0] === val) {
|
|
211
|
-
val = part[2];
|
|
212
|
-
replacerStack.splice(i, 1);
|
|
213
|
-
break;
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
return replacer.call(this, key, val);
|
|
218
|
-
};
|
|
219
|
-
}
|
package/src/index.ts
DELETED
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
-
/* eslint-disable no-instanceof/no-instanceof */
|
|
3
|
-
import { load } from '@langchain/core/load';
|
|
4
|
-
import type { SerializerProtocol } from '@langchain/langgraph-checkpoint';
|
|
5
|
-
import { stringify } from '../graph/stringify.js';
|
|
6
|
-
|
|
7
|
-
function isLangChainSerializedObject(value: Record<string, unknown>) {
|
|
8
|
-
return value !== null && value.lc === 1 && value.type === 'constructor' && Array.isArray(value.id);
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* The replacer in stringify does not allow delegation to built-in LangChain
|
|
13
|
-
* serialization methods, and instead immediately calls `.toJSON()` and
|
|
14
|
-
* continues to stringify subfields.
|
|
15
|
-
*
|
|
16
|
-
* We therefore must start from the most nested elements in the input and
|
|
17
|
-
* deserialize upwards rather than top-down.
|
|
18
|
-
*/
|
|
19
|
-
async function _reviver(value: any): Promise<any> {
|
|
20
|
-
if (value && typeof value === 'object') {
|
|
21
|
-
if (Array.isArray(value)) {
|
|
22
|
-
const revivedArray = await Promise.all(value.map((item) => _reviver(item)));
|
|
23
|
-
return revivedArray;
|
|
24
|
-
} else {
|
|
25
|
-
const revivedObj: any = {};
|
|
26
|
-
for (const [k, v] of Object.entries(value)) {
|
|
27
|
-
revivedObj[k] = await _reviver(v);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
if (revivedObj.lc === 2 && revivedObj.type === 'undefined') {
|
|
31
|
-
return undefined;
|
|
32
|
-
} else if (revivedObj.lc === 2 && revivedObj.type === 'constructor' && Array.isArray(revivedObj.id)) {
|
|
33
|
-
try {
|
|
34
|
-
const constructorName = revivedObj.id[revivedObj.id.length - 1];
|
|
35
|
-
let constructor: any;
|
|
36
|
-
|
|
37
|
-
switch (constructorName) {
|
|
38
|
-
case 'Set':
|
|
39
|
-
constructor = Set;
|
|
40
|
-
break;
|
|
41
|
-
case 'Map':
|
|
42
|
-
constructor = Map;
|
|
43
|
-
break;
|
|
44
|
-
case 'RegExp':
|
|
45
|
-
constructor = RegExp;
|
|
46
|
-
break;
|
|
47
|
-
case 'Error':
|
|
48
|
-
constructor = Error;
|
|
49
|
-
break;
|
|
50
|
-
default:
|
|
51
|
-
return revivedObj;
|
|
52
|
-
}
|
|
53
|
-
if (revivedObj.method) {
|
|
54
|
-
return (constructor as any)[revivedObj.method](...(revivedObj.args || []));
|
|
55
|
-
} else {
|
|
56
|
-
return new (constructor as any)(...(revivedObj.args || []));
|
|
57
|
-
}
|
|
58
|
-
} catch (error) {
|
|
59
|
-
return revivedObj;
|
|
60
|
-
}
|
|
61
|
-
} else if (isLangChainSerializedObject(revivedObj)) {
|
|
62
|
-
return load(JSON.stringify(revivedObj));
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
return revivedObj;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
return value;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function _encodeConstructorArgs(
|
|
72
|
-
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
73
|
-
constructor: Function,
|
|
74
|
-
method?: string,
|
|
75
|
-
args?: any[],
|
|
76
|
-
kwargs?: Record<string, any>,
|
|
77
|
-
): object {
|
|
78
|
-
return {
|
|
79
|
-
lc: 2,
|
|
80
|
-
type: 'constructor',
|
|
81
|
-
id: [constructor.name],
|
|
82
|
-
method: method ?? null,
|
|
83
|
-
args: args ?? [],
|
|
84
|
-
kwargs: kwargs ?? {},
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
function _default(obj: any): any {
|
|
89
|
-
if (obj === undefined) {
|
|
90
|
-
return {
|
|
91
|
-
lc: 2,
|
|
92
|
-
type: 'undefined',
|
|
93
|
-
};
|
|
94
|
-
} else if (obj instanceof Set || obj instanceof Map) {
|
|
95
|
-
return _encodeConstructorArgs(obj.constructor, undefined, [Array.from(obj)]);
|
|
96
|
-
} else if (obj instanceof RegExp) {
|
|
97
|
-
return _encodeConstructorArgs(RegExp, undefined, [obj.source, obj.flags]);
|
|
98
|
-
} else if (obj instanceof Error) {
|
|
99
|
-
return _encodeConstructorArgs(obj.constructor, undefined, [obj.message]);
|
|
100
|
-
// TODO: Remove special case
|
|
101
|
-
} else if (obj?.lg_name === 'Send') {
|
|
102
|
-
return {
|
|
103
|
-
node: obj.node,
|
|
104
|
-
args: obj.args,
|
|
105
|
-
};
|
|
106
|
-
} else {
|
|
107
|
-
return obj;
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
export class JsonPlusSerializer implements SerializerProtocol {
|
|
112
|
-
protected _dumps(obj: any): Uint8Array {
|
|
113
|
-
const encoder = new TextEncoder();
|
|
114
|
-
return encoder.encode(
|
|
115
|
-
stringify(obj, (_: string, value: any) => {
|
|
116
|
-
return _default(value);
|
|
117
|
-
}),
|
|
118
|
-
);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
async dumpsTyped(obj: any): Promise<[string, Uint8Array]> {
|
|
122
|
-
if (obj instanceof Uint8Array) {
|
|
123
|
-
return ['bytes', obj];
|
|
124
|
-
} else {
|
|
125
|
-
return ['json', this._dumps(obj)];
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
protected async _loads(data: string): Promise<any> {
|
|
130
|
-
const parsed = JSON.parse(data);
|
|
131
|
-
return _reviver(parsed);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
async loadsTyped(type: string, data: Uint8Array | string): Promise<any> {
|
|
135
|
-
if (type === 'bytes') {
|
|
136
|
-
return typeof data === 'string' ? new TextEncoder().encode(data) : data;
|
|
137
|
-
} else if (type === 'json') {
|
|
138
|
-
return this._loads(typeof data === 'string' ? data : new TextDecoder().decode(data));
|
|
139
|
-
} else {
|
|
140
|
-
throw new Error(`Unknown serialization type: ${type}`);
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
export class EventMessage {
|
|
2
|
-
event: string;
|
|
3
|
-
data: unknown;
|
|
4
|
-
id?: string;
|
|
5
|
-
constructor(event: string, data?: unknown) {
|
|
6
|
-
this.event = event;
|
|
7
|
-
this.data = data;
|
|
8
|
-
}
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export class CancelEventMessage extends EventMessage {
|
|
12
|
-
constructor() {
|
|
13
|
-
super('__system_cancel__', 'user cancel this run');
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export class StreamEndEventMessage extends EventMessage {
|
|
18
|
-
constructor() {
|
|
19
|
-
super('__stream_end__', 'stream end');
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export class StreamErrorEventMessage extends EventMessage {
|
|
24
|
-
public constructor(error: Error) {
|
|
25
|
-
super('__stream_error__', {
|
|
26
|
-
error: error.name,
|
|
27
|
-
message: error.message,
|
|
28
|
-
});
|
|
29
|
-
}
|
|
30
|
-
}
|
|
@@ -1,237 +0,0 @@
|
|
|
1
|
-
import { EventEmitter } from 'eventemitter3';
|
|
2
|
-
import { JsonPlusSerializer } from './JsonPlusSerializer.js';
|
|
3
|
-
import { EventMessage } from './event_message.js';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* 流队列事件接口
|
|
7
|
-
* Stream queue events interface
|
|
8
|
-
*/
|
|
9
|
-
interface StreamQueueEvents<T extends EventMessage> {
|
|
10
|
-
/** 数据事件:当有新数据时触发 / Data event: triggered when new data arrives */
|
|
11
|
-
data: (data: T) => void;
|
|
12
|
-
/** 其他事件 / Other events */
|
|
13
|
-
[key: string]: ((...args: any[]) => void) | undefined;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* 基础流队列类
|
|
18
|
-
* Base stream queue class
|
|
19
|
-
*/
|
|
20
|
-
export class BaseStreamQueue extends EventEmitter<StreamQueueEvents<EventMessage>> {
|
|
21
|
-
/** 序列化器实例 / Serializer instance */
|
|
22
|
-
serializer = new JsonPlusSerializer();
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* 构造函数
|
|
26
|
-
* Constructor
|
|
27
|
-
* @param compressMessages 是否压缩消息 / Whether to compress messages
|
|
28
|
-
*/
|
|
29
|
-
constructor(readonly id: string, readonly compressMessages: boolean = true) {
|
|
30
|
-
super();
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* 编码数据为 Uint8Array
|
|
35
|
-
* Encode data to Uint8Array
|
|
36
|
-
* @param message 要编码的消息 / Message to encode
|
|
37
|
-
* @returns 编码后的 Uint8Array / Encoded Uint8Array
|
|
38
|
-
*/
|
|
39
|
-
async encodeData(message: EventMessage): Promise<Uint8Array> {
|
|
40
|
-
const [_, serializedMessage] = await this.serializer.dumpsTyped(message);
|
|
41
|
-
return serializedMessage;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* 解码数据为 EventMessage
|
|
46
|
-
* Decode data to EventMessage
|
|
47
|
-
* @param serializedMessage 要解码的消息 / Message to decode
|
|
48
|
-
* @returns 解码后的 EventMessage / Decoded EventMessage
|
|
49
|
-
*/
|
|
50
|
-
async decodeData(serializedMessage: string | Uint8Array): Promise<EventMessage> {
|
|
51
|
-
const message = (await this.serializer.loadsTyped('json', serializedMessage)) as EventMessage;
|
|
52
|
-
return message;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* 基础流队列接口
|
|
58
|
-
* Base stream queue interface
|
|
59
|
-
*/
|
|
60
|
-
export interface BaseStreamQueueInterface {
|
|
61
|
-
id: string;
|
|
62
|
-
/** 是否压缩消息 / Whether to compress messages */
|
|
63
|
-
compressMessages: boolean;
|
|
64
|
-
/**
|
|
65
|
-
* 推送数据项到队列
|
|
66
|
-
* Push item to queue
|
|
67
|
-
* @param item 要推送的数据项 / Item to push
|
|
68
|
-
*/
|
|
69
|
-
push(item: EventMessage): Promise<void>;
|
|
70
|
-
/** 获取所有数据 / Get all data */
|
|
71
|
-
getAll(): Promise<EventMessage[]>;
|
|
72
|
-
/** 清空队列 / Clear queue */
|
|
73
|
-
clear(): void;
|
|
74
|
-
/**
|
|
75
|
-
* 监听数据变化
|
|
76
|
-
* Listen for data changes
|
|
77
|
-
* @param listener 数据变化监听器 / Data change listener
|
|
78
|
-
* @returns 取消监听函数 / Unsubscribe function
|
|
79
|
-
*/
|
|
80
|
-
onDataReceive(): AsyncGenerator<EventMessage, void, unknown>;
|
|
81
|
-
/** 取消信号控制器 / Cancel signal controller */
|
|
82
|
-
cancelSignal: AbortController;
|
|
83
|
-
/** 取消操作 / Cancel operation */
|
|
84
|
-
cancel(): void;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* StreamQueue 管理器,通过 id 管理多个队列实例
|
|
89
|
-
* StreamQueue manager, manages multiple queue instances by id
|
|
90
|
-
*/
|
|
91
|
-
export class StreamQueueManager<Q extends BaseStreamQueueInterface> {
|
|
92
|
-
/** 存储队列实例的 Map / Map storing queue instances */
|
|
93
|
-
private queues: Map<string, Q> = new Map();
|
|
94
|
-
/** 默认是否压缩消息 / Default compress messages setting */
|
|
95
|
-
private defaultCompressMessages: boolean;
|
|
96
|
-
/** 队列构造函数 / Queue constructor */
|
|
97
|
-
private queueConstructor: new (queueId: string) => Q;
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* 构造函数
|
|
101
|
-
* Constructor
|
|
102
|
-
* @param queueConstructor 队列构造函数 / Queue constructor
|
|
103
|
-
* @param options 配置选项 / Configuration options
|
|
104
|
-
*/
|
|
105
|
-
constructor(
|
|
106
|
-
queueConstructor: new (id: string) => Q,
|
|
107
|
-
options: {
|
|
108
|
-
/** 默认是否压缩消息 / Default compress messages setting */
|
|
109
|
-
defaultCompressMessages?: boolean;
|
|
110
|
-
} = {},
|
|
111
|
-
) {
|
|
112
|
-
this.defaultCompressMessages = options.defaultCompressMessages ?? true;
|
|
113
|
-
this.queueConstructor = queueConstructor;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* 创建指定 id 的队列
|
|
118
|
-
* Create queue with specified id
|
|
119
|
-
* @param id 队列 ID / Queue ID
|
|
120
|
-
* @param compressMessages 是否压缩消息 / Whether to compress messages
|
|
121
|
-
* @returns 创建的队列实例 / Created queue instance
|
|
122
|
-
*/
|
|
123
|
-
createQueue(id: string, compressMessages?: boolean): Q {
|
|
124
|
-
const compress = compressMessages ?? this.defaultCompressMessages;
|
|
125
|
-
this.queues.set(id, new this.queueConstructor(id));
|
|
126
|
-
return this.queues.get(id)!;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* 获取或创建指定 id 的队列
|
|
131
|
-
* Get or create queue with specified id
|
|
132
|
-
* @param id 队列 ID / Queue ID
|
|
133
|
-
* @param compressMessages 是否压缩消息,默认为构造函数中的默认值 / Whether to compress messages, defaults to constructor default
|
|
134
|
-
* @returns StreamQueue 实例 / StreamQueue instance
|
|
135
|
-
*/
|
|
136
|
-
getQueue(id: string): Q {
|
|
137
|
-
const queue = this.queues.get(id);
|
|
138
|
-
if (!queue) {
|
|
139
|
-
throw new Error(`Queue with id '${id}' does not exist`);
|
|
140
|
-
}
|
|
141
|
-
return queue;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* 取消指定 id 的队列
|
|
146
|
-
* Cancel queue with specified id
|
|
147
|
-
* @param id 队列 ID / Queue ID
|
|
148
|
-
*/
|
|
149
|
-
cancelQueue(id: string): void {
|
|
150
|
-
const queue = this.queues.get(id);
|
|
151
|
-
if (queue) {
|
|
152
|
-
queue.cancel();
|
|
153
|
-
this.removeQueue(id);
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
/**
|
|
157
|
-
* 向指定 id 的队列推送数据
|
|
158
|
-
* Push data to queue with specified id
|
|
159
|
-
* @param id 队列 ID / Queue ID
|
|
160
|
-
* @param item 要推送的数据项 / Item to push
|
|
161
|
-
* @param compressMessages 是否压缩消息,默认为构造函数中的默认值 / Whether to compress messages, defaults to constructor default
|
|
162
|
-
*/
|
|
163
|
-
async pushToQueue(id: string, item: EventMessage, compressMessages?: boolean): Promise<void> {
|
|
164
|
-
const queue = this.getQueue(id);
|
|
165
|
-
await queue.push(item);
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
/**
|
|
169
|
-
* 获取指定 id 队列中的所有数据
|
|
170
|
-
* Get all data from queue with specified id
|
|
171
|
-
* @param id 队列 ID / Queue ID
|
|
172
|
-
* @returns 队列中的所有数据 / All data in the queue
|
|
173
|
-
*/
|
|
174
|
-
async getQueueData(id: string): Promise<EventMessage[]> {
|
|
175
|
-
const queue = this.queues.get(id);
|
|
176
|
-
if (!queue) {
|
|
177
|
-
throw new Error(`Queue with id '${id}' does not exist`);
|
|
178
|
-
}
|
|
179
|
-
return await queue.getAll();
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
/**
|
|
183
|
-
* 清空指定 id 的队列
|
|
184
|
-
* Clear queue with specified id
|
|
185
|
-
* @param id 队列 ID / Queue ID
|
|
186
|
-
*/
|
|
187
|
-
clearQueue(id: string): void {
|
|
188
|
-
const queue = this.queues.get(id);
|
|
189
|
-
if (queue) {
|
|
190
|
-
queue.clear();
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
/**
|
|
195
|
-
* 删除指定 id 的队列
|
|
196
|
-
* Remove queue with specified id
|
|
197
|
-
* @param id 队列 ID / Queue ID
|
|
198
|
-
* @returns 是否成功删除 / Whether successfully deleted
|
|
199
|
-
*/
|
|
200
|
-
removeQueue(id: string) {
|
|
201
|
-
setTimeout(() => {
|
|
202
|
-
return this.queues.delete(id);
|
|
203
|
-
}, 500);
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
/**
|
|
207
|
-
* 获取所有队列的 ID
|
|
208
|
-
* Get all queue IDs
|
|
209
|
-
* @returns 所有队列 ID 的数组 / Array of all queue IDs
|
|
210
|
-
*/
|
|
211
|
-
getAllQueueIds(): string[] {
|
|
212
|
-
return Array.from(this.queues.keys());
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
/**
|
|
216
|
-
* 获取所有队列及其数据的快照
|
|
217
|
-
* Get snapshot of all queues and their data
|
|
218
|
-
* @returns 包含所有队列数据的结果对象 / Result object containing all queue data
|
|
219
|
-
*/
|
|
220
|
-
async getAllQueuesData(): Promise<Record<string, EventMessage[]>> {
|
|
221
|
-
const result: Record<string, EventMessage[]> = {};
|
|
222
|
-
for (const [id, queue] of this.queues) {
|
|
223
|
-
result[id] = await queue.getAll();
|
|
224
|
-
}
|
|
225
|
-
return result;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
/**
|
|
229
|
-
* 清空所有队列
|
|
230
|
-
* Clear all queues
|
|
231
|
-
*/
|
|
232
|
-
clearAllQueues(): void {
|
|
233
|
-
for (const queue of this.queues.values()) {
|
|
234
|
-
queue.clear();
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
}
|
package/src/storage/index.ts
DELETED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import { BaseStreamQueueInterface, StreamQueueManager } from '../queue/stream_queue';
|
|
2
|
-
import { MemorySaver } from './memory/checkpoint';
|
|
3
|
-
import { MemoryStreamQueue } from './memory/queue';
|
|
4
|
-
import { MemoryThreadsManager } from './memory/threads';
|
|
5
|
-
import type { SqliteSaver as SqliteSaverType } from './sqlite/checkpoint';
|
|
6
|
-
import { SQLiteThreadsManager } from './sqlite/threads';
|
|
7
|
-
|
|
8
|
-
// 所有的适配实现,都请写到这里,通过环境变量进行判断使用哪种方式进行适配
|
|
9
|
-
|
|
10
|
-
export const createCheckPointer = async () => {
|
|
11
|
-
if (
|
|
12
|
-
(process.env.REDIS_URL && process.env.CHECKPOINT_TYPE === 'redis') ||
|
|
13
|
-
process.env.CHECKPOINT_TYPE === 'shallow/redis'
|
|
14
|
-
) {
|
|
15
|
-
if (process.env.CHECKPOINT_TYPE === 'redis') {
|
|
16
|
-
const { RedisSaver } = await import('@langchain/langgraph-checkpoint-redis');
|
|
17
|
-
return await RedisSaver.fromUrl(process.env.REDIS_URL!, {
|
|
18
|
-
defaultTTL: 60, // TTL in minutes
|
|
19
|
-
refreshOnRead: true,
|
|
20
|
-
});
|
|
21
|
-
}
|
|
22
|
-
if (process.env.CHECKPOINT_TYPE === 'shallow/redis') {
|
|
23
|
-
const { ShallowRedisSaver } = await import('@langchain/langgraph-checkpoint-redis/shallow');
|
|
24
|
-
return await ShallowRedisSaver.fromUrl(process.env.REDIS_URL!);
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
if (process.env.SQLITE_DATABASE_URI) {
|
|
28
|
-
const { SqliteSaver } = await import('./sqlite/checkpoint');
|
|
29
|
-
const db = SqliteSaver.fromConnString(process.env.SQLITE_DATABASE_URI);
|
|
30
|
-
return db;
|
|
31
|
-
}
|
|
32
|
-
return new MemorySaver();
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
export const createMessageQueue = async () => {
|
|
36
|
-
let q: new (id: string) => BaseStreamQueueInterface;
|
|
37
|
-
if (process.env.REDIS_URL) {
|
|
38
|
-
console.log('Using redis as stream queue');
|
|
39
|
-
const { RedisStreamQueue } = await import('./redis/queue');
|
|
40
|
-
q = RedisStreamQueue;
|
|
41
|
-
} else {
|
|
42
|
-
q = MemoryStreamQueue;
|
|
43
|
-
}
|
|
44
|
-
return new StreamQueueManager(q);
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
export const createThreadManager = (config: { checkpointer?: SqliteSaverType }) => {
|
|
48
|
-
if (process.env.SQLITE_DATABASE_URI && config.checkpointer) {
|
|
49
|
-
return new SQLiteThreadsManager(config.checkpointer);
|
|
50
|
-
}
|
|
51
|
-
return new MemoryThreadsManager();
|
|
52
|
-
};
|