| --- |
| 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: "<ACTION>" |
| macro: "{turn|switch|dial|let|set|get|put}" |
| - name: "<KILL>" |
| macro: "{shut|kill|stop|eliminate}" |
| - name: "<ENTIRE_OPT>" |
| macro: "{entire|full|whole|total|_}" |
| - name: "<FLOOR_OPT>" |
| macro: "{upstairs|downstairs|{1st|2nd|3rd|4th|5th|top|ground} floor|_}" |
| - name: "<TYPE>" |
| macro: "{room|closet|attic|loft|{store|storage} {room|_}}" |
| - name: "<LIGHT>" |
| macro: "{all|_} {it|them|light|illumination|lamp|lamplight}" |
| |
| enabledBuiltInTokens: [] |
| |
| permutateSynonyms: true |
| sparse: true |
| |
| elements: |
| - id: "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}" |
| |
| - id: "ls:on" |
| groups: |
| - "act" |
| description: "Light switch ON action." |
| synonyms: |
| - "<ACTION> {on|up|_} <LIGHT> {on|up|_}" |
| - "<LIGHT> {on|up}" |
| |
| - id: "ls:off" |
| groups: |
| - "act" |
| description: "Light switch OFF action." |
| synonyms: |
| - "<ACTION> <LIGHT> {off|out}" |
| - "{<ACTION>|<KILL>} {off|out} <LIGHT>" |
| - "<KILL> <LIGHT>" |
| - "<LIGHT> <KILL>" |
| - "no <LIGHT>" |
| |
| 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">&</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">&</span> Repeat</a></li> |
| {% include quick-links.html %} |
| </ul> |
| </div> |
| |
| |
| |
| |
| |
| |