botium-core 1.13.2 → 1.13.3

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.
Files changed (54) hide show
  1. package/.eslintrc.js +6 -3
  2. package/dist/botium-cjs.js +214 -61
  3. package/dist/botium-cjs.js.map +1 -1
  4. package/dist/botium-es.js +213 -61
  5. package/dist/botium-es.js.map +1 -1
  6. package/package.json +3 -1
  7. package/src/Capabilities.js +2 -1
  8. package/src/containers/plugins/SimpleRestContainer.js +20 -16
  9. package/src/grid/inbound/proxy.js +2 -1
  10. package/src/scripting/Convo.js +16 -10
  11. package/src/scripting/MatchFunctions.js +10 -0
  12. package/src/scripting/ScriptingProvider.js +106 -37
  13. package/src/scripting/logichook/LogicHookConsts.js +1 -1
  14. package/src/scripting/logichook/asserter/WerAsserter.js +59 -0
  15. package/src/scripting/logichook/logichooks/UpdateCustomLogicHook.js +3 -2
  16. package/test/compiler/compilercsv.spec.js +104 -3
  17. package/test/compiler/compilerjson.spec.js +0 -2
  18. package/test/compiler/compilerxlsx.spec.js +1 -1
  19. package/test/compiler/convos/csv/utterances_liveperson2.csv +12 -0
  20. package/test/connectors/simplerest.spec.js +1012 -969
  21. package/test/convo/fillAndApplyScriptingMemory.spec.js +804 -785
  22. package/test/convo/partialconvo.spec.js +345 -339
  23. package/test/driver/capabilities.spec.js +156 -151
  24. package/test/logichooks/hookfromsrc.spec.js +79 -73
  25. package/test/plugins/plugins.spec.js +44 -42
  26. package/test/scripting/asserters/buttonsAsserter.spec.js +257 -240
  27. package/test/scripting/asserters/cardsAsserter.spec.js +214 -212
  28. package/test/scripting/asserters/convos/wer_threshold_nok.yml +7 -0
  29. package/test/scripting/asserters/convos/wer_threshold_ok.yml +7 -0
  30. package/test/scripting/asserters/intentConfidenceAsserter.spec.js +34 -35
  31. package/test/scripting/asserters/jsonpathAsserter.spec.js +307 -308
  32. package/test/scripting/asserters/mediaAsserter.spec.js +236 -234
  33. package/test/scripting/asserters/werAsserter.spec.js +51 -0
  34. package/test/scripting/logichooks/setClearScriptingMemory.spec.js +202 -192
  35. package/test/scripting/matching/matchingmode.spec.js +306 -258
  36. package/test/scripting/scriptingProvider.spec.js +666 -633
  37. package/test/scripting/scriptingmemory/fillScriptingMemoryFromFile.spec.js +299 -281
  38. package/test/scripting/scriptingmemory/useScriptingMemoryForAssertion.spec.js +94 -80
  39. package/test/scripting/userinputs/defaultUserInputs.spec.js +233 -127
  40. package/test/scripting/userinputs/mediaInputConvos.spec.js +409 -403
  41. package/test/scripting/utteranceexpansion/associateByIndex.spec.js +259 -0
  42. package/test/scripting/utteranceexpansion/convos/associate_utterances_by_index.json +33 -0
  43. package/test/scripting/utteranceexpansion/convos/media.convo.txt +19 -0
  44. package/test/scripting/utteranceexpansion/files/step0voice0.wav +0 -0
  45. package/test/scripting/utteranceexpansion/files/step0voice1.wav +0 -0
  46. package/test/scripting/utteranceexpansion/files/step0voice2.wav +0 -0
  47. package/test/scripting/utteranceexpansion/files/step1voice0.wav +0 -0
  48. package/test/scripting/utteranceexpansion/files/step2voice0.wav +0 -0
  49. package/test/scripting/utteranceexpansion/files/step2voice1.wav +0 -0
  50. package/test/scripting/utteranceexpansion/files/step2voice2.wav +0 -0
  51. package/test/scripting/utteranceexpansion/files/step2voice4.wav +0 -0
  52. package/test/scripting/utteranceexpansion/files/step2voice5.wav +0 -0
  53. package/test/security/allowUnsafe.spec.js +274 -268
  54. package/test/utils.spec.js +40 -38
package/dist/botium-es.js CHANGED
@@ -20,6 +20,7 @@ import promise from 'promise.allsettled';
20
20
  import uuid from 'uuid';
21
21
  import jsonpath from 'jsonpath';
22
22
  import isJson$1 from 'is-json';
23
+ import wordErrorRate from 'word-error-rate';
23
24
  import esprima from 'esprima';
24
25
  import markdownIt from 'markdown-it';
25
26
  import xlsx from 'xlsx';
@@ -36,7 +37,7 @@ import express from 'express';
36
37
  import bodyParser from 'body-parser';
37
38
 
38
39
  var name = "botium-core";
39
- var version$1 = "1.13.2";
40
+ var version$1 = "1.13.3";
40
41
  var description = "The Selenium for Chatbots";
41
42
  var main = "index.js";
42
43
  var module = "dist/botium-es.js";
