@tanstack/ai-svelte 0.1.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Tanner Linsley
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,83 @@
1
+ # @tanstack/ai-svelte
2
+
3
+ Svelte bindings for TanStack AI.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @tanstack/ai-svelte
9
+ # or
10
+ pnpm add @tanstack/ai-svelte
11
+ # or
12
+ yarn add @tanstack/ai-svelte
13
+ ```
14
+
15
+ ## Usage
16
+
17
+ ```svelte
18
+ <script>
19
+ import { createChat, fetchServerSentEvents } from '@tanstack/ai-svelte'
20
+
21
+ const chat = createChat({
22
+ connection: fetchServerSentEvents('/api/chat'),
23
+ })
24
+ </script>
25
+
26
+ <div>
27
+ {#each chat.messages as message}
28
+ <div>{message.role}: {message.parts[0].content}</div>
29
+ {/each}
30
+
31
+ {#if chat.isLoading}
32
+ <button onclick={chat.stop}>Stop</button>
33
+ {/if}
34
+
35
+ <button onclick={() => chat.sendMessage('Hello!')}> Send </button>
36
+ </div>
37
+ ```
38
+
39
+ ## API
40
+
41
+ ### `createChat(options)`
42
+
43
+ Creates a reactive chat instance. Returns an object with reactive getters and methods:
44
+
45
+ **Reactive Properties (no `$` prefix needed):**
46
+
47
+ - `messages` - Array of messages in the conversation
48
+ - `isLoading` - Boolean indicating if a response is being generated
49
+ - `error` - Current error, if any
50
+
51
+ **Methods:**
52
+
53
+ - `sendMessage(content)` - Send a message
54
+ - `append(message)` - Append a message
55
+ - `reload()` - Reload the last assistant message
56
+ - `stop()` - Stop the current response generation
57
+ - `clear()` - Clear all messages
58
+ - `setMessages(messages)` - Set messages manually
59
+ - `addToolResult(result)` - Add a tool result
60
+ - `addToolApprovalResponse(response)` - Respond to a tool approval request
61
+
62
+ ## Svelte 5 Runes
63
+
64
+ This library uses Svelte 5 runes (`$state`) internally, providing a clean API where you don't need to use the `$` prefix to access reactive state:
65
+
66
+ ```svelte
67
+ <script>
68
+ const chat = createChat({ ... })
69
+
70
+ // No $ needed - these are reactive getters!
71
+ console.log(chat.messages)
72
+ console.log(chat.isLoading)
73
+ </script>
74
+
75
+ <!-- Reactivity works automatically in templates -->
76
+ {#each chat.messages as message}
77
+ ...
78
+ {/each}
79
+ ```
80
+
81
+ ## License
82
+
83
+ MIT
@@ -0,0 +1,33 @@
1
+ import type { AnyClientTool } from '@tanstack/ai';
2
+ import type { CreateChatOptions, CreateChatReturn } from './types';
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 declare function createChat<TTools extends ReadonlyArray<AnyClientTool> = any>(options: CreateChatOptions<TTools>): CreateChatReturn<TTools>;
@@ -0,0 +1,108 @@
1
+ import { ChatClient } from '@tanstack/ai-client';
2
+ /**
3
+ * Creates a reactive chat instance for Svelte 5.
4
+ *
5
+ * This function wraps the ChatClient from @tanstack/ai-client and exposes
6
+ * reactive state using Svelte 5 runes. The returned object has reactive
7
+ * getters that automatically update when state changes.
8
+ *
9
+ * @example
10
+ * ```svelte
11
+ * <script>
12
+ * import { createChat, fetchServerSentEvents } from '@tanstack/ai-svelte'
13
+ *
14
+ * const chat = createChat({
15
+ * connection: fetchServerSentEvents('/api/chat'),
16
+ * })
17
+ * </script>
18
+ *
19
+ * <div>
20
+ * {#each chat.messages as message}
21
+ * <div>{message.role}: {message.parts[0].content}</div>
22
+ * {/each}
23
+ *
24
+ * {#if chat.isLoading}
25
+ * <button onclick={chat.stop}>Stop</button>
26
+ * {/if}
27
+ *
28
+ * <button onclick={() => chat.sendMessage('Hello!')}>Send</button>
29
+ * </div>
30
+ * ```
31
+ */
32
+ export function createChat(options) {
33
+ // Generate a unique ID for this chat instance
34
+ const clientId = options.id ||
35
+ `chat-${Date.now()}-${Math.random().toString(36).substring(7)}`;
36
+ // Create reactive state using Svelte 5 runes
37
+ let messages = $state(options.initialMessages || []);
38
+ let isLoading = $state(false);
39
+ let error = $state(undefined);
40
+ // Create ChatClient instance
41
+ const client = new ChatClient({
42
+ connection: options.connection,
43
+ id: clientId,
44
+ initialMessages: options.initialMessages,
45
+ body: options.body,
46
+ onResponse: options.onResponse,
47
+ onChunk: options.onChunk,
48
+ onFinish: options.onFinish,
49
+ onError: options.onError,
50
+ tools: options.tools,
51
+ streamProcessor: options.streamProcessor,
52
+ onMessagesChange: (newMessages) => {
53
+ messages = newMessages;
54
+ },
55
+ onLoadingChange: (newIsLoading) => {
56
+ isLoading = newIsLoading;
57
+ },
58
+ onErrorChange: (newError) => {
59
+ error = newError;
60
+ },
61
+ });
62
+ // Define methods
63
+ const sendMessage = async (content) => {
64
+ await client.sendMessage(content);
65
+ };
66
+ const append = async (message) => {
67
+ await client.append(message);
68
+ };
69
+ const reload = async () => {
70
+ await client.reload();
71
+ };
72
+ const stop = () => {
73
+ client.stop();
74
+ };
75
+ const clear = () => {
76
+ client.clear();
77
+ };
78
+ const setMessages = (newMessages) => {
79
+ client.setMessagesManually(newMessages);
80
+ };
81
+ const addToolResult = async (result) => {
82
+ await client.addToolResult(result);
83
+ };
84
+ const addToolApprovalResponse = async (response) => {
85
+ await client.addToolApprovalResponse(response);
86
+ };
87
+ // Return the chat interface with reactive getters
88
+ // Using getters allows Svelte to track reactivity without needing $ prefix
89
+ return {
90
+ get messages() {
91
+ return messages;
92
+ },
93
+ get isLoading() {
94
+ return isLoading;
95
+ },
96
+ get error() {
97
+ return error;
98
+ },
99
+ sendMessage,
100
+ append,
101
+ reload,
102
+ stop,
103
+ setMessages,
104
+ clear,
105
+ addToolResult,
106
+ addToolApprovalResponse,
107
+ };
108
+ }
@@ -0,0 +1,3 @@
1
+ export { createChat } from './create-chat.svelte';
2
+ export type { CreateChatOptions, CreateChatReturn, UIMessage, ChatRequestBody, } from './types';
3
+ export { fetchServerSentEvents, fetchHttpStream, stream, createChatClientOptions, clientTools, type ConnectionAdapter, type FetchConnectionOptions, type InferChatMessages, } from '@tanstack/ai-client';
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export { createChat } from './create-chat.svelte';
2
+ // Re-export from ai-client for convenience
3
+ export { fetchServerSentEvents, fetchHttpStream, stream, createChatClientOptions, clientTools, } from '@tanstack/ai-client';
@@ -0,0 +1,74 @@
1
+ import type { AnyClientTool, ModelMessage } from '@tanstack/ai';
2
+ import type { ChatClientOptions, ChatRequestBody, UIMessage } from '@tanstack/ai-client';
3
+ export type { UIMessage, ChatRequestBody };
4
+ /**
5
+ * Options for the createChat function.
6
+ *
7
+ * This extends ChatClientOptions but omits the state change callbacks that are
8
+ * managed internally by Svelte state:
9
+ * - `onMessagesChange` - Managed by Svelte state (exposed as `messages`)
10
+ * - `onLoadingChange` - Managed by Svelte state (exposed as `isLoading`)
11
+ * - `onErrorChange` - Managed by Svelte state (exposed as `error`)
12
+ *
13
+ * All other callbacks (onResponse, onChunk, onFinish, onError) are
14
+ * passed through to the underlying ChatClient and can be used for side effects.
15
+ *
16
+ * Note: Connection and body changes will recreate the ChatClient instance.
17
+ * To update these options, remount the component or use a key prop.
18
+ */
19
+ export type CreateChatOptions<TTools extends ReadonlyArray<AnyClientTool> = any> = Omit<ChatClientOptions<TTools>, 'onMessagesChange' | 'onLoadingChange' | 'onErrorChange'>;
20
+ export interface CreateChatReturn<TTools extends ReadonlyArray<AnyClientTool> = any> {
21
+ /**
22
+ * Current messages in the conversation (reactive getter)
23
+ */
24
+ readonly messages: Array<UIMessage<TTools>>;
25
+ /**
26
+ * Send a message and get a response
27
+ */
28
+ sendMessage: (content: string) => Promise<void>;
29
+ /**
30
+ * Append a message to the conversation
31
+ */
32
+ append: (message: ModelMessage | UIMessage<TTools>) => Promise<void>;
33
+ /**
34
+ * Add the result of a client-side tool execution
35
+ */
36
+ addToolResult: (result: {
37
+ toolCallId: string;
38
+ tool: string;
39
+ output: any;
40
+ state?: 'output-available' | 'output-error';
41
+ errorText?: string;
42
+ }) => Promise<void>;
43
+ /**
44
+ * Respond to a tool approval request
45
+ */
46
+ addToolApprovalResponse: (response: {
47
+ id: string;
48
+ approved: boolean;
49
+ }) => Promise<void>;
50
+ /**
51
+ * Reload the last assistant message
52
+ */
53
+ reload: () => Promise<void>;
54
+ /**
55
+ * Stop the current response generation
56
+ */
57
+ stop: () => void;
58
+ /**
59
+ * Whether a response is currently being generated (reactive getter)
60
+ */
61
+ readonly isLoading: boolean;
62
+ /**
63
+ * Current error, if any (reactive getter)
64
+ */
65
+ readonly error: Error | undefined;
66
+ /**
67
+ * Set messages manually
68
+ */
69
+ setMessages: (messages: Array<UIMessage<TTools>>) => void;
70
+ /**
71
+ * Clear all messages
72
+ */
73
+ clear: () => void;
74
+ }
package/dist/types.js ADDED
@@ -0,0 +1,3 @@
1
+ export {};
2
+ // Note: createChatClientOptions and InferChatMessages are now in @tanstack/ai-client
3
+ // and re-exported from there for convenience
package/package.json ADDED
@@ -0,0 +1,67 @@
1
+ {
2
+ "name": "@tanstack/ai-svelte",
3
+ "version": "0.1.1",
4
+ "description": "Svelte bindings for TanStack AI",
5
+ "author": "",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/TanStack/ai.git",
10
+ "directory": "packages/typescript/ai-svelte"
11
+ },
12
+ "type": "module",
13
+ "svelte": "./dist/index.js",
14
+ "module": "./dist/index.js",
15
+ "types": "./dist/index.d.ts",
16
+ "exports": {
17
+ ".": {
18
+ "types": "./dist/index.d.ts",
19
+ "svelte": "./dist/index.js",
20
+ "import": "./dist/index.js"
21
+ }
22
+ },
23
+ "files": [
24
+ "dist",
25
+ "src"
26
+ ],
27
+ "keywords": [
28
+ "ai",
29
+ "svelte",
30
+ "hooks",
31
+ "tanstack",
32
+ "chat",
33
+ "streaming"
34
+ ],
35
+ "dependencies": {
36
+ "svelte": "^5.20.0",
37
+ "@tanstack/ai": "0.0.3",
38
+ "@tanstack/ai-client": "0.0.3"
39
+ },
40
+ "devDependencies": {
41
+ "@sveltejs/package": "^2.3.10",
42
+ "@sveltejs/vite-plugin-svelte": "^5.1.1",
43
+ "@types/node": "^24.10.1",
44
+ "@vitest/coverage-v8": "4.0.14",
45
+ "jsdom": "^27.2.0",
46
+ "svelte": "^5.20.0",
47
+ "svelte-check": "^4.2.0",
48
+ "typescript": "5.9.3",
49
+ "vite": "^7.2.4",
50
+ "zod": "^4.1.13"
51
+ },
52
+ "peerDependencies": {
53
+ "svelte": "^5.0.0",
54
+ "@tanstack/ai": "0.0.3",
55
+ "@tanstack/ai-client": "0.0.3"
56
+ },
57
+ "scripts": {
58
+ "clean": "rm -rf ./dist",
59
+ "lint:fix": "eslint ./src --fix",
60
+ "test:eslint": "eslint ./src",
61
+ "test:lib": "vitest run",
62
+ "test:lib:dev": "pnpm test:lib --watch",
63
+ "test:types": "svelte-check",
64
+ "test:build": "publint --strict",
65
+ "build": "svelte-package -i src -o dist"
66
+ }
67
+ }
@@ -0,0 +1,134 @@
1
+ import { ChatClient } from '@tanstack/ai-client'
2
+ import type { AnyClientTool, ModelMessage } from '@tanstack/ai'
3
+ import type { CreateChatOptions, CreateChatReturn, UIMessage } from './types'
4
+
5
+ /**
6
+ * Creates a reactive chat instance for Svelte 5.
7
+ *
8
+ * This function wraps the ChatClient from @tanstack/ai-client and exposes
9
+ * reactive state using Svelte 5 runes. The returned object has reactive
10
+ * getters that automatically update when state changes.
11
+ *
12
+ * @example
13
+ * ```svelte
14
+ * <script>
15
+ * import { createChat, fetchServerSentEvents } from '@tanstack/ai-svelte'
16
+ *
17
+ * const chat = createChat({
18
+ * connection: fetchServerSentEvents('/api/chat'),
19
+ * })
20
+ * </script>
21
+ *
22
+ * <div>
23
+ * {#each chat.messages as message}
24
+ * <div>{message.role}: {message.parts[0].content}</div>
25
+ * {/each}
26
+ *
27
+ * {#if chat.isLoading}
28
+ * <button onclick={chat.stop}>Stop</button>
29
+ * {/if}
30
+ *
31
+ * <button onclick={() => chat.sendMessage('Hello!')}>Send</button>
32
+ * </div>
33
+ * ```
34
+ */
35
+ export function createChat<TTools extends ReadonlyArray<AnyClientTool> = any>(
36
+ options: CreateChatOptions<TTools>,
37
+ ): CreateChatReturn<TTools> {
38
+ // Generate a unique ID for this chat instance
39
+ const clientId =
40
+ options.id ||
41
+ `chat-${Date.now()}-${Math.random().toString(36).substring(7)}`
42
+
43
+ // Create reactive state using Svelte 5 runes
44
+ let messages = $state<Array<UIMessage<TTools>>>(options.initialMessages || [])
45
+ let isLoading = $state(false)
46
+ let error = $state<Error | undefined>(undefined)
47
+
48
+ // Create ChatClient instance
49
+ const client = new ChatClient({
50
+ connection: options.connection,
51
+ id: clientId,
52
+ initialMessages: options.initialMessages,
53
+ body: options.body,
54
+ onResponse: options.onResponse,
55
+ onChunk: options.onChunk,
56
+ onFinish: options.onFinish,
57
+ onError: options.onError,
58
+ tools: options.tools,
59
+ streamProcessor: options.streamProcessor,
60
+ onMessagesChange: (newMessages: Array<UIMessage<TTools>>) => {
61
+ messages = newMessages
62
+ },
63
+ onLoadingChange: (newIsLoading: boolean) => {
64
+ isLoading = newIsLoading
65
+ },
66
+ onErrorChange: (newError: Error | undefined) => {
67
+ error = newError
68
+ },
69
+ })
70
+
71
+ // Define methods
72
+ const sendMessage = async (content: string) => {
73
+ await client.sendMessage(content)
74
+ }
75
+
76
+ const append = async (message: ModelMessage | UIMessage<TTools>) => {
77
+ await client.append(message)
78
+ }
79
+
80
+ const reload = async () => {
81
+ await client.reload()
82
+ }
83
+
84
+ const stop = () => {
85
+ client.stop()
86
+ }
87
+
88
+ const clear = () => {
89
+ client.clear()
90
+ }
91
+
92
+ const setMessages = (newMessages: Array<UIMessage<TTools>>) => {
93
+ client.setMessagesManually(newMessages)
94
+ }
95
+
96
+ const addToolResult = async (result: {
97
+ toolCallId: string
98
+ tool: string
99
+ output: any
100
+ state?: 'output-available' | 'output-error'
101
+ errorText?: string
102
+ }) => {
103
+ await client.addToolResult(result)
104
+ }
105
+
106
+ const addToolApprovalResponse = async (response: {
107
+ id: string
108
+ approved: boolean
109
+ }) => {
110
+ await client.addToolApprovalResponse(response)
111
+ }
112
+
113
+ // Return the chat interface with reactive getters
114
+ // Using getters allows Svelte to track reactivity without needing $ prefix
115
+ return {
116
+ get messages() {
117
+ return messages
118
+ },
119
+ get isLoading() {
120
+ return isLoading
121
+ },
122
+ get error() {
123
+ return error
124
+ },
125
+ sendMessage,
126
+ append,
127
+ reload,
128
+ stop,
129
+ setMessages,
130
+ clear,
131
+ addToolResult,
132
+ addToolApprovalResponse,
133
+ }
134
+ }
package/src/index.ts ADDED
@@ -0,0 +1,19 @@
1
+ export { createChat } from './create-chat.svelte'
2
+ export type {
3
+ CreateChatOptions,
4
+ CreateChatReturn,
5
+ UIMessage,
6
+ ChatRequestBody,
7
+ } from './types'
8
+
9
+ // Re-export from ai-client for convenience
10
+ export {
11
+ fetchServerSentEvents,
12
+ fetchHttpStream,
13
+ stream,
14
+ createChatClientOptions,
15
+ clientTools,
16
+ type ConnectionAdapter,
17
+ type FetchConnectionOptions,
18
+ type InferChatMessages,
19
+ } from '@tanstack/ai-client'
package/src/types.ts ADDED
@@ -0,0 +1,102 @@
1
+ import type { AnyClientTool, ModelMessage } from '@tanstack/ai'
2
+ import type {
3
+ ChatClientOptions,
4
+ ChatRequestBody,
5
+ UIMessage,
6
+ } from '@tanstack/ai-client'
7
+
8
+ // Re-export types from ai-client
9
+ export type { UIMessage, ChatRequestBody }
10
+
11
+ /**
12
+ * Options for the createChat function.
13
+ *
14
+ * This extends ChatClientOptions but omits the state change callbacks that are
15
+ * managed internally by Svelte state:
16
+ * - `onMessagesChange` - Managed by Svelte state (exposed as `messages`)
17
+ * - `onLoadingChange` - Managed by Svelte state (exposed as `isLoading`)
18
+ * - `onErrorChange` - Managed by Svelte state (exposed as `error`)
19
+ *
20
+ * All other callbacks (onResponse, onChunk, onFinish, onError) are
21
+ * passed through to the underlying ChatClient and can be used for side effects.
22
+ *
23
+ * Note: Connection and body changes will recreate the ChatClient instance.
24
+ * To update these options, remount the component or use a key prop.
25
+ */
26
+ export type CreateChatOptions<
27
+ TTools extends ReadonlyArray<AnyClientTool> = any,
28
+ > = Omit<
29
+ ChatClientOptions<TTools>,
30
+ 'onMessagesChange' | 'onLoadingChange' | 'onErrorChange'
31
+ >
32
+
33
+ export interface CreateChatReturn<
34
+ TTools extends ReadonlyArray<AnyClientTool> = any,
35
+ > {
36
+ /**
37
+ * Current messages in the conversation (reactive getter)
38
+ */
39
+ readonly messages: Array<UIMessage<TTools>>
40
+
41
+ /**
42
+ * Send a message and get a response
43
+ */
44
+ sendMessage: (content: string) => Promise<void>
45
+
46
+ /**
47
+ * Append a message to the conversation
48
+ */
49
+ append: (message: ModelMessage | UIMessage<TTools>) => Promise<void>
50
+
51
+ /**
52
+ * Add the result of a client-side tool execution
53
+ */
54
+ addToolResult: (result: {
55
+ toolCallId: string
56
+ tool: string
57
+ output: any
58
+ state?: 'output-available' | 'output-error'
59
+ errorText?: string
60
+ }) => Promise<void>
61
+
62
+ /**
63
+ * Respond to a tool approval request
64
+ */
65
+ addToolApprovalResponse: (response: {
66
+ id: string // approval.id, not toolCallId
67
+ approved: boolean
68
+ }) => Promise<void>
69
+
70
+ /**
71
+ * Reload the last assistant message
72
+ */
73
+ reload: () => Promise<void>
74
+
75
+ /**
76
+ * Stop the current response generation
77
+ */
78
+ stop: () => void
79
+
80
+ /**
81
+ * Whether a response is currently being generated (reactive getter)
82
+ */
83
+ readonly isLoading: boolean
84
+
85
+ /**
86
+ * Current error, if any (reactive getter)
87
+ */
88
+ readonly error: Error | undefined
89
+
90
+ /**
91
+ * Set messages manually
92
+ */
93
+ setMessages: (messages: Array<UIMessage<TTools>>) => void
94
+
95
+ /**
96
+ * Clear all messages
97
+ */
98
+ clear: () => void
99
+ }
100
+
101
+ // Note: createChatClientOptions and InferChatMessages are now in @tanstack/ai-client
102
+ // and re-exported from there for convenience