Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DERCBOT-1309] Enable Annotation and Issue Tracking for Bot Responses #1824

Open
wants to merge 21 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
209 changes: 209 additions & 0 deletions bot/admin/server/src/main/kotlin/BotAdminService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package ai.tock.bot.admin

import ai.tock.bot.admin.FaqAdminService.FAQ_CATEGORY
import ai.tock.bot.admin.annotation.*
import ai.tock.bot.admin.answer.AnswerConfiguration
import ai.tock.bot.admin.answer.AnswerConfigurationType.builtin
import ai.tock.bot.admin.answer.AnswerConfigurationType.script
Expand Down Expand Up @@ -45,6 +46,7 @@ import ai.tock.bot.admin.story.dump.*
import ai.tock.bot.admin.user.UserReportDAO
import ai.tock.bot.connector.ConnectorType
import ai.tock.bot.definition.IntentWithoutNamespace
import ai.tock.bot.engine.action.Action
import ai.tock.bot.engine.config.SatisfactionIntent
import ai.tock.bot.engine.dialog.Dialog
import ai.tock.bot.engine.dialog.DialogFlowDAO
Expand All @@ -61,6 +63,7 @@ import ai.tock.nlp.front.shared.config.*
import ai.tock.nlp.front.shared.config.ClassifiedSentenceStatus.model
import ai.tock.nlp.front.shared.config.ClassifiedSentenceStatus.validated
import ai.tock.shared.*
import ai.tock.shared.exception.rest.NotFoundException
import ai.tock.shared.security.UserLogin
import ai.tock.shared.security.key.HasSecretKey
import ai.tock.shared.security.key.SecretKey
Expand All @@ -69,6 +72,7 @@ import ai.tock.translator.*
import com.github.salomonbrys.kodein.instance
import mu.KotlinLogging
import org.litote.kmongo.Id
import org.litote.kmongo.newId
import org.litote.kmongo.toId
import java.time.Instant
import java.util.*
Expand Down Expand Up @@ -147,6 +151,211 @@ object BotAdminService {
}
}

fun addCommentToAnnotation(
dialogId: String,
actionId: String,
eventDTO: BotAnnotationEventDTO,
user: String
): BotAnnotationEvent {

if (eventDTO.type != BotAnnotationEventType.COMMENT) {
throw IllegalArgumentException("Only COMMENT events are allowed")
}

require(eventDTO.comment != null) { "Comment is required for COMMENT event type" }
scezen marked this conversation as resolved.
Show resolved Hide resolved

val annotation = dialogReportDAO.getAnnotationByActionId(dialogId, actionId)
?: throw IllegalStateException("Annotation not found")

val event = BotAnnotationEventComment(
eventId = newId(),
creationDate = Instant.now(),
lastUpdateDate = Instant.now(),
user = user,
comment = eventDTO.comment!!
)

dialogReportDAO.addAnnotationEvent(dialogId, actionId, event)

return event
}

fun updateAnnotationEvent(
dialogId: String,
actionId: String,
eventId: String,
eventDTO: BotAnnotationEventDTO,
user: String
): BotAnnotationEvent {
val existingEvent = dialogReportDAO.getAnnotationEvent(dialogId, actionId, eventId)
?: throw IllegalArgumentException("Event not found")

if (existingEvent.type != BotAnnotationEventType.COMMENT) {
throw IllegalArgumentException("Only comment events can be updated")
}

if (eventDTO.type != BotAnnotationEventType.COMMENT) {
throw IllegalArgumentException("Event type must be COMMENT")
}

require(eventDTO.comment != null) { "Comment must be provided" }

val annotation = dialogReportDAO.getAnnotationByActionId(dialogId, actionId)
?: throw IllegalStateException("Annotation not found")

val existingCommentEvent = existingEvent as BotAnnotationEventComment
val updatedEvent = existingCommentEvent.copy(
comment = eventDTO.comment!!,
lastUpdateDate = Instant.now()
)

dialogReportDAO.updateAnnotationEvent(dialogId, actionId, eventId, updatedEvent)

return updatedEvent
}

