Azure QnA bot in SPFX

In my earlier blog we saw how to create a QnA bot using  Microsoft Azure Cognitive service “QnA maker”, in continuation with that in this blog we will see how to integrate that QnA chat bot in our SharePoint online modern page.

What do you need?

  • Create a SPFX extension (application customizer)
  • Cognitive Services API Key and Knowledge Base ID

I have referred GitHub for more details get into it. Thanks for the team.

Step 1: Create a SPFX application customizer extension

As we want our chat bot to be available as a pop up at the bottom of the SharePoint modern page, we will create a SPFX application customizer extension

Step 2: Create Azure QnA connection file

Open the SharePoint extension project using ‘code .’ (your preferred IDE), navigate to project “src” folder and create a new folder “services” then create a new file named “cognitiveservices.ts”

Paste the below code in and save it.

import { HttpClient, IHttpClientOptions, HttpClientConfiguration } from "@microsoft/sp-http";
import { ApplicationCustomizerContext } from "@microsoft/sp-application-base";

export interface CognitiveServiceConfiguration {
    context: ApplicationCustomizerContext;
}

export class CognitiveService {
    private context: ApplicationCustomizerContext;
    private qnamakerEndpoint: string = "Update your Endpoint URL";
    private qnamakerEndpointKey: string = "Update your Key here";
    private knowledgebaseId: string = "Update your KB ID here";

    constructor(config: CognitiveServiceConfiguration) {
        this.context = config.context;
    }

    public async getQnaAnswer(userQuery: string): Promise<String> {
        let answer: string = 'Could not find the answer to your question... sorry!';
        // Build URI
        const postURL = this.qnamakerEndpoint + `/qnamaker/knowledgebases/${this.knowledgebaseId}/generateAnswer`;

        // Build body
        const body: string = JSON.stringify({
            'question': userQuery
        });

        // Build headers
        const requestHeaders: Headers = new Headers();
        requestHeaders.append('Content-type', 'application/json');
        requestHeaders.append('Authorization', this.qnamakerEndpointKey);

        const httpClientOptions: IHttpClientOptions = {
            body: body,
            headers: requestHeaders
        };

        let response = await this.context.httpClient.post(
            postURL,
            HttpClient.configurations.v1,
            httpClientOptions
        );

        if (response.ok) {
            let json = await response.json();
            if (json.answers[0].answer != 'No good match found in the KB')
                answer = json.answers[0].answer;
        }
        return answer;
    }
}

Step 3: Set title for Bot

First let us declare the title strings in “src\extensions\spfxQnA\loc\myStrings.d.ts” as below

declare interface ISpfxQnAApplicationCustomizerStrings {
  Title: string;
  ChatTitle: string;
  ChatSubtitle: string;
}

And then to assign values go to “src\extensions\spfxQnA\loc\en-us.js”, update the title of the bot as required

define([], function() {
  return {
    "Title": "SpfxQnAApplicationCustomizer",
    "ChatTitle": "SharePoint Doubts",
    "ChatSubtitle": "Chatbot using MS Azure cognitive QandA services"
  }
});

Step 4: Add chat widget

React chat widget gives nice chat experience so we will use that in our extension. To do that we need to install react chat widget using npm

npm install --save react-chat-widget
npm install @types/react-chat-widget

To accommodate the react chat widget into our project we will create 3 more files ‘FooterChat.module.scss’,’FooterChat.tsx’,’IFooterChatProps.ts’ in a new folder named ‘components’ under “src\extensions\spfxQnA”. Lets do one by now.

1st Create a props file ‘IFooterChatProps.ts’ and paste the below code to import the cognitive service connection files

import { CognitiveService } from "../../../services/cognitiveservices";

export interface IFooterChatProps{
    cognitiveService: CognitiveService;
}

export interface IFooterChatState{
    // Empty for now
}

2nd Create a css style file ‘FooterChat.module.scss’ under the “components” folder and paste this style which positions our chat DIV

.FooterChat{    
    .FooterChat>div{
        margin: 0 20px 60px 0 !important;
    }
}

3rd Create a typescript file named “FooterChat.tsx” to import our widget

import * as React from 'react';
import * as strings from 'SpfxQnAApplicationCustomizerStrings';
import styles from './FooterChat.module.scss';
import { IFooterChatProps, IFooterChatState } from './IFooterChatProps';
import { Widget, addResponseMessage } from 'react-chat-widget';
import 'react-chat-widget/lib/styles.css';

export default class FooterChat extends React.Component<IFooterChatProps, IFooterChatState> {
    constructor(props: IFooterChatProps, state: IFooterChatState) {
        super(props);

        this.state = {
            items: []
        };
    }
public componentDidMount():void {
        addResponseMessage("Welcome to this awesome chat!");
    }

    private _handleNewUserMessage = (newMessage) => {
        this.props.cognitiveService.getQnaAnswer(newMessage).then((answer) => {
            addResponseMessage(answer);
        });
    }

    public render() {
        return (
            <div className={styles.FooterChat}>
                <Widget
                    handleNewUserMessage={this._handleNewUserMessage}
                    title={strings.ChatTitle}
                    subtitle={strings.ChatSubtitle}
                />
            </div>
        );
    }
}

Step 5: Update extension placeholder + add widget

As we ready with our widget and cognitive services connection, lets import those files into our SPFX extension by adding the below import statements under the default imports in file “SpfxQnAApplicationCustomizer.ts”

import {
  BaseApplicationCustomizer,
  PlaceholderContent,
  PlaceholderName
} from '@microsoft/sp-application-base';


import * as React from 'react';
import * as ReactDom from 'react-dom';
import { IFooterChatProps } from './components/IFooterChatProps';
import FooterChat from './components/FooterChat';
import { CognitiveService } from '../../services/cognitiveservices';

To set context, add the customizer bottom placeholder and to add the widget, replace the default class with below code

private _bottomPlaceholder: PlaceholderContent | undefined;

    @override
    public onInit(): Promise<void> {
      // Call render method for generating the needed html elements
      this._renderFooter();
  
      return Promise.resolve();
    }
  
    private _renderFooter(): void {
      // Instantiate cognitive service
      const cognitiveService = new CognitiveService({
        context: this.context,
      });
  
      // Handling the bottom placeholder
      if (!this._bottomPlaceholder) {
        this._bottomPlaceholder =
          this.context.placeholderProvider.tryCreateContent(
            PlaceholderName.Bottom,
            { onDispose: this._onDispose });
  
        // The extension should not assume that the expected placeholder is available.
        if (!this._bottomPlaceholder) {
          console.error('The expected placeholder (Bottom) was not found.');
          return;
        }
  
        const element: React.ReactElement<IFooterChatProps> = React.createElement(
          FooterChat,
          {
            cognitiveService: cognitiveService
          });
  
        ReactDom.render(element, this._bottomPlaceholder.domElement);
      }
    }
  
    private _onDispose(): void {
      console.log('Disposed custom bottom placeholders.');
    }

Step 6: Update Azure service values

Almost ready, before build we need to update the actual key values from our azure subscription.

Edit “cognitiveservices.ts” file to set your QnA maker end point URL, Key and Knowledge Base ID

private qnamakerEnpoint: string = "Update your end point URL";
private qnamakerEndpointKey: string = "Update your Key here";
private knowledgebaseId: string = "Update KB ID here"; 

Where do I get them?

Go to https://www.qnamaker.ai/ login with your valid credentials and pick your created knowledge base

Then move on to the “Publish” page of the knowledge base and click “Publish” it takes less than a minute.

Step 7: Build and Test

Gulp trust-dev-cert
Gulp -serve
https://sharepointsamples.sharepoint.com/SitePages/Chat.aspx?debugManifestsFile=https%3A%2F%2Flocalhost%3A4321%2Ftemp%2Fmanifests.js&loadSPFX=true&customActions=%7B%22e7e36857-bc8c-409a-9808-5fca579abaa2%22%3A%7B%22location%22%3A%22ClientSideExtension.ApplicationCustomizer%22%2C%22properties%22%3A%7B%22testMessage%22%3A%22Test+message%22%7D%7D%7D

Excited to work with Azure cognitive services, amazing alternate for legacy FAQ & Knowledge base systems.Thanks for your time in reading my blog. Hope it was useful.

Happy learning. Sharing is Caring.