You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nlpcraft.apache.org by ar...@apache.org on 2020/06/01 23:35:30 UTC

[incubator-nlpcraft-website] branch master updated: Documentation & code cleanup on weather example.

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

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


The following commit(s) were added to refs/heads/master by this push:
     new 51b9c8a  Documentation & code cleanup on weather example.
51b9c8a is described below

commit 51b9c8af86b96973a4f00a8e8fe439259e5f9ae2
Author: Aaron Radzinzski <ar...@datalingvo.com>
AuthorDate: Mon Jun 1 16:32:56 2020 -0700

    Documentation & code cleanup on weather example.
---
 _layouts/documentation.html |   7 +
 examples/alarm_clock.html   |  21 +-
 examples/light_switch.html  |  22 ++-
 examples/weather_bot.html   | 468 +++++++++++++++++++++++++++++++++++++++++++-
 first-example.html          |   2 +-
 tools/embedded_probe.html   |   2 +-
 tools/test_framework.html   |   2 +-
 7 files changed, 506 insertions(+), 18 deletions(-)

diff --git a/_layouts/documentation.html b/_layouts/documentation.html
index 2a4d342..1d697b2 100644
--- a/_layouts/documentation.html
+++ b/_layouts/documentation.html
@@ -142,6 +142,13 @@ layout: interior
                 <a href="/examples/light_switch.html">Light Switch</a>
                 {% endif %}
             </li>
+            <li>
+                {% if page.id == "weather_bot" %}
+                <a class="active" href="/examples/weather_bot.html">Weather Bot</a>
+                {% else %}
+                <a href="/examples/weather_bot.html">Weather Bot</a>
+                {% endif %}
+            </li>
         </ul>
     </div>
 
diff --git a/examples/alarm_clock.html b/examples/alarm_clock.html
index 4bfc704..ca9d50f 100644
--- a/examples/alarm_clock.html
+++ b/examples/alarm_clock.html
@@ -105,7 +105,8 @@ id: alarm_clock
     <section id="model">
         <h3 class="section-title">Data Model</h3>
         <p>
-            We are going to start with declaring our semantic model using JSON. Create new <code>alarm_model.json</code>
+            We are going to start with declaring the static part of our semantic model using JSON which we will later load using
+            <code>NCModelFileAdapter</code> in our Java-based model implementation. Create new <code>alarm_model.json</code>
             file and add the following model declaration into it:
         </p>
         <pre class="brush: js, highlight: [7, 16, 25]">
