doomiaichat 4.7.0 → 4.9.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/dist/openai.d.ts CHANGED
@@ -82,7 +82,7 @@ export default class OpenAIGpt extends GptBase {
82
82
  * @param {*} messages
83
83
  * @returns
84
84
  */
85
- protected pickUpFaqContent(messages: Array<any>): Array<FaqItem>;
85
+ protected pickUpFaqContent(messages: Array<any>): Promise<Array<FaqItem>>;
86
86
  /**
87
87
  * 从指定的文本内容中生成一张试卷
88
88
  * @param {*} content
@@ -97,7 +97,7 @@ export default class OpenAIGpt extends GptBase {
97
97
  * @param {*} result
98
98
  *
99
99
  */
100
- protected pickUpQuestions(result: Array<any>, count: number, questiontype: string, score?: number): Array<QuestionItem>;
100
+ protected pickUpQuestions(result: Array<any>, count: number, questiontype: string, score?: number): Promise<Array<QuestionItem>>;
101
101
  /**
102
102
  * 验证JSON字符串是否是真正可转换为JSON的合法格式
103
103
  * 这里只能做一个最简单的处理,就是用两端的符号
package/dist/openai.js CHANGED
@@ -64,10 +64,19 @@ const QUESTION_ROLE_DEFINE = {
64
64
  * 问题生成的Prompt
65
65
  */
66
66
  const QUESTION_PROMPT = {
67
- singlechoice: '根据以下内容,生成@ITEMCOUNT@道单选题,每道题目4个选项,每道题的选项中的元素用大写字母ABCD开头,每道题一个正确答案,输出结果必须是JSON数组并按照[{"question":"","choice":[],"answer":[]}]的结构输出',
68
- multiplechoice: '根据以下内容,请生成@ITEMCOUNT@道多选题,提供4个选项,每道题的选项中的元素用大写字母ABCD开头,每道题的答案至少有两个选项,输出结果必须是JSON数组并按照[{"question":"","choice":[],"answer":[]}]的结构输出',
69
- trueorfalse: '根据以下内容,请生成@ITEMCOUNT@道判断题,每道题正确和错误两个选项,输出结果必须是JSON数组并按照[{"question":"","choice":["A.正确","B.错误"],"answer":[]}]的结构输出',
70
- completion: '根据以下内容,请生成@ITEMCOUNT@道填空题和对应答案,输出结果必须是JSON数组并按照[{"question":"","answer":["填空答案1","填空答案2"]}]的结构输出'
67
+ singlechoice: '根据以下内容,生成@ITEMCOUNT@道单选题,每道题目4个选项,每道题的选项中的元素用大写字母ABCD开头,每道题一个正确答案,输出结果必须是JSON数组并按照[{"question":"","choice":[],"answer":[]}]的结构输出。如果内容不足以提取问题和答案,请直接输出JSON空数组。',
68
+ multiplechoice: '根据以下内容,请生成@ITEMCOUNT@道多选题,提供4个选项,每道题的选项中的元素用大写字母ABCD开头,每道题的答案至少有两个选项,输出结果必须是JSON数组并按照[{"question":"","choice":[],"answer":[]}]的结构输出。如果内容不足以提取问题和答案,请直接输出JSON空数组。',
69
+ trueorfalse: '根据以下内容,请生成@ITEMCOUNT@道判断题,每道题正确和错误两个选项,输出结果必须是JSON数组并按照[{"question":"","choice":["A.正确","B.错误"],"answer":[]}]的结构输出。如果内容不足以提取问题和答案,请直接输出JSON空数组。',
70
+ completion: '根据以下内容,请生成@ITEMCOUNT@道填空题和对应答案,输出结果必须是JSON数组并按照[{"question":"","answer":["填空答案1","填空答案2"]}]的结构输出。如果内容不足以提取问题和答案,请直接输出JSON空数组。'
71
+ };
72
+ /**
73
+ * 问题生成的Prompt
74
+ */
75
+ const QUESTION_PROMPT_FIXED = {
76
+ singlechoice: '[{"question":"","choice":[],"answer":[]}]',
77
+ multiplechoice: '[{"question":"","choice":[],"answer":[]}]',
78
+ trueorfalse: '[{"question":"","choice":["A.正确","B.错误"],"answer":[]}]',
79
+ completion: '[{"question":"","answer":["填空答案1","填空答案2"]}]'
71
80
  };
72
81
  const QUESTION_TYPE = ['singlechoice', 'multiplechoice', 'trueorfalse', 'completion'];
73
82
  class OpenAIGpt extends gptbase_1.default {
@@ -137,6 +146,12 @@ class OpenAIGpt extends gptbase_1.default {
137
146
  max_tokens: Number((callChatOption === null || callChatOption === void 0 ? void 0 : callChatOption.maxtoken) || this.maxtoken),
138
147
  n: Number((callChatOption === null || callChatOption === void 0 ? void 0 : callChatOption.replyCounts) || 1) || 1
139
148
  }, axiosOption);
149
+ // console.log('finish_reason==>', response.data.choices)
150
+ ////输出的内容不合规
151
+ if (response.data.choices[0].finish_reason === 'content_filter') {
152
+ console.log('content_filter');
153
+ return { successed: false, error: 'content_filter' };
154
+ }
140
155
  return { successed: true, message: response.data.choices, usage: response.data.usage };
141
156
  }
