Preserve Conversation To Cosmos DB Using Azure Bot Framework SDK V4

In this article, we will learn how to store the conversation to Azure Cosmos DB with Bot Framework SDK v4.

The Bot Framework SDK supports the preservation of the user input as it is received. Here, we will store each transaction into the memory object. The same object will be saved to the storage at the end. 
 
In this tutorial, you will learn the following.
  1. Setup a Cosmos DB.
  2. Create a basic bot.
  3. Setup configuration information to the Azure bot.
  4. Implement Cosmos DB storage to Azure bot.
  5. Build and test your bot with Emulator.
Below are the prerequisites.
 
The bot created in the previous article will be used here to help you understand how to build a basic bot.
 
Let's understand the information flow with respect to this article.
 
 
You can read and write directly to your storage object without using middleware or context object. This can be appropriate for data your bot uses to preserve a conversation or data that comes from a source outside your bot's conversation flow. In this data storage model, data is read directly from storage instead of using a state manager.
 
Step 1 - Set up Cosmos DB
  • Sign in to the Azure portal with your Azure credentials.
  • Click "Create a resource".
  • Click “Azure Cosmos DB".
  • Click Create a resource > Databases > Azure Cosmos DB
  • On the New account page, 

    • provide Subscription, 
    • Resource group information. 
    • Create a unique name for your Account Name field - this eventually becomes part of your data access URL name. 
    • For API, select Core(SQL), 
    • Provide a nearby Location to improve data access times.

  • Then click Review + Create.
  •  Once validation has been successful, click Create.
 
The account creation takes a few minutes and Your Azure Cosmos DB account created.
  • Search for setting
  • Click Settings
  • Click the New Container.
  • Add Container area is displayed on the far right.
  • Fill new database id i.e. bot-cosmos-sql-db
  • Container id i.e. bot-storage
  • Partition key i.e. /id
  • Click Ok to create.
 
Cosmos DB container created with name bot-cosmos-sql-db
 
 
Keys to access new database collection, "bot-cosmos-sql-db" with a container id of "bot-storage." 
 
 
Step 2 - Create a basic bot
  • Browse Visual Studio 2017.
  • Select Bot Framework.
  • Select Echo Bot (Bot Framework V4).
  • Give the appropriate name and click ok to proceed.
 
Sample Echo Bot project will be created with basic functionality.
 
Step 3 - Setup configuration information to the Azure bot
  •  Firstly, install the NuGet package solution.
  • Add Cosmos DB configuration to Visual Studio solution. 
  1. private const string CosmosServiceEndpoint = "https://bot-cosmos-sql-db.documents.azure.com:443/";  
  2.         private const string CosmosDBKey = "vr1psMExluqGtp45XbIu4q1w2gmwfFksLr8GX4TDE5AkdRBiLE1fGmPsEoVpDXrM6qtOfLEGS25SzCO9G5XORg==";  
  3.         private const string CosmosDBDatabaseName = "bot-cosmos-sql-db";  
  4.         private const string CosmosDBCollectionName = "bot-storage";  
  5.   
  6.         // Create Cosmos DB  Storage.  
  7.         private static readonly CosmosDbStorage query = new CosmosDbStorage(new CosmosDbStorageOptions  
  8.         {  
  9.             AuthKey = CosmosDBKey,  
  10.             CollectionId = CosmosDBCollectionName,  
  11.             CosmosDBEndpoint = new Uri(CosmosServiceEndpoint),  
  12.             DatabaseId = CosmosDBDatabaseName,  
  13.         });  
 
Step 4 - Implement Cosmos DB storage to azure bot
 
To preserve conversation to Azure Cosmos DB, I am going to create an object which will hold properties of the same conversation, it will concatenate and store all at one instance. To get started, Create one class with basic properties. 
  • Create UtteranceLog.cs file and below properties 
  1. public class UtteranceLog : IStoreItem  
  2.    {  
  3.        // A list of things that users have said to the bot  
  4.        public List<string> UtteranceList { get; } = new List<string>();  
  5.   
  6.        // The number of conversational turns that have occurred          
  7.        public int TurnNumber { get; set; } = 0;  
  8.   
  9.        // Create concurrency control where this is used.  
  10.        public string ETag { get; set; } = "*";  
  11.    }  
