@tanstack/ai-client 0.25.1 → 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.
- package/README.md +2 -1
- package/dist/esm/chat-client.d.ts +2 -2
- package/dist/esm/chat-client.js +48 -29
- package/dist/esm/chat-client.js.map +1 -1
- package/dist/esm/connection-adapters.js +37 -21
- package/dist/esm/connection-adapters.js.map +1 -1
- package/dist/esm/generation-client.js +4 -2
- package/dist/esm/generation-client.js.map +1 -1
- package/dist/esm/generation-types.js +4 -2
- package/dist/esm/generation-types.js.map +1 -1
- package/dist/esm/types.d.ts +17 -0
- package/dist/esm/types.js.map +1 -1
- package/dist/esm/video-generation-client.js +4 -2
- package/dist/esm/video-generation-client.js.map +1 -1
- package/package.json +3 -3
- package/src/chat-client.ts +50 -17
- package/src/connection-adapters.ts +67 -41
- package/src/generation-client.ts +5 -5
- package/src/generation-types.ts +8 -2
- package/src/types.ts +17 -0
- package/src/video-generation-client.ts +5 -5
package/README.md
CHANGED
|
@@ -210,8 +210,9 @@ Official adapters include:
|
|
|
210
210
|
| ------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------ |
|
|
211
211
|
| [`@tanstack/ai-openrouter`](https://tanstack.com/ai/latest/docs/adapters/openrouter) | 300+ models through one OpenRouter API, with per-request cost tracking |
|
|
212
212
|
| [`@tanstack/ai-openai`](https://tanstack.com/ai/latest/docs/adapters/openai) | OpenAI chat, image, video, speech, transcription, realtime, and provider tools |
|
|
213
|
-
| [`@tanstack/ai-anthropic`](https://tanstack.com/ai/latest/docs/adapters/anthropic) | Anthropic Claude chat, thinking, tools, and
|
|
213
|
+
| [`@tanstack/ai-anthropic`](https://tanstack.com/ai/latest/docs/adapters/anthropic) | Anthropic Claude chat, thinking, tools, structured outputs, and Vertex Claude |
|
|
214
214
|
| [`@tanstack/ai-gemini`](https://tanstack.com/ai/latest/docs/adapters/gemini) | Google Gemini chat, image, speech, and audio generation |
|
|
215
|
+
| [`@tanstack/ai-vertex`](https://tanstack.com/ai/latest/docs/adapters/vertex) | Gemini on Vertex AI with regional endpoints and Google Cloud credentials |
|
|
215
216
|
| [`@tanstack/ai-ollama`](https://tanstack.com/ai/latest/docs/adapters/ollama) | Local Ollama models |
|
|
216
217
|
| [`@tanstack/ai-grok`](https://tanstack.com/ai/latest/docs/adapters/grok) | xAI Grok chat, images, and realtime |
|
|
217
218
|
| [`@tanstack/ai-groq`](https://tanstack.com/ai/latest/docs/adapters/groq) | Groq low-latency inference |
|
|
@@ -378,8 +378,8 @@ export declare class ChatClient<TTools extends ReadonlyArray<AnyClientTool> = an
|
|
|
378
378
|
private decideWhenBusy;
|
|
379
379
|
private enqueueMessage;
|
|
380
380
|
/**
|
|
381
|
-
* Normalize the message input to extract content
|
|
382
|
-
* Trims string content
|
|
381
|
+
* Normalize the message input to extract content, optional id, and
|
|
382
|
+
* optional metadata. String form has no metadata. Trims string content.
|
|
383
383
|
*/
|
|
384
384
|
private normalizeMessageInput;
|
|
385
385
|
/**
|
package/dist/esm/chat-client.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { createNoOpChatDevtoolsBridge } from "./devtools-noop.js";
|
|
2
|
-
import { fetcherToConnectionAdapter, getChunkRunId, normalizeConnectionAdapter } from "./connection-adapters.js";
|
|
2
|
+
import { fetcherToConnectionAdapter, getChunkRunId as getChunkRunId$1, normalizeConnectionAdapter } from "./connection-adapters.js";
|
|
3
3
|
import { ChatPersistor } from "./client-persistor.js";
|
|
4
4
|
import { ClearedStreamTracker } from "./cleared-stream-tracker.js";
|
|
5
5
|
import { InterruptManager } from "./interrupt-manager.js";
|
|
6
|
-
import { StreamProcessor, convertSchemaToJsonSchema, generateMessageId, isStandardSchema, normalizeToUIMessage, parseWithStandardSchema } from "@tanstack/ai/client";
|
|
6
|
+
import { StreamProcessor, convertSchemaToJsonSchema, generateMessageId, isStandardSchema, mergeMetadata, normalizeToUIMessage, parseWithStandardSchema, restoreInboundChunk, tanstackMetadata } from "@tanstack/ai/client";
|
|
7
7
|
//#region src/chat-client.ts
|
|
8
8
|
function assertUniqueInterruptDefinitions(interrupts) {
|
|
9
9
|
const ids = /* @__PURE__ */ new Set();
|
|
@@ -49,6 +49,7 @@ function normalizeQueueOption(option) {
|
|
|
49
49
|
* Merge a run of queued messages into a single send for `drain: 'batch'`.
|
|
50
50
|
* All-string content is joined with newlines; mixed/multimodal content is
|
|
51
51
|
* flattened into a single `ContentPart` array. The last item's `body` wins.
|
|
52
|
+
* Object-form metadata is merged last-write-wins per key.
|
|
52
53
|
*/
|
|
53
54
|
function mergeQueuedMessages(items) {
|
|
54
55
|
const body = items.at(-1)?.body;
|
|
@@ -62,17 +63,27 @@ function mergeQueuedMessages(items) {
|
|
|
62
63
|
...body !== void 0 ? { body } : {}
|
|
63
64
|
};
|
|
64
65
|
const parts = [];
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
66
|
+
let metadata;
|
|
67
|
+
for (const item of items) {
|
|
68
|
+
if (typeof item.content === "string") {
|
|
69
|
+
parts.push({
|
|
70
|
+
type: "text",
|
|
71
|
+
content: item.content
|
|
72
|
+
});
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
if (typeof item.content.content === "string") parts.push({
|
|
76
|
+
type: "text",
|
|
77
|
+
content: item.content.content
|
|
78
|
+
});
|
|
79
|
+
else parts.push(...item.content.content);
|
|
80
|
+
metadata = mergeMetadata(metadata, item.content.metadata);
|
|
81
|
+
}
|
|
74
82
|
return {
|
|
75
|
-
content: {
|
|
83
|
+
content: {
|
|
84
|
+
content: parts,
|
|
85
|
+
...metadata !== void 0 ? { metadata } : {}
|
|
86
|
+
},
|
|
76
87
|
...body !== void 0 ? { body } : {}
|
|
77
88
|
};
|
|
78
89
|
}
|
|
@@ -511,15 +522,15 @@ var ChatClient = class {
|
|
|
511
522
|
}
|
|
512
523
|
retireIgnoredClearedTerminalChunk(chunk) {
|
|
513
524
|
if (chunk.type !== "RUN_FINISHED" && chunk.type !== "RUN_ERROR") return;
|
|
514
|
-
const runId = getChunkRunId(chunk) ?? this.clearedStreamTracker.takeRunlessRunId();
|
|
525
|
+
const runId = getChunkRunId$1(chunk) ?? this.clearedStreamTracker.takeRunlessRunId();
|
|
515
526
|
if (!runId) return;
|
|
516
527
|
this.activeRunIds.delete(runId);
|
|
517
528
|
this.setSessionGenerating(this.activeRunIds.size > 0);
|
|
518
|
-
if (!getChunkRunId(chunk)) this.resolveProcessing();
|
|
529
|
+
if (!getChunkRunId$1(chunk)) this.resolveProcessing();
|
|
519
530
|
}
|
|
520
531
|
updateRunLifecycle(chunk, options) {
|
|
521
532
|
if (chunk.type === "RUN_STARTED") {
|
|
522
|
-
const chunkRunId = getChunkRunId(chunk) ?? chunk.runId;
|
|
533
|
+
const chunkRunId = getChunkRunId$1(chunk) ?? chunk.runId;
|
|
523
534
|
this.activeResumeThreadId = "threadId" in chunk && typeof chunk.threadId === "string" ? chunk.threadId : this.activeResumeThreadId;
|
|
524
535
|
this.activeResumeRunId = chunkRunId;
|
|
525
536
|
this.activeRunIds.add(chunkRunId);
|
|
@@ -532,7 +543,7 @@ var ChatClient = class {
|
|
|
532
543
|
return;
|
|
533
544
|
}
|
|
534
545
|
if (chunk.type !== "RUN_FINISHED" && chunk.type !== "RUN_ERROR") return;
|
|
535
|
-
const runId = getChunkRunId(chunk);
|
|
546
|
+
const runId = getChunkRunId$1(chunk);
|
|
536
547
|
if (runId) {
|
|
537
548
|
this.activeRunIds.delete(runId);
|
|
538
549
|
this.clearedStreamTracker.onRunSettled(runId);
|
|
@@ -552,7 +563,7 @@ var ChatClient = class {
|
|
|
552
563
|
observeInterruptState(chunk) {
|
|
553
564
|
if (chunk.type !== "RUN_FINISHED" && chunk.type !== "RUN_ERROR") return;
|
|
554
565
|
if (this.activeInterruptSubmission && chunk.type === "RUN_ERROR") return;
|
|
555
|
-
const runId = getChunkRunId(chunk);
|
|
566
|
+
const runId = getChunkRunId$1(chunk);
|
|
556
567
|
const threadId = "threadId" in chunk && typeof chunk.threadId === "string" ? chunk.threadId : this.activeResumeThreadId;
|
|
557
568
|
if (chunk.type === "RUN_FINISHED" && chunk.outcome?.type === "interrupt") {
|
|
558
569
|
const interruptedRunId = this.currentRunId ?? runId ?? this.activeResumeRunId ?? "";
|
|
@@ -943,11 +954,15 @@ var ChatClient = class {
|
|
|
943
954
|
if (last && last.role === "assistant") this.processor.setMessages(messages.slice(0, -1));
|
|
944
955
|
}
|
|
945
956
|
async processIncomingChunk(chunk, options) {
|
|
946
|
-
|
|
957
|
+
chunk = restoreInboundChunk(chunk);
|
|
958
|
+
if (chunk.type === "RUN_ERROR" && this.isActiveInterruptSubmissionFailure(chunk)) {
|
|
959
|
+
const interruptErrors = tanstackMetadata(chunk)?.interruptErrors;
|
|
960
|
+
this.interruptSubmissionFailure = { errors: Array.isArray(interruptErrors) ? interruptErrors : [] };
|
|
961
|
+
}
|
|
947
962
|
if (this.connectionStatus === "connecting") this.setConnectionStatus("connected");
|
|
948
963
|
if (this.clearedStreamTracker.shouldIgnoreChunk(chunk)) {
|
|
949
964
|
if (chunk.type === "RUN_FINISHED" || chunk.type === "RUN_ERROR") {
|
|
950
|
-
if (getChunkRunId(chunk)) this.updateRunLifecycle(chunk, { resolveProcessing: false });
|
|
965
|
+
if (getChunkRunId$1(chunk)) this.updateRunLifecycle(chunk, { resolveProcessing: false });
|
|
951
966
|
else this.drainIgnoredRunlessChunk(chunk);
|
|
952
967
|
this.retireIgnoredClearedTerminalChunk(chunk);
|
|
953
968
|
this.resolveJoinedRun(chunk);
|
|
@@ -959,21 +974,24 @@ var ChatClient = class {
|
|
|
959
974
|
this.processor.processChunk(chunk);
|
|
960
975
|
this.updateRunLifecycle(chunk);
|
|
961
976
|
this.observeInterruptState(chunk);
|
|
962
|
-
if (options?.defer !== false) await new Promise((resolve) => setTimeout(resolve, 0));
|
|
977
|
+
if (options?.defer !== false && (typeof document === "undefined" || !document.hidden)) await new Promise((resolve) => setTimeout(resolve, 0));
|
|
963
978
|
this.resolveJoinedRun(chunk);
|
|
964
979
|
}
|
|
965
980
|
isActiveInterruptSubmissionFailure(chunk) {
|
|
966
981
|
const submission = this.activeInterruptSubmission;
|
|
967
|
-
const errors = chunk
|
|
968
|
-
if (!submission || !errors || errors.length === 0) return false;
|
|
969
|
-
const runId = getChunkRunId(chunk);
|
|
982
|
+
const errors = tanstackMetadata(chunk)?.interruptErrors;
|
|
983
|
+
if (!submission || !Array.isArray(errors) || errors.length === 0) return false;
|
|
984
|
+
const runId = getChunkRunId$1(chunk);
|
|
970
985
|
if (runId !== void 0 && runId !== this.currentRunId) return false;
|
|
971
986
|
if (typeof chunk.threadId === "string" && chunk.threadId !== submission.threadId) return false;
|
|
972
|
-
return errors.every((error) =>
|
|
987
|
+
return errors.every((error) => {
|
|
988
|
+
if (error == null || typeof error !== "object" || typeof error.threadId !== "string" || typeof error.interruptedRunId !== "string" || typeof error.generation !== "number") return false;
|
|
989
|
+
return error.threadId === submission.threadId && error.interruptedRunId === submission.interruptedRunId && error.generation === submission.generation;
|
|
990
|
+
});
|
|
973
991
|
}
|
|
974
992
|
resolveJoinedRun(chunk) {
|
|
975
993
|
if (chunk.type !== "RUN_FINISHED" && chunk.type !== "RUN_ERROR") return;
|
|
976
|
-
const runId = getChunkRunId(chunk);
|
|
994
|
+
const runId = getChunkRunId$1(chunk);
|
|
977
995
|
if (runId === void 0) return;
|
|
978
996
|
const resolve = this.joinedRunWaiters.get(runId);
|
|
979
997
|
if (resolve === void 0) return;
|
|
@@ -1096,7 +1114,7 @@ var ChatClient = class {
|
|
|
1096
1114
|
try {
|
|
1097
1115
|
const normalizedContent = this.normalizeMessageInput(content);
|
|
1098
1116
|
this.pendingMessageBody = body;
|
|
1099
|
-
const userMessage = this.processor.addUserMessage(normalizedContent.content, normalizedContent.id);
|
|
1117
|
+
const userMessage = this.processor.addUserMessage(normalizedContent.content, normalizedContent.id, normalizedContent.metadata);
|
|
1100
1118
|
this.events.messageSent(userMessage.id, normalizedContent.content);
|
|
1101
1119
|
return await this.streamResponse();
|
|
1102
1120
|
} finally {
|
|
@@ -1149,14 +1167,15 @@ var ChatClient = class {
|
|
|
1149
1167
|
this.emitQueueChange();
|
|
1150
1168
|
}
|
|
1151
1169
|
/**
|
|
1152
|
-
* Normalize the message input to extract content
|
|
1153
|
-
* Trims string content
|
|
1170
|
+
* Normalize the message input to extract content, optional id, and
|
|
1171
|
+
* optional metadata. String form has no metadata. Trims string content.
|
|
1154
1172
|
*/
|
|
1155
1173
|
normalizeMessageInput(input) {
|
|
1156
1174
|
if (typeof input === "string") return { content: input.trim() };
|
|
1157
1175
|
return {
|
|
1158
1176
|
content: input.content,
|
|
1159
|
-
id: input.id
|
|
1177
|
+
id: input.id,
|
|
1178
|
+
...input.metadata !== void 0 ? { metadata: input.metadata } : {}
|
|
1160
1179
|
};
|
|
1161
1180
|
}
|
|
1162
1181
|
/**
|