@convex-dev/agent 0.2.5-alpha.0 → 0.2.6-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client/definePlaygroundAPI.d.ts +248 -18
- package/dist/client/definePlaygroundAPI.d.ts.map +1 -1
- package/dist/client/index.d.ts +211 -33
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +56 -52
- package/dist/client/index.js.map +1 -1
- package/dist/client/messages.d.ts +50 -4
- package/dist/client/messages.d.ts.map +1 -1
- package/dist/client/mockModel.d.ts +30 -0
- package/dist/client/mockModel.d.ts.map +1 -0
- package/dist/client/mockModel.js +153 -0
- package/dist/client/mockModel.js.map +1 -0
- package/dist/client/search.d.ts +50 -4
- package/dist/client/search.d.ts.map +1 -1
- package/dist/client/streaming.d.ts +603 -46
- package/dist/client/streaming.d.ts.map +1 -1
- package/dist/client/streaming.js +30 -35
- package/dist/client/streaming.js.map +1 -1
- package/dist/client/textStreamParts.d.ts +5 -0
- package/dist/client/textStreamParts.d.ts.map +1 -0
- package/dist/{parts.js → client/textStreamParts.js} +18 -1
- package/dist/client/textStreamParts.js.map +1 -0
- package/dist/component/_generated/api.d.ts +240 -16
- package/dist/component/messages.d.ts +549 -43
- package/dist/component/messages.d.ts.map +1 -1
- package/dist/component/messages.js +1 -1
- package/dist/component/messages.js.map +1 -1
- package/dist/component/schema.d.ts +1546 -98
- package/dist/component/schema.d.ts.map +1 -1
- package/dist/mapping.d.ts.map +1 -1
- package/dist/mapping.js +30 -18
- package/dist/mapping.js.map +1 -1
- package/dist/react/useSmoothText.d.ts +1 -1
- package/dist/react/useSmoothText.d.ts.map +1 -1
- package/dist/react/useSmoothText.js +30 -22
- package/dist/react/useSmoothText.js.map +1 -1
- package/dist/validators.d.ts +3740 -201
- package/dist/validators.d.ts.map +1 -1
- package/dist/validators.js +10 -1
- package/dist/validators.js.map +1 -1
- package/package.json +1 -1
- package/src/client/index.ts +90 -82
- package/src/client/mockModel.ts +195 -0
- package/src/client/streaming.ts +46 -54
- package/src/{parts.ts → client/textStreamParts.ts} +33 -1
- package/src/component/_generated/api.d.ts +240 -16
- package/src/component/messages.ts +1 -1
- package/src/mapping.test.ts +50 -1
- package/src/mapping.ts +42 -17
- package/src/react/toUIMessages.test.ts +16 -4
- package/src/react/useSmoothText.ts +40 -31
- package/src/validators.ts +23 -1
- package/dist/parts.d.ts +0 -3
- package/dist/parts.d.ts.map +0 -1
- package/dist/parts.js.map +0 -1
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
LanguageModelV2,
|
|
3
|
+
LanguageModelV2StreamPart,
|
|
4
|
+
} from "@ai-sdk/provider";
|
|
5
|
+
import type { ReasoningPart, TextPart } from "@ai-sdk/provider-utils";
|
|
6
|
+
import { simulateReadableStream } from "ai";
|
|
7
|
+
|
|
8
|
+
const longDefaultText = `
|
|
9
|
+
A A A A A A A A A A A A A A A
|
|
10
|
+
B B B B B B B B B B B B B B B
|
|
11
|
+
C C C C C C C C C C C C C C C
|
|
12
|
+
D D D D D D D D D D D D D D D
|
|
13
|
+
`;
|
|
14
|
+
const defaultUsage = { outputTokens: 10, inputTokens: 3, totalTokens: 13 };
|
|
15
|
+
|
|
16
|
+
export type MockModelArgs = {
|
|
17
|
+
provider?: LanguageModelV2["provider"];
|
|
18
|
+
modelId?: LanguageModelV2["modelId"];
|
|
19
|
+
supportedUrls?:
|
|
20
|
+
| LanguageModelV2["supportedUrls"]
|
|
21
|
+
| (() => LanguageModelV2["supportedUrls"]);
|
|
22
|
+
chunkDelayInMs?: number;
|
|
23
|
+
initialDelayInMs?: number;
|
|
24
|
+
// provide either content or doGenerate & doStream
|
|
25
|
+
content?: (TextPart | ReasoningPart)[];
|
|
26
|
+
doGenerate?: LanguageModelV2["doGenerate"];
|
|
27
|
+
doStream?: LanguageModelV2["doStream"];
|
|
28
|
+
fail?:
|
|
29
|
+
| boolean
|
|
30
|
+
| {
|
|
31
|
+
probability?: number;
|
|
32
|
+
error?: string;
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export function mockModel(args?: MockModelArgs): LanguageModelV2 {
|
|
37
|
+
return new MockLanguageModel(args ?? {});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export class MockLanguageModel implements LanguageModelV2 {
|
|
41
|
+
readonly specificationVersion = "v2";
|
|
42
|
+
|
|
43
|
+
private _supportedUrls: () => LanguageModelV2["supportedUrls"];
|
|
44
|
+
|
|
45
|
+
readonly provider: LanguageModelV2["provider"];
|
|
46
|
+
readonly modelId: LanguageModelV2["modelId"];
|
|
47
|
+
|
|
48
|
+
doGenerate: LanguageModelV2["doGenerate"];
|
|
49
|
+
doStream: LanguageModelV2["doStream"];
|
|
50
|
+
|
|
51
|
+
doGenerateCalls: Parameters<LanguageModelV2["doGenerate"]>[0][] = [];
|
|
52
|
+
doStreamCalls: Parameters<LanguageModelV2["doStream"]>[0][] = [];
|
|
53
|
+
|
|
54
|
+
constructor(args: MockModelArgs) {
|
|
55
|
+
this.provider = args.provider || "mock-provider";
|
|
56
|
+
this.modelId = args.modelId || "mock-model-id";
|
|
57
|
+
const {
|
|
58
|
+
content = [{ type: "text", text: longDefaultText }],
|
|
59
|
+
chunkDelayInMs = 200,
|
|
60
|
+
initialDelayInMs = 1000,
|
|
61
|
+
supportedUrls = {},
|
|
62
|
+
} = args;
|
|
63
|
+
const fail =
|
|
64
|
+
args.fail &&
|
|
65
|
+
(args.fail === true ||
|
|
66
|
+
!args.fail.probability ||
|
|
67
|
+
Math.random() < args.fail.probability);
|
|
68
|
+
const error =
|
|
69
|
+
(typeof args.fail === "object" && args.fail.error) ||
|
|
70
|
+
"Mock error message";
|
|
71
|
+
|
|
72
|
+
const chunks: LanguageModelV2StreamPart[] = [
|
|
73
|
+
{ type: "stream-start", warnings: [] },
|
|
74
|
+
];
|
|
75
|
+
chunks.push(
|
|
76
|
+
...content.flatMap((c, ci) => {
|
|
77
|
+
const deltas = c.text.split(" ");
|
|
78
|
+
let parts: LanguageModelV2StreamPart[] = [];
|
|
79
|
+
if (c.type === "reasoning") {
|
|
80
|
+
parts.push({
|
|
81
|
+
type: "reasoning-start",
|
|
82
|
+
id: `${ci}-reasoning-start`,
|
|
83
|
+
});
|
|
84
|
+
parts.push(
|
|
85
|
+
...deltas.map(
|
|
86
|
+
(delta, di) =>
|
|
87
|
+
({
|
|
88
|
+
type: "reasoning-delta",
|
|
89
|
+
delta,
|
|
90
|
+
id: `${ci}-reasoning-${di}`,
|
|
91
|
+
providerMetadata: {
|
|
92
|
+
mockProvider: { mock: { reasoningDetails: null } },
|
|
93
|
+
},
|
|
94
|
+
}) satisfies LanguageModelV2StreamPart,
|
|
95
|
+
),
|
|
96
|
+
);
|
|
97
|
+
parts.push({
|
|
98
|
+
type: "reasoning-end",
|
|
99
|
+
id: `${ci}-reasoning-end`,
|
|
100
|
+
});
|
|
101
|
+
} else if (c.type === "text") {
|
|
102
|
+
parts.push({
|
|
103
|
+
type: "text-start",
|
|
104
|
+
id: `${ci}-text-start`,
|
|
105
|
+
});
|
|
106
|
+
parts = deltas.map((delta, di) => ({
|
|
107
|
+
type: "text-delta",
|
|
108
|
+
delta,
|
|
109
|
+
id: `${ci}-text-${di}`,
|
|
110
|
+
}));
|
|
111
|
+
parts.push({
|
|
112
|
+
type: "text-end",
|
|
113
|
+
id: `${ci}-text-end`,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
return parts;
|
|
117
|
+
}),
|
|
118
|
+
);
|
|
119
|
+
if (fail) {
|
|
120
|
+
chunks.push({
|
|
121
|
+
type: "error",
|
|
122
|
+
error,
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
chunks.push({
|
|
126
|
+
type: "finish",
|
|
127
|
+
finishReason: fail ? "error" : "stop",
|
|
128
|
+
usage: defaultUsage,
|
|
129
|
+
providerMetadata: {
|
|
130
|
+
mockProvider: { mock: "mock metadata" },
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
this.doGenerate = async (options) => {
|
|
134
|
+
this.doGenerateCalls.push(options);
|
|
135
|
+
|
|
136
|
+
if (fail) {
|
|
137
|
+
throw new Error(error);
|
|
138
|
+
}
|
|
139
|
+
if (typeof args.doGenerate === "function") {
|
|
140
|
+
return args.doGenerate(options);
|
|
141
|
+
} else if (Array.isArray(args.doGenerate)) {
|
|
142
|
+
return args.doGenerate[this.doGenerateCalls.length];
|
|
143
|
+
} else if (content) {
|
|
144
|
+
return {
|
|
145
|
+
content,
|
|
146
|
+
finishReason: "stop",
|
|
147
|
+
usage: defaultUsage,
|
|
148
|
+
providerMetadata: { mockProvider: { mock: "mock metadata" } },
|
|
149
|
+
warnings: [],
|
|
150
|
+
};
|
|
151
|
+
} else {
|
|
152
|
+
throw new Error("Unexpected: no content or doGenerate");
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
this._supportedUrls =
|
|
156
|
+
typeof supportedUrls === "function"
|
|
157
|
+
? supportedUrls
|
|
158
|
+
: async () => supportedUrls;
|
|
159
|
+
this.doStream = async (options) => {
|
|
160
|
+
this.doStreamCalls.push(options);
|
|
161
|
+
|
|
162
|
+
if (typeof args.doStream === "function") {
|
|
163
|
+
return args.doStream(options);
|
|
164
|
+
} else if (Array.isArray(args.doStream)) {
|
|
165
|
+
return args.doStream[this.doStreamCalls.length];
|
|
166
|
+
} else if (content) {
|
|
167
|
+
const stream = simulateReadableStream({
|
|
168
|
+
chunks,
|
|
169
|
+
initialDelayInMs,
|
|
170
|
+
chunkDelayInMs,
|
|
171
|
+
});
|
|
172
|
+
if (options.abortSignal) {
|
|
173
|
+
throw new Error("abortSignal in mock model");
|
|
174
|
+
}
|
|
175
|
+
return {
|
|
176
|
+
stream,
|
|
177
|
+
request: { body: {} },
|
|
178
|
+
response: { headers: {} },
|
|
179
|
+
};
|
|
180
|
+
} else if (args.doStream) {
|
|
181
|
+
return args.doStream;
|
|
182
|
+
} else {
|
|
183
|
+
throw new Error("Provide either content or doStream");
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
this._supportedUrls =
|
|
187
|
+
typeof supportedUrls === "function"
|
|
188
|
+
? supportedUrls
|
|
189
|
+
: async () => supportedUrls;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
get supportedUrls() {
|
|
193
|
+
return this._supportedUrls();
|
|
194
|
+
}
|
|
195
|
+
}
|
package/src/client/streaming.ts
CHANGED
|
@@ -1,10 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
type ChunkDetector,
|
|
3
|
-
smoothStream,
|
|
4
|
-
type StreamTextTransform,
|
|
5
|
-
type TextStreamPart,
|
|
6
|
-
type ToolSet,
|
|
7
|
-
} from "ai";
|
|
1
|
+
import { type ChunkDetector } from "ai";
|
|
8
2
|
import {
|
|
9
3
|
vStreamDelta,
|
|
10
4
|
vStreamMessage,
|
|
@@ -21,8 +15,6 @@ import type {
|
|
|
21
15
|
RunQueryCtx,
|
|
22
16
|
SyncStreamsReturnValue,
|
|
23
17
|
} from "./types.js";
|
|
24
|
-
import { omit } from "convex-helpers";
|
|
25
|
-
import { serializeTextStreamingPartsV5 } from "../parts.js";
|
|
26
18
|
import { v } from "convex/values";
|
|
27
19
|
import { vMessageDoc } from "../component/schema.js";
|
|
28
20
|
|
|
@@ -161,33 +153,21 @@ export const DEFAULT_STREAMING_OPTIONS = {
|
|
|
161
153
|
returnImmediately: false,
|
|
162
154
|
} satisfies StreamingOptions;
|
|
163
155
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
return existing;
|
|
173
|
-
}
|
|
174
|
-
const chunking =
|
|
175
|
-
typeof options === "boolean"
|
|
176
|
-
? DEFAULT_STREAMING_OPTIONS.chunking
|
|
177
|
-
: options.chunking;
|
|
178
|
-
const transforms = Array.isArray(existing)
|
|
179
|
-
? existing
|
|
180
|
-
: existing
|
|
181
|
-
? [existing]
|
|
182
|
-
: [];
|
|
183
|
-
transforms.push(smoothStream({ delayInMs: null, chunking }));
|
|
184
|
-
return transforms;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
export class DeltaStreamer {
|
|
156
|
+
/**
|
|
157
|
+
* DeltaStreamer can be used to save a stream of "parts" by writing
|
|
158
|
+
* batches of them in "deltas" to the database so clients can subscribe
|
|
159
|
+
* (using the syncStreams utility and client hooks) and re-hydrate the stream.
|
|
160
|
+
* You can optionally compress the parts, e.g. concatenating text deltas, to
|
|
161
|
+
* optimize the data in transit.
|
|
162
|
+
*/
|
|
163
|
+
export class DeltaStreamer<T> {
|
|
188
164
|
public streamId: string | undefined;
|
|
189
|
-
public readonly
|
|
190
|
-
|
|
165
|
+
public readonly config: {
|
|
166
|
+
stream: Required<StreamingOptions>;
|
|
167
|
+
onAsyncAbort: (reason: string) => Promise<void>;
|
|
168
|
+
compress: ((parts: T[]) => T[]) | null;
|
|
169
|
+
};
|
|
170
|
+
#nextParts: T[] = [];
|
|
191
171
|
#latestWrite: number = 0;
|
|
192
172
|
#ongoingWrite: Promise<void> | undefined;
|
|
193
173
|
#cursor: number = 0;
|
|
@@ -196,7 +176,12 @@ export class DeltaStreamer {
|
|
|
196
176
|
constructor(
|
|
197
177
|
public readonly component: AgentComponent,
|
|
198
178
|
public readonly ctx: RunActionCtx,
|
|
199
|
-
|
|
179
|
+
config: {
|
|
180
|
+
stream: true | StreamingOptions;
|
|
181
|
+
onAsyncAbort: (reason: string) => Promise<void>;
|
|
182
|
+
abortSignal: AbortSignal | undefined;
|
|
183
|
+
compress: ((parts: T[]) => T[]) | null;
|
|
184
|
+
},
|
|
200
185
|
public readonly metadata: {
|
|
201
186
|
threadId: string;
|
|
202
187
|
userId?: string;
|
|
@@ -206,45 +191,49 @@ export class DeltaStreamer {
|
|
|
206
191
|
model?: string;
|
|
207
192
|
provider?: string;
|
|
208
193
|
providerOptions?: ProviderOptions;
|
|
209
|
-
abortSignal?: AbortSignal;
|
|
210
194
|
},
|
|
211
195
|
) {
|
|
212
|
-
this.
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
196
|
+
this.config = {
|
|
197
|
+
stream:
|
|
198
|
+
config.stream === true
|
|
199
|
+
? DEFAULT_STREAMING_OPTIONS
|
|
200
|
+
: { ...DEFAULT_STREAMING_OPTIONS, ...config },
|
|
201
|
+
onAsyncAbort: config.onAsyncAbort,
|
|
202
|
+
compress: config.compress,
|
|
203
|
+
};
|
|
216
204
|
this.#nextParts = [];
|
|
217
205
|
this.abortController = new AbortController();
|
|
218
|
-
if (
|
|
219
|
-
|
|
206
|
+
if (config.abortSignal) {
|
|
207
|
+
config.abortSignal.addEventListener("abort", async () => {
|
|
208
|
+
if (this.abortController.signal.aborted) {
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
220
211
|
if (this.streamId) {
|
|
221
212
|
this.abortController.abort();
|
|
222
|
-
const finalDelta = this.#createDelta();
|
|
223
213
|
await this.#ongoingWrite;
|
|
224
214
|
await this.ctx.runMutation(this.component.streams.abort, {
|
|
225
215
|
streamId: this.streamId,
|
|
226
216
|
reason: "abortSignal",
|
|
227
|
-
finalDelta,
|
|
228
217
|
});
|
|
229
218
|
}
|
|
230
219
|
});
|
|
231
220
|
}
|
|
232
221
|
}
|
|
233
222
|
|
|
234
|
-
public async addParts(parts:
|
|
223
|
+
public async addParts(parts: T[]) {
|
|
235
224
|
if (this.abortController.signal.aborted) {
|
|
236
225
|
return;
|
|
237
226
|
}
|
|
238
227
|
if (!this.streamId) {
|
|
239
228
|
this.streamId = await this.ctx.runMutation(
|
|
240
229
|
this.component.streams.create,
|
|
241
|
-
|
|
230
|
+
this.metadata,
|
|
242
231
|
);
|
|
243
232
|
}
|
|
244
233
|
this.#nextParts.push(...parts);
|
|
245
234
|
if (
|
|
246
235
|
!this.#ongoingWrite &&
|
|
247
|
-
Date.now() - this.#latestWrite >= this.
|
|
236
|
+
Date.now() - this.#latestWrite >= this.config.stream.throttleMs
|
|
248
237
|
) {
|
|
249
238
|
this.#ongoingWrite = this.#sendDelta();
|
|
250
239
|
}
|
|
@@ -265,16 +254,21 @@ export class DeltaStreamer {
|
|
|
265
254
|
delta,
|
|
266
255
|
);
|
|
267
256
|
if (!success) {
|
|
257
|
+
await this.config.onAsyncAbort("async abort");
|
|
268
258
|
this.abortController.abort();
|
|
259
|
+
return;
|
|
269
260
|
}
|
|
270
261
|
} catch (e) {
|
|
262
|
+
await this.config.onAsyncAbort(
|
|
263
|
+
e instanceof Error ? e.message : "unknown error",
|
|
264
|
+
);
|
|
271
265
|
this.abortController.abort();
|
|
272
266
|
throw e;
|
|
273
267
|
}
|
|
274
268
|
// Now that we've sent the delta, check if we need to send another one.
|
|
275
269
|
if (
|
|
276
270
|
this.#nextParts.length > 0 &&
|
|
277
|
-
Date.now() - this.#latestWrite >= this.
|
|
271
|
+
Date.now() - this.#latestWrite >= this.config.stream.throttleMs
|
|
278
272
|
) {
|
|
279
273
|
// We send again immediately with the accumulated deltas.
|
|
280
274
|
this.#ongoingWrite = this.#sendDelta();
|
|
@@ -290,7 +284,9 @@ export class DeltaStreamer {
|
|
|
290
284
|
const start = this.#cursor;
|
|
291
285
|
const end = start + this.#nextParts.length;
|
|
292
286
|
this.#cursor = end;
|
|
293
|
-
const parts =
|
|
287
|
+
const parts = this.config.compress
|
|
288
|
+
? this.config.compress(this.#nextParts)
|
|
289
|
+
: this.#nextParts;
|
|
294
290
|
this.#nextParts = [];
|
|
295
291
|
if (!this.streamId) {
|
|
296
292
|
throw new Error("Creating a delta before the stream is created");
|
|
@@ -302,11 +298,9 @@ export class DeltaStreamer {
|
|
|
302
298
|
if (!this.streamId) {
|
|
303
299
|
return;
|
|
304
300
|
}
|
|
305
|
-
const finalDelta = this.#createDelta();
|
|
306
301
|
await this.#ongoingWrite;
|
|
307
302
|
await this.ctx.runMutation(this.component.streams.finish, {
|
|
308
303
|
streamId: this.streamId,
|
|
309
|
-
finalDelta,
|
|
310
304
|
});
|
|
311
305
|
}
|
|
312
306
|
|
|
@@ -318,12 +312,10 @@ export class DeltaStreamer {
|
|
|
318
312
|
if (!this.streamId) {
|
|
319
313
|
return;
|
|
320
314
|
}
|
|
321
|
-
const finalDelta = this.#createDelta();
|
|
322
315
|
await this.#ongoingWrite;
|
|
323
316
|
await this.ctx.runMutation(this.component.streams.abort, {
|
|
324
317
|
streamId: this.streamId,
|
|
325
318
|
reason,
|
|
326
|
-
finalDelta,
|
|
327
319
|
});
|
|
328
320
|
}
|
|
329
321
|
}
|
|
@@ -1,4 +1,13 @@
|
|
|
1
|
-
import
|
|
1
|
+
import {
|
|
2
|
+
smoothStream,
|
|
3
|
+
type StreamTextTransform,
|
|
4
|
+
type ToolSet,
|
|
5
|
+
type TextStreamPart,
|
|
6
|
+
} from "ai";
|
|
7
|
+
import {
|
|
8
|
+
DEFAULT_STREAMING_OPTIONS,
|
|
9
|
+
type StreamingOptions,
|
|
10
|
+
} from "./streaming.js";
|
|
2
11
|
|
|
3
12
|
export function serializeTextStreamingPartsV5(
|
|
4
13
|
parts: TextStreamPart<ToolSet>[],
|
|
@@ -37,3 +46,26 @@ export function serializeTextStreamingPartsV5(
|
|
|
37
46
|
}
|
|
38
47
|
return compressed;
|
|
39
48
|
}
|
|
49
|
+
|
|
50
|
+
export function mergeTransforms<TOOLS extends ToolSet>(
|
|
51
|
+
options: StreamingOptions | boolean | undefined,
|
|
52
|
+
existing:
|
|
53
|
+
| StreamTextTransform<TOOLS>
|
|
54
|
+
| Array<StreamTextTransform<TOOLS>>
|
|
55
|
+
| undefined,
|
|
56
|
+
) {
|
|
57
|
+
if (!options) {
|
|
58
|
+
return existing;
|
|
59
|
+
}
|
|
60
|
+
const chunking =
|
|
61
|
+
typeof options === "boolean"
|
|
62
|
+
? DEFAULT_STREAMING_OPTIONS.chunking
|
|
63
|
+
: options.chunking;
|
|
64
|
+
const transforms = Array.isArray(existing)
|
|
65
|
+
? existing
|
|
66
|
+
: existing
|
|
67
|
+
? [existing]
|
|
68
|
+
: [];
|
|
69
|
+
transforms.push(smoothStream({ delayInMs: null, chunking }));
|
|
70
|
+
return transforms;
|
|
71
|
+
}
|