Add StreamOutput config option

Added StreamOutput config parameter to control whether the ChatGPT
responses are streamed or batched.

Jira-Id: IT-103
Change-Id: I02986f99749a64bd039aa1658de91a93b78b53ce
Signed-off-by: Patrizio <patrizio.gelosi@amarulasolutions.com>
diff --git a/README.md b/README.md
index aa60949..e1986ac 100644
--- a/README.md
+++ b/README.md
@@ -197,6 +197,7 @@
   your preferred prompt.
 - `gptTemperature`: The default value is 1. What sampling temperature to use, between 0 and 2. Higher values like 0.8
   will make the output more random, while lower values like 0.2 will make it more focused and deterministic.
+- `gptStreamOutput`: The default value is true. Whether the response is expected in stream output mode or not.
 - `patchSetReduction`: The default value is false. If set to true, the plugin will attempt to reduce patch content by
   compressing redundant blank lines, tabs, import statements, etc., in order to decrease the token count.
 - `maxReviewLines`: The default value is 1000. This sets a limit on the number of lines of code included in the review.
@@ -215,4 +216,4 @@
 
 ## License
 
-Apache License 2.0
\ No newline at end of file
+Apache License 2.0
diff --git a/gerrit.config b/gerrit.config
index f6bddb9..cf859cc 100644
--- a/gerrit.config
+++ b/gerrit.config
@@ -1,6 +1,7 @@
 [plugin "chatgpt-code-review-gerrit-plugin"]
 	gptToken = <CHATGPT_API_TOKEN>
 	gptModel = gpt-4
+	gptStreamOutput = false
 	gerritAuthBaseUrl = <CANONICAL_WEB_URL>
 	gerritUserName = <GERRIT_USER_NAME>
 	gerritPassword = <GERRIT_PASSWORD>
