botium-core 1.13.0 → 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.
- package/.eslintrc.js +6 -3
- package/dist/botium-cjs.js +305 -123
- package/dist/botium-cjs.js.map +1 -1
- package/dist/botium-es.js +323 -142
- package/dist/botium-es.js.map +1 -1
- package/package.json +17 -15
- package/src/Capabilities.js +2 -1
- package/src/containers/plugins/SimpleRestContainer.js +23 -16
- package/src/grid/inbound/proxy.js +2 -1
- package/src/helpers/RetryHelper.js +13 -7
- package/src/scripting/Convo.js +36 -10
- package/src/scripting/MatchFunctions.js +10 -0
- package/src/scripting/ScriptingProvider.js +106 -37
- package/src/scripting/logichook/LogicHookConsts.js +1 -1
- package/src/scripting/logichook/LogicHookUtils.js +1 -1
- package/src/scripting/logichook/asserter/WerAsserter.js +59 -0
- package/src/scripting/logichook/logichooks/UpdateCustomLogicHook.js +3 -2
- package/test/compiler/compilercsv.spec.js +104 -3
- package/test/compiler/compilerjson.spec.js +0 -2
- package/test/compiler/compilerxlsx.spec.js +1 -1
- package/test/compiler/convos/csv/utterances_liveperson2.csv +12 -0
- package/test/connectors/simplerest.spec.js +1012 -969
- package/test/convo/fillAndApplyScriptingMemory.spec.js +804 -785
- package/test/convo/partialconvo.spec.js +345 -339
- package/test/convo/retryconvo.spec.js +134 -0
- package/test/driver/capabilities.spec.js +156 -151
- package/test/logichooks/hookfromsrc.spec.js +79 -73
- package/test/plugins/plugins.spec.js +44 -42
- package/test/scripting/asserters/buttonsAsserter.spec.js +257 -240
- package/test/scripting/asserters/cardsAsserter.spec.js +214 -212
- package/test/scripting/asserters/convos/wer_threshold_nok.yml +7 -0
- package/test/scripting/asserters/convos/wer_threshold_ok.yml +7 -0
- package/test/scripting/asserters/intentConfidenceAsserter.spec.js +34 -35
- package/test/scripting/asserters/jsonpathAsserter.spec.js +307 -308
- package/test/scripting/asserters/mediaAsserter.spec.js +236 -234
- package/test/scripting/asserters/werAsserter.spec.js +51 -0
- package/test/scripting/logichooks/setClearScriptingMemory.spec.js +202 -192
- package/test/scripting/matching/matchingmode.spec.js +306 -258
- package/test/scripting/scriptingProvider.spec.js +666 -633
- package/test/scripting/scriptingmemory/fillScriptingMemoryFromFile.spec.js +299 -281
- package/test/scripting/scriptingmemory/useScriptingMemoryForAssertion.spec.js +94 -80
- package/test/scripting/userinputs/defaultUserInputs.spec.js +233 -127
- package/test/scripting/userinputs/mediaInputConvos.spec.js +409 -403
- package/test/scripting/utteranceexpansion/associateByIndex.spec.js +259 -0
- package/test/scripting/utteranceexpansion/convos/associate_utterances_by_index.json +33 -0
- package/test/scripting/utteranceexpansion/convos/media.convo.txt +19 -0
- package/test/scripting/utteranceexpansion/files/step0voice0.wav +0 -0
- package/test/scripting/utteranceexpansion/files/step0voice1.wav +0 -0
- package/test/scripting/utteranceexpansion/files/step0voice2.wav +0 -0
- package/test/scripting/utteranceexpansion/files/step1voice0.wav +0 -0
- package/test/scripting/utteranceexpansion/files/step2voice0.wav +0 -0
- package/test/scripting/utteranceexpansion/files/step2voice1.wav +0 -0
- package/test/scripting/utteranceexpansion/files/step2voice2.wav +0 -0
- package/test/scripting/utteranceexpansion/files/step2voice4.wav +0 -0
- package/test/scripting/utteranceexpansion/files/step2voice5.wav +0 -0
- package/test/security/allowUnsafe.spec.js +274 -268
- package/test/utils.spec.js +40 -38
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "botium-core",
|
|
3
|
-
"version": "1.13.
|
|
3
|
+
"version": "1.13.3",
|
|
4
4
|
"description": "The Selenium for Chatbots",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"module": "dist/botium-es.js",
|
|
@@ -32,17 +32,17 @@
|
|
|
32
32
|
},
|
|
33
33
|
"homepage": "https://www.botium.ai",
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@babel/runtime": "^7.18.
|
|
35
|
+
"@babel/runtime": "^7.18.6",
|
|
36
36
|
"async": "^3.2.4",
|
|
37
37
|
"body-parser": "^1.20.0",
|
|
38
38
|
"boolean": "^3.2.0",
|
|
39
39
|
"bottleneck": "^2.19.5",
|
|
40
|
-
"csv-parse": "^5.
|
|
40
|
+
"csv-parse": "^5.3.0",
|
|
41
41
|
"debug": "^4.3.4",
|
|
42
42
|
"esprima": "^4.0.1",
|
|
43
43
|
"express": "^4.18.1",
|
|
44
44
|
"globby": "11.0.4",
|
|
45
|
-
"ioredis": "^5.0
|
|
45
|
+
"ioredis": "^5.1.0",
|
|
46
46
|
"is-class": "^0.0.9",
|
|
47
47
|
"is-json": "^2.0.1",
|
|
48
48
|
"jsonpath": "^1.1.1",
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"markdown-it": "^13.0.1",
|
|
51
51
|
"mime-types": "^2.1.35",
|
|
52
52
|
"mkdirp": "^1.0.4",
|
|
53
|
-
"moment": "^2.29.
|
|
53
|
+
"moment": "^2.29.4",
|
|
54
54
|
"mustache": "^4.2.0",
|
|
55
55
|
"promise-retry": "^2.0.1",
|
|
56
56
|
"promise.allsettled": "^1.0.5",
|
|
@@ -65,33 +65,35 @@
|
|
|
65
65
|
"swagger-jsdoc": "^6.2.1",
|
|
66
66
|
"swagger-ui-express": "^4.4.0",
|
|
67
67
|
"uuid": "^8.3.2",
|
|
68
|
-
"vm2": "^3.9.
|
|
68
|
+
"vm2": "^3.9.10",
|
|
69
|
+
"word-error-rate": "0.0.7",
|
|
69
70
|
"write-yaml": "^1.0.0",
|
|
70
71
|
"xlsx": "^0.18.5",
|
|
71
72
|
"xregexp": "^5.1.1",
|
|
72
73
|
"yaml": "^2.1.1"
|
|
73
74
|
},
|
|
74
75
|
"devDependencies": {
|
|
75
|
-
"@babel/core": "^7.18.
|
|
76
|
-
"@babel/node": "^7.18.
|
|
77
|
-
"@babel/plugin-transform-runtime": "^7.18.
|
|
78
|
-
"@babel/preset-env": "^7.18.
|
|
76
|
+
"@babel/core": "^7.18.6",
|
|
77
|
+
"@babel/node": "^7.18.6",
|
|
78
|
+
"@babel/plugin-transform-runtime": "^7.18.6",
|
|
79
|
+
"@babel/preset-env": "^7.18.6",
|
|
79
80
|
"chai": "^4.3.6",
|
|
80
81
|
"chai-as-promised": "^7.1.1",
|
|
81
82
|
"cross-env": "^7.0.3",
|
|
82
|
-
"eslint": "^8.
|
|
83
|
+
"eslint": "^8.19.0",
|
|
83
84
|
"eslint-config-standard": "^17.0.0",
|
|
84
85
|
"eslint-plugin-import": "^2.26.0",
|
|
85
|
-
"eslint-plugin-
|
|
86
|
+
"eslint-plugin-mocha": "^10.1.0",
|
|
87
|
+
"eslint-plugin-n": "^15.2.4",
|
|
86
88
|
"eslint-plugin-promise": "^6.0.0",
|
|
87
89
|
"eslint-plugin-standard": "^4.1.0",
|
|
88
90
|
"license-checker": "^25.0.1",
|
|
89
91
|
"license-compatibility-checker": "^0.3.5",
|
|
90
92
|
"mocha": "^10.0.0",
|
|
91
|
-
"nock": "^13.2.
|
|
92
|
-
"npm-check-updates": "^
|
|
93
|
+
"nock": "^13.2.8",
|
|
94
|
+
"npm-check-updates": "^15.2.6",
|
|
93
95
|
"nyc": "^15.1.0",
|
|
94
|
-
"rollup": "^2.
|
|
96
|
+
"rollup": "^2.76.0",
|
|
95
97
|
"rollup-plugin-babel": "^4.4.0",
|
|
96
98
|
"rollup-plugin-commonjs": "^10.1.0",
|
|
97
99
|
"rollup-plugin-json": "^4.0.0",
|
package/src/Capabilities.js
CHANGED
|
@@ -126,8 +126,9 @@ module.exports = {
|
|
|
126
126
|
SCRIPTING_ENABLE_MULTIPLE_ASSERT_ERRORS: 'SCRIPTING_ENABLE_MULTIPLE_ASSERT_ERRORS',
|
|
127
127
|
SCRIPTING_ENABLE_SKIP_ASSERT_ERRORS: 'SCRIPTING_ENABLE_SKIP_ASSERT_ERRORS',
|
|
128
128
|
SCRIPTING_FORCE_BOT_CONSUMED: 'SCRIPTING_FORCE_BOT_CONSUMED',
|
|
129
|
-
// regexp, regexpIgnoreCase, wildcard, wildcardIgnoreCase, wildcardExact, wildcardExactIgnoreCase, include, includeIgnoreCase, equals, equalsIgnoreCase
|
|
129
|
+
// regexp, regexpIgnoreCase, wildcard, wildcardIgnoreCase, wildcardExact, wildcardExactIgnoreCase, include, includeIgnoreCase, equals, equalsIgnoreCase, wer
|
|
130
130
|
SCRIPTING_MATCHING_MODE: 'SCRIPTING_MATCHING_MODE',
|
|
131
|
+
SCRIPTING_MATCHING_MODE_ARGS: 'SCRIPTING_MATCHING_MODE_ARGS',
|
|
131
132
|
// all, first, random
|
|
132
133
|
SCRIPTING_UTTEXPANSION_MODE: 'SCRIPTING_UTTEXPANSION_MODE',
|
|
133
134
|
SCRIPTING_UTTEXPANSION_RANDOM_COUNT: 'SCRIPTING_UTTEXPANSION_RANDOM_COUNT',
|
|
@@ -42,7 +42,7 @@ module.exports = class SimpleRestContainer {
|
|
|
42
42
|
return qr[0]
|
|
43
43
|
})
|
|
44
44
|
for (const event of sortedEvents) {
|
|
45
|
-
setTimeout(() => this._processBodyAsync(event.body, true, !!this.caps[Capabilities.SIMPLEREST_INBOUND_UPDATE_CONTEXT]), 0)
|
|
45
|
+
setTimeout(() => this._processBodyAsync(event.body, event.headers, true, !!this.caps[Capabilities.SIMPLEREST_INBOUND_UPDATE_CONTEXT]), 0)
|
|
46
46
|
}
|
|
47
47
|
}, debounceTimeout)
|
|
48
48
|
}
|
|
@@ -142,13 +142,13 @@ module.exports = class SimpleRestContainer {
|
|
|
142
142
|
(pingComplete) => {
|
|
143
143
|
if (this.caps[Capabilities.SIMPLEREST_PING_URL]) {
|
|
144
144
|
this._makeCall('SIMPLEREST_PING')
|
|
145
|
-
.then(body => {
|
|
145
|
+
.then(({ body, headers }) => {
|
|
146
146
|
if (this.caps[Capabilities.SIMPLEREST_PING_UPDATE_CONTEXT] || this.caps[Capabilities.SIMPLEREST_PING_PROCESS_RESPONSE]) {
|
|
147
147
|
return this._parseResponseBody(body)
|
|
148
148
|
.then(body => {
|
|
149
149
|
if (body) {
|
|
150
150
|
debug(`Ping Uri ${this.caps[Capabilities.SIMPLEREST_PING_URL]} returned JSON response: ${botiumUtils.shortenJsonString(body)}`)
|
|
151
|
-
return this._processBodyAsync(body, !!this.caps[Capabilities.SIMPLEREST_PING_PROCESS_RESPONSE], !!this.caps[Capabilities.SIMPLEREST_PING_UPDATE_CONTEXT])
|
|
151
|
+
return this._processBodyAsync(body, headers, !!this.caps[Capabilities.SIMPLEREST_PING_PROCESS_RESPONSE], !!this.caps[Capabilities.SIMPLEREST_PING_UPDATE_CONTEXT])
|
|
152
152
|
} else {
|
|
153
153
|
debug(`Ping Uri ${this.caps[Capabilities.SIMPLEREST_PING_URL]} didn't return JSON response, ignoring it.`)
|
|
154
154
|
}
|
|
@@ -178,13 +178,13 @@ module.exports = class SimpleRestContainer {
|
|
|
178
178
|
this.processInbound = true
|
|
179
179
|
if (this.caps[Capabilities.SIMPLEREST_START_URL]) {
|
|
180
180
|
this._makeCall('SIMPLEREST_START')
|
|
181
|
-
.then(body => {
|
|
181
|
+
.then(({ body, headers }) => {
|
|
182
182
|
if (this.caps[Capabilities.SIMPLEREST_START_UPDATE_CONTEXT] || this.caps[Capabilities.SIMPLEREST_START_PROCESS_RESPONSE]) {
|
|
183
183
|
return this._parseResponseBody(body)
|
|
184
184
|
.then(body => {
|
|
185
185
|
if (body) {
|
|
186
186
|
debug(`Start Uri ${this.caps[Capabilities.SIMPLEREST_START_URL]} returned JSON response: ${botiumUtils.shortenJsonString(body)}`)
|
|
187
|
-
return this._processBodyAsync(body, !!this.caps[Capabilities.SIMPLEREST_START_PROCESS_RESPONSE], !!this.caps[Capabilities.SIMPLEREST_START_UPDATE_CONTEXT])
|
|
187
|
+
return this._processBodyAsync(body, headers, !!this.caps[Capabilities.SIMPLEREST_START_PROCESS_RESPONSE], !!this.caps[Capabilities.SIMPLEREST_START_UPDATE_CONTEXT])
|
|
188
188
|
} else {
|
|
189
189
|
debug(`Start Uri ${this.caps[Capabilities.SIMPLEREST_START_URL]} didn't return JSON response, ignoring it.`)
|
|
190
190
|
}
|
|
@@ -235,10 +235,10 @@ module.exports = class SimpleRestContainer {
|
|
|
235
235
|
}
|
|
236
236
|
|
|
237
237
|
// Separated just for better module testing
|
|
238
|
-
async _processBodyAsync (body, isFromUser, updateContext) {
|
|
238
|
+
async _processBodyAsync (body, headers, isFromUser, updateContext) {
|
|
239
239
|
const p = async () => {
|
|
240
240
|
try {
|
|
241
|
-
const results = await this._processBodyAsyncImpl(body, isFromUser, updateContext)
|
|
241
|
+
const results = await this._processBodyAsyncImpl(body, headers, isFromUser, updateContext)
|
|
242
242
|
if (results) {
|
|
243
243
|
for (const result of results) {
|
|
244
244
|
setTimeout(() => this.queueBotSays(result), 0)
|
|
@@ -266,7 +266,8 @@ module.exports = class SimpleRestContainer {
|
|
|
266
266
|
}
|
|
267
267
|
|
|
268
268
|
// Separated just for better module testing
|
|
269
|
-
async _processBodyAsyncImpl (body, isFromUser, updateContext) {
|
|
269
|
+
async _processBodyAsyncImpl (body, headers, isFromUser, updateContext) {
|
|
270
|
+
this.view.response = { body, headers }
|
|
270
271
|
if (updateContext) {
|
|
271
272
|
const mergeMode = this.caps[Capabilities.SIMPLEREST_CONTEXT_MERGE_OR_REPLACE]
|
|
272
273
|
const jsonPathsContext = getAllCapValues(Capabilities.SIMPLEREST_CONTEXT_JSONPATH, this.caps)
|
|
@@ -451,7 +452,7 @@ module.exports = class SimpleRestContainer {
|
|
|
451
452
|
}
|
|
452
453
|
|
|
453
454
|
if (body) {
|
|
454
|
-
debug(`got response code: ${response.statusCode}, body: ${botiumUtils.shortenJsonString(body)}`)
|
|
455
|
+
debug(`got response code: ${response.statusCode}, body: ${botiumUtils.shortenJsonString(body)}, headers: ${botiumUtils.shortenJsonString(response.headers)}`)
|
|
455
456
|
this._storeCookiesFromResponse(response)
|
|
456
457
|
try {
|
|
457
458
|
body = await this._parseResponseBody(body)
|
|
@@ -463,7 +464,7 @@ module.exports = class SimpleRestContainer {
|
|
|
463
464
|
}
|
|
464
465
|
|
|
465
466
|
if (body) {
|
|
466
|
-
this._processBodyAsync(body, isFromUser, updateContext).then(() => resolve(this)).then(() => this._emptyWaitProcessQueue())
|
|
467
|
+
this._processBodyAsync(body, response.headers, isFromUser, updateContext).then(() => resolve(this)).then(() => this._emptyWaitProcessQueue())
|
|
467
468
|
} else {
|
|
468
469
|
debug('ignoring response body (no string and no JSON object)')
|
|
469
470
|
resolve(this)
|
|
@@ -520,6 +521,9 @@ module.exports = class SimpleRestContainer {
|
|
|
520
521
|
try {
|
|
521
522
|
requestOptions.body = this._getMustachedCap(Capabilities.SIMPLEREST_BODY_TEMPLATE, !bodyRaw)
|
|
522
523
|
requestOptions.json = !bodyRaw
|
|
524
|
+
if (requestOptions.json && (!requestOptions.body || Object.keys(requestOptions.body).length === 0)) {
|
|
525
|
+
debug(`warning: requestOptions.body content seems to be empty - ${requestOptions.body} - capability: "${this.caps[Capabilities.SIMPLEREST_BODY_TEMPLATE]}"`)
|
|
526
|
+
}
|
|
523
527
|
} catch (err) {
|
|
524
528
|
throw new Error(`composing body from SIMPLEREST_BODY_TEMPLATE failed (${err.message})`)
|
|
525
529
|
}
|
|
@@ -596,9 +600,9 @@ module.exports = class SimpleRestContainer {
|
|
|
596
600
|
debug(`_waitForUrlResponse success on url check ${pingConfig.uri}: ${response.statusCode}/${response.statusMessage}`)
|
|
597
601
|
this._storeCookiesFromResponse(response)
|
|
598
602
|
if (debug.enabled && body) {
|
|
599
|
-
debug(botiumUtils.shortenJsonString(body))
|
|
603
|
+
debug(`body: ${botiumUtils.shortenJsonString(body)}, headers: ${botiumUtils.shortenJsonString(response.headers)}`)
|
|
600
604
|
}
|
|
601
|
-
return body
|
|
605
|
+
return { body, headers: response.headers }
|
|
602
606
|
}
|
|
603
607
|
}
|
|
604
608
|
}
|
|
@@ -635,7 +639,10 @@ module.exports = class SimpleRestContainer {
|
|
|
635
639
|
try {
|
|
636
640
|
return JSON.parse(raw)
|
|
637
641
|
} catch (err) {
|
|
638
|
-
|
|
642
|
+
if (debug.enabled) {
|
|
643
|
+
debug(`JSON parsing failed (${err.message}) for: ${botiumUtils.shortenJsonString(raw)}`)
|
|
644
|
+
}
|
|
645
|
+
throw new Error(`JSON parsing failed - try to use {{#fnc.jsonify}}{{xxx}}{{/fnc.jsonify}} to escape JSON special characters (ERR: ${err.message})`)
|
|
639
646
|
}
|
|
640
647
|
} else {
|
|
641
648
|
return raw
|
|
@@ -673,7 +680,7 @@ module.exports = class SimpleRestContainer {
|
|
|
673
680
|
this.inboundEvents.push(event)
|
|
674
681
|
this._processOrderedInboundEventsArrayAsync()
|
|
675
682
|
} else {
|
|
676
|
-
setTimeout(() => this._processBodyAsync(event.body, true, !!this.caps[Capabilities.SIMPLEREST_INBOUND_UPDATE_CONTEXT]), 0)
|
|
683
|
+
setTimeout(() => this._processBodyAsync(event.body, event.headers, true, !!this.caps[Capabilities.SIMPLEREST_INBOUND_UPDATE_CONTEXT]), 0)
|
|
677
684
|
}
|
|
678
685
|
}
|
|
679
686
|
|
|
@@ -797,7 +804,7 @@ module.exports = class SimpleRestContainer {
|
|
|
797
804
|
debug(botiumUtils.shortenJsonString(body))
|
|
798
805
|
}
|
|
799
806
|
} else if (body) {
|
|
800
|
-
debug(`_runPolling: got response code: ${response.statusCode}, body: ${botiumUtils.shortenJsonString(body)}`)
|
|
807
|
+
debug(`_runPolling: got response code: ${response.statusCode}, body: ${botiumUtils.shortenJsonString(body)}, headers: ${botiumUtils.shortenJsonString(response.headers)}`)
|
|
801
808
|
this._storeCookiesFromResponse(response)
|
|
802
809
|
try {
|
|
803
810
|
body = await this._parseResponseBody(body)
|
|
@@ -806,7 +813,7 @@ module.exports = class SimpleRestContainer {
|
|
|
806
813
|
return
|
|
807
814
|
}
|
|
808
815
|
if (body) {
|
|
809
|
-
setTimeout(() => this._processBodyAsync(body, true, !!this.caps[Capabilities.SIMPLEREST_POLL_UPDATE_CONTEXT]), 0)
|
|
816
|
+
setTimeout(() => this._processBodyAsync(body, response.headers, true, !!this.caps[Capabilities.SIMPLEREST_POLL_UPDATE_CONTEXT]), 0)
|
|
810
817
|
} else {
|
|
811
818
|
debug('_runPolling: ignoring response body (no string and no JSON object)')
|
|
812
819
|
}
|
|
@@ -29,7 +29,8 @@ const setupEndpoints = ({ app, endpoint, middleware, processEvent }) => {
|
|
|
29
29
|
processEvent({
|
|
30
30
|
originalUrl: req.originalUrl,
|
|
31
31
|
originalMethod: req.method,
|
|
32
|
-
body: req.body
|
|
32
|
+
body: req.body,
|
|
33
|
+
headers: req.headers
|
|
33
34
|
})
|
|
34
35
|
res.status(200).json({}).end()
|
|
35
36
|
} else {
|
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
const util = require('util')
|
|
2
2
|
const _ = require('lodash')
|
|
3
|
+
const debug = require('debug')('botium-core-RetryHelper')
|
|
3
4
|
|
|
4
5
|
module.exports = class RetryHelper {
|
|
5
|
-
constructor (caps, section) {
|
|
6
|
-
this.retrySettings = {
|
|
7
|
-
retries: caps[`RETRY_${section.toUpperCase()}_NUMRETRIES`] || 1,
|
|
8
|
-
factor: caps[`RETRY_${section.toUpperCase()}_FACTOR`] || 1,
|
|
9
|
-
minTimeout: caps[`RETRY_${section.toUpperCase()}_MINTIMEOUT`] || 1000
|
|
10
|
-
}
|
|
6
|
+
constructor (caps, section, options = {}) {
|
|
11
7
|
this.retryErrorPatterns = []
|
|
12
8
|
const onErrorRegexp = caps[`RETRY_${section.toUpperCase()}_ONERROR_REGEXP`] || []
|
|
13
9
|
if (onErrorRegexp) {
|
|
@@ -22,10 +18,20 @@ module.exports = class RetryHelper {
|
|
|
22
18
|
this.retryErrorPatterns.push(onErrorRegexp)
|
|
23
19
|
}
|
|
24
20
|
}
|
|
21
|
+
|
|
22
|
+
// to turn on retries, NUMRETRIES or ONERROR_REGEXP has to be set
|
|
23
|
+
this.retrySettings = {
|
|
24
|
+
retries: caps[`RETRY_${section.toUpperCase()}_NUMRETRIES`] || (!_.isNil(options.numRetries) ? options.numRetries : (this.retryErrorPatterns.length === 0) ? 0 : 1),
|
|
25
|
+
factor: caps[`RETRY_${section.toUpperCase()}_FACTOR`] || (_.isNil(options.factor) ? 1 : options.factor),
|
|
26
|
+
minTimeout: caps[`RETRY_${section.toUpperCase()}_MINTIMEOUT`] || (_.isNil(options.minTimeout) ? 1000 : options.minTimeout)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
debug(`Retry for ${section} is ${this.retrySettings.retries > 0 ? 'enabled' : 'disabled'}. Settings: ${JSON.stringify(this.retrySettings)} Patterns: ${JSON.stringify(this.retryErrorPatterns.map(r => r.toString()))}`)
|
|
25
30
|
}
|
|
26
31
|
|
|
27
32
|
shouldRetry (err) {
|
|
28
|
-
if (!err
|
|
33
|
+
if (!err) return false
|
|
34
|
+
if (this.retryErrorPatterns.length === 0) return true
|
|
29
35
|
const errString = util.inspect(err)
|
|
30
36
|
for (const re of this.retryErrorPatterns) {
|
|
31
37
|
if (errString.match(re)) return true
|
package/src/scripting/Convo.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const util = require('util')
|
|
2
2
|
const _ = require('lodash')
|
|
3
3
|
const debug = require('debug')('botium-core-Convo')
|
|
4
|
+
const promiseRetry = require('promise-retry')
|
|
4
5
|
|
|
5
6
|
const BotiumMockMessage = require('../mocks/BotiumMockMessage')
|
|
6
7
|
const Capabilities = require('../Capabilities')
|
|
@@ -8,6 +9,7 @@ const Events = require('../Events')
|
|
|
8
9
|
const ScriptingMemory = require('./ScriptingMemory')
|
|
9
10
|
const { BotiumError, botiumErrorFromErr, botiumErrorFromList } = require('./BotiumError')
|
|
10
11
|
const { normalizeText, toString, removeBuffers, splitStringInNonEmptyLines } = require('./helper')
|
|
12
|
+
const RetryHelper = require('../helpers/RetryHelper')
|
|
11
13
|
|
|
12
14
|
const { LOGIC_HOOK_INCLUDE } = require('./logichook/LogicHookConsts')
|
|
13
15
|
|
|
@@ -89,9 +91,9 @@ class ConvoStep {
|
|
|
89
91
|
'#' + this.sender +
|
|
90
92
|
' - ' + (this.optional ? '?' : '') + (this.not ? '!' : '') +
|
|
91
93
|
(this.messageText || '') +
|
|
92
|
-
(this.asserters && this.asserters.length > 0 ? ' ' + this.asserters.map(a => a.toString()).join('
|
|
93
|
-
(this.logicHooks && this.logicHooks.length > 0 ? ' ' + this.logicHooks.map(l => l.toString()).join('
|
|
94
|
-
(this.userInputs && this.userInputs.length > 0 ? ' ' + this.userInputs.map(u => u.toString()).join('
|
|
94
|
+
(this.asserters && this.asserters.length > 0 ? ' ' + this.asserters.map(a => a.toString()).join(' ') : '') +
|
|
95
|
+
(this.logicHooks && this.logicHooks.length > 0 ? ' ' + this.logicHooks.map(l => l.toString()).join(' ') : '') +
|
|
96
|
+
(this.userInputs && this.userInputs.length > 0 ? ' ' + this.userInputs.map(u => u.toString()).join(' ') : '')
|
|
95
97
|
}
|
|
96
98
|
}
|
|
97
99
|
|
|
@@ -117,7 +119,7 @@ class Transcript {
|
|
|
117
119
|
}
|
|
118
120
|
}
|
|
119
121
|
|
|
120
|
-
class TranscriptAttachment {
|
|
122
|
+
class TranscriptAttachment {
|
|
121
123
|
constructor (fromJson = {}) {
|
|
122
124
|
this.name = fromJson.name
|
|
123
125
|
this.mimeType = fromJson.mimeType
|
|
@@ -210,6 +212,24 @@ class Convo {
|
|
|
210
212
|
}
|
|
211
213
|
|
|
212
214
|
async Run (container) {
|
|
215
|
+
const retryHelper = new RetryHelper(container.caps, 'CONVO')
|
|
216
|
+
return promiseRetry(async (retry, number) => {
|
|
217
|
+
return this.RunImpl(container).catch(err => {
|
|
218
|
+
const retryRemaining = retryHelper.retrySettings.retries - number + 1
|
|
219
|
+
if (retryHelper.shouldRetry(err)) {
|
|
220
|
+
debug(`Convo failed with error "${err.message || JSON.stringify(err)}". Retry ${retryRemaining > 0 ? 'enabled' : 'disabled'} (remaining #${retryRemaining}/${retryHelper.retrySettings.retries}, criterion matches)`)
|
|
221
|
+
retry(err)
|
|
222
|
+
} else {
|
|
223
|
+
if (retryHelper.retryErrorPatterns.length > 0) {
|
|
224
|
+
debug(`Convo failed with error "${err.message || JSON.stringify(err)}". Retry 'disabled' (remaining (#${retryRemaining}/${retryHelper.retrySettings.retries}), criterion does not match)`)
|
|
225
|
+
}
|
|
226
|
+
throw err
|
|
227
|
+
}
|
|
228
|
+
})
|
|
229
|
+
}, retryHelper.retrySettings)
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
async RunImpl (container) {
|
|
213
233
|
const transcript = new Transcript({
|
|
214
234
|
steps: [],
|
|
215
235
|
attachments: [],
|
|
@@ -694,9 +714,15 @@ class Convo {
|
|
|
694
714
|
}
|
|
695
715
|
}
|
|
696
716
|
|
|
697
|
-
module
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
717
|
+
module.exports = {
|
|
718
|
+
Convo,
|
|
719
|
+
ConvoHeader,
|
|
720
|
+
ConvoStep,
|
|
721
|
+
ConvoStepAssert,
|
|
722
|
+
ConvoStepLogicHook,
|
|
723
|
+
ConvoStepUserInput,
|
|
724
|
+
Transcript,
|
|
725
|
+
TranscriptAttachment,
|
|
726
|
+
TranscriptStep,
|
|
727
|
+
TranscriptError
|
|
728
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const _ = require('lodash')
|
|
2
|
+
const speechScorer = require('word-error-rate')
|
|
2
3
|
|
|
3
4
|
const { toString, quoteRegexpString } = require('./helper')
|
|
4
5
|
|
|
@@ -79,6 +80,12 @@ const equals = (ignoreCase) => (botresponse, utterance) => {
|
|
|
79
80
|
return botresponse === utterance
|
|
80
81
|
}
|
|
81
82
|
|
|
83
|
+
const wer = () => (botresponse, utterance, args) => {
|
|
84
|
+
botresponse = _normalize(botresponse || '')
|
|
85
|
+
utterance = toString(utterance || '')
|
|
86
|
+
return speechScorer.wordErrorRate(botresponse, utterance) <= args[0]
|
|
87
|
+
}
|
|
88
|
+
|
|
82
89
|
const getMatchFunction = (matchingMode) => {
|
|
83
90
|
if (matchingMode === 'regexp' || matchingMode === 'regexpIgnoreCase') {
|
|
84
91
|
return regexp(matchingMode === 'regexpIgnoreCase')
|
|
@@ -90,6 +97,8 @@ const getMatchFunction = (matchingMode) => {
|
|
|
90
97
|
return include(matchingMode === 'includeIgnoreCase' || matchingMode === 'includeLowerCase')
|
|
91
98
|
} else if (matchingMode === 'equals' || matchingMode === 'equalsIgnoreCase') {
|
|
92
99
|
return equals(matchingMode === 'equalsIgnoreCase')
|
|
100
|
+
} else if (matchingMode === 'wer') {
|
|
101
|
+
return wer()
|
|
93
102
|
} else {
|
|
94
103
|
return equals(false)
|
|
95
104
|
}
|
|
@@ -101,5 +110,6 @@ module.exports = {
|
|
|
101
110
|
wildcardExact,
|
|
102
111
|
include,
|
|
103
112
|
equals,
|
|
113
|
+
wer,
|
|
104
114
|
getMatchFunction
|
|
105
115
|
}
|
|
@@ -134,7 +134,8 @@ module.exports = class ScriptingProvider {
|
|
|
134
134
|
tomatch = [tomatch]
|
|
135
135
|
}
|
|
136
136
|
debug(`assertBotResponse ${stepTag} ${meMsg ? `(${meMsg}) ` : ''}BOT: ${botresponse} = ${tomatch} ...`)
|
|
137
|
-
const found = _.find(tomatch, (utt) => this.matchFn(botresponse, utt))
|
|
137
|
+
const found = _.find(tomatch, (utt) => this.matchFn(botresponse, utt, this.caps[Capabilities.SCRIPTING_MATCHING_MODE_ARGS]))
|
|
138
|
+
const asserterType = this.caps[Capabilities.SCRIPTING_MATCHING_MODE] === 'wer' ? 'WerAsserter' : 'TextMatchAsserter'
|
|
138
139
|
if (_.isNil(found)) {
|
|
139
140
|
let message = `${stepTag}: Bot response `
|
|
140
141
|
message += meMsg ? `(on ${meMsg}) ` : ''
|
|
@@ -146,7 +147,7 @@ module.exports = class ScriptingProvider {
|
|
|
146
147
|
message,
|
|
147
148
|
{
|
|
148
149
|
type: 'asserter',
|
|
149
|
-
source:
|
|
150
|
+
source: asserterType,
|
|
150
151
|
context: {
|
|
151
152
|
stepTag
|
|
152
153
|
},
|
|
@@ -164,7 +165,8 @@ module.exports = class ScriptingProvider {
|
|
|
164
165
|
nottomatch = [nottomatch]
|
|
165
166
|
}
|
|
166
167
|
debug(`assertBotNotResponse ${stepTag} ${meMsg ? `(${meMsg}) ` : ''}BOT: ${botresponse} != ${nottomatch} ...`)
|
|
167
|
-
const found = _.find(nottomatch, (utt) => this.matchFn(botresponse, utt))
|
|
168
|
+
const found = _.find(nottomatch, (utt) => this.matchFn(botresponse, utt, this.caps[Capabilities.SCRIPTING_MATCHING_MODE_ARGS]))
|
|
169
|
+
const asserterType = this.caps[Capabilities.SCRIPTING_MATCHING_MODE] === 'wer' ? 'WerAsserter' : 'TextMatchAsserter'
|
|
168
170
|
if (!_.isNil(found)) {
|
|
169
171
|
let message = `${stepTag}: Bot response `
|
|
170
172
|
message += meMsg ? `(on ${meMsg}) ` : ''
|
|
@@ -176,7 +178,7 @@ module.exports = class ScriptingProvider {
|
|
|
176
178
|
message,
|
|
177
179
|
{
|
|
178
180
|
type: 'asserter',
|
|
179
|
-
source:
|
|
181
|
+
source: asserterType,
|
|
180
182
|
context: {
|
|
181
183
|
stepTag
|
|
182
184
|
},
|
|
@@ -401,7 +403,7 @@ module.exports = class ScriptingProvider {
|
|
|
401
403
|
}
|
|
402
404
|
|
|
403
405
|
Match (botresponse, utterance) {
|
|
404
|
-
return this.matchFn(botresponse, utterance)
|
|
406
|
+
return this.matchFn(botresponse, utterance, this.caps[Capabilities.SCRIPTING_MATCHING_MODE_ARGS])
|
|
405
407
|
}
|
|
406
408
|
|
|
407
409
|
Compile (scriptBuffer, scriptFormat, scriptType) {
|
|
@@ -859,9 +861,10 @@ module.exports = class ScriptingProvider {
|
|
|
859
861
|
* @param currentConvo
|
|
860
862
|
* @param convoStepIndex
|
|
861
863
|
* @param convoStepsStack list of ConvoSteps
|
|
864
|
+
* @param context {width: }
|
|
862
865
|
* @private
|
|
863
866
|
*/
|
|
864
|
-
_expandConvo (expandedConvos, currentConvo, convoStepIndex = 0, convoStepsStack = []) {
|
|
867
|
+
_expandConvo (expandedConvos, currentConvo, convoStepIndex = 0, convoStepsStack = [], context = {}) {
|
|
865
868
|
const utterancePostfix = (lineTag, uttOrUserInput) => {
|
|
866
869
|
const naming = this.caps[Capabilities.SCRIPTING_UTTEXPANSION_NAMING_MODE] || Defaults.capabilities[Capabilities.SCRIPTING_UTTEXPANSION_NAMING_MODE]
|
|
867
870
|
if (naming === 'justLineTag') {
|
|
@@ -881,7 +884,7 @@ module.exports = class ScriptingProvider {
|
|
|
881
884
|
if (currentStep.sender === 'bot' || currentStep.sender === 'begin' || currentStep.sender === 'end') {
|
|
882
885
|
const currentStepsStack = convoStepsStack.slice()
|
|
883
886
|
currentStepsStack.push(_.cloneDeep(currentStep))
|
|
884
|
-
this._expandConvo(expandedConvos, currentConvo, convoStepIndex + 1, currentStepsStack)
|
|
887
|
+
this._expandConvo(expandedConvos, currentConvo, convoStepIndex + 1, currentStepsStack, context)
|
|
885
888
|
} else if (currentStep.sender === 'me') {
|
|
886
889
|
let useUnexpanded = true
|
|
887
890
|
if (currentStep.messageText) {
|
|
@@ -898,29 +901,61 @@ module.exports = class ScriptingProvider {
|
|
|
898
901
|
}
|
|
899
902
|
if (this.utterances[uttName]) {
|
|
900
903
|
const allutterances = this.utterances[uttName].utterances
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
sampleutterances = allutterances
|
|
906
|
-
.map(x => ({ x, r: Math.random() }))
|
|
907
|
-
.sort((a, b) => a.r - b.r)
|
|
908
|
-
.map(a => a.x)
|
|
909
|
-
.slice(0, this.caps[Capabilities.SCRIPTING_UTTEXPANSION_RANDOM_COUNT])
|
|
904
|
+
const processSampleUtterances = (sampleutterances, myContext) => {
|
|
905
|
+
sampleutterances.forEach((utt, index) => {
|
|
906
|
+
processSampleUtterance(utt, sampleutterances.length, index, Object.assign({ indexExpansionModeIndex: index }, myContext || context))
|
|
907
|
+
})
|
|
910
908
|
}
|
|
911
|
-
|
|
912
|
-
const lineTag = `${index + 1}`.padStart(`${
|
|
909
|
+
const processSampleUtterance = (sampleutterance, length, index, myContext) => {
|
|
910
|
+
const lineTag = `${index + 1}`.padStart(`${length}`.length, '0')
|
|
913
911
|
const currentStepsStack = convoStepsStack.slice()
|
|
914
912
|
if (uttArgs) {
|
|
915
|
-
|
|
913
|
+
sampleutterance = util.format(sampleutterance, ...uttArgs)
|
|
916
914
|
}
|
|
917
|
-
currentStepsStack.push(Object.assign(_.cloneDeep(currentStep), { messageText:
|
|
915
|
+
currentStepsStack.push(Object.assign(_.cloneDeep(currentStep), { messageText: sampleutterance }))
|
|
918
916
|
const currentConvoLabeled = _.cloneDeep(currentConvo)
|
|
919
|
-
Object.assign(currentConvoLabeled.header, { name: `${currentConvo.header.name}/${uttName}-${utterancePostfix(lineTag,
|
|
917
|
+
Object.assign(currentConvoLabeled.header, { name: `${currentConvo.header.name}/${uttName}-${utterancePostfix(lineTag, sampleutterance)}` })
|
|
920
918
|
if (!currentConvoLabeled.sourceTag) currentConvoLabeled.sourceTag = {}
|
|
921
919
|
if (!currentConvoLabeled.sourceTag.origConvoName) currentConvoLabeled.sourceTag.origConvoName = currentConvo.header.name
|
|
922
|
-
this._expandConvo(expandedConvos, currentConvoLabeled, convoStepIndex + 1, currentStepsStack)
|
|
923
|
-
}
|
|
920
|
+
this._expandConvo(expandedConvos, currentConvoLabeled, convoStepIndex + 1, currentStepsStack, myContext || context)
|
|
921
|
+
}
|
|
922
|
+
if (this.caps[Capabilities.SCRIPTING_UTTEXPANSION_MODE] === 'index') {
|
|
923
|
+
if (_.isNil(context.indexExpansionModeWidth)) {
|
|
924
|
+
// executed for the first found utterance
|
|
925
|
+
processSampleUtterances(allutterances, Object.assign({}, context, { indexExpansionModeWidth: allutterances.length }))
|
|
926
|
+
} else {
|
|
927
|
+
if (_.isNil(context.indexExpansionModeIndex)) {
|
|
928
|
+
throw new Error('indexExpansionModeIndex must be set!')
|
|
929
|
+
}
|
|
930
|
+
// executing the current 'thread', if current utterance has no example to current index, fallback to the last one
|
|
931
|
+
const localIndex = Math.min(context.indexExpansionModeIndex, allutterances.length - 1)
|
|
932
|
+
if (localIndex < context.indexExpansionModeIndex && context.indexExpansionModeIndex === context.indexExpansionModeWidth - 1) {
|
|
933
|
+
debug(`While expanding convos by index found in utterance "${uttName}" less examples (${allutterances.length}) as expected (${context.indexExpansionModeWidth})`)
|
|
934
|
+
}
|
|
935
|
+
const myContext = Object.assign({}, context, { indexExpansionModeWidth: Math.max(allutterances.length, context.indexExpansionModeWidth) })
|
|
936
|
+
processSampleUtterance(allutterances[localIndex], allutterances.length, localIndex, myContext)
|
|
937
|
+
if (allutterances.length > context.indexExpansionModeWidth && context.indexExpansionModeIndex + 1 === context.indexExpansionModeWidth) {
|
|
938
|
+
debug(`While expanding convos by index found in utterance "${uttName}" more examples (${allutterances.length}) as expected (${context.indexExpansionModeWidth})`)
|
|
939
|
+
for (let i = context.indexExpansionModeWidth; i < allutterances.length; i++) {
|
|
940
|
+
// if we found a utterance with more examples as any utterances before, we have to start new 'thread'
|
|
941
|
+
const myContext = Object.assign({}, context, { indexExpansionModeWidth: allutterances.length, indexExpansionModeIndex: i })
|
|
942
|
+
processSampleUtterance(allutterances[i], allutterances.length, i, myContext)
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
} else {
|
|
947
|
+
if (this.caps[Capabilities.SCRIPTING_UTTEXPANSION_MODE] === 'first') {
|
|
948
|
+
processSampleUtterances([allutterances[0]])
|
|
949
|
+
} else if (this.caps[Capabilities.SCRIPTING_UTTEXPANSION_MODE] === 'random') {
|
|
950
|
+
processSampleUtterances(allutterances
|
|
951
|
+
.map(x => ({ x, r: Math.random() }))
|
|
952
|
+
.sort((a, b) => a.r - b.r)
|
|
953
|
+
.map(a => a.x)
|
|
954
|
+
.slice(0, this.caps[Capabilities.SCRIPTING_UTTEXPANSION_RANDOM_COUNT]))
|
|
955
|
+
} else {
|
|
956
|
+
processSampleUtterances(allutterances)
|
|
957
|
+
}
|
|
958
|
+
}
|
|
924
959
|
useUnexpanded = false
|
|
925
960
|
}
|
|
926
961
|
}
|
|
@@ -930,18 +965,14 @@ module.exports = class ScriptingProvider {
|
|
|
930
965
|
if (userInput && userInput.expandConvo) {
|
|
931
966
|
const expandedUserInputs = userInput.expandConvo({ convo: currentConvo, convoStep: currentStep, args: ui.args })
|
|
932
967
|
if (expandedUserInputs && expandedUserInputs.length > 0) {
|
|
933
|
-
let sampleinputs = expandedUserInputs
|
|
934
|
-
|
|
935
|
-
sampleinputs
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
.map(x => ({ x, r: Math.random() }))
|
|
939
|
-
.sort((a, b) => a.r - b.r)
|
|
940
|
-
.map(a => a.x)
|
|
941
|
-
.slice(0, this.caps[Capabilities.SCRIPTING_UTTEXPANSION_RANDOM_COUNT])
|
|
968
|
+
// let sampleinputs = expandedUserInputs
|
|
969
|
+
const processSampleInputs = (sampleinputs, myContext, uiIndex) => {
|
|
970
|
+
sampleinputs.forEach((input, index) => {
|
|
971
|
+
processSampleInput(input, sampleinputs.length, index, Object.assign({ indexExpansionModeIndex: index }, myContext || context), uiIndex)
|
|
972
|
+
})
|
|
942
973
|
}
|
|
943
|
-
|
|
944
|
-
const lineTag = `${index + 1}`.padStart(`${
|
|
974
|
+
const processSampleInput = (sampleinput, length, index, myContext, uiIndex) => {
|
|
975
|
+
const lineTag = `${index + 1}`.padStart(`${length}`.length, '0')
|
|
945
976
|
const currentStepsStack = convoStepsStack.slice()
|
|
946
977
|
const currentStepMod = _.cloneDeep(currentStep)
|
|
947
978
|
currentStepMod.userInputs[uiIndex] = sampleinput
|
|
@@ -949,8 +980,46 @@ module.exports = class ScriptingProvider {
|
|
|
949
980
|
currentStepsStack.push(currentStepMod)
|
|
950
981
|
const currentConvoLabeled = _.cloneDeep(currentConvo)
|
|
951
982
|
Object.assign(currentConvoLabeled.header, { name: `${currentConvo.header.name}/${ui.name}-${utterancePostfix(lineTag, (sampleinput.args && sampleinput.args.length) ? sampleinput.args.join(', ') : 'no-args')}` })
|
|
952
|
-
this._expandConvo(expandedConvos, currentConvoLabeled, convoStepIndex + 1, currentStepsStack)
|
|
953
|
-
}
|
|
983
|
+
this._expandConvo(expandedConvos, currentConvoLabeled, convoStepIndex + 1, currentStepsStack, myContext || context)
|
|
984
|
+
}
|
|
985
|
+
if (this.caps[Capabilities.SCRIPTING_UTTEXPANSION_MODE] === 'index') {
|
|
986
|
+
if (_.isNil(context.indexExpansionModeWidth)) {
|
|
987
|
+
processSampleInputs(expandedUserInputs, Object.assign({}, context, { indexExpansionModeWidth: expandedUserInputs.length }), uiIndex)
|
|
988
|
+
} else {
|
|
989
|
+
if (_.isNil(context.indexExpansionModeIndex)) {
|
|
990
|
+
throw new Error('indexExpansionModeIndex must be set!')
|
|
991
|
+
}
|
|
992
|
+
// executing the current 'thread', if current utterance has no example to current index, fallback to the last one
|
|
993
|
+
const localIndex = Math.min(context.indexExpansionModeIndex, expandedUserInputs.length - 1)
|
|
994
|
+
if (localIndex < context.indexExpansionModeIndex && context.indexExpansionModeIndex === context.indexExpansionModeWidth - 1) {
|
|
995
|
+
debug(`While expanding convos by index found user input "${ui.name}, ${ui.args}" less examples (${expandedUserInputs.length}) as expected (${context.indexExpansionModeWidth})`)
|
|
996
|
+
}
|
|
997
|
+
const myContext = Object.assign({}, context, { indexExpansionModeWidth: Math.max(expandedUserInputs.length, context.indexExpansionModeWidth) })
|
|
998
|
+
processSampleInput(expandedUserInputs[localIndex], expandedUserInputs.length, localIndex, myContext, uiIndex)
|
|
999
|
+
if (expandedUserInputs.length > context.indexExpansionModeWidth && context.indexExpansionModeIndex + 1 === context.indexExpansionModeWidth) {
|
|
1000
|
+
debug(`While expanding convos by index found user input "${ui.name}, ${ui.args}" more examples (${expandedUserInputs.length}) as expected (${context.indexExpansionModeWidth})`)
|
|
1001
|
+
for (let i = context.indexExpansionModeWidth; i < expandedUserInputs.length; i++) {
|
|
1002
|
+
const myContext = Object.assign({}, context, { indexExpansionModeWidth: expandedUserInputs.length, indexExpansionModeIndex: i })
|
|
1003
|
+
processSampleInput(expandedUserInputs[i], expandedUserInputs.length, i, myContext, uiIndex)
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
} else {
|
|
1008
|
+
if (this.caps[Capabilities.SCRIPTING_UTTEXPANSION_MODE] === 'first') {
|
|
1009
|
+
processSampleInputs([expandedUserInputs[0]], context, uiIndex)
|
|
1010
|
+
} else if (this.caps[Capabilities.SCRIPTING_UTTEXPANSION_MODE] === 'random') {
|
|
1011
|
+
processSampleInputs(expandedUserInputs
|
|
1012
|
+
.map(x => ({
|
|
1013
|
+
x,
|
|
1014
|
+
r: Math.random()
|
|
1015
|
+
}))
|
|
1016
|
+
.sort((a, b) => a.r - b.r)
|
|
1017
|
+
.map(a => a.x)
|
|
1018
|
+
.slice(0, this.caps[Capabilities.SCRIPTING_UTTEXPANSION_RANDOM_COUNT]), context, uiIndex)
|
|
1019
|
+
} else {
|
|
1020
|
+
processSampleInputs(expandedUserInputs, context, uiIndex)
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
954
1023
|
useUnexpanded = false
|
|
955
1024
|
}
|
|
956
1025
|
}
|
|
@@ -959,7 +1028,7 @@ module.exports = class ScriptingProvider {
|
|
|
959
1028
|
if (useUnexpanded) {
|
|
960
1029
|
const currentStepsStack = convoStepsStack.slice()
|
|
961
1030
|
currentStepsStack.push(_.cloneDeep(currentStep))
|
|
962
|
-
this._expandConvo(expandedConvos, currentConvo, convoStepIndex + 1, currentStepsStack)
|
|
1031
|
+
this._expandConvo(expandedConvos, currentConvo, convoStepIndex + 1, currentStepsStack, context)
|
|
963
1032
|
}
|
|
964
1033
|
}
|
|
965
1034
|
} else {
|
|
@@ -45,7 +45,7 @@ module.exports = {
|
|
|
45
45
|
{ name: 'TEXT_EQUALS_IC', className: 'TextEqualsAnyICAsserter' },
|
|
46
46
|
{ name: 'TEXT', className: 'TextEqualsAnyAsserter' },
|
|
47
47
|
{ name: 'TEXT_IC', className: 'TextEqualsAnyICAsserter' },
|
|
48
|
-
|
|
48
|
+
{ name: 'TEXT_WER', className: 'WerAsserter' },
|
|
49
49
|
{ name: 'BOT_CONSUMED', className: 'BotRepliesConsumedAsserter' },
|
|
50
50
|
{ name: 'BOT_UNCONSUMED_COUNT', className: 'BotRepliesUnconsumedCountAsserter' }
|
|
51
51
|
],
|
|
@@ -192,7 +192,7 @@ module.exports = class LogicHookUtils {
|
|
|
192
192
|
})
|
|
193
193
|
return vm.run(script)
|
|
194
194
|
} catch (err) {
|
|
195
|
-
throw new Error(
|
|
195
|
+
throw new Error(`Script ${key} is not valid - ${err.message || err}`)
|
|
196
196
|
}
|
|
197
197
|
} else {
|
|
198
198
|
throw new Error(`Script "${key}" is not valid - only functions and javascript code accepted`)
|