| --- |
| active_crumb: First Example |
| 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">Project Setup</h2> |
| <p> |
| Let's setup a project for our first example with the following assumptions: |
| </p> |
| <ul> |
| <li>We'll use Mac OS/Linux environment</li> |
| <li>We'll use Scala to implement our model</li> |
| <li>We'll use <a target=_ href="https://maven.apache.org/install.html">Maven</a> to create and build our project</li> |
| <li>We'll use <a target=_ href="https://www.jetbrains.com/idea/">JetBrains IDEA</a> as our Scala/Java IDE</li> |
| </ul> |
| </section> |
| <section id="new_project"> |
| <h3 class="section-title">Create New Project</h3> |
| <p> |
| You can create new Maven-based project in many different ways - we'll use maven archetype generation |
| for that. In your home folder run the following command: |
| </p> |
| <pre class="brush: text"> |
| mvn archetype:generate -DgroupId=examples -DartifactId=my-app -DarchetypeVersion=1.4 -DinteractiveMode=false |
| </pre> |
| <p> |
| This will create <code>my-app</code> folder with the following default maven project structure: |
| </p> |
| <pre class="console"> |
| ├── <b>pom.xml</b> |
| └── src |
| ├── main |
| │ └── java |
| │ └── examples |
| │ └── App.java |
| └── test |
| └── java |
| └── examples |
| └── AppTest.java |
| </pre> |
| <p> |
| Create new IDEA project from this source folder (make sure to pick JDK 8 or later JDK and language support). |
| Let's also delete auto-generated files <code>App.java</code> and <code>AppTest.java</code> from our |
| project as we won't be using them. |
| </p> |
| </section> |
| <section id="add_nlpcraft"> |
| <h3 class="section-title">Add NLPCraft</h3> |
| <p> |
| We also need to add NLPCraft dependency to our new project. Open <code>pom.xml</code> file and replace |
| <code>dependencies</code> section with the following code: |
| </p> |
| <pre class="brush: xml, highlight: [3, 4, 5]"> |
| <dependencies> |
| <dependency> |
| <groupId>org.apache.nlpcraft</groupId> |
| <artifactId>nlpcraft</artifactId> |
| <version>{{site.latest_version}}</version> |
| </dependency> |
| </dependencies> |
| </pre> |
| <p> |
| Also make sure that you have correct JDK version (1.8 or above) for the maven compiler plugin: |
| </p> |
| <pre class="brush: xml, highlight: [3, 4]"> |
| <properties> |
| <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> |
| <maven.compiler.source>1.8</maven.compiler.source> |
| <maven.compiler.target>1.8</maven.compiler.target> |
| </properties> |
| </pre> |
| <p> |
| IDEA should automatically reload the project with newly updated <code>pom.xml</code> file and |
| we should be ready now to develop our new first data model. |
| </p> |
| </section> |
| <section id="data_model"> |
| <h2 class="section-title">Data Model</h2> |
| <p> |
| For our example we'll develop a prototypical light switch 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 only on understanding the natural |
| language commands. At the end, we should be able to say <code>"Turn the lights off in the guest bedroom"</code> |
| or <code>"Could you please switch off all the lights?"</code> |
| </p> |
| <p> |
| In NLPCraft a data model is simply an implementation of <a target="javadoc" href="/apis/latest/org/apache/nlpcraft/model/NCModel.html">NCModel</a> |
| interface. You can move most of the model configuration out to an external JSON or YAML file and use |
| <a target="javadoc" href="/apis/latest/org/apache/nlpcraft/model/NCModelFileAdapter.html">NCModelFileAdapter</a> to |
| load this external configuration when creating a new model. Note that this is a canonical pattern to build |
| models in NLPCraft and it allows you cleanly separate the logic of the model from its declarative configuration. |
| </p> |
| <p> |
| Let's add new <code>lightswitch_model.yml</code> file containing our model's static configuration in YAML |
| into <code>src/main/java/examples</code> folder with the following content: |
| </p> |
| <pre class="brush: js, highlight: [1, 14, 21, 28]"> |
| 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|control|let|set|get|put}" |
| - name: "<ENTIRE_OPT>" |
| macro: "{entire|full|whole|total|*}" |
| - name: "<LIGHT>" |
| macro: "{it|them|light|illumination|lamp|lamplight}" |
| enabledBuiltInTokens: [] # Don't use any built-in tokens. |
| elements: |
| - id: "ls:loc" |
| description: "Location of lights." |
| synonyms: |
| - "<ENTIRE_OPT> {upstairs|downstairs|*} {kitchen|library|closet|garage|office|playroom|{dinning|laundry|play} room}" |
| - "<ENTIRE_OPT> {upstairs|downstairs|*} {master|kid|children|child|guest|*} {bedroom|bathroom|washroom|storage} {closet|*}" |
| - "<ENTIRE_OPT> {house|home|building|{1st|first} floor|{2nd|second} floor}" |
| |
| - id: "ls:on" |
| group: "act" |
| description: "Light switch ON action." |
| synonyms: |
| - "<ACTION> <LIGHT> on" |
| - "<ACTION> on <LIGHT>" |
| |
| - id: "ls:off" |
| group: "act" |
| description: "Light switch OFF action." |
| synonyms: |
| - "<ACTION> <LIGHT> {off|out}" |
| - "{<ACTION>|shut|kill|stop|eliminate} {off|out} <LIGHT>" |
| - "no <LIGHT>" |
| </pre> |
| <p> |
| Notice the model ID <code>nlpcraft.lightswitch.ex</code> as well as semantic model elements |
| that we'll use later: |
| </p> |
| <ul> |
| <li><code>ls:loc</code></li> |
| <li><code>ls:on</code></li> |
| <li><code>ls:off</code></li> |
| </ul> |
| <p> |
| Model element <code>ls:loc</code> defines a location where we want to control the lights. Model |
| elements <code>ls:on</code> and <code>ls:off</code> defines corresponding "on" and "off" lights |
| actions. We'll use these elements in our model's intent-based matching logic. |
| </p> |
| <p> |
| Next let's go ahead and add model's logic. Create new <code>LightSwitchModel.scala</code> file along side |
| <code>lightswitch_model.json</code> configuration file and copy the following model code into it: |
| </p> |
| <pre class="brush: java, highlight: [7,9,10]"> |
| package examples; |
| |
| import org.apache.nlpcraft.model._ |
| import org.apache.nlpcraft.model.NCIntentTerm |
| |
| class LightSwitchModel extends NCModelFileAdapter("org/apache/nlpcraft/examples/lightswitch/lightswitch_model.yaml") { |
| @NCIntent("intent=ls conv=false term(act)={groups @@ 'act'} term(loc)={id == 'ls:loc'}*") |
| 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. |
| |
| NCResult.text(s"Lights '$status' in '${locations.toLowerCase}'.") |
| } |
| } |
| </pre> |
| <p> |
| Chapter <a href="/data-model.html">Data Model</a> will provide detailed explanation on how |
| data models work. Here are few comments as to what this code does and how it is organized: |
| </p> |
| <ul> |
| <li> |
| On line 6 we use <a target="javadoc" href="/apis/latest/org/apache/nlpcraft/model/NCModelFileAdapter.html">NCModelFileAdapter</a> |
| adapter and load model static configuration from <code>lightswitch_model.yml</code> file. |
| </li> |
| <li> |
| Our data model uses intent-based matching of the user input. Intent is a template and a callback for when |
| a template is matched. |
| Lines 7, 9 and 10 show the usage of |
| <a target="javadoc" href="/apis/latest/org/apache/nlpcraft/model/inteint/NCIntent.html">@NCIntent</a> and |
| <a target="javadoc" href="/apis/latest/org/apache/nlpcraft/model/inteint/NCIntent.html">@NCIntentTerm</a> |
| annotations that are used to connect the matching logic with a callback method. |
| </li> |
| <li> |
| The intent defined through <a target="javadoc" href="/apis/latest/org/apache/nlpcraft/model/inteint/NCIntent.html">@NCIntent</a> annotation matches |
| two parts: first should be any token belonging to <code>act</code> group (i.e. "on" or "off" action), |
| and the second should be a zero or more tokens with ID equal to <code>ls:loc</code> (i.e. optional list of locations). |
| </li> |
| <li> |
| If and when our intent is matched the method <code>onMatch(...)</code> on line 8 is invoked. It simply |
| returns a text response that indicates the light status at a requested location. That's where you can |
| also add <a target=_ href="https://developer.apple.com/homekit/">HomeKit</a>, |
| <a href="https://www.arduino.cc/" target=_>Ardunio</a> or other integrations to make a real |
| lightswitch in your home. |
| </li> |
| </ul> |
| </section> |
| <section> |
| <h3 class="section-title">Start Data Probe</h3> |
| <p> |
| NLPCraft data models get deployed into data probe. Let's start data probe to deploy our newly |
| created light switch data model. To start data probe we need to configure Run Configuration in IDEA with |
| the following parameters: |
| </p> |
| <ul> |
| <li> |
| <b>Main class:</b> <code>org.apache.nlpcraft.NCStart</code> |
| </li> |
| <li> |
| <b>VM arguments:</b> <code>-Dconfig.override_with_env_vars=true</code> |
| </li> |
| <li> |
| <b>Environment variable:</b> <code>CONFIG_FORCE_nlpcraft_probe_models.0=org.apache.nlpcraft.examples.lightswitch.LightSwitchModel</code> |
| </li> |
| <li> |
| <b>Program arguments: </b> <code>-probe</code> |
| </li> |
| </ul> |
| <div class="bq info"> |
| <p> |
| <b>NOTE:</b> instead of supplying a <a href="server-and-probe.html">full configuration file</a> we |
| just use the default configuration and override one configuration property using |
| configuration override via environment variables. |
| </p> |
| </div> |
| <p> |
| Start this run configuration and make sure you have positive console output indicating that our model |
| has been successfully loaded and probe started. |
| </p> |
| </section> |
| <section> |
| <h3 class="section-title">Start REST Server</h3> |
| <p> |
| REST server listens for requests from client applications and routes them to the requested data models |
| via connected data probes. REST server starts the same way as the data probe. Configure new |
| Run Configuration in IDEA with the following parameters: |
| </p> |
| <ul> |
| <li> |
| <b>Main class:</b> <code>org.apache.nlpcraft.NCStart</code> |
| </li> |
| <li> |
| <b>Program arguments: </b> <code>-server</code> |
| </li> |
| </ul> |
| <p> |
| Once started ensure that your REST server console output shows that data probe is connected and the |
| REST server is listening on the default <code>localhost:8081</code> endpoint. |
| </p> |
| <p> |
| At this point we've developed our data model, deployed it into the data probe, and started the REST server. |
| Our NLI-based light switch is ready to accept user requests. Instead of using direct REST calls we'll |
| demonstrate the built-in <a href="/testing-data-model.html">test framework</a> that allows you to write |
| convenient unit tests against your data model. |
| </p> |
| </section> |
| <section> |
| <h3 class="section-title">Testing</h3> |
| <p> |
| NLPCraft comes with easy to use <a target="javadoc" href="/apis/latest/org/apache/nlpcraft/model/test/package-summary.html">test framework</a> for data models that can be used with |
| any unit testing framework like JUnit or ScalaTest. It is essentially a simplified |
| version of Java REST client that is custom designed for data model testing. |
| </p> |
| <p> |
| We would like to test the following user requests: |
| </p> |
| <ul> |
| <li><code>"Turn the lights off in the entire house."</code></li> |
| <li><code>"Switch on the illumination in the master bedroom closet."</code></li> |
| <li><code>"Get the lights on."</code></li> |
| <li><code>"Please, put the light out in the upstairs bedroom."</code></li> |
| <li><code>"Set the lights on in the entire house."</code></li> |
| <li><code>"Turn the lights off in the guest bedroom."</code></li> |
| <li><code>"Could you please switch off all the lights?"</code></li> |
| <li><code>"Dial off illumination on the 2nd floor."</code></li> |
| <li><code>"Please, no lights!"</code></li> |
| <li><code>"Kill off all the lights now!"</code></li> |
| <li><code>"No lights in the bedroom, please."</code></li> |
| </ul> |
| <p> |
| Let's create new Java class <code>LightSwitchTest.java</code> in <code>src/<b>test</b>/java/examples</code> |
| folder with the following code: |
| </p> |
| <pre class="brush: java, highlight: [17]"> |
| package examples; |
| |
| import org.junit.jupiter.api.*; |
| import org.apache.nlpcraft.common.*; |
| import org.apache.nlpcraft.model.tools.test.*; |
| import java.io.*; |
| |
| import static org.junit.jupiter.api.Assertions.*; |
| |
| public class LightSwitchTest { |
| private NCTestClient cli; |
| |
| @BeforeEach |
| void setUp() throws NCException, IOException { |
| cli = new NCTestClientBuilder().newBuilder().build(); |
| |
| cli.open("nlpcraft.lightswitch.ex"); |
| } |
| |
| @AfterEach |
| void tearDown() throws NCException, IOException { |
| cli.close(); |
| } |
| |
| @Test |
| public void test() throws NCException, IOException { |
| assertTrue(cli.ask("Turn the lights off in the entire house.").isOk()); |
| assertTrue(cli.ask("Switch on the illumination in the master bedroom closet.").isOk()); |
| assertTrue(cli.ask("Get the lights on.").isOk()); |
| assertTrue(cli.ask("Please, put the light out in the upstairs bedroom.").isOk()); |
| assertTrue(cli.ask("Set the lights on in the entire house.").isOk()); |
| assertTrue(cli.ask("Turn the lights off in the guest bedroom.").isOk()); |
| assertTrue(cli.ask("Could you please switch off all the lights?").isOk()); |
| assertTrue(cli.ask("Dial off illumination on the 2nd floor.").isOk()); |
| assertTrue(cli.ask("Please, no lights!").isOk()); |
| assertTrue(cli.ask("Kill off all the lights now!").isOk()); |
| assertTrue(cli.ask("No lights in the bedroom, please.").isOk()); |
| } |
| } |
| </pre> |
| <p> |
| Right click on this class in the project view and run it. You should be getting standard output in |
| JUnit panel as well as the lights status based on the above requests. |
| </p> |
| </section> |
| <section> |
| <h2 class="section-title">Congratulation! 👌</h2> |
| <p> |
| You've created your first data model, deployed it into the data probe, started the |
| REST server and tested the model using JUnit 5 and 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="#setup">Project Setup</a></li> |
| <li><a href="#data_model">Data Model</a></li> |
| {% include quick-links.html %} |
| </ul> |
| </div> |
| |
| |
| |
| |