@ai-sdk/vue 2.0.0-alpha.5 → 2.0.0-alpha.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/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # @ai-sdk/vue
2
2
 
3
+ ## 2.0.0-alpha.6
4
+
5
+ ### Major Changes
6
+
7
+ - 8cbbad6: chore (ai): refactor and use chatstore in vue
8
+
9
+ ### Patch Changes
10
+
11
+ - Updated dependencies [0d2c085]
12
+ - Updated dependencies [48a7606]
13
+ - ai@5.0.0-alpha.6
14
+ - @ai-sdk/provider-utils@3.0.0-alpha.6
15
+
3
16
  ## 2.0.0-alpha.5
4
17
 
5
18
  ### Patch Changes
package/dist/index.d.mts CHANGED
@@ -1,17 +1,24 @@
1
- import { UIMessage, CreateUIMessage, ChatRequestOptions, FileUIPart, OriginalUseChatOptions, JSONValue, CompletionRequestOptions, UseCompletionOptions } from 'ai';
1
+ import { UIDataPartSchemas, UIMessage, InferUIDataParts, CreateUIMessage, ChatRequestOptions, FileUIPart, ChatStatus, UseChatOptions, CompletionRequestOptions, UseCompletionOptions } from 'ai';
2
2
  export { CreateUIMessage, UIMessage, UseChatOptions, UseCompletionOptions } from 'ai';
3
3
  import { Ref } from 'vue';
4
4
 
