@ai-sdk/rsc 3.0.122 → 3.0.124

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.
@@ -1,748 +1,606 @@
1
- // src/ai-state.tsx
2
1
  import * as jsondiffpatch from "jsondiffpatch";
3
- import { AsyncLocalStorage } from "async_hooks";
4
-
5
- // src/util/create-resolvable-promise.ts
2
+ import { AsyncLocalStorage } from "node:async_hooks";
3
+ import * as React from "react";
4
+ import { Suspense } from "react";
5
+ import { InternalAIProvider } from "./rsc-shared.js";
6
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
7
+ import { safeParseJSON } from "@ai-sdk/provider-utils";
8
+ import { InvalidToolInputError, NoSuchToolError } from "ai";
9
+ import { asLanguageModelUsage, convertToLanguageModelPrompt, prepareLanguageModelCallOptions, prepareRetries, prepareToolChoice, prepareTools, standardizePrompt } from "ai/internal";
10
+ //#region src/util/create-resolvable-promise.ts
11
+ /**
12
+ * Creates a Promise with externally accessible resolve and reject functions.
13
+ *
14
+ * @template T - The type of the value that the Promise will resolve to.
15
+ * @returns An object containing:
16
+ * - promise: A Promise that can be resolved or rejected externally.
17
+ * - resolve: A function to resolve the Promise with a value of type T.
18
+ * - reject: A function to reject the Promise with an error.
19
+ */
6
20
  function createResolvablePromise() {
7
- let resolve;
8
- let reject;
9
- const promise = new Promise((res, rej) => {
10
- resolve = res;
11
- reject = rej;
12
- });
13
- return {
14
- promise,
15
- resolve,
16
- reject
17
- };
21
+ let resolve;
22
+ let reject;
23
+ return {
24
+ promise: new Promise((res, rej) => {
25
+ resolve = res;
26
+ reject = rej;
27
+ }),
28
+ resolve,
29
+ reject
30
+ };
18
31
  }
19
-
20
- // src/util/is-function.ts
21
- var isFunction = (value) => typeof value === "function";
22
-
23
- // src/ai-state.tsx
24
- var asyncAIStateStorage = new AsyncLocalStorage();
32
+ //#endregion
33
+ //#region src/util/is-function.ts
34
+ /**
35
+ * Checks if the given value is a function.
36
+ *
37
+ * @param {unknown} value - The value to check.
38
+ * @returns {boolean} True if the value is a function, false otherwise.
39
+ */
40
+ const isFunction = (value) => typeof value === "function";
41
+ //#endregion
42
+ //#region src/ai-state.tsx
43
+ const asyncAIStateStorage = new AsyncLocalStorage();
25
44
  function getAIStateStoreOrThrow(message) {
26
- const store = asyncAIStateStorage.getStore();
27
- if (!store) {
28
- throw new Error(message);
29
- }
30
- return store;
45
+ const store = asyncAIStateStorage.getStore();
46
+ if (!store) throw new Error(message);
47
+ return store;
31
48
  }
32
49
  function withAIState({ state, options }, fn) {
33
- return asyncAIStateStorage.run(
34
- {
35
- currentState: JSON.parse(JSON.stringify(state)),
36
- // deep clone object
37
- originalState: state,
38
- sealed: false,
39
- options
40
- },
41
- fn
42
- );
50
+ return asyncAIStateStorage.run({
51
+ currentState: JSON.parse(JSON.stringify(state)),
52
+ originalState: state,
53
+ sealed: false,
54
+ options
55
+ }, fn);
43
56
  }
44
57
  function getAIStateDeltaPromise() {
45
- const store = getAIStateStoreOrThrow("Internal error occurred.");
46
- return store.mutationDeltaPromise;
58
+ return getAIStateStoreOrThrow("Internal error occurred.").mutationDeltaPromise;
47
59
  }
48
60
  function sealMutableAIState() {
49
- const store = getAIStateStoreOrThrow("Internal error occurred.");
50
- store.sealed = true;
61
+ const store = getAIStateStoreOrThrow("Internal error occurred.");
62
+ store.sealed = true;
51
63
  }
52
64
  function getAIState(...args) {
53
- const store = getAIStateStoreOrThrow(
54
- "`getAIState` must be called within an AI Action."
55
- );
56
- if (args.length > 0) {
57
- const key = args[0];
58
- if (typeof store.currentState !== "object") {
59
- throw new Error(
60
- `You can't get the "${String(
61
- key
62
- )}" field from the AI state because it's not an object.`
63
- );
64
- }
65
- return store.currentState[key];
66
- }
67
- return store.currentState;
65
+ const store = getAIStateStoreOrThrow("`getAIState` must be called within an AI Action.");
66
+ if (args.length > 0) {
67
+ const key = args[0];
68
+ if (typeof store.currentState !== "object") throw new Error(`You can't get the "${String(key)}" field from the AI state because it's not an object.`);
69
+ return store.currentState[key];
70
+ }
71
+ return store.currentState;
68
72
  }
