“Alexa, ask New Relic Insights if I should hit AWS re:Invent in Las Vegas this year?”

Recently I was curious to see if I could use my voice to command Alexa—Amazon’s personal assistant running on the Echo and similar devices—to retrieve and read back to me some high-level information about my New Relic Insights account. The answer is yes—and it’s pretty easy, too!

To connect Alexa and New Relic, I created a custom “skill” for Alexa using the Alexa Skills Kit. An Alexa Skill is simply a capability to enhance and personalize your interactions with Amazon Echo devices. It’s easy to get started creating skills—all you need is an Amazon developer account. (You may also want to register and add your new Alexa Skill to the skills store).

alexa skill

[click to enlarge]

Some tools and requirements to connect Alexa and New Relic

Since I’m a C# .NET guy at heart, I developed my project in Visual Studio. To get started I relied on a useful blog post by Tim Heuer titled Write your Amazon Alexa Skill using C# on AWS Lambda services. The post highlights the basic steps to get started using .NET Core and how to set up a serverless function in AWS Lambda to run your code (although you could also run it via a web service). I’m not going to walk you through all the steps involved, but here’s the high-level overview of what I did:

  1. Get the tools you’ll need:
  2. Create your AWS Lambda project in Visual Studio
  3. Create the function handler for the new Alexa Skill
  4. Publish and test your Lambda function. The Alexa Skills Kit has a great guide on deploying custom skills to AWS Lambda.

