wingbot 3.26.0 → 3.27.0-alpha.1

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.0",
3
+ "version": "3.27.0-alpha.1",
4
4
  "description": "Enterprise Messaging Bot Conversation Engine",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/src/AiMatching.js CHANGED
@@ -403,13 +403,25 @@ class AiMatching {
403
403
  score: regexpScore - handicap
404
404
  };
405
405
  }
406
+
407
+ let useState;
408
+ if (stateless || intents.length === 0) {
409
+ useState = Object.entries(req.state)
410
+ .reduce((o, [k, v]) => {
411
+ if (k.startsWith('@')) {
412
+ return o;
413
+ }
414
+ return Object.assign(o, { [k]: v });
415
+ }, {});
416
+ } else {
417
+ useState = req.state;
418
+ }
419
+
406
420
  const { score, handicap, matched } = this
407
421
  ._entityMatching(
408
422
  entities,
409
423
  reqEntities,
410
- stateless || intents.length === 0
411
- ? {}
412
- : req.state
424
+ useState
413
425
  );
414
426
 
415
427
  const allOptional = entities.every((e) => e.optional);
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];
@@ -194,8 +228,14 @@ class CustomEntityDetectionModel {
194
228
  }
195
229
  }
196
230
 
197
- _escapeRegex (string) {
198
- return string.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');
231
+ _escapeRegex (string, shouldReplaceDiacritics) {
232
+ const ret = string.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');
233
+
234
+ if (!shouldReplaceDiacritics) {
235
+ return ret;
236
+ }
237
+
238
+ return replaceDiacritics(ret);
199
239
  }
200
240
 
201
241
  /**
@@ -279,16 +319,50 @@ class CustomEntityDetectionModel {
279
319
  return res;
280
320
  }
281
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
+
282
350
  /**
283
351
  *
284
352
  * @param {string} text
285
353
  * @param {string} [singleEntity]
286
- * @param {string[]} expectedEntities
354
+ * @param {string[]} [expected]
287
355
  * @param {DetectedEntity[]} [entities] - previously detected entities to include
356
+ * @param {DetectedEntity[]} [subWord] - previously detected entities within words
288
357
  * @returns {Promise<DetectedEntity[]>}
289
358
  */
290
- async resolveEntities (text, singleEntity = null, expectedEntities = [], entities = []) {
291
- const resolved = new Set();
359
+ async resolveEntities (text, singleEntity = null, expected = [], entities = [], subWord = []) {
360
+ // mark unknown dependencies as resolved
361
+ const resolved = new Set(
362
+ this.getDependentEntities(false)
363
+ .map((entity) => resolved.add(`@${entity.toUpperCase()}`))
364
+ );
365
+
292
366
  let missing = Array.from(this._entityDetectors.keys());
293
367
 
294
368
  while (missing.length !== 0) {
@@ -315,7 +389,7 @@ class CustomEntityDetectionModel {
315
389
  }
316
390
 
317
391
  const results = await Promise.all(
318
- detect.map((entity) => this._detectEntities(entity, text, entities))
392
+ detect.map((entity) => this._detectAllEntities(entity, text, entities, subWord))
319
393
  );
320
394
 
321
395
  detect.forEach((entity) => resolved.add(`@${entity.toUpperCase()}`));
@@ -323,7 +397,7 @@ class CustomEntityDetectionModel {
323
397
  results.forEach((res) => entities.push(...res));
324
398
  }
325
399
 
326
- const clean = this.nonOverlapping(entities, expectedEntities);
400
+ const clean = this.nonOverlapping(entities, expected);
327
401
 
328
402
  if (!singleEntity) {
329
403
  return clean;
@@ -415,8 +489,9 @@ class CustomEntityDetectionModel {
415
489
  /**
416
490
  * @param {string} text
417
491
  * @param {DetectedEntity[]} entities
492
+ * @param {boolean} searchWithinWords
418
493
  */
419
- return (text, entities) => {
494
+ return (text, entities, searchWithinWords) => {
420
495
  if (typeof extractValue === 'string'
421
496
  && !this._entityByDependency(entities, extractValue)) {
422
497
 
@@ -426,15 +501,16 @@ class CustomEntityDetectionModel {
426
501
  let replaced = source.replace(/@[A-Z0-9-]+/g, (value) => {
427
502
  const matchingEntities = entities
428
503
  .filter((e) => `@${e.entity.toUpperCase()}` === value)
429
- .map((e) => this._escapeRegex(e.text));
504
+ .map((e) => this._escapeRegex(e.text, options.replaceDiacritics));
430
505
 
431
506
  if (matchingEntities.length === 0) {
432
507
  return value;
433
508
  }
509
+ matchingEntities.sort((a, z) => z.length - a.length);
434
510
  return `(${matchingEntities.join('|')})`;
435
511
  });
436
512
 
437
- if (options.matchWholeWords) {
513
+ if (options.matchWholeWords && !searchWithinWords) {
438
514
  replaced = `(?<=(^|[^a-z0-9\u00C0-\u017F]))${replaced}(?=([^a-z0-9\u00C0-\u017F]|$))`;
439
515
  }
440
516
 
@@ -446,6 +522,8 @@ class CustomEntityDetectionModel {
446
522
  }
447
523
  const match = matchText.match(r);
448
524
 
525
+ // console.log({ matchText, replaced, match: match && match[0], searchWithinWords });
526
+
449
527
  if (!match) {
450
528
  return null;
451
529
  }