feat: 多功能升级 — 流式逐字渲染、对话缓存、会话组织优化、记忆管理修复、性能仪表盘

- 前端消息流式逐字渲染 (AI-Core ChatStream → SSE → Gateway → WebSocket stream_chunk → fadeInUp + cursorBlink)
- 后端对话缓存 (conversationCache sync.Map, GET /sessions/:id/messages)
- 前端侧边栏历史多轮对话显示
- DevTools 性能监控图标移至首页仪表盘
- DevTools 用户记忆查询/删减功能修复 (补全 DELETE 数据链路)
- 后端和 DevTools 按用户分类组织实时活动会话 (map[userID]map[sessionID]*Client)
- 新增 docs/api-reference/ 路由参考文档
- 新增 docs/message-flow-architecture.md 消息链路架构文档
This commit is contained in:
2026-05-16 17:44:03 +08:00
parent 63513210b7
commit 186513f381
24 changed files with 1024 additions and 216 deletions
+35 -1
View File
@@ -6,6 +6,8 @@ interface ChatStore {
isTyping: boolean;
addMessage: (message: Message) => void;
appendToLastMessage: (content: string) => void;
finishStreaming: () => void;
setMessages: (messages: Message[]) => void;
setTyping: (typing: boolean) => void;
clearMessages: () => void;
@@ -17,9 +19,41 @@ export const useChatStore = create<ChatStore>((set) => ({
addMessage: (message) =>
set((state) => ({
messages: [...state.messages, message],
messages: [
...state.messages,
{
...message,
isStreaming: message.role === 'assistant' ? (message.isStreaming ?? true) : message.isStreaming,
},
],
})),
appendToLastMessage: (content) =>
set((state) => {
const msgs = [...state.messages];
const last = msgs[msgs.length - 1];
if (last && last.role === 'assistant' && last.isStreaming) {
msgs[msgs.length - 1] = {
...last,
content: last.content + content,
};
}
return { messages: msgs };
}),
finishStreaming: () =>
set((state) => {
const msgs = [...state.messages];
const last = msgs[msgs.length - 1];
if (last && last.role === 'assistant' && last.isStreaming) {
msgs[msgs.length - 1] = {
...last,
isStreaming: false,
};
}
return { messages: msgs, isTyping: false };
}),
setMessages: (messages) => set({ messages }),
setTyping: (typing) => set({ isTyping: typing }),
+45 -1
View File
@@ -1,22 +1,29 @@
import { create } from 'zustand';
import type { Session } from '@/types/session';
import type { Message } from '@/types/chat';
import { fetchSessionMessages as apiFetchMessages } from '@/api/client';
import { useChatStore } from '@/store/chatStore';
interface SessionStore {
sessions: Session[];
currentSessionId: string | null;
loading: boolean;
messages: Message[];
setSessions: (sessions: Session[]) => void;
addSession: (session: Session) => void;
removeSession: (id: string) => void;
setCurrentSessionId: (id: string | null) => void;
setLoading: (loading: boolean) => void;
setMessages: (messages: Message[]) => void;
clearMessages: () => void;
}
export const useSessionStore = create<SessionStore>((set) => ({
sessions: [],
currentSessionId: null,
loading: false,
messages: [],
setSessions: (sessions) => set({ sessions }),
addSession: (session) =>
@@ -25,7 +32,44 @@ export const useSessionStore = create<SessionStore>((set) => ({
set((state) => ({
sessions: state.sessions.filter((s) => s.id !== id),
currentSessionId: state.currentSessionId === id ? null : state.currentSessionId,
messages: state.currentSessionId === id ? [] : state.messages,
})),
setCurrentSessionId: (id) => set({ currentSessionId: id }),
setCurrentSessionId: async (id) => {
set({ currentSessionId: id, loading: true });
// 清除旧消息(同时清 chatStore)
if (id === null) {
set({ messages: [], loading: false });
useChatStore.getState().clearMessages();
return;
}
// 从后端加载历史消息
try {
const resp = await apiFetchMessages(id);
if (resp.data) {
const data = resp.data as { messages: Message[] };
const msgs = data.messages || [];
set({ messages: msgs, loading: false });
// 同步到 chatStore 以便 ChatContainer 渲染
useChatStore.getState().setMessages(msgs);
} else {
set({ messages: [], loading: false });
useChatStore.getState().clearMessages();
}
} catch {
set({ messages: [], loading: false });
useChatStore.getState().clearMessages();
}
},
setLoading: (loading) => set({ loading }),
setMessages: (messages) => {
set({ messages });
// 同步到 chatStore
useChatStore.getState().setMessages(messages);
},
clearMessages: () => {
set({ messages: [] });
useChatStore.getState().clearMessages();
},
}));