add messages chat
This commit is contained in:
		
							parent
							
								
									d808bbfe26
								
							
						
					
					
						commit
						6f3b1c2d43
					
				
							
								
								
									
										1
									
								
								chat_history.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								chat_history.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | |||||||
|  | [] | ||||||
							
								
								
									
										100
									
								
								server.js
									
									
									
									
									
								
							
							
						
						
									
										100
									
								
								server.js
									
									
									
									
									
								
							| @ -4,15 +4,24 @@ const socketIo = require('socket.io'); | |||||||
| const cors = require('cors'); | const cors = require('cors'); | ||||||
| const path = require('path'); | const path = require('path'); | ||||||
| const fs = require('fs'); | const fs = require('fs'); | ||||||
|  | const { MessageHistory } = require('./src/message_history.js'); | ||||||
| 
 | 
 | ||||||
| const app = express(); | const app = express(); | ||||||
| const server = http.createServer(app); | const server = http.createServer(app); | ||||||
| const io = socketIo(server, { | const io = socketIo(server); | ||||||
|   cors: { | 
 | ||||||
|     origin: "*", | // 创建消息历史管理器
 | ||||||
|     methods: ["GET", "POST"] | const messageHistory = new MessageHistory(); | ||||||
|   } | 
 | ||||||
| }); | // 服务器启动时初始化历史消息
 | ||||||
