From e66c54869c075909310ee2c379747f099a7ed34e Mon Sep 17 00:00:00 2001 From: Jaland Date: Tue, 12 Nov 2024 13:00:13 -0500 Subject: [PATCH] Checkstyle (#3) * Saving checkstyle changes * Checkstyle changes * Adding checkstyle reporting * Adding checkstyle reporting * Adding checkstyle reporting * Adding checkstyle reporting * Adding checkstyle reporting * Adding checkstyle reporting * Adding checkstyle reporting * Testing failed checkstyle * Updating Readme * Fixing Admin API --- .assets/CheckstyleReportExample.png | Bin 0 -> 157153 bytes .../{ci.yaml => build-and-push.yaml} | 0 .github/workflows/reporting.yaml | 46 ++ README.md | 216 ++++++--- checkstyle-suppressions.xml | 9 + checkstyle.xml | 440 ++++++++++++++++++ documentation/ADMIN_WORKFLOW.MD | 3 + documentation/WEAVIATE_SETUP.md | 5 + pom.xml | 87 +++- ...ntAdminAPI.java => AssistantAdminApi.java} | 42 +- .../com/redhat/composer/api/AssistantApi.java | 10 +- .../api/{ChatBotAPI.java => ChatBotApi.java} | 15 +- .../{EmbeddingAPI.java => EmbeddingApi.java} | 12 +- .../composer/api/{LlmAPI.java => LlmApi.java} | 39 +- ...etriverAPI.java => VectorRetriverApi.java} | 30 +- ...idationAPI.java => OidcValidationApi.java} | 8 +- .../validation/SteramingValidationAPI.java | 37 -- ...cesFactory.java => AiServicesFactory.java} | 14 +- .../config/llm/aiservices/BaseAIService.java | 17 - .../config/llm/aiservices/BaseAiService.java | 27 ++ .../llm/aiservices/HealthCareService.java | 39 +- ...AiService.java => Mistral7bAiService.java} | 46 +- ...ngModel.java => OpenAiStreamingModel.java} | 57 ++- .../models/streaming/StreamingBaseModel.java | 10 +- .../streaming/StreamingModelFactory.java | 14 +- .../{OpenAIModel.java => OpenAiModel.java} | 33 +- .../synchronous/SynchronousBaseModel.java | 10 +- .../synchronous/SynchronousModelFactory.java | 14 +- .../BaseContentRetrieverClient.java | 22 +- .../ContentRetrieverClientFactory.java | 15 +- .../Neo4jContentRetrieverClient.java | 16 +- .../WeaviateContentRetrieverClient.java | 44 +- .../custom/WeaviateEmbeddingStoreCustom.java | 3 +- .../BaseLocalEmbeddingModelClient.java | 11 +- .../EmbeddingModelFactory.java | 12 +- .../NomicLocalEmbeddingModelClient.java | 32 +- .../model/enums/ContentRetrieverType.java | 40 +- .../composer/model/mongo/BaseEntity.java | 3 + ...onEntity.java => LlmConnectionEntity.java} | 23 +- .../mongo/RetrieverConnectionEntity.java | 7 +- .../contentRetrieverEntites/Neo4JEntity.java | 8 - .../BaseRetrieverConnectionEntity.java | 24 +- .../contentretrieverentites/Neo4jEntity.java | 12 + .../WeaviateConnectionEntity.java | 9 +- .../model/request/AssistantChatRequest.java | 2 + .../request/AssistantCreationRequest.java | 1 + .../model/request/ChatBotRequest.java | 1 + .../composer/model/request/LLMRequest.java | 1 + .../model/request/RetrieverRequest.java | 1 + .../retriever/BaseRetrieverRequest.java | 1 + .../model/request/retriever/Neo4JRequest.java | 1 + .../request/retriever/WeaviateRequest.java | 1 + .../model/response/AssistantResponse.java | 13 +- .../model/response/ContentResponse.java | 2 +- .../model/response/SourceResponse.java | 1 + .../repositories/AssistantRepository.java | 14 +- .../services/AssistantInfoService.java | 54 ++- .../composer/services/ChatBotService.java | 70 +-- .../composer/services/EmbeddingService.java | 12 +- .../composer/services/RetrieveService.java | 24 +- .../composer/util/DisabledAuthController.java | 19 +- .../composer/util/mappers/MapperUtil.java | 70 ++- .../util/mappers/QuarkusMapperConfig.java | 7 +- .../mappers/RetrieverConnectionMapper.java | 46 +- src/main/resources/application-dev.properties | 2 +- src/main/resources/application.properties | 2 +- 66 files changed, 1479 insertions(+), 427 deletions(-) create mode 100644 .assets/CheckstyleReportExample.png rename .github/workflows/{ci.yaml => build-and-push.yaml} (100%) create mode 100644 .github/workflows/reporting.yaml create mode 100644 checkstyle-suppressions.xml create mode 100644 checkstyle.xml create mode 100644 documentation/WEAVIATE_SETUP.md rename src/main/java/com/redhat/composer/api/{AssistantAdminAPI.java => AssistantAdminApi.java} (58%) rename src/main/java/com/redhat/composer/api/{ChatBotAPI.java => ChatBotApi.java} (68%) rename src/main/java/com/redhat/composer/api/{EmbeddingAPI.java => EmbeddingApi.java} (69%) rename src/main/java/com/redhat/composer/api/{LlmAPI.java => LlmApi.java} (72%) rename src/main/java/com/redhat/composer/api/{VectorRetriverAPI.java => VectorRetriverApi.java} (62%) rename src/main/java/com/redhat/composer/api/validation/{OIDCValidationAPI.java => OidcValidationApi.java} (84%) delete mode 100644 src/main/java/com/redhat/composer/api/validation/SteramingValidationAPI.java rename src/main/java/com/redhat/composer/config/llm/aiservices/{AIServicesFactory.java => AiServicesFactory.java} (66%) delete mode 100644 src/main/java/com/redhat/composer/config/llm/aiservices/BaseAIService.java create mode 100644 src/main/java/com/redhat/composer/config/llm/aiservices/BaseAiService.java rename src/main/java/com/redhat/composer/config/llm/aiservices/{Mistral7BAiService.java => Mistral7bAiService.java} (59%) rename src/main/java/com/redhat/composer/config/llm/models/streaming/{OpenAIStreamingModel.java => OpenAiStreamingModel.java} (51%) rename src/main/java/com/redhat/composer/config/llm/models/synchronous/{OpenAIModel.java => OpenAiModel.java} (70%) rename src/main/java/com/redhat/composer/config/retriever/{contentRetriever => contentretriever}/BaseContentRetrieverClient.java (61%) rename src/main/java/com/redhat/composer/config/retriever/{contentRetriever => contentretriever}/ContentRetrieverClientFactory.java (70%) rename src/main/java/com/redhat/composer/config/retriever/{contentRetriever => contentretriever}/Neo4jContentRetrieverClient.java (82%) rename src/main/java/com/redhat/composer/config/retriever/{contentRetriever => contentretriever}/WeaviateContentRetrieverClient.java (72%) rename src/main/java/com/redhat/composer/config/retriever/{contentRetriever => contentretriever}/custom/WeaviateEmbeddingStoreCustom.java (99%) rename src/main/java/com/redhat/composer/config/retriever/{embeddingModel => embeddingmodel}/BaseLocalEmbeddingModelClient.java (79%) rename src/main/java/com/redhat/composer/config/retriever/{embeddingModel => embeddingmodel}/EmbeddingModelFactory.java (74%) rename src/main/java/com/redhat/composer/config/retriever/{embeddingModel => embeddingmodel}/NomicLocalEmbeddingModelClient.java (52%) rename src/main/java/com/redhat/composer/model/mongo/{LLMConnectionEntity.java => LlmConnectionEntity.java} (80%) delete mode 100644 src/main/java/com/redhat/composer/model/mongo/contentRetrieverEntites/Neo4JEntity.java rename src/main/java/com/redhat/composer/model/mongo/{contentRetrieverEntites => contentretrieverentites}/BaseRetrieverConnectionEntity.java (63%) create mode 100644 src/main/java/com/redhat/composer/model/mongo/contentretrieverentites/Neo4jEntity.java rename src/main/java/com/redhat/composer/model/mongo/{contentRetrieverEntites => contentretrieverentites}/WeaviateConnectionEntity.java (93%) diff --git a/.assets/CheckstyleReportExample.png b/.assets/CheckstyleReportExample.png new file mode 100644 index 0000000000000000000000000000000000000000..c8ebb9dc93443e54f068dbddaae295d66cddf769 GIT binary patch literal 157153 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 KyKCkL#caS~M@_2E6YL!bUnj-OJT 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 z%0DL@5sU7I;omws{WkTdh$B`) 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 [!NOTE] +> Currently working on finding a less restrictive way than `AI Services` in which to perform the orchestration of our calls + +## Chat Bot Endpoints + +### Assistant + +The `assistant/chat/streaming` is the primary entrypoint into the application. This used to specify an assistant + +**Example Message** ```json { "message": "User Message", @@ -17,95 +31,183 @@ The `assistant/chat/streaming` will be the main use endpoint. In which we specif } ``` -Available Assistants by default: -- default_ocp -- default_rhel -- default_rho_2025_faq -- default_ansible -- default_rhoai +#### Default Assistants +The following assistants are loaded into the application by default using liquibase and [changeLog File](src/main/resources/db/changeLog.yml): -The `/chatbot/chat/stream` allows for connections to be specified directly through the UI +| Assistant Name | Description | +|---------------------------|--------------------------------------------------| +| default_ocp | Default assistant for OpenShift Container Platform (OCP) | +| default_rhel | Default assistant for Red Hat Enterprise Linux (RHEL) | +| default_rho_2025_faq | Default assistant for RHO 2025 FAQ | +| default_ansible | Default assistant for Ansible automation | +| default_rhoai | Default assistant for RHO AI | +| default_assistant | General default assistant | + +### Direct Chat + +The `/chatbot/chat/stream` endpoint allows for connections to be specified directly, and can be used for initial testing of connections. ```json - { - message: "User Message", - context: "Message history", - retriverRequest: { - index: "weveIndex", - scheme: "weveSchem", - host: "weavHost.com", - apiKey: "xxx", - } - modelRequest { - modelType: "servingRuntime", - apiKey: "xxxxx", - modelName: "mistral-instruct", - } +{ + "message": "User Message", + "context": "Message history", + "retriverRequest": { + "index": "weveIndex", + "scheme": "weveScheme", + "host": "weavHost.com", + "apiKey": "xxx" + }, + "modelRequest": { + "modelType": "servingRuntime", + "apiKey": "xxxxx", + "modelName": "mistral-instruct" } +} ``` -## Assistant Workflow +## Local Development -The main workflow will be through assistants. These will be created by administrators and once created can be accessed by users. +Use the following commands to run locally: -```json - { - message: "User Message", - context: "Message history", - assistantName: "Name of Assistant", - assistantId: "Id of Assistant" // Ignored in name is provided - } +```sh +mvn clean install +# Setting the profile to to use the `application-local.properties` as explained below +mvn quarkus:dev -Dquarkus.profile=local ``` +> [!TIP] +> Recommended that properties below are set in the `application-local.properties` file which is get ignored. This will prevent any accidental check-ins of secret information -> Important: This is assuming access to a default weaviate db with the correct indexes populated. +### LLM Connection -Test Curl Command: -```json -curl -X 'POST' 'http://localhost:8080/assistant/chat/streaming' -H 'Content-Type: application/json' -d '{ - "message": "What is this product?", - "assistantName": "default_rhoai" -}' -N +The following properties should be set in order to connect to properly connect to your LLM running on an OpenAI instance: +```properties +openai.default.url=/v1 +openai.default.apiKey= # If Required +openai.default.modelName= ``` -## Install Locally +> [!TIP] +> The `default_assistant` can be used without having to configure a rag data source + +### Weaviate Setup -To install locally run: +The default assistants all assume a connection to a Weaviate DB for RAG purposes. + +A locally hosted Weaviate can be deployed and used, more information found [here(TBD)](documentation/WEAVIATE_SETUP.md) + +If a remote instance of weaviate exist on an OpenShift cluster and has the correct indexes, that instance can be used with the following port forward commands: ```sh -mvn clean install -mvn quarkus:dev +oc project $PROJECT +oc port-forward service/weaviate-vector-db 8086:8080 50051:50051 ``` -Need to login to Openshift using `oc login` and run the following command to port forward the vector DB +Once forwarded the following values can be changed + +```properties +weaviate.default.scheme=http +weaviate.default.host=localhost:8086 +weaviate.default.apiKey= +``` + +> If using the App of Apps repo the API key is retrieved from autogenerated secret `weaviate-api-key-secret` + +## Embedding Model + +Currently the supported models are added to the resources folder and [loaded directly](src/main/java/com/redhat/composer/config/retriever/embeddingmodel/NomicLocalEmbeddingModelClient.java). We would like to move this logic to pull these models using maven as seen [here](https://docs.langchain4j.dev/category/embedding-models) + +> [!IMPORTANT] +> The embedding model is too large to check into our repo. +> Download it from [huggingface](https://huggingface.co/nomic-ai/nomic-embed-text-v1/resolve/main/onnx/model_quantized.onnx?download=true) or [here](https://drive.google.com/drive/folders/1jZe0cEw8p_E-fghd6IFPjwiabDNAhtp7?usp=drive_link) if internal to RH. +> Then add it to `resources/embedding/nomic` with the name `model.onnx`, it should be gitignored if done correctly. + +## Local Curl + +If the LLM Connection has been setup correctly the following curl command should stream a response from your LLM ```sh -oc port-forward service/weaviate-vector-db 8086:8080 50051:50051 +curl -X 'POST' 'http://localhost:8080/assistant/chat/streaming' -H 'Content-Type: application/json' -d '{ + "message": "What is this product?", + "assistantName": "default_assistant" +}' -N ``` -## Test Locally +The `assistantName` can be swapped out for other assistants inside of the table above, but the other assistants will required a connection to a weaviate db with the correct indexes. The App Of Apps repository contains a [validation script](https://github.com/redhat-composer-ai/appOfApps/blob/main/data-ingestion/weaviate/validation.sh) that can be used to show which indexes currently exist. -The following curl command should return a streaming output answering based on the default Rag/LLM settings (set in the properties file) +## Admin Flow -`curl -X POST -H "Content-Type: application/json" -d '{"message":"What is a route?"}' http://localhost:8080/chatbot/chat/streaming -N` +Information about the creation/updating of Assistants, ContentRetrievers, and LLMs can be found in the [admin flow docs](documentation/ADMIN_WORKFLOW.MD) -> Note: There are endpoint for testing each of the specific components +## Contributing -## Embedding Model +We welcome contributions from the community\! Here's how you can get involved: + +**1. Find an Issue:** + + * Check the [issue tracker](https://github.com/redhat-composer-ai/quarkus-llm-router/issues) or [jira board](https://issues.redhat.com/secure/RapidBoard.jspa?projectKey=REDSAIA&rapidView=20236) for open issues that interest you. + * If you find a bug or have a feature request, please open a new issue with a clear description. + +> [!NOTE] +> Currently this project is primarlly tracking using Red Hat's internal Jira. + +**2. Fork the Repository:** + + * Fork this repository to your own GitHub account. + +**3. Create a Branch:** + + * Create a new branch for your changes. Use a descriptive name that reflects the issue or feature you're working on (e.g., `fix-issue-123` or `add-new-feature`). + +**4. Make Changes:** + + * Make your desired changes to the codebase. + * Follow the existing code style and conventions. + * Write clear commit messages that explain the purpose of your changes. + +**5. Test Your Changes:** + + * Thoroughly test your changes to ensure they work as expected. + * If there are existing tests, make sure they all pass. Consider adding new tests to cover your changes. + +**6. Code Style and Formatting:** + + * Ensure your code adheres to the project's established code style guidelines. + * This project uses Checkstyle's automated code formatting tools. Your code must pass these checks before it can be merged. + + +> [!TIP] +> Checkstyle can be run locally using `mvn site` which creates a report page under `target/site`. +> +> It is also recommended that you use a checkstyle tool in your IDE such as this [VS Code Plugin](https://marketplace.visualstudio.com/items?itemName=shengchen.vscode-checkstyle) in order order to adhear to guidelines as you code. + +**7. Open a Pull Request:** + + * Push your branch to your forked repository. + * Open a pull request to the main repository. + * In the pull request description, clearly explain the changes you've made and reference the related issue (if applicable). + * Validate that all automate checks are passing + +**8. Review Process:** + + * Your pull request will be reviewed by the project maintainers. + * Feel free to ping in our general slack channel asking for approval/assistants + * Be prepared to address any feedback or questions. + * Once your code has passed all automated checks and received at least one approval from a maintainer, it will be merged. -IMPORTANT: The embedding modes is to large to check into our repo. So you need to download from the following link and put it in `resources/embedding/nomic`. -https://drive.google.com/drive/folders/1jZe0cEw8p_E-fghd6IFPjwiabDNAhtp7?usp=drive_link +**Important Notes:** -We need to figure out a better way to handel this in the future (or put this on github) + * All code contributions must pass automated code scanning checks before they can be merged. + * At least one approval from a maintainer is required for all pull requests. -## Code Standards +**Thank you for your contributions\!** -### OWasp Security Scanning +### Security Scanning -The [OWASP Dependency-Check Plugin](https://owasp.org/www-project-dependency-check/) can be run using the following command: +The [OWASP Dependency-Check Plugin](https://owasp.org/www-project-dependency-check/) not required to pass but is included, we ask that the scanner be run if any changes are made to the dependencies. ```sh mvn validate -P security-scanner diff --git a/checkstyle-suppressions.xml b/checkstyle-suppressions.xml new file mode 100644 index 0000000..cbdf916 --- /dev/null +++ b/checkstyle-suppressions.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/checkstyle.xml b/checkstyle.xml new file mode 100644 index 0000000..d00b19a --- /dev/null +++ b/checkstyle.xml @@ -0,0 +1,440 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/documentation/ADMIN_WORKFLOW.MD b/documentation/ADMIN_WORKFLOW.MD index 828ebb2..fc9cb3c 100644 --- a/documentation/ADMIN_WORKFLOW.MD +++ b/documentation/ADMIN_WORKFLOW.MD @@ -22,3 +22,6 @@ Backend includes the ability to dynamically create Assistants, Rag Connections, "description": "Example of a rag connection" } ``` + +> [!NOTE] +> This document is a work in progress. diff --git a/documentation/WEAVIATE_SETUP.md b/documentation/WEAVIATE_SETUP.md new file mode 100644 index 0000000..9ee1c40 --- /dev/null +++ b/documentation/WEAVIATE_SETUP.md @@ -0,0 +1,5 @@ +# Weaviate Setup + +Information about setting up a local weaviate can be found [here](https://weaviate.io/developers/weaviate/quickstart/local). + +More to come on local data ingestion. diff --git a/pom.xml b/pom.xml index 8b67b4e..dafae10 100644 --- a/pom.xml +++ b/pom.xml @@ -18,6 +18,8 @@ 0.35.0 0.21.0.CR1 1.6.2 + 11.1.0 + 3.6.0 @@ -140,6 +142,29 @@ + + + + + org.apache.maven.plugins + maven-site-plugin + 4.0.0-M16 + + + org.apache.maven.plugins + maven-checkstyle-plugin + 3.6.0 + + + com.puppycrawl.tools + checkstyle + 10.20.1 + + + + + + ${quarkus.platform.group-id} @@ -192,7 +217,25 @@ ${maven.home} - + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + ${checkstyle-maven-plugin.version} + + checkstyle.xml + checkstyle-suppressions.xml + + + + + check + + + + @@ -209,5 +252,47 @@ true + + + + security-scanner + + + + org.owasp + dependency-check-maven + ${owasp-dependency-check-plugin.version} + + + + check + + + + + + 7 + + ${project.basedir}/dependency-cpe-suppression.xml + + + + + + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + ${checkstyle-maven-plugin.version} + + checkstyle.xml + checkstyle-suppressions.xml + + + + diff --git a/src/main/java/com/redhat/composer/api/AssistantAdminAPI.java b/src/main/java/com/redhat/composer/api/AssistantAdminApi.java similarity index 58% rename from src/main/java/com/redhat/composer/api/AssistantAdminAPI.java rename to src/main/java/com/redhat/composer/api/AssistantAdminApi.java index 68ec838..d270f09 100644 --- a/src/main/java/com/redhat/composer/api/AssistantAdminAPI.java +++ b/src/main/java/com/redhat/composer/api/AssistantAdminApi.java @@ -3,7 +3,7 @@ import java.util.List; import com.redhat.composer.model.mongo.AssistantEntity; -import com.redhat.composer.model.mongo.LLMConnectionEntity; +import com.redhat.composer.model.mongo.LlmConnectionEntity; import com.redhat.composer.model.mongo.RetrieverConnectionEntity; import com.redhat.composer.model.request.AssistantCreationRequest; import com.redhat.composer.model.request.LLMRequest; @@ -17,42 +17,72 @@ import jakarta.ws.rs.POST; import jakarta.ws.rs.Path; +/** + * Admin API for Creating and Managing Assistants. + */ @Path("/admin/assistant") @Authenticated -public class AssistantAdminAPI { +public class AssistantAdminApi { @Inject AssistantInfoService assistantService; + /** + * Create a LLM Connection. + * @param request the LLMRequest + * @return the LLMConnectionEntity + */ @POST @Path("llm") - public LLMConnectionEntity createLLM(LLMRequest request) { - return assistantService.createLLMConnection(request); + public LlmConnectionEntity createLlm(LLMRequest request) { + return assistantService.createLlmConnection(request); } + /** + * Get all LLM Connections. + * @return the list of LLMConnectionEntity + */ @GET @Path("llm") - public List getLLMs() { - return assistantService.getLLMConnections(); + public List getLlms() { + return assistantService.getLlmConnections(); } + /** + * Create a Retriever Connection. + * @param request the RetrieverRequest + * @return the RetrieverConnectionEntity + */ @POST @Path("retrieverConnection") public RetrieverConnectionEntity createRetrieverConnection(RetrieverRequest request) { return assistantService.createRetrieverConnectionEntity(request); } + /** + * Get all Retriever Connections. + * @return the list of RetrieverConnectionEntity + */ @GET @Path("retrieverConnection") public List getRetrieverConnection() { return assistantService.getRetrieverConnections(); } + /** + * Create an Assistant. + * @param request the AssistantCreationRequest + * @return the AssistantEntity + */ @POST public AssistantEntity createAssistant(AssistantCreationRequest request) { return assistantService.createAssistant(request); } + /** + * Get all Assistants. + * @return the list of AssistantResponse + */ @GET public List getAssistant() { return assistantService.getAssistant(); diff --git a/src/main/java/com/redhat/composer/api/AssistantApi.java b/src/main/java/com/redhat/composer/api/AssistantApi.java index 74aa839..96e7aa6 100644 --- a/src/main/java/com/redhat/composer/api/AssistantApi.java +++ b/src/main/java/com/redhat/composer/api/AssistantApi.java @@ -13,6 +13,9 @@ import jakarta.ws.rs.Path; import jakarta.ws.rs.core.MediaType; +/** + * Assistant API for Chatting using assistants. + */ @Path("/assistant/chat") @Authenticated public class AssistantApi { @@ -23,11 +26,16 @@ public class AssistantApi { ChatBotService chatBotService; + /** + * Chat with an assistant. + * @param input the AssistantChatRequest + * @return the Multi of String + */ @POST @Path("streaming") @Consumes(MediaType.APPLICATION_JSON) public Multi chat(AssistantChatRequest input) { - return chatBotService.chat(input); + return chatBotService.chat(input); } } diff --git a/src/main/java/com/redhat/composer/api/ChatBotAPI.java b/src/main/java/com/redhat/composer/api/ChatBotApi.java similarity index 68% rename from src/main/java/com/redhat/composer/api/ChatBotAPI.java rename to src/main/java/com/redhat/composer/api/ChatBotApi.java index 1d153a0..581e946 100644 --- a/src/main/java/com/redhat/composer/api/ChatBotAPI.java +++ b/src/main/java/com/redhat/composer/api/ChatBotApi.java @@ -13,21 +13,28 @@ import jakarta.ws.rs.Path; import jakarta.ws.rs.core.MediaType; +/** + * ChatBotAPI for Chatting using ChatBots. + */ @Path("/chatbot/chat") @Authenticated -public class ChatBotAPI { +public class ChatBotApi { - Logger log = Logger.getLogger(ChatBotAPI.class); + Logger log = Logger.getLogger(ChatBotApi.class); @Inject ChatBotService chatBotService; - + /** + * Chat with a ChatBot. + * @param input ChatBotRequest infromation + * @return Streamed response + */ @POST @Path("streaming") @Consumes(MediaType.APPLICATION_JSON) public Multi chat(ChatBotRequest input) { - return chatBotService.chat(input); + return chatBotService.chat(input); } } diff --git a/src/main/java/com/redhat/composer/api/EmbeddingAPI.java b/src/main/java/com/redhat/composer/api/EmbeddingApi.java similarity index 69% rename from src/main/java/com/redhat/composer/api/EmbeddingAPI.java rename to src/main/java/com/redhat/composer/api/EmbeddingApi.java index 6fa0da2..cfe844c 100644 --- a/src/main/java/com/redhat/composer/api/EmbeddingAPI.java +++ b/src/main/java/com/redhat/composer/api/EmbeddingApi.java @@ -12,20 +12,26 @@ import jakarta.ws.rs.core.MediaType; /** - * Api For Testing Embedding + * Api For Testing Embedding. */ @Path("/embedding") @Authenticated -public class EmbeddingAPI { +public class EmbeddingApi { @Inject EmbeddingService embeddingService; + /** + * Embedd a string. + * @param text the text to embedd + * @param embeddingType the type of embedding to use + * @return the embedded string + */ @POST @Path("{embeddingType}") @Consumes(MediaType.TEXT_PLAIN) @Produces(MediaType.TEXT_PLAIN) public String embeddString(String text, @PathParam("embeddingType") String embeddingType) { - return embeddingService.embedding(text,embeddingType).toString(); + return embeddingService.embedding(text, embeddingType).toString(); } } diff --git a/src/main/java/com/redhat/composer/api/LlmAPI.java b/src/main/java/com/redhat/composer/api/LlmApi.java similarity index 72% rename from src/main/java/com/redhat/composer/api/LlmAPI.java rename to src/main/java/com/redhat/composer/api/LlmApi.java index a9b492d..140c6ee 100644 --- a/src/main/java/com/redhat/composer/api/LlmAPI.java +++ b/src/main/java/com/redhat/composer/api/LlmApi.java @@ -22,12 +22,12 @@ import jakarta.ws.rs.core.MediaType; /** - * Api For Testing Embedding + * Api For Testing the LLM. */ @Path("/llm") -public class LlmAPI { +public class LlmApi { - Logger log = Logger.getLogger(LlmAPI.class); + Logger log = Logger.getLogger(LlmApi.class); @Inject SynchronousModelFactory synchronousModelFactory; @@ -35,6 +35,12 @@ public class LlmAPI { @Inject StreamingModelFactory streamingModelFactory; + /** + * Generate a response for a message. + * @param request the LLMRequest + * @param message message from LLM + * @return the response + */ @POST @Path("/generate") @Authenticated @@ -48,6 +54,12 @@ public String syncRequest(LLMRequest request, @QueryParam("message") String mess return llm.generate(message); } + /** + * Generate a response for a message. + * @param request the LLMRequest + * @param message streaming message from LLM + * @return the response + */ @POST @Path("/generate/streaming") public Multi streamingRequest(LLMRequest request, @QueryParam("message") String message) { @@ -64,27 +76,6 @@ public Multi streamingRequest(LLMRequest request, @QueryParam("message") return response; } - - // Don't think we want to use this. - @POST - @Produces(MediaType.SERVER_SENT_EVENTS) - @RestStreamElementType("text/plain") - @Path("/generate/streaming/sse") - public Multi streamingRequestSSE(LLMRequest request, @QueryParam("message") String message) { - log.info("Generating response for message: " + message); - if (request == null) { - request = new LLMRequest(); - } - StreamingBaseModel model = streamingModelFactory.getModel(request.getModelType()); - - StreamingChatLanguageModel llm = model.getChatModel(request); - Assistant assistant = AiServices.create(Assistant.class, llm); - - Multi response = assistant.chat(message); - return response; - } - - interface Assistant { Multi chat(String message); } diff --git a/src/main/java/com/redhat/composer/api/VectorRetriverAPI.java b/src/main/java/com/redhat/composer/api/VectorRetriverApi.java similarity index 62% rename from src/main/java/com/redhat/composer/api/VectorRetriverAPI.java rename to src/main/java/com/redhat/composer/api/VectorRetriverApi.java index 9bfe3e7..2a23191 100644 --- a/src/main/java/com/redhat/composer/api/VectorRetriverAPI.java +++ b/src/main/java/com/redhat/composer/api/VectorRetriverApi.java @@ -16,11 +16,11 @@ import jakarta.ws.rs.QueryParam; /** - * Api For Testing Store Retrievers + * Api For Testing Store Retrievers. */ @Path("/retriver") @Authenticated -public class VectorRetriverAPI { +public class VectorRetriverApi { @Inject RetrieveService retrieveService; @@ -28,21 +28,25 @@ public class VectorRetriverAPI { @Inject MapperUtil mapperUtil; + /** + * Retrieve sources. + * @param request the RetrieverRequest + * @param message the message + * @return the list of SourceResponse + */ @POST @Path("/sources") - public List retrieveSources(RetrieverRequest request, @QueryParam("message") String message) { - return retrieveService.retrieveContent(request, message).stream().map(VectorRetriverAPI::toSourceResponse).toList(); - } - - @POST - @Path("/sources/metadata") - public List> retrieveSourceMetadata(RetrieverRequest request, @QueryParam("message") String message) { - return retrieveService.retrieveContent(request, message) - .stream() - .map(content -> content.textSegment().metadata().toMap()) // Accessing metadata inside textSegment - .toList(); + public List retrieveSources(RetrieverRequest request, + @QueryParam("message") String message) { + return retrieveService.retrieveContent(request, message).stream() + .map(VectorRetriverApi::toSourceResponse).toList(); } + /** + * Retrieve sources. + * @param content the Content returned from the retriever + * @return source info + */ public static SourceResponse toSourceResponse(Content content) { SourceResponse response = new SourceResponse(); response.setContent(content.textSegment().text()); diff --git a/src/main/java/com/redhat/composer/api/validation/OIDCValidationAPI.java b/src/main/java/com/redhat/composer/api/validation/OidcValidationApi.java similarity index 84% rename from src/main/java/com/redhat/composer/api/validation/OIDCValidationAPI.java rename to src/main/java/com/redhat/composer/api/validation/OidcValidationApi.java index cdf2b91..7cdce56 100644 --- a/src/main/java/com/redhat/composer/api/validation/OIDCValidationAPI.java +++ b/src/main/java/com/redhat/composer/api/validation/OidcValidationApi.java @@ -1,6 +1,5 @@ package com.redhat.composer.api.validation; -import java.util.Map; import java.util.Set; import io.quarkus.security.Authenticated; @@ -9,9 +8,12 @@ import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; +/** + * OIDCValidationAPI. + */ @Path("/auth") @Authenticated -public class OIDCValidationAPI { +public class OidcValidationApi { @Inject SecurityIdentity securityIdentity; @@ -26,7 +28,7 @@ public String getPrincipal() { @GET @Path("roles") - public Set getRoles() { + public Set getRoles() { return securityIdentity.getRoles(); } diff --git a/src/main/java/com/redhat/composer/api/validation/SteramingValidationAPI.java b/src/main/java/com/redhat/composer/api/validation/SteramingValidationAPI.java deleted file mode 100644 index 343478c..0000000 --- a/src/main/java/com/redhat/composer/api/validation/SteramingValidationAPI.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.redhat.composer.api.validation; - -import java.util.Random; - -import org.jboss.resteasy.reactive.RestStreamElementType; - -import io.quarkus.security.Authenticated; -import io.smallrye.mutiny.Multi; -import jakarta.ws.rs.GET; -import jakarta.ws.rs.Path; -import jakarta.ws.rs.Produces; -import jakarta.ws.rs.core.MediaType; - -@Path("/streaming") -@Authenticated -public class SteramingValidationAPI { - - - @Path("basic") - public Multi streamBasic() { - return Multi.createFrom().ticks().every(java.time.Duration.ofSeconds(1)) - .onItem().transform(n -> getRandomLetter()); - } - - - @GET - @Path("basic") - public String getBasic() { - return "Hello"; - } - - private String getRandomLetter() { - Random r = new Random(); - return String.valueOf((char) (r.nextInt(26) + 'a')); - } - -} diff --git a/src/main/java/com/redhat/composer/config/llm/aiservices/AIServicesFactory.java b/src/main/java/com/redhat/composer/config/llm/aiservices/AiServicesFactory.java similarity index 66% rename from src/main/java/com/redhat/composer/config/llm/aiservices/AIServicesFactory.java rename to src/main/java/com/redhat/composer/config/llm/aiservices/AiServicesFactory.java index ddfc07d..b535ae6 100644 --- a/src/main/java/com/redhat/composer/config/llm/aiservices/AIServicesFactory.java +++ b/src/main/java/com/redhat/composer/config/llm/aiservices/AiServicesFactory.java @@ -2,8 +2,11 @@ import jakarta.enterprise.context.ApplicationScoped; +/** + * Factory for AI Services. + */ @ApplicationScoped -public class AIServicesFactory { +public class AiServicesFactory { public static final String MISTRAL7B_AI_SERVICE = "mistral7b"; @@ -11,10 +14,15 @@ public class AIServicesFactory { public static final String HEALTHCARE_SERVICE = "healthcare"; - public Class getAiService(String aiServiceType) { + /** + * Get the AI service class. + * @param aiServiceType the AI service type + * @return the AI service class + */ + public Class getAiService(String aiServiceType) { switch (aiServiceType) { case MISTRAL7B_AI_SERVICE: - return Mistral7BAiService.class; + return Mistral7bAiService.class; case HEALTHCARE_SERVICE: return HealthCareService.class; default: diff --git a/src/main/java/com/redhat/composer/config/llm/aiservices/BaseAIService.java b/src/main/java/com/redhat/composer/config/llm/aiservices/BaseAIService.java deleted file mode 100644 index b5365b8..0000000 --- a/src/main/java/com/redhat/composer/config/llm/aiservices/BaseAIService.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.redhat.composer.config.llm.aiservices; - -import dev.langchain4j.service.TokenStream; -import io.quarkiverse.langchain4j.RegisterAiService; -import io.smallrye.mutiny.Multi; - -/** - * Mistral7BAiService - */ -public interface BaseAIService { - - // TokenStream should be used when possible - TokenStream chatToken(String context, String input); - - Multi chatStream(String context, String input); - -} \ No newline at end of file diff --git a/src/main/java/com/redhat/composer/config/llm/aiservices/BaseAiService.java b/src/main/java/com/redhat/composer/config/llm/aiservices/BaseAiService.java new file mode 100644 index 0000000..40e7f20 --- /dev/null +++ b/src/main/java/com/redhat/composer/config/llm/aiservices/BaseAiService.java @@ -0,0 +1,27 @@ +package com.redhat.composer.config.llm.aiservices; + +import dev.langchain4j.service.TokenStream; +import io.smallrye.mutiny.Multi; + +/** + * Base AI Service Interface. + */ +public interface BaseAiService { + + /** + * Returns TokenStream given input. + * @param context Context information such as chat history and source information + * @param input User Message + * @return the TokenStream + */ + TokenStream chatToken(String context, String input); + + /** + * Returns a Multi of String given input. + * @param context Context information such as chat history and source information + * @param input User Message + * @return the Multi of String + */ + Multi chatStream(String context, String input); + +} \ No newline at end of file diff --git a/src/main/java/com/redhat/composer/config/llm/aiservices/HealthCareService.java b/src/main/java/com/redhat/composer/config/llm/aiservices/HealthCareService.java index 6444d1e..b15ea11 100644 --- a/src/main/java/com/redhat/composer/config/llm/aiservices/HealthCareService.java +++ b/src/main/java/com/redhat/composer/config/llm/aiservices/HealthCareService.java @@ -6,16 +6,15 @@ import io.smallrye.mutiny.Multi; /** - * Mistral7BAiService + * Mistral7BAiService. */ +public interface HealthCareService extends BaseAiService { -public interface HealthCareService extends BaseAIService { - - final static String systemMessage = """ + static final String systemMessage = """ You are a helpful, respectful and honest assistant answering questions about healthcare. -"""; + """; - final static String userMessage =""" + static final String userMessage = """ {context} @@ -24,16 +23,22 @@ public interface HealthCareService extends BaseAIService { Question: {input} <|eot_id|> <|start_header_id|>assistant<|end_header_id|> -"""; - - @SystemMessage(systemMessage) - @UserMessage(userMessage) - TokenStream chatToken(String context, String input); - - - - @SystemMessage(systemMessage) - @UserMessage(userMessage) - Multi chatStream(String context, String input); + """; + + /** + * Returns TokenStream given input. + * @param context Context information such as chat history and source information + */ + @SystemMessage(systemMessage) + @UserMessage(userMessage) + TokenStream chatToken(String context, String input); + + + /** + * Returns a Multi of String given input. + */ + @SystemMessage(systemMessage) + @UserMessage(userMessage) + Multi chatStream(String context, String input); } \ No newline at end of file diff --git a/src/main/java/com/redhat/composer/config/llm/aiservices/Mistral7BAiService.java b/src/main/java/com/redhat/composer/config/llm/aiservices/Mistral7bAiService.java similarity index 59% rename from src/main/java/com/redhat/composer/config/llm/aiservices/Mistral7BAiService.java rename to src/main/java/com/redhat/composer/config/llm/aiservices/Mistral7bAiService.java index 2a3e79c..46662af 100644 --- a/src/main/java/com/redhat/composer/config/llm/aiservices/Mistral7BAiService.java +++ b/src/main/java/com/redhat/composer/config/llm/aiservices/Mistral7bAiService.java @@ -5,13 +5,14 @@ import dev.langchain4j.service.UserMessage; import io.smallrye.mutiny.Multi; + /** - * Mistral7BAiService + * Mistral7BAiService. */ +@SuppressWarnings("LineLengthCheck") +public interface Mistral7bAiService extends BaseAiService { -public interface Mistral7BAiService extends BaseAIService { - - final static String systemMessage = """ + static final String systemMessage = """ You are a helpful, respectful and honest assistant answering questions about products from the company called Red Hat. You will be given a question you need to answer about this product. If a question is about a specific product you will be given the product name and version, and references to provide you with additional information. @@ -21,9 +22,9 @@ public interface Mistral7BAiService extends BaseAIService { Please ensure that your responses are socially unbiased and positive in nature. If a question does not make any sense, or is not factually coherent, explain why instead of answering something not correct. If you don't know the answer to a question, please don't share false information. -"""; + """; - final static String userMessage =""" + static final String userMessage = """ {context} @@ -32,16 +33,27 @@ public interface Mistral7BAiService extends BaseAIService { Question: {input} <|eot_id|> <|start_header_id|>assistant<|end_header_id|> -"""; - - @SystemMessage(systemMessage) - @UserMessage(userMessage) - TokenStream chatToken(String context, String input); - - - - @SystemMessage(systemMessage) - @UserMessage(userMessage) - Multi chatStream(String context, String input); + """; + + /** + * Returns TokenStream given input. + * @param context Context information such as chat history and source information + * @param input User Message + * @return the TokenStream + */ + @SystemMessage(systemMessage) + @UserMessage(userMessage) + TokenStream chatToken(String context, String input); + + + /** + * Returns a Multi of String given input. + * @param context Context information such as chat history and source information + * @param input User Message + * @return the Multi of String + */ + @SystemMessage(systemMessage) + @UserMessage(userMessage) + Multi chatStream(String context, String input); } \ No newline at end of file diff --git a/src/main/java/com/redhat/composer/config/llm/models/streaming/OpenAIStreamingModel.java b/src/main/java/com/redhat/composer/config/llm/models/streaming/OpenAiStreamingModel.java similarity index 51% rename from src/main/java/com/redhat/composer/config/llm/models/streaming/OpenAIStreamingModel.java rename to src/main/java/com/redhat/composer/config/llm/models/streaming/OpenAiStreamingModel.java index dba2143..b9eb6f8 100644 --- a/src/main/java/com/redhat/composer/config/llm/models/streaming/OpenAIStreamingModel.java +++ b/src/main/java/com/redhat/composer/config/llm/models/streaming/OpenAiStreamingModel.java @@ -3,7 +3,7 @@ import org.eclipse.microprofile.config.inject.ConfigProperty; import org.jboss.logging.Logger; -import com.redhat.composer.config.retriever.contentRetriever.WeaviateContentRetrieverClient; +import com.redhat.composer.config.retriever.contentretriever.WeaviateContentRetrieverClient; import com.redhat.composer.model.request.LLMRequest; import dev.langchain4j.model.chat.StreamingChatLanguageModel; @@ -11,13 +11,15 @@ import dev.langchain4j.model.openai.OpenAiStreamingChatModel.OpenAiStreamingChatModelBuilder; import jakarta.enterprise.context.ApplicationScoped; - +/** + * OpenAI Streaming Model. + */ @ApplicationScoped -public class OpenAIStreamingModel extends StreamingBaseModel { +public class OpenAiStreamingModel extends StreamingBaseModel { Logger log = Logger.getLogger(WeaviateContentRetrieverClient.class); - @ConfigProperty( name = "openai.default.url") + @ConfigProperty(name = "openai.default.url") private String mistralDefaultUrl; @ConfigProperty(name = "openai.default.apiKey") @@ -29,28 +31,35 @@ public class OpenAIStreamingModel extends StreamingBaseModel { @ConfigProperty(name = "openai.default.temp") private double openaiDefaultTemp; - + /** + * Get the Chat Model. + * @param request the LLMRequest + * @return the StreamingChatLanguageModel + */ public StreamingChatLanguageModel getChatModel(LLMRequest request) { - log.info("Attempting to create OpenAI Streaming Chat Model at: " + request.getUrl() + " with model name: " + request.getModelName()); + + log.info("Attempting to create OpenAI Streaming Chat Model at: " + request.getUrl() + + " with model name: " + request.getModelName()); + OpenAiStreamingChatModelBuilder builder = OpenAiStreamingChatModel.builder(); - builder.baseUrl(request.getUrl() == null ? mistralDefaultUrl : request.getUrl()); - builder.apiKey(request.getApiKey() == null ? mistralDefaultApiKey : request.getApiKey()); - - builder.modelName(request.getModelName() == null ? mistralDefaultModelName : request.getModelName()); - - // TODO: Add all the following to the request - builder.temperature(openaiDefaultTemp); - - // TODO: Fill all this out - // if (modelName != null) { - // builder.modelName(modelName); - // } - // if (maxTokens != null) { - // builder.maxTokens(maxTokens); - // } - // if (safePrompt != null) { - // builder.safePrompt(safePrompt); - // } + builder.baseUrl(request.getUrl() == null ? mistralDefaultUrl : request.getUrl()); + builder.apiKey(request.getApiKey() == null ? mistralDefaultApiKey : request.getApiKey()); + + builder.modelName(request.getModelName() == null ? mistralDefaultModelName : request.getModelName()); + + // TODO: Add all the following to the request + builder.temperature(openaiDefaultTemp); + + // TODO: Fill all this out + // if (modelName != null) { + // builder.modelName(modelName); + // } + // if (maxTokens != null) { + // builder.maxTokens(maxTokens); + // } + // if (safePrompt != null) { + // builder.safePrompt(safePrompt); + // } return builder.build(); } diff --git a/src/main/java/com/redhat/composer/config/llm/models/streaming/StreamingBaseModel.java b/src/main/java/com/redhat/composer/config/llm/models/streaming/StreamingBaseModel.java index c652890..6581542 100644 --- a/src/main/java/com/redhat/composer/config/llm/models/streaming/StreamingBaseModel.java +++ b/src/main/java/com/redhat/composer/config/llm/models/streaming/StreamingBaseModel.java @@ -6,9 +6,17 @@ import dev.langchain4j.model.chat.StreamingChatLanguageModel; +/** + * Streaming Base Model. + */ public class StreamingBaseModel { + /** + * Get Chat Model. + * @param request the LLMRequest + * @return the StreamingChatLanguageModel + */ public StreamingChatLanguageModel getChatModel(LLMRequest request) { - throw new NotImplementedException("Not implemented"); + throw new NotImplementedException("Not implemented"); } } diff --git a/src/main/java/com/redhat/composer/config/llm/models/streaming/StreamingModelFactory.java b/src/main/java/com/redhat/composer/config/llm/models/streaming/StreamingModelFactory.java index e0b0091..f2b4fac 100644 --- a/src/main/java/com/redhat/composer/config/llm/models/streaming/StreamingModelFactory.java +++ b/src/main/java/com/redhat/composer/config/llm/models/streaming/StreamingModelFactory.java @@ -3,24 +3,32 @@ import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; +/** + * Streaming Model Factory. + */ @ApplicationScoped public class StreamingModelFactory { @Inject - OpenAIStreamingModel openAIModel; + OpenAiStreamingModel openAiModel; public static final String OPENAI_MODEL = "openai"; // TODO: Make this configurable public static final String DEFAULT_MODEL = OPENAI_MODEL; + /** + * Get the model. + * @param modelType the model type + * @return the model + */ public StreamingBaseModel getModel(String modelType) { - if(modelType == null) { + if (modelType == null) { modelType = DEFAULT_MODEL; } switch (modelType) { case OPENAI_MODEL: - return openAIModel; + return openAiModel; default: throw new RuntimeException("Model type not found: " + modelType); } diff --git a/src/main/java/com/redhat/composer/config/llm/models/synchronous/OpenAIModel.java b/src/main/java/com/redhat/composer/config/llm/models/synchronous/OpenAiModel.java similarity index 70% rename from src/main/java/com/redhat/composer/config/llm/models/synchronous/OpenAIModel.java rename to src/main/java/com/redhat/composer/config/llm/models/synchronous/OpenAiModel.java index c26fffb..8444d5b 100644 --- a/src/main/java/com/redhat/composer/config/llm/models/synchronous/OpenAIModel.java +++ b/src/main/java/com/redhat/composer/config/llm/models/synchronous/OpenAiModel.java @@ -9,11 +9,13 @@ import dev.langchain4j.model.openai.OpenAiChatModel.OpenAiChatModelBuilder; import jakarta.enterprise.context.ApplicationScoped; - +/** + * OpenAI Model. + */ @ApplicationScoped -public class OpenAIModel extends SynchronousBaseModel { +public class OpenAiModel extends SynchronousBaseModel { - @ConfigProperty( name = "openai.default.url") + @ConfigProperty(name = "openai.default.url") private String mistralDefaultUrl; @ConfigProperty(name = "openai.default.apiKey") @@ -26,6 +28,11 @@ public class OpenAIModel extends SynchronousBaseModel { private double openaiDefaultTemp; + /** + * Get the Chat Model. + * @param request the LLMRequest + * @return the ChatLanguageModel + */ public ChatLanguageModel getChatModel(LLMRequest request) { OpenAiChatModelBuilder builder = OpenAiChatModel.builder(); builder.baseUrl(request.getUrl() == null ? mistralDefaultUrl : request.getUrl()); @@ -36,16 +43,16 @@ public ChatLanguageModel getChatModel(LLMRequest request) { // TODO: Add all the following to the request builder.temperature(openaiDefaultTemp); - // Model names can be derived from MistralAiChatModelName enum - // if (modelName != null) { - // builder.modelName(modelName); - // } - // if (maxTokens != null) { - // builder.maxTokens(maxTokens); - // } - // if (safePrompt != null) { - // builder.safePrompt(safePrompt); - // } + // Model names can be derived from MistralAiChatModelName enum + // if (modelName != null) { + // builder.modelName(modelName); + // } + // if (maxTokens != null) { + // builder.maxTokens(maxTokens); + // } + // if (safePrompt != null) { + // builder.safePrompt(safePrompt); + // } return builder.build(); } diff --git a/src/main/java/com/redhat/composer/config/llm/models/synchronous/SynchronousBaseModel.java b/src/main/java/com/redhat/composer/config/llm/models/synchronous/SynchronousBaseModel.java index a3e0d3c..479ef7d 100644 --- a/src/main/java/com/redhat/composer/config/llm/models/synchronous/SynchronousBaseModel.java +++ b/src/main/java/com/redhat/composer/config/llm/models/synchronous/SynchronousBaseModel.java @@ -6,9 +6,17 @@ import dev.langchain4j.model.chat.ChatLanguageModel; +/** + * Synchronous Base Model. + */ public class SynchronousBaseModel { + /** + * Get Chat Model. + * @param request the LLMRequest + * @return the ChatLanguageModel + */ public ChatLanguageModel getChatModel(LLMRequest request) { - throw new NotImplementedException("Not implemented"); + throw new NotImplementedException("Not implemented"); } } diff --git a/src/main/java/com/redhat/composer/config/llm/models/synchronous/SynchronousModelFactory.java b/src/main/java/com/redhat/composer/config/llm/models/synchronous/SynchronousModelFactory.java index 804bee3..3d438ce 100644 --- a/src/main/java/com/redhat/composer/config/llm/models/synchronous/SynchronousModelFactory.java +++ b/src/main/java/com/redhat/composer/config/llm/models/synchronous/SynchronousModelFactory.java @@ -3,24 +3,32 @@ import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; +/** + * Synchronous Model Factory. + */ @ApplicationScoped public class SynchronousModelFactory { @Inject - OpenAIModel openAIModel; + OpenAiModel openAiModel; public static final String OPENAI_MODEL = "openai"; public static final String DEFAULT_MODEL = OPENAI_MODEL; + /** + * Get the model. + * @param modelType the model type + * @return the model + */ public SynchronousBaseModel getModel(String modelType) { - if(modelType == null) { + if (modelType == null) { modelType = DEFAULT_MODEL; } switch (modelType) { case OPENAI_MODEL: - return openAIModel; + return openAiModel; default: throw new RuntimeException("Model type not found: " + modelType); } diff --git a/src/main/java/com/redhat/composer/config/retriever/contentRetriever/BaseContentRetrieverClient.java b/src/main/java/com/redhat/composer/config/retriever/contentretriever/BaseContentRetrieverClient.java similarity index 61% rename from src/main/java/com/redhat/composer/config/retriever/contentRetriever/BaseContentRetrieverClient.java rename to src/main/java/com/redhat/composer/config/retriever/contentretriever/BaseContentRetrieverClient.java index 018083a..815b181 100644 --- a/src/main/java/com/redhat/composer/config/retriever/contentRetriever/BaseContentRetrieverClient.java +++ b/src/main/java/com/redhat/composer/config/retriever/contentretriever/BaseContentRetrieverClient.java @@ -1,23 +1,35 @@ -package com.redhat.composer.config.retriever.contentRetriever; +package com.redhat.composer.config.retriever.contentretriever; -import com.redhat.composer.config.retriever.embeddingModel.EmbeddingModelFactory; +import com.redhat.composer.config.retriever.embeddingmodel.EmbeddingModelFactory; import com.redhat.composer.model.request.RetrieverRequest; import dev.langchain4j.model.embedding.EmbeddingModel; import dev.langchain4j.rag.content.retriever.ContentRetriever; import jakarta.inject.Inject; +/** + * Base Content Retriever Client. + */ public class BaseContentRetrieverClient { @Inject EmbeddingModelFactory embeddingModelFactory; - - public ContentRetriever getContentRetriever(RetrieverRequest request){ + /** + * Get Content Retriever. + * @param request the RetrieverRequest + * @return ContentRetriever + */ + public ContentRetriever getContentRetriever(RetrieverRequest request) { throw new UnsupportedOperationException("Unimplemented method 'getContentRetriever'"); } - protected EmbeddingModel getEmbeddingModel(String embeddingType){ + /** + * Get Embedding Model. + * @param embeddingType the String + * @return EmbeddingModel + */ + protected EmbeddingModel getEmbeddingModel(String embeddingType) { return embeddingModelFactory.getEmbeddingModel(embeddingType); } diff --git a/src/main/java/com/redhat/composer/config/retriever/contentRetriever/ContentRetrieverClientFactory.java b/src/main/java/com/redhat/composer/config/retriever/contentretriever/ContentRetrieverClientFactory.java similarity index 70% rename from src/main/java/com/redhat/composer/config/retriever/contentRetriever/ContentRetrieverClientFactory.java rename to src/main/java/com/redhat/composer/config/retriever/contentretriever/ContentRetrieverClientFactory.java index 3cdde7a..b324411 100644 --- a/src/main/java/com/redhat/composer/config/retriever/contentRetriever/ContentRetrieverClientFactory.java +++ b/src/main/java/com/redhat/composer/config/retriever/contentretriever/ContentRetrieverClientFactory.java @@ -1,10 +1,13 @@ -package com.redhat.composer.config.retriever.contentRetriever; +package com.redhat.composer.config.retriever.contentretriever; import com.redhat.composer.model.enums.ContentRetrieverType; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; +/** + * Factory for Content Retriever Clients. + */ @ApplicationScoped public class ContentRetrieverClientFactory { @@ -14,10 +17,16 @@ public class ContentRetrieverClientFactory { @Inject Neo4jContentRetrieverClient neo4jContentRetrieverClient; - final static ContentRetrieverType DEFAULT_CONTENT_RETRIEVER = ContentRetrieverType.WEAVIATE; + static final ContentRetrieverType DEFAULT_CONTENT_RETRIEVER = ContentRetrieverType.WEAVIATE; + + /** + * Get the Content Retriever Client. + * @param contentRetrieverType the ContentRetrieverType + * @return the Content Retriever Client + */ public BaseContentRetrieverClient getContentRetrieverClient(ContentRetrieverType contentRetrieverType) { - if(contentRetrieverType == null) { + if (contentRetrieverType == null) { contentRetrieverType = DEFAULT_CONTENT_RETRIEVER; } diff --git a/src/main/java/com/redhat/composer/config/retriever/contentRetriever/Neo4jContentRetrieverClient.java b/src/main/java/com/redhat/composer/config/retriever/contentretriever/Neo4jContentRetrieverClient.java similarity index 82% rename from src/main/java/com/redhat/composer/config/retriever/contentRetriever/Neo4jContentRetrieverClient.java rename to src/main/java/com/redhat/composer/config/retriever/contentretriever/Neo4jContentRetrieverClient.java index 3860df0..a37d1ee 100644 --- a/src/main/java/com/redhat/composer/config/retriever/contentRetriever/Neo4jContentRetrieverClient.java +++ b/src/main/java/com/redhat/composer/config/retriever/contentretriever/Neo4jContentRetrieverClient.java @@ -1,4 +1,4 @@ -package com.redhat.composer.config.retriever.contentRetriever; +package com.redhat.composer.config.retriever.contentretriever; import org.eclipse.microprofile.config.inject.ConfigProperty; import org.neo4j.driver.AuthTokens; @@ -17,19 +17,27 @@ import jakarta.inject.Inject; import jakarta.inject.Singleton; +/** + * Neo4j Content Retriever Client. + */ @Singleton public class Neo4jContentRetrieverClient extends BaseContentRetrieverClient { - @ConfigProperty( name = "neo4j.default.url") + @ConfigProperty(name = "neo4j.default.url") private String neo4jUrl; - @ConfigProperty( name = "neo4j.default.username") + @ConfigProperty(name = "neo4j.default.username") private String neo4jUser; - @ConfigProperty( name = "neo4j.default.password") + @ConfigProperty(name = "neo4j.default.password") private String neo4jPassword; @Inject SynchronousModelFactory synchronousModelFactory; + /** + * Get the Content Retriever. + * @param request the RetrieverRequest + * @return the Content Retriever + */ public ContentRetriever getContentRetriever(RetrieverRequest request) { Driver driver = GraphDatabase.driver(neo4jUrl, AuthTokens.basic(neo4jUser, neo4jPassword)); diff --git a/src/main/java/com/redhat/composer/config/retriever/contentRetriever/WeaviateContentRetrieverClient.java b/src/main/java/com/redhat/composer/config/retriever/contentretriever/WeaviateContentRetrieverClient.java similarity index 72% rename from src/main/java/com/redhat/composer/config/retriever/contentRetriever/WeaviateContentRetrieverClient.java rename to src/main/java/com/redhat/composer/config/retriever/contentretriever/WeaviateContentRetrieverClient.java index 9ba2797..5e566cf 100644 --- a/src/main/java/com/redhat/composer/config/retriever/contentRetriever/WeaviateContentRetrieverClient.java +++ b/src/main/java/com/redhat/composer/config/retriever/contentretriever/WeaviateContentRetrieverClient.java @@ -1,9 +1,9 @@ -package com.redhat.composer.config.retriever.contentRetriever; +package com.redhat.composer.config.retriever.contentretriever; import org.eclipse.microprofile.config.inject.ConfigProperty; import org.jboss.logging.Logger; -import com.redhat.composer.config.retriever.contentRetriever.custom.WeaviateEmbeddingStoreCustom; +import com.redhat.composer.config.retriever.contentretriever.custom.WeaviateEmbeddingStoreCustom; import com.redhat.composer.model.request.RetrieverRequest; import com.redhat.composer.model.request.retriever.WeaviateRequest; @@ -12,15 +12,18 @@ import dev.langchain4j.rag.content.retriever.EmbeddingStoreContentRetriever; import jakarta.inject.Singleton; +/** + * Weaviate Content Retriever Client. + */ @Singleton public class WeaviateContentRetrieverClient extends BaseContentRetrieverClient { Logger log = Logger.getLogger(WeaviateContentRetrieverClient.class); - @ConfigProperty( name = "weaviate.default.scheme") + @ConfigProperty(name = "weaviate.default.scheme") private String weaviateScheme; - @ConfigProperty( name = "weaviate.default.host") + @ConfigProperty(name = "weaviate.default.host") private String weaviateHost; @ConfigProperty(name = "weaviate.default.apiKey") @@ -32,9 +35,14 @@ public class WeaviateContentRetrieverClient extends BaseContentRetrieverClient { @ConfigProperty(name = "weaviate.default.textKey") private String weaviateTextKey; + /** + * Get the Content Retriever. + * @param request the RetrieverRequest + * @return the Content Retriever + */ public ContentRetriever getContentRetriever(RetrieverRequest request) { WeaviateRequest weaviateRequest = (WeaviateRequest) request.getBaseRetrieverRequest(); - if(weaviateRequest == null) { + if (weaviateRequest == null) { weaviateRequest = new WeaviateRequest(); } String scheme = weaviateRequest.getScheme() != null ? weaviateRequest.getScheme() : weaviateScheme; @@ -47,16 +55,16 @@ public ContentRetriever getContentRetriever(RetrieverRequest request) { log.info("Attempting to connect to Weaviate at " + scheme + "://" + host + " with index " + index); WeaviateEmbeddingStoreCustom store = WeaviateEmbeddingStoreCustom.builder() - .scheme(scheme) - .host(host) - .apiKey(apiKey) - .metadataFieldName("") - // .metadataFieldName(null) - .metadataKeys(weaviateRequest.getMetadataFields()) - .objectClass(index) - .avoidDups(true) - .textFieldName(textKey) - .build(); + .scheme(scheme) + .host(host) + .apiKey(apiKey) + .metadataFieldName("") + // .metadataFieldName(null) + .metadataKeys(weaviateRequest.getMetadataFields()) + .objectClass(index) + .avoidDups(true) + .textFieldName(textKey) + .build(); // Retrieve the embedding model @@ -66,9 +74,9 @@ public ContentRetriever getContentRetriever(RetrieverRequest request) { // Create the content retriever ContentRetriever contentRetriever = EmbeddingStoreContentRetriever.builder() - .embeddingStore(store) - .embeddingModel(embeddingModel) - .build(); + .embeddingStore(store) + .embeddingModel(embeddingModel) + .build(); return contentRetriever; } diff --git a/src/main/java/com/redhat/composer/config/retriever/contentRetriever/custom/WeaviateEmbeddingStoreCustom.java b/src/main/java/com/redhat/composer/config/retriever/contentretriever/custom/WeaviateEmbeddingStoreCustom.java similarity index 99% rename from src/main/java/com/redhat/composer/config/retriever/contentRetriever/custom/WeaviateEmbeddingStoreCustom.java rename to src/main/java/com/redhat/composer/config/retriever/contentretriever/custom/WeaviateEmbeddingStoreCustom.java index 0584bd5..9ca5046 100644 --- a/src/main/java/com/redhat/composer/config/retriever/contentRetriever/custom/WeaviateEmbeddingStoreCustom.java +++ b/src/main/java/com/redhat/composer/config/retriever/contentretriever/custom/WeaviateEmbeddingStoreCustom.java @@ -1,4 +1,4 @@ -package com.redhat.composer.config.retriever.contentRetriever.custom; +package com.redhat.composer.config.retriever.contentretriever.custom; import dev.langchain4j.data.document.Metadata; import dev.langchain4j.data.embedding.Embedding; @@ -37,6 +37,7 @@ * Represents the Weaviate vector database. * Current implementation assumes the cosine distance metric is used. */ +@SuppressWarnings("all") public class WeaviateEmbeddingStoreCustom implements EmbeddingStore { private static final String ADDITIONALS = "_additional"; diff --git a/src/main/java/com/redhat/composer/config/retriever/embeddingModel/BaseLocalEmbeddingModelClient.java b/src/main/java/com/redhat/composer/config/retriever/embeddingmodel/BaseLocalEmbeddingModelClient.java similarity index 79% rename from src/main/java/com/redhat/composer/config/retriever/embeddingModel/BaseLocalEmbeddingModelClient.java rename to src/main/java/com/redhat/composer/config/retriever/embeddingmodel/BaseLocalEmbeddingModelClient.java index cd90388..303ccc3 100644 --- a/src/main/java/com/redhat/composer/config/retriever/embeddingModel/BaseLocalEmbeddingModelClient.java +++ b/src/main/java/com/redhat/composer/config/retriever/embeddingmodel/BaseLocalEmbeddingModelClient.java @@ -1,14 +1,19 @@ -package com.redhat.composer.config.retriever.embeddingModel; +package com.redhat.composer.config.retriever.embeddingmodel; import java.util.concurrent.Executor; import dev.langchain4j.model.embedding.onnx.AbstractInProcessEmbeddingModel; import dev.langchain4j.model.embedding.onnx.OnnxBertBiEncoder; - +/** + * Base Local Embedding Model Client. + */ public class BaseLocalEmbeddingModelClient extends AbstractInProcessEmbeddingModel { - // Add explicit constructor + /** + * Constructor. + * @param executor the Executor + */ public BaseLocalEmbeddingModelClient(Executor executor) { super(executor); } diff --git a/src/main/java/com/redhat/composer/config/retriever/embeddingModel/EmbeddingModelFactory.java b/src/main/java/com/redhat/composer/config/retriever/embeddingmodel/EmbeddingModelFactory.java similarity index 74% rename from src/main/java/com/redhat/composer/config/retriever/embeddingModel/EmbeddingModelFactory.java rename to src/main/java/com/redhat/composer/config/retriever/embeddingmodel/EmbeddingModelFactory.java index 2f6fb6c..7f3a64d 100644 --- a/src/main/java/com/redhat/composer/config/retriever/embeddingModel/EmbeddingModelFactory.java +++ b/src/main/java/com/redhat/composer/config/retriever/embeddingmodel/EmbeddingModelFactory.java @@ -1,9 +1,12 @@ -package com.redhat.composer.config.retriever.embeddingModel; +package com.redhat.composer.config.retriever.embeddingmodel; import dev.langchain4j.model.embedding.EmbeddingModel; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; +/** + * Embedding Model Factory. + */ @ApplicationScoped public class EmbeddingModelFactory { @@ -13,8 +16,13 @@ public class EmbeddingModelFactory { public static final String DEFAULT_EMBEDDING = NOMIC_EMBEDDING; + /** + * Get the Embedding Model. + * @param embeddingType the String + * @return the Embedding Model + */ public EmbeddingModel getEmbeddingModel(String embeddingType) { - if(embeddingType == null) { + if (embeddingType == null) { embeddingType = DEFAULT_EMBEDDING; } switch (embeddingType) { diff --git a/src/main/java/com/redhat/composer/config/retriever/embeddingModel/NomicLocalEmbeddingModelClient.java b/src/main/java/com/redhat/composer/config/retriever/embeddingmodel/NomicLocalEmbeddingModelClient.java similarity index 52% rename from src/main/java/com/redhat/composer/config/retriever/embeddingModel/NomicLocalEmbeddingModelClient.java rename to src/main/java/com/redhat/composer/config/retriever/embeddingmodel/NomicLocalEmbeddingModelClient.java index 9027ba6..a949016 100644 --- a/src/main/java/com/redhat/composer/config/retriever/embeddingModel/NomicLocalEmbeddingModelClient.java +++ b/src/main/java/com/redhat/composer/config/retriever/embeddingmodel/NomicLocalEmbeddingModelClient.java @@ -1,14 +1,17 @@ -package com.redhat.composer.config.retriever.embeddingModel; +package com.redhat.composer.config.retriever.embeddingmodel; import dev.langchain4j.model.embedding.onnx.OnnxBertBiEncoder; import dev.langchain4j.model.embedding.onnx.PoolingMode; import io.smallrye.mutiny.infrastructure.Infrastructure; import jakarta.inject.Singleton; +/** + * Nomic Local Embedding Model Client. + */ @Singleton public class NomicLocalEmbeddingModelClient extends BaseLocalEmbeddingModelClient { - private final static String MODEL_FOLDER = "embeddings/nomic/"; + private static final String MODEL_FOLDER = "embeddings/nomic/"; private static final OnnxBertBiEncoder MODEL = loadFromJar( @@ -17,14 +20,21 @@ public class NomicLocalEmbeddingModelClient extends BaseLocalEmbeddingModelClien PoolingMode.CLS ); - public NomicLocalEmbeddingModelClient() { - super(Infrastructure.getDefaultExecutor()); - } - - - @Override - protected OnnxBertBiEncoder model() { - return MODEL; - } + /** + * Constructor. + */ + public NomicLocalEmbeddingModelClient() { + super(Infrastructure.getDefaultExecutor()); + } + + + /** + * Get the Model Folder. + * @return the Model Folder + */ + @Override + protected OnnxBertBiEncoder model() { + return MODEL; + } } diff --git a/src/main/java/com/redhat/composer/model/enums/ContentRetrieverType.java b/src/main/java/com/redhat/composer/model/enums/ContentRetrieverType.java index c23f889..a9e0a25 100644 --- a/src/main/java/com/redhat/composer/model/enums/ContentRetrieverType.java +++ b/src/main/java/com/redhat/composer/model/enums/ContentRetrieverType.java @@ -1,27 +1,35 @@ package com.redhat.composer.model.enums; +/** + * Content Retriever Type. + */ public enum ContentRetrieverType { - WEAVIATE("weaviate"), - NEO4J("neo4j"); + WEAVIATE("weaviate"), + NEO4J("neo4j"); - private final String type; + private final String type; - ContentRetrieverType(String type) { - this.type = type; - } + ContentRetrieverType(String type) { + this.type = type; + } - public String getType() { - return type; - } + public String getType() { + return type; + } - public static ContentRetrieverType fromString(String type) { - for (ContentRetrieverType retrieverType : ContentRetrieverType.values()) { - if (retrieverType.getType().equalsIgnoreCase(type)) { - return retrieverType; - } - } - throw new IllegalArgumentException("No constant with type " + type + " found"); + /** + * Get the Content Retriever Type from String. + * @param type string representation of the type + * @return the ContentRetrieverType + */ + public static ContentRetrieverType fromString(String type) { + for (ContentRetrieverType retrieverType : ContentRetrieverType.values()) { + if (retrieverType.getType().equalsIgnoreCase(type)) { + return retrieverType; + } } + throw new IllegalArgumentException("No constant with type " + type + " found"); + } } diff --git a/src/main/java/com/redhat/composer/model/mongo/BaseEntity.java b/src/main/java/com/redhat/composer/model/mongo/BaseEntity.java index 552bb4a..c25c537 100644 --- a/src/main/java/com/redhat/composer/model/mongo/BaseEntity.java +++ b/src/main/java/com/redhat/composer/model/mongo/BaseEntity.java @@ -7,6 +7,9 @@ import io.quarkus.mongodb.panache.PanacheMongoEntity; +/** + * BaseEntity. + */ public class BaseEntity extends PanacheMongoEntity { diff --git a/src/main/java/com/redhat/composer/model/mongo/LLMConnectionEntity.java b/src/main/java/com/redhat/composer/model/mongo/LlmConnectionEntity.java similarity index 80% rename from src/main/java/com/redhat/composer/model/mongo/LLMConnectionEntity.java rename to src/main/java/com/redhat/composer/model/mongo/LlmConnectionEntity.java index 8676fdd..b40f4de 100644 --- a/src/main/java/com/redhat/composer/model/mongo/LLMConnectionEntity.java +++ b/src/main/java/com/redhat/composer/model/mongo/LlmConnectionEntity.java @@ -6,8 +6,13 @@ import io.quarkus.mongodb.panache.common.MongoEntity; +/** + * LlmConnectionEntity + */ +// CHECKSTYLE +@SuppressWarnings("all") @MongoEntity(collection = "llm_connection") -public class LLMConnectionEntity extends BaseEntity { +public class LlmConnectionEntity extends BaseEntity { private String name; @@ -20,10 +25,10 @@ public class LLMConnectionEntity extends BaseEntity { private String modelName; - public LLMConnectionEntity() { + public LlmConnectionEntity() { } - public LLMConnectionEntity(String name, String description, String modelType, String url, String apiKey, String modelName) { + public LlmConnectionEntity(String name, String description, String modelType, String url, String apiKey, String modelName) { this.name = name; this.description = description; this.modelType = modelType; @@ -80,32 +85,32 @@ public void setModelName(String modelName) { this.modelName = modelName; } - public LLMConnectionEntity name(String name) { + public LlmConnectionEntity name(String name) { setName(name); return this; } - public LLMConnectionEntity description(String description) { + public LlmConnectionEntity description(String description) { setDescription(description); return this; } - public LLMConnectionEntity modelType(String modelType) { + public LlmConnectionEntity modelType(String modelType) { setModelType(modelType); return this; } - public LLMConnectionEntity url(String url) { + public LlmConnectionEntity url(String url) { setUrl(url); return this; } - public LLMConnectionEntity apiKey(String apiKey) { + public LlmConnectionEntity apiKey(String apiKey) { setApiKey(apiKey); return this; } - public LLMConnectionEntity modelName(String modelName) { + public LlmConnectionEntity modelName(String modelName) { setModelName(modelName); return this; } diff --git a/src/main/java/com/redhat/composer/model/mongo/RetrieverConnectionEntity.java b/src/main/java/com/redhat/composer/model/mongo/RetrieverConnectionEntity.java index d06c740..04cc8b2 100644 --- a/src/main/java/com/redhat/composer/model/mongo/RetrieverConnectionEntity.java +++ b/src/main/java/com/redhat/composer/model/mongo/RetrieverConnectionEntity.java @@ -4,11 +4,14 @@ import org.apache.commons.lang3.builder.EqualsBuilder; -import com.redhat.composer.model.mongo.contentRetrieverEntites.BaseRetrieverConnectionEntity; +import com.redhat.composer.model.mongo.contentretrieverentites.BaseRetrieverConnectionEntity; import io.quarkus.mongodb.panache.common.MongoEntity; - +/** + * RetrieverConnectionEntity + */ +@SuppressWarnings("all") @MongoEntity(collection = "retriever_connection") public class RetrieverConnectionEntity extends BaseEntity { diff --git a/src/main/java/com/redhat/composer/model/mongo/contentRetrieverEntites/Neo4JEntity.java b/src/main/java/com/redhat/composer/model/mongo/contentRetrieverEntites/Neo4JEntity.java deleted file mode 100644 index b559c50..0000000 --- a/src/main/java/com/redhat/composer/model/mongo/contentRetrieverEntites/Neo4JEntity.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.redhat.composer.model.mongo.contentRetrieverEntites; - -import org.bson.codecs.pojo.annotations.BsonDiscriminator; - -@BsonDiscriminator("neo4j") -public class Neo4JEntity extends BaseRetrieverConnectionEntity { - -} diff --git a/src/main/java/com/redhat/composer/model/mongo/contentRetrieverEntites/BaseRetrieverConnectionEntity.java b/src/main/java/com/redhat/composer/model/mongo/contentretrieverentites/BaseRetrieverConnectionEntity.java similarity index 63% rename from src/main/java/com/redhat/composer/model/mongo/contentRetrieverEntites/BaseRetrieverConnectionEntity.java rename to src/main/java/com/redhat/composer/model/mongo/contentretrieverentites/BaseRetrieverConnectionEntity.java index 3a0c0ba..ae99ca4 100644 --- a/src/main/java/com/redhat/composer/model/mongo/contentRetrieverEntites/BaseRetrieverConnectionEntity.java +++ b/src/main/java/com/redhat/composer/model/mongo/contentretrieverentites/BaseRetrieverConnectionEntity.java @@ -1,21 +1,36 @@ -package com.redhat.composer.model.mongo.contentRetrieverEntites; +package com.redhat.composer.model.mongo.contentretrieverentites; + import org.bson.codecs.pojo.annotations.BsonDiscriminator; import com.redhat.composer.model.enums.ContentRetrieverType; +/** + * Base Retriever Connection Entity. + */ +@SuppressWarnings("all") @BsonDiscriminator public class BaseRetrieverConnectionEntity { ContentRetrieverType contentRetrieverType; - + /** + * Constructor. + */ public BaseRetrieverConnectionEntity() { } + /** + * Constructor. + * @param contentRetrieverType the ContentRetrieverType + */ public BaseRetrieverConnectionEntity(ContentRetrieverType contentRetrieverType) { this.contentRetrieverType = contentRetrieverType; } + /** + * Get the Content Retriever Type. + * @return the ContentRetrieverType + */ public ContentRetrieverType getContentRetrieverType() { return this.contentRetrieverType; } @@ -24,6 +39,11 @@ public void setContentRetrieverType(ContentRetrieverType contentRetrieverType) { this.contentRetrieverType = contentRetrieverType; } + /** + * Set the Content Retriever Type. + * @param contentRetrieverType the ContentRetrieverType + * @return BaseRetrieverConnectionEntity + */ public BaseRetrieverConnectionEntity contentRetrieverType(ContentRetrieverType contentRetrieverType) { setContentRetrieverType(contentRetrieverType); return this; diff --git a/src/main/java/com/redhat/composer/model/mongo/contentretrieverentites/Neo4jEntity.java b/src/main/java/com/redhat/composer/model/mongo/contentretrieverentites/Neo4jEntity.java new file mode 100644 index 0000000..38aa0e6 --- /dev/null +++ b/src/main/java/com/redhat/composer/model/mongo/contentretrieverentites/Neo4jEntity.java @@ -0,0 +1,12 @@ +package com.redhat.composer.model.mongo.contentretrieverentites; + +import org.bson.codecs.pojo.annotations.BsonDiscriminator; + +/** + * Neo4J Entity. + */ +@SuppressWarnings("all") +@BsonDiscriminator("neo4j") +public class Neo4jEntity extends BaseRetrieverConnectionEntity { + +} diff --git a/src/main/java/com/redhat/composer/model/mongo/contentRetrieverEntites/WeaviateConnectionEntity.java b/src/main/java/com/redhat/composer/model/mongo/contentretrieverentites/WeaviateConnectionEntity.java similarity index 93% rename from src/main/java/com/redhat/composer/model/mongo/contentRetrieverEntites/WeaviateConnectionEntity.java rename to src/main/java/com/redhat/composer/model/mongo/contentretrieverentites/WeaviateConnectionEntity.java index ee31ba5..a292a8e 100644 --- a/src/main/java/com/redhat/composer/model/mongo/contentRetrieverEntites/WeaviateConnectionEntity.java +++ b/src/main/java/com/redhat/composer/model/mongo/contentretrieverentites/WeaviateConnectionEntity.java @@ -1,4 +1,4 @@ -package com.redhat.composer.model.mongo.contentRetrieverEntites; +package com.redhat.composer.model.mongo.contentretrieverentites; import org.apache.commons.lang3.builder.EqualsBuilder; import org.bson.codecs.pojo.annotations.BsonDiscriminator; @@ -8,6 +8,10 @@ import java.util.List; import java.util.Objects; +/** + * Weaviate Connection Entity. + */ +@SuppressWarnings("all") @BsonDiscriminator("weaviate") public class WeaviateConnectionEntity extends BaseRetrieverConnectionEntity { @@ -25,7 +29,6 @@ public class WeaviateConnectionEntity extends BaseRetrieverConnectionEntity { String apiKey; - public WeaviateConnectionEntity() { contentRetrieverType = ContentRetrieverType.WEAVIATE; } @@ -110,7 +113,7 @@ public WeaviateConnectionEntity apiKey(String apiKey) { @Override public boolean equals(Object o) { - return EqualsBuilder.reflectionEquals(this, o); + return EqualsBuilder.reflectionEquals(this, o); } @Override diff --git a/src/main/java/com/redhat/composer/model/request/AssistantChatRequest.java b/src/main/java/com/redhat/composer/model/request/AssistantChatRequest.java index d157a40..e9d4952 100644 --- a/src/main/java/com/redhat/composer/model/request/AssistantChatRequest.java +++ b/src/main/java/com/redhat/composer/model/request/AssistantChatRequest.java @@ -1,8 +1,10 @@ package com.redhat.composer.model.request; + import java.util.Objects; import org.apache.commons.lang3.builder.EqualsBuilder; +@SuppressWarnings("all") public class AssistantChatRequest{ private String message = ""; diff --git a/src/main/java/com/redhat/composer/model/request/AssistantCreationRequest.java b/src/main/java/com/redhat/composer/model/request/AssistantCreationRequest.java index e8da730..63ed20e 100644 --- a/src/main/java/com/redhat/composer/model/request/AssistantCreationRequest.java +++ b/src/main/java/com/redhat/composer/model/request/AssistantCreationRequest.java @@ -4,6 +4,7 @@ import org.apache.commons.lang3.builder.EqualsBuilder; +@SuppressWarnings("all") public class AssistantCreationRequest { String name; diff --git a/src/main/java/com/redhat/composer/model/request/ChatBotRequest.java b/src/main/java/com/redhat/composer/model/request/ChatBotRequest.java index 33362bf..95ed2fe 100644 --- a/src/main/java/com/redhat/composer/model/request/ChatBotRequest.java +++ b/src/main/java/com/redhat/composer/model/request/ChatBotRequest.java @@ -3,6 +3,7 @@ import org.apache.commons.lang3.builder.EqualsBuilder; +@SuppressWarnings("all") public class ChatBotRequest{ private String message = ""; diff --git a/src/main/java/com/redhat/composer/model/request/LLMRequest.java b/src/main/java/com/redhat/composer/model/request/LLMRequest.java index 1b46b59..d692ed3 100644 --- a/src/main/java/com/redhat/composer/model/request/LLMRequest.java +++ b/src/main/java/com/redhat/composer/model/request/LLMRequest.java @@ -6,6 +6,7 @@ import com.redhat.composer.config.llm.models.streaming.StreamingModelFactory; +@SuppressWarnings("all") public class LLMRequest { private String name; diff --git a/src/main/java/com/redhat/composer/model/request/RetrieverRequest.java b/src/main/java/com/redhat/composer/model/request/RetrieverRequest.java index a16b710..d50f251 100644 --- a/src/main/java/com/redhat/composer/model/request/RetrieverRequest.java +++ b/src/main/java/com/redhat/composer/model/request/RetrieverRequest.java @@ -5,6 +5,7 @@ import org.apache.commons.lang3.builder.EqualsBuilder; +@SuppressWarnings("all") public class RetrieverRequest { BaseRetrieverRequest baseRetrieverRequest; diff --git a/src/main/java/com/redhat/composer/model/request/retriever/BaseRetrieverRequest.java b/src/main/java/com/redhat/composer/model/request/retriever/BaseRetrieverRequest.java index 894af54..891af7a 100644 --- a/src/main/java/com/redhat/composer/model/request/retriever/BaseRetrieverRequest.java +++ b/src/main/java/com/redhat/composer/model/request/retriever/BaseRetrieverRequest.java @@ -5,6 +5,7 @@ import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo; +@SuppressWarnings("all") @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "contentRetrieverType", visible = true) @JsonSubTypes({ @JsonSubTypes.Type(value = WeaviateRequest.class, name = "weaviate"), diff --git a/src/main/java/com/redhat/composer/model/request/retriever/Neo4JRequest.java b/src/main/java/com/redhat/composer/model/request/retriever/Neo4JRequest.java index bdd57ad..eb0f7ed 100644 --- a/src/main/java/com/redhat/composer/model/request/retriever/Neo4JRequest.java +++ b/src/main/java/com/redhat/composer/model/request/retriever/Neo4JRequest.java @@ -1,5 +1,6 @@ package com.redhat.composer.model.request.retriever; +@SuppressWarnings("all") public class Neo4JRequest extends BaseRetrieverRequest { diff --git a/src/main/java/com/redhat/composer/model/request/retriever/WeaviateRequest.java b/src/main/java/com/redhat/composer/model/request/retriever/WeaviateRequest.java index 9752fdd..1557932 100644 --- a/src/main/java/com/redhat/composer/model/request/retriever/WeaviateRequest.java +++ b/src/main/java/com/redhat/composer/model/request/retriever/WeaviateRequest.java @@ -7,6 +7,7 @@ import com.redhat.composer.model.enums.ContentRetrieverType; +@SuppressWarnings("all") public class WeaviateRequest extends BaseRetrieverRequest { // Key of the value containing the text used for retrieval and passed into the LLM diff --git a/src/main/java/com/redhat/composer/model/response/AssistantResponse.java b/src/main/java/com/redhat/composer/model/response/AssistantResponse.java index a4195f3..efb0bad 100644 --- a/src/main/java/com/redhat/composer/model/response/AssistantResponse.java +++ b/src/main/java/com/redhat/composer/model/response/AssistantResponse.java @@ -5,12 +5,13 @@ import org.apache.commons.lang3.builder.EqualsBuilder; import com.redhat.composer.model.mongo.AssistantEntity; -import com.redhat.composer.model.mongo.LLMConnectionEntity; +import com.redhat.composer.model.mongo.LlmConnectionEntity; import com.redhat.composer.model.mongo.RetrieverConnectionEntity; +@SuppressWarnings("all") public class AssistantResponse extends AssistantEntity { - private LLMConnectionEntity llmConnection; + private LlmConnectionEntity llmConnection; private RetrieverConnectionEntity retrieverConnection; @@ -18,16 +19,16 @@ public AssistantResponse() { } - public AssistantResponse(LLMConnectionEntity llmConnection, RetrieverConnectionEntity retrieverConnection) { + public AssistantResponse(LlmConnectionEntity llmConnection, RetrieverConnectionEntity retrieverConnection) { this.llmConnection = llmConnection; this.retrieverConnection = retrieverConnection; } - public LLMConnectionEntity getLlmConnection() { + public LlmConnectionEntity getLlmConnection() { return this.llmConnection; } - public void setLlmConnection(LLMConnectionEntity llmConnection) { + public void setLlmConnection(LlmConnectionEntity llmConnection) { this.llmConnection = llmConnection; } @@ -39,7 +40,7 @@ public void setRetrieverConnection(RetrieverConnectionEntity retrieverConnection this.retrieverConnection = retrieverConnection; } - public AssistantResponse llmConnection(LLMConnectionEntity llmConnection) { + public AssistantResponse llmConnection(LlmConnectionEntity llmConnection) { setLlmConnection(llmConnection); return this; } diff --git a/src/main/java/com/redhat/composer/model/response/ContentResponse.java b/src/main/java/com/redhat/composer/model/response/ContentResponse.java index 85f148f..d0c803d 100644 --- a/src/main/java/com/redhat/composer/model/response/ContentResponse.java +++ b/src/main/java/com/redhat/composer/model/response/ContentResponse.java @@ -8,7 +8,7 @@ import dev.langchain4j.rag.content.Content; - +@SuppressWarnings("all") public class ContentResponse { @JsonProperty("content") diff --git a/src/main/java/com/redhat/composer/model/response/SourceResponse.java b/src/main/java/com/redhat/composer/model/response/SourceResponse.java index 0b60cb0..374a3e1 100644 --- a/src/main/java/com/redhat/composer/model/response/SourceResponse.java +++ b/src/main/java/com/redhat/composer/model/response/SourceResponse.java @@ -4,6 +4,7 @@ import org.apache.commons.lang3.builder.EqualsBuilder; +@SuppressWarnings("all") public class SourceResponse { private String content; diff --git a/src/main/java/com/redhat/composer/repositories/AssistantRepository.java b/src/main/java/com/redhat/composer/repositories/AssistantRepository.java index 14a14fa..a3ffae8 100644 --- a/src/main/java/com/redhat/composer/repositories/AssistantRepository.java +++ b/src/main/java/com/redhat/composer/repositories/AssistantRepository.java @@ -5,11 +5,19 @@ import io.quarkus.mongodb.panache.PanacheMongoRepository; import jakarta.enterprise.context.ApplicationScoped; +/** + * Assistant Repository. + */ @ApplicationScoped public class AssistantRepository implements PanacheMongoRepository { - + + /** + * Find an Assistant by name. + * @param name the name of the Assistant + * @return the AssistantEntity + */ public AssistantEntity findByName(String name) { - return find("name", name).firstResult(); - } + return find("name", name).firstResult(); + } } diff --git a/src/main/java/com/redhat/composer/services/AssistantInfoService.java b/src/main/java/com/redhat/composer/services/AssistantInfoService.java index 290ae5a..b4b08fa 100644 --- a/src/main/java/com/redhat/composer/services/AssistantInfoService.java +++ b/src/main/java/com/redhat/composer/services/AssistantInfoService.java @@ -6,7 +6,7 @@ import org.bson.types.ObjectId; import com.redhat.composer.model.mongo.AssistantEntity; -import com.redhat.composer.model.mongo.LLMConnectionEntity; +import com.redhat.composer.model.mongo.LlmConnectionEntity; import com.redhat.composer.model.mongo.RetrieverConnectionEntity; import com.redhat.composer.model.request.AssistantCreationRequest; import com.redhat.composer.model.request.LLMRequest; @@ -17,31 +17,45 @@ import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; -// TODO: Set up MapStruct service or something -// https://mapstruct.org/ - +/** + * AssistantInfoService. + */ @ApplicationScoped public class AssistantInfoService { @Inject MapperUtil mapperUtil; + /** + * Create an Assistant. + * @param request the AssistantCreationRequest + * @return the AssistantEntity + */ public AssistantEntity createAssistant(AssistantCreationRequest request) { AssistantEntity assistant = new AssistantEntity(); + LlmConnectionEntity llm = (LlmConnectionEntity) LlmConnectionEntity.findByIdOptional( + new ObjectId(request.getLlmConnectionId())) + .orElseThrow(() -> new IllegalArgumentException("LLM Connection not found")); - LLMConnectionEntity llm = (LLMConnectionEntity) LLMConnectionEntity.findByIdOptional(new ObjectId(request.getLlmConnectionId())).orElseThrow(() -> new IllegalArgumentException("LLM Connection not found")); + assistant.setLlmConnectionId(llm.id); + if (request.getRetrieverConnectionId() != null) { - RetrieverConnectionEntity retriever = (RetrieverConnectionEntity) RetrieverConnectionEntity.findByIdOptional(new ObjectId(request.getRetrieverConnectionId())).orElseThrow(() -> new IllegalArgumentException("Retriever Connection not found")); + RetrieverConnectionEntity retriever = (RetrieverConnectionEntity) RetrieverConnectionEntity + .findByIdOptional(new ObjectId(request.getRetrieverConnectionId())) + .orElseThrow(() -> new IllegalArgumentException("Retriever Connection not found")); assistant.setRetrieverConnectionId(retriever.id); } assistant.setName(request.getName()); assistant.setDisplayName(request.getDisplayName()); assistant.setDescription(request.getDescription()); - assistant.setLlmConnectionId(llm.id); assistant.persist(); return assistant; } + /** + * Get all Assistants. + * @return a list of AssistantResponse + */ public List getAssistant() { Stream stream = AssistantEntity.streamAll(); return stream.map(entity -> { @@ -50,13 +64,18 @@ public List getAssistant() { response.setName(entity.getName()); response.setDisplayName(entity.getDisplayName()); response.setDescription(entity.getDescription()); - response.setLlmConnection(LLMConnectionEntity.findById(entity.getLlmConnectionId())); + response.setLlmConnection(LlmConnectionEntity.findById(entity.getLlmConnectionId())); response.setRetrieverConnection(RetrieverConnectionEntity.findById(entity.getRetrieverConnectionId())); return response; } ).toList(); } + /** + * Create a RetrieverConnectionEntity. + * @param request the RetrieverRequest + * @return the RetrieverConnectionEntity + */ public RetrieverConnectionEntity createRetrieverConnectionEntity(RetrieverRequest request) { RetrieverConnectionEntity entity = mapperUtil.toEntity(request); entity.persist(); @@ -66,9 +85,14 @@ public RetrieverConnectionEntity createRetrieverConnectionEntity(RetrieverReques public List getRetrieverConnections() { return RetrieverConnectionEntity.listAll(); } - - public LLMConnectionEntity createLLMConnection(LLMRequest request) { - LLMConnectionEntity entity = new LLMConnectionEntity(); + + /** + * Create a LLMConnectionEntity. + * @param request the LLMRequest + * @return the LLMConnectionEntity + */ + public LlmConnectionEntity createLlmConnection(LLMRequest request) { + LlmConnectionEntity entity = new LlmConnectionEntity(); entity.setName(request.getName()); entity.setDescription(request.getDescription()); entity.setUrl(request.getUrl()); @@ -78,8 +102,12 @@ public LLMConnectionEntity createLLMConnection(LLMRequest request) { return entity; } - public List getLLMConnections() { - return LLMConnectionEntity.listAll(); + /** + * Get all LLMConnections. + * @return a list of LlmConnectionEntity + */ + public List getLlmConnections() { + return LlmConnectionEntity.listAll(); } } diff --git a/src/main/java/com/redhat/composer/services/ChatBotService.java b/src/main/java/com/redhat/composer/services/ChatBotService.java index 748d5d9..28eebb4 100644 --- a/src/main/java/com/redhat/composer/services/ChatBotService.java +++ b/src/main/java/com/redhat/composer/services/ChatBotService.java @@ -1,17 +1,14 @@ package com.redhat.composer.services; -import java.util.ArrayList; -import java.util.List; - import org.jboss.logging.Logger; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; -import com.redhat.composer.config.llm.aiservices.AIServicesFactory; -import com.redhat.composer.config.llm.aiservices.BaseAIService; +import com.redhat.composer.config.llm.aiservices.AiServicesFactory; +import com.redhat.composer.config.llm.aiservices.BaseAiService; import com.redhat.composer.config.llm.models.streaming.StreamingModelFactory; import com.redhat.composer.model.mongo.AssistantEntity; -import com.redhat.composer.model.mongo.LLMConnectionEntity; +import com.redhat.composer.model.mongo.LlmConnectionEntity; import com.redhat.composer.model.mongo.RetrieverConnectionEntity; import com.redhat.composer.model.request.AssistantChatRequest; import com.redhat.composer.model.request.ChatBotRequest; @@ -20,7 +17,6 @@ import com.redhat.composer.util.mappers.MapperUtil; import dev.langchain4j.model.chat.StreamingChatLanguageModel; -import dev.langchain4j.rag.content.Content; import dev.langchain4j.service.AiServices; import io.opentelemetry.api.trace.Span; import io.quarkus.runtime.util.StringUtil; @@ -28,6 +24,9 @@ import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; +/** + * ChatBotService class. + */ @ApplicationScoped public class ChatBotService { @@ -37,7 +36,7 @@ public class ChatBotService { StreamingModelFactory modelTemplateFactory; @Inject - AIServicesFactory aiServicesFactory; + AiServicesFactory aiServicesFactory; @Inject RetrieveService ragService; @@ -51,18 +50,26 @@ public class ChatBotService { @Inject ObjectMapper objectMapper; + /** + * Chat with an assistant. + * @param request the AssistantChatRequest + * @return stream of chat response + */ public Multi chat(AssistantChatRequest request) { AssistantEntity assistant; - if(!StringUtil.isNullOrEmpty(request.getAssistantName())) { - assistant = assistantRepository.findByName(request.getAssistantName()); - } else if(!StringUtil.isNullOrEmpty(request.getAssistantId())) { + if (!StringUtil.isNullOrEmpty(request.getAssistantName())) { + assistant = assistantRepository.findByName(request.getAssistantName()); + } else if (!StringUtil.isNullOrEmpty(request.getAssistantId())) { assistant = AssistantEntity.findById(request.getAssistantId()); } else { throw new RuntimeException("Assistant Name or ID Required"); } - LLMConnectionEntity llmConnection = LLMConnectionEntity.findById(assistant.getLlmConnectionId()); - RetrieverConnectionEntity retrieverConnection = RetrieverConnectionEntity.findById(assistant.getRetrieverConnectionId()); + + LlmConnectionEntity llmConnection = LlmConnectionEntity.findById(assistant.getLlmConnectionId()); + + RetrieverConnectionEntity retrieverConnection = RetrieverConnectionEntity + .findById(assistant.getRetrieverConnectionId()); ChatBotRequest chatBotRequest = new ChatBotRequest(); chatBotRequest.setMessage(request.getMessage()); @@ -73,6 +80,11 @@ public Multi chat(AssistantChatRequest request) { return chat(chatBotRequest); } + /** + * Chat with a model. + * @param request the ChatBotRequest + * @return stream of chat response + */ public Multi chat(ChatBotRequest request) { String traceId = Span.current().getSpanContext().getTraceId(); @@ -80,21 +92,20 @@ public Multi chat(ChatBotRequest request) { validateRequest(request); StreamingChatLanguageModel llm = modelTemplateFactory.getModel(request.getModelRequest().getModelType()) - .getChatModel(request.getModelRequest()); + .getChatModel(request.getModelRequest()); - // TODO: Make this configurable - Class aiServiceClass = aiServicesFactory.getAiService(AIServicesFactory.MISTRAL7B_AI_SERVICE); + // TODO: Make this configurable + Class aiServiceClass = aiServicesFactory + .getAiService(AiServicesFactory.MISTRAL7B_AI_SERVICE); - AiServices builder = AiServices.builder(aiServiceClass) + AiServices builder = AiServices.builder(aiServiceClass) .streamingChatLanguageModel(llm); - if(request.getRetrieverRequest() != null) { - builder.contentRetriever(ragService.getContentRetriever(request.getRetrieverRequest())); + if (request.getRetrieverRequest() != null) { + builder.contentRetriever(ragService.getContentRetriever(request.getRetrieverRequest())); } - BaseAIService aiService = builder.build(); - - + BaseAiService aiService = builder.build(); try { Multi multi = Multi.createFrom().emitter(em -> { @@ -111,20 +122,21 @@ public Multi chat(ChatBotRequest request) { }) .onError(em::fail) .onComplete(response -> { - em.complete();}) - .start(); - }); + em.complete(); + }) + .start(); + }); return multi; } catch (Exception e) { - log.error("Error in ChatBotService.chat", e); - return Multi.createFrom().failure(e); - } + log.error("Error in ChatBotService.chat", e); + return Multi.createFrom().failure(e); + } } // TODO: Support non-streaming chat? private void validateRequest(ChatBotRequest request) { - if(request.getMessage() == null) { + if (request.getMessage() == null) { throw new RuntimeException("Request Message Required"); } } diff --git a/src/main/java/com/redhat/composer/services/EmbeddingService.java b/src/main/java/com/redhat/composer/services/EmbeddingService.java index c9de112..b81fd68 100644 --- a/src/main/java/com/redhat/composer/services/EmbeddingService.java +++ b/src/main/java/com/redhat/composer/services/EmbeddingService.java @@ -2,7 +2,7 @@ import org.jboss.logging.Logger; -import com.redhat.composer.config.retriever.embeddingModel.EmbeddingModelFactory; +import com.redhat.composer.config.retriever.embeddingmodel.EmbeddingModelFactory; import dev.langchain4j.data.embedding.Embedding; import dev.langchain4j.model.embedding.EmbeddingModel; @@ -16,15 +16,19 @@ @ApplicationScoped public class EmbeddingService { - private static final Logger logger = Logger.getLogger(EmbeddingService.class); - @Inject EmbeddingModelFactory embeddingTemplateFactory; + /** + * Embeds the given text using the specified embedding model. + * @param text the text to embed + * @param embeddingType the type of embedding model to use + * @return the embedded text + */ public Embedding embedding(String text, String embeddingType) { EmbeddingModel embedding = embeddingTemplateFactory.getEmbeddingModel(embeddingType); Response response = embedding.embed(text); return response.content(); } -} +} \ No newline at end of file diff --git a/src/main/java/com/redhat/composer/services/RetrieveService.java b/src/main/java/com/redhat/composer/services/RetrieveService.java index e263645..5a1482a 100644 --- a/src/main/java/com/redhat/composer/services/RetrieveService.java +++ b/src/main/java/com/redhat/composer/services/RetrieveService.java @@ -2,8 +2,8 @@ import java.util.List; -import com.redhat.composer.config.retriever.contentRetriever.BaseContentRetrieverClient; -import com.redhat.composer.config.retriever.contentRetriever.ContentRetrieverClientFactory; +import com.redhat.composer.config.retriever.contentretriever.BaseContentRetrieverClient; +import com.redhat.composer.config.retriever.contentretriever.ContentRetrieverClientFactory; import com.redhat.composer.model.enums.ContentRetrieverType; import com.redhat.composer.model.request.RetrieverRequest; @@ -13,19 +13,35 @@ import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; +/** + * Service for retrieving content. + */ @ApplicationScoped public class RetrieveService { @Inject ContentRetrieverClientFactory contentRetrieverClientFactory; + /** + * Retrieves content from a given message. + * @param request the request to retrieve content + * @return the retrieved content + */ public ContentRetriever getContentRetriever(RetrieverRequest request) { - ContentRetrieverType contentRetrieverType = ContentRetrieverType.fromString(request.getBaseRetrieverRequest().getContentRetrieverType()); - BaseContentRetrieverClient client = contentRetrieverClientFactory.getContentRetrieverClient(contentRetrieverType); //TODO: Fix this + ContentRetrieverType contentRetrieverType = ContentRetrieverType.fromString( + request.getBaseRetrieverRequest().getContentRetrieverType()); + BaseContentRetrieverClient client = contentRetrieverClientFactory + .getContentRetrieverClient(contentRetrieverType); return client.getContentRetriever(request); } + /** + * Retrieves content from a given message. + * @param request the request to retrieve content + * @param message the message to retrieve content from + * @return the retrieved content + */ public List retrieveContent(RetrieverRequest request, String message) { ContentRetriever contentRetriever = getContentRetriever(request); Query query = Query.from(message); diff --git a/src/main/java/com/redhat/composer/util/DisabledAuthController.java b/src/main/java/com/redhat/composer/util/DisabledAuthController.java index d52f857..f263b63 100644 --- a/src/main/java/com/redhat/composer/util/DisabledAuthController.java +++ b/src/main/java/com/redhat/composer/util/DisabledAuthController.java @@ -1,7 +1,5 @@ package com.redhat.composer.util; - - import org.eclipse.microprofile.config.inject.ConfigProperty; import io.quarkus.security.spi.runtime.AuthorizationController; @@ -11,16 +9,19 @@ import jakarta.interceptor.Interceptor; -// TODO: Remove this class once we have the frontend setup +/** + * Disabled Authorization Controller. + */ @Alternative @Priority(Interceptor.Priority.LIBRARY_AFTER) @ApplicationScoped public class DisabledAuthController extends AuthorizationController { - @ConfigProperty(name = "disable.authorization", defaultValue = "false") - boolean disableAuthorization; - @Override - public boolean isAuthorizationEnabled() { - return !disableAuthorization; - } + @ConfigProperty(name = "disable.authorization", defaultValue = "false") + boolean disableAuthorization; + + @Override + public boolean isAuthorizationEnabled() { + return !disableAuthorization; + } } \ No newline at end of file diff --git a/src/main/java/com/redhat/composer/util/mappers/MapperUtil.java b/src/main/java/com/redhat/composer/util/mappers/MapperUtil.java index ead1774..85c08cf 100644 --- a/src/main/java/com/redhat/composer/util/mappers/MapperUtil.java +++ b/src/main/java/com/redhat/composer/util/mappers/MapperUtil.java @@ -2,14 +2,15 @@ import org.mapstruct.Mapper; import org.mapstruct.Mapping; +import org.mapstruct.Mappings; import org.mapstruct.factory.Mappers; import com.redhat.composer.model.enums.ContentRetrieverType; -import com.redhat.composer.model.mongo.LLMConnectionEntity; +import com.redhat.composer.model.mongo.LlmConnectionEntity; import com.redhat.composer.model.mongo.RetrieverConnectionEntity; -import com.redhat.composer.model.mongo.contentRetrieverEntites.BaseRetrieverConnectionEntity; -import com.redhat.composer.model.mongo.contentRetrieverEntites.Neo4JEntity; -import com.redhat.composer.model.mongo.contentRetrieverEntites.WeaviateConnectionEntity; +import com.redhat.composer.model.mongo.contentretrieverentites.BaseRetrieverConnectionEntity; +import com.redhat.composer.model.mongo.contentretrieverentites.Neo4jEntity; +import com.redhat.composer.model.mongo.contentretrieverentites.WeaviateConnectionEntity; import com.redhat.composer.model.request.LLMRequest; import com.redhat.composer.model.request.RetrieverRequest; import com.redhat.composer.model.request.retriever.BaseRetrieverRequest; @@ -18,26 +19,59 @@ import jakarta.enterprise.inject.Default; +/** + * MapperUtil interface. + */ @Default @Mapper(config = QuarkusMapperConfig.class) public interface MapperUtil { RetrieverConnectionMapper retrieverConnectionMapper = Mappers.getMapper(RetrieverConnectionMapper.class); - @Mapping(target = "connectionEntity", source = "baseRetrieverRequest") + /** + * Maps a RetrieverRequest to a RetrieverConnectionEntity. + */ + @Mappings({ + @Mapping(target = "connectionEntity", source = "baseRetrieverRequest"), + @Mapping(target = "id", ignore = true) + }) RetrieverConnectionEntity toEntity(RetrieverRequest request); - @Mapping(source = "connectionEntity", target = "baseRetrieverRequest") - RetrieverRequest toRequest(RetrieverConnectionEntity entity); + /** + * Maps a LLMRequest to a LLMConnectionEntity. + * @param request the LLMRequest to map + * @return the LLMConnectionEntity + */ + @Mapping(target = "id", ignore = true) + LlmConnectionEntity toEntity(LLMRequest request); - LLMConnectionEntity toEntity(LLMRequest request); - LLMRequest toRequest(LLMConnectionEntity entity); + /** + * Maps a RetrieverConnectionEntity to a RetrieverRequest. + * @param entity the RetrieverConnectionEntity to map + * @return the RetrieverRequest + */ + @Mapping(source = "connectionEntity", target = "baseRetrieverRequest") + RetrieverRequest toRequest(RetrieverConnectionEntity entity); + + /** + * Maps a LLMConnectionEntity to a LLMRequest. + * @param entity the LLMConnectionEntity to map + * @return the LLMRequest + */ + LLMRequest toRequest(LlmConnectionEntity entity); + /** + * Maps a BaseRetrieverRequest to a BaseRetrieverConnectionEntity. + * @param request the BaseRetrieverRequest to map + * @return the BaseRetrieverConnectionEntity + */ default BaseRetrieverConnectionEntity mapToBaseEntity(BaseRetrieverRequest request) { - if(request == null) { + + if (request == null) { return null; } + switch (ContentRetrieverType.fromString(request.getContentRetrieverType())) { case ContentRetrieverType.WEAVIATE: return retrieverConnectionMapper.toEntity((WeaviateRequest) request); @@ -48,19 +82,27 @@ default BaseRetrieverConnectionEntity mapToBaseEntity(BaseRetrieverRequest reque } } - default BaseRetrieverRequest mapToBaseRequest(BaseRetrieverConnectionEntity entity){ - if(entity == null || entity.getContentRetrieverType() == null) { + /** + * Maps a BaseRetrieverConnectionEntity to a BaseRetrieverRequest. + + * @param entity the BaseRetrieverConnectionEntity to map + * @return the BaseRetrieverRequest + */ + default BaseRetrieverRequest mapToBaseRequest(BaseRetrieverConnectionEntity entity) { + + if (entity == null || entity.getContentRetrieverType() == null) { return null; } + switch (entity.getContentRetrieverType()) { case ContentRetrieverType.WEAVIATE: return retrieverConnectionMapper.toRequest((WeaviateConnectionEntity) entity); case ContentRetrieverType.NEO4J: - return retrieverConnectionMapper.toRequest((Neo4JEntity) entity); + return retrieverConnectionMapper.toRequest((Neo4jEntity) entity); default: return null; } } - + } diff --git a/src/main/java/com/redhat/composer/util/mappers/QuarkusMapperConfig.java b/src/main/java/com/redhat/composer/util/mappers/QuarkusMapperConfig.java index 0a0d054..91a110e 100644 --- a/src/main/java/com/redhat/composer/util/mappers/QuarkusMapperConfig.java +++ b/src/main/java/com/redhat/composer/util/mappers/QuarkusMapperConfig.java @@ -2,8 +2,9 @@ import org.mapstruct.MapperConfig; +/** + * Quarkus Mapper Config. + */ @MapperConfig(componentModel = "cdi") public interface QuarkusMapperConfig { - - -} +} diff --git a/src/main/java/com/redhat/composer/util/mappers/RetrieverConnectionMapper.java b/src/main/java/com/redhat/composer/util/mappers/RetrieverConnectionMapper.java index 680ccfc..66ec816 100644 --- a/src/main/java/com/redhat/composer/util/mappers/RetrieverConnectionMapper.java +++ b/src/main/java/com/redhat/composer/util/mappers/RetrieverConnectionMapper.java @@ -3,26 +3,64 @@ import org.mapstruct.Mapper; import com.redhat.composer.model.enums.ContentRetrieverType; -import com.redhat.composer.model.mongo.contentRetrieverEntites.Neo4JEntity; -import com.redhat.composer.model.mongo.contentRetrieverEntites.WeaviateConnectionEntity; +import com.redhat.composer.model.mongo.contentretrieverentites.Neo4jEntity; +import com.redhat.composer.model.mongo.contentretrieverentites.WeaviateConnectionEntity; import com.redhat.composer.model.request.retriever.Neo4JRequest; import com.redhat.composer.model.request.retriever.WeaviateRequest; +/** + * RetrieverConnectionMapper interface. + */ @Mapper(config = QuarkusMapperConfig.class) public interface RetrieverConnectionMapper { + /** + * Maps a WeaviateConnectionEntity to a WeaviateRequest. + + * @param request the WeaviateConnectionEntity to map + * @return the WeaviateRequest + */ WeaviateConnectionEntity toEntity(WeaviateRequest request); - Neo4JEntity toEntity(Neo4JRequest request); + /** + * Maps a Neo4JEntity to a Neo4JRequest. + + * @param request the Neo4JEntity to map + * @return the Neo4JRequest + */ + Neo4jEntity toEntity(Neo4JRequest request); + + /** + * Maps a WeaviateConnectionEntity to a WeaviateRequest. + * @param entity the WeaviateConnectionEntity to map + * @return the WeaviateRequest + */ WeaviateRequest toRequest(WeaviateConnectionEntity entity); - Neo4JRequest toRequest(Neo4JEntity entity); + /** + * Maps a Neo4JEntity to a Neo4JRequest. + + * @param entity the Neo4JEntity to map + * @return the Neo4JRequest + */ + Neo4JRequest toRequest(Neo4jEntity entity); + + /** + * Maps a ContentRetrieverType to a String. + * @param contentRetrieverType the ContentRetrieverType to map + * @return type value + */ default String toString(ContentRetrieverType contentRetrieverType) { return contentRetrieverType.getType(); } + /** + * Maps a String to a ContentRetrieverType. + * @param value the String to map + * @return the ContentRetrieverType + */ default ContentRetrieverType toContentRetrieverType(String value) { return ContentRetrieverType.fromString(value); } diff --git a/src/main/resources/application-dev.properties b/src/main/resources/application-dev.properties index 1aa4043..a500025 100644 --- a/src/main/resources/application-dev.properties +++ b/src/main/resources/application-dev.properties @@ -13,4 +13,4 @@ quarkus.http.cors=true quarkus.http.cors.origins=* # Set logging to debug -logging.level.root=DEBUG \ No newline at end of file +logging.level.root=DEBUG diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 11159bf..06f1aee 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -30,7 +30,7 @@ mistral.default.temp=0.8 ############################################################ # Default OpenAI Model ############################################################ -openai.default.url= https://vllm-predictor-composer-ai-app.apps.cluster-c4gwj.c4gwj.sandbox1157.opentlc.com/v1 +openai.default.url=https://vllm-predictor-composer-ai-app.apps.cluster-c4gwj.c4gwj.sandbox1157.opentlc.com/v1 openai.default.apiKey=abc123 # openai.default.url=https://mistral-7b-instruct-v0-3-maas-apicast-production.apps.prod.rhoai.rh-aiservices-bu.com:443/v1 # openai.default.apiKey=