assistant-stream 0.2.0 → 0.2.2

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 (199) hide show
  1. package/dist/ai-sdk/index.d.ts +5 -0
  2. package/dist/ai-sdk/index.d.ts.map +1 -0
  3. package/dist/ai-sdk/index.js +4 -3
  4. package/dist/ai-sdk/index.js.map +1 -1
  5. package/dist/ai-sdk/language-model.d.ts +6 -0
  6. package/dist/ai-sdk/language-model.d.ts.map +1 -0
  7. package/dist/ai-sdk/language-model.js +4 -3
  8. package/dist/ai-sdk/language-model.js.map +1 -1
  9. package/dist/ai-sdk.d.ts +3 -0
  10. package/dist/ai-sdk.d.ts.map +1 -0
  11. package/dist/ai-sdk.js +3 -2
  12. package/dist/ai-sdk.js.map +1 -1
  13. package/dist/core/AssistantStream.d.ts +12 -0
  14. package/dist/core/AssistantStream.d.ts.map +1 -0
  15. package/dist/core/AssistantStream.js +2 -1
  16. package/dist/core/AssistantStream.js.map +1 -1
  17. package/dist/core/AssistantStreamChunk.d.ts +64 -0
  18. package/dist/core/AssistantStreamChunk.d.ts.map +1 -0
  19. package/dist/core/accumulators/AssistantMessageStream.d.ts +13 -0
  20. package/dist/core/accumulators/AssistantMessageStream.d.ts.map +1 -0
  21. package/dist/core/accumulators/AssistantMessageStream.js +7 -6
  22. package/dist/core/accumulators/AssistantMessageStream.js.map +1 -1
  23. package/dist/core/accumulators/assistant-message-accumulator.d.ts +8 -0
  24. package/dist/core/accumulators/assistant-message-accumulator.d.ts.map +1 -0
  25. package/dist/core/accumulators/assistant-message-accumulator.js +19 -18
  26. package/dist/core/accumulators/assistant-message-accumulator.js.map +1 -1
  27. package/dist/core/index.d.ts +11 -0
  28. package/dist/core/index.d.ts.map +1 -0
  29. package/dist/core/index.js +8 -7
  30. package/dist/core/index.js.map +1 -1
  31. package/dist/core/modules/assistant-stream.d.ts +31 -0
  32. package/dist/core/modules/assistant-stream.d.ts.map +1 -0
  33. package/dist/core/modules/assistant-stream.js +12 -11
  34. package/dist/core/modules/assistant-stream.js.map +1 -1
  35. package/dist/core/modules/text.d.ts +9 -0
  36. package/dist/core/modules/text.d.ts.map +1 -0
  37. package/dist/core/modules/text.js +5 -4
  38. package/dist/core/modules/text.js.map +1 -1
  39. package/dist/core/modules/tool-call.d.ts +13 -0
  40. package/dist/core/modules/tool-call.d.ts.map +1 -0
  41. package/dist/core/modules/tool-call.js +6 -5
  42. package/dist/core/modules/tool-call.js.map +1 -1
  43. package/dist/core/serialization/PlainText.d.ts +11 -0
  44. package/dist/core/serialization/PlainText.d.ts.map +1 -0
  45. package/dist/core/serialization/PlainText.js +7 -6
  46. package/dist/core/serialization/PlainText.js.map +1 -1
  47. package/dist/core/serialization/data-stream/DataStream.d.ts +11 -0
  48. package/dist/core/serialization/data-stream/DataStream.d.ts.map +1 -0
  49. package/dist/core/serialization/data-stream/DataStream.js +12 -11
  50. package/dist/core/serialization/data-stream/DataStream.js.map +1 -1
  51. package/dist/core/serialization/data-stream/chunk-types.d.ts +86 -0
  52. package/dist/core/serialization/data-stream/chunk-types.d.ts.map +1 -0
  53. package/dist/core/serialization/data-stream/chunk-types.js +1 -0
  54. package/dist/core/serialization/data-stream/chunk-types.js.map +1 -1
  55. package/dist/core/serialization/data-stream/serialization.d.ts +8 -0
  56. package/dist/core/serialization/data-stream/serialization.d.ts.map +1 -0
  57. package/dist/core/serialization/data-stream/serialization.js +5 -4
  58. package/dist/core/serialization/data-stream/serialization.js.map +1 -1
  59. package/dist/core/tool/ToolCallReader.d.ts +33 -0
  60. package/dist/core/tool/ToolCallReader.d.ts.map +1 -0
  61. package/dist/core/tool/ToolCallReader.js +17 -16
  62. package/dist/core/tool/ToolCallReader.js.map +1 -1
  63. package/dist/core/tool/ToolExecutionStream.d.ts +24 -0
  64. package/dist/core/tool/ToolExecutionStream.d.ts.map +1 -0
  65. package/dist/core/tool/ToolExecutionStream.js +8 -7
  66. package/dist/core/tool/ToolExecutionStream.js.map +1 -1
  67. package/dist/core/tool/ToolResponse.d.ts +17 -0
  68. package/dist/core/tool/ToolResponse.d.ts.map +1 -0
  69. package/dist/core/tool/ToolResponse.js +4 -3
  70. package/dist/core/tool/ToolResponse.js.map +1 -1
  71. package/dist/core/tool/index.d.ts +6 -0
  72. package/dist/core/tool/index.d.ts.map +1 -0
  73. package/dist/core/tool/index.js +4 -3
  74. package/dist/core/tool/index.js.map +1 -1
  75. package/dist/core/tool/tool-types.d.ts +76 -0
  76. package/dist/core/tool/tool-types.d.ts.map +1 -0
  77. package/dist/core/tool/toolResultStream.d.ts +6 -0
  78. package/dist/core/tool/toolResultStream.d.ts.map +1 -0
  79. package/dist/core/tool/toolResultStream.js +4 -3
  80. package/dist/core/tool/toolResultStream.js.map +1 -1
  81. package/dist/core/tool/type-path-utils.d.ts +23 -0
  82. package/dist/core/tool/type-path-utils.d.ts.map +1 -0
  83. package/dist/core/utils/Counter.d.ts +5 -0
  84. package/dist/core/utils/Counter.d.ts.map +1 -0
  85. package/dist/core/utils/Counter.js +3 -2
  86. package/dist/core/utils/Counter.js.map +1 -1
  87. package/dist/core/utils/generateId.d.ts +2 -0
  88. package/dist/core/utils/generateId.d.ts.map +1 -0
  89. package/dist/core/utils/generateId.js +2 -1
  90. package/dist/core/utils/generateId.js.map +1 -1
  91. package/dist/core/utils/stream/AssistantMetaTransformStream.d.ts +20 -0
  92. package/dist/core/utils/stream/AssistantMetaTransformStream.d.ts.map +1 -0
  93. package/dist/core/utils/stream/AssistantMetaTransformStream.js +3 -2
  94. package/dist/core/utils/stream/AssistantMetaTransformStream.js.map +1 -1
  95. package/dist/core/utils/stream/AssistantTransformStream.d.ts +15 -0
  96. package/dist/core/utils/stream/AssistantTransformStream.d.ts.map +1 -0
  97. package/dist/core/utils/stream/AssistantTransformStream.js +4 -3
  98. package/dist/core/utils/stream/AssistantTransformStream.js.map +1 -1
  99. package/dist/core/utils/stream/LineDecoderStream.d.ts +5 -0
  100. package/dist/core/utils/stream/LineDecoderStream.d.ts.map +1 -0
  101. package/dist/core/utils/stream/LineDecoderStream.js +3 -2
  102. package/dist/core/utils/stream/LineDecoderStream.js.map +1 -1
  103. package/dist/core/utils/stream/PipeableTransformStream.d.ts +4 -0
  104. package/dist/core/utils/stream/PipeableTransformStream.d.ts.map +1 -0
  105. package/dist/core/utils/stream/PipeableTransformStream.js +3 -2
  106. package/dist/core/utils/stream/PipeableTransformStream.js.map +1 -1
  107. package/dist/core/utils/stream/UnderlyingReadable.d.ts +6 -0
  108. package/dist/core/utils/stream/UnderlyingReadable.d.ts.map +1 -0
  109. package/dist/core/utils/stream/merge.d.ts +9 -0
  110. package/dist/core/utils/stream/merge.d.ts.map +1 -0
  111. package/dist/core/utils/stream/merge.js +3 -2
  112. package/dist/core/utils/stream/merge.js.map +1 -1
  113. package/dist/core/utils/stream/path-utils.d.ts +12 -0
  114. package/dist/core/utils/stream/path-utils.d.ts.map +1 -0
  115. package/dist/core/utils/stream/path-utils.js +8 -7
  116. package/dist/core/utils/stream/path-utils.js.map +1 -1
  117. package/dist/core/utils/types.d.ts +102 -0
  118. package/dist/core/utils/types.d.ts.map +1 -0
  119. package/dist/core/utils/withPromiseOrValue.d.ts +2 -0
  120. package/dist/core/utils/withPromiseOrValue.d.ts.map +1 -0
  121. package/dist/core/utils/withPromiseOrValue.js +1 -0
  122. package/dist/core/utils/withPromiseOrValue.js.map +1 -1
  123. package/dist/index.d.ts +2 -0
  124. package/dist/index.d.ts.map +1 -0
  125. package/dist/index.js +2 -1
  126. package/dist/index.js.map +1 -1
  127. package/dist/utils/AsyncIterableStream.d.ts +3 -0
  128. package/dist/utils/AsyncIterableStream.d.ts.map +1 -0
  129. package/dist/utils/AsyncIterableStream.js +1 -0
  130. package/dist/utils/AsyncIterableStream.js.map +1 -1
  131. package/dist/utils/json/fix-json.d.ts +2 -0
  132. package/dist/utils/json/fix-json.d.ts.map +1 -0
  133. package/dist/utils/json/fix-json.js +1 -0
  134. package/dist/utils/json/fix-json.js.map +1 -1
  135. package/dist/utils/json/is-json.d.ts +5 -0
  136. package/dist/utils/json/is-json.d.ts.map +1 -0
  137. package/dist/utils/json/is-json.js +1 -0
  138. package/dist/utils/json/is-json.js.map +1 -1
  139. package/dist/utils/json/json-value.d.ts +6 -0
  140. package/dist/utils/json/json-value.d.ts.map +1 -0
  141. package/dist/utils/json/parse-partial-json-object.d.ts +14 -0
  142. package/dist/utils/json/parse-partial-json-object.d.ts.map +1 -0
  143. package/dist/utils/json/parse-partial-json-object.js +7 -6
  144. package/dist/utils/json/parse-partial-json-object.js.map +1 -1
  145. package/dist/utils/json/parse-partial-json-object.test.d.ts +2 -0
  146. package/dist/utils/json/parse-partial-json-object.test.d.ts.map +1 -0
  147. package/dist/utils/promiseWithResolvers.d.ts +6 -0
  148. package/dist/utils/promiseWithResolvers.d.ts.map +1 -0
  149. package/dist/utils/promiseWithResolvers.js +2 -1
  150. package/dist/utils/promiseWithResolvers.js.map +1 -1
  151. package/dist/utils.d.ts +4 -0
  152. package/dist/utils.d.ts.map +1 -0
  153. package/dist/utils.js +3 -2
  154. package/dist/utils.js.map +1 -1
  155. package/package.json +3 -2
  156. package/src/ai-sdk/index.ts +204 -0
  157. package/src/ai-sdk/language-model.ts +122 -0
  158. package/src/ai-sdk.ts +2 -0
  159. package/src/core/AssistantStream.ts +39 -0
  160. package/src/core/AssistantStreamChunk.ts +93 -0
  161. package/src/core/accumulators/AssistantMessageStream.ts +56 -0
  162. package/src/core/accumulators/assistant-message-accumulator.ts +394 -0
  163. package/src/core/index.ts +17 -0
  164. package/src/core/modules/assistant-stream.ts +264 -0
  165. package/src/core/modules/text.ts +64 -0
  166. package/src/core/modules/tool-call.ts +105 -0
  167. package/src/core/serialization/PlainText.ts +68 -0
  168. package/src/core/serialization/data-stream/DataStream.ts +355 -0
  169. package/src/core/serialization/data-stream/chunk-types.ts +93 -0
  170. package/src/core/serialization/data-stream/serialization.ts +32 -0
  171. package/src/core/tool/ToolCallReader.ts +432 -0
  172. package/src/core/tool/ToolExecutionStream.ts +180 -0
  173. package/src/core/tool/ToolResponse.ts +31 -0
  174. package/src/core/tool/index.ts +8 -0
  175. package/src/core/tool/tool-types.ts +107 -0
  176. package/src/core/tool/toolResultStream.ts +119 -0
  177. package/src/core/tool/type-path-utils.ts +36 -0
  178. package/src/core/utils/Counter.ts +7 -0
  179. package/src/core/utils/generateId.tsx +6 -0
  180. package/src/core/utils/stream/AssistantMetaTransformStream.ts +74 -0
  181. package/src/core/utils/stream/AssistantTransformStream.ts +74 -0
  182. package/src/core/utils/stream/LineDecoderStream.ts +29 -0
  183. package/src/core/utils/stream/PipeableTransformStream.ts +10 -0
  184. package/src/core/utils/stream/UnderlyingReadable.ts +5 -0
  185. package/src/core/utils/stream/merge.ts +212 -0
  186. package/src/core/utils/stream/path-utils.ts +71 -0
  187. package/src/core/utils/types.ts +150 -0
  188. package/src/core/utils/withPromiseOrValue.ts +20 -0
  189. package/src/index.ts +1 -0
  190. package/src/utils/AsyncIterableStream.ts +24 -0
  191. package/src/utils/json/fix-json.ts +488 -0
  192. package/src/utils/json/is-json.ts +43 -0
  193. package/src/utils/json/json-value.ts +13 -0
  194. package/src/utils/json/parse-partial-json-object.test.ts +225 -0
  195. package/src/utils/json/parse-partial-json-object.ts +103 -0
  196. package/src/utils/promiseWithResolvers.ts +10 -0
  197. package/src/utils.ts +13 -0
  198. package/utils/README.md +0 -1
  199. package/utils/package.json +0 -5