@@ -275,7 +276,7 @@ public class AlarmModel extends NCModelFileAdapter {
             </li>
         </ul>
     </section>
-    <section>
+    <section id="start_probe">
         <h3 class="section-title">Start Data Probe</h3>
         <p>
             NLPCraft data models get deployed into data probe. Let's start data probe with our newly
@@ -290,7 +291,7 @@ public class AlarmModel extends NCModelFileAdapter {
                 <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=path.to.AlarmModel</code>
+                <b>Environment variable:</b> <code>CONFIG_FORCE_nlpcraft_probe_models.0=org.apache.nlpcraft.examples.alarm.AlarmModel</code>
             </li>
             <li>
                 <b>Program arguments: </b> <code>-probe</code>
@@ -307,7 +308,7 @@ public class AlarmModel extends NCModelFileAdapter {
             has been successfully loaded and probe started.
         </p>
     </section>
-    <section>
+    <section id="start_server">
         <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
@@ -328,14 +329,14 @@ public class AlarmModel extends NCModelFileAdapter {
         </p>
         <p>
             At this point we've developed our data model, deployed it into the data probe, and started the REST server.
-            To test it, we'll use the built-in <a href="/testing-data-model.html">test framework</a>
+            To test it, we'll use the built-in <a href="/tools/test_framework.html">test framework</a>
             that allows you to write convenient unit tests against your data model.
         </p>
     </section>
-    <section>
+    <section id="testing">
         <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
+            NLPCraft comes with easy to use <a href="/tools/test_framework.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.
@@ -413,6 +414,12 @@ class AlarmTest {
     <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="#add_nlpcraft">Add NLPCraft</a></li>
+        <li><a href="#model">Data Model</a></li>
+        <li><a href="#code">Model Class</a></li>
+        <li><a href="#start_probe">Start Probe</a></li>
+        <li><a href="#start_server">Start Server</a></li>
         {% include quick-links.html %}
     </ul>
 </div>
diff --git a/examples/light_switch.html b/examples/light_switch.html
index fc720db..6a9161c 100644
--- a/examples/light_switch.html
+++ b/examples/light_switch.html
@@ -105,7 +105,8 @@ id: light_switch
     <section id="model">
         <h3 class="section-title">Data Model</h3>
         <p>
-            We are going to start with declaring our semantic model using YAML. Create new <code>lightswitch_model.yaml</code>
+            We are going to start with declaring the static part of our semantic model using YAML which we will later load using
+            <code>NCModelFileAdapter</code> in our Sccla-based model implementation. Create new <code>lightswitch_model.yaml</code>
             file and add the following model declaration into it:
         </p>
         <pre class="brush: js, highlight: [10, 19, 26, 34, 42]">
@@ -229,7 +230,7 @@ class LightSwitchModel extends NCModelFileAdapter("org/apache/nlpcraft/examples/
             </li>
         </ul>
     </section>
-    <section>
+    <section id="start_probe">
         <h3 class="section-title">Start Data Probe</h3>
         <p>
             NLPCraft data models get deployed into data probe. Let's start data probe with our newly
@@ -244,7 +245,7 @@ class LightSwitchModel extends NCModelFileAdapter("org/apache/nlpcraft/examples/
                 <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=path.to.LightSwitchModel</code>
+                <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>
@@ -262,7 +263,7 @@ class LightSwitchModel extends NCModelFileAdapter("org/apache/nlpcraft/examples/
             has been successfully loaded and probe started.
         </p>
     </section>
-    <section>
+    <section id="start_server">
         <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
@@ -283,14 +284,14 @@ class LightSwitchModel extends NCModelFileAdapter("org/apache/nlpcraft/examples/
         </p>
         <p>
             At this point we've developed our data model, deployed it into the data probe, and started the REST server.
-            To test it, we'll use the built-in <a href="/testing-data-model.html">test framework</a>
+            To test it, we'll use the built-in <a href="/tools/test_framework.html">test framework</a>
             that allows you to write convenient unit tests against your data model.
         </p>
     </section>
-    <section>
+    <section id="testing">
         <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
+            NLPCraft comes with easy to use <a href="/tools/test_framework.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.
@@ -383,6 +384,13 @@ class LightSwitchTest {
     <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="#add_nlpcraft">Add NLPCraft</a></li>
+        <li><a href="#model">Data Model</a></li>
+        <li><a href="#code">Model Class</a></li>
+        <li><a href="#start_probe">Start Probe</a></li>
+        <li><a href="#start_server">Start Server</a></li>
+        <li><a href="#testing">Testing</a></li>
         {% include quick-links.html %}
     </ul>
 </div>
diff --git a/examples/weather_bot.html b/examples/weather_bot.html
index a5ae4f9..afb9e86 100644
--- a/examples/weather_bot.html
+++ b/examples/weather_bot.html
@@ -26,7 +26,7 @@ id: weather_bot
         <h2 class="section-title">Overview</h2>
         <p>
             This example demonstrates relatively complete NLI-based weather service with JSON output and a non-trivial
-            intent matching logic. It uses <a target="new" href="https://www.apixu.com">https://www.apixu.com</a>
+            intent matching logic. It uses Apple's <a target="new" href="https://darksky.net">DarkSky</a>
             REST service for the actual weather information.
         </p>
         <p>
@@ -34,11 +34,477 @@ id: weather_bot
             Source code: <a target="github" href="https://github.com/apache/incubator-nlpcraft/tree/master/src/main/scala/org/apache/nlpcraft/examples/weather">GitHub</a>
         </p>
     </section>
+    <section id="new_project">
+        <h3 class="section-title">Create New Project</h3>
+        <p>
+            You can create new Java 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>
+        <div class="bq info">
+            <p>
+                Note that this setup is same for all examples. Note also that you can use any other tools for
+                creating and managing Java project with or without Maven.
+            </p>
+        </div>
+        <p>
+            For our example we'll use JetBrain's <a target=_new href="https://www.jetbrains.com/idea/">IntelliJ IDEA</a>.
+            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>
+            Next we 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]">
+            &lt;dependencies&gt;
+                &lt;dependency&gt;
+                    &lt;groupId&gt;org.apache.nlpcraft&lt;/groupId&gt;
+                    &lt;artifactId&gt;nlpcraft&lt;/artifactId&gt;
+                    &lt;version&gt;{{site.latest_version}}&lt;/version&gt;
+                &lt;/dependency&gt;
+            &lt;/dependencies&gt;
+        </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]">
+            &lt;properties&gt;
+                &lt;project.build.sourceEncoding&gt;UTF-8&lt;/project.build.sourceEncoding&gt;
+                &lt;maven.compiler.source&gt;1.8&lt;/maven.compiler.source&gt;
+                &lt;maven.compiler.target&gt;1.8&lt;/maven.compiler.target&gt;
+            &lt;/properties&gt;
+        </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 data model.
+        </p>
+    </section>
+    <section id="model">
+        <h3 class="section-title">Data Model</h3>
+        <p>
+            We are going to start with declaring the static part of our semantic model using JSON which we will later load using
+            <code>NCModelFileAdapter</code> in our Java-based model implementation. Create new <code>weather_model.json</code>
+            file and add the following model declaration into it:
+        </p>
+        <pre class="brush: js, highlight: [10, 42, 50, 59]">
+{
+    "id": "nlpcraft.weather.ex",
+    "name": "Weather Example Model",
+    "version": "1.0",
+    "description": "Weather example model.",
+    "examples": [
+        "What's the local weather forecast?",
+        "What's the weather in Moscow?"
+    ],
+    "macros": [
+        {
+            "name": "&lt;OF&gt;",
+            "macro": "{of|for|per}"
+        },
+        {
+            "name": "&lt;CHANCE&gt;",
+            "macro": "{chance|possibility|probability|odds|likelihood|potential|risk|opportunity}"
+        },
+        {
+            "name": "&lt;PHENOMENON&gt;",
+            "macro": "{high sea|severe weather|hail|heat wave|cold wave|derecho|supercell|avalanche|cyclone|wildfire|landslide|firestorm|dust storm|thunder snow|winter storm|cloudburst|shower|condensation|precipitation|drizzle|rainstorm|rain storm|rainfall|rain|storm|sun|sunshine|cloud|hot|cold|dry|wet|wind||hurricane|typhoon|sand-storm|sand storm|tornado|humid|fog|snow|smog|black ice|haze|thundershower|thundersnow|sleet|drought|wildfire|blizzard|avalanche|mist|thunderstorm}"
+        },
+        {
+            "name": "&lt;CUR&gt;",
+            "macro": "{current|present|moment|now}"
+        },
+        {
+            "name": "&lt;WEATHER&gt;",
+            "macro": "{weather {condition|temp|temperature|data|*}|condition|temp|temperature}"
+        },
+        {
+            "name": "&lt;FORECAST&gt;",
+            "macro": "{forecast|prognosis|prediction}"
+        },
+        {
+            "name": "&lt;HISTORY&gt;",
+            "macro": "{history|past}"
+        }
+    ],
+    "elements": [
+        {
+            "id": "wt:hist",
+            "description": "Past weather conditions.",
+            "synonyms": [
+                "{&lt;WEATHER&gt;|*} &lt;HISTORY&gt;",
+                "&lt;HISTORY&gt; {&lt;OF&gt;|*} {&lt;WEATHER&gt;|&lt;PHENOMENON&gt;}"
+            ]
+        },
+        {
+            "id": "wt:curr",
+            "description": "Current weather conditions.",
+            "synonyms": [
+                "{&lt;CUR&gt;|*} {&lt;WEATHER&gt;|&lt;PHENOMENON&gt;}",
+                "&lt;CHANCE&gt; &lt;OF&gt; &lt;PHENOMENON&gt;",
+                "&lt;PHENOMENON&gt; {&lt;CHANCE&gt;|*}"
+            ]
+        },
+        {
+            "id": "wt:fcast",
+            "description": "Future weather forecast.",
+            "synonyms": [
+                "{&lt;PHENOMENON&gt;|&lt;WEATHER&gt;|*} &lt;FORECAST&gt;",
+                "&lt;FORECAST&gt; {&lt;OF&gt;|*} {&lt;WEATHER&gt;|&lt;PHENOMENON&gt;}"
+            ]
+        }
+    ]
+}
+        </pre>
+        <p>There are number of important points here:</p>
+        <ul>
+            <li>
+                <code>Line 10</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 42, 50, 59</code> define three model elements: the past, present and future (forecast) weather
+                condition.
+            </li>
+        </ul>
+        <p>
+            Now that our model is ready let's create a Java class that would load this model and define intents
+            that use the model elements we have just defined.
+        </p>
+    </section>
+    <section id="code">
+        <h3 class="section-title">Model Class</h3>
+        <p>
+            Our Java implementation uses one auxiliary class <a target="github" href="https://github.com/apache/incubator-nlpcraft/blob/master/src/main/scala/org/apache/nlpcraft/examples/weather/WeatherResultWrapper.java">WeatherResultWrapper.java</a>
+            that is used for JSON result formatting. Note that the main <a target="github" href="https://github.com/apache/incubator-nlpcraft/blob/master/src/main/scala/org/apache/nlpcraft/examples/weather/WeatherModel.java">WeatherModel</a> class
+            have number of utility methods that we'll skip here (you can see the entire class by following the link above) and
+            we'll concentrate on the intents only:
+        </p>
+        <pre class="brush: java, highlight: [1, 2, 3, 4, 5, 10, 11, 12, 13, 14, 19, 20, 21, 22, 23]">
+    @NCIntent("intent=fcast term={id == 'wt:fcast'} term(city)={id == 'nlpcraft:city'}? term(date)={id == 'nlpcraft:date'}?")
+    public NCResult onForecastMatch(
+        NCIntentMatch ctx,
+        @NCIntentTerm("city") Optional&lt;NCToken&gt; cityTokOpt,
+        @NCIntentTerm("date") Optional&lt;NCToken&gt; dateTokOpt
+    ) {
+        return onPeriodMatch(ctx, cityTokOpt, dateTokOpt, 5);
+    }
+                
+    @NCIntent("intent=hist term={id == 'wt:hist'} term(city)={id == 'nlpcraft:city'}? term(date)={id == 'nlpcraft:date'}?")
+    public NCResult onHistoryMatch(
+        NCIntentMatch ctx,
+        @NCIntentTerm("city") Optional&lt;NCToken&gt; cityTokOpt,
+        @NCIntentTerm("date") Optional&lt;NCToken&gt; dateTokOpt
+    ) {
+        return onPeriodMatch(ctx, cityTokOpt, dateTokOpt, -5);
+    }
+                        
+    @NCIntent("intent=curr term={id == 'wt:curr'} term(city)={id == 'nlpcraft:city'}? term(date)={id == 'nlpcraft:date'}?")
+    public NCResult onCurrentMatch(
+        NCIntentMatch ctx,
+        @NCIntentTerm("city") Optional&lt;NCToken&gt; cityTokOpt,
+        @NCIntentTerm("date") Optional&lt;NCToken&gt; dateTokOpt
+    ) {
+        checkMatch(ctx);
+
+        try {
+            Coordinate cr = prepGeo(ctx, cityTokOpt);
+
+            if (dateTokOpt.isPresent()) {
+                DateRange range = extractDate(dateTokOpt.get());
+
+                return makeResult(srv.getTimeMachine(cr.latitude, cr.longitude, range.from, range.to), ctx.getIntentId());
+            }
+
+            return makeResult(srv.getCurrent(cr.latitude, cr.longitude), ctx.getIntentId());
+        }
+        catch (DarkSkyException e) {
+            throw new NCRejection(e.getLocalizedMessage());
+        }
+        catch (NCRejection e) {
+            throw e;
+        }
+        catch (Exception e) {
+            throw new NCRejection("Weather provider error.", e);
+        }
+    }
+        </pre>
+        <p>
+            There three methods define three intents. Each intent is defined "in place", i.e. as an annotation on the
+            method that acts as a callback for that intent:
+        </p>
+        <ul>
+            <li>
+                Line 1 defines intent and the callback for the weather forecast (weather in the future):
+                <ul>
+                    <li>Intent is ordered (default), supports conversation (default) and has ID <code>fcast</code></li>
+                    <li>Intent has one mandatory term and two optional terms:
+                        <ul>
+                            <li>
+                                Mandatory term is defined as a token with ID <code>wt:fcast</code> as defined
+                                in the model.
+                            </li>
+                            <li>
+                                Two optional terms (their IDs are <code>city</code> and <code>date</code>) define
+                                optional city and date for the weather report. If not provided, their will be taken
+                                from either conversation context or assume the default value (current geo location
+                                and current time, if available).
+                            </li>
+                        </ul>
+                    </li>
+                </ul>
+            </li>
+            <li>
+                Lines 3-5 define formal parameters for the <code>fcast</code> intent callback method. Note that we use term IDs
+                (<code>city</code> and <code>date</code>) in <code>@NCIntentTerm</code> annotations to automatically
+                assign tokens from the detected terms to their corresponding method formal parameters.
+            </li>
+            <li>
+                Lines 10-14 and lines 19-23 define two other intents and their callbacks methods in the same manner.
+            </li>
+            <li>
+                Implementation of all intent callback method is pretty straightforward and deals mostly with figuring
+                out the default location and time if either one isn't provided.
+            </li>
+        </ul>
+    </section>
+    <section id="tools">
+        <h3 class="section-title">External Tools</h3>
+        <p>
+            This example uses several external tools to implement its functionality:
+        </p>
+        <ul>
+            <li>
+                <a target=_ href="https://darksky.net">Apple DarkSky</a> - to provide actual weather data service
+                See <code>org.apache.nlpcraft.examples.misc.darksky</code> package for details.
+            </li>
+            <li>
+                <a target="_" href="https://tools.keycdn.com/geo">KeyCDN's IP Location Finder</a> - to provide IP location
+                service. See <code>org.apache.nlpcraft.examples.misc.geo.keycdn</code> package for details.
+            </li>
+            <li>
+                City to timezone mapper. See <code>org.apache.nlpcraft.examples.misc.geo.cities</code> package for details.
+            </li>
+        </ul>
+    </section>
+    <section id="start_probe">
+        <h3 class="section-title">Start Data Probe</h3>
+        <p>
+            NLPCraft data models get deployed into data probe. Let's start data probe with our newly
+            created 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.weather.WeatherModel</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 id="start_server">
+        <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.
+            To test it, we'll use the built-in <a href="/tools/test_framework.html">test framework</a>
+            that allows you to write convenient unit tests against your data model.
+        </p>
+    </section>
+    <section id="testing">
+        <h3 class="section-title">Testing</h3>
+        <p>
+            NLPCraft comes with easy to use <a href="/tools/test_framework.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 with following user requests:
+        </p>
+        <ul>
+            <li><code>"What's the local weather forecast?"</code></li>
+            <li><code>"What's the weather in Moscow?"</code></li>
+            <li><code>"Chance of snow?"</code> (using conversation context)</li>
+            <li><code>"Moscow?"</code> (using conversation context).</li>
+        </ul>
+        <p>
+            Let's create new Java class <code>WeatherTest.java</code> with the following code:
+        </p>
+        <pre class="brush: java, highlight: [15, 24]">
+package org.apache.nlpcraft.examples.weather;
+
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
+import org.apache.nlpcraft.common.NCException;
+import org.apache.nlpcraft.model.tools.test.NCTestClient;
+import org.apache.nlpcraft.model.tools.test.NCTestClientBuilder;
+import org.apache.nlpcraft.model.tools.test.NCTestResult;
+import org.apache.nlpcraft.probe.embedded.NCEmbeddedProbe;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+import java.lang.reflect.Type;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class WeatherTest {
+    private static final Gson GSON = new Gson();
+    private static final Type TYPE_MAP_RESP = new TypeToken&lt;HashMap&lt;String, Object&gt;&gt;() {}.getType();
+    private NCTestClient cli;
+            
+    private void checkIntent(String txt, String intentId, boolean shouldBeSame) throws NCException, IOException {
+        NCTestResult res = cli.ask(txt);
+
+        assertTrue(res.isOk(), () -&gt; res.getResultError().get());
+
+        assert res.getResult().isPresent();
+
+        Map&lt;String, Object&gt; map = GSON.fromJson(res.getResult().get(), TYPE_MAP_RESP);
+
+        if (shouldBeSame)
+            assertEquals(intentId, map.get("intentId"));
+        else
+            assertNotEquals(intentId, map.get("intentId"));
+    }
+
+    @BeforeEach
+    void setUp() throws NCException, IOException {
+        NCEmbeddedProbe.start(WeatherModel.class);
+
+        cli = new NCTestClientBuilder().newBuilder().build();
+
+        cli.open("nlpcraft.weather.ex");  // See weather_model.json
+    }
+
+    @AfterEach
+    void tearDown() throws NCException, IOException {
+        if (cli != null)
+            cli.close();
+
+        NCEmbeddedProbe.stop();
+    }
+
+    @Test
+    void test() throws NCException, IOException {
+        // Empty parameter.
+        assertTrue(cli.ask("").isFailed());
+
+        // Only latin charset is supported.
+        assertTrue(cli.ask("El tiempo en España").isFailed());
+
+        // Should be passed.
+        checkIntent("What's the local weather forecast?", "fcast", true);
+        checkIntent("What's the weather in Moscow?", "curr", true);
+        // Can be answered with conversation.
+        checkIntent("Chance of snow?", "curr", true);
+        checkIntent("Moscow", "curr", true);
+
+        cli.clearConversation();
+
+        // Cannot be answered without conversation.
+        assertTrue(cli.ask("Moscow").isFailed());
+    }
+}
+        </pre>
+        <p>
+            This test is pretty straight forward:
+        </p>
+        <ul>
+            <li>
+                On <code>line 15</code> we open the test client with the model ID (see <code>lightswitch_model.yaml</code>
+                file for where we declared it).
+            </li>
+            <li>
+                Test on <code>line 24</code> is where we issue our test sentences and we should see
+                the confirmation messages in our test console output.
+            </li>
+        </ul>
+        <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 output in the data probe console.
+        </p>
+    </section>
+    <section>
+        <h2 class="section-title">Done! 👌</h2>
+        <p>
+            You've created pretty full featured weather bot data model, deployed it into the data probe, started the
+            REST server and tested this 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="#overview">Overview</a></li>
+        <li><a href="#new_project">New Project</a></li>
+        <li><a href="#add_nlpcraft">Add NLPCraft</a></li>
+        <li><a href="#model">Data Model</a></li>
+        <li><a href="#code">Model Class</a></li>
+        <li><a href="#tools">External Tools</a></li>
+        <li><a href="#start_probe">Start Probe</a></li>
+        <li><a href="#start_server">Start Server</a></li>
+        <li><a href="#testing">Testing</a></li>
         {% include quick-links.html %}
     </ul>
 </div>
diff --git a/first-example.html b/first-example.html
index fad60d3..28265f3 100644
--- a/first-example.html
+++ b/first-example.html
@@ -280,7 +280,7 @@ class LightSwitchModel extends NCModelFileAdapter("org/apache/nlpcraft/examples/
         <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
+            demonstrate the built-in <a href="/tools/test_framework.html">test framework</a> that allows you to write
             convenient unit tests against your data model.
         </p>
     </section>
diff --git a/tools/embedded_probe.html b/tools/embedded_probe.html
index a69b929..ed2435a 100644
--- a/tools/embedded_probe.html
+++ b/tools/embedded_probe.html
@@ -73,7 +73,7 @@ id: embedded_probe
             </li>
         </ul>
         <p>
-            Here's an code snippet from <a target="javadoc" href="https://github.com/apache/incubator-nlpcraft/blob/master/src/main/scala/org/apache/nlpcraft/examples/alarm/AlarmTest.java">Alarm Clock</a>
+            Here's an code snippet from <a href="/examples/alarm_clock.html">Alarm Clock</a>
             example illustrating the usage of embedded probe together with NLPCraft test framework and JUnit 5:
         </p>
         <pre class="brush: java, highlight: [6, 18]">
diff --git a/tools/test_framework.html b/tools/test_framework.html
index 8b393a7..2cd613d 100644
--- a/tools/test_framework.html
+++ b/tools/test_framework.html
@@ -67,7 +67,7 @@ id: test_framework
     <section id="usage">
         <h2 class="section-title">Usage</h2>
         <p>
-            Here's an code snippet from <a target="javadoc" href="https://github.com/apache/incubator-nlpcraft/blob/master/src/main/scala/org/apache/nlpcraft/examples/alarm/AlarmTest.java">Alarm Clock</a>
+            Here's an code snippet from <a href="/examples/alarm_clock.html">Alarm Clock</a>
             example illustrating the usage of test framework together with JUnit 5:
         </p>
             <pre class="brush: java, highlight: [8, 10, 16, 24, 25, 26]">