| --- |
| active_crumb: Docs |
| layout: documentation |
| id: first-example |
| --- |
| |
| <!-- |
| Licensed to the Apache Software Foundation (ASF) under one or more |
| contributor license agreements. See the NOTICE file distributed with |
| this work for additional information regarding copyright ownership. |
| The ASF licenses this file to You under the Apache License, Version 2.0 |
| (the "License"); you may not use this file except in compliance with |
| the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| --> |
| |
| <div class="col-md-8 second-column"> |
| <section id="setup"> |
| <h2 class="section-title">Overview <a href="#"><i class="top-link fas fa-fw fa-angle-double-up"></i></a></h2> |
| <p> |
| It is highly recommended to familiarize yourself with the <a href="/key-concepts.html">key concepts</a> first. |
| </p> |
| <p> |
| Let's develop the first NLPCraft example to familiarize ourselves with the main workflow of NLPCraft. |
| We'll put together a NLI-powered home light switch prototype that can be controlled through the natural language. |
| We'll keep <a target=_ href="https://cloud.google.com/speech-to-text/">speech-to-text conversion</a> and integration |
| with <a target=_ href="https://developer.apple.com/homekit/">HomeKit</a> or |
| <a href="https://www.arduino.cc/" target=_>Ardunio</a> outside of this example - and concentrate just on understanding |
| the natural language commands. |
| </p> |
| </section> |
| <section id="new_project"> |
| <h2 class="section-title">Create New Project <a href="#"><i class="top-link fas fa-fw fa-angle-double-up"></i></a></h2> |
| <p> |
| You can create new Scala projects in many ways - we'll use SBT |
| to accomplish this task. Make sure that <code>build.sbt</code> file has the following content: |
| </p> |
| <pre class="brush: js, highlight: [7]"> |
| ThisBuild / version := "0.1.0-SNAPSHOT" |
| ThisBuild / scalaVersion := "3.2.2" |
| lazy val root = (project in file(".")) |
| .settings( |
| name := "NLPCraft LightSwitch Example", |
| version := "{{site.latest_version}}", |
| libraryDependencies += "org.apache.nlpcraft" % "nlpcraft" % "{{site.latest_version}}", |
| libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.15" % "test" |
| ) |
| </pre> |
| <p><b>NOTE: </b>use the latest versions of Scala and ScalaTest.</p> |
| <p>Create the following files so that resulting project structure would look like the following:</p> |
| <ul> |
| <li><code>lightswitch_model.yaml</code> - YAML configuration file which contains model description.</li> |
| <li><code>LightSwitchModel.scala</code> - Model implementation.</li> |
| <li><code>LightSwitchModelSpec.scala</code> - Test that allows to test your model.</li> |
| </ul> |
| <pre class="brush: plain, highlight: [7, 10, 14]"> |
| | build.sbt |
| +--project |
| | build.properties |
| \--src |
| +--main |
| | +--resources |
| | | lightswitch_model.yaml |
| | \--scala |
| | \--demo |
| | LightSwitchModel.scala |
| \--test |
| \--scala |
| \--demo |
| LightSwitchModelSpec.scala |
| </pre> |
| </section> |
| <section id="model"> |
| <h2 class="section-title">Data Model<a href="#"><i class="top-link fas fa-fw fa-angle-double-up"></i></a></h2> |
| <p> |
| We are going to start with declaring the static part of our model using YAML which we will later load |
| in our Scala-based model implementation. |
| Open <code>src/main/resources/<b>lightswitch_model.yaml</b></code> |
| file and replace its content with the following YAML: |
| </p> |
| <pre class="brush: js, highlight: [1, 10, 17, 25]"> |
| macros: |
| "<ACTION>" : "{turn|switch|dial|let|set|get|put}" |
| "<KILL>" : "{shut|kill|stop|eliminate}" |
| "<ENTIRE_OPT>" : "{entire|full|whole|total|_}" |
| "<FLOOR_OPT>" : "{upstairs|downstairs|{1st|first|2nd|second|3rd|third|4th|fourth|5th|fifth|top|ground} floor|_}" |
| "<TYPE>" : "{room|closet|attic|loft|{store|storage} {room|_}}" |
| "<LIGHT>" : "{all|_} {it|them|light|illumination|lamp|lamplight}" |
| |
| elements: |
| - type: "ls:loc" |
| description: "Location of lights." |
| synonyms: |
| - "<ENTIRE_OPT> <FLOOR_OPT> {kitchen|library|closet|garage|office|playroom|{dinning|laundry|play} <TYPE>}" |
| - "<ENTIRE_OPT> <FLOOR_OPT> {master|kid|children|child|guest|_} {bedroom|bathroom|washroom|storage} {<TYPE>|_}" |
| - "<ENTIRE_OPT> {house|home|building|{1st|first} floor|{2nd|second} floor}" |
| |
| - type: "ls:on" |
| groups: |
| - "act" |
| description: "Light switch ON action." |
| synonyms: |
| - "<ACTION> {on|up|_} <LIGHT> {on|up|_}" |
| - "<LIGHT> {on|up}" |
| |
| - type: "ls:off" |
| groups: |
| - "act" |
| description: "Light switch OFF action." |
| synonyms: |
| - "<ACTION> <LIGHT> {off|out|down}" |
| - "{<ACTION>|<KILL>} {off|out|down} <LIGHT>" |
| - "<KILL> <LIGHT>" |
| - "<LIGHT> <KILL>" |
| - "{out|no|off|down} <LIGHT>" |
| - "<LIGHT> {out|off|down}" |
| </pre> |
| |
| <ul> |
| <li> |
| <code>Line 1</code> defines several macros that are used later on throughout the model's elements |
| to shorten the synonym declarations. Note how macros coupled with option groups |
| shorten overall synonym declarations 1000:1 vs. manually listing all possible word permutations. |
| </li> |
| <li> |
| <code>Lines 10, 17, 25</code> define three model elements: the location of the light, and actions to turn |
| the light on and off. Action elements belong to the same group <code>act</code> which |
| will be used in our intent, defined in <code>LightSwitchModel</code> class. Note that these model |
| elements are defined mostly through macros we have defined above. |
| </li> |
| </ul> |
| <div class="bq info"> |
| <p><b>YAML vs. API</b></p> |
| <p> |
| As usual, this YAML-based static model definition is convenient but totally optional. All elements definitions |
| can be provided programmatically inside Scala model <code>LightSwitchModel</code> class as well. |
| </p> |
| </div> |
| </section> |
| <section id="code"> |
| <h2 class="section-title">Model Class <a href="#"><i class="top-link fas fa-fw fa-angle-double-up"></i></a></h2> |
| <p> |
| Open <code>src/main/scala/demo/<b>LightSwitchModel.scala</b></code> file and replace its content with the following code: |
| </p> |
| <pre class="brush: scala, highlight: [6, 7, 8, 10, 11, 14, 15, 22]"> |
| package demo |
| |
| import org.apache.nlpcraft.* |
| import org.apache.nlpcraft.annotations.* |
| |
| class LightSwitchModel extends NCModel( |
| NCModelConfig("nlpcraft.lightswitch.java.ex", "LightSwitch Example Model", "1.0"), |
| new NCPipelineBuilder().withSemantic("en", "lightswitch_model.yaml").build |
| ): |
| @NCIntent("intent=ls term(act)={has(ent_groups, 'act')} term(loc)={# == 'ls:loc'}*") |
| def onMatch( |
| ctx: NCContext, |
| im: NCIntentMatch, |
| @NCIntentTerm("act") actEnt: NCEntity, |
| @NCIntentTerm("loc") locEnts: List[NCEntity] |
| ): NCResult = |
| val status = if actEnt.getType == "ls:on" then "on" else "off" |
| val locations = if locEnts.isEmpty then "entire house" else locEnts.map(_.mkText).mkString(", ") |
| |
| // Add HomeKit, Arduino or other integration here. |
| // By default - just return a descriptive action string. |
| NCResult(s"Lights are [$status] in [${locations.toLowerCase}].") |
| </pre> |
| <p> |
| The intent callback logic is very simple - we return a descriptive confirmation message |
| back (explaining what lights were changed). With action and location detected, you can add |
| the actual light switching using HomeKit or Arduino devices. Let's review this implementation step by step: |
| </p> |
| <ul> |
| <li> |
| On <code>line 6</code> our class extends {% scaladoc NCModel NCModel %} with two mandatory parameters. |
| </li> |
| <li> |
| <code>Line 7</code> creates model configuration with most default parameters. |
| </li> |
| <li> |
| <code>Line 8</code> creates pipeline, based on semantic model definition, |
| described in <code>lightswitch_model.yaml</code> file. |
| </li> |
| <li> |
| <code>Lines 10 and 11</code> annotate intents <code>ls</code> and its callback method <code>onMatch()</code>. |
| Intent <code>ls</code> requires one action (a token belonging to the group <code>act</code>) and optional list of light locations |
| (zero or more tokens with ID <code>ls:loc</code>) - by default we assume the entire house as a default location. |
| </li> |
| <li> |
| <code>Lines 14 and 15</code> map terms from detected intent to the formal method parameters of the |
| <code>onMatch()</code> method. |
| </li> |
| <li> |
| On the <code>line 22</code> the intent callback simply returns a confirmation message. |
| </li> |
| </ul> |
| </section> |
| |
| <section id="build"> |
| <h2 class="section-title">Build Project<a href="#"><i class="top-link fas fa-fw fa-angle-double-up"></i></a></h2> |
| |
| <p> |
| Once the model ready, run the SBT build from the project folder: |
| </p> |
| |
| <pre class="brush: scala, highlight: []"> |
| $ sbt clean compile |
| </pre> |
| </section> |
| |
| <section id="testing"> |
| <h2 class="section-title">Testing <a href="#"><i class="top-link fas fa-fw fa-angle-double-up"></i></a></h2> |
| <p> |
| The test defined in <code>LightSwitchModelSpec</code> allows to check that all input test sentences are |
| processed correctly and trigger the expected intent <code>ls</code>: |
| </p> |
| <pre class="brush: scala, highlight: [9, 11]"> |
| package demo |
| |
| import org.apache.nlpcraft.* |
| import org.scalatest.funsuite.AnyFunSuite |
| import scala.util.Using |
| |
| class LightSwitchModelSpec extends AnyFunSuite: |
| test("test") { |
| Using.resource(new NCModelClient(new LightSwitchModel())) { client => |
| def check(txt: String): Unit = |
| require(client.debugAsk(txt, "userId", true).getIntentId == "ls") |
| |
| check("Turn the lights off in the entire house.") |
| check("Turn off all lights now") |
| check("Switch on the illumination in the master bedroom closet.") |
| check("Get the lights on.") |
| check("Off the lights on the 1st floor") |
| check("Lights up in the kitchen.") |
| check("Please, put the light out in the upstairs bedroom.") |
| check("Set the lights on in the entire house.") |
| check("Turn the lights off in the guest bedroom.") |
| check("Could you please switch off all the lights?") |
| check("Dial off illumination on the 2nd floor.") |
| check("Turn down lights in 1st floor bedroom") |
| check("Lights on at second floor kitchen") |
| check("Please, no lights!") |
| check("Kill off all the lights now!") |
| check("Down the lights in the garage") |
| check("Lights down in the kitchen!") |
| check("Turn up the illumination in garage and master bedroom") |
| check("Turn down all the light now!") |
| check("No lights in the bedroom, please.") |
| check("Light up the garage, please!") |
| check("Kill the illumination now!") |
| } |
| } |
| </pre> |
| <ul> |
| <li> |
| <code>Line 9</code> creates the client for our model. |
| </li> |
| <li> |
| <code>Line 11</code> calls a special method |
| {% scaladoc NCModelClient#debugAsk-fffff96c debugAsk() %}. |
| It allows to check the winning intent and its callback parameters without actually |
| calling the intent. |
| </li> |
| <li> |
| <code>Lines 13-34</code> define all the test input sentences that should all |
| trigger <code>ls</code> intent. |
| </li> |
| </ul> |
| <p> |
| You can run this test via SBT task <code>executeTests</code> or using IDE. |
| </p> |
| <pre class="brush: scala, highlight: []"> |
| $ sbt executeTests |
| </pre> |
| </section> |
| <section> |
| <h2 class="section-title">Done! 👌 <a href="#"><i class="top-link fas fa-fw fa-angle-double-up"></i></a></h2> |
| <p> |
| You've created light switch data model and tested it. |
| </p> |
| </section> |
| </div> |
| <div class="col-md-2 third-column"> |
| <ul class="side-nav"> |
| <li class="side-nav-title">On This Page</li> |
| <li><a href="#new_project">New Project</a></li> |
| <li><a href="#model">Data Model</a></li> |
| <li><a href="#code">Model Class</a></li> |
| <li><a href="#build">Build Project</a></li> |
| <li><a href="#testing">Testing</a></li> |
| {% include quick-links.html %} |
| </ul> |
| </div> |
| |
| |
| |
| |