This is an automated email from the ASF dual-hosted git repository.

sergeykamov pushed a commit to branch NLPCRAFT-513
in repository https://gitbox.apache.org/repos/asf/incubator-nlpcraft-website.git


The following commit(s) were added to refs/heads/NLPCRAFT-513 by this push:
     new 8ecf8fb  WIP.
8ecf8fb is described below

commit 8ecf8fbe4b8e3eef02779f24cf0abb7403e1e7d3
Author: skhdl <[email protected]>
AuthorDate: Tue Oct 18 12:59:48 2022 +0400

    WIP.
---
 _includes/left-side-menu.html |  13 +-
 examples/calculator.html      | 268 +++++++++++++++++++++++
 examples/light_switch.html    |  33 +--
 examples/light_switch_fr.html | 484 ++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 780 insertions(+), 18 deletions(-)

diff --git a/_includes/left-side-menu.html b/_includes/left-side-menu.html
index c62d1eb..1b9ffcb 100644
--- a/_includes/left-side-menu.html
+++ b/_includes/left-side-menu.html
@@ -125,10 +125,10 @@
     </li>
     <li class="side-nav-title">Examples</li>
     <li>
-        {% if page.id == "alarm_clock" %}
-        <a class="active" href="/examples/alarm_clock.html">Alarm Clock</a>
+        {% if page.id == "calculator" %}
+        <a class="active" href="/examples/calculator.html">Calculator</a>
         {% else %}
-        <a href="/examples/alarm_clock.html">Alarm Clock</a>
+        <a href="/examples/calculator.html">Calculator</a>
         {% endif %}
     </li>
     <li>
@@ -138,6 +138,13 @@
         <a href="/examples/light_switch.html">Light Switch</a>
         {% endif %}
     </li>
+    <li>
+        {% if page.id == "light_switch_fr" %}
+        <a class="active" href="/examples/light_switch_fr.html">Light Switch 
FR</a>
+        {% else %}
+        <a href="/examples/light_switch_fr.html">Light Switch FR</a>
+        {% endif %}
+    </li>
     <li>
         {% if page.id == "weather_bot" %}
         <a class="active" href="/examples/weather_bot.html">Weather Bot</a>
