ai 2.2.23 → 2.2.25

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.
@@ -3,8 +3,134 @@ import { createSignal } from "solid-js";
3
3
  import { useSWRStore } from "solid-swr-store";
4
4
  import { createSWRStore } from "swr-store";
5
5
 
6
+ // shared/call-api.ts
7
+ import { nanoid as nanoid2 } from "nanoid";
8
+
6
9
  // shared/utils.ts
7
10
  import { customAlphabet } from "nanoid/non-secure";
11
+
12
+ // shared/stream-parts.ts
13
+ var textStreamPart = {
14
+ code: "0",
15
+ name: "text",
16
+ parse: (value) => {
17
+ if (typeof value !== "string") {
18
+ throw new Error('"text" parts expect a string value.');
19
+ }
20
+ return { type: "text", value };
21
+ }
22
+ };
23
+ var functionCallStreamPart = {
24
+ code: "1",
25
+ name: "function_call",
26
+ parse: (value) => {
27
+ if (value == null || typeof value !== "object" || !("function_call" in value) || typeof value.function_call !== "object" || value.function_call == null || !("name" in value.function_call) || !("arguments" in value.function_call) || typeof value.function_call.name !== "string" || typeof value.function_call.arguments !== "string") {
28
+ throw new Error(
29
+ '"function_call" parts expect an object with a "function_call" property.'
30
+ );
31
+ }
32
+ return {
33
+ type: "function_call",
34
+ value
35
+ };
36
+ }
37
+ };
38
+ var dataStreamPart = {
39
+ code: "2",
40
+ name: "data",
41
+ parse: (value) => {
42
+ if (!Array.isArray(value)) {
43
+ throw new Error('"data" parts expect an array value.');
44
+ }
45
+ return { type: "data", value };
46
+ }
47
+ };
48
+ var errorStreamPart = {
49
+ code: "3",
50
+ name: "error",
51
+ parse: (value) => {
52
+ if (typeof value !== "string") {
53
+ throw new Error('"error" parts expect a string value.');
54
+ }
55
+ return { type: "error", value };
56
+ }
57
+ };
58
+ var assistantMessage = {
59
+ code: "4",
60
+ name: "assistant_message",
61
+ parse: (value) => {
62
+ if (value == null || typeof value !== "object" || !("id" in value) || !("role" in value) || !("content" in value) || typeof value.id !== "string" || typeof value.role !== "string" || value.role !== "assistant" || !Array.isArray(value.content) || !value.content.every(
63
+ (item) => item != null && typeof item === "object" && "type" in item && item.type === "text" && "text" in item && item.text != null && typeof item.text === "object" && "value" in item.text && typeof item.text.value === "string"
64
+ )) {
65
+ throw new Error(
66
+ '"assistant_message" parts expect an object with an "id", "role", and "content" property.'
67
+ );
68
+ }
69
+ return {
70
+ type: "assistant_message",
71
+ value
72
+ };
73
+ }
74
+ };
75
+ var assistantControlData = {
76
+ code: "5",
77
+ name: "assistant_control_data",
78
+ parse: (value) => {
79
+ if (value == null || typeof value !== "object" || !("threadId" in value) || !("messageId" in value) || typeof value.threadId !== "string" || typeof value.messageId !== "string") {
80
+ throw new Error(
81
+ '"assistant_control_data" parts expect an object with a "threadId" and "messageId" property.'
82
+ );
83
+ }
84
+ return {
85
+ type: "assistant_control_data",
86
+ value: {
87
+ threadId: value.threadId,
88
+ messageId: value.messageId
89
+ }
90
+ };
91
+ }
92
+ };
93
+ var streamParts = [
94
+ textStreamPart,
95
+ functionCallStreamPart,
96
+ dataStreamPart,
97
+ errorStreamPart,
98
+ assistantMessage,
99
+ assistantControlData
100
+ ];
101
+ var streamPartsByCode = {
102
+ [textStreamPart.code]: textStreamPart,
103
+ [functionCallStreamPart.code]: functionCallStreamPart,
104
+ [dataStreamPart.code]: dataStreamPart,
105
+ [errorStreamPart.code]: errorStreamPart,
106
+ [assistantMessage.code]: assistantMessage,
107
+ [assistantControlData.code]: assistantControlData
108
+ };
109
+ var StreamStringPrefixes = {
110
+ [textStreamPart.name]: textStreamPart.code,
111
+ [functionCallStreamPart.name]: functionCallStreamPart.code,
112
+ [dataStreamPart.name]: dataStreamPart.code,
113
+ [errorStreamPart.name]: errorStreamPart.code,
114
+ [assistantMessage.name]: assistantMessage.code,
115
+ [assistantControlData.name]: assistantControlData.code
116
+ };
117
+ var validCodes = streamParts.map((part) => part.code);
118
+ var parseStreamPart = (line) => {
119
+ const firstSeparatorIndex = line.indexOf(":");
120
+ if (firstSeparatorIndex === -1) {
121
+ throw new Error("Failed to parse stream string. No separator found.");
122
+ }
123
+ const prefix = line.slice(0, firstSeparatorIndex);
124
+ if (!validCodes.includes(prefix)) {
125
+ throw new Error(`Failed to parse stream string. Invalid code ${prefix}.`);
126
+ }
127
+ const code = prefix;
128
+ const textValue = line.slice(firstSeparatorIndex + 1);
129
+ const jsonValue = JSON.parse(textValue);
130
+ return streamPartsByCode[code].parse(jsonValue);
131
+ };
132
+
133
+ // shared/utils.ts
8
134
  var nanoid = customAlphabet(
9
135
  "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
10
136
  7
@@ -20,36 +146,249 @@ function createChunkDecoder(complex) {
20
146
  }
21
147
  return function(chunk) {
22
148
  const decoded = decoder.decode(chunk, { stream: true }).split("\n").filter((line) => line !== "");
23
- return decoded.map(getStreamStringTypeAndValue).filter(Boolean);
149
+ return decoded.map(parseStreamPart).filter(Boolean);
24
150
  };
25
151
  }
