@botonic/plugin-flow-builder 0.26.1 → 0.27.0-alpha.0
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/lib/cjs/action/index.js +39 -19
- package/lib/cjs/action/index.js.map +1 -1
- package/lib/cjs/action/knowledge-bases.js +14 -4
- package/lib/cjs/action/knowledge-bases.js.map +1 -1
- package/lib/cjs/api.d.ts +4 -6
- package/lib/cjs/api.js +7 -33
- package/lib/cjs/api.js.map +1 -1
- package/lib/cjs/constants.d.ts +1 -0
- package/lib/cjs/constants.js +2 -1
- package/lib/cjs/constants.js.map +1 -1
- package/lib/cjs/content-fields/flow-button.d.ts +0 -1
- package/lib/cjs/content-fields/flow-button.js +0 -10
- package/lib/cjs/content-fields/flow-button.js.map +1 -1
- package/lib/cjs/content-fields/flow-handoff.js +7 -19
- package/lib/cjs/content-fields/flow-handoff.js.map +1 -1
- package/lib/cjs/content-fields/hubtype-fields/button.d.ts +1 -2
- package/lib/cjs/content-fields/index.d.ts +1 -0
- package/lib/cjs/content-fields/index.js +3 -1
- package/lib/cjs/content-fields/index.js.map +1 -1
- package/lib/cjs/functions/conditional-queue-status.d.ts +7 -1
- package/lib/cjs/functions/conditional-queue-status.js +34 -12
- package/lib/cjs/functions/conditional-queue-status.js.map +1 -1
- package/lib/cjs/index.d.ts +7 -1
- package/lib/cjs/index.js +29 -6
- package/lib/cjs/index.js.map +1 -1
- package/lib/cjs/tracking.d.ts +18 -14
- package/lib/cjs/tracking.js +27 -20
- package/lib/cjs/tracking.js.map +1 -1
- package/lib/cjs/types.d.ts +14 -1
- package/lib/cjs/types.js.map +1 -1
- package/lib/cjs/user-input/index.d.ts +2 -1
- package/lib/cjs/user-input/index.js +9 -3
- package/lib/cjs/user-input/index.js.map +1 -1
- package/lib/cjs/user-input/intent.js +12 -8
- package/lib/cjs/user-input/intent.js.map +1 -1
- package/lib/cjs/user-input/keyword.d.ts +21 -1
- package/lib/cjs/user-input/keyword.js +68 -13
- package/lib/cjs/user-input/keyword.js.map +1 -1
- package/lib/cjs/user-input/smart-intent.d.ts +18 -1
- package/lib/cjs/user-input/smart-intent.js +46 -29
- package/lib/cjs/user-input/smart-intent.js.map +1 -1
- package/lib/esm/action/index.js +39 -19
- package/lib/esm/action/index.js.map +1 -1
- package/lib/esm/action/knowledge-bases.js +15 -5
- package/lib/esm/action/knowledge-bases.js.map +1 -1
- package/lib/esm/api.d.ts +4 -6
- package/lib/esm/api.js +8 -34
- package/lib/esm/api.js.map +1 -1
- package/lib/esm/constants.d.ts +1 -0
- package/lib/esm/constants.js +1 -0
- package/lib/esm/constants.js.map +1 -1
- package/lib/esm/content-fields/flow-button.d.ts +0 -1
- package/lib/esm/content-fields/flow-button.js +0 -10
- package/lib/esm/content-fields/flow-button.js.map +1 -1
- package/lib/esm/content-fields/flow-handoff.js +7 -19
- package/lib/esm/content-fields/flow-handoff.js.map +1 -1
- package/lib/esm/content-fields/hubtype-fields/button.d.ts +1 -2
- package/lib/esm/content-fields/index.d.ts +1 -0
- package/lib/esm/content-fields/index.js +1 -0
- package/lib/esm/content-fields/index.js.map +1 -1
- package/lib/esm/functions/conditional-queue-status.d.ts +7 -1
- package/lib/esm/functions/conditional-queue-status.js +32 -11
- package/lib/esm/functions/conditional-queue-status.js.map +1 -1
- package/lib/esm/index.d.ts +7 -1
- package/lib/esm/index.js +30 -7
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/tracking.d.ts +18 -14
- package/lib/esm/tracking.js +25 -19
- package/lib/esm/tracking.js.map +1 -1
- package/lib/esm/types.d.ts +14 -1
- package/lib/esm/types.js.map +1 -1
- package/lib/esm/user-input/index.d.ts +2 -1
- package/lib/esm/user-input/index.js +11 -5
- package/lib/esm/user-input/index.js.map +1 -1
- package/lib/esm/user-input/intent.js +13 -9
- package/lib/esm/user-input/intent.js.map +1 -1
- package/lib/esm/user-input/keyword.d.ts +21 -1
- package/lib/esm/user-input/keyword.js +67 -12
- package/lib/esm/user-input/keyword.js.map +1 -1
- package/lib/esm/user-input/smart-intent.d.ts +18 -1
- package/lib/esm/user-input/smart-intent.js +44 -27
- package/lib/esm/user-input/smart-intent.js.map +1 -1
- package/package.json +3 -3
- package/src/action/index.tsx +58 -24
- package/src/action/knowledge-bases.ts +16 -5
- package/src/api.ts +12 -55
- package/src/constants.ts +1 -0
- package/src/content-fields/flow-button.tsx +0 -10
- package/src/content-fields/flow-handoff.tsx +8 -28
- package/src/content-fields/hubtype-fields/button.ts +1 -7
- package/src/content-fields/index.ts +1 -0
- package/src/functions/conditional-queue-status.ts +36 -13
- package/src/index.ts +48 -7
- package/src/tracking.ts +25 -14
- package/src/types.ts +13 -1
- package/src/user-input/index.ts +13 -7
- package/src/user-input/intent.ts +17 -10
- package/src/user-input/keyword.ts +93 -14
- package/src/user-input/smart-intent.ts +57 -30
package/src/types.ts
CHANGED
|
@@ -12,12 +12,13 @@ export interface BotonicPluginFlowBuilderOptions {
|
|
|
12
12
|
getAccessToken: () => string
|
|
13
13
|
trackEvent?: (
|
|
14
14
|
request: ActionRequest,
|
|
15
|
-
|
|
15
|
+
eventAction: string,
|
|
16
16
|
args?: Record<string, any>
|
|
17
17
|
) => Promise<void>
|
|
18
18
|
getKnowledgeBaseResponse?: (
|
|
19
19
|
request: ActionRequest
|
|
20
20
|
) => Promise<KnowledgeBaseResponse>
|
|
21
|
+
smartIntentsConfig?: { numSmartIntentsToUse: number }
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
export interface FlowBuilderApiOptions {
|
|
@@ -46,6 +47,17 @@ export interface KnowledgeBaseResponse {
|
|
|
46
47
|
}[]
|
|
47
48
|
}
|
|
48
49
|
|
|
50
|
+
export interface SmartIntentResponse {
|
|
51
|
+
data: {
|
|
52
|
+
smart_intent_title: string
|
|
53
|
+
text: string
|
|
54
|
+
smart_intents_used: {
|
|
55
|
+
title: string
|
|
56
|
+
description: string
|
|
57
|
+
}[]
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
49
61
|
export interface PayloadParamsBase {
|
|
50
62
|
followUpContentID?: string
|
|
51
63
|
}
|
package/src/user-input/index.ts
CHANGED
|
@@ -7,24 +7,30 @@ import {
|
|
|
7
7
|
HtSmartIntentNode,
|
|
8
8
|
} from '../content-fields/hubtype-fields'
|
|
9
9
|
import { getIntentNodeByInput } from './intent'
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
10
|
+
import { KeywordMatcher } from './keyword'
|
|
11
|
+
import { SmartIntentsApi, SmartIntentsInferenceConfig } from './smart-intent'
|
|
12
12
|
|
|
13
13
|
export async function getNodeByUserInput(
|
|
14
14
|
cmsApi: FlowBuilderApi,
|
|
15
15
|
locale: string,
|
|
16
|
-
request: ActionRequest
|
|
16
|
+
request: ActionRequest,
|
|
17
|
+
smartIntentsConfig: SmartIntentsInferenceConfig
|
|
17
18
|
): Promise<HtSmartIntentNode | HtIntentNode | HtKeywordNode | undefined> {
|
|
18
19
|
if (request.input.data) {
|
|
19
|
-
const
|
|
20
|
+
const keywordMatcher = new KeywordMatcher({
|
|
20
21
|
cmsApi,
|
|
21
22
|
locale,
|
|
22
23
|
request,
|
|
23
|
-
|
|
24
|
-
)
|
|
24
|
+
})
|
|
25
|
+
const keywordNode = await keywordMatcher.getNodeByInput(request.input.data)
|
|
25
26
|
if (keywordNode) return keywordNode
|
|
26
27
|
|
|
27
|
-
const
|
|
28
|
+
const smartIntentsApi = new SmartIntentsApi(
|
|
29
|
+
cmsApi,
|
|
30
|
+
request,
|
|
31
|
+
smartIntentsConfig
|
|
32
|
+
)
|
|
33
|
+
const smartIntentNode = smartIntentsApi.getNodeByInput()
|
|
28
34
|
if (smartIntentNode) return smartIntentNode
|
|
29
35
|
|
|
30
36
|
const intentNode = await getIntentNodeByInput(cmsApi, locale, request)
|
package/src/user-input/intent.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { ActionRequest } from '@botonic/react'
|
|
|
2
2
|
|
|
3
3
|
import { FlowBuilderApi } from '../api'
|
|
4
4
|
import { HtIntentNode } from '../content-fields/hubtype-fields'
|
|
5
|
-
import {
|
|
5
|
+
import { EventAction, trackEvent } from '../tracking'
|
|
6
6
|
|
|
7
7
|
export async function getIntentNodeByInput(
|
|
8
8
|
cmsApi: FlowBuilderApi,
|
|
@@ -10,24 +10,31 @@ export async function getIntentNodeByInput(
|
|
|
10
10
|
request: ActionRequest
|
|
11
11
|
): Promise<HtIntentNode | undefined> {
|
|
12
12
|
const intentNode = cmsApi.getIntentNode(request.input, locale)
|
|
13
|
-
|
|
14
|
-
intent: request.input.intent as string,
|
|
15
|
-
confidence: request.input.confidence as number,
|
|
16
|
-
confidence_successful: true,
|
|
17
|
-
}
|
|
13
|
+
|
|
18
14
|
if (request.input.confidence && request.input.intent && intentNode) {
|
|
15
|
+
await trackIntentEvent(request, intentNode)
|
|
16
|
+
|
|
19
17
|
if (isIntentValid(intentNode, request, cmsApi)) {
|
|
20
|
-
await trackEvent(request, EventName.botAiModel, eventArgs)
|
|
21
18
|
return intentNode
|
|
22
19
|
}
|
|
23
|
-
|
|
24
|
-
eventArgs.confidence_successful = false
|
|
25
|
-
await trackEvent(request, EventName.botAiModel, eventArgs)
|
|
26
20
|
}
|
|
27
21
|
|
|
28
22
|
return undefined
|
|
29
23
|
}
|
|
30
24
|
|
|
25
|
+
async function trackIntentEvent(
|
|
26
|
+
request: ActionRequest,
|
|
27
|
+
intentNode: HtIntentNode
|
|
28
|
+
) {
|
|
29
|
+
const eventArgs = {
|
|
30
|
+
nluIntentLabel: request.input.intent,
|
|
31
|
+
nluIntentConfidence: request.input.confidence,
|
|
32
|
+
nluIntentThreshold: intentNode?.content.confidence,
|
|
33
|
+
nluIntentMessageId: request.input.message_id,
|
|
34
|
+
}
|
|
35
|
+
await trackEvent(request, EventAction.intent, eventArgs)
|
|
36
|
+
}
|
|
37
|
+
|
|
31
38
|
function isIntentValid(
|
|
32
39
|
intentNode: HtIntentNode,
|
|
33
40
|
request: ActionRequest,
|
|
@@ -1,22 +1,101 @@
|
|
|
1
1
|
import { ActionRequest } from '@botonic/react'
|
|
2
2
|
|
|
3
3
|
import { FlowBuilderApi } from '../api'
|
|
4
|
+
import { REG_EXP_PATTERN } from '../constants'
|
|
4
5
|
import { HtKeywordNode } from '../content-fields/hubtype-fields'
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
cmsApi: FlowBuilderApi
|
|
9
|
-
locale: string
|
|
10
|
-
request: ActionRequest
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
6
|
+
import { EventAction, trackEvent } from '../tracking'
|
|
7
|
+
|
|
8
|
+
interface KeywordProps {
|
|
9
|
+
cmsApi: FlowBuilderApi
|
|
10
|
+
locale: string
|
|
11
|
+
request: ActionRequest
|
|
12
|
+
}
|
|
13
|
+
export class KeywordMatcher {
|
|
14
|
+
public cmsApi: FlowBuilderApi
|
|
15
|
+
public locale: string
|
|
16
|
+
public request: ActionRequest
|
|
17
|
+
public isRegExp: boolean
|
|
18
|
+
public matchedKeyword?: string
|
|
19
|
+
public keywordNodeId?: string
|
|
20
|
+
|
|
21
|
+
constructor({ cmsApi, locale, request }: KeywordProps) {
|
|
22
|
+
this.cmsApi = cmsApi
|
|
23
|
+
this.locale = locale
|
|
24
|
+
this.request = request
|
|
25
|
+
this.isRegExp = false
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async getNodeByInput(userInput: string): Promise<HtKeywordNode | undefined> {
|
|
29
|
+
const keywordNodes = this.cmsApi.getKeywordNodes()
|
|
30
|
+
const keywordNode = this.getNodeByKeyword(userInput, keywordNodes)
|
|
31
|
+
if (!keywordNode) {
|
|
32
|
+
return undefined
|
|
33
|
+
}
|
|
34
|
+
this.trackKeywordEvent()
|
|
35
|
+
return keywordNode
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
private getNodeByKeyword(
|
|
39
|
+
userInput: string,
|
|
40
|
+
keywordNodes: HtKeywordNode[]
|
|
41
|
+
): HtKeywordNode | undefined {
|
|
42
|
+
const matchedKeywordNodes = keywordNodes.filter(node =>
|
|
43
|
+
this.matchKeywords(userInput, node)
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
if (matchedKeywordNodes.length > 0 && matchedKeywordNodes[0].target) {
|
|
47
|
+
return matchedKeywordNodes[0]
|
|
48
|
+
}
|
|
49
|
+
|
|
15
50
|
return undefined
|
|
16
51
|
}
|
|
17
|
-
|
|
18
|
-
|
|
52
|
+
|
|
53
|
+
private matchKeywords(userInput: string, node: HtKeywordNode): boolean {
|
|
54
|
+
const result = node.content.keywords.find(keywords => {
|
|
55
|
+
if (keywords.locale === this.locale) {
|
|
56
|
+
this.keywordNodeId = node.id
|
|
57
|
+
return this.inputMatchesAnyKeyword(userInput, keywords.values)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return false
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
return Boolean(result)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
private inputMatchesAnyKeyword(
|
|
67
|
+
userInput: string,
|
|
68
|
+
keywords: string[]
|
|
69
|
+
): boolean {
|
|
70
|
+
return keywords.some(keyword => {
|
|
71
|
+
const regExpMatchArray = keyword.match(REG_EXP_PATTERN)
|
|
72
|
+
|
|
73
|
+
if (regExpMatchArray) {
|
|
74
|
+
const keywordAsRegExp = this.resolveKeywordAsRegExp(regExpMatchArray)
|
|
75
|
+
const match = userInput.match(keywordAsRegExp)
|
|
76
|
+
this.isRegExp = true
|
|
77
|
+
this.matchedKeyword = match ? match[0] : undefined
|
|
78
|
+
} else {
|
|
79
|
+
this.isRegExp = false
|
|
80
|
+
this.matchedKeyword = userInput.includes(keyword) ? keyword : undefined
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return this.matchedKeyword !== undefined
|
|
84
|
+
})
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
private resolveKeywordAsRegExp(regExpMatchArray: RegExpMatchArray): RegExp {
|
|
88
|
+
const [, pattern, flags] = regExpMatchArray
|
|
89
|
+
return new RegExp(pattern, flags)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
private async trackKeywordEvent() {
|
|
93
|
+
const eventArgs = {
|
|
94
|
+
nluKeywordId: this.keywordNodeId,
|
|
95
|
+
nluKeywordName: this.matchedKeyword,
|
|
96
|
+
nluKeywordIsRegex: this.isRegExp,
|
|
97
|
+
nluKeywordMessageId: this.request.input.message_id,
|
|
98
|
+
}
|
|
99
|
+
await trackEvent(this.request, EventAction.keyword, eventArgs)
|
|
19
100
|
}
|
|
20
|
-
await trackEvent(request, EventName.botKeywordsModel, eventArgs)
|
|
21
|
-
return keywordNode
|
|
22
101
|
}
|
|
@@ -3,45 +3,72 @@ import axios from 'axios'
|
|
|
3
3
|
|
|
4
4
|
import { FlowBuilderApi } from '../api'
|
|
5
5
|
import { HtSmartIntentNode } from '../content-fields/hubtype-fields/smart-intent'
|
|
6
|
+
import { EventAction, trackEvent } from '../tracking'
|
|
7
|
+
import { SmartIntentResponse } from '../types'
|
|
6
8
|
|
|
7
|
-
export
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
export interface SmartIntentsInferenceParams {
|
|
10
|
+
bot_id: string
|
|
11
|
+
text: string
|
|
12
|
+
num_smart_intents_to_use?: number
|
|
13
|
+
use_latest: boolean
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface SmartIntentsInferenceConfig {
|
|
17
|
+
useLatest: boolean
|
|
18
|
+
numSmartIntentsToUse?: number
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export class SmartIntentsApi {
|
|
22
|
+
constructor(
|
|
23
|
+
public cmsApi: FlowBuilderApi,
|
|
24
|
+
public currentRequest: ActionRequest,
|
|
25
|
+
public smartIntentsConfig: SmartIntentsInferenceConfig
|
|
26
|
+
) {}
|
|
27
|
+
|
|
28
|
+
async getNodeByInput(): Promise<HtSmartIntentNode | undefined> {
|
|
29
|
+
if (!this.currentRequest.input.data) return undefined
|
|
30
|
+
const smartIntentNodes = this.cmsApi.getSmartIntentNodes()
|
|
31
|
+
if (!smartIntentNodes.length) return undefined
|
|
32
|
+
|
|
33
|
+
const params = {
|
|
34
|
+
bot_id: this.currentRequest.session.bot.id,
|
|
35
|
+
text: this.currentRequest.input.data,
|
|
36
|
+
num_smart_intents_to_use: this.smartIntentsConfig.numSmartIntentsToUse,
|
|
37
|
+
use_latest: this.smartIntentsConfig.useLatest,
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
const response = await this.getInference(params)
|
|
42
|
+
const smartIntentNode = smartIntentNodes.find(
|
|
43
|
+
smartIntentNode =>
|
|
44
|
+
smartIntentNode.content.title === response.data.smart_intent_title
|
|
45
|
+
)
|
|
46
|
+
if (smartIntentNode) {
|
|
47
|
+
trackEvent(this.currentRequest, EventAction.intentSmart, {
|
|
48
|
+
nluIntentSmartTitle: response.data.smart_intent_title,
|
|
49
|
+
nluIntentSmartNumUsed: response.data.smart_intents_used,
|
|
50
|
+
nluIntentSmartMessageId: this.currentRequest.input.message_id,
|
|
51
|
+
})
|
|
52
|
+
return smartIntentNode
|
|
53
|
+
}
|
|
54
|
+
} catch (e) {
|
|
55
|
+
console.error(e)
|
|
56
|
+
}
|
|
13
57
|
return undefined
|
|
14
58
|
}
|
|
15
59
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
21
|
-
})
|
|
22
|
-
intentsInferenceParams.push({
|
|
23
|
-
name: 'Other',
|
|
24
|
-
definition: 'The text does not belong to any other intent.',
|
|
25
|
-
})
|
|
26
|
-
|
|
27
|
-
try {
|
|
28
|
-
const response = await axios({
|
|
60
|
+
private async getInference(
|
|
61
|
+
inferenceParams: SmartIntentsInferenceParams
|
|
62
|
+
): Promise<SmartIntentResponse> {
|
|
63
|
+
return await axios({
|
|
29
64
|
method: 'POST',
|
|
30
|
-
url: `${process.env.HUBTYPE_API_URL}/external/
|
|
65
|
+
url: `${process.env.HUBTYPE_API_URL}/external/v2/ai/smart_intents/inference/`,
|
|
31
66
|
headers: {
|
|
32
|
-
Authorization: `Bearer ${
|
|
67
|
+
Authorization: `Bearer ${this.currentRequest.session._access_token}`,
|
|
33
68
|
'Content-Type': 'application/json',
|
|
34
69
|
},
|
|
35
|
-
data:
|
|
70
|
+
data: inferenceParams,
|
|
36
71
|
timeout: 10000,
|
|
37
72
|
})
|
|
38
|
-
console.log({ response })
|
|
39
|
-
return smartIntentNodes.find(
|
|
40
|
-
smartIntentNode =>
|
|
41
|
-
smartIntentNode.content.title === response.data.intent_name
|
|
42
|
-
)
|
|
43
|
-
} catch (e) {
|
|
44
|
-
console.error(e)
|
|
45
|
-
return undefined
|
|
46
73
|
}
|
|
47
74
|
}
|