5
- type UseChatHelpers<MESSAGE_METADATA> = {
5
+ type UseChatHelpers<MESSAGE_METADATA = unknown, DATA_PART_SCHEMAS extends UIDataPartSchemas = UIDataPartSchemas> = {
6
+ /**
7
+ * The id of the chat.
8
+ */
9
+ readonly chatId: string;
6
10
  /** Current messages in the chat */
7
- messages: Ref<UIMessage<MESSAGE_METADATA>[]>;
11
+ readonly messages: Ref<UIMessage<MESSAGE_METADATA, InferUIDataParts<DATA_PART_SCHEMAS>>[]>;
8
12
  /** The error object of the API request */
9
- error: Ref<undefined | Error>;
13
+ readonly error: Ref<Error | undefined>;
10
14
  /**
11
15
  * Append a user message to the chat list. This triggers the API call to fetch
12
16
  * the assistant's response.
17
+ *
18
+ * @param message The message to append
19
+ * @param options Additional options to pass to the API call
13
20
  */
14
- append: (message: UIMessage<MESSAGE_METADATA> | CreateUIMessage<MESSAGE_METADATA>, chatRequestOptions?: ChatRequestOptions) => Promise<string | null | undefined>;
21
+ append: (message: CreateUIMessage<MESSAGE_METADATA, InferUIDataParts<DATA_PART_SCHEMAS>>, options?: ChatRequestOptions) => Promise<void>;
15
22
  /**
16
23
  * Reload the last AI chat response for the given chat history. If the last
17
24
  * message isn't from the assistant, it will request the API to generate a
@@ -27,7 +34,7 @@ type UseChatHelpers<MESSAGE_METADATA> = {
27
34
  * edit the messages on the client, and then trigger the `reload` method
28
35
  * manually to regenerate the AI response.
29
36
  */
30
- setMessages: (messages: UIMessage<MESSAGE_METADATA>[] | ((messages: UIMessage<MESSAGE_METADATA>[]) => UIMessage<MESSAGE_METADATA>[])) => void;
37
+ setMessages: (messages: UIMessage<MESSAGE_METADATA, InferUIDataParts<DATA_PART_SCHEMAS>>[] | ((messages: UIMessage<MESSAGE_METADATA, InferUIDataParts<DATA_PART_SCHEMAS>>[]) => UIMessage<MESSAGE_METADATA, InferUIDataParts<DATA_PART_SCHEMAS>>[])) => void;
31
38
  /** The current value of the input */
32
39
  input: Ref<string>;
33
40
  /** Form submission handler to automatically reset input and append a user message */
@@ -44,32 +51,13 @@ type UseChatHelpers<MESSAGE_METADATA> = {
44
51
  * - `ready`: The full response has been received and processed; a new user message can be submitted.
45
52
  * - `error`: An error occurred during the API request, preventing successful completion.
46
53
  */
47
- status: Ref<'submitted' | 'streaming' | 'ready' | 'error'>;
54
+ status: Ref<ChatStatus>;
48
55
  addToolResult: ({ toolCallId, result, }: {
49
56
  toolCallId: string;
50
57
  result: any;
51
58
  }) => void;
52
- /** The id of the chat */
53
- chatId: string;
54
59
  };
55
- declare function useChat<MESSAGE_METADATA = unknown>({ api, chatId, initialMessages, initialInput, streamProtocol, onFinish, onError, credentials, headers: metadataHeaders, body: metadataBody, generateId, onToolCall, fetch, maxSteps, experimental_prepareRequestBody, messageMetadataSchema, }?: OriginalUseChatOptions<MESSAGE_METADATA> & {
56
- /**
57
- * Experimental (Vue only). When a function is provided, it will be used
58
- * to prepare the request body for the chat API. This can be useful for
59
- * customizing the request body based on the messages and data in the chat.
60
- *
61
- * @param id The chat ID
62
- * @param messages The current messages in the chat
63
- * @param requestData The data object passed in the chat request
64
- * @param requestBody The request body object passed in the chat request
65
- */
66
- experimental_prepareRequestBody?: (options: {
67
- chatId: string;
68
- messages: UIMessage<MESSAGE_METADATA>[];
69
- requestData?: JSONValue;
70
- requestBody?: object;
71
- }) => unknown;
72
- }): UseChatHelpers<MESSAGE_METADATA>;
60
+ declare function useChat<MESSAGE_METADATA = unknown, DATA_PART_SCHEMAS extends UIDataPartSchemas = UIDataPartSchemas>({ chatId, initialInput, onFinish, onError, generateId, onToolCall, chatStore: chatStoreArg, }?: UseChatOptions<MESSAGE_METADATA, DATA_PART_SCHEMAS>): UseChatHelpers<MESSAGE_METADATA, DATA_PART_SCHEMAS>;
73
61
 
74
62
  type UseCompletionHelpers = {
75
63
  /** The current completion result */
package/dist/index.d.ts CHANGED
@@ -1,17 +1,24 @@
1
- import { UIMessage, CreateUIMessage, ChatRequestOptions, FileUIPart, OriginalUseChatOptions, JSONValue, CompletionRequestOptions, UseCompletionOptions } from 'ai';
1
+ import { UIDataPartSchemas, UIMessage, InferUIDataParts, CreateUIMessage, ChatRequestOptions, FileUIPart, ChatStatus, UseChatOptions, CompletionRequestOptions, UseCompletionOptions } from 'ai';
2
2
  export { CreateUIMessage, UIMessage, UseChatOptions, UseCompletionOptions } from 'ai';
3
3
  import { Ref } from 'vue';
4
4
 
5
- type UseChatHelpers<MESSAGE_METADATA> = {
5
+ type UseChatHelpers<MESSAGE_METADATA = unknown, DATA_PART_SCHEMAS extends UIDataPartSchemas = UIDataPartSchemas> = {
6
+ /**
7
+ * The id of the chat.
8
+ */
9
+ readonly chatId: string;
6
10
  /** Current messages in the chat */
7
- messages: Ref<UIMessage<MESSAGE_METADATA>[]>;
11
+ readonly messages: Ref<UIMessage<MESSAGE_METADATA, InferUIDataParts<DATA_PART_SCHEMAS>>[]>;
8
12
  /** The error object of the API request */
9
- error: Ref<undefined | Error>;
13
+ readonly error: Ref<Error | undefined>;
10
14
  /**
11
15
  * Append a user message to the chat list. This triggers the API call to fetch
12
16
  * the assistant's response.
17
+ *
18
+ * @param message The message to append
19
+ * @param options Additional options to pass to the API call
13
20
  */
14
- append: (message: UIMessage<MESSAGE_METADATA> | CreateUIMessage<MESSAGE_METADATA>, chatRequestOptions?: ChatRequestOptions) => Promise<string | null | undefined>;
21
+ append: (message: CreateUIMessage<MESSAGE_METADATA, InferUIDataParts<DATA_PART_SCHEMAS>>, options?: ChatRequestOptions) => Promise<void>;
15
22
  /**
16
23
  * Reload the last AI chat response for the given chat history. If the last
17
24
  * message isn't from the assistant, it will request the API to generate a
@@ -27,7 +34,7 @@ type UseChatHelpers<MESSAGE_METADATA> = {
27
34
  * edit the messages on the client, and then trigger the `reload` method
28
35
  * manually to regenerate the AI response.
29
36
  */
30
- setMessages: (messages: UIMessage<MESSAGE_METADATA>[] | ((messages: UIMessage<MESSAGE_METADATA>[]) => UIMessage<MESSAGE_METADATA>[])) => void;
37
+ setMessages: (messages: UIMessage<MESSAGE_METADATA, InferUIDataParts<DATA_PART_SCHEMAS>>[] | ((messages: UIMessage<MESSAGE_METADATA, InferUIDataParts<DATA_PART_SCHEMAS>>[]) => UIMessage<MESSAGE_METADATA, InferUIDataParts<DATA_PART_SCHEMAS>>[])) => void;
31
38
  /** The current value of the input */
32
39
  input: Ref<string>;
33
40
  /** Form submission handler to automatically reset input and append a user message */
@@ -44,32 +51,13 @@ type UseChatHelpers<MESSAGE_METADATA> = {
44
51
  * - `ready`: The full response has been received and processed; a new user message can be submitted.
45
52
  * - `error`: An error occurred during the API request, preventing successful completion.
46
53
  */
47
- status: Ref<'submitted' | 'streaming' | 'ready' | 'error'>;
54
+ status: Ref<ChatStatus>;
48
55
  addToolResult: ({ toolCallId, result, }: {
49
56
  toolCallId: string;
50
57
  result: any;
51
58
  }) => void;
52
- /** The id of the chat */
53
- chatId: string;
54
59
  };
55
- declare function useChat<MESSAGE_METADATA = unknown>({ api, chatId, initialMessages, initialInput, streamProtocol, onFinish, onError, credentials, headers: metadataHeaders, body: metadataBody, generateId, onToolCall, fetch, maxSteps, experimental_prepareRequestBody, messageMetadataSchema, }?: OriginalUseChatOptions<MESSAGE_METADATA> & {
56
- /**
57
- * Experimental (Vue only). When a function is provided, it will be used
58
- * to prepare the request body for the chat API. This can be useful for
59
- * customizing the request body based on the messages and data in the chat.
60
- *
61
- * @param id The chat ID
62
- * @param messages The current messages in the chat
63
- * @param requestData The data object passed in the chat request
64
- * @param requestBody The request body object passed in the chat request
65
- */
66
- experimental_prepareRequestBody?: (options: {
67
- chatId: string;
68
- messages: UIMessage<MESSAGE_METADATA>[];
69
- requestData?: JSONValue;
70
- requestBody?: object;
71
- }) => unknown;
72
- }): UseChatHelpers<MESSAGE_METADATA>;
60
+ declare function useChat<MESSAGE_METADATA = unknown, DATA_PART_SCHEMAS extends UIDataPartSchemas = UIDataPartSchemas>({ chatId, initialInput, onFinish, onError, generateId, onToolCall, chatStore: chatStoreArg, }?: UseChatOptions<MESSAGE_METADATA, DATA_PART_SCHEMAS>): UseChatHelpers<MESSAGE_METADATA, DATA_PART_SCHEMAS>;
73
61
 
74
62
  type UseCompletionHelpers = {
75
63
  /** The current completion result */
package/dist/index.js CHANGED
@@ -36,190 +36,132 @@ __export(src_exports, {
36
36
  module.exports = __toCommonJS(src_exports);
37
37
 
38
38
  // src/use-chat.ts
39
+ var import_ai2 = require("ai");
40
+ var import_vue2 = require("vue");
41
+
42
+ // src/chat-store.ts
39
43
  var import_ai = require("ai");
40
- var import_swrv = __toESM(require("swrv"));
41
44
  var import_vue = require("vue");
42
- var useSWRV = import_swrv.default.default || import_swrv.default;
43
- var store = {};
44
- var statusStore = {};
45
+ var VueChat = class {
46
+ constructor(messages) {
47
+ this.statusRef = (0, import_vue.ref)("ready");
48
+ this.errorRef = (0, import_vue.ref)(void 0);
49
+ this.activeResponse = void 0;
50
+ this.jobExecutor = new import_ai.SerialJobExecutor();
51
+ this.setStatus = (status) => {
52
+ this.statusRef.value = status;
53
+ };
54
+ this.setError = (error) => {
55
+ this.errorRef.value = error;
56
+ };
57
+ this.setActiveResponse = (activeResponse) => {
58
+ this.activeResponse = activeResponse;
59
+ };
60
+ this.setMessages = (messages) => {
61
+ this.messagesRef.value = messages;
62
+ };
63
+ this.pushMessage = (message) => {
64
+ this.messagesRef.value.push(message);
65
+ };
66
+ this.popMessage = () => {
67
+ this.messagesRef.value.pop();
68
+ };
69
+ this.replaceMessage = (index, message) => {
70
+ this.messagesRef.value[index] = { ...message };
71
+ };
72
+ this.messagesRef = (0, import_vue.ref)(messages != null ? messages : []);
73
+ }
74
+ get messages() {
75
+ return this.messagesRef.value;
76
+ }
77
+ get status() {
78
+ return this.statusRef.value;
79
+ }
80
+ get error() {
81
+ return this.errorRef.value;
82
+ }
83
+ };
84
+ function createChatStore(options) {
85
+ return new import_ai.ChatStore({
86
+ ...options,
87
+ createChat: (options2) => new VueChat(
88
+ options2.messages
89
+ )
90
+ });
91
+ }
92
+
93
+ // src/use-chat.ts
45
94
  function useChat({
46
- api = "/api/chat",
47
95
  chatId,
48
- initialMessages = [],
49
96
  initialInput = "",
50
- streamProtocol = "ui-message",
51
97
  onFinish,
52
98
  onError,
53
- credentials,
54
- headers: metadataHeaders,
55
- body: metadataBody,
56
- generateId = import_ai.generateId,
99
+ generateId = import_ai2.generateId,
57
100
  onToolCall,
58
- fetch,
59
- maxSteps = 1,
60
- experimental_prepareRequestBody,
61
- messageMetadataSchema
62
- } = {
63
- maxSteps: 1
64
- }) {
65
- var _a, _b;
101
+ chatStore: chatStoreArg
102
+ } = {}) {
66
103
  const stableChatId = chatId != null ? chatId : generateId();
67
- const key = `${api}|${stableChatId}`;
68
- const { data: messagesData, mutate: originalMutate } = useSWRV(key, () => {
69
- var _a2;
70
- return (_a2 = store[key]) != null ? _a2 : initialMessages;
104
+ const chatStore = chatStoreArg == null ? createChatStore(
105
+ (0, import_ai2.defaultChatStoreOptions)({
106
+ api: "/api/chat",
107
+ generateId
108
+ })()
109
+ ) : typeof chatStoreArg === "function" ? createChatStore(chatStoreArg()) : chatStoreArg;
110
+ const messages = (0, import_vue2.computed)(() => chatStore.getMessages(stableChatId));
111
+ const status = (0, import_vue2.computed)(() => chatStore.getStatus(stableChatId));
112
+ const error = (0, import_vue2.computed)(() => chatStore.getError(stableChatId));
113
+ const append = async (message, { headers, body } = {}) => chatStore.submitMessage({
114
+ chatId: stableChatId,
115
+ message,
116
+ headers,
117
+ body,
118
+ onError,
119
+ onToolCall,
120
+ onFinish
121
+ });
122
+ const reload = async ({ headers, body } = {}) => chatStore.resubmitLastUserMessage({
123
+ chatId: stableChatId,
124
+ headers,
125
+ body,
126
+ onError,
127
+ onToolCall,
128
+ onFinish
71
129
  });
72
- const status = (_a = statusStore[stableChatId]) != null ? _a : statusStore[stableChatId] = (0, import_vue.ref)("ready");
73
- (_b = messagesData.value) != null ? _b : messagesData.value = initialMessages;
74
- const mutate = (data) => {
75
- store[key] = data;
76
- return originalMutate();
77
- };
78
- const messages = messagesData;
79
- const error = (0, import_vue.ref)(void 0);
80
- let abortController = null;
81
- async function triggerRequest(messagesSnapshot, { headers, body } = {}) {
82
- var _a2;
83
- error.value = void 0;
84
- status.value = "submitted";
85
- const messageCount = messages.value.length;
86
- const lastMessage = messages.value.at(-1);
87
- const maxStep = lastMessage != null ? (0, import_ai.extractMaxToolInvocationStep)((0, import_ai.getToolInvocations)(lastMessage)) : 0;
88
- try {
89
- abortController = new AbortController();
90
- mutate(messagesSnapshot);
91
- await (0, import_ai.callChatApi)({
92
- api,
93
- body: (_a2 = experimental_prepareRequestBody == null ? void 0 : experimental_prepareRequestBody({
94
- chatId: stableChatId,
95
- messages: messagesSnapshot,
96
- requestBody: body
97
- })) != null ? _a2 : {
98
- chatId: stableChatId,
99
- messages: messagesSnapshot,
100
- ...(0, import_vue.unref)(metadataBody),
101
- // Use unref to unwrap the ref value
102
- ...body
103
- },
104
- streamProtocol,
105
- headers: {
106
- ...metadataHeaders,
107
- ...headers
108
- },
109
- abortController: () => abortController,
110
- credentials,
111
- onUpdate({ message }) {
112
- status.value = "streaming";
113
- const replaceLastMessage = message.id === messagesSnapshot[messagesSnapshot.length - 1].id;
114
- mutate([
115
- ...replaceLastMessage ? messagesSnapshot.slice(0, messagesSnapshot.length - 1) : messagesSnapshot,
116
- message
117
- ]);
118
- },
119
- onFinish,
120
- generateId,
121
- onToolCall,
122
- fetch,
123
- // enabled use of structured clone in processChatResponse:
124
- lastMessage: recursiveToRaw(
125
- messagesSnapshot[messagesSnapshot.length - 1]
126
- ),
127
- messageMetadataSchema
128
- });
129
- status.value = "ready";
130
- } catch (err) {
131
- if (err.name === "AbortError") {
132
- abortController = null;
133
- status.value = "ready";
134
- return null;
135
- }
136
- if (onError && err instanceof Error) {
137
- onError(err);
138
- }
139
- error.value = err;
140
- status.value = "error";
141
- } finally {
142
- abortController = null;
143
- }
144
- if ((0, import_ai.shouldResubmitMessages)({
145
- originalMaxToolInvocationStep: maxStep,
146
- originalMessageCount: messageCount,
147
- maxSteps,
148
- messages: messages.value
149
- })) {
150
- await triggerRequest(messages.value);
151
- }
152
- }
153
- const append = async (message, options) => {
154
- var _a2;
155
- return triggerRequest(
156
- messages.value.concat({
157
- ...message,
158
- id: (_a2 = message.id) != null ? _a2 : generateId(),
159
- parts: message.parts
160
- }),
161
- options
162
- );
163
- };
164
- const reload = async (options) => {
165
- const messagesSnapshot = messages.value;
166
- if (messagesSnapshot.length === 0)
167
- return null;
168
- const lastMessage = messagesSnapshot[messagesSnapshot.length - 1];
169
- if (lastMessage.role === "assistant") {
170
- return triggerRequest(messagesSnapshot.slice(0, -1), options);
171
- }
172
- return triggerRequest(messagesSnapshot, options);
173
- };
174
130
  const stop = () => {
175
- if (abortController) {
176
- abortController.abort();
177
- abortController = null;
178
- }
131
+ chatStore.stopStream({ chatId: stableChatId });
179
132
  };
180
- const setMessages = (messagesArg) => {
181
- if (typeof messagesArg === "function") {
182
- messagesArg = messagesArg(messages.value);
133
+ const setMessages = (messagesParam) => {
134
+ if (typeof messagesParam === "function") {
135
+ messagesParam = messagesParam(chatStore.getMessages(stableChatId));
183
136
  }
184
- mutate(messagesArg);
137
+ chatStore.setMessages({
138
+ id: stableChatId,
139
+ messages: messagesParam
140
+ });
185
141
  };
186
- const input = (0, import_vue.ref)(initialInput);
142
+ const input = (0, import_vue2.ref)(initialInput);
187
143
  const handleSubmit = async (event, options = {}) => {
188
- var _a2;
189
- (_a2 = event == null ? void 0 : event.preventDefault) == null ? void 0 : _a2.call(event);
144
+ var _a;
145
+ (_a = event == null ? void 0 : event.preventDefault) == null ? void 0 : _a.call(event);
190
146
  const inputValue = input.value;
191
- const fileParts = Array.isArray(options == null ? void 0 : options.files) ? options.files : await (0, import_ai.convertFileListToFileUIParts)(options == null ? void 0 : options.files);
147
+ const fileParts = Array.isArray(options == null ? void 0 : options.files) ? options.files : await (0, import_ai2.convertFileListToFileUIParts)(options == null ? void 0 : options.files);
192
148
  if (!inputValue && fileParts.length === 0)
193
149
  return;
194
- triggerRequest(
195
- messages.value.concat({
150
+ await append(
151
+ {
196
152
  id: generateId(),
197
153
  role: "user",
154
+ metadata: void 0,
198
155
  parts: [...fileParts, { type: "text", text: inputValue }]
199
- }),
200
- options
156
+ },
157
+ {
158
+ headers: options.headers,
159
+ body: options.body
160
+ }
201
161
  );
202
162
  input.value = "";
203
163
  };
204
- const addToolResult = ({
205
- toolCallId,
206
- result
207
- }) => {
208
- const currentMessages = messages.value;
209
- (0, import_ai.updateToolCallResult)({
210
- messages: currentMessages,
211
- toolCallId,
212
- toolResult: result
213
- });
214
- mutate(currentMessages);
215
- if (status.value === "submitted" || status.value === "streaming") {
216
- return;
217
- }
218
- const lastMessage = currentMessages[currentMessages.length - 1];
219
- if ((0, import_ai.isAssistantMessageWithCompletedToolCalls)(lastMessage)) {
220
- triggerRequest(currentMessages);
221
- }
222
- };
164
+ const addToolResult = (options) => chatStore.addToolResult({ chatId: stableChatId, ...options });
223
165
  return {
224
166
  chatId: stableChatId,
225
167
  messages,
@@ -234,27 +176,14 @@ function useChat({
234
176
  addToolResult
235
177
  };
236
178
  }
237
- function recursiveToRaw(inputValue) {
238
- if (Array.isArray(inputValue)) {
239
- return [...inputValue.map(recursiveToRaw)];
240
- } else if (typeof inputValue === "object" && inputValue !== null) {
241
- const clone = {};
242
- for (const [key, value] of Object.entries(inputValue)) {
243
- clone[key] = recursiveToRaw(value);
244
- }
245
- return clone;
246
- } else {
247
- return inputValue;
248
- }
249
- }
250
179
 
251
180
  // src/use-completion.ts
252
- var import_ai2 = require("ai");
253
- var import_swrv2 = __toESM(require("swrv"));
254
- var import_vue2 = require("vue");
181
+ var import_ai3 = require("ai");
182
+ var import_swrv = __toESM(require("swrv"));
183
+ var import_vue3 = require("vue");
255
184
  var uniqueId = 0;
256
- var useSWRV2 = import_swrv2.default.default || import_swrv2.default;
257
- var store2 = {};
185
+ var useSWRV = import_swrv.default.default || import_swrv.default;
186
+ var store = {};
258
187
  function useCompletion({
259
188
  api = "/api/completion",
260
189
  id,
@@ -271,25 +200,25 @@ function useCompletion({
271
200
  var _a;
272
201
  const completionId = id || `completion-${uniqueId++}`;
273
202
  const key = `${api}|${completionId}`;
274
- const { data, mutate: originalMutate } = useSWRV2(
203
+ const { data, mutate: originalMutate } = useSWRV(
275
204
  key,
276
- () => store2[key] || initialCompletion
205
+ () => store[key] || initialCompletion
277
206
  );
278
- const { data: isLoading, mutate: mutateLoading } = useSWRV2(
207
+ const { data: isLoading, mutate: mutateLoading } = useSWRV(
279
208
  `${completionId}-loading`,
280
209
  null
281
210
  );
282
211
  (_a = isLoading.value) != null ? _a : isLoading.value = false;
283
212
  data.value || (data.value = initialCompletion);
284
213
  const mutate = (data2) => {
285
- store2[key] = data2;
214
+ store[key] = data2;
286
215
  return originalMutate();
287
216
  };
288
217
  const completion = data;
289
- const error = (0, import_vue2.ref)(void 0);
218
+ const error = (0, import_vue3.ref)(void 0);
290
219
  let abortController = null;
291
220
  async function triggerRequest(prompt, options) {
292
- return (0, import_ai2.callCompletionApi)({
221
+ return (0, import_ai3.callCompletionApi)({
293
222
  api,
294
223
  prompt,
295
224
  credentials,
@@ -298,7 +227,7 @@ function useCompletion({
298
227
  ...options == null ? void 0 : options.headers
299
228
  },
300
229
  body: {
301
- ...(0, import_vue2.unref)(body),
230
+ ...(0, import_vue3.unref)(body),
302
231
  ...options == null ? void 0 : options.body
303
232
  },
304
233
  streamProtocol,
@@ -327,7 +256,7 @@ function useCompletion({
327
256
  const setCompletion = (completion2) => {
328
257
  mutate(completion2);
329
258
  };
330
- const input = (0, import_vue2.ref)(initialInput);
259
+ const input = (0, import_vue3.ref)(initialInput);
331
260
  const handleSubmit = (event) => {
332
261
  var _a2;
333
262
  (_a2 = event == null ? void 0 : event.preventDefault) == null ? void 0 : _a2.call(event);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/use-chat.ts","../src/use-completion.ts"],"sourcesContent":["export * from './use-chat';\nexport * from './use-completion';\n","import type {\n ChatRequestOptions,\n CreateUIMessage,\n FileUIPart,\n JSONValue,\n OriginalUseChatOptions,\n UIMessage,\n UseChatOptions,\n} from 'ai';\nimport {\n callChatApi,\n convertFileListToFileUIParts,\n extractMaxToolInvocationStep,\n generateId as generateIdFunc,\n getToolInvocations,\n isAssistantMessageWithCompletedToolCalls,\n shouldResubmitMessages,\n updateToolCallResult,\n} from 'ai';\nimport swrv from 'swrv';\nimport type { Ref } from 'vue';\nimport { ref, unref } from 'vue';\n\nexport type { CreateUIMessage, UIMessage, UseChatOptions };\n\nexport type UseChatHelpers<MESSAGE_METADATA> = {\n /** Current messages in the chat */\n messages: Ref<UIMessage<MESSAGE_METADATA>[]>;\n /** The error object of the API request */\n error: Ref<undefined | Error>;\n /**\n * Append a user message to the chat list. This triggers the API call to fetch\n * the assistant's response.\n */\n append: (\n message: UIMessage<MESSAGE_METADATA> | CreateUIMessage<MESSAGE_METADATA>,\n chatRequestOptions?: ChatRequestOptions,\n ) => Promise<string | null | undefined>;\n /**\n * Reload the last AI chat response for the given chat history. If the last\n * message isn't from the assistant, it will request the API to generate a\n * new response.\n */\n reload: (\n chatRequestOptions?: ChatRequestOptions,\n ) => Promise<string | null | undefined>;\n /**\n * Abort the current request immediately, keep the generated tokens if any.\n */\n stop: () => void;\n /**\n * Update the `messages` state locally. This is useful when you want to\n * edit the messages on the client, and then trigger the `reload` method\n * manually to regenerate the AI response.\n */\n setMessages: (\n messages:\n | UIMessage<MESSAGE_METADATA>[]\n | ((\n messages: UIMessage<MESSAGE_METADATA>[],\n ) => UIMessage<MESSAGE_METADATA>[]),\n ) => void;\n /** The current value of the input */\n input: Ref<string>;\n /** Form submission handler to automatically reset input and append a user message */\n handleSubmit: (\n event?: { preventDefault?: () => void },\n chatRequestOptions?: ChatRequestOptions & {\n files?: FileList | FileUIPart[];\n },\n ) => void;\n\n /**\n * Hook status:\n *\n * - `submitted`: The message has been sent to the API and we're awaiting the start of the response stream.\n * - `streaming`: The response is actively streaming in from the API, receiving chunks of data.\n * - `ready`: The full response has been received and processed; a new user message can be submitted.\n * - `error`: An error occurred during the API request, preventing successful completion.\n */\n status: Ref<'submitted' | 'streaming' | 'ready' | 'error'>;\n\n addToolResult: ({\n toolCallId,\n result,\n }: {\n toolCallId: string;\n result: any;\n }) => void;\n\n /** The id of the chat */\n chatId: string;\n};\n\n// @ts-expect-error - some issues with the default export of useSWRV\nconst useSWRV = (swrv.default as (typeof import('swrv'))['default']) || swrv;\nconst store: Record<string, UIMessage<any>[] | undefined> = {};\nconst statusStore: Record<\n string,\n Ref<'submitted' | 'streaming' | 'ready' | 'error'>\n> = {};\n\nexport function useChat<MESSAGE_METADATA = unknown>(\n {\n api = '/api/chat',\n chatId,\n initialMessages = [],\n initialInput = '',\n streamProtocol = 'ui-message',\n onFinish,\n onError,\n credentials,\n headers: metadataHeaders,\n body: metadataBody,\n generateId = generateIdFunc,\n onToolCall,\n fetch,\n maxSteps = 1,\n experimental_prepareRequestBody,\n messageMetadataSchema,\n }: OriginalUseChatOptions<MESSAGE_METADATA> & {\n /**\n * Experimental (Vue only). When a function is provided, it will be used\n * to prepare the request body for the chat API. This can be useful for\n * customizing the request body based on the messages and data in the chat.\n *\n * @param id The chat ID\n * @param messages The current messages in the chat\n * @param requestData The data object passed in the chat request\n * @param requestBody The request body object passed in the chat request\n */\n experimental_prepareRequestBody?: (options: {\n chatId: string;\n messages: UIMessage<MESSAGE_METADATA>[];\n requestData?: JSONValue;\n requestBody?: object;\n }) => unknown;\n } = {\n maxSteps: 1,\n },\n): UseChatHelpers<MESSAGE_METADATA> {\n // Generate a unique ID for the chat if not provided.\n const stableChatId = chatId ?? generateId();\n\n const key = `${api}|${stableChatId}`;\n const { data: messagesData, mutate: originalMutate } = useSWRV<\n UIMessage<MESSAGE_METADATA>[]\n >(key, () => store[key] ?? initialMessages);\n\n const status =\n statusStore[stableChatId] ??\n (statusStore[stableChatId] = ref<\n 'submitted' | 'streaming' | 'ready' | 'error'\n >('ready'));\n\n // Force the `data` to be `initialMessages` if it's `undefined`.\n messagesData.value ??= initialMessages;\n\n const mutate = (data?: UIMessage<MESSAGE_METADATA>[]) => {\n store[key] = data;\n return originalMutate();\n };\n\n // Because of the `initialData` option, the `data` will never be `undefined`.\n const messages = messagesData as Ref<UIMessage<MESSAGE_METADATA>[]>;\n\n const error = ref<undefined | Error>(undefined);\n\n let abortController: AbortController | null = null;\n\n async function triggerRequest(\n messagesSnapshot: UIMessage<MESSAGE_METADATA>[],\n { headers, body }: ChatRequestOptions = {},\n ) {\n error.value = undefined;\n status.value = 'submitted';\n\n const messageCount = messages.value.length;\n const lastMessage = messages.value.at(-1);\n const maxStep =\n lastMessage != null\n ? extractMaxToolInvocationStep(getToolInvocations(lastMessage))\n : 0;\n\n try {\n abortController = new AbortController();\n\n // Do an optimistic update to show the updated messages immediately:\n mutate(messagesSnapshot);\n\n await callChatApi({\n api,\n body: experimental_prepareRequestBody?.({\n chatId: stableChatId,\n messages: messagesSnapshot,\n requestBody: body,\n }) ?? {\n chatId: stableChatId,\n messages: messagesSnapshot,\n ...unref(metadataBody), // Use unref to unwrap the ref value\n ...body,\n },\n streamProtocol,\n headers: {\n ...metadataHeaders,\n ...headers,\n },\n abortController: () => abortController,\n credentials,\n onUpdate({ message }) {\n status.value = 'streaming';\n\n const replaceLastMessage =\n message.id === messagesSnapshot[messagesSnapshot.length - 1].id;\n\n mutate([\n ...(replaceLastMessage\n ? messagesSnapshot.slice(0, messagesSnapshot.length - 1)\n : messagesSnapshot),\n message,\n ]);\n },\n onFinish,\n generateId,\n onToolCall,\n fetch,\n // enabled use of structured clone in processChatResponse:\n lastMessage: recursiveToRaw(\n messagesSnapshot[messagesSnapshot.length - 1],\n ),\n messageMetadataSchema,\n });\n\n status.value = 'ready';\n } catch (err) {\n // Ignore abort errors as they are expected.\n if ((err as any).name === 'AbortError') {\n abortController = null;\n status.value = 'ready';\n return null;\n }\n\n if (onError && err instanceof Error) {\n onError(err);\n }\n\n error.value = err as Error;\n status.value = 'error';\n } finally {\n abortController = null;\n }\n\n // auto-submit when all tool calls in the last assistant message have results:\n if (\n shouldResubmitMessages({\n originalMaxToolInvocationStep: maxStep,\n originalMessageCount: messageCount,\n maxSteps,\n messages: messages.value,\n })\n ) {\n await triggerRequest(messages.value);\n }\n }\n\n const append: UseChatHelpers<MESSAGE_METADATA>['append'] = async (\n message,\n options,\n ) => {\n return triggerRequest(\n messages.value.concat({\n ...message,\n id: message.id ?? generateId(),\n parts: message.parts,\n }),\n options,\n );\n };\n\n const reload: UseChatHelpers<MESSAGE_METADATA>['reload'] = async options => {\n const messagesSnapshot = messages.value;\n if (messagesSnapshot.length === 0) return null;\n\n const lastMessage = messagesSnapshot[messagesSnapshot.length - 1];\n if (lastMessage.role === 'assistant') {\n return triggerRequest(messagesSnapshot.slice(0, -1), options);\n }\n\n return triggerRequest(messagesSnapshot, options);\n };\n\n const stop = () => {\n if (abortController) {\n abortController.abort();\n abortController = null;\n }\n };\n\n const setMessages = (\n messagesArg:\n | UIMessage<MESSAGE_METADATA>[]\n | ((\n messages: UIMessage<MESSAGE_METADATA>[],\n ) => UIMessage<MESSAGE_METADATA>[]),\n ) => {\n if (typeof messagesArg === 'function') {\n messagesArg = messagesArg(messages.value);\n }\n\n mutate(messagesArg);\n };\n\n const input = ref(initialInput);\n\n const handleSubmit = async (\n event?: { preventDefault?: () => void },\n options: ChatRequestOptions & { files?: FileList | FileUIPart[] } = {},\n ) => {\n event?.preventDefault?.();\n\n const inputValue = input.value;\n\n const fileParts = Array.isArray(options?.files)\n ? options.files\n : await convertFileListToFileUIParts(options?.files);\n\n if (!inputValue && fileParts.length === 0) return;\n\n triggerRequest(\n messages.value.concat({\n id: generateId(),\n role: 'user',\n parts: [...fileParts, { type: 'text', text: inputValue }],\n }),\n options,\n );\n\n input.value = '';\n };\n\n const addToolResult = ({\n toolCallId,\n result,\n }: {\n toolCallId: string;\n result: unknown;\n }) => {\n const currentMessages = messages.value;\n\n updateToolCallResult({\n messages: currentMessages,\n toolCallId,\n toolResult: result,\n });\n\n mutate(currentMessages);\n\n // when the request is ongoing, the auto-submit will be triggered after the request is finished\n if (status.value === 'submitted' || status.value === 'streaming') {\n return;\n }\n\n // auto-submit when all tool calls in the last assistant message have results:\n const lastMessage = currentMessages[currentMessages.length - 1];\n if (isAssistantMessageWithCompletedToolCalls(lastMessage)) {\n triggerRequest(currentMessages);\n }\n };\n\n return {\n chatId: stableChatId,\n messages,\n append,\n error,\n reload,\n stop,\n setMessages,\n input,\n handleSubmit,\n status: status as Ref<'submitted' | 'streaming' | 'ready' | 'error'>,\n addToolResult,\n };\n}\n\n// required for use of structured clone\nfunction recursiveToRaw<T>(inputValue: T): T {\n if (Array.isArray(inputValue)) {\n return [...inputValue.map(recursiveToRaw)] as T;\n } else if (typeof inputValue === 'object' && inputValue !== null) {\n const clone: any = {};\n for (const [key, value] of Object.entries(inputValue)) {\n clone[key] = recursiveToRaw(value);\n }\n return clone;\n } else {\n return inputValue;\n }\n}\n","import type { CompletionRequestOptions, UseCompletionOptions } from 'ai';\nimport { callCompletionApi } from 'ai';\nimport swrv from 'swrv';\nimport type { Ref } from 'vue';\nimport { ref, unref } from 'vue';\n\nexport type { UseCompletionOptions };\n\nexport type UseCompletionHelpers = {\n /** The current completion result */\n completion: Ref<string>;\n /** The error object of the API request */\n error: Ref<undefined | Error>;\n /**\n * Send a new prompt to the API endpoint and update the completion state.\n */\n complete: (\n prompt: string,\n options?: CompletionRequestOptions,\n ) => Promise<string | null | undefined>;\n /**\n * Abort the current API request but keep the generated tokens.\n */\n stop: () => void;\n /**\n * Update the `completion` state locally.\n */\n setCompletion: (completion: string) => void;\n /** The current value of the input */\n input: Ref<string>;\n /**\n * Form submission handler to automatically reset input and append a user message\n * @example\n * ```jsx\n * <form @submit=\"handleSubmit\">\n * <input @change=\"handleInputChange\" v-model=\"input\" />\n * </form>\n * ```\n */\n handleSubmit: (event?: { preventDefault?: () => void }) => void;\n /** Whether the API request is in progress */\n isLoading: Ref<boolean | undefined>;\n};\n\nlet uniqueId = 0;\n\n// @ts-expect-error - some issues with the default export of useSWRV\nconst useSWRV = (swrv.default as (typeof import('swrv'))['default']) || swrv;\nconst store: Record<string, any> = {};\n\nexport function useCompletion({\n api = '/api/completion',\n id,\n initialCompletion = '',\n initialInput = '',\n credentials,\n headers,\n body,\n streamProtocol,\n onFinish,\n onError,\n fetch,\n}: UseCompletionOptions = {}): UseCompletionHelpers {\n // Generate an unique id for the completion if not provided.\n const completionId = id || `completion-${uniqueId++}`;\n\n const key = `${api}|${completionId}`;\n const { data, mutate: originalMutate } = useSWRV<string>(\n key,\n () => store[key] || initialCompletion,\n );\n\n const { data: isLoading, mutate: mutateLoading } = useSWRV<boolean>(\n `${completionId}-loading`,\n null,\n );\n\n isLoading.value ??= false;\n\n // Force the `data` to be `initialCompletion` if it's `undefined`.\n data.value ||= initialCompletion;\n\n const mutate = (data: string) => {\n store[key] = data;\n return originalMutate();\n };\n\n // Because of the `initialData` option, the `data` will never be `undefined`.\n const completion = data as Ref<string>;\n\n const error = ref<undefined | Error>(undefined);\n\n let abortController: AbortController | null = null;\n\n async function triggerRequest(\n prompt: string,\n options?: CompletionRequestOptions,\n ) {\n return callCompletionApi({\n api,\n prompt,\n credentials,\n headers: {\n ...headers,\n ...options?.headers,\n },\n body: {\n ...unref(body),\n ...options?.body,\n },\n streamProtocol,\n setCompletion: mutate,\n setLoading: loading => mutateLoading(() => loading),\n setError: err => {\n error.value = err;\n },\n setAbortController: controller => {\n abortController = controller;\n },\n onFinish,\n onError,\n fetch,\n });\n }\n\n const complete: UseCompletionHelpers['complete'] = async (\n prompt,\n options,\n ) => {\n return triggerRequest(prompt, options);\n };\n\n const stop = () => {\n if (abortController) {\n abortController.abort();\n abortController = null;\n }\n };\n\n const setCompletion = (completion: string) => {\n mutate(completion);\n };\n\n const input = ref(initialInput);\n\n const handleSubmit = (event?: { preventDefault?: () => void }) => {\n event?.preventDefault?.();\n const inputValue = input.value;\n return inputValue ? complete(inputValue) : undefined;\n };\n\n return {\n completion,\n complete,\n error,\n stop,\n setCompletion,\n input,\n handleSubmit,\n isLoading,\n };\n}\n"],"mappings":"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","names":["swrv","generateIdFunc","_a","import_ai","import_swrv","import_vue","useSWRV","swrv","store","data","completion","_a"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/use-chat.ts","../src/chat-store.ts","../src/use-completion.ts"],"sourcesContent":["export * from './use-chat';\nexport * from './use-completion';\n","import type {\n ChatRequestOptions,\n ChatStatus,\n ChatStore,\n CreateUIMessage,\n FileUIPart,\n InferUIDataParts,\n UIDataPartSchemas,\n UIMessage,\n UseChatOptions,\n} from 'ai';\nimport {\n convertFileListToFileUIParts,\n defaultChatStoreOptions,\n generateId as generateIdFunc,\n} from 'ai';\nimport type { Ref } from 'vue';\nimport { computed, ref } from 'vue';\nimport { createChatStore } from './chat-store';\n\nexport type { CreateUIMessage, UIMessage, UseChatOptions };\n\nexport type UseChatHelpers<\n MESSAGE_METADATA = unknown,\n DATA_PART_SCHEMAS extends UIDataPartSchemas = UIDataPartSchemas,\n> = {\n /**\n * The id of the chat.\n */\n readonly chatId: string;\n\n /** Current messages in the chat */\n readonly messages: Ref<\n UIMessage<MESSAGE_METADATA, InferUIDataParts<DATA_PART_SCHEMAS>>[]\n >;\n\n /** The error object of the API request */\n readonly error: Ref<Error | undefined>;\n\n /**\n * Append a user message to the chat list. This triggers the API call to fetch\n * the assistant's response.\n *\n * @param message The message to append\n * @param options Additional options to pass to the API call\n */\n append: (\n message: CreateUIMessage<\n MESSAGE_METADATA,\n InferUIDataParts<DATA_PART_SCHEMAS>\n >,\n options?: ChatRequestOptions,\n ) => Promise<void>;\n\n /**\n * Reload the last AI chat response for the given chat history. If the last\n * message isn't from the assistant, it will request the API to generate a\n * new response.\n */\n reload: (\n chatRequestOptions?: ChatRequestOptions,\n ) => Promise<string | null | undefined>;\n\n /**\n * Abort the current request immediately, keep the generated tokens if any.\n */\n stop: () => void;\n\n /**\n * Update the `messages` state locally. This is useful when you want to\n * edit the messages on the client, and then trigger the `reload` method\n * manually to regenerate the AI response.\n */\n setMessages: (\n messages:\n | UIMessage<MESSAGE_METADATA, InferUIDataParts<DATA_PART_SCHEMAS>>[]\n | ((\n messages: UIMessage<\n MESSAGE_METADATA,\n InferUIDataParts<DATA_PART_SCHEMAS>\n >[],\n ) => UIMessage<\n MESSAGE_METADATA,\n InferUIDataParts<DATA_PART_SCHEMAS>\n >[]),\n ) => void;\n\n /** The current value of the input */\n input: Ref<string>;\n\n /** Form submission handler to automatically reset input and append a user message */\n handleSubmit: (\n event?: { preventDefault?: () => void },\n chatRequestOptions?: ChatRequestOptions & {\n files?: FileList | FileUIPart[];\n },\n ) => void;\n\n /**\n * Hook status:\n *\n * - `submitted`: The message has been sent to the API and we're awaiting the start of the response stream.\n * - `streaming`: The response is actively streaming in from the API, receiving chunks of data.\n * - `ready`: The full response has been received and processed; a new user message can be submitted.\n * - `error`: An error occurred during the API request, preventing successful completion.\n */\n status: Ref<ChatStatus>;\n\n addToolResult: ({\n toolCallId,\n result,\n }: {\n toolCallId: string;\n result: any;\n }) => void;\n};\n\nexport function useChat<\n MESSAGE_METADATA = unknown,\n DATA_PART_SCHEMAS extends UIDataPartSchemas = UIDataPartSchemas,\n>({\n chatId,\n initialInput = '',\n onFinish,\n onError,\n generateId = generateIdFunc,\n onToolCall,\n chatStore: chatStoreArg,\n}: UseChatOptions<MESSAGE_METADATA, DATA_PART_SCHEMAS> = {}): UseChatHelpers<\n MESSAGE_METADATA,\n DATA_PART_SCHEMAS\n> {\n const stableChatId = chatId ?? generateId();\n\n const chatStore =\n chatStoreArg == null\n ? createChatStore(\n defaultChatStoreOptions<MESSAGE_METADATA, DATA_PART_SCHEMAS>({\n api: '/api/chat',\n generateId,\n })(),\n )\n : typeof chatStoreArg === 'function'\n ? createChatStore(chatStoreArg())\n : chatStoreArg;\n\n const messages = computed(() => chatStore.getMessages(stableChatId));\n const status = computed(() => chatStore.getStatus(stableChatId));\n const error = computed(() => chatStore.getError(stableChatId));\n\n const append = async (\n message: CreateUIMessage<\n MESSAGE_METADATA,\n InferUIDataParts<DATA_PART_SCHEMAS>\n >,\n { headers, body }: ChatRequestOptions = {},\n ) =>\n chatStore.submitMessage({\n chatId: stableChatId,\n message,\n headers,\n body,\n onError,\n onToolCall,\n onFinish,\n });\n\n const reload = async ({ headers, body }: ChatRequestOptions = {}) =>\n chatStore.resubmitLastUserMessage({\n chatId: stableChatId,\n headers,\n body,\n onError,\n onToolCall,\n onFinish,\n });\n\n const stop = () => {\n chatStore.stopStream({ chatId: stableChatId });\n };\n\n const setMessages = (\n messagesParam:\n | UIMessage<MESSAGE_METADATA, InferUIDataParts<DATA_PART_SCHEMAS>>[]\n | ((\n messages: UIMessage<\n MESSAGE_METADATA,\n InferUIDataParts<DATA_PART_SCHEMAS>\n >[],\n ) => UIMessage<\n MESSAGE_METADATA,\n InferUIDataParts<DATA_PART_SCHEMAS>\n >[]),\n ) => {\n if (typeof messagesParam === 'function') {\n messagesParam = messagesParam(chatStore.getMessages(stableChatId));\n }\n\n chatStore.setMessages({\n id: stableChatId,\n messages: messagesParam,\n });\n };\n\n const input = ref(initialInput);\n\n const handleSubmit = async (\n event?: { preventDefault?: () => void },\n options: ChatRequestOptions & { files?: FileList | FileUIPart[] } = {},\n ) => {\n event?.preventDefault?.();\n\n const inputValue = input.value;\n\n const fileParts = Array.isArray(options?.files)\n ? options.files\n : await convertFileListToFileUIParts(options?.files);\n\n if (!inputValue && fileParts.length === 0) return;\n\n await append(\n {\n id: generateId(),\n role: 'user',\n metadata: undefined,\n parts: [...fileParts, { type: 'text', text: inputValue }],\n },\n {\n headers: options.headers,\n body: options.body,\n },\n );\n\n input.value = '';\n };\n\n const addToolResult = (\n options: Omit<\n Parameters<\n ChatStore<MESSAGE_METADATA, DATA_PART_SCHEMAS>['addToolResult']\n >[0],\n 'chatId'\n >,\n ) => chatStore.addToolResult({ chatId: stableChatId, ...options });\n\n return {\n chatId: stableChatId,\n messages,\n append,\n error,\n reload,\n stop,\n setMessages,\n input,\n handleSubmit,\n status,\n addToolResult,\n };\n}\n","import {\n ChatStore,\n SerialJobExecutor,\n type ActiveResponse,\n type Chat,\n type ChatStatus,\n type ChatStoreOptions,\n type InferUIDataParts,\n type UIDataPartSchemas,\n type UIDataTypes,\n type UIMessage,\n} from 'ai';\nimport { Ref, ref } from 'vue';\n\nclass VueChat<MESSAGE_METADATA, DATA_TYPES extends UIDataTypes>\n implements Chat<MESSAGE_METADATA, DATA_TYPES>\n{\n private messagesRef: Ref<UIMessage<MESSAGE_METADATA, DATA_TYPES>[]>;\n private statusRef = ref<ChatStatus>('ready');\n private errorRef = ref<Error | undefined>(undefined);\n\n activeResponse: ActiveResponse<MESSAGE_METADATA> | undefined = undefined;\n jobExecutor = new SerialJobExecutor();\n\n constructor(messages?: UIMessage<MESSAGE_METADATA, DATA_TYPES>[]) {\n this.messagesRef = ref(messages ?? []) as Ref<\n UIMessage<MESSAGE_METADATA, DATA_TYPES>[]\n >;\n }\n\n get messages(): UIMessage<MESSAGE_METADATA, DATA_TYPES>[] {\n return this.messagesRef.value;\n }\n\n get status(): ChatStatus {\n return this.statusRef.value;\n }\n\n get error(): Error | undefined {\n return this.errorRef.value;\n }\n\n setStatus = (status: ChatStatus) => {\n this.statusRef.value = status;\n };\n\n setError = (error: Error | undefined) => {\n this.errorRef.value = error;\n };\n\n setActiveResponse = (\n activeResponse: ActiveResponse<MESSAGE_METADATA> | undefined,\n ) => {\n this.activeResponse = activeResponse;\n };\n\n setMessages = (messages: UIMessage<MESSAGE_METADATA, DATA_TYPES>[]) => {\n this.messagesRef.value = messages;\n };\n\n pushMessage = (message: UIMessage<MESSAGE_METADATA, DATA_TYPES>) => {\n this.messagesRef.value.push(message);\n };\n\n popMessage = () => {\n this.messagesRef.value.pop();\n };\n\n replaceMessage = (\n index: number,\n message: UIMessage<MESSAGE_METADATA, DATA_TYPES>,\n ) => {\n // message is cloned here because vue's deep reactivity shows unexpected behavior, particularly when updating tool invocation parts\n this.messagesRef.value[index] = { ...message };\n };\n}\n\nexport function createChatStore<\n MESSAGE_METADATA = unknown,\n DATA_PART_SCHEMAS extends UIDataPartSchemas = UIDataPartSchemas,\n>(\n options: ChatStoreOptions<MESSAGE_METADATA, DATA_PART_SCHEMAS>,\n): ChatStore<MESSAGE_METADATA, DATA_PART_SCHEMAS> {\n return new ChatStore<MESSAGE_METADATA, DATA_PART_SCHEMAS>({\n ...options,\n createChat: options =>\n new VueChat<MESSAGE_METADATA, InferUIDataParts<DATA_PART_SCHEMAS>>(\n options.messages,\n ),\n });\n}\n","import type { CompletionRequestOptions, UseCompletionOptions } from 'ai';\nimport { callCompletionApi } from 'ai';\nimport swrv from 'swrv';\nimport type { Ref } from 'vue';\nimport { ref, unref } from 'vue';\n\nexport type { UseCompletionOptions };\n\nexport type UseCompletionHelpers = {\n /** The current completion result */\n completion: Ref<string>;\n /** The error object of the API request */\n error: Ref<undefined | Error>;\n /**\n * Send a new prompt to the API endpoint and update the completion state.\n */\n complete: (\n prompt: string,\n options?: CompletionRequestOptions,\n ) => Promise<string | null | undefined>;\n /**\n * Abort the current API request but keep the generated tokens.\n */\n stop: () => void;\n /**\n * Update the `completion` state locally.\n */\n setCompletion: (completion: string) => void;\n /** The current value of the input */\n input: Ref<string>;\n /**\n * Form submission handler to automatically reset input and append a user message\n * @example\n * ```jsx\n * <form @submit=\"handleSubmit\">\n * <input @change=\"handleInputChange\" v-model=\"input\" />\n * </form>\n * ```\n */\n handleSubmit: (event?: { preventDefault?: () => void }) => void;\n /** Whether the API request is in progress */\n isLoading: Ref<boolean | undefined>;\n};\n\nlet uniqueId = 0;\n\n// @ts-expect-error - some issues with the default export of useSWRV\nconst useSWRV = (swrv.default as (typeof import('swrv'))['default']) || swrv;\nconst store: Record<string, any> = {};\n\nexport function useCompletion({\n api = '/api/completion',\n id,\n initialCompletion = '',\n initialInput = '',\n credentials,\n headers,\n body,\n streamProtocol,\n onFinish,\n onError,\n fetch,\n}: UseCompletionOptions = {}): UseCompletionHelpers {\n // Generate an unique id for the completion if not provided.\n const completionId = id || `completion-${uniqueId++}`;\n\n const key = `${api}|${completionId}`;\n const { data, mutate: originalMutate } = useSWRV<string>(\n key,\n () => store[key] || initialCompletion,\n );\n\n const { data: isLoading, mutate: mutateLoading } = useSWRV<boolean>(\n `${completionId}-loading`,\n null,\n );\n\n isLoading.value ??= false;\n\n // Force the `data` to be `initialCompletion` if it's `undefined`.\n data.value ||= initialCompletion;\n\n const mutate = (data: string) => {\n store[key] = data;\n return originalMutate();\n };\n\n // Because of the `initialData` option, the `data` will never be `undefined`.\n const completion = data as Ref<string>;\n\n const error = ref<undefined | Error>(undefined);\n\n let abortController: AbortController | null = null;\n\n async function triggerRequest(\n prompt: string,\n options?: CompletionRequestOptions,\n ) {\n return callCompletionApi({\n api,\n prompt,\n credentials,\n headers: {\n ...headers,\n ...options?.headers,\n },\n body: {\n ...unref(body),\n ...options?.body,\n },\n streamProtocol,\n setCompletion: mutate,\n setLoading: loading => mutateLoading(() => loading),\n setError: err => {\n error.value = err;\n },\n setAbortController: controller => {\n abortController = controller;\n },\n onFinish,\n onError,\n fetch,\n });\n }\n\n const complete: UseCompletionHelpers['complete'] = async (\n prompt,\n options,\n ) => {\n return triggerRequest(prompt, options);\n };\n\n const stop = () => {\n if (abortController) {\n abortController.abort();\n abortController = null;\n }\n };\n\n const setCompletion = (completion: string) => {\n mutate(completion);\n };\n\n const input = ref(initialInput);\n\n const handleSubmit = (event?: { preventDefault?: () => void }) => {\n event?.preventDefault?.();\n const inputValue = input.value;\n return inputValue ? complete(inputValue) : undefined;\n };\n\n return {\n completion,\n complete,\n error,\n stop,\n setCompletion,\n input,\n handleSubmit,\n isLoading,\n };\n}\n"],"mappings":"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","names":["import_ai","import_vue","options","generateIdFunc","import_ai","import_vue","swrv","data","completion","_a"]}
package/dist/index.mjs CHANGED
@@ -1,197 +1,137 @@
1
1
  // src/use-chat.ts
2
2
  import {
3
- callChatApi,
4
3
  convertFileListToFileUIParts,
5
- extractMaxToolInvocationStep,
6
- generateId as generateIdFunc,
7
- getToolInvocations,
8
- isAssistantMessageWithCompletedToolCalls,
9
- shouldResubmitMessages,
10
- updateToolCallResult
4
+ defaultChatStoreOptions,
5
+ generateId as generateIdFunc
11
6
  } from "ai";
12
- import swrv from "swrv";
13
- import { ref, unref } from "vue";
14
- var useSWRV = swrv.default || swrv;
15
- var store = {};
16
- var statusStore = {};
7
+ import { computed, ref as ref2 } from "vue";
8
+
9
+ // src/chat-store.ts
10
+ import {
11
+ ChatStore,
12
+ SerialJobExecutor
13
+ } from "ai";
14
+ import { ref } from "vue";
15
+ var VueChat = class {
16
+ constructor(messages) {
17
+ this.statusRef = ref("ready");
18
+ this.errorRef = ref(void 0);
19
+ this.activeResponse = void 0;
20
+ this.jobExecutor = new SerialJobExecutor();
21
+ this.setStatus = (status) => {
22
+ this.statusRef.value = status;
23
+ };
24
+ this.setError = (error) => {
25
+ this.errorRef.value = error;
26
+ };
27
+ this.setActiveResponse = (activeResponse) => {
28
+ this.activeResponse = activeResponse;
29
+ };
30
+ this.setMessages = (messages) => {
31
+ this.messagesRef.value = messages;
32
+ };
33
+ this.pushMessage = (message) => {
34
+ this.messagesRef.value.push(message);
35
+ };
36
+ this.popMessage = () => {
37
+ this.messagesRef.value.pop();
38
+ };
39
+ this.replaceMessage = (index, message) => {
40
+ this.messagesRef.value[index] = { ...message };
41
+ };
42
+ this.messagesRef = ref(messages != null ? messages : []);
43
+ }
44
+ get messages() {
45
+ return this.messagesRef.value;
46
+ }
47
+ get status() {
48
+ return this.statusRef.value;
49
+ }
50
+ get error() {
51
+ return this.errorRef.value;
52
+ }
53
+ };
54
+ function createChatStore(options) {
55
+ return new ChatStore({
56
+ ...options,
57
+ createChat: (options2) => new VueChat(
58
+ options2.messages
59
+ )
60
+ });
61
+ }
62
+
63
+ // src/use-chat.ts
17
64
  function useChat({
18
- api = "/api/chat",
19
65
  chatId,
20
- initialMessages = [],
21
66
  initialInput = "",
22
- streamProtocol = "ui-message",
23
67
  onFinish,
24
68
  onError,
25
- credentials,
26
- headers: metadataHeaders,
27
- body: metadataBody,
28
69
  generateId = generateIdFunc,
29
70
  onToolCall,
30
- fetch,
31
- maxSteps = 1,
32
- experimental_prepareRequestBody,
33
- messageMetadataSchema
34
- } = {
35
- maxSteps: 1
36
- }) {
37
- var _a, _b;
71
+ chatStore: chatStoreArg
72
+ } = {}) {
38
73
  const stableChatId = chatId != null ? chatId : generateId();
39
- const key = `${api}|${stableChatId}`;
40
- const { data: messagesData, mutate: originalMutate } = useSWRV(key, () => {
41
- var _a2;
42
- return (_a2 = store[key]) != null ? _a2 : initialMessages;
74
+ const chatStore = chatStoreArg == null ? createChatStore(
75
+ defaultChatStoreOptions({
76
+ api: "/api/chat",
77
+ generateId
78
+ })()
79
+ ) : typeof chatStoreArg === "function" ? createChatStore(chatStoreArg()) : chatStoreArg;
80
+ const messages = computed(() => chatStore.getMessages(stableChatId));
81
+ const status = computed(() => chatStore.getStatus(stableChatId));
82
+ const error = computed(() => chatStore.getError(stableChatId));
83
+ const append = async (message, { headers, body } = {}) => chatStore.submitMessage({
84
+ chatId: stableChatId,
85
+ message,
86
+ headers,
87
+ body,
88
+ onError,
89
+ onToolCall,
90
+ onFinish
91
+ });
92
+ const reload = async ({ headers, body } = {}) => chatStore.resubmitLastUserMessage({
93
+ chatId: stableChatId,
94
+ headers,
95
+ body,
96
+ onError,
97
+ onToolCall,
98
+ onFinish
43
99
  });
44
- const status = (_a = statusStore[stableChatId]) != null ? _a : statusStore[stableChatId] = ref("ready");
45
- (_b = messagesData.value) != null ? _b : messagesData.value = initialMessages;
46
- const mutate = (data) => {
47
- store[key] = data;
48
- return originalMutate();
49
- };
50
- const messages = messagesData;
51
- const error = ref(void 0);
52
- let abortController = null;
53
- async function triggerRequest(messagesSnapshot, { headers, body } = {}) {
54
- var _a2;
55
- error.value = void 0;
56
- status.value = "submitted";
57
- const messageCount = messages.value.length;
58
- const lastMessage = messages.value.at(-1);
59
- const maxStep = lastMessage != null ? extractMaxToolInvocationStep(getToolInvocations(lastMessage)) : 0;
60
- try {
61
- abortController = new AbortController();
62
- mutate(messagesSnapshot);
63
- await callChatApi({
64
- api,
65
- body: (_a2 = experimental_prepareRequestBody == null ? void 0 : experimental_prepareRequestBody({
66
- chatId: stableChatId,
67
- messages: messagesSnapshot,
68
- requestBody: body
69
- })) != null ? _a2 : {
70
- chatId: stableChatId,
71
- messages: messagesSnapshot,
72
- ...unref(metadataBody),
73
- // Use unref to unwrap the ref value
74
- ...body
75
- },
76
- streamProtocol,
77
- headers: {
78
- ...metadataHeaders,
79
- ...headers
80
- },
81
- abortController: () => abortController,
82
- credentials,
83
- onUpdate({ message }) {
84
- status.value = "streaming";
85
- const replaceLastMessage = message.id === messagesSnapshot[messagesSnapshot.length - 1].id;
86
- mutate([
87
- ...replaceLastMessage ? messagesSnapshot.slice(0, messagesSnapshot.length - 1) : messagesSnapshot,
88
- message
89
- ]);
90
- },
91
- onFinish,
92
- generateId,
93
- onToolCall,
94
- fetch,
95
- // enabled use of structured clone in processChatResponse:
96
- lastMessage: recursiveToRaw(
97
- messagesSnapshot[messagesSnapshot.length - 1]
98
- ),
99
- messageMetadataSchema
100
- });
101
- status.value = "ready";
102
- } catch (err) {
103
- if (err.name === "AbortError") {
104
- abortController = null;
105
- status.value = "ready";
106
- return null;
107
- }
108
- if (onError && err instanceof Error) {
109
- onError(err);
110
- }
111
- error.value = err;
112
- status.value = "error";
113
- } finally {
114
- abortController = null;
115
- }
116
- if (shouldResubmitMessages({
117
- originalMaxToolInvocationStep: maxStep,
118
- originalMessageCount: messageCount,
119
- maxSteps,
120
- messages: messages.value
121
- })) {
122
- await triggerRequest(messages.value);
123
- }
124
- }
125
- const append = async (message, options) => {
126
- var _a2;
127
- return triggerRequest(
128
- messages.value.concat({
129
- ...message,
130
- id: (_a2 = message.id) != null ? _a2 : generateId(),
131
- parts: message.parts
132
- }),
133
- options
134
- );
135
- };
136
- const reload = async (options) => {
137
- const messagesSnapshot = messages.value;
138
- if (messagesSnapshot.length === 0)
139
- return null;
140
- const lastMessage = messagesSnapshot[messagesSnapshot.length - 1];
141
- if (lastMessage.role === "assistant") {
142
- return triggerRequest(messagesSnapshot.slice(0, -1), options);
143
- }
144
- return triggerRequest(messagesSnapshot, options);
145
- };
146
100
  const stop = () => {
147
- if (abortController) {
148
- abortController.abort();
149
- abortController = null;
150
- }
101
+ chatStore.stopStream({ chatId: stableChatId });
151
102
  };
152
- const setMessages = (messagesArg) => {
153
- if (typeof messagesArg === "function") {
154
- messagesArg = messagesArg(messages.value);
103
+ const setMessages = (messagesParam) => {
104
+ if (typeof messagesParam === "function") {
105
+ messagesParam = messagesParam(chatStore.getMessages(stableChatId));
155
106
  }
156
- mutate(messagesArg);
107
+ chatStore.setMessages({
108
+ id: stableChatId,
109
+ messages: messagesParam
110
+ });
157
111
  };
158
- const input = ref(initialInput);
112
+ const input = ref2(initialInput);
159
113
  const handleSubmit = async (event, options = {}) => {
160
- var _a2;
161
- (_a2 = event == null ? void 0 : event.preventDefault) == null ? void 0 : _a2.call(event);
114
+ var _a;
115
+ (_a = event == null ? void 0 : event.preventDefault) == null ? void 0 : _a.call(event);
162
116
  const inputValue = input.value;
163
117
  const fileParts = Array.isArray(options == null ? void 0 : options.files) ? options.files : await convertFileListToFileUIParts(options == null ? void 0 : options.files);
164
118
  if (!inputValue && fileParts.length === 0)
165
119
  return;
166
- triggerRequest(
167
- messages.value.concat({
120
+ await append(
121
+ {
168
122
  id: generateId(),
169
123
  role: "user",
124
+ metadata: void 0,
170
125
  parts: [...fileParts, { type: "text", text: inputValue }]
171
- }),
172
- options
126
+ },
127
+ {
128
+ headers: options.headers,
129
+ body: options.body
130
+ }
173
131
  );
174
132
  input.value = "";
175
133
  };
176
- const addToolResult = ({
177
- toolCallId,
178
- result
179
- }) => {
180
- const currentMessages = messages.value;
181
- updateToolCallResult({
182
- messages: currentMessages,
183
- toolCallId,
184
- toolResult: result
185
- });
186
- mutate(currentMessages);
187
- if (status.value === "submitted" || status.value === "streaming") {
188
- return;
189
- }
190
- const lastMessage = currentMessages[currentMessages.length - 1];
191
- if (isAssistantMessageWithCompletedToolCalls(lastMessage)) {
192
- triggerRequest(currentMessages);
193
- }
194
- };
134
+ const addToolResult = (options) => chatStore.addToolResult({ chatId: stableChatId, ...options });
195
135
  return {
196
136
  chatId: stableChatId,
197
137
  messages,
@@ -206,27 +146,14 @@ function useChat({
206
146
  addToolResult
207
147
  };
208
148
  }
209
- function recursiveToRaw(inputValue) {
210
- if (Array.isArray(inputValue)) {
211
- return [...inputValue.map(recursiveToRaw)];
212
- } else if (typeof inputValue === "object" && inputValue !== null) {
213
- const clone = {};
214
- for (const [key, value] of Object.entries(inputValue)) {
215
- clone[key] = recursiveToRaw(value);
216
- }
217
- return clone;
218
- } else {
219
- return inputValue;
220
- }
221
- }
222
149
 
223
150
  // src/use-completion.ts
224
151
  import { callCompletionApi } from "ai";
225
- import swrv2 from "swrv";
226
- import { ref as ref2, unref as unref2 } from "vue";
152
+ import swrv from "swrv";
153
+ import { ref as ref3, unref } from "vue";
227
154
  var uniqueId = 0;
228
- var useSWRV2 = swrv2.default || swrv2;
229
- var store2 = {};
155
+ var useSWRV = swrv.default || swrv;
156
+ var store = {};
230
157
  function useCompletion({
231
158
  api = "/api/completion",
232
159
  id,
@@ -243,22 +170,22 @@ function useCompletion({
243
170
  var _a;
244
171
  const completionId = id || `completion-${uniqueId++}`;
245
172
  const key = `${api}|${completionId}`;
246
- const { data, mutate: originalMutate } = useSWRV2(
173
+ const { data, mutate: originalMutate } = useSWRV(
247
174
  key,
248
- () => store2[key] || initialCompletion
175
+ () => store[key] || initialCompletion
249
176
  );
250
- const { data: isLoading, mutate: mutateLoading } = useSWRV2(
177
+ const { data: isLoading, mutate: mutateLoading } = useSWRV(
251
178
  `${completionId}-loading`,
252
179
  null
253
180
  );
254
181
  (_a = isLoading.value) != null ? _a : isLoading.value = false;
255
182
  data.value || (data.value = initialCompletion);
256
183
  const mutate = (data2) => {
257
- store2[key] = data2;
184
+ store[key] = data2;
258
185
  return originalMutate();
259
186
  };
260
187
  const completion = data;
261
- const error = ref2(void 0);
188
+ const error = ref3(void 0);
262
189
  let abortController = null;
263
190
  async function triggerRequest(prompt, options) {
264
191
  return callCompletionApi({
@@ -270,7 +197,7 @@ function useCompletion({
270
197
  ...options == null ? void 0 : options.headers
271
198
  },
272
199
  body: {
273
- ...unref2(body),
200
+ ...unref(body),
274
201
  ...options == null ? void 0 : options.body
275
202
  },
276
203
  streamProtocol,
@@ -299,7 +226,7 @@ function useCompletion({
299
226
  const setCompletion = (completion2) => {
300
227
  mutate(completion2);
301
228
  };
302
- const input = ref2(initialInput);
229
+ const input = ref3(initialInput);
303
230
  const handleSubmit = (event) => {
304
231
  var _a2;
305
232
  (_a2 = event == null ? void 0 : event.preventDefault) == null ? void 0 : _a2.call(event);
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/use-chat.ts","../src/use-completion.ts"],"sourcesContent":["import type {\n ChatRequestOptions,\n CreateUIMessage,\n FileUIPart,\n JSONValue,\n OriginalUseChatOptions,\n UIMessage,\n UseChatOptions,\n} from 'ai';\nimport {\n callChatApi,\n convertFileListToFileUIParts,\n extractMaxToolInvocationStep,\n generateId as generateIdFunc,\n getToolInvocations,\n isAssistantMessageWithCompletedToolCalls,\n shouldResubmitMessages,\n updateToolCallResult,\n} from 'ai';\nimport swrv from 'swrv';\nimport type { Ref } from 'vue';\nimport { ref, unref } from 'vue';\n\nexport type { CreateUIMessage, UIMessage, UseChatOptions };\n\nexport type UseChatHelpers<MESSAGE_METADATA> = {\n /** Current messages in the chat */\n messages: Ref<UIMessage<MESSAGE_METADATA>[]>;\n /** The error object of the API request */\n error: Ref<undefined | Error>;\n /**\n * Append a user message to the chat list. This triggers the API call to fetch\n * the assistant's response.\n */\n append: (\n message: UIMessage<MESSAGE_METADATA> | CreateUIMessage<MESSAGE_METADATA>,\n chatRequestOptions?: ChatRequestOptions,\n ) => Promise<string | null | undefined>;\n /**\n * Reload the last AI chat response for the given chat history. If the last\n * message isn't from the assistant, it will request the API to generate a\n * new response.\n */\n reload: (\n chatRequestOptions?: ChatRequestOptions,\n ) => Promise<string | null | undefined>;\n /**\n * Abort the current request immediately, keep the generated tokens if any.\n */\n stop: () => void;\n /**\n * Update the `messages` state locally. This is useful when you want to\n * edit the messages on the client, and then trigger the `reload` method\n * manually to regenerate the AI response.\n */\n setMessages: (\n messages:\n | UIMessage<MESSAGE_METADATA>[]\n | ((\n messages: UIMessage<MESSAGE_METADATA>[],\n ) => UIMessage<MESSAGE_METADATA>[]),\n ) => void;\n /** The current value of the input */\n input: Ref<string>;\n /** Form submission handler to automatically reset input and append a user message */\n handleSubmit: (\n event?: { preventDefault?: () => void },\n chatRequestOptions?: ChatRequestOptions & {\n files?: FileList | FileUIPart[];\n },\n ) => void;\n\n /**\n * Hook status:\n *\n * - `submitted`: The message has been sent to the API and we're awaiting the start of the response stream.\n * - `streaming`: The response is actively streaming in from the API, receiving chunks of data.\n * - `ready`: The full response has been received and processed; a new user message can be submitted.\n * - `error`: An error occurred during the API request, preventing successful completion.\n */\n status: Ref<'submitted' | 'streaming' | 'ready' | 'error'>;\n\n addToolResult: ({\n toolCallId,\n result,\n }: {\n toolCallId: string;\n result: any;\n }) => void;\n\n /** The id of the chat */\n chatId: string;\n};\n\n// @ts-expect-error - some issues with the default export of useSWRV\nconst useSWRV = (swrv.default as (typeof import('swrv'))['default']) || swrv;\nconst store: Record<string, UIMessage<any>[] | undefined> = {};\nconst statusStore: Record<\n string,\n Ref<'submitted' | 'streaming' | 'ready' | 'error'>\n> = {};\n\nexport function useChat<MESSAGE_METADATA = unknown>(\n {\n api = '/api/chat',\n chatId,\n initialMessages = [],\n initialInput = '',\n streamProtocol = 'ui-message',\n onFinish,\n onError,\n credentials,\n headers: metadataHeaders,\n body: metadataBody,\n generateId = generateIdFunc,\n onToolCall,\n fetch,\n maxSteps = 1,\n experimental_prepareRequestBody,\n messageMetadataSchema,\n }: OriginalUseChatOptions<MESSAGE_METADATA> & {\n /**\n * Experimental (Vue only). When a function is provided, it will be used\n * to prepare the request body for the chat API. This can be useful for\n * customizing the request body based on the messages and data in the chat.\n *\n * @param id The chat ID\n * @param messages The current messages in the chat\n * @param requestData The data object passed in the chat request\n * @param requestBody The request body object passed in the chat request\n */\n experimental_prepareRequestBody?: (options: {\n chatId: string;\n messages: UIMessage<MESSAGE_METADATA>[];\n requestData?: JSONValue;\n requestBody?: object;\n }) => unknown;\n } = {\n maxSteps: 1,\n },\n): UseChatHelpers<MESSAGE_METADATA> {\n // Generate a unique ID for the chat if not provided.\n const stableChatId = chatId ?? generateId();\n\n const key = `${api}|${stableChatId}`;\n const { data: messagesData, mutate: originalMutate } = useSWRV<\n UIMessage<MESSAGE_METADATA>[]\n >(key, () => store[key] ?? initialMessages);\n\n const status =\n statusStore[stableChatId] ??\n (statusStore[stableChatId] = ref<\n 'submitted' | 'streaming' | 'ready' | 'error'\n >('ready'));\n\n // Force the `data` to be `initialMessages` if it's `undefined`.\n messagesData.value ??= initialMessages;\n\n const mutate = (data?: UIMessage<MESSAGE_METADATA>[]) => {\n store[key] = data;\n return originalMutate();\n };\n\n // Because of the `initialData` option, the `data` will never be `undefined`.\n const messages = messagesData as Ref<UIMessage<MESSAGE_METADATA>[]>;\n\n const error = ref<undefined | Error>(undefined);\n\n let abortController: AbortController | null = null;\n\n async function triggerRequest(\n messagesSnapshot: UIMessage<MESSAGE_METADATA>[],\n { headers, body }: ChatRequestOptions = {},\n ) {\n error.value = undefined;\n status.value = 'submitted';\n\n const messageCount = messages.value.length;\n const lastMessage = messages.value.at(-1);\n const maxStep =\n lastMessage != null\n ? extractMaxToolInvocationStep(getToolInvocations(lastMessage))\n : 0;\n\n try {\n abortController = new AbortController();\n\n // Do an optimistic update to show the updated messages immediately:\n mutate(messagesSnapshot);\n\n await callChatApi({\n api,\n body: experimental_prepareRequestBody?.({\n chatId: stableChatId,\n messages: messagesSnapshot,\n requestBody: body,\n }) ?? {\n chatId: stableChatId,\n messages: messagesSnapshot,\n ...unref(metadataBody), // Use unref to unwrap the ref value\n ...body,\n },\n streamProtocol,\n headers: {\n ...metadataHeaders,\n ...headers,\n },\n abortController: () => abortController,\n credentials,\n onUpdate({ message }) {\n status.value = 'streaming';\n\n const replaceLastMessage =\n message.id === messagesSnapshot[messagesSnapshot.length - 1].id;\n\n mutate([\n ...(replaceLastMessage\n ? messagesSnapshot.slice(0, messagesSnapshot.length - 1)\n : messagesSnapshot),\n message,\n ]);\n },\n onFinish,\n generateId,\n onToolCall,\n fetch,\n // enabled use of structured clone in processChatResponse:\n lastMessage: recursiveToRaw(\n messagesSnapshot[messagesSnapshot.length - 1],\n ),\n messageMetadataSchema,\n });\n\n status.value = 'ready';\n } catch (err) {\n // Ignore abort errors as they are expected.\n if ((err as any).name === 'AbortError') {\n abortController = null;\n status.value = 'ready';\n return null;\n }\n\n if (onError && err instanceof Error) {\n onError(err);\n }\n\n error.value = err as Error;\n status.value = 'error';\n } finally {\n abortController = null;\n }\n\n // auto-submit when all tool calls in the last assistant message have results:\n if (\n shouldResubmitMessages({\n originalMaxToolInvocationStep: maxStep,\n originalMessageCount: messageCount,\n maxSteps,\n messages: messages.value,\n })\n ) {\n await triggerRequest(messages.value);\n }\n }\n\n const append: UseChatHelpers<MESSAGE_METADATA>['append'] = async (\n message,\n options,\n ) => {\n return triggerRequest(\n messages.value.concat({\n ...message,\n id: message.id ?? generateId(),\n parts: message.parts,\n }),\n options,\n );\n };\n\n const reload: UseChatHelpers<MESSAGE_METADATA>['reload'] = async options => {\n const messagesSnapshot = messages.value;\n if (messagesSnapshot.length === 0) return null;\n\n const lastMessage = messagesSnapshot[messagesSnapshot.length - 1];\n if (lastMessage.role === 'assistant') {\n return triggerRequest(messagesSnapshot.slice(0, -1), options);\n }\n\n return triggerRequest(messagesSnapshot, options);\n };\n\n const stop = () => {\n if (abortController) {\n abortController.abort();\n abortController = null;\n }\n };\n\n const setMessages = (\n messagesArg:\n | UIMessage<MESSAGE_METADATA>[]\n | ((\n messages: UIMessage<MESSAGE_METADATA>[],\n ) => UIMessage<MESSAGE_METADATA>[]),\n ) => {\n if (typeof messagesArg === 'function') {\n messagesArg = messagesArg(messages.value);\n }\n\n mutate(messagesArg);\n };\n\n const input = ref(initialInput);\n\n const handleSubmit = async (\n event?: { preventDefault?: () => void },\n options: ChatRequestOptions & { files?: FileList | FileUIPart[] } = {},\n ) => {\n event?.preventDefault?.();\n\n const inputValue = input.value;\n\n const fileParts = Array.isArray(options?.files)\n ? options.files\n : await convertFileListToFileUIParts(options?.files);\n\n if (!inputValue && fileParts.length === 0) return;\n\n triggerRequest(\n messages.value.concat({\n id: generateId(),\n role: 'user',\n parts: [...fileParts, { type: 'text', text: inputValue }],\n }),\n options,\n );\n\n input.value = '';\n };\n\n const addToolResult = ({\n toolCallId,\n result,\n }: {\n toolCallId: string;\n result: unknown;\n }) => {\n const currentMessages = messages.value;\n\n updateToolCallResult({\n messages: currentMessages,\n toolCallId,\n toolResult: result,\n });\n\n mutate(currentMessages);\n\n // when the request is ongoing, the auto-submit will be triggered after the request is finished\n if (status.value === 'submitted' || status.value === 'streaming') {\n return;\n }\n\n // auto-submit when all tool calls in the last assistant message have results:\n const lastMessage = currentMessages[currentMessages.length - 1];\n if (isAssistantMessageWithCompletedToolCalls(lastMessage)) {\n triggerRequest(currentMessages);\n }\n };\n\n return {\n chatId: stableChatId,\n messages,\n append,\n error,\n reload,\n stop,\n setMessages,\n input,\n handleSubmit,\n status: status as Ref<'submitted' | 'streaming' | 'ready' | 'error'>,\n addToolResult,\n };\n}\n\n// required for use of structured clone\nfunction recursiveToRaw<T>(inputValue: T): T {\n if (Array.isArray(inputValue)) {\n return [...inputValue.map(recursiveToRaw)] as T;\n } else if (typeof inputValue === 'object' && inputValue !== null) {\n const clone: any = {};\n for (const [key, value] of Object.entries(inputValue)) {\n clone[key] = recursiveToRaw(value);\n }\n return clone;\n } else {\n return inputValue;\n }\n}\n","import type { CompletionRequestOptions, UseCompletionOptions } from 'ai';\nimport { callCompletionApi } from 'ai';\nimport swrv from 'swrv';\nimport type { Ref } from 'vue';\nimport { ref, unref } from 'vue';\n\nexport type { UseCompletionOptions };\n\nexport type UseCompletionHelpers = {\n /** The current completion result */\n completion: Ref<string>;\n /** The error object of the API request */\n error: Ref<undefined | Error>;\n /**\n * Send a new prompt to the API endpoint and update the completion state.\n */\n complete: (\n prompt: string,\n options?: CompletionRequestOptions,\n ) => Promise<string | null | undefined>;\n /**\n * Abort the current API request but keep the generated tokens.\n */\n stop: () => void;\n /**\n * Update the `completion` state locally.\n */\n setCompletion: (completion: string) => void;\n /** The current value of the input */\n input: Ref<string>;\n /**\n * Form submission handler to automatically reset input and append a user message\n * @example\n * ```jsx\n * <form @submit=\"handleSubmit\">\n * <input @change=\"handleInputChange\" v-model=\"input\" />\n * </form>\n * ```\n */\n handleSubmit: (event?: { preventDefault?: () => void }) => void;\n /** Whether the API request is in progress */\n isLoading: Ref<boolean | undefined>;\n};\n\nlet uniqueId = 0;\n\n// @ts-expect-error - some issues with the default export of useSWRV\nconst useSWRV = (swrv.default as (typeof import('swrv'))['default']) || swrv;\nconst store: Record<string, any> = {};\n\nexport function useCompletion({\n api = '/api/completion',\n id,\n initialCompletion = '',\n initialInput = '',\n credentials,\n headers,\n body,\n streamProtocol,\n onFinish,\n onError,\n fetch,\n}: UseCompletionOptions = {}): UseCompletionHelpers {\n // Generate an unique id for the completion if not provided.\n const completionId = id || `completion-${uniqueId++}`;\n\n const key = `${api}|${completionId}`;\n const { data, mutate: originalMutate } = useSWRV<string>(\n key,\n () => store[key] || initialCompletion,\n );\n\n const { data: isLoading, mutate: mutateLoading } = useSWRV<boolean>(\n `${completionId}-loading`,\n null,\n );\n\n isLoading.value ??= false;\n\n // Force the `data` to be `initialCompletion` if it's `undefined`.\n data.value ||= initialCompletion;\n\n const mutate = (data: string) => {\n store[key] = data;\n return originalMutate();\n };\n\n // Because of the `initialData` option, the `data` will never be `undefined`.\n const completion = data as Ref<string>;\n\n const error = ref<undefined | Error>(undefined);\n\n let abortController: AbortController | null = null;\n\n async function triggerRequest(\n prompt: string,\n options?: CompletionRequestOptions,\n ) {\n return callCompletionApi({\n api,\n prompt,\n credentials,\n headers: {\n ...headers,\n ...options?.headers,\n },\n body: {\n ...unref(body),\n ...options?.body,\n },\n streamProtocol,\n setCompletion: mutate,\n setLoading: loading => mutateLoading(() => loading),\n setError: err => {\n error.value = err;\n },\n setAbortController: controller => {\n abortController = controller;\n },\n onFinish,\n onError,\n fetch,\n });\n }\n\n const complete: UseCompletionHelpers['complete'] = async (\n prompt,\n options,\n ) => {\n return triggerRequest(prompt, options);\n };\n\n const stop = () => {\n if (abortController) {\n abortController.abort();\n abortController = null;\n }\n };\n\n const setCompletion = (completion: string) => {\n mutate(completion);\n };\n\n const input = ref(initialInput);\n\n const handleSubmit = (event?: { preventDefault?: () => void }) => {\n event?.preventDefault?.();\n const inputValue = input.value;\n return inputValue ? complete(inputValue) : undefined;\n };\n\n return {\n completion,\n complete,\n error,\n stop,\n setCompletion,\n input,\n handleSubmit,\n isLoading,\n };\n}\n"],"mappings":"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","names":["_a","swrv","ref","unref","useSWRV","store","data","completion","_a"]}
1
+ {"version":3,"sources":["../src/use-chat.ts","../src/chat-store.ts","../src/use-completion.ts"],"sourcesContent":["import type {\n ChatRequestOptions,\n ChatStatus,\n ChatStore,\n CreateUIMessage,\n FileUIPart,\n InferUIDataParts,\n UIDataPartSchemas,\n UIMessage,\n UseChatOptions,\n} from 'ai';\nimport {\n convertFileListToFileUIParts,\n defaultChatStoreOptions,\n generateId as generateIdFunc,\n} from 'ai';\nimport type { Ref } from 'vue';\nimport { computed, ref } from 'vue';\nimport { createChatStore } from './chat-store';\n\nexport type { CreateUIMessage, UIMessage, UseChatOptions };\n\nexport type UseChatHelpers<\n MESSAGE_METADATA = unknown,\n DATA_PART_SCHEMAS extends UIDataPartSchemas = UIDataPartSchemas,\n> = {\n /**\n * The id of the chat.\n */\n readonly chatId: string;\n\n /** Current messages in the chat */\n readonly messages: Ref<\n UIMessage<MESSAGE_METADATA, InferUIDataParts<DATA_PART_SCHEMAS>>[]\n >;\n\n /** The error object of the API request */\n readonly error: Ref<Error | undefined>;\n\n /**\n * Append a user message to the chat list. This triggers the API call to fetch\n * the assistant's response.\n *\n * @param message The message to append\n * @param options Additional options to pass to the API call\n */\n append: (\n message: CreateUIMessage<\n MESSAGE_METADATA,\n InferUIDataParts<DATA_PART_SCHEMAS>\n >,\n options?: ChatRequestOptions,\n ) => Promise<void>;\n\n /**\n * Reload the last AI chat response for the given chat history. If the last\n * message isn't from the assistant, it will request the API to generate a\n * new response.\n */\n reload: (\n chatRequestOptions?: ChatRequestOptions,\n ) => Promise<string | null | undefined>;\n\n /**\n * Abort the current request immediately, keep the generated tokens if any.\n */\n stop: () => void;\n\n /**\n * Update the `messages` state locally. This is useful when you want to\n * edit the messages on the client, and then trigger the `reload` method\n * manually to regenerate the AI response.\n */\n setMessages: (\n messages:\n | UIMessage<MESSAGE_METADATA, InferUIDataParts<DATA_PART_SCHEMAS>>[]\n | ((\n messages: UIMessage<\n MESSAGE_METADATA,\n InferUIDataParts<DATA_PART_SCHEMAS>\n >[],\n ) => UIMessage<\n MESSAGE_METADATA,\n InferUIDataParts<DATA_PART_SCHEMAS>\n >[]),\n ) => void;\n\n /** The current value of the input */\n input: Ref<string>;\n\n /** Form submission handler to automatically reset input and append a user message */\n handleSubmit: (\n event?: { preventDefault?: () => void },\n chatRequestOptions?: ChatRequestOptions & {\n files?: FileList | FileUIPart[];\n },\n ) => void;\n\n /**\n * Hook status:\n *\n * - `submitted`: The message has been sent to the API and we're awaiting the start of the response stream.\n * - `streaming`: The response is actively streaming in from the API, receiving chunks of data.\n * - `ready`: The full response has been received and processed; a new user message can be submitted.\n * - `error`: An error occurred during the API request, preventing successful completion.\n */\n status: Ref<ChatStatus>;\n\n addToolResult: ({\n toolCallId,\n result,\n }: {\n toolCallId: string;\n result: any;\n }) => void;\n};\n\nexport function useChat<\n MESSAGE_METADATA = unknown,\n DATA_PART_SCHEMAS extends UIDataPartSchemas = UIDataPartSchemas,\n>({\n chatId,\n initialInput = '',\n onFinish,\n onError,\n generateId = generateIdFunc,\n onToolCall,\n chatStore: chatStoreArg,\n}: UseChatOptions<MESSAGE_METADATA, DATA_PART_SCHEMAS> = {}): UseChatHelpers<\n MESSAGE_METADATA,\n DATA_PART_SCHEMAS\n> {\n const stableChatId = chatId ?? generateId();\n\n const chatStore =\n chatStoreArg == null\n ? createChatStore(\n defaultChatStoreOptions<MESSAGE_METADATA, DATA_PART_SCHEMAS>({\n api: '/api/chat',\n generateId,\n })(),\n )\n : typeof chatStoreArg === 'function'\n ? createChatStore(chatStoreArg())\n : chatStoreArg;\n\n const messages = computed(() => chatStore.getMessages(stableChatId));\n const status = computed(() => chatStore.getStatus(stableChatId));\n const error = computed(() => chatStore.getError(stableChatId));\n\n const append = async (\n message: CreateUIMessage<\n MESSAGE_METADATA,\n InferUIDataParts<DATA_PART_SCHEMAS>\n >,\n { headers, body }: ChatRequestOptions = {},\n ) =>\n chatStore.submitMessage({\n chatId: stableChatId,\n message,\n headers,\n body,\n onError,\n onToolCall,\n onFinish,\n });\n\n const reload = async ({ headers, body }: ChatRequestOptions = {}) =>\n chatStore.resubmitLastUserMessage({\n chatId: stableChatId,\n headers,\n body,\n onError,\n onToolCall,\n onFinish,\n });\n\n const stop = () => {\n chatStore.stopStream({ chatId: stableChatId });\n };\n\n const setMessages = (\n messagesParam:\n | UIMessage<MESSAGE_METADATA, InferUIDataParts<DATA_PART_SCHEMAS>>[]\n | ((\n messages: UIMessage<\n MESSAGE_METADATA,\n InferUIDataParts<DATA_PART_SCHEMAS>\n >[],\n ) => UIMessage<\n MESSAGE_METADATA,\n InferUIDataParts<DATA_PART_SCHEMAS>\n >[]),\n ) => {\n if (typeof messagesParam === 'function') {\n messagesParam = messagesParam(chatStore.getMessages(stableChatId));\n }\n\n chatStore.setMessages({\n id: stableChatId,\n messages: messagesParam,\n });\n };\n\n const input = ref(initialInput);\n\n const handleSubmit = async (\n event?: { preventDefault?: () => void },\n options: ChatRequestOptions & { files?: FileList | FileUIPart[] } = {},\n ) => {\n event?.preventDefault?.();\n\n const inputValue = input.value;\n\n const fileParts = Array.isArray(options?.files)\n ? options.files\n : await convertFileListToFileUIParts(options?.files);\n\n if (!inputValue && fileParts.length === 0) return;\n\n await append(\n {\n id: generateId(),\n role: 'user',\n metadata: undefined,\n parts: [...fileParts, { type: 'text', text: inputValue }],\n },\n {\n headers: options.headers,\n body: options.body,\n },\n );\n\n input.value = '';\n };\n\n const addToolResult = (\n options: Omit<\n Parameters<\n ChatStore<MESSAGE_METADATA, DATA_PART_SCHEMAS>['addToolResult']\n >[0],\n 'chatId'\n >,\n ) => chatStore.addToolResult({ chatId: stableChatId, ...options });\n\n return {\n chatId: stableChatId,\n messages,\n append,\n error,\n reload,\n stop,\n setMessages,\n input,\n handleSubmit,\n status,\n addToolResult,\n };\n}\n","import {\n ChatStore,\n SerialJobExecutor,\n type ActiveResponse,\n type Chat,\n type ChatStatus,\n type ChatStoreOptions,\n type InferUIDataParts,\n type UIDataPartSchemas,\n type UIDataTypes,\n type UIMessage,\n} from 'ai';\nimport { Ref, ref } from 'vue';\n\nclass VueChat<MESSAGE_METADATA, DATA_TYPES extends UIDataTypes>\n implements Chat<MESSAGE_METADATA, DATA_TYPES>\n{\n private messagesRef: Ref<UIMessage<MESSAGE_METADATA, DATA_TYPES>[]>;\n private statusRef = ref<ChatStatus>('ready');\n private errorRef = ref<Error | undefined>(undefined);\n\n activeResponse: ActiveResponse<MESSAGE_METADATA> | undefined = undefined;\n jobExecutor = new SerialJobExecutor();\n\n constructor(messages?: UIMessage<MESSAGE_METADATA, DATA_TYPES>[]) {\n this.messagesRef = ref(messages ?? []) as Ref<\n UIMessage<MESSAGE_METADATA, DATA_TYPES>[]\n >;\n }\n\n get messages(): UIMessage<MESSAGE_METADATA, DATA_TYPES>[] {\n return this.messagesRef.value;\n }\n\n get status(): ChatStatus {\n return this.statusRef.value;\n }\n\n get error(): Error | undefined {\n return this.errorRef.value;\n }\n\n setStatus = (status: ChatStatus) => {\n this.statusRef.value = status;\n };\n\n setError = (error: Error | undefined) => {\n this.errorRef.value = error;\n };\n\n setActiveResponse = (\n activeResponse: ActiveResponse<MESSAGE_METADATA> | undefined,\n ) => {\n this.activeResponse = activeResponse;\n };\n\n setMessages = (messages: UIMessage<MESSAGE_METADATA, DATA_TYPES>[]) => {\n this.messagesRef.value = messages;\n };\n\n pushMessage = (message: UIMessage<MESSAGE_METADATA, DATA_TYPES>) => {\n this.messagesRef.value.push(message);\n };\n\n popMessage = () => {\n this.messagesRef.value.pop();\n };\n\n replaceMessage = (\n index: number,\n message: UIMessage<MESSAGE_METADATA, DATA_TYPES>,\n ) => {\n // message is cloned here because vue's deep reactivity shows unexpected behavior, particularly when updating tool invocation parts\n this.messagesRef.value[index] = { ...message };\n };\n}\n\nexport function createChatStore<\n MESSAGE_METADATA = unknown,\n DATA_PART_SCHEMAS extends UIDataPartSchemas = UIDataPartSchemas,\n>(\n options: ChatStoreOptions<MESSAGE_METADATA, DATA_PART_SCHEMAS>,\n): ChatStore<MESSAGE_METADATA, DATA_PART_SCHEMAS> {\n return new ChatStore<MESSAGE_METADATA, DATA_PART_SCHEMAS>({\n ...options,\n createChat: options =>\n new VueChat<MESSAGE_METADATA, InferUIDataParts<DATA_PART_SCHEMAS>>(\n options.messages,\n ),\n });\n}\n","import type { CompletionRequestOptions, UseCompletionOptions } from 'ai';\nimport { callCompletionApi } from 'ai';\nimport swrv from 'swrv';\nimport type { Ref } from 'vue';\nimport { ref, unref } from 'vue';\n\nexport type { UseCompletionOptions };\n\nexport type UseCompletionHelpers = {\n /** The current completion result */\n completion: Ref<string>;\n /** The error object of the API request */\n error: Ref<undefined | Error>;\n /**\n * Send a new prompt to the API endpoint and update the completion state.\n */\n complete: (\n prompt: string,\n options?: CompletionRequestOptions,\n ) => Promise<string | null | undefined>;\n /**\n * Abort the current API request but keep the generated tokens.\n */\n stop: () => void;\n /**\n * Update the `completion` state locally.\n */\n setCompletion: (completion: string) => void;\n /** The current value of the input */\n input: Ref<string>;\n /**\n * Form submission handler to automatically reset input and append a user message\n * @example\n * ```jsx\n * <form @submit=\"handleSubmit\">\n * <input @change=\"handleInputChange\" v-model=\"input\" />\n * </form>\n * ```\n */\n handleSubmit: (event?: { preventDefault?: () => void }) => void;\n /** Whether the API request is in progress */\n isLoading: Ref<boolean | undefined>;\n};\n\nlet uniqueId = 0;\n\n// @ts-expect-error - some issues with the default export of useSWRV\nconst useSWRV = (swrv.default as (typeof import('swrv'))['default']) || swrv;\nconst store: Record<string, any> = {};\n\nexport function useCompletion({\n api = '/api/completion',\n id,\n initialCompletion = '',\n initialInput = '',\n credentials,\n headers,\n body,\n streamProtocol,\n onFinish,\n onError,\n fetch,\n}: UseCompletionOptions = {}): UseCompletionHelpers {\n // Generate an unique id for the completion if not provided.\n const completionId = id || `completion-${uniqueId++}`;\n\n const key = `${api}|${completionId}`;\n const { data, mutate: originalMutate } = useSWRV<string>(\n key,\n () => store[key] || initialCompletion,\n );\n\n const { data: isLoading, mutate: mutateLoading } = useSWRV<boolean>(\n `${completionId}-loading`,\n null,\n );\n\n isLoading.value ??= false;\n\n // Force the `data` to be `initialCompletion` if it's `undefined`.\n data.value ||= initialCompletion;\n\n const mutate = (data: string) => {\n store[key] = data;\n return originalMutate();\n };\n\n // Because of the `initialData` option, the `data` will never be `undefined`.\n const completion = data as Ref<string>;\n\n const error = ref<undefined | Error>(undefined);\n\n let abortController: AbortController | null = null;\n\n async function triggerRequest(\n prompt: string,\n options?: CompletionRequestOptions,\n ) {\n return callCompletionApi({\n api,\n prompt,\n credentials,\n headers: {\n ...headers,\n ...options?.headers,\n },\n body: {\n ...unref(body),\n ...options?.body,\n },\n streamProtocol,\n setCompletion: mutate,\n setLoading: loading => mutateLoading(() => loading),\n setError: err => {\n error.value = err;\n },\n setAbortController: controller => {\n abortController = controller;\n },\n onFinish,\n onError,\n fetch,\n });\n }\n\n const complete: UseCompletionHelpers['complete'] = async (\n prompt,\n options,\n ) => {\n return triggerRequest(prompt, options);\n };\n\n const stop = () => {\n if (abortController) {\n abortController.abort();\n abortController = null;\n }\n };\n\n const setCompletion = (completion: string) => {\n mutate(completion);\n };\n\n const input = ref(initialInput);\n\n const handleSubmit = (event?: { preventDefault?: () => void }) => {\n event?.preventDefault?.();\n const inputValue = input.value;\n return inputValue ? complete(inputValue) : undefined;\n };\n\n return {\n completion,\n complete,\n error,\n stop,\n setCompletion,\n input,\n handleSubmit,\n isLoading,\n };\n}\n"],"mappings":"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","names":["ref","options","ref","ref","data","completion","_a"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-sdk/vue",
3
- "version": "2.0.0-alpha.5",
3
+ "version": "2.0.0-alpha.6",
4
4
  "license": "Apache-2.0",
5
5
  "sideEffects": false,
6
6
  "main": "./dist/index.js",
@@ -20,8 +20,8 @@
20
20
  ],
21
21
  "dependencies": {
22
22
  "swrv": "^1.0.4",
23
- "ai": "5.0.0-alpha.5",
24
- "@ai-sdk/provider-utils": "3.0.0-alpha.4"
23
+ "ai": "5.0.0-alpha.6",
24
+ "@ai-sdk/provider-utils": "3.0.0-alpha.6"
25
25
  },
26
26
  "devDependencies": {
27
27
  "@testing-library/jest-dom": "^6.6.3",