blob: 5906dbad690e16599ceb3d2616b4c1510fe856dc [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"><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="/ext/bootstrap_4_6_0.css"> <script src="/ext/jquery-3.5.1.slim.min.js"></script> <script src="/ext/popper.min_1_14_0.js"></script> <script src="/ext/bootstrap.bundle.min_4_6_0.js"></script><link href="/ext/fontawesome_5_7_1.css" rel="stylesheet" type="text/css"><link href="/ext/syntaxhighlighter/styles/shCoreNLPCraft.css" rel="stylesheet" type="text/css"><link href="/ext/syntaxhighlighter/styles/shThemeNLPCraft.css" rel="stylesheet" type="text/css"> <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">Calculator <code><sub>ex</sub></code></ol><h1 class="page-title"> <span> <i class="fas fa-fw fa-cube"></i> Calculator <code><sub>ex</sub></code> </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 href="/short-term-memory.html">Short-Term Memory</a><li class="side-nav-title">Examples<li> <a class="active" 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 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 for 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/calculator">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: [7]">
ThisBuild / version := "0.1.0-SNAPSHOT"
ThisBuild / scalaVersion := "3.2.2"
lazy val root = (project in file("."))
.settings(
name := "NLPCraft Calculator Example",
version := "1.0.0",
libraryDependencies += "org.apache.nlpcraft" % "nlpcraft" % "1.0.0",
libraryDependencies += "org.apache.nlpcraft" % "nlpcraft-stanford" % "1.0.0",
libraryDependencies += "edu.stanford.nlp" % "stanford-corenlp" % "4.5.1",
libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.15" % "test"
)
</pre><p><b>NOTE: </b>use the latest versions of Scala, ScalaTest and StanfordNLP library.</p><p>Create the following files so that resulting project structure would look like the following:</p><ul><li><code>CalculatorModel.scala</code> - Model implementation<li><code>CalculatorModelSpec.scala</code> - Test that allows to test your model.</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 element definitions are provided programmatically inside Scala model <code>CalculatorModel</code> class. 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.parsers.*
import org.apache.nlpcraft.nlp.stanford.*
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 NCModel(
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:entity: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:entity: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 with two input parameters, second uses last operation result instead of the first argument. Note also that the arithmetic notations (<code>+</code>, <code>-</code>, <code>*</code>, <code>/</code>) are used as is for without any additional synonyms. Let's review this implementation step by step:</p><ul><li> <code>Line 11</code> declares <code>CalculatorModel</code> model companion object which contains static content and helper methods.<li> <code>Line 12</code> defines arithmetic operations map with notations as keys and functions definitions as values.<li> <code>Line 14</code> defines model pipeline based on three built-in components. <code>Line 21</code> defines Stanford token parser <code>NCStanfordNLPTokenParser</code> (we use Stanford NLP components in this example).<li> <code>Line 22</code> declares entity parser <a target="scaladoc" href="/apis/latest/org/apache/nlpcraft/nlp/parsers/NCNLPEntityParser">NCNLPEntityParser</a> which allows to find arithmetic operations notations.<li> <code>Line 23</code> defines entity parser <code>NCStanfordNLPEntityParser</code> which allows to find numerics in the text input.<li> <code>Line 31</code> declares <code>CalculatorModel</code> model class.<li> <code>line 35</code> declares variable named <code>mem</code> which act as a holder for the last operation result.<li> <code>Lines 41 and 47</code> annotate intents <code>calc</code> and its callback method <code>onMatch()</code>. Intent <code>calc</code> requires one arithmetic operation notation and two numerics as its arguments.<li> <code>Lines 56 and 61</code> annotate intents <code>calcMem</code> and its callback method <code>onMatchMem()</code>. Intent <code>calcMem</code> requires one arithmetic operation notation and one numeric as its second arguments. Note that it attempts to use last operation result as its first argument, if one exists.</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 provided in <code>CalculatorModelSpec</code> allows to check that all input test sentences are processed correctly and trigger the expected intents <code>calc</code> or <code>calcMem</code>:</p><pre class="brush: scala, highlight: [9, 11, 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> <code>Line 9</code> creates the client for our model.<li> <code>Line 11</code> calls the method <code>ask()</code>. Its result is checked with expected value.<li> Note that test sentences on <code>lines 15, 16</code> trigger <code>calcMem</code> intent while other sentences trigger <code>calc</code> intent.</ul><p> You can run this test via SBT task <code>executeTests</code> or using IDE.</p><pre class="brush: scala, highlight: []">
$ 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><a href="#overview">Overview</a><li><a href="#new_project">New Project</a><li><a href="#model">Data Model</a><li><a href="#testing">Testing</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="/ext/jquery-3.5.1.slim.min.js"></script> <script src="/ext/popper.min_1_14_0.js"></script> <script src="/ext/bootstrap.bundle.min_4_6_0.js"></script> <script src="/ext//lodash.min_4_17_4.js" type="text/javascript"></script> <script src="/ext/moment.min_2_12_0.js" type="text/javascript"></script> <script src="/ext/moment-timezone-with-data.min_0_5_5.js" type="text/javascript"></script> <script type="text/javascript"> SyntaxHighlighter.defaults["auto-links"] = false; SyntaxHighlighter.defaults["tab-size"] = 2; SyntaxHighlighter.all(); </script>