Skip to Content
Technical Articles

Build On-Premise ChatBot App with SAP Cloud Foundry and Conversational AI

In this blog, I would like share how to build a simple chatbot running on-premise with SAP Cloud Foundry and integrated with SAP Conversational AI Natural Language Processing (NLP) component. The bot will answer user’s query on the NCT (ClinicalTrials.gov identifier number) title and status. User can say:

nct title for NCT00269126

status for NCT00269126

And the bot will response with the corresponding title and status for that NCT number.

The reason that this chatbot app running on-premise is because we need to integrate with any on-premise back end system. I find it easier to put the chatbot app on-premise in terms of security, privacy, feature updates and integration with back end system.

Here is the high level diagram to illustrate what we are going to build.

Basically, we need the following components to make it work:

  • SAP Cloud Platform: Cloud Foundry subaccount
  • SAP Conversational AI
  • SAP Cloud Connector & NodeJS app installed on local machine

Make sure you have the account for those SAP components before you continue.

Let’s go through the following steps to build one.

NodeJS Bot App

  • Create NodeJS Bot app on your local machine: appbot.js.
    Take note the bot is running on port 8081. We will configure this port in Cloud Connector and Cloud Foundry later on.
const express = require('express')
const bodyParser = require('body-parser')
const request = require('request');
const parser = require('xml2json');

const app = express()
const port = 8081
app.use(bodyParser.json())

app.post('/', (req, res) => {
   // console.log(req.body)

    let query = req.body.nlp.entities.query[0].value;
    let nctid = req.body.nlp.entities.nctid[0].value;
	let msg = '';

    if (typeof nctid != 'undefined') {
        getNCTID(nctid, function(returnValue) {
            //console.log(returnValue);

            try {
                let json = parser.toJson(returnValue);
                json = JSON.parse(json);

                if (query == 'title')
                    msg = 'Title is ' + json.clinical_study.brief_title;
                else
                    msg = 'Status is ' + json.clinical_study.overall_status;

                res.send({
                    replies: [{
                        type: 'text',
                        content: msg,
                    }],
                    conversation: {
                        memory: {
                            key: msg
                        }
                    }
                })
            } catch (e) {
                console.log(e);
                msg =  'No NCTID ' + nctid;

				res.send({
                    replies: [{
                        type: 'text',
                        content: msg,
                    }],
                    conversation: {
                        memory: {
                            key: msg
                        }
                    }
                })

            }
        });
    }
})

app.post('/errors', (req, res) => {
    console.log(req.body)
    res.send()
})

app.listen(port, () => {
    console.log('Server is running on port ' + port)
})

function getNCTID(nctid, callback) {

    let url = "https://clinicaltrials.gov/ct2/show/" + nctid + "?displayxml=true";

    let options = {
        url: url,
        method: 'GET'
    }

	let requestWithEncoding = function(options, callback) {
        let req = request.get(options);

        req.on('response', function(res) {
            let chunks = [];
            res.on('data', function(chunk) {
                chunks.push(chunk);
            });

            res.on('end', function() {
                let buffer = Buffer.concat(chunks);
                let encoding = res.headers['content-encoding'];
                if (encoding == 'gzip') {
                    zlib.gunzip(buffer, function(err, decoded) {
                        callback(err, decoded && decoded.toString());
                    });
                } else if (encoding == 'deflate') {
                    zlib.inflate(buffer, function(err, decoded) {
                        callback(err, decoded && decoded.toString());
                    })
                } else {
                    callback(null, buffer.toString());
                }
            });
        });

        req.on('error', function(err) {
            callback(err);
        });
    }


    requestWithEncoding(options, function(err, data) {
        if (err) {
            console.log('err:' + err);
            callback('error');
        } else
            //console.log(data);
            callback(data);
    })
}

Install the necessary components:

npm install express body-parser request xml2json

Run the bot from your local machine with this command:

node appbot.js

SAP Cloud Connector

Install the SAP Cloud Connector on your local machine and configure it.

Logon to https://<SAP_Cloud_Connector_IP_Addr>:8443/ and add subaccount of your Cloud Foundry.

 

Fill in the information:

  • Back-end Type: Non-SAP System
  • Protocol: HTTP
  • Internal Host is the IP of your local machine where the bot is running
  • Internal Port: 8081 (our NodeJS bot is running on this port – feel free to change it if this port is not available)
  • Principal Type: None

Make sure the host status is reachable and also add the resource “/”

