
engine.config.BotDefinitionWrapper.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.engine.config
import ai.tock.bot.admin.answer.AnswerConfigurationType.builtin
import ai.tock.bot.admin.bot.BotApplicationConfigurationKey
import ai.tock.bot.admin.story.StoryDefinitionConfiguration
import ai.tock.bot.definition.BotDefinition
import ai.tock.bot.definition.Intent
import ai.tock.bot.definition.Intent.Companion.ragexcluded
import ai.tock.bot.definition.Intent.Companion.unknown
import ai.tock.bot.definition.IntentAware
import ai.tock.bot.definition.StoryDefinition
import ai.tock.bot.definition.StoryHandler
import ai.tock.bot.definition.StoryTag
import ai.tock.bot.engine.action.Action
import ai.tock.bot.engine.dialog.Dialog
import ai.tock.bot.engine.user.UserTimeline
import mu.KotlinLogging
/**
*
*/
internal class BotDefinitionWrapper(val botDefinition: BotDefinition) : BotDefinition by botDefinition {
private val logger = KotlinLogging.logger {}
// stories with configuration (including built-in)
@Volatile
private var configuredStories: Map> = emptyMap()
@Volatile
private var allStoriesById: Map = botDefinition.stories.associateBy { it.id }
// all stories
@Volatile
private var allStories: List = botDefinition.stories
override fun disableBot(timeline: UserTimeline, dialog: Dialog, action: Action): Boolean =
super.disableBot(timeline, dialog, action)
override fun enableBot(timeline: UserTimeline, dialog: Dialog, action: Action): Boolean =
super.enableBot(timeline, dialog, action)
override fun hasDisableTagIntent(dialog: Dialog): Boolean =
super.hasDisableTagIntent(dialog)
private fun findStoryDefinitionByTag(tag: StoryTag): List =
stories.filter { it.tags.contains(tag) }
override val botDisabledStories: List
get() = findStoryDefinitionByTag(StoryTag.DISABLE)
override val botEnabledStories: List
get() = findStoryDefinitionByTag(StoryTag.ENABLE)
// only built-in
private val builtInStoriesMap: Map = botDefinition.stories.associateBy { it.id }
fun updateStories(configuredStories: List) {
val activatedModules = findActivatedModules(configuredStories.map { it.storyId })
val botStoryHandlers = activatedModules.flatMap { it.storiesById.entries }.associateBy({ it.key }) { it.value }
this.configuredStories =
configuredStories
.map {
ConfiguredStoryDefinition(
definition = this,
configuration = it,
configurationStoryHandler = botStoryHandlers[it.storyId]
)
}
.groupBy { it.storyId }
allStories = (
this.configuredStories +
// in order to handle built-in not yet configured...
botDefinition
.stories
.asSequence()
.filterNot { this.configuredStories.containsKey(it.id) }
.groupBy { it.id }
)
.values.flatten()
this.allStoriesById = allStories.associateBy { it.id }
}
override val stories: List
get() = allStories
override fun findIntent(intent: String, applicationId: String): Intent {
val i = super.findIntent(intent, applicationId)
return if (i == ragexcluded) {
val i2 = botDefinition.findIntent(intent, applicationId)
if (i2 == ragexcluded) BotDefinition.findIntent(stories, intent) else i2
} else if (i == unknown) {
val i2 = botDefinition.findIntent(intent, applicationId)
if (i2 == unknown) BotDefinition.findIntent(stories, intent) else i2
} else i
}
override fun findStoryDefinition(intent: IntentAware?, applicationId: String): StoryDefinition {
return findStoryDefinition(intent?.wrappedIntent()?.name, applicationId)
}
private fun findStory(intent: String?, applicationId: String): StoryDefinition =
BotDefinition.findStoryDefinition(
stories
.asSequence()
.filter {
when (it) {
is ConfiguredStoryDefinition -> !it.isDisabled(applicationId)
else -> true
}
}
.map { it.checkApplicationId(applicationId) }
.toList(),
intent,
unknownStory,
keywordStory,
ragExcludedStory,
ragStory,
botDefinition.ragConfiguration
)
internal fun builtInStory(storyId: String): StoryDefinition =
builtInStoriesMap[storyId] ?: returnsUnknownStory(storyId)
private fun returnsUnknownStory(storyId: String): StoryDefinition =
unknownStory.also {
logger.warn { "unknown story: $storyId" }
}
private fun findStoryDefinition(intent: String?, applicationId: String, initialIntent: String?): StoryDefinition {
val story = findStory(intent, applicationId)
return (story as? ConfiguredStoryDefinition)?.let {
val switchId = it.findEnabledStorySwitchId(applicationId)
if (switchId != null) {
(configuredStories[switchId] ?: listOfNotNull(builtInStoriesMap[switchId]))
.let { stories ->
val targetStory = stories
.asSequence()
.filterIsInstance()
.filterNot { c -> c.isDisabled(applicationId) }
.run {
firstOrNull { c -> c.answerType != builtin } ?: firstOrNull()
}
?: stories.firstOrNull { c -> c !is ConfiguredStoryDefinition }
targetStory
?.let { toStory ->
val storyMainIntent = toStory.mainIntent().name
if (storyMainIntent == initialIntent) {
toStory.checkApplicationId(applicationId)
} else {
findStoryDefinition(storyMainIntent, applicationId, initialIntent)
}
}
}
?: story
} else {
it
}
} ?: story
}
override fun findStoryDefinition(intent: String?, applicationId: String): StoryDefinition =
findStoryDefinition(intent, applicationId, intent).let {
if (it is ConfiguredStoryDefinition && it.answerType == builtin) {
builtInStory(it.storyId)
} else {
it
}
}
override fun findStoryDefinitionById(storyId: String, applicationId: String): StoryDefinition =
// first search into built-in then in configured, fallback to search by intent
builtInStoriesMap[storyId] ?: allStoriesById[storyId]?.checkApplicationId(applicationId)
?: findStoryDefinition(
storyId,
applicationId
)
override fun findStoryByStoryHandler(storyHandler: StoryHandler, applicationId: String): StoryDefinition? =
(
botDefinition.stories.find { it.storyHandler == storyHandler }
?: stories.find { it.storyHandler == storyHandler }
)
?.checkApplicationId(applicationId)
private fun StoryDefinition.checkApplicationId(applicationId: String): StoryDefinition =
if (this is ConfiguredStoryDefinition &&
configuration.configuredSteps.isNotEmpty() &&
answerType != builtin
) {
ConfiguredStoryDefinition(
this@BotDefinitionWrapper,
configuration,
BotApplicationConfigurationKey(applicationId, this@BotDefinitionWrapper)
)
} else {
this
}
override fun toString(): String {
return "Wrapper($botDefinition)"
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy