This is an automated email from the ASF dual-hosted git repository.

sergeykamov pushed a commit to branch NLPCRAFT-490
in repository https://gitbox.apache.org/repos/asf/incubator-nlpcraft.git

commit 8f100a4dc26ac41d17e71f49e42d395a9e7fdee9
Author: Sergey Kamov <[email protected]>
AuthorDate: Tue Mar 29 13:30:02 2022 +0300

    WIP.
---
 .../scala/org/apache/nlpcraft/NCModelClient.java   | 17 +++-
 .../scala/org/apache/nlpcraft/NCWinnerIntent.java  | 29 +++++++
 .../nlpcraft/internal/impl/NCModelClientImpl.scala | 11 ++-
 .../intent/matcher/NCIntentSolverManager.scala     | 87 ++++++++++++--------
 .../nlpcraft/internal/impl/NCModelClientSpec.scala |  5 +-
 .../internal/impl/NCModelClientSpec2.scala         | 96 ++++++++++++++++++++++
 6 files changed, 200 insertions(+), 45 deletions(-)

diff --git a/nlpcraft/src/main/scala/org/apache/nlpcraft/NCModelClient.java 
b/nlpcraft/src/main/scala/org/apache/nlpcraft/NCModelClient.java
index 7b98fd9..a0a6ba0 100644
--- a/nlpcraft/src/main/scala/org/apache/nlpcraft/NCModelClient.java
+++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/NCModelClient.java
@@ -20,7 +20,6 @@ package org.apache.nlpcraft;
 import org.apache.nlpcraft.internal.impl.NCModelClientImpl;
 
 import java.util.Map;
-import java.util.List;
 import java.util.function.Predicate;
 
 /**
@@ -100,7 +99,19 @@ public class NCModelClient implements AutoCloseable {
         impl.validateSamples();
     }
 
-    public List<List<NCEntity>> validateIntentArguments(String txt, 
Map<String, Object> data, String usrId) {
-        return impl.validateIntentArguments(txt, data, usrId);
+    /**
+     * TODO:
+     * Gets intent information which contains intent ID and its callback 
arguments entities.
+     * Note that
+     *  - Callback is not called  in this case.
+     *  - if model `onContext` method overrided - error thrown because we 
don't find intents in this case.
+     *
+     * @param txt
+     * @param data
+     * @param usrId
+     * @return
+     */
+    public NCWinnerIntent getWinnerIntent(String txt, Map<String, Object> 
data, String usrId) {
+        return impl.getWinnerIntent(txt, data, usrId);
     }
 }
diff --git a/nlpcraft/src/main/scala/org/apache/nlpcraft/NCWinnerIntent.java 
b/nlpcraft/src/main/scala/org/apache/nlpcraft/NCWinnerIntent.java
new file mode 100644
index 0000000..56baca7
--- /dev/null
+++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/NCWinnerIntent.java
@@ -0,0 +1,29 @@
+/*
+ * 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
+ *
+ *      https://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;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * TODO:
+ */
+public interface NCWinnerIntent {
+    String getIntentId();
+    List<List<NCEntity>> getArguments();
+}
diff --git 
a/nlpcraft/src/main/scala/org/apache/nlpcraft/internal/impl/NCModelClientImpl.scala
 
b/nlpcraft/src/main/scala/org/apache/nlpcraft/internal/impl/NCModelClientImpl.scala
index 83cb579..7c605b5 100644
--- 
a/nlpcraft/src/main/scala/org/apache/nlpcraft/internal/impl/NCModelClientImpl.scala
+++ 
b/nlpcraft/src/main/scala/org/apache/nlpcraft/internal/impl/NCModelClientImpl.scala
@@ -75,7 +75,7 @@ class NCModelClientImpl(mdl: NCModel) extends LazyLogging:
         dlgMgr.start()
         plMgr.start()
 
-    private def ask0(txt: String, data: JMap[String, AnyRef], usrId: String, 
isValidation: Boolean): NCResult =
+    private def ask0(txt: String, data: JMap[String, AnyRef], usrId: String, 
testRun: Boolean): Either[NCResult, NCWinnerIntent] =
         val plData = plMgr.prepare(txt, data, usrId)
 
         val userId = plData.request.getUserId
@@ -99,8 +99,7 @@ class NCModelClientImpl(mdl: NCModel) extends LazyLogging:
                 override val getVariants: util.Collection[NCVariant] = 
plData.variants.asJava
                 override val getTokens: JList[NCToken] = plData.tokens
 