142
157
  catch (error) {
@@ -372,8 +387,15 @@ class OpenAIGpt extends gptbase_1.default {
372
387
  * @returns
373
388
  */ //并在答案末尾处必须给出答案内容中的关键词
374
389
  generateQuestionsFromContent(content, count = 1, everyContentLength = SECTION_LENGTH, axiosOption = {}) {
390
+ var _a;
375
391
  return __awaiter(this, void 0, void 0, function* () {
376
392
  let arrContent = this.splitLongText(content, everyContentLength || SECTION_LENGTH);
393
+ ///如果最后一段的文字内容过短,则把最后一段内容追加到前一段中,并删除最后一段
394
+ let totalLen = arrContent.length;
395
+ if (totalLen >= 2 && (((_a = arrContent[totalLen - 1]) === null || _a === void 0 ? void 0 : _a.length) || 0) < 100) {
396
+ arrContent[totalLen - 2] += arrContent[totalLen - 1];
397
+ arrContent.splice(totalLen - 1, 1);
398
+ }
377
399
  ///没20句话分为一组,适应大文件内容多次请求组合结果
378
400
  ///每一句话需要产生的题目
379
401
  let questions4EverySentense = count / arrContent.length; //Math.ceil(arrContent.length / 20);
@@ -384,19 +406,18 @@ class OpenAIGpt extends gptbase_1.default {
384
406
  let itemCount = Math.min(Math.ceil(questions4EverySentense), count - gotted);
385
407
  let subarray = [
386
408
  { role: 'system', content: FAQ_ROLE_DEFINE },
387
- { role: 'user', content: `从以下内容中提取${itemCount}条提问及答案,并从答案内容提取出至少2个关键词,最终结果按照[{"question":"提问内容","answer":"答案内容","keywords":["关键词1","关键词2"]}]的JSON数组结构输出。` },
388
- { role: 'user', content: arrContent.slice(0, 1)[0]
389
- }
409
+ { role: 'user', content: `从以下内容中提取${itemCount}条提问及答案,并从答案内容提取出至少2个关键词,最终结果按照[{"question":"提问内容","answer":"答案内容","keywords":["关键词1","关键词2"]}]的JSON数组结构输出。如果内容不足以提取问题和答案,请直接输出JSON空数组 []。` },
410
+ { role: 'user', content: arrContent.slice(0, 1)[0] }
390
411
  ];
391
- //subarray.push({ role: 'user', content:'请根据上述内容,给出一道提问与答案以及答案关键词,按照先问题内容,再标准答案,再关键词的顺序输出,关键词之间用、分开'})
392
- //subarray.unshift({ role: 'system', content: `你是一位专业培训师,从以下内容中提取${itemCount}条提问及答案,并从答案内容提取出至少2个关键词,最终结果按照[{"question":"提问内容","answer":"答案内容","keywords":["关键词1","关键词2"]}]的JSON数组结构输出。` })
393
- // subarray.unshift({role: 'system', content: FAQ_ROLE_DEFINE});
394
- //subarray.unshift({ role: 'system', content: `你是一位专业程序开发工程师,根据以下内容,按照[{"question":"问题内容","answer":"答案内容","keywords":["关键词1","关键词2"]}]JSON数组结构,给出${itemCount}条提问问题及答案以及答案关键词` })
395
- // console.log('subarray', subarray)
412
+ console.log('Faq Question Pick Prompt:', subarray);
396
413
  let result = yield this.chatRequest(subarray, { replyCounts: 1 }, axiosOption);
414
+ ///如果请求发生了网络错误(不是内容合规问题),则再重试一次,如果任然有错则放弃
415
+ if (!result.successed && result.error != 'content_filter') {
416
+ console.log('network error,retry onemore time');
417
+ result = yield this.chatRequest(subarray, { replyCounts: 1 }, axiosOption);
418
+ }
397
419
  if (result.successed && result.message) {
398
- // console.log('result is ', result.message[0].message.content)
399
- let msgs = this.pickUpFaqContent(result.message);
420
+ let msgs = yield this.pickUpFaqContent(result.message);
400
421
  if (msgs.length) {
401
422
  ///对外发送检出问答题的信号
402
423
  this.emit('parseout', { type: 'qa', items: msgs });
@@ -420,32 +441,49 @@ class OpenAIGpt extends gptbase_1.default {
420
441
  */
421
442
  pickUpFaqContent(messages) {
422
443
  var _a, _b;
423
- if (!((_b = (_a = messages[0]) === null || _a === void 0 ? void 0 : _a.message) === null || _b === void 0 ? void 0 : _b.content))
424
- return [];
425
- let answerString = messages[0].message.content.trim().replace(/\t|\n|\v|\r|\f/g, '');
426
- let jsonObj = this.fixedJsonString(answerString);
427
- if (!jsonObj.length)
428
- return [];
429
- try {
430
- //let jsonObj = JSON.parse(answerString);
431
- //let jsonObj = eval(answerString);
432
- jsonObj.map((item) => {
433
- let realKeyword = [];
434
- let keywords = (item.keywords + '').split(',');
435
- let answer = item.answer || '';
436
- for (const k of keywords) {
437
- if (k && answer.indexOf(k) >= 0)
438
- realKeyword.push(k);
444
+ return __awaiter(this, void 0, void 0, function* () {
445
+ if (!((_b = (_a = messages[0]) === null || _a === void 0 ? void 0 : _a.message) === null || _b === void 0 ? void 0 : _b.content))
446
+ return [];
447
+ let answerString = messages[0].message.content.trim().replace(/\t|\n|\v|\r|\f/g, '');
448
+ if (answerString === '[]')
449
+ return [];
450
+ let jsonObj = this.fixedJsonString(answerString);
451
+ if (!jsonObj.length) {
452
+ let fixedAsk = [
453
+ { role: 'system', content: '角色扮演:假设你是一位高级JSON数据分析师' },
454
+ { role: 'user', content: `请分析以下内容,严格按照[{"question":"提问内容","answer":"答案内容","keywords":["关键词1","关键词2"]}]的标准JSON数组结构输出。如果内容不足以提取问题和答案,请直接输出JSON空数组,无需提供参考。` },
455
+ { role: 'user', content: answerString },
456
+ ];
457
+ console.log('pickUpFaqContent fixedAsk', fixedAsk);
458
+ let fixedJsonResult = yield this.chatRequest(fixedAsk, { replyCounts: 1 }, {});
459
+ if (fixedJsonResult.successed) {
460
+ answerString = fixedJsonResult.message[0].message.content.trim().replace(/\t|\n|\v|\r|\f/g, '');
461
+ jsonObj = this.fixedJsonString(answerString);
439
462
  }
440
- item.keywords = realKeyword;
441
- return item;
442
- });
443
- return jsonObj;
444
- }
445
- catch (err) {
446
- console.log('JSON error', err);
447
- return [];
448
- }
463
+ if (!jsonObj.length)
464
+ return [];
465
+ }
466
+ try {
467
+ //let jsonObj = JSON.parse(answerString);
468
+ //let jsonObj = eval(answerString);
469
+ jsonObj.map((item) => {
470
+ let realKeyword = [];
471
+ let keywords = (item.keywords + '').split(',');
472
+ let answer = item.answer || '';
473
+ for (const k of keywords) {
474
+ if (k && answer.indexOf(k) >= 0)
475
+ realKeyword.push(k);
476
+ }
477
+ item.keywords = realKeyword;
478
+ return item;
479
+ });
480
+ return jsonObj;
481
+ }
482
+ catch (err) {
483
+ console.log('JSON error', err);
484
+ return [];
485
+ }
486
+ });
449
487
  }
450
488
  /**
451
489
  * 从指定的文本内容中生成一张试卷
@@ -457,28 +495,34 @@ class OpenAIGpt extends gptbase_1.default {
457
495
  * @returns
458
496
  */ //并在答案末尾处必须给出答案内容中的关键词
459
497
  generateExaminationPaperFromContent(content, paperOption = {}, everyContentLength = SECTION_LENGTH, axiosOption = {}) {
460
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
498
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
461
499
  return __awaiter(this, void 0, void 0, function* () {
462
500
  let arrContent = this.splitLongText(content, everyContentLength || SECTION_LENGTH);
501
+ ///如果最后一段的文字内容过短,则把最后一段内容追加到前一段中,并删除最后一段
502
+ let totalLen = arrContent.length;
503
+ if (totalLen >= 2 && (((_a = arrContent[totalLen - 1]) === null || _a === void 0 ? void 0 : _a.length) || 0) < 100) {
504
+ arrContent[totalLen - 2] += arrContent[totalLen - 1];
505
+ arrContent.splice(totalLen - 1, 1);
506
+ }
463
507
  let sectionCount = {
464
- singlechoice: (((_a = paperOption.singlechoice) === null || _a === void 0 ? void 0 : _a.count) || 0) / arrContent.length,
465
- multiplechoice: (((_b = paperOption.multiplechoice) === null || _b === void 0 ? void 0 : _b.count) || 0) / arrContent.length,
466
- trueorfalse: (((_c = paperOption.trueorfalse) === null || _c === void 0 ? void 0 : _c.count) || 0) / arrContent.length,
467
- completion: (((_d = paperOption.completion) === null || _d === void 0 ? void 0 : _d.count) || 0) / arrContent.length
508
+ singlechoice: (((_b = paperOption.singlechoice) === null || _b === void 0 ? void 0 : _b.count) || 0) / arrContent.length,
509
+ multiplechoice: (((_c = paperOption.multiplechoice) === null || _c === void 0 ? void 0 : _c.count) || 0) / arrContent.length,
510
+ trueorfalse: (((_d = paperOption.trueorfalse) === null || _d === void 0 ? void 0 : _d.count) || 0) / arrContent.length,
511
+ completion: (((_e = paperOption.completion) === null || _e === void 0 ? void 0 : _e.count) || 0) / arrContent.length
468
512
  };
469
513
  ///剩余待生成的题目数量
470
514
  let remainCount = {
471
- singlechoice: ((_e = paperOption.singlechoice) === null || _e === void 0 ? void 0 : _e.count) || 0,
472
- multiplechoice: ((_f = paperOption.multiplechoice) === null || _f === void 0 ? void 0 : _f.count) || 0,
473
- trueorfalse: ((_g = paperOption.trueorfalse) === null || _g === void 0 ? void 0 : _g.count) || 0,
474
- completion: ((_h = paperOption.completion) === null || _h === void 0 ? void 0 : _h.count) || 0
515
+ singlechoice: ((_f = paperOption.singlechoice) === null || _f === void 0 ? void 0 : _f.count) || 0,
516
+ multiplechoice: ((_g = paperOption.multiplechoice) === null || _g === void 0 ? void 0 : _g.count) || 0,
517
+ trueorfalse: ((_h = paperOption.trueorfalse) === null || _h === void 0 ? void 0 : _h.count) || 0,
518
+ completion: ((_j = paperOption.completion) === null || _j === void 0 ? void 0 : _j.count) || 0
475
519
  };
476
520
  ///每种类型的题目的分数
477
521
  let ITEM_SCORE = {
478
- singlechoice: ((_j = paperOption.singlechoice) === null || _j === void 0 ? void 0 : _j.score) || 0,
479
- multiplechoice: ((_k = paperOption.multiplechoice) === null || _k === void 0 ? void 0 : _k.score) || 0,
480
- trueorfalse: ((_l = paperOption.trueorfalse) === null || _l === void 0 ? void 0 : _l.score) || 0,
481
- completion: ((_m = paperOption.completion) === null || _m === void 0 ? void 0 : _m.score) || 0
522
+ singlechoice: ((_k = paperOption.singlechoice) === null || _k === void 0 ? void 0 : _k.score) || 0,
523
+ multiplechoice: ((_l = paperOption.multiplechoice) === null || _l === void 0 ? void 0 : _l.score) || 0,
524
+ trueorfalse: ((_m = paperOption.trueorfalse) === null || _m === void 0 ? void 0 : _m.score) || 0,
525
+ completion: ((_o = paperOption.completion) === null || _o === void 0 ? void 0 : _o.score) || 0
482
526
  };
483
527
  ///最后生成出来的结果
484
528
  let paperReturned = {
@@ -489,7 +533,6 @@ class OpenAIGpt extends gptbase_1.default {
489
533
  /**
490
534
  * 每种类型的题目进行遍历
491
535
  */
492
- console.log('arrContent.length', arrContent.length);
493
536
  noMoreQuestionRetrive = true;
494
537
  for (const key of QUESTION_TYPE) {
495
538
  ///还需要抓取题目
@@ -505,10 +548,15 @@ class OpenAIGpt extends gptbase_1.default {
505
548
  // subarray.unshift()
506
549
  console.log('subarray', subarray);
507
550
  let result = yield this.chatRequest(subarray, { replyCounts: 1 }, axiosOption);
551
+ ///如果请求发生了网络错误(不是内容合规问题),则再重试一次,如果任然有错则放弃
552
+ if (!result.successed && result.error != 'content_filter') {
553
+ console.log('network error,retry onemore time');
554
+ result = yield this.chatRequest(subarray, { replyCounts: 1 }, axiosOption);
555
+ }
508
556
  console.log('subarray returned', result.successed);
509
557
  if (result.successed && result.message) {
510
558
  //console.log('paper result', key, result.message.length)
511
- let pickedQuestions = this.pickUpQuestions(result.message, itemCount, key, ITEM_SCORE[key]);
559
+ let pickedQuestions = yield this.pickUpQuestions(result.message, itemCount, key, ITEM_SCORE[key]);
512
560
  if (pickedQuestions.length) {
513
561
  ///对外发送检出题目的信号
514
562
  this.emit('parseout', { type: 'question', name: key, items: pickedQuestions });
@@ -537,76 +585,97 @@ class OpenAIGpt extends gptbase_1.default {
537
585
  */
538
586
  pickUpQuestions(result, count, questiontype, score = 1) {
539
587
  var _a, _b;
540
- if (!((_b = (_a = result[0]) === null || _a === void 0 ? void 0 : _a.message) === null || _b === void 0 ? void 0 : _b.content))
541
- return [];
542
- let answerString = result[0].message.content.trim().replace(/\t|\n|\v|\r|\f/g, '');
543
- let jsonObj = this.fixedJsonString(answerString);
544
- if (!jsonObj.length)
545
- return [];
546
- let returnItems = [];
547
- try {
548
- // let jsonObj = JSON.parse(answerString);
549
- returnItems = jsonObj.map((questionitem) => {
550
- console.log('answer item from jsonObj', questionitem);
551
- if (questionitem.choice && Array.isArray(questionitem.choice) && questiontype != 'completion') {
552
- questionitem.fullanswer = (questionitem.answer + '').replace(/,|[^ABCDE]/g, '');
553
- questionitem.score = score;
554
- if (questionitem.choice) {
555
- questionitem.choice = questionitem.choice.map((item, index) => {
556
- let seqNo = 'ABCDEFG'[index]; //String.fromCharCode(65 + index);
557
- let correctReg = new RegExp(`${seqNo}.|${seqNo}`, 'ig');
558
- // console.log('itemitemitem', item)
559
- //let answer = jsonObj.fullanswer
560
- return {
561
- id: seqNo,
562
- content: (item + '').replace(correctReg, '').trim(),
563
- iscorrect: (questionitem.fullanswer || '').indexOf(seqNo) >= 0 ? 1 : 0
564
- //|| jsonObj.fullanswer.indexOf(m))
565
- };
566
- });
588
+ return __awaiter(this, void 0, void 0, function* () {
589
+ if (!((_b = (_a = result[0]) === null || _a === void 0 ? void 0 : _a.message) === null || _b === void 0 ? void 0 : _b.content))
590
+ return [];
591
+ let answerString = result[0].message.content.trim().replace(/\t|\n|\v|\r|\f/g, '');
592
+ if (answerString === '[]')
593
+ return [];
594
+ let jsonObj = this.fixedJsonString(answerString);
595
+ ////修复的结果无法用程序修复,请求GPT来分析修复一下这个结果
596
+ if (!jsonObj.length) {
597
+ let fixedAsk = [
598
+ { role: 'system', content: '角色扮演:假设你是一位高级JSON数据分析师' },
599
+ { role: 'user', content: `请分析以下内容,严格按照${QUESTION_PROMPT_FIXED[questiontype]}的标准JSON数组结构输出。如果内容不足以提取问题和答案,请直接输出JSON空数组,无需提供参考。` },
600
+ { role: 'user', content: answerString },
601
+ ];
602
+ console.log('fixedAsk', fixedAsk);
603
+ let fixedJsonResult = yield this.chatRequest(fixedAsk, { replyCounts: 1 }, {});
604
+ if (fixedJsonResult.successed) {
605
+ answerString = fixedJsonResult.message[0].message.content.trim().replace(/\t|\n|\v|\r|\f/g, '');
606
+ jsonObj = this.fixedJsonString(answerString);
607
+ }
608
+ if (!jsonObj.length)
609
+ return [];
610
+ }
611
+ let returnItems = [];
612
+ try {
613
+ // let jsonObj = JSON.parse(answerString);
614
+ returnItems = jsonObj.map((questionitem) => {
615
+ console.log('answer item from jsonObj', questionitem);
616
+ if (questionitem.choice && Array.isArray(questionitem.choice) && questiontype != 'completion') {
617
+ questionitem.fullanswer = (questionitem.answer + '').replace(/,|[^ABCDE]/g, '');
618
+ questionitem.score = score;
619
+ if (questionitem.choice) {
620
+ questionitem.choice = questionitem.choice.map((item, index) => {
621
+ let seqNo = 'ABCDEFG'[index]; //String.fromCharCode(65 + index);
622
+ let correctReg = new RegExp(`${seqNo}.|${seqNo}`, 'ig');
623
+ // console.log('itemitemitem', item)
624
+ //let answer = jsonObj.fullanswer
625
+ return {
626
+ id: seqNo,
627
+ content: (item + '').replace(correctReg, '').trim(),
628
+ iscorrect: (questionitem.fullanswer || '').indexOf(seqNo) >= 0 ? 1 : 0
629
+ //|| jsonObj.fullanswer.indexOf(m))
630
+ };
631
+ });
632
+ }
633
+ ///如果是非判断题,题目的选项数量小于2 ,则无效
634
+ ///如果是判断题,题目的选项必须=2
635
+ if (!questionitem.choice || (questiontype != 'trueorfalse' && questionitem.choice.length < 3) || (questiontype == 'trueorfalse' && questionitem.choice.length != 2)) {
636
+ return null;
637
+ }
567
638
  }
568
- ///如果是非判断题,题目的选项数量小于2 ,则无效
569
- ///如果是判断题,题目的选项必须=2
570
- if (!questionitem.choice || (questiontype != 'trueorfalse' && questionitem.choice.length < 3) || (questiontype == 'trueorfalse' && questionitem.choice.length != 2)) {
571
- return null;
639
+ switch (questiontype) {
640
+ case 'singlechoice':
641
+ questionitem.answer = (questionitem.answer + '').replace(/,|[^ABCDEFG]/g, '').split('').slice(0, 1);
642
+ break;
643
+ case 'multiplechoice':
644
+ questionitem.answer = Array.from(new Set((questionitem.answer + '').replace(/,|[^ABCDEFG]/g, '').split('')));
645
+ break;
646
+ case 'trueorfalse':
647
+ let rightItem = questionitem.choice.find((x) => { return x.iscorrect == 1; });
648
+ questionitem.answer = [(rightItem === null || rightItem === void 0 ? void 0 : rightItem.id) || 'Z']; //[(questionitem.answer + '').indexOf('正确') >= 0 ? 'A' : 'B']
649
+ break;
572
650
  }
573
- }
574
- switch (questiontype) {
575
- case 'singlechoice':
576
- questionitem.answer = (questionitem.answer + '').replace(/,|[^ABCDEFG]/g, '').split('').slice(0, 1);
577
- break;
578
- case 'multiplechoice':
579
- questionitem.answer = (questionitem.answer + '').replace(/,|[^ABCDEFG]/g, '').split('');
580
- break;
581
- case 'trueorfalse':
582
- let rightItem = questionitem.choice.find((x) => { return x.iscorrect == 1; });
583
- questionitem.answer = [(rightItem === null || rightItem === void 0 ? void 0 : rightItem.id) || 'Z']; //[(questionitem.answer + '').indexOf('正确') >= 0 ? 'A' : 'B']
584
- break;
585
- }
586
- ///单选题验证
587
- if (questiontype == 'singlechoice') {
588
- let rightAnswer = questionitem.choice ? questionitem.choice.filter((item) => { return item.iscorrect === 1; }) : [];
589
- ///单选题的正确选项大于了1个
590
- if (rightAnswer.length != 1 || !questionitem.answer || questionitem.answer.length !== 1)
591
- return null;
592
- ///正确选项和答案不一致
593
- if (rightAnswer[0].id.toUpperCase() != (questionitem.answer[0] || '').toUpperCase())
594
- return null;
595
- }
596
- ///多选题验证
597
- if (questiontype == 'multiplechoice') {
598
- let rightAnswer = questionitem.choice ? questionitem.choice.filter((item) => { return item.iscorrect === 1; }) : [];
599
- ///单选题的正确选项大于了1个
600
- if (rightAnswer.length === 0 || !questionitem.answer || questionitem.answer.length === 0)
651
+ ///单选题验证
652
+ if (questiontype == 'singlechoice') {
653
+ let rightAnswer = questionitem.choice ? questionitem.choice.filter((item) => { return item.iscorrect === 1; }) : [];
654
+ ///单选题的正确选项大于了1
655
+ if (rightAnswer.length != 1 || !questionitem.answer || questionitem.answer.length !== 1)
656
+ return null;
657
+ ///正确选项和答案不一致
658
+ if (rightAnswer[0].id.toUpperCase() != (questionitem.answer[0] || '').toUpperCase())
659
+ return null;
660
+ }
661
+ ///多选题验证
662
+ if (questiontype == 'multiplechoice') {
663
+ let rightAnswer = questionitem.choice ? questionitem.choice.filter((item) => { return item.iscorrect === 1; }) : [];
664
+ ///单选题的正确选项大于了1个
665
+ if (rightAnswer.length === 0 || !questionitem.answer || questionitem.answer.length === 0)
666
+ return null;
667
+ }
668
+ ///判断题验证:防止没有答案的
669
+ if (questiontype == 'trueorfalse' && !questionitem.answer.length)
601
670
  return null;
602
- }
603
- return questionitem;
604
- });
605
- }
606
- catch (err) {
607
- console.log('error happened:', err);
608
- }
609
- return returnItems.filter(i => { return i != null; }).slice(0, count);
671
+ return questionitem;
672
+ });
673
+ }
674
+ catch (err) {
675
+ console.log('error happened:', err);
676
+ }
677
+ return returnItems.filter(i => { return i != null; }).slice(0, count);
678
+ });
610
679
  }
611
680
  /**
612
681
  * 验证JSON字符串是否是真正可转换为JSON的合法格式
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "doomiaichat",
3
- "version": "4.7.0",
3
+ "version": "4.9.0",
4
4
  "description": "Doomisoft OpenAI",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {
package/src/openai.ts CHANGED
@@ -52,10 +52,20 @@ const QUESTION_ROLE_DEFINE: any = {
52
52
  * 问题生成的Prompt
53
53
  */
54
54
  const QUESTION_PROMPT: any ={
55
- singlechoice:'根据以下内容,生成@ITEMCOUNT@道单选题,每道题目4个选项,每道题的选项中的元素用大写字母ABCD开头,每道题一个正确答案,输出结果必须是JSON数组并按照[{"question":"","choice":[],"answer":[]}]的结构输出',
56
- multiplechoice: '根据以下内容,请生成@ITEMCOUNT@道多选题,提供4个选项,每道题的选项中的元素用大写字母ABCD开头,每道题的答案至少有两个选项,输出结果必须是JSON数组并按照[{"question":"","choice":[],"answer":[]}]的结构输出',
57
- trueorfalse: '根据以下内容,请生成@ITEMCOUNT@道判断题,每道题正确和错误两个选项,输出结果必须是JSON数组并按照[{"question":"","choice":["A.正确","B.错误"],"answer":[]}]的结构输出',
58
- completion: '根据以下内容,请生成@ITEMCOUNT@道填空题和对应答案,输出结果必须是JSON数组并按照[{"question":"","answer":["填空答案1","填空答案2"]}]的结构输出'
55
+ singlechoice:'根据以下内容,生成@ITEMCOUNT@道单选题,每道题目4个选项,每道题的选项中的元素用大写字母ABCD开头,每道题一个正确答案,输出结果必须是JSON数组并按照[{"question":"","choice":[],"answer":[]}]的结构输出。如果内容不足以提取问题和答案,请直接输出JSON空数组。',
56
+ multiplechoice: '根据以下内容,请生成@ITEMCOUNT@道多选题,提供4个选项,每道题的选项中的元素用大写字母ABCD开头,每道题的答案至少有两个选项,输出结果必须是JSON数组并按照[{"question":"","choice":[],"answer":[]}]的结构输出。如果内容不足以提取问题和答案,请直接输出JSON空数组。',
57
+ trueorfalse: '根据以下内容,请生成@ITEMCOUNT@道判断题,每道题正确和错误两个选项,输出结果必须是JSON数组并按照[{"question":"","choice":["A.正确","B.错误"],"answer":[]}]的结构输出。如果内容不足以提取问题和答案,请直接输出JSON空数组。',
58
+ completion: '根据以下内容,请生成@ITEMCOUNT@道填空题和对应答案,输出结果必须是JSON数组并按照[{"question":"","answer":["填空答案1","填空答案2"]}]的结构输出。如果内容不足以提取问题和答案,请直接输出JSON空数组。'
59
+ }
60
+
61
+ /**
62
+ * 问题生成的Prompt
63
+ */
64
+ const QUESTION_PROMPT_FIXED: any = {
65
+ singlechoice: '[{"question":"","choice":[],"answer":[]}]',
66
+ multiplechoice: '[{"question":"","choice":[],"answer":[]}]',
67
+ trueorfalse: '[{"question":"","choice":["A.正确","B.错误"],"answer":[]}]',
68
+ completion: '[{"question":"","answer":["填空答案1","填空答案2"]}]'
59
69
  }
60
70
  const QUESTION_TYPE: string[] = ['singlechoice', 'multiplechoice', 'trueorfalse', 'completion']
61
71
 
@@ -129,6 +139,12 @@ export default class OpenAIGpt extends GptBase {
129
139
  max_tokens: Number(callChatOption?.maxtoken || this.maxtoken),
130
140
  n: Number(callChatOption?.replyCounts || 1) || 1
131
141
  }, axiosOption);
142
+ // console.log('finish_reason==>', response.data.choices)
143
+ ////输出的内容不合规
144
+ if (response.data.choices[0].finish_reason ==='content_filter') {
145
+ console.log('content_filter')
146
+ return { successed: false, error:'content_filter'}
147
+ }
132
148
  return { successed: true, message: response.data.choices, usage: response.data.usage };
133
149
  } catch (error) {
134
150
  console.log('result is error ', error)
@@ -342,6 +358,12 @@ export default class OpenAIGpt extends GptBase {
342
358
  *///并在答案末尾处必须给出答案内容中的关键词
343
359
  override async generateQuestionsFromContent(content: string, count: number = 1, everyContentLength: number = SECTION_LENGTH, axiosOption: any = {}): Promise<ChatReponse> {
344
360
  let arrContent = this.splitLongText(content, everyContentLength || SECTION_LENGTH);
361
+ ///如果最后一段的文字内容过短,则把最后一段内容追加到前一段中,并删除最后一段
362
+ let totalLen = arrContent.length;
363
+ if (totalLen>=2 && (arrContent[totalLen-1]?.length||0)<100){
364
+ arrContent[totalLen - 2] += arrContent[totalLen - 1];
365
+ arrContent.splice(totalLen-1,1);
366
+ }
345
367
  ///没20句话分为一组,适应大文件内容多次请求组合结果
346
368
  ///每一句话需要产生的题目
347
369
  let questions4EverySentense: number = count / arrContent.length; //Math.ceil(arrContent.length / 20);
@@ -352,28 +374,25 @@ export default class OpenAIGpt extends GptBase {
352
374
  let itemCount = Math.min(Math.ceil(questions4EverySentense), count - gotted);
353
375
  let subarray = [
354
376
  { role: 'system', content: FAQ_ROLE_DEFINE },
355
- { role: 'user', content: `从以下内容中提取${itemCount}条提问及答案,并从答案内容提取出至少2个关键词,最终结果按照[{"question":"提问内容","answer":"答案内容","keywords":["关键词1","关键词2"]}]的JSON数组结构输出。`},
356
- { role: 'user', content: arrContent.slice(0, 1)[0]
357
- }
358
- ]
359
- //subarray.push({ role: 'user', content:'请根据上述内容,给出一道提问与答案以及答案关键词,按照先问题内容,再标准答案,再关键词的顺序输出,关键词之间用、分开'})
360
- //subarray.unshift({ role: 'system', content: `你是一位专业培训师,从以下内容中提取${itemCount}条提问及答案,并从答案内容提取出至少2个关键词,最终结果按照[{"question":"提问内容","answer":"答案内容","keywords":["关键词1","关键词2"]}]的JSON数组结构输出。` })
361
- // subarray.unshift({role: 'system', content: FAQ_ROLE_DEFINE});
362
- //subarray.unshift({ role: 'system', content: `你是一位专业程序开发工程师,根据以下内容,按照[{"question":"问题内容","answer":"答案内容","keywords":["关键词1","关键词2"]}]JSON数组结构,给出${itemCount}条提问问题及答案以及答案关键词` })
363
- // console.log('subarray', subarray)
377
+ { role: 'user', content: `从以下内容中提取${itemCount}条提问及答案,并从答案内容提取出至少2个关键词,最终结果按照[{"question":"提问内容","answer":"答案内容","keywords":["关键词1","关键词2"]}]的JSON数组结构输出。如果内容不足以提取问题和答案,请直接输出JSON空数组 []。`},
378
+ { role: 'user', content: arrContent.slice(0, 1)[0]}
379
+ ]
380
+ console.log('Faq Question Pick Prompt:', subarray)
364
381
  let result = await this.chatRequest(subarray, { replyCounts: 1 }, axiosOption);
382
+ ///如果请求发生了网络错误(不是内容合规问题),则再重试一次,如果任然有错则放弃
383
+ if (!result.successed && result.error!='content_filter'){
384
+ console.log('network error,retry onemore time')
385
+ result = await this.chatRequest(subarray, { replyCounts: 1 }, axiosOption);
386
+ }
365
387
  if (result.successed && result.message) {
366
- // console.log('result is ', result.message[0].message.content)
367
- let msgs = this.pickUpFaqContent(result.message);
388
+ let msgs = await this.pickUpFaqContent(result.message);
368
389
  if (msgs.length) {
369
390
  ///对外发送检出问答题的信号
370
391
  this.emit('parseout', { type: 'qa', items: msgs })
371
392
  gotted += msgs.length; //result.message.length;
372
393
  faqs = faqs.concat(msgs);
373
-
374
394
  }
375
395
  }
376
-
377
396
  ////删除已经处理的文本
378
397
  arrContent.splice(0, 1);
379
398
  }
@@ -388,11 +407,27 @@ export default class OpenAIGpt extends GptBase {
388
407
  * @param {*} messages
389
408
  * @returns
390
409
  */
391
- protected pickUpFaqContent(messages: Array<any>): Array<FaqItem> {
410
+ protected async pickUpFaqContent(messages: Array<any>): Promise<Array<FaqItem>> {
392
411
  if (!messages[0]?.message?.content) return [];
393
412
  let answerString = messages[0].message.content.trim().replace(/\t|\n|\v|\r|\f/g, '');
413
+ if (answerString==='[]') return [];
394
414
  let jsonObj = this.fixedJsonString(answerString);
395
- if (!jsonObj.length) return []
415
+ if (!jsonObj.length){
416
+ let fixedAsk = [
417
+ { role: 'system', content: '角色扮演:假设你是一位高级JSON数据分析师' },
418
+ { role: 'user', content: `请分析以下内容,严格按照[{"question":"提问内容","answer":"答案内容","keywords":["关键词1","关键词2"]}]的标准JSON数组结构输出。如果内容不足以提取问题和答案,请直接输出JSON空数组,无需提供参考。` },
419
+ { role: 'user', content: answerString },
420
+ ]
421
+ console.log('pickUpFaqContent fixedAsk', fixedAsk)
422
+ let fixedJsonResult: any = await this.chatRequest(fixedAsk, { replyCounts: 1 }, {})
423
+ if (fixedJsonResult.successed) {
424
+ answerString = fixedJsonResult.message[0].message.content.trim().replace(/\t|\n|\v|\r|\f/g, '');
425
+ jsonObj = this.fixedJsonString(answerString);
426
+ }
427
+ if (!jsonObj.length) return []
428
+ }
429
+
430
+
396
431
  try {
397
432
  //let jsonObj = JSON.parse(answerString);
398
433
  //let jsonObj = eval(answerString);
@@ -424,6 +459,12 @@ export default class OpenAIGpt extends GptBase {
424
459
  *///并在答案末尾处必须给出答案内容中的关键词
425
460
  override async generateExaminationPaperFromContent(content: string, paperOption: any = {}, everyContentLength: number = SECTION_LENGTH, axiosOption: any = {}): Promise<ExaminationPaperResult> {
426
461
  let arrContent = this.splitLongText(content, everyContentLength || SECTION_LENGTH);
462
+ ///如果最后一段的文字内容过短,则把最后一段内容追加到前一段中,并删除最后一段
463
+ let totalLen = arrContent.length;
464
+ if (totalLen >= 2 && (arrContent[totalLen - 1]?.length || 0) < 100) {
465
+ arrContent[totalLen - 2] += arrContent[totalLen - 1];
466
+ arrContent.splice(totalLen - 1, 1);
467
+ }
427
468
  let sectionCount: any = {
428
469
  singlechoice: (paperOption.singlechoice?.count || 0) / arrContent.length,
429
470
  multiplechoice: (paperOption.multiplechoice?.count || 0) / arrContent.length,
@@ -455,7 +496,6 @@ export default class OpenAIGpt extends GptBase {
455
496
  /**
456
497
  * 每种类型的题目进行遍历
457
498
  */
458
- console.log('arrContent.length', arrContent.length)
459
499
  noMoreQuestionRetrive = true;
460
500
  for (const key of QUESTION_TYPE) {
461
501
  ///还需要抓取题目
@@ -472,10 +512,15 @@ export default class OpenAIGpt extends GptBase {
472
512
  // subarray.unshift()
473
513
  console.log('subarray', subarray)
474
514
  let result = await this.chatRequest(subarray, { replyCounts: 1 }, axiosOption);
515
+ ///如果请求发生了网络错误(不是内容合规问题),则再重试一次,如果任然有错则放弃
516
+ if (!result.successed && result.error != 'content_filter') {
517
+ console.log('network error,retry onemore time')
518
+ result = await this.chatRequest(subarray, { replyCounts: 1 }, axiosOption);
519
+ }
475
520
  console.log('subarray returned', result.successed)
476
521
  if (result.successed && result.message) {
477
522
  //console.log('paper result', key, result.message.length)
478
- let pickedQuestions = this.pickUpQuestions(result.message, itemCount, key, ITEM_SCORE[key]);
523
+ let pickedQuestions = await this.pickUpQuestions(result.message, itemCount, key, ITEM_SCORE[key]);
479
524
  if (pickedQuestions.length) {
480
525
  ///对外发送检出题目的信号
481
526
  this.emit('parseout', { type: 'question', name: key, items: pickedQuestions })
@@ -502,11 +547,26 @@ export default class OpenAIGpt extends GptBase {
502
547
  * @param {*} result
503
548
  *
504
549
  */
505
- protected pickUpQuestions(result: Array<any>, count: number, questiontype: string, score: number = 1): Array<QuestionItem> {
550
+ protected async pickUpQuestions(result: Array<any>, count: number, questiontype: string, score: number = 1): Promise<Array<QuestionItem>> {
506
551
  if (!result[0]?.message?.content) return [];
507
552
  let answerString = result[0].message.content.trim().replace(/\t|\n|\v|\r|\f/g, '');
553
+ if (answerString === '[]') return [];
508
554
  let jsonObj = this.fixedJsonString(answerString);
509
- if (!jsonObj.length) return []
555
+ ////修复的结果无法用程序修复,请求GPT来分析修复一下这个结果
556
+ if (!jsonObj.length){
557
+ let fixedAsk = [
558
+ {role:'system',content:'角色扮演:假设你是一位高级JSON数据分析师'},
559
+ { role: 'user', content: `请分析以下内容,严格按照${QUESTION_PROMPT_FIXED[questiontype]}的标准JSON数组结构输出。如果内容不足以提取问题和答案,请直接输出JSON空数组,无需提供参考。` },
560
+ { role: 'user', content: answerString },
561
+ ]
562
+ console.log('fixedAsk', fixedAsk)
563
+ let fixedJsonResult:any =await this.chatRequest(fixedAsk,{replyCounts:1},{})
564
+ if (fixedJsonResult.successed){
565
+ answerString = fixedJsonResult.message[0].message.content.trim().replace(/\t|\n|\v|\r|\f/g, '');
566
+ jsonObj = this.fixedJsonString(answerString);
567
+ }
568
+ if (!jsonObj.length) return []
569
+ }
510
570
  let returnItems: QuestionItem[] = [];
511
571
  try {
512
572
  // let jsonObj = JSON.parse(answerString);
@@ -541,7 +601,7 @@ export default class OpenAIGpt extends GptBase {
541
601
  questionitem.answer = (questionitem.answer + '').replace(/,|[^ABCDEFG]/g, '').split('').slice(0, 1);
542
602
  break;
543
603
  case 'multiplechoice':
544
- questionitem.answer = (questionitem.answer + '').replace(/,|[^ABCDEFG]/g, '').split('');
604
+ questionitem.answer =Array.from(new Set((questionitem.answer + '').replace(/,|[^ABCDEFG]/g, '').split('')));
545
605
  break;
546
606
  case 'trueorfalse':
547
607
  let rightItem = questionitem.choice.find((x: any) => { return x.iscorrect == 1 });
@@ -562,6 +622,8 @@ export default class OpenAIGpt extends GptBase {
562
622
  ///单选题的正确选项大于了1个
563
623
  if (rightAnswer.length === 0 || !questionitem.answer || questionitem.answer.length === 0) return null;
564
624
  }
625
+ ///判断题验证:防止没有答案的
626
+ if (questiontype == 'trueorfalse' && !questionitem.answer.length ) return null;
565
627
 
566
628
  return questionitem;
567
629
  })