ai 2.2.18 → 2.2.20

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/index.d.ts CHANGED
@@ -42,6 +42,7 @@ interface Message {
42
42
  id: string;
43
43
  createdAt?: Date;
44
44
  content: string;
45
+ ui?: string | JSX.Element | JSX.Element[] | null | undefined;
45
46
  role: 'system' | 'user' | 'assistant' | 'function';
46
47
  /**
47
48
  * If the message has a role of `function`, the `name` field is the name of the function.
@@ -601,4 +602,32 @@ declare const getStreamStringTypeAndValue: (line: string) => {
601
602
  */
602
603
  declare const COMPLEX_HEADER = "X-Experimental-Stream-Data";
603
604
 
604
- export { AIStream, AIStreamCallbacksAndOptions, AIStreamParser, AnthropicStream, COMPLEX_HEADER, ChatRequest, ChatRequestOptions, CohereStream, CompletionUsage, CreateMessage, FunctionCall, FunctionCallHandler, FunctionCallPayload, HuggingFaceStream, JSONValue, LangChainStream, Message, OpenAIStream, OpenAIStreamCallbacks, ReplicateStream, RequestOptions, StreamString, StreamStringPrefixes, StreamingTextResponse, UseChatOptions, UseCompletionOptions, createCallbacksTransformer, createChunkDecoder, createEventStreamTransformer, createStreamDataTransformer, experimental_StreamData, getStreamString, getStreamStringTypeAndValue, isStreamStringEqualToType, nanoid, readableFromAsyncIterable, streamToResponse, trimStartOfStreamHelper };
605
+ /**
606
+ * This is a naive implementation of the streaming React response API.
607
+ * Currently, it can carry the original raw content, data payload and a special
608
+ * UI payload and stream them via "rows" (nested promises).
609
+ * It must be used inside Server Actions so Flight can encode the React elements.
610
+ *
611
+ * It is naive as unlike the StreamingTextResponse, it does not send the diff
612
+ * between the rows, but flushing the full payload on each row.
613
+ */
614
+ type UINode = string | JSX.Element | JSX.Element[] | null | undefined;
615
+ type Payload = {
616
+ ui: UINode | Promise<UINode>;
617
+ content: string;
618
+ };
619
+ type ReactResponseRow = Payload & {
620
+ next: null | Promise<ReactResponseRow>;
621
+ };
622
+ /**
623
+ * A utility class for streaming React responses.
624
+ */
625
+ declare class experimental_StreamingReactResponse {
626
+ constructor(res: ReadableStream, options?: {
627
+ ui?: (message: {
628
+ content: string;
629
+ }) => UINode | Promise<UINode>;
630
+ });
631
+ }
632
+
633
+ export { AIStream, AIStreamCallbacksAndOptions, AIStreamParser, AnthropicStream, COMPLEX_HEADER, ChatRequest, ChatRequestOptions, CohereStream, CompletionUsage, CreateMessage, FunctionCall, FunctionCallHandler, FunctionCallPayload, HuggingFaceStream, JSONValue, LangChainStream, Message, OpenAIStream, OpenAIStreamCallbacks, ReactResponseRow, ReplicateStream, RequestOptions, StreamString, StreamStringPrefixes, StreamingTextResponse, UseChatOptions, UseCompletionOptions, createCallbacksTransformer, createChunkDecoder, createEventStreamTransformer, createStreamDataTransformer, experimental_StreamData, experimental_StreamingReactResponse, getStreamString, getStreamStringTypeAndValue, isStreamStringEqualToType, nanoid, readableFromAsyncIterable, streamToResponse, trimStartOfStreamHelper };
package/dist/index.js CHANGED
@@ -35,6 +35,7 @@ __export(streams_exports, {
35
35
  createEventStreamTransformer: () => createEventStreamTransformer,
36
36
  createStreamDataTransformer: () => createStreamDataTransformer,
37
37
  experimental_StreamData: () => experimental_StreamData,
38
+ experimental_StreamingReactResponse: () => experimental_StreamingReactResponse,
38
39
  getStreamString: () => getStreamString,
39
40
  getStreamStringTypeAndValue: () => getStreamStringTypeAndValue,
40
41
  isStreamStringEqualToType: () => isStreamStringEqualToType,
@@ -177,7 +178,7 @@ function createChunkDecoder(complex) {
177
178
  };
178
179
  }
179
180
  return function(chunk) {
180
- const decoded = decoder.decode(chunk, { stream: true }).split("\n");
181
+ const decoded = decoder.decode(chunk, { stream: true }).split("\n").filter((line) => line !== "");
181
182
  return decoded.map(getStreamStringTypeAndValue).filter(Boolean);
182
183
  };
183
184
  }
@@ -231,7 +232,6 @@ var experimental_StreamData = class {
231
232
  self.controller = controller;
232
233
  },
233
234
  transform: async (chunk, controller) => {
234
- controller.enqueue(chunk);
235
235
  if (self.data.length > 0) {
236
236
  const encodedData = self.encoder.encode(
237
237
  getStreamString("data", JSON.stringify(self.data))
@@ -239,6 +239,7 @@ var experimental_StreamData = class {
239
239
  self.data = [];
240
240
  controller.enqueue(encodedData);
241
241
  }
242
+ controller.enqueue(chunk);
242
243
  },
243
244
  async flush(controller) {
244
245
  const warningTimeout = process.env.NODE_ENV === "development" ? setTimeout(() => {
@@ -551,9 +552,7 @@ var utf8Decoder = new TextDecoder("utf-8");
551
552
  async function processLines(lines, controller) {
552
553
  for (const line of lines) {
553
554
  const { text, is_finished } = JSON.parse(line);
554
- if (is_finished === true) {
555
- controller.close();
556
- } else {
555
+ if (!is_finished) {
557
556
  controller.enqueue(text);
558
557
  }
559
558
  }
@@ -713,6 +712,46 @@ async function ReplicateStream(res, cb) {
713
712
  createStreamDataTransformer(cb == null ? void 0 : cb.experimental_streamData)
714
713
  );
715
714
  }
715
+
716
+ // streams/streaming-react-response.ts
717
+ var experimental_StreamingReactResponse = class {
718
+ constructor(res, options) {
719
+ let resolveFunc = () => {
720
+ };
721
+ let next = new Promise((resolve) => {
722
+ resolveFunc = resolve;
723
+ });
724
+ let content = "";
725
+ const decode = createChunkDecoder();
726
+ const reader = res.getReader();
727
+ async function readChunk() {
728
+ var _a;
729
+ const { done, value } = await reader.read();
730
+ if (!done) {
731
+ content += decode(value);
732
+ }
733
+ const ui = ((_a = options == null ? void 0 : options.ui) == null ? void 0 : _a.call(options, { content })) || content;
734
+ const payload = {
735
+ ui,
736
+ content
737
+ };
738
+ const resolvePrevious = resolveFunc;
739
+ const nextRow = done ? null : new Promise((resolve) => {
740
+ resolveFunc = resolve;
741
+ });
742
+ resolvePrevious({
743
+ next: nextRow,
744
+ ...payload
745
+ });
746
+ if (done) {
747
+ return;
748
+ }
749
+ await readChunk();
750
+ }
751
+ readChunk();
752
+ return next;
753
+ }
754
+ };
716
755
  // Annotate the CommonJS export names for ESM import in node:
717
756
  0 && (module.exports = {
718
757
  AIStream,
@@ -730,6 +769,7 @@ async function ReplicateStream(res, cb) {
730
769
  createEventStreamTransformer,
731
770
  createStreamDataTransformer,
732
771
  experimental_StreamData,
772
+ experimental_StreamingReactResponse,
733
773
  getStreamString,
734
774
  getStreamStringTypeAndValue,
735
775
  isStreamStringEqualToType,
package/dist/index.mjs CHANGED
@@ -132,7 +132,7 @@ function createChunkDecoder(complex) {
132
132
  };
133
133
  }
134
134
  return function(chunk) {
135
- const decoded = decoder.decode(chunk, { stream: true }).split("\n");
135
+ const decoded = decoder.decode(chunk, { stream: true }).split("\n").filter((line) => line !== "");
136
136
  return decoded.map(getStreamStringTypeAndValue).filter(Boolean);
137
137
  };
138
138
  }
@@ -186,7 +186,6 @@ var experimental_StreamData = class {
186
186
  self.controller = controller;
187
187
  },
188
188
  transform: async (chunk, controller) => {
189
- controller.enqueue(chunk);
190
189
  if (self.data.length > 0) {
191
190
  const encodedData = self.encoder.encode(
192
191
  getStreamString("data", JSON.stringify(self.data))
@@ -194,6 +193,7 @@ var experimental_StreamData = class {
194
193
  self.data = [];
195
194
  controller.enqueue(encodedData);
196
195
  }
196
+ controller.enqueue(chunk);
197
197
  },
198
198
  async flush(controller) {
199
199
  const warningTimeout = process.env.NODE_ENV === "development" ? setTimeout(() => {
@@ -506,9 +506,7 @@ var utf8Decoder = new TextDecoder("utf-8");
506
506
  async function processLines(lines, controller) {
507
507
  for (const line of lines) {
508
508
  const { text, is_finished } = JSON.parse(line);
509
- if (is_finished === true) {
510
- controller.close();
511
- } else {
509
+ if (!is_finished) {
512
510
  controller.enqueue(text);
513
511
  }
514
512
  }
@@ -668,6 +666,46 @@ async function ReplicateStream(res, cb) {
668
666
  createStreamDataTransformer(cb == null ? void 0 : cb.experimental_streamData)
669
667
  );
670
668
  }
669
+
670
+ // streams/streaming-react-response.ts
671
+ var experimental_StreamingReactResponse = class {
672
+ constructor(res, options) {
673
+ let resolveFunc = () => {
674
+ };
675
+ let next = new Promise((resolve) => {
676
+ resolveFunc = resolve;
677
+ });
678
+ let content = "";
679
+ const decode = createChunkDecoder();
680
+ const reader = res.getReader();
681
+ async function readChunk() {
682
+ var _a;
683
+ const { done, value } = await reader.read();
684
+ if (!done) {
685
+ content += decode(value);
686
+ }
687
+ const ui = ((_a = options == null ? void 0 : options.ui) == null ? void 0 : _a.call(options, { content })) || content;
688
+ const payload = {
689
+ ui,
690
+ content
691
+ };
692
+ const resolvePrevious = resolveFunc;
693
+ const nextRow = done ? null : new Promise((resolve) => {
694
+ resolveFunc = resolve;
695
+ });
696
+ resolvePrevious({
697
+ next: nextRow,
698
+ ...payload
699
+ });
700
+ if (done) {
701
+ return;
702
+ }
703
+ await readChunk();
704
+ }
705
+ readChunk();
706
+ return next;
707
+ }
708
+ };
671
709
  export {
672
710
  AIStream,
673
711
  AnthropicStream,
@@ -684,6 +722,7 @@ export {
684
722
  createEventStreamTransformer,
685
723
  createStreamDataTransformer,
686
724
  experimental_StreamData,
725
+ experimental_StreamingReactResponse,
687
726
  getStreamString,
688
727
  getStreamStringTypeAndValue,
689
728
  isStreamStringEqualToType,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai",
3
- "version": "2.2.18",
3
+ "version": "2.2.20",
4
4
  "license": "Apache-2.0",
5
5
  "sideEffects": false,
6
6
  "main": "./dist/index.js",
@@ -75,6 +75,7 @@
75
75
  "@types/react-dom": "^18.2.0",
76
76
  "eslint": "^7.32.0",
77
77
  "jest": "29.2.1",
78
+ "langchain": "0.0.172",
78
79
  "ts-jest": "29.0.3",
79
80
  "tsup": "^6.7.0",
80
81
  "typescript": "5.1.3",
@@ -18,6 +18,7 @@ interface Message {
18
18
  id: string;
19
19
  createdAt?: Date;
20
20
  content: string;
21
+ ui?: string | JSX.Element | JSX.Element[] | null | undefined;
21
22
  role: 'system' | 'user' | 'assistant' | 'function';
22
23
  /**
23
24
  * If the message has a role of `function`, the `name` field is the name of the function.
@@ -40,6 +40,7 @@ interface Message {
40
40
  id: string;
41
41
  createdAt?: Date;
42
42
  content: string;
43
+ ui?: string | JSX.Element | JSX.Element[] | null | undefined;
43
44
  role: 'system' | 'user' | 'assistant' | 'function';
44
45
  /**
45
46
  * If the message has a role of `function`, the `name` field is the name of the function.
@@ -197,6 +198,27 @@ type UseCompletionOptions = {
197
198
  body?: object;
198
199
  };
199
200
 
201
+ /**
202
+ * This is a naive implementation of the streaming React response API.
203
+ * Currently, it can carry the original raw content, data payload and a special
204
+ * UI payload and stream them via "rows" (nested promises).
205
+ * It must be used inside Server Actions so Flight can encode the React elements.
206
+ *
207
+ * It is naive as unlike the StreamingTextResponse, it does not send the diff
208
+ * between the rows, but flushing the full payload on each row.
209
+ */
210
+ type UINode = string | JSX.Element | JSX.Element[] | null | undefined;
211
+ /**
212
+ * A utility class for streaming React responses.
213
+ */
214
+ declare class experimental_StreamingReactResponse {
215
+ constructor(res: ReadableStream, options?: {
216
+ ui?: (message: {
217
+ content: string;
218
+ }) => UINode | Promise<UINode>;
219
+ });
220
+ }
221
+
200
222
  type UseChatHelpers = {
201
223
  /** Current messages in the chat */
202
224
  messages: Message[];
@@ -239,7 +261,12 @@ type UseChatHelpers = {
239
261
  /** Additional data added on the server via StreamData */
240
262
  data?: any;
241
263
  };
242
- declare function useChat({ api, id, initialMessages, initialInput, sendExtraMessageFields, experimental_onFunctionCall, onResponse, onFinish, onError, credentials, headers, body, }?: UseChatOptions): UseChatHelpers;
264
+ type StreamingReactResponseAction = (payload: {
265
+ messages: Message[];
266
+ }) => Promise<experimental_StreamingReactResponse>;
267
+ declare function useChat({ api, id, initialMessages, initialInput, sendExtraMessageFields, experimental_onFunctionCall, onResponse, onFinish, onError, credentials, headers, body, }?: Omit<UseChatOptions, 'api'> & {
268
+ api?: string | StreamingReactResponseAction;
269
+ }): UseChatHelpers;
243
270
 
244
271
  type UseCompletionHelpers = {
245
272
  /** The current completion result */
@@ -56,7 +56,7 @@ function createChunkDecoder(complex) {
56
56
  };
57
57
  }
58
58
  return function(chunk) {
59
- const decoded = decoder.decode(chunk, { stream: true }).split("\n");
59
+ const decoded = decoder.decode(chunk, { stream: true }).split("\n").filter((line) => line !== "");
60
60
  return decoded.map(getStreamStringTypeAndValue).filter(Boolean);
61
61
  };
62
62
  }
@@ -86,24 +86,151 @@ var getStreamStringTypeAndValue = (line) => {
86
86
  };
87
87
  var COMPLEX_HEADER = "X-Experimental-Stream-Data";
88
88
 
89
+ // react/parseComplexResponse.ts
90
+ async function parseComplexResponse({
91
+ reader,
92
+ abortControllerRef,
93
+ update
94
+ }) {
95
+ const decode = createChunkDecoder(true);
96
+ const createdAt = /* @__PURE__ */ new Date();
97
+ const prefixMap = {};
98
+ const NEWLINE = "\n".charCodeAt(0);
99
+ let chunks = [];
100
+ let totalLength = 0;
101
+ while (true) {
102
+ const { value } = await reader.read();
103
+ if (value) {
104
+ chunks.push(value);
105
+ totalLength += value.length;
106
+ if (value[value.length - 1] !== NEWLINE) {
107
+ continue;
108
+ }
109
+ }
110
+ if (chunks.length === 0) {
111
+ break;
112
+ }
113
+ let concatenatedChunks = new Uint8Array(totalLength);
114
+ let offset = 0;
115
+ for (const chunk of chunks) {
116
+ concatenatedChunks.set(chunk, offset);
117
+ offset += chunk.length;
118
+ }
119
+ chunks.length = 0;
120
+ totalLength = 0;
121
+ const lines = decode(concatenatedChunks);
122
+ if (typeof lines === "string") {
123
+ throw new Error(
124
+ "Invalid response format. Complex mode was set but the response is a string. This should never happen."
125
+ );
126
+ }
127
+ for (const { type, value: value2 } of lines) {
128
+ if (type === "text") {
129
+ if (prefixMap["text"]) {
130
+ prefixMap["text"] = {
131
+ ...prefixMap["text"],
132
+ content: (prefixMap["text"].content || "") + value2
133
+ };
134
+ } else {
135
+ prefixMap["text"] = {
136
+ id: nanoid(),
137
+ role: "assistant",
138
+ content: value2,
139
+ createdAt
140
+ };
141
+ }
142
+ }
143
+ let functionCallMessage = null;
144
+ if (type === "function_call") {
145
+ prefixMap["function_call"] = value2;
146
+ let functionCall = prefixMap["function_call"];
147
+ if (functionCall && typeof functionCall === "string") {
148
+ const parsedFunctionCall = JSON.parse(
149
+ functionCall
150
+ ).function_call;
151
+ functionCallMessage = {
152
+ id: nanoid(),
153
+ role: "assistant",
154
+ content: "",
155
+ function_call: parsedFunctionCall,
156
+ name: parsedFunctionCall.name,
157
+ createdAt
158
+ };
159
+ prefixMap["function_call"] = functionCallMessage;
160
+ }
161
+ }
162
+ if (type === "data") {
163
+ const parsedValue = JSON.parse(value2);
164
+ if (prefixMap["data"]) {
165
+ prefixMap["data"] = [...prefixMap["data"], ...parsedValue];
166
+ } else {
167
+ prefixMap["data"] = parsedValue;
168
+ }
169
+ }
170
+ const data = prefixMap["data"];
171
+ const responseMessage = prefixMap["text"];
172
+ const merged = [functionCallMessage, responseMessage].filter(
173
+ Boolean
174
+ );
175
+ update(merged, data);
176
+ if (abortControllerRef.current === null) {
177
+ reader.cancel();
178
+ break;
179
+ }
180
+ }
181
+ }
182
+ return prefixMap;
183
+ }
184
+
89
185
  // react/use-chat.ts
90
186
  var getStreamedResponse = async (api, chatRequest, mutate, mutateStreamData, existingData, extraMetadataRef, messagesRef, abortControllerRef, onFinish, onResponse, sendExtraMessageFields) => {
91
187
  var _a, _b;
92
188
  const previousMessages = messagesRef.current;
93
189
  mutate(chatRequest.messages, false);
190
+ const constructedMessagesPayload = sendExtraMessageFields ? chatRequest.messages : chatRequest.messages.map(({ role, content, name, function_call }) => ({
191
+ role,
192
+ content,
193
+ ...name !== void 0 && { name },
194
+ ...function_call !== void 0 && {
195
+ function_call
196
+ }
197
+ }));
198
+ if (typeof api !== "string") {
199
+ const replyId = nanoid();
200
+ const createdAt = /* @__PURE__ */ new Date();
201
+ let responseMessage = {
202
+ id: replyId,
203
+ createdAt,
204
+ content: "",
205
+ role: "assistant"
206
+ };
207
+ async function readRow(promise) {
208
+ const { content, ui, next } = await promise;
209
+ responseMessage["content"] = content;
210
+ responseMessage["ui"] = await ui;
211
+ mutate([...chatRequest.messages, { ...responseMessage }], false);
212
+ if (next) {
213
+ await readRow(next);
214
+ }
215
+ }
216
+ try {
217
+ const promise = api({
218
+ messages: constructedMessagesPayload
219
+ });
220
+ await readRow(promise);
221
+ } catch (e) {
222
+ mutate(previousMessages, false);
223
+ throw e;
224
+ }
225
+ if (onFinish) {
226
+ onFinish(responseMessage);
227
+ }
228
+ return responseMessage;
229
+ }
94
230
  const res = await fetch(api, {
95
231
  method: "POST",
96
232
  body: JSON.stringify({
97
- messages: sendExtraMessageFields ? chatRequest.messages : chatRequest.messages.map(
98
- ({ role, content, name, function_call }) => ({
99
- role,
100
- content,
101
- ...name !== void 0 && { name },
102
- ...function_call !== void 0 && {
103
- function_call
104
- }
105
- })
106
- ),
233
+ messages: constructedMessagesPayload,
107
234
  ...extraMetadataRef.current.body,
108
235
  ...(_a = chatRequest.options) == null ? void 0 : _a.body,
109
236
  ...chatRequest.functions !== void 0 && {
@@ -140,98 +267,18 @@ var getStreamedResponse = async (api, chatRequest, mutate, mutateStreamData, exi
140
267
  throw new Error("The response body is empty.");
141
268
  }
142
269
  const isComplexMode = res.headers.get(COMPLEX_HEADER) === "true";
143
- const createdAt = /* @__PURE__ */ new Date();
144
- const reader = res.body.getReader();
145
- const decode = createChunkDecoder(isComplexMode);
146
270
  let responseMessages = [];
271
+ const reader = res.body.getReader();
147
272
  let responseData = [];
148
- const prefixMap = {};
149
- const NEWLINE = "\n".charCodeAt(0);
150
- let chunks = [];
151
- let totalLength = 0;
152
273
  if (isComplexMode) {
153
- while (true) {
154
- const { value } = await reader.read();
155
- if (value) {
156
- chunks.push(value);
157
- totalLength += value.length;
158
- if (value[value.length - 1] !== NEWLINE) {
159
- continue;
160
- }
161
- }
162
- if (chunks.length === 0) {
163
- break;
164
- }
165
- let concatenatedChunks = new Uint8Array(totalLength);
166
- let offset = 0;
167
- for (const chunk of chunks) {
168
- concatenatedChunks.set(chunk, offset);
169
- offset += chunk.length;
170
- }
171
- chunks.length = 0;
172
- totalLength = 0;
173
- const lines = decode(concatenatedChunks);
174
- if (typeof lines === "string") {
175
- throw new Error(
176
- "Invalid response format. Complex mode was set but the response is a string. This should never happen."
177
- );
178
- }
179
- for (const { type, value: value2 } of lines) {
180
- if (type === "text") {
181
- if (prefixMap["text"]) {
182
- prefixMap["text"] = {
183
- ...prefixMap["text"],
184
- content: (prefixMap["text"].content || "") + value2
185
- };
186
- } else {
187
- prefixMap["text"] = {
188
- id: nanoid(),
189
- role: "assistant",
190
- content: value2,
191
- createdAt
192
- };
193
- }
194
- }
195
- let functionCallMessage = null;
196
- if (type === "function_call") {
197
- prefixMap["function_call"] = value2;
198
- let functionCall = prefixMap["function_call"];
199
- if (functionCall && typeof functionCall === "string") {
200
- const parsedFunctionCall = JSON.parse(
201
- functionCall
202
- ).function_call;
203
- functionCallMessage = {
204
- id: nanoid(),
205
- role: "assistant",
206
- content: "",
207
- function_call: parsedFunctionCall,
208
- name: parsedFunctionCall.name,
209
- createdAt
210
- };
211
- prefixMap["function_call"] = functionCallMessage;
212
- }
213
- }
214
- if (type === "data") {
215
- const parsedValue = JSON.parse(value2);
216
- if (prefixMap["data"]) {
217
- prefixMap["data"] = [...prefixMap["data"], ...parsedValue];
218
- } else {
219
- prefixMap["data"] = parsedValue;
220
- }
221
- }
222
- const data = prefixMap["data"];
223
- const responseMessage = prefixMap["text"];
224
- const merged = [functionCallMessage, responseMessage].filter(
225
- Boolean
226
- );
274
+ const prefixMap = await parseComplexResponse({
275
+ reader,
276
+ abortControllerRef,
277
+ update(merged, data) {
227
278
  mutate([...chatRequest.messages, ...merged], false);
228
279
  mutateStreamData([...existingData || [], ...data || []], false);
229
- if (abortControllerRef.current === null) {
230
- reader.cancel();
231
- break;
232
- }
233
280
  }
234
- }
281
+ });
235
282
  for (const [type, item] of Object.entries(prefixMap)) {
236
283
  if (onFinish && type === "text") {
237
284
  onFinish(item);
@@ -244,6 +291,8 @@ var getStreamedResponse = async (api, chatRequest, mutate, mutateStreamData, exi
244
291
  }
245
292
  return { messages: responseMessages, data: responseData };
246
293
  } else {
294
+ const createdAt = /* @__PURE__ */ new Date();
295
+ const decode = createChunkDecoder(false);
247
296
  let streamedResponse = "";
248
297
  const replyId = nanoid();
249
298
  let responseMessage = {
@@ -20,7 +20,7 @@ function createChunkDecoder(complex) {
20
20
  };
21
21
  }
22
22
  return function(chunk) {
23
- const decoded = decoder.decode(chunk, { stream: true }).split("\n");
23
+ const decoded = decoder.decode(chunk, { stream: true }).split("\n").filter((line) => line !== "");
24
24
  return decoded.map(getStreamStringTypeAndValue).filter(Boolean);
25
25
  };
26
26
  }
@@ -50,24 +50,151 @@ var getStreamStringTypeAndValue = (line) => {
50
50
  };
51
51
  var COMPLEX_HEADER = "X-Experimental-Stream-Data";
52
52
 
53
+ // react/parseComplexResponse.ts
54
+ async function parseComplexResponse({
55
+ reader,
56
+ abortControllerRef,
57
+ update
58
+ }) {
59
+ const decode = createChunkDecoder(true);
60
+ const createdAt = /* @__PURE__ */ new Date();
61
+ const prefixMap = {};
62
+ const NEWLINE = "\n".charCodeAt(0);
63
+ let chunks = [];
64
+ let totalLength = 0;
65
+ while (true) {
66
+ const { value } = await reader.read();
67
+ if (value) {
68
+ chunks.push(value);
69
+ totalLength += value.length;
70
+ if (value[value.length - 1] !== NEWLINE) {
71
+ continue;
72
+ }
73
+ }
74
+ if (chunks.length === 0) {
75
+ break;
76
+ }
77
+ let concatenatedChunks = new Uint8Array(totalLength);
78
+ let offset = 0;
79
+ for (const chunk of chunks) {
80
+ concatenatedChunks.set(chunk, offset);
81
+ offset += chunk.length;
82
+ }
83
+ chunks.length = 0;
84
+ totalLength = 0;
85
+ const lines = decode(concatenatedChunks);
86
+ if (typeof lines === "string") {
87
+ throw new Error(
88
+ "Invalid response format. Complex mode was set but the response is a string. This should never happen."
89
+ );
90
+ }
91
+ for (const { type, value: value2 } of lines) {
92
+ if (type === "text") {
93
+ if (prefixMap["text"]) {
94
+ prefixMap["text"] = {
95
+ ...prefixMap["text"],
96
+ content: (prefixMap["text"].content || "") + value2
97
+ };
98
+ } else {
99
+ prefixMap["text"] = {
100
+ id: nanoid(),
101
+ role: "assistant",
102
+ content: value2,
103
+ createdAt
104
+ };
105
+ }
106
+ }
107
+ let functionCallMessage = null;
108
+ if (type === "function_call") {
109
+ prefixMap["function_call"] = value2;
110
+ let functionCall = prefixMap["function_call"];
111
+ if (functionCall && typeof functionCall === "string") {
112
+ const parsedFunctionCall = JSON.parse(
113
+ functionCall
114
+ ).function_call;
115
+ functionCallMessage = {
116
+ id: nanoid(),
117
+ role: "assistant",
118
+ content: "",
119
+ function_call: parsedFunctionCall,
120
+ name: parsedFunctionCall.name,
121
+ createdAt
122
+ };
123
+ prefixMap["function_call"] = functionCallMessage;
124
+ }
125
+ }
126
+ if (type === "data") {
127
+ const parsedValue = JSON.parse(value2);
128
+ if (prefixMap["data"]) {
129
+ prefixMap["data"] = [...prefixMap["data"], ...parsedValue];
130
+ } else {
131
+ prefixMap["data"] = parsedValue;
132
+ }
133
+ }
134
+ const data = prefixMap["data"];
135
+ const responseMessage = prefixMap["text"];
136
+ const merged = [functionCallMessage, responseMessage].filter(
137
+ Boolean
138
+ );
139
+ update(merged, data);
140
+ if (abortControllerRef.current === null) {
141
+ reader.cancel();
142
+ break;
143
+ }
144
+ }
145
+ }
146
+ return prefixMap;
147
+ }
148
+
53
149
  // react/use-chat.ts
54
150
  var getStreamedResponse = async (api, chatRequest, mutate, mutateStreamData, existingData, extraMetadataRef, messagesRef, abortControllerRef, onFinish, onResponse, sendExtraMessageFields) => {
55
151
  var _a, _b;
56
152
  const previousMessages = messagesRef.current;
57
153
  mutate(chatRequest.messages, false);
154
+ const constructedMessagesPayload = sendExtraMessageFields ? chatRequest.messages : chatRequest.messages.map(({ role, content, name, function_call }) => ({
155
+ role,
156
+ content,
157
+ ...name !== void 0 && { name },
158
+ ...function_call !== void 0 && {
159
+ function_call
160
+ }
161
+ }));
162
+ if (typeof api !== "string") {
163
+ const replyId = nanoid();
164
+ const createdAt = /* @__PURE__ */ new Date();
165
+ let responseMessage = {
166
+ id: replyId,
167
+ createdAt,
168
+ content: "",
169
+ role: "assistant"
170
+ };
171
+ async function readRow(promise) {
172
+ const { content, ui, next } = await promise;
173
+ responseMessage["content"] = content;
174
+ responseMessage["ui"] = await ui;
175
+ mutate([...chatRequest.messages, { ...responseMessage }], false);
176
+ if (next) {
177
+ await readRow(next);
178
+ }
179
+ }
180
+ try {
181
+ const promise = api({
182
+ messages: constructedMessagesPayload
183
+ });
184
+ await readRow(promise);
185
+ } catch (e) {
186
+ mutate(previousMessages, false);
187
+ throw e;
188
+ }
189
+ if (onFinish) {
190
+ onFinish(responseMessage);
191
+ }
192
+ return responseMessage;
193
+ }
58
194
  const res = await fetch(api, {
59
195
  method: "POST",
60
196
  body: JSON.stringify({
61
- messages: sendExtraMessageFields ? chatRequest.messages : chatRequest.messages.map(
62
- ({ role, content, name, function_call }) => ({
63
- role,
64
- content,
65
- ...name !== void 0 && { name },
66
- ...function_call !== void 0 && {
67
- function_call
68
- }
69
- })
70
- ),
197
+ messages: constructedMessagesPayload,
71
198
  ...extraMetadataRef.current.body,
72
199
  ...(_a = chatRequest.options) == null ? void 0 : _a.body,
73
200
  ...chatRequest.functions !== void 0 && {
@@ -104,98 +231,18 @@ var getStreamedResponse = async (api, chatRequest, mutate, mutateStreamData, exi
104
231
  throw new Error("The response body is empty.");
105
232
  }
106
233
  const isComplexMode = res.headers.get(COMPLEX_HEADER) === "true";
107
- const createdAt = /* @__PURE__ */ new Date();
108
- const reader = res.body.getReader();
109
- const decode = createChunkDecoder(isComplexMode);
110
234
  let responseMessages = [];
235
+ const reader = res.body.getReader();
111
236
  let responseData = [];
112
- const prefixMap = {};
113
- const NEWLINE = "\n".charCodeAt(0);
114
- let chunks = [];
115
- let totalLength = 0;
116
237
  if (isComplexMode) {
117
- while (true) {
118
- const { value } = await reader.read();
119
- if (value) {
120
- chunks.push(value);
121
- totalLength += value.length;
122
- if (value[value.length - 1] !== NEWLINE) {
123
- continue;
124
- }
125
- }
126
- if (chunks.length === 0) {
127
- break;
128
- }
129
- let concatenatedChunks = new Uint8Array(totalLength);
130
- let offset = 0;
131
- for (const chunk of chunks) {
132
- concatenatedChunks.set(chunk, offset);
133
- offset += chunk.length;
134
- }
135
- chunks.length = 0;
136
- totalLength = 0;
137
- const lines = decode(concatenatedChunks);
138
- if (typeof lines === "string") {
139
- throw new Error(
140
- "Invalid response format. Complex mode was set but the response is a string. This should never happen."
141
- );
142
- }
143
- for (const { type, value: value2 } of lines) {
144
- if (type === "text") {
145
- if (prefixMap["text"]) {
146
- prefixMap["text"] = {
147
- ...prefixMap["text"],
148
- content: (prefixMap["text"].content || "") + value2
149
- };
150
- } else {
151
- prefixMap["text"] = {
152
- id: nanoid(),
153
- role: "assistant",
154
- content: value2,
155
- createdAt
156
- };
157
- }
158
- }
159
- let functionCallMessage = null;
160
- if (type === "function_call") {
161
- prefixMap["function_call"] = value2;
162
- let functionCall = prefixMap["function_call"];
163
- if (functionCall && typeof functionCall === "string") {
164
- const parsedFunctionCall = JSON.parse(
165
- functionCall
166
- ).function_call;
167
- functionCallMessage = {
168
- id: nanoid(),
169
- role: "assistant",
170
- content: "",
171
- function_call: parsedFunctionCall,
172
- name: parsedFunctionCall.name,
173
- createdAt
174
- };
175
- prefixMap["function_call"] = functionCallMessage;
176
- }
177
- }
178
- if (type === "data") {
179
- const parsedValue = JSON.parse(value2);
180
- if (prefixMap["data"]) {
181
- prefixMap["data"] = [...prefixMap["data"], ...parsedValue];
182
- } else {
183
- prefixMap["data"] = parsedValue;
184
- }
185
- }
186
- const data = prefixMap["data"];
187
- const responseMessage = prefixMap["text"];
188
- const merged = [functionCallMessage, responseMessage].filter(
189
- Boolean
190
- );
238
+ const prefixMap = await parseComplexResponse({
239
+ reader,
240
+ abortControllerRef,
241
+ update(merged, data) {
191
242
  mutate([...chatRequest.messages, ...merged], false);
192
243
  mutateStreamData([...existingData || [], ...data || []], false);
193
- if (abortControllerRef.current === null) {
194
- reader.cancel();
195
- break;
196
- }
197
244
  }
198
- }
245
+ });
199
246
  for (const [type, item] of Object.entries(prefixMap)) {
200
247
  if (onFinish && type === "text") {
201
248
  onFinish(item);
@@ -208,6 +255,8 @@ var getStreamedResponse = async (api, chatRequest, mutate, mutateStreamData, exi
208
255
  }
209
256
  return { messages: responseMessages, data: responseData };
210
257
  } else {
258
+ const createdAt = /* @__PURE__ */ new Date();
259
+ const decode = createChunkDecoder(false);
211
260
  let streamedResponse = "";
212
261
  const replyId = nanoid();
213
262
  let responseMessage = {
@@ -42,6 +42,7 @@ interface Message {
42
42
  id: string;
43
43
  createdAt?: Date;
44
44
  content: string;
45
+ ui?: string | JSX.Element | JSX.Element[] | null | undefined;
45
46
  role: 'system' | 'user' | 'assistant' | 'function';
46
47
  /**
47
48
  * If the message has a role of `function`, the `name` field is the name of the function.
@@ -46,7 +46,7 @@ function createChunkDecoder(complex) {
46
46
  };
47
47
  }
48
48
  return function(chunk) {
49
- const decoded = decoder.decode(chunk, { stream: true }).split("\n");
49
+ const decoded = decoder.decode(chunk, { stream: true }).split("\n").filter((line) => line !== "");
50
50
  return decoded.map(getStreamStringTypeAndValue).filter(Boolean);
51
51
  };
52
52
  }
@@ -19,7 +19,7 @@ function createChunkDecoder(complex) {
19
19
  };
20
20
  }
21
21
  return function(chunk) {
22
- const decoded = decoder.decode(chunk, { stream: true }).split("\n");
22
+ const decoded = decoder.decode(chunk, { stream: true }).split("\n").filter((line) => line !== "");
23
23
  return decoded.map(getStreamStringTypeAndValue).filter(Boolean);
24
24
  };
25
25
  }
@@ -42,6 +42,7 @@ interface Message {
42
42
  id: string;
43
43
  createdAt?: Date;
44
44
  content: string;
45
+ ui?: string | JSX.Element | JSX.Element[] | null | undefined;
45
46
  role: 'system' | 'user' | 'assistant' | 'function';
46
47
  /**
47
48
  * If the message has a role of `function`, the `name` field is the name of the function.
@@ -541,7 +541,7 @@ function createChunkDecoder(complex) {
541
541
  };
542
542
  }
543
543
  return function(chunk) {
544
- const decoded = decoder.decode(chunk, { stream: true }).split("\n");
544
+ const decoded = decoder.decode(chunk, { stream: true }).split("\n").filter((line) => line !== "");
545
545
  return decoded.map(getStreamStringTypeAndValue).filter(Boolean);
546
546
  };
547
547
  }
@@ -514,7 +514,7 @@ function createChunkDecoder(complex) {
514
514
  };
515
515
  }
516
516
  return function(chunk) {
517
- const decoded = decoder.decode(chunk, { stream: true }).split("\n");
517
+ const decoded = decoder.decode(chunk, { stream: true }).split("\n").filter((line) => line !== "");
518
518
  return decoded.map(getStreamStringTypeAndValue).filter(Boolean);
519
519
  };
520
520
  }
@@ -42,6 +42,7 @@ interface Message {
42
42
  id: string;
43
43
  createdAt?: Date;
44
44
  content: string;
45
+ ui?: string | JSX.Element | JSX.Element[] | null | undefined;
45
46
  role: 'system' | 'user' | 'assistant' | 'function';
46
47
  /**
47
48
  * If the message has a role of `function`, the `name` field is the name of the function.
package/vue/dist/index.js CHANGED
@@ -55,7 +55,7 @@ function createChunkDecoder(complex) {
55
55
  };
56
56
  }
57
57
  return function(chunk) {
58
- const decoded = decoder.decode(chunk, { stream: true }).split("\n");
58
+ const decoded = decoder.decode(chunk, { stream: true }).split("\n").filter((line) => line !== "");
59
59
  return decoded.map(getStreamStringTypeAndValue).filter(Boolean);
60
60
  };
61
61
  }
@@ -18,7 +18,7 @@ function createChunkDecoder(complex) {
18
18
  };
19
19
  }
20
20
  return function(chunk) {
21
- const decoded = decoder.decode(chunk, { stream: true }).split("\n");
21
+ const decoded = decoder.decode(chunk, { stream: true }).split("\n").filter((line) => line !== "");
22
22
  return decoded.map(getStreamStringTypeAndValue).filter(Boolean);
23
23
  };
24
24
  }