diff --git a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/ChatCompletionBase.java b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/ChatCompletionBase.java
new file mode 100644
index 0000000..202454a
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/ChatCompletionBase.java
@@ -0,0 +1,28 @@
+package com.googlesource.gerrit.plugins.chatgpt.client;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+
+@Data
+public class ChatCompletionBase {
+
+    protected String id;
+    protected String object;
+    protected long created;
+    protected String model;
+
+
+    @Data
+    public static class Choice {
+        protected Delta delta;
+        protected int index;
+        @SerializedName("finish_reason")
+        protected String finishReason;
+    }
+
+    @Data
+    public static class Delta {
+        private String role;
+        private String content;
+    }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/ChatCompletionResponse.java b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/ChatCompletionResponse.java
index 7f1123c..ee0c1db 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/ChatCompletionResponse.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/ChatCompletionResponse.java
@@ -1,30 +1,11 @@
 package com.googlesource.gerrit.plugins.chatgpt.client;
 
-import com.google.gson.annotations.SerializedName;
 import lombok.Data;
 
 import java.util.List;
 
 @Data
-public class ChatCompletionResponse {
+public class ChatCompletionResponse extends ChatCompletionBase {
 
-    private String id;
-    private String object;
-    private long created;
-    private String model;
     private List<Choice> choices;
-
-    @Data
-    public static class Choice {
-        private Delta delta;
-        private int index;
-        @SerializedName("finish_reason")
-        private String finishReason;
-    }
-
-    @Data
-    public static class Delta {
-        private String role;
-        private String content;
-    }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/ChatCompletionResponseMessage.java b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/ChatCompletionResponseMessage.java
new file mode 100644
index 0000000..c9bd202
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/ChatCompletionResponseMessage.java
@@ -0,0 +1,23 @@
+package com.googlesource.gerrit.plugins.chatgpt.client;
+
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class ChatCompletionResponseMessage extends ChatCompletionBase {
+
+    private List<MessageChoice> choices;
+
+
+    @Data
+    public static class MessageChoice extends ChatCompletionBase.Choice {
+        private Message message;
+    }
+
+    @Data
+    public static class Message {
+        private String role;
+        private String content;
+    }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/OpenAiClient.java b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/OpenAiClient.java
index 5343f00..43e7528 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/OpenAiClient.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/client/OpenAiClient.java
@@ -33,16 +33,28 @@
         if (body == null) {
             throw new IOException("responseBody is null");
         }
+        String content = extractContent(config, body);
+        log.debug("content: {}", content);
 
-        StringBuilder finalContent = new StringBuilder();
-        try (BufferedReader reader = new BufferedReader(new StringReader(body))) {
-            String line;
-            while ((line = reader.readLine()) != null) {
-                extractContentFromLine(line).ifPresent(finalContent::append);
+        return content;
+    }
+
+    public String extractContent(Configuration config, String body) throws Exception {
+        if (config.getGptStreamOutput()) {
+            StringBuilder finalContent = new StringBuilder();
+            try (BufferedReader reader = new BufferedReader(new StringReader(body))) {
+                String line;
+                while ((line = reader.readLine()) != null) {
+                    extractContentFromLine(line).ifPresent(finalContent::append);
+                }
             }
+            return finalContent.toString();
         }
-
-        return finalContent.toString();
+        else {
+            ChatCompletionResponseMessage chatCompletionResponseMessage =
+                    gson.fromJson(body, ChatCompletionResponseMessage.class);
+            return chatCompletionResponseMessage.getChoices().get(0).getMessage().getContent();
+        }
     }
 
     private HttpRequest createRequest(Configuration config, String patchSet) {
@@ -75,7 +87,7 @@
                 .model(config.getGptModel())
                 .messages(messages)
                 .temperature(config.getGptTemperature())
-                .stream(true)
+                .stream(config.getGptStreamOutput())
                 .build();
 
         return gson.toJson(chatCompletionRequest);
@@ -93,4 +105,4 @@
         return Optional.ofNullable(content);
     }
 
-}
\ No newline at end of file
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/config/Configuration.java b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/config/Configuration.java
index 2f30c57..13a8f1f 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/config/Configuration.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/config/Configuration.java
@@ -15,6 +15,7 @@
     public static final String NOT_CONFIGURED_ERROR_MSG = "%s is not configured";
     public static final String KEY_GPT_PROMPT = "gptPrompt";
     private static final String DEFAULT_GPT_TEMPERATURE = "1";
+    private static final boolean DEFAULT_STREAM_OUTPUT = true;
     private static final boolean DEFAULT_GLOBAL_ENABLE = false;
     private static final String DEFAULT_ENABLED_PROJECTS = "";
     private static final boolean DEFAULT_PATCH_SET_REDUCTION = false;
@@ -27,6 +28,7 @@
     private static final String KEY_GPT_DOMAIN = "gptDomain";
     private static final String KEY_GPT_MODEL = "gptModel";
     private static final String KEY_GPT_TEMPERATURE = "gptTemperature";
+    private static final String KEY_STREAM_OUTPUT = "gptStreamOutput";
     private static final String KEY_PROJECT_ENABLE = "isEnabled";
     private static final String KEY_GLOBAL_ENABLE = "globalEnable";
     private static final String KEY_ENABLED_PROJECTS = "enabledProjects";
@@ -80,6 +82,10 @@
         return Double.parseDouble(getString(KEY_GPT_TEMPERATURE, DEFAULT_GPT_TEMPERATURE));
     }
 
+    public boolean getGptStreamOutput() {
+        return getBoolean(KEY_STREAM_OUTPUT, DEFAULT_STREAM_OUTPUT);
+    }
+
     public boolean isProjectEnable() {
         return projectConfig.getBoolean(KEY_PROJECT_ENABLE, DEFAULT_PROJECT_ENABLE);
     }
diff --git a/src/test/java/com/googlesource/gerrit/plugins/chatgpt/ChatGptReviewTest.java b/src/test/java/com/googlesource/gerrit/plugins/chatgpt/ChatGptReviewTest.java
index 25e83fc..f1254e4 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/chatgpt/ChatGptReviewTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/chatgpt/ChatGptReviewTest.java
@@ -65,6 +65,7 @@
         when(config.getGerritAuthBaseUrl()).thenReturn("http://localhost:9527");
         when(config.getGptDomain()).thenReturn("http://localhost:9527");
         when(config.getGptTemperature()).thenReturn(1.0);
+        when(config.getGptStreamOutput()).thenReturn(true);
         when(config.getMaxReviewLines()).thenReturn(500);
         when(config.getEnabledProjects()).thenReturn("");
         when(config.isProjectEnable()).thenReturn(true);