@tanstack/ai-client 0.26.0 → 0.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/byok/client.d.ts +52 -0
- package/dist/esm/byok/client.js +300 -0
- package/dist/esm/byok/client.js.map +1 -0
- package/dist/esm/byok/passkey.d.ts +43 -0
- package/dist/esm/byok/passkey.js +251 -0
- package/dist/esm/byok/passkey.js.map +1 -0
- package/dist/esm/byok/resolve.d.ts +8 -0
- package/dist/esm/byok/resolve.js +20 -0
- package/dist/esm/byok/resolve.js.map +1 -0
- package/dist/esm/byok/storage.d.ts +15 -0
- package/dist/esm/byok/storage.js +16 -0
- package/dist/esm/byok/storage.js.map +1 -0
- package/dist/esm/byok.d.ts +8 -0
- package/dist/esm/byok.js +5 -0
- package/dist/esm/chat-client.d.ts +16 -3
- package/dist/esm/chat-client.js +49 -31
- package/dist/esm/chat-client.js.map +1 -1
- package/dist/esm/connection-adapters.d.ts +2 -0
- package/dist/esm/connection-adapters.js +33 -9
- package/dist/esm/connection-adapters.js.map +1 -1
- package/dist/esm/generation-client.d.ts +3 -1
- package/dist/esm/generation-client.js +23 -4
- package/dist/esm/generation-client.js.map +1 -1
- package/dist/esm/generation-types.d.ts +14 -0
- package/dist/esm/generation-types.js.map +1 -1
- package/dist/esm/interrupt-manager.d.ts +1 -0
- package/dist/esm/interrupt-manager.js +15 -6
- package/dist/esm/interrupt-manager.js.map +1 -1
- package/dist/esm/sse-parser.js +8 -1
- package/dist/esm/sse-parser.js.map +1 -1
- package/dist/esm/types.d.ts +29 -3
- package/dist/esm/types.js.map +1 -1
- package/dist/esm/video-generation-client.d.ts +3 -1
- package/dist/esm/video-generation-client.js +25 -6
- package/dist/esm/video-generation-client.js.map +1 -1
- package/package.json +6 -2
- package/src/byok/client.ts +360 -0
- package/src/byok/passkey.ts +399 -0
- package/src/byok/resolve.ts +30 -0
- package/src/byok/storage.ts +29 -0
- package/src/byok.ts +20 -0
- package/src/chat-client.ts +95 -65
- package/src/connection-adapters.ts +41 -12
- package/src/generation-client.ts +48 -4
- package/src/generation-types.ts +16 -0
- package/src/interrupt-manager.ts +20 -6
- package/src/sse-parser.ts +10 -0
- package/src/types.ts +31 -3
- package/src/video-generation-client.ts +45 -4
package/src/types.ts
CHANGED
|
@@ -25,6 +25,7 @@ import type {
|
|
|
25
25
|
UIResourcePart,
|
|
26
26
|
VideoPart,
|
|
27
27
|
} from '@tanstack/ai/client'
|
|
28
|
+
import type { ByokClient } from './byok'
|
|
28
29
|
import type { ConnectionAdapter } from './connection-adapters'
|
|
29
30
|
import type { AIDevtoolsClientMetadata } from './devtools'
|
|
30
31
|
import type { ChatDevtoolsBridgeFactory } from './devtools-noop'
|
|
@@ -291,6 +292,8 @@ export interface ChatFetcherInput {
|
|
|
291
292
|
export interface ChatFetcherOptions {
|
|
292
293
|
/** Fires when `stop()` is called or the request is superseded. */
|
|
293
294
|
signal: AbortSignal
|
|
295
|
+
/** Extra request headers for this run (e.g. BYOK keys). */
|
|
296
|
+
headers?: Record<string, string>
|
|
294
297
|
}
|
|
295
298
|
|
|
296
299
|
/**
|
|
@@ -474,9 +477,9 @@ export interface QueueConfig {
|
|
|
474
477
|
* function form (no `batch` via function). Per-call `sendOptions.whenBusy`
|
|
475
478
|
* overrides the strategy for that send.
|
|
476
479
|
*
|
|
477
|
-
* Actions match {@link WhenBusy}
|
|
478
|
-
*
|
|
479
|
-
*
|
|
480
|
+
* Actions match {@link WhenBusy}. Concurrent streams are not supported.
|
|
481
|
+
* `pending.id` is the id that will be stored if the action is `'queue'`
|
|
482
|
+
* (safe to pass to `cancelQueued`).
|
|
480
483
|
*/
|
|
481
484
|
export type QueueStrategy = (ctx: {
|
|
482
485
|
pending: QueuedMessage
|
|
@@ -491,6 +494,16 @@ export type QueueOption = WhenBusy | QueueConfig | QueueStrategy
|
|
|
491
494
|
export interface SendMessageOptions {
|
|
492
495
|
/** Overrides the configured `whenBusy` for this one send. */
|
|
493
496
|
whenBusy?: WhenBusy
|
|
497
|
+
/**
|
|
498
|
+
* Extra JSON merged into this request's wire `forwardedProps`.
|
|
499
|
+
* Shallow merge: `{ ...chatBody, ...positionalBody, ...body }`.
|
|
500
|
+
* This field wins on key collisions.
|
|
501
|
+
*
|
|
502
|
+
* Framework hooks (`useChat`, `injectChat`, `createChat`) expose
|
|
503
|
+
* `sendMessage(content, options)` with no positional body, so this field
|
|
504
|
+
* is the per-call body channel on those surfaces.
|
|
505
|
+
*/
|
|
506
|
+
body?: Record<string, any>
|
|
494
507
|
}
|
|
495
508
|
|
|
496
509
|
/**
|
|
@@ -862,6 +875,21 @@ export interface ChatClientBaseOptions<
|
|
|
862
875
|
*/
|
|
863
876
|
body?: Record<string, any>
|
|
864
877
|
|
|
878
|
+
/**
|
|
879
|
+
* Optional BYOK keyring. On each send the client prepares the resolved
|
|
880
|
+
* provider and stamps `x-byok-*` request headers. Keys never go in the body.
|
|
881
|
+
*/
|
|
882
|
+
byok?: ByokClient
|
|
883
|
+
|
|
884
|
+
/**
|
|
885
|
+
* Optional provider id for this chat. If it returns a provider slug,
|
|
886
|
+
* only that key is prepared and sent. Otherwise the merged `provider`
|
|
887
|
+
* from `forwardedProps`, `body`, and per-call `sendMessage` `body` is
|
|
888
|
+
* used. Later sources win. If no slug resolves, the send throws
|
|
889
|
+
* instead of attaching every stored key.
|
|
890
|
+
*/
|
|
891
|
+
byokProvider?: () => string | undefined
|
|
892
|
+
|
|
865
893
|
/**
|
|
866
894
|
* Client-local runtime context passed to client tool implementations.
|
|
867
895
|
*
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
import { ByokBlockedError, ByokMissingError } from '@tanstack/ai/byok'
|
|
2
|
+
import {
|
|
3
|
+
prepareResolvedByokHeaders,
|
|
4
|
+
resolveByokProviderId,
|
|
5
|
+
} from './byok/resolve'
|
|
1
6
|
import {
|
|
2
7
|
GENERATION_EVENTS,
|
|
3
8
|
GENERATION_STREAM_TRUNCATED_MESSAGE,
|
|
@@ -12,6 +17,7 @@ import { createNoOpVideoDevtoolsBridge } from './devtools-noop'
|
|
|
12
17
|
import { parseSSEResponse } from './sse-parser'
|
|
13
18
|
import { restoreInboundChunk } from '@tanstack/ai/client'
|
|
14
19
|
import type { StreamChunk } from '@tanstack/ai/client'
|
|
20
|
+
import type { ByokClient } from './byok'
|
|
15
21
|
import type {
|
|
16
22
|
ConnectConnectionAdapter,
|
|
17
23
|
GenerationHydrationResult,
|
|
@@ -122,6 +128,8 @@ export class VideoGenerationClient<TOutput = VideoGenerateResult> {
|
|
|
122
128
|
// the client hydrates the last generation for `threadId` from the server.
|
|
123
129
|
private readonly serverDriven: boolean = false
|
|
124
130
|
private body: Record<string, any>
|
|
131
|
+
private byok: ByokClient | undefined
|
|
132
|
+
private byokProvider: (() => string | undefined) | undefined
|
|
125
133
|
|
|
126
134
|
private result: TOutput | null = null
|
|
127
135
|
private input: VideoGenerateInput | null = null
|
|
@@ -156,6 +164,8 @@ export class VideoGenerationClient<TOutput = VideoGenerateResult> {
|
|
|
156
164
|
this.hydrateGenerationHandler = options.hydrateGeneration
|
|
157
165
|
this.joinRunHandler = options.joinRun
|
|
158
166
|
this.body = options.body ?? {}
|
|
167
|
+
this.byok = options.byok
|
|
168
|
+
this.byokProvider = options.byokProvider
|
|
159
169
|
// `persistence` is `false`/omitted (ephemeral) or `true` (server-driven:
|
|
160
170
|
// hydrate the last generation for `threadId` from the server on mount).
|
|
161
171
|
this.serverDriven = options.persistence === true
|
|
@@ -258,15 +268,24 @@ export class VideoGenerationClient<TOutput = VideoGenerateResult> {
|
|
|
258
268
|
const { signal } = abortController
|
|
259
269
|
|
|
260
270
|
try {
|
|
271
|
+
let headers: Record<string, string> | undefined
|
|
272
|
+
if (this.byok) {
|
|
273
|
+
const provider = resolveByokProviderId(
|
|
274
|
+
this.byokProvider,
|
|
275
|
+
this.body.provider,
|
|
276
|
+
)
|
|
277
|
+
headers = await prepareResolvedByokHeaders(this.byok, provider)
|
|
278
|
+
}
|
|
279
|
+
|
|
261
280
|
if (this.fetcher) {
|
|
262
|
-
await this.generateWithFetcher(input, signal, runId)
|
|
281
|
+
await this.generateWithFetcher(input, signal, runId, headers)
|
|
263
282
|
} else if (this.connection) {
|
|
264
283
|
const mergedData = { ...this.body, ...input }
|
|
265
284
|
const stream = this.connection.connect(
|
|
266
285
|
[],
|
|
267
286
|
mergedData,
|
|
268
287
|
signal,
|
|
269
|
-
this.createRunContext(runId),
|
|
288
|
+
this.createRunContext(runId, headers),
|
|
270
289
|
)
|
|
271
290
|
await this.processStream(stream, runId, signal)
|
|
272
291
|
} else {
|
|
@@ -284,6 +303,12 @@ export class VideoGenerationClient<TOutput = VideoGenerateResult> {
|
|
|
284
303
|
} catch (err: unknown) {
|
|
285
304
|
if (signal.aborted) return
|
|
286
305
|
const error = err instanceof Error ? err : new Error(String(err))
|
|
306
|
+
if (error instanceof ByokMissingError) {
|
|
307
|
+
this.byok?.request(error.provider, 'missing')
|
|
308
|
+
}
|
|
309
|
+
if (error instanceof ByokBlockedError && error.reason === 'locked') {
|
|
310
|
+
this.byok?.request(error.provider, 'locked')
|
|
311
|
+
}
|
|
287
312
|
this.setError(error)
|
|
288
313
|
this.setStatus('error')
|
|
289
314
|
this.recordResumeSnapshotError(error)
|
|
@@ -309,11 +334,15 @@ export class VideoGenerationClient<TOutput = VideoGenerateResult> {
|
|
|
309
334
|
input: VideoGenerateInput,
|
|
310
335
|
signal: AbortSignal,
|
|
311
336
|
runId: string,
|
|
337
|
+
headers?: Record<string, string>,
|
|
312
338
|
): Promise<void> {
|
|
313
339
|
if (!this.fetcher) return
|
|
314
340
|
|
|
315
341
|
// Fetcher returns a completed result directly, or a Response with SSE body
|
|
316
|
-
const result = await this.fetcher(
|
|
342
|
+
const result = await this.fetcher(
|
|
343
|
+
input,
|
|
344
|
+
headers === undefined ? { signal } : { signal, headers },
|
|
345
|
+
)
|
|
317
346
|
if (signal.aborted) return
|
|
318
347
|
|
|
319
348
|
if (result instanceof Response) {
|
|
@@ -470,6 +499,8 @@ export class VideoGenerationClient<TOutput = VideoGenerateResult> {
|
|
|
470
499
|
Pick<
|
|
471
500
|
VideoGenerationClientOptions<TOutput>,
|
|
472
501
|
| 'body'
|
|
502
|
+
| 'byok'
|
|
503
|
+
| 'byokProvider'
|
|
473
504
|
| 'onResult'
|
|
474
505
|
| 'onError'
|
|
475
506
|
| 'onProgress'
|
|
@@ -482,6 +513,12 @@ export class VideoGenerationClient<TOutput = VideoGenerateResult> {
|
|
|
482
513
|
if (options.body !== undefined) {
|
|
483
514
|
this.body = options.body ?? {}
|
|
484
515
|
}
|
|
516
|
+
if (options.byok !== undefined) {
|
|
517
|
+
this.byok = options.byok
|
|
518
|
+
}
|
|
519
|
+
if (options.byokProvider !== undefined) {
|
|
520
|
+
this.byokProvider = options.byokProvider
|
|
521
|
+
}
|
|
485
522
|
if (options.onResult !== undefined) {
|
|
486
523
|
this.callbacksRef.onResult = options.onResult
|
|
487
524
|
}
|
|
@@ -694,10 +731,14 @@ export class VideoGenerationClient<TOutput = VideoGenerateResult> {
|
|
|
694
731
|
return `${prefix}-${Date.now()}-${Math.random().toString(36).substring(7)}`
|
|
695
732
|
}
|
|
696
733
|
|
|
697
|
-
private createRunContext(
|
|
734
|
+
private createRunContext(
|
|
735
|
+
runId: string,
|
|
736
|
+
headers?: Record<string, string>,
|
|
737
|
+
): RunAgentInputContext {
|
|
698
738
|
return {
|
|
699
739
|
threadId: this.threadId,
|
|
700
740
|
runId,
|
|
741
|
+
...(headers ? { headers } : {}),
|
|
701
742
|
}
|
|
702
743
|
}
|
|
703
744
|
|