botium-core 1.13.8 → 1.13.10
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/dist/botium-cjs.js +214 -1481
- package/dist/botium-cjs.js.map +1 -1
- package/dist/botium-es.js +214 -1481
- package/dist/botium-es.js.map +1 -1
- package/package.json +24 -24
- package/src/scripting/Convo.js +42 -15
- package/src/scripting/MatchFunctions.js +3 -4
- package/src/scripting/ScriptingProvider.js +11 -8
- package/src/scripting/helper.js +61 -2
- package/src/scripting/logichook/asserter/JsonPathAsserter.js +1 -1
- package/src/scripting/logichook/asserter/WerAsserter.js +3 -2
- package/src/scripting/logichook/logichooks/SetScriptingMemoryLogicHook.js +1 -1
- package/test/convo/convos/applyscriptingmemoryinbegin.convo.txt +14 -0
- package/test/convo/fillAndApplyScriptingMemory.spec.js +20 -0
- package/test/scripting/asserters/convos/wer_threshold_wildcard_nok_float.yml +7 -0
- package/test/scripting/asserters/convos/wer_threshold_wildcard_nok_percentage.yml +7 -0
- package/test/scripting/asserters/convos/wer_threshold_wildcard_ok_float.yml +7 -0
- package/test/scripting/asserters/convos/wer_threshold_wildcard_ok_percentage.yml +7 -0
- package/test/scripting/asserters/jsonpathAsserter.spec.js +11 -0
- package/test/scripting/asserters/werAsserter.spec.js +41 -0
- package/test/scripting/matching/matchingmode.spec.js +82 -0
- package/test/scripting/scriptingModificator.spec.js +2 -0
- package/test/scripting/scriptingmemory/convosPartial/buy.convo.txt +7 -0
- package/test/scripting/scriptingmemory/convosPartial/product.scriptingmemory.txt +2 -0
- package/test/scripting/scriptingmemory/convosPartial/sub1.pconvo.txt +7 -0
- package/test/scripting/scriptingmemory/convosPartial/sub2.pconvo.txt +7 -0
- package/test/scripting/scriptingmemory/fillScriptingMemoryFromFile.spec.js +10 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "botium-core",
|
|
3
|
-
"version": "1.13.
|
|
3
|
+
"version": "1.13.10",
|
|
4
4
|
"description": "The Selenium for Chatbots",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"module": "dist/botium-es.js",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"test": "cross-env NODE_PATH=\"./test/plugins/plugindir/fromfolder:./test/plugins/plugindir/fromfile:./test/security/resources\" mocha \"./test/**/*.spec.js\"",
|
|
20
20
|
"coverage:report": "nyc report --reporter=lcov npm test",
|
|
21
21
|
"license-checker": "license-checker > LICENSES-3RDPARTY.txt",
|
|
22
|
-
"update-dependencies": "npm-check-updates --reject globby -u --timeout 120000"
|
|
22
|
+
"update-dependencies": "npm-check-updates --reject globby,rollup -u --timeout 120000"
|
|
23
23
|
},
|
|
24
24
|
"repository": {
|
|
25
25
|
"type": "git",
|
|
@@ -32,17 +32,17 @@
|
|
|
32
32
|
},
|
|
33
33
|
"homepage": "https://www.botium.ai",
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@babel/runtime": "^7.
|
|
35
|
+
"@babel/runtime": "^7.20.6",
|
|
36
36
|
"async": "^3.2.4",
|
|
37
|
-
"body-parser": "^1.20.
|
|
37
|
+
"body-parser": "^1.20.1",
|
|
38
38
|
"boolean": "^3.2.0",
|
|
39
39
|
"bottleneck": "^2.19.5",
|
|
40
|
-
"csv-parse": "^5.3.
|
|
40
|
+
"csv-parse": "^5.3.3",
|
|
41
41
|
"debug": "^4.3.4",
|
|
42
42
|
"esprima": "^4.0.1",
|
|
43
|
-
"express": "^4.18.
|
|
43
|
+
"express": "^4.18.2",
|
|
44
44
|
"globby": "11.0.4",
|
|
45
|
-
"ioredis": "^5.2.
|
|
45
|
+
"ioredis": "^5.2.4",
|
|
46
46
|
"is-class": "^0.0.9",
|
|
47
47
|
"is-json": "^2.0.1",
|
|
48
48
|
"jsonpath": "^1.1.1",
|
|
@@ -53,47 +53,47 @@
|
|
|
53
53
|
"moment": "^2.29.4",
|
|
54
54
|
"mustache": "^4.2.0",
|
|
55
55
|
"promise-retry": "^2.0.1",
|
|
56
|
-
"promise.allsettled": "^1.0.
|
|
56
|
+
"promise.allsettled": "^1.0.6",
|
|
57
57
|
"randomatic": "^3.1.1",
|
|
58
58
|
"request": "^2.88.2",
|
|
59
59
|
"rimraf": "^3.0.2",
|
|
60
60
|
"sanitize-filename": "^1.6.3",
|
|
61
61
|
"slugify": "^1.6.5",
|
|
62
|
-
"socket.io": "^4.5.
|
|
63
|
-
"socket.io-client": "^4.5.
|
|
62
|
+
"socket.io": "^4.5.4",
|
|
63
|
+
"socket.io-client": "^4.5.4",
|
|
64
64
|
"socketio-auth": "^0.1.1",
|
|
65
65
|
"swagger-jsdoc": "^6.2.5",
|
|
66
|
-
"swagger-ui-express": "^4.
|
|
66
|
+
"swagger-ui-express": "^4.6.0",
|
|
67
67
|
"uuid": "^9.0.0",
|
|
68
|
-
"vm2": "^3.9.
|
|
68
|
+
"vm2": "^3.9.13",
|
|
69
69
|
"word-error-rate": "0.0.7",
|
|
70
70
|
"write-yaml": "^1.0.0",
|
|
71
71
|
"xlsx": "^0.18.5",
|
|
72
72
|
"xregexp": "^5.1.1",
|
|
73
|
-
"yaml": "^2.1.
|
|
73
|
+
"yaml": "^2.1.3"
|
|
74
74
|
},
|
|
75
75
|
"devDependencies": {
|
|
76
|
-
"@babel/core": "^7.
|
|
77
|
-
"@babel/node": "^7.
|
|
78
|
-
"@babel/plugin-transform-runtime": "^7.19.
|
|
79
|
-
"@babel/preset-env": "^7.
|
|
80
|
-
"chai": "^4.3.
|
|
76
|
+
"@babel/core": "^7.20.5",
|
|
77
|
+
"@babel/node": "^7.20.5",
|
|
78
|
+
"@babel/plugin-transform-runtime": "^7.19.6",
|
|
79
|
+
"@babel/preset-env": "^7.20.2",
|
|
80
|
+
"chai": "^4.3.7",
|
|
81
81
|
"chai-as-promised": "^7.1.1",
|
|
82
82
|
"cross-env": "^7.0.3",
|
|
83
|
-
"eslint": "^8.
|
|
83
|
+
"eslint": "^8.29.0",
|
|
84
84
|
"eslint-config-standard": "^17.0.0",
|
|
85
85
|
"eslint-plugin-import": "^2.26.0",
|
|
86
86
|
"eslint-plugin-mocha": "^10.1.0",
|
|
87
|
-
"eslint-plugin-n": "^15.
|
|
88
|
-
"eslint-plugin-promise": "^6.
|
|
87
|
+
"eslint-plugin-n": "^15.6.0",
|
|
88
|
+
"eslint-plugin-promise": "^6.1.1",
|
|
89
89
|
"eslint-plugin-standard": "^4.1.0",
|
|
90
90
|
"license-checker": "^25.0.1",
|
|
91
91
|
"license-compatibility-checker": "^0.3.5",
|
|
92
|
-
"mocha": "^10.
|
|
92
|
+
"mocha": "^10.2.0",
|
|
93
93
|
"nock": "^13.2.9",
|
|
94
|
-
"npm-check-updates": "^16.
|
|
94
|
+
"npm-check-updates": "^16.5.6",
|
|
95
95
|
"nyc": "^15.1.0",
|
|
96
|
-
"rollup": "
|
|
96
|
+
"rollup": "2.79.1",
|
|
97
97
|
"rollup-plugin-babel": "^4.4.0",
|
|
98
98
|
"rollup-plugin-commonjs": "^10.1.0",
|
|
99
99
|
"rollup-plugin-json": "^4.0.0",
|
package/src/scripting/Convo.js
CHANGED
|
@@ -591,26 +591,53 @@ class Convo {
|
|
|
591
591
|
}
|
|
592
592
|
|
|
593
593
|
GetScriptingMemoryAllVariables (container) {
|
|
594
|
-
const
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
594
|
+
const partialConvos = this.context.GetPartialConvos()
|
|
595
|
+
|
|
596
|
+
const variableNames = this.conversation.reduce((acc, convoStep) => {
|
|
597
|
+
if (convoStep.sender === 'include') {
|
|
598
|
+
if (convoStep.channel) {
|
|
599
|
+
const partialConvo = partialConvos[convoStep.channel]
|
|
600
|
+
if (partialConvo) {
|
|
601
|
+
acc = [...acc, ...partialConvo.GetScriptingMemoryAllVariables(container)]
|
|
602
602
|
}
|
|
603
603
|
}
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
604
|
+
if (convoStep.messageText) {
|
|
605
|
+
for (const partialConvoName of splitStringInNonEmptyLines(convoStep.messageText)) {
|
|
606
|
+
const partialConvo = partialConvos[partialConvoName]
|
|
607
|
+
if (partialConvo) {
|
|
608
|
+
acc = [...acc, ...partialConvo.GetScriptingMemoryAllVariables(container)]
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
} else {
|
|
613
|
+
acc = [...acc, ...this.GetScriptingMemoryVariables(container, convoStep.messageText)]
|
|
609
614
|
|
|
610
|
-
|
|
615
|
+
const __extractFromArgs = (convoStepItems) => {
|
|
616
|
+
let resultInner = []
|
|
617
|
+
for (const item of (convoStepItems || [])) {
|
|
618
|
+
for (const arg of (item.args || [])) {
|
|
619
|
+
resultInner = resultInner.concat(this.GetScriptingMemoryVariables(container, arg))
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
return resultInner
|
|
623
|
+
}
|
|
624
|
+
acc = [...acc, ...__extractFromArgs(convoStep.asserters)]
|
|
625
|
+
acc = [...acc, ...__extractFromArgs(convoStep.logicHooks)]
|
|
626
|
+
acc = [...acc, ...__extractFromArgs(convoStep.userInputs)]
|
|
627
|
+
|
|
628
|
+
convoStep.logicHooks.forEach((logicHook) => {
|
|
629
|
+
if (logicHook.name === LOGIC_HOOK_INCLUDE) {
|
|
630
|
+
const partialConvo = partialConvos[logicHook.args[0]]
|
|
631
|
+
if (partialConvo) {
|
|
632
|
+
acc = [...acc, ...partialConvo.GetScriptingMemoryAllVariables(container)]
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
})
|
|
636
|
+
}
|
|
637
|
+
return acc
|
|
611
638
|
}, [])
|
|
612
639
|
|
|
613
|
-
return
|
|
640
|
+
return _.uniq(variableNames)
|
|
614
641
|
}
|
|
615
642
|
|
|
616
643
|
GetScriptingMemoryVariables (container, utterance) {
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
const _ = require('lodash')
|
|
2
|
-
const speechScorer = require('word-error-rate')
|
|
3
2
|
|
|
4
|
-
const { toString, quoteRegexpString } = require('./helper')
|
|
3
|
+
const { toString, quoteRegexpString, calculateWer } = require('./helper')
|
|
5
4
|
|
|
6
5
|
const _normalize = (botresponse) => {
|
|
7
|
-
if (_.isUndefined(botresponse)) return ''
|
|
6
|
+
if (_.isUndefined(botresponse) || _.isNil(botresponse)) return ''
|
|
8
7
|
if (_.isObject(botresponse) && _.has(botresponse, 'messageText')) {
|
|
9
8
|
return toString(botresponse.messageText) || ''
|
|
10
9
|
}
|
|
@@ -85,7 +84,7 @@ const wer = () => (botresponse, utterance, args) => {
|
|
|
85
84
|
utterance = toString(utterance || '')
|
|
86
85
|
|
|
87
86
|
const threshold = ([',', '.'].find(p => `${args[0]}`.includes(p)) ? parseFloat(args[0]) : parseInt(args[0]) / 100)
|
|
88
|
-
return
|
|
87
|
+
return calculateWer(botresponse, utterance) <= threshold
|
|
89
88
|
}
|
|
90
89
|
|
|
91
90
|
const getMatchFunction = (matchingMode) => {
|
|
@@ -209,7 +209,7 @@ module.exports = class ScriptingProvider {
|
|
|
209
209
|
this.retryHelperUserInput = new RetryHelper(this.caps, 'USERINPUT')
|
|
210
210
|
}
|
|
211
211
|
|
|
212
|
-
_createAsserterPromises ({ asserterType, asserters, convo, convoStep, scriptingMemory, ...rest }) {
|
|
212
|
+
_createAsserterPromises ({ asserterType, asserters, convo, convoStep, scriptingMemory, container, ...rest }) {
|
|
213
213
|
if (!this._isValidAsserterType(asserterType)) {
|
|
214
214
|
throw Error(`Unknown asserterType ${asserterType}`)
|
|
215
215
|
}
|
|
@@ -254,13 +254,14 @@ module.exports = class ScriptingProvider {
|
|
|
254
254
|
convo,
|
|
255
255
|
convoStep,
|
|
256
256
|
scriptingMemory,
|
|
257
|
-
|
|
257
|
+
container,
|
|
258
|
+
args: ScriptingMemory.applyToArgs(a.args, scriptingMemory, container.caps, rest.botMsg),
|
|
258
259
|
isGlobal: false,
|
|
259
260
|
...rest
|
|
260
261
|
}))
|
|
261
262
|
const globalAsserter = Object.values(this.globalAsserter)
|
|
262
263
|
.filter(a => a[asserterType])
|
|
263
|
-
.map(a => p(this.retryHelperAsserter, () => a[asserterType]({ convo, convoStep, scriptingMemory, args: [], isGlobal: true, ...rest })))
|
|
264
|
+
.map(a => p(this.retryHelperAsserter, () => a[asserterType]({ convo, convoStep, scriptingMemory, container, args: [], isGlobal: true, ...rest })))
|
|
264
265
|
|
|
265
266
|
const allPromises = [...convoAsserter, ...globalAsserter]
|
|
266
267
|
if (this.caps[Capabilities.SCRIPTING_ENABLE_MULTIPLE_ASSERT_ERRORS]) {
|
|
@@ -276,7 +277,7 @@ module.exports = class ScriptingProvider {
|
|
|
276
277
|
return Promise.resolve(false)
|
|
277
278
|
}
|
|
278
279
|
|
|
279
|
-
_createLogicHookPromises ({ hookType, logicHooks, convo, convoStep, scriptingMemory, ...rest }) {
|
|
280
|
+
_createLogicHookPromises ({ hookType, logicHooks, convo, convoStep, scriptingMemory, container, ...rest }) {
|
|
280
281
|
if (hookType !== 'onMeStart' && hookType !== 'onMePrepare' && hookType !== 'onMeEnd' && hookType !== 'onBotStart' && hookType !== 'onBotPrepare' && hookType !== 'onBotEnd' &&
|
|
281
282
|
hookType !== 'onConvoBegin' && hookType !== 'onConvoEnd'
|
|
282
283
|
) {
|
|
@@ -289,28 +290,30 @@ module.exports = class ScriptingProvider {
|
|
|
289
290
|
convo,
|
|
290
291
|
convoStep,
|
|
291
292
|
scriptingMemory,
|
|
292
|
-
|
|
293
|
+
container,
|
|
294
|
+
args: ScriptingMemory.applyToArgs(l.args, scriptingMemory, container.caps, rest.botMsg),
|
|
293
295
|
isGlobal: false,
|
|
294
296
|
...rest
|
|
295
297
|
})))
|
|
296
298
|
|
|
297
299
|
const globalPromises = Object.values(this.globalLogicHook)
|
|
298
300
|
.filter(l => l[hookType])
|
|
299
|
-
.map(l => p(this.retryHelperLogicHook, () => l[hookType]({ convo, convoStep, scriptingMemory, args: [], isGlobal: true, ...rest })))
|
|
301
|
+
.map(l => p(this.retryHelperLogicHook, () => l[hookType]({ convo, convoStep, scriptingMemory, container, args: [], isGlobal: true, ...rest })))
|
|
300
302
|
|
|
301
303
|
const allPromises = [...convoStepPromises, ...globalPromises]
|
|
302
304
|
if (allPromises.length > 0) return Promise.all(allPromises).then(() => true)
|
|
303
305
|
return Promise.resolve(false)
|
|
304
306
|
}
|
|
305
307
|
|
|
306
|
-
_createUserInputPromises ({ convo, convoStep, scriptingMemory, ...rest }) {
|
|
308
|
+
_createUserInputPromises ({ convo, convoStep, scriptingMemory, container, ...rest }) {
|
|
307
309
|
const convoStepPromises = (convoStep.userInputs || [])
|
|
308
310
|
.filter(ui => this.userInputs[ui.name])
|
|
309
311
|
.map(ui => p(this.retryHelperUserInput, () => this.userInputs[ui.name].setUserInput({
|
|
310
312
|
convo,
|
|
311
313
|
convoStep,
|
|
312
314
|
scriptingMemory,
|
|
313
|
-
|
|
315
|
+
container,
|
|
316
|
+
args: ScriptingMemory.applyToArgs(ui.args, scriptingMemory, container.caps, rest.meMsg),
|
|
314
317
|
...rest
|
|
315
318
|
})))
|
|
316
319
|
|
package/src/scripting/helper.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const _ = require('lodash')
|
|
2
2
|
const isJSON = require('is-json')
|
|
3
|
+
const speechScorer = require('word-error-rate')
|
|
3
4
|
|
|
4
5
|
const { E_SCRIPTING_MEMORY_COLUMN_MODE } = require('../Enums')
|
|
5
6
|
|
|
@@ -61,7 +62,7 @@ const removeBuffers = obj => {
|
|
|
61
62
|
}
|
|
62
63
|
|
|
63
64
|
const toString = (value) => {
|
|
64
|
-
if (_.isUndefined(value)) return
|
|
65
|
+
if (_.isUndefined(value) || _.isNil(value)) return ''
|
|
65
66
|
if (_.isString(value)) return value
|
|
66
67
|
if (_.isNumber(value)) return value.toString()
|
|
67
68
|
if (_.isArray(value)) return value.map(v => toString(v)).join(',')
|
|
@@ -524,6 +525,63 @@ const linesToScriptingMemories = (lines, columnMode = null) => {
|
|
|
524
525
|
return scriptingMemories
|
|
525
526
|
}
|
|
526
527
|
|
|
528
|
+
const calculateWer = (str, pattern) => {
|
|
529
|
+
const _prepareString = (str, remWildcard = false) => {
|
|
530
|
+
if (remWildcard) return str.replace(/[.,/#!$%^&;:*{}=\-_`~()]/g, '').toLowerCase()
|
|
531
|
+
return str.replace(/[.,/#!$%^&;:{}=\-_`~()]/g, '').toLowerCase()
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
const _getSubsets = (array, size) => {
|
|
535
|
+
const subsets = []
|
|
536
|
+
for (const index in array) {
|
|
537
|
+
const end = parseInt(index) + size
|
|
538
|
+
if (end <= array.length) { subsets.push(array.slice(index, end)) }
|
|
539
|
+
}
|
|
540
|
+
return subsets
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
const _getWords = str => str.split(' ').map(w => w.trim())
|
|
544
|
+
|
|
545
|
+
const _getErrors = (words1, words2) => words1.map((w, i) => {
|
|
546
|
+
return w !== words2[i]
|
|
547
|
+
})
|
|
548
|
+
|
|
549
|
+
const utterance = pattern
|
|
550
|
+
|
|
551
|
+
const botMessage = _prepareString(str)
|
|
552
|
+
const botMessageWords = botMessage.split(' ').map(bm => bm.trim())
|
|
553
|
+
const utt = _prepareString(utterance)
|
|
554
|
+
|
|
555
|
+
const errors = []
|
|
556
|
+
for (let wildcardPart of utt.split('*')) {
|
|
557
|
+
let wer = 1
|
|
558
|
+
wildcardPart = wildcardPart.trim()
|
|
559
|
+
if (wildcardPart.length === 0) {
|
|
560
|
+
errors.push([false])
|
|
561
|
+
continue
|
|
562
|
+
}
|
|
563
|
+
const wordCount = wildcardPart.split(' ').length
|
|
564
|
+
const subsetPhrases = _getSubsets(botMessageWords, wordCount).map(subset => subset.join(' ')) // botMessageWordsSubsets.filter(subset => subset.length === wordCount).map(subset => subset.reverse().join(' '))
|
|
565
|
+
let subsetPhraseFound = null
|
|
566
|
+
for (const subsetPhrase of subsetPhrases) {
|
|
567
|
+
const localWer = speechScorer.wordErrorRate(subsetPhrase, wildcardPart).toFixed(2)
|
|
568
|
+
if (localWer <= wer) {
|
|
569
|
+
subsetPhraseFound = subsetPhrase
|
|
570
|
+
wer = localWer
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
console.log('lala', subsetPhraseFound)
|
|
574
|
+
errors.push(_getErrors(_getWords(wildcardPart), _getWords(subsetPhraseFound)))
|
|
575
|
+
}
|
|
576
|
+
let errCount = 0
|
|
577
|
+
let allCount = 0
|
|
578
|
+
for (const err of errors) {
|
|
579
|
+
errCount += err.filter(err => err === true).length
|
|
580
|
+
allCount += err.length
|
|
581
|
+
}
|
|
582
|
+
return (errCount / allCount).toFixed(2)
|
|
583
|
+
}
|
|
584
|
+
|
|
527
585
|
module.exports = {
|
|
528
586
|
normalizeText,
|
|
529
587
|
splitStringInNonEmptyLines,
|
|
@@ -537,5 +595,6 @@ module.exports = {
|
|
|
537
595
|
validSenders,
|
|
538
596
|
validateSender,
|
|
539
597
|
validateConvo,
|
|
540
|
-
linesToScriptingMemories
|
|
598
|
+
linesToScriptingMemories,
|
|
599
|
+
calculateWer
|
|
541
600
|
}
|
|
@@ -139,7 +139,7 @@ module.exports = class JsonPathAsserter {
|
|
|
139
139
|
matchFn = getMatchFunction(this.globalArgs.matchingMode)
|
|
140
140
|
}
|
|
141
141
|
|
|
142
|
-
const match =
|
|
142
|
+
const match = jsonPathValues.findIndex(a => matchFn(a, assert)) >= 0
|
|
143
143
|
|
|
144
144
|
if (not && match) {
|
|
145
145
|
return Promise.reject(new BotiumError(`${convoStep.stepTag}: Not expected: "${actual === '' ? '<empty>' : toString(actual)}" in jsonPath ${path}"`,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// const _ = require('lodash')
|
|
2
|
-
const speechScorer = require('word-error-rate')
|
|
3
2
|
const { BotiumError } = require('../../BotiumError')
|
|
3
|
+
const { calculateWer } = require('../../helper')
|
|
4
4
|
|
|
5
5
|
module.exports = class WerAsserter {
|
|
6
6
|
constructor (context, caps = {}) {
|
|
@@ -34,7 +34,8 @@ module.exports = class WerAsserter {
|
|
|
34
34
|
const utterance = args[0]
|
|
35
35
|
const threshold = ([',', '.'].find(p => `${args[1]}`.includes(p)) ? parseFloat(args[1]) : parseInt(args[1]) / 100).toFixed(2)
|
|
36
36
|
|
|
37
|
-
const wer =
|
|
37
|
+
const wer = calculateWer(botMsg.messageText, utterance)
|
|
38
|
+
|
|
38
39
|
if (wer > threshold) {
|
|
39
40
|
const _toPercent = (s) => `${(s * 100).toFixed(0)}%`
|
|
40
41
|
|
|
@@ -44,7 +44,7 @@ module.exports = class SetScriptingMemoryLogicHook {
|
|
|
44
44
|
// args[0] cant have the whole name of the variable, because the variable names are replaced
|
|
45
45
|
const name = '$' + params.name
|
|
46
46
|
const value = params.value
|
|
47
|
-
debug(`Set scripting memory variable "${name}" from "${scriptingMemory[name]}" to "${value}, isGlobal: ${isGlobal}, type: ${type}"`)
|
|
47
|
+
debug(`Set scripting memory variable "${name}" from "${scriptingMemory[name]}" to "${value}", isGlobal: ${isGlobal}, type: ${type}"`)
|
|
48
48
|
scriptingMemory[name] = value
|
|
49
49
|
|
|
50
50
|
return Promise.resolve()
|
|
@@ -163,6 +163,26 @@ describe('convo.fillAndApplyScriptingMemory', function () {
|
|
|
163
163
|
const transcript = await compiler.convos[0].Run(container)
|
|
164
164
|
assert.isObject(transcript.scriptingMemory)
|
|
165
165
|
})
|
|
166
|
+
it('should apply scripting memory in begin args', async function () {
|
|
167
|
+
const myCaps = {
|
|
168
|
+
[Capabilities.PROJECTNAME]: 'convo.scriptingmemory',
|
|
169
|
+
[Capabilities.CONTAINERMODE]: echoConnector,
|
|
170
|
+
[Capabilities.SCRIPTING_ENABLE_MEMORY]: true,
|
|
171
|
+
[Capabilities.CUSTOMHOOK_ONBUILD]: 'module.exports = ({ container }) => { console.log("customhooks called"); container.caps.MYTOKEN = "test1234" }'
|
|
172
|
+
}
|
|
173
|
+
const driver = new BotDriver(myCaps)
|
|
174
|
+
const compiler = driver.BuildCompiler()
|
|
175
|
+
const container = await driver.Build()
|
|
176
|
+
|
|
177
|
+
compiler.ReadScript(path.resolve(__dirname, 'convos'), 'applyscriptingmemoryinbegin.convo.txt')
|
|
178
|
+
assert.equal(compiler.convos.length, 1)
|
|
179
|
+
|
|
180
|
+
const transcript = await compiler.convos[0].Run(container)
|
|
181
|
+
assert.isObject(transcript.scriptingMemory)
|
|
182
|
+
assert.equal(transcript.scriptingMemory.$access_token, 'test1234')
|
|
183
|
+
assert.equal(transcript.steps[0].actual.messageText, 'access token: test1234')
|
|
184
|
+
assert.equal(transcript.steps[2].actual.messageText, 'access token: test1234')
|
|
185
|
+
})
|
|
166
186
|
})
|
|
167
187
|
|
|
168
188
|
describe('api', function () {
|
|
@@ -34,6 +34,17 @@ describe('scripting.asserters.jsonPathAsserter', function () {
|
|
|
34
34
|
}
|
|
35
35
|
})
|
|
36
36
|
})
|
|
37
|
+
it('should succeed if null', async function () {
|
|
38
|
+
await this.jsonPathAsserterEquals.assertConvoStep({
|
|
39
|
+
convoStep: { stepTag: 'test' },
|
|
40
|
+
args: ['$.test', ''],
|
|
41
|
+
botMsg: {
|
|
42
|
+
sourceData: {
|
|
43
|
+
test: null
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
})
|
|
47
|
+
})
|
|
37
48
|
it('should succeed if expected is empty, and asserter is negated', async function () {
|
|
38
49
|
await this.jsonPathAsserterEquals.assertNotConvoStep({
|
|
39
50
|
convoStep: { stepTag: 'test' },
|
|
@@ -68,4 +68,45 @@ describe('scripting.asserters.werAsserter', function () {
|
|
|
68
68
|
assert.equal(err.message, 'wer_threshold_nok/Line 2: assertion error - Line 2: Word Error Rate (50%) higher than accepted (10%)')
|
|
69
69
|
}
|
|
70
70
|
})
|
|
71
|
+
|
|
72
|
+
it('ok wildcard (percentage)', async function () {
|
|
73
|
+
this.compiler.ReadScriptsFromDirectory(path.resolve(__dirname, 'convos', 'wer_threshold_wildcard_ok_percentage.yml'))
|
|
74
|
+
|
|
75
|
+
this.compiler.ExpandScriptingMemoryToConvos()
|
|
76
|
+
assert.equal(this.compiler.convos.length, 1)
|
|
77
|
+
await this.compiler.convos[0].Run(this.container)
|
|
78
|
+
})
|
|
79
|
+
it('ok wildcard (float)', async function () {
|
|
80
|
+
this.compiler.ReadScriptsFromDirectory(path.resolve(__dirname, 'convos', 'wer_threshold_wildcard_ok_float.yml'))
|
|
81
|
+
|
|
82
|
+
this.compiler.ExpandScriptingMemoryToConvos()
|
|
83
|
+
assert.equal(this.compiler.convos.length, 1)
|
|
84
|
+
await this.compiler.convos[0].Run(this.container)
|
|
85
|
+
})
|
|
86
|
+
it('nok wildcard (percentage)', async function () {
|
|
87
|
+
this.compiler.ReadScriptsFromDirectory(path.resolve(__dirname, 'convos', 'wer_threshold_wildcard_nok_percentage.yml'))
|
|
88
|
+
|
|
89
|
+
this.compiler.ExpandScriptingMemoryToConvos()
|
|
90
|
+
assert.equal(this.compiler.convos.length, 1)
|
|
91
|
+
|
|
92
|
+
try {
|
|
93
|
+
await this.compiler.convos[0].Run(this.container)
|
|
94
|
+
assert.fail('expected error')
|
|
95
|
+
} catch (err) {
|
|
96
|
+
assert.equal(err.message, 'wer_threshold_wildcard_nok/Line 2: assertion error - Line 2: Word Error Rate (33%) higher than accepted (1%)')
|
|
97
|
+
}
|
|
98
|
+
})
|
|
99
|
+
it('nok wildcard (float)', async function () {
|
|
100
|
+
this.compiler.ReadScriptsFromDirectory(path.resolve(__dirname, 'convos', 'wer_threshold_wildcard_nok_float.yml'))
|
|
101
|
+
|
|
102
|
+
this.compiler.ExpandScriptingMemoryToConvos()
|
|
103
|
+
assert.equal(this.compiler.convos.length, 1)
|
|
104
|
+
|
|
105
|
+
try {
|
|
106
|
+
await this.compiler.convos[0].Run(this.container)
|
|
107
|
+
assert.fail('expected error')
|
|
108
|
+
} catch (err) {
|
|
109
|
+
assert.equal(err.message, 'wer_threshold_wildcard_nok/Line 2: assertion error - Line 2: Word Error Rate (75%) higher than accepted (40%)')
|
|
110
|
+
}
|
|
111
|
+
})
|
|
71
112
|
})
|
|
@@ -387,4 +387,86 @@ describe('scripting.matching.matchingmode', function () {
|
|
|
387
387
|
assert.isTrue(this.compiler.Match('test 123', 'tast 123'))
|
|
388
388
|
})
|
|
389
389
|
})
|
|
390
|
+
|
|
391
|
+
describe('wer.lowthreshold.wildcard (float)', function () {
|
|
392
|
+
beforeEach(async function () {
|
|
393
|
+
const myCaps = {
|
|
394
|
+
[Capabilities.PROJECTNAME]: 'matching.matchingmode',
|
|
395
|
+
[Capabilities.CONTAINERMODE]: echoConnector,
|
|
396
|
+
[Capabilities.SCRIPTING_MATCHING_MODE]: 'wer',
|
|
397
|
+
[Capabilities.SCRIPTING_MATCHING_MODE_ARGS]: [0.4]
|
|
398
|
+
}
|
|
399
|
+
const driver = new BotDriver(myCaps)
|
|
400
|
+
this.compiler = driver.BuildCompiler()
|
|
401
|
+
this.container = await driver.Build()
|
|
402
|
+
})
|
|
403
|
+
afterEach(async function () {
|
|
404
|
+
this.container && await this.container.Clean()
|
|
405
|
+
})
|
|
406
|
+
|
|
407
|
+
it('should not match because of low threshold', async function () {
|
|
408
|
+
assert.isFalse(this.compiler.Match('This is an example', '* that are * hot'))
|
|
409
|
+
})
|
|
410
|
+
})
|
|
411
|
+
describe('wer.lowthreshold.wildcard (percentage)', function () {
|
|
412
|
+
beforeEach(async function () {
|
|
413
|
+
const myCaps = {
|
|
414
|
+
[Capabilities.PROJECTNAME]: 'matching.matchingmode',
|
|
415
|
+
[Capabilities.CONTAINERMODE]: echoConnector,
|
|
416
|
+
[Capabilities.SCRIPTING_MATCHING_MODE]: 'wer',
|
|
417
|
+
[Capabilities.SCRIPTING_MATCHING_MODE_ARGS]: [10]
|
|
418
|
+
}
|
|
419
|
+
const driver = new BotDriver(myCaps)
|
|
420
|
+
this.compiler = driver.BuildCompiler()
|
|
421
|
+
this.container = await driver.Build()
|
|
422
|
+
})
|
|
423
|
+
afterEach(async function () {
|
|
424
|
+
this.container && await this.container.Clean()
|
|
425
|
+
})
|
|
426
|
+
|
|
427
|
+
it('should not match because of low threshold', async function () {
|
|
428
|
+
assert.isFalse(this.compiler.Match('This is an example', '* is * hot'))
|
|
429
|
+
})
|
|
430
|
+
})
|
|
431
|
+
|
|
432
|
+
describe('wer.highthreshold.wildcard (float)', function () {
|
|
433
|
+
beforeEach(async function () {
|
|
434
|
+
const myCaps = {
|
|
435
|
+
[Capabilities.PROJECTNAME]: 'matching.matchingmode',
|
|
436
|
+
[Capabilities.CONTAINERMODE]: echoConnector,
|
|
437
|
+
[Capabilities.SCRIPTING_MATCHING_MODE]: 'wer',
|
|
438
|
+
[Capabilities.SCRIPTING_MATCHING_MODE_ARGS]: [0.4]
|
|
439
|
+
}
|
|
440
|
+
const driver = new BotDriver(myCaps)
|
|
441
|
+
this.compiler = driver.BuildCompiler()
|
|
442
|
+
this.container = await driver.Build()
|
|
443
|
+
})
|
|
444
|
+
afterEach(async function () {
|
|
445
|
+
this.container && await this.container.Clean()
|
|
446
|
+
})
|
|
447
|
+
|
|
448
|
+
it('should match because of high threshold', async function () {
|
|
449
|
+
assert.isTrue(this.compiler.Match('this is an example', 'this is * sample'))
|
|
450
|
+
})
|
|
451
|
+
})
|
|
452
|
+
describe('wer.highthreshold.wildcard (percentage)', function () {
|
|
453
|
+
beforeEach(async function () {
|
|
454
|
+
const myCaps = {
|
|
455
|
+
[Capabilities.PROJECTNAME]: 'matching.matchingmode',
|
|
456
|
+
[Capabilities.CONTAINERMODE]: echoConnector,
|
|
457
|
+
[Capabilities.SCRIPTING_MATCHING_MODE]: 'wer',
|
|
458
|
+
[Capabilities.SCRIPTING_MATCHING_MODE_ARGS]: [30]
|
|
459
|
+
}
|
|
460
|
+
const driver = new BotDriver(myCaps)
|
|
461
|
+
this.compiler = driver.BuildCompiler()
|
|
462
|
+
this.container = await driver.Build()
|
|
463
|
+
})
|
|
464
|
+
afterEach(async function () {
|
|
465
|
+
this.container && await this.container.Clean()
|
|
466
|
+
})
|
|
467
|
+
|
|
468
|
+
it('should match because of high threshold', async function () {
|
|
469
|
+
assert.isTrue(this.compiler.Match('this is an example', 'this is another *'))
|
|
470
|
+
})
|
|
471
|
+
})
|
|
390
472
|
})
|
|
@@ -20,6 +20,7 @@ describe('scriptingModificator.assertions', function () {
|
|
|
20
20
|
]
|
|
21
21
|
},
|
|
22
22
|
scriptingMemory: {},
|
|
23
|
+
container: {},
|
|
23
24
|
botMsg: {
|
|
24
25
|
buttons: [
|
|
25
26
|
{
|
|
@@ -47,6 +48,7 @@ describe('scriptingModificator.assertions', function () {
|
|
|
47
48
|
]
|
|
48
49
|
},
|
|
49
50
|
scriptingMemory: {},
|
|
51
|
+
container: {},
|
|
50
52
|
botMsg: {
|
|
51
53
|
nlp: {
|
|
52
54
|
intent: { name: 'test1' }
|