That’s all for the SAP Cloud Connector setup.

SAP Cloud Foundry

Step 1 – Create a destination

  • Navigate to your Cloud Foundry sub-account
  • Choose Connectivity > Destinations > New Destination

  • Enter the following information.

  • Name: nodeBackend1 (this will be used as a destination when we configure the application router later on step 5)
    Type: HTTP
    URL: is the URL with http:// to your local machine with port 8081 (example: http://19.42.133.112:8081)
    Proxy Type: OnPremise
    Authentication: NoAuthentication

Step 2 – Create a destination instance

  • Navigate to space dev of your trial account

  • Choose Services > Service > Marketplace. Filter for dest and choose destination.

  • On screen Service: destination – Instances, click New Instance.

  • Choose lite as service plan and click Next.

  • On the next screen, click Next.

  • We will specify the required service in the manifest file to create such binding. Click Next to continue.

  • On the next screen, enter destinationnode-demo-lite as Instance Name and click Finish.

  • We created the destination instance.

Step 3 – Create a Connectivity Instance

  • Go back to your dev space again and navigate to Service Marketplace. Filter by conn and choose connectivity.

  • On screen Service: connectivity – Instances,click New Instance.

  • Choose lite as a service plan and click Next.

  • On the next screen choose Next.

  • We will specify the required service in the manifest file to create such binding. Click Next to continue.

  • On the next screen, use connectivity-demo-lite as Instance Name and click Finish.

  • The connectivity instance is created.

Step 4 – Create an XSUAA Instance

  • Go to your dev space and navigate to Service Marketplace. Filter by Auth and choose Authorization & Trust Management.

  • On screen Service: Authorization & Trust Management – Instances choose New Instances.

  • Choose application as service plan and choose Next.

  • Provide the following parameter and click Next.
{
	"xsappname": "appnode-demo",
	"tenant-mode": "dedicated",
	"description": "Security profile of called application",
	"scopes": [
		{
			"name": "uaa.user",
			"description": "UAA"
		}
	],
	"role-templates": [
		{
			"name": "Token_Exchange",
			"description": "UAA",
			"scope-references": [
				"uaa.user"
			]
		}
	]
} 

  • We will specify the required service in the manifest file to create such binding. Click Next to continue.

  • On the next screen, use xsuaanode-demo as Instance Name and click Finish.

  • We created the XSUAA instance.

  • You should now see the following services instances:

Step 5 – Create & configure the app router

