doomiaichat 1.4.4 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/declare.d.ts +99 -0
- package/dist/declare.js +2 -0
- package/dist/index.d.ts +35 -1
- package/dist/index.js +239 -63
- package/dist/openai.d.ts +95 -0
- package/dist/openai.js +496 -0
- package/package.json +5 -3
- package/src/azureai.ts +53 -0
- package/src/declare.ts +105 -0
- package/src/index.ts +1 -392
- package/src/openai.ts +479 -0
- package/src/openaiprovider.ts +32 -0
- package/tsconfig.json +1 -1
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
interface ApiResult {
|
|
2
|
+
/**
|
|
3
|
+
* return the result of api called
|
|
4
|
+
* @type {boolean}
|
|
5
|
+
*/
|
|
6
|
+
'successed': boolean;
|
|
7
|
+
/**
|
|
8
|
+
* The error info
|
|
9
|
+
* @type {any}
|
|
10
|
+
* @memberof ChatReponse
|
|
11
|
+
*/
|
|
12
|
+
'error'?: any;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Api封装后的返回结果
|
|
16
|
+
*/
|
|
17
|
+
export interface ChatReponse extends ApiResult {
|
|
18
|
+
/**
|
|
19
|
+
* The name of the user in a multi-user chat
|
|
20
|
+
* @type {Array<any>}
|
|
21
|
+
* @memberof ChatReponse
|
|
22
|
+
*/
|
|
23
|
+
'message'?: Array<any>;
|
|
24
|
+
}
|
|
25
|
+
export interface OutlineSummaryItem {
|
|
26
|
+
/**
|
|
27
|
+
* The name of the user in a multi-user chat
|
|
28
|
+
* @type {Array<any>}
|
|
29
|
+
* @memberof SummaryReponse
|
|
30
|
+
*/
|
|
31
|
+
'outline'?: string;
|
|
32
|
+
'summary'?: Array<string>;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* 摘要信息
|
|
36
|
+
*/
|
|
37
|
+
export interface SummaryReponse extends ApiResult {
|
|
38
|
+
/**
|
|
39
|
+
* The name of the user in a multi-user chat
|
|
40
|
+
* @type {Array<any>}
|
|
41
|
+
* @memberof SummaryReponse
|
|
42
|
+
*/
|
|
43
|
+
'article'?: Array<OutlineSummaryItem>;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* 调用OpenAI Api的参数约定
|
|
47
|
+
*/
|
|
48
|
+
export interface OpenAIApiParameters {
|
|
49
|
+
'model'?: string;
|
|
50
|
+
'maxtoken'?: number;
|
|
51
|
+
'temperature'?: number;
|
|
52
|
+
'replyCounts'?: number;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Azure 上的OpenAI的链接参数
|
|
56
|
+
*/
|
|
57
|
+
export interface AzureOpenAIPatameters {
|
|
58
|
+
'endpoint': string;
|
|
59
|
+
'engine': string;
|
|
60
|
+
'version'?: string;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* 调用OpenAI Api的参数约定
|
|
64
|
+
*/
|
|
65
|
+
export interface FaqItem {
|
|
66
|
+
'question': string;
|
|
67
|
+
'answer'?: string;
|
|
68
|
+
'keywords'?: Array<string>;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* 调用OpenAI Api的参数约定
|
|
72
|
+
*/
|
|
73
|
+
export interface ExaminationPaperResult extends ApiResult {
|
|
74
|
+
'score': number;
|
|
75
|
+
'paper': any;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* 调用OpenAI Api的参数约定
|
|
79
|
+
*/
|
|
80
|
+
export interface QuestionItem {
|
|
81
|
+
'question': string;
|
|
82
|
+
'fullanswer'?: string;
|
|
83
|
+
'answer'?: Array<string>;
|
|
84
|
+
'choice'?: any[];
|
|
85
|
+
'score'?: number;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* 调用OpenAI Api的参数约定
|
|
89
|
+
*/
|
|
90
|
+
export interface SimilarityResult extends ApiResult {
|
|
91
|
+
'value'?: number;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* 调用OpenAI Api的参数约定
|
|
95
|
+
*/
|
|
96
|
+
export interface EmotionResult extends ApiResult {
|
|
97
|
+
'emotion'?: string;
|
|
98
|
+
}
|
|
99
|
+
export {};
|
package/dist/declare.js
ADDED
package/dist/index.d.ts
CHANGED
|
@@ -39,6 +39,12 @@ export declare class AIChat extends EventEmitter {
|
|
|
39
39
|
* @param {需要出来的数量} count
|
|
40
40
|
*/
|
|
41
41
|
getSimilarityContent(content: string, count?: number, axiosOption?: AxiosRequestConfig): Promise<ChatReponse>;
|
|
42
|
+
/**
|
|
43
|
+
* 提取内容的中心思想摘要
|
|
44
|
+
* @param content
|
|
45
|
+
* @param axiosOption
|
|
46
|
+
*/
|
|
47
|
+
getSummaryOfContent(content: string | Array<any>, axiosOption?: AxiosRequestConfig): Promise<SummaryReponse>;
|
|
42
48
|
/**
|
|
43
49
|
* 从指定的文本内容中生成相关的问答
|
|
44
50
|
* @param {*} content
|
|
@@ -46,6 +52,13 @@ export declare class AIChat extends EventEmitter {
|
|
|
46
52
|
* @param {*} axiosOption
|
|
47
53
|
* @returns
|
|
48
54
|
*/ generateQuestionsFromContent(content: string, count?: number, axiosOption?: AxiosRequestConfig): Promise<ChatReponse>;
|
|
55
|
+
/**
|
|
56
|
+
* 从指定的文本内容中生成相关的问答
|
|
57
|
+
* @param {*} content
|
|
58
|
+
* @param {*} count
|
|
59
|
+
* @param {*} axiosOption
|
|
60
|
+
* @returns
|
|
61
|
+
*/ generateQuestionsFromContent_Orgin(content: string, count?: number, promotion?: string | null, sliceslength?: number, axiosOption?: AxiosRequestConfig): Promise<ChatReponse>;
|
|
49
62
|
/**
|
|
50
63
|
* 解析Faq返回的问题
|
|
51
64
|
* @param {*} messages
|
|
@@ -97,6 +110,26 @@ export interface ChatReponse extends ApiResult {
|
|
|
97
110
|
*/
|
|
98
111
|
'message'?: Array<any>;
|
|
99
112
|
}
|
|
113
|
+
interface OutlineSummaryItem {
|
|
114
|
+
/**
|
|
115
|
+
* The name of the user in a multi-user chat
|
|
116
|
+
* @type {Array<any>}
|
|
117
|
+
* @memberof SummaryReponse
|
|
118
|
+
*/
|
|
119
|
+
'outline'?: string;
|
|
120
|
+
'summary'?: Array<string>;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* 摘要信息
|
|
124
|
+
*/
|
|
125
|
+
export interface SummaryReponse extends ApiResult {
|
|
126
|
+
/**
|
|
127
|
+
* The name of the user in a multi-user chat
|
|
128
|
+
* @type {Array<any>}
|
|
129
|
+
* @memberof SummaryReponse
|
|
130
|
+
*/
|
|
131
|
+
'article'?: Array<OutlineSummaryItem>;
|
|
132
|
+
}
|
|
100
133
|
/**
|
|
101
134
|
* 调用OpenAI Api的参数约定
|
|
102
135
|
*/
|
|
@@ -126,8 +159,9 @@ export interface ExaminationPaperResult extends ApiResult {
|
|
|
126
159
|
*/
|
|
127
160
|
export interface QuestionItem {
|
|
128
161
|
'question': string;
|
|
162
|
+
'fullanswer'?: string;
|
|
129
163
|
'answer'?: Array<string>;
|
|
130
|
-
'choice'?:
|
|
164
|
+
'choice'?: any[];
|
|
131
165
|
'score'?: number;
|
|
132
166
|
}
|
|
133
167
|
/**
|
package/dist/index.js
CHANGED
|
@@ -12,15 +12,19 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
12
12
|
exports.AIChat = void 0;
|
|
13
13
|
const openai_1 = require("openai");
|
|
14
14
|
const events_1 = require("events");
|
|
15
|
-
const SECTION_LENGTH =
|
|
16
|
-
const MESSAGE_LENGTH =
|
|
15
|
+
const SECTION_LENGTH = 1600; ///每2400个字符分成一组
|
|
16
|
+
const MESSAGE_LENGTH = 1; ///每次送8句话给openai 进行解析,送多了,会报错
|
|
17
17
|
//请将答案放在最后,标记为答案:()
|
|
18
18
|
const QUESTION_TEXT_MAPPING = {
|
|
19
|
-
singlechoice: '
|
|
20
|
-
multiplechoice: '
|
|
21
|
-
trueorfalse: '
|
|
22
|
-
completion: '
|
|
19
|
+
singlechoice: '你是一名专业的出题老师,根据以下内容,生成@ITEMCOUNT@道单选题,每道题目4个选项,每道题的选项中的元素用大写字母ABCD开头,每道题一个正确答案,输出结果必须是JSON数组并按照[{"question":"","choice":[],"answer":[]}]的结构输出',
|
|
20
|
+
multiplechoice: '你是一名专业的出题老师,根据以下内容,请生成@ITEMCOUNT@道多选题,提供4个选项,每道题的选项中的元素用大写字母ABCD开头,每道题的答案至少有两个选项,输出结果必须是JSON数组并按照[{"question":"","choice":[],"answer":[]}]的结构输出',
|
|
21
|
+
trueorfalse: '你是一名专业的出题老师,根据以下内容,请生成@ITEMCOUNT@道判断题,每道题正确和错误两个选项,输出结果必须是JSON数组并按照[{"question":"","choice":["A.正确","B.错误"],"answer":[]}]的结构输出',
|
|
22
|
+
completion: '你是一名专业的出题老师,根据以下内容,请生成@ITEMCOUNT@道填空题和对应答案,输出结果必须是JSON数组并按照[{"question":"","answer":["填空答案1","填空答案2"]}]的结构输出' //请将答案放在最后,标记为答案:()
|
|
23
23
|
};
|
|
24
|
+
// singlechoice: '你是一名专业的培训老师,根据以下内容,生成@ITEMCOUNT@道单选题,每道题目4个选项,每道题的选项中的元素用大写字母ABCD开头,每道题的只能有一个正确答案,最终结果按照[{"question":"","choice":[],"answer":[]}]的JSON数组输出',
|
|
25
|
+
// multiplechoice: '你是一名专业的培训老师,根据以下内容,请生成@ITEMCOUNT@道多选题,提供4个选项,答案至少1个以上选项,请按照{"question":"","choice":[],"answer":[]}的JSON结构输出,choice中的元素用大写字母ABCD开头,answer数组中包含正确答案选项', //请将答案放在最后,标记为答案:()
|
|
26
|
+
// trueorfalse: '你是一名专业的培训老师,根据以下内容,请生成@ITEMCOUNT@道判断题,请按照{"question":"","choice":["A.正确","B.错误"],"answer":[]}的JSON结构输出,answer数组中包含一个元素,"正确"或"错误"', //标记为答案:(正确或错误)
|
|
27
|
+
// completion: '你是一名专业的培训老师,根据以下内容,请生成@ITEMCOUNT@道填空题,每道题目1个填空,请按照{"question":"","answer":[]}的JSON结构输出,answer数组中包含填空答案' //请将答案放在最后,标记为答案:()
|
|
24
28
|
const QUESTION_TYPE = ['singlechoice', 'multiplechoice', 'trueorfalse', 'completion'];
|
|
25
29
|
class AIChat extends events_1.EventEmitter {
|
|
26
30
|
/**
|
|
@@ -32,7 +36,7 @@ class AIChat extends events_1.EventEmitter {
|
|
|
32
36
|
super();
|
|
33
37
|
this.chatRobot = new openai_1.OpenAIApi(new openai_1.Configuration({ apiKey }));
|
|
34
38
|
this.chatModel = apiOption.model || 'gpt-3.5-turbo';
|
|
35
|
-
this.maxtoken = apiOption.maxtoken ||
|
|
39
|
+
this.maxtoken = apiOption.maxtoken || 2048;
|
|
36
40
|
this.temperature = apiOption.temperature || 0.9;
|
|
37
41
|
}
|
|
38
42
|
/**
|
|
@@ -59,6 +63,7 @@ class AIChat extends events_1.EventEmitter {
|
|
|
59
63
|
return { successed: true, message: response.data.choices };
|
|
60
64
|
}
|
|
61
65
|
catch (error) {
|
|
66
|
+
console.log('result is error ', error);
|
|
62
67
|
return { successed: false, error };
|
|
63
68
|
}
|
|
64
69
|
});
|
|
@@ -135,6 +140,36 @@ class AIChat extends events_1.EventEmitter {
|
|
|
135
140
|
return { successed: true, message: replys };
|
|
136
141
|
});
|
|
137
142
|
}
|
|
143
|
+
/**
|
|
144
|
+
* 提取内容的中心思想摘要
|
|
145
|
+
* @param content
|
|
146
|
+
* @param axiosOption
|
|
147
|
+
*/
|
|
148
|
+
getSummaryOfContent(content, axiosOption = {}) {
|
|
149
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
150
|
+
const arrContent = typeof (content) == 'string' ? this.splitLongText(content) : content;
|
|
151
|
+
let summary = [];
|
|
152
|
+
while (arrContent.length > 0) {
|
|
153
|
+
let subarray = arrContent.slice(0, MESSAGE_LENGTH);
|
|
154
|
+
subarray.push({ role: 'user', content: '根据上述内容精简,提炼内容提纲及摘要内容,每项摘要内容尽量精简数据化不超过100字,结果严格按照[{outline:"提纲标题","summary":["摘要内容1","摘要内容2","摘要内容3"]}]的JSON结构输出' });
|
|
155
|
+
let result = yield this.chatRequest(subarray, {}, axiosOption);
|
|
156
|
+
if (result.successed && result.message) {
|
|
157
|
+
try {
|
|
158
|
+
// console.log('result.message[0].content', result.message[0].message.content)
|
|
159
|
+
let jsonObjItems = JSON.parse(result.message[0].message.content);
|
|
160
|
+
if (Array.isArray(jsonObjItems))
|
|
161
|
+
summary = summary.concat(jsonObjItems);
|
|
162
|
+
}
|
|
163
|
+
catch (error) {
|
|
164
|
+
console.log('result.message[0].content', error);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
////删除已经处理的文本
|
|
168
|
+
arrContent.splice(0, MESSAGE_LENGTH);
|
|
169
|
+
}
|
|
170
|
+
return { successed: true, article: summary };
|
|
171
|
+
});
|
|
172
|
+
}
|
|
138
173
|
/**
|
|
139
174
|
* 从指定的文本内容中生成相关的问答
|
|
140
175
|
* @param {*} content
|
|
@@ -144,19 +179,23 @@ class AIChat extends events_1.EventEmitter {
|
|
|
144
179
|
*/ //并在答案末尾处必须给出答案内容中的关键词
|
|
145
180
|
generateQuestionsFromContent(content, count = 1, axiosOption = {}) {
|
|
146
181
|
return __awaiter(this, void 0, void 0, function* () {
|
|
147
|
-
let arrContent = this.splitLongText(content);
|
|
182
|
+
let arrContent = this.splitLongText(content, 300);
|
|
148
183
|
///没20句话分为一组,适应大文件内容多次请求组合结果
|
|
149
184
|
///每一句话需要产生的题目
|
|
150
185
|
let questions4EverySentense = count / arrContent.length; //Math.ceil(arrContent.length / 20);
|
|
151
186
|
let faqs = [], gotted = 0;
|
|
152
187
|
while (arrContent.length > 0 && gotted < count) {
|
|
188
|
+
questions4EverySentense = (count - gotted) / arrContent.length;
|
|
153
189
|
////每次最多送MESSAGE_LENGTH句话给openai
|
|
154
|
-
let subarray = arrContent.slice(0,
|
|
190
|
+
let subarray = arrContent.slice(0, 4);
|
|
155
191
|
let itemCount = Math.min(Math.ceil(subarray.length * questions4EverySentense), count - gotted);
|
|
156
192
|
//subarray.push({ role: 'user', content:'请根据上述内容,给出一道提问与答案以及答案关键词,按照先问题内容,再标准答案,再关键词的顺序输出,关键词之间用、分开'})
|
|
157
|
-
subarray.
|
|
158
|
-
|
|
193
|
+
subarray.unshift({ role: 'system', content: `你是一位专业培训师,从以下内容中提取${itemCount}条提问及答案,并从答案内容提取出至少2个关键词,最终结果按照[{"question":"提问内容","answer":"答案内容","keywords":["关键词1","关键词2"]}]的JSON数组结构输出。` });
|
|
194
|
+
//subarray.unshift({ role: 'system', content: `你是一位专业程序开发工程师,根据以下内容,按照[{"question":"问题内容","answer":"答案内容","keywords":["关键词1","关键词2"]}]JSON数组结构,给出${itemCount}条提问问题及答案以及答案关键词` })
|
|
195
|
+
console.log('subarray', subarray);
|
|
196
|
+
let result = yield this.chatRequest(subarray, { replyCounts: 1 }, axiosOption);
|
|
159
197
|
if (result.successed && result.message) {
|
|
198
|
+
// console.log('result is ', result.message[0].message.content)
|
|
160
199
|
let msgs = this.pickUpFaqContent(result.message);
|
|
161
200
|
if (msgs.length) {
|
|
162
201
|
///对外发送检出问答题的信号
|
|
@@ -167,7 +206,7 @@ class AIChat extends events_1.EventEmitter {
|
|
|
167
206
|
}
|
|
168
207
|
}
|
|
169
208
|
////删除已经处理的文本
|
|
170
|
-
arrContent.splice(0,
|
|
209
|
+
arrContent.splice(0, 4);
|
|
171
210
|
}
|
|
172
211
|
arrContent = []; /// 释放内存
|
|
173
212
|
///发出信号,解析完毕
|
|
@@ -175,27 +214,86 @@ class AIChat extends events_1.EventEmitter {
|
|
|
175
214
|
return { successed: true, message: faqs };
|
|
176
215
|
});
|
|
177
216
|
}
|
|
217
|
+
/**
|
|
218
|
+
* 从指定的文本内容中生成相关的问答
|
|
219
|
+
* @param {*} content
|
|
220
|
+
* @param {*} count
|
|
221
|
+
* @param {*} axiosOption
|
|
222
|
+
* @returns
|
|
223
|
+
*/ //并在答案末尾处必须给出答案内容中的关键词
|
|
224
|
+
generateQuestionsFromContent_Orgin(content, count = 1, promotion = null, sliceslength = SECTION_LENGTH, axiosOption = {}) {
|
|
225
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
226
|
+
let arrContent = this.splitLongText(content, sliceslength || 300);
|
|
227
|
+
///没20句话分为一组,适应大文件内容多次请求组合结果
|
|
228
|
+
///每一句话需要产生的题目
|
|
229
|
+
let questions4EverySentense = count / arrContent.length; //Math.ceil(arrContent.length / 20);
|
|
230
|
+
let faqs = [], gotted = 0;
|
|
231
|
+
while (arrContent.length > 0 && gotted < count) {
|
|
232
|
+
questions4EverySentense = (count - gotted) / arrContent.length;
|
|
233
|
+
////每次最多送MESSAGE_LENGTH句话给openai
|
|
234
|
+
let subarray = arrContent.slice(0, 4);
|
|
235
|
+
let itemCount = Math.min(Math.ceil(subarray.length * questions4EverySentense), count - gotted);
|
|
236
|
+
subarray.unshift({ role: 'system', content: promotion || `你是一位专业培训师,从以下内容中提取${itemCount}条提问及答案,并从答案内容提取出至少2个关键词,最终结果按照[{"question":"提问内容","answer":"答案内容","keywords":["关键词1","关键词2"]}]的JSON数组结构输出。` });
|
|
237
|
+
let result = yield this.chatRequest(subarray, { replyCounts: 1 }, axiosOption);
|
|
238
|
+
if (result.successed && result.message) {
|
|
239
|
+
let answer = result.message.map(i => { return i.message.content.trim().replace(/\t|\n|\v|\r|\f/g, ''); });
|
|
240
|
+
faqs = faqs.concat(answer);
|
|
241
|
+
}
|
|
242
|
+
////删除已经处理的文本
|
|
243
|
+
arrContent.splice(0, 4);
|
|
244
|
+
}
|
|
245
|
+
arrContent = []; /// 释放内存
|
|
246
|
+
return { successed: true, message: faqs };
|
|
247
|
+
});
|
|
248
|
+
}
|
|
178
249
|
/**
|
|
179
250
|
* 解析Faq返回的问题
|
|
180
251
|
* @param {*} messages
|
|
181
252
|
* @returns
|
|
182
253
|
*/
|
|
183
254
|
pickUpFaqContent(messages) {
|
|
184
|
-
let
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
255
|
+
let answerString = messages[0].message.content.trim().replace(/\t|\n|\v|\r|\f/g, '');
|
|
256
|
+
const firstsybmol = answerString.indexOf("["); ////必须过滤出来数组
|
|
257
|
+
const lastsybmol = answerString.lastIndexOf("]");
|
|
258
|
+
console.log('answerString', answerString);
|
|
259
|
+
if (firstsybmol < 0 || lastsybmol < 0 || lastsybmol <= firstsybmol)
|
|
260
|
+
return [];
|
|
261
|
+
answerString = answerString.substr(firstsybmol, lastsybmol - firstsybmol + 1);
|
|
262
|
+
console.log('answerString', answerString);
|
|
263
|
+
try {
|
|
264
|
+
//let jsonObj = JSON.parse(answerString);
|
|
265
|
+
let jsonObj = eval(answerString);
|
|
266
|
+
jsonObj.map((item) => {
|
|
267
|
+
let realKeyword = [];
|
|
268
|
+
let keywords = (item.keywords + '').split(',');
|
|
269
|
+
let answer = item.answer || '';
|
|
270
|
+
for (const k of keywords) {
|
|
271
|
+
if (k && answer.indexOf(k) >= 0)
|
|
272
|
+
realKeyword.push(k);
|
|
190
273
|
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
274
|
+
item.keywords = realKeyword;
|
|
275
|
+
return item;
|
|
276
|
+
});
|
|
277
|
+
return jsonObj;
|
|
278
|
+
}
|
|
279
|
+
catch (err) {
|
|
280
|
+
console.log('JSON error', err);
|
|
281
|
+
return [];
|
|
282
|
+
}
|
|
283
|
+
// let replys = messages.map(item => {
|
|
284
|
+
// let content = item.message.content.trim().replace(/\t|\n|\v|\r|\f/g, '');
|
|
285
|
+
// try {
|
|
286
|
+
// let jsonObj = JSON.parse(content);
|
|
287
|
+
// if (!Array.isArray(jsonObj.keywords)) {
|
|
288
|
+
// jsonObj.keywords = (jsonObj.keywords || '').split(',')
|
|
289
|
+
// }
|
|
290
|
+
// return jsonObj ;
|
|
291
|
+
// } catch (err) {
|
|
292
|
+
// console.log('JSON error', content, err)
|
|
293
|
+
// return {question:null};
|
|
294
|
+
// }
|
|
295
|
+
// })
|
|
296
|
+
// return replys.filter(n => { return n.question != null });
|
|
199
297
|
}
|
|
200
298
|
/**
|
|
201
299
|
* 从指定的文本内容中生成一张试卷
|
|
@@ -209,7 +307,7 @@ class AIChat extends events_1.EventEmitter {
|
|
|
209
307
|
generateExaminationPaperFromContent(content, paperOption = {}, axiosOption = {}) {
|
|
210
308
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
|
|
211
309
|
return __awaiter(this, void 0, void 0, function* () {
|
|
212
|
-
let arrContent = this.splitLongText(content);
|
|
310
|
+
let arrContent = this.splitLongText(content, 1024);
|
|
213
311
|
let sectionCount = {
|
|
214
312
|
singlechoice: (((_a = paperOption.singlechoice) === null || _a === void 0 ? void 0 : _a.count) || 0) / arrContent.length,
|
|
215
313
|
multiplechoice: (((_b = paperOption.multiplechoice) === null || _b === void 0 ? void 0 : _b.count) || 0) / arrContent.length,
|
|
@@ -245,13 +343,13 @@ class AIChat extends events_1.EventEmitter {
|
|
|
245
343
|
///还需要抓取题目
|
|
246
344
|
if (remainCount[key] > 0) {
|
|
247
345
|
noMoreQuestionRetrive = false;
|
|
248
|
-
subarray.push({ role: 'user', content: QUESTION_TEXT_MAPPING[key] });
|
|
249
346
|
let itemCount = Math.min(remainCount[key], Math.ceil(subarray.length * sectionCount[key]));
|
|
250
|
-
|
|
251
|
-
|
|
347
|
+
subarray.unshift({ role: 'system', content: QUESTION_TEXT_MAPPING[key].replace('@ITEMCOUNT@', itemCount) });
|
|
348
|
+
console.log(subarray);
|
|
349
|
+
let result = yield this.chatRequest(subarray, { replyCounts: 1 }, axiosOption);
|
|
252
350
|
if (result.successed && result.message) {
|
|
253
351
|
//console.log('paper result', key, result.message.length)
|
|
254
|
-
let pickedQuestions = this.pickUpQuestions(result.message, key, ITEM_SCORE[key]);
|
|
352
|
+
let pickedQuestions = this.pickUpQuestions(result.message, itemCount, key, ITEM_SCORE[key]);
|
|
255
353
|
if (pickedQuestions.length) {
|
|
256
354
|
///对外发送检出题目的信号
|
|
257
355
|
this.emit('parseout', { type: 'question', name: key, items: pickedQuestions });
|
|
@@ -260,7 +358,8 @@ class AIChat extends events_1.EventEmitter {
|
|
|
260
358
|
totalscore = totalscore + pickedQuestions.length * ITEM_SCORE[key];
|
|
261
359
|
}
|
|
262
360
|
}
|
|
263
|
-
subarray.splice(
|
|
361
|
+
subarray.splice(0, 1); ///把第一个角色定位的问法删除
|
|
362
|
+
// subarray.splice(subarray.length - 1, 1); ///把第一个角色定位的问法删除
|
|
264
363
|
}
|
|
265
364
|
}
|
|
266
365
|
////删除已经处理的文本
|
|
@@ -276,44 +375,113 @@ class AIChat extends events_1.EventEmitter {
|
|
|
276
375
|
* @param {*} result
|
|
277
376
|
*
|
|
278
377
|
*/
|
|
279
|
-
pickUpQuestions(result, questiontype, score = 1) {
|
|
280
|
-
let
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
378
|
+
pickUpQuestions(result, count, questiontype, score = 1) {
|
|
379
|
+
let answerString = result[0].message.content.trim().replace(/\t|\n|\v|\r|\f/g, '');
|
|
380
|
+
const firstsybmol = answerString.indexOf("["); ////必须过滤出来数组
|
|
381
|
+
const lastsybmol = answerString.lastIndexOf("]");
|
|
382
|
+
console.log('answerString', answerString);
|
|
383
|
+
if (firstsybmol < 0 || lastsybmol < 0 || lastsybmol <= firstsybmol)
|
|
384
|
+
return [];
|
|
385
|
+
answerString = answerString.substr(firstsybmol, lastsybmol - firstsybmol + 1);
|
|
386
|
+
console.log('answerString', answerString);
|
|
387
|
+
let returnItems = [];
|
|
388
|
+
try {
|
|
389
|
+
let jsonObj = JSON.parse(answerString);
|
|
390
|
+
returnItems = jsonObj.map((questionitem) => {
|
|
391
|
+
console.log('answer item', questionitem);
|
|
392
|
+
if (questionitem.choice && Array.isArray(questionitem.choice) && questiontype != 'completion') {
|
|
393
|
+
questionitem.fullanswer = (questionitem.answer + '').replace(/,|[^ABCDE]/g, '');
|
|
394
|
+
questionitem.score = score;
|
|
395
|
+
if (questionitem.choice) {
|
|
396
|
+
questionitem.choice = questionitem.choice.map((item, index) => {
|
|
397
|
+
let seqNo = String.fromCharCode(65 + index);
|
|
398
|
+
let correctReg = new RegExp(`${seqNo}.|${seqNo}`, 'ig');
|
|
399
|
+
//let answer = jsonObj.fullanswer
|
|
400
|
+
return {
|
|
401
|
+
id: seqNo,
|
|
402
|
+
content: item.replace(correctReg, '').trim(),
|
|
403
|
+
iscorrect: (questionitem.fullanswer || '').indexOf(seqNo) >= 0 ? 1 : 0
|
|
404
|
+
//|| jsonObj.fullanswer.indexOf(m))
|
|
405
|
+
};
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
///如果是非判断题,题目的选项数量小于2 ,则无效
|
|
409
|
+
///如果是判断题,题目的选项必须=2
|
|
410
|
+
if (!questionitem.choice || (questiontype != 'trueorfalse' && questionitem.choice.length < 3) || (questiontype == 'trueorfalse' && questionitem.choice.length != 2)) {
|
|
411
|
+
return null;
|
|
412
|
+
}
|
|
297
413
|
}
|
|
298
414
|
switch (questiontype) {
|
|
299
415
|
case 'singlechoice':
|
|
300
|
-
|
|
416
|
+
questionitem.answer = (questionitem.answer + '').replace(/,|[^ABCDEFG]/g, '').split('').slice(0, 1);
|
|
301
417
|
break;
|
|
302
418
|
case 'multiplechoice':
|
|
303
|
-
|
|
419
|
+
questionitem.answer = (questionitem.answer + '').replace(/,|[^ABCDEFG]/g, '').split('');
|
|
304
420
|
break;
|
|
305
421
|
case 'trueorfalse':
|
|
306
|
-
|
|
422
|
+
questionitem.answer = [(questionitem.answer + '').indexOf('正确') >= 0 ? 'A' : 'B'];
|
|
307
423
|
break;
|
|
308
424
|
}
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
425
|
+
///单选题验证
|
|
426
|
+
if (questiontype == 'singlechoice') {
|
|
427
|
+
let rightAnswer = questionitem.choice ? questionitem.choice.filter(item => { return item.iscorrect === 1; }) : [];
|
|
428
|
+
///单选题的正确选项大于了1个
|
|
429
|
+
if (rightAnswer.length != 1 || !questionitem.answer || questionitem.answer.length !== 1)
|
|
430
|
+
return null;
|
|
431
|
+
///正确选项和答案不一致
|
|
432
|
+
if (rightAnswer[0].id.toUpperCase() != (questionitem.answer[0] || '').toUpperCase())
|
|
433
|
+
return null;
|
|
434
|
+
}
|
|
435
|
+
///多选题验证
|
|
436
|
+
if (questiontype == 'multiplechoice') {
|
|
437
|
+
let rightAnswer = questionitem.choice ? questionitem.choice.filter(item => { return item.iscorrect === 1; }) : [];
|
|
438
|
+
///单选题的正确选项大于了1个
|
|
439
|
+
if (rightAnswer.length === 0 || !questionitem.answer || questionitem.answer.length === 0)
|
|
440
|
+
return null;
|
|
441
|
+
}
|
|
442
|
+
return questionitem;
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
catch (err) {
|
|
446
|
+
console.log('error happened:', err);
|
|
447
|
+
}
|
|
448
|
+
return returnItems.filter(i => { return i != null; }).slice(0, count);
|
|
449
|
+
// let item = result.map(m => {
|
|
450
|
+
// ////防止输出的JSON格式不合法
|
|
451
|
+
// try {
|
|
452
|
+
// let jsonObj = JSON.parse(m.message.content)
|
|
453
|
+
// jsonObj.score = score;
|
|
454
|
+
// if (jsonObj.choice && Array.isArray(jsonObj.choice) && questiontype != 'completion') {
|
|
455
|
+
// jsonObj.fullanswer = (jsonObj.answer + '').replace(/,|[^ABCDE]/g, '');
|
|
456
|
+
// jsonObj.choice = jsonObj.choice.map((item:string, index:number) => {
|
|
457
|
+
// let seqNo = String.fromCharCode(65 + index);
|
|
458
|
+
// let correctReg = new RegExp(`${seqNo}.|${seqNo}`, 'ig')
|
|
459
|
+
// //let answer = jsonObj.fullanswer
|
|
460
|
+
// return {
|
|
461
|
+
// id: seqNo,
|
|
462
|
+
// content: item.replace(correctReg, '').trim(),
|
|
463
|
+
// iscorrect: (jsonObj.fullanswer.indexOf(seqNo) >= 0 || jsonObj.fullanswer.indexOf(m)) >= 0 ? 1 : 0
|
|
464
|
+
// }
|
|
465
|
+
// })
|
|
466
|
+
// }
|
|
467
|
+
// switch (questiontype) {
|
|
468
|
+
// case 'singlechoice':
|
|
469
|
+
// jsonObj.answer = (jsonObj.answer + '').replace(/,|[^ABCDEFG]/g, '').split('').slice(0, 1);
|
|
470
|
+
// break;
|
|
471
|
+
// case 'multiplechoice':
|
|
472
|
+
// jsonObj.answer = (jsonObj.answer + '').replace(/,|[^ABCDEFG]/g, '').split('');
|
|
473
|
+
// break;
|
|
474
|
+
// case 'trueorfalse':
|
|
475
|
+
// jsonObj.answer = [(jsonObj.answer + '').indexOf('正确') >= 0 ? 'A' : 'B']
|
|
476
|
+
// break;
|
|
477
|
+
// }
|
|
478
|
+
// return jsonObj;
|
|
479
|
+
// } catch (err) {
|
|
480
|
+
// console.log('error happened:', err);
|
|
481
|
+
// return null;
|
|
482
|
+
// }
|
|
483
|
+
// })
|
|
484
|
+
// return item.filter(i => { return i != null; });
|
|
317
485
|
}
|
|
318
486
|
/**
|
|
319
487
|
* 将一段很长的文本,按1024长度来划分到多个中
|
|
@@ -322,10 +490,18 @@ class AIChat extends events_1.EventEmitter {
|
|
|
322
490
|
splitLongText(content, len = SECTION_LENGTH) {
|
|
323
491
|
let start = 0, message = [], length = content.length;
|
|
324
492
|
while (start < length) {
|
|
325
|
-
|
|
493
|
+
let realLength = len;
|
|
494
|
+
////以句号或引号进行分段,不要随意截取
|
|
495
|
+
for (let i = start + len; i >= start; i--) {
|
|
496
|
+
if (/[。”"??]/.test(content[i] + '')) {
|
|
497
|
+
realLength = i - start + 1;
|
|
498
|
+
break;
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
const subtext = content.substr(start, realLength);
|
|
326
502
|
if (subtext)
|
|
327
503
|
message.push({ role: 'user', content: subtext });
|
|
328
|
-
start += len;
|
|
504
|
+
start += realLength || len;
|
|
329
505
|
}
|
|
330
506
|
return message;
|
|
331
507
|
}
|