blob: 81fbaa3243b21d79b8b6ecf978b19ac4407d81d7 [file] [log] [blame]
---
active_crumb: Light Switch <code><sub>ex</sub></code>
layout: documentation
id: light_switch
fa_icon: fa-cube
---
<!--
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 example">
<section id="overview">
<h2 class="section-title">Overview <a href="#"><i class="top-link fas fa-fw fa-angle-double-up"></i></a></h2>
<p>
This example provides very simple implementation for NLI-powered light switch. You can say something like
"Turn the lights off in the entire house" or "Switch on the illumination in the master bedroom closet".
You can modify intent callbacks to perform the actual light switching using HomeKit or Arduino-based
controllers.
</p>
<p>
<b>Complexity:</b> <span class="complexity-one-star"><i class="fas fa-star"></i> <i class="far fa-star"></i> <i class="far fa-star"></i></span><br/>
<span class="ex-src">Source code: <a target="github" href="https://github.com/apache/incubator-nlpcraft/tree/master/nlpcraft-examples/lightswitch">GitHub <i class="fab fa-fw fa-github"></i></a><br/></span>
<span class="ex-review-all">Review: <a target="github" href="https://github.com/apache/incubator-nlpcraft/tree/master/nlpcraft-examples">All Examples at GitHub <i class="fab fa-fw fa-github"></i></a></span>
</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 <a href="/tools/script.html">NLPCraft CLI</a>
to accomplish this task:
</p>
<nav>
<div class="nav nav-tabs" role="tablist">
<a class="nav-item nav-link active" data-toggle="tab" href="#nav-prj-cmd" role="tab">Command</a>
<a class="nav-item nav-link" data-toggle="tab" href="#nav-prj-out" role="tab">Output <i class="fa fa-desktop output"></i></a>
</div>
</nav>
<div class="tab-content">
<div class="tab-pane fade show active" id="nav-prj-cmd" role="tabpanel">
<pre class="brush: bash">
$ bin/nlpcraft.sh gen-project --baseName=LightSwitch --outputDir=~ --pkgName=demo --lang=scala
</pre>
<p>
<b>NOTES:</b>
</p>
<ul>
<li>
New project created in <code>/home/LightSwitch</code> directory.
</li>
<li>
<code>gen-project</code> command defaults to Java and Maven as its built tool.
</li>
<li>
Run <code class="script">bin/nlpcraft.sh help --cmd=gen-project</code> to get a full help on <code>gen-project</code> command.
</li>
<li>
<a href="/tools/script.html">NLPCraft CLI</a> is available as <code>nlpcraft.sh</code> for
<i class="fab fa-fw fa-linux"></i> and <code>nlpcraft.cmd</code>
for <i class="fab fa-fw fa-windows"></i>.
</li>
</ul>
</div>
<div class="tab-pane fade show" id="nav-prj-out" role="tabpanel">
<p></p>
<img alt="" class="img-fluid" src="/images/light_switch_fig1.png">
</div>
</div>
</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 using
<code>NCModelFileAdapter</code> in our Scala-based model implementation. Open <code>src/main/resources/<b>light_switch.yaml</b></code>
file and replace its content with the following YAML:
</p>
<pre class="brush: js, highlight: [6, 22, 23, 26, 33, 41, 52]">
id: "nlpcraft.lightswitch.ex"
name: "Light Switch Example Model"
version: "1.0"
description: "NLI-powered light switch example model."
macros:
- name: "&lt;ACTION&gt;"
macro: "{turn|switch|dial|let|set|get|put}"
- name: "&lt;KILL&gt;"
macro: "{shut|kill|stop|eliminate}"
- name: "&lt;ENTIRE_OPT&gt;"
macro: "{entire|full|whole|total|_}"
- name: "&lt;FLOOR_OPT&gt;"
macro: "{upstairs|downstairs|{1st|2nd|3rd|4th|5th|top|ground} floor|_}"
- name: "&lt;TYPE&gt;"
macro: "{room|closet|attic|loft|{store|storage} {room|_}}"
- name: "&lt;LIGHT&gt;"
macro: "{all|_} {it|them|light|illumination|lamp|lamplight}"
enabledBuiltInTokens: []
permutateSynonyms: true
sparse: true
elements:
- id: "ls:loc"
description: "Location of lights."
synonyms:
- "&lt;ENTIRE_OPT&gt; &lt;FLOOR_OPT&gt; {kitchen|library|closet|garage|office|playroom|{dinning|laundry|play} &lt;TYPE&gt;}"
- "&lt;ENTIRE_OPT&gt; &lt;FLOOR_OPT&gt; {master|kid|children|child|guest|_} {bedroom|bathroom|washroom|storage} {&lt;TYPE&gt;|_}"
- "&lt;ENTIRE_OPT&gt; {house|home|building|{1st|first} floor|{2nd|second} floor}"
- id: "ls:on"
groups:
- "act"
description: "Light switch ON action."
synonyms:
- "&lt;ACTION&gt; {on|up|_} &lt;LIGHT&gt; {on|up|_}"
- "&lt;LIGHT&gt; {on|up}"
- id: "ls:off"
groups:
- "act"
description: "Light switch OFF action."
synonyms:
- "&lt;ACTION&gt; &lt;LIGHT&gt; {off|out}"
- "{&lt;ACTION&gt;|&lt;KILL&gt;} {off|out} &lt;LIGHT&gt;"
- "&lt;KILL&gt; &lt;LIGHT&gt;"
- "&lt;LIGHT&gt; &lt;KILL&gt;"
- "no &lt;LIGHT&gt;"
intents:
- "intent=ls term(act)={has(tok_groups, 'act')} term(loc)={# == 'ls:loc'}*"
</pre>
<p>There are number of important points here:</p>
<ul>
<li>
<code>Line 6</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 22, 23</code> define model properties that allow for multi-word synonyms in this model
to be
<a class="not-code" target="javadoc" href="/apis/latest/org/apache/nlpcraft/model/NCModelView.html#isSparse()">sparse</a> and
<a class="not-code" target="javadoc" href="/apis/latest/org/apache/nlpcraft/model/NCModelView.html#isPermutateSynonyms()">permutate</a> them for better detection. These two properties generally enable a free-form
natural language comprehension.
</li>
<li>
<code>Lines 26, 33, 41</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 (<code>line 42</code>). Note that these model elements are defined mostly
through macros we have provided above.
</li>
<li>
On <code>line 52</code> we define a non-conversational intent <code>ls</code> that 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>
</ul>
<p>
Now that our model is ready let's create a Java class that would load this model and provide the actual
callback for when the intent <code>ls</code> is detected in the user input.
</p>
</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>LightSwitch.scala</b></code> file and replace its content with the following code:
</p>
<pre class="brush: scala, highlight: [5, 6, 7, 25, 26, 38]">
package demo
import org.apache.nlpcraft.model.{NCIntentTerm, _}
class LightSwitch extends NCModelFileAdapter("light_switch.yaml") {
@NCIntentRef("ls")
@NCIntentSample(Array(
"Turn the lights off in the entire house.",
"Turn off all lights now",
"Switch on the illumination in the master bedroom closet.",
"Get the lights on.",
"Lights up in the kitchen.",
"Please, put the light out in the upstairs bedroom.",
"Set the lights on in the entire house.",
"Turn the lights off in the guest bedroom.",
"Could you please switch off all the lights?",
"Dial off illumination on the 2nd floor.",
"Please, no lights!",
"Kill off all the lights now!",
"No lights in the bedroom, please.",
"Light up the garage, please!",
"Kill the illumination now!"
))
def onMatch(
@NCIntentTerm("act") actTok: NCToken,
@NCIntentTerm("loc") locToks: List[NCToken]
): NCResult = {
val status = if (actTok.getId == "ls:on") "on" else "off"
val locations =
if (locToks.isEmpty)
"entire house"
else
locToks.map(_.getOriginalText()).mkString(", ")
// Add HomeKit, Arduino or other integration here.
// By default - return a descriptive action string.
NCResult.text(s"Lights '$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 5</code> our class extends <code>NCModelFileAdapter</code> that allows us to load most
of the model declaration from the external YAML file and only provide functionality that we
couldn't express in declarative portion in YAML.
</li>
<li>
<code>Line 6</code> annotates method <code>onMatch</code> as a callback for the intent <code>ls</code>
when it is detected in the user input. Note that intent <code>ls</code> is defined in the <code>light_switch.yaml</code>
file that was loaded by <code>NCModelFileAdapter</code> class.
</li>
<li>
Note the <code>line 7</code> where we use <a target="javadoc" href="/apis/latest/org/apache/nlpcraft/model/NCIntentSample.html">@NCIntentSample</a>
annotation to provide samples of the user input that this intent should match. Apart from documentation
purpose these samples will be used when we will be <a href="#testing">testing out model below.</a>
</li>
<li>
<code>Lines 25 and 26</code> map terms from detected intent to the formal method parameters of the
<code>onMatch</code> method.
</li>
<li>
On the <code>line 38</code> the intent callback simply returns a confirmation message.
</li>
</ul>
</section>
<section id="build_project">
<h2 class="section-title">Build Project <a href="#"><i class="top-link fas fa-fw fa-angle-double-up"></i></a></h2>
<p>
Once we have our model ready let's go to <code>~/LightSwitch</code> directory and run the Maven build:
</p>
<pre class="brush: bash">
$ cd ~/LightSwitch
$ mvn clean package
</pre>
<p>
At this stage we have our project built and we are ready to start testing.
</p>
</section>
<section id="start_server">
<h2 class="section-title">Start Server <a href="#"><i class="top-link fas fa-fw fa-angle-double-up"></i></a></h2>
<p>
Run the following command to start local REST server, if it hasn't been started already, from the NLPCraft installation directory:
</p>
<nav>
<div class="nav nav-tabs" role="tablist">
<a class="nav-item nav-link active" data-toggle="tab" href="#nav-srv-cmd" role="tab">Command</a>
<a class="nav-item nav-link" data-toggle="tab" href="#nav-srv-out" role="tab">Output <i class="fa fa-desktop output"></i></a>
</div>
</nav>
<div class="tab-content">
<div class="tab-pane fade show active" id="nav-srv-cmd" role="tabpanel">
<pre class="brush: bash">
$ bin/nlpcraft.sh start-server
</pre>
</div>
<div class="tab-pane fade show" id="nav-srv-out" role="tabpanel">
<p></p>
<p>
<img class="img-fluid" alt="" src="/images/server-fig1.png">
</p>
</div>
</div>
<p>
<b>NOTES:</b>
</p>
<ul>
<li>
<i style="color: #F39C12" class="fa fa-exclamation-triangle"></i> REST server is a "fire-and-forget" component that you generally need to start it only once
for this and other examples.
</li>
<li>
Run <code class="script">bin/nlpcraft.sh help --cmd=start-server</code> to get a full help on this command.
</li>
<li>
<a href="/tools/script.html">NLPCraft CLI</a> is available as <code>nlpcraft.sh</code> for
<i class="fab fa-fw fa-linux"></i> and <code>nlpcraft.cmd</code>
for <i class="fab fa-fw fa-windows"></i>.
</li>
</ul>
</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>
Remember the <a target="javadoc" href="/apis/latest/org/apache/nlpcraft/model/NCIntentSample.html">@NCIntentSample</a>
annotation we have used in our model code next to intent definition?
</p>
<p>
Part of the <a href="/tools/test_framework.html">test framework</a>, the auto-validator class <a
target="javadoc"
href="/apis/latest/org/apache/nlpcraft/model/tools/test/NCTestAutoModelValidator.html">NCTestAutoModelValidator</a> takes one or more model IDs
(or class names) and performs validation. Validation consists of starting an <a href="/tools/embedded_probe.html">embedded probe</a> with a given model,
scanning for <a target="javadoc" href="/apis/latest/org/apache/nlpcraft/model/NCIntentSample.html">@NCIntentSample</a> and
<a target="javadoc" href="/apis/latest/org/apache/nlpcraft/model/NCIntentSampleRef.html">@NCIntentSampleRef</a> annotations
and their corresponding callback methods, submitting each sample input
sentences from these annotations and checking that resulting intent matches the intent the sample was attached to.
Note that auto-testing does not require any additional code to be written - the class gathers all required information from the model
itself.
</p>
<p>
As always, you can launch model auto-validator as any other Java class but we'll use NLPCraft CLI
to do it more conveniently:
</p>
<pre class="brush: bash">
$ bin/nlpcraft.sh test-model --cp=~/LightSwitch/target/classes --mdls=demo.LightSwitch
</pre>
<p>
<b>NOTES:</b>
</p>
<ul>
<li>
Run <code class="script">bin/nlpcraft.sh help --cmd=test-model</code> to get a full help on this command.
</li>
<li>
Note that you can use <code>retest-model</code> command in REPL mode to re-run the last model test
avoiding the retyping of all required parameters.
</li>
<li>
<a href="/tools/script.html">NLPCraft CLI</a> is available as <code>nlpcraft.sh</code> for
<i class="fab fa-fw fa-linux"></i> and <code>nlpcraft.cmd</code>
for <i class="fab fa-fw fa-windows"></i>.
</li>
</ul>
<p>
Look at the output of this command and you will see the test results for all our sample utterances:
</p>
<p>
<img style="max-width: 928px !important;" class="img-fluid" alt="" src="/images/light-switch-test.png">
</p>
</section>
<section id="rinse">
<h2 class="section-title">Rinse <span class="amp">&amp;</span> Repeat <a href="#"><i class="top-link fas fa-fw fa-angle-double-up"></i></a></h2>
<p>
Typical development cycle consists of:
</p>
<ul>
<li>
<a href="#model">Modifying the model</a>
</li>
<li>
<a href="#build_project">Re-building the project</a>
</li>
<li>
<a href="#testing">Re-running the test</a>
</li>
</ul>
<p>
All of these operations can be performed from <a href="/tools/script.html">NLPCraft CLI</a> in REPL mode or from any IDE.
</p>
<p>
NOTE: you don't need to restart REST server every time - it only needs to be started once.
</p>
</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, started the REST server and tested this model using the built-in test framework.
</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="#overview">Overview</a></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_project">Build Project</a></li>
<li><a href="#start_server">Start Server</a></li>
<li><a href="#testing">Testing</a></li>
<li><a href="#rinse">Rinse <span class="amp">&amp;</span> Repeat</a></li>
{% include quick-links.html %}
</ul>
</div>