diff --git a/backend/api/route.py b/backend/api/route.py index e68013e..d19ee1d 100644 --- a/backend/api/route.py +++ b/backend/api/route.py @@ -1,7 +1,7 @@ from fastapi import APIRouter from ..core.items import ChatRequest from ..tools.get_all_role_and_chat import get_all_role_and_chat -from ..core.models import chat_history +from ..core.models.chat_history import ChatHistory # 修改导入语句 router = APIRouter() @@ -12,9 +12,9 @@ def get_all_role_and_chat_endpoint(): return get_all_role_and_chat() # 2. 根据rolename和chatname读取特定聊天记录 -@router.post("/chat_box/get_chat_history") +@router.get("/chat_box/get_chat_history") async def get_chat_history_endpoint(role_name: str, chat_name: str): # 实例化工具类 - reader = chat_history.load_from_file(role_name, chat_name) + reader = ChatHistory.load_from_file(role_name, chat_name) return reader.to_chatbox_format() diff --git a/backend/core/models/chat_history.py b/backend/core/models/chat_history.py index e9dc7a4..5f96ae9 100644 --- a/backend/core/models/chat_history.py +++ b/backend/core/models/chat_history.py @@ -49,7 +49,8 @@ class ChatMetadata(BaseModel): note_interval: int = Field(0, description="笔记插入间隔数") note_position: int = Field(0, description="笔记插入位置") note_depth: int = Field(0, description="笔记插入深度") - note_role: str = Field("", description="笔记使用角色类型") + # 0:System,1:User,2:Assistant + note_role: int = Field("", description="笔记使用角色类型") # 扩展信息 extensions: Dict[str, Any] = Field( diff --git a/data/chat/testRole1/111.jsonl b/data/chat/testRole1/111.jsonl new file mode 100644 index 0000000..58a165e --- /dev/null +++ b/data/chat/testRole1/111.jsonl @@ -0,0 +1,3 @@ +{"integrity": "test-uuid-1", "chat_id_hash": "hash1", "note_prompt": "", "note_interval": 0, "note_position": 0, "note_depth": 0, "note_role": 0, "extensions": {}, "timedWorldInfo": {}, "variables": {}, "tainted": false, "lastInContextMessageId": -1} +{"name": "User", "is_user": true, "is_system": false, "floor": 0,"send_date": "1700000000000", "mes": "你好,我是新来的冒险者。", "extra": {}, "swipes": [], "swipe_id": 0, "swipe_info": [], "title": "", "force_avatar": null, "variables": [], "variables_initialized": [], "is_ejs_processed": [], "gen_started": null, "gen_finished": null} +{"name": "Guide", "is_user": false, "is_system": false, "floor": 1,"send_date": "1700000001000", "mes": "欢迎来到我们的世界!你需要什么帮助?", "extra": {}, "swipes": [], "swipe_id": 0, "swipe_info": [], "title": "", "force_avatar": null, "variables": [], "variables_initialized": [], "is_ejs_processed": [], "gen_started": null, "gen_finished": null} diff --git a/data/chat/testRole1/11111.jsonl b/data/chat/testRole1/11111.jsonl new file mode 100644 index 0000000..41362b0 --- /dev/null +++ b/data/chat/testRole1/11111.jsonl @@ -0,0 +1,4 @@ +{"integrity": "test-uuid-2", "chat_id_hash": "hash2", "note_prompt": "探索神秘森林", "note_interval": 5, "note_position": 0, "note_depth": 2, "note_role": 1, "extensions": {}, "timedWorldInfo": {}, "variables": {"location": "forest"}, "tainted": false, "lastInContextMessageId": -1} +{"name": "System", "is_user": false, "is_system": true, "floor": 0,"send_date": "1700000002000", "mes": "你进入了一片神秘的森林,周围充满了未知的危险。", "extra": {}, "swipes": [], "swipe_id": 0, "swipe_info": [], "title": "", "force_avatar": null, "variables": [], "variables_initialized": [], "is_ejs_processed": [], "gen_started": null, "gen_finished": null} +{"name": "User", "is_user": true, "is_system": false, "floor": 1,"send_date": "1700000003000", "mes": "我该往哪个方向走?", "extra": {}, "swipes": [], "swipe_id": 0, "swipe_info": [], "title": "", "force_avatar": null, "variables": [], "variables_initialized": [], "is_ejs_processed": [], "gen_started": null, "gen_finished": null} +{"name": "Ranger", "is_user": false, "is_system": false, "floor": 2,"send_date": "1700000004000", "mes": "东边有一条小路,但西边似乎有奇怪的声音。", "extra": {}, "swipes": ["东边有一条小路,但西边似乎有奇怪的声音。", "建议往东走,那边比较安全。", "小心西边,可能有野兽。"], "swipe_id": 0, "swipe_info": [], "title": "", "force_avatar": null, "variables": [], "variables_initialized": [], "is_ejs_processed": [], "gen_started": null, "gen_finished": null} diff --git a/data/chat/testRole1/12331212.jsonl b/data/chat/testRole1/12331212.jsonl deleted file mode 100644 index 30f9980..0000000 --- a/data/chat/testRole1/12331212.jsonl +++ /dev/null @@ -1,4 +0,0 @@ -{"integrity": "test-uuid-2", "chat_id_hash": "hash2", "note_prompt": "探索神秘森林", "note_interval": 5, "note_position": 0, "note_depth": 2, "note_role": 1, "extensions": {}, "timedWorldInfo": {}, "variables": {"location": "forest"}, "tainted": false, "lastInContextMessageId": -1} -{"name": "System", "is_user": false, "is_system": true, "send_date": "1700000002000", "mes": "你进入了一片神秘的森林,周围充满了未知的危险。", "extra": {}, "swipes": [], "swipe_id": 0, "swipe_info": [], "title": "", "force_avatar": null, "variables": [], "variables_initialized": [], "is_ejs_processed": [], "gen_started": null, "gen_finished": null} -{"name": "User", "is_user": true, "is_system": false, "send_date": "1700000003000", "mes": "我该往哪个方向走?", "extra": {}, "swipes": [], "swipe_id": 0, "swipe_info": [], "title": "", "force_avatar": null, "variables": [], "variables_initialized": [], "is_ejs_processed": [], "gen_started": null, "gen_finished": null} -{"name": "Ranger", "is_user": false, "is_system": false, "send_date": "1700000004000", "mes": "东边有一条小路,但西边似乎有奇怪的声音。", "extra": {}, "swipes": ["东边有一条小路,但西边似乎有奇怪的声音。", "建议往东走,那边比较安全。", "小心西边,可能有野兽。"], "swipe_id": 0, "swipe_info": [], "title": "", "force_avatar": null, "variables": [], "variables_initialized": [], "is_ejs_processed": [], "gen_started": null, "gen_finished": null} diff --git a/data/chat/testRole1/33211.jsonl b/data/chat/testRole1/33211.jsonl deleted file mode 100644 index 5700a36..0000000 --- a/data/chat/testRole1/33211.jsonl +++ /dev/null @@ -1,3 +0,0 @@ -{"integrity": "test-uuid-1", "chat_id_hash": "hash1", "note_prompt": "", "note_interval": 0, "note_position": 0, "note_depth": 0, "note_role": 0, "extensions": {}, "timedWorldInfo": {}, "variables": {}, "tainted": false, "lastInContextMessageId": -1} -{"name": "User", "is_user": true, "is_system": false, "send_date": "1700000000000", "mes": "你好,我是新来的冒险者。", "extra": {}, "swipes": [], "swipe_id": 0, "swipe_info": [], "title": "", "force_avatar": null, "variables": [], "variables_initialized": [], "is_ejs_processed": [], "gen_started": null, "gen_finished": null} -{"name": "Guide", "is_user": false, "is_system": false, "send_date": "1700000001000", "mes": "欢迎来到我们的世界!你需要什么帮助?", "extra": {}, "swipes": [], "swipe_id": 0, "swipe_info": [], "title": "", "force_avatar": null, "variables": [], "variables_initialized": [], "is_ejs_processed": [], "gen_started": null, "gen_finished": null} diff --git a/data/chat/testRole2/222.jsonl b/data/chat/testRole2/222.jsonl index 5ea8768..7a56d08 100644 --- a/data/chat/testRole2/222.jsonl +++ b/data/chat/testRole2/222.jsonl @@ -1,4 +1,4 @@ {"integrity": "test-uuid-3", "chat_id_hash": "hash3", "note_prompt": "酒馆闲聊", "note_interval": 0, "note_position": 0, "note_depth": 0, "note_role": 0, "extensions": {}, "timedWorldInfo": {}, "variables": {}, "tainted": false, "lastInContextMessageId": -1} -{"name": "User", "is_user": true, "is_system": false, "send_date": "1700000005000", "mes": "有人知道关于龙的消息吗?", "extra": {}, "swipes": [], "swipe_id": 0, "swipe_info": [], "title": "", "force_avatar": null, "variables": [], "variables_initialized": [], "is_ejs_processed": [], "gen_started": null, "gen_finished": null} -{"name": "Bard", "is_user": false, "is_system": false, "send_date": "1700000006000", "mes": "听说北边的山脉里有一条红龙,它守护着巨大的宝藏。", "extra": {}, "swipes": [], "swipe_id": 0, "swipe_info": [], "title": "", "force_avatar": null, "variables": [], "variables_initialized": [], "is_ejs_processed": [], "gen_started": null, "gen_finished": null} -{"name": "Warrior", "is_user": false, "is_system": false, "send_date": "1700000007000", "mes": "别信那些谣言,我上周去过那里,什么都没发现。", "extra": {}, "swipes": [], "swipe_id": 0, "swipe_info": [], "title": "", "force_avatar": null, "variables": [], "variables_initialized": [], "is_ejsprocessed": [], "gen_started": null, "gen_finished": null} +{"name": "User", "is_user": true, "is_system": false, "floor": 0,"send_date": "1700000005000", "mes": "有人知道关于龙的消息吗?", "extra": {}, "swipes": [], "swipe_id": 0, "swipe_info": [], "title": "", "force_avatar": null, "variables": [], "variables_initialized": [], "is_ejs_processed": [], "gen_started": null, "gen_finished": null} +{"name": "Bard", "is_user": false, "is_system": false, "floor": 1,"send_date": "1700000006000", "mes": "听说北边的山脉里有一条红龙,它守护着巨大的宝藏。", "extra": {}, "swipes": [], "swipe_id": 0, "swipe_info": [], "title": "", "force_avatar": null, "variables": [], "variables_initialized": [], "is_ejs_processed": [], "gen_started": null, "gen_finished": null} +{"name": "Warrior", "is_user": false, "is_system": false, "floor": 2,"send_date": "1700000007000", "mes": "别信那些谣言,我上周去过那里,什么都没发现。", "extra": {}, "swipes": [], "swipe_id": 0, "swipe_info": [], "title": "", "force_avatar": null, "variables": [], "variables_initialized": [], "is_ejsprocessed": [], "gen_started": null, "gen_finished": null} diff --git a/data/chat/testRole3/333.jsonl b/data/chat/testRole3/333.jsonl new file mode 100644 index 0000000..a1c152d --- /dev/null +++ b/data/chat/testRole3/333.jsonl @@ -0,0 +1,6 @@ +{"integrity": "test-uuid-3", "chat_id_hash": "hash3", "note_prompt": "酒馆闲聊", "note_interval": 0, "note_position": 0, "note_depth": 0, "note_role": 0, "extensions": {}, "timedWorldInfo": {}, "variables": {}, "tainted": false, "lastInContextMessageId": -1} +{"name": "User", "is_user": true, "is_system": false, "floor": 0,"send_date": "1700000005000", "mes": "有人知道关于龙的消息吗?", "extra": {}, "swipes": [], "swipe_id": 0, "swipe_info": [], "title": "", "force_avatar": null, "variables": [], "variables_initialized": [], "is_ejs_processed": [], "gen_started": null, "gen_finished": null} +{"name": "Bard", "is_user": false, "is_system": false, "floor": 1,"send_date": "1700000006000", "mes": "听说北边的山脉里有一条红龙,它守护着巨大的宝藏。", "extra": {}, "swipes": [], "swipe_id": 0, "swipe_info": [], "title": "", "force_avatar": null, "variables": [], "variables_initialized": [], "is_ejs_processed": [], "gen_started": null, "gen_finished": null} +{"name": "User", "is_user": true, "is_system": false, "floor": 3,"send_date": "1700000005000", "mes": "别信那些谣言,我上周去过那里,什么都没发现。", "extra": {}, "swipes": [], "swipe_id": 0, "swipe_info": [], "title": "", "force_avatar": null, "variables": [], "variables_initialized": [], "is_ejs_processed": [], "gen_started": null, "gen_finished": null} +{"name": "Warrior", "is_user": false, "is_system": false, "floor": 4,"send_date": "1700000007000", "mes": "别信那些谣言,我上周去过那里,什么都没发现。", "extra": {}, "swipes": [], "swipe_id": 0, "swipe_info": [], "title": "", "force_avatar": null, "variables": [], "variables_initialized": [], "is_ejsprocessed": [], "gen_started": null, "gen_finished": null} +{"name": "User", "is_user": true, "is_system": false, "floor": 5,"send_date": "1700000005000", "mes": "你要是发现了,那我还去干什么?", "extra": {}, "swipes": [], "swipe_id": 0, "swipe_info": [], "title": "", "force_avatar": null, "variables": [], "variables_initialized": [], "is_ejs_processed": [], "gen_started": null, "gen_finished": null} diff --git a/frontend-react/src/Store/Slices/ChatBoxSlice.jsx b/frontend-react/src/Store/Slices/ChatBoxSlice.jsx index 1c20fb9..578acab 100644 --- a/frontend-react/src/Store/Slices/ChatBoxSlice.jsx +++ b/frontend-react/src/Store/Slices/ChatBoxSlice.jsx @@ -39,6 +39,13 @@ const useChatBoxStore = create( // 设置当前聊天 setCurrentChat: (chat) => set({ currentChat: chat }), + // 同时设置角色和聊天 + setChatBoxRoleAndChat: (role, chat) => set({ + currentRole: role, + currentChat: chat + }), + + // 加载聊天历史 fetchChatHistory: async (roleName, chatName) => { set({ isLoading: true, error: null }); @@ -48,10 +55,11 @@ const useChatBoxStore = create( throw new Error('Failed to fetch chat history'); } const data = await response.json(); + // 修改数据处理逻辑,适配API返回的数据结构 set({ - messages: data.messages || [], - userName: data.userName || 'User', - characterName: data.characterName || 'Assistant', + messages: data || [], // 直接使用返回的数组 + userName: 'User', // 固定用户名 + characterName: roleName || 'Assistant', // 使用角色名作为角色名称 isLoading: false }); } catch (error) { @@ -62,6 +70,7 @@ const useChatBoxStore = create( } }, + // 清空聊天历史 clearChatHistory: () => set({ messages: [], @@ -82,14 +91,21 @@ const useChatBoxStore = create( // 监听角色和聊天变化,自动加载聊天历史 useChatBoxStore.subscribe( (state) => ({ role: state.currentRole, chat: state.currentChat }), - ({ role, chat }) => { - if (role && chat) { - useChatBoxStore.getState().fetchChatHistory(role, chat); - } else { - useChatBoxStore.getState().clearChatHistory(); + ({ role, chat }, prev) => { + // 只有当角色或聊天发生变化时才处理 + if (role !== prev.role || chat !== prev.chat) { + // 确保角色和聊天都存在且不为null + if (role && chat) { + useChatBoxStore.getState().fetchChatHistory(role, chat); + } else { + useChatBoxStore.getState().clearChatHistory(); + } } }, { equalityFn: (a, b) => a.role === b.role && a.chat === b.chat } ); + + + export default useChatBoxStore; diff --git a/frontend-react/src/Store/Slices/RoleSelectorSlice.jsx b/frontend-react/src/Store/Slices/RoleSelectorSlice.jsx index 4f56608..dedc148 100644 --- a/frontend-react/src/Store/Slices/RoleSelectorSlice.jsx +++ b/frontend-react/src/Store/Slices/RoleSelectorSlice.jsx @@ -63,6 +63,9 @@ const useRoleSelectorStore = create((set, get) => ({ setDeleteType: (type) => set({ deleteType: type }), + // 同时更新角色和聊天 + setSelectedRoleAndChat: (role, chat) => set({ selectedRole: role, selectedChat: chat }), + handleRenameRole: (oldName, newName) => { const { roleData, selectedRole } = get(); if (newName && newName !== oldName) { @@ -199,4 +202,4 @@ const useRoleSelectorStore = create((set, get) => ({ }) })); -export default useRoleSelectorStore; +export default useRoleSelectorStore; \ No newline at end of file diff --git a/frontend-react/src/components/ChatBox/ChatBox.css b/frontend-react/src/components/ChatBox/ChatBox.css index c901b29..015a6ca 100644 --- a/frontend-react/src/components/ChatBox/ChatBox.css +++ b/frontend-react/src/components/ChatBox/ChatBox.css @@ -14,7 +14,6 @@ .chat-messages { flex: 1; min-height: 0; - max-height: calc(95vh - 62px); /* 减去输入区域的高度 */ overflow-y: auto; padding: 20px; padding-top: 60px; @@ -427,3 +426,55 @@ margin: 10px 0; } +/* 确保消息名称和工具栏在移动设备上也能正常显示 */ +@media (max-width: 768px) { + .message-container { + max-width: 85%; + } + + .message-header { + flex-wrap: wrap; + } + + .message-toolbar { + opacity: 1; /* 在移动设备上始终显示工具栏 */ + } +} + +/* 添加滚动条样式,使其更美观 */ +.chat-messages::-webkit-scrollbar { + width: 6px; +} + +.chat-messages::-webkit-scrollbar-track { + background: #f1f1f1; +} + +.chat-messages::-webkit-scrollbar-thumb { + background: #c1c1c1; + border-radius: 3px; +} + +.chat-messages::-webkit-scrollbar-thumb:hover { + background: #a8a8a8; +} + +/* 确保消息内容中的链接样式 */ +.bubble a { + color: inherit; + text-decoration: underline; +} + +/* 代码块样式 */ +.bubble pre { + background-color: #f6f8fa; + padding: 10px; + border-radius: 6px; + overflow-x: auto; + margin: 8px 0; +} + +.bubble code { + font-family: 'Courier New', Courier, monospace; + font-size: 0.9em; +} diff --git a/frontend-react/src/components/ChatBox/ChatBox.jsx b/frontend-react/src/components/ChatBox/ChatBox.jsx index 8fe227b..a75240a 100644 --- a/frontend-react/src/components/ChatBox/ChatBox.jsx +++ b/frontend-react/src/components/ChatBox/ChatBox.jsx @@ -1,156 +1,133 @@ -import React, { useState, useRef } from 'react'; +import React, { useState, useEffect, useRef } from 'react'; import useChatBoxStore from '../../Store/Slices/ChatBoxSlice'; import './ChatBox.css'; const ChatBox = () => { - const [isHtmlRender, setIsHtmlRender] = useState(false); - const [isImageGen, setIsImageGen] = useState(false); - const [isDynamicTable, setIsDynamicTable] = useState(false); const [editingId, setEditingId] = useState(null); const [editContent, setEditContent] = useState(''); + const messagesEndRef = useRef(null); - const textareaRef = useRef(null); + // 从 ChatBoxStore 获取状态和方法 + const { + messages, + isLoading, + error, + userName, + characterName, + updateMessage + } = useChatBoxStore(); - // 从 store 获取状态 - const { messages, userName, characterName, isLoading, error } = useChatBoxStore(); - - // 自动调整 Textarea 高度 - const adjustHeight = () => { - const textarea = textareaRef.current; - const wrapper = textarea?.closest('.chat-input-wrapper'); - - if (textarea && wrapper) { - textarea.style.height = '42px'; - wrapper.style.height = 'auto'; - const newHeight = textarea.scrollHeight; - - if (newHeight > 42) { - textarea.style.height = newHeight + 'px'; - } else { - textarea.style.height = '42px'; - } - } + // 自动滚动到底部 + const scrollToBottom = () => { + messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); }; - const handleInput = () => { - adjustHeight(); + useEffect(() => { + scrollToBottom(); + }, [messages]); + + // 处理编辑消息 + const handleEdit = (message) => { + setEditingId(message.floor); + setEditContent(message.mes); }; - // 开始编辑消息 - const startEdit = (id, content) => { - setEditingId(id); - setEditContent(content); - }; - - // 保存编辑的消息 - const saveEdit = (id) => { - // 这里可以添加向后端发送请求的代码 - console.log(`保存消息 ${id} 的编辑内容: ${editContent}`); - - // 更新前端显示 - const messageIndex = messages.findIndex(msg => msg.id === id); - if (messageIndex !== -1) { - messages[messageIndex].content = editContent; - } - - // 退出编辑模式 + // 保存编辑 + const handleSaveEdit = (messageId) => { + // 调用 store 中的 updateMessage 方法 + updateMessage(messageId, editContent); setEditingId(null); setEditContent(''); }; // 取消编辑 - const cancelEdit = () => { + const handleCancelEdit = () => { setEditingId(null); setEditContent(''); }; - return ( -
- {/* 上方:消息列表区域 */} -
- {/* 加载状态和错误信息 */} - {isLoading &&
加载中...
} - {error &&
{error}
} + // 渲染单条消息 + const renderMessage = (message) => { + const isUser = message.is_user; + const isEditing = editingId === message.floor; - {/* 消息列表 */} - {messages.map((msg) => ( -
-
- {/* 消息名称和工具栏在同一行 */} -
-
{msg.role === 'user' ? userName : characterName}
+ // 根据消息类型设置显示名称 + const displayName = isUser ? userName : characterName; - {/* 消息工具栏 */} -
- ID: {msg.id} -
- - -
-
-
- - {/* 消息内容 */} -
-
- {editingId === msg.id ? ( -
-