@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 +21 -0
- package/README.md +83 -0
- package/dist/create-chat.svelte.d.ts +33 -0
- package/dist/create-chat.svelte.js +108 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/types.d.ts +74 -0
- package/dist/types.js +3 -0
- package/package.json +67 -0
- package/src/create-chat.svelte.ts +134 -0
- package/src/index.ts +19 -0
- package/src/types.ts +102 -0
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
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -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
package/dist/types.d.ts
ADDED
|
@@ -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
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
|