Skip to content

Commit 4d20425

Browse files
author
tfomkin
committed
feat: save/save as copy edited AI message functionality
1 parent 1ba83bb commit 4d20425

5 files changed

Lines changed: 119 additions & 40 deletions

File tree

libs/mobile/chat/features/use-edit-message/src/use-edit-message.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@ import { FormValues } from '@open-webui-react-native/mobile/shared/utils/form';
44
import {
55
chatApi,
66
ChatResponse,
7-
prepareUpdateMessageInChatPayload,
87
prepareCompleteChatPayload,
98
prepareUpdateMessageToSendPayload,
9+
prepareEditAssistantMessagePayload,
10+
prepareCopyEditedMessagePayload,
1011
} from '@open-webui-react-native/shared/data-access/api';
12+
import { Role } from '@open-webui-react-native/shared/data-access/common';
1113
import { socketService } from '@open-webui-react-native/shared/data-access/websocket';
1214

1315
interface UseEditMessageProps {
@@ -47,7 +49,7 @@ export const useEditMessage = ({ chat, modelId }: UseEditMessageProps): typeof r
4749
return;
4850
}
4951

50-
const preparedChat = prepareUpdateMessageInChatPayload(chat, editingMessageId, message);
52+
const preparedChat = prepareCopyEditedMessagePayload(chat, editingMessageId, message);
5153

5254
await updateChat(preparedChat);
5355
cancelEditing();
@@ -60,10 +62,18 @@ export const useEditMessage = ({ chat, modelId }: UseEditMessageProps): typeof r
6062
const chatHistory = chat.chat.history;
6163
const editedMessage = chatHistory.messages[editingMessageId];
6264

63-
const preparedChat = prepareUpdateMessageToSendPayload(chat, message, modelId, editedMessage.parentId);
65+
let preparedChat: ChatResponse;
66+
67+
if (editedMessage.role === Role.ASSISTANT) {
68+
preparedChat = prepareEditAssistantMessagePayload(chat, editingMessageId, message);
69+
} else {
70+
preparedChat = prepareUpdateMessageToSendPayload(chat, message, modelId, editedMessage.parentId);
71+
}
6472

6573
await updateChat(preparedChat, {
6674
onSuccess: (data) => {
75+
if (editedMessage.role === Role.ASSISTANT) return;
76+
6777
const completePayload = prepareCompleteChatPayload({
6878
chatId: data.id,
6979
messageId: data.chat!.history.currentId,

libs/shared/data-access/api/src/lib/chats/utils/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,6 @@ export * from './invalidate-archived-chat-list-query';
1414
export * from './invalidate-search-chats-query';
1515
export * from './create-messages-list';
1616
export * from './patch-chat-with-selected-messages';
17-
export * from './prepare-update-message-in-chat-payload';
1817
export * from './prepare-update-message-to-send-payload';
18+
export * from './prepare-edit-assistant-message-payload';
19+
export * from './prepare-copy-edited-message-payload';
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import { Role } from '@open-webui-react-native/shared/data-access/common';
2+
import { ChatResponse, Message } from '../models';
3+
4+
export function prepareCopyEditedMessagePayload(
5+
oldData: ChatResponse,
6+
messageId: string,
7+
newContent: string,
8+
): ChatResponse {
9+
const history = oldData.chat.history;
10+
const messagesMap = { ...history.messages };
11+
12+
const target = messagesMap[messageId];
13+
if (!target) return oldData;
14+
15+
const isAIMessage = target.role === Role.ASSISTANT;
16+
17+
const updatedMessage: Message = {
18+
...target,
19+
content: newContent,
20+
};
21+
messagesMap[messageId] = updatedMessage;
22+
23+
if (!isAIMessage) {
24+
const patchedMessagesList = oldData.chat.messages.map((msg) => (msg.id === messageId ? updatedMessage : msg));
25+
const lastAssistantMessage = oldData.chat.history.lastAssistantMessage;
26+
27+
return {
28+
...oldData,
29+
chat: {
30+
...oldData.chat,
31+
history: {
32+
...history,
33+
messages: messagesMap,
34+
lastAssistantMessage,
35+
},
36+
messages: patchedMessagesList,
37+
},
38+
};
39+
}
40+
41+
const chain: Array<Message> = [];
42+
let pointer: Message | undefined = updatedMessage;
43+
44+
while (pointer) {
45+
chain.unshift(pointer);
46+
pointer = pointer.parentId ? messagesMap[pointer.parentId] : undefined;
47+
}
48+
49+
const newCurrentId = updatedMessage.id;
50+
51+
return {
52+
...oldData,
53+
chat: {
54+
...oldData.chat,
55+
history: {
56+
...history,
57+
messages: messagesMap,
58+
currentId: newCurrentId,
59+
lastAssistantMessage: updatedMessage,
60+
},
61+
messages: chain,
62+
},
63+
};
64+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { Role } from '@open-webui-react-native/shared/data-access/common';
2+
import { ChatResponse } from '../models';
3+
4+
export function prepareEditAssistantMessagePayload(
5+
oldData: ChatResponse,
6+
messageId: string,
7+
newContent: string,
8+
): ChatResponse {
9+
const history = oldData.chat.history;
10+
const messagesMap = { ...history.messages };
11+
12+
const target = messagesMap[messageId];
13+
if (!target || target.role !== Role.ASSISTANT) return oldData;
14+
15+
const updatedMessage = {
16+
...target,
17+
content: newContent,
18+
done: true,
19+
};
20+
21+
messagesMap[messageId] = updatedMessage;
22+
23+
const updatedMessagesList = oldData.chat.messages.map((m) => (m.id === messageId ? updatedMessage : m));
24+
25+
const lastAssistantMessage =
26+
history.lastAssistantMessage?.id === messageId ? updatedMessage : history.lastAssistantMessage;
27+
28+
return {
29+
...oldData,
30+
chat: {
31+
...oldData.chat,
32+
history: {
33+
...history,
34+
messages: messagesMap,
35+
lastAssistantMessage,
36+
},
37+
messages: updatedMessagesList,
38+
},
39+
};
40+
}

libs/shared/data-access/api/src/lib/chats/utils/prepare-update-message-in-chat-payload.ts

Lines changed: 0 additions & 36 deletions
This file was deleted.

0 commit comments

Comments
 (0)