refactor(server,stage-ui): use local first message key

This commit is contained in:
RainbowBird
2026-01-24 19:32:39 +08:00
parent 7c9a23f250
commit fe65a207bf
7 changed files with 1561 additions and 18 deletions
@@ -0,0 +1,3 @@
ALTER TABLE "chat_members" DROP CONSTRAINT "chat_members_user_id_user_id_fk";
--> statement-breakpoint
ALTER TABLE "chat_members" DROP CONSTRAINT "chat_members_character_id_characters_id_fk";
File diff suppressed because it is too large Load Diff
+7
View File
@@ -50,6 +50,13 @@
"when": 1769186900274,
"tag": "0006_gray_stardust",
"breakpoints": true
},
{
"idx": 7,
"version": "7",
"when": 1769253946442,
"tag": "0007_dazzling_ken_ellis",
"breakpoints": true
}
]
}
+2 -4
View File
@@ -3,8 +3,6 @@ import type { InferInsertModel, InferSelectModel } from 'drizzle-orm'
import { integer, pgTable, text, timestamp } from 'drizzle-orm/pg-core'
import { nanoid } from '../utils/id'
import { user } from './accounts'
import { character } from './characters'
export const media = pgTable(
'media',
@@ -65,8 +63,8 @@ export const chatMembers = pgTable(
id: text('id').primaryKey().$defaultFn(() => nanoid()),
chatId: text('chat_id').notNull().references(() => chats.id, { onDelete: 'cascade' }),
memberType: text('member_type').notNull().$type<ChatMemberType>(),
userId: text('user_id').references(() => user.id, { onDelete: 'cascade' }),
characterId: text('character_id').references(() => character.id, { onDelete: 'cascade' }),
userId: text('user_id'),
characterId: text('character_id'),
},
)
+4 -3
View File
@@ -6,6 +6,7 @@ import type { ChatAssistantMessage, ChatSlices, ChatStreamEventContext, Streamin
import type { StreamEvent, StreamOptions } from './llm'
import { createQueue } from '@proj-airi/stream-kit'
import { nanoid } from 'nanoid'
import { defineStore, storeToRefs } from 'pinia'
import { ref, toRaw } from 'vue'
@@ -112,7 +113,7 @@ export const useChatOrchestratorStore = defineStore('chat-orchestrator', () => {
const sendingCreatedAt = Date.now()
const streamingMessageContext: ChatStreamEventContext = {
message: { role: 'user', content: sendingMessage, createdAt: sendingCreatedAt },
message: { role: 'user', content: sendingMessage, createdAt: sendingCreatedAt, id: nanoid() },
contexts: chatContext.getContextsSnapshot(),
composedMessage: [],
input: options.input,
@@ -127,7 +128,7 @@ export const useChatOrchestratorStore = defineStore('chat-orchestrator', () => {
const isForegroundSession = () => sessionId === activeSessionId.value
const buildingMessage: StreamingAssistantMessage = { role: 'assistant', content: '', slices: [], tool_results: [], createdAt: Date.now() }
const buildingMessage: StreamingAssistantMessage = { role: 'assistant', content: '', slices: [], tool_results: [], createdAt: Date.now(), id: nanoid() }
const updateUI = () => {
if (isForegroundSession()) {
@@ -170,7 +171,7 @@ export const useChatOrchestratorStore = defineStore('chat-orchestrator', () => {
return
const sessionMessagesForSend = chatSession.getSessionMessages(sessionId)
sessionMessagesForSend.push({ role: 'user', content: finalContent, createdAt: sendingCreatedAt })
sessionMessagesForSend.push({ role: 'user', content: finalContent, createdAt: sendingCreatedAt, id: nanoid() })
chatSession.persistSessionMessages(sessionId)
const categorizer = createStreamingCategorizer(activeProvider.value)
@@ -72,14 +72,28 @@ export const useChatSessionStore = defineStore('chat-session', () => {
return ''
}
function buildMessageId(sessionId: string, message: ChatHistoryItem, index: number) {
const createdAt = message.createdAt ?? 0
return `${sessionId}:${createdAt}:${index}`
function ensureSessionMessageIds(sessionId: string) {
const current = sessionMessages.value[sessionId] ?? []
let changed = false
const next = current.map((message) => {
if (message.id)
return message
changed = true
return {
...message,
id: nanoid(),
}
})
if (changed)
sessionMessages.value[sessionId] = next
return next
}
function buildSyncMessages(sessionId: string, messages: ChatHistoryItem[]) {
return messages.map((message, index) => ({
id: buildMessageId(sessionId, message, index),
function buildSyncMessages(messages: ChatHistoryItem[]) {
return messages.map(message => ({
id: message.id ?? nanoid(),
role: message.role,
content: extractMessageContent(message),
createdAt: message.createdAt,
@@ -106,13 +120,22 @@ export const useChatSessionStore = defineStore('chat-session', () => {
{ type: 'user', userId: userId.value },
]
if (cachedRecord.meta.characterId) {
if (cachedRecord.meta.characterId && cachedRecord.meta.characterId !== 'default') {
members.push({
type: 'character',
characterId: cachedRecord.meta.characterId,
})
}
const normalizedMessages = cachedRecord.messages.map(message => message.id ? message : { ...message, id: nanoid() })
if (normalizedMessages.some((message, index) => cachedRecord?.messages[index]?.id !== message.id)) {
cachedRecord = {
...cachedRecord,
messages: normalizedMessages,
}
await chatSessionsRepo.saveSession(sessionId, cachedRecord)
}
const res = await client.api.chats.sync.$post({
json: {
chat: {
@@ -123,7 +146,7 @@ export const useChatSessionStore = defineStore('chat-session', () => {
updatedAt: cachedRecord.meta.updatedAt,
},
members,
messages: buildSyncMessages(cachedRecord.meta.sessionId, cachedRecord.messages),
messages: buildSyncMessages(cachedRecord.messages),
},
})
@@ -155,6 +178,7 @@ export const useChatSessionStore = defineStore('chat-session', () => {
return {
role: 'system',
content,
id: nanoid(),
createdAt: Date.now(),
} satisfies ChatHistoryItem
}
@@ -193,7 +217,7 @@ export const useChatSessionStore = defineStore('chat-session', () => {
const meta = sessionMetas.value[sessionId]
if (!meta)
return
const messages = snapshotMessages(sessionMessages.value[sessionId] ?? [])
const messages = snapshotMessages(ensureSessionMessageIds(sessionId))
const now = Date.now()
const updatedMeta = {
...meta,
+2 -2
View File
@@ -45,7 +45,7 @@ export interface ContextMessage extends ContextUpdate<Record<string, unknown>, s
createdAt: number
}
export type ChatHistoryItem = (ChatMessage | ErrorMessage) & { context?: ContextMessage } & { createdAt?: number }
export type ChatHistoryItem = (ChatMessage | ErrorMessage) & { context?: ContextMessage } & { createdAt?: number, id?: string }
export interface ChatStreamEventContext {
message: ChatHistoryItem
@@ -65,4 +65,4 @@ export type ChatStreamEvent
| { type: 'assistant-end', message: string, sessionId: string, context: ChatStreamEventContext }
| { type: 'assistant-message', message: ChatAssistantMessage, sessionId: string, messageText: string, context: ChatStreamEventContext }
export type StreamingAssistantMessage = ChatAssistantMessage & { context?: ContextMessage } & { createdAt?: number }
export type StreamingAssistantMessage = ChatAssistantMessage & { context?: ContextMessage } & { createdAt?: number, id?: string }