@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 +3 -2
- package/dist/index.js +2 -1
- package/dist/use-chat.js +3 -3
- package/dist/use-chat.js.map +1 -1
- package/dist/use-generate-audio.d.ts +86 -0
- package/dist/use-generate-audio.js +43 -0
- package/dist/use-generate-audio.js.map +1 -0
- package/package.json +4 -4
- package/src/index.ts +7 -0
- package/src/use-chat.ts +12 -10
- package/src/use-generate-audio.ts +110 -0
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);
|
package/dist/use-chat.js.map
CHANGED
|
@@ -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
|
|
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.
|
|
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.
|
|
34
|
+
"@tanstack/ai-client": "0.9.0"
|
|
35
35
|
},
|
|
36
36
|
"peerDependencies": {
|
|
37
37
|
"solid-js": ">=1.9.10",
|
|
38
|
-
"@tanstack/ai": "^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.
|
|
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
|
-
//
|
|
38
|
-
//
|
|
39
|
-
//
|
|
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:
|
|
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
|
-
//
|
|
127
|
-
//
|
|
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
|
+
}
|