@tanstack/ai-client 0.23.3 → 0.25.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 +10 -1
- package/dist/esm/chat-client.d.ts +17 -13
- package/dist/esm/chat-client.js +49 -18
- package/dist/esm/chat-client.js.map +1 -1
- package/dist/esm/connection-adapters.d.ts +49 -0
- package/dist/esm/connection-adapters.js +269 -29
- package/dist/esm/connection-adapters.js.map +1 -1
- package/dist/esm/devtools.d.ts +0 -1
- package/dist/esm/devtools.js +4 -10
- package/dist/esm/devtools.js.map +1 -1
- package/dist/esm/generation-client.d.ts +5 -11
- package/dist/esm/generation-client.js +18 -5
- package/dist/esm/generation-client.js.map +1 -1
- package/dist/esm/generation-types.d.ts +15 -45
- package/dist/esm/generation-types.js.map +1 -1
- package/dist/esm/index.d.ts +2 -2
- package/dist/esm/index.js +2 -2
- package/dist/esm/interrupt-manager.d.ts +15 -10
- package/dist/esm/interrupt-manager.js +176 -83
- package/dist/esm/interrupt-manager.js.map +1 -1
- package/dist/esm/types.d.ts +66 -48
- package/dist/esm/types.js.map +1 -1
- package/dist/esm/video-generation-client.d.ts +5 -11
- package/dist/esm/video-generation-client.js +18 -5
- package/dist/esm/video-generation-client.js.map +1 -1
- package/package.json +3 -3
- package/src/chat-client.ts +148 -53
- package/src/connection-adapters.ts +505 -47
- package/src/devtools.ts +6 -22
- package/src/generation-client.ts +36 -24
- package/src/generation-types.ts +15 -47
- package/src/index.ts +6 -0
- package/src/interrupt-manager.ts +386 -145
- package/src/types.ts +133 -50
- package/src/video-generation-client.ts +33 -20
package/src/types.ts
CHANGED
|
@@ -9,6 +9,7 @@ import type {
|
|
|
9
9
|
DocumentPart,
|
|
10
10
|
ImagePart,
|
|
11
11
|
InferSchemaType,
|
|
12
|
+
InterruptDefinition,
|
|
12
13
|
InferToolInput,
|
|
13
14
|
InferToolOutput,
|
|
14
15
|
InputSchemaOf,
|
|
@@ -86,6 +87,51 @@ export interface GenericAGUIInterrupt extends BoundInterruptBase {
|
|
|
86
87
|
resolveInterrupt: (payload: unknown) => void
|
|
87
88
|
}
|
|
88
89
|
|
|
90
|
+
type InterruptResponseInput<TDefinition> =
|
|
91
|
+
TDefinition extends InterruptDefinition<any, any, infer TResponseSchema, any>
|
|
92
|
+
? InferSchemaType<TResponseSchema>
|
|
93
|
+
: never
|
|
94
|
+
|
|
95
|
+
type RegisteredGenericInterruptFor<
|
|
96
|
+
TDefinition extends InterruptDefinition<any, any, any, any>,
|
|
97
|
+
> =
|
|
98
|
+
TDefinition extends InterruptDefinition<
|
|
99
|
+
infer TDefinitionId,
|
|
100
|
+
any,
|
|
101
|
+
any,
|
|
102
|
+
infer TPayload
|
|
103
|
+
>
|
|
104
|
+
? BoundInterruptBase & {
|
|
105
|
+
readonly kind: 'generic'
|
|
106
|
+
readonly definitionId: TDefinitionId
|
|
107
|
+
readonly key: string
|
|
108
|
+
readonly payload: TPayload | undefined
|
|
109
|
+
readonly binding: Readonly<
|
|
110
|
+
Extract<InterruptBinding, { kind: 'generic' }> & {
|
|
111
|
+
definitionId: TDefinitionId
|
|
112
|
+
key: string
|
|
113
|
+
batchIndex: number
|
|
114
|
+
}
|
|
115
|
+
>
|
|
116
|
+
resolveInterrupt: (
|
|
117
|
+
response: InterruptResponseInput<TDefinition>,
|
|
118
|
+
) => void
|
|
119
|
+
}
|
|
120
|
+
: never
|
|
121
|
+
|
|
122
|
+
export type RegisteredGenericInterrupt<
|
|
123
|
+
TInterrupts extends ReadonlyArray<InterruptDefinition<any, any, any, any>>,
|
|
124
|
+
> = TInterrupts[number] extends infer TDefinition
|
|
125
|
+
? TDefinition extends InterruptDefinition<any, any, any, any>
|
|
126
|
+
? RegisteredGenericInterruptFor<TDefinition>
|
|
127
|
+
: never
|
|
128
|
+
: never
|
|
129
|
+
|
|
130
|
+
/** A bound generic interrupt for one `defineInterrupt()` definition. */
|
|
131
|
+
export type GenericInterrupt<
|
|
132
|
+
TDefinition extends InterruptDefinition<any, any, any, any>,
|
|
133
|
+
> = RegisteredGenericInterruptFor<TDefinition>
|
|
134
|
+
|
|
89
135
|
/**
|
|
90
136
|
* An interrupt that arrived on the stream carrying no resume binding this
|
|
91
137
|
* client understands — no `tanstack:interruptBinding`, or one written at a
|
|
@@ -98,7 +144,10 @@ export interface GenericAGUIInterrupt extends BoundInterruptBase {
|
|
|
98
144
|
* send an answer no one is waiting for. Render it, or route it to whatever
|
|
99
145
|
* actually owns the pause.
|
|
100
146
|
*/
|
|
101
|
-
export interface UnboundInterrupt extends
|
|
147
|
+
export interface UnboundInterrupt extends Omit<
|
|
148
|
+
BoundInterruptBase,
|
|
149
|
+
'cancel' | 'clearResolution'
|
|
150
|
+
> {
|
|
102
151
|
readonly kind: 'unbound'
|
|
103
152
|
readonly binding?: undefined
|
|
104
153
|
readonly canResolve: false
|
|
@@ -188,18 +237,37 @@ type ApprovalInterrupts<TTools extends ReadonlyArray<AnyClientTool>> =
|
|
|
188
237
|
// union.
|
|
189
238
|
export type ChatInterrupt<
|
|
190
239
|
TTools extends ReadonlyArray<AnyClientTool> = ReadonlyArray<AnyClientTool>,
|
|
191
|
-
|
|
240
|
+
TInterrupts extends ReadonlyArray<InterruptDefinition<any, any, any, any>> =
|
|
241
|
+
readonly [],
|
|
242
|
+
> =
|
|
243
|
+
| GenericAGUIInterrupt
|
|
244
|
+
| RegisteredGenericInterrupt<TInterrupts>
|
|
245
|
+
| UnboundInterrupt
|
|
246
|
+
| ApprovalInterrupts<TTools>
|
|
247
|
+
|
|
248
|
+
export type ResolvableChatInterrupt<
|
|
249
|
+
TTools extends ReadonlyArray<AnyClientTool> = ReadonlyArray<AnyClientTool>,
|
|
250
|
+
TInterrupts extends ReadonlyArray<InterruptDefinition<any, any, any, any>> =
|
|
251
|
+
readonly [],
|
|
252
|
+
> =
|
|
253
|
+
| GenericAGUIInterrupt
|
|
254
|
+
| RegisteredGenericInterrupt<TInterrupts>
|
|
255
|
+
| ApprovalInterrupts<TTools>
|
|
192
256
|
|
|
193
257
|
export type BoundInterrupts<
|
|
194
258
|
TTools extends ReadonlyArray<AnyClientTool> = ReadonlyArray<AnyClientTool>,
|
|
195
|
-
|
|
259
|
+
TInterrupts extends ReadonlyArray<InterruptDefinition<any, any, any, any>> =
|
|
260
|
+
readonly [],
|
|
261
|
+
> = ReadonlyArray<ChatInterrupt<TTools, TInterrupts>>
|
|
196
262
|
|
|
197
263
|
export interface ChatInterruptState<
|
|
198
264
|
TTools extends ReadonlyArray<AnyClientTool> = ReadonlyArray<AnyClientTool>,
|
|
265
|
+
TInterrupts extends ReadonlyArray<InterruptDefinition<any, any, any, any>> =
|
|
266
|
+
readonly [],
|
|
199
267
|
> {
|
|
200
|
-
readonly interrupts: BoundInterrupts<TTools>
|
|
268
|
+
readonly interrupts: BoundInterrupts<TTools, TInterrupts>
|
|
201
269
|
/** @deprecated Use `interrupts`. Same snapshot today. */
|
|
202
|
-
readonly pendingInterrupts: BoundInterrupts<TTools>
|
|
270
|
+
readonly pendingInterrupts: BoundInterrupts<TTools, TInterrupts>
|
|
203
271
|
readonly interruptErrors: ReadonlyArray<BatchInterruptError>
|
|
204
272
|
readonly resuming: boolean
|
|
205
273
|
}
|
|
@@ -628,6 +696,36 @@ export type ChatPersistenceOption<
|
|
|
628
696
|
TTools extends ReadonlyArray<AnyClientTool> = any,
|
|
629
697
|
> = boolean | ChatClientPersistence<TTools>
|
|
630
698
|
|
|
699
|
+
/**
|
|
700
|
+
* The `persistence` / `threadId` pairing for `ChatClient` and the chat hooks.
|
|
701
|
+
*
|
|
702
|
+
* Persistence that is on (`true` or a storage adapter) requires a `threadId`.
|
|
703
|
+
* A minted id changes every reload, so nothing would restore. The compiler
|
|
704
|
+
* asks for the conversation id instead.
|
|
705
|
+
*
|
|
706
|
+
* Omit `persistence`, or set it to `false`, and `threadId` stays optional.
|
|
707
|
+
* The client then mints one after mount for the wire and DevTools.
|
|
708
|
+
*
|
|
709
|
+
* Intersect this onto `ChatClientOptions`. Do not apply a later plain `Omit`
|
|
710
|
+
* to that type: it collapses the union and the requirement disappears. Use
|
|
711
|
+
* {@link DistributedOmit}.
|
|
712
|
+
*/
|
|
713
|
+
export type ChatPersistenceOptions<
|
|
714
|
+
TTools extends ReadonlyArray<AnyClientTool> = any,
|
|
715
|
+
> =
|
|
716
|
+
| {
|
|
717
|
+
persistence: true
|
|
718
|
+
threadId: string
|
|
719
|
+
}
|
|
720
|
+
| {
|
|
721
|
+
persistence: ChatClientPersistence<TTools>
|
|
722
|
+
threadId: string
|
|
723
|
+
}
|
|
724
|
+
| {
|
|
725
|
+
persistence?: false | undefined
|
|
726
|
+
threadId?: string
|
|
727
|
+
}
|
|
728
|
+
|
|
631
729
|
type IsUnknown<T> = unknown extends T
|
|
632
730
|
? [T] extends [unknown]
|
|
633
731
|
? true
|
|
@@ -712,49 +810,14 @@ export type ClientContextOptionFromTools<TTools, TContext> = [
|
|
|
712
810
|
export interface ChatClientBaseOptions<
|
|
713
811
|
TTools extends ReadonlyArray<AnyClientTool> = any,
|
|
714
812
|
TContext = unknown,
|
|
813
|
+
TInterrupts extends ReadonlyArray<InterruptDefinition<any, any, any, any>> =
|
|
814
|
+
readonly [],
|
|
715
815
|
> {
|
|
716
816
|
/**
|
|
717
817
|
* Initial messages to populate the chat
|
|
718
818
|
*/
|
|
719
819
|
initialMessages?: Array<UIMessage<TTools>>
|
|
720
820
|
|
|
721
|
-
/**
|
|
722
|
-
* How this chat persists across reloads. See {@link ChatPersistenceOption}.
|
|
723
|
-
*
|
|
724
|
-
* - Omit or `false`: ephemeral, in-memory only.
|
|
725
|
-
* - `true`: server-authoritative. The client caches nothing and hydrates the
|
|
726
|
-
* thread from the server by its `threadId` on mount (needs a connection with
|
|
727
|
-
* a `hydrate` handler). Big transcripts never touch the browser, and the same
|
|
728
|
-
* thread opens the same way on another device.
|
|
729
|
-
* - a {@link ChatClientPersistence} adapter: client-authoritative. The combined
|
|
730
|
-
* {@link ChatPersistedState} record (transcript plus resume pointer) is cached
|
|
731
|
-
* in the browser, restoring the transcript, pending interrupts, and an
|
|
732
|
-
* in-flight run on reload.
|
|
733
|
-
*
|
|
734
|
-
* Use `initialResumeSnapshot` for a host-supplied in-memory rehydrate instead.
|
|
735
|
-
*/
|
|
736
|
-
persistence?: ChatPersistenceOption<TTools>
|
|
737
|
-
|
|
738
|
-
/**
|
|
739
|
-
* Optional storage-key override for this chat instance, and the devtools
|
|
740
|
-
* instance id. Persistence keys on `threadId` by default; set `id` only when
|
|
741
|
-
* you need the persisted record keyed separately from the wire thread.
|
|
742
|
-
* Prefer a stable `threadId` for the common case.
|
|
743
|
-
*
|
|
744
|
-
* The framework hooks (`useChat` / `createChat`) do NOT expose `id`: a hook's
|
|
745
|
-
* identity is its `threadId`. This lower-level escape hatch exists only for
|
|
746
|
-
* direct `ChatClient` construction.
|
|
747
|
-
*/
|
|
748
|
-
id?: string
|
|
749
|
-
|
|
750
|
-
/**
|
|
751
|
-
* The conversation id for this chat, stable across sends and reloads. It is
|
|
752
|
-
* the AG-UI thread key on the wire AND the key client persistence stores the
|
|
753
|
-
* conversation under, so set a stable `threadId` to have a reload restore the
|
|
754
|
-
* same conversation. If omitted, a unique thread id is generated per session.
|
|
755
|
-
*/
|
|
756
|
-
threadId?: string
|
|
757
|
-
|
|
758
821
|
/**
|
|
759
822
|
* Initial resumable run state, useful when rehydrating a persisted client
|
|
760
823
|
* after a full page reload. This restores the client-side interrupt
|
|
@@ -871,7 +934,7 @@ export interface ChatClientBaseOptions<
|
|
|
871
934
|
*/
|
|
872
935
|
onResumeStateChange?: (
|
|
873
936
|
resumeState: ChatResumeState | null,
|
|
874
|
-
pendingInterrupts: BoundInterrupts<TTools>,
|
|
937
|
+
pendingInterrupts: BoundInterrupts<TTools, TInterrupts>,
|
|
875
938
|
) => void
|
|
876
939
|
|
|
877
940
|
/**
|
|
@@ -880,8 +943,15 @@ export interface ChatClientBaseOptions<
|
|
|
880
943
|
*/
|
|
881
944
|
onRunIdChange?: (runId: string | null) => void
|
|
882
945
|
|
|
883
|
-
/**
|
|
884
|
-
|
|
946
|
+
/**
|
|
947
|
+
* Callback when the immutable interrupt state snapshot changes.
|
|
948
|
+
* Snapshot restoration passes `{ source: 'hydrate' }`; streamed and
|
|
949
|
+
* client-initiated updates pass `{ source: 'live' }`.
|
|
950
|
+
*/
|
|
951
|
+
onInterruptStateChange?: (
|
|
952
|
+
state: ChatInterruptState<TTools, TInterrupts>,
|
|
953
|
+
context: { source: 'hydrate' | 'live' },
|
|
954
|
+
) => void
|
|
885
955
|
|
|
886
956
|
/**
|
|
887
957
|
* Callback when a custom event is received from a server-side tool.
|
|
@@ -903,6 +973,9 @@ export interface ChatClientBaseOptions<
|
|
|
903
973
|
*/
|
|
904
974
|
tools?: TTools
|
|
905
975
|
|
|
976
|
+
/** First-party generic interrupts this client can type and resolve. */
|
|
977
|
+
interrupts?: TInterrupts
|
|
978
|
+
|
|
906
979
|
/**
|
|
907
980
|
* Devtools hook metadata for this client instance.
|
|
908
981
|
*/
|
|
@@ -932,14 +1005,21 @@ export interface ChatClientBaseOptions<
|
|
|
932
1005
|
|
|
933
1006
|
/**
|
|
934
1007
|
* Options for `ChatClient`. Exactly one of `connection` or `fetcher` must be
|
|
935
|
-
* provided — the type-level XOR is enforced via `ChatTransport`.
|
|
1008
|
+
* provided — the type-level XOR is enforced via `ChatTransport`. Persistence
|
|
1009
|
+
* that is on requires a `threadId` via {@link ChatPersistenceOptions}.
|
|
936
1010
|
*/
|
|
937
1011
|
export type ChatClientOptions<
|
|
938
1012
|
TTools extends ReadonlyArray<AnyClientTool> = any,
|
|
939
1013
|
TContext = InferredClientContext<TTools>,
|
|
940
|
-
|
|
1014
|
+
TInterrupts extends ReadonlyArray<InterruptDefinition<any, any, any, any>> =
|
|
1015
|
+
readonly [],
|
|
1016
|
+
> = DistributedOmit<
|
|
1017
|
+
ChatClientBaseOptions<TTools, TContext, TInterrupts>,
|
|
1018
|
+
'context'
|
|
1019
|
+
> &
|
|
941
1020
|
ClientContextOptionFromTools<TTools, TContext> &
|
|
942
|
-
ChatTransport
|
|
1021
|
+
ChatTransport &
|
|
1022
|
+
ChatPersistenceOptions<TTools>
|
|
943
1023
|
|
|
944
1024
|
export interface ChatRequestBody {
|
|
945
1025
|
messages: Array<ModelMessage>
|
|
@@ -986,9 +1066,12 @@ export function clientTools<const T extends Array<AnyClientTool>>(
|
|
|
986
1066
|
export function createChatClientOptions<
|
|
987
1067
|
const TTools extends ReadonlyArray<AnyClientTool>,
|
|
988
1068
|
TContext = InferredClientContext<TTools>,
|
|
1069
|
+
const TInterrupts extends ReadonlyArray<
|
|
1070
|
+
InterruptDefinition<any, any, any, any>
|
|
1071
|
+
> = readonly [],
|
|
989
1072
|
>(
|
|
990
|
-
options: ChatClientOptions<TTools, TContext>,
|
|
991
|
-
): ChatClientOptions<TTools, TContext> {
|
|
1073
|
+
options: ChatClientOptions<TTools, TContext, TInterrupts>,
|
|
1074
|
+
): ChatClientOptions<TTools, TContext, TInterrupts> {
|
|
992
1075
|
return options
|
|
993
1076
|
}
|
|
994
1077
|
|
|
@@ -25,8 +25,10 @@ import type {
|
|
|
25
25
|
import type {
|
|
26
26
|
GenerationClientState,
|
|
27
27
|
GenerationFetcher,
|
|
28
|
+
GenerationPersistenceOptions,
|
|
28
29
|
GenerationResumeSnapshot,
|
|
29
30
|
GenerationResumeState,
|
|
31
|
+
GenerationTransport,
|
|
30
32
|
VideoGenerateInput,
|
|
31
33
|
VideoGenerateResult,
|
|
32
34
|
VideoGenerationClientOptions,
|
|
@@ -111,10 +113,10 @@ export class VideoGenerationClient<TOutput = VideoGenerateResult> {
|
|
|
111
113
|
private readonly fetcher:
|
|
112
114
|
| GenerationFetcher<VideoGenerateInput, VideoGenerateResult>
|
|
113
115
|
| undefined
|
|
114
|
-
private
|
|
116
|
+
private uniqueId: string
|
|
115
117
|
private readonly devtoolsMetadata: AIDevtoolsClientMetadata
|
|
116
118
|
private readonly devtoolsBridge: VideoDevtoolsBridge<TOutput>
|
|
117
|
-
private
|
|
119
|
+
private threadId: string
|
|
118
120
|
// Server-driven mode (`persistence: true`): no local snapshot store; on mount
|
|
119
121
|
// the client hydrates the last generation for `threadId` from the server.
|
|
120
122
|
private readonly serverDriven: boolean = false
|
|
@@ -137,22 +139,17 @@ export class VideoGenerationClient<TOutput = VideoGenerateResult> {
|
|
|
137
139
|
private serverHydrationStarted = false
|
|
138
140
|
|
|
139
141
|
constructor(
|
|
140
|
-
options:
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
}
|
|
147
|
-
),
|
|
142
|
+
options: Omit<
|
|
143
|
+
VideoGenerationClientOptions<TOutput>,
|
|
144
|
+
'persistence' | 'threadId'
|
|
145
|
+
> &
|
|
146
|
+
GenerationPersistenceOptions &
|
|
147
|
+
GenerationTransport<VideoGenerateInput, VideoGenerateResult>,
|
|
148
148
|
) {
|
|
149
|
-
// `threadId` is the
|
|
150
|
-
//
|
|
151
|
-
this.
|
|
152
|
-
|
|
153
|
-
// The wire/hydration thread key. Server-driven mode needs a stable key, so
|
|
154
|
-
// prefer an explicit `threadId`, then legacy `id`, then a generated id.
|
|
155
|
-
this.threadId = options.threadId ?? this.uniqueId
|
|
149
|
+
// `threadId` is the only identity. Do not mint a random id during
|
|
150
|
+
// construct: hooks build this client during render.
|
|
151
|
+
this.threadId = options.threadId ?? ''
|
|
152
|
+
this.uniqueId = this.threadId
|
|
156
153
|
this.connection = options.connection
|
|
157
154
|
this.fetcher = options.fetcher
|
|
158
155
|
this.hydrateGenerationHandler = options.hydrateGeneration
|
|
@@ -192,10 +189,17 @@ export class VideoGenerationClient<TOutput = VideoGenerateResult> {
|
|
|
192
189
|
}
|
|
193
190
|
|
|
194
191
|
private buildDevtoolsBridgeOptions(): VideoDevtoolsBridgeOptions<TOutput> {
|
|
192
|
+
const client = this
|
|
195
193
|
return {
|
|
196
|
-
hookId
|
|
197
|
-
|
|
198
|
-
|
|
194
|
+
get hookId() {
|
|
195
|
+
return client.uniqueId
|
|
196
|
+
},
|
|
197
|
+
get clientId() {
|
|
198
|
+
return client.uniqueId
|
|
199
|
+
},
|
|
200
|
+
get threadId() {
|
|
201
|
+
return client.threadId
|
|
202
|
+
},
|
|
199
203
|
metadata: this.devtoolsMetadata,
|
|
200
204
|
getCoreState: () => ({
|
|
201
205
|
input: this.input,
|
|
@@ -211,6 +215,7 @@ export class VideoGenerationClient<TOutput = VideoGenerateResult> {
|
|
|
211
215
|
}
|
|
212
216
|
|
|
213
217
|
mountDevtools(): void {
|
|
218
|
+
this.ensureThreadId()
|
|
214
219
|
// Mounting revives a disposed client. Framework hooks call this from
|
|
215
220
|
// their mount effect, so a dispose → remount cycle (e.g. React
|
|
216
221
|
// StrictMode's mount → cleanup → mount replay against the same memoized
|
|
@@ -677,6 +682,14 @@ export class VideoGenerationClient<TOutput = VideoGenerateResult> {
|
|
|
677
682
|
}
|
|
678
683
|
}
|
|
679
684
|
|
|
685
|
+
private ensureThreadId(): string {
|
|
686
|
+
if (!this.threadId) {
|
|
687
|
+
this.threadId = this.generateUniqueId('video')
|
|
688
|
+
}
|
|
689
|
+
this.uniqueId = this.threadId
|
|
690
|
+
return this.threadId
|
|
691
|
+
}
|
|
692
|
+
|
|
680
693
|
private generateUniqueId(prefix: string): string {
|
|
681
694
|
return `${prefix}-${Date.now()}-${Math.random().toString(36).substring(7)}`
|
|
682
695
|
}
|