@tanstack/ai-react 0.11.8 → 0.12.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/dist/esm/index.d.ts +1 -1
- package/dist/esm/types.d.ts +7 -2
- package/dist/esm/use-chat.js +14 -3
- package/dist/esm/use-chat.js.map +1 -1
- package/dist/esm/use-generate-audio.d.ts +3 -1
- package/dist/esm/use-generate-audio.js +10 -1
- package/dist/esm/use-generate-audio.js.map +1 -1
- package/dist/esm/use-generate-image.d.ts +3 -1
- package/dist/esm/use-generate-image.js +10 -1
- package/dist/esm/use-generate-image.js.map +1 -1
- package/dist/esm/use-generate-speech.d.ts +3 -1
- package/dist/esm/use-generate-speech.js +10 -1
- package/dist/esm/use-generate-speech.js.map +1 -1
- package/dist/esm/use-generate-video.d.ts +3 -1
- package/dist/esm/use-generate-video.js +10 -1
- package/dist/esm/use-generate-video.js.map +1 -1
- package/dist/esm/use-generation.d.ts +3 -1
- package/dist/esm/use-generation.js +9 -1
- package/dist/esm/use-generation.js.map +1 -1
- package/dist/esm/use-summarize.d.ts +3 -1
- package/dist/esm/use-summarize.js +10 -3
- package/dist/esm/use-summarize.js.map +1 -1
- package/dist/esm/use-transcription.d.ts +3 -1
- package/dist/esm/use-transcription.js +10 -3
- package/dist/esm/use-transcription.js.map +1 -1
- package/package.json +5 -5
- package/src/index.ts +3 -0
- package/src/types.ts +9 -1
- package/src/use-chat.ts +22 -3
- package/src/use-generate-audio.ts +13 -1
- package/src/use-generate-image.ts +13 -1
- package/src/use-generate-speech.ts +13 -1
- package/src/use-generate-video.ts +14 -1
- package/src/use-generation.ts +13 -1
- package/src/use-summarize.ts +13 -3
- package/src/use-transcription.ts +13 -3
package/dist/esm/index.d.ts
CHANGED
|
@@ -16,4 +16,4 @@ export { useSummarize } from './use-summarize.js';
|
|
|
16
16
|
export type { UseSummarizeOptions, UseSummarizeReturn } from './use-summarize.js';
|
|
17
17
|
export { useGenerateVideo } from './use-generate-video.js';
|
|
18
18
|
export type { UseGenerateVideoOptions, UseGenerateVideoReturn, } from './use-generate-video.js';
|
|
19
|
-
export { fetchServerSentEvents, fetchHttpStream, stream, rpcStream, createChatClientOptions, type ConnectionAdapter, type ConnectConnectionAdapter, type SubscribeConnectionAdapter, type RunAgentInputContext, type FetchConnectionOptions, type InferChatMessages, type GenerationClientState, type ImageGenerateInput, type AudioGenerateInput, type SpeechGenerateInput, type TranscriptionGenerateInput, type SummarizeGenerateInput, type VideoGenerateInput, type VideoGenerateResult, type VideoStatusInfo, } from '@tanstack/ai-client';
|
|
19
|
+
export { fetchServerSentEvents, fetchHttpStream, stream, rpcStream, createChatClientOptions, type ChatFetcher, type ChatFetcherInput, type ChatFetcherOptions, type ConnectionAdapter, type ConnectConnectionAdapter, type SubscribeConnectionAdapter, type RunAgentInputContext, type FetchConnectionOptions, type InferChatMessages, type GenerationClientState, type ImageGenerateInput, type AudioGenerateInput, type SpeechGenerateInput, type TranscriptionGenerateInput, type SummarizeGenerateInput, type VideoGenerateInput, type VideoGenerateResult, type VideoStatusInfo, } from '@tanstack/ai-client';
|
package/dist/esm/types.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AnyClientTool, InferSchemaType, ModelMessage, SchemaInput } from '@tanstack/ai';
|
|
2
|
-
import { ChatClientOptions, ChatClientState, ChatRequestBody, ConnectionStatus, MultimodalContent, UIMessage } from '@tanstack/ai-client';
|
|
2
|
+
import { AIDevtoolsDisplayOptions, ChatClientOptions, ChatClientState, ChatRequestBody, ConnectionStatus, DistributedOmit, MultimodalContent, UIMessage } from '@tanstack/ai-client';
|
|
3
3
|
export type { ChatRequestBody, MultimodalContent, UIMessage };
|
|
4
4
|
/**
|
|
5
5
|
* Recursive partial — every property and every nested array element is optional.
|
|
@@ -12,6 +12,9 @@ export type DeepPartial<T> = T extends ReadonlyArray<infer U> ? Array<DeepPartia
|
|
|
12
12
|
/**
|
|
13
13
|
* Options for the useChat hook.
|
|
14
14
|
*
|
|
15
|
+
* Pass either `connection` or `fetcher` — the XOR is enforced at the type
|
|
16
|
+
* level via `ChatTransport`.
|
|
17
|
+
*
|
|
15
18
|
* This extends ChatClientOptions but omits the state change callbacks that are
|
|
16
19
|
* managed internally by React state:
|
|
17
20
|
* - `onMessagesChange` - Managed by React state (exposed as `messages`)
|
|
@@ -32,7 +35,9 @@ export type DeepPartial<T> = T extends ReadonlyArray<infer U> ? Array<DeepPartia
|
|
|
32
35
|
* Note: Connection and body changes will recreate the ChatClient instance.
|
|
33
36
|
* To update these options, remount the component or use a key prop.
|
|
34
37
|
*/
|
|
35
|
-
export type UseChatOptions<TTools extends ReadonlyArray<AnyClientTool> = any, TSchema extends SchemaInput | undefined = undefined> =
|
|
38
|
+
export type UseChatOptions<TTools extends ReadonlyArray<AnyClientTool> = any, TSchema extends SchemaInput | undefined = undefined> = DistributedOmit<ChatClientOptions<TTools>, 'onMessagesChange' | 'onLoadingChange' | 'onErrorChange' | 'onStatusChange' | 'onSubscriptionChange' | 'onConnectionStatusChange' | 'onSessionGeneratingChange' | 'devtools'> & {
|
|
39
|
+
/** Display options for TanStack AI Devtools. */
|
|
40
|
+
devtools?: AIDevtoolsDisplayOptions;
|
|
36
41
|
/**
|
|
37
42
|
* Opt into mount-time live subscription behavior.
|
|
38
43
|
* When enabled, the hook subscribes on mount and unsubscribes on unmount.
|
package/dist/esm/use-chat.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ChatClient } from "@tanstack/ai-client";
|
|
2
|
+
import { createChatDevtoolsBridge } from "@tanstack/ai-client/devtools";
|
|
2
3
|
import { useId, useState, useRef, useMemo, useEffect, useCallback } from "react";
|
|
3
4
|
function useChat(options) {
|
|
4
5
|
const hookId = useId();
|
|
@@ -23,14 +24,22 @@ function useChat(options) {
|
|
|
23
24
|
const messagesToUse = isFirstMountRef.current ? options.initialMessages || [] : messagesRef.current;
|
|
24
25
|
isFirstMountRef.current = false;
|
|
25
26
|
const initialOptions = optionsRef.current;
|
|
27
|
+
const transport = initialOptions.connection ? { connection: initialOptions.connection } : { fetcher: initialOptions.fetcher };
|
|
26
28
|
return new ChatClient({
|
|
27
|
-
|
|
29
|
+
devtoolsBridgeFactory: createChatDevtoolsBridge,
|
|
30
|
+
...transport,
|
|
28
31
|
id: clientId,
|
|
29
32
|
initialMessages: messagesToUse,
|
|
30
33
|
...initialOptions.body !== void 0 && { body: initialOptions.body },
|
|
31
34
|
...initialOptions.forwardedProps !== void 0 && {
|
|
32
35
|
forwardedProps: initialOptions.forwardedProps
|
|
33
36
|
},
|
|
37
|
+
devtools: {
|
|
38
|
+
...initialOptions.devtools,
|
|
39
|
+
framework: "react",
|
|
40
|
+
hookName: "useChat",
|
|
41
|
+
outputKind: initialOptions.outputSchema ? "structured" : "chat"
|
|
42
|
+
},
|
|
34
43
|
onResponse: (response) => {
|
|
35
44
|
void optionsRef.current.onResponse?.(response);
|
|
36
45
|
},
|
|
@@ -98,14 +107,16 @@ function useChat(options) {
|
|
|
98
107
|
}
|
|
99
108
|
}, [client, options.live]);
|
|
100
109
|
useEffect(() => {
|
|
110
|
+
client.mountDevtools();
|
|
101
111
|
return () => {
|
|
102
|
-
if (
|
|
112
|
+
if (optionsRef.current.live) {
|
|
103
113
|
client.unsubscribe();
|
|
104
114
|
} else {
|
|
105
115
|
client.stop();
|
|
106
116
|
}
|
|
117
|
+
client.dispose();
|
|
107
118
|
};
|
|
108
|
-
}, [client
|
|
119
|
+
}, [client]);
|
|
109
120
|
const sendMessage = useCallback(
|
|
110
121
|
async (content) => {
|
|
111
122
|
await client.sendMessage(content);
|
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 { useCallback, useEffect, useId, useMemo, useRef, useState } from 'react'\nimport type {\n AnyClientTool,\n InferSchemaType,\n ModelMessage,\n SchemaInput,\n StreamChunk,\n} from '@tanstack/ai'\nimport type {\n ChatClientState,\n ConnectionStatus,\n StructuredOutputPart,\n} from '@tanstack/ai-client'\n\nimport type {\n DeepPartial,\n MultimodalContent,\n UIMessage,\n UseChatOptions,\n UseChatReturn,\n} from './types'\n\nexport function useChat<\n TTools extends ReadonlyArray<AnyClientTool> = any,\n TSchema extends SchemaInput | undefined = undefined,\n>(options: UseChatOptions<TTools, TSchema>): UseChatReturn<TTools, TSchema> {\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 type Partial = DeepPartial<InferSchemaType<NonNullable<TSchema>>>\n type Final = InferSchemaType<NonNullable<TSchema>>\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\n // Update ref synchronously during render so it's always current when useMemo runs.\n messagesRef.current = messages\n\n // Track current options in a ref to avoid recreating client when options change\n const optionsRef = useRef<UseChatOptions<TTools, TSchema>>(options)\n optionsRef.current = options\n\n // Create ChatClient instance with callbacks to sync state\n const client = useMemo(() => {\n const messagesToUse = isFirstMountRef.current\n ? options.initialMessages || []\n : messagesRef.current\n\n isFirstMountRef.current = false\n\n // Build options with conditional spreads for fields whose source\n // type is `T | undefined` but the ChatClient target uses a strict\n // optional (`field?: T`) — `exactOptionalPropertyTypes` rejects\n // assigning `undefined` to those, so we omit the key when absent.\n const initialOptions = optionsRef.current\n return new ChatClient({\n connection: initialOptions.connection,\n id: clientId,\n initialMessages: messagesToUse,\n ...(initialOptions.body !== undefined && { body: initialOptions.body }),\n ...(initialOptions.forwardedProps !== undefined && {\n forwardedProps: initialOptions.forwardedProps,\n }),\n onResponse: (response) => {\n void optionsRef.current.onResponse?.(response)\n },\n onChunk: (chunk: StreamChunk) => {\n optionsRef.current.onChunk?.(chunk)\n },\n onFinish: (message: UIMessage<TTools>) => {\n optionsRef.current.onFinish?.(message)\n },\n onError: (error: Error) => {\n optionsRef.current.onError?.(error)\n },\n ...(initialOptions.tools !== undefined && {\n tools: initialOptions.tools,\n }),\n onCustomEvent: (eventType, data, context) => {\n optionsRef.current.onCustomEvent?.(eventType, data, context)\n },\n ...(options.streamProcessor !== undefined && {\n streamProcessor: options.streamProcessor,\n }),\n onMessagesChange: (newMessages: Array<UIMessage<TTools>>) => {\n setMessages(newMessages)\n },\n onLoadingChange: (newIsLoading: boolean) => {\n setIsLoading(newIsLoading)\n },\n onErrorChange: (newError: Error | undefined) => {\n setError(newError)\n },\n onStatusChange: (status: ChatClientState) => {\n setStatus(status)\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 useEffect(() => {\n // Conditional spread: `updateOptions` declares strict-optional\n // fields and rejects explicit `undefined` under EOPT.\n client.updateOptions({\n body: options.body,\n ...(options.forwardedProps !== undefined && {\n forwardedProps: options.forwardedProps,\n }),\n })\n }, [client, options.body, options.forwardedProps])\n\n useEffect(() => {\n if (options.initialMessages && options.initialMessages.length > 0) {\n if (messages.length === 0) {\n client.setMessagesManually(options.initialMessages)\n }\n }\n }, [])\n\n useEffect(() => {\n if (options.live) {\n client.subscribe()\n } else {\n client.unsubscribe()\n }\n }, [client, options.live])\n\n useEffect(() => {\n return () => {\n if (options.live) {\n client.unsubscribe()\n } else {\n client.stop()\n }\n }\n }, [client, options.live])\n\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 // The \"active\" structured-output part is the one on the assistant message\n // that follows the latest user message. No such message exists between\n // sendMessage() and the first chunk, so partial/final naturally read as\n // cleared. Historical parts on earlier assistant messages remain available\n // via `messages` directly.\n //\n // When there is NO user message yet (e.g. `initialMessages` contains only\n // a stale assistant turn or a system prompt) we deliberately return null\n // rather than scanning historical assistants — otherwise a `final` from a\n // previous session would leak into the hook value on first render.\n const activeStructuredPart = useMemo<StructuredOutputPart | null>(() => {\n let lastUserIndex = -1\n for (let i = messages.length - 1; i >= 0; i--) {\n if (messages[i]?.role === 'user') {\n lastUserIndex = i\n break\n }\n }\n if (lastUserIndex === -1) return null\n for (let i = messages.length - 1; i > lastUserIndex; i--) {\n const m = messages[i]\n if (m?.role !== 'assistant') continue\n const part = m.parts.find(\n (p): p is StructuredOutputPart => p.type === 'structured-output',\n )\n if (part) return part\n }\n return null\n }, [messages])\n\n const partial = useMemo<Partial>(() => {\n if (!activeStructuredPart) return {} as Partial\n const v = activeStructuredPart.partial ?? activeStructuredPart.data\n return (v ?? {}) as Partial\n }, [activeStructuredPart])\n\n const final = useMemo<Final | null>(() => {\n if (!activeStructuredPart || activeStructuredPart.status !== 'complete') {\n return null\n }\n return activeStructuredPart.data as Final\n }, [activeStructuredPart])\n\n // The runtime shape unconditionally exposes partial/final; the public\n // return type hides them when no outputSchema was supplied. TS can't\n // structurally narrow across that conditional, so the `as` is the seam.\n // eslint-disable-next-line no-restricted-syntax -- hook return shape diverges from generic UseChatReturn<TTools, TSchema> due to conditional type on TSchema; TS can't structurally narrow\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 partial,\n final,\n } as unknown as UseChatReturn<TTools, TSchema>\n}\n"],"names":["error","status"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"use-chat.js","sources":["../../src/use-chat.ts"],"sourcesContent":["import { ChatClient } from '@tanstack/ai-client'\nimport { createChatDevtoolsBridge } from '@tanstack/ai-client/devtools'\nimport { useCallback, useEffect, useId, useMemo, useRef, useState } from 'react'\nimport type {\n AnyClientTool,\n InferSchemaType,\n ModelMessage,\n SchemaInput,\n StreamChunk,\n} from '@tanstack/ai'\nimport type {\n ChatClientState,\n ConnectionStatus,\n StructuredOutputPart,\n} from '@tanstack/ai-client'\n\nimport type {\n DeepPartial,\n MultimodalContent,\n UIMessage,\n UseChatOptions,\n UseChatReturn,\n} from './types'\n\nexport function useChat<\n TTools extends ReadonlyArray<AnyClientTool> = any,\n TSchema extends SchemaInput | undefined = undefined,\n>(options: UseChatOptions<TTools, TSchema>): UseChatReturn<TTools, TSchema> {\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 type Partial = DeepPartial<InferSchemaType<NonNullable<TSchema>>>\n type Final = InferSchemaType<NonNullable<TSchema>>\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\n // Update ref synchronously during render so it's always current when useMemo runs.\n messagesRef.current = messages\n\n // Track current options in a ref to avoid recreating client when options change\n const optionsRef = useRef<UseChatOptions<TTools, TSchema>>(options)\n optionsRef.current = options\n\n // Create ChatClient instance with callbacks to sync state\n const client = useMemo(() => {\n const messagesToUse = isFirstMountRef.current\n ? options.initialMessages || []\n : messagesRef.current\n\n isFirstMountRef.current = false\n\n // Build options with conditional spreads for fields whose source\n // type is `T | undefined` but the ChatClient target uses a strict\n // optional (`field?: T`) — `exactOptionalPropertyTypes` rejects\n // assigning `undefined` to those, so we omit the key when absent.\n const initialOptions = optionsRef.current\n const transport = initialOptions.connection\n ? { connection: initialOptions.connection }\n : { fetcher: initialOptions.fetcher }\n\n return new ChatClient({\n devtoolsBridgeFactory: createChatDevtoolsBridge,\n ...transport,\n id: clientId,\n initialMessages: messagesToUse,\n ...(initialOptions.body !== undefined && { body: initialOptions.body }),\n ...(initialOptions.forwardedProps !== undefined && {\n forwardedProps: initialOptions.forwardedProps,\n }),\n devtools: {\n ...initialOptions.devtools,\n framework: 'react',\n hookName: 'useChat',\n outputKind: initialOptions.outputSchema ? 'structured' : 'chat',\n },\n onResponse: (response) => {\n void optionsRef.current.onResponse?.(response)\n },\n onChunk: (chunk: StreamChunk) => {\n optionsRef.current.onChunk?.(chunk)\n },\n onFinish: (message: UIMessage<TTools>) => {\n optionsRef.current.onFinish?.(message)\n },\n onError: (error: Error) => {\n optionsRef.current.onError?.(error)\n },\n ...(initialOptions.tools !== undefined && {\n tools: initialOptions.tools,\n }),\n onCustomEvent: (eventType, data, context) => {\n optionsRef.current.onCustomEvent?.(eventType, data, context)\n },\n ...(options.streamProcessor !== undefined && {\n streamProcessor: options.streamProcessor,\n }),\n onMessagesChange: (newMessages: Array<UIMessage<TTools>>) => {\n setMessages(newMessages)\n },\n onLoadingChange: (newIsLoading: boolean) => {\n setIsLoading(newIsLoading)\n },\n onErrorChange: (newError: Error | undefined) => {\n setError(newError)\n },\n onStatusChange: (status: ChatClientState) => {\n setStatus(status)\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 useEffect(() => {\n // Conditional spread: `updateOptions` declares strict-optional\n // fields and rejects explicit `undefined` under EOPT.\n client.updateOptions({\n body: options.body,\n ...(options.forwardedProps !== undefined && {\n forwardedProps: options.forwardedProps,\n }),\n })\n }, [client, options.body, options.forwardedProps])\n\n useEffect(() => {\n if (options.initialMessages && options.initialMessages.length > 0) {\n if (messages.length === 0) {\n client.setMessagesManually(options.initialMessages)\n }\n }\n }, [])\n\n useEffect(() => {\n if (options.live) {\n client.subscribe()\n } else {\n client.unsubscribe()\n }\n }, [client, options.live])\n\n useEffect(() => {\n client.mountDevtools()\n\n return () => {\n // Subscribe/unsubscribe on `options.live` is owned by the dedicated\n // effect above. This cleanup only fires on unmount or client swap,\n // so read `live` through the ref to avoid disposing the client every\n // time `live` toggles.\n if (optionsRef.current.live) {\n client.unsubscribe()\n } else {\n client.stop()\n }\n client.dispose()\n }\n }, [client])\n\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 // The \"active\" structured-output part is the one on the assistant message\n // that follows the latest user message. No such message exists between\n // sendMessage() and the first chunk, so partial/final naturally read as\n // cleared. Historical parts on earlier assistant messages remain available\n // via `messages` directly.\n //\n // When there is NO user message yet (e.g. `initialMessages` contains only\n // a stale assistant turn or a system prompt) we deliberately return null\n // rather than scanning historical assistants — otherwise a `final` from a\n // previous session would leak into the hook value on first render.\n const activeStructuredPart = useMemo<StructuredOutputPart | null>(() => {\n let lastUserIndex = -1\n for (let i = messages.length - 1; i >= 0; i--) {\n if (messages[i]?.role === 'user') {\n lastUserIndex = i\n break\n }\n }\n if (lastUserIndex === -1) return null\n for (let i = messages.length - 1; i > lastUserIndex; i--) {\n const m = messages[i]\n if (m?.role !== 'assistant') continue\n const part = m.parts.find(\n (p): p is StructuredOutputPart => p.type === 'structured-output',\n )\n if (part) return part\n }\n return null\n }, [messages])\n\n const partial = useMemo<Partial>(() => {\n if (!activeStructuredPart) return {} as Partial\n const v = activeStructuredPart.partial ?? activeStructuredPart.data\n return (v ?? {}) as Partial\n }, [activeStructuredPart])\n\n const final = useMemo<Final | null>(() => {\n if (!activeStructuredPart || activeStructuredPart.status !== 'complete') {\n return null\n }\n return activeStructuredPart.data as Final\n }, [activeStructuredPart])\n\n // The runtime shape unconditionally exposes partial/final; the public\n // return type hides them when no outputSchema was supplied. TS can't\n // structurally narrow across that conditional, so the `as` is the seam.\n // eslint-disable-next-line no-restricted-syntax -- hook return shape diverges from generic UseChatReturn<TTools, TSchema> due to conditional type on TSchema; TS can't structurally narrow\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 partial,\n final,\n } as unknown as UseChatReturn<TTools, TSchema>\n}\n"],"names":["error","status"],"mappings":";;;AAwBO,SAAS,QAGd,SAA0E;AAC1E,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;AAC7D,QAAM,CAAC,cAAc,eAAe,IAAI,SAAS,KAAK;AACtD,QAAM,CAAC,kBAAkB,mBAAmB,IAC1C,SAA2B,cAAc;AAC3C,QAAM,CAAC,mBAAmB,oBAAoB,IAAI,SAAS,KAAK;AAMhE,QAAM,cAAc;AAAA,IAClB,QAAQ,mBAAmB,CAAA;AAAA,EAAC;AAE9B,QAAM,kBAAkB,OAAO,IAAI;AAGnC,cAAY,UAAU;AAGtB,QAAM,aAAa,OAAwC,OAAO;AAClE,aAAW,UAAU;AAGrB,QAAM,SAAS,QAAQ,MAAM;AAC3B,UAAM,gBAAgB,gBAAgB,UAClC,QAAQ,mBAAmB,CAAA,IAC3B,YAAY;AAEhB,oBAAgB,UAAU;AAM1B,UAAM,iBAAiB,WAAW;AAClC,UAAM,YAAY,eAAe,aAC7B,EAAE,YAAY,eAAe,eAC7B,EAAE,SAAS,eAAe,QAAA;AAE9B,WAAO,IAAI,WAAW;AAAA,MACpB,uBAAuB;AAAA,MACvB,GAAG;AAAA,MACH,IAAI;AAAA,MACJ,iBAAiB;AAAA,MACjB,GAAI,eAAe,SAAS,UAAa,EAAE,MAAM,eAAe,KAAA;AAAA,MAChE,GAAI,eAAe,mBAAmB,UAAa;AAAA,QACjD,gBAAgB,eAAe;AAAA,MAAA;AAAA,MAEjC,UAAU;AAAA,QACR,GAAG,eAAe;AAAA,QAClB,WAAW;AAAA,QACX,UAAU;AAAA,QACV,YAAY,eAAe,eAAe,eAAe;AAAA,MAAA;AAAA,MAE3D,YAAY,CAAC,aAAa;AACxB,aAAK,WAAW,QAAQ,aAAa,QAAQ;AAAA,MAC/C;AAAA,MACA,SAAS,CAAC,UAAuB;AAC/B,mBAAW,QAAQ,UAAU,KAAK;AAAA,MACpC;AAAA,MACA,UAAU,CAAC,YAA+B;AACxC,mBAAW,QAAQ,WAAW,OAAO;AAAA,MACvC;AAAA,MACA,SAAS,CAACA,WAAiB;AACzB,mBAAW,QAAQ,UAAUA,MAAK;AAAA,MACpC;AAAA,MACA,GAAI,eAAe,UAAU,UAAa;AAAA,QACxC,OAAO,eAAe;AAAA,MAAA;AAAA,MAExB,eAAe,CAAC,WAAW,MAAM,YAAY;AAC3C,mBAAW,QAAQ,gBAAgB,WAAW,MAAM,OAAO;AAAA,MAC7D;AAAA,MACA,GAAI,QAAQ,oBAAoB,UAAa;AAAA,QAC3C,iBAAiB,QAAQ;AAAA,MAAA;AAAA,MAE3B,kBAAkB,CAAC,gBAA0C;AAC3D,oBAAY,WAAW;AAAA,MACzB;AAAA,MACA,iBAAiB,CAAC,iBAA0B;AAC1C,qBAAa,YAAY;AAAA,MAC3B;AAAA,MACA,eAAe,CAAC,aAAgC;AAC9C,iBAAS,QAAQ;AAAA,MACnB;AAAA,MACA,gBAAgB,CAACC,YAA4B;AAC3C,kBAAUA,OAAM;AAAA,MAClB;AAAA,MACA,sBAAsB,CAAC,qBAA8B;AACnD,wBAAgB,gBAAgB;AAAA,MAClC;AAAA,MACA,0BAA0B,CAAC,eAAiC;AAC1D,4BAAoB,UAAU;AAAA,MAChC;AAAA,MACA,2BAA2B,CAAC,iBAA0B;AACpD,6BAAqB,YAAY;AAAA,MACnC;AAAA,IAAA,CACD;AAAA,EACH,GAAG,CAAC,QAAQ,CAAC;AAEb,YAAU,MAAM;AAGd,WAAO,cAAc;AAAA,MACnB,MAAM,QAAQ;AAAA,MACd,GAAI,QAAQ,mBAAmB,UAAa;AAAA,QAC1C,gBAAgB,QAAQ;AAAA,MAAA;AAAA,IAC1B,CACD;AAAA,EACH,GAAG,CAAC,QAAQ,QAAQ,MAAM,QAAQ,cAAc,CAAC;AAEjD,YAAU,MAAM;AACd,QAAI,QAAQ,mBAAmB,QAAQ,gBAAgB,SAAS,GAAG;AACjE,UAAI,SAAS,WAAW,GAAG;AACzB,eAAO,oBAAoB,QAAQ,eAAe;AAAA,MACpD;AAAA,IACF;AAAA,EACF,GAAG,CAAA,CAAE;AAEL,YAAU,MAAM;AACd,QAAI,QAAQ,MAAM;AAChB,aAAO,UAAA;AAAA,IACT,OAAO;AACL,aAAO,YAAA;AAAA,IACT;AAAA,EACF,GAAG,CAAC,QAAQ,QAAQ,IAAI,CAAC;AAEzB,YAAU,MAAM;AACd,WAAO,cAAA;AAEP,WAAO,MAAM;AAKX,UAAI,WAAW,QAAQ,MAAM;AAC3B,eAAO,YAAA;AAAA,MACT,OAAO;AACL,eAAO,KAAA;AAAA,MACT;AACA,aAAO,QAAA;AAAA,IACT;AAAA,EACF,GAAG,CAAC,MAAM,CAAC;AAEX,QAAM,cAAc;AAAA,IAClB,OAAO,YAAwC;AAC7C,YAAM,OAAO,YAAY,OAAO;AAAA,IAClC;AAAA,IACA,CAAC,MAAM;AAAA,EAAA;AAGT,QAAM,SAAS;AAAA,IACb,OAAO,YAAsC;AAC3C,YAAM,OAAO,OAAO,OAAO;AAAA,IAC7B;AAAA,IACA,CAAC,MAAM;AAAA,EAAA;AAGT,QAAM,SAAS,YAAY,YAAY;AACrC,UAAM,OAAO,OAAA;AAAA,EACf,GAAG,CAAC,MAAM,CAAC;AAEX,QAAM,OAAO,YAAY,MAAM;AAC7B,WAAO,KAAA;AAAA,EACT,GAAG,CAAC,MAAM,CAAC;AAEX,QAAM,QAAQ,YAAY,MAAM;AAC9B,WAAO,MAAA;AAAA,EACT,GAAG,CAAC,MAAM,CAAC;AAEX,QAAM,sBAAsB;AAAA,IAC1B,CAAC,gBAA0C;AACzC,aAAO,oBAAoB,WAAW;AAAA,IACxC;AAAA,IACA,CAAC,MAAM;AAAA,EAAA;AAGT,QAAM,gBAAgB;AAAA,IACpB,OAAO,WAMD;AACJ,YAAM,OAAO,cAAc,MAAM;AAAA,IACnC;AAAA,IACA,CAAC,MAAM;AAAA,EAAA;AAGT,QAAM,0BAA0B;AAAA,IAC9B,OAAO,aAAgD;AACrD,YAAM,OAAO,wBAAwB,QAAQ;AAAA,IAC/C;AAAA,IACA,CAAC,MAAM;AAAA,EAAA;AAaT,QAAM,uBAAuB,QAAqC,MAAM;AACtE,QAAI,gBAAgB;AACpB,aAAS,IAAI,SAAS,SAAS,GAAG,KAAK,GAAG,KAAK;AAC7C,UAAI,SAAS,CAAC,GAAG,SAAS,QAAQ;AAChC,wBAAgB;AAChB;AAAA,MACF;AAAA,IACF;AACA,QAAI,kBAAkB,GAAI,QAAO;AACjC,aAAS,IAAI,SAAS,SAAS,GAAG,IAAI,eAAe,KAAK;AACxD,YAAM,IAAI,SAAS,CAAC;AACpB,UAAI,GAAG,SAAS,YAAa;AAC7B,YAAM,OAAO,EAAE,MAAM;AAAA,QACnB,CAAC,MAAiC,EAAE,SAAS;AAAA,MAAA;AAE/C,UAAI,KAAM,QAAO;AAAA,IACnB;AACA,WAAO;AAAA,EACT,GAAG,CAAC,QAAQ,CAAC;AAEb,QAAM,UAAU,QAAiB,MAAM;AACrC,QAAI,CAAC,qBAAsB,QAAO,CAAA;AAClC,UAAM,IAAI,qBAAqB,WAAW,qBAAqB;AAC/D,WAAQ,KAAK,CAAA;AAAA,EACf,GAAG,CAAC,oBAAoB,CAAC;AAEzB,QAAM,QAAQ,QAAsB,MAAM;AACxC,QAAI,CAAC,wBAAwB,qBAAqB,WAAW,YAAY;AACvE,aAAO;AAAA,IACT;AACA,WAAO,qBAAqB;AAAA,EAC9B,GAAG,CAAC,oBAAoB,CAAC;AAMzB,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,aAAa;AAAA,IACb;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EAAA;AAEJ;"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AudioGenerationResult, StreamChunk } from '@tanstack/ai';
|
|
2
|
-
import { AudioGenerateInput, ConnectConnectionAdapter, GenerationClientState, GenerationFetcher, InferGenerationOutput } from '@tanstack/ai-client';
|
|
2
|
+
import { AIDevtoolsDisplayOptions, AudioGenerateInput, ConnectConnectionAdapter, GenerationClientState, GenerationFetcher, InferGenerationOutput } from '@tanstack/ai-client';
|
|
3
3
|
/**
|
|
4
4
|
* Options for the useGenerateAudio hook.
|
|
5
5
|
*
|
|
@@ -14,6 +14,8 @@ export interface UseGenerateAudioOptions<TOutput = AudioGenerationResult> {
|
|
|
14
14
|
id?: string;
|
|
15
15
|
/** Additional body parameters to send with connect-based adapter requests */
|
|
16
16
|
body?: Record<string, any>;
|
|
17
|
+
/** Display options for TanStack AI Devtools. */
|
|
18
|
+
devtools?: AIDevtoolsDisplayOptions;
|
|
17
19
|
/**
|
|
18
20
|
* Callback when audio is generated. Can optionally return a transformed value.
|
|
19
21
|
*
|
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
import { useGeneration } from "./use-generation.js";
|
|
2
2
|
function useGenerateAudio(options) {
|
|
3
|
-
const
|
|
3
|
+
const devtools = {
|
|
4
|
+
...options.devtools,
|
|
5
|
+
framework: "react",
|
|
6
|
+
hookName: "useGenerateAudio",
|
|
7
|
+
outputKind: "audio"
|
|
8
|
+
};
|
|
9
|
+
const { generate, result, isLoading, error, status, stop, reset } = useGeneration({
|
|
10
|
+
...options,
|
|
11
|
+
devtools
|
|
12
|
+
});
|
|
4
13
|
return {
|
|
5
14
|
generate,
|
|
6
15
|
result,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-generate-audio.js","sources":["../../src/use-generate-audio.ts"],"sourcesContent":["import { useGeneration } from './use-generation'\nimport type { AudioGenerationResult, StreamChunk } from '@tanstack/ai'\nimport type {\n AudioGenerateInput,\n ConnectConnectionAdapter,\n GenerationClientState,\n GenerationFetcher,\n InferGenerationOutput,\n} from '@tanstack/ai-client'\n\n/**\n * Options for the useGenerateAudio hook.\n *\n * @template TOutput - The output type after optional transform (defaults to AudioGenerationResult)\n */\nexport interface UseGenerateAudioOptions<TOutput = AudioGenerationResult> {\n /** Connect-based adapter for streaming transport (SSE, HTTP stream, custom) */\n connection?: ConnectConnectionAdapter\n /** Direct async function for audio generation */\n fetcher?: GenerationFetcher<AudioGenerateInput, AudioGenerationResult>\n /** Unique identifier for this generation instance */\n id?: string\n /** Additional body parameters to send with connect-based adapter requests */\n body?: Record<string, any>\n /**\n * Callback when audio is generated. Can optionally return a transformed value.\n *\n * - Return a non-null value to transform and store it as the result\n * - Return `null` to keep the previous result unchanged\n * - Return nothing (`void`) to store the raw result as-is\n */\n onResult?: (result: AudioGenerationResult) => TOutput | null | void\n /** Callback when an error occurs */\n onError?: (error: Error) => void\n /** Callback when progress is reported (0-100) */\n onProgress?: (progress: number, message?: string) => void\n /** Callback for each stream chunk (connect-based adapter mode only) */\n onChunk?: (chunk: StreamChunk) => void\n}\n\n/**\n * Return type for the useGenerateAudio hook.\n *\n * @template TOutput - The output type (after optional transform)\n */\nexport interface UseGenerateAudioReturn<TOutput = AudioGenerationResult> {\n /** Trigger audio generation */\n generate: (input: AudioGenerateInput) => Promise<void>\n /** The generation result containing audio, or null */\n result: TOutput | null\n /** Whether generation is in progress */\n isLoading: boolean\n /** Current error, if any */\n error: Error | undefined\n /** Current state of the generation */\n status: GenerationClientState\n /** Abort the current generation */\n stop: () => void\n /** Clear result, error, and return to idle */\n reset: () => void\n}\n\n/**\n * React hook for generating audio (music, sound effects) using AI models.\n *\n * Supports two transport modes:\n * - **ConnectConnectionAdapter** — Streaming transport (SSE, HTTP stream, custom)\n * - **Fetcher** — Direct async function call\n *\n * @example\n * ```tsx\n * import { useGenerateAudio } from '@tanstack/ai-react'\n * import { fetchServerSentEvents } from '@tanstack/ai-client'\n *\n * function AudioGenerator() {\n * const { generate, result, isLoading, error, reset } = useGenerateAudio({\n * connection: fetchServerSentEvents('/api/generate/audio'),\n * })\n *\n * return (\n * <div>\n * <button onClick={() => generate({ prompt: 'An upbeat electronic track', duration: 10 })}>\n * Generate\n * </button>\n * {isLoading && <p>Generating...</p>}\n * {error && <p>Error: {error.message}</p>}\n * {result?.audio.url && <audio src={result.audio.url} controls />}\n * </div>\n * )\n * }\n * ```\n */\nexport function useGenerateAudio<\n TOnResult extends ((result: AudioGenerationResult) => any) | undefined =\n undefined,\n>(\n options: Omit<UseGenerateAudioOptions, 'onResult'> & {\n onResult?: TOnResult\n },\n): UseGenerateAudioReturn<\n InferGenerationOutput<AudioGenerationResult, TOnResult>\n> {\n const { generate, result, isLoading, error, status, stop, reset } =\n useGeneration<AudioGenerateInput, AudioGenerationResult, TOnResult>(options)\n\n return {\n generate: generate as (input: AudioGenerateInput) => Promise<void>,\n result,\n isLoading,\n error,\n status,\n stop,\n reset,\n }\n}\n"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"use-generate-audio.js","sources":["../../src/use-generate-audio.ts"],"sourcesContent":["import { useGeneration } from './use-generation'\nimport type { AudioGenerationResult, StreamChunk } from '@tanstack/ai'\nimport type {\n AIDevtoolsDisplayOptions,\n AudioGenerateInput,\n ConnectConnectionAdapter,\n GenerationClientState,\n GenerationFetcher,\n InferGenerationOutput,\n} from '@tanstack/ai-client'\n\n/**\n * Options for the useGenerateAudio hook.\n *\n * @template TOutput - The output type after optional transform (defaults to AudioGenerationResult)\n */\nexport interface UseGenerateAudioOptions<TOutput = AudioGenerationResult> {\n /** Connect-based adapter for streaming transport (SSE, HTTP stream, custom) */\n connection?: ConnectConnectionAdapter\n /** Direct async function for audio generation */\n fetcher?: GenerationFetcher<AudioGenerateInput, AudioGenerationResult>\n /** Unique identifier for this generation instance */\n id?: string\n /** Additional body parameters to send with connect-based adapter requests */\n body?: Record<string, any>\n /** Display options for TanStack AI Devtools. */\n devtools?: AIDevtoolsDisplayOptions\n /**\n * Callback when audio is generated. Can optionally return a transformed value.\n *\n * - Return a non-null value to transform and store it as the result\n * - Return `null` to keep the previous result unchanged\n * - Return nothing (`void`) to store the raw result as-is\n */\n onResult?: (result: AudioGenerationResult) => TOutput | null | void\n /** Callback when an error occurs */\n onError?: (error: Error) => void\n /** Callback when progress is reported (0-100) */\n onProgress?: (progress: number, message?: string) => void\n /** Callback for each stream chunk (connect-based adapter mode only) */\n onChunk?: (chunk: StreamChunk) => void\n}\n\n/**\n * Return type for the useGenerateAudio hook.\n *\n * @template TOutput - The output type (after optional transform)\n */\nexport interface UseGenerateAudioReturn<TOutput = AudioGenerationResult> {\n /** Trigger audio generation */\n generate: (input: AudioGenerateInput) => Promise<void>\n /** The generation result containing audio, or null */\n result: TOutput | null\n /** Whether generation is in progress */\n isLoading: boolean\n /** Current error, if any */\n error: Error | undefined\n /** Current state of the generation */\n status: GenerationClientState\n /** Abort the current generation */\n stop: () => void\n /** Clear result, error, and return to idle */\n reset: () => void\n}\n\n/**\n * React hook for generating audio (music, sound effects) using AI models.\n *\n * Supports two transport modes:\n * - **ConnectConnectionAdapter** — Streaming transport (SSE, HTTP stream, custom)\n * - **Fetcher** — Direct async function call\n *\n * @example\n * ```tsx\n * import { useGenerateAudio } from '@tanstack/ai-react'\n * import { fetchServerSentEvents } from '@tanstack/ai-client'\n *\n * function AudioGenerator() {\n * const { generate, result, isLoading, error, reset } = useGenerateAudio({\n * connection: fetchServerSentEvents('/api/generate/audio'),\n * })\n *\n * return (\n * <div>\n * <button onClick={() => generate({ prompt: 'An upbeat electronic track', duration: 10 })}>\n * Generate\n * </button>\n * {isLoading && <p>Generating...</p>}\n * {error && <p>Error: {error.message}</p>}\n * {result?.audio.url && <audio src={result.audio.url} controls />}\n * </div>\n * )\n * }\n * ```\n */\nexport function useGenerateAudio<\n TOnResult extends ((result: AudioGenerationResult) => any) | undefined =\n undefined,\n>(\n options: Omit<UseGenerateAudioOptions, 'onResult'> & {\n onResult?: TOnResult\n },\n): UseGenerateAudioReturn<\n InferGenerationOutput<AudioGenerationResult, TOnResult>\n> {\n const devtools = {\n ...options.devtools,\n framework: 'react',\n hookName: 'useGenerateAudio',\n outputKind: 'audio' as const,\n }\n const { generate, result, isLoading, error, status, stop, reset } =\n useGeneration<AudioGenerateInput, AudioGenerationResult, TOnResult>({\n ...options,\n devtools,\n })\n\n return {\n generate: generate as (input: AudioGenerateInput) => Promise<void>,\n result,\n isLoading,\n error,\n status,\n stop,\n reset,\n }\n}\n"],"names":[],"mappings":";AA+FO,SAAS,iBAId,SAKA;AACA,QAAM,WAAW;AAAA,IACf,GAAG,QAAQ;AAAA,IACX,WAAW;AAAA,IACX,UAAU;AAAA,IACV,YAAY;AAAA,EAAA;AAEd,QAAM,EAAE,UAAU,QAAQ,WAAW,OAAO,QAAQ,MAAM,MAAA,IACxD,cAAoE;AAAA,IAClE,GAAG;AAAA,IACH;AAAA,EAAA,CACD;AAEH,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EAAA;AAEJ;"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ImageGenerationResult, StreamChunk } from '@tanstack/ai';
|
|
2
|
-
import { ConnectConnectionAdapter, GenerationClientState, GenerationFetcher, ImageGenerateInput, InferGenerationOutput } from '@tanstack/ai-client';
|
|
2
|
+
import { AIDevtoolsDisplayOptions, ConnectConnectionAdapter, GenerationClientState, GenerationFetcher, ImageGenerateInput, InferGenerationOutput } from '@tanstack/ai-client';
|
|
3
3
|
/**
|
|
4
4
|
* Options for the useGenerateImage hook.
|
|
5
5
|
*
|
|
@@ -14,6 +14,8 @@ export interface UseGenerateImageOptions<TOutput = ImageGenerationResult> {
|
|
|
14
14
|
id?: string;
|
|
15
15
|
/** Additional body parameters to send with connect-based adapter requests */
|
|
16
16
|
body?: Record<string, any>;
|
|
17
|
+
/** Display options for TanStack AI Devtools. */
|
|
18
|
+
devtools?: AIDevtoolsDisplayOptions;
|
|
17
19
|
/**
|
|
18
20
|
* Callback when images are generated. Can optionally return a transformed value.
|
|
19
21
|
*
|
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
import { useGeneration } from "./use-generation.js";
|
|
2
2
|
function useGenerateImage(options) {
|
|
3
|
-
const
|
|
3
|
+
const devtools = {
|
|
4
|
+
...options.devtools,
|
|
5
|
+
framework: "react",
|
|
6
|
+
hookName: "useGenerateImage",
|
|
7
|
+
outputKind: "image"
|
|
8
|
+
};
|
|
9
|
+
const { generate, result, isLoading, error, status, stop, reset } = useGeneration({
|
|
10
|
+
...options,
|
|
11
|
+
devtools
|
|
12
|
+
});
|
|
4
13
|
return {
|
|
5
14
|
generate,
|
|
6
15
|
result,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-generate-image.js","sources":["../../src/use-generate-image.ts"],"sourcesContent":["import { useGeneration } from './use-generation'\nimport type { ImageGenerationResult, StreamChunk } from '@tanstack/ai'\nimport type {\n ConnectConnectionAdapter,\n GenerationClientState,\n GenerationFetcher,\n ImageGenerateInput,\n InferGenerationOutput,\n} from '@tanstack/ai-client'\n\n/**\n * Options for the useGenerateImage hook.\n *\n * @template TOutput - The output type after optional transform (defaults to ImageGenerationResult)\n */\nexport interface UseGenerateImageOptions<TOutput = ImageGenerationResult> {\n /** Connect-based adapter for streaming transport (SSE, HTTP stream, custom) */\n connection?: ConnectConnectionAdapter\n /** Direct async function for image generation */\n fetcher?: GenerationFetcher<ImageGenerateInput, ImageGenerationResult>\n /** Unique identifier for this generation instance */\n id?: string\n /** Additional body parameters to send with connect-based adapter requests */\n body?: Record<string, any>\n /**\n * Callback when images are generated. Can optionally return a transformed value.\n *\n * - Return a non-null value to transform and store it as the result\n * - Return `null` to keep the previous result unchanged\n * - Return nothing (`void`) to store the raw result as-is\n */\n onResult?: (result: ImageGenerationResult) => TOutput | null | void\n /** Callback when an error occurs */\n onError?: (error: Error) => void\n /** Callback when progress is reported (0-100) */\n onProgress?: (progress: number, message?: string) => void\n /** Callback for each stream chunk (connect-based adapter mode only) */\n onChunk?: (chunk: StreamChunk) => void\n}\n\n/**\n * Return type for the useGenerateImage hook.\n *\n * @template TOutput - The output type (after optional transform)\n */\nexport interface UseGenerateImageReturn<TOutput = ImageGenerationResult> {\n /** Trigger image generation */\n generate: (input: ImageGenerateInput) => Promise<void>\n /** The generation result containing images, or null */\n result: TOutput | null\n /** Whether generation is in progress */\n isLoading: boolean\n /** Current error, if any */\n error: Error | undefined\n /** Current state of the generation */\n status: GenerationClientState\n /** Abort the current generation */\n stop: () => void\n /** Clear result, error, and return to idle */\n reset: () => void\n}\n\n/**\n * React hook for generating images using AI models.\n *\n * Supports two transport modes:\n * - **ConnectConnectionAdapter** — Streaming transport (SSE, HTTP stream, custom)\n * - **Fetcher** — Direct async function call\n *\n * @example\n * ```tsx\n * import { useGenerateImage } from '@tanstack/ai-react'\n * import { fetchServerSentEvents } from '@tanstack/ai-client'\n *\n * function ImageGenerator() {\n * const { generate, result, isLoading, error, reset } = useGenerateImage({\n * connection: fetchServerSentEvents('/api/generate/image'),\n * })\n *\n * return (\n * <div>\n * <button onClick={() => generate({ prompt: 'A sunset over mountains' })}>\n * Generate\n * </button>\n * {isLoading && <p>Generating...</p>}\n * {error && <p>Error: {error.message}</p>}\n * {result?.images.map((img, i) => (\n * <img key={i} src={img.url || `data:image/png;base64,${img.b64Json}`} />\n * ))}\n * </div>\n * )\n * }\n * ```\n */\nexport function useGenerateImage<\n TOnResult extends ((result: ImageGenerationResult) => any) | undefined =\n undefined,\n>(\n options: Omit<UseGenerateImageOptions, 'onResult'> & {\n onResult?: TOnResult\n },\n): UseGenerateImageReturn<\n InferGenerationOutput<ImageGenerationResult, TOnResult>\n> {\n const { generate, result, isLoading, error, status, stop, reset } =\n useGeneration<ImageGenerateInput, ImageGenerationResult, TOnResult>(options)\n\n return {\n generate: generate as (input: ImageGenerateInput) => Promise<void>,\n result,\n isLoading,\n error,\n status,\n stop,\n reset,\n }\n}\n"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"use-generate-image.js","sources":["../../src/use-generate-image.ts"],"sourcesContent":["import { useGeneration } from './use-generation'\nimport type { ImageGenerationResult, StreamChunk } from '@tanstack/ai'\nimport type {\n AIDevtoolsDisplayOptions,\n ConnectConnectionAdapter,\n GenerationClientState,\n GenerationFetcher,\n ImageGenerateInput,\n InferGenerationOutput,\n} from '@tanstack/ai-client'\n\n/**\n * Options for the useGenerateImage hook.\n *\n * @template TOutput - The output type after optional transform (defaults to ImageGenerationResult)\n */\nexport interface UseGenerateImageOptions<TOutput = ImageGenerationResult> {\n /** Connect-based adapter for streaming transport (SSE, HTTP stream, custom) */\n connection?: ConnectConnectionAdapter\n /** Direct async function for image generation */\n fetcher?: GenerationFetcher<ImageGenerateInput, ImageGenerationResult>\n /** Unique identifier for this generation instance */\n id?: string\n /** Additional body parameters to send with connect-based adapter requests */\n body?: Record<string, any>\n /** Display options for TanStack AI Devtools. */\n devtools?: AIDevtoolsDisplayOptions\n /**\n * Callback when images are generated. Can optionally return a transformed value.\n *\n * - Return a non-null value to transform and store it as the result\n * - Return `null` to keep the previous result unchanged\n * - Return nothing (`void`) to store the raw result as-is\n */\n onResult?: (result: ImageGenerationResult) => TOutput | null | void\n /** Callback when an error occurs */\n onError?: (error: Error) => void\n /** Callback when progress is reported (0-100) */\n onProgress?: (progress: number, message?: string) => void\n /** Callback for each stream chunk (connect-based adapter mode only) */\n onChunk?: (chunk: StreamChunk) => void\n}\n\n/**\n * Return type for the useGenerateImage hook.\n *\n * @template TOutput - The output type (after optional transform)\n */\nexport interface UseGenerateImageReturn<TOutput = ImageGenerationResult> {\n /** Trigger image generation */\n generate: (input: ImageGenerateInput) => Promise<void>\n /** The generation result containing images, or null */\n result: TOutput | null\n /** Whether generation is in progress */\n isLoading: boolean\n /** Current error, if any */\n error: Error | undefined\n /** Current state of the generation */\n status: GenerationClientState\n /** Abort the current generation */\n stop: () => void\n /** Clear result, error, and return to idle */\n reset: () => void\n}\n\n/**\n * React hook for generating images using AI models.\n *\n * Supports two transport modes:\n * - **ConnectConnectionAdapter** — Streaming transport (SSE, HTTP stream, custom)\n * - **Fetcher** — Direct async function call\n *\n * @example\n * ```tsx\n * import { useGenerateImage } from '@tanstack/ai-react'\n * import { fetchServerSentEvents } from '@tanstack/ai-client'\n *\n * function ImageGenerator() {\n * const { generate, result, isLoading, error, reset } = useGenerateImage({\n * connection: fetchServerSentEvents('/api/generate/image'),\n * })\n *\n * return (\n * <div>\n * <button onClick={() => generate({ prompt: 'A sunset over mountains' })}>\n * Generate\n * </button>\n * {isLoading && <p>Generating...</p>}\n * {error && <p>Error: {error.message}</p>}\n * {result?.images.map((img, i) => (\n * <img key={i} src={img.url || `data:image/png;base64,${img.b64Json}`} />\n * ))}\n * </div>\n * )\n * }\n * ```\n */\nexport function useGenerateImage<\n TOnResult extends ((result: ImageGenerationResult) => any) | undefined =\n undefined,\n>(\n options: Omit<UseGenerateImageOptions, 'onResult'> & {\n onResult?: TOnResult\n },\n): UseGenerateImageReturn<\n InferGenerationOutput<ImageGenerationResult, TOnResult>\n> {\n const devtools = {\n ...options.devtools,\n framework: 'react',\n hookName: 'useGenerateImage',\n outputKind: 'image' as const,\n }\n const { generate, result, isLoading, error, status, stop, reset } =\n useGeneration<ImageGenerateInput, ImageGenerationResult, TOnResult>({\n ...options,\n devtools,\n })\n\n return {\n generate: generate as (input: ImageGenerateInput) => Promise<void>,\n result,\n isLoading,\n error,\n status,\n stop,\n reset,\n }\n}\n"],"names":[],"mappings":";AAiGO,SAAS,iBAId,SAKA;AACA,QAAM,WAAW;AAAA,IACf,GAAG,QAAQ;AAAA,IACX,WAAW;AAAA,IACX,UAAU;AAAA,IACV,YAAY;AAAA,EAAA;AAEd,QAAM,EAAE,UAAU,QAAQ,WAAW,OAAO,QAAQ,MAAM,MAAA,IACxD,cAAoE;AAAA,IAClE,GAAG;AAAA,IACH;AAAA,EAAA,CACD;AAEH,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EAAA;AAEJ;"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { StreamChunk, TTSResult } from '@tanstack/ai';
|
|
2
|
-
import { ConnectConnectionAdapter, GenerationClientState, GenerationFetcher, InferGenerationOutput, SpeechGenerateInput } from '@tanstack/ai-client';
|
|
2
|
+
import { AIDevtoolsDisplayOptions, ConnectConnectionAdapter, GenerationClientState, GenerationFetcher, InferGenerationOutput, SpeechGenerateInput } from '@tanstack/ai-client';
|
|
3
3
|
/**
|
|
4
4
|
* Options for the useGenerateSpeech hook.
|
|
5
5
|
*
|
|
@@ -14,6 +14,8 @@ export interface UseGenerateSpeechOptions<TOutput = TTSResult> {
|
|
|
14
14
|
id?: string;
|
|
15
15
|
/** Additional body parameters to send with connect-based adapter requests */
|
|
16
16
|
body?: Record<string, any>;
|
|
17
|
+
/** Display options for TanStack AI Devtools. */
|
|
18
|
+
devtools?: AIDevtoolsDisplayOptions;
|
|
17
19
|
/**
|
|
18
20
|
* Callback when speech is generated. Can optionally return a transformed value.
|
|
19
21
|
*
|
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
import { useGeneration } from "./use-generation.js";
|
|
2
2
|
function useGenerateSpeech(options) {
|
|
3
|
-
const
|
|
3
|
+
const devtools = {
|
|
4
|
+
...options.devtools,
|
|
5
|
+
framework: "react",
|
|
6
|
+
hookName: "useGenerateSpeech",
|
|
7
|
+
outputKind: "audio"
|
|
8
|
+
};
|
|
9
|
+
const { generate, result, isLoading, error, status, stop, reset } = useGeneration({
|
|
10
|
+
...options,
|
|
11
|
+
devtools
|
|
12
|
+
});
|
|
4
13
|
return {
|
|
5
14
|
generate,
|
|
6
15
|
result,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-generate-speech.js","sources":["../../src/use-generate-speech.ts"],"sourcesContent":["import { useGeneration } from './use-generation'\nimport type { StreamChunk, TTSResult } from '@tanstack/ai'\nimport type {\n ConnectConnectionAdapter,\n GenerationClientState,\n GenerationFetcher,\n InferGenerationOutput,\n SpeechGenerateInput,\n} from '@tanstack/ai-client'\n\n/**\n * Options for the useGenerateSpeech hook.\n *\n * @template TOutput - The output type after optional transform (defaults to TTSResult)\n */\nexport interface UseGenerateSpeechOptions<TOutput = TTSResult> {\n /** Connect-based adapter for streaming transport (SSE, HTTP stream, custom) */\n connection?: ConnectConnectionAdapter\n /** Direct async function for speech generation */\n fetcher?: GenerationFetcher<SpeechGenerateInput, TTSResult>\n /** Unique identifier for this generation instance */\n id?: string\n /** Additional body parameters to send with connect-based adapter requests */\n body?: Record<string, any>\n /**\n * Callback when speech is generated. Can optionally return a transformed value.\n *\n * - Return a non-null value to transform and store it as the result\n * - Return `null` to keep the previous result unchanged\n * - Return nothing (`void`) to store the raw result as-is\n */\n onResult?: (result: TTSResult) => TOutput | null | void\n /** Callback when an error occurs */\n onError?: (error: Error) => void\n /** Callback when progress is reported (0-100) */\n onProgress?: (progress: number, message?: string) => void\n /** Callback for each stream chunk (connect-based adapter mode only) */\n onChunk?: (chunk: StreamChunk) => void\n}\n\n/**\n * Return type for the useGenerateSpeech hook.\n *\n * @template TOutput - The output type (after optional transform)\n */\nexport interface UseGenerateSpeechReturn<TOutput = TTSResult> {\n /** Trigger speech generation */\n generate: (input: SpeechGenerateInput) => Promise<void>\n /** The TTS result containing audio data, or null */\n result: TOutput | null\n /** Whether generation is in progress */\n isLoading: boolean\n /** Current error, if any */\n error: Error | undefined\n /** Current state of the generation */\n status: GenerationClientState\n /** Abort the current generation */\n stop: () => void\n /** Clear result, error, and return to idle */\n reset: () => void\n}\n\n/**\n * React hook for generating speech (text-to-speech) using AI models.\n *\n * @example\n * ```tsx\n * import { useGenerateSpeech } from '@tanstack/ai-react'\n * import { fetchServerSentEvents } from '@tanstack/ai-client'\n *\n * function SpeechGenerator() {\n * const { generate, result, isLoading } = useGenerateSpeech({\n * connection: fetchServerSentEvents('/api/generate/speech'),\n * })\n *\n * return (\n * <div>\n * <button onClick={() => generate({ text: 'Hello world', voice: 'alloy' })}>\n * Generate Speech\n * </button>\n * {result && (\n * <audio src={`data:audio/${result.format};base64,${result.audio}`} controls />\n * )}\n * </div>\n * )\n * }\n * ```\n */\nexport function useGenerateSpeech<\n TOnResult extends ((result: TTSResult) => any) | undefined = undefined,\n>(\n options: Omit<UseGenerateSpeechOptions, 'onResult'> & {\n onResult?: TOnResult\n },\n): UseGenerateSpeechReturn<InferGenerationOutput<TTSResult, TOnResult>> {\n const { generate, result, isLoading, error, status, stop, reset } =\n useGeneration<SpeechGenerateInput, TTSResult, TOnResult>(options)\n\n return {\n generate: generate as (input: SpeechGenerateInput) => Promise<void>,\n result,\n isLoading,\n error,\n status,\n stop,\n reset,\n }\n}\n"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"use-generate-speech.js","sources":["../../src/use-generate-speech.ts"],"sourcesContent":["import { useGeneration } from './use-generation'\nimport type { StreamChunk, TTSResult } from '@tanstack/ai'\nimport type {\n AIDevtoolsDisplayOptions,\n ConnectConnectionAdapter,\n GenerationClientState,\n GenerationFetcher,\n InferGenerationOutput,\n SpeechGenerateInput,\n} from '@tanstack/ai-client'\n\n/**\n * Options for the useGenerateSpeech hook.\n *\n * @template TOutput - The output type after optional transform (defaults to TTSResult)\n */\nexport interface UseGenerateSpeechOptions<TOutput = TTSResult> {\n /** Connect-based adapter for streaming transport (SSE, HTTP stream, custom) */\n connection?: ConnectConnectionAdapter\n /** Direct async function for speech generation */\n fetcher?: GenerationFetcher<SpeechGenerateInput, TTSResult>\n /** Unique identifier for this generation instance */\n id?: string\n /** Additional body parameters to send with connect-based adapter requests */\n body?: Record<string, any>\n /** Display options for TanStack AI Devtools. */\n devtools?: AIDevtoolsDisplayOptions\n /**\n * Callback when speech is generated. Can optionally return a transformed value.\n *\n * - Return a non-null value to transform and store it as the result\n * - Return `null` to keep the previous result unchanged\n * - Return nothing (`void`) to store the raw result as-is\n */\n onResult?: (result: TTSResult) => TOutput | null | void\n /** Callback when an error occurs */\n onError?: (error: Error) => void\n /** Callback when progress is reported (0-100) */\n onProgress?: (progress: number, message?: string) => void\n /** Callback for each stream chunk (connect-based adapter mode only) */\n onChunk?: (chunk: StreamChunk) => void\n}\n\n/**\n * Return type for the useGenerateSpeech hook.\n *\n * @template TOutput - The output type (after optional transform)\n */\nexport interface UseGenerateSpeechReturn<TOutput = TTSResult> {\n /** Trigger speech generation */\n generate: (input: SpeechGenerateInput) => Promise<void>\n /** The TTS result containing audio data, or null */\n result: TOutput | null\n /** Whether generation is in progress */\n isLoading: boolean\n /** Current error, if any */\n error: Error | undefined\n /** Current state of the generation */\n status: GenerationClientState\n /** Abort the current generation */\n stop: () => void\n /** Clear result, error, and return to idle */\n reset: () => void\n}\n\n/**\n * React hook for generating speech (text-to-speech) using AI models.\n *\n * @example\n * ```tsx\n * import { useGenerateSpeech } from '@tanstack/ai-react'\n * import { fetchServerSentEvents } from '@tanstack/ai-client'\n *\n * function SpeechGenerator() {\n * const { generate, result, isLoading } = useGenerateSpeech({\n * connection: fetchServerSentEvents('/api/generate/speech'),\n * })\n *\n * return (\n * <div>\n * <button onClick={() => generate({ text: 'Hello world', voice: 'alloy' })}>\n * Generate Speech\n * </button>\n * {result && (\n * <audio src={`data:audio/${result.format};base64,${result.audio}`} controls />\n * )}\n * </div>\n * )\n * }\n * ```\n */\nexport function useGenerateSpeech<\n TOnResult extends ((result: TTSResult) => any) | undefined = undefined,\n>(\n options: Omit<UseGenerateSpeechOptions, 'onResult'> & {\n onResult?: TOnResult\n },\n): UseGenerateSpeechReturn<InferGenerationOutput<TTSResult, TOnResult>> {\n const devtools = {\n ...options.devtools,\n framework: 'react',\n hookName: 'useGenerateSpeech',\n outputKind: 'audio' as const,\n }\n const { generate, result, isLoading, error, status, stop, reset } =\n useGeneration<SpeechGenerateInput, TTSResult, TOnResult>({\n ...options,\n devtools,\n })\n\n return {\n generate: generate as (input: SpeechGenerateInput) => Promise<void>,\n result,\n isLoading,\n error,\n status,\n stop,\n reset,\n }\n}\n"],"names":[],"mappings":";AA2FO,SAAS,kBAGd,SAGsE;AACtE,QAAM,WAAW;AAAA,IACf,GAAG,QAAQ;AAAA,IACX,WAAW;AAAA,IACX,UAAU;AAAA,IACV,YAAY;AAAA,EAAA;AAEd,QAAM,EAAE,UAAU,QAAQ,WAAW,OAAO,QAAQ,MAAM,MAAA,IACxD,cAAyD;AAAA,IACvD,GAAG;AAAA,IACH;AAAA,EAAA,CACD;AAEH,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EAAA;AAEJ;"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { StreamChunk } from '@tanstack/ai';
|
|
2
|
-
import { ConnectConnectionAdapter, GenerationClientState, GenerationFetcher, InferGenerationOutput, VideoGenerateInput, VideoGenerateResult, VideoStatusInfo } from '@tanstack/ai-client';
|
|
2
|
+
import { AIDevtoolsDisplayOptions, ConnectConnectionAdapter, GenerationClientState, GenerationFetcher, InferGenerationOutput, VideoGenerateInput, VideoGenerateResult, VideoStatusInfo } from '@tanstack/ai-client';
|
|
3
3
|
/**
|
|
4
4
|
* Options for the useGenerateVideo hook.
|
|
5
5
|
*/
|
|
@@ -12,6 +12,8 @@ export interface UseGenerateVideoOptions<TOutput = VideoGenerateResult> {
|
|
|
12
12
|
id?: string;
|
|
13
13
|
/** Additional body parameters to send with connect-based adapter requests */
|
|
14
14
|
body?: Record<string, any>;
|
|
15
|
+
/** Display options for TanStack AI Devtools. */
|
|
16
|
+
devtools?: AIDevtoolsDisplayOptions;
|
|
15
17
|
/**
|
|
16
18
|
* Callback when video generation completes. Can optionally return a transformed value.
|
|
17
19
|
*
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { VideoGenerationClient } from "@tanstack/ai-client";
|
|
2
|
+
import { createVideoDevtoolsBridge } from "@tanstack/ai-client/devtools";
|
|
2
3
|
import { useId, useState, useRef, useMemo, useEffect, useCallback } from "react";
|
|
3
4
|
function useGenerateVideo(options) {
|
|
4
5
|
const hookId = useId();
|
|
@@ -16,6 +17,13 @@ function useGenerateVideo(options) {
|
|
|
16
17
|
const baseOptions = {
|
|
17
18
|
id: clientId,
|
|
18
19
|
body: opts.body,
|
|
20
|
+
devtoolsBridgeFactory: createVideoDevtoolsBridge,
|
|
21
|
+
devtools: {
|
|
22
|
+
...opts.devtools,
|
|
23
|
+
framework: "react",
|
|
24
|
+
hookName: "useGenerateVideo",
|
|
25
|
+
outputKind: "video"
|
|
26
|
+
},
|
|
19
27
|
onResult: (r) => optionsRef.current.onResult?.(r),
|
|
20
28
|
onError: (e) => {
|
|
21
29
|
optionsRef.current.onError?.(e);
|
|
@@ -61,8 +69,9 @@ function useGenerateVideo(options) {
|
|
|
61
69
|
});
|
|
62
70
|
}, [client, options.body]);
|
|
63
71
|
useEffect(() => {
|
|
72
|
+
client.mountDevtools();
|
|
64
73
|
return () => {
|
|
65
|
-
client.
|
|
74
|
+
client.dispose();
|
|
66
75
|
};
|
|
67
76
|
}, [client]);
|
|
68
77
|
const generate = useCallback(
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-generate-video.js","sources":["../../src/use-generate-video.ts"],"sourcesContent":["import { VideoGenerationClient } from '@tanstack/ai-client'\nimport { useCallback, useEffect, useId, useMemo, useRef, useState } from 'react'\nimport type { StreamChunk } from '@tanstack/ai'\nimport type {\n ConnectConnectionAdapter,\n GenerationClientState,\n GenerationFetcher,\n InferGenerationOutput,\n VideoGenerateInput,\n VideoGenerateResult,\n VideoStatusInfo,\n} from '@tanstack/ai-client'\n\n/**\n * Options for the useGenerateVideo hook.\n */\nexport interface UseGenerateVideoOptions<TOutput = VideoGenerateResult> {\n /** Connect-based adapter for streaming transport (server handles polling) */\n connection?: ConnectConnectionAdapter\n /** Direct async function that returns a completed video result */\n fetcher?: GenerationFetcher<VideoGenerateInput, VideoGenerateResult>\n /** Unique identifier for this generation instance */\n id?: string\n /** Additional body parameters to send with connect-based adapter requests */\n body?: Record<string, any>\n /**\n * Callback when video generation completes. Can optionally return a transformed value.\n *\n * - Return a non-null value to transform and store it as the result\n * - Return `null` to keep the previous result unchanged\n * - Return nothing (`void`) to store the raw result as-is\n */\n onResult?: (result: VideoGenerateResult) => TOutput | null | void\n /** Callback when an error occurs */\n onError?: (error: Error) => void\n /** Callback when progress is reported (0-100) */\n onProgress?: (progress: number, message?: string) => void\n /** Callback when a video job is created */\n onJobCreated?: (jobId: string) => void\n /** Callback on each status update */\n onStatusUpdate?: (status: VideoStatusInfo) => void\n /** Callback for each stream chunk (connect-based adapter mode only) */\n onChunk?: (chunk: StreamChunk) => void\n}\n\n/**\n * Return type for the useGenerateVideo hook.\n *\n * @template TOutput - The output type (after optional transform)\n */\nexport interface UseGenerateVideoReturn<TOutput = VideoGenerateResult> {\n /** Trigger video generation */\n generate: (input: VideoGenerateInput) => Promise<void>\n /** The final video result (with URL), or null */\n result: TOutput | null\n /** The current job ID, or null */\n jobId: string | null\n /** Current video generation status info, or null */\n videoStatus: VideoStatusInfo | null\n /** Whether generation/polling is in progress */\n isLoading: boolean\n /** Current error, if any */\n error: Error | undefined\n /** Current state of the generation */\n status: GenerationClientState\n /** Abort the current generation/polling */\n stop: () => void\n /** Clear all state and return to idle */\n reset: () => void\n}\n\n/**\n * React hook for generating videos using AI models.\n *\n * Video generation is asynchronous: a job is created, then polled for status\n * until completion. This hook handles the full lifecycle.\n *\n * @example\n * ```tsx\n * import { useGenerateVideo } from '@tanstack/ai-react'\n * import { fetchServerSentEvents } from '@tanstack/ai-client'\n *\n * function VideoGenerator() {\n * const { generate, result, videoStatus, isLoading } = useGenerateVideo({\n * connection: fetchServerSentEvents('/api/generate/video'),\n * onStatusUpdate: (status) => console.log(`Progress: ${status.progress}%`),\n * })\n *\n * return (\n * <div>\n * <button onClick={() => generate({ prompt: 'A flying car over a city' })}>\n * Generate Video\n * </button>\n * {isLoading && videoStatus && (\n * <p>Status: {videoStatus.status} ({videoStatus.progress}%)</p>\n * )}\n * {result && <video src={result.url} controls />}\n * </div>\n * )\n * }\n * ```\n */\nexport function useGenerateVideo<\n TOnResult extends ((result: VideoGenerateResult) => any) | undefined =\n undefined,\n>(\n options: Omit<UseGenerateVideoOptions, 'onResult'> & {\n onResult?: TOnResult\n },\n): UseGenerateVideoReturn<\n InferGenerationOutput<VideoGenerateResult, TOnResult>\n> {\n type TOutput = InferGenerationOutput<VideoGenerateResult, TOnResult>\n const hookId = useId()\n const clientId = options.id || hookId\n\n const [result, setResult] = useState<TOutput | null>(null)\n const [jobId, setJobId] = useState<string | null>(null)\n const [videoStatus, setVideoStatus] = useState<VideoStatusInfo | null>(null)\n const [isLoading, setIsLoading] = useState(false)\n const [error, setError] = useState<Error | undefined>(undefined)\n const [status, setStatus] = useState<GenerationClientState>('idle')\n\n const optionsRef = useRef(options)\n optionsRef.current = options\n\n const client = useMemo(() => {\n const opts = optionsRef.current\n\n // Conditional spread for `body` (strict-optional in target).\n // Optional callbacks are wrapped in non-returning bodies so\n // `?.()`'s implicit `undefined` doesn't widen the function\n // return type (which `exactOptionalPropertyTypes` rejects\n // against the strict-optional target).\n const baseOptions = {\n id: clientId,\n body: opts.body,\n onResult: (r: VideoGenerateResult) => optionsRef.current.onResult?.(r),\n onError: (e: Error) => {\n optionsRef.current.onError?.(e)\n },\n onProgress: (p: number, m?: string) => {\n optionsRef.current.onProgress?.(p, m)\n },\n onChunk: (c: StreamChunk) => {\n optionsRef.current.onChunk?.(c)\n },\n onJobCreated: (id: string) => {\n optionsRef.current.onJobCreated?.(id)\n },\n onStatusUpdate: (s: VideoStatusInfo) => {\n optionsRef.current.onStatusUpdate?.(s)\n },\n onResultChange: setResult,\n onLoadingChange: setIsLoading,\n onErrorChange: setError,\n onStatusChange: setStatus,\n onJobIdChange: setJobId,\n onVideoStatusChange: setVideoStatus,\n }\n\n if (opts.connection) {\n return new VideoGenerationClient<TOutput>({\n ...baseOptions,\n connection: opts.connection,\n })\n }\n\n if (opts.fetcher) {\n return new VideoGenerationClient<TOutput>({\n ...baseOptions,\n fetcher: opts.fetcher,\n })\n }\n\n throw new Error(\n 'useGenerateVideo requires either a connection or fetcher option',\n )\n }, [clientId])\n\n // Sync body changes without recreating client\n useEffect(() => {\n // Conditional spread: target uses strict-optional `body?: T`.\n client.updateOptions({\n ...(options.body !== undefined && { body: options.body }),\n })\n }, [client, options.body])\n\n // Cleanup on unmount\n useEffect(() => {\n return () => {\n client.
|
|
1
|
+
{"version":3,"file":"use-generate-video.js","sources":["../../src/use-generate-video.ts"],"sourcesContent":["import { VideoGenerationClient } from '@tanstack/ai-client'\nimport { createVideoDevtoolsBridge } from '@tanstack/ai-client/devtools'\nimport { useCallback, useEffect, useId, useMemo, useRef, useState } from 'react'\nimport type { StreamChunk } from '@tanstack/ai'\nimport type {\n AIDevtoolsDisplayOptions,\n ConnectConnectionAdapter,\n GenerationClientState,\n GenerationFetcher,\n InferGenerationOutput,\n VideoGenerateInput,\n VideoGenerateResult,\n VideoStatusInfo,\n} from '@tanstack/ai-client'\n\n/**\n * Options for the useGenerateVideo hook.\n */\nexport interface UseGenerateVideoOptions<TOutput = VideoGenerateResult> {\n /** Connect-based adapter for streaming transport (server handles polling) */\n connection?: ConnectConnectionAdapter\n /** Direct async function that returns a completed video result */\n fetcher?: GenerationFetcher<VideoGenerateInput, VideoGenerateResult>\n /** Unique identifier for this generation instance */\n id?: string\n /** Additional body parameters to send with connect-based adapter requests */\n body?: Record<string, any>\n /** Display options for TanStack AI Devtools. */\n devtools?: AIDevtoolsDisplayOptions\n /**\n * Callback when video generation completes. Can optionally return a transformed value.\n *\n * - Return a non-null value to transform and store it as the result\n * - Return `null` to keep the previous result unchanged\n * - Return nothing (`void`) to store the raw result as-is\n */\n onResult?: (result: VideoGenerateResult) => TOutput | null | void\n /** Callback when an error occurs */\n onError?: (error: Error) => void\n /** Callback when progress is reported (0-100) */\n onProgress?: (progress: number, message?: string) => void\n /** Callback when a video job is created */\n onJobCreated?: (jobId: string) => void\n /** Callback on each status update */\n onStatusUpdate?: (status: VideoStatusInfo) => void\n /** Callback for each stream chunk (connect-based adapter mode only) */\n onChunk?: (chunk: StreamChunk) => void\n}\n\n/**\n * Return type for the useGenerateVideo hook.\n *\n * @template TOutput - The output type (after optional transform)\n */\nexport interface UseGenerateVideoReturn<TOutput = VideoGenerateResult> {\n /** Trigger video generation */\n generate: (input: VideoGenerateInput) => Promise<void>\n /** The final video result (with URL), or null */\n result: TOutput | null\n /** The current job ID, or null */\n jobId: string | null\n /** Current video generation status info, or null */\n videoStatus: VideoStatusInfo | null\n /** Whether generation/polling is in progress */\n isLoading: boolean\n /** Current error, if any */\n error: Error | undefined\n /** Current state of the generation */\n status: GenerationClientState\n /** Abort the current generation/polling */\n stop: () => void\n /** Clear all state and return to idle */\n reset: () => void\n}\n\n/**\n * React hook for generating videos using AI models.\n *\n * Video generation is asynchronous: a job is created, then polled for status\n * until completion. This hook handles the full lifecycle.\n *\n * @example\n * ```tsx\n * import { useGenerateVideo } from '@tanstack/ai-react'\n * import { fetchServerSentEvents } from '@tanstack/ai-client'\n *\n * function VideoGenerator() {\n * const { generate, result, videoStatus, isLoading } = useGenerateVideo({\n * connection: fetchServerSentEvents('/api/generate/video'),\n * onStatusUpdate: (status) => console.log(`Progress: ${status.progress}%`),\n * })\n *\n * return (\n * <div>\n * <button onClick={() => generate({ prompt: 'A flying car over a city' })}>\n * Generate Video\n * </button>\n * {isLoading && videoStatus && (\n * <p>Status: {videoStatus.status} ({videoStatus.progress}%)</p>\n * )}\n * {result && <video src={result.url} controls />}\n * </div>\n * )\n * }\n * ```\n */\nexport function useGenerateVideo<\n TOnResult extends ((result: VideoGenerateResult) => any) | undefined =\n undefined,\n>(\n options: Omit<UseGenerateVideoOptions, 'onResult'> & {\n onResult?: TOnResult\n },\n): UseGenerateVideoReturn<\n InferGenerationOutput<VideoGenerateResult, TOnResult>\n> {\n type TOutput = InferGenerationOutput<VideoGenerateResult, TOnResult>\n const hookId = useId()\n const clientId = options.id || hookId\n\n const [result, setResult] = useState<TOutput | null>(null)\n const [jobId, setJobId] = useState<string | null>(null)\n const [videoStatus, setVideoStatus] = useState<VideoStatusInfo | null>(null)\n const [isLoading, setIsLoading] = useState(false)\n const [error, setError] = useState<Error | undefined>(undefined)\n const [status, setStatus] = useState<GenerationClientState>('idle')\n\n const optionsRef = useRef(options)\n optionsRef.current = options\n\n const client = useMemo(() => {\n const opts = optionsRef.current\n\n // Conditional spread for `body` (strict-optional in target).\n // Optional callbacks are wrapped in non-returning bodies so\n // `?.()`'s implicit `undefined` doesn't widen the function\n // return type (which `exactOptionalPropertyTypes` rejects\n // against the strict-optional target).\n const baseOptions = {\n id: clientId,\n body: opts.body,\n devtoolsBridgeFactory: createVideoDevtoolsBridge,\n devtools: {\n ...opts.devtools,\n framework: 'react',\n hookName: 'useGenerateVideo',\n outputKind: 'video' as const,\n },\n onResult: (r: VideoGenerateResult) => optionsRef.current.onResult?.(r),\n onError: (e: Error) => {\n optionsRef.current.onError?.(e)\n },\n onProgress: (p: number, m?: string) => {\n optionsRef.current.onProgress?.(p, m)\n },\n onChunk: (c: StreamChunk) => {\n optionsRef.current.onChunk?.(c)\n },\n onJobCreated: (id: string) => {\n optionsRef.current.onJobCreated?.(id)\n },\n onStatusUpdate: (s: VideoStatusInfo) => {\n optionsRef.current.onStatusUpdate?.(s)\n },\n onResultChange: setResult,\n onLoadingChange: setIsLoading,\n onErrorChange: setError,\n onStatusChange: setStatus,\n onJobIdChange: setJobId,\n onVideoStatusChange: setVideoStatus,\n }\n\n if (opts.connection) {\n return new VideoGenerationClient<TOutput>({\n ...baseOptions,\n connection: opts.connection,\n })\n }\n\n if (opts.fetcher) {\n return new VideoGenerationClient<TOutput>({\n ...baseOptions,\n fetcher: opts.fetcher,\n })\n }\n\n throw new Error(\n 'useGenerateVideo requires either a connection or fetcher option',\n )\n }, [clientId])\n\n // Sync body changes without recreating client\n useEffect(() => {\n // Conditional spread: target uses strict-optional `body?: T`.\n client.updateOptions({\n ...(options.body !== undefined && { body: options.body }),\n })\n }, [client, options.body])\n\n // Cleanup on unmount\n useEffect(() => {\n client.mountDevtools()\n\n return () => {\n client.dispose()\n }\n }, [client])\n\n const generate = useCallback(\n async (input: VideoGenerateInput) => {\n await client.generate(input)\n },\n [client],\n )\n\n const stop = useCallback(() => {\n client.stop()\n }, [client])\n\n const reset = useCallback(() => {\n client.reset()\n }, [client])\n\n return {\n generate,\n result,\n jobId,\n videoStatus,\n isLoading,\n error,\n status,\n stop,\n reset,\n }\n}\n"],"names":[],"mappings":";;;AA0GO,SAAS,iBAId,SAKA;AAEA,QAAM,SAAS,MAAA;AACf,QAAM,WAAW,QAAQ,MAAM;AAE/B,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAyB,IAAI;AACzD,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAwB,IAAI;AACtD,QAAM,CAAC,aAAa,cAAc,IAAI,SAAiC,IAAI;AAC3E,QAAM,CAAC,WAAW,YAAY,IAAI,SAAS,KAAK;AAChD,QAAM,CAAC,OAAO,QAAQ,IAAI,SAA4B,MAAS;AAC/D,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAgC,MAAM;AAElE,QAAM,aAAa,OAAO,OAAO;AACjC,aAAW,UAAU;AAErB,QAAM,SAAS,QAAQ,MAAM;AAC3B,UAAM,OAAO,WAAW;AAOxB,UAAM,cAAc;AAAA,MAClB,IAAI;AAAA,MACJ,MAAM,KAAK;AAAA,MACX,uBAAuB;AAAA,MACvB,UAAU;AAAA,QACR,GAAG,KAAK;AAAA,QACR,WAAW;AAAA,QACX,UAAU;AAAA,QACV,YAAY;AAAA,MAAA;AAAA,MAEd,UAAU,CAAC,MAA2B,WAAW,QAAQ,WAAW,CAAC;AAAA,MACrE,SAAS,CAAC,MAAa;AACrB,mBAAW,QAAQ,UAAU,CAAC;AAAA,MAChC;AAAA,MACA,YAAY,CAAC,GAAW,MAAe;AACrC,mBAAW,QAAQ,aAAa,GAAG,CAAC;AAAA,MACtC;AAAA,MACA,SAAS,CAAC,MAAmB;AAC3B,mBAAW,QAAQ,UAAU,CAAC;AAAA,MAChC;AAAA,MACA,cAAc,CAAC,OAAe;AAC5B,mBAAW,QAAQ,eAAe,EAAE;AAAA,MACtC;AAAA,MACA,gBAAgB,CAAC,MAAuB;AACtC,mBAAW,QAAQ,iBAAiB,CAAC;AAAA,MACvC;AAAA,MACA,gBAAgB;AAAA,MAChB,iBAAiB;AAAA,MACjB,eAAe;AAAA,MACf,gBAAgB;AAAA,MAChB,eAAe;AAAA,MACf,qBAAqB;AAAA,IAAA;AAGvB,QAAI,KAAK,YAAY;AACnB,aAAO,IAAI,sBAA+B;AAAA,QACxC,GAAG;AAAA,QACH,YAAY,KAAK;AAAA,MAAA,CAClB;AAAA,IACH;AAEA,QAAI,KAAK,SAAS;AAChB,aAAO,IAAI,sBAA+B;AAAA,QACxC,GAAG;AAAA,QACH,SAAS,KAAK;AAAA,MAAA,CACf;AAAA,IACH;AAEA,UAAM,IAAI;AAAA,MACR;AAAA,IAAA;AAAA,EAEJ,GAAG,CAAC,QAAQ,CAAC;AAGb,YAAU,MAAM;AAEd,WAAO,cAAc;AAAA,MACnB,GAAI,QAAQ,SAAS,UAAa,EAAE,MAAM,QAAQ,KAAA;AAAA,IAAK,CACxD;AAAA,EACH,GAAG,CAAC,QAAQ,QAAQ,IAAI,CAAC;AAGzB,YAAU,MAAM;AACd,WAAO,cAAA;AAEP,WAAO,MAAM;AACX,aAAO,QAAA;AAAA,IACT;AAAA,EACF,GAAG,CAAC,MAAM,CAAC;AAEX,QAAM,WAAW;AAAA,IACf,OAAO,UAA8B;AACnC,YAAM,OAAO,SAAS,KAAK;AAAA,IAC7B;AAAA,IACA,CAAC,MAAM;AAAA,EAAA;AAGT,QAAM,OAAO,YAAY,MAAM;AAC7B,WAAO,KAAA;AAAA,EACT,GAAG,CAAC,MAAM,CAAC;AAEX,QAAM,QAAQ,YAAY,MAAM;AAC9B,WAAO,MAAA;AAAA,EACT,GAAG,CAAC,MAAM,CAAC;AAEX,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EAAA;AAEJ;"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { StreamChunk } from '@tanstack/ai';
|
|
2
|
-
import { ConnectConnectionAdapter, GenerationClientState, GenerationFetcher, InferGenerationOutput } from '@tanstack/ai-client';
|
|
2
|
+
import { AIDevtoolsDisplayOptions, ConnectConnectionAdapter, GenerationClientState, GenerationFetcher, InferGenerationOutput } from '@tanstack/ai-client';
|
|
3
3
|
/**
|
|
4
4
|
* Options for the useGeneration hook.
|
|
5
5
|
*
|
|
@@ -18,6 +18,8 @@ export interface UseGenerationOptions<TInput, TResult, TOutput = TResult> {
|
|
|
18
18
|
id?: string;
|
|
19
19
|
/** Additional body parameters to send with connect-based adapter requests */
|
|
20
20
|
body?: Record<string, any>;
|
|
21
|
+
/** Display options for TanStack AI Devtools. */
|
|
22
|
+
devtools?: AIDevtoolsDisplayOptions;
|
|
21
23
|
/**
|
|
22
24
|
* Callback when a result is received. Can optionally return a transformed value.
|
|
23
25
|
*
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { GenerationClient } from "@tanstack/ai-client";
|
|
2
|
+
import { createGenerationDevtoolsBridge } from "@tanstack/ai-client/devtools";
|
|
2
3
|
import { useId, useState, useRef, useMemo, useEffect, useCallback } from "react";
|
|
3
4
|
function useGeneration(options) {
|
|
4
5
|
const hookId = useId();
|
|
@@ -14,6 +15,12 @@ function useGeneration(options) {
|
|
|
14
15
|
const clientOptions = {
|
|
15
16
|
id: clientId,
|
|
16
17
|
body: opts.body,
|
|
18
|
+
devtoolsBridgeFactory: createGenerationDevtoolsBridge,
|
|
19
|
+
devtools: {
|
|
20
|
+
hookName: "useGeneration",
|
|
21
|
+
framework: "react",
|
|
22
|
+
...opts.devtools
|
|
23
|
+
},
|
|
17
24
|
onResult: (r) => optionsRef.current.onResult?.(r),
|
|
18
25
|
onError: (e) => {
|
|
19
26
|
optionsRef.current.onError?.(e);
|
|
@@ -51,8 +58,9 @@ function useGeneration(options) {
|
|
|
51
58
|
});
|
|
52
59
|
}, [client, options.body]);
|
|
53
60
|
useEffect(() => {
|
|
61
|
+
client.mountDevtools();
|
|
54
62
|
return () => {
|
|
55
|
-
client.
|
|
63
|
+
client.dispose();
|
|
56
64
|
};
|
|
57
65
|
}, [client]);
|
|
58
66
|
const generate = useCallback(
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-generation.js","sources":["../../src/use-generation.ts"],"sourcesContent":["import { GenerationClient } from '@tanstack/ai-client'\nimport { useCallback, useEffect, useId, useMemo, useRef, useState } from 'react'\nimport type { StreamChunk } from '@tanstack/ai'\nimport type {\n ConnectConnectionAdapter,\n GenerationClientOptions,\n GenerationClientState,\n GenerationFetcher,\n InferGenerationOutput,\n} from '@tanstack/ai-client'\n\n/**\n * Options for the useGeneration hook.\n *\n * Accepts either a `connection` (streaming transport) or a `fetcher` (direct async call).\n *\n * @template TInput - The input type for the generation request\n * @template TResult - The result type returned by the generation\n * @template TOutput - The output type after optional transform (defaults to TResult)\n */\nexport interface UseGenerationOptions<TInput, TResult, TOutput = TResult> {\n /** Connect-based adapter for streaming transport (SSE, HTTP stream, custom) */\n connection?: ConnectConnectionAdapter\n /** Direct async function for one-shot generation (no streaming protocol needed) */\n fetcher?: GenerationFetcher<TInput, TResult>\n /** Unique identifier for this generation instance */\n id?: string\n /** Additional body parameters to send with connect-based adapter requests */\n body?: Record<string, any>\n /**\n * Callback when a result is received. Can optionally return a transformed value.\n *\n * - Return a non-null value to transform and store it as the result\n * - Return `null` to keep the previous result unchanged\n * - Return nothing (`void`) to store the raw result as-is\n */\n onResult?: (result: TResult) => TOutput | null | void\n /** Callback when an error occurs */\n onError?: (error: Error) => void\n /** Callback when progress is reported (0-100) */\n onProgress?: (progress: number, message?: string) => void\n /** Callback for each stream chunk (connect-based adapter mode only) */\n onChunk?: (chunk: StreamChunk) => void\n}\n\n/**\n * Return type for the useGeneration hook.\n *\n * @template TOutput - The output type (after optional transform)\n */\nexport interface UseGenerationReturn<TOutput> {\n /** Trigger a generation request */\n generate: (input: Record<string, any>) => Promise<void>\n /** The generation result, or null if not yet generated */\n result: TOutput | null\n /** Whether a generation is currently in progress */\n isLoading: boolean\n /** Current error, if any */\n error: Error | undefined\n /** Current state of the generation client */\n status: GenerationClientState\n /** Abort the current generation */\n stop: () => void\n /** Clear result, error, and return to idle */\n reset: () => void\n}\n\n/**\n * Generic React hook for one-shot generation tasks.\n *\n * This is the base hook used by `useGenerateImage`, `useGenerateSpeech`,\n * `useTranscription`, and `useSummarize`. You can also use it directly\n * for custom generation types.\n *\n * @template TInput - The input type for the generation request\n * @template TResult - The result type returned by the generation\n *\n * @example\n * ```tsx\n * const { generate, result, isLoading } = useGeneration<MyInput, MyResult>({\n * connection: fetchServerSentEvents('/api/generate/custom'),\n * })\n *\n * await generate({ prompt: 'Hello' })\n * ```\n */\nexport function useGeneration<\n TInput extends Record<string, any>,\n TResult,\n TOnResult extends ((result: TResult) => any) | undefined = undefined,\n>(\n options: Omit<UseGenerationOptions<TInput, TResult>, 'onResult'> & {\n onResult?: TOnResult\n },\n): UseGenerationReturn<InferGenerationOutput<TResult, TOnResult>> {\n type TOutput = InferGenerationOutput<TResult, TOnResult>\n const hookId = useId()\n const clientId = options.id || hookId\n\n const [result, setResult] = useState<TOutput | null>(null)\n const [isLoading, setIsLoading] = useState(false)\n const [error, setError] = useState<Error | undefined>(undefined)\n const [status, setStatus] = useState<GenerationClientState>('idle')\n\n const optionsRef = useRef(options)\n optionsRef.current = options\n\n const client = useMemo(() => {\n const opts = optionsRef.current\n\n // Conditional spread for `body` (strict-optional in target;\n // local source is `Record<string, any> | undefined`). Callbacks\n // wrap optional ones in non-returning bodies so `?.()`'s\n // implicit `undefined` doesn't pollute the function return type.\n const clientOptions: GenerationClientOptions<TInput, TResult, TOutput> = {\n id: clientId,\n body: opts.body,\n onResult: (r: TResult) => optionsRef.current.onResult?.(r),\n onError: (e: Error) => {\n optionsRef.current.onError?.(e)\n },\n onProgress: (p: number, m?: string) => {\n optionsRef.current.onProgress?.(p, m)\n },\n onChunk: (c: StreamChunk) => {\n optionsRef.current.onChunk?.(c)\n },\n onResultChange: setResult,\n onLoadingChange: setIsLoading,\n onErrorChange: setError,\n onStatusChange: setStatus,\n }\n\n if (opts.connection) {\n return new GenerationClient<TInput, TResult, TOutput>({\n ...clientOptions,\n connection: opts.connection,\n })\n }\n\n if (opts.fetcher) {\n return new GenerationClient<TInput, TResult, TOutput>({\n ...clientOptions,\n fetcher: opts.fetcher,\n })\n }\n\n throw new Error(\n 'useGeneration requires either a connection or fetcher option',\n )\n }, [clientId])\n\n // Sync body changes without recreating client\n useEffect(() => {\n // Conditional spread: target uses strict-optional `body?: T`.\n client.updateOptions({\n ...(options.body !== undefined && { body: options.body }),\n })\n }, [client, options.body])\n\n // Cleanup on unmount\n useEffect(() => {\n return () => {\n client.
|
|
1
|
+
{"version":3,"file":"use-generation.js","sources":["../../src/use-generation.ts"],"sourcesContent":["import { GenerationClient } from '@tanstack/ai-client'\nimport { createGenerationDevtoolsBridge } from '@tanstack/ai-client/devtools'\nimport { useCallback, useEffect, useId, useMemo, useRef, useState } from 'react'\nimport type { StreamChunk } from '@tanstack/ai'\nimport type {\n AIDevtoolsDisplayOptions,\n ConnectConnectionAdapter,\n GenerationClientOptions,\n GenerationClientState,\n GenerationFetcher,\n InferGenerationOutput,\n} from '@tanstack/ai-client'\n\n/**\n * Options for the useGeneration hook.\n *\n * Accepts either a `connection` (streaming transport) or a `fetcher` (direct async call).\n *\n * @template TInput - The input type for the generation request\n * @template TResult - The result type returned by the generation\n * @template TOutput - The output type after optional transform (defaults to TResult)\n */\nexport interface UseGenerationOptions<TInput, TResult, TOutput = TResult> {\n /** Connect-based adapter for streaming transport (SSE, HTTP stream, custom) */\n connection?: ConnectConnectionAdapter\n /** Direct async function for one-shot generation (no streaming protocol needed) */\n fetcher?: GenerationFetcher<TInput, TResult>\n /** Unique identifier for this generation instance */\n id?: string\n /** Additional body parameters to send with connect-based adapter requests */\n body?: Record<string, any>\n /** Display options for TanStack AI Devtools. */\n devtools?: AIDevtoolsDisplayOptions\n /**\n * Callback when a result is received. Can optionally return a transformed value.\n *\n * - Return a non-null value to transform and store it as the result\n * - Return `null` to keep the previous result unchanged\n * - Return nothing (`void`) to store the raw result as-is\n */\n onResult?: (result: TResult) => TOutput | null | void\n /** Callback when an error occurs */\n onError?: (error: Error) => void\n /** Callback when progress is reported (0-100) */\n onProgress?: (progress: number, message?: string) => void\n /** Callback for each stream chunk (connect-based adapter mode only) */\n onChunk?: (chunk: StreamChunk) => void\n}\n\n/**\n * Return type for the useGeneration hook.\n *\n * @template TOutput - The output type (after optional transform)\n */\nexport interface UseGenerationReturn<TOutput> {\n /** Trigger a generation request */\n generate: (input: Record<string, any>) => Promise<void>\n /** The generation result, or null if not yet generated */\n result: TOutput | null\n /** Whether a generation is currently in progress */\n isLoading: boolean\n /** Current error, if any */\n error: Error | undefined\n /** Current state of the generation client */\n status: GenerationClientState\n /** Abort the current generation */\n stop: () => void\n /** Clear result, error, and return to idle */\n reset: () => void\n}\n\n/**\n * Generic React hook for one-shot generation tasks.\n *\n * This is the base hook used by `useGenerateImage`, `useGenerateSpeech`,\n * `useTranscription`, and `useSummarize`. You can also use it directly\n * for custom generation types.\n *\n * @template TInput - The input type for the generation request\n * @template TResult - The result type returned by the generation\n *\n * @example\n * ```tsx\n * const { generate, result, isLoading } = useGeneration<MyInput, MyResult>({\n * connection: fetchServerSentEvents('/api/generate/custom'),\n * })\n *\n * await generate({ prompt: 'Hello' })\n * ```\n */\nexport function useGeneration<\n TInput extends Record<string, any>,\n TResult,\n TOnResult extends ((result: TResult) => any) | undefined = undefined,\n>(\n options: Omit<UseGenerationOptions<TInput, TResult>, 'onResult'> & {\n onResult?: TOnResult\n },\n): UseGenerationReturn<InferGenerationOutput<TResult, TOnResult>> {\n type TOutput = InferGenerationOutput<TResult, TOnResult>\n const hookId = useId()\n const clientId = options.id || hookId\n\n const [result, setResult] = useState<TOutput | null>(null)\n const [isLoading, setIsLoading] = useState(false)\n const [error, setError] = useState<Error | undefined>(undefined)\n const [status, setStatus] = useState<GenerationClientState>('idle')\n\n const optionsRef = useRef(options)\n optionsRef.current = options\n\n const client = useMemo(() => {\n const opts = optionsRef.current\n\n // Conditional spread for `body` (strict-optional in target;\n // local source is `Record<string, any> | undefined`). Callbacks\n // wrap optional ones in non-returning bodies so `?.()`'s\n // implicit `undefined` doesn't pollute the function return type.\n const clientOptions: GenerationClientOptions<TInput, TResult, TOutput> = {\n id: clientId,\n body: opts.body,\n devtoolsBridgeFactory: createGenerationDevtoolsBridge,\n devtools: {\n hookName: 'useGeneration',\n framework: 'react',\n ...opts.devtools,\n },\n onResult: (r: TResult) => optionsRef.current.onResult?.(r),\n onError: (e: Error) => {\n optionsRef.current.onError?.(e)\n },\n onProgress: (p: number, m?: string) => {\n optionsRef.current.onProgress?.(p, m)\n },\n onChunk: (c: StreamChunk) => {\n optionsRef.current.onChunk?.(c)\n },\n onResultChange: setResult,\n onLoadingChange: setIsLoading,\n onErrorChange: setError,\n onStatusChange: setStatus,\n }\n\n if (opts.connection) {\n return new GenerationClient<TInput, TResult, TOutput>({\n ...clientOptions,\n connection: opts.connection,\n })\n }\n\n if (opts.fetcher) {\n return new GenerationClient<TInput, TResult, TOutput>({\n ...clientOptions,\n fetcher: opts.fetcher,\n })\n }\n\n throw new Error(\n 'useGeneration requires either a connection or fetcher option',\n )\n }, [clientId])\n\n // Sync body changes without recreating client\n useEffect(() => {\n // Conditional spread: target uses strict-optional `body?: T`.\n client.updateOptions({\n ...(options.body !== undefined && { body: options.body }),\n })\n }, [client, options.body])\n\n // Cleanup on unmount\n useEffect(() => {\n client.mountDevtools()\n\n return () => {\n client.dispose()\n }\n }, [client])\n\n const generate = useCallback(\n async (input: TInput) => {\n await client.generate(input)\n },\n [client],\n )\n\n const stop = useCallback(() => {\n client.stop()\n }, [client])\n\n const reset = useCallback(() => {\n client.reset()\n }, [client])\n\n return {\n generate: generate as (input: Record<string, any>) => Promise<void>,\n result,\n isLoading,\n error,\n status,\n stop,\n reset,\n }\n}\n"],"names":[],"mappings":";;;AA0FO,SAAS,cAKd,SAGgE;AAEhE,QAAM,SAAS,MAAA;AACf,QAAM,WAAW,QAAQ,MAAM;AAE/B,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAyB,IAAI;AACzD,QAAM,CAAC,WAAW,YAAY,IAAI,SAAS,KAAK;AAChD,QAAM,CAAC,OAAO,QAAQ,IAAI,SAA4B,MAAS;AAC/D,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAgC,MAAM;AAElE,QAAM,aAAa,OAAO,OAAO;AACjC,aAAW,UAAU;AAErB,QAAM,SAAS,QAAQ,MAAM;AAC3B,UAAM,OAAO,WAAW;AAMxB,UAAM,gBAAmE;AAAA,MACvE,IAAI;AAAA,MACJ,MAAM,KAAK;AAAA,MACX,uBAAuB;AAAA,MACvB,UAAU;AAAA,QACR,UAAU;AAAA,QACV,WAAW;AAAA,QACX,GAAG,KAAK;AAAA,MAAA;AAAA,MAEV,UAAU,CAAC,MAAe,WAAW,QAAQ,WAAW,CAAC;AAAA,MACzD,SAAS,CAAC,MAAa;AACrB,mBAAW,QAAQ,UAAU,CAAC;AAAA,MAChC;AAAA,MACA,YAAY,CAAC,GAAW,MAAe;AACrC,mBAAW,QAAQ,aAAa,GAAG,CAAC;AAAA,MACtC;AAAA,MACA,SAAS,CAAC,MAAmB;AAC3B,mBAAW,QAAQ,UAAU,CAAC;AAAA,MAChC;AAAA,MACA,gBAAgB;AAAA,MAChB,iBAAiB;AAAA,MACjB,eAAe;AAAA,MACf,gBAAgB;AAAA,IAAA;AAGlB,QAAI,KAAK,YAAY;AACnB,aAAO,IAAI,iBAA2C;AAAA,QACpD,GAAG;AAAA,QACH,YAAY,KAAK;AAAA,MAAA,CAClB;AAAA,IACH;AAEA,QAAI,KAAK,SAAS;AAChB,aAAO,IAAI,iBAA2C;AAAA,QACpD,GAAG;AAAA,QACH,SAAS,KAAK;AAAA,MAAA,CACf;AAAA,IACH;AAEA,UAAM,IAAI;AAAA,MACR;AAAA,IAAA;AAAA,EAEJ,GAAG,CAAC,QAAQ,CAAC;AAGb,YAAU,MAAM;AAEd,WAAO,cAAc;AAAA,MACnB,GAAI,QAAQ,SAAS,UAAa,EAAE,MAAM,QAAQ,KAAA;AAAA,IAAK,CACxD;AAAA,EACH,GAAG,CAAC,QAAQ,QAAQ,IAAI,CAAC;AAGzB,YAAU,MAAM;AACd,WAAO,cAAA;AAEP,WAAO,MAAM;AACX,aAAO,QAAA;AAAA,IACT;AAAA,EACF,GAAG,CAAC,MAAM,CAAC;AAEX,QAAM,WAAW;AAAA,IACf,OAAO,UAAkB;AACvB,YAAM,OAAO,SAAS,KAAK;AAAA,IAC7B;AAAA,IACA,CAAC,MAAM;AAAA,EAAA;AAGT,QAAM,OAAO,YAAY,MAAM;AAC7B,WAAO,KAAA;AAAA,EACT,GAAG,CAAC,MAAM,CAAC;AAEX,QAAM,QAAQ,YAAY,MAAM;AAC9B,WAAO,MAAA;AAAA,EACT,GAAG,CAAC,MAAM,CAAC;AAEX,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EAAA;AAEJ;"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { StreamChunk, SummarizationResult } from '@tanstack/ai';
|
|
2
|
-
import { ConnectConnectionAdapter, GenerationClientState, GenerationFetcher, InferGenerationOutput, SummarizeGenerateInput } from '@tanstack/ai-client';
|
|
2
|
+
import { AIDevtoolsDisplayOptions, ConnectConnectionAdapter, GenerationClientState, GenerationFetcher, InferGenerationOutput, SummarizeGenerateInput } from '@tanstack/ai-client';
|
|
3
3
|
/**
|
|
4
4
|
* Options for the useSummarize hook.
|
|
5
5
|
*
|
|
@@ -14,6 +14,8 @@ export interface UseSummarizeOptions<TOutput = SummarizationResult> {
|
|
|
14
14
|
id?: string;
|
|
15
15
|
/** Additional body parameters to send with connect-based adapter requests */
|
|
16
16
|
body?: Record<string, any>;
|
|
17
|
+
/** Display options for TanStack AI Devtools. */
|
|
18
|
+
devtools?: AIDevtoolsDisplayOptions;
|
|
17
19
|
/**
|
|
18
20
|
* Callback when summarization is complete. Can optionally return a transformed value.
|
|
19
21
|
*
|
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
import { useGeneration } from "./use-generation.js";
|
|
2
2
|
function useSummarize(options) {
|
|
3
|
-
const
|
|
4
|
-
options
|
|
5
|
-
|
|
3
|
+
const devtools = {
|
|
4
|
+
...options.devtools,
|
|
5
|
+
framework: "react",
|
|
6
|
+
hookName: "useSummarize",
|
|
7
|
+
outputKind: "text"
|
|
8
|
+
};
|
|
9
|
+
const { generate, result, isLoading, error, status, stop, reset } = useGeneration({
|
|
10
|
+
...options,
|
|
11
|
+
devtools
|
|
12
|
+
});
|
|
6
13
|
return {
|
|
7
14
|
generate,
|
|
8
15
|
result,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-summarize.js","sources":["../../src/use-summarize.ts"],"sourcesContent":["import { useGeneration } from './use-generation'\nimport type { StreamChunk, SummarizationResult } from '@tanstack/ai'\nimport type {\n ConnectConnectionAdapter,\n GenerationClientState,\n GenerationFetcher,\n InferGenerationOutput,\n SummarizeGenerateInput,\n} from '@tanstack/ai-client'\n\n/**\n * Options for the useSummarize hook.\n *\n * @template TOutput - The output type after optional transform (defaults to SummarizationResult)\n */\nexport interface UseSummarizeOptions<TOutput = SummarizationResult> {\n /** Connect-based adapter for streaming transport (SSE, HTTP stream, custom) */\n connection?: ConnectConnectionAdapter\n /** Direct async function for summarization */\n fetcher?: GenerationFetcher<SummarizeGenerateInput, SummarizationResult>\n /** Unique identifier for this generation instance */\n id?: string\n /** Additional body parameters to send with connect-based adapter requests */\n body?: Record<string, any>\n /**\n * Callback when summarization is complete. Can optionally return a transformed value.\n *\n * - Return a non-null value to transform and store it as the result\n * - Return `null` to keep the previous result unchanged\n * - Return nothing (`void`) to store the raw result as-is\n */\n onResult?: (result: SummarizationResult) => TOutput | null | void\n /** Callback when an error occurs */\n onError?: (error: Error) => void\n /** Callback when progress is reported (0-100) */\n onProgress?: (progress: number, message?: string) => void\n /** Callback for each stream chunk (connect-based adapter mode only) */\n onChunk?: (chunk: StreamChunk) => void\n}\n\n/**\n * Return type for the useSummarize hook.\n *\n * @template TOutput - The output type (after optional transform)\n */\nexport interface UseSummarizeReturn<TOutput = SummarizationResult> {\n /** Trigger summarization */\n generate: (input: SummarizeGenerateInput) => Promise<void>\n /** The summarization result, or null */\n result: TOutput | null\n /** Whether summarization is in progress */\n isLoading: boolean\n /** Current error, if any */\n error: Error | undefined\n /** Current state of the generation */\n status: GenerationClientState\n /** Abort the current summarization */\n stop: () => void\n /** Clear result, error, and return to idle */\n reset: () => void\n}\n\n/**\n * React hook for summarizing text using AI models.\n *\n * @example\n * ```tsx\n * import { useSummarize } from '@tanstack/ai-react'\n * import { fetchServerSentEvents } from '@tanstack/ai-client'\n *\n * function Summarizer() {\n * const { generate, result, isLoading } = useSummarize({\n * connection: fetchServerSentEvents('/api/summarize'),\n * })\n *\n * return (\n * <div>\n * <button onClick={() => generate({\n * text: 'Long article text...',\n * style: 'bullet-points',\n * maxLength: 200,\n * })}>\n * Summarize\n * </button>\n * {isLoading && <p>Summarizing...</p>}\n * {result && <p>{result.summary}</p>}\n * </div>\n * )\n * }\n * ```\n */\nexport function useSummarize<\n TOnResult extends ((result: SummarizationResult) => any) | undefined =\n undefined,\n>(\n options: Omit<UseSummarizeOptions, 'onResult'> & {\n onResult?: TOnResult\n },\n): UseSummarizeReturn<InferGenerationOutput<SummarizationResult, TOnResult>> {\n const { generate, result, isLoading, error, status, stop, reset } =\n useGeneration<SummarizeGenerateInput, SummarizationResult, TOnResult>(\n options,\n )\n\n return {\n generate: generate as (input: SummarizeGenerateInput) => Promise<void>,\n result,\n isLoading,\n error,\n status,\n stop,\n reset,\n }\n}\n"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"use-summarize.js","sources":["../../src/use-summarize.ts"],"sourcesContent":["import { useGeneration } from './use-generation'\nimport type { StreamChunk, SummarizationResult } from '@tanstack/ai'\nimport type {\n AIDevtoolsDisplayOptions,\n ConnectConnectionAdapter,\n GenerationClientState,\n GenerationFetcher,\n InferGenerationOutput,\n SummarizeGenerateInput,\n} from '@tanstack/ai-client'\n\n/**\n * Options for the useSummarize hook.\n *\n * @template TOutput - The output type after optional transform (defaults to SummarizationResult)\n */\nexport interface UseSummarizeOptions<TOutput = SummarizationResult> {\n /** Connect-based adapter for streaming transport (SSE, HTTP stream, custom) */\n connection?: ConnectConnectionAdapter\n /** Direct async function for summarization */\n fetcher?: GenerationFetcher<SummarizeGenerateInput, SummarizationResult>\n /** Unique identifier for this generation instance */\n id?: string\n /** Additional body parameters to send with connect-based adapter requests */\n body?: Record<string, any>\n /** Display options for TanStack AI Devtools. */\n devtools?: AIDevtoolsDisplayOptions\n /**\n * Callback when summarization is complete. Can optionally return a transformed value.\n *\n * - Return a non-null value to transform and store it as the result\n * - Return `null` to keep the previous result unchanged\n * - Return nothing (`void`) to store the raw result as-is\n */\n onResult?: (result: SummarizationResult) => TOutput | null | void\n /** Callback when an error occurs */\n onError?: (error: Error) => void\n /** Callback when progress is reported (0-100) */\n onProgress?: (progress: number, message?: string) => void\n /** Callback for each stream chunk (connect-based adapter mode only) */\n onChunk?: (chunk: StreamChunk) => void\n}\n\n/**\n * Return type for the useSummarize hook.\n *\n * @template TOutput - The output type (after optional transform)\n */\nexport interface UseSummarizeReturn<TOutput = SummarizationResult> {\n /** Trigger summarization */\n generate: (input: SummarizeGenerateInput) => Promise<void>\n /** The summarization result, or null */\n result: TOutput | null\n /** Whether summarization is in progress */\n isLoading: boolean\n /** Current error, if any */\n error: Error | undefined\n /** Current state of the generation */\n status: GenerationClientState\n /** Abort the current summarization */\n stop: () => void\n /** Clear result, error, and return to idle */\n reset: () => void\n}\n\n/**\n * React hook for summarizing text using AI models.\n *\n * @example\n * ```tsx\n * import { useSummarize } from '@tanstack/ai-react'\n * import { fetchServerSentEvents } from '@tanstack/ai-client'\n *\n * function Summarizer() {\n * const { generate, result, isLoading } = useSummarize({\n * connection: fetchServerSentEvents('/api/summarize'),\n * })\n *\n * return (\n * <div>\n * <button onClick={() => generate({\n * text: 'Long article text...',\n * style: 'bullet-points',\n * maxLength: 200,\n * })}>\n * Summarize\n * </button>\n * {isLoading && <p>Summarizing...</p>}\n * {result && <p>{result.summary}</p>}\n * </div>\n * )\n * }\n * ```\n */\nexport function useSummarize<\n TOnResult extends ((result: SummarizationResult) => any) | undefined =\n undefined,\n>(\n options: Omit<UseSummarizeOptions, 'onResult'> & {\n onResult?: TOnResult\n },\n): UseSummarizeReturn<InferGenerationOutput<SummarizationResult, TOnResult>> {\n const devtools = {\n ...options.devtools,\n framework: 'react',\n hookName: 'useSummarize',\n outputKind: 'text' as const,\n }\n const { generate, result, isLoading, error, status, stop, reset } =\n useGeneration<SummarizeGenerateInput, SummarizationResult, TOnResult>({\n ...options,\n devtools,\n })\n\n return {\n generate: generate as (input: SummarizeGenerateInput) => Promise<void>,\n result,\n isLoading,\n error,\n status,\n stop,\n reset,\n }\n}\n"],"names":[],"mappings":";AA8FO,SAAS,aAId,SAG2E;AAC3E,QAAM,WAAW;AAAA,IACf,GAAG,QAAQ;AAAA,IACX,WAAW;AAAA,IACX,UAAU;AAAA,IACV,YAAY;AAAA,EAAA;AAEd,QAAM,EAAE,UAAU,QAAQ,WAAW,OAAO,QAAQ,MAAM,MAAA,IACxD,cAAsE;AAAA,IACpE,GAAG;AAAA,IACH;AAAA,EAAA,CACD;AAEH,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EAAA;AAEJ;"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { StreamChunk, TranscriptionResult } from '@tanstack/ai';
|
|
2
|
-
import { ConnectConnectionAdapter, GenerationClientState, GenerationFetcher, InferGenerationOutput, TranscriptionGenerateInput } from '@tanstack/ai-client';
|
|
2
|
+
import { AIDevtoolsDisplayOptions, ConnectConnectionAdapter, GenerationClientState, GenerationFetcher, InferGenerationOutput, TranscriptionGenerateInput } from '@tanstack/ai-client';
|
|
3
3
|
/**
|
|
4
4
|
* Options for the useTranscription hook.
|
|
5
5
|
*
|
|
@@ -14,6 +14,8 @@ export interface UseTranscriptionOptions<TOutput = TranscriptionResult> {
|
|
|
14
14
|
id?: string;
|
|
15
15
|
/** Additional body parameters to send with connect-based adapter requests */
|
|
16
16
|
body?: Record<string, any>;
|
|
17
|
+
/** Display options for TanStack AI Devtools. */
|
|
18
|
+
devtools?: AIDevtoolsDisplayOptions;
|
|
17
19
|
/**
|
|
18
20
|
* Callback when transcription is complete. Can optionally return a transformed value.
|
|
19
21
|
*
|
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
import { useGeneration } from "./use-generation.js";
|
|
2
2
|
function useTranscription(options) {
|
|
3
|
-
const
|
|
4
|
-
options
|
|
5
|
-
|
|
3
|
+
const devtools = {
|
|
4
|
+
...options.devtools,
|
|
5
|
+
framework: "react",
|
|
6
|
+
hookName: "useTranscription",
|
|
7
|
+
outputKind: "text"
|
|
8
|
+
};
|
|
9
|
+
const { generate, result, isLoading, error, status, stop, reset } = useGeneration({
|
|
10
|
+
...options,
|
|
11
|
+
devtools
|
|
12
|
+
});
|
|
6
13
|
return {
|
|
7
14
|
generate,
|
|
8
15
|
result,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-transcription.js","sources":["../../src/use-transcription.ts"],"sourcesContent":["import { useGeneration } from './use-generation'\nimport type { StreamChunk, TranscriptionResult } from '@tanstack/ai'\nimport type {\n ConnectConnectionAdapter,\n GenerationClientState,\n GenerationFetcher,\n InferGenerationOutput,\n TranscriptionGenerateInput,\n} from '@tanstack/ai-client'\n\n/**\n * Options for the useTranscription hook.\n *\n * @template TOutput - The output type after optional transform (defaults to TranscriptionResult)\n */\nexport interface UseTranscriptionOptions<TOutput = TranscriptionResult> {\n /** Connect-based adapter for streaming transport (SSE, HTTP stream, custom) */\n connection?: ConnectConnectionAdapter\n /** Direct async function for transcription */\n fetcher?: GenerationFetcher<TranscriptionGenerateInput, TranscriptionResult>\n /** Unique identifier for this generation instance */\n id?: string\n /** Additional body parameters to send with connect-based adapter requests */\n body?: Record<string, any>\n /**\n * Callback when transcription is complete. Can optionally return a transformed value.\n *\n * - Return a non-null value to transform and store it as the result\n * - Return `null` to keep the previous result unchanged\n * - Return nothing (`void`) to store the raw result as-is\n */\n onResult?: (result: TranscriptionResult) => TOutput | null | void\n /** Callback when an error occurs */\n onError?: (error: Error) => void\n /** Callback when progress is reported (0-100) */\n onProgress?: (progress: number, message?: string) => void\n /** Callback for each stream chunk (connect-based adapter mode only) */\n onChunk?: (chunk: StreamChunk) => void\n}\n\n/**\n * Return type for the useTranscription hook.\n *\n * @template TOutput - The output type (after optional transform)\n */\nexport interface UseTranscriptionReturn<TOutput = TranscriptionResult> {\n /** Trigger transcription */\n generate: (input: TranscriptionGenerateInput) => Promise<void>\n /** The transcription result, or null */\n result: TOutput | null\n /** Whether transcription is in progress */\n isLoading: boolean\n /** Current error, if any */\n error: Error | undefined\n /** Current state of the generation */\n status: GenerationClientState\n /** Abort the current transcription */\n stop: () => void\n /** Clear result, error, and return to idle */\n reset: () => void\n}\n\n/**\n * React hook for transcribing audio to text using AI models.\n *\n * @example\n * ```tsx\n * import { useTranscription } from '@tanstack/ai-react'\n * import { fetchServerSentEvents } from '@tanstack/ai-client'\n *\n * function Transcriber() {\n * const { generate, result, isLoading } = useTranscription({\n * connection: fetchServerSentEvents('/api/transcribe'),\n * })\n *\n * const handleFile = (e: React.ChangeEvent<HTMLInputElement>) => {\n * const file = e.target.files?.[0]\n * if (file) {\n * const reader = new FileReader()\n * reader.onload = () => {\n * generate({ audio: reader.result as string, language: 'en' })\n * }\n * reader.readAsDataURL(file)\n * }\n * }\n *\n * return (\n * <div>\n * <input type=\"file\" accept=\"audio/*\" onChange={handleFile} />\n * {isLoading && <p>Transcribing...</p>}\n * {result && <p>{result.text}</p>}\n * </div>\n * )\n * }\n * ```\n */\nexport function useTranscription<\n TOnResult extends ((result: TranscriptionResult) => any) | undefined =\n undefined,\n>(\n options: Omit<UseTranscriptionOptions, 'onResult'> & {\n onResult?: TOnResult\n },\n): UseTranscriptionReturn<\n InferGenerationOutput<TranscriptionResult, TOnResult>\n> {\n const { generate, result, isLoading, error, status, stop, reset } =\n useGeneration<TranscriptionGenerateInput, TranscriptionResult, TOnResult>(\n options,\n )\n\n return {\n generate: generate as (input: TranscriptionGenerateInput) => Promise<void>,\n result,\n isLoading,\n error,\n status,\n stop,\n reset,\n }\n}\n"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"use-transcription.js","sources":["../../src/use-transcription.ts"],"sourcesContent":["import { useGeneration } from './use-generation'\nimport type { StreamChunk, TranscriptionResult } from '@tanstack/ai'\nimport type {\n AIDevtoolsDisplayOptions,\n ConnectConnectionAdapter,\n GenerationClientState,\n GenerationFetcher,\n InferGenerationOutput,\n TranscriptionGenerateInput,\n} from '@tanstack/ai-client'\n\n/**\n * Options for the useTranscription hook.\n *\n * @template TOutput - The output type after optional transform (defaults to TranscriptionResult)\n */\nexport interface UseTranscriptionOptions<TOutput = TranscriptionResult> {\n /** Connect-based adapter for streaming transport (SSE, HTTP stream, custom) */\n connection?: ConnectConnectionAdapter\n /** Direct async function for transcription */\n fetcher?: GenerationFetcher<TranscriptionGenerateInput, TranscriptionResult>\n /** Unique identifier for this generation instance */\n id?: string\n /** Additional body parameters to send with connect-based adapter requests */\n body?: Record<string, any>\n /** Display options for TanStack AI Devtools. */\n devtools?: AIDevtoolsDisplayOptions\n /**\n * Callback when transcription is complete. Can optionally return a transformed value.\n *\n * - Return a non-null value to transform and store it as the result\n * - Return `null` to keep the previous result unchanged\n * - Return nothing (`void`) to store the raw result as-is\n */\n onResult?: (result: TranscriptionResult) => TOutput | null | void\n /** Callback when an error occurs */\n onError?: (error: Error) => void\n /** Callback when progress is reported (0-100) */\n onProgress?: (progress: number, message?: string) => void\n /** Callback for each stream chunk (connect-based adapter mode only) */\n onChunk?: (chunk: StreamChunk) => void\n}\n\n/**\n * Return type for the useTranscription hook.\n *\n * @template TOutput - The output type (after optional transform)\n */\nexport interface UseTranscriptionReturn<TOutput = TranscriptionResult> {\n /** Trigger transcription */\n generate: (input: TranscriptionGenerateInput) => Promise<void>\n /** The transcription result, or null */\n result: TOutput | null\n /** Whether transcription is in progress */\n isLoading: boolean\n /** Current error, if any */\n error: Error | undefined\n /** Current state of the generation */\n status: GenerationClientState\n /** Abort the current transcription */\n stop: () => void\n /** Clear result, error, and return to idle */\n reset: () => void\n}\n\n/**\n * React hook for transcribing audio to text using AI models.\n *\n * @example\n * ```tsx\n * import { useTranscription } from '@tanstack/ai-react'\n * import { fetchServerSentEvents } from '@tanstack/ai-client'\n *\n * function Transcriber() {\n * const { generate, result, isLoading } = useTranscription({\n * connection: fetchServerSentEvents('/api/transcribe'),\n * })\n *\n * const handleFile = (e: React.ChangeEvent<HTMLInputElement>) => {\n * const file = e.target.files?.[0]\n * if (file) {\n * const reader = new FileReader()\n * reader.onload = () => {\n * generate({ audio: reader.result as string, language: 'en' })\n * }\n * reader.readAsDataURL(file)\n * }\n * }\n *\n * return (\n * <div>\n * <input type=\"file\" accept=\"audio/*\" onChange={handleFile} />\n * {isLoading && <p>Transcribing...</p>}\n * {result && <p>{result.text}</p>}\n * </div>\n * )\n * }\n * ```\n */\nexport function useTranscription<\n TOnResult extends ((result: TranscriptionResult) => any) | undefined =\n undefined,\n>(\n options: Omit<UseTranscriptionOptions, 'onResult'> & {\n onResult?: TOnResult\n },\n): UseTranscriptionReturn<\n InferGenerationOutput<TranscriptionResult, TOnResult>\n> {\n const devtools = {\n ...options.devtools,\n framework: 'react',\n hookName: 'useTranscription',\n outputKind: 'text' as const,\n }\n const { generate, result, isLoading, error, status, stop, reset } =\n useGeneration<TranscriptionGenerateInput, TranscriptionResult, TOnResult>({\n ...options,\n devtools,\n })\n\n return {\n generate: generate as (input: TranscriptionGenerateInput) => Promise<void>,\n result,\n isLoading,\n error,\n status,\n stop,\n reset,\n }\n}\n"],"names":[],"mappings":";AAmGO,SAAS,iBAId,SAKA;AACA,QAAM,WAAW;AAAA,IACf,GAAG,QAAQ;AAAA,IACX,WAAW;AAAA,IACX,UAAU;AAAA,IACV,YAAY;AAAA,EAAA;AAEd,QAAM,EAAE,UAAU,QAAQ,WAAW,OAAO,QAAQ,MAAM,MAAA,IACxD,cAA0E;AAAA,IACxE,GAAG;AAAA,IACH;AAAA,EAAA,CACD;AAEH,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EAAA;AAEJ;"}
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/ai-react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.1",
|
|
4
4
|
"description": "React hooks for TanStack AI streaming chat, realtime voice, structured outputs, and media generation.",
|
|
5
5
|
"author": "",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
9
9
|
"url": "git+https://github.com/TanStack/ai.git",
|
|
10
|
-
"directory": "packages/
|
|
10
|
+
"directory": "packages/ai-react"
|
|
11
11
|
},
|
|
12
12
|
"type": "module",
|
|
13
13
|
"module": "./dist/esm/index.js",
|
|
@@ -37,12 +37,12 @@
|
|
|
37
37
|
"media-generation"
|
|
38
38
|
],
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@tanstack/ai-client": "0.
|
|
40
|
+
"@tanstack/ai-client": "0.13.0"
|
|
41
41
|
},
|
|
42
42
|
"peerDependencies": {
|
|
43
43
|
"@types/react": ">=18.0.0",
|
|
44
44
|
"react": ">=18.0.0",
|
|
45
|
-
"@tanstack/ai": "^0.22.
|
|
45
|
+
"@tanstack/ai": "^0.22.1"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"@standard-schema/spec": "^1.1.0",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"jsdom": "^27.2.0",
|
|
53
53
|
"react": "^19.2.3",
|
|
54
54
|
"vite": "^7.3.3",
|
|
55
|
-
"@tanstack/ai": "0.22.
|
|
55
|
+
"@tanstack/ai": "0.22.1"
|
|
56
56
|
},
|
|
57
57
|
"scripts": {
|
|
58
58
|
"clean": "premove ./build ./dist",
|
package/src/index.ts
CHANGED
package/src/types.ts
CHANGED
|
@@ -5,10 +5,12 @@ import type {
|
|
|
5
5
|
SchemaInput,
|
|
6
6
|
} from '@tanstack/ai'
|
|
7
7
|
import type {
|
|
8
|
+
AIDevtoolsDisplayOptions,
|
|
8
9
|
ChatClientOptions,
|
|
9
10
|
ChatClientState,
|
|
10
11
|
ChatRequestBody,
|
|
11
12
|
ConnectionStatus,
|
|
13
|
+
DistributedOmit,
|
|
12
14
|
MultimodalContent,
|
|
13
15
|
UIMessage,
|
|
14
16
|
} from '@tanstack/ai-client'
|
|
@@ -31,6 +33,9 @@ export type DeepPartial<T> =
|
|
|
31
33
|
/**
|
|
32
34
|
* Options for the useChat hook.
|
|
33
35
|
*
|
|
36
|
+
* Pass either `connection` or `fetcher` — the XOR is enforced at the type
|
|
37
|
+
* level via `ChatTransport`.
|
|
38
|
+
*
|
|
34
39
|
* This extends ChatClientOptions but omits the state change callbacks that are
|
|
35
40
|
* managed internally by React state:
|
|
36
41
|
* - `onMessagesChange` - Managed by React state (exposed as `messages`)
|
|
@@ -54,7 +59,7 @@ export type DeepPartial<T> =
|
|
|
54
59
|
export type UseChatOptions<
|
|
55
60
|
TTools extends ReadonlyArray<AnyClientTool> = any,
|
|
56
61
|
TSchema extends SchemaInput | undefined = undefined,
|
|
57
|
-
> =
|
|
62
|
+
> = DistributedOmit<
|
|
58
63
|
ChatClientOptions<TTools>,
|
|
59
64
|
| 'onMessagesChange'
|
|
60
65
|
| 'onLoadingChange'
|
|
@@ -63,7 +68,10 @@ export type UseChatOptions<
|
|
|
63
68
|
| 'onSubscriptionChange'
|
|
64
69
|
| 'onConnectionStatusChange'
|
|
65
70
|
| 'onSessionGeneratingChange'
|
|
71
|
+
| 'devtools'
|
|
66
72
|
> & {
|
|
73
|
+
/** Display options for TanStack AI Devtools. */
|
|
74
|
+
devtools?: AIDevtoolsDisplayOptions
|
|
67
75
|
/**
|
|
68
76
|
* Opt into mount-time live subscription behavior.
|
|
69
77
|
* When enabled, the hook subscribes on mount and unsubscribes on unmount.
|
package/src/use-chat.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ChatClient } from '@tanstack/ai-client'
|
|
2
|
+
import { createChatDevtoolsBridge } from '@tanstack/ai-client/devtools'
|
|
2
3
|
import { useCallback, useEffect, useId, useMemo, useRef, useState } from 'react'
|
|
3
4
|
import type {
|
|
4
5
|
AnyClientTool,
|
|
@@ -68,14 +69,25 @@ export function useChat<
|
|
|
68
69
|
// optional (`field?: T`) — `exactOptionalPropertyTypes` rejects
|
|
69
70
|
// assigning `undefined` to those, so we omit the key when absent.
|
|
70
71
|
const initialOptions = optionsRef.current
|
|
72
|
+
const transport = initialOptions.connection
|
|
73
|
+
? { connection: initialOptions.connection }
|
|
74
|
+
: { fetcher: initialOptions.fetcher }
|
|
75
|
+
|
|
71
76
|
return new ChatClient({
|
|
72
|
-
|
|
77
|
+
devtoolsBridgeFactory: createChatDevtoolsBridge,
|
|
78
|
+
...transport,
|
|
73
79
|
id: clientId,
|
|
74
80
|
initialMessages: messagesToUse,
|
|
75
81
|
...(initialOptions.body !== undefined && { body: initialOptions.body }),
|
|
76
82
|
...(initialOptions.forwardedProps !== undefined && {
|
|
77
83
|
forwardedProps: initialOptions.forwardedProps,
|
|
78
84
|
}),
|
|
85
|
+
devtools: {
|
|
86
|
+
...initialOptions.devtools,
|
|
87
|
+
framework: 'react',
|
|
88
|
+
hookName: 'useChat',
|
|
89
|
+
outputKind: initialOptions.outputSchema ? 'structured' : 'chat',
|
|
90
|
+
},
|
|
79
91
|
onResponse: (response) => {
|
|
80
92
|
void optionsRef.current.onResponse?.(response)
|
|
81
93
|
},
|
|
@@ -149,14 +161,21 @@ export function useChat<
|
|
|
149
161
|
}, [client, options.live])
|
|
150
162
|
|
|
151
163
|
useEffect(() => {
|
|
164
|
+
client.mountDevtools()
|
|
165
|
+
|
|
152
166
|
return () => {
|
|
153
|
-
|
|
167
|
+
// Subscribe/unsubscribe on `options.live` is owned by the dedicated
|
|
168
|
+
// effect above. This cleanup only fires on unmount or client swap,
|
|
169
|
+
// so read `live` through the ref to avoid disposing the client every
|
|
170
|
+
// time `live` toggles.
|
|
171
|
+
if (optionsRef.current.live) {
|
|
154
172
|
client.unsubscribe()
|
|
155
173
|
} else {
|
|
156
174
|
client.stop()
|
|
157
175
|
}
|
|
176
|
+
client.dispose()
|
|
158
177
|
}
|
|
159
|
-
}, [client
|
|
178
|
+
}, [client])
|
|
160
179
|
|
|
161
180
|
const sendMessage = useCallback(
|
|
162
181
|
async (content: string | MultimodalContent) => {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useGeneration } from './use-generation'
|
|
2
2
|
import type { AudioGenerationResult, StreamChunk } from '@tanstack/ai'
|
|
3
3
|
import type {
|
|
4
|
+
AIDevtoolsDisplayOptions,
|
|
4
5
|
AudioGenerateInput,
|
|
5
6
|
ConnectConnectionAdapter,
|
|
6
7
|
GenerationClientState,
|
|
@@ -22,6 +23,8 @@ export interface UseGenerateAudioOptions<TOutput = AudioGenerationResult> {
|
|
|
22
23
|
id?: string
|
|
23
24
|
/** Additional body parameters to send with connect-based adapter requests */
|
|
24
25
|
body?: Record<string, any>
|
|
26
|
+
/** Display options for TanStack AI Devtools. */
|
|
27
|
+
devtools?: AIDevtoolsDisplayOptions
|
|
25
28
|
/**
|
|
26
29
|
* Callback when audio is generated. Can optionally return a transformed value.
|
|
27
30
|
*
|
|
@@ -100,8 +103,17 @@ export function useGenerateAudio<
|
|
|
100
103
|
): UseGenerateAudioReturn<
|
|
101
104
|
InferGenerationOutput<AudioGenerationResult, TOnResult>
|
|
102
105
|
> {
|
|
106
|
+
const devtools = {
|
|
107
|
+
...options.devtools,
|
|
108
|
+
framework: 'react',
|
|
109
|
+
hookName: 'useGenerateAudio',
|
|
110
|
+
outputKind: 'audio' as const,
|
|
111
|
+
}
|
|
103
112
|
const { generate, result, isLoading, error, status, stop, reset } =
|
|
104
|
-
useGeneration<AudioGenerateInput, AudioGenerationResult, TOnResult>(
|
|
113
|
+
useGeneration<AudioGenerateInput, AudioGenerationResult, TOnResult>({
|
|
114
|
+
...options,
|
|
115
|
+
devtools,
|
|
116
|
+
})
|
|
105
117
|
|
|
106
118
|
return {
|
|
107
119
|
generate: generate as (input: AudioGenerateInput) => Promise<void>,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useGeneration } from './use-generation'
|
|
2
2
|
import type { ImageGenerationResult, StreamChunk } from '@tanstack/ai'
|
|
3
3
|
import type {
|
|
4
|
+
AIDevtoolsDisplayOptions,
|
|
4
5
|
ConnectConnectionAdapter,
|
|
5
6
|
GenerationClientState,
|
|
6
7
|
GenerationFetcher,
|
|
@@ -22,6 +23,8 @@ export interface UseGenerateImageOptions<TOutput = ImageGenerationResult> {
|
|
|
22
23
|
id?: string
|
|
23
24
|
/** Additional body parameters to send with connect-based adapter requests */
|
|
24
25
|
body?: Record<string, any>
|
|
26
|
+
/** Display options for TanStack AI Devtools. */
|
|
27
|
+
devtools?: AIDevtoolsDisplayOptions
|
|
25
28
|
/**
|
|
26
29
|
* Callback when images are generated. Can optionally return a transformed value.
|
|
27
30
|
*
|
|
@@ -102,8 +105,17 @@ export function useGenerateImage<
|
|
|
102
105
|
): UseGenerateImageReturn<
|
|
103
106
|
InferGenerationOutput<ImageGenerationResult, TOnResult>
|
|
104
107
|
> {
|
|
108
|
+
const devtools = {
|
|
109
|
+
...options.devtools,
|
|
110
|
+
framework: 'react',
|
|
111
|
+
hookName: 'useGenerateImage',
|
|
112
|
+
outputKind: 'image' as const,
|
|
113
|
+
}
|
|
105
114
|
const { generate, result, isLoading, error, status, stop, reset } =
|
|
106
|
-
useGeneration<ImageGenerateInput, ImageGenerationResult, TOnResult>(
|
|
115
|
+
useGeneration<ImageGenerateInput, ImageGenerationResult, TOnResult>({
|
|
116
|
+
...options,
|
|
117
|
+
devtools,
|
|
118
|
+
})
|
|
107
119
|
|
|
108
120
|
return {
|
|
109
121
|
generate: generate as (input: ImageGenerateInput) => Promise<void>,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useGeneration } from './use-generation'
|
|
2
2
|
import type { StreamChunk, TTSResult } from '@tanstack/ai'
|
|
3
3
|
import type {
|
|
4
|
+
AIDevtoolsDisplayOptions,
|
|
4
5
|
ConnectConnectionAdapter,
|
|
5
6
|
GenerationClientState,
|
|
6
7
|
GenerationFetcher,
|
|
@@ -22,6 +23,8 @@ export interface UseGenerateSpeechOptions<TOutput = TTSResult> {
|
|
|
22
23
|
id?: string
|
|
23
24
|
/** Additional body parameters to send with connect-based adapter requests */
|
|
24
25
|
body?: Record<string, any>
|
|
26
|
+
/** Display options for TanStack AI Devtools. */
|
|
27
|
+
devtools?: AIDevtoolsDisplayOptions
|
|
25
28
|
/**
|
|
26
29
|
* Callback when speech is generated. Can optionally return a transformed value.
|
|
27
30
|
*
|
|
@@ -93,8 +96,17 @@ export function useGenerateSpeech<
|
|
|
93
96
|
onResult?: TOnResult
|
|
94
97
|
},
|
|
95
98
|
): UseGenerateSpeechReturn<InferGenerationOutput<TTSResult, TOnResult>> {
|
|
99
|
+
const devtools = {
|
|
100
|
+
...options.devtools,
|
|
101
|
+
framework: 'react',
|
|
102
|
+
hookName: 'useGenerateSpeech',
|
|
103
|
+
outputKind: 'audio' as const,
|
|
104
|
+
}
|
|
96
105
|
const { generate, result, isLoading, error, status, stop, reset } =
|
|
97
|
-
useGeneration<SpeechGenerateInput, TTSResult, TOnResult>(
|
|
106
|
+
useGeneration<SpeechGenerateInput, TTSResult, TOnResult>({
|
|
107
|
+
...options,
|
|
108
|
+
devtools,
|
|
109
|
+
})
|
|
98
110
|
|
|
99
111
|
return {
|
|
100
112
|
generate: generate as (input: SpeechGenerateInput) => Promise<void>,
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { VideoGenerationClient } from '@tanstack/ai-client'
|
|
2
|
+
import { createVideoDevtoolsBridge } from '@tanstack/ai-client/devtools'
|
|
2
3
|
import { useCallback, useEffect, useId, useMemo, useRef, useState } from 'react'
|
|
3
4
|
import type { StreamChunk } from '@tanstack/ai'
|
|
4
5
|
import type {
|
|
6
|
+
AIDevtoolsDisplayOptions,
|
|
5
7
|
ConnectConnectionAdapter,
|
|
6
8
|
GenerationClientState,
|
|
7
9
|
GenerationFetcher,
|
|
@@ -23,6 +25,8 @@ export interface UseGenerateVideoOptions<TOutput = VideoGenerateResult> {
|
|
|
23
25
|
id?: string
|
|
24
26
|
/** Additional body parameters to send with connect-based adapter requests */
|
|
25
27
|
body?: Record<string, any>
|
|
28
|
+
/** Display options for TanStack AI Devtools. */
|
|
29
|
+
devtools?: AIDevtoolsDisplayOptions
|
|
26
30
|
/**
|
|
27
31
|
* Callback when video generation completes. Can optionally return a transformed value.
|
|
28
32
|
*
|
|
@@ -135,6 +139,13 @@ export function useGenerateVideo<
|
|
|
135
139
|
const baseOptions = {
|
|
136
140
|
id: clientId,
|
|
137
141
|
body: opts.body,
|
|
142
|
+
devtoolsBridgeFactory: createVideoDevtoolsBridge,
|
|
143
|
+
devtools: {
|
|
144
|
+
...opts.devtools,
|
|
145
|
+
framework: 'react',
|
|
146
|
+
hookName: 'useGenerateVideo',
|
|
147
|
+
outputKind: 'video' as const,
|
|
148
|
+
},
|
|
138
149
|
onResult: (r: VideoGenerateResult) => optionsRef.current.onResult?.(r),
|
|
139
150
|
onError: (e: Error) => {
|
|
140
151
|
optionsRef.current.onError?.(e)
|
|
@@ -188,8 +199,10 @@ export function useGenerateVideo<
|
|
|
188
199
|
|
|
189
200
|
// Cleanup on unmount
|
|
190
201
|
useEffect(() => {
|
|
202
|
+
client.mountDevtools()
|
|
203
|
+
|
|
191
204
|
return () => {
|
|
192
|
-
client.
|
|
205
|
+
client.dispose()
|
|
193
206
|
}
|
|
194
207
|
}, [client])
|
|
195
208
|
|
package/src/use-generation.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { GenerationClient } from '@tanstack/ai-client'
|
|
2
|
+
import { createGenerationDevtoolsBridge } from '@tanstack/ai-client/devtools'
|
|
2
3
|
import { useCallback, useEffect, useId, useMemo, useRef, useState } from 'react'
|
|
3
4
|
import type { StreamChunk } from '@tanstack/ai'
|
|
4
5
|
import type {
|
|
6
|
+
AIDevtoolsDisplayOptions,
|
|
5
7
|
ConnectConnectionAdapter,
|
|
6
8
|
GenerationClientOptions,
|
|
7
9
|
GenerationClientState,
|
|
@@ -27,6 +29,8 @@ export interface UseGenerationOptions<TInput, TResult, TOutput = TResult> {
|
|
|
27
29
|
id?: string
|
|
28
30
|
/** Additional body parameters to send with connect-based adapter requests */
|
|
29
31
|
body?: Record<string, any>
|
|
32
|
+
/** Display options for TanStack AI Devtools. */
|
|
33
|
+
devtools?: AIDevtoolsDisplayOptions
|
|
30
34
|
/**
|
|
31
35
|
* Callback when a result is received. Can optionally return a transformed value.
|
|
32
36
|
*
|
|
@@ -115,6 +119,12 @@ export function useGeneration<
|
|
|
115
119
|
const clientOptions: GenerationClientOptions<TInput, TResult, TOutput> = {
|
|
116
120
|
id: clientId,
|
|
117
121
|
body: opts.body,
|
|
122
|
+
devtoolsBridgeFactory: createGenerationDevtoolsBridge,
|
|
123
|
+
devtools: {
|
|
124
|
+
hookName: 'useGeneration',
|
|
125
|
+
framework: 'react',
|
|
126
|
+
...opts.devtools,
|
|
127
|
+
},
|
|
118
128
|
onResult: (r: TResult) => optionsRef.current.onResult?.(r),
|
|
119
129
|
onError: (e: Error) => {
|
|
120
130
|
optionsRef.current.onError?.(e)
|
|
@@ -160,8 +170,10 @@ export function useGeneration<
|
|
|
160
170
|
|
|
161
171
|
// Cleanup on unmount
|
|
162
172
|
useEffect(() => {
|
|
173
|
+
client.mountDevtools()
|
|
174
|
+
|
|
163
175
|
return () => {
|
|
164
|
-
client.
|
|
176
|
+
client.dispose()
|
|
165
177
|
}
|
|
166
178
|
}, [client])
|
|
167
179
|
|
package/src/use-summarize.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useGeneration } from './use-generation'
|
|
2
2
|
import type { StreamChunk, SummarizationResult } from '@tanstack/ai'
|
|
3
3
|
import type {
|
|
4
|
+
AIDevtoolsDisplayOptions,
|
|
4
5
|
ConnectConnectionAdapter,
|
|
5
6
|
GenerationClientState,
|
|
6
7
|
GenerationFetcher,
|
|
@@ -22,6 +23,8 @@ export interface UseSummarizeOptions<TOutput = SummarizationResult> {
|
|
|
22
23
|
id?: string
|
|
23
24
|
/** Additional body parameters to send with connect-based adapter requests */
|
|
24
25
|
body?: Record<string, any>
|
|
26
|
+
/** Display options for TanStack AI Devtools. */
|
|
27
|
+
devtools?: AIDevtoolsDisplayOptions
|
|
25
28
|
/**
|
|
26
29
|
* Callback when summarization is complete. Can optionally return a transformed value.
|
|
27
30
|
*
|
|
@@ -97,10 +100,17 @@ export function useSummarize<
|
|
|
97
100
|
onResult?: TOnResult
|
|
98
101
|
},
|
|
99
102
|
): UseSummarizeReturn<InferGenerationOutput<SummarizationResult, TOnResult>> {
|
|
103
|
+
const devtools = {
|
|
104
|
+
...options.devtools,
|
|
105
|
+
framework: 'react',
|
|
106
|
+
hookName: 'useSummarize',
|
|
107
|
+
outputKind: 'text' as const,
|
|
108
|
+
}
|
|
100
109
|
const { generate, result, isLoading, error, status, stop, reset } =
|
|
101
|
-
useGeneration<SummarizeGenerateInput, SummarizationResult, TOnResult>(
|
|
102
|
-
options,
|
|
103
|
-
|
|
110
|
+
useGeneration<SummarizeGenerateInput, SummarizationResult, TOnResult>({
|
|
111
|
+
...options,
|
|
112
|
+
devtools,
|
|
113
|
+
})
|
|
104
114
|
|
|
105
115
|
return {
|
|
106
116
|
generate: generate as (input: SummarizeGenerateInput) => Promise<void>,
|
package/src/use-transcription.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useGeneration } from './use-generation'
|
|
2
2
|
import type { StreamChunk, TranscriptionResult } from '@tanstack/ai'
|
|
3
3
|
import type {
|
|
4
|
+
AIDevtoolsDisplayOptions,
|
|
4
5
|
ConnectConnectionAdapter,
|
|
5
6
|
GenerationClientState,
|
|
6
7
|
GenerationFetcher,
|
|
@@ -22,6 +23,8 @@ export interface UseTranscriptionOptions<TOutput = TranscriptionResult> {
|
|
|
22
23
|
id?: string
|
|
23
24
|
/** Additional body parameters to send with connect-based adapter requests */
|
|
24
25
|
body?: Record<string, any>
|
|
26
|
+
/** Display options for TanStack AI Devtools. */
|
|
27
|
+
devtools?: AIDevtoolsDisplayOptions
|
|
25
28
|
/**
|
|
26
29
|
* Callback when transcription is complete. Can optionally return a transformed value.
|
|
27
30
|
*
|
|
@@ -104,10 +107,17 @@ export function useTranscription<
|
|
|
104
107
|
): UseTranscriptionReturn<
|
|
105
108
|
InferGenerationOutput<TranscriptionResult, TOnResult>
|
|
106
109
|
> {
|
|
110
|
+
const devtools = {
|
|
111
|
+
...options.devtools,
|
|
112
|
+
framework: 'react',
|
|
113
|
+
hookName: 'useTranscription',
|
|
114
|
+
outputKind: 'text' as const,
|
|
115
|
+
}
|
|
107
116
|
const { generate, result, isLoading, error, status, stop, reset } =
|
|
108
|
-
useGeneration<TranscriptionGenerateInput, TranscriptionResult, TOnResult>(
|
|
109
|
-
options,
|
|
110
|
-
|
|
117
|
+
useGeneration<TranscriptionGenerateInput, TranscriptionResult, TOnResult>({
|
|
118
|
+
...options,
|
|
119
|
+
devtools,
|
|
120
|
+
})
|
|
111
121
|
|
|
112
122
|
return {
|
|
113
123
|
generate: generate as (input: TranscriptionGenerateInput) => Promise<void>,
|