assistant-stream 0.1.8 → 0.2.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/ai-sdk/package.json +2 -2
- package/dist/ai-sdk/index.js +7 -34
- package/dist/ai-sdk/index.js.map +1 -1
- package/dist/ai-sdk/language-model.js +5 -31
- package/dist/ai-sdk/language-model.js.map +1 -1
- package/dist/ai-sdk.js +4 -32
- package/dist/ai-sdk.js.map +1 -1
- package/dist/core/AssistantStream.js +3 -29
- package/dist/core/AssistantStream.js.map +1 -1
- package/dist/core/AssistantStreamChunk.js +0 -18
- package/dist/core/AssistantStreamChunk.js.map +1 -1
- package/dist/core/accumulators/AssistantMessageStream.js +9 -35
- package/dist/core/accumulators/AssistantMessageStream.js.map +1 -1
- package/dist/core/accumulators/assistant-message-accumulator.js +22 -48
- package/dist/core/accumulators/assistant-message-accumulator.js.map +1 -1
- package/dist/core/index.js +16 -46
- package/dist/core/index.js.map +1 -1
- package/dist/core/modules/assistant-stream.js +29 -52
- package/dist/core/modules/assistant-stream.js.map +1 -1
- package/dist/core/modules/text.js +6 -33
- package/dist/core/modules/text.js.map +1 -1
- package/dist/core/modules/tool-call.js +8 -35
- package/dist/core/modules/tool-call.js.map +1 -1
- package/dist/core/serialization/PlainText.js +9 -36
- package/dist/core/serialization/PlainText.js.map +1 -1
- package/dist/core/serialization/data-stream/DataStream.js +57 -79
- package/dist/core/serialization/data-stream/DataStream.js.map +1 -1
- package/dist/core/serialization/data-stream/chunk-types.js +2 -28
- package/dist/core/serialization/data-stream/chunk-types.js.map +1 -1
- package/dist/core/serialization/data-stream/serialization.js +6 -33
- package/dist/core/serialization/data-stream/serialization.js.map +1 -1
- package/dist/core/tool/ToolCallReader.js +30 -55
- package/dist/core/tool/ToolCallReader.js.map +1 -1
- package/dist/core/tool/ToolExecutionStream.js +18 -52
- package/dist/core/tool/ToolExecutionStream.js.map +1 -1
- package/dist/core/tool/ToolResponse.js +5 -31
- package/dist/core/tool/ToolResponse.js.map +1 -1
- package/dist/core/tool/index.js +9 -35
- package/dist/core/tool/index.js.map +1 -1
- package/dist/core/tool/tool-types.js +0 -18
- package/dist/core/tool/tool-types.js.map +1 -1
- package/dist/core/tool/toolResultStream.js +8 -35
- package/dist/core/tool/toolResultStream.js.map +1 -1
- package/dist/core/tool/type-path-utils.js +0 -18
- package/dist/core/tool/type-path-utils.js.map +1 -1
- package/dist/core/utils/Counter.js +4 -30
- package/dist/core/utils/Counter.js.map +1 -1
- package/dist/core/utils/generateId.js +4 -30
- package/dist/core/utils/generateId.js.map +1 -1
- package/dist/core/utils/stream/AssistantMetaTransformStream.js +4 -30
- package/dist/core/utils/stream/AssistantMetaTransformStream.js.map +1 -1
- package/dist/core/utils/stream/AssistantTransformStream.js +8 -32
- package/dist/core/utils/stream/AssistantTransformStream.js.map +1 -1
- package/dist/core/utils/stream/LineDecoderStream.js +4 -30
- package/dist/core/utils/stream/LineDecoderStream.js.map +1 -1
- package/dist/core/utils/stream/PipeableTransformStream.js +4 -30
- package/dist/core/utils/stream/PipeableTransformStream.js.map +1 -1
- package/dist/core/utils/stream/UnderlyingReadable.js +0 -18
- package/dist/core/utils/stream/UnderlyingReadable.js.map +1 -1
- package/dist/core/utils/stream/merge.js +5 -31
- package/dist/core/utils/stream/merge.js.map +1 -1
- package/dist/core/utils/stream/path-utils.js +10 -38
- package/dist/core/utils/stream/path-utils.js.map +1 -1
- package/dist/core/utils/types.js +0 -18
- package/dist/core/utils/types.js.map +1 -1
- package/dist/core/utils/withPromiseOrValue.js +2 -28
- package/dist/core/utils/withPromiseOrValue.js.map +1 -1
- package/dist/index.js +1 -24
- package/dist/index.js.map +1 -1
- package/dist/utils/AsyncIterableStream.js +2 -28
- package/dist/utils/AsyncIterableStream.js.map +1 -1
- package/dist/utils/json/fix-json.js +2 -28
- package/dist/utils/json/fix-json.js.map +1 -1
- package/dist/utils/json/is-json.js +2 -30
- package/dist/utils/json/is-json.js.map +1 -1
- package/dist/utils/json/json-value.js +0 -18
- package/dist/utils/json/json-value.js.map +1 -1
- package/dist/utils/json/parse-partial-json-object.js +12 -50
- package/dist/utils/json/parse-partial-json-object.js.map +1 -1
- package/dist/utils/promiseWithResolvers.js +3 -29
- package/dist/utils/promiseWithResolvers.js.map +1 -1
- package/dist/utils.js +9 -32
- package/dist/utils.js.map +1 -1
- package/package.json +7 -9
- package/{dist/ai-sdk/index.mjs → src/ai-sdk/index.ts} +64 -33
- package/{dist/ai-sdk/language-model.mjs → src/ai-sdk/language-model.ts} +35 -19
- package/src/ai-sdk.ts +2 -0
- package/src/core/AssistantStream.ts +39 -0
- package/src/core/AssistantStreamChunk.ts +93 -0
- package/src/core/accumulators/AssistantMessageStream.ts +56 -0
- package/{dist/core/accumulators/assistant-message-accumulator.mjs → src/core/accumulators/assistant-message-accumulator.ts} +152 -67
- package/src/core/index.ts +17 -0
- package/{dist/core/modules/assistant-stream.mjs → src/core/modules/assistant-stream.ts} +122 -60
- package/src/core/modules/text.ts +64 -0
- package/{dist/core/modules/tool-call.mjs → src/core/modules/tool-call.ts} +48 -27
- package/src/core/serialization/PlainText.ts +68 -0
- package/{dist/core/serialization/data-stream/DataStream.mjs → src/core/serialization/data-stream/DataStream.ts} +111 -67
- package/src/core/serialization/data-stream/chunk-types.ts +93 -0
- package/src/core/serialization/data-stream/serialization.ts +32 -0
- package/src/core/tool/ToolCallReader.ts +432 -0
- package/{dist/core/tool/ToolExecutionStream.mjs → src/core/tool/ToolExecutionStream.ts} +83 -35
- package/src/core/tool/ToolResponse.ts +31 -0
- package/src/core/tool/index.ts +8 -0
- package/src/core/tool/tool-types.ts +107 -0
- package/src/core/tool/toolResultStream.ts +119 -0
- package/src/core/tool/type-path-utils.ts +36 -0
- package/src/core/utils/Counter.ts +7 -0
- package/src/core/utils/generateId.tsx +6 -0
- package/src/core/utils/stream/AssistantMetaTransformStream.ts +74 -0
- package/src/core/utils/stream/AssistantTransformStream.ts +74 -0
- package/{dist/core/utils/stream/LineDecoderStream.mjs → src/core/utils/stream/LineDecoderStream.ts} +12 -10
- package/src/core/utils/stream/PipeableTransformStream.ts +10 -0
- package/src/core/utils/stream/UnderlyingReadable.ts +5 -0
- package/src/core/utils/stream/merge.ts +212 -0
- package/src/core/utils/stream/path-utils.ts +71 -0
- package/src/core/utils/types.ts +150 -0
- package/src/core/utils/withPromiseOrValue.ts +20 -0
- package/src/index.ts +1 -0
- package/src/utils/AsyncIterableStream.ts +24 -0
- package/{dist/utils/json/fix-json.mjs → src/utils/json/fix-json.ts} +146 -20
- package/src/utils/json/is-json.ts +43 -0
- package/src/utils/json/json-value.ts +13 -0
- package/src/utils/json/parse-partial-json-object.test.ts +225 -0
- package/src/utils/json/parse-partial-json-object.ts +103 -0
- package/src/utils/promiseWithResolvers.ts +10 -0
- package/src/utils.ts +13 -0
- package/dist/ai-sdk/index.mjs.map +0 -1
- package/dist/ai-sdk/language-model.mjs.map +0 -1
- package/dist/ai-sdk.mjs +0 -7
- package/dist/ai-sdk.mjs.map +0 -1
- package/dist/core/AssistantStream.mjs +0 -21
- package/dist/core/AssistantStream.mjs.map +0 -1
- package/dist/core/AssistantStreamChunk.mjs +0 -1
- package/dist/core/AssistantStreamChunk.mjs.map +0 -1
- package/dist/core/accumulators/AssistantMessageStream.mjs +0 -54
- package/dist/core/accumulators/AssistantMessageStream.mjs.map +0 -1
- package/dist/core/accumulators/assistant-message-accumulator.mjs.map +0 -1
- package/dist/core/index.mjs +0 -26
- package/dist/core/index.mjs.map +0 -1
- package/dist/core/modules/assistant-stream.mjs.map +0 -1
- package/dist/core/modules/text.mjs +0 -52
- package/dist/core/modules/text.mjs.map +0 -1
- package/dist/core/modules/tool-call.mjs.map +0 -1
- package/dist/core/serialization/PlainText.mjs +0 -44
- package/dist/core/serialization/PlainText.mjs.map +0 -1
- package/dist/core/serialization/data-stream/DataStream.mjs.map +0 -1
- package/dist/core/serialization/data-stream/chunk-types.mjs +0 -24
- package/dist/core/serialization/data-stream/chunk-types.mjs.map +0 -1
- package/dist/core/serialization/data-stream/serialization.mjs +0 -30
- package/dist/core/serialization/data-stream/serialization.mjs.map +0 -1
- package/dist/core/tool/ToolCallReader.mjs +0 -315
- package/dist/core/tool/ToolCallReader.mjs.map +0 -1
- package/dist/core/tool/ToolExecutionStream.mjs.map +0 -1
- package/dist/core/tool/ToolResponse.mjs +0 -22
- package/dist/core/tool/ToolResponse.mjs.map +0 -1
- package/dist/core/tool/index.mjs +0 -14
- package/dist/core/tool/index.mjs.map +0 -1
- package/dist/core/tool/tool-types.mjs +0 -1
- package/dist/core/tool/tool-types.mjs.map +0 -1
- package/dist/core/tool/toolResultStream.mjs +0 -78
- package/dist/core/tool/toolResultStream.mjs.map +0 -1
- package/dist/core/tool/type-path-utils.mjs +0 -1
- package/dist/core/tool/type-path-utils.mjs.map +0 -1
- package/dist/core/utils/Counter.mjs +0 -11
- package/dist/core/utils/Counter.mjs.map +0 -1
- package/dist/core/utils/generateId.mjs +0 -10
- package/dist/core/utils/generateId.mjs.map +0 -1
- package/dist/core/utils/stream/AssistantMetaTransformStream.mjs +0 -44
- package/dist/core/utils/stream/AssistantMetaTransformStream.mjs.map +0 -1
- package/dist/core/utils/stream/AssistantTransformStream.mjs +0 -46
- package/dist/core/utils/stream/AssistantTransformStream.mjs.map +0 -1
- package/dist/core/utils/stream/LineDecoderStream.mjs.map +0 -1
- package/dist/core/utils/stream/PipeableTransformStream.mjs +0 -15
- package/dist/core/utils/stream/PipeableTransformStream.mjs.map +0 -1
- package/dist/core/utils/stream/UnderlyingReadable.mjs +0 -1
- package/dist/core/utils/stream/UnderlyingReadable.mjs.map +0 -1
- package/dist/core/utils/stream/merge.mjs +0 -85
- package/dist/core/utils/stream/merge.mjs.map +0 -1
- package/dist/core/utils/stream/path-utils.mjs +0 -61
- package/dist/core/utils/stream/path-utils.mjs.map +0 -1
- package/dist/core/utils/types.mjs +0 -1
- package/dist/core/utils/types.mjs.map +0 -1
- package/dist/core/utils/withPromiseOrValue.mjs +0 -17
- package/dist/core/utils/withPromiseOrValue.mjs.map +0 -1
- package/dist/index.mjs +0 -3
- package/dist/index.mjs.map +0 -1
- package/dist/utils/AsyncIterableStream.mjs +0 -21
- package/dist/utils/AsyncIterableStream.mjs.map +0 -1
- package/dist/utils/json/fix-json.mjs.map +0 -1
- package/dist/utils/json/is-json.mjs +0 -29
- package/dist/utils/json/is-json.mjs.map +0 -1
- package/dist/utils/json/json-value.mjs +0 -1
- package/dist/utils/json/json-value.mjs.map +0 -1
- package/dist/utils/json/parse-partial-json-object.mjs +0 -65
- package/dist/utils/json/parse-partial-json-object.mjs.map +0 -1
- package/dist/utils/promiseWithResolvers.mjs +0 -15
- package/dist/utils/promiseWithResolvers.mjs.map +0 -1
- package/dist/utils.mjs +0 -14
- package/dist/utils.mjs.map +0 -1
- package/utils/README.md +0 -1
- package/utils/package.json +0 -5
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
import { promiseWithResolvers } from "../../utils/promiseWithResolvers";
|
|
2
|
+
import {
|
|
3
|
+
parsePartialJsonObject,
|
|
4
|
+
getPartialJsonObjectFieldState,
|
|
5
|
+
} from "../../utils/json/parse-partial-json-object";
|
|
6
|
+
import {
|
|
7
|
+
ToolCallArgsReader,
|
|
8
|
+
ToolCallReader,
|
|
9
|
+
ToolCallResponseReader,
|
|
10
|
+
} from "./tool-types";
|
|
11
|
+
import { TypeAtPath, TypePath } from "./type-path-utils";
|
|
12
|
+
import { ToolResponse } from "./ToolResponse";
|
|
13
|
+
|
|
14
|
+
// TODO: remove dispose
|
|
15
|
+
|
|
16
|
+
function getField<T>(obj: T, fieldPath: (string | number)[]): any {
|
|
17
|
+
let current: any = obj;
|
|
18
|
+
for (const key of fieldPath) {
|
|
19
|
+
if (current === undefined || current === null) {
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
current = current[key as string | number];
|
|
23
|
+
}
|
|
24
|
+
return current;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface Handle {
|
|
28
|
+
update(args: unknown): void;
|
|
29
|
+
dispose(): void;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
class GetHandle<T> implements Handle {
|
|
33
|
+
private resolve: (value: any) => void;
|
|
34
|
+
private reject: (reason: unknown) => void;
|
|
35
|
+
private disposed = false;
|
|
36
|
+
private fieldPath: (string | number)[];
|
|
37
|
+
|
|
38
|
+
constructor(
|
|
39
|
+
resolve: (value: any) => void,
|
|
40
|
+
reject: (reason: unknown) => void,
|
|
41
|
+
fieldPath: (string | number)[],
|
|
42
|
+
) {
|
|
43
|
+
this.resolve = resolve;
|
|
44
|
+
this.reject = reject;
|
|
45
|
+
this.fieldPath = fieldPath;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
update(args: unknown): void {
|
|
49
|
+
if (this.disposed) return;
|
|
50
|
+
|
|
51
|
+
try {
|
|
52
|
+
// Check if the field is complete
|
|
53
|
+
if (
|
|
54
|
+
getPartialJsonObjectFieldState(
|
|
55
|
+
args as Record<string, unknown>,
|
|
56
|
+
this.fieldPath,
|
|
57
|
+
) === "complete"
|
|
58
|
+
) {
|
|
59
|
+
const value = getField(args as T, this.fieldPath);
|
|
60
|
+
if (value !== undefined) {
|
|
61
|
+
this.resolve(value);
|
|
62
|
+
this.dispose();
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
} catch (e) {
|
|
66
|
+
this.reject(e);
|
|
67
|
+
this.dispose();
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
dispose(): void {
|
|
72
|
+
this.disposed = true;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
class StreamValuesHandle<T> implements Handle {
|
|
77
|
+
private controller: ReadableStreamDefaultController<any>;
|
|
78
|
+
private disposed = false;
|
|
79
|
+
private fieldPath: (string | number)[];
|
|
80
|
+
|
|
81
|
+
constructor(
|
|
82
|
+
controller: ReadableStreamDefaultController<any>,
|
|
83
|
+
fieldPath: (string | number)[],
|
|
84
|
+
) {
|
|
85
|
+
this.controller = controller;
|
|
86
|
+
this.fieldPath = fieldPath;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
update(args: unknown): void {
|
|
90
|
+
if (this.disposed) return;
|
|
91
|
+
|
|
92
|
+
try {
|
|
93
|
+
const value = getField(args as T, this.fieldPath);
|
|
94
|
+
|
|
95
|
+
if (value !== undefined) {
|
|
96
|
+
this.controller.enqueue(value);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Check if the field is complete, if so close the stream
|
|
100
|
+
if (
|
|
101
|
+
getPartialJsonObjectFieldState(
|
|
102
|
+
args as Record<string, unknown>,
|
|
103
|
+
this.fieldPath,
|
|
104
|
+
) === "complete"
|
|
105
|
+
) {
|
|
106
|
+
this.controller.close();
|
|
107
|
+
this.dispose();
|
|
108
|
+
}
|
|
109
|
+
} catch (e) {
|
|
110
|
+
this.controller.error(e);
|
|
111
|
+
this.dispose();
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
dispose(): void {
|
|
116
|
+
this.disposed = true;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
class StreamTextHandle<T> implements Handle {
|
|
121
|
+
private controller: ReadableStreamDefaultController<any>;
|
|
122
|
+
private disposed = false;
|
|
123
|
+
private fieldPath: (string | number)[];
|
|
124
|
+
private lastValue: any = undefined;
|
|
125
|
+
|
|
126
|
+
constructor(
|
|
127
|
+
controller: ReadableStreamDefaultController<any>,
|
|
128
|
+
fieldPath: (string | number)[],
|
|
129
|
+
) {
|
|
130
|
+
this.controller = controller;
|
|
131
|
+
this.fieldPath = fieldPath;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
update(args: unknown): void {
|
|
135
|
+
if (this.disposed) return;
|
|
136
|
+
|
|
137
|
+
try {
|
|
138
|
+
const value = getField(args as T, this.fieldPath);
|
|
139
|
+
|
|
140
|
+
if (value !== undefined && typeof value === "string") {
|
|
141
|
+
const delta = value.substring(this.lastValue?.length || 0);
|
|
142
|
+
this.lastValue = value;
|
|
143
|
+
this.controller.enqueue(delta);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Check if the field is complete, if so close the stream
|
|
147
|
+
if (
|
|
148
|
+
getPartialJsonObjectFieldState(
|
|
149
|
+
args as Record<string, unknown>,
|
|
150
|
+
this.fieldPath,
|
|
151
|
+
) === "complete"
|
|
152
|
+
) {
|
|
153
|
+
this.controller.close();
|
|
154
|
+
this.dispose();
|
|
155
|
+
}
|
|
156
|
+
} catch (e) {
|
|
157
|
+
this.controller.error(e);
|
|
158
|
+
this.dispose();
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
dispose(): void {
|
|
163
|
+
this.disposed = true;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
class ForEachHandle<T> implements Handle {
|
|
168
|
+
private controller: ReadableStreamDefaultController<any>;
|
|
169
|
+
private disposed = false;
|
|
170
|
+
private fieldPath: (string | number)[];
|
|
171
|
+
private processedIndexes = new Set<number>();
|
|
172
|
+
|
|
173
|
+
constructor(
|
|
174
|
+
controller: ReadableStreamDefaultController<any>,
|
|
175
|
+
fieldPath: (string | number)[],
|
|
176
|
+
) {
|
|
177
|
+
this.controller = controller;
|
|
178
|
+
this.fieldPath = fieldPath;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
update(args: unknown): void {
|
|
182
|
+
if (this.disposed) return;
|
|
183
|
+
|
|
184
|
+
try {
|
|
185
|
+
const array = getField(args as T, this.fieldPath) as unknown as any[];
|
|
186
|
+
|
|
187
|
+
if (!Array.isArray(array)) {
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Check each array element and emit completed ones that haven't been processed
|
|
192
|
+
for (let i = 0; i < array.length; i++) {
|
|
193
|
+
if (!this.processedIndexes.has(i)) {
|
|
194
|
+
const elementPath = [...this.fieldPath, i];
|
|
195
|
+
if (
|
|
196
|
+
getPartialJsonObjectFieldState(
|
|
197
|
+
args as Record<string, unknown>,
|
|
198
|
+
elementPath,
|
|
199
|
+
) === "complete"
|
|
200
|
+
) {
|
|
201
|
+
this.controller.enqueue(array[i]);
|
|
202
|
+
this.processedIndexes.add(i);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Check if the entire array is complete
|
|
208
|
+
if (
|
|
209
|
+
getPartialJsonObjectFieldState(
|
|
210
|
+
args as Record<string, unknown>,
|
|
211
|
+
this.fieldPath,
|
|
212
|
+
) === "complete"
|
|
213
|
+
) {
|
|
214
|
+
this.controller.close();
|
|
215
|
+
this.dispose();
|
|
216
|
+
}
|
|
217
|
+
} catch (e) {
|
|
218
|
+
this.controller.error(e);
|
|
219
|
+
this.dispose();
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
dispose(): void {
|
|
224
|
+
this.disposed = true;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Implementation of ToolCallReader that uses stream of partial JSON
|
|
229
|
+
export class ToolCallArgsReaderImpl<T> implements ToolCallArgsReader<T> {
|
|
230
|
+
private argTextDeltas: ReadableStream<string>;
|
|
231
|
+
private handles: Set<Handle> = new Set();
|
|
232
|
+
private args: any = parsePartialJsonObject("");
|
|
233
|
+
|
|
234
|
+
constructor(argTextDeltas: ReadableStream<string>) {
|
|
235
|
+
this.argTextDeltas = argTextDeltas;
|
|
236
|
+
this.processStream();
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
private async processStream(): Promise<void> {
|
|
240
|
+
try {
|
|
241
|
+
let accumulatedText = "";
|
|
242
|
+
const reader = this.argTextDeltas.getReader();
|
|
243
|
+
|
|
244
|
+
while (true) {
|
|
245
|
+
const { value, done } = await reader.read();
|
|
246
|
+
if (done) break;
|
|
247
|
+
|
|
248
|
+
accumulatedText += value;
|
|
249
|
+
const parsedArgs = parsePartialJsonObject(accumulatedText);
|
|
250
|
+
|
|
251
|
+
if (parsedArgs !== undefined) {
|
|
252
|
+
this.args = parsedArgs;
|
|
253
|
+
// Notify all handles of the updated args
|
|
254
|
+
for (const handle of this.handles) {
|
|
255
|
+
handle.update(parsedArgs);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
} catch (error) {
|
|
260
|
+
console.error("Error processing argument stream:", error);
|
|
261
|
+
// Notify handles of the error
|
|
262
|
+
for (const handle of this.handles) {
|
|
263
|
+
handle.dispose();
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
get<PathT extends TypePath<T>>(
|
|
269
|
+
...fieldPath: PathT
|
|
270
|
+
): Promise<TypeAtPath<T, PathT>> {
|
|
271
|
+
return new Promise<any>((resolve, reject) => {
|
|
272
|
+
const handle = new GetHandle<T>(resolve, reject, fieldPath);
|
|
273
|
+
|
|
274
|
+
// Check if the field is already complete in current args
|
|
275
|
+
if (
|
|
276
|
+
this.args &&
|
|
277
|
+
getPartialJsonObjectFieldState(
|
|
278
|
+
this.args as Record<string, unknown>,
|
|
279
|
+
fieldPath,
|
|
280
|
+
) === "complete"
|
|
281
|
+
) {
|
|
282
|
+
const value = getField(this.args as T, fieldPath);
|
|
283
|
+
if (value !== undefined) {
|
|
284
|
+
resolve(value);
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
this.handles.add(handle);
|
|
290
|
+
handle.update(this.args);
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
streamValues<PathT extends TypePath<T>>(...fieldPath: PathT): any {
|
|
295
|
+
// Use a type assertion to convert the complex TypePath to a simple array
|
|
296
|
+
const simplePath = fieldPath as unknown as (string | number)[];
|
|
297
|
+
|
|
298
|
+
const stream = new ReadableStream<any>({
|
|
299
|
+
start: (controller) => {
|
|
300
|
+
const handle = new StreamValuesHandle<T>(controller, simplePath);
|
|
301
|
+
this.handles.add(handle);
|
|
302
|
+
|
|
303
|
+
// Check current args immediately
|
|
304
|
+
handle.update(this.args);
|
|
305
|
+
},
|
|
306
|
+
cancel: () => {
|
|
307
|
+
// Find and dispose the corresponding handle
|
|
308
|
+
for (const handle of this.handles) {
|
|
309
|
+
if (handle instanceof StreamValuesHandle) {
|
|
310
|
+
handle.dispose();
|
|
311
|
+
this.handles.delete(handle);
|
|
312
|
+
break;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
},
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
// For type compatibility, cast the stream to the required type
|
|
319
|
+
return stream as any;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
streamText<PathT extends TypePath<T>>(...fieldPath: PathT): any {
|
|
323
|
+
// Use a type assertion to convert the complex TypePath to a simple array
|
|
324
|
+
const simplePath = fieldPath as unknown as (string | number)[];
|
|
325
|
+
|
|
326
|
+
const stream = new ReadableStream<any>({
|
|
327
|
+
start: (controller) => {
|
|
328
|
+
const handle = new StreamTextHandle<T>(controller, simplePath);
|
|
329
|
+
this.handles.add(handle);
|
|
330
|
+
|
|
331
|
+
// Check current args immediately
|
|
332
|
+
handle.update(this.args);
|
|
333
|
+
},
|
|
334
|
+
cancel: () => {
|
|
335
|
+
// Find and dispose the corresponding handle
|
|
336
|
+
for (const handle of this.handles) {
|
|
337
|
+
if (handle instanceof StreamTextHandle) {
|
|
338
|
+
handle.dispose();
|
|
339
|
+
this.handles.delete(handle);
|
|
340
|
+
break;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
},
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
// For type compatibility, cast the stream to the required type
|
|
347
|
+
return stream as any;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
forEach<PathT extends TypePath<T>>(...fieldPath: PathT): any {
|
|
351
|
+
// Use a type assertion to convert the complex TypePath to a simple array
|
|
352
|
+
const simplePath = fieldPath as unknown as (string | number)[];
|
|
353
|
+
|
|
354
|
+
const stream = new ReadableStream<any>({
|
|
355
|
+
start: (controller) => {
|
|
356
|
+
const handle = new ForEachHandle<T>(controller, simplePath);
|
|
357
|
+
this.handles.add(handle);
|
|
358
|
+
|
|
359
|
+
// Check current args immediately
|
|
360
|
+
handle.update(this.args);
|
|
361
|
+
},
|
|
362
|
+
cancel: () => {
|
|
363
|
+
// Find and dispose the corresponding handle
|
|
364
|
+
for (const handle of this.handles) {
|
|
365
|
+
if (handle instanceof ForEachHandle) {
|
|
366
|
+
handle.dispose();
|
|
367
|
+
this.handles.delete(handle);
|
|
368
|
+
break;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
},
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
// For type compatibility, cast the stream to the required type
|
|
375
|
+
return stream as any;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
export class ToolCallResponseReaderImpl<TResult>
|
|
380
|
+
implements ToolCallResponseReader<TResult>
|
|
381
|
+
{
|
|
382
|
+
constructor(private readonly promise: Promise<ToolResponse<TResult>>) {}
|
|
383
|
+
|
|
384
|
+
public get() {
|
|
385
|
+
return this.promise;
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
export class ToolCallReaderImpl<TArgs, TResult>
|
|
390
|
+
implements ToolCallReader<TArgs, TResult>
|
|
391
|
+
{
|
|
392
|
+
public readonly args: ToolCallArgsReaderImpl<TArgs>;
|
|
393
|
+
public readonly response: ToolCallResponseReaderImpl<TResult>;
|
|
394
|
+
private readonly writable: WritableStream<string>;
|
|
395
|
+
private readonly resolve: (value: ToolResponse<TResult>) => void;
|
|
396
|
+
|
|
397
|
+
public argsText: string = "";
|
|
398
|
+
|
|
399
|
+
constructor() {
|
|
400
|
+
const stream = new TransformStream<string, string>();
|
|
401
|
+
this.writable = stream.writable;
|
|
402
|
+
this.args = new ToolCallArgsReaderImpl<TArgs>(stream.readable);
|
|
403
|
+
|
|
404
|
+
const { promise, resolve } = promiseWithResolvers<ToolResponse<TResult>>();
|
|
405
|
+
this.resolve = resolve;
|
|
406
|
+
this.response = new ToolCallResponseReaderImpl<TResult>(promise);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
async appendArgsTextDelta(text: string): Promise<void> {
|
|
410
|
+
const writer = this.writable.getWriter();
|
|
411
|
+
try {
|
|
412
|
+
await writer.write(text);
|
|
413
|
+
} catch (err) {
|
|
414
|
+
console.warn(err);
|
|
415
|
+
} finally {
|
|
416
|
+
writer.releaseLock();
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
this.argsText += text;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
setResponse(value: ToolResponse<TResult>): void {
|
|
423
|
+
this.resolve(value);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
result = {
|
|
427
|
+
get: async () => {
|
|
428
|
+
const response = await this.response.get();
|
|
429
|
+
return response.result;
|
|
430
|
+
},
|
|
431
|
+
};
|
|
432
|
+
}
|
|
@@ -1,115 +1,164 @@
|
|
|
1
|
-
// src/core/tool/ToolExecutionStream.ts
|
|
2
1
|
import sjson from "secure-json-parse";
|
|
2
|
+
import { AssistantStreamChunk } from "../AssistantStreamChunk";
|
|
3
3
|
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
4
|
+
AssistantMetaStreamChunk,
|
|
5
|
+
AssistantMetaTransformStream,
|
|
6
|
+
} from "../utils/stream/AssistantMetaTransformStream";
|
|
7
|
+
import { PipeableTransformStream } from "../utils/stream/PipeableTransformStream";
|
|
8
|
+
import { ReadonlyJSONValue } from "../../utils/json/json-value";
|
|
9
|
+
import { ToolResponse } from "./ToolResponse";
|
|
10
|
+
import { withPromiseOrValue } from "../utils/withPromiseOrValue";
|
|
11
|
+
import { ToolCallReaderImpl } from "./ToolCallReader";
|
|
12
|
+
import { ToolCallReader } from "./tool-types";
|
|
13
|
+
|
|
14
|
+
type ToolCallback = (toolCall: {
|
|
15
|
+
toolCallId: string;
|
|
16
|
+
toolName: string;
|
|
17
|
+
args: unknown;
|
|
18
|
+
}) =>
|
|
19
|
+
| Promise<ToolResponse<ReadonlyJSONValue>>
|
|
20
|
+
| ToolResponse<ReadonlyJSONValue>
|
|
21
|
+
| undefined;
|
|
22
|
+
|
|
23
|
+
type ToolStreamCallback = <TArgs, TResult>(toolCall: {
|
|
24
|
+
reader: ToolCallReader<TArgs, TResult>;
|
|
25
|
+
toolCallId: string;
|
|
26
|
+
toolName: string;
|
|
27
|
+
}) => void;
|
|
28
|
+
|
|
29
|
+
type ToolExecutionOptions = {
|
|
30
|
+
execute: ToolCallback;
|
|
31
|
+
streamCall: ToolStreamCallback;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export class ToolExecutionStream extends PipeableTransformStream<
|
|
35
|
+
AssistantStreamChunk,
|
|
36
|
+
AssistantStreamChunk
|
|
37
|
+
> {
|
|
38
|
+
constructor(options: ToolExecutionOptions) {
|
|
39
|
+
const toolCallPromises = new Map<string, PromiseLike<void>>();
|
|
40
|
+
const toolCallControllers = new Map<
|
|
41
|
+
string,
|
|
42
|
+
ToolCallReaderImpl<unknown, unknown>
|
|
43
|
+
>();
|
|
44
|
+
|
|
14
45
|
super((readable) => {
|
|
15
|
-
const transform = new TransformStream
|
|
46
|
+
const transform = new TransformStream<
|
|
47
|
+
AssistantMetaStreamChunk,
|
|
48
|
+
AssistantStreamChunk
|
|
49
|
+
>({
|
|
16
50
|
transform(chunk, controller) {
|
|
51
|
+
// forward everything
|
|
17
52
|
if (chunk.type !== "part-finish" || chunk.meta.type !== "tool-call") {
|
|
18
53
|
controller.enqueue(chunk);
|
|
19
54
|
}
|
|
55
|
+
|
|
20
56
|
const type = chunk.type;
|
|
57
|
+
|
|
21
58
|
switch (type) {
|
|
22
59
|
case "part-start":
|
|
23
60
|
if (chunk.part.type === "tool-call") {
|
|
24
|
-
const reader = new ToolCallReaderImpl();
|
|
61
|
+
const reader = new ToolCallReaderImpl<unknown, unknown>();
|
|
25
62
|
toolCallControllers.set(chunk.part.toolCallId, reader);
|
|
63
|
+
|
|
26
64
|
options.streamCall({
|
|
27
65
|
reader,
|
|
28
66
|
toolCallId: chunk.part.toolCallId,
|
|
29
|
-
toolName: chunk.part.toolName
|
|
67
|
+
toolName: chunk.part.toolName,
|
|
30
68
|
});
|
|
31
69
|
}
|
|
32
70
|
break;
|
|
33
71
|
case "text-delta": {
|
|
34
72
|
if (chunk.meta.type === "tool-call") {
|
|
35
73
|
const toolCallId = chunk.meta.toolCallId;
|
|
36
|
-
|
|
37
|
-
|
|
74
|
+
|
|
75
|
+
const controller = toolCallControllers.get(toolCallId);
|
|
76
|
+
if (!controller)
|
|
38
77
|
throw new Error("No controller found for tool call");
|
|
39
|
-
|
|
78
|
+
controller.appendArgsTextDelta(chunk.textDelta);
|
|
40
79
|
}
|
|
41
80
|
break;
|
|
42
81
|
}
|
|
43
82
|
case "tool-call-args-text-finish": {
|
|
44
83
|
if (chunk.meta.type !== "tool-call") break;
|
|
84
|
+
|
|
45
85
|
const { toolCallId, toolName } = chunk.meta;
|
|
46
|
-
const streamController = toolCallControllers.get(toolCallId)
|
|
86
|
+
const streamController = toolCallControllers.get(toolCallId)!;
|
|
47
87
|
if (!streamController)
|
|
48
88
|
throw new Error("No controller found for tool call");
|
|
89
|
+
|
|
49
90
|
const promise = withPromiseOrValue(
|
|
50
91
|
() => {
|
|
51
92
|
if (!streamController.argsText) {
|
|
52
93
|
console.log(
|
|
53
|
-
"Encountered tool call without args, this should never happen"
|
|
94
|
+
"Encountered tool call without args, this should never happen",
|
|
54
95
|
);
|
|
55
96
|
throw new Error(
|
|
56
|
-
"Encountered tool call without args, this is unexpected."
|
|
97
|
+
"Encountered tool call without args, this is unexpected.",
|
|
57
98
|
);
|
|
58
99
|
}
|
|
100
|
+
|
|
59
101
|
let args;
|
|
60
102
|
try {
|
|
61
103
|
args = sjson.parse(streamController.argsText);
|
|
62
104
|
} catch (e) {
|
|
63
105
|
throw new Error(
|
|
64
|
-
`Function parameter parsing failed. ${JSON.stringify(e.message)}
|
|
106
|
+
`Function parameter parsing failed. ${JSON.stringify((e as Error).message)}`,
|
|
65
107
|
);
|
|
66
108
|
}
|
|
109
|
+
|
|
67
110
|
return options.execute({
|
|
68
111
|
toolCallId,
|
|
69
112
|
toolName,
|
|
70
|
-
args
|
|
113
|
+
args,
|
|
71
114
|
});
|
|
72
115
|
},
|
|
73
116
|
(c) => {
|
|
74
|
-
if (c ===
|
|
117
|
+
if (c === undefined) return;
|
|
118
|
+
|
|
119
|
+
// TODO how to handle new ToolResult({ result: undefined })?
|
|
75
120
|
const result = new ToolResponse({
|
|
76
121
|
artifact: c.artifact,
|
|
77
122
|
result: c.result,
|
|
78
|
-
isError: c.isError
|
|
123
|
+
isError: c.isError,
|
|
79
124
|
});
|
|
80
125
|
streamController.setResponse(result);
|
|
81
126
|
controller.enqueue({
|
|
82
127
|
type: "result",
|
|
83
128
|
path: chunk.path,
|
|
84
|
-
...result
|
|
129
|
+
...result,
|
|
85
130
|
});
|
|
86
131
|
},
|
|
87
132
|
(e) => {
|
|
88
133
|
const result = new ToolResponse({
|
|
89
134
|
result: String(e),
|
|
90
|
-
isError: true
|
|
135
|
+
isError: true,
|
|
91
136
|
});
|
|
137
|
+
|
|
92
138
|
streamController.setResponse(result);
|
|
93
139
|
controller.enqueue({
|
|
94
140
|
type: "result",
|
|
95
141
|
path: chunk.path,
|
|
96
|
-
...result
|
|
142
|
+
...result,
|
|
97
143
|
});
|
|
98
|
-
}
|
|
144
|
+
},
|
|
99
145
|
);
|
|
100
146
|
if (promise) {
|
|
101
147
|
toolCallPromises.set(toolCallId, promise);
|
|
102
148
|
}
|
|
103
149
|
break;
|
|
104
150
|
}
|
|
151
|
+
|
|
105
152
|
case "part-finish": {
|
|
106
153
|
if (chunk.meta.type !== "tool-call") break;
|
|
154
|
+
|
|
107
155
|
const { toolCallId } = chunk.meta;
|
|
108
156
|
const toolCallPromise = toolCallPromises.get(toolCallId);
|
|
109
157
|
if (toolCallPromise) {
|
|
110
158
|
toolCallPromise.then(() => {
|
|
111
159
|
toolCallPromises.delete(toolCallId);
|
|
112
160
|
toolCallControllers.delete(toolCallId);
|
|
161
|
+
|
|
113
162
|
controller.enqueue(chunk);
|
|
114
163
|
});
|
|
115
164
|
} else {
|
|
@@ -120,13 +169,12 @@ var ToolExecutionStream = class extends PipeableTransformStream {
|
|
|
120
169
|
},
|
|
121
170
|
async flush() {
|
|
122
171
|
await Promise.all(toolCallPromises.values());
|
|
123
|
-
}
|
|
172
|
+
},
|
|
124
173
|
});
|
|
125
|
-
|
|
174
|
+
|
|
175
|
+
return readable
|
|
176
|
+
.pipeThrough(new AssistantMetaTransformStream())
|
|
177
|
+
.pipeThrough(transform);
|
|
126
178
|
});
|
|
127
179
|
}
|
|
128
|
-
}
|
|
129
|
-
export {
|
|
130
|
-
ToolExecutionStream
|
|
131
|
-
};
|
|
132
|
-
//# sourceMappingURL=ToolExecutionStream.mjs.map
|
|
180
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { ReadonlyJSONValue } from "../../utils/json/json-value";
|
|
2
|
+
|
|
3
|
+
const TOOL_RESPONSE_SYMBOL = Symbol.for("aui.tool-response");
|
|
4
|
+
|
|
5
|
+
export type ToolResponseInit<TResult> = {
|
|
6
|
+
result: TResult;
|
|
7
|
+
artifact?: ReadonlyJSONValue | undefined;
|
|
8
|
+
isError?: boolean | undefined;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export class ToolResponse<TResult> {
|
|
12
|
+
get [TOOL_RESPONSE_SYMBOL]() {
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
readonly artifact?: ReadonlyJSONValue | undefined;
|
|
17
|
+
readonly result: TResult;
|
|
18
|
+
readonly isError: boolean;
|
|
19
|
+
|
|
20
|
+
constructor(options: ToolResponseInit<TResult>) {
|
|
21
|
+
this.artifact = options.artifact;
|
|
22
|
+
this.result = options.result;
|
|
23
|
+
this.isError = options.isError ?? false;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
static [Symbol.hasInstance](obj: unknown): obj is ToolResponse<unknown> {
|
|
27
|
+
return (
|
|
28
|
+
typeof obj === "object" && obj !== null && TOOL_RESPONSE_SYMBOL in obj
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export type { Tool } from "./tool-types";
|
|
2
|
+
export { ToolResponse } from "./ToolResponse";
|
|
3
|
+
export { ToolExecutionStream } from "./ToolExecutionStream";
|
|
4
|
+
export type { ToolCallReader } from "./tool-types";
|
|
5
|
+
export {
|
|
6
|
+
toolResultStream as unstable_toolResultStream,
|
|
7
|
+
unstable_runPendingTools,
|
|
8
|
+
} from "./toolResultStream";
|