|  | async function initializeServer() { | ||||||
|  |     try { | ||||||
|  |         await messageHistory.initialize(); | ||||||
|  |         console.log('消息历史初始化完成'); | ||||||
|  |     } catch (error) { | ||||||
|  |         console.error('初始化消息历史失败:', error); | ||||||
|  |     } | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| // 中间件
 | // 中间件
 | ||||||
| app.use(cors()); | app.use(cors()); | ||||||
| @ -20,22 +29,72 @@ app.use(express.json()); | |||||||
| app.use(express.static('src')); | app.use(express.static('src')); | ||||||
| app.use('/videos', express.static('videos')); | app.use('/videos', express.static('videos')); | ||||||
| 
 | 
 | ||||||
|  | // API路由 - 获取历史消息(用于LLM上下文)
 | ||||||
|  | app.get('/api/messages/for-llm', (req, res) => { | ||||||
|  |     try { | ||||||
|  |         const { includeSystem = true, recentCount = 5 } = req.query; | ||||||
|  |         const messages = messageHistory.getMessagesForLLM( | ||||||
|  |             includeSystem === 'true',  | ||||||
|  |             parseInt(recentCount) | ||||||
|  |         ); | ||||||
|  |         res.json({ messages }); | ||||||
|  |     } catch (error) { | ||||||
|  |         console.error('获取LLM消息失败:', error); | ||||||
|  |         res.status(500).json({ error: '获取消息失败' }); | ||||||
|  |     } | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | // API路由 - 保存新消息
 | ||||||
|  | app.post('/api/messages/save', async (req, res) => { | ||||||
|  |     try { | ||||||
|  |         const { userInput, assistantResponse } = req.body; | ||||||
|  |         if (!userInput || !assistantResponse) { | ||||||
|  |             return res.status(400).json({ error: '缺少必要参数' }); | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         await messageHistory.addMessage(userInput, assistantResponse); | ||||||
|  |         res.json({ success: true, message: '消息已保存' }); | ||||||
|  |     } catch (error) { | ||||||
|  |         console.error('保存消息失败:', error); | ||||||
|  |         res.status(500).json({ error: '保存消息失败' }); | ||||||
|  |     } | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | // API路由 - 获取完整历史(可选,用于调试或展示)
 | ||||||
|  | app.get('/api/messages/history', (req, res) => { | ||||||
|  |     try { | ||||||
|  |         const history = messageHistory.getFullHistory(); | ||||||
|  |         res.json({ history }); | ||||||
|  |     } catch (error) { | ||||||
|  |         console.error('获取历史消息失败:', error); | ||||||
|  |         res.status(500).json({ error: '获取历史消息失败' }); | ||||||
|  |     } | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | // API路由 - 清空历史
 | ||||||
|  | app.delete('/api/messages/clear', async (req, res) => { | ||||||
|  |     try { | ||||||
|  |         await messageHistory.clearHistory(); | ||||||
|  |         res.json({ success: true, message: '历史消息已清空' }); | ||||||
|  |     } catch (error) { | ||||||
|  |         console.error('清空历史消息失败:', error); | ||||||
|  |         res.status(500).json({ error: '清空历史消息失败' }); | ||||||
|  |     } | ||||||
|  | }); | ||||||
|  | 
 | ||||||
| // 存储连接的客户端和他们的视频流状态
 | // 存储连接的客户端和他们的视频流状态
 | ||||||
| const connectedClients = new Map(); | const connectedClients = new Map(); | ||||||
| 
 | 
 | ||||||
| // 视频映射配置
 | // 视频映射配置
 | ||||||
| const videoMapping = { | const videoMapping = { | ||||||
|   '你好': 'asd.mp4', |   'say-6s-m-e': '1-m.mp4', | ||||||
|   'hello': 'asd.mp4', |   'default': '0.mp4', | ||||||
|   '再见': 'zxc.mp4', |   'say-5s-amplitude': '2.mp4', | ||||||
|   'goodbye': 'zxc.mp4', |   'say-5s-m-e': 'zxc.mp4' | ||||||
|   '谢谢': 'jkl.mp4', |  | ||||||
|   'thank you': 'jkl.mp4', |  | ||||||
|   '默认': 'asd.mp4' |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| // 默认视频流配置
 | // 默认视频流配置
 | ||||||
| const DEFAULT_VIDEO = 'asd.mp4'; | const DEFAULT_VIDEO = '0.mp4'; | ||||||
| const INTERACTION_TIMEOUT = 10000; // 10秒后回到默认视频
 | const INTERACTION_TIMEOUT = 10000; // 10秒后回到默认视频
 | ||||||
| 
 | 
 | ||||||
| // 获取视频列表
 | // 获取视频列表
 | ||||||
| @ -217,8 +276,13 @@ io.on('connection', (socket) => { | |||||||
|   }); |   }); | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
|  | // 启动服务器
 | ||||||
| const PORT = process.env.PORT || 3000; | const PORT = process.env.PORT || 3000; | ||||||
| server.listen(PORT, () => { | server.listen(PORT, async () => { | ||||||
|   console.log(`服务器运行在端口 ${PORT}`); |     console.log(`服务器运行在端口 ${PORT}`); | ||||||
|   console.log(`访问 http://localhost:${PORT} 开始使用`); |     await initializeServer(); | ||||||
| });  | }); | ||||||
|  | 
 | ||||||
|  | // 导出消息历史管理器供其他模块使用
 | ||||||
|  | module.exports = { messageHistory }; | ||||||
|  | console.log(`访问 http://localhost:${PORT} 开始使用`); | ||||||
| @ -10,82 +10,183 @@ let isPlaying = false; | |||||||
| let audioQueue = []; | let audioQueue = []; | ||||||
| let isProcessingQueue = false; | let isProcessingQueue = false; | ||||||
| 
 | 
 | ||||||
| async function chatWithAudioStream(userInput) { | // 历史消息缓存
 | ||||||
|   // 验证配置
 | let historyMessage = []; | ||||||
|   if (!validateConfig()) { | let isInitialized = false; | ||||||
|     throw new Error('配置不完整,请检查config.js文件中的API密钥设置'); | 
 | ||||||
|   } | // 初始化历史消息
 | ||||||
|    | async function initializeHistoryMessage(recentCount = 5) { | ||||||
|   console.log('用户输入:', userInput); |     if (isInitialized) return historyMessage; | ||||||
|    |  | ||||||
|   // 获取配置
 |  | ||||||
|   const llmConfig = getLLMConfig(); |  | ||||||
|   const minimaxiConfig = getMinimaxiConfig(); |  | ||||||
|   const audioConfig = getAudioConfig(); |  | ||||||
|    |  | ||||||
|   // 清空音频队列
 |  | ||||||
|   audioQueue = []; |  | ||||||
|    |  | ||||||
|   // 定义段落处理函数
 |  | ||||||
|   const handleSegment = async (segment) => { |  | ||||||
|     console.log('\n=== 处理文本段落 ==='); |  | ||||||
|     console.log('段落内容:', segment); |  | ||||||
|      |      | ||||||
|     try { |     try { | ||||||
|       // 为每个段落生成音频
 |         const response = await fetch(`/api/messages/for-llm?includeSystem=true&recentCount=${recentCount}`); | ||||||
|       const audioResult = await requestMinimaxi({ |         if (!response.ok) { | ||||||
|         apiKey: minimaxiConfig.apiKey, |             throw new Error('获取历史消息失败'); | ||||||
|         groupId: minimaxiConfig.groupId, |         } | ||||||
|         body: { |         const data = await response.json(); | ||||||
|           model: audioConfig.model, |         historyMessage = data.messages || []; | ||||||
|           text: segment, |         isInitialized = true; | ||||||
|           stream: audioConfig.stream, |         console.log("历史消息初始化完成:", historyMessage.length, "条消息"); | ||||||
|           language_boost: audioConfig.language_boost, |         return historyMessage; | ||||||
|           output_format: audioConfig.output_format, |  | ||||||
|           voice_setting: audioConfig.voiceSetting, |  | ||||||
|           audio_setting: audioConfig.audioSetting, |  | ||||||
|         }, |  | ||||||
|         stream: true, |  | ||||||
|       }); |  | ||||||
|        |  | ||||||
|       // 将音频添加到播放队列
 |  | ||||||
|       if (audioResult && audioResult.data && audioResult.data.audio) { |  | ||||||
|         audioQueue.push({ |  | ||||||
|           text: segment, |  | ||||||
|           audioHex: audioResult.data.audio |  | ||||||
|         }); |  | ||||||
|         console.log('音频已添加到队列,队列长度:', audioQueue.length); |  | ||||||
|          |  | ||||||
|         // 开始处理队列
 |  | ||||||
|         processAudioQueue(); |  | ||||||
|       } |  | ||||||
|     } catch (error) { |     } catch (error) { | ||||||
|       console.error('生成音频失败:', error); |         console.error('获取历史消息失败,使用默认格式:', error); | ||||||
|  |         historyMessage = [ | ||||||
|  |             { role: 'system', content: 'You are a helpful assistant.' } | ||||||
|  |         ]; | ||||||
|  |         isInitialized = true; | ||||||
|  |         return historyMessage; | ||||||
|     } |     } | ||||||
|   }; |  | ||||||
|    |  | ||||||
|   // 1. 请求大模型回答,并实时处理段落
 |  | ||||||
|   console.log('\n=== 请求大模型回答 ==='); |  | ||||||
|   const llmResponse = await requestLLMStream({ |  | ||||||
|     apiKey: llmConfig.apiKey, |  | ||||||
|     model: llmConfig.model, |  | ||||||
|     messages: [ |  | ||||||
|       { role: 'system', content: 'You are a helpful assistant.' }, |  | ||||||
|       { role: 'user', content: userInput }, |  | ||||||
|     ], |  | ||||||
|     onSegment: handleSegment // 传入段落处理回调
 |  | ||||||
|   }); |  | ||||||
|    |  | ||||||
|   console.log('\n=== 大模型完整回答 ==='); |  | ||||||
|   console.log("llmResponse: ", llmResponse); |  | ||||||
|    |  | ||||||
|   return { |  | ||||||
|     userInput, |  | ||||||
|     llmResponse, |  | ||||||
|     audioQueue: audioQueue.map(item => ({ text: item.text, hasAudio: !!item.audioHex })) |  | ||||||
|   }; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // 获取当前历史消息(同步方法)
 | ||||||
|  | function getCurrentHistoryMessage() { | ||||||
|  |     if (!isInitialized) { | ||||||
|  |         console.warn('历史消息未初始化,返回默认消息'); | ||||||
|  |         return [{ role: 'system', content: 'You are a helpful assistant.' }]; | ||||||
|  |     } | ||||||
|  |     return [...historyMessage]; // 返回副本,避免外部修改
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // 更新历史消息
 | ||||||
|  | function updateHistoryMessage(userInput, assistantResponse) { | ||||||
|  |     if (!isInitialized) { | ||||||
|  |         console.warn('历史消息未初始化,无法更新'); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     historyMessage.push( | ||||||
|  |         { role: 'user', content: userInput }, | ||||||
|  |         { role: 'assistant', content: assistantResponse } | ||||||
|  |     ); | ||||||
|  |      | ||||||
|  |     // 可选:限制历史消息数量,保持最近的对话
 | ||||||
|  |     const maxMessages = 20; // 保留最近10轮对话(20条消息)
 | ||||||
|  |     if (historyMessage.length > maxMessages) { | ||||||
|  |         // 保留系统消息和最近的对话
 | ||||||
|  |         const systemMessages = historyMessage.filter(msg => msg.role === 'system'); | ||||||
|  |         const recentMessages = historyMessage.slice(-maxMessages + systemMessages.length); | ||||||
|  |         historyMessage = [...systemMessages, ...recentMessages.filter(msg => msg.role !== 'system')]; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // 保存消息到服务端
 | ||||||
|  | async function saveMessage(userInput, assistantResponse) { | ||||||
|  |     try { | ||||||
|  |         const response = await fetch('/api/messages/save', { | ||||||
|  |             method: 'POST', | ||||||
|  |             headers: { | ||||||
|  |                 'Content-Type': 'application/json' | ||||||
|  |             }, | ||||||
|  |             body: JSON.stringify({ | ||||||
|  |                 userInput, | ||||||
|  |                 assistantResponse | ||||||
|  |             }) | ||||||
|  |         }); | ||||||
|  |          | ||||||
|  |         if (!response.ok) { | ||||||
|  |             throw new Error('保存消息失败'); | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         console.log('消息已保存到服务端'); | ||||||
|  |     } catch (error) { | ||||||
|  |         console.error('保存消息失败:', error); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | async function chatWithAudioStream(userInput) { | ||||||
|  |     // 确保历史消息已初始化
 | ||||||
|  |     if (!isInitialized) { | ||||||
|  |         await initializeHistoryMessage(); | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     // 验证配置
 | ||||||
|  |     if (!validateConfig()) { | ||||||
|  |         throw new Error('配置不完整,请检查config.js文件中的API密钥设置'); | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     console.log('用户输入:', userInput); | ||||||
|  |      | ||||||
|  |     // 获取配置
 | ||||||
|  |     const llmConfig = getLLMConfig(); | ||||||
|  |     const minimaxiConfig = getMinimaxiConfig(); | ||||||
|  |     const audioConfig = getAudioConfig(); | ||||||
|  |      | ||||||
|  |     // 清空音频队列
 | ||||||
|  |     audioQueue = []; | ||||||
|  |      | ||||||
|  |     // 定义段落处理函数
 | ||||||
|  |     const handleSegment = async (segment) => { | ||||||
|  |         console.log('\n=== 处理文本段落 ==='); | ||||||
|  |         console.log('段落内容:', segment); | ||||||
|  |          | ||||||
|  |         try { | ||||||
|  |             // 为每个段落生成音频
 | ||||||
|  |             const audioResult = await requestMinimaxi({ | ||||||
|  |                 apiKey: minimaxiConfig.apiKey, | ||||||
|  |                 groupId: minimaxiConfig.groupId, | ||||||
|  |                 body: { | ||||||
|  |                     model: audioConfig.model, | ||||||
|  |                     text: segment, | ||||||
|  |                     stream: audioConfig.stream, | ||||||
|  |                     language_boost: audioConfig.language_boost, | ||||||
|  |                     output_format: audioConfig.output_format, | ||||||
|  |                     voice_setting: audioConfig.voiceSetting, | ||||||
|  |                     audio_setting: audioConfig.audioSetting, | ||||||
|  |                 }, | ||||||
|  |                 stream: true, | ||||||
|  |             }); | ||||||
|  |              | ||||||
|  |             // 将音频添加到播放队列
 | ||||||
|  |             if (audioResult && audioResult.data && audioResult.data.audio) { | ||||||
|  |                 audioQueue.push({ | ||||||
|  |                     text: segment, | ||||||
|  |                     audioHex: audioResult.data.audio | ||||||
|  |                 }); | ||||||
|  |                 console.log('音频已添加到队列,队列长度:', audioQueue.length); | ||||||
|  |                  | ||||||
|  |                 // 开始处理队列
 | ||||||
|  |                 processAudioQueue(); | ||||||
|  |             } | ||||||
|  |         } catch (error) { | ||||||
|  |             console.error('生成音频失败:', error); | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  |      | ||||||
|  |     // 1. 获取包含历史的消息列表
 | ||||||
|  |     console.log('\n=== 获取历史消息 ==='); | ||||||
|  |     const messages = getCurrentHistoryMessage(); | ||||||
|  |     messages.push({role: 'user', content: userInput}); | ||||||
|  |     console.log('发送的消息数量:', messages); | ||||||
|  |      | ||||||
|  |     // 2. 请求大模型回答
 | ||||||
|  |     console.log('\n=== 请求大模型回答 ==='); | ||||||
|  |     const llmResponse = await requestLLMStream({ | ||||||
|  |         apiKey: llmConfig.apiKey, | ||||||
|  |         model: llmConfig.model, | ||||||
|  |         messages: messages, | ||||||
|  |         onSegment: handleSegment | ||||||
|  |     }); | ||||||
|  |      | ||||||
|  |     console.log('\n=== 大模型完整回答 ==='); | ||||||
|  |     console.log("llmResponse: ", llmResponse); | ||||||
|  |      | ||||||
|  |     // 3. 保存对话到服务端
 | ||||||
|  |     await saveMessage(userInput, llmResponse); | ||||||
|  |      | ||||||
|  |     // 4. 更新本地历史消息
 | ||||||
|  |     updateHistoryMessage(userInput, llmResponse); | ||||||
|  |     console.log('历史消息数量:', historyMessage.length); | ||||||
|  |      | ||||||
|  |     return { | ||||||
|  |         userInput, | ||||||
|  |         llmResponse, | ||||||
|  |         audioQueue: audioQueue.map(item => ({ text: item.text, hasAudio: !!item.audioHex })) | ||||||
|  |     }; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // 导出初始化函数,供外部调用
 | ||||||
|  | export { chatWithAudioStream, initializeHistoryMessage, getCurrentHistoryMessage }; | ||||||
|  | 
 | ||||||
| // 处理音频播放队列
 | // 处理音频播放队列
 | ||||||
| async function processAudioQueue() { | async function processAudioQueue() { | ||||||
|   if (isProcessingQueue) return; |   if (isProcessingQueue) return; | ||||||
| @ -209,4 +310,4 @@ async function playAudioStreamNode(audioHex) { | |||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| export { chatWithAudioStream, playAudioStream, playAudioStreamNode}; | // export { chatWithAudioStream, playAudioStream, playAudioStreamNode, initializeHistoryMessage, getCurrentHistoryMessage };
 | ||||||
| @ -77,6 +77,6 @@ | |||||||
|     <video id="remoteVideo" autoplay playsinline style="display: none;"></video> |     <video id="remoteVideo" autoplay playsinline style="display: none;"></video> | ||||||
| 
 | 
 | ||||||
|     <script src="/socket.io/socket.io.js"></script> |     <script src="/socket.io/socket.io.js"></script> | ||||||
|     <script type="module" src="index.js"></script> |     <script type="module" src="./index.js"></script> | ||||||
| </body> | </body> | ||||||
| </html>  | </html>  | ||||||
							
								
								
									
										34
									
								
								src/index.js
									
									
									
									
									
								
							
							
						
						
									
										34
									
								
								src/index.js
									
									
									
									
									
								
							| @ -1,9 +1,17 @@ | |||||||
|  | console.log('视频文件:'); | ||||||
| // WebRTC 音视频通话应用
 | // WebRTC 音视频通话应用
 | ||||||
| import { chatWithAudioStream } from './chat_with_audio.js'; | // import { chatWithAudioStream } from './chat_with_audio.js';
 | ||||||
|  | import { chatWithAudioStream, initializeHistoryMessage } from './chat_with_audio.js'; | ||||||
| import { AudioProcessor } from './audio_processor.js'; | import { AudioProcessor } from './audio_processor.js'; | ||||||
| 
 | 
 | ||||||
|  | // 在应用初始化时调用
 | ||||||
| class WebRTCChat { | class WebRTCChat { | ||||||
|     constructor() { |     constructor() { | ||||||
|  |         console.log('WebRTCChat 构造函数开始执行'); | ||||||
|  |          | ||||||
|  |         // 初始化历史消息(异步)
 | ||||||
|  |         this.initializeHistory(); | ||||||
|  |          | ||||||
|         this.socket = null; |         this.socket = null; | ||||||
|         this.localStream = null; |         this.localStream = null; | ||||||
|         this.peerConnection = null; |         this.peerConnection = null; | ||||||
| @ -11,11 +19,13 @@ class WebRTCChat { | |||||||
|         this.mediaRecorder = null; |         this.mediaRecorder = null; | ||||||
|         this.audioChunks = []; |         this.audioChunks = []; | ||||||
|         this.videoMapping = {}; |         this.videoMapping = {}; | ||||||
|         this.defaultVideo = 'asd.mp4'; |         this.defaultVideo = '0.mp4'; | ||||||
|         this.currentVideo = null; |         this.currentVideo = null; | ||||||
|         this.videoStreams = new Map(); // 存储不同视频的MediaStream
 |         this.videoStreams = new Map(); // 存储不同视频的MediaStream
 | ||||||
|         this.currentVideoStream = null; |         this.currentVideoStream = null; | ||||||
|          |          | ||||||
|  |         // 初始化音频处理器
 | ||||||
|  |         console.log('开始初始化音频处理器'); | ||||||
|         // 初始化音频处理器
 |         // 初始化音频处理器
 | ||||||
|         this.audioProcessor = new AudioProcessor({ |         this.audioProcessor = new AudioProcessor({ | ||||||
|             onSpeechStart: () => { |             onSpeechStart: () => { | ||||||
| @ -39,6 +49,8 @@ class WebRTCChat { | |||||||
|                 this.voiceStatus.textContent = message; |                 this.voiceStatus.textContent = message; | ||||||
|             } |             } | ||||||
|         }); |         }); | ||||||
|  | 
 | ||||||
|  |         console.log('WebRTC 聊天应用初始化完成'); | ||||||
|          |          | ||||||
|         this.initializeElements(); |         this.initializeElements(); | ||||||
|         this.initializeSocket(); |         this.initializeSocket(); | ||||||
| @ -117,6 +129,15 @@ class WebRTCChat { | |||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     async initializeHistory() { | ||||||
|  |         try { | ||||||
|  |             await initializeHistoryMessage(); | ||||||
|  |             console.log('历史消息初始化完成'); | ||||||
|  |         } catch (error) { | ||||||
|  |             console.error('历史消息初始化失败:', error); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     async loadVideoMapping() { |     async loadVideoMapping() { | ||||||
|         try { |         try { | ||||||
|             const response = await fetch('/api/video-mapping'); |             const response = await fetch('/api/video-mapping'); | ||||||
| @ -782,5 +803,10 @@ class WebRTCChat { | |||||||
| 
 | 
 | ||||||
| // 页面加载完成后初始化应用
 | // 页面加载完成后初始化应用
 | ||||||
| document.addEventListener('DOMContentLoaded', () => { | document.addEventListener('DOMContentLoaded', () => { | ||||||
|     new WebRTCChat(); |     console.log('DOMContentLoaded 事件触发'); | ||||||
| }); |     try { | ||||||
|  |         new WebRTCChat(); | ||||||
|  |     } catch (error) { | ||||||
|  |         console.error('WebRTCChat 初始化失败:', error); | ||||||
|  |     } | ||||||
|  | }); | ||||||
|  | |||||||
							
								
								
									
										134
									
								
								src/message_history.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										134
									
								
								src/message_history.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,134 @@ | |||||||
|  | const fs = require('fs').promises; | ||||||
|  | const path = require('path'); | ||||||
|  | 
 | ||||||
|  | class MessageHistory { | ||||||
|  |     constructor() { | ||||||
|  |         this.historyFile = path.join(__dirname, '..', 'chat_history.json'); | ||||||
|  |         this.messages = []; // 内存中的消息历史
 | ||||||
|  |         this.maxMessages = 100; // 最大保存消息数量
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // 服务器启动时初始化,从JSON文件预加载历史
 | ||||||
|  |     async initialize() { | ||||||
|  |         try { | ||||||
|  |             const data = await fs.readFile(this.historyFile, 'utf8'); | ||||||
|  |             this.messages = JSON.parse(data); | ||||||
|  |             console.log(`已加载 ${this.messages.length} 条历史消息`); | ||||||
|  |         } catch (error) { | ||||||
|  |             if (error.code === 'ENOENT') { | ||||||
|  |                 console.log('历史文件不存在,创建新的消息历史'); | ||||||
|  |                 this.messages = []; | ||||||
|  |                 await this.saveToFile(); | ||||||
|  |             } else { | ||||||
|  |                 console.error('加载历史消息失败:', error); | ||||||
|  |                 this.messages = []; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // 添加新消息(用户输入和助手回复)
 | ||||||
|  |     async addMessage(userInput, assistantResponse) { | ||||||
|  |         // 添加用户消息
 | ||||||
|  |         this.messages.push({ | ||||||
|  |             role: 'user', | ||||||
|  |             content: userInput, | ||||||
|  |             timestamp: new Date().toISOString() | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         // 添加助手回复
 | ||||||
|  |         this.messages.push({ | ||||||
|  |             role: 'assistant',  | ||||||
|  |             content: assistantResponse, | ||||||
|  |             timestamp: new Date().toISOString() | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         // 限制消息数量(保留最近的对话)
 | ||||||
|  |         if (this.messages.length > this.maxMessages * 2) { | ||||||
|  |             this.messages = this.messages.slice(-this.maxMessages * 2); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // 同时保存到文件和内存
 | ||||||
|  |         await this.saveToFile(); | ||||||
|  |          | ||||||
|  |         console.log(`已保存对话,当前历史消息数: ${this.messages.length}`); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // 获取用于大模型的消息格式
 | ||||||
|  |     getMessagesForLLM(includeSystem = true, recentCount = 10) { | ||||||
|  |         const messages = []; | ||||||
|  |          | ||||||
|  |         // 添加系统消息
 | ||||||
|  |         if (includeSystem) { | ||||||
|  |             messages.push({ | ||||||
|  |                 role: 'system', | ||||||
|  |                 content: 'You are a helpful assistant.' | ||||||
|  |             }); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // 获取最近的对话历史
 | ||||||
|  |         const recentMessages = this.messages.slice(-recentCount * 2); // 用户+助手成对出现
 | ||||||
|  |         messages.push(...recentMessages.map(msg => ({ | ||||||
|  |             role: msg.role, | ||||||
|  |             content: msg.content | ||||||
|  |         }))); | ||||||
|  | 
 | ||||||
|  |         return messages; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // 获取完整历史(包含时间戳)
 | ||||||
|  |     getFullHistory() { | ||||||
|  |         return [...this.messages]; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // 清空历史
 | ||||||
|  |     async clearHistory() { | ||||||
|  |         this.messages = []; | ||||||
|  |         await this.saveToFile(); | ||||||
|  |         console.log('历史消息已清空'); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // 保存到JSON文件
 | ||||||
|  |     async saveToFile() { | ||||||
|  |         try { | ||||||
|  |             await fs.writeFile(this.historyFile, JSON.stringify(this.messages, null, 2), 'utf8'); | ||||||
|  |         } catch (error) { | ||||||
|  |             console.error('保存历史消息到文件失败:', error); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // 导出历史(备份)
 | ||||||
|  |     async exportHistory(filename) { | ||||||
|  |         const exportPath = filename || `chat_backup_${new Date().toISOString().split('T')[0]}.json`; | ||||||
|  |         try { | ||||||
|  |             await fs.writeFile(exportPath, JSON.stringify(this.messages, null, 2), 'utf8'); | ||||||
|  |             console.log(`历史消息已导出到: ${exportPath}`); | ||||||
|  |             return exportPath; | ||||||
|  |         } catch (error) { | ||||||
|  |             console.error('导出历史消息失败:', error); | ||||||
|  |             throw error; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // 导入历史
 | ||||||
|  |     async importHistory(filename) { | ||||||
|  |         try { | ||||||
|  |             const data = await fs.readFile(filename, 'utf8'); | ||||||
|  |             const importedMessages = JSON.parse(data); | ||||||
|  |              | ||||||
|  |             // 验证消息格式
 | ||||||
|  |             if (Array.isArray(importedMessages)) { | ||||||
|  |                 this.messages = importedMessages; | ||||||
|  |                 await this.saveToFile(); | ||||||
|  |                 console.log(`已导入 ${this.messages.length} 条历史消息`); | ||||||
|  |                 return true; | ||||||
|  |             } else { | ||||||
|  |                 throw new Error('无效的消息格式'); | ||||||
|  |             } | ||||||
|  |         } catch (error) { | ||||||
|  |             console.error('导入历史消息失败:', error); | ||||||
|  |             throw error; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | module.exports = { MessageHistory }; | ||||||
							
								
								
									
										
											BIN
										
									
								
								videos/0.mp4
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								videos/0.mp4
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								videos/1-m.mp4
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								videos/1-m.mp4
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								videos/2.mp4
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								videos/2.mp4
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								videos/4-m.mp4
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								videos/4-m.mp4
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								videos/asd.mp4
									
									
									
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								videos/asd.mp4
									
									
									
									
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								videos/jkl.mp4
									
									
									
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								videos/jkl.mp4
									
									
									
									
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								videos/zxc.mp4
									
									
									
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								videos/zxc.mp4
									
									
									
									
									
								
							
										
											Binary file not shown.
										
									
								
							
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user