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.
Files changed (201) hide show
  1. package/ai-sdk/package.json +2 -2
  2. package/dist/ai-sdk/index.js +7 -34
  3. package/dist/ai-sdk/index.js.map +1 -1
  4. package/dist/ai-sdk/language-model.js +5 -31
  5. package/dist/ai-sdk/language-model.js.map +1 -1
  6. package/dist/ai-sdk.js +4 -32
  7. package/dist/ai-sdk.js.map +1 -1
  8. package/dist/core/AssistantStream.js +3 -29
  9. package/dist/core/AssistantStream.js.map +1 -1
  10. package/dist/core/AssistantStreamChunk.js +0 -18
  11. package/dist/core/AssistantStreamChunk.js.map +1 -1
  12. package/dist/core/accumulators/AssistantMessageStream.js +9 -35
  13. package/dist/core/accumulators/AssistantMessageStream.js.map +1 -1
  14. package/dist/core/accumulators/assistant-message-accumulator.js +22 -48
  15. package/dist/core/accumulators/assistant-message-accumulator.js.map +1 -1
  16. package/dist/core/index.js +16 -46
  17. package/dist/core/index.js.map +1 -1
  18. package/dist/core/modules/assistant-stream.js +29 -52
  19. package/dist/core/modules/assistant-stream.js.map +1 -1
  20. package/dist/core/modules/text.js +6 -33
  21. package/dist/core/modules/text.js.map +1 -1
  22. package/dist/core/modules/tool-call.js +8 -35
  23. package/dist/core/modules/tool-call.js.map +1 -1
  24. package/dist/core/serialization/PlainText.js +9 -36
  25. package/dist/core/serialization/PlainText.js.map +1 -1
  26. package/dist/core/serialization/data-stream/DataStream.js +57 -79
  27. package/dist/core/serialization/data-stream/DataStream.js.map +1 -1
  28. package/dist/core/serialization/data-stream/chunk-types.js +2 -28
  29. package/dist/core/serialization/data-stream/chunk-types.js.map +1 -1
  30. package/dist/core/serialization/data-stream/serialization.js +6 -33
  31. package/dist/core/serialization/data-stream/serialization.js.map +1 -1
  32. package/dist/core/tool/ToolCallReader.js +30 -55
  33. package/dist/core/tool/ToolCallReader.js.map +1 -1
  34. package/dist/core/tool/ToolExecutionStream.js +18 -52
  35. package/dist/core/tool/ToolExecutionStream.js.map +1 -1
  36. package/dist/core/tool/ToolResponse.js +5 -31
  37. package/dist/core/tool/ToolResponse.js.map +1 -1
  38. package/dist/core/tool/index.js +9 -35
  39. package/dist/core/tool/index.js.map +1 -1
  40. package/dist/core/tool/tool-types.js +0 -18
  41. package/dist/core/tool/tool-types.js.map +1 -1
  42. package/dist/core/tool/toolResultStream.js +8 -35
  43. package/dist/core/tool/toolResultStream.js.map +1 -1
  44. package/dist/core/tool/type-path-utils.js +0 -18
  45. package/dist/core/tool/type-path-utils.js.map +1 -1
  46. package/dist/core/utils/Counter.js +4 -30
  47. package/dist/core/utils/Counter.js.map +1 -1
  48. package/dist/core/utils/generateId.js +4 -30
  49. package/dist/core/utils/generateId.js.map +1 -1
  50. package/dist/core/utils/stream/AssistantMetaTransformStream.js +4 -30
  51. package/dist/core/utils/stream/AssistantMetaTransformStream.js.map +1 -1
  52. package/dist/core/utils/stream/AssistantTransformStream.js +8 -32
  53. package/dist/core/utils/stream/AssistantTransformStream.js.map +1 -1
  54. package/dist/core/utils/stream/LineDecoderStream.js +4 -30
  55. package/dist/core/utils/stream/LineDecoderStream.js.map +1 -1
  56. package/dist/core/utils/stream/PipeableTransformStream.js +4 -30
  57. package/dist/core/utils/stream/PipeableTransformStream.js.map +1 -1
  58. package/dist/core/utils/stream/UnderlyingReadable.js +0 -18
  59. package/dist/core/utils/stream/UnderlyingReadable.js.map +1 -1
  60. package/dist/core/utils/stream/merge.js +5 -31
  61. package/dist/core/utils/stream/merge.js.map +1 -1
  62. package/dist/core/utils/stream/path-utils.js +10 -38
  63. package/dist/core/utils/stream/path-utils.js.map +1 -1
  64. package/dist/core/utils/types.js +0 -18
  65. package/dist/core/utils/types.js.map +1 -1
  66. package/dist/core/utils/withPromiseOrValue.js +2 -28
  67. package/dist/core/utils/withPromiseOrValue.js.map +1 -1
  68. package/dist/index.js +1 -24
  69. package/dist/index.js.map +1 -1
  70. package/dist/utils/AsyncIterableStream.js +2 -28
  71. package/dist/utils/AsyncIterableStream.js.map +1 -1
  72. package/dist/utils/json/fix-json.js +2 -28
  73. package/dist/utils/json/fix-json.js.map +1 -1
  74. package/dist/utils/json/is-json.js +2 -30
  75. package/dist/utils/json/is-json.js.map +1 -1
  76. package/dist/utils/json/json-value.js +0 -18
  77. package/dist/utils/json/json-value.js.map +1 -1
  78. package/dist/utils/json/parse-partial-json-object.js +12 -50
  79. package/dist/utils/json/parse-partial-json-object.js.map +1 -1
  80. package/dist/utils/promiseWithResolvers.js +3 -29
  81. package/dist/utils/promiseWithResolvers.js.map +1 -1
  82. package/dist/utils.js +9 -32
  83. package/dist/utils.js.map +1 -1
  84. package/package.json +7 -9
  85. package/{dist/ai-sdk/index.mjs → src/ai-sdk/index.ts} +64 -33
  86. package/{dist/ai-sdk/language-model.mjs → src/ai-sdk/language-model.ts} +35 -19
  87. package/src/ai-sdk.ts +2 -0
  88. package/src/core/AssistantStream.ts +39 -0
  89. package/src/core/AssistantStreamChunk.ts +93 -0
  90. package/src/core/accumulators/AssistantMessageStream.ts +56 -0
  91. package/{dist/core/accumulators/assistant-message-accumulator.mjs → src/core/accumulators/assistant-message-accumulator.ts} +152 -67
  92. package/src/core/index.ts +17 -0
  93. package/{dist/core/modules/assistant-stream.mjs → src/core/modules/assistant-stream.ts} +122 -60
  94. package/src/core/modules/text.ts +64 -0
  95. package/{dist/core/modules/tool-call.mjs → src/core/modules/tool-call.ts} +48 -27
  96. package/src/core/serialization/PlainText.ts +68 -0
  97. package/{dist/core/serialization/data-stream/DataStream.mjs → src/core/serialization/data-stream/DataStream.ts} +111 -67
  98. package/src/core/serialization/data-stream/chunk-types.ts +93 -0
  99. package/src/core/serialization/data-stream/serialization.ts +32 -0
  100. package/src/core/tool/ToolCallReader.ts +432 -0
  101. package/{dist/core/tool/ToolExecutionStream.mjs → src/core/tool/ToolExecutionStream.ts} +83 -35
  102. package/src/core/tool/ToolResponse.ts +31 -0
  103. package/src/core/tool/index.ts +8 -0
  104. package/src/core/tool/tool-types.ts +107 -0
  105. package/src/core/tool/toolResultStream.ts +119 -0
  106. package/src/core/tool/type-path-utils.ts +36 -0
  107. package/src/core/utils/Counter.ts +7 -0
  108. package/src/core/utils/generateId.tsx +6 -0
  109. package/src/core/utils/stream/AssistantMetaTransformStream.ts +74 -0
  110. package/src/core/utils/stream/AssistantTransformStream.ts +74 -0
  111. package/{dist/core/utils/stream/LineDecoderStream.mjs → src/core/utils/stream/LineDecoderStream.ts} +12 -10
  112. package/src/core/utils/stream/PipeableTransformStream.ts +10 -0
  113. package/src/core/utils/stream/UnderlyingReadable.ts +5 -0
  114. package/src/core/utils/stream/merge.ts +212 -0
  115. package/src/core/utils/stream/path-utils.ts +71 -0
  116. package/src/core/utils/types.ts +150 -0
  117. package/src/core/utils/withPromiseOrValue.ts +20 -0
  118. package/src/index.ts +1 -0
  119. package/src/utils/AsyncIterableStream.ts +24 -0
  120. package/{dist/utils/json/fix-json.mjs → src/utils/json/fix-json.ts} +146 -20
  121. package/src/utils/json/is-json.ts +43 -0
  122. package/src/utils/json/json-value.ts +13 -0
  123. package/src/utils/json/parse-partial-json-object.test.ts +225 -0
  124. package/src/utils/json/parse-partial-json-object.ts +103 -0
  125. package/src/utils/promiseWithResolvers.ts +10 -0
  126. package/src/utils.ts +13 -0
  127. package/dist/ai-sdk/index.mjs.map +0 -1
  128. package/dist/ai-sdk/language-model.mjs.map +0 -1
  129. package/dist/ai-sdk.mjs +0 -7
  130. package/dist/ai-sdk.mjs.map +0 -1
  131. package/dist/core/AssistantStream.mjs +0 -21
  132. package/dist/core/AssistantStream.mjs.map +0 -1
  133. package/dist/core/AssistantStreamChunk.mjs +0 -1
  134. package/dist/core/AssistantStreamChunk.mjs.map +0 -1
  135. package/dist/core/accumulators/AssistantMessageStream.mjs +0 -54
  136. package/dist/core/accumulators/AssistantMessageStream.mjs.map +0 -1
  137. package/dist/core/accumulators/assistant-message-accumulator.mjs.map +0 -1
  138. package/dist/core/index.mjs +0 -26
  139. package/dist/core/index.mjs.map +0 -1
  140. package/dist/core/modules/assistant-stream.mjs.map +0 -1
  141. package/dist/core/modules/text.mjs +0 -52
  142. package/dist/core/modules/text.mjs.map +0 -1
  143. package/dist/core/modules/tool-call.mjs.map +0 -1
  144. package/dist/core/serialization/PlainText.mjs +0 -44
  145. package/dist/core/serialization/PlainText.mjs.map +0 -1
  146. package/dist/core/serialization/data-stream/DataStream.mjs.map +0 -1
  147. package/dist/core/serialization/data-stream/chunk-types.mjs +0 -24
  148. package/dist/core/serialization/data-stream/chunk-types.mjs.map +0 -1
  149. package/dist/core/serialization/data-stream/serialization.mjs +0 -30
  150. package/dist/core/serialization/data-stream/serialization.mjs.map +0 -1
  151. package/dist/core/tool/ToolCallReader.mjs +0 -315
  152. package/dist/core/tool/ToolCallReader.mjs.map +0 -1
  153. package/dist/core/tool/ToolExecutionStream.mjs.map +0 -1
  154. package/dist/core/tool/ToolResponse.mjs +0 -22
  155. package/dist/core/tool/ToolResponse.mjs.map +0 -1
  156. package/dist/core/tool/index.mjs +0 -14
  157. package/dist/core/tool/index.mjs.map +0 -1
  158. package/dist/core/tool/tool-types.mjs +0 -1
  159. package/dist/core/tool/tool-types.mjs.map +0 -1
  160. package/dist/core/tool/toolResultStream.mjs +0 -78
  161. package/dist/core/tool/toolResultStream.mjs.map +0 -1
  162. package/dist/core/tool/type-path-utils.mjs +0 -1
  163. package/dist/core/tool/type-path-utils.mjs.map +0 -1
  164. package/dist/core/utils/Counter.mjs +0 -11
  165. package/dist/core/utils/Counter.mjs.map +0 -1
  166. package/dist/core/utils/generateId.mjs +0 -10
  167. package/dist/core/utils/generateId.mjs.map +0 -1
  168. package/dist/core/utils/stream/AssistantMetaTransformStream.mjs +0 -44
  169. package/dist/core/utils/stream/AssistantMetaTransformStream.mjs.map +0 -1
  170. package/dist/core/utils/stream/AssistantTransformStream.mjs +0 -46
  171. package/dist/core/utils/stream/AssistantTransformStream.mjs.map +0 -1
  172. package/dist/core/utils/stream/LineDecoderStream.mjs.map +0 -1
  173. package/dist/core/utils/stream/PipeableTransformStream.mjs +0 -15
  174. package/dist/core/utils/stream/PipeableTransformStream.mjs.map +0 -1
  175. package/dist/core/utils/stream/UnderlyingReadable.mjs +0 -1
  176. package/dist/core/utils/stream/UnderlyingReadable.mjs.map +0 -1
  177. package/dist/core/utils/stream/merge.mjs +0 -85
  178. package/dist/core/utils/stream/merge.mjs.map +0 -1
  179. package/dist/core/utils/stream/path-utils.mjs +0 -61
  180. package/dist/core/utils/stream/path-utils.mjs.map +0 -1
  181. package/dist/core/utils/types.mjs +0 -1
  182. package/dist/core/utils/types.mjs.map +0 -1
  183. package/dist/core/utils/withPromiseOrValue.mjs +0 -17
  184. package/dist/core/utils/withPromiseOrValue.mjs.map +0 -1
  185. package/dist/index.mjs +0 -3
  186. package/dist/index.mjs.map +0 -1
  187. package/dist/utils/AsyncIterableStream.mjs +0 -21
  188. package/dist/utils/AsyncIterableStream.mjs.map +0 -1
  189. package/dist/utils/json/fix-json.mjs.map +0 -1
  190. package/dist/utils/json/is-json.mjs +0 -29
  191. package/dist/utils/json/is-json.mjs.map +0 -1
  192. package/dist/utils/json/json-value.mjs +0 -1
  193. package/dist/utils/json/json-value.mjs.map +0 -1
  194. package/dist/utils/json/parse-partial-json-object.mjs +0 -65
  195. package/dist/utils/json/parse-partial-json-object.mjs.map +0 -1
  196. package/dist/utils/promiseWithResolvers.mjs +0 -15
  197. package/dist/utils/promiseWithResolvers.mjs.map +0 -1
  198. package/dist/utils.mjs +0 -14
  199. package/dist/utils.mjs.map +0 -1
  200. package/utils/README.md +0 -1
  201. 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
- AssistantMetaTransformStream
5
- } from "../utils/stream/AssistantMetaTransformStream.mjs";
6
- import { PipeableTransformStream } from "../utils/stream/PipeableTransformStream.mjs";
7
- import { ToolResponse } from "./ToolResponse.mjs";
8
- import { withPromiseOrValue } from "../utils/withPromiseOrValue.mjs";
9
- import { ToolCallReaderImpl } from "./ToolCallReader.mjs";
10
- var ToolExecutionStream = class extends PipeableTransformStream {
11
- constructor(options) {
12
- const toolCallPromises = /* @__PURE__ */ new Map();
13
- const toolCallControllers = /* @__PURE__ */ new Map();
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
- const controller2 = toolCallControllers.get(toolCallId);
37
- if (!controller2)
74
+
75
+ const controller = toolCallControllers.get(toolCallId);
76
+ if (!controller)
38
77
  throw new Error("No controller found for tool call");
39
- controller2.appendArgsTextDelta(chunk.textDelta);
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 === void 0) return;
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
- return readable.pipeThrough(new AssistantMetaTransformStream()).pipeThrough(transform);
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";