@tanstack/ai-svelte 0.15.1 → 0.16.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/README.md +17 -0
- package/dist/create-chat.svelte.js +119 -12
- package/dist/create-generate-audio.svelte.d.ts +9 -12
- package/dist/create-generate-audio.svelte.js +6 -0
- package/dist/create-generate-image.svelte.d.ts +9 -12
- package/dist/create-generate-image.svelte.js +6 -0
- package/dist/create-generate-speech.svelte.d.ts +9 -12
- package/dist/create-generate-speech.svelte.js +6 -0
- package/dist/create-generate-video.svelte.d.ts +50 -4
- package/dist/create-generate-video.svelte.js +63 -8
- package/dist/create-generation.svelte.d.ts +59 -6
- package/dist/create-generation.svelte.js +55 -6
- package/dist/create-summarize.svelte.d.ts +9 -12
- package/dist/create-summarize.svelte.js +6 -0
- package/dist/create-transcription.svelte.d.ts +9 -12
- package/dist/create-transcription.svelte.js +6 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/types.d.ts +29 -3
- package/package.json +7 -7
- package/src/create-chat.svelte.ts +134 -14
- package/src/create-generate-audio.svelte.ts +29 -12
- package/src/create-generate-image.svelte.ts +29 -12
- package/src/create-generate-speech.svelte.ts +28 -12
- package/src/create-generate-video.svelte.ts +104 -12
- package/src/create-generation.svelte.ts +115 -13
- package/src/create-summarize.svelte.ts +29 -12
- package/src/create-transcription.svelte.ts +33 -14
- package/src/index.ts +10 -0
- package/src/types.ts +44 -0
|
@@ -6,6 +6,7 @@ import type {
|
|
|
6
6
|
ConnectConnectionAdapter,
|
|
7
7
|
GenerationClientState,
|
|
8
8
|
GenerationFetcher,
|
|
9
|
+
GenerationPersistenceOptions,
|
|
9
10
|
InferGenerationOutputFromReturn,
|
|
10
11
|
VideoGenerateInput,
|
|
11
12
|
VideoGenerateResult,
|
|
@@ -22,12 +23,51 @@ export interface CreateGenerateVideoOptions<TOutput = VideoGenerateResult> {
|
|
|
22
23
|
connection?: ConnectConnectionAdapter
|
|
23
24
|
/** Direct async function that returns a completed video result */
|
|
24
25
|
fetcher?: GenerationFetcher<VideoGenerateInput, VideoGenerateResult>
|
|
25
|
-
/**
|
|
26
|
+
/**
|
|
27
|
+
* @deprecated Prefer `threadId`. Only allowed when `threadId` is omitted (see `GenerationPersistenceOptions`).
|
|
28
|
+
*/
|
|
26
29
|
id?: string
|
|
27
30
|
/** Additional body parameters to send with connect-based adapter requests */
|
|
28
31
|
body?: Record<string, any>
|
|
29
32
|
/** Display options for TanStack AI Devtools. */
|
|
30
33
|
devtools?: AIDevtoolsDisplayOptions
|
|
34
|
+
/**
|
|
35
|
+
* How this generation persists across reloads.
|
|
36
|
+
* - Omit / `false`: ephemeral, in-memory only.
|
|
37
|
+
* - `true`: server-driven — on mount the client hydrates the last generation
|
|
38
|
+
* for its `threadId` from the server (needs a connection with a
|
|
39
|
+
* `hydrateGeneration` handler) and repaints it; it never auto-starts a run.
|
|
40
|
+
*/
|
|
41
|
+
persistence?: boolean
|
|
42
|
+
/**
|
|
43
|
+
* The **scope** this generation belongs to: a stable, app-chosen name for the
|
|
44
|
+
* slot successive runs fill — not a link to a chat conversation.
|
|
45
|
+
*
|
|
46
|
+
* The hook starts empty and produces many runs over its life; each gets its
|
|
47
|
+
* own `runId`, but all belong to one scope. Persistence keys on this, so
|
|
48
|
+
* derive it from your own domain and keep it identical across reloads (e.g.
|
|
49
|
+
* `` `video-${videoId}-start-frame` ``). It is also sent as the AG-UI thread
|
|
50
|
+
* id on the wire, which the protocol requires.
|
|
51
|
+
*
|
|
52
|
+
* **Required whenever `persistence` is set** — an app that cannot name the
|
|
53
|
+
* scope has nothing to restore to. Optional for ephemeral generations, where
|
|
54
|
+
* it falls back to `id` purely to satisfy the wire.
|
|
55
|
+
*/
|
|
56
|
+
threadId?: string
|
|
57
|
+
/**
|
|
58
|
+
* Server-driven hydration handler for `persistence: true` when the
|
|
59
|
+
* connection doesn't carry one (e.g. alongside `fetcher`, or a `stream()` /
|
|
60
|
+
* `rpcStream()` adapter built without handlers) — typically a one-line
|
|
61
|
+
* server-function call. The connection's own handler takes precedence.
|
|
62
|
+
*/
|
|
63
|
+
hydrateGeneration?: ConnectConnectionAdapter['hydrateGeneration']
|
|
64
|
+
/**
|
|
65
|
+
* Re-attach handler that replays a run still generating to completion on
|
|
66
|
+
* mount, when the connection doesn't carry one. Without it, a restored
|
|
67
|
+
* `running` snapshot surfaces as an (interrupted) error. The connection's
|
|
68
|
+
* own handler takes precedence.
|
|
69
|
+
*/
|
|
70
|
+
joinRun?: ConnectConnectionAdapter['joinRun']
|
|
31
71
|
/**
|
|
32
72
|
* Callback when video generation completes. Can optionally return a transformed value.
|
|
33
73
|
*
|
|
@@ -76,6 +116,13 @@ export interface CreateGenerateVideoReturn<TOutput = VideoGenerateResult> {
|
|
|
76
116
|
dispose: () => void
|
|
77
117
|
/** Update additional body parameters */
|
|
78
118
|
updateBody: (body: Record<string, any>) => void
|
|
119
|
+
/**
|
|
120
|
+
* The id of the generation job currently running, or `null` when nothing is in
|
|
121
|
+
* flight. Each call to `generate` is one job with its own id. Pass it to your
|
|
122
|
+
* own endpoint to cancel or poll the provider job — `stop()` only aborts the
|
|
123
|
+
* local stream, it does not stop work already running on the provider.
|
|
124
|
+
*/
|
|
125
|
+
readonly runId: string | null
|
|
79
126
|
}
|
|
80
127
|
|
|
81
128
|
/**
|
|
@@ -112,9 +159,12 @@ export interface CreateGenerateVideoReturn<TOutput = VideoGenerateResult> {
|
|
|
112
159
|
// parameter is typed as `VideoGenerateResult` and `result` narrows to the
|
|
113
160
|
// transform's return. See issue #848.
|
|
114
161
|
export function createGenerateVideo<TTransformed = void>(
|
|
115
|
-
options: Omit<
|
|
162
|
+
options: Omit<
|
|
163
|
+
CreateGenerateVideoOptions,
|
|
164
|
+
'onResult' | 'persistence' | 'threadId' | 'id'
|
|
165
|
+
> & {
|
|
116
166
|
onResult?: (result: VideoGenerateResult) => TTransformed
|
|
117
|
-
},
|
|
167
|
+
} & GenerationPersistenceOptions,
|
|
118
168
|
): CreateGenerateVideoReturn<
|
|
119
169
|
InferGenerationOutputFromReturn<VideoGenerateResult, TTransformed>
|
|
120
170
|
> {
|
|
@@ -122,9 +172,7 @@ export function createGenerateVideo<TTransformed = void>(
|
|
|
122
172
|
VideoGenerateResult,
|
|
123
173
|
TTransformed
|
|
124
174
|
>
|
|
125
|
-
const
|
|
126
|
-
options.id ||
|
|
127
|
-
`video-${Date.now()}-${Math.random().toString(36).substring(7)}`
|
|
175
|
+
const fallbackId = `video-${Date.now()}-${Math.random().toString(36).substring(7)}`
|
|
128
176
|
|
|
129
177
|
// Create reactive state using Svelte 5 runes
|
|
130
178
|
let result = $state<TOutput | null>(null)
|
|
@@ -133,14 +181,26 @@ export function createGenerateVideo<TTransformed = void>(
|
|
|
133
181
|
let isLoading = $state(false)
|
|
134
182
|
let error = $state<Error | undefined>(undefined)
|
|
135
183
|
let status = $state<GenerationClientState>('idle')
|
|
184
|
+
let runId = $state<string | null>(null)
|
|
185
|
+
let disposed = false
|
|
136
186
|
|
|
137
187
|
// `body` uses a conditional spread because `VideoGenerationClientOptions.body`
|
|
138
188
|
// is declared `body?: Record<string, any>` (absent vs. present) under
|
|
139
189
|
// `exactOptionalPropertyTypes`. The optional caller `options.body` may be
|
|
140
190
|
// undefined, in which case we want the key to be absent on the target.
|
|
191
|
+
// Identity: pass `threadId` alone when set (never also pass deprecated `id`).
|
|
141
192
|
const baseOptions = {
|
|
142
|
-
id: clientId,
|
|
143
193
|
body: options.body,
|
|
194
|
+
...(options.threadId !== undefined
|
|
195
|
+
? { threadId: options.threadId }
|
|
196
|
+
: { id: options.id ?? fallbackId }),
|
|
197
|
+
...(options.persistence !== undefined && {
|
|
198
|
+
persistence: options.persistence,
|
|
199
|
+
}),
|
|
200
|
+
...(options.hydrateGeneration !== undefined && {
|
|
201
|
+
hydrateGeneration: options.hydrateGeneration,
|
|
202
|
+
}),
|
|
203
|
+
...(options.joinRun !== undefined && { joinRun: options.joinRun }),
|
|
144
204
|
devtoolsBridgeFactory: createVideoDevtoolsBridge,
|
|
145
205
|
devtools: {
|
|
146
206
|
...options.devtools,
|
|
@@ -154,29 +214,49 @@ export function createGenerateVideo<TTransformed = void>(
|
|
|
154
214
|
onResult: ((r: VideoGenerateResult) => options.onResult?.(r)) as (
|
|
155
215
|
result: VideoGenerateResult,
|
|
156
216
|
) => TOutput | null | void,
|
|
157
|
-
onError: (e: Error) =>
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
217
|
+
onError: (e: Error) => {
|
|
218
|
+
if (!disposed) options.onError?.(e)
|
|
219
|
+
},
|
|
220
|
+
onProgress: (p: number, m?: string) => {
|
|
221
|
+
if (!disposed) options.onProgress?.(p, m)
|
|
222
|
+
},
|
|
223
|
+
onChunk: (c: StreamChunk) => {
|
|
224
|
+
if (!disposed) options.onChunk?.(c)
|
|
225
|
+
},
|
|
226
|
+
onJobCreated: (id: string) => {
|
|
227
|
+
if (!disposed) options.onJobCreated?.(id)
|
|
228
|
+
},
|
|
229
|
+
onStatusUpdate: (s: VideoStatusInfo) => {
|
|
230
|
+
if (!disposed) options.onStatusUpdate?.(s)
|
|
231
|
+
},
|
|
162
232
|
onResultChange: (r: TOutput | null) => {
|
|
233
|
+
if (disposed) return
|
|
163
234
|
result = r
|
|
164
235
|
},
|
|
165
236
|
onLoadingChange: (l: boolean) => {
|
|
237
|
+
if (disposed) return
|
|
166
238
|
isLoading = l
|
|
167
239
|
},
|
|
168
240
|
onErrorChange: (e: Error | undefined) => {
|
|
241
|
+
if (disposed) return
|
|
169
242
|
error = e
|
|
170
243
|
},
|
|
171
244
|
onStatusChange: (s: GenerationClientState) => {
|
|
245
|
+
if (disposed) return
|
|
172
246
|
status = s
|
|
173
247
|
},
|
|
174
248
|
onJobIdChange: (id: string | null) => {
|
|
249
|
+
if (disposed) return
|
|
175
250
|
jobId = id
|
|
176
251
|
},
|
|
177
252
|
onVideoStatusChange: (s: VideoStatusInfo | null) => {
|
|
253
|
+
if (disposed) return
|
|
178
254
|
videoStatus = s
|
|
179
255
|
},
|
|
256
|
+
onResumeStateChange: (rs: { runId: string } | null) => {
|
|
257
|
+
if (disposed) return
|
|
258
|
+
runId = rs?.runId ?? null
|
|
259
|
+
},
|
|
180
260
|
}
|
|
181
261
|
|
|
182
262
|
let client: VideoGenerationClient<TOutput>
|
|
@@ -197,6 +277,8 @@ export function createGenerateVideo<TTransformed = void>(
|
|
|
197
277
|
)
|
|
198
278
|
}
|
|
199
279
|
|
|
280
|
+
// Mount devtools only. Generation runs are never auto-started on setup —
|
|
281
|
+
// persisted state is read-only for display.
|
|
200
282
|
client.mountDevtools()
|
|
201
283
|
|
|
202
284
|
// Note: Cleanup is handled by calling dispose() directly when needed.
|
|
@@ -205,6 +287,12 @@ export function createGenerateVideo<TTransformed = void>(
|
|
|
205
287
|
// Users should call video.dispose() in their component's cleanup if needed.
|
|
206
288
|
|
|
207
289
|
const generate = async (input: VideoGenerateInput) => {
|
|
290
|
+
// Svelte has no remount effect to revive a disposed client (the other
|
|
291
|
+
// frameworks revive via mountDevtools() in their mount effects), so an
|
|
292
|
+
// explicit generate() after dispose() is the Svelte revive path: bring
|
|
293
|
+
// the client and the reactive bindings back together.
|
|
294
|
+
disposed = false
|
|
295
|
+
client.mountDevtools()
|
|
208
296
|
await client.generate(input)
|
|
209
297
|
}
|
|
210
298
|
|
|
@@ -217,6 +305,7 @@ export function createGenerateVideo<TTransformed = void>(
|
|
|
217
305
|
}
|
|
218
306
|
|
|
219
307
|
const dispose = () => {
|
|
308
|
+
disposed = true
|
|
220
309
|
client.dispose()
|
|
221
310
|
}
|
|
222
311
|
|
|
@@ -248,5 +337,8 @@ export function createGenerateVideo<TTransformed = void>(
|
|
|
248
337
|
reset,
|
|
249
338
|
dispose,
|
|
250
339
|
updateBody,
|
|
340
|
+
get runId() {
|
|
341
|
+
return runId
|
|
342
|
+
},
|
|
251
343
|
}
|
|
252
344
|
}
|
|
@@ -7,6 +7,8 @@ import type {
|
|
|
7
7
|
GenerationClientOptions,
|
|
8
8
|
GenerationClientState,
|
|
9
9
|
GenerationFetcher,
|
|
10
|
+
GenerationPersistenceOptions,
|
|
11
|
+
GenerationRestoredResult,
|
|
10
12
|
InferGenerationOutputFromReturn,
|
|
11
13
|
} from '@tanstack/ai-client'
|
|
12
14
|
|
|
@@ -24,12 +26,51 @@ export interface CreateGenerationOptions<TInput, TResult, TOutput = TResult> {
|
|
|
24
26
|
connection?: ConnectConnectionAdapter
|
|
25
27
|
/** Direct async function for one-shot generation (no streaming protocol needed) */
|
|
26
28
|
fetcher?: GenerationFetcher<TInput, TResult>
|
|
27
|
-
/**
|
|
29
|
+
/**
|
|
30
|
+
* @deprecated Prefer `threadId`. Only allowed when `threadId` is omitted (see `GenerationPersistenceOptions`).
|
|
31
|
+
*/
|
|
28
32
|
id?: string
|
|
29
33
|
/** Additional body parameters to send with connect-based adapter requests */
|
|
30
34
|
body?: Record<string, any>
|
|
31
35
|
/** Display options for TanStack AI Devtools. */
|
|
32
36
|
devtools?: AIDevtoolsDisplayOptions
|
|
37
|
+
/**
|
|
38
|
+
* How this generation persists across reloads.
|
|
39
|
+
* - Omit / `false`: ephemeral, in-memory only.
|
|
40
|
+
* - `true`: server-driven — on mount the client hydrates the last generation
|
|
41
|
+
* for its `threadId` from the server (needs a connection with a
|
|
42
|
+
* `hydrateGeneration` handler) and repaints it; it never auto-starts a run.
|
|
43
|
+
*/
|
|
44
|
+
persistence?: boolean
|
|
45
|
+
/**
|
|
46
|
+
* The **scope** this generation belongs to: a stable, app-chosen name for the
|
|
47
|
+
* slot successive runs fill — not a link to a chat conversation.
|
|
48
|
+
*
|
|
49
|
+
* The hook starts empty and produces many runs over its life; each gets its
|
|
50
|
+
* own `runId`, but all belong to one scope. Persistence keys on this, so
|
|
51
|
+
* derive it from your own domain and keep it identical across reloads (e.g.
|
|
52
|
+
* `` `video-${videoId}-start-frame` ``). It is also sent as the AG-UI thread
|
|
53
|
+
* id on the wire, which the protocol requires.
|
|
54
|
+
*
|
|
55
|
+
* **Required whenever `persistence` is set** — an app that cannot name the
|
|
56
|
+
* scope has nothing to restore to. Optional for ephemeral generations, where
|
|
57
|
+
* it falls back to `id` purely to satisfy the wire.
|
|
58
|
+
*/
|
|
59
|
+
threadId?: string
|
|
60
|
+
/**
|
|
61
|
+
* Server-driven hydration handler for `persistence: true` when the
|
|
62
|
+
* connection doesn't carry one (e.g. alongside `fetcher`, or a `stream()` /
|
|
63
|
+
* `rpcStream()` adapter built without handlers) — typically a one-line
|
|
64
|
+
* server-function call. The connection's own handler takes precedence.
|
|
65
|
+
*/
|
|
66
|
+
hydrateGeneration?: ConnectConnectionAdapter['hydrateGeneration']
|
|
67
|
+
/**
|
|
68
|
+
* Re-attach handler that replays a run still generating to completion on
|
|
69
|
+
* mount, when the connection doesn't carry one. Without it, a restored
|
|
70
|
+
* `running` snapshot surfaces as an (interrupted) error. The connection's
|
|
71
|
+
* own handler takes precedence.
|
|
72
|
+
*/
|
|
73
|
+
joinRun?: ConnectConnectionAdapter['joinRun']
|
|
33
74
|
/**
|
|
34
75
|
* Callback when a result is received. Can optionally return a transformed value.
|
|
35
76
|
*
|
|
@@ -44,14 +85,24 @@ export interface CreateGenerationOptions<TInput, TResult, TOutput = TResult> {
|
|
|
44
85
|
onProgress?: (progress: number, message?: string) => void
|
|
45
86
|
/** Callback for each stream chunk (connect-based adapter mode only) */
|
|
46
87
|
onChunk?: (chunk: StreamChunk) => void
|
|
88
|
+
/**
|
|
89
|
+
* @internal Rebuild a typed result from a restored snapshot, injected by each
|
|
90
|
+
* specialized function (image / speech / audio / transcription / summarize).
|
|
91
|
+
* Forwarded to the client so a server-hydrate restore repaints `result`.
|
|
92
|
+
*/
|
|
93
|
+
reconstructResult?: (restored: GenerationRestoredResult) => TResult | null
|
|
47
94
|
}
|
|
48
95
|
|
|
49
96
|
/**
|
|
50
97
|
* Return type for the createGeneration function.
|
|
51
98
|
*
|
|
52
99
|
* @template TOutput - The output type (after optional transform)
|
|
100
|
+
* @template TInput - The input type accepted by `generate` (defaults to any object)
|
|
53
101
|
*/
|
|
54
|
-
export interface CreateGenerationReturn<
|
|
102
|
+
export interface CreateGenerationReturn<
|
|
103
|
+
TOutput,
|
|
104
|
+
TInput extends Record<string, any> = Record<string, any>,
|
|
105
|
+
> {
|
|
55
106
|
/** The generation result, or null if not yet generated */
|
|
56
107
|
readonly result: TOutput | null
|
|
57
108
|
/** Whether a generation is currently in progress */
|
|
@@ -61,7 +112,7 @@ export interface CreateGenerationReturn<TOutput> {
|
|
|
61
112
|
/** Current state of the generation client */
|
|
62
113
|
readonly status: GenerationClientState
|
|
63
114
|
/** Trigger a generation request */
|
|
64
|
-
generate: (input:
|
|
115
|
+
generate: (input: TInput) => Promise<void>
|
|
65
116
|
/** Abort the current generation */
|
|
66
117
|
stop: () => void
|
|
67
118
|
/** Clear result, error, and return to idle */
|
|
@@ -70,6 +121,13 @@ export interface CreateGenerationReturn<TOutput> {
|
|
|
70
121
|
dispose: () => void
|
|
71
122
|
/** Update additional body parameters */
|
|
72
123
|
updateBody: (body: Record<string, any>) => void
|
|
124
|
+
/**
|
|
125
|
+
* The id of the generation job currently running, or `null` when nothing is in
|
|
126
|
+
* flight. Each call to `generate` is one job with its own id. Pass it to your
|
|
127
|
+
* own endpoint to cancel or poll the provider job — `stop()` only aborts the
|
|
128
|
+
* local stream, it does not stop work already running on the provider.
|
|
129
|
+
*/
|
|
130
|
+
readonly runId: string | null
|
|
73
131
|
}
|
|
74
132
|
|
|
75
133
|
/**
|
|
@@ -114,30 +172,48 @@ export function createGeneration<
|
|
|
114
172
|
TResult,
|
|
115
173
|
TTransformed = void,
|
|
116
174
|
>(
|
|
117
|
-
options: Omit<
|
|
175
|
+
options: Omit<
|
|
176
|
+
CreateGenerationOptions<TInput, TResult>,
|
|
177
|
+
'onResult' | 'persistence' | 'threadId' | 'id'
|
|
178
|
+
> & {
|
|
118
179
|
onResult?: (result: TResult) => TTransformed
|
|
119
|
-
},
|
|
180
|
+
} & GenerationPersistenceOptions,
|
|
120
181
|
): CreateGenerationReturn<
|
|
121
|
-
InferGenerationOutputFromReturn<TResult, TTransformed
|
|
182
|
+
InferGenerationOutputFromReturn<TResult, TTransformed>,
|
|
183
|
+
TInput
|
|
122
184
|
> {
|
|
123
185
|
type TOutput = InferGenerationOutputFromReturn<TResult, TTransformed>
|
|
124
|
-
const
|
|
125
|
-
options.id || `gen-${Date.now()}-${Math.random().toString(36).substring(7)}`
|
|
186
|
+
const fallbackId = `gen-${Date.now()}-${Math.random().toString(36).substring(7)}`
|
|
126
187
|
|
|
127
188
|
// Create reactive state using Svelte 5 runes
|
|
128
189
|
let result = $state<TOutput | null>(null)
|
|
129
190
|
let isLoading = $state(false)
|
|
130
191
|
let error = $state<Error | undefined>(undefined)
|
|
131
192
|
let status = $state<GenerationClientState>('idle')
|
|
193
|
+
let runId = $state<string | null>(null)
|
|
194
|
+
let disposed = false
|
|
132
195
|
|
|
133
196
|
// `body` uses a conditional spread because `GenerationClientOptions.body`
|
|
134
197
|
// is declared `body?: Record<string, any>` (absent vs. present) under
|
|
135
198
|
// `exactOptionalPropertyTypes`. Assigning `undefined` directly would be
|
|
136
199
|
// rejected — the optional caller `options.body` may be undefined, in which
|
|
137
200
|
// case we want the key to be absent.
|
|
201
|
+
// Identity: pass `threadId` alone when set (never also pass deprecated `id`).
|
|
138
202
|
const clientOptions: GenerationClientOptions<TInput, TResult, TOutput> = {
|
|
139
|
-
id: clientId,
|
|
140
203
|
body: options.body,
|
|
204
|
+
...(options.threadId !== undefined
|
|
205
|
+
? { threadId: options.threadId }
|
|
206
|
+
: { id: options.id ?? fallbackId }),
|
|
207
|
+
...(options.persistence !== undefined && {
|
|
208
|
+
persistence: options.persistence,
|
|
209
|
+
}),
|
|
210
|
+
...(options.hydrateGeneration !== undefined && {
|
|
211
|
+
hydrateGeneration: options.hydrateGeneration,
|
|
212
|
+
}),
|
|
213
|
+
...(options.joinRun !== undefined && { joinRun: options.joinRun }),
|
|
214
|
+
...(options.reconstructResult
|
|
215
|
+
? { reconstructResult: options.reconstructResult }
|
|
216
|
+
: {}),
|
|
141
217
|
devtoolsBridgeFactory: createGenerationDevtoolsBridge,
|
|
142
218
|
devtools: {
|
|
143
219
|
...options.devtools,
|
|
@@ -150,21 +226,35 @@ export function createGeneration<
|
|
|
150
226
|
onResult: ((r: TResult) => options.onResult?.(r)) as (
|
|
151
227
|
result: TResult,
|
|
152
228
|
) => TOutput | null | void,
|
|
153
|
-
onError: (e: Error) =>
|
|
154
|
-
|
|
155
|
-
|
|
229
|
+
onError: (e: Error) => {
|
|
230
|
+
if (!disposed) options.onError?.(e)
|
|
231
|
+
},
|
|
232
|
+
onProgress: (p: number, m?: string) => {
|
|
233
|
+
if (!disposed) options.onProgress?.(p, m)
|
|
234
|
+
},
|
|
235
|
+
onChunk: (c: StreamChunk) => {
|
|
236
|
+
if (!disposed) options.onChunk?.(c)
|
|
237
|
+
},
|
|
156
238
|
onResultChange: (r: TOutput | null) => {
|
|
239
|
+
if (disposed) return
|
|
157
240
|
result = r
|
|
158
241
|
},
|
|
159
242
|
onLoadingChange: (l: boolean) => {
|
|
243
|
+
if (disposed) return
|
|
160
244
|
isLoading = l
|
|
161
245
|
},
|
|
162
246
|
onErrorChange: (e: Error | undefined) => {
|
|
247
|
+
if (disposed) return
|
|
163
248
|
error = e
|
|
164
249
|
},
|
|
165
250
|
onStatusChange: (s: GenerationClientState) => {
|
|
251
|
+
if (disposed) return
|
|
166
252
|
status = s
|
|
167
253
|
},
|
|
254
|
+
onResumeStateChange: (rs) => {
|
|
255
|
+
if (disposed) return
|
|
256
|
+
runId = rs?.runId ?? null
|
|
257
|
+
},
|
|
168
258
|
}
|
|
169
259
|
|
|
170
260
|
let client: GenerationClient<TInput, TResult, TOutput>
|
|
@@ -185,6 +275,8 @@ export function createGeneration<
|
|
|
185
275
|
)
|
|
186
276
|
}
|
|
187
277
|
|
|
278
|
+
// Mount devtools only. Generation runs are never auto-started on setup —
|
|
279
|
+
// persisted state is read-only for display.
|
|
188
280
|
client.mountDevtools()
|
|
189
281
|
|
|
190
282
|
// Note: Cleanup is handled by calling dispose() directly when needed.
|
|
@@ -193,6 +285,12 @@ export function createGeneration<
|
|
|
193
285
|
// Users should call gen.dispose() in their component's cleanup if needed.
|
|
194
286
|
|
|
195
287
|
const generate = async (input: TInput) => {
|
|
288
|
+
// Svelte has no remount effect to revive a disposed client (the other
|
|
289
|
+
// frameworks revive via mountDevtools() in their mount effects), so an
|
|
290
|
+
// explicit generate() after dispose() is the Svelte revive path: bring
|
|
291
|
+
// the client and the reactive bindings back together.
|
|
292
|
+
disposed = false
|
|
293
|
+
client.mountDevtools()
|
|
196
294
|
await client.generate(input)
|
|
197
295
|
}
|
|
198
296
|
|
|
@@ -205,6 +303,7 @@ export function createGeneration<
|
|
|
205
303
|
}
|
|
206
304
|
|
|
207
305
|
const dispose = () => {
|
|
306
|
+
disposed = true
|
|
208
307
|
client.dispose()
|
|
209
308
|
}
|
|
210
309
|
|
|
@@ -225,10 +324,13 @@ export function createGeneration<
|
|
|
225
324
|
get status() {
|
|
226
325
|
return status
|
|
227
326
|
},
|
|
228
|
-
generate
|
|
327
|
+
generate,
|
|
229
328
|
stop,
|
|
230
329
|
reset,
|
|
231
330
|
dispose,
|
|
232
331
|
updateBody,
|
|
332
|
+
get runId() {
|
|
333
|
+
return runId
|
|
334
|
+
},
|
|
233
335
|
}
|
|
234
336
|
}
|
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
import { createGeneration } from './create-generation.svelte'
|
|
2
|
+
import { reconstructSummarizeResult } from '@tanstack/ai-client'
|
|
3
|
+
import type {
|
|
4
|
+
CreateGenerationOptions,
|
|
5
|
+
CreateGenerationReturn,
|
|
6
|
+
} from './create-generation.svelte'
|
|
2
7
|
import type { StreamChunk, SummarizationResult } from '@tanstack/ai'
|
|
3
8
|
import type {
|
|
4
9
|
AIDevtoolsDisplayOptions,
|
|
5
10
|
ConnectConnectionAdapter,
|
|
6
11
|
GenerationClientState,
|
|
7
12
|
GenerationFetcher,
|
|
13
|
+
GenerationPersistenceOptions,
|
|
8
14
|
InferGenerationOutputFromReturn,
|
|
9
15
|
SummarizeGenerateInput,
|
|
10
16
|
} from '@tanstack/ai-client'
|
|
@@ -14,12 +20,19 @@ import type {
|
|
|
14
20
|
*
|
|
15
21
|
* @template TOutput - The output type after optional transform (defaults to SummarizationResult)
|
|
16
22
|
*/
|
|
17
|
-
export interface CreateSummarizeOptions<
|
|
23
|
+
export interface CreateSummarizeOptions<
|
|
24
|
+
TOutput = SummarizationResult,
|
|
25
|
+
> extends Pick<
|
|
26
|
+
CreateGenerationOptions<SummarizeGenerateInput, SummarizationResult, TOutput>,
|
|
27
|
+
'persistence' | 'threadId' | 'hydrateGeneration' | 'joinRun'
|
|
28
|
+
> {
|
|
18
29
|
/** Connect-based adapter for streaming transport (SSE, HTTP stream, custom) */
|
|
19
30
|
connection?: ConnectConnectionAdapter
|
|
20
31
|
/** Direct async function for summarization */
|
|
21
32
|
fetcher?: GenerationFetcher<SummarizeGenerateInput, SummarizationResult>
|
|
22
|
-
/**
|
|
33
|
+
/**
|
|
34
|
+
* @deprecated Prefer `threadId`. Only allowed when `threadId` is omitted (see `GenerationPersistenceOptions`).
|
|
35
|
+
*/
|
|
23
36
|
id?: string
|
|
24
37
|
/** Additional body parameters to send with connect-based adapter requests */
|
|
25
38
|
body?: Record<string, any>
|
|
@@ -46,7 +59,9 @@ export interface CreateSummarizeOptions<TOutput = SummarizationResult> {
|
|
|
46
59
|
*
|
|
47
60
|
* @template TOutput - The output type (after optional transform)
|
|
48
61
|
*/
|
|
49
|
-
export interface CreateSummarizeReturn<
|
|
62
|
+
export interface CreateSummarizeReturn<
|
|
63
|
+
TOutput = SummarizationResult,
|
|
64
|
+
> extends Omit<CreateGenerationReturn<TOutput>, 'generate'> {
|
|
50
65
|
/** The summarization result, or null */
|
|
51
66
|
readonly result: TOutput | null
|
|
52
67
|
/** Whether summarization is in progress */
|
|
@@ -57,12 +72,6 @@ export interface CreateSummarizeReturn<TOutput = SummarizationResult> {
|
|
|
57
72
|
readonly status: GenerationClientState
|
|
58
73
|
/** Trigger summarization */
|
|
59
74
|
generate: (input: SummarizeGenerateInput) => Promise<void>
|
|
60
|
-
/** Abort the current summarization */
|
|
61
|
-
stop: () => void
|
|
62
|
-
/** Clear result, error, and return to idle */
|
|
63
|
-
reset: () => void
|
|
64
|
-
/** Update additional body parameters */
|
|
65
|
-
updateBody: (body: Record<string, any>) => void
|
|
66
75
|
}
|
|
67
76
|
|
|
68
77
|
/**
|
|
@@ -96,9 +105,12 @@ export interface CreateSummarizeReturn<TOutput = SummarizationResult> {
|
|
|
96
105
|
* ```
|
|
97
106
|
*/
|
|
98
107
|
export function createSummarize<TTransformed = void>(
|
|
99
|
-
options: Omit<
|
|
108
|
+
options: Omit<
|
|
109
|
+
CreateSummarizeOptions,
|
|
110
|
+
'onResult' | 'persistence' | 'threadId' | 'id'
|
|
111
|
+
> & {
|
|
100
112
|
onResult?: (result: SummarizationResult) => TTransformed
|
|
101
|
-
},
|
|
113
|
+
} & GenerationPersistenceOptions,
|
|
102
114
|
): CreateSummarizeReturn<
|
|
103
115
|
InferGenerationOutputFromReturn<SummarizationResult, TTransformed>
|
|
104
116
|
> {
|
|
@@ -115,6 +127,7 @@ export function createSummarize<TTransformed = void>(
|
|
|
115
127
|
>({
|
|
116
128
|
...options,
|
|
117
129
|
devtools,
|
|
130
|
+
reconstructResult: reconstructSummarizeResult,
|
|
118
131
|
})
|
|
119
132
|
|
|
120
133
|
return {
|
|
@@ -130,9 +143,13 @@ export function createSummarize<TTransformed = void>(
|
|
|
130
143
|
get status() {
|
|
131
144
|
return gen.status
|
|
132
145
|
},
|
|
133
|
-
generate: gen.generate
|
|
146
|
+
generate: gen.generate,
|
|
134
147
|
stop: gen.stop,
|
|
135
148
|
reset: gen.reset,
|
|
136
149
|
updateBody: gen.updateBody,
|
|
150
|
+
dispose: gen.dispose,
|
|
151
|
+
get runId() {
|
|
152
|
+
return gen.runId
|
|
153
|
+
},
|
|
137
154
|
}
|
|
138
155
|
}
|
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
import { createGeneration } from './create-generation.svelte'
|
|
2
|
+
import { reconstructTranscriptionResult } from '@tanstack/ai-client'
|
|
3
|
+
import type {
|
|
4
|
+
CreateGenerationOptions,
|
|
5
|
+
CreateGenerationReturn,
|
|
6
|
+
} from './create-generation.svelte'
|
|
2
7
|
import type { StreamChunk, TranscriptionResult } from '@tanstack/ai'
|
|
3
8
|
import type {
|
|
4
9
|
AIDevtoolsDisplayOptions,
|
|
5
10
|
ConnectConnectionAdapter,
|
|
6
11
|
GenerationClientState,
|
|
7
12
|
GenerationFetcher,
|
|
13
|
+
GenerationPersistenceOptions,
|
|
8
14
|
InferGenerationOutputFromReturn,
|
|
9
15
|
TranscriptionGenerateInput,
|
|
10
16
|
} from '@tanstack/ai-client'
|
|
@@ -14,12 +20,23 @@ import type {
|
|
|
14
20
|
*
|
|
15
21
|
* @template TOutput - The output type after optional transform (defaults to TranscriptionResult)
|
|
16
22
|
*/
|
|
17
|
-
export interface CreateTranscriptionOptions<
|
|
23
|
+
export interface CreateTranscriptionOptions<
|
|
24
|
+
TOutput = TranscriptionResult,
|
|
25
|
+
> extends Pick<
|
|
26
|
+
CreateGenerationOptions<
|
|
27
|
+
TranscriptionGenerateInput,
|
|
28
|
+
TranscriptionResult,
|
|
29
|
+
TOutput
|
|
30
|
+
>,
|
|
31
|
+
'persistence' | 'threadId' | 'hydrateGeneration' | 'joinRun'
|
|
32
|
+
> {
|
|
18
33
|
/** Connect-based adapter for streaming transport (SSE, HTTP stream, custom) */
|
|
19
34
|
connection?: ConnectConnectionAdapter
|
|
20
35
|
/** Direct async function for transcription */
|
|
21
36
|
fetcher?: GenerationFetcher<TranscriptionGenerateInput, TranscriptionResult>
|
|
22
|
-
/**
|
|
37
|
+
/**
|
|
38
|
+
* @deprecated Prefer `threadId`. Only allowed when `threadId` is omitted (see `GenerationPersistenceOptions`).
|
|
39
|
+
*/
|
|
23
40
|
id?: string
|
|
24
41
|
/** Additional body parameters to send with connect-based adapter requests */
|
|
25
42
|
body?: Record<string, any>
|
|
@@ -46,7 +63,9 @@ export interface CreateTranscriptionOptions<TOutput = TranscriptionResult> {
|
|
|
46
63
|
*
|
|
47
64
|
* @template TOutput - The output type (after optional transform)
|
|
48
65
|
*/
|
|
49
|
-
export interface CreateTranscriptionReturn<
|
|
66
|
+
export interface CreateTranscriptionReturn<
|
|
67
|
+
TOutput = TranscriptionResult,
|
|
68
|
+
> extends Omit<CreateGenerationReturn<TOutput>, 'generate'> {
|
|
50
69
|
/** The transcription result, or null */
|
|
51
70
|
readonly result: TOutput | null
|
|
52
71
|
/** Whether transcription is in progress */
|
|
@@ -57,12 +76,6 @@ export interface CreateTranscriptionReturn<TOutput = TranscriptionResult> {
|
|
|
57
76
|
readonly status: GenerationClientState
|
|
58
77
|
/** Trigger transcription */
|
|
59
78
|
generate: (input: TranscriptionGenerateInput) => Promise<void>
|
|
60
|
-
/** Abort the current transcription */
|
|
61
|
-
stop: () => void
|
|
62
|
-
/** Clear result, error, and return to idle */
|
|
63
|
-
reset: () => void
|
|
64
|
-
/** Update additional body parameters */
|
|
65
|
-
updateBody: (body: Record<string, any>) => void
|
|
66
79
|
}
|
|
67
80
|
|
|
68
81
|
/**
|
|
@@ -101,9 +114,12 @@ export interface CreateTranscriptionReturn<TOutput = TranscriptionResult> {
|
|
|
101
114
|
* ```
|
|
102
115
|
*/
|
|
103
116
|
export function createTranscription<TTransformed = void>(
|
|
104
|
-
options: Omit<
|
|
117
|
+
options: Omit<
|
|
118
|
+
CreateTranscriptionOptions,
|
|
119
|
+
'onResult' | 'persistence' | 'threadId' | 'id'
|
|
120
|
+
> & {
|
|
105
121
|
onResult?: (result: TranscriptionResult) => TTransformed
|
|
106
|
-
},
|
|
122
|
+
} & GenerationPersistenceOptions,
|
|
107
123
|
): CreateTranscriptionReturn<
|
|
108
124
|
InferGenerationOutputFromReturn<TranscriptionResult, TTransformed>
|
|
109
125
|
> {
|
|
@@ -120,6 +136,7 @@ export function createTranscription<TTransformed = void>(
|
|
|
120
136
|
>({
|
|
121
137
|
...options,
|
|
122
138
|
devtools,
|
|
139
|
+
reconstructResult: reconstructTranscriptionResult,
|
|
123
140
|
})
|
|
124
141
|
|
|
125
142
|
return {
|
|
@@ -135,11 +152,13 @@ export function createTranscription<TTransformed = void>(
|
|
|
135
152
|
get status() {
|
|
136
153
|
return gen.status
|
|
137
154
|
},
|
|
138
|
-
generate: gen.generate
|
|
139
|
-
input: TranscriptionGenerateInput,
|
|
140
|
-
) => Promise<void>,
|
|
155
|
+
generate: gen.generate,
|
|
141
156
|
stop: gen.stop,
|
|
142
157
|
reset: gen.reset,
|
|
143
158
|
updateBody: gen.updateBody,
|
|
159
|
+
dispose: gen.dispose,
|
|
160
|
+
get runId() {
|
|
161
|
+
return gen.runId
|
|
162
|
+
},
|
|
144
163
|
}
|
|
145
164
|
}
|
package/src/index.ts
CHANGED
|
@@ -65,6 +65,16 @@ export type {
|
|
|
65
65
|
// Re-export from ai-client for convenience
|
|
66
66
|
export {
|
|
67
67
|
fetchServerSentEvents,
|
|
68
|
+
localStoragePersistence,
|
|
69
|
+
sessionStoragePersistence,
|
|
70
|
+
indexedDBPersistence,
|
|
71
|
+
StorageUnavailableError,
|
|
72
|
+
type ChatClientPersistence,
|
|
73
|
+
type ChatPersistedState,
|
|
74
|
+
type ChatPersistenceOption,
|
|
75
|
+
type ChatStorageAdapter,
|
|
76
|
+
type WebStoragePersistenceOptions,
|
|
77
|
+
type IndexedDBPersistenceOptions,
|
|
68
78
|
fetchHttpStream,
|
|
69
79
|
xhrServerSentEvents,
|
|
70
80
|
xhrHttpStream,
|