WIP.
diff --git a/nlpcraft/src/main/scala/org/apache/nlpcraft/probe/mgrs/cmd/NCCommandManager.scala b/nlpcraft/src/main/scala/org/apache/nlpcraft/probe/mgrs/cmd/NCCommandManager.scala
index f63a088..26d29c1 100644
--- a/nlpcraft/src/main/scala/org/apache/nlpcraft/probe/mgrs/cmd/NCCommandManager.scala
+++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/probe/mgrs/cmd/NCCommandManager.scala
@@ -17,11 +17,12 @@
package org.apache.nlpcraft.probe.mgrs.cmd
-import com.google.gson.Gson
+import com.fasterxml.jackson.databind.ObjectMapper
+import com.fasterxml.jackson.module.scala.DefaultScalaModule
import io.opencensus.trace.Span
import org.apache.nlpcraft.common.nlp.NCNlpSentence
import org.apache.nlpcraft.common.{NCService, _}
-import org.apache.nlpcraft.model.NCToken
+import org.apache.nlpcraft.model.{NCCustomParser, NCElement, NCModelView, NCToken, NCValue, NCValueLoader}
import org.apache.nlpcraft.probe.mgrs.NCProbeMessage
import org.apache.nlpcraft.probe.mgrs.conn.NCConnectionManager
import org.apache.nlpcraft.probe.mgrs.conversation.NCConversationManager
@@ -30,21 +31,18 @@
import org.apache.nlpcraft.probe.mgrs.nlp.NCProbeEnrichmentManager
import java.io.{Serializable => JSerializable}
-import java.util
-import java.util.{Collections, List => JList}
-import scala.jdk.CollectionConverters.{ListHasAsScala, MapHasAsJava, MapHasAsScala, SeqHasAsJava, SetHasAsScala}
+import java.util.{Collections, Optional, List => JList}
+import java.{lang, util}
+import scala.jdk.CollectionConverters.{ListHasAsScala, MapHasAsJava, MapHasAsScala, SeqHasAsJava, SetHasAsJava, SetHasAsScala}
/**
* Probe commands processor.
*/
object NCCommandManager extends NCService {
- private final val GSON = new Gson()
+ private final val JS_MAPPER = new ObjectMapper()
- /**
- * Starts this service.
- *
- * @param parent Optional parent span.
- */
+ JS_MAPPER.registerModule(DefaultScalaModule)
+
override def start(parent: Span): NCService = startScopedSpan("start", parent) { _ =>
ackStarting()
ackStarted()
@@ -128,7 +126,7 @@
span
)
- case "S2P_MODEL_INFO" =>
+ case "S2P_MODEL_SYNS_INFO" =>
send0(
mkMsg = () => {
val mdlId = msg.data[String]("mdlId")
@@ -142,9 +140,9 @@
mdlData.samples.map(p => p._1 -> p._2.map(_.asJava).asJava).toMap.asJava
NCProbeMessage(
- "P2S_MODEL_INFO",
+ "P2S_MODEL_SYNS_INFO",
"reqGuid" -> msg.getGuid,
- "resp" -> GSON.toJson(
+ "resp" -> JS_MAPPER.writeValueAsString(
Map(
"macros" -> macros.asInstanceOf[JSerializable],
"synonyms" -> syns.asInstanceOf[JSerializable],
@@ -155,7 +153,7 @@
},
mkErrorMsg = e =>
NCProbeMessage(
- "P2S_MODEL_INFO",
+ "P2S_MODEL_SYNS_INFO",
"reqGuid" -> msg.getGuid,
"error" -> e.getLocalizedMessage
),
@@ -182,7 +180,7 @@
NCProbeMessage(
"P2S_MODEL_ELEMENT_INFO",
"reqGuid" -> msg.getGuid,
- "resp" -> GSON.toJson(
+ "resp" -> JS_MAPPER.writeValueAsString(
Map(
"synonyms" -> elm.getSynonyms.asInstanceOf[JSerializable],
"values" -> vals.asInstanceOf[JSerializable],
@@ -200,6 +198,103 @@
span
)
+ case "S2P_MODEL_INFO" =>
+ send0(
+ mkMsg = () => {
+ val mdlId = msg.data[String]("mdlId")
+
+ val mdl = NCModelManager.getModel(mdlId).model
+
+ val convertedMdl =
+ new NCModelView {
+ // As is.
+ override def getId: String = mdl.getId
+ override def getName: String = mdl.getName
+ override def getVersion: String = mdl.getVersion
+ override def getDescription: String = mdl.getDescription
+ override def getOrigin: String = mdl.getOrigin
+ override def getMaxUnknownWords: Int = mdl.getMaxUnknownWords
+ override def getMaxFreeWords: Int = mdl.getMaxFreeWords
+ override def getMaxSuspiciousWords: Int = mdl.getMaxSuspiciousWords
+ override def getMinWords: Int = mdl.getMinWords
+ override def getMaxWords: Int = mdl.getMaxWords
+ override def getMinTokens: Int = mdl.getMinTokens
+ override def getMaxTokens: Int = mdl.getMaxTokens
+ override def getMinNonStopwords: Int = mdl.getMinNonStopwords
+ override def isNonEnglishAllowed: Boolean = mdl.isNonEnglishAllowed
+ override def isNotLatinCharsetAllowed: Boolean = mdl.isNotLatinCharsetAllowed
+ override def isSwearWordsAllowed: Boolean = mdl.isSwearWordsAllowed
+ override def isNoNounsAllowed: Boolean = mdl.isNoNounsAllowed
+ override def isPermutateSynonyms: Boolean = mdl.isPermutateSynonyms
+ override def isDupSynonymsAllowed: Boolean = mdl.isDupSynonymsAllowed
+ override def getMaxTotalSynonyms: Int = mdl.getMaxTotalSynonyms
+ override def isNoUserTokensAllowed: Boolean = mdl.isNoUserTokensAllowed
+ override def isSparse: Boolean = mdl.isSparse
+ override def getMetadata: util.Map[String, AnyRef] = mdl.getMetadata
+ override def getAdditionalStopWords: util.Set[String] =
+ mdl.getAdditionalStopWords
+ override def getExcludedStopWords: util.Set[String] = mdl.getExcludedStopWords
+ override def getSuspiciousWords: util.Set[String] = mdl.getSuspiciousWords
+ override def getMacros: util.Map[String, String] = mdl.getMacros
+ override def getEnabledBuiltInTokens: util.Set[String] =
+ mdl.getEnabledBuiltInTokens
+ override def getAbstractTokens: util.Set[String] = mdl.getAbstractTokens
+ override def getMaxElementSynonyms: Int = mdl.getMaxElementSynonyms
+ override def isMaxSynonymsThresholdError: Boolean =
+ mdl.isMaxSynonymsThresholdError
+ override def getConversationTimeout: Long = mdl.getConversationTimeout
+ override def getConversationDepth: Int = mdl.getConversationDepth
+ override def getRestrictedCombinations: util.Map[String, util.Set[String]] =
+ mdl.getRestrictedCombinations
+
+ // Cleared.
+ override def getParsers: JList[NCCustomParser] = null
+ // Converted.
+ override def getElements: util.Set[NCElement] = mdl.getElements.asScala.map(e =>
+ new NCElement {
+ // As is.
+ override def getId: String = e.getId
+ override def getGroups: JList[String] = e.getGroups
+ override def getMetadata: util.Map[String, AnyRef] = e.getMetadata
+ override def getDescription: String = e.getDescription
+ override def getParentId: String = e.getParentId
+ override def getSynonyms: JList[String] = e.getSynonyms
+ override def isPermutateSynonyms: Optional[lang.Boolean] =
+ e.isPermutateSynonyms
+ override def isSparse: Optional[lang.Boolean] = e.isSparse
+
+ // Cleared.
+ override def getValueLoader: Optional[NCValueLoader] = null
+ // Converted.
+ override def getValues: JList[NCValue] =
+ if (e.getValues != null) {
+ e.getValues.asScala.map(v => new NCValue {
+ override def getName: String = v.getName
+ // Cleared.
+ override def getSynonyms: JList[String] = null
+ }).asJava
+ }
+ else
+ null
+ }
+ ).asJava
+ }
+
+ NCProbeMessage(
+ "P2S_MODEL_INFO",
+ "reqGuid" -> msg.getGuid,
+ "resp" -> JS_MAPPER.writeValueAsString(convertedMdl)
+ )
+ },
+ mkErrorMsg = e =>
+ NCProbeMessage(
+ "P2S_MODEL_SYNS_INFO",
+ "reqGuid" -> msg.getGuid,
+ "error" -> e.getLocalizedMessage
+ ),
+ span
+ )
+
case _ =>
logger.error(s"Received unknown server message (you need to update the probe): ${msg.getType}")
}
diff --git a/nlpcraft/src/main/scala/org/apache/nlpcraft/server/probe/NCProbeManager.scala b/nlpcraft/src/main/scala/org/apache/nlpcraft/server/probe/NCProbeManager.scala
index 04792f6..3c7c46c 100644
--- a/nlpcraft/src/main/scala/org/apache/nlpcraft/server/probe/NCProbeManager.scala
+++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/server/probe/NCProbeManager.scala
@@ -25,7 +25,7 @@
import org.apache.nlpcraft.common.crypto.NCCipher
import org.apache.nlpcraft.common.makro.NCMacroParser
import org.apache.nlpcraft.common.nlp.NCNlpSentence
-import org.apache.nlpcraft.common.nlp.core.{NCNlpCoreManager, NCNlpPorterStemmer}
+import org.apache.nlpcraft.common.nlp.core.NCNlpCoreManager
import org.apache.nlpcraft.common.pool.NCThreadPoolManager
import org.apache.nlpcraft.common.socket.NCSocket
import org.apache.nlpcraft.common.version.NCVersion
@@ -54,8 +54,7 @@
*/
object NCProbeManager extends NCService {
private final val GSON = new Gson()
- private final val TYPE_MODEL_INFO_RESP = new TypeToken[JavaMeta]() {}.getType
- private final val TYPE_MODEL_ELEMENT_INFO_RESP = new TypeToken[JavaMeta]() {}.getType
+ private final val TYPE_JAVA_META = new TypeToken[JavaMeta]() {}.getType
// Type safe and eager configuration container.
private object Config extends NCConfigurable {
@@ -67,7 +66,7 @@
def reconnectTimeoutMs: Long = getLong(s"$pre.reconnectTimeoutMs")
def pingTimeoutMs: Long = getLong(s"$pre.pingTimeoutMs")
def soTimeoutMs: Int = getInt(s"$pre.soTimeoutMs")
-
+
/**
*
*/
@@ -158,8 +157,9 @@
// All probes pending complete handshake keyed by probe key.
@volatile private var pending: mutable.Map[ProbeKey, ProbeHolder] = _
- @volatile private var modelsInfo: ConcurrentHashMap[String, Promise[JavaMeta]] = _
+ @volatile private var modelsSynsInfo: ConcurrentHashMap[String, Promise[JavaMeta]] = _
@volatile private var modelElmsInfo: ConcurrentHashMap[String, Promise[JavaMeta]] = _
+ @volatile private var modelsInfo: ConcurrentHashMap[String, Promise[JavaMeta]] = _
/**
*
@@ -181,8 +181,9 @@
"downlink" -> s"$dnHost:$dnPort"
)
- modelsInfo = new ConcurrentHashMap[String, Promise[JavaMeta]]()
+ modelsSynsInfo = new ConcurrentHashMap[String, Promise[JavaMeta]]()
modelElmsInfo = new ConcurrentHashMap[String, Promise[JavaMeta]]()
+ modelsInfo = new ConcurrentHashMap[String, Promise[JavaMeta]]()
dnSrv = startServer("Downlink", dnHost, dnPort, downLinkHandler)
upSrv = startServer("Uplink", upHost, upPort, upLinkHandler)
@@ -219,8 +220,9 @@
U.stopThread(dnSrv)
U.stopThread(upSrv)
- modelsInfo = null
+ modelsSynsInfo = null
modelElmsInfo = null
+ modelsInfo = null
ackStopped()
}
@@ -691,6 +693,23 @@
}
}
}
+
+ /**
+ *
+ * @param probeMsg
+ * @param m
+ */
+ private def processJavaMetaMessage(probeMsg: NCProbeMessage, m: ConcurrentHashMap[String, Promise[JavaMeta]]): Unit = {
+ val p = m.remove(probeMsg.data[String]("reqGuid"))
+
+ if (p != null)
+ probeMsg.dataOpt[String]("resp") match {
+ case Some(resp) => p.success(GSON.fromJson(resp, TYPE_JAVA_META))
+ case None => p.failure(new NCE(probeMsg.data[String]("error")))
+ }
+ else
+ logger.warn(s"Message ignored: $probeMsg")
+ }
/**
* Processes the messages received from the probe.
@@ -716,27 +735,9 @@
typ match {
case "P2S_PING" => ()
- case "P2S_MODEL_INFO" =>
- val p = modelsInfo.remove(probeMsg.data[String]("reqGuid"))
-
- if (p != null)
- probeMsg.dataOpt[String]("resp") match {
- case Some(resp) => p.success(GSON.fromJson(resp, TYPE_MODEL_INFO_RESP))
- case None => p.failure(new NCE(probeMsg.data[String]("error")))
- }
- else
- logger.warn(s"Message ignored: $probeMsg")
-
- case "P2S_MODEL_ELEMENT_INFO" =>
- val p = modelElmsInfo.remove(probeMsg.data[String]("reqGuid"))
-
- if (p != null)
- probeMsg.dataOpt[String]("resp") match {
- case Some(resp) => p.success(GSON.fromJson(resp, TYPE_MODEL_ELEMENT_INFO_RESP))
- case None => p.failure(new NCE(probeMsg.data[String]("error")))
- }
- else
- logger.warn(s"Message ignored: $probeMsg")
+ case "P2S_MODEL_SYNS_INFO" => processJavaMetaMessage(probeMsg, modelsSynsInfo)
+ case "P2S_MODEL_ELEMENT_INFO" => processJavaMetaMessage(probeMsg, modelElmsInfo)
+ case "P2S_MODEL_INFO" => processJavaMetaMessage(probeMsg, modelsInfo)
case "P2S_ASK_RESULT" =>
val srvReqId = probeMsg.data[String]("srvReqId")
@@ -1089,12 +1090,12 @@
* @param parent
* @return
*/
- def getModelInfo(mdlId: String, parent: Span = null): Future[JavaMeta] =
- startScopedSpan("getModelInfo", parent, "mdlId" -> mdlId) { _ =>
+ def getModelSynonymsInfo(mdlId: String, parent: Span = null): Future[JavaMeta] =
+ startScopedSpan("getModelSynonymsInfo", parent, "mdlId" -> mdlId) { _ =>
processModelDataRequest(
mdlId,
- NCProbeMessage("S2P_MODEL_INFO", "mdlId" -> mdlId),
- modelsInfo,
+ NCProbeMessage("S2P_MODEL_SYNS_INFO", "mdlId" -> mdlId),
+ modelsSynsInfo,
parent
)
}
@@ -1142,4 +1143,15 @@
}
)
}
+
+ def getModelInfo(mdlId: String, parent: Span = null): Future[JavaMeta] =
+ startScopedSpan("getModelInfo", parent, "mdlId" -> mdlId) { _ =>
+ processModelDataRequest(
+ mdlId,
+ NCProbeMessage("S2P_MODEL_INFO", "mdlId" -> mdlId),
+ modelsInfo,
+ parent
+ )
+ }
+
}
diff --git a/nlpcraft/src/main/scala/org/apache/nlpcraft/server/rest/NCBasicRestApi.scala b/nlpcraft/src/main/scala/org/apache/nlpcraft/server/rest/NCBasicRestApi.scala
index e74fb68..39b84e4 100644
--- a/nlpcraft/src/main/scala/org/apache/nlpcraft/server/rest/NCBasicRestApi.scala
+++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/server/rest/NCBasicRestApi.scala
@@ -879,6 +879,51 @@
}
}
+
+ /**
+ *
+ * @return
+ */
+ protected def $model$info(): Route = {
+ case class Req$Model$Info(
+ acsTok: String,
+ mdlId: String
+ )
+
+ implicit val reqFmt: RootJsonFormat[Req$Model$Info] = jsonFormat2(Req$Model$Info)
+
+ entity(as[Req$Model$Info]) { req =>
+ startScopedSpan(
+ "model$syns",
+ "acsTok" -> req.acsTok,
+ "mdlId" -> req.mdlId
+ ) { span =>
+ checkLength("acsTok" -> req.acsTok, "mdlId" -> req.mdlId)
+
+ val admUsr = authenticateAsAdmin(req.acsTok)
+ val compId = admUsr.companyId
+
+ if (!NCProbeManager.existsForModel(compId, req.mdlId))
+ throw InvalidModelId(req.mdlId)
+
+ val fut = NCProbeManager.getModelInfo(req.mdlId, span)
+
+ successWithJs(
+ fut.collect {
+ // We have to use Jackson (not spray) here to serialize 'result' field.
+ case res =>
+ toJs(
+ Map(
+ "status" -> API_OK.toString,
+ "model" -> res
+ )
+ )
+ }
+ )
+ }
+ }
+ }
+
/**
*
* @return
@@ -2040,6 +2085,7 @@
path(API / "probe" / "all") { withMetric(M_PROBE_ALL_LATENCY_MS, $probe$All) } ~
path(API / "model" / "sugsyn") { withMetric(M_MODEL_SUGSYN_LATENCY_MS, $model$sugsyn) } ~
path(API / "model" / "syns") { withMetric(M_MODEL_SYNS_LATENCY_MS, $model$syns) } ~
+ path(API / "model" / "info") { withMetric(M_MODEL_SYNS_LATENCY_MS, $model$info) } ~
path(API / "ask") { withMetric(M_ASK_LATENCY_MS, $ask) } ~
path(API / "ask" / "sync") { withMetric(M_ASK_SYNC_LATENCY_MS, $ask$Sync) }
}
diff --git a/nlpcraft/src/main/scala/org/apache/nlpcraft/server/sugsyn/NCSuggestSynonymManager.scala b/nlpcraft/src/main/scala/org/apache/nlpcraft/server/sugsyn/NCSuggestSynonymManager.scala
index fae55d0..d89ba98 100644
--- a/nlpcraft/src/main/scala/org/apache/nlpcraft/server/sugsyn/NCSuggestSynonymManager.scala
+++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/server/sugsyn/NCSuggestSynonymManager.scala
@@ -143,7 +143,7 @@
val promise = Promise[NCSuggestSynonymResult]()
- NCProbeManager.getModelInfo(mdlId, parent).onComplete {
+ NCProbeManager.getModelSynonymsInfo(mdlId, parent).onComplete {
case Success(m) =>
try {
require(
diff --git a/nlpcraft/src/test/scala/org/apache/nlpcraft/server/rest/NCRestModelSpec.scala b/nlpcraft/src/test/scala/org/apache/nlpcraft/server/rest/NCRestModelSpec.scala
index 0b8c03f..d7d22b8 100644
--- a/nlpcraft/src/test/scala/org/apache/nlpcraft/server/rest/NCRestModelSpec.scala
+++ b/nlpcraft/src/test/scala/org/apache/nlpcraft/server/rest/NCRestModelSpec.scala
@@ -111,4 +111,15 @@
postError("model/syns", 400, "NC_INVALID_FIELD", "mdlId" -> "rest.test.model", "elmId" -> ("A" * 65))
postError("model/syns", 400, "NC_ERROR", "mdlId" -> "rest.test.model")
}
+
+ @Test
+ def testModelInfo(): Unit = {
+ post("model/info", "mdlId" -> "rest.test.model")(
+ ("$.status", (status: String) => assertEquals("API_OK", status)),
+ ("$.model", (data: java.util.Map[Object, Object]) => assertTrue(!data.isEmpty))
+ )
+
+ postError("model/info", 400, "NC_INVALID_FIELD", "mdlId" -> "UNKNOWN")
+ postError("model/info", 400, "NC_ERROR")
+ }
}