@langchain/langgraph-sdk 0.0.112 → 0.1.1
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/CHANGELOG.md +25 -0
- package/dist/auth/types.d.ts +1 -0
- package/dist/client.cjs +21 -8
- package/dist/client.d.ts +4 -0
- package/dist/client.js +21 -8
- package/dist/react/branching.cjs +149 -0
- package/dist/react/branching.d.ts +35 -0
- package/dist/react/branching.js +144 -0
- package/dist/react/errors.cjs +13 -0
- package/dist/react/errors.d.ts +12 -0
- package/dist/react/errors.js +9 -0
- package/dist/react/index.d.ts +2 -1
- package/dist/react/index.js +1 -1
- package/dist/react/manager.cjs +221 -0
- package/dist/react/manager.d.ts +86 -0
- package/dist/react/manager.js +217 -0
- package/dist/react/messages.cjs +69 -0
- package/dist/react/messages.d.ts +18 -0
- package/dist/react/messages.js +64 -0
- package/dist/react/stream.cjs +179 -436
- package/dist/react/stream.d.ts +1 -323
- package/dist/react/stream.js +178 -435
- package/dist/react/types.cjs +2 -0
- package/dist/react/types.d.ts +316 -0
- package/dist/react/types.js +1 -0
- package/dist/react/utils.cjs +14 -0
- package/dist/react/utils.d.ts +2 -0
- package/dist/react/utils.js +10 -0
- package/dist/react-ui/client.d.ts +1 -1
- package/package.json +1 -1
- package/dist/react/debug.cjs +0 -31
- package/dist/react/debug.d.ts +0 -23
- package/dist/react/debug.js +0 -28
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
# @langchain/langgraph-sdk
|
|
2
2
|
|
|
3
|
+
## 0.1.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 7de6680: Fix `onRequest` not being called when streaming runs or threads (#1585)
|
|
8
|
+
- df8b662: Fix interrupts not being exposed in `useStream["interrupt"]` when `fetchStateHistory: false`
|
|
9
|
+
- 572de43: feat(threads): add `ids` filter to Threads.search
|
|
10
|
+
|
|
11
|
+
- SDK: `ThreadsClient.search` now accepts `ids?: string[]` and forwards it to `/threads/search`.
|
|
12
|
+
- API: `/threads/search` schema accepts `ids` and storage filters by provided thread IDs.
|
|
13
|
+
|
|
14
|
+
This enables fetching a specific set of threads directly via the search endpoint, while remaining backward compatible.
|
|
15
|
+
|
|
16
|
+
## 0.1.0
|
|
17
|
+
|
|
18
|
+
### Minor Changes
|
|
19
|
+
|
|
20
|
+
- 35a0f1c: feat(sdk): set default limit of fetch history to 10
|
|
21
|
+
- 35a0f1c: feat(sdk): set default of `fetchStateHistory` to `false`
|
|
22
|
+
|
|
23
|
+
### Patch Changes
|
|
24
|
+
|
|
25
|
+
- 35a0f1c: chore(sdk): decouple stream manager from React
|
|
26
|
+
- 35a0f1c: fix(sdk): prevent partial history from hiding all values
|
|
27
|
+
|
|
3
28
|
## 0.0.112
|
|
4
29
|
|
|
5
30
|
### Patch Changes
|
package/dist/auth/types.d.ts
CHANGED
|
@@ -89,6 +89,7 @@ interface ThreadDelete {
|
|
|
89
89
|
*/
|
|
90
90
|
interface ThreadSearch {
|
|
91
91
|
thread_id?: Maybe<string>;
|
|
92
|
+
ids?: Maybe<string[]>;
|
|
92
93
|
status?: Maybe<"idle" | "busy" | "interrupted" | "error" | (string & {})>;
|
|
93
94
|
metadata?: Maybe<Record<string, unknown>>;
|
|
94
95
|
values?: Maybe<Record<string, unknown>>;
|
package/dist/client.cjs
CHANGED
|
@@ -563,6 +563,7 @@ class ThreadsClient extends BaseClient {
|
|
|
563
563
|
method: "POST",
|
|
564
564
|
json: {
|
|
565
565
|
metadata: query?.metadata ?? undefined,
|
|
566
|
+
ids: query?.ids ?? undefined,
|
|
566
567
|
limit: query?.limit ?? 10,
|
|
567
568
|
offset: query?.offset ?? 0,
|
|
568
569
|
status: query?.status,
|
|
@@ -671,7 +672,7 @@ class ThreadsClient extends BaseClient {
|
|
|
671
672
|
async *joinStream(threadId, options
|
|
672
673
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
673
674
|
) {
|
|
674
|
-
|
|
675
|
+
let [url, init] = this.prepareFetchOptions(`/threads/${threadId}/stream`, {
|
|
675
676
|
method: "GET",
|
|
676
677
|
headers: options?.lastEventId
|
|
677
678
|
? { "Last-Event-ID": options.lastEventId }
|
|
@@ -679,7 +680,10 @@ class ThreadsClient extends BaseClient {
|
|
|
679
680
|
params: options?.streamMode
|
|
680
681
|
? { stream_mode: options.streamMode }
|
|
681
682
|
: undefined,
|
|
682
|
-
})
|
|
683
|
+
});
|
|
684
|
+
if (this.onRequest != null)
|
|
685
|
+
init = await this.onRequest(url, init);
|
|
686
|
+
const response = await this.asyncCaller.fetch(url, init);
|
|
683
687
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
684
688
|
const stream = (response.body || new ReadableStream({ start: (ctrl) => ctrl.close() }))
|
|
685
689
|
.pipeThrough((0, sse_js_1.BytesLineDecoder)())
|
|
@@ -722,12 +726,15 @@ class RunsClient extends BaseClient {
|
|
|
722
726
|
durability: payload?.durability,
|
|
723
727
|
};
|
|
724
728
|
const endpoint = threadId == null ? `/runs/stream` : `/threads/${threadId}/runs/stream`;
|
|
725
|
-
|
|
729
|
+
let [url, init] = this.prepareFetchOptions(endpoint, {
|
|
726
730
|
method: "POST",
|
|
727
731
|
json,
|
|
728
732
|
timeoutMs: null,
|
|
729
733
|
signal: payload?.signal,
|
|
730
|
-
})
|
|
734
|
+
});
|
|
735
|
+
if (this.onRequest != null)
|
|
736
|
+
init = await this.onRequest(url, init);
|
|
737
|
+
const response = await this.asyncCaller.fetch(url, init);
|
|
731
738
|
const runMetadata = getRunMetadataFromResponse(response);
|
|
732
739
|
if (runMetadata)
|
|
733
740
|
payload?.onRunCreated?.(runMetadata);
|
|
@@ -939,7 +946,7 @@ class RunsClient extends BaseClient {
|
|
|
939
946
|
options instanceof AbortSignal
|
|
940
947
|
? { signal: options }
|
|
941
948
|
: options;
|
|
942
|
-
|
|
949
|
+
let [url, init] = this.prepareFetchOptions(threadId != null
|
|
943
950
|
? `/threads/${threadId}/runs/${runId}/stream`
|
|
944
951
|
: `/runs/${runId}/stream`, {
|
|
945
952
|
method: "GET",
|
|
@@ -952,7 +959,10 @@ class RunsClient extends BaseClient {
|
|
|
952
959
|
cancel_on_disconnect: opts?.cancelOnDisconnect ? "1" : "0",
|
|
953
960
|
stream_mode: opts?.streamMode,
|
|
954
961
|
},
|
|
955
|
-
})
|
|
962
|
+
});
|
|
963
|
+
if (this.onRequest != null)
|
|
964
|
+
init = await this.onRequest(url, init);
|
|
965
|
+
const response = await this.asyncCaller.fetch(url, init);
|
|
956
966
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
957
967
|
const stream = (response.body || new ReadableStream({ start: (ctrl) => ctrl.close() }))
|
|
958
968
|
.pipeThrough((0, sse_js_1.BytesLineDecoder)())
|
|
@@ -1172,14 +1182,17 @@ class UiClient extends BaseClient {
|
|
|
1172
1182
|
}
|
|
1173
1183
|
async getComponent(assistantId, agentName) {
|
|
1174
1184
|
return UiClient.getOrCached(`${this.apiUrl}-${assistantId}-${agentName}`, async () => {
|
|
1175
|
-
|
|
1185
|
+
let [url, init] = this.prepareFetchOptions(`/ui/${assistantId}`, {
|
|
1176
1186
|
headers: {
|
|
1177
1187
|
Accept: "text/html",
|
|
1178
1188
|
"Content-Type": "application/json",
|
|
1179
1189
|
},
|
|
1180
1190
|
method: "POST",
|
|
1181
1191
|
json: { name: agentName },
|
|
1182
|
-
})
|
|
1192
|
+
});
|
|
1193
|
+
if (this.onRequest != null)
|
|
1194
|
+
init = await this.onRequest(url, init);
|
|
1195
|
+
const response = await this.asyncCaller.fetch(url, init);
|
|
1183
1196
|
return response.text();
|
|
1184
1197
|
});
|
|
1185
1198
|
}
|
package/dist/client.d.ts
CHANGED
|
@@ -297,6 +297,10 @@ export declare class ThreadsClient<TStateType = DefaultValues, TUpdateType = TSt
|
|
|
297
297
|
* Metadata to filter threads by.
|
|
298
298
|
*/
|
|
299
299
|
metadata?: Metadata;
|
|
300
|
+
/**
|
|
301
|
+
* Filter by specific thread IDs.
|
|
302
|
+
*/
|
|
303
|
+
ids?: string[];
|
|
300
304
|
/**
|
|
301
305
|
* Maximum number of threads to return.
|
|
302
306
|
* Defaults to 10
|
package/dist/client.js
CHANGED
|
@@ -556,6 +556,7 @@ export class ThreadsClient extends BaseClient {
|
|
|
556
556
|
method: "POST",
|
|
557
557
|
json: {
|
|
558
558
|
metadata: query?.metadata ?? undefined,
|
|
559
|
+
ids: query?.ids ?? undefined,
|
|
559
560
|
limit: query?.limit ?? 10,
|
|
560
561
|
offset: query?.offset ?? 0,
|
|
561
562
|
status: query?.status,
|
|
@@ -664,7 +665,7 @@ export class ThreadsClient extends BaseClient {
|
|
|
664
665
|
async *joinStream(threadId, options
|
|
665
666
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
666
667
|
) {
|
|
667
|
-
|
|
668
|
+
let [url, init] = this.prepareFetchOptions(`/threads/${threadId}/stream`, {
|
|
668
669
|
method: "GET",
|
|
669
670
|
headers: options?.lastEventId
|
|
670
671
|
? { "Last-Event-ID": options.lastEventId }
|
|
@@ -672,7 +673,10 @@ export class ThreadsClient extends BaseClient {
|
|
|
672
673
|
params: options?.streamMode
|
|
673
674
|
? { stream_mode: options.streamMode }
|
|
674
675
|
: undefined,
|
|
675
|
-
})
|
|
676
|
+
});
|
|
677
|
+
if (this.onRequest != null)
|
|
678
|
+
init = await this.onRequest(url, init);
|
|
679
|
+
const response = await this.asyncCaller.fetch(url, init);
|
|
676
680
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
677
681
|
const stream = (response.body || new ReadableStream({ start: (ctrl) => ctrl.close() }))
|
|
678
682
|
.pipeThrough(BytesLineDecoder())
|
|
@@ -714,12 +718,15 @@ export class RunsClient extends BaseClient {
|
|
|
714
718
|
durability: payload?.durability,
|
|
715
719
|
};
|
|
716
720
|
const endpoint = threadId == null ? `/runs/stream` : `/threads/${threadId}/runs/stream`;
|
|
717
|
-
|
|
721
|
+
let [url, init] = this.prepareFetchOptions(endpoint, {
|
|
718
722
|
method: "POST",
|
|
719
723
|
json,
|
|
720
724
|
timeoutMs: null,
|
|
721
725
|
signal: payload?.signal,
|
|
722
|
-
})
|
|
726
|
+
});
|
|
727
|
+
if (this.onRequest != null)
|
|
728
|
+
init = await this.onRequest(url, init);
|
|
729
|
+
const response = await this.asyncCaller.fetch(url, init);
|
|
723
730
|
const runMetadata = getRunMetadataFromResponse(response);
|
|
724
731
|
if (runMetadata)
|
|
725
732
|
payload?.onRunCreated?.(runMetadata);
|
|
@@ -931,7 +938,7 @@ export class RunsClient extends BaseClient {
|
|
|
931
938
|
options instanceof AbortSignal
|
|
932
939
|
? { signal: options }
|
|
933
940
|
: options;
|
|
934
|
-
|
|
941
|
+
let [url, init] = this.prepareFetchOptions(threadId != null
|
|
935
942
|
? `/threads/${threadId}/runs/${runId}/stream`
|
|
936
943
|
: `/runs/${runId}/stream`, {
|
|
937
944
|
method: "GET",
|
|
@@ -944,7 +951,10 @@ export class RunsClient extends BaseClient {
|
|
|
944
951
|
cancel_on_disconnect: opts?.cancelOnDisconnect ? "1" : "0",
|
|
945
952
|
stream_mode: opts?.streamMode,
|
|
946
953
|
},
|
|
947
|
-
})
|
|
954
|
+
});
|
|
955
|
+
if (this.onRequest != null)
|
|
956
|
+
init = await this.onRequest(url, init);
|
|
957
|
+
const response = await this.asyncCaller.fetch(url, init);
|
|
948
958
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
949
959
|
const stream = (response.body || new ReadableStream({ start: (ctrl) => ctrl.close() }))
|
|
950
960
|
.pipeThrough(BytesLineDecoder())
|
|
@@ -1162,14 +1172,17 @@ class UiClient extends BaseClient {
|
|
|
1162
1172
|
}
|
|
1163
1173
|
async getComponent(assistantId, agentName) {
|
|
1164
1174
|
return UiClient.getOrCached(`${this.apiUrl}-${assistantId}-${agentName}`, async () => {
|
|
1165
|
-
|
|
1175
|
+
let [url, init] = this.prepareFetchOptions(`/ui/${assistantId}`, {
|
|
1166
1176
|
headers: {
|
|
1167
1177
|
Accept: "text/html",
|
|
1168
1178
|
"Content-Type": "application/json",
|
|
1169
1179
|
},
|
|
1170
1180
|
method: "POST",
|
|
1171
1181
|
json: { name: agentName },
|
|
1172
|
-
})
|
|
1182
|
+
});
|
|
1183
|
+
if (this.onRequest != null)
|
|
1184
|
+
init = await this.onRequest(url, init);
|
|
1185
|
+
const response = await this.asyncCaller.fetch(url, init);
|
|
1173
1186
|
return response.text();
|
|
1174
1187
|
});
|
|
1175
1188
|
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getBranchSequence = getBranchSequence;
|
|
4
|
+
exports.getBranchView = getBranchView;
|
|
5
|
+
exports.getBranchContext = getBranchContext;
|
|
6
|
+
function getBranchSequence(history) {
|
|
7
|
+
const nodeIds = new Set();
|
|
8
|
+
const childrenMap = {};
|
|
9
|
+
// Short circuit if there's only a singular one state
|
|
10
|
+
if (history.length <= 1) {
|
|
11
|
+
return {
|
|
12
|
+
rootSequence: {
|
|
13
|
+
type: "sequence",
|
|
14
|
+
items: history.map((value) => ({ type: "node", value, path: [] })),
|
|
15
|
+
},
|
|
16
|
+
paths: [],
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
// First pass - collect nodes for each checkpoint
|
|
20
|
+
history.forEach((state) => {
|
|
21
|
+
const checkpointId = state.parent_checkpoint?.checkpoint_id ?? "$";
|
|
22
|
+
childrenMap[checkpointId] ??= [];
|
|
23
|
+
childrenMap[checkpointId].push(state);
|
|
24
|
+
if (state.checkpoint?.checkpoint_id != null) {
|
|
25
|
+
nodeIds.add(state.checkpoint.checkpoint_id);
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
// If dealing with partial history, take the branch
|
|
29
|
+
// with the latest checkpoint and mark it as the root.
|
|
30
|
+
const maxId = (...ids) => ids
|
|
31
|
+
.filter((i) => i != null)
|
|
32
|
+
.sort((a, b) => a.localeCompare(b))
|
|
33
|
+
.at(-1);
|
|
34
|
+
const lastOrphanedNode = childrenMap.$ == null
|
|
35
|
+
? Object.keys(childrenMap)
|
|
36
|
+
.filter((parentId) => !nodeIds.has(parentId))
|
|
37
|
+
.map((parentId) => {
|
|
38
|
+
const queue = [parentId];
|
|
39
|
+
const seen = new Set();
|
|
40
|
+
let lastId = parentId;
|
|
41
|
+
while (queue.length > 0) {
|
|
42
|
+
const current = queue.shift();
|
|
43
|
+
if (seen.has(current))
|
|
44
|
+
continue;
|
|
45
|
+
seen.add(current);
|
|
46
|
+
const children = (childrenMap[current] ?? []).flatMap((i) => i.checkpoint?.checkpoint_id ?? []);
|
|
47
|
+
lastId = maxId(lastId, ...children);
|
|
48
|
+
queue.push(...children);
|
|
49
|
+
}
|
|
50
|
+
return { parentId, lastId };
|
|
51
|
+
})
|
|
52
|
+
.sort((a, b) => a.lastId.localeCompare(b.lastId))
|
|
53
|
+
.at(-1)?.parentId
|
|
54
|
+
: undefined;
|
|
55
|
+
if (lastOrphanedNode != null)
|
|
56
|
+
childrenMap.$ = childrenMap[lastOrphanedNode];
|
|
57
|
+
const rootSequence = { type: "sequence", items: [] };
|
|
58
|
+
const queue = [{ id: "$", sequence: rootSequence, path: [] }];
|
|
59
|
+
const paths = [];
|
|
60
|
+
const visited = new Set();
|
|
61
|
+
while (queue.length > 0) {
|
|
62
|
+
const task = queue.shift();
|
|
63
|
+
if (visited.has(task.id))
|
|
64
|
+
continue;
|
|
65
|
+
visited.add(task.id);
|
|
66
|
+
const children = childrenMap[task.id];
|
|
67
|
+
if (children == null || children.length === 0)
|
|
68
|
+
continue;
|
|
69
|
+
// If we've encountered a fork (2+ children), push the fork
|
|
70
|
+
// to the sequence and add a new sequence for each child
|
|
71
|
+
let fork;
|
|
72
|
+
if (children.length > 1) {
|
|
73
|
+
fork = { type: "fork", items: [] };
|
|
74
|
+
task.sequence.items.push(fork);
|
|
75
|
+
}
|
|
76
|
+
for (const value of children) {
|
|
77
|
+
const id = value.checkpoint?.checkpoint_id;
|
|
78
|
+
if (id == null)
|
|
79
|
+
continue;
|
|
80
|
+
let { sequence } = task;
|
|
81
|
+
let { path } = task;
|
|
82
|
+
if (fork != null) {
|
|
83
|
+
sequence = { type: "sequence", items: [] };
|
|
84
|
+
fork.items.unshift(sequence);
|
|
85
|
+
path = path.slice();
|
|
86
|
+
path.push(id);
|
|
87
|
+
paths.push(path);
|
|
88
|
+
}
|
|
89
|
+
sequence.items.push({ type: "node", value, path });
|
|
90
|
+
queue.push({ id, sequence, path });
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return { rootSequence, paths };
|
|
94
|
+
}
|
|
95
|
+
const PATH_SEP = ">";
|
|
96
|
+
const ROOT_ID = "$";
|
|
97
|
+
// Get flat view
|
|
98
|
+
function getBranchView(sequence, paths, branch) {
|
|
99
|
+
const path = branch.split(PATH_SEP);
|
|
100
|
+
const pathMap = {};
|
|
101
|
+
for (const path of paths) {
|
|
102
|
+
const parent = path.at(-2) ?? ROOT_ID;
|
|
103
|
+
pathMap[parent] ??= [];
|
|
104
|
+
pathMap[parent].unshift(path);
|
|
105
|
+
}
|
|
106
|
+
const history = [];
|
|
107
|
+
const branchByCheckpoint = {};
|
|
108
|
+
const forkStack = path.slice();
|
|
109
|
+
const queue = [...sequence.items];
|
|
110
|
+
while (queue.length > 0) {
|
|
111
|
+
const item = queue.shift();
|
|
112
|
+
if (item.type === "node") {
|
|
113
|
+
history.push(item.value);
|
|
114
|
+
const checkpointId = item.value.checkpoint?.checkpoint_id;
|
|
115
|
+
if (checkpointId == null)
|
|
116
|
+
continue;
|
|
117
|
+
branchByCheckpoint[checkpointId] = {
|
|
118
|
+
branch: item.path.join(PATH_SEP),
|
|
119
|
+
branchOptions: (item.path.length > 0
|
|
120
|
+
? pathMap[item.path.at(-2) ?? ROOT_ID] ?? []
|
|
121
|
+
: []).map((p) => p.join(PATH_SEP)),
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
if (item.type === "fork") {
|
|
125
|
+
const forkId = forkStack.shift();
|
|
126
|
+
const index = forkId != null
|
|
127
|
+
? item.items.findIndex((value) => {
|
|
128
|
+
const firstItem = value.items.at(0);
|
|
129
|
+
if (!firstItem || firstItem.type !== "node")
|
|
130
|
+
return false;
|
|
131
|
+
return firstItem.value.checkpoint?.checkpoint_id === forkId;
|
|
132
|
+
})
|
|
133
|
+
: -1;
|
|
134
|
+
const nextItems = item.items.at(index)?.items ?? [];
|
|
135
|
+
queue.push(...nextItems);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return { history, branchByCheckpoint };
|
|
139
|
+
}
|
|
140
|
+
function getBranchContext(branch, history) {
|
|
141
|
+
const { rootSequence: branchTree, paths } = getBranchSequence(history ?? []);
|
|
142
|
+
const { history: flatHistory, branchByCheckpoint } = getBranchView(branchTree, paths, branch);
|
|
143
|
+
return {
|
|
144
|
+
branchTree,
|
|
145
|
+
flatHistory,
|
|
146
|
+
branchByCheckpoint,
|
|
147
|
+
threadHead: flatHistory.at(-1),
|
|
148
|
+
};
|
|
149
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { ThreadState } from "../schema.js";
|
|
2
|
+
interface Node<StateType = any> {
|
|
3
|
+
type: "node";
|
|
4
|
+
value: ThreadState<StateType>;
|
|
5
|
+
path: string[];
|
|
6
|
+
}
|
|
7
|
+
interface Fork<StateType = any> {
|
|
8
|
+
type: "fork";
|
|
9
|
+
items: Array<Sequence<StateType>>;
|
|
10
|
+
}
|
|
11
|
+
export interface Sequence<StateType = any> {
|
|
12
|
+
type: "sequence";
|
|
13
|
+
items: Array<Node<StateType> | Fork<StateType>>;
|
|
14
|
+
}
|
|
15
|
+
export declare function getBranchSequence<StateType extends Record<string, unknown>>(history: ThreadState<StateType>[]): {
|
|
16
|
+
rootSequence: Sequence<any>;
|
|
17
|
+
paths: string[][];
|
|
18
|
+
};
|
|
19
|
+
export declare function getBranchView<StateType extends Record<string, unknown>>(sequence: Sequence<StateType>, paths: string[][], branch: string): {
|
|
20
|
+
history: ThreadState<StateType>[];
|
|
21
|
+
branchByCheckpoint: Record<string, {
|
|
22
|
+
branch: string | undefined;
|
|
23
|
+
branchOptions: string[] | undefined;
|
|
24
|
+
}>;
|
|
25
|
+
};
|
|
26
|
+
export declare function getBranchContext<StateType extends Record<string, unknown>>(branch: string, history: ThreadState<StateType>[] | undefined): {
|
|
27
|
+
branchTree: Sequence<any>;
|
|
28
|
+
flatHistory: ThreadState<any>[];
|
|
29
|
+
branchByCheckpoint: Record<string, {
|
|
30
|
+
branch: string | undefined;
|
|
31
|
+
branchOptions: string[] | undefined;
|
|
32
|
+
}>;
|
|
33
|
+
threadHead: ThreadState<any> | undefined;
|
|
34
|
+
};
|
|
35
|
+
export {};
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
export function getBranchSequence(history) {
|
|
2
|
+
const nodeIds = new Set();
|
|
3
|
+
const childrenMap = {};
|
|
4
|
+
// Short circuit if there's only a singular one state
|
|
5
|
+
if (history.length <= 1) {
|
|
6
|
+
return {
|
|
7
|
+
rootSequence: {
|
|
8
|
+
type: "sequence",
|
|
9
|
+
items: history.map((value) => ({ type: "node", value, path: [] })),
|
|
10
|
+
},
|
|
11
|
+
paths: [],
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
// First pass - collect nodes for each checkpoint
|
|
15
|
+
history.forEach((state) => {
|
|
16
|
+
const checkpointId = state.parent_checkpoint?.checkpoint_id ?? "$";
|
|
17
|
+
childrenMap[checkpointId] ??= [];
|
|
18
|
+
childrenMap[checkpointId].push(state);
|
|
19
|
+
if (state.checkpoint?.checkpoint_id != null) {
|
|
20
|
+
nodeIds.add(state.checkpoint.checkpoint_id);
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
// If dealing with partial history, take the branch
|
|
24
|
+
// with the latest checkpoint and mark it as the root.
|
|
25
|
+
const maxId = (...ids) => ids
|
|
26
|
+
.filter((i) => i != null)
|
|
27
|
+
.sort((a, b) => a.localeCompare(b))
|
|
28
|
+
.at(-1);
|
|
29
|
+
const lastOrphanedNode = childrenMap.$ == null
|
|
30
|
+
? Object.keys(childrenMap)
|
|
31
|
+
.filter((parentId) => !nodeIds.has(parentId))
|
|
32
|
+
.map((parentId) => {
|
|
33
|
+
const queue = [parentId];
|
|
34
|
+
const seen = new Set();
|
|
35
|
+
let lastId = parentId;
|
|
36
|
+
while (queue.length > 0) {
|
|
37
|
+
const current = queue.shift();
|
|
38
|
+
if (seen.has(current))
|
|
39
|
+
continue;
|
|
40
|
+
seen.add(current);
|
|
41
|
+
const children = (childrenMap[current] ?? []).flatMap((i) => i.checkpoint?.checkpoint_id ?? []);
|
|
42
|
+
lastId = maxId(lastId, ...children);
|
|
43
|
+
queue.push(...children);
|
|
44
|
+
}
|
|
45
|
+
return { parentId, lastId };
|
|
46
|
+
})
|
|
47
|
+
.sort((a, b) => a.lastId.localeCompare(b.lastId))
|
|
48
|
+
.at(-1)?.parentId
|
|
49
|
+
: undefined;
|
|
50
|
+
if (lastOrphanedNode != null)
|
|
51
|
+
childrenMap.$ = childrenMap[lastOrphanedNode];
|
|
52
|
+
const rootSequence = { type: "sequence", items: [] };
|
|
53
|
+
const queue = [{ id: "$", sequence: rootSequence, path: [] }];
|
|
54
|
+
const paths = [];
|
|
55
|
+
const visited = new Set();
|
|
56
|
+
while (queue.length > 0) {
|
|
57
|
+
const task = queue.shift();
|
|
58
|
+
if (visited.has(task.id))
|
|
59
|
+
continue;
|
|
60
|
+
visited.add(task.id);
|
|
61
|
+
const children = childrenMap[task.id];
|
|
62
|
+
if (children == null || children.length === 0)
|
|
63
|
+
continue;
|
|
64
|
+
// If we've encountered a fork (2+ children), push the fork
|
|
65
|
+
// to the sequence and add a new sequence for each child
|
|
66
|
+
let fork;
|
|
67
|
+
if (children.length > 1) {
|
|
68
|
+
fork = { type: "fork", items: [] };
|
|
69
|
+
task.sequence.items.push(fork);
|
|
70
|
+
}
|
|
71
|
+
for (const value of children) {
|
|
72
|
+
const id = value.checkpoint?.checkpoint_id;
|
|
73
|
+
if (id == null)
|
|
74
|
+
continue;
|
|
75
|
+
let { sequence } = task;
|
|
76
|
+
let { path } = task;
|
|
77
|
+
if (fork != null) {
|
|
78
|
+
sequence = { type: "sequence", items: [] };
|
|
79
|
+
fork.items.unshift(sequence);
|
|
80
|
+
path = path.slice();
|
|
81
|
+
path.push(id);
|
|
82
|
+
paths.push(path);
|
|
83
|
+
}
|
|
84
|
+
sequence.items.push({ type: "node", value, path });
|
|
85
|
+
queue.push({ id, sequence, path });
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return { rootSequence, paths };
|
|
89
|
+
}
|
|
90
|
+
const PATH_SEP = ">";
|
|
91
|
+
const ROOT_ID = "$";
|
|
92
|
+
// Get flat view
|
|
93
|
+
export function getBranchView(sequence, paths, branch) {
|
|
94
|
+
const path = branch.split(PATH_SEP);
|
|
95
|
+
const pathMap = {};
|
|
96
|
+
for (const path of paths) {
|
|
97
|
+
const parent = path.at(-2) ?? ROOT_ID;
|
|
98
|
+
pathMap[parent] ??= [];
|
|
99
|
+
pathMap[parent].unshift(path);
|
|
100
|
+
}
|
|
101
|
+
const history = [];
|
|
102
|
+
const branchByCheckpoint = {};
|
|
103
|
+
const forkStack = path.slice();
|
|
104
|
+
const queue = [...sequence.items];
|
|
105
|
+
while (queue.length > 0) {
|
|
106
|
+
const item = queue.shift();
|
|
107
|
+
if (item.type === "node") {
|
|
108
|
+
history.push(item.value);
|
|
109
|
+
const checkpointId = item.value.checkpoint?.checkpoint_id;
|
|
110
|
+
if (checkpointId == null)
|
|
111
|
+
continue;
|
|
112
|
+
branchByCheckpoint[checkpointId] = {
|
|
113
|
+
branch: item.path.join(PATH_SEP),
|
|
114
|
+
branchOptions: (item.path.length > 0
|
|
115
|
+
? pathMap[item.path.at(-2) ?? ROOT_ID] ?? []
|
|
116
|
+
: []).map((p) => p.join(PATH_SEP)),
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
if (item.type === "fork") {
|
|
120
|
+
const forkId = forkStack.shift();
|
|
121
|
+
const index = forkId != null
|
|
122
|
+
? item.items.findIndex((value) => {
|
|
123
|
+
const firstItem = value.items.at(0);
|
|
124
|
+
if (!firstItem || firstItem.type !== "node")
|
|
125
|
+
return false;
|
|
126
|
+
return firstItem.value.checkpoint?.checkpoint_id === forkId;
|
|
127
|
+
})
|
|
128
|
+
: -1;
|
|
129
|
+
const nextItems = item.items.at(index)?.items ?? [];
|
|
130
|
+
queue.push(...nextItems);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return { history, branchByCheckpoint };
|
|
134
|
+
}
|
|
135
|
+
export function getBranchContext(branch, history) {
|
|
136
|
+
const { rootSequence: branchTree, paths } = getBranchSequence(history ?? []);
|
|
137
|
+
const { history: flatHistory, branchByCheckpoint } = getBranchView(branchTree, paths, branch);
|
|
138
|
+
return {
|
|
139
|
+
branchTree,
|
|
140
|
+
flatHistory,
|
|
141
|
+
branchByCheckpoint,
|
|
142
|
+
threadHead: flatHistory.at(-1),
|
|
143
|
+
};
|
|
144
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.StreamError = void 0;
|
|
4
|
+
class StreamError extends Error {
|
|
5
|
+
constructor(data) {
|
|
6
|
+
super(data.message);
|
|
7
|
+
this.name = data.name ?? data.error ?? "StreamError";
|
|
8
|
+
}
|
|
9
|
+
static isStructuredError(error) {
|
|
10
|
+
return typeof error === "object" && error != null && "message" in error;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
exports.StreamError = StreamError;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export class StreamError extends Error {
|
|
2
|
+
constructor(data) {
|
|
3
|
+
super(data.message);
|
|
4
|
+
this.name = data.name ?? data.error ?? "StreamError";
|
|
5
|
+
}
|
|
6
|
+
static isStructuredError(error) {
|
|
7
|
+
return typeof error === "object" && error != null && "message" in error;
|
|
8
|
+
}
|
|
9
|
+
}
|
package/dist/react/index.d.ts
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
export { useStream
|
|
1
|
+
export { useStream } from "./stream.js";
|
|
2
|
+
export type { MessageMetadata, UseStream, UseStreamOptions } from "./types.js";
|
package/dist/react/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { useStream
|
|
1
|
+
export { useStream } from "./stream.js";
|