While it is possible to create your very own Botium Connector for your own Conversational AI API from scratch, for low to medium complex APIs based on HTTP/JSON it is usually easier to leverage the included Generic HTTP(S)/JSON Connector and adapt it to your own needs.
Using the SIMPLEREST_ capabilities
You can use the exported capabilities prefixed with SIMPLEREST_ in your botium.json (Botium Core) or in the Botium Connector Settings screen:

Configuring a Botium Connector this way has some drawbacks:
-
For accessing multiple endpoints of your API (Dev/Test/Prod) you have to duplicate all of the connector configuration settings
-
When extending behaviour with Javascript code, there is no source code management, no syntax highlighting, no other developer tools - just a plain field holding Javascript code (like back in the 80s)
-
Hard to handover Botium Connector logic to other developers
-
Users can “change” (=break) the Botium Connector logic
Extend Generic HTTP/JSON Connector
The better option is to develop your own Botium Connector based on the Generic HTTP/JSON Connector. All that is needed is one single Javascript file. This approach solves most of the issues described before:
-
The Javascript file can be added to source control and edited with developer tool
-
It exposes the configurable parts of your API to Botium as your custom capabilities
-
Can be used as any other connector in Botium Core and Botium
Boilerplate Code
Here is some boilerplate code you can use to build your own Botium Connector. The only function that contains real logic is the Validate function, which is used to dynamically generate the configuration for the Generic HTTP/JSON Connector, tailored to your custom API:
const SimpleRestContainer = require('botium-core/src/containers/plugins/SimpleRestContainer.js')
const CoreCapabilities = require('botium-core/src/Capabilities')
const Capabilities = {
MYAPI_URL: 'MYAPI_URL',
MYAPI_TOKEN: 'MYAPI_TOKEN'
}
class BotiumConnectorMyApi {
constructor ({ queueBotSays, caps }) {
this.queueBotSays = queueBotSays
this.caps = caps
this.delegateContainer = null
this.delegateCaps = null
this.userSaysCounter = 0
}
Validate () {
if (!this.caps[Capabilities.MYAPI_URL]) throw new Error('MYAPI_URL capability required')
if (!this.delegateContainer) {
this.delegateCaps = {
[CoreCapabilities.SIMPLEREST_URL]: this.caps[Capabilities.MYAPI_URL],
[CoreCapabilities.SIMPLEREST_METHOD]: 'POST',
[CoreCapabilities.SIMPLEREST_RESPONSE_JSONPATH]: '$.reply',
[CoreCapabilities.SIMPLEREST_BODY_TEMPLATE]: JSON.stringify({
username: 'botium',
message: '{{msg.messageText}}',
session: '{{botium.conversationId}}',
startsession: false,
quickreply: null
})
}
if (this.caps[Capabilities.MYAPI_TOKEN]) {
this.delegateCaps[CoreCapabilities.SIMPLEREST_HEADERS_TEMPLATE] = `{ "Authorization": "Token ${this.caps[Capabilities.MYAPI_TOKEN]}"}`
}
this.delegateCaps[CoreCapabilities.SIMPLEREST_REQUEST_HOOK] = ({ msg, requestOptions, context }) => {
if (this.userSaysCounter === 0) {
requestOptions.body.startsession = true
} else {
requestOptions.body.startsession = false
}
if (msg.buttons && msg.buttons.length > 0) {
delete requestOptions.body.message
requestOptions.body.quickreply = msg.buttons[0].payload || msg.buttons[0].text
}
}
this.delegateCaps[CoreCapabilities.SIMPLEREST_RESPONSE_HOOK] = ({ botMsg, botMsgRoot }) => {
if (botMsgRoot.status === 'error') throw new Error(`MyAPI Error: ${botMsgRoot.message}`)
if (botMsgRoot.quickreplies && botMsgRoot.quickreplies.length > 0) {
botMsg.buttons = botMsgRoot.quickreplies.map(b => ({ text: b.title, payload: b.value }))
}
}
this.delegateCaps = Object.assign({}, this.caps, this.delegateCaps)
this.delegateContainer = new SimpleRestContainer({ queueBotSays: this.queueBotSays, caps: this.delegateCaps })
}
return this.delegateContainer.Validate()
}
async Build () {
await this.delegateContainer.Build()
}
async Start () {
this.userSaysCounter = 0
await this.delegateContainer.Start()
}
async UserSays (msg) {
await this.delegateContainer.UserSays(msg)
this.userSaysCounter++
}
async Stop () {
await this.delegateContainer.Stop()
}
async Clean () {
await this.delegateContainer.Clean()
}
}
module.exports = {
PluginVersion: 1,
PluginClass: BotiumConnectorMyApi,
PluginDesc: {
name: 'My API',
provider: 'Me',
capabilities: [
{
name: 'MYAPI_URL',
label: 'MyAPI Endpoint',
type: 'url',
required: true
},
{
name: 'MYAPI_TOKEN',
label: 'MyAPI Authorization Token',
type: 'secret',
required: false
}
]
}
}
You can find this code in the Botium Core repository on Github.
Code Walkthrough
The custom API in this case is a simple HTTP/JSON endpoint which receives a flat JSON document and responds with a flat JSON document.
-
The endpoint is given by the MYAPI_URL capability
-
The SIMPLEREST_BODY_TEMPLATE capability is used for building the main structure of the JSON document sent to the endpoint
-
The SIMPLEREST_REQUEST_HOOK capability is a function doing some additional stuff on the JSON document:
-
If it is the first message sent by the user, a startsession flag is activated
-
If there are button clicks (quick replies) to simulate instead of message text the quickreply-payload is sent
-
-
If an authorization token is given in the MYAPI_TOKEN capability, it is added as a HTTP header
When receiving the response from the endpoint, the JSON response document is evaluated:
-
The SIMPLEREST_RESPONSE_JSONPATH capability extracts the response text from the JSON response document
-
The SIMPLEREST_RESPONSE_HOOK capability is a function for dynamic evaluation of the JSON response document:
-
Detect if there is a processing error
-
Detect additional fields in the JSON response document, in this case if there are quickreply buttons available
-
For detailed documentation of the available SIMPLEREST_ capabilities see here: Generic HTTP(S)/JSON Connector
Configuration Options (Capabilities)
The boilerplate code also contains a plugin descriptor holding the name of the connector as well as the capabilities metadata. This is not only for documentation, but Botium will use this to show a nice configuration screen (see below).
Comments
0 comments
Please sign in to leave a comment.