This is an automated email from the ASF dual-hosted git repository. sergeykamov pushed a commit to branch NLPCRAFT-479 in repository https://gitbox.apache.org/repos/asf/incubator-nlpcraft.git
commit c357f02c0a4817596c2f9e36928e5a10f35ff0ca Author: Sergey Kamov <skhdlem...@gmail.com> AuthorDate: Thu Feb 24 17:38:12 2022 +0300 RU Lightswitch simplified example. --- nlpcraft-examples/lightswitch-ru/README.md | 50 ++++++++++++ nlpcraft-examples/lightswitch-ru/pom.xml | 94 ++++++++++++++++++++++ .../examples/lightswitch/LightSwitchModelRu.scala | 89 ++++++++++++++++++++ .../lightswitch/ru/NCSemanticStemmerRu.scala | 26 ++++++ .../ru/NCStopWordsTokenEnricherRu.scala | 43 ++++++++++ .../examples/lightswitch/ru/NCTokenParserRu.scala | 78 ++++++++++++++++++ .../src/main/resources/lightswitch_model_ru.yaml | 45 +++++++++++ .../lightswitch/NCModelValidationSpec.scala | 32 ++++++++ .../internal/impl/NCModelPipelineManager.scala | 26 +++++- pom.xml | 1 + 10 files changed, 480 insertions(+), 4 deletions(-) diff --git a/nlpcraft-examples/lightswitch-ru/README.md b/nlpcraft-examples/lightswitch-ru/README.md new file mode 100644 index 0000000..f073eff --- /dev/null +++ b/nlpcraft-examples/lightswitch-ru/README.md @@ -0,0 +1,50 @@ +<!-- + 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. +--> + +<img alt="" src="https://nlpcraft.apache.org/images/nlpcraft_logo_black.gif" height="80px"> +<br> + +[](https://raw.githubusercontent.com/apache/opennlp/master/LICENSE) +[](https://github.com/apache/incubator-nlpcraft/actions) +[](https://nlpcraft.apache.org/docs.html) +[](https://gitter.im/apache-nlpcraft/community) + +### Light Switch Example +This example provides very simple implementation for NLI-powered light switch. You can say something like `turn the lights off in +the entire house` or `switch on the illumination in the master bedroom closet`. +You can easily modify intent callbacks to perform the actual light switching using HomeKit or Arduino-based +controllers. + +### Documentation +See [Light Switch](https://nlpcraft.apache.org/examples/light_switch.html) guide for more instructions on how to run this example. + +For any questions, feedback or suggestions: + + * View & run other [examples](https://github.com/apache/incubator-nlpcraft/tree/master/nlpcraft-examples) + * Read [documentation](https://nlpcraft.apache.org/docs.html), latest [Javadoc](https://nlpcraft.apache.org/apis/latest/index.html) and [REST APIs](https://nlpcraft.apache.org/using-rest.html) + * Download & Maven/Grape/Gradle/SBT [instructions](https://nlpcraft.apache.org/download.html) + * File a bug or improvement in [JIRA](https://issues.apache.org/jira/projects/NLPCRAFT) + * Post a question at [Stack Overflow](https://stackoverflow.com/questions/ask) using <code>nlpcraft</code> tag + * Access [GitHub](https://github.com/apache/incubator-nlpcraft) mirror repository. + * Join project developers on [d...@nlpcraft.apache.org](mailto:dev-subscr...@nlpcraft.apache.org) + +### Copyright +Copyright (C) 2021 Apache Software Foundation + +<img src="https://www.apache.org/img/ASF20thAnniversary.jpg" height="64px" alt="ASF Logo"> + + diff --git a/nlpcraft-examples/lightswitch-ru/pom.xml b/nlpcraft-examples/lightswitch-ru/pom.xml new file mode 100644 index 0000000..67c610f --- /dev/null +++ b/nlpcraft-examples/lightswitch-ru/pom.xml @@ -0,0 +1,94 @@ +<?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 Light Switch RU</name> + <artifactId>nlpcraft-example-lightswitch-ru</artifactId> + + <parent> + <artifactId>nlpcraft-parent</artifactId> + <groupId>org.apache.nlpcraft</groupId> + <version>1.0.0</version> + <relativePath>../../pom.xml</relativePath> + </parent> + + <dependencies> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>nlpcraft</artifactId> + <version>${project.version}</version> + </dependency> + + <dependency> + <groupId>org.apache.lucene</groupId> + <artifactId>lucene-analyzers-common</artifactId> + <version>8.11.1</version> + </dependency> + + <dependency> + <groupId>org.languagetool</groupId> + <artifactId>language-de</artifactId> + <version>5.6</version> + </dependency> + <dependency> + <groupId>org.languagetool</groupId> + <artifactId>language-ru</artifactId> + <version>5.6</version> + </dependency> + + <dependency> + <groupId>org.languagetool</groupId> + <artifactId>languagetool-core</artifactId> + <version>5.6</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> + </plugins> + </build> +</project> \ No newline at end of file diff --git a/nlpcraft-examples/lightswitch-ru/src/main/java/org/apache/nlpcraft/examples/lightswitch/LightSwitchModelRu.scala b/nlpcraft-examples/lightswitch-ru/src/main/java/org/apache/nlpcraft/examples/lightswitch/LightSwitchModelRu.scala new file mode 100644 index 0000000..67af24a --- /dev/null +++ b/nlpcraft-examples/lightswitch-ru/src/main/java/org/apache/nlpcraft/examples/lightswitch/LightSwitchModelRu.scala @@ -0,0 +1,89 @@ +/* + * 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.lightswitch + +import org.apache.nlpcraft.* +import org.apache.nlpcraft.examples.lightswitch.ru.* +import org.apache.nlpcraft.nlp.entity.parser.nlp.NCNLPEntityParser +import org.apache.nlpcraft.nlp.entity.parser.semantic.NCSemanticEntityParser +import org.apache.nlpcraft.nlp.entity.parser.semantic.impl.en.NCEnSemanticPorterStemmer +import org.apache.nlpcraft.nlp.token.parser.opennlp.NCOpenNLPTokenParser +import org.apache.nlpcraft.nlp.token.enricher.en.NCStopWordsTokenEnricher + +/** + * This example provides very simple implementation for NLI-powered light switch. + * You can say something like this: + * <ul> + * <li>"Turn the lights off in the entire house."</li> + * <li>"Switch on the illumination in the master bedroom closet."</li> + * </ul> + * You can easily modify intent callbacks to perform the actual light switching using + * HomeKit or Arduino-based controllers. + * <p> + * See 'README.md' file in the same folder for running and testing instructions. + */ + +class LightSwitchModelRu extends NCModel: + override val getConfig: NCModelConfig = new NCModelConfig("nlpcraft.lightswitch.ru.ex", "LightSwitch Example Model RU", "1.0") + override val getPipeline: NCModelPipeline = + val tp = new NCTokenParserRu + new NCModelPipelineBuilder( + tp, + new NCSemanticEntityParser(new NCSemanticStemmerRu(), tp, "lightswitch_model_ru.yaml") + ). + withTokenEnricher(new NCStopWordsTokenEnricherRu()). + build() + + /** + * Intent and its on-match callback. + * + * @param actEnt Token from `act` term (guaranteed to be one). + * @param locEnts Tokens from `loc` term (zero or more). + * @return Query result to be sent to the REST caller. + */ + @NCIntent("intent=ls term(act)={has(ent_groups, 'act')} term(loc)={# == 'ls:loc'}*") + @NCIntentSample(Array( + "Выключи свет по всем доме", + "Выруби электричество!", + "Включи свет в детской", + "Включай повсюду освещение", + "Зажигай лампы в детской комнате", + "Свет на кухне пожалуйста приглуши", + "Нельзя ли повсюду выключить свет", + "Пожалуйста без света", + "Отключи электричесвто в ванной", + "Выключи, пожалуйста, тут всюду свет", + "Выключай все!", + "Свет пожалуйсте везде включи" + )) + def onMatch( + @NCIntentTerm("act") actEnt: NCEntity, + @NCIntentTerm("loc") locEnts: List[NCEntity] + ): NCResult = + val status = if actEnt.getId == "ls:on" then "on" else "off" + val locations = if locEnts.isEmpty then "entire house" else locEnts.map(_.mkText()).mkString(", ") + + // Add HomeKit, Arduino or other integration here. + + // By default - just return a descriptive action string. + val res = new NCResult() + + res.setType(NCResultType.ASK_RESULT) + res.setBody(s"Lights are [$status] in [${locations.toLowerCase}].") + + res \ No newline at end of file diff --git a/nlpcraft-examples/lightswitch-ru/src/main/java/org/apache/nlpcraft/examples/lightswitch/ru/NCSemanticStemmerRu.scala b/nlpcraft-examples/lightswitch-ru/src/main/java/org/apache/nlpcraft/examples/lightswitch/ru/NCSemanticStemmerRu.scala new file mode 100644 index 0000000..e49c72c --- /dev/null +++ b/nlpcraft-examples/lightswitch-ru/src/main/java/org/apache/nlpcraft/examples/lightswitch/ru/NCSemanticStemmerRu.scala @@ -0,0 +1,26 @@ +/* + * 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.lightswitch.ru + +import opennlp.tools.stemmer.snowball.SnowballStemmer +import org.apache.nlpcraft.nlp.entity.parser.semantic.NCSemanticStemmer + +class NCSemanticStemmerRu extends NCSemanticStemmer: + private val stemmer = new SnowballStemmer(SnowballStemmer.ALGORITHM.RUSSIAN) + + override def stem(txt: String): String = stemmer.synchronized { stemmer.stem(txt.toLowerCase).toString } diff --git a/nlpcraft-examples/lightswitch-ru/src/main/java/org/apache/nlpcraft/examples/lightswitch/ru/NCStopWordsTokenEnricherRu.scala b/nlpcraft-examples/lightswitch-ru/src/main/java/org/apache/nlpcraft/examples/lightswitch/ru/NCStopWordsTokenEnricherRu.scala new file mode 100644 index 0000000..0e9c064 --- /dev/null +++ b/nlpcraft-examples/lightswitch-ru/src/main/java/org/apache/nlpcraft/examples/lightswitch/ru/NCStopWordsTokenEnricherRu.scala @@ -0,0 +1,43 @@ +/* + * 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.lightswitch.ru + +import org.apache.lucene.analysis.ru.RussianAnalyzer +import org.apache.nlpcraft.* + +import java.util +import scala.jdk.CollectionConverters.* + +/** + * + */ +class NCStopWordsTokenEnricherRu extends NCTokenEnricher: + private final val stops = RussianAnalyzer.getDefaultStopSet + + override def enrich(req: NCRequest, cfg: NCModelConfig, toks: util.List[NCToken]): Unit = + toks.asScala.foreach(t => + t.put( + "stopword", + t.getLemma.length == 1 && !Character.isLetter(t.getLemma.head) || + t.getPos.startsWith("PARTICLE") || + t.getPos.startsWith("INTERJECTION") || + t.getPos.startsWith("PREP") || + stops.contains(t.getLemma) || + stops.contains(t.getText.toLowerCase) + ) + ) diff --git a/nlpcraft-examples/lightswitch-ru/src/main/java/org/apache/nlpcraft/examples/lightswitch/ru/NCTokenParserRu.scala b/nlpcraft-examples/lightswitch-ru/src/main/java/org/apache/nlpcraft/examples/lightswitch/ru/NCTokenParserRu.scala new file mode 100644 index 0000000..5bda243 --- /dev/null +++ b/nlpcraft-examples/lightswitch-ru/src/main/java/org/apache/nlpcraft/examples/lightswitch/ru/NCTokenParserRu.scala @@ -0,0 +1,78 @@ +/* + * 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.lightswitch.ru + +import org.apache.lucene.analysis.ru.RussianAnalyzer +import org.apache.nlpcraft.* +import org.languagetool.AnalyzedToken +import org.languagetool.language.Russian +import org.languagetool.rules.ngrams.* +import org.languagetool.tagging.ru.* +import org.languagetool.tokenizers.WordTokenizer + +import java.util +import scala.jdk.CollectionConverters.* + +object NCTokenParserRu: + private val tokenizer = new WordTokenizer + + private case class Span(word: String, start: Int, end: Int) + + private def nvl(v: String, dflt : => String): String = if v != null then v else dflt + + private def split(text: String): Seq[Span] = + val spans = collection.mutable.ArrayBuffer.empty[Span] + var sumLen = 0 + + for (((word, len), idx) <- tokenizer.tokenize(text).asScala.map(p => p -> p.length).zipWithIndex) + if word.strip.nonEmpty then spans += Span(word, sumLen, sumLen + word.length) + sumLen += word.length + + spans.toSeq + +import org.apache.nlpcraft.examples.lightswitch.ru.NCTokenParserRu.* + +class NCTokenParserRu extends NCTokenParser: + override def tokenize(text: String): util.List[NCToken] = + val spans = split(text) + val tags = RussianTagger.INSTANCE.tag(spans.map(_.word).asJava).asScala + + require(spans.size == tags.size) + + spans.zip(tags).zipWithIndex.map { case ((span, tag), idx) => + val readings = tag.getReadings.asScala + + val (lemma, pos) = + readings.size match + // No data. Lemma is word as is, POS is undefined. + case 0 => (span.word, "") + // Takes first. Other variants ignored. + case _ => + val aTok: AnalyzedToken = readings.head + (nvl(aTok.getLemma, span.word), nvl(aTok.getPOSTag, "")) + + val tok: NCToken = + new NCPropertyMapAdapter with NCToken: + override val getText: String = span.word + override val getIndex: Int = idx + override val getStartCharIndex: Int = span.start + override val getEndCharIndex: Int = span.end + override val getLemma: String = lemma.toLowerCase // TODO: discuss + override val getPos: String = pos + tok + }.asJava \ No newline at end of file diff --git a/nlpcraft-examples/lightswitch-ru/src/main/resources/lightswitch_model_ru.yaml b/nlpcraft-examples/lightswitch-ru/src/main/resources/lightswitch_model_ru.yaml new file mode 100644 index 0000000..f8294ac --- /dev/null +++ b/nlpcraft-examples/lightswitch-ru/src/main/resources/lightswitch_model_ru.yaml @@ -0,0 +1,45 @@ +# +# 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. +# + +macros: + "<TURN_ON>" : "{включить|включать|врубить|врубать|запустить|запускать|зажигать|зажечь}" + "<TURN_OFF>" : "{погасить|загасить|гасить|выключить|выключать|вырубить|вырубать|отключить|отключать|убрать|убирать|приглушить|приглушать|стоп}" + "<ENTIRE_OPT>" : "{весь|все|всё|повсюду|вокруг|полностью|везде|_}" + "<LIGHT_OPT>" : "{это|лампа|бра|люстра|светильник|лампочка|лампа|освещение|свет|электричество|электрика|_}" + +elements: + - id: "ls:loc" + description: "Location of lights." + synonyms: + - "<ENTIRE_OPT> {здание|помещение|дом|кухня|детская|кабинет|гостиная|спальня|ванная|туалет|{большая|обеденная|ванная|детская|туалетная} комната}" + + - id: "ls:on" + groups: + - "act" + description: "Light switch ON action." + synonyms: + - "<LIGHT_OPT> <ENTIRE_OPT> <TURN_ON>" + - "<TURN_ON> <ENTIRE_OPT> <LIGHT_OPT>" + + - id: "ls:off" + groups: + - "act" + description: "Light switch OFF action." + synonyms: + - "<LIGHT_OPT> <ENTIRE_OPT> <TURN_OFF>" + - "<TURN_OFF> <ENTIRE_OPT> <LIGHT_OPT>" + - "без <ENTIRE_OPT> <LIGHT_OPT>" diff --git a/nlpcraft-examples/lightswitch-ru/src/test/java/org/apache/nlpcraft/examples/lightswitch/NCModelValidationSpec.scala b/nlpcraft-examples/lightswitch-ru/src/test/java/org/apache/nlpcraft/examples/lightswitch/NCModelValidationSpec.scala new file mode 100644 index 0000000..b6d9d1b --- /dev/null +++ b/nlpcraft-examples/lightswitch-ru/src/test/java/org/apache/nlpcraft/examples/lightswitch/NCModelValidationSpec.scala @@ -0,0 +1,32 @@ +/* + * 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.lightswitch + +import org.apache.nlpcraft.* +import org.junit.jupiter.api.* + +import scala.util.Using + +/** + * JUnit models validation. + */ +class NCModelValidationSpec: + private val MDL = new LightSwitchModelRu + + @Test + def test(): Unit = Using.resource(new NCModelClient(MDL)) { client => client.validateSamples() } diff --git a/nlpcraft/src/main/scala/org/apache/nlpcraft/internal/impl/NCModelPipelineManager.scala b/nlpcraft/src/main/scala/org/apache/nlpcraft/internal/impl/NCModelPipelineManager.scala index e1f56cb..afcd63a 100644 --- a/nlpcraft/src/main/scala/org/apache/nlpcraft/internal/impl/NCModelPipelineManager.scala +++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/internal/impl/NCModelPipelineManager.scala @@ -86,6 +86,15 @@ class NCModelPipelineManager(cfg: NCModelConfig, pipeline: NCModelPipeline) exte /** * + * @param m + * @return + */ + private def mkProps(m: NCPropertyMap): String = + if m.keysSet().isEmpty then "" + else m.keysSet().asScala.toSeq.sorted.map(p => s"$p=${m.get[Any](p)}").mkString("{", ", ", "}") + + /** + * * @param txt * @param data * @param usrId @@ -119,6 +128,19 @@ class NCModelPipelineManager(cfg: NCModelConfig, pipeline: NCModelPipeline) exte check() e.enrich(req, cfg, toks) + val tbl = NCAsciiTable("Text", "Lemma", "POS", "Start index", "End index", "Properties") + + for (t <- toks.asScala) + tbl += ( + t.getText, + t.getLemma, + t.getPos, + t.getStartCharIndex, + t.getEndCharIndex, + mkProps(t) + ) + tbl.info(logger, Option(s"Tokens for: ${req.getText}")) + // NOTE: we run validators regardless of whether token list is empty. for (v <- tokVals) check() @@ -169,10 +191,6 @@ class NCModelPipelineManager(cfg: NCModelConfig, pipeline: NCModelPipeline) exte for ((v, i) <- vrnts.zipWithIndex) val tbl = NCAsciiTable("EntityId", "Tokens", "Tokens Position", "Properties") - def mkProps(m: NCPropertyMap): String = - if m.keysSet().isEmpty then "" - else m.keysSet().asScala.toSeq.sorted.map(p => s"$p=${m.get[Any](p)}").mkString("{", ", ", "}") - for (e <- v.getEntities.asScala) val toks = e.getTokens.asScala tbl += ( diff --git a/pom.xml b/pom.xml index fc180eb..49c430d 100644 --- a/pom.xml +++ b/pom.xml @@ -393,6 +393,7 @@ <module>nlpcraft-examples/lightswitch</module> <module>nlpcraft-examples/time</module> <module>nlpcraft-examples/weather</module> + <module>nlpcraft-examples/lightswitch-ru</module> </modules> </profile> </profiles>