@botonic/plugin-flow-builder 0.26.2-alpha.0 → 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.
Files changed (89) hide show
  1. package/lib/cjs/action/index.js +38 -18
  2. package/lib/cjs/action/index.js.map +1 -1
  3. package/lib/cjs/action/knowledge-bases.js +14 -4
  4. package/lib/cjs/action/knowledge-bases.js.map +1 -1
  5. package/lib/cjs/api.d.ts +2 -4
  6. package/lib/cjs/api.js +7 -32
  7. package/lib/cjs/api.js.map +1 -1
  8. package/lib/cjs/constants.d.ts +1 -1
  9. package/lib/cjs/constants.js +2 -2
  10. package/lib/cjs/constants.js.map +1 -1
  11. package/lib/cjs/content-fields/flow-handoff.js +7 -19
  12. package/lib/cjs/content-fields/flow-handoff.js.map +1 -1
  13. package/lib/cjs/content-fields/index.d.ts +1 -0
  14. package/lib/cjs/content-fields/index.js +3 -1
  15. package/lib/cjs/content-fields/index.js.map +1 -1
  16. package/lib/cjs/functions/conditional-queue-status.d.ts +7 -1
  17. package/lib/cjs/functions/conditional-queue-status.js +34 -12
  18. package/lib/cjs/functions/conditional-queue-status.js.map +1 -1
  19. package/lib/cjs/index.d.ts +6 -3
  20. package/lib/cjs/index.js +22 -13
  21. package/lib/cjs/index.js.map +1 -1
  22. package/lib/cjs/tracking.d.ts +18 -14
  23. package/lib/cjs/tracking.js +27 -20
  24. package/lib/cjs/tracking.js.map +1 -1
  25. package/lib/cjs/types.d.ts +14 -1
  26. package/lib/cjs/types.js.map +1 -1
  27. package/lib/cjs/user-input/index.d.ts +2 -1
  28. package/lib/cjs/user-input/index.js +9 -3
  29. package/lib/cjs/user-input/index.js.map +1 -1
  30. package/lib/cjs/user-input/intent.js +12 -8
  31. package/lib/cjs/user-input/intent.js.map +1 -1
  32. package/lib/cjs/user-input/keyword.d.ts +21 -1
  33. package/lib/cjs/user-input/keyword.js +68 -13
  34. package/lib/cjs/user-input/keyword.js.map +1 -1
  35. package/lib/cjs/user-input/smart-intent.d.ts +18 -1
  36. package/lib/cjs/user-input/smart-intent.js +46 -28
  37. package/lib/cjs/user-input/smart-intent.js.map +1 -1
  38. package/lib/esm/action/index.js +39 -19
  39. package/lib/esm/action/index.js.map +1 -1
  40. package/lib/esm/action/knowledge-bases.js +15 -5
  41. package/lib/esm/action/knowledge-bases.js.map +1 -1
  42. package/lib/esm/api.d.ts +2 -4
  43. package/lib/esm/api.js +8 -33
  44. package/lib/esm/api.js.map +1 -1
  45. package/lib/esm/constants.d.ts +1 -1
  46. package/lib/esm/constants.js +1 -1
  47. package/lib/esm/constants.js.map +1 -1
  48. package/lib/esm/content-fields/flow-handoff.js +7 -19
  49. package/lib/esm/content-fields/flow-handoff.js.map +1 -1
  50. package/lib/esm/content-fields/index.d.ts +1 -0
  51. package/lib/esm/content-fields/index.js +1 -0
  52. package/lib/esm/content-fields/index.js.map +1 -1
  53. package/lib/esm/functions/conditional-queue-status.d.ts +7 -1
  54. package/lib/esm/functions/conditional-queue-status.js +32 -11
  55. package/lib/esm/functions/conditional-queue-status.js.map +1 -1
  56. package/lib/esm/index.d.ts +6 -3
  57. package/lib/esm/index.js +23 -14
  58. package/lib/esm/index.js.map +1 -1
  59. package/lib/esm/tracking.d.ts +18 -14
  60. package/lib/esm/tracking.js +25 -19
  61. package/lib/esm/tracking.js.map +1 -1
  62. package/lib/esm/types.d.ts +14 -1
  63. package/lib/esm/types.js.map +1 -1
  64. package/lib/esm/user-input/index.d.ts +2 -1
  65. package/lib/esm/user-input/index.js +11 -5
  66. package/lib/esm/user-input/index.js.map +1 -1
  67. package/lib/esm/user-input/intent.js +13 -9
  68. package/lib/esm/user-input/intent.js.map +1 -1
  69. package/lib/esm/user-input/keyword.d.ts +21 -1
  70. package/lib/esm/user-input/keyword.js +67 -12
  71. package/lib/esm/user-input/keyword.js.map +1 -1
  72. package/lib/esm/user-input/smart-intent.d.ts +18 -1
  73. package/lib/esm/user-input/smart-intent.js +44 -26
  74. package/lib/esm/user-input/smart-intent.js.map +1 -1
  75. package/package.json +2 -2
  76. package/src/action/index.tsx +58 -24
  77. package/src/action/knowledge-bases.ts +16 -5
  78. package/src/api.ts +11 -57
  79. package/src/constants.ts +1 -1
  80. package/src/content-fields/flow-handoff.tsx +8 -28
  81. package/src/content-fields/index.ts +1 -0
  82. package/src/functions/conditional-queue-status.ts +36 -13
  83. package/src/index.ts +39 -19
  84. package/src/tracking.ts +25 -14
  85. package/src/types.ts +13 -1
  86. package/src/user-input/index.ts +13 -7
  87. package/src/user-input/intent.ts +17 -10
  88. package/src/user-input/keyword.ts +93 -14
  89. package/src/user-input/smart-intent.ts +57 -29