diff --git a/examples/calculator.html b/examples/calculator.html
new file mode 100644
index 0000000..a4715bc
--- /dev/null
+++ b/examples/calculator.html
@@ -0,0 +1,268 @@
+---
+active_crumb: Calculator <code><sub>ex</sub></code>
+layout: documentation
+id: calculator
+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 a very simple calculator implementation.
+            It supports restricted set of arithmetic operations under integer 
numeric values.
+        </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/caclulator";>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 SBT
+            to accomplish this task. Make sure that <code>build.sbt</code> 
file has the following content:
+        </p>
+        <pre class="brush: js, highlight: []">
+            ThisBuild / version := "0.1.0-SNAPSHOT"
+            ThisBuild / scalaVersion := "3.1.3"
+            lazy val root = (project in file("."))
+              .settings(
+                name := "NLPCraft Calculator Example",
+                version := "{{site.latest_version}}",
+                libraryDependencies += "org.apache.nlpcraft" % "nlpcraft" % 
"{{site.latest_version}}",
+                libraryDependencies += "org.scalatest" %% "scalatest" % 
"3.2.14" % "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>CalculatorModel.scala</code> - Scala class, model 
implementation.</li>
+            <li><code>CalculatorModelSpec.scala</code> - Scala tests class, 
which allows to test your model.</li>
+        </ul>
+        <pre class="brush: plain, highlight: [8, 12]">
+            |  build.sbt
+            +--project
+            |    build.properties
+            \--src
+               +--main
+               |  \--scala
+               |     \--demo
+               |          CalculatorModel.scala
+               \--test
+                  \--scala
+                     \--demo
+                          CalculatorModelSpec.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>
+            All elements definitions can be provided programmatically inside 
Scala model <code>CalculatorModel</code> class as well.
+        </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>CalculatorModel.scala</b></code> 
file and replace its content with the following code:
+        </p>
+        <pre class="brush: scala, highlight: [11, 12, 14, 21, 22, 23, 31, 35, 
41, 47, 56, 61]">
+            package demo
+
+            import edu.stanford.nlp.pipeline.StanfordCoreNLP
+            import org.apache.nlpcraft.*
+            import org.apache.nlpcraft.annotations.*
+            import org.apache.nlpcraft.nlp.entity.parser.stanford.*
+            import org.apache.nlpcraft.nlp.parsers.*
+            import 
org.apache.nlpcraft.nlp.token.parser.stanford.NCStanfordNLPTokenParser
+            import java.util.Properties
+
+            object CalculatorModel:
+                private val OPS: Map[String, (Int, Int) => Int] =
+                    Map("+" -> (_ + _), "-" -> (_ - _), "*" -> (_ * _), "/" -> 
(_ / _))
+                private val PIPELINE: NCPipeline =
+                    val props = new Properties()
+                    props.setProperty("annotators", "tokenize, ssplit, pos, 
lemma, ner")
+
+                    val stanford = new StanfordCoreNLP(props)
+
+                    new NCPipelineBuilder().
+                        withTokenParser(new 
NCStanfordNLPTokenParser(stanford)).
+                        withEntityParser(new NCNLPEntityParser(t => 
OPS.contains(t.getText))).
+                        withEntityParser(new 
NCStanfordNLPEntityParser(stanford, Set("number"))).
+                        build
+
+                private def nne(e: NCEntity): Int =
+                    
java.lang.Double.parseDouble(e[String]("stanford:number:nne")).intValue
+
+            import CalculatorModel.*
+
+            class CalculatorModel extends NCModelAdapter(
+                NCModelConfig("nlpcraft.calculator.ex", "Calculator Example 
Model", "1.0"),
+                PIPELINE
+            ) :
+                private var mem: Option[Int] = None
+
+                private def calc(x: Int, op: String, y: Int): NCResult =
+                    mem = Some(OPS.getOrElse(op, throw new 
IllegalStateException()).apply(x, y))
+                    NCResult(mem.get)
+
+                @NCIntent(
+                    "intent=calc options={ 'ordered': true }" +
+                    "   term(x)={# == 'stanford:number'}" +
+                    "   term(op)={has(list('+', '-', '*', '/'), 
meta_ent('nlp:token:text')) == true}" +
+                    "   term(y)={# == 'stanford:number'}"
+                )
+                def onMatch(
+                    ctx: NCContext,
+                    im: NCIntentMatch,
+                    @NCIntentTerm("x") x: NCEntity,
+                    @NCIntentTerm("op") op: NCEntity,
+                    @NCIntentTerm("y") y: NCEntity
+                ): NCResult =
+                    calc(nne(x), op.mkText, nne(y))
+
+                @NCIntent(
+                    "intent=calcMem options={ 'ordered': true }" +
+                    "   term(op)={has(list('+', '-', '*', '/'), 
meta_ent('nlp:token:text')) == true}" +
+                    "   term(y)={# == 'stanford:number'}"
+                )
+                def onMatchMem(
+                    ctx: NCContext,
+                    im: NCIntentMatch,
+                    @NCIntentTerm("op") op: NCEntity,
+                    @NCIntentTerm("y") y: NCEntity
+                ): NCResult =
+                    calc(mem.getOrElse(throw new NCRejection("Memory is 
empty.")), op.mkText, nne(y))
+        </pre>
+        <p>
+            There are two intents with simple logic. First returns arithmetic 
operation result under two input parameters,
+            second uses last operation result instead of first input value.
+            Also, the implication here is that arithmetic operations notations
+            (<code>+</code>, <code>-</code>, <code>*</code>, <code>/</code>) 
are used as is for simplifying the given example,
+            without any synonyms for them.
+            Let's review this implementation step by step:
+        </p>
+        <ul>
+            <li>
+                On <code>line 11</code> declared <code>CalculatorModel</code>, 
model companion object, which contains
+                static context and helper methods.
+            </li>
+            <li>
+                On <code>line 12</code> defined arithmetic operations map, 
with notations as keys and functions definitions as values.
+            </li>
+            <li>
+                On <code>line 14</code> defined model pipeline, based on three 
built components.
+                On <code>line 21</code> defined Stanford token parser 
<code>NCStanfordNLPTokenParser</code>,
+                we have to use it because our model uses Stanford NLP 
components.
+                On <code>line 22</code> entity parser 
<code>NCNLPEntityParser</code>,
+                which allows find arithmetic operations notations.
+                On <code>line 23</code> defined entity parser 
<code>NCStanfordNLPEntityParser</code>,
+                which allows to find numerics in the text input.
+            </li>
+            <li>
+                On <code>line 31</code> declared <code>CalculatorModel</code> 
model class.
+            </li>
+            <li>
+                On <code>line 35</code> declared variable named 
<code>mem</code>, last operation result.
+            </li>
+            <li>
+                <code>Lines 41 and 47</code> annotates intents 
<code>calc</code> and its callback method <code>onMatch</code>.
+                Intent <code>calc</code> requires one arithmetic operation 
notation and two numerics as this operation arguments.
+            </li>
+            <li>
+                <code>Lines 56 and 61</code> annotates intents 
<code>calcMem</code> and its callback method <code>onMatchMem</code>.
+                Intent <code>calcMem</code> requires one arithmetic operation 
notation and one numeric as this operation second arguments.
+                As first argument it uses last operation result, if there is 
existed.
+            </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>
+            The test defined in <code>CalculatorModelSpec</code> allows to 
check that all input test sentences are
+            processed correctly and trigger the expected intents 
<code>calc</code> and <code>calcMem</code>:
+        </p>
+        <pre class="brush: scala, highlight: [9, 10, 15, 16]">
+            package demo
+
+            import org.apache.nlpcraft.*
+            import org.scalatest.funsuite.AnyFunSuite
+            import scala.util.Using
+
+            class CalculatorModelSpec extends AnyFunSuite:
+                test("test") {
+                    Using.resource(new NCModelClient(new CalculatorModel())) { 
client =>
+                        def check(txt: String, v: Int): Unit =
+                            require(v == client.ask(txt, "userId").getBody)
+
+                        check("2 + 2", 4)
+                        check("3 * 4", 12)
+                        check("/ two", 6)
+                        check("+ twenty two", 28)
+                        check("7 + 2", 9)
+                    }
+                }
+        </pre>
+        <ul>
+            <li>
+                On <code>line 9</code> the client for our model is created.
+            </li>
+            <li>
+                On <code>line 10</code> a method <code>ask</code> is called. 
Its result is checked with expected value.
+            </li>
+            <li>
+                Note that for inputs on <code>lines 15, 16</code> expected 
<code>calcMem</code> intent triggering,
+                and <code>calc</code> intent for another inputs lines.
+            </li>
+        </ul>
+        <p>
+            You can run this test via SBT task <code>executeTests</code> or 
using IDE.
+        </p>
+        <pre class="brush: scala, highlight: []">
+            PS C:\apache\incubator-nlpcraft-examples\calculator> 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 calculator 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="#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="#testing">Testing</a></li>
+        {% include quick-links.html %}
+    </ul>
+</div>
+
+
+
+
+
+
diff --git a/examples/light_switch.html b/examples/light_switch.html
index 2008c6f..96db20d 100644
--- a/examples/light_switch.html
+++ b/examples/light_switch.html
@@ -66,16 +66,16 @@ fa_icon: fa-cube
             +--project
             |    build.properties
             \--src
-                +--main
-                |   +--resources
-                |   |  lightswitch_model.yaml
-                |   \--scala
-                |       \--demo
-                |          LightSwitchModel.scala
-                \---test
-                    \---scala
-                        \---demo
-                            LightSwitchModelSpec.scala
+               +--main
+               |  +--resources
+               |  |    lightswitch_model.yaml
+               |  \--scala
+               |     \--demo
+               |          LightSwitchModel.scala
+               \--test
+                  \--scala
+                     \--demo
+                          LightSwitchModelSpec.scala
         </pre>
     </section>
     <section id="model">
@@ -133,7 +133,7 @@ fa_icon: fa-cube
             <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>LightSwitchScalaModel</code> class. Note that these model
+                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>
@@ -141,14 +141,14 @@ fa_icon: fa-cube
             <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>LightSwitchScalaModel</code> class as well.
+                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>LightSwitch.scala</b></code> 
file and replace its content with the following code:
+            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
@@ -260,13 +260,16 @@ fa_icon: fa-cube
                 calling the intent.
             </li>
             <li>
-                <code>Lines 13-24</code> define all the test input sentences 
that should all
+                <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.
+            You can run this test via SBT task <code>executeTests</code> or 
using IDE.
         </p>
+        <pre class="brush: scala, highlight: []">
+            PS C:\apache\incubator-nlpcraft-examples\lightswitch> 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>
diff --git a/examples/light_switch_fr.html b/examples/light_switch_fr.html
new file mode 100644
index 0000000..29180f9
--- /dev/null
+++ b/examples/light_switch_fr.html
@@ -0,0 +1,484 @@
+---
+active_crumb: Light Switch FR <code><sub>ex</sub></code>
+layout: documentation
+id: light_switch_fr
+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 a very simple French language implementation 
for NLI-powered light switch. You can say something like
+            "Éteignez les lumières dans toute la maison" or "Allumez les 
lumières".
+            By modifying intent callbacks using, for example, HomeKit or 
Arduino-based controllers you can provide the
+            actual light switching.
+        </p>
+        <p>
+            <b>Complexity:</b> <span class="complexity-two-star"><i class="fas 
fa-square"></i> <i class="fas fa-square"></i> <i class="far 
fa-square"></i></span><br/>
+            <span class="ex-src">Source code: <a target="github" 
href="https://github.com/apache/incubator-nlpcraft/tree/master/nlpcraft-examples/lightswitch_fr";>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 SBT
+            to accomplish this task. Make sure that <code>build.sbt</code> 
file has the following content:
+        </p>
+        <pre class="brush: js, highlight: [8, 9, 10]">
+            ThisBuild / version := "0.1.0-SNAPSHOT"
+            ThisBuild / scalaVersion := "3.1.3"
+            lazy val root = (project in file("."))
+              .settings(
+                name := "NLPCraft LightSwitch FR Example",
+                version := "{{site.latest_version}}",
+                libraryDependencies += "org.apache.nlpcraft" % "nlpcraft" % 
"{{site.latest_version}}",
+                libraryDependencies += "org.apache.lucene" % 
"lucene-analyzers-common" % "8.11.2",
+                libraryDependencies += "org.languagetool" % 
"languagetool-core" % "5.8",
+                libraryDependencies += "org.languagetool" % "language-fr" % 
"5.8"
+                libraryDependencies += "org.scalatest" %% "scalatest" % 
"3.2.14" % "test"
+              )
+        </pre>
+
+        <p>
+            On <code>lines 8, 9 and 10</code> added libraries, which used for 
support base NLP operations with French language.
+        </p>
+
+        <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_fr.yaml</code> - YAML configuration 
file, which contains model description.</li>
+            <li><code>LightSwitchFrModel.scala</code> - Scala class, model 
implementation.</li>
+            <li><code>NCFrSemanticEntityParser.scala</code> - Scala class, 
semantic entity parser, custom implementation for French language.</li>
+            <li><code>NCFrLemmaPosTokenEnricher.scala</code> - Scala class, 
lemma and point of speech token enricher, custom implementation for French 
language.</li>
+            <li><code>NCFrStopWordsTokenEnricher.scala</code> - Scala class, 
stop-words token enricher, custom implementation for French language.</li>
+            <li><code>NCFrTokenParser.scala</code> - Scala class, token 
parser, custom implementation for French language.</li>
+            <li><code>LightSwitchFrModelSpec.scala</code> - Scala tests class, 
which allows to test your model.</li>
+        </ul>
+        <pre class="brush: plain, highlight: [7, 10, 14, 17, 18, 20, 24]">
+            |  build.sbt
+            +--project
+            |    build.properties
+            \--src
+               +--main
+               |  +--resources
+               |  |  lightswitch_model_fr.yaml
+               |  \--scala
+               |     \--demo
+               |        |  LightSwitchFrModel.scala
+               |        \--nlp
+               |           +--entity
+               |           |  \--parser
+               |           |       NCFrSemanticEntityParser.scala
+               |           \--token
+               |              +--enricher
+               |              |    NCFrLemmaPosTokenEnricher.scala
+               |              |    NCFrStopWordsTokenEnricher.scala
+               |              \--parser
+               |                   NCFrTokenParser.scala
+               \--test
+                   \--scala
+                       \--demo
+                            LightSwitchFrModelSpec.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 using
+            <code>NCModelAdapter</code> in our Scala-based model 
implementation.
+            Open <code>src/main/resources/<b>light_switch_fr.yaml</b></code>
+            file and replace its content with the following YAML:
+        </p>
+        <pre class="brush: js, highlight: [1, 10, 17, 25]">
+            macros:
+              "&lt;ACTION&gt;" : "{allumer|laisser|mettre}"
+              "&lt;KILL&gt;" : 
"{éteindre|couper|tuer|arrêter|éliminer|baisser|no}"
+              "&lt;ENTIRE_OPT&gt;" : "{entière|pleine|tout|total|_}"
+              "&lt;FLOOR_OPT&gt;" : "{là-haut|à l'étage|en 
bas|{1er|premier|2ème|deuxième|3ème|troisième|4ème|quatrième|5ème|cinquième|dernier|haut|rez-de-chaussée|en
 bas} étage|_}"
+              "&lt;TYPE&gt;" : 
"{chambre|salle|pièce|placard|mansardé|loft|mezzanine|rangement 
{chambre|salle|pièce|_}}"
+              "&lt;LIGHT&gt;" : "{tout|_} 
{cela|lumière|éclairage|illumination|lampe}"
+
+            elements:
+              - id: "ls:loc"
+                description: "Location of lights."
+                synonyms:
+                  - "&lt;ENTIRE_OPT&gt; &lt;FLOOR_OPT&gt; 
{cuisine|bibliothèque|placard|garage|bureau|salle de jeux|{salle à 
manger|buanderie|jeu} &lt;TYPE&gt;}"
+                  - "&lt;ENTIRE_OPT&gt; &lt;FLOOR_OPT&gt; 
{maître|gamin|bébé|enfant|hôte|client|_} {coucher|bains|toilette|rangement} 
{&lt;TYPE&gt;|_}"
+                  - "&lt;ENTIRE_OPT&gt; {maison|foyer|bâtiment|{1er|premier} 
étage|chaussée|{2ème|deuxième} étage}"
+
+              - id: "ls:on"
+                groups:
+                  - "act"
+                description: "Light switch ON action."
+                synonyms:
+                  - "{&lt;ACTION&gt;|_} &lt;LIGHT&gt;"
+                  - "{&lt;LIGHT&gt;|_} &lt;ACTION&gt;"
+
+              - id: "ls:off"
+                groups:
+                  - "act"
+                description: "Light switch OFF action."
+                synonyms:
+                  - "&lt;KILL&gt; &lt;LIGHT&gt;"
+                  - "&lt;LIGHT&gt; &lt;KILL&gt;"
+        </pre>
+        <p>There are number of important points here:</p>
+        <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>LightSwitchFrModel</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>LightSwitchFrModel</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>LightSwitchFrModel.scala</b></code> file and 
replace its content with the following code:
+        </p>
+        <pre class="brush: scala, highlight: [11, 12, 13, 20, 21, 24, 25, 32]">
+            package demo
+
+            import com.google.gson.Gson
+            import org.apache.nlpcraft.*
+            import org.apache.nlpcraft.annotations.*
+
+            import demo.nlp.entity.parser.NCFrSemanticEntityParser
+            import demo.lightswitch.nlp.token.enricher.*
+            import demo.lightswitch.nlp.token.parser.NCFrTokenParser
+
+            import scala.jdk.CollectionConverters.*
+
+            class LightSwitchFrModel extends NCModelAdapter(
+                NCModelConfig("nlpcraft.lightswitch.fr.ex", "LightSwitch 
Example Model FR", "1.0"),
+                new NCPipelineBuilder().
+                    withTokenParser(new NCFrTokenParser()).
+                    withTokenEnricher(new NCFrLemmaPosTokenEnricher()).
+                    withTokenEnricher(new NCFrStopWordsTokenEnricher()).
+                    withEntityParser(new 
NCFrSemanticEntityParser("lightswitch_model_fr.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 action = if actEnt.getId == "ls:on" then "allumer" 
else "éteindre"
+                    val locations = if locEnts.isEmpty then "toute la maison" 
else locEnts.map(_.mkText).mkString(", ")
+
+                    // Add HomeKit, Arduino or other integration here.
+                    // By default - just return a descriptive action string.
+                    NCResult(new Gson().toJson(Map("locations" -> locations, 
"action" -> action).asJava))
+        </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 11</code> our class extends 
<code>NCModelAdapter</code> that allows us to pass
+                prepared configuration and pipeline into model.
+            </li>
+            <li>
+                On <code>line 12</code> created model configuration with most 
default parameters.
+            </li>
+            <li>
+                On <code>line 13</code> created pipeline, based on custom 
French language components, which are described below.
+                <ul>
+                    <li><code>NCFrTokenParser</code>. Token parser.</li>
+                    <li><code>NCFrLemmaPosTokenEnricher</code>. Lemma and 
point of speech token enricher.</li>
+                    <li><code>NCFrStopWordsTokenEnricher</code>. Stop-words 
token enricher.</li>
+                    <li><code>NCFrSemanticEntityParser</code>. Semantic entity 
parser extending.</li>
+                </ul>
+                Note that <code>NCFrSemanticEntityParser</code> is based on 
semantic model definition,
+                described in <code>lightswitch_model_fr.yaml</code> file.
+            </li>
+            <li>
+                <code>Lines 20 and 21</code> annotates intents <code>ls</code> 
and its callback method <code>onMatch</code>.
+                Intent <code>ls</code> requires one action (a token belonging 
to the group act) and optional list of light locations
+                (zero or more tokens with ID ls:loc) - by default we assume 
the entire house as a default location.
+            </li>
+            <li>
+                <code>Lines 24 and 25</code> map terms from detected intent to 
the formal method parameters of the
+                <code>onMatch</code> method.
+            </li>
+            <li>
+                On the <code>line 32</code> the intent callback simply returns 
a confirmation message.
+            </li>
+        </ul>
+
+        <p>
+            Lets review each custom pipeline components.
+        </p>
+
+        <p>
+            Open 
<code>src/main/scala/demo/nlp/token/parser/<b>NCFrTokenParser.scala</b></code> 
file and replace its content with the following code:
+        </p>
+
+        <pre class="brush: scala, highlight: [19]">
+            package demo.nlp.token.parser
+
+            import org.apache.nlpcraft.*
+            import org.languagetool.tokenizers.fr.FrenchWordTokenizer
+            import scala.jdk.CollectionConverters.*
+
+            class NCFrTokenParser extends NCTokenParser:
+                private val tokenizer = new FrenchWordTokenizer
+
+                override def tokenize(text: String): List[NCToken] =
+                    val toks = collection.mutable.ArrayBuffer.empty[NCToken]
+                    var sumLen = 0
+
+                    for ((word, idx) <- 
tokenizer.tokenize(text).asScala.zipWithIndex)
+                        val start = sumLen
+                        val end = sumLen + word.length
+
+                        if word.strip.nonEmpty then
+                            toks += new NCPropertyMapAdapter with NCToken:
+                                override def getText: String = word
+                                override def getIndex: Int = idx
+                                override def getStartCharIndex: Int = start
+                                override def getEndCharIndex: Int = end
+
+                        sumLen = end
+
+                    toks.toList
+        </pre>
+        <p>
+            <code>NCFrTokenParser</code> is simple wrapper, which implements 
<code>NCTokenParser</code> based on
+            open source solution <a href="https://languagetool.org";>Language 
Tool</a>  solution.
+            On <code>line 19</code> <code>NCToken</code> instances created.
+        </p>
+
+        <p>
+            Open 
<code>src/main/scala/demo/nlp/token/enricher/<b>NCFrLemmaPosTokenEnricher.scala</b></code>
 file and replace its content with the following code:
+        </p>
+        <pre class="brush: scala, highlight: [27, 28]">
+            package demo.nlp.token.enricher
+
+            import org.apache.nlpcraft.*
+            import org.languagetool.AnalyzedToken
+            import org.languagetool.tagging.fr.FrenchTagger
+            import scala.jdk.CollectionConverters.*
+
+            class NCFrLemmaPosTokenEnricher extends NCTokenEnricher:
+                private def nvl(v: String, dflt : => String): String = if v != 
null then v else dflt
+
+                override def enrich(req: NCRequest, cfg: NCModelConfig, toks: 
List[NCToken]): Unit =
+                    val tags = 
FrenchTagger.INSTANCE.tag(toks.map(_.getText).asJava).asScala
+
+                    require(toks.sizeIs == tags.size)
+
+                    toks.zip(tags).foreach { case (tok, tag) =>
+                        val readings = tag.getReadings.asScala
+
+                        val (lemma, pos) = readings.size match
+                            // No data. Lemma is word as is, POS is undefined.
+                            case 0 => (tok.getText, "")
+                            // Takes first. Other variants ignored.
+                            case _ =>
+                                val aTok: AnalyzedToken = readings.head
+                                (nvl(aTok.getLemma, tok.getText), 
nvl(aTok.getPOSTag, ""))
+
+                        tok.put("pos", pos)
+                        tok.put("lemma", lemma)
+
+                        () // Otherwise NPE.
+                    }
+        </pre>
+        <p>
+            <code>NCFrLemmaPosTokenEnricher</code> lemma and point of speech 
tokens enricher, based on
+            open source solution <a href="https://languagetool.org";>Language 
Tool</a> solution.
+            On <code>line 27 and 28</code> tokens are enriched by 
<code>pos</code> and <code>lemma</code> data.
+        </p>
+
+        <p>
+            Open 
<code>src/main/scala/demo/nlp/token/enricher/<b>NCFrStopWordsTokenEnricher.scala</b></code>
 file and replace its content with the following code:
+        </p>
+
+        <pre class="brush: scala, highlight: [17]">
+            package demo.nlp.token.enricher
+
+            import org.apache.lucene.analysis.fr.FrenchAnalyzer
+            import org.apache.nlpcraft.*
+
+            class NCFrStopWordsTokenEnricher extends NCTokenEnricher:
+                private final val stops = FrenchAnalyzer.getDefaultStopSet
+
+                private def getPos(t: NCToken): String = 
t.get("pos").getOrElse(throw new NCException("POS not found in token."))
+                private def getLemma(t: NCToken): String = 
t.get("lemma").getOrElse(throw new NCException("Lemma not found in token."))
+
+                override def enrich(req: NCRequest, cfg: NCModelConfig, toks: 
List[NCToken]): Unit =
+                    for (t <- toks)
+                        val lemma = getLemma(t)
+                        lazy val pos = getPos(t)
+
+                        t.put(
+                            "stopword",
+                            lemma.length == 1 && 
!Character.isLetter(lemma.head) && !Character.isDigit(lemma.head) ||
+                            stops.contains(lemma.toLowerCase) ||
+                            pos.startsWith("I") ||
+                            pos.startsWith("O") ||
+                            pos.startsWith("P") ||
+                            pos.startsWith("D")
+                        )
+        </pre>
+        <p>
+            <code>NCFrStopWordsTokenEnricher</code> stop-words tokens 
enricher, based on
+            open source solution <a href="https://lucene.apache.org/";>Apache 
Lucene</a> solution.
+            On <code>line 17</code> tokens are enriched by 
<code>stopword</code> flags data.
+        </p>
+
+        <p>
+            Open 
<code>src/main/scala/demo/nlp/entity/parser/<b>NCFrSemanticEntityParser.scala</b></code>
 file and replace its content with the following code:
+        </p>
+
+        <pre class="brush: scala, highlight: [8, 12]">
+            package demo.nlp.entity.parser
+
+            import opennlp.tools.stemmer.snowball.SnowballStemmer
+            import 
org.apache.nlpcraft.examples.lightswitch.nlp.token.parser.NCFrTokenParser
+            import org.apache.nlpcraft.nlp.parsers.*
+
+            class NCFrSemanticEntityParser(src: String) extends 
NCSemanticEntityParser(
+                new NCSemanticStemmer:
+                    private val stemmer = new 
SnowballStemmer(SnowballStemmer.ALGORITHM.FRENCH)
+                    override def stem(txt: String): String = 
stemmer.synchronized { stemmer.stem(txt.toLowerCase).toString }
+                ,
+                new NCFrTokenParser(),
+                mdlSrcOpt = Option(src)
+            )
+
+        </pre>
+        <p>
+            <code>NCFrSemanticEntityParser</code> extends 
<code>NCSemanticEntityParser</code>
+            It uses stemmer implementation from <a 
href="https://opennlp.apache.org/";>Apache OpenNLP</a> solution
+            on <code>line 8</code> and already described 
<code>NCFrTokenParser</code> token parser implementation on <code>line 
12</code>.
+        </p>
+    </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>LightSwitchFrModelSpec</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 LightSwitchFrModelSpec extends AnyFunSuite:
+                test("test") {
+                    Using.resource(new NCModelClient(new LightSwitchFrModel)) 
{ client =>
+                        def check(txt: String): Unit =
+                            require(client.debugAsk(txt, "userId", 
true).getIntentId == "ls")
+
+                        check("Éteignez les lumières dans toute la maison.")
+                        check("Éteignez toutes les lumières maintenant.")
+                        check("Allumez l'éclairage dans le placard de la 
chambre des maîtres.")
+                        check("Éteindre les lumières au 1er étage.")
+                        check("Allumez les lumières.")
+                        check("Allumes dans la cuisine.")
+                        check("S'il vous plait, éteignez la lumière dans la 
chambre à l'étage.")
+                        check("Allumez les lumières dans toute la maison.")
+                        check("Éteignez les lumières dans la chambre d'hôtes.")
+                        check("Pourriez-vous éteindre toutes les lumières s'il 
vous plait?")
+                        check("Désactivez l'éclairage au 2ème étage.")
+                        check("Éteignez les lumières dans la chambre au 1er 
étage.")
+                        check("Lumières allumées à la cuisine du deuxième 
étage.")
+                        check("S'il te plaît, pas de lumières!")
+                        check("Coupez toutes les lumières maintenant!")
+                        check("Éteindre les lumières dans le garage.")
+                        check("Lumières éteintes dans la cuisine!")
+                        check("Augmentez l'éclairage dans le garage et la 
chambre des maîtres.")
+                        check("Baissez toute la lumière maintenant!")
+                        check("Pas de lumières dans la chambre, s'il vous 
plait.")
+                        check("Allumez le garage, s'il vous plait.")
+                        check("Tuez l'illumination maintenant.")
+                    }
+                }
+        </pre>
+        <ul>
+            <li>
+                On <code>line 9</code> the client for our model is created.
+            </li>
+            <li>
+                On <code>line 11</code> a special method <code>debugAsk</code> 
is called.
+                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: []">
+            PS C:\apache\incubator-nlpcraft-examples\lightswitch_fr> 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="#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="#testing">Testing</a></li>
+        {% include quick-links.html %}
+    </ul>
+</div>
+
+
+
+
+
+


Reply via email to