@kortyx/runtime 0.3.0 → 0.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 (49) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/checkpointer.d.ts +2 -2
  3. package/dist/checkpointer.d.ts.map +1 -1
  4. package/dist/checkpointer.js +11 -5
  5. package/dist/framework/adapter.d.ts +21 -0
  6. package/dist/framework/adapter.d.ts.map +1 -0
  7. package/dist/framework/adapter.js +74 -0
  8. package/dist/framework/in-memory-checkpointer.d.ts +9 -0
  9. package/dist/framework/in-memory-checkpointer.d.ts.map +1 -0
  10. package/dist/framework/in-memory-checkpointer.js +134 -0
  11. package/dist/framework/pending-requests.d.ts +33 -0
  12. package/dist/framework/pending-requests.d.ts.map +1 -0
  13. package/dist/framework/pending-requests.js +32 -0
  14. package/dist/framework/redis/pending-request-store.d.ts +8 -0
  15. package/dist/framework/redis/pending-request-store.d.ts.map +1 -0
  16. package/dist/framework/redis/pending-request-store.js +30 -0
  17. package/dist/framework/redis/redis-checkpointer.d.ts +13 -0
  18. package/dist/framework/redis/redis-checkpointer.d.ts.map +1 -0
  19. package/dist/framework/redis/redis-checkpointer.js +159 -0
  20. package/dist/framework/redis/redis-client.d.ts +13 -0
  21. package/dist/framework/redis/redis-client.d.ts.map +1 -0
  22. package/dist/framework/redis/redis-client.js +181 -0
  23. package/dist/framework/redis/redis-store.d.ts +20 -0
  24. package/dist/framework/redis/redis-store.d.ts.map +1 -0
  25. package/dist/framework/redis/redis-store.js +107 -0
  26. package/dist/graph/create-langgraph.d.ts +4 -2
  27. package/dist/graph/create-langgraph.d.ts.map +1 -1
  28. package/dist/graph/create-langgraph.js +56 -34
  29. package/dist/index.d.ts +4 -0
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/index.js +4 -0
  32. package/dist/node-loader.d.ts +7 -0
  33. package/dist/node-loader.d.ts.map +1 -0
  34. package/dist/node-loader.js +58 -0
  35. package/dist/node-registry.d.ts +7 -0
  36. package/dist/node-registry.d.ts.map +1 -0
  37. package/dist/node-registry.js +27 -0
  38. package/dist/registry/file-registry.d.ts.map +1 -1
  39. package/dist/registry/file-registry.js +25 -2
  40. package/dist/registry/in-memory-registry.d.ts +6 -0
  41. package/dist/registry/in-memory-registry.d.ts.map +1 -0
  42. package/dist/registry/in-memory-registry.js +25 -0
  43. package/dist/registry/interface.d.ts +2 -2
  44. package/dist/registry/interface.d.ts.map +1 -1
  45. package/dist/state/build-initial-state.d.ts +1 -1
  46. package/dist/state/build-initial-state.d.ts.map +1 -1
  47. package/dist/state/build-initial-state.js +6 -2
  48. package/package.json +17 -16
  49. package/LICENSE +0 -201
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.4.0](https://github.com/kortyx-io/Kortyx/compare/runtime-v0.3.0...runtime-v0.4.0) (2026-02-07)
4
+
5
+
6
+ ### Features
7
+
8
+ * **runtime:** add node registry and declarative workflow support ([8a4a66c](https://github.com/kortyx-io/Kortyx/commit/8a4a66c4bba6eed688e35380e000e053fb4af52c))
9
+ * **runtime:** framework persistence adapter ([8383ad2](https://github.com/kortyx-io/Kortyx/commit/8383ad2d73754ccf4daf33e414e15c3ce44df8cb))
10
+
3
11
  ## [0.3.0](https://github.com/kortyx-io/kortyx/compare/runtime-v0.2.2...runtime-v0.3.0) (2026-01-25)
4
12
 
5
13
 
@@ -1,3 +1,3 @@
1
- import { MemorySaver } from "@langchain/langgraph";
2
- export declare function getCheckpointer(key: string): MemorySaver;
1
+ import type { BaseCheckpointSaver } from "@langchain/langgraph-checkpoint";
2
+ export declare function getCheckpointer(key: string): BaseCheckpointSaver;
3
3
  //# sourceMappingURL=checkpointer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"checkpointer.d.ts","sourceRoot":"","sources":["../src/checkpointer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAInD,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,WAAW,CAQxD"}
1
+ {"version":3,"file":"checkpointer.d.ts","sourceRoot":"","sources":["../src/checkpointer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AAM3E,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,mBAAmB,CAehE"}
@@ -1,14 +1,20 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getCheckpointer = getCheckpointer;
4
- const langgraph_1 = require("@langchain/langgraph");
4
+ const in_memory_checkpointer_1 = require("./framework/in-memory-checkpointer");
5
+ const MAX_CHECKPOINTERS = 200;
5
6
  const map = new Map();
6
7
  function getCheckpointer(key) {
7
8
  const k = key || "__default__";
8
- let saver = map.get(k);
9
- if (!saver) {
10
- saver = new langgraph_1.MemorySaver();
11
- map.set(k, saver);
9
+ const existing = map.get(k);
10
+ if (existing)
11
+ return existing;
12
+ const saver = (0, in_memory_checkpointer_1.createInMemoryCheckpointSaver)();
13
+ map.set(k, saver);
14
+ if (map.size > MAX_CHECKPOINTERS) {
15
+ const first = map.keys().next().value;
16
+ if (first)
17
+ map.delete(first);
12
18
  }
13
19
  return saver;
14
20
  }
@@ -0,0 +1,21 @@
1
+ import type { BaseCheckpointSaver } from "@langchain/langgraph-checkpoint";
2
+ import { type PendingRequestStore } from "./pending-requests";
3
+ export type FrameworkAdapter = {
4
+ kind: "memory" | "redis";
5
+ pendingRequests: PendingRequestStore;
6
+ checkpointer: BaseCheckpointSaver;
7
+ ttlMs: number;
8
+ cleanupRun?: (runId: string, namespaces: string[]) => Promise<void>;
9
+ };
10
+ export type CreateInMemoryFrameworkAdapterOptions = {
11
+ ttlMs?: number;
12
+ };
13
+ export declare function createInMemoryFrameworkAdapter(options?: CreateInMemoryFrameworkAdapterOptions): FrameworkAdapter;
14
+ export type CreateRedisFrameworkAdapterOptions = {
15
+ url: string;
16
+ ttlMs?: number;
17
+ prefix?: string;
18
+ };
19
+ export declare function createRedisFrameworkAdapter(options: CreateRedisFrameworkAdapterOptions): FrameworkAdapter;
20
+ export declare function createFrameworkAdapterFromEnv(env?: Record<string, string | undefined>): FrameworkAdapter;
21
+ //# sourceMappingURL=adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../src/framework/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AAE3E,OAAO,EAEL,KAAK,mBAAmB,EACzB,MAAM,oBAAoB,CAAC;AAK5B,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC;IACzB,eAAe,EAAE,mBAAmB,CAAC;IACrC,YAAY,EAAE,mBAAmB,CAAC;IAClC,KAAK,EAAE,MAAM,CAAC;IAKd,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACrE,CAAC;AAEF,MAAM,MAAM,qCAAqC,GAAG;IAClD,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,wBAAgB,8BAA8B,CAC5C,OAAO,CAAC,EAAE,qCAAqC,GAC9C,gBAAgB,CAgBlB;AAED,MAAM,MAAM,kCAAkC,GAAG;IAC/C,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,kCAAkC,GAC1C,gBAAgB,CAsClB;AAED,wBAAgB,6BAA6B,CAC3C,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAe,GACpD,gBAAgB,CAclB"}
@@ -0,0 +1,74 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createInMemoryFrameworkAdapter = createInMemoryFrameworkAdapter;
4
+ exports.createRedisFrameworkAdapter = createRedisFrameworkAdapter;
5
+ exports.createFrameworkAdapterFromEnv = createFrameworkAdapterFromEnv;
6
+ const in_memory_checkpointer_1 = require("./in-memory-checkpointer");
7
+ const pending_requests_1 = require("./pending-requests");
8
+ const pending_request_store_1 = require("./redis/pending-request-store");
9
+ const redis_checkpointer_1 = require("./redis/redis-checkpointer");
10
+ const redis_store_1 = require("./redis/redis-store");
11
+ function createInMemoryFrameworkAdapter(options) {
12
+ const ttlMs = options?.ttlMs ?? 15 * 60 * 1000;
13
+ const checkpointer = (0, in_memory_checkpointer_1.createInMemoryCheckpointSaver)();
14
+ return {
15
+ kind: "memory",
16
+ ttlMs,
17
+ pendingRequests: (0, pending_requests_1.createInMemoryPendingRequestStore)(),
18
+ checkpointer,
19
+ cleanupRun: async (runId) => {
20
+ try {
21
+ await checkpointer.deleteThread(runId);
22
+ }
23
+ catch {
24
+ }
25
+ },
26
+ };
27
+ }
28
+ function createRedisFrameworkAdapter(options) {
29
+ const ttlMs = options.ttlMs ?? 15 * 60 * 1000;
30
+ const store = (0, redis_store_1.createRedisFrameworkStore)({
31
+ url: options.url,
32
+ prefix: options.prefix ?? "kortyx:fw:",
33
+ });
34
+ const cpPrefix = "kortyx:cp:";
35
+ return {
36
+ kind: "redis",
37
+ ttlMs,
38
+ pendingRequests: (0, pending_request_store_1.createRedisPendingRequestStore)({
39
+ store,
40
+ prefix: "kortyx:pending:",
41
+ }),
42
+ checkpointer: (0, redis_checkpointer_1.createRedisCheckpointSaver)({
43
+ store,
44
+ ttlMs,
45
+ prefix: cpPrefix,
46
+ }),
47
+ cleanupRun: async (runId, namespaces) => {
48
+ const nsList = namespaces.length > 0 ? namespaces : [""];
49
+ await Promise.all(nsList.map(async (checkpointNs) => {
50
+ const ns = String(checkpointNs ?? "");
51
+ const latestKey = `${cpPrefix}latest:${runId}:${ns}`;
52
+ const checkpointId = (await store.get(latestKey)) ?? "";
53
+ if (!checkpointId)
54
+ return;
55
+ await Promise.all([
56
+ store.del(`${cpPrefix}chk:${runId}:${ns}:${checkpointId}`),
57
+ store.del(`${cpPrefix}wr:${runId}:${ns}:${checkpointId}`),
58
+ store.del(latestKey),
59
+ ]);
60
+ }));
61
+ },
62
+ };
63
+ }
64
+ function createFrameworkAdapterFromEnv(env = process.env) {
65
+ const url = env.KORTYX_REDIS_URL ||
66
+ env.REDIS_URL ||
67
+ env.KORTYX_FRAMEWORK_REDIS_URL ||
68
+ "";
69
+ const ttlMsRaw = env.KORTYX_FRAMEWORK_TTL_MS || env.KORTYX_TTL_MS || "";
70
+ const ttlMs = ttlMsRaw ? Number(ttlMsRaw) : undefined;
71
+ if (url)
72
+ return createRedisFrameworkAdapter({ url, ...(ttlMs ? { ttlMs } : {}) });
73
+ return createInMemoryFrameworkAdapter({ ...(ttlMs ? { ttlMs } : {}) });
74
+ }
@@ -0,0 +1,9 @@
1
+ import { type BaseCheckpointSaver } from "@langchain/langgraph-checkpoint";
2
+ export type InMemoryCheckpointSaverOptions = {
3
+ maxWritesPerCheckpoint?: number;
4
+ };
5
+ export type InMemoryCheckpointSaver = BaseCheckpointSaver & {
6
+ deleteThread: (threadId: string) => Promise<void>;
7
+ };
8
+ export declare function createInMemoryCheckpointSaver(options?: InMemoryCheckpointSaverOptions): InMemoryCheckpointSaver;
9
+ //# sourceMappingURL=in-memory-checkpointer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"in-memory-checkpointer.d.ts","sourceRoot":"","sources":["../../src/framework/in-memory-checkpointer.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,mBAAmB,EASzB,MAAM,iCAAiC,CAAC;AAczC,MAAM,MAAM,8BAA8B,GAAG;IAK3C,sBAAsB,CAAC,EAAE,MAAM,CAAC;CACjC,CAAC;AAeF,MAAM,MAAM,uBAAuB,GAAG,mBAAmB,GAAG;IAC1D,YAAY,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACnD,CAAC;AASF,wBAAgB,6BAA6B,CAC3C,OAAO,CAAC,EAAE,8BAA8B,GACvC,uBAAuB,CAyKzB"}
@@ -0,0 +1,134 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createInMemoryCheckpointSaver = createInMemoryCheckpointSaver;
4
+ const langgraph_checkpoint_1 = require("@langchain/langgraph-checkpoint");
5
+ const utf8Bytes = (s) => new Uint8Array(Buffer.from(s, "utf8"));
6
+ const utf8String = (u8) => typeof u8 === "string" ? u8 : Buffer.from(u8).toString("utf8");
7
+ const jsonSerde = {
8
+ async dumpsTyped(data) {
9
+ return ["json", utf8Bytes(JSON.stringify(data))];
10
+ },
11
+ async loadsTyped(_type, data) {
12
+ return JSON.parse(utf8String(data));
13
+ },
14
+ };
15
+ function createInMemoryCheckpointSaver(options) {
16
+ const maxWritesPerCheckpoint = options?.maxWritesPerCheckpoint ?? 2_000;
17
+ const latest = new Map();
18
+ const checkpoints = new Map();
19
+ const nsKey = (threadId, checkpointNs) => `${threadId}\u0001${checkpointNs}`;
20
+ const chkKey = (threadId, checkpointNs, checkpointId) => `${threadId}\u0001${checkpointNs}\u0001${checkpointId}`;
21
+ const saver = {
22
+ serde: jsonSerde,
23
+ async get(config) {
24
+ const value = await saver.getTuple(config);
25
+ return value ? value.checkpoint : undefined;
26
+ },
27
+ getNextVersion(current) {
28
+ if (typeof current === "string") {
29
+ throw new Error("Please override this method to use string versions.");
30
+ }
31
+ return current !== undefined && typeof current === "number"
32
+ ? current + 1
33
+ : 1;
34
+ },
35
+ async getTuple(config) {
36
+ const threadId = config.configurable?.thread_id;
37
+ const checkpointNs = config.configurable?.checkpoint_ns ?? "";
38
+ if (!threadId)
39
+ return undefined;
40
+ const requested = (0, langgraph_checkpoint_1.getCheckpointId)(config);
41
+ const id = requested || latest.get(nsKey(threadId, checkpointNs));
42
+ if (!id)
43
+ return undefined;
44
+ const stored = checkpoints.get(chkKey(threadId, checkpointNs, id));
45
+ if (!stored)
46
+ return undefined;
47
+ const pendingWrites = Array.from(stored.writes.values());
48
+ const tuple = {
49
+ config: {
50
+ configurable: {
51
+ thread_id: threadId,
52
+ checkpoint_ns: checkpointNs,
53
+ checkpoint_id: id,
54
+ },
55
+ },
56
+ checkpoint: stored.checkpoint,
57
+ metadata: stored.metadata,
58
+ pendingWrites: pendingWrites,
59
+ };
60
+ if (stored.parentCheckpointId) {
61
+ tuple.parentConfig = {
62
+ configurable: {
63
+ thread_id: threadId,
64
+ checkpoint_ns: checkpointNs,
65
+ checkpoint_id: stored.parentCheckpointId,
66
+ },
67
+ };
68
+ }
69
+ return tuple;
70
+ },
71
+ async *list(_config, _options) {
72
+ },
73
+ async put(config, checkpoint, metadata, _newVersions) {
74
+ const threadId = config.configurable?.thread_id;
75
+ const checkpointNs = config.configurable?.checkpoint_ns ?? "";
76
+ if (!threadId) {
77
+ throw new Error('Failed to put checkpoint: missing "thread_id" in config.configurable.');
78
+ }
79
+ const ns = nsKey(threadId, checkpointNs);
80
+ const prevId = latest.get(ns);
81
+ const prepared = (0, langgraph_checkpoint_1.copyCheckpoint)(checkpoint);
82
+ const entry = {
83
+ checkpoint: prepared,
84
+ metadata,
85
+ parentCheckpointId: config.configurable?.checkpoint_id,
86
+ writes: new Map(),
87
+ };
88
+ const id = checkpoint.id;
89
+ checkpoints.set(chkKey(threadId, checkpointNs, id), entry);
90
+ latest.set(ns, id);
91
+ if (prevId && prevId !== id) {
92
+ checkpoints.delete(chkKey(threadId, checkpointNs, prevId));
93
+ }
94
+ return {
95
+ configurable: {
96
+ thread_id: threadId,
97
+ checkpoint_ns: checkpointNs,
98
+ checkpoint_id: id,
99
+ },
100
+ };
101
+ },
102
+ async putWrites(config, writes, taskId) {
103
+ const threadId = config.configurable?.thread_id;
104
+ const checkpointNs = config.configurable?.checkpoint_ns ?? "";
105
+ const checkpointId = config.configurable?.checkpoint_id;
106
+ if (!threadId || !checkpointId)
107
+ return;
108
+ const stored = checkpoints.get(chkKey(threadId, checkpointNs, checkpointId));
109
+ if (!stored)
110
+ return;
111
+ for (let idx = 0; idx < writes.length; idx++) {
112
+ const [channel, value] = writes[idx];
113
+ const mappedIdx = langgraph_checkpoint_1.WRITES_IDX_MAP[channel] ?? idx;
114
+ const key = `${taskId},${mappedIdx}`;
115
+ if (mappedIdx >= 0 && stored.writes.has(key))
116
+ continue;
117
+ if (stored.writes.size >= maxWritesPerCheckpoint)
118
+ break;
119
+ stored.writes.set(key, [taskId, channel, value]);
120
+ }
121
+ },
122
+ async deleteThread(threadId) {
123
+ for (const k of Array.from(latest.keys())) {
124
+ if (k.startsWith(`${threadId}\u0001`))
125
+ latest.delete(k);
126
+ }
127
+ for (const k of Array.from(checkpoints.keys())) {
128
+ if (k.startsWith(`${threadId}\u0001`))
129
+ checkpoints.delete(k);
130
+ }
131
+ },
132
+ };
133
+ return saver;
134
+ }
@@ -0,0 +1,33 @@
1
+ import type { GraphState } from "@kortyx/core";
2
+ export type HumanInputKind = "choice" | "multi-choice" | "text";
3
+ export interface HumanInputOption {
4
+ id: string;
5
+ label: string;
6
+ description?: string;
7
+ value?: unknown;
8
+ }
9
+ export interface PendingRequestRecord {
10
+ token: string;
11
+ requestId: string;
12
+ sessionId?: string | undefined;
13
+ runId: string;
14
+ workflow: string;
15
+ node: string;
16
+ state?: GraphState;
17
+ schema: {
18
+ kind: HumanInputKind;
19
+ multiple: boolean;
20
+ question?: string;
21
+ };
22
+ options: HumanInputOption[];
23
+ createdAt: number;
24
+ ttlMs: number;
25
+ }
26
+ export interface PendingRequestStore {
27
+ save: (rec: PendingRequestRecord) => Promise<void>;
28
+ get: (token: string) => Promise<PendingRequestRecord | null>;
29
+ delete: (token: string) => Promise<void>;
30
+ update: (token: string, patch: Partial<PendingRequestRecord>) => Promise<void>;
31
+ }
32
+ export declare function createInMemoryPendingRequestStore(): PendingRequestStore;
33
+ //# sourceMappingURL=pending-requests.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pending-requests.d.ts","sourceRoot":"","sources":["../../src/framework/pending-requests.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE/C,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,cAAc,GAAG,MAAM,CAAC;AAEhE,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAK/B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,MAAM,EAAE;QAAE,IAAI,EAAE,cAAc,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACvE,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,CAAC,GAAG,EAAE,oBAAoB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACnD,GAAG,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAAC;IAC7D,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACzC,MAAM,EAAE,CACN,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,OAAO,CAAC,oBAAoB,CAAC,KACjC,OAAO,CAAC,IAAI,CAAC,CAAC;CACpB;AAED,wBAAgB,iCAAiC,IAAI,mBAAmB,CA4BvE"}
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createInMemoryPendingRequestStore = createInMemoryPendingRequestStore;
4
+ function createInMemoryPendingRequestStore() {
5
+ const store = new Map();
6
+ const prune = () => {
7
+ const t = Date.now();
8
+ for (const [k, v] of store.entries()) {
9
+ if (t - v.createdAt > v.ttlMs)
10
+ store.delete(k);
11
+ }
12
+ };
13
+ return {
14
+ async save(rec) {
15
+ prune();
16
+ store.set(rec.token, rec);
17
+ },
18
+ async get(token) {
19
+ prune();
20
+ return store.get(token) ?? null;
21
+ },
22
+ async delete(token) {
23
+ store.delete(token);
24
+ },
25
+ async update(token, patch) {
26
+ const prev = store.get(token);
27
+ if (!prev)
28
+ return;
29
+ store.set(token, { ...prev, ...patch });
30
+ },
31
+ };
32
+ }
@@ -0,0 +1,8 @@
1
+ import type { PendingRequestStore } from "../pending-requests";
2
+ import type { RedisFrameworkStore } from "./redis-store";
3
+ export type RedisPendingRequestStoreOptions = {
4
+ store: RedisFrameworkStore;
5
+ prefix?: string;
6
+ };
7
+ export declare function createRedisPendingRequestStore(options: RedisPendingRequestStoreOptions): PendingRequestStore;
8
+ //# sourceMappingURL=pending-request-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pending-request-store.d.ts","sourceRoot":"","sources":["../../../src/framework/redis/pending-request-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,mBAAmB,EACpB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAEzD,MAAM,MAAM,+BAA+B,GAAG;IAC5C,KAAK,EAAE,mBAAmB,CAAC;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,wBAAgB,8BAA8B,CAC5C,OAAO,EAAE,+BAA+B,GACvC,mBAAmB,CAyBrB"}
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createRedisPendingRequestStore = createRedisPendingRequestStore;
4
+ function createRedisPendingRequestStore(options) {
5
+ const prefix = options.prefix ?? "kortyx:pending:";
6
+ const key = (token) => `${prefix}${token}`;
7
+ return {
8
+ async save(rec) {
9
+ await options.store.set(key(rec.token), JSON.stringify(rec), rec.ttlMs);
10
+ },
11
+ async get(token) {
12
+ const raw = await options.store.get(key(token));
13
+ if (!raw)
14
+ return null;
15
+ return JSON.parse(raw);
16
+ },
17
+ async delete(token) {
18
+ await options.store.del(key(token));
19
+ },
20
+ async update(token, patch) {
21
+ const prevRaw = await options.store.get(key(token));
22
+ if (!prevRaw)
23
+ return;
24
+ const prev = JSON.parse(prevRaw);
25
+ const next = { ...prev, ...patch };
26
+ const ttlLeft = Math.max(1, next.ttlMs - (Date.now() - next.createdAt));
27
+ await options.store.set(key(token), JSON.stringify(next), ttlLeft);
28
+ },
29
+ };
30
+ }
@@ -0,0 +1,13 @@
1
+ import { type BaseCheckpointSaver } from "@langchain/langgraph-checkpoint";
2
+ import type { RedisFrameworkStore } from "./redis-store";
3
+ type RedisCheckpointSaverOptions = {
4
+ store: RedisFrameworkStore;
5
+ prefix?: string;
6
+ ttlMs: number;
7
+ };
8
+ export type RedisCheckpointSaver = BaseCheckpointSaver & {
9
+ deleteThread: (threadId: string) => Promise<void>;
10
+ };
11
+ export declare function createRedisCheckpointSaver(options: RedisCheckpointSaverOptions): RedisCheckpointSaver;
12
+ export {};
13
+ //# sourceMappingURL=redis-checkpointer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redis-checkpointer.d.ts","sourceRoot":"","sources":["../../../src/framework/redis/redis-checkpointer.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,mBAAmB,EASzB,MAAM,iCAAiC,CAAC;AACzC,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAOzD,KAAK,2BAA2B,GAAG;IACjC,KAAK,EAAE,mBAAmB,CAAC;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AA8BF,MAAM,MAAM,oBAAoB,GAAG,mBAAmB,GAAG;IACvD,YAAY,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACnD,CAAC;AAEF,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,2BAA2B,GACnC,oBAAoB,CAwNtB"}
@@ -0,0 +1,159 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createRedisCheckpointSaver = createRedisCheckpointSaver;
4
+ const langgraph_checkpoint_1 = require("@langchain/langgraph-checkpoint");
5
+ const utf8Bytes = (s) => new Uint8Array(Buffer.from(s, "utf8"));
6
+ const utf8String = (u8) => typeof u8 === "string" ? u8 : Buffer.from(u8).toString("utf8");
7
+ const jsonSerde = {
8
+ async dumpsTyped(data) {
9
+ return ["json", utf8Bytes(JSON.stringify(data))];
10
+ },
11
+ async loadsTyped(_type, data) {
12
+ return JSON.parse(utf8String(data));
13
+ },
14
+ };
15
+ const b64FromBytes = (u8) => Buffer.from(u8).toString("base64");
16
+ const bytesFromB64 = (s) => new Uint8Array(Buffer.from(s, "base64"));
17
+ function createRedisCheckpointSaver(options) {
18
+ const store = options.store;
19
+ const ttlMs = options.ttlMs;
20
+ const prefix = options.prefix ?? "kortyx:cp:";
21
+ const latestKey = (threadId, checkpointNs) => `${prefix}latest:${threadId}:${checkpointNs}`;
22
+ const checkpointKey = (threadId, checkpointNs, checkpointId) => `${prefix}chk:${threadId}:${checkpointNs}:${checkpointId}`;
23
+ const writesKey = (threadId, checkpointNs, checkpointId) => `${prefix}wr:${threadId}:${checkpointNs}:${checkpointId}`;
24
+ const serde = jsonSerde;
25
+ const saver = {
26
+ serde,
27
+ async get(config) {
28
+ const value = await saver.getTuple(config);
29
+ return value ? value.checkpoint : undefined;
30
+ },
31
+ getNextVersion(current) {
32
+ if (typeof current === "string") {
33
+ throw new Error("Please override this method to use string versions.");
34
+ }
35
+ return current !== undefined && typeof current === "number"
36
+ ? current + 1
37
+ : 1;
38
+ },
39
+ async getTuple(config) {
40
+ const threadId = config.configurable?.thread_id;
41
+ const checkpointNs = config.configurable?.checkpoint_ns ?? "";
42
+ if (!threadId)
43
+ return undefined;
44
+ const requested = (0, langgraph_checkpoint_1.getCheckpointId)(config);
45
+ const checkpointId = requested || (await store.get(latestKey(threadId, checkpointNs))) || "";
46
+ if (!checkpointId)
47
+ return undefined;
48
+ const raw = await store.get(checkpointKey(threadId, checkpointNs, checkpointId));
49
+ if (!raw)
50
+ return undefined;
51
+ const parsed = JSON.parse(raw);
52
+ const checkpoint = (await serde.loadsTyped("json", bytesFromB64(parsed.checkpoint)));
53
+ const metadata = (await serde.loadsTyped("json", bytesFromB64(parsed.metadata)));
54
+ const writes = await store.hgetall(writesKey(threadId, checkpointNs, checkpointId));
55
+ const pendingWrites = await Promise.all(Object.values(writes).map(async (v) => {
56
+ const w = JSON.parse(v);
57
+ return [
58
+ w.taskId,
59
+ w.channel,
60
+ await serde.loadsTyped("json", bytesFromB64(w.value)),
61
+ ];
62
+ }));
63
+ const tuple = {
64
+ config: {
65
+ configurable: {
66
+ thread_id: threadId,
67
+ checkpoint_ns: checkpointNs,
68
+ checkpoint_id: checkpointId,
69
+ },
70
+ },
71
+ checkpoint,
72
+ metadata,
73
+ pendingWrites,
74
+ };
75
+ if (parsed.parentCheckpointId) {
76
+ tuple.parentConfig = {
77
+ configurable: {
78
+ thread_id: threadId,
79
+ checkpoint_ns: checkpointNs,
80
+ checkpoint_id: parsed.parentCheckpointId,
81
+ },
82
+ };
83
+ }
84
+ return tuple;
85
+ },
86
+ async *list(_config, _options) {
87
+ },
88
+ async put(config, checkpoint, metadata, _newVersions) {
89
+ const threadId = config.configurable?.thread_id;
90
+ const checkpointNs = config.configurable?.checkpoint_ns ?? "";
91
+ if (!threadId) {
92
+ throw new Error('Failed to put checkpoint: missing "thread_id" in config.configurable.');
93
+ }
94
+ const prevId = await store.get(latestKey(threadId, checkpointNs));
95
+ const prepared = (0, langgraph_checkpoint_1.copyCheckpoint)(checkpoint);
96
+ const [[, chkBytes], [, metaBytes]] = await Promise.all([
97
+ serde.dumpsTyped(prepared),
98
+ serde.dumpsTyped(metadata),
99
+ ]);
100
+ const checkpointId = checkpoint.id;
101
+ const key = checkpointKey(threadId, checkpointNs, checkpointId);
102
+ const stored = {
103
+ checkpoint: b64FromBytes(chkBytes),
104
+ metadata: b64FromBytes(metaBytes),
105
+ parentCheckpointId: config.configurable?.checkpoint_id,
106
+ };
107
+ await store.set(key, JSON.stringify(stored), ttlMs);
108
+ await store.set(latestKey(threadId, checkpointNs), checkpointId, ttlMs);
109
+ if (prevId && prevId !== checkpointId) {
110
+ await store.del(checkpointKey(threadId, checkpointNs, prevId));
111
+ await store.del(writesKey(threadId, checkpointNs, prevId));
112
+ }
113
+ return {
114
+ configurable: {
115
+ thread_id: threadId,
116
+ checkpoint_ns: checkpointNs,
117
+ checkpoint_id: checkpointId,
118
+ },
119
+ };
120
+ },
121
+ async putWrites(config, writes, taskId) {
122
+ const threadId = config.configurable?.thread_id;
123
+ const checkpointNs = config.configurable?.checkpoint_ns;
124
+ const checkpointId = config.configurable?.checkpoint_id;
125
+ if (!threadId) {
126
+ throw new Error('Failed to put writes: missing "thread_id" in config.configurable.');
127
+ }
128
+ if (!checkpointId) {
129
+ throw new Error('Failed to put writes: missing "checkpoint_id" in config.configurable.');
130
+ }
131
+ const outer = writesKey(threadId, checkpointNs ?? "", checkpointId);
132
+ await store.expire(outer, ttlMs);
133
+ await Promise.all(writes.map(async ([channel, value], idx) => {
134
+ const [, vBytes] = await serde.dumpsTyped(value);
135
+ const mappedIdx = langgraph_checkpoint_1.WRITES_IDX_MAP[channel] ?? idx;
136
+ const innerKeyStr = `${taskId},${mappedIdx}`;
137
+ const payload = {
138
+ taskId,
139
+ channel,
140
+ value: b64FromBytes(vBytes),
141
+ };
142
+ const serialized = JSON.stringify(payload);
143
+ if (mappedIdx >= 0) {
144
+ await store.hsetnx(outer, innerKeyStr, serialized);
145
+ }
146
+ else {
147
+ await store.hset(outer, innerKeyStr, serialized);
148
+ }
149
+ }));
150
+ },
151
+ async deleteThread(threadId) {
152
+ const chkKeys = await store.scanKeys(`${prefix}chk:${threadId}:`);
153
+ const wrKeys = await store.scanKeys(`${prefix}wr:${threadId}:`);
154
+ const latestKeys = await store.scanKeys(`${prefix}latest:${threadId}:`);
155
+ await store.delRaw([...chkKeys, ...wrKeys, ...latestKeys]);
156
+ },
157
+ };
158
+ return saver;
159
+ }
@@ -0,0 +1,13 @@
1
+ export type RedisReply = string | number | null | RedisReply[] | {
2
+ type: "error";
3
+ message: string;
4
+ };
5
+ export type RedisClientOptions = {
6
+ url: string;
7
+ connectTimeoutMs?: number;
8
+ };
9
+ export type RedisClient = {
10
+ command: (cmd: string, args?: string[]) => Promise<RedisReply>;
11
+ };
12
+ export declare function createRedisClient(options: RedisClientOptions): RedisClient;
13
+ //# sourceMappingURL=redis-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redis-client.d.ts","sourceRoot":"","sources":["../../../src/framework/redis/redis-client.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,UAAU,GAClB,MAAM,GACN,MAAM,GACN,IAAI,GACJ,UAAU,EAAE,GACZ;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAEvC,MAAM,MAAM,kBAAkB,GAAG;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,CAAC;AA0EF,MAAM,MAAM,WAAW,GAAG;IACxB,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;CAChE,CAAC;AAEF,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,kBAAkB,GAAG,WAAW,CA6H1E"}