wingbot 3.74.7 → 3.75.9-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/src/Request.js CHANGED
@@ -5,10 +5,9 @@
5
5
 
6
6
  const Ai = require('./Ai');
7
7
  const { tokenize, parseActionPayload } = require('./utils');
8
- const { quickReplyAction } = require('./utils/quickReplies');
9
8
  const { ResponseFlag } = require('./analytics/consts');
10
9
  const { getSetState } = require('./utils/getUpdate');
11
- const { vars, checkSetState } = require('./utils/stateVariables');
10
+ const { vars } = require('./utils/stateVariables');
12
11
  const OrchestratorClient = require('./OrchestratorClient');
13
12
  const { cachedTranslatedCompilator, stateData } = require('./resolvers/utils');
14
13
  const {
@@ -19,8 +18,7 @@ const {
19
18
  FEATURE_TRACKING,
20
19
  getDefaultFeatureList
21
20
  } = require('./features');
22
-
23
- const BASE64_REGEX = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;
21
+ const LLMDispatcher = require('./LLMDispatcher');
24
22
 
25
23
  const counter = {
26
24
  _t: 0,
@@ -39,6 +37,7 @@ function makeTimestamp () {
39
37
  }
40
38
 
41
39
  /** @typedef {import('./Router').BaseConfiguration} BaseConfiguration */
40
+ /** @typedef {import('./LLMMockProvider').LLMMockResponse} LLMMockResponse */
42
41
 
43
42
  /**
44
43
  * @typedef {object} Entity
@@ -51,31 +50,31 @@ function makeTimestamp () {
51
50
  /**
52
51
  * @typedef {object} Intent
53
52
  * @prop {string} intent
54
- * @prop {number} score
53
+ * @prop {number} [score]
55
54
  * @prop {Entity[]} [entities]
56
55
  */
57
56
 
58
57
  /**
59
58
  * @typedef {object} Action
60
59
  * @prop {string} action
61
- * @prop {object} data
60
+ * @prop {object} [data]
62
61
  * @prop {object|null} [setState]
63
62
  */
64
63
 
65
64
  /**
66
65
  * @typedef {object} IntentAction
67
66
  * @prop {string} action
68
- * @prop {Intent} intent
69
- * @prop {number} sort
70
- * @prop {boolean} local
71
- * @prop {boolean} aboveConfidence
67
+ * @prop {Intent} [intent]
68
+ * @prop {number} [sort]
69
+ * @prop {boolean} [local]
70
+ * @prop {boolean} [aboveConfidence]
72
71
  * @prop {object} [data]
73
72
  * @prop {string|string[]} [match]
74
73
  * @prop {object} [setState]
75
74
  * @prop {boolean} [winner]
76
75
  * @prop {string|Function} [title]
77
76
  * @prop {boolean} [hasAiTitle]
78
- * @prop {object} meta
77
+ * @prop {object} [meta]
79
78
  * @prop {string} [meta.targetAppId]
80
79
  * @prop {string|null} [meta.targetAction]
81
80
  * @prop {string} [meta.resolverTag]
@@ -111,7 +110,7 @@ function makeTimestamp () {
111
110
 
112
111
  /**
113
112
  * @typedef {object} Attachment
114
- * @prop {'file'|'audio'|'video'|'image'} type
113
+ * @prop {'file'|'audio'|'video'|'image'|'location'} type
115
114
  * @prop {object} payload
116
115
  * @prop {string} payload.url
117
116
  */
@@ -135,7 +134,7 @@ class Request {
135
134
  * @param {*} event
136
135
  * @param {S} state
137
136
  * @param {string} pageId
138
- * @param {Map} globalIntents
137
+ * @param {LLMDispatcher} [dispatcher]
139
138
  * @param {RequestOrchestratorOptions} [orchestratorOptions]
140
139
  * @param {C} [configuration]
141
140
  */
@@ -143,7 +142,7 @@ class Request {
143
142
  event,
144
143
  state,
145
144
  pageId,
146
- globalIntents = new Map(),
145
+ dispatcher = new LLMDispatcher(Ai.ai),
147
146
  orchestratorOptions = {},
148
147
  // @ts-ignore
149
148
  configuration = {}
@@ -154,6 +153,9 @@ class Request {
154
153
 
155
154
  this._event = event;
156
155
 
156
+ /** @type {LLMDispatcher} */
157
+ this._dispatcher = dispatcher;
158
+
157
159
  /**
158
160
  * @enum {AiSetStateOption}
159
161
  */
@@ -165,8 +167,6 @@ class Request {
165
167
  EXCLUDE_WITHOUT_SET_ENTITIES: -3
166
168
  };
167
169
 
168
- this.globalIntents = globalIntents;
169
-
170
170
  /**
171
171
  * @prop {object} params - plugin configuration
172
172
  */
@@ -174,13 +174,13 @@ class Request {
174
174
 
175
175
  this.message = event.message || null;
176
176
 
177
- this._postback = event.postback || null;
177
+ this.postback = event.postback || null;
178
178
 
179
- this._referral = (this._postback && this._postback.referral)
179
+ this.referral = (this.postback && this.postback.referral)
180
180
  || event.referral
181
181
  || null;
182
182
 
183
- this._optin = event.optin || null;
183
+ this.optin = event.optin || null;
184
184
 
185
185
  /** @type {Attachment[]} */
186
186
  this.attachments = (event.message
@@ -236,18 +236,19 @@ class Request {
236
236
  this.intents = [];
237
237
 
238
238
  /**
239
- * @type {Action}
240
- * @private
239
+ * type {Action}
240
+ * private
241
241
  */
242
- this._action = undefined;
242
+ // this._action = undefined;
243
243
 
244
+ /** @deprecated */
244
245
  this._winningIntent = null;
245
246
 
246
- this._aiActions = null;
247
+ // this._aiActions = null;
247
248
 
248
- this._quickReplyActions = null;
249
+ // this._quickReplyActions = null;
249
250
 
250
- this._aiWinner = null;
251
+ // this._aiWinner = null;
251
252
 
252
253
  // protected for now, filled by AI
253
254
  this._anonymizedText = null;
@@ -290,6 +291,13 @@ class Request {
290
291
  this.configuration = configuration;
291
292
  }
292
293
 
294
+ /**
295
+ * @returns {LLMDispatcher}
296
+ */
297
+ get dispatcher () {
298
+ return this._dispatcher;
299
+ }
300
+
293
301
  get data () {
294
302
  // eslint-disable-next-line
295
303
  console.info('wingbot: req.data is deprecated, use req.event instead');
@@ -332,10 +340,9 @@ class Request {
332
340
  */
333
341
  aiActions (local = false) {
334
342
  if (local) {
335
- return this._resolveQuickReplyActions();
343
+ return this._dispatcher.resolveQuickReplyActions();
336
344
  }
337
- this.aiActionsWinner();
338
- return this._aiActions;
345
+ return this._dispatcher.aiActions();
339
346
  }
340
347
 
341
348
  /**
@@ -347,14 +354,10 @@ class Request {
347
354
  * @returns {QuickReplyDisambiguation[]}
348
355
  */
349
356
  aiActionsForQuickReplies (limit = 5, aiActions = null, overrideAction = null) {
350
- if (aiActions === null) {
351
- this.aiActionsWinner();
352
- }
353
-
354
357
  const text = this.text();
355
358
  const knownTexts = new Set();
356
359
 
357
- return (aiActions || this._aiActions)
360
+ return (aiActions || this._dispatcher.aiActions())
358
361
  .filter((a) => a.title)
359
362
  .slice(0, limit)
360
363
  .map((a) => {
@@ -432,10 +435,9 @@ class Request {
432
435
  hasAiActionsForDisambiguation (minimum = 1, local = false) {
433
436
  let iterate;
434
437
  if (local) {
435
- iterate = this._resolveQuickReplyActions();
438
+ iterate = this._dispatcher.resolveQuickReplyActions();
436
439
  } else {
437
- this.aiActionsWinner();
438
- iterate = this._aiActions;
440
+ iterate = this._dispatcher.aiActions();
439
441
  }
440
442
  return iterate
441
443
  .filter((a) => a.title)
@@ -638,6 +640,7 @@ class Request {
638
640
  return null;
639
641
  }
640
642
 
643
+ // @ts-ignore
641
644
  return location.payload.coordinates;
642
645
  }
643
646
 
@@ -695,7 +698,7 @@ class Request {
695
698
  * @returns {boolean}
696
699
  */
697
700
  isText () {
698
- return (this._postback === null
701
+ return (this.postback === null
699
702
  && this.message !== null
700
703
  && !this.message.quick_reply
701
704
  && typeof this.message.text === 'string')
@@ -720,8 +723,10 @@ class Request {
720
723
  && this.attachments[0].type === 'image'
721
724
  && typeof this.attachments[0].payload === 'object'
722
725
  && this.attachments[0].payload !== null
726
+ // @ts-ignore
723
727
  && typeof this.attachments[0].payload.sticker_id !== 'undefined'
724
728
  && (includeToTextStickers
729
+ // @ts-ignore
725
730
  || this._stickerIdToText(this.attachments[0].payload.sticker_id) === null);
726
731
  }
727
732
 
@@ -739,6 +744,7 @@ class Request {
739
744
  return '';
740
745
  }
741
746
 
747
+ // @ts-ignore
742
748
  return this._stickerIdToText(this.attachments[0].payload.sticker_id) || '';
743
749
  }
744
750
 
@@ -879,7 +885,7 @@ class Request {
879
885
  let shouldIncludeKeywords = includeKeywords;
880
886
 
881
887
  if (expected) {
882
- const { action, data = {} } = expected;
888
+ const { action, data = {}, bounceAllowedTo } = expected;
883
889
 
884
890
  if (!data._expectedFallbackOccured || !justOnce) {
885
891
  Object.assign(ret, {
@@ -888,7 +894,8 @@ class Request {
888
894
  data: {
889
895
  ...data,
890
896
  _expectedFallbackOccured: true
891
- }
897
+ },
898
+ bounceAllowedTo
892
899
  }
893
900
  });
894
901
  } else if (justOnce) {
@@ -948,7 +955,7 @@ class Request {
948
955
  * @returns {boolean}
949
956
  */
950
957
  isPostBack () {
951
- return this._postback !== null;
958
+ return this.postback !== null;
952
959
  }
953
960
 
954
961
  /**
@@ -957,7 +964,7 @@ class Request {
957
964
  * @returns {boolean}
958
965
  */
959
966
  isReferral () {
960
- return this._referral !== null;
967
+ return this.referral !== null;
961
968
  }
962
969
 
963
970
  /**
@@ -966,7 +973,7 @@ class Request {
966
973
  * @returns {boolean}
967
974
  */
968
975
  isOptin () {
969
- return this._optin !== null;
976
+ return this.optin !== null;
970
977
  }
971
978
 
972
979
  /**
@@ -978,14 +985,16 @@ class Request {
978
985
  */
979
986
  setAction (action, data = {}) {
980
987
  // fetch previous action
981
- const previousAction = this._action;
988
+ const previousAction = this.dispatcher.action();
982
989
 
990
+ let act;
983
991
  if (typeof action === 'object' || typeof action === 'undefined') { // accepts also a null
984
- this._action = action;
992
+ act = action;
985
993
  } else {
986
- this._action = { action, data };
994
+ act = { action, data };
987
995
  }
988
996
 
997
+ this.dispatcher.overrideAction(act);
989
998
  return previousAction;
990
999
  }
991
1000
 
@@ -1010,18 +1019,17 @@ class Request {
1010
1019
  * typeof res.actionData() === 'object';
1011
1020
  */
1012
1021
  action (getData = false) {
1013
- if (typeof this._action === 'undefined') {
1014
- this._action = this._resolveAction();
1015
- }
1022
+ const action = this._dispatcher.action();
1016
1023
 
1017
1024
  if (getData) {
1018
1025
  // eslint-disable-next-line no-console
1019
1026
  console.info('wingbot: deprecated using req.action(true), use req.actionData() instead');
1020
1027
 
1021
- return this._action ? this._action.data : {};
1028
+ // @ts-ignore
1029
+ return action?.data || {};
1022
1030
  }
1023
1031
 
1024
- return this._action && this._action.action;
1032
+ return action?.action || null;
1025
1033
  }
1026
1034
 
1027
1035
  /**
@@ -1030,10 +1038,8 @@ class Request {
1030
1038
  * @returns {object}
1031
1039
  */
1032
1040
  actionData () {
1033
- if (typeof this._action === 'undefined') {
1034
- this._action = this._resolveAction();
1035
- }
1036
- return this._action ? this._action.data : {};
1041
+ const action = this._dispatcher.action();
1042
+ return action?.data || {};
1037
1043
  }
1038
1044
 
1039
1045
  // eslint-disable-next-line jsdoc/require-param
@@ -1047,12 +1053,9 @@ class Request {
1047
1053
  * res.setState(req.getSetState());
1048
1054
  */
1049
1055
  getSetState (keysFromAi = this.AI_SETSTATE.INCLUDE, useState = null) {
1050
- if (typeof this._action === 'undefined') {
1051
- this._action = this._resolveAction();
1052
- }
1056
+ const action = this._dispatcher.action();
1053
1057
 
1054
- let setState = (this._action && this._action.setState)
1055
- || this._event.setState;
1058
+ let setState = action?.setState || this._event.setState;
1056
1059
 
1057
1060
  // orchestrators context updates
1058
1061
  if (this.event.set_context || this.event.context) {
@@ -1073,7 +1076,7 @@ class Request {
1073
1076
  }
1074
1077
 
1075
1078
  // @ts-ignore
1076
- const { _aiKeys: aiKeys = [] } = this._action || {};
1079
+ const { _aiKeys: aiKeys = [] } = action || {};
1077
1080
 
1078
1081
  const ret = {};
1079
1082
 
@@ -1117,92 +1120,6 @@ class Request {
1117
1120
  return this.state._expectedConfidentInput === true;
1118
1121
  }
1119
1122
 
1120
- _resolveAction () {
1121
- let res = null;
1122
-
1123
- if (this._referral !== null && this._referral.ref) {
1124
- res = parseActionPayload({ payload: this._referral.ref });
1125
- }
1126
-
1127
- if (!res && this._postback !== null) {
1128
- res = parseActionPayload(this._postback);
1129
- }
1130
-
1131
- if (!res && this._optin !== null && this._optin.ref) {
1132
- res = this._base64Ref(this._optin);
1133
- }
1134
-
1135
- if (!res && this._optin !== null && this._optin.payload) {
1136
- res = parseActionPayload(this._optin);
1137
- }
1138
-
1139
- if (!res && this.message !== null && this.message.quick_reply) {
1140
- res = parseActionPayload(this.message.quick_reply);
1141
- }
1142
-
1143
- // @ts-ignore
1144
- if (!res && this.state._expectedKeywords) {
1145
- // @ts-ignore
1146
- res = this._actionByExpectedKeywords(this.state._expected);
1147
- }
1148
-
1149
- // @ts-ignore
1150
- if (!res && this.state._expected) {
1151
- // @ts-ignore
1152
- res = parseActionPayload(this.state._expected);
1153
- }
1154
-
1155
- if (res) {
1156
- // find global intent
1157
- let entitiesSetState = {};
1158
- let { setState = {} } = res;
1159
- for (const gi of this.globalIntents.values()) {
1160
- if (gi.action === res.action) {
1161
- entitiesSetState = { ...gi.entitiesSetState };
1162
-
1163
- const values = Array.from(Object.values(entitiesSetState));
1164
-
1165
- for (const value of values) {
1166
- if (typeof value === 'function') {
1167
- Object.assign(entitiesSetState, value(stateData(this)));
1168
- }
1169
- }
1170
- }
1171
- }
1172
- const newState = {
1173
- ...entitiesSetState,
1174
- ...setState
1175
- };
1176
- checkSetState(setState, newState);
1177
- setState = newState;
1178
- const aiKeysSet = new Set([
1179
- ...(res._aiKeys || []),
1180
- ...Object.keys(entitiesSetState)
1181
- ]);
1182
- const aiKeys = Array.from(aiKeysSet)
1183
- .filter((k) => typeof setState[k] !== 'undefined' && k.startsWith('@'));
1184
-
1185
- return {
1186
- ...res,
1187
- setState,
1188
- _aiKeys: aiKeys
1189
- };
1190
- }
1191
-
1192
- if (this.isTextOrIntent()) {
1193
- const winner = this.aiActionsWinner();
1194
- if (winner) {
1195
- const _aiKeys = winner.setState ? Object.keys(winner.setState) : [];
1196
-
1197
- res = {
1198
- action: winner.action, data: {}, setState: winner.setState, _aiKeys
1199
- };
1200
- }
1201
- }
1202
-
1203
- return res;
1204
- }
1205
-
1206
1123
  /**
1207
1124
  * Returs action string, if there is an action detected by NLP
1208
1125
  *
@@ -1229,13 +1146,14 @@ class Request {
1229
1146
  * res.setState({ email: req.text() });
1230
1147
  * });
1231
1148
  *
1149
+ *
1232
1150
  */
1233
1151
  actionByAi () {
1234
1152
  const winner = this.aiActionsWinner();
1235
1153
  return winner ? winner.action : null;
1236
1154
  }
1237
1155
 
1238
- _getLocalPathRegexp () {
1156
+ getLocalPathRegexp () {
1239
1157
  // @ts-ignore
1240
1158
  if (this.state._lastVisitedPath) {
1241
1159
  // @ts-ignore
@@ -1256,47 +1174,7 @@ class Request {
1256
1174
  * @returns {IntentAction|null}
1257
1175
  */
1258
1176
  aiActionsWinner () {
1259
- if (this._aiActions) {
1260
- return this._aiWinner;
1261
- }
1262
- if (!this.isTextOrIntent()) {
1263
- this._aiActions = [];
1264
- return null;
1265
- }
1266
-
1267
- const aiActions = [];
1268
-
1269
- // to match the local context intent
1270
- const localRegexToMatch = this._getLocalPathRegexp();
1271
-
1272
- for (const gi of this.globalIntents.values()) {
1273
- const pathMatches = localRegexToMatch && localRegexToMatch.exec(gi.action);
1274
- if (gi.local && !pathMatches) {
1275
- continue;
1276
- }
1277
- const intent = gi.matcher(this, null, true);
1278
- if (intent !== null) {
1279
- const sort = intent.score + (pathMatches
1280
- ? Ai.ai.localEnhancement
1281
- : 0);
1282
- // console.log(sort, wi.intent);
1283
- aiActions.push({
1284
- ...gi,
1285
- intent,
1286
- setState: intent.setState,
1287
- aboveConfidence: intent.aboveConfidence,
1288
- sort,
1289
- winner: false
1290
- });
1291
- }
1292
- }
1293
-
1294
- aiActions.sort((l, r) => r.sort - l.sort);
1295
- const winner = this._winner(aiActions);
1296
-
1297
- this._aiActions = aiActions;
1298
- this._aiWinner = winner;
1299
- return winner;
1177
+ return this._dispatcher.aiActionWinner();
1300
1178
  }
1301
1179
 
1302
1180
  _winner (aiActions) {
@@ -1323,7 +1201,7 @@ class Request {
1323
1201
  return null;
1324
1202
  }
1325
1203
 
1326
- const actions = this._resolveQuickReplyActions();
1204
+ const actions = this._dispatcher.resolveQuickReplyActions();
1327
1205
 
1328
1206
  if (expected && Ai.ai.shouldDisambiguate(actions, true)) {
1329
1207
  return parseActionPayload(expected);
@@ -1337,23 +1215,6 @@ class Request {
1337
1215
  return parseActionPayload(payload);
1338
1216
  }
1339
1217
 
1340
- _resolveQuickReplyActions () {
1341
- if (this._quickReplyActions === null) {
1342
- // @ts-ignore
1343
- if (this.state._expectedKeywords) {
1344
- this._quickReplyActions = quickReplyAction(
1345
- // @ts-ignore
1346
- this.state._expectedKeywords,
1347
- this,
1348
- Ai.ai
1349
- );
1350
- } else {
1351
- this._quickReplyActions = [];
1352
- }
1353
- }
1354
- return this._quickReplyActions;
1355
- }
1356
-
1357
1218
  /**
1358
1219
  * Returns action or data of postback
1359
1220
  * When `getData` is `true`, object will be returned. Otherwise string or null.
@@ -1366,26 +1227,10 @@ class Request {
1366
1227
  * typeof res.postBack(true) === 'object';
1367
1228
  */
1368
1229
  postBack (getData = false) {
1369
- if (this._postback === null) {
1230
+ if (this.postback === null) {
1370
1231
  return null;
1371
1232
  }
1372
- return this._processPayload(this._postback, getData);
1373
- }
1374
-
1375
- _base64Ref (object = {}) {
1376
- let process = {};
1377
-
1378
- if (object && object.ref) {
1379
- let payload = object.ref;
1380
-
1381
- if (typeof payload === 'string' && payload.match(BASE64_REGEX)) {
1382
- payload = Buffer.from(payload, 'base64').toString('utf8');
1383
- }
1384
-
1385
- process = { payload };
1386
- }
1387
-
1388
- return parseActionPayload(process);
1233
+ return this._processPayload(this.postback, getData);
1389
1234
  }
1390
1235
 
1391
1236
  _processPayload (object = {}, getData = false) {
@@ -1419,15 +1264,11 @@ class Request {
1419
1264
  }
1420
1265
  }
1421
1266
 
1422
- const localRegexToMatch = this._getLocalPathRegexp();
1423
-
1424
1267
  const entitySet = new Set();
1425
1268
 
1426
- for (const gi of this.globalIntents.values()) {
1427
- const pathMatches = localRegexToMatch && localRegexToMatch.exec(gi.action);
1428
- if (gi.local && !pathMatches) {
1429
- continue;
1430
- }
1269
+ const iterator = this._dispatcher.globalIntentsAtPath();
1270
+
1271
+ for (const [gi] of iterator) {
1431
1272
  gi.usedEntities.forEach((e) => entitySet.add(e));
1432
1273
  }
1433
1274
 
@@ -1594,6 +1435,22 @@ It looks like the bot isn't connected to class BotApp or the Processor is used w
1594
1435
  return Request.addIntentToRequest(res, intent, [], score);
1595
1436
  }
1596
1437
 
1438
+ /**
1439
+ *
1440
+ * @param {string} senderId
1441
+ * @param {string} text
1442
+ * @param {LLMMockResponse[]} [llmMocks]
1443
+ * @param {number} [timestamp]
1444
+ * @returns {object}
1445
+ */
1446
+ static llm (senderId, text, llmMocks, timestamp = makeTimestamp()) {
1447
+ const res = Request.text(senderId, text, timestamp);
1448
+
1449
+ return Object.assign(res, {
1450
+ llmMocks
1451
+ });
1452
+ }
1453
+
1597
1454
  static intent (senderId, intent, score = 1, timestamp = makeTimestamp()) {
1598
1455
 
1599
1456
  return {