@assistant-ui/react-ai-sdk 1.3.7 → 1.3.9
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/ui/getVercelAIMessages.d.ts.map +1 -1
- package/dist/ui/getVercelAIMessages.js.map +1 -1
- package/dist/ui/use-chat/AssistantChatTransport.d.ts.map +1 -1
- package/dist/ui/use-chat/AssistantChatTransport.js.map +1 -1
- package/dist/ui/use-chat/useAISDKRuntime.d.ts.map +1 -1
- package/dist/ui/use-chat/useAISDKRuntime.js +22 -10
- package/dist/ui/use-chat/useAISDKRuntime.js.map +1 -1
- package/dist/ui/use-chat/useChatRuntime.d.ts.map +1 -1
- package/dist/ui/use-chat/useChatRuntime.js.map +1 -1
- package/dist/ui/use-chat/useExternalHistory.d.ts.map +1 -1
- package/dist/ui/use-chat/useExternalHistory.js +137 -24
- package/dist/ui/use-chat/useExternalHistory.js.map +1 -1
- package/dist/ui/use-chat/useStreamingTiming.d.ts +11 -0
- package/dist/ui/use-chat/useStreamingTiming.d.ts.map +1 -0
- package/dist/ui/use-chat/useStreamingTiming.js +84 -0
- package/dist/ui/use-chat/useStreamingTiming.js.map +1 -0
- package/dist/ui/utils/convertMessage.d.ts +4 -4
- package/dist/ui/utils/convertMessage.d.ts.map +1 -1
- package/dist/ui/utils/convertMessage.js +84 -75
- package/dist/ui/utils/convertMessage.js.map +1 -1
- package/dist/ui/utils/sliceMessagesUntil.d.ts.map +1 -1
- package/dist/ui/utils/sliceMessagesUntil.js.map +1 -1
- package/dist/ui/utils/vercelAttachmentAdapter.js +1 -1
- package/dist/ui/utils/vercelAttachmentAdapter.js.map +1 -1
- package/package.json +14 -8
- package/src/ui/use-chat/useAISDKRuntime.test.ts +225 -0
- package/src/ui/use-chat/{useAISDKRuntime.tsx → useAISDKRuntime.ts} +34 -10
- package/src/ui/use-chat/useExternalHistory.ts +283 -0
- package/src/ui/use-chat/useStreamingTiming.ts +102 -0
- package/src/ui/utils/convertMessage.test.ts +125 -0
- package/src/ui/utils/convertMessage.ts +98 -82
- package/src/ui/utils/vercelAttachmentAdapter.ts +1 -1
- package/src/ui/use-chat/useExternalHistory.tsx +0 -134
- /package/src/ui/{getVercelAIMessages.tsx → getVercelAIMessages.ts} +0 -0
- /package/src/ui/use-chat/{AssistantChatTransport.tsx → AssistantChatTransport.ts} +0 -0
- /package/src/ui/use-chat/{useChatRuntime.tsx → useChatRuntime.ts} +0 -0
- /package/src/ui/utils/{sliceMessagesUntil.tsx → sliceMessagesUntil.ts} +0 -0
|
@@ -3,55 +3,79 @@ import { unstable_createMessageConverter, } from "@assistant-ui/react";
|
|
|
3
3
|
function stripClosingDelimiters(json) {
|
|
4
4
|
return json.replace(/[}\]"]+$/, "");
|
|
5
5
|
}
|
|
6
|
-
|
|
6
|
+
/**
|
|
7
|
+
* Resolves the interrupt fields for a tool call part.
|
|
8
|
+
*
|
|
9
|
+
* Two interrupt paths for tool approvals:
|
|
10
|
+
* 1. AI SDK server-side approval: approval-requested state with part.approval payload
|
|
11
|
+
* 2. Frontend tools: toolStatuses interrupt from context.human()
|
|
12
|
+
*/
|
|
13
|
+
function getToolInterrupt(part, toolStatus) {
|
|
14
|
+
if (part.state === "approval-requested" && "approval" in part) {
|
|
15
|
+
return {
|
|
16
|
+
interrupt: {
|
|
17
|
+
type: "human",
|
|
18
|
+
payload: part.approval,
|
|
19
|
+
},
|
|
20
|
+
status: {
|
|
21
|
+
type: "requires-action",
|
|
22
|
+
reason: "interrupt",
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
if (toolStatus?.type === "interrupt") {
|
|
27
|
+
return {
|
|
28
|
+
interrupt: toolStatus.payload,
|
|
29
|
+
status: {
|
|
30
|
+
type: "requires-action",
|
|
31
|
+
reason: "interrupt",
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
return {};
|
|
36
|
+
}
|
|
37
|
+
function convertParts(message, metadata) {
|
|
7
38
|
if (!message.parts || message.parts.length === 0) {
|
|
8
39
|
return [];
|
|
9
40
|
}
|
|
10
41
|
const converted = message.parts
|
|
11
42
|
.filter((p) => p.type !== "step-start" && p.type !== "file")
|
|
12
43
|
.map((part) => {
|
|
13
|
-
|
|
14
|
-
// Handle text parts
|
|
15
|
-
if (type === "text") {
|
|
44
|
+
if (part.type === "text") {
|
|
16
45
|
return {
|
|
17
46
|
type: "text",
|
|
18
47
|
text: part.text,
|
|
19
48
|
};
|
|
20
49
|
}
|
|
21
|
-
|
|
22
|
-
if (type === "reasoning") {
|
|
50
|
+
if (part.type === "reasoning") {
|
|
23
51
|
return {
|
|
24
52
|
type: "reasoning",
|
|
25
53
|
text: part.text,
|
|
26
54
|
};
|
|
27
55
|
}
|
|
28
|
-
// Handle tool parts (both static tool-* and dynamic-tool)
|
|
29
|
-
// In AI SDK v6, isToolUIPart returns true for both static and dynamic tools
|
|
30
56
|
if (isToolUIPart(part)) {
|
|
31
|
-
// Use getToolName which works for both static and dynamic tools
|
|
32
57
|
const toolName = getToolName(part);
|
|
33
58
|
const toolCallId = part.toolCallId;
|
|
34
|
-
|
|
35
|
-
let args = {};
|
|
59
|
+
const args = part.input || {};
|
|
36
60
|
let result;
|
|
37
61
|
let isError = false;
|
|
38
|
-
if (part.state === "
|
|
39
|
-
part.state === "input-available") {
|
|
40
|
-
args = part.input || {};
|
|
41
|
-
}
|
|
42
|
-
else if (part.state === "output-available") {
|
|
43
|
-
args = part.input || {};
|
|
62
|
+
if (part.state === "output-available") {
|
|
44
63
|
result = part.output;
|
|
45
64
|
}
|
|
46
65
|
else if (part.state === "output-error") {
|
|
47
|
-
args = part.input || {};
|
|
48
66
|
isError = true;
|
|
49
67
|
result = { error: part.errorText };
|
|
50
68
|
}
|
|
69
|
+
else if (part.state === "output-denied") {
|
|
70
|
+
isError = true;
|
|
71
|
+
result = {
|
|
72
|
+
error: part.approval.reason ||
|
|
73
|
+
"Tool approval denied",
|
|
74
|
+
};
|
|
75
|
+
}
|
|
51
76
|
let argsText = JSON.stringify(args);
|
|
52
77
|
if (part.state === "input-streaming") {
|
|
53
|
-
//
|
|
54
|
-
// these are added by the AI SDK in fix-json
|
|
78
|
+
// strip closing delimiters added by the AI SDK's fix-json
|
|
55
79
|
argsText = stripClosingDelimiters(argsText);
|
|
56
80
|
}
|
|
57
81
|
const toolStatus = metadata.toolStatuses?.[toolCallId];
|
|
@@ -63,17 +87,10 @@ const convertParts = (message, metadata) => {
|
|
|
63
87
|
args,
|
|
64
88
|
result,
|
|
65
89
|
isError,
|
|
66
|
-
...(toolStatus
|
|
67
|
-
interrupt: toolStatus.payload,
|
|
68
|
-
status: {
|
|
69
|
-
type: "requires-action",
|
|
70
|
-
reason: "interrupt",
|
|
71
|
-
},
|
|
72
|
-
}),
|
|
90
|
+
...getToolInterrupt(part, toolStatus),
|
|
73
91
|
};
|
|
74
92
|
}
|
|
75
|
-
|
|
76
|
-
if (type === "source-url") {
|
|
93
|
+
if (part.type === "source-url") {
|
|
77
94
|
return {
|
|
78
95
|
type: "source",
|
|
79
96
|
sourceType: "url",
|
|
@@ -82,22 +99,18 @@ const convertParts = (message, metadata) => {
|
|
|
82
99
|
title: part.title || "",
|
|
83
100
|
};
|
|
84
101
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
console.warn(`Source document part type ${type} is not yet supported in conversion`);
|
|
102
|
+
if (part.type === "source-document") {
|
|
103
|
+
console.warn("Source document parts are not yet supported in conversion");
|
|
88
104
|
return null;
|
|
89
105
|
}
|
|
90
|
-
|
|
91
|
-
if (type.startsWith("data-")) {
|
|
92
|
-
const name = type.substring(5);
|
|
106
|
+
if (part.type.startsWith("data-")) {
|
|
93
107
|
return {
|
|
94
108
|
type: "data",
|
|
95
|
-
name,
|
|
109
|
+
name: part.type.substring(5),
|
|
96
110
|
data: part.data,
|
|
97
111
|
};
|
|
98
112
|
}
|
|
99
|
-
|
|
100
|
-
console.warn(`Unsupported message part type: ${type}`);
|
|
113
|
+
console.warn(`Unsupported message part type: ${part.type}`);
|
|
101
114
|
return null;
|
|
102
115
|
})
|
|
103
116
|
.filter(Boolean);
|
|
@@ -110,60 +123,56 @@ const convertParts = (message, metadata) => {
|
|
|
110
123
|
}
|
|
111
124
|
return true;
|
|
112
125
|
});
|
|
113
|
-
}
|
|
126
|
+
}
|
|
114
127
|
export const AISDKMessageConverter = unstable_createMessageConverter((message, metadata) => {
|
|
115
|
-
// UIMessage doesn't have createdAt, so we'll use current date or undefined
|
|
116
128
|
const createdAt = new Date();
|
|
129
|
+
const content = convertParts(message, metadata);
|
|
117
130
|
switch (message.role) {
|
|
118
131
|
case "user":
|
|
119
132
|
return {
|
|
120
133
|
role: "user",
|
|
121
134
|
id: message.id,
|
|
122
135
|
createdAt,
|
|
123
|
-
content
|
|
136
|
+
content,
|
|
124
137
|
attachments: message.parts
|
|
125
138
|
?.filter((p) => p.type === "file")
|
|
126
|
-
.map((part, idx) => {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
:
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
};
|
|
148
|
-
}),
|
|
139
|
+
.map((part, idx) => ({
|
|
140
|
+
id: idx.toString(),
|
|
141
|
+
type: part.mediaType.startsWith("image/") ? "image" : "file",
|
|
142
|
+
name: part.filename ?? "file",
|
|
143
|
+
content: [
|
|
144
|
+
part.mediaType.startsWith("image/")
|
|
145
|
+
? {
|
|
146
|
+
type: "image",
|
|
147
|
+
image: part.url,
|
|
148
|
+
filename: part.filename,
|
|
149
|
+
}
|
|
150
|
+
: {
|
|
151
|
+
type: "file",
|
|
152
|
+
filename: part.filename,
|
|
153
|
+
data: part.url,
|
|
154
|
+
mimeType: part.mediaType,
|
|
155
|
+
},
|
|
156
|
+
],
|
|
157
|
+
contentType: part.mediaType ?? "unknown/unknown",
|
|
158
|
+
status: { type: "complete" },
|
|
159
|
+
})),
|
|
149
160
|
metadata: message.metadata,
|
|
150
161
|
};
|
|
151
162
|
case "system":
|
|
163
|
+
case "assistant": {
|
|
164
|
+
const timing = metadata.messageTiming?.[message.id];
|
|
152
165
|
return {
|
|
153
|
-
role:
|
|
166
|
+
role: message.role,
|
|
154
167
|
id: message.id,
|
|
155
168
|
createdAt,
|
|
156
|
-
content
|
|
157
|
-
metadata:
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
role: "assistant",
|
|
162
|
-
id: message.id,
|
|
163
|
-
createdAt,
|
|
164
|
-
content: convertParts(message, metadata),
|
|
165
|
-
metadata: message.metadata,
|
|
169
|
+
content,
|
|
170
|
+
metadata: {
|
|
171
|
+
...message.metadata,
|
|
172
|
+
...(timing && { timing }),
|
|
173
|
+
},
|
|
166
174
|
};
|
|
175
|
+
}
|
|
167
176
|
default:
|
|
168
177
|
console.warn(`Unsupported message role: ${message.role}`);
|
|
169
178
|
return [];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"convertMessage.js","sourceRoot":"","sources":["../../../src/ui/utils/convertMessage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,WAAW,EAAkB,MAAM,IAAI,CAAC;AAC/D,OAAO,EACL,+BAA+B,GAQhC,MAAM,qBAAqB,CAAC;AAK7B,SAAS,sBAAsB,CAAC,IAAY;IAC1C,OAAO,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;AACtC,CAAC;AAED,MAAM,YAAY,
|
|
1
|
+
{"version":3,"file":"convertMessage.js","sourceRoot":"","sources":["../../../src/ui/utils/convertMessage.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sliceMessagesUntil.d.ts","sourceRoot":"","sources":["../../../src/ui/utils/sliceMessagesUntil.
|
|
1
|
+
{"version":3,"file":"sliceMessagesUntil.d.ts","sourceRoot":"","sources":["../../../src/ui/utils/sliceMessagesUntil.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AAEpC,eAAO,MAAM,kBAAkB,GAAI,UAAU,SAAS,SAAS,GAAG,SAAS,EACzE,UAAU,UAAU,EAAE,EACtB,WAAW,MAAM,GAAG,IAAI,iBAezB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sliceMessagesUntil.js","sourceRoot":"","sources":["../../../src/ui/utils/sliceMessagesUntil.
|
|
1
|
+
{"version":3,"file":"sliceMessagesUntil.js","sourceRoot":"","sources":["../../../src/ui/utils/sliceMessagesUntil.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAChC,QAAsB,EACtB,SAAwB,EACxB,EAAE;IACF,IAAI,SAAS,IAAI,IAAI;QAAE,OAAO,EAAE,CAAC;IAEjC,IAAI,UAAU,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,SAAS,CAAC,CAAC;IAC/D,IAAI,UAAU,KAAK,CAAC,CAAC;QACnB,MAAM,IAAI,KAAK,CACb,4FAA4F,CAC7F,CAAC;IAEJ,OAAO,QAAQ,CAAC,UAAU,GAAG,CAAC,CAAC,EAAE,IAAI,KAAK,WAAW,EAAE,CAAC;QACtD,UAAU,EAAE,CAAC;IACf,CAAC;IAED,OAAO,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,GAAG,CAAC,CAAC,CAAC;AAC3C,CAAC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vercelAttachmentAdapter.js","sourceRoot":"","sources":["../../../src/ui/utils/vercelAttachmentAdapter.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAEhC,MAAM,cAAc,GAAG,CAAC,IAAU,EAAE,EAAE,CACpC,IAAI,OAAO,CAAS,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;IACtC,MAAM,MAAM,GAAG,IAAI,UAAU,EAAE,CAAC;IAEhC,MAAM,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,MAAgB,CAAC,CAAC;IACvD,MAAM,CAAC,OAAO,GAAG,CAAC,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAE1C,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;AAC7B,CAAC,CAAC,CAAC;AAEL,MAAM,CAAC,MAAM,uBAAuB,GAAsB;IACxD,MAAM,EACJ,wFAAwF;IAC1F,KAAK,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE;QAChB,OAAO;YACL,EAAE,EAAE,UAAU,EAAE;YAChB,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM;YACvD,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,IAAI;YACJ,WAAW,EAAE,IAAI,CAAC,IAAI;YACtB,OAAO,EAAE,EAAE;YACX,MAAM,EAAE,EAAE,IAAI,EAAE,iBAAiB,EAAE,MAAM,EAAE,eAAe,EAAE;SAC7D,CAAC;IACJ,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,UAAU;QACnB,OAAO;QACP,OAAO;YACL,GAAG,UAAU;YACb,MAAM,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE;YAC5B,OAAO,EAAE;gBACP;oBACE,IAAI,EAAE,MAAM;oBACZ,QAAQ,EAAE,UAAU,CAAC,WAAW;
|
|
1
|
+
{"version":3,"file":"vercelAttachmentAdapter.js","sourceRoot":"","sources":["../../../src/ui/utils/vercelAttachmentAdapter.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAEhC,MAAM,cAAc,GAAG,CAAC,IAAU,EAAE,EAAE,CACpC,IAAI,OAAO,CAAS,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;IACtC,MAAM,MAAM,GAAG,IAAI,UAAU,EAAE,CAAC;IAEhC,MAAM,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,MAAgB,CAAC,CAAC;IACvD,MAAM,CAAC,OAAO,GAAG,CAAC,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAE1C,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;AAC7B,CAAC,CAAC,CAAC;AAEL,MAAM,CAAC,MAAM,uBAAuB,GAAsB;IACxD,MAAM,EACJ,wFAAwF;IAC1F,KAAK,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE;QAChB,OAAO;YACL,EAAE,EAAE,UAAU,EAAE;YAChB,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM;YACvD,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,IAAI;YACJ,WAAW,EAAE,IAAI,CAAC,IAAI;YACtB,OAAO,EAAE,EAAE;YACX,MAAM,EAAE,EAAE,IAAI,EAAE,iBAAiB,EAAE,MAAM,EAAE,eAAe,EAAE;SAC7D,CAAC;IACJ,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,UAAU;QACnB,OAAO;QACP,OAAO;YACL,GAAG,UAAU;YACb,MAAM,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE;YAC5B,OAAO,EAAE;gBACP;oBACE,IAAI,EAAE,MAAM;oBACZ,QAAQ,EAAE,UAAU,CAAC,WAAW,IAAI,EAAE;oBACtC,QAAQ,EAAE,UAAU,CAAC,IAAI;oBACzB,IAAI,EAAE,MAAM,cAAc,CAAC,UAAU,CAAC,IAAI,CAAC;iBAC5C;aACF;SACF,CAAC;IACJ,CAAC;IACD,KAAK,CAAC,MAAM;QACV,OAAO;IACT,CAAC;CACF,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@assistant-ui/react-ai-sdk",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.9",
|
|
4
4
|
"description": "Vercel AI SDK adapter for assistant-ui",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai-sdk",
|
|
@@ -31,12 +31,12 @@
|
|
|
31
31
|
],
|
|
32
32
|
"sideEffects": false,
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@ai-sdk/react": "^3.0.
|
|
35
|
-
"ai": "^6.0.
|
|
34
|
+
"@ai-sdk/react": "^3.0.100",
|
|
35
|
+
"ai": "^6.0.98",
|
|
36
36
|
"zod": "^4.3.6"
|
|
37
37
|
},
|
|
38
38
|
"peerDependencies": {
|
|
39
|
-
"@assistant-ui/react": "^0.12.
|
|
39
|
+
"@assistant-ui/react": "^0.12.12",
|
|
40
40
|
"@types/react": "*",
|
|
41
41
|
"assistant-cloud": "*",
|
|
42
42
|
"react": "^18 || ^19"
|
|
@@ -50,12 +50,16 @@
|
|
|
50
50
|
}
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
|
+
"@testing-library/react": "^16.3.2",
|
|
53
54
|
"@types/json-schema": "^7.0.15",
|
|
54
|
-
"@types/react": "^19.2.
|
|
55
|
+
"@types/react": "^19.2.14",
|
|
56
|
+
"@types/react-dom": "^19.2.3",
|
|
57
|
+
"jsdom": "^28.1.0",
|
|
55
58
|
"react": "^19.2.4",
|
|
56
|
-
"
|
|
59
|
+
"vitest": "^4.0.18",
|
|
60
|
+
"@assistant-ui/react": "0.12.12",
|
|
57
61
|
"@assistant-ui/x-buildutils": "0.0.1",
|
|
58
|
-
"assistant-stream": "0.3.
|
|
62
|
+
"assistant-stream": "0.3.4"
|
|
59
63
|
},
|
|
60
64
|
"publishConfig": {
|
|
61
65
|
"access": "public",
|
|
@@ -71,6 +75,8 @@
|
|
|
71
75
|
"url": "https://github.com/assistant-ui/assistant-ui/issues"
|
|
72
76
|
},
|
|
73
77
|
"scripts": {
|
|
74
|
-
"build": "aui-build"
|
|
78
|
+
"build": "aui-build",
|
|
79
|
+
"test": "vitest run",
|
|
80
|
+
"test:watch": "vitest"
|
|
75
81
|
}
|
|
76
82
|
}
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
// @vitest-environment jsdom
|
|
2
|
+
|
|
3
|
+
import { act, renderHook, waitFor } from "@testing-library/react";
|
|
4
|
+
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
5
|
+
|
|
6
|
+
// Mock only the sibling module that requires AUI store context (not available
|
|
7
|
+
// in isolation). Every other dependency — useExternalStoreRuntime,
|
|
8
|
+
// useToolInvocations, the message converter — runs for real.
|
|
9
|
+
vi.mock("./useExternalHistory", () => ({
|
|
10
|
+
useExternalHistory: vi.fn(() => false),
|
|
11
|
+
toExportedMessageRepository: vi.fn(),
|
|
12
|
+
}));
|
|
13
|
+
|
|
14
|
+
import { useAISDKRuntime } from "./useAISDKRuntime";
|
|
15
|
+
|
|
16
|
+
const createChatHelpers = (messages: any[] = []) => {
|
|
17
|
+
let currentMessages = [...messages];
|
|
18
|
+
|
|
19
|
+
const chatHelpers: any = {
|
|
20
|
+
status: "ready",
|
|
21
|
+
error: null,
|
|
22
|
+
messages: currentMessages,
|
|
23
|
+
setMessages: vi.fn((next: any) => {
|
|
24
|
+
currentMessages =
|
|
25
|
+
typeof next === "function" ? next(currentMessages) : [...next];
|
|
26
|
+
chatHelpers.messages = currentMessages;
|
|
27
|
+
return currentMessages;
|
|
28
|
+
}),
|
|
29
|
+
sendMessage: vi.fn().mockResolvedValue(undefined),
|
|
30
|
+
regenerate: vi.fn().mockResolvedValue(undefined),
|
|
31
|
+
addToolResult: vi.fn(),
|
|
32
|
+
addToolOutput: vi.fn(),
|
|
33
|
+
stop: vi.fn(),
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
return chatHelpers;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
describe("useAISDKRuntime", () => {
|
|
40
|
+
beforeEach(() => {
|
|
41
|
+
vi.clearAllMocks();
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it("sends a new user message through the runtime", async () => {
|
|
45
|
+
const chat = createChatHelpers();
|
|
46
|
+
|
|
47
|
+
const { result } = renderHook(() => useAISDKRuntime(chat));
|
|
48
|
+
|
|
49
|
+
act(() => {
|
|
50
|
+
result.current.thread.append({
|
|
51
|
+
role: "user",
|
|
52
|
+
content: [{ type: "text", text: "hello" }],
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
await waitFor(() => {
|
|
57
|
+
expect(chat.sendMessage).toHaveBeenCalledTimes(1);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
expect(chat.sendMessage).toHaveBeenCalledWith(
|
|
61
|
+
expect.objectContaining({
|
|
62
|
+
role: "user",
|
|
63
|
+
parts: expect.arrayContaining([
|
|
64
|
+
expect.objectContaining({ type: "text", text: "hello" }),
|
|
65
|
+
]),
|
|
66
|
+
}),
|
|
67
|
+
expect.anything(),
|
|
68
|
+
);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it("forwards runConfig as metadata when sending", async () => {
|
|
72
|
+
const chat = createChatHelpers();
|
|
73
|
+
|
|
74
|
+
const { result } = renderHook(() => useAISDKRuntime(chat));
|
|
75
|
+
|
|
76
|
+
act(() => {
|
|
77
|
+
result.current.thread.append({
|
|
78
|
+
role: "user",
|
|
79
|
+
content: [{ type: "text", text: "hello" }],
|
|
80
|
+
runConfig: { custom: { model: "gpt-4.1" } },
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
await waitFor(() => {
|
|
85
|
+
expect(chat.sendMessage).toHaveBeenCalledWith(expect.anything(), {
|
|
86
|
+
metadata: { custom: { model: "gpt-4.1" } },
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it("cancels pending tool calls before sending a new message", async () => {
|
|
92
|
+
const chat = createChatHelpers([
|
|
93
|
+
{
|
|
94
|
+
id: "a1",
|
|
95
|
+
role: "assistant",
|
|
96
|
+
parts: [
|
|
97
|
+
{
|
|
98
|
+
type: "tool-weather",
|
|
99
|
+
toolCallId: "tc-1",
|
|
100
|
+
state: "input-available",
|
|
101
|
+
input: { city: "NYC" },
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
type: "tool-weather",
|
|
105
|
+
toolCallId: "tc-2",
|
|
106
|
+
state: "output-available",
|
|
107
|
+
input: { city: "LA" },
|
|
108
|
+
output: { temp: 70 },
|
|
109
|
+
},
|
|
110
|
+
],
|
|
111
|
+
},
|
|
112
|
+
]);
|
|
113
|
+
|
|
114
|
+
const { result } = renderHook(() => useAISDKRuntime(chat));
|
|
115
|
+
|
|
116
|
+
// Wait for the runtime to process the initial messages
|
|
117
|
+
await waitFor(() => {
|
|
118
|
+
expect(result.current.thread.getState().messages.length).toBeGreaterThan(
|
|
119
|
+
0,
|
|
120
|
+
);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
act(() => {
|
|
124
|
+
result.current.thread.append({
|
|
125
|
+
role: "user",
|
|
126
|
+
content: [{ type: "text", text: "continue" }],
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
await waitFor(() => {
|
|
131
|
+
expect(chat.sendMessage).toHaveBeenCalledTimes(1);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// Pending tool (tc-1) should be marked as cancelled
|
|
135
|
+
expect(chat.messages[0].parts[0].state).toBe("output-error");
|
|
136
|
+
expect(chat.messages[0].parts[0].errorText).toBe(
|
|
137
|
+
"User cancelled tool call by sending a new message.",
|
|
138
|
+
);
|
|
139
|
+
// Completed tool (tc-2) should remain unchanged
|
|
140
|
+
expect(chat.messages[0].parts[1].state).toBe("output-available");
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it("edit slices history to parentId and sends the edited message", async () => {
|
|
144
|
+
const chat = createChatHelpers([
|
|
145
|
+
{ id: "u1", role: "user", parts: [{ type: "text", text: "first" }] },
|
|
146
|
+
{
|
|
147
|
+
id: "a1",
|
|
148
|
+
role: "assistant",
|
|
149
|
+
parts: [{ type: "text", text: "first-answer" }],
|
|
150
|
+
},
|
|
151
|
+
{ id: "u2", role: "user", parts: [{ type: "text", text: "second" }] },
|
|
152
|
+
{
|
|
153
|
+
id: "a2",
|
|
154
|
+
role: "assistant",
|
|
155
|
+
parts: [{ type: "text", text: "second-answer" }],
|
|
156
|
+
},
|
|
157
|
+
]);
|
|
158
|
+
|
|
159
|
+
const { result } = renderHook(() => useAISDKRuntime(chat));
|
|
160
|
+
|
|
161
|
+
await waitFor(() => {
|
|
162
|
+
expect(result.current.thread.getState().messages.length).toBe(4);
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
// Append with parentId != last message triggers onEdit
|
|
166
|
+
act(() => {
|
|
167
|
+
result.current.thread.append({
|
|
168
|
+
role: "user",
|
|
169
|
+
parentId: "u1",
|
|
170
|
+
content: [{ type: "text", text: "rewrite first" }],
|
|
171
|
+
runConfig: { custom: { temperature: 0.2 } },
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
await waitFor(() => {
|
|
176
|
+
expect(chat.sendMessage).toHaveBeenCalledTimes(1);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
// sliceMessagesUntil("u1") keeps u1 + following assistant messages (a1)
|
|
180
|
+
expect(chat.messages.map((m: any) => m.id)).toEqual(["u1", "a1"]);
|
|
181
|
+
expect(chat.sendMessage).toHaveBeenCalledWith(
|
|
182
|
+
expect.objectContaining({ role: "user" }),
|
|
183
|
+
{ metadata: { custom: { temperature: 0.2 } } },
|
|
184
|
+
);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
it("reload slices history and regenerates with metadata", async () => {
|
|
188
|
+
const chat = createChatHelpers([
|
|
189
|
+
{ id: "u1", role: "user", parts: [{ type: "text", text: "first" }] },
|
|
190
|
+
{
|
|
191
|
+
id: "a1",
|
|
192
|
+
role: "assistant",
|
|
193
|
+
parts: [{ type: "text", text: "first-answer" }],
|
|
194
|
+
},
|
|
195
|
+
{ id: "u2", role: "user", parts: [{ type: "text", text: "second" }] },
|
|
196
|
+
{
|
|
197
|
+
id: "a2",
|
|
198
|
+
role: "assistant",
|
|
199
|
+
parts: [{ type: "text", text: "second-answer" }],
|
|
200
|
+
},
|
|
201
|
+
]);
|
|
202
|
+
|
|
203
|
+
const { result } = renderHook(() => useAISDKRuntime(chat));
|
|
204
|
+
|
|
205
|
+
await waitFor(() => {
|
|
206
|
+
expect(result.current.thread.getState().messages.length).toBe(4);
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
act(() => {
|
|
210
|
+
result.current.thread.startRun({
|
|
211
|
+
parentId: "u1",
|
|
212
|
+
runConfig: { custom: { maxTokens: 100 } },
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
await waitFor(() => {
|
|
217
|
+
expect(chat.regenerate).toHaveBeenCalledTimes(1);
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
expect(chat.messages.map((m: any) => m.id)).toEqual(["u1", "a1"]);
|
|
221
|
+
expect(chat.regenerate).toHaveBeenCalledWith({
|
|
222
|
+
metadata: { custom: { maxTokens: 100 } },
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
|
+
});
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
type AssistantRuntime,
|
|
11
11
|
type ThreadMessage,
|
|
12
12
|
type MessageFormatAdapter,
|
|
13
|
+
type MessageFormatItem,
|
|
13
14
|
type MessageFormatRepository,
|
|
14
15
|
useRuntimeAdapters,
|
|
15
16
|
INTERNAL,
|
|
@@ -30,6 +31,7 @@ import {
|
|
|
30
31
|
useExternalHistory,
|
|
31
32
|
toExportedMessageRepository,
|
|
32
33
|
} from "./useExternalHistory";
|
|
34
|
+
import { useStreamingTiming } from "./useStreamingTiming";
|
|
33
35
|
|
|
34
36
|
export type CustomToCreateMessageFunction = <
|
|
35
37
|
UI_MESSAGE extends UIMessage = UIMessage,
|
|
@@ -76,15 +78,18 @@ export const useAISDKRuntime = <UI_MESSAGE extends UIMessage = UIMessage>(
|
|
|
76
78
|
chatHelpers.status === "streaming" ||
|
|
77
79
|
hasExecutingTools;
|
|
78
80
|
|
|
81
|
+
const messageTiming = useStreamingTiming(chatHelpers.messages, isRunning);
|
|
82
|
+
|
|
79
83
|
const messages = AISDKMessageConverter.useThreadMessages({
|
|
80
84
|
isRunning,
|
|
81
85
|
messages: chatHelpers.messages,
|
|
82
86
|
metadata: useMemo(
|
|
83
87
|
() => ({
|
|
84
88
|
toolStatuses,
|
|
89
|
+
messageTiming,
|
|
85
90
|
...(chatHelpers.error && { error: chatHelpers.error.message }),
|
|
86
91
|
}),
|
|
87
|
-
[toolStatuses, chatHelpers.error],
|
|
92
|
+
[toolStatuses, messageTiming, chatHelpers.error],
|
|
88
93
|
),
|
|
89
94
|
});
|
|
90
95
|
|
|
@@ -174,20 +179,39 @@ export const useAISDKRuntime = <UI_MESSAGE extends UIMessage = UIMessage>(
|
|
|
174
179
|
.flat(),
|
|
175
180
|
),
|
|
176
181
|
onExportExternalState: (): MessageFormatRepository<UI_MESSAGE> => {
|
|
177
|
-
// Export the thread's MessageRepository
|
|
178
182
|
const exported = runtimeRef.current.thread.export();
|
|
179
183
|
|
|
180
|
-
|
|
184
|
+
const expandedMessages: MessageFormatItem<UI_MESSAGE>[] = [];
|
|
185
|
+
const lastInnerIdMap = new Map<string, string>();
|
|
186
|
+
|
|
187
|
+
for (const item of exported.messages) {
|
|
188
|
+
const innerMessages = getExternalStoreMessages<UI_MESSAGE>(
|
|
189
|
+
item.message,
|
|
190
|
+
);
|
|
191
|
+
let parentId =
|
|
192
|
+
item.parentId != null
|
|
193
|
+
? (lastInnerIdMap.get(item.parentId) ?? item.parentId)
|
|
194
|
+
: null;
|
|
195
|
+
for (const innerMessage of innerMessages) {
|
|
196
|
+
expandedMessages.push({ parentId, message: innerMessage });
|
|
197
|
+
parentId = aiSDKV6FormatAdapter.getId(innerMessage as UIMessage);
|
|
198
|
+
}
|
|
199
|
+
if (innerMessages.length > 0) {
|
|
200
|
+
lastInnerIdMap.set(
|
|
201
|
+
item.message.id,
|
|
202
|
+
aiSDKV6FormatAdapter.getId(
|
|
203
|
+
innerMessages[innerMessages.length - 1]! as UIMessage,
|
|
204
|
+
),
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
181
209
|
const result: MessageFormatRepository<UI_MESSAGE> = {
|
|
182
|
-
messages:
|
|
183
|
-
parentId: item.parentId,
|
|
184
|
-
message: getExternalStoreMessages<UI_MESSAGE>(item.message)[0]!,
|
|
185
|
-
})),
|
|
210
|
+
messages: expandedMessages,
|
|
186
211
|
};
|
|
187
212
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
result.headId = exported.headId;
|
|
213
|
+
if (exported.headId != null) {
|
|
214
|
+
result.headId = lastInnerIdMap.get(exported.headId) ?? exported.headId;
|
|
191
215
|
}
|
|
192
216
|
|
|
193
217
|
return result;
|