my-ai-chat-framework 1.0.2 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env +15 -0
- package/README.md +70 -268
- package/dist/my-ai-chat-framework.browser.es.js +427 -2627
- package/dist/my-ai-chat-framework.browser.es.js.map +1 -1
- package/dist/my-ai-chat-framework.browser.umd.js +442 -2633
- package/dist/my-ai-chat-framework.browser.umd.js.map +1 -1
- package/dist/my-ai-chat-framework.node.cjs.js +436 -1378
- package/dist/my-ai-chat-framework.node.cjs.js.map +1 -1
- package/package.json +15 -32
- package/src/adapters/openai.js +130 -0
- package/src/core/ChatService.js +110 -0
- package/src/core/EventEmitter.js +32 -126
- package/src/core/MessageStore.js +73 -0
- package/src/index.js +10 -414
- package/src/plugins/tool-calling.js +119 -0
- package/test.js +107 -0
- package/README_ZH.md +0 -401
- package/dist/my-ai-chat-framework.cjs.js +0 -2630
- package/dist/my-ai-chat-framework.cjs.js.map +0 -1
- package/dist/my-ai-chat-framework.es.js +0 -2630
- package/dist/my-ai-chat-framework.es.js.map +0 -1
- package/dist/my-ai-chat-framework.umd.js +0 -2634
- package/dist/my-ai-chat-framework.umd.js.map +0 -1
- package/src/core/ApiClient.js +0 -288
- package/src/core/MessageFormatter.js +0 -126
- package/src/core/Messages.js +0 -562
- package/src/core/RequestBuilder.js +0 -96
- package/src/core/ToolManager.js +0 -175
- package/src/utils/index.js +0 -21
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"my-ai-chat-framework.node.cjs.js","sources":["../src/core/MessageFormatter.js","../src/core/Messages.js","../src/core/EventEmitter.js","../src/core/ToolManager.js","../src/core/RequestBuilder.js","../src/core/ApiClient.js","../src/index.js"],"sourcesContent":["/**\r\n * 消息格式转换器\r\n * 负责将Messages的状态转换为不同格式\r\n */\r\nclass MessageFormatter {\r\n /**\r\n * 将消息转换为标准格式(过滤掉自定义元数据)\r\n */\r\n static toStandardFormat(message) {\r\n const { role, content } = message;\r\n const standardMessage = { role, content };\r\n \r\n // 保留重要的特殊字段\r\n if (message.reasoning_content) {\r\n standardMessage.reasoning_content = message.reasoning_content;\r\n }\r\n if (message.tool_calls) {\r\n standardMessage.tool_calls = message.tool_calls;\r\n }\r\n if (message.tool_call_id) {\r\n standardMessage.tool_call_id = message.tool_call_id; // ✅ 修复:保留tool_call_id\r\n }\r\n \r\n return standardMessage;\r\n }\r\n\r\n /**\r\n * 批量转换消息为标准格式\r\n */\r\n static batchToStandardFormat(messages) {\r\n return messages.map(msg => this.toStandardFormat(msg));\r\n }\r\n\r\n /**\r\n * 从Messages实例获取格式化后的消息数组\r\n */\r\n static formatMessages(messagesInstance, baseRounds, cycleRounds) {\r\n // 使用新的方法名\r\n const systems = messagesInstance.getEnabledSystemPrompts();\r\n \r\n // 添加提示系统\r\n const hintSystem = {\r\n role: 'system',\r\n content: `你的名字||标题是:${messagesInstance.getName() || '未知'}`\r\n };\r\n systems.unshift(hintSystem);\r\n \r\n // 使用新的方法名\r\n const recentMessages = messagesInstance.getRecentMessages(baseRounds, cycleRounds);\r\n return [...systems, ...recentMessages];\r\n }\r\n\r\n /**\r\n * 构造工具参数\r\n */\r\n static constructParameters(array) {\r\n let paramsArray = [];\r\n\r\n if (Array.isArray(array)) {\r\n paramsArray = array;\r\n } else if (typeof array === 'object' && array !== null) {\r\n paramsArray = [array];\r\n } else {\r\n throw new Error('[系统提示异常] 检测到无效的参数格式');\r\n }\r\n\r\n const parameters = {};\r\n\r\n for (let i = 0; i < paramsArray.length; i++) {\r\n const param = paramsArray[i];\r\n\r\n if (!param?.name?.trim()) {\r\n throw new Error(`[系统提示异常] 检测到空参数名称 位置: 第 ${i + 1} 个参数`);\r\n }\r\n\r\n if (!param?.type?.trim()) {\r\n throw new Error(`[系统提示异常] 检测到空参数类型 位置: 参数 \"${param.name}\"`);\r\n }\r\n\r\n if (!param?.description?.trim()) {\r\n throw new Error(`[系统提示异常] 检测到空参数描述 位置: 参数 \"${param.name}\"`);\r\n }\r\n\r\n parameters[param.name] = {\r\n type: param.type,\r\n description: param.description,\r\n };\r\n }\r\n\r\n return {\r\n type: 'object',\r\n properties: parameters,\r\n required: Object.keys(parameters),\r\n };\r\n }\r\n\r\n /**\r\n * 构造工具定义\r\n */\r\n static constructTool({ name, description, parameters }) {\r\n if (!name?.trim()) {\r\n throw new Error('[系统提示异常] 检测到空工具名称');\r\n }\r\n if (!description?.trim()) {\r\n throw new Error('[系统提示异常] 检测到空工具描述');\r\n }\r\n\r\n let processedParameters;\r\n try {\r\n processedParameters = this.constructParameters(parameters);\r\n } catch (error) {\r\n throw new Error(`[系统提示异常] 工具参数验证失败 工具名称: ${name} 错误详情: ${error.message}`);\r\n }\r\n\r\n return {\r\n type: 'function',\r\n function: {\r\n name,\r\n description,\r\n parameters: processedParameters,\r\n },\r\n };\r\n }\r\n}\r\n\r\nexport default MessageFormatter;","import MessageFormatter from './MessageFormatter.js';\n\nclass Messages {\n constructor() {\n this.messages = [];\n this.systemPrompts = [];\n this.tools = [];\n this.metadata = {\n name: '',\n createdAt: new Date(),\n lastModified: new Date()\n };\n }\n\n // ========== 核心消息方法 ==========\n\n /**\n * 添加消息(通用方法)\n * @param {string} role - 角色:user/assistant/system/tool\n * @param {string} content - 消息内容\n * @param {object} metadata - 额外元数据\n * @returns {number} 新消息的索引\n */\n addMessage(role, content, metadata = {}) {\n // 验证角色\n const validRoles = ['user', 'assistant', 'system', 'tool'];\n if (!validRoles.includes(role)) {\n throw new Error(`无效的角色: ${role},有效值: ${validRoles.join(', ')}`);\n }\n \n // 特殊处理:tool消息可以没有content(但必须有tool_call_id)\n if (role === 'tool') {\n // tool消息必须有tool_call_id\n if (!metadata.tool_call_id) {\n throw new Error('tool消息必须包含tool_call_id');\n }\n // tool消息的content可以为空\n } else {\n // 其他消息必须有内容\n if (typeof content !== 'string') {\n throw new Error('消息内容必须是字符串');\n }\n // 允许空字符串,但不允许undefined/null\n if (content === undefined || content === null) {\n throw new Error('消息内容不能为undefined或null');\n }\n }\n \n // 创建消息对象\n const message = {\n role,\n content: content ? content.trim() : '', // 允许空字符串\n timestamp: new Date()\n };\n \n // 合并元数据(过滤掉undefined和null)\n if (metadata && typeof metadata === 'object') {\n for (const [key, value] of Object.entries(metadata)) {\n if (value !== undefined && value !== null) {\n message[key] = value;\n }\n }\n }\n \n return this.messages.push(message) - 1;\n }\n\n /**\n * 添加用户消息(便捷方法)\n */\n addUserMessage(content, metadata = {}) {\n return this.addMessage('user', content, metadata);\n }\n\n /**\n * 添加助手消息(便捷方法)\n */\n addAssistantMessage(content, metadata = {}) {\n return this.addMessage('assistant', content, metadata);\n }\n\n /**\n * 添加系统消息(便捷方法)\n */\n addSystemMessage(content, metadata = {}) {\n return this.addMessage('system', content, metadata);\n }\n\n /**\n * 添加工具消息(便捷方法)\n */\n addToolMessage(content, toolCallId, metadata = {}) {\n if (!toolCallId || typeof toolCallId !== 'string') {\n throw new Error('tool_call_id不能为空');\n }\n \n return this.addMessage('tool', content, {\n tool_call_id: toolCallId,\n ...metadata\n });\n }\n\n /**\n * 批量添加消息\n */\n addMessages(messagesArray) {\n if (!Array.isArray(messagesArray)) {\n throw new Error('参数必须是数组');\n }\n \n const indices = [];\n for (const msg of messagesArray) {\n if (!msg.role || !msg.content) {\n throw new Error('消息必须包含role和content属性');\n }\n \n const { role, content, ...metadata } = msg;\n const index = this.addMessage(role, content, metadata);\n indices.push(index);\n }\n \n return indices;\n }\n\n // ========== 消息查询 ==========\n\n /**\n * 获取所有消息\n */\n getMessages() {\n return [...this.messages];\n }\n\n /**\n * 按角色获取消息\n */\n getMessagesByRole(role) {\n const validRoles = ['user', 'assistant', 'system', 'tool'];\n if (!validRoles.includes(role)) {\n throw new Error(`无效的角色: ${role}`);\n }\n return this.messages.filter(msg => msg.role === role);\n }\n\n /**\n * 获取最后一条消息\n */\n getLastMessage() {\n return this.messages.length > 0 \n ? { ...this.messages[this.messages.length - 1] }\n : null;\n }\n\n /**\n * 获取最后一条用户消息\n */\n getLastUserMessage() {\n for (let i = this.messages.length - 1; i >= 0; i--) {\n if (this.messages[i].role === 'user') {\n return { ...this.messages[i] };\n }\n }\n return null;\n }\n\n /**\n * 获取最后一条助手消息\n */\n getLastAssistantMessage() {\n for (let i = this.messages.length - 1; i >= 0; i--) {\n if (this.messages[i].role === 'assistant') {\n return { ...this.messages[i] };\n }\n }\n return null;\n }\n\n /**\n * 获取消息数量\n */\n getMessageCount() {\n return this.messages.length;\n }\n\n // ========== 消息修改 ==========\n\n /**\n * 更新消息内容\n */\n updateMessage(index, content) {\n if (!Number.isInteger(index) || index < 0 || index >= this.messages.length) {\n throw new Error(`无效的消息索引: ${index}`);\n }\n \n if (typeof content !== 'string' || !content.trim()) {\n throw new Error('消息内容不能为空');\n }\n \n this.messages[index].content = content.trim();\n this.messages[index].lastModified = new Date();\n return this;\n }\n\n /**\n * 更新最后一条消息\n */\n updateLastMessage(content) {\n if (this.messages.length === 0) {\n throw new Error('没有消息可以修改');\n }\n return this.updateMessage(this.messages.length - 1, content);\n }\n\n /**\n * 设置消息字段\n */\n setMessageField(index, field, value) {\n if (!Number.isInteger(index) || index < 0 || index >= this.messages.length) {\n throw new Error(`无效的消息索引: ${index}`);\n }\n \n this.messages[index][field] = value;\n this.messages[index].lastModified = new Date();\n return this;\n }\n\n // ========== 消息删除 ==========\n\n /**\n * 删除消息\n */\n removeMessage(index) {\n if (!Number.isInteger(index) || index < 0 || index >= this.messages.length) {\n throw new Error(`无效的消息索引: ${index}`);\n }\n \n return this.messages.splice(index, 1)[0];\n }\n\n /**\n * 删除最后一条消息\n */\n removeLastMessage() {\n if (this.messages.length === 0) {\n throw new Error('没有消息可以删除');\n }\n return this.removeMessage(this.messages.length - 1);\n }\n\n /**\n * 撤回到上一条助手消息\n */\n undoToAssistant() {\n if (this.messages.length === 0) {\n return [];\n }\n \n // 找到最后一条助手消息的位置\n let lastAssistantIndex = -1;\n for (let i = this.messages.length - 1; i >= 0; i--) {\n if (this.messages[i].role === 'assistant') {\n lastAssistantIndex = i;\n break;\n }\n }\n \n if (lastAssistantIndex >= 0) {\n // 删除该助手消息之后的所有消息\n return this.messages.splice(lastAssistantIndex + 1);\n } else {\n // 没有助手消息,清空所有\n const removed = [...this.messages];\n this.messages = [];\n return removed;\n }\n }\n\n /**\n * 是否可以撤回\n */\n canUndo() {\n return this.messages.some(msg => msg.role === 'assistant');\n }\n\n /**\n * 清空所有消息\n */\n clearMessages() {\n const removed = [...this.messages];\n this.messages = [];\n this.metadata.lastModified = new Date();\n return removed;\n }\n\n // ========== 系统提示管理 ==========\n\n /**\n * 添加系统提示\n */\n addSystemPrompt(content, enabled = true) {\n if (typeof content !== 'string' || !content.trim()) {\n throw new Error('系统提示内容不能为空');\n }\n \n const prompt = {\n role: 'system',\n content: content.trim(),\n enabled: Boolean(enabled),\n createdAt: new Date()\n };\n \n return this.systemPrompts.push(prompt) - 1;\n }\n\n /**\n * 获取启用的系统提示\n */\n getEnabledSystemPrompts() {\n return this.systemPrompts\n .filter(prompt => prompt.enabled)\n .map(prompt => ({ role: prompt.role, content: prompt.content }));\n }\n\n /**\n * 切换系统提示状态\n */\n toggleSystemPrompt(index) {\n if (!Number.isInteger(index) || index < 0 || index >= this.systemPrompts.length) {\n throw new Error(`无效的系统提示索引: ${index}`);\n }\n \n this.systemPrompts[index].enabled = !this.systemPrompts[index].enabled;\n return this;\n }\n\n /**\n * 更新系统提示内容\n */\n updateSystemPrompt(index, content) {\n if (!Number.isInteger(index) || index < 0 || index >= this.systemPrompts.length) {\n throw new Error(`无效的系统提示索引: ${index}`);\n }\n \n if (typeof content !== 'string' || !content.trim()) {\n throw new Error('系统提示内容不能为空');\n }\n \n this.systemPrompts[index].content = content.trim();\n this.systemPrompts[index].lastModified = new Date();\n return this;\n }\n\n /**\n * 删除系统提示\n */\n removeSystemPrompt(index) {\n if (!Number.isInteger(index) || index < 0 || index >= this.systemPrompts.length) {\n throw new Error(`无效的系统提示索引: ${index}`);\n }\n \n return this.systemPrompts.splice(index, 1)[0];\n }\n\n // ========== 工具管理 ==========\n\n /**\n * 添加工具\n */\n addTool(toolDefinition) {\n if (typeof toolDefinition === 'object' && toolDefinition !== null) {\n // 如果已经是格式化好的工具对象\n if (!toolDefinition.type || toolDefinition.type !== 'function') {\n throw new Error(`无效的工具类型,期望: 'function',实际: '${toolDefinition.type || 'undefined'}'`);\n }\n if (!toolDefinition.function?.name?.trim()) {\n throw new Error('工具名称不能为空');\n }\n return this.tools.push(toolDefinition) - 1;\n }\n\n // 如果是工具定义对象,使用MessageFormatter构造\n if (toolDefinition.name && toolDefinition.description && toolDefinition.parameters) {\n const constructedTool = MessageFormatter.constructTool(toolDefinition);\n return this.tools.push(constructedTool) - 1;\n }\n\n throw new Error('无效的工具格式');\n }\n\n /**\n * 从定义添加工具\n */\n addToolFromDefinition(name, description, parameters) {\n const tool = MessageFormatter.constructTool({ name, description, parameters });\n return this.tools.push(tool) - 1;\n }\n\n /**\n * 获取所有工具\n */\n getTools() {\n return [...this.tools];\n }\n\n /**\n * 删除工具\n */\n removeTool(index) {\n if (!Number.isInteger(index) || index < 0 || index >= this.tools.length) {\n throw new Error(`无效的工具索引: ${index}`);\n }\n \n return this.tools.splice(index, 1)[0];\n }\n\n /**\n * 清空所有工具\n */\n clearTools() {\n const removed = [...this.tools];\n this.tools = [];\n return removed;\n }\n\n // ========== 元数据管理 ==========\n\n setName(name) {\n if (typeof name !== 'string' || !name.trim()) {\n throw new Error('名称不能为空');\n }\n this.metadata.name = name.trim();\n this.metadata.lastModified = new Date();\n return this;\n }\n\n getName() {\n return this.metadata.name;\n }\n\n // ========== 兼容性方法 ==========\n\n /**\n * 获取最近的消息(兼容旧方法)\n */\n getRecentMessages(baseRounds, cycleRounds) {\n const messages = this.messages.map(item => {\n // 使用MessageFormatter.toStandardFormat来确保一致性\n return MessageFormatter.toStandardFormat(item);\n });\n \n const msgTotal = messages.length;\n const totalRounds = msgTotal / 2;\n\n if (!baseRounds || isNaN(baseRounds) || baseRounds <= 0) {\n return messages;\n }\n\n const maxCycleRoundMultiple = cycleRounds > 0\n ? Math.floor(totalRounds / cycleRounds) * cycleRounds\n : 0;\n const actualRounds = baseRounds + (totalRounds - maxCycleRoundMultiple);\n const actualMsgCount = actualRounds * 2;\n const n = Math.max(msgTotal - actualMsgCount, 0);\n\n return messages.slice(n);\n }\n\n /**\n * 获取所有格式化消息(兼容旧方法)\n */\n getMessagesFormatted(baseRounds, cycleRounds) {\n const systems = this.getEnabledSystemPrompts();\n const hintSystem = {\n role: 'system',\n content: `你的名字||标题是:${this.metadata.name || '未知'}`\n };\n systems.unshift(hintSystem);\n \n const recentMessages = this.getRecentMessages(baseRounds, cycleRounds);\n return [...systems, ...recentMessages];\n }\n\n // ========== 导入导出 ==========\n\n export() {\n return {\n __format_version: 2,\n exportedAt: new Date().toISOString(),\n metadata: { ...this.metadata },\n payload: {\n messages: JSON.parse(JSON.stringify(this.messages)),\n systemPrompts: JSON.parse(JSON.stringify(this.systemPrompts)),\n tools: JSON.parse(JSON.stringify(this.tools))\n }\n };\n }\n\n import(data) {\n const obj = typeof data === 'string' ? JSON.parse(data) : data;\n const parsed = Messages.parseCompatible(obj);\n \n this.messages = parsed.messages || [];\n this.systemPrompts = parsed.systemPrompts || [];\n this.tools = parsed.tools || [];\n this.metadata = parsed.metadata || { \n name: '', \n createdAt: new Date(), \n lastModified: new Date() \n };\n \n this.metadata.lastModified = new Date();\n return this;\n }\n\n static parseCompatible(obj) {\n // 处理新格式(v2)\n if (obj?.__format_version === 2 && obj.payload) {\n return {\n messages: obj.payload.messages || [],\n systemPrompts: obj.payload.systemPrompts || [],\n tools: obj.payload.tools || [],\n metadata: obj.metadata || { \n name: '', \n createdAt: new Date(), \n lastModified: new Date() \n }\n };\n }\n \n // 处理旧格式(v1)\n if (obj?.__format_version === 1 && obj.payload) {\n return {\n messages: obj.payload.messages || [],\n systemPrompts: obj.payload.systems || [],\n tools: obj.payload.tools || [],\n metadata: { \n name: obj.payload.hintData?.name || '',\n createdAt: new Date(),\n lastModified: new Date()\n }\n };\n }\n\n // 处理更旧的格式\n if (obj?.messages && Array.isArray(obj.messages)) {\n return {\n messages: obj.messages || [],\n systemPrompts: obj.systems || [],\n tools: obj.tools || [],\n metadata: { \n name: obj.hintData?.name || '',\n createdAt: new Date(),\n lastModified: new Date()\n }\n };\n }\n\n throw new Error('无法识别的聊天数据格式');\n }\n}\n\nexport default Messages;","/**\r\n * 简单的事件发射器类\r\n * 实现观察者模式,允许对象监听和触发事件\r\n */\r\nclass EventEmitter {\r\n constructor() {\r\n //存储事件和对应的回调数组\r\n //结构: \r\n // { 'event_name': [ callback1, callback2 ] }\r\n this.events = {}\r\n }\r\n\r\n /**\r\n * 监听事件\r\n * @param {string} eventName - 要注册的事件名称\r\n * @param {Function} callback - 事件触发时执行的回调函数\r\n */\r\n //实现一个监听事件的方法\r\n on(eventName, callback) {\r\n //1.如果没有事件数组,先创建\r\n if (!this.events[eventName]) {\r\n this.events[eventName] = []\r\n }\r\n\r\n //2.给数组添加(push)事件\r\n this.events[eventName].push(callback)\r\n\r\n //返回一个取消键听得函数(方便实用)\r\n return () => this.off(eventName, callback)\r\n }\r\n\r\n /**\r\n * 触发事件\r\n * @param {string} eventName --要触发的事件名称\r\n * @param {any} data --传递给回调函数的数据\r\n */\r\n emit(eventName, data) {\r\n //获取这个事件的所有回调函数\r\n const callbacks = this.events[eventName]\r\n\r\n //仅在有回调函数时调用他们,否则提前退出(可以这么写吧?少一层嵌套)\r\n if (!callbacks) return\r\n\r\n //使用slice()创建副本,放置在循环中修改数组导致问题\r\n const callbakesCopy = callbacks.slice()\r\n\r\n for (let i = 0; i < callbakesCopy.length; i++) {\r\n try {\r\n //执行回调函数,并传入数据\r\n callbakesCopy[i](data)\r\n } catch (error) {\r\n // 如果某个回调出错,不影响其他回调\r\n console.error(`事件 ${eventName} 的回调函数执行出错:`, error)\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * 取消监听事件\r\n * @param {string} eventName --要取消的事件名称\r\n * @param {Function} callback --要移除的回调函数\r\n */\r\n off(eventName, callback) {\r\n const callbacks = this.events[eventName]\r\n\r\n //判断一下防止报错\r\n if (!callbacks) return\r\n\r\n //过滤掉要移除的回调函数\r\n this.events[eventName] = callbacks.filter(item => item !== callback)\r\n\r\n // 如果这个事件没有监听器了,删除这个事件\r\n if (this.events[eventName].length === 0) {\r\n delete this.events[eventName]\r\n }\r\n }\r\n\r\n /**\r\n * 只监听一次事件(触发后自动取消)\r\n * @param {string} eventName - 事件名称\r\n * @param {Function} callback - 回调函数\r\n */\r\n once(evenName, callback) {\r\n // 创建一个包装函数,执行完成后自动取消\r\n const onceCallback = (data) => {\r\n //先取消监听\r\n this.off(evenName, onceCallback)\r\n //在执行原回调\r\n callback(data)\r\n }\r\n\r\n //监听包装后的函数\r\n this.on(evenName, onceCallback)\r\n\r\n //返回取消函数\r\n return () => this.off(evenName, onceCallback)\r\n }\r\n\r\n /**\r\n * 移除某个事件的所有监听器\r\n * @param {string} eventName - 事件名称\r\n */\r\n removeAllListeners(eventName) {\r\n if (eventName) {\r\n // 移除指定事件的所有监听器\r\n delete this.events[eventName]\r\n } else {\r\n // 如果没有指定事件名,移除所有事件的监听器\r\n this.events = {}\r\n }\r\n }\r\n\r\n /**\r\n * 获取某个事件的所有监听器数量\r\n * @param {string} eventName - 事件名称\r\n * @returns {number} 监听器数量\r\n */\r\n listenerCount(eventName) {\r\n const callbacks = this.events[eventName]\r\n return callbacks ? callbacks.length : 0\r\n }\r\n\r\n}\r\n\r\n//导出EventEmiter类\r\nexport default EventEmitter","import EventEmitter from './EventEmitter'\r\n/**\r\n * 工具管理器\r\n * 负责注册、管理和执行工具\r\n*/\r\nclass ToolManager extends EventEmitter {\r\n constructor() {\r\n super()\r\n this.tools = new Map() // 工具名 -> 工具函数\r\n }\r\n /**\r\n * 注册工具\r\n * @param {string} name - 工具名称\r\n * @param {object} toolDefinition - 工具定义(包含描述和参数信息)\r\n * @param {Function} executor - 工具执行函数,接受参数对象并返回结果\r\n */\r\n registerTool(name, toolDefinition, executor) {\r\n // 判断是否已经注册过同名工具\r\n if (this.tools.has(name)) {\r\n throw new Error(`[系统提示异常] 工具 \"${name}\" 已经注册过了`);\r\n }\r\n\r\n //在工具管理器中注册工具的函数\r\n this.tools.set(name, {\r\n definition: toolDefinition,\r\n executor: executor\r\n })\r\n // 触发工具注册事件,供外部监听\r\n this.emit('tool-registered', { name, definition: toolDefinition })\r\n // 返回this以支持链式调用\r\n return this\r\n }\r\n\r\n /**\r\n * 执行工具调用\r\n * @param {object} toolCall - 工具调用信息,包含工具名称和参数\r\n * @returns {Promise<any>} - 工具执行结果\r\n */\r\n async executeToolCall(toolCall) {\r\n const { id, function: func } = toolCall;\r\n const { name, arguments: argsStr } = func;\r\n \r\n if (!this.tools.has(name)) {\r\n throw new Error(`未找到工具: ${name}`);\r\n }\r\n \r\n const tool = this.tools.get(name);\r\n \r\n try {\r\n // 解析参数\r\n const args = JSON.parse(argsStr);\r\n \r\n // 触发事件:开始执行工具\r\n this.emit('tool-execute-start', { name, args });\r\n \r\n // 执行工具\r\n const result = await tool.executor(args);\r\n \r\n // 触发事件:工具执行成功\r\n this.emit('tool-execute-success', { name, args, result });\r\n \r\n // 确保返回正确的格式\r\n return {\r\n tool_call_id: id, // 必须包含tool_call_id\r\n name,\r\n result: JSON.stringify(result),\r\n success: true\r\n };\r\n } catch (error) {\r\n // 触发事件:工具执行失败\r\n this.emit('tool-execute-error', { name, error });\r\n \r\n // 即使失败也要返回tool_call_id\r\n return {\r\n tool_call_id: id,\r\n name,\r\n error: error.message,\r\n success: false\r\n };\r\n }\r\n }\r\n\r\n /**\r\n * 获取所有工具定义(用于发送给AI)\r\n */\r\n getToolDefinitions() {\r\n const definitions = [];\r\n for (const [name, tool] of this.tools) {\r\n definitions.push(tool.definition);\r\n }\r\n return definitions;\r\n }\r\n\r\n /**\r\n * 检查是否有工具\r\n */\r\n hasTools() {\r\n return this.tools.size > 0;\r\n }\r\n\r\n /**\r\n * 批量执行工具调用\r\n * @param {Array} toolCalls - 工具调用数组\r\n * @returns {Promise<Array>} - 所有工具的执行结果\r\n */\r\n async executeToolCalls(toolCalls) {\r\n if (!Array.isArray(toolCalls)) {\r\n throw new Error('toolCalls必须是数组');\r\n }\r\n \r\n const results = [];\r\n \r\n // 并行执行所有工具调用\r\n const promises = toolCalls.map(async (toolCall) => {\r\n try {\r\n const result = await this.executeToolCall(toolCall);\r\n return result;\r\n } catch (error) {\r\n // 某个工具失败,返回错误信息\r\n return {\r\n tool_call_id: toolCall.id,\r\n name: toolCall.function.name,\r\n error: error.message,\r\n success: false\r\n };\r\n }\r\n });\r\n \r\n // 等待所有工具执行完成\r\n const settledResults = await Promise.allSettled(promises);\r\n \r\n // 处理结果\r\n for (const settled of settledResults) {\r\n if (settled.status === 'fulfilled') {\r\n results.push(settled.value);\r\n } else {\r\n // 理论上不会到这里,因为错误已经在上面捕获了\r\n results.push({\r\n error: settled.reason.message,\r\n success: false\r\n });\r\n }\r\n }\r\n \r\n return results;\r\n }\r\n \r\n /**\r\n * 串行执行工具调用(如果需要顺序执行)\r\n */\r\n async executeToolCallsSequentially(toolCalls) {\r\n const results = [];\r\n \r\n for (const toolCall of toolCalls) {\r\n try {\r\n const result = await this.executeToolCall(toolCall);\r\n results.push(result);\r\n } catch (error) {\r\n // 某个工具失败,可以选择停止或继续\r\n results.push({\r\n tool_call_id: toolCall.id,\r\n name: toolCall.function.name,\r\n error: error.message,\r\n success: false\r\n });\r\n // 可以选择继续执行下一个工具\r\n // 或者抛出错误停止执行:throw error;\r\n }\r\n }\r\n \r\n return results;\r\n }\r\n}\r\n\r\nexport default ToolManager","import MessageFormatter from './MessageFormatter.js';\r\nimport ToolManager from './ToolManager.js';\r\nimport EventEmitter from './EventEmitter.js';\r\n\r\n/**\r\n * 请求构建器\r\n * 负责构建符合不同API规范的请求体\r\n */\r\nclass RequestBuilder {\r\n constructor(model, messagesInstance, config = {}) {\r\n this.model = model;\r\n this.messages = messagesInstance;\r\n this.config = {\r\n // temperature: 0.7,\r\n // max_tokens: 2000,\r\n // stream: false, // 默认关闭流式传输\r\n ...config\r\n };\r\n this.toolManager = null; // 可选的ToolManager引用\r\n }\r\n\r\n /**\r\n * 设置ToolManager引用\r\n */\r\n setToolManager(toolManager) {\r\n this.toolManager = toolManager;\r\n return this;\r\n }\r\n\r\n /**\r\n * 构建OpenAI兼容格式的请求体\r\n */\r\n buildOpenAIFormat(baseRounds, cycleRounds) {\r\n // 使用新的MessageFormatter方法\r\n const formattedMessages = MessageFormatter.formatMessages(\r\n this.messages, \r\n baseRounds, \r\n cycleRounds\r\n );\r\n \r\n const request = {\r\n model: this.model,\r\n messages: formattedMessages,\r\n ...this.config\r\n };\r\n\r\n // 优先从ToolManager获取工具定义,其次从Messages获取\r\n let tools = [];\r\n if (this.toolManager && this.toolManager.hasTools()) {\r\n tools = this.toolManager.getToolDefinitions();\r\n } else {\r\n tools = this.messages.getTools();\r\n }\r\n \r\n if (tools && tools.length > 0) {\r\n request.tools = tools;\r\n request.tool_choice = 'auto';\r\n }\r\n\r\n return request;\r\n }\r\n\r\n /**\r\n * 构建请求体并返回JSON字符串\r\n */\r\n toJSON(baseRounds, cycleRounds) {\r\n const request = this.buildOpenAIFormat(baseRounds, cycleRounds);\r\n return JSON.stringify(request);\r\n }\r\n\r\n /**\r\n * 构建请求体并返回对象\r\n */\r\n toObject(baseRounds, cycleRounds) {\r\n return this.buildOpenAIFormat(baseRounds, cycleRounds);\r\n }\r\n\r\n /**\r\n * 设置配置项\r\n */\r\n setConfig(key, value) {\r\n this.config[key] = value;\r\n return this; // 支持链式调用\r\n }\r\n\r\n /**\r\n * 批量设置配置项\r\n */\r\n setConfigs(configs) {\r\n Object.assign(this.config, configs);\r\n return this;\r\n }\r\n}\r\n\r\nexport default RequestBuilder;\r\n\r\n","import axios from 'axios';\nimport EventEmitter from './EventEmitter.js';\n\n/**\n * API客户端(使用axios)\n * 支持浏览器和Node.js环境\n */\nclass ApiClient extends EventEmitter {\n constructor(apiKey, apiUrl = 'https://api.deepseek.com/v1/chat/completions') {\n super();\n this.apiKey = apiKey;\n this.url = apiUrl;\n this.isGenerating = false;\n this.cancelTokenSource = null;\n \n // 创建axios实例\n this.axiosInstance = axios.create({\n baseURL: apiUrl,\n headers: {\n 'Authorization': `Bearer ${apiKey}`,\n 'Content-Type': 'application/json'\n },\n timeout: 30000 // 30秒超时\n });\n }\n\n setApiKey(apiKey) {\n if (!apiKey || typeof apiKey !== 'string') {\n throw new Error('无效的API Key');\n }\n this.apiKey = apiKey;\n this.axiosInstance.defaults.headers['Authorization'] = `Bearer ${apiKey}`;\n }\n\n setApiUrl(apiUrl) {\n if (!apiUrl || typeof apiUrl !== 'string') {\n throw new Error('无效的API URL');\n }\n this.url = apiUrl;\n this.axiosInstance.defaults.baseURL = apiUrl;\n }\n\n async send(body) {\n this.isGenerating = true;\n this.cancelTokenSource = axios.CancelToken.source();\n \n try {\n // 触发事件:开始发送\n this.emit('request-start', { body });\n \n const requestBody = this._buildRequestBody(body);\n const response = await this.axiosInstance.post('', requestBody, {\n cancelToken: this.cancelTokenSource.token\n });\n \n // 触发事件:请求成功\n this.emit('request-success', { response: response.data });\n \n return response.data;\n } catch (error) {\n if (axios.isCancel(error)) {\n const cancelError = new Error('请求被中断');\n cancelError.name = 'CancelError';\n // 触发事件:请求被取消\n this.emit('request-cancel', { error: cancelError });\n throw cancelError;\n }\n \n // 触发事件:请求失败\n this.emit('request-error', { error });\n throw this._handleAxiosError(error);\n } finally {\n this.isGenerating = false;\n this.cancelTokenSource = null;\n }\n }\n\n async strSend(body, onProgress = () => {}, onDone = () => {}) {\n this.isGenerating = true;\n this.cancelTokenSource = axios.CancelToken.source();\n \n try {\n // 触发事件:开始流式请求\n this.emit('stream-start', { body });\n \n const requestBody = this._buildRequestBody(body);\n const response = await fetch(this.url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${this.apiKey}`,\n 'Accept': 'text/event-stream'\n },\n body: requestBody,\n signal: this.cancelTokenSource.token ? \n new AbortController().signal : undefined\n });\n\n if (!response.ok) {\n throw new Error(`HTTP错误: ${response.status}`);\n }\n\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n const message = {\n content: '',\n reasoning_content: '',\n tool_calls: [] // 新增:收集工具调用\n };\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n let lines = buffer.split(/(\\r?\\n){2,}/);\n buffer = lines.pop() || '';\n\n for (let rawChunk of lines) {\n const cleanChunk = rawChunk.trim().replace(/^data: /, '');\n if (!cleanChunk) continue;\n if (cleanChunk === '[DONE]') {\n // 触发事件:流式传输完成\n this.emit('stream-done', { message });\n onDone({ ...message });\n return;\n }\n \n try {\n const obj = JSON.parse(cleanChunk);\n console.log('🔍 收到流式chunk:', obj);\n \n // 收集内容\n if (obj.choices?.[0]?.delta?.content) {\n message.content += obj.choices[0].delta.content;\n }\n if (obj.choices?.[0]?.delta?.reasoning_content) {\n message.reasoning_content += obj.choices[0].delta.reasoning_content;\n }\n \n // 收集工具调用\n if (obj.choices?.[0]?.delta?.tool_calls) {\n const deltaToolCalls = obj.choices[0].delta.tool_calls;\n deltaToolCalls.forEach(toolCall => {\n const index = toolCall.index || 0;\n \n // 确保数组存在\n if (!message.tool_calls) message.tool_calls = [];\n \n // 初始化或更新\n if (!message.tool_calls[index]) {\n message.tool_calls[index] = {\n id: '',\n type: 'function',\n function: { name: '', arguments: '' }\n };\n }\n \n // 只更新非空字段\n if (toolCall.id) message.tool_calls[index].id = toolCall.id;\n if (toolCall.type) message.tool_calls[index].type = toolCall.type;\n if (toolCall.function?.name) {\n message.tool_calls[index].function.name += toolCall.function.name;\n }\n if (toolCall.function?.arguments) {\n message.tool_calls[index].function.arguments += toolCall.function.arguments;\n }\n });\n }\n \n // 触发事件:收到流式数据块\n this.emit('stream-chunk', { chunk: obj, message });\n onProgress({ ...message });\n } catch (e) {\n console.error('🔍 解析chunk错误:', e, '原始数据:', cleanChunk);\n }\n }\n\n if (!this.isGenerating) {\n break;\n }\n }\n \n // 触发事件:流式传输完成\n this.emit('stream-done', { message });\n onDone({ ...message });\n \n } catch (error) {\n if (error.name === 'AbortError' || axios.isCancel(error)) {\n const cancelError = new Error('请求被中断');\n cancelError.name = 'CancelError';\n // 触发事件:流式请求被取消\n this.emit('stream-cancel', { error: cancelError });\n throw cancelError;\n }\n \n // 触发事件:流式请求失败\n this.emit('stream-error', { error });\n throw error;\n } finally {\n this.isGenerating = false;\n this.cancelTokenSource = null;\n }\n }\n\n interrupt() {\n if (this.isGenerating && this.cancelTokenSource) {\n this.cancelTokenSource.cancel('用户中断请求');\n this.isGenerating = false;\n }\n }\n\n _buildRequestBody(body) {\n if (typeof body?.toJSON === 'function') {\n return body.toJSON();\n } else if (typeof body === 'object' && body !== null) {\n return JSON.stringify(body);\n } else if (typeof body === 'string') {\n try {\n JSON.parse(body);\n return body;\n } catch {\n throw new Error('字符串不是有效的JSON格式');\n }\n } else {\n throw new Error('无效的请求体类型,应传入对象或JSON字符串');\n }\n }\n\n _handleAxiosError(error) {\n if (error.response) {\n // 服务器返回错误状态码\n const status = error.response.status;\n const data = error.response.data;\n \n let message = `API错误 ${status}`;\n if (data?.error?.message) {\n message += `: ${data.error.message}`;\n }\n \n return new Error(message);\n } else if (error.request) {\n // 请求发送但无响应\n return new Error('网络错误:无法连接到API服务器');\n } else {\n // 其他错误\n return error;\n }\n }\n\n static parseCompatible(obj) {\n // 保持原有方法不变\n if (obj?.__format_version === 1 && obj.payload) {\n return {\n messages: obj.payload.messages || [],\n systems: obj.payload.systems || [],\n hintData: obj.payload.hintData || { \n name: '', \n toolState: { \n currentTime: '同步失败', \n currentTime_TS: 0 \n }\n },\n tools: obj.payload.tools || []\n };\n }\n\n if (obj?.messages && Array.isArray(obj.messages)) {\n return {\n messages: obj.messages || [],\n systems: obj.systems || [],\n hintData: obj.hintData || { \n name: '', \n toolState: { \n currentTime: '同步失败', \n currentTime_TS: 0 \n }\n },\n tools: obj.tools || []\n };\n }\n\n throw new Error('无法识别的聊天数据格式');\n }\n}\n\nexport default ApiClient;","// 这是 AI 聊天框架的入口文件\n// 导出所有核心组件供外部使用\n\nimport Messages from './core/Messages.js';\nimport RequestBuilder from './core/RequestBuilder.js';\nimport ApiClient from './core/ApiClient.js';\nimport EventEmitter from './core/EventEmitter.js';\nimport MessageFormatter from './core/MessageFormatter.js';\nimport ToolManager from './core/ToolManager.js'; // 新增导入\n\n// 导出所有核心类\nexport { Messages, RequestBuilder, ApiClient, EventEmitter, MessageFormatter, ToolManager };\n\n// ChatService类,继承EventEmitter\nclass ChatService extends EventEmitter {\n constructor(apiKey, model = 'deepseek-chat', config = {}) {\n super();\n \n this.messages = new Messages();\n this.apiClient = new ApiClient(apiKey);\n this.requestBuilder = new RequestBuilder(model, this.messages, config);\n this.toolManager = new ToolManager(); // 新增:工具管理器\n \n \n this._setupDefaultListeners();\n }\n\n _setupDefaultListeners() {\n // 监听API客户端的事件\n this.apiClient.on('request-start', (data) => {\n this.emit('request-start', data);\n });\n \n this.apiClient.on('request-success', (data) => {\n this.emit('request-success', data);\n });\n \n this.apiClient.on('request-error', (data) => {\n this.emit('request-error', data);\n });\n \n this.apiClient.on('stream-chunk', (data) => {\n this.emit('stream-chunk', data);\n });\n\n // 监听工具管理器的事件\n this.toolManager.on('tool-execute-start', (data) => {\n this.emit('tool-execute-start', data);\n });\n \n this.toolManager.on('tool-execute-success', (data) => {\n this.emit('tool-execute-success', data);\n });\n \n this.toolManager.on('tool-execute-error', (data) => {\n this.emit('tool-execute-error', data);\n });\n }\n\n /**\n * 注册工具\n */\n registerTool(name, definition, executor) {\n // 1. 在ToolManager中注册\n this.toolManager.registerTool(name, definition, executor);\n \n // 2. 在Messages中添加工具定义(用于发送给AI)\n this.messages.addTool(definition);\n \n return this; // 支持链式调用\n }\n\n /**\n * 发送消息(支持工具调用)\n */\n async send(userMessage, options = {}) {\n try {\n // 触发事件:开始发送\n this.emit('sending', { \n role: 'user', \n content: userMessage,\n timestamp: new Date()\n });\n \n // 添加用户消息\n this.messages.addUserMessage(userMessage);\n \n // 构建请求体(会自动包含工具定义)\n // 注意:这里需要确保RequestBuilder的配置正确\n const requestBody = this.requestBuilder.toJSON(\n options.baseRounds, \n options.cycleRounds\n );\n \n // 发送请求\n const response = await this.apiClient.send(requestBody);\n const aiMessage = response.choices[0].message;\n \n // 检查是否有工具调用\n if (aiMessage.tool_calls && aiMessage.tool_calls.length > 0) {\n // 处理工具调用\n return await this._handleToolCalls(aiMessage);\n } else {\n // 普通回复\n return await this._handleNormalResponse(aiMessage);\n }\n } catch (error) {\n this.emit('error', {\n error: error,\n message: userMessage,\n timestamp: new Date()\n });\n throw error;\n }\n }\n\n /**\n * 处理工具调用(支持循环调用)\n */\n async _handleToolCalls(aiMessage, maxIterations = 5) {\n console.log('🔍 处理工具调用,aiMessage:', aiMessage);\n \n let iteration = 0;\n let currentMessage = aiMessage;\n \n // 循环处理工具调用,直到没有工具调用或达到最大迭代次数\n while (iteration < maxIterations) {\n iteration++;\n console.log(`🔄 工具调用迭代 ${iteration}/${maxIterations}`);\n \n // 1. 添加助手消息(包含工具调用和思考内容)\n this.messages.addAssistantMessage(currentMessage.content || '', {\n tool_calls: currentMessage.tool_calls,\n reasoning_content: currentMessage.reasoning_content || '' // ✅ 新增:保存思考内容\n });\n \n // 触发事件:AI请求工具调用\n this.emit('tool-call-requested', {\n tool_calls: currentMessage.tool_calls,\n reasoning_content: currentMessage.reasoning_content, // ✅ 新增:包含思考内容\n timestamp: new Date(),\n iteration: iteration\n });\n \n // 2. 执行工具\n const toolResults = await this.toolManager.executeToolCalls(currentMessage.tool_calls);\n console.log('🔍 工具执行结果:', toolResults);\n \n // 3. 添加工具结果消息\n for (const result of toolResults) {\n console.log('🔍 添加工具消息,result:', result);\n this.messages.addToolMessage(\n result.result || result.error || '', \n result.tool_call_id\n );\n }\n \n // 4. 检查当前消息状态\n console.log('🔍 当前所有消息:', this.messages.getMessages());\n \n // 5. 再次发送请求,获取AI对工具结果的响应\n const requestBody = this.requestBuilder.toJSON();\n console.log('🔍 第', iteration, '次请求体:', requestBody);\n \n const response = await this.apiClient.send(requestBody);\n currentMessage = response.choices[0].message;\n \n // 6. 检查AI是否还有新的工具调用请求\n if (!currentMessage.tool_calls || currentMessage.tool_calls.length === 0) {\n // 没有更多工具调用,结束循环\n console.log('✅ 工具调用完成,AI给出最终回复');\n break;\n }\n \n console.log('🔄 AI请求了新的工具调用,继续处理...');\n }\n \n // 7. 添加最终助手消息\n this.messages.addAssistantMessage(currentMessage.content || '', {\n reasoning_content: currentMessage.reasoning_content || '' // ✅ 新增:保存最终思考内容\n });\n \n // 触发事件:收到最终回复\n this.emit('message', {\n role: 'assistant',\n content: currentMessage.content,\n reasoning_content: currentMessage.reasoning_content, // ✅ 新增:包含思考内容\n timestamp: new Date(),\n response: { choices: [{ message: currentMessage }] },\n toolIterations: iteration\n });\n \n return currentMessage.content;\n }\n\n /**\n * 处理普通回复\n */\n async _handleNormalResponse(aiMessage) {\n // 添加助手消息,处理空内容\n this.messages.addAssistantMessage(aiMessage.content || '', {\n reasoning_content: aiMessage.reasoning_content\n });\n \n // 触发事件:收到回复\n this.emit('message', {\n role: 'assistant',\n content: aiMessage.content,\n timestamp: new Date(),\n response: { choices: [{ message: aiMessage }] }\n });\n \n return aiMessage.content;\n }\n\n /**\n * 流式发送消息(支持工具调用)\n */\n async stream(userMessage, onProgress, onDone, options = {}) {\n try {\n // 触发事件:开始发送\n this.emit('sending', { \n role: 'user', \n content: userMessage,\n timestamp: new Date()\n });\n \n // 添加用户消息\n this.messages.addUserMessage(userMessage);\n \n // 构建请求体 - 关键:设置stream: true\n const requestBuilder = new RequestBuilder(this.requestBuilder.model, this.messages, {\n ...this.requestBuilder.config,\n stream: true // ✅ 必须设置为true!\n });\n \n const requestBody = requestBuilder.toJSON(\n options.baseRounds, \n options.cycleRounds\n );\n \n // 流式发送\n const response = await this.apiClient.strSend(\n requestBody,\n (chunk) => {\n // 流式进度回调\n this.emit('stream-progress', chunk);\n onProgress(chunk);\n },\n async (finalMessage) => {\n // 流式完成回调\n if (finalMessage.tool_calls && finalMessage.tool_calls.length > 0) {\n // 处理流式中的工具调用\n await this._handleStreamToolCalls(finalMessage, onProgress, onDone);\n } else {\n // 普通流式回复\n this.messages.addAssistantMessage(finalMessage.content || '', {\n reasoning_content: finalMessage.reasoning_content\n });\n \n this.emit('stream-done', { message: finalMessage });\n onDone(finalMessage);\n }\n }\n );\n \n return response;\n } catch (error) {\n this.emit('error', {\n error: error,\n message: userMessage,\n timestamp: new Date()\n });\n throw error;\n }\n }\n\n /**\n * 处理流式传输中的工具调用(支持循环调用)\n */\n async _handleStreamToolCalls(aiMessage, onProgress, onDone, maxIterations = 5) {\n let iteration = 0;\n let currentMessage = aiMessage;\n \n // 循环处理工具调用\n while (iteration < maxIterations) {\n iteration++;\n \n // 1. 添加助手消息(包含工具调用和思考内容)\n this.messages.addAssistantMessage(currentMessage.content || '', {\n tool_calls: currentMessage.tool_calls,\n reasoning_content: currentMessage.reasoning_content || '' // ✅ 新增:保存思考内容\n });\n \n // 触发事件:AI请求工具调用\n this.emit('tool-call-requested', {\n tool_calls: currentMessage.tool_calls,\n reasoning_content: currentMessage.reasoning_content, // ✅ 新增:包含思考内容\n timestamp: new Date(),\n iteration: iteration\n });\n \n // 2. 执行工具\n const toolResults = await this.toolManager.executeToolCalls(currentMessage.tool_calls);\n \n // 3. 添加工具结果消息\n for (const result of toolResults) {\n this.messages.addToolMessage(result.result || result.error, result.tool_call_id);\n }\n \n // 4. 再次发送流式请求(包含工具结果)\n const requestBody = this.requestBuilder.toJSON();\n \n // 使用Promise包装流式请求,等待完成\n await new Promise((resolve, reject) => {\n this.apiClient.strSend(\n requestBody,\n (chunk) => {\n this.emit('stream-progress', chunk);\n onProgress(chunk);\n },\n (finalMessage) => {\n currentMessage = finalMessage;\n \n // 检查是否还有工具调用\n if (!currentMessage.tool_calls || currentMessage.tool_calls.length === 0) {\n // 没有更多工具调用,结束循环\n resolve();\n } else {\n // 还有工具调用,继续循环\n resolve();\n }\n }\n ).catch(reject);\n });\n \n // 如果没有工具调用了,跳出循环\n if (!currentMessage.tool_calls || currentMessage.tool_calls.length === 0) {\n break;\n }\n }\n \n // 添加最终助手消息\n this.messages.addAssistantMessage(currentMessage.content, {\n reasoning_content: currentMessage.reasoning_content || '' // ✅ 新增:保存最终思考内容\n });\n \n this.emit('stream-done', { \n message: currentMessage,\n reasoning_content: currentMessage.reasoning_content, // ✅ 新增:包含思考内容\n toolIterations: iteration\n });\n onDone(currentMessage);\n }\n\n /**\n * 撤回消息\n */\n undo() {\n const removed = this.messages.undoToAssistant();\n if (removed.length > 0) {\n this.emit('undo', {\n removedMessages: removed,\n timestamp: new Date()\n });\n }\n return removed;\n }\n\n /**\n * 清空消息\n */\n clear() {\n const removed = this.messages.clearMessages();\n this.emit('clear', {\n removedMessages: removed,\n timestamp: new Date()\n });\n return removed;\n }\n\n /**\n * 导出对话数据\n */\n export() {\n const data = this.messages.export();\n this.emit('export', {\n data: data,\n timestamp: new Date()\n });\n return data;\n }\n\n /**\n * 导入对话数据\n */\n import(data) {\n this.messages.import(data);\n this.emit('import', {\n data: data,\n timestamp: new Date()\n });\n return this;\n }\n}\n\n// 导出 ChatService\nexport { ChatService };\n\n// 导出默认对象\nexport default {\n Messages,\n RequestBuilder,\n ApiClient,\n EventEmitter,\n MessageFormatter,\n ToolManager,\n ChatService\n};"],"names":["index"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"my-ai-chat-framework.node.cjs.js","names":[],"sources":["../src/core/EventEmitter.js","../src/core/MessageStore.js","../src/core/ChatService.js","../src/adapters/openai.js","../src/plugins/tool-calling.js","../src/index.js"],"sourcesContent":["/**\n * 极简事件发射器\n */\nexport class EventEmitter {\n constructor() {\n this._events = new Map();\n }\n\n on(event, handler) {\n if (!this._events.has(event)) this._events.set(event, []);\n this._events.get(event).push(handler);\n return () => this.off(event, handler);\n }\n\n off(event, handler) {\n if (!this._events.has(event)) return;\n const handlers = this._events.get(event).filter(h => h !== handler);\n if (handlers.length) this._events.set(event, handlers);\n else this._events.delete(event);\n }\n\n emit(event, data) {\n if (!this._events.has(event)) return;\n for (const handler of this._events.get(event)) {\n try {\n handler(data);\n } catch (err) {\n console.error(`事件 ${event} 处理出错:`, err);\n }\n }\n }\n}\n","/**\n * 消息存储 - 极简实现\n * 每条消息格式:\n * {\n * id?: string,\n * role: 'user'|'assistant'|'system'|'tool',\n * content: string,\n * toolCalls?: Array,\n * toolCallId?: string,\n * timestamp?: number,\n * metadata?: any\n * }\n */\nexport class MessageStore {\n constructor() {\n this._messages = [];\n this._idCounter = 0;\n }\n\n add(message) {\n const msg = {\n id: message.id || `msg_${Date.now()}_${++this._idCounter}`,\n timestamp: message.timestamp || Date.now(),\n ...message\n };\n this._messages.push(msg);\n return msg;\n }\n\n addUser(content, metadata = {}) {\n return this.add({ role: 'user', content, metadata });\n }\n\n addAssistant(content, metadata = {}) {\n return this.add({ role: 'assistant', content, metadata });\n }\n\n addSystem(content, metadata = {}) {\n return this.add({ role: 'system', content, metadata });\n }\n\n addTool(content, toolCallId, metadata = {}) {\n return this.add({ role: 'tool', content, toolCallId, metadata });\n }\n\n getLast() {\n return this._messages[this._messages.length - 1] || null;\n }\n\n getAll() {\n return [...this._messages];\n }\n\n clear() {\n this._messages = [];\n }\n\n // 撤回最后一条助手消息及其后的所有消息(用于工具调用循环)\n undoToLastAssistant() {\n let lastAssistantIndex = -1;\n for (let i = this._messages.length - 1; i >= 0; i--) {\n if (this._messages[i].role === 'assistant') {\n lastAssistantIndex = i;\n break;\n }\n }\n if (lastAssistantIndex >= 0) {\n const removed = this._messages.splice(lastAssistantIndex + 1);\n return removed;\n }\n return [];\n }\n}\n","import { EventEmitter } from './EventEmitter.js';\nimport { MessageStore } from './MessageStore.js';\n\n/**\n * 核心聊天服务\n * 极简设计:只负责消息存储、事件发射、插件管理、请求委托\n */\nexport class ChatService extends EventEmitter {\n constructor(config = {}) {\n super();\n this.config = {\n apiKey: config.apiKey || '',\n model: config.model || 'gpt-3.5-turbo',\n ...config\n };\n this.messages = new MessageStore();\n this._plugins = [];\n this._adapter = null; // 当前使用的 API 适配器\n this._pendingRequest = null;\n }\n\n /**\n * 加载插件\n * @param {object} plugin - 必须包含 install 方法\n */\n use(plugin) {\n if (typeof plugin.install === 'function') {\n plugin.install(this);\n this._plugins.push(plugin);\n } else {\n throw new Error('插件必须提供 install 方法');\n }\n return this;\n }\n\n /**\n * 设置 API 适配器\n */\n setAdapter(adapter) {\n this._adapter = adapter;\n return this;\n }\n\n /**\n * 发送消息(非流式)\n * @param {string|object} input - 字符串或消息对象\n */\n async send(input) {\n const userMsg = typeof input === 'string'\n ? { role: 'user', content: input }\n : { role: 'user', ...input };\n\n this.messages.add(userMsg);\n this.emit('sending', userMsg);\n\n // 构建请求\n const requestBody = this._adapter.buildRequest(this.messages.getAll(), this.config);\n const response = await this._adapter.send(requestBody, this.config);\n const assistantMsg = this._adapter.parseResponse(response);\n\n this.messages.add(assistantMsg);\n this.emit('message', assistantMsg);\n return assistantMsg;\n }\n\n /**\n * 流式发送消息\n * @param {string|object} input\n * @param {function} onProgress - 每次收到数据块时调用,参数为累积的当前消息对象\n * @param {function} onDone - 完成时调用,参数为最终消息对象\n */\n async stream(input, onProgress, onDone) {\n const userMsg = typeof input === 'string'\n ? { role: 'user', content: input }\n : { role: 'user', ...input };\n\n this.messages.add(userMsg);\n this.emit('sending', userMsg);\n\n const requestBody = this._adapter.buildRequest(this.messages.getAll(), this.config);\n\n let accumulated = { role: 'assistant', content: '' };\n await this._adapter.stream(\n requestBody,\n this.config,\n (chunk) => {\n // 累积消息\n if (chunk.content) accumulated.content += chunk.content;\n if (chunk.toolCalls) accumulated.toolCalls = chunk.toolCalls;\n if (chunk.reasoningContent) accumulated.reasoningContent = chunk.reasoningContent;\n // 触发进度事件\n this.emit('stream-progress', { ...accumulated });\n if (onProgress) onProgress({ ...accumulated });\n },\n (finalMessage) => {\n this.messages.add(finalMessage);\n this.emit('message', finalMessage);\n if (onDone) onDone(finalMessage);\n }\n );\n }\n\n /**\n * 注册工具(由工具调用插件实现)\n * 这里只留一个空方法,插件会覆盖它\n */\n registerTool(name, description, executor) {\n throw new Error('工具调用插件未加载,请先使用 use(toolCallingPlugin)');\n }\n}\n","/**\n * OpenAI 兼容 API 适配器\n * 支持 DeepSeek 等完全兼容 OpenAI 接口的服务\n */\nexport const openaiAdapter = {\n name: 'openai',\n\n install(chatService) {\n // 将自身设置为适配器\n chatService.setAdapter(this);\n },\n\n buildRequest(messages, config) {\n // 将内部消息格式转为 OpenAI 格式\n const apiMessages = messages.map(msg => {\n const base = { role: msg.role, content: msg.content };\n if (msg.role === 'assistant' && msg.toolCalls) {\n base.tool_calls = msg.toolCalls;\n }\n if (msg.role === 'tool' && msg.toolCallId) {\n base.tool_call_id = msg.toolCallId;\n }\n return base;\n });\n\n return {\n model: config.model,\n messages: apiMessages,\n temperature: config.temperature ?? 0.7,\n max_tokens: config.maxTokens ?? 2000,\n stream: false // 流式请求会在 stream 方法中单独处理\n };\n },\n\n async send(requestBody, config) {\n const response = await fetch(config.apiUrl || 'https://api.openai.com/v1/chat/completions', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${config.apiKey}`\n },\n body: JSON.stringify(requestBody)\n });\n if (!response.ok) {\n const error = await response.text();\n throw new Error(`API 请求失败: ${response.status} ${error}`);\n }\n const data = await response.json();\n return data;\n },\n\n parseResponse(apiResponse) {\n const choice = apiResponse.choices[0];\n const msg = choice.message;\n // 转换为内部格式\n const internal = {\n role: msg.role,\n content: msg.content || '',\n };\n if (msg.tool_calls) {\n internal.toolCalls = msg.tool_calls;\n }\n if (msg.reasoning_content) {\n internal.reasoningContent = msg.reasoning_content;\n }\n return internal;\n },\n\n async stream(requestBody, config, onProgress, onDone) {\n // 流式请求需要设置 stream: true\n const streamBody = { ...requestBody, stream: true };\n const response = await fetch(config.apiUrl || 'https://api.openai.com/v1/chat/completions', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${config.apiKey}`,\n 'Accept': 'text/event-stream'\n },\n body: JSON.stringify(streamBody)\n });\n if (!response.ok) {\n const error = await response.text();\n throw new Error(`流式请求失败: ${response.status} ${error}`);\n }\n\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n let accumulated = { role: 'assistant', content: '' };\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split(/\\n\\n/);\n buffer = lines.pop();\n\n for (const line of lines) {\n const dataLine = line.replace(/^data: /, '').trim();\n if (!dataLine || dataLine === '[DONE]') continue;\n try {\n const chunk = JSON.parse(dataLine);\n const delta = chunk.choices?.[0]?.delta;\n if (!delta) continue;\n\n const update = { role: delta.role || 'assistant', content: delta.content || '' };\n if (delta.tool_calls) update.toolCalls = delta.tool_calls;\n if (delta.reasoning_content) update.reasoningContent = delta.reasoning_content;\n\n if (update.content) accumulated.content += update.content;\n if (update.toolCalls) accumulated.toolCalls = update.toolCalls;\n if (update.reasoningContent) accumulated.reasoningContent = update.reasoningContent;\n\n onProgress({ ...accumulated });\n } catch (e) {\n console.warn('解析流式数据块失败:', e, dataLine);\n }\n }\n }\n\n // 流结束,最终消息\n const finalMessage = {\n role: 'assistant',\n content: accumulated.content,\n toolCalls: accumulated.toolCalls,\n reasoningContent: accumulated.reasoningContent\n };\n onDone(finalMessage);\n }\n};\n","/**\n * 工具调用插件\n * 拦截消息中的 tool_calls,执行注册的工具,并将结果插入对话,然后继续请求\n */\nexport const toolCallingPlugin = {\n name: 'tool-calling',\n maxIterations: 5, // 防止无限循环\n\n install(chatService) {\n this.chatService = chatService;\n this._tools = new Map(); // 工具名 -> { executor, definition }\n\n // 覆盖 registerTool 方法\n chatService.registerTool = (name, description, executor) => {\n this._tools.set(name, { executor, description });\n // 可选:同时生成工具定义,以便在请求中告知 AI\n // 这里简化,依赖适配器在请求中自动包含工具定义(如果需要)\n return chatService;\n };\n\n // 拦截发送流程:在发送前注入工具定义(如果需要)\n // 在流式和非流式响应后检测 tool_calls\n const originalSend = chatService.send;\n chatService.send = async (input) => {\n return this._handleWithTools(originalSend, input, false);\n };\n\n const originalStream = chatService.stream;\n chatService.stream = async (input, onProgress, onDone) => {\n return this._handleWithTools(originalStream, input, true, onProgress, onDone);\n };\n },\n\n async _handleWithTools(originalMethod, input, isStream, onProgress, onDone) {\n const self = this;\n const chat = this.chatService;\n let iteration = 0;\n\n // 包装回调,用于递归处理\n async function processResponse(initialResponse) {\n let lastResponse = initialResponse;\n while (iteration < self.maxIterations) {\n // 检查是否有工具调用\n const toolCalls = lastResponse?.toolCalls;\n if (!toolCalls || toolCalls.length === 0) break;\n\n iteration++;\n // 执行所有工具\n const toolResults = [];\n for (const call of toolCalls) {\n const toolName = call.function?.name;\n const args = JSON.parse(call.function?.arguments || '{}');\n const tool = self._tools.get(toolName);\n if (!tool) {\n console.warn(`未找到工具: ${toolName}`);\n toolResults.push({ tool_call_id: call.id, error: `工具 ${toolName} 未注册`, success: false });\n continue;\n }\n try {\n const result = await tool.executor(args);\n toolResults.push({\n tool_call_id: call.id,\n content: typeof result === 'string' ? result : JSON.stringify(result),\n success: true\n });\n } catch (err) {\n console.error(`工具 ${toolName} 执行失败:`, err);\n toolResults.push({ tool_call_id: call.id, error: err.message, success: false });\n }\n }\n\n // 添加工具结果消息\n for (const tr of toolResults) {\n chat.messages.addTool(tr.content || tr.error, tr.tool_call_id);\n }\n\n // 再次请求\n if (isStream) {\n // 流式重新请求:需要重新构建请求体\n const userMsg = chat.messages.getLast(); // 实际上应该重新生成请求\n // 这里简化:直接调用原 stream 方法,但需要避免循环\n // 实际实现需更细致,我们这里仅示意\n await new Promise((resolve) => {\n originalStream.call(chat, null, (chunk) => {\n if (onProgress) onProgress(chunk);\n lastResponse = chunk;\n }, (final) => {\n lastResponse = final;\n resolve();\n });\n });\n } else {\n const reqBody = chat._adapter.buildRequest(chat.messages.getAll(), chat.config);\n const apiResp = await chat._adapter.send(reqBody, chat.config);\n lastResponse = chat._adapter.parseResponse(apiResp);\n }\n }\n return lastResponse;\n }\n\n if (isStream) {\n // 流式模式\n let finalMessage;\n await originalMethod.call(chat, input,\n (chunk) => {\n if (onProgress) onProgress(chunk);\n },\n async (final) => {\n finalMessage = await processResponse(final);\n if (onDone) onDone(finalMessage);\n }\n );\n } else {\n const resp = await originalMethod.call(chat, input);\n const final = await processResponse(resp);\n return final;\n }\n }\n};\n","import { ChatService } from './core/ChatService.js';\nimport { MessageStore } from './core/MessageStore.js';\nimport { EventEmitter } from './core/EventEmitter.js';\nimport { openaiAdapter } from './adapters/openai.js';\nimport { toolCallingPlugin } from './plugins/tool-calling.js';\n\nexport { ChatService, MessageStore, EventEmitter, openaiAdapter, toolCallingPlugin };\n\nexport default {\n ChatService,\n MessageStore,\n EventEmitter,\n openaiAdapter,\n toolCallingPlugin\n};"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,54 +1,37 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "my-ai-chat-framework",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "A
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "A lightweight AI chat framework with plugin system, unified message format, and tool calling support.",
|
|
5
|
+
"type": "module",
|
|
5
6
|
"main": "dist/my-ai-chat-framework.node.cjs.js",
|
|
6
7
|
"module": "dist/my-ai-chat-framework.browser.es.js",
|
|
7
8
|
"browser": "dist/my-ai-chat-framework.browser.umd.js",
|
|
8
9
|
"exports": {
|
|
9
10
|
".": {
|
|
10
|
-
"node": "./dist/my-ai-chat-framework.node.cjs.js",
|
|
11
11
|
"import": "./dist/my-ai-chat-framework.browser.es.js",
|
|
12
12
|
"require": "./dist/my-ai-chat-framework.node.cjs.js",
|
|
13
|
-
"
|
|
13
|
+
"browser": "./dist/my-ai-chat-framework.browser.umd.js"
|
|
14
14
|
}
|
|
15
15
|
},
|
|
16
16
|
"scripts": {
|
|
17
|
+
"build": "vite build",
|
|
17
18
|
"dev": "vite",
|
|
18
|
-
"build:browser": "vite build --config vite.config.browser.js",
|
|
19
|
-
"build:node": "vite build --config vite.config.node.js",
|
|
20
|
-
"build": "npm run build:browser && npm run build:node",
|
|
21
19
|
"preview": "vite preview",
|
|
22
|
-
"
|
|
23
|
-
"version": "npm run build && git add -A dist",
|
|
24
|
-
"postversion": "git push && git push --tags"
|
|
20
|
+
"test": "node test.js"
|
|
25
21
|
},
|
|
26
22
|
"keywords": [
|
|
27
|
-
"
|
|
23
|
+
"ai",
|
|
28
24
|
"chat",
|
|
29
|
-
"framework",
|
|
30
|
-
"messages",
|
|
31
|
-
"API",
|
|
32
|
-
"deepseek",
|
|
33
25
|
"openai",
|
|
34
|
-
"
|
|
26
|
+
"deepseek",
|
|
27
|
+
"tool-calling",
|
|
28
|
+
"streaming",
|
|
29
|
+
"plugin"
|
|
35
30
|
],
|
|
36
|
-
"author": "
|
|
31
|
+
"author": "Your Name",
|
|
37
32
|
"license": "MIT",
|
|
38
|
-
"peerDependencies": {
|
|
39
|
-
"axios": ">=0.21.0"
|
|
40
|
-
},
|
|
41
|
-
"dependencies": {
|
|
42
|
-
"axios": "^0.21.4"
|
|
43
|
-
},
|
|
44
33
|
"devDependencies": {
|
|
45
|
-
"
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
"dist/",
|
|
49
|
-
"src/",
|
|
50
|
-
"README.md",
|
|
51
|
-
"README_ZH.md",
|
|
52
|
-
"LICENSE"
|
|
53
|
-
]
|
|
34
|
+
"dotenv": "^17.3.1",
|
|
35
|
+
"vite": "^8.0.2"
|
|
36
|
+
}
|
|
54
37
|
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenAI 兼容 API 适配器
|
|
3
|
+
* 支持 DeepSeek 等完全兼容 OpenAI 接口的服务
|
|
4
|
+
*/
|
|
5
|
+
export const openaiAdapter = {
|
|
6
|
+
name: 'openai',
|
|
7
|
+
|
|
8
|
+
install(chatService) {
|
|
9
|
+
// 将自身设置为适配器
|
|
10
|
+
chatService.setAdapter(this);
|
|
11
|
+
},
|
|
12
|
+
|
|
13
|
+
buildRequest(messages, config) {
|
|
14
|
+
// 将内部消息格式转为 OpenAI 格式
|
|
15
|
+
const apiMessages = messages.map(msg => {
|
|
16
|
+
const base = { role: msg.role, content: msg.content };
|
|
17
|
+
if (msg.role === 'assistant' && msg.toolCalls) {
|
|
18
|
+
base.tool_calls = msg.toolCalls;
|
|
19
|
+
}
|
|
20
|
+
if (msg.role === 'tool' && msg.toolCallId) {
|
|
21
|
+
base.tool_call_id = msg.toolCallId;
|
|
22
|
+
}
|
|
23
|
+
return base;
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
return {
|
|
27
|
+
model: config.model,
|
|
28
|
+
messages: apiMessages,
|
|
29
|
+
temperature: config.temperature ?? 0.7,
|
|
30
|
+
max_tokens: config.maxTokens ?? 2000,
|
|
31
|
+
stream: false // 流式请求会在 stream 方法中单独处理
|
|
32
|
+
};
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
async send(requestBody, config) {
|
|
36
|
+
const response = await fetch(config.apiUrl || 'https://api.openai.com/v1/chat/completions', {
|
|
37
|
+
method: 'POST',
|
|
38
|
+
headers: {
|
|
39
|
+
'Content-Type': 'application/json',
|
|
40
|
+
'Authorization': `Bearer ${config.apiKey}`
|
|
41
|
+
},
|
|
42
|
+
body: JSON.stringify(requestBody)
|
|
43
|
+
});
|
|
44
|
+
if (!response.ok) {
|
|
45
|
+
const error = await response.text();
|
|
46
|
+
throw new Error(`API 请求失败: ${response.status} ${error}`);
|
|
47
|
+
}
|
|
48
|
+
const data = await response.json();
|
|
49
|
+
return data;
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
parseResponse(apiResponse) {
|
|
53
|
+
const choice = apiResponse.choices[0];
|
|
54
|
+
const msg = choice.message;
|
|
55
|
+
// 转换为内部格式
|
|
56
|
+
const internal = {
|
|
57
|
+
role: msg.role,
|
|
58
|
+
content: msg.content || '',
|
|
59
|
+
};
|
|
60
|
+
if (msg.tool_calls) {
|
|
61
|
+
internal.toolCalls = msg.tool_calls;
|
|
62
|
+
}
|
|
63
|
+
if (msg.reasoning_content) {
|
|
64
|
+
internal.reasoningContent = msg.reasoning_content;
|
|
65
|
+
}
|
|
66
|
+
return internal;
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
async stream(requestBody, config, onProgress, onDone) {
|
|
70
|
+
// 流式请求需要设置 stream: true
|
|
71
|
+
const streamBody = { ...requestBody, stream: true };
|
|
72
|
+
const response = await fetch(config.apiUrl || 'https://api.openai.com/v1/chat/completions', {
|
|
73
|
+
method: 'POST',
|
|
74
|
+
headers: {
|
|
75
|
+
'Content-Type': 'application/json',
|
|
76
|
+
'Authorization': `Bearer ${config.apiKey}`,
|
|
77
|
+
'Accept': 'text/event-stream'
|
|
78
|
+
},
|
|
79
|
+
body: JSON.stringify(streamBody)
|
|
80
|
+
});
|
|
81
|
+
if (!response.ok) {
|
|
82
|
+
const error = await response.text();
|
|
83
|
+
throw new Error(`流式请求失败: ${response.status} ${error}`);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const reader = response.body.getReader();
|
|
87
|
+
const decoder = new TextDecoder();
|
|
88
|
+
let buffer = '';
|
|
89
|
+
let accumulated = { role: 'assistant', content: '' };
|
|
90
|
+
|
|
91
|
+
while (true) {
|
|
92
|
+
const { done, value } = await reader.read();
|
|
93
|
+
if (done) break;
|
|
94
|
+
buffer += decoder.decode(value, { stream: true });
|
|
95
|
+
const lines = buffer.split(/\n\n/);
|
|
96
|
+
buffer = lines.pop();
|
|
97
|
+
|
|
98
|
+
for (const line of lines) {
|
|
99
|
+
const dataLine = line.replace(/^data: /, '').trim();
|
|
100
|
+
if (!dataLine || dataLine === '[DONE]') continue;
|
|
101
|
+
try {
|
|
102
|
+
const chunk = JSON.parse(dataLine);
|
|
103
|
+
const delta = chunk.choices?.[0]?.delta;
|
|
104
|
+
if (!delta) continue;
|
|
105
|
+
|
|
106
|
+
const update = { role: delta.role || 'assistant', content: delta.content || '' };
|
|
107
|
+
if (delta.tool_calls) update.toolCalls = delta.tool_calls;
|
|
108
|
+
if (delta.reasoning_content) update.reasoningContent = delta.reasoning_content;
|
|
109
|
+
|
|
110
|
+
if (update.content) accumulated.content += update.content;
|
|
111
|
+
if (update.toolCalls) accumulated.toolCalls = update.toolCalls;
|
|
112
|
+
if (update.reasoningContent) accumulated.reasoningContent = update.reasoningContent;
|
|
113
|
+
|
|
114
|
+
onProgress({ ...accumulated });
|
|
115
|
+
} catch (e) {
|
|
116
|
+
console.warn('解析流式数据块失败:', e, dataLine);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// 流结束,最终消息
|
|
122
|
+
const finalMessage = {
|
|
123
|
+
role: 'assistant',
|
|
124
|
+
content: accumulated.content,
|
|
125
|
+
toolCalls: accumulated.toolCalls,
|
|
126
|
+
reasoningContent: accumulated.reasoningContent
|
|
127
|
+
};
|
|
128
|
+
onDone(finalMessage);
|
|
129
|
+
}
|
|
130
|
+
};
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { EventEmitter } from './EventEmitter.js';
|
|
2
|
+
import { MessageStore } from './MessageStore.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* 核心聊天服务
|
|
6
|
+
* 极简设计:只负责消息存储、事件发射、插件管理、请求委托
|
|
7
|
+
*/
|
|
8
|
+
export class ChatService extends EventEmitter {
|
|
9
|
+
constructor(config = {}) {
|
|
10
|
+
super();
|
|
11
|
+
this.config = {
|
|
12
|
+
apiKey: config.apiKey || '',
|
|
13
|
+
model: config.model || 'gpt-3.5-turbo',
|
|
14
|
+
...config
|
|
15
|
+
};
|
|
16
|
+
this.messages = new MessageStore();
|
|
17
|
+
this._plugins = [];
|
|
18
|
+
this._adapter = null; // 当前使用的 API 适配器
|
|
19
|
+
this._pendingRequest = null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* 加载插件
|
|
24
|
+
* @param {object} plugin - 必须包含 install 方法
|
|
25
|
+
*/
|
|
26
|
+
use(plugin) {
|
|
27
|
+
if (typeof plugin.install === 'function') {
|
|
28
|
+
plugin.install(this);
|
|
29
|
+
this._plugins.push(plugin);
|
|
30
|
+
} else {
|
|
31
|
+
throw new Error('插件必须提供 install 方法');
|
|
32
|
+
}
|
|
33
|
+
return this;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* 设置 API 适配器
|
|
38
|
+
*/
|
|
39
|
+
setAdapter(adapter) {
|
|
40
|
+
this._adapter = adapter;
|
|
41
|
+
return this;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* 发送消息(非流式)
|
|
46
|
+
* @param {string|object} input - 字符串或消息对象
|
|
47
|
+
*/
|
|
48
|
+
async send(input) {
|
|
49
|
+
const userMsg = typeof input === 'string'
|
|
50
|
+
? { role: 'user', content: input }
|
|
51
|
+
: { role: 'user', ...input };
|
|
52
|
+
|
|
53
|
+
this.messages.add(userMsg);
|
|
54
|
+
this.emit('sending', userMsg);
|
|
55
|
+
|
|
56
|
+
// 构建请求
|
|
57
|
+
const requestBody = this._adapter.buildRequest(this.messages.getAll(), this.config);
|
|
58
|
+
const response = await this._adapter.send(requestBody, this.config);
|
|
59
|
+
const assistantMsg = this._adapter.parseResponse(response);
|
|
60
|
+
|
|
61
|
+
this.messages.add(assistantMsg);
|
|
62
|
+
this.emit('message', assistantMsg);
|
|
63
|
+
return assistantMsg;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* 流式发送消息
|
|
68
|
+
* @param {string|object} input
|
|
69
|
+
* @param {function} onProgress - 每次收到数据块时调用,参数为累积的当前消息对象
|
|
70
|
+
* @param {function} onDone - 完成时调用,参数为最终消息对象
|
|
71
|
+
*/
|
|
72
|
+
async stream(input, onProgress, onDone) {
|
|
73
|
+
const userMsg = typeof input === 'string'
|
|
74
|
+
? { role: 'user', content: input }
|
|
75
|
+
: { role: 'user', ...input };
|
|
76
|
+
|
|
77
|
+
this.messages.add(userMsg);
|
|
78
|
+
this.emit('sending', userMsg);
|
|
79
|
+
|
|
80
|
+
const requestBody = this._adapter.buildRequest(this.messages.getAll(), this.config);
|
|
81
|
+
|
|
82
|
+
let accumulated = { role: 'assistant', content: '' };
|
|
83
|
+
await this._adapter.stream(
|
|
84
|
+
requestBody,
|
|
85
|
+
this.config,
|
|
86
|
+
(chunk) => {
|
|
87
|
+
// 累积消息
|
|
88
|
+
if (chunk.content) accumulated.content += chunk.content;
|
|
89
|
+
if (chunk.toolCalls) accumulated.toolCalls = chunk.toolCalls;
|
|
90
|
+
if (chunk.reasoningContent) accumulated.reasoningContent = chunk.reasoningContent;
|
|
91
|
+
// 触发进度事件
|
|
92
|
+
this.emit('stream-progress', { ...accumulated });
|
|
93
|
+
if (onProgress) onProgress({ ...accumulated });
|
|
94
|
+
},
|
|
95
|
+
(finalMessage) => {
|
|
96
|
+
this.messages.add(finalMessage);
|
|
97
|
+
this.emit('message', finalMessage);
|
|
98
|
+
if (onDone) onDone(finalMessage);
|
|
99
|
+
}
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* 注册工具(由工具调用插件实现)
|
|
105
|
+
* 这里只留一个空方法,插件会覆盖它
|
|
106
|
+
*/
|
|
107
|
+
registerTool(name, description, executor) {
|
|
108
|
+
throw new Error('工具调用插件未加载,请先使用 use(toolCallingPlugin)');
|
|
109
|
+
}
|
|
110
|
+
}
|