ai 2.2.22 → 2.2.24

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.mjs CHANGED
@@ -116,6 +116,135 @@ function readableFromAsyncIterable(iterable) {
116
116
  });
117
117
  }
118
118
 
119
+ // shared/stream-parts.ts
120
+ var textStreamPart = {
121
+ code: "0",
122
+ name: "text",
123
+ parse: (value) => {
124
+ if (typeof value !== "string") {
125
+ throw new Error('"text" parts expect a string value.');
126
+ }
127
+ return { type: "text", value };
128
+ }
129
+ };
130
+ var functionCallStreamPart = {
131
+ code: "1",
132
+ name: "function_call",
133
+ parse: (value) => {
134
+ 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") {
135
+ throw new Error(
136
+ '"function_call" parts expect an object with a "function_call" property.'
137
+ );
138
+ }
139
+ return {
140
+ type: "function_call",
141
+ value
142
+ };
143
+ }
144
+ };
145
+ var dataStreamPart = {
146
+ code: "2",
147
+ name: "data",
148
+ parse: (value) => {
149
+ if (!Array.isArray(value)) {
150
+ throw new Error('"data" parts expect an array value.');
151
+ }
152
+ return { type: "data", value };
153
+ }
154
+ };
155
+ var errorStreamPart = {
156
+ code: "3",
157
+ name: "error",
158
+ parse: (value) => {
159
+ if (typeof value !== "string") {
160
+ throw new Error('"error" parts expect a string value.');
161
+ }
162
+ return { type: "error", value };
163
+ }
164
+ };
165
+ var assistantMessage = {
166
+ code: "4",
167
+ name: "assistant_message",
168
+ parse: (value) => {
169
+ 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(
170
+ (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"
171
+ )) {
172
+ throw new Error(
173
+ '"assistant_message" parts expect an object with an "id", "role", and "content" property.'
174
+ );
175
+ }
176
+ return {
177
+ type: "assistant_message",
178
+ value
179
+ };
180
+ }
181
+ };
182
+ var assistantControlData = {
183
+ code: "5",
184
+ name: "assistant_control_data",
185
+ parse: (value) => {
186
+ if (value == null || typeof value !== "object" || !("threadId" in value) || !("messageId" in value) || typeof value.threadId !== "string" || typeof value.messageId !== "string") {
187
+ throw new Error(
188
+ '"assistant_control_data" parts expect an object with a "threadId" and "messageId" property.'
189
+ );
190
+ }
191
+ return {
192
+ type: "assistant_control_data",
193
+ value: {
194
+ threadId: value.threadId,
195
+ messageId: value.messageId
196
+ }
197
+ };
198
+ }
199
+ };
200
+ var streamParts = [
201
+ textStreamPart,
202
+ functionCallStreamPart,
203
+ dataStreamPart,
204
+ errorStreamPart,
205
+ assistantMessage,
206
+ assistantControlData
207
+ ];
208
+ var streamPartsByCode = {
209
+ [textStreamPart.code]: textStreamPart,
210
+ [functionCallStreamPart.code]: functionCallStreamPart,
211
+ [dataStreamPart.code]: dataStreamPart,
212
+ [errorStreamPart.code]: errorStreamPart,
213
+ [assistantMessage.code]: assistantMessage,
214
+ [assistantControlData.code]: assistantControlData
215
+ };
216
+ var StreamStringPrefixes = {
217
+ [textStreamPart.name]: textStreamPart.code,
218
+ [functionCallStreamPart.name]: functionCallStreamPart.code,
219
+ [dataStreamPart.name]: dataStreamPart.code,
220
+ [errorStreamPart.name]: errorStreamPart.code,
221
+ [assistantMessage.name]: assistantMessage.code,
222
+ [assistantControlData.name]: assistantControlData.code
223
+ };
224
+ var validCodes = streamParts.map((part) => part.code);
225
+ var parseStreamPart = (line) => {
226
+ const firstSeparatorIndex = line.indexOf(":");
227
+ if (firstSeparatorIndex === -1) {
228
+ throw new Error("Failed to parse stream string. No separator found.");
229
+ }
230
+ const prefix = line.slice(0, firstSeparatorIndex);
231
+ if (!validCodes.includes(prefix)) {
232
+ throw new Error(`Failed to parse stream string. Invalid code ${prefix}.`);
233
+ }
234
+ const code = prefix;
235
+ const textValue = line.slice(firstSeparatorIndex + 1);
236
+ const jsonValue = JSON.parse(textValue);
237
+ return streamPartsByCode[code].parse(jsonValue);
238
+ };
239
+ function formatStreamPart(type, value) {
240
+ const streamPart = streamParts.find((part) => part.name === type);
241
+ if (!streamPart) {
242
+ throw new Error(`Invalid stream part type: ${type}`);
243
+ }
244
+ return `${streamPart.code}:${JSON.stringify(value)}
245
+ `;
246
+ }
247
+
119
248
  // shared/utils.ts
