@aws-amplify/ui-react-ai 1.5.0 → 1.5.2
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.
|
@@ -2,19 +2,19 @@ const contentFromEvents = (contentBlocks) => {
|
|
|
2
2
|
if (!contentBlocks)
|
|
3
3
|
return [];
|
|
4
4
|
return contentBlocks.map((contentBlock) => {
|
|
5
|
-
|
|
5
|
+
// Filter out sparse array holes from out-of-order direct index assignment
|
|
6
|
+
const events = contentBlock.filter(Boolean);
|
|
7
|
+
const isTextBlock = events.some((event) => event.text);
|
|
6
8
|
if (isTextBlock) {
|
|
7
9
|
return {
|
|
8
|
-
text:
|
|
9
|
-
.map((event) =>
|
|
10
|
-
return event.text;
|
|
11
|
-
})
|
|
10
|
+
text: events
|
|
11
|
+
.map((event) => event.text ?? '')
|
|
12
12
|
.join(''),
|
|
13
13
|
};
|
|
14
14
|
}
|
|
15
15
|
// tool use is never chunked
|
|
16
|
-
if (
|
|
17
|
-
return { toolUse:
|
|
16
|
+
if (events[0]?.toolUse) {
|
|
17
|
+
return { toolUse: events[0].toolUse };
|
|
18
18
|
}
|
|
19
19
|
});
|
|
20
20
|
};
|
|
@@ -4,6 +4,13 @@ import { isFunction } from '@aws-amplify/ui';
|
|
|
4
4
|
import { contentFromEvents } from './contentFromEvents.mjs';
|
|
5
5
|
import { exhaustivelyListMessages } from './exhaustivelyListMessages.mjs';
|
|
6
6
|
|
|
7
|
+
// Synthetic ids used for the messages optimistically added by
|
|
8
|
+
// `handleSendMessage` before the backend assigns real ids. The streamed
|
|
9
|
+
// assistant events carry the real message id, so the first event of a turn
|
|
10
|
+
// won't match by id; we use this to swap the placeholder in place instead of
|
|
11
|
+
// appending a second (empty) assistant bubble.
|
|
12
|
+
const OPTIMISTIC_USER_MESSAGE_ID = 'temp-id';
|
|
13
|
+
const OPTIMISTIC_ASSISTANT_MESSAGE_ID = 'temp-id-2';
|
|
7
14
|
function hasStarted(state) {
|
|
8
15
|
return ['initialLoading', 'initialized'].includes(state);
|
|
9
16
|
}
|
|
@@ -174,11 +181,13 @@ function createUseAIConversation(client) {
|
|
|
174
181
|
contentBlocksRef.current[contentBlockIndex] = [event];
|
|
175
182
|
}
|
|
176
183
|
else {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
event
|
|
180
|
-
|
|
181
|
-
|
|
184
|
+
// Direct index assignment: idempotent, handles out-of-order and duplicates
|
|
185
|
+
if (contentBlockDeltaIndex !== undefined) {
|
|
186
|
+
currentBlock[contentBlockDeltaIndex] = event;
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
currentBlock.push(event);
|
|
190
|
+
}
|
|
182
191
|
}
|
|
183
192
|
}
|
|
184
193
|
setClientState((prev) => {
|
|
@@ -190,13 +199,27 @@ function createUseAIConversation(client) {
|
|
|
190
199
|
role: 'assistant',
|
|
191
200
|
isLoading: true,
|
|
192
201
|
};
|
|
202
|
+
// Match by real message ID (handles out-of-order / duplicate
|
|
203
|
+
// events for a message we've already started rendering). On the
|
|
204
|
+
// first event of a turn there is no match yet, so fall back to the
|
|
205
|
+
// optimistic assistant placeholder added by handleSendMessage;
|
|
206
|
+
// replacing it in place avoids leaving an empty assistant bubble.
|
|
207
|
+
const existingIndex = prev.data.messages.findIndex((m) => m.id === id);
|
|
208
|
+
const targetIndex = existingIndex >= 0
|
|
209
|
+
? existingIndex
|
|
210
|
+
: prev.data.messages.findIndex((m) => m.id === OPTIMISTIC_ASSISTANT_MESSAGE_ID);
|
|
211
|
+
const updatedMessages = targetIndex >= 0
|
|
212
|
+
? [
|
|
213
|
+
...prev.data.messages.slice(0, targetIndex),
|
|
214
|
+
message,
|
|
215
|
+
...prev.data.messages.slice(targetIndex + 1),
|
|
216
|
+
]
|
|
217
|
+
: [...prev.data.messages, message];
|
|
193
218
|
return {
|
|
194
219
|
...prev,
|
|
195
220
|
data: {
|
|
196
221
|
...prev.data,
|
|
197
|
-
|
|
198
|
-
// message, but maybe we should match it by message ID?
|
|
199
|
-
messages: [...prev.data.messages.slice(0, -1), message],
|
|
222
|
+
messages: updatedMessages,
|
|
200
223
|
},
|
|
201
224
|
};
|
|
202
225
|
});
|
|
@@ -235,14 +258,14 @@ function createUseAIConversation(client) {
|
|
|
235
258
|
content,
|
|
236
259
|
role: 'user',
|
|
237
260
|
createdAt: new Date().toISOString(),
|
|
238
|
-
id:
|
|
261
|
+
id: OPTIMISTIC_USER_MESSAGE_ID,
|
|
239
262
|
conversationId: conversation.id ?? '',
|
|
240
263
|
},
|
|
241
264
|
{
|
|
242
265
|
content: [{ text: ' ' }],
|
|
243
266
|
role: 'assistant',
|
|
244
267
|
createdAt: new Date().toISOString(),
|
|
245
|
-
id:
|
|
268
|
+
id: OPTIMISTIC_ASSISTANT_MESSAGE_ID,
|
|
246
269
|
conversationId: conversation.id ?? '',
|
|
247
270
|
isLoading: true,
|
|
248
271
|
},
|
package/dist/esm/version.mjs
CHANGED
package/dist/index.js
CHANGED
|
@@ -1230,7 +1230,7 @@ const PromptList = ({ setInput, suggestedPrompts = [], }) => {
|
|
|
1230
1230
|
})));
|
|
1231
1231
|
};
|
|
1232
1232
|
|
|
1233
|
-
const VERSION = '1.5.
|
|
1233
|
+
const VERSION = '1.5.2';
|
|
1234
1234
|
|
|
1235
1235
|
function AIConversationBase({ avatars, controls, messages, ...rest }) {
|
|
1236
1236
|
uiReactCore.useSetUserAgent({
|
|
@@ -1306,19 +1306,19 @@ const contentFromEvents = (contentBlocks) => {
|
|
|
1306
1306
|
if (!contentBlocks)
|
|
1307
1307
|
return [];
|
|
1308
1308
|
return contentBlocks.map((contentBlock) => {
|
|
1309
|
-
|
|
1309
|
+
// Filter out sparse array holes from out-of-order direct index assignment
|
|
1310
|
+
const events = contentBlock.filter(Boolean);
|
|
1311
|
+
const isTextBlock = events.some((event) => event.text);
|
|
1310
1312
|
if (isTextBlock) {
|
|
1311
1313
|
return {
|
|
1312
|
-
text:
|
|
1313
|
-
.map((event) =>
|
|
1314
|
-
return event.text;
|
|
1315
|
-
})
|
|
1314
|
+
text: events
|
|
1315
|
+
.map((event) => event.text ?? '')
|
|
1316
1316
|
.join(''),
|
|
1317
1317
|
};
|
|
1318
1318
|
}
|
|
1319
1319
|
// tool use is never chunked
|
|
1320
|
-
if (
|
|
1321
|
-
return { toolUse:
|
|
1320
|
+
if (events[0]?.toolUse) {
|
|
1321
|
+
return { toolUse: events[0].toolUse };
|
|
1322
1322
|
}
|
|
1323
1323
|
});
|
|
1324
1324
|
};
|
|
@@ -1341,6 +1341,13 @@ async function exhaustivelyListMessages({ conversation, messages = [], nextToken
|
|
|
1341
1341
|
};
|
|
1342
1342
|
}
|
|
1343
1343
|
|
|
1344
|
+
// Synthetic ids used for the messages optimistically added by
|
|
1345
|
+
// `handleSendMessage` before the backend assigns real ids. The streamed
|
|
1346
|
+
// assistant events carry the real message id, so the first event of a turn
|
|
1347
|
+
// won't match by id; we use this to swap the placeholder in place instead of
|
|
1348
|
+
// appending a second (empty) assistant bubble.
|
|
1349
|
+
const OPTIMISTIC_USER_MESSAGE_ID = 'temp-id';
|
|
1350
|
+
const OPTIMISTIC_ASSISTANT_MESSAGE_ID = 'temp-id-2';
|
|
1344
1351
|
function hasStarted(state) {
|
|
1345
1352
|
return ['initialLoading', 'initialized'].includes(state);
|
|
1346
1353
|
}
|
|
@@ -1511,11 +1518,13 @@ function createUseAIConversation(client) {
|
|
|
1511
1518
|
contentBlocksRef.current[contentBlockIndex] = [event];
|
|
1512
1519
|
}
|
|
1513
1520
|
else {
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
event
|
|
1517
|
-
|
|
1518
|
-
|
|
1521
|
+
// Direct index assignment: idempotent, handles out-of-order and duplicates
|
|
1522
|
+
if (contentBlockDeltaIndex !== undefined) {
|
|
1523
|
+
currentBlock[contentBlockDeltaIndex] = event;
|
|
1524
|
+
}
|
|
1525
|
+
else {
|
|
1526
|
+
currentBlock.push(event);
|
|
1527
|
+
}
|
|
1519
1528
|
}
|
|
1520
1529
|
}
|
|
1521
1530
|
setClientState((prev) => {
|
|
@@ -1527,13 +1536,27 @@ function createUseAIConversation(client) {
|
|
|
1527
1536
|
role: 'assistant',
|
|
1528
1537
|
isLoading: true,
|
|
1529
1538
|
};
|
|
1539
|
+
// Match by real message ID (handles out-of-order / duplicate
|
|
1540
|
+
// events for a message we've already started rendering). On the
|
|
1541
|
+
// first event of a turn there is no match yet, so fall back to the
|
|
1542
|
+
// optimistic assistant placeholder added by handleSendMessage;
|
|
1543
|
+
// replacing it in place avoids leaving an empty assistant bubble.
|
|
1544
|
+
const existingIndex = prev.data.messages.findIndex((m) => m.id === id);
|
|
1545
|
+
const targetIndex = existingIndex >= 0
|
|
1546
|
+
? existingIndex
|
|
1547
|
+
: prev.data.messages.findIndex((m) => m.id === OPTIMISTIC_ASSISTANT_MESSAGE_ID);
|
|
1548
|
+
const updatedMessages = targetIndex >= 0
|
|
1549
|
+
? [
|
|
1550
|
+
...prev.data.messages.slice(0, targetIndex),
|
|
1551
|
+
message,
|
|
1552
|
+
...prev.data.messages.slice(targetIndex + 1),
|
|
1553
|
+
]
|
|
1554
|
+
: [...prev.data.messages, message];
|
|
1530
1555
|
return {
|
|
1531
1556
|
...prev,
|
|
1532
1557
|
data: {
|
|
1533
1558
|
...prev.data,
|
|
1534
|
-
|
|
1535
|
-
// message, but maybe we should match it by message ID?
|
|
1536
|
-
messages: [...prev.data.messages.slice(0, -1), message],
|
|
1559
|
+
messages: updatedMessages,
|
|
1537
1560
|
},
|
|
1538
1561
|
};
|
|
1539
1562
|
});
|
|
@@ -1572,14 +1595,14 @@ function createUseAIConversation(client) {
|
|
|
1572
1595
|
content,
|
|
1573
1596
|
role: 'user',
|
|
1574
1597
|
createdAt: new Date().toISOString(),
|
|
1575
|
-
id:
|
|
1598
|
+
id: OPTIMISTIC_USER_MESSAGE_ID,
|
|
1576
1599
|
conversationId: conversation.id ?? '',
|
|
1577
1600
|
},
|
|
1578
1601
|
{
|
|
1579
1602
|
content: [{ text: ' ' }],
|
|
1580
1603
|
role: 'assistant',
|
|
1581
1604
|
createdAt: new Date().toISOString(),
|
|
1582
|
-
id:
|
|
1605
|
+
id: OPTIMISTIC_ASSISTANT_MESSAGE_ID,
|
|
1583
1606
|
conversationId: conversation.id ?? '',
|
|
1584
1607
|
isLoading: true,
|
|
1585
1608
|
},
|
package/dist/types/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const VERSION = "1.5.
|
|
1
|
+
export declare const VERSION = "1.5.2";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aws-amplify/ui-react-ai",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.2",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"module": "dist/esm/index.mjs",
|
|
6
6
|
"exports": {
|
|
@@ -48,9 +48,9 @@
|
|
|
48
48
|
"react-dom": "^16.14 || ^17 || ^18 || ^19"
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"@aws-amplify/ui": "^6.
|
|
52
|
-
"@aws-amplify/ui-react": "^6.
|
|
53
|
-
"@aws-amplify/ui-react-core": "^3.
|
|
51
|
+
"@aws-amplify/ui": "^6.15.6",
|
|
52
|
+
"@aws-amplify/ui-react": "^6.15.6",
|
|
53
|
+
"@aws-amplify/ui-react-core": "^3.6.6"
|
|
54
54
|
},
|
|
55
55
|
"size-limit": [
|
|
56
56
|
{
|