@ai-sdk/svelte 2.1.12 → 3.0.0-alpha.10
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 +354 -24
- package/dist/chat.svelte.d.ts +4 -73
- package/dist/chat.svelte.d.ts.map +1 -1
- package/dist/chat.svelte.js +23 -243
- package/dist/completion.svelte.d.ts +2 -9
- package/dist/completion.svelte.d.ts.map +1 -1
- package/dist/completion.svelte.js +4 -22
- package/dist/context-provider.d.ts.map +1 -1
- package/dist/context-provider.js +0 -3
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/structured-object-context.svelte.d.ts +1 -1
- package/dist/structured-object-context.svelte.d.ts.map +1 -1
- package/dist/structured-object.svelte.d.ts +8 -7
- package/dist/structured-object.svelte.d.ts.map +1 -1
- package/dist/structured-object.svelte.js +5 -6
- package/dist/tests/structured-object-synchronization.svelte +1 -1
- package/dist/tests/structured-object-synchronization.svelte.d.ts +3 -3
- package/dist/tests/structured-object-synchronization.svelte.d.ts.map +1 -1
- package/dist/utils.svelte.js +1 -1
- package/package.json +12 -9
- package/src/chat.svelte.ts +40 -333
- package/src/completion-context.svelte.ts +1 -1
- package/src/completion.svelte.ts +11 -28
- package/src/context-provider.ts +0 -4
- package/src/index.ts +3 -12
- package/src/structured-object-context.svelte.ts +1 -1
- package/src/structured-object.svelte.ts +21 -12
- package/src/tests/structured-object-synchronization.svelte +1 -1
- package/src/utils.svelte.ts +1 -1
- package/dist/chat-context.svelte.d.ts +0 -14
- package/dist/chat-context.svelte.d.ts.map +0 -1
- package/dist/chat-context.svelte.js +0 -13
- package/dist/completion-context.svelte.d.ts +0 -15
- package/dist/completion-context.svelte.d.ts.map +0 -1
- package/dist/tests/chat-synchronization.svelte +0 -12
- package/dist/tests/chat-synchronization.svelte.d.ts +0 -11
- package/dist/tests/chat-synchronization.svelte.d.ts.map +0 -1
- package/src/chat-context.svelte.ts +0 -23
- package/src/tests/chat-synchronization.svelte +0 -12
package/src/chat.svelte.ts
CHANGED
|
@@ -1,350 +1,57 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
AbstractChat,
|
|
3
|
+
type ChatInit,
|
|
4
|
+
type ChatState,
|
|
5
|
+
type ChatStatus,
|
|
6
|
+
type CreateUIMessage,
|
|
7
|
+
type UIDataPartSchemas,
|
|
8
|
+
type UIDataTypes,
|
|
4
9
|
type UIMessage,
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
} from '@ai-sdk/ui-utils';
|
|
19
|
-
import { isAbortError } from '@ai-sdk/provider-utils';
|
|
20
|
-
import {
|
|
21
|
-
KeyedChatStore,
|
|
22
|
-
getChatContext,
|
|
23
|
-
hasChatContext,
|
|
24
|
-
} from './chat-context.svelte.js';
|
|
25
|
-
import { untrack } from 'svelte';
|
|
26
|
-
|
|
27
|
-
export type ChatOptions = Readonly<
|
|
28
|
-
Omit<UseChatOptions, 'keepLastMessageOnError'> & {
|
|
29
|
-
/**
|
|
30
|
-
* Maximum number of sequential LLM calls (steps), e.g. when you use tool calls.
|
|
31
|
-
* Must be at least 1.
|
|
32
|
-
* A maximum number is required to prevent infinite loops in the case of misconfigured tools.
|
|
33
|
-
* By default, it's set to 1, which means that only a single LLM call is made.
|
|
34
|
-
* @default 1
|
|
35
|
-
*/
|
|
36
|
-
maxSteps?: number;
|
|
37
|
-
}
|
|
38
|
-
>;
|
|
39
|
-
|
|
40
|
-
export type { CreateMessage, Message, UIMessage };
|
|
41
|
-
|
|
42
|
-
export class Chat {
|
|
43
|
-
readonly #options: ChatOptions = {};
|
|
44
|
-
readonly #api = $derived(this.#options.api ?? '/api/chat');
|
|
45
|
-
readonly #generateId = $derived(this.#options.generateId ?? generateId);
|
|
46
|
-
readonly #maxSteps = $derived(this.#options.maxSteps ?? 1);
|
|
47
|
-
readonly #streamProtocol = $derived(this.#options.streamProtocol ?? 'data');
|
|
48
|
-
readonly #keyedStore = $state<KeyedChatStore>()!;
|
|
49
|
-
/**
|
|
50
|
-
* The id of the chat. If not provided through the constructor, a random ID will be generated
|
|
51
|
-
* using the provided `generateId` function, or a built-in function if not provided.
|
|
52
|
-
*/
|
|
53
|
-
readonly id = $derived(this.#options.id ?? this.#generateId());
|
|
54
|
-
readonly #store = $derived(this.#keyedStore.get(this.id));
|
|
55
|
-
#abortController: AbortController | undefined;
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Additional data added on the server via StreamData.
|
|
59
|
-
*
|
|
60
|
-
* This is writable, so you can use it to transform or clear the chat data.
|
|
61
|
-
*/
|
|
62
|
-
get data() {
|
|
63
|
-
return this.#store.data;
|
|
64
|
-
}
|
|
65
|
-
set data(value: JSONValue[] | undefined) {
|
|
66
|
-
this.#store.data = value;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Hook status:
|
|
71
|
-
*
|
|
72
|
-
* - `submitted`: The message has been sent to the API and we're awaiting the start of the response stream.
|
|
73
|
-
* - `streaming`: The response is actively streaming in from the API, receiving chunks of data.
|
|
74
|
-
* - `ready`: The full response has been received and processed; a new user message can be submitted.
|
|
75
|
-
* - `error`: An error occurred during the API request, preventing successful completion.
|
|
76
|
-
*/
|
|
77
|
-
get status() {
|
|
78
|
-
return this.#store.status;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/** The error object of the API request */
|
|
82
|
-
get error() {
|
|
83
|
-
return this.#store.error;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/** The current value of the input. Writable, so it can be bound to form inputs. */
|
|
87
|
-
input = $state<string>()!;
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Current messages in the chat.
|
|
91
|
-
*
|
|
92
|
-
* This is writable, which is useful when you want to edit the messages on the client, and then
|
|
93
|
-
* trigger {@link reload} to regenerate the AI response.
|
|
94
|
-
*/
|
|
95
|
-
get messages(): UIMessage[] {
|
|
96
|
-
return this.#store.messages;
|
|
97
|
-
}
|
|
98
|
-
set messages(value: Message[]) {
|
|
99
|
-
untrack(() => (this.#store.messages = fillMessageParts(value)));
|
|
10
|
+
} from 'ai';
|
|
11
|
+
|
|
12
|
+
export type { CreateUIMessage, UIMessage };
|
|
13
|
+
|
|
14
|
+
export class Chat<
|
|
15
|
+
MESSAGE_METADATA = unknown,
|
|
16
|
+
DATA_PART_SCHEMAS extends UIDataPartSchemas = UIDataPartSchemas,
|
|
17
|
+
> extends AbstractChat<MESSAGE_METADATA, DATA_PART_SCHEMAS> {
|
|
18
|
+
constructor(init: ChatInit<MESSAGE_METADATA, DATA_PART_SCHEMAS>) {
|
|
19
|
+
super({
|
|
20
|
+
...init,
|
|
21
|
+
state: new SvelteChatState(init.messages),
|
|
22
|
+
});
|
|
100
23
|
}
|
|
24
|
+
}
|
|
101
25
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
26
|
+
class SvelteChatState<MESSAGE_METADATA, DATA_TYPES extends UIDataTypes>
|
|
27
|
+
implements ChatState<MESSAGE_METADATA, DATA_TYPES>
|
|
28
|
+
{
|
|
29
|
+
messages: UIMessage<MESSAGE_METADATA, DATA_TYPES>[];
|
|
30
|
+
status = $state<ChatStatus>('ready');
|
|
31
|
+
error = $state<Error | undefined>(undefined);
|
|
108
32
|
|
|
109
|
-
|
|
110
|
-
this.messages =
|
|
111
|
-
this.input = options.initialInput ?? '';
|
|
33
|
+
constructor(messages: UIMessage<MESSAGE_METADATA, DATA_TYPES>[] = []) {
|
|
34
|
+
this.messages = $state(messages);
|
|
112
35
|
}
|
|
113
36
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
* the assistant's response.
|
|
117
|
-
* @param message The message to append
|
|
118
|
-
* @param options Additional options to pass to the API call
|
|
119
|
-
*/
|
|
120
|
-
append = async (
|
|
121
|
-
message: Message | CreateMessage,
|
|
122
|
-
{ data, headers, body, experimental_attachments }: ChatRequestOptions = {},
|
|
123
|
-
) => {
|
|
124
|
-
const attachmentsForRequest = await prepareAttachmentsForRequest(
|
|
125
|
-
experimental_attachments,
|
|
126
|
-
);
|
|
127
|
-
|
|
128
|
-
const messages = this.messages.concat({
|
|
129
|
-
...message,
|
|
130
|
-
id: message.id ?? this.#generateId(),
|
|
131
|
-
createdAt: message.createdAt ?? new Date(),
|
|
132
|
-
experimental_attachments:
|
|
133
|
-
attachmentsForRequest.length > 0 ? attachmentsForRequest : undefined,
|
|
134
|
-
parts: getMessageParts(message),
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
return this.#triggerRequest({ messages, headers, body, data });
|
|
37
|
+
setMessages = (messages: UIMessage<MESSAGE_METADATA, DATA_TYPES>[]) => {
|
|
38
|
+
this.messages = messages;
|
|
138
39
|
};
|
|
139
40
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
* message isn't from the assistant, it will request the API to generate a
|
|
143
|
-
* new response.
|
|
144
|
-
*/
|
|
145
|
-
reload = async ({ data, headers, body }: ChatRequestOptions = {}) => {
|
|
146
|
-
if (this.messages.length === 0) {
|
|
147
|
-
return;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
const lastMessage = this.messages[this.messages.length - 1];
|
|
151
|
-
await this.#triggerRequest({
|
|
152
|
-
messages:
|
|
153
|
-
lastMessage.role === 'assistant'
|
|
154
|
-
? this.messages.slice(0, -1)
|
|
155
|
-
: this.messages,
|
|
156
|
-
headers,
|
|
157
|
-
body,
|
|
158
|
-
data,
|
|
159
|
-
});
|
|
41
|
+
pushMessage = (message: UIMessage<MESSAGE_METADATA, DATA_TYPES>) => {
|
|
42
|
+
this.messages.push(message);
|
|
160
43
|
};
|
|
161
44
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
*/
|
|
165
|
-
stop = () => {
|
|
166
|
-
try {
|
|
167
|
-
this.#abortController?.abort();
|
|
168
|
-
} catch {
|
|
169
|
-
// ignore
|
|
170
|
-
} finally {
|
|
171
|
-
this.#store.status = 'ready';
|
|
172
|
-
this.#abortController = undefined;
|
|
173
|
-
}
|
|
45
|
+
popMessage = () => {
|
|
46
|
+
this.messages.pop();
|
|
174
47
|
};
|
|
175
48
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
options: ChatRequestOptions = {},
|
|
49
|
+
replaceMessage = (
|
|
50
|
+
index: number,
|
|
51
|
+
message: UIMessage<MESSAGE_METADATA, DATA_TYPES>,
|
|
180
52
|
) => {
|
|
181
|
-
|
|
182
|
-
if (!this.input && !options.allowEmptySubmit) return;
|
|
183
|
-
|
|
184
|
-
const attachmentsForRequest = await prepareAttachmentsForRequest(
|
|
185
|
-
options.experimental_attachments,
|
|
186
|
-
);
|
|
187
|
-
|
|
188
|
-
const messages = this.messages.concat({
|
|
189
|
-
id: this.#generateId(),
|
|
190
|
-
createdAt: new Date(),
|
|
191
|
-
role: 'user',
|
|
192
|
-
content: this.input,
|
|
193
|
-
experimental_attachments:
|
|
194
|
-
attachmentsForRequest.length > 0 ? attachmentsForRequest : undefined,
|
|
195
|
-
parts: [{ type: 'text', text: this.input }],
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
const chatRequest: ChatRequest = {
|
|
199
|
-
messages,
|
|
200
|
-
headers: options.headers,
|
|
201
|
-
body: options.body,
|
|
202
|
-
data: options.data,
|
|
203
|
-
};
|
|
204
|
-
|
|
205
|
-
const request = this.#triggerRequest(chatRequest);
|
|
206
|
-
this.input = '';
|
|
207
|
-
await request;
|
|
53
|
+
this.messages[index] = message;
|
|
208
54
|
};
|
|
209
55
|
|
|
210
|
-
|
|
211
|
-
toolCallId,
|
|
212
|
-
result,
|
|
213
|
-
}: {
|
|
214
|
-
toolCallId: string;
|
|
215
|
-
result: unknown;
|
|
216
|
-
}) => {
|
|
217
|
-
updateToolCallResult({
|
|
218
|
-
messages: this.messages,
|
|
219
|
-
toolCallId,
|
|
220
|
-
toolResult: result,
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
// when the request is ongoing, the auto-submit will be triggered after the request is finished
|
|
224
|
-
if (
|
|
225
|
-
this.#store.status === 'submitted' ||
|
|
226
|
-
this.#store.status === 'streaming'
|
|
227
|
-
) {
|
|
228
|
-
return;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
const lastMessage = this.messages[this.messages.length - 1];
|
|
232
|
-
if (isAssistantMessageWithCompletedToolCalls(lastMessage)) {
|
|
233
|
-
await this.#triggerRequest({ messages: this.messages });
|
|
234
|
-
}
|
|
235
|
-
};
|
|
236
|
-
|
|
237
|
-
#triggerRequest = async (chatRequest: ChatRequest) => {
|
|
238
|
-
this.#store.status = 'submitted';
|
|
239
|
-
this.#store.error = undefined;
|
|
240
|
-
|
|
241
|
-
const messages = fillMessageParts(chatRequest.messages);
|
|
242
|
-
const messageCount = messages.length;
|
|
243
|
-
const maxStep = extractMaxToolInvocationStep(
|
|
244
|
-
messages[messages.length - 1]?.toolInvocations,
|
|
245
|
-
);
|
|
246
|
-
|
|
247
|
-
try {
|
|
248
|
-
const abortController = new AbortController();
|
|
249
|
-
this.#abortController = abortController;
|
|
250
|
-
|
|
251
|
-
// Optimistically update messages
|
|
252
|
-
this.messages = messages;
|
|
253
|
-
|
|
254
|
-
const constructedMessagesPayload = this.#options.sendExtraMessageFields
|
|
255
|
-
? messages
|
|
256
|
-
: messages.map(
|
|
257
|
-
({
|
|
258
|
-
role,
|
|
259
|
-
content,
|
|
260
|
-
experimental_attachments,
|
|
261
|
-
data,
|
|
262
|
-
annotations,
|
|
263
|
-
toolInvocations,
|
|
264
|
-
parts,
|
|
265
|
-
}) => ({
|
|
266
|
-
role,
|
|
267
|
-
content,
|
|
268
|
-
...(experimental_attachments !== undefined && {
|
|
269
|
-
experimental_attachments,
|
|
270
|
-
}),
|
|
271
|
-
...(data !== undefined && { data }),
|
|
272
|
-
...(annotations !== undefined && { annotations }),
|
|
273
|
-
...(toolInvocations !== undefined && { toolInvocations }),
|
|
274
|
-
...(parts !== undefined && { parts }),
|
|
275
|
-
}),
|
|
276
|
-
);
|
|
277
|
-
|
|
278
|
-
const existingData = this.data ?? [];
|
|
279
|
-
await callChatApi({
|
|
280
|
-
api: this.#api,
|
|
281
|
-
body: {
|
|
282
|
-
id: this.id,
|
|
283
|
-
messages: constructedMessagesPayload,
|
|
284
|
-
data: chatRequest.data,
|
|
285
|
-
...$state.snapshot(this.#options.body),
|
|
286
|
-
...chatRequest.body,
|
|
287
|
-
},
|
|
288
|
-
streamProtocol: this.#streamProtocol,
|
|
289
|
-
credentials: this.#options.credentials,
|
|
290
|
-
headers: {
|
|
291
|
-
...this.#options.headers,
|
|
292
|
-
...chatRequest.headers,
|
|
293
|
-
},
|
|
294
|
-
abortController: () => abortController,
|
|
295
|
-
restoreMessagesOnFailure: () => {},
|
|
296
|
-
onResponse: this.#options.onResponse,
|
|
297
|
-
onUpdate: ({ message, data, replaceLastMessage }) => {
|
|
298
|
-
this.#store.status = 'streaming';
|
|
299
|
-
|
|
300
|
-
this.messages = messages;
|
|
301
|
-
if (replaceLastMessage) {
|
|
302
|
-
this.messages[this.messages.length - 1] = message;
|
|
303
|
-
} else {
|
|
304
|
-
this.messages.push(message);
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
if (data?.length) {
|
|
308
|
-
this.data = existingData;
|
|
309
|
-
this.data.push(...data);
|
|
310
|
-
}
|
|
311
|
-
},
|
|
312
|
-
onToolCall: this.#options.onToolCall,
|
|
313
|
-
onFinish: this.#options.onFinish,
|
|
314
|
-
generateId: this.#generateId,
|
|
315
|
-
fetch: this.#options.fetch,
|
|
316
|
-
// callChatApi calls structuredClone on the message
|
|
317
|
-
lastMessage: $state.snapshot(this.messages[this.messages.length - 1]),
|
|
318
|
-
});
|
|
319
|
-
|
|
320
|
-
this.#abortController = undefined;
|
|
321
|
-
this.#store.status = 'ready';
|
|
322
|
-
} catch (error) {
|
|
323
|
-
if (isAbortError(error)) {
|
|
324
|
-
return;
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
const coalescedError =
|
|
328
|
-
error instanceof Error ? error : new Error(String(error));
|
|
329
|
-
if (this.#options.onError) {
|
|
330
|
-
this.#options.onError(coalescedError);
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
this.#store.status = 'error';
|
|
334
|
-
this.#store.error = coalescedError;
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
// auto-submit when all tool calls in the last assistant message have results
|
|
338
|
-
// and assistant has not answered yet
|
|
339
|
-
if (
|
|
340
|
-
shouldResubmitMessages({
|
|
341
|
-
originalMaxToolInvocationStep: maxStep,
|
|
342
|
-
originalMessageCount: messageCount,
|
|
343
|
-
maxSteps: this.#maxSteps,
|
|
344
|
-
messages: this.messages,
|
|
345
|
-
})
|
|
346
|
-
) {
|
|
347
|
-
await this.#triggerRequest({ messages: this.messages });
|
|
348
|
-
}
|
|
349
|
-
};
|
|
56
|
+
snapshot = <T>(thing: T): T => $state.snapshot(thing) as T;
|
|
350
57
|
}
|
package/src/completion.svelte.ts
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
|
+
callCompletionApi,
|
|
2
3
|
generateId,
|
|
4
|
+
type CompletionRequestOptions,
|
|
3
5
|
type UseCompletionOptions,
|
|
4
|
-
|
|
5
|
-
type RequestOptions,
|
|
6
|
-
callCompletionApi,
|
|
7
|
-
} from '@ai-sdk/ui-utils';
|
|
6
|
+
} from 'ai';
|
|
8
7
|
import {
|
|
9
8
|
KeyedCompletionStore,
|
|
10
9
|
getCompletionContext,
|
|
@@ -30,18 +29,6 @@ export class Completion {
|
|
|
30
29
|
this.#store.completions.set(this.#id, value);
|
|
31
30
|
}
|
|
32
31
|
|
|
33
|
-
/**
|
|
34
|
-
* Additional data added on the server via StreamData.
|
|
35
|
-
*
|
|
36
|
-
* This is writable, so you can use it to transform or clear the chat data.
|
|
37
|
-
*/
|
|
38
|
-
get data() {
|
|
39
|
-
return this.#store.data;
|
|
40
|
-
}
|
|
41
|
-
set data(value: JSONValue[]) {
|
|
42
|
-
this.#store.data = value;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
32
|
/** The error object of the API request */
|
|
46
33
|
get error() {
|
|
47
34
|
return this.#store.error;
|
|
@@ -58,12 +45,9 @@ export class Completion {
|
|
|
58
45
|
}
|
|
59
46
|
|
|
60
47
|
constructor(options: CompletionOptions = {}) {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
this.#keyedStore = new KeyedCompletionStore();
|
|
65
|
-
}
|
|
66
|
-
|
|
48
|
+
this.#keyedStore = hasCompletionContext()
|
|
49
|
+
? getCompletionContext()
|
|
50
|
+
: new KeyedCompletionStore();
|
|
67
51
|
this.#options = options;
|
|
68
52
|
this.completion = options.initialCompletion ?? '';
|
|
69
53
|
this.input = options.initialInput ?? '';
|
|
@@ -86,7 +70,7 @@ export class Completion {
|
|
|
86
70
|
/**
|
|
87
71
|
* Send a new prompt to the API endpoint and update the completion state.
|
|
88
72
|
*/
|
|
89
|
-
complete = async (prompt: string, options?:
|
|
73
|
+
complete = async (prompt: string, options?: CompletionRequestOptions) =>
|
|
90
74
|
this.#triggerRequest(prompt, options);
|
|
91
75
|
|
|
92
76
|
/** Form submission handler to automatically reset input and call the completion API */
|
|
@@ -97,7 +81,10 @@ export class Completion {
|
|
|
97
81
|
}
|
|
98
82
|
};
|
|
99
83
|
|
|
100
|
-
#triggerRequest = async (
|
|
84
|
+
#triggerRequest = async (
|
|
85
|
+
prompt: string,
|
|
86
|
+
options?: CompletionRequestOptions,
|
|
87
|
+
) => {
|
|
101
88
|
return callCompletionApi({
|
|
102
89
|
api: this.#api,
|
|
103
90
|
prompt,
|
|
@@ -113,9 +100,6 @@ export class Completion {
|
|
|
113
100
|
setCompletion: completion => {
|
|
114
101
|
this.completion = completion;
|
|
115
102
|
},
|
|
116
|
-
onData: data => {
|
|
117
|
-
this.data.push(...data);
|
|
118
|
-
},
|
|
119
103
|
setLoading: loading => {
|
|
120
104
|
this.#store.loading = loading;
|
|
121
105
|
},
|
|
@@ -125,7 +109,6 @@ export class Completion {
|
|
|
125
109
|
setAbortController: abortController => {
|
|
126
110
|
this.#abortController = abortController ?? undefined;
|
|
127
111
|
},
|
|
128
|
-
onResponse: this.#options.onResponse,
|
|
129
112
|
onFinish: this.#options.onFinish,
|
|
130
113
|
onError: this.#options.onError,
|
|
131
114
|
});
|
package/src/context-provider.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { KeyedChatStore, setChatContext } from './chat-context.svelte.js';
|
|
2
1
|
import {
|
|
3
2
|
KeyedCompletionStore,
|
|
4
3
|
setCompletionContext,
|
|
@@ -9,9 +8,6 @@ import {
|
|
|
9
8
|
} from './structured-object-context.svelte.js';
|
|
10
9
|
|
|
11
10
|
export function createAIContext() {
|
|
12
|
-
const chatStore = new KeyedChatStore();
|
|
13
|
-
setChatContext(chatStore);
|
|
14
|
-
|
|
15
11
|
const completionStore = new KeyedCompletionStore();
|
|
16
12
|
setCompletionContext(completionStore);
|
|
17
13
|
|
package/src/index.ts
CHANGED
|
@@ -1,16 +1,7 @@
|
|
|
1
|
-
export {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
type CreateMessage,
|
|
5
|
-
type Message,
|
|
6
|
-
type UIMessage,
|
|
7
|
-
} from './chat.svelte.js';
|
|
8
|
-
|
|
1
|
+
export { Chat, type CreateUIMessage, type UIMessage } from './chat.svelte.js';
|
|
2
|
+
export { Completion, type CompletionOptions } from './completion.svelte.js';
|
|
3
|
+
export { createAIContext } from './context-provider.js';
|
|
9
4
|
export {
|
|
10
5
|
StructuredObject as Experimental_StructuredObject,
|
|
11
6
|
type Experimental_StructuredObjectOptions,
|
|
12
7
|
} from './structured-object.svelte.js';
|
|
13
|
-
|
|
14
|
-
export { Completion, type CompletionOptions } from './completion.svelte.js';
|
|
15
|
-
|
|
16
|
-
export { createAIContext } from './context-provider.js';
|
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
isAbortError,
|
|
4
4
|
safeValidateTypes,
|
|
5
5
|
type FetchFunction,
|
|
6
|
+
type InferSchema,
|
|
6
7
|
} from '@ai-sdk/provider-utils';
|
|
7
8
|
import {
|
|
8
9
|
asSchema,
|
|
@@ -10,8 +11,9 @@ import {
|
|
|
10
11
|
parsePartialJson,
|
|
11
12
|
type DeepPartial,
|
|
12
13
|
type Schema,
|
|
13
|
-
} from '
|
|
14
|
-
import
|
|
14
|
+
} from 'ai';
|
|
15
|
+
import type * as z3 from 'zod/v3';
|
|
16
|
+
import type * as z4 from 'zod/v4/core';
|
|
15
17
|
import {
|
|
16
18
|
getStructuredObjectContext,
|
|
17
19
|
hasStructuredObjectContext,
|
|
@@ -19,7 +21,10 @@ import {
|
|
|
19
21
|
type StructuredObjectStore,
|
|
20
22
|
} from './structured-object-context.svelte.js';
|
|
21
23
|
|
|
22
|
-
export type Experimental_StructuredObjectOptions<
|
|
24
|
+
export type Experimental_StructuredObjectOptions<
|
|
25
|
+
SCHEMA extends z3.Schema | z4.$ZodType | Schema,
|
|
26
|
+
RESULT = InferSchema<SCHEMA>,
|
|
27
|
+
> = {
|
|
23
28
|
/**
|
|
24
29
|
* The API endpoint. It should stream JSON that matches the schema as chunked text.
|
|
25
30
|
*/
|
|
@@ -28,7 +33,7 @@ export type Experimental_StructuredObjectOptions<RESULT> = {
|
|
|
28
33
|
/**
|
|
29
34
|
* A Zod schema that defines the shape of the complete object.
|
|
30
35
|
*/
|
|
31
|
-
schema:
|
|
36
|
+
schema: SCHEMA;
|
|
32
37
|
|
|
33
38
|
/**
|
|
34
39
|
* An unique identifier. If not provided, a random one will be
|
|
@@ -82,9 +87,13 @@ export type Experimental_StructuredObjectOptions<RESULT> = {
|
|
|
82
87
|
credentials?: RequestCredentials;
|
|
83
88
|
};
|
|
84
89
|
|
|
85
|
-
export class StructuredObject<
|
|
86
|
-
|
|
87
|
-
|
|
90
|
+
export class StructuredObject<
|
|
91
|
+
SCHEMA extends z3.Schema | z4.$ZodType | Schema,
|
|
92
|
+
RESULT = InferSchema<SCHEMA>,
|
|
93
|
+
INPUT = unknown,
|
|
94
|
+
> {
|
|
95
|
+
#options: Experimental_StructuredObjectOptions<SCHEMA, RESULT> =
|
|
96
|
+
{} as Experimental_StructuredObjectOptions<SCHEMA, RESULT>;
|
|
88
97
|
readonly #id = $derived(this.#options.id ?? generateId());
|
|
89
98
|
readonly #keyedStore = $state<KeyedStructuredObjectStore>()!;
|
|
90
99
|
readonly #store = $derived(
|
|
@@ -114,7 +123,7 @@ export class StructuredObject<RESULT, INPUT = unknown> {
|
|
|
114
123
|
return this.#store.loading;
|
|
115
124
|
}
|
|
116
125
|
|
|
117
|
-
constructor(options: Experimental_StructuredObjectOptions<RESULT>) {
|
|
126
|
+
constructor(options: Experimental_StructuredObjectOptions<SCHEMA, RESULT>) {
|
|
118
127
|
if (hasStructuredObjectContext()) {
|
|
119
128
|
this.#keyedStore = getStructuredObjectContext();
|
|
120
129
|
} else {
|
|
@@ -177,13 +186,13 @@ export class StructuredObject<RESULT, INPUT = unknown> {
|
|
|
177
186
|
|
|
178
187
|
await response.body.pipeThrough(new TextDecoderStream()).pipeTo(
|
|
179
188
|
new WritableStream<string>({
|
|
180
|
-
write: chunk => {
|
|
189
|
+
write: async chunk => {
|
|
181
190
|
if (abortController?.signal.aborted) {
|
|
182
191
|
throw new DOMException('Stream aborted', 'AbortError');
|
|
183
192
|
}
|
|
184
193
|
accumulatedText += chunk;
|
|
185
194
|
|
|
186
|
-
const { value } = parsePartialJson(accumulatedText);
|
|
195
|
+
const { value } = await parsePartialJson(accumulatedText);
|
|
187
196
|
const currentObject = value as DeepPartial<RESULT>;
|
|
188
197
|
|
|
189
198
|
if (!isDeepEqualData(latestObject, currentObject)) {
|
|
@@ -193,12 +202,12 @@ export class StructuredObject<RESULT, INPUT = unknown> {
|
|
|
193
202
|
}
|
|
194
203
|
},
|
|
195
204
|
|
|
196
|
-
close: () => {
|
|
205
|
+
close: async () => {
|
|
197
206
|
this.#store.loading = false;
|
|
198
207
|
this.#abortController = undefined;
|
|
199
208
|
|
|
200
209
|
if (this.#options.onFinish != null) {
|
|
201
|
-
const validationResult = safeValidateTypes({
|
|
210
|
+
const validationResult = await safeValidateTypes({
|
|
202
211
|
value: latestObject,
|
|
203
212
|
schema: asSchema(this.#options.schema),
|
|
204
213
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script lang="ts" generics="RESULT">
|
|
2
2
|
import { createAIContext } from '../context-provider.js';
|
|
3
3
|
import { StructuredObject } from '../structured-object.svelte.js';
|
|
4
|
-
import type { Schema } from '
|
|
4
|
+
import type { Schema } from 'ai';
|
|
5
5
|
import type { z } from 'zod';
|
|
6
6
|
|
|
7
7
|
let {
|
package/src/utils.svelte.ts
CHANGED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import type { JSONValue, UIMessage } from '@ai-sdk/ui-utils';
|
|
2
|
-
import { KeyedStore } from './utils.svelte.js';
|
|
3
|
-
declare class ChatStore {
|
|
4
|
-
messages: UIMessage[];
|
|
5
|
-
data: JSONValue[] | undefined;
|
|
6
|
-
status: "submitted" | "streaming" | "ready" | "error";
|
|
7
|
-
error: Error | undefined;
|
|
8
|
-
}
|
|
9
|
-
export declare class KeyedChatStore extends KeyedStore<ChatStore> {
|
|
10
|
-
constructor(value?: Iterable<readonly [string, ChatStore]> | null | undefined);
|
|
11
|
-
}
|
|
12
|
-
export declare const hasChatContext: () => boolean, getChatContext: () => KeyedChatStore, setChatContext: (value: KeyedChatStore) => KeyedChatStore;
|
|
13
|
-
export {};
|
|
14
|
-
//# sourceMappingURL=chat-context.svelte.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"chat-context.svelte.d.ts","sourceRoot":"","sources":["../src/chat-context.svelte.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7D,OAAO,EAAiB,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAE9D,cAAM,SAAS;IACb,QAAQ,cAA2B;IACnC,IAAI,0BAAyB;IAC7B,MAAM,gDAAkE;IACxE,KAAK,oBAAmB;CACzB;AAED,qBAAa,cAAe,SAAQ,UAAU,CAAC,SAAS,CAAC;gBAErD,KAAK,CAAC,EAAE,QAAQ,CAAC,SAAS,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,GAAG,IAAI,GAAG,SAAS;CAIpE;AAED,eAAO,MACO,cAAc,iBACd,cAAc,wBACd,cAAc,2CACa,CAAC"}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { createContext, KeyedStore } from './utils.svelte.js';
|
|
2
|
-
class ChatStore {
|
|
3
|
-
messages = $state([]);
|
|
4
|
-
data = $state();
|
|
5
|
-
status = $state('ready');
|
|
6
|
-
error = $state();
|
|
7
|
-
}
|
|
8
|
-
export class KeyedChatStore extends KeyedStore {
|
|
9
|
-
constructor(value) {
|
|
10
|
-
super(ChatStore, value);
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
export const { hasContext: hasChatContext, getContext: getChatContext, setContext: setChatContext, } = createContext('Chat');
|