UNPKG

doomiaichat

Version:

Doomisoft OpenAI

409 lines (408 loc) 21.3 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __asyncValues = (this && this.__asyncValues) || function (o) { if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); var m = o[Symbol.asyncIterator], i; return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i); function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; } function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); } }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); /** * 扣子智能体 */ const api_1 = require("@coze/api"); const gptbase_1 = __importDefault(require("./gptbase")); const querystring_1 = require("querystring"); // 定义深度思考的动作标签 // 这是小鹭里面专用的几个思考动作标签 const DeepThinkingAction = { thinking: { start: '<xiaolu-think>', end: '</xiaolu-think>', tag: 'xiaolu-think' }, reasoning: { start: '<xiaolu-reason>', end: '</xiaolu-reason>', tag: 'xiaolu-reason' }, card: { start: '<xiaolu-card>', end: '</xiaolu-card>', tag: 'xiaolu-card' }, }; // 定义深度思考的状态 var DeepThinkingStatus; (function (DeepThinkingStatus) { DeepThinkingStatus[DeepThinkingStatus["None"] = 0] = "None"; DeepThinkingStatus[DeepThinkingStatus["Thinking"] = 1] = "Thinking"; // ContentOutput, DeepThinkingStatus[DeepThinkingStatus["ThinkingOver"] = 2] = "ThinkingOver"; })(DeepThinkingStatus || (DeepThinkingStatus = {})); // type TThinkingMessage = { action: string, textposition: number } class CorzBot extends gptbase_1.default { /** * * @param apikey 调用AI中台 的key * @param botid 智能体信息 */ constructor(authorizationProvider, setting = {}) { super(); this.authorizationProvider = authorizationProvider; this.setting = setting; this.botid = null; // 智能体id this.workflowid = null; // 工作流id this.talkflowid = null; // 对话流id ////初始化扣子客户端 if (this.setting.workflowid) this.workflowid = this.setting.workflowid; else if (this.setting.talkflowid) this.talkflowid = this.setting.talkflowid; else if (this.setting.botid || this.setting.botID) this.botid = this.setting.botid || this.setting.botID; else throw new Error('no botid or talkflowid or workflowid setting for coze'); this.apiKey = this.setting['apiKey']; } createClient() { return __awaiter(this, void 0, void 0, function* () { // 存在AppKey则使用AppKey进行认证 if (this.apiKey) return new api_1.CozeAPI({ baseURL: api_1.COZE_CN_BASE_URL, token: this.apiKey }); const accessToken = yield this.authorizationProvider.getAccessToken(); if (!accessToken) throw new Error('get access token failed'); return new api_1.CozeAPI({ baseURL: api_1.COZE_CN_BASE_URL, token: accessToken }); }); } /** * 发起一次会话 */ createCoversation(client) { return __awaiter(this, void 0, void 0, function* () { try { const czApi = client !== null && client !== void 0 ? client : yield this.createClient(); const params = this.botid ? { bot_id: this.botid } : {}; const result = yield czApi.conversations.create(params); return result.id; } catch (error) { console.error('createCoversation error in coze api'); return null; } }); } /** * 设置Coze的变量 * @param params * @returns */ setVariables(params) { return __awaiter(this, void 0, void 0, function* () { const client = yield this.createClient(); const cozeParams = Object.assign({}, this.botid ? { bot_id: this.botid } : {}, params); client.variables.update(cozeParams); return { successed: true }; }); } /** * 获取设置的变量 * @returns */ getVariables(params) { return __awaiter(this, void 0, void 0, function* () { const client = yield this.createClient(); const cozeParams = Object.assign({}, { bot_id: this.botid }, params); const data = yield client.variables.retrieve(cozeParams); if (data) return Object.assign({ successed: true }, data); return { successed: false, error: 'get variables failed' }; }); } /** * 组装请求参数 * @param message * @param callChatOption * @param attach * @param _axiosOption */ getRequestStream(client, message, callChatOption = {}) { var _a, _b; return __awaiter(this, void 0, void 0, function* () { //简单的对话请求 const conversation_id = (_a = callChatOption.session_id) !== null && _a !== void 0 ? _a : yield this.createCoversation(client); if (this.botid) { const req = { bot_id: this.botid, additional_messages: message, user_id: callChatOption.userid || callChatOption.cozeUserID, conversation_id, parameters: Object.assign({ request_src: 1 }, callChatOption.parameters || {}), }; req.custom_variables = Object.assign({}, this.setting.customVariables || {}, callChatOption.customVariables || {}); return req; } if (this.workflowid) { const worflowreq = { ext: callChatOption.ext, workflow_id: this.workflowid, is_async: false, // parameters: Object.assign({ input: message[0]?.content }, this.setting.customVariables || {}, callChatOption.customVariables || {}), parameters: Object.assign({ request_src: 1 }, callChatOption.parameters || {}, { input: (_b = message[0]) === null || _b === void 0 ? void 0 : _b.content }) }; return worflowreq; } const worflowreq = { additional_messages: message, ext: callChatOption.ext, workflow_id: this.talkflowid, conversation_id, parameters: Object.assign({ request_src: 1 }, callChatOption.parameters || {}) //Object.assign({}, this.setting.customVariables || {}, callChatOption.customVariables || {}), //parameters: { input: message[0]?.content } }; return worflowreq; }); } /** * 同步调用的问答请求需要获取会话详情 * @param chatid * @param conversation_id */ getChatDetail(client, conversation_id, chatid) { return __awaiter(this, void 0, void 0, function* () { const maxWaitTime = 120000; // 最大等待120秒 const startTime = Date.now(); while ((Date.now() - startTime) < maxWaitTime) { const chatinfo = yield client.chat.retrieve(conversation_id, chatid); // 状态已经是完成了,可以去获取对话的内容了 if (chatinfo.status === api_1.ChatStatus.COMPLETED) return { usage: chatinfo.usage, messages: yield client.chat.messages.list(conversation_id, chatid) }; yield new Promise(resolve => setTimeout(resolve, 1500)); // 等待1500ms } return null; }); } /** * 非流式传输聊天请求 * @param chatText * @param callChatOption * @param _axiosOption */ chatRequest(message, callChatOption = {}, _axiosOption = {}) { return __awaiter(this, void 0, void 0, function* () { if (!message) this.emit('chaterror', { successed: false, error: 'no message in chat' }); ///如果是字符,则组装成API需要的消息 if (typeof message === 'string') message = [ { role: api_1.RoleType.User, content_type: 'text', content: message } ]; const client = yield this.createClient(); ////如果参数中用的是Workflow,则调用对话流来输出结果 ////否则用智能体的对话来输出结果 const params = yield this.getRequestStream(client, message, callChatOption); const response = this.botid ? yield client.chat.create(params) : yield client.workflows.runs.create(params); if (this.workflowid && response.msg === 'Success') { const resp = response.data; return { successed: true, message: [{ role: 'assistant', type: 'answer', content: resp }] }; // try { // return { successed: true, message: [{ role: 'assistant', type: 'answer', content: JSON.parse(resp).data }] }; // } catch (error) { // return { successed: true, message: [{ role: 'assistant', type: 'answer', content: resp }] }; // } } if (!this.workflowid && response.conversation_id && response.id) { const ccd = response; const chatData = yield this.getChatDetail(client, ccd.conversation_id, ccd.id); if (chatData) { const message = chatData.messages.filter(x => x.type === 'answer').map(item => ({ role: item.role, type: item.type, content: item.content, })); return { successed: true, message, usage: chatData.usage, session_id: ccd.conversation_id }; } } return { successed: false, error: { message: '聊天未完成' } }; }); } /** * 提取XML标签中间的内容, * @param xmlStr * @param tagName * @returns */ extractXmlContent(xmlStr, tagName) { // 构建匹配标签的正则(支持任意空白字符和大小写) const regex = new RegExp(`<\\s*${tagName}\\s*>([\\s\\S]*?)<\\s*\\/\\s*${tagName}\\s*>`, 'i'); const match = xmlStr.match(regex); if (match && match[1]) return match[1].trim(); return ''; // 未找到标签或内容 } /** * 解析深度思考的JSON内容 * @param content * @param status * @returns */ parseDeepThinkingJson(content) { // const thinkingStartIndex = status === DeepThinkingStatus.Thinking ? content.indexOf("{\"process_msg") : // (status===DeepThinkingStatus.ReasonOutput ? content.indexOf("{\"reasoning_content") : content.indexOf("{\"card_resource")) ; const xmlTagLocation = [content.indexOf(DeepThinkingAction.thinking.start), content.indexOf(DeepThinkingAction.reasoning.start), content.indexOf(DeepThinkingAction.card.start)]; let minLocation = 10000, minIndex = -1; // 找到最小的并且大于0 的位置的下标 xmlTagLocation.forEach((x, index) => { if (x >= 0 && x < minLocation) { minLocation = x; minIndex = index; } }); // const tagLocation = xmlTagLocation.filter(x => x >= 0); const thinkingStartIndex = minIndex >= 0 ? minLocation : -1; //tagLocation.length > 0 ? Math.min(...tagLocation) : -1; if (thinkingStartIndex < 0) return { content, status: DeepThinkingStatus.ThinkingOver }; const currentAction = [DeepThinkingAction.thinking, DeepThinkingAction.reasoning, DeepThinkingAction.card][minIndex]; const currentActionIsOver = content.indexOf(currentAction.end, thinkingStartIndex); const thinkingEndIndex = currentActionIsOver >= 0 ? currentActionIsOver : content.indexOf('</', thinkingStartIndex); const thinkingContent = this.extractXmlContent(content.substring(thinkingStartIndex, thinkingEndIndex >= 0 ? thinkingEndIndex : undefined) + currentAction.end, currentAction.tag); //"\"}"; if (currentActionIsOver >= 0) content = content.substring(currentActionIsOver + currentAction.end.length); return { thinking: { action: currentAction, text: thinkingContent, completed: currentActionIsOver >= 0 }, content }; } /** * 流式传输聊天请求 * @param chatText * @param callChatOption * @param attach * @param _axiosOption * @returns */ chatRequestInStream(message, callChatOption = {}, attach, _axiosOption) { var _a, e_1, _b, _c; var _d, _e, _f; return __awaiter(this, void 0, void 0, function* () { if (!message) this.emit('chaterror', { successed: false, error: 'no message in chat' }); ///如果是字符,则组装成API需要的消息 if (typeof message === 'string') message = [ { role: api_1.RoleType.User, content_type: 'text', content: message } ]; const client = yield this.createClient(); ////如果参数中用的是Workflow,则调用对话流来输出结果 ////否则用智能体的对话来输出结果 let requestid = Math.ceil(Math.random() * (new Date().getTime() * Math.random()) / 1000), index = 0; const params = yield this.getRequestStream(client, message, callChatOption); const stream = this.botid ? client.chat.stream(params) : (this.workflowid ? client.workflows.runs.stream(params) : client.workflows.chat.stream(params)); let deltaindex = 0, fullanswer = [], followup = [], cardData = []; let deepThinking = '', thinkingStatus = DeepThinkingStatus.None, cardResource = ''; // 是否在深度思考中 try { for (var _g = true, stream_1 = __asyncValues(stream), stream_1_1; stream_1_1 = yield stream_1.next(), _a = stream_1_1.done, !_a;) { _c = stream_1_1.value; _g = false; try { const part = _c; if (part.event === api_1.ChatEventType.ERROR) return this.emit('chaterror', { successed: false, error: 'call failed' }); if (part.event === api_1.ChatEventType.CONVERSATION_MESSAGE_DELTA || part.event === api_1.WorkflowEventType.MESSAGE) { let { conversation_id, content_type, type = 'answer', role = 'assistant', content, reasoning_content: reasoning } = part.data; if (!content && reasoning) { this.emit('chatthinking', { text: reasoning, completed: false, action: 'deep-thinking' }); continue; } // 如果存在深度思考,则一开始就会带有相关的关键信息 if (deltaindex === 0 && content.startsWith("<xiaolu-")) { thinkingStatus = DeepThinkingStatus.Thinking; deltaindex++; } // 如果在深度思考中,则不输出消息 if (thinkingStatus === DeepThinkingStatus.Thinking) { deepThinking += content; const result = this.parseDeepThinkingJson(deepThinking); const thinking = result.thinking; if (thinking) { deepThinking = result.content; if (thinking.action === DeepThinkingAction.card) { cardResource += result.thinking.text; // 卡片流结束,解析卡片资源数据 if (result.thinking.completed) { const allCards = cardResource.replace(/[\x00-\x1F\x7F]/g, '').split('|'); for (const item of allCards) { const cardinfo = (0, querystring_1.parse)(item); if (cardinfo.type && cardinfo.tag) cardData.push(Object.assign({}, cardinfo)); } // 将卡片资源返回给客户端 this.emit('chatcard', cardData); } } else { this.emit('chatthinking', { text: result.thinking.text, completed: result.thinking.completed, action: (_d = thinking.action) === null || _d === void 0 ? void 0 : _d.tag }); } } if (result.status != DeepThinkingStatus.ThinkingOver) continue; thinkingStatus = DeepThinkingStatus.ThinkingOver; // 将排除了thinking之后的消息内容要带下去成为正式的输出内容 content = deepThinking; } fullanswer.push(content); let output = { successed: true, type, content_type, role, requestid, segment: content, text: fullanswer.join(''), index: index++, session_id: conversation_id }; if (attach) output = Object.assign({}, output, attach); this.emit('chattext', output); } ////在流式传输中,提取相关推荐问题 if (part.event === api_1.ChatEventType.CONVERSATION_MESSAGE_COMPLETED) { const { type, content } = (_e = part.data) !== null && _e !== void 0 ? _e : {}; if (type === 'follow_up') followup.push(content); } ///整个对话结束 if (part.event === api_1.ChatEventType.CONVERSATION_CHAT_COMPLETED || part.event === api_1.WorkflowEventType.DONE) { const { conversation_id, content } = (_f = (part.data)) !== null && _f !== void 0 ? _f : {}; let output = { successed: true, cards: cardData.length ? cardData : null, followup, type: 'answer', content_type: 'text', role: api_1.RoleType.Assistant, requestid, segment: null, text: content !== null && content !== void 0 ? content : fullanswer.join(''), index: index++, session_id: conversation_id }; if (attach) output = Object.assign({}, output, attach); this.emit('chatdone', output); } } finally { _g = true; } } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (!_g && !_a && (_b = stream_1.return)) yield _b.call(stream_1); } finally { if (e_1) throw e_1.error; } } return { successed: true, requestid }; }); } } exports.default = CorzBot;