This is an automated email from the ASF dual-hosted git repository.
sergeykamov pushed a commit to branch NLPCRAFT-477
in repository https://gitbox.apache.org/repos/asf/incubator-nlpcraft.git
The following commit(s) were added to refs/heads/NLPCRAFT-477 by this push:
new a9dd255 Dialog manager added.
a9dd255 is described below
commit a9dd2551fdea773701eb0e3733ba4c4a5aec93b4
Author: Sergey Kamov <[email protected]>
AuthorDate: Thu Feb 3 16:30:33 2022 +0300
Dialog manager added.
---
.../conversation/NCConversationHolder.scala | 4 +-
.../conversation/NCConversationManager.scala | 15 ++--
.../conversation/NCConversationManagerSpec.scala | 95 ++++++++++++++++++++++
.../dialogflow/NCDialogFlowManagerSpec.scala | 4 +-
4 files changed, 106 insertions(+), 12 deletions(-)
diff --git
a/nlpcraft/src/main/scala/org/apache/nlpcraft/internal/conversation/NCConversationHolder.scala
b/nlpcraft/src/main/scala/org/apache/nlpcraft/internal/conversation/NCConversationHolder.scala
index de4a0e5..7d62dd5 100644
---
a/nlpcraft/src/main/scala/org/apache/nlpcraft/internal/conversation/NCConversationHolder.scala
+++
b/nlpcraft/src/main/scala/org/apache/nlpcraft/internal/conversation/NCConversationHolder.scala
@@ -33,7 +33,7 @@ import scala.jdk.CollectionConverters.*
* An active conversation is an ordered set of utterances for the specific
user and data model.
*/
case class NCConversationHolder(
- usrId: Long,
+ usrId: String,
mdlId: String,
timeoutMs: Long,
maxDepth: Int
@@ -206,7 +206,7 @@ case class NCConversationHolder(
*
* @return
*/
- def getEntity: util.List[NCEntity] =
+ def getEntities: util.List[NCEntity] =
stm.synchronized {
val reqIds = ctx.map(_.getRequestId).distinct.zipWithIndex.toMap
val ents = ctx.groupBy(_.getRequestId).toSeq.sortBy(p =>
reqIds(p._1)).reverse.flatMap(_._2)
diff --git
a/nlpcraft/src/main/scala/org/apache/nlpcraft/internal/conversation/NCConversationManager.scala
b/nlpcraft/src/main/scala/org/apache/nlpcraft/internal/conversation/NCConversationManager.scala
index 0a58184..4377e32 100644
---
a/nlpcraft/src/main/scala/org/apache/nlpcraft/internal/conversation/NCConversationManager.scala
+++
b/nlpcraft/src/main/scala/org/apache/nlpcraft/internal/conversation/NCConversationManager.scala
@@ -27,9 +27,8 @@ import scala.collection.*
* Conversation manager.
*/
class NCConversationManager(mdlCfg: NCModelConfig) extends LazyLogging:
- case class Key(usrId: Long, mdlId: String)
case class Value(conv: NCConversationHolder, var tstamp: Long = 0)
- private final val convs: mutable.Map[Key, Value] =
mutable.HashMap.empty[Key, Value]
+ private final val convs: mutable.Map[String, Value] =
mutable.HashMap.empty[String, Value]
@volatile private var gc: Thread = _
/**
@@ -67,30 +66,30 @@ class NCConversationManager(mdlCfg: NCModelConfig) extends
LazyLogging:
require(Thread.holdsLock(convs))
val now = NCUtils.now()
- val delKeys = mutable.HashSet.empty[Key]
+ val delKeys = mutable.HashSet.empty[String]
for ((key, value) <- convs)
if value.tstamp < now - mdlCfg.getConversationTimeout then
value.conv.getUserData.clear()
delKeys += key
+
convs --= delKeys
if convs.nonEmpty then convs.values.map(v => v.tstamp +
v.conv.timeoutMs).min
else Long.MaxValue
/**
- * Gets conversation for given key.
+ * Gets conversation for given user ID.
*
* @param usrId User ID.
- * @param mdlId Model ID.
* @return New or existing conversation.
*/
- def getConversation(usrId: Long, mdlId: String): NCConversationHolder =
+ def getConversation(usrId: String): NCConversationHolder =
convs.synchronized {
val v = convs.getOrElseUpdate(
- Key(usrId, mdlId),
- Value(NCConversationHolder(usrId, mdlId,
mdlCfg.getConversationTimeout, mdlCfg.getConversationDepth))
+ usrId,
+ Value(NCConversationHolder(usrId, mdlCfg.getId,
mdlCfg.getConversationTimeout, mdlCfg.getConversationDepth))
)
v.tstamp = NCUtils.nowUtcMs()
diff --git
a/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/conversation/NCConversationManagerSpec.scala
b/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/conversation/NCConversationManagerSpec.scala
new file mode 100644
index 0000000..61af791
--- /dev/null
+++
b/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/conversation/NCConversationManagerSpec.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
+ *
+ * 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.conversation
+
+import org.apache.nlpcraft.*
+import org.apache.nlpcraft.nlp.util.*
+import org.junit.jupiter.api.Test
+
+import java.util.function.Predicate
+
+/**
+ *
+ */
+class NCConversationManagerSpec:
+ case class ModelConfigMock(timeout: Long = Long.MaxValue) extends
NCModelConfig("testId", "test", "1.0", "Test description", "Test origin"):
+ override def getConversationTimeout: Long = timeout
+
+ @Test
+ def test(): Unit =
+ val mgr = NCConversationManager(ModelConfigMock())
+ val t = NCTestToken()
+ val reqId = "req1"
+
+ val conv = mgr.getConversation("user1")
+
+ def checkSize(size: Int): Unit =
+ require(conv.getEntities.size() == size, s"Unexpected entities
size: ${conv.getEntities.size()}, expected: $size")
+
+ // Initial empty.
+ checkSize(0)
+
+ // Added. Still empty.
+ conv.addEntities(reqId, Seq(NCTestEntity("e1", reqId, tokens = t),
NCTestEntity("e2", reqId, tokens = t)))
+ checkSize(0)
+
+ // Updated. Not empty.
+ conv.updateEntities()
+ checkSize(2)
+
+ // Partially cleared.
+ conv.clearEntities(new Predicate[NCEntity]:
+ override def test(t: NCEntity): Boolean = t.getId == "e1"
+ )
+ checkSize(1)
+ require(conv.getEntities.get(0).getId == "e2")
+
+ @Test
+ def testTimeout(): Unit =
+ val timeout = 1000
+
+ val mgr = NCConversationManager(ModelConfigMock(timeout))
+ val t = NCTestToken()
+ val reqId = "req1"
+
+ // TODO: Drop method and use saved conversation instead - error is
thrown
+ def getConversation: NCConversationHolder =
mgr.getConversation("user1")
+
+ def checkSize(size: Int): Unit =
+ val conv = getConversation
+ require(conv.getEntities.size() == size, s"Unexpected entities
size: ${conv.getEntities.size()}, expected: $size")
+
+ // Initial empty.
+ checkSize(0)
+
+ // Added. Still empty.
+ getConversation.addEntities(reqId, Seq(NCTestEntity("e1", reqId,
tokens = t), NCTestEntity("e2", reqId, tokens = t)))
+ checkSize(0)
+
+ // Updated. Not empty.
+ getConversation.updateEntities()
+ checkSize(2)
+
+ // Cleared by timeout.
+ try
+ mgr.start()
+ Thread.sleep(timeout * 2)
+ checkSize(0)
+ finally
+ mgr.stop()
+
diff --git
a/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/dialogflow/NCDialogFlowManagerSpec.scala
b/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/dialogflow/NCDialogFlowManagerSpec.scala
index be1b559..5e26746 100644
---
a/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/dialogflow/NCDialogFlowManagerSpec.scala
+++
b/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/dialogflow/NCDialogFlowManagerSpec.scala
@@ -19,8 +19,8 @@ package org.apache.nlpcraft.internal.dialogflow
import org.apache.nlpcraft.internal.util.NCUtils
import org.apache.nlpcraft.nlp.util.NCTestRequest
-import org.apache.nlpcraft.{NCContext, NCConversation, NCEntity,
NCIntentMatch, NCModelConfig, NCRequest, NCResult, NCVariant}
-import org.junit.jupiter.api.{AfterEach, Test}
+import org.apache.nlpcraft.*
+import org.junit.jupiter.api.*
import java.util