Converting natural language into actions with NLPCraft and Groovy

Author: Paul King
Published: 2023-03-10 07:22PM (Last updated: 2023-03-13 01:32PM)


This blog looks at using Apache NLPCraft with Groovy.

Apache NLPCraft (incubating) is a library for converting natural language into actions. It is designed around an advanced Intent Definition Language (IDL) for defining natural language intents and a fully deterministic intent matching algorithm.

The groovy-data-science GitHub repo has an example using the previous 0.9.0 version of Apache NLPCraft. The earlier version supported Java, Scala2, Kotlin and Groovy. The example in the LanguageProcessingNLPCraft project showed how to interact with models in all 4 of those languages.

The project recently announced the release of version 1.0.0 which represents a deep refactoring over 18 months. The new version offers many enhancements and supports Scala3 for its models. If you are going to be using NLPCraft extensively, then Scala3 is probably your best choice as programming language. Having said that, since we are on the JVM, certain integration steps aren’t too hard. We’ll show using Groovy as the client language.

Controlling house light switches

House lighting (source: accentuate.io) First, a bit of background about the example. We are trying to determine the intent behind English language commands to turn lights on and off in a house. We are going to use the project’s pre-defined model which is part of their examples. It is defined using a combination of YAML and Scala3 code. The key thing to note is that the model is made up of a number of keywords comprising actions and locations. If these are matched, the lightswitch (ls) intent is triggered. Actions and locations will become more obvious soon in our example.

Our example uses an English language model but NLPCraft can support any natural language. They provide the lightswitch model also in French and other languages.

We also need to add NLPCraft dependencies: org.apache.nlpcraft:nlpcraft:1.0.0 and org.apache.nlpcraft:nlpcraft-example-lightswitch:1.0.0 (for the pre-compiled model). You can use @Grab statements in your script or add the dependencies to your build file.

Note
Depending on the Groovy version and modules you are using, if you see exceptions complaining about Jackson databind versions, simply exclude the Jackson versions Groovy is referencing. They are more recent than what Scala is expecting.

Finally, here is the Groovy code:

import org.apache.nlpcraft.NCModelClient
import org.apache.nlpcraft.examples.lightswitch.LightSwitchModel
import static scala.collection.immutable.HashMap$.MODULE$ as ScalaMap

var data = ScalaMap.empty()  // no optional data
var user = 'someUserId'
var expectedIntent = 'ls'    // from model

var phrases = [
    'Turn on the lights in the master bedroom',
    'Please, no lights!',
    'Turn up the illumination in garage and 1st floor'
]

new NCModelClient(new LightSwitchModel()).withCloseable { client ->
    phrases.each { phrase ->
        var result = client.ask(phrase, user, data)
        println result.body
        assert result.intentId.get() == expectedIntent
    }
}

Here, we are just going to print the result returned by the callback in the model class. Typically, we’d instead have HomeKit, Arduino or some other kind of integration at this point. We’ll also check that the ls intent was triggered.

When we run this script, our assertions pass, and the output is:

Lights are [on] in [master bedroom].
Lights are [off] in [entire house].
Lights are [on] in [1st floor, garage].

Asking for the time

Robot watch (source: personalrobots.biz) Let’s now look at the NLPCraft time example. It’s a very simple world time bot implementation.

The model is again defined as a combination of YAML and Scala3 code. A pre-compiled version is available as org.apache.nlpcraft:nlpcraft-example-time:1.0.0 from Maven central. We used a locally built version with DefaultScalaModule enabled for YAML processing. We also need the groovy-yaml module on the Groovy side if not already there.

The model defines two intents:

  • intent2 should be triggered if a match is found for a city location.

  • intent1 will be triggered if it looks like you are asking for the time without a city; this corresponds to finding the local time.

Here is the Groovy client code:

import groovy.yaml.YamlSlurper
import org.apache.nlpcraft.NCModelClient
import org.apache.nlpcraft.examples.time.TimeModel
import static scala.collection.immutable.HashMap$.MODULE$ as ScalaMap

var data = ScalaMap.empty()  // no optional data
var user = 'someUserId'
var phrases = [
    "What time is it now in New York City?"                 : 'intent2',
    "What's the current time in Singapore?"                 : 'intent2',
    "Show me time of the day in London."                    : 'intent2',
    "Can you please give me Tokyo's current date and time." : 'intent2',
    "What's the local time?"                                : 'intent1'
]

new NCModelClient(new TimeModel()).withCloseable { client ->
    phrases.each { phrase, expected ->
        var result = client.ask(phrase, user, data)
        assert result.intentId.get() == expected
        var body = new YamlSlurper().parseText(result.body)
        body.with{ println "$city, $country: $localTime" }
    }
}

Again, all our assertions pass, and here is the output:

New york city, United states: 12 Mar. 2023, 10:09:41 pm
Singapore, Singapore: 13 Mar. 2023, 10:09:41 am
London, United kingdom: 13 Mar. 2023, 2:09:41 am
Tokyo, Japan: 13 Mar. 2023, 11:09:41 am
Brisbane, Australia: 13 Mar. 2023, 12:09:42 pm

Further information

Update history

13/Mar/2023: Added the time example.