This is an automated email from the ASF dual-hosted git repository.
sergeykamov pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-nlpcraft.git
The following commit(s) were added to refs/heads/master by this push:
new 1556a3a Api extended. Bugfixes.
1556a3a is described below
commit 1556a3ad6919419f57e67f6609f2cc1446e9bc1a
Author: Sergey Kamov <[email protected]>
AuthorDate: Sun Apr 3 11:38:38 2022 +0300
Api extended. Bugfixes.
---
.../scala/org/apache/nlpcraft/NCCallbackData.java | 44 +++++
.../scala/org/apache/nlpcraft/NCModelClient.java | 25 +++
.../internal/conversation/NCConversationData.scala | 25 ++-
.../conversation/NCConversationManager.scala | 2 +-
.../internal/dialogflow/NCDialogFlowManager.scala | 38 ++++-
.../nlpcraft/internal/impl/NCModelClientImpl.scala | 34 +++-
.../intent/matcher/NCIntentSolverManager.scala | 186 ++++++++++++++++-----
.../conversation/NCConversationManagerSpec.scala | 8 +-
.../internal/conversation/NCConversationSpec.scala | 77 +++++++++
.../conversation/NCConversationTimeoutSpec.scala | 76 +++++++++
.../internal/impl/NCModelCallbacksSpec.scala | 2 +-
.../nlpcraft/internal/impl/NCModelClientSpec.scala | 25 ++-
.../internal/impl/NCModelClientSpec2.scala | 102 +++++++++++
.../internal/impl/NCModelClientSpec3.scala | 69 ++++++++
.../internal/impl/NCModelPingPongSpec.scala | 2 +-
.../internal/impl/NCPipelineManagerSpec.scala | 2 +-
.../semantic/NCSemanticEntityParserJsonSpec.scala | 2 +-
.../semantic/NCSemanticEntityParserSpec.scala | 2 +-
.../semantic/NCSemanticEntityParserYamlSpec.scala | 2 +-
.../org/apache/nlpcraft/nlp/util/NCTestUtils.scala | 12 +-
20 files changed, 663 insertions(+), 72 deletions(-)
diff --git a/nlpcraft/src/main/scala/org/apache/nlpcraft/NCCallbackData.java
b/nlpcraft/src/main/scala/org/apache/nlpcraft/NCCallbackData.java
new file mode 100644
index 0000000..d6a3db2
--- /dev/null
+++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/NCCallbackData.java
@@ -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
+ *
+ * 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.function.Function;
+
+/**
+ *
+ */
+public interface NCCallbackData {
+ /**
+ *
+ * @return
+ */
+ String getIntentId();
+
+ /**
+ *
+ * @return
+ */
+ List<List<NCEntity>> getCallbackArguments();
+
+ /**
+ *
+ * @return
+ */
+ Function<List<List<NCEntity>>, NCResult> getCallback();
+}
diff --git a/nlpcraft/src/main/scala/org/apache/nlpcraft/NCModelClient.java
b/nlpcraft/src/main/scala/org/apache/nlpcraft/NCModelClient.java
index 54f023b..f625233 100644
--- a/nlpcraft/src/main/scala/org/apache/nlpcraft/NCModelClient.java
+++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/NCModelClient.java
@@ -98,4 +98,29 @@ public class NCModelClient implements AutoCloseable {
public void validateSamples() {
impl.validateSamples();
}
+
+ /**
+ * TODO:
+ * Gets callback information which contains intent ID and 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.
+ *
+ * Callback.
+ * - You can call callback only one time.
+ * - You can't call callback if it is not last request.
+ * - if you call callback and 'saveHistory' flag was true - dialog
overriden by callback result instead of saved before empty result.
+ * - if you call callback and 'saveHistory' flag was false - history
data is still ignored.
+ * - No matter of callback execution time - history data based on
request timestamp.
+ *
+ * @param txt
+ * @param data
+ * @param usrId
+ * @param saveHistory if true that found intent data added to dialog flow
(with empty NCResult, bacause callback wasn't called) and STM.
+ * if false that found intent is not saved in STM and
dialog flow.
+ * @return
+ */
+ public NCCallbackData debugAsk(String txt, Map<String, Object> data,
String usrId, boolean saveHistory) {
+ return impl.debugAsk(txt, data, usrId, saveHistory);
+ }
}
diff --git
a/nlpcraft/src/main/scala/org/apache/nlpcraft/internal/conversation/NCConversationData.scala
b/nlpcraft/src/main/scala/org/apache/nlpcraft/internal/conversation/NCConversationData.scala
index 472dc6a..3092964 100644
---
a/nlpcraft/src/main/scala/org/apache/nlpcraft/internal/conversation/NCConversationData.scala
+++
b/nlpcraft/src/main/scala/org/apache/nlpcraft/internal/conversation/NCConversationData.scala
@@ -36,7 +36,7 @@ case class NCConversationData(
mdlId: String,
timeoutMs: Long,
maxDepth: Int
-) extends LazyLogging {
+) extends LazyLogging:
private final val data = new NCPropertyMapAdapter()
case class EntityHolder(entity: NCEntity, var entityTypeUsageTime: Long =
0)
@@ -47,6 +47,12 @@ case class NCConversationData(
private val lastEnts = mutable.ArrayBuffer.empty[Iterable[NCEntity]]
private val ctx = mutable.ArrayBuffer.empty[NCEntity]
+ /**
+ *
+ */
+ val getUserData: NCPropertyMap = data
+
+
@volatile private var lastUpdateTstamp = NCUtils.nowUtcMs()
@volatile private var depth = 0
@@ -195,14 +201,17 @@ case class NCConversationData(
*
* @return
*/
- def getEntities: Seq[NCEntity] =
- stm.synchronized {
- val reqIds = ctx.map(_.getRequestId).distinct.zipWithIndex.toMap
- ctx.groupBy(_.getRequestId).toSeq.sortBy(p =>
reqIds(p._1)).reverse.flatMap(_._2)
- }
+ def getEntities: Seq[NCEntity] = stm.synchronized {
+ val reqIds = ctx.map(_.getRequestId).distinct.zipWithIndex.toMap
+ ctx.groupBy(_.getRequestId).toSeq.sortBy(p =>
reqIds(p._1)).reverse.flatMap(_._2)
+ }
/**
*
*/
- val getUserData: NCPropertyMap = data
-}
\ No newline at end of file
+ def clear(): Unit = stm.synchronized {
+ ctx.clear()
+ stm.clear()
+ lastEnts.clear()
+ data.clear()
+ }
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 bc7c557..ce9d66c 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
@@ -61,7 +61,7 @@ class NCConversationManager(cfg: NCModelConfig) extends
LazyLogging:
for ((key, value) <- convs)
if value.tstamp < now - cfg.getConversationTimeout then
- value.conv.getUserData.clear()
+ value.conv.clear()
delKeys += key
convs --= delKeys
diff --git
a/nlpcraft/src/main/scala/org/apache/nlpcraft/internal/dialogflow/NCDialogFlowManager.scala
b/nlpcraft/src/main/scala/org/apache/nlpcraft/internal/dialogflow/NCDialogFlowManager.scala
index 4c76db8..20b3f9e 100644
---
a/nlpcraft/src/main/scala/org/apache/nlpcraft/internal/dialogflow/NCDialogFlowManager.scala
+++
b/nlpcraft/src/main/scala/org/apache/nlpcraft/internal/dialogflow/NCDialogFlowManager.scala
@@ -63,6 +63,19 @@ class NCDialogFlowManager(cfg: NCModelConfig) extends
LazyLogging:
/**
*
+ * @param intentMatch
+ * @param res
+ * @param ctx
+ * @return
+ */
+ private def mkItem(intentMatch: NCIntentMatch, res: NCResult, ctx:
NCContext): NCDialogFlowItem =
+ new NCDialogFlowItem:
+ override val getIntentMatch: NCIntentMatch = intentMatch
+ override val getRequest: NCRequest = ctx.getRequest
+ override val getResult: NCResult = res
+
+ /**
+ *
* @return
*/
def start(): Unit =
@@ -98,10 +111,7 @@ class NCDialogFlowManager(cfg: NCModelConfig) extends
LazyLogging:
* @param ctx Original query context.
*/
def addMatchedIntent(intentMatch: NCIntentMatch, res: NCResult, ctx:
NCContext): Unit =
- val item: NCDialogFlowItem = new NCDialogFlowItem:
- override val getIntentMatch: NCIntentMatch = intentMatch
- override val getRequest: NCRequest = ctx.getRequest
- override val getResult: NCResult = res
+ val item = mkItem(intentMatch, res, ctx)
flow.synchronized {
flow.getOrElseUpdate(ctx.getRequest.getUserId,
mutable.ArrayBuffer.empty[NCDialogFlowItem]).append(item)
@@ -109,6 +119,26 @@ class NCDialogFlowManager(cfg: NCModelConfig) extends
LazyLogging:
}
/**
+ *
+ * @param intentMatch
+ * @param res
+ * @param ctx
+ */
+ def replaceLastItem(intentMatch: NCIntentMatch, res: NCResult, ctx:
NCContext): Unit =
+ val item = mkItem(intentMatch, res, ctx)
+
+ flow.synchronized {
+ val buf = flow.getOrElseUpdate(ctx.getRequest.getUserId,
mutable.ArrayBuffer.empty[NCDialogFlowItem])
+
+ // If buf is empty - it cleared by timer, so there is nothing to
replace.
+ if buf.nonEmpty then
+ buf.remove(buf.size - 1)
+ buf.append(item)
+
+ flow.notifyAll()
+ }
+
+ /**
* Gets sequence of dialog flow items sorted from oldest to newest (i.e.
dialog flow) for given user ID.
*
* @param usrId User ID.
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 30913c0..4615c0c 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
@@ -48,7 +48,7 @@ class NCModelClientImpl(mdl: NCModel) extends LazyLogging:
private val convMgr = NCConversationManager(mdl.getConfig)
private val dlgMgr = NCDialogFlowManager(mdl.getConfig)
private val plMgr = NCModelPipelineManager(mdl.getConfig, mdl.getPipeline)
- private val intentsMgr = NCIntentSolverManager(dlgMgr, intents.map(p =>
p.intent -> p.function).toMap)
+ private val intentsMgr = NCIntentSolverManager(dlgMgr, convMgr,
intents.map(p => p.intent -> p.function).toMap)
init()
@@ -75,19 +75,23 @@ class NCModelClientImpl(mdl: NCModel) extends LazyLogging:
dlgMgr.start()
plMgr.start()
- /*
+ /**
+ *
* @param txt
* @param data
* @param usrId
+ * @param typ
* @return
*/
- def ask(txt: String, data: JMap[String, AnyRef], usrId: String): NCResult =
+ private def ask0(txt: String, data: JMap[String, AnyRef], usrId: String,
typ: NCIntentSolveType): Either[NCResult, NCCallbackData] =
val plData = plMgr.prepare(txt, data, usrId)
val userId = plData.request.getUserId
val convHldr = convMgr.getConversation(userId)
val allEnts = plData.variants.flatMap(_.getEntities.asScala)
+ convHldr.updateEntities()
+
val conv: NCConversation =
new NCConversation:
override val getData: NCPropertyMap = convHldr.getUserData
@@ -105,7 +109,16 @@ 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)
+ intentsMgr.solve(mdl, ctx, typ)
+
+ /*
+ * @param txt
+ * @param data
+ * @param usrId
+ * @return
+ */
+ def ask(txt: String, data: JMap[String, AnyRef], usrId: String): NCResult =
+ ask0(txt, data, usrId, NCIntentSolveType.REGULAR).swap.toOption.get
/**
*
@@ -187,3 +200,16 @@ class NCModelClientImpl(mdl: NCModel) extends LazyLogging:
plMgr.close()
dlgMgr.close()
convMgr.close()
+ intentsMgr.close()
+
+ /**
+ *
+ * @param txt
+ * @param data
+ * @param usrId
+ * @param saveHist
+ * @return
+ */
+ def debugAsk(txt: String, data: JMap[String, AnyRef], usrId: String,
saveHist: Boolean): NCCallbackData =
+ import NCIntentSolveType.*
+ ask0(txt, data, usrId, if saveHist then SEARCH else
SEARCH_NO_HISTORY).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 afe36a9..c2c3f06 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
@@ -20,6 +20,7 @@ package org.apache.nlpcraft.internal.intent.matcher
import com.typesafe.scalalogging.LazyLogging
import org.apache.nlpcraft.*
import org.apache.nlpcraft.internal.ascii.NCAsciiTable
+import org.apache.nlpcraft.internal.conversation.NCConversationManager
import org.apache.nlpcraft.internal.dialogflow.NCDialogFlowManager
import org.apache.nlpcraft.internal.intent.*
@@ -31,6 +32,15 @@ import scala.collection.mutable.ArrayBuffer
import scala.jdk.CollectionConverters.*
import scala.language.postfixOps
+/**
+ *
+ */
+enum NCIntentSolveType:
+ case REGULAR, SEARCH, SEARCH_NO_HISTORY
+
+/**
+ *
+ */
object NCIntentSolverManager:
/**
* Sentence variant & its weight.
@@ -72,6 +82,18 @@ object NCIntentSolverManager:
/**
*
+ * @param getIntentId
+ * @param getCallbackArguments
+ * @param getCallback
+ */
+ private case class CallbackDataImpl(
+ getIntentId: String,
+ getCallbackArguments: JList[JList[NCEntity]],
+ getCallback: Function[JList[JList[NCEntity]], NCResult]
+ ) extends NCCallbackData
+
+ /**
+ *
* @param intentId
* @param fn
* @param groups
@@ -162,12 +184,14 @@ object NCIntentSolverManager:
*/
private case class IntentEntity(var used: Boolean, var conv: Boolean,
entity: NCEntity)
+ type ResultData = Either[NCResult, NCCallbackData]
+
/**
*
* @param result
* @param intentMatch
*/
- private case class IterationResult(result: NCResult, intentMatch:
NCIntentMatch)
+ private case class IterationResult(result: ResultData, intentMatch:
NCIntentMatch)
/**
* @param termId
@@ -221,12 +245,25 @@ object NCIntentSolverManager:
variantIdx: Int // Variant index.
)
+ /**
+ *
+ * @param userId
+ * @param mldId
+ */
+ private case class UserModelKey(userId: String, mldId: String)
+
import org.apache.nlpcraft.internal.intent.matcher.NCIntentSolverManager.*
/**
* Intent solver that finds the best matching intent given user sentence.
*/
-class NCIntentSolverManager(dialog: NCDialogFlowManager, intents:
Map[NCIDLIntent, NCIntentMatch => NCResult]) extends LazyLogging:
+class NCIntentSolverManager(
+ dialog: NCDialogFlowManager,
+ conv: NCConversationManager,
+ intents: Map[NCIDLIntent, NCIntentMatch => NCResult]
+) extends LazyLogging:
+ private final val reqIds = mutable.HashMap.empty[UserModelKey, String]
+
/**
* Main entry point for intent engine.
*
@@ -244,7 +281,7 @@ class NCIntentSolverManager(dialog: NCDialogFlowManager,
intents: Map[NCIDLInten
for (
(vrn, vrnIdx) <- ctx.getVariants.asScala.zipWithIndex if
mdl.onVariant(vrn);
ents = vrn.getEntities.asScala;
- varEntsGroups = ents.map(t => if t.getGroups != null then
t.getGroups.asScala else Set.empty[String]);
+ varEntsGroups = ents.filter(t => t.getGroups != null &&
!t.getGroups.isEmpty).map(_.getGroups.asScala);
(intent, callback) <- intents
)
val convEnts: Seq[IntentEntity] =
@@ -388,7 +425,6 @@ class NCIntentSolverManager(dialog: NCDialogFlowManager,
intents: Map[NCIDLInten
val opts = intent.options
val flow = dialog.getDialogFlow(ctx.getRequest.getUserId)
val varStr = s"(variant #${varIdx + 1})"
- val flowRegex = intent.flowRegex
// Check dialog flow regex first, if any.
val flowMatched: Boolean =
@@ -450,7 +486,6 @@ class NCIntentSolverManager(dialog: NCDialogFlowManager,
intents: Map[NCIDLInten
None
else
val usedSenEnts = senEnts.filter(_.used)
- val unusedSenEnts = senEnts.filter(!_.used)
val usedConvEnts = convEnts.filter(_.used)
val usedToks = usedSenEnts.flatMap(_.entity.getTokens.asScala)
val unusedToks = ctx.getTokens.asScala.filter(p =>
!usedToks.contains(p))
@@ -553,7 +588,7 @@ class NCIntentSolverManager(dialog: NCDialogFlowManager,
intents: Map[NCIDLInten
)
// Term not found at all.
case None => None
- catch case e: Exception => throw new NCException(s"Runtime error
processing IDL term: $term", e)
+ catch case e: Exception => E(s"Runtime error processing IDL term:
$term", e)
/**
* Solves term's predicate.
@@ -612,7 +647,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,9 +657,11 @@ class NCIntentSolverManager(dialog: NCDialogFlowManager,
intents: Map[NCIDLInten
*
* @param mdl
* @param ctx
+ * @param typ
+ * @param key
* @return
*/
- private def solveIteration(mdl: NCModel, ctx: NCContext):
Option[IterationResult] =
+ private def solveIteration(mdl: NCModel, ctx: NCContext, typ:
NCIntentSolveType, key: UserModelKey): Option[IterationResult] =
require(intents.nonEmpty)
val req = ctx.getRequest
@@ -636,17 +673,17 @@ 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 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."))
+ def hasNext: Boolean = data == null
+ def finish(data: IterationResult): Unit = Loop.data = Option(data)
+ def finish(): Unit = Loop.data = None
+ 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 =
+ def mkIntentMatch(arg: JList[JList[NCEntity]]): NCIntentMatch =
new NCIntentMatch:
override val getContext: NCContext = ctx
override val getIntentId: String = intentRes.intentId
@@ -659,16 +696,59 @@ class NCIntentSolverManager(dialog: NCDialogFlowManager,
intents: Map[NCIDLInten
override val getVariant: NCVariant =
new NCVariant:
override def getEntities: JList[NCEntity] =
intentRes.variant.entities.asJava
+
+ val im =
mkIntentMatch(intentRes.groups.map(_.entities).map(_.asJava).asJava)
try
- if mdl.onMatchedIntent(intentMatch) then
+ if mdl.onMatchedIntent(im) then
// This can throw NCIntentSkip exception.
- val cbRes = 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)))
+ import NCIntentSolveType.*
+
+ def saveHistory(res: NCResult, im: NCIntentMatch): Unit =
+ dialog.addMatchedIntent(im, res, ctx)
+ conv.getConversation(req.getUserId).addEntities(
+ req.getRequestId,
im.getIntentEntities.asScala.flatMap(_.asScala).toSeq.distinct
+ )
+ logger.info(s"Intent '${intentRes.intentId}' for
variant #${intentRes.variantIdx + 1} selected as the <|best match|>")
+
+ def executeCallback(im: NCIntentMatch): NCResult =
+ val cbRes = intentRes.fn(im)
+ // Store winning intent match in the input.
+ if cbRes.getIntentId == null then
cbRes.setIntentId(intentRes.intentId)
+ cbRes
+
+ def finishSearch(): Unit =
+ val cb = new Function[JList[JList[NCEntity]],
NCResult]:
+ @volatile private var called = false
+ override def apply(args: JList[JList[NCEntity]]):
NCResult =
+ if called then E("Callback was already
called.")
+ called = true
+
+ val reqId = reqIds.synchronized {
reqIds.getOrElse(key, null) }
+
+ // TODO: text.
+ if reqId != ctx.getRequest.getRequestId then
E("Callback is out of date.")
+
+ typ match
+ case SEARCH =>
+ val imNew = mkIntentMatch(args)
+ val cbRes = executeCallback(imNew)
+ dialog.replaceLastItem(imNew, cbRes,
ctx)
+ cbRes
+ case SEARCH_NO_HISTORY =>
executeCallback(mkIntentMatch(args))
+ case _ => throw new
AssertionError(s"Unexpected state: $typ")
+
+
Loop.finish(IterationResult(Right(CallbackDataImpl(im.getIntentId,
im.getIntentEntities, cb)), im))
+
+ typ match
+ case REGULAR =>
+ val cbRes = executeCallback(im)
+ saveHistory(cbRes, im)
+ Loop.finish(IterationResult(Left(cbRes), im))
+ case SEARCH =>
+ saveHistory(new NCResult(), im) // Added dummy
result.
+ finishSearch()
+ case SEARCH_NO_HISTORY =>
+ finishSearch()
else
logger.info(s"Model '${ctx.getModelConfig.getId}'
triggered rematching of intents by intent '${intentRes.intentId}' on variant
#${intentRes.variantIdx + 1}.")
Loop.finish()
@@ -679,42 +759,66 @@ class NCIntentSolverManager(dialog: NCDialogFlowManager,
intents: Map[NCIDLInten
case s if s != null => logger.info(s"Selected
intent '${intentRes.intentId}' skipped: $s")
case _ => logger.info(s"Selected intent
'${intentRes.intentId}' skipped.")
- Loop.result
+ Loop.result()
/**
*
* @param mdl
* @param ctx
+ * @param typ
* @return
*/
- def solve(mdl: NCModel, ctx: NCContext): NCResult =
- var res: NCResult = mdl.onContext(ctx)
+ def solve(mdl: NCModel, ctx: NCContext, typ: NCIntentSolveType):
ResultData =
+ import NCIntentSolveType.REGULAR
+
+ val key = UserModelKey(ctx.getRequest.getUserId, mdl.getConfig.getId)
+ reqIds.synchronized { reqIds.put(key, ctx.getRequest.getRequestId)}
+
+ val mdlCtxRes = mdl.onContext(ctx)
- if res != null then
- // TODO: text.
- if intents.nonEmpty then logger.warn("`onContext` method overrides
existed intents. They are ignored.")
+ if mdlCtxRes != null then
+ if typ != REGULAR then E("'onContext()' method is overridden,
intents cannot be found.")
+ if intents.nonEmpty then logger.warn("'onContext()' method
overrides existing intents - they are ignored.")
- res
+ Left(mdlCtxRes)
else
if intents.isEmpty then
- // TODO: text.
- throw NCRejection("Intent solver has no registered intents and
model's `onContext` method returns null result.")
+ throw NCRejection("There are no registered intents and model's
'onContext()' method returns 'null' result.")
var loopRes: IterationResult = null
try
while (loopRes == null)
- solveIteration(mdl, ctx) match
+ solveIteration(mdl, ctx, typ, key) 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
+ typ match
+ case REGULAR =>
+ mdl.onResult(loopRes.intentMatch,
loopRes.result.swap.toOption.get) match
+ case null => loopRes.result
+ case mdlRes => Left(mdlRes)
+ case _ => loopRes.result
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
+ typ match
+ case REGULAR =>
+ mdl.onRejection(if loopRes != null then
loopRes.intentMatch else null, e) match
+ case null => throw e
+ case mdlRejRes => Left(mdlRejRes)
+ case _ => throw e
+
case e: Throwable =>
- val res = mdl.onError(ctx, e)
- if res != null then res else throw e
\ No newline at end of file
+ typ match
+ case REGULAR =>
+ mdl.onError(ctx, e) match
+ case null => throw e
+ case mdlErrRes =>
+ logger.warn("Error during execution.", e)
+ Left(mdlErrRes)
+ case _ => throw e
+
+ /**
+ *
+ */
+ def close(): Unit = reqIds.clear()
\ No newline at end of file
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
index 113b538..1b6d6dc 100644
---
a/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/conversation/NCConversationManagerSpec.scala
+++
b/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/conversation/NCConversationManagerSpec.scala
@@ -65,22 +65,20 @@ class NCConversationManagerSpec:
val t = NCTestToken()
val reqId = "req1"
- // TODO: important (error in code) - drop method and use saved
conversation instead - error is thrown.
- def getConversation: NCConversationData = mgr.getConversation("user1")
+ val conv = mgr.getConversation("user1")
def checkSize(size: Int): Unit =
- val conv = getConversation
require(conv.getEntities.sizeIs == 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)))
+ conv.addEntities(reqId, Seq(NCTestEntity("e1", reqId, tokens = t),
NCTestEntity("e2", reqId, tokens = t)))
checkSize(0)
// Updated. Not empty.
- getConversation.updateEntities()
+ conv.updateEntities()
checkSize(2)
// Cleared by timeout.
diff --git
a/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/conversation/NCConversationSpec.scala
b/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/conversation/NCConversationSpec.scala
new file mode 100644
index 0000000..112c01a
--- /dev/null
+++
b/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/conversation/NCConversationSpec.scala
@@ -0,0 +1,77 @@
+/*
+ * 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.entity.parser.semantic.NCSemanticTestElement
+import org.junit.jupiter.api.Test
+import org.apache.nlpcraft.nlp.util.*
+import org.junit.jupiter.api.Assertions.{assertFalse, assertTrue}
+
+import scala.jdk.CollectionConverters.*
+import scala.util.Using
+
+/**
+ *
+ */
+class NCConversationSpec:
+ private val usrId = "userId"
+
+ /**
+ *
+ */
+ @Test
+ def test(): Unit =
+ val mdl: NCModel =
+ new NCTestModelAdapter:
+ import NCSemanticTestElement as TE
+ override val getPipeline: NCPipeline =
+ val pl = mkEnPipeline
+
pl.getEntityParsers.add(NCTestUtils.mkEnSemanticParser(TE("e1"), TE("e2")))
+ pl
+
+ @NCIntent("intent=i1 term(t1)~{# == 'e1'} term(t2)~{# ==
'e2'}?")
+ def onMatch(@NCIntentTerm("t1") t1: NCEntity,
@NCIntentTerm("t2") t2: Option[NCEntity]): NCResult = new NCResult()
+
+ Using.resource(new NCModelClient(mdl)) { cli =>
+ def execOk(txt: String): Unit = cli.ask(txt, null, usrId)
+ def execReject(txt: String): Unit =
+ try
+ cli.ask(txt, null, usrId)
+ require(false)
+ catch
+ case e: NCRejection => // OK.
+ case e: Throwable => throw e
+
+ // missed 'e1'
+ execReject("e2")
+ execOk("e1 e2")
+
+ // 'e1' received from conversation.
+ execOk("e2")
+
+ cli.clearStm(usrId)
+ cli.clearDialog(usrId)
+
+ // missed 'e1' again.
+ execReject("e2")
+ execOk("e1 e2")
+
+ // 'e1' received from conversation.
+ execOk("e2")
+ }
diff --git
a/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/conversation/NCConversationTimeoutSpec.scala
b/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/conversation/NCConversationTimeoutSpec.scala
new file mode 100644
index 0000000..99a4499
--- /dev/null
+++
b/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/conversation/NCConversationTimeoutSpec.scala
@@ -0,0 +1,76 @@
+/*
+ * 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.entity.parser.semantic.NCSemanticTestElement
+import org.apache.nlpcraft.nlp.util.*
+import org.junit.jupiter.api.Assertions.*
+import org.junit.jupiter.api.Test
+
+import scala.jdk.CollectionConverters.*
+import scala.util.Using
+
+/**
+ *
+ */
+class NCConversationTimeoutSpec:
+ private val TIMEOUT = 200
+ private val VALUE = "value"
+ private val EMPTY = "empty"
+
+ /**
+ *
+ */
+ @Test
+ def test(): Unit =
+ val mdl: NCModel =
+ new NCTestModelAdapter:
+ override val getConfig: NCModelConfig =
+ val cfg = new NCModelConfig("testId", "test", "1.0", "Test
description", "Test origin")
+ cfg.setConversationTimeout(TIMEOUT)
+ cfg
+
+ override val getPipeline: NCPipeline =
+ val pl = mkEnPipeline
+ import NCSemanticTestElement as TE
+
pl.getEntityParsers.add(NCTestUtils.mkEnSemanticParser(TE("test")))
+ pl
+
+ @NCIntent("intent=i term(e)~{# == 'test'}")
+ def onMatch(im: NCIntentMatch, @NCIntentTerm("e") e:
NCEntity): NCResult =
+ val conv = im.getContext.getConversation
+ val res = new
NCResult(conv.getData.getOpt("key").orElse(EMPTY), NCResultType.ASK_RESULT)
+
+ // For next calls.
+ conv.getData.put("key", VALUE)
+
+ res
+
+ Using.resource(new NCModelClient(mdl)) { cli =>
+ def check(hasValue: Boolean): Unit =
+ require(cli.ask("test", null, "userId").getBody.toString ==
(if hasValue then VALUE else EMPTY))
+
+ check(false)
+ check(true)
+
+ Thread.sleep(TIMEOUT * 2)
+
+ check(false)
+ check(true)
+ }
diff --git
a/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/impl/NCModelCallbacksSpec.scala
b/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/impl/NCModelCallbacksSpec.scala
index 6da1b1d..6fea886 100644
---
a/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/impl/NCModelCallbacksSpec.scala
+++
b/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/impl/NCModelCallbacksSpec.scala
@@ -62,7 +62,7 @@ class NCModelCallbacksSpec:
override def onRejection(ctx: NCIntentMatch, e: NCRejection):
NCResult = getOrElse(RejectionNotNull, RESULT_REJECTION, null)
override def onError(ctx: NCContext, e: Throwable): NCResult =
getOrElse(ErrorNotNull, RESULT_ERROR, null)
-
MDL.getPipeline.getEntityParsers.add(NCTestUtils.mkENSemanticParser(Seq(NCSemanticTestElement("x")).asJava))
+
MDL.getPipeline.getEntityParsers.add(NCTestUtils.mkEnSemanticParser(Seq(NCSemanticTestElement("x")).asJava))
/**
*
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 5123c84..5140d7b 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
@@ -26,9 +26,24 @@ import org.junit.jupiter.api.Test
import scala.jdk.CollectionConverters.*
import scala.util.Using
+/**
+ *
+ */
class NCModelClientSpec:
+ /**
+ *
+ * @param e
+ * @return
+ */
+ private def s(e: NCEntity): String =
+ s"Entity [id=${e.getId}, text=${e.mkText()},
properties={${e.keysSet().asScala.map(k => s"$k=${e.get(k)}")}}]"
+
+ /**
+ *
+ * @param mdl
+ */
private def test0(mdl: NCTestModelAdapter): Unit =
-
mdl.getPipeline.getEntityParsers.add(NCTestUtils.mkENSemanticParser("models/lightswitch_model.yaml"))
+
mdl.getPipeline.getEntityParsers.add(NCTestUtils.mkEnSemanticParser("models/lightswitch_model.yaml"))
Using.resource(new NCModelClient(mdl)) { client =>
val res = client.ask("Lights on at second floor kitchen", null,
"userId")
@@ -37,7 +52,12 @@ class NCModelClientSpec:
println(s"Body: ${res.getBody}")
client.validateSamples()
+
+ val winner = client.debugAsk("Lights on at second floor kitchen",
null, "userId", true)
+ println(s"Winner intent: ${winner.getIntentId}")
+ println("Entities: \n" + winner.getCallbackArguments.asScala.map(p
=> p.asScala.map(s).mkString(", ")).mkString("\n"))
}
+
/**
*
*/
@@ -50,6 +70,9 @@ class NCModelClientSpec:
def onMatch(@NCIntentTerm("act") act: NCEntity,
@NCIntentTerm("loc") locs: List[NCEntity]): NCResult = new NCResult()
)
+ /**
+ *
+ */
@Test
def test2(): Unit =
test0(
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..01194a2
--- /dev/null
+++
b/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/impl/NCModelClientSpec2.scala
@@ -0,0 +1,102 @@
+/*
+ * 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 NCTestModelAdapter:
+ override val getPipeline: NCPipeline =
+ val pl = mkEnPipeline
+
pl.getEntityParsers.add(NCTestUtils.mkEnSemanticParser(TE("e1"), TE("e2")))
+ pl.getTokenEnrichers.add(EN_TOK_LEMMA_POS_ENRICHER)
+ pl
+
+ @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.debugAsk(txt, null, "userId", true)
+ private val allArgs: JList[JList[NCEntity]] =
wi.getCallbackArguments
+
+ 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.debugAsk("x", null, "userId", false)
+
+ require(false)
+ catch
+ case e: NCRejection => println(s"Expected rejection:
${e.getMessage}")
+ case e: Throwable => throw e
+ }
+
+
+
diff --git
a/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/impl/NCModelClientSpec3.scala
b/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/impl/NCModelClientSpec3.scala
new file mode 100644
index 0000000..962399f
--- /dev/null
+++
b/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/impl/NCModelClientSpec3.scala
@@ -0,0 +1,69 @@
+/*
+ * 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 NCModelClientSpec3:
+ @Test
+ def test(): Unit =
+ import NCSemanticTestElement as TE
+ val mdl: NCTestModelAdapter = new NCTestModelAdapter:
+ override val getPipeline: NCPipeline =
+ val pl = mkEnPipeline
+
pl.getEntityParsers.add(NCTestUtils.mkEnSemanticParser(TE("e1")))
+ pl
+
+ @NCIntent("intent=i1 term(t1)={# == 'e1'}")
+ def onMatch(@NCIntentTerm("t1") t1: NCEntity): NCResult = new
NCResult("Data", NCResultType.ASK_RESULT)
+
+ Using.resource(new NCModelClient(mdl)) { client =>
+ def ask(): NCCallbackData = client.debugAsk("e1", null, "userId",
true)
+ def execCallback(cb: NCCallbackData): NCResult =
cb.getCallback.apply(cb.getCallbackArguments)
+ def execCallbackOk(cb: NCCallbackData): Unit = println(s"Result:
${execCallback(cb).getBody}")
+ def execCallbackFail(cb: NCCallbackData): Unit =
+ try execCallback(cb)
+ catch case e: NCException => println(s"Expected error:
${e.getMessage}")
+
+ var cbData = ask()
+ execCallbackOk(cbData)
+ execCallbackFail(cbData) // It cannot be called again (Error is
'Callback was already called.')
+
+ cbData = ask()
+ execCallbackOk(cbData)
+
+ cbData = ask()
+ ask()
+ execCallbackFail(cbData) // Cannot be called, because there are
new requests (Error is 'Callback is out of date.')
+ }
+
+
+
diff --git
a/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/impl/NCModelPingPongSpec.scala
b/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/impl/NCModelPingPongSpec.scala
index 3428ab6..7428b3f 100644
---
a/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/impl/NCModelPingPongSpec.scala
+++
b/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/impl/NCModelPingPongSpec.scala
@@ -61,7 +61,7 @@ class NCModelPingPongSpec:
def onOther(im: NCIntentMatch, @NCIntentTerm("other") other:
NCEntity): NCResult =
R(ASK_RESULT, s"Some request by: ${other.mkText()}")
-
MDL.getPipeline.getEntityParsers.add(NCTestUtils.mkENSemanticParser(Seq(STE("command"),
STE("confirm"), STE("other")).asJava))
+
MDL.getPipeline.getEntityParsers.add(NCTestUtils.mkEnSemanticParser(Seq(STE("command"),
STE("confirm"), STE("other")).asJava))
@BeforeEach
def setUp(): Unit = client = new NCModelClient(MDL)
diff --git
a/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/impl/NCPipelineManagerSpec.scala
b/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/impl/NCPipelineManagerSpec.scala
index 5c50dd2..85c3e85 100644
---
a/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/impl/NCPipelineManagerSpec.scala
+++
b/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/impl/NCPipelineManagerSpec.scala
@@ -43,7 +43,7 @@ class NCPipelineManagerSpec:
def test(txt: String, variantCnt: Int, elements: NCSemanticElement*):
Unit =
val pipeline = mkEnPipeline
-
pipeline.getEntityParsers.add(NCTestUtils.mkENSemanticParser(elements.asJava))
+
pipeline.getEntityParsers.add(NCTestUtils.mkEnSemanticParser(elements.asJava))
val res = new NCModelPipelineManager(CFG, pipeline).prepare(txt,
null, "userId")
diff --git
a/nlpcraft/src/test/scala/org/apache/nlpcraft/nlp/entity/parser/semantic/NCSemanticEntityParserJsonSpec.scala
b/nlpcraft/src/test/scala/org/apache/nlpcraft/nlp/entity/parser/semantic/NCSemanticEntityParserJsonSpec.scala
index b11bcf5..00de5ab 100644
---
a/nlpcraft/src/test/scala/org/apache/nlpcraft/nlp/entity/parser/semantic/NCSemanticEntityParserJsonSpec.scala
+++
b/nlpcraft/src/test/scala/org/apache/nlpcraft/nlp/entity/parser/semantic/NCSemanticEntityParserJsonSpec.scala
@@ -34,7 +34,7 @@ import scala.jdk.OptionConverters.RichOptional
*
*/
class NCSemanticEntityParserJsonSpec:
- private val semParser =
NCTestUtils.mkENSemanticParser("models/alarm_model.json")
+ private val semParser =
NCTestUtils.mkEnSemanticParser("models/alarm_model.json")
/**
*
diff --git
a/nlpcraft/src/test/scala/org/apache/nlpcraft/nlp/entity/parser/semantic/NCSemanticEntityParserSpec.scala
b/nlpcraft/src/test/scala/org/apache/nlpcraft/nlp/entity/parser/semantic/NCSemanticEntityParserSpec.scala
index 71935ea..6e5ed77 100644
---
a/nlpcraft/src/test/scala/org/apache/nlpcraft/nlp/entity/parser/semantic/NCSemanticEntityParserSpec.scala
+++
b/nlpcraft/src/test/scala/org/apache/nlpcraft/nlp/entity/parser/semantic/NCSemanticEntityParserSpec.scala
@@ -36,7 +36,7 @@ import scala.jdk.OptionConverters.RichOptional
class NCSemanticEntityParserSpec:
import NCSemanticTestElement as E
private val semParser: NCSemanticEntityParser =
- NCTestUtils.mkENSemanticParser(
+ NCTestUtils.mkEnSemanticParser(
Seq(
// Standard.
E("t1", synonyms = Set("t1")),
diff --git
a/nlpcraft/src/test/scala/org/apache/nlpcraft/nlp/entity/parser/semantic/NCSemanticEntityParserYamlSpec.scala
b/nlpcraft/src/test/scala/org/apache/nlpcraft/nlp/entity/parser/semantic/NCSemanticEntityParserYamlSpec.scala
index 541c4e5..89f3ddd 100644
---
a/nlpcraft/src/test/scala/org/apache/nlpcraft/nlp/entity/parser/semantic/NCSemanticEntityParserYamlSpec.scala
+++
b/nlpcraft/src/test/scala/org/apache/nlpcraft/nlp/entity/parser/semantic/NCSemanticEntityParserYamlSpec.scala
@@ -32,7 +32,7 @@ import scala.jdk.OptionConverters.RichOptional
*
*/
class NCSemanticEntityParserYamlSpec:
- private val semParser: NCSemanticEntityParser =
NCTestUtils.mkENSemanticParser("models/lightswitch_model.yaml")
+ private val semParser: NCSemanticEntityParser =
NCTestUtils.mkEnSemanticParser("models/lightswitch_model.yaml")
/**
*
diff --git
a/nlpcraft/src/test/scala/org/apache/nlpcraft/nlp/util/NCTestUtils.scala
b/nlpcraft/src/test/scala/org/apache/nlpcraft/nlp/util/NCTestUtils.scala
index eb727d0..b444cf9 100644
--- a/nlpcraft/src/test/scala/org/apache/nlpcraft/nlp/util/NCTestUtils.scala
+++ b/nlpcraft/src/test/scala/org/apache/nlpcraft/nlp/util/NCTestUtils.scala
@@ -138,13 +138,21 @@ object NCTestUtils:
* @param macros
* @return
*/
- def mkENSemanticParser(elms: JList[NCSemanticElement], macros:
JMap[String, String] = null): NCSemanticEntityParser =
+ def mkEnSemanticParser(elms: JList[NCSemanticElement], macros:
JMap[String, String] = null): NCSemanticEntityParser =
new NCSemanticEntityParser(mkSemanticStemmer, EN_TOK_PARSER, macros,
elms)
/**
*
+ * @param elms
+ * @return
+ */
+ def mkEnSemanticParser(elms: NCSemanticElement*): NCSemanticEntityParser =
+ new NCSemanticEntityParser(mkSemanticStemmer, EN_TOK_PARSER, null,
elms.asJava)
+
+ /**
+ *
* @param src
* @return
*/
- def mkENSemanticParser(src: String): NCSemanticEntityParser =
+ def mkEnSemanticParser(src: String): NCSemanticEntityParser =
new NCSemanticEntityParser(mkSemanticStemmer, EN_TOK_PARSER, src)
\ No newline at end of file