@tanstack/ai 0.22.0 → 0.23.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/activities/chat/index.js +1 -0
- package/dist/esm/activities/chat/index.js.map +1 -1
- package/dist/esm/activities/chat/middleware/types.d.ts +10 -11
- package/dist/esm/activities/chat/stream/processor.d.ts +15 -0
- package/dist/esm/activities/chat/stream/processor.js +56 -0
- package/dist/esm/activities/chat/stream/processor.js.map +1 -1
- package/dist/esm/client.d.ts +44 -0
- package/dist/esm/client.js +65 -0
- package/dist/esm/client.js.map +1 -0
- package/dist/esm/types.d.ts +33 -6
- package/package.json +7 -3
- package/skills/ai-core/adapter-configuration/SKILL.md +1 -1
- package/skills/ai-core/debug-logging/SKILL.md +1 -1
- package/src/activities/chat/index.ts +1 -0
- package/src/activities/chat/middleware/types.ts +10 -13
- package/src/activities/chat/stream/processor.ts +92 -0
- package/src/client.ts +132 -0
- package/src/types.ts +35 -6
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { JSONSchema, ModelMessage, StreamChunk, Tool, ToolCall } from '../../../types.js';
|
|
1
|
+
import { JSONSchema, ModelMessage, StreamChunk, Tool, ToolCall, UsageTotals } from '../../../types.js';
|
|
2
2
|
import { SystemPrompt } from '../../../system-prompts.js';
|
|
3
3
|
/**
|
|
4
4
|
* Phase of the chat middleware lifecycle.
|
|
@@ -20,6 +20,8 @@ export interface ChatMiddlewareContext {
|
|
|
20
20
|
requestId: string;
|
|
21
21
|
/** Unique identifier for this stream */
|
|
22
22
|
streamId: string;
|
|
23
|
+
/** AG-UI run identifier for correlating client and server events */
|
|
24
|
+
runId: string;
|
|
23
25
|
/**
|
|
24
26
|
* AG-UI thread identifier — a stable per-conversation ID used to
|
|
25
27
|
* correlate client and server devtools events. Resolves to the
|
|
@@ -201,11 +203,12 @@ export interface ToolPhaseCompleteInfo {
|
|
|
201
203
|
/**
|
|
202
204
|
* Token usage statistics passed to the onUsage hook.
|
|
203
205
|
* Extracted from the RUN_FINISHED chunk when usage data is present.
|
|
206
|
+
*
|
|
207
|
+
* Includes optional provider-reported `cost`/`costDetails` (see {@link UsageTotals}).
|
|
208
|
+
* Kept as an interface extending `UsageTotals` to preserve declaration merging for
|
|
209
|
+
* this publicly exported type.
|
|
204
210
|
*/
|
|
205
|
-
export interface UsageInfo {
|
|
206
|
-
promptTokens: number;
|
|
207
|
-
completionTokens: number;
|
|
208
|
-
totalTokens: number;
|
|
211
|
+
export interface UsageInfo extends UsageTotals {
|
|
209
212
|
}
|
|
210
213
|
/**
|
|
211
214
|
* Information passed to onFinish.
|
|
@@ -217,12 +220,8 @@ export interface FinishInfo {
|
|
|
217
220
|
duration: number;
|
|
218
221
|
/** Final accumulated text content */
|
|
219
222
|
content: string;
|
|
220
|
-
/** Final usage totals, if available */
|
|
221
|
-
usage?:
|
|
222
|
-
promptTokens: number;
|
|
223
|
-
completionTokens: number;
|
|
224
|
-
totalTokens: number;
|
|
225
|
-
} | undefined;
|
|
223
|
+
/** Final usage totals, if available (optionally including provider-reported cost) */
|
|
224
|
+
usage?: UsageTotals | undefined;
|
|
226
225
|
}
|
|
227
226
|
/**
|
|
228
227
|
* Information passed to onAbort.
|
|
@@ -25,6 +25,17 @@ export interface StreamProcessorEvents {
|
|
|
25
25
|
onTextUpdate?: (messageId: string, content: string) => void;
|
|
26
26
|
onToolCallStateChange?: (messageId: string, toolCallId: string, state: ToolCallState, args: string) => void;
|
|
27
27
|
onThinkingUpdate?: (messageId: string, stepId: string, content: string) => void;
|
|
28
|
+
onStructuredOutputChange?: (args: {
|
|
29
|
+
phase: 'start' | 'update' | 'complete' | 'error';
|
|
30
|
+
messageId: string;
|
|
31
|
+
status: 'streaming' | 'complete' | 'error';
|
|
32
|
+
raw: string;
|
|
33
|
+
partial?: unknown;
|
|
34
|
+
data?: unknown;
|
|
35
|
+
reasoning?: string;
|
|
36
|
+
errorMessage?: string;
|
|
37
|
+
delta?: string;
|
|
38
|
+
}) => void;
|
|
28
39
|
}
|
|
29
40
|
/**
|
|
30
41
|
* Options for StreamProcessor
|
|
@@ -69,6 +80,7 @@ export declare class StreamProcessor {
|
|
|
69
80
|
private pendingManualMessageId;
|
|
70
81
|
private pendingThinkingStepId;
|
|
71
82
|
private readonly structuredMessageIds;
|
|
83
|
+
private readonly structuredOutputUpdateBatches;
|
|
72
84
|
private readonly activeRuns;
|
|
73
85
|
private finishReason;
|
|
74
86
|
private hasError;
|
|
@@ -359,6 +371,9 @@ export declare class StreamProcessor {
|
|
|
359
371
|
* @see docs/chat-architecture.md#uimessage-part-ordering-invariants — Replace vs. push logic
|
|
360
372
|
*/
|
|
361
373
|
private emitTextUpdateForMessage;
|
|
374
|
+
private queueStructuredOutputUpdate;
|
|
375
|
+
private flushStructuredOutputUpdate;
|
|
376
|
+
private emitStructuredOutputChange;
|
|
362
377
|
/**
|
|
363
378
|
* Emit messages change event
|
|
364
379
|
*/
|
|
@@ -2,6 +2,7 @@ import { generateMessageId, uiMessageToModelMessages } from "../messages.js";
|
|
|
2
2
|
import { defaultJSONParser } from "./json-parser.js";
|
|
3
3
|
import { updateToolCallWithOutput, updateToolResultPart, updateToolCallApprovalResponse, appendStructuredOutputDelta, updateToolCallPart, errorStructuredOutputPart, updateThinkingPart, completeStructuredOutputPart, updateToolCallApproval, updateTextPart } from "./message-updaters.js";
|
|
4
4
|
import { ImmediateStrategy } from "./strategies.js";
|
|
5
|
+
const STRUCTURED_OUTPUT_UPDATE_BATCH_SIZE = 12;
|
|
5
6
|
class StreamProcessor {
|
|
6
7
|
chunkStrategy;
|
|
7
8
|
events;
|
|
@@ -16,6 +17,7 @@ class StreamProcessor {
|
|
|
16
17
|
pendingManualMessageId = null;
|
|
17
18
|
pendingThinkingStepId = null;
|
|
18
19
|
structuredMessageIds = /* @__PURE__ */ new Set();
|
|
20
|
+
structuredOutputUpdateBatches = /* @__PURE__ */ new Map();
|
|
19
21
|
// Run tracking (for concurrent run safety)
|
|
20
22
|
activeRuns = /* @__PURE__ */ new Set();
|
|
21
23
|
// Shared stream state
|
|
@@ -205,6 +207,9 @@ class StreamProcessor {
|
|
|
205
207
|
for (const id of this.structuredMessageIds) {
|
|
206
208
|
if (!keptIds.has(id)) this.structuredMessageIds.delete(id);
|
|
207
209
|
}
|
|
210
|
+
for (const id of this.structuredOutputUpdateBatches.keys()) {
|
|
211
|
+
if (!keptIds.has(id)) this.structuredOutputUpdateBatches.delete(id);
|
|
212
|
+
}
|
|
208
213
|
for (const id of this.messageStates.keys()) {
|
|
209
214
|
if (!keptIds.has(id)) this.messageStates.delete(id);
|
|
210
215
|
}
|
|
@@ -226,6 +231,7 @@ class StreamProcessor {
|
|
|
226
231
|
this.activeMessageIds.clear();
|
|
227
232
|
this.toolCallToMessage.clear();
|
|
228
233
|
this.structuredMessageIds.clear();
|
|
234
|
+
this.structuredOutputUpdateBatches.clear();
|
|
229
235
|
this.pendingManualMessageId = null;
|
|
230
236
|
this.emitMessagesChange();
|
|
231
237
|
}
|
|
@@ -579,6 +585,7 @@ class StreamProcessor {
|
|
|
579
585
|
delta2
|
|
580
586
|
);
|
|
581
587
|
state.totalTextContent += delta2;
|
|
588
|
+
this.queueStructuredOutputUpdate(messageId, delta2);
|
|
582
589
|
this.emitMessagesChange();
|
|
583
590
|
}
|
|
584
591
|
return;
|
|
@@ -846,12 +853,14 @@ class StreamProcessor {
|
|
|
846
853
|
);
|
|
847
854
|
}
|
|
848
855
|
if (this.structuredMessageIds.has(messageId)) {
|
|
856
|
+
this.flushStructuredOutputUpdate(messageId);
|
|
849
857
|
this.messages = errorStructuredOutputPart(
|
|
850
858
|
this.messages,
|
|
851
859
|
messageId,
|
|
852
860
|
errorMessage
|
|
853
861
|
);
|
|
854
862
|
this.structuredMessageIds.delete(messageId);
|
|
863
|
+
this.emitStructuredOutputChange(messageId, "error");
|
|
855
864
|
this.emitMessagesChange();
|
|
856
865
|
}
|
|
857
866
|
this.events.onError?.(new Error(errorMessage));
|
|
@@ -994,6 +1003,13 @@ class StreamProcessor {
|
|
|
994
1003
|
if (targetId) {
|
|
995
1004
|
this.ensureAssistantMessage(targetId);
|
|
996
1005
|
this.structuredMessageIds.add(targetId);
|
|
1006
|
+
this.structuredOutputUpdateBatches.delete(targetId);
|
|
1007
|
+
this.events.onStructuredOutputChange?.({
|
|
1008
|
+
phase: "start",
|
|
1009
|
+
messageId: targetId,
|
|
1010
|
+
status: "streaming",
|
|
1011
|
+
raw: ""
|
|
1012
|
+
});
|
|
997
1013
|
}
|
|
998
1014
|
return;
|
|
999
1015
|
}
|
|
@@ -1001,6 +1017,7 @@ class StreamProcessor {
|
|
|
1001
1017
|
const v = chunk.value;
|
|
1002
1018
|
const targetId = v.messageId ?? messageId;
|
|
1003
1019
|
if (targetId) {
|
|
1020
|
+
this.flushStructuredOutputUpdate(targetId);
|
|
1004
1021
|
this.messages = completeStructuredOutputPart(
|
|
1005
1022
|
this.messages,
|
|
1006
1023
|
targetId,
|
|
@@ -1009,6 +1026,7 @@ class StreamProcessor {
|
|
|
1009
1026
|
v.reasoning
|
|
1010
1027
|
);
|
|
1011
1028
|
this.structuredMessageIds.delete(targetId);
|
|
1029
|
+
this.emitStructuredOutputChange(targetId, "complete");
|
|
1012
1030
|
this.emitMessagesChange();
|
|
1013
1031
|
}
|
|
1014
1032
|
}
|
|
@@ -1132,6 +1150,40 @@ class StreamProcessor {
|
|
|
1132
1150
|
this.emitMessagesChange();
|
|
1133
1151
|
this.events.onTextUpdate?.(messageId, state.currentSegmentText);
|
|
1134
1152
|
}
|
|
1153
|
+
queueStructuredOutputUpdate(messageId, delta) {
|
|
1154
|
+
const existing = this.structuredOutputUpdateBatches.get(messageId);
|
|
1155
|
+
const next = {
|
|
1156
|
+
delta: `${existing?.delta ?? ""}${delta}`,
|
|
1157
|
+
chunkCount: (existing?.chunkCount ?? 0) + 1
|
|
1158
|
+
};
|
|
1159
|
+
this.structuredOutputUpdateBatches.set(messageId, next);
|
|
1160
|
+
if (next.chunkCount >= STRUCTURED_OUTPUT_UPDATE_BATCH_SIZE) {
|
|
1161
|
+
this.flushStructuredOutputUpdate(messageId);
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
flushStructuredOutputUpdate(messageId) {
|
|
1165
|
+
const batch = this.structuredOutputUpdateBatches.get(messageId);
|
|
1166
|
+
if (!batch || batch.chunkCount === 0) return;
|
|
1167
|
+
this.structuredOutputUpdateBatches.delete(messageId);
|
|
1168
|
+
this.emitStructuredOutputChange(messageId, "update", batch.delta);
|
|
1169
|
+
}
|
|
1170
|
+
emitStructuredOutputChange(messageId, phase, delta) {
|
|
1171
|
+
const part = this.messages.find((message) => message.id === messageId)?.parts.find(
|
|
1172
|
+
(messagePart) => messagePart.type === "structured-output"
|
|
1173
|
+
);
|
|
1174
|
+
if (!part) return;
|
|
1175
|
+
this.events.onStructuredOutputChange?.({
|
|
1176
|
+
phase,
|
|
1177
|
+
messageId,
|
|
1178
|
+
status: part.status,
|
|
1179
|
+
raw: part.raw,
|
|
1180
|
+
...part.partial !== void 0 ? { partial: part.partial } : {},
|
|
1181
|
+
...part.data !== void 0 ? { data: part.data } : {},
|
|
1182
|
+
...part.reasoning !== void 0 ? { reasoning: part.reasoning } : {},
|
|
1183
|
+
...part.errorMessage !== void 0 ? { errorMessage: part.errorMessage } : {},
|
|
1184
|
+
...delta !== void 0 ? { delta } : {}
|
|
1185
|
+
});
|
|
1186
|
+
}
|
|
1135
1187
|
/**
|
|
1136
1188
|
* Emit messages change event
|
|
1137
1189
|
*/
|
|
@@ -1163,13 +1215,16 @@ class StreamProcessor {
|
|
|
1163
1215
|
}
|
|
1164
1216
|
}
|
|
1165
1217
|
for (const messageId of this.structuredMessageIds) {
|
|
1218
|
+
this.flushStructuredOutputUpdate(messageId);
|
|
1166
1219
|
this.messages = errorStructuredOutputPart(
|
|
1167
1220
|
this.messages,
|
|
1168
1221
|
messageId,
|
|
1169
1222
|
"Stream ended without structured-output.complete"
|
|
1170
1223
|
);
|
|
1224
|
+
this.emitStructuredOutputChange(messageId, "error");
|
|
1171
1225
|
}
|
|
1172
1226
|
this.structuredMessageIds.clear();
|
|
1227
|
+
this.structuredOutputUpdateBatches.clear();
|
|
1173
1228
|
this.activeMessageIds.clear();
|
|
1174
1229
|
if (lastAssistantMessage && !this.hasError) {
|
|
1175
1230
|
if (this.isWhitespaceOnlyMessage(lastAssistantMessage)) {
|
|
@@ -1283,6 +1338,7 @@ class StreamProcessor {
|
|
|
1283
1338
|
this.activeRuns.clear();
|
|
1284
1339
|
this.toolCallToMessage.clear();
|
|
1285
1340
|
this.structuredMessageIds.clear();
|
|
1341
|
+
this.structuredOutputUpdateBatches.clear();
|
|
1286
1342
|
this.pendingManualMessageId = null;
|
|
1287
1343
|
this.pendingThinkingStepId = null;
|
|
1288
1344
|
this.finishReason = null;
|