@tanstack/ai-solid 0.13.14 → 0.14.0
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 +12 -0
- package/dist/index.js +12 -0
- package/dist/types.d.ts +139 -0
- package/dist/use-audio-recorder.d.ts +37 -0
- package/dist/use-audio-recorder.js +39 -0
- package/dist/use-audio-recorder.js.map +1 -0
- package/dist/use-chat.d.ts +9 -0
- package/dist/use-chat.js +162 -0
- package/dist/use-chat.js.map +1 -0
- package/dist/use-generate-audio.d.ts +88 -0
- package/dist/use-generate-audio.js +52 -0
- package/dist/use-generate-audio.js.map +1 -0
- package/dist/use-generate-image.d.ts +96 -0
- package/dist/use-generate-image.js +60 -0
- package/dist/use-generate-image.js.map +1 -0
- package/dist/use-generate-speech.d.ts +90 -0
- package/dist/use-generate-speech.js +54 -0
- package/dist/use-generate-speech.js.map +1 -0
- package/dist/use-generate-video.d.ts +103 -0
- package/dist/use-generate-video.js +114 -0
- package/dist/use-generate-video.js.map +1 -0
- package/dist/use-generation.d.ts +88 -0
- package/dist/use-generation.js +94 -0
- package/dist/use-generation.js.map +1 -0
- package/dist/use-summarize.d.ts +93 -0
- package/dist/use-summarize.js +57 -0
- package/dist/use-summarize.js.map +1 -0
- package/dist/use-transcription.d.ts +99 -0
- package/dist/use-transcription.js +63 -0
- package/dist/use-transcription.js.map +1 -0
- package/package.json +4 -4
- package/src/index.ts +6 -0
- package/src/use-audio-recorder.ts +87 -0
- package/src/use-generate-audio.ts +5 -8
- package/src/use-generate-image.ts +5 -8
- package/src/use-generate-speech.ts +7 -7
- package/src/use-generate-video.ts +17 -9
- package/src/use-generation.ts +17 -6
- package/src/use-summarize.ts +7 -8
- package/src/use-transcription.ts +9 -11
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { ChatRequestBody, DeepPartial, UIMessage, UseChatOptions, UseChatReturn } from "./types.js";
|
|
2
|
+
import { useChat } from "./use-chat.js";
|
|
3
|
+
import { UseGenerationOptions, UseGenerationReturn, useGeneration } from "./use-generation.js";
|
|
4
|
+
import { UseGenerateImageOptions, UseGenerateImageReturn, useGenerateImage } from "./use-generate-image.js";
|
|
5
|
+
import { UseGenerateAudioOptions, UseGenerateAudioReturn, useGenerateAudio } from "./use-generate-audio.js";
|
|
6
|
+
import { UseGenerateSpeechOptions, UseGenerateSpeechReturn, useGenerateSpeech } from "./use-generate-speech.js";
|
|
7
|
+
import { UseTranscriptionOptions, UseTranscriptionReturn, useTranscription } from "./use-transcription.js";
|
|
8
|
+
import { UseSummarizeOptions, UseSummarizeReturn, useSummarize } from "./use-summarize.js";
|
|
9
|
+
import { UseGenerateVideoOptions, UseGenerateVideoReturn, useGenerateVideo } from "./use-generate-video.js";
|
|
10
|
+
import { UseAudioRecorderOptions, UseAudioRecorderReturn, useAudioRecorder } from "./use-audio-recorder.js";
|
|
11
|
+
import { AudioGenerateInput, ConnectConnectionAdapter, ConnectionAdapter, FetchConnectionOptions, GenerationClientState, ImageGenerateInput, InferChatMessages, RunAgentInputContext, SpeechGenerateInput, SubscribeConnectionAdapter, SummarizeGenerateInput, TranscriptionGenerateInput, VideoGenerateInput, VideoGenerateResult, VideoStatusInfo, XhrConnectionOptions, createChatClientOptions, fetchHttpStream, fetchServerSentEvents, rpcStream, stream, xhrHttpStream, xhrServerSentEvents } from "@tanstack/ai-client";
|
|
12
|
+
export { type AudioGenerateInput, type ChatRequestBody, type ConnectConnectionAdapter, type ConnectionAdapter, type DeepPartial, type FetchConnectionOptions, type GenerationClientState, type ImageGenerateInput, type InferChatMessages, type RunAgentInputContext, type SpeechGenerateInput, type SubscribeConnectionAdapter, type SummarizeGenerateInput, type TranscriptionGenerateInput, type UIMessage, type UseAudioRecorderOptions, type UseAudioRecorderReturn, 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, type XhrConnectionOptions, createChatClientOptions, fetchHttpStream, fetchServerSentEvents, rpcStream, stream, useAudioRecorder, useChat, useGenerateAudio, useGenerateImage, useGenerateSpeech, useGenerateVideo, useGeneration, useSummarize, useTranscription, xhrHttpStream, xhrServerSentEvents };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { useChat } from "./use-chat.js";
|
|
2
|
+
import { useGeneration } from "./use-generation.js";
|
|
3
|
+
import { useGenerateImage } from "./use-generate-image.js";
|
|
4
|
+
import { useGenerateAudio } from "./use-generate-audio.js";
|
|
5
|
+
import { useGenerateSpeech } from "./use-generate-speech.js";
|
|
6
|
+
import { useTranscription } from "./use-transcription.js";
|
|
7
|
+
import { useSummarize } from "./use-summarize.js";
|
|
8
|
+
import { useGenerateVideo } from "./use-generate-video.js";
|
|
9
|
+
import { useAudioRecorder } from "./use-audio-recorder.js";
|
|
10
|
+
import { createChatClientOptions, fetchHttpStream, fetchServerSentEvents, rpcStream, stream, xhrHttpStream, xhrServerSentEvents } from "@tanstack/ai-client";
|
|
11
|
+
|
|
12
|
+
export { createChatClientOptions, fetchHttpStream, fetchServerSentEvents, rpcStream, stream, useAudioRecorder, useChat, useGenerateAudio, useGenerateImage, useGenerateSpeech, useGenerateVideo, useGeneration, useSummarize, useTranscription, xhrHttpStream, xhrServerSentEvents };
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { Accessor } from "solid-js";
|
|
2
|
+
import { AIDevtoolsDisplayOptions, ChatClientOptions, ChatClientState, ChatRequestBody, ClientContextOptionFromTools, ConnectionStatus, DistributedOmit, InferredClientContext, MultimodalContent, UIMessage } from "@tanstack/ai-client";
|
|
3
|
+
import { AnyClientTool, InferSchemaType, ModelMessage, SchemaInput } from "@tanstack/ai";
|
|
4
|
+
|
|
5
|
+
//#region src/types.d.ts
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Recursive partial — every property and every nested array element is optional.
|
|
9
|
+
* Used to type the in-flight `partial` accessor while a structured-output
|
|
10
|
+
* stream is still arriving.
|
|
11
|
+
*/
|
|
12
|
+
type DeepPartial<T> = T extends ReadonlyArray<infer U> ? Array<DeepPartial<U>> : T extends object ? { [K in keyof T]?: DeepPartial<T[K]> } : T;
|
|
13
|
+
/**
|
|
14
|
+
* Options for the useChat hook.
|
|
15
|
+
*
|
|
16
|
+
* This extends ChatClientOptions but omits the state change callbacks that are
|
|
17
|
+
* managed internally by Solid signals:
|
|
18
|
+
* - `onMessagesChange` - Managed by Solid signal (exposed as `messages`)
|
|
19
|
+
* - `onLoadingChange` - Managed by Solid signal (exposed as `isLoading`)
|
|
20
|
+
* - `onErrorChange` - Managed by Solid signal (exposed as `error`)
|
|
21
|
+
* - `onStatusChange` - Managed by Solid signal (exposed as `status`)
|
|
22
|
+
*
|
|
23
|
+
* All other callbacks (onResponse, onChunk, onFinish, onError) are
|
|
24
|
+
* passed through to the underlying ChatClient and can be used for side effects.
|
|
25
|
+
*
|
|
26
|
+
* When `outputSchema` is supplied, the hook returns typed `partial` and `final`
|
|
27
|
+
* accessors. The schema is used purely for type inference; server-side
|
|
28
|
+
* validation still runs against the schema passed to `chat({ outputSchema })`
|
|
29
|
+
* on the server route.
|
|
30
|
+
*
|
|
31
|
+
* Note: Connection and body changes will recreate the ChatClient instance.
|
|
32
|
+
* To update these options, remount the component or use a key prop.
|
|
33
|
+
*/
|
|
34
|
+
type UseChatOptions<TTools extends ReadonlyArray<AnyClientTool> = any, TSchema extends SchemaInput | undefined = undefined, TContext = InferredClientContext<TTools>> = DistributedOmit<ChatClientOptions<TTools, TContext>, 'onMessagesChange' | 'onLoadingChange' | 'onErrorChange' | 'onStatusChange' | 'onSubscriptionChange' | 'onConnectionStatusChange' | 'onSessionGeneratingChange' | 'context' | 'devtools'> & {
|
|
35
|
+
/** Display options for TanStack AI Devtools. */
|
|
36
|
+
devtools?: AIDevtoolsDisplayOptions;
|
|
37
|
+
live?: boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Standard-schema-compatible schema (Zod, Valibot, ArkType, or plain JSON
|
|
40
|
+
* Schema). Used to infer the shape of `partial` and `final`.
|
|
41
|
+
*/
|
|
42
|
+
outputSchema?: TSchema;
|
|
43
|
+
} & ClientContextOptionFromTools<TTools, TContext>;
|
|
44
|
+
/**
|
|
45
|
+
* Discriminated return shape: when `outputSchema` is supplied, the hook adds
|
|
46
|
+
* typed `partial` / `final` accessors; otherwise the return is unchanged.
|
|
47
|
+
*/
|
|
48
|
+
type UseChatReturn<TTools extends ReadonlyArray<AnyClientTool> = any, TSchema extends SchemaInput | undefined = undefined> = BaseUseChatReturn<TTools, TSchema extends SchemaInput ? InferSchemaType<TSchema> : unknown> & (TSchema extends SchemaInput ? {
|
|
49
|
+
/**
|
|
50
|
+
* Live progressively-parsed structured output. Derived from the
|
|
51
|
+
* latest assistant message's structured-output part.
|
|
52
|
+
*/
|
|
53
|
+
partial: Accessor<DeepPartial<InferSchemaType<TSchema>>>;
|
|
54
|
+
/**
|
|
55
|
+
* Final, schema-validated structured output. `null` until the latest
|
|
56
|
+
* assistant turn's structured-output part transitions to `complete`.
|
|
57
|
+
*/
|
|
58
|
+
final: Accessor<InferSchemaType<TSchema> | null>;
|
|
59
|
+
} : Record<never, never>);
|
|
60
|
+
interface BaseUseChatReturn<TTools extends ReadonlyArray<AnyClientTool> = any, TData = unknown> {
|
|
61
|
+
/**
|
|
62
|
+
* Current messages in the conversation. When `outputSchema` is supplied,
|
|
63
|
+
* `messages()[i].parts.find(p => p.type === 'structured-output')` is typed
|
|
64
|
+
* by the schema — `data: T`, `partial: DeepPartial<T>`.
|
|
65
|
+
*/
|
|
66
|
+
messages: Accessor<Array<UIMessage<TTools, TData>>>;
|
|
67
|
+
/**
|
|
68
|
+
* Send a message and get a response.
|
|
69
|
+
* Can be a simple string or multimodal content with images, audio, etc.
|
|
70
|
+
*/
|
|
71
|
+
sendMessage: (content: string | MultimodalContent) => Promise<void>;
|
|
72
|
+
/**
|
|
73
|
+
* Append a message to the conversation
|
|
74
|
+
*/
|
|
75
|
+
append: (message: ModelMessage | UIMessage<TTools, TData>) => Promise<void>;
|
|
76
|
+
/**
|
|
77
|
+
* Add the result of a client-side tool execution
|
|
78
|
+
*/
|
|
79
|
+
addToolResult: (result: {
|
|
80
|
+
toolCallId: string;
|
|
81
|
+
tool: string;
|
|
82
|
+
output: any;
|
|
83
|
+
state?: 'output-available' | 'output-error';
|
|
84
|
+
errorText?: string;
|
|
85
|
+
}) => Promise<void>;
|
|
86
|
+
/**
|
|
87
|
+
* Respond to a tool approval request
|
|
88
|
+
*/
|
|
89
|
+
addToolApprovalResponse: (response: {
|
|
90
|
+
id: string;
|
|
91
|
+
approved: boolean;
|
|
92
|
+
}) => Promise<void>;
|
|
93
|
+
/**
|
|
94
|
+
* Reload the last assistant message
|
|
95
|
+
*/
|
|
96
|
+
reload: () => Promise<void>;
|
|
97
|
+
/**
|
|
98
|
+
* Stop the current response generation
|
|
99
|
+
*/
|
|
100
|
+
stop: () => void;
|
|
101
|
+
/**
|
|
102
|
+
* Whether a response is currently being generated
|
|
103
|
+
*/
|
|
104
|
+
isLoading: Accessor<boolean>;
|
|
105
|
+
/**
|
|
106
|
+
* Current error, if any
|
|
107
|
+
*/
|
|
108
|
+
error: Accessor<Error | undefined>;
|
|
109
|
+
/**
|
|
110
|
+
* Set messages manually
|
|
111
|
+
*/
|
|
112
|
+
setMessages: (messages: Array<UIMessage<TTools, TData>>) => void;
|
|
113
|
+
/**
|
|
114
|
+
* Clear all messages
|
|
115
|
+
*/
|
|
116
|
+
clear: () => void;
|
|
117
|
+
/**
|
|
118
|
+
* Current generation status
|
|
119
|
+
*/
|
|
120
|
+
status: Accessor<ChatClientState>;
|
|
121
|
+
/**
|
|
122
|
+
* Whether the subscription loop is currently active
|
|
123
|
+
*/
|
|
124
|
+
isSubscribed: Accessor<boolean>;
|
|
125
|
+
/**
|
|
126
|
+
* Current connection lifecycle status
|
|
127
|
+
*/
|
|
128
|
+
connectionStatus: Accessor<ConnectionStatus>;
|
|
129
|
+
/**
|
|
130
|
+
* Whether the shared session is actively generating.
|
|
131
|
+
* Derived from stream run events (RUN_STARTED / RUN_FINISHED / RUN_ERROR).
|
|
132
|
+
* Unlike `isLoading` (request-local), this reflects shared generation
|
|
133
|
+
* activity visible to all subscribers (e.g. across tabs/devices).
|
|
134
|
+
*/
|
|
135
|
+
sessionGenerating: Accessor<boolean>;
|
|
136
|
+
}
|
|
137
|
+
//#endregion
|
|
138
|
+
export { type ChatRequestBody, DeepPartial, type UIMessage, UseChatOptions, UseChatReturn };
|
|
139
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { AudioRecorderOptions, AudioRecording, InferAudioRecordingOutput } from "@tanstack/ai-client";
|
|
2
|
+
|
|
3
|
+
//#region src/use-audio-recorder.d.ts
|
|
4
|
+
type UseAudioRecorderOptions<TOnComplete> = AudioRecorderOptions & {
|
|
5
|
+
/**
|
|
6
|
+
* Optional transform applied to the recording when `stop()` resolves. Its
|
|
7
|
+
* (awaited) return value becomes `recording` and the resolved value of
|
|
8
|
+
* `stop()`. Return nothing to keep the raw `AudioRecording`.
|
|
9
|
+
*/
|
|
10
|
+
onComplete?: TOnComplete;
|
|
11
|
+
};
|
|
12
|
+
interface UseAudioRecorderReturn<TOutput> {
|
|
13
|
+
/** Solid accessor: latest recording (transformed if `onComplete` provided), or null. */
|
|
14
|
+
recording: () => TOutput | null;
|
|
15
|
+
/** Solid accessor: true while actively capturing audio. */
|
|
16
|
+
isRecording: () => boolean;
|
|
17
|
+
/** Whether the browser supports recording. */
|
|
18
|
+
isSupported: boolean;
|
|
19
|
+
start: () => Promise<void>;
|
|
20
|
+
/** Stop and resolve with the completed recording (transformed if `onComplete` provided). */
|
|
21
|
+
stop: () => Promise<TOutput>;
|
|
22
|
+
/** Discard the in-progress recording and release the mic. */
|
|
23
|
+
cancel: () => void;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Solid hook for recording an audio message. The resolved recording carries
|
|
27
|
+
* `.part` (for `useChat.sendMessage`) and `.base64` (for generation hooks).
|
|
28
|
+
*
|
|
29
|
+
* Errors are delivered via `onError`. `start()` and `stop()` also reject on
|
|
30
|
+
* failure (and `stop()` rejects with `Recording cancelled` if `cancel()` runs
|
|
31
|
+
* while a stop is in flight, e.g. on unmount) — handle one channel, not both.
|
|
32
|
+
*/
|
|
33
|
+
declare function useAudioRecorder<TOnComplete extends (recording: AudioRecording) => unknown>(options: UseAudioRecorderOptions<TOnComplete>): UseAudioRecorderReturn<InferAudioRecordingOutput<TOnComplete>>;
|
|
34
|
+
declare function useAudioRecorder(options?: UseAudioRecorderOptions<undefined>): UseAudioRecorderReturn<AudioRecording>;
|
|
35
|
+
//#endregion
|
|
36
|
+
export { UseAudioRecorderOptions, UseAudioRecorderReturn, useAudioRecorder };
|
|
37
|
+
//# sourceMappingURL=use-audio-recorder.d.ts.map
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { createSignal, onCleanup } from "solid-js";
|
|
2
|
+
import { AudioRecorder } from "@tanstack/ai-client";
|
|
3
|
+
|
|
4
|
+
//#region src/use-audio-recorder.ts
|
|
5
|
+
function useAudioRecorder(options = {}) {
|
|
6
|
+
const recorder = new AudioRecorder({
|
|
7
|
+
...options.audio !== void 0 && { audio: options.audio },
|
|
8
|
+
...options.mimeType !== void 0 && { mimeType: options.mimeType },
|
|
9
|
+
...options.onError !== void 0 && { onError: options.onError }
|
|
10
|
+
});
|
|
11
|
+
const [isRecording, setIsRecording] = createSignal(false);
|
|
12
|
+
const [recording, setRecording] = createSignal(null);
|
|
13
|
+
const unsubscribe = recorder.subscribe((state) => {
|
|
14
|
+
setIsRecording(state === "recording");
|
|
15
|
+
});
|
|
16
|
+
onCleanup(() => {
|
|
17
|
+
unsubscribe();
|
|
18
|
+
recorder.cancel();
|
|
19
|
+
});
|
|
20
|
+
const stop = async () => {
|
|
21
|
+
const rawRecording = await recorder.stop();
|
|
22
|
+
const transformed = await options.onComplete?.(rawRecording);
|
|
23
|
+
const output = transformed === void 0 ? rawRecording : transformed;
|
|
24
|
+
setRecording(() => output);
|
|
25
|
+
return output;
|
|
26
|
+
};
|
|
27
|
+
return {
|
|
28
|
+
recording,
|
|
29
|
+
isRecording,
|
|
30
|
+
isSupported: AudioRecorder.isSupported(),
|
|
31
|
+
start: () => recorder.start(),
|
|
32
|
+
stop,
|
|
33
|
+
cancel: () => recorder.cancel()
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
//#endregion
|
|
38
|
+
export { useAudioRecorder };
|
|
39
|
+
//# sourceMappingURL=use-audio-recorder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-audio-recorder.js","names":[],"sources":["../src/use-audio-recorder.ts"],"sourcesContent":["import { createSignal, onCleanup } from 'solid-js'\nimport { AudioRecorder } from '@tanstack/ai-client'\nimport type {\n AudioRecorderOptions,\n AudioRecording,\n InferAudioRecordingOutput,\n} from '@tanstack/ai-client'\n\nexport type UseAudioRecorderOptions<TOnComplete> = AudioRecorderOptions & {\n /**\n * Optional transform applied to the recording when `stop()` resolves. Its\n * (awaited) return value becomes `recording` and the resolved value of\n * `stop()`. Return nothing to keep the raw `AudioRecording`.\n */\n onComplete?: TOnComplete\n}\n\nexport interface UseAudioRecorderReturn<TOutput> {\n /** Solid accessor: latest recording (transformed if `onComplete` provided), or null. */\n recording: () => TOutput | null\n /** Solid accessor: true while actively capturing audio. */\n isRecording: () => boolean\n /** Whether the browser supports recording. */\n isSupported: boolean\n start: () => Promise<void>\n /** Stop and resolve with the completed recording (transformed if `onComplete` provided). */\n stop: () => Promise<TOutput>\n /** Discard the in-progress recording and release the mic. */\n cancel: () => void\n}\n\n/**\n * Solid hook for recording an audio message. The resolved recording carries\n * `.part` (for `useChat.sendMessage`) and `.base64` (for generation hooks).\n *\n * Errors are delivered via `onError`. `start()` and `stop()` also reject on\n * failure (and `stop()` rejects with `Recording cancelled` if `cancel()` runs\n * while a stop is in flight, e.g. on unmount) — handle one channel, not both.\n */\nexport function useAudioRecorder<\n TOnComplete extends (recording: AudioRecording) => unknown,\n>(\n options: UseAudioRecorderOptions<TOnComplete>,\n): UseAudioRecorderReturn<InferAudioRecordingOutput<TOnComplete>>\nexport function useAudioRecorder(\n options?: UseAudioRecorderOptions<undefined>,\n): UseAudioRecorderReturn<AudioRecording>\nexport function useAudioRecorder(\n options: UseAudioRecorderOptions<(recording: AudioRecording) => unknown> = {},\n): UseAudioRecorderReturn<unknown> {\n const recorder = new AudioRecorder({\n ...(options.audio !== undefined && { audio: options.audio }),\n ...(options.mimeType !== undefined && { mimeType: options.mimeType }),\n ...(options.onError !== undefined && { onError: options.onError }),\n })\n const [isRecording, setIsRecording] = createSignal(false)\n const [recording, setRecording] = createSignal<unknown>(null)\n\n const unsubscribe = recorder.subscribe((state) => {\n setIsRecording(state === 'recording')\n })\n\n onCleanup(() => {\n unsubscribe()\n recorder.cancel()\n })\n\n const stop = async (): Promise<unknown> => {\n const rawRecording = await recorder.stop()\n const transformed = await options.onComplete?.(rawRecording)\n // Only `undefined` (returning nothing) keeps the raw recording; a returned\n // null is a real value, matching the inferred output type.\n const output = transformed === undefined ? rawRecording : transformed\n // Store via updater so a function-valued transform result isn't invoked.\n setRecording(() => output)\n return output\n }\n\n return {\n recording,\n isRecording,\n isSupported: AudioRecorder.isSupported(),\n start: () => recorder.start(),\n stop,\n cancel: () => recorder.cancel(),\n }\n}\n"],"mappings":";;;;AA+CA,SAAgB,iBACd,UAA2E,EAAE,EAC5C;CACjC,MAAM,WAAW,IAAI,cAAc;EACjC,GAAI,QAAQ,UAAU,UAAa,EAAE,OAAO,QAAQ,OAAO;EAC3D,GAAI,QAAQ,aAAa,UAAa,EAAE,UAAU,QAAQ,UAAU;EACpE,GAAI,QAAQ,YAAY,UAAa,EAAE,SAAS,QAAQ,SAAS;EAClE,CAAC;CACF,MAAM,CAAC,aAAa,kBAAkB,aAAa,MAAM;CACzD,MAAM,CAAC,WAAW,gBAAgB,aAAsB,KAAK;CAE7D,MAAM,cAAc,SAAS,WAAW,UAAU;AAChD,iBAAe,UAAU,YAAY;GACrC;AAEF,iBAAgB;AACd,eAAa;AACb,WAAS,QAAQ;GACjB;CAEF,MAAM,OAAO,YAA8B;EACzC,MAAM,eAAe,MAAM,SAAS,MAAM;EAC1C,MAAM,cAAc,MAAM,QAAQ,aAAa,aAAa;EAG5D,MAAM,SAAS,gBAAgB,SAAY,eAAe;AAE1D,qBAAmB,OAAO;AAC1B,SAAO;;AAGT,QAAO;EACL;EACA;EACA,aAAa,cAAc,aAAa;EACxC,aAAa,SAAS,OAAO;EAC7B;EACA,cAAc,SAAS,QAAQ;EAChC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { UseChatOptions, UseChatReturn } from "./types.js";
|
|
2
|
+
import { InferredClientContext } from "@tanstack/ai-client";
|
|
3
|
+
import { AnyClientTool, SchemaInput } from "@tanstack/ai";
|
|
4
|
+
|
|
5
|
+
//#region src/use-chat.d.ts
|
|
6
|
+
declare function useChat<TTools extends ReadonlyArray<AnyClientTool> = any, TSchema extends SchemaInput | undefined = undefined, TContext = InferredClientContext<TTools>>(options?: UseChatOptions<TTools, TSchema, TContext>): UseChatReturn<TTools, TSchema>;
|
|
7
|
+
//#endregion
|
|
8
|
+
export { useChat };
|
|
9
|
+
//# sourceMappingURL=use-chat.d.ts.map
|
package/dist/use-chat.js
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { createEffect, createMemo, createSignal, createUniqueId, onCleanup, onMount } from "solid-js";
|
|
2
|
+
import { ChatClient } from "@tanstack/ai-client";
|
|
3
|
+
import { createChatDevtoolsBridge } from "@tanstack/ai-client/devtools";
|
|
4
|
+
|
|
5
|
+
//#region src/use-chat.ts
|
|
6
|
+
function useChat(options = {}) {
|
|
7
|
+
const hookId = createUniqueId();
|
|
8
|
+
const clientId = options.id || hookId;
|
|
9
|
+
const [messages, setMessages] = createSignal(options.initialMessages || []);
|
|
10
|
+
const [isLoading, setIsLoading] = createSignal(false);
|
|
11
|
+
const [error, setError] = createSignal(void 0);
|
|
12
|
+
const [status, setStatus] = createSignal("ready");
|
|
13
|
+
const [isSubscribed, setIsSubscribed] = createSignal(false);
|
|
14
|
+
const [connectionStatus, setConnectionStatus] = createSignal("disconnected");
|
|
15
|
+
const [sessionGenerating, setSessionGenerating] = createSignal(false);
|
|
16
|
+
const client = createMemo(() => {
|
|
17
|
+
return new ChatClient({
|
|
18
|
+
devtoolsBridgeFactory: createChatDevtoolsBridge,
|
|
19
|
+
...options.connection ? { connection: options.connection } : { fetcher: options.fetcher },
|
|
20
|
+
id: clientId,
|
|
21
|
+
...options.initialMessages !== void 0 && { initialMessages: options.initialMessages },
|
|
22
|
+
...options.persistence !== void 0 && { persistence: options.persistence },
|
|
23
|
+
body: options.body,
|
|
24
|
+
...options.threadId !== void 0 && { threadId: options.threadId },
|
|
25
|
+
...options.forwardedProps !== void 0 && { forwardedProps: options.forwardedProps },
|
|
26
|
+
...options.context !== void 0 && { context: options.context },
|
|
27
|
+
devtools: {
|
|
28
|
+
...options.devtools,
|
|
29
|
+
framework: "solid",
|
|
30
|
+
hookName: "useChat",
|
|
31
|
+
outputKind: options.outputSchema ? "structured" : "chat"
|
|
32
|
+
},
|
|
33
|
+
onResponse: (response) => options.onResponse?.(response),
|
|
34
|
+
onChunk: (chunk) => {
|
|
35
|
+
options.onChunk?.(chunk);
|
|
36
|
+
},
|
|
37
|
+
onFinish: (message) => {
|
|
38
|
+
options.onFinish?.(message);
|
|
39
|
+
},
|
|
40
|
+
onError: (err) => {
|
|
41
|
+
options.onError?.(err);
|
|
42
|
+
},
|
|
43
|
+
tools: options.tools,
|
|
44
|
+
onCustomEvent: (eventType, data, context) => options.onCustomEvent?.(eventType, data, context),
|
|
45
|
+
...options.streamProcessor !== void 0 && { streamProcessor: options.streamProcessor },
|
|
46
|
+
onMessagesChange: (newMessages) => {
|
|
47
|
+
setMessages(newMessages);
|
|
48
|
+
},
|
|
49
|
+
onLoadingChange: (newIsLoading) => {
|
|
50
|
+
setIsLoading(newIsLoading);
|
|
51
|
+
},
|
|
52
|
+
onStatusChange: (newStatus) => {
|
|
53
|
+
setStatus(newStatus);
|
|
54
|
+
},
|
|
55
|
+
onErrorChange: (newError) => {
|
|
56
|
+
setError(newError);
|
|
57
|
+
},
|
|
58
|
+
onSubscriptionChange: (nextIsSubscribed) => {
|
|
59
|
+
setIsSubscribed(nextIsSubscribed);
|
|
60
|
+
},
|
|
61
|
+
onConnectionStatusChange: (nextStatus) => {
|
|
62
|
+
setConnectionStatus(nextStatus);
|
|
63
|
+
},
|
|
64
|
+
onSessionGeneratingChange: (isGenerating) => {
|
|
65
|
+
setSessionGenerating(isGenerating);
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
}, [clientId]);
|
|
69
|
+
setMessages(client().getMessages());
|
|
70
|
+
createEffect(() => {
|
|
71
|
+
client().updateOptions({
|
|
72
|
+
...options.body !== void 0 && { body: options.body },
|
|
73
|
+
...options.forwardedProps !== void 0 && { forwardedProps: options.forwardedProps },
|
|
74
|
+
context: options.context
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
if (options.live) client().subscribe();
|
|
78
|
+
else client().unsubscribe();
|
|
79
|
+
createEffect(() => {
|
|
80
|
+
if (options.live) client().subscribe();
|
|
81
|
+
else client().unsubscribe();
|
|
82
|
+
});
|
|
83
|
+
onMount(() => {
|
|
84
|
+
client().mountDevtools();
|
|
85
|
+
});
|
|
86
|
+
onCleanup(() => {
|
|
87
|
+
if (options.live) client().unsubscribe();
|
|
88
|
+
else client().stop();
|
|
89
|
+
client().dispose();
|
|
90
|
+
});
|
|
91
|
+
const sendMessage = async (content) => {
|
|
92
|
+
await client().sendMessage(content);
|
|
93
|
+
};
|
|
94
|
+
const append = async (message) => {
|
|
95
|
+
await client().append(message);
|
|
96
|
+
};
|
|
97
|
+
const reload = async () => {
|
|
98
|
+
await client().reload();
|
|
99
|
+
};
|
|
100
|
+
const stop = () => {
|
|
101
|
+
client().stop();
|
|
102
|
+
};
|
|
103
|
+
const clear = () => {
|
|
104
|
+
client().clear();
|
|
105
|
+
};
|
|
106
|
+
const setMessagesManually = (newMessages) => {
|
|
107
|
+
client().setMessagesManually(newMessages);
|
|
108
|
+
};
|
|
109
|
+
const addToolResult = async (result) => {
|
|
110
|
+
await client().addToolResult(result);
|
|
111
|
+
};
|
|
112
|
+
const addToolApprovalResponse = async (response) => {
|
|
113
|
+
await client().addToolApprovalResponse(response);
|
|
114
|
+
};
|
|
115
|
+
const activeStructuredPart = createMemo(() => {
|
|
116
|
+
const list = messages();
|
|
117
|
+
let lastUserIndex = -1;
|
|
118
|
+
for (let i = list.length - 1; i >= 0; i--) if (list[i]?.role === "user") {
|
|
119
|
+
lastUserIndex = i;
|
|
120
|
+
break;
|
|
121
|
+
}
|
|
122
|
+
if (lastUserIndex === -1) return null;
|
|
123
|
+
for (let i = list.length - 1; i > lastUserIndex; i--) {
|
|
124
|
+
const m = list[i];
|
|
125
|
+
if (m?.role !== "assistant") continue;
|
|
126
|
+
const part = m.parts.find((p) => p.type === "structured-output");
|
|
127
|
+
if (part) return part;
|
|
128
|
+
}
|
|
129
|
+
return null;
|
|
130
|
+
});
|
|
131
|
+
return {
|
|
132
|
+
messages,
|
|
133
|
+
sendMessage,
|
|
134
|
+
append,
|
|
135
|
+
reload,
|
|
136
|
+
stop,
|
|
137
|
+
isLoading,
|
|
138
|
+
error,
|
|
139
|
+
status,
|
|
140
|
+
isSubscribed,
|
|
141
|
+
connectionStatus,
|
|
142
|
+
sessionGenerating,
|
|
143
|
+
setMessages: setMessagesManually,
|
|
144
|
+
clear,
|
|
145
|
+
addToolResult,
|
|
146
|
+
addToolApprovalResponse,
|
|
147
|
+
partial: createMemo(() => {
|
|
148
|
+
const part = activeStructuredPart();
|
|
149
|
+
if (!part) return {};
|
|
150
|
+
return part.partial ?? part.data ?? {};
|
|
151
|
+
}),
|
|
152
|
+
final: createMemo(() => {
|
|
153
|
+
const part = activeStructuredPart();
|
|
154
|
+
if (!part || part.status !== "complete") return null;
|
|
155
|
+
return part.data;
|
|
156
|
+
})
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
//#endregion
|
|
161
|
+
export { useChat };
|
|
162
|
+
//# sourceMappingURL=use-chat.js.map
|
|
@@ -0,0 +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 onMount,\n} from 'solid-js'\n\nimport { ChatClient } from '@tanstack/ai-client'\nimport { createChatDevtoolsBridge } from '@tanstack/ai-client/devtools'\nimport type {\n ChatClientState,\n ConnectionStatus,\n InferredClientContext,\n StructuredOutputPart,\n} from '@tanstack/ai-client'\nimport type {\n AnyClientTool,\n InferSchemaType,\n ModelMessage,\n SchemaInput,\n StreamChunk,\n} from '@tanstack/ai'\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 TContext = InferredClientContext<TTools>,\n>(\n options: UseChatOptions<TTools, TSchema, TContext> = {} as UseChatOptions<\n TTools,\n TSchema,\n TContext\n >,\n): UseChatReturn<TTools, TSchema> {\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 // Structured-output `partial` / `final` are derived from `messages` —\n // specifically from the structured-output part on the latest assistant\n // message (the one after the most recent user message). Per-turn parts\n // keep history coherent without a separate reset signal.\n type Partial = DeepPartial<InferSchemaType<NonNullable<TSchema>>>\n type Final = InferSchemaType<NonNullable<TSchema>>\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 // 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 transport = options.connection\n ? { connection: options.connection }\n : { fetcher: options.fetcher }\n return new ChatClient<TTools, TContext>({\n devtoolsBridgeFactory: createChatDevtoolsBridge,\n ...transport,\n id: clientId,\n ...(options.initialMessages !== undefined && {\n initialMessages: options.initialMessages,\n }),\n ...(options.persistence !== undefined && {\n persistence: options.persistence,\n }),\n body: options.body,\n ...(options.threadId !== undefined && { threadId: options.threadId }),\n ...(options.forwardedProps !== undefined && {\n forwardedProps: options.forwardedProps,\n }),\n ...(options.context !== undefined && { context: options.context }),\n devtools: {\n ...options.devtools,\n framework: 'solid',\n hookName: 'useChat',\n outputKind: options.outputSchema ? 'structured' : 'chat',\n },\n onResponse: (response) => options.onResponse?.(response),\n onChunk: (chunk: StreamChunk) => {\n options.onChunk?.(chunk)\n },\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 ...(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 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 setMessages(client().getMessages())\n\n // Sync body / forwardedProps changes to the client.\n // Both populate the same wire payload; `forwardedProps` is preferred\n // and `body` is deprecated but still supported.\n createEffect(() => {\n // Conditional spread: `updateOptions` declares strict-optional\n // fields and rejects explicit `undefined` under EOPT.\n client().updateOptions({\n ...(options.body !== undefined && { body: options.body }),\n ...(options.forwardedProps !== undefined && {\n forwardedProps: options.forwardedProps,\n }),\n context: options.context,\n })\n })\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 onMount(() => {\n client().mountDevtools()\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 client().dispose()\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 // The \"active\" structured-output part is on the assistant message after\n // the latest user message. When no user message exists yet, return null\n // rather than scanning history — otherwise a stale `final` from\n // `initialMessages` would leak into the value on first render.\n const activeStructuredPart = createMemo<StructuredOutputPart | null>(() => {\n const list = messages()\n let lastUserIndex = -1\n for (let i = list.length - 1; i >= 0; i--) {\n if (list[i]?.role === 'user') {\n lastUserIndex = i\n break\n }\n }\n if (lastUserIndex === -1) return null\n for (let i = list.length - 1; i > lastUserIndex; i--) {\n const m = list[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 })\n\n const partial = createMemo<Partial>(() => {\n const part = activeStructuredPart()\n if (!part) return {} as Partial\n const v = part.partial ?? part.data\n return (v ?? {}) as Partial\n })\n\n const final = createMemo<Final | null>(() => {\n const part = activeStructuredPart()\n if (!part || part.status !== 'complete') return null\n return part.data as Final\n })\n\n // partial / final are runtime-tracked unconditionally; the conditional\n // return type hides them when no `outputSchema` is supplied.\n // eslint-disable-next-line no-restricted-syntax -- primitive return shape diverges from generic UseChatReturn<TTools, TSchema>; TS can't structurally narrow the conditional partial/final fields\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"],"mappings":"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"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { Accessor } from "solid-js";
|
|
2
|
+
import { AIDevtoolsDisplayOptions, AudioGenerateInput, ConnectConnectionAdapter, GenerationClientState, GenerationFetcher, InferGenerationOutputFromReturn } 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
|
+
/** Display options for TanStack AI Devtools. */
|
|
22
|
+
devtools?: AIDevtoolsDisplayOptions;
|
|
23
|
+
/**
|
|
24
|
+
* Callback when audio is generated. Can optionally return a transformed value.
|
|
25
|
+
*
|
|
26
|
+
* - Return a non-null value to transform and store it as the result
|
|
27
|
+
* - Return `null` to keep the previous result unchanged
|
|
28
|
+
* - Return nothing (`void`) to store the raw result as-is
|
|
29
|
+
*/
|
|
30
|
+
onResult?: (result: AudioGenerationResult) => TOutput | null | void;
|
|
31
|
+
/** Callback when an error occurs */
|
|
32
|
+
onError?: (error: Error) => void;
|
|
33
|
+
/** Callback when progress is reported (0-100) */
|
|
34
|
+
onProgress?: (progress: number, message?: string) => void;
|
|
35
|
+
/** Callback for each stream chunk (connect-based adapter mode only) */
|
|
36
|
+
onChunk?: (chunk: StreamChunk) => void;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Return type for the useGenerateAudio hook.
|
|
40
|
+
*
|
|
41
|
+
* @template TOutput - The transformed output type (defaults to AudioGenerationResult)
|
|
42
|
+
*/
|
|
43
|
+
interface UseGenerateAudioReturn<TOutput = AudioGenerationResult> {
|
|
44
|
+
/** Trigger audio generation */
|
|
45
|
+
generate: (input: AudioGenerateInput) => Promise<void>;
|
|
46
|
+
/** The generation result containing audio, or null */
|
|
47
|
+
result: Accessor<TOutput | null>;
|
|
48
|
+
/** Whether generation is in progress */
|
|
49
|
+
isLoading: Accessor<boolean>;
|
|
50
|
+
/** Current error, if any */
|
|
51
|
+
error: Accessor<Error | undefined>;
|
|
52
|
+
/** Current state of the generation */
|
|
53
|
+
status: Accessor<GenerationClientState>;
|
|
54
|
+
/** Abort the current generation */
|
|
55
|
+
stop: () => void;
|
|
56
|
+
/** Clear result, error, and return to idle */
|
|
57
|
+
reset: () => void;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Solid hook for generating audio (music, sound effects) using AI models.
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```tsx
|
|
64
|
+
* import { useGenerateAudio } from '@tanstack/ai-solid'
|
|
65
|
+
* import { fetchServerSentEvents } from '@tanstack/ai-client'
|
|
66
|
+
*
|
|
67
|
+
* function AudioGenerator() {
|
|
68
|
+
* const { generate, result, isLoading } = useGenerateAudio({
|
|
69
|
+
* connection: fetchServerSentEvents('/api/generate/audio'),
|
|
70
|
+
* })
|
|
71
|
+
*
|
|
72
|
+
* return (
|
|
73
|
+
* <div>
|
|
74
|
+
* <button onClick={() => generate({ prompt: 'An upbeat electronic track', duration: 10 })}>
|
|
75
|
+
* Generate
|
|
76
|
+
* </button>
|
|
77
|
+
* {result()?.audio.url && <audio src={result()!.audio.url} controls />}
|
|
78
|
+
* </div>
|
|
79
|
+
* )
|
|
80
|
+
* }
|
|
81
|
+
* ```
|
|
82
|
+
*/
|
|
83
|
+
declare function useGenerateAudio<TTransformed = void>(options: Omit<UseGenerateAudioOptions, 'onResult'> & {
|
|
84
|
+
onResult?: (result: AudioGenerationResult) => TTransformed;
|
|
85
|
+
}): UseGenerateAudioReturn<InferGenerationOutputFromReturn<AudioGenerationResult, TTransformed>>;
|
|
86
|
+
//#endregion
|
|
87
|
+
export { UseGenerateAudioOptions, UseGenerateAudioReturn, useGenerateAudio };
|
|
88
|
+
//# sourceMappingURL=use-generate-audio.d.ts.map
|
|
@@ -0,0 +1,52 @@
|
|
|
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 devtools = {
|
|
30
|
+
...options.devtools,
|
|
31
|
+
framework: "solid",
|
|
32
|
+
hookName: "useGenerateAudio",
|
|
33
|
+
outputKind: "audio"
|
|
34
|
+
};
|
|
35
|
+
const { generate, result, isLoading, error, status, stop, reset } = useGeneration({
|
|
36
|
+
...options,
|
|
37
|
+
devtools
|
|
38
|
+
});
|
|
39
|
+
return {
|
|
40
|
+
generate,
|
|
41
|
+
result,
|
|
42
|
+
isLoading,
|
|
43
|
+
error,
|
|
44
|
+
status,
|
|
45
|
+
stop,
|
|
46
|
+
reset
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
//#endregion
|
|
51
|
+
export { useGenerateAudio };
|
|
52
|
+
//# 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 AIDevtoolsDisplayOptions,\n AudioGenerateInput,\n ConnectConnectionAdapter,\n GenerationClientState,\n GenerationFetcher,\n InferGenerationOutputFromReturn,\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 /** 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 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<TTransformed = void>(\n options: Omit<UseGenerateAudioOptions, 'onResult'> & {\n onResult?: (result: AudioGenerationResult) => TTransformed\n },\n): UseGenerateAudioReturn<\n InferGenerationOutputFromReturn<AudioGenerationResult, TTransformed>\n> {\n const devtools = {\n ...options.devtools,\n framework: 'solid',\n hookName: 'useGenerateAudio',\n outputKind: 'audio' as const,\n }\n const { generate, result, isLoading, error, status, stop, reset } =\n useGeneration<AudioGenerateInput, AudioGenerationResult, TTransformed>({\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"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AA0FA,SAAgB,iBACd,SAKA;CACA,MAAM,WAAW;EACf,GAAG,QAAQ;EACX,WAAW;EACX,UAAU;EACV,YAAY;EACb;CACD,MAAM,EAAE,UAAU,QAAQ,WAAW,OAAO,QAAQ,MAAM,UACxD,cAAuE;EACrE,GAAG;EACH;EACD,CAAC;AAEJ,QAAO;EACK;EACV;EACA;EACA;EACA;EACA;EACA;EACD"}
|