120
249
  import { customAlphabet } from "nanoid/non-secure";
121
250
  var nanoid = customAlphabet(
@@ -133,36 +262,10 @@ function createChunkDecoder(complex) {
133
262
  }
134
263
  return function(chunk) {
135
264
  const decoded = decoder.decode(chunk, { stream: true }).split("\n").filter((line) => line !== "");
136
- return decoded.map(getStreamStringTypeAndValue).filter(Boolean);
265
+ return decoded.map(parseStreamPart).filter(Boolean);
137
266
  };
138
267
  }
139
- var StreamStringPrefixes = {
140
- text: 0,
141
- function_call: 1,
142
- data: 2
143
- // user_err: 3?
144
- };
145
268
  var isStreamStringEqualToType = (type, value) => value.startsWith(`${StreamStringPrefixes[type]}:`) && value.endsWith("\n");
146
- var getStreamString = (type, value) => `${StreamStringPrefixes[type]}:${JSON.stringify(value)}
147
- `;
148
- var getStreamStringTypeAndValue = (line) => {
149
- const firstSeperatorIndex = line.indexOf(":");
150
- const prefix = line.slice(0, firstSeperatorIndex);
151
- const type = Object.keys(StreamStringPrefixes).find(
152
- (key) => StreamStringPrefixes[key] === Number(prefix)
153
- );
154
- const val = line.slice(firstSeperatorIndex + 1);
155
- let parsedVal = val;
156
- if (!val) {
157
- return { type, value: "" };
158
- }
159
- try {
160
- parsedVal = JSON.parse(val);
161
- } catch (e) {
162
- console.error("Failed to parse JSON value:", val);
163
- }
164
- return { type, value: parsedVal };
165
- };
166
269
  var COMPLEX_HEADER = "X-Experimental-Stream-Data";
167
270
 
168
271
  // streams/stream-data.ts