@@ -0,0 +1,264 @@
1
+ import { AssistantStream } from "../AssistantStream";
2
+ import { AssistantStreamChunk, PartInit } from "../AssistantStreamChunk";
3
+ import { createMergeStream } from "../utils/stream/merge";
4
+ import { createTextStreamController, TextStreamController } from "./text";
5
+ import {
6
+ createToolCallStreamController,
7
+ ToolCallStreamController,
8
+ } from "./tool-call";
9
+ import { Counter } from "../utils/Counter";
10
+ import {
11
+ PathAppendEncoder,
12
+ PathMergeEncoder,
13
+ } from "../utils/stream/path-utils";
14
+ import { DataStreamEncoder } from "../serialization/data-stream/DataStream";
15
+ import { FilePart, SourcePart } from "../utils/types";
16
+ import { generateId } from "../utils/generateId";
17
+ import {
18
+ ReadonlyJSONObject,
19
+ ReadonlyJSONValue,
20
+ } from "../../utils/json/json-value";
21
+ import { ToolResponseInit } from "../tool/ToolResponse";
22
+ import { promiseWithResolvers } from "../../utils/promiseWithResolvers";
23
+
24
+ type ToolCallPartInit = {
25
+ toolCallId?: string;
26
+ toolName: string;
27
+ argsText?: string;
28
+ args?: ReadonlyJSONObject;
29
+ response?: ToolResponseInit<ReadonlyJSONValue>;
30
+ };
31
+
32
+ export type AssistantStreamController = {
33
+ appendText(textDelta: string): void;
34
+ appendReasoning(reasoningDelta: string): void;
35
+ appendSource(options: SourcePart): void;
36
+ appendFile(options: FilePart): void;
37
+ addTextPart(): TextStreamController;
38
+ addToolCallPart(options: string): ToolCallStreamController;
39
+ addToolCallPart(options: ToolCallPartInit): ToolCallStreamController;
40
+ enqueue(chunk: AssistantStreamChunk): void;
41
+ merge(stream: AssistantStream): void;
42
+ close(): void;
43
+ };
44
+
45
+ class AssistantStreamControllerImpl implements AssistantStreamController {
46
+ private _merger = createMergeStream();
47
+ private _append:
48
+ | {
49
+ controller: TextStreamController;
50
+ kind: "text" | "reasoning";
51
+ }
52
+ | undefined;
53
+ private _contentCounter = new Counter();
54
+
55
+ get __internal_isClosed() {
56
+ return this._merger.isSealed();
57
+ }
58
+
59
+ __internal_getReadable() {
60
+ return this._merger.readable;
61
+ }
62
+
63
+ private _closeSubscriber: undefined | (() => void);
64
+ __internal_subscribeToClose(callback: () => void) {
65
+ this._closeSubscriber = callback;
66
+ }
67
+
68
+ private _addPart(part: PartInit, stream: AssistantStream) {
69
+ if (this._append) {
70
+ this._append.controller.close();
71
+ this._append = undefined;
72
+ }
73
+
74
+ this.enqueue({
75
+ type: "part-start",
76
+ part,
77
+ path: [],
78
+ });
79
+ this._merger.addStream(
80
+ stream.pipeThrough(new PathAppendEncoder(this._contentCounter.value)),
81
+ );
82
+ }
83
+
84
+ merge(stream: AssistantStream) {
85
+ this._merger.addStream(
86
+ stream.pipeThrough(new PathMergeEncoder(this._contentCounter)),
87
+ );
88
+ }
89
+
90
+ appendText(textDelta: string) {
91
+ if (this._append?.kind !== "text") {
92
+ this._append = {
93
+ kind: "text",
94
+ controller: this.addTextPart(),
95
+ };
96
+ }
97
+ this._append.controller.append(textDelta);
98
+ }
99
+
100
+ appendReasoning(textDelta: string) {
101
+ if (this._append?.kind !== "reasoning") {
102
+ this._append = {
103
+ kind: "reasoning",
104
+ controller: this.addReasoningPart(),
105
+ };
106
+ }
107
+ this._append.controller.append(textDelta);
108
+ }
109
+
110
+ addTextPart() {
111
+ const [stream, controller] = createTextStreamController();
112
+ this._addPart({ type: "text" }, stream);
113
+ return controller;
114
+ }
115
+
116
+ addReasoningPart() {
117
+ const [stream, controller] = createTextStreamController();
118
+ this._addPart({ type: "reasoning" }, stream);
119
+ return controller;
120
+ }
121
+
122
+ addToolCallPart(
123
+ options: string | ToolCallPartInit,
124
+ ): ToolCallStreamController {
125
+ const opt = typeof options === "string" ? { toolName: options } : options;
126
+ const toolName = opt.toolName;
127
+ const toolCallId = opt.toolCallId ?? generateId();
128
+
129
+ const [stream, controller] = createToolCallStreamController();
130
+ this._addPart({ type: "tool-call", toolName, toolCallId }, stream);
131
+
132
+ if (opt.argsText !== undefined) {
133
+ controller.argsText.append(opt.argsText);
134
+ controller.argsText.close();
135
+ }
136
+ if (opt.args !== undefined) {
137
+ controller.argsText.append(JSON.stringify(opt.args));
138
+ controller.argsText.close();
139
+ }
140
+ if (opt.response !== undefined) {
141
+ controller.setResponse(opt.response);
142
+ }
143
+
144
+ return controller;
145
+ }
146
+
147
+ appendSource(options: SourcePart) {
148
+ this._addPart(
149
+ options,
150
+ new ReadableStream({
151
+ start(controller) {
152
+ controller.enqueue({
153
+ type: "part-finish",
154
+ path: [],
155
+ });
156
+ controller.close();
157
+ },
158
+ }),
159
+ );
160
+ }
161
+
162
+ appendFile(options: FilePart) {
163
+ this._addPart(
164
+ options,
165
+ new ReadableStream({
166
+ start(controller) {
167
+ controller.enqueue({
168
+ type: "part-finish",
169
+ path: [],
170
+ });
171
+ controller.close();
172
+ },
173
+ }),
174
+ );
175
+ }
176
+
177
+ enqueue(chunk: AssistantStreamChunk) {
178
+ this._merger.enqueue(chunk);
179
+
180
+ if (chunk.type === "part-start" && chunk.path.length === 0) {
181
+ this._contentCounter.up();
182
+ }
183
+ }
184
+
185
+ close() {
186
+ this._merger.seal();
187
+ this._append?.controller?.close();
188
+
189
+ this._closeSubscriber?.();
190
+ }
191
+ }
192
+
193
+ export function createAssistantStream(
194
+ callback: (controller: AssistantStreamController) => PromiseLike<void> | void,
195
+ ): AssistantStream {
196
+ const controller = new AssistantStreamControllerImpl();
197
+
198
+ let promiseOrVoid: PromiseLike<void> | void;
199
+ try {
200
+ promiseOrVoid = callback(controller);
201
+ } catch (e) {
202
+ if (!controller.__internal_isClosed) {
203
+ controller.enqueue({
204
+ type: "error",
205
+ path: [],
206
+ error: String(e),
207
+ });
208
+ controller.close();
209
+ }
210
+ throw e;
211
+ }
212
+
213
+ if (promiseOrVoid instanceof Promise) {
214
+ const runTask = async () => {
215
+ try {
216
+ await promiseOrVoid;
217
+ } catch (e) {
218
+ if (!controller.__internal_isClosed) {
219
+ controller.enqueue({
220
+ type: "error",
221
+ path: [],
222
+ error: String(e),
223
+ });
224
+ }
225
+ throw e;
226
+ } finally {
227
+ if (!controller.__internal_isClosed) {
228
+ controller.close();
229
+ }
230
+ }
231
+ };
232
+ runTask();
233
+ } else {
234
+ if (!controller.__internal_isClosed) {
235
+ controller.close();
236
+ }
237
+ }
238
+
239
+ return controller.__internal_getReadable();
240
+ }
241
+
242
+ export function createAssistantStreamController() {
243
+ const { resolve, promise } = promiseWithResolvers<void>();
244
+ let controller!: AssistantStreamController;
245
+ const stream = createAssistantStream((c) => {
246
+ controller = c;
247
+
248
+ (controller as AssistantStreamControllerImpl).__internal_subscribeToClose(
249
+ resolve,
250
+ );
251
+
252
+ return promise;
253
+ });
254
+ return [stream, controller] as const;
255
+ }
256
+
257
+ export function createAssistantStreamResponse(
258
+ callback: (controller: AssistantStreamController) => PromiseLike<void> | void,
259
+ ) {
260
+ return AssistantStream.toResponse(
261
+ createAssistantStream(callback),
262
+ new DataStreamEncoder(),
263
+ );
264
+ }
@@ -0,0 +1,64 @@
1
+ import { AssistantStream } from "../AssistantStream";
2
+ import { AssistantStreamChunk } from "../AssistantStreamChunk";
3
+ import { UnderlyingReadable } from "../utils/stream/UnderlyingReadable";
4
+
5
+ export type TextStreamController = {
6
+ append(textDelta: string): void;
7
+ close(): void; // TODO reason? error?
8
+ };
9
+
10
+ class TextStreamControllerImpl implements TextStreamController {
11
+ private _controller: ReadableStreamDefaultController<AssistantStreamChunk>;
12
+ private _isClosed = false;
13
+
14
+ constructor(
15
+ controller: ReadableStreamDefaultController<AssistantStreamChunk>,
16
+ ) {
17
+ this._controller = controller;
18
+ }
19
+
20
+ append(textDelta: string) {
21
+ this._controller.enqueue({
22
+ type: "text-delta",
23
+ path: [],
24
+ textDelta,
25
+ });
26
+ return this;
27
+ }
28
+
29
+ close() {
30
+ if (this._isClosed) return;
31
+ this._isClosed = true;
32
+ this._controller.enqueue({
33
+ type: "part-finish",
34
+ path: [],
35
+ });
36
+ this._controller.close();
37
+ }
38
+ }
39
+
40
+ export const createTextStream = (
41
+ readable: UnderlyingReadable<TextStreamController>,
42
+ ): AssistantStream => {
43
+ return new ReadableStream({
44
+ start(c) {
45
+ return readable.start?.(new TextStreamControllerImpl(c));
46
+ },
47
+ pull(c) {
48
+ return readable.pull?.(new TextStreamControllerImpl(c));
49
+ },
50
+ cancel(c) {
51
+ return readable.cancel?.(c);
52
+ },
53
+ });
54
+ };
55
+
56
+ export const createTextStreamController = () => {
57
+ let controller!: TextStreamController;
58
+ const stream = createTextStream({
59
+ start(c) {
60
+ controller = c;
61
+ },
62
+ });
63
+ return [stream, controller] as const;
64
+ };
@@ -0,0 +1,105 @@
1
+ import { AssistantStream } from "../AssistantStream";
2
+ import { AssistantStreamChunk } from "../AssistantStreamChunk";
3
+ import { ToolResponseInit } from "../tool/ToolResponse";
4
+ import { ReadonlyJSONValue } from "../../utils/json/json-value";
5
+ import { UnderlyingReadable } from "../utils/stream/UnderlyingReadable";
6
+ import { createTextStream, TextStreamController } from "./text";
7
+
8
+ export type ToolCallStreamController = {
9
+ argsText: TextStreamController;
10
+
11
+ setResponse(response: ToolResponseInit<ReadonlyJSONValue>): void;
12
+ close(): void;
13
+ };
14
+
15
+ class ToolCallStreamControllerImpl implements ToolCallStreamController {
16
+ private _isClosed = false;
17
+
18
+ private _mergeTask: Promise<void>;
19
+ constructor(
20
+ private _controller: ReadableStreamDefaultController<AssistantStreamChunk>,
21
+ ) {
22
+ const stream = createTextStream({
23
+ start: (c) => {
24
+ this._argsTextController = c;
25
+ },
26
+ });
27
+ this._mergeTask = stream.pipeTo(
28
+ new WritableStream({
29
+ write: (chunk) => {
30
+ switch (chunk.type) {
31
+ case "text-delta":
32
+ this._controller.enqueue(chunk);
33
+ break;
34
+
35
+ case "part-finish":
36
+ this._controller.enqueue({
37
+ type: "tool-call-args-text-finish",
38
+ path: [],
39
+ });
40
+ break;
41
+
42
+ default:
43
+ throw new Error(`Unexpected chunk type: ${chunk.type}`);
44
+ }
45
+ },
46
+ }),
47
+ );
48
+ }
49
+
50
+ get argsText() {
51
+ return this._argsTextController;
52
+ }
53
+
54
+ private _argsTextController!: TextStreamController;
55
+
56
+ setResponse(response: ToolResponseInit<ReadonlyJSONValue>) {
57
+ this._controller.enqueue({
58
+ type: "result",
59
+ path: [],
60
+ artifact: response.artifact,
61
+ result: response.result,
62
+ isError: response.isError ?? false,
63
+ });
64
+ }
65
+
66
+ async close() {
67
+ if (this._isClosed) return;
68
+
69
+ this._isClosed = true;
70
+ this._argsTextController.close();
71
+ await this._mergeTask;
72
+
73
+ this._controller.enqueue({
74
+ type: "part-finish",
75
+ path: [],
76
+ });
77
+ this._controller.close();
78
+ }
79
+ }
80
+
81
+ export const createToolCallStream = (
82
+ readable: UnderlyingReadable<ToolCallStreamController>,
83
+ ): AssistantStream => {
84
+ return new ReadableStream({
85
+ start(c) {
86
+ return readable.start?.(new ToolCallStreamControllerImpl(c));
87
+ },
88
+ pull(c) {
89
+ return readable.pull?.(new ToolCallStreamControllerImpl(c));
90
+ },
91
+ cancel(c) {
92
+ return readable.cancel?.(c);
93
+ },
94
+ });
95
+ };
96
+
97
+ export const createToolCallStreamController = () => {
98
+ let controller!: ToolCallStreamController;
99
+ const stream = createToolCallStream({
100
+ start(c) {
101
+ controller = c;
102
+ },
103
+ });
104
+ return [stream, controller] as const;
105
+ };
@@ -0,0 +1,68 @@
1
+ import { AssistantStreamEncoder } from "../AssistantStream";
2
+ import { AssistantStreamChunk } from "../AssistantStreamChunk";
3
+ import { AssistantTransformStream } from "../utils/stream/AssistantTransformStream";
4
+ import { PipeableTransformStream } from "../utils/stream/PipeableTransformStream";
5
+
6
+ export class PlainTextEncoder
7
+ extends PipeableTransformStream<AssistantStreamChunk, Uint8Array>
8
+ implements AssistantStreamEncoder
9
+ {
10
+ headers = new Headers({
11
+ "Content-Type": "text/plain; charset=utf-8",
12
+ "x-vercel-ai-data-stream": "v1",
13
+ });
14
+
15
+ constructor() {
16
+ super((readable) => {
17
+ const transform = new TransformStream<AssistantStreamChunk, string>({
18
+ transform(chunk, controller) {
19
+ const type = chunk.type;
20
+ switch (type) {
21
+ case "text-delta":
22
+ controller.enqueue(chunk.textDelta);
23
+ break;
24
+
25
+ default:
26
+ const unsupportedType:
27
+ | "part-start"
28
+ | "part-finish"
29
+ | "tool-call-args-text-finish"
30
+ | "data"
31
+ | "step-start"
32
+ | "step-finish"
33
+ | "message-finish"
34
+ | "annotations"
35
+ | "tool-call-begin"
36
+ | "tool-call-delta"
37
+ | "result"
38
+ | "error" = type;
39
+ throw new Error(`unsupported chunk type: ${unsupportedType}`);
40
+ }
41
+ },
42
+ });
43
+
44
+ return readable
45
+ .pipeThrough(transform)
46
+ .pipeThrough(new TextEncoderStream());
47
+ });
48
+ }
49
+ }
50
+
51
+ export class PlainTextDecoder extends PipeableTransformStream<
52
+ Uint8Array,
53
+ AssistantStreamChunk
54
+ > {
55
+ constructor() {
56
+ super((readable) => {
57
+ const transform = new AssistantTransformStream<string>({
58
+ transform(chunk, controller) {
59
+ controller.appendText(chunk);
60
+ },
61
+ });
62
+
63
+ return readable
64
+ .pipeThrough(new TextDecoderStream())
65
+ .pipeThrough(transform);
66
+ });
67
+ }
68
+ }