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 3657b80 NCIntentSolver initial version added. 3657b80 is described below commit 3657b806ab4df5488b3f71cb22755821b025f3ab Author: Sergey Kamov <skhdlem...@gmail.com> AuthorDate: Thu Feb 17 13:32:38 2022 +0300 NCIntentSolver initial version added. --- .../internal/intent/matcher/NCIntentSolver.scala | 125 +++++++++++++++++++++ 1 file changed, 125 insertions(+) diff --git a/nlpcraft/src/main/scala/org/apache/nlpcraft/internal/intent/matcher/NCIntentSolver.scala b/nlpcraft/src/main/scala/org/apache/nlpcraft/internal/intent/matcher/NCIntentSolver.scala new file mode 100644 index 0000000..4f17322 --- /dev/null +++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/internal/intent/matcher/NCIntentSolver.scala @@ -0,0 +1,125 @@ +/* + * 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.intent.matcher + +import com.typesafe.scalalogging.LazyLogging +import org.apache.nlpcraft.* +import org.apache.nlpcraft.internal.dialogflow.NCDialogFlowManager +import org.apache.nlpcraft.internal.intent.NCIDLIntent + +import java.util.{Collections, List as JList} +import scala.collection.mutable +import scala.jdk.CollectionConverters.* + +/** + * Front-end for intent solver. + */ +class NCIntentSolver( + engine: NCIntentSolverEngine, + dialog: NCDialogFlowManager, + // TODO: order. + // TODO: NCIntentSolverInput contains model. + // TODO: logic with RedoSolver. + // TODO: NCIntentMatcher API. + // TODO: why 2 classes NCIntentSolver and NCIntentSolverEngine. + intents: Map[NCIDLIntent, NCIntentMatch => NCResult] +) extends LazyLogging: + /** + * + * @param in + * @param span + * @return + */ + def solve(in: NCIntentSolverInput): NCResult = + var res: NCResult = null + + while (res != null) + solve0(in) match + case Some(solverRes) => res = solverRes + case None => // No-op. + + res + + /** + * + * @param in Intent solver input. + * @param span Parent span. + * @return + * @throws NCRejection + */ + private def solve0(in: NCIntentSolverInput): Option[NCResult] = + // Should it be an assertion? + if intents.isEmpty then throw new NCRejection("Intent solver has no registered intents.") + + val ctx = in.context + val req = ctx.getRequest + + val results = + try engine.solve(ctx, intents) + catch case e: Exception => throw new NCRejection("Processing failed due to unexpected error.", e) + + if results.isEmpty then throw new NCRejection("No matching intent found.") + + var i = -1 + + for (res <- results if res != null) + try + i += 1 + + val intentMatch: NCIntentMatch = + new NCIntentMatch: + override val getIntentId: String = res.intentId + override val getIntentEntities: JList[JList[NCEntity]] = res.groups.map(_.entities).map(_.asJava).asJava + override def getTermEntities(idx: Int): JList[NCEntity] = res.groups(idx).entities.asJava + override def getTermEntities(termId: String): JList[NCEntity] = + res.groups.find(_.termId === termId) match + case Some(g) => g.entities.asJava + case None => Collections.emptyList() + override val getVariant: NCVariant = + new NCVariant: + override def getEntities: JList[NCEntity] = res.variant.entities.asJava + + if !in.model.onMatchedIntent(intentMatch) then + logger.info( + s"Model '${ctx.getModelConfig.getId}' triggered rematching of intents by intent '${res.intentId}' on variant #${res.variantIdx + 1}." + ) + + return None + + // This can throw NCIntentSkip exception. + val cbRes = res.fn(intentMatch) + + // Store won intent match in the input. + in.intentMatch = intentMatch + + if cbRes.getIntentId == null then + cbRes.setIntentId(res.intentId) + + logger.info(s"Intent '${res.intentId}' for variant #${res.variantIdx + 1} selected as the <|best match|>") + + dialog.addMatchedIntent(intentMatch, cbRes, ctx) + + return Option(cbRes) + catch + case e: NCIntentSkip => + // No-op - just skipping this result. + e.getMessage match + case s if s != null => logger.info(s"Selected intent '${res.intentId}' skipped: $s") + case _ => logger.info(s"Selected intent '${res.intentId}' skipped.") + + throw new NCRejection("No matching intent found - all intents were skipped.")