(Obviously, you don’t have to use C# and Visual studio, but be sure to check the Alexa Skills Kit requirements for supported languages, which vary depending on whether you’ll use Lambda or a web service to execute your code.)

Setting the invocation name and interaction model

To start an interaction with a custom Alexa Skill, users need to say an invocation name. In my case, I wanted the invocation name to be “new relic insights” so I could say, for example, “Alexa, ask New Relic Insights what is the status of my default account.”

invocation_name

Next I had to define the interaction model for my custom Alexa Skill. To do this, I created an intent and a slot. The intent represents the action that Alexa takes after a user issues a spoken request. In the function’s code, it’s represented as inputRequest.Intent.Name, and takes the value NewRelicInsights.

The slot, represented as inputRequest.Intent.Slot, is an optional argument for the intent request. In my code the slot takes the value of the nraccountid parameter, derived from my Insights Query API key. The nraccountid parameter is also added as a parameter to the sample utterances I’ve included in the intent. (I’ll show you where I define this parameter in a moment.)

A complete skill would then allow me to say, “Alexa, ask New Relic Insights with account id {read from the nraccountid value} if I have any alerts.”

Here’s a look at my intent and some of the sample utterances I set up in the Alexa Skill Kit.

intent and slot

[click to enlarge]

With this intent, I can, for example, ask Alexa:

  • “What’s the status of my default account?”
  • “How many users visited my site today?”
  • “Did my revenue increase today?”

Examining the skill code

Let me explain the part of the function that controls the intent request.

...
        {
            Alexa.NET.Response.IResponse response;
            IOutputSpeech innerResponse = null;
            var log = context.Logger;

            // check what type of a request it is like an IntentRequest or a LaunchRequest
            var requestType = input.GetRequestType();
            if (requestType != null)
            {
                log.LogLine(requestType.ToString());
            }

            if (requestType == typeof(IntentRequest))
            {
                // do some intent-based stuff
                // intent request, process the intent
                log.LogLine($"Intent Requested {input.Request.RequestId}");

                var intentRequest = input.Request as IntentRequest;
                string nraccountid = DEFAULT_NEWRELIC_ACCOUNT;
                // check the name to determine what you should do
                if (intentRequest.Intent.Name.Equals("NewRelicInsights”)
                {
                    log.LogLine($"Intent identified as NewRelicInsights");

                    nraccountid = intentRequest.Intent.Slots["nraccountid"].Value;
                    
                    // get the slots and check for NR account number
                    if (nraccountid == null)
                    {
                        log.LogLine($"Intent identified as NewRelicInsights with default account");
                        nraccountid = DEFAULT_NEWRELIC_ACCOUNT;
                    }
                    else if (nraccountid != null)
                    {
                        log.LogLine($"Intent identified as NewRelicInsights with nraccountid {intentRequest.Intent.Slots["nraccountid"].Value.ToString()}");

                        nraccountid = intentRequest.Intent.Slots["nraccountid"].Value;
                    }
                    else
                    {
                        log.LogLine($"Intent identified as NewRelicInsights with no nraccountid");
                        nraccountid = DEFAULT_NEWRELIC_ACCOUNT; 
                    }
                }
                else
                {
                    log.LogLine($"Intent not identified as NewRelicInsights");
                }
...

Starting on line 6, I check to see if the incoming request is an actual intent request. Then I check to see whether or not Alexa identified the intent request as a “NewRelicInsights” request.

I also set a value for the nraccountid slot parameter, which specifies the New Relic account to query. In this case, the nraccountid slot get the value of “DEFAULT_NEWRELIC_ACCOUNT”.

Now that I identified the request as a “NewRelicInsights” request, and know what account to query, I can then execute the request with the New Relic Insights Query API. On line 12, you’ll see where the nraccountid parameter gets its value from the account associated with my API key (disguised here as an environment variable).


                long iNRAccountID = long.MinValue;
                if (long.TryParse(nraccountid, out iNRAccountID))
                {
                    log.LogLine($"Intent checking Insights with acount id {iNRAccountID.ToString()}");

                    string url = "https://insights-api.newrelic.com/v1/accounts/" + nraccountid + "/query?nrql=SELECT%20count%28*%29%20FROM%20TransactionError%20SINCE%2030%20MINUTES%20AGO";

                    var client = new System.Net.Http.HttpClient();
                    client.DefaultRequestHeaders.Add("Accept", "application/json");
                    client.DefaultRequestHeaders.Add("X-Query-Key", Environment.GetEnvironmentVariable("NewRelicXQueryKey"));
                    HttpResponseMessage result = await client.GetAsync(url);

                    // convert data into objects
                    string res = await result.Content.ReadAsStringAsync();
                    log.LogLine($"Result from the New Relic Insights query: " + res);
                    int nrInsightsErrors = 0;
                    int.TryParse(res, out nrInsightsErrors);

                    int iNRaccountid = 0;
                    int.TryParse(nraccountid, out iNRaccountid);

                    log.LogLine("intentRequest.Locale: " + intentRequest.Locale);
                    AlexaResponseStrings ars = new AlexaResponseStrings_EN_US(iNRaccountid, nrInsightsErrors);
                    if (intentRequest.Locale.Equals("de-DE"))
                    {
                        ars = new AlexaResponseStrings_DE_DE(iNRaccountid, nrInsightsErrors);
                    }

                    innerResponse = new PlainTextOutputSpeech();
                    string baseResponse = ars.BaseResponseDefault;          // $"Your New Relic account {nraccountid.ToString()} currently has {nrInsightsErrors.ToString()} errors. ";
                    string followUpAction = ars.FollowupActionDefault;      //"You may want to check your New Relic account!";
                    if (res.Equals("{\"error\":\"Account not found.\"}"))
                    {
                        baseResponse = ars.BaseResponseErrNoAccount;        // $"I am sorry, but the account {nraccountid.ToString()} does not seem to exist. ";
                    }
                    else if (res.Equals("{\"error\":\"Invalid query key.\"}"))
                    {
                        baseResponse = ars.BaseResponseErrInvQueryKey;      // $"I am sorry, but you do not have access to account {nraccountid.ToString()}. ";
                    }
                    else if (nrInsightsErrors.Equals(0))
                    {
                        followUpAction = ars.FollowupActionWiesn;           //"You are good to go to Oktoberfest (or Wiesn how we call it over here)!";
                    }
                    (innerResponse as PlainTextOutputSpeech).Text = baseResponse + followUpAction;

                    // build the speech response 
                    var speech = new Alexa.NET.Response.SsmlOutputSpeech();
                    speech.Ssml = "" + baseResponse + followUpAction + "";

                    // create the response using the ResponseBuilder
                    var finalResponse = ResponseBuilder.Tell(speech);
                    return finalResponse;
                }

After the HTTP client executes the request, I parse the result and Alexa builds a speech response based on the sample utterances.

I can now have Alexa query New Relic Insights and tell me, for example, how many visitors have been to my website today or how many transaction errors visitors have encountered.

That’s not too bad for just a couple hours worth of work.

What insights can Amazon’s Alexa give you?

I built this fairly simple skill as a proof of concept just to see if I could get Alexa to read back details about my New Relic account. As more and more skills hit the market—there are already thousands available—and more users come to rely on them in their day-to-day activities, it’ll be essential to understand how they’re built, as the next—and obvious—stage will be monitoring these skills. Skill providers will want to ensure their skills are readily available and are able to execute requests as efficiently as possible, whether the skills are running on dedicated Amazon EC2 instances or in AWS Lambda functions.

But first, “Alexa, what’s the hottest news out of AWS re:Invent this year?”

(Editor’s note: Special thanks to Karissa Peth for her early prototype that connected our office Echo to New Relic Insights.)

Harry Kimpel is a Technical Account Manager based in Munich, Germany. He has a software development background and has worked in cloud architecture and cloud migrations for many years. Sometimes his work feels more like a software archeology, especially when re-architecting legacy applications.

View posts by .

Interested in writing for New Relic Blog? Send us a pitch!