Chatbot Generation

Enter URL and get Reference Code
To generate a chatbot, the user will need to input an NHS trust's website URL and get the Reference Code. This process is handled by the UrlInput component, and the most essential part of this component is the handleUrlSubmit function. This function posts the URL string from the user input to the backend to register the URL in the database. After the URL successfully registered, it will post both the URL string and the reference code to the backend to start the chatbot generation. In the end, a notification will appear to let the user know if the generation is successful.

const handleUrlSubmit = async () => {
    if(urlRef.current.value !== "" && urlRef.current.value !== undefined) {
        let referenceNum = await register_instance.post("", {
            "url": urlRef.current.value
        }).catch((err) => {
            …
        })  // use .data to fetch information in the axios promise
        if (referenceNum) {
            handleClickOpenSubmitNotif(referenceNum)
            …
            generate_instance.post("", {
                "url": urlRef.current.value,
                "ref": referenceNum.data
            }).then(res => {
                console.log("json generated")
                …
            }).catch((err) => {
                console.log(err.message) 
                …
            })
            …
        } else {
            …
            console.log("submit failed")
        }
        …
    } else {
        …
    }
}

Use Reference Code to get the chatbot file
To get the chatbot file, the user needs to input a reference code got at the beginning of the chatbot generation. The handleRefCodeSubmit function in the DialogSearch component will handle the submission and send the reference code to the backend to let the backend find the corresponding chatbot JSON file. After getting the chatbot file, it will show the file in the text field and allow the user to download or preview the chatbot.

const handleRefCodeSubmit = async () => {
    try {
        let answer = await instance.get("get_json/?id=" + dialogRef.current.value)
        console.log(answer.data)
        let result = answer.data
        if(Object.keys(result).length === 0) {
            setHasError(true)
            …
        } else {
            setDialogJson(JSON.stringify(answer.data))
            setAllowPreview(true)
            setAllowDownloadAndCopy(true)
            setHasError(false)
            setHelperText("")
        }
    } catch (e) {
        …
    }
}

Download the chatbot file
The handleDownload function in the DialogSearch component will handle the chatbot JSON file download. It will convert the chatbot file from string form to JSON form first and then generate a download link that will start the download automatically.

const handleDownload = () => {
    const jsonString = `data:text/json;chatset=utf-8,${encodeURIComponent(
        dialogJson
    )}`;
    const link = document.createElement("a");
    link.href = jsonString;
    link.download = "chatbot_dialog.json";

    link.click();
}

Chatbot Preview

Connection Process
To open the chatbot preview, the frontend will start with getting the token of accessing Watson Assistant API, the id of the Watson Assistant instance, and a new Watson Assistant workspace. When creating the new workspace, the existing ones will be deleted to avoid exceeding the workspace number limit. The connection process is handled by the handlePreview function in the DialogSearch component.

const handlePreview = async () => {
    try {
        …
        if(token === ""){
            token = (await getBearerToken(wtsnAssistant.iamKey))["access_token"]
            assert(token !== undefined, "failed to get token")
            setWtsnAssistant({…})
        }
        const workspaces = await getWorkspaceList(token, wtsnAssistant.instanceId)
        const workspaceIds = workspaces["workspaces"].map(ws => ws["workspace_id"])
        console.log(workspaceIds)
        for(let i = 0; i < workspaceIds.length; i++){
            await delWorkspaceById(token, wtsnAssistant.instanceId, workspaceIds[i])
        }
        const newWS = await newWorkspace(token, wtsnAssistant.instanceId, dialogJson)
        const newWSID = newWS["workspace_id"]
        console.log(newWSID)
        setWtsnAssistant({…})
        …
    } catch (e) {
        …
    }
}

The functions for connecting Watson Assistant API are imported from the watsonAssistantConnection.js in the CustomizedChatbot folder. Each of the functions will call an Azure Function. For example the following is the sendMsgToWatsonAssistant function and corresponding Azure Function sendMsgToWtsnAssistant.

sendMsgToWatsonAssistant

