blob: 4b5553e18e15e566450a0cf8684f8dcd7aba2a94 [file] [log] [blame]
/*
* 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 com.typesafe.scalalogging.LazyLogging
import org.apache.nlpcraft.*
import org.apache.nlpcraft.internal.*
import org.apache.nlpcraft.internal.ascii.NCAsciiTable
import org.apache.nlpcraft.internal.conversation.*
import org.apache.nlpcraft.internal.dialogflow.NCDialogFlowManager
import org.apache.nlpcraft.internal.impl.*
import org.apache.nlpcraft.internal.intent.matcher.*
import org.apache.nlpcraft.internal.util.*
import java.util
import java.util.concurrent.*
import java.util.concurrent.atomic.*
import java.util.{Objects, UUID}
import scala.concurrent.ExecutionContext
/**
*
* @param mdl
*/
class NCModelClient(mdl: NCModel) extends LazyLogging, AutoCloseable:
verify()
private val intents = NCModelScanner.scan(mdl)
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, convMgr, intents.map(p => p.intent -> p.function).toMap)
init()
/**
*
*/
private def verify(): Unit =
require(mdl != null, "Model cannot be null.")
val cfg = mdl.getConfig
val pipeline = mdl.getPipeline
require(cfg.getId != null, "Model ID cannot be null.")
require(cfg.getName != null, "Model name cannot be null.")
require(cfg.getVersion != null, "Model version cannot be null.")
require(pipeline.getTokenParser != null, "Token parser cannot be null.")
require(pipeline.getEntityParsers != null, "List of entity parsers in the pipeline cannot be null.")
/**
*
*/
private def init(): Unit =
convMgr.start()
dlgMgr.start()
plMgr.start()
/**
*
* @param txt
* @param data
* @param usrId
* @param typ
* @return
*/
private def ask0(txt: String, data: Map[String, Any], 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)
convHldr.updateEntities()
val conv: NCConversation =
new NCConversation:
override val getData: NCPropertyMap = convHldr.getUserData
override val getStm: List[NCEntity] = convHldr.getEntities
override val getDialogFlow: List[NCDialogFlowItem] = dlgMgr.getDialogFlow(userId)
override def clearStm(filter: NCEntity => Boolean): Unit = convHldr.clear(filter)
override def clearDialog(filter: NCDialogFlowItem => Boolean): Unit = dlgMgr.clear(userId, (s: NCDialogFlowItem) => filter(s))
val ctx: NCContext =
new NCContext:
override def isOwnerOf(ent: NCEntity): Boolean = allEnts.contains(ent)
override val getModelConfig: NCModelConfig = mdl.getConfig
override val getRequest: NCRequest = plData.request
override val getConversation: NCConversation = conv
override val getVariants: List[NCVariant] = plData.variants
override val getTokens: List[NCToken] = plData.tokens
intentsMgr.solve(mdl, ctx, typ)
/*
* @param txt
* @param data
* @param usrId
* @return
*/
def ask(txt: String, data: Map[String, AnyRef], usrId: String): NCResult =
require(txt != null, "Input text cannot be null.")
require(data != null, "Data cannot be null.")
require(usrId != null, "User id cannot be null.")
ask0(txt, data, usrId, NCIntentSolveType.REGULAR).swap.toOption.get
def ask(txt: String, usrId: String): NCResult = ask(txt, Map.empty, usrId)
/**
*
* @param usrId
*/
def clearStm(usrId: String): Unit =
require(usrId != null, "User id cannot be null.")
convMgr.getConversation(usrId).clear(_ => true)
/**
*
* @param usrId
* @param filter
*/
def clearStm(usrId: String, filter: NCEntity => Boolean): Unit =
require(usrId != null, "User id cannot be null.")
require(filter != null, "Filter cannot be null.")
convMgr.getConversation(usrId).clear(filter)
/**
*
* @param usrId
*/
def clearDialog(usrId: String): Unit =
require(usrId != null, "User id cannot be null.")
dlgMgr.clear(usrId)
/**
*
* @param usrId
*/
def clearDialog(usrId: String, filter: NCDialogFlowItem => Boolean): Unit =
require(usrId != null, "User id cannot be null.")
require(usrId != null, "Filter cannot be null.")
dlgMgr.clear(usrId, (i: NCDialogFlowItem) => filter(i))
/**
*
*/
def validateSamples(): Unit =
case class Result(intentId: String, text: String, error: Option[String], time: Long)
val userId = UUID.randomUUID().toString
val res = scala.collection.mutable.ArrayBuffer.empty[Result]
def now: Long = System.currentTimeMillis()
for (i <- intents; samples <- i.samples)
for (sample <- samples)
val start = now
val err: Option[String] =
try
val r = ask(sample, Map.empty, userId)
Option.when(r.getIntentId.isEmpty || r.getIntentId.get != i.intent.id)(s"Unexpected intent ID: '${r.getIntentId.getOrElse("(not set)")}'")
catch case e: Throwable =>
logger.warn("Unexpected error.", e)
Option(e.getLocalizedMessage)
res += Result(i.intent.id, sample, err, now - start)
clearDialog(userId)
clearStm(userId)
val tbl = NCAsciiTable()
tbl #= ("Intent ID", "+/-", "Text", "Error", "ms.")
for (res <- res)
tbl += (
res.intentId,
if res.error.isEmpty then "OK" else "FAIL",
res.text,
res.error.getOrElse(""),
res.time
)
val passCnt = res.count(_.error.isEmpty)
val failCnt = res.count(_.error.isDefined)
tbl.info(logger, Option(s"Model auto-validation results: OK $passCnt, FAIL $failCnt:"))
if failCnt > 0 then require(false, "Some tests failed.")
/**
*
*/
def close(): Unit =
plMgr.close()
dlgMgr.close()
convMgr.close()
intentsMgr.close()
/**
*
* @param txt
* @param data
* @param usrId
* @param saveHist
* @return
*/
def debugAsk(txt: String, data: Map[String, AnyRef], usrId: String, saveHist: Boolean): NCCallbackData =
require(txt != null, "Input text cannot be null.")
require(data != null, "Data cannot be null.")
require(usrId != null, "User id cannot be null.")
import NCIntentSolveType.*
ask0(txt, data, usrId, if saveHist then SEARCH else SEARCH_NO_HISTORY).toOption.get
def debugAsk(txt: String, usrId: String, saveHist: Boolean): NCCallbackData = debugAsk(txt, Map.empty, usrId, saveHist)