@tanstack/ai-solid 0.6.19 → 0.7.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/index.d.ts CHANGED
@@ -2,9 +2,10 @@ import { ChatRequestBody, UIMessage, UseChatOptions, UseChatReturn } from "./typ
2
2
  import { useChat } from "./use-chat.js";
3
3
  import { UseGenerationOptions, UseGenerationReturn, useGeneration } from "./use-generation.js";
4
4
  import { UseGenerateImageOptions, UseGenerateImageReturn, useGenerateImage } from "./use-generate-image.js";
5
+ import { UseGenerateAudioOptions, UseGenerateAudioReturn, useGenerateAudio } from "./use-generate-audio.js";
5
6
  import { UseGenerateSpeechOptions, UseGenerateSpeechReturn, useGenerateSpeech } from "./use-generate-speech.js";
6
7
  import { UseTranscriptionOptions, UseTranscriptionReturn, useTranscription } from "./use-transcription.js";
7
8
  import { UseSummarizeOptions, UseSummarizeReturn, useSummarize } from "./use-summarize.js";
8
9
  import { UseGenerateVideoOptions, UseGenerateVideoReturn, useGenerateVideo } from "./use-generate-video.js";
9
- import { ConnectionAdapter, FetchConnectionOptions, GenerationClientState, ImageGenerateInput, InferChatMessages, SpeechGenerateInput, SummarizeGenerateInput, TranscriptionGenerateInput, VideoGenerateInput, VideoGenerateResult, VideoStatusInfo, createChatClientOptions, fetchHttpStream, fetchServerSentEvents, stream } from "@tanstack/ai-client";
10
- export { type ChatRequestBody, type ConnectionAdapter, type FetchConnectionOptions, type GenerationClientState, type ImageGenerateInput, type InferChatMessages, type SpeechGenerateInput, type SummarizeGenerateInput, type TranscriptionGenerateInput, type UIMessage, type UseChatOptions, type UseChatReturn, type UseGenerateImageOptions, type UseGenerateImageReturn, type UseGenerateSpeechOptions, type UseGenerateSpeechReturn, type UseGenerateVideoOptions, type UseGenerateVideoReturn, type UseGenerationOptions, type UseGenerationReturn, type UseSummarizeOptions, type UseSummarizeReturn, type UseTranscriptionOptions, type UseTranscriptionReturn, type VideoGenerateInput, type VideoGenerateResult, type VideoStatusInfo, createChatClientOptions, fetchHttpStream, fetchServerSentEvents, stream, useChat, useGenerateImage, useGenerateSpeech, useGenerateVideo, useGeneration, useSummarize, useTranscription };
10
+ import { AudioGenerateInput, ConnectionAdapter, FetchConnectionOptions, GenerationClientState, ImageGenerateInput, InferChatMessages, SpeechGenerateInput, SummarizeGenerateInput, TranscriptionGenerateInput, VideoGenerateInput, VideoGenerateResult, VideoStatusInfo, createChatClientOptions, fetchHttpStream, fetchServerSentEvents, stream } from "@tanstack/ai-client";
11
+ export { type AudioGenerateInput, type ChatRequestBody, type ConnectionAdapter, type FetchConnectionOptions, type GenerationClientState, type ImageGenerateInput, type InferChatMessages, type SpeechGenerateInput, type SummarizeGenerateInput, type TranscriptionGenerateInput, type UIMessage, type UseChatOptions, type UseChatReturn, type UseGenerateAudioOptions, type UseGenerateAudioReturn, type UseGenerateImageOptions, type UseGenerateImageReturn, type UseGenerateSpeechOptions, type UseGenerateSpeechReturn, type UseGenerateVideoOptions, type UseGenerateVideoReturn, type UseGenerationOptions, type UseGenerationReturn, type UseSummarizeOptions, type UseSummarizeReturn, type UseTranscriptionOptions, type UseTranscriptionReturn, type VideoGenerateInput, type VideoGenerateResult, type VideoStatusInfo, createChatClientOptions, fetchHttpStream, fetchServerSentEvents, stream, useChat, useGenerateAudio, useGenerateImage, useGenerateSpeech, useGenerateVideo, useGeneration, useSummarize, useTranscription };
package/dist/index.js CHANGED
@@ -1,10 +1,11 @@
1
1
  import { useChat } from "./use-chat.js";
2
2
  import { useGeneration } from "./use-generation.js";
3
3
  import { useGenerateImage } from "./use-generate-image.js";
4
+ import { useGenerateAudio } from "./use-generate-audio.js";
4
5
  import { useGenerateSpeech } from "./use-generate-speech.js";
5
6
  import { useTranscription } from "./use-transcription.js";
6
7
  import { useSummarize } from "./use-summarize.js";
7
8
  import { useGenerateVideo } from "./use-generate-video.js";
8
9
  import { createChatClientOptions, fetchHttpStream, fetchServerSentEvents, stream } from "@tanstack/ai-client";
9
10
 
10
- export { createChatClientOptions, fetchHttpStream, fetchServerSentEvents, stream, useChat, useGenerateImage, useGenerateSpeech, useGenerateVideo, useGeneration, useSummarize, useTranscription };
11
+ export { createChatClientOptions, fetchHttpStream, fetchServerSentEvents, stream, useChat, useGenerateAudio, useGenerateImage, useGenerateSpeech, useGenerateVideo, useGeneration, useSummarize, useTranscription };
package/dist/use-chat.js CHANGED
@@ -18,8 +18,8 @@ function useChat(options = {}) {
18
18
  id: clientId,
19
19
  initialMessages: options.initialMessages,
20
20
  body: options.body,
21
- onResponse: options.onResponse,
22
- onChunk: options.onChunk,
21
+ onResponse: (response) => options.onResponse?.(response),
22
+ onChunk: (chunk) => options.onChunk?.(chunk),
23
23
  onFinish: (message) => {
24
24
  options.onFinish?.(message);
25
25
  },
@@ -27,7 +27,7 @@ function useChat(options = {}) {
27
27
  options.onError?.(err);
28
28
  },
29
29
  tools: options.tools,
30
- onCustomEvent: options.onCustomEvent,
30
+ onCustomEvent: (eventType, data, context) => options.onCustomEvent?.(eventType, data, context),
31
31
  streamProcessor: options.streamProcessor,
32
32
  onMessagesChange: (newMessages) => {
33
33
  setMessages(newMessages);
@@ -1 +1 @@
1
- {"version":3,"file":"use-chat.js","names":[],"sources":["../src/use-chat.ts"],"sourcesContent":["import {\n createEffect,\n createMemo,\n createSignal,\n createUniqueId,\n onCleanup,\n} from 'solid-js'\n\nimport { ChatClient } from '@tanstack/ai-client'\nimport type { ChatClientState, ConnectionStatus } from '@tanstack/ai-client'\nimport type { AnyClientTool, ModelMessage } from '@tanstack/ai'\nimport type {\n MultimodalContent,\n UIMessage,\n UseChatOptions,\n UseChatReturn,\n} from './types'\n\nexport function useChat<TTools extends ReadonlyArray<AnyClientTool> = any>(\n options: UseChatOptions<TTools> = {} as UseChatOptions<TTools>,\n): UseChatReturn<TTools> {\n const hookId = createUniqueId()\n const clientId = options.id || hookId\n\n const [messages, setMessages] = createSignal<Array<UIMessage<TTools>>>(\n options.initialMessages || [],\n )\n const [isLoading, setIsLoading] = createSignal(false)\n const [error, setError] = createSignal<Error | undefined>(undefined)\n const [status, setStatus] = createSignal<ChatClientState>('ready')\n const [isSubscribed, setIsSubscribed] = createSignal(false)\n const [connectionStatus, setConnectionStatus] =\n createSignal<ConnectionStatus>('disconnected')\n const [sessionGenerating, setSessionGenerating] = createSignal(false)\n\n // Create ChatClient instance with callbacks to sync state\n // Note: Options are captured at client creation time.\n // The connection adapter can use functions for dynamic values (url, headers, etc.)\n // which are evaluated lazily on each request.\n const client = createMemo(() => {\n return new ChatClient({\n connection: options.connection,\n id: clientId,\n initialMessages: options.initialMessages,\n body: options.body,\n onResponse: options.onResponse,\n onChunk: options.onChunk,\n onFinish: (message) => {\n options.onFinish?.(message)\n },\n onError: (err) => {\n options.onError?.(err)\n },\n tools: options.tools,\n onCustomEvent: options.onCustomEvent,\n streamProcessor: options.streamProcessor,\n onMessagesChange: (newMessages: Array<UIMessage<TTools>>) => {\n setMessages(newMessages)\n },\n onLoadingChange: (newIsLoading: boolean) => {\n setIsLoading(newIsLoading)\n },\n onStatusChange: (newStatus: ChatClientState) => {\n setStatus(newStatus)\n },\n onErrorChange: (newError: Error | undefined) => {\n setError(newError)\n },\n onSubscriptionChange: (nextIsSubscribed: boolean) => {\n setIsSubscribed(nextIsSubscribed)\n },\n onConnectionStatusChange: (nextStatus: ConnectionStatus) => {\n setConnectionStatus(nextStatus)\n },\n onSessionGeneratingChange: (isGenerating: boolean) => {\n setSessionGenerating(isGenerating)\n },\n })\n // Only recreate when clientId changes\n // Connection and other options are captured at creation time\n }, [clientId])\n\n // Sync body changes to the client\n // This allows dynamic body values (like model selection) to be updated without recreating the client\n createEffect(() => {\n const currentBody = options.body\n client().updateOptions({ body: currentBody })\n })\n\n // Sync initial messages on mount only\n // Note: initialMessages are passed to ChatClient constructor, but we also\n // set them here to ensure React state is in sync\n createEffect(() => {\n if (options.initialMessages && options.initialMessages.length > 0) {\n // Only set if current messages are empty (initial state)\n if (messages().length === 0) {\n client().setMessagesManually(options.initialMessages)\n }\n }\n }) // Only run on mount - initialMessages are handled by ChatClient constructor\n\n // Apply initial live mode immediately on hook creation.\n if (options.live) {\n client().subscribe()\n } else {\n client().unsubscribe()\n }\n\n createEffect(() => {\n if (options.live) {\n client().subscribe()\n } else {\n client().unsubscribe()\n }\n })\n\n // Cleanup on unmount: stop any in-flight requests.\n onCleanup(() => {\n if (options.live) {\n client().unsubscribe()\n } else {\n client().stop()\n }\n })\n\n // Note: Callback options (onResponse, onChunk, onFinish, onError, onToolCall)\n // are captured at client creation time. Changes to these callbacks require\n // remounting the component or changing the connection to recreate the client.\n\n const sendMessage = async (content: string | MultimodalContent) => {\n await client().sendMessage(content)\n }\n\n const append = async (message: ModelMessage | UIMessage<TTools>) => {\n await client().append(message)\n }\n\n const reload = async () => {\n await client().reload()\n }\n\n const stop = () => {\n client().stop()\n }\n\n const clear = () => {\n client().clear()\n }\n\n const setMessagesManually = (newMessages: Array<UIMessage<TTools>>) => {\n client().setMessagesManually(newMessages)\n }\n\n const addToolResult = 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\n const addToolApprovalResponse = async (response: {\n id: string\n approved: boolean\n }) => {\n await client().addToolApprovalResponse(response)\n }\n\n return {\n messages,\n sendMessage,\n append,\n reload,\n stop,\n isLoading,\n error,\n status,\n isSubscribed,\n connectionStatus,\n sessionGenerating,\n setMessages: setMessagesManually,\n clear,\n addToolResult,\n addToolApprovalResponse,\n }\n}\n"],"mappings":";;;;AAkBA,SAAgB,QACd,UAAkC,EAAE,EACb;CACvB,MAAM,SAAS,gBAAgB;CAC/B,MAAM,WAAW,QAAQ,MAAM;CAE/B,MAAM,CAAC,UAAU,eAAe,aAC9B,QAAQ,mBAAmB,EAAE,CAC9B;CACD,MAAM,CAAC,WAAW,gBAAgB,aAAa,MAAM;CACrD,MAAM,CAAC,OAAO,YAAY,aAAgC,OAAU;CACpE,MAAM,CAAC,QAAQ,aAAa,aAA8B,QAAQ;CAClE,MAAM,CAAC,cAAc,mBAAmB,aAAa,MAAM;CAC3D,MAAM,CAAC,kBAAkB,uBACvB,aAA+B,eAAe;CAChD,MAAM,CAAC,mBAAmB,wBAAwB,aAAa,MAAM;CAMrE,MAAM,SAAS,iBAAiB;AAC9B,SAAO,IAAI,WAAW;GACpB,YAAY,QAAQ;GACpB,IAAI;GACJ,iBAAiB,QAAQ;GACzB,MAAM,QAAQ;GACd,YAAY,QAAQ;GACpB,SAAS,QAAQ;GACjB,WAAW,YAAY;AACrB,YAAQ,WAAW,QAAQ;;GAE7B,UAAU,QAAQ;AAChB,YAAQ,UAAU,IAAI;;GAExB,OAAO,QAAQ;GACf,eAAe,QAAQ;GACvB,iBAAiB,QAAQ;GACzB,mBAAmB,gBAA0C;AAC3D,gBAAY,YAAY;;GAE1B,kBAAkB,iBAA0B;AAC1C,iBAAa,aAAa;;GAE5B,iBAAiB,cAA+B;AAC9C,cAAU,UAAU;;GAEtB,gBAAgB,aAAgC;AAC9C,aAAS,SAAS;;GAEpB,uBAAuB,qBAA8B;AACnD,oBAAgB,iBAAiB;;GAEnC,2BAA2B,eAAiC;AAC1D,wBAAoB,WAAW;;GAEjC,4BAA4B,iBAA0B;AACpD,yBAAqB,aAAa;;GAErC,CAAC;IAGD,CAAC,SAAS,CAAC;AAId,oBAAmB;EACjB,MAAM,cAAc,QAAQ;AAC5B,UAAQ,CAAC,cAAc,EAAE,MAAM,aAAa,CAAC;GAC7C;AAKF,oBAAmB;AACjB,MAAI,QAAQ,mBAAmB,QAAQ,gBAAgB,SAAS,GAE9D;OAAI,UAAU,CAAC,WAAW,EACxB,SAAQ,CAAC,oBAAoB,QAAQ,gBAAgB;;GAGzD;AAGF,KAAI,QAAQ,KACV,SAAQ,CAAC,WAAW;KAEpB,SAAQ,CAAC,aAAa;AAGxB,oBAAmB;AACjB,MAAI,QAAQ,KACV,SAAQ,CAAC,WAAW;MAEpB,SAAQ,CAAC,aAAa;GAExB;AAGF,iBAAgB;AACd,MAAI,QAAQ,KACV,SAAQ,CAAC,aAAa;MAEtB,SAAQ,CAAC,MAAM;GAEjB;CAMF,MAAM,cAAc,OAAO,YAAwC;AACjE,QAAM,QAAQ,CAAC,YAAY,QAAQ;;CAGrC,MAAM,SAAS,OAAO,YAA8C;AAClE,QAAM,QAAQ,CAAC,OAAO,QAAQ;;CAGhC,MAAM,SAAS,YAAY;AACzB,QAAM,QAAQ,CAAC,QAAQ;;CAGzB,MAAM,aAAa;AACjB,UAAQ,CAAC,MAAM;;CAGjB,MAAM,cAAc;AAClB,UAAQ,CAAC,OAAO;;CAGlB,MAAM,uBAAuB,gBAA0C;AACrE,UAAQ,CAAC,oBAAoB,YAAY;;CAG3C,MAAM,gBAAgB,OAAO,WAMvB;AACJ,QAAM,QAAQ,CAAC,cAAc,OAAO;;CAGtC,MAAM,0BAA0B,OAAO,aAGjC;AACJ,QAAM,QAAQ,CAAC,wBAAwB,SAAS;;AAGlD,QAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,aAAa;EACb;EACA;EACA;EACD"}
1
+ {"version":3,"file":"use-chat.js","names":[],"sources":["../src/use-chat.ts"],"sourcesContent":["import {\n createEffect,\n createMemo,\n createSignal,\n createUniqueId,\n onCleanup,\n} from 'solid-js'\n\nimport { ChatClient } from '@tanstack/ai-client'\nimport type { ChatClientState, ConnectionStatus } from '@tanstack/ai-client'\nimport type { AnyClientTool, ModelMessage } from '@tanstack/ai'\nimport type {\n MultimodalContent,\n UIMessage,\n UseChatOptions,\n UseChatReturn,\n} from './types'\n\nexport function useChat<TTools extends ReadonlyArray<AnyClientTool> = any>(\n options: UseChatOptions<TTools> = {} as UseChatOptions<TTools>,\n): UseChatReturn<TTools> {\n const hookId = createUniqueId()\n const clientId = options.id || hookId\n\n const [messages, setMessages] = createSignal<Array<UIMessage<TTools>>>(\n options.initialMessages || [],\n )\n const [isLoading, setIsLoading] = createSignal(false)\n const [error, setError] = createSignal<Error | undefined>(undefined)\n const [status, setStatus] = createSignal<ChatClientState>('ready')\n const [isSubscribed, setIsSubscribed] = createSignal(false)\n const [connectionStatus, setConnectionStatus] =\n createSignal<ConnectionStatus>('disconnected')\n const [sessionGenerating, setSessionGenerating] = createSignal(false)\n\n // Create ChatClient instance with callbacks to sync state.\n // Every user-provided callback is wrapped so the LATEST `options.xxx` value\n // is read at call time. Direct assignment would freeze the callback to the\n // reference we saw at creation; the wrapper lets reactive `options` or\n // in-place mutations propagate. When the user clears a callback (sets it to\n // undefined), `?.` no-ops.\n const client = createMemo(() => {\n return new ChatClient({\n connection: options.connection,\n id: clientId,\n initialMessages: options.initialMessages,\n body: options.body,\n onResponse: (response) => options.onResponse?.(response),\n onChunk: (chunk) => options.onChunk?.(chunk),\n onFinish: (message) => {\n options.onFinish?.(message)\n },\n onError: (err) => {\n options.onError?.(err)\n },\n tools: options.tools,\n onCustomEvent: (eventType, data, context) =>\n options.onCustomEvent?.(eventType, data, context),\n streamProcessor: options.streamProcessor,\n onMessagesChange: (newMessages: Array<UIMessage<TTools>>) => {\n setMessages(newMessages)\n },\n onLoadingChange: (newIsLoading: boolean) => {\n setIsLoading(newIsLoading)\n },\n onStatusChange: (newStatus: ChatClientState) => {\n setStatus(newStatus)\n },\n onErrorChange: (newError: Error | undefined) => {\n setError(newError)\n },\n onSubscriptionChange: (nextIsSubscribed: boolean) => {\n setIsSubscribed(nextIsSubscribed)\n },\n onConnectionStatusChange: (nextStatus: ConnectionStatus) => {\n setConnectionStatus(nextStatus)\n },\n onSessionGeneratingChange: (isGenerating: boolean) => {\n setSessionGenerating(isGenerating)\n },\n })\n // Only recreate when clientId changes\n // Connection and other options are captured at creation time\n }, [clientId])\n\n // Sync body changes to the client\n // This allows dynamic body values (like model selection) to be updated without recreating the client\n createEffect(() => {\n const currentBody = options.body\n client().updateOptions({ body: currentBody })\n })\n\n // Sync initial messages on mount only\n // Note: initialMessages are passed to ChatClient constructor, but we also\n // set them here to ensure React state is in sync\n createEffect(() => {\n if (options.initialMessages && options.initialMessages.length > 0) {\n // Only set if current messages are empty (initial state)\n if (messages().length === 0) {\n client().setMessagesManually(options.initialMessages)\n }\n }\n }) // Only run on mount - initialMessages are handled by ChatClient constructor\n\n // Apply initial live mode immediately on hook creation.\n if (options.live) {\n client().subscribe()\n } else {\n client().unsubscribe()\n }\n\n createEffect(() => {\n if (options.live) {\n client().subscribe()\n } else {\n client().unsubscribe()\n }\n })\n\n // Cleanup on unmount: stop any in-flight requests.\n onCleanup(() => {\n if (options.live) {\n client().unsubscribe()\n } else {\n client().stop()\n }\n })\n\n // Callback options are read through `options.xxx` at call time, so reactive\n // or mutated options propagate without recreating the client.\n\n const sendMessage = async (content: string | MultimodalContent) => {\n await client().sendMessage(content)\n }\n\n const append = async (message: ModelMessage | UIMessage<TTools>) => {\n await client().append(message)\n }\n\n const reload = async () => {\n await client().reload()\n }\n\n const stop = () => {\n client().stop()\n }\n\n const clear = () => {\n client().clear()\n }\n\n const setMessagesManually = (newMessages: Array<UIMessage<TTools>>) => {\n client().setMessagesManually(newMessages)\n }\n\n const addToolResult = 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\n const addToolApprovalResponse = async (response: {\n id: string\n approved: boolean\n }) => {\n await client().addToolApprovalResponse(response)\n }\n\n return {\n messages,\n sendMessage,\n append,\n reload,\n stop,\n isLoading,\n error,\n status,\n isSubscribed,\n connectionStatus,\n sessionGenerating,\n setMessages: setMessagesManually,\n clear,\n addToolResult,\n addToolApprovalResponse,\n }\n}\n"],"mappings":";;;;AAkBA,SAAgB,QACd,UAAkC,EAAE,EACb;CACvB,MAAM,SAAS,gBAAgB;CAC/B,MAAM,WAAW,QAAQ,MAAM;CAE/B,MAAM,CAAC,UAAU,eAAe,aAC9B,QAAQ,mBAAmB,EAAE,CAC9B;CACD,MAAM,CAAC,WAAW,gBAAgB,aAAa,MAAM;CACrD,MAAM,CAAC,OAAO,YAAY,aAAgC,OAAU;CACpE,MAAM,CAAC,QAAQ,aAAa,aAA8B,QAAQ;CAClE,MAAM,CAAC,cAAc,mBAAmB,aAAa,MAAM;CAC3D,MAAM,CAAC,kBAAkB,uBACvB,aAA+B,eAAe;CAChD,MAAM,CAAC,mBAAmB,wBAAwB,aAAa,MAAM;CAQrE,MAAM,SAAS,iBAAiB;AAC9B,SAAO,IAAI,WAAW;GACpB,YAAY,QAAQ;GACpB,IAAI;GACJ,iBAAiB,QAAQ;GACzB,MAAM,QAAQ;GACd,aAAa,aAAa,QAAQ,aAAa,SAAS;GACxD,UAAU,UAAU,QAAQ,UAAU,MAAM;GAC5C,WAAW,YAAY;AACrB,YAAQ,WAAW,QAAQ;;GAE7B,UAAU,QAAQ;AAChB,YAAQ,UAAU,IAAI;;GAExB,OAAO,QAAQ;GACf,gBAAgB,WAAW,MAAM,YAC/B,QAAQ,gBAAgB,WAAW,MAAM,QAAQ;GACnD,iBAAiB,QAAQ;GACzB,mBAAmB,gBAA0C;AAC3D,gBAAY,YAAY;;GAE1B,kBAAkB,iBAA0B;AAC1C,iBAAa,aAAa;;GAE5B,iBAAiB,cAA+B;AAC9C,cAAU,UAAU;;GAEtB,gBAAgB,aAAgC;AAC9C,aAAS,SAAS;;GAEpB,uBAAuB,qBAA8B;AACnD,oBAAgB,iBAAiB;;GAEnC,2BAA2B,eAAiC;AAC1D,wBAAoB,WAAW;;GAEjC,4BAA4B,iBAA0B;AACpD,yBAAqB,aAAa;;GAErC,CAAC;IAGD,CAAC,SAAS,CAAC;AAId,oBAAmB;EACjB,MAAM,cAAc,QAAQ;AAC5B,UAAQ,CAAC,cAAc,EAAE,MAAM,aAAa,CAAC;GAC7C;AAKF,oBAAmB;AACjB,MAAI,QAAQ,mBAAmB,QAAQ,gBAAgB,SAAS,GAE9D;OAAI,UAAU,CAAC,WAAW,EACxB,SAAQ,CAAC,oBAAoB,QAAQ,gBAAgB;;GAGzD;AAGF,KAAI,QAAQ,KACV,SAAQ,CAAC,WAAW;KAEpB,SAAQ,CAAC,aAAa;AAGxB,oBAAmB;AACjB,MAAI,QAAQ,KACV,SAAQ,CAAC,WAAW;MAEpB,SAAQ,CAAC,aAAa;GAExB;AAGF,iBAAgB;AACd,MAAI,QAAQ,KACV,SAAQ,CAAC,aAAa;MAEtB,SAAQ,CAAC,MAAM;GAEjB;CAKF,MAAM,cAAc,OAAO,YAAwC;AACjE,QAAM,QAAQ,CAAC,YAAY,QAAQ;;CAGrC,MAAM,SAAS,OAAO,YAA8C;AAClE,QAAM,QAAQ,CAAC,OAAO,QAAQ;;CAGhC,MAAM,SAAS,YAAY;AACzB,QAAM,QAAQ,CAAC,QAAQ;;CAGzB,MAAM,aAAa;AACjB,UAAQ,CAAC,MAAM;;CAGjB,MAAM,cAAc;AAClB,UAAQ,CAAC,OAAO;;CAGlB,MAAM,uBAAuB,gBAA0C;AACrE,UAAQ,CAAC,oBAAoB,YAAY;;CAG3C,MAAM,gBAAgB,OAAO,WAMvB;AACJ,QAAM,QAAQ,CAAC,cAAc,OAAO;;CAGtC,MAAM,0BAA0B,OAAO,aAGjC;AACJ,QAAM,QAAQ,CAAC,wBAAwB,SAAS;;AAGlD,QAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,aAAa;EACb;EACA;EACA;EACD"}
@@ -0,0 +1,86 @@
1
+ import { Accessor } from "solid-js";
2
+ import { AudioGenerateInput, ConnectConnectionAdapter, GenerationClientState, GenerationFetcher, InferGenerationOutput } from "@tanstack/ai-client";
3
+ import { AudioGenerationResult, StreamChunk } from "@tanstack/ai";
4
+
5
+ //#region src/use-generate-audio.d.ts
6
+
7
+ /**
8
+ * Options for the useGenerateAudio hook.
9
+ *
10
+ * @template TOutput - The transformed output type (defaults to AudioGenerationResult)
11
+ */
12
+ interface UseGenerateAudioOptions<TOutput = AudioGenerationResult> {
13
+ /** Connect-based adapter for streaming transport (SSE, HTTP stream, custom) */
14
+ connection?: ConnectConnectionAdapter;
15
+ /** Direct async function for audio generation */
16
+ fetcher?: GenerationFetcher<AudioGenerateInput, AudioGenerationResult>;
17
+ /** Unique identifier for this generation instance */
18
+ id?: string;
19
+ /** Additional body parameters to send with connect-based adapter requests */
20
+ body?: Record<string, any>;
21
+ /**
22
+ * Callback when audio is generated. Can optionally return a transformed value.
23
+ *
24
+ * - Return a non-null value to transform and store it as the result
25
+ * - Return `null` to keep the previous result unchanged
26
+ * - Return nothing (`void`) to store the raw result as-is
27
+ */
28
+ onResult?: (result: AudioGenerationResult) => TOutput | null | void;
29
+ /** Callback when an error occurs */
30
+ onError?: (error: Error) => void;
31
+ /** Callback when progress is reported (0-100) */
32
+ onProgress?: (progress: number, message?: string) => void;
33
+ /** Callback for each stream chunk (connect-based adapter mode only) */
34
+ onChunk?: (chunk: StreamChunk) => void;
35
+ }
36
+ /**
37
+ * Return type for the useGenerateAudio hook.
38
+ *
39
+ * @template TOutput - The transformed output type (defaults to AudioGenerationResult)
40
+ */
41
+ interface UseGenerateAudioReturn<TOutput = AudioGenerationResult> {
42
+ /** Trigger audio generation */
43
+ generate: (input: AudioGenerateInput) => Promise<void>;
44
+ /** The generation result containing audio, or null */
45
+ result: Accessor<TOutput | null>;
46
+ /** Whether generation is in progress */
47
+ isLoading: Accessor<boolean>;
48
+ /** Current error, if any */
49
+ error: Accessor<Error | undefined>;
50
+ /** Current state of the generation */
51
+ status: Accessor<GenerationClientState>;
52
+ /** Abort the current generation */
53
+ stop: () => void;
54
+ /** Clear result, error, and return to idle */
55
+ reset: () => void;
56
+ }
57
+ /**
58
+ * Solid hook for generating audio (music, sound effects) using AI models.
59
+ *
60
+ * @example
61
+ * ```tsx
62
+ * import { useGenerateAudio } from '@tanstack/ai-solid'
63
+ * import { fetchServerSentEvents } from '@tanstack/ai-client'
64
+ *
65
+ * function AudioGenerator() {
66
+ * const { generate, result, isLoading } = useGenerateAudio({
67
+ * connection: fetchServerSentEvents('/api/generate/audio'),
68
+ * })
69
+ *
70
+ * return (
71
+ * <div>
72
+ * <button onClick={() => generate({ prompt: 'An upbeat electronic track', duration: 10 })}>
73
+ * Generate
74
+ * </button>
75
+ * {result()?.audio.url && <audio src={result()!.audio.url} controls />}
76
+ * </div>
77
+ * )
78
+ * }
79
+ * ```
80
+ */
81
+ declare function useGenerateAudio<TOnResult extends ((result: AudioGenerationResult) => any) | undefined = undefined>(options: Omit<UseGenerateAudioOptions, 'onResult'> & {
82
+ onResult?: TOnResult;
83
+ }): UseGenerateAudioReturn<InferGenerationOutput<AudioGenerationResult, TOnResult>>;
84
+ //#endregion
85
+ export { UseGenerateAudioOptions, UseGenerateAudioReturn, useGenerateAudio };
86
+ //# sourceMappingURL=use-generate-audio.d.ts.map
@@ -0,0 +1,43 @@
1
+ import { useGeneration } from "./use-generation.js";
2
+
3
+ //#region src/use-generate-audio.ts
4
+ /**
5
+ * Solid hook for generating audio (music, sound effects) using AI models.
6
+ *
7
+ * @example
8
+ * ```tsx
9
+ * import { useGenerateAudio } from '@tanstack/ai-solid'
10
+ * import { fetchServerSentEvents } from '@tanstack/ai-client'
11
+ *
12
+ * function AudioGenerator() {
13
+ * const { generate, result, isLoading } = useGenerateAudio({
14
+ * connection: fetchServerSentEvents('/api/generate/audio'),
15
+ * })
16
+ *
17
+ * return (
18
+ * <div>
19
+ * <button onClick={() => generate({ prompt: 'An upbeat electronic track', duration: 10 })}>
20
+ * Generate
21
+ * </button>
22
+ * {result()?.audio.url && <audio src={result()!.audio.url} controls />}
23
+ * </div>
24
+ * )
25
+ * }
26
+ * ```
27
+ */
28
+ function useGenerateAudio(options) {
29
+ const { generate, result, isLoading, error, status, stop, reset } = useGeneration(options);
30
+ return {
31
+ generate,
32
+ result,
33
+ isLoading,
34
+ error,
35
+ status,
36
+ stop,
37
+ reset
38
+ };
39
+ }
40
+
41
+ //#endregion
42
+ export { useGenerateAudio };
43
+ //# sourceMappingURL=use-generate-audio.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-generate-audio.js","names":[],"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'\nimport type { Accessor } from 'solid-js'\n\n/**\n * Options for the useGenerateAudio hook.\n *\n * @template TOutput - The transformed output type (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 transformed output type (defaults to AudioGenerationResult)\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: Accessor<TOutput | null>\n /** Whether generation is in progress */\n isLoading: Accessor<boolean>\n /** Current error, if any */\n error: Accessor<Error | undefined>\n /** Current state of the generation */\n status: Accessor<GenerationClientState>\n /** Abort the current generation */\n stop: () => void\n /** Clear result, error, and return to idle */\n reset: () => void\n}\n\n/**\n * Solid hook for generating audio (music, sound effects) using AI models.\n *\n * @example\n * ```tsx\n * import { useGenerateAudio } from '@tanstack/ai-solid'\n * import { fetchServerSentEvents } from '@tanstack/ai-client'\n *\n * function AudioGenerator() {\n * const { generate, result, isLoading } = 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 * {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"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AAuFA,SAAgB,iBAId,SAKA;CACA,MAAM,EAAE,UAAU,QAAQ,WAAW,OAAO,QAAQ,MAAM,UACxD,cAAoE,QAAQ;AAE9E,QAAO;EACK;EACV;EACA;EACA;EACA;EACA;EACA;EACD"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/ai-solid",
3
- "version": "0.6.19",
3
+ "version": "0.7.1",
4
4
  "description": "Solid hooks for TanStack AI",
5
5
  "author": "",
6
6
  "license": "MIT",
@@ -31,11 +31,11 @@
31
31
  "streaming"
32
32
  ],
33
33
  "dependencies": {
34
- "@tanstack/ai-client": "0.7.14"
34
+ "@tanstack/ai-client": "0.9.0"
35
35
  },
36
36
  "peerDependencies": {
37
37
  "solid-js": ">=1.9.10",
38
- "@tanstack/ai": "^0.13.0"
38
+ "@tanstack/ai": "^0.15.0"
39
39
  },
40
40
  "devDependencies": {
41
41
  "@solidjs/testing-library": "^0.8.10",
@@ -46,7 +46,7 @@
46
46
  "tsdown": "^0.17.0-beta.6",
47
47
  "typescript": "5.9.3",
48
48
  "vitest": "^4.0.14",
49
- "@tanstack/ai": "0.13.0"
49
+ "@tanstack/ai": "0.15.0"
50
50
  },
51
51
  "scripts": {
52
52
  "clean": "premove ./build ./dist",
package/src/index.ts CHANGED
@@ -19,6 +19,12 @@ export type {
19
19
  UseGenerateImageReturn,
20
20
  } from './use-generate-image'
21
21
 
22
+ export { useGenerateAudio } from './use-generate-audio'
23
+ export type {
24
+ UseGenerateAudioOptions,
25
+ UseGenerateAudioReturn,
26
+ } from './use-generate-audio'
27
+
22
28
  export { useGenerateSpeech } from './use-generate-speech'
23
29
  export type {
24
30
  UseGenerateSpeechOptions,
@@ -51,6 +57,7 @@ export {
51
57
  type InferChatMessages,
52
58
  type GenerationClientState,
53
59
  type ImageGenerateInput,
60
+ type AudioGenerateInput,
54
61
  type SpeechGenerateInput,
55
62
  type TranscriptionGenerateInput,
56
63
  type SummarizeGenerateInput,
package/src/use-chat.ts CHANGED
@@ -33,18 +33,20 @@ export function useChat<TTools extends ReadonlyArray<AnyClientTool> = any>(
33
33
  createSignal<ConnectionStatus>('disconnected')
34
34
  const [sessionGenerating, setSessionGenerating] = createSignal(false)
35
35
 
36
- // Create ChatClient instance with callbacks to sync state
37
- // Note: Options are captured at client creation time.
38
- // The connection adapter can use functions for dynamic values (url, headers, etc.)
39
- // which are evaluated lazily on each request.
36
+ // Create ChatClient instance with callbacks to sync state.
37
+ // Every user-provided callback is wrapped so the LATEST `options.xxx` value
38
+ // is read at call time. Direct assignment would freeze the callback to the
39
+ // reference we saw at creation; the wrapper lets reactive `options` or
40
+ // in-place mutations propagate. When the user clears a callback (sets it to
41
+ // undefined), `?.` no-ops.
40
42
  const client = createMemo(() => {
41
43
  return new ChatClient({
42
44
  connection: options.connection,
43
45
  id: clientId,
44
46
  initialMessages: options.initialMessages,
45
47
  body: options.body,
46
- onResponse: options.onResponse,
47
- onChunk: options.onChunk,
48
+ onResponse: (response) => options.onResponse?.(response),
49
+ onChunk: (chunk) => options.onChunk?.(chunk),
48
50
  onFinish: (message) => {
49
51
  options.onFinish?.(message)
50
52
  },
@@ -52,7 +54,8 @@ export function useChat<TTools extends ReadonlyArray<AnyClientTool> = any>(
52
54
  options.onError?.(err)
53
55
  },
54
56
  tools: options.tools,
55
- onCustomEvent: options.onCustomEvent,
57
+ onCustomEvent: (eventType, data, context) =>
58
+ options.onCustomEvent?.(eventType, data, context),
56
59
  streamProcessor: options.streamProcessor,
57
60
  onMessagesChange: (newMessages: Array<UIMessage<TTools>>) => {
58
61
  setMessages(newMessages)
@@ -123,9 +126,8 @@ export function useChat<TTools extends ReadonlyArray<AnyClientTool> = any>(
123
126
  }
124
127
  })
125
128
 
126
- // Note: Callback options (onResponse, onChunk, onFinish, onError, onToolCall)
127
- // are captured at client creation time. Changes to these callbacks require
128
- // remounting the component or changing the connection to recreate the client.
129
+ // Callback options are read through `options.xxx` at call time, so reactive
130
+ // or mutated options propagate without recreating the client.
129
131
 
130
132
  const sendMessage = async (content: string | MultimodalContent) => {
131
133
  await client().sendMessage(content)
@@ -0,0 +1,110 @@
1
+ import { useGeneration } from './use-generation'
2
+ import type { AudioGenerationResult, StreamChunk } from '@tanstack/ai'
3
+ import type {
4
+ AudioGenerateInput,
5
+ ConnectConnectionAdapter,
6
+ GenerationClientState,
7
+ GenerationFetcher,
8
+ InferGenerationOutput,
9
+ } from '@tanstack/ai-client'
10
+ import type { Accessor } from 'solid-js'
11
+
12
+ /**
13
+ * Options for the useGenerateAudio hook.
14
+ *
15
+ * @template TOutput - The transformed output type (defaults to AudioGenerationResult)
16
+ */
17
+ export interface UseGenerateAudioOptions<TOutput = AudioGenerationResult> {
18
+ /** Connect-based adapter for streaming transport (SSE, HTTP stream, custom) */
19
+ connection?: ConnectConnectionAdapter
20
+ /** Direct async function for audio generation */
21
+ fetcher?: GenerationFetcher<AudioGenerateInput, AudioGenerationResult>
22
+ /** Unique identifier for this generation instance */
23
+ id?: string
24
+ /** Additional body parameters to send with connect-based adapter requests */
25
+ body?: Record<string, any>
26
+ /**
27
+ * Callback when audio is generated. Can optionally return a transformed value.
28
+ *
29
+ * - Return a non-null value to transform and store it as the result
30
+ * - Return `null` to keep the previous result unchanged
31
+ * - Return nothing (`void`) to store the raw result as-is
32
+ */
33
+ onResult?: (result: AudioGenerationResult) => TOutput | null | void
34
+ /** Callback when an error occurs */
35
+ onError?: (error: Error) => void
36
+ /** Callback when progress is reported (0-100) */
37
+ onProgress?: (progress: number, message?: string) => void
38
+ /** Callback for each stream chunk (connect-based adapter mode only) */
39
+ onChunk?: (chunk: StreamChunk) => void
40
+ }
41
+
42
+ /**
43
+ * Return type for the useGenerateAudio hook.
44
+ *
45
+ * @template TOutput - The transformed output type (defaults to AudioGenerationResult)
46
+ */
47
+ export interface UseGenerateAudioReturn<TOutput = AudioGenerationResult> {
48
+ /** Trigger audio generation */
49
+ generate: (input: AudioGenerateInput) => Promise<void>
50
+ /** The generation result containing audio, or null */
51
+ result: Accessor<TOutput | null>
52
+ /** Whether generation is in progress */
53
+ isLoading: Accessor<boolean>
54
+ /** Current error, if any */
55
+ error: Accessor<Error | undefined>
56
+ /** Current state of the generation */
57
+ status: Accessor<GenerationClientState>
58
+ /** Abort the current generation */
59
+ stop: () => void
60
+ /** Clear result, error, and return to idle */
61
+ reset: () => void
62
+ }
63
+
64
+ /**
65
+ * Solid hook for generating audio (music, sound effects) using AI models.
66
+ *
67
+ * @example
68
+ * ```tsx
69
+ * import { useGenerateAudio } from '@tanstack/ai-solid'
70
+ * import { fetchServerSentEvents } from '@tanstack/ai-client'
71
+ *
72
+ * function AudioGenerator() {
73
+ * const { generate, result, isLoading } = useGenerateAudio({
74
+ * connection: fetchServerSentEvents('/api/generate/audio'),
75
+ * })
76
+ *
77
+ * return (
78
+ * <div>
79
+ * <button onClick={() => generate({ prompt: 'An upbeat electronic track', duration: 10 })}>
80
+ * Generate
81
+ * </button>
82
+ * {result()?.audio.url && <audio src={result()!.audio.url} controls />}
83
+ * </div>
84
+ * )
85
+ * }
86
+ * ```
87
+ */
88
+ export function useGenerateAudio<
89
+ TOnResult extends ((result: AudioGenerationResult) => any) | undefined =
90
+ undefined,
91
+ >(
92
+ options: Omit<UseGenerateAudioOptions, 'onResult'> & {
93
+ onResult?: TOnResult
94
+ },
95
+ ): UseGenerateAudioReturn<
96
+ InferGenerationOutput<AudioGenerationResult, TOnResult>
97
+ > {
98
+ const { generate, result, isLoading, error, status, stop, reset } =
99
+ useGeneration<AudioGenerateInput, AudioGenerationResult, TOnResult>(options)
100
+
101
+ return {
102
+ generate: generate as (input: AudioGenerateInput) => Promise<void>,
103
+ result,
104
+ isLoading,
105
+ error,
106
+ status,
107
+ stop,
108
+ reset,
109
+ }
110
+ }