fun deleteAnnotationEvent(
dialogId: String,
actionId: String,
annotationId: String,
eventId: String,
user: String
) {
val existingEvent = dialogReportDAO.getAnnotationEvent(dialogId, actionId, eventId)
?: throw IllegalArgumentException("Event not found")

if (existingEvent.type != BotAnnotationEventType.COMMENT) {
throw IllegalArgumentException("Only comment events can be deleted")
}

val annotation = dialogReportDAO.getAnnotationByActionId(dialogId, actionId)
scezen marked this conversation as resolved.
Show resolved Hide resolved
?: throw IllegalStateException("Annotation not found")


dialogReportDAO.deleteAnnotationEvent(dialogId, actionId, eventId)
}

fun updateAnnotation(
dialogId: String,
actionId: String,
annotationId: String,
updatedAnnotationDTO: BotAnnotationUpdateDTO,
user: String
): BotAnnotation {
val existingAnnotation = dialogReportDAO.getAnnotation(dialogId, actionId, annotationId)
?: throw IllegalStateException("Annotation not found")

val events = mutableListOf<BotAnnotationEvent>()

updatedAnnotationDTO.state?.let { newState ->
Copy link
Contributor

@assouktim assouktim Jan 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 possible improvements:
here we check if there has been a change in status/reason/... so as to avoid duplicating code,
1- can you use a generic method to do this?
2- to reduce the amount of code, it's better to initialize the eventId, creationDate and lastUpdateDate directly in the BotAnnotationEventChange class, so that when you use it, you can only specify the user, before and after values to the constructor.

if (existingAnnotation.state != newState) {
events.add(
BotAnnotationEventState(
eventId = newId(),
creationDate = Instant.now(),
lastUpdateDate = Instant.now(),
user = user,
before = existingAnnotation.state.name,
after = newState.name
)
)
existingAnnotation.state = newState
}
}

updatedAnnotationDTO.reason?.let { newReason ->
if (existingAnnotation.reason != newReason) {
events.add(
BotAnnotationEventReason(
eventId = newId(),
creationDate = Instant.now(),
lastUpdateDate = Instant.now(),
user = user,
before = existingAnnotation.reason?.name,
after = newReason.name
)
)
existingAnnotation.reason = newReason
}
}

updatedAnnotationDTO.groundTruth?.let { newGroundTruth ->
if (existingAnnotation.groundTruth != newGroundTruth) {
events.add(
BotAnnotationEventGroundTruth(
eventId = newId(),
creationDate = Instant.now(),
lastUpdateDate = Instant.now(),
user = user,
before = existingAnnotation.groundTruth,
after = newGroundTruth
)
)
existingAnnotation.groundTruth = newGroundTruth
}
}

updatedAnnotationDTO.description?.let { newDescription ->
if (existingAnnotation.description != newDescription) {
events.add(
BotAnnotationEventDescription(
eventId = newId(),
creationDate = Instant.now(),
lastUpdateDate = Instant.now(),
user = user,
before = existingAnnotation.description,
after = newDescription
)
)
existingAnnotation.description = newDescription
}
}

existingAnnotation.lastUpdateDate = Instant.now()

existingAnnotation.events.addAll(events)

dialogReportDAO.updateAnnotation(dialogId, actionId, existingAnnotation)

return existingAnnotation
}

fun createAnnotation(
dialogId: String,
actionId: String,
annotationDTO: BotAnnotationDTO,
user: String
scezen marked this conversation as resolved.
Show resolved Hide resolved
): BotAnnotation {

if (dialogReportDAO.annotationExists(dialogId, actionId)) {
throw IllegalStateException("Une annotation existe déjà pour cette action.")
scezen marked this conversation as resolved.
Show resolved Hide resolved
}

val annotation = BotAnnotation(
state = annotationDTO.state,
reason = annotationDTO.reason,
description = annotationDTO.description,
groundTruth = annotationDTO.groundTruth,
actionId = actionId,
dialogId = dialogId,
scezen marked this conversation as resolved.
Show resolved Hide resolved
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.updateAnnotation(dialogId, actionId, annotation)
return annotation
}

fun createOrGetIntent(
namespace: String,
intentName: String,
Expand Down
Loading