@tanstack/ai-client 0.25.2 → 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/README.md +2 -1
- 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 +18 -5
- package/dist/esm/chat-client.js +96 -59
- package/dist/esm/chat-client.js.map +1 -1
- package/dist/esm/connection-adapters.d.ts +2 -0
- package/dist/esm/connection-adapters.js +70 -30
- package/dist/esm/connection-adapters.js.map +1 -1
- package/dist/esm/generation-client.d.ts +3 -1
- package/dist/esm/generation-client.js +27 -6
- package/dist/esm/generation-client.js.map +1 -1
- package/dist/esm/generation-types.d.ts +14 -0
- package/dist/esm/generation-types.js +4 -2
- 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 +46 -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 +29 -8
- package/dist/esm/video-generation-client.js.map +1 -1
- package/package.json +7 -3
- 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 +138 -77
- package/src/connection-adapters.ts +108 -53
- package/src/generation-client.ts +53 -9
- package/src/generation-types.ts +24 -2
- package/src/interrupt-manager.ts +20 -6
- package/src/sse-parser.ts +10 -0
- package/src/types.ts +48 -3
- package/src/video-generation-client.ts +50 -9
|
@@ -94,6 +94,8 @@ export interface RunAgentInputContext {
|
|
|
94
94
|
}>;
|
|
95
95
|
/** Arbitrary user-controlled passthrough data. */
|
|
96
96
|
forwardedProps?: Record<string, unknown>;
|
|
97
|
+
/** Extra request headers for this run (e.g. BYOK keys). POST only. */
|
|
98
|
+
headers?: Record<string, string>;
|
|
97
99
|
}
|
|
98
100
|
export interface ConnectConnectionAdapter {
|
|
99
101
|
/**
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createResponseStreamTextDecoder, getResponseStreamReader } from "./response-stream.js";
|
|
2
2
|
import { parseSseDataLine } from "./sse-utils.js";
|
|
3
|
-
import { EventType, uiMessagesToWire } from "@tanstack/ai/client";
|
|
3
|
+
import { EventType, getChunkRunId, restoreInboundChunk, tanstackMetadata, uiMessagesToWire, withTanstackMetadata } from "@tanstack/ai/client";
|
|
4
|
+
import { ByokMissingError, isByokMissingBody } from "@tanstack/ai/byok";
|
|
4
5
|
//#region src/connection-adapters.ts
|
|
5
6
|
/**
|
|
6
7
|
* Associates connect-wrapped chunks with the run they were produced under.
|
|
@@ -15,8 +16,8 @@ var chunkRunIds = /* @__PURE__ */ new WeakMap();
|
|
|
15
16
|
* (RUN_STARTED / RUN_FINISHED / RUN_ERROR carry one) and falling back to the
|
|
16
17
|
* run the connect wrapper stamped it with.
|
|
17
18
|
*/
|
|
18
|
-
function getChunkRunId(chunk) {
|
|
19
|
-
return chunkRunIds.get(chunk) ?? (
|
|
19
|
+
function getChunkRunId$1(chunk) {
|
|
20
|
+
return chunkRunIds.get(chunk) ?? getChunkRunId(chunk);
|
|
20
21
|
}
|
|
21
22
|
/**
|
|
22
23
|
* Thrown when an SSE/HTTP stream ends with a non-empty unterminated buffer.
|
|
@@ -206,6 +207,19 @@ async function* readStreamLines(reader, abortSignal) {
|
|
|
206
207
|
function isNdjsonEnvelope(value) {
|
|
207
208
|
return typeof value === "object" && value !== null && "chunk" in value && "id" in value && typeof value.id === "string" && !("type" in value);
|
|
208
209
|
}
|
|
210
|
+
/** Rebuild pre-wire extras after SSE/NDJSON ingest. */
|
|
211
|
+
function restoreInboundUsage(chunk) {
|
|
212
|
+
return restoreInboundChunk(chunk);
|
|
213
|
+
}
|
|
214
|
+
function sseChunkModel(chunk) {
|
|
215
|
+
const tanstackModel = tanstackMetadata(chunk)?.model;
|
|
216
|
+
if (typeof tanstackModel === "string") return tanstackModel;
|
|
217
|
+
const usage = "usage" in chunk ? chunk.usage : void 0;
|
|
218
|
+
if (Array.isArray(usage)) {
|
|
219
|
+
const model = usage[0]?.model;
|
|
220
|
+
if (typeof model === "string") return model;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
209
223
|
/**
|
|
210
224
|
* Parse SSE-format lines into stream events, pairing each chunk with the `id:`
|
|
211
225
|
* offset of the event it arrived on. Shared by the fetch- and XHR-backed SSE
|
|
@@ -234,20 +248,22 @@ async function* linesToSSEEvents(lines, fallbackIds) {
|
|
|
234
248
|
if (line.startsWith(":") || line.startsWith("event:") || line.startsWith("retry:")) continue;
|
|
235
249
|
const data = parseSseDataLine(line);
|
|
236
250
|
if (data === "[DONE]") {
|
|
237
|
-
yield { chunk: {
|
|
251
|
+
yield { chunk: withTanstackMetadata({
|
|
238
252
|
type: EventType.RUN_FINISHED,
|
|
239
253
|
threadId: lastThreadId ?? fallbackIds?.threadId ?? "",
|
|
240
254
|
runId: lastRunId ?? fallbackIds?.runId ?? "",
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
finishReason: "stop"
|
|
244
|
-
|
|
255
|
+
timestamp: Date.now()
|
|
256
|
+
}, {
|
|
257
|
+
finishReason: "stop",
|
|
258
|
+
...lastModel !== void 0 ? { model: lastModel } : {}
|
|
259
|
+
}) };
|
|
245
260
|
return;
|
|
246
261
|
}
|
|
247
|
-
const chunk = JSON.parse(data);
|
|
262
|
+
const chunk = restoreInboundUsage(JSON.parse(data));
|
|
248
263
|
if ("threadId" in chunk && typeof chunk.threadId === "string") lastThreadId = chunk.threadId;
|
|
249
264
|
if ("runId" in chunk && typeof chunk.runId === "string") lastRunId = chunk.runId;
|
|
250
|
-
|
|
265
|
+
const model = sseChunkModel(chunk);
|
|
266
|
+
if (model !== void 0) lastModel = model;
|
|
251
267
|
const id = pendingId;
|
|
252
268
|
pendingId = void 0;
|
|
253
269
|
yield {
|
|
@@ -266,14 +282,31 @@ async function* linesToNdjsonEvents(lines) {
|
|
|
266
282
|
for await (const line of lines) {
|
|
267
283
|
const parsed = JSON.parse(line);
|
|
268
284
|
if (isNdjsonEnvelope(parsed)) yield {
|
|
269
|
-
chunk: parsed.chunk,
|
|
285
|
+
chunk: restoreInboundUsage(parsed.chunk),
|
|
270
286
|
id: parsed.id
|
|
271
287
|
};
|
|
272
|
-
else yield { chunk: parsed };
|
|
288
|
+
else yield { chunk: restoreInboundUsage(parsed) };
|
|
273
289
|
}
|
|
274
290
|
}
|
|
275
|
-
function assertResponseOk(response) {
|
|
276
|
-
if (
|
|
291
|
+
async function assertResponseOk(response) {
|
|
292
|
+
if (response.ok) return;
|
|
293
|
+
if (response.status === 401) {
|
|
294
|
+
const body = await response.clone().json().catch(() => null);
|
|
295
|
+
if (isByokMissingBody(body)) throw new ByokMissingError(body.error.provider);
|
|
296
|
+
}
|
|
297
|
+
throw new Error(`HTTP error! status: ${response.status} ${response.statusText}`);
|
|
298
|
+
}
|
|
299
|
+
function errorFromXhrStatus(xhr) {
|
|
300
|
+
if (xhr.status === 401) {
|
|
301
|
+
let parsed = null;
|
|
302
|
+
try {
|
|
303
|
+
parsed = JSON.parse(xhr.responseText);
|
|
304
|
+
} catch {
|
|
305
|
+
parsed = null;
|
|
306
|
+
}
|
|
307
|
+
if (isByokMissingBody(parsed)) return new ByokMissingError(parsed.error.provider);
|
|
308
|
+
}
|
|
309
|
+
return /* @__PURE__ */ new Error(`XHR error! status: ${xhr.status} ${xhr.statusText}`);
|
|
277
310
|
}
|
|
278
311
|
/**
|
|
279
312
|
* GET the hydration endpoint for a thread and parse its JSON `{ messages,
|
|
@@ -291,7 +324,7 @@ async function fetchThreadHydration(fetchClient, url, headers, credentials, thre
|
|
|
291
324
|
},
|
|
292
325
|
credentials
|
|
293
326
|
});
|
|
294
|
-
assertResponseOk(response);
|
|
327
|
+
await assertResponseOk(response);
|
|
295
328
|
const data = await response.json();
|
|
296
329
|
const activeRun = data.activeRun && typeof data.activeRun.runId === "string" ? { runId: data.activeRun.runId } : null;
|
|
297
330
|
const interrupts = data.interrupts && typeof data.interrupts.runId === "string" && Array.isArray(data.interrupts.pending) && data.interrupts.pending.length > 0 ? {
|
|
@@ -320,7 +353,7 @@ async function fetchGenerationHydration(fetchClient, url, headers, credentials,
|
|
|
320
353
|
},
|
|
321
354
|
credentials
|
|
322
355
|
});
|
|
323
|
-
assertResponseOk(response);
|
|
356
|
+
await assertResponseOk(response);
|
|
324
357
|
const raw = await response.json();
|
|
325
358
|
if (raw === null) return {
|
|
326
359
|
resumeSnapshot: null,
|
|
@@ -336,12 +369,12 @@ async function fetchGenerationHydration(fetchClient, url, headers, credentials,
|
|
|
336
369
|
}
|
|
337
370
|
/** Yield SSE stream events (chunk + offset) from a fetch Response body. */
|
|
338
371
|
async function* responseToSSEEvents(response, abortSignal, fallbackIds) {
|
|
339
|
-
assertResponseOk(response);
|
|
372
|
+
await assertResponseOk(response);
|
|
340
373
|
yield* linesToSSEEvents(readStreamLines(getResponseStreamReader(response), abortSignal), fallbackIds);
|
|
341
374
|
}
|
|
342
375
|
/** Yield NDJSON stream events (chunk + offset) from a fetch Response body. */
|
|
343
376
|
async function* responseToNdjsonEvents(response, abortSignal) {
|
|
344
|
-
assertResponseOk(response);
|
|
377
|
+
await assertResponseOk(response);
|
|
345
378
|
yield* linesToNdjsonEvents(readStreamLines(getResponseStreamReader(response), abortSignal));
|
|
346
379
|
}
|
|
347
380
|
async function* responseToSSEChunks(response, abortSignal) {
|
|
@@ -481,14 +514,15 @@ function normalizeConnectionAdapter(connection) {
|
|
|
481
514
|
if (chunk.type === "RUN_FINISHED" || chunk.type === "RUN_ERROR") hasTerminalEvent = true;
|
|
482
515
|
push(chunk, runContext?.runId);
|
|
483
516
|
}
|
|
484
|
-
if (!abortSignal?.aborted && !hasTerminalEvent) push({
|
|
517
|
+
if (!abortSignal?.aborted && !hasTerminalEvent) push(withTanstackMetadata({
|
|
485
518
|
type: EventType.RUN_FINISHED,
|
|
486
519
|
threadId: requireSyntheticId(upstreamThreadId ?? runContext?.threadId, "threadId"),
|
|
487
520
|
runId: requireSyntheticId(upstreamRunId ?? runContext?.runId, "runId"),
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
finishReason: "stop"
|
|
491
|
-
|
|
521
|
+
timestamp: Date.now()
|
|
522
|
+
}, {
|
|
523
|
+
finishReason: "stop",
|
|
524
|
+
model: "connect-wrapper"
|
|
525
|
+
}), runContext?.runId);
|
|
492
526
|
} catch (err) {
|
|
493
527
|
if (!abortSignal?.aborted && !hasTerminalEvent) try {
|
|
494
528
|
const message = err instanceof Error ? err.message : "Unknown error in connect()";
|
|
@@ -572,6 +606,7 @@ function fetchServerSentEvents(url, options = {}) {
|
|
|
572
606
|
const requestHeaders = {
|
|
573
607
|
"Content-Type": "application/json",
|
|
574
608
|
...mergeHeaders(resolvedOptions.headers),
|
|
609
|
+
...mergeHeaders(runContext?.headers),
|
|
575
610
|
...runIdHeader(runContext?.runId)
|
|
576
611
|
};
|
|
577
612
|
const requestBody = buildRunAgentInputBody(messages, data, runContext, resolvedOptions);
|
|
@@ -657,6 +692,7 @@ function fetchHttpStream(url, options = {}) {
|
|
|
657
692
|
const requestHeaders = {
|
|
658
693
|
"Content-Type": "application/json",
|
|
659
694
|
...mergeHeaders(resolvedOptions.headers),
|
|
695
|
+
...mergeHeaders(runContext?.headers),
|
|
660
696
|
...runIdHeader(runContext?.runId)
|
|
661
697
|
};
|
|
662
698
|
const requestBody = buildRunAgentInputBody(messages, data, runContext, resolvedOptions);
|
|
@@ -723,7 +759,7 @@ function readXhrLines(xhr, abortSignal) {
|
|
|
723
759
|
};
|
|
724
760
|
const enqueueDelta = () => {
|
|
725
761
|
if (xhr.status !== 0 && (xhr.status < 200 || xhr.status >= 300)) {
|
|
726
|
-
error =
|
|
762
|
+
error = errorFromXhrStatus(xhr);
|
|
727
763
|
done = true;
|
|
728
764
|
return;
|
|
729
765
|
}
|
|
@@ -740,7 +776,7 @@ function readXhrLines(xhr, abortSignal) {
|
|
|
740
776
|
};
|
|
741
777
|
const finish = () => {
|
|
742
778
|
enqueueDelta();
|
|
743
|
-
if (xhr.status !== 0 && (xhr.status < 200 || xhr.status >= 300)) error =
|
|
779
|
+
if (xhr.status !== 0 && (xhr.status < 200 || xhr.status >= 300)) error = errorFromXhrStatus(xhr);
|
|
744
780
|
else if (buffer.trim() && !aborted) error = new StreamTruncatedError();
|
|
745
781
|
done = true;
|
|
746
782
|
wake();
|
|
@@ -797,6 +833,7 @@ function createConfiguredXhrRequest(url, options, messages, data, runContext, me
|
|
|
797
833
|
const requestHeaders = {
|
|
798
834
|
"Content-Type": "application/json",
|
|
799
835
|
...mergeHeaders(options.headers),
|
|
836
|
+
...mergeHeaders(method === "POST" ? runContext?.headers : void 0),
|
|
800
837
|
...method === "POST" ? runIdHeader(runContext?.runId) : {},
|
|
801
838
|
...extraHeaders
|
|
802
839
|
};
|
|
@@ -1018,12 +1055,12 @@ function webSocket(url, options = {}) {
|
|
|
1018
1055
|
}
|
|
1019
1056
|
if (isPingFrame(parsed)) return;
|
|
1020
1057
|
const envelopeId = isNdjsonEnvelope(parsed) ? parsed.id : void 0;
|
|
1021
|
-
const chunk = isNdjsonEnvelope(parsed) ? parsed.chunk : parsed;
|
|
1058
|
+
const chunk = restoreInboundUsage(isNdjsonEnvelope(parsed) ? parsed.chunk : parsed);
|
|
1022
1059
|
const session = currentSession;
|
|
1023
1060
|
if (session) {
|
|
1024
1061
|
if (session.tracker.note(envelopeId) === "duplicate") return;
|
|
1025
1062
|
session.progressed = true;
|
|
1026
|
-
if (session.runId === void 0) session.runId = getChunkRunId(chunk);
|
|
1063
|
+
if (session.runId === void 0) session.runId = getChunkRunId$1(chunk);
|
|
1027
1064
|
if (chunk.type === "RUN_FINISHED" || chunk.type === "RUN_ERROR") session.sawTerminal = true;
|
|
1028
1065
|
}
|
|
1029
1066
|
for (const l of listeners) l.push(chunk);
|
|
@@ -1126,7 +1163,7 @@ function webSocket(url, options = {}) {
|
|
|
1126
1163
|
return;
|
|
1127
1164
|
}
|
|
1128
1165
|
if (isPingFrame(parsed)) return;
|
|
1129
|
-
pipe.push(isNdjsonEnvelope(parsed) ? parsed.chunk : parsed);
|
|
1166
|
+
pipe.push(restoreInboundUsage(isNdjsonEnvelope(parsed) ? parsed.chunk : parsed));
|
|
1130
1167
|
};
|
|
1131
1168
|
ws.onclose = (event) => {
|
|
1132
1169
|
if (event?.code === 1e3) {
|
|
@@ -1195,7 +1232,10 @@ function fetcherToConnectionAdapter(fetcher) {
|
|
|
1195
1232
|
runId: runContext.runId,
|
|
1196
1233
|
...runContext.parentRunId !== void 0 ? { parentRunId: runContext.parentRunId } : {},
|
|
1197
1234
|
...runContext.resume !== void 0 ? { resume: runContext.resume } : {}
|
|
1198
|
-
}, {
|
|
1235
|
+
}, {
|
|
1236
|
+
signal: abortSignal,
|
|
1237
|
+
headers: runContext.headers
|
|
1238
|
+
});
|
|
1199
1239
|
if (result instanceof Response) yield* responseToSSEChunks(result, abortSignal);
|
|
1200
1240
|
else yield* abortableIterable(result, abortSignal);
|
|
1201
1241
|
} };
|
|
@@ -1263,6 +1303,6 @@ function rpcStream(rpcCall, handlers) {
|
|
|
1263
1303
|
};
|
|
1264
1304
|
}
|
|
1265
1305
|
//#endregion
|
|
1266
|
-
export { DurableStreamIncompleteError, StreamReconnectLimitError, StreamTruncatedError, createReconnectTracker, fetchHttpStream, fetchServerSentEvents, fetcherToConnectionAdapter, getChunkRunId, normalizeConnectionAdapter, rpcStream, stream, webSocket, xhrHttpStream, xhrServerSentEvents };
|
|
1306
|
+
export { DurableStreamIncompleteError, StreamReconnectLimitError, StreamTruncatedError, createReconnectTracker, fetchHttpStream, fetchServerSentEvents, fetcherToConnectionAdapter, getChunkRunId$1 as getChunkRunId, normalizeConnectionAdapter, rpcStream, stream, webSocket, xhrHttpStream, xhrServerSentEvents };
|
|
1267
1307
|
|
|
1268
1308
|
//# sourceMappingURL=connection-adapters.js.map
|