@tanstack/ai-svelte 0.13.12 → 0.13.14

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/ai-svelte",
3
- "version": "0.13.12",
3
+ "version": "0.13.14",
4
4
  "description": "Svelte 5 bindings for TanStack AI streaming chat, structured outputs, and media generation.",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
@@ -46,11 +46,11 @@
46
46
  "media-generation"
47
47
  ],
48
48
  "dependencies": {
49
- "@tanstack/ai-client": "0.18.3"
49
+ "@tanstack/ai-client": "0.18.5"
50
50
  },
51
51
  "peerDependencies": {
52
52
  "svelte": "^5.0.0",
53
- "@tanstack/ai": "^0.34.1"
53
+ "@tanstack/ai": "^0.36.0"
54
54
  },
55
55
  "devDependencies": {
56
56
  "@standard-schema/spec": "^1.1.0",
@@ -63,7 +63,7 @@
63
63
  "svelte-check": "^4.2.0",
64
64
  "typescript": "5.9.3",
65
65
  "vite": "^7.3.3",
66
- "@tanstack/ai": "0.34.1"
66
+ "@tanstack/ai": "0.36.0"
67
67
  },
68
68
  "scripts": {
69
69
  "clean": "rm -rf ./dist",
@@ -1,34 +0,0 @@
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>;
@@ -1,248 +0,0 @@
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
- }
@@ -1,85 +0,0 @@
1
- import type { AudioGenerationResult, StreamChunk } from '@tanstack/ai';
2
- import type { AIDevtoolsDisplayOptions, AudioGenerateInput, ConnectConnectionAdapter, GenerationClientState, GenerationFetcher, InferGenerationOutput } 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<TOnResult extends ((result: AudioGenerationResult) => any) | undefined = undefined>(options: Omit<CreateGenerateAudioOptions, 'onResult'> & {
84
- onResult?: TOnResult;
85
- }): CreateGenerateAudioReturn<InferGenerationOutput<AudioGenerationResult, TOnResult>>;
@@ -1,57 +0,0 @@
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
- }
@@ -1,94 +0,0 @@
1
- import type { ImageGenerationResult, StreamChunk } from '@tanstack/ai';
2
- import type { AIDevtoolsDisplayOptions, ConnectConnectionAdapter, GenerationClientState, GenerationFetcher, ImageGenerateInput, InferGenerationOutput } 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<TOnResult extends ((result: ImageGenerationResult) => any) | undefined = undefined>(options: Omit<CreateGenerateImageOptions, 'onResult'> & {
93
- onResult?: TOnResult;
94
- }): CreateGenerateImageReturn<InferGenerationOutput<ImageGenerationResult, TOnResult>>;
@@ -1,66 +0,0 @@
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
- }