-        intentsMgr.solve(mdl, ctx, isValidation)
-
+        intentsMgr.solve(mdl, ctx, testRun)
 
      /*
       * @param txt
@@ -108,7 +107,7 @@ class NCModelClientImpl(mdl: NCModel) extends LazyLogging:
       * @param usrId
       * @return
       */
-    def ask(txt: String, data: JMap[String, AnyRef], usrId: String): NCResult 
= ask0(txt, data, usrId, false)
+    def ask(txt: String, data: JMap[String, AnyRef], usrId: String): NCResult 
= ask0(txt, data, usrId, false).swap.toOption.get
 
     /**
       *
@@ -191,5 +190,5 @@ class NCModelClientImpl(mdl: NCModel) extends LazyLogging:
         dlgMgr.close()
         convMgr.close()
 
-    def validateIntentArguments(txt: String, data: JMap[String, AnyRef], 
usrId: String): JList[JList[NCEntity]] =
-        ask0(txt, data, usrId, 
true).getBody.asInstanceOf[JList[JList[NCEntity]]]
\ No newline at end of file
+    def getWinnerIntent(txt: String, data: JMap[String, AnyRef], usrId: 
String): NCWinnerIntent =
+        ask0(txt, data, usrId, true).toOption.get
\ No newline at end of file
diff --git 
a/nlpcraft/src/main/scala/org/apache/nlpcraft/internal/intent/matcher/NCIntentSolverManager.scala
 
b/nlpcraft/src/main/scala/org/apache/nlpcraft/internal/intent/matcher/NCIntentSolverManager.scala
index 28fd705..b46f38f 100644
--- 
a/nlpcraft/src/main/scala/org/apache/nlpcraft/internal/intent/matcher/NCIntentSolverManager.scala
+++ 
b/nlpcraft/src/main/scala/org/apache/nlpcraft/internal/intent/matcher/NCIntentSolverManager.scala
@@ -69,6 +69,7 @@ object NCIntentSolverManager:
       * @param entities
       */
     private case class IntentTermEntities(termId: Option[String], entities: 
Seq[NCEntity])
+    private case class NCWinnerIntentImpl(getIntentId: String, getArguments: 
JList[JList[NCEntity]]) extends NCWinnerIntent
 
     /**
       *
@@ -162,12 +163,14 @@ object NCIntentSolverManager:
       */
     private case class IntentEntity(var used: Boolean, var conv: Boolean, 
entity: NCEntity)
 
+    type ResultData = Either[NCResult, NCWinnerIntent]
+
     /**
       *
       * @param result
       * @param intentMatch
       */
-    private case class IterationResult(result: NCResult, intentMatch: 
NCIntentMatch)
+    private case class IterationResult(result: ResultData, intentMatch: 
NCIntentMatch)
 
     /**
       * @param termId
@@ -612,7 +615,7 @@ class NCIntentSolverManager(dialog: NCDialogFlowManager, 
intents: Map[NCIDLInten
                 Option.when(depth >= 0)(depth + 1)
 
             val convDepthsSum = -usedEnts.flatMap(getConversationDepth).sum
-            
+
             // Mark found entities as used.
             for (e <- usedEnts) e.used = true
 
@@ -622,10 +625,10 @@ class NCIntentSolverManager(dialog: NCDialogFlowManager, 
intents: Map[NCIDLInten
       *
       * @param mdl
       * @param ctx
-      * @param isValidation
+      * @param testRun
       * @return
       */
-    private def solveIteration(mdl: NCModel, ctx: NCContext, isValidation: 
Boolean): Option[IterationResult] =
+    private def solveIteration(mdl: NCModel, ctx: NCContext, testRun: 
Boolean): Option[IterationResult] =
         require(intents.nonEmpty)
 
         val req = ctx.getRequest