We will create an application router NodeJS that is installed using SAP’s NPM registry https://npm.sap.com. The information about this registry is in npm startup file .npmrc.

  • Create a new folder called appnode on your local machine.
  • Create a new file package.json in this folder with this content:
{
  "name": "appnode-demo",
  "version": "1.0.0",
  "description": "",
  "scripts": {
    "start": "node node_modules/@sap/approuter/approuter.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "license": "ISC",
  "dependencies": {
    "@sap/approuter": "^5.3.0"
  }
}
  • Create a new file .npmrc in the same folder with this content:
@sap:registry=https://npm.sap.com/
  • Create a new file xs-app.json in the same folder with the following content. The destination nodeBackend1 is the one we created on step 1 earlier.
    We will set the authentication type to basic for webhook call purpose on SAP Conversational AI.
{
  "authenticationMethod": "route",
  "routes": [
    {
      "source": "/",
      "target": "/",
      "destination": "nodeBackend1",
      "csrfProtection": false,
      "authenticationType": "basic"
    }
  ]
}

Step 6 – Deploy the app router

  • Create a zip file appnode.zip which contains all files you created earlier.

  • Create a file manifest.yml for the appnode. We define the required services xsuaanode-democonnectivity-demo-lite and destinationnode-demo-lite in order to create the binding between the app router and these services during deployment.
---
applications:

- name: appnode-demo
  host: appnode-demo-<unique ID>
  buildpack: https://github.com/cloudfoundry/nodejs-buildpack.git
  memory: 128M
  services:
    - xsuaanode-demo
    - connectivity-demo-lite
    - destinationnode-demo-lite
  • Replace unique-ID of the host property with your subaccount id.
  • Navigate to dev space and choose Applications > Deploy Application.

  • Browse to zip file appnode.zip and to manifest.yml and click Deploy.

  • Once you deployed, SAP Cloud Platform will try to start the app router and check the Requested State status, it should turn green if no error.

Step 7  – Run the app router

  • On your dev space, navigate to Applications > appnode-demo.

  • Take note the link of the app route: https://appnode-demo-<unique-ID>.cfapps.eu10.hana.ondemand.com/. We will use this link in SAP Conversational AI webhook setup later on.

That’s all for the SAP Cloud Foundry setup.

SAP Conversational AI

  • Logon to SAP Conversational AI and create a new bot called nct.

  • Once you created you will see five tabs: Train, Build, Code, Connect and Monitor.

Train Tab

  • Navigate to Train tab, create a new intent with the same name nct as well and click Create Intent. You can define any name you like.

  • Create a custom entity query, select Free entitiy and click Create. Also create another entitiy called NCTID.

  • In the end, we will have two entities created: query and NCTID.

  • Navigate back to intent and add an expression: nct title for nct00269126. You can put any identifier for nct number (nct00269126).

    Also add another expression status for nct00269126.  We will tell the bot to recognize these two intents and response back accordingly.Add a few more examples for the bot to recognize the pattern better.

  • Map title to query entity and NCT number to NCTID entity. We will use these entities in the NodeJS bot logic.

Build Tab

  • Navigate to Build tab and click Create skill. Enter skill name nct, select skill type Business and click Create Skill.

  • Select nct skill we created.

  • Navigate to Triggers tab, enter nct entitiy and click Save.

  • Navigate to Action tab and click Add New Message Group.

  • Click Add Condition to trigger messagesSelect entity nct and click Save.

  • Click Call Webhook.

  • Select Post request, enter the app url (https://appnode-demo-<unique-ID>.cfapps.eu10.hana.ondemand.com/) we created earlier in SAP Cloud Foundry.Select Basic authentication for authentication type and enter your Cloud Foundry username and password. Click Save.

  • Under Body tab, use standard default body. We will use intents tag as I highlighted in screenshot below.

  • Let’s take a quick look on the NodeJS bot app. On the Post function, we get the query and nctid value from the request body > nlp > entities in JSON structure below.Once we got it, we call getNCTID function to retrieve the information on the title and status and response back to user.

app.post('/', (req, res) => {
   // console.log(req.body)

    let query = req.body.nlp.entities.query[0].value;
    let nctid = req.body.nlp.entities.nctid[0].value;
	let msg = '';

    if (typeof nctid != 'undefined') {
        getNCTID(nctid, function(returnValue) {
            //console.log(returnValue);

            try {
                let json = parser.toJson(returnValue);
                json = JSON.parse(json);

                if (query == 'title')
                    msg = 'Title is ' + json.clinical_study.brief_title;
                else
                    msg = 'Status is ' + json.clinical_study.overall_status;

                res.send({
                    replies: [{
                        type: 'text',
                        content: msg,
                    }],
                    conversation: {
                        memory: {
                            key: msg
                        }
                    }
                })
            } catch (e) {
                console.log(e);
                msg =  'No NCTID ' + nctid;

				res.send({
                    replies: [{
                        type: 'text',
                        content: msg,
                    }],
                    conversation: {
                        memory: {
                            key: msg
                        }
                    }
                })

            }
        });
    }
})

 

That’s all the required steps that we need to do and we can run the bot for testing now. Thanks.

 

Reference:

https://developers.sap.com/india/tutorials/cp-connectivity-consume-odata-service-approuter.html

 

 

4 Comments
You must be Logged on to comment or reply to a post.
  • Hello Ferry,

    This is a very interesting and well documented in terms of end-to-end chatbot building. Thanks for sharing!

    You have put the App Router in place for Nodejs endpoint security, which is handled by XSUAA in CF. And in your scenario chatbot interface is outside the CF and it will also be secure with any channel with any other protocols. 

    In my scenario, there is no SAP Cloud Platform – CF. And our back-end system is On-Premise ECC (private cloud). So in this scenario, how we should proceed with Nodejs hosting and make the security intact.

    Any suggestion, much appreciated!

     

    -Partha

    • Hi Partha,

      I’ve worked on integrating ChatBot with ECC system. But have not used Node.js application. Reply back incase you have similar requirement.

       

      • Hi Kshitija Shetty,

        I have similar requirement. Could you please let me know how to integrate SAP CAI and ECC/Gateway on premise without SAP Cloud?

        Regards,

        Sathappan

  • Ferry Djaja thanks for youre Wonderful Tutorial,

    i have a Question, where are the Answerse, i mean in the code it s here:

    msg=’Title is ‘+json.clinical_study.brief_title;
    but what is this json.clinical_study.brief_title; and where !?
    thanks