From 4f2cabc5c211dd89877595b3202157fc3759a278 Mon Sep 17 00:00:00 2001 From: Jordan Ang Date: Mon, 23 Sep 2024 23:19:25 -0700 Subject: [PATCH 01/10] Log partial result --- .../amazonq/chat/ChatMessageProvider.java | 4 +++ .../chat/models/ChatRequestParams.java | 33 ++++++++++++++++--- .../amazonq/lsp/AmazonQLspClientImpl.java | 6 ++++ .../amazonq/lsp/AmazonQLspServerBuilder.java | 3 ++ 4 files changed, 42 insertions(+), 4 deletions(-) diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatMessageProvider.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatMessageProvider.java index 5470cd5f..f25c5628 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatMessageProvider.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatMessageProvider.java @@ -3,6 +3,7 @@ package software.aws.toolkits.eclipse.amazonq.chat; import java.util.concurrent.CompletableFuture; +import java.util.UUID; import software.aws.toolkits.eclipse.amazonq.chat.models.ChatRequestParams; import software.aws.toolkits.eclipse.amazonq.chat.models.ChatResult; @@ -28,6 +29,9 @@ private ChatMessageProvider(final AmazonQLspServer amazonQLspServer) { public CompletableFuture sendChatPrompt(final ChatRequestParams chatRequestParams) { try { + String partialResultToken = UUID.randomUUID().toString(); + chatRequestParams.setPartialResultToken(partialResultToken); + PluginLogger.info("Sending " + Command.CHAT_SEND_PROMPT + " message to Amazon Q LSP server"); return amazonQLspServer.sendChatPrompt(chatRequestParams); } catch (Exception e) { diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/models/ChatRequestParams.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/models/ChatRequestParams.java index 89d50f74..42d31cdd 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/models/ChatRequestParams.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/models/ChatRequestParams.java @@ -4,8 +4,33 @@ import com.fasterxml.jackson.annotation.JsonProperty; -public record ChatRequestParams( - @JsonProperty("tabId") String tabId, - @JsonProperty("prompt") ChatPrompt prompt -) { } +public class ChatRequestParams { + private final String tabId; + private final ChatPrompt prompt; + private String partialResultToken; + + public ChatRequestParams( + @JsonProperty("tabId") String tabId, + @JsonProperty("prompt") ChatPrompt prompt + ) { + this.tabId = tabId; + this.prompt = prompt; + } + + public String getTabId() { + return tabId; + } + + public ChatPrompt getPrompt() { + return prompt; + } + + public String getPartialResultToken() { + return partialResultToken; + } + + public void setPartialResultToken(String partialResultToken) { + this.partialResultToken = partialResultToken; + } +} diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspClientImpl.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspClientImpl.java index 09f47053..fa2fca44 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspClientImpl.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspClientImpl.java @@ -11,11 +11,13 @@ import org.eclipse.lsp4e.LanguageClientImpl; import org.eclipse.lsp4j.ConfigurationParams; +import org.eclipse.lsp4j.ProgressParams; import software.aws.toolkits.eclipse.amazonq.configuration.PluginStore; import software.aws.toolkits.eclipse.amazonq.lsp.model.ConnectionMetadata; import software.aws.toolkits.eclipse.amazonq.lsp.model.SsoProfileData; import software.aws.toolkits.eclipse.amazonq.util.Constants; +import software.aws.toolkits.eclipse.amazonq.util.PluginLogger; @SuppressWarnings("restriction") public class AmazonQLspClientImpl extends LanguageClientImpl implements AmazonQLspClient { @@ -49,4 +51,8 @@ public final CompletableFuture> configuration(final ConfigurationPa return CompletableFuture.completedFuture(output); } + @Override + public void notifyProgress(final ProgressParams params) { + PluginLogger.info("Notify Progress message caught: " + params.toString()); + } } diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspServerBuilder.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspServerBuilder.java index bd2685df..a7ea0e33 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspServerBuilder.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspServerBuilder.java @@ -13,6 +13,7 @@ import software.aws.toolkits.eclipse.amazonq.providers.LspProvider; import software.aws.toolkits.eclipse.amazonq.util.ClientMetadata; +import software.aws.toolkits.eclipse.amazonq.util.PluginLogger; public class AmazonQLspServerBuilder extends Builder { @@ -27,6 +28,8 @@ public final Launcher create() { @Override protected final MessageConsumer wrapMessageConsumer(final MessageConsumer consumer) { return super.wrapMessageConsumer((Message message) -> { + PluginLogger.info("Raw Message Received: " + message.toString()); + if (message instanceof RequestMessage && ((RequestMessage) message).getMethod().equals("initialize")) { InitializeParams initParams = (InitializeParams) ((RequestMessage) message).getParams(); initParams.setClientInfo(new ClientInfo(ClientMetadata.getPluginName(), ClientMetadata.getPluginVersion())); From ad3d3a8ae7e62782868cb767985f4c8d2bc5d2a4 Mon Sep 17 00:00:00 2001 From: Jordan Ang Date: Tue, 24 Sep 2024 00:54:27 -0700 Subject: [PATCH 02/10] Reaching partial result handler --- .../chat/ChatCommunicationManager.java | 45 +++++++++++++++++++ .../eclipse/amazonq/chat/ChatMessage.java | 41 +++++++++++++++++ .../amazonq/chat/ChatMessageProvider.java | 13 ++---- .../chat/ChatPartialResultManager.java | 42 +++++++++++++++++ .../amazonq/lsp/AmazonQLspClientImpl.java | 5 ++- .../amazonq/lsp/AmazonQLspServerBuilder.java | 2 +- 6 files changed, 136 insertions(+), 12 deletions(-) create mode 100644 plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatMessage.java create mode 100644 plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatPartialResultManager.java diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatCommunicationManager.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatCommunicationManager.java index 123dd04b..46390d03 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatCommunicationManager.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatCommunicationManager.java @@ -4,8 +4,12 @@ import java.util.concurrent.CompletableFuture; +import org.eclipse.lsp4j.ProgressParams; import org.eclipse.swt.browser.Browser; +import com.google.gson.Gson; +import com.google.gson.JsonElement; + import software.aws.toolkits.eclipse.amazonq.chat.models.ChatRequestParams; import software.aws.toolkits.eclipse.amazonq.chat.models.ChatResult; import software.aws.toolkits.eclipse.amazonq.chat.models.ChatUIInboundCommand; @@ -19,10 +23,15 @@ public final class ChatCommunicationManager { private final JsonHandler jsonHandler; private final CompletableFuture chatMessageProvider; + private final ChatPartialResultManager chatPartialResultManager; + private Gson gson; + public ChatCommunicationManager() { this.jsonHandler = new JsonHandler(); this.chatMessageProvider = ChatMessageProvider.createAsync(); + this.chatPartialResultManager = ChatPartialResultManager.getInstance(); + this.gson = new Gson(); } public CompletableFuture sendMessageToChatServer(final Command command, final Object params) { @@ -56,5 +65,41 @@ public void sendMessageToChatUI(final Browser browser, final ChatUIInboundComman browser.evaluate(script); }); } + + /* + * Handles progress notifications from the Amazon Q LSP server. Sends a chat prompt message to the webview. + */ + public void handleProgressNotification(ProgressParams params) { + String token; + + // Convert token to String + if (params.getToken().isLeft()) { + token = params.getToken().getLeft(); + } else { + token = params.getToken().getRight().toString(); + } + if (!chatPartialResultManager.shouldHandlePartialResult(token)) { + PluginLogger.info("Not a partial result notification"); + return; + } + + if (params.getValue().isLeft()) { + PluginLogger.info("Expected object not WorkDoneProgressNotifcation"); + return; + } + + Object value = params.getValue().getRight(); + + if (!(value instanceof JsonElement)) { + PluginLogger.info("Value is not the expected JsonElement"); + return; + } + + ChatResult chatResult = gson.fromJson(((JsonElement)value), ChatResult.class); + + // ChatResult chatResult = jsonHandler.convertObject(paramsObject, ChatResult.class); + + chatPartialResultManager.handlePartialResult(this, chatResult); + } } diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatMessage.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatMessage.java new file mode 100644 index 00000000..8592ef52 --- /dev/null +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatMessage.java @@ -0,0 +1,41 @@ +// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. + +package software.aws.toolkits.eclipse.amazonq.chat; + +import java.util.UUID; +import java.util.concurrent.ExecutionException; + +import software.aws.toolkits.eclipse.amazonq.chat.models.ChatRequestParams; +import software.aws.toolkits.eclipse.amazonq.chat.models.ChatResult; +import software.aws.toolkits.eclipse.amazonq.exception.AmazonQPluginException; +import software.aws.toolkits.eclipse.amazonq.lsp.AmazonQLspServer; +import software.aws.toolkits.eclipse.amazonq.util.PluginLogger; +import software.aws.toolkits.eclipse.amazonq.views.model.Command; + +public class ChatMessage { + private final ChatRequestParams chatRequestParams; + private final ChatPartialResultManager chatPartialResultManager; + private final AmazonQLspServer amazonQLspServer; + + public ChatMessage(final AmazonQLspServer amazonQLspServer, final ChatRequestParams chatRequestParams) { + this.amazonQLspServer = amazonQLspServer; + this.chatRequestParams = chatRequestParams; + this.chatPartialResultManager = ChatPartialResultManager.getInstance(); + } + + public ChatResult sendChatMessageWithProgress() { + try { + String partialResultToken = UUID.randomUUID().toString(); + chatRequestParams.setPartialResultToken(partialResultToken); + + chatPartialResultManager.setPartialResultTokenMapEntry(partialResultToken, this); + + PluginLogger.info("Sending " + Command.CHAT_SEND_PROMPT + " message to Amazon Q LSP server"); + ChatResult chatResult = amazonQLspServer.sendChatPrompt(chatRequestParams).get(); + return chatResult; + } catch (InterruptedException | ExecutionException e) { + PluginLogger.error("Error occurred while sending " + Command.CHAT_SEND_PROMPT + " message to Amazon Q LSP server", e); + throw new AmazonQPluginException(e); + } + } +} diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatMessageProvider.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatMessageProvider.java index f25c5628..077b19dd 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatMessageProvider.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatMessageProvider.java @@ -4,6 +4,7 @@ import java.util.concurrent.CompletableFuture; import java.util.UUID; +import java.util.concurrent.ExecutionException; import software.aws.toolkits.eclipse.amazonq.chat.models.ChatRequestParams; import software.aws.toolkits.eclipse.amazonq.chat.models.ChatResult; @@ -28,16 +29,8 @@ private ChatMessageProvider(final AmazonQLspServer amazonQLspServer) { } public CompletableFuture sendChatPrompt(final ChatRequestParams chatRequestParams) { - try { - String partialResultToken = UUID.randomUUID().toString(); - chatRequestParams.setPartialResultToken(partialResultToken); - - PluginLogger.info("Sending " + Command.CHAT_SEND_PROMPT + " message to Amazon Q LSP server"); - return amazonQLspServer.sendChatPrompt(chatRequestParams); - } catch (Exception e) { - PluginLogger.error("Error occurred while sending " + Command.CHAT_SEND_PROMPT + " message to Amazon Q LSP server", e); - return CompletableFuture.failedFuture(new AmazonQPluginException(e)); - } + ChatMessage chatMessage = new ChatMessage(amazonQLspServer, chatRequestParams); + return chatMessage.sendChatMessageWithProgress(); } public void sendChatReady() { diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatPartialResultManager.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatPartialResultManager.java new file mode 100644 index 00000000..f81db15b --- /dev/null +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatPartialResultManager.java @@ -0,0 +1,42 @@ +// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. + +package software.aws.toolkits.eclipse.amazonq.chat; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import software.aws.toolkits.eclipse.amazonq.chat.models.ChatResult; +import software.aws.toolkits.eclipse.amazonq.util.PluginLogger; + +public class ChatPartialResultManager { + private static ChatPartialResultManager instance; + private final Map partialResultTokenMap; + + private ChatPartialResultManager() { + partialResultTokenMap = new ConcurrentHashMap(); + } + + public static synchronized ChatPartialResultManager getInstance() { + if (instance == null) { + instance = new ChatPartialResultManager(); + } + return instance; + } + + public void setPartialResultTokenMapEntry(String token, ChatMessage chatMessage) { + partialResultTokenMap.put(token, chatMessage); + } + + public void deletePartialResultTokenMapEntry(String token) { + partialResultTokenMap.remove(token); + } + + public Boolean shouldHandlePartialResult(String token) { + return token != null && partialResultTokenMap.containsKey(token); + } + + public void handlePartialResult(ChatCommunicationManager chatCommunicationManager, ChatResult chatResult){ + PluginLogger.info("Handling partial result...: " + chatResult.toString()); + } + +} diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspClientImpl.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspClientImpl.java index fa2fca44..4fa970da 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspClientImpl.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspClientImpl.java @@ -14,6 +14,7 @@ import org.eclipse.lsp4j.ProgressParams; import software.aws.toolkits.eclipse.amazonq.configuration.PluginStore; +import software.aws.toolkits.eclipse.amazonq.chat.ChatCommunicationManager; import software.aws.toolkits.eclipse.amazonq.lsp.model.ConnectionMetadata; import software.aws.toolkits.eclipse.amazonq.lsp.model.SsoProfileData; import software.aws.toolkits.eclipse.amazonq.util.Constants; @@ -53,6 +54,8 @@ public final CompletableFuture> configuration(final ConfigurationPa @Override public void notifyProgress(final ProgressParams params) { - PluginLogger.info("Notify Progress message caught: " + params.toString()); + PluginLogger.info("Notify Progress caught..."); + ChatCommunicationManager chatCommunicationManager = new ChatCommunicationManager(); + chatCommunicationManager.handleProgressNotification(params); } } diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspServerBuilder.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspServerBuilder.java index a7ea0e33..8dd533d4 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspServerBuilder.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspServerBuilder.java @@ -28,7 +28,7 @@ public final Launcher create() { @Override protected final MessageConsumer wrapMessageConsumer(final MessageConsumer consumer) { return super.wrapMessageConsumer((Message message) -> { - PluginLogger.info("Raw Message Received: " + message.toString()); + PluginLogger.info("Raw Message Received..."); if (message instanceof RequestMessage && ((RequestMessage) message).getMethod().equals("initialize")) { InitializeParams initParams = (InitializeParams) ((RequestMessage) message).getParams(); From 7e75224b93bbb2a904ec8419c3c23450cb75488d Mon Sep 17 00:00:00 2001 From: Jordan Ang Date: Tue, 24 Sep 2024 09:16:18 -0700 Subject: [PATCH 03/10] Rendering partial responses successfully --- .../chat/ChatCommunicationManager.java | 55 ++++--------------- .../eclipse/amazonq/chat/ChatMessage.java | 16 +++++- .../amazonq/chat/ChatMessageProvider.java | 4 +- .../chat/ChatPartialResultManager.java | 25 ++++----- .../amazonq/lsp/AmazonQLspClientImpl.java | 13 ++++- .../amazonq/lsp/AmazonQLspServerBuilder.java | 2 - .../eclipse/amazonq/util/JsonHandler.java | 16 ++++++ .../util/ProgressNotficationUtils.java | 46 ++++++++++++++++ .../views/AmazonQChatViewActionHandler.java | 44 ++++++++++++++- 9 files changed, 155 insertions(+), 66 deletions(-) create mode 100644 plugin/src/software/aws/toolkits/eclipse/amazonq/util/ProgressNotficationUtils.java diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatCommunicationManager.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatCommunicationManager.java index 46390d03..4ffed67a 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatCommunicationManager.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatCommunicationManager.java @@ -4,19 +4,14 @@ import java.util.concurrent.CompletableFuture; -import org.eclipse.lsp4j.ProgressParams; import org.eclipse.swt.browser.Browser; -import com.google.gson.Gson; -import com.google.gson.JsonElement; - import software.aws.toolkits.eclipse.amazonq.chat.models.ChatRequestParams; import software.aws.toolkits.eclipse.amazonq.chat.models.ChatResult; import software.aws.toolkits.eclipse.amazonq.chat.models.ChatUIInboundCommand; import software.aws.toolkits.eclipse.amazonq.chat.models.GenericTabParams; import software.aws.toolkits.eclipse.amazonq.exception.AmazonQPluginException; import software.aws.toolkits.eclipse.amazonq.util.JsonHandler; -import software.aws.toolkits.eclipse.amazonq.util.PluginLogger; import software.aws.toolkits.eclipse.amazonq.views.model.Command; public final class ChatCommunicationManager { @@ -24,14 +19,11 @@ public final class ChatCommunicationManager { private final JsonHandler jsonHandler; private final CompletableFuture chatMessageProvider; private final ChatPartialResultManager chatPartialResultManager; - private Gson gson; - public ChatCommunicationManager() { this.jsonHandler = new JsonHandler(); this.chatMessageProvider = ChatMessageProvider.createAsync(); this.chatPartialResultManager = ChatPartialResultManager.getInstance(); - this.gson = new Gson(); } public CompletableFuture sendMessageToChatServer(final Command command, final Object params) { @@ -59,47 +51,22 @@ public CompletableFuture sendMessageToChatServer(final Command comma } public void sendMessageToChatUI(final Browser browser, final ChatUIInboundCommand command) { - String message = this.jsonHandler.serialize(command); + // Mynah-ui will not render the partial result if null values are included. Must serialize with ignoreNulls set to True. + Boolean ignoreNull = true; + String message = this.jsonHandler.serialize(command, ignoreNull); + String script = "window.postMessage(" + message + ");"; browser.getDisplay().asyncExec(() -> { browser.evaluate(script); }); } - /* - * Handles progress notifications from the Amazon Q LSP server. Sends a chat prompt message to the webview. - */ - public void handleProgressNotification(ProgressParams params) { - String token; - - // Convert token to String - if (params.getToken().isLeft()) { - token = params.getToken().getLeft(); - } else { - token = params.getToken().getRight().toString(); - } - - if (!chatPartialResultManager.shouldHandlePartialResult(token)) { - PluginLogger.info("Not a partial result notification"); - return; - } - - if (params.getValue().isLeft()) { - PluginLogger.info("Expected object not WorkDoneProgressNotifcation"); - return; - } - - Object value = params.getValue().getRight(); - - if (!(value instanceof JsonElement)) { - PluginLogger.info("Value is not the expected JsonElement"); - return; - } - - ChatResult chatResult = gson.fromJson(((JsonElement)value), ChatResult.class); - - // ChatResult chatResult = jsonHandler.convertObject(paramsObject, ChatResult.class); - - chatPartialResultManager.handlePartialResult(this, chatResult); + public boolean isProcessingChatMessage(String partialResultToken) { + return chatPartialResultManager.hasKey(partialResultToken); + } + + public ChatMessage getChatMessage(String partialResultToken) { + return chatPartialResultManager.getValue(partialResultToken); } } + diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatMessage.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatMessage.java index 8592ef52..ccc75cd2 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatMessage.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatMessage.java @@ -5,6 +5,8 @@ import java.util.UUID; import java.util.concurrent.ExecutionException; +import org.eclipse.swt.browser.Browser; + import software.aws.toolkits.eclipse.amazonq.chat.models.ChatRequestParams; import software.aws.toolkits.eclipse.amazonq.chat.models.ChatResult; import software.aws.toolkits.eclipse.amazonq.exception.AmazonQPluginException; @@ -13,22 +15,32 @@ import software.aws.toolkits.eclipse.amazonq.views.model.Command; public class ChatMessage { + private final Browser browser; private final ChatRequestParams chatRequestParams; private final ChatPartialResultManager chatPartialResultManager; private final AmazonQLspServer amazonQLspServer; - public ChatMessage(final AmazonQLspServer amazonQLspServer, final ChatRequestParams chatRequestParams) { + public ChatMessage(final AmazonQLspServer amazonQLspServer, final Browser browser, final ChatRequestParams chatRequestParams) { this.amazonQLspServer = amazonQLspServer; + this.browser = browser; this.chatRequestParams = chatRequestParams; this.chatPartialResultManager = ChatPartialResultManager.getInstance(); } + public Browser getBrowser() { + return browser; + } + + public ChatRequestParams getChatRequestParams() { + return chatRequestParams; + } + public ChatResult sendChatMessageWithProgress() { try { String partialResultToken = UUID.randomUUID().toString(); chatRequestParams.setPartialResultToken(partialResultToken); - chatPartialResultManager.setPartialResultTokenMapEntry(partialResultToken, this); + chatPartialResultManager.setMapEntry(partialResultToken, this); PluginLogger.info("Sending " + Command.CHAT_SEND_PROMPT + " message to Amazon Q LSP server"); ChatResult chatResult = amazonQLspServer.sendChatPrompt(chatRequestParams).get(); diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatMessageProvider.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatMessageProvider.java index 077b19dd..7fa73ea3 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatMessageProvider.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatMessageProvider.java @@ -6,6 +6,8 @@ import java.util.UUID; import java.util.concurrent.ExecutionException; +import org.eclipse.swt.browser.Browser; + import software.aws.toolkits.eclipse.amazonq.chat.models.ChatRequestParams; import software.aws.toolkits.eclipse.amazonq.chat.models.ChatResult; import software.aws.toolkits.eclipse.amazonq.chat.models.GenericTabParams; @@ -28,7 +30,7 @@ private ChatMessageProvider(final AmazonQLspServer amazonQLspServer) { this.amazonQLspServer = amazonQLspServer; } - public CompletableFuture sendChatPrompt(final ChatRequestParams chatRequestParams) { + public CompletableFuture sendChatPrompt(Browser browser, final ChatRequestParams chatRequestParams) { ChatMessage chatMessage = new ChatMessage(amazonQLspServer, chatRequestParams); return chatMessage.sendChatMessageWithProgress(); } diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatPartialResultManager.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatPartialResultManager.java index f81db15b..5ae4318a 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatPartialResultManager.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatPartialResultManager.java @@ -10,10 +10,10 @@ public class ChatPartialResultManager { private static ChatPartialResultManager instance; - private final Map partialResultTokenMap; + private final Map tokenToChatMessageMap; private ChatPartialResultManager() { - partialResultTokenMap = new ConcurrentHashMap(); + tokenToChatMessageMap = new ConcurrentHashMap(); } public static synchronized ChatPartialResultManager getInstance() { @@ -23,20 +23,19 @@ public static synchronized ChatPartialResultManager getInstance() { return instance; } - public void setPartialResultTokenMapEntry(String token, ChatMessage chatMessage) { - partialResultTokenMap.put(token, chatMessage); + public void setMapEntry(String token, ChatMessage chatMessage) { + tokenToChatMessageMap.put(token, chatMessage); } - public void deletePartialResultTokenMapEntry(String token) { - partialResultTokenMap.remove(token); + public void deleteMapEntry(String token) { + tokenToChatMessageMap.remove(token); } - - public Boolean shouldHandlePartialResult(String token) { - return token != null && partialResultTokenMap.containsKey(token); + + public ChatMessage getValue(String token) { + return tokenToChatMessageMap.getOrDefault(token, null); } - - public void handlePartialResult(ChatCommunicationManager chatCommunicationManager, ChatResult chatResult){ - PluginLogger.info("Handling partial result...: " + chatResult.toString()); + + public Boolean hasKey(String token) { + return tokenToChatMessageMap.containsKey(token); } - } diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspClientImpl.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspClientImpl.java index 4fa970da..d0b85ab5 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspClientImpl.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspClientImpl.java @@ -19,6 +19,8 @@ import software.aws.toolkits.eclipse.amazonq.lsp.model.SsoProfileData; import software.aws.toolkits.eclipse.amazonq.util.Constants; import software.aws.toolkits.eclipse.amazonq.util.PluginLogger; +import software.aws.toolkits.eclipse.amazonq.util.ThreadingUtils; +import software.aws.toolkits.eclipse.amazonq.views.AmazonQChatViewActionHandler; @SuppressWarnings("restriction") public class AmazonQLspClientImpl extends LanguageClientImpl implements AmazonQLspClient { @@ -55,7 +57,14 @@ public final CompletableFuture> configuration(final ConfigurationPa @Override public void notifyProgress(final ProgressParams params) { PluginLogger.info("Notify Progress caught..."); - ChatCommunicationManager chatCommunicationManager = new ChatCommunicationManager(); - chatCommunicationManager.handleProgressNotification(params); + + ThreadingUtils.executeAsyncTask(() -> { + try { + AmazonQChatViewActionHandler chatActionHandler = new AmazonQChatViewActionHandler(); + chatActionHandler.handleProgressNotification(params);; + } catch (Exception e) { + PluginLogger.error("Error processing message from Browser", e); + } + }); } } diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspServerBuilder.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspServerBuilder.java index 8dd533d4..2a2225ed 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspServerBuilder.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspServerBuilder.java @@ -28,8 +28,6 @@ public final Launcher create() { @Override protected final MessageConsumer wrapMessageConsumer(final MessageConsumer consumer) { return super.wrapMessageConsumer((Message message) -> { - PluginLogger.info("Raw Message Received..."); - if (message instanceof RequestMessage && ((RequestMessage) message).getMethod().equals("initialize")) { InitializeParams initParams = (InitializeParams) ((RequestMessage) message).getParams(); initParams.setClientInfo(new ClientInfo(ClientMetadata.getPluginName(), ClientMetadata.getPluginVersion())); diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/util/JsonHandler.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/util/JsonHandler.java index 8ffc7803..46174319 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/util/JsonHandler.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/util/JsonHandler.java @@ -4,6 +4,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.annotation.JsonInclude; public final class JsonHandler { private final ObjectMapper objectMapper; @@ -22,6 +23,21 @@ public String serialize(final Object obj) { } return serializedObj; } + + public String serialize(final Object obj, Boolean ignoreNull) { + String serializedObj = null; + try { + ObjectMapper mapper = objectMapper.copy(); + if (ignoreNull) { + mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + } + serializedObj = mapper.writeValueAsString(obj); + } catch (JsonProcessingException e) { + PluginLogger.error("Error occurred while serializing object: " + obj.toString(), e); + return null; + } + return serializedObj; + } public T deserialize(final String jsonString, final Class cls) { try { diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/util/ProgressNotficationUtils.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/util/ProgressNotficationUtils.java new file mode 100644 index 00000000..95e73b73 --- /dev/null +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/util/ProgressNotficationUtils.java @@ -0,0 +1,46 @@ +// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. + +package software.aws.toolkits.eclipse.amazonq.util; + +import org.eclipse.lsp4j.ProgressParams; + +import com.google.gson.Gson; +import com.google.gson.JsonElement; + + +public class ProgressNotficationUtils { + /* + * Get the token from the ProgressParams value + * @return The token as a String + */ + public static String getToken(ProgressParams params) { + String token; + + if (params.getToken().isLeft()) { + token = params.getToken().getLeft(); + } else { + token = params.getToken().getRight().toString(); + } + + return token; + } + + /* + * Get the object from the ProgressParams value + * @param cls The class of the object to be deserialized + * @return The deserialized object, or null if the value is not a JsonElement + */ + public static T getObject(ProgressParams params, Class cls) { + Object val = params.getValue().getRight(); + + if (!(val instanceof JsonElement)) { + return null; + } + + Gson gson = new Gson(); + JsonElement element = (JsonElement) val; + T obj = gson.fromJson(element, cls); + + return obj; + } +} diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQChatViewActionHandler.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQChatViewActionHandler.java index 2e15f79d..05bda363 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQChatViewActionHandler.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQChatViewActionHandler.java @@ -3,15 +3,18 @@ package software.aws.toolkits.eclipse.amazonq.views; +import org.eclipse.lsp4j.ProgressParams; import org.eclipse.swt.browser.Browser; import software.aws.toolkits.eclipse.amazonq.chat.ChatCommunicationManager; +import software.aws.toolkits.eclipse.amazonq.chat.ChatMessage; import software.aws.toolkits.eclipse.amazonq.chat.models.ChatRequestParams; import software.aws.toolkits.eclipse.amazonq.chat.models.ChatUIInboundCommand; import software.aws.toolkits.eclipse.amazonq.chat.models.ChatUIInboundCommandName; import software.aws.toolkits.eclipse.amazonq.exception.AmazonQPluginException; import software.aws.toolkits.eclipse.amazonq.util.JsonHandler; import software.aws.toolkits.eclipse.amazonq.util.PluginLogger; +import software.aws.toolkits.eclipse.amazonq.util.ProgressNotficationUtils; import software.aws.toolkits.eclipse.amazonq.views.model.Command; import software.aws.toolkits.eclipse.amazonq.views.model.ParsedCommand; @@ -49,10 +52,10 @@ public final void handleCommand(final ParsedCommand parsedCommand, final Browser }); break; case CHAT_READY: - chatCommunicationManager.sendMessageToChatServer(command, params); + chatCommunicationManager.sendMessageToChatServer(browser, command, params); break; case CHAT_TAB_ADD: - chatCommunicationManager.sendMessageToChatServer(command, params); + chatCommunicationManager.sendMessageToChatServer(browser, command, params); break; case TELEMETRY_EVENT: break; @@ -60,5 +63,42 @@ public final void handleCommand(final ParsedCommand parsedCommand, final Browser throw new AmazonQPluginException("Unhandled command in AmazonQChatViewActionHandler: " + command.toString()); } } + + + /* + * Handles chat progress notifications from the Amazon Q LSP server. Sends a chat prompt message to the webview. + */ + public final void handleProgressNotification(ProgressParams params) { + String token = ProgressNotficationUtils.getToken(params); + if (!chatCommunicationManager.isProcessingChatMessage(token)) { + PluginLogger.info("Not processing - not a partial result token"); + return; + } + + if (params.getValue().isLeft()) { + String e = "Error occurred while handling partial result notification: Expected Object not WorkDoneProgressNotification value for partial result token " + token; + throw new AmazonQPluginException(e); + } + + PluginLogger.info("Handling progress notification"); + + ChatResult chatResult = ProgressNotficationUtils.getObject(params, ChatResult.class); + ChatMessage chatMessage = chatCommunicationManager.getChatMessage(token); + Browser browser = chatMessage.getBrowser(); + + // Sending a response would clear the loading indicator, so we should ensure that there is content first for a good UX + // (Note: Response is accumulative) + if (chatResult.body() != null && chatResult.body().length() > 0) { + ChatUIInboundCommand chatUIInboundCommand = new ChatUIInboundCommand( + ChatUIInboundCommandName.ChatPrompt.toString(), + chatMessage.getChatRequestParams().getTabId(), + chatResult, + true + ); + + chatCommunicationManager.sendMessageToChatUI(browser, chatUIInboundCommand); + } + + } } From d499aae20968db5bbe11574c43e1cab5897f7b55 Mon Sep 17 00:00:00 2001 From: Jordan Ang Date: Tue, 24 Sep 2024 16:53:42 -0700 Subject: [PATCH 04/10] Add comments --- .../chat/ChatCommunicationManager.java | 15 +++++- .../eclipse/amazonq/chat/ChatMessage.java | 4 ++ .../chat/ChatPartialResultManager.java | 12 +++++ .../amazonq/lsp/AmazonQLspClientImpl.java | 14 ++++-- .../views/AmazonQChatViewActionHandler.java | 46 ++++++++++--------- 5 files changed, 62 insertions(+), 29 deletions(-) diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatCommunicationManager.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatCommunicationManager.java index 4ffed67a..f3fddd26 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatCommunicationManager.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatCommunicationManager.java @@ -14,6 +14,11 @@ import software.aws.toolkits.eclipse.amazonq.util.JsonHandler; import software.aws.toolkits.eclipse.amazonq.views.model.Command; +/** + * ChatCommunicationManager is responsible for managing communication between + * the Amazon Q Eclipse Plugin and the LSP server as well as communication + * between the Amazon Q Eclipse Plugin and the webview. + */ public final class ChatCommunicationManager { private final JsonHandler jsonHandler; @@ -61,11 +66,17 @@ public void sendMessageToChatUI(final Browser browser, final ChatUIInboundComman }); } - public boolean isProcessingChatMessage(String partialResultToken) { + /* + * Checks if a partial result is being processed with the provided token. + */ + public boolean isProcessingPartialChatMessage(String partialResultToken) { return chatPartialResultManager.hasKey(partialResultToken); } - public ChatMessage getChatMessage(String partialResultToken) { + /* + * Gets the partial chat message using the provided token. + */ + public ChatMessage getPartialChatMessage(String partialResultToken) { return chatPartialResultManager.getValue(partialResultToken); } } diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatMessage.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatMessage.java index ccc75cd2..f8248b66 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatMessage.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatMessage.java @@ -35,6 +35,10 @@ public ChatRequestParams getChatRequestParams() { return chatRequestParams; } + public String getPartialResultToken() { + return chatRequestParams.getPartialResultToken(); + } + public ChatResult sendChatMessageWithProgress() { try { String partialResultToken = UUID.randomUUID().toString(); diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatPartialResultManager.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatPartialResultManager.java index 5ae4318a..dbe46c91 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatPartialResultManager.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatPartialResultManager.java @@ -8,6 +8,18 @@ import software.aws.toolkits.eclipse.amazonq.chat.models.ChatResult; import software.aws.toolkits.eclipse.amazonq.util.PluginLogger; +/** + * ChatPartialResultManager is responsible for maintaining a mapping between + * partial result tokens and the associated ChatMessage objects. It is implemented + * as a singleton to centralize control of all partial results in the plugin. + * + * $/progress notifications are caught and handled in the AmazonQLspClientImpl + * notifyProgress method. Within a progress notification, we are provided ProgressParams + * containing a token and a partial result object. The tokenToChatMessage map in + * this class allows us to find the original ChatMessage associated with the token. + * + * @see AmazonQLspClientImpl#notifyProgress(ProgressParams) + */ public class ChatPartialResultManager { private static ChatPartialResultManager instance; private final Map tokenToChatMessageMap; diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspClientImpl.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspClientImpl.java index d0b85ab5..7cf2e874 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspClientImpl.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspClientImpl.java @@ -35,6 +35,11 @@ public final CompletableFuture getConnectionMetadata() { return CompletableFuture.completedFuture(metadata); } + /* + * Handles the progress notifications received from the LSP server. + * - Process partial results for Chat messages if provided token is maintained by chatPartialResultManager + * - Other notifications are ignored at this time. + */ @Override public final CompletableFuture> configuration(final ConfigurationParams configurationParams) { if (configurationParams.getItems().size() == 0) { @@ -56,14 +61,13 @@ public final CompletableFuture> configuration(final ConfigurationPa @Override public void notifyProgress(final ProgressParams params) { - PluginLogger.info("Notify Progress caught..."); - + AmazonQChatViewActionHandler chatActionHandler = new AmazonQChatViewActionHandler(); + ThreadingUtils.executeAsyncTask(() -> { try { - AmazonQChatViewActionHandler chatActionHandler = new AmazonQChatViewActionHandler(); - chatActionHandler.handleProgressNotification(params);; + chatActionHandler.handlePartialResultProgressNotification(params);; } catch (Exception e) { - PluginLogger.error("Error processing message from Browser", e); + PluginLogger.error("Error processing partial result progress notification", e); } }); } diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQChatViewActionHandler.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQChatViewActionHandler.java index 05bda363..66502722 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQChatViewActionHandler.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQChatViewActionHandler.java @@ -3,6 +3,8 @@ package software.aws.toolkits.eclipse.amazonq.views; +import java.util.Objects; + import org.eclipse.lsp4j.ProgressParams; import org.eclipse.swt.browser.Browser; @@ -66,39 +68,39 @@ public final void handleCommand(final ParsedCommand parsedCommand, final Browser /* - * Handles chat progress notifications from the Amazon Q LSP server. Sends a chat prompt message to the webview. + * Handles chat progress notifications from the Amazon Q LSP server. Sends a partial chat prompt message to the webview. */ - public final void handleProgressNotification(ProgressParams params) { + public final void handlePartialResultProgressNotification(ProgressParams params) { String token = ProgressNotficationUtils.getToken(params); - if (!chatCommunicationManager.isProcessingChatMessage(token)) { - PluginLogger.info("Not processing - not a partial result token"); + // Check to ensure progress notification is a partial result for chat + if (!chatCommunicationManager.isProcessingPartialChatMessage(token)) { return; } - - if (params.getValue().isLeft()) { - String e = "Error occurred while handling partial result notification: Expected Object not WorkDoneProgressNotification value for partial result token " + token; + + // Check to ensure Object is sent in params + if (params.getValue().isLeft() || Objects.isNull(params.getValue().getRight())) { + String e = "Error occurred while handling partial result notification: expected Object value"; throw new AmazonQPluginException(e); } - - PluginLogger.info("Handling progress notification"); - + ChatResult chatResult = ProgressNotficationUtils.getObject(params, ChatResult.class); - ChatMessage chatMessage = chatCommunicationManager.getChatMessage(token); + ChatMessage chatMessage = chatCommunicationManager.getPartialChatMessage(token); Browser browser = chatMessage.getBrowser(); - // Sending a response would clear the loading indicator, so we should ensure that there is content first for a good UX - // (Note: Response is accumulative) - if (chatResult.body() != null && chatResult.body().length() > 0) { - ChatUIInboundCommand chatUIInboundCommand = new ChatUIInboundCommand( - ChatUIInboundCommandName.ChatPrompt.toString(), - chatMessage.getChatRequestParams().getTabId(), - chatResult, - true - ); - - chatCommunicationManager.sendMessageToChatUI(browser, chatUIInboundCommand); + // Check to ensure the body has content in order to keep displaying the spinner while loading + if (chatResult.body() == null && chatResult.body().length() == 0) { + return; } + Boolean isPartialResult = true; + ChatUIInboundCommand chatUIInboundCommand = new ChatUIInboundCommand( + ChatUIInboundCommandName.ChatPrompt.toString(), + chatMessage.getChatRequestParams().getTabId(), + chatResult, + isPartialResult + ); + + chatCommunicationManager.sendMessageToChatUI(browser, chatUIInboundCommand); } } From 53883a0da473e952a71b43de82a03147648f9049 Mon Sep 17 00:00:00 2001 From: Jordan Ang Date: Wed, 25 Sep 2024 12:13:44 -0700 Subject: [PATCH 05/10] Cleaned up code and added comments --- .../chat/ChatCommunicationManager.java | 39 +++++++++++++++---- .../eclipse/amazonq/chat/ChatMessage.java | 19 +++++---- ...Manager.java => ChatPartialResultMap.java} | 24 ++++-------- .../views/AmazonQChatViewActionHandler.java | 13 +++---- 4 files changed, 56 insertions(+), 39 deletions(-) rename plugin/src/software/aws/toolkits/eclipse/amazonq/chat/{ChatPartialResultManager.java => ChatPartialResultMap.java} (63%) diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatCommunicationManager.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatCommunicationManager.java index f3fddd26..1e08e58e 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatCommunicationManager.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatCommunicationManager.java @@ -3,6 +3,7 @@ package software.aws.toolkits.eclipse.amazonq.chat; import java.util.concurrent.CompletableFuture; +import java.util.UUID; import org.eclipse.swt.browser.Browser; @@ -17,20 +18,29 @@ /** * ChatCommunicationManager is responsible for managing communication between * the Amazon Q Eclipse Plugin and the LSP server as well as communication - * between the Amazon Q Eclipse Plugin and the webview. + * between the Amazon Q Eclipse Plugin and the webview. It is implemented + * as a singleton to centralize control of all communication in the plugin. */ public final class ChatCommunicationManager { + private static ChatCommunicationManager instance; private final JsonHandler jsonHandler; private final CompletableFuture chatMessageProvider; private final ChatPartialResultManager chatPartialResultManager; - public ChatCommunicationManager() { + private ChatCommunicationManager() { this.jsonHandler = new JsonHandler(); this.chatMessageProvider = ChatMessageProvider.createAsync(); this.chatPartialResultManager = ChatPartialResultManager.getInstance(); } + public static synchronized ChatCommunicationManager getInstance() { + if (instance == null) { + instance = new ChatCommunicationManager(); + } + return instance; + } + public CompletableFuture sendMessageToChatServer(final Command command, final Object params) { return chatMessageProvider.thenCompose(chatMessageProvider -> { try { @@ -67,17 +77,30 @@ public void sendMessageToChatUI(final Browser browser, final ChatUIInboundComman } /* - * Checks if a partial result is being processed with the provided token. + * Gets the partial chat message using the provided token. */ - public boolean isProcessingPartialChatMessage(String partialResultToken) { - return chatPartialResultManager.hasKey(partialResultToken); + public ChatMessage getPartialChatMessage(String partialResultToken) { + return chatPartialResultMap.getValue(partialResultToken); } /* - * Gets the partial chat message using the provided token. + * Adds an entry to the partialResultToken to ChatMessage map. */ - public ChatMessage getPartialChatMessage(String partialResultToken) { - return chatPartialResultManager.getValue(partialResultToken); + public String addPartialChatMessage(ChatMessage chatMessage) { + String partialResultToken = UUID.randomUUID().toString(); + + // Indicator for the server to send partial result notifications + chatMessage.getChatRequestParams().setPartialResultToken(partialResultToken); + + chatPartialResultMap.setEntry(partialResultToken, chatMessage); + return partialResultToken; + } + + /* + * Removes an entry from the partialResultToken to ChatMessage map. + */ + public void removePartialChatMessage(String partialResultToken) { + chatPartialResultMap.removeEntry(partialResultToken); } } diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatMessage.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatMessage.java index f8248b66..63f800e2 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatMessage.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatMessage.java @@ -2,7 +2,6 @@ package software.aws.toolkits.eclipse.amazonq.chat; -import java.util.UUID; import java.util.concurrent.ExecutionException; import org.eclipse.swt.browser.Browser; @@ -17,14 +16,14 @@ public class ChatMessage { private final Browser browser; private final ChatRequestParams chatRequestParams; - private final ChatPartialResultManager chatPartialResultManager; private final AmazonQLspServer amazonQLspServer; + private final ChatCommunicationManager chatCommunicationManager; public ChatMessage(final AmazonQLspServer amazonQLspServer, final Browser browser, final ChatRequestParams chatRequestParams) { this.amazonQLspServer = amazonQLspServer; this.browser = browser; this.chatRequestParams = chatRequestParams; - this.chatPartialResultManager = ChatPartialResultManager.getInstance(); + this.chatCommunicationManager = ChatCommunicationManager.getInstance(); } public Browser getBrowser() { @@ -41,13 +40,17 @@ public String getPartialResultToken() { public ChatResult sendChatMessageWithProgress() { try { - String partialResultToken = UUID.randomUUID().toString(); - chatRequestParams.setPartialResultToken(partialResultToken); - - chatPartialResultManager.setMapEntry(partialResultToken, this); - + // Retrieving the chat result is expected to be a long-running process with intermittent progress notifications being sent + // from the LSP server. The progress notifications provide a token and a result - we are utilizing this token to + // ChatMessage mapping to acquire the associated ChatMessage. + String partialResultToken = chatCommunicationManager.addPartialChatMessage(this); + PluginLogger.info("Sending " + Command.CHAT_SEND_PROMPT + " message to Amazon Q LSP server"); ChatResult chatResult = amazonQLspServer.sendChatPrompt(chatRequestParams).get(); + + // The mapping entry no longer needs to be maintained once the final result is retrieved. + chatCommunicationManager.removePartialChatMessage(partialResultToken); + return chatResult; } catch (InterruptedException | ExecutionException e) { PluginLogger.error("Error occurred while sending " + Command.CHAT_SEND_PROMPT + " message to Amazon Q LSP server", e); diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatPartialResultManager.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatPartialResultMap.java similarity index 63% rename from plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatPartialResultManager.java rename to plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatPartialResultMap.java index dbe46c91..7a71905e 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatPartialResultManager.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatPartialResultMap.java @@ -9,37 +9,29 @@ import software.aws.toolkits.eclipse.amazonq.util.PluginLogger; /** - * ChatPartialResultManager is responsible for maintaining a mapping between - * partial result tokens and the associated ChatMessage objects. It is implemented - * as a singleton to centralize control of all partial results in the plugin. + * ChatPartialResultMap is responsible for maintaining a mapping between + * partial result tokens and the associated ChatMessage objects. * * $/progress notifications are caught and handled in the AmazonQLspClientImpl * notifyProgress method. Within a progress notification, we are provided ProgressParams * containing a token and a partial result object. The tokenToChatMessage map in - * this class allows us to find the original ChatMessage associated with the token. + * this class allows us to find the associated ChatMessage associated with the token. * * @see AmazonQLspClientImpl#notifyProgress(ProgressParams) */ -public class ChatPartialResultManager { - private static ChatPartialResultManager instance; +public class ChatPartialResultMap { + private final Map tokenToChatMessageMap; - private ChatPartialResultManager() { + public ChatPartialResultMap() { tokenToChatMessageMap = new ConcurrentHashMap(); } - public static synchronized ChatPartialResultManager getInstance() { - if (instance == null) { - instance = new ChatPartialResultManager(); - } - return instance; - } - - public void setMapEntry(String token, ChatMessage chatMessage) { + public void setEntry(String token, ChatMessage chatMessage) { tokenToChatMessageMap.put(token, chatMessage); } - public void deleteMapEntry(String token) { + public void removeEntry(String token) { tokenToChatMessageMap.remove(token); } diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQChatViewActionHandler.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQChatViewActionHandler.java index 66502722..9ea60f96 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQChatViewActionHandler.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQChatViewActionHandler.java @@ -26,7 +26,7 @@ public class AmazonQChatViewActionHandler implements ViewActionHandler { public AmazonQChatViewActionHandler() { this.jsonHandler = new JsonHandler(); - chatCommunicationManager = new ChatCommunicationManager(); + chatCommunicationManager = ChatCommunicationManager.getInstance(); } /* @@ -72,9 +72,9 @@ public final void handleCommand(final ParsedCommand parsedCommand, final Browser */ public final void handlePartialResultProgressNotification(ProgressParams params) { String token = ProgressNotficationUtils.getToken(params); + ChatMessage chatMessage = chatCommunicationManager.getPartialChatMessage(token); - // Check to ensure progress notification is a partial result for chat - if (!chatCommunicationManager.isProcessingPartialChatMessage(token)) { + if (chatMessage == null) { return; } @@ -84,12 +84,11 @@ public final void handlePartialResultProgressNotification(ProgressParams params) throw new AmazonQPluginException(e); } - ChatResult chatResult = ProgressNotficationUtils.getObject(params, ChatResult.class); - ChatMessage chatMessage = chatCommunicationManager.getPartialChatMessage(token); + ChatResult partialChatResult = ProgressNotficationUtils.getObject(params, ChatResult.class); Browser browser = chatMessage.getBrowser(); // Check to ensure the body has content in order to keep displaying the spinner while loading - if (chatResult.body() == null && chatResult.body().length() == 0) { + if (partialChatResult.body() == null || partialChatResult.body().length() == 0) { return; } @@ -97,7 +96,7 @@ public final void handlePartialResultProgressNotification(ProgressParams params) ChatUIInboundCommand chatUIInboundCommand = new ChatUIInboundCommand( ChatUIInboundCommandName.ChatPrompt.toString(), chatMessage.getChatRequestParams().getTabId(), - chatResult, + partialChatResult, isPartialResult ); From 34f05863df9d0b3d501c90f74eb9170c0b15cdba Mon Sep 17 00:00:00 2001 From: Jordan Ang Date: Thu, 26 Sep 2024 11:05:34 -0700 Subject: [PATCH 06/10] Update comment --- .../aws/toolkits/eclipse/amazonq/lsp/AmazonQLspClientImpl.java | 2 +- .../toolkits/eclipse/amazonq/lsp/AmazonQLspServerBuilder.java | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspClientImpl.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspClientImpl.java index 7cf2e874..0c4ff584 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspClientImpl.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspClientImpl.java @@ -37,7 +37,7 @@ public final CompletableFuture getConnectionMetadata() { /* * Handles the progress notifications received from the LSP server. - * - Process partial results for Chat messages if provided token is maintained by chatPartialResultManager + * - Process partial results for Chat messages if provided token is maintained by ChatCommunicationManager * - Other notifications are ignored at this time. */ @Override diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspServerBuilder.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspServerBuilder.java index 2a2225ed..bd2685df 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspServerBuilder.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspServerBuilder.java @@ -13,7 +13,6 @@ import software.aws.toolkits.eclipse.amazonq.providers.LspProvider; import software.aws.toolkits.eclipse.amazonq.util.ClientMetadata; -import software.aws.toolkits.eclipse.amazonq.util.PluginLogger; public class AmazonQLspServerBuilder extends Builder { From c6b3db619cd6bcff5bdbd9d6cb25a011b04ba719 Mon Sep 17 00:00:00 2001 From: Jordan Ang Date: Thu, 26 Sep 2024 12:11:07 -0700 Subject: [PATCH 07/10] Merged with latest changes --- .../chat/ChatCommunicationManager.java | 20 +++++----- .../eclipse/amazonq/chat/ChatMessage.java | 37 ++++++++----------- .../amazonq/chat/ChatMessageProvider.java | 2 +- .../amazonq/chat/ChatPartialResultMap.java | 22 +++++++---- .../views/AmazonQChatViewActionHandler.java | 5 ++- 5 files changed, 44 insertions(+), 42 deletions(-) diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatCommunicationManager.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatCommunicationManager.java index 1e08e58e..049894de 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatCommunicationManager.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatCommunicationManager.java @@ -13,25 +13,27 @@ import software.aws.toolkits.eclipse.amazonq.chat.models.GenericTabParams; import software.aws.toolkits.eclipse.amazonq.exception.AmazonQPluginException; import software.aws.toolkits.eclipse.amazonq.util.JsonHandler; +import software.aws.toolkits.eclipse.amazonq.util.PluginLogger; import software.aws.toolkits.eclipse.amazonq.views.model.Command; /** - * ChatCommunicationManager is responsible for managing communication between - * the Amazon Q Eclipse Plugin and the LSP server as well as communication - * between the Amazon Q Eclipse Plugin and the webview. It is implemented - * as a singleton to centralize control of all communication in the plugin. + * ChatCommunicationManager is a central component of the Amazon Q Eclipse Plugin that + * acts as a bridge between the plugin's UI and the LSP server. It is also responsible + * for managing communication between the plugin and the webview used for displaying + * chat conversations. It is implemented as a singleton to centralize control of all + * communication in the plugin. */ public final class ChatCommunicationManager { private static ChatCommunicationManager instance; private final JsonHandler jsonHandler; private final CompletableFuture chatMessageProvider; - private final ChatPartialResultManager chatPartialResultManager; + private final ChatPartialResultMap chatPartialResultMap; private ChatCommunicationManager() { this.jsonHandler = new JsonHandler(); this.chatMessageProvider = ChatMessageProvider.createAsync(); - this.chatPartialResultManager = ChatPartialResultManager.getInstance(); + this.chatPartialResultMap = new ChatPartialResultMap(); } public static synchronized ChatCommunicationManager getInstance() { @@ -41,13 +43,13 @@ public static synchronized ChatCommunicationManager getInstance() { return instance; } - public CompletableFuture sendMessageToChatServer(final Command command, final Object params) { + public CompletableFuture sendMessageToChatServer(final Browser browser, final Command command, final Object params) { return chatMessageProvider.thenCompose(chatMessageProvider -> { try { switch (command) { case CHAT_SEND_PROMPT: ChatRequestParams chatRequestParams = jsonHandler.convertObject(params, ChatRequestParams.class); - return chatMessageProvider.sendChatPrompt(chatRequestParams); + return chatMessageProvider.sendChatPrompt(browser, chatRequestParams); case CHAT_READY: chatMessageProvider.sendChatReady(); return CompletableFuture.completedFuture(null); @@ -68,7 +70,7 @@ public CompletableFuture sendMessageToChatServer(final Command comma public void sendMessageToChatUI(final Browser browser, final ChatUIInboundCommand command) { // Mynah-ui will not render the partial result if null values are included. Must serialize with ignoreNulls set to True. Boolean ignoreNull = true; - String message = this.jsonHandler.serialize(command, ignoreNull); + String message = jsonHandler.serialize(command, ignoreNull); String script = "window.postMessage(" + message + ");"; browser.getDisplay().asyncExec(() -> { diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatMessage.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatMessage.java index 63f800e2..e74d3cb0 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatMessage.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatMessage.java @@ -2,16 +2,13 @@ package software.aws.toolkits.eclipse.amazonq.chat; -import java.util.concurrent.ExecutionException; +import java.util.concurrent.CompletableFuture; import org.eclipse.swt.browser.Browser; import software.aws.toolkits.eclipse.amazonq.chat.models.ChatRequestParams; import software.aws.toolkits.eclipse.amazonq.chat.models.ChatResult; -import software.aws.toolkits.eclipse.amazonq.exception.AmazonQPluginException; import software.aws.toolkits.eclipse.amazonq.lsp.AmazonQLspServer; -import software.aws.toolkits.eclipse.amazonq.util.PluginLogger; -import software.aws.toolkits.eclipse.amazonq.views.model.Command; public class ChatMessage { private final Browser browser; @@ -38,23 +35,19 @@ public String getPartialResultToken() { return chatRequestParams.getPartialResultToken(); } - public ChatResult sendChatMessageWithProgress() { - try { - // Retrieving the chat result is expected to be a long-running process with intermittent progress notifications being sent - // from the LSP server. The progress notifications provide a token and a result - we are utilizing this token to - // ChatMessage mapping to acquire the associated ChatMessage. - String partialResultToken = chatCommunicationManager.addPartialChatMessage(this); - - PluginLogger.info("Sending " + Command.CHAT_SEND_PROMPT + " message to Amazon Q LSP server"); - ChatResult chatResult = amazonQLspServer.sendChatPrompt(chatRequestParams).get(); - - // The mapping entry no longer needs to be maintained once the final result is retrieved. - chatCommunicationManager.removePartialChatMessage(partialResultToken); - - return chatResult; - } catch (InterruptedException | ExecutionException e) { - PluginLogger.error("Error occurred while sending " + Command.CHAT_SEND_PROMPT + " message to Amazon Q LSP server", e); - throw new AmazonQPluginException(e); - } + public CompletableFuture sendChatMessageWithProgress() { + // Retrieving the chat result is expected to be a long-running process with intermittent progress notifications being sent + // from the LSP server. The progress notifications provide a token and a partial result Object - we are utilizing a token to + // ChatMessage mapping to acquire the associated ChatMessage so we can formulate a message for the UI. + String partialResultToken = chatCommunicationManager.addPartialChatMessage(this); + + CompletableFuture chatResult = amazonQLspServer.sendChatPrompt(chatRequestParams) + .thenApply(result -> { + // The mapping entry no longer needs to be maintained once the final result is retrieved. + chatCommunicationManager.removePartialChatMessage(partialResultToken); + return result; + }); + + return chatResult; } } diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatMessageProvider.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatMessageProvider.java index 7fa73ea3..da241ea0 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatMessageProvider.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatMessageProvider.java @@ -31,7 +31,7 @@ private ChatMessageProvider(final AmazonQLspServer amazonQLspServer) { } public CompletableFuture sendChatPrompt(Browser browser, final ChatRequestParams chatRequestParams) { - ChatMessage chatMessage = new ChatMessage(amazonQLspServer, chatRequestParams); + ChatMessage chatMessage = new ChatMessage(amazonQLspServer, browser, chatRequestParams); return chatMessage.sendChatMessageWithProgress(); } diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatPartialResultMap.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatPartialResultMap.java index 7a71905e..5f1c0f43 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatPartialResultMap.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatPartialResultMap.java @@ -6,18 +6,24 @@ import java.util.concurrent.ConcurrentHashMap; import software.aws.toolkits.eclipse.amazonq.chat.models.ChatResult; +import software.aws.toolkits.eclipse.amazonq.lsp.AmazonQLspClientImpl; import software.aws.toolkits.eclipse.amazonq.util.PluginLogger; /** - * ChatPartialResultMap is responsible for maintaining a mapping between - * partial result tokens and the associated ChatMessage objects. - * - * $/progress notifications are caught and handled in the AmazonQLspClientImpl - * notifyProgress method. Within a progress notification, we are provided ProgressParams - * containing a token and a partial result object. The tokenToChatMessage map in - * this class allows us to find the associated ChatMessage associated with the token. + * ChatPartialResultMap is a utility class responsible for managing the mapping between + * partial result tokens and their corresponding ChatMessage objects in the Amazon Q plugin for Eclipse. * - * @see AmazonQLspClientImpl#notifyProgress(ProgressParams) + * The Language Server Protocol (LSP) server sends progress notifications during long-running operations, + * such as processing chat requests. These notifications include a token that identifies the specific operation + * and a partial result object containing the progress information. + * + * This class maintains a concurrent map (tokenToChatMessageMap) that associates each token with + * its respective ChatMessage object. This mapping is crucial for correctly updating the chat UI + * with the latest progress information as it becomes available from the LSP server. + * + * The progress notifications are handled by the {@link AmazonQLspClientImpl#notifyProgress(ProgressParams)} + * method, which retrieves the corresponding ChatMessage object from the tokenToChatMessageMap using + * the token provided in the ProgressParams. The ChatMessage can then be updated with the partial result. */ public class ChatPartialResultMap { diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQChatViewActionHandler.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQChatViewActionHandler.java index 9ea60f96..dbff51f3 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQChatViewActionHandler.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQChatViewActionHandler.java @@ -11,6 +11,7 @@ import software.aws.toolkits.eclipse.amazonq.chat.ChatCommunicationManager; import software.aws.toolkits.eclipse.amazonq.chat.ChatMessage; import software.aws.toolkits.eclipse.amazonq.chat.models.ChatRequestParams; +import software.aws.toolkits.eclipse.amazonq.chat.models.ChatResult; import software.aws.toolkits.eclipse.amazonq.chat.models.ChatUIInboundCommand; import software.aws.toolkits.eclipse.amazonq.chat.models.ChatUIInboundCommandName; import software.aws.toolkits.eclipse.amazonq.exception.AmazonQPluginException; @@ -41,12 +42,12 @@ public final void handleCommand(final ParsedCommand parsedCommand, final Browser switch (command) { case CHAT_SEND_PROMPT: - chatCommunicationManager.sendMessageToChatServer(command, params) + chatCommunicationManager.sendMessageToChatServer(browser, command, params) .thenAccept(chatResult -> { ChatRequestParams chatRequestParams = jsonHandler.convertObject(params, ChatRequestParams.class); ChatUIInboundCommand chatUIInboundCommand = new ChatUIInboundCommand( ChatUIInboundCommandName.ChatPrompt.toString(), - chatRequestParams.tabId(), + chatRequestParams.getTabId(), chatResult, false ); From 892cd2b206530fba99e3bd0d72ac3973d1eaf5ba Mon Sep 17 00:00:00 2001 From: Jordan Ang Date: Fri, 27 Sep 2024 12:35:14 -0700 Subject: [PATCH 08/10] Move ignore null serialization to ChatResult record --- .../amazonq/chat/ChatCommunicationManager.java | 4 +--- .../eclipse/amazonq/chat/models/ChatResult.java | 3 +++ .../eclipse/amazonq/util/JsonHandler.java | 15 --------------- .../views/AmazonQChatViewActionHandler.java | 6 ++---- 4 files changed, 6 insertions(+), 22 deletions(-) diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatCommunicationManager.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatCommunicationManager.java index 049894de..ca8754ac 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatCommunicationManager.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatCommunicationManager.java @@ -68,9 +68,7 @@ public CompletableFuture sendMessageToChatServer(final Browser brows } public void sendMessageToChatUI(final Browser browser, final ChatUIInboundCommand command) { - // Mynah-ui will not render the partial result if null values are included. Must serialize with ignoreNulls set to True. - Boolean ignoreNull = true; - String message = jsonHandler.serialize(command, ignoreNull); + String message = jsonHandler.serialize(command); String script = "window.postMessage(" + message + ");"; browser.getDisplay().asyncExec(() -> { diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/models/ChatResult.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/models/ChatResult.java index 632a9ff3..81ba03b9 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/models/ChatResult.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/models/ChatResult.java @@ -2,8 +2,11 @@ package software.aws.toolkits.eclipse.amazonq.chat.models; +import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +// Mynah-ui will not render the partial result if null values are included. Must ignore nulls values. +@JsonInclude(JsonInclude.Include.NON_NULL) public record ChatResult( @JsonProperty("body") String body, @JsonProperty("messageId") String messageId, diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/util/JsonHandler.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/util/JsonHandler.java index 46174319..ed1a9b4c 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/util/JsonHandler.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/util/JsonHandler.java @@ -23,21 +23,6 @@ public String serialize(final Object obj) { } return serializedObj; } - - public String serialize(final Object obj, Boolean ignoreNull) { - String serializedObj = null; - try { - ObjectMapper mapper = objectMapper.copy(); - if (ignoreNull) { - mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); - } - serializedObj = mapper.writeValueAsString(obj); - } catch (JsonProcessingException e) { - PluginLogger.error("Error occurred while serializing object: " + obj.toString(), e); - return null; - } - return serializedObj; - } public T deserialize(final String jsonString, final Class cls) { try { diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQChatViewActionHandler.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQChatViewActionHandler.java index dbff51f3..e3333121 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQChatViewActionHandler.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQChatViewActionHandler.java @@ -81,8 +81,7 @@ public final void handlePartialResultProgressNotification(ProgressParams params) // Check to ensure Object is sent in params if (params.getValue().isLeft() || Objects.isNull(params.getValue().getRight())) { - String e = "Error occurred while handling partial result notification: expected Object value"; - throw new AmazonQPluginException(e); + throw new AmazonQPluginException("Error occurred while handling partial result notification: expected Object value"); } ChatResult partialChatResult = ProgressNotficationUtils.getObject(params, ChatResult.class); @@ -93,12 +92,11 @@ public final void handlePartialResultProgressNotification(ProgressParams params) return; } - Boolean isPartialResult = true; ChatUIInboundCommand chatUIInboundCommand = new ChatUIInboundCommand( ChatUIInboundCommandName.ChatPrompt.toString(), chatMessage.getChatRequestParams().getTabId(), partialChatResult, - isPartialResult + true ); chatCommunicationManager.sendMessageToChatUI(browser, chatUIInboundCommand); From a08960580b746b0e20f683160fb838ae3be1d825 Mon Sep 17 00:00:00 2001 From: Jordan Ang Date: Mon, 30 Sep 2024 09:06:53 -0700 Subject: [PATCH 09/10] Fix checkstyle errors --- .../chat/ChatCommunicationManager.java | 24 +++++++++--------- .../eclipse/amazonq/chat/ChatMessage.java | 16 ++++++------ .../amazonq/chat/ChatMessageProvider.java | 4 +-- .../amazonq/chat/ChatPartialResultMap.java | 25 ++++++++----------- .../chat/models/ChatRequestParams.java | 10 ++++---- .../amazonq/lsp/AmazonQLspClientImpl.java | 10 ++++---- .../eclipse/amazonq/util/JsonHandler.java | 1 - .../util/ProgressNotficationUtils.java | 10 +++++--- .../views/AmazonQChatViewActionHandler.java | 12 ++++----- 9 files changed, 55 insertions(+), 57 deletions(-) diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatCommunicationManager.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatCommunicationManager.java index ca8754ac..b2514bc6 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatCommunicationManager.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatCommunicationManager.java @@ -18,9 +18,9 @@ /** * ChatCommunicationManager is a central component of the Amazon Q Eclipse Plugin that - * acts as a bridge between the plugin's UI and the LSP server. It is also responsible - * for managing communication between the plugin and the webview used for displaying - * chat conversations. It is implemented as a singleton to centralize control of all + * acts as a bridge between the plugin's UI and the LSP server. It is also responsible + * for managing communication between the plugin and the webview used for displaying + * chat conversations. It is implemented as a singleton to centralize control of all * communication in the plugin. */ public final class ChatCommunicationManager { @@ -69,37 +69,37 @@ public CompletableFuture sendMessageToChatServer(final Browser brows public void sendMessageToChatUI(final Browser browser, final ChatUIInboundCommand command) { String message = jsonHandler.serialize(command); - + String script = "window.postMessage(" + message + ");"; browser.getDisplay().asyncExec(() -> { browser.evaluate(script); }); } - + /* * Gets the partial chat message using the provided token. */ - public ChatMessage getPartialChatMessage(String partialResultToken) { + public ChatMessage getPartialChatMessage(final String partialResultToken) { return chatPartialResultMap.getValue(partialResultToken); } - + /* * Adds an entry to the partialResultToken to ChatMessage map. */ - public String addPartialChatMessage(ChatMessage chatMessage) { + public String addPartialChatMessage(final ChatMessage chatMessage) { String partialResultToken = UUID.randomUUID().toString(); - + // Indicator for the server to send partial result notifications chatMessage.getChatRequestParams().setPartialResultToken(partialResultToken); - + chatPartialResultMap.setEntry(partialResultToken, chatMessage); return partialResultToken; } - + /* * Removes an entry from the partialResultToken to ChatMessage map. */ - public void removePartialChatMessage(String partialResultToken) { + public void removePartialChatMessage(final String partialResultToken) { chatPartialResultMap.removeEntry(partialResultToken); } } diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatMessage.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatMessage.java index e74d3cb0..bdd2204c 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatMessage.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatMessage.java @@ -10,44 +10,44 @@ import software.aws.toolkits.eclipse.amazonq.chat.models.ChatResult; import software.aws.toolkits.eclipse.amazonq.lsp.AmazonQLspServer; -public class ChatMessage { +public final class ChatMessage { private final Browser browser; private final ChatRequestParams chatRequestParams; private final AmazonQLspServer amazonQLspServer; private final ChatCommunicationManager chatCommunicationManager; - + public ChatMessage(final AmazonQLspServer amazonQLspServer, final Browser browser, final ChatRequestParams chatRequestParams) { this.amazonQLspServer = amazonQLspServer; this.browser = browser; this.chatRequestParams = chatRequestParams; this.chatCommunicationManager = ChatCommunicationManager.getInstance(); } - + public Browser getBrowser() { return browser; } - + public ChatRequestParams getChatRequestParams() { return chatRequestParams; } - + public String getPartialResultToken() { return chatRequestParams.getPartialResultToken(); } - + public CompletableFuture sendChatMessageWithProgress() { // Retrieving the chat result is expected to be a long-running process with intermittent progress notifications being sent // from the LSP server. The progress notifications provide a token and a partial result Object - we are utilizing a token to // ChatMessage mapping to acquire the associated ChatMessage so we can formulate a message for the UI. String partialResultToken = chatCommunicationManager.addPartialChatMessage(this); - + CompletableFuture chatResult = amazonQLspServer.sendChatPrompt(chatRequestParams) .thenApply(result -> { // The mapping entry no longer needs to be maintained once the final result is retrieved. chatCommunicationManager.removePartialChatMessage(partialResultToken); return result; }); - + return chatResult; } } diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatMessageProvider.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatMessageProvider.java index da241ea0..90f611e3 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatMessageProvider.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatMessageProvider.java @@ -3,8 +3,6 @@ package software.aws.toolkits.eclipse.amazonq.chat; import java.util.concurrent.CompletableFuture; -import java.util.UUID; -import java.util.concurrent.ExecutionException; import org.eclipse.swt.browser.Browser; @@ -30,7 +28,7 @@ private ChatMessageProvider(final AmazonQLspServer amazonQLspServer) { this.amazonQLspServer = amazonQLspServer; } - public CompletableFuture sendChatPrompt(Browser browser, final ChatRequestParams chatRequestParams) { + public CompletableFuture sendChatPrompt(final Browser browser, final ChatRequestParams chatRequestParams) { ChatMessage chatMessage = new ChatMessage(amazonQLspServer, browser, chatRequestParams); return chatMessage.sendChatMessageWithProgress(); } diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatPartialResultMap.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatPartialResultMap.java index 5f1c0f43..f0ad7a5d 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatPartialResultMap.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatPartialResultMap.java @@ -5,9 +5,6 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import software.aws.toolkits.eclipse.amazonq.chat.models.ChatResult; -import software.aws.toolkits.eclipse.amazonq.lsp.AmazonQLspClientImpl; -import software.aws.toolkits.eclipse.amazonq.util.PluginLogger; /** * ChatPartialResultMap is a utility class responsible for managing the mapping between @@ -25,27 +22,27 @@ * method, which retrieves the corresponding ChatMessage object from the tokenToChatMessageMap using * the token provided in the ProgressParams. The ChatMessage can then be updated with the partial result. */ -public class ChatPartialResultMap { - +public final class ChatPartialResultMap { + private final Map tokenToChatMessageMap; - + public ChatPartialResultMap() { tokenToChatMessageMap = new ConcurrentHashMap(); } - - public void setEntry(String token, ChatMessage chatMessage) { + + public void setEntry(final String token, final ChatMessage chatMessage) { tokenToChatMessageMap.put(token, chatMessage); } - - public void removeEntry(String token) { + + public void removeEntry(final String token) { tokenToChatMessageMap.remove(token); } - - public ChatMessage getValue(String token) { + + public ChatMessage getValue(final String token) { return tokenToChatMessageMap.getOrDefault(token, null); } - - public Boolean hasKey(String token) { + + public Boolean hasKey(final String token) { return tokenToChatMessageMap.containsKey(token); } } diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/models/ChatRequestParams.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/models/ChatRequestParams.java index 42d31cdd..de43d2fb 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/models/ChatRequestParams.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/models/ChatRequestParams.java @@ -4,14 +4,14 @@ import com.fasterxml.jackson.annotation.JsonProperty; -public class ChatRequestParams { +public final class ChatRequestParams { private final String tabId; private final ChatPrompt prompt; private String partialResultToken; - + public ChatRequestParams( - @JsonProperty("tabId") String tabId, - @JsonProperty("prompt") ChatPrompt prompt + @JsonProperty("tabId") final String tabId, + @JsonProperty("prompt") final ChatPrompt prompt ) { this.tabId = tabId; this.prompt = prompt; @@ -29,7 +29,7 @@ public String getPartialResultToken() { return partialResultToken; } - public void setPartialResultToken(String partialResultToken) { + public void setPartialResultToken(final String partialResultToken) { this.partialResultToken = partialResultToken; } } diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspClientImpl.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspClientImpl.java index 0c4ff584..fc7d0aeb 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspClientImpl.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspClientImpl.java @@ -60,15 +60,15 @@ public final CompletableFuture> configuration(final ConfigurationPa } @Override - public void notifyProgress(final ProgressParams params) { + public final void notifyProgress(final ProgressParams params) { AmazonQChatViewActionHandler chatActionHandler = new AmazonQChatViewActionHandler(); - - ThreadingUtils.executeAsyncTask(() -> { + + ThreadingUtils.executeAsyncTask(() -> { try { - chatActionHandler.handlePartialResultProgressNotification(params);; + chatActionHandler.handlePartialResultProgressNotification(params); } catch (Exception e) { PluginLogger.error("Error processing partial result progress notification", e); } }); - } + } } diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/util/JsonHandler.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/util/JsonHandler.java index ed1a9b4c..8ffc7803 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/util/JsonHandler.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/util/JsonHandler.java @@ -4,7 +4,6 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.annotation.JsonInclude; public final class JsonHandler { private final ObjectMapper objectMapper; diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/util/ProgressNotficationUtils.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/util/ProgressNotficationUtils.java index 95e73b73..c697eece 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/util/ProgressNotficationUtils.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/util/ProgressNotficationUtils.java @@ -8,12 +8,16 @@ import com.google.gson.JsonElement; -public class ProgressNotficationUtils { +public final class ProgressNotficationUtils { + private ProgressNotficationUtils() { + // Prevent instantiation + } + /* * Get the token from the ProgressParams value * @return The token as a String */ - public static String getToken(ProgressParams params) { + public static String getToken(final ProgressParams params) { String token; if (params.getToken().isLeft()) { @@ -30,7 +34,7 @@ public static String getToken(ProgressParams params) { * @param cls The class of the object to be deserialized * @return The deserialized object, or null if the value is not a JsonElement */ - public static T getObject(ProgressParams params, Class cls) { + public static T getObject(final ProgressParams params, final Class cls) { Object val = params.getValue().getRight(); if (!(val instanceof JsonElement)) { diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQChatViewActionHandler.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQChatViewActionHandler.java index e3333121..46d5a610 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQChatViewActionHandler.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQChatViewActionHandler.java @@ -66,12 +66,12 @@ public final void handleCommand(final ParsedCommand parsedCommand, final Browser throw new AmazonQPluginException("Unhandled command in AmazonQChatViewActionHandler: " + command.toString()); } } - - + + /* * Handles chat progress notifications from the Amazon Q LSP server. Sends a partial chat prompt message to the webview. */ - public final void handlePartialResultProgressNotification(ProgressParams params) { + public final void handlePartialResultProgressNotification(final ProgressParams params) { String token = ProgressNotficationUtils.getToken(params); ChatMessage chatMessage = chatCommunicationManager.getPartialChatMessage(token); @@ -86,19 +86,19 @@ public final void handlePartialResultProgressNotification(ProgressParams params) ChatResult partialChatResult = ProgressNotficationUtils.getObject(params, ChatResult.class); Browser browser = chatMessage.getBrowser(); - + // Check to ensure the body has content in order to keep displaying the spinner while loading if (partialChatResult.body() == null || partialChatResult.body().length() == 0) { return; } - + ChatUIInboundCommand chatUIInboundCommand = new ChatUIInboundCommand( ChatUIInboundCommandName.ChatPrompt.toString(), chatMessage.getChatRequestParams().getTabId(), partialChatResult, true ); - + chatCommunicationManager.sendMessageToChatUI(browser, chatUIInboundCommand); } } From b1d129497498690598e0197557120dcac4dfd974 Mon Sep 17 00:00:00 2001 From: Jordan Ang Date: Tue, 1 Oct 2024 09:44:45 -0700 Subject: [PATCH 10/10] Fix checkstyle after merge --- .../aws/toolkits/eclipse/amazonq/lsp/AmazonQLspClientImpl.java | 1 - 1 file changed, 1 deletion(-) diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspClientImpl.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspClientImpl.java index fc7d0aeb..194e98ae 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspClientImpl.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspClientImpl.java @@ -14,7 +14,6 @@ import org.eclipse.lsp4j.ProgressParams; import software.aws.toolkits.eclipse.amazonq.configuration.PluginStore; -import software.aws.toolkits.eclipse.amazonq.chat.ChatCommunicationManager; import software.aws.toolkits.eclipse.amazonq.lsp.model.ConnectionMetadata; import software.aws.toolkits.eclipse.amazonq.lsp.model.SsoProfileData; import software.aws.toolkits.eclipse.amazonq.util.Constants;