@tanstack/ai 0.47.3 → 0.48.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/activities/chat/adapter.d.ts +5 -4
- package/dist/esm/activities/chat/adapter.js.map +1 -1
- package/dist/esm/activities/chat/index.d.ts +4 -7
- package/dist/esm/activities/chat/index.js +201 -243
- package/dist/esm/activities/chat/index.js.map +1 -1
- package/dist/esm/activities/chat/messages.js +115 -25
- package/dist/esm/activities/chat/messages.js.map +1 -1
- package/dist/esm/activities/chat/stream/processor.d.ts +38 -17
- package/dist/esm/activities/chat/stream/processor.js +186 -101
- package/dist/esm/activities/chat/stream/processor.js.map +1 -1
- package/dist/esm/activities/chat/tools/tool-calls.d.ts +3 -2
- package/dist/esm/activities/chat/tools/tool-calls.js +15 -10
- package/dist/esm/activities/chat/tools/tool-calls.js.map +1 -1
- package/dist/esm/activities/generateVideo/index.js +6 -6
- package/dist/esm/activities/generateVideo/index.js.map +1 -1
- package/dist/esm/activities/stream-generation-result.js +7 -8
- package/dist/esm/activities/stream-generation-result.js.map +1 -1
- package/dist/esm/activities/summarize/chat-stream-summarize.d.ts +2 -1
- package/dist/esm/activities/summarize/chat-stream-summarize.js +56 -53
- package/dist/esm/activities/summarize/chat-stream-summarize.js.map +1 -1
- package/dist/esm/adapter-internals.d.ts +2 -0
- package/dist/esm/adapter-internals.js +3 -1
- package/dist/esm/client.d.ts +8 -1
- package/dist/esm/client.js +7 -2
- package/dist/esm/client.js.map +1 -1
- package/dist/esm/index.d.ts +6 -0
- package/dist/esm/index.js +6 -2
- package/dist/esm/middlewares/otel.js +11 -4
- package/dist/esm/middlewares/otel.js.map +1 -1
- package/dist/esm/stream-to-response.js +8 -5
- package/dist/esm/stream-to-response.js.map +1 -1
- package/dist/esm/stream-to-websocket.js +4 -2
- package/dist/esm/stream-to-websocket.js.map +1 -1
- package/dist/esm/strip-to-spec-middleware.d.ts +10 -13
- package/dist/esm/strip-to-spec-middleware.js +24 -22
- package/dist/esm/strip-to-spec-middleware.js.map +1 -1
- package/dist/esm/types.d.ts +82 -305
- package/dist/esm/utilities/adapter-yield-chunk.d.ts +31 -0
- package/dist/esm/utilities/ag-ui-usage.d.ts +24 -0
- package/dist/esm/utilities/ag-ui-usage.js +66 -0
- package/dist/esm/utilities/ag-ui-usage.js.map +1 -0
- package/dist/esm/utilities/ag-ui-wire.d.ts +14 -7
- package/dist/esm/utilities/ag-ui-wire.js +71 -30
- package/dist/esm/utilities/ag-ui-wire.js.map +1 -1
- package/dist/esm/utilities/chat-params.d.ts +3 -3
- package/dist/esm/utilities/chat-params.js +10 -30
- package/dist/esm/utilities/chat-params.js.map +1 -1
- package/dist/esm/utilities/chunk-ids.d.ts +5 -0
- package/dist/esm/utilities/chunk-ids.js +25 -0
- package/dist/esm/utilities/chunk-ids.js.map +1 -0
- package/dist/esm/utilities/merge-metadata.d.ts +14 -0
- package/dist/esm/utilities/merge-metadata.js +43 -0
- package/dist/esm/utilities/merge-metadata.js.map +1 -0
- package/dist/esm/utilities/normalize-stream-chunk.d.ts +3 -0
- package/dist/esm/utilities/normalize-stream-chunk.js +100 -0
- package/dist/esm/utilities/normalize-stream-chunk.js.map +1 -0
- package/dist/esm/utilities/reasoning-encrypted-value.d.ts +8 -0
- package/dist/esm/utilities/reasoning-encrypted-value.js +16 -0
- package/dist/esm/utilities/reasoning-encrypted-value.js.map +1 -0
- package/dist/esm/utilities/restore-inbound-chunk.d.ts +15 -0
- package/dist/esm/utilities/restore-inbound-chunk.js +43 -0
- package/dist/esm/utilities/restore-inbound-chunk.js.map +1 -0
- package/dist/esm/utilities/spec-event-keys.d.ts +2 -0
- package/dist/esm/utilities/spec-event-keys.js +56 -0
- package/dist/esm/utilities/spec-event-keys.js.map +1 -0
- package/dist/esm/utilities/structured-output-events.d.ts +3 -3
- package/dist/esm/utilities/structured-output-events.js.map +1 -1
- package/package.json +2 -2
- package/src/activities/chat/adapter.ts +4 -4
- package/src/activities/chat/index.ts +398 -400
- package/src/activities/chat/messages.ts +174 -34
- package/src/activities/chat/stream/processor.ts +289 -218
- package/src/activities/chat/tools/tool-calls.ts +23 -22
- package/src/activities/generateVideo/index.ts +7 -6
- package/src/activities/stream-generation-result.ts +8 -12
- package/src/activities/summarize/chat-stream-summarize.ts +94 -70
- package/src/adapter-internals.ts +2 -0
- package/src/client.ts +13 -0
- package/src/index.ts +6 -0
- package/src/middlewares/otel.ts +16 -8
- package/src/stream-to-response.ts +11 -4
- package/src/stream-to-websocket.ts +3 -1
- package/src/strip-to-spec-middleware.ts +48 -24
- package/src/types.ts +109 -393
- package/src/utilities/adapter-yield-chunk.ts +30 -0
- package/src/utilities/ag-ui-usage.test.ts +194 -0
- package/src/utilities/ag-ui-usage.ts +148 -0
- package/src/utilities/ag-ui-wire.ts +149 -31
- package/src/utilities/chat-params.ts +22 -39
- package/src/utilities/chunk-ids.ts +24 -0
- package/src/utilities/merge-metadata.test.ts +117 -0
- package/src/utilities/merge-metadata.ts +59 -0
- package/src/utilities/normalize-stream-chunk.test.ts +423 -0
- package/src/utilities/normalize-stream-chunk.ts +186 -0
- package/src/utilities/reasoning-encrypted-value.ts +18 -0
- package/src/utilities/restore-inbound-chunk.test.ts +133 -0
- package/src/utilities/restore-inbound-chunk.ts +72 -0
- package/src/utilities/spec-event-keys.test.ts +34 -0
- package/src/utilities/spec-event-keys.ts +74 -0
- package/src/utilities/structured-output-events.ts +3 -3
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { isProviderExecutedToolCall } from '../../utilities/provider-executed'
|
|
2
2
|
import { normalizeToolResult } from '../../utilities/tool-result'
|
|
3
|
+
import { tanstackMetadata } from '../../utilities/merge-metadata'
|
|
3
4
|
import type { Message as AGUIMessage } from '@ag-ui/core'
|
|
4
5
|
import type {
|
|
5
6
|
ContentPart,
|
|
@@ -7,6 +8,7 @@ import type {
|
|
|
7
8
|
ModelMessage,
|
|
8
9
|
StructuredOutputPart,
|
|
9
10
|
TextPart,
|
|
11
|
+
ToolCall,
|
|
10
12
|
ToolCallPart,
|
|
11
13
|
UIMessage,
|
|
12
14
|
} from '../../types'
|
|
@@ -36,6 +38,34 @@ export function safeJsonStringify(value: unknown): string {
|
|
|
36
38
|
}
|
|
37
39
|
}
|
|
38
40
|
|
|
41
|
+
function nonEmptyString(value: unknown): string | undefined {
|
|
42
|
+
return typeof value === 'string' && value !== '' ? value : undefined
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function encryptedValueFrom(value: object): string | undefined {
|
|
46
|
+
if ('encryptedValue' in value) {
|
|
47
|
+
const fromSpec = nonEmptyString(value.encryptedValue)
|
|
48
|
+
if (fromSpec !== undefined) return fromSpec
|
|
49
|
+
}
|
|
50
|
+
return nonEmptyString(tanstackMetadata(value)?.signature)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function toolCallFromWire(toolCall: ToolCall, bag: unknown): ToolCall {
|
|
54
|
+
const fromBag =
|
|
55
|
+
bag != null && typeof bag === 'object' && !Array.isArray(bag)
|
|
56
|
+
? bag
|
|
57
|
+
: undefined
|
|
58
|
+
const encrypted = encryptedValueFrom(toolCall)
|
|
59
|
+
if (fromBag === undefined && encrypted === undefined) return toolCall
|
|
60
|
+
return {
|
|
61
|
+
...toolCall,
|
|
62
|
+
metadata: {
|
|
63
|
+
...(fromBag ?? {}),
|
|
64
|
+
...(encrypted !== undefined ? { thoughtSignature: encrypted } : {}),
|
|
65
|
+
},
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
39
69
|
function parseToolResultContent(content: string): unknown {
|
|
40
70
|
try {
|
|
41
71
|
return JSON.parse(content)
|
|
@@ -68,8 +98,13 @@ function collapseContentParts(
|
|
|
68
98
|
* Extract text content from ModelMessage content (string, null, or ContentPart array).
|
|
69
99
|
* Used when only the text portion is needed (e.g., tool result content).
|
|
70
100
|
*/
|
|
71
|
-
function getTextContent(
|
|
72
|
-
|
|
101
|
+
function getTextContent(
|
|
102
|
+
content: string | null | undefined | Array<ContentPart>,
|
|
103
|
+
): string {
|
|
104
|
+
// Tool-call-only assistant turns carry no text and reach here as `null` or
|
|
105
|
+
// `undefined`; both must collapse to an empty string rather than crash on
|
|
106
|
+
// `.filter` (issue #532 — the interrupt-boundary MessagesSnapshot).
|
|
107
|
+
if (content === null || content === undefined) return ''
|
|
73
108
|
if (typeof content === 'string') return content
|
|
74
109
|
return content
|
|
75
110
|
.filter((part): part is TextPart => part.type === 'text')
|
|
@@ -98,16 +133,15 @@ export function convertMessagesToModelMessages(
|
|
|
98
133
|
}
|
|
99
134
|
|
|
100
135
|
const modelMessages: Array<ModelMessage> = []
|
|
136
|
+
let pendingThinking: Array<{ content: string; signature?: string }> = []
|
|
101
137
|
for (const msg of messages) {
|
|
102
138
|
if ('parts' in msg) {
|
|
103
|
-
// UIMessage anchor — existing fan-out path
|
|
104
139
|
modelMessages.push(...uiMessageToModelMessages(msg))
|
|
105
140
|
continue
|
|
106
141
|
}
|
|
107
142
|
|
|
108
143
|
const role = (msg as { role: string }).role
|
|
109
144
|
|
|
110
|
-
// AG-UI tool fan-out duplicate — drop if anchor already covers it
|
|
111
145
|
if (
|
|
112
146
|
role === 'tool' &&
|
|
113
147
|
msg.toolCallId &&
|
|
@@ -116,12 +150,22 @@ export function convertMessagesToModelMessages(
|
|
|
116
150
|
continue
|
|
117
151
|
}
|
|
118
152
|
|
|
119
|
-
|
|
120
|
-
|
|
153
|
+
if (role === 'reasoning') {
|
|
154
|
+
const content = (msg as { content?: string }).content
|
|
155
|
+
if (content) {
|
|
156
|
+
const signature = encryptedValueFrom(msg)
|
|
157
|
+
pendingThinking.push({
|
|
158
|
+
content,
|
|
159
|
+
...(signature !== undefined ? { signature } : {}),
|
|
160
|
+
})
|
|
161
|
+
}
|
|
162
|
+
continue
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (role === 'activity') {
|
|
121
166
|
continue
|
|
122
167
|
}
|
|
123
168
|
|
|
124
|
-
// AG-UI developer — collapse to system
|
|
125
169
|
if (role === 'developer') {
|
|
126
170
|
modelMessages.push({
|
|
127
171
|
role: 'system' as ModelMessage['role'],
|
|
@@ -130,8 +174,56 @@ export function convertMessagesToModelMessages(
|
|
|
130
174
|
continue
|
|
131
175
|
}
|
|
132
176
|
|
|
133
|
-
|
|
134
|
-
|
|
177
|
+
if (
|
|
178
|
+
role === 'user' &&
|
|
179
|
+
Array.isArray((msg as { content?: unknown }).content)
|
|
180
|
+
) {
|
|
181
|
+
const content = (msg as { content: Array<{ type: string }> }).content
|
|
182
|
+
// TanStack ModelMessage text parts use `{ content }`. AG-UI wire text
|
|
183
|
+
// parts use `{ text }`. Only rewrite the AG-UI shape.
|
|
184
|
+
if (
|
|
185
|
+
!content.some(
|
|
186
|
+
(part) =>
|
|
187
|
+
part.type === 'text' && 'text' in part && !('content' in part),
|
|
188
|
+
)
|
|
189
|
+
) {
|
|
190
|
+
modelMessages.push(msg as ModelMessage)
|
|
191
|
+
continue
|
|
192
|
+
}
|
|
193
|
+
const parts = aguiUserContentToParts(
|
|
194
|
+
content as Extract<AGUIMessage, { role: 'user' }>['content'],
|
|
195
|
+
)
|
|
196
|
+
const contentParts = parts.filter(isContentPart)
|
|
197
|
+
modelMessages.push({
|
|
198
|
+
role: 'user',
|
|
199
|
+
content: collapseContentParts(contentParts),
|
|
200
|
+
...((msg as { id?: string }).id !== undefined && {
|
|
201
|
+
id: (msg as { id: string }).id,
|
|
202
|
+
}),
|
|
203
|
+
})
|
|
204
|
+
continue
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (role === 'assistant') {
|
|
208
|
+
const source = msg as ModelMessage
|
|
209
|
+
const toolCallMetadata = tanstackMetadata(msg)?.toolCallMetadata
|
|
210
|
+
const toolCalls = source.toolCalls?.map((toolCall) =>
|
|
211
|
+
toolCallFromWire(toolCall, toolCallMetadata?.[toolCall.id]),
|
|
212
|
+
)
|
|
213
|
+
modelMessages.push({
|
|
214
|
+
...source,
|
|
215
|
+
...(toolCalls !== undefined ? { toolCalls } : {}),
|
|
216
|
+
...(pendingThinking.length > 0
|
|
217
|
+
? {
|
|
218
|
+
thinking: [...(source.thinking ?? []), ...pendingThinking],
|
|
219
|
+
}
|
|
220
|
+
: {}),
|
|
221
|
+
})
|
|
222
|
+
pendingThinking = []
|
|
223
|
+
continue
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
modelMessages.push(msg as ModelMessage)
|
|
135
227
|
}
|
|
136
228
|
return modelMessages
|
|
137
229
|
}
|
|
@@ -536,58 +628,106 @@ export function aguiSnapshotMessageToUIMessage(
|
|
|
536
628
|
message: AGUIMessage | UIMessage,
|
|
537
629
|
): UIMessage {
|
|
538
630
|
if ('parts' in message) {
|
|
539
|
-
return
|
|
631
|
+
return applySnapshotMetadata(message, {
|
|
632
|
+
...message,
|
|
633
|
+
id: message.id || generateMessageId(),
|
|
634
|
+
})
|
|
540
635
|
}
|
|
541
636
|
|
|
542
637
|
const id = message.id || generateMessageId()
|
|
543
638
|
|
|
544
639
|
switch (message.role) {
|
|
545
640
|
case 'user':
|
|
546
|
-
return {
|
|
641
|
+
return applySnapshotMetadata(message, {
|
|
547
642
|
id,
|
|
548
643
|
role: 'user',
|
|
549
644
|
parts: aguiUserContentToParts(message.content),
|
|
550
|
-
}
|
|
551
|
-
case 'assistant':
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
645
|
+
})
|
|
646
|
+
case 'assistant': {
|
|
647
|
+
const toolCallMetadata = tanstackMetadata(message)?.toolCallMetadata
|
|
648
|
+
const toolCalls = message.toolCalls?.map((toolCall) => {
|
|
649
|
+
const metadata =
|
|
650
|
+
toolCallMetadata != null && typeof toolCallMetadata === 'object'
|
|
651
|
+
? (toolCallMetadata as Record<string, unknown>)[toolCall.id]
|
|
652
|
+
: undefined
|
|
653
|
+
return metadata !== undefined ? { ...toolCall, metadata } : toolCall
|
|
654
|
+
})
|
|
655
|
+
return applySnapshotMetadata(
|
|
656
|
+
message,
|
|
657
|
+
modelMessageToUIMessage(
|
|
658
|
+
{
|
|
659
|
+
role: 'assistant',
|
|
660
|
+
content: message.content ?? null,
|
|
661
|
+
...(toolCalls && { toolCalls }),
|
|
662
|
+
},
|
|
663
|
+
id,
|
|
664
|
+
),
|
|
559
665
|
)
|
|
666
|
+
}
|
|
560
667
|
case 'tool':
|
|
561
|
-
return
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
668
|
+
return applySnapshotMetadata(
|
|
669
|
+
message,
|
|
670
|
+
modelMessageToUIMessage(
|
|
671
|
+
{
|
|
672
|
+
role: 'tool',
|
|
673
|
+
content: message.content,
|
|
674
|
+
toolCallId: message.toolCallId,
|
|
675
|
+
},
|
|
676
|
+
id,
|
|
677
|
+
),
|
|
568
678
|
)
|
|
569
679
|
case 'system':
|
|
570
680
|
case 'developer':
|
|
571
681
|
// `ModelMessage` has no system/developer role; build the part directly.
|
|
572
|
-
return {
|
|
682
|
+
return applySnapshotMetadata(message, {
|
|
573
683
|
id,
|
|
574
684
|
role: 'system',
|
|
575
685
|
parts: message.content
|
|
576
686
|
? [{ type: 'text', content: message.content }]
|
|
577
687
|
: [],
|
|
578
|
-
}
|
|
579
|
-
case 'reasoning':
|
|
580
|
-
|
|
688
|
+
})
|
|
689
|
+
case 'reasoning': {
|
|
690
|
+
const signature = encryptedValueFrom(message)
|
|
691
|
+
return applySnapshotMetadata(message, {
|
|
581
692
|
id,
|
|
582
693
|
role: 'assistant',
|
|
583
694
|
parts: message.content
|
|
584
|
-
? [
|
|
695
|
+
? [
|
|
696
|
+
{
|
|
697
|
+
type: 'thinking' as const,
|
|
698
|
+
content: message.content,
|
|
699
|
+
...(signature !== undefined ? { signature } : {}),
|
|
700
|
+
},
|
|
701
|
+
]
|
|
585
702
|
: [],
|
|
586
|
-
}
|
|
703
|
+
})
|
|
704
|
+
}
|
|
587
705
|
case 'activity':
|
|
588
706
|
default:
|
|
589
707
|
// `activity` (and any future role) has no text/parts equivalent today.
|
|
590
|
-
return
|
|
708
|
+
return applySnapshotMetadata(message, {
|
|
709
|
+
id,
|
|
710
|
+
role: 'assistant',
|
|
711
|
+
parts: [],
|
|
712
|
+
})
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
/** Copy snapshot metadata when it is a record. Rebuild createdAt from tanstack.createdAt. */
|
|
717
|
+
function applySnapshotMetadata(source: object, ui: UIMessage): UIMessage {
|
|
718
|
+
if (!('metadata' in source)) return ui
|
|
719
|
+
const raw = source.metadata
|
|
720
|
+
if (raw == null || typeof raw !== 'object' || Array.isArray(raw)) return ui
|
|
721
|
+
const metadata = raw as NonNullable<UIMessage['metadata']>
|
|
722
|
+
const createdAtRaw = tanstackMetadata(metadata)?.createdAt
|
|
723
|
+
const createdAt =
|
|
724
|
+
typeof createdAtRaw === 'string' ? new Date(createdAtRaw) : undefined
|
|
725
|
+
const createdAtValid =
|
|
726
|
+
createdAt !== undefined && !Number.isNaN(createdAt.getTime())
|
|
727
|
+
return {
|
|
728
|
+
...ui,
|
|
729
|
+
metadata,
|
|
730
|
+
...(createdAtValid ? { createdAt } : {}),
|
|
591
731
|
}
|
|
592
732
|
}
|
|
593
733
|
|