From 46d2d03b7329ae245ad116a7544460bf6b1c44e4 Mon Sep 17 00:00:00 2001 From: Arpit Gupta <162559421+arpitg-1@users.noreply.github.com> Date: Wed, 7 Aug 2024 15:50:48 +0530 Subject: [PATCH] [WIP] W-16354625: Added required exceptions for each operation (#18) * W-16354625: Added required exceptions for each operation * W-16354625: Added exception handling for tools operations * W-16354625: Fixed reformatting * W-16354625: Bug fixes and added supported errors per operation --- pom.xml | 2 +- .../config/LangchainLLMConfiguration.java | 5 +- .../internal/error/MuleChainErrorType.java | 5 +- .../provider/AiServiceErrorTypeProvider.java | 23 + .../provider/EmbeddingErrorTypeProvider.java | 28 + .../provider/ImageErrorTypeProvider.java | 27 + .../internal/exception/ChatException.java | 14 + .../exception/FileHandlingException.java | 18 + .../exception/PromptTemplateException.java | 14 + .../exception/SentimentAnalyzerException.java | 14 + .../config/ConfigValidationException.java | 14 + .../EmbeddingStoreOperationsException.java | 14 + .../exception/embedding/RagException.java | 14 + .../image/ImageAnalyzerException.java | 14 + .../image/ImageGenerationException.java | 14 + .../image/ImageProcessingException.java | 14 + .../tools/ToolsOperationException.java | 14 + .../LangchainEmbeddingStoresOperations.java | 681 ++++++++++-------- .../LangchainImageModelsOperations.java | 92 ++- .../operation/LangchainLLMOperations.java | 86 ++- 20 files changed, 726 insertions(+), 381 deletions(-) create mode 100644 src/main/java/org/mule/extension/mulechain/internal/error/provider/AiServiceErrorTypeProvider.java create mode 100644 src/main/java/org/mule/extension/mulechain/internal/error/provider/EmbeddingErrorTypeProvider.java create mode 100644 src/main/java/org/mule/extension/mulechain/internal/error/provider/ImageErrorTypeProvider.java create mode 100644 src/main/java/org/mule/extension/mulechain/internal/exception/ChatException.java create mode 100644 src/main/java/org/mule/extension/mulechain/internal/exception/FileHandlingException.java create mode 100644 src/main/java/org/mule/extension/mulechain/internal/exception/PromptTemplateException.java create mode 100644 src/main/java/org/mule/extension/mulechain/internal/exception/SentimentAnalyzerException.java create mode 100644 src/main/java/org/mule/extension/mulechain/internal/exception/config/ConfigValidationException.java create mode 100644 src/main/java/org/mule/extension/mulechain/internal/exception/embedding/EmbeddingStoreOperationsException.java create mode 100644 src/main/java/org/mule/extension/mulechain/internal/exception/embedding/RagException.java create mode 100644 src/main/java/org/mule/extension/mulechain/internal/exception/image/ImageAnalyzerException.java create mode 100644 src/main/java/org/mule/extension/mulechain/internal/exception/image/ImageGenerationException.java create mode 100644 src/main/java/org/mule/extension/mulechain/internal/exception/image/ImageProcessingException.java create mode 100644 src/main/java/org/mule/extension/mulechain/internal/exception/tools/ToolsOperationException.java diff --git a/pom.xml b/pom.xml index 3ad1aec..0f7bf82 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ 4.0.0 com.mule.mulechain mulechain-ai-connector - 0.2.13 + 0.2.14-SNAPSHOT mule-extension MuleChain diff --git a/src/main/java/org/mule/extension/mulechain/internal/config/LangchainLLMConfiguration.java b/src/main/java/org/mule/extension/mulechain/internal/config/LangchainLLMConfiguration.java index b869a0c..2ad6d7c 100644 --- a/src/main/java/org/mule/extension/mulechain/internal/config/LangchainLLMConfiguration.java +++ b/src/main/java/org/mule/extension/mulechain/internal/config/LangchainLLMConfiguration.java @@ -4,6 +4,7 @@ package org.mule.extension.mulechain.internal.config; import dev.langchain4j.model.chat.ChatLanguageModel; +import org.mule.extension.mulechain.internal.exception.config.ConfigValidationException; import org.mule.extension.mulechain.internal.operation.LangchainEmbeddingStoresOperations; import org.mule.extension.mulechain.internal.operation.LangchainImageModelsOperations; import org.mule.extension.mulechain.internal.llm.type.LangchainLLMType; @@ -141,7 +142,7 @@ private ChatLanguageModel createModel(ConfigExtractor configExtractor) { if (llmMap.containsKey(type)) { return llmMap.get(type).apply(configExtractor, this); } - throw new IllegalArgumentException("LLM Type not supported: " + llmType); + throw new ConfigValidationException("LLM Type not supported: " + llmType); } @Override @@ -151,7 +152,7 @@ public void initialise() throws InitialisationException { configExtractor = configExtractorMap.get(config).apply(this); model = createModel(configExtractor); } else { - throw new IllegalArgumentException("Config Type not supported: " + configType); + throw new ConfigValidationException("Config Type not supported: " + configType); } } } diff --git a/src/main/java/org/mule/extension/mulechain/internal/error/MuleChainErrorType.java b/src/main/java/org/mule/extension/mulechain/internal/error/MuleChainErrorType.java index cf68ef6..716f206 100644 --- a/src/main/java/org/mule/extension/mulechain/internal/error/MuleChainErrorType.java +++ b/src/main/java/org/mule/extension/mulechain/internal/error/MuleChainErrorType.java @@ -1,8 +1,11 @@ +/** + * (c) 2003-2024 MuleSoft, Inc. The software in this package is published under the terms of the Commercial Free Software license V.1 a copy of which has been included with this distribution in the LICENSE.md file. + */ package org.mule.extension.mulechain.internal.error; import org.mule.runtime.extension.api.error.ErrorTypeDefinition; public enum MuleChainErrorType implements ErrorTypeDefinition { - INVALID_AUTHENTICATION, IO_EXCEPTION, TIME_OUT, RATE_LIMIT_OR_QUOTA_EXCEEDED; + AI_SERVICES_FAILURE, IMAGE_ANALYSIS_FAILURE, IMAGE_GENERATION_FAILURE, IMAGE_PROCESSING_FAILURE, FILE_HANDLING_FAILURE, RAG_FAILURE, EMBEDDING_OPERATIONS_FAILURE, TOOLS_OPERATION_FAILURE, VALIDATION_FAILURE } diff --git a/src/main/java/org/mule/extension/mulechain/internal/error/provider/AiServiceErrorTypeProvider.java b/src/main/java/org/mule/extension/mulechain/internal/error/provider/AiServiceErrorTypeProvider.java new file mode 100644 index 0000000..6486822 --- /dev/null +++ b/src/main/java/org/mule/extension/mulechain/internal/error/provider/AiServiceErrorTypeProvider.java @@ -0,0 +1,23 @@ +/** + * (c) 2003-2024 MuleSoft, Inc. The software in this package is published under the terms of the Commercial Free Software license V.1 a copy of which has been included with this distribution in the LICENSE.md file. + */ +package org.mule.extension.mulechain.internal.error.provider; + +import org.mule.runtime.extension.api.annotation.error.ErrorTypeProvider; +import org.mule.runtime.extension.api.error.ErrorTypeDefinition; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import static java.util.Collections.unmodifiableSet; +import static org.mule.extension.mulechain.internal.error.MuleChainErrorType.AI_SERVICES_FAILURE; + +public class AiServiceErrorTypeProvider implements ErrorTypeProvider { + + @SuppressWarnings("rawtypes") + @Override + public Set getErrorTypes() { + return unmodifiableSet(new HashSet<>(Collections.singletonList(AI_SERVICES_FAILURE))); + } +} diff --git a/src/main/java/org/mule/extension/mulechain/internal/error/provider/EmbeddingErrorTypeProvider.java b/src/main/java/org/mule/extension/mulechain/internal/error/provider/EmbeddingErrorTypeProvider.java new file mode 100644 index 0000000..c184588 --- /dev/null +++ b/src/main/java/org/mule/extension/mulechain/internal/error/provider/EmbeddingErrorTypeProvider.java @@ -0,0 +1,28 @@ +/** + * (c) 2003-2024 MuleSoft, Inc. The software in this package is published under the terms of the Commercial Free Software license V.1 a copy of which has been included with this distribution in the LICENSE.md file. + */ +package org.mule.extension.mulechain.internal.error.provider; + +import org.mule.runtime.extension.api.annotation.error.ErrorTypeProvider; +import org.mule.runtime.extension.api.error.ErrorTypeDefinition; + +import java.util.HashSet; +import java.util.Set; + +import static java.util.Arrays.asList; +import static java.util.Collections.unmodifiableSet; +import static org.mule.extension.mulechain.internal.error.MuleChainErrorType.AI_SERVICES_FAILURE; +import static org.mule.extension.mulechain.internal.error.MuleChainErrorType.EMBEDDING_OPERATIONS_FAILURE; +import static org.mule.extension.mulechain.internal.error.MuleChainErrorType.FILE_HANDLING_FAILURE; +import static org.mule.extension.mulechain.internal.error.MuleChainErrorType.RAG_FAILURE; +import static org.mule.extension.mulechain.internal.error.MuleChainErrorType.TOOLS_OPERATION_FAILURE; + +public class EmbeddingErrorTypeProvider implements ErrorTypeProvider { + + @SuppressWarnings("rawtypes") + @Override + public Set getErrorTypes() { + return unmodifiableSet(new HashSet<>(asList(EMBEDDING_OPERATIONS_FAILURE, AI_SERVICES_FAILURE, RAG_FAILURE, + FILE_HANDLING_FAILURE, TOOLS_OPERATION_FAILURE))); + } +} diff --git a/src/main/java/org/mule/extension/mulechain/internal/error/provider/ImageErrorTypeProvider.java b/src/main/java/org/mule/extension/mulechain/internal/error/provider/ImageErrorTypeProvider.java new file mode 100644 index 0000000..dbb58a3 --- /dev/null +++ b/src/main/java/org/mule/extension/mulechain/internal/error/provider/ImageErrorTypeProvider.java @@ -0,0 +1,27 @@ +/** + * (c) 2003-2024 MuleSoft, Inc. The software in this package is published under the terms of the Commercial Free Software license V.1 a copy of which has been included with this distribution in the LICENSE.md file. + */ +package org.mule.extension.mulechain.internal.error.provider; + +import org.mule.runtime.extension.api.annotation.error.ErrorTypeProvider; +import org.mule.runtime.extension.api.error.ErrorTypeDefinition; + +import java.util.HashSet; +import java.util.Set; + +import static java.util.Arrays.asList; +import static java.util.Collections.unmodifiableSet; +import static org.mule.extension.mulechain.internal.error.MuleChainErrorType.FILE_HANDLING_FAILURE; +import static org.mule.extension.mulechain.internal.error.MuleChainErrorType.IMAGE_ANALYSIS_FAILURE; +import static org.mule.extension.mulechain.internal.error.MuleChainErrorType.IMAGE_GENERATION_FAILURE; +import static org.mule.extension.mulechain.internal.error.MuleChainErrorType.IMAGE_PROCESSING_FAILURE; + +public class ImageErrorTypeProvider implements ErrorTypeProvider { + + @SuppressWarnings("rawtypes") + @Override + public Set getErrorTypes() { + return unmodifiableSet(new HashSet<>(asList(IMAGE_ANALYSIS_FAILURE, IMAGE_GENERATION_FAILURE, IMAGE_PROCESSING_FAILURE, + FILE_HANDLING_FAILURE))); + } +} diff --git a/src/main/java/org/mule/extension/mulechain/internal/exception/ChatException.java b/src/main/java/org/mule/extension/mulechain/internal/exception/ChatException.java new file mode 100644 index 0000000..b15a86c --- /dev/null +++ b/src/main/java/org/mule/extension/mulechain/internal/exception/ChatException.java @@ -0,0 +1,14 @@ +/** + * (c) 2003-2024 MuleSoft, Inc. The software in this package is published under the terms of the Commercial Free Software license V.1 a copy of which has been included with this distribution in the LICENSE.md file. + */ +package org.mule.extension.mulechain.internal.exception; + +import org.mule.extension.mulechain.internal.error.MuleChainErrorType; +import org.mule.runtime.extension.api.exception.ModuleException; + +public class ChatException extends ModuleException { + + public ChatException(String message, Exception exception) { + super(message, MuleChainErrorType.AI_SERVICES_FAILURE, exception); + } +} diff --git a/src/main/java/org/mule/extension/mulechain/internal/exception/FileHandlingException.java b/src/main/java/org/mule/extension/mulechain/internal/exception/FileHandlingException.java new file mode 100644 index 0000000..054ebf5 --- /dev/null +++ b/src/main/java/org/mule/extension/mulechain/internal/exception/FileHandlingException.java @@ -0,0 +1,18 @@ +/** + * (c) 2003-2024 MuleSoft, Inc. The software in this package is published under the terms of the Commercial Free Software license V.1 a copy of which has been included with this distribution in the LICENSE.md file. + */ +package org.mule.extension.mulechain.internal.exception; + +import org.mule.extension.mulechain.internal.error.MuleChainErrorType; +import org.mule.runtime.extension.api.exception.ModuleException; + +public class FileHandlingException extends ModuleException { + + public FileHandlingException(String message, Exception exception) { + super(message, MuleChainErrorType.FILE_HANDLING_FAILURE, exception); + } + + public FileHandlingException(String message) { + super(message, MuleChainErrorType.FILE_HANDLING_FAILURE); + } +} diff --git a/src/main/java/org/mule/extension/mulechain/internal/exception/PromptTemplateException.java b/src/main/java/org/mule/extension/mulechain/internal/exception/PromptTemplateException.java new file mode 100644 index 0000000..1d51edc --- /dev/null +++ b/src/main/java/org/mule/extension/mulechain/internal/exception/PromptTemplateException.java @@ -0,0 +1,14 @@ +/** + * (c) 2003-2024 MuleSoft, Inc. The software in this package is published under the terms of the Commercial Free Software license V.1 a copy of which has been included with this distribution in the LICENSE.md file. + */ +package org.mule.extension.mulechain.internal.exception; + +import org.mule.extension.mulechain.internal.error.MuleChainErrorType; +import org.mule.runtime.extension.api.exception.ModuleException; + +public class PromptTemplateException extends ModuleException { + + public PromptTemplateException(String message, Exception exception) { + super(message, MuleChainErrorType.AI_SERVICES_FAILURE, exception); + } +} diff --git a/src/main/java/org/mule/extension/mulechain/internal/exception/SentimentAnalyzerException.java b/src/main/java/org/mule/extension/mulechain/internal/exception/SentimentAnalyzerException.java new file mode 100644 index 0000000..6b59518 --- /dev/null +++ b/src/main/java/org/mule/extension/mulechain/internal/exception/SentimentAnalyzerException.java @@ -0,0 +1,14 @@ +/** + * (c) 2003-2024 MuleSoft, Inc. The software in this package is published under the terms of the Commercial Free Software license V.1 a copy of which has been included with this distribution in the LICENSE.md file. + */ +package org.mule.extension.mulechain.internal.exception; + +import org.mule.extension.mulechain.internal.error.MuleChainErrorType; +import org.mule.runtime.extension.api.exception.ModuleException; + +public class SentimentAnalyzerException extends ModuleException { + + public SentimentAnalyzerException(String message, Exception exception) { + super(message, MuleChainErrorType.AI_SERVICES_FAILURE, exception); + } +} diff --git a/src/main/java/org/mule/extension/mulechain/internal/exception/config/ConfigValidationException.java b/src/main/java/org/mule/extension/mulechain/internal/exception/config/ConfigValidationException.java new file mode 100644 index 0000000..8d20fd3 --- /dev/null +++ b/src/main/java/org/mule/extension/mulechain/internal/exception/config/ConfigValidationException.java @@ -0,0 +1,14 @@ +/** + * (c) 2003-2024 MuleSoft, Inc. The software in this package is published under the terms of the Commercial Free Software license V.1 a copy of which has been included with this distribution in the LICENSE.md file. + */ +package org.mule.extension.mulechain.internal.exception.config; + +import org.mule.extension.mulechain.internal.error.MuleChainErrorType; +import org.mule.runtime.extension.api.exception.ModuleException; + +public class ConfigValidationException extends ModuleException { + + public ConfigValidationException(String message) { + super(message, MuleChainErrorType.VALIDATION_FAILURE); + } +} diff --git a/src/main/java/org/mule/extension/mulechain/internal/exception/embedding/EmbeddingStoreOperationsException.java b/src/main/java/org/mule/extension/mulechain/internal/exception/embedding/EmbeddingStoreOperationsException.java new file mode 100644 index 0000000..e583746 --- /dev/null +++ b/src/main/java/org/mule/extension/mulechain/internal/exception/embedding/EmbeddingStoreOperationsException.java @@ -0,0 +1,14 @@ +/** + * (c) 2003-2024 MuleSoft, Inc. The software in this package is published under the terms of the Commercial Free Software license V.1 a copy of which has been included with this distribution in the LICENSE.md file. + */ +package org.mule.extension.mulechain.internal.exception.embedding; + +import org.mule.extension.mulechain.internal.error.MuleChainErrorType; +import org.mule.runtime.extension.api.exception.ModuleException; + +public class EmbeddingStoreOperationsException extends ModuleException { + + public EmbeddingStoreOperationsException(String message, Exception exception) { + super(message, MuleChainErrorType.EMBEDDING_OPERATIONS_FAILURE, exception); + } +} diff --git a/src/main/java/org/mule/extension/mulechain/internal/exception/embedding/RagException.java b/src/main/java/org/mule/extension/mulechain/internal/exception/embedding/RagException.java new file mode 100644 index 0000000..748b57b --- /dev/null +++ b/src/main/java/org/mule/extension/mulechain/internal/exception/embedding/RagException.java @@ -0,0 +1,14 @@ +/** + * (c) 2003-2024 MuleSoft, Inc. The software in this package is published under the terms of the Commercial Free Software license V.1 a copy of which has been included with this distribution in the LICENSE.md file. + */ +package org.mule.extension.mulechain.internal.exception.embedding; + +import org.mule.extension.mulechain.internal.error.MuleChainErrorType; +import org.mule.runtime.extension.api.exception.ModuleException; + +public class RagException extends ModuleException { + + public RagException(String message, Exception exception) { + super(message, MuleChainErrorType.RAG_FAILURE, exception); + } +} diff --git a/src/main/java/org/mule/extension/mulechain/internal/exception/image/ImageAnalyzerException.java b/src/main/java/org/mule/extension/mulechain/internal/exception/image/ImageAnalyzerException.java new file mode 100644 index 0000000..745b8fc --- /dev/null +++ b/src/main/java/org/mule/extension/mulechain/internal/exception/image/ImageAnalyzerException.java @@ -0,0 +1,14 @@ +/** + * (c) 2003-2024 MuleSoft, Inc. The software in this package is published under the terms of the Commercial Free Software license V.1 a copy of which has been included with this distribution in the LICENSE.md file. + */ +package org.mule.extension.mulechain.internal.exception.image; + +import org.mule.extension.mulechain.internal.error.MuleChainErrorType; +import org.mule.runtime.extension.api.exception.ModuleException; + +public class ImageAnalyzerException extends ModuleException { + + public ImageAnalyzerException(String message, Exception exception) { + super(message, MuleChainErrorType.IMAGE_ANALYSIS_FAILURE, exception); + } +} diff --git a/src/main/java/org/mule/extension/mulechain/internal/exception/image/ImageGenerationException.java b/src/main/java/org/mule/extension/mulechain/internal/exception/image/ImageGenerationException.java new file mode 100644 index 0000000..ab62051 --- /dev/null +++ b/src/main/java/org/mule/extension/mulechain/internal/exception/image/ImageGenerationException.java @@ -0,0 +1,14 @@ +/** + * (c) 2003-2024 MuleSoft, Inc. The software in this package is published under the terms of the Commercial Free Software license V.1 a copy of which has been included with this distribution in the LICENSE.md file. + */ +package org.mule.extension.mulechain.internal.exception.image; + +import org.mule.extension.mulechain.internal.error.MuleChainErrorType; +import org.mule.runtime.extension.api.exception.ModuleException; + +public class ImageGenerationException extends ModuleException { + + public ImageGenerationException(String message, Exception exception) { + super(message, MuleChainErrorType.IMAGE_GENERATION_FAILURE, exception); + } +} diff --git a/src/main/java/org/mule/extension/mulechain/internal/exception/image/ImageProcessingException.java b/src/main/java/org/mule/extension/mulechain/internal/exception/image/ImageProcessingException.java new file mode 100644 index 0000000..f22c58f --- /dev/null +++ b/src/main/java/org/mule/extension/mulechain/internal/exception/image/ImageProcessingException.java @@ -0,0 +1,14 @@ +/** + * (c) 2003-2024 MuleSoft, Inc. The software in this package is published under the terms of the Commercial Free Software license V.1 a copy of which has been included with this distribution in the LICENSE.md file. + */ +package org.mule.extension.mulechain.internal.exception.image; + +import org.mule.extension.mulechain.internal.error.MuleChainErrorType; +import org.mule.runtime.extension.api.exception.ModuleException; + +public class ImageProcessingException extends ModuleException { + + public ImageProcessingException(String message, Exception exception) { + super(message, MuleChainErrorType.IMAGE_PROCESSING_FAILURE, exception); + } +} diff --git a/src/main/java/org/mule/extension/mulechain/internal/exception/tools/ToolsOperationException.java b/src/main/java/org/mule/extension/mulechain/internal/exception/tools/ToolsOperationException.java new file mode 100644 index 0000000..5188ac7 --- /dev/null +++ b/src/main/java/org/mule/extension/mulechain/internal/exception/tools/ToolsOperationException.java @@ -0,0 +1,14 @@ +/** + * (c) 2003-2024 MuleSoft, Inc. The software in this package is published under the terms of the Commercial Free Software license V.1 a copy of which has been included with this distribution in the LICENSE.md file. + */ +package org.mule.extension.mulechain.internal.exception.tools; + +import org.mule.extension.mulechain.internal.error.MuleChainErrorType; +import org.mule.runtime.extension.api.exception.ModuleException; + +public class ToolsOperationException extends ModuleException { + + public ToolsOperationException(String message, Exception exception) { + super(message, MuleChainErrorType.TOOLS_OPERATION_FAILURE, exception); + } +} diff --git a/src/main/java/org/mule/extension/mulechain/internal/operation/LangchainEmbeddingStoresOperations.java b/src/main/java/org/mule/extension/mulechain/internal/operation/LangchainEmbeddingStoresOperations.java index 1cb0b31..77fb1d5 100644 --- a/src/main/java/org/mule/extension/mulechain/internal/operation/LangchainEmbeddingStoresOperations.java +++ b/src/main/java/org/mule/extension/mulechain/internal/operation/LangchainEmbeddingStoresOperations.java @@ -29,12 +29,19 @@ import org.mapdb.DB; import org.mapdb.DBMaker; import org.mule.extension.mulechain.internal.constants.MuleChainConstants; +import org.mule.extension.mulechain.internal.error.provider.EmbeddingErrorTypeProvider; +import org.mule.extension.mulechain.internal.exception.ChatException; +import org.mule.extension.mulechain.internal.exception.embedding.EmbeddingStoreOperationsException; +import org.mule.extension.mulechain.internal.exception.FileHandlingException; +import org.mule.extension.mulechain.internal.exception.embedding.RagException; +import org.mule.extension.mulechain.internal.exception.tools.ToolsOperationException; import org.mule.extension.mulechain.internal.helpers.FileType; import org.mule.extension.mulechain.internal.helpers.FileTypeParameters; import org.mule.extension.mulechain.internal.config.LangchainLLMConfiguration; import org.mule.extension.mulechain.internal.tools.GenericRestApiTool; import org.mule.extension.mulechain.internal.util.JsonUtils; import org.mule.runtime.extension.api.annotation.Alias; +import org.mule.runtime.extension.api.annotation.error.Throws; import org.mule.runtime.extension.api.annotation.param.MediaType; import org.mule.runtime.extension.api.annotation.param.ParameterGroup; import org.mule.runtime.extension.api.annotation.param.Config; @@ -45,7 +52,6 @@ import dev.langchain4j.service.UserMessage; import java.util.ArrayList; -import java.util.Collection; import java.util.List; import java.util.Map; import dev.langchain4j.chain.ConversationalRetrievalChain; @@ -72,6 +78,8 @@ import static dev.langchain4j.data.message.ChatMessageSerializer.messagesToJson; import java.util.regex.Matcher; import java.util.regex.Pattern; + +import org.mule.sdk.api.exception.ModuleException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -82,7 +90,7 @@ public class LangchainEmbeddingStoresOperations { private static final Logger LOGGER = LoggerFactory.getLogger(LangchainEmbeddingStoresOperations.class); - private EmbeddingModel embeddingModel; + private final EmbeddingModel embeddingModel; private static InMemoryEmbeddingStore deserializedStore; @@ -100,45 +108,52 @@ public LangchainEmbeddingStoresOperations() { @MediaType(value = ANY, strict = false) @Alias("RAG-load-document") + @Throws(EmbeddingErrorTypeProvider.class) public String loadDocumentFile(@Config LangchainLLMConfiguration configuration, String data, String contextPath, @ParameterGroup(name = "Context") FileTypeParameters fileType) { - EmbeddingStore embeddingStore = new InMemoryEmbeddingStore<>(); + try { + EmbeddingStore embeddingStore = new InMemoryEmbeddingStore<>(); - EmbeddingStoreIngestor ingestor = EmbeddingStoreIngestor.builder() - .documentSplitter(DocumentSplitters.recursive(1000, 200, new OpenAiTokenizer())) - .embeddingModel(embeddingModel) - .embeddingStore(embeddingStore) - .build(); + EmbeddingStoreIngestor ingestor = EmbeddingStoreIngestor.builder() + .documentSplitter(DocumentSplitters.recursive(1000, 200, new OpenAiTokenizer())) + .embeddingModel(embeddingModel) + .embeddingStore(embeddingStore) + .build(); - LOGGER.info("RAG loading document with file type: {}", fileType.getFileType()); + LOGGER.info("RAG loading document with file type: {}", fileType.getFileType()); - ingestDocument(fileType, contextPath, ingestor); + ingestDocument(fileType, contextPath, ingestor); - ChatLanguageModel model = configuration.getModel(); + ChatLanguageModel model = configuration.getModel(); - // MIGRATE CHAINS TO AI SERVICES: https://docs.langchain4j.dev/tutorials/ai-services/ - // and Specifically the RAG section: https://docs.langchain4j.dev/tutorials/ai-services#rag - //chains are legacy now, please use AI Services: https://docs.langchain4j.dev/tutorials/ai-services > Update to AI Services + // MIGRATE CHAINS TO AI SERVICES: https://docs.langchain4j.dev/tutorials/ai-services/ + // and Specifically the RAG section: https://docs.langchain4j.dev/tutorials/ai-services#rag + //chains are legacy now, please use AI Services: https://docs.langchain4j.dev/tutorials/ai-services > Update to AI Services - ContentRetriever contentRetriever = new EmbeddingStoreContentRetriever(embeddingStore, embeddingModel); + ContentRetriever contentRetriever = new EmbeddingStoreContentRetriever(embeddingStore, embeddingModel); - AssistantSources assistant = AiServices.builder(AssistantSources.class) - .chatLanguageModel(model) - .contentRetriever(contentRetriever) - .build(); + AssistantSources assistant = AiServices.builder(AssistantSources.class) + .chatLanguageModel(model) + .contentRetriever(contentRetriever) + .build(); - Result answer = assistant.chat(data); + Result answer = assistant.chat(data); - JSONObject jsonObject = new JSONObject(); - jsonObject.put(MuleChainConstants.RESPONSE, answer.content()); - jsonObject.put(MuleChainConstants.TOKEN_USAGE, JsonUtils.getTokenUsage(answer)); - jsonObject.put(MuleChainConstants.FILE_PATH, contextPath); - jsonObject.put(MuleChainConstants.FILE_TYPE, fileType.getFileType()); - jsonObject.put(MuleChainConstants.QUESTION, data); + JSONObject jsonObject = new JSONObject(); + jsonObject.put(MuleChainConstants.RESPONSE, answer.content()); + jsonObject.put(MuleChainConstants.TOKEN_USAGE, JsonUtils.getTokenUsage(answer)); + jsonObject.put(MuleChainConstants.FILE_PATH, contextPath); + jsonObject.put(MuleChainConstants.FILE_TYPE, fileType.getFileType()); + jsonObject.put(MuleChainConstants.QUESTION, data); - return jsonObject.toString(); + return jsonObject.toString(); + } catch (ModuleException e) { + throw e; + } catch (Exception e) { + throw new RagException("Error while loading and retrieving content from the document " + contextPath, e); + } } private void ingestDocument(FileTypeParameters fileType, String contextPath, EmbeddingStoreIngestor ingestor) { @@ -157,7 +172,7 @@ private void ingestDocument(FileTypeParameters fileType, String contextPath, Emb try { url = new URL(contextPath); } catch (MalformedURLException e) { - LOGGER.error("Error while loading the document: " + contextPath, e); + throw new FileHandlingException("Error while loading the document: " + contextPath, e); } Document htmlDocument = UrlDocumentLoader.load(url, new TextDocumentParser()); @@ -167,7 +182,7 @@ private void ingestDocument(FileTypeParameters fileType, String contextPath, Emb ingestor.ingest(document); break; default: - throw new IllegalArgumentException("Unsupported File Type: " + fileType.getFileType()); + throw new FileHandlingException("Unsupported File Type: " + fileType.getFileType()); } } @@ -183,42 +198,45 @@ interface AssistantMemory { */ @MediaType(value = ANY, strict = false) @Alias("CHAT-answer-prompt-with-memory") + @Throws(EmbeddingErrorTypeProvider.class) public String chatWithPersistentMemory(@Config LangchainLLMConfiguration configuration, String data, String memoryName, String dbFilePath, int maxMessages) { - ChatLanguageModel model = configuration.getModel(); + try { + ChatLanguageModel model = configuration.getModel(); - PersistentChatMemoryStore.initialize(dbFilePath); + PersistentChatMemoryStore.initialize(dbFilePath); - PersistentChatMemoryStore store = new PersistentChatMemoryStore(); + PersistentChatMemoryStore store = new PersistentChatMemoryStore(); - ChatMemoryProvider chatMemoryProvider = memoryId -> MessageWindowChatMemory.builder() - .id(memoryName) - .maxMessages(maxMessages) - .chatMemoryStore(store) - .build(); + ChatMemoryProvider chatMemoryProvider = memoryId -> MessageWindowChatMemory.builder() + .id(memoryName) + .maxMessages(maxMessages) + .chatMemoryStore(store) + .build(); - AssistantMemory assistant = AiServices.builder(AssistantMemory.class) - .chatLanguageModel(model) - .chatMemoryProvider(chatMemoryProvider) - .build(); + AssistantMemory assistant = AiServices.builder(AssistantMemory.class) + .chatLanguageModel(model) + .chatMemoryProvider(chatMemoryProvider) + .build(); - Result response = assistant.chat(memoryName, data); + Result response = assistant.chat(memoryName, data); - JSONObject jsonObject = new JSONObject(); - jsonObject.put(MuleChainConstants.RESPONSE, response.content()); - jsonObject.put(MuleChainConstants.TOKEN_USAGE, JsonUtils.getTokenUsage(response)); - jsonObject.put(MuleChainConstants.MEMORY_NAME, memoryName); - jsonObject.put(MuleChainConstants.DB_FILE_PATH, dbFilePath); - jsonObject.put(MuleChainConstants.MAX_MESSAGES, maxMessages); + JSONObject jsonObject = new JSONObject(); + jsonObject.put(MuleChainConstants.RESPONSE, response.content()); + jsonObject.put(MuleChainConstants.TOKEN_USAGE, JsonUtils.getTokenUsage(response)); + jsonObject.put(MuleChainConstants.MEMORY_NAME, memoryName); + jsonObject.put(MuleChainConstants.DB_FILE_PATH, dbFilePath); + jsonObject.put(MuleChainConstants.MAX_MESSAGES, maxMessages); - return jsonObject.toString(); + return jsonObject.toString(); + } catch (Exception e) { + throw new ChatException("Error while responding with the chat provided", e); + } } static class PersistentChatMemoryStore implements ChatMemoryStore { - //private final DB db = DBMaker.fileDB("/Users/amir.khan/Documents/langchain4mule resources/multi-user-chat-memory.db").transactionEnable().fileLockDisable().make(); - //private final Map map = db.hashMap("messages", INTEGER, STRING).createOrOpen(); private static DB db; // private static Map map; private static Map map; @@ -228,7 +246,6 @@ public static void initialize(String dbMFilePath) { .transactionEnable() .fileLockDisable() .make(); - //map = db.hashMap("messages", INTEGER, STRING).createOrOpen(); map = db.hashMap("messages", STRING, STRING).createOrOpen(); } @@ -258,59 +275,64 @@ public void deleteMessages(Object memoryId) { */ @MediaType(value = ANY, strict = false) @Alias("TOOLS-use-ai-service-legacy") + @Throws(EmbeddingErrorTypeProvider.class) public String useTools(@Config LangchainLLMConfiguration configuration, String data, String toolConfig) { - EmbeddingStore embeddingStore = new InMemoryEmbeddingStore<>(); + try { + EmbeddingStore embeddingStore = new InMemoryEmbeddingStore<>(); - EmbeddingStoreIngestor ingestor = EmbeddingStoreIngestor.builder() - .documentSplitter(DocumentSplitters.recursive(30000, 200)) - .embeddingModel(embeddingModel) - .embeddingStore(embeddingStore) - .build(); + EmbeddingStoreIngestor ingestor = EmbeddingStoreIngestor.builder() + .documentSplitter(DocumentSplitters.recursive(30000, 200)) + .embeddingModel(embeddingModel) + .embeddingStore(embeddingStore) + .build(); - Document document = loadDocument(toolConfig, new TextDocumentParser()); - ingestor.ingest(document); + Document document = loadDocument(toolConfig, new TextDocumentParser()); + ingestor.ingest(document); - ChatLanguageModel model = configuration.getModel(); + ChatLanguageModel model = configuration.getModel(); - // MIGRATE CHAINS TO AI SERVICES: https://docs.langchain4j.dev/tutorials/ai-services/ - // and Specifically the RAG section: https://docs.langchain4j.dev/tutorials/ai-services#rag - //chains are legacy now, please use AI Services: https://docs.langchain4j.dev/tutorials/ai-services > Update to AI Services - ConversationalRetrievalChain chain = ConversationalRetrievalChain.builder() - .chatLanguageModel(model) - .retriever(EmbeddingStoreRetriever.from(embeddingStore, embeddingModel)) - // .chatMemory() // you can override default chat memory - // .promptTemplate() // you can override default prompt template - .build(); + // MIGRATE CHAINS TO AI SERVICES: https://docs.langchain4j.dev/tutorials/ai-services/ + // and Specifically the RAG section: https://docs.langchain4j.dev/tutorials/ai-services#rag + //chains are legacy now, please use AI Services: https://docs.langchain4j.dev/tutorials/ai-services > Update to AI Services + ConversationalRetrievalChain chain = ConversationalRetrievalChain.builder() + .chatLanguageModel(model) + .retriever(EmbeddingStoreRetriever.from(embeddingStore, embeddingModel)) + // .chatMemory() // you can override default chat memory + // .promptTemplate() // you can override default prompt template + .build(); - String intermediateAnswer = chain.execute(data); - String response = model.generate(data); - List findURL = extractUrls(intermediateAnswer); - boolean toolsUsed = false; + String intermediateAnswer = chain.execute(data); + String response = model.generate(data); + List findURL = extractUrls(intermediateAnswer); + boolean toolsUsed = false; - if (findURL != null) { + if (findURL != null) { - toolsUsed = true; + toolsUsed = true; - // Create an instance of the custom tool with parameters - GenericRestApiTool restApiTool = new GenericRestApiTool(findURL.get(0), "API Call", "Execute GET or POST Requests"); + // Create an instance of the custom tool with parameters + GenericRestApiTool restApiTool = new GenericRestApiTool(findURL.get(0), "API Call", "Execute GET or POST Requests"); - // Build the assistant with the custom tool - AssistantC assistant = AiServices.builder(AssistantC.class) - .chatLanguageModel(model) - .tools(restApiTool) - //.chatMemory(MessageWindowChatMemory.withMaxMessages(10)) - .build(); - // Use the assistant to make a query - response = assistant.chat(intermediateAnswer); - LOGGER.info("Response: {}", response); - } + // Build the assistant with the custom tool + AssistantC assistant = AiServices.builder(AssistantC.class) + .chatLanguageModel(model) + .tools(restApiTool) + //.chatMemory(MessageWindowChatMemory.withMaxMessages(10)) + .build(); + // Use the assistant to make a query + response = assistant.chat(intermediateAnswer); + LOGGER.info("Response: {}", response); + } - JSONObject jsonObject = new JSONObject(); - jsonObject.put(MuleChainConstants.RESPONSE, response); - jsonObject.put(MuleChainConstants.TOOLS_USED, toolsUsed); + JSONObject jsonObject = new JSONObject(); + jsonObject.put(MuleChainConstants.RESPONSE, response); + jsonObject.put(MuleChainConstants.TOOLS_USED, toolsUsed); - return jsonObject.toString(); + return jsonObject.toString(); + } catch (Exception e) { + throw new ToolsOperationException("Error occurred while executing AI Tools with the provided config", e); + } } @@ -361,16 +383,21 @@ private static List extractUrls(String input) { */ @MediaType(value = ANY, strict = false) @Alias("EMBEDDING-new-store") + @Throws(EmbeddingErrorTypeProvider.class) public String createEmbedding(String storeName) { - InMemoryEmbeddingStore embeddingStore = new InMemoryEmbeddingStore<>(); + try { + InMemoryEmbeddingStore embeddingStore = new InMemoryEmbeddingStore<>(); - embeddingStore.serializeToFile(storeName); + embeddingStore.serializeToFile(storeName); - JSONObject jsonObject = new JSONObject(); - jsonObject.put(MuleChainConstants.STORE_NAME, storeName); - jsonObject.put(MuleChainConstants.STATUS, MuleChainConstants.CREATED); + JSONObject jsonObject = new JSONObject(); + jsonObject.put(MuleChainConstants.STORE_NAME, storeName); + jsonObject.put(MuleChainConstants.STATUS, MuleChainConstants.CREATED); - return jsonObject.toString(); + return jsonObject.toString(); + } catch (Exception e) { + throw new EmbeddingStoreOperationsException("Error while creating new Embedding store: " + storeName, e); + } } @@ -380,28 +407,37 @@ public String createEmbedding(String storeName) { */ @MediaType(value = ANY, strict = false) @Alias("EMBEDDING-add-document-to-store") + @Throws(EmbeddingErrorTypeProvider.class) public String addFileEmbedding(String storeName, String contextPath, @ParameterGroup(name = "Context") FileTypeParameters fileType) { - InMemoryEmbeddingStore deserializedStore = InMemoryEmbeddingStore.fromFile(storeName); + try { + InMemoryEmbeddingStore deserializedStore = InMemoryEmbeddingStore.fromFile(storeName); - EmbeddingStoreIngestor ingestor = EmbeddingStoreIngestor.builder() - .documentSplitter(DocumentSplitters.recursive(2000, 200)) - .embeddingModel(this.embeddingModel) - .embeddingStore(deserializedStore) - .build(); + EmbeddingStoreIngestor ingestor = EmbeddingStoreIngestor.builder() + .documentSplitter(DocumentSplitters.recursive(2000, 200)) + .embeddingModel(this.embeddingModel) + .embeddingStore(deserializedStore) + .build(); - ingestDocument(fileType, contextPath, ingestor); + ingestDocument(fileType, contextPath, ingestor); - deserializedStore.serializeToFile(storeName); + deserializedStore.serializeToFile(storeName); - JSONObject jsonObject = new JSONObject(); - jsonObject.put(MuleChainConstants.FILE_TYPE, fileType.getFileType()); - jsonObject.put(MuleChainConstants.FILE_PATH, contextPath); - jsonObject.put(MuleChainConstants.STORE_NAME, storeName); - jsonObject.put(MuleChainConstants.STATUS, MuleChainConstants.UPDATED); + JSONObject jsonObject = new JSONObject(); + jsonObject.put(MuleChainConstants.FILE_TYPE, fileType.getFileType()); + jsonObject.put(MuleChainConstants.FILE_PATH, contextPath); + jsonObject.put(MuleChainConstants.STORE_NAME, storeName); + jsonObject.put(MuleChainConstants.STATUS, MuleChainConstants.UPDATED); - return jsonObject.toString(); + return jsonObject.toString(); + } catch (ModuleException e) { + throw e; + } catch (Exception e) { + throw new EmbeddingStoreOperationsException(String.format("Error while adding document %s to the Embedding store %s", + contextPath, storeName), + e); + } } @@ -410,56 +446,61 @@ public String addFileEmbedding(String storeName, String contextPath, */ @MediaType(value = ANY, strict = false) @Alias("EMBEDDING-query-from-store") + @Throws(EmbeddingErrorTypeProvider.class) public String queryFromEmbedding(String storeName, String question, int maxResults, double minScore, boolean getLatest) { - if (minScore == 0) { - minScore = 0.7; - } - - InMemoryEmbeddingStore store = getDeserializedStore(storeName, getLatest); - - Embedding questionEmbedding = this.embeddingModel.embed(question).content(); - - List> relevantEmbeddings = store.findRelevant(questionEmbedding, maxResults, minScore); - - String information = relevantEmbeddings.stream() - .map(match -> match.embedded().text()) - .collect(joining("\n\n")); - - JSONObject jsonObject = new JSONObject(); - jsonObject.put(MuleChainConstants.MAX_RESULTS, maxResults); - jsonObject.put(MuleChainConstants.MIN_SCORE, minScore); - jsonObject.put(MuleChainConstants.QUESTION, question); - jsonObject.put(MuleChainConstants.STORE_NAME, storeName); - jsonObject.put(MuleChainConstants.INFORMATION, information); - - JSONArray sources = new JSONArray(); - String absoluteDirectoryPath; - String fileName; - String url; - - JSONObject contentObject; - String fullPath; - for (EmbeddingMatch match : relevantEmbeddings) { - Metadata matchMetadata = match.embedded().metadata(); - - fileName = matchMetadata.getString(MuleChainConstants.EmbeddingConstants.FILE_NAME); - url = matchMetadata.getString(MuleChainConstants.URL); - fullPath = matchMetadata.getString(MuleChainConstants.EmbeddingConstants.FULL_PATH); - absoluteDirectoryPath = matchMetadata.getString(MuleChainConstants.EmbeddingConstants.ABSOLUTE_DIRECTORY_PATH); - - contentObject = new JSONObject(); - contentObject.put(MuleChainConstants.ABSOLUTE_DIRECTORY_PATH, absoluteDirectoryPath); - contentObject.put(MuleChainConstants.FULL_PATH, fullPath); - contentObject.put(MuleChainConstants.FILE_NAME, fileName); - contentObject.put(MuleChainConstants.URL, url); - contentObject.put(MuleChainConstants.INDIVIDUAL_SCORE, match.score()); - contentObject.put(MuleChainConstants.TEXT_SEGMENT, match.embedded().text()); - - sources.put(contentObject); + try { + if (minScore == 0) { + minScore = 0.7; + } + + InMemoryEmbeddingStore store = getDeserializedStore(storeName, getLatest); + + Embedding questionEmbedding = this.embeddingModel.embed(question).content(); + + List> relevantEmbeddings = store.findRelevant(questionEmbedding, maxResults, minScore); + + String information = relevantEmbeddings.stream() + .map(match -> match.embedded().text()) + .collect(joining("\n\n")); + + JSONObject jsonObject = new JSONObject(); + jsonObject.put(MuleChainConstants.MAX_RESULTS, maxResults); + jsonObject.put(MuleChainConstants.MIN_SCORE, minScore); + jsonObject.put(MuleChainConstants.QUESTION, question); + jsonObject.put(MuleChainConstants.STORE_NAME, storeName); + jsonObject.put(MuleChainConstants.INFORMATION, information); + + JSONArray sources = new JSONArray(); + String absoluteDirectoryPath; + String fileName; + String url; + + JSONObject contentObject; + String fullPath; + for (EmbeddingMatch match : relevantEmbeddings) { + Metadata matchMetadata = match.embedded().metadata(); + + fileName = matchMetadata.getString(MuleChainConstants.EmbeddingConstants.FILE_NAME); + url = matchMetadata.getString(MuleChainConstants.URL); + fullPath = matchMetadata.getString(MuleChainConstants.EmbeddingConstants.FULL_PATH); + absoluteDirectoryPath = matchMetadata.getString(MuleChainConstants.EmbeddingConstants.ABSOLUTE_DIRECTORY_PATH); + + contentObject = new JSONObject(); + contentObject.put(MuleChainConstants.ABSOLUTE_DIRECTORY_PATH, absoluteDirectoryPath); + contentObject.put(MuleChainConstants.FULL_PATH, fullPath); + contentObject.put(MuleChainConstants.FILE_NAME, fileName); + contentObject.put(MuleChainConstants.URL, url); + contentObject.put(MuleChainConstants.INDIVIDUAL_SCORE, match.score()); + contentObject.put(MuleChainConstants.TEXT_SEGMENT, match.embedded().text()); + + sources.put(contentObject); + } + jsonObject.put(MuleChainConstants.SOURCES, sources); + + return jsonObject.toString(); + } catch (Exception e) { + throw new EmbeddingStoreOperationsException("Error while querying from the embedding store " + storeName, e); } - jsonObject.put(MuleChainConstants.SOURCES, sources); - - return jsonObject.toString(); } /** @@ -467,54 +508,59 @@ public String queryFromEmbedding(String storeName, String question, int maxResul */ @MediaType(value = ANY, strict = false) @Alias("EMBEDDING-get-info-from-store") + @Throws(EmbeddingErrorTypeProvider.class) public String promptFromEmbedding(@Config LangchainLLMConfiguration configuration, String storeName, String data, boolean getLatest) { - InMemoryEmbeddingStore store = getDeserializedStore(storeName, getLatest); - - ChatLanguageModel model = configuration.getModel(); - - ContentRetriever contentRetriever = new EmbeddingStoreContentRetriever(store, this.embeddingModel); - - AssistantSources assistantSources = AiServices.builder(AssistantSources.class) - .chatLanguageModel(model) - .contentRetriever(contentRetriever) - .build(); - - Result results; - results = assistantSources.chat(data); - List contents = results.sources(); - - JSONObject jsonObject = new JSONObject(); - jsonObject.put(MuleChainConstants.RESPONSE, results.content()); - jsonObject.put(MuleChainConstants.STORE_NAME, storeName); - jsonObject.put(MuleChainConstants.QUESTION, data); - jsonObject.put(MuleChainConstants.GET_LATEST, getLatest); - - JSONArray sources = new JSONArray(); - String absoluteDirectoryPath; - String fileName; - String url; - Metadata metadata; - - JSONObject contentObject; - for (Content content : contents) { - metadata = content.textSegment().metadata(); - absoluteDirectoryPath = (String) metadata.getString(MuleChainConstants.EmbeddingConstants.ABSOLUTE_DIRECTORY_PATH); - fileName = (String) metadata.getString(MuleChainConstants.EmbeddingConstants.FILE_NAME); - url = (String) metadata.getString(MuleChainConstants.URL); - - contentObject = new JSONObject(); - contentObject.put(MuleChainConstants.ABSOLUTE_DIRECTORY_PATH, absoluteDirectoryPath); - contentObject.put(MuleChainConstants.FILE_NAME, fileName); - contentObject.put(MuleChainConstants.URL, url); - contentObject.put(MuleChainConstants.TEXT_SEGMENT, content.textSegment().text()); - sources.put(contentObject); - } - jsonObject.put(MuleChainConstants.SOURCES, sources); - jsonObject.put(MuleChainConstants.TOKEN_USAGE, JsonUtils.getTokenUsage(results)); + try { + InMemoryEmbeddingStore store = getDeserializedStore(storeName, getLatest); + + ChatLanguageModel model = configuration.getModel(); + + ContentRetriever contentRetriever = new EmbeddingStoreContentRetriever(store, this.embeddingModel); + + AssistantSources assistantSources = AiServices.builder(AssistantSources.class) + .chatLanguageModel(model) + .contentRetriever(contentRetriever) + .build(); - return jsonObject.toString(); + Result results; + results = assistantSources.chat(data); + List contents = results.sources(); + + JSONObject jsonObject = new JSONObject(); + jsonObject.put(MuleChainConstants.RESPONSE, results.content()); + jsonObject.put(MuleChainConstants.STORE_NAME, storeName); + jsonObject.put(MuleChainConstants.QUESTION, data); + jsonObject.put(MuleChainConstants.GET_LATEST, getLatest); + + JSONArray sources = new JSONArray(); + String absoluteDirectoryPath; + String fileName; + String url; + Metadata metadata; + + JSONObject contentObject; + for (Content content : contents) { + metadata = content.textSegment().metadata(); + absoluteDirectoryPath = (String) metadata.getString(MuleChainConstants.EmbeddingConstants.ABSOLUTE_DIRECTORY_PATH); + fileName = (String) metadata.getString(MuleChainConstants.EmbeddingConstants.FILE_NAME); + url = (String) metadata.getString(MuleChainConstants.URL); + + contentObject = new JSONObject(); + contentObject.put(MuleChainConstants.ABSOLUTE_DIRECTORY_PATH, absoluteDirectoryPath); + contentObject.put(MuleChainConstants.FILE_NAME, fileName); + contentObject.put(MuleChainConstants.URL, url); + contentObject.put(MuleChainConstants.TEXT_SEGMENT, content.textSegment().text()); + sources.put(contentObject); + } + jsonObject.put(MuleChainConstants.SOURCES, sources); + jsonObject.put(MuleChainConstants.TOKEN_USAGE, JsonUtils.getTokenUsage(results)); + + return jsonObject.toString(); + } catch (Exception e) { + throw new EmbeddingStoreOperationsException(String.format("Error while getting info from the store %s", storeName), e); + } } interface AssistantSources { @@ -527,26 +573,31 @@ interface AssistantSources { */ @MediaType(value = ANY, strict = false) @Alias("EMBEDDING-get-info-from-store-legacy") + @Throws(EmbeddingErrorTypeProvider.class) public String promptFromEmbeddingLegacy(@Config LangchainLLMConfiguration configuration, String storeName, String data, boolean getLatest) { - InMemoryEmbeddingStore store = getDeserializedStore(storeName, getLatest); + try { + InMemoryEmbeddingStore store = getDeserializedStore(storeName, getLatest); - ChatLanguageModel model = configuration.getModel(); + ChatLanguageModel model = configuration.getModel(); - ConversationalRetrievalChain chain = ConversationalRetrievalChain.builder() - .chatLanguageModel(model) - .retriever(EmbeddingStoreRetriever.from(store, this.embeddingModel)) - .build(); + ConversationalRetrievalChain chain = ConversationalRetrievalChain.builder() + .chatLanguageModel(model) + .retriever(EmbeddingStoreRetriever.from(store, this.embeddingModel)) + .build(); - String answer = chain.execute(data); + String answer = chain.execute(data); - JSONObject jsonObject = new JSONObject(); - jsonObject.put(MuleChainConstants.RESPONSE, answer); - jsonObject.put(MuleChainConstants.STORE_NAME, storeName); - jsonObject.put(MuleChainConstants.GET_LATEST, getLatest); + JSONObject jsonObject = new JSONObject(); + jsonObject.put(MuleChainConstants.RESPONSE, answer); + jsonObject.put(MuleChainConstants.STORE_NAME, storeName); + jsonObject.put(MuleChainConstants.GET_LATEST, getLatest); - return jsonObject.toString(); + return jsonObject.toString(); + } catch (Exception e) { + throw new EmbeddingStoreOperationsException(String.format("Error while getting info from the store %s", storeName), e); + } } @@ -561,54 +612,58 @@ interface AssistantEmbedding { */ @MediaType(value = ANY, strict = false) @Alias("TOOLS-use-ai-service") + @Throws(EmbeddingErrorTypeProvider.class) public String useAIServiceTools(@Config LangchainLLMConfiguration configuration, String data, String toolConfig) { + try { + EmbeddingStore embeddingStore = new InMemoryEmbeddingStore<>(); - EmbeddingStore embeddingStore = new InMemoryEmbeddingStore<>(); - - EmbeddingStoreIngestor ingestor = EmbeddingStoreIngestor.builder() - .documentSplitter(DocumentSplitters.recursive(30000, 200)) - .embeddingModel(embeddingModel) - .embeddingStore(embeddingStore) - .build(); - - Document document = loadDocument(toolConfig, new TextDocumentParser()); - ingestor.ingest(document); - - ChatLanguageModel model = configuration.getModel(); - ContentRetriever contentRetriever = new EmbeddingStoreContentRetriever(embeddingStore, embeddingModel); - - AssistantEmbedding assistant = AiServices.builder(AssistantEmbedding.class) - .chatLanguageModel(model) - .contentRetriever(contentRetriever) - .build(); - - String intermediateAnswer = assistant.chat(data); - String response = model.generate(data); - List findURL = extractUrls(intermediateAnswer); - boolean toolsUsed = false; + EmbeddingStoreIngestor ingestor = EmbeddingStoreIngestor.builder() + .documentSplitter(DocumentSplitters.recursive(30000, 200)) + .embeddingModel(embeddingModel) + .embeddingStore(embeddingStore) + .build(); - if (findURL != null) { + Document document = loadDocument(toolConfig, new TextDocumentParser()); + ingestor.ingest(document); - toolsUsed = true; - // Create an instance of the custom tool with parameters - GenericRestApiTool restApiTool = new GenericRestApiTool(findURL.get(0), "API Call", "Execute GET or POST Requests"); + ChatLanguageModel model = configuration.getModel(); + ContentRetriever contentRetriever = new EmbeddingStoreContentRetriever(embeddingStore, embeddingModel); - // Build the assistant with the custom tool - AssistantC assistantC = AiServices.builder(AssistantC.class) + AssistantEmbedding assistant = AiServices.builder(AssistantEmbedding.class) .chatLanguageModel(model) - .tools(restApiTool) - //.chatMemory(MessageWindowChatMemory.withMaxMessages(10)) + .contentRetriever(contentRetriever) .build(); - // Use the assistant to make a query - response = assistantC.chat(intermediateAnswer); - LOGGER.info("Response: {}", response); - } - JSONObject jsonObject = new JSONObject(); - jsonObject.put(MuleChainConstants.RESPONSE, response); - jsonObject.put(MuleChainConstants.TOOLS_USED, toolsUsed); - - return jsonObject.toString(); + String intermediateAnswer = assistant.chat(data); + String response = model.generate(data); + List findURL = extractUrls(intermediateAnswer); + boolean toolsUsed = false; + + if (findURL != null) { + + toolsUsed = true; + // Create an instance of the custom tool with parameters + GenericRestApiTool restApiTool = new GenericRestApiTool(findURL.get(0), "API Call", "Execute GET or POST Requests"); + + // Build the assistant with the custom tool + AssistantC assistantC = AiServices.builder(AssistantC.class) + .chatLanguageModel(model) + .tools(restApiTool) + //.chatMemory(MessageWindowChatMemory.withMaxMessages(10)) + .build(); + // Use the assistant to make a query + response = assistantC.chat(intermediateAnswer); + LOGGER.info("Response: {}", response); + } + + JSONObject jsonObject = new JSONObject(); + jsonObject.put(MuleChainConstants.RESPONSE, response); + jsonObject.put(MuleChainConstants.TOOLS_USED, toolsUsed); + + return jsonObject.toString(); + } catch (Exception e) { + throw new ToolsOperationException("Error occurred while executing AI Tools with the provided config", e); + } } @@ -617,63 +672,73 @@ public String useAIServiceTools(@Config LangchainLLMConfiguration configuration, */ @MediaType(value = ANY, strict = false) @Alias("EMBEDDING-add-folder-to-store") + @Throws(EmbeddingErrorTypeProvider.class) public String addFilesFromFolderEmbedding(String storeName, String contextPath, @ParameterGroup(name = "Context") FileTypeParameters fileType) { - InMemoryEmbeddingStore deserializedStore = InMemoryEmbeddingStore.fromFile(storeName); - - EmbeddingStoreIngestor ingestor = EmbeddingStoreIngestor.builder() - .documentSplitter(DocumentSplitters.recursive(2000, 200)) - .embeddingModel(this.embeddingModel) - .embeddingStore(deserializedStore) - .build(); - - long totalFiles = 0; - try (Stream paths = Files.walk(Paths.get(contextPath))) { - totalFiles = paths.filter(Files::isRegularFile).count(); - } catch (IOException e) { - LOGGER.error("Unable to load files in the path: " + contextPath, e); - } + try { + InMemoryEmbeddingStore deserializedStore = InMemoryEmbeddingStore.fromFile(storeName); - LOGGER.info("Total number of files to process: {}", totalFiles); - AtomicInteger fileCounter = new AtomicInteger(0); - try (Stream paths = Files.walk(Paths.get(contextPath))) { - paths.filter(Files::isRegularFile).forEach(file -> { - int currentFileCounter = fileCounter.incrementAndGet(); - LOGGER.info("Processing file {}: {}", currentFileCounter, file.getFileName()); - Document document = null; - try { - switch (FileType.fromValue(fileType.getFileType())) { - case TEXT: - document = loadDocument(file.toString(), new TextDocumentParser()); - ingestor.ingest(document); - break; - case PDF: - document = loadDocument(file.toString(), new ApacheTikaDocumentParser()); - ingestor.ingest(document); - break; - case URL: - // Handle URLs separately if needed - break; - default: - throw new IllegalArgumentException("Unsupported File Type: " + fileType.getFileType()); + EmbeddingStoreIngestor ingestor = EmbeddingStoreIngestor.builder() + .documentSplitter(DocumentSplitters.recursive(2000, 200)) + .embeddingModel(this.embeddingModel) + .embeddingStore(deserializedStore) + .build(); + + long totalFiles = 0; + try (Stream paths = Files.walk(Paths.get(contextPath))) { + totalFiles = paths.filter(Files::isRegularFile).count(); + } catch (IOException e) { + LOGGER.error("Unable to load files in the path: " + contextPath, e); + } + + LOGGER.info("Total number of files to process: {}", totalFiles); + AtomicInteger fileCounter = new AtomicInteger(0); + try (Stream paths = Files.walk(Paths.get(contextPath))) { + paths.filter(Files::isRegularFile).forEach(file -> { + int currentFileCounter = fileCounter.incrementAndGet(); + LOGGER.info("Processing file {}: {}", currentFileCounter, file.getFileName()); + Document document = null; + try { + switch (FileType.fromValue(fileType.getFileType())) { + case TEXT: + document = loadDocument(file.toString(), new TextDocumentParser()); + ingestor.ingest(document); + break; + case PDF: + document = loadDocument(file.toString(), new ApacheTikaDocumentParser()); + ingestor.ingest(document); + break; + case URL: + // Handle URLs separately if needed + break; + default: + throw new FileHandlingException("Unsupported File Type: " + fileType.getFileType()); + } + } catch (BlankDocumentException e) { + LOGGER.warn("Skipping file due to BlankDocumentException: {}", file.getFileName()); } - } catch (BlankDocumentException e) { - LOGGER.warn("Skipping file due to BlankDocumentException: {}", file.getFileName()); - } - }); - } catch (IOException e) { - LOGGER.error("Exception occurred while loading files: " + contextPath, e); + }); + } catch (IOException e) { + LOGGER.error("Exception occurred while loading files: " + contextPath, e); + throw new FileHandlingException("Exception occurred while loading files: " + contextPath, e); + } + + deserializedStore.serializeToFile(storeName); + + JSONObject jsonObject = new JSONObject(); + jsonObject.put(MuleChainConstants.FILES_COUNT, totalFiles); + jsonObject.put(MuleChainConstants.FOLDER_PATH, contextPath); + jsonObject.put(MuleChainConstants.STORE_NAME, storeName); + jsonObject.put(MuleChainConstants.STATUS, MuleChainConstants.UPDATED); + + return jsonObject.toString(); + } catch (ModuleException e) { + throw e; + } catch (Exception e) { + throw new EmbeddingStoreOperationsException(String.format("Error while adding folder %s into the store %s", contextPath, + storeName), + e); } - - deserializedStore.serializeToFile(storeName); - - JSONObject jsonObject = new JSONObject(); - jsonObject.put(MuleChainConstants.FILES_COUNT, totalFiles); - jsonObject.put(MuleChainConstants.FOLDER_PATH, contextPath); - jsonObject.put(MuleChainConstants.STORE_NAME, storeName); - jsonObject.put(MuleChainConstants.STATUS, MuleChainConstants.UPDATED); - - return jsonObject.toString(); } } diff --git a/src/main/java/org/mule/extension/mulechain/internal/operation/LangchainImageModelsOperations.java b/src/main/java/org/mule/extension/mulechain/internal/operation/LangchainImageModelsOperations.java index e771c53..a0ee009 100644 --- a/src/main/java/org/mule/extension/mulechain/internal/operation/LangchainImageModelsOperations.java +++ b/src/main/java/org/mule/extension/mulechain/internal/operation/LangchainImageModelsOperations.java @@ -7,9 +7,15 @@ import org.json.JSONObject; import org.mule.extension.mulechain.internal.config.LangchainLLMConfiguration; import org.mule.extension.mulechain.internal.constants.MuleChainConstants; +import org.mule.extension.mulechain.internal.error.provider.ImageErrorTypeProvider; +import org.mule.extension.mulechain.internal.exception.FileHandlingException; +import org.mule.extension.mulechain.internal.exception.image.ImageAnalyzerException; +import org.mule.extension.mulechain.internal.exception.image.ImageGenerationException; +import org.mule.extension.mulechain.internal.exception.image.ImageProcessingException; import org.mule.extension.mulechain.internal.llm.config.ConfigExtractor; import org.mule.extension.mulechain.internal.util.JsonUtils; import org.mule.runtime.extension.api.annotation.Alias; +import org.mule.runtime.extension.api.annotation.error.Throws; import org.mule.runtime.extension.api.annotation.param.MediaType; import org.mule.runtime.extension.api.annotation.param.Config; @@ -24,13 +30,15 @@ import dev.langchain4j.data.message.TextContent; import dev.langchain4j.data.message.UserMessage; import dev.langchain4j.model.chat.ChatLanguageModel; +import org.mule.sdk.api.exception.ModuleException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; -import java.io.FileInputStream; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; import java.util.Base64; import javax.imageio.ImageIO; @@ -51,21 +59,27 @@ public class LangchainImageModelsOperations { */ @MediaType(value = ANY, strict = false) @Alias("IMAGE-read") + @Throws(ImageErrorTypeProvider.class) public String readFromImage(@Config LangchainLLMConfiguration configuration, String data, String contextURL) { + try { + ChatLanguageModel model = configuration.getModel(); - ChatLanguageModel model = configuration.getModel(); - - UserMessage userMessage = UserMessage.from( - TextContent.from(data), - ImageContent.from(contextURL)); + UserMessage userMessage = UserMessage.from( + TextContent.from(data), + ImageContent.from(contextURL)); - Response response = model.generate(userMessage); + Response response = model.generate(userMessage); - JSONObject jsonObject = new JSONObject(); - jsonObject.put(MuleChainConstants.RESPONSE, response.content().text()); - jsonObject.put(MuleChainConstants.TOKEN_USAGE, JsonUtils.getTokenUsage(response)); + JSONObject jsonObject = new JSONObject(); + jsonObject.put(MuleChainConstants.RESPONSE, response.content().text()); + jsonObject.put(MuleChainConstants.TOKEN_USAGE, JsonUtils.getTokenUsage(response)); - return jsonObject.toString(); + return jsonObject.toString(); + } catch (Exception e) { + throw new ImageAnalyzerException(String.format("Unable to analyze the provided image %s with the text: %s", contextURL, + data), + e); + } } /** @@ -73,53 +87,56 @@ public String readFromImage(@Config LangchainLLMConfiguration configuration, Str */ @MediaType(value = ANY, strict = false) @Alias("IMAGE-generate") + @Throws(ImageErrorTypeProvider.class) public String drawImage(@Config LangchainLLMConfiguration configuration, String data) { - ConfigExtractor configExtractor = configuration.getConfigExtractor(); - ImageModel model = OpenAiImageModel.builder() - .modelName(configuration.getModelName()) - .apiKey(configExtractor.extractValue("OPENAI_API_KEY")) - .build(); - - Response response = model.generate(data); - LOGGER.info("Generated Image: {}", response.content().url()); - - JSONObject jsonObject = new JSONObject(); - jsonObject.put(MuleChainConstants.RESPONSE, response.content().url()); - - return jsonObject.toString(); + try { + ConfigExtractor configExtractor = configuration.getConfigExtractor(); + ImageModel model = OpenAiImageModel.builder() + .modelName(configuration.getModelName()) + .apiKey(configExtractor.extractValue("OPENAI_API_KEY")) + .build(); + + Response response = model.generate(data); + LOGGER.info("Generated Image: {}", response.content().url()); + + JSONObject jsonObject = new JSONObject(); + jsonObject.put(MuleChainConstants.RESPONSE, response.content().url()); + + return jsonObject.toString(); + } catch (Exception e) { + throw new ImageGenerationException("Error while generating the required image: " + data, e); + } } /** - * Reads an scanned document. + * Reads a scanned document. */ @MediaType(value = ANY, strict = false) @Alias("IMAGE-read-scanned-documents") + @Throws(ImageErrorTypeProvider.class) public String readScannedDocumentPDF(@Config LangchainLLMConfiguration configuration, String data, String filePath) { ChatLanguageModel model = configuration.getModel(); - String sourceDir = filePath; - JSONObject jsonObject = new JSONObject(); JSONArray docPages = new JSONArray(); //try (PDDocument document = Loader.loadPDF(new File(sourceDir))) { - try (InputStream inputStream = new FileInputStream(sourceDir); + try (InputStream inputStream = Files.newInputStream(Paths.get(filePath)); PDDocument document = PDDocument.load(inputStream);) { - PDFRenderer pdfRenderer = new PDFRenderer(document); int totalPages = document.getNumberOfPages(); - LOGGER.info("Total files to be converted -> " + totalPages); + LOGGER.info("Total files to be converted -> {}", totalPages); jsonObject.put(MuleChainConstants.TOTAL_PAGES, totalPages); JSONObject docPage; for (int pageNumber = 0; pageNumber < totalPages; pageNumber++) { BufferedImage image = pdfRenderer.renderImageWithDPI(pageNumber, 300); - LOGGER.info("Reading page -> " + pageNumber); + LOGGER.info("Reading page -> {}", pageNumber); String imageBase64 = convertToBase64String(image); UserMessage userMessage = UserMessage.from( @@ -136,7 +153,14 @@ public String readScannedDocumentPDF(@Config LangchainLLMConfiguration configura } } catch (IOException e) { - LOGGER.info("Error occurred while processing the file: " + e.getMessage()); + LOGGER.error("Error occurred in processing the document: " + filePath, e); + throw new FileHandlingException("Error occurred while processing the document file: " + filePath, e); + } catch (ModuleException e) { + throw e; + } catch (Exception e) { + throw new ImageAnalyzerException(String.format("Unable to analyze the provided document %s with the text: %s", filePath, + data), + e); } jsonObject.put(MuleChainConstants.PAGES, docPages); @@ -152,9 +176,7 @@ private String convertToBase64String(BufferedImage image) { base64String = Base64.getEncoder().encodeToString(imageBytes); return base64String; } catch (IOException e) { - e.printStackTrace(); - LOGGER.info("Error occurred while processing the file: " + e.getMessage()); - return "Error"; + throw new ImageProcessingException("Error occurred while processing the image", e); } } } diff --git a/src/main/java/org/mule/extension/mulechain/internal/operation/LangchainLLMOperations.java b/src/main/java/org/mule/extension/mulechain/internal/operation/LangchainLLMOperations.java index 9a44a41..cd6ef19 100644 --- a/src/main/java/org/mule/extension/mulechain/internal/operation/LangchainLLMOperations.java +++ b/src/main/java/org/mule/extension/mulechain/internal/operation/LangchainLLMOperations.java @@ -13,8 +13,13 @@ import org.json.JSONObject; import org.mule.extension.mulechain.internal.config.LangchainLLMConfiguration; import org.mule.extension.mulechain.internal.constants.MuleChainConstants; +import org.mule.extension.mulechain.internal.error.provider.AiServiceErrorTypeProvider; +import org.mule.extension.mulechain.internal.exception.ChatException; +import org.mule.extension.mulechain.internal.exception.PromptTemplateException; +import org.mule.extension.mulechain.internal.exception.SentimentAnalyzerException; import org.mule.extension.mulechain.internal.util.JsonUtils; import org.mule.runtime.extension.api.annotation.Alias; +import org.mule.runtime.extension.api.annotation.error.Throws; import org.mule.runtime.extension.api.annotation.param.Config; import org.mule.runtime.extension.api.annotation.param.MediaType; import dev.langchain4j.model.input.Prompt; @@ -42,50 +47,58 @@ interface Assistant { */ @MediaType(value = ANY, strict = false) @Alias("CHAT-answer-prompt") + @Throws(AiServiceErrorTypeProvider.class) public String answerPromptByModelName(@Config LangchainLLMConfiguration configuration, String prompt) { // OpenAI parameters are explained here: https://platform.openai.com/docs/api-reference/chat/create - ChatLanguageModel model = configuration.getModel(); + try { + ChatLanguageModel model = configuration.getModel(); + Assistant assistant = AiServices.create(Assistant.class, model); + Result answer = assistant.chat(prompt); - Assistant assistant = AiServices.create(Assistant.class, model); + JSONObject jsonObject = new JSONObject(); + jsonObject.put(MuleChainConstants.RESPONSE, answer.content()); + jsonObject.put(MuleChainConstants.TOKEN_USAGE, JsonUtils.getTokenUsage(answer)); - Result answer = assistant.chat(prompt); - - JSONObject jsonObject = new JSONObject(); - jsonObject.put(MuleChainConstants.RESPONSE, answer.content()); - jsonObject.put(MuleChainConstants.TOKEN_USAGE, JsonUtils.getTokenUsage(answer)); - return jsonObject.toString(); + return jsonObject.toString(); + } catch (Exception e) { + throw new ChatException("Unable to respond with the chat provided", e); + } } /** - * Helps defining an AI Agent with a prompt template + * Helps in defining an AI Agent with a prompt template */ @MediaType(value = ANY, strict = false) @Alias("AGENT-define-prompt-template") + @Throws(AiServiceErrorTypeProvider.class) public String definePromptTemplate(@Config LangchainLLMConfiguration configuration, String template, String instructions, String dataset) { - ChatLanguageModel model = configuration.getModel(); + try { + ChatLanguageModel model = configuration.getModel(); - PromptTemplate promptTemplate = PromptTemplate.from(template + System.lineSeparator() + "Instructions: {{instructions}}" - + System.lineSeparator() + "Dataset: {{dataset}}"); + PromptTemplate promptTemplate = PromptTemplate.from(template + System.lineSeparator() + "Instructions: {{instructions}}" + + System.lineSeparator() + "Dataset: {{dataset}}"); - Map variables = new HashMap<>(); - variables.put(MuleChainConstants.INSTRUCTIONS, instructions); - variables.put(MuleChainConstants.DATASET, dataset); + Map variables = new HashMap<>(); + variables.put(MuleChainConstants.INSTRUCTIONS, instructions); + variables.put(MuleChainConstants.DATASET, dataset); - Prompt prompt = promptTemplate.apply(variables); + Prompt prompt = promptTemplate.apply(variables); - //String response = model.generate(prompt.text()); - Assistant assistant = AiServices.create(Assistant.class, model); + Assistant assistant = AiServices.create(Assistant.class, model); - Result answer = assistant.chat(prompt.text()); + Result answer = assistant.chat(prompt.text()); - JSONObject jsonObject = new JSONObject(); - jsonObject.put(MuleChainConstants.RESPONSE, answer.content()); - jsonObject.put(MuleChainConstants.TOKEN_USAGE, JsonUtils.getTokenUsage(answer)); + JSONObject jsonObject = new JSONObject(); + jsonObject.put(MuleChainConstants.RESPONSE, answer.content()); + jsonObject.put(MuleChainConstants.TOKEN_USAGE, JsonUtils.getTokenUsage(answer)); - return jsonObject.toString(); + return jsonObject.toString(); + } catch (Exception e) { + throw new PromptTemplateException("Unable to reply with the correct prompt template", e); + } } /** @@ -109,24 +122,29 @@ interface SentimentAnalyzer { */ @MediaType(value = ANY, strict = false) @Alias("SENTIMENT-analyze") + @Throws(AiServiceErrorTypeProvider.class) public String extractSentiments(@Config LangchainLLMConfiguration configuration, String data) { - ChatLanguageModel model = configuration.getModel(); + try { + ChatLanguageModel model = configuration.getModel(); - SentimentAnalyzer sentimentAnalyzer = AiServices.create(SentimentAnalyzer.class, model); + SentimentAnalyzer sentimentAnalyzer = AiServices.create(SentimentAnalyzer.class, model); - Result sentiment = sentimentAnalyzer.analyzeSentimentOf(data); - LOGGER.info("Analyzed sentiment: {}", sentiment); // POSITIVE + Result sentiment = sentimentAnalyzer.analyzeSentimentOf(data); + LOGGER.info("Analyzed sentiment: {}", sentiment); // POSITIVE - boolean positive = sentimentAnalyzer.isPositive(data); - LOGGER.info("Is sentiment positive: {}", positive); // false + boolean positive = sentimentAnalyzer.isPositive(data); + LOGGER.info("Is sentiment positive: {}", positive); // false - JSONObject jsonObject = new JSONObject(); - jsonObject.put(MuleChainConstants.SENTIMENT, sentiment.content()); - jsonObject.put(MuleChainConstants.IS_POSITIVE, positive); - jsonObject.put(MuleChainConstants.TOKEN_USAGE, JsonUtils.getTokenUsage(sentiment)); + JSONObject jsonObject = new JSONObject(); + jsonObject.put(MuleChainConstants.SENTIMENT, sentiment.content()); + jsonObject.put(MuleChainConstants.IS_POSITIVE, positive); + jsonObject.put(MuleChainConstants.TOKEN_USAGE, JsonUtils.getTokenUsage(sentiment)); - return jsonObject.toString(); + return jsonObject.toString(); + } catch (Exception e) { + throw new SentimentAnalyzerException("Unable to provide the correct sentiments", e); + } }