299 lines
8.6 KiB
JavaScript
299 lines
8.6 KiB
JavaScript
'use client';
|
|
|
|
// ==================== 多会话管理模块 ====================
|
|
|
|
import { persistGet, persistSet } from './persistence';
|
|
|
|
const STORAGE_KEY = 'author-chat-sessions';
|
|
|
|
function generateId(prefix = 'session') {
|
|
return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`;
|
|
}
|
|
|
|
/**
|
|
* 从持久化存储读取全部会话数据 (Async)
|
|
*/
|
|
export async function loadSessionStore() {
|
|
if (typeof window === 'undefined') return { activeSessionId: null, sessions: [] };
|
|
try {
|
|
const store = await persistGet(STORAGE_KEY);
|
|
if (store) return store;
|
|
} catch { }
|
|
return { activeSessionId: null, sessions: [] };
|
|
}
|
|
|
|
/**
|
|
* 保存全部会话数据到持久化存储 (Async)
|
|
*/
|
|
export async function saveSessionStore(store) {
|
|
if (typeof window === 'undefined') return;
|
|
try {
|
|
await persistSet(STORAGE_KEY, store);
|
|
} catch { }
|
|
}
|
|
|
|
/**
|
|
* 兼容迁移:将旧 chatHistory 数组迁移为多会话结构
|
|
*/
|
|
export function migrateFromLegacy(legacyChatHistory) {
|
|
if (!legacyChatHistory || legacyChatHistory.length === 0) return null;
|
|
const session = {
|
|
id: generateId(),
|
|
title: autoTitle(legacyChatHistory),
|
|
createdAt: legacyChatHistory[0]?.timestamp || Date.now(),
|
|
updatedAt: Date.now(),
|
|
messages: legacyChatHistory,
|
|
};
|
|
return session;
|
|
}
|
|
|
|
/**
|
|
* 从首条用户消息自动生成标题
|
|
*/
|
|
function autoTitle(messages) {
|
|
const first = messages.find(m => m.role === 'user');
|
|
if (!first) return '新对话';
|
|
const text = first.content.slice(0, 30);
|
|
return text.length < first.content.length ? text + '…' : text;
|
|
}
|
|
|
|
// ==================== 会话级操作 ====================
|
|
|
|
/**
|
|
* 创建新的空白会话
|
|
*/
|
|
export function createSession(store) {
|
|
const session = {
|
|
id: generateId(), title: '新对话', createdAt: Date.now(),
|
|
updatedAt: Date.now(), messages: [],
|
|
};
|
|
const newStore = {
|
|
activeSessionId: session.id,
|
|
sessions: [...store.sessions, session],
|
|
};
|
|
saveSessionStore(newStore); // Run in background
|
|
return newStore;
|
|
}
|
|
|
|
/**
|
|
* 删除会话
|
|
*/
|
|
export function deleteSession(store, sessionId) {
|
|
const remaining = store.sessions.filter(s => s.id !== sessionId);
|
|
const newActive = store.activeSessionId === sessionId
|
|
? (remaining[remaining.length - 1]?.id || null)
|
|
: store.activeSessionId;
|
|
const newStore = { activeSessionId: newActive, sessions: remaining };
|
|
saveSessionStore(newStore);
|
|
return newStore;
|
|
}
|
|
|
|
/**
|
|
* 重命名会话
|
|
*/
|
|
export function renameSession(store, sessionId, newTitle) {
|
|
const newStore = {
|
|
...store,
|
|
sessions: store.sessions.map(s =>
|
|
s.id === sessionId ? { ...s, title: newTitle, updatedAt: Date.now() } : s
|
|
),
|
|
};
|
|
saveSessionStore(newStore);
|
|
return newStore;
|
|
}
|
|
|
|
/**
|
|
* 切换活动会话
|
|
*/
|
|
export function switchSession(store, sessionId) {
|
|
const newStore = { ...store, activeSessionId: sessionId };
|
|
saveSessionStore(newStore);
|
|
return newStore;
|
|
}
|
|
|
|
/**
|
|
* 获取当前活动会话
|
|
*/
|
|
export function getActiveSession(store) {
|
|
if (!store.activeSessionId) return null;
|
|
return store.sessions.find(s => s.id === store.activeSessionId) || null;
|
|
}
|
|
|
|
// ==================== 消息级操作 ====================
|
|
|
|
/**
|
|
* 向活动会话添加消息
|
|
*/
|
|
export function addMessage(store, msg) {
|
|
const newStore = {
|
|
...store,
|
|
sessions: store.sessions.map(s => {
|
|
if (s.id !== store.activeSessionId) return s;
|
|
const messages = [...s.messages, msg];
|
|
const title = s.messages.length === 0 && msg.role === 'user'
|
|
? (msg.content.slice(0, 30) + (msg.content.length > 30 ? '…' : ''))
|
|
: s.title;
|
|
return { ...s, messages, title, updatedAt: Date.now() };
|
|
}),
|
|
};
|
|
saveSessionStore(newStore);
|
|
return newStore;
|
|
}
|
|
|
|
/**
|
|
* 更新活动会话的最后一条消息(用于流式输出拼接)
|
|
*/
|
|
export function updateLastMessage(store, updater) {
|
|
const newStore = {
|
|
...store,
|
|
sessions: store.sessions.map(s => {
|
|
if (s.id !== store.activeSessionId) return s;
|
|
if (s.messages.length === 0) return s;
|
|
const messages = [...s.messages];
|
|
messages[messages.length - 1] = updater(messages[messages.length - 1]);
|
|
return { ...s, messages, updatedAt: Date.now() };
|
|
}),
|
|
};
|
|
return newStore;
|
|
}
|
|
|
|
/**
|
|
* 编辑指定消息的内容
|
|
*/
|
|
export function editMessage(store, msgId, newContent) {
|
|
const newStore = {
|
|
...store,
|
|
sessions: store.sessions.map(s => {
|
|
if (s.id !== store.activeSessionId) return s;
|
|
return {
|
|
...s,
|
|
messages: s.messages.map(m =>
|
|
m.id === msgId ? { ...m, content: newContent, editedAt: Date.now() } : m
|
|
),
|
|
updatedAt: Date.now(),
|
|
};
|
|
}),
|
|
};
|
|
saveSessionStore(newStore);
|
|
return newStore;
|
|
}
|
|
|
|
/**
|
|
* 删除指定消息
|
|
*/
|
|
export function deleteMessage(store, msgId) {
|
|
const newStore = {
|
|
...store,
|
|
sessions: store.sessions.map(s => {
|
|
if (s.id !== store.activeSessionId) return s;
|
|
return {
|
|
...s,
|
|
messages: s.messages.filter(m => m.id !== msgId),
|
|
updatedAt: Date.now(),
|
|
};
|
|
}),
|
|
};
|
|
saveSessionStore(newStore);
|
|
return newStore;
|
|
}
|
|
|
|
/**
|
|
* 从指定消息创建分支 — 复制该消息及之前的所有消息到新会话
|
|
*/
|
|
export function createBranch(store, msgId) {
|
|
const activeSession = getActiveSession(store);
|
|
if (!activeSession) return store;
|
|
|
|
const msgIndex = activeSession.messages.findIndex(m => m.id === msgId);
|
|
if (msgIndex < 0) return store;
|
|
|
|
const branchedMessages = activeSession.messages.slice(0, msgIndex + 1).map(m => ({
|
|
...m, id: generateId('msg'),
|
|
}));
|
|
|
|
const branchSession = {
|
|
id: generateId(), title: `${activeSession.title} (分支)`,
|
|
createdAt: Date.now(), updatedAt: Date.now(), messages: branchedMessages,
|
|
};
|
|
|
|
const newStore = {
|
|
activeSessionId: branchSession.id,
|
|
sessions: [...store.sessions, branchSession],
|
|
};
|
|
saveSessionStore(newStore);
|
|
return newStore;
|
|
}
|
|
|
|
/**
|
|
* 替换活动会话的消息列表(用于总结等批量操作)
|
|
*/
|
|
export function replaceMessages(store, newMessages) {
|
|
const newStore = {
|
|
...store,
|
|
sessions: store.sessions.map(s => {
|
|
if (s.id !== store.activeSessionId) return s;
|
|
return { ...s, messages: newMessages, updatedAt: Date.now() };
|
|
}),
|
|
};
|
|
saveSessionStore(newStore);
|
|
return newStore;
|
|
}
|
|
|
|
/**
|
|
* 为指定消息添加一个变体(多次重新生成的结果)
|
|
*/
|
|
export function addVariant(store, msgId, variantData) {
|
|
const newStore = {
|
|
...store,
|
|
sessions: store.sessions.map(s => {
|
|
if (s.id !== store.activeSessionId) return s;
|
|
return {
|
|
...s,
|
|
messages: s.messages.map(m => {
|
|
if (m.id !== msgId) return m;
|
|
const existingVariants = m.variants || [{
|
|
content: m.content,
|
|
thinking: m.thinking || '',
|
|
timestamp: m.timestamp,
|
|
}];
|
|
const newVariants = [...existingVariants, variantData];
|
|
return {
|
|
...m,
|
|
variants: newVariants,
|
|
activeVariant: newVariants.length - 1,
|
|
content: variantData.content,
|
|
thinking: variantData.thinking || '',
|
|
};
|
|
}),
|
|
updatedAt: Date.now(),
|
|
};
|
|
}),
|
|
};
|
|
saveSessionStore(newStore);
|
|
return newStore;
|
|
}
|
|
|
|
/**
|
|
* 切换指定消息的当前显示变体
|
|
*/
|
|
export function switchVariant(store, msgId, variantIndex) {
|
|
const newStore = {
|
|
...store,
|
|
sessions: store.sessions.map(s => {
|
|
if (s.id !== store.activeSessionId) return s;
|
|
return {
|
|
...s,
|
|
messages: s.messages.map(m => {
|
|
if (m.id !== msgId || !m.variants) return m;
|
|
const idx = Math.max(0, Math.min(variantIndex, m.variants.length - 1));
|
|
const variant = m.variants[idx];
|
|
return { ...m, activeVariant: idx, content: variant.content, thinking: variant.thinking || '' };
|
|
}),
|
|
updatedAt: Date.now(),
|
|
};
|
|
}),
|
|
};
|
|
saveSessionStore(newStore);
|
|
return newStore;
|
|
}
|