Skip to content

Commit ac20a25

Browse files
shudingMaxLeiter
andauthoredMar 1, 2024··
Fix render() generator cases and use onText (#1021)
Co-authored-by: Max Leiter <max.leiter@vercel.com>
1 parent b88778f commit ac20a25

File tree

2 files changed

+39
-17
lines changed

2 files changed

+39
-17
lines changed
 

‎.changeset/late-pugs-cheer.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'ai': patch
3+
---
4+
5+
ai/rsc: fix text response and async generator

‎packages/core/rsc/streamable.tsx

+34-17
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,12 @@ export function render<
227227
)
228228
: undefined;
229229

230+
if (functions && tools) {
231+
throw new Error(
232+
"You can't have both functions and tools defined. Please choose one or the other.",
233+
);
234+
}
235+
230236
let finished: ReturnType<typeof createResolvablePromise> | undefined;
231237

232238
async function handleRender(
@@ -254,20 +260,23 @@ export function render<
254260
typeof value === 'object' &&
255261
Symbol.asyncIterator in value
256262
) {
257-
for await (const node of value as AsyncGenerator<
263+
const it = value as AsyncGenerator<
258264
React.ReactNode,
259265
React.ReactNode,
260266
void
261-
>) {
262-
res.update(node);
267+
>;
268+
while (true) {
269+
const { done, value } = await it.next();
270+
res.update(value);
271+
if (done) break;
263272
}
264273
finished?.resolve(void 0);
265274
} else if (value && typeof value === 'object' && Symbol.iterator in value) {
266275
const it = value as Generator<React.ReactNode, React.ReactNode, void>;
267276
while (true) {
268277
const { done, value } = it.next();
269-
if (done) break;
270278
res.update(value);
279+
if (done) break;
271280
}
272281
finished?.resolve(void 0);
273282
} else {
@@ -299,14 +308,19 @@ export function render<
299308
: {}),
300309
})) as any,
301310
{
302-
async experimental_onFunctionCall(functionCallPayload) {
303-
hasFunction = true;
304-
handleRender(
305-
functionCallPayload.arguments,
306-
options.functions?.[functionCallPayload.name as any]?.render,
307-
ui,
308-
);
309-
},
311+
...(functions
312+
? {
313+
async experimental_onFunctionCall(functionCallPayload) {
314+
hasFunction = true;
315+
handleRender(
316+
functionCallPayload.arguments,
317+
options.functions?.[functionCallPayload.name as any]
318+
?.render,
319+
ui,
320+
);
321+
},
322+
}
323+
: {}),
310324
...(tools
311325
? {
312326
async experimental_onToolCall(toolCallPayload: any) {
@@ -323,15 +337,18 @@ export function render<
323337
},
324338
}
325339
: {}),
326-
onToken(token) {
327-
text += token;
328-
if (hasFunction) return;
340+
onText(chunk) {
341+
text += chunk;
329342
handleRender({ content: text, done: false }, options.text, ui);
330343
},
331344
async onFinal() {
332-
if (hasFunction) return;
333-
handleRender({ content: text, done: true }, options.text, ui);
345+
if (hasFunction) {
346+
await finished?.promise;
347+
ui.done();
348+
return;
349+
}
334350

351+
handleRender({ content: text, done: true }, options.text, ui);
335352
await finished?.promise;
336353
ui.done();
337354
},

0 commit comments

Comments
 (0)
Please sign in to comment.