Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

plugin-flow-builder: add support for smart intent nodes #2803

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6,358 changes: 4,641 additions & 1,717 deletions package-lock.json

Large diffs are not rendered by default.

27 changes: 16 additions & 11 deletions packages/botonic-cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,16 @@ Build Chatbots Using React
[![License](https://img.shields.io/npm/l/@botonic/cli.svg)](https://github.com/hubtype/botonic/blob/master/package.json)

<!-- toc -->
* [@botonic/cli](#botoniccli)
* [Usage](#usage)
* [Commands](#commands)

- [@botonic/cli](#botoniccli)
- [Usage](#usage)
- [Commands](#commands)
<!-- tocstop -->

# Usage

<!-- usage -->

```sh-session
$ npm install -g @botonic/cli
$ botonic COMMAND
Expand All @@ -27,19 +29,21 @@ USAGE
$ botonic COMMAND
...
```

<!-- usagestop -->

# Commands

<!-- commands -->
* [`botonic deploy [PROVIDER]`](#botonic-deploy-provider)
* [`botonic destroy [PROVIDER]`](#botonic-destroy-provider)
* [`botonic help [COMMAND]`](#botonic-help-command)
* [`botonic login`](#botonic-login)
* [`botonic logout`](#botonic-logout)
* [`botonic new NAME [PROJECTNAME]`](#botonic-new-name-projectname)
* [`botonic serve`](#botonic-serve)
* [`botonic test`](#botonic-test)

- [`botonic deploy [PROVIDER]`](#botonic-deploy-provider)
- [`botonic destroy [PROVIDER]`](#botonic-destroy-provider)
- [`botonic help [COMMAND]`](#botonic-help-command)
- [`botonic login`](#botonic-login)
- [`botonic logout`](#botonic-logout)
- [`botonic new NAME [PROJECTNAME]`](#botonic-new-name-projectname)
- [`botonic serve`](#botonic-serve)
- [`botonic test`](#botonic-test)

## `botonic deploy [PROVIDER]`

Expand Down Expand Up @@ -192,4 +196,5 @@ EXAMPLE
```

_See code: [lib/commands/test.js](https://github.com/hubtype/botonic/blob/v0.25.0-beta.0/lib/commands/test.js)_

<!-- commandsstop -->
2 changes: 1 addition & 1 deletion packages/botonic-plugin-flow-builder/src/action/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { FlowContent, FlowHandoff } from '../content-fields'
import { HtNodeWithContent } from '../content-fields/hubtype-fields'
import { getFlowBuilderPlugin } from '../helpers'
import { createNodeFromKnowledgeBase } from './knowledge-bases'
import { EventName, trackEvent } from './tracking'
import { EventName, trackEvent } from '../tracking'

export type FlowBuilderActionProps = {
contents: FlowContent[]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
HtTextNode,
} from '../content-fields/hubtype-fields'
import { getFlowBuilderPlugin } from '../helpers'
import { EventName, trackEvent } from './tracking'
import { EventName, trackEvent } from '../tracking'

export async function createNodeFromKnowledgeBase(
cmsApi: FlowBuilderApi,
Expand Down Expand Up @@ -47,6 +47,7 @@ export async function createNodeFromKnowledgeBase(
buttons_style: undefined,
buttons: [],
},
flow_id: 'randomUUID', // TODO: Add flow_id consequentially with HtBaseNode changes
id: uuid(),
code: 'knowledge-response',
meta: {
Expand Down
7 changes: 7 additions & 0 deletions packages/botonic-plugin-flow-builder/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
HtNodeWithoutContentType,
HtPayloadNode,
} from './content-fields/hubtype-fields'
import { HtSmartIntentNode } from './content-fields/hubtype-fields/smart-intent'
import { FlowBuilderApiOptions } from './types'

export class FlowBuilderApi {
Expand Down Expand Up @@ -125,6 +126,12 @@ export class FlowBuilderApi {
return undefined
}

getSmartIntentNodes(): HtSmartIntentNode[] {
return this.flow.nodes.filter(
node => node.type === HtNodeWithContentType.SMART_INTENT
) as HtSmartIntentNode[]
}

private nodeContainsIntent(
node: HtIntentNode,
intent: string,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { HandOffBuilder } from '@botonic/core'
import { ActionRequest, WebchatSettings } from '@botonic/react'
import React from 'react'

import { EventName, trackEvent } from '../action/tracking'
import { EventName, trackEvent } from '../tracking'
import { FlowBuilderApi } from '../api'
import { getQueueAvailability } from '../functions/conditional-queue-status'
import { ContentFieldsBase } from './content-fields-base'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export interface HtBaseNode {
}
follow_up?: HtNodeLink
target?: HtNodeLink
flow_id: string // TODO: Review if this field is necessary in all HtBaseNode
}

export interface HtTextLocale {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export * from './keyword'
export * from './node-types'
export * from './nodes'
export * from './payload'
export * from './smart-intent'
export * from './text'
export * from './url'
export * from './video'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export enum HtNodeWithContentType {
TEXT = 'text',
KEYWORD = 'keyword',
INTENT = 'intent',
SMART_INTENT = 'smart-intent',
FUNCTION = 'function',
FALLBACK = 'fallback',
VIDEO = 'video',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { HtImageNode } from './image'
import { HtIntentNode } from './intent'
import { HtKeywordNode } from './keyword'
import { HtPayloadNode } from './payload'
import { HtSmartIntentNode } from './smart-intent'
import { HtTextNode } from './text'
import { HtUrlNode } from './url'
import { HtVideoNode } from './video'
Expand All @@ -24,6 +25,7 @@ export type HtNodeWithContent =
| HtFunctionNode
| HtFallbackNode
| HtWhatsappButtonListNode
| HtSmartIntentNode

export type HtNodeWithoutContent =
| HtUrlNode
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { HtBaseNode } from './common'
import { HtNodeWithContentType } from './node-types'

interface SmartIntent {
title: string
description: string
}

export interface HtSmartIntentNode extends HtBaseNode {
type: HtNodeWithContentType.SMART_INTENT
content: SmartIntent
}
2 changes: 1 addition & 1 deletion packages/botonic-plugin-flow-builder/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { Plugin, PluginPreRequest, Session } from '@botonic/core'
import { ActionRequest } from '@botonic/react'

import { getNodeByUserInput } from './action/user-input'
import { FlowBuilderApi } from './api'
import { SEPARATOR, SOURCE_INFO_SEPARATOR } from './constants'
import {
Expand All @@ -28,6 +27,7 @@ import {
KnowledgeBaseResponse,
PayloadParamsBase,
} from './types'
import { getNodeByUserInput } from './user-input'
import { resolveGetAccessToken } from './utils'

export default class BotonicPluginFlowBuilder implements Plugin {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ActionRequest } from '@botonic/react'

import { getFlowBuilderPlugin } from '../helpers'
import { getFlowBuilderPlugin } from './helpers'

export async function trackEvent(
request: ActionRequest,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,34 @@
import { ActionRequest } from '@botonic/react'

import { FlowBuilderApi } from '../api'
import { HtIntentNode, HtKeywordNode } from '../content-fields/hubtype-fields'
import {
HtIntentNode,
HtKeywordNode,
HtSmartIntentNode,
} from '../content-fields/hubtype-fields'
import { getIntentNodeByInput } from './intent'
import { getKeywordNodeByInput } from './keyword'
import { getSmartIntentNodeByInput } from './smart-intent'

export async function getNodeByUserInput(
cmsApi: FlowBuilderApi,
locale: string,
request: ActionRequest
): Promise<HtIntentNode | HtKeywordNode | undefined> {
): Promise<HtSmartIntentNode | HtIntentNode | HtKeywordNode | undefined> {
if (request.input.data) {
const intentNode = await getIntentNodeByInput(cmsApi, locale, request)
if (intentNode) return intentNode

const keywordNode = await getKeywordNodeByInput(
cmsApi,
locale,
request,
request.input.data
)
if (keywordNode) return keywordNode

const smartIntentNode = await getSmartIntentNodeByInput(cmsApi, request)
if (smartIntentNode) return smartIntentNode

const intentNode = await getIntentNodeByInput(cmsApi, locale, request)
if (intentNode) return intentNode
}
return undefined
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { ActionRequest } from '@botonic/react'

import { FlowBuilderApi } from '../api'
import { HtIntentNode } from '../content-fields/hubtype-fields'
import { EventName, trackEvent } from './tracking'
import { EventName, trackEvent } from '../tracking'

export async function getIntentNodeByInput(
cmsApi: FlowBuilderApi,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { ActionRequest } from '@botonic/react'

import { FlowBuilderApi } from '../api'
import { HtKeywordNode } from '../content-fields/hubtype-fields'
import { EventName, trackEvent } from './tracking'
import { EventName, trackEvent } from '../tracking'

export async function getKeywordNodeByInput(
cmsApi: FlowBuilderApi,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { ActionRequest } from '@botonic/react'
import axios from 'axios'

import { FlowBuilderApi } from '../api'
import { HtSmartIntentNode } from '../content-fields/hubtype-fields/smart-intent'

export async function getSmartIntentNodeByInput(
cmsApi: FlowBuilderApi,
request: ActionRequest
): Promise<HtSmartIntentNode | undefined> {
const smartIntentNodes = cmsApi.getSmartIntentNodes()
const intentsInferenceParams = smartIntentNodes.map(smartIntentNode => {
return {
name: smartIntentNode.content.title,
definition: smartIntentNode.content.description,
}
})
intentsInferenceParams.push({
name: 'Other',
definition: 'The text does not belong to any other intent.',
})
try {
const response = await axios({
method: 'POST',
url: `${process.env.HUBTYPE_API_URL}/external/v1/ai/smart_intents/inference/`,
headers: {
Authorization: `Bearer ${request.session._access_token}`,
'Content-Type': 'application/json',
},
data: { text: request.input.data, intents: intentsInferenceParams },
timeout: 10000,
})
console.log({ response })
return smartIntentNodes.find(
smartIntentNode =>
smartIntentNode.content.title === response.data.intent_name
)
} catch (e) {
console.error(e)
return undefined
}
}
Loading