blob: a23ad5a6864af246dacfee57a8d33a53888d5795 [file] [log] [blame]
<!--
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.
-->
<!doctype html><html lang="en"> <script async src="https://www.googletagmanager.com/gtag/js?id=UA-180663034-1"></script> <script> window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);} gtag('js', new Date()); gtag('config', 'UA-180663034-1'); </script><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"><meta name="description" content="An open source API to convert natural language into actions."><meta name="author" content="NLPCraft."><link rel="apple-touch-icon" sizes="144x144" href="/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png"><link rel="manifest" href="/site.webmanifest"><link rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5"><meta name="msapplication-TileColor" content="#2d89ef"><meta name="theme-color" content="#ffffff"><title>Apache NLPCraft - Natural Language Interface</title><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/css/bootstrap.min.css" integrity="sha384-B0vP5xmATw1+K9KRQjQERJvTumQW0nPEzvF6L/Z6nronJ3oUOFUFpCjEUQouq2+l" crossorigin="anonymous"> <script src="//code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script> <script src="//cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-Piv4xVNRyMGpqkS2by6br4gNJ7DXjqk09RmUpJ8jgGtD7zP9yug3goQfGII0yAns" crossorigin="anonymous"></script><link href="//use.fontawesome.com/releases/v5.7.1/css/all.css" integrity="sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr" rel="stylesheet" crossorigin="anonymous"><link href="/ext/syntaxhighlighter/styles/shCoreNLPCraft.css" rel="stylesheet" type="text/css"><link href="/ext/syntaxhighlighter/styles/shThemeNLPCraft.css" rel="stylesheet" type="text/css"><link rel="preconnect" href="https://fonts.gstatic.com"><link href="//fonts.googleapis.com/css?family=Amatic+SC|Roboto+Mono|Baskervville" rel="stylesheet"><link href="//fonts.googleapis.com/css2?family=Libre+Baskerville:ital@1&display=swap" rel="stylesheet"> <script src="/ext/syntaxhighlighter/scripts/XRegExp.js" type="text/javascript"></script> <script src="/ext/syntaxhighlighter/scripts/shCore.js" type="text/javascript"></script> <script src="/ext/syntaxhighlighter/scripts/shBrushXml.js" type="text/javascript"></script> <script src="/ext/syntaxhighlighter/scripts/shBrushBash.js" type="text/javascript"></script> <script src="/ext/syntaxhighlighter/scripts/shBrushPlain.js" type="text/javascript"></script> <script src="/ext/syntaxhighlighter/scripts/shBrushJava.js" type="text/javascript"></script> <script src="/ext/syntaxhighlighter/scripts/shBrushScala.js" type="text/javascript"></script> <script src="/ext/syntaxhighlighter/scripts/shBrushIdl.js" type="text/javascript"></script> <script src="/ext/syntaxhighlighter/scripts/shBrushPython.js" type="text/javascript"></script> <script src="/ext/syntaxhighlighter/scripts/shBrushJScript.js" type="text/javascript"></script> <script async defer src="https://buttons.github.io/buttons.js"></script><link rel="stylesheet" type="text/css" href="/assets/css/style.css"/><nav class="navbar navbar-expand-lg navbar-light bg-light" id="top-header"> <a class="navbar-brand mr-4" href="/index.html"> <img src="/images/nlpcraft_logo_white-h24.png" alt=""> </a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent"> <span class="navbar-toggler-icon"></span> </button><div class="collapse navbar-collapse" id="navbarSupportedContent"><ul class="navbar-nav mr-auto"><li class="nav-item"> <a class="nav-link" href="/docs.html">Docs</a><li class="nav-item"> <a class="nav-link" href="/community.html">Community</a><li class="nav-item"> <a class="nav-link" href="/use-cases.html">Use Cases</a><li class="nav-item"> <a class="nav-link" href="/download.html">Downloads</a></ul><ul class="navbar-nav ml-auto"><li class="nav-item mr-2"> <a class="nav-link" href="/download.html">v.1.0.0</a><li class="nav-item"> <a class="nav-link fork-link" target="github" href="https://github.com/apache/incubator-nlpcraft">GitHub <i class="fab fa-fw fa-github mr-2"></i></a></ul></div></nav><div class="container-fluid"><div class="navbar-aligned"><ol class="breadcrumb"><li class="mr-1"><a href="/index.html">Home</a><li class="mr-1 active">Shot-Term Memory</ol><h1 class="page-title"> <span> <i class="fas fa-fw fa-book"></i> Shot-Term Memory </span></h1><div class="row three-cols-container"><div class="col-md-2 first-column"><ul class="side-nav"><li class="side-nav-title">Introduction<li> <a href="/docs.html">Overview</a><li> <a href="/installation.html">Installation</a><li> <a href="/first-example.html">First Example</a><li class="side-nav-title">Developer Guide<li> <a href="/key-concepts.html">Key Concepts</a><li> <a href="/intent-matching.html">Intent Matching</a><li> <a class="active" href="/short-term-memory.html">Short-Term Memory</a><li class="side-nav-title">Examples<li> <a href="/examples/calculator.html">Calculator</a><li> <a href="/examples/time.html">Time</a><li> <a href="/examples/light_switch.html">Light Switch</a><li> <a href="/examples/light_switch_fr.html">Light Switch FR</a><li> <a href="/examples/light_switch_ru.html">Light Switch RU</a><li> <a href="/examples/pizzeria.html">Pizzeria</a></ul></div><div id="short-term-memory" class="col-md-8 second-column"><section id="overview"><h2 class="section-title">Conversation <span class="amp">&</span> STM <a href="#"><i class="top-link fas fa-fw fa-angle-double-up"></i></a></h2><p> NLPCraft provides automatic conversation management right out of the box that is fully integrated with <a href="/intent-matching.html">intent matching</a>. Conversation management is based on the idea of short-term memory (STM) - the mechanism by which NLPCraft "remembers" the context of the conversation for a certain amount of time and certain depth of the conversation. STM is automatically maintained by NLPCraft per each user and data model combination. Intent matching algorithm can "recall" the missing tokens from STM when trying to find a match for the conversational terms.</p><p> Why is this so important? Maintaining conversation state is necessary for effective context resolution, so that users could ask, for example, the following sequence of questions using example weather model:</p><dl class="stm-example"><dd><i class="fa fa-fw fa-angle-right"></i>What’s the weather in London today?<dt><p> User gets the current London’s weather. STM is empty at this moment so NLPCraft expects to get all necessary information from the user sentence. Meaningful parts of the sentence get stored in STM.</p><div class="stm-state"><div class="stm"> <label><b>STM</b> Before:</label> <span>&nbsp;</span></div><div class="stm"> <label><b>STM</b> After:</label> <span>weather</span> <span>london</span> <span>today</span></div></div><dd><i class="fa fa-fw fa-angle-right"></i>And what about Berlin?<dt><p> User gets the current Berlin’s weather. The only useful data in the user sentence is name of the city <code>Berlin</code>. But since NLPCraft now has data from the previous question in its STM it can safely deduce that we are asking about <code>weather</code> for <code>today</code>. <code>Berlin</code> overrides <code>London</code> in STM.</p><div class="stm-state"><div class="stm"> <label><b>STM</b> Before:</label> <span>weather</span> <span>London</span> <span>today</span></div><div class="stm"> <label><b>STM</b> After:</label> <span>weather</span> <span><b>berlin</b><br/><i>london</i></span> <span>today</span></div></div><dd><i class="fa fa-fw fa-angle-right"></i>Next week forecast?<dt><p> User gets the next week forecast for Berlin. Again, the only useful data in the user sentence is <code>next week</code> and <code>forecast</code>. STM supplies <code>Berlin</code>. <code>Next week</code> override <code>today</code>, and <code>forecast</code> override <code>weather</code> in STM.</p><div class="stm-state"><div class="stm"> <label><b>STM</b> Before:</label> <span>weather</span> <span>Berlin</span> <span>today</span></div><div class="stm"> <label><b>STM</b> After:</label> <span><b>forecast</b><br/><i>weather</i></span> <span>berlin</span> <span><b>next week</b><br/><i>today</i></span></div></div></dl><p> Note that STM is maintained per user and per data model. Conversation management implementation is also smart enough to clear STM after certain period of time, i.e. it “forgets” the conversational context after few minutes of inactivity. Note also that conversational context can also be cleared explicitly using <a target="scaladoc" href="/apis/latest/org/apache/nlpcraft/NCConversation">NCConversation</a>.</p><p> To understand the algorithm behind the STM management let's back up a few steps...</p></section><section id="parsing"><h2 class="section-title">Parsing User Input <a href="#"><i class="top-link fas fa-fw fa-angle-double-up"></i></a></h2><p> One of the key objectives when parsing user input sentence for Natural Language Understanding (NLU) is to detect all possible semantic entities, a.k.a <em>named entities</em>. Let's consider a few examples:</p><ul><li> <code>"What's the current weather in Tokyo?"</code><br/> This sentence is fully sufficient for the processing since it contains the topic <code>weather</code> as well as all necessary parameters like time (<code>current</code>) and location (<code>Tokyo</code>).<li> <code>"What about Tokyo?"</code><br/> This is an unclear sentence since it does not have the subject of the question - what is it about Tokyo?<li> <code>"What's the weather?"</code><br/> This is also unclear since we are missing important parameters of location and time for our request.</ul><p> Sometimes we can use default values like the current user's location and the current time (if they are missing). However, this can lead to the wrong interpretation if the conversation has an existing context.</p><p> In real life, as well as in NLP-based systems, we always try to start a conversation with a fully defined sentence since without a context the missing information cannot be obtained and the sentenced cannot be interpreted.</p></section><section id="ne"><h2 class="section-title">Named Entities <a href="#"><i class="top-link fas fa-fw fa-angle-double-up"></i></a></h2><p> Let's take a closer look at the named entities from the above examples:</p><ul><li> <code>weather</code> - this is an indicator of the subject of the conversation. Note that it indicates the type of question rather than being an entity with multiple possible values.<li> <code>current</code> - this is an entity of type <code>Date</code> with the value of <code>now</code>.<li> <code>Tokyo</code> - this is an entity of type <code>Location</code> with two values:<ul><li><code>city</code> - type of the location.<li><code>Tokyo, Japan</code> - normalized name of the location.</ul></ul><p> We have two distinct classes of entities:</p><ul><li> Entities that have no values and only act as indicators or types. The entity <code>weather</code> is the type indicator for the subject of the user input.<li> Entities that additionally have one or more specific values like <code>current</code> and <code>Tokyo</code> entities.</ul></section><section id="incomplete"><h2 class="section-title">Incomplete Sentences <a href="#"><i class="top-link fas fa-fw fa-angle-double-up"></i></a></h2><p> Assuming previously asked questions about the weather in Tokyo (in the span of the ongoing conversation) one could presumably ask the following questions using a <em>shorter, incomplete</em>, form:</p><ul><li> <code>"What about Kyoto?</code><br/> This question is missing both the subject and the time. However, we can safely assume we are still talking about current weather.<li> <code>"What about tomorrow?"</code><br/> Like above we automatically assume the weather subject but use <code>Kyoto</code> as the location since it was mentioned the last.</ul><p> These are incomplete sentences. This type of short-hands cannot be interpreted without prior context (neither by humans nor by machines) since by themselves they are missing necessary information. In the context of the conversation, however, these incomplete sentences work. We can simply provide one or two entities and rely on the <em>"listener"</em> to recall the rest of missing information from a short-term memory.</p><p> In NLPCraft, the intent-matching logic will automatically try to find missing information in the conversation context (that is automatically maintained). Moreover, it will properly treat such recalled information during weighted intent matching since it naturally has less "weight" than something that was found explicitly in the user input.</p></section><section id="stm"><h2 class="section-title">Short-Term Memory <a href="#"><i class="top-link fas fa-fw fa-angle-double-up"></i></a></h2><p> The short-term memory is exactly that - a memory that keeps only small amount of recently used information and that evicts its contents after a short period of inactivity.</p><p> Let's look at the example from a real life. If you called your friend in a couple of hours asking <code>"What about a day after?"</code> (still talking about weather in Kyoto) - he'll likely be thoroughly confused. The conversation is timed out, and your friend has lost (forgotten) its context. You will have to explain again to your confused friend what is that you are asking about...</p><p> NLPCraft has a simple rule that 5 minutes pause in conversation leads to the conversation context reset. However, what happens if we switch the topic before this timeout elapsed?</p></section><section id="ctx-switch"><h2 class="section-title">Context Switch <a href="#"><i class="top-link fas fa-fw fa-angle-double-up"></i></a></h2><p> Resetting the context by the timeout is, obviously, not a hard thing to do. What can be trickier is to detect when conversation topic is switched and the previous context needs to be forgotten to avoid very confusing interpretation errors. It is uncanny how humans can detect such switch with seemingly no effort, and yet automating this task by the computer is anything but effortless...</p><p> Let's continue our weather-related conversation. All of a sudden, we ask about something completely different:</p><ul><li> <code>"How much is mocha latter at Starbucks?"</code><br/> At this point we should forget all about previous conversation about weather and assume going forward that we are talking about coffee in Starbucks.<li> <code>"What about Peet's?"</code><br/> We are talking about latter at Peet's.<li> <code>"...and croissant?"</code><br/> Asking about Peet's crescent-shaped fresh rolls.</ul><p> Despite somewhat obvious logic the implementation of context switch is not an exact science. Sometimes, you can have a "soft" context switch where you don't change the topic of the conversation 100% but yet sufficiently enough to forget at least some parts of the previously collected context. NLPCraft has a built-in algorithm to detect the hard switch in the conversation. You can also use <a target="scaladoc" href="/apis/latest/org/apache/nlpcraft/NCConversation">NCConversation</a> to perform a selective reset on the conversation in case of "soft" switch.</p><p> See <a target="scaladoc" href="/apis/latest/org/apache/nlpcraft/NCConversation">NCConversation</a> interface for API details for STM management.</p></section><section id="override"><h2 class="section-title">Overriding Entities <a href="#"><i class="top-link fas fa-fw fa-angle-double-up"></i></a></h2><p> As we've seen above one named entity can replace or override an older entity in the STM, e.g. <code>"Peet's"</code> replaced <code>"Starbucks"</code> in our previous questions. <b>The actual algorithm that governs this logic is one of the most important part of STM implementation.</b> In human conversations we perform this logic seemingly subconsciously — but the computer algorithm to do it is not that trivial. Let's see how it is done in NLPCraft.</p><p> One of the important supporting design decision is that an entity can belong to one or more groups. You can think of groups as types, or classes of entities (to be mathematically precise these are the categories). The entity's membership in such groups is what drives the rule of overriding.</p><p> Let's look at the specific example.</p><p> Consider a data model that defined 3 entities:</p><ul><li> <code>"sell"</code> (with synonym <code>"sales"</code>)<li> <code>"buy"</code> (with synonym <code>"purchase"</code>)<li> <code>"best_employee"</code> (with synonyms like <code>"best"</code>, <code>"best employee"</code>, <code>"best colleague"</code>)</ul><p> Our task is to support for following conversation:</p><ul><li> <code>"Give me the sales data"</code><br/> We return sales information since we detected <code>"sell"</code> entity by its synonym <code>"sales"</code>.<li> <code>"Who was the best?"</code><br/> We return the best salesmen since we detected <code>"best_employee"</code> and we should pick <code>"sell"</code> entity from the STM.<li> <code>"OK, give me the purchasing report now."</code><br/> This is a bit trickier. We should return general purchasing data and not a best purchaser employee. It feels counter-intuitive but we should NOT take <code>"best_employee"</code> entity from STM and, in fact, we should remove it from STM.<li> <code>"...and who's the best there?"</code><br/> Now, we should return the best purchasing employee. We detected <code>"best_employee"</code> entity and we should pick <code>"buy"</code> entity from STM.<li> <code>"One more time - show me the general purchasing data again"</code><br/> Once again, we should return a general purchasing report and ignore (and remove) <code>"best_employee"</code> from STM.</ul></section><section id="rule"><h2 class="section-title">Overriding Rule <a href="#"><i class="top-link fas fa-fw fa-angle-double-up"></i></a></h2><p> Here's the rule we developed at NLPCraft and have been successfully using in various models:</p><div class="bq success"><div class="bq-idea-container"><div><div>💡</div></div><div> <b>Overriding Rule</b><p> The entity will override other entity or entities in STM that belong to the same group set or its superset.</p></div></div></div><p> In other words, an entity with a smaller group set (more specific one) will override entity with the same or larger group set (more generic one). Let's consider an entity that belongs to the following groups: <code>{G1, G2, G3}</code>. This entity:</p><ul><li> WILL override existing entity belonging to <code>{G1, G2, G3}</code> groups (same set).<li> WILL override existing entity belonging to <code>{G1, G2, G3, G4}</code> groups (superset).<li> WILL NOT override existing entity belonging to <code>{G1, G2}</code> groups.<li> WIL NOT override existing entity belonging to <code>{G10, G20}</code> groups.</ul><p> Let's come back to our sell/buy/best example. To interpret the questions we've outlined above we need to have the following 4 intents:</p><ul><li><code>intent=sale term={# == 'sale'}</code><li><code>intent=best_sale_person term={# == 'sale'} term={# == 'best_employee'}</code><li><code>intent=buy term={# == 'buy'}</code><li><code>intent=buy_best_person term={# == 'buy'} term={# == 'best_employee'}</code></ul><p> (this is actual <a href="/intent-matching.html">Intent Definition Language</a> (IDL) used by NLPCraft - <code>term</code> here is basically what's often referred to as a slot in other systems).</p><p> We also need to properly configure groups for our entities (names of the groups are arbitrary):</p><ul><li>Entity <code>"sell"</code> belongs to group <b>A</b><li>Entity <code>"buy"</code> belongs to group <b>B</b><li>Entity <code>"best_employee"</code> belongs to groups <b>A</b> and <b>B</b></ul><p> Let’s run through our example again with this configuration:</p><ul><li> <code>"Give me the sales data"</code><ul><li>We detected entity from group <b>A</b>.<li>STM is empty at this point.<li>Return general sales report.<li>Store <code>"sell"</code> entity with group <b>A</b> in STM.</ul><li> <code>"Who was the best?"</code><ul><li>We detected entity belonging to groups <b>A</b> and <b>B</b>.<li>STM has entity belonging to group <b>A</b>.<li><b>{A, B}</b> does NOT override <b>{A}</b>.<li>Return best salesmen report.<li>Store detected <code>"best_employee"</code> entity.<li>STM now has two entities with <b>{A}</b> and <b>{A, B}</b> group sets.</ul><li> <code>"OK, give me the purchasing report now."</code><ul><li>We detected <code>"buy"</code> entity with group <b>A</b>.<li>STM has two entities with <b>{A}</b> and <b>{A, B}</b> group sets.<li><b>{A}</b> overrides both <b>{A}</b> and <b>{A, B}</b>.<li>Return general purchasing report.<li>Store <code>"buy"</code> entity with group <b>A</b> in STM.</ul></ul></section><section id="switch"><h2 class="section-title">Explicit Context Switch <a href="#"><i class="top-link fas fa-fw fa-angle-double-up"></i></a></h2><p> In some cases you may need to explicitly clear the conversation STM without relying on algorithmic behavior. It happens when current and new topic of the conversation share some of the same entities. Although at first it sounds counter-intuitive there are many examples of that in day to day life.</p><p> Let’s look at this sample conversation:</p><ul><li> <b>Q</b>: <code>"What the weather in Tokyo?"</code><br/> <b>A</b>: Current weather in Tokyo...<li> <b>Q</b>: <code>"Let’s do New York after all then!"</code><br/> <b>A</b>: Without an explicit conversation reset we would return current New York weather 🤔</ul><p> The second question was about going to New York (booking tickets, etc.). In real life - your counterpart will likely ask what you mean by "doing New York after all" and you’ll have to explain the abrupt change in the topic. You can avoid this confusion by simply saying: "Enough about weather! Let’s talk about this weekend plans" - after which the second question becomes clear. That sentence is an explicit context switch which you can also detect in the NLPCraft model.</p><p> In NLPCraft you can also explicitly reset conversation context through <a target="scaladoc" href="/apis/latest/org/apache/nlpcraft/NCConversation">NCConversation</a> interface or by switching the model on the request.</p></section></div><div class="col-md-2 third-column"><ul class="side-nav"><li class="side-nav-title">On This Page<li><a href="#overview">Conversation <span class="amp">&</span> STM</a><li><a href="#parsing">Parsing User Input</a><li><a href="#ne">Named Entities</a><li><a href="#incomplete">Incomplete Sentences</a><li><a href="#stm">Short-Term-Memory</a><li><a href="#ctx-switch">Context Switch</a><li><a href="#override">Overriding Entities</a><li><a href="#rule">Overriding Rule</a><li><a href="#switch">Context Switch</a><li class="side-nav-title">Quick Links<li> <i class="fab fa-fw fa-github mr-2"></i><a target="github" href="https://github.com/apache/incubator-nlpcraft/tree/master/nlpcraft-examples">Examples</a><li> <i class="fas fa-fw fa-book mr-2"></i><a target="_" href="/apis/latest/index.html">Scaladoc</a><li> <i class="fas fa-fw fa-download mr-2"></i><a href="/download.html">Download</a><li> <i class="fas fa-fw fa-cog mr-2"></i><a href="/installation.html">Installation</a><li class="side-nav-title">Support<li> <nobr> <i class="fab fa-fw fa-jira mr-2"></i><a target="jira" href="https://issues.apache.org/jira/projects/NLPCRAFT/issues">JIRA</a> </nobr><li> <nobr> <i class="far fa-fw fa-envelope mr-2"></i><a target="dev-list" href="https://mail-archives.apache.org/mod_mbox/nlpcraft-dev/">Dev List</a> </nobr><li> <nobr> <i class="fab fa-fw fa-stack-overflow mr-2"></i><a target="so" href="https://stackoverflow.com/questions/ask">Stack Overflow</a> </nobr><li> <nobr> <i class="fab fa-fw fa-github mr-2"></i><a target="github" href="https://github.com/apache/incubator-nlpcraft">GitHub</a> </nobr><li> <nobr> <i class="fab fa-fw fa-gitter mr-2"></i><a target="gitter" href="https://gitter.im/apache-nlpcraft/community">Gitter</a> </nobr><li> <nobr> <i class="fab fa-fw fa-twitter mr-2"></i><a target="twitter" href="https://twitter.com/ApacheNLPCraft">Twitter</a> </nobr></ul></div></div></div></div><div id="footer"><div class="container"><div class="text-muted text-center"> <span>Copyright &copy; 2023 Apache Software Foundation</span> <span> <a target=_new href="https://apache.org"><img alt="asf" src="/images/asf_logo-h24.png"></a> </span> <a target="asf" href="https://www.apache.org/events/current-event.html" class="btn btn-link ml-4">Events</a> <span class="sep"></span> <a target="asf" href="https://apache.org/foundation/policies/privacy.html" class="btn btn-link">Privacy</a> <span class="sep"></span> <a href="/index.html#news" class="btn btn-link">News</a> <span class="sep"></span> <a href="/docs.html" class="btn btn-link">Docs</a> <span class="ml-4">release: <a href="/download.html"><code>1.0.0</code></a></span> <span class="ml-2"> <a target="github" href="https://github.com/apache/incubator-nlpcraft/actions"><img alt="" src="https://github.com/apache/incubator-nlpcraft/workflows/build/badge.svg"></a> <a target=_ href="https://gitter.im/apache-nlpcraft/community"><img alt="Gitter" src="https://badges.gitter.im/apache-nlpcraft/community.svg"></a> </span> <span class="ml-2"> Built in:<a target="scala" href="https://scala-lang.org/"><img style="margin-left: 5px" src="/images/scala-logo-h16.png" alt=""></a> </span></div></div></div><script src="//code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script> <script src="//cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.0/umd/popper.min.js" integrity="sha384-cs/chFZiN24E4KMATLdqdvsezGxaGsi4hLGOzlXwp5UZB1LY//20VyM2taTB4QvJ" crossorigin="anonymous"></script> <script src="//stackpath.bootstrapcdn.com/bootstrap/4.1.0/js/bootstrap.min.js" integrity="sha384-uefMccjFJAIv6A+rW+L4AHf99KvxDjWSu1z9VI8SKNVmz4sk7buKt/6v9KI65qnm" crossorigin="anonymous"></script> <script src="//cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js" type="text/javascript" ></script> <script src="//cdnjs.cloudflare.com/ajax/libs/moment.js/2.12.0/moment.min.js" type="text/javascript" ></script> <script src="//cdnjs.cloudflare.com/ajax/libs/moment-timezone/0.5.5/moment-timezone-with-data.min.js" type="text/javascript" ></script> <script type="text/javascript"> SyntaxHighlighter.defaults["auto-links"] = false; SyntaxHighlighter.defaults["tab-size"] = 2; SyntaxHighlighter.all(); </script>