-
Notifications
You must be signed in to change notification settings - Fork 26
feat(ui): add room message export/import #118
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,5 @@ | ||
| import type { CommonContentPart } from 'xsai' | ||
| import type { Message, MessageRole } from '~/types/messages' | ||
| import type { ExportedRoomMessages, Message, MessageRole } from '~/types/messages' | ||
| import { defineStore } from 'pinia' | ||
| import { computed, ref } from 'vue' | ||
| import { useMessageModel } from '~/models/messages' | ||
|
|
@@ -203,6 +203,94 @@ export const useMessagesStore = defineStore('messages', () => { | |
| return messages.value.some(message => message.parent_id === messageId) | ||
| } | ||
|
|
||
| async function exportRoomMessages(roomId: string): Promise<ExportedRoomMessages> { | ||
| const room = roomsStore.rooms.find(item => item.id === roomId) | ||
| if (!room) { | ||
| throw new Error('Room not found') | ||
| } | ||
|
|
||
| const roomMessages = await messageModel.getByRoomId(roomId) | ||
| return { | ||
| version: 1, | ||
| exported_at: new Date().toISOString(), | ||
| room: { | ||
| id: room.id, | ||
| name: room.name, | ||
| }, | ||
| messages: roomMessages.map(message => ({ | ||
| id: message.id, | ||
| parent_id: message.parent_id, | ||
| role: message.role as MessageRole, | ||
| provider: message.provider, | ||
| model: message.model, | ||
| summary: message.summary, | ||
| show_summary: message.show_summary ?? false, | ||
| memory: message.memory ?? [], | ||
| content: message.content, | ||
| })), | ||
| } | ||
| } | ||
|
|
||
| async function importRoomMessages(roomId: string, payload: ExportedRoomMessages) { | ||
| if (!payload || payload.version !== 1) { | ||
| throw new Error('Unsupported export format') | ||
| } | ||
|
|
||
| const pending = [...payload.messages] | ||
| const idMap = new Map<string, string>() | ||
| let guard = 0 | ||
|
|
||
| while (pending.length > 0) { | ||
| guard += 1 | ||
| if (guard > payload.messages.length + 1) { | ||
| throw new Error('Failed to resolve message parent links') | ||
| } | ||
|
|
||
| let progressed = false | ||
| const remaining: typeof pending = [] | ||
|
|
||
| for (const message of pending) { | ||
| const parentId = message.parent_id ? idMap.get(message.parent_id) : null | ||
| if (message.parent_id && !parentId) { | ||
| remaining.push(message) | ||
| continue | ||
| } | ||
|
|
||
| const [created] = await messageModel.create({ | ||
| role: message.role, | ||
| parent_id: parentId ?? null, | ||
| provider: message.provider, | ||
| model: message.model, | ||
| room_id: roomId, | ||
| memory: message.memory ?? [], | ||
| summary: message.summary ?? null, | ||
| }) | ||
|
|
||
| if (message.content.length > 0) { | ||
| await messageModel.appendContentBatch(created.id, message.content) | ||
| } | ||
|
|
||
| if (message.show_summary) { | ||
| await messageModel.updateShowSummary(created.id, true) | ||
| } | ||
|
|
||
| idMap.set(message.id, created.id) | ||
| progressed = true | ||
| } | ||
|
Comment on lines
+252
to
+279
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The To improve performance, consider batching these operations. For each level of the message tree dependency, you could:
This would reduce the number of round-trips to the database and should significantly speed up the import process for large files. |
||
|
|
||
| if (!progressed) { | ||
| throw new Error('Failed to import messages') | ||
| } | ||
|
|
||
| pending.length = 0 | ||
| pending.push(...remaining) | ||
| } | ||
|
|
||
| if (roomsStore.currentRoomId === roomId) { | ||
| await retrieveMessages() | ||
| } | ||
| } | ||
|
|
||
| return { | ||
| // State | ||
| messages, | ||
|
|
@@ -235,5 +323,7 @@ export const useMessagesStore = defineStore('messages', () => { | |
| updateShowSummary, | ||
|
|
||
| hasChildren, | ||
| exportRoomMessages, | ||
| importRoomMessages, | ||
| } | ||
| }) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The type guard
isExportedRoomMessagesprovides a basic check on the imported data, but it could be more robust. It currently verifies thatvalue.messagesis an array, but it doesn't validate the contents of the array. If themessagesarray contains items that don't match theExportedMessageinterface, it could lead to runtime errors later in theimportRoomMessagesfunction inmessages.ts. I suggest adding a more thorough validation by checking that each item in the array has the expected structure.