Skip to content

Commit 22359bb

Browse files
authored
fixes #1972 [Bot Engine]: fix story listener (#1974)
1 parent 792689f commit 22359bb

File tree

7 files changed

+111
-72
lines changed

7 files changed

+111
-72
lines changed

bot/engine/src/main/kotlin/definition/AsyncStoryHandlerBase.kt

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,15 @@ package ai.tock.bot.definition
1818

1919
import ai.tock.bot.engine.AsyncBotBus
2020
import ai.tock.bot.engine.AsyncBus
21+
import ai.tock.bot.engine.Bot
22+
import ai.tock.bot.engine.BotBus
2123
import ai.tock.shared.InternalTockApi
2224
import ai.tock.shared.coroutines.ExperimentalTockCoroutines
2325
import ai.tock.shared.defaultNamespace
2426
import ai.tock.translator.I18nKeyProvider.Companion.generateKey
2527
import ai.tock.translator.I18nLabelValue
2628
import ai.tock.translator.I18nLocalizedLabel
29+
import kotlinx.coroutines.runBlocking
2730
import mu.KotlinLogging
2831

2932
/**
@@ -41,8 +44,15 @@ abstract class AsyncStoryHandlerBase(
4144
override var i18nNamespace: String = defaultNamespace
4245
@InternalTockApi set
4346

47+
@Deprecated("Use coroutines to call this interface", replaceWith = ReplaceWith("handle(asyncBus)"))
48+
override fun handle(bus: BotBus) {
49+
runBlocking(
50+
Bot.handlerCoroutineName { findStoryDefinition(bus)?.id ?: mainIntent?.name ?: "??" },
51+
) { handle(AsyncBotBus(bus)) }
52+
}
53+
4454
override suspend fun handle(bus: AsyncBus) {
45-
val baseBus = (bus as AsyncBotBus).botBus
55+
val baseBus = (bus as AsyncBotBus).syncBus
4656
val storyDefinition = findStoryDefinition(bus)
4757
// if not supported user interface, use unknown
4858
if (storyDefinition?.unsupportedUserInterfaces?.contains(bus.userInterfaceType) == true) {
@@ -63,12 +73,18 @@ abstract class AsyncStoryHandlerBase(
6373

6474
protected abstract suspend fun action(bus: AsyncBus)
6575

66-
protected fun AsyncBus.isEndCalled() = StoryHandlerBase.isEndCalled((this as AsyncBotBus).botBus)
76+
protected fun AsyncBus.isEndCalled() = StoryHandlerBase.isEndCalled((this as AsyncBotBus).syncBus)
6777

6878
/**
6979
* Finds the story definition of this handler.
7080
*/
71-
open fun findStoryDefinition(bus: AsyncBus): StoryDefinition? = (bus as AsyncBotBus).botBus.botDefinition.findStoryByStoryHandler(this, bus.connectorId)
81+
open fun findStoryDefinition(bus: AsyncBus): StoryDefinition? {
82+
return findStoryDefinition((bus as AsyncBotBus).syncBus)
83+
}
84+
85+
private fun findStoryDefinition(bus: BotBus): StoryDefinition? {
86+
return bus.botDefinition.findStoryByStoryHandler(this, bus.connectorId)
87+
}
7288

7389
/**
7490
* Story i18n category.

bot/engine/src/main/kotlin/definition/definition/AsyncStoryHandlerListener.kt renamed to bot/engine/src/main/kotlin/definition/AsyncStoryHandlerListener.kt

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,8 @@
1414
* limitations under the License.
1515
*/
1616

17-
package ai.tock.bot.definition.definition
17+
package ai.tock.bot.definition
1818

19-
import ai.tock.bot.definition.StoryHandler
20-
import ai.tock.bot.definition.StoryHandlerListener
2119
import ai.tock.bot.engine.AsyncBotBus
2220
import ai.tock.bot.engine.AsyncBus
2321
import ai.tock.bot.engine.BotBus

bot/engine/src/main/kotlin/engine/AsyncBotBus.kt

Lines changed: 55 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ import kotlin.coroutines.CoroutineContext
5252
import kotlin.reflect.safeCast
5353

5454
@ExperimentalTockCoroutines
55-
open class AsyncBotBus(val botBus: BotBus) : AsyncBus {
55+
open class AsyncBotBus(val syncBus: BotBus) : AsyncBus {
5656
companion object {
5757
/**
5858
* Helper method to retrieve the current bus,
@@ -61,25 +61,28 @@ open class AsyncBotBus(val botBus: BotBus) : AsyncBus {
6161
suspend fun retrieveCurrentBus(): AsyncBotBus? = currentCoroutineContext()[Ref]?.bus
6262
}
6363

64+
@Deprecated("Use syncBus instead", ReplaceWith("syncBus"))
65+
val botBus get() = syncBus
66+
6467
private val executor: Executor get() = injector.provide()
6568
private val featureDao: FeatureDAO get() = injector.provide()
6669

6770
override val connectorId: String
68-
get() = botBus.connectorId
71+
get() = syncBus.connectorId
6972
override val targetConnectorType: ConnectorType
70-
get() = botBus.targetConnectorType
73+
get() = syncBus.targetConnectorType
7174
override val botId: PlayerId
72-
get() = botBus.botId
75+
get() = syncBus.botId
7376
override val userId: PlayerId
74-
get() = botBus.userId
77+
get() = syncBus.userId
7578
override val userLocale: Locale
76-
get() = botBus.userLocale
79+
get() = syncBus.userLocale
7780
override val userInterfaceType: UserInterfaceType
78-
get() = botBus.userInterfaceType
81+
get() = syncBus.userInterfaceType
7982
override val intent: IntentAware?
80-
get() = botBus.intent
83+
get() = syncBus.intent
8184
override val currentIntent: IntentAware?
82-
get() = botBus.currentIntent
85+
get() = syncBus.currentIntent
8386
override val currentStoryDefinition: StoryDefinition
8487
get() = story.definition
8588
override var step: AsyncStoryStep<*>?
@@ -88,90 +91,90 @@ open class AsyncBotBus(val botBus: BotBus) : AsyncBus {
8891
story.step = step?.name
8992
}
9093
override val userInfo: UserPreferences
91-
get() = botBus.userPreferences
94+
get() = syncBus.userPreferences
9295
override val userState: UserState
93-
get() = botBus.userTimeline.userState
94-
val story: Story get() = botBus.story
96+
get() = syncBus.userTimeline.userState
97+
val story: Story get() = syncBus.story
9598

96-
override fun defaultAnswerDelay() = botBus.defaultDelay(botBus.currentAnswerIndex)
99+
override fun defaultAnswerDelay() = syncBus.defaultDelay(syncBus.currentAnswerIndex)
97100

98101
override suspend fun constrainNlp(nextActionState: NextUserActionState) {
99-
botBus.nextUserActionState = nextActionState
102+
syncBus.nextUserActionState = nextActionState
100103
}
101104

102105
override fun choice(key: ParameterKey): String? {
103-
return botBus.choice(key)
106+
return syncBus.choice(key)
104107
}
105108

106109
override fun booleanChoice(key: ParameterKey): Boolean {
107-
return botBus.booleanChoice(key)
110+
return syncBus.booleanChoice(key)
108111
}
109112

110113
override fun hasActionEntity(role: String): Boolean {
111-
return botBus.hasActionEntity(role)
114+
return syncBus.hasActionEntity(role)
112115
}
113116

114117
override fun <T : Value> entityValue(
115118
role: String,
116119
valueTransformer: (EntityValue) -> T?,
117120
): T? {
118-
return synchronized(botBus) { botBus.entityValue(role, valueTransformer) }
121+
return synchronized(syncBus) { syncBus.entityValue(role, valueTransformer) }
119122
}
120123

121124
override fun entityValueDetails(role: String): EntityValue? {
122-
return synchronized(botBus) { botBus.entityValueDetails(role) }
125+
return synchronized(syncBus) { syncBus.entityValueDetails(role) }
123126
}
124127

125128
override fun changeEntityValue(
126129
role: String,
127130
newValue: EntityValue?,
128131
) {
129-
synchronized(botBus) { botBus.changeEntityValue(role, newValue) }
132+
synchronized(syncBus) { syncBus.changeEntityValue(role, newValue) }
130133
}
131134

132135
override fun changeEntityValue(
133136
entity: Entity,
134137
newValue: Value?,
135138
) {
136-
return synchronized(botBus) { botBus.changeEntityValue(entity, newValue) }
139+
return synchronized(syncBus) { syncBus.changeEntityValue(entity, newValue) }
137140
}
138141

139142
override fun removeAllEntityValues() {
140143
// Synchronized to avoid ConcurrentModificationException with other entity setters
141-
synchronized(botBus) {
142-
botBus.removeAllEntityValues()
144+
synchronized(syncBus) {
145+
syncBus.removeAllEntityValues()
143146
}
144147
}
145148

146149
override fun <T : Any> getContextValue(key: DialogContextKey<T>): T? {
147-
return botBus.dialog.state.context[key.name]?.let(key.type::safeCast)
150+
return syncBus.dialog.state.context[key.name]?.let(key.type::safeCast)
148151
}
149152

150153
override fun <T : Any> setContextValue(
151154
key: DialogContextKey<T>,
152155
value: T?,
153156
) {
154-
botBus.dialog.state.setContextValue(key, value)
157+
syncBus.dialog.state.setContextValue(key, value)
155158
}
156159

157160
override fun <T : Any> setBusContextValue(
158161
key: DialogContextKey<T>,
159162
value: T?,
160163
) {
161-
botBus.setBusContextValue(key.name, value)
164+
syncBus.setBusContextValue(key.name, value)
162165
}
163166

164167
override fun <T : Any> getBusContextValue(key: DialogContextKey<T>): T? {
165-
return botBus.getBusContextValue<Any?>(key.name)?.let(key.type::safeCast)
168+
return syncBus.getBusContextValue<Any?>(key.name)?.let(key.type::safeCast)
166169
}
167170

168171
override suspend fun isFeatureEnabled(
169172
feature: FeatureType,
170173
default: Boolean,
171174
): Boolean =
172175
featureDao.isEnabled(
173-
botBus.botDefinition.botId,
174-
botBus.botDefinition.namespace,
176+
syncBus.botDefinition.botId,
177+
syncBus.botDefinition.namespace,
175178
feature,
176179
connectorId,
177180
default,
@@ -183,21 +186,21 @@ open class AsyncBotBus(val botBus: BotBus) : AsyncBus {
183186
starterIntent: Intent,
184187
step: StoryStepDef?,
185188
) {
186-
synchronized(botBus) {
187-
botBus.stepDef = step
188-
botBus.switchStory(storyDefinition, starterIntent)
189-
botBus.hasCurrentSwitchStoryProcess = false
189+
synchronized(syncBus) {
190+
syncBus.stepDef = step
191+
syncBus.switchStory(storyDefinition, starterIntent)
192+
syncBus.hasCurrentSwitchStoryProcess = false
190193
}
191194
(storyDefinition.storyHandler as? AsyncStoryHandler)?.handle(this)
192-
?: storyDefinition.storyHandler.handle(botBus)
195+
?: storyDefinition.storyHandler.handle(syncBus)
193196
}
194197

195198
override fun i18nWithKey(
196199
key: String,
197200
defaultLabel: String,
198201
vararg args: Any?,
199202
): I18nLabelValue {
200-
return botBus.i18nKey(key, defaultLabel, *args)
203+
return syncBus.i18nKey(key, defaultLabel, *args)
201204
}
202205

203206
override fun i18nWithKey(
@@ -206,23 +209,23 @@ open class AsyncBotBus(val botBus: BotBus) : AsyncBus {
206209
defaultI18n: Set<I18nLocalizedLabel>,
207210
vararg args: Any?,
208211
): I18nLabelValue {
209-
return botBus.i18nKey(key, defaultLabel, defaultI18n, *args)
212+
return syncBus.i18nKey(key, defaultLabel, defaultI18n, *args)
210213
}
211214

212215
override fun i18n(
213216
defaultLabel: CharSequence,
214217
args: List<Any?>,
215218
): I18nLabelValue {
216-
return botBus.i18n(defaultLabel, args)
219+
return syncBus.i18n(defaultLabel, args)
217220
}
218221

219222
override suspend fun send(
220223
i18nText: CharSequence,
221224
delay: Long,
222225
) {
223226
withContext(executor.asCoroutineDispatcher()) {
224-
synchronized(botBus) {
225-
botBus.send(i18nText, delay = delay)
227+
synchronized(syncBus) {
228+
syncBus.send(i18nText, delay = delay)
226229
}
227230
}
228231
}
@@ -232,8 +235,8 @@ open class AsyncBotBus(val botBus: BotBus) : AsyncBus {
232235
vararg i18nArgs: Any?,
233236
) {
234237
withContext(executor.asCoroutineDispatcher()) {
235-
synchronized(botBus) {
236-
botBus.send(i18nText, *i18nArgs)
238+
synchronized(syncBus) {
239+
syncBus.send(i18nText, *i18nArgs)
237240
}
238241
}
239242
}
@@ -243,8 +246,8 @@ open class AsyncBotBus(val botBus: BotBus) : AsyncBus {
243246
delay: Long,
244247
) {
245248
withContext(executor.asCoroutineDispatcher()) {
246-
synchronized(botBus) {
247-
botBus.end(i18nText, delay = delay)
249+
synchronized(syncBus) {
250+
syncBus.end(i18nText, delay = delay)
248251
}
249252
}
250253
}
@@ -254,8 +257,8 @@ open class AsyncBotBus(val botBus: BotBus) : AsyncBus {
254257
vararg i18nArgs: Any?,
255258
) {
256259
withContext(executor.asCoroutineDispatcher()) {
257-
synchronized(botBus) {
258-
botBus.end(i18nText, *i18nArgs)
260+
synchronized(syncBus) {
261+
syncBus.end(i18nText, *i18nArgs)
259262
}
260263
}
261264
}
@@ -265,11 +268,11 @@ open class AsyncBotBus(val botBus: BotBus) : AsyncBus {
265268
messageProvider: Bus<*>.() -> Any?,
266269
) {
267270
val messages = toMessageList(messageProvider)
268-
synchronized(botBus) {
271+
synchronized(syncBus) {
269272
if (messages.messages.isEmpty()) {
270-
botBus.send(delay)
273+
syncBus.send(delay)
271274
} else {
272-
botBus.send(messages, delay)
275+
syncBus.send(messages, delay)
273276
}
274277
}
275278
}
@@ -279,11 +282,11 @@ open class AsyncBotBus(val botBus: BotBus) : AsyncBus {
279282
messageProvider: Bus<*>.() -> Any?,
280283
) {
281284
val messages = toMessageList(messageProvider)
282-
synchronized(botBus) {
285+
synchronized(syncBus) {
283286
if (messages.messages.isEmpty()) {
284-
botBus.end(delay)
287+
syncBus.end(delay)
285288
} else {
286-
botBus.end(messages, delay)
289+
syncBus.end(messages, delay)
287290
}
288291
}
289292
}
@@ -292,7 +295,7 @@ open class AsyncBotBus(val botBus: BotBus) : AsyncBus {
292295
// calls to `translate` are blocking (database and possibly translator API),
293296
// so we ensure they are done in a worker thread
294297
withContext(executor.asCoroutineDispatcher()) {
295-
toMessageList(null, botBus, messageProvider)
298+
toMessageList(null, syncBus, messageProvider)
296299
}
297300

298301
data class Ref(val bus: AsyncBotBus) : CoroutineContext.Element {

bot/engine/src/main/kotlin/engine/Bot.kt

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,15 @@ import ai.tock.bot.engine.feature.DefaultFeatureType
3434
import ai.tock.bot.engine.nlp.NlpController
3535
import ai.tock.bot.engine.user.UserTimeline
3636
import ai.tock.shared.coroutines.ExperimentalTockCoroutines
37+
import ai.tock.shared.devEnvironment
3738
import ai.tock.shared.injector
3839
import com.github.salomonbrys.kodein.instance
40+
import kotlinx.coroutines.CoroutineName
3941
import kotlinx.coroutines.asContextElement
4042
import kotlinx.coroutines.withContext
4143
import mu.KotlinLogging
4244
import java.util.Locale
45+
import kotlin.coroutines.EmptyCoroutineContext
4346

4447
/**
4548
*
@@ -54,6 +57,13 @@ internal class Bot(botDefinitionBase: BotDefinition, val configuration: BotAppli
5457
* (warning: advanced usage only).
5558
*/
5659
internal fun retrieveCurrentBus(): BotBus? = currentBus.get()
60+
61+
internal inline fun handlerCoroutineName(storyId: () -> String) =
62+
if (devEnvironment) {
63+
CoroutineName("handler(${storyId()})")
64+
} else {
65+
EmptyCoroutineContext
66+
}
5767
}
5868

5969
private val logger = KotlinLogging.logger {}
@@ -127,7 +137,11 @@ internal class Bot(botDefinitionBase: BotDefinition, val configuration: BotAppli
127137
val bus = TockBotBus(connector, userTimeline, dialog, action, connectorData, botDefinition)
128138
val asyncBus = AsyncBotBus(bus)
129139

130-
withContext(AsyncBotBus.Ref(asyncBus) + currentBus.asContextElement(bus)) {
140+
withContext(
141+
AsyncBotBus.Ref(asyncBus) +
142+
currentBus.asContextElement(bus) +
143+
handlerCoroutineName(story.definition::id),
144+
) {
131145
val closeMessageQueue = bus.deferMessageSending(this)
132146

133147
if (asyncBus.isFeatureEnabled(DefaultFeatureType.DISABLE_BOT)) {

0 commit comments

Comments
 (0)