diff --git a/pom.xml b/pom.xml index 19252be..79989f6 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ 3.31.4 true 3.5.4 - 0.4.2 + 0.4.3-SNAPSHOT diff --git a/src/main/java/net/dontcode/prj/generate/GenerateProjectModel.java b/src/main/java/net/dontcode/prj/generate/GenerateProjectModel.java index 253e4f2..604792b 100644 --- a/src/main/java/net/dontcode/prj/generate/GenerateProjectModel.java +++ b/src/main/java/net/dontcode/prj/generate/GenerateProjectModel.java @@ -2,7 +2,7 @@ import net.dontcode.core.project.DontCodeProjectModel; -public record GenerateProjectModel(String response, DontCodeProjectModel model) +public record GenerateProjectModel(String response, String error, DontCodeProjectModel model) { } diff --git a/src/main/java/net/dontcode/prj/generate/GenerateProjectResource.java b/src/main/java/net/dontcode/prj/generate/GenerateProjectResource.java index e33cc27..e154fe6 100644 --- a/src/main/java/net/dontcode/prj/generate/GenerateProjectResource.java +++ b/src/main/java/net/dontcode/prj/generate/GenerateProjectResource.java @@ -3,14 +3,18 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import io.quarkus.websockets.next.OnError; -import io.quarkus.websockets.next.OnOpen; import io.quarkus.websockets.next.OnTextMessage; import io.quarkus.websockets.next.WebSocket; +import io.quarkus.websockets.next.WebSocketConnection; +import io.smallrye.common.annotation.Blocking; +import io.smallrye.mutiny.Uni; +import io.smallrye.mutiny.infrastructure.Infrastructure; import jakarta.inject.Inject; -import net.dontcode.core.project.DontCodeProjectModel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.IOException; + @WebSocket(path = "/generate") public class GenerateProjectResource { private static Logger log = LoggerFactory.getLogger(GenerateProjectResource.class); @@ -21,20 +25,26 @@ public class GenerateProjectResource { public GenerateProjectResource() { } - - @OnOpen - public String onOpen() { - return "Hello, please describe the application you want to generate."; - } - @OnTextMessage() - public String onMessage(String message) { - return projectToString(service.generateProjectJson(message)); + public Uni onMessage(WebSocketConnection connection, String message) throws IOException { + return Uni.createFrom().item(() -> { + try { + GenerateProjectModel model = service.generateProjectJson(connection.id(),message); + return projectToString(model); + } catch (Throwable err) { + log.error("Error calling Application Generator", err); + return errorResponse (err); + } + }).runSubscriptionOn(Infrastructure.getDefaultWorkerPool()); } @OnError public String onError(Throwable throwable) { - return throwable.getMessage(); + return errorResponse(throwable); + } + + protected String errorResponse(Throwable throwable) { + return projectToString(new GenerateProjectModel(null, throwable.getMessage(), null)); } protected String projectToString (GenerateProjectModel prj) { @@ -43,6 +53,7 @@ protected String projectToString (GenerateProjectModel prj) { try { json = mapper.writeValueAsString(prj); } catch (JsonProcessingException e) { + System.err.println("Error converting GenerateProjectModel to JSON " + prj); throw new RuntimeException("Error decoding project", e); } return json; diff --git a/src/main/java/net/dontcode/prj/generate/GenerateProjectService.java b/src/main/java/net/dontcode/prj/generate/GenerateProjectService.java index b4fe34c..f3701f7 100644 --- a/src/main/java/net/dontcode/prj/generate/GenerateProjectService.java +++ b/src/main/java/net/dontcode/prj/generate/GenerateProjectService.java @@ -1,8 +1,9 @@ package net.dontcode.prj.generate; +import dev.langchain4j.service.MemoryId; import dev.langchain4j.service.SystemMessage; import io.quarkiverse.langchain4j.RegisterAiService; -import jakarta.enterprise.context.SessionScoped; +import jakarta.enterprise.context.RequestScoped; @RegisterAiService @SystemMessage(""" @@ -16,10 +17,10 @@ These fields are included in the "fields" list of each entity, a field can have the following pre-defined types: "number","string","date","time","date-time","currency","country","money-amount","eur-amount","usd-amount","image","link","rating","recurring-task","task-complete" A field can be of the type of another entity as well. - Optionally, a field can reference another entity by adding "reference" to the field description and by filling all the necessaries information to link both entities. + Optionally, a field can reference another entity by adding "reference" to the field description and by filling all the necessaries information to link both entities. For now, reference type can only be "OneToMany" """) -@SessionScoped +@RequestScoped public interface GenerateProjectService { - GenerateProjectModel generateProjectJson (String msg); + GenerateProjectModel generateProjectJson (@MemoryId String memoryId, String msg); } diff --git a/src/test/java/net/dontcode/prj/GenerateProjectResourceTest.java b/src/test/java/net/dontcode/prj/GenerateProjectResourceTest.java index b92a00b..9e3bc31 100644 --- a/src/test/java/net/dontcode/prj/GenerateProjectResourceTest.java +++ b/src/test/java/net/dontcode/prj/GenerateProjectResourceTest.java @@ -1,9 +1,12 @@ package net.dontcode.prj; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; import io.quarkus.test.InjectMock; import io.quarkus.test.common.http.TestHTTPResource; import io.quarkus.test.junit.QuarkusTest; import jakarta.websocket.*; +import net.dontcode.core.Message; import net.dontcode.core.project.*; import net.dontcode.prj.generate.GenerateProjectModel; import net.dontcode.prj.generate.GenerateProjectService; @@ -30,7 +33,7 @@ public class GenerateProjectResourceTest { public void testGeneration() throws DeploymentException, IOException, InterruptedException { DontCodeProjectEntities[] entities = new DontCodeProjectEntities[]{}; Mockito.when(serviceMock.generateProjectJson(anyString())).thenReturn( - new GenerateProjectModel("Here is an application that would fit", + new GenerateProjectModel("Here is an application that would fit", null, new DontCodeProjectModel("Test", "Test application.", new DontCodeProjectContent( new DontCodeProjectCreation("Test App", DontCodeProjectCreationType.application, entities)))) @@ -58,6 +61,10 @@ public void testGeneration() throws DeploymentException, IOException, Interrupte } Assertions.assertNotNull(ClientTestSession.response); + + GenerateProjectModel model=parseResponse (ClientTestSession.response); + Assertions.assertNotNull(model.model().name()); + ClientTestSession.response=null; session.getAsyncRemote().sendText("The application should be named Super Test").get(); @@ -70,6 +77,8 @@ public void testGeneration() throws DeploymentException, IOException, Interrupte } Assertions.assertNotNull(ClientTestSession.response); + model=parseResponse (ClientTestSession.response); + Assertions.assertNotNull(model.model().name()); Mockito.verify(serviceMock, Mockito.times(2)).generateProjectJson(anyString()); } catch (ExecutionException e) { @@ -78,6 +87,17 @@ public void testGeneration() throws DeploymentException, IOException, Interrupte } } + private GenerateProjectModel parseResponse(String response) { + ObjectMapper mapper = new ObjectMapper(); + GenerateProjectModel obj = null; + try { + obj = mapper.readValue(response, GenerateProjectModel.class); + } catch (JsonProcessingException e) { + throw new RuntimeException( "Cannot decode ProjectModel "+ response); + } + return obj; + } + @ClientEndpoint public static class ClientTestSession { diff --git a/src/test/java/net/dontcode/prj/GenerateProjectServiceIT.java b/src/test/java/net/dontcode/prj/GenerateProjectServiceIT.java index 3ef22ce..e4e58bd 100644 --- a/src/test/java/net/dontcode/prj/GenerateProjectServiceIT.java +++ b/src/test/java/net/dontcode/prj/GenerateProjectServiceIT.java @@ -33,7 +33,7 @@ public void testSimpleApplication () throws DeploymentException, IOException, In } Assertions.assertTrue(wsClient.opened, "Session was not opened"); - String response=wsClient.waitForMessage(10); + String response=wsClient.waitForMessage(70); Assertions.assertNotNull(response); session.getBasicRemote().sendText("Please create a cooking recipe application");