From f9882738114ff8fb3311f18e5a544ddefc7eaf54 Mon Sep 17 00:00:00 2001 From: Raja Kolli Date: Thu, 28 Mar 2024 12:59:38 +0000 Subject: [PATCH] feat : convert from get to post endpoint --- .../controller/AiController.java | 24 +++----- .../model/request/AIChatRequest.java | 13 +++++ .../model/response/AIChatResponse.java | 5 ++ .../service/AIChatService.java | 11 ++-- .../LlmRagWithSpringAiApplicationIntTest.java | 56 ++++++++++++------- 5 files changed, 69 insertions(+), 40 deletions(-) create mode 100644 rag-springai-openai-llm/src/main/java/com/learning/ai/llmragwithspringai/model/request/AIChatRequest.java create mode 100644 rag-springai-openai-llm/src/main/java/com/learning/ai/llmragwithspringai/model/response/AIChatResponse.java diff --git a/rag-springai-openai-llm/src/main/java/com/learning/ai/llmragwithspringai/controller/AiController.java b/rag-springai-openai-llm/src/main/java/com/learning/ai/llmragwithspringai/controller/AiController.java index 0cd2830..1945559 100644 --- a/rag-springai-openai-llm/src/main/java/com/learning/ai/llmragwithspringai/controller/AiController.java +++ b/rag-springai-openai-llm/src/main/java/com/learning/ai/llmragwithspringai/controller/AiController.java @@ -1,14 +1,13 @@ package com.learning.ai.llmragwithspringai.controller; +import com.learning.ai.llmragwithspringai.model.request.AIChatRequest; +import com.learning.ai.llmragwithspringai.model.response.AIChatResponse; import com.learning.ai.llmragwithspringai.service.AIChatService; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.Pattern; -import jakarta.validation.constraints.Size; -import java.util.Map; +import jakarta.validation.Valid; import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @@ -22,14 +21,9 @@ public AiController(AIChatService aiChatService) { this.aiChatService = aiChatService; } - @GetMapping("/chat") - Map ragService( - @RequestParam - @NotBlank(message = "Query cannot be empty") - @Size(max = 255, message = "Query exceeds maximum length") - @Pattern(regexp = "^[a-zA-Z0-9 ]*$", message = "Invalid characters in query") - String question) { - String chatResponse = aiChatService.chat(question); - return Map.of("response", chatResponse); + @PostMapping("/chat") + AIChatResponse ragService(@Valid @RequestBody AIChatRequest aiChatRequest) { + String chatResponse = aiChatService.chat(aiChatRequest.question()); + return new AIChatResponse(chatResponse); } } diff --git a/rag-springai-openai-llm/src/main/java/com/learning/ai/llmragwithspringai/model/request/AIChatRequest.java b/rag-springai-openai-llm/src/main/java/com/learning/ai/llmragwithspringai/model/request/AIChatRequest.java new file mode 100644 index 0000000..ffc9efe --- /dev/null +++ b/rag-springai-openai-llm/src/main/java/com/learning/ai/llmragwithspringai/model/request/AIChatRequest.java @@ -0,0 +1,13 @@ +package com.learning.ai.llmragwithspringai.model.request; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Pattern; +import jakarta.validation.constraints.Size; +import java.io.Serializable; + +public record AIChatRequest( + @NotBlank(message = "Query cannot be empty") + @Size(max = 800, message = "Query exceeds maximum length") + @Pattern(regexp = "^[a-zA-Z0-9 ?]*$", message = "Invalid characters in query") + String question) + implements Serializable {} diff --git a/rag-springai-openai-llm/src/main/java/com/learning/ai/llmragwithspringai/model/response/AIChatResponse.java b/rag-springai-openai-llm/src/main/java/com/learning/ai/llmragwithspringai/model/response/AIChatResponse.java new file mode 100644 index 0000000..c298a0b --- /dev/null +++ b/rag-springai-openai-llm/src/main/java/com/learning/ai/llmragwithspringai/model/response/AIChatResponse.java @@ -0,0 +1,5 @@ +package com.learning.ai.llmragwithspringai.model.response; + +import java.io.Serializable; + +public record AIChatResponse(String response) implements Serializable {} diff --git a/rag-springai-openai-llm/src/main/java/com/learning/ai/llmragwithspringai/service/AIChatService.java b/rag-springai-openai-llm/src/main/java/com/learning/ai/llmragwithspringai/service/AIChatService.java index dc41a0f..66a76a5 100644 --- a/rag-springai-openai-llm/src/main/java/com/learning/ai/llmragwithspringai/service/AIChatService.java +++ b/rag-springai-openai-llm/src/main/java/com/learning/ai/llmragwithspringai/service/AIChatService.java @@ -18,14 +18,13 @@ public class AIChatService { private static final String template = """ + You're assisting with questions about cricket - You're assisting with questions about cricketers Cricket is a bat-and-ball game that is played between two teams of eleven players on a field at the centre of which is a 22-yard (20-metre) pitch with a wicket at each end, each comprising two bails balanced on three stumps. Two players from the batting team (the striker and nonstriker) stand in front of either wicket, with one player from the fielding team (the bowler) bowling the ball towards the striker's wicket from the opposite end of the pitch. - The striker's goal is to hit the bowled ball and then switch places with the nonstriker, - with the batting team scoring one run for each exchange. + The striker's goal is to hit the bowled ball and then switch places with the nonstriker, with the batting team scoring one run for each exchange. Runs are also scored when the ball reaches or crosses the boundary of the field or when the ball is bowled illegally. Use the information from the DOCUMENTS section to provide accurate answers but act as if you knew this information innately. @@ -44,16 +43,16 @@ public AIChatService(ChatClient aiClient, VectorStore vectorStore) { this.vectorStore = vectorStore; } - public String chat(String message) { + public String chat(String searchQuery) { // Querying the VectorStore using natural language looking for the information about info asked. - List listOfSimilarDocuments = this.vectorStore.similaritySearch(message); + List listOfSimilarDocuments = this.vectorStore.similaritySearch(searchQuery); String documents = listOfSimilarDocuments.stream() .map(Document::getContent) .collect(Collectors.joining(System.lineSeparator())); // Constructing the systemMessage to indicate the AI model to use the passed information // to answer the question. Message systemMessage = new SystemPromptTemplate(template).createMessage(Map.of("documents", documents)); - UserMessage userMessage = new UserMessage(message); + UserMessage userMessage = new UserMessage(searchQuery); Prompt prompt = new Prompt(List.of(systemMessage, userMessage)); ChatResponse aiResponse = aiClient.call(prompt); return aiResponse.getResult().getOutput().getContent(); diff --git a/rag-springai-openai-llm/src/test/java/com/learning/ai/llmragwithspringai/LlmRagWithSpringAiApplicationIntTest.java b/rag-springai-openai-llm/src/test/java/com/learning/ai/llmragwithspringai/LlmRagWithSpringAiApplicationIntTest.java index 55d1977..6308f98 100644 --- a/rag-springai-openai-llm/src/test/java/com/learning/ai/llmragwithspringai/LlmRagWithSpringAiApplicationIntTest.java +++ b/rag-springai-openai-llm/src/test/java/com/learning/ai/llmragwithspringai/LlmRagWithSpringAiApplicationIntTest.java @@ -4,11 +4,15 @@ import static org.hamcrest.Matchers.*; import com.learning.ai.llmragwithspringai.config.AbstractIntegrationTest; +import com.learning.ai.llmragwithspringai.model.request.AIChatRequest; import io.restassured.RestAssured; +import io.restassured.http.ContentType; +import org.apache.http.HttpStatus; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; import org.springframework.boot.test.web.server.LocalServerPort; +import org.springframework.http.MediaType; @TestInstance(TestInstance.Lifecycle.PER_CLASS) class LlmRagWithSpringAiApplicationIntTest extends AbstractIntegrationTest { @@ -23,30 +27,37 @@ void setUp() { @Test void testRag() { - given().param("question", "What trophies did Rohit won") + given().contentType(MediaType.APPLICATION_JSON_VALUE) + .body(new AIChatRequest("What trophies did Rohit won?")) .when() - .get("/api/ai/chat") + .post("/api/ai/chat") .then() - .statusCode(200) + .statusCode(HttpStatus.SC_OK) .body("response", containsString("2007 T20 World Cup")) - .body("response", containsString("2013 ICC Champions Trophy")); + .body("response", containsString("2013 ICC Champions Trophy")) + .log() + .all(); } @Test void testRag2() { - given().param("question", "Who is successful IPL captain") + given().contentType(MediaType.APPLICATION_JSON_VALUE) + .body(new AIChatRequest("Who is successful IPL captain?")) .when() - .get("/api/ai/chat") + .post("/api/ai/chat") .then() .statusCode(200) - .body("response", containsString("Rohit Sharma")); + .body("response", containsString("Rohit Sharma")) + .log() + .all(); } @Test void testEmptyQuery() { - given().param("question", "") + given().contentType(ContentType.JSON) + .body(new AIChatRequest("")) .when() - .get("/api/ai/chat") + .post("/api/ai/chat") .then() .statusCode(400) .header("Content-Type", is("application/problem+json")) @@ -54,16 +65,19 @@ void testEmptyQuery() { .body("instance", is("/api/ai/chat")) .body("title", is("Constraint Violation")) .body("violations", hasSize(1)) - .body("violations[0].field", is("ragService.question")) - .body("violations[0].message", containsString("Query cannot be empty")); + .body("violations[0].field", is("question")) + .body("violations[0].message", containsString("Query cannot be empty")) + .log() + .all(); } @Test void testLongQueryString() { String longQuery = "a".repeat(1000); // Example of a very long query string - given().param("question", longQuery) + given().contentType(ContentType.JSON) + .body(new AIChatRequest(longQuery)) .when() - .get("/api/ai/chat") + .post("/api/ai/chat") .then() .statusCode(400) .header("Content-Type", is("application/problem+json")) @@ -71,15 +85,18 @@ void testLongQueryString() { .body("instance", is("/api/ai/chat")) .body("title", is("Constraint Violation")) .body("violations", hasSize(1)) - .body("violations[0].field", is("ragService.question")) - .body("violations[0].message", containsString("Query exceeds maximum length")); + .body("violations[0].field", is("question")) + .body("violations[0].message", containsString("Query exceeds maximum length")) + .log() + .all(); } @Test void testSpecialCharactersInQuery() { - given().param("question", "@#$%^&*()") + given().contentType(ContentType.JSON) + .body(new AIChatRequest("@#$%^&*()")) .when() - .get("/api/ai/chat") + .post("/api/ai/chat") .then() .statusCode(400) .header("Content-Type", is("application/problem+json")) @@ -87,8 +104,9 @@ void testSpecialCharactersInQuery() { .body("instance", is("/api/ai/chat")) .body("title", is("Constraint Violation")) .body("violations", hasSize(1)) - .body("violations[0].field", is("ragService.question")) + .body("violations[0].field", is("question")) .body("violations[0].message", containsString("Invalid characters in query")) - .log(); + .log() + .all(); } }