Skip to content

Commit

Permalink
Merge pull request #1653 from CreditMutuelArkea/feature/langfuse/develop
Browse files Browse the repository at this point in the history
Observability integration for Gen AI orchestrator
  • Loading branch information
Benvii authored Jul 10, 2024
2 parents b476823 + 39df125 commit 256a938
Show file tree
Hide file tree
Showing 86 changed files with 3,342 additions and 1,024 deletions.
69 changes: 9 additions & 60 deletions bot/admin/server/src/main/kotlin/BotAdminService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -28,48 +28,15 @@ import ai.tock.bot.admin.answer.SimpleAnswerConfiguration
import ai.tock.bot.admin.bot.BotApplicationConfiguration
import ai.tock.bot.admin.bot.BotApplicationConfigurationDAO
import ai.tock.bot.admin.bot.BotConfiguration
import ai.tock.bot.admin.bot.BotVersion
import ai.tock.bot.admin.bot.rag.BotRAGConfiguration
import ai.tock.bot.admin.bot.rag.BotRAGConfigurationDAO
import ai.tock.bot.admin.bot.BotVersion
import ai.tock.bot.admin.dialog.ApplicationDialogFlowData
import ai.tock.bot.admin.dialog.DialogReportDAO
import ai.tock.bot.admin.dialog.DialogReportQueryResult
import ai.tock.bot.admin.dialog.RatingReportQueryResult
import ai.tock.bot.admin.dialog.DialogReport
import ai.tock.bot.admin.dialog.*
import ai.tock.bot.admin.kotlin.compiler.KotlinFile
import ai.tock.bot.admin.kotlin.compiler.client.KotlinCompilerClient
import ai.tock.bot.admin.model.BotAnswerConfiguration
import ai.tock.bot.admin.model.BotBuiltinAnswerConfiguration
import ai.tock.bot.admin.model.BotConfiguredAnswer
import ai.tock.bot.admin.model.BotConfiguredSteps
import ai.tock.bot.admin.model.BotScriptAnswerConfiguration
import ai.tock.bot.admin.model.BotSimpleAnswerConfiguration
import ai.tock.bot.admin.model.BotStoryDefinitionConfiguration
import ai.tock.bot.admin.model.BotStoryDefinitionConfigurationMandatoryEntity
import ai.tock.bot.admin.model.BotStoryDefinitionConfigurationStep
import ai.tock.bot.admin.model.CreateI18nLabelRequest
import ai.tock.bot.admin.model.CreateStoryRequest
import ai.tock.bot.admin.model.DialogFlowRequest
import ai.tock.bot.admin.model.DialogsSearchQuery
import ai.tock.bot.admin.model.Feature
import ai.tock.bot.admin.model.StorySearchRequest
import ai.tock.bot.admin.model.SummaryStorySearchRequest
import ai.tock.bot.admin.model.UserSearchQuery
import ai.tock.bot.admin.model.UserSearchQueryResult
import ai.tock.bot.admin.story.StoryDefinitionConfiguration
import ai.tock.bot.admin.story.StoryDefinitionConfigurationByBotStep
import ai.tock.bot.admin.story.StoryDefinitionConfigurationDAO
import ai.tock.bot.admin.story.StoryDefinitionConfigurationFeature
import ai.tock.bot.admin.story.StoryDefinitionConfigurationMandatoryEntity
import ai.tock.bot.admin.story.StoryDefinitionConfigurationStep
import ai.tock.bot.admin.story.StoryDefinitionConfigurationSummaryExtended
import ai.tock.bot.admin.story.StoryDefinitionConfigurationSummaryMinimumMetrics
import ai.tock.bot.admin.story.dump.ScriptAnswerVersionedConfigurationDump
import ai.tock.bot.admin.story.dump.StoriesImportMode
import ai.tock.bot.admin.story.dump.StoryDefinitionConfigurationDump
import ai.tock.bot.admin.story.dump.StoryDefinitionConfigurationDumpController
import ai.tock.bot.admin.story.dump.StoryDefinitionConfigurationDumpImport
import ai.tock.bot.admin.story.dump.StoryDefinitionConfigurationFeatureDump
import ai.tock.bot.admin.model.*
import ai.tock.bot.admin.story.*
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
Expand All @@ -83,33 +50,19 @@ import ai.tock.nlp.admin.AdminService
import ai.tock.nlp.core.Intent
import ai.tock.nlp.front.client.FrontClient
import ai.tock.nlp.front.service.applicationDAO
import ai.tock.nlp.front.shared.config.ApplicationDefinition
import ai.tock.nlp.front.shared.config.Classification
import ai.tock.nlp.front.shared.config.ClassifiedSentence
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.nlp.front.shared.config.EntityDefinition
import ai.tock.nlp.front.shared.config.EntityTypeDefinition
import ai.tock.nlp.front.shared.config.IntentDefinition
import ai.tock.nlp.front.shared.config.SentencesQuery
import ai.tock.shared.Dice
import ai.tock.shared.defaultLocale
import ai.tock.shared.injector
import ai.tock.shared.provide
import ai.tock.shared.*
import ai.tock.shared.security.UserLogin
import ai.tock.shared.vertx.WebVerticle.Companion.badRequest
import ai.tock.shared.withoutNamespace
import ai.tock.translator.I18nDAO
import ai.tock.translator.I18nKeyProvider
import ai.tock.translator.I18nLabel
import ai.tock.translator.I18nLabelValue
import ai.tock.translator.Translator
import ai.tock.translator.*
import com.github.salomonbrys.kodein.instance
import mu.KotlinLogging
import org.litote.kmongo.Id
import org.litote.kmongo.toId
import java.time.Instant
import java.util.Locale
import java.util.*

object BotAdminService {

Expand Down Expand Up @@ -341,10 +294,6 @@ object BotAdminService {
}
}

fun getRAGConfiguration(namespace: String, botId: String): BotRAGConfiguration? {
return ragConfigurationDAO.findByNamespaceAndBotId(namespace, botId)
}

fun searchStories(request: StorySearchRequest): List<StoryDefinitionConfigurationSummaryExtended> =
storyDefinitionDAO.searchStoryDefinitionSummariesExtended(request.toSummaryRequest())

Expand Down
49 changes: 18 additions & 31 deletions bot/admin/server/src/main/kotlin/BotAdminVerticle.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,33 +24,9 @@ import ai.tock.bot.admin.BotAdminService.importStories
import ai.tock.bot.admin.bot.BotApplicationConfiguration
import ai.tock.bot.admin.bot.BotConfiguration
import ai.tock.bot.admin.constants.Properties
import ai.tock.bot.admin.model.BotAdminConfiguration
import ai.tock.bot.admin.model.BotConnectorConfiguration
import ai.tock.bot.admin.model.BotI18nLabel
import ai.tock.bot.admin.model.BotI18nLabelUpdate
import ai.tock.bot.admin.model.BotI18nLabels
import ai.tock.bot.admin.model.BotRAGConfigurationDTO
import ai.tock.bot.admin.model.BotSentenceGenerationConfigurationDTO
import ai.tock.bot.admin.model.BotSentenceGenerationInfoDTO
import ai.tock.bot.admin.model.BotStoryDefinitionConfiguration
import ai.tock.bot.admin.model.BotSynchronization
import ai.tock.bot.admin.model.CreateI18nLabelRequest
import ai.tock.bot.admin.model.CreateStoryRequest
import ai.tock.bot.admin.model.DialogFlowRequest
import ai.tock.bot.admin.model.DialogsSearchQuery
import ai.tock.bot.admin.model.FaqDefinitionRequest
import ai.tock.bot.admin.model.FaqSearchRequest
import ai.tock.bot.admin.model.Feature
import ai.tock.bot.admin.model.I18LabelQuery
import ai.tock.bot.admin.model.SentenceGenerationRequest
import ai.tock.bot.admin.model.StorySearchRequest
import ai.tock.bot.admin.model.SummaryStorySearchRequest
import ai.tock.bot.admin.model.UserSearchQuery
import ai.tock.bot.admin.model.*
import ai.tock.bot.admin.module.satisfactionContentModule
import ai.tock.bot.admin.service.CompletionService
import ai.tock.bot.admin.service.RAGService
import ai.tock.bot.admin.service.SentenceGenerationService
import ai.tock.bot.admin.service.SynchronizationService
import ai.tock.bot.admin.service.*
import ai.tock.bot.admin.story.dump.StoryDefinitionConfigurationDumpImport
import ai.tock.bot.admin.test.TestPlanService
import ai.tock.bot.admin.test.findTestService
Expand Down Expand Up @@ -79,11 +55,7 @@ import ai.tock.shared.injector
import ai.tock.shared.jackson.mapper
import ai.tock.shared.provide
import ai.tock.shared.security.NoEncryptionPassException
import ai.tock.shared.security.TockUserRole.admin
import ai.tock.shared.security.TockUserRole.botUser
import ai.tock.shared.security.TockUserRole.faqBotUser
import ai.tock.shared.security.TockUserRole.faqNlpUser
import ai.tock.shared.security.TockUserRole.nlpUser
import ai.tock.shared.security.TockUserRole.*
import ai.tock.shared.vertx.ServerStatus
import ai.tock.translator.I18nDAO
import ai.tock.translator.I18nLabel
Expand Down Expand Up @@ -492,6 +464,21 @@ open class BotAdminVerticle : AdminVerticle() {
}
}

