Skip to content
42 changes: 42 additions & 0 deletions bot/admin/server/src/main/kotlin/BotAdminService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,48 @@ object BotAdminService {
}
}

fun saveAnnotation(
dialogId: String,
actionId: String,
annotationDTO: BotAnnotationDTO,
user: String
): BotAnnotation {
return if (dialogReportDAO.annotationExists(dialogId, actionId)) {
updateAnnotation(dialogId, actionId, annotationDTO, user)
} else {
createAnnotationInternal(dialogId, actionId, annotationDTO, user)
}
}

private fun createAnnotationInternal(
dialogId: String,
actionId: String,
annotationDTO: BotAnnotationDTO,
user: String
): BotAnnotation {
val annotation = BotAnnotation(
state = annotationDTO.state,
reason = annotationDTO.reason,
description = annotationDTO.description,
groundTruth = annotationDTO.groundTruth,
events = mutableListOf(),
lastUpdateDate = Instant.now()
)

val event = BotAnnotationEventState(
eventId = newId(),
creationDate = Instant.now(),
lastUpdateDate = Instant.now(),
user = user,
before = null,
after = annotationDTO.state.name
)

annotation.events.add(event)
dialogReportDAO.insertAnnotation(dialogId, actionId, annotation)
return annotation
}

fun addCommentToAnnotation(
dialogId: String,
actionId: String,
Expand Down
26 changes: 24 additions & 2 deletions bot/admin/server/src/main/kotlin/model/DialogsSearchQuery.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,14 @@

package ai.tock.bot.admin.model

import ai.tock.bot.admin.annotation.BotAnnotationReasonType
import ai.tock.bot.admin.annotation.BotAnnotationState
import ai.tock.bot.admin.dialog.DialogReportQuery
import ai.tock.bot.connector.ConnectorType
import ai.tock.bot.engine.dialog.SortDirection
import ai.tock.bot.engine.user.PlayerId
import ai.tock.nlp.admin.model.PaginatedQuery
import java.time.ZonedDateTime

/**
*
Expand All @@ -36,7 +40,16 @@ data class DialogsSearchQuery(
val ratings: Set<Int> = emptySet(),
val applicationId: String?,
val intentsToHide: Set<String> = emptySet(),
val isGenAiRagDialog: Boolean?
val isGenAiRagDialog: Boolean?,
val withAnnotations: Boolean?,
val annotationStates: Set<BotAnnotationState> = emptySet(),
val annotationReasons: Set<BotAnnotationReasonType> = emptySet(),
val annotationSort: SortDirection? = null,
val dialogSort: SortDirection? = null,
val annotationCreationDateFrom: ZonedDateTime? = null,
val annotationCreationDateTo: ZonedDateTime? = null,
val dialogCreationDateFrom: ZonedDateTime? = null,
val dialogCreationDateTo: ZonedDateTime? = null,
) : PaginatedQuery() {

fun toDialogReportQuery(): DialogReportQuery {
Expand All @@ -57,7 +70,16 @@ data class DialogsSearchQuery(
ratings = ratings,
applicationId = applicationId,
intentsToHide = intentsToHide,
isGenAiRagDialog = isGenAiRagDialog
isGenAiRagDialog = isGenAiRagDialog,
withAnnotations = withAnnotations,
annotationStates = annotationStates,
annotationReasons = annotationReasons,
annotationSort = annotationSort,
dialogSort = dialogSort,
annotationCreationDateFrom = annotationCreationDateFrom,
annotationCreationDateTo = annotationCreationDateTo,
dialogCreationDateFrom = dialogCreationDateFrom,
dialogCreationDateTo = dialogCreationDateTo,
)
}
}
4 changes: 2 additions & 2 deletions bot/admin/server/src/main/kotlin/verticle/DialogVerticle.kt
Original file line number Diff line number Diff line change
Expand Up @@ -176,11 +176,11 @@ class DialogVerticle {

// --------------------------------- Annotation Routes ----------------------------------

// CREATE ANNOTATION
// CREATE/UPDATE ANNOTATION
blockingJsonPost(PATH_ANNOTATION, setOf(TockUserRole.botUser)) { context, annotationDTO: BotAnnotationDTO ->
val botId = context.path("botId")
context.checkBotId(botId)
BotAdminService.createAnnotation(
BotAdminService.saveAnnotation(
context.path("dialogId"),
context.path("actionId"),
annotationDTO,
Expand Down
3 changes: 0 additions & 3 deletions bot/engine/src/main/kotlin/admin/annotation/BotAnnotation.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,9 @@
package ai.tock.bot.admin.annotation


import org.litote.kmongo.Id
import org.litote.kmongo.newId
import java.time.Instant

data class BotAnnotation(
val _id: Id<Annotation> = newId(),
var state: BotAnnotationState,
var reason: BotAnnotationReasonType?,
var description: String,
Expand Down
15 changes: 14 additions & 1 deletion bot/engine/src/main/kotlin/admin/dialog/DialogReportQuery.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@

package ai.tock.bot.admin.dialog

import ai.tock.bot.admin.annotation.BotAnnotationReasonType
import ai.tock.bot.admin.annotation.BotAnnotationState
import ai.tock.bot.connector.ConnectorType
import ai.tock.bot.engine.dialog.SortDirection
import ai.tock.bot.engine.user.PlayerId
import java.time.ZonedDateTime
import java.util.Locale
Expand Down Expand Up @@ -60,5 +63,15 @@ data class DialogReportQuery(

val intentsToHide : Set<String> = emptySet(),

val isGenAiRagDialog: Boolean? = null
val isGenAiRagDialog: Boolean? = null,

val withAnnotations: Boolean? = null,
val annotationStates: Set<BotAnnotationState> = emptySet(),
val annotationReasons: Set<BotAnnotationReasonType> = emptySet(),
val annotationSort: SortDirection? = null,
val dialogSort: SortDirection? = null,
val annotationCreationDateFrom: ZonedDateTime? = null,
val annotationCreationDateTo: ZonedDateTime? = null,
val dialogCreationDateFrom: ZonedDateTime? = null,
val dialogCreationDateTo: ZonedDateTime? = null,
)
22 changes: 22 additions & 0 deletions bot/engine/src/main/kotlin/engine/dialog/SortDirection.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* 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.dialog

enum class SortDirection {
ASC,
DESC
}
4 changes: 3 additions & 1 deletion bot/storage-mongo/src/main/kotlin/BotDataRegistry.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package ai.tock.bot.mongo

import ai.tock.bot.admin.annotation.BotAnnotation
import ai.tock.bot.admin.bot.BotApplicationConfiguration
import ai.tock.bot.admin.bot.BotConfiguration
import ai.tock.bot.admin.story.StoryDefinitionConfiguration
Expand Down Expand Up @@ -49,7 +50,8 @@ import org.litote.kmongo.DataRegistry
PlayerId::class,
EventState::class,
ConnectorType::class,
ActionMetadata::class
ActionMetadata::class,
BotAnnotation::class
]
)
@JacksonDataRegistry(
Expand Down
42 changes: 33 additions & 9 deletions bot/storage-mongo/src/main/kotlin/UserTimelineMongoDAO.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,7 @@ import ai.tock.bot.definition.BotDefinition
import ai.tock.bot.definition.StoryDefinition
import ai.tock.bot.engine.action.Action
import ai.tock.bot.engine.action.SendSentence
import ai.tock.bot.engine.dialog.ArchivedEntityValue
import ai.tock.bot.engine.dialog.Dialog
import ai.tock.bot.engine.dialog.EntityStateValue
import ai.tock.bot.engine.dialog.Snapshot
import ai.tock.bot.engine.dialog.*
import ai.tock.bot.engine.nlp.NlpCallStats
import ai.tock.bot.engine.nlp.NlpStats
import ai.tock.bot.engine.user.PlayerId
Expand All @@ -47,6 +44,7 @@ import ai.tock.bot.mongo.DialogTextCol_.Companion.Text
import ai.tock.bot.mongo.MongoBotConfiguration.database
import ai.tock.bot.mongo.NlpStatsCol_.Companion.AppNamespace
import ai.tock.bot.mongo.UserTimelineCol_.Companion.ApplicationIds
import ai.tock.bot.mongo.UserTimelineCol_.Companion.CreationDate
import ai.tock.bot.mongo.UserTimelineCol_.Companion.LastUpdateDate
import ai.tock.bot.mongo.UserTimelineCol_.Companion.LastUserActionDate
import ai.tock.bot.mongo.UserTimelineCol_.Companion.Namespace
Expand All @@ -63,9 +61,6 @@ import com.mongodb.client.model.ReplaceOptions
import mu.KotlinLogging
import org.litote.kmongo.*
import org.litote.kmongo.MongoOperator.*
import kotlinx.coroutines.newSingleThreadContext
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import java.time.Instant
import java.time.Instant.now
import java.time.ZoneOffset
Expand Down Expand Up @@ -597,16 +592,45 @@ internal object UserTimelineMongoDAO : UserTimelineDAO, UserReportDAO, DialogRep
if (query.intentName.isNullOrBlank()) null else Stories.currentIntent.name_ eq query.intentName,
if (query.ratings.isNotEmpty()) DialogCol_.Rating `in` query.ratings.toSet() else null,
if (query.applicationId.isNullOrBlank()) null else DialogCol_.ApplicationIds `in` setOf( query.applicationId),
if (query.isGenAiRagDialog == true) Stories.actions.botMetadata.isGenAiRagAnswer eq true else null
if (query.isGenAiRagDialog == true) Stories.actions.botMetadata.isGenAiRagAnswer eq true else null,
if (query.withAnnotations == true) Stories.actions.annotation.state `in` BotAnnotationState.entries else null,
if (query.annotationStates.isNotEmpty()) Stories.actions.annotation.state `in` query.annotationStates else null,
if (query.annotationReasons.isNotEmpty()) Stories.actions.annotation.reason `in` query.annotationReasons else null,
if (annotationCreationDateFrom == null) null
else Stories.actions.annotation.creationDate gt annotationCreationDateFrom?.toInstant(),
if (annotationCreationDateTo == null) null
else Stories.actions.annotation.creationDate lt annotationCreationDateTo?.toInstant(),
if (dialogCreationDateFrom == null) null
else Stories.actions.date gt dialogCreationDateFrom?.toInstant(),
if (dialogCreationDateTo == null) null
else Stories.actions.date lt dialogCreationDateTo?.toInstant(),
)
logger.debug { "dialog search query: $filter" }
val c = dialogCol.withReadPreference(secondaryPreferred())
val count = c.countDocuments(filter, defaultCountOptions)
return if (count > start) {
val sortBson = when {
annotationSort != null -> {
if (annotationSort == SortDirection.ASC)
ascending(Stories.actions.annotation.lastUpdateDate)
else
descending(Stories.actions.annotation.lastUpdateDate)
}

dialogSort != null -> {
if (dialogSort == SortDirection.ASC)
orderBy(mapOf(Stories.actions.date to true))
else
orderBy(mapOf(Stories.actions.date to false))
}

// If no filter is specified, we keep default filtering
else -> descending(LastUpdateDate)
}
val list = c.find(filter)
.skip(start.toInt())
.limit(size)
.descendingSort(LastUpdateDate)
.sort(sortBson)
.run {
map { it.toDialogReport() }
.toList()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
package ai.tock.bot.admin.annotation

import java.time.Instant
import kotlin.String
import kotlin.Suppress
import kotlin.collections.Collection
import kotlin.collections.List
import kotlin.collections.Map
import kotlin.reflect.KProperty1
import org.litote.kmongo.property.KCollectionPropertyPath
import org.litote.kmongo.property.KCollectionSimplePropertyPath
import org.litote.kmongo.property.KMapPropertyPath
import org.litote.kmongo.property.KPropertyPath

private val __State: KProperty1<BotAnnotation, BotAnnotationState?>
get() = BotAnnotation::state
private val __Reason: KProperty1<BotAnnotation, BotAnnotationReasonType?>
get() = BotAnnotation::reason
private val __Description: KProperty1<BotAnnotation, String?>
get() = BotAnnotation::description
private val __GroundTruth: KProperty1<BotAnnotation, String?>
get() = BotAnnotation::groundTruth
private val __Events: KProperty1<BotAnnotation, List<BotAnnotationEvent>?>
get() = BotAnnotation::events
private val __CreationDate: KProperty1<BotAnnotation, Instant?>
get() = BotAnnotation::creationDate
private val __LastUpdateDate: KProperty1<BotAnnotation, Instant?>
get() = BotAnnotation::lastUpdateDate
class BotAnnotation_<T>(previous: KPropertyPath<T, *>?, property: KProperty1<*, BotAnnotation?>) :
KPropertyPath<T, BotAnnotation?>(previous,property) {
val state: KPropertyPath<T, BotAnnotationState?>
get() = KPropertyPath(this,__State)

val reason: KPropertyPath<T, BotAnnotationReasonType?>
get() = KPropertyPath(this,__Reason)

val description: KPropertyPath<T, String?>
get() = KPropertyPath(this,__Description)

val groundTruth: KPropertyPath<T, String?>
get() = KPropertyPath(this,__GroundTruth)

val events: KCollectionSimplePropertyPath<T, BotAnnotationEvent?>
get() = KCollectionSimplePropertyPath(this,BotAnnotation::events)

val creationDate: KPropertyPath<T, Instant?>
get() = KPropertyPath(this,__CreationDate)

val lastUpdateDate: KPropertyPath<T, Instant?>
get() = KPropertyPath(this,__LastUpdateDate)

companion object {
val State: KProperty1<BotAnnotation, BotAnnotationState?>
get() = __State
val Reason: KProperty1<BotAnnotation, BotAnnotationReasonType?>
get() = __Reason
val Description: KProperty1<BotAnnotation, String?>
get() = __Description
val GroundTruth: KProperty1<BotAnnotation, String?>
get() = __GroundTruth
val Events: KCollectionSimplePropertyPath<BotAnnotation, BotAnnotationEvent?>
get() = KCollectionSimplePropertyPath(null, __Events)
val CreationDate: KProperty1<BotAnnotation, Instant?>
get() = __CreationDate
val LastUpdateDate: KProperty1<BotAnnotation, Instant?>
get() = __LastUpdateDate}
}

class BotAnnotation_Col<T>(previous: KPropertyPath<T, *>?, property: KProperty1<*,
Collection<BotAnnotation>?>) : KCollectionPropertyPath<T, BotAnnotation?,
BotAnnotation_<T>>(previous,property) {
val state: KPropertyPath<T, BotAnnotationState?>
get() = KPropertyPath(this,__State)

val reason: KPropertyPath<T, BotAnnotationReasonType?>
get() = KPropertyPath(this,__Reason)

val description: KPropertyPath<T, String?>
get() = KPropertyPath(this,__Description)

val groundTruth: KPropertyPath<T, String?>
get() = KPropertyPath(this,__GroundTruth)

val events: KCollectionSimplePropertyPath<T, BotAnnotationEvent?>
get() = KCollectionSimplePropertyPath(this,BotAnnotation::events)

val creationDate: KPropertyPath<T, Instant?>
get() = KPropertyPath(this,__CreationDate)

val lastUpdateDate: KPropertyPath<T, Instant?>
get() = KPropertyPath(this,__LastUpdateDate)

@Suppress("UNCHECKED_CAST")
override fun memberWithAdditionalPath(additionalPath: String): BotAnnotation_<T> =
BotAnnotation_(this, customProperty(this, additionalPath))}

class BotAnnotation_Map<T, K>(previous: KPropertyPath<T, *>?, property: KProperty1<*, Map<K,
BotAnnotation>?>) : KMapPropertyPath<T, K, BotAnnotation?,
BotAnnotation_<T>>(previous,property) {
val state: KPropertyPath<T, BotAnnotationState?>
get() = KPropertyPath(this,__State)

val reason: KPropertyPath<T, BotAnnotationReasonType?>
get() = KPropertyPath(this,__Reason)

val description: KPropertyPath<T, String?>
get() = KPropertyPath(this,__Description)

val groundTruth: KPropertyPath<T, String?>
get() = KPropertyPath(this,__GroundTruth)

val events: KCollectionSimplePropertyPath<T, BotAnnotationEvent?>
get() = KCollectionSimplePropertyPath(this,BotAnnotation::events)

val creationDate: KPropertyPath<T, Instant?>
get() = KPropertyPath(this,__CreationDate)

val lastUpdateDate: KPropertyPath<T, Instant?>
get() = KPropertyPath(this,__LastUpdateDate)

@Suppress("UNCHECKED_CAST")
override fun memberWithAdditionalPath(additionalPath: String): BotAnnotation_<T> =
BotAnnotation_(this, customProperty(this, additionalPath))}
Loading