@@ -188,7 +291,7 @@ var experimental_StreamData = class {
188
291
  transform: async (chunk, controller) => {
189
292
  if (self.data.length > 0) {
190
293
  const encodedData = self.encoder.encode(
191
- getStreamString("data", JSON.stringify(self.data))
294
+ formatStreamPart("data", self.data)
192
295
  );
193
296
  self.data = [];
194
297
  controller.enqueue(encodedData);
@@ -207,7 +310,7 @@ var experimental_StreamData = class {
207
310
  }
208
311
  if (self.data.length) {
209
312
  const encodedData = self.encoder.encode(
210
- getStreamString("data", JSON.stringify(self.data))
313
+ formatStreamPart("data", self.data)
211
314
  );
212
315
  controller.enqueue(encodedData);
213
316
  }
@@ -245,7 +348,7 @@ function createStreamDataTransformer(experimental_streamData) {
245
348
  return new TransformStream({
246
349
  transform: async (chunk, controller) => {
247
350
  const message = decoder.decode(chunk);
248
- controller.enqueue(encoder.encode(getStreamString("text", message)));
351
+ controller.enqueue(encoder.encode(formatStreamPart("text", message)));
249
352
  }
250
353
  });
251
354
  }
@@ -353,7 +456,7 @@ function createFunctionCallTransformer(callbacks) {
353
456
  }
354
457
  if (!isFunctionStreamingIn) {
355
458
  controller.enqueue(
356
- isComplexMode ? textEncoder.encode(getStreamString("text", message)) : chunk
459
+ isComplexMode ? textEncoder.encode(formatStreamPart("text", message)) : chunk
357
460
  );
358
461
  return;
359
462
  } else {
@@ -395,13 +498,17 @@ function createFunctionCallTransformer(callbacks) {
395
498
  if (!functionResponse) {
396
499
  controller.enqueue(
397
500
  textEncoder.encode(
398
- isComplexMode ? getStreamString("function_call", aggregatedResponse) : aggregatedResponse
501
+ isComplexMode ? formatStreamPart(
502
+ "function_call",
503
+ // parse to prevent double-encoding:
504
+ JSON.parse(aggregatedResponse)
505
+ ) : aggregatedResponse
399
506
  )
400
507
  );
401
508
  return;
402
509
  } else if (typeof functionResponse === "string") {
403
510
  controller.enqueue(
404
- isComplexMode ? textEncoder.encode(getStreamString("text", functionResponse)) : textEncoder.encode(functionResponse)
511
+ isComplexMode ? textEncoder.encode(formatStreamPart("text", functionResponse)) : textEncoder.encode(functionResponse)
405
512
  );
406
513
  return;
407
514
  }
@@ -666,6 +773,100 @@ async function ReplicateStream(res, cb, options) {
666
773
  );
667
774
  }
668
775
 
776
+ // shared/parse-complex-response.ts
777
+ async function parseComplexResponse({
778
+ reader,
779
+ abortControllerRef,
780
+ update,
781
+ onFinish,
782
+ generateId = nanoid,
783
+ getCurrentDate = () => /* @__PURE__ */ new Date()
784
+ }) {
785
+ const createdAt = getCurrentDate();
786
+ const decode = createChunkDecoder(true);
787
+ const prefixMap = {
788
+ data: []
789
+ };
790
+ const NEWLINE = "\n".charCodeAt(0);
791
+ const chunks = [];
792
+ let totalLength = 0;
793
+ while (true) {
794
+ const { value } = await reader.read();
795
+ if (value) {
796
+ chunks.push(value);
797
+ totalLength += value.length;
798
+ if (value[value.length - 1] !== NEWLINE) {
799
+ continue;
800
+ }
801
+ }
802
+ if (chunks.length === 0) {
803
+ break;
804
+ }
805
+ let concatenatedChunks = new Uint8Array(totalLength);
806
+ let offset = 0;
807
+ for (const chunk of chunks) {
808
+ concatenatedChunks.set(chunk, offset);
809
+ offset += chunk.length;
810
+ }
811
+ chunks.length = 0;
812
+ totalLength = 0;
813
+ const lines = decode(concatenatedChunks);
814
+ if (typeof lines === "string") {
815
+ throw new Error(
816
+ "Invalid response format. Complex mode was set but the response is a string. This should never happen."
817
+ );
818
+ }
819
+ for (const { type, value: value2 } of lines) {
820
+ if (type === "text") {
821
+ if (prefixMap["text"]) {
822
+ prefixMap["text"] = {
823
+ ...prefixMap["text"],
824
+ content: (prefixMap["text"].content || "") + value2
825
+ };
826
+ } else {
827
+ prefixMap["text"] = {
828
+ id: generateId(),
829
+ role: "assistant",
830
+ content: value2,
831
+ createdAt
832
+ };
833
+ }
834
+ }
835
+ let functionCallMessage = null;
836
+ if (type === "function_call") {
837
+ prefixMap["function_call"] = {
838
+ id: generateId(),
839
+ role: "assistant",
840
+ content: "",
841
+ function_call: value2.function_call,
842
+ name: value2.function_call.name,
843
+ createdAt
844
+ };
845
+ functionCallMessage = prefixMap["function_call"];
846
+ }
847
+ if (type === "data") {
848
+ prefixMap["data"].push(...value2);
849
+ }
850
+ const responseMessage = prefixMap["text"];
851
+ const merged = [functionCallMessage, responseMessage].filter(
852
+ Boolean
853
+ );
854
+ update(merged, [...prefixMap["data"]]);
855
+ if ((abortControllerRef == null ? void 0 : abortControllerRef.current) === null) {
856
+ reader.cancel();
857
+ break;
858
+ }
859
+ }
860
+ }
861
+ onFinish == null ? void 0 : onFinish(prefixMap);
862
+ return {
863
+ messages: [prefixMap.text, prefixMap.function_call].filter(
864
+ Boolean
865
+ ),
866
+ data: prefixMap.data
867
+ };
868
+ }
869
+
669
870
  // streams/streaming-react-response.ts
670
871
  var experimental_StreamingReactResponse = class {
671
872
  constructor(res, options) {
@@ -674,6 +875,39 @@ var experimental_StreamingReactResponse = class {
674
875
  let next = new Promise((resolve) => {
675
876
  resolveFunc = resolve;
676
877
  });
878
+ if (options == null ? void 0 : options.data) {
879
+ const processedStream = res.pipeThrough(
880
+ options.data.stream
881
+ );
882
+ let lastPayload = void 0;
883
+ parseComplexResponse({
884
+ reader: processedStream.getReader(),
885
+ update: (merged, data) => {
886
+ var _a, _b, _c;
887
+ const content2 = (_b = (_a = merged[0]) == null ? void 0 : _a.content) != null ? _b : "";
888
+ const ui = ((_c = options == null ? void 0 : options.ui) == null ? void 0 : _c.call(options, { content: content2, data })) || content2;
889
+ const payload = { ui, content: content2 };
890
+ const resolvePrevious = resolveFunc;
891
+ const nextRow = new Promise((resolve) => {
892
+ resolveFunc = resolve;
893
+ });
894
+ resolvePrevious({
895
+ next: nextRow,
896
+ ...payload
897
+ });
898
+ lastPayload = payload;
899
+ },
900
+ onFinish: () => {
901
+ if (lastPayload !== void 0) {
902
+ resolveFunc({
903
+ next: null,
904
+ ...lastPayload
905
+ });
906
+ }
907
+ }
908
+ });
909
+ return next;
910
+ }
677
911
  let content = "";
678
912
  const decode = createChunkDecoder();
679
913
  const reader = res.getReader();
@@ -705,6 +939,55 @@ var experimental_StreamingReactResponse = class {
705
939
  return next;
706
940
  }
707
941
  };
942
+
943
+ // streams/assistant-response.ts
944
+ function experimental_AssistantResponse({ threadId, messageId }, process2) {
945
+ const stream = new ReadableStream({
946
+ async start(controller) {
947
+ var _a;
948
+ const textEncoder = new TextEncoder();
949
+ const sendMessage = (message) => {
950
+ controller.enqueue(
951
+ textEncoder.encode(formatStreamPart("assistant_message", message))
952
+ );
953
+ };
954
+ const sendError = (errorMessage) => {
955
+ controller.enqueue(
956
+ textEncoder.encode(formatStreamPart("error", errorMessage))
957
+ );
958
+ };
959
+ controller.enqueue(
960
+ textEncoder.encode(
961
+ formatStreamPart("assistant_control_data", {
962
+ threadId,
963
+ messageId
964
+ })
965
+ )
966
+ );
967
+ try {
968
+ await process2({
969
+ threadId,
970
+ messageId,
971
+ sendMessage
972
+ });
973
+ } catch (error) {
974
+ sendError((_a = error.message) != null ? _a : `${error}`);
975
+ } finally {
976
+ controller.close();
977
+ }
978
+ },
979
+ pull(controller) {
980
+ },
981
+ cancel() {
982
+ }
983
+ });
984
+ return new Response(stream, {
985
+ status: 200,
986
+ headers: {
987
+ "Content-Type": "text/plain; charset=utf-8"
988
+ }
989
+ });
990
+ }
708
991
  export {
709
992
  AIStream,
710
993
  AnthropicStream,
@@ -714,16 +997,14 @@ export {
714
997
  LangChainStream,
715
998
  OpenAIStream,
716
999
  ReplicateStream,
717
- StreamStringPrefixes,
718
1000
  StreamingTextResponse,
719
1001
  createCallbacksTransformer,
720
1002
  createChunkDecoder,
721
1003
  createEventStreamTransformer,
722
1004
  createStreamDataTransformer,
1005
+ experimental_AssistantResponse,
723
1006
  experimental_StreamData,
724
1007
  experimental_StreamingReactResponse,
725
- getStreamString,
726
- getStreamStringTypeAndValue,
727
1008
  isStreamStringEqualToType,
728
1009
  nanoid,
729
1010
  readableFromAsyncIterable,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai",
3
- "version": "2.2.22",
3
+ "version": "2.2.24",
4
4
  "license": "Apache-2.0",
5
5
  "sideEffects": false,
6
6
  "main": "./dist/index.js",
@@ -54,10 +54,6 @@
54
54
  "require": "./solid/dist/index.js"
55
55
  }
56
56
  },
57
- "jest": {
58
- "preset": "ts-jest",
59
- "testEnvironment": "node"
60
- },
61
57
  "dependencies": {
62
58
  "eventsource-parser": "1.0.0",
63
59
  "nanoid": "3.3.6",
@@ -70,12 +66,17 @@
70
66
  "devDependencies": {
71
67
  "@edge-runtime/jest-environment": "1.1.0-beta.31",
72
68
  "@huggingface/inference": "2.6.4",
69
+ "@testing-library/jest-dom": "^6.1.4",
70
+ "@testing-library/react": "^14.0.0",
71
+ "@testing-library/user-event": "^14.5.1",
73
72
  "@types/jest": "29.2.0",
74
73
  "@types/node": "^17.0.12",
75
74
  "@types/react": "^18.2.8",
76
75
  "@types/react-dom": "^18.2.0",
77
76
  "eslint": "^7.32.0",
78
77
  "jest": "29.2.1",
78
+ "jest-environment-jsdom": "^29.7.0",
79
+ "jsdom": "^22.1.0",
79
80
  "langchain": "0.0.172",
80
81
  "openai": "4.16.1",
81
82
  "ts-jest": "29.0.3",
@@ -132,6 +133,9 @@
132
133
  "lint": "eslint \"./**/*.ts*\"",
133
134
  "type-check": "tsc --noEmit",
134
135
  "prettier-check": "prettier --check \"./**/*.ts*\"",
135
- "test": "jest --forceExit --env @edge-runtime/jest-environment .test.ts && jest --forceExit --env node .test.ts"
136
+ "test": "pnpm test:node && pnpm test:edge && pnpm test:ui",
137
+ "test:edge": "jest --forceExit --config jest.node.config.js --env @edge-runtime/jest-environment .test.ts ",
138
+ "test:node": "jest --forceExit --config jest.node.config.js --env node .test.ts",
139
+ "test:ui": "jest --forceExit --config jest.ui.config.js .ui.test.ts"
136
140
  }
137
141
  }
@@ -62,6 +62,7 @@ type ChatRequest = {
62
62
  options?: RequestOptions;
63
63
  functions?: Array<Function>;
64
64
  function_call?: FunctionCall;
65
+ data?: Record<string, string>;
65
66
  };
66
67
  type FunctionCallHandler = (chatMessages: Message[], functionCall: FunctionCall) => Promise<ChatRequest | void>;
67
68
  type RequestOptions = {
@@ -72,6 +73,7 @@ type ChatRequestOptions = {
72
73
  options?: RequestOptions;
73
74
  functions?: Array<Function>;
74
75
  function_call?: FunctionCall;
76
+ data?: Record<string, string>;
75
77
  };
76
78
  type UseChatOptions = {
77
79
  /**
@@ -197,6 +199,25 @@ type UseCompletionOptions = {
197
199
  */
198
200
  body?: object;
199
201
  };
202
+ type JSONValue = null | string | number | boolean | {
203
+ [x: string]: JSONValue;
204
+ } | Array<JSONValue>;
205
+
206
+ /**
207
+ * A stream wrapper to send custom JSON-encoded data back to the client.
208
+ */
209
+ declare class experimental_StreamData {
210
+ private encoder;
211
+ private controller;
212
+ stream: TransformStream<Uint8Array, Uint8Array>;
213
+ private isClosedPromise;
214
+ private isClosedPromiseResolver;
215
+ private isClosed;
216
+ private data;
217
+ constructor();
218
+ close(): Promise<void>;
219
+ append(value: JSONValue): void;
220
+ }
200
221
 
201
222
  /**
202
223
  * This is a naive implementation of the streaming React response API.
@@ -207,6 +228,7 @@ type UseCompletionOptions = {
207
228
  * It is naive as unlike the StreamingTextResponse, it does not send the diff
208
229
  * between the rows, but flushing the full payload on each row.
209
230
  */
231
+
210
232
  type UINode = string | JSX.Element | JSX.Element[] | null | undefined;
211
233
  /**
212
234
  * A utility class for streaming React responses.
@@ -215,7 +237,9 @@ declare class experimental_StreamingReactResponse {
215
237
  constructor(res: ReadableStream, options?: {
216
238
  ui?: (message: {
217
239
  content: string;
240
+ data?: JSONValue[] | undefined;
218
241
  }) => UINode | Promise<UINode>;
242
+ data?: experimental_StreamData;
219
243
  });
220
244
  }
221
245
 
@@ -259,10 +283,11 @@ type UseChatHelpers = {
259
283
  /** Whether the API request is in progress */
260
284
  isLoading: boolean;
261
285
  /** Additional data added on the server via StreamData */
262
- data?: any;
286
+ data?: JSONValue[] | undefined;
263
287
  };
264
288
  type StreamingReactResponseAction = (payload: {
265
289
  messages: Message[];
290
+ data?: Record<string, string>;
266
291
  }) => Promise<experimental_StreamingReactResponse>;
267
292
  declare function useChat({ api, id, initialMessages, initialInput, sendExtraMessageFields, experimental_onFunctionCall, onResponse, onFinish, onError, credentials, headers, body, }?: Omit<UseChatOptions, 'api'> & {
268
293
  api?: string | StreamingReactResponseAction;
@@ -312,7 +337,20 @@ type UseCompletionHelpers = {
312
337
  };
313
338
  declare function useCompletion({ api, id, initialCompletion, initialInput, credentials, headers, body, onResponse, onFinish, onError, }?: UseCompletionOptions): UseCompletionHelpers;
314
339
 
315
- export { CreateMessage, Message, UseChatHelpers, UseChatOptions, UseCompletionHelpers, useChat, useCompletion };
340
+ type AssistantStatus = 'in_progress' | 'awaiting_message';
341
+ declare function experimental_useAssistant({ api, threadId: threadIdParam, }: {
342
+ api: string;
343
+ threadId?: string | undefined;
344
+ }): {
345
+ messages: Message[];
346
+ input: string;
347
+ handleInputChange: (e: any) => void;
348
+ submitMessage: (e: any) => Promise<void>;
349
+ status: AssistantStatus;
350
+ error: unknown;
351
+ };
352
+
353
+ export { AssistantStatus, CreateMessage, Message, UseChatHelpers, UseChatOptions, UseCompletionHelpers, experimental_useAssistant, useChat, useCompletion };
316
354
  import * as react_jsx_runtime from 'react/jsx-runtime';
317
355
 
318
356
  type Props = {