69
73
  function getMutableAIState(...args) {
70
- const store = getAIStateStoreOrThrow(
71
- "`getMutableAIState` must be called within an AI Action."
72
- );
73
- if (store.sealed) {
74
- throw new Error(
75
- "`getMutableAIState` must be called before returning from an AI Action. Please move it to the top level of the Action's function body."
76
- );
77
- }
78
- if (!store.mutationDeltaPromise) {
79
- const { promise, resolve } = createResolvablePromise();
80
- store.mutationDeltaPromise = promise;
81
- store.mutationDeltaResolve = resolve;
82
- }
83
- function doUpdate(newState, done) {
84
- if (args.length > 0) {
85
- if (typeof store.currentState !== "object") {
86
- const key = args[0];
87
- throw new Error(
88
- `You can't modify the "${String(
89
- key
90
- )}" field of the AI state because it's not an object.`
91
- );
92
- }
93
- }
94
- if (isFunction(newState)) {
95
- if (args.length > 0) {
96
- store.currentState[args[0]] = newState(store.currentState[args[0]]);
97
- } else {
98
- store.currentState = newState(store.currentState);
99
- }
100
- } else {
101
- if (args.length > 0) {
102
- store.currentState[args[0]] = newState;
103
- } else {
104
- store.currentState = newState;
105
- }
106
- }
107
- store.options.onSetAIState?.({
108
- key: args.length > 0 ? args[0] : void 0,
109
- state: store.currentState,
110
- done
111
- });
112
- }
113
- const mutableState = {
114
- get: () => {
115
- if (args.length > 0) {
116
- const key = args[0];
117
- if (typeof store.currentState !== "object") {
118
- throw new Error(
119
- `You can't get the "${String(
120
- key
121
- )}" field from the AI state because it's not an object.`
122
- );
123
- }
124
- return store.currentState[key];
125
- }
126
- return store.currentState;
127
- },
128
- update: function update(newAIState) {
129
- doUpdate(newAIState, false);
130
- },
131
- done: function done(...doneArgs) {
132
- if (doneArgs.length > 0) {
133
- doUpdate(doneArgs[0], true);
134
- }
135
- const delta = jsondiffpatch.diff(store.originalState, store.currentState);
136
- store.mutationDeltaResolve(delta);
137
- }
138
- };
139
- return mutableState;
74
+ const store = getAIStateStoreOrThrow("`getMutableAIState` must be called within an AI Action.");
75
+ if (store.sealed) throw new Error("`getMutableAIState` must be called before returning from an AI Action. Please move it to the top level of the Action's function body.");
76
+ if (!store.mutationDeltaPromise) {
77
+ const { promise, resolve } = createResolvablePromise();
78
+ store.mutationDeltaPromise = promise;
79
+ store.mutationDeltaResolve = resolve;
80
+ }
81
+ function doUpdate(newState, done) {
82
+ if (args.length > 0) {
83
+ if (typeof store.currentState !== "object") {
84
+ const key = args[0];
85
+ throw new Error(`You can't modify the "${String(key)}" field of the AI state because it's not an object.`);
86
+ }
87
+ }
88
+ if (isFunction(newState)) {
89
+ if (args.length > 0) store.currentState[args[0]] = newState(store.currentState[args[0]]);
90
+ else store.currentState = newState(store.currentState);
91
+ } else if (args.length > 0) store.currentState[args[0]] = newState;
92
+ else store.currentState = newState;
93
+ store.options.onSetAIState?.({
94
+ key: args.length > 0 ? args[0] : void 0,
95
+ state: store.currentState,
96
+ done
97
+ });
98
+ }
99
+ return {
100
+ get: () => {
101
+ if (args.length > 0) {
102
+ const key = args[0];
103
+ if (typeof store.currentState !== "object") throw new Error(`You can't get the "${String(key)}" field from the AI state because it's not an object.`);
104
+ return store.currentState[key];
105
+ }
106
+ return store.currentState;
107
+ },
108
+ update: function update(newAIState) {
109
+ doUpdate(newAIState, false);
110
+ },
111
+ done: function done(...doneArgs) {
112
+ if (doneArgs.length > 0) doUpdate(doneArgs[0], true);
113
+ const delta = jsondiffpatch.diff(store.originalState, store.currentState);
114
+ store.mutationDeltaResolve(delta);
115
+ }
116
+ };
140
117
  }
