@tanstack/ai-react 0.18.1 → 0.19.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.
Files changed (49) hide show
  1. package/README.md +15 -1
  2. package/dist/esm/index.d.ts +1 -1
  3. package/dist/esm/index.js +2 -23
  4. package/dist/esm/mcp-app-resource.js +45 -45
  5. package/dist/esm/mcp-app-resource.js.map +1 -1
  6. package/dist/esm/mcp-apps.js +1 -4
  7. package/dist/esm/types.d.ts +29 -3
  8. package/dist/esm/use-audio-recorder.js +41 -46
  9. package/dist/esm/use-audio-recorder.js.map +1 -1
  10. package/dist/esm/use-chat.js +330 -275
  11. package/dist/esm/use-chat.js.map +1 -1
  12. package/dist/esm/use-generate-audio.d.ts +50 -4
  13. package/dist/esm/use-generate-audio.js +47 -23
  14. package/dist/esm/use-generate-audio.js.map +1 -1
  15. package/dist/esm/use-generate-image.d.ts +50 -4
  16. package/dist/esm/use-generate-image.js +49 -23
  17. package/dist/esm/use-generate-image.js.map +1 -1
  18. package/dist/esm/use-generate-speech.d.ts +50 -4
  19. package/dist/esm/use-generate-speech.js +43 -23
  20. package/dist/esm/use-generate-speech.js.map +1 -1
  21. package/dist/esm/use-generate-video.d.ts +50 -4
  22. package/dist/esm/use-generate-video.js +141 -103
  23. package/dist/esm/use-generate-video.js.map +1 -1
  24. package/dist/esm/use-generation.d.ts +59 -6
  25. package/dist/esm/use-generation.js +113 -90
  26. package/dist/esm/use-generation.js.map +1 -1
  27. package/dist/esm/use-mcp-app-bridge.js +46 -23
  28. package/dist/esm/use-mcp-app-bridge.js.map +1 -1
  29. package/dist/esm/use-realtime-chat.js +185 -185
  30. package/dist/esm/use-realtime-chat.js.map +1 -1
  31. package/dist/esm/use-summarize.d.ts +50 -4
  32. package/dist/esm/use-summarize.js +46 -23
  33. package/dist/esm/use-summarize.js.map +1 -1
  34. package/dist/esm/use-transcription.d.ts +50 -4
  35. package/dist/esm/use-transcription.js +51 -20
  36. package/dist/esm/use-transcription.js.map +1 -1
  37. package/package.json +7 -7
  38. package/src/index.ts +10 -0
  39. package/src/types.ts +44 -0
  40. package/src/use-chat.ts +221 -35
  41. package/src/use-generate-audio.ts +64 -17
  42. package/src/use-generate-image.ts +64 -17
  43. package/src/use-generate-speech.ts +64 -17
  44. package/src/use-generate-video.ts +99 -18
  45. package/src/use-generation.ts +112 -18
  46. package/src/use-summarize.ts +64 -17
  47. package/src/use-transcription.ts +60 -18
  48. package/dist/esm/index.js.map +0 -1
  49. package/dist/esm/mcp-apps.js.map +0 -1
@@ -1,279 +1,334 @@
1
1
  import { ChatClient } from "@tanstack/ai-client";
2
2
  import { createChatDevtoolsBridge } from "@tanstack/ai-client/devtools";
