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:
@@ -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 }),
|
||||
|
||||
@@ -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();
|
||||
},
|
||||
}));
|
||||
|
||||
Reference in New Issue
Block a user