blockingJsonPost("/configuration/bots/:botId/observability", admin) { context, configuration: BotObservabilityConfigurationDTO ->
if (context.organization == configuration.namespace) {
BotObservabilityConfigurationDTO(ObservabilityService.saveObservability(configuration))
} else {
unauthorized()
}
}

blockingJsonGet("/configuration/bots/:botId/observability", admin) { context ->
ObservabilityService.getObservabilityConfiguration(context.organization, context.path("botId"))
?.let {
BotObservabilityConfigurationDTO(it)
}
}

blockingJsonPost(
"/configuration/bot", admin,
logger = logger<BotConnectorConfiguration>("Create or Update Bot Connector Configuration") { _, c ->
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* 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.admin.model


import ai.tock.bot.admin.bot.observability.BotObservabilityConfiguration
import ai.tock.genai.orchestratorcore.mappers.ObservabilitySettingMapper
import ai.tock.genai.orchestratorcore.models.Constants
import ai.tock.genai.orchestratorcore.models.observability.ObservabilitySettingDTO
import ai.tock.genai.orchestratorcore.utils.SecurityUtils
import org.litote.kmongo.newId
import org.litote.kmongo.toId

data class BotObservabilityConfigurationDTO(
val id: String? = null,
val namespace: String,
val botId: String,
val enabled: Boolean = false,
val setting: ObservabilitySettingDTO,
) {
constructor(configuration: BotObservabilityConfiguration) : this(
configuration._id.toString(),
configuration.namespace,
configuration.botId,
configuration.enabled,
ObservabilitySettingMapper.toDTO(configuration.setting),
)

fun toBotObservabilityConfiguration(): BotObservabilityConfiguration =
BotObservabilityConfiguration(
id?.toId() ?: newId(),
namespace,
botId,
enabled,
ObservabilitySettingMapper.toEntity(
setting,
SecurityUtils.generateAwsSecretName(
namespace, botId, Constants.GEN_AI_OBSERVABILITY
)
),
)
}



Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@ object CompletionService {

// call the completion service to generate sentences
return completionService
.generateSentences(SentenceGenerationQuery(llmSetting, prompt))
.generateSentences(
SentenceGenerationQuery(
llmSetting, prompt,
ObservabilityService.getObservabilityConfiguration(namespace, botId, enabled = true)?.setting
)
)
}
}
94 changes: 94 additions & 0 deletions bot/admin/server/src/main/kotlin/service/ObservabilityService.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/*
* 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.admin.service

import ai.tock.bot.admin.BotAdminService
import ai.tock.bot.admin.bot.observability.BotObservabilityConfiguration
import ai.tock.bot.admin.bot.observability.BotObservabilityConfigurationDAO
import ai.tock.bot.admin.model.BotObservabilityConfigurationDTO
import ai.tock.shared.exception.rest.BadRequestException
import ai.tock.shared.injector
import ai.tock.shared.provide
import ai.tock.shared.vertx.WebVerticle
import com.mongodb.MongoWriteException
import mu.KLogger
import mu.KotlinLogging

/**
* Service that manage the observability functionality
*/
object ObservabilityService {

private val logger: KLogger = KotlinLogging.logger {}
private val observabilityConfigurationDAO: BotObservabilityConfigurationDAO get() = injector.provide()
/**
* Get the Observability configuration
*/
fun getObservabilityConfiguration(namespace: String, botId: String): BotObservabilityConfiguration? {
return observabilityConfigurationDAO.findByNamespaceAndBotId(namespace, botId)
}

/**
* Get the Observability configuration
* @param namespace: the namespace
* @param botId: the botId
* @param enabled: the observability activation (enabled or not)
*/
fun getObservabilityConfiguration(namespace: String, botId: String, enabled: Boolean): BotObservabilityConfiguration? {
return observabilityConfigurationDAO.findByNamespaceAndBotIdAndEnabled(namespace, botId, enabled)
}

/**
* Save Observability configuration and filter errors
* @param observabilityConfig : the observability configuration to create or update
* @throws [BadRequestException] if the observability configuration is invalid
* @return [BotObservabilityConfiguration]
*/
fun saveObservability(
observabilityConfig: BotObservabilityConfigurationDTO
): BotObservabilityConfiguration {
BotAdminService.getBotConfigurationsByNamespaceAndBotId(observabilityConfig.namespace, observabilityConfig.botId).firstOrNull()
?: WebVerticle.badRequest("No bot configuration is defined yet [namespace: ${observabilityConfig.namespace}, botId = ${observabilityConfig.botId}]")
return saveObservabilityConfiguration(observabilityConfig)
}

/**
* Save the Observability configuration
* @param observabilityConfiguration [BotObservabilityConfigurationDTO]
*/
private fun saveObservabilityConfiguration(
observabilityConfiguration: BotObservabilityConfigurationDTO
): BotObservabilityConfiguration {
val observabilityConfig = observabilityConfiguration.toBotObservabilityConfiguration()

// Check validity of the observability configuration
ObservabilityValidationService.validate(observabilityConfig).let { errors ->
if (errors.isNotEmpty()) {
throw BadRequestException(errors)
}
}

return try {
observabilityConfigurationDAO.save(observabilityConfig)
} catch (e: MongoWriteException) {
throw BadRequestException(e.message ?: "Observability Configuration: registration failed on mongo ")
} catch (e: Exception) {
throw BadRequestException(e.message ?: "Observability Configuration: registration failed ")
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* 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.admin.service

import ai.tock.bot.admin.bot.observability.BotObservabilityConfiguration
import ai.tock.genai.orchestratorclient.requests.ObservabilityProviderSettingStatusQuery
import ai.tock.genai.orchestratorclient.responses.ProviderSettingStatusResponse
import ai.tock.genai.orchestratorclient.services.ObservabilityProviderService
import ai.tock.shared.exception.error.ErrorMessage
import ai.tock.shared.injector
import ai.tock.shared.provide


object ObservabilityValidationService {

private val observabilityProviderService: ObservabilityProviderService get() = injector.provide()

fun validate(ragConfig: BotObservabilityConfiguration): Set<ErrorMessage> {
return mutableSetOf<ErrorMessage>().apply {
addAll(
observabilityProviderService
.checkSetting(ObservabilityProviderSettingStatusQuery(ragConfig.setting))
.getErrors("Observability setting check failed")
)
}
}

private fun ProviderSettingStatusResponse?.getErrors(message: String): Set<ErrorMessage> =
this?.errors?.map { ErrorMessage(message = message, params = errors) }?.toSet() ?: emptySet()

}
Loading

0 comments on commit 256a938

Please sign in to comment.