141
-
142
- // src/provider.tsx
143
- import * as React from "react";
144
- import { InternalAIProvider } from "./rsc-shared.js";
145
- import { jsx } from "react/jsx-runtime";
146
- async function innerAction({
147
- action,
148
- options
149
- }, state, ...args) {
150
- "use server";
151
- return await withAIState(
152
- {
153
- state,
154
- options
155
- },
156
- async () => {
157
- const result = await action(...args);
158
- sealMutableAIState();
159
- return [getAIStateDeltaPromise(), result];
160
- }
161
- );
118
+ //#endregion
119
+ //#region src/provider.tsx
120
+ async function innerAction({ action, options }, state, ...args) {
121
+ "use server";
122
+ return await withAIState({
123
+ state,
124
+ options
125
+ }, async () => {
126
+ const result = await action(...args);
127
+ sealMutableAIState();
128
+ return [getAIStateDeltaPromise(), result];
129
+ });
162
130
  }
163
131
  function wrapAction(action, options) {
164
- return innerAction.bind(null, { action, options });
132
+ return innerAction.bind(null, {
133
+ action,
134
+ options
135
+ });
165
136
  }
166
- function createAI({
167
- actions,
168
- initialAIState,
169
- initialUIState,
170
- onSetAIState,
171
- onGetUIState
172
- }) {
173
- const wrappedActions = {};
174
- for (const name in actions) {
175
- wrappedActions[name] = wrapAction(actions[name], {
176
- onSetAIState
177
- });
178
- }
179
- const wrappedSyncUIState = onGetUIState ? wrapAction(onGetUIState, {}) : void 0;
180
- const AI = async (props) => {
181
- if ("useState" in React) {
182
- throw new Error(
183
- "This component can only be used inside Server Components."
184
- );
185
- }
186
- let uiState = props.initialUIState ?? initialUIState;
187
- let aiState = props.initialAIState ?? initialAIState;
188
- let aiStateDelta = void 0;
189
- if (wrappedSyncUIState) {
190
- const [newAIStateDelta, newUIState] = await wrappedSyncUIState(aiState);
191
- if (newUIState !== void 0) {
192
- aiStateDelta = newAIStateDelta;
193
- uiState = newUIState;
194
- }
195
- }
196
- return /* @__PURE__ */ jsx(
197
- InternalAIProvider,
198
- {
199
- wrappedActions,
200
- wrappedSyncUIState,
201
- initialUIState: uiState,
202
- initialAIState: aiState,
203
- initialAIStatePatch: aiStateDelta,
204
- children: props.children
205
- }
206
- );
207
- };
208
- return AI;
137
+ function createAI({ actions, initialAIState, initialUIState, onSetAIState, onGetUIState }) {
138
+ const wrappedActions = {};
139
+ for (const name in actions) wrappedActions[name] = wrapAction(actions[name], { onSetAIState });
140
+ const wrappedSyncUIState = onGetUIState ? wrapAction(onGetUIState, {}) : void 0;
141
+ const AI = async (props) => {
142
+ if ("useState" in React) throw new Error("This component can only be used inside Server Components.");
143
+ let uiState = props.initialUIState ?? initialUIState;
144
+ let aiState = props.initialAIState ?? initialAIState;
145
+ let aiStateDelta = void 0;
146
+ if (wrappedSyncUIState) {
147
+ const [newAIStateDelta, newUIState] = await wrappedSyncUIState(aiState);
148
+ if (newUIState !== void 0) {
149
+ aiStateDelta = newAIStateDelta;
150
+ uiState = newUIState;
151
+ }
152
+ }
153
+ return /* @__PURE__ */ jsx(InternalAIProvider, {
154
+ wrappedActions,
155
+ wrappedSyncUIState,
156
+ initialUIState: uiState,
157
+ initialAIState: aiState,
158
+ initialAIStatePatch: aiStateDelta,
159
+ children: props.children
160
+ });
161
+ };
162
+ return AI;
209
163
  }
210
-
211
- // src/stream-ui/stream-ui.tsx
212
- import {
213
- safeParseJSON
214
- } from "@ai-sdk/provider-utils";
215
- import {
216
- InvalidToolInputError,
217
- NoSuchToolError
218
- } from "ai";
219
- import {
220
- asLanguageModelUsage,
221
- convertToLanguageModelPrompt,
222
- prepareLanguageModelCallOptions,
223
- prepareRetries,
224
- prepareToolChoice,
225
- prepareTools,
226
- standardizePrompt
227
- } from "ai/internal";
228
-
229
- // src/util/constants.ts
230
- var HANGING_STREAM_WARNING_TIME_MS = 15 * 1e3;
231
-
232
- // src/streamable-ui/create-suspended-chunk.tsx
233
- import { Suspense } from "react";
234
- import { Fragment, jsx as jsx2, jsxs } from "react/jsx-runtime";
235
- var R = [
236
- async ({
237
- c: current,
238
- n: next
239
- }) => {
240
- const chunk = await next;
241
- if (chunk.done) {
242
- return chunk.value;
243
- }
244
- if (chunk.append) {
245
- return /* @__PURE__ */ jsxs(Fragment, { children: [
246
- current,
247
- /* @__PURE__ */ jsx2(Suspense, { fallback: chunk.value, children: /* @__PURE__ */ jsx2(R, { c: chunk.value, n: chunk.next }) })
248
- ] });
249
- }
250
- return /* @__PURE__ */ jsx2(Suspense, { fallback: chunk.value, children: /* @__PURE__ */ jsx2(R, { c: chunk.value, n: chunk.next }) });
251
- }
252
- ][0];
164
+ //#endregion
165
+ //#region src/util/constants.ts
166
+ /**
167
+ * Warning time for notifying developers that a stream is hanging in dev mode
168
+ * using a console.warn.
169
+ */
170
+ const HANGING_STREAM_WARNING_TIME_MS = 15e3;
171
+ //#endregion
172
+ //#region src/streamable-ui/create-suspended-chunk.tsx
173
+ const R = [(async ({ c: current, n: next }) => {
174
+ const chunk = await next;
175
+ if (chunk.done) return chunk.value;
176
+ if (chunk.append) return /* @__PURE__ */ jsxs(Fragment, { children: [current, /* @__PURE__ */ jsx(Suspense, {
177
+ fallback: chunk.value,
178
+ children: /* @__PURE__ */ jsx(R, {
179
+ c: chunk.value,
180
+ n: chunk.next
181
+ })
182
+ })] });
183
+ return /* @__PURE__ */ jsx(Suspense, {
184
+ fallback: chunk.value,
185
+ children: /* @__PURE__ */ jsx(R, {
186
+ c: chunk.value,
187
+ n: chunk.next
188
+ })
189
+ });
190
+ })][0];
191
+ /**
192
+ * Creates a suspended chunk for React Server Components.
193
+ *
194
+ * This function generates a suspenseful React component that can be dynamically updated.
195
+ * It's useful for streaming updates to the client in a React Server Components context.
196
+ *
197
+ * @param {React.ReactNode} initialValue - The initial value to render while the promise is pending.
198
+ * @returns {Object} An object containing:
199
+ * - row: A React node that renders the suspenseful content.
200
+ * - resolve: A function to resolve the promise with a new value.
201
+ * - reject: A function to reject the promise with an error.
202
+ */
253
203
  function createSuspendedChunk(initialValue) {
254
- const { promise, resolve, reject } = createResolvablePromise();
255
- return {
256
- row: /* @__PURE__ */ jsx2(Suspense, { fallback: initialValue, children: /* @__PURE__ */ jsx2(R, { c: initialValue, n: promise }) }),
257
- resolve,
258
- reject
259
- };
204
+ const { promise, resolve, reject } = createResolvablePromise();
205
+ return {
206
+ row: /* @__PURE__ */ jsx(Suspense, {
207
+ fallback: initialValue,
208
+ children: /* @__PURE__ */ jsx(R, {
209
+ c: initialValue,
210
+ n: promise
211
+ })
212
+ }),
213
+ resolve,
214
+ reject
215
+ };
260
216
  }
261
-
262
- // src/streamable-ui/create-streamable-ui.tsx
217
+ //#endregion
218
+ //#region src/streamable-ui/create-streamable-ui.tsx
219
+ /**
220
+ * Create a piece of changeable UI that can be streamed to the client.
221
+ * On the client side, it can be rendered as a normal React node.
222
+ */
263
223
  function createStreamableUI(initialValue) {
264
- let currentValue = initialValue;
265
- let closed = false;
266
- let { row, resolve, reject } = createSuspendedChunk(initialValue);
267
- function assertStream(method) {
268
- if (closed) {
269
- throw new Error(method + ": UI stream is already closed.");
270
- }
271
- }
272
- let warningTimeout;
273
- function warnUnclosedStream() {
274
- if (process.env.NODE_ENV === "development") {
275
- if (warningTimeout) {
276
- clearTimeout(warningTimeout);
277
- }
278
- warningTimeout = setTimeout(() => {
279
- console.warn(
280
- "The streamable UI has been slow to update. This may be a bug or a performance issue or you forgot to call `.done()`."
281
- );
282
- }, HANGING_STREAM_WARNING_TIME_MS);
283
- }
284
- }
285
- warnUnclosedStream();
286
- const streamable = {
287
- value: row,
288
- update(value) {
289
- assertStream(".update()");
290
- if (value === currentValue) {
291
- warnUnclosedStream();
292
- return streamable;
293
- }
294
- const resolvable = createResolvablePromise();
295
- currentValue = value;
296
- resolve({ value: currentValue, done: false, next: resolvable.promise });
297
- resolve = resolvable.resolve;
298
- reject = resolvable.reject;
299
- warnUnclosedStream();
300
- return streamable;
301
- },
302
- append(value) {
303
- assertStream(".append()");
304
- const resolvable = createResolvablePromise();
305
- currentValue = value;
306
- resolve({ value, done: false, append: true, next: resolvable.promise });
307
- resolve = resolvable.resolve;
308
- reject = resolvable.reject;
309
- warnUnclosedStream();
310
- return streamable;
311
- },
312
- error(error) {
313
- assertStream(".error()");
314
- if (warningTimeout) {
315
- clearTimeout(warningTimeout);
316
- }
317
- closed = true;
318
- reject(error);
319
- return streamable;
320
- },
321
- done(...args) {
322
- assertStream(".done()");
323
- if (warningTimeout) {
324
- clearTimeout(warningTimeout);
325
- }
326
- closed = true;
327
- if (args.length) {
328
- resolve({ value: args[0], done: true });
329
- return streamable;
330
- }
331
- resolve({ value: currentValue, done: true });
332
- return streamable;
333
- }
334
- };
335
- return streamable;
224
+ let currentValue = initialValue;
225
+ let closed = false;
226
+ let { row, resolve, reject } = createSuspendedChunk(initialValue);
227
+ function assertStream(method) {
228
+ if (closed) throw new Error(method + ": UI stream is already closed.");
229
+ }
230
+ let warningTimeout;
231
+ function warnUnclosedStream() {
232
+ if (process.env.NODE_ENV === "development") {
233
+ if (warningTimeout) clearTimeout(warningTimeout);
234
+ warningTimeout = setTimeout(() => {
235
+ console.warn("The streamable UI has been slow to update. This may be a bug or a performance issue or you forgot to call `.done()`.");
236
+ }, HANGING_STREAM_WARNING_TIME_MS);
237
+ }
238
+ }
239
+ warnUnclosedStream();
240
+ const streamable = {
241
+ value: row,
242
+ update(value) {
243
+ assertStream(".update()");
244
+ if (value === currentValue) {
245
+ warnUnclosedStream();
246
+ return streamable;
247
+ }
248
+ const resolvable = createResolvablePromise();
249
+ currentValue = value;
250
+ resolve({
251
+ value: currentValue,
252
+ done: false,
253
+ next: resolvable.promise
254
+ });
255
+ resolve = resolvable.resolve;
256
+ reject = resolvable.reject;
257
+ warnUnclosedStream();
258
+ return streamable;
259
+ },
260
+ append(value) {
261
+ assertStream(".append()");
262
+ const resolvable = createResolvablePromise();
263
+ currentValue = value;
264
+ resolve({
265
+ value,
266
+ done: false,
267
+ append: true,
268
+ next: resolvable.promise
269
+ });
270
+ resolve = resolvable.resolve;
271
+ reject = resolvable.reject;
272
+ warnUnclosedStream();
273
+ return streamable;
274
+ },
275
+ error(error) {
276
+ assertStream(".error()");
277
+ if (warningTimeout) clearTimeout(warningTimeout);
278
+ closed = true;
279
+ reject(error);
280
+ return streamable;
281
+ },
282
+ done(...args) {
283
+ assertStream(".done()");
284
+ if (warningTimeout) clearTimeout(warningTimeout);
285
+ closed = true;
286
+ if (args.length) {
287
+ resolve({
288
+ value: args[0],
289
+ done: true
290
+ });
291
+ return streamable;
292
+ }
293
+ resolve({
294
+ value: currentValue,
295
+ done: true
296
+ });
297
+ return streamable;
298
+ }
299
+ };
300
+ return streamable;
336
301
  }
337
-
338
- // src/util/is-async-generator.ts
302
+ //#endregion
303
+ //#region src/util/is-async-generator.ts
339
304
  function isAsyncGenerator(value) {
340
- return value != null && typeof value === "object" && Symbol.asyncIterator in value;
305
+ return value != null && typeof value === "object" && Symbol.asyncIterator in value;
341
306
  }
342
-
343
- // src/util/is-generator.ts
307
+ //#endregion
308
+ //#region src/util/is-generator.ts
344
309
  function isGenerator(value) {
345
- return value != null && typeof value === "object" && Symbol.iterator in value;
310
+ return value != null && typeof value === "object" && Symbol.iterator in value;
346
311
  }
347
-
348
- // src/stream-ui/stream-ui.tsx
349
- var defaultTextRenderer = ({ content }) => content;
350
- async function streamUI({
351
- model,
352
- tools,
353
- toolChoice,
354
- instructions,
355
- system,
356
- prompt,
357
- messages,
358
- allowSystemInMessages,
359
- maxRetries,
360
- abortSignal,
361
- headers,
362
- initial,
363
- text,
364
- providerOptions,
365
- onFinish,
366
- ...settings
367
- }) {
368
- if (typeof model === "string") {
369
- throw new Error(
370
- "`model` cannot be a string in `streamUI`. Use the actual model instance instead."
371
- );
372
- }
373
- if ("functions" in settings) {
374
- throw new Error(
375
- "`functions` is not supported in `streamUI`, use `tools` instead."
376
- );
377
- }
378
- if ("provider" in settings) {
379
- throw new Error(
380
- "`provider` is no longer needed in `streamUI`. Use `model` instead."
381
- );
382
- }
383
- if (tools) {
384
- for (const [name, tool] of Object.entries(tools)) {
385
- if ("render" in tool) {
386
- throw new Error(
387
- "Tool definition in `streamUI` should not have `render` property. Use `generate` instead. Found in tool: " + name
388
- );
389
- }
390
- }
391
- }
392
- const ui = createStreamableUI(initial);
393
- const textRender = text || defaultTextRenderer;
394
- let finished;
395
- let finishEvent = null;
396
- async function render({
397
- args,
398
- renderer,
399
- streamableUI,
400
- isLastCall = false
401
- }) {
402
- if (!renderer)
403
- return;
404
- const renderFinished = createResolvablePromise();
405
- finished = finished ? finished.then(() => renderFinished.promise) : renderFinished.promise;
406
- const rendererResult = renderer(...args);
407
- if (isAsyncGenerator(rendererResult) || isGenerator(rendererResult)) {
408
- while (true) {
409
- const { done, value } = await rendererResult.next();
410
- const node = await value;
411
- if (isLastCall && done) {
412
- streamableUI.done(node);
413
- } else {
414
- streamableUI.update(node);
415
- }
416
- if (done)
417
- break;
418
- }
419
- } else {
420
- const node = await rendererResult;
421
- if (isLastCall) {
422
- streamableUI.done(node);
423
- } else {
424
- streamableUI.update(node);
425
- }
426
- }
427
- renderFinished.resolve(void 0);
428
- }
429
- const { retry } = prepareRetries({ maxRetries, abortSignal });
430
- const validatedPrompt = await standardizePrompt({
431
- instructions,
432
- system,
433
- prompt,
434
- messages,
435
- allowSystemInMessages
436
- });
437
- const languageModelTools = await prepareTools({
438
- tools
439
- });
440
- const languageModelToolChoice = prepareToolChoice({
441
- toolChoice
442
- });
443
- const result = await retry(
444
- async () => model.doStream({
445
- ...prepareLanguageModelCallOptions(settings),
446
- tools: languageModelTools,
447
- toolChoice: languageModelToolChoice,
448
- prompt: await convertToLanguageModelPrompt({
449
- prompt: validatedPrompt,
450
- supportedUrls: await model.supportedUrls,
451
- download: void 0,
452
- provider: model.provider.split(".")[0]
453
- }),
454
- providerOptions,
455
- abortSignal,
456
- headers,
457
- includeRawChunks: false
458
- })
459
- );
460
- const [stream, forkedStream] = result.stream.tee();
461
- (async () => {
462
- try {
463
- let content = "";
464
- let hasToolCall = false;
465
- let warnings;
466
- const reader = forkedStream.getReader();
467
- while (true) {
468
- const { done, value } = await reader.read();
469
- if (done)
470
- break;
471
- switch (value.type) {
472
- case "stream-start": {
473
- warnings = value.warnings;
474
- break;
475
- }
476
- case "text-delta": {
477
- content += value.delta;
478
- render({
479
- renderer: textRender,
480
- args: [{ content, done: false, delta: value.delta }],
481
- streamableUI: ui
482
- });
483
- break;
484
- }
485
- case "tool-input-start":
486
- case "tool-input-delta": {
487
- hasToolCall = true;
488
- break;
489
- }
490
- case "tool-call": {
491
- const toolName = value.toolName;
492
- if (!tools) {
493
- throw new NoSuchToolError({ toolName });
494
- }
495
- const tool = tools[toolName];
496
- if (!tool) {
497
- throw new NoSuchToolError({
498
- toolName,
499
- availableTools: Object.keys(tools)
500
- });
501
- }
502
- hasToolCall = true;
503
- const parseResult = await safeParseJSON({
504
- text: value.input,
505
- schema: tool.inputSchema
506
- });
507
- if (parseResult.success === false) {
508
- throw new InvalidToolInputError({
509
- toolName,
510
- toolInput: value.input,
511
- cause: parseResult.error
512
- });
513
- }
514
- render({
515
- renderer: tool.generate,
516
- args: [
517
- parseResult.value,
518
- {
519
- toolName,
520
- toolCallId: value.toolCallId
521
- }
522
- ],
523
- streamableUI: ui,
524
- isLastCall: true
525
- });
526
- break;
527
- }
528
- case "error": {
529
- throw value.error;
530
- }
531
- case "finish": {
532
- finishEvent = {
533
- finishReason: value.finishReason?.unified,
534
- usage: value.usage,
535
- warnings,
536
- response: result.response
537
- };
538
- break;
539
- }
540
- }
541
- }
542
- if (!hasToolCall) {
543
- render({
544
- renderer: textRender,
545
- args: [{ content, done: true }],
546
- streamableUI: ui,
547
- isLastCall: true
548
- });
549
- }
550
- await finished;
551
- if (finishEvent && onFinish) {
552
- await onFinish({
553
- ...finishEvent,
554
- usage: asLanguageModelUsage(finishEvent.usage),
555
- value: ui.value
556
- });
557
- }
558
- } catch (error) {
559
- ui.error(error);
560
- }
561
- })();
562
- return {
563
- ...result,
564
- stream,
565
- value: ui.value
566
- };
312
+ //#endregion
313
+ //#region src/stream-ui/stream-ui.tsx
314
+ const defaultTextRenderer = ({ content }) => content;
315
+ /**
316
+ * `streamUI` is a helper function to create a streamable UI from LLMs.
317
+ */
318
+ async function streamUI({ model, tools, toolChoice, instructions, system, prompt, messages, allowSystemInMessages, maxRetries, abortSignal, headers, initial, text, providerOptions, onFinish, ...settings }) {
319
+ if (typeof model === "string") throw new Error("`model` cannot be a string in `streamUI`. Use the actual model instance instead.");
320
+ if ("functions" in settings) throw new Error("`functions` is not supported in `streamUI`, use `tools` instead.");
321
+ if ("provider" in settings) throw new Error("`provider` is no longer needed in `streamUI`. Use `model` instead.");
322
+ if (tools) {
323
+ for (const [name, tool] of Object.entries(tools)) if ("render" in tool) throw new Error("Tool definition in `streamUI` should not have `render` property. Use `generate` instead. Found in tool: " + name);
324
+ }
325
+ const ui = createStreamableUI(initial);
326
+ const textRender = text || defaultTextRenderer;
327
+ let finished;
328
+ let finishEvent = null;
329
+ async function render({ args, renderer, streamableUI, isLastCall = false }) {
330
+ if (!renderer) return;
331
+ const renderFinished = createResolvablePromise();
332
+ finished = finished ? finished.then(() => renderFinished.promise) : renderFinished.promise;
333
+ const rendererResult = renderer(...args);
334
+ if (isAsyncGenerator(rendererResult) || isGenerator(rendererResult)) while (true) {
335
+ const { done, value } = await rendererResult.next();
336
+ const node = await value;
337
+ if (isLastCall && done) streamableUI.done(node);
338
+ else streamableUI.update(node);
339
+ if (done) break;
340
+ }
341
+ else {
342
+ const node = await rendererResult;
343
+ if (isLastCall) streamableUI.done(node);
344
+ else streamableUI.update(node);
345
+ }
346
+ renderFinished.resolve(void 0);
347
+ }
348
+ const { retry } = prepareRetries({
349
+ maxRetries,
350
+ abortSignal
351
+ });
352
+ const validatedPrompt = await standardizePrompt({
353
+ instructions,
354
+ system,
355
+ prompt,
356
+ messages,
357
+ allowSystemInMessages
358
+ });
359
+ const languageModelTools = await prepareTools({ tools });
360
+ const languageModelToolChoice = prepareToolChoice({ toolChoice });
361
+ const result = await retry(async () => model.doStream({
362
+ ...prepareLanguageModelCallOptions(settings),
363
+ tools: languageModelTools,
364
+ toolChoice: languageModelToolChoice,
365
+ prompt: await convertToLanguageModelPrompt({
366
+ prompt: validatedPrompt,
367
+ supportedUrls: await model.supportedUrls,
368
+ download: void 0,
369
+ provider: model.provider.split(".")[0]
370
+ }),
371
+ providerOptions,
372
+ abortSignal,
373
+ headers,
374
+ includeRawChunks: false
375
+ }));
376
+ const [stream, forkedStream] = result.stream.tee();
377
+ (async () => {
378
+ try {
379
+ let content = "";
380
+ let hasToolCall = false;
381
+ let warnings;
382
+ const reader = forkedStream.getReader();
383
+ while (true) {
384
+ const { done, value } = await reader.read();
385
+ if (done) break;
386
+ switch (value.type) {
387
+ case "stream-start":
388
+ warnings = value.warnings;
389
+ break;
390
+ case "text-delta":
391
+ content += value.delta;
392
+ render({
393
+ renderer: textRender,
394
+ args: [{
395
+ content,
396
+ done: false,
397
+ delta: value.delta
398
+ }],
399
+ streamableUI: ui
400
+ });
401
+ break;
402
+ case "tool-input-start":
403
+ case "tool-input-delta":
404
+ hasToolCall = true;
405
+ break;
406
+ case "tool-call": {
407
+ const toolName = value.toolName;
408
+ if (!tools) throw new NoSuchToolError({ toolName });
409
+ const tool = tools[toolName];
410
+ if (!tool) throw new NoSuchToolError({
411
+ toolName,
412
+ availableTools: Object.keys(tools)
413
+ });
414
+ hasToolCall = true;
415
+ const parseResult = await safeParseJSON({
416
+ text: value.input,
417
+ schema: tool.inputSchema
418
+ });
419
+ if (parseResult.success === false) throw new InvalidToolInputError({
420
+ toolName,
421
+ toolInput: value.input,
422
+ cause: parseResult.error
423
+ });
424
+ render({
425
+ renderer: tool.generate,
426
+ args: [parseResult.value, {
427
+ toolName,
428
+ toolCallId: value.toolCallId
429
+ }],
430
+ streamableUI: ui,
431
+ isLastCall: true
432
+ });
433
+ break;
434
+ }
435
+ case "error": throw value.error;
436
+ case "finish": finishEvent = {
437
+ finishReason: value.finishReason?.unified,
438
+ usage: value.usage,
439
+ warnings,
440
+ response: result.response
441
+ };
442
+ }
443
+ }
444
+ if (!hasToolCall) render({
445
+ renderer: textRender,
446
+ args: [{
447
+ content,
448
+ done: true
449
+ }],
450
+ streamableUI: ui,
451
+ isLastCall: true
452
+ });
453
+ await finished;
454
+ if (finishEvent && onFinish) await onFinish({
455
+ ...finishEvent,
456
+ usage: asLanguageModelUsage(finishEvent.usage),
457
+ value: ui.value
458
+ });
459
+ } catch (error) {
460
+ ui.error(error);
461
+ }
462
+ })();
463
+ return {
464
+ ...result,
465
+ stream,
466
+ value: ui.value
467
+ };
567
468
  }
568
-
569
- // src/streamable-value/streamable-value.ts
570
- var STREAMABLE_VALUE_TYPE = Symbol.for("ui.streamable.value");
571
-
572
- // src/streamable-value/create-streamable-value.ts
573
- var STREAMABLE_VALUE_INTERNAL_LOCK = Symbol("streamable.value.lock");
469
+ //#endregion
470
+ //#region src/streamable-value/streamable-value.ts
471
+ const STREAMABLE_VALUE_TYPE = Symbol.for("ui.streamable.value");
472
+ //#endregion
473
+ //#region src/streamable-value/create-streamable-value.ts
474
+ const STREAMABLE_VALUE_INTERNAL_LOCK = Symbol("streamable.value.lock");
475
+ /**
476
+ * Create a wrapped, changeable value that can be streamed to the client.
477
+ * On the client side, the value can be accessed via the readStreamableValue() API.
478
+ */
574
479
  function createStreamableValue(initialValue) {
575
- const isReadableStream = initialValue instanceof ReadableStream || typeof initialValue === "object" && initialValue !== null && "getReader" in initialValue && typeof initialValue.getReader === "function" && "locked" in initialValue && typeof initialValue.locked === "boolean";
576
- if (!isReadableStream) {
577
- return createStreamableValueImpl(initialValue);
578
- }
579
- const streamableValue = createStreamableValueImpl();
580
- streamableValue[STREAMABLE_VALUE_INTERNAL_LOCK] = true;
581
- (async () => {
582
- try {
583
- const reader = initialValue.getReader();
584
- while (true) {
585
- const { value, done } = await reader.read();
586
- if (done) {
587
- break;
588
- }
589
- streamableValue[STREAMABLE_VALUE_INTERNAL_LOCK] = false;
590
- if (typeof value === "string") {
591
- streamableValue.append(value);
592
- } else {
593
- streamableValue.update(value);
594
- }
595
- streamableValue[STREAMABLE_VALUE_INTERNAL_LOCK] = true;
596
- }
597
- streamableValue[STREAMABLE_VALUE_INTERNAL_LOCK] = false;
598
- streamableValue.done();
599
- } catch (e) {
600
- streamableValue[STREAMABLE_VALUE_INTERNAL_LOCK] = false;
601
- streamableValue.error(e);
602
- }
603
- })();
604
- return streamableValue;
480
+ if (!(initialValue instanceof ReadableStream || typeof initialValue === "object" && initialValue !== null && "getReader" in initialValue && typeof initialValue.getReader === "function" && "locked" in initialValue && typeof initialValue.locked === "boolean")) return createStreamableValueImpl(initialValue);
481
+ const streamableValue = createStreamableValueImpl();
482
+ streamableValue[STREAMABLE_VALUE_INTERNAL_LOCK] = true;
483
+ (async () => {
484
+ try {
485
+ const reader = initialValue.getReader();
486
+ while (true) {
487
+ const { value, done } = await reader.read();
488
+ if (done) break;
489
+ streamableValue[STREAMABLE_VALUE_INTERNAL_LOCK] = false;
490
+ if (typeof value === "string") streamableValue.append(value);
491
+ else streamableValue.update(value);
492
+ streamableValue[STREAMABLE_VALUE_INTERNAL_LOCK] = true;
493
+ }
494
+ streamableValue[STREAMABLE_VALUE_INTERNAL_LOCK] = false;
495
+ streamableValue.done();
496
+ } catch (e) {
497
+ streamableValue[STREAMABLE_VALUE_INTERNAL_LOCK] = false;
498
+ streamableValue.error(e);
499
+ }
500
+ })();
501
+ return streamableValue;
605
502
  }
606
503
  function createStreamableValueImpl(initialValue) {
607
- let closed = false;
608
- let locked = false;
609
- let resolvable = createResolvablePromise();
610
- let currentValue = initialValue;
611
- let currentError;
612
- let currentPromise = resolvable.promise;
613
- let currentPatchValue;
614
- function assertStream(method) {
615
- if (closed) {
616
- throw new Error(method + ": Value stream is already closed.");
617
- }
618
- if (locked) {
619
- throw new Error(
620
- method + ": Value stream is locked and cannot be updated."
621
- );
622
- }
623
- }
624
- let warningTimeout;
625
- function warnUnclosedStream() {
626
- if (process.env.NODE_ENV === "development") {
627
- if (warningTimeout) {
628
- clearTimeout(warningTimeout);
629
- }
630
- warningTimeout = setTimeout(() => {
631
- console.warn(
632
- "The streamable value has been slow to update. This may be a bug or a performance issue or you forgot to call `.done()`."
633
- );
634
- }, HANGING_STREAM_WARNING_TIME_MS);
635
- }
636
- }
637
- warnUnclosedStream();
638
- function createWrapped(initialChunk) {
639
- let init;
640
- if (currentError !== void 0) {
641
- init = { error: currentError };
642
- } else {
643
- if (currentPatchValue && !initialChunk) {
644
- init = { diff: currentPatchValue };
645
- } else {
646
- init = { curr: currentValue };
647
- }
648
- }
649
- if (currentPromise) {
650
- init.next = currentPromise;
651
- }
652
- if (initialChunk) {
653
- init.type = STREAMABLE_VALUE_TYPE;
654
- }
655
- return init;
656
- }
657
- function updateValueStates(value) {
658
- currentPatchValue = void 0;
659
- if (typeof value === "string") {
660
- if (typeof currentValue === "string") {
661
- if (value.startsWith(currentValue)) {
662
- currentPatchValue = [0, value.slice(currentValue.length)];
663
- }
664
- }
665
- }
666
- currentValue = value;
667
- }
668
- const streamable = {
669
- set [STREAMABLE_VALUE_INTERNAL_LOCK](state) {
670
- locked = state;
671
- },
672
- get value() {
673
- return createWrapped(true);
674
- },
675
- update(value) {
676
- assertStream(".update()");
677
- const resolvePrevious = resolvable.resolve;
678
- resolvable = createResolvablePromise();
679
- updateValueStates(value);
680
- currentPromise = resolvable.promise;
681
- resolvePrevious(createWrapped());
682
- warnUnclosedStream();
683
- return streamable;
684
- },
685
- append(value) {
686
- assertStream(".append()");
687
- if (typeof currentValue !== "string" && typeof currentValue !== "undefined") {
688
- throw new Error(
689
- `.append(): The current value is not a string. Received: ${typeof currentValue}`
690
- );
691
- }
692
- if (typeof value !== "string") {
693
- throw new Error(
694
- `.append(): The value is not a string. Received: ${typeof value}`
695
- );
696
- }
697
- const resolvePrevious = resolvable.resolve;
698
- resolvable = createResolvablePromise();
699
- if (typeof currentValue === "string") {
700
- currentPatchValue = [0, value];
701
- currentValue = currentValue + value;
702
- } else {
703
- currentPatchValue = void 0;
704
- currentValue = value;
705
- }
706
- currentPromise = resolvable.promise;
707
- resolvePrevious(createWrapped());
708
- warnUnclosedStream();
709
- return streamable;
710
- },
711
- error(error) {
712
- assertStream(".error()");
713
- if (warningTimeout) {
714
- clearTimeout(warningTimeout);
715
- }
716
- closed = true;
717
- currentError = error;
718
- currentPromise = void 0;
719
- resolvable.resolve({ error });
720
- return streamable;
721
- },
722
- done(...args) {
723
- assertStream(".done()");
724
- if (warningTimeout) {
725
- clearTimeout(warningTimeout);
726
- }
727
- closed = true;
728
- currentPromise = void 0;
729
- if (args.length) {
730
- updateValueStates(args[0]);
731
- resolvable.resolve(createWrapped());
732
- return streamable;
733
- }
734
- resolvable.resolve({});
735
- return streamable;
736
- }
737
- };
738
- return streamable;
504
+ let closed = false;
505
+ let locked = false;
506
+ let resolvable = createResolvablePromise();
507
+ let currentValue = initialValue;
508
+ let currentError;
509
+ let currentPromise = resolvable.promise;
510
+ let currentPatchValue;
511
+ function assertStream(method) {
512
+ if (closed) throw new Error(method + ": Value stream is already closed.");
513
+ if (locked) throw new Error(method + ": Value stream is locked and cannot be updated.");
514
+ }
515
+ let warningTimeout;
516
+ function warnUnclosedStream() {
517
+ if (process.env.NODE_ENV === "development") {
518
+ if (warningTimeout) clearTimeout(warningTimeout);
519
+ warningTimeout = setTimeout(() => {
520
+ console.warn("The streamable value has been slow to update. This may be a bug or a performance issue or you forgot to call `.done()`.");
521
+ }, HANGING_STREAM_WARNING_TIME_MS);
522
+ }
523
+ }
524
+ warnUnclosedStream();
525
+ function createWrapped(initialChunk) {
526
+ let init;
527
+ if (currentError !== void 0) init = { error: currentError };
528
+ else if (currentPatchValue && !initialChunk) init = { diff: currentPatchValue };
529
+ else init = { curr: currentValue };
530
+ if (currentPromise) init.next = currentPromise;
531
+ if (initialChunk) init.type = STREAMABLE_VALUE_TYPE;
532
+ return init;
533
+ }
534
+ function updateValueStates(value) {
535
+ currentPatchValue = void 0;
536
+ if (typeof value === "string") {
537
+ if (typeof currentValue === "string") {
538
+ if (value.startsWith(currentValue)) currentPatchValue = [0, value.slice(currentValue.length)];
539
+ }
540
+ }
541
+ currentValue = value;
542
+ }
543
+ const streamable = {
544
+ set [STREAMABLE_VALUE_INTERNAL_LOCK](state) {
545
+ locked = state;
546
+ },
547
+ get value() {
548
+ return createWrapped(true);
549
+ },
550
+ update(value) {
551
+ assertStream(".update()");
552
+ const resolvePrevious = resolvable.resolve;
553
+ resolvable = createResolvablePromise();
554
+ updateValueStates(value);
555
+ currentPromise = resolvable.promise;
556
+ resolvePrevious(createWrapped());
557
+ warnUnclosedStream();
558
+ return streamable;
559
+ },
560
+ append(value) {
561
+ assertStream(".append()");
562
+ if (typeof currentValue !== "string" && typeof currentValue !== "undefined") throw new Error(`.append(): The current value is not a string. Received: ${typeof currentValue}`);
563
+ if (typeof value !== "string") throw new Error(`.append(): The value is not a string. Received: ${typeof value}`);
564
+ const resolvePrevious = resolvable.resolve;
565
+ resolvable = createResolvablePromise();
566
+ if (typeof currentValue === "string") {
567
+ currentPatchValue = [0, value];
568
+ currentValue = currentValue + value;
569
+ } else {
570
+ currentPatchValue = void 0;
571
+ currentValue = value;
572
+ }
573
+ currentPromise = resolvable.promise;
574
+ resolvePrevious(createWrapped());
575
+ warnUnclosedStream();
576
+ return streamable;
577
+ },
578
+ error(error) {
579
+ assertStream(".error()");
580
+ if (warningTimeout) clearTimeout(warningTimeout);
581
+ closed = true;
582
+ currentError = error;
583
+ currentPromise = void 0;
584
+ resolvable.resolve({ error });
585
+ return streamable;
586
+ },
587
+ done(...args) {
588
+ assertStream(".done()");
589
+ if (warningTimeout) clearTimeout(warningTimeout);
590
+ closed = true;
591
+ currentPromise = void 0;
592
+ if (args.length) {
593
+ updateValueStates(args[0]);
594
+ resolvable.resolve(createWrapped());
595
+ return streamable;
596
+ }
597
+ resolvable.resolve({});
598
+ return streamable;
599
+ }
600
+ };
601
+ return streamable;
739
602
  }
740
- export {
741
- createAI,
742
- createStreamableUI,
743
- createStreamableValue,
744
- getAIState,
745
- getMutableAIState,
746
- streamUI
747
- };
603
+ //#endregion
604
+ export { createAI, createStreamableUI, createStreamableValue, getAIState, getMutableAIState, streamUI };
605
+
748
606
  //# sourceMappingURL=rsc-server.js.map