@tanstack/ai-react 0.19.3 → 0.21.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/README.md +14 -5
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/index.js +2 -2
- package/dist/esm/types.d.ts +9 -9
- package/dist/esm/use-chat.d.ts +2 -2
- package/dist/esm/use-chat.js +92 -55
- package/dist/esm/use-chat.js.map +1 -1
- package/dist/esm/use-generate-audio.d.ts +3 -7
- package/dist/esm/use-generate-audio.js.map +1 -1
- package/dist/esm/use-generate-image.d.ts +3 -7
- package/dist/esm/use-generate-image.js.map +1 -1
- package/dist/esm/use-generate-speech.d.ts +3 -7
- package/dist/esm/use-generate-speech.js.map +1 -1
- package/dist/esm/use-generate-video.d.ts +3 -7
- package/dist/esm/use-generate-video.js +7 -3
- package/dist/esm/use-generate-video.js.map +1 -1
- package/dist/esm/use-generation.d.ts +3 -7
- package/dist/esm/use-generation.js +7 -3
- package/dist/esm/use-generation.js.map +1 -1
- package/dist/esm/use-summarize.d.ts +3 -7
- package/dist/esm/use-summarize.js.map +1 -1
- package/dist/esm/use-transcription.d.ts +3 -7
- package/dist/esm/use-transcription.js.map +1 -1
- package/package.json +4 -4
- package/src/index.ts +4 -0
- package/src/types.ts +19 -11
- package/src/use-chat.ts +132 -72
- package/src/use-generate-audio.ts +3 -7
- package/src/use-generate-image.ts +3 -7
- package/src/use-generate-speech.ts +3 -7
- package/src/use-generate-video.ts +22 -15
- package/src/use-generation.ts +21 -15
- package/src/use-summarize.ts +3 -7
- package/src/use-transcription.ts +3 -7
package/src/types.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
AnyClientTool,
|
|
3
|
+
InterruptDefinition,
|
|
3
4
|
InferSchemaType,
|
|
4
5
|
ModelMessage,
|
|
5
6
|
RunAgentResumeItem,
|
|
@@ -10,7 +11,7 @@ import type {
|
|
|
10
11
|
BoundInterrupts,
|
|
11
12
|
ChatClientOptions,
|
|
12
13
|
ChatClientState,
|
|
13
|
-
|
|
14
|
+
ResolvableChatInterrupt,
|
|
14
15
|
ChatInterruptState,
|
|
15
16
|
ChatRequestBody,
|
|
16
17
|
ChatResumeState,
|
|
@@ -83,8 +84,10 @@ export type UseChatOptions<
|
|
|
83
84
|
TTools extends ReadonlyArray<AnyClientTool> = any,
|
|
84
85
|
TSchema extends SchemaInput | undefined = undefined,
|
|
85
86
|
TContext = InferredClientContext<TTools>,
|
|
87
|
+
TInterrupts extends ReadonlyArray<InterruptDefinition<any, any, any, any>> =
|
|
88
|
+
readonly [],
|
|
86
89
|
> = DistributedOmit<
|
|
87
|
-
ChatClientOptions<TTools, TContext>,
|
|
90
|
+
ChatClientOptions<TTools, TContext, TInterrupts>,
|
|
88
91
|
| 'onMessagesChange'
|
|
89
92
|
| 'onLoadingChange'
|
|
90
93
|
| 'onErrorChange'
|
|
@@ -97,10 +100,6 @@ export type UseChatOptions<
|
|
|
97
100
|
| 'onRunIdChange'
|
|
98
101
|
| 'context'
|
|
99
102
|
| 'devtools'
|
|
100
|
-
// `id` is not a hook option: the hook's identity is its `threadId`, which is
|
|
101
|
-
// also the persistence key. Persist across reloads by passing a stable
|
|
102
|
-
// `threadId`; there is no separate id to set.
|
|
103
|
-
| 'id'
|
|
104
103
|
> & {
|
|
105
104
|
/** Display options for TanStack AI Devtools. */
|
|
106
105
|
devtools?: AIDevtoolsDisplayOptions
|
|
@@ -126,9 +125,12 @@ export type UseChatOptions<
|
|
|
126
125
|
export type UseChatReturn<
|
|
127
126
|
TTools extends ReadonlyArray<AnyClientTool> = any,
|
|
128
127
|
TSchema extends SchemaInput | undefined = undefined,
|
|
128
|
+
TInterrupts extends ReadonlyArray<InterruptDefinition<any, any, any, any>> =
|
|
129
|
+
readonly [],
|
|
129
130
|
> = BaseUseChatReturn<
|
|
130
131
|
TTools,
|
|
131
|
-
TSchema extends SchemaInput ? InferSchemaType<TSchema> : unknown
|
|
132
|
+
TSchema extends SchemaInput ? InferSchemaType<TSchema> : unknown,
|
|
133
|
+
TInterrupts
|
|
132
134
|
> &
|
|
133
135
|
(TSchema extends SchemaInput
|
|
134
136
|
? {
|
|
@@ -151,6 +153,8 @@ export type UseChatReturn<
|
|
|
151
153
|
interface BaseUseChatReturn<
|
|
152
154
|
TTools extends ReadonlyArray<AnyClientTool> = any,
|
|
153
155
|
TData = unknown,
|
|
156
|
+
TInterrupts extends ReadonlyArray<InterruptDefinition<any, any, any, any>> =
|
|
157
|
+
readonly [],
|
|
154
158
|
> {
|
|
155
159
|
/**
|
|
156
160
|
* Current messages in the conversation. When `outputSchema` is supplied,
|
|
@@ -218,14 +222,18 @@ interface BaseUseChatReturn<
|
|
|
218
222
|
* it, correlate a log line).
|
|
219
223
|
*/
|
|
220
224
|
runId: string | null
|
|
221
|
-
interrupts: BoundInterrupts<TTools>
|
|
225
|
+
interrupts: BoundInterrupts<TTools, TInterrupts>
|
|
222
226
|
/** @deprecated Use `interrupts`. */
|
|
223
|
-
pendingInterrupts: BoundInterrupts<TTools>
|
|
224
|
-
interruptErrors: ChatInterruptState<TTools>['interruptErrors']
|
|
227
|
+
pendingInterrupts: BoundInterrupts<TTools, TInterrupts>
|
|
228
|
+
interruptErrors: ChatInterruptState<TTools, TInterrupts>['interruptErrors']
|
|
225
229
|
resuming: boolean
|
|
226
230
|
resolveInterrupts: {
|
|
227
231
|
(approved: boolean): void
|
|
228
|
-
(
|
|
232
|
+
(
|
|
233
|
+
resolver: (
|
|
234
|
+
interrupt: ResolvableChatInterrupt<TTools, TInterrupts>,
|
|
235
|
+
) => undefined,
|
|
236
|
+
): void
|
|
229
237
|
}
|
|
230
238
|
cancelInterrupts: () => void
|
|
231
239
|
retryInterrupts: () => void
|
package/src/use-chat.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { useCallback, useEffect, useId, useMemo, useRef, useState } from 'react'
|
|
|
4
4
|
import type {
|
|
5
5
|
AnyClientTool,
|
|
6
6
|
InferSchemaType,
|
|
7
|
+
InterruptDefinition,
|
|
7
8
|
ModelMessage,
|
|
8
9
|
RunAgentResumeItem,
|
|
9
10
|
SchemaInput,
|
|
@@ -11,7 +12,7 @@ import type {
|
|
|
11
12
|
} from '@tanstack/ai/client'
|
|
12
13
|
import type {
|
|
13
14
|
ChatClientState,
|
|
14
|
-
|
|
15
|
+
ResolvableChatInterrupt,
|
|
15
16
|
ChatInterruptState,
|
|
16
17
|
ChatResumeState,
|
|
17
18
|
ConnectionStatus,
|
|
@@ -36,13 +37,15 @@ export function useChat<
|
|
|
36
37
|
const TTools extends ReadonlyArray<AnyClientTool> = any,
|
|
37
38
|
TSchema extends SchemaInput | undefined = undefined,
|
|
38
39
|
TContext = InferredClientContext<TTools>,
|
|
40
|
+
const TInterrupts extends ReadonlyArray<
|
|
41
|
+
InterruptDefinition<any, any, any, any>
|
|
42
|
+
> = readonly [],
|
|
39
43
|
>(
|
|
40
|
-
options: UseChatOptions<TTools, TSchema, TContext>,
|
|
41
|
-
): UseChatReturn<TTools, TSchema> {
|
|
42
|
-
// The hook's identity is its `threadId
|
|
43
|
-
//
|
|
44
|
-
//
|
|
45
|
-
// `threadId` is given (an ephemeral chat), never a persistence key.
|
|
44
|
+
options: UseChatOptions<TTools, TSchema, TContext, TInterrupts>,
|
|
45
|
+
): UseChatReturn<TTools, TSchema, TInterrupts> {
|
|
46
|
+
// The hook's identity is its `threadId`. Reload with the same `threadId`
|
|
47
|
+
// restores the same conversation. `hookId` is only a React recreation key
|
|
48
|
+
// when no `threadId` is given. It is never sent on the wire.
|
|
46
49
|
const hookId = useId()
|
|
47
50
|
const clientId = options.threadId ?? hookId
|
|
48
51
|
|
|
@@ -59,7 +62,7 @@ export function useChat<
|
|
|
59
62
|
const [queue, setQueue] = useState<Array<QueuedMessage>>([])
|
|
60
63
|
const [runId, setRunId] = useState<string | null>(null)
|
|
61
64
|
const [interruptState, setInterruptState] = useState<
|
|
62
|
-
ChatInterruptState<TTools>
|
|
65
|
+
ChatInterruptState<TTools, TInterrupts>
|
|
63
66
|
>(() => ({
|
|
64
67
|
interrupts: EMPTY_INTERRUPTS,
|
|
65
68
|
pendingInterrupts: EMPTY_INTERRUPTS,
|
|
@@ -89,7 +92,8 @@ export function useChat<
|
|
|
89
92
|
messagesRef.current = messages
|
|
90
93
|
|
|
91
94
|
// Track current options in a ref to avoid recreating client when options change
|
|
92
|
-
const optionsRef =
|
|
95
|
+
const optionsRef =
|
|
96
|
+
useRef<UseChatOptions<TTools, TSchema, TContext, TInterrupts>>(options)
|
|
93
97
|
optionsRef.current = options
|
|
94
98
|
|
|
95
99
|
const syncResumeState = useCallback((target: ChatClient | null) => {
|
|
@@ -99,7 +103,7 @@ export function useChat<
|
|
|
99
103
|
}, [])
|
|
100
104
|
|
|
101
105
|
// Create ChatClient instance with callbacks to sync state
|
|
102
|
-
const client = useMemo(() => {
|
|
106
|
+
const { client, initialization } = useMemo(() => {
|
|
103
107
|
const messagesToUse = options.initialMessages || []
|
|
104
108
|
isFirstMountRef.current = false
|
|
105
109
|
|
|
@@ -113,7 +117,7 @@ export function useChat<
|
|
|
113
117
|
: { fetcher: initialOptions.fetcher }
|
|
114
118
|
|
|
115
119
|
const instanceHolder: {
|
|
116
|
-
current: ChatClient<TTools, TContext> | undefined
|
|
120
|
+
current: ChatClient<TTools, TContext, TInterrupts> | undefined
|
|
117
121
|
} = { current: undefined }
|
|
118
122
|
const getActiveInstance = () => {
|
|
119
123
|
const currentInstance = instanceHolder.current
|
|
@@ -122,21 +126,43 @@ export function useChat<
|
|
|
122
126
|
}
|
|
123
127
|
return currentInstance
|
|
124
128
|
}
|
|
125
|
-
|
|
126
|
-
|
|
129
|
+
// ChatClient may publish while its constructor is running or while async
|
|
130
|
+
// persistence resolves before commit. Preserve those exact notifications
|
|
131
|
+
// until this render commits; invoking them here would run state setters and
|
|
132
|
+
// user callbacks for a client React may abandon.
|
|
133
|
+
const initializationState = {
|
|
134
|
+
ready: false,
|
|
135
|
+
callbacks: [] as Array<() => void>,
|
|
136
|
+
}
|
|
137
|
+
const runOrQueueForActiveInstance = (callback: () => void) => {
|
|
138
|
+
if (!initializationState.ready) {
|
|
139
|
+
initializationState.callbacks.push(callback)
|
|
140
|
+
return
|
|
141
|
+
}
|
|
142
|
+
const currentInstance = instanceHolder.current
|
|
143
|
+
if (!currentInstance || activeClientRef.current !== currentInstance)
|
|
144
|
+
return
|
|
145
|
+
callback()
|
|
146
|
+
}
|
|
147
|
+
const instance = new ChatClient<TTools, TContext, TInterrupts>({
|
|
127
148
|
devtoolsBridgeFactory: createChatDevtoolsBridge,
|
|
128
149
|
...transport,
|
|
129
150
|
initialMessages: messagesToUse,
|
|
151
|
+
...(typeof initialOptions.threadId === 'string' &&
|
|
152
|
+
initialOptions.persistence
|
|
153
|
+
? {
|
|
154
|
+
persistence: initialOptions.persistence,
|
|
155
|
+
threadId: initialOptions.threadId,
|
|
156
|
+
}
|
|
157
|
+
: {
|
|
158
|
+
...(initialOptions.threadId !== undefined && {
|
|
159
|
+
threadId: initialOptions.threadId,
|
|
160
|
+
}),
|
|
161
|
+
}),
|
|
130
162
|
...(initialOptions.body !== undefined && { body: initialOptions.body }),
|
|
131
|
-
...(initialOptions.threadId !== undefined && {
|
|
132
|
-
threadId: initialOptions.threadId,
|
|
133
|
-
}),
|
|
134
163
|
...(initialOptions.forwardedProps !== undefined && {
|
|
135
164
|
forwardedProps: initialOptions.forwardedProps,
|
|
136
165
|
}),
|
|
137
|
-
...(initialOptions.persistence !== undefined && {
|
|
138
|
-
persistence: initialOptions.persistence,
|
|
139
|
-
}),
|
|
140
166
|
...(initialOptions.initialResumeSnapshot !== undefined && {
|
|
141
167
|
initialResumeSnapshot: initialOptions.initialResumeSnapshot,
|
|
142
168
|
}),
|
|
@@ -150,100 +176,131 @@ export function useChat<
|
|
|
150
176
|
outputKind: initialOptions.outputSchema ? 'structured' : 'chat',
|
|
151
177
|
},
|
|
152
178
|
onResponse: (response) => {
|
|
179
|
+
// ChatClient awaits this return value. Queuing would drop the promise.
|
|
153
180
|
if (!getActiveInstance()) return
|
|
154
|
-
|
|
181
|
+
return optionsRef.current.onResponse?.(response)
|
|
155
182
|
},
|
|
156
183
|
onChunk: (chunk: StreamChunk) => {
|
|
157
|
-
|
|
158
|
-
|
|
184
|
+
runOrQueueForActiveInstance(() => {
|
|
185
|
+
optionsRef.current.onChunk?.(chunk)
|
|
186
|
+
})
|
|
159
187
|
},
|
|
160
188
|
onFinish: (message: UIMessage<TTools>) => {
|
|
161
|
-
|
|
162
|
-
|
|
189
|
+
runOrQueueForActiveInstance(() => {
|
|
190
|
+
optionsRef.current.onFinish?.(message)
|
|
191
|
+
})
|
|
163
192
|
},
|
|
164
193
|
onError: (error: Error) => {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
return
|
|
169
|
-
}
|
|
170
|
-
if (activeClientRef.current !== currentInstance) return
|
|
171
|
-
optionsRef.current.onError?.(error)
|
|
194
|
+
runOrQueueForActiveInstance(() => {
|
|
195
|
+
optionsRef.current.onError?.(error)
|
|
196
|
+
})
|
|
172
197
|
},
|
|
173
198
|
...(initialOptions.tools !== undefined && {
|
|
174
199
|
tools: initialOptions.tools,
|
|
175
200
|
}),
|
|
201
|
+
...(initialOptions.interrupts !== undefined && {
|
|
202
|
+
interrupts: initialOptions.interrupts,
|
|
203
|
+
}),
|
|
176
204
|
onCustomEvent: (eventType, data, context) => {
|
|
177
|
-
|
|
178
|
-
|
|
205
|
+
runOrQueueForActiveInstance(() => {
|
|
206
|
+
optionsRef.current.onCustomEvent?.(eventType, data, context)
|
|
207
|
+
})
|
|
179
208
|
},
|
|
180
209
|
...(options.streamProcessor !== undefined && {
|
|
181
210
|
streamProcessor: options.streamProcessor,
|
|
182
211
|
}),
|
|
183
212
|
onMessagesChange: (newMessages: Array<UIMessage<TTools>>) => {
|
|
184
|
-
|
|
185
|
-
|
|
213
|
+
runOrQueueForActiveInstance(() => {
|
|
214
|
+
setMessages(newMessages)
|
|
215
|
+
})
|
|
186
216
|
},
|
|
187
217
|
onLoadingChange: (newIsLoading: boolean) => {
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
218
|
+
runOrQueueForActiveInstance(() => {
|
|
219
|
+
const currentInstance = getActiveInstance()
|
|
220
|
+
if (!currentInstance) return
|
|
221
|
+
setIsLoading(newIsLoading)
|
|
222
|
+
syncResumeState(currentInstance)
|
|
223
|
+
})
|
|
192
224
|
},
|
|
193
225
|
onErrorChange: (newError: Error | undefined) => {
|
|
194
|
-
|
|
195
|
-
|
|
226
|
+
runOrQueueForActiveInstance(() => {
|
|
227
|
+
setError(newError)
|
|
228
|
+
})
|
|
196
229
|
},
|
|
197
230
|
onStatusChange: (status: ChatClientState) => {
|
|
198
|
-
|
|
199
|
-
|
|
231
|
+
runOrQueueForActiveInstance(() => {
|
|
232
|
+
setStatus(status)
|
|
233
|
+
})
|
|
200
234
|
},
|
|
201
235
|
onSubscriptionChange: (nextIsSubscribed: boolean) => {
|
|
202
|
-
|
|
203
|
-
|
|
236
|
+
runOrQueueForActiveInstance(() => {
|
|
237
|
+
setIsSubscribed(nextIsSubscribed)
|
|
238
|
+
})
|
|
204
239
|
},
|
|
205
240
|
onConnectionStatusChange: (nextStatus: ConnectionStatus) => {
|
|
206
|
-
|
|
207
|
-
|
|
241
|
+
runOrQueueForActiveInstance(() => {
|
|
242
|
+
setConnectionStatus(nextStatus)
|
|
243
|
+
})
|
|
208
244
|
},
|
|
209
245
|
onSessionGeneratingChange: (isGenerating: boolean) => {
|
|
210
|
-
|
|
211
|
-
|
|
246
|
+
runOrQueueForActiveInstance(() => {
|
|
247
|
+
setSessionGenerating(isGenerating)
|
|
248
|
+
})
|
|
212
249
|
},
|
|
213
250
|
...(optionsRef.current.queue !== undefined && {
|
|
214
251
|
queue: optionsRef.current.queue,
|
|
215
252
|
}),
|
|
216
253
|
onQueueChange: (nextQueue: Array<QueuedMessage>) => {
|
|
217
|
-
|
|
218
|
-
|
|
254
|
+
runOrQueueForActiveInstance(() => {
|
|
255
|
+
setQueue(nextQueue)
|
|
256
|
+
})
|
|
219
257
|
},
|
|
220
258
|
onRunIdChange: (nextRunId) => {
|
|
221
|
-
|
|
222
|
-
|
|
259
|
+
runOrQueueForActiveInstance(() => {
|
|
260
|
+
setRunId(nextRunId)
|
|
261
|
+
})
|
|
223
262
|
},
|
|
224
263
|
onResumeStateChange: (_nextResumeState, nextPendingInterrupts) => {
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
264
|
+
runOrQueueForActiveInstance(() => {
|
|
265
|
+
setInterruptState((current) => ({
|
|
266
|
+
...current,
|
|
267
|
+
interrupts: nextPendingInterrupts,
|
|
268
|
+
pendingInterrupts: nextPendingInterrupts,
|
|
269
|
+
}))
|
|
270
|
+
})
|
|
231
271
|
},
|
|
232
|
-
onInterruptStateChange: (nextInterruptState) => {
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
272
|
+
onInterruptStateChange: (nextInterruptState, context) => {
|
|
273
|
+
runOrQueueForActiveInstance(() => {
|
|
274
|
+
setInterruptState(nextInterruptState)
|
|
275
|
+
optionsRef.current.onInterruptStateChange?.(
|
|
276
|
+
nextInterruptState,
|
|
277
|
+
context,
|
|
278
|
+
)
|
|
279
|
+
})
|
|
236
280
|
},
|
|
237
281
|
})
|
|
238
282
|
instanceHolder.current = instance
|
|
239
|
-
|
|
240
|
-
for (const error of pendingInitializationErrors) {
|
|
241
|
-
if (activeClientRef.current !== instance) break
|
|
242
|
-
optionsRef.current.onError?.(error)
|
|
243
|
-
}
|
|
244
|
-
return instance
|
|
283
|
+
return { client: instance, initialization: initializationState }
|
|
245
284
|
}, [clientId, syncResumeState])
|
|
246
285
|
|
|
286
|
+
useEffect(() => {
|
|
287
|
+
activeClientRef.current = client
|
|
288
|
+
try {
|
|
289
|
+
// Keep initialization closed while draining so callbacks published by a
|
|
290
|
+
// queued callback are appended and delivered in the same commit.
|
|
291
|
+
while (initialization.callbacks.length > 0) {
|
|
292
|
+
if (activeClientRef.current !== client) {
|
|
293
|
+
initialization.callbacks.length = 0
|
|
294
|
+
break
|
|
295
|
+
}
|
|
296
|
+
initialization.callbacks.shift()?.()
|
|
297
|
+
}
|
|
298
|
+
} finally {
|
|
299
|
+
// A throw from a queued user callback must not leave the queue closed.
|
|
300
|
+
initialization.ready = true
|
|
301
|
+
}
|
|
302
|
+
}, [client, initialization])
|
|
303
|
+
|
|
247
304
|
useEffect(() => {
|
|
248
305
|
const clientMessages = client.getMessages()
|
|
249
306
|
if (clientMessages !== messagesRef.current) {
|
|
@@ -327,7 +384,6 @@ export function useChat<
|
|
|
327
384
|
clearTimeout(cleanupInvalidationRef.current)
|
|
328
385
|
cleanupInvalidationRef.current = null
|
|
329
386
|
}
|
|
330
|
-
activeClientRef.current = client
|
|
331
387
|
client.mountDevtools()
|
|
332
388
|
// Delivery-durability resume is transparent: the resumable SSE connection
|
|
333
389
|
// adapter re-attaches via the browser's native Last-Event-ID on reconnect.
|
|
@@ -458,7 +514,11 @@ export function useChat<
|
|
|
458
514
|
|
|
459
515
|
const resolveInterrupts = useCallback(
|
|
460
516
|
(
|
|
461
|
-
resolution:
|
|
517
|
+
resolution:
|
|
518
|
+
| boolean
|
|
519
|
+
| ((
|
|
520
|
+
interrupt: ResolvableChatInterrupt<TTools, TInterrupts>,
|
|
521
|
+
) => undefined),
|
|
462
522
|
) => {
|
|
463
523
|
if (typeof resolution === 'boolean') {
|
|
464
524
|
client.resolveInterrupts(resolution)
|
|
@@ -562,5 +622,5 @@ export function useChat<
|
|
|
562
622
|
resumeInterrupts,
|
|
563
623
|
partial,
|
|
564
624
|
final,
|
|
565
|
-
} as unknown as UseChatReturn<TTools, TSchema>
|
|
625
|
+
} as unknown as UseChatReturn<TTools, TSchema, TInterrupts>
|
|
566
626
|
}
|
|
@@ -21,10 +21,6 @@ export interface UseGenerateAudioOptions<TOutput = AudioGenerationResult> {
|
|
|
21
21
|
connection?: ConnectConnectionAdapter
|
|
22
22
|
/** Direct async function for audio generation */
|
|
23
23
|
fetcher?: GenerationFetcher<AudioGenerateInput, AudioGenerationResult>
|
|
24
|
-
/**
|
|
25
|
-
* @deprecated Prefer `threadId`. Only allowed when `threadId` is omitted (see `GenerationPersistenceOptions`).
|
|
26
|
-
*/
|
|
27
|
-
id?: string
|
|
28
24
|
/** Additional body parameters to send with connect-based adapter requests */
|
|
29
25
|
body?: Record<string, any>
|
|
30
26
|
/** Display options for TanStack AI Devtools. */
|
|
@@ -48,8 +44,8 @@ export interface UseGenerateAudioOptions<TOutput = AudioGenerationResult> {
|
|
|
48
44
|
* id on the wire, which the protocol requires.
|
|
49
45
|
*
|
|
50
46
|
* **Required whenever `persistence` is set** — an app that cannot name the
|
|
51
|
-
* scope has nothing to restore to. Optional for ephemeral generations
|
|
52
|
-
*
|
|
47
|
+
* scope has nothing to restore to. Optional for ephemeral generations. If
|
|
48
|
+
* omitted, the client mints a wire id after mount.
|
|
53
49
|
*/
|
|
54
50
|
threadId?: string
|
|
55
51
|
/**
|
|
@@ -144,7 +140,7 @@ export interface UseGenerateAudioReturn<TOutput = AudioGenerationResult> {
|
|
|
144
140
|
export function useGenerateAudio<TTransformed = void>(
|
|
145
141
|
options: Omit<
|
|
146
142
|
UseGenerateAudioOptions,
|
|
147
|
-
'onResult' | 'persistence' | 'threadId'
|
|
143
|
+
'onResult' | 'persistence' | 'threadId'
|
|
148
144
|
> & {
|
|
149
145
|
onResult?: (result: AudioGenerationResult) => TTransformed
|
|
150
146
|
} & GenerationPersistenceOptions,
|
|
@@ -21,10 +21,6 @@ export interface UseGenerateImageOptions<TOutput = ImageGenerationResult> {
|
|
|
21
21
|
connection?: ConnectConnectionAdapter
|
|
22
22
|
/** Direct async function for image generation */
|
|
23
23
|
fetcher?: GenerationFetcher<ImageGenerateInput, ImageGenerationResult>
|
|
24
|
-
/**
|
|
25
|
-
* @deprecated Prefer `threadId`. Only allowed when `threadId` is omitted (see `GenerationPersistenceOptions`).
|
|
26
|
-
*/
|
|
27
|
-
id?: string
|
|
28
24
|
/** Additional body parameters to send with connect-based adapter requests */
|
|
29
25
|
body?: Record<string, any>
|
|
30
26
|
/** Display options for TanStack AI Devtools. */
|
|
@@ -48,8 +44,8 @@ export interface UseGenerateImageOptions<TOutput = ImageGenerationResult> {
|
|
|
48
44
|
* id on the wire, which the protocol requires.
|
|
49
45
|
*
|
|
50
46
|
* **Required whenever `persistence` is set** — an app that cannot name the
|
|
51
|
-
* scope has nothing to restore to. Optional for ephemeral generations
|
|
52
|
-
*
|
|
47
|
+
* scope has nothing to restore to. Optional for ephemeral generations. If
|
|
48
|
+
* omitted, the client mints a wire id after mount.
|
|
53
49
|
*/
|
|
54
50
|
threadId?: string
|
|
55
51
|
/**
|
|
@@ -146,7 +142,7 @@ export interface UseGenerateImageReturn<TOutput = ImageGenerationResult> {
|
|
|
146
142
|
export function useGenerateImage<TTransformed = void>(
|
|
147
143
|
options: Omit<
|
|
148
144
|
UseGenerateImageOptions,
|
|
149
|
-
'onResult' | 'persistence' | 'threadId'
|
|
145
|
+
'onResult' | 'persistence' | 'threadId'
|
|
150
146
|
> & {
|
|
151
147
|
onResult?: (result: ImageGenerationResult) => TTransformed
|
|
152
148
|
} & GenerationPersistenceOptions,
|
|
@@ -21,10 +21,6 @@ export interface UseGenerateSpeechOptions<TOutput = TTSResult> {
|
|
|
21
21
|
connection?: ConnectConnectionAdapter
|
|
22
22
|
/** Direct async function for speech generation */
|
|
23
23
|
fetcher?: GenerationFetcher<SpeechGenerateInput, TTSResult>
|
|
24
|
-
/**
|
|
25
|
-
* @deprecated Prefer `threadId`. Only allowed when `threadId` is omitted (see `GenerationPersistenceOptions`).
|
|
26
|
-
*/
|
|
27
|
-
id?: string
|
|
28
24
|
/** Additional body parameters to send with connect-based adapter requests */
|
|
29
25
|
body?: Record<string, any>
|
|
30
26
|
/** Display options for TanStack AI Devtools. */
|
|
@@ -48,8 +44,8 @@ export interface UseGenerateSpeechOptions<TOutput = TTSResult> {
|
|
|
48
44
|
* id on the wire, which the protocol requires.
|
|
49
45
|
*
|
|
50
46
|
* **Required whenever `persistence` is set** — an app that cannot name the
|
|
51
|
-
* scope has nothing to restore to. Optional for ephemeral generations
|
|
52
|
-
*
|
|
47
|
+
* scope has nothing to restore to. Optional for ephemeral generations. If
|
|
48
|
+
* omitted, the client mints a wire id after mount.
|
|
53
49
|
*/
|
|
54
50
|
threadId?: string
|
|
55
51
|
/**
|
|
@@ -140,7 +136,7 @@ export interface UseGenerateSpeechReturn<TOutput = TTSResult> {
|
|
|
140
136
|
export function useGenerateSpeech<TTransformed = void>(
|
|
141
137
|
options: Omit<
|
|
142
138
|
UseGenerateSpeechOptions,
|
|
143
|
-
'onResult' | 'persistence' | 'threadId'
|
|
139
|
+
'onResult' | 'persistence' | 'threadId'
|
|
144
140
|
> & {
|
|
145
141
|
onResult?: (result: TTSResult) => TTransformed
|
|
146
142
|
} & GenerationPersistenceOptions,
|
|
@@ -11,6 +11,7 @@ import type {
|
|
|
11
11
|
InferGenerationOutputFromReturn,
|
|
12
12
|
VideoGenerateInput,
|
|
13
13
|
VideoGenerateResult,
|
|
14
|
+
VideoGenerationClientOptions,
|
|
14
15
|
VideoStatusInfo,
|
|
15
16
|
} from '@tanstack/ai-client'
|
|
16
17
|
|
|
@@ -22,10 +23,6 @@ export interface UseGenerateVideoOptions<TOutput = VideoGenerateResult> {
|
|
|
22
23
|
connection?: ConnectConnectionAdapter
|
|
23
24
|
/** Direct async function that returns a completed video result */
|
|
24
25
|
fetcher?: GenerationFetcher<VideoGenerateInput, VideoGenerateResult>
|
|
25
|
-
/**
|
|
26
|
-
* @deprecated Prefer `threadId`. Only allowed when `threadId` is omitted (see `GenerationPersistenceOptions`).
|
|
27
|
-
*/
|
|
28
|
-
id?: string
|
|
29
26
|
/** Additional body parameters to send with connect-based adapter requests */
|
|
30
27
|
body?: Record<string, any>
|
|
31
28
|
/** Display options for TanStack AI Devtools. */
|
|
@@ -49,8 +46,8 @@ export interface UseGenerateVideoOptions<TOutput = VideoGenerateResult> {
|
|
|
49
46
|
* id on the wire, which the protocol requires.
|
|
50
47
|
*
|
|
51
48
|
* **Required whenever `persistence` is set** — an app that cannot name the
|
|
52
|
-
* scope has nothing to restore to. Optional for ephemeral generations
|
|
53
|
-
*
|
|
49
|
+
* scope has nothing to restore to. Optional for ephemeral generations. If
|
|
50
|
+
* omitted, the client mints a wire id after mount.
|
|
54
51
|
*/
|
|
55
52
|
threadId?: string
|
|
56
53
|
/**
|
|
@@ -157,7 +154,7 @@ export interface UseGenerateVideoReturn<TOutput = VideoGenerateResult> {
|
|
|
157
154
|
export function useGenerateVideo<TTransformed = void>(
|
|
158
155
|
options: Omit<
|
|
159
156
|
UseGenerateVideoOptions,
|
|
160
|
-
'onResult' | 'persistence' | 'threadId'
|
|
157
|
+
'onResult' | 'persistence' | 'threadId'
|
|
161
158
|
> & {
|
|
162
159
|
onResult?: (result: VideoGenerateResult) => TTransformed
|
|
163
160
|
} & GenerationPersistenceOptions,
|
|
@@ -169,8 +166,8 @@ export function useGenerateVideo<TTransformed = void>(
|
|
|
169
166
|
TTransformed
|
|
170
167
|
>
|
|
171
168
|
const hookId = useId()
|
|
172
|
-
//
|
|
173
|
-
const clientIdentity = options.threadId ??
|
|
169
|
+
// The hook identity is `threadId`. `hookId` is only a React recreation key.
|
|
170
|
+
const clientIdentity = options.threadId ?? hookId
|
|
174
171
|
|
|
175
172
|
const [result, setResult] = useState<TOutput | null>(null)
|
|
176
173
|
const [jobId, setJobId] = useState<string | null>(null)
|
|
@@ -192,13 +189,11 @@ export function useGenerateVideo<TTransformed = void>(
|
|
|
192
189
|
// `?.()`'s implicit `undefined` doesn't widen the function
|
|
193
190
|
// return type (which `exactOptionalPropertyTypes` rejects
|
|
194
191
|
// against the strict-optional target).
|
|
195
|
-
|
|
196
|
-
|
|
192
|
+
const baseOptions: Omit<
|
|
193
|
+
VideoGenerationClientOptions<TOutput>,
|
|
194
|
+
'persistence' | 'threadId'
|
|
195
|
+
> = {
|
|
197
196
|
body: opts.body,
|
|
198
|
-
...(opts.threadId !== undefined
|
|
199
|
-
? { threadId: opts.threadId }
|
|
200
|
-
: { id: opts.id ?? hookId }),
|
|
201
|
-
...(opts.persistence !== undefined && { persistence: opts.persistence }),
|
|
202
197
|
...(opts.hydrateGeneration !== undefined && {
|
|
203
198
|
hydrateGeneration: opts.hydrateGeneration,
|
|
204
199
|
}),
|
|
@@ -255,9 +250,20 @@ export function useGenerateVideo<TTransformed = void>(
|
|
|
255
250
|
},
|
|
256
251
|
}
|
|
257
252
|
|
|
253
|
+
const persistenceProps =
|
|
254
|
+
typeof opts.threadId === 'string' && opts.persistence
|
|
255
|
+
? {
|
|
256
|
+
persistence: opts.persistence,
|
|
257
|
+
threadId: opts.threadId,
|
|
258
|
+
}
|
|
259
|
+
: {
|
|
260
|
+
...(opts.threadId !== undefined && { threadId: opts.threadId }),
|
|
261
|
+
}
|
|
262
|
+
|
|
258
263
|
if (opts.connection) {
|
|
259
264
|
return new VideoGenerationClient<TOutput>({
|
|
260
265
|
...baseOptions,
|
|
266
|
+
...persistenceProps,
|
|
261
267
|
connection: opts.connection,
|
|
262
268
|
})
|
|
263
269
|
}
|
|
@@ -265,6 +271,7 @@ export function useGenerateVideo<TTransformed = void>(
|
|
|
265
271
|
if (opts.fetcher) {
|
|
266
272
|
return new VideoGenerationClient<TOutput>({
|
|
267
273
|
...baseOptions,
|
|
274
|
+
...persistenceProps,
|
|
268
275
|
fetcher: opts.fetcher,
|
|
269
276
|
})
|
|
270
277
|
}
|