wingbot 3.26.3 → 3.27.0-alpha.4

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wingbot",
3
- "version": "3.26.3",
3
+ "version": "3.27.0-alpha.4",
4
4
  "description": "Enterprise Messaging Bot Conversation Engine",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/src/Processor.js CHANGED
@@ -179,7 +179,7 @@ class Processor extends EventEmitter {
179
179
  }
180
180
  }
181
181
 
182
- _createPostBack (postbackAcumulator, req, res) {
182
+ _createPostBack (postbackAcumulator, req, res, features) {
183
183
  const postBack = (action, inputData = {}, dontWaitTillEndOfLoop = false) => {
184
184
  let data = inputData;
185
185
  if (typeof data === 'function') {
@@ -214,11 +214,12 @@ class Processor extends EventEmitter {
214
214
  postbackAcumulator.push(data
215
215
  .then((result) => ({
216
216
  action,
217
- data: Object.assign(result || {}, { _localpostback: true })
217
+ data: Object.assign(result || {}, { _localpostback: true }),
218
+ features
218
219
  })));
219
220
  } else {
220
221
  Object.assign(data, { _localpostback: true });
221
- postbackAcumulator.push({ action, data });
222
+ postbackAcumulator.push({ action, data, features });
222
223
  }
223
224
 
224
225
  return Promise.resolve();
@@ -512,16 +513,17 @@ class Processor extends EventEmitter {
512
513
  }
513
514
  );
514
515
 
516
+ const features = [
517
+ ...(this.options.features || []),
518
+ ...req.features
519
+ ];
515
520
  const options = {
516
521
  ...this.options,
517
- features: [
518
- ...(this.options.features || []),
519
- ...req.features
520
- ]
522
+ features
521
523
  };
522
524
 
523
525
  res = new Responder(senderId, messageSender, token, options, responderData);
524
- const postBack = this._createPostBack(postbackAcumulator, req, res);
526
+ const postBack = this._createPostBack(postbackAcumulator, req, res, features);
525
527
 
526
528
  let continueDispatching = true;
527
529
 
@@ -729,13 +731,14 @@ class Processor extends EventEmitter {
729
731
  _processPostbacks (postbackAcumulator, senderId, pageId, messageSender, responderData) {
730
732
  return postbackAcumulator.reduce((promise, postback) => promise
731
733
  .then(() => postback)
732
- .then(({ action, data = {} }) => {
734
+ .then(({ action, data = {}, features }) => {
733
735
  let request;
734
736
  if (typeof action === 'object') {
735
737
  request = action;
736
738
  } else {
737
739
  request = Request.postBack(senderId, action, data);
738
740
  }
741
+ Object.assign(request, { features });
739
742
  return this._processMessage(request, pageId, messageSender, responderData);
740
743
  }), Promise.resolve({}));
741
744
  }
@@ -19,6 +19,7 @@ const { replaceDiacritics } = require('../utils');
19
19
  * @callback EntityDetector
20
20
  * @param {string} text - part of text
21
21
  * @param {DetectedEntity[]} entities - dependent entities
22
+ * @param {boolean} [searchWithinWords] - optional ability to search within words
22
23
  * @returns {DetectedEntity[]|DetectedEntity|Promise<DetectedEntity>|Promise<DetectedEntity[]>}
23
24
  */
24
25
 
@@ -154,19 +155,52 @@ class CustomEntityDetectionModel {
154
155
  * @param {string} entity
155
156
  * @param {string} text
156
157
  * @param {DetectedEntity[]} entities
158
+ * @param {DetectedEntity[]} subWord
157
159
  * @returns {Promise<DetectedEntity[]>}
158
160
  */
159
- async _detectEntities (entity, text, entities) {
161
+ async _detectAllEntities (entity, text, entities, subWord) {
162
+ const [regularResults, subWordResults] = await Promise.all([
163
+ this._detectEntities(entity, text, entities, subWord, false),
164
+ this._detectEntities(entity, text, entities, subWord, true)
165
+ ]);
166
+
167
+ const cleanSubWordResults = subWordResults
168
+ .filter((regular) => regularResults
169
+ .some((e) => e.start < regular.end && e.end > regular.start));
170
+
171
+ subWord.push(...cleanSubWordResults);
172
+
173
+ return regularResults;
174
+ }
175
+
176
+ /**
177
+ *
178
+ * @param {string} entity
179
+ * @param {string} text
180
+ * @param {DetectedEntity[]} entities
181
+ * @param {DetectedEntity[]} subWord
182
+ * @param {boolean} detectSubWords
183
+ * @returns {Promise<DetectedEntity[]>}
184
+ */
185
+ async _detectEntities (entity, text, entities, subWord, detectSubWords) {
160
186
  const { entityDetector, dependencies } = this._entityDetectors.get(entity);
161
187
 
188
+ if (detectSubWords && entityDetector.length < 3) {
189
+ return [];
190
+ }
191
+
162
192
  const collected = [];
163
193
  let o = 0;
164
194
  let t = text;
165
195
  try {
166
196
  for (let i = 0; i < text.length; i++) {
167
-
168
- const dependentEntities = entities.filter((e) => dependencies.includes(`@${e.entity.toUpperCase()}`));
169
- const res = await Promise.resolve(entityDetector(t, dependentEntities));
197
+ const dependentEntities = [
198
+ ...subWord.filter((e) => dependencies.includes(`@${e.entity.toUpperCase()}`)),
199
+ ...entities.filter((e) => dependencies.includes(`@${e.entity.toUpperCase()}`))
200
+ ];
201
+ const res = await Promise.resolve(
202
+ entityDetector(t, dependentEntities, detectSubWords)
203
+ );
170
204
 
171
205
  const resWasArray = Array.isArray(res);
172
206
  const resArray = resWasArray ? res : [res];
@@ -285,17 +319,60 @@ class CustomEntityDetectionModel {
285
319
  return res;
286
320
  }
287
321
 
322
+ /**
323
+ *
324
+ * @param {boolean} [known]
325
+ * @returns {string[]} -
326
+ */
327
+ getDependentEntities (known = null) {
328
+ const entities = new Set();
329
+ const knownEntities = new Map(
330
+ Array.from(this._entityDetectors.keys())
331
+ .map((key) => [key.toLowerCase(), key])
332
+ );
333
+
334
+ for (const { dependencies } of this._entityDetectors.values()) {
335
+ for (const entity of dependencies) {
336
+ const lowerCase = entity.replace(/^@/, '').toLowerCase();
337
+ const isKnown = knownEntities.has(lowerCase);
338
+
339
+ if (isKnown && known !== false) {
340
+ entities.add(knownEntities.get(lowerCase));
341
+ } else if (!isKnown && known !== true) {
342
+ entities.add(lowerCase);
343
+ }
344
+ }
345
+ }
346
+
347
+ return Array.from(entities.values());
348
+ }
349
+
288
350
  /**
289
351
  *
290
352
  * @param {string} text
291
353
  * @param {string} [singleEntity]
292
- * @param {string[]} expectedEntities
293
- * @param {DetectedEntity[]} [entities] - previously detected entities to include
354
+ * @param {string[]} [expected]
355
+ * @param {DetectedEntity[]} [prevEnts] - previously detected entities to include
356
+ * @param {DetectedEntity[]} [subWord] - previously detected entities within words
294
357
  * @returns {Promise<DetectedEntity[]>}
295
358
  */
296
- async resolveEntities (text, singleEntity = null, expectedEntities = [], entities = []) {
297
- const resolved = new Set();
359
+ async resolveEntities (text, singleEntity = null, expected = [], prevEnts = [], subWord = []) {
360
+ // mark unknown dependencies as resolved
361
+ const resolved = new Set(
362
+ this.getDependentEntities(false)
363
+ .map((entity) => `@${entity.toUpperCase()}`)
364
+ );
365
+
298
366
  let missing = Array.from(this._entityDetectors.keys());
367
+ const entities = prevEnts.map((e) => {
368
+ if (typeof e.text === 'string') {
369
+ return e;
370
+ }
371
+ return {
372
+ ...e,
373
+ text: text.substring(e.start, e.end)
374
+ };
375
+ });
299
376
 
300
377
  while (missing.length !== 0) {
301
378
  let detect = [];
@@ -321,7 +398,7 @@ class CustomEntityDetectionModel {
321
398
  }
322
399
 
323
400
  const results = await Promise.all(
324
- detect.map((entity) => this._detectEntities(entity, text, entities))
401
+ detect.map((entity) => this._detectAllEntities(entity, text, entities, subWord))
325
402
  );
326
403
 
327
404
  detect.forEach((entity) => resolved.add(`@${entity.toUpperCase()}`));
@@ -329,7 +406,7 @@ class CustomEntityDetectionModel {
329
406
  results.forEach((res) => entities.push(...res));
330
407
  }
331
408
 
332
- const clean = this.nonOverlapping(entities, expectedEntities);
409
+ const clean = this.nonOverlapping(entities, expected);
333
410
 
334
411
  if (!singleEntity) {
335
412
  return clean;
@@ -421,8 +498,9 @@ class CustomEntityDetectionModel {
421
498
  /**
422
499
  * @param {string} text
423
500
  * @param {DetectedEntity[]} entities
501
+ * @param {boolean} searchWithinWords
424
502
  */
425
- return (text, entities) => {
503
+ return (text, entities, searchWithinWords) => {
426
504
  if (typeof extractValue === 'string'
427
505
  && !this._entityByDependency(entities, extractValue)) {
428
506
 
@@ -437,10 +515,11 @@ class CustomEntityDetectionModel {
437
515
  if (matchingEntities.length === 0) {
438
516
  return value;
439
517
  }
518
+ matchingEntities.sort((a, z) => z.length - a.length);
440
519
  return `(${matchingEntities.join('|')})`;
441
520
  });
442
521
 
443
- if (options.matchWholeWords) {
522
+ if (options.matchWholeWords && !searchWithinWords) {
444
523
  replaced = `(?<=(^|[^a-z0-9\u00C0-\u017F]))${replaced}(?=([^a-z0-9\u00C0-\u017F]|$))`;
445
524
  }
446
525
 
@@ -452,6 +531,8 @@ class CustomEntityDetectionModel {
452
531
  }
453
532
  const match = matchText.match(r);
454
533
 
534
+ // console.log({ matchText, replaced, match: match && match[0], searchWithinWords });
535
+
455
536
  if (!match) {
456
537
  return null;
457
538
  }
@@ -467,19 +548,15 @@ class CustomEntityDetectionModel {
467
548
 
468
549
  if (typeof extractValue === 'function') {
469
550
  value = extractValue(match, useEntities);
470
- } else if (typeof extractValue === 'string' || dependencies.length === 1) {
551
+ } else if (typeof extractValue === 'string' || dependencies.length > 0) {
471
552
  const entityName = typeof extractValue === 'string'
472
553
  ? extractValue
473
554
  : dependencies[0];
474
555
 
475
556
  const entity = this._entityByDependency(useEntities, entityName);
476
557
  value = entity ? entity.value : null;
477
- } else if (entities.length === 0) {
478
- [value] = match;
479
558
  } else {
480
- value = useEntities.reduce((o, e) => Object.assign(o, {
481
- [e.entity]: e.value
482
- }), {});
559
+ [value] = match;
483
560
  }
484
561
 
485
562
  return {