doomiaichat 7.1.10 → 7.1.12

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/dist/corzbot.d.ts CHANGED
@@ -8,10 +8,10 @@ import { ApiResult } from './declare';
8
8
  export default class CorzBot extends GptBase {
9
9
  private authorizationProvider;
10
10
  private setting;
11
- protected botid: string;
12
- protected workflowid: string;
11
+ private botid;
12
+ private workflowid;
13
+ private talkflowid;
13
14
  private apiKey;
14
- private lastThinkingMessage;
15
15
  /**
16
16
  *
17
17
  * @param apikey 调用AI中台 的key
@@ -23,6 +23,11 @@ export default class CorzBot extends GptBase {
23
23
  * 发起一次会话
24
24
  */
25
25
  createCoversation(client?: CozeAPI): Promise<string | null>;
26
+ /**
27
+ * 设置Coze的变量
28
+ * @param params
29
+ * @returns
30
+ */
26
31
  setVariables(params: any): Promise<ApiResult>;
27
32
  /**
28
33
  * 获取设置的变量
@@ -50,6 +55,13 @@ export default class CorzBot extends GptBase {
50
55
  * @param _axiosOption
51
56
  */
52
57
  chatRequest(message: any[] | string, callChatOption?: any, _axiosOption?: any): Promise<any>;
58
+ /**
59
+ * 提取XML标签中间的内容,
60
+ * @param xmlStr
61
+ * @param tagName
62
+ * @returns
63
+ */
64
+ extractXmlContent(xmlStr: string, tagName: string): string;
53
65
  /**
54
66
  * 解析深度思考的JSON内容
55
67
  * @param content
package/dist/corzbot.js CHANGED
@@ -25,23 +25,23 @@ Object.defineProperty(exports, "__esModule", { value: true });
25
25
  const api_1 = require("@coze/api");
26
26
  const gptbase_1 = __importDefault(require("./gptbase"));
27
27
  const querystring_1 = require("querystring");
28
- // 定义深度思考的动作
29
- var DeepThinkingAction;
30
- (function (DeepThinkingAction) {
31
- DeepThinkingAction["process"] = "process_msg";
32
- DeepThinkingAction["content"] = "reasoning_content";
33
- DeepThinkingAction["card"] = "card_resource";
34
- })(DeepThinkingAction || (DeepThinkingAction = {}));
28
+ // 定义深度思考的动作标签
29
+ // 这是小鹭里面专用的几个思考动作标签
30
+ const DeepThinkingAction = {
31
+ thinking: { start: '<xiaolu-think>', end: '</xiaolu-think>', tag: 'xiaolu-think' },
32
+ reasoning: { start: '<xiaolu-reason>', end: '</xiaolu-reason>', tag: 'xiaolu-reason' },
33
+ card: { start: '<xiaolu-card>', end: '</xiaolu-card>', tag: 'xiaolu-card' },
34
+ };
35
35
  // 定义深度思考的状态
36
36
  var DeepThinkingStatus;
37
37
  (function (DeepThinkingStatus) {
38
38
  DeepThinkingStatus[DeepThinkingStatus["None"] = 0] = "None";
39
39
  DeepThinkingStatus[DeepThinkingStatus["Thinking"] = 1] = "Thinking";
40
- DeepThinkingStatus[DeepThinkingStatus["ContentOutput"] = 2] = "ContentOutput";
41
- DeepThinkingStatus[DeepThinkingStatus["ThinkingOver"] = 3] = "ThinkingOver";
40
+ // ContentOutput,
41
+ DeepThinkingStatus[DeepThinkingStatus["ThinkingOver"] = 2] = "ThinkingOver";
42
42
  })(DeepThinkingStatus || (DeepThinkingStatus = {}));
43
+ // type TThinkingMessage = { action: string, textposition: number }
43
44
  class CorzBot extends gptbase_1.default {
44
- // protected client: CozeAPI;
45
45
  /**
46
46
  *
47
47
  * @param apikey 调用AI中台 的key
@@ -51,10 +51,18 @@ class CorzBot extends gptbase_1.default {
51
51
  super();
52
52
  this.authorizationProvider = authorizationProvider;
53
53
  this.setting = setting;
54
- this.lastThinkingMessage = { action: '', textposition: 0 };
54
+ this.botid = null; // 智能体id
55
+ this.workflowid = null; // 工作流id
56
+ this.talkflowid = null; // 对话流id
55
57
  ////初始化扣子客户端
56
- this.botid = this.setting['botid'] || this.setting['botID'];
57
- this.workflowid = this.setting['workflowid'];
58
+ if (this.setting.workflowid)
59
+ this.workflowid = this.setting.workflowid;
60
+ else if (this.setting.talkflowid)
61
+ this.talkflowid = this.setting.talkflowid;
62
+ else if (this.setting.botid || this.setting.botID)
63
+ this.botid = this.setting.botid || this.setting.botID;
64
+ else
65
+ throw new Error('no botid or talkflowid or workflowid setting for coze');
58
66
  this.apiKey = this.setting['apiKey'];
59
67
  }
60
68
  createClient() {
@@ -85,6 +93,11 @@ class CorzBot extends gptbase_1.default {
85
93
  }
86
94
  });
87
95
  }
96
+ /**
97
+ * 设置Coze的变量
98
+ * @param params
99
+ * @returns
100
+ */
88
101
  setVariables(params) {
89
102
  return __awaiter(this, void 0, void 0, function* () {
90
103
  const client = yield this.createClient();
@@ -119,24 +132,33 @@ class CorzBot extends gptbase_1.default {
119
132
  return __awaiter(this, void 0, void 0, function* () {
120
133
  //简单的对话请求
121
134
  const conversation_id = (_a = callChatOption.session_id) !== null && _a !== void 0 ? _a : yield this.createCoversation(client);
122
- if (!this.workflowid) {
135
+ if (this.botid) {
123
136
  const req = {
124
137
  bot_id: this.botid,
125
138
  additional_messages: message,
126
139
  user_id: callChatOption.userid || callChatOption.cozeUserID,
127
- conversation_id
140
+ conversation_id,
141
+ parameters: Object.assign({ request_src: 1 }, callChatOption.parameters || {}),
128
142
  };
129
143
  req.custom_variables = Object.assign({}, this.setting.customVariables || {}, callChatOption.customVariables || {});
130
144
  return req;
131
145
  }
146
+ if (this.workflowid) {
147
+ const worflowreq = {
148
+ ext: callChatOption.ext,
149
+ workflow_id: this.workflowid,
150
+ is_async: false,
151
+ // parameters: Object.assign({ input: message[0]?.content }, this.setting.customVariables || {}, callChatOption.customVariables || {}),
152
+ parameters: Object.assign({ request_src: 1 }, callChatOption.parameters || {}, { input: (_b = message[0]) === null || _b === void 0 ? void 0 : _b.content })
153
+ };
154
+ return worflowreq;
155
+ }
132
156
  const worflowreq = {
133
- bot_id: this.botid,
134
157
  additional_messages: message,
135
158
  ext: callChatOption.ext,
136
- workflow_id: this.workflowid,
159
+ workflow_id: this.talkflowid,
137
160
  conversation_id,
138
- is_async: false,
139
- parameters: Object.assign({ input: (_b = message[0]) === null || _b === void 0 ? void 0 : _b.content }, this.setting.customVariables || {}, callChatOption.customVariables || {}),
161
+ parameters: Object.assign({ request_src: 1 }, callChatOption.parameters || {}) //Object.assign({}, this.setting.customVariables || {}, callChatOption.customVariables || {}),
140
162
  //parameters: { input: message[0]?.content }
141
163
  };
142
164
  return worflowreq;
@@ -184,15 +206,15 @@ class CorzBot extends gptbase_1.default {
184
206
  ////如果参数中用的是Workflow,则调用对话流来输出结果
185
207
  ////否则用智能体的对话来输出结果
186
208
  const params = yield this.getRequestStream(client, message, callChatOption);
187
- const response = !this.workflowid ? yield client.chat.create(params) : yield client.workflows.runs.create(params);
209
+ const response = this.botid ? yield client.chat.create(params) : yield client.workflows.runs.create(params);
188
210
  if (this.workflowid && response.msg === 'Success') {
189
211
  const resp = response.data;
190
- try {
191
- return { successed: true, message: [{ role: 'assistant', type: 'answer', content: JSON.parse(resp).data }] };
192
- }
193
- catch (error) {
194
- return { successed: true, message: [{ role: 'assistant', type: 'answer', content: resp }] };
195
- }
212
+ return { successed: true, message: [{ role: 'assistant', type: 'answer', content: resp }] };
213
+ // try {
214
+ // return { successed: true, message: [{ role: 'assistant', type: 'answer', content: JSON.parse(resp).data }] };
215
+ // } catch (error) {
216
+ // return { successed: true, message: [{ role: 'assistant', type: 'answer', content: resp }] };
217
+ // }
196
218
  }
197
219
  if (!this.workflowid && response.conversation_id && response.id) {
198
220
  const ccd = response;
@@ -209,68 +231,57 @@ class CorzBot extends gptbase_1.default {
209
231
  return { successed: false, error: { message: '聊天未完成' } };
210
232
  });
211
233
  }
234
+ /**
235
+ * 提取XML标签中间的内容,
236
+ * @param xmlStr
237
+ * @param tagName
238
+ * @returns
239
+ */
240
+ extractXmlContent(xmlStr, tagName) {
241
+ // 构建匹配标签的正则(支持任意空白字符和大小写)
242
+ const regex = new RegExp(`<\\s*${tagName}\\s*>([\\s\\S]*?)<\\s*\\/\\s*${tagName}\\s*>`, 'i');
243
+ const match = xmlStr.match(regex);
244
+ if (match && match[1])
245
+ return match[1].trim();
246
+ return ''; // 未找到标签或内容
247
+ }
212
248
  /**
213
249
  * 解析深度思考的JSON内容
214
250
  * @param content
215
251
  * @param status
216
252
  * @returns
217
253
  */
218
- parseDeepThinkingJson(content, status) {
254
+ parseDeepThinkingJson(content) {
219
255
  // const thinkingStartIndex = status === DeepThinkingStatus.Thinking ? content.indexOf("{\"process_msg") :
220
256
  // (status===DeepThinkingStatus.ReasonOutput ? content.indexOf("{\"reasoning_content") : content.indexOf("{\"card_resource")) ;
221
- const tagLocation = [content.indexOf("{\"process_msg"),
222
- content.indexOf("{\"reasoning_content"),
223
- content.indexOf("{\"card_resource")].filter(x => x >= 0);
224
- const thinkingStartIndex = tagLocation.length > 0 ? Math.min(...tagLocation) : -1;
225
- const thinkingEndIndex = content.indexOf("\"}");
226
- // 如果没有找到相关的JSON内容,则直接返回
227
- if (thinkingStartIndex < 0)
228
- return { successed: true, content, status: DeepThinkingStatus.ThinkingOver };
229
- let jsonStr = null;
230
- try {
231
- jsonStr = content.substring(thinkingStartIndex, thinkingEndIndex >= 0 ? thinkingEndIndex : undefined) + "\"}";
232
- // 转换JSON的时候需要将非法字符过滤掉
233
- const thinkingObject = JSON.parse(jsonStr.replace(/[\x00-\x1F\x7F]/g, ''));
234
- const thinkingAction = Object.keys(thinkingObject)[0];
235
- // 需要将内容的原文返回
236
- const originalContent = jsonStr.split('":"')[1];
237
- // 如果正确的解析JSON,并且当前有包含JSON结束的字符,则把当前思考的JSON内容替换掉
238
- if (thinkingEndIndex >= 0)
239
- content = content.substring(thinkingEndIndex + 2);
240
- // 如果正常输出了卡片资源,并且json结束 card_resource 只有一个,所以一旦结束,则整个思考过程完毕
241
- // if (status === DeepThinkingStatus.ContentOutput && thinkingEndIndex >= 0) status = DeepThinkingStatus.ThinkingOver;
242
- // 判断整个思考过程是否到达等待卡片资源(有可能智能体输出不了卡片资源) reasoning_content 只有一个,所以一旦结束,则进入下一个阶段 card_resource
243
- //if (status === DeepThinkingStatus.ContentOutput && thinkingEndIndex >= 0) status = DeepThinkingStatus.ContentOutput;
244
- // 判断当前是否从前期思考切换到思考内容输出
245
- if (status === DeepThinkingStatus.Thinking && thinkingAction !== DeepThinkingAction.process)
246
- status = DeepThinkingStatus.ContentOutput;
247
- //if (status === DeepThinkingStatus.Thinking && content.indexOf("{\"reasoning_content") >= 0) status=DeepThinkingStatus.ReasonOutput;
248
- //const removeOutputContent = content.replace(originalContent!.substring(1, originalContent!.length - 2),"")
249
- let thinkingContent = originalContent === null || originalContent === void 0 ? void 0 : originalContent.substring(0, originalContent.length - 2);
250
- const outputLength = (thinkingContent === null || thinkingContent === void 0 ? void 0 : thinkingContent.length) || 0;
251
- // 用lastThinkingMessage来记录上一次的思考内容,避免重复输出内容,导致的网络传输内容过多
252
- if (this.lastThinkingMessage.action === thinkingAction && this.lastThinkingMessage.textposition) {
253
- thinkingContent = thinkingContent.substring(this.lastThinkingMessage.textposition);
254
- }
255
- this.lastThinkingMessage = thinkingEndIndex >= 0 ? { action: thinkingAction, textposition: 0 } : { action: thinkingAction, textposition: outputLength };
256
- return {
257
- successed: true, thinking: {
258
- action: thinkingAction,
259
- text: thinkingContent,
260
- completed: thinkingEndIndex >= 0
261
- }, content, status
262
- };
263
- }
264
- catch (error) {
265
- // 如果在content之后发生了错误,可能该智能体输出不了卡片类型的数据,则输出的内容已经是正文了
266
- if (status === DeepThinkingStatus.ContentOutput) {
267
- status = DeepThinkingStatus.ThinkingOver;
268
- return { successed: true, content, status };
257
+ const xmlTagLocation = [content.indexOf(DeepThinkingAction.thinking.start),
258
+ content.indexOf(DeepThinkingAction.reasoning.start),
259
+ content.indexOf(DeepThinkingAction.card.start)];
260
+ let minLocation = 10000, minIndex = -1;
261
+ // 找到最小的并且大于0 的位置的下标
262
+ xmlTagLocation.forEach((x, index) => {
263
+ if (x >= 0 && x < minLocation) {
264
+ minLocation = x;
265
+ minIndex = index;
269
266
  }
270
- console.error('解析JSON出错了:', jsonStr, status);
271
- // 当前解析出错,等待下一次解析
272
- return { successed: false, content, status };
273
- }
267
+ });
268
+ // const tagLocation = xmlTagLocation.filter(x => x >= 0);
269
+ const thinkingStartIndex = minIndex >= 0 ? minLocation : -1; //tagLocation.length > 0 ? Math.min(...tagLocation) : -1;
270
+ if (thinkingStartIndex < 0)
271
+ return { content, status: DeepThinkingStatus.ThinkingOver };
272
+ const currentAction = [DeepThinkingAction.thinking, DeepThinkingAction.reasoning, DeepThinkingAction.card][minIndex];
273
+ const currentActionIsOver = content.indexOf(currentAction.end, thinkingStartIndex);
274
+ const thinkingEndIndex = currentActionIsOver >= 0 ? currentActionIsOver : content.indexOf('</', thinkingStartIndex);
275
+ const thinkingContent = this.extractXmlContent(content.substring(thinkingStartIndex, thinkingEndIndex >= 0 ? thinkingEndIndex : undefined) + currentAction.end, currentAction.tag); //"\"}";
276
+ if (currentActionIsOver >= 0)
277
+ content = content.substring(currentActionIsOver + currentAction.end.length);
278
+ return {
279
+ thinking: {
280
+ action: currentAction,
281
+ text: thinkingContent,
282
+ completed: currentActionIsOver >= 0
283
+ }, content
284
+ };
274
285
  }
275
286
  /**
276
287
  * 流式传输聊天请求
@@ -282,6 +293,7 @@ class CorzBot extends gptbase_1.default {
282
293
  */
283
294
  chatRequestInStream(message, callChatOption = {}, attach, _axiosOption) {
284
295
  var _a, e_1, _b, _c;
296
+ var _d, _e, _f;
285
297
  return __awaiter(this, void 0, void 0, function* () {
286
298
  if (!message)
287
299
  this.emit('chaterror', { successed: false, error: 'no message in chat' });
@@ -299,56 +311,59 @@ class CorzBot extends gptbase_1.default {
299
311
  ////否则用智能体的对话来输出结果
300
312
  let requestid = Math.ceil(Math.random() * (new Date().getTime() * Math.random()) / 1000), index = 0;
301
313
  const params = yield this.getRequestStream(client, message, callChatOption);
302
- const stream = !this.workflowid ? client.chat.stream(params) : client.workflows.runs.stream(params);
314
+ const stream = this.botid ? client.chat.stream(params) :
315
+ (this.workflowid ? client.workflows.runs.stream(params) :
316
+ client.workflows.chat.stream(params));
303
317
  let deltaindex = 0, fullanswer = [], followup = [], cardData = [];
304
318
  let deepThinking = '', thinkingStatus = DeepThinkingStatus.None, cardResource = ''; // 是否在深度思考中
305
319
  try {
306
- for (var _d = true, stream_1 = __asyncValues(stream), stream_1_1; stream_1_1 = yield stream_1.next(), _a = stream_1_1.done, !_a;) {
320
+ for (var _g = true, stream_1 = __asyncValues(stream), stream_1_1; stream_1_1 = yield stream_1.next(), _a = stream_1_1.done, !_a;) {
307
321
  _c = stream_1_1.value;
308
- _d = false;
322
+ _g = false;
309
323
  try {
310
324
  const part = _c;
311
325
  if (part.event === api_1.ChatEventType.ERROR)
312
326
  return this.emit('chaterror', { successed: false, error: 'call failed' });
313
- if (part.event === api_1.ChatEventType.CONVERSATION_MESSAGE_DELTA) {
314
- let { conversation_id, content_type, type, role, content } = part.data;
327
+ if (part.event === api_1.ChatEventType.CONVERSATION_MESSAGE_DELTA ||
328
+ part.event === api_1.WorkflowEventType.MESSAGE) {
329
+ let { conversation_id, content_type, type = 'answer', role = 'assistant', content, reasoning_content: reasoning } = part.data;
330
+ if (!content && reasoning) {
331
+ this.emit('chatthinking', { text: reasoning, completed: false, action: 'deep-thinking' });
332
+ continue;
333
+ }
315
334
  // 如果存在深度思考,则一开始就会带有相关的关键信息
316
- if (deltaindex === 0 && content.startsWith("{\"process_msg")) {
335
+ if (deltaindex === 0 && content.startsWith("<xiaolu-")) {
317
336
  thinkingStatus = DeepThinkingStatus.Thinking;
318
337
  deltaindex++;
319
338
  }
320
339
  // 如果在深度思考中,则不输出消息
321
- if (thinkingStatus !== DeepThinkingStatus.None
322
- && thinkingStatus !== DeepThinkingStatus.ThinkingOver) {
340
+ if (thinkingStatus === DeepThinkingStatus.Thinking) {
323
341
  deepThinking += content;
324
- const result = this.parseDeepThinkingJson(deepThinking, thinkingStatus);
325
- if (result.successed) {
326
- const thinking = result.thinking;
327
- if (thinking) {
328
- deepThinking = result.content;
329
- if (thinking.action === DeepThinkingAction.card) {
330
- cardResource += result.thinking.text;
331
- // 卡片流结束,解析卡片资源数据
332
- if (result.thinking.completed) {
333
- const allCards = cardResource.replace(/[\x00-\x1F\x7F]/g, '').split('|');
334
- for (const item of allCards) {
335
- const cardinfo = (0, querystring_1.parse)(item);
336
- if (cardinfo.type && cardinfo.tag)
337
- cardData.push({ type: Number(cardinfo.type), tag: cardinfo.tag });
338
- }
339
- // 将卡片资源返回给客户端
340
- this.emit('chatcard', cardData);
342
+ const result = this.parseDeepThinkingJson(deepThinking);
343
+ const thinking = result.thinking;
344
+ if (thinking) {
345
+ deepThinking = result.content;
346
+ if (thinking.action === DeepThinkingAction.card) {
347
+ cardResource += result.thinking.text;
348
+ // 卡片流结束,解析卡片资源数据
349
+ if (result.thinking.completed) {
350
+ const allCards = cardResource.replace(/[\x00-\x1F\x7F]/g, '').split('|');
351
+ for (const item of allCards) {
352
+ const cardinfo = (0, querystring_1.parse)(item);
353
+ if (cardinfo.type && cardinfo.tag)
354
+ cardData.push({ type: Number(cardinfo.type), tag: cardinfo.tag });
341
355
  }
356
+ // 将卡片资源返回给客户端
357
+ this.emit('chatcard', cardData);
342
358
  }
343
- else {
344
- this.emit('chatthinking', result.thinking);
345
- }
346
359
  }
347
- thinkingStatus = result.status;
360
+ else {
361
+ this.emit('chatthinking', { text: result.thinking.text, completed: result.thinking.completed, action: (_d = thinking.action) === null || _d === void 0 ? void 0 : _d.tag });
362
+ }
348
363
  }
349
- if (thinkingStatus != DeepThinkingStatus.ThinkingOver)
364
+ if (result.status != DeepThinkingStatus.ThinkingOver)
350
365
  continue;
351
- // thinkingStatus = DeepThinkingStatus.None;
366
+ thinkingStatus = DeepThinkingStatus.ThinkingOver;
352
367
  // 将排除了thinking之后的消息内容要带下去成为正式的输出内容
353
368
  content = deepThinking;
354
369
  }
@@ -360,28 +375,29 @@ class CorzBot extends gptbase_1.default {
360
375
  }
361
376
  ////在流式传输中,提取相关推荐问题
362
377
  if (part.event === api_1.ChatEventType.CONVERSATION_MESSAGE_COMPLETED) {
363
- const { type, content } = part.data;
378
+ const { type, content } = (_e = part.data) !== null && _e !== void 0 ? _e : {};
364
379
  if (type === 'follow_up')
365
380
  followup.push(content);
366
381
  }
367
382
  ///整个对话结束
368
- if (part.event === api_1.ChatEventType.CONVERSATION_CHAT_COMPLETED) {
369
- const { conversation_id } = part.data;
370
- let output = { successed: true, cards: cardData.length ? cardData : null, followup, type: 'answer', content_type: 'text', role: api_1.RoleType.Assistant, requestid, segment: null, text: fullanswer.join(''), index: index++, session_id: conversation_id };
383
+ if (part.event === api_1.ChatEventType.CONVERSATION_CHAT_COMPLETED ||
384
+ part.event === api_1.WorkflowEventType.DONE) {
385
+ const { conversation_id, content } = (_f = (part.data)) !== null && _f !== void 0 ? _f : {};
386
+ 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 };
371
387
  if (attach)
372
388
  output = Object.assign({}, output, attach);
373
389
  this.emit('chatdone', output);
374
390
  }
375
391
  }
376
392
  finally {
377
- _d = true;
393
+ _g = true;
378
394
  }
379
395
  }
380
396
  }
381
397
  catch (e_1_1) { e_1 = { error: e_1_1 }; }
382
398
  finally {
383
399
  try {
384
- if (!_d && !_a && (_b = stream_1.return)) yield _b.call(stream_1);
400
+ if (!_g && !_a && (_b = stream_1.return)) yield _b.call(stream_1);
385
401
  }
386
402
  finally { if (e_1) throw e_1.error; }
387
403
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "doomiaichat",
3
- "version": "7.1.10",
3
+ "version": "7.1.12",
4
4
  "description": "Doomisoft OpenAI",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {
package/src/corzbot.ts CHANGED
@@ -17,35 +17,37 @@ import {
17
17
  RunWorkflowReq,
18
18
  RunWorkflowData,
19
19
  CreateChatData,
20
- ChatStatus
20
+ ChatStatus,
21
+ WorkflowEventType,
22
+ ChatWorkflowReq
21
23
  } from '@coze/api';
22
24
  import GptBase from "./gptbase"
23
25
  import { CorzAuthorization } from './corzauthorization';
24
26
  import { parse } from 'querystring'
25
27
  import { ApiResult } from './declare';
26
28
 
27
- // 定义深度思考的动作
28
- enum DeepThinkingAction {
29
- process = 'process_msg',
30
- content = 'reasoning_content',
31
- card = 'card_resource'
29
+ // 定义深度思考的动作标签
30
+ // 这是小鹭里面专用的几个思考动作标签
31
+ const DeepThinkingAction: any = {
32
+ thinking: { start: '<xiaolu-think>', end: '</xiaolu-think>', tag: 'xiaolu-think' },
33
+ reasoning: { start: '<xiaolu-reason>', end: '</xiaolu-reason>', tag: 'xiaolu-reason' },
34
+ card: { start: '<xiaolu-card>', end: '</xiaolu-card>', tag: 'xiaolu-card' },
32
35
  }
33
36
  // 定义深度思考的状态
34
37
  enum DeepThinkingStatus {
35
38
  None,
36
39
  Thinking,
37
- ContentOutput,
40
+ // ContentOutput,
38
41
  ThinkingOver
39
42
  }
40
43
  // 智能体思考时输出的动作
41
44
  type CardType = { type: number, tag: string }
42
- type TThinkingMessage = { action: string, textposition: number }
45
+ // type TThinkingMessage = { action: string, textposition: number }
43
46
  export default class CorzBot extends GptBase {
44
- protected botid: string;
45
- protected workflowid: string;
47
+ private botid: string | null = null; // 智能体id
48
+ private workflowid: string | null = null; // 工作流id
49
+ private talkflowid: string | null = null; // 对话流id
46
50
  private apiKey: string;
47
- private lastThinkingMessage: TThinkingMessage = { action: '', textposition: 0 };
48
- // protected client: CozeAPI;
49
51
  /**
50
52
  *
51
53
  * @param apikey 调用AI中台 的key
@@ -54,8 +56,15 @@ export default class CorzBot extends GptBase {
54
56
  constructor(private authorizationProvider: CorzAuthorization, private setting: any = {}) {
55
57
  super();
56
58
  ////初始化扣子客户端
57
- this.botid = this.setting['botid'] || this.setting['botID'];
58
- this.workflowid = this.setting['workflowid'];
59
+
60
+ if (this.setting.workflowid)
61
+ this.workflowid = this.setting.workflowid;
62
+ else if (this.setting.talkflowid)
63
+ this.talkflowid = this.setting.talkflowid;
64
+ else if (this.setting.botid || this.setting.botID)
65
+ this.botid = this.setting.botid || this.setting.botID;
66
+ else
67
+ throw new Error('no botid or talkflowid or workflowid setting for coze');
59
68
  this.apiKey = this.setting['apiKey'];
60
69
  }
61
70
  private async createClient(): Promise<CozeAPI> {
@@ -79,7 +88,11 @@ export default class CorzBot extends GptBase {
79
88
  return null;
80
89
  }
81
90
  }
82
-
91
+ /**
92
+ * 设置Coze的变量
93
+ * @param params
94
+ * @returns
95
+ */
83
96
  override async setVariables(params: any): Promise<ApiResult> {
84
97
  const client = await this.createClient();
85
98
  const cozeParams: VariableUpdateReq = Object.assign({}, this.botid ? { bot_id: this.botid } : {}, params);
@@ -107,24 +120,33 @@ export default class CorzBot extends GptBase {
107
120
  async getRequestStream<T>(client: CozeAPI, message: EnterMessage[], callChatOption: any = {}): Promise<T> {
108
121
  //简单的对话请求
109
122
  const conversation_id = callChatOption.session_id ?? await this.createCoversation(client);
110
- if (!this.workflowid) {
123
+ if (this.botid) {
111
124
  const req: StreamChatReq = {
112
125
  bot_id: this.botid,
113
126
  additional_messages: message,
114
127
  user_id: callChatOption.userid || callChatOption.cozeUserID,
115
- conversation_id
128
+ conversation_id,
129
+ parameters: Object.assign({ request_src:1 }, callChatOption.parameters || {}),
116
130
  }
117
131
  req.custom_variables = Object.assign({}, this.setting.customVariables || {}, callChatOption.customVariables || {});
118
132
  return req as T;
119
133
  }
120
- const worflowreq: any = {
121
- bot_id: this.botid,
134
+ if (this.workflowid){
135
+ const worflowreq: RunWorkflowReq = {
136
+ ext: callChatOption.ext,
137
+ workflow_id: this.workflowid,//callChatOption.workflowid,
138
+ is_async: false,
139
+ // parameters: Object.assign({ input: message[0]?.content }, this.setting.customVariables || {}, callChatOption.customVariables || {}),
140
+ parameters: Object.assign({ request_src :1},callChatOption.parameters || {}, { input: message[0]?.content})
141
+ }
142
+ return worflowreq as T;
143
+ }
144
+ const worflowreq: ChatWorkflowReq = {
122
145
  additional_messages: message,
123
146
  ext: callChatOption.ext,
124
- workflow_id: this.workflowid,//callChatOption.workflowid,
147
+ workflow_id: this.talkflowid!,//callChatOption.workflowid,
125
148
  conversation_id,
126
- is_async:false,
127
- parameters: Object.assign({ input: message[0]?.content }, this.setting.customVariables || {}, callChatOption.customVariables || {}),
149
+ parameters: Object.assign({request_src:1},callChatOption.parameters || {}) //Object.assign({}, this.setting.customVariables || {}, callChatOption.customVariables || {}),
128
150
  //parameters: { input: message[0]?.content }
129
151
  }
130
152
  return worflowreq as T;
@@ -140,10 +162,10 @@ export default class CorzBot extends GptBase {
140
162
  while ((Date.now() - startTime) < maxWaitTime) {
141
163
  const chatinfo = await client.chat.retrieve(conversation_id, chatid);
142
164
  // 状态已经是完成了,可以去获取对话的内容了
143
- if (chatinfo.status === ChatStatus.COMPLETED) return { usage: chatinfo.usage, messages: await client.chat.messages.list(conversation_id, chatid)};
165
+ if (chatinfo.status === ChatStatus.COMPLETED) return { usage: chatinfo.usage, messages: await client.chat.messages.list(conversation_id, chatid) };
144
166
  await new Promise(resolve => setTimeout(resolve, 1500)); // 等待1500ms
145
167
  }
146
- return null;
168
+ return null;
147
169
  }
148
170
  /**
149
171
  * 非流式传输聊天请求
@@ -162,33 +184,47 @@ export default class CorzBot extends GptBase {
162
184
  }
163
185
  ];
164
186
  const client = await this.createClient();
165
-
187
+
166
188
  ////如果参数中用的是Workflow,则调用对话流来输出结果
167
189
  ////否则用智能体的对话来输出结果
168
190
  const params = await this.getRequestStream(client, message, callChatOption);
169
- const response = !this.workflowid ? await client.chat.create(params as CreateChatReq) : await client.workflows.runs.create(params as RunWorkflowReq);
191
+ const response = this.botid ? await client.chat.create(params as CreateChatReq) :await client.workflows.runs.create(params as RunWorkflowReq);
170
192
  if (this.workflowid && (response as RunWorkflowData).msg === 'Success') {
171
193
  const resp = (response as RunWorkflowData).data;
172
- try {
173
- return { successed: true, message: [{ role: 'assistant', type:'answer', content: JSON.parse(resp).data }]};
174
- } catch (error) {
175
- return { successed: true, message: [{ role: 'assistant', type: 'answer', content: resp }] };
176
- }
194
+ return { successed: true, message: [{ role: 'assistant', type: 'answer', content: resp }] };
195
+ // try {
196
+ // return { successed: true, message: [{ role: 'assistant', type: 'answer', content: JSON.parse(resp).data }] };
197
+ // } catch (error) {
198
+ // return { successed: true, message: [{ role: 'assistant', type: 'answer', content: resp }] };
199
+ // }
177
200
  }
178
201
  if (!this.workflowid && (response as CreateChatData).conversation_id && (response as CreateChatData).id) {
179
202
  const ccd = response as CreateChatData;
180
203
  const chatData = await this.getChatDetail(client, ccd.conversation_id, ccd.id);
181
- if (chatData){
204
+ if (chatData) {
182
205
  const message = chatData.messages.filter(x => x.type === 'answer').map(item => ({
183
206
  role: item.role,
184
207
  type: item.type,
185
208
  content: item.content,
186
209
  }));
187
- return { successed: true, message, usage: chatData.usage,session_id:ccd.conversation_id };
210
+ return { successed: true, message, usage: chatData.usage, session_id: ccd.conversation_id };
188
211
  }
189
212
  }
190
213
  return { successed: false, error: { message: '聊天未完成' } };
191
-
214
+
215
+ }
216
+ /**
217
+ * 提取XML标签中间的内容,
218
+ * @param xmlStr
219
+ * @param tagName
220
+ * @returns
221
+ */
222
+ extractXmlContent(xmlStr: string, tagName: string): string {
223
+ // 构建匹配标签的正则(支持任意空白字符和大小写)
224
+ const regex = new RegExp(`<\\s*${tagName}\\s*>([\\s\\S]*?)<\\s*\\/\\s*${tagName}\\s*>`, 'i');
225
+ const match = xmlStr.match(regex);
226
+ if (match && match[1]) return match[1].trim();
227
+ return ''; // 未找到标签或内容
192
228
  }
193
229
  /**
194
230
  * 解析深度思考的JSON内容
@@ -196,63 +232,35 @@ export default class CorzBot extends GptBase {
196
232
  * @param status
197
233
  * @returns
198
234
  */
199
- private parseDeepThinkingJson(content: string, status: DeepThinkingStatus) {
200
-
235
+ private parseDeepThinkingJson(content: string) {
201
236
  // const thinkingStartIndex = status === DeepThinkingStatus.Thinking ? content.indexOf("{\"process_msg") :
202
237
  // (status===DeepThinkingStatus.ReasonOutput ? content.indexOf("{\"reasoning_content") : content.indexOf("{\"card_resource")) ;
203
- const tagLocation = [content.indexOf("{\"process_msg"),
204
- content.indexOf("{\"reasoning_content"),
205
- content.indexOf("{\"card_resource")].filter(x => x >= 0);
206
- const thinkingStartIndex = tagLocation.length > 0 ? Math.min(...tagLocation) : -1;
207
- const thinkingEndIndex = content.indexOf("\"}");
208
- // 如果没有找到相关的JSON内容,则直接返回
209
- if (thinkingStartIndex < 0) return { successed: true, content, status: DeepThinkingStatus.ThinkingOver };
210
- let jsonStr = null;
211
- try {
212
- jsonStr = content.substring(thinkingStartIndex, thinkingEndIndex >= 0 ? thinkingEndIndex : undefined) + "\"}";
213
- // 转换JSON的时候需要将非法字符过滤掉
214
- const thinkingObject = JSON.parse(jsonStr.replace(/[\x00-\x1F\x7F]/g, ''));
215
- const thinkingAction = Object.keys(thinkingObject)[0];
216
- // 需要将内容的原文返回
217
- const originalContent = jsonStr.split('":"')[1];
218
- // 如果正确的解析JSON,并且当前有包含JSON结束的字符,则把当前思考的JSON内容替换掉
219
- if (thinkingEndIndex >= 0) content = content.substring(thinkingEndIndex + 2);
220
- // 如果正常输出了卡片资源,并且json结束 card_resource 只有一个,所以一旦结束,则整个思考过程完毕
221
- // if (status === DeepThinkingStatus.ContentOutput && thinkingEndIndex >= 0) status = DeepThinkingStatus.ThinkingOver;
222
- // 判断整个思考过程是否到达等待卡片资源(有可能智能体输出不了卡片资源) reasoning_content 只有一个,所以一旦结束,则进入下一个阶段 card_resource
223
- //if (status === DeepThinkingStatus.ContentOutput && thinkingEndIndex >= 0) status = DeepThinkingStatus.ContentOutput;
224
- // 判断当前是否从前期思考切换到思考内容输出
225
- if (status === DeepThinkingStatus.Thinking && thinkingAction !== DeepThinkingAction.process)
226
- status = DeepThinkingStatus.ContentOutput;
227
- //if (status === DeepThinkingStatus.Thinking && content.indexOf("{\"reasoning_content") >= 0) status=DeepThinkingStatus.ReasonOutput;
228
- //const removeOutputContent = content.replace(originalContent!.substring(1, originalContent!.length - 2),"")
229
- let thinkingContent = originalContent?.substring(0, originalContent.length - 2)
230
- const outputLength = thinkingContent?.length || 0;
231
-
232
- // 用lastThinkingMessage来记录上一次的思考内容,避免重复输出内容,导致的网络传输内容过多
233
- if (this.lastThinkingMessage.action === thinkingAction && this.lastThinkingMessage.textposition) {
234
- thinkingContent = thinkingContent!.substring(this.lastThinkingMessage.textposition)
238
+ const xmlTagLocation = [content.indexOf(DeepThinkingAction.thinking.start),
239
+ content.indexOf(DeepThinkingAction.reasoning.start),
240
+ content.indexOf(DeepThinkingAction.card.start)];
241
+ let minLocation = 10000, minIndex = -1;
242
+ // 找到最小的并且大于0 的位置的下标
243
+ xmlTagLocation.forEach((x, index) => {
244
+ if (x >= 0 && x < minLocation) {
245
+ minLocation = x;
246
+ minIndex = index;
235
247
  }
236
- this.lastThinkingMessage = thinkingEndIndex >= 0 ? { action: thinkingAction!, textposition: 0 } : { action: thinkingAction!, textposition: outputLength }
237
-
238
- return {
239
- successed: true, thinking: {
240
- action: thinkingAction,
241
- text: thinkingContent,//Object.values(thinkingObject)[0]
242
- completed: thinkingEndIndex >= 0
243
- }, content, status
244
- }
245
- }
246
- catch (error) {
247
- // 如果在content之后发生了错误,可能该智能体输出不了卡片类型的数据,则输出的内容已经是正文了
248
- if (status === DeepThinkingStatus.ContentOutput) {
249
- status = DeepThinkingStatus.ThinkingOver;
250
- return { successed: true, content, status };
251
- }
252
- console.error('解析JSON出错了:', jsonStr, status)
253
- // 当前解析出错,等待下一次解析
254
- return { successed: false, content, status }
255
- }
248
+ });
249
+ // const tagLocation = xmlTagLocation.filter(x => x >= 0);
250
+ const thinkingStartIndex = minIndex >= 0 ? minLocation : -1; //tagLocation.length > 0 ? Math.min(...tagLocation) : -1;
251
+ if (thinkingStartIndex < 0) return { content, status: DeepThinkingStatus.ThinkingOver };
252
+ const currentAction = [DeepThinkingAction.thinking, DeepThinkingAction.reasoning, DeepThinkingAction.card][minIndex];
253
+ const currentActionIsOver = content.indexOf(currentAction.end, thinkingStartIndex);
254
+ const thinkingEndIndex = currentActionIsOver >= 0 ? currentActionIsOver: content.indexOf('</', thinkingStartIndex);
255
+ const thinkingContent = this.extractXmlContent(content.substring(thinkingStartIndex, thinkingEndIndex >= 0 ? thinkingEndIndex : undefined) + currentAction.end, currentAction.tag); //"\"}";
256
+ if (currentActionIsOver >= 0) content = content.substring(currentActionIsOver + currentAction.end.length);
257
+ return {
258
+ thinking: {
259
+ action: currentAction,
260
+ text: thinkingContent,//Object.values(thinkingObject)[0]
261
+ completed: currentActionIsOver >= 0
262
+ }, content};
263
+
256
264
  }
257
265
  /**
258
266
  * 流式传输聊天请求
@@ -277,48 +285,51 @@ export default class CorzBot extends GptBase {
277
285
  ////否则用智能体的对话来输出结果
278
286
  let requestid = Math.ceil(Math.random() * (new Date().getTime() * Math.random()) / 1000), index = 0;
279
287
  const params = await this.getRequestStream(client, message, callChatOption);
280
- const stream = !this.workflowid ? client.chat.stream(params as StreamChatReq) : client.workflows.runs.stream(params as RunWorkflowReq);
288
+ const stream = this.botid ? client.chat.stream(params as StreamChatReq) :
289
+ (this.workflowid ? client.workflows.runs.stream(params as RunWorkflowReq) :
290
+ client.workflows.chat.stream(params as ChatWorkflowReq)) ;
281
291
  let deltaindex = 0, fullanswer: string[] = [], followup: string[] = [], cardData: CardType[] = [];
282
292
  let deepThinking = '', thinkingStatus = DeepThinkingStatus.None, cardResource = ''; // 是否在深度思考中
283
293
  for await (const part of stream) {
284
294
  if (part.event === ChatEventType.ERROR) return this.emit('chaterror', { successed: false, error: 'call failed' });
285
- if (part.event === ChatEventType.CONVERSATION_MESSAGE_DELTA) {
286
- let { conversation_id, content_type, type, role, content } = part.data;
295
+ if (part.event === ChatEventType.CONVERSATION_MESSAGE_DELTA ||
296
+ part.event === WorkflowEventType.MESSAGE
297
+ ) {
298
+ let { conversation_id, content_type, type = 'answer', role = 'assistant', content, reasoning_content: reasoning } = part.data as any;
299
+ if (!content&& reasoning) {
300
+ this.emit('chatthinking', { text: reasoning, completed: false, action: 'deep-thinking' });
301
+ continue;
302
+ }
287
303
  // 如果存在深度思考,则一开始就会带有相关的关键信息
288
- if (deltaindex === 0 && content.startsWith("{\"process_msg")) {
304
+ if (deltaindex === 0 && content.startsWith("<xiaolu-")) {
289
305
  thinkingStatus = DeepThinkingStatus.Thinking;
290
306
  deltaindex++;
291
307
  }
292
308
  // 如果在深度思考中,则不输出消息
293
- if (thinkingStatus !== DeepThinkingStatus.None
294
- && thinkingStatus !== DeepThinkingStatus.ThinkingOver
295
- ) {
309
+ if (thinkingStatus === DeepThinkingStatus.Thinking) {
296
310
  deepThinking += content;
297
- const result = this.parseDeepThinkingJson(deepThinking, thinkingStatus)
298
- if (result.successed) {
299
- const thinking = result.thinking;
300
- if (thinking) {
301
- deepThinking = result.content;
302
- if (thinking.action === DeepThinkingAction.card) {
303
- cardResource += result.thinking.text;
304
- // 卡片流结束,解析卡片资源数据
305
- if (result.thinking.completed) {
306
- const allCards = cardResource.replace(/[\x00-\x1F\x7F]/g, '').split('|')
307
- for (const item of allCards) {
308
- const cardinfo: any = parse(item);
309
- if (cardinfo.type && cardinfo.tag) cardData.push({ type: Number(cardinfo.type), tag: cardinfo.tag })
310
- }
311
- // 将卡片资源返回给客户端
312
- this.emit('chatcard', cardData);
311
+ const result = this.parseDeepThinkingJson(deepThinking)
312
+ const thinking = result.thinking;
313
+ if (thinking) {
314
+ deepThinking = result.content;
315
+ if (thinking.action === DeepThinkingAction.card) {
316
+ cardResource += result.thinking.text;
317
+ // 卡片流结束,解析卡片资源数据
318
+ if (result.thinking.completed) {
319
+ const allCards = cardResource.replace(/[\x00-\x1F\x7F]/g, '').split('|')
320
+ for (const item of allCards) {
321
+ const cardinfo: any = parse(item);
322
+ if (cardinfo.type && cardinfo.tag) cardData.push({ type: Number(cardinfo.type), tag: cardinfo.tag })
313
323
  }
314
- } else {
315
- this.emit('chatthinking', result.thinking);
324
+ // 将卡片资源返回给客户端
325
+ this.emit('chatcard', cardData);
316
326
  }
327
+ } else {
328
+ this.emit('chatthinking', { text: result.thinking.text, completed: result.thinking.completed, action: thinking.action?.tag });
317
329
  }
318
- thinkingStatus = result.status;
319
330
  }
320
- if (thinkingStatus != DeepThinkingStatus.ThinkingOver) continue;
321
- // thinkingStatus = DeepThinkingStatus.None;
331
+ if (result.status != DeepThinkingStatus.ThinkingOver) continue;
332
+ thinkingStatus = DeepThinkingStatus.ThinkingOver;
322
333
  // 将排除了thinking之后的消息内容要带下去成为正式的输出内容
323
334
  content = deepThinking;
324
335
  }
@@ -329,13 +340,15 @@ export default class CorzBot extends GptBase {
329
340
  }
330
341
  ////在流式传输中,提取相关推荐问题
331
342
  if (part.event === ChatEventType.CONVERSATION_MESSAGE_COMPLETED) {
332
- const { type, content } = part.data;
343
+ const { type, content } = part.data ?? {};
333
344
  if (type === 'follow_up') followup.push(content);
334
345
  }
335
346
  ///整个对话结束
336
- if (part.event === ChatEventType.CONVERSATION_CHAT_COMPLETED) {
337
- const { conversation_id } = part.data;
338
- let output = { successed: true, cards: cardData.length ? cardData : null, followup, type: 'answer', content_type: 'text', role: RoleType.Assistant, requestid, segment: null, text: fullanswer.join(''), index: index++, session_id: conversation_id };
347
+ if (part.event === ChatEventType.CONVERSATION_CHAT_COMPLETED ||
348
+ part.event === WorkflowEventType.DONE
349
+ ) {
350
+ const { conversation_id, content } =( part.data )?? {} as any;
351
+ let output = { successed: true, cards: cardData.length ? cardData : null, followup, type: 'answer', content_type: 'text', role: RoleType.Assistant, requestid, segment: null, text: content??fullanswer.join(''), index: index++, session_id: conversation_id };
339
352
  if (attach) output = Object.assign({}, output, attach);
340
353
  this.emit('chatdone', output)
341
354
  }