26
- var StreamStringPrefixes = {
27
- text: 0,
28
- function_call: 1,
29
- data: 2
30
- // user_err: 3?
31
- };
32
- var getStreamStringTypeAndValue = (line) => {
33
- const firstSeperatorIndex = line.indexOf(":");
34
- if (firstSeperatorIndex === -1) {
35
- throw new Error("Failed to parse stream string");
152
+ var COMPLEX_HEADER = "X-Experimental-Stream-Data";
153
+
154
+ // shared/parse-complex-response.ts
155
+ async function parseComplexResponse({
156
+ reader,
157
+ abortControllerRef,
158
+ update,
159
+ onFinish,
160
+ generateId = nanoid,
161
+ getCurrentDate = () => /* @__PURE__ */ new Date()
162
+ }) {
163
+ const createdAt = getCurrentDate();
164
+ const decode = createChunkDecoder(true);
165
+ const prefixMap = {
166
+ data: []
167
+ };
168
+ const NEWLINE = "\n".charCodeAt(0);
169
+ const chunks = [];
170
+ let totalLength = 0;
171
+ while (true) {
172
+ const { value } = await reader.read();
173
+ if (value) {
174
+ chunks.push(value);
175
+ totalLength += value.length;
176
+ if (value[value.length - 1] !== NEWLINE) {
177
+ continue;
178
+ }
179
+ }
180
+ if (chunks.length === 0) {
181
+ break;
182
+ }
183
+ let concatenatedChunks = new Uint8Array(totalLength);
184
+ let offset = 0;
185
+ for (const chunk of chunks) {
186
+ concatenatedChunks.set(chunk, offset);
187
+ offset += chunk.length;
188
+ }
189
+ chunks.length = 0;
190
+ totalLength = 0;
191
+ const lines = decode(concatenatedChunks);
192
+ if (typeof lines === "string") {
193
+ throw new Error(
194
+ "Invalid response format. Complex mode was set but the response is a string. This should never happen."
195
+ );
196
+ }
197
+ for (const { type, value: value2 } of lines) {
198
+ if (type === "text") {
199
+ if (prefixMap["text"]) {
200
+ prefixMap["text"] = {
201
+ ...prefixMap["text"],
202
+ content: (prefixMap["text"].content || "") + value2
203
+ };
204
+ } else {
205
+ prefixMap["text"] = {
206
+ id: generateId(),
207
+ role: "assistant",
208
+ content: value2,
209
+ createdAt
210
+ };
211
+ }
212
+ }
213
+ let functionCallMessage = null;
214
+ if (type === "function_call") {
215
+ prefixMap["function_call"] = {
216
+ id: generateId(),
217
+ role: "assistant",
218
+ content: "",
219
+ function_call: value2.function_call,
220
+ name: value2.function_call.name,
221
+ createdAt
222
+ };
223
+ functionCallMessage = prefixMap["function_call"];
224
+ }
225
+ if (type === "data") {
226
+ prefixMap["data"].push(...value2);
227
+ }
228
+ const responseMessage = prefixMap["text"];
229
+ const merged = [functionCallMessage, responseMessage].filter(
230
+ Boolean
231
+ );
232
+ update(merged, [...prefixMap["data"]]);
233
+ if ((abortControllerRef == null ? void 0 : abortControllerRef.current) === null) {
234
+ reader.cancel();
235
+ break;
236
+ }
237
+ }
36
238
  }
37
- const prefix = line.slice(0, firstSeperatorIndex);
38
- const type = Object.keys(StreamStringPrefixes).find(
39
- (key) => StreamStringPrefixes[key] === Number(prefix)
40
- );
41
- const val = line.slice(firstSeperatorIndex + 1);
42
- let parsedVal = val;
43
- if (!val) {
44
- return { type, value: "" };
239
+ onFinish == null ? void 0 : onFinish(prefixMap);
240
+ return {
241
+ messages: [prefixMap.text, prefixMap.function_call].filter(
242
+ Boolean
243
+ ),
244
+ data: prefixMap.data
245
+ };
246
+ }
247
+
248
+ // shared/call-api.ts
249
+ async function callApi({
250
+ api,
251
+ messages,
252
+ body,
253
+ credentials,
254
+ headers,
255
+ abortController,
256
+ appendMessage,
257
+ restoreMessagesOnFailure,
258
+ onResponse,
259
+ onUpdate,
260
+ onFinish
261
+ }) {
262
+ var _a;
263
+ const response = await fetch(api, {
264
+ method: "POST",
265
+ body: JSON.stringify({
266
+ messages,
267
+ ...body
268
+ }),
269
+ headers,
270
+ signal: (_a = abortController == null ? void 0 : abortController()) == null ? void 0 : _a.signal,
271
+ credentials
272
+ }).catch((err) => {
273
+ restoreMessagesOnFailure();
274
+ throw err;
275
+ });
276
+ if (onResponse) {
277
+ try {
278
+ await onResponse(response);
279
+ } catch (err) {
280
+ throw err;
281
+ }
45
282
  }
46
- try {
47
- parsedVal = JSON.parse(val);
48
- } catch (e) {
49
- console.error("Failed to parse JSON value:", val);
283
+ if (!response.ok) {
284
+ restoreMessagesOnFailure();
285
+ throw new Error(
286
+ await response.text() || "Failed to fetch the chat response."
287
+ );
50
288
  }
51
- return { type, value: parsedVal };
52
- };
289
+ if (!response.body) {
290
+ throw new Error("The response body is empty.");
291
+ }
292
+ const reader = response.body.getReader();
293
+ const isComplexMode = response.headers.get(COMPLEX_HEADER) === "true";
294
+ if (isComplexMode) {
295
+ return await parseComplexResponse({
296
+ reader,
297
+ abortControllerRef: abortController != null ? { current: abortController() } : void 0,
298
+ update: onUpdate,
299
+ onFinish(prefixMap) {
300
+ if (onFinish && prefixMap.text != null) {
301
+ onFinish(prefixMap.text);
302
+ }
303
+ }
304
+ });
305
+ } else {
306
+ const createdAt = /* @__PURE__ */ new Date();
307
+ const decode = createChunkDecoder(false);
308
+ let streamedResponse = "";
309
+ const replyId = nanoid2();
310
+ let responseMessage = {
311
+ id: replyId,
312
+ createdAt,
313
+ content: "",
314
+ role: "assistant"
315
+ };
316
+ while (true) {
317
+ const { done, value } = await reader.read();
318
+ if (done) {
319
+ break;
320
+ }
321
+ streamedResponse += decode(value);
322
+ if (streamedResponse.startsWith('{"function_call":')) {
323
+ responseMessage["function_call"] = streamedResponse;
324
+ } else {
325
+ responseMessage["content"] = streamedResponse;
326
+ }
327
+ appendMessage({ ...responseMessage });
328
+ if ((abortController == null ? void 0 : abortController()) === null) {
329
+ reader.cancel();
330
+ break;
331
+ }
332
+ }
333
+ if (streamedResponse.startsWith('{"function_call":')) {
334
+ const parsedFunctionCall = JSON.parse(streamedResponse).function_call;
335
+ responseMessage["function_call"] = parsedFunctionCall;
336
+ appendMessage({ ...responseMessage });
337
+ }
338
+ if (onFinish) {
339
+ onFinish(responseMessage);
340
+ }
341
+ return responseMessage;
342
+ }
343
+ }
344
+
345
+ // shared/process-chat-stream.ts
346
+ async function processChatStream({
347
+ getStreamedResponse,
348
+ experimental_onFunctionCall,
349
+ updateChatRequest,
350
+ getCurrentMessages
351
+ }) {
352
+ while (true) {
353
+ const messagesAndDataOrJustMessage = await getStreamedResponse();
354
+ if ("messages" in messagesAndDataOrJustMessage) {
355
+ let hasFollowingResponse = false;
356
+ for (const message of messagesAndDataOrJustMessage.messages) {
357
+ if (message.function_call === void 0 || typeof message.function_call === "string") {
358
+ continue;
359
+ }
360
+ hasFollowingResponse = true;
361
+ if (experimental_onFunctionCall) {
362
+ const functionCall = message.function_call;
363
+ const functionCallResponse = await experimental_onFunctionCall(
364
+ getCurrentMessages(),
365
+ functionCall
366
+ );
367
+ if (functionCallResponse === void 0) {
368
+ hasFollowingResponse = false;
369
+ break;
370
+ }
371
+ updateChatRequest(functionCallResponse);
372
+ }
373
+ }
374
+ if (!hasFollowingResponse) {
375
+ break;
376
+ }
377
+ } else {
378
+ const streamedResponseMessage = messagesAndDataOrJustMessage;
379
+ if (streamedResponseMessage.function_call === void 0 || typeof streamedResponseMessage.function_call === "string") {
380
+ break;
381
+ }
382
+ if (experimental_onFunctionCall) {
383
+ const functionCall = streamedResponseMessage.function_call;
384
+ const functionCallResponse = await experimental_onFunctionCall(getCurrentMessages(), functionCall);
385
+ if (functionCallResponse === void 0)
386
+ break;
387
+ updateChatRequest(functionCallResponse);
388
+ }
389
+ }
390
+ }
391
+ }
53
392
 
54
393
  // solid/use-chat.ts
55
394
  var uniqueId = 0;
@@ -66,6 +405,7 @@ function useChat({
66
405
  initialMessages = [],
67
406
  initialInput = "",
68
407
  sendExtraMessageFields,
408
+ experimental_onFunctionCall,
69
409
  onResponse,
70
410
  onFinish,
71
411
  onError,
@@ -75,18 +415,20 @@ function useChat({
75
415
  } = {}) {
76
416
  const chatId = id || `chat-${uniqueId++}`;
77
417
  const key = `${api}|${chatId}`;
78
- const data = useSWRStore(chatApiStore, () => [key], {
418
+ const messages = useSWRStore(chatApiStore, () => [key], {
79
419
  initialData: initialMessages
80
420
  });
81
- const mutate = (data2) => {
82
- store[key] = data2;
421
+ const mutate = (data) => {
422
+ store[key] = data;
83
423
  return chatApiStore.mutate([key], {
84
424
  status: "success",
85
- data: data2
425
+ data
86
426
  });
87
427
  };
88
- const messages = data;
89
428
  const [error, setError] = createSignal(void 0);
429
+ const [streamData, setStreamData] = createSignal(
430
+ void 0
431
+ );
90
432
  const [isLoading, setIsLoading] = createSignal(false);
91
433
  let abortController = null;
92
434
  async function triggerRequest(messagesSnapshot, options) {
@@ -94,91 +436,64 @@ function useChat({
94
436
  setError(void 0);
95
437
  setIsLoading(true);
96
438
  abortController = new AbortController();
97
- const previousMessages = chatApiStore.get([key], {
439
+ const getCurrentMessages = () => chatApiStore.get([key], {
98
440
  shouldRevalidate: false
99
441
  });
442
+ const previousMessages = getCurrentMessages();
100
443
  mutate(messagesSnapshot);
101
- const res = await fetch(api, {
102
- method: "POST",
103
- body: JSON.stringify({
104
- messages: sendExtraMessageFields ? messagesSnapshot : messagesSnapshot.map(
105
- ({ role, content, name, function_call }) => ({
106
- role,
107
- content,
108
- ...name !== void 0 && { name },
109
- ...function_call !== void 0 && {
110
- function_call
444
+ let chatRequest = {
445
+ messages: messagesSnapshot,
446
+ options
447
+ };
448
+ await processChatStream({
449
+ getStreamedResponse: async () => {
450
+ var _a;
451
+ const existingData = (_a = streamData()) != null ? _a : [];
452
+ return await callApi({
453
+ api,
454
+ messages: sendExtraMessageFields ? chatRequest.messages : chatRequest.messages.map(
455
+ ({ role, content, name, function_call }) => ({
456
+ role,
457
+ content,
458
+ ...name !== void 0 && { name },
459
+ ...function_call !== void 0 && {
460
+ function_call
461
+ }
462
+ })
463
+ ),
464
+ body: {
465
+ ...body,
466
+ ...options == null ? void 0 : options.body
467
+ },
468
+ headers: {
469
+ ...headers,
470
+ ...options == null ? void 0 : options.headers
471
+ },
472
+ abortController: () => abortController,
473
+ credentials,
474
+ onResponse,
475
+ onUpdate(merged, data) {
476
+ mutate([...chatRequest.messages, ...merged]);
477
+ setStreamData([...existingData, ...data != null ? data : []]);
478
+ },
479
+ onFinish,
480
+ appendMessage(message) {
481
+ mutate([...chatRequest.messages, message]);
482
+ },
483
+ restoreMessagesOnFailure() {
484
+ if (previousMessages.status === "success") {
485
+ mutate(previousMessages.data);
111
486
  }
112
- })
113
- ),
114
- ...body,
115
- ...options == null ? void 0 : options.body
116
- }),
117
- headers: {
118
- ...headers,
119
- ...options == null ? void 0 : options.headers
487
+ }
488
+ });
120
489
  },
121
- signal: abortController.signal,
122
- credentials
123
- }).catch((err) => {
124
- if (previousMessages.status === "success") {
125
- mutate(previousMessages.data);
126
- }
127
- throw err;
490
+ experimental_onFunctionCall,
491
+ updateChatRequest(newChatRequest) {
492
+ chatRequest = newChatRequest;
493
+ },
494
+ getCurrentMessages: () => getCurrentMessages().data
128
495
  });
129
- if (onResponse) {
130
- try {
131
- await onResponse(res);
132
- } catch (err) {
133
- throw err;
134
- }
135
- }
136
- if (!res.ok) {
137
- if (previousMessages.status === "success") {
138
- mutate(previousMessages.data);
139
- }
140
- throw new Error(
141
- await res.text() || "Failed to fetch the chat response."
142
- );
143
- }
144
- if (!res.body) {
145
- throw new Error("The response body is empty.");
146
- }
147
- let result = "";
148
- const createdAt = /* @__PURE__ */ new Date();
149
- const replyId = nanoid();
150
- const reader = res.body.getReader();
151
- const decoder = createChunkDecoder();
152
- while (true) {
153
- const { done, value } = await reader.read();
154
- if (done) {
155
- break;
156
- }
157
- result += decoder(value);
158
- mutate([
159
- ...messagesSnapshot,
160
- {
161
- id: replyId,
162
- createdAt,
163
- content: result,
164
- role: "assistant"
165
- }
166
- ]);
167
- if (abortController === null) {
168
- reader.cancel();
169
- break;
170
- }
171
- }
172
- if (onFinish) {
173
- onFinish({
174
- id: replyId,
175
- createdAt,
176
- content: result,
177
- role: "assistant"
178
- });
179
- }
180
496
  abortController = null;
181
- return result;
182
497
  } catch (err) {
183
498
  if (err.name === "AbortError") {
184
499
  abortController = null;
@@ -244,7 +559,8 @@ function useChat({
244
559
  input,
245
560
  setInput,
246
561
  handleSubmit,
247
- isLoading
562
+ isLoading,
563
+ data: streamData
248
564
  };
249
565
  }
250
566
 
@@ -201,6 +201,9 @@ type UseCompletionOptions = {
201
201
  */
202
202
  body?: object;
203
203
  };
204
+ type JSONValue = null | string | number | boolean | {
205
+ [x: string]: JSONValue;
206
+ } | Array<JSONValue>;
204
207
 
205
208
  type UseChatHelpers = {
206
209
  /** Current messages in the chat */
@@ -237,6 +240,8 @@ type UseChatHelpers = {
237
240
  metadata?: Object;
238
241
  /** Whether the API request is in progress */
239
242
  isLoading: Readable<boolean | undefined>;
243
+ /** Additional data added on the server via StreamData */
244
+ data: Readable<JSONValue[] | undefined>;
240
245
  };
241
246
  declare function useChat({ api, id, initialMessages, initialInput, sendExtraMessageFields, experimental_onFunctionCall, onResponse, onFinish, onError, credentials, headers, body, }?: UseChatOptions): UseChatHelpers;
242
247