@@ -637,17 +640,18 @@ class NCIntentSolverManager(dialog: NCDialogFlowManager, 
intents: Map[NCIDLInten
         if intentResults.isEmpty then throw new NCRejection("No matching 
intent found.")
 
         object Loop:
-            private var data: Option[Option[IterationResult]] = None
-            private var stopped: Boolean = false
+            private var data: Option[IterationResult] = _
 
-            def hasNext: Boolean = !stopped
+            def hasNext: Boolean = data == null
             def finish(data: Option[IterationResult] = None): Unit =
-                Loop.data = Option(data)
-                Loop.stopped = true
-            def result: Option[IterationResult] = data.getOrElse(throw new 
NCRejection("No matching intent found - all intents were skipped."))
+                require(data != null)
+                Loop.data = data
+            def result: Option[IterationResult] =
+                if data == null then throw new NCRejection("No matching intent 
found - all intents were skipped.")
+                data
 
         for (intentRes <- intentResults.filter(_ != null) if Loop.hasNext)
-            val intentMatch: NCIntentMatch =
+            val im: NCIntentMatch =
                 new NCIntentMatch:
                     override val getContext: NCContext = ctx
                     override val getIntentId: String = intentRes.intentId
@@ -661,15 +665,18 @@ class NCIntentSolverManager(dialog: NCDialogFlowManager, 
intents: Map[NCIDLInten
                         new NCVariant:
                             override def getEntities: JList[NCEntity] = 
intentRes.variant.entities.asJava
             try
-                if mdl.onMatchedIntent(intentMatch) then
+                if mdl.onMatchedIntent(im) then
                     // This can throw NCIntentSkip exception.
-                    val cbRes = if isValidation then new 
NCResult(intentMatch.getIntentEntities, NCResultType.ASK_RESULT) else 
intentRes.fn(intentMatch)
-                    // Store won intent match in the input.
-                    if cbRes.getIntentId == null then
-                        cbRes.setIntentId(intentRes.intentId)
-                    logger.info(s"Intent '${intentRes.intentId}' for variant 
#${intentRes.variantIdx + 1} selected as the <|best match|>")
-                    dialog.addMatchedIntent(intentMatch, cbRes, ctx)
-                    Loop.finish(Option(IterationResult(cbRes, intentMatch)))
+                    if testRun then
+                        
Loop.finish(Option(IterationResult(Right(NCWinnerIntentImpl(im.getIntentId, 
im.getIntentEntities)), im)))
+                    else
+                        val cbRes = intentRes.fn(im)
+                        // Store won intent match in the input.
+                        if cbRes.getIntentId == null then
+                            cbRes.setIntentId(intentRes.intentId)
+                        logger.info(s"Intent '${intentRes.intentId}' for 
variant #${intentRes.variantIdx + 1} selected as the <|best match|>")
+                        dialog.addMatchedIntent(im, cbRes, ctx)
+                        Loop.finish(Option(IterationResult(Left(cbRes), im)))
                 else
                     logger.info(s"Model '${ctx.getModelConfig.getId}' 
triggered rematching of intents by intent '${intentRes.intentId}' on variant 
#${intentRes.variantIdx + 1}.")
                     Loop.finish()
@@ -686,17 +693,17 @@ class NCIntentSolverManager(dialog: NCDialogFlowManager, 
intents: Map[NCIDLInten
       *
       * @param mdl
       * @param ctx
-      * @param isValidation
+      * @param testRun
       * @return
       */
-    def solve(mdl: NCModel, ctx: NCContext, isValidation: Boolean): NCResult =
-        var res: NCResult = mdl.onContext(ctx)
+    def solve(mdl: NCModel, ctx: NCContext, testRun: Boolean): ResultData =
+        val ctxRes = mdl.onContext(ctx)
 
-        if res != null then
-            // TODO: text.
-            if intents.nonEmpty then logger.warn("`onContext` method overrides 
existed intents. They are ignored.")
+        if ctxRes != null then
+            if testRun then E("`onContext` method overriden, intents cannot be 
found.") // TODO: test
+            if intents.nonEmpty then logger.warn("`onContext` method overrides 
existed intents. They are ignored.") // TODO: text.
 
-            res
+            Left(ctxRes)
         else
             if intents.isEmpty then
                 // TODO: text.
@@ -706,17 +713,29 @@ class NCIntentSolverManager(dialog: NCDialogFlowManager, 
intents: Map[NCIDLInten
 
             try
                 while (loopRes == null)
-                    solveIteration(mdl, ctx, isValidation) match
+                    solveIteration(mdl, ctx, testRun) match
                         case Some(iterRes) => loopRes = iterRes
                         case None => // No-op.
 
-                res = mdl.onResult(loopRes.intentMatch, loopRes.result)
-
-                if res != null then res else loopRes.result
+                if testRun then
+                    loopRes.result
+                else
+                    mdl.onResult(loopRes.intentMatch, 
loopRes.result.swap.toOption.get) match
+                        case null => loopRes.result
+                        case res => Left(res)
             catch
                 case e: NCRejection =>
-                    val res = mdl.onRejection(if loopRes != null then 
loopRes.intentMatch else null, e)
-                    if res != null then res else throw e
+                    if testRun then throw e
+
+                    mdl.onRejection(if loopRes != null then 
loopRes.intentMatch else null, e) match
+                        case null => throw e
+                        case res => Left(res)
                 case e: Throwable =>
-                    val res = mdl.onError(ctx, e)
-                    if res != null then res else throw e
\ No newline at end of file
+                    if testRun then throw e
+
+                    mdl.onError(ctx, e) match
+                        case null => throw e
+                        case res =>
+                            logger.warn("Error during execution.", e)
+
+                            Left(res)
diff --git 
a/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/impl/NCModelClientSpec.scala
 
b/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/impl/NCModelClientSpec.scala
index 9168b8b..f50fb14 100644
--- 
a/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/impl/NCModelClientSpec.scala
+++ 
b/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/impl/NCModelClientSpec.scala
@@ -40,9 +40,10 @@ class NCModelClientSpec:
             println(s"Body: ${res.getBody}")
 
             client.validateSamples()
-            val entities = client.validateIntentArguments("Lights on at second 
floor kitchen", null, "userId")
 
-            println("Entities: \n" + entities.asScala.map(p => 
p.asScala.map(s).mkString(", ")).mkString("\n"))
+            val winner = client.getWinnerIntent("Lights on at second floor 
kitchen", null, "userId")
+            println(s"Winner intent: ${winner.getIntentId}")
+            println("Entities: \n" + winner.getArguments.asScala.map(p => 
p.asScala.map(s).mkString(", ")).mkString("\n"))
         }
     /**
       *
diff --git 
a/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/impl/NCModelClientSpec2.scala
 
b/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/impl/NCModelClientSpec2.scala
new file mode 100644
index 0000000..99e5d3b
--- /dev/null
+++ 
b/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/impl/NCModelClientSpec2.scala
@@ -0,0 +1,96 @@
+/*
+ * 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
+ *
+ *      https://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.internal.impl
+
+import org.apache.nlpcraft.*
+import org.apache.nlpcraft.nlp.entity.parser.*
+import org.apache.nlpcraft.nlp.entity.parser.semantic.*
+import org.apache.nlpcraft.nlp.util.*
+import org.junit.jupiter.api.Test
+
+import java.util
+import java.util.List as JList
+import scala.collection.mutable
+import scala.jdk.CollectionConverters.*
+import scala.util.Using
+
+class NCModelClientSpec2:
+    @Test
+    def test(): Unit =
+        import NCSemanticTestElement as TE
+
+        val mdl = new NCModel:
+            override def getConfig: NCModelConfig = CFG
+            override def getPipeline: NCPipeline = new 
NCPipelineBuilder().withSemantic("en", Seq(TE("e1"), TE("e2")).asJava).build()
+
+            @NCIntent("intent=i1 term(t1)={# == 'e1'} term(t2List)={# == 
'e2'}*")
+            def onMatch(@NCIntentTerm("t1") act: NCEntity, 
@NCIntentTerm("t2List") locs: List[NCEntity]): NCResult =
+                E("Shouldn't be called.")
+
+        Using.resource(new NCModelClient(mdl)) { client =>
+            case class Result(txt: String):
+                private val wi = client.getWinnerIntent(txt, null, "userId")
+                private val allArgs: JList[JList[NCEntity]] = wi.getArguments
+
+                val intentId: String = wi.getIntentId
+                val size: Int = allArgs.size()
+
+                lazy val first: Seq[NCEntity] = 
allArgs.asScala.head.asScala.toSeq
+                lazy val second: Seq[NCEntity] = 
allArgs.asScala.last.asScala.toSeq
+
+                // 1. One argument.
+            var res = Result("e1")
+
+            require(res.intentId == "i1")
+            require(res.size == 2)
+
+            def check(e: NCEntity, txt: String): Unit =
+                require(e.mkText() == txt)
+                // All data aren't lost.
+                require(e.getTokens.get(0).keysSet().contains("lemma"))
+
+            require(res.first.size == 1)
+            check(res.first.head, "e1")
+
+            require(res.second.isEmpty)
+
+            // 2. One argument.
+            res = Result("e1 e2 e2")
+
+            require(res.intentId == "i1")
+            require(res.size == 2)
+
+            require(res.first.size == 1)
+            check(res.first.head, "e1")
+
+            require(res.second.size == 2)
+            check(res.second.head, "e2")
+            check(res.second.last, "e2")
+
+            // 3. No winners.
+            try
+                client.getWinnerIntent("x", null, "userId")
+
+                require(false)
+            catch
+                case e: NCRejection => println(s"Expected rejection: 
${e.getMessage}")
+                case e: Throwable => throw e
+        }
+
+
+

Reply via email to