Handle "message_creation" responses in JSON format Occasionally, ChatGPT's "message_creation" response may include a JSON-structured output embedded within a plain text message. This update ensures these cases are properly managed. Change-Id: I16ef37448cd65bbf10ba779636b74754ba2317f8 Signed-off-by: Patrizio <patrizio.gelosi@amarulasolutions.com>
diff --git a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/mode/stateful/client/api/chatgpt/ChatGptClientStateful.java b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/mode/stateful/client/api/chatgpt/ChatGptClientStateful.java index f0eb5f8..3d7b0b7 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/mode/stateful/client/api/chatgpt/ChatGptClientStateful.java +++ b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/mode/stateful/client/api/chatgpt/ChatGptClientStateful.java
@@ -14,6 +14,10 @@ import com.googlesource.gerrit.plugins.chatgpt.mode.stateful.model.api.chatgpt.ChatGptThreadMessageResponse; import lombok.extern.slf4j.Slf4j; +import static com.googlesource.gerrit.plugins.chatgpt.utils.GsonUtils.getGson; +import static com.googlesource.gerrit.plugins.chatgpt.utils.JsonTextUtils.isJsonString; +import static com.googlesource.gerrit.plugins.chatgpt.utils.JsonTextUtils.unwrapJsonCode; + @Slf4j @Singleton public class ChatGptClientStateful extends ChatGptClient implements IChatGptClient { @@ -87,6 +91,17 @@ ChatGptThreadMessageResponse threadMessageResponse = chatGptThreadMessage.retrieveMessage( chatGptRun.getFirstStepDetails().getMessageCreation().getMessageId() ); - return new ChatGptResponseContent(threadMessageResponse.getContent().get(0).getText().getValue()); + String responseText = threadMessageResponse.getContent().get(0).getText().getValue(); + if (responseText == null) { + throw new RuntimeException("ChatGPT thread message response content is null"); + } + if (isJsonString(responseText)) { + return extractResponseContent(responseText); + } + return new ChatGptResponseContent(responseText); + } + + private ChatGptResponseContent extractResponseContent(String responseText) { + return getGson().fromJson(unwrapJsonCode(responseText), ChatGptResponseContent.class); } }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/utils/JsonTextUtils.java b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/utils/JsonTextUtils.java new file mode 100644 index 0000000..e03ca7b --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/utils/JsonTextUtils.java
@@ -0,0 +1,20 @@ +package com.googlesource.gerrit.plugins.chatgpt.utils; + +import lombok.extern.slf4j.Slf4j; + +import java.util.regex.Pattern; + +@Slf4j +public class JsonTextUtils extends TextUtils { + private static final Pattern JSON_DELIMITED = Pattern.compile("^.*?" + CODE_DELIMITER + "json\\s*(.*)\\s*" + + CODE_DELIMITER + ".*$", Pattern.DOTALL); + private static final Pattern JSON_OBJECT = Pattern.compile("^\\{.*\\}$", Pattern.DOTALL); + + public static String unwrapJsonCode(String text) { + return JSON_DELIMITED.matcher(text).replaceAll("$1"); + } + + public static boolean isJsonString(String text) { + return JSON_OBJECT.matcher(text).matches() || JSON_DELIMITED.matcher(text).matches(); + } +}
diff --git a/src/test/java/com/googlesource/gerrit/plugins/chatgpt/ChatGptReviewStatefulTest.java b/src/test/java/com/googlesource/gerrit/plugins/chatgpt/ChatGptReviewStatefulTest.java index 75be9c3..5b07882 100644 --- a/src/test/java/com/googlesource/gerrit/plugins/chatgpt/ChatGptReviewStatefulTest.java +++ b/src/test/java/com/googlesource/gerrit/plugins/chatgpt/ChatGptReviewStatefulTest.java
@@ -204,7 +204,7 @@ } @Test - public void gptMentionedInCommentMessageResponse() throws RestApiException { + public void gptMentionedInCommentMessageResponseText() throws RestApiException { String reviewMessageCommitMessage = getReviewMessage("__files/chatGptResponseRequestStateful.json", 0); chatGptPromptStateful.setCommentEvent(true); @@ -220,7 +220,33 @@ .willReturn(WireMock.aResponse() .withStatus(HTTP_OK) .withHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString()) - .withBodyFile("chatGptResponseThreadMessage.json"))); + .withBodyFile("chatGptResponseThreadMessageText.json"))); + + handleEventBasedOnType(true); + + ArgumentCaptor<ReviewInput> captor = testRequestSent(); + Assert.assertEquals(promptTagComments, requestContent); + Assert.assertEquals(reviewMessageCommitMessage, getCapturedMessage(captor, GERRIT_PATCH_SET_FILENAME)); + } + + @Test + public void gptMentionedInCommentMessageResponseJson() throws RestApiException { + String reviewMessageCommitMessage = getReviewMessage("__files/chatGptResponseRequestStateful.json", 0); + + chatGptPromptStateful.setCommentEvent(true); + // Mock the behavior of the ChatGPT retrieve-run-steps request + WireMock.stubFor(WireMock.get(WireMock.urlEqualTo(URI.create(config.getGptDomain() + + UriResourceLocatorStateful.runStepsUri(CHAT_GPT_THREAD_ID, CHAT_GPT_RUN_ID)).getPath())) + .willReturn(WireMock.aResponse() + .withStatus(HTTP_OK) + .withHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString()) + .withBodyFile("chatGptResponseRequestMessageStateful.json"))); + WireMock.stubFor(WireMock.get(WireMock.urlEqualTo(URI.create(config.getGptDomain() + + UriResourceLocatorStateful.threadMessageRetrieveUri(CHAT_GPT_THREAD_ID, CHAT_GPT_MESSAGE_ID)).getPath())) + .willReturn(WireMock.aResponse() + .withStatus(HTTP_OK) + .withHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString()) + .withBodyFile("chatGptResponseThreadMessageJson.json"))); handleEventBasedOnType(true);
diff --git a/src/test/resources/__files/chatGptResponseThreadMessageJson.json b/src/test/resources/__files/chatGptResponseThreadMessageJson.json new file mode 100644 index 0000000..0cabd66 --- /dev/null +++ b/src/test/resources/__files/chatGptResponseThreadMessageJson.json
@@ -0,0 +1,12 @@ +{ + "object": "thread.message", + "role": "assistant", + "content": [ + { + "type": "text", + "text": { + "value": "```json\n{\n \"replies\": [\n {\n \"id\": 0,\n \"reply\": \"The commit message 'Corrected Indentation in Module-Class Retrieval Line' accurately represents the change made in the code.\"\n }\n ]\n}\n```" + } + } + ] +}
diff --git a/src/test/resources/__files/chatGptResponseThreadMessage.json b/src/test/resources/__files/chatGptResponseThreadMessageText.json similarity index 100% rename from src/test/resources/__files/chatGptResponseThreadMessage.json rename to src/test/resources/__files/chatGptResponseThreadMessageText.json