One of the many new Bot Framework features we showcased at Build 2017 was Adaptive Cards. Modern ‘cards’ as a UI feature have existed for several years now, birthed by the advent of the modern web and mobile technologies. Most of us have interacted with cards on some level, whether or not we realize it - a card is a UI container for short, grouped or related pieces of information. Consider the following examples of everyday card use:
Cards are used everywhere! Currently, large and popular platforms like twitter and facebook provide templates for developers to create cards from. Using templates, large platforms like facebook can tightly control third party content to ensure that users get a consistent experience (i.e facebook always looks like facebook), and content authors only need to decorate the meta-data in their content once.
Current card limitations
While this current way of creating cards is simple and effective, there are certainly limitations to using templates.
- For the host application, let’s say facebook, every template must be designed, implemented and documented on every platform
- For the content author, you are limited to the types of content the templating will allow, and you have no ability to customize the templates
As an alternative to the rigid nature of the template model, on the other end of the spectrum we can create cards using HTML and CSS, which of course includes the following limitations as well:
- Performance and security costs with mixing UI stacks
- Difficult to enforce consistency, and restrict what card authors can do
- Content authors must design and implement every style of card across each platform
Introducing Adaptive Cards
So, what’s the deal with adaptive cards and why are they significant? Adaptive cards provides a nice middle-ground solution between using fixed templates and HTML.
They can also improve cards by adding the following features:
- card automatically adapts to host U/X and brand guidelines, using a json payload
- schema to layout content
- actions to show other cards, urls, etc
- input to gather info from users
- speech enabled from day one
You can click here to check out the in-depth presentation from Build 2017 where we introduced Adaptive cards and it’s key features.
Demo - adding Adaptive cards to a Bot
In this example, we’ll show you how to create an Adaptive Card to display weather information by using the LUIS Action Bindings Sample’s GetWeatherInPlace Action. We will walk through setting up the LUIS Model,creating the solution, and modifying the code to display a weather card using data retrieved from the APIXU weather service.
- Free luis.ai account
- Bot Framework Emulator
- Free APIXU account
- Visual Studio 2017 or 2015 Community (update all extensions)
LUIS model setup
1) Create a new LUIS app, naming it whatever you like.
2) Click on intents on the left menu to add an Intent named WeatherInPlace. This is the intent we’ll be using from the LUIS service to bind to an action defined in the client.
Add utterances for the intent, in our model we added:
"what is the weather like in hong kong" "what is the weather like in mexico city" "tell me the weather in jerusalem" "what is the weather in miami, fl" "what temperature is it in paris, france" "weather in dallas, texas" "what is the weather in seattle, wa"
3) Select entities from the left menu, and add the prebuilt geography entity to the model.
4) Create a custom entity with the name Place and for Entity type select Composite. Now click Add child and select geography from the dropdown.
5) Add a phrase list for weather synonyms. Try adding ““weather”, “forecast”, “temperature”, “warm”, and “rain”. This will enable asking more varied questions, such as: “what is the temperature in Miami, FL?” or “How warm is it in Philadelphia, PA?”
6) Finally, retrieve the LUIS subscription id which can be found under my keys in the top menu, and application id which can be found under then dashboard section of the left menu. You’ll need these keys to allow your Bot to access the LUIS service.
7) Now Train and Publish the model.
The exported model should be similar to something like this:
C# Solution Setup
The Bot Builder Samples and APIXU C# Library are available on GitHub.
1) Use git to pull the Bot Builder Samples
3) Open the LuisActionBinding solution from the \BotBuilder-Samples\CSharp\Blog-LUISActionBinding\ folder.
4) Remove the LuisActions.Samples.Console, .Web and .Nuget projects, as we’re only concerned with building a bot.
5) Ensure that the .Bot project is set as the startup project.
6) Add the APIXU C# library project to the solution. It’s in the \apixu-csharp\APIXULib\ folder. Once added, reference the project in the .Bot and .Shared projects.
7) Add the Adaptive Cards Nuget package to the .Bot and .Shared projects as shown.
8) Update Microsoft.Bot.Builder to version 3.8.1 in nugget package manager. Also restore packages if you have not already.
9) Change the configuration settings in your Bot project’s web.config to be your keys obtained from LUIS and APIXU.
<add key="LUIS_SubscriptionKey" value="LUISSUbscriptionKey" /> <add key="LUIS_ModelId" value="LUISModelId" /> <add key="APIXUKey" value="APIXUKey" />
We’ve set up our LUIS model, added the appropriate libraries, and added the necessary authorization settings for our Bot to communicate with the services we need. Now we need to define the action to create our adaptive cards.
Next, open the GetWeatherInPlaceAction.cs file in the .Shared project and change it to the following:
FulfillAsync, all of the other methods included are just used to customize our adaptive card properties. Under the hood, the adaptive cards library is responsible for determining which type of channel or host app the bot is running, and adapting it accordingly to the host app’s styling.
You can check the adaptive cards schema documentation for a detailed listing of all of the different properties you can use to format an adaptive card.
Open the RootDialog in the .Bot project, and modify the
WeatherInPlaceActionHandlerAsync method as follows:
This change allows the bot’s
IMessageActivity to handle an adaptive card.
Add the GetMessage method as follows, which takes our newly created card and adds it as an attachment to the message which will be returned to the user:
Running our bot using the emulator, this is what our adaptive card looks like.
We’ve now added Adaptive Cards to one of our pre-existing sample bots (LUIS Action Binding sample). The adaptive cards library allows us to provision our bots (and other applications) with scalable components to cover most of the needs of the developer while still maintaining the styling and security of the host application.
You can check out the Adaptive Cards presentation from Build 2017 here.
At the presentation we also made the Adaptive Cards Github repo public. Although we defined the properties we want our card to display, the library is responsible for determining which channel the bot is being accessed on, and scaling those properties accordingly - or should we say - Adaptively!
Eric Dahlvang and Matthew Shim from the Bot Framework team