@tanstack/ai-solid 0.15.1 → 0.16.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/types.d.ts +29 -3
- package/dist/use-chat.js +81 -5
- package/dist/use-chat.js.map +1 -1
- package/dist/use-generate-audio.d.ts +9 -10
- package/dist/use-generate-audio.js +4 -11
- package/dist/use-generate-audio.js.map +1 -1
- package/dist/use-generate-image.d.ts +9 -10
- package/dist/use-generate-image.js +4 -11
- package/dist/use-generate-image.js.map +1 -1
- package/dist/use-generate-speech.d.ts +9 -10
- package/dist/use-generate-speech.js +4 -11
- package/dist/use-generate-speech.js.map +1 -1
- package/dist/use-generate-video.d.ts +50 -4
- package/dist/use-generate-video.js +54 -23
- package/dist/use-generate-video.js.map +1 -1
- package/dist/use-generation.d.ts +59 -6
- package/dist/use-generation.js +43 -19
- package/dist/use-generation.js.map +1 -1
- package/dist/use-summarize.d.ts +9 -10
- package/dist/use-summarize.js +4 -11
- package/dist/use-summarize.js.map +1 -1
- package/dist/use-transcription.d.ts +9 -10
- package/dist/use-transcription.js +4 -11
- package/dist/use-transcription.js.map +1 -1
- package/package.json +6 -6
- package/src/index.ts +10 -0
- package/src/types.ts +44 -0
- package/src/use-chat.ts +123 -6
- package/src/use-generate-audio.ts +33 -23
- package/src/use-generate-image.ts +33 -23
- package/src/use-generate-speech.ts +32 -23
- package/src/use-generate-video.ts +120 -25
- package/src/use-generation.ts +129 -25
- package/src/use-summarize.ts +34 -23
- package/src/use-transcription.ts +33 -24
package/README.md
CHANGED
|
@@ -1,3 +1,20 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
<picture>
|
|
3
|
+
<source
|
|
4
|
+
media="(prefers-color-scheme: dark)"
|
|
5
|
+
srcset="https://tanstack.com/api/readme/ai.png?framework=solid&theme=dark"
|
|
6
|
+
/>
|
|
7
|
+
<source
|
|
8
|
+
media="(prefers-color-scheme: light)"
|
|
9
|
+
srcset="https://tanstack.com/api/readme/ai.png?framework=solid"
|
|
10
|
+
/>
|
|
11
|
+
<img
|
|
12
|
+
src="https://tanstack.com/api/readme/ai.png?framework=solid"
|
|
13
|
+
alt="TanStack Solid AI"
|
|
14
|
+
width="900"
|
|
15
|
+
/>
|
|
16
|
+
</picture>
|
|
17
|
+
</div>
|
|
1
18
|
# @tanstack/ai-react
|
|
2
19
|
|
|
3
20
|
React hooks for building AI chat interfaces with TanStack AI.
|
package/dist/index.d.ts
CHANGED
|
@@ -8,5 +8,5 @@ import { UseTranscriptionOptions, UseTranscriptionReturn, useTranscription } fro
|
|
|
8
8
|
import { UseSummarizeOptions, UseSummarizeReturn, useSummarize } from "./use-summarize.js";
|
|
9
9
|
import { UseGenerateVideoOptions, UseGenerateVideoReturn, useGenerateVideo } from "./use-generate-video.js";
|
|
10
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 QueueConfig, type QueueOption, type QueueStrategy, type QueuedMessage, type RunAgentInputContext, type SendMessageOptions, 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 WhenBusy, type XhrConnectionOptions, createChatClientOptions, fetchHttpStream, fetchServerSentEvents, rpcStream, stream, useAudioRecorder, useChat, useGenerateAudio, useGenerateImage, useGenerateSpeech, useGenerateVideo, useGeneration, useSummarize, useTranscription, xhrHttpStream, xhrServerSentEvents };
|
|
11
|
+
import { AudioGenerateInput, ChatClientPersistence, ChatPersistedState, ChatPersistenceOption, ChatStorageAdapter, ConnectConnectionAdapter, ConnectionAdapter, FetchConnectionOptions, GenerationClientState, ImageGenerateInput, IndexedDBPersistenceOptions, InferChatMessages, RunAgentInputContext, SpeechGenerateInput, StorageUnavailableError, SubscribeConnectionAdapter, SummarizeGenerateInput, TranscriptionGenerateInput, VideoGenerateInput, VideoGenerateResult, VideoStatusInfo, WebStoragePersistenceOptions, XhrConnectionOptions, createChatClientOptions, fetchHttpStream, fetchServerSentEvents, indexedDBPersistence, localStoragePersistence, rpcStream, sessionStoragePersistence, stream, xhrHttpStream, xhrServerSentEvents } from "@tanstack/ai-client";
|
|
12
|
+
export { type AudioGenerateInput, type ChatClientPersistence, type ChatPersistedState, type ChatPersistenceOption, type ChatRequestBody, type ChatStorageAdapter, type ConnectConnectionAdapter, type ConnectionAdapter, type DeepPartial, type FetchConnectionOptions, type GenerationClientState, type ImageGenerateInput, type IndexedDBPersistenceOptions, type InferChatMessages, type QueueConfig, type QueueOption, type QueueStrategy, type QueuedMessage, type RunAgentInputContext, type SendMessageOptions, type SpeechGenerateInput, StorageUnavailableError, 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 WebStoragePersistenceOptions, type WhenBusy, type XhrConnectionOptions, createChatClientOptions, fetchHttpStream, fetchServerSentEvents, indexedDBPersistence, localStoragePersistence, rpcStream, sessionStoragePersistence, stream, useAudioRecorder, useChat, useGenerateAudio, useGenerateImage, useGenerateSpeech, useGenerateVideo, useGeneration, useSummarize, useTranscription, xhrHttpStream, xhrServerSentEvents };
|
package/dist/index.js
CHANGED
|
@@ -7,6 +7,6 @@ import { useTranscription } from "./use-transcription.js";
|
|
|
7
7
|
import { useSummarize } from "./use-summarize.js";
|
|
8
8
|
import { useGenerateVideo } from "./use-generate-video.js";
|
|
9
9
|
import { useAudioRecorder } from "./use-audio-recorder.js";
|
|
10
|
-
import { createChatClientOptions, fetchHttpStream, fetchServerSentEvents, rpcStream, stream, xhrHttpStream, xhrServerSentEvents } from "@tanstack/ai-client";
|
|
10
|
+
import { StorageUnavailableError, createChatClientOptions, fetchHttpStream, fetchServerSentEvents, indexedDBPersistence, localStoragePersistence, rpcStream, sessionStoragePersistence, stream, xhrHttpStream, xhrServerSentEvents } from "@tanstack/ai-client";
|
|
11
11
|
|
|
12
|
-
export { createChatClientOptions, fetchHttpStream, fetchServerSentEvents, rpcStream, stream, useAudioRecorder, useChat, useGenerateAudio, useGenerateImage, useGenerateSpeech, useGenerateVideo, useGeneration, useSummarize, useTranscription, xhrHttpStream, xhrServerSentEvents };
|
|
12
|
+
export { StorageUnavailableError, createChatClientOptions, fetchHttpStream, fetchServerSentEvents, indexedDBPersistence, localStoragePersistence, rpcStream, sessionStoragePersistence, stream, useAudioRecorder, useChat, useGenerateAudio, useGenerateImage, useGenerateSpeech, useGenerateVideo, useGeneration, useSummarize, useTranscription, xhrHttpStream, xhrServerSentEvents };
|
package/dist/types.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Accessor } from "solid-js";
|
|
2
|
-
import { AIDevtoolsDisplayOptions, ChatClientOptions, ChatClientState, ChatRequestBody, ClientContextOptionFromTools, ConnectionStatus, DistributedOmit, InferredClientContext, MultimodalContent, QueueConfig, QueueOption, QueueStrategy, QueuedMessage, SendMessageOptions, UIMessage, WhenBusy } from "@tanstack/ai-client";
|
|
3
|
-
import { AnyClientTool, InferSchemaType, ModelMessage, SchemaInput } from "@tanstack/ai";
|
|
2
|
+
import { AIDevtoolsDisplayOptions, BoundInterrupts, ChatClientOptions, ChatClientState, ChatInterrupt, ChatInterruptState, ChatRequestBody, ChatResumeState, ClientContextOptionFromTools, ConnectionStatus, DistributedOmit, InferredClientContext, MultimodalContent, QueueConfig, QueueOption, QueueStrategy, QueuedMessage, SendMessageOptions, UIMessage, WhenBusy } from "@tanstack/ai-client";
|
|
3
|
+
import { AnyClientTool, InferSchemaType, ModelMessage, RunAgentResumeItem, SchemaInput } from "@tanstack/ai";
|
|
4
4
|
|
|
5
5
|
//#region src/types.d.ts
|
|
6
6
|
|
|
@@ -31,7 +31,7 @@ type DeepPartial<T> = T extends ReadonlyArray<infer U> ? Array<DeepPartial<U>> :
|
|
|
31
31
|
* Note: Connection and body changes will recreate the ChatClient instance.
|
|
32
32
|
* To update these options, remount the component or use a key prop.
|
|
33
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' | 'onQueueChange' | 'context' | 'devtools'> & {
|
|
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' | 'onQueueChange' | 'onResumeStateChange' | 'onRunIdChange' | 'context' | 'devtools' | 'id'> & {
|
|
35
35
|
/** Display options for TanStack AI Devtools. */
|
|
36
36
|
devtools?: AIDevtoolsDisplayOptions;
|
|
37
37
|
live?: boolean;
|
|
@@ -102,6 +102,32 @@ interface BaseUseChatReturn<TTools extends ReadonlyArray<AnyClientTool> = any, T
|
|
|
102
102
|
id: string;
|
|
103
103
|
approved: boolean;
|
|
104
104
|
}) => Promise<void>;
|
|
105
|
+
/**
|
|
106
|
+
* The id of the run this client has in flight — one it started or rejoined —
|
|
107
|
+
* or `null` when there is none (including while a run sits paused on an
|
|
108
|
+
* interrupt, waiting on approval).
|
|
109
|
+
*
|
|
110
|
+
* A run is one turn of the conversation, so this changes from turn to turn. A
|
|
111
|
+
* whole tool loop stays inside one run, while resuming after an interrupt
|
|
112
|
+
* continues the turn under a new id — so one user message can produce several
|
|
113
|
+
* run ids. Use it to talk to your own server about that run (cancel it, poll
|
|
114
|
+
* it, correlate a log line).
|
|
115
|
+
*/
|
|
116
|
+
runId: Accessor<string | null>;
|
|
117
|
+
interrupts: Accessor<BoundInterrupts<TTools>>;
|
|
118
|
+
/** @deprecated Use `interrupts`. */
|
|
119
|
+
pendingInterrupts: Accessor<BoundInterrupts<TTools>>;
|
|
120
|
+
interruptErrors: Accessor<ChatInterruptState<TTools>['interruptErrors']>;
|
|
121
|
+
resuming: Accessor<boolean>;
|
|
122
|
+
resolveInterrupts: {
|
|
123
|
+
(approved: boolean): void;
|
|
124
|
+
(resolver: (interrupt: ChatInterrupt<TTools>) => undefined): void;
|
|
125
|
+
};
|
|
126
|
+
cancelInterrupts: () => void;
|
|
127
|
+
retryInterrupts: () => void;
|
|
128
|
+
resumeInterruptsUnsafe: (resume: Array<RunAgentResumeItem>, state?: ChatResumeState) => Promise<boolean>;
|
|
129
|
+
/** @deprecated Use bound interrupt methods or `resumeInterruptsUnsafe`. */
|
|
130
|
+
resumeInterrupts: (resume: Array<RunAgentResumeItem>, state?: ChatResumeState) => Promise<boolean>;
|
|
105
131
|
/**
|
|
106
132
|
* Reload the last assistant message
|
|
107
133
|
*/
|
package/dist/use-chat.js
CHANGED
|
@@ -3,9 +3,11 @@ import { ChatClient } from "@tanstack/ai-client";
|
|
|
3
3
|
import { createChatDevtoolsBridge } from "@tanstack/ai-client/devtools";
|
|
4
4
|
|
|
5
5
|
//#region src/use-chat.ts
|
|
6
|
+
const EMPTY_INTERRUPTS = Object.freeze([]);
|
|
7
|
+
const EMPTY_INTERRUPT_ERRORS = Object.freeze([]);
|
|
6
8
|
function useChat(options = {}) {
|
|
7
9
|
const hookId = createUniqueId();
|
|
8
|
-
const clientId = options.
|
|
10
|
+
const clientId = options.threadId ?? hookId;
|
|
9
11
|
const [messages, setMessages] = createSignal(options.initialMessages || []);
|
|
10
12
|
const [isLoading, setIsLoading] = createSignal(false);
|
|
11
13
|
const [error, setError] = createSignal(void 0);
|
|
@@ -14,13 +16,24 @@ function useChat(options = {}) {
|
|
|
14
16
|
const [connectionStatus, setConnectionStatus] = createSignal("disconnected");
|
|
15
17
|
const [sessionGenerating, setSessionGenerating] = createSignal(false);
|
|
16
18
|
const [queue, setQueue] = createSignal([]);
|
|
19
|
+
const [runId, setRunId] = createSignal(null);
|
|
20
|
+
const [interruptState, setInterruptState] = createSignal({
|
|
21
|
+
interrupts: EMPTY_INTERRUPTS,
|
|
22
|
+
pendingInterrupts: EMPTY_INTERRUPTS,
|
|
23
|
+
interruptErrors: EMPTY_INTERRUPT_ERRORS,
|
|
24
|
+
resuming: false
|
|
25
|
+
});
|
|
26
|
+
const syncResumeState = () => {
|
|
27
|
+
setRunId(client().getCurrentRunId());
|
|
28
|
+
setInterruptState(client().getInterruptState());
|
|
29
|
+
};
|
|
17
30
|
const client = createMemo(() => {
|
|
18
31
|
return new ChatClient({
|
|
19
32
|
devtoolsBridgeFactory: createChatDevtoolsBridge,
|
|
20
33
|
...options.connection ? { connection: options.connection } : { fetcher: options.fetcher },
|
|
21
|
-
id: clientId,
|
|
22
34
|
...options.initialMessages !== void 0 && { initialMessages: options.initialMessages },
|
|
23
35
|
...options.persistence !== void 0 && { persistence: options.persistence },
|
|
36
|
+
...options.initialResumeSnapshot !== void 0 && { initialResumeSnapshot: options.initialResumeSnapshot },
|
|
24
37
|
body: options.body,
|
|
25
38
|
...options.threadId !== void 0 && { threadId: options.threadId },
|
|
26
39
|
...options.forwardedProps !== void 0 && { forwardedProps: options.forwardedProps },
|
|
@@ -49,6 +62,7 @@ function useChat(options = {}) {
|
|
|
49
62
|
},
|
|
50
63
|
onLoadingChange: (newIsLoading) => {
|
|
51
64
|
setIsLoading(newIsLoading);
|
|
65
|
+
syncResumeState();
|
|
52
66
|
},
|
|
53
67
|
onStatusChange: (newStatus) => {
|
|
54
68
|
setStatus(newStatus);
|
|
@@ -68,10 +82,25 @@ function useChat(options = {}) {
|
|
|
68
82
|
...options.queue !== void 0 && { queue: options.queue },
|
|
69
83
|
onQueueChange: (nextQueue) => {
|
|
70
84
|
setQueue(nextQueue);
|
|
85
|
+
},
|
|
86
|
+
onRunIdChange: (nextRunId) => {
|
|
87
|
+
setRunId(nextRunId);
|
|
88
|
+
},
|
|
89
|
+
onResumeStateChange: (_nextResumeState, nextPendingInterrupts) => {
|
|
90
|
+
setInterruptState((current) => ({
|
|
91
|
+
...current,
|
|
92
|
+
interrupts: nextPendingInterrupts,
|
|
93
|
+
pendingInterrupts: nextPendingInterrupts
|
|
94
|
+
}));
|
|
95
|
+
},
|
|
96
|
+
onInterruptStateChange: (nextInterruptState) => {
|
|
97
|
+
setInterruptState(nextInterruptState);
|
|
98
|
+
options.onInterruptStateChange?.(nextInterruptState);
|
|
71
99
|
}
|
|
72
100
|
});
|
|
73
101
|
}, [clientId]);
|
|
74
102
|
setMessages(client().getMessages());
|
|
103
|
+
syncResumeState();
|
|
75
104
|
createEffect(() => {
|
|
76
105
|
client().updateOptions({
|
|
77
106
|
...options.body !== void 0 && { body: options.body },
|
|
@@ -87,27 +116,43 @@ function useChat(options = {}) {
|
|
|
87
116
|
else client().unsubscribe();
|
|
88
117
|
});
|
|
89
118
|
onMount(() => {
|
|
119
|
+
client().attach();
|
|
90
120
|
client().mountDevtools();
|
|
121
|
+
syncResumeState();
|
|
91
122
|
});
|
|
92
123
|
onCleanup(() => {
|
|
124
|
+
client().detach();
|
|
93
125
|
if (options.live) client().unsubscribe();
|
|
94
126
|
else client().stop();
|
|
95
127
|
client().dispose();
|
|
96
128
|
});
|
|
97
129
|
const sendMessage = async (content, sendOptions) => {
|
|
98
|
-
|
|
130
|
+
try {
|
|
131
|
+
await client().sendMessage(content, void 0, sendOptions);
|
|
132
|
+
} finally {
|
|
133
|
+
syncResumeState();
|
|
134
|
+
}
|
|
99
135
|
};
|
|
100
136
|
const append = async (message) => {
|
|
101
|
-
|
|
137
|
+
try {
|
|
138
|
+
await client().append(message);
|
|
139
|
+
} finally {
|
|
140
|
+
syncResumeState();
|
|
141
|
+
}
|
|
102
142
|
};
|
|
103
143
|
const reload = async () => {
|
|
104
|
-
|
|
144
|
+
try {
|
|
145
|
+
await client().reload();
|
|
146
|
+
} finally {
|
|
147
|
+
syncResumeState();
|
|
148
|
+
}
|
|
105
149
|
};
|
|
106
150
|
const stop = () => {
|
|
107
151
|
client().stop();
|
|
108
152
|
};
|
|
109
153
|
const clear = () => {
|
|
110
154
|
client().clear();
|
|
155
|
+
syncResumeState();
|
|
111
156
|
};
|
|
112
157
|
const setMessagesManually = (newMessages) => {
|
|
113
158
|
client().setMessagesManually(newMessages);
|
|
@@ -117,8 +162,29 @@ function useChat(options = {}) {
|
|
|
117
162
|
};
|
|
118
163
|
const addToolApprovalResponse = async (response) => {
|
|
119
164
|
await client().addToolApprovalResponse(response);
|
|
165
|
+
syncResumeState();
|
|
120
166
|
};
|
|
121
167
|
const cancelQueued = (id) => client().cancelQueued(id);
|
|
168
|
+
const resumeInterrupts = async (resumeItems, state) => {
|
|
169
|
+
const result = await client().resumeInterrupts(resumeItems, state);
|
|
170
|
+
syncResumeState();
|
|
171
|
+
return result;
|
|
172
|
+
};
|
|
173
|
+
const resolveInterrupts = (resolution) => {
|
|
174
|
+
if (typeof resolution === "boolean") client().resolveInterrupts(resolution);
|
|
175
|
+
else client().resolveInterrupts(resolution);
|
|
176
|
+
};
|
|
177
|
+
const cancelInterrupts = () => {
|
|
178
|
+
client().cancelInterrupts();
|
|
179
|
+
};
|
|
180
|
+
const retryInterrupts = () => {
|
|
181
|
+
client().retryInterrupts();
|
|
182
|
+
};
|
|
183
|
+
const resumeInterruptsUnsafe = (resumeItems, state) => client().resumeInterruptsUnsafe(resumeItems, state);
|
|
184
|
+
const interrupts = () => interruptState().interrupts;
|
|
185
|
+
const pendingInterrupts = () => interruptState().pendingInterrupts;
|
|
186
|
+
const interruptErrors = () => interruptState().interruptErrors;
|
|
187
|
+
const resuming = () => interruptState().resuming;
|
|
122
188
|
const activeStructuredPart = createMemo(() => {
|
|
123
189
|
const list = messages();
|
|
124
190
|
let lastUserIndex = -1;
|
|
@@ -153,6 +219,16 @@ function useChat(options = {}) {
|
|
|
153
219
|
addToolResult,
|
|
154
220
|
addToolApprovalResponse,
|
|
155
221
|
cancelQueued,
|
|
222
|
+
runId,
|
|
223
|
+
interrupts,
|
|
224
|
+
pendingInterrupts,
|
|
225
|
+
interruptErrors,
|
|
226
|
+
resuming,
|
|
227
|
+
resolveInterrupts,
|
|
228
|
+
cancelInterrupts,
|
|
229
|
+
retryInterrupts,
|
|
230
|
+
resumeInterruptsUnsafe,
|
|
231
|
+
resumeInterrupts,
|
|
156
232
|
partial: createMemo(() => {
|
|
157
233
|
const part = activeStructuredPart();
|
|
158
234
|
if (!part) return {};
|
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 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 QueuedMessage,\n SendMessageOptions,\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 const 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 const [queue, setQueue] = createSignal<Array<QueuedMessage>>([])\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 ...(options.queue !== undefined && { queue: options.queue }),\n onQueueChange: (nextQueue: Array<QueuedMessage>) => {\n setQueue(nextQueue)\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 ...(options.queue !== undefined && { queue: options.queue }),\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 (\n content: string | MultimodalContent,\n sendOptions?: SendMessageOptions,\n ) => {\n await client().sendMessage(content, undefined, sendOptions)\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 const cancelQueued = (id: string) => client().cancelQueued(id)\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 queue,\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 cancelQueued,\n partial,\n final,\n } as unknown as UseChatReturn<TTools, TSchema>\n}\n"],"mappings":"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"}
|
|
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 ChatInterrupt,\n ChatInterruptState,\n ChatResumeState,\n ConnectionStatus,\n InferredClientContext,\n QueuedMessage,\n SendMessageOptions,\n StructuredOutputPart,\n} from '@tanstack/ai-client'\nimport type {\n AnyClientTool,\n InferSchemaType,\n ModelMessage,\n RunAgentResumeItem,\n SchemaInput,\n StreamChunk,\n} from '@tanstack/ai'\nimport type {\n DeepPartial,\n MultimodalContent,\n UIMessage,\n UseChatOptions,\n UseChatReturn,\n} from './types'\n\nconst EMPTY_INTERRUPTS = Object.freeze([])\nconst EMPTY_INTERRUPT_ERRORS = Object.freeze([])\n\nexport function useChat<\n const 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 // The hook's identity is its `threadId` — also the persistence key, so a\n // reload with the same `threadId` restores the same conversation. `hookId` is\n // only a stable fallback for client-recreation keying when no `threadId` is\n // given (an ephemeral chat), never a persistence key.\n const hookId = createUniqueId()\n const clientId = options.threadId ?? 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 const [queue, setQueue] = createSignal<Array<QueuedMessage>>([])\n const [runId, setRunId] = createSignal<string | null>(null)\n const [interruptState, setInterruptState] = createSignal<\n ChatInterruptState<TTools>\n >({\n interrupts: EMPTY_INTERRUPTS,\n pendingInterrupts: EMPTY_INTERRUPTS,\n interruptErrors: EMPTY_INTERRUPT_ERRORS,\n resuming: false,\n })\n\n const syncResumeState = () => {\n setRunId(client().getCurrentRunId())\n setInterruptState(client().getInterruptState())\n }\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 ...(options.initialMessages !== undefined && {\n initialMessages: options.initialMessages,\n }),\n ...(options.persistence !== undefined && {\n persistence: options.persistence,\n }),\n ...(options.initialResumeSnapshot !== undefined && {\n initialResumeSnapshot: options.initialResumeSnapshot,\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 syncResumeState()\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 ...(options.queue !== undefined && { queue: options.queue }),\n onQueueChange: (nextQueue: Array<QueuedMessage>) => {\n setQueue(nextQueue)\n },\n onRunIdChange: (nextRunId) => {\n setRunId(nextRunId)\n },\n onResumeStateChange: (_nextResumeState, nextPendingInterrupts) => {\n setInterruptState((current) => ({\n ...current,\n interrupts: nextPendingInterrupts,\n pendingInterrupts: nextPendingInterrupts,\n }))\n },\n onInterruptStateChange: (nextInterruptState) => {\n setInterruptState(nextInterruptState)\n options.onInterruptStateChange?.(nextInterruptState)\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 syncResumeState()\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 ...(options.queue !== undefined && { queue: options.queue }),\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 // START TAILING HERE, not in the constructor. A client is idle until a view\n // attaches it, so a client that gets built and thrown away never opens a\n // connection — an unreachable stream would hold one of the browser's ~6\n // connections per origin until the page reloaded.\n client().attach()\n client().mountDevtools()\n // Delivery-durability resume is transparent: the resumable SSE connection\n // adapter reattaches via the browser's native Last-Event-ID on reconnect.\n // We only seed interrupt (state) resume from the client here.\n syncResumeState()\n })\n\n // Cleanup on unmount: stop any in-flight requests.\n onCleanup(() => {\n // Release the connection first: `detach` is the counterpart of the `attach`\n // above, and it keeps the transcript and resume pointer so a later mount can\n // pick the run back up from the durable log.\n client().detach()\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 (\n content: string | MultimodalContent,\n sendOptions?: SendMessageOptions,\n ) => {\n try {\n await client().sendMessage(content, undefined, sendOptions)\n } finally {\n syncResumeState()\n }\n }\n\n const append = async (message: ModelMessage | UIMessage<TTools>) => {\n try {\n await client().append(message)\n } finally {\n syncResumeState()\n }\n }\n\n const reload = async () => {\n try {\n await client().reload()\n } finally {\n syncResumeState()\n }\n }\n\n const stop = () => {\n client().stop()\n }\n\n const clear = () => {\n client().clear()\n syncResumeState()\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 syncResumeState()\n }\n\n const cancelQueued = (id: string) => client().cancelQueued(id)\n\n const resumeInterrupts = async (\n resumeItems: Array<RunAgentResumeItem>,\n state?: ChatResumeState,\n ) => {\n const result = await client().resumeInterrupts(resumeItems, state)\n syncResumeState()\n return result\n }\n\n const resolveInterrupts = (\n resolution: boolean | ((interrupt: ChatInterrupt<TTools>) => undefined),\n ) => {\n if (typeof resolution === 'boolean') {\n client().resolveInterrupts(resolution)\n } else {\n client().resolveInterrupts(resolution)\n }\n }\n\n const cancelInterrupts = () => {\n client().cancelInterrupts()\n }\n\n const retryInterrupts = () => {\n client().retryInterrupts()\n }\n\n const resumeInterruptsUnsafe = (\n resumeItems: Array<RunAgentResumeItem>,\n state?: ChatResumeState,\n ) => client().resumeInterruptsUnsafe(resumeItems, state)\n\n const interrupts = () => interruptState().interrupts\n const pendingInterrupts = () => interruptState().pendingInterrupts\n const interruptErrors = () => interruptState().interruptErrors\n const resuming = () => interruptState().resuming\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 // oxlint-disable-next-line eslint-js/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 queue,\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 cancelQueued,\n runId,\n interrupts,\n pendingInterrupts,\n interruptErrors,\n resuming,\n resolveInterrupts,\n cancelInterrupts,\n retryInterrupts,\n resumeInterruptsUnsafe,\n resumeInterrupts,\n partial,\n final,\n } as unknown as UseChatReturn<TTools, TSchema>\n}\n"],"mappings":"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"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { UseGenerationOptions, UseGenerationReturn } from "./use-generation.js";
|
|
1
2
|
import { Accessor } from "solid-js";
|
|
2
|
-
import { AIDevtoolsDisplayOptions, AudioGenerateInput, ConnectConnectionAdapter, GenerationClientState, GenerationFetcher, InferGenerationOutputFromReturn } from "@tanstack/ai-client";
|
|
3
|
+
import { AIDevtoolsDisplayOptions, AudioGenerateInput, ConnectConnectionAdapter, GenerationClientState, GenerationFetcher, GenerationPersistenceOptions, InferGenerationOutputFromReturn } from "@tanstack/ai-client";
|
|
3
4
|
import { AudioGenerationResult, StreamChunk } from "@tanstack/ai";
|
|
4
5
|
|
|
5
6
|
//#region src/use-generate-audio.d.ts
|
|
@@ -9,12 +10,14 @@ import { AudioGenerationResult, StreamChunk } from "@tanstack/ai";
|
|
|
9
10
|
*
|
|
10
11
|
* @template TOutput - The transformed output type (defaults to AudioGenerationResult)
|
|
11
12
|
*/
|
|
12
|
-
interface UseGenerateAudioOptions<TOutput = AudioGenerationResult> {
|
|
13
|
+
interface UseGenerateAudioOptions<TOutput = AudioGenerationResult> extends Pick<UseGenerationOptions<AudioGenerateInput, AudioGenerationResult, TOutput>, 'persistence' | 'threadId' | 'hydrateGeneration' | 'joinRun'> {
|
|
13
14
|
/** Connect-based adapter for streaming transport (SSE, HTTP stream, custom) */
|
|
14
15
|
connection?: ConnectConnectionAdapter;
|
|
15
16
|
/** Direct async function for audio generation */
|
|
16
17
|
fetcher?: GenerationFetcher<AudioGenerateInput, AudioGenerationResult>;
|
|
17
|
-
/**
|
|
18
|
+
/**
|
|
19
|
+
* @deprecated Prefer `threadId`. Only allowed when `threadId` is omitted (see `GenerationPersistenceOptions`).
|
|
20
|
+
*/
|
|
18
21
|
id?: string;
|
|
19
22
|
/** Additional body parameters to send with connect-based adapter requests */
|
|
20
23
|
body?: Record<string, any>;
|
|
@@ -40,7 +43,7 @@ interface UseGenerateAudioOptions<TOutput = AudioGenerationResult> {
|
|
|
40
43
|
*
|
|
41
44
|
* @template TOutput - The transformed output type (defaults to AudioGenerationResult)
|
|
42
45
|
*/
|
|
43
|
-
interface UseGenerateAudioReturn<TOutput = AudioGenerationResult> {
|
|
46
|
+
interface UseGenerateAudioReturn<TOutput = AudioGenerationResult> extends Omit<UseGenerationReturn<TOutput>, 'generate'> {
|
|
44
47
|
/** Trigger audio generation */
|
|
45
48
|
generate: (input: AudioGenerateInput) => Promise<void>;
|
|
46
49
|
/** The generation result containing audio, or null */
|
|
@@ -51,10 +54,6 @@ interface UseGenerateAudioReturn<TOutput = AudioGenerationResult> {
|
|
|
51
54
|
error: Accessor<Error | undefined>;
|
|
52
55
|
/** Current state of the generation */
|
|
53
56
|
status: Accessor<GenerationClientState>;
|
|
54
|
-
/** Abort the current generation */
|
|
55
|
-
stop: () => void;
|
|
56
|
-
/** Clear result, error, and return to idle */
|
|
57
|
-
reset: () => void;
|
|
58
57
|
}
|
|
59
58
|
/**
|
|
60
59
|
* Solid hook for generating audio (music, sound effects) using AI models.
|
|
@@ -80,9 +79,9 @@ interface UseGenerateAudioReturn<TOutput = AudioGenerationResult> {
|
|
|
80
79
|
* }
|
|
81
80
|
* ```
|
|
82
81
|
*/
|
|
83
|
-
declare function useGenerateAudio<TTransformed = void>(options: Omit<UseGenerateAudioOptions, 'onResult'> & {
|
|
82
|
+
declare function useGenerateAudio<TTransformed = void>(options: Omit<UseGenerateAudioOptions, 'onResult' | 'persistence' | 'threadId' | 'id'> & {
|
|
84
83
|
onResult?: (result: AudioGenerationResult) => TTransformed;
|
|
85
|
-
}): UseGenerateAudioReturn<InferGenerationOutputFromReturn<AudioGenerationResult, TTransformed>>;
|
|
84
|
+
} & GenerationPersistenceOptions): UseGenerateAudioReturn<InferGenerationOutputFromReturn<AudioGenerationResult, TTransformed>>;
|
|
86
85
|
//#endregion
|
|
87
86
|
export { UseGenerateAudioOptions, UseGenerateAudioReturn, useGenerateAudio };
|
|
88
87
|
//# sourceMappingURL=use-generate-audio.d.ts.map
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { useGeneration } from "./use-generation.js";
|
|
2
|
+
import { reconstructAudioResult } from "@tanstack/ai-client";
|
|
2
3
|
|
|
3
4
|
//#region src/use-generate-audio.ts
|
|
4
5
|
/**
|
|
@@ -32,19 +33,11 @@ function useGenerateAudio(options) {
|
|
|
32
33
|
hookName: "useGenerateAudio",
|
|
33
34
|
outputKind: "audio"
|
|
34
35
|
};
|
|
35
|
-
|
|
36
|
+
return useGeneration({
|
|
36
37
|
...options,
|
|
37
|
-
devtools
|
|
38
|
+
devtools,
|
|
39
|
+
reconstructResult: reconstructAudioResult
|
|
38
40
|
});
|
|
39
|
-
return {
|
|
40
|
-
generate,
|
|
41
|
-
result,
|
|
42
|
-
isLoading,
|
|
43
|
-
error,
|
|
44
|
-
status,
|
|
45
|
-
stop,
|
|
46
|
-
reset
|
|
47
|
-
};
|
|
48
41
|
}
|
|
49
42
|
|
|
50
43
|
//#endregion
|
|
@@ -1 +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
|
|
1
|
+
{"version":3,"file":"use-generate-audio.js","names":[],"sources":["../src/use-generate-audio.ts"],"sourcesContent":["import { useGeneration } from './use-generation'\nimport { reconstructAudioResult } from '@tanstack/ai-client'\nimport type {\n UseGenerationOptions,\n UseGenerationReturn,\n} from './use-generation'\nimport type { AudioGenerationResult, StreamChunk } from '@tanstack/ai'\nimport type {\n AIDevtoolsDisplayOptions,\n AudioGenerateInput,\n ConnectConnectionAdapter,\n GenerationClientState,\n GenerationFetcher,\n GenerationPersistenceOptions,\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<\n TOutput = AudioGenerationResult,\n> extends Pick<\n UseGenerationOptions<AudioGenerateInput, AudioGenerationResult, TOutput>,\n 'persistence' | 'threadId' | 'hydrateGeneration' | 'joinRun'\n> {\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 /**\n * @deprecated Prefer `threadId`. Only allowed when `threadId` is omitted (see `GenerationPersistenceOptions`).\n */\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<\n TOutput = AudioGenerationResult,\n> extends Omit<UseGenerationReturn<TOutput>, 'generate'> {\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}\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<\n UseGenerateAudioOptions,\n 'onResult' | 'persistence' | 'threadId' | 'id'\n > & {\n onResult?: (result: AudioGenerationResult) => TTransformed\n } & GenerationPersistenceOptions,\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 generation = useGeneration<\n AudioGenerateInput,\n AudioGenerationResult,\n TTransformed\n >({\n ...options,\n devtools,\n reconstructResult: reconstructAudioResult,\n })\n\n return generation\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqGA,SAAgB,iBACd,SAQA;CACA,MAAM,WAAW;EACf,GAAG,QAAQ;EACX,WAAW;EACX,UAAU;EACV,YAAY;EACb;AAWD,QAVmB,cAIjB;EACA,GAAG;EACH;EACA,mBAAmB;EACpB,CAAC"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { UseGenerationOptions, UseGenerationReturn } from "./use-generation.js";
|
|
1
2
|
import { Accessor } from "solid-js";
|
|
2
|
-
import { AIDevtoolsDisplayOptions, ConnectConnectionAdapter, GenerationClientState, GenerationFetcher, ImageGenerateInput, InferGenerationOutputFromReturn } from "@tanstack/ai-client";
|
|
3
|
+
import { AIDevtoolsDisplayOptions, ConnectConnectionAdapter, GenerationClientState, GenerationFetcher, GenerationPersistenceOptions, ImageGenerateInput, InferGenerationOutputFromReturn } from "@tanstack/ai-client";
|
|
3
4
|
import { ImageGenerationResult, StreamChunk } from "@tanstack/ai";
|
|
4
5
|
|
|
5
6
|
//#region src/use-generate-image.d.ts
|
|
@@ -9,12 +10,14 @@ import { ImageGenerationResult, StreamChunk } from "@tanstack/ai";
|
|
|
9
10
|
*
|
|
10
11
|
* @template TOutput - The transformed output type (defaults to ImageGenerationResult)
|
|
11
12
|
*/
|
|
12
|
-
interface UseGenerateImageOptions<TOutput = ImageGenerationResult> {
|
|
13
|
+
interface UseGenerateImageOptions<TOutput = ImageGenerationResult> extends Pick<UseGenerationOptions<ImageGenerateInput, ImageGenerationResult, TOutput>, 'persistence' | 'threadId' | 'hydrateGeneration' | 'joinRun'> {
|
|
13
14
|
/** Connect-based adapter for streaming transport (SSE, HTTP stream, custom) */
|
|
14
15
|
connection?: ConnectConnectionAdapter;
|
|
15
16
|
/** Direct async function for image generation */
|
|
16
17
|
fetcher?: GenerationFetcher<ImageGenerateInput, ImageGenerationResult>;
|
|
17
|
-
/**
|
|
18
|
+
/**
|
|
19
|
+
* @deprecated Prefer `threadId`. Only allowed when `threadId` is omitted (see `GenerationPersistenceOptions`).
|
|
20
|
+
*/
|
|
18
21
|
id?: string;
|
|
19
22
|
/** Additional body parameters to send with connect-based adapter requests */
|
|
20
23
|
body?: Record<string, any>;
|
|
@@ -40,7 +43,7 @@ interface UseGenerateImageOptions<TOutput = ImageGenerationResult> {
|
|
|
40
43
|
*
|
|
41
44
|
* @template TOutput - The transformed output type (defaults to ImageGenerationResult)
|
|
42
45
|
*/
|
|
43
|
-
interface UseGenerateImageReturn<TOutput = ImageGenerationResult> {
|
|
46
|
+
interface UseGenerateImageReturn<TOutput = ImageGenerationResult> extends Omit<UseGenerationReturn<TOutput>, 'generate'> {
|
|
44
47
|
/** Trigger image generation */
|
|
45
48
|
generate: (input: ImageGenerateInput) => Promise<void>;
|
|
46
49
|
/** The generation result containing images, or null */
|
|
@@ -51,10 +54,6 @@ interface UseGenerateImageReturn<TOutput = ImageGenerationResult> {
|
|
|
51
54
|
error: Accessor<Error | undefined>;
|
|
52
55
|
/** Current state of the generation */
|
|
53
56
|
status: Accessor<GenerationClientState>;
|
|
54
|
-
/** Abort the current generation */
|
|
55
|
-
stop: () => void;
|
|
56
|
-
/** Clear result, error, and return to idle */
|
|
57
|
-
reset: () => void;
|
|
58
57
|
}
|
|
59
58
|
/**
|
|
60
59
|
* Solid hook for generating images using AI models.
|
|
@@ -88,9 +87,9 @@ interface UseGenerateImageReturn<TOutput = ImageGenerationResult> {
|
|
|
88
87
|
* }
|
|
89
88
|
* ```
|
|
90
89
|
*/
|
|
91
|
-
declare function useGenerateImage<TTransformed = void>(options: Omit<UseGenerateImageOptions, 'onResult'> & {
|
|
90
|
+
declare function useGenerateImage<TTransformed = void>(options: Omit<UseGenerateImageOptions, 'onResult' | 'persistence' | 'threadId' | 'id'> & {
|
|
92
91
|
onResult?: (result: ImageGenerationResult) => TTransformed;
|
|
93
|
-
}): UseGenerateImageReturn<InferGenerationOutputFromReturn<ImageGenerationResult, TTransformed>>;
|
|
92
|
+
} & GenerationPersistenceOptions): UseGenerateImageReturn<InferGenerationOutputFromReturn<ImageGenerationResult, TTransformed>>;
|
|
94
93
|
//#endregion
|
|
95
94
|
export { UseGenerateImageOptions, UseGenerateImageReturn, useGenerateImage };
|
|
96
95
|
//# sourceMappingURL=use-generate-image.d.ts.map
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { useGeneration } from "./use-generation.js";
|
|
2
|
+
import { reconstructImageResult } from "@tanstack/ai-client";
|
|
2
3
|
|
|
3
4
|
//#region src/use-generate-image.ts
|
|
4
5
|
/**
|
|
@@ -40,19 +41,11 @@ function useGenerateImage(options) {
|
|
|
40
41
|
hookName: "useGenerateImage",
|
|
41
42
|
outputKind: "image"
|
|
42
43
|
};
|
|
43
|
-
|
|
44
|
+
return useGeneration({
|
|
44
45
|
...options,
|
|
45
|
-
devtools
|
|
46
|
+
devtools,
|
|
47
|
+
reconstructResult: reconstructImageResult
|
|
46
48
|
});
|
|
47
|
-
return {
|
|
48
|
-
generate,
|
|
49
|
-
result,
|
|
50
|
-
isLoading,
|
|
51
|
-
error,
|
|
52
|
-
status,
|
|
53
|
-
stop,
|
|
54
|
-
reset
|
|
55
|
-
};
|
|
56
49
|
}
|
|
57
50
|
|
|
58
51
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-generate-image.js","names":[],"sources":["../src/use-generate-image.ts"],"sourcesContent":["import { useGeneration } from './use-generation'\nimport type { ImageGenerationResult, StreamChunk } from '@tanstack/ai'\nimport type {\n AIDevtoolsDisplayOptions,\n ConnectConnectionAdapter,\n GenerationClientState,\n GenerationFetcher,\n ImageGenerateInput,\n InferGenerationOutputFromReturn,\n} from '@tanstack/ai-client'\nimport type { Accessor } from 'solid-js'\n\n/**\n * Options for the useGenerateImage hook.\n *\n * @template TOutput - The transformed output type (defaults to ImageGenerationResult)\n */\nexport interface UseGenerateImageOptions
|
|
1
|
+
{"version":3,"file":"use-generate-image.js","names":[],"sources":["../src/use-generate-image.ts"],"sourcesContent":["import { useGeneration } from './use-generation'\nimport { reconstructImageResult } from '@tanstack/ai-client'\nimport type {\n UseGenerationOptions,\n UseGenerationReturn,\n} from './use-generation'\nimport type { ImageGenerationResult, StreamChunk } from '@tanstack/ai'\nimport type {\n AIDevtoolsDisplayOptions,\n ConnectConnectionAdapter,\n GenerationClientState,\n GenerationFetcher,\n GenerationPersistenceOptions,\n ImageGenerateInput,\n InferGenerationOutputFromReturn,\n} from '@tanstack/ai-client'\nimport type { Accessor } from 'solid-js'\n\n/**\n * Options for the useGenerateImage hook.\n *\n * @template TOutput - The transformed output type (defaults to ImageGenerationResult)\n */\nexport interface UseGenerateImageOptions<\n TOutput = ImageGenerationResult,\n> extends Pick<\n UseGenerationOptions<ImageGenerateInput, ImageGenerationResult, TOutput>,\n 'persistence' | 'threadId' | 'hydrateGeneration' | 'joinRun'\n> {\n /** Connect-based adapter for streaming transport (SSE, HTTP stream, custom) */\n connection?: ConnectConnectionAdapter\n /** Direct async function for image generation */\n fetcher?: GenerationFetcher<ImageGenerateInput, ImageGenerationResult>\n /**\n * @deprecated Prefer `threadId`. Only allowed when `threadId` is omitted (see `GenerationPersistenceOptions`).\n */\n id?: string\n /** Additional body parameters to send with connect-based adapter requests */\n body?: Record<string, any>\n /** Display options for TanStack AI Devtools. */\n devtools?: AIDevtoolsDisplayOptions\n /**\n * Callback when images are generated. Can optionally return a transformed value.\n *\n * - Return a non-null value to transform and store it as the result\n * - Return `null` to keep the previous result unchanged\n * - Return nothing (`void`) to store the raw result as-is\n */\n onResult?: (result: ImageGenerationResult) => TOutput | null | void\n /** Callback when an error occurs */\n onError?: (error: Error) => void\n /** Callback when progress is reported (0-100) */\n onProgress?: (progress: number, message?: string) => void\n /** Callback for each stream chunk (connect-based adapter mode only) */\n onChunk?: (chunk: StreamChunk) => void\n}\n\n/**\n * Return type for the useGenerateImage hook.\n *\n * @template TOutput - The transformed output type (defaults to ImageGenerationResult)\n */\nexport interface UseGenerateImageReturn<\n TOutput = ImageGenerationResult,\n> extends Omit<UseGenerationReturn<TOutput>, 'generate'> {\n /** Trigger image generation */\n generate: (input: ImageGenerateInput) => Promise<void>\n /** The generation result containing images, 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}\n\n/**\n * Solid hook for generating images using AI models.\n *\n * Supports two transport modes:\n * - **ConnectConnectionAdapter** -- Streaming transport (SSE, HTTP stream, custom)\n * - **Fetcher** -- Direct async function call\n *\n * @example\n * ```tsx\n * import { useGenerateImage } from '@tanstack/ai-solid'\n * import { fetchServerSentEvents } from '@tanstack/ai-client'\n *\n * function ImageGenerator() {\n * const { generate, result, isLoading, error, reset } = useGenerateImage({\n * connection: fetchServerSentEvents('/api/generate/image'),\n * })\n *\n * return (\n * <div>\n * <button onClick={() => generate({ prompt: 'A sunset over mountains' })}>\n * Generate\n * </button>\n * {isLoading() && <p>Generating...</p>}\n * {error() && <p>Error: {error()!.message}</p>}\n * {result()?.images.map((img, i) => (\n * <img src={img.url || `data:image/png;base64,${img.b64Json}`} />\n * ))}\n * </div>\n * )\n * }\n * ```\n */\nexport function useGenerateImage<TTransformed = void>(\n options: Omit<\n UseGenerateImageOptions,\n 'onResult' | 'persistence' | 'threadId' | 'id'\n > & {\n onResult?: (result: ImageGenerationResult) => TTransformed\n } & GenerationPersistenceOptions,\n): UseGenerateImageReturn<\n InferGenerationOutputFromReturn<ImageGenerationResult, TTransformed>\n> {\n const devtools = {\n ...options.devtools,\n framework: 'solid',\n hookName: 'useGenerateImage',\n outputKind: 'image' as const,\n }\n const generation = useGeneration<\n ImageGenerateInput,\n ImageGenerationResult,\n TTransformed\n >({\n ...options,\n devtools,\n reconstructResult: reconstructImageResult,\n })\n\n return generation\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6GA,SAAgB,iBACd,SAQA;CACA,MAAM,WAAW;EACf,GAAG,QAAQ;EACX,WAAW;EACX,UAAU;EACV,YAAY;EACb;AAWD,QAVmB,cAIjB;EACA,GAAG;EACH;EACA,mBAAmB;EACpB,CAAC"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { UseGenerationOptions, UseGenerationReturn } from "./use-generation.js";
|
|
1
2
|
import { Accessor } from "solid-js";
|
|
2
|
-
import { AIDevtoolsDisplayOptions, ConnectConnectionAdapter, GenerationClientState, GenerationFetcher, InferGenerationOutputFromReturn, SpeechGenerateInput } from "@tanstack/ai-client";
|
|
3
|
+
import { AIDevtoolsDisplayOptions, ConnectConnectionAdapter, GenerationClientState, GenerationFetcher, GenerationPersistenceOptions, InferGenerationOutputFromReturn, SpeechGenerateInput } from "@tanstack/ai-client";
|
|
3
4
|
import { StreamChunk, TTSResult } from "@tanstack/ai";
|
|
4
5
|
|
|
5
6
|
//#region src/use-generate-speech.d.ts
|
|
@@ -9,12 +10,14 @@ import { StreamChunk, TTSResult } from "@tanstack/ai";
|
|
|
9
10
|
*
|
|
10
11
|
* @template TOutput - The transformed output type (defaults to TTSResult)
|
|
11
12
|
*/
|
|
12
|
-
interface UseGenerateSpeechOptions<TOutput = TTSResult> {
|
|
13
|
+
interface UseGenerateSpeechOptions<TOutput = TTSResult> extends Pick<UseGenerationOptions<SpeechGenerateInput, TTSResult, TOutput>, 'persistence' | 'threadId' | 'hydrateGeneration' | 'joinRun'> {
|
|
13
14
|
/** Connect-based adapter for streaming transport (SSE, HTTP stream, custom) */
|
|
14
15
|
connection?: ConnectConnectionAdapter;
|
|
15
16
|
/** Direct async function for speech generation */
|
|
16
17
|
fetcher?: GenerationFetcher<SpeechGenerateInput, TTSResult>;
|
|
17
|
-
/**
|
|
18
|
+
/**
|
|
19
|
+
* @deprecated Prefer `threadId`. Only allowed when `threadId` is omitted (see `GenerationPersistenceOptions`).
|
|
20
|
+
*/
|
|
18
21
|
id?: string;
|
|
19
22
|
/** Additional body parameters to send with connect-based adapter requests */
|
|
20
23
|
body?: Record<string, any>;
|
|
@@ -40,7 +43,7 @@ interface UseGenerateSpeechOptions<TOutput = TTSResult> {
|
|
|
40
43
|
*
|
|
41
44
|
* @template TOutput - The transformed output type (defaults to TTSResult)
|
|
42
45
|
*/
|
|
43
|
-
interface UseGenerateSpeechReturn<TOutput = TTSResult> {
|
|
46
|
+
interface UseGenerateSpeechReturn<TOutput = TTSResult> extends Omit<UseGenerationReturn<TOutput>, 'generate'> {
|
|
44
47
|
/** Trigger speech generation */
|
|
45
48
|
generate: (input: SpeechGenerateInput) => Promise<void>;
|
|
46
49
|
/** The TTS result containing audio data, or null */
|
|
@@ -51,10 +54,6 @@ interface UseGenerateSpeechReturn<TOutput = TTSResult> {
|
|
|
51
54
|
error: Accessor<Error | undefined>;
|
|
52
55
|
/** Current state of the generation */
|
|
53
56
|
status: Accessor<GenerationClientState>;
|
|
54
|
-
/** Abort the current generation */
|
|
55
|
-
stop: () => void;
|
|
56
|
-
/** Clear result, error, and return to idle */
|
|
57
|
-
reset: () => void;
|
|
58
57
|
}
|
|
59
58
|
/**
|
|
60
59
|
* Solid hook for generating speech (text-to-speech) using AI models.
|
|
@@ -82,9 +81,9 @@ interface UseGenerateSpeechReturn<TOutput = TTSResult> {
|
|
|
82
81
|
* }
|
|
83
82
|
* ```
|
|
84
83
|
*/
|
|
85
|
-
declare function useGenerateSpeech<TTransformed = void>(options: Omit<UseGenerateSpeechOptions, 'onResult'> & {
|
|
84
|
+
declare function useGenerateSpeech<TTransformed = void>(options: Omit<UseGenerateSpeechOptions, 'onResult' | 'persistence' | 'threadId' | 'id'> & {
|
|
86
85
|
onResult?: (result: TTSResult) => TTransformed;
|
|
87
|
-
}): UseGenerateSpeechReturn<InferGenerationOutputFromReturn<TTSResult, TTransformed>>;
|
|
86
|
+
} & GenerationPersistenceOptions): UseGenerateSpeechReturn<InferGenerationOutputFromReturn<TTSResult, TTransformed>>;
|
|
88
87
|
//#endregion
|
|
89
88
|
export { UseGenerateSpeechOptions, UseGenerateSpeechReturn, useGenerateSpeech };
|
|
90
89
|
//# sourceMappingURL=use-generate-speech.d.ts.map
|