doomiaichat
Version:
Doomisoft OpenAI
409 lines (408 loc) • 21.3 kB
JavaScript
"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;