export const sendMsgToWatsonAssistant = async (instanceId, workspaceId, token, message) => {
    const options = {
        method: 'POST',
        headers: {'Content-Type': 'application/json'},
        body: `{"token":"${token}","msg":"${message}","instanceId":"${instanceId}","workspaceId":"${workspaceId}"}`
    };

    return await fetch(bingApiConnections.sendMsgToWatsonAssistant, options)
        .then(response => response.json())
        .catch(err => console.error(err));
}

Azure Function sendMsgToWtsnAssistant
module.exports = async function (context, req) {
    const token = req.body.token
    const msg = req.body.msg
    const instanceId = req.body.instanceId
    const workspaceId = req.body.workspaceId

    const options = {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
            Authorization: `Bearer ${token}`
        },
        body: `{"input":{"text":"${msg}","spelling_suggestions":true,"spelling_auto_correct":true}}`
    };

    const baseUrl = "https://api.us-south.assistant.watson.cloud.ibm.com"
    const version = "2021-11-27"

    const res = await fetch(`${baseUrl}/instances/${instanceId}/v1/workspaces/${workspaceId}/message?version=${version}`, options)
        .then(response => response.json())

    context.res = {
        headers: {
            'Content-Type': 'application/json'
        },
        body: {
            "text": res["output"]["generic"][0]["text"] || "invaild"
        }
    };
}

Preview Chatbot (using React Chatbot Kit)

We use the Node.js library React Chatbot Kit to build the chatbot integrated into the NHS Chatbot Generation website for preview. All the preview-chatbot-related files are stored in the CustomizedChatbot folder, and CustomizedBotPreview is the React component for the preview chatbot.

To start chatting with the preview bot, the handleStartChatSession function will be executed first. It starts a session with Watson Assistant using the first workspace.

const handleStartChatSession = async () => {
    let accessToken
    try {
        setIsConnecting(true)
        accessToken = (await getBearerToken(wtsnAssistant.iamKey))["access_token"]

        assert(accessToken !== undefined && accessToken !== "", "failed to get token")
    } catch (e) {
        console.error("failed to get a token - " + accessToken)
        setIsConnecting(false)
    }
    let wsId
    try {
        // connect to the first workspace
        wsId = (await getWorkspaceList(accessToken, wtsnAssistant.instanceId))["workspaces"][0]["workspace_id"]
        console.log("workspace id:\n" + wsId)
        setShowBot(true)
        console.log("Watson Assistant session established")
    } catch (e) {
        console.error("failed to get the workspace id - " + wsId)
    } finally {
        setIsConnecting(false)
    }
    …
}

Then the MessageParser function will start listing the message input from user, and it will call different actions according to the message parse result. Here, as long as the message is not empty, it will call the handleMessage function from the ActionProvider.

const MessageParser = ({ children, actions }) => {
    const parse = (message) => {
        if(message !== ""){
            actions.handleMessage(message).catch(err => console.error((err)))
        }
    };
    …
};

The handleMessage function will send the message from the user to Watson Assistant and creates an answer message element from Watson Assistant's response.

const ActionProvider = ({ setState, children }) => {
    const handleMessage = async (userMsg) => {
        const resp = await sendMsgToWatsonAssistant(wtsnAssistant.instanceId, wtsnAssistant.workspaceId, wtsnAssistant.token, userMsg)
        console.log(resp)
        const botMessage = createCustomMessage("custom", 'htmlMsg', {
            payload: {
                message: resp !== undefined ? resp["text"] : "ERROR"
            }
        })
        setState((prev) => ({
            ...prev,
            messages: [...prev.messages, botMessage],
        }));
    };
    …
};

Chatbot Generation History

A TableContainer from Material UI is used to display the history of chatbot generation. The refreshTable function uses content obtained from the MongoDB database to map the content of the TableContainer.

const refreshTable = () => {
    instance.get("")
        .then((res =>
                setRows(
                    res.data.map(row =>
                        createData(row["url"], row["dialog_ref"]["$oid"], row["_id"]["$oid"])
                    ).reverse()
                )
        )).catch((err) => {
            console.log("get list failed")
            console.log(err.message)
        })
}