@@ -102,6 +103,7 @@ var dependencies = {
102
103
  "swagger-ui-express": "^4.4.0",
103
104
  uuid: "^8.3.2",
104
105
  vm2: "^3.9.10",
106
+ "word-error-rate": "0.0.7",
105
107
  "write-yaml": "^1.0.0",
106
108
  xlsx: "^0.18.5",
107
109
  xregexp: "^5.1.1",
@@ -118,6 +120,7 @@ var devDependencies = {
118
120
  eslint: "^8.19.0",
119
121
  "eslint-config-standard": "^17.0.0",
120
122
  "eslint-plugin-import": "^2.26.0",
123
+ "eslint-plugin-mocha": "^10.1.0",
121
124
  "eslint-plugin-n": "^15.2.4",
122
125
  "eslint-plugin-promise": "^6.0.0",
123
126
  "eslint-plugin-standard": "^4.1.0",
@@ -297,8 +300,9 @@ var Capabilities = {
297
300
  SCRIPTING_ENABLE_MULTIPLE_ASSERT_ERRORS: 'SCRIPTING_ENABLE_MULTIPLE_ASSERT_ERRORS',
298
301
  SCRIPTING_ENABLE_SKIP_ASSERT_ERRORS: 'SCRIPTING_ENABLE_SKIP_ASSERT_ERRORS',
299
302
  SCRIPTING_FORCE_BOT_CONSUMED: 'SCRIPTING_FORCE_BOT_CONSUMED',
300
- // regexp, regexpIgnoreCase, wildcard, wildcardIgnoreCase, wildcardExact, wildcardExactIgnoreCase, include, includeIgnoreCase, equals, equalsIgnoreCase
303
+ // regexp, regexpIgnoreCase, wildcard, wildcardIgnoreCase, wildcardExact, wildcardExactIgnoreCase, include, includeIgnoreCase, equals, equalsIgnoreCase, wer
301
304
  SCRIPTING_MATCHING_MODE: 'SCRIPTING_MATCHING_MODE',
305
+ SCRIPTING_MATCHING_MODE_ARGS: 'SCRIPTING_MATCHING_MODE_ARGS',
302
306
  // all, first, random
303
307
  SCRIPTING_UTTEXPANSION_MODE: 'SCRIPTING_UTTEXPANSION_MODE',
304
308
  SCRIPTING_UTTEXPANSION_RANDOM_COUNT: 'SCRIPTING_UTTEXPANSION_RANDOM_COUNT',
@@ -452,6 +456,7 @@ Capabilities.SCRIPTING_ENABLE_MULTIPLE_ASSERT_ERRORS;
452
456
  Capabilities.SCRIPTING_ENABLE_SKIP_ASSERT_ERRORS;
453
457
  Capabilities.SCRIPTING_FORCE_BOT_CONSUMED;
454
458
  Capabilities.SCRIPTING_MATCHING_MODE;
459
+ Capabilities.SCRIPTING_MATCHING_MODE_ARGS;
455
460
  Capabilities.SCRIPTING_UTTEXPANSION_MODE;
456
461
  Capabilities.SCRIPTING_UTTEXPANSION_RANDOM_COUNT;
457
462
  Capabilities.SCRIPTING_UTTEXPANSION_INCOMPREHENSION;
@@ -1137,6 +1142,9 @@ var LogicHookConsts = {
1137
1142
  }, {
1138
1143
  name: 'TEXT_IC',
1139
1144
  className: 'TextEqualsAnyICAsserter'
1145
+ }, {
1146
+ name: 'TEXT_WER',
1147
+ className: 'WerAsserter'
1140
1148
  }, {
1141
1149
  name: 'BOT_CONSUMED',
1142
1150
  className: 'BotRepliesConsumedAsserter'
@@ -2839,7 +2847,7 @@ class ConvoStep$1 {
2839
2847
  }
2840
2848
 
2841
2849
  toString() {
2842
- return (this.stepTag ? this.stepTag + ': ' : '') + '#' + this.sender + ' - ' + (this.optional ? '?' : '') + (this.not ? '!' : '') + (this.messageText || '') + (this.asserters && this.asserters.length > 0 ? ' ' + this.asserters.map(a => a.toString()).join(' ASS: ') : '') + (this.logicHooks && this.logicHooks.length > 0 ? ' ' + this.logicHooks.map(l => l.toString()).join(' LH: ') : '') + (this.userInputs && this.userInputs.length > 0 ? ' ' + this.userInputs.map(u => u.toString()).join(' UI: ') : '');
2850
+ return (this.stepTag ? this.stepTag + ': ' : '') + '#' + this.sender + ' - ' + (this.optional ? '?' : '') + (this.not ? '!' : '') + (this.messageText || '') + (this.asserters && this.asserters.length > 0 ? ' ' + this.asserters.map(a => a.toString()).join(' ') : '') + (this.logicHooks && this.logicHooks.length > 0 ? ' ' + this.logicHooks.map(l => l.toString()).join(' ') : '') + (this.userInputs && this.userInputs.length > 0 ? ' ' + this.userInputs.map(u => u.toString()).join(' ') : '');
2843
2851
  }
2844
2852
 
2845
2853
  }
@@ -2874,6 +2882,16 @@ class Transcript {
2874
2882
 
2875
2883
  }
2876
2884
 
2885
+ class TranscriptAttachment {
2886
+ constructor(fromJson = {}) {
2887
+ this.name = fromJson.name;
2888
+ this.mimeType = fromJson.mimeType;
2889
+ this.base64 = fromJson.base64;
2890
+ this.href = fromJson.href;
2891
+ }
2892
+
2893
+ }
2894
+
2877
2895
  class TranscriptStep {
2878
2896
  constructor({
2879
2897
  expected,
@@ -3654,9 +3672,16 @@ class Convo$6 {
3654
3672
  }
3655
3673
 
3656
3674
  var Convo_1 = {
3657
- ConvoHeader: ConvoHeader$1,
3658
3675
  Convo: Convo$6,
3659
- ConvoStep: ConvoStep$1
3676
+ ConvoHeader: ConvoHeader$1,
3677
+ ConvoStep: ConvoStep$1,
3678
+ ConvoStepAssert,
3679
+ ConvoStepLogicHook,
3680
+ ConvoStepUserInput,
3681
+ Transcript,
3682
+ TranscriptAttachment,
3683
+ TranscriptStep,
3684
+ TranscriptError
3660
3685
  };
3661
3686
 
3662
3687
  const {
@@ -3744,6 +3769,12 @@ const equals = ignoreCase => (botresponse, utterance) => {
3744
3769
  return botresponse === utterance;
3745
3770
  };
3746
3771
 
3772
+ const wer = () => (botresponse, utterance, args) => {
3773
+ botresponse = _normalize(botresponse || '');
3774
+ utterance = toString(utterance || '');
3775
+ return wordErrorRate.wordErrorRate(botresponse, utterance) <= args[0];
3776
+ };
3777
+
3747
3778
  const getMatchFunction$1 = matchingMode => {
3748
3779
  if (matchingMode === 'regexp' || matchingMode === 'regexpIgnoreCase') {
3749
3780
  return regexp(matchingMode === 'regexpIgnoreCase');
@@ -3755,6 +3786,8 @@ const getMatchFunction$1 = matchingMode => {
3755
3786
  return include(matchingMode === 'includeIgnoreCase' || matchingMode === 'includeLowerCase');
3756
3787
  } else if (matchingMode === 'equals' || matchingMode === 'equalsIgnoreCase') {
3757
3788
  return equals(matchingMode === 'equalsIgnoreCase');
3789
+ } else if (matchingMode === 'wer') {
3790
+ return wer();
3758
3791
  } else {
3759
3792
  return equals(false);
3760
3793
  }
@@ -3766,6 +3799,7 @@ var MatchFunctions = {
3766
3799
  wildcardExact,
3767
3800
  include,
3768
3801
  equals,
3802
+ wer,
3769
3803
  getMatchFunction: getMatchFunction$1
3770
3804
  };
3771
3805
 
@@ -6130,7 +6164,9 @@ var ScriptingProvider_1 = class ScriptingProvider {
6130
6164
 
6131
6165
  debug$9(`assertBotResponse ${stepTag} ${meMsg ? `(${meMsg}) ` : ''}BOT: ${botresponse} = ${tomatch} ...`);
6132
6166
 
6133
- const found = lodash.find(tomatch, utt => this.matchFn(botresponse, utt));
6167
+ const found = lodash.find(tomatch, utt => this.matchFn(botresponse, utt, this.caps[Capabilities.SCRIPTING_MATCHING_MODE_ARGS]));
6168
+
6169
+ const asserterType = this.caps[Capabilities.SCRIPTING_MATCHING_MODE] === 'wer' ? 'WerAsserter' : 'TextMatchAsserter';
6134
6170
 
6135
6171
  if (lodash.isNil(found)) {
6136
6172
  let message = `${stepTag}: Bot response `;
@@ -6141,7 +6177,7 @@ var ScriptingProvider_1 = class ScriptingProvider {
6141
6177
  message += `${tomatch.map(e => e ? '"' + e + '"' : '<any response>').join(', ')}`;
6142
6178
  throw new BotiumError$2(message, {
6143
6179
  type: 'asserter',
6144
- source: 'TextMatchAsserter',
6180
+ source: asserterType,
6145
6181
  context: {
6146
6182
  stepTag
6147
6183
  },
@@ -6160,7 +6196,9 @@ var ScriptingProvider_1 = class ScriptingProvider {
6160
6196
 
6161
6197
  debug$9(`assertBotNotResponse ${stepTag} ${meMsg ? `(${meMsg}) ` : ''}BOT: ${botresponse} != ${nottomatch} ...`);
6162
6198
 
6163
- const found = lodash.find(nottomatch, utt => this.matchFn(botresponse, utt));
6199
+ const found = lodash.find(nottomatch, utt => this.matchFn(botresponse, utt, this.caps[Capabilities.SCRIPTING_MATCHING_MODE_ARGS]));
6200
+
6201
+ const asserterType = this.caps[Capabilities.SCRIPTING_MATCHING_MODE] === 'wer' ? 'WerAsserter' : 'TextMatchAsserter';
6164
6202
 
6165
6203
  if (!lodash.isNil(found)) {
6166
6204
  let message = `${stepTag}: Bot response `;
@@ -6171,7 +6209,7 @@ var ScriptingProvider_1 = class ScriptingProvider {
6171
6209
  message += `${nottomatch.map(e => e ? '"' + e + '"' : '<any response>').join(', ')}`;
6172
6210
  throw new BotiumError$2(message, {
6173
6211
  type: 'asserter',
6174
- source: 'TextMatchAsserter',
6212
+ source: asserterType,
6175
6213
  context: {
6176
6214
  stepTag
6177
6215
  },
@@ -6418,7 +6456,7 @@ var ScriptingProvider_1 = class ScriptingProvider {
6418
6456
  }
6419
6457
 
6420
6458
  Match(botresponse, utterance) {
6421
- return this.matchFn(botresponse, utterance);
6459
+ return this.matchFn(botresponse, utterance, this.caps[Capabilities.SCRIPTING_MATCHING_MODE_ARGS]);
6422
6460
  }
6423
6461
 
6424
6462
  Compile(scriptBuffer, scriptFormat, scriptType) {
@@ -6938,11 +6976,12 @@ var ScriptingProvider_1 = class ScriptingProvider {
6938
6976
  * @param currentConvo
6939
6977
  * @param convoStepIndex
6940
6978
  * @param convoStepsStack list of ConvoSteps
6979
+ * @param context {width: }
6941
6980
  * @private
6942
6981
  */
6943
6982
 
6944
6983
 
6945
- _expandConvo(expandedConvos, currentConvo, convoStepIndex = 0, convoStepsStack = []) {
6984
+ _expandConvo(expandedConvos, currentConvo, convoStepIndex = 0, convoStepsStack = [], context = {}) {
6946
6985
  const utterancePostfix = (lineTag, uttOrUserInput) => {
6947
6986
  const naming = this.caps[Capabilities.SCRIPTING_UTTEXPANSION_NAMING_MODE] || Defaults$1.capabilities[Capabilities.SCRIPTING_UTTEXPANSION_NAMING_MODE];
6948
6987
 
@@ -6969,7 +7008,7 @@ var ScriptingProvider_1 = class ScriptingProvider {
6969
7008
  const currentStepsStack = convoStepsStack.slice();
6970
7009
  currentStepsStack.push(lodash.cloneDeep(currentStep));
6971
7010
 
6972
- this._expandConvo(expandedConvos, currentConvo, convoStepIndex + 1, currentStepsStack);
7011
+ this._expandConvo(expandedConvos, currentConvo, convoStepIndex + 1, currentStepsStack, context);
6973
7012
  } else if (currentStep.sender === 'me') {
6974
7013
  let useUnexpanded = true;
6975
7014
 
@@ -6990,39 +7029,87 @@ var ScriptingProvider_1 = class ScriptingProvider {
6990
7029
 
6991
7030
  if (this.utterances[uttName]) {
6992
7031
  const allutterances = this.utterances[uttName].utterances;
6993
- let sampleutterances = allutterances;
6994
-
6995
- if (this.caps[Capabilities.SCRIPTING_UTTEXPANSION_MODE] === 'first') {
6996
- sampleutterances = [allutterances[0]];
6997
- } else if (this.caps[Capabilities.SCRIPTING_UTTEXPANSION_MODE] === 'random') {
6998
- sampleutterances = allutterances.map(x => ({
6999
- x,
7000
- r: Math.random()
7001
- })).sort((a, b) => a.r - b.r).map(a => a.x).slice(0, this.caps[Capabilities.SCRIPTING_UTTEXPANSION_RANDOM_COUNT]);
7002
- }
7003
7032
 
7004
- sampleutterances.forEach((utt, index) => {
7005
- const lineTag = `${index + 1}`.padStart(`${sampleutterances.length}`.length, '0');
7033
+ const processSampleUtterances = (sampleutterances, myContext) => {
7034
+ sampleutterances.forEach((utt, index) => {
7035
+ processSampleUtterance(utt, sampleutterances.length, index, Object.assign({
7036
+ indexExpansionModeIndex: index
7037
+ }, myContext || context));
7038
+ });
7039
+ };
7040
+
7041
+ const processSampleUtterance = (sampleutterance, length, index, myContext) => {
7042
+ const lineTag = `${index + 1}`.padStart(`${length}`.length, '0');
7006
7043
  const currentStepsStack = convoStepsStack.slice();
7007
7044
 
7008
7045
  if (uttArgs) {
7009
- utt = util.format(utt, ...uttArgs);
7046
+ sampleutterance = util.format(sampleutterance, ...uttArgs);
7010
7047
  }
7011
7048
 
7012
7049
  currentStepsStack.push(Object.assign(lodash.cloneDeep(currentStep), {
7013
- messageText: utt
7050
+ messageText: sampleutterance
7014
7051
  }));
7015
7052
 
7016
7053
  const currentConvoLabeled = lodash.cloneDeep(currentConvo);
7017
7054
 
7018
7055
  Object.assign(currentConvoLabeled.header, {
7019
- name: `${currentConvo.header.name}/${uttName}-${utterancePostfix(lineTag, utt)}`
7056
+ name: `${currentConvo.header.name}/${uttName}-${utterancePostfix(lineTag, sampleutterance)}`
7020
7057
  });
7021
7058
  if (!currentConvoLabeled.sourceTag) currentConvoLabeled.sourceTag = {};
7022
7059
  if (!currentConvoLabeled.sourceTag.origConvoName) currentConvoLabeled.sourceTag.origConvoName = currentConvo.header.name;
7023
7060
 
7024
- this._expandConvo(expandedConvos, currentConvoLabeled, convoStepIndex + 1, currentStepsStack);
7025
- });
7061
+ this._expandConvo(expandedConvos, currentConvoLabeled, convoStepIndex + 1, currentStepsStack, myContext || context);
7062
+ };
7063
+
7064
+ if (this.caps[Capabilities.SCRIPTING_UTTEXPANSION_MODE] === 'index') {
7065
+ if (lodash.isNil(context.indexExpansionModeWidth)) {
7066
+ // executed for the first found utterance
7067
+ processSampleUtterances(allutterances, Object.assign({}, context, {
7068
+ indexExpansionModeWidth: allutterances.length
7069
+ }));
7070
+ } else {
7071
+ if (lodash.isNil(context.indexExpansionModeIndex)) {
7072
+ throw new Error('indexExpansionModeIndex must be set!');
7073
+ } // executing the current 'thread', if current utterance has no example to current index, fallback to the last one
7074
+
7075
+
7076
+ const localIndex = Math.min(context.indexExpansionModeIndex, allutterances.length - 1);
7077
+
7078
+ if (localIndex < context.indexExpansionModeIndex && context.indexExpansionModeIndex === context.indexExpansionModeWidth - 1) {
7079
+ debug$9(`While expanding convos by index found in utterance "${uttName}" less examples (${allutterances.length}) as expected (${context.indexExpansionModeWidth})`);
7080
+ }
7081
+
7082
+ const myContext = Object.assign({}, context, {
7083
+ indexExpansionModeWidth: Math.max(allutterances.length, context.indexExpansionModeWidth)
7084
+ });
7085
+ processSampleUtterance(allutterances[localIndex], allutterances.length, localIndex, myContext);
7086
+
7087
+ if (allutterances.length > context.indexExpansionModeWidth && context.indexExpansionModeIndex + 1 === context.indexExpansionModeWidth) {
7088
+ debug$9(`While expanding convos by index found in utterance "${uttName}" more examples (${allutterances.length}) as expected (${context.indexExpansionModeWidth})`);
7089
+
7090
+ for (let i = context.indexExpansionModeWidth; i < allutterances.length; i++) {
7091
+ // if we found a utterance with more examples as any utterances before, we have to start new 'thread'
7092
+ const myContext = Object.assign({}, context, {
7093
+ indexExpansionModeWidth: allutterances.length,
7094
+ indexExpansionModeIndex: i
7095
+ });
7096
+ processSampleUtterance(allutterances[i], allutterances.length, i, myContext);
7097
+ }
7098
+ }
7099
+ }
7100
+ } else {
7101
+ if (this.caps[Capabilities.SCRIPTING_UTTEXPANSION_MODE] === 'first') {
7102
+ processSampleUtterances([allutterances[0]]);
7103
+ } else if (this.caps[Capabilities.SCRIPTING_UTTEXPANSION_MODE] === 'random') {
7104
+ processSampleUtterances(allutterances.map(x => ({
7105
+ x,
7106
+ r: Math.random()
7107
+ })).sort((a, b) => a.r - b.r).map(a => a.x).slice(0, this.caps[Capabilities.SCRIPTING_UTTEXPANSION_RANDOM_COUNT]));
7108
+ } else {
7109
+ processSampleUtterances(allutterances);
7110
+ }
7111
+ }
7112
+
7026
7113
  useUnexpanded = false;
7027
7114
  }
7028
7115
  }
@@ -7039,19 +7126,17 @@ var ScriptingProvider_1 = class ScriptingProvider {
7039
7126
  });
7040
7127
 
7041
7128
  if (expandedUserInputs && expandedUserInputs.length > 0) {
7042
- let sampleinputs = expandedUserInputs;
7043
-
7044
- if (this.caps[Capabilities.SCRIPTING_UTTEXPANSION_MODE] === 'first') {
7045
- sampleinputs = [expandedUserInputs[0]];
7046
- } else if (this.caps[Capabilities.SCRIPTING_UTTEXPANSION_MODE] === 'random') {
7047
- sampleinputs = expandedUserInputs.map(x => ({
7048
- x,
7049
- r: Math.random()
7050
- })).sort((a, b) => a.r - b.r).map(a => a.x).slice(0, this.caps[Capabilities.SCRIPTING_UTTEXPANSION_RANDOM_COUNT]);
7051
- }
7129
+ // let sampleinputs = expandedUserInputs
7130
+ const processSampleInputs = (sampleinputs, myContext, uiIndex) => {
7131
+ sampleinputs.forEach((input, index) => {
7132
+ processSampleInput(input, sampleinputs.length, index, Object.assign({
7133
+ indexExpansionModeIndex: index
7134
+ }, myContext || context), uiIndex);
7135
+ });
7136
+ };
7052
7137
 
7053
- sampleinputs.forEach((sampleinput, index) => {
7054
- const lineTag = `${index + 1}`.padStart(`${sampleinputs.length}`.length, '0');
7138
+ const processSampleInput = (sampleinput, length, index, myContext, uiIndex) => {
7139
+ const lineTag = `${index + 1}`.padStart(`${length}`.length, '0');
7055
7140
  const currentStepsStack = convoStepsStack.slice();
7056
7141
 
7057
7142
  const currentStepMod = lodash.cloneDeep(currentStep);
@@ -7065,8 +7150,56 @@ var ScriptingProvider_1 = class ScriptingProvider {
7065
7150
  name: `${currentConvo.header.name}/${ui.name}-${utterancePostfix(lineTag, sampleinput.args && sampleinput.args.length ? sampleinput.args.join(', ') : 'no-args')}`
7066
7151
  });
7067
7152
 
7068
- this._expandConvo(expandedConvos, currentConvoLabeled, convoStepIndex + 1, currentStepsStack);
7069
- });
7153
+ this._expandConvo(expandedConvos, currentConvoLabeled, convoStepIndex + 1, currentStepsStack, myContext || context);
7154
+ };
7155
+
7156
+ if (this.caps[Capabilities.SCRIPTING_UTTEXPANSION_MODE] === 'index') {
7157
+ if (lodash.isNil(context.indexExpansionModeWidth)) {
7158
+ processSampleInputs(expandedUserInputs, Object.assign({}, context, {
7159
+ indexExpansionModeWidth: expandedUserInputs.length
7160
+ }), uiIndex);
7161
+ } else {
7162
+ if (lodash.isNil(context.indexExpansionModeIndex)) {
7163
+ throw new Error('indexExpansionModeIndex must be set!');
7164
+ } // executing the current 'thread', if current utterance has no example to current index, fallback to the last one
7165
+
7166
+
7167
+ const localIndex = Math.min(context.indexExpansionModeIndex, expandedUserInputs.length - 1);
7168
+
7169
+ if (localIndex < context.indexExpansionModeIndex && context.indexExpansionModeIndex === context.indexExpansionModeWidth - 1) {
7170
+ debug$9(`While expanding convos by index found user input "${ui.name}, ${ui.args}" less examples (${expandedUserInputs.length}) as expected (${context.indexExpansionModeWidth})`);
7171
+ }
7172
+
7173
+ const myContext = Object.assign({}, context, {
7174
+ indexExpansionModeWidth: Math.max(expandedUserInputs.length, context.indexExpansionModeWidth)
7175
+ });
7176
+ processSampleInput(expandedUserInputs[localIndex], expandedUserInputs.length, localIndex, myContext, uiIndex);
7177
+
7178
+ if (expandedUserInputs.length > context.indexExpansionModeWidth && context.indexExpansionModeIndex + 1 === context.indexExpansionModeWidth) {
7179
+ debug$9(`While expanding convos by index found user input "${ui.name}, ${ui.args}" more examples (${expandedUserInputs.length}) as expected (${context.indexExpansionModeWidth})`);
7180
+
7181
+ for (let i = context.indexExpansionModeWidth; i < expandedUserInputs.length; i++) {
7182
+ const myContext = Object.assign({}, context, {
7183
+ indexExpansionModeWidth: expandedUserInputs.length,
7184
+ indexExpansionModeIndex: i
7185
+ });
7186
+ processSampleInput(expandedUserInputs[i], expandedUserInputs.length, i, myContext, uiIndex);
7187
+ }
7188
+ }
7189
+ }
7190
+ } else {
7191
+ if (this.caps[Capabilities.SCRIPTING_UTTEXPANSION_MODE] === 'first') {
7192
+ processSampleInputs([expandedUserInputs[0]], context, uiIndex);
7193
+ } else if (this.caps[Capabilities.SCRIPTING_UTTEXPANSION_MODE] === 'random') {
7194
+ processSampleInputs(expandedUserInputs.map(x => ({
7195
+ x,
7196
+ r: Math.random()
7197
+ })).sort((a, b) => a.r - b.r).map(a => a.x).slice(0, this.caps[Capabilities.SCRIPTING_UTTEXPANSION_RANDOM_COUNT]), context, uiIndex);
7198
+ } else {
7199
+ processSampleInputs(expandedUserInputs, context, uiIndex);
7200
+ }
7201
+ }
7202
+
7070
7203
  useUnexpanded = false;
7071
7204
  }
7072
7205
  }
@@ -7077,7 +7210,7 @@ var ScriptingProvider_1 = class ScriptingProvider {
7077
7210
  const currentStepsStack = convoStepsStack.slice();
7078
7211
  currentStepsStack.push(lodash.cloneDeep(currentStep));
7079
7212
 
7080
- this._expandConvo(expandedConvos, currentConvo, convoStepIndex + 1, currentStepsStack);
7213
+ this._expandConvo(expandedConvos, currentConvo, convoStepIndex + 1, currentStepsStack, context);
7081
7214
  }
7082
7215
  }
7083
7216
  } else {
@@ -8133,7 +8266,8 @@ const setupEndpoints = ({
8133
8266
  processEvent({
8134
8267
  originalUrl: req.originalUrl,
8135
8268
  originalMethod: req.method,
8136
- body: req.body
8269
+ body: req.body,
8270
+ headers: req.headers
8137
8271
  });
8138
8272
  res.status(200).json({}).end();
8139
8273
  } else {
@@ -8238,7 +8372,7 @@ var SimpleRestContainer_1 = class SimpleRestContainer {
8238
8372
  });
8239
8373
 
8240
8374
  for (const event of sortedEvents) {
8241
- setTimeout(() => this._processBodyAsync(event.body, true, !!this.caps[Capabilities.SIMPLEREST_INBOUND_UPDATE_CONTEXT]), 0);
8375
+ setTimeout(() => this._processBodyAsync(event.body, event.headers, true, !!this.caps[Capabilities.SIMPLEREST_INBOUND_UPDATE_CONTEXT]), 0);
8242
8376
  }
8243
8377
  }, debounceTimeout);
8244
8378
  }
@@ -8333,12 +8467,15 @@ var SimpleRestContainer_1 = class SimpleRestContainer {
8333
8467
  this._startPolling().then(() => startPollingComplete()).catch(startPollingComplete);
8334
8468
  }, pingComplete => {
8335
8469
  if (this.caps[Capabilities.SIMPLEREST_PING_URL]) {
8336
- this._makeCall('SIMPLEREST_PING').then(body => {
8470
+ this._makeCall('SIMPLEREST_PING').then(({
8471
+ body,
8472
+ headers
8473
+ }) => {
8337
8474
  if (this.caps[Capabilities.SIMPLEREST_PING_UPDATE_CONTEXT] || this.caps[Capabilities.SIMPLEREST_PING_PROCESS_RESPONSE]) {
8338
8475
  return this._parseResponseBody(body).then(body => {
8339
8476
  if (body) {
8340
8477
  debug$4(`Ping Uri ${this.caps[Capabilities.SIMPLEREST_PING_URL]} returned JSON response: ${Utils.shortenJsonString(body)}`);
8341
- return this._processBodyAsync(body, !!this.caps[Capabilities.SIMPLEREST_PING_PROCESS_RESPONSE], !!this.caps[Capabilities.SIMPLEREST_PING_UPDATE_CONTEXT]);
8478
+ return this._processBodyAsync(body, headers, !!this.caps[Capabilities.SIMPLEREST_PING_PROCESS_RESPONSE], !!this.caps[Capabilities.SIMPLEREST_PING_UPDATE_CONTEXT]);
8342
8479
  } else {
8343
8480
  debug$4(`Ping Uri ${this.caps[Capabilities.SIMPLEREST_PING_URL]} didn't return JSON response, ignoring it.`);
8344
8481
  }
@@ -8366,12 +8503,15 @@ var SimpleRestContainer_1 = class SimpleRestContainer {
8366
8503
  this.processInbound = true;
8367
8504
 
8368
8505
  if (this.caps[Capabilities.SIMPLEREST_START_URL]) {
8369
- this._makeCall('SIMPLEREST_START').then(body => {
8506
+ this._makeCall('SIMPLEREST_START').then(({
8507
+ body,
8508
+ headers
8509
+ }) => {
8370
8510
  if (this.caps[Capabilities.SIMPLEREST_START_UPDATE_CONTEXT] || this.caps[Capabilities.SIMPLEREST_START_PROCESS_RESPONSE]) {
8371
8511
  return this._parseResponseBody(body).then(body => {
8372
8512
  if (body) {
8373
8513
  debug$4(`Start Uri ${this.caps[Capabilities.SIMPLEREST_START_URL]} returned JSON response: ${Utils.shortenJsonString(body)}`);
8374
- return this._processBodyAsync(body, !!this.caps[Capabilities.SIMPLEREST_START_PROCESS_RESPONSE], !!this.caps[Capabilities.SIMPLEREST_START_UPDATE_CONTEXT]);
8514
+ return this._processBodyAsync(body, headers, !!this.caps[Capabilities.SIMPLEREST_START_PROCESS_RESPONSE], !!this.caps[Capabilities.SIMPLEREST_START_UPDATE_CONTEXT]);
8375
8515
  } else {
8376
8516
  debug$4(`Start Uri ${this.caps[Capabilities.SIMPLEREST_START_URL]} didn't return JSON response, ignoring it.`);
8377
8517
  }
@@ -8423,10 +8563,10 @@ var SimpleRestContainer_1 = class SimpleRestContainer {
8423
8563
  } // Separated just for better module testing
8424
8564
 
8425
8565
 
8426
- async _processBodyAsync(body, isFromUser, updateContext) {
8566
+ async _processBodyAsync(body, headers, isFromUser, updateContext) {
8427
8567
  const p = async () => {
8428
8568
  try {
8429
- const results = await this._processBodyAsyncImpl(body, isFromUser, updateContext);
8569
+ const results = await this._processBodyAsyncImpl(body, headers, isFromUser, updateContext);
8430
8570
 
8431
8571
  if (results) {
8432
8572
  for (const result of results) {
@@ -8457,7 +8597,12 @@ var SimpleRestContainer_1 = class SimpleRestContainer {
8457
8597
  } // Separated just for better module testing
8458
8598
 
8459
8599
 
8460
- async _processBodyAsyncImpl(body, isFromUser, updateContext) {
8600
+ async _processBodyAsyncImpl(body, headers, isFromUser, updateContext) {
8601
+ this.view.response = {
8602
+ body,
8603
+ headers
8604
+ };
8605
+
8461
8606
  if (updateContext) {
8462
8607
  const mergeMode = this.caps[Capabilities.SIMPLEREST_CONTEXT_MERGE_OR_REPLACE];
8463
8608
  const jsonPathsContext = getAllCapValues(Capabilities.SIMPLEREST_CONTEXT_JSONPATH, this.caps);
@@ -8667,7 +8812,7 @@ var SimpleRestContainer_1 = class SimpleRestContainer {
8667
8812
  }
8668
8813
 
8669
8814
  if (body) {
8670
- debug$4(`got response code: ${response.statusCode}, body: ${Utils.shortenJsonString(body)}`);
8815
+ debug$4(`got response code: ${response.statusCode}, body: ${Utils.shortenJsonString(body)}, headers: ${Utils.shortenJsonString(response.headers)}`);
8671
8816
 
8672
8817
  this._storeCookiesFromResponse(response);
8673
8818
 
@@ -8683,7 +8828,7 @@ var SimpleRestContainer_1 = class SimpleRestContainer {
8683
8828
  }
8684
8829
 
8685
8830
  if (body) {
8686
- this._processBodyAsync(body, isFromUser, updateContext).then(() => resolve(this)).then(() => this._emptyWaitProcessQueue());
8831
+ this._processBodyAsync(body, response.headers, isFromUser, updateContext).then(() => resolve(this)).then(() => this._emptyWaitProcessQueue());
8687
8832
  } else {
8688
8833
  debug$4('ignoring response body (no string and no JSON object)');
8689
8834
  resolve(this);
@@ -8847,10 +8992,13 @@ var SimpleRestContainer_1 = class SimpleRestContainer {
8847
8992
  this._storeCookiesFromResponse(response);
8848
8993
 
8849
8994
  if (debug$4.enabled && body) {
8850
- debug$4(Utils.shortenJsonString(body));
8995
+ debug$4(`body: ${Utils.shortenJsonString(body)}, headers: ${Utils.shortenJsonString(response.headers)}`);
8851
8996
  }
8852
8997
 
8853
- return body;
8998
+ return {
8999
+ body,
9000
+ headers: response.headers
9001
+ };
8854
9002
  }
8855
9003
  }
8856
9004
  }
@@ -8894,7 +9042,11 @@ var SimpleRestContainer_1 = class SimpleRestContainer {
8894
9042
  try {
8895
9043
  return JSON.parse(raw);
8896
9044
  } catch (err) {
8897
- return new Error(`JSON parsing failed - try to use {{#fnc.jsonify}}{{xxx}}{{/fnc.jsonify}} to escape JSON special characters (ERR: ${err.message})`);
9045
+ if (debug$4.enabled) {
9046
+ debug$4(`JSON parsing failed (${err.message}) for: ${Utils.shortenJsonString(raw)}`);
9047
+ }
9048
+
9049
+ throw new Error(`JSON parsing failed - try to use {{#fnc.jsonify}}{{xxx}}{{/fnc.jsonify}} to escape JSON special characters (ERR: ${err.message})`);
8898
9050
  }
8899
9051
  } else {
8900
9052
  return raw;
@@ -8939,7 +9091,7 @@ var SimpleRestContainer_1 = class SimpleRestContainer {
8939
9091
 
8940
9092
  this._processOrderedInboundEventsArrayAsync();
8941
9093
  } else {
8942
- setTimeout(() => this._processBodyAsync(event.body, true, !!this.caps[Capabilities.SIMPLEREST_INBOUND_UPDATE_CONTEXT]), 0);
9094
+ setTimeout(() => this._processBodyAsync(event.body, event.headers, true, !!this.caps[Capabilities.SIMPLEREST_INBOUND_UPDATE_CONTEXT]), 0);
8943
9095
  }
8944
9096
  }
8945
9097
 
@@ -9080,7 +9232,7 @@ var SimpleRestContainer_1 = class SimpleRestContainer {
9080
9232
  debug$4(Utils.shortenJsonString(body));
9081
9233
  }
9082
9234
  } else if (body) {
9083
- debug$4(`_runPolling: got response code: ${response.statusCode}, body: ${Utils.shortenJsonString(body)}`);
9235
+ debug$4(`_runPolling: got response code: ${response.statusCode}, body: ${Utils.shortenJsonString(body)}, headers: ${Utils.shortenJsonString(response.headers)}`);
9084
9236
 
9085
9237
  this._storeCookiesFromResponse(response);
9086
9238
 
@@ -9092,7 +9244,7 @@ var SimpleRestContainer_1 = class SimpleRestContainer {
9092
9244
  }
9093
9245
 
9094
9246
  if (body) {
9095
- setTimeout(() => this._processBodyAsync(body, true, !!this.caps[Capabilities.SIMPLEREST_POLL_UPDATE_CONTEXT]), 0);
9247
+ setTimeout(() => this._processBodyAsync(body, response.headers, true, !!this.caps[Capabilities.SIMPLEREST_POLL_UPDATE_CONTEXT]), 0);
9096
9248
  } else {
9097
9249
  debug$4('_runPolling: ignoring response body (no string and no JSON object)');
9098
9250
  }