diff --git a/bot/admin/server/src/main/kotlin/BotAdminService.kt b/bot/admin/server/src/main/kotlin/BotAdminService.kt index 0cd624f092..d3e3ff87b7 100644 --- a/bot/admin/server/src/main/kotlin/BotAdminService.kt +++ b/bot/admin/server/src/main/kotlin/BotAdminService.kt @@ -29,6 +29,7 @@ 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.compressor.BotDocumentCompressorConfigurationDAO import ai.tock.bot.admin.bot.observability.BotObservabilityConfigurationDAO import ai.tock.bot.admin.bot.rag.BotRAGConfiguration import ai.tock.bot.admin.bot.rag.BotRAGConfigurationDAO @@ -83,6 +84,7 @@ object BotAdminService { private val ragConfigurationDAO: BotRAGConfigurationDAO get() = injector.provide() private val sentenceGenerationConfigurationDAO: BotSentenceGenerationConfigurationDAO get() = injector.provide() private val observabilityConfigurationDAO: BotObservabilityConfigurationDAO get() = injector.provide() + private val documentProcessorConfigurationDAO: BotDocumentCompressorConfigurationDAO get() = injector.provide() private val vectorStoreConfigurationDAO: BotVectorStoreConfigurationDAO get() = injector.provide() private val storyDefinitionDAO: StoryDefinitionConfigurationDAO get() = injector.provide() private val featureDAO: FeatureDAO get() = injector.provide() @@ -1167,6 +1169,11 @@ object BotAdminService { } } + // delete the Document Compressor configuration + documentProcessorConfigurationDAO.findByNamespaceAndBotId(app.namespace, app.name)?.let { + documentProcessorConfigurationDAO.delete(it._id) + } + // delete the Vector Store configuration vectorStoreConfigurationDAO.findByNamespaceAndBotId(app.namespace, app.name)?.let { config -> vectorStoreConfigurationDAO.delete(config._id) diff --git a/bot/admin/server/src/main/kotlin/BotAdminVerticle.kt b/bot/admin/server/src/main/kotlin/BotAdminVerticle.kt index 00f7e2e058..177db28b99 100644 --- a/bot/admin/server/src/main/kotlin/BotAdminVerticle.kt +++ b/bot/admin/server/src/main/kotlin/BotAdminVerticle.kt @@ -57,7 +57,6 @@ import ai.tock.translator.I18nLabel import ai.tock.translator.Translator import ai.tock.translator.Translator.initTranslator import ai.tock.translator.TranslatorEngine -import ch.tutteli.kbox.isNotNullAndNotBlank import com.fasterxml.jackson.module.kotlin.readValue import com.github.salomonbrys.kodein.instance import io.vertx.core.http.HttpMethod.GET @@ -481,6 +480,25 @@ open class BotAdminVerticle : AdminVerticle() { ObservabilityService.deleteConfig(context.organization, context.path("botId")) } + blockingJsonPost("/configuration/bots/:botId/document-compressor", admin) { context, configuration: BotDocumentCompressorConfigurationDTO -> + if (context.organization == configuration.namespace) { + BotDocumentCompressorConfigurationDTO(DocumentCompressorService.saveDocumentCompressor(configuration)) + } else { + unauthorized() + } + } + + blockingJsonGet("/configuration/bots/:botId/document-compressor", admin) { context -> + DocumentCompressorService.getDocumentCompressorConfiguration(context.organization, context.path("botId")) + ?.let { + BotDocumentCompressorConfigurationDTO(it) + } + } + + blockingDelete("/configuration/bots/:botId/document-compressor", admin) { context -> + DocumentCompressorService.deleteConfig(context.organization, context.path("botId")) + } + blockingJsonPost("/configuration/bots/:botId/vector-store", admin) { context, configuration: BotVectorStoreConfigurationDTO -> if (context.organization == configuration.namespace) { BotVectorStoreConfigurationDTO(VectorStoreService.saveVectorStore(configuration)) diff --git a/bot/admin/server/src/main/kotlin/model/BotDocumentCompressorConfigurationDTO.kt b/bot/admin/server/src/main/kotlin/model/BotDocumentCompressorConfigurationDTO.kt new file mode 100644 index 0000000000..2aba0424c6 --- /dev/null +++ b/bot/admin/server/src/main/kotlin/model/BotDocumentCompressorConfigurationDTO.kt @@ -0,0 +1,51 @@ +/* + * 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.compressor.BotDocumentCompressorConfiguration +import ai.tock.genai.orchestratorcore.models.compressor.DocumentCompressorSetting +import org.litote.kmongo.newId +import org.litote.kmongo.toId + +data class BotDocumentCompressorConfigurationDTO( + val id: String? = null, + val namespace: String, + val botId: String, + val enabled: Boolean = false, + val setting: DocumentCompressorSetting, +) { + constructor(configuration: BotDocumentCompressorConfiguration) : this( + id = configuration._id.toString(), + namespace = configuration.namespace, + botId = configuration.botId, + enabled = configuration.enabled, + setting = configuration.setting, + ) + + fun toBotDocumentCompressorConfiguration(): BotDocumentCompressorConfiguration = + BotDocumentCompressorConfiguration( + _id = id?.toId() ?: newId(), + namespace = namespace, + botId = botId, + enabled = enabled, + setting = setting, + ) +} + + + diff --git a/bot/admin/server/src/main/kotlin/service/DocumentCompressorService.kt b/bot/admin/server/src/main/kotlin/service/DocumentCompressorService.kt new file mode 100644 index 0000000000..2bd377f800 --- /dev/null +++ b/bot/admin/server/src/main/kotlin/service/DocumentCompressorService.kt @@ -0,0 +1,110 @@ +/* + * 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.compressor.BotDocumentCompressorConfiguration +import ai.tock.bot.admin.bot.compressor.BotDocumentCompressorConfigurationDAO +import ai.tock.bot.admin.model.BotDocumentCompressorConfigurationDTO +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 document compressor functionality + */ +object DocumentCompressorService { + + private val logger: KLogger = KotlinLogging.logger {} + private val documentCompressorConfigurationDAO: BotDocumentCompressorConfigurationDAO get() = injector.provide() + /** + * Get the Document Compressor Configuration + * @param namespace: the namespace + * @param botId: the bot ID + */ + fun getDocumentCompressorConfiguration(namespace: String, botId: String): BotDocumentCompressorConfiguration? { + return documentCompressorConfigurationDAO.findByNamespaceAndBotId(namespace, botId) + } + + /** + * Get the Document Compressor Configuration + * @param namespace: the namespace + * @param botId: the botId + * @param enabled: the document compressor activation (enabled or not) + */ + fun getDocumentCompressorConfiguration(namespace: String, botId: String, enabled: Boolean): BotDocumentCompressorConfiguration? { + return documentCompressorConfigurationDAO.findByNamespaceAndBotIdAndEnabled(namespace, botId, enabled) + } + + /** + * Deleting the Document Compressor Configuration + * @param namespace: the namespace + * @param botId: the bot ID + */ + fun deleteConfig(namespace: String, botId: String) { + val documentCompressorConfig = documentCompressorConfigurationDAO.findByNamespaceAndBotId(namespace, botId) + ?: WebVerticle.badRequest("No Document Compressor Configuration is defined yet [namespace: $namespace, botId: $botId]") + logger.info { "Deleting the Document Compressor Configuration [namespace: $namespace, botId: $botId]" } + return documentCompressorConfigurationDAO.delete(documentCompressorConfig._id) + } + + /** + * Save Document Compressor Configuration and filter errors + * @param documentCompressorConfig : the document compressor configuration to create or update + * @throws [BadRequestException] if the document compressor configuration is invalid + * @return [BotDocumentCompressorConfiguration] + */ + fun saveDocumentCompressor( + documentCompressorConfig: BotDocumentCompressorConfigurationDTO + ): BotDocumentCompressorConfiguration { + BotAdminService.getBotConfigurationsByNamespaceAndBotId(documentCompressorConfig.namespace, documentCompressorConfig.botId).firstOrNull() + ?: WebVerticle.badRequest("No bot configuration is defined yet [namespace: ${documentCompressorConfig.namespace}, botId = ${documentCompressorConfig.botId}]") + return saveDocumentCompressorConfiguration(documentCompressorConfig) + } + + /** + * Save the Document Compressor Configuration + * @param documentCompressorConfiguration [BotDocumentCompressorConfigurationDTO] + */ + private fun saveDocumentCompressorConfiguration( + documentCompressorConfiguration: BotDocumentCompressorConfigurationDTO + ): BotDocumentCompressorConfiguration { + val documentCompressorConfig = documentCompressorConfiguration.toBotDocumentCompressorConfiguration() + + // Check validity of the document compressor configuration + if(documentCompressorConfig.enabled) { + DocumentCompressorValidationService.validate(documentCompressorConfig).let { errors -> + if (errors.isNotEmpty()) { + throw BadRequestException(errors) + } + } + } + + return try { + documentCompressorConfigurationDAO.save(documentCompressorConfig) + } catch (e: MongoWriteException) { + throw BadRequestException(e.message ?: "Document Compressor Configuration: registration failed on mongo ") + } catch (e: Exception) { + throw BadRequestException(e.message ?: "Document Compressor Configuration: registration failed ") + } + } + +} diff --git a/bot/admin/server/src/main/kotlin/service/DocumentCompressorValidationService.kt b/bot/admin/server/src/main/kotlin/service/DocumentCompressorValidationService.kt new file mode 100644 index 0000000000..0418434619 --- /dev/null +++ b/bot/admin/server/src/main/kotlin/service/DocumentCompressorValidationService.kt @@ -0,0 +1,49 @@ +/* + * 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.compressor.BotDocumentCompressorConfiguration +import ai.tock.genai.orchestratorclient.requests.DocumentCompressorProviderSettingStatusQuery +import ai.tock.genai.orchestratorclient.responses.ProviderSettingStatusResponse +import ai.tock.genai.orchestratorclient.services.DocumentCompressorProviderService +import ai.tock.shared.exception.error.ErrorMessage +import ai.tock.shared.injector +import ai.tock.shared.provide + + +object DocumentCompressorValidationService { + + private val documentCompressorProviderService: DocumentCompressorProviderService get() = injector.provide() + + fun validate(config: BotDocumentCompressorConfiguration): Set { + return mutableSetOf().apply { + addAll( + documentCompressorProviderService + .checkSetting( + DocumentCompressorProviderSettingStatusQuery( + setting = config.setting + ) + ) + .getErrors("Document Compressor setting check failed") + ) + } + } + + private fun ProviderSettingStatusResponse?.getErrors(message: String): Set = + this?.errors?.map { ErrorMessage(message = message, params = errors) }?.toSet() ?: emptySet() + +} \ No newline at end of file diff --git a/bot/admin/server/src/main/kotlin/service/ObservabilityValidationService.kt b/bot/admin/server/src/main/kotlin/service/ObservabilityValidationService.kt index 894c1e67d0..8cc4700e34 100644 --- a/bot/admin/server/src/main/kotlin/service/ObservabilityValidationService.kt +++ b/bot/admin/server/src/main/kotlin/service/ObservabilityValidationService.kt @@ -29,11 +29,11 @@ object ObservabilityValidationService { private val observabilityProviderService: ObservabilityProviderService get() = injector.provide() - fun validate(ragConfig: BotObservabilityConfiguration): Set { + fun validate(config: BotObservabilityConfiguration): Set { return mutableSetOf().apply { addAll( observabilityProviderService - .checkSetting(ObservabilityProviderSettingStatusQuery(ragConfig.setting)) + .checkSetting(ObservabilityProviderSettingStatusQuery(config.setting)) .getErrors("Observability setting check failed") ) } diff --git a/bot/connector-rest-client/src/main/kotlin/model/ClientFootnote.kt b/bot/connector-rest-client/src/main/kotlin/model/ClientFootnote.kt index 144c9d91c0..1492e653ad 100644 --- a/bot/connector-rest-client/src/main/kotlin/model/ClientFootnote.kt +++ b/bot/connector-rest-client/src/main/kotlin/model/ClientFootnote.kt @@ -23,5 +23,6 @@ data class ClientFootnote( val identifier: String, val title: String, val url: String?, - val content: String? + val content: String?, + val score: Float? ) diff --git a/bot/connector-web-model/src/main/kotlin/ai/tock/bot/connector/web/send/Footnote.kt b/bot/connector-web-model/src/main/kotlin/ai/tock/bot/connector/web/send/Footnote.kt index d91d0c0cde..080818b8ee 100644 --- a/bot/connector-web-model/src/main/kotlin/ai/tock/bot/connector/web/send/Footnote.kt +++ b/bot/connector-web-model/src/main/kotlin/ai/tock/bot/connector/web/send/Footnote.kt @@ -37,4 +37,8 @@ data class Footnote( * A footnote content */ val content: String?, + /** + * A footnote score + */ + val score: Float?, ) \ No newline at end of file diff --git a/bot/connector-web/src/main/kotlin/WebMessageProcessor.kt b/bot/connector-web/src/main/kotlin/WebMessageProcessor.kt index 3c45670e62..0a6fc1560c 100644 --- a/bot/connector-web/src/main/kotlin/WebMessageProcessor.kt +++ b/bot/connector-web/src/main/kotlin/WebMessageProcessor.kt @@ -43,7 +43,8 @@ internal class WebMessageProcessor(private val processMarkdown: Boolean) { footnote.identifier, footnote.title, footnote.url, - footnote.content?.let { postProcess(it) } + footnote.content?.let { postProcess(it) }, + footnote.score ) }) } diff --git a/bot/connector-web/src/test/kotlin/WebConnectorResponseTest.kt b/bot/connector-web/src/test/kotlin/WebConnectorResponseTest.kt index cfadb01a75..a23f9c88cd 100644 --- a/bot/connector-web/src/test/kotlin/WebConnectorResponseTest.kt +++ b/bot/connector-web/src/test/kotlin/WebConnectorResponseTest.kt @@ -51,8 +51,8 @@ internal class WebConnectorResponseTest { responses = listOf( WebMessageContent( text = "Text with 2 footnotes", footnotes = listOf( - Footnote("e122e97a5cc7", "title 1", url = "https://doc.tock.ai", content = "content 1"), - Footnote("fcad492fdb99", "title 2", url = "https://github.com/theopenconversationkit/tock", content = "content 2") + Footnote("e122e97a5cc7", "title 1", url = "https://doc.tock.ai", content = "content 1", score = 0.965521F), + Footnote("fcad492fdb99", "title 2", url = "https://github.com/theopenconversationkit/tock", content = "content 2", score = 0.965521F) ) ) ) diff --git a/bot/engine/src/main/kotlin/admin/bot/compressor/BotDocumentCompressorConfiguration.kt b/bot/engine/src/main/kotlin/admin/bot/compressor/BotDocumentCompressorConfiguration.kt new file mode 100644 index 0000000000..5efe698de9 --- /dev/null +++ b/bot/engine/src/main/kotlin/admin/bot/compressor/BotDocumentCompressorConfiguration.kt @@ -0,0 +1,28 @@ +/* + * 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.bot.compressor + +import ai.tock.genai.orchestratorcore.models.compressor.DocumentCompressorSetting +import org.litote.kmongo.Id + +data class BotDocumentCompressorConfiguration( + val _id: Id, + val namespace: String, + val botId: String, + val enabled: Boolean, + val setting: DocumentCompressorSetting, +) \ No newline at end of file diff --git a/bot/engine/src/main/kotlin/admin/bot/compressor/BotDocumentCompressorConfigurationDAO.kt b/bot/engine/src/main/kotlin/admin/bot/compressor/BotDocumentCompressorConfigurationDAO.kt new file mode 100644 index 0000000000..b354ad1ea2 --- /dev/null +++ b/bot/engine/src/main/kotlin/admin/bot/compressor/BotDocumentCompressorConfigurationDAO.kt @@ -0,0 +1,32 @@ +/* + * 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.bot.compressor + +import org.litote.kmongo.Id + +interface BotDocumentCompressorConfigurationDAO { + + fun listenChanges(listener: () -> Unit) + + fun save(conf: BotDocumentCompressorConfiguration): BotDocumentCompressorConfiguration + + fun findByNamespaceAndBotId(namespace: String, botId: String): BotDocumentCompressorConfiguration? + + fun findByNamespaceAndBotIdAndEnabled(namespace: String, botId: String, enabled: Boolean): BotDocumentCompressorConfiguration? + + fun delete(id: Id) +} diff --git a/bot/engine/src/main/kotlin/definition/BotDefinition.kt b/bot/engine/src/main/kotlin/definition/BotDefinition.kt index f2b95713e5..5bdbd82b86 100644 --- a/bot/engine/src/main/kotlin/definition/BotDefinition.kt +++ b/bot/engine/src/main/kotlin/definition/BotDefinition.kt @@ -16,6 +16,7 @@ package ai.tock.bot.definition +import ai.tock.bot.admin.bot.compressor.BotDocumentCompressorConfiguration import ai.tock.bot.admin.bot.observability.BotObservabilityConfiguration import ai.tock.bot.admin.bot.rag.BotRAGConfiguration import ai.tock.bot.admin.bot.vectorstore.BotVectorStoreConfiguration @@ -134,6 +135,11 @@ interface BotDefinition : I18nKeyProvider { */ var observabilityConfiguration: BotObservabilityConfiguration? + /** + * Document Compressor configuration + */ + var documentCompressorConfiguration: BotDocumentCompressorConfiguration? + /** * The list of each story. */ diff --git a/bot/engine/src/main/kotlin/definition/BotDefinitionBase.kt b/bot/engine/src/main/kotlin/definition/BotDefinitionBase.kt index 3fe1aabec5..a950a69320 100644 --- a/bot/engine/src/main/kotlin/definition/BotDefinitionBase.kt +++ b/bot/engine/src/main/kotlin/definition/BotDefinitionBase.kt @@ -16,6 +16,7 @@ package ai.tock.bot.definition +import ai.tock.bot.admin.bot.compressor.BotDocumentCompressorConfiguration import ai.tock.bot.admin.bot.observability.BotObservabilityConfiguration import ai.tock.bot.admin.bot.rag.BotRAGConfiguration import ai.tock.bot.admin.bot.vectorstore.BotVectorStoreConfiguration @@ -62,7 +63,8 @@ open class BotDefinitionBase( override val ragStory: StoryDefinition = defaultRagStory, override var ragConfiguration: BotRAGConfiguration? = null, override var vectorStoreConfiguration: BotVectorStoreConfiguration? = null, - override var observabilityConfiguration: BotObservabilityConfiguration? = null + override var observabilityConfiguration: BotObservabilityConfiguration? = null, + override var documentCompressorConfiguration: BotDocumentCompressorConfiguration? = null ) : BotDefinition { companion object { diff --git a/bot/engine/src/main/kotlin/engine/BotRepository.kt b/bot/engine/src/main/kotlin/engine/BotRepository.kt index 7dc890dee0..ee15c5fdd5 100644 --- a/bot/engine/src/main/kotlin/engine/BotRepository.kt +++ b/bot/engine/src/main/kotlin/engine/BotRepository.kt @@ -42,6 +42,7 @@ import ai.tock.bot.definition.StoryHandlerListener import ai.tock.bot.definition.StoryStep import ai.tock.bot.engine.action.ActionNotificationType import ai.tock.bot.engine.config.BotObservabilityConfigurationMonitor +import ai.tock.bot.engine.config.BotDocumentCompressorConfigurationMonitor import ai.tock.bot.engine.config.BotRAGConfigurationMonitor import ai.tock.bot.engine.config.BotVectorStoreConfigurationMonitor import ai.tock.bot.engine.config.StoryConfigurationMonitor @@ -563,6 +564,7 @@ object BotRepository { BotRAGConfigurationMonitor.monitor(bot) BotObservabilityConfigurationMonitor.monitor(bot) BotVectorStoreConfigurationMonitor.monitor(bot) + BotDocumentCompressorConfigurationMonitor.monitor(bot) // register connector controller map connectorControllerMap[this] = controller applicationIdBotApplicationConfigurationMap[toKey()] = this @@ -584,6 +586,7 @@ object BotRepository { BotRAGConfigurationMonitor.unmonitor(controller.bot) BotObservabilityConfigurationMonitor.unmonitor(controller.bot) BotVectorStoreConfigurationMonitor.unmonitor(controller.bot) + BotDocumentCompressorConfigurationMonitor.unmonitor(controller.bot) TockConnectorController.unregister(controller) } } diff --git a/bot/engine/src/main/kotlin/engine/action/Footnote.kt b/bot/engine/src/main/kotlin/engine/action/Footnote.kt index 218fb060fb..d15554b5d6 100644 --- a/bot/engine/src/main/kotlin/engine/action/Footnote.kt +++ b/bot/engine/src/main/kotlin/engine/action/Footnote.kt @@ -37,4 +37,8 @@ data class Footnote( * A footnote content */ val content: String?, + /** + * A footnote score + */ + val score: Float?, ) \ No newline at end of file diff --git a/bot/engine/src/main/kotlin/engine/config/BotDocumentCompressorConfigurationMonitor.kt b/bot/engine/src/main/kotlin/engine/config/BotDocumentCompressorConfigurationMonitor.kt new file mode 100644 index 0000000000..a363147ade --- /dev/null +++ b/bot/engine/src/main/kotlin/engine/config/BotDocumentCompressorConfigurationMonitor.kt @@ -0,0 +1,64 @@ +/* + * 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.config + +import ai.tock.bot.admin.bot.compressor.BotDocumentCompressorConfigurationDAO +import ai.tock.bot.engine.Bot +import ai.tock.shared.injector +import com.github.salomonbrys.kodein.instance +import mu.KotlinLogging +import java.util.concurrent.CopyOnWriteArraySet + +/** + * + */ +internal object BotDocumentCompressorConfigurationMonitor { + + private val logger = KotlinLogging.logger {} + + private val documentCompressorConfigurationDAO: BotDocumentCompressorConfigurationDAO by injector.instance() + private val botsToMonitor: MutableSet = CopyOnWriteArraySet() + + init { + logger.info { "start bot document compressor configuration monitor" } + documentCompressorConfigurationDAO.listenChanges { + logger.info { "refresh bots document compressor configuration" } + botsToMonitor.forEach { + refresh(it) + } + } + } + + fun monitor(bot: Bot) { + logger.debug { "load document compressor configuration & monitor bot $bot" } + refresh(bot) + botsToMonitor.add(bot) + } + + fun unmonitor(bot: Bot) { + botsToMonitor.remove(bot) + } + + private fun refresh(bot: Bot) { + logger.debug { "Refreshing bot document compressor configuration ${bot.botDefinition.botId} (${bot.configuration.applicationId}-${bot.configuration._id})..." } + bot.botDefinition.documentCompressorConfiguration = documentCompressorConfigurationDAO.findByNamespaceAndBotIdAndEnabled( + bot.botDefinition.namespace, + bot.botDefinition.botId, + enabled = true + ) + } +} diff --git a/bot/engine/src/main/kotlin/engine/config/RAGAnswerHandler.kt b/bot/engine/src/main/kotlin/engine/config/RAGAnswerHandler.kt index 39c92fbc26..a604f99650 100644 --- a/bot/engine/src/main/kotlin/engine/config/RAGAnswerHandler.kt +++ b/bot/engine/src/main/kotlin/engine/config/RAGAnswerHandler.kt @@ -84,7 +84,8 @@ object RAGAnswerHandler : AbstractProactiveAnswerHandler { botId, applicationId, userId, text = answer.text, footnotes = answer.footnotes.map { Footnote( it.identifier, it.title, it.url, - if(action.metadata.sourceWithContent) it.content else null + if(action.metadata.sourceWithContent) it.content else null, + it.score ) }.toMutableList(), metadata = ActionMetadata(isGenAiRagAnswer = true) @@ -189,6 +190,7 @@ object RAGAnswerHandler : AbstractProactiveAnswerHandler { embeddingQuestionEmSetting = ragConfiguration.emSetting, documentIndexName = indexName, documentSearchParams = documentSearchParams, + compressorSetting = botDefinition.documentCompressorConfiguration?.setting, vectorStoreSetting = vectorStoreSetting, observabilitySetting = botDefinition.observabilityConfiguration?.setting ), debug = action.metadata.debugEnabled || ragDebugEnabled diff --git a/bot/engine/src/main/kotlin/engine/ioc.kt b/bot/engine/src/main/kotlin/engine/ioc.kt index babca27987..c18d60d106 100644 --- a/bot/engine/src/main/kotlin/engine/ioc.kt +++ b/bot/engine/src/main/kotlin/engine/ioc.kt @@ -41,4 +41,5 @@ val botModule = Kodein.Module { bind() with singleton { RAGServiceImpl() } bind() with singleton { ObservabilityProviderServiceImpl() } bind() with singleton { VectorStoreProviderServiceImpl() } + bind() with singleton { DocumentCompressorProviderServiceImpl() } } diff --git a/bot/storage-mongo/src/main/kotlin/BotDocumentCompressorConfigurationMongoDAO.kt b/bot/storage-mongo/src/main/kotlin/BotDocumentCompressorConfigurationMongoDAO.kt new file mode 100644 index 0000000000..fcd98e5ecf --- /dev/null +++ b/bot/storage-mongo/src/main/kotlin/BotDocumentCompressorConfigurationMongoDAO.kt @@ -0,0 +1,73 @@ +/* + * 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.mongo + +import ai.tock.bot.admin.bot.compressor.BotDocumentCompressorConfiguration +import ai.tock.bot.admin.bot.compressor.BotDocumentCompressorConfigurationDAO +import ai.tock.bot.mongo.MongoBotConfiguration.asyncDatabase +import ai.tock.bot.mongo.MongoBotConfiguration.database +import ai.tock.shared.ensureUniqueIndex +import ai.tock.shared.watch +import org.litote.kmongo.* +import org.litote.kmongo.reactivestreams.getCollectionOfName + +internal object BotDocumentCompressorConfigurationMongoDAO : BotDocumentCompressorConfigurationDAO { + + private const val COLLECTION_NAME = "bot_document_compressor_configuration" + internal val col = database.getCollection(COLLECTION_NAME) + private val asyncCol = asyncDatabase.getCollectionOfName(COLLECTION_NAME) + + init { + col.ensureUniqueIndex(BotDocumentCompressorConfiguration::namespace, BotDocumentCompressorConfiguration::botId) + } + + override fun listenChanges(listener: () -> Unit) { + asyncCol.watch { listener() } + } + + override fun findByNamespaceAndBotId( + namespace: String, + botId: String + ): BotDocumentCompressorConfiguration? { + return col.findOne( + BotDocumentCompressorConfiguration::namespace eq namespace, + BotDocumentCompressorConfiguration::botId eq botId + ) + } + + override fun findByNamespaceAndBotIdAndEnabled( + namespace: String, + botId: String, + enabled: Boolean + ): BotDocumentCompressorConfiguration? { + return col.findOne( + BotDocumentCompressorConfiguration::namespace eq namespace, + BotDocumentCompressorConfiguration::botId eq botId, + BotDocumentCompressorConfiguration::enabled eq enabled + ) + } + + override fun save(conf: BotDocumentCompressorConfiguration): BotDocumentCompressorConfiguration { + col.save(conf) + return conf + } + + override fun delete(id: Id) { + col.deleteOneById(id) + } + +} diff --git a/bot/storage-mongo/src/main/kotlin/ioc.kt b/bot/storage-mongo/src/main/kotlin/ioc.kt index ceb2e13594..f435d37cca 100644 --- a/bot/storage-mongo/src/main/kotlin/ioc.kt +++ b/bot/storage-mongo/src/main/kotlin/ioc.kt @@ -17,6 +17,7 @@ package ai.tock.bot.mongo import ai.tock.bot.admin.bot.BotApplicationConfigurationDAO +import ai.tock.bot.admin.bot.compressor.BotDocumentCompressorConfigurationDAO import ai.tock.bot.admin.bot.observability.BotObservabilityConfigurationDAO import ai.tock.bot.admin.bot.rag.BotRAGConfigurationDAO import ai.tock.bot.admin.bot.sentencegeneration.BotSentenceGenerationConfigurationDAO @@ -55,6 +56,7 @@ val botMongoModule = Kodein.Module { bind() with provider { BotApplicationConfigurationMongoDAO } bind() with provider { BotRAGConfigurationMongoDAO } bind() with provider { BotObservabilityConfigurationMongoDAO } + bind() with provider { BotDocumentCompressorConfigurationMongoDAO } bind() with provider { BotVectorStoreConfigurationMongoDAO } bind() with provider { BotSentenceGenerationConfigurationMongoDAO } bind() with provider { StoryDefinitionConfigurationMongoDAO } diff --git a/gen-ai/orchestrator-client/src/main/kotlin/ai/tock/genai/orchestratorclient/api/DocumentCompressorProviderApi.kt b/gen-ai/orchestrator-client/src/main/kotlin/ai/tock/genai/orchestratorclient/api/DocumentCompressorProviderApi.kt new file mode 100644 index 0000000000..b39735b973 --- /dev/null +++ b/gen-ai/orchestrator-client/src/main/kotlin/ai/tock/genai/orchestratorclient/api/DocumentCompressorProviderApi.kt @@ -0,0 +1,35 @@ +/* + * 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.genai.orchestratorclient.api + +import ai.tock.genai.orchestratorclient.requests.DocumentCompressorProviderSettingStatusQuery +import ai.tock.genai.orchestratorclient.responses.ProviderSettingStatusResponse +import ai.tock.genai.orchestratorcore.models.compressor.DocumentCompressorProvider +import retrofit2.Call +import retrofit2.http.Body +import retrofit2.http.POST +import retrofit2.http.Path + +internal interface DocumentCompressorProviderApi { + @POST("/document-compressor-providers/{provider-id}/setting/status") + fun checkDocumentCompressorSetting( + @Body query: DocumentCompressorProviderSettingStatusQuery, + @Path("provider-id") providerId: DocumentCompressorProvider + ): Call + +} + diff --git a/gen-ai/orchestrator-client/src/main/kotlin/ai/tock/genai/orchestratorclient/requests/DocumentCompressorProviderSettingStatusQuery.kt b/gen-ai/orchestrator-client/src/main/kotlin/ai/tock/genai/orchestratorclient/requests/DocumentCompressorProviderSettingStatusQuery.kt new file mode 100644 index 0000000000..17d51b5984 --- /dev/null +++ b/gen-ai/orchestrator-client/src/main/kotlin/ai/tock/genai/orchestratorclient/requests/DocumentCompressorProviderSettingStatusQuery.kt @@ -0,0 +1,24 @@ +/* + * 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.genai.orchestratorclient.requests + +import ai.tock.genai.orchestratorcore.models.compressor.DocumentCompressorSetting + + +class DocumentCompressorProviderSettingStatusQuery( + val setting: DocumentCompressorSetting +) \ No newline at end of file diff --git a/gen-ai/orchestrator-client/src/main/kotlin/ai/tock/genai/orchestratorclient/requests/RAGQuery.kt b/gen-ai/orchestrator-client/src/main/kotlin/ai/tock/genai/orchestratorclient/requests/RAGQuery.kt index 458ab163ef..c3e4b32d89 100644 --- a/gen-ai/orchestrator-client/src/main/kotlin/ai/tock/genai/orchestratorclient/requests/RAGQuery.kt +++ b/gen-ai/orchestrator-client/src/main/kotlin/ai/tock/genai/orchestratorclient/requests/RAGQuery.kt @@ -15,6 +15,7 @@ */ package ai.tock.genai.orchestratorclient.requests +import ai.tock.genai.orchestratorcore.models.compressor.DocumentCompressorSetting import ai.tock.genai.orchestratorcore.models.em.EMSetting import ai.tock.genai.orchestratorcore.models.llm.LLMSetting import ai.tock.genai.orchestratorcore.models.observability.ObservabilitySetting @@ -30,6 +31,7 @@ data class RAGQuery( val embeddingQuestionEmSetting: EMSetting, val documentIndexName: String, val documentSearchParams: DocumentSearchParamsBase, + val compressorSetting: DocumentCompressorSetting?, val vectorStoreSetting: VectorStoreSetting?, val observabilitySetting: ObservabilitySetting? ) diff --git a/gen-ai/orchestrator-client/src/main/kotlin/ai/tock/genai/orchestratorclient/responses/RAGResponse.kt b/gen-ai/orchestrator-client/src/main/kotlin/ai/tock/genai/orchestratorclient/responses/RAGResponse.kt index 81ff2f57dd..9357d742f8 100644 --- a/gen-ai/orchestrator-client/src/main/kotlin/ai/tock/genai/orchestratorclient/responses/RAGResponse.kt +++ b/gen-ai/orchestrator-client/src/main/kotlin/ai/tock/genai/orchestratorclient/responses/RAGResponse.kt @@ -30,5 +30,6 @@ data class Footnote( val identifier: String, val title: String, val url: String? = null, - val content: String?, + val content: String? = null, + val score: Float? = null, ) \ No newline at end of file diff --git a/gen-ai/orchestrator-client/src/main/kotlin/ai/tock/genai/orchestratorclient/services/DocumentCompressorProviderService.kt b/gen-ai/orchestrator-client/src/main/kotlin/ai/tock/genai/orchestratorclient/services/DocumentCompressorProviderService.kt new file mode 100644 index 0000000000..52ec4ed689 --- /dev/null +++ b/gen-ai/orchestrator-client/src/main/kotlin/ai/tock/genai/orchestratorclient/services/DocumentCompressorProviderService.kt @@ -0,0 +1,24 @@ +/* + * 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.genai.orchestratorclient.services + +import ai.tock.genai.orchestratorclient.requests.DocumentCompressorProviderSettingStatusQuery +import ai.tock.genai.orchestratorclient.responses.ProviderSettingStatusResponse + +interface DocumentCompressorProviderService { + fun checkSetting(query: DocumentCompressorProviderSettingStatusQuery): ProviderSettingStatusResponse? +} \ No newline at end of file diff --git a/gen-ai/orchestrator-client/src/main/kotlin/ai/tock/genai/orchestratorclient/services/RAGService.kt b/gen-ai/orchestrator-client/src/main/kotlin/ai/tock/genai/orchestratorclient/services/RAGService.kt index bdfa0fba6f..9e39591e58 100644 --- a/gen-ai/orchestrator-client/src/main/kotlin/ai/tock/genai/orchestratorclient/services/RAGService.kt +++ b/gen-ai/orchestrator-client/src/main/kotlin/ai/tock/genai/orchestratorclient/services/RAGService.kt @@ -16,9 +16,7 @@ package ai.tock.genai.orchestratorclient.services -import ai.tock.genai.orchestratorclient.requests.EMProviderSettingStatusQuery import ai.tock.genai.orchestratorclient.requests.RAGQuery -import ai.tock.genai.orchestratorclient.responses.ProviderSettingStatusResponse import ai.tock.genai.orchestratorclient.responses.RAGResponse interface RAGService { diff --git a/gen-ai/orchestrator-client/src/main/kotlin/ai/tock/genai/orchestratorclient/services/impl/DocumentCompressorProviderServiceImpl.kt b/gen-ai/orchestrator-client/src/main/kotlin/ai/tock/genai/orchestratorclient/services/impl/DocumentCompressorProviderServiceImpl.kt new file mode 100644 index 0000000000..0fd1b8224f --- /dev/null +++ b/gen-ai/orchestrator-client/src/main/kotlin/ai/tock/genai/orchestratorclient/services/impl/DocumentCompressorProviderServiceImpl.kt @@ -0,0 +1,33 @@ +/* + * 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.genai.orchestratorclient.services.impl + +import ai.tock.genai.orchestratorclient.retrofit.GenAIOrchestratorClient +import ai.tock.genai.orchestratorclient.api.DocumentCompressorProviderApi +import ai.tock.genai.orchestratorclient.requests.DocumentCompressorProviderSettingStatusQuery +import ai.tock.genai.orchestratorclient.responses.ProviderSettingStatusResponse +import ai.tock.genai.orchestratorclient.services.DocumentCompressorProviderService + +class DocumentCompressorProviderServiceImpl: DocumentCompressorProviderService { + private val retrofit = GenAIOrchestratorClient.getClient() + private val documentCompressorProviderApi = retrofit.create(DocumentCompressorProviderApi::class.java) + + override fun checkSetting(query: DocumentCompressorProviderSettingStatusQuery): ProviderSettingStatusResponse? { + val response = documentCompressorProviderApi.checkDocumentCompressorSetting(query, query.setting.provider).execute() + return response.body() + } +} \ No newline at end of file diff --git a/gen-ai/orchestrator-core/src/main/kotlin/ai/tock/genai/orchestratorcore/models/Constants.kt b/gen-ai/orchestrator-core/src/main/kotlin/ai/tock/genai/orchestratorcore/models/Constants.kt index fcb244ffb7..987109151c 100644 --- a/gen-ai/orchestrator-core/src/main/kotlin/ai/tock/genai/orchestratorcore/models/Constants.kt +++ b/gen-ai/orchestrator-core/src/main/kotlin/ai/tock/genai/orchestratorcore/models/Constants.kt @@ -26,6 +26,8 @@ object Constants { const val OPEN_SEARCH = "OpenSearch" const val PG_VECTOR = "PGVector" + const val BLOOMZ = "BloomzRerank" + private const val GEN_AI="GenAI" private const val GEN_AI_RAG="$GEN_AI/RAG" private const val GEN_AI_COMPLETION="$GEN_AI/COMPLETION" @@ -35,6 +37,7 @@ object Constants { const val GEN_AI_COMPLETION_SENTENCE_GENERATION="$GEN_AI_COMPLETION/sentenceGeneration" + const val GEN_AI_COMPRESSION="$GEN_AI/COMPRESSION" const val GEN_AI_VECTOR_STORE="$GEN_AI/VECTOR_STORE" const val GEN_AI_OBSERVABILITY="$GEN_AI/OBSERVABILITY" } diff --git a/gen-ai/orchestrator-core/src/main/kotlin/ai/tock/genai/orchestratorcore/models/compressor/BloomzDocumentCompressorSetting.kt b/gen-ai/orchestrator-core/src/main/kotlin/ai/tock/genai/orchestratorcore/models/compressor/BloomzDocumentCompressorSetting.kt new file mode 100644 index 0000000000..03487d6010 --- /dev/null +++ b/gen-ai/orchestrator-core/src/main/kotlin/ai/tock/genai/orchestratorcore/models/compressor/BloomzDocumentCompressorSetting.kt @@ -0,0 +1,24 @@ +/* + * 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.genai.orchestratorcore.models.compressor + +data class BloomzDocumentCompressorSetting( + val maxDocuments: Int, + val minScore: Float, + val label: String, + val endpoint: String, +) : DocumentCompressorSettingBase(DocumentCompressorProvider.BloomzRerank) \ No newline at end of file diff --git a/gen-ai/orchestrator-core/src/main/kotlin/ai/tock/genai/orchestratorcore/models/compressor/DocumentCompressorProvider.kt b/gen-ai/orchestrator-core/src/main/kotlin/ai/tock/genai/orchestratorcore/models/compressor/DocumentCompressorProvider.kt new file mode 100644 index 0000000000..2f0bafe9bc --- /dev/null +++ b/gen-ai/orchestrator-core/src/main/kotlin/ai/tock/genai/orchestratorcore/models/compressor/DocumentCompressorProvider.kt @@ -0,0 +1,27 @@ +/* + * 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.genai.orchestratorcore.models.compressor + +enum class DocumentCompressorProvider{ + BloomzRerank; + + companion object { + fun findByName(provider: String): DocumentCompressorProvider? { + return entries.firstOrNull { it.name == provider } + } + } +} \ No newline at end of file diff --git a/gen-ai/orchestrator-core/src/main/kotlin/ai/tock/genai/orchestratorcore/models/compressor/DocumentCompressorSettingBase.kt b/gen-ai/orchestrator-core/src/main/kotlin/ai/tock/genai/orchestratorcore/models/compressor/DocumentCompressorSettingBase.kt new file mode 100644 index 0000000000..2126346c85 --- /dev/null +++ b/gen-ai/orchestrator-core/src/main/kotlin/ai/tock/genai/orchestratorcore/models/compressor/DocumentCompressorSettingBase.kt @@ -0,0 +1,36 @@ +/* + * 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.genai.orchestratorcore.models.compressor + + +import ai.tock.genai.orchestratorcore.models.Constants +import com.fasterxml.jackson.annotation.JsonSubTypes +import com.fasterxml.jackson.annotation.JsonTypeInfo + +@JsonTypeInfo( + use = JsonTypeInfo.Id.NAME, + include = JsonTypeInfo.As.EXISTING_PROPERTY, + property = "provider" +) +@JsonSubTypes( + JsonSubTypes.Type(value = BloomzDocumentCompressorSetting::class, name = Constants.BLOOMZ) +) +abstract class DocumentCompressorSettingBase( + val provider: DocumentCompressorProvider +) + +typealias DocumentCompressorSetting = DocumentCompressorSettingBase diff --git a/gen-ai/orchestrator-server/src/main/python/server/.run/Gen AI Orchestrator.run.xml b/gen-ai/orchestrator-server/src/main/python/server/.run/Gen AI Orchestrator.run.xml index 2b491fd8ef..50ce9447cc 100644 --- a/gen-ai/orchestrator-server/src/main/python/server/.run/Gen AI Orchestrator.run.xml +++ b/gen-ai/orchestrator-server/src/main/python/server/.run/Gen AI Orchestrator.run.xml @@ -6,6 +6,7 @@