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 f4bcd1d WIP.
f4bcd1d is described below
commit f4bcd1d3527051f38d276c13acbcb67176127206
Author: Aaron Radzinski <[email protected]>
AuthorDate: Thu Aug 20 21:33:14 2020 -0700
WIP.
---
_layouts/documentation.html | 7 ++
examples/weather_bot.html | 273 ++++++++++++++++++++++++++++----------------
intent-matching.html | 2 +-
tools/model_inspector.html | 45 ++++++++
4 files changed, 229 insertions(+), 98 deletions(-)
diff --git a/_layouts/documentation.html b/_layouts/documentation.html
index 074e27d..51b3a61 100644
--- a/_layouts/documentation.html
+++ b/_layouts/documentation.html
@@ -127,6 +127,13 @@ layout: interior
<a href="/tools/sql_model_gen.html">SQL Model Generator</a>
{% endif %}
</li>
+ <li>
+ {% if page.id == "model_inspector" %}
+ <a class="active" href="/tools/model_inspector.html">Model
Inspector</a>
+ {% else %}
+ <a href="/tools/model_inspector.html">Model Inspector</a>
+ {% endif %}
+ </li>
<li class="side-nav-title">Examples</li>
<li>
{% if page.id == "alarm_clock" %}
diff --git a/examples/weather_bot.html b/examples/weather_bot.html
index 5d07fa0..2094f15 100644
--- a/examples/weather_bot.html
+++ b/examples/weather_bot.html
@@ -108,85 +108,58 @@ id: weather_bot
<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]">
+ <pre class="brush: js, highlight: [10, 18, 28]">
{
- "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": "<OF>",
- "macro": "{of|for|per}"
- },
- {
- "name": "<CHANCE>",
- "macro":
"{chance|possibility|probability|odds|likelihood|potential|risk|opportunity}"
- },
- {
- "name": "<PHENOMENON>",
- "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": "<CUR>",
- "macro": "{current|present|moment|now}"
- },
- {
- "name": "<WEATHER>",
- "macro": "{weather
{condition|temp|temperature|data|*}|condition|temp|temperature}"
- },
- {
- "name": "<FORECAST>",
- "macro": "{forecast|prognosis|prediction}"
- },
- {
- "name": "<HISTORY>",
- "macro": "{history|past}"
- }
- ],
- "elements": [
- {
- "id": "wt:hist",
- "description": "Past weather conditions.",
- "synonyms": [
- "{<WEATHER>|*} <HISTORY>",
- "<HISTORY> {<OF>|*}
{<WEATHER>|<PHENOMENON>}"
- ]
- },
- {
- "id": "wt:curr",
- "description": "Current weather conditions.",
- "synonyms": [
- "{<CUR>|*} {<WEATHER>|<PHENOMENON>}",
- "<CHANCE> <OF> <PHENOMENON>",
- "<PHENOMENON> {<CHANCE>|*}"
- ]
- },
- {
- "id": "wt:fcast",
- "description": "Future weather forecast.",
- "synonyms": [
- "{<PHENOMENON>|<WEATHER>|*} <FORECAST>",
- "<FORECAST> {<OF>|*}
{<WEATHER>|<PHENOMENON>}"
- ]
- }
- ]
+ "id": "nlpcraft.weather.ex",
+ "name": "Weather Example Model",
+ "version": "1.0",
+ "description": "Weather example model.",
+ "macros": [
+ ],
+ "elements": [
+ {
+ "id": "wt:phen",
+ "description": "Weather phenomenon.",
+ "synonyms": [
+ "{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}",
+ "{weather
{condition|temp|temperature|data|*}|condition|temp|temperature}"
+ ]
+ },
+ {
+ "id": "wt:hist",
+ "description": "History (past) indicator.",
+ "groups": [
+ "indicator"
+ ],
+ "synonyms": [
+ "{history|past|previous}"
+ ]
+ },
+ {
+ "id": "wt:fcast",
+ "description": "Forecast (future) indicator.",
+ "groups": [
+ "indicator"
+ ],
+ "synonyms": [
+ "{future|forecast|prognosis|prediction}"
+ ]
+ }
+ ]
}
</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.
+ <code>Line 10</code> defines an element <code>wt:phen</code>
for various weather phenomenon.
+ </li>
+ <li>
+ <code>Line 18</code> defines an element <code>wt:hist</code>
which presence will indicate the
+ request for the past (history) weather information.
</li>
<li>
- <code>Lines 42, 50, 59</code> define three model elements: the
past, present and future (forecast) weather
- condition.
+ <code>Line 28</code> defines an element <code>wt:fcast</code>
which presence will indicate the
+ request for the future (forecast) weather information.
</li>
</ul>
<p>
@@ -202,43 +175,138 @@ id: weather_bot
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<NCToken> cityTokOpt,
- @NCIntentTerm("date") Optional<NCToken> 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<NCToken> cityTokOpt,
- @NCIntentTerm("date") Optional<NCToken> dateTokOpt
- ) {
- return onPeriodMatch(ctx, cityTokOpt, dateTokOpt, -5);
+ <pre class="brush: java, highlight: []">
+package org.apache.nlpcraft.examples.weather;
+
+import com.google.gson.Gson;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.nlpcraft.examples.misc.darksky.DarkSkyException;
+import org.apache.nlpcraft.examples.misc.darksky.DarkSkyService;
+import org.apache.nlpcraft.examples.misc.geo.keycdn.GeoManager;
+import org.apache.nlpcraft.examples.misc.geo.keycdn.beans.GeoDataBean;
+import org.apache.nlpcraft.model.*;
+import java.time.Instant;
+import java.util.*;
+import static java.time.temporal.ChronoUnit.DAYS;
+
+public class WeatherModel extends NCModelFileAdapter {
+ // Please register your own account at
https://darksky.net/dev/docs/libraries and
+ // replace this demo token with your own.
+ private final DarkSkyService darkSky = new
DarkSkyService("097e1aad75b22b88f494cf49211975aa", 31);
+
+ private final GeoManager geoMrg = new GeoManager();
+ private static final int DAYS_SHIFT = 5;
+ private static final Gson GSON = new Gson();
+ private static final Set<String> LOCAL_WORDS = new
HashSet<>(Arrays.asList("my", "local", "hometown"));
+
+ private Pair<Double, Double> prepGeo(NCIntentMatch ctx,
Optional<NCToken> geoTokOpt) throws NCRejection {
+ if (geoTokOpt.isPresent()) {
+ NCToken geoTok = geoTokOpt.get();
+
+ Map<String, Object> cityMeta =
geoTok.meta("nlpcraft:city:citymeta");
+
+ Double lat = (Double)cityMeta.get("latitude");
+ Double lon = (Double)cityMeta.get("longitude");
+
+ if (lat == null || lon == null) {
+ String city = geoTok.meta("nlpcraft:city:city");
+
+ throw new NCRejection(String.format("Latitude and longitude
not found for: %s", city));
+ }
+
+ return Pair.of(lat, lon);
+ }
+
+ Optional<GeoDataBean> geoOpt =
geoMrg.get(ctx.getContext().getRequest());
+
+ if (!geoOpt.isPresent())
+ throw new NCRejection("City cannot be determined.");
+
+ // Manually process request for local weather. We need to separate
between 'local Moscow weather'
+ // and 'local weather' which are different. Basically, if there is
word 'local/my/hometown' in the user
+ // input and there is no city in the current sentence - this is a
request for the weather at user's
+ // current location, i.e. we should implicitly assume user's location
and clear conversion context.
+ // In all other cases - we take location from either current sentence
or conversation STM.
+
+ // NOTE: we don't do this separation on intent level as it is easier
to do it here instead of
+ // creating more intents with almost identical callbacks.
+
+ @SuppressWarnings("SuspiciousMethodCalls")
+ boolean hasLocalWord =
+ ctx.getVariant().stream().anyMatch(t ->
LOCAL_WORDS.contains(t.meta("nlpcraft:nlp:origtext")));
+
+ if (hasLocalWord)
+ // Because we implicitly assume user's current city at this point
we need to clear
+ // 'nlpcraft:city' tokens from conversation since they would no
longer be valid.
+ ctx.getContext().getConversation().clearStm(t ->
t.getId().equals("nlpcraft:city"));
+
+ // Try current user location.
+ GeoDataBean geo = geoOpt.get();
+
+ return Pair.of(geo.getLatitude(), geo.getLongitude());
}
-
- @NCIntent("intent=curr term={id == 'wt:curr'} term(city)={id ==
'nlpcraft:city'}? term(date)={id == 'nlpcraft:date'}?")
- public NCResult onCurrentMatch(
+
+ @NCIntent(
+ "intent=req " +
+ "conv=true " + // Support conversation context (i.e. short term
memory).
+ "term={id == 'wt:phen'}+ " + // One or more weather phenomenon (at
least is mandatory).
+ "term(ind)={groups @@ 'indicator'}* " + // Optional indicator words
(zero or more).
+ "term(city)={id == 'nlpcraft:city'}? " + // Optional city.
+ "term(date)={id == 'nlpcraft:date'}?" // Optional date (overrides
indicator words).
+ )
+ @NCIntentSample({
+ "What's the local weather forecast?",
+ "What's the weather in Moscow?",
+ "What is the weather like outside?",
+ "How's the weather?",
+ "What's the weather forecast for the rest of the week?",
+ "What's the weather forecast this week?",
+ "What's the weather out there?",
+ "Is it cold outside?",
+ "Is it hot outside?",
+ "Will it rain today?",
+ "When it will rain in Delhi?",
+ "Is there any possibility of rain in Delhi?",
+ "Is it raining now?",
+ "Is there any chance of rain today?",
+ "Was it raining in Beirut last week?",
+ "How about yesterday?"
+ })
+ public NCResult onMatch(
NCIntentMatch ctx,
+ @NCIntentTerm("ind") List<NCToken> indToksOpt,
@NCIntentTerm("city") Optional<NCToken> cityTokOpt,
@NCIntentTerm("date") Optional<NCToken> dateTokOpt
) {
- checkMatch(ctx);
+ // Reject if intent match is not exact (at least one "dangling" token
remain).
+ if (ctx.isAmbiguous())
+ throw new NCRejection("Please clarify your request.");
try {
- Coordinate cr = prepGeo(ctx, cityTokOpt);
+ Instant now = Instant.now();
- if (dateTokOpt.isPresent()) {
- DateRange range = extractDate(dateTokOpt.get());
+ Instant from = now;
+ Instant to = now;
- return makeResult(srv.getTimeMachine(cr.latitude,
cr.longitude, range.from, range.to), ctx.getIntentId());
+ if (indToksOpt.stream().anyMatch(tok ->
tok.getId().equals("wt:hist")))
+ from = from.minus(DAYS_SHIFT, DAYS);
+ else if (indToksOpt.stream().anyMatch(tok ->
tok.getId().equals("wt:fcast")))
+ to = from.plus(DAYS_SHIFT, DAYS);
+
+ if (dateTokOpt.isPresent()) { // Date token overrides any
indicators.
+ NCToken dateTok = dateTokOpt.get();
+
+ from =
Instant.ofEpochMilli(dateTok.meta("nlpcraft:date:from"));
+ to = Instant.ofEpochMilli(dateTok.meta("nlpcraft:date:to"));
}
- return makeResult(srv.getCurrent(cr.latitude, cr.longitude),
ctx.getIntentId());
+ Pair<Double, Double> latLon = prepGeo(ctx, cityTokOpt); //
Handles optional city too.
+
+ double lat = latLon.getLeft();
+ double lon = latLon.getRight();
+
+ return NCResult.json(GSON.toJson(from == to ?
darkSky.getCurrent(lat, lon) :
+ darkSky.getTimeMachine(lat, lon, from, to)));
}
catch (DarkSkyException e) {
throw new NCRejection(e.getLocalizedMessage());
@@ -250,6 +318,17 @@ id: weather_bot
throw new NCRejection("Weather provider error.", e);
}
}
+
+ public WeatherModel() {
+ // Load model from external JSON file on classpath.
+ super("org/apache/nlpcraft/examples/weather/weather_model.json");
+ }
+
+ @Override
+ public void onDiscard() {
+ darkSky.stop();
+ }
+}
</pre>
<p>
There three methods define three intents. Each intent is defined
"in place", i.e. as an annotation on the
diff --git a/intent-matching.html b/intent-matching.html
index fd6b25e..c433ac0 100644
--- a/intent-matching.html
+++ b/intent-matching.html
@@ -78,7 +78,7 @@ id: intent_matching
Optional annotation that provides one or more sample
of the input that associated intent should match on.
Although this annotation is optional it's highly
recommended to provide at least 5 samples per intent.
These samples serve not only documentation purpose but
also used in built-in model <a
href="/tools/test_framework.html">auto-validation</a>
- and model lint tool.
+ and <a href="/tools/model_inspector.html">model
inspector</a> tool.
</td>
</tr>
</tbody>
diff --git a/tools/model_inspector.html b/tools/model_inspector.html
new file mode 100644
index 0000000..18321cf
--- /dev/null
+++ b/tools/model_inspector.html
@@ -0,0 +1,45 @@
+---
+active_crumb: Model Inspector
+layout: documentation
+id: model_inspector
+---
+
+<!--
+ 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="overview">
+ <h2 class="section-title">Overview</h2>
+ </section>
+ <section id="usage">
+ <h2 class="section-title">Usage</h2>
+ </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="#usage">Usage</a></li>
+ {% include quick-links.html %}
+ </ul>
+</div>
+
+
+
+
+
+