3
- import { useId, useState, useRef, useMemo, useEffect, useCallback } from "react";
3
+ import { useCallback, useEffect, useId, useMemo, useRef, useState } from "react";
4
+ //#region src/use-chat.ts
5
+ var EMPTY_INTERRUPTS = Object.freeze([]);
6
+ var EMPTY_INTERRUPT_ERRORS = Object.freeze([]);
4
7
  function useChat(options) {
5
- const hookId = useId();
6
- const clientId = options.id || hookId;
7
- const [messages, setMessages] = useState(
8
- options.initialMessages || []
9
- );
10
- const [isLoading, setIsLoading] = useState(false);
11
- const [error, setError] = useState(void 0);
12
- const [status, setStatus] = useState("ready");
13
- const [isSubscribed, setIsSubscribed] = useState(false);
14
- const [connectionStatus, setConnectionStatus] = useState("disconnected");
15
- const [sessionGenerating, setSessionGenerating] = useState(false);
16
- const [queue, setQueue] = useState([]);
17
- const messagesRef = useRef(
18
- options.initialMessages || []
19
- );
20
- const isFirstMountRef = useRef(true);
21
- const activeClientRef = useRef(null);
22
- const cleanupInvalidationRef = useRef(
23
- null
24
- );
25
- messagesRef.current = messages;
26
- const optionsRef = useRef(options);
27
- optionsRef.current = options;
28
- const client = useMemo(() => {
29
- const messagesToUse = options.initialMessages || [];
30
- isFirstMountRef.current = false;
31
- const initialOptions = optionsRef.current;
32
- const transport = initialOptions.connection ? { connection: initialOptions.connection } : { fetcher: initialOptions.fetcher };
33
- const instance = new ChatClient({
34
- devtoolsBridgeFactory: createChatDevtoolsBridge,
35
- ...transport,
36
- id: clientId,
37
- initialMessages: messagesToUse,
38
- ...initialOptions.body !== void 0 && { body: initialOptions.body },
39
- ...initialOptions.threadId !== void 0 && {
40
- threadId: initialOptions.threadId
41
- },
42
- ...initialOptions.forwardedProps !== void 0 && {
43
- forwardedProps: initialOptions.forwardedProps
44
- },
45
- ...initialOptions.persistence !== void 0 && {
46
- persistence: initialOptions.persistence
47
- },
48
- ...initialOptions.context !== void 0 && {
49
- context: initialOptions.context
50
- },
51
- devtools: {
52
- ...initialOptions.devtools,
53
- framework: "react",
54
- hookName: "useChat",
55
- outputKind: initialOptions.outputSchema ? "structured" : "chat"
56
- },
57
- onResponse: (response) => {
58
- if (activeClientRef.current !== instance) return;
59
- void optionsRef.current.onResponse?.(response);
60
- },
61
- onChunk: (chunk) => {
62
- if (activeClientRef.current !== instance) return;
63
- optionsRef.current.onChunk?.(chunk);
64
- },
65
- onFinish: (message) => {
66
- if (activeClientRef.current !== instance) return;
67
- optionsRef.current.onFinish?.(message);
68
- },
69
- onError: (error2) => {
70
- if (activeClientRef.current !== instance) return;
71
- optionsRef.current.onError?.(error2);
72
- },
73
- ...initialOptions.tools !== void 0 && {
74
- tools: initialOptions.tools
75
- },
76
- onCustomEvent: (eventType, data, context) => {
77
- if (activeClientRef.current !== instance) return;
78
- optionsRef.current.onCustomEvent?.(eventType, data, context);
79
- },
80
- ...options.streamProcessor !== void 0 && {
81
- streamProcessor: options.streamProcessor
82
- },
83
- onMessagesChange: (newMessages) => {
84
- if (activeClientRef.current !== instance) return;
85
- setMessages(newMessages);
86
- },
87
- onLoadingChange: (newIsLoading) => {
88
- if (activeClientRef.current !== instance) return;
89
- setIsLoading(newIsLoading);
90
- },
91
- onErrorChange: (newError) => {
92
- if (activeClientRef.current !== instance) return;
93
- setError(newError);
94
- },
95
- onStatusChange: (status2) => {
96
- if (activeClientRef.current !== instance) return;
97
- setStatus(status2);
98
- },
99
- onSubscriptionChange: (nextIsSubscribed) => {
100
- if (activeClientRef.current !== instance) return;
101
- setIsSubscribed(nextIsSubscribed);
102
- },
103
- onConnectionStatusChange: (nextStatus) => {
104
- if (activeClientRef.current !== instance) return;
105
- setConnectionStatus(nextStatus);
106
- },
107
- onSessionGeneratingChange: (isGenerating) => {
108
- if (activeClientRef.current !== instance) return;
109
- setSessionGenerating(isGenerating);
110
- },
111
- ...optionsRef.current.queue !== void 0 && {
112
- queue: optionsRef.current.queue
113
- },
114
- onQueueChange: (nextQueue) => {
115
- if (activeClientRef.current !== instance) return;
116
- setQueue(nextQueue);
117
- }
118
- });
119
- activeClientRef.current = instance;
120
- return instance;
121
- }, [clientId]);
122
- useEffect(() => {
123
- const clientMessages = client.getMessages();
124
- if (clientMessages !== messagesRef.current) {
125
- setMessages(clientMessages);
126
- }
127
- }, [client]);
128
- useEffect(() => {
129
- client.updateOptions({ body: options.body });
130
- }, [client, options.body]);
131
- useEffect(() => {
132
- if (options.forwardedProps !== void 0) {
133
- client.updateOptions({ forwardedProps: options.forwardedProps });
134
- }
135
- }, [client, options.forwardedProps]);
136
- useEffect(() => {
137
- if (options.tools !== void 0) {
138
- client.updateOptions({ tools: options.tools });
139
- }
140
- }, [client, options.tools]);
141
- useEffect(() => {
142
- client.updateOptions({ context: options.context });
143
- }, [client, options.context]);
144
- useEffect(() => {
145
- if (options.queue !== void 0) {
146
- client.updateOptions({ queue: options.queue });
147
- }
148
- }, [client, options.queue]);
149
- useEffect(() => {
150
- if (options.live) {
151
- client.subscribe();
152
- } else {
153
- client.unsubscribe();
154
- }
155
- }, [client, options.live]);
156
- useEffect(() => {
157
- if (cleanupInvalidationRef.current) {
158
- clearTimeout(cleanupInvalidationRef.current);
159
- cleanupInvalidationRef.current = null;
160
- }
161
- activeClientRef.current = client;
162
- client.mountDevtools();
163
- return () => {
164
- cleanupInvalidationRef.current = setTimeout(() => {
165
- if (activeClientRef.current === client) {
166
- activeClientRef.current = null;
167
- }
168
- cleanupInvalidationRef.current = null;
169
- }, 0);
170
- if (optionsRef.current.live) {
171
- client.unsubscribe();
172
- } else {
173
- client.stop();
174
- }
175
- client.dispose();
176
- };
177
- }, [client]);
178
- const sendMessage = useCallback(
179
- async (content, sendOptions) => {
180
- await client.sendMessage(content, void 0, sendOptions);
181
- },
182
- [client]
183
- );
184
- const cancelQueued = useCallback(
185
- (id) => {
186
- client.cancelQueued(id);
187
- },
188
- [client]
189
- );
190
- const append = useCallback(
191
- async (message) => {
192
- await client.append(message);
193
- },
194
- [client]
195
- );
196
- const reload = useCallback(async () => {
197
- await client.reload();
198
- }, [client]);
199
- const stop = useCallback(() => {
200
- client.stop();
201
- }, [client]);
202
- const clear = useCallback(() => {
203
- client.clear();
204
- }, [client]);
205
- const setMessagesManually = useCallback(
206
- (newMessages) => {
207
- client.setMessagesManually(newMessages);
208
- },
209
- [client]
210
- );
211
- const addToolResult = useCallback(
212
- async (result) => {
213
- await client.addToolResult(result);
214
- },
215
- [client]
216
- );
217
- const addToolApprovalResponse = useCallback(
218
- async (response) => {
219
- await client.addToolApprovalResponse(response);
220
- },
221
- [client]
222
- );
223
- const renderedMessages = client.getMessages();
224
- const activeStructuredPart = useMemo(() => {
225
- let lastUserIndex = -1;
226
- for (let i = renderedMessages.length - 1; i >= 0; i--) {
227
- if (renderedMessages[i]?.role === "user") {
228
- lastUserIndex = i;
229
- break;
230
- }
231
- }
232
- if (lastUserIndex === -1) return null;
233
- for (let i = renderedMessages.length - 1; i > lastUserIndex; i--) {
234
- const m = renderedMessages[i];
235
- if (m?.role !== "assistant") continue;
236
- const part = m.parts.find(
237
- (p) => p.type === "structured-output"
238
- );
239
- if (part) return part;
240
- }
241
- return null;
242
- }, [renderedMessages]);
243
- const partial = useMemo(() => {
244
- if (!activeStructuredPart) return {};
245
- const v = activeStructuredPart.partial ?? activeStructuredPart.data;
246
- return v ?? {};
247
- }, [activeStructuredPart]);
248
- const final = useMemo(() => {
249
- if (!activeStructuredPart || activeStructuredPart.status !== "complete") {
250
- return null;
251
- }
252
- return activeStructuredPart.data;
253
- }, [activeStructuredPart]);
254
- return {
255
- messages: renderedMessages,
256
- sendMessage,
257
- append,
258
- reload,
259
- stop,
260
- isLoading,
261
- error,
262
- status,
263
- isSubscribed,
264
- connectionStatus,
265
- sessionGenerating,
266
- setMessages: setMessagesManually,
267
- clear,
268
- addToolResult,
269
- addToolApprovalResponse,
270
- queue,
271
- cancelQueued,
272
- partial,
273
- final
274
- };
8
+ const hookId = useId();
9
+ const clientId = options.threadId ?? hookId;
10
+ const [messages, setMessages] = useState(options.initialMessages || []);
11
+ const [isLoading, setIsLoading] = useState(false);
12
+ const [error, setError] = useState(void 0);
13
+ const [status, setStatus] = useState("ready");
14
+ const [isSubscribed, setIsSubscribed] = useState(false);
15
+ const [connectionStatus, setConnectionStatus] = useState("disconnected");
16
+ const [sessionGenerating, setSessionGenerating] = useState(false);
17
+ const [queue, setQueue] = useState([]);
18
+ const [runId, setRunId] = useState(null);
19
+ const [interruptState, setInterruptState] = useState(() => ({
20
+ interrupts: EMPTY_INTERRUPTS,
21
+ pendingInterrupts: EMPTY_INTERRUPTS,
22
+ interruptErrors: EMPTY_INTERRUPT_ERRORS,
23
+ resuming: false
24
+ }));
25
+ const messagesRef = useRef(options.initialMessages || []);
26
+ const isFirstMountRef = useRef(true);
27
+ const subscribedRef = useRef(false);
28
+ const activeClientRef = useRef(null);
29
+ const cleanupInvalidationRef = useRef(null);
30
+ const cleanupDisposalRef = useRef(null);
31
+ messagesRef.current = messages;
32
+ const optionsRef = useRef(options);
33
+ optionsRef.current = options;
34
+ const syncResumeState = useCallback((target) => {
35
+ if (!target) return;
36
+ setRunId(target.getCurrentRunId());
37
+ setInterruptState(target.getInterruptState());
38
+ }, []);
39
+ const client = useMemo(() => {
40
+ const messagesToUse = options.initialMessages || [];
41
+ isFirstMountRef.current = false;
42
+ const initialOptions = optionsRef.current;
43
+ const transport = initialOptions.connection ? { connection: initialOptions.connection } : { fetcher: initialOptions.fetcher };
44
+ const instanceHolder = { current: void 0 };
45
+ const getActiveInstance = () => {
46
+ const currentInstance = instanceHolder.current;
47
+ if (!currentInstance || activeClientRef.current !== currentInstance) return;
48
+ return currentInstance;
49
+ };
50
+ const pendingInitializationErrors = [];
51
+ const instance = new ChatClient({
52
+ devtoolsBridgeFactory: createChatDevtoolsBridge,
53
+ ...transport,
54
+ initialMessages: messagesToUse,
55
+ ...initialOptions.body !== void 0 && { body: initialOptions.body },
56
+ ...initialOptions.threadId !== void 0 && { threadId: initialOptions.threadId },
57
+ ...initialOptions.forwardedProps !== void 0 && { forwardedProps: initialOptions.forwardedProps },
58
+ ...initialOptions.persistence !== void 0 && { persistence: initialOptions.persistence },
59
+ ...initialOptions.initialResumeSnapshot !== void 0 && { initialResumeSnapshot: initialOptions.initialResumeSnapshot },
60
+ ...initialOptions.context !== void 0 && { context: initialOptions.context },
61
+ devtools: {
62
+ ...initialOptions.devtools,
63
+ framework: "react",
64
+ hookName: "useChat",
65
+ outputKind: initialOptions.outputSchema ? "structured" : "chat"
66
+ },
67
+ onResponse: (response) => {
68
+ if (!getActiveInstance()) return;
69
+ optionsRef.current.onResponse?.(response);
70
+ },
71
+ onChunk: (chunk) => {
72
+ if (!getActiveInstance()) return;
73
+ optionsRef.current.onChunk?.(chunk);
74
+ },
75
+ onFinish: (message) => {
76
+ if (!getActiveInstance()) return;
77
+ optionsRef.current.onFinish?.(message);
78
+ },
79
+ onError: (error) => {
80
+ const currentInstance = instanceHolder.current;
81
+ if (!currentInstance) {
82
+ pendingInitializationErrors.push(error);
83
+ return;
84
+ }
85
+ if (activeClientRef.current !== currentInstance) return;
86
+ optionsRef.current.onError?.(error);
87
+ },
88
+ ...initialOptions.tools !== void 0 && { tools: initialOptions.tools },
89
+ onCustomEvent: (eventType, data, context) => {
90
+ if (!getActiveInstance()) return;
91
+ optionsRef.current.onCustomEvent?.(eventType, data, context);
92
+ },
93
+ ...options.streamProcessor !== void 0 && { streamProcessor: options.streamProcessor },
94
+ onMessagesChange: (newMessages) => {
95
+ if (!getActiveInstance()) return;
96
+ setMessages(newMessages);
97
+ },
98
+ onLoadingChange: (newIsLoading) => {
99
+ const currentInstance = getActiveInstance();
100
+ if (!currentInstance) return;
101
+ setIsLoading(newIsLoading);
102
+ syncResumeState(currentInstance);
103
+ },
104
+ onErrorChange: (newError) => {
105
+ if (!getActiveInstance()) return;
106
+ setError(newError);
107
+ },
108
+ onStatusChange: (status) => {
109
+ if (!getActiveInstance()) return;
110
+ setStatus(status);
111
+ },
112
+ onSubscriptionChange: (nextIsSubscribed) => {
113
+ if (!getActiveInstance()) return;
114
+ setIsSubscribed(nextIsSubscribed);
115
+ },
116
+ onConnectionStatusChange: (nextStatus) => {
117
+ if (!getActiveInstance()) return;
118
+ setConnectionStatus(nextStatus);
119
+ },
120
+ onSessionGeneratingChange: (isGenerating) => {
121
+ if (!getActiveInstance()) return;
122
+ setSessionGenerating(isGenerating);
123
+ },
124
+ ...optionsRef.current.queue !== void 0 && { queue: optionsRef.current.queue },
125
+ onQueueChange: (nextQueue) => {
126
+ if (activeClientRef.current !== instance) return;
127
+ setQueue(nextQueue);
128
+ },
129
+ onRunIdChange: (nextRunId) => {
130
+ if (!getActiveInstance()) return;
131
+ setRunId(nextRunId);
132
+ },
133
+ onResumeStateChange: (_nextResumeState, nextPendingInterrupts) => {
134
+ if (!getActiveInstance()) return;
135
+ setInterruptState((current) => ({
136
+ ...current,
137
+ interrupts: nextPendingInterrupts,
138
+ pendingInterrupts: nextPendingInterrupts
139
+ }));
140
+ },
141
+ onInterruptStateChange: (nextInterruptState) => {
142
+ if (!getActiveInstance()) return;
143
+ setInterruptState(nextInterruptState);
144
+ optionsRef.current.onInterruptStateChange?.(nextInterruptState);
145
+ }
146
+ });
147
+ instanceHolder.current = instance;
148
+ activeClientRef.current = instance;
149
+ for (const error of pendingInitializationErrors) {
150
+ if (activeClientRef.current !== instance) break;
151
+ optionsRef.current.onError?.(error);
152
+ }
153
+ return instance;
154
+ }, [clientId, syncResumeState]);
155
+ useEffect(() => {
156
+ const clientMessages = client.getMessages();
157
+ if (clientMessages !== messagesRef.current) setMessages(clientMessages);
158
+ }, [client]);
159
+ useEffect(() => {
160
+ client.updateOptions({ body: options.body });
161
+ }, [client, options.body]);
162
+ useEffect(() => {
163
+ if (options.forwardedProps !== void 0) client.updateOptions({ forwardedProps: options.forwardedProps });
164
+ }, [client, options.forwardedProps]);
165
+ useEffect(() => {
166
+ if (options.tools !== void 0) client.updateOptions({ tools: options.tools });
167
+ }, [client, options.tools]);
168
+ useEffect(() => {
169
+ client.updateOptions({ context: options.context });
170
+ }, [client, options.context]);
171
+ useEffect(() => {
172
+ if (options.queue !== void 0) client.updateOptions({ queue: options.queue });
173
+ }, [client, options.queue]);
174
+ useEffect(() => {
175
+ if (options.live) {
176
+ client.subscribe();
177
+ subscribedRef.current = true;
178
+ } else if (subscribedRef.current) {
179
+ client.unsubscribe();
180
+ subscribedRef.current = false;
181
+ }
182
+ }, [client, options.live]);
183
+ useEffect(() => {
184
+ client.attach();
185
+ return () => {
186
+ client.detach();
187
+ };
188
+ }, [client]);
189
+ useEffect(() => {
190
+ if (cleanupDisposalRef.current?.client === client) {
191
+ clearTimeout(cleanupDisposalRef.current.timeout);
192
+ cleanupDisposalRef.current = null;
193
+ }
194
+ if (cleanupInvalidationRef.current) {
195
+ clearTimeout(cleanupInvalidationRef.current);
196
+ cleanupInvalidationRef.current = null;
197
+ }
198
+ activeClientRef.current = client;
199
+ client.mountDevtools();
200
+ syncResumeState(client);
201
+ return () => {
202
+ cleanupInvalidationRef.current = setTimeout(() => {
203
+ if (activeClientRef.current === client) activeClientRef.current = null;
204
+ cleanupInvalidationRef.current = null;
205
+ }, 0);
206
+ const disposal = {
207
+ client,
208
+ timeout: setTimeout(() => {
209
+ if (optionsRef.current.live) client.unsubscribe();
210
+ else client.stop();
211
+ client.dispose();
212
+ if (cleanupDisposalRef.current === disposal) cleanupDisposalRef.current = null;
213
+ }, 0)
214
+ };
215
+ cleanupDisposalRef.current = disposal;
216
+ };
217
+ }, [client, syncResumeState]);
218
+ const sendMessage = useCallback(async (content, sendOptions) => {
219
+ try {
220
+ await client.sendMessage(content, void 0, sendOptions);
221
+ } finally {
222
+ syncResumeState(client);
223
+ }
224
+ }, [client, syncResumeState]);
225
+ const cancelQueued = useCallback((id) => {
226
+ client.cancelQueued(id);
227
+ }, [client, syncResumeState]);
228
+ const append = useCallback(async (message) => {
229
+ try {
230
+ await client.append(message);
231
+ } finally {
232
+ syncResumeState(client);
233
+ }
234
+ }, [client, syncResumeState]);
235
+ const reload = useCallback(async () => {
236
+ try {
237
+ await client.reload();
238
+ } finally {
239
+ syncResumeState(client);
240
+ }
241
+ }, [client, syncResumeState]);
242
+ const stop = useCallback(() => {
243
+ client.stop();
244
+ }, [client]);
245
+ const clear = useCallback(() => {
246
+ client.clear();
247
+ syncResumeState(client);
248
+ }, [client, syncResumeState]);
249
+ const setMessagesManually = useCallback((newMessages) => {
250
+ client.setMessagesManually(newMessages);
251
+ }, [client]);
252
+ const addToolResult = useCallback(async (result) => {
253
+ await client.addToolResult(result);
254
+ }, [client]);
255
+ const addToolApprovalResponse = useCallback(async (response) => {
256
+ await client.addToolApprovalResponse(response);
257
+ syncResumeState(client);
258
+ }, [client, syncResumeState]);
259
+ const resumeInterrupts = useCallback(async (resumeItems, state) => {
260
+ const result = await client.resumeInterrupts(resumeItems, state ?? void 0);
261
+ syncResumeState(client);
262
+ return result;
263
+ }, [client, syncResumeState]);
264
+ const resolveInterrupts = useCallback((resolution) => {
265
+ if (typeof resolution === "boolean") client.resolveInterrupts(resolution);
266
+ else client.resolveInterrupts(resolution);
267
+ }, [client]);
268
+ const cancelInterrupts = useCallback(() => {
269
+ client.cancelInterrupts();
270
+ }, [client]);
271
+ const retryInterrupts = useCallback(() => {
272
+ client.retryInterrupts();
273
+ }, [client]);
274
+ const resumeInterruptsUnsafe = useCallback((resumeItems, state) => client.resumeInterruptsUnsafe(resumeItems, state), [client]);
275
+ const renderedMessages = client.getMessages();
276
+ const activeStructuredPart = useMemo(() => {
277
+ let lastUserIndex = -1;
278
+ for (let i = renderedMessages.length - 1; i >= 0; i--) if (renderedMessages[i]?.role === "user") {
279
+ lastUserIndex = i;
280
+ break;
281
+ }
282
+ if (lastUserIndex === -1) return null;
283
+ for (let i = renderedMessages.length - 1; i > lastUserIndex; i--) {
284
+ const m = renderedMessages[i];
285
+ if (m?.role !== "assistant") continue;
286
+ const part = m.parts.find((p) => p.type === "structured-output");
287
+ if (part) return part;
288
+ }
289
+ return null;
290
+ }, [renderedMessages]);
291
+ const partial = useMemo(() => {
292
+ if (!activeStructuredPart) return {};
293
+ return activeStructuredPart.partial ?? activeStructuredPart.data ?? {};
294
+ }, [activeStructuredPart]);
295
+ const final = useMemo(() => {
296
+ if (!activeStructuredPart || activeStructuredPart.status !== "complete") return null;
297
+ return activeStructuredPart.data;
298
+ }, [activeStructuredPart]);
299
+ return {
300
+ messages: renderedMessages,
301
+ sendMessage,
302
+ append,
303
+ reload,
304
+ stop,
305
+ isLoading,
306
+ error,
307
+ status,
308
+ isSubscribed,
309
+ connectionStatus,
310
+ sessionGenerating,
311
+ setMessages: setMessagesManually,
312
+ clear,
313
+ addToolResult,
314
+ addToolApprovalResponse,
315
+ queue,
316
+ cancelQueued,
317
+ runId,
318
+ interrupts: interruptState.interrupts,
319
+ pendingInterrupts: interruptState.pendingInterrupts,
320
+ interruptErrors: interruptState.interruptErrors,
321
+ resuming: interruptState.resuming,
322
+ resolveInterrupts,
323
+ cancelInterrupts,
324
+ retryInterrupts,
325
+ resumeInterruptsUnsafe,
326
+ resumeInterrupts,
327
+ partial,
328
+ final
329
+ };
275
330
  }
276
- export {
277
- useChat
278
- };
279
- //# sourceMappingURL=use-chat.js.map
331
+ //#endregion
332
+ export { useChat };
333
+
334
+ //# sourceMappingURL=use-chat.js.map