It's just a screen shot of above code placement.

 
  • Replace OnMessageActivityAsyn method with below code section
  1. protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)  
  2.       {  
  3.           // preserve user input.  
  4.           var utterance = turnContext.Activity.Text;  
  5.           // make empty local logitems list.  
  6.           UtteranceLog logItems = null;  
  7.   
  8.           // see if there are previous messages saved in storage.  
  9.           try  
  10.           {  
  11.               string[] utteranceList = { "UtteranceLog" };  
  12.               logItems = query.ReadAsync<UtteranceLog>(utteranceList).Result?.FirstOrDefault().Value;  
  13.           }  
  14.           catch  
  15.           {  
  16.               // Inform the user an error occured.  
  17.               await turnContext.SendActivityAsync("Sorry, something went wrong reading your stored messages!");  
  18.           }  
  19.   
  20.           // If no stored messages were found, create and store a new entry.  
  21.           if (logItems is null)  
  22.           {  
  23.               // add the current utterance to a new object.  
  24.               logItems = new UtteranceLog();  
  25.               logItems.UtteranceList.Add(utterance);  
  26.               // set initial turn counter to 1.  
  27.               logItems.TurnNumber++;  
  28.   
  29.               // Show user new user message.  
  30.               await turnContext.SendActivityAsync($"Echo" + turnContext.Activity.Text);  
  31.   
  32.               // Create Dictionary object to hold received user messages.  
  33.               var changes = new Dictionary<string, object>();  
  34.               {  
  35.                   changes.Add("UtteranceLog", logItems);  
  36.               }  
  37.               try  
  38.               {  
  39.                   // Save the user message to your Storage.  
  40.                   await query.WriteAsync(changes, cancellationToken);  
  41.               }  
  42.               catch  
  43.               {  
  44.                   // Inform the user an error occured.  
  45.                   await turnContext.SendActivityAsync("Sorry, something went wrong storing your message!");  
  46.               }  
  47.           }  
  48.           // Else, our Storage already contained saved user messages, add new one to the list.  
  49.           else  
  50.           {  
  51.               // add new message to list of messages to display.  
  52.               logItems.UtteranceList.Add(utterance);  
  53.               // increment turn counter.  
  54.               logItems.TurnNumber++;  
  55.   
  56.               // show user new list of saved messages.  
  57.               await turnContext.SendActivityAsync($"Echo " + turnContext.Activity.Text);  
  58.   
  59.               // Create Dictionary object to hold new list of messages.  
  60.               var changes = new Dictionary<string, object>();  
  61.               {  
  62.                   changes.Add("UtteranceLog", logItems);  
  63.               };  
  64.   
  65.               try  
  66.               {  
  67.                   // Save new list to your Storage.  
  68.                   await query.WriteAsync(changes, cancellationToken);  
  69.               }  
  70.               catch  
  71.               {  
  72.                   // Inform the user an error occured.  
  73.                   await turnContext.SendActivityAsync("Sorry, something went wrong storing your message!");  
  74.               }  
  75.           }  
  76.       }  
Step 5 - Build and Test your bot with Emulator.
  • Press F5 to run the Visual Studio solution locally.
  • Start the bot emulator and then connect to your bot in the emulator:
  • Click the Create new bot configuration link in the emulator "Welcome" tab.
  • Fill in fields to connect to your bot, given the information on the webpage displayed when you started your bot.
  • Interact with your bot
  • Send a message to your bot. The bot will list the messages it has received
 
OutPut
  • Navigate to Azure Cosmos DB instance and expand the items under bot-storage.
  • Select the utterances log,
  • All asked queries turn to storage.
 
The previous article can be found from these links.
I hope you have enjoyed and learned something new in this article. Thanks for reading and stay tuned for the next article.