Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
c118570
resolves #1907 bot_engine: implement coroutine-based stories
Fabilin Aug 13, 2025
c457bf7
clean up new methods
Fabilin Aug 13, 2025
2063132
add test for executor-coroutine bridge
Fabilin Aug 18, 2025
36fd718
add optional parameters to Executor.launchCoroutine
Fabilin Aug 18, 2025
792bb72
add async story steps
Fabilin Aug 19, 2025
4ecea06
rename UserLock#lock
Fabilin Aug 19, 2025
448853c
Create parallel class hierarchy for coroutine stories
Fabilin Sep 1, 2025
dd180b6
add I18nLabelValue.withArgs(*args) helper method
Fabilin Sep 1, 2025
bf49fca
add deadlock safeguard in handleAndSwitchStory
Fabilin Sep 1, 2025
a55e532
add synchronization to AsyncBus methods
Fabilin Sep 2, 2025
2f66ac7
add a warning to handleAndSwitchStory
Fabilin Sep 2, 2025
e34f79c
remove unnecessary annotation
Fabilin Sep 2, 2025
2f54e2e
add BotBus.stepDef for raw step access
Fabilin Sep 3, 2025
88d0990
shorter definition builders' handling creator parameter name
Fabilin Sep 3, 2025
41e90fb
fix DefaultConnectorHandlerProvider failing on parameterized classes
Fabilin Sep 3, 2025
4e3aef7
fix leftover types
Fabilin Sep 3, 2025
4438bce
fix DialogEntityManager.entityValue
Fabilin Sep 3, 2025
caca28a
simplify some implementations
Fabilin Sep 12, 2025
570fd81
rename a parameter
Fabilin Sep 12, 2025
203d2b3
remove extra synchronized accessors
Fabilin Sep 15, 2025
d73e0ac
Merge branch 'master' into 1907-story-coroutines
Fabilin Sep 15, 2025
a539ba6
add documentation for coroutine-based API
Fabilin Sep 22, 2025
59d290d
remove unused interface
Fabilin Sep 22, 2025
ad890d6
update doc references to DialogEntityAccess
Fabilin Sep 22, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion bot/api/service/src/main/kotlin/BotApiHandler.kt
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import ai.tock.bot.connector.media.MediaCard
import ai.tock.bot.connector.media.MediaCarousel
import ai.tock.bot.connector.media.MediaFile
import ai.tock.bot.definition.StoryDefinition
import ai.tock.bot.definition.StoryStep
import ai.tock.bot.engine.BotBus
import ai.tock.bot.engine.action.Action
import ai.tock.bot.engine.action.SendAttachment.AttachmentType
Expand Down Expand Up @@ -134,7 +135,7 @@ internal class BotApiHandler(
}
// set step
if (response.step != null) {
step = story.definition.allSteps().find { it.name == response.step }
step = story.definition.allSteps().find { it.name == response.step } as? StoryStep<*>
}

//Handle current story and switch to ending story
Expand Down
10 changes: 5 additions & 5 deletions bot/api/service/src/test/kotlin/BotApiHandlerTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,17 @@ import ai.tock.bot.api.model.websocket.ResponseData
import ai.tock.bot.api.service.BotApiClientController
import ai.tock.bot.api.service.BotApiDefinitionProvider
import ai.tock.bot.api.service.BotApiHandler
import ai.tock.bot.definition.BotDefinition
import ai.tock.bot.api.service.toUserRequest
import ai.tock.bot.definition.BotDefinition
import ai.tock.bot.definition.StoryDefinition
import ai.tock.bot.engine.BotBus
import ai.tock.bot.engine.action.ActionMetadata
import ai.tock.bot.engine.action.SendSentence
import ai.tock.bot.engine.dialog.Dialog
import ai.tock.bot.engine.dialog.DialogState
import ai.tock.bot.engine.dialog.NextUserActionState
import ai.tock.bot.engine.user.UserTimelineDAO
import ai.tock.bot.engine.user.PlayerId
import ai.tock.bot.engine.user.UserTimelineDAO
import ai.tock.nlp.api.client.model.NlpIntentQualifier
import ai.tock.shared.tockInternalInjector
import com.github.salomonbrys.kodein.Kodein
Expand All @@ -47,11 +47,11 @@ import io.mockk.justRun
import io.mockk.mockk
import io.mockk.slot
import io.mockk.verify
import kotlin.test.assertEquals
import kotlin.test.assertTrue
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue


