@langchain/langgraph-sdk 0.0.111 → 0.1.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/CHANGELOG.md +20 -0
- package/dist/client.cjs +23 -0
- package/dist/client.d.ts +9 -1
- package/dist/client.js +23 -0
- 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 +219 -0
- package/dist/react/manager.d.ts +86 -0
- package/dist/react/manager.js +215 -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 +174 -437
- package/dist/react/stream.d.ts +1 -315
- package/dist/react/stream.js +173 -436
- 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/dist/types.d.ts +10 -0
- package/dist/types.stream.d.ts +1 -0
- 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,25 @@
|
|
|
1
1
|
# @langchain/langgraph-sdk
|
|
2
2
|
|
|
3
|
+
## 0.1.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 35a0f1c: feat(sdk): set default limit of fetch history to 10
|
|
8
|
+
- 35a0f1c: feat(sdk): set default of `fetchStateHistory` to `false`
|
|
9
|
+
|
|
10
|
+
### Patch Changes
|
|
11
|
+
|
|
12
|
+
- 35a0f1c: chore(sdk): decouple stream manager from React
|
|
13
|
+
- 35a0f1c: fix(sdk): prevent partial history from hiding all values
|
|
14
|
+
|
|
15
|
+
## 0.0.112
|
|
16
|
+
|
|
17
|
+
### Patch Changes
|
|
18
|
+
|
|
19
|
+
- a50e02e: feat(sdk): add thread streaming endpoint
|
|
20
|
+
- 7e210a1: feat(sdk): add durability param to run methods
|
|
21
|
+
- 5766b62: Fix `isThreadLoading: false` when initially mounting in useStream
|
|
22
|
+
|
|
3
23
|
## 0.0.111
|
|
4
24
|
|
|
5
25
|
### Patch Changes
|
package/dist/client.cjs
CHANGED
|
@@ -244,6 +244,7 @@ class CronsClient extends BaseClient {
|
|
|
244
244
|
multitask_strategy: payload?.multitaskStrategy,
|
|
245
245
|
if_not_exists: payload?.ifNotExists,
|
|
246
246
|
checkpoint_during: payload?.checkpointDuring,
|
|
247
|
+
durability: payload?.durability,
|
|
247
248
|
};
|
|
248
249
|
return this.fetch(`/threads/${threadId}/runs/crons`, {
|
|
249
250
|
method: "POST",
|
|
@@ -270,6 +271,7 @@ class CronsClient extends BaseClient {
|
|
|
270
271
|
multitask_strategy: payload?.multitaskStrategy,
|
|
271
272
|
if_not_exists: payload?.ifNotExists,
|
|
272
273
|
checkpoint_during: payload?.checkpointDuring,
|
|
274
|
+
durability: payload?.durability,
|
|
273
275
|
};
|
|
274
276
|
return this.fetch(`/runs/crons`, {
|
|
275
277
|
method: "POST",
|
|
@@ -666,6 +668,24 @@ class ThreadsClient extends BaseClient {
|
|
|
666
668
|
},
|
|
667
669
|
});
|
|
668
670
|
}
|
|
671
|
+
async *joinStream(threadId, options
|
|
672
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
673
|
+
) {
|
|
674
|
+
const response = await this.asyncCaller.fetch(...this.prepareFetchOptions(`/threads/${threadId}/stream`, {
|
|
675
|
+
method: "GET",
|
|
676
|
+
headers: options?.lastEventId
|
|
677
|
+
? { "Last-Event-ID": options.lastEventId }
|
|
678
|
+
: undefined,
|
|
679
|
+
params: options?.streamMode
|
|
680
|
+
? { stream_mode: options.streamMode }
|
|
681
|
+
: undefined,
|
|
682
|
+
}));
|
|
683
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
684
|
+
const stream = (response.body || new ReadableStream({ start: (ctrl) => ctrl.close() }))
|
|
685
|
+
.pipeThrough((0, sse_js_1.BytesLineDecoder)())
|
|
686
|
+
.pipeThrough((0, sse_js_1.SSEDecoder)());
|
|
687
|
+
yield* stream_js_1.IterableReadableStream.fromReadableStream(stream);
|
|
688
|
+
}
|
|
669
689
|
}
|
|
670
690
|
exports.ThreadsClient = ThreadsClient;
|
|
671
691
|
class RunsClient extends BaseClient {
|
|
@@ -699,6 +719,7 @@ class RunsClient extends BaseClient {
|
|
|
699
719
|
after_seconds: payload?.afterSeconds,
|
|
700
720
|
if_not_exists: payload?.ifNotExists,
|
|
701
721
|
checkpoint_during: payload?.checkpointDuring,
|
|
722
|
+
durability: payload?.durability,
|
|
702
723
|
};
|
|
703
724
|
const endpoint = threadId == null ? `/runs/stream` : `/threads/${threadId}/runs/stream`;
|
|
704
725
|
const response = await this.asyncCaller.fetch(...this.prepareFetchOptions(endpoint, {
|
|
@@ -744,6 +765,7 @@ class RunsClient extends BaseClient {
|
|
|
744
765
|
after_seconds: payload?.afterSeconds,
|
|
745
766
|
if_not_exists: payload?.ifNotExists,
|
|
746
767
|
checkpoint_during: payload?.checkpointDuring,
|
|
768
|
+
durability: payload?.durability,
|
|
747
769
|
langsmith_tracer: payload?._langsmithTracer
|
|
748
770
|
? {
|
|
749
771
|
project_name: payload?._langsmithTracer?.projectName,
|
|
@@ -806,6 +828,7 @@ class RunsClient extends BaseClient {
|
|
|
806
828
|
after_seconds: payload?.afterSeconds,
|
|
807
829
|
if_not_exists: payload?.ifNotExists,
|
|
808
830
|
checkpoint_during: payload?.checkpointDuring,
|
|
831
|
+
durability: payload?.durability,
|
|
809
832
|
langsmith_tracer: payload?._langsmithTracer
|
|
810
833
|
? {
|
|
811
834
|
project_name: payload?._langsmithTracer?.projectName,
|
package/dist/client.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Assistant, AssistantGraph, AssistantSortBy, AssistantSelectField, AssistantVersion, CancelAction, Checkpoint, Config, Cron, CronSelectField, CronCreateForThreadResponse, CronCreateResponse, CronSortBy, DefaultValues, GraphSchema, Item, ListNamespaceResponse, Metadata, Run, RunSelectField, RunStatus, SearchItemsResponse, SortOrder, Subgraphs, Thread, ThreadSelectField, ThreadSortBy, ThreadState, ThreadStatus } from "./schema.js";
|
|
2
2
|
import type { Command, CronsCreatePayload, OnConflictBehavior, RunsCreatePayload, RunsStreamPayload, RunsWaitPayload, StreamEvent } from "./types.js";
|
|
3
|
-
import type { StreamMode, TypedAsyncGenerator } from "./types.stream.js";
|
|
3
|
+
import type { StreamMode, ThreadStreamMode, TypedAsyncGenerator } from "./types.stream.js";
|
|
4
4
|
import { AsyncCaller, AsyncCallerParams } from "./utils/async_caller.js";
|
|
5
5
|
type HeaderValue = string | undefined | null;
|
|
6
6
|
/**
|
|
@@ -375,6 +375,14 @@ export declare class ThreadsClient<TStateType = DefaultValues, TUpdateType = TSt
|
|
|
375
375
|
checkpoint?: Partial<Omit<Checkpoint, "thread_id">>;
|
|
376
376
|
metadata?: Metadata;
|
|
377
377
|
}): Promise<ThreadState<ValuesType>[]>;
|
|
378
|
+
joinStream(threadId: string, options?: {
|
|
379
|
+
lastEventId?: string;
|
|
380
|
+
streamMode?: ThreadStreamMode | ThreadStreamMode[];
|
|
381
|
+
}): AsyncGenerator<{
|
|
382
|
+
id?: string;
|
|
383
|
+
event: StreamEvent;
|
|
384
|
+
data: any;
|
|
385
|
+
}>;
|
|
378
386
|
}
|
|
379
387
|
export declare class RunsClient<TStateType = DefaultValues, TUpdateType = TStateType, TCustomEventType = unknown> extends BaseClient {
|
|
380
388
|
stream<TStreamMode extends StreamMode | StreamMode[] = StreamMode, TSubgraphs extends boolean = false>(threadId: null, assistantId: string, payload?: Omit<RunsStreamPayload<TStreamMode, TSubgraphs>, "multitaskStrategy" | "onCompletion">): TypedAsyncGenerator<TStreamMode, TSubgraphs, TStateType, TUpdateType, TCustomEventType>;
|
package/dist/client.js
CHANGED
|
@@ -239,6 +239,7 @@ export class CronsClient extends BaseClient {
|
|
|
239
239
|
multitask_strategy: payload?.multitaskStrategy,
|
|
240
240
|
if_not_exists: payload?.ifNotExists,
|
|
241
241
|
checkpoint_during: payload?.checkpointDuring,
|
|
242
|
+
durability: payload?.durability,
|
|
242
243
|
};
|
|
243
244
|
return this.fetch(`/threads/${threadId}/runs/crons`, {
|
|
244
245
|
method: "POST",
|
|
@@ -265,6 +266,7 @@ export class CronsClient extends BaseClient {
|
|
|
265
266
|
multitask_strategy: payload?.multitaskStrategy,
|
|
266
267
|
if_not_exists: payload?.ifNotExists,
|
|
267
268
|
checkpoint_during: payload?.checkpointDuring,
|
|
269
|
+
durability: payload?.durability,
|
|
268
270
|
};
|
|
269
271
|
return this.fetch(`/runs/crons`, {
|
|
270
272
|
method: "POST",
|
|
@@ -659,6 +661,24 @@ export class ThreadsClient extends BaseClient {
|
|
|
659
661
|
},
|
|
660
662
|
});
|
|
661
663
|
}
|
|
664
|
+
async *joinStream(threadId, options
|
|
665
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
666
|
+
) {
|
|
667
|
+
const response = await this.asyncCaller.fetch(...this.prepareFetchOptions(`/threads/${threadId}/stream`, {
|
|
668
|
+
method: "GET",
|
|
669
|
+
headers: options?.lastEventId
|
|
670
|
+
? { "Last-Event-ID": options.lastEventId }
|
|
671
|
+
: undefined,
|
|
672
|
+
params: options?.streamMode
|
|
673
|
+
? { stream_mode: options.streamMode }
|
|
674
|
+
: undefined,
|
|
675
|
+
}));
|
|
676
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
677
|
+
const stream = (response.body || new ReadableStream({ start: (ctrl) => ctrl.close() }))
|
|
678
|
+
.pipeThrough(BytesLineDecoder())
|
|
679
|
+
.pipeThrough(SSEDecoder());
|
|
680
|
+
yield* IterableReadableStream.fromReadableStream(stream);
|
|
681
|
+
}
|
|
662
682
|
}
|
|
663
683
|
export class RunsClient extends BaseClient {
|
|
664
684
|
/**
|
|
@@ -691,6 +711,7 @@ export class RunsClient extends BaseClient {
|
|
|
691
711
|
after_seconds: payload?.afterSeconds,
|
|
692
712
|
if_not_exists: payload?.ifNotExists,
|
|
693
713
|
checkpoint_during: payload?.checkpointDuring,
|
|
714
|
+
durability: payload?.durability,
|
|
694
715
|
};
|
|
695
716
|
const endpoint = threadId == null ? `/runs/stream` : `/threads/${threadId}/runs/stream`;
|
|
696
717
|
const response = await this.asyncCaller.fetch(...this.prepareFetchOptions(endpoint, {
|
|
@@ -736,6 +757,7 @@ export class RunsClient extends BaseClient {
|
|
|
736
757
|
after_seconds: payload?.afterSeconds,
|
|
737
758
|
if_not_exists: payload?.ifNotExists,
|
|
738
759
|
checkpoint_during: payload?.checkpointDuring,
|
|
760
|
+
durability: payload?.durability,
|
|
739
761
|
langsmith_tracer: payload?._langsmithTracer
|
|
740
762
|
? {
|
|
741
763
|
project_name: payload?._langsmithTracer?.projectName,
|
|
@@ -798,6 +820,7 @@ export class RunsClient extends BaseClient {
|
|
|
798
820
|
after_seconds: payload?.afterSeconds,
|
|
799
821
|
if_not_exists: payload?.ifNotExists,
|
|
800
822
|
checkpoint_during: payload?.checkpointDuring,
|
|
823
|
+
durability: payload?.durability,
|
|
801
824
|
langsmith_tracer: payload?._langsmithTracer
|
|
802
825
|
? {
|
|
803
826
|
project_name: payload?._langsmithTracer?.projectName,
|
|
@@ -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";
|