@tanstack/ai 0.39.0 → 0.39.1
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 +4 -0
- package/dist/esm/activities/chat/index.js.map +1 -1
- package/dist/esm/activities/chat/stream/processor.d.ts +18 -0
- package/dist/esm/activities/chat/stream/processor.js +86 -4
- package/dist/esm/activities/chat/stream/processor.js.map +1 -1
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.js +3 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/types.d.ts +22 -1
- package/dist/esm/utilities/provider-executed.d.ts +18 -0
- package/dist/esm/utilities/provider-executed.js +15 -0
- package/dist/esm/utilities/provider-executed.js.map +1 -0
- package/package.json +1 -1
- package/skills/ai-core/adapter-configuration/references/anthropic-adapter.md +43 -12
- package/skills/ai-core/media-generation/SKILL.md +1 -1
- package/src/activities/chat/index.ts +8 -0
- package/src/activities/chat/stream/processor.ts +141 -3
- package/src/index.ts +5 -0
- package/src/types.ts +23 -1
- package/src/utilities/provider-executed.ts +32 -0
|
@@ -220,6 +220,24 @@ export declare class StreamProcessor {
|
|
|
220
220
|
* Handle MESSAGES_SNAPSHOT event
|
|
221
221
|
*/
|
|
222
222
|
private handleMessagesSnapshotEvent;
|
|
223
|
+
/**
|
|
224
|
+
* Reconcile a freshly normalized snapshot with the pre-snapshot message
|
|
225
|
+
* state so unreconstructable tool-call metadata is preserved.
|
|
226
|
+
*
|
|
227
|
+
* Post-pass (a): anchor `tool-result`-only assistant messages (the shape
|
|
228
|
+
* `aguiSnapshotMessageToUIMessage` emits for AG-UI `role: 'tool'` wire
|
|
229
|
+
* messages) into the message containing the matching `tool-call` part, or —
|
|
230
|
+
* when the snapshot supplies no such part — the nearest earlier anchorable
|
|
231
|
+
* assistant message, matching the in-stream fan-out shape
|
|
232
|
+
* `assistant: [text, tool-call, tool-result, ...]`. Detached messages with
|
|
233
|
+
* no earlier anchorable assistant are kept verbatim.
|
|
234
|
+
*
|
|
235
|
+
* Post-pass (b): when a `tool-result` part references a `toolCallId` whose
|
|
236
|
+
* `tool-call` part is absent from the snapshot, carry the `tool-call` part
|
|
237
|
+
* forward from the pre-snapshot state (state and output untouched) so a
|
|
238
|
+
* subsequent `addToolResult(toolCallId)` can still locate the call.
|
|
239
|
+
*/
|
|
240
|
+
private reconcileSnapshotToolCalls;
|
|
223
241
|
/**
|
|
224
242
|
* Handle TEXT_MESSAGE_CONTENT event.
|
|
225
243
|
*
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { generateMessageId, uiMessageToModelMessages, aguiSnapshotMessageToUIMessage } from "../messages.js";
|
|
2
2
|
import { normalizeToolResult } from "../../../utilities/tool-result.js";
|
|
3
|
+
import { isProviderExecutedToolCall } from "../../../utilities/provider-executed.js";
|
|
3
4
|
import { defaultJSONParser } from "./json-parser.js";
|
|
4
5
|
import { updateToolCallWithOutput, updateToolResultPart, updateToolCallApprovalResponse, appendStructuredOutputDelta, updateToolCallPart, errorStructuredOutputPart, updateThinkingPart, completeStructuredOutputPart, updateToolCallApproval, updateTextPart } from "./message-updaters.js";
|
|
5
6
|
import { ImmediateStrategy } from "./strategies.js";
|
|
@@ -197,7 +198,7 @@ class StreamProcessor {
|
|
|
197
198
|
lastAssistant.parts.filter((p) => p.type === "tool-result").map((p) => p.toolCallId)
|
|
198
199
|
);
|
|
199
200
|
return toolParts.every(
|
|
200
|
-
(part) => part.state === "complete" || part.state === "approval-responded" || part.output !== void 0 && !part.approval || toolResultIds.has(part.id)
|
|
201
|
+
(part) => part.state === "complete" || part.state === "approval-responded" || part.output !== void 0 && !part.approval || toolResultIds.has(part.id) || isProviderExecutedToolCall(part)
|
|
201
202
|
);
|
|
202
203
|
}
|
|
203
204
|
/**
|
|
@@ -548,9 +549,87 @@ class StreamProcessor {
|
|
|
548
549
|
*/
|
|
549
550
|
handleMessagesSnapshotEvent(chunk) {
|
|
550
551
|
this.resetStreamState();
|
|
551
|
-
|
|
552
|
+
const prevMessages = this.messages;
|
|
553
|
+
const normalized = chunk.messages.map(aguiSnapshotMessageToUIMessage);
|
|
554
|
+
this.messages = this.reconcileSnapshotToolCalls(normalized, prevMessages);
|
|
552
555
|
this.emitMessagesChange();
|
|
553
556
|
}
|
|
557
|
+
/**
|
|
558
|
+
* Reconcile a freshly normalized snapshot with the pre-snapshot message
|
|
559
|
+
* state so unreconstructable tool-call metadata is preserved.
|
|
560
|
+
*
|
|
561
|
+
* Post-pass (a): anchor `tool-result`-only assistant messages (the shape
|
|
562
|
+
* `aguiSnapshotMessageToUIMessage` emits for AG-UI `role: 'tool'` wire
|
|
563
|
+
* messages) into the message containing the matching `tool-call` part, or —
|
|
564
|
+
* when the snapshot supplies no such part — the nearest earlier anchorable
|
|
565
|
+
* assistant message, matching the in-stream fan-out shape
|
|
566
|
+
* `assistant: [text, tool-call, tool-result, ...]`. Detached messages with
|
|
567
|
+
* no earlier anchorable assistant are kept verbatim.
|
|
568
|
+
*
|
|
569
|
+
* Post-pass (b): when a `tool-result` part references a `toolCallId` whose
|
|
570
|
+
* `tool-call` part is absent from the snapshot, carry the `tool-call` part
|
|
571
|
+
* forward from the pre-snapshot state (state and output untouched) so a
|
|
572
|
+
* subsequent `addToolResult(toolCallId)` can still locate the call.
|
|
573
|
+
*/
|
|
574
|
+
reconcileSnapshotToolCalls(snapshot, prevMessages) {
|
|
575
|
+
const prevToolCalls = /* @__PURE__ */ new Map();
|
|
576
|
+
for (const msg of prevMessages) {
|
|
577
|
+
for (const part of msg.parts) {
|
|
578
|
+
if (part.type === "tool-call") {
|
|
579
|
+
prevToolCalls.set(part.id, part);
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
const snapshotToolCallIds = /* @__PURE__ */ new Set();
|
|
584
|
+
for (const msg of snapshot) {
|
|
585
|
+
for (const part of msg.parts) {
|
|
586
|
+
if (part.type === "tool-call") {
|
|
587
|
+
snapshotToolCallIds.add(part.id);
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
const reconciled = [];
|
|
592
|
+
for (const msg of snapshot) {
|
|
593
|
+
const toolResultPart = msg.role === "assistant" && msg.parts.length === 1 ? msg.parts.find((p) => p.type === "tool-result") : void 0;
|
|
594
|
+
if (!toolResultPart) {
|
|
595
|
+
reconciled.push(msg);
|
|
596
|
+
continue;
|
|
597
|
+
}
|
|
598
|
+
const target = reconciled.findLast(
|
|
599
|
+
(m) => m.parts.some(
|
|
600
|
+
(p) => p.type === "tool-call" && p.id === toolResultPart.toolCallId
|
|
601
|
+
)
|
|
602
|
+
) ?? reconciled.findLast(
|
|
603
|
+
(m) => m.role === "assistant" && !(m.parts.length === 1 && m.parts[0]?.type === "tool-result")
|
|
604
|
+
);
|
|
605
|
+
if (!target) {
|
|
606
|
+
if (!snapshotToolCallIds.has(toolResultPart.toolCallId)) {
|
|
607
|
+
console.warn(
|
|
608
|
+
`[StreamProcessor] MESSAGES_SNAPSHOT contains a tool-result for "${toolResultPart.toolCallId}" but no matching tool-call exists in the snapshot, and there is no assistant message to anchor into; addToolResult("${toolResultPart.toolCallId}") will not be able to locate this call`
|
|
609
|
+
);
|
|
610
|
+
}
|
|
611
|
+
reconciled.push(msg);
|
|
612
|
+
continue;
|
|
613
|
+
}
|
|
614
|
+
const parts = [...target.parts];
|
|
615
|
+
if (!snapshotToolCallIds.has(toolResultPart.toolCallId) && !parts.some(
|
|
616
|
+
(p) => p.type === "tool-call" && p.id === toolResultPart.toolCallId
|
|
617
|
+
)) {
|
|
618
|
+
const prev = prevToolCalls.get(toolResultPart.toolCallId);
|
|
619
|
+
if (prev) {
|
|
620
|
+
parts.push({ ...prev });
|
|
621
|
+
snapshotToolCallIds.add(prev.id);
|
|
622
|
+
} else {
|
|
623
|
+
console.warn(
|
|
624
|
+
`[StreamProcessor] MESSAGES_SNAPSHOT contains a tool-result for "${toolResultPart.toolCallId}" but no matching tool-call exists in the snapshot or the pre-snapshot state; addToolResult("${toolResultPart.toolCallId}") will not be able to locate this call`
|
|
625
|
+
);
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
parts.push(toolResultPart);
|
|
629
|
+
target.parts = parts;
|
|
630
|
+
}
|
|
631
|
+
return reconciled;
|
|
632
|
+
}
|
|
554
633
|
/**
|
|
555
634
|
* Handle TEXT_MESSAGE_CONTENT event.
|
|
556
635
|
*
|
|
@@ -1185,8 +1264,11 @@ class StreamProcessor {
|
|
|
1185
1264
|
*/
|
|
1186
1265
|
isToolCallPartErrored(toolCallId) {
|
|
1187
1266
|
return this.messages.some(
|
|
1188
|
-
(msg) =>
|
|
1189
|
-
|
|
1267
|
+
(msg) => (
|
|
1268
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- `parts` is typed as required, but seeded ModelMessage-shaped messages can lack it at runtime.
|
|
1269
|
+
msg.parts?.some(
|
|
1270
|
+
(part) => part.type === "tool-call" && part.id === toolCallId && part.state === "error"
|
|
1271
|
+
)
|
|
1190
1272
|
)
|
|
1191
1273
|
);
|
|
1192
1274
|
}
|