@tanstack/ai-client 0.25.2 → 0.26.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.
@@ -1,6 +1,6 @@
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
4
  //#region src/connection-adapters.ts
5
5
  /**
6
6
  * Associates connect-wrapped chunks with the run they were produced under.
@@ -15,8 +15,8 @@ var chunkRunIds = /* @__PURE__ */ new WeakMap();
15
15
  * (RUN_STARTED / RUN_FINISHED / RUN_ERROR carry one) and falling back to the
16
16
  * run the connect wrapper stamped it with.
17
17
  */
18
- function getChunkRunId(chunk) {
19
- return chunkRunIds.get(chunk) ?? ("runId" in chunk && typeof chunk.runId === "string" ? chunk.runId : void 0);
18
+ function getChunkRunId$1(chunk) {
19
+ return chunkRunIds.get(chunk) ?? getChunkRunId(chunk);
20
20
  }
21
21
  /**
22
22
  * Thrown when an SSE/HTTP stream ends with a non-empty unterminated buffer.
@@ -206,6 +206,19 @@ async function* readStreamLines(reader, abortSignal) {
206
206
  function isNdjsonEnvelope(value) {
207
207
  return typeof value === "object" && value !== null && "chunk" in value && "id" in value && typeof value.id === "string" && !("type" in value);
208
208
  }
209
+ /** Rebuild pre-wire extras after SSE/NDJSON ingest. */
210
+ function restoreInboundUsage(chunk) {
211
+ return restoreInboundChunk(chunk);
212
+ }
213
+ function sseChunkModel(chunk) {
214
+ const tanstackModel = tanstackMetadata(chunk)?.model;
215
+ if (typeof tanstackModel === "string") return tanstackModel;
216
+ const usage = "usage" in chunk ? chunk.usage : void 0;
217
+ if (Array.isArray(usage)) {
218
+ const model = usage[0]?.model;
219
+ if (typeof model === "string") return model;
220
+ }
221
+ }
209
222
  /**
210
223
  * Parse SSE-format lines into stream events, pairing each chunk with the `id:`
211
224
  * offset of the event it arrived on. Shared by the fetch- and XHR-backed SSE
@@ -234,20 +247,22 @@ async function* linesToSSEEvents(lines, fallbackIds) {
234
247
  if (line.startsWith(":") || line.startsWith("event:") || line.startsWith("retry:")) continue;
235
248
  const data = parseSseDataLine(line);
236
249
  if (data === "[DONE]") {
237
- yield { chunk: {
250
+ yield { chunk: withTanstackMetadata({
238
251
  type: EventType.RUN_FINISHED,
239
252
  threadId: lastThreadId ?? fallbackIds?.threadId ?? "",
240
253
  runId: lastRunId ?? fallbackIds?.runId ?? "",
241
- model: lastModel ?? "",
242
- timestamp: Date.now(),
243
- finishReason: "stop"
244
- } };
254
+ timestamp: Date.now()
255
+ }, {
256
+ finishReason: "stop",
257
+ ...lastModel !== void 0 ? { model: lastModel } : {}
258
+ }) };
245
259
  return;
246
260
  }
247
- const chunk = JSON.parse(data);
261
+ const chunk = restoreInboundUsage(JSON.parse(data));
248
262
  if ("threadId" in chunk && typeof chunk.threadId === "string") lastThreadId = chunk.threadId;
249
263
  if ("runId" in chunk && typeof chunk.runId === "string") lastRunId = chunk.runId;
250
- if ("model" in chunk && typeof chunk.model === "string") lastModel = chunk.model;
264
+ const model = sseChunkModel(chunk);
265
+ if (model !== void 0) lastModel = model;
251
266
  const id = pendingId;
252
267
  pendingId = void 0;
253
268
  yield {
@@ -266,10 +281,10 @@ async function* linesToNdjsonEvents(lines) {
266
281
  for await (const line of lines) {
267
282
  const parsed = JSON.parse(line);
268
283
  if (isNdjsonEnvelope(parsed)) yield {
269
- chunk: parsed.chunk,
284
+ chunk: restoreInboundUsage(parsed.chunk),
270
285
  id: parsed.id
271
286
  };
272
- else yield { chunk: parsed };
287
+ else yield { chunk: restoreInboundUsage(parsed) };
273
288
  }
274
289
  }
275
290
  function assertResponseOk(response) {
@@ -481,14 +496,15 @@ function normalizeConnectionAdapter(connection) {
481
496
  if (chunk.type === "RUN_FINISHED" || chunk.type === "RUN_ERROR") hasTerminalEvent = true;
482
497
  push(chunk, runContext?.runId);
483
498
  }
484
- if (!abortSignal?.aborted && !hasTerminalEvent) push({
499
+ if (!abortSignal?.aborted && !hasTerminalEvent) push(withTanstackMetadata({
485
500
  type: EventType.RUN_FINISHED,
486
501
  threadId: requireSyntheticId(upstreamThreadId ?? runContext?.threadId, "threadId"),
487
502
  runId: requireSyntheticId(upstreamRunId ?? runContext?.runId, "runId"),
488
- model: "connect-wrapper",
489
- timestamp: Date.now(),
490
- finishReason: "stop"
491
- }, runContext?.runId);
503
+ timestamp: Date.now()
504
+ }, {
505
+ finishReason: "stop",
506
+ model: "connect-wrapper"
507
+ }), runContext?.runId);
492
508
  } catch (err) {
493
509
  if (!abortSignal?.aborted && !hasTerminalEvent) try {
494
510
  const message = err instanceof Error ? err.message : "Unknown error in connect()";
@@ -1018,12 +1034,12 @@ function webSocket(url, options = {}) {
1018
1034
  }
1019
1035
  if (isPingFrame(parsed)) return;
1020
1036
  const envelopeId = isNdjsonEnvelope(parsed) ? parsed.id : void 0;
1021
- const chunk = isNdjsonEnvelope(parsed) ? parsed.chunk : parsed;
1037
+ const chunk = restoreInboundUsage(isNdjsonEnvelope(parsed) ? parsed.chunk : parsed);
1022
1038
  const session = currentSession;
1023
1039
  if (session) {
1024
1040
  if (session.tracker.note(envelopeId) === "duplicate") return;
1025
1041
  session.progressed = true;
1026
- if (session.runId === void 0) session.runId = getChunkRunId(chunk);
1042
+ if (session.runId === void 0) session.runId = getChunkRunId$1(chunk);
1027
1043
  if (chunk.type === "RUN_FINISHED" || chunk.type === "RUN_ERROR") session.sawTerminal = true;
1028
1044
  }
1029
1045
  for (const l of listeners) l.push(chunk);
@@ -1126,7 +1142,7 @@ function webSocket(url, options = {}) {
1126
1142
  return;
1127
1143
  }
1128
1144
  if (isPingFrame(parsed)) return;
1129
- pipe.push(isNdjsonEnvelope(parsed) ? parsed.chunk : parsed);
1145
+ pipe.push(restoreInboundUsage(isNdjsonEnvelope(parsed) ? parsed.chunk : parsed));
1130
1146
  };
1131
1147
  ws.onclose = (event) => {
1132
1148
  if (event?.code === 1e3) {
@@ -1263,6 +1279,6 @@ function rpcStream(rpcCall, handlers) {
1263
1279
  };
1264
1280
  }
1265
1281
  //#endregion
1266
- export { DurableStreamIncompleteError, StreamReconnectLimitError, StreamTruncatedError, createReconnectTracker, fetchHttpStream, fetchServerSentEvents, fetcherToConnectionAdapter, getChunkRunId, normalizeConnectionAdapter, rpcStream, stream, webSocket, xhrHttpStream, xhrServerSentEvents };
1282
+ export { DurableStreamIncompleteError, StreamReconnectLimitError, StreamTruncatedError, createReconnectTracker, fetchHttpStream, fetchServerSentEvents, fetcherToConnectionAdapter, getChunkRunId$1 as getChunkRunId, normalizeConnectionAdapter, rpcStream, stream, webSocket, xhrHttpStream, xhrServerSentEvents };
1267
1283
 
1268
1284
  //# sourceMappingURL=connection-adapters.js.map