class BotApiHandlerTest {
Expand Down Expand Up @@ -140,7 +140,7 @@ class BotApiHandlerTest {
handler.send(bus)

verify { bus.setBusContextValue("_viewed_stories_tock_switch", any()) }
verify { bus.handleAndSwitchStory(any(), any()) }
verify { bus.handleAndSwitchStory(any<StoryDefinition>(), any()) }
}

@Test
Expand Down
3 changes: 2 additions & 1 deletion bot/connector-alcmeon/src/main/kotlin/AlcmeonHandler.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package ai.tock.bot.connector.alcmeon

import ai.tock.bot.connector.ConnectorHandler
import ai.tock.bot.definition.ConnectorSpecificHandling
import ai.tock.bot.definition.ConnectorStoryHandler
import ai.tock.bot.definition.StoryHandlerDefinition
import kotlin.reflect.KClass
Expand All @@ -28,4 +29,4 @@ import kotlin.reflect.KClass
@ConnectorHandler(connectorTypeId = ALCMEON_CONNECTOR_TYPE_ID)
@Target(AnnotationTarget.CLASS)
@MustBeDocumented
annotation class AlcmeonHandler(val value: KClass<out ConnectorStoryHandler<*>>)
annotation class AlcmeonHandler(val value: KClass<out ConnectorSpecificHandling>)
7 changes: 5 additions & 2 deletions bot/connector-alexa/src/main/kotlin/AlexaHandler.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,18 @@
package ai.tock.bot.connector.alexa

import ai.tock.bot.connector.ConnectorHandler
import ai.tock.bot.definition.AsyncStoryHandling
import ai.tock.bot.definition.ConnectorSpecificHandling
import ai.tock.bot.definition.ConnectorStoryHandler
import ai.tock.bot.definition.StoryHandlerDefinitionBase
import kotlin.reflect.KClass

/**
* To specify [ConnectorStoryHandler] for Alexa connector.
* [KClass] passed as [value] of this annotation must have a primary constructor
* with a single not optional [StoryHandlerDefinitionBase] argument.
* with a single not optional [StoryHandlerDefinitionBase] or [AsyncStoryHandling] argument.
*/
@ConnectorHandler(connectorTypeId = ALEXA_CONNECTOR_TYPE_ID)
@Target(AnnotationTarget.CLASS)
@MustBeDocumented
annotation class AlexaHandler(val value: KClass<out ConnectorStoryHandler<*>>)
annotation class AlexaHandler(val value: KClass<out ConnectorSpecificHandling>)
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,18 @@
package ai.tock.bot.connector.businesschat

import ai.tock.bot.connector.ConnectorHandler
import ai.tock.bot.definition.AsyncStoryHandling
import ai.tock.bot.definition.ConnectorSpecificHandling
import ai.tock.bot.definition.ConnectorStoryHandler
import kotlin.annotation.AnnotationTarget
import kotlin.annotation.MustBeDocumented
import kotlin.annotation.Target
import ai.tock.bot.definition.StoryHandlerDefinitionBase
import kotlin.reflect.KClass

/**
* To specify [ConnectorStoryHandler] for BusinessChat connector.
* [KClass] passed as [value] of this annotation must have a primary constructor
* with a single not optional [StoryHandlerDefinitionBase] argument.
* with a single not optional [StoryHandlerDefinitionBase] or [AsyncStoryHandling] argument.
*/
@ConnectorHandler(connectorTypeId = BUSINESS_CHAT_CONNECTOR_TYPE_ID)
@Target(AnnotationTarget.CLASS)
@MustBeDocumented
annotation class BusinessChatHandler(val value: KClass<out ConnectorStoryHandler<*>>)
annotation class BusinessChatHandler(val value: KClass<out ConnectorSpecificHandling>)
5 changes: 2 additions & 3 deletions bot/connector-ga/src/main/kotlin/GABuilders.kt
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,7 @@ import ai.tock.bot.connector.ga.model.response.GASimpleSelect
import ai.tock.bot.connector.ga.model.response.GAStructuredResponse
import ai.tock.bot.connector.ga.model.response.GAUpdatePermissionValueSpec
import ai.tock.bot.definition.IntentAware
import ai.tock.bot.definition.StoryHandlerDefinition
import ai.tock.bot.definition.StoryStep
import ai.tock.bot.definition.StoryStepDef
import ai.tock.bot.engine.Bus
import ai.tock.bot.engine.I18nTranslator
import ai.tock.bot.engine.action.SendChoice
Expand Down Expand Up @@ -305,7 +304,7 @@ fun I18nTranslator.gaButton(title: CharSequence, url: String): GAButton {
fun <T : Bus<T>> T.optionInfo(
title: CharSequence,
targetIntent: IntentAware,
step: StoryStep<out StoryHandlerDefinition>? = null,
step: StoryStepDef? = null,
vararg parameters: Pair<String, String>
): GAOptionInfo {
val t = translate(title)
Expand Down
6 changes: 3 additions & 3 deletions bot/connector-ga/src/main/kotlin/GACarouselBuilders.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ import ai.tock.bot.connector.ga.model.response.GAExpectedIntent
import ai.tock.bot.connector.ga.model.response.GAImage
import ai.tock.bot.definition.IntentAware
import ai.tock.bot.definition.Parameters
import ai.tock.bot.definition.StoryHandlerDefinition
import ai.tock.bot.definition.StoryStep
import ai.tock.bot.definition.StoryStepDef
import ai.tock.bot.engine.Bus
import ai.tock.bot.engine.I18nTranslator
import ai.tock.translator.raw
Expand Down Expand Up @@ -163,7 +163,7 @@ fun <T : Bus<T>> T.carouselItem(
*/
fun <T : Bus<T>> T.carouselItem(
targetIntent: IntentAware,
step: StoryStep<out StoryHandlerDefinition>?,
step: StoryStepDef?,
title: CharSequence,
description: CharSequence? = null,
image: GAImage? = null,
Expand All @@ -176,7 +176,7 @@ fun <T : Bus<T>> T.carouselItem(
*/
fun <T : Bus<T>> T.carouselItem(
targetIntent: IntentAware,
step: StoryStep<out StoryHandlerDefinition>?,
step: StoryStepDef?,
title: CharSequence,
description: CharSequence? = null,
image: GAImage? = null,
Expand Down
3 changes: 2 additions & 1 deletion bot/connector-ga/src/main/kotlin/GAHandler.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package ai.tock.bot.connector.ga

import ai.tock.bot.connector.ConnectorHandler
import ai.tock.bot.definition.ConnectorSpecificHandling
import ai.tock.bot.definition.ConnectorStoryHandler
import kotlin.reflect.KClass

Expand All @@ -28,4 +29,4 @@ import kotlin.reflect.KClass
@ConnectorHandler(connectorTypeId = GA_CONNECTOR_TYPE_ID)
@Target(AnnotationTarget.CLASS)
@MustBeDocumented
annotation class GAHandler(val value: KClass<out ConnectorStoryHandler<*>>)
annotation class GAHandler(val value: KClass<out ConnectorSpecificHandling>)
10 changes: 5 additions & 5 deletions bot/connector-ga/src/main/kotlin/GAListBuilders.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ import ai.tock.bot.connector.ga.model.response.GARichResponse
import ai.tock.bot.connector.ga.model.response.GASuggestion
import ai.tock.bot.definition.IntentAware
import ai.tock.bot.definition.Parameters
import ai.tock.bot.definition.StoryHandlerDefinition
import ai.tock.bot.definition.StoryStep
import ai.tock.bot.definition.StoryStepDef
import ai.tock.bot.engine.Bus
import ai.tock.bot.engine.I18nTranslator
import ai.tock.translator.raw
Expand Down Expand Up @@ -169,7 +169,7 @@ fun <T : Bus<T>> T.listItem(
fun <T : Bus<T>> T.listItem(
title: CharSequence,
targetIntent: IntentAware,
step: StoryStep<out StoryHandlerDefinition>?,
step: StoryStepDef?,
parameters: Parameters
): GAListItem = listItem(title, targetIntent, step, null, null, parameters)

Expand All @@ -179,7 +179,7 @@ fun <T : Bus<T>> T.listItem(
fun <T : Bus<T>> T.listItem(
title: CharSequence,
targetIntent: IntentAware,
step: StoryStep<out StoryHandlerDefinition>?,
step: StoryStepDef?,
vararg parameters: Pair<String, String>
): GAListItem = listItem<T>(title, targetIntent, step, null, null, *parameters)

Expand All @@ -189,7 +189,7 @@ fun <T : Bus<T>> T.listItem(
fun <T : Bus<T>> T.listItem(
title: CharSequence,
targetIntent: IntentAware,
step: StoryStep<out StoryHandlerDefinition>?,
step: StoryStepDef?,
description: CharSequence? = null,
imageUrl: String? = null,
parameters: Parameters
Expand All @@ -201,7 +201,7 @@ fun <T : Bus<T>> T.listItem(
fun <T : Bus<T>> T.listItem(
title: CharSequence,
targetIntent: IntentAware,
step: StoryStep<out StoryHandlerDefinition>?,
step: StoryStepDef?,
description: CharSequence? = null,
imageUrl: String? = null,
vararg parameters: Pair<String, String>
Expand Down
6 changes: 3 additions & 3 deletions bot/connector-ga/src/main/kotlin/GASelectBuilders.kt
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ import ai.tock.bot.connector.ga.model.response.GASelectItem
import ai.tock.bot.connector.ga.model.response.GASimpleSelect
import ai.tock.bot.definition.IntentAware
import ai.tock.bot.definition.Parameters
import ai.tock.bot.definition.StoryHandlerDefinition
import ai.tock.bot.definition.StoryStep
import ai.tock.bot.definition.StoryStepDef
import ai.tock.bot.engine.Bus
import ai.tock.bot.engine.I18nTranslator

Expand Down Expand Up @@ -64,7 +64,7 @@ fun <T : Bus<T>> T.selectItem(
fun <T : Bus<T>> T.selectItem(
title: CharSequence,
targetIntent: IntentAware,
step: StoryStep<out StoryHandlerDefinition>,
step: StoryStepDef,
optionTitle: CharSequence? = null,
parameters: Parameters
): GASelectItem = selectItem(title, targetIntent, step, optionTitle, *parameters.toArray())
Expand All @@ -75,7 +75,7 @@ fun <T : Bus<T>> T.selectItem(
fun <T : Bus<T>> T.selectItem(
title: CharSequence,
targetIntent: IntentAware,
step: StoryStep<out StoryHandlerDefinition>? = null,
step: StoryStepDef? = null,
optionTitle: CharSequence? = null,
vararg parameters: Pair<String, String>
): GASelectItem {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package ai.tock.bot.connector.googlechat

import ai.tock.bot.connector.ConnectorHandler
import ai.tock.bot.connector.googlechat.builder.GOOGLE_CHAT_CONNECTOR_TYPE_ID
import ai.tock.bot.definition.ConnectorSpecificHandling
import ai.tock.bot.definition.ConnectorStoryHandler
import ai.tock.bot.definition.StoryHandlerDefinition
import kotlin.reflect.KClass
Expand All @@ -29,4 +30,4 @@ import kotlin.reflect.KClass
@ConnectorHandler(connectorTypeId = GOOGLE_CHAT_CONNECTOR_TYPE_ID)
@Target(AnnotationTarget.CLASS)
@MustBeDocumented
annotation class GoogleChatHandler(val value: KClass<out ConnectorStoryHandler<*>>)
annotation class GoogleChatHandler(val value: KClass<out ConnectorSpecificHandling>)
23 changes: 11 additions & 12 deletions bot/connector-messenger/src/main/kotlin/MessengerBuilders.kt
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,7 @@ import ai.tock.bot.connector.messenger.model.send.UserAction.Companion.extractQu
import ai.tock.bot.definition.Intent
import ai.tock.bot.definition.IntentAware
import ai.tock.bot.definition.Parameters
import ai.tock.bot.definition.StoryHandlerDefinition
import ai.tock.bot.definition.StoryStep
import ai.tock.bot.definition.StoryStepDef
import ai.tock.bot.engine.Bus
import ai.tock.bot.engine.I18nTranslator
import ai.tock.bot.engine.action.ActionMetadata
Expand Down Expand Up @@ -373,15 +372,15 @@ fun I18nTranslator.standaloneQuickReply(
/**
* The target step.
*/
step: StoryStep<out StoryHandlerDefinition>? = null,
step: StoryStepDef? = null,
/**
* The image url of the quick reply.
*/
imageUrl: String? = null,
/**
* The current step of the Bus<T>.
*/
busStep: StoryStep<out StoryHandlerDefinition>? = null,
busStep: StoryStepDef? = null,
/**
* The current intent of the Bus<T>.
*/
Expand Down Expand Up @@ -412,7 +411,7 @@ fun <T : Bus<T>> T.quickReply(
title: CharSequence,
targetIntent: IntentAware,
imageUrl: String? = null,
step: StoryStep<out StoryHandlerDefinition>? = null,
step: StoryStepDef? = null,
parameters: Parameters
): QuickReply =
quickReply(title, targetIntent, imageUrl, step, parameters.toMap())
Expand All @@ -424,7 +423,7 @@ fun <T : Bus<T>> T.quickReply(
title: CharSequence,
targetIntent: IntentAware,
imageUrl: String? = null,
step: StoryStep<out StoryHandlerDefinition>? = null,
step: StoryStepDef? = null,
vararg parameters: Pair<String, String>
): QuickReply =
quickReply(title, targetIntent.wrappedIntent(), imageUrl, step, parameters.toMap())
Expand All @@ -436,7 +435,7 @@ fun <T : Bus<T>> T.quickReply(
title: CharSequence,
targetIntent: IntentAware,
imageUrl: String? = null,
step: StoryStep<out StoryHandlerDefinition>? = null,
step: StoryStepDef? = null,
parameters: Collection<Pair<String, String>>
): QuickReply =
quickReply(title, targetIntent, imageUrl, step, parameters.toMap())
Expand All @@ -448,7 +447,7 @@ fun <T : Bus<T>> T.quickReply(
title: CharSequence,
targetIntent: IntentAware,
imageUrl: String? = null,
step: StoryStep<out StoryHandlerDefinition>? = null,
step: StoryStepDef? = null,
parameters: Map<String, String>
): QuickReply =
quickReply(title, targetIntent, imageUrl, step?.name, parameters)
Expand Down Expand Up @@ -515,7 +514,7 @@ fun <T : Bus<T>> T.postbackButton(
fun <T : Bus<T>> T.postbackButton(
title: CharSequence,
targetIntent: IntentAware,
step: StoryStep<out StoryHandlerDefinition>? = null,
step: StoryStepDef? = null,
parameters: Parameters
): PostbackButton =
postbackButton(title, targetIntent, step, *parameters.toArray())
Expand All @@ -526,7 +525,7 @@ fun <T : Bus<T>> T.postbackButton(
fun <T : Bus<T>> T.postbackButton(
title: CharSequence,
targetIntent: IntentAware,
step: StoryStep<out StoryHandlerDefinition>? = null,
step: StoryStepDef? = null,
vararg parameters: Pair<String, String>
): PostbackButton =
postbackButton(
Expand All @@ -541,9 +540,9 @@ fun <T : Bus<T>> T.postbackButton(
private fun I18nTranslator.postbackButton(
title: CharSequence,
targetIntent: IntentAware,
step: StoryStep<out StoryHandlerDefinition>? = null,
step: StoryStepDef? = null,
parameters: Map<String, String>,
payloadEncoder: (IntentAware, StoryStep<out StoryHandlerDefinition>?, Map<String, String>) -> String
payloadEncoder: (IntentAware, StoryStepDef?, Map<String, String>) -> String
): PostbackButton {
val t = translate(title)
if (t.length > 20) {
Expand Down
Loading
Loading