ai 2.1.2 → 2.1.6

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/README.md ADDED
@@ -0,0 +1,91 @@
1
+ # Vercel AI SDK
2
+
3
+ The Vercel AI SDK is **a library for building edge-ready AI-powered streaming text and chat UIs**.
4
+
5
+ ## Features
6
+
7
+ - [SWR](https://swr.vercel.app)-powered React, Svelte and Vue helpers for streaming text responses and building chat and completion UIs
8
+ - First-class support for [LangChain](js.langchain.com/docs) and [OpenAI](https://openai.com), [Anthropic](https://www.anthropic.com), and [HuggingFace](https://huggingface.co)
9
+ - [Edge Runtime](https://edge-runtime.vercel.app/) compatibility
10
+ - Callbacks for saving completed streaming responses to a database (in the same request)
11
+
12
+ ## Installation
13
+
14
+ ```sh
15
+ pnpm install ai
16
+ ```
17
+
18
+ View the full documentation and examples on [sdk.vercel.ai/docs](https://sdk.vercel.ai/docs)
19
+
20
+ ## Example: An AI Chatbot with Next.js and OpenAI
21
+
22
+ With the Vercel AI SDK, you can build a ChatGPT-like app in just a few lines of code:
23
+
24
+ ```tsx
25
+ // ./app/api/chat/route.js
26
+ import { Configuration, OpenAIApi } from 'openai-edge'
27
+ import { OpenAIStream, StreamingTextResponse } from 'ai'
28
+
29
+ const config = new Configuration({
30
+ apiKey: process.env.OPENAI_API_KEY
31
+ })
32
+ const openai = new OpenAIApi(config)
33
+
34
+ export const runtime = 'edge'
35
+
36
+ export async function POST(req) {
37
+ const { messages } = await req.json()
38
+ const response = await openai.createChatCompletion({
39
+ model: 'gpt-4',
40
+ stream: true,
41
+ messages
42
+ })
43
+ const stream = OpenAIStream(response)
44
+ return new StreamingTextResponse(stream)
45
+ }
46
+ ```
47
+
48
+ ```tsx
49
+ // ./app/page.js
50
+ 'use client'
51
+
52
+ import { useChat } from 'ai/react'
53
+
54
+ export default function Chat() {
55
+ const { messages, input, handleInputChange, handleSubmit } = useChat()
56
+
57
+ return (
58
+ <div>
59
+ {messages.map(m => (
60
+ <div key={m.id}>
61
+ {m.role}: {m.content}
62
+ </div>
63
+ ))}
64
+
65
+ <form onSubmit={handleSubmit}>
66
+ <input
67
+ value={input}
68
+ placeholder="Say something..."
69
+ onChange={handleInputChange}
70
+ />
71
+ </form>
72
+ </div>
73
+ )
74
+ }
75
+ ```
76
+
77
+ ---
78
+
79
+ View the full documentation and examples on [sdk.vercel.ai/docs](https://sdk.vercel.ai/docs)
80
+
81
+ ## Authors
82
+
83
+ This library is created by [Vercel](https://vercel.com) and [Next.js](https://nextjs.org) team members, with contributions from:
84
+
85
+ - Jared Palmer ([@jaredpalmer](https://twitter.com/jaredpalmer)) - [Vercel](https://vercel.com)
86
+ - Shu Ding ([@shuding\_](https://twitter.com/shuding_)) - [Vercel](https://vercel.com)
87
+ - Max Leiter ([@max_leiter](https://twitter.com/max_leiter)) - [Vercel](https://vercel.com)
88
+ - Malte Ubl ([@cramforce](https://twitter.com/cramforce)) - [Vercel](https://vercel.com)
89
+ - Justin Ridgewell ([@jridgewell](https://github.com/jridgewell)) - [Vercel](https://vercel.com)
90
+
91
+ [Contributors](https://github.com/vercel-labs/ai/graphs/contributors)
package/dist/index.d.ts CHANGED
@@ -1,21 +1,75 @@
1
1
  import { ServerResponse } from 'node:http';
2
2
 
3
+ /**
4
+ * Helper callback methods for AIStream stream lifecycle events
5
+ * @interface
6
+ */
3
7
  interface AIStreamCallbacks {
4
8
  onStart?: () => Promise<void>;
5
9
  onCompletion?: (completion: string) => Promise<void>;
6
10
  onToken?: (token: string) => Promise<void>;
7
11
  }
12
+ /**
13
+ * Custom parser for AIStream data.
14
+ * @interface
15
+ */
8
16
  interface AIStreamParser {
9
17
  (data: string): string | void;
10
18
  }
19
+ /**
20
+ * Creates a TransformStream that parses events from an EventSource stream using a custom parser.
21
+ * @param {AIStreamParser} customParser - Function to handle event data.
22
+ * @returns {TransformStream<Uint8Array, string>} TransformStream parsing events.
23
+ */
11
24
  declare function createEventStreamTransformer(customParser: AIStreamParser): TransformStream<Uint8Array, string>;
12
25
  /**
13
- * This stream forks input stream, allowing us to use the result as a
14
- * bytestream of the messages and pass the messages to our callback interface.
26
+ * Creates a transform stream that encodes input messages and invokes optional callback functions.
27
+ * The transform stream uses the provided callbacks to execute custom logic at different stages of the stream's lifecycle.
28
+ * - `onStart`: Called once when the stream is initialized.
29
+ * - `onToken`: Called for each tokenized message.
30
+ * - `onCompletion`: Called once when the stream is flushed, with the aggregated messages.
31
+ *
32
+ * This function is useful when you want to process a stream of messages and perform specific actions during the stream's lifecycle.
33
+ *
34
+ * @param {AIStreamCallbacks} [callbacks] - An object containing the callback functions.
35
+ * @return {TransformStream<string, Uint8Array>} A transform stream that encodes input messages as Uint8Array and allows the execution of custom logic through callbacks.
36
+ *
37
+ * @example
38
+ * const callbacks = {
39
+ * onStart: async () => console.log('Stream started'),
40
+ * onToken: async (token) => console.log(`Token: ${token}`),
41
+ * onCompletion: async (completion) => console.log(`Completion: ${completion}`)
42
+ * };
43
+ * const transformer = createCallbacksTransformer(callbacks);
15
44
  */
16
45
  declare function createCallbacksTransformer(callbacks: AIStreamCallbacks | undefined): TransformStream<string, Uint8Array>;
46
+ /**
47
+ * Returns a stateful function that, when invoked, trims leading whitespace
48
+ * from the input text. The trimming only occurs on the first invocation, ensuring that
49
+ * subsequent calls do not alter the input text. This is particularly useful in scenarios
50
+ * where a text stream is being processed and only the initial whitespace should be removed.
51
+ *
52
+ * @return {function(string): string} A function that takes a string as input and returns a string
53
+ * with leading whitespace removed if it is the first invocation; otherwise, it returns the input unchanged.
54
+ *
55
+ * @example
56
+ * const trimStart = trimStartOfStreamHelper();
57
+ * const output1 = trimStart(" text"); // "text"
58
+ * const output2 = trimStart(" text"); // " text"
59
+ *
60
+ */
17
61
  declare function trimStartOfStreamHelper(): (text: string) => string;
18
- declare function AIStream(res: Response, customParser: AIStreamParser, callbacks?: AIStreamCallbacks): ReadableStream;
62
+ /**
63
+ * Returns a ReadableStream created from the response, parsed and handled with custom logic.
64
+ * The stream goes through two transformation stages, first parsing the events and then
65
+ * invoking the provided callbacks.
66
+ * @param {Response} response - The response.
67
+ * @param {AIStreamParser} customParser - The custom parser function.
68
+ * @param {AIStreamCallbacks} callbacks - The callbacks.
69
+ * @return {ReadableStream} The AIStream.
70
+ * @throws Will throw an error if the response is not OK.
71
+ */
72
+ declare function AIStream(response: Response, customParser: AIStreamParser, callbacks?: AIStreamCallbacks): ReadableStream;
19
73
 
20
74
  declare function OpenAIStream(res: Response, cb?: AIStreamCallbacks): ReadableStream;
21
75
 
@@ -49,19 +103,19 @@ declare function LangChainStream(callbacks?: AIStreamCallbacks): {
49
103
  /**
50
104
  * Shared types between the API and UI packages.
51
105
  */
52
- type Message = {
106
+ declare type Message = {
53
107
  id: string;
54
108
  createdAt?: Date;
55
109
  content: string;
56
110
  role: 'system' | 'user' | 'assistant';
57
111
  };
58
- type CreateMessage = {
112
+ declare type CreateMessage = {
59
113
  id?: string;
60
114
  createdAt?: Date;
61
115
  content: string;
62
116
  role: 'system' | 'user' | 'assistant';
63
117
  };
64
- type UseChatOptions = {
118
+ declare type UseChatOptions = {
65
119
  /**
66
120
  * The API endpoint that accepts a `{ messages: Message[] }` object and returns
67
121
  * a stream of tokens of the AI chat response. Defaults to `/api/chat`.
@@ -117,7 +171,7 @@ type UseChatOptions = {
117
171
  */
118
172
  sendExtraMessageFields?: boolean;
119
173
  };
120
- type UseCompletionOptions = {
174
+ declare type UseCompletionOptions = {
121
175
  /**
122
176
  * The API endpoint that accepts a `{ prompt: string }` object and returns
123
177
  * a stream of tokens of the AI completion response. Defaults to `/api/completion`.
package/dist/index.js CHANGED
@@ -73,34 +73,34 @@ module.exports = __toCommonJS(streams_exports);
73
73
  // streams/ai-stream.ts
74
74
  var import_eventsource_parser = require("eventsource-parser");
75
75
  function createEventStreamTransformer(customParser) {
76
- const decoder = new TextDecoder();
77
- let parser;
76
+ const textDecoder = new TextDecoder();
77
+ let eventSourceParser;
78
78
  return new TransformStream({
79
79
  start(controller) {
80
80
  return __async(this, null, function* () {
81
- function onParse(event) {
82
- if (event.type === "event") {
83
- const data = event.data;
84
- if (data === "[DONE]") {
81
+ eventSourceParser = (0, import_eventsource_parser.createParser)(
82
+ (event) => {
83
+ if ("data" in event && event.type === "event" && event.data === "[DONE]") {
85
84
  controller.terminate();
86
85
  return;
87
86
  }
88
- const message = customParser(data);
89
- if (message)
90
- controller.enqueue(message);
87
+ if ("data" in event) {
88
+ const parsedMessage = customParser(event.data);
89
+ if (parsedMessage)
90
+ controller.enqueue(parsedMessage);
91
+ }
91
92
  }
92
- }
93
- parser = (0, import_eventsource_parser.createParser)(onParse);
93
+ );
94
94
  });
95
95
  },
96
96
  transform(chunk) {
97
- parser.feed(decoder.decode(chunk));
97
+ eventSourceParser.feed(textDecoder.decode(chunk));
98
98
  }
99
99
  });
100
100
  }
101
101
  function createCallbacksTransformer(callbacks) {
102
- const encoder = new TextEncoder();
103
- let fullResponse = "";
102
+ const textEncoder = new TextEncoder();
103
+ let aggregatedResponse = "";
104
104
  const { onStart, onToken, onCompletion } = callbacks || {};
105
105
  return new TransformStream({
106
106
  start() {
@@ -111,42 +111,47 @@ function createCallbacksTransformer(callbacks) {
111
111
  },
112
112
  transform(message, controller) {
113
113
  return __async(this, null, function* () {
114
- controller.enqueue(encoder.encode(message));
114
+ controller.enqueue(textEncoder.encode(message));
115
115
  if (onToken)
116
116
  yield onToken(message);
117
117
  if (onCompletion)
118
- fullResponse += message;
118
+ aggregatedResponse += message;
119
119
  });
120
120
  },
121
121
  flush() {
122
122
  return __async(this, null, function* () {
123
- yield onCompletion == null ? void 0 : onCompletion(fullResponse);
123
+ if (onCompletion)
124
+ yield onCompletion(aggregatedResponse);
124
125
  });
125
126
  }
126
127
  });
127
128
  }
128
129
  function trimStartOfStreamHelper() {
129
- let start = true;
130
+ let isStreamStart = true;
130
131
  return (text) => {
131
- if (start)
132
+ if (isStreamStart) {
132
133
  text = text.trimStart();
133
- if (text)
134
- start = false;
134
+ if (text)
135
+ isStreamStart = false;
136
+ }
135
137
  return text;
136
138
  };
137
139
  }
138
- function AIStream(res, customParser, callbacks) {
139
- if (!res.ok) {
140
+ function AIStream(response, customParser, callbacks) {
141
+ if (!response.ok) {
140
142
  throw new Error(
141
- `Failed to convert the response to stream. Received status code: ${res.status}.`
143
+ `Failed to convert the response to stream. Received status code: ${response.status}.`
142
144
  );
143
145
  }
144
- const stream = res.body || new ReadableStream({
146
+ const responseBodyStream = response.body || createEmptyReadableStream();
147
+ return responseBodyStream.pipeThrough(createEventStreamTransformer(customParser)).pipeThrough(createCallbacksTransformer(callbacks));
148
+ }
149
+ function createEmptyReadableStream() {
150
+ return new ReadableStream({
145
151
  start(controller) {
146
152
  controller.close();
147
153
  }
148
154
  });
149
- return stream.pipeThrough(createEventStreamTransformer(customParser)).pipeThrough(createCallbacksTransformer(callbacks));
150
155
  }
151
156
 
152
157
  // streams/openai-stream.ts
package/dist/index.mjs CHANGED
@@ -43,34 +43,34 @@ import {
43
43
  createParser
44
44
  } from "eventsource-parser";
45
45
  function createEventStreamTransformer(customParser) {
46
- const decoder = new TextDecoder();
47
- let parser;
46
+ const textDecoder = new TextDecoder();
47
+ let eventSourceParser;
48
48
  return new TransformStream({
49
49
  start(controller) {
50
50
  return __async(this, null, function* () {
51
- function onParse(event) {
52
- if (event.type === "event") {
53
- const data = event.data;
54
- if (data === "[DONE]") {
51
+ eventSourceParser = createParser(
52
+ (event) => {
53
+ if ("data" in event && event.type === "event" && event.data === "[DONE]") {
55
54
  controller.terminate();
56
55
  return;
57
56
  }
58
- const message = customParser(data);
59
- if (message)
60
- controller.enqueue(message);
57
+ if ("data" in event) {
58
+ const parsedMessage = customParser(event.data);
59
+ if (parsedMessage)
60
+ controller.enqueue(parsedMessage);
61
+ }
61
62
  }
62
- }
63
- parser = createParser(onParse);
63
+ );
64
64
  });
65
65
  },
66
66
  transform(chunk) {
67
- parser.feed(decoder.decode(chunk));
67
+ eventSourceParser.feed(textDecoder.decode(chunk));
68
68
  }
69
69
  });
70
70
  }
71
71
  function createCallbacksTransformer(callbacks) {
72
- const encoder = new TextEncoder();
73
- let fullResponse = "";
72
+ const textEncoder = new TextEncoder();
73
+ let aggregatedResponse = "";
74
74
  const { onStart, onToken, onCompletion } = callbacks || {};
75
75
  return new TransformStream({
76
76
  start() {
@@ -81,42 +81,47 @@ function createCallbacksTransformer(callbacks) {
81
81
  },
82
82
  transform(message, controller) {
83
83
  return __async(this, null, function* () {
84
- controller.enqueue(encoder.encode(message));
84
+ controller.enqueue(textEncoder.encode(message));
85
85
  if (onToken)
86
86
  yield onToken(message);
87
87
  if (onCompletion)
88
- fullResponse += message;
88
+ aggregatedResponse += message;
89
89
  });
90
90
  },
91
91
  flush() {
92
92
  return __async(this, null, function* () {
93
- yield onCompletion == null ? void 0 : onCompletion(fullResponse);
93
+ if (onCompletion)
94
+ yield onCompletion(aggregatedResponse);
94
95
  });
95
96
  }
96
97
  });
97
98
  }
98
99
  function trimStartOfStreamHelper() {
99
- let start = true;
100
+ let isStreamStart = true;
100
101
  return (text) => {
101
- if (start)
102
+ if (isStreamStart) {
102
103
  text = text.trimStart();
103
- if (text)
104
- start = false;
104
+ if (text)
105
+ isStreamStart = false;
106
+ }
105
107
  return text;
106
108
  };
107
109
  }
108
- function AIStream(res, customParser, callbacks) {
109
- if (!res.ok) {
110
+ function AIStream(response, customParser, callbacks) {
111
+ if (!response.ok) {
110
112
  throw new Error(
111
- `Failed to convert the response to stream. Received status code: ${res.status}.`
113
+ `Failed to convert the response to stream. Received status code: ${response.status}.`
112
114
  );
113
115
  }
114
- const stream = res.body || new ReadableStream({
116
+ const responseBodyStream = response.body || createEmptyReadableStream();
117
+ return responseBodyStream.pipeThrough(createEventStreamTransformer(customParser)).pipeThrough(createCallbacksTransformer(callbacks));
118
+ }
119
+ function createEmptyReadableStream() {
120
+ return new ReadableStream({
115
121
  start(controller) {
116
122
  controller.close();
117
123
  }
118
124
  });
119
- return stream.pipeThrough(createEventStreamTransformer(customParser)).pipeThrough(createCallbacksTransformer(callbacks));
120
125
  }
121
126
 
122
127
  // streams/openai-stream.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai",
3
- "version": "2.1.2",
3
+ "version": "2.1.6",
4
4
  "license": "Apache-2.0",
5
5
  "sideEffects": false,
6
6
  "main": "./dist/index.js",
@@ -61,8 +61,8 @@
61
61
  "ts-jest": "29.0.3",
62
62
  "tsup": "^6.7.0",
63
63
  "typescript": "^4.5.3",
64
- "eslint-config-vercel-ai": "0.0.0",
65
- "@vercel/ai-tsconfig": "0.0.0"
64
+ "@vercel/ai-tsconfig": "0.0.0",
65
+ "eslint-config-vercel-ai": "0.0.0"
66
66
  },
67
67
  "peerDependencies": {
68
68
  "react": "^18.0.0",
@@ -1,19 +1,19 @@
1
1
  /**
2
2
  * Shared types between the API and UI packages.
3
3
  */
4
- type Message = {
4
+ declare type Message = {
5
5
  id: string;
6
6
  createdAt?: Date;
7
7
  content: string;
8
8
  role: 'system' | 'user' | 'assistant';
9
9
  };
10
- type CreateMessage = {
10
+ declare type CreateMessage = {
11
11
  id?: string;
12
12
  createdAt?: Date;
13
13
  content: string;
14
14
  role: 'system' | 'user' | 'assistant';
15
15
  };
16
- type UseChatOptions = {
16
+ declare type UseChatOptions = {
17
17
  /**
18
18
  * The API endpoint that accepts a `{ messages: Message[] }` object and returns
19
19
  * a stream of tokens of the AI chat response. Defaults to `/api/chat`.
@@ -69,7 +69,7 @@ type UseChatOptions = {
69
69
  */
70
70
  sendExtraMessageFields?: boolean;
71
71
  };
72
- type UseCompletionOptions = {
72
+ declare type UseCompletionOptions = {
73
73
  /**
74
74
  * The API endpoint that accepts a `{ prompt: string }` object and returns
75
75
  * a stream of tokens of the AI completion response. Defaults to `/api/completion`.
@@ -120,7 +120,7 @@ type UseCompletionOptions = {
120
120
  body?: object;
121
121
  };
122
122
 
123
- type UseChatHelpers = {
123
+ declare type UseChatHelpers = {
124
124
  /** Current messages in the chat */
125
125
  messages: Message[];
126
126
  /** The error object of the API request */
@@ -151,7 +151,7 @@ type UseChatHelpers = {
151
151
  /** setState-powered method to update the input value */
152
152
  setInput: React.Dispatch<React.SetStateAction<string>>;
153
153
  /** An input/textarea-ready onChange handler to control the value of the input */
154
- handleInputChange: (e: any) => void;
154
+ handleInputChange: (e: React.ChangeEvent<HTMLInputElement> | React.ChangeEvent<HTMLTextAreaElement>) => void;
155
155
  /** Form submission handler to automattically reset input and append a user message */
156
156
  handleSubmit: (e: React.FormEvent<HTMLFormElement>) => void;
157
157
  /** Whether the API request is in progress */
@@ -159,7 +159,7 @@ type UseChatHelpers = {
159
159
  };
160
160
  declare function useChat({ api, id, initialMessages, initialInput, sendExtraMessageFields, onResponse, onFinish, onError, headers, body }?: UseChatOptions): UseChatHelpers;
161
161
 
162
- type UseCompletionHelpers = {
162
+ declare type UseCompletionHelpers = {
163
163
  /** The current completion result */
164
164
  completion: string;
165
165
  /**
@@ -187,7 +187,7 @@ type UseCompletionHelpers = {
187
187
  * <input onChange={handleInputChange} value={input} />
188
188
  * ```
189
189
  */
190
- handleInputChange: (e: any) => void;
190
+ handleInputChange: (e: React.ChangeEvent<HTMLInputElement> | React.ChangeEvent<HTMLTextAreaElement>) => void;
191
191
  /**
192
192
  * Form submission handler to automattically reset input and append a user message
193
193
  * @example
@@ -81,9 +81,13 @@ var nanoid = (0, import_nanoid.customAlphabet)(
81
81
  "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
82
82
  7
83
83
  );
84
- var decoder = new TextDecoder();
85
- function decodeAIStreamChunk(chunk) {
86
- return decoder.decode(chunk);
84
+ function createChunkDecoder() {
85
+ const decoder = new TextDecoder();
86
+ return function(chunk) {
87
+ if (!chunk)
88
+ return "";
89
+ return decoder.decode(chunk, { stream: true });
90
+ };
87
91
  }
88
92
 
89
93
  // react/use-chat.ts
@@ -162,12 +166,13 @@ function useChat({
162
166
  const createdAt = /* @__PURE__ */ new Date();
163
167
  const replyId = nanoid();
164
168
  const reader = res.body.getReader();
169
+ const decode = createChunkDecoder();
165
170
  while (true) {
166
171
  const { done, value } = yield reader.read();
167
172
  if (done) {
168
173
  break;
169
174
  }
170
- result += decodeAIStreamChunk(value);
175
+ result += decode(value);
171
176
  mutate(
172
177
  [
173
178
  ...messagesSnapshot,
@@ -250,7 +255,8 @@ function useChat({
250
255
  return;
251
256
  append({
252
257
  content: input,
253
- role: "user"
258
+ role: "user",
259
+ createdAt: /* @__PURE__ */ new Date()
254
260
  });
255
261
  setInput("");
256
262
  },
@@ -340,12 +346,13 @@ function useCompletion({
340
346
  }
341
347
  let result = "";
342
348
  const reader = res.body.getReader();
349
+ const decoder = createChunkDecoder();
343
350
  while (true) {
344
351
  const { done, value } = yield reader.read();
345
352
  if (done) {
346
353
  break;
347
354
  }
348
- result += decodeAIStreamChunk(value);
355
+ result += decoder(value);
349
356
  mutate(result, false);
350
357
  if (abortController2 === null) {
351
358
  reader.cancel();
@@ -47,9 +47,13 @@ var nanoid = customAlphabet(
47
47
  "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
48
48
  7
49
49
  );
50
- var decoder = new TextDecoder();
51
- function decodeAIStreamChunk(chunk) {
52
- return decoder.decode(chunk);
50
+ function createChunkDecoder() {
51
+ const decoder = new TextDecoder();
52
+ return function(chunk) {
53
+ if (!chunk)
54
+ return "";
55
+ return decoder.decode(chunk, { stream: true });
56
+ };
53
57
  }
54
58
 
55
59
  // react/use-chat.ts
@@ -128,12 +132,13 @@ function useChat({
128
132
  const createdAt = /* @__PURE__ */ new Date();
129
133
  const replyId = nanoid();
130
134
  const reader = res.body.getReader();
135
+ const decode = createChunkDecoder();
131
136
  while (true) {
132
137
  const { done, value } = yield reader.read();
133
138
  if (done) {
134
139
  break;
135
140
  }
136
- result += decodeAIStreamChunk(value);
141
+ result += decode(value);
137
142
  mutate(
138
143
  [
139
144
  ...messagesSnapshot,
@@ -216,7 +221,8 @@ function useChat({
216
221
  return;
217
222
  append({
218
223
  content: input,
219
- role: "user"
224
+ role: "user",
225
+ createdAt: /* @__PURE__ */ new Date()
220
226
  });
221
227
  setInput("");
222
228
  },
@@ -306,12 +312,13 @@ function useCompletion({
306
312
  }
307
313
  let result = "";
308
314
  const reader = res.body.getReader();
315
+ const decoder = createChunkDecoder();
309
316
  while (true) {
310
317
  const { done, value } = yield reader.read();
311
318
  if (done) {
312
319
  break;
313
320
  }
314
- result += decodeAIStreamChunk(value);
321
+ result += decoder(value);
315
322
  mutate(result, false);
316
323
  if (abortController2 === null) {
317
324
  reader.cancel();
@@ -3,19 +3,19 @@ import { Readable, Writable } from 'svelte/store';
3
3
  /**
4
4
  * Shared types between the API and UI packages.
5
5
  */
6
- type Message = {
6
+ declare type Message = {
7
7
  id: string;
8
8
  createdAt?: Date;
9
9
  content: string;
10
10
  role: 'system' | 'user' | 'assistant';
11
11
  };
12
- type CreateMessage = {
12
+ declare type CreateMessage = {
13
13
  id?: string;
14
14
  createdAt?: Date;
15
15
  content: string;
16
16
  role: 'system' | 'user' | 'assistant';
17
17
  };
18
- type UseChatOptions = {
18
+ declare type UseChatOptions = {
19
19
  /**
20
20
  * The API endpoint that accepts a `{ messages: Message[] }` object and returns
21
21
  * a stream of tokens of the AI chat response. Defaults to `/api/chat`.
@@ -71,7 +71,7 @@ type UseChatOptions = {
71
71
  */
72
72
  sendExtraMessageFields?: boolean;
73
73
  };
74
- type UseCompletionOptions = {
74
+ declare type UseCompletionOptions = {
75
75
  /**
76
76
  * The API endpoint that accepts a `{ prompt: string }` object and returns
77
77
  * a stream of tokens of the AI completion response. Defaults to `/api/completion`.
@@ -122,7 +122,7 @@ type UseCompletionOptions = {
122
122
  body?: object;
123
123
  };
124
124
 
125
- type UseChatHelpers = {
125
+ declare type UseChatHelpers = {
126
126
  /** Current messages in the chat */
127
127
  messages: Readable<Message[]>;
128
128
  /** The error object of the API request */
@@ -157,7 +157,7 @@ type UseChatHelpers = {
157
157
  };
158
158
  declare function useChat({ api, id, initialMessages, initialInput, sendExtraMessageFields, onResponse, onFinish, onError, headers, body }?: UseChatOptions): UseChatHelpers;
159
159
 
160
- type UseCompletionHelpers = {
160
+ declare type UseCompletionHelpers = {
161
161
  /** The current completion result */
162
162
  completion: Readable<string>;
163
163
  /** The error object of the API request */
@@ -395,7 +395,7 @@ var SWR = class {
395
395
  }
396
396
  };
397
397
 
398
- // ../../node_modules/.pnpm/sswr@1.10.0_svelte@3.59.1/node_modules/sswr/dist/sswr.mjs
398
+ // ../../node_modules/.pnpm/sswr@1.10.0_svelte@3.54.0/node_modules/sswr/dist/sswr.mjs
399
399
  var import_svelte = require("svelte");
400
400
  function h() {
401
401
  }
@@ -506,9 +506,13 @@ var nanoid = (0, import_nanoid.customAlphabet)(
506
506
  "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
507
507
  7
508
508
  );
509
- var decoder = new TextDecoder();
510
- function decodeAIStreamChunk(chunk) {
511
- return decoder.decode(chunk);
509
+ function createChunkDecoder() {
510
+ const decoder = new TextDecoder();
511
+ return function(chunk) {
512
+ if (!chunk)
513
+ return "";
514
+ return decoder.decode(chunk, { stream: true });
515
+ };
512
516
  }
513
517
 
514
518
  // svelte/use-chat.ts
@@ -582,12 +586,13 @@ function useChat({
582
586
  const createdAt = /* @__PURE__ */ new Date();
583
587
  const replyId = nanoid();
584
588
  const reader = res.body.getReader();
589
+ const decoder = createChunkDecoder();
585
590
  while (true) {
586
591
  const { done, value } = yield reader.read();
587
592
  if (done) {
588
593
  break;
589
594
  }
590
- result += decodeAIStreamChunk(value);
595
+ result += decoder(value);
591
596
  mutate([
592
597
  ...messagesSnapshot,
593
598
  {
@@ -659,7 +664,8 @@ function useChat({
659
664
  return;
660
665
  append({
661
666
  content: inputValue,
662
- role: "user"
667
+ role: "user",
668
+ createdAt: /* @__PURE__ */ new Date()
663
669
  });
664
670
  input.set("");
665
671
  };
@@ -739,12 +745,13 @@ function useCompletion({
739
745
  }
740
746
  let result = "";
741
747
  const reader = res.body.getReader();
748
+ const decoder = createChunkDecoder();
742
749
  while (true) {
743
750
  const { done, value } = yield reader.read();
744
751
  if (done) {
745
752
  break;
746
753
  }
747
- result += decodeAIStreamChunk(value);
754
+ result += decoder(value);
748
755
  mutate(result);
749
756
  if (abortController === null) {
750
757
  reader.cancel();
@@ -371,7 +371,7 @@ var SWR = class {
371
371
  }
372
372
  };
373
373
 
374
- // ../../node_modules/.pnpm/sswr@1.10.0_svelte@3.59.1/node_modules/sswr/dist/sswr.mjs
374
+ // ../../node_modules/.pnpm/sswr@1.10.0_svelte@3.54.0/node_modules/sswr/dist/sswr.mjs
375
375
  import { beforeUpdate as _, onDestroy as D } from "svelte";
376
376
  function h() {
377
377
  }
@@ -482,9 +482,13 @@ var nanoid = customAlphabet(
482
482
  "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
483
483
  7
484
484
  );
485
- var decoder = new TextDecoder();
486
- function decodeAIStreamChunk(chunk) {
487
- return decoder.decode(chunk);
485
+ function createChunkDecoder() {
486
+ const decoder = new TextDecoder();
487
+ return function(chunk) {
488
+ if (!chunk)
489
+ return "";
490
+ return decoder.decode(chunk, { stream: true });
491
+ };
488
492
  }
489
493
 
490
494
  // svelte/use-chat.ts
@@ -558,12 +562,13 @@ function useChat({
558
562
  const createdAt = /* @__PURE__ */ new Date();
559
563
  const replyId = nanoid();
560
564
  const reader = res.body.getReader();
565
+ const decoder = createChunkDecoder();
561
566
  while (true) {
562
567
  const { done, value } = yield reader.read();
563
568
  if (done) {
564
569
  break;
565
570
  }
566
- result += decodeAIStreamChunk(value);
571
+ result += decoder(value);
567
572
  mutate([
568
573
  ...messagesSnapshot,
569
574
  {
@@ -635,7 +640,8 @@ function useChat({
635
640
  return;
636
641
  append({
637
642
  content: inputValue,
638
- role: "user"
643
+ role: "user",
644
+ createdAt: /* @__PURE__ */ new Date()
639
645
  });
640
646
  input.set("");
641
647
  };
@@ -715,12 +721,13 @@ function useCompletion({
715
721
  }
716
722
  let result = "";
717
723
  const reader = res.body.getReader();
724
+ const decoder = createChunkDecoder();
718
725
  while (true) {
719
726
  const { done, value } = yield reader.read();
720
727
  if (done) {
721
728
  break;
722
729
  }
723
- result += decodeAIStreamChunk(value);
730
+ result += decoder(value);
724
731
  mutate(result);
725
732
  if (abortController === null) {
726
733
  reader.cancel();
@@ -3,19 +3,19 @@ import { Ref } from 'vue';
3
3
  /**
4
4
  * Shared types between the API and UI packages.
5
5
  */
6
- type Message = {
6
+ declare type Message = {
7
7
  id: string;
8
8
  createdAt?: Date;
9
9
  content: string;
10
10
  role: 'system' | 'user' | 'assistant';
11
11
  };
12
- type CreateMessage = {
12
+ declare type CreateMessage = {
13
13
  id?: string;
14
14
  createdAt?: Date;
15
15
  content: string;
16
16
  role: 'system' | 'user' | 'assistant';
17
17
  };
18
- type UseChatOptions = {
18
+ declare type UseChatOptions = {
19
19
  /**
20
20
  * The API endpoint that accepts a `{ messages: Message[] }` object and returns
21
21
  * a stream of tokens of the AI chat response. Defaults to `/api/chat`.
@@ -71,7 +71,7 @@ type UseChatOptions = {
71
71
  */
72
72
  sendExtraMessageFields?: boolean;
73
73
  };
74
- type UseCompletionOptions = {
74
+ declare type UseCompletionOptions = {
75
75
  /**
76
76
  * The API endpoint that accepts a `{ prompt: string }` object and returns
77
77
  * a stream of tokens of the AI completion response. Defaults to `/api/completion`.
@@ -122,7 +122,7 @@ type UseCompletionOptions = {
122
122
  body?: object;
123
123
  };
124
124
 
125
- type UseChatHelpers = {
125
+ declare type UseChatHelpers = {
126
126
  /** Current messages in the chat */
127
127
  messages: Ref<Message[]>;
128
128
  /** The error object of the API request */
@@ -157,7 +157,7 @@ type UseChatHelpers = {
157
157
  };
158
158
  declare function useChat({ api, id, initialMessages, initialInput, sendExtraMessageFields, onResponse, onFinish, onError, headers, body }?: UseChatOptions): UseChatHelpers;
159
159
 
160
- type UseCompletionHelpers = {
160
+ declare type UseCompletionHelpers = {
161
161
  /** The current completion result */
162
162
  completion: Ref<string>;
163
163
  /** The error object of the API request */
package/vue/dist/index.js CHANGED
@@ -79,9 +79,13 @@ var nanoid = (0, import_nanoid.customAlphabet)(
79
79
  "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
80
80
  7
81
81
  );
82
- var decoder = new TextDecoder();
83
- function decodeAIStreamChunk(chunk) {
84
- return decoder.decode(chunk);
82
+ function createChunkDecoder() {
83
+ const decoder = new TextDecoder();
84
+ return function(chunk) {
85
+ if (!chunk)
86
+ return "";
87
+ return decoder.decode(chunk, { stream: true });
88
+ };
85
89
  }
86
90
 
87
91
  // vue/use-chat.ts
@@ -156,12 +160,13 @@ function useChat({
156
160
  const createdAt = /* @__PURE__ */ new Date();
157
161
  const replyId = nanoid();
158
162
  const reader = res.body.getReader();
163
+ const decoder = createChunkDecoder();
159
164
  while (true) {
160
165
  const { done, value } = yield reader.read();
161
166
  if (done) {
162
167
  break;
163
168
  }
164
- result += decodeAIStreamChunk(value);
169
+ result += decoder(value);
165
170
  mutate([
166
171
  ...messagesSnapshot,
167
172
  {
@@ -315,12 +320,13 @@ function useCompletion({
315
320
  }
316
321
  let result = "";
317
322
  const reader = res.body.getReader();
323
+ const decoder = createChunkDecoder();
318
324
  while (true) {
319
325
  const { done, value } = yield reader.read();
320
326
  if (done) {
321
327
  break;
322
328
  }
323
- result += decodeAIStreamChunk(value);
329
+ result += decoder(value);
324
330
  mutate(result);
325
331
  if (abortController === null) {
326
332
  reader.cancel();
@@ -45,9 +45,13 @@ var nanoid = customAlphabet(
45
45
  "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
46
46
  7
47
47
  );
48
- var decoder = new TextDecoder();
49
- function decodeAIStreamChunk(chunk) {
50
- return decoder.decode(chunk);
48
+ function createChunkDecoder() {
49
+ const decoder = new TextDecoder();
50
+ return function(chunk) {
51
+ if (!chunk)
52
+ return "";
53
+ return decoder.decode(chunk, { stream: true });
54
+ };
51
55
  }
52
56
 
53
57
  // vue/use-chat.ts
@@ -122,12 +126,13 @@ function useChat({
122
126
  const createdAt = /* @__PURE__ */ new Date();
123
127
  const replyId = nanoid();
124
128
  const reader = res.body.getReader();
129
+ const decoder = createChunkDecoder();
125
130
  while (true) {
126
131
  const { done, value } = yield reader.read();
127
132
  if (done) {
128
133
  break;
129
134
  }
130
- result += decodeAIStreamChunk(value);
135
+ result += decoder(value);
131
136
  mutate([
132
137
  ...messagesSnapshot,
133
138
  {
@@ -281,12 +286,13 @@ function useCompletion({
281
286
  }
282
287
  let result = "";
283
288
  const reader = res.body.getReader();
289
+ const decoder = createChunkDecoder();
284
290
  while (true) {
285
291
  const { done, value } = yield reader.read();
286
292
  if (done) {
287
293
  break;
288
294
  }
289
- result += decodeAIStreamChunk(value);
295
+ result += decoder(value);
290
296
  mutate(result);
291
297
  if (abortController === null) {
292
298
  reader.cancel();