wingbot 3.42.0 → 3.43.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/package.json +1 -1
- package/plugins/ai.wingbot.slotsContinue/plugin.js +8 -2
- package/plugins/plugins.json +13 -1
- package/src/Ai.js +77 -3
- package/src/Processor.js +1 -0
- package/src/Responder.js +15 -4
- package/src/systemEntities/email.js +6 -1
- package/src/systemEntities/phone.js +5 -0
- package/src/wingbot/CachedModel.js +34 -6
- package/src/wingbot/CustomEntityDetectionModel.js +16 -9
- package/src/wingbot/WingbotModel.js +1 -7
package/package.json
CHANGED
|
@@ -11,10 +11,12 @@ const { vars } = require('../../src/utils/stateVariables');
|
|
|
11
11
|
/**
|
|
12
12
|
* @param {object} params
|
|
13
13
|
* @param {string} [params.skip]
|
|
14
|
+
* @param {string} [params.fill]
|
|
14
15
|
* @returns {SlotsResolver}
|
|
15
16
|
*/
|
|
16
17
|
function slotsContinue ({
|
|
17
|
-
skip
|
|
18
|
+
skip,
|
|
19
|
+
fill
|
|
18
20
|
}) {
|
|
19
21
|
|
|
20
22
|
/**
|
|
@@ -39,6 +41,10 @@ function slotsContinue ({
|
|
|
39
41
|
.split(',')
|
|
40
42
|
.map((e) => e.trim());
|
|
41
43
|
|
|
44
|
+
const fillEntities = compileWithState(req, res, fill)
|
|
45
|
+
.split(',')
|
|
46
|
+
.map((e) => e.trim());
|
|
47
|
+
|
|
42
48
|
const clear = {};
|
|
43
49
|
|
|
44
50
|
slotState = slotState.map((s) => {
|
|
@@ -52,7 +58,7 @@ function slotsContinue ({
|
|
|
52
58
|
return { ...s, s: StepState.INITIALIZED };
|
|
53
59
|
}
|
|
54
60
|
|
|
55
|
-
return s.e === step.entity
|
|
61
|
+
return s.e === step.entity || fillEntities.includes(s.e)
|
|
56
62
|
? { ...s, s: StepState.FILLED }
|
|
57
63
|
: s;
|
|
58
64
|
});
|
package/plugins/plugins.json
CHANGED
|
@@ -415,7 +415,7 @@
|
|
|
415
415
|
{
|
|
416
416
|
"type": "select",
|
|
417
417
|
"name": "type",
|
|
418
|
-
"label": "
|
|
418
|
+
"label": "Type",
|
|
419
419
|
"options": [
|
|
420
420
|
{ "label": "Required", "value": "req" },
|
|
421
421
|
{ "label": "Multi value", "value": "mul" },
|
|
@@ -454,6 +454,18 @@
|
|
|
454
454
|
"validations": [
|
|
455
455
|
{ "type": "regexp", "value": "^\\s*@[a-zA-Z0-9-]+\\s*(,\\s*@[a-zA-Z0-9-]+\\s*)*$", "message": "the entity for the slot filling should be valid" }
|
|
456
456
|
]
|
|
457
|
+
},
|
|
458
|
+
{
|
|
459
|
+
"name": "fill",
|
|
460
|
+
"label": "Skip entity (mark as filled)",
|
|
461
|
+
"type": "text",
|
|
462
|
+
"validations": [
|
|
463
|
+
{
|
|
464
|
+
"type": "regexp",
|
|
465
|
+
"value": "^(\\s*@[a-zA-Z0-9-]+\\s*,?)*$",
|
|
466
|
+
"message": "the entity to skip should be valid"
|
|
467
|
+
}
|
|
468
|
+
]
|
|
457
469
|
}
|
|
458
470
|
],
|
|
459
471
|
"items": [
|
package/src/Ai.js
CHANGED
|
@@ -7,12 +7,15 @@ const { WingbotModel } = require('./wingbot');
|
|
|
7
7
|
const AiMatching = require('./AiMatching');
|
|
8
8
|
const { vars } = require('./utils/stateVariables');
|
|
9
9
|
const { deepEqual } = require('./utils/deepMapTools');
|
|
10
|
+
const systemEntities = require('./systemEntities');
|
|
10
11
|
const CustomEntityDetectionModel = require('./wingbot/CustomEntityDetectionModel');
|
|
11
12
|
|
|
12
13
|
const DEFAULT_PREFIX = 'default';
|
|
13
14
|
|
|
14
15
|
let uq = 1;
|
|
15
16
|
|
|
17
|
+
/** @typedef {import('./AiMatching').Compare} Compare */
|
|
18
|
+
|
|
16
19
|
/**
|
|
17
20
|
* @typedef {object} EntityExpression
|
|
18
21
|
* @prop {string} entity - the requested entity
|
|
@@ -43,6 +46,8 @@ let uq = 1;
|
|
|
43
46
|
/** @typedef {import('./Responder')} Responder */
|
|
44
47
|
/** @typedef {import('./wingbot/CachedModel').Result} Result */
|
|
45
48
|
/** @typedef {import('./wingbot/CustomEntityDetectionModel').Phrases} Phrases */
|
|
49
|
+
/** @typedef {import('./wingbot/CustomEntityDetectionModel').EntityDetector} EntityDetector */
|
|
50
|
+
/** @typedef {import('./wingbot/CustomEntityDetectionModel').DetectorOptions} DetectorOptions */
|
|
46
51
|
|
|
47
52
|
/**
|
|
48
53
|
* @class Ai
|
|
@@ -50,8 +55,20 @@ let uq = 1;
|
|
|
50
55
|
class Ai {
|
|
51
56
|
|
|
52
57
|
constructor () {
|
|
58
|
+
/**
|
|
59
|
+
* @private
|
|
60
|
+
* @type {Map<string,CustomEntityDetectionModel>}
|
|
61
|
+
*/
|
|
53
62
|
this._keyworders = new Map();
|
|
54
63
|
|
|
64
|
+
/**
|
|
65
|
+
* @private
|
|
66
|
+
* @type {Map<string,[string,EntityDetector|RegExp,DetectorOptions]>}
|
|
67
|
+
*/
|
|
68
|
+
this._detectors = new Map(
|
|
69
|
+
systemEntities.map((a) => [a[0], a])
|
|
70
|
+
);
|
|
71
|
+
|
|
55
72
|
/**
|
|
56
73
|
* Upper threshold - for match method and for navigate method
|
|
57
74
|
*
|
|
@@ -157,29 +174,86 @@ class Ai {
|
|
|
157
174
|
/**
|
|
158
175
|
* Registers Wingbot AI model
|
|
159
176
|
*
|
|
160
|
-
* @template T
|
|
177
|
+
* @template {CustomEntityDetectionModel} T
|
|
161
178
|
* @param {string|WingbotModel|T} model - wingbot model name or AI plugin
|
|
162
179
|
* @param {string} prefix - model prefix
|
|
163
180
|
*
|
|
164
|
-
* @returns {
|
|
181
|
+
* @returns {T}
|
|
165
182
|
* @memberOf Ai
|
|
166
183
|
*/
|
|
167
184
|
register (model, prefix = 'default') {
|
|
185
|
+
/** @type {T} */
|
|
168
186
|
let modelObj;
|
|
169
187
|
|
|
170
188
|
if (typeof model === 'string') {
|
|
189
|
+
// @ts-ignore
|
|
171
190
|
modelObj = new WingbotModel({
|
|
172
191
|
model
|
|
173
192
|
}, this.logger);
|
|
174
193
|
} else {
|
|
194
|
+
// @ts-ignore
|
|
175
195
|
modelObj = model;
|
|
176
196
|
}
|
|
177
197
|
|
|
178
198
|
this._keyworders.set(prefix, modelObj);
|
|
179
199
|
|
|
200
|
+
for (const entityArgs of this._detectors.values()) {
|
|
201
|
+
modelObj.setEntityDetector(...entityArgs);
|
|
202
|
+
}
|
|
203
|
+
|
|
180
204
|
return modelObj;
|
|
181
205
|
}
|
|
182
206
|
|
|
207
|
+
/**
|
|
208
|
+
*
|
|
209
|
+
* @param {string} name
|
|
210
|
+
* @param {EntityDetector|RegExp} detector
|
|
211
|
+
* @param {object} [options]
|
|
212
|
+
* @param {boolean} [options.anonymize] - if true, value will not be sent to NLP
|
|
213
|
+
* @param {Function|string} [options.extractValue] - entity extractor
|
|
214
|
+
* @param {boolean} [options.matchWholeWords] - match whole words at regular expression
|
|
215
|
+
* @param {boolean} [options.replaceDiacritics] - keep diacritics when matching regexp
|
|
216
|
+
* @param {string[]} [options.dependencies] - array of dependent entities
|
|
217
|
+
* @param {boolean} [options.clearOverlaps] - let longer entities from NLP to replace entity
|
|
218
|
+
* @returns {this}
|
|
219
|
+
*/
|
|
220
|
+
registerEntityDetector (name, detector, options = {}) {
|
|
221
|
+
const useOptions = { clearOverlaps: true, ...options };
|
|
222
|
+
|
|
223
|
+
this._detectors.set(name, [name, detector, useOptions]);
|
|
224
|
+
|
|
225
|
+
for (const model of this._keyworders.values()) {
|
|
226
|
+
model.setEntityDetector(name, detector, useOptions);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return this;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Sets options to entity detector.
|
|
234
|
+
* Useful for disabling anonymization of local system entities.
|
|
235
|
+
*
|
|
236
|
+
* @param {string} name
|
|
237
|
+
* @param {object} options
|
|
238
|
+
* @param {boolean} [options.anonymize]
|
|
239
|
+
* @returns {this}
|
|
240
|
+
* @example
|
|
241
|
+
*
|
|
242
|
+
* ai.register('wingbot-model-name')
|
|
243
|
+
* .setDetectorOptions('phone', { anonymize: false })
|
|
244
|
+
* .setDetectorOptions('email', { anonymize: false })
|
|
245
|
+
*/
|
|
246
|
+
configureEntityDetector (name, options) {
|
|
247
|
+
if (!this._detectors.has(name)) {
|
|
248
|
+
throw new Error(`Can't set entity detector options. Entity "${name}" does not exist.`);
|
|
249
|
+
}
|
|
250
|
+
Object.assign(this._detectors.get(name)[2], options);
|
|
251
|
+
for (const model of this._keyworders.values()) {
|
|
252
|
+
model.setDetectorOptions(name, options);
|
|
253
|
+
}
|
|
254
|
+
return this;
|
|
255
|
+
}
|
|
256
|
+
|
|
183
257
|
/**
|
|
184
258
|
* Remove registered model
|
|
185
259
|
*
|
|
@@ -194,7 +268,7 @@ class Ai {
|
|
|
194
268
|
*
|
|
195
269
|
* @param {string} prefix - model prefix
|
|
196
270
|
*
|
|
197
|
-
* @returns {
|
|
271
|
+
* @returns {CustomEntityDetectionModel}
|
|
198
272
|
* @memberOf Ai
|
|
199
273
|
*/
|
|
200
274
|
getModel (prefix = 'default') {
|
package/src/Processor.js
CHANGED
package/src/Responder.js
CHANGED
|
@@ -63,6 +63,12 @@ Object.freeze(ExpectedInput);
|
|
|
63
63
|
* @prop {string} [voice]
|
|
64
64
|
*/
|
|
65
65
|
|
|
66
|
+
/**
|
|
67
|
+
* @callback VoiceControlFactory
|
|
68
|
+
* @param {object} state
|
|
69
|
+
* @returns {VoiceControl}
|
|
70
|
+
*/
|
|
71
|
+
|
|
66
72
|
/** @typedef {import('./ReturnSender').UploadResult} UploadResult */
|
|
67
73
|
|
|
68
74
|
/**
|
|
@@ -91,6 +97,7 @@ class Responder {
|
|
|
91
97
|
this._bookmark = null;
|
|
92
98
|
|
|
93
99
|
this.options = {
|
|
100
|
+
state: Object.freeze({}),
|
|
94
101
|
translator: (w) => w,
|
|
95
102
|
appUrl: ''
|
|
96
103
|
};
|
|
@@ -149,9 +156,9 @@ class Responder {
|
|
|
149
156
|
this.startedOutput = false;
|
|
150
157
|
|
|
151
158
|
/**
|
|
152
|
-
* @type {VoiceControl}
|
|
159
|
+
* @type {VoiceControl|VoiceControlFactory}
|
|
153
160
|
*/
|
|
154
|
-
this.voiceControl =
|
|
161
|
+
this.voiceControl = null;
|
|
155
162
|
|
|
156
163
|
this._trackAsAction = null;
|
|
157
164
|
|
|
@@ -477,11 +484,15 @@ class Responder {
|
|
|
477
484
|
}
|
|
478
485
|
|
|
479
486
|
if (this._features.includes(FEATURE_VOICE)
|
|
480
|
-
&& (voice ||
|
|
487
|
+
&& (voice || this.voiceControl)) {
|
|
481
488
|
|
|
482
489
|
Object.assign(messageData.message, {
|
|
483
490
|
voice: {
|
|
484
|
-
...this.voiceControl
|
|
491
|
+
...(typeof this.voiceControl === 'function'
|
|
492
|
+
? this.voiceControl(Object.freeze({
|
|
493
|
+
...this.options.state, ...this.newState
|
|
494
|
+
}))
|
|
495
|
+
: this.voiceControl),
|
|
485
496
|
...voice
|
|
486
497
|
}
|
|
487
498
|
});
|
|
@@ -3,6 +3,11 @@
|
|
|
3
3
|
*/
|
|
4
4
|
'use strict';
|
|
5
5
|
|
|
6
|
+
/** @typedef {import('../wingbot/CustomEntityDetectionModel').EntityDetector} EntityDetector */
|
|
7
|
+
/** @typedef {import('../wingbot/CustomEntityDetectionModel').DetectorOptions} DetectorOptions */
|
|
8
|
+
|
|
9
|
+
/** @type {[string,EntityDetector|RegExp,DetectorOptions]} */
|
|
6
10
|
module.exports = ['email', /(?<=(\s|^|:))[a-zA-Z0-9!#$%&'*+\-=?^_`{|}~"][^@:\s]*@[^.@\s]+\.[^@\s,]+/, {
|
|
7
|
-
anonymize: true
|
|
11
|
+
anonymize: true,
|
|
12
|
+
clearOverlaps: true
|
|
8
13
|
}];
|
|
@@ -3,11 +3,16 @@
|
|
|
3
3
|
*/
|
|
4
4
|
'use strict';
|
|
5
5
|
|
|
6
|
+
/** @typedef {import('../wingbot/CustomEntityDetectionModel').EntityDetector} EntityDetector */
|
|
7
|
+
/** @typedef {import('../wingbot/CustomEntityDetectionModel').DetectorOptions} DetectorOptions */
|
|
8
|
+
|
|
9
|
+
/** @type {[string,EntityDetector|RegExp,DetectorOptions]} */
|
|
6
10
|
module.exports = [
|
|
7
11
|
'phone',
|
|
8
12
|
/((00|\+)[\s-]?[0-9]{1,4}[\s-]?)?([0-9]{3,4}[\s-]?([0-9]{2,3}[\s-]?[0-9]{2}[\s-]?[0-9]{2,3}|[0-9]{3,4}[\s-]?[0-9]{3,4}))(?=(\s|$|[,!.?\-:]))/,
|
|
9
13
|
{
|
|
10
14
|
anonymize: true,
|
|
15
|
+
clearOverlaps: true,
|
|
11
16
|
extractValue: (match) => {
|
|
12
17
|
let [, internat,, number] = match;
|
|
13
18
|
|
|
@@ -35,7 +35,7 @@ class CachedModel extends CustomEntityDetectionModel {
|
|
|
35
35
|
* @param {object} options
|
|
36
36
|
* @param {number} [options.cacheSize]
|
|
37
37
|
* @param {number} [options.cachePhrasesTime]
|
|
38
|
-
* @param {{ warn: Function, error: Function }} [log]
|
|
38
|
+
* @param {{ warn: Function, error: Function, log: Function }} [log]
|
|
39
39
|
*/
|
|
40
40
|
constructor (options, log = console) {
|
|
41
41
|
super(options, log);
|
|
@@ -88,11 +88,19 @@ class CachedModel extends CustomEntityDetectionModel {
|
|
|
88
88
|
const res = await promise;
|
|
89
89
|
let { intents = [], entities = [] } = Array.isArray(res) ? { intents: res } : res;
|
|
90
90
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
91
|
+
const expectedEntities = req ? req.expectedEntities() : [];
|
|
92
|
+
|
|
93
|
+
const before = local.entities
|
|
94
|
+
.filter((e) => this._entityDetectors.has(e.entity)
|
|
95
|
+
&& this._entityDetectors.get(e.entity).clearOverlaps);
|
|
96
|
+
|
|
97
|
+
[intents, entities] = this._attachEntities(intents, entities, before, expectedEntities);
|
|
98
|
+
|
|
99
|
+
const after = local.entities
|
|
100
|
+
.filter((e) => !this._entityDetectors.has(e.entity)
|
|
101
|
+
|| !this._entityDetectors.get(e.entity).clearOverlaps);
|
|
102
|
+
|
|
103
|
+
[intents, entities] = this._attachEntities(intents, entities, after);
|
|
96
104
|
|
|
97
105
|
return {
|
|
98
106
|
text: local.text,
|
|
@@ -101,6 +109,26 @@ class CachedModel extends CustomEntityDetectionModel {
|
|
|
101
109
|
};
|
|
102
110
|
}
|
|
103
111
|
|
|
112
|
+
_attachEntities (intents, entities, attachEntities, expectedEntities = null) {
|
|
113
|
+
const retEntities = [...entities, ...attachEntities];
|
|
114
|
+
const retIntents = intents
|
|
115
|
+
.map((i) => {
|
|
116
|
+
const ents = [...(i.entities || []), ...attachEntities];
|
|
117
|
+
|
|
118
|
+
return {
|
|
119
|
+
...i,
|
|
120
|
+
entities: expectedEntities
|
|
121
|
+
? this.nonOverlapping(ents, expectedEntities)
|
|
122
|
+
: ents
|
|
123
|
+
};
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
return [
|
|
127
|
+
retIntents,
|
|
128
|
+
expectedEntities ? this.nonOverlapping(retEntities, expectedEntities) : retEntities
|
|
129
|
+
];
|
|
130
|
+
}
|
|
131
|
+
|
|
104
132
|
async getPhrases () {
|
|
105
133
|
if (this._phrasesCachedAt > (Date.now() - this.phrasesCacheTime)) {
|
|
106
134
|
return this._phrasesCache;
|
|
@@ -51,6 +51,16 @@ const { replaceDiacritics } = require('../utils');
|
|
|
51
51
|
* @prop {Intent[]} intents
|
|
52
52
|
*/
|
|
53
53
|
|
|
54
|
+
/**
|
|
55
|
+
* @typedef {object} DetectorOptions
|
|
56
|
+
* @prop {boolean} [anonymize] - if true, value will not be sent to NLP
|
|
57
|
+
* @prop {Function|string} [extractValue] - entity extractor
|
|
58
|
+
* @prop {boolean} [matchWholeWords] - match whole words at regular expression
|
|
59
|
+
* @prop {boolean} [replaceDiacritics] - keep diacritics when matching regexp
|
|
60
|
+
* @prop {string[]} [dependencies] - array of dependent entities
|
|
61
|
+
* @prop {boolean} [clearOverlaps] - let longer entities from NLP to replace entity
|
|
62
|
+
*/
|
|
63
|
+
|
|
54
64
|
/**
|
|
55
65
|
* @typedef {object} Phrases
|
|
56
66
|
* @prop {Map<string,string[]>} phrases
|
|
@@ -70,7 +80,7 @@ class CustomEntityDetectionModel {
|
|
|
70
80
|
|
|
71
81
|
/**
|
|
72
82
|
* @param {object} options
|
|
73
|
-
* @param {{ warn: Function, error: Function }} [log]
|
|
83
|
+
* @param {{ warn: Function, error: Function, log: Function }} [log]
|
|
74
84
|
*/
|
|
75
85
|
constructor (options, log = console) {
|
|
76
86
|
this._options = options;
|
|
@@ -582,12 +592,7 @@ class CustomEntityDetectionModel {
|
|
|
582
592
|
*
|
|
583
593
|
* @param {string} name
|
|
584
594
|
* @param {EntityDetector|RegExp} detector
|
|
585
|
-
* @param {
|
|
586
|
-
* @param {boolean} [options.anonymize] - if true, value will not be sent to NLP
|
|
587
|
-
* @param {Function|string} [options.extractValue] - entity extractor
|
|
588
|
-
* @param {boolean} [options.matchWholeWords] - match whole words at regular expression
|
|
589
|
-
* @param {boolean} [options.replaceDiacritics] - keep diacritics when matching regexp
|
|
590
|
-
* @param {string[]} [options.dependencies] - array of dependent entities
|
|
595
|
+
* @param {DetectorOptions} [options]
|
|
591
596
|
* @returns {this}
|
|
592
597
|
*/
|
|
593
598
|
setEntityDetector (name, detector, options = {}) {
|
|
@@ -610,7 +615,8 @@ class CustomEntityDetectionModel {
|
|
|
610
615
|
entityDetector,
|
|
611
616
|
detector,
|
|
612
617
|
dependencies,
|
|
613
|
-
anonymize: !!options.anonymize
|
|
618
|
+
anonymize: !!options.anonymize,
|
|
619
|
+
clearOverlaps: !!options.clearOverlaps
|
|
614
620
|
});
|
|
615
621
|
|
|
616
622
|
return this;
|
|
@@ -623,6 +629,7 @@ class CustomEntityDetectionModel {
|
|
|
623
629
|
* @param {string} name
|
|
624
630
|
* @param {object} options
|
|
625
631
|
* @param {boolean} [options.anonymize]
|
|
632
|
+
* @param {boolean} [options.clearOverlaps]
|
|
626
633
|
* @returns {this}
|
|
627
634
|
* @example
|
|
628
635
|
*
|
|
@@ -632,7 +639,7 @@ class CustomEntityDetectionModel {
|
|
|
632
639
|
*/
|
|
633
640
|
setDetectorOptions (name, options) {
|
|
634
641
|
if (!this._entityDetectors.has(name)) {
|
|
635
|
-
throw new Error(
|
|
642
|
+
throw new Error(`Can't set entity detector options. Entity "${name}" does not exist.`);
|
|
636
643
|
}
|
|
637
644
|
Object.assign(this._entityDetectors.get(name), options);
|
|
638
645
|
return this;
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
const { default: fetch } = require('node-fetch');
|
|
4
4
|
const assert = require('assert');
|
|
5
5
|
const CachedModel = require('./CachedModel');
|
|
6
|
-
const systemEntities = require('../systemEntities');
|
|
7
6
|
|
|
8
7
|
const DEFAULT_MATCHES = 3;
|
|
9
8
|
const SERVICE_URL = 'https://model.wingbot.ai';
|
|
@@ -42,7 +41,7 @@ class WingbotModel extends CachedModel {
|
|
|
42
41
|
* @param {number} [options.cacheSize]
|
|
43
42
|
* @param {number} [options.matches]
|
|
44
43
|
* @param {Function} [options.fetch]
|
|
45
|
-
* @param {{ warn: Function, log: Function }} [log]
|
|
44
|
+
* @param {{ warn: Function, log: Function, error: Function }} [log]
|
|
46
45
|
*/
|
|
47
46
|
constructor (options, log = console) {
|
|
48
47
|
super(options, log);
|
|
@@ -60,11 +59,6 @@ class WingbotModel extends CachedModel {
|
|
|
60
59
|
this._serviceUrl = options.serviceUrl || SERVICE_URL;
|
|
61
60
|
this._trainingUrl = options.trainingUrl || TRAINING_URL;
|
|
62
61
|
this._model = options.model;
|
|
63
|
-
|
|
64
|
-
// apply default entities
|
|
65
|
-
systemEntities
|
|
66
|
-
// @ts-ignore
|
|
67
|
-
.forEach(([name, d, opts = {}]) => this.setEntityDetector(name, d, opts));
|
|
68
62
|
}
|
|
69
63
|
|
|
70
64
|
async _getPhrases () {
|