@tanstack/ai-preact 0.6.4 → 0.6.7
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/README.md +6 -6
- package/dist/esm/types.d.ts +19 -2
- package/dist/esm/use-chat.js +28 -2
- package/dist/esm/use-chat.js.map +1 -1
- package/package.json +3 -3
- package/src/types.ts +29 -2
- package/src/use-chat.ts +31 -3
package/README.md
CHANGED
|
@@ -85,18 +85,18 @@ Available adapters: `openaiText`, `openaiEmbed`, `openaiSummarize`, `anthropicTe
|
|
|
85
85
|
<td>
|
|
86
86
|
<a href="https://www.coderabbit.ai/?via=tanstack&dub_id=aCcEEdAOqqutX6OS" >
|
|
87
87
|
<picture>
|
|
88
|
-
<source media="(prefers-color-scheme: dark)" srcset="https://tanstack.com/assets/coderabbit-dark-
|
|
89
|
-
<source media="(prefers-color-scheme: light)" srcset="https://tanstack.com/assets/coderabbit-light-
|
|
90
|
-
<img src="https://tanstack.com/assets/coderabbit-light-
|
|
88
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://tanstack.com/assets/coderabbit-dark-D643Zkrv.svg" height="40" />
|
|
89
|
+
<source media="(prefers-color-scheme: light)" srcset="https://tanstack.com/assets/coderabbit-light-CIzGLYU_.svg" height="40" />
|
|
90
|
+
<img src="https://tanstack.com/assets/coderabbit-light-CIzGLYU_.svg" height="40" alt="CodeRabbit" />
|
|
91
91
|
</picture>
|
|
92
92
|
</a>
|
|
93
93
|
</td>
|
|
94
94
|
<td>
|
|
95
95
|
<a href="https://www.cloudflare.com?utm_source=tanstack">
|
|
96
96
|
<picture>
|
|
97
|
-
<source media="(prefers-color-scheme: dark)" srcset="https://tanstack.com/assets/cloudflare-white-
|
|
98
|
-
<source media="(prefers-color-scheme: light)" srcset="https://tanstack.com/assets/cloudflare-black-
|
|
99
|
-
<img src="https://tanstack.com/assets/cloudflare-black-
|
|
97
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://tanstack.com/assets/cloudflare-white-Co-Tyjbl.svg" height="60" />
|
|
98
|
+
<source media="(prefers-color-scheme: light)" srcset="https://tanstack.com/assets/cloudflare-black-6Ojsn8yh.svg" height="60" />
|
|
99
|
+
<img src="https://tanstack.com/assets/cloudflare-black-6Ojsn8yh.svg" height="60" alt="Cloudflare" />
|
|
100
100
|
</picture>
|
|
101
101
|
</a>
|
|
102
102
|
</td>
|
package/dist/esm/types.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AnyClientTool, ModelMessage } from '@tanstack/ai';
|
|
2
|
-
import { ChatClientOptions, ChatClientState, ChatRequestBody, MultimodalContent, UIMessage } from '@tanstack/ai-client';
|
|
2
|
+
import { ChatClientOptions, ChatClientState, ChatRequestBody, ConnectionStatus, MultimodalContent, UIMessage } from '@tanstack/ai-client';
|
|
3
3
|
export type { ChatRequestBody, MultimodalContent, UIMessage };
|
|
4
4
|
/**
|
|
5
5
|
* Options for the useChat hook.
|
|
@@ -17,7 +17,9 @@ export type { ChatRequestBody, MultimodalContent, UIMessage };
|
|
|
17
17
|
* Note: Connection and body changes will recreate the ChatClient instance.
|
|
18
18
|
* To update these options, remount the component or use a key prop.
|
|
19
19
|
*/
|
|
20
|
-
export type UseChatOptions<TTools extends ReadonlyArray<AnyClientTool> = any> = Omit<ChatClientOptions<TTools>, 'onMessagesChange' | 'onLoadingChange' | 'onErrorChange' | 'onStatusChange'
|
|
20
|
+
export type UseChatOptions<TTools extends ReadonlyArray<AnyClientTool> = any> = Omit<ChatClientOptions<TTools>, 'onMessagesChange' | 'onLoadingChange' | 'onErrorChange' | 'onStatusChange' | 'onSubscriptionChange' | 'onConnectionStatusChange' | 'onSessionGeneratingChange'> & {
|
|
21
|
+
live?: boolean;
|
|
22
|
+
};
|
|
21
23
|
export interface UseChatReturn<TTools extends ReadonlyArray<AnyClientTool> = any> {
|
|
22
24
|
/**
|
|
23
25
|
* Current messages in the conversation
|
|
@@ -77,4 +79,19 @@ export interface UseChatReturn<TTools extends ReadonlyArray<AnyClientTool> = any
|
|
|
77
79
|
* Current generation status
|
|
78
80
|
*/
|
|
79
81
|
status: ChatClientState;
|
|
82
|
+
/**
|
|
83
|
+
* Whether the subscription loop is currently active
|
|
84
|
+
*/
|
|
85
|
+
isSubscribed: boolean;
|
|
86
|
+
/**
|
|
87
|
+
* Current connection lifecycle status
|
|
88
|
+
*/
|
|
89
|
+
connectionStatus: ConnectionStatus;
|
|
90
|
+
/**
|
|
91
|
+
* Whether the shared session is actively generating.
|
|
92
|
+
* Derived from stream run events (RUN_STARTED / RUN_FINISHED / RUN_ERROR).
|
|
93
|
+
* Unlike `isLoading` (request-local), this reflects shared generation
|
|
94
|
+
* activity visible to all subscribers (e.g. across tabs/devices).
|
|
95
|
+
*/
|
|
96
|
+
sessionGenerating: boolean;
|
|
80
97
|
}
|
package/dist/esm/use-chat.js
CHANGED
|
@@ -9,6 +9,9 @@ function useChat(options) {
|
|
|
9
9
|
const [isLoading, setIsLoading] = useState(false);
|
|
10
10
|
const [error, setError] = useState(void 0);
|
|
11
11
|
const [status, setStatus] = useState("ready");
|
|
12
|
+
const [isSubscribed, setIsSubscribed] = useState(false);
|
|
13
|
+
const [connectionStatus, setConnectionStatus] = useState("disconnected");
|
|
14
|
+
const [sessionGenerating, setSessionGenerating] = useState(false);
|
|
12
15
|
const messagesRef = useRef(
|
|
13
16
|
options.initialMessages || []
|
|
14
17
|
);
|
|
@@ -47,6 +50,15 @@ function useChat(options) {
|
|
|
47
50
|
},
|
|
48
51
|
onErrorChange: (newError) => {
|
|
49
52
|
setError(newError);
|
|
53
|
+
},
|
|
54
|
+
onSubscriptionChange: (nextIsSubscribed) => {
|
|
55
|
+
setIsSubscribed(nextIsSubscribed);
|
|
56
|
+
},
|
|
57
|
+
onConnectionStatusChange: (nextStatus) => {
|
|
58
|
+
setConnectionStatus(nextStatus);
|
|
59
|
+
},
|
|
60
|
+
onSessionGeneratingChange: (isGenerating) => {
|
|
61
|
+
setSessionGenerating(isGenerating);
|
|
50
62
|
}
|
|
51
63
|
});
|
|
52
64
|
}, [clientId]);
|
|
@@ -58,11 +70,22 @@ function useChat(options) {
|
|
|
58
70
|
client.setMessagesManually(options.initialMessages);
|
|
59
71
|
}
|
|
60
72
|
}, []);
|
|
73
|
+
useEffect(() => {
|
|
74
|
+
if (options.live) {
|
|
75
|
+
client.subscribe();
|
|
76
|
+
} else {
|
|
77
|
+
client.unsubscribe();
|
|
78
|
+
}
|
|
79
|
+
}, [client, options.live]);
|
|
61
80
|
useEffect(() => {
|
|
62
81
|
return () => {
|
|
63
|
-
|
|
82
|
+
if (options.live) {
|
|
83
|
+
client.unsubscribe();
|
|
84
|
+
} else {
|
|
85
|
+
client.stop();
|
|
86
|
+
}
|
|
64
87
|
};
|
|
65
|
-
}, [client]);
|
|
88
|
+
}, [client, options.live]);
|
|
66
89
|
const sendMessage = useCallback(
|
|
67
90
|
async (content) => {
|
|
68
91
|
await client.sendMessage(content);
|
|
@@ -111,6 +134,9 @@ function useChat(options) {
|
|
|
111
134
|
isLoading,
|
|
112
135
|
error,
|
|
113
136
|
status,
|
|
137
|
+
isSubscribed,
|
|
138
|
+
connectionStatus,
|
|
139
|
+
sessionGenerating,
|
|
114
140
|
setMessages: setMessagesManually,
|
|
115
141
|
clear,
|
|
116
142
|
addToolResult,
|
package/dist/esm/use-chat.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-chat.js","sources":["../../src/use-chat.ts"],"sourcesContent":["import { ChatClient } from '@tanstack/ai-client'\nimport {\n useCallback,\n useEffect,\n useId,\n useMemo,\n useRef,\n useState,\n} from 'preact/hooks'\nimport type { ChatClientState } from '@tanstack/ai-client'\nimport type { AnyClientTool, ModelMessage } from '@tanstack/ai'\n\nimport type {\n MultimodalContent,\n UIMessage,\n UseChatOptions,\n UseChatReturn,\n} from './types'\n\nexport function useChat<TTools extends ReadonlyArray<AnyClientTool> = any>(\n options: UseChatOptions<TTools>,\n): UseChatReturn<TTools> {\n const hookId = useId()\n const clientId = options.id || hookId\n\n const [messages, setMessages] = useState<Array<UIMessage<TTools>>>(\n options.initialMessages || [],\n )\n const [isLoading, setIsLoading] = useState(false)\n const [error, setError] = useState<Error | undefined>(undefined)\n const [status, setStatus] = useState<ChatClientState>('ready')\n\n // Track current messages in a ref to preserve them when client is recreated\n const messagesRef = useRef<Array<UIMessage<TTools>>>(\n options.initialMessages || [],\n )\n const isFirstMountRef = useRef(true)\n const optionsRef = useRef<UseChatOptions<TTools>>(options)\n\n optionsRef.current = options\n\n useEffect(() => {\n messagesRef.current = messages\n }, [messages])\n\n const client = useMemo(() => {\n // On first mount, use initialMessages. On subsequent recreations, preserve existing messages.\n const messagesToUse = isFirstMountRef.current\n ? options.initialMessages || []\n : messagesRef.current\n\n isFirstMountRef.current = false\n\n return new ChatClient({\n connection: optionsRef.current.connection,\n id: clientId,\n initialMessages: messagesToUse,\n body: optionsRef.current.body,\n onResponse: optionsRef.current.onResponse,\n onChunk: optionsRef.current.onChunk,\n onFinish: (message) => {\n optionsRef.current.onFinish?.(message)\n },\n onError: (err) => {\n optionsRef.current.onError?.(err)\n },\n tools: optionsRef.current.tools,\n streamProcessor: options.streamProcessor,\n onMessagesChange: (newMessages: Array<UIMessage<TTools>>) => {\n setMessages(newMessages)\n },\n onLoadingChange: (newIsLoading: boolean) => {\n setIsLoading(newIsLoading)\n },\n onStatusChange: (newStatus: ChatClientState) => {\n setStatus(newStatus)\n },\n onErrorChange: (newError: Error | undefined) => {\n setError(newError)\n },\n })\n }, [clientId])\n\n // Sync body changes to the client\n // This allows dynamic body values (like model selection) to be updated without recreating the client\n useEffect(() => {\n client.updateOptions({ body: options.body })\n }, [client, options.body])\n\n // Sync initial messages on mount only\n // Note: initialMessages are passed to ChatClient constructor, but we also\n // set them here to ensure Preact state is in sync\n useEffect(() => {\n if (\n options.initialMessages &&\n options.initialMessages.length &&\n !messages.length\n ) {\n client.setMessagesManually(options.initialMessages)\n }\n }, [])\n\n // Cleanup on unmount: stop any in-flight requests\n // Note: We only cleanup when client changes or component unmounts.\n // DO NOT include isLoading in dependencies - that would cause the cleanup\n // to run when isLoading changes, aborting continuation requests.\n useEffect(() => {\n return () => {\n client.stop()\n }\n }, [client])\n\n // Note: Callback options (onResponse, onChunk, onFinish, onError, onToolCall)\n // are captured at client creation time. Changes to these callbacks require\n // remounting the component or changing the connection to recreate the client.\n const sendMessage = useCallback(\n async (content: string | MultimodalContent) => {\n await client.sendMessage(content)\n },\n [client],\n )\n\n const append = useCallback(\n async (message: ModelMessage | UIMessage) => {\n await client.append(message)\n },\n [client],\n )\n\n const reload = useCallback(async () => {\n await client.reload()\n }, [client])\n\n const stop = useCallback(() => {\n client.stop()\n }, [client])\n\n const clear = useCallback(() => {\n client.clear()\n }, [client])\n\n const setMessagesManually = useCallback(\n (newMessages: Array<UIMessage<TTools>>) => {\n client.setMessagesManually(newMessages)\n },\n [client],\n )\n\n const addToolResult = useCallback(\n async (result: {\n toolCallId: string\n tool: string\n output: any\n state?: 'output-available' | 'output-error'\n errorText?: string\n }) => {\n await client.addToolResult(result)\n },\n [client],\n )\n\n const addToolApprovalResponse = useCallback(\n async (response: { id: string; approved: boolean }) => {\n await client.addToolApprovalResponse(response)\n },\n [client],\n )\n\n return {\n messages,\n sendMessage,\n append,\n reload,\n stop,\n isLoading,\n error,\n status,\n setMessages: setMessagesManually,\n clear,\n addToolResult,\n addToolApprovalResponse,\n }\n}\n"],"names":[],"mappings":";;AAmBO,SAAS,QACd,SACuB;AACvB,QAAM,SAAS,MAAA;AACf,QAAM,WAAW,QAAQ,MAAM;AAE/B,QAAM,CAAC,UAAU,WAAW,IAAI;AAAA,IAC9B,QAAQ,mBAAmB,CAAA;AAAA,EAAC;AAE9B,QAAM,CAAC,WAAW,YAAY,IAAI,SAAS,KAAK;AAChD,QAAM,CAAC,OAAO,QAAQ,IAAI,SAA4B,MAAS;AAC/D,QAAM,CAAC,QAAQ,SAAS,IAAI,SAA0B,OAAO;
|
|
1
|
+
{"version":3,"file":"use-chat.js","sources":["../../src/use-chat.ts"],"sourcesContent":["import { ChatClient } from '@tanstack/ai-client'\nimport {\n useCallback,\n useEffect,\n useId,\n useMemo,\n useRef,\n useState,\n} from 'preact/hooks'\nimport type { ChatClientState, ConnectionStatus } from '@tanstack/ai-client'\nimport type { AnyClientTool, ModelMessage } from '@tanstack/ai'\n\nimport type {\n MultimodalContent,\n UIMessage,\n UseChatOptions,\n UseChatReturn,\n} from './types'\n\nexport function useChat<TTools extends ReadonlyArray<AnyClientTool> = any>(\n options: UseChatOptions<TTools>,\n): UseChatReturn<TTools> {\n const hookId = useId()\n const clientId = options.id || hookId\n\n const [messages, setMessages] = useState<Array<UIMessage<TTools>>>(\n options.initialMessages || [],\n )\n const [isLoading, setIsLoading] = useState(false)\n const [error, setError] = useState<Error | undefined>(undefined)\n const [status, setStatus] = useState<ChatClientState>('ready')\n const [isSubscribed, setIsSubscribed] = useState(false)\n const [connectionStatus, setConnectionStatus] =\n useState<ConnectionStatus>('disconnected')\n const [sessionGenerating, setSessionGenerating] = useState(false)\n\n // Track current messages in a ref to preserve them when client is recreated\n const messagesRef = useRef<Array<UIMessage<TTools>>>(\n options.initialMessages || [],\n )\n const isFirstMountRef = useRef(true)\n const optionsRef = useRef<UseChatOptions<TTools>>(options)\n\n optionsRef.current = options\n\n useEffect(() => {\n messagesRef.current = messages\n }, [messages])\n\n const client = useMemo(() => {\n // On first mount, use initialMessages. On subsequent recreations, preserve existing messages.\n const messagesToUse = isFirstMountRef.current\n ? options.initialMessages || []\n : messagesRef.current\n\n isFirstMountRef.current = false\n\n return new ChatClient({\n connection: optionsRef.current.connection,\n id: clientId,\n initialMessages: messagesToUse,\n body: optionsRef.current.body,\n onResponse: optionsRef.current.onResponse,\n onChunk: optionsRef.current.onChunk,\n onFinish: (message) => {\n optionsRef.current.onFinish?.(message)\n },\n onError: (err) => {\n optionsRef.current.onError?.(err)\n },\n tools: optionsRef.current.tools,\n streamProcessor: options.streamProcessor,\n onMessagesChange: (newMessages: Array<UIMessage<TTools>>) => {\n setMessages(newMessages)\n },\n onLoadingChange: (newIsLoading: boolean) => {\n setIsLoading(newIsLoading)\n },\n onStatusChange: (newStatus: ChatClientState) => {\n setStatus(newStatus)\n },\n onErrorChange: (newError: Error | undefined) => {\n setError(newError)\n },\n onSubscriptionChange: (nextIsSubscribed: boolean) => {\n setIsSubscribed(nextIsSubscribed)\n },\n onConnectionStatusChange: (nextStatus: ConnectionStatus) => {\n setConnectionStatus(nextStatus)\n },\n onSessionGeneratingChange: (isGenerating: boolean) => {\n setSessionGenerating(isGenerating)\n },\n })\n }, [clientId])\n\n // Sync body changes to the client\n // This allows dynamic body values (like model selection) to be updated without recreating the client\n useEffect(() => {\n client.updateOptions({ body: options.body })\n }, [client, options.body])\n\n // Sync initial messages on mount only\n // Note: initialMessages are passed to ChatClient constructor, but we also\n // set them here to ensure Preact state is in sync\n useEffect(() => {\n if (\n options.initialMessages &&\n options.initialMessages.length &&\n !messages.length\n ) {\n client.setMessagesManually(options.initialMessages)\n }\n }, [])\n\n useEffect(() => {\n if (options.live) {\n client.subscribe()\n } else {\n client.unsubscribe()\n }\n }, [client, options.live])\n\n // Cleanup on unmount: stop any in-flight requests\n // Note: We only cleanup when client changes or component unmounts.\n // DO NOT include isLoading in dependencies - that would cause the cleanup\n // to run when isLoading changes, aborting continuation requests.\n useEffect(() => {\n return () => {\n if (options.live) {\n client.unsubscribe()\n } else {\n client.stop()\n }\n }\n }, [client, options.live])\n\n // Note: Callback options (onResponse, onChunk, onFinish, onError, onToolCall)\n // are captured at client creation time. Changes to these callbacks require\n // remounting the component or changing the connection to recreate the client.\n const sendMessage = useCallback(\n async (content: string | MultimodalContent) => {\n await client.sendMessage(content)\n },\n [client],\n )\n\n const append = useCallback(\n async (message: ModelMessage | UIMessage) => {\n await client.append(message)\n },\n [client],\n )\n\n const reload = useCallback(async () => {\n await client.reload()\n }, [client])\n\n const stop = useCallback(() => {\n client.stop()\n }, [client])\n\n const clear = useCallback(() => {\n client.clear()\n }, [client])\n\n const setMessagesManually = useCallback(\n (newMessages: Array<UIMessage<TTools>>) => {\n client.setMessagesManually(newMessages)\n },\n [client],\n )\n\n const addToolResult = useCallback(\n async (result: {\n toolCallId: string\n tool: string\n output: any\n state?: 'output-available' | 'output-error'\n errorText?: string\n }) => {\n await client.addToolResult(result)\n },\n [client],\n )\n\n const addToolApprovalResponse = useCallback(\n async (response: { id: string; approved: boolean }) => {\n await client.addToolApprovalResponse(response)\n },\n [client],\n )\n\n return {\n messages,\n sendMessage,\n append,\n reload,\n stop,\n isLoading,\n error,\n status,\n isSubscribed,\n connectionStatus,\n sessionGenerating,\n setMessages: setMessagesManually,\n clear,\n addToolResult,\n addToolApprovalResponse,\n }\n}\n"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/ai-preact",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.7",
|
|
4
4
|
"description": "Preact hooks for TanStack AI",
|
|
5
5
|
"author": "",
|
|
6
6
|
"license": "MIT",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"streaming"
|
|
32
32
|
],
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@tanstack/ai-client": "0.
|
|
34
|
+
"@tanstack/ai-client": "0.7.2"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@testing-library/preact": "^3.2.4",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
},
|
|
43
43
|
"peerDependencies": {
|
|
44
44
|
"preact": ">=10.11.0",
|
|
45
|
-
"@tanstack/ai": "^0.
|
|
45
|
+
"@tanstack/ai": "^0.8.1"
|
|
46
46
|
},
|
|
47
47
|
"scripts": {
|
|
48
48
|
"clean": "premove ./build ./dist",
|
package/src/types.ts
CHANGED
|
@@ -3,6 +3,7 @@ import type {
|
|
|
3
3
|
ChatClientOptions,
|
|
4
4
|
ChatClientState,
|
|
5
5
|
ChatRequestBody,
|
|
6
|
+
ConnectionStatus,
|
|
6
7
|
MultimodalContent,
|
|
7
8
|
UIMessage,
|
|
8
9
|
} from '@tanstack/ai-client'
|
|
@@ -29,8 +30,16 @@ export type { ChatRequestBody, MultimodalContent, UIMessage }
|
|
|
29
30
|
export type UseChatOptions<TTools extends ReadonlyArray<AnyClientTool> = any> =
|
|
30
31
|
Omit<
|
|
31
32
|
ChatClientOptions<TTools>,
|
|
32
|
-
|
|
33
|
-
|
|
33
|
+
| 'onMessagesChange'
|
|
34
|
+
| 'onLoadingChange'
|
|
35
|
+
| 'onErrorChange'
|
|
36
|
+
| 'onStatusChange'
|
|
37
|
+
| 'onSubscriptionChange'
|
|
38
|
+
| 'onConnectionStatusChange'
|
|
39
|
+
| 'onSessionGeneratingChange'
|
|
40
|
+
> & {
|
|
41
|
+
live?: boolean
|
|
42
|
+
}
|
|
34
43
|
|
|
35
44
|
export interface UseChatReturn<
|
|
36
45
|
TTools extends ReadonlyArray<AnyClientTool> = any,
|
|
@@ -104,4 +113,22 @@ export interface UseChatReturn<
|
|
|
104
113
|
* Current generation status
|
|
105
114
|
*/
|
|
106
115
|
status: ChatClientState
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Whether the subscription loop is currently active
|
|
119
|
+
*/
|
|
120
|
+
isSubscribed: boolean
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Current connection lifecycle status
|
|
124
|
+
*/
|
|
125
|
+
connectionStatus: ConnectionStatus
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Whether the shared session is actively generating.
|
|
129
|
+
* Derived from stream run events (RUN_STARTED / RUN_FINISHED / RUN_ERROR).
|
|
130
|
+
* Unlike `isLoading` (request-local), this reflects shared generation
|
|
131
|
+
* activity visible to all subscribers (e.g. across tabs/devices).
|
|
132
|
+
*/
|
|
133
|
+
sessionGenerating: boolean
|
|
107
134
|
}
|
package/src/use-chat.ts
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
useRef,
|
|
8
8
|
useState,
|
|
9
9
|
} from 'preact/hooks'
|
|
10
|
-
import type { ChatClientState } from '@tanstack/ai-client'
|
|
10
|
+
import type { ChatClientState, ConnectionStatus } from '@tanstack/ai-client'
|
|
11
11
|
import type { AnyClientTool, ModelMessage } from '@tanstack/ai'
|
|
12
12
|
|
|
13
13
|
import type {
|
|
@@ -29,6 +29,10 @@ export function useChat<TTools extends ReadonlyArray<AnyClientTool> = any>(
|
|
|
29
29
|
const [isLoading, setIsLoading] = useState(false)
|
|
30
30
|
const [error, setError] = useState<Error | undefined>(undefined)
|
|
31
31
|
const [status, setStatus] = useState<ChatClientState>('ready')
|
|
32
|
+
const [isSubscribed, setIsSubscribed] = useState(false)
|
|
33
|
+
const [connectionStatus, setConnectionStatus] =
|
|
34
|
+
useState<ConnectionStatus>('disconnected')
|
|
35
|
+
const [sessionGenerating, setSessionGenerating] = useState(false)
|
|
32
36
|
|
|
33
37
|
// Track current messages in a ref to preserve them when client is recreated
|
|
34
38
|
const messagesRef = useRef<Array<UIMessage<TTools>>>(
|
|
@@ -78,6 +82,15 @@ export function useChat<TTools extends ReadonlyArray<AnyClientTool> = any>(
|
|
|
78
82
|
onErrorChange: (newError: Error | undefined) => {
|
|
79
83
|
setError(newError)
|
|
80
84
|
},
|
|
85
|
+
onSubscriptionChange: (nextIsSubscribed: boolean) => {
|
|
86
|
+
setIsSubscribed(nextIsSubscribed)
|
|
87
|
+
},
|
|
88
|
+
onConnectionStatusChange: (nextStatus: ConnectionStatus) => {
|
|
89
|
+
setConnectionStatus(nextStatus)
|
|
90
|
+
},
|
|
91
|
+
onSessionGeneratingChange: (isGenerating: boolean) => {
|
|
92
|
+
setSessionGenerating(isGenerating)
|
|
93
|
+
},
|
|
81
94
|
})
|
|
82
95
|
}, [clientId])
|
|
83
96
|
|
|
@@ -100,15 +113,27 @@ export function useChat<TTools extends ReadonlyArray<AnyClientTool> = any>(
|
|
|
100
113
|
}
|
|
101
114
|
}, [])
|
|
102
115
|
|
|
116
|
+
useEffect(() => {
|
|
117
|
+
if (options.live) {
|
|
118
|
+
client.subscribe()
|
|
119
|
+
} else {
|
|
120
|
+
client.unsubscribe()
|
|
121
|
+
}
|
|
122
|
+
}, [client, options.live])
|
|
123
|
+
|
|
103
124
|
// Cleanup on unmount: stop any in-flight requests
|
|
104
125
|
// Note: We only cleanup when client changes or component unmounts.
|
|
105
126
|
// DO NOT include isLoading in dependencies - that would cause the cleanup
|
|
106
127
|
// to run when isLoading changes, aborting continuation requests.
|
|
107
128
|
useEffect(() => {
|
|
108
129
|
return () => {
|
|
109
|
-
|
|
130
|
+
if (options.live) {
|
|
131
|
+
client.unsubscribe()
|
|
132
|
+
} else {
|
|
133
|
+
client.stop()
|
|
134
|
+
}
|
|
110
135
|
}
|
|
111
|
-
}, [client])
|
|
136
|
+
}, [client, options.live])
|
|
112
137
|
|
|
113
138
|
// Note: Callback options (onResponse, onChunk, onFinish, onError, onToolCall)
|
|
114
139
|
// are captured at client creation time. Changes to these callbacks require
|
|
@@ -175,6 +200,9 @@ export function useChat<TTools extends ReadonlyArray<AnyClientTool> = any>(
|
|
|
175
200
|
isLoading,
|
|
176
201
|
error,
|
|
177
202
|
status,
|
|
203
|
+
isSubscribed,
|
|
204
|
+
connectionStatus,
|
|
205
|
+
sessionGenerating,
|
|
178
206
|
setMessages: setMessagesManually,
|
|
179
207
|
clear,
|
|
180
208
|
addToolResult,
|