
definition.BotDefinitionBase.kt Maven / Gradle / Ivy
/*
* Copyright (C) 2017/2021 e-voyageurs technologies
*
* Licensed 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
*
* http://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 ai.tock.bot.definition
import ai.tock.bot.admin.bot.observability.BotObservabilityConfiguration
import ai.tock.bot.admin.bot.rag.BotRAGConfiguration
import ai.tock.bot.admin.bot.vectorstore.BotVectorStoreConfiguration
import ai.tock.bot.engine.BotBus
import ai.tock.bot.engine.action.Action
import ai.tock.bot.engine.action.SendSentence
import ai.tock.bot.engine.config.RAGAnswerHandler
import ai.tock.bot.engine.dialog.Dialog
import ai.tock.bot.engine.nlp.BuiltInKeywordListener.deleteKeyword
import ai.tock.bot.engine.nlp.BuiltInKeywordListener.endTestContextKeyword
import ai.tock.bot.engine.nlp.BuiltInKeywordListener.testContextKeyword
import ai.tock.bot.engine.nlp.keywordServices
import ai.tock.bot.engine.user.UserTimelineDAO
import ai.tock.shared.error
import ai.tock.shared.injector
import ai.tock.shared.vertx.vertx
import ai.tock.translator.I18nKeyProvider.Companion.generateKey
import ai.tock.translator.I18nLabelValue
import com.github.salomonbrys.kodein.instance
import io.vertx.core.Promise
import mu.KotlinLogging
/**
* Base implementation of [BotDefinition].
*/
open class BotDefinitionBase(
override val botId: String,
override val namespace: String,
override val stories: List,
override val nlpModelName: String = botId,
override val unknownStory: StoryDefinition = defaultUnknownStory,
override val helloStory: StoryDefinition? = null,
override val goodbyeStory: StoryDefinition? = null,
override val noInputStory: StoryDefinition? = null,
override val botDisabledStory: StoryDefinition? = null,
override val botEnabledStory: StoryDefinition? = null,
override val userLocationStory: StoryDefinition? = null,
override val handleAttachmentStory: StoryDefinition? = null,
override val eventListener: EventListener = EventListenerBase(),
override val keywordStory: StoryDefinition = defaultKeywordStory,
override val flowDefinition: DialogFlowDefinition? = null,
override val botEnabledListener: (Action) -> Unit = {},
override val ragExcludedStory: StoryDefinition = defaultRagExcludedStory,
override val ragStory: StoryDefinition = defaultRagStory,
override var ragConfiguration: BotRAGConfiguration? = null,
override var vectorStoreConfiguration: BotVectorStoreConfiguration? = null,
override var observabilityConfiguration: BotObservabilityConfiguration? = null
) : BotDefinition {
companion object {
private val logger = KotlinLogging.logger {}
/**
* The default [unknownStory].
*/
val defaultUnknownStory =
SimpleStoryDefinition(
"tock_unknown_story",
object : SimpleStoryHandlerBase() {
override fun action(bus: BotBus) {
bus.markAsUnknown()
bus.end(bus.botDefinition.defaultUnknownAnswer)
}
},
setOf(Intent.unknown)
)
/**
* The default [ragExcludedStory].
*/
val defaultRagExcludedStory =
SimpleStoryDefinition(
"tock_ragexcluded_story",
object : SimpleStoryHandlerBase() {
override fun action(bus: BotBus) {
bus.end(bus.botDefinition.defaultRagExcludedAnswer)
}
},
setOf(Intent.ragexcluded)
)
val defaultRagStory =
RAGStoryDefinition(
object : SimpleStoryHandlerBase() {
override fun action(bus: BotBus) {
bus.markAsUnknown()
RAGAnswerHandler.handle(bus)
}
},
)
/**
* Returns a (potential) keyword from the [BotBus].
*/
fun getKeyword(bus: BotBus): String? {
return if (bus.action is SendSentence) {
(bus.action as SendSentence).stringText
} else {
null
}
}
/**
* The default handler used to handle test context initialization.
*/
fun testContextKeywordHandler(bus: BotBus, sendEnd: Boolean = true) {
bus.userTimeline.dialogs.add(
Dialog(
setOf(bus.userId, bus.botId)
)
)
bus.botDefinition.testBehaviour.setup(bus)
if (sendEnd) {
bus.end(bus.baseI18nValue("test context activated (user state cleaned)"))
}
}
/**
* The default handler used to cleanup test context.
*/
fun endTestContextKeywordHandler(bus: BotBus, sendEnd: Boolean = true) {
bus.userTimeline.dialogs.add(
Dialog(
setOf(bus.userId, bus.botId)
)
)
bus.botDefinition.testBehaviour.cleanup(bus)
if (sendEnd) {
bus.end(bus.baseI18nValue("test context disabled"))
}
}
/**
* The default handler used to delete the current user.
*/
fun deleteKeywordHandler(bus: BotBus, sendEnd: Boolean = true) {
bus.handleDelete()
if (sendEnd) {
bus.end(
bus.baseI18nValue(
"user removed - {0} {1}",
bus.userTimeline.userPreferences.firstName,
bus.userTimeline.userPreferences.lastName
)
)
}
}
private fun BotBus.baseI18nValue(
defaultLabel: String,
vararg args: Any?
): I18nLabelValue = i18nValue(botDefinition.namespace, defaultLabel, *args)
private fun i18nValue(
namespace: String,
defaultLabel: String,
vararg args: Any?
): I18nLabelValue =
I18nLabelValue(
generateKey(namespace, "keywords", defaultLabel),
namespace,
"keywords",
defaultLabel,
args.toList()
)
private fun BotBus.handleDelete() {
val userTimelineDao: UserTimelineDAO by injector.instance()
// run later to avoid the lock effect :)
vertx.setTimer(1000) {
vertx.executeBlocking(
{ it: Promise ->
try {
userTimelineDao.remove(botDefinition.namespace, userId)
} catch (e: Exception) {
logger.error(e)
} finally {
it.complete()
}
},
false, {}
)
}
}
/**
* The default [keywordStory].
*/
val defaultKeywordStory =
SimpleStoryDefinition(
"tock_keyword_story",
object : SimpleStoryHandlerBase() {
override fun action(bus: BotBus) {
val text = getKeyword(bus)
if (!handleWithKeywordListeners(bus, text)) {
when (text) {
deleteKeyword -> deleteKeywordHandler(bus)
testContextKeyword -> testContextKeywordHandler(bus)
endTestContextKeyword -> endTestContextKeywordHandler(bus)
else -> bus.end(bus.baseI18nValue("unknown keyword : {0}", text))
}
}
}
},
setOf(Intent.keyword)
)
fun handleWithKeywordListeners(bus: BotBus, keyword: String?): Boolean {
if (keyword != null) {
keywordServices.asSequence().map { it.keywordHandler(keyword) }.firstOrNull()?.let { handler ->
handler(bus)
return true
}
}
return false
}
}
/**
* Constructor intended to be used by an enum.
*/
constructor(botId: String, stories: Array) : this(botId, botId, stories.toList(), botId)
/**
* The default unknown answer.
*/
override val defaultUnknownAnswer: I18nLabelValue get() = i18n("Sorry, I didn't understand :(")
/**
* The default ragExcluded answer.
*/
override val defaultRagExcludedAnswer: I18nLabelValue get() = i18n("Sorry, I can't answer your question (Topic not covered)")
override fun toString(): String {
return botId
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy