@tanstack/ai-svelte 0.13.14 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/create-audio-recorder.svelte.d.ts +37 -0
- package/dist/create-audio-recorder.svelte.js +36 -0
- package/dist/create-chat.svelte.d.ts +34 -0
- package/dist/create-chat.svelte.js +248 -0
- package/dist/create-generate-audio.svelte.d.ts +85 -0
- package/dist/create-generate-audio.svelte.js +57 -0
- package/dist/create-generate-image.svelte.d.ts +94 -0
- package/dist/create-generate-image.svelte.js +66 -0
- package/dist/create-generate-speech.svelte.d.ts +85 -0
- package/dist/create-generate-speech.svelte.js +57 -0
- package/dist/create-generate-video.svelte.d.ts +99 -0
- package/dist/create-generate-video.svelte.js +149 -0
- package/dist/create-generation.svelte.d.ts +96 -0
- package/dist/create-generation.svelte.js +136 -0
- package/dist/create-summarize.svelte.d.ts +89 -0
- package/dist/create-summarize.svelte.js +61 -0
- package/dist/create-transcription.svelte.d.ts +94 -0
- package/dist/create-transcription.svelte.js +66 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +12 -0
- package/dist/types.d.ts +157 -0
- package/dist/types.js +3 -0
- package/package.json +4 -4
- package/src/create-audio-recorder.svelte.ts +93 -0
- package/src/create-generate-audio.svelte.ts +5 -8
- package/src/create-generate-image.svelte.ts +5 -8
- package/src/create-generate-speech.svelte.ts +7 -7
- package/src/create-generate-video.svelte.ts +17 -9
- package/src/create-generation.svelte.ts +19 -6
- package/src/create-summarize.svelte.ts +5 -8
- package/src/create-transcription.svelte.ts +5 -8
- package/src/index.ts +6 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { AudioRecorderOptions, AudioRecording, InferAudioRecordingOutput } from '@tanstack/ai-client';
|
|
2
|
+
export type CreateAudioRecorderOptions<TOnComplete> = AudioRecorderOptions & {
|
|
3
|
+
/**
|
|
4
|
+
* Optional transform applied to the recording when `stop()` resolves. Its
|
|
5
|
+
* (awaited) return value becomes `recording` and the resolved value of
|
|
6
|
+
* `stop()`. Return nothing to keep the raw `AudioRecording`.
|
|
7
|
+
*/
|
|
8
|
+
onComplete?: TOnComplete;
|
|
9
|
+
};
|
|
10
|
+
export interface CreateAudioRecorderReturn<TOutput> {
|
|
11
|
+
/** Reactive: latest recording (transformed if `onComplete` provided), or null. */
|
|
12
|
+
readonly recording: TOutput | null;
|
|
13
|
+
/** Reactive: true while actively capturing audio. */
|
|
14
|
+
readonly isRecording: boolean;
|
|
15
|
+
/** Whether the browser supports recording. */
|
|
16
|
+
readonly isSupported: boolean;
|
|
17
|
+
start: () => Promise<void>;
|
|
18
|
+
/** Stop and resolve with the completed recording (transformed if `onComplete` provided). */
|
|
19
|
+
stop: () => Promise<TOutput>;
|
|
20
|
+
/**
|
|
21
|
+
* Discard the in-progress recording and release the mic. Svelte 5 runes
|
|
22
|
+
* can't register automatic teardown here (matching `createChat`), so call
|
|
23
|
+
* this from your component's cleanup if a recording may still be active.
|
|
24
|
+
*/
|
|
25
|
+
cancel: () => void;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Svelte 5 factory for recording an audio message. The resolved recording
|
|
29
|
+
* carries `.part` (for `createChat.sendMessage`) and `.base64` (for the
|
|
30
|
+
* generation factories).
|
|
31
|
+
*
|
|
32
|
+
* Errors are delivered via `onError`. `start()` and `stop()` also reject on
|
|
33
|
+
* failure (and `stop()` rejects with `Recording cancelled` if `cancel()` runs
|
|
34
|
+
* while a stop is in flight) — handle one channel, not both.
|
|
35
|
+
*/
|
|
36
|
+
export declare function createAudioRecorder<TOnComplete extends (recording: AudioRecording) => unknown>(options: CreateAudioRecorderOptions<TOnComplete>): CreateAudioRecorderReturn<InferAudioRecordingOutput<TOnComplete>>;
|
|
37
|
+
export declare function createAudioRecorder(options?: CreateAudioRecorderOptions<undefined>): CreateAudioRecorderReturn<AudioRecording>;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { AudioRecorder } from '@tanstack/ai-client';
|
|
2
|
+
export function createAudioRecorder(options = {}) {
|
|
3
|
+
const recorder = new AudioRecorder({
|
|
4
|
+
...(options.audio !== undefined && { audio: options.audio }),
|
|
5
|
+
...(options.mimeType !== undefined && { mimeType: options.mimeType }),
|
|
6
|
+
...(options.onError !== undefined && { onError: options.onError }),
|
|
7
|
+
});
|
|
8
|
+
let isRecording = $state(false);
|
|
9
|
+
let recording = $state(null);
|
|
10
|
+
recorder.subscribe((state) => {
|
|
11
|
+
isRecording = state === 'recording';
|
|
12
|
+
});
|
|
13
|
+
const stop = async () => {
|
|
14
|
+
const rawRecording = await recorder.stop();
|
|
15
|
+
const transformed = await options.onComplete?.(rawRecording);
|
|
16
|
+
// Only `undefined` (returning nothing) keeps the raw recording; a returned
|
|
17
|
+
// null is a real value, matching the inferred output type.
|
|
18
|
+
const output = transformed === undefined ? rawRecording : transformed;
|
|
19
|
+
recording = output;
|
|
20
|
+
return output;
|
|
21
|
+
};
|
|
22
|
+
return {
|
|
23
|
+
get recording() {
|
|
24
|
+
return recording;
|
|
25
|
+
},
|
|
26
|
+
get isRecording() {
|
|
27
|
+
return isRecording;
|
|
28
|
+
},
|
|
29
|
+
get isSupported() {
|
|
30
|
+
return AudioRecorder.isSupported();
|
|
31
|
+
},
|
|
32
|
+
start: () => recorder.start(),
|
|
33
|
+
stop,
|
|
34
|
+
cancel: () => recorder.cancel(),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { InferredClientContext } from '@tanstack/ai-client';
|
|
2
|
+
import type { AnyClientTool, SchemaInput } from '@tanstack/ai';
|
|
3
|
+
import type { CreateChatOptions, CreateChatReturn } from './types';
|
|
4
|
+
/**
|
|
5
|
+
* Creates a reactive chat instance for Svelte 5.
|
|
6
|
+
*
|
|
7
|
+
* This function wraps the ChatClient from @tanstack/ai-client and exposes
|
|
8
|
+
* reactive state using Svelte 5 runes. The returned object has reactive
|
|
9
|
+
* getters that automatically update when state changes.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```svelte
|
|
13
|
+
* <script>
|
|
14
|
+
* import { createChat, fetchServerSentEvents } from '@tanstack/ai-svelte'
|
|
15
|
+
*
|
|
16
|
+
* const chat = createChat({
|
|
17
|
+
* connection: fetchServerSentEvents('/api/chat'),
|
|
18
|
+
* })
|
|
19
|
+
* </script>
|
|
20
|
+
*
|
|
21
|
+
* <div>
|
|
22
|
+
* {#each chat.messages as message}
|
|
23
|
+
* <div>{message.role}: {message.parts[0].content}</div>
|
|
24
|
+
* {/each}
|
|
25
|
+
*
|
|
26
|
+
* {#if chat.isLoading}
|
|
27
|
+
* <button onclick={chat.stop}>Stop</button>
|
|
28
|
+
* {/if}
|
|
29
|
+
*
|
|
30
|
+
* <button onclick={() => chat.sendMessage('Hello!')}>Send</button>
|
|
31
|
+
* </div>
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export declare function createChat<TTools extends ReadonlyArray<AnyClientTool> = any, TSchema extends SchemaInput | undefined = undefined, TContext = InferredClientContext<TTools>>(options: CreateChatOptions<TTools, TSchema, TContext>): CreateChatReturn<TTools, TSchema, TContext>;
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import { ChatClient } from '@tanstack/ai-client';
|
|
2
|
+
import { createChatDevtoolsBridge } from '@tanstack/ai-client/devtools';
|
|
3
|
+
/**
|
|
4
|
+
* Creates a reactive chat instance for Svelte 5.
|
|
5
|
+
*
|
|
6
|
+
* This function wraps the ChatClient from @tanstack/ai-client and exposes
|
|
7
|
+
* reactive state using Svelte 5 runes. The returned object has reactive
|
|
8
|
+
* getters that automatically update when state changes.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```svelte
|
|
12
|
+
* <script>
|
|
13
|
+
* import { createChat, fetchServerSentEvents } from '@tanstack/ai-svelte'
|
|
14
|
+
*
|
|
15
|
+
* const chat = createChat({
|
|
16
|
+
* connection: fetchServerSentEvents('/api/chat'),
|
|
17
|
+
* })
|
|
18
|
+
* </script>
|
|
19
|
+
*
|
|
20
|
+
* <div>
|
|
21
|
+
* {#each chat.messages as message}
|
|
22
|
+
* <div>{message.role}: {message.parts[0].content}</div>
|
|
23
|
+
* {/each}
|
|
24
|
+
*
|
|
25
|
+
* {#if chat.isLoading}
|
|
26
|
+
* <button onclick={chat.stop}>Stop</button>
|
|
27
|
+
* {/if}
|
|
28
|
+
*
|
|
29
|
+
* <button onclick={() => chat.sendMessage('Hello!')}>Send</button>
|
|
30
|
+
* </div>
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export function createChat(options) {
|
|
34
|
+
// Generate a unique ID for this chat instance
|
|
35
|
+
const clientId = options.id ||
|
|
36
|
+
`chat-${Date.now()}-${Math.random().toString(36).substring(7)}`;
|
|
37
|
+
// Create reactive state using Svelte 5 runes
|
|
38
|
+
let messages = $state(options.initialMessages || []);
|
|
39
|
+
let isLoading = $state(false);
|
|
40
|
+
let error = $state(undefined);
|
|
41
|
+
let status = $state('ready');
|
|
42
|
+
let isSubscribed = $state(false);
|
|
43
|
+
let connectionStatus = $state('disconnected');
|
|
44
|
+
let sessionGenerating = $state(false);
|
|
45
|
+
// Create ChatClient instance.
|
|
46
|
+
// Note: Svelte's createChat runs once per instance and `options` is captured
|
|
47
|
+
// by reference. Callbacks are therefore frozen to whatever the caller passed
|
|
48
|
+
// at creation — to swap them dynamically, mutate the options object
|
|
49
|
+
// in-place or call `client.updateOptions(...)` imperatively.
|
|
50
|
+
// Optional fields use conditional spread because the target
|
|
51
|
+
// `ChatClientOptions` declares them as `field?: T` (absent vs. present)
|
|
52
|
+
// rather than `field?: T | undefined`. Under `exactOptionalPropertyTypes`,
|
|
53
|
+
// passing an explicit `undefined` for an absent-only optional is a type
|
|
54
|
+
// error, so we omit the key when the caller's value is undefined.
|
|
55
|
+
const transport = options.connection
|
|
56
|
+
? { connection: options.connection }
|
|
57
|
+
: { fetcher: options.fetcher };
|
|
58
|
+
const client = new ChatClient({
|
|
59
|
+
devtoolsBridgeFactory: createChatDevtoolsBridge,
|
|
60
|
+
...transport,
|
|
61
|
+
id: clientId,
|
|
62
|
+
...(options.initialMessages !== undefined && {
|
|
63
|
+
initialMessages: options.initialMessages,
|
|
64
|
+
}),
|
|
65
|
+
...(options.persistence !== undefined && {
|
|
66
|
+
persistence: options.persistence,
|
|
67
|
+
}),
|
|
68
|
+
...(options.body !== undefined && { body: options.body }),
|
|
69
|
+
...(options.threadId !== undefined && { threadId: options.threadId }),
|
|
70
|
+
...(options.forwardedProps !== undefined && {
|
|
71
|
+
forwardedProps: options.forwardedProps,
|
|
72
|
+
}),
|
|
73
|
+
...(options.context !== undefined && { context: options.context }),
|
|
74
|
+
devtools: {
|
|
75
|
+
...options.devtools,
|
|
76
|
+
framework: 'svelte',
|
|
77
|
+
hookName: 'useChat',
|
|
78
|
+
outputKind: options.outputSchema ? 'structured' : 'chat',
|
|
79
|
+
},
|
|
80
|
+
...(options.onResponse !== undefined && { onResponse: options.onResponse }),
|
|
81
|
+
onChunk: (chunk) => {
|
|
82
|
+
options.onChunk?.(chunk);
|
|
83
|
+
},
|
|
84
|
+
onFinish: (message) => {
|
|
85
|
+
options.onFinish?.(message);
|
|
86
|
+
},
|
|
87
|
+
onError: (err) => {
|
|
88
|
+
options.onError?.(err);
|
|
89
|
+
},
|
|
90
|
+
tools: options.tools,
|
|
91
|
+
...(options.onCustomEvent !== undefined && {
|
|
92
|
+
onCustomEvent: options.onCustomEvent,
|
|
93
|
+
}),
|
|
94
|
+
...(options.streamProcessor !== undefined && {
|
|
95
|
+
streamProcessor: options.streamProcessor,
|
|
96
|
+
}),
|
|
97
|
+
onMessagesChange: (newMessages) => {
|
|
98
|
+
messages = newMessages;
|
|
99
|
+
},
|
|
100
|
+
onLoadingChange: (newIsLoading) => {
|
|
101
|
+
isLoading = newIsLoading;
|
|
102
|
+
},
|
|
103
|
+
onStatusChange: (newStatus) => {
|
|
104
|
+
status = newStatus;
|
|
105
|
+
},
|
|
106
|
+
onErrorChange: (newError) => {
|
|
107
|
+
error = newError;
|
|
108
|
+
},
|
|
109
|
+
onSubscriptionChange: (nextIsSubscribed) => {
|
|
110
|
+
isSubscribed = nextIsSubscribed;
|
|
111
|
+
},
|
|
112
|
+
onConnectionStatusChange: (nextStatus) => {
|
|
113
|
+
connectionStatus = nextStatus;
|
|
114
|
+
},
|
|
115
|
+
onSessionGeneratingChange: (isGenerating) => {
|
|
116
|
+
sessionGenerating = isGenerating;
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
messages = client.getMessages();
|
|
120
|
+
if (options.live) {
|
|
121
|
+
client.subscribe();
|
|
122
|
+
}
|
|
123
|
+
client.mountDevtools();
|
|
124
|
+
// Note: Cleanup is handled by calling stop() directly when needed.
|
|
125
|
+
// Unlike React/Vue/Solid, Svelte 5 runes like $effect can only be used
|
|
126
|
+
// during component initialization, so we don't add automatic cleanup here.
|
|
127
|
+
// Users should call chat.stop() in their component's cleanup if needed.
|
|
128
|
+
// Define methods
|
|
129
|
+
const sendMessage = async (content) => {
|
|
130
|
+
await client.sendMessage(content);
|
|
131
|
+
};
|
|
132
|
+
const append = async (message) => {
|
|
133
|
+
await client.append(message);
|
|
134
|
+
};
|
|
135
|
+
const reload = async () => {
|
|
136
|
+
await client.reload();
|
|
137
|
+
};
|
|
138
|
+
const stop = () => {
|
|
139
|
+
client.stop();
|
|
140
|
+
};
|
|
141
|
+
const dispose = () => {
|
|
142
|
+
client.dispose();
|
|
143
|
+
};
|
|
144
|
+
const clear = () => {
|
|
145
|
+
client.clear();
|
|
146
|
+
};
|
|
147
|
+
const setMessages = (newMessages) => {
|
|
148
|
+
client.setMessagesManually(newMessages);
|
|
149
|
+
};
|
|
150
|
+
const addToolResult = async (result) => {
|
|
151
|
+
await client.addToolResult(result);
|
|
152
|
+
};
|
|
153
|
+
const addToolApprovalResponse = async (response) => {
|
|
154
|
+
await client.addToolApprovalResponse(response);
|
|
155
|
+
};
|
|
156
|
+
/**
|
|
157
|
+
* @deprecated Use `updateForwardedProps` instead.
|
|
158
|
+
* Both populate the same wire payload.
|
|
159
|
+
*/
|
|
160
|
+
const updateBody = (newBody) => {
|
|
161
|
+
client.updateOptions({ body: newBody });
|
|
162
|
+
};
|
|
163
|
+
const updateForwardedProps = (newForwardedProps) => {
|
|
164
|
+
client.updateOptions({ forwardedProps: newForwardedProps });
|
|
165
|
+
};
|
|
166
|
+
const updateContext = (newContext) => {
|
|
167
|
+
client.updateOptions({ context: newContext });
|
|
168
|
+
};
|
|
169
|
+
// The "active" structured-output part is the one on the assistant message
|
|
170
|
+
// after the latest user message. When no user message exists yet (e.g.
|
|
171
|
+
// `initialMessages` carries only a stale assistant turn), we return null
|
|
172
|
+
// rather than scanning history — otherwise a `final` from a previous
|
|
173
|
+
// session would leak in on first render. Uses `$derived.by` so the
|
|
174
|
+
// multi-line scan re-runs whenever `messages` changes.
|
|
175
|
+
const activeStructuredPart = $derived.by(() => {
|
|
176
|
+
let lastUserIndex = -1;
|
|
177
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
178
|
+
if (messages[i]?.role === 'user') {
|
|
179
|
+
lastUserIndex = i;
|
|
180
|
+
break;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
if (lastUserIndex === -1)
|
|
184
|
+
return null;
|
|
185
|
+
for (let i = messages.length - 1; i > lastUserIndex; i--) {
|
|
186
|
+
const m = messages[i];
|
|
187
|
+
if (m?.role !== 'assistant')
|
|
188
|
+
continue;
|
|
189
|
+
const part = m.parts.find((p) => p.type === 'structured-output');
|
|
190
|
+
if (part)
|
|
191
|
+
return part;
|
|
192
|
+
}
|
|
193
|
+
return null;
|
|
194
|
+
});
|
|
195
|
+
const partial = $derived.by(() => {
|
|
196
|
+
if (!activeStructuredPart)
|
|
197
|
+
return {};
|
|
198
|
+
const v = activeStructuredPart.partial ?? activeStructuredPart.data;
|
|
199
|
+
return (v ?? {});
|
|
200
|
+
});
|
|
201
|
+
const final = $derived(activeStructuredPart && activeStructuredPart.status === 'complete'
|
|
202
|
+
? activeStructuredPart.data
|
|
203
|
+
: null);
|
|
204
|
+
// Return the chat interface with reactive getters
|
|
205
|
+
// Using getters allows Svelte to track reactivity without needing $ prefix
|
|
206
|
+
// eslint-disable-next-line no-restricted-syntax -- rune return shape diverges from generic CreateChatReturn<TTools, TSchema, TContext> due to TSchema conditional partial/final fields; TS can't structurally narrow.
|
|
207
|
+
return {
|
|
208
|
+
get messages() {
|
|
209
|
+
return messages;
|
|
210
|
+
},
|
|
211
|
+
get isLoading() {
|
|
212
|
+
return isLoading;
|
|
213
|
+
},
|
|
214
|
+
get error() {
|
|
215
|
+
return error;
|
|
216
|
+
},
|
|
217
|
+
get status() {
|
|
218
|
+
return status;
|
|
219
|
+
},
|
|
220
|
+
get isSubscribed() {
|
|
221
|
+
return isSubscribed;
|
|
222
|
+
},
|
|
223
|
+
get connectionStatus() {
|
|
224
|
+
return connectionStatus;
|
|
225
|
+
},
|
|
226
|
+
get sessionGenerating() {
|
|
227
|
+
return sessionGenerating;
|
|
228
|
+
},
|
|
229
|
+
get partial() {
|
|
230
|
+
return partial;
|
|
231
|
+
},
|
|
232
|
+
get final() {
|
|
233
|
+
return final;
|
|
234
|
+
},
|
|
235
|
+
sendMessage,
|
|
236
|
+
append,
|
|
237
|
+
reload,
|
|
238
|
+
stop,
|
|
239
|
+
dispose,
|
|
240
|
+
setMessages,
|
|
241
|
+
clear,
|
|
242
|
+
addToolResult,
|
|
243
|
+
addToolApprovalResponse,
|
|
244
|
+
updateBody,
|
|
245
|
+
updateForwardedProps,
|
|
246
|
+
updateContext,
|
|
247
|
+
};
|
|
248
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import type { AudioGenerationResult, StreamChunk } from '@tanstack/ai';
|
|
2
|
+
import type { AIDevtoolsDisplayOptions, AudioGenerateInput, ConnectConnectionAdapter, GenerationClientState, GenerationFetcher, InferGenerationOutputFromReturn } from '@tanstack/ai-client';
|
|
3
|
+
/**
|
|
4
|
+
* Options for the createGenerateAudio function.
|
|
5
|
+
*
|
|
6
|
+
* @template TOutput - The output type after optional transform (defaults to AudioGenerationResult)
|
|
7
|
+
*/
|
|
8
|
+
export interface CreateGenerateAudioOptions<TOutput = AudioGenerationResult> {
|
|
9
|
+
/** Connect-based adapter for streaming transport (SSE, HTTP stream, custom) */
|
|
10
|
+
connection?: ConnectConnectionAdapter;
|
|
11
|
+
/** Direct async function for audio generation */
|
|
12
|
+
fetcher?: GenerationFetcher<AudioGenerateInput, AudioGenerationResult>;
|
|
13
|
+
/** Unique identifier for this generation instance */
|
|
14
|
+
id?: string;
|
|
15
|
+
/** Additional body parameters to send with connect-based adapter requests */
|
|
16
|
+
body?: Record<string, any>;
|
|
17
|
+
/** Display options for TanStack AI Devtools. */
|
|
18
|
+
devtools?: AIDevtoolsDisplayOptions;
|
|
19
|
+
/**
|
|
20
|
+
* Callback when audio is generated. Can optionally return a transformed value.
|
|
21
|
+
*
|
|
22
|
+
* - Return a non-null value to transform and store it as the result
|
|
23
|
+
* - Return `null` to keep the previous result unchanged
|
|
24
|
+
* - Return nothing (`void`) to store the raw result as-is
|
|
25
|
+
*/
|
|
26
|
+
onResult?: (result: AudioGenerationResult) => TOutput | null | void;
|
|
27
|
+
/** Callback when an error occurs */
|
|
28
|
+
onError?: (error: Error) => void;
|
|
29
|
+
/** Callback when progress is reported (0-100) */
|
|
30
|
+
onProgress?: (progress: number, message?: string) => void;
|
|
31
|
+
/** Callback for each stream chunk (connect-based adapter mode only) */
|
|
32
|
+
onChunk?: (chunk: StreamChunk) => void;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Return type for the createGenerateAudio function.
|
|
36
|
+
*
|
|
37
|
+
* @template TOutput - The output type (after optional transform)
|
|
38
|
+
*/
|
|
39
|
+
export interface CreateGenerateAudioReturn<TOutput = AudioGenerationResult> {
|
|
40
|
+
/** The generation result containing audio, or null */
|
|
41
|
+
readonly result: TOutput | null;
|
|
42
|
+
/** Whether generation is in progress */
|
|
43
|
+
readonly isLoading: boolean;
|
|
44
|
+
/** Current error, if any */
|
|
45
|
+
readonly error: Error | undefined;
|
|
46
|
+
/** Current state of the generation */
|
|
47
|
+
readonly status: GenerationClientState;
|
|
48
|
+
/** Trigger audio generation */
|
|
49
|
+
generate: (input: AudioGenerateInput) => Promise<void>;
|
|
50
|
+
/** Abort the current generation */
|
|
51
|
+
stop: () => void;
|
|
52
|
+
/** Clear result, error, and return to idle */
|
|
53
|
+
reset: () => void;
|
|
54
|
+
/** Update additional body parameters */
|
|
55
|
+
updateBody: (body: Record<string, any>) => void;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Creates a reactive audio generation instance for Svelte 5.
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* ```svelte
|
|
62
|
+
* <script>
|
|
63
|
+
* import { createGenerateAudio, fetchServerSentEvents } from '@tanstack/ai-svelte'
|
|
64
|
+
*
|
|
65
|
+
* const audio = createGenerateAudio({
|
|
66
|
+
* connection: fetchServerSentEvents('/api/generate/audio'),
|
|
67
|
+
* })
|
|
68
|
+
* </script>
|
|
69
|
+
*
|
|
70
|
+
* <div>
|
|
71
|
+
* <button onclick={() => audio.generate({ prompt: 'An upbeat electronic track', duration: 10 })}>
|
|
72
|
+
* Generate
|
|
73
|
+
* </button>
|
|
74
|
+
* {#if audio.isLoading}
|
|
75
|
+
* <p>Generating...</p>
|
|
76
|
+
* {/if}
|
|
77
|
+
* {#if audio.result?.audio.url}
|
|
78
|
+
* <audio src={audio.result.audio.url} controls></audio>
|
|
79
|
+
* {/if}
|
|
80
|
+
* </div>
|
|
81
|
+
* ```
|
|
82
|
+
*/
|
|
83
|
+
export declare function createGenerateAudio<TTransformed = void>(options: Omit<CreateGenerateAudioOptions, 'onResult'> & {
|
|
84
|
+
onResult?: (result: AudioGenerationResult) => TTransformed;
|
|
85
|
+
}): CreateGenerateAudioReturn<InferGenerationOutputFromReturn<AudioGenerationResult, TTransformed>>;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { createGeneration } from './create-generation.svelte';
|
|
2
|
+
/**
|
|
3
|
+
* Creates a reactive audio generation instance for Svelte 5.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```svelte
|
|
7
|
+
* <script>
|
|
8
|
+
* import { createGenerateAudio, fetchServerSentEvents } from '@tanstack/ai-svelte'
|
|
9
|
+
*
|
|
10
|
+
* const audio = createGenerateAudio({
|
|
11
|
+
* connection: fetchServerSentEvents('/api/generate/audio'),
|
|
12
|
+
* })
|
|
13
|
+
* </script>
|
|
14
|
+
*
|
|
15
|
+
* <div>
|
|
16
|
+
* <button onclick={() => audio.generate({ prompt: 'An upbeat electronic track', duration: 10 })}>
|
|
17
|
+
* Generate
|
|
18
|
+
* </button>
|
|
19
|
+
* {#if audio.isLoading}
|
|
20
|
+
* <p>Generating...</p>
|
|
21
|
+
* {/if}
|
|
22
|
+
* {#if audio.result?.audio.url}
|
|
23
|
+
* <audio src={audio.result.audio.url} controls></audio>
|
|
24
|
+
* {/if}
|
|
25
|
+
* </div>
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export function createGenerateAudio(options) {
|
|
29
|
+
const devtools = {
|
|
30
|
+
...options.devtools,
|
|
31
|
+
framework: 'svelte',
|
|
32
|
+
hookName: 'createGenerateAudio',
|
|
33
|
+
outputKind: 'audio',
|
|
34
|
+
};
|
|
35
|
+
const gen = createGeneration({
|
|
36
|
+
...options,
|
|
37
|
+
devtools,
|
|
38
|
+
});
|
|
39
|
+
return {
|
|
40
|
+
get result() {
|
|
41
|
+
return gen.result;
|
|
42
|
+
},
|
|
43
|
+
get isLoading() {
|
|
44
|
+
return gen.isLoading;
|
|
45
|
+
},
|
|
46
|
+
get error() {
|
|
47
|
+
return gen.error;
|
|
48
|
+
},
|
|
49
|
+
get status() {
|
|
50
|
+
return gen.status;
|
|
51
|
+
},
|
|
52
|
+
generate: gen.generate,
|
|
53
|
+
stop: gen.stop,
|
|
54
|
+
reset: gen.reset,
|
|
55
|
+
updateBody: gen.updateBody,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import type { ImageGenerationResult, StreamChunk } from '@tanstack/ai';
|
|
2
|
+
import type { AIDevtoolsDisplayOptions, ConnectConnectionAdapter, GenerationClientState, GenerationFetcher, ImageGenerateInput, InferGenerationOutputFromReturn } from '@tanstack/ai-client';
|
|
3
|
+
/**
|
|
4
|
+
* Options for the createGenerateImage function.
|
|
5
|
+
*
|
|
6
|
+
* @template TOutput - The output type after optional transform (defaults to ImageGenerationResult)
|
|
7
|
+
*/
|
|
8
|
+
export interface CreateGenerateImageOptions<TOutput = ImageGenerationResult> {
|
|
9
|
+
/** Connect-based adapter for streaming transport (SSE, HTTP stream, custom) */
|
|
10
|
+
connection?: ConnectConnectionAdapter;
|
|
11
|
+
/** Direct async function for image generation */
|
|
12
|
+
fetcher?: GenerationFetcher<ImageGenerateInput, ImageGenerationResult>;
|
|
13
|
+
/** Unique identifier for this generation instance */
|
|
14
|
+
id?: string;
|
|
15
|
+
/** Additional body parameters to send with connect-based adapter requests */
|
|
16
|
+
body?: Record<string, any>;
|
|
17
|
+
/** Display options for TanStack AI Devtools. */
|
|
18
|
+
devtools?: AIDevtoolsDisplayOptions;
|
|
19
|
+
/**
|
|
20
|
+
* Callback when images are generated. Can optionally return a transformed value.
|
|
21
|
+
*
|
|
22
|
+
* - Return a non-null value to transform and store it as the result
|
|
23
|
+
* - Return `null` to keep the previous result unchanged
|
|
24
|
+
* - Return nothing (`void`) to store the raw result as-is
|
|
25
|
+
*/
|
|
26
|
+
onResult?: (result: ImageGenerationResult) => TOutput | null | void;
|
|
27
|
+
/** Callback when an error occurs */
|
|
28
|
+
onError?: (error: Error) => void;
|
|
29
|
+
/** Callback when progress is reported (0-100) */
|
|
30
|
+
onProgress?: (progress: number, message?: string) => void;
|
|
31
|
+
/** Callback for each stream chunk (connect-based adapter mode only) */
|
|
32
|
+
onChunk?: (chunk: StreamChunk) => void;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Return type for the createGenerateImage function.
|
|
36
|
+
*
|
|
37
|
+
* @template TOutput - The output type (after optional transform)
|
|
38
|
+
*/
|
|
39
|
+
export interface CreateGenerateImageReturn<TOutput = ImageGenerationResult> {
|
|
40
|
+
/** The generation result containing images, or null */
|
|
41
|
+
readonly result: TOutput | null;
|
|
42
|
+
/** Whether generation is in progress */
|
|
43
|
+
readonly isLoading: boolean;
|
|
44
|
+
/** Current error, if any */
|
|
45
|
+
readonly error: Error | undefined;
|
|
46
|
+
/** Current state of the generation */
|
|
47
|
+
readonly status: GenerationClientState;
|
|
48
|
+
/** Trigger image generation */
|
|
49
|
+
generate: (input: ImageGenerateInput) => Promise<void>;
|
|
50
|
+
/** Abort the current generation */
|
|
51
|
+
stop: () => void;
|
|
52
|
+
/** Clear result, error, and return to idle */
|
|
53
|
+
reset: () => void;
|
|
54
|
+
/** Update additional body parameters */
|
|
55
|
+
updateBody: (body: Record<string, any>) => void;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Creates a reactive image generation instance for Svelte 5.
|
|
59
|
+
*
|
|
60
|
+
* Supports two transport modes:
|
|
61
|
+
* - **ConnectConnectionAdapter** -- Streaming transport (SSE, HTTP stream, custom)
|
|
62
|
+
* - **Fetcher** -- Direct async function call
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* ```svelte
|
|
66
|
+
* <script>
|
|
67
|
+
* import { createGenerateImage, fetchServerSentEvents } from '@tanstack/ai-svelte'
|
|
68
|
+
*
|
|
69
|
+
* const imageGen = createGenerateImage({
|
|
70
|
+
* connection: fetchServerSentEvents('/api/generate/image'),
|
|
71
|
+
* })
|
|
72
|
+
* </script>
|
|
73
|
+
*
|
|
74
|
+
* <div>
|
|
75
|
+
* <button onclick={() => imageGen.generate({ prompt: 'A sunset over mountains' })}>
|
|
76
|
+
* Generate
|
|
77
|
+
* </button>
|
|
78
|
+
* {#if imageGen.isLoading}
|
|
79
|
+
* <p>Generating...</p>
|
|
80
|
+
* {/if}
|
|
81
|
+
* {#if imageGen.error}
|
|
82
|
+
* <p>Error: {imageGen.error.message}</p>
|
|
83
|
+
* {/if}
|
|
84
|
+
* {#if imageGen.result}
|
|
85
|
+
* {#each imageGen.result.images as img}
|
|
86
|
+
* <img src={img.url || `data:image/png;base64,${img.b64Json}`} alt="Generated" />
|
|
87
|
+
* {/each}
|
|
88
|
+
* {/if}
|
|
89
|
+
* </div>
|
|
90
|
+
* ```
|
|
91
|
+
*/
|
|
92
|
+
export declare function createGenerateImage<TTransformed = void>(options: Omit<CreateGenerateImageOptions, 'onResult'> & {
|
|
93
|
+
onResult?: (result: ImageGenerationResult) => TTransformed;
|
|
94
|
+
}): CreateGenerateImageReturn<InferGenerationOutputFromReturn<ImageGenerationResult, TTransformed>>;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { createGeneration } from './create-generation.svelte';
|
|
2
|
+
/**
|
|
3
|
+
* Creates a reactive image generation instance for Svelte 5.
|
|
4
|
+
*
|
|
5
|
+
* Supports two transport modes:
|
|
6
|
+
* - **ConnectConnectionAdapter** -- Streaming transport (SSE, HTTP stream, custom)
|
|
7
|
+
* - **Fetcher** -- Direct async function call
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```svelte
|
|
11
|
+
* <script>
|
|
12
|
+
* import { createGenerateImage, fetchServerSentEvents } from '@tanstack/ai-svelte'
|
|
13
|
+
*
|
|
14
|
+
* const imageGen = createGenerateImage({
|
|
15
|
+
* connection: fetchServerSentEvents('/api/generate/image'),
|
|
16
|
+
* })
|
|
17
|
+
* </script>
|
|
18
|
+
*
|
|
19
|
+
* <div>
|
|
20
|
+
* <button onclick={() => imageGen.generate({ prompt: 'A sunset over mountains' })}>
|
|
21
|
+
* Generate
|
|
22
|
+
* </button>
|
|
23
|
+
* {#if imageGen.isLoading}
|
|
24
|
+
* <p>Generating...</p>
|
|
25
|
+
* {/if}
|
|
26
|
+
* {#if imageGen.error}
|
|
27
|
+
* <p>Error: {imageGen.error.message}</p>
|
|
28
|
+
* {/if}
|
|
29
|
+
* {#if imageGen.result}
|
|
30
|
+
* {#each imageGen.result.images as img}
|
|
31
|
+
* <img src={img.url || `data:image/png;base64,${img.b64Json}`} alt="Generated" />
|
|
32
|
+
* {/each}
|
|
33
|
+
* {/if}
|
|
34
|
+
* </div>
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export function createGenerateImage(options) {
|
|
38
|
+
const devtools = {
|
|
39
|
+
...options.devtools,
|
|
40
|
+
framework: 'svelte',
|
|
41
|
+
hookName: 'createGenerateImage',
|
|
42
|
+
outputKind: 'image',
|
|
43
|
+
};
|
|
44
|
+
const gen = createGeneration({
|
|
45
|
+
...options,
|
|
46
|
+
devtools,
|
|
47
|
+
});
|
|
48
|
+
return {
|
|
49
|
+
get result() {
|
|
50
|
+
return gen.result;
|
|
51
|
+
},
|
|
52
|
+
get isLoading() {
|
|
53
|
+
return gen.isLoading;
|
|
54
|
+
},
|
|
55
|
+
get error() {
|
|
56
|
+
return gen.error;
|
|
57
|
+
},
|
|
58
|
+
get status() {
|
|
59
|
+
return gen.status;
|
|
60
|
+
},
|
|
61
|
+
generate: gen.generate,
|
|
62
|
+
stop: gen.stop,
|
|
63
|
+
reset: gen.reset,
|
|
64
|
+
updateBody: gen.updateBody,
|
|
65
|
+
};
|
|
66
|
+
}
|