package/src/api.ts CHANGED
@@ -1,11 +1,7 @@
1
1
  import { Input, PluginPreRequest } from '@botonic/core'
2
2
  import axios from 'axios'
3
3
 
4
- import {
5
- BOT_ACTION_PAYLOAD_SEPARATOR,
6
- REG_EXP_PATTERN,
7
- SEPARATOR,
8
- } from './constants'
4
+ import { BOT_ACTION_PAYLOAD_PREFIX, SEPARATOR } from './constants'
9
5
  import {
10
6
  HtBotActionNode,
11
7
  HtFallbackNode,
@@ -154,57 +150,10 @@ export class FlowBuilderApi {
154
150
  return predictedConfidence >= nodeConfidence
155
151
  }
156
152
 
157
- getNodeByKeyword(
158
- userInput: string,
159
- locale: string
160
- ): HtKeywordNode | undefined {
161
- try {
162
- const keywordNodes = this.flow.nodes.filter(
163
- node => node.type === HtNodeWithContentType.KEYWORD
164
- ) as HtKeywordNode[]
165
- const matchedKeywordNodes = keywordNodes.filter(node =>
166
- this.matchKeywords(node, userInput, locale)
167
- )
168
- if (matchedKeywordNodes.length > 0 && matchedKeywordNodes[0].target) {
169
- return matchedKeywordNodes[0]
170
- }
171
- } catch (error) {
172
- console.error(`Error getting node by keyword '${userInput}': `, error)
173
- }
174
-
175
- return undefined
176
- }
177
-
178
- private matchKeywords(
179
- node: HtKeywordNode,
180
- input: string,
181
- locale: string
182
- ): boolean {
183
- const result = node.content.keywords.find(
184
- keywords =>
185
- keywords.locale === locale &&
186
- this.inputMatchesAnyKeyword(input, keywords.values)
187
- )
188
- return Boolean(result)
189
- }
190
-
191
- private inputMatchesAnyKeyword(input: string, keywords: string[]): boolean {
192
- return keywords.some(keyword => {
193
- const regExpMatchArray = keyword.match(REG_EXP_PATTERN)
194
- if (regExpMatchArray) {
195
- return this.resolveKeywordAsRegExp(regExpMatchArray, input)
196
- }
197
- return input.includes(keyword)
198
- })
199
- }
200
-
201
- private resolveKeywordAsRegExp(
202
- regExpMatchArray: RegExpMatchArray,
203
- input: string
204
- ) {
205
- const [, pattern, flags] = regExpMatchArray
206
- const keywordAsRegExp = new RegExp(pattern, flags)
207
- return input.match(keywordAsRegExp)
153
+ getKeywordNodes(): HtKeywordNode[] {
154
+ return this.flow.nodes.filter(
155
+ node => node.type === HtNodeWithContentType.KEYWORD
156
+ ) as HtKeywordNode[]
208
157
  }
209
158
 
210
159
  getPayload(target?: HtNodeLink): string | undefined {
@@ -213,7 +162,7 @@ export class FlowBuilderApi {
213
162
  }
214
163
 
215
164
  if (target.type === HtNodeWithoutContentType.BOT_ACTION) {
216
- return `${BOT_ACTION_PAYLOAD_SEPARATOR}${target.id}`
165
+ return `${BOT_ACTION_PAYLOAD_PREFIX}${target.id}`
217
166
  }
218
167
 
219
168
  return target.id
@@ -250,6 +199,11 @@ export class FlowBuilderApi {
250
199
  }
251
200
  }
252
201
 
202
+ getFlowName(flowId: string): string {
203
+ const flow = this.flow.flows.find(flow => flow.id === flowId)
204
+ return flow ? flow.name : ''
205
+ }
206
+
253
207
  getResolvedLocale(locale: string): string {
254
208
  if (this.flow.locales.find(flowLocale => flowLocale === locale)) {
255
209
  return locale
package/src/constants.ts CHANGED
@@ -2,7 +2,7 @@ export const FLOW_BUILDER_API_URL_PROD =
2
2
  'https://api.ent0.flowbuilder.prod.hubtype.com'
3
3
  export const SEPARATOR = '|'
4
4
  export const SOURCE_INFO_SEPARATOR = `${SEPARATOR}source_`
5
- export const BOT_ACTION_PAYLOAD_SEPARATOR = `ba${SEPARATOR}`
5
+ export const BOT_ACTION_PAYLOAD_PREFIX = `ba${SEPARATOR}`
6
6
  export const VARIABLE_PATTERN = /{([^}]+)}/g
7
7
  export const ACCESS_TOKEN_VARIABLE_KEY = '_access_token'
8
8
  export const REG_EXP_PATTERN = /^\/(.*)\/([gimyus]*)$/
@@ -3,10 +3,8 @@ import { ActionRequest, WebchatSettings } from '@botonic/react'
3
3
  import React from 'react'
4
4
 
5
5
  import { FlowBuilderApi } from '../api'
6
- import { getQueueAvailability } from '../functions/conditional-queue-status'
7
- import { EventName, trackEvent } from '../tracking'
8
6
  import { ContentFieldsBase } from './content-fields-base'
9
- import { HtHandoffNode, HtPayloadNode, HtQueueLocale } from './hubtype-fields'
7
+ import { HtHandoffNode, HtQueueLocale } from './hubtype-fields'
10
8
 
11
9
  export class FlowHandoff extends ContentFieldsBase {
12
10
  public code: string
@@ -22,11 +20,7 @@ export class FlowHandoff extends ContentFieldsBase {
22
20
  const newHandoff = new FlowHandoff(cmsHandoff.id)
23
21
  newHandoff.code = cmsHandoff.code
24
22
  newHandoff.queue = this.getQueueByLocale(locale, cmsHandoff.content.queue)
25
- newHandoff.onFinishPayload = this.getOnFinishPayload(
26
- cmsHandoff,
27
- locale,
28
- cmsApi
29
- )
23
+ newHandoff.onFinishPayload = this.getOnFinishPayload(cmsHandoff, cmsApi)
30
24
  newHandoff.handoffAutoAssign = cmsHandoff.content.has_auto_assign
31
25
 
32
26
  return newHandoff
@@ -34,43 +28,29 @@ export class FlowHandoff extends ContentFieldsBase {
34
28
 
35
29
  private static getOnFinishPayload(
36
30
  cmsHandoff: HtHandoffNode,
37
- locale: string,
38
31
  cmsApi: FlowBuilderApi
39
32
  ): string | undefined {
40
33
  if (cmsHandoff.target?.id) {
41
34
  return cmsApi.getPayload(cmsHandoff.target)
42
35
  }
43
36
 
44
- // OLD PAYLOAD
45
- const payloadId = cmsHandoff.content.payload.find(
46
- payload => payload.locale === locale
47
- )?.id
48
- if (payloadId) {
49
- return cmsApi.getNodeById<HtPayloadNode>(payloadId).content.payload
50
- }
51
-
52
37
  return undefined
53
38
  }
54
39
 
55
40
  async doHandoff(request: ActionRequest): Promise<void> {
56
- // @ts-ignore
57
41
  const handOffBuilder = new HandOffBuilder(request.session)
58
42
  handOffBuilder.withAutoAssignOnWaiting(this.handoffAutoAssign)
43
+
59
44
  if (this.onFinishPayload) {
60
45
  handOffBuilder.withOnFinishPayload(this.onFinishPayload)
61
46
  }
62
- if (this.queue) {
63
- const availabilityData = await getQueueAvailability(this.queue.id)
64
- const eventArgs = {
65
- queue_open: availabilityData.open,
66
- queue_id: this.queue.id,
67
- available_agents: availabilityData.available_agents > 0,
68
- threshold_reached:
69
- availabilityData.availability_threshold_waiting_cases > 0,
70
- }
71
- await trackEvent(request, EventName.handoffSuccess, eventArgs)
72
47
 
48
+ if (this.queue) {
73
49
  handOffBuilder.withQueue(this.queue.id)
50
+ handOffBuilder.withBotEvent({
51
+ language: request.session.user.extra_data.language,
52
+ country: request.session.user.extra_data.country,
53
+ })
74
54
  await handOffBuilder.handOff()
75
55
  }
76
56
  }
@@ -11,6 +11,7 @@ export { FlowButton } from './flow-button'
11
11
  export { FlowElement } from './flow-element'
12
12
  export { FlowCarousel, FlowImage, FlowText, FlowVideo, FlowWhatsappButtonList }
13
13
  export { FlowHandoff } from './flow-handoff'
14
+ export { FlowWhatsappCtaUrlButtonNode } from './flow-whatsapp-cta-url-button'
14
15
 
15
16
  export type FlowContent =
16
17
  | FlowCarousel
@@ -1,6 +1,8 @@
1
1
  import { ActionRequest } from '@botonic/react'
2
2
  import axios from 'axios'
3
3
 
4
+ // import { EventAction, trackEvent } from '../tracking'
5
+
4
6
  const HUBTYPE_API_URL = process.env.HUBTYPE_API_URL || 'https://api.hubtype.com'
5
7
 
6
8
  type ConditionalQueueStatusArgs = {
@@ -17,17 +19,48 @@ enum QueueStatusResult {
17
19
  }
18
20
 
19
21
  export async function conditionalQueueStatus({
22
+ // request,
20
23
  queue_id,
24
+ // queue_name,
21
25
  check_available_agents,
22
26
  }: ConditionalQueueStatusArgs): Promise<QueueStatusResult> {
23
27
  const data = await getQueueAvailability(queue_id, check_available_agents)
28
+ // await trackEvent(request, EventAction.handoffOption, {
29
+ // queueId: queue_id,
30
+ // queueName: queue_name,
31
+ // })
24
32
  if (check_available_agents && data.open && data.available_agents === 0) {
25
33
  return QueueStatusResult.OPEN_WITHOUT_AGENTS
26
34
  }
27
35
  return data.open ? QueueStatusResult.OPEN : QueueStatusResult.CLOSED
28
36
  }
29
37
 
30
- interface AvailabilityData {
38
+ export class HubtypeQueuesApi {
39
+ public queueId: string
40
+ public checkAvailableAgents: boolean
41
+
42
+ constructor(queueId: string, checkAvailableAgents?: boolean) {
43
+ this.queueId = queueId
44
+ this.checkAvailableAgents = checkAvailableAgents || false
45
+ }
46
+
47
+ async getAvailability(): Promise<AvailabilityData> {
48
+ const response = await axios.get(
49
+ `${HUBTYPE_API_URL}/public/v1/queues/${this.queueId}/availability/`,
50
+ // TODO: Make it configurable in the future
51
+ {
52
+ params: {
53
+ check_queue_schedule: true,
54
+ check_waiting_cases: false,
55
+ check_available_agents: this.checkAvailableAgents,
56
+ },
57
+ }
58
+ )
59
+ return response.data
60
+ }
61
+ }
62
+
63
+ export interface AvailabilityData {
31
64
  available: boolean
32
65
  waiting_cases: number
33
66
  availability_threshold_waiting_cases: number
@@ -40,16 +73,6 @@ export async function getQueueAvailability(
40
73
  queueId: string,
41
74
  checkAvailableAgents = false
42
75
  ): Promise<AvailabilityData> {
43
- const response = await axios.get(
44
- `${HUBTYPE_API_URL}/public/v1/queues/${queueId}/availability/`,
45
- // TODO: Make it configurable in the future
46
- {
47
- params: {
48
- check_queue_schedule: true,
49
- check_waiting_cases: false,
50
- check_available_agents: checkAvailableAgents,
51
- },
52
- }
53
- )
54
- return response.data
76
+ const queuesApi = new HubtypeQueuesApi(queueId, checkAvailableAgents)
77
+ return await queuesApi.getAvailability()
55
78
  }
package/src/index.ts CHANGED
@@ -1,9 +1,10 @@
1
1
  import { Plugin, PluginPreRequest, Session } from '@botonic/core'
2
2
  import { ActionRequest } from '@botonic/react'
3
+ import { v4 as uuid } from 'uuid'
3
4
 
4
5
  import { FlowBuilderApi } from './api'
5
6
  import {
6
- BOT_ACTION_PAYLOAD_SEPARATOR,
7
+ BOT_ACTION_PAYLOAD_PREFIX,
7
8
  FLOW_BUILDER_API_URL_PROD,
8
9
  SEPARATOR,
9
10
  SOURCE_INFO_SEPARATOR,
@@ -29,6 +30,7 @@ import {
29
30
  HtNodeWithContentType,
30
31
  } from './content-fields/hubtype-fields'
31
32
  import { DEFAULT_FUNCTIONS } from './functions'
33
+ import { EventAction, getNodeEventArgs, trackEvent } from './tracking'
32
34
  import {
33
35
  BotonicPluginFlowBuilderOptions,
34
36
  FlowBuilderJSONVersion,
@@ -36,8 +38,8 @@ import {
36
38
  PayloadParamsBase,
37
39
  } from './types'
38
40
  import { getNodeByUserInput } from './user-input'
41
+ import { SmartIntentsInferenceConfig } from './user-input/smart-intent'
39
42
  import { resolveGetAccessToken } from './utils'
40
-
41
43
  export default class BotonicPluginFlowBuilder implements Plugin {
42
44
  public cmsApi: FlowBuilderApi
43
45
  private flowUrl: string
@@ -48,12 +50,13 @@ export default class BotonicPluginFlowBuilder implements Plugin {
48
50
  public getLocale: (session: Session) => string
49
51
  public trackEvent?: (
50
52
  request: ActionRequest,
51
- eventName: string,
53
+ eventAction: string,
52
54
  args?: Record<string, any>
53
55
  ) => Promise<void>
54
56
  public getKnowledgeBaseResponse?: (
55
57
  request: ActionRequest
56
58
  ) => Promise<KnowledgeBaseResponse>
59
+ public smartIntentsConfig: SmartIntentsInferenceConfig
57
60
 
58
61
  constructor(readonly options: BotonicPluginFlowBuilderOptions) {
59
62
  const apiUrl = options.apiUrl || FLOW_BUILDER_API_URL_PROD
@@ -64,6 +67,10 @@ export default class BotonicPluginFlowBuilder implements Plugin {
64
67
  this.getAccessToken = resolveGetAccessToken(options)
65
68
  this.trackEvent = options.trackEvent
66
69
  this.getKnowledgeBaseResponse = options.getKnowledgeBaseResponse
70
+ this.smartIntentsConfig = {
71
+ ...options?.smartIntentsConfig,
72
+ useLatest: jsonVersion === FlowBuilderJSONVersion.LATEST,
73
+ }
67
74
  const customFunctions = options.customFunctions || {}
68
75
  this.functions = { ...DEFAULT_FUNCTIONS, ...customFunctions }
69
76
  }
@@ -88,7 +95,8 @@ export default class BotonicPluginFlowBuilder implements Plugin {
88
95
  const nodeByUserInput = await getNodeByUserInput(
89
96
  this.cmsApi,
90
97
  resolvedLocale,
91
- request as unknown as ActionRequest
98
+ request as unknown as ActionRequest,
99
+ this.smartIntentsConfig
92
100
  )
93
101
  request.input.payload = this.cmsApi.getPayload(nodeByUserInput?.target)
94
102
  }
@@ -98,25 +106,22 @@ export default class BotonicPluginFlowBuilder implements Plugin {
98
106
 
99
107
  private updateRequestBeforeRoutes(request: PluginPreRequest) {
100
108
  if (request.input.payload) {
101
- request.input.payload = this.removeSourceSeparatorFromPayload(
102
- request.input.payload
103
- )
104
- request.input.payload = this.updateBotActionPayload(request.input.payload)
109
+ request.input.payload = this.removeSourceSufix(request.input.payload)
110
+
111
+ if (request.input.payload.startsWith(BOT_ACTION_PAYLOAD_PREFIX)) {
112
+ request.input.payload = this.replacePayload(request.input.payload)
113
+ }
105
114
  }
106
115
  }
107
116
 
108
- private removeSourceSeparatorFromPayload(payload: string): string {
109
- return payload?.split(SOURCE_INFO_SEPARATOR)[0]
117
+ private removeSourceSufix(payload: string): string {
118
+ return payload.split(SOURCE_INFO_SEPARATOR)[0]
110
119
  }
111
120
 
112
- private updateBotActionPayload(payload: string): string {
113
- if (payload.startsWith(BOT_ACTION_PAYLOAD_SEPARATOR)) {
114
- const botActionId = payload.split(SEPARATOR)[1]
115
- const botActionNode =
116
- this.cmsApi.getNodeById<HtBotActionNode>(botActionId)
117
- return this.cmsApi.createPayloadWithParams(botActionNode)
118
- }
119
- return payload
121
+ private replacePayload(payload: string): string {
122
+ const botActionId = payload.split(SEPARATOR)[1]
123
+ const botActionNode = this.cmsApi.getNodeById<HtBotActionNode>(botActionId)
124
+ return this.cmsApi.createPayloadWithParams(botActionNode)
120
125
  }
121
126
 
122
127
  async getContentsByContentID(
@@ -143,8 +148,19 @@ export default class BotonicPluginFlowBuilder implements Plugin {
143
148
  }
144
149
 
145
150
  async getStartContents(locale: string): Promise<FlowContent[]> {
151
+ const resolvedLocale = this.cmsApi.getResolvedLocale(locale)
146
152
  const startNode = this.cmsApi.getStartNode()
147
- return await this.getContentsByNode(startNode, locale)
153
+ this.currentRequest.session.flow_thread_id = uuid()
154
+ const eventArgs = getNodeEventArgs(
155
+ this.currentRequest as unknown as ActionRequest,
156
+ startNode
157
+ )
158
+ await trackEvent(
159
+ this.currentRequest as unknown as ActionRequest,
160
+ EventAction.flowNode,
161
+ eventArgs
162
+ )
163
+ return await this.getContentsByNode(startNode, resolvedLocale)
148
164
  }
149
165
 
150
166
  async getContentsByNode(
@@ -260,6 +276,10 @@ export default class BotonicPluginFlowBuilder implements Plugin {
260
276
  const payloadParams = JSON.parse(payload.split(SEPARATOR)[1] || '{}')
261
277
  return payloadParams
262
278
  }
279
+
280
+ getFlowName(flowId: string): string {
281
+ return this.cmsApi.getFlowName(flowId)
282
+ }
263
283
  }
264
284
 
265
285
  export * from './action'
package/src/tracking.ts CHANGED
@@ -1,32 +1,43 @@
1
1
  import { ActionRequest } from '@botonic/react'
2
2
 
3
+ import { HtNodeWithContent } from './content-fields/hubtype-fields'
3
4
  import { getFlowBuilderPlugin } from './helpers'
4
5
 
5
6
  export async function trackEvent(
6
7
  request: ActionRequest,
7
- eventName: EventName,
8
+ eventAction: EventAction,
8
9
  args?: Record<string, any>
9
10
  ): Promise<void> {
10
11
  const flowBuilderPlugin = getFlowBuilderPlugin(request.plugins)
11
12
  if (flowBuilderPlugin.trackEvent) {
12
- await flowBuilderPlugin.trackEvent(request, eventName, args)
13
+ await flowBuilderPlugin.trackEvent(request, eventAction, args)
13
14
  }
14
15
  return
15
16
  }
16
17
 
17
- export enum EventName {
18
- botAgentRating = 'bot_agent_rating',
19
- botChannelRating = 'bot_channel_rating',
20
- botFaqUseful = 'bot_faq_useful',
21
- botRating = 'bot_rating',
22
- botFaq = 'bot_faq',
23
- botStart = 'bot_start',
24
- botOpen = 'bot_open',
25
- botAiModel = 'bot_ai_model',
26
- botAiKnowledgeBase = 'bot_ai_knowledge_base',
27
- botKeywordsModel = 'bot_keywords_model',
28
- fallback = 'fallback',
18
+ export function getNodeEventArgs(
19
+ request: ActionRequest,
20
+ node: HtNodeWithContent
21
+ ) {
22
+ const flowBuilderPlugin = getFlowBuilderPlugin(request.plugins)
23
+ return {
24
+ flowThreadId: request.session.flow_thread_id,
25
+ flowId: node.flow_id,
26
+ flowName: flowBuilderPlugin.getFlowName(node.flow_id),
27
+ flowNodeId: node.id,
28
+ flowNodeContentId: node.code,
29
+ flowNodeIsMeaningful: undefined, //node?.isMeaningful,
30
+ }
31
+ }
32
+
33
+ export enum EventAction {
34
+ flowNode = 'flow_node',
29
35
  handoffOption = 'handoff_option',
30
36
  handoffSuccess = 'handoff_success',
31
37
  handoffFail = 'handoff_fail',
38
+ keyword = 'nlu_keyword',
39
+ intent = 'nlu_intent',
40
+ intentSmart = 'nlu_intent_smart',
41
+ knowledgebase = 'knowledgebase',
42
+ fallback = 'fallback',
32
43
  }
package/src/types.ts CHANGED
@@ -12,12 +12,13 @@ export interface BotonicPluginFlowBuilderOptions {
12
12
  getAccessToken: () => string
13
13
  trackEvent?: (
14
14
  request: ActionRequest,
15
- eventName: string,
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
  }
@@ -7,24 +7,30 @@ import {
7
7
  HtSmartIntentNode,
8
8
  } from '../content-fields/hubtype-fields'
9
9
  import { getIntentNodeByInput } from './intent'
10
- import { getKeywordNodeByInput } from './keyword'
11
- import { getSmartIntentNodeByInput } from './smart-intent'
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 keywordNode = await getKeywordNodeByInput(
20
+ const keywordMatcher = new KeywordMatcher({
20
21
  cmsApi,
21
22
  locale,
22
23
  request,
23
- request.input.data
24
- )
24
+ })
25
+ const keywordNode = await keywordMatcher.getNodeByInput(request.input.data)
25
26
  if (keywordNode) return keywordNode
26
27
 
27
- const smartIntentNode = await getSmartIntentNodeByInput(cmsApi, request)
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)
@@ -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 { EventName, trackEvent } from '../tracking'
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
- const eventArgs = {
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,