Merge branch 'master' into NLPCRAFT-383
diff --git a/nlpcraft-examples/solarsystem/pom.xml b/nlpcraft-examples/solarsystem/pom.xml
new file mode 100644
index 0000000..23b4079
--- /dev/null
+++ b/nlpcraft-examples/solarsystem/pom.xml
@@ -0,0 +1,151 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+ 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.
+-->
+
+<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <name>NLPCraft example Solar System</name>
+ <artifactId>nlpcraft-example-solarsystem</artifactId>
+
+ <parent>
+ <artifactId>nlpcraft-parent</artifactId>
+ <groupId>org.apache.nlpcraft</groupId>
+ <version>0.9.0</version>
+ <relativePath>../../pom.xml</relativePath>
+ </parent>
+
+ <properties>
+ <nlpcraft.server.module>nlpcraft</nlpcraft.server.module>
+ <nlpcraft.all.deps.jar>apache-${nlpcraft.server.module}-incubating-${project.version}-all-deps.jar</nlpcraft.all.deps.jar>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>nlpcraft</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <!-- Test dependencies. -->
+ <dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter-engine</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>nlpcraft</artifactId>
+ <version>${project.version}</version>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>${maven.compiler.plugin.ver}</version>
+ <configuration>
+ <source>${java.ver}</source>
+ <target>${java.ver}</target>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>com.bazaarvoice.maven.plugins</groupId>
+ <artifactId>process-exec-maven-plugin</artifactId>
+ <version>${maven.bazaarvoice.plugin.ver}</version>
+ <executions>
+ <execution>
+ <id>pre-integration-test</id>
+ <phase>pre-integration-test</phase>
+ <goals>
+ <goal>start</goal>
+ </goals>
+ <configuration>
+ <!--
+ Depending on the console config and how maven is run this will produce the output with ANSI colors.
+ To strip out ANSI escape sequences from the log file, see the following:
+ https://stackoverflow.com/questions/17998978/removing-colors-from-output
+ -->
+ <name>server</name>
+ <healthcheckUrl>http://localhost:8081/api/v1/health</healthcheckUrl>
+ <waitAfterLaunch>600</waitAfterLaunch>
+ <processLogFile>${project.build.directory}/server-${timestamp}.log</processLogFile>
+ <arguments>
+ <argument>${java.home}/bin/java</argument>
+ <argument>-Xmx4G</argument>
+ <argument>-Xms4G</argument>
+ <argument>--add-exports=java.base/jdk.internal.misc=ALL-UNNAMED</argument>
+ <argument>--add-exports=java.base/sun.nio.ch=ALL-UNNAMED</argument>
+ <argument>--add-exports=java.management/com.sun.jmx.mbeanserver=ALL-UNNAMED</argument>
+ <argument>--add-exports=jdk.internal.jvmstat/sun.jvmstat.monitor=ALL-UNNAMED</argument>
+ <argument>--add-exports=java.base/sun.reflect.generics.reflectiveObjects=ALL-UNNAMED</argument>
+ <argument>--add-opens=jdk.management/com.sun.management.internal=ALL-UNNAMED</argument>
+ <argument>--illegal-access=permit</argument>
+ <argument>-DNLPCRAFT_ANSI_COLOR_DISABLED=true</argument> <!-- Remove ANSI at least from NLPCraft output. -->
+ <argument>-Djdk.tls.client.protocols=TLSv1.2</argument>
+ <argument>-jar</argument>
+ <argument>${project.basedir}/../../${nlpcraft.server.module}/target/${nlpcraft.all.deps.jar}</argument>
+ <argument>-server</argument>
+ </arguments>
+ </configuration>
+ </execution>
+ <execution>
+ <id>stop-all</id>
+ <phase>post-integration-test</phase>
+ <goals>
+ <goal>stop-all</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <version>${maven.surefire.plugin.ver}</version>
+ <configuration>
+ <!-- Skips all tests on phase `test`. -->
+ <skip>true</skip>
+ </configuration>
+ <executions>
+ <!-- All tests are defined as integration. -->
+ <execution>
+ <id>integration-tests</id>
+ <phase>integration-test</phase>
+ <goals>
+ <goal>test</goal>
+ </goals>
+ <configuration>
+ <skip>false</skip>
+ <!-- Mandatory part. -->
+ <includes>
+ <include>**/*.*</include>
+ </includes>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
\ No newline at end of file
diff --git a/nlpcraft-examples/solarsystem/src/main/java/org/apache/nlpcraft/examples/solarsystem/SolarSystemModel.scala b/nlpcraft-examples/solarsystem/src/main/java/org/apache/nlpcraft/examples/solarsystem/SolarSystemModel.scala
new file mode 100644
index 0000000..3338615
--- /dev/null
+++ b/nlpcraft-examples/solarsystem/src/main/java/org/apache/nlpcraft/examples/solarsystem/SolarSystemModel.scala
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+package org.apache.nlpcraft.examples.solarsystem
+
+import com.typesafe.scalalogging.LazyLogging
+import org.apache.nlpcraft.examples.solarsystem.api.SolarSystemOpenApiService
+import org.apache.nlpcraft.model.{NCModelAddPackage, NCModelFileAdapter}
+
+@NCModelAddPackage(Array("org.apache.nlpcraft.examples.solarsystem.intents"))
+class SolarSystemModel extends NCModelFileAdapter("solarsystem_model.yaml") with LazyLogging {
+ protected var api: SolarSystemOpenApiService = _
+
+ override def onInit(): Unit = {
+ api = SolarSystemOpenApiService.getInstance()
+
+ logger.info("Solar System API initialized.")
+ }
+
+ override def onDiscard(): Unit = {
+ if (api != null)
+ api.stop()
+
+ logger.info("Solar System API closed.")
+ }
+}
\ No newline at end of file
diff --git a/nlpcraft-examples/solarsystem/src/main/java/org/apache/nlpcraft/examples/solarsystem/api/SolarSystemOpenApiBodyRequest.scala b/nlpcraft-examples/solarsystem/src/main/java/org/apache/nlpcraft/examples/solarsystem/api/SolarSystemOpenApiBodyRequest.scala
new file mode 100644
index 0000000..aa981a1
--- /dev/null
+++ b/nlpcraft-examples/solarsystem/src/main/java/org/apache/nlpcraft/examples/solarsystem/api/SolarSystemOpenApiBodyRequest.scala
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+
+package org.apache.nlpcraft.examples.solarsystem.api
+
+trait SolarSystemOpenApiBodyRequest {
+ def withParameters(params: String*): SolarSystemOpenApiBodyRequest
+ def withFilter(data: String, oper: String, value: String): SolarSystemOpenApiBodyRequest
+
+ def execute(): Seq[Map[String, Object]]
+}
diff --git a/nlpcraft-examples/solarsystem/src/main/java/org/apache/nlpcraft/examples/solarsystem/api/SolarSystemOpenApiService.scala b/nlpcraft-examples/solarsystem/src/main/java/org/apache/nlpcraft/examples/solarsystem/api/SolarSystemOpenApiService.scala
new file mode 100644
index 0000000..f5ba5b8
--- /dev/null
+++ b/nlpcraft-examples/solarsystem/src/main/java/org/apache/nlpcraft/examples/solarsystem/api/SolarSystemOpenApiService.scala
@@ -0,0 +1,144 @@
+/*
+ * 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.
+ */
+
+package org.apache.nlpcraft.examples.solarsystem.api
+
+import com.fasterxml.jackson.databind.ObjectMapper
+import com.fasterxml.jackson.module.scala.DefaultScalaModule
+import com.typesafe.scalalogging.LazyLogging
+import org.apache.nlpcraft.examples.solarsystem.api.SolarSystemOpenApiService.BodiesBean
+
+import java.net.http.HttpClient.Version
+import java.net.http.{HttpClient, HttpRequest, HttpResponse}
+import java.net.{URI, URLEncoder}
+
+object SolarSystemOpenApiService {
+ private final val URL_BODIES = "https://api.le-systeme-solaire.net/rest/bodies"
+ private final val MAPPER = new ObjectMapper().registerModule(DefaultScalaModule)
+
+ case class BodiesBean(bodies: Seq[Map[String, Object]])
+
+ private var s: SolarSystemOpenApiService = _
+
+ def getInstance(): SolarSystemOpenApiService = {
+ if (s == null)
+ this.synchronized {
+ if (s == null) {
+ s = new SolarSystemOpenApiService
+
+ // Skips errors processing for simplifying.
+ s.start()
+ }
+ }
+
+ s
+ }
+}
+
+import SolarSystemOpenApiService._
+
+class SolarSystemOpenApiService extends LazyLogging {
+ private var client: HttpClient = _
+
+ private var planets: Map[String, String] = _
+ private var discovers: Seq[String] = _
+
+ // Simplified implementation of 'https://api.le-systeme-solaire.net/rest/bodies/' request.
+ // Only single filter can be used.
+ def bodyRequest(): SolarSystemOpenApiBodyRequest = new SolarSystemOpenApiBodyRequest() {
+ case class Filter(data: String, oper: String, value: String) {
+ require(data != null)
+ require(oper != null)
+ require(value != null)
+ }
+
+ private var params: Seq[String] = _
+ private var filter: Filter = _
+
+ override def withParameters(params: String*): SolarSystemOpenApiBodyRequest = {
+ this.params = params
+
+ this
+ }
+
+ override def withFilter(data: String, oper: String, value: String): SolarSystemOpenApiBodyRequest = {
+ this.filter = Filter(data, oper, value)
+
+ this
+ }
+
+ override def execute(): Seq[Map[String, Object]] = {
+ var url = URL_BODIES
+
+ def getSeparator: String = if (url == URL_BODIES) "?" else "&"
+
+ if (params != null)
+ url = s"$url${getSeparator}data=${params.mkString(",")}"
+
+ if (filter != null) {
+ val v = URLEncoder.encode(filter.value, "UTF-8")
+
+ url = s"$url${getSeparator}filter=${filter.data},${filter.oper},$v"
+ }
+
+ logger.info(s"Request prepared: $url")
+
+ val respJs = client.sendAsync(
+ HttpRequest.newBuilder(URI.create(url)).
+ header("Content-Type", "application/json").GET().build(),
+ HttpResponse.BodyHandlers.ofString()
+ ).get().body()
+
+ val respLog = if (respJs.length > 100) s"${respJs.take(100)} ....." else respJs
+
+ logger.info(s"Response received: $respLog")
+
+ MAPPER.readValue(respJs, classOf[BodiesBean]).bodies
+ }
+ }
+
+ def start(): Unit = {
+ client = HttpClient.newBuilder.version(Version.HTTP_2).build
+
+ val res = bodyRequest().withParameters("id", "englishName", "discoveredBy").execute()
+
+ def str(m: Map[String, Object], name: String): String = m(name).asInstanceOf[String].strip
+
+ planets =
+ res.map(row => str(row, "id") -> str(row, "englishName")).
+ filter(p => p._1.nonEmpty && p._2.nonEmpty).toMap
+
+ discovers = res.map(row => str(row, "discoveredBy")).distinct
+
+ logger.info(
+ s"Solar System Open Api Service started. " +
+ s"Initial data discovered [planets=${planets.size}, discovers=${discovers.size}]"
+ )
+ }
+
+ def stop(): Unit = {
+ planets = null
+ discovers = null
+
+ client = null
+
+ logger.info(s"Solar System Open Api Service stopped.")
+ }
+
+ def getAllPlanets: Map[String, String] = planets
+ def getAllDiscovers: Seq[String] = discovers
+}
diff --git a/nlpcraft-examples/solarsystem/src/main/java/org/apache/nlpcraft/examples/solarsystem/intents/SolarSystemDiscoverer.scala b/nlpcraft-examples/solarsystem/src/main/java/org/apache/nlpcraft/examples/solarsystem/intents/SolarSystemDiscoverer.scala
new file mode 100644
index 0000000..914c100
--- /dev/null
+++ b/nlpcraft-examples/solarsystem/src/main/java/org/apache/nlpcraft/examples/solarsystem/intents/SolarSystemDiscoverer.scala
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+package org.apache.nlpcraft.examples.solarsystem.intents
+
+import org.apache.nlpcraft.examples.solarsystem.api.SolarSystemOpenApiService
+import org.apache.nlpcraft.model.{NCIntent, NCIntentSample, NCIntentTerm, NCResult, NCToken}
+
+class SolarSystemDiscoverer {
+ @NCIntentSample(
+ Array(
+ "What was discovered by Asaph Hall",
+ "What was discovered by Hall",
+ "Galileo Galilei planets",
+ "Galilei planets"
+ )
+ )
+ @NCIntent(
+ "intent=discoverer " +
+ " options={'unused_usr_toks': true}" +
+ " term(discoverer)={tok_id() == 'discoverer'}"
+ )
+ def discoverer(@NCIntentTerm("discoverer") discoverer: NCToken): NCResult =
+ NCResult.text(
+ SolarSystemOpenApiService.
+ getInstance().
+ bodyRequest().
+ withFilter("discoveredBy", "cs", discoverer.getNormalizedText).
+ execute().
+ toString()
+ )
+}
\ No newline at end of file
diff --git a/nlpcraft-examples/solarsystem/src/main/java/org/apache/nlpcraft/examples/solarsystem/intents/SolarSystemDiscoveryDate.scala b/nlpcraft-examples/solarsystem/src/main/java/org/apache/nlpcraft/examples/solarsystem/intents/SolarSystemDiscoveryDate.scala
new file mode 100644
index 0000000..7fe1b96
--- /dev/null
+++ b/nlpcraft-examples/solarsystem/src/main/java/org/apache/nlpcraft/examples/solarsystem/intents/SolarSystemDiscoveryDate.scala
@@ -0,0 +1,95 @@
+/*
+ * 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.
+ */
+
+package org.apache.nlpcraft.examples.solarsystem.intents
+
+import com.typesafe.scalalogging.LazyLogging
+import org.apache.nlpcraft.examples.solarsystem.api.SolarSystemOpenApiService
+import org.apache.nlpcraft.model.{NCIntent, NCIntentSample, NCIntentTerm, NCResult, NCToken}
+
+import java.time.format.{DateTimeFormatter, DateTimeFormatterBuilder, DateTimeParseException}
+import java.time.temporal.ChronoField.{DAY_OF_MONTH, MONTH_OF_YEAR}
+import java.time.{LocalDate, ZoneOffset}
+
+class SolarSystemDiscoveryDate extends LazyLogging {
+ @NCIntentSample(
+ Array(
+ "After 1900 year",
+ "Before 1900 year",
+ "Between 1800 and 1900 years"
+ )
+ )
+ @NCIntent(
+ "intent=discoveryDate " +
+ " options={'unused_usr_toks': true}" +
+ " term(year)={" +
+ " tok_id() == 'nlpcraft:num' && " +
+ " (" +
+ " meta_tok('nlpcraft:num:unit') == 'year' || " +
+ " meta_tok('nlpcraft:num:from') >= 1610 && meta_tok('nlpcraft:num:from') <= year" +
+ " )" +
+ " }"
+ )
+ def date(@NCIntentTerm("year") year: NCToken): NCResult = {
+ // API doesn't support filter by dates.
+ // We do it here.
+ var res = SolarSystemOpenApiService.getInstance().bodyRequest().execute()
+
+ val supportedFmts =
+ Seq(
+ DateTimeFormatter.ofPattern("dd/MM/yyyy"),
+ new DateTimeFormatterBuilder().
+ appendPattern("yyyy").
+ parseDefaulting(MONTH_OF_YEAR, 1).
+ parseDefaulting(DAY_OF_MONTH, 1).
+ toFormatter(),
+ new DateTimeFormatterBuilder().
+ appendPattern("??/MM/yyyy").
+ parseDefaulting(DAY_OF_MONTH, 1).
+ toFormatter()
+ )
+
+ val fromYear: Double = year.metax("nlpcraft:num:from")
+ val toYear: Double = year.metax("nlpcraft:num:to")
+
+ res = res.filter(row => {
+ val dateStr = row("discoveryDate").asInstanceOf[String]
+
+ // TODO: fix condition.
+ if (dateStr.nonEmpty)
+ supportedFmts.flatMap(p =>
+ try {
+ val years = LocalDate.parse(dateStr, p).atStartOfDay(ZoneOffset.UTC).getYear
+
+ Some(years >= fromYear && years <= toYear)
+ }
+ catch {
+ case _: DateTimeParseException => None
+ }
+ ).
+ to(LazyList).
+ headOption.
+ getOrElse(throw new AssertionError(s"Template not found for: $dateStr"))
+ else
+ false
+ })
+
+ logger.info(s"Request result filtered with years range ${fromYear.toLong}-${toYear.toLong}, rows=${res.size}")
+
+ NCResult.text(res.toString())
+ }
+}
\ No newline at end of file
diff --git a/nlpcraft-examples/solarsystem/src/main/java/org/apache/nlpcraft/examples/solarsystem/intents/SolarSystemNumAggrConditions.scala b/nlpcraft-examples/solarsystem/src/main/java/org/apache/nlpcraft/examples/solarsystem/intents/SolarSystemNumAggrConditions.scala
new file mode 100644
index 0000000..da57d74
--- /dev/null
+++ b/nlpcraft-examples/solarsystem/src/main/java/org/apache/nlpcraft/examples/solarsystem/intents/SolarSystemNumAggrConditions.scala
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+
+package org.apache.nlpcraft.examples.solarsystem.intents
+
+import org.apache.nlpcraft.examples.solarsystem.api.SolarSystemOpenApiService
+import org.apache.nlpcraft.model.{NCIntent, NCIntentSample, NCIntentTerm, NCResult, NCToken}
+
+class SolarSystemNumAggrConditions {
+ @NCIntentSample(
+ Array(
+ "Planets with maximum mass",
+ "Planets with minimum radius"
+ )
+ )
+ @NCIntent(
+ "intent=aggregate " +
+ " term(condition)={# == 'aggr'}" +
+ " term(num)={# == 'nlpcraft:num' && meta_tok('nlpcraft:num:unit') == null}"
+ )
+ def discoverer(@NCIntentTerm("condition") cond: NCToken): NCResult = {
+ val s: String = cond.getMetadata.get("").asInstanceOf[String]
+
+ NCResult.text(
+ SolarSystemOpenApiService.
+ getInstance().
+ bodyRequest().
+ withFilter(s, "cs", cond.getNormalizedText).
+ execute().
+ toString()
+ )
+ }
+}
\ No newline at end of file
diff --git a/nlpcraft-examples/solarsystem/src/main/java/org/apache/nlpcraft/examples/solarsystem/intents/SolarSystemNumConditions.scala b/nlpcraft-examples/solarsystem/src/main/java/org/apache/nlpcraft/examples/solarsystem/intents/SolarSystemNumConditions.scala
new file mode 100644
index 0000000..30909ac
--- /dev/null
+++ b/nlpcraft-examples/solarsystem/src/main/java/org/apache/nlpcraft/examples/solarsystem/intents/SolarSystemNumConditions.scala
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+package org.apache.nlpcraft.examples.solarsystem.intents
+
+import org.apache.nlpcraft.examples.solarsystem.api.SolarSystemOpenApiService
+import org.apache.nlpcraft.model.{NCIntent, NCIntentSample, NCIntentTerm, NCResult, NCToken}
+
+class SolarSystemNumConditions {
+ @NCIntentSample(
+ Array(
+ "Planets with mass more 10",
+ )
+ )
+ @NCIntent(
+ "intent=aggregate " +
+ " term(condition)={tok_group == 'propNum'}" +
+ " term(num)={# == 'nlpcraft:num' && meta_tok('nlpcraft:num:unit') == null}"
+ )
+ def discoverer(@NCIntentTerm("condition") cond: NCToken): NCResult = {
+ val s: String = cond.getMetadata.get("").asInstanceOf[String]
+
+ NCResult.text(
+ SolarSystemOpenApiService.
+ getInstance().
+ bodyRequest().
+ withFilter(s, "cs", cond.getNormalizedText).
+ execute().
+ toString()
+ )
+ }
+}
\ No newline at end of file
diff --git a/nlpcraft-examples/solarsystem/src/main/java/org/apache/nlpcraft/examples/solarsystem/intents/SolarSystemPlanetInfo.scala b/nlpcraft-examples/solarsystem/src/main/java/org/apache/nlpcraft/examples/solarsystem/intents/SolarSystemPlanetInfo.scala
new file mode 100644
index 0000000..a674c2e
--- /dev/null
+++ b/nlpcraft-examples/solarsystem/src/main/java/org/apache/nlpcraft/examples/solarsystem/intents/SolarSystemPlanetInfo.scala
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+package org.apache.nlpcraft.examples.solarsystem.intents
+
+import org.apache.nlpcraft.examples.solarsystem.api.SolarSystemOpenApiService
+import org.apache.nlpcraft.model.{NCIntent, NCIntentSample, NCIntentTerm, NCResult, NCToken}
+
+class SolarSystemPlanetInfo {
+ @NCIntentSample(
+ Array(
+ "Moon!",
+ "give me information about Larissa",
+ )
+ )
+ @NCIntent(
+ "intent=planetInfo " +
+ " options={'unused_usr_toks': false }" +
+ " term(planet)={tok_id() == 'planet'}"
+ )
+ def planetInfo(@NCIntentTerm("planet") planet: NCToken): NCResult =
+ NCResult.text(
+ SolarSystemOpenApiService.
+ getInstance().
+ bodyRequest().
+ withFilter("id", "eq", planet.getNormalizedText).
+ execute().
+ toString()
+ )
+}
\ No newline at end of file
diff --git a/nlpcraft-examples/solarsystem/src/main/java/org/apache/nlpcraft/examples/solarsystem/loaders/SolarSystemDiscoversValueLoader.scala b/nlpcraft-examples/solarsystem/src/main/java/org/apache/nlpcraft/examples/solarsystem/loaders/SolarSystemDiscoversValueLoader.scala
new file mode 100644
index 0000000..21a3aa0
--- /dev/null
+++ b/nlpcraft-examples/solarsystem/src/main/java/org/apache/nlpcraft/examples/solarsystem/loaders/SolarSystemDiscoversValueLoader.scala
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+package org.apache.nlpcraft.examples.solarsystem.loaders
+
+import org.apache.nlpcraft.examples.solarsystem.api.SolarSystemOpenApiService
+import org.apache.nlpcraft.model.{NCElement, NCValue, NCValueLoader}
+
+import java.util
+import scala.collection.mutable.ArrayBuffer
+import scala.jdk.CollectionConverters.{SeqHasAsJava, SetHasAsJava}
+
+class SolarSystemDiscoversValueLoader extends NCValueLoader {
+ // Example: "Scott S. Sheppard, David C. Jewitt"
+ private def mkValue(discInfo: String): NCValue = {
+ val syns = ArrayBuffer.empty[String]
+
+ for (d <- discInfo.split(",").map(_.trim).filter(_.nonEmpty)) {
+ val lc = d.toLowerCase
+
+ syns += lc
+
+ val lastNameIdx = lc.lastIndexOf(" ")
+
+ // Tries to detect last name.
+ if (lastNameIdx > 0)
+ syns += lc.substring(lastNameIdx + 1)
+ }
+
+ new NCValue {
+ override def getName: String = discInfo
+ override def getSynonyms: util.List[String] = syns.asJava
+ }
+ }
+
+ override def load(owner: NCElement): util.Set[NCValue] =
+ SolarSystemOpenApiService.getInstance().getAllDiscovers.map(mkValue).toSet.asJava
+}
diff --git a/nlpcraft-examples/solarsystem/src/main/java/org/apache/nlpcraft/examples/solarsystem/loaders/SolarSystemPlanetsValueLoader.scala b/nlpcraft-examples/solarsystem/src/main/java/org/apache/nlpcraft/examples/solarsystem/loaders/SolarSystemPlanetsValueLoader.scala
new file mode 100644
index 0000000..fb90fe3
--- /dev/null
+++ b/nlpcraft-examples/solarsystem/src/main/java/org/apache/nlpcraft/examples/solarsystem/loaders/SolarSystemPlanetsValueLoader.scala
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+package org.apache.nlpcraft.examples.solarsystem.loaders
+
+import org.apache.nlpcraft.examples.solarsystem.api.SolarSystemOpenApiService
+import org.apache.nlpcraft.model.{NCElement, NCValue, NCValueLoader}
+
+import java.util
+import scala.jdk.CollectionConverters.{SeqHasAsJava, SetHasAsJava}
+
+class SolarSystemPlanetsValueLoader extends NCValueLoader {
+ private def mkValue(id: String, v: String): NCValue =
+ new NCValue {
+ override def getName: String = id
+ override def getSynonyms: util.List[String] = Seq(id.toLowerCase, v.toLowerCase).asJava
+ }
+
+ override def load(owner: NCElement): util.Set[NCValue] =
+ SolarSystemOpenApiService.getInstance().getAllPlanets.map{ case (id, v) => mkValue(id, v) }.toSet.asJava
+}
diff --git a/nlpcraft-examples/solarsystem/src/main/resources/probe.conf b/nlpcraft-examples/solarsystem/src/main/resources/probe.conf
new file mode 100644
index 0000000..faea33f
--- /dev/null
+++ b/nlpcraft-examples/solarsystem/src/main/resources/probe.conf
@@ -0,0 +1,148 @@
+#
+# 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.
+#
+
+#
+# This is joint configuration file for both the server and the data probes. Note that
+# server and probe configuration can be placed into separate files - each file containing only
+# 'nlpcraft.server' or 'nlpcraft.probe' sub-sections.
+#
+# You can also provide configuration properties or override the default ones via environment variables.
+# To use environment variables override:
+# 1. Set probe or server JVM system property -Dconfig.override_with_env_vars=true
+# 2. For each configuration 'x.y.z' set the environment variable CONFIG_FORCE_x_y_z=some_value
+#
+# Examples of environment variables:
+# -- Overrides 'nlpcraft.sever.host' configuration property.
+# CONFIG_FORCE_nlpcraft_server_rest_host="localhost"
+#
+# -- Overrides 'nlpcraft.sever.models' configuration property.
+# CONFIG_FORCE_nlpcraft_server_models="com.models.MyModel"
+#
+# See https://nlpcraft.apache.org/server-and-probe.html for more details.
+#
+
+# Common server/probe configuration root.
+nlpcraft {
+ # Basic NLP toolkit to use on both server and probes. Possible values:
+ # - 'opennlp'
+ # - 'stanford'
+ #
+ # NOTE: Stanford CoreNLP requires special installation due to its licensing.
+ # See https://nlpcraft.apache.org/integrations.html#stanford for more details.
+ nlpEngine = "opennlp"
+
+ # External configuration resources.
+ #
+ # NOTE:
+ # ----
+ # Due to licensing restrictions of the official ASF release policy some of the
+ # configuration for NLPCraft cannot be shipped with the official Apache release.
+ # Instead, NLPCraft will attempt to download these configuration files from the
+ # external URL upon the first start.
+ #
+ # NLPCraft will attempt to download the missing configuration files from URL defined
+ # in 'nlpcraft.extConfig.extUrl' property and place them into 'nlpcraft.extConfig.locDir'
+ # folder on the local file system. On subsequent starts, NLPCraft will check if the required
+ # file is already present locally and skip the download in such case. If 'nlpcraft.extConfig.checkMd5'
+ # property is set to 'true' then on each start NLPCraft will check the checksum of each file
+ # locally and remote and will re-download such file if the MD5 checksums don't match.
+ #
+ # By default, the external configuration is stored in the main Git repository for NLPCraft
+ # project from where it will be downloaded ('/external' folder). See this folder in the Git
+ # repository for more information: https://github.com/apache/incubator-nlpcraft/raw/external_config/external
+ extConfig {
+ # Mandatory.
+ extUrl = "https://github.com/apache/incubator-nlpcraft/raw/external_config/external"
+
+ # Optional.
+ # Default value is $USER_HOME/.nlpcraft/extcfg
+ # locDir = ...
+
+ # If 'true', on each start NLPCraft will check the MD5 checksum of the each local and remote
+ # external configuration file and will re-download such file if the checksum doesn't match.
+ # Set it to 'false' to speed up the bootstrap of the NLPCraft server and the data probe if you
+ # are certain that all external configuration files are properly downloaded and available
+ # in 'nlpcraft.extConfig.locDir' local folder.
+ checkMd5 = true
+ }
+
+ # +---------------------+
+ # | Probe configuration. |
+ # +---------------------+
+ probe {
+ # Any arbitrary descriptive name.
+ id = "solarsystem"
+
+ # This is the default token (as in default company).
+ # Note that this token must match the probe token for the company this probe
+ # associated with. If changed from default, this token must be kept secure.
+ token = "3141592653589793"
+
+ # These are default up-link and down-link endpoints that the probe will connect to.
+ # If changed - they need to be changed on both server and probe.
+ upLink = "0.0.0.0:8201" # Server to probe data pipe.
+ downLink = "0.0.0.0:8202" # Probe to server data pipe.
+
+ # All JARs in this folder will be scanned for models.
+ # Safely ignored if 'null' - but then 'models' should have at least one element.
+ jarsFolder = null
+
+ # Specifies fully qualifies model class names for the probe to start with.
+ #
+ # Note that following models require 'google' on the server side.
+ # See https://nlpcraft.apache.org/integrations.html#nlp for more details
+ # on how to configure 3rd party token providers:
+ models = org.apache.nlpcraft.examples.solarsystem.SolarSystemModel
+
+ # Specify class names for probe life cycle components.
+ # Each class should extend 'NCProbeLifecycle' interface and provide a no-arg constructor.
+ #
+ # The following built-in OpenCensus exporters are supported as lifecycle components:
+ # - org.apache.nlpcraft.model.opencensus.NCJaegerExporter (traces)
+ # - org.apache.nlpcraft.model.opencensus.NCZipkinExporter (traces)
+ # - org.apache.nlpcraft.model.opencensus.NCPrometheusExporter (stats)
+ # - org.apache.nlpcraft.model.opencensus.NCStackdriverTraceExporter (traces)
+ # - org.apache.nlpcraft.model.opencensus.NCStackdriverStatsExporter (stats)
+ lifecycle = ""
+
+ # Properties for built-in OpenCensus exporters.
+ # All configuration properties are optional unless otherwise specified.
+ # opencensus {
+ # jaeger {
+ # thriftUrl = "http://127.0.0.1:14268/api/traces"
+ # serviceName = "nlpcraft-probe"
+ # }
+ # prometheus {
+ # hostPort = "localhost:8889"
+ # namespace = "nlpcraft-probe"
+ # }
+ # stackdriver {
+ # # Mandatory Google project ID.
+ # googleProjectId = "your_google_project_id"
+ # metricsPrefix = "custom.googleapis.com/nlpcraft/probe"
+ # }
+ # zipkin {
+ # v2Url = "http://127.0.0.1:9411/api/v2/spans"
+ # serviceName = "nlpcraft-probe"
+ # }
+ # }
+
+ # Maximum execution result size in bytes. Default value is 1M.
+ # When exceeded the request will be automatically rejected.
+ resultMaxSizeBytes = 1048576
+ }
+}
diff --git a/nlpcraft-examples/solarsystem/src/main/resources/solarsystem_model.yaml b/nlpcraft-examples/solarsystem/src/main/resources/solarsystem_model.yaml
new file mode 100644
index 0000000..e47e9f8
--- /dev/null
+++ b/nlpcraft-examples/solarsystem/src/main/resources/solarsystem_model.yaml
@@ -0,0 +1,85 @@
+#
+# 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.
+#
+
+id: "nlpcraft.solarsystem.ex"
+name: "Solar System Example Model"
+version: "1.0"
+description: "NLI-powered Solar system data chat example model."
+# Note that "nlpcraft:date" excluded because this NER doesn't support some dates period (before 1900 year)
+enabledBuiltInTokens: ["nlpcraft:num", "nlpcraft:limit", "nlpcraft:sort", "nlpcraft:relation"]
+
+permutateSynonyms: false
+sparse: false
+
+elements:
+ - id: "prop:name"
+ groups:
+ - "prop"
+ description: "..."
+ synonyms:
+ - "name"
+
+ - id: "prop:moon"
+ groups:
+ - "prop"
+ description: "..."
+ synonyms:
+ - "moon"
+
+ - id: "prop:mass"
+ groups:
+ - "prop"
+ - "propNum"
+ description: "..."
+ metadata:
+ filterName: "mass"
+ synonyms:
+ - "mass"
+
+ - id: "prop:radius"
+ groups:
+ - "prop"
+ - "propNum"
+ description: "..."
+ metadata:
+ filterName: "meanRadius"
+ synonyms:
+ - "radius"
+
+ - id: "prop:temperature"
+ groups:
+ - "propNum"
+ description: "..."
+ metadata:
+ filterName: "avgTemp"
+ synonyms:
+ - "{temperature|average temperature}"
+
+ - id: "prop:discoveredBy"
+ groups:
+ - "prop"
+ description: "..."
+ synonyms:
+ - "{discovered {by|_}}"
+
+ - id: "planet"
+ description: "..."
+ valueLoader: "org.apache.nlpcraft.examples.solarsystem.loaders.SolarSystemPlanetsValueLoader"
+
+ - id: "discoverer"
+ description: "..."
+ valueLoader: "org.apache.nlpcraft.examples.solarsystem.loaders.SolarSystemDiscoversValueLoader"
\ No newline at end of file
diff --git a/nlpcraft-examples/solarsystem/src/test/java/org/apache/nlpcraft/examples/solarsystem/NCModelValidationSpec.scala b/nlpcraft-examples/solarsystem/src/test/java/org/apache/nlpcraft/examples/solarsystem/NCModelValidationSpec.scala
new file mode 100644
index 0000000..bedc16d
--- /dev/null
+++ b/nlpcraft-examples/solarsystem/src/test/java/org/apache/nlpcraft/examples/solarsystem/NCModelValidationSpec.scala
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+package org.apache.nlpcraft.examples.solarsystem
+
+import org.apache.nlpcraft.model.tools.test.NCTestAutoModelValidator
+import org.junit.jupiter.api.{Assertions, Test}
+
+/**
+ * JUnit model validation.
+ */
+class NCModelValidationSpec {
+ @Test
+ def test(): Unit = {
+ // Instruct auto-validator what models to test.
+ System.setProperty("NLPCRAFT_TEST_MODELS", classOf[SolarSystemModel].getName)
+
+ // Start model auto-validator.
+ Assertions.assertTrue(NCTestAutoModelValidator.isValid(),"See error logs above.")
+ }
+}
diff --git a/nlpcraft-examples/solarsystem/src/test/java/org/apache/nlpcraft/examples/solarsystem/NCSolarSystemModelSpec.scala b/nlpcraft-examples/solarsystem/src/test/java/org/apache/nlpcraft/examples/solarsystem/NCSolarSystemModelSpec.scala
new file mode 100644
index 0000000..21b880b
--- /dev/null
+++ b/nlpcraft-examples/solarsystem/src/test/java/org/apache/nlpcraft/examples/solarsystem/NCSolarSystemModelSpec.scala
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+package org.apache.nlpcraft.examples.solarsystem
+
+import org.apache.nlpcraft.{NCTestContext, NCTestEnvironment}
+import org.junit.jupiter.api.Test
+/**
+ *
+ */
+@NCTestEnvironment(model = classOf[SolarSystemModel], startClient = true)
+class NCSolarSystemModelSpec extends NCTestContext {
+ @Test
+ def planetInfo(): Unit = {
+ checkIntent("moon", "planetInfo")
+ checkIntent("give me information about Larissa", "planetInfo")
+ }
+
+ @Test
+ def planetDate(): Unit = {
+ checkIntent("After 1900 year", "discoveryDate")
+ checkIntent("Before 1900 year", "discoveryDate")
+ checkIntent("between 1900 and 1907 years", "discoveryDate")
+ checkIntent("between 1900 and 1907", "discoveryDate")
+ }
+}
diff --git a/nlpcraft/src/main/scala/org/apache/nlpcraft/probe/mgrs/deploy/NCDeployManager.scala b/nlpcraft/src/main/scala/org/apache/nlpcraft/probe/mgrs/deploy/NCDeployManager.scala
index 332dd26..827846f 100644
--- a/nlpcraft/src/main/scala/org/apache/nlpcraft/probe/mgrs/deploy/NCDeployManager.scala
+++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/probe/mgrs/deploy/NCDeployManager.scala
@@ -1899,7 +1899,7 @@
s"origin=${wrappedMdl.getOrigin}, " +
s"intentIds=${unusedIntents.map(_.id).mkString("(", ", ", ")")}]"
)
-
+
intents.toSet
}
diff --git a/pom.xml b/pom.xml
index da61813..c7ba319 100644
--- a/pom.xml
+++ b/pom.xml
@@ -40,6 +40,7 @@
<modules>
<module>nlpcraft</module>
+ <module>nlpcraft-examples/solarsystem</module>
</modules>
<developers>