Skip to content

Commit 142576e

Browse files
authored
feat (ui): support message replacement in chat via messageId param on sendMessage (vercel#6775)
## Background Editing and submitting user messages is a common task in advanced chat UIs that should be easy to do with the AI SDK. ## Summary Add `messageId` option to `sendMessage`. If set, it replaces an existing message. ## Verification Integrated message replace into `next` example and tested it manually.
1 parent b4b4bb2 commit 142576e

File tree

7 files changed

+608
-16
lines changed

7 files changed

+608
-16
lines changed

.changeset/tall-garlics-sit.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'ai': patch
3+
---
4+
5+
feat (ui): support message replacement in chat via messageId param on sendMessage

examples/next/app/api/chat/route.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,18 @@ export async function POST(req: Request) {
2121
let messages: MyUIMessage[] = chat.messages;
2222

2323
if (trigger === 'submit-user-message') {
24-
messages = [...messages, message!];
24+
if (messageId != null) {
25+
const messageIndex = messages.findIndex(m => m.id === messageId);
26+
27+
if (messageIndex === -1) {
28+
throw new Error(`message ${messageId} not found`);
29+
}
30+
31+
messages = messages.slice(0, messageIndex);
32+
messages.push(message!);
33+
} else {
34+
messages = [...messages, message!];
35+
}
2536
} else if (trigger === 'regenerate-assistant-message') {
2637
const messageIndex =
2738
messageId == null

examples/next/app/chat/[chatId]/chat.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ export default function ChatComponent({
4343
trigger: 'submit-user-message',
4444
id,
4545
message: messages[messages.length - 1],
46+
messageId,
4647
},
4748
};
4849

@@ -73,7 +74,13 @@ export default function ChatComponent({
7374
return (
7475
<div className="flex flex-col w-full max-w-md py-24 mx-auto stretch">
7576
{messages.map(message => (
76-
<Message key={message.id} message={message} regenerate={regenerate} />
77+
<Message
78+
key={message.id}
79+
message={message}
80+
regenerate={regenerate}
81+
sendMessage={sendMessage}
82+
status={status}
83+
/>
7784
))}
7885
<ChatInput
7986
status={status}

examples/next/app/chat/[chatId]/message.tsx

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,22 @@
11
import { MyUIMessage } from '@/util/chat-schema';
2+
import { ChatStatus } from 'ai';
23

34
export default function Message({
45
message,
6+
status,
57
regenerate,
8+
sendMessage,
69
}: {
10+
status: ChatStatus;
711
message: MyUIMessage;
812
regenerate: ({ messageId }: { messageId: string }) => void;
13+
sendMessage: ({
14+
text,
15+
messageId,
16+
}: {
17+
text: string;
18+
messageId?: string;
19+
}) => void;
920
}) {
1021
const date = message.metadata?.createdAt
1122
? new Date(message.metadata.createdAt).toLocaleString()
@@ -25,12 +36,24 @@ export default function Message({
2536
.join('')}
2637
</div>
2738
{message.role === 'user' && (
28-
<button
29-
onClick={() => regenerate({ messageId: message.id })}
30-
className="px-3 py-1 mt-2 text-sm transition-colors bg-gray-200 rounded-md hover:bg-gray-300"
31-
>
32-
Regenerate
33-
</button>
39+
<>
40+
<button
41+
onClick={() => regenerate({ messageId: message.id })}
42+
className="px-3 py-1 mt-2 text-sm transition-colors bg-gray-200 rounded-md hover:bg-gray-300"
43+
disabled={status !== 'ready'}
44+
>
45+
Regenerate
46+
</button>
47+
<button
48+
onClick={() =>
49+
sendMessage({ text: 'Hello', messageId: message.id })
50+
}
51+
className="px-3 py-1 mt-2 text-sm transition-colors bg-gray-200 rounded-md hover:bg-gray-300"
52+
disabled={status !== 'ready'}
53+
>
54+
Replace with Hello
55+
</button>
56+
</>
3457
)}
3558
</div>
3659
);

0 commit comments

Comments
 (0)