From c3da76f2bdaee782b96c59db692b01f7a3dac775 Mon Sep 17 00:00:00 2001
From: Carl-Robert Linnupuu
Date: Thu, 9 Nov 2023 03:11:50 +0200
Subject: [PATCH] Clean up BaseChatToolWindowTabPanel code
---
.../completions/CompletionClientProvider.java | 13 +-
.../completions/CompletionRequestHandler.java | 135 +-----
.../completions/CompletionRequestService.java | 78 ++++
.../ToolWindowCompletionEventListener.java | 24 ++
.../conversations/message/Message.java | 2 +-
.../service/YouServiceSelectionForm.java | 9 +-
.../codegpt/settings/state/SettingsState.java | 31 ++
.../chat/BaseChatToolWindowTabPanel.java | 405 ++++++++----------
.../components/ChatMessageResponseBody.java | 30 +-
.../chat/components/YouProCheckbox.java | 40 ++
.../ContextualChatToolWindowLandingPanel.java | 2 +-
.../chat/editor/ResponseEditor.java | 6 +-
.../StandardChatToolWindowLandingPanel.java | 34 +-
.../StandardChatToolWindowTabPanel.java | 8 +-
.../carlrobert/codegpt/util/SwingUtils.java | 7 +-
.../DefaultCompletionRequestHandlerTest.java | 26 +-
16 files changed, 450 insertions(+), 400 deletions(-)
create mode 100644 src/main/java/ee/carlrobert/codegpt/completions/CompletionRequestService.java
create mode 100644 src/main/java/ee/carlrobert/codegpt/completions/ToolWindowCompletionEventListener.java
create mode 100644 src/main/java/ee/carlrobert/codegpt/toolwindow/chat/components/YouProCheckbox.java
diff --git a/src/main/java/ee/carlrobert/codegpt/completions/CompletionClientProvider.java b/src/main/java/ee/carlrobert/codegpt/completions/CompletionClientProvider.java
index 7f3767ca..1c11b732 100644
--- a/src/main/java/ee/carlrobert/codegpt/completions/CompletionClientProvider.java
+++ b/src/main/java/ee/carlrobert/codegpt/completions/CompletionClientProvider.java
@@ -1,6 +1,7 @@
package ee.carlrobert.codegpt.completions;
import ee.carlrobert.codegpt.CodeGPTPlugin;
+import ee.carlrobert.codegpt.completions.you.YouUserManager;
import ee.carlrobert.codegpt.credentials.AzureCredentialsManager;
import ee.carlrobert.codegpt.credentials.OpenAICredentialsManager;
import ee.carlrobert.codegpt.settings.advanced.AdvancedSettingsState;
@@ -30,12 +31,22 @@ public class CompletionClientProvider {
return getAzureClientBuilder().build();
}
- public static YouClient getYouClient(String sessionId, String accessToken) {
+ public static YouClient getYouClient() {
var utmParameters = new UTMParameters();
utmParameters.setSource("ide");
utmParameters.setMedium("jetbrains");
utmParameters.setCampaign(CodeGPTPlugin.getVersion());
utmParameters.setContent("CodeGPT");
+
+ var sessionId = "";
+ var accessToken = "";
+ var youUserManager = YouUserManager.getInstance();
+ if (youUserManager.isAuthenticated()) {
+ var authenticationResponse = youUserManager.getAuthenticationResponse().getData();
+ sessionId = authenticationResponse.getSession().getSessionId();
+ accessToken = authenticationResponse.getSessionJwt();
+ }
+
// FIXME
return (YouClient) new YouClient.Builder(sessionId, accessToken)
.setUTMParameters(utmParameters)
diff --git a/src/main/java/ee/carlrobert/codegpt/completions/CompletionRequestHandler.java b/src/main/java/ee/carlrobert/codegpt/completions/CompletionRequestHandler.java
index 316b3f8b..77a1c6fc 100644
--- a/src/main/java/ee/carlrobert/codegpt/completions/CompletionRequestHandler.java
+++ b/src/main/java/ee/carlrobert/codegpt/completions/CompletionRequestHandler.java
@@ -1,12 +1,8 @@
package ee.carlrobert.codegpt.completions;
import com.intellij.openapi.diagnostic.Logger;
-import ee.carlrobert.codegpt.completions.you.YouUserManager;
import ee.carlrobert.codegpt.conversations.Conversation;
import ee.carlrobert.codegpt.conversations.message.Message;
-import ee.carlrobert.codegpt.settings.service.ServiceType;
-import ee.carlrobert.codegpt.settings.state.AzureSettingsState;
-import ee.carlrobert.codegpt.settings.state.OpenAISettingsState;
import ee.carlrobert.codegpt.settings.state.SettingsState;
import ee.carlrobert.codegpt.telemetry.TelemetryAction;
import ee.carlrobert.llm.client.openai.completion.ErrorDetails;
@@ -14,50 +10,25 @@ import ee.carlrobert.llm.client.you.completion.YouCompletionEventListener;
import ee.carlrobert.llm.client.you.completion.YouSerpResult;
import ee.carlrobert.llm.completion.CompletionEventListener;
import java.util.List;
-import java.util.function.BiConsumer;
-import java.util.function.Consumer;
import javax.swing.SwingWorker;
import okhttp3.sse.EventSource;
import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
public class CompletionRequestHandler {
private static final Logger LOG = Logger.getInstance(CompletionRequestHandler.class);
private final StringBuilder messageBuilder = new StringBuilder();
+ private final boolean useContextualSearch;
+ private final ToolWindowCompletionEventListener toolWindowCompletionEventListener;
private SwingWorker swingWorker;
private EventSource eventSource;
- private @Nullable Consumer messageListener;
- private @Nullable BiConsumer errorListener;
- private @Nullable Consumer completedListener;
- private @Nullable Consumer> serpResultsListener;
- private @Nullable Runnable tokensExceededListener;
- private boolean useContextualSearch;
- public CompletionRequestHandler withContextualSearch(boolean useContextualSearch) {
+ public CompletionRequestHandler(
+ boolean useContextualSearch,
+ ToolWindowCompletionEventListener toolWindowCompletionEventListener) {
this.useContextualSearch = useContextualSearch;
- return this;
- }
-
- public void addMessageListener(Consumer messageListener) {
- this.messageListener = messageListener;
- }
-
- public void addErrorListener(BiConsumer errorListener) {
- this.errorListener = errorListener;
- }
-
- public void addRequestCompletedListener(Consumer completedListener) {
- this.completedListener = completedListener;
- }
-
- public void addTokensExceededListener(Runnable tokensExceededListener) {
- this.tokensExceededListener = tokensExceededListener;
- }
-
- public void addSerpResultsListener(Consumer> serpResultsListener) {
- this.serpResultsListener = serpResultsListener;
+ this.toolWindowCompletionEventListener = toolWindowCompletionEventListener;
}
public void call(Conversation conversation, Message message, boolean isRetry) {
@@ -75,60 +46,13 @@ public class CompletionRequestHandler {
private EventSource startCall(
@NotNull Conversation conversation,
@NotNull Message message,
- boolean isRetry,
+ boolean retry,
CompletionEventListener eventListener) {
- var settings = SettingsState.getInstance();
- var requestProvider = new CompletionRequestProvider(conversation);
-
try {
- if (settings.getSelectedService() == ServiceType.LLAMA_CPP) {
- return CompletionClientProvider.getLlamaClient()
- .getChatCompletion(requestProvider.buildLlamaCompletionRequest(message), eventListener);
- }
-
- if (settings.getSelectedService() == ServiceType.YOU) {
- var sessionId = "";
- var accessToken = "";
- var youUserManager = YouUserManager.getInstance();
- if (youUserManager.isAuthenticated()) {
- var authenticationResponse =
- youUserManager.getAuthenticationResponse().getData();
- sessionId = authenticationResponse.getSession().getSessionId();
- accessToken = authenticationResponse.getSessionJwt();
- }
- var request = requestProvider.buildYouCompletionRequest(message);
- LOG.info("Initiating completion request using model: " +
- (request.isUseGPT4Model() ? "GPT-4" : "YouBot"));
-
- return CompletionClientProvider.getYouClient(sessionId, accessToken)
- .getChatCompletion(request, eventListener);
- }
-
- if (settings.getSelectedService() == ServiceType.AZURE) {
- var azureSettings = AzureSettingsState.getInstance();
- return CompletionClientProvider.getAzureClient().getChatCompletion(
- requestProvider.buildOpenAIChatCompletionRequest(
- azureSettings.getModel(),
- message,
- isRetry,
- useContextualSearch,
- azureSettings.isUsingCustomPath() ? azureSettings.getPath() : null),
- eventListener);
- }
-
- var openAISettings = OpenAISettingsState.getInstance();
- return CompletionClientProvider.getOpenAIClient().getChatCompletion(
- requestProvider.buildOpenAIChatCompletionRequest(
- openAISettings.getModel(),
- message,
- isRetry,
- useContextualSearch,
- openAISettings.isUsingCustomPath() ? openAISettings.getPath() : null),
- eventListener);
+ return CompletionRequestService.getInstance()
+ .getChatCompletionAsync(conversation, message, retry, useContextualSearch, eventListener);
} catch (Throwable t) {
- if (errorListener != null) {
- errorListener.accept(new ErrorDetails("Something went wrong"), t);
- }
+ toolWindowCompletionEventListener.handleError(new ErrorDetails("Something went wrong"), t);
throw t;
}
}
@@ -152,13 +76,9 @@ public class CompletionRequestHandler {
conversation,
message,
isRetry,
- settings.getSelectedService() == ServiceType.YOU ?
- new YouRequestCompletionEventListener() :
- new BaseCompletionEventListener());
+ new YouRequestCompletionEventListener());
} catch (TotalUsageExceededException e) {
- if (tokensExceededListener != null) {
- tokensExceededListener.run();
- }
+ toolWindowCompletionEventListener.handleTokensExceeded(conversation, message);
} finally {
sendInfo(settings);
}
@@ -169,13 +89,16 @@ public class CompletionRequestHandler {
message.setResponse(messageBuilder.toString());
for (String text : chunks) {
messageBuilder.append(text);
- if (messageListener != null) {
- messageListener.accept(text);
- }
+ toolWindowCompletionEventListener.handleMessage(text);
}
}
- class BaseCompletionEventListener implements CompletionEventListener {
+ class YouRequestCompletionEventListener implements YouCompletionEventListener {
+
+ @Override
+ public void onSerpResults(List results) {
+ toolWindowCompletionEventListener.handleSerpResults(results, message);
+ }
@Override
public void onMessage(String message) {
@@ -184,34 +107,20 @@ public class CompletionRequestHandler {
@Override
public void onComplete(StringBuilder messageBuilder) {
- if (completedListener != null) {
- completedListener.accept(messageBuilder.toString());
- }
+ toolWindowCompletionEventListener.handleCompleted(messageBuilder.toString(), message,
+ conversation, isRetry);
}
@Override
public void onError(ErrorDetails error, Throwable ex) {
try {
- if (errorListener != null) {
- errorListener.accept(error, ex);
- }
+ toolWindowCompletionEventListener.handleError(error, ex);
} finally {
sendError(error, ex);
}
}
}
- class YouRequestCompletionEventListener extends BaseCompletionEventListener
- implements YouCompletionEventListener {
-
- @Override
- public void onSerpResults(List results) {
- if (serpResultsListener != null) {
- serpResultsListener.accept(results);
- }
- }
- }
-
private void sendInfo(SettingsState settings) {
TelemetryAction.COMPLETION.createActionMessage()
.property("conversationId", conversation.getId().toString())
diff --git a/src/main/java/ee/carlrobert/codegpt/completions/CompletionRequestService.java b/src/main/java/ee/carlrobert/codegpt/completions/CompletionRequestService.java
new file mode 100644
index 00000000..c6c59b86
--- /dev/null
+++ b/src/main/java/ee/carlrobert/codegpt/completions/CompletionRequestService.java
@@ -0,0 +1,78 @@
+package ee.carlrobert.codegpt.completions;
+
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.components.Service;
+import ee.carlrobert.codegpt.conversations.Conversation;
+import ee.carlrobert.codegpt.conversations.message.Message;
+import ee.carlrobert.codegpt.credentials.AzureCredentialsManager;
+import ee.carlrobert.codegpt.credentials.OpenAICredentialsManager;
+import ee.carlrobert.codegpt.settings.service.ServiceType;
+import ee.carlrobert.codegpt.settings.state.AzureSettingsState;
+import ee.carlrobert.codegpt.settings.state.OpenAISettingsState;
+import ee.carlrobert.codegpt.settings.state.SettingsState;
+import ee.carlrobert.llm.completion.CompletionEventListener;
+import okhttp3.sse.EventSource;
+import org.jetbrains.annotations.NotNull;
+
+@Service
+public final class CompletionRequestService {
+
+ private CompletionRequestService() {
+ }
+
+ public static CompletionRequestService getInstance() {
+ return ApplicationManager.getApplication().getService(CompletionRequestService.class);
+ }
+
+ public EventSource getChatCompletionAsync(
+ @NotNull Conversation conversation,
+ @NotNull Message message,
+ boolean retry,
+ boolean useContextualSearch,
+ CompletionEventListener eventListener) {
+ var requestProvider = new CompletionRequestProvider(conversation);
+ switch (SettingsState.getInstance().getSelectedService()) {
+ case OPENAI:
+ var openAISettings = OpenAISettingsState.getInstance();
+ return CompletionClientProvider.getOpenAIClient().getChatCompletion(
+ requestProvider.buildOpenAIChatCompletionRequest(
+ openAISettings.getModel(),
+ message,
+ retry,
+ useContextualSearch,
+ openAISettings.isUsingCustomPath() ? openAISettings.getPath() : null),
+ eventListener);
+ case AZURE:
+ var azureSettings = AzureSettingsState.getInstance();
+ return CompletionClientProvider.getAzureClient().getChatCompletion(
+ requestProvider.buildOpenAIChatCompletionRequest(
+ azureSettings.getModel(),
+ message,
+ retry,
+ useContextualSearch,
+ azureSettings.isUsingCustomPath() ? azureSettings.getPath() : null),
+ eventListener);
+ case YOU:
+ return CompletionClientProvider.getYouClient().getChatCompletion(
+ requestProvider.buildYouCompletionRequest(message),
+ eventListener);
+ case LLAMA_CPP:
+ return CompletionClientProvider.getLlamaClient().getChatCompletion(
+ requestProvider.buildLlamaCompletionRequest(message),
+ eventListener);
+ default:
+ throw new IllegalArgumentException();
+ }
+ }
+
+ public boolean isRequestAllowed() {
+ var selectedService = SettingsState.getInstance().getSelectedService();
+ if (selectedService == ServiceType.AZURE) {
+ return AzureCredentialsManager.getInstance().isCredentialSet();
+ }
+ if (selectedService == ServiceType.OPENAI) {
+ return OpenAICredentialsManager.getInstance().isApiKeySet();
+ }
+ return true;
+ }
+}
diff --git a/src/main/java/ee/carlrobert/codegpt/completions/ToolWindowCompletionEventListener.java b/src/main/java/ee/carlrobert/codegpt/completions/ToolWindowCompletionEventListener.java
new file mode 100644
index 00000000..94d669af
--- /dev/null
+++ b/src/main/java/ee/carlrobert/codegpt/completions/ToolWindowCompletionEventListener.java
@@ -0,0 +1,24 @@
+package ee.carlrobert.codegpt.completions;
+
+import ee.carlrobert.codegpt.conversations.Conversation;
+import ee.carlrobert.codegpt.conversations.message.Message;
+import ee.carlrobert.llm.client.openai.completion.ErrorDetails;
+import ee.carlrobert.llm.client.you.completion.YouSerpResult;
+import java.util.List;
+
+public interface ToolWindowCompletionEventListener {
+
+ default void handleMessage(String message) {}
+
+ default void handleError(ErrorDetails error, Throwable ex) {}
+
+ default void handleTokensExceeded(Conversation conversation, Message message) {}
+
+ default void handleCompleted(
+ String fullMessage,
+ Message message,
+ Conversation conversation,
+ boolean isRetry) {}
+
+ default void handleSerpResults(List results, Message message) {}
+}
diff --git a/src/main/java/ee/carlrobert/codegpt/conversations/message/Message.java b/src/main/java/ee/carlrobert/codegpt/conversations/message/Message.java
index 7f09a378..2e81e627 100644
--- a/src/main/java/ee/carlrobert/codegpt/conversations/message/Message.java
+++ b/src/main/java/ee/carlrobert/codegpt/conversations/message/Message.java
@@ -2,7 +2,7 @@ package ee.carlrobert.codegpt.conversations.message;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
-import ee.carlrobert.codegpt.completions.you.YouSerpResult;
+import ee.carlrobert.llm.client.you.completion.YouSerpResult;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
diff --git a/src/main/java/ee/carlrobert/codegpt/settings/service/YouServiceSelectionForm.java b/src/main/java/ee/carlrobert/codegpt/settings/service/YouServiceSelectionForm.java
index 72c3e916..0c6a0209 100644
--- a/src/main/java/ee/carlrobert/codegpt/settings/service/YouServiceSelectionForm.java
+++ b/src/main/java/ee/carlrobert/codegpt/settings/service/YouServiceSelectionForm.java
@@ -134,15 +134,10 @@ public class YouServiceSelectionForm extends JPanel {
}
private JTextPane createSignUpTextPane() {
- var textPane = createTextPane(
+ var textPane = SwingUtils.createTextPane(
"Don't have an account? Sign up");
textPane.setBorder(JBUI.Borders.emptyLeft(4));
- return textPane;
- }
-
- private JTextPane createTextPane(String htmlContent) {
- var textPane = SwingUtils.createTextPane(SwingUtils::handleHyperlinkClicked);
- textPane.setText(htmlContent);
+ textPane.setOpaque(false);
return textPane;
}
diff --git a/src/main/java/ee/carlrobert/codegpt/settings/state/SettingsState.java b/src/main/java/ee/carlrobert/codegpt/settings/state/SettingsState.java
index 6fa4d4c4..48cd7484 100644
--- a/src/main/java/ee/carlrobert/codegpt/settings/state/SettingsState.java
+++ b/src/main/java/ee/carlrobert/codegpt/settings/state/SettingsState.java
@@ -6,6 +6,7 @@ import com.intellij.openapi.components.State;
import com.intellij.openapi.components.Storage;
import com.intellij.util.xmlb.XmlSerializerUtil;
import ee.carlrobert.codegpt.completions.HuggingFaceModel;
+import ee.carlrobert.codegpt.completions.llama.LlamaModel;
import ee.carlrobert.codegpt.conversations.Conversation;
import ee.carlrobert.codegpt.settings.service.ServiceType;
import org.jetbrains.annotations.NotNull;
@@ -62,6 +63,36 @@ public class SettingsState implements PersistentStateComponent {
}
}
+ public String getModel() {
+ switch (selectedService) {
+ case OPENAI:
+ return OpenAISettingsState.getInstance().getModel();
+ case AZURE:
+ return AzureSettingsState.getInstance().getModel();
+ case YOU:
+ return "YouCode";
+ case LLAMA_CPP:
+ var llamaSettings = LlamaSettingsState.getInstance();
+ if (llamaSettings.isUseCustomModel()) {
+ var filePath = llamaSettings.getCustomLlamaModelPath();
+ int lastSeparatorIndex = filePath.lastIndexOf('/');
+ if (lastSeparatorIndex == -1) {
+ return filePath;
+ }
+ return filePath.substring(lastSeparatorIndex + 1);
+ }
+ var huggingFaceModel = llamaSettings.getHuggingFaceModel();
+ var llamaModel = LlamaModel.findByHuggingFaceModel(huggingFaceModel);
+ return String.format(
+ "%s %dB (Q%d)",
+ llamaModel.getLabel(),
+ huggingFaceModel.getParameterSize(),
+ huggingFaceModel.getQuantization());
+ default:
+ return "Unknown";
+ }
+ }
+
public String getEmail() {
return email;
}
diff --git a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/BaseChatToolWindowTabPanel.java b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/BaseChatToolWindowTabPanel.java
index ec96a670..78e31129 100644
--- a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/BaseChatToolWindowTabPanel.java
+++ b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/BaseChatToolWindowTabPanel.java
@@ -3,35 +3,30 @@ package ee.carlrobert.codegpt.toolwindow.chat;
import static com.intellij.openapi.ui.Messages.OK;
import static ee.carlrobert.codegpt.util.ThemeUtils.getPanelBackgroundColor;
import static java.lang.String.format;
-import static java.util.stream.Collectors.toList;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.impl.EditorImpl;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ui.componentsList.components.ScrollablePanel;
+import com.intellij.openapi.roots.ui.componentsList.layout.VerticalStackLayout;
import com.intellij.ui.JBColor;
+import com.intellij.ui.ScrollPaneFactory;
import com.intellij.ui.components.JBCheckBox;
-import com.intellij.ui.components.JBScrollPane;
import com.intellij.util.messages.MessageBusConnection;
import com.intellij.util.ui.JBUI;
import com.intellij.util.ui.JBUI.Borders;
-import ee.carlrobert.codegpt.CodeGPTBundle;
import ee.carlrobert.codegpt.actions.ActionType;
import ee.carlrobert.codegpt.completions.CompletionRequestHandler;
-import ee.carlrobert.codegpt.completions.llama.LlamaModel;
-import ee.carlrobert.codegpt.completions.you.YouSerpResult;
+import ee.carlrobert.codegpt.completions.CompletionRequestService;
+import ee.carlrobert.codegpt.completions.ToolWindowCompletionEventListener;
import ee.carlrobert.codegpt.completions.you.YouSubscriptionNotifier;
import ee.carlrobert.codegpt.completions.you.YouUserManager;
import ee.carlrobert.codegpt.completions.you.auth.SignedOutNotifier;
import ee.carlrobert.codegpt.conversations.Conversation;
import ee.carlrobert.codegpt.conversations.ConversationService;
import ee.carlrobert.codegpt.conversations.message.Message;
-import ee.carlrobert.codegpt.credentials.AzureCredentialsManager;
-import ee.carlrobert.codegpt.credentials.OpenAICredentialsManager;
import ee.carlrobert.codegpt.settings.service.ServiceType;
-import ee.carlrobert.codegpt.settings.state.AzureSettingsState;
-import ee.carlrobert.codegpt.settings.state.LlamaSettingsState;
import ee.carlrobert.codegpt.settings.state.OpenAISettingsState;
import ee.carlrobert.codegpt.settings.state.SettingsState;
import ee.carlrobert.codegpt.settings.state.YouSettingsState;
@@ -42,10 +37,13 @@ import ee.carlrobert.codegpt.toolwindow.chat.components.ResponsePanel;
import ee.carlrobert.codegpt.toolwindow.chat.components.SmartScroller;
import ee.carlrobert.codegpt.toolwindow.chat.components.UserMessagePanel;
import ee.carlrobert.codegpt.toolwindow.chat.components.UserPromptTextArea;
+import ee.carlrobert.codegpt.toolwindow.chat.components.YouProCheckbox;
import ee.carlrobert.codegpt.util.EditorUtils;
import ee.carlrobert.codegpt.util.OverlayUtils;
import ee.carlrobert.codegpt.util.SwingUtils;
import ee.carlrobert.codegpt.util.file.FileUtils;
+import ee.carlrobert.llm.client.openai.completion.ErrorDetails;
+import ee.carlrobert.llm.client.you.completion.YouSerpResult;
import java.awt.BorderLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
@@ -57,6 +55,7 @@ import java.util.UUID;
import javax.swing.BoxLayout;
import javax.swing.JComponent;
import javax.swing.JPanel;
+import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.ScrollPaneConstants;
import javax.swing.SwingUtilities;
@@ -67,6 +66,9 @@ public abstract class BaseChatToolWindowTabPanel implements ChatToolWindowTabPan
private static final Logger LOG = Logger.getInstance(BaseChatToolWindowTabPanel.class);
+ private final SettingsState settings;
+ private final YouUserManager youUserManager;
+
private final boolean useContextualSearch;
private final JPanel rootPanel;
private final ScrollablePanel scrollablePanel;
@@ -85,11 +87,15 @@ public abstract class BaseChatToolWindowTabPanel implements ChatToolWindowTabPan
this.project = project;
this.useContextualSearch = useContextualSearch;
this.conversationService = ConversationService.getInstance();
- this.rootPanel = new JPanel(new GridBagLayout());
- this.scrollablePanel = new ScrollablePanel();
+ this.scrollablePanel = new ScrollablePanel(new VerticalStackLayout());
this.userPromptTextArea = new UserPromptTextArea(this::handleSubmit);
- this.gpt4CheckBox = createGPT4ModelCheckBox();
- init();
+ this.gpt4CheckBox = new YouProCheckbox(project);
+ this.settings = SettingsState.getInstance();
+ this.youUserManager = YouUserManager.getInstance();
+ this.rootPanel = createRootPanel();
+
+ userPromptTextArea.requestFocusInWindow();
+ userPromptTextArea.requestFocus();
}
public void requestFocusForTextArea() {
@@ -115,31 +121,14 @@ public abstract class BaseChatToolWindowTabPanel implements ChatToolWindowTabPan
public void displayLandingView() {
scrollablePanel.removeAll();
scrollablePanel.add(getLandingView());
- var youUserManager = YouUserManager.getInstance();
- if (SettingsState.getInstance().getSelectedService() == ServiceType.YOU &&
+ if (settings.getSelectedService() == ServiceType.YOU &&
(!youUserManager.isAuthenticated() || !youUserManager.isSubscribed())) {
- scrollablePanel.add(new ResponsePanel().addContent(createTextPane()));
+ scrollablePanel.add(new ResponsePanel().addContent(createYouCouponTextPane()));
}
scrollablePanel.repaint();
scrollablePanel.revalidate();
}
- private JTextPane createTextPane() {
- var textPane = SwingUtils.createTextPane(SwingUtils::handleHyperlinkClicked);
- textPane.setBackground(getPanelBackgroundColor());
- textPane.setFocusable(false);
- textPane.setText(
- "\n"
- + "\n"
- + " Use CodeGPT coupon for free month of GPT-4.
\n"
- + " \n"
- + " Sign up here\n"
- + "
\n"
- + "\n"
- + "");
- return textPane;
- }
-
@Override
public void startNewConversation(Message message) {
conversation = conversationService.startConversation();
@@ -160,9 +149,10 @@ public abstract class BaseChatToolWindowTabPanel implements ChatToolWindowTabPan
this));
var responsePanel = new ResponsePanel()
.withReloadAction(() -> reloadMessage(message, conversation))
- .withDeleteAction(() -> deleteMessage(message.getId(), messageWrapper, conversation))
+ .withDeleteAction(() -> removeMessage(message.getId(), messageWrapper, conversation))
.addContent(new ChatMessageResponseBody(project, true, this));
messageWrapper.add(responsePanel);
+
call(conversation, message, responsePanel, false);
}
@@ -170,106 +160,6 @@ public abstract class BaseChatToolWindowTabPanel implements ChatToolWindowTabPan
public void dispose() {
}
- private boolean isRequestAllowed() {
- var settings = SettingsState.getInstance();
- if (settings.getSelectedService() == ServiceType.AZURE) {
- return AzureCredentialsManager.getInstance().isCredentialSet();
- }
- if (settings.getSelectedService() == ServiceType.OPENAI) {
- return OpenAICredentialsManager.getInstance().isApiKeySet();
- }
- return true;
- }
-
- private void call(
- Conversation conversation,
- Message message,
- ResponsePanel responsePanel,
- boolean isRetry) {
- ChatMessageResponseBody responseContainer = (ChatMessageResponseBody) responsePanel.getContent();
-
- if (!isRequestAllowed()) {
- responseContainer.displayMissingCredential();
- return;
- }
-
- var requestHandler = new CompletionRequestHandler();
- requestHandler.withContextualSearch(useContextualSearch);
- requestHandler.addMessageListener(partialMessage -> {
- try {
- LOG.debug(partialMessage);
- ApplicationManager.getApplication()
- .invokeLater(() -> responseContainer.update(partialMessage));
- } catch (Exception e) {
- responseContainer.displayDefaultError();
- throw new RuntimeException("Error while updating the content", e);
- }
- });
- requestHandler.addRequestCompletedListener(completeMessage -> {
- responsePanel.enableActions();
- conversationService.saveMessage(completeMessage, message, conversation, isRetry);
- stopStreaming(responseContainer);
-
- var serpResults = serpResultsMapping.get(message.getId());
- var containsResults = serpResults != null && !serpResults.isEmpty();
- if (YouSettingsState.getInstance().isDisplayWebSearchResults()) {
- if (containsResults) {
- responseContainer.displaySerpResults(serpResults);
- }
- }
-
- if (containsResults) {
- message.setSerpResults(serpResults.stream()
- .map(result -> new YouSerpResult(
- result.getUrl(),
- result.getName(),
- result.getSnippet(),
- result.getSnippetSource()))
- .collect(toList()));
- }
- });
- requestHandler.addTokensExceededListener(() -> SwingUtilities.invokeLater(() -> {
- var answer = OverlayUtils.showTokenLimitExceededDialog();
- if (answer == OK) {
- TelemetryAction.IDE_ACTION.createActionMessage()
- .property("action", "DISCARD_TOKEN_LIMIT")
- .property("model", conversation.getModel())
- .send();
-
- conversationService.discardTokenLimits(conversation);
- requestHandler.call(conversation, message, true);
- } else {
- stopStreaming(responseContainer);
- }
- }));
- requestHandler.addErrorListener((error, ex) -> {
- try {
- if ("insufficient_quota".equals(error.getCode())) {
- if (SettingsState.getInstance().getSelectedService() == ServiceType.OPENAI) {
- OpenAISettingsState.getInstance().setOpenAIQuotaExceeded(true);
- }
- responseContainer.displayQuotaExceeded();
- } else {
- responseContainer.displayError(error.getMessage());
- }
- } finally {
- responsePanel.enableActions();
- stopStreaming(responseContainer);
- }
- });
- requestHandler.addSerpResultsListener(
- serpResults -> serpResultsMapping.put(message.getId(), serpResults.stream()
- .map(result -> new YouSerpResult(
- result.getUrl(),
- result.getName(),
- result.getSnippet(),
- result.getSnippetSource()))
- .collect(toList())));
- userPromptTextArea.setRequestHandler(requestHandler);
- userPromptTextArea.setSubmitEnabled(false);
- requestHandler.call(conversation, message, isRetry);
- }
-
protected void reloadMessage(Message message, Conversation conversation) {
ResponsePanel responsePanel = null;
try {
@@ -281,8 +171,10 @@ public abstract class BaseChatToolWindowTabPanel implements ChatToolWindowTabPan
scrollablePanel.revalidate();
scrollablePanel.repaint();
} catch (Exception e) {
- throw new RuntimeException("Couldn't reload message", e);
+ throw new RuntimeException("Couldn't delete the existing message component", e);
} finally {
+ LOG.debug("Reloading message: " + message.getId());
+
if (responsePanel != null) {
message.setResponse("");
conversationService.saveMessage(conversation, message);
@@ -295,7 +187,7 @@ public abstract class BaseChatToolWindowTabPanel implements ChatToolWindowTabPan
}
}
- protected void deleteMessage(UUID messageId, JPanel messageWrapper, Conversation conversation) {
+ protected void removeMessage(UUID messageId, JPanel messageWrapper, Conversation conversation) {
scrollablePanel.remove(messageWrapper);
scrollablePanel.repaint();
scrollablePanel.revalidate();
@@ -327,11 +219,24 @@ public abstract class BaseChatToolWindowTabPanel implements ChatToolWindowTabPan
scrollablePanel.repaint();
}
- private void stopStreaming(ChatMessageResponseBody responseContainer) {
- SwingUtilities.invokeLater(() -> {
- userPromptTextArea.setSubmitEnabled(true);
- responseContainer.hideCarets();
- });
+ private void call(
+ Conversation conversation,
+ Message message,
+ ResponsePanel responsePanel,
+ boolean isRetry) {
+ ChatMessageResponseBody responseContainer = (ChatMessageResponseBody) responsePanel.getContent();
+
+ if (!CompletionRequestService.getInstance().isRequestAllowed()) {
+ responseContainer.displayMissingCredential();
+ return;
+ }
+
+ var requestHandler = new CompletionRequestHandler(
+ useContextualSearch,
+ new ChatToolWindowCompletionEventListener(responsePanel));
+ userPromptTextArea.setRequestHandler(requestHandler);
+ userPromptTextArea.setSubmitEnabled(false);
+ requestHandler.call(conversation, message, isRetry);
}
private void handleSubmit(String text) {
@@ -356,46 +261,44 @@ public abstract class BaseChatToolWindowTabPanel implements ChatToolWindowTabPan
}
}
- private void init() {
+ private static JScrollPane createScrollPane(ScrollablePanel scrollablePanel) {
+ var scrollPane = ScrollPaneFactory.createScrollPane(scrollablePanel, true);
+ scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
+ new SmartScroller(scrollPane);
+ return scrollPane;
+ }
+
+ private JPanel createRootPanel() {
+ var rootPanel = new JPanel(new GridBagLayout());
var gbc = new GridBagConstraints();
gbc.fill = GridBagConstraints.BOTH;
gbc.weighty = 1;
gbc.weightx = 1;
gbc.gridx = 0;
gbc.gridy = 0;
-
- scrollablePanel.setLayout(new BoxLayout(scrollablePanel, BoxLayout.Y_AXIS));
- JBScrollPane scrollPane = new JBScrollPane(scrollablePanel);
- scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
- scrollPane.setBorder(null);
- scrollPane.setViewportBorder(null);
- rootPanel.add(scrollPane, gbc);
- new SmartScroller(scrollPane);
-
- gbc.weighty = 0;
- gbc.fill = GridBagConstraints.HORIZONTAL;
- gbc.gridy = 1;
-
- var model = getModel();
- var modelIconWrapper = JBUI.Panels
- .simplePanel(new ModelIconLabel(
- SettingsState.getInstance()
- .getSelectedService()
- .getCompletionCode(),
- model))
- .withBorder(Borders.emptyRight(4))
- .withBackground(getPanelBackgroundColor());
+ rootPanel.add(createScrollPane(scrollablePanel), gbc);
var wrapper = new JPanel(new BorderLayout());
wrapper.setBorder(JBUI.Borders.compound(
JBUI.Borders.customLine(JBColor.border(), 1, 0, 0, 0),
JBUI.Borders.empty(8)));
wrapper.setBackground(getPanelBackgroundColor());
+ wrapper.add(createPromptTextAreaHeader(), BorderLayout.NORTH);
wrapper.add(userPromptTextArea, BorderLayout.SOUTH);
+ gbc.weighty = 0;
+ gbc.fill = GridBagConstraints.HORIZONTAL;
+ gbc.gridy = 1;
+ rootPanel.add(wrapper, gbc);
+
+ return rootPanel;
+ }
+
+ private JPanel createPromptTextAreaHeader() {
var header = new JPanel(new BorderLayout());
header.setBackground(getPanelBackgroundColor());
header.setBorder(JBUI.Borders.emptyBottom(8));
+ var model = settings.getModel();
if ("YouCode".equals(model)) {
var messageBusConnection = ApplicationManager.getApplication().getMessageBus().connect();
subscribeToYouModelChangeTopic();
@@ -403,18 +306,13 @@ public abstract class BaseChatToolWindowTabPanel implements ChatToolWindowTabPan
subscribeToSignedOutTopic(messageBusConnection);
header.add(gpt4CheckBox, BorderLayout.LINE_START);
}
- header.add(modelIconWrapper, BorderLayout.LINE_END);
- wrapper.add(header);
-
- rootPanel.add(wrapper, gbc);
- userPromptTextArea.requestFocusInWindow();
- userPromptTextArea.requestFocus();
- }
-
- private void subscribeToSignedOutTopic(MessageBusConnection messageBusConnection) {
- messageBusConnection.subscribe(
- SignedOutNotifier.SIGNED_OUT_TOPIC,
- (SignedOutNotifier) () -> gpt4CheckBox.setEnabled(false));
+ header.add(JBUI.Panels
+ .simplePanel(
+ new ModelIconLabel(settings.getSelectedService().getCompletionCode(),
+ model))
+ .withBorder(Borders.emptyRight(4))
+ .withBackground(getPanelBackgroundColor()), BorderLayout.LINE_END);
+ return header;
}
private void subscribeToYouModelChangeTopic() {
@@ -425,6 +323,12 @@ public abstract class BaseChatToolWindowTabPanel implements ChatToolWindowTabPan
(YouModelChangeNotifier) gpt4CheckBox::setSelected);
}
+ private void subscribeToSignedOutTopic(MessageBusConnection messageBusConnection) {
+ messageBusConnection.subscribe(
+ SignedOutNotifier.SIGNED_OUT_TOPIC,
+ (SignedOutNotifier) () -> gpt4CheckBox.setEnabled(false));
+ }
+
private void subscribeToYouSubscriptionTopic(MessageBusConnection messageBusConnection) {
messageBusConnection.subscribe(
YouSubscriptionNotifier.SUBSCRIPTION_TOPIC,
@@ -434,64 +338,111 @@ public abstract class BaseChatToolWindowTabPanel implements ChatToolWindowTabPan
});
}
- private JBCheckBox createGPT4ModelCheckBox() {
- var gpt4CheckBox = new JBCheckBox(CodeGPTBundle.get("toolwindow.chat.youProCheckBox.text"));
- gpt4CheckBox.setOpaque(false);
- gpt4CheckBox.setEnabled(YouUserManager.getInstance().isSubscribed());
- gpt4CheckBox.setSelected(YouSettingsState.getInstance().isUseGPT4Model());
- gpt4CheckBox.setToolTipText(getTooltipText(gpt4CheckBox.isSelected()));
- gpt4CheckBox.addChangeListener(e -> {
- var selected = ((JBCheckBox) e.getSource()).isSelected();
- var tooltipText = getTooltipText(selected);
- gpt4CheckBox.setToolTipText(tooltipText);
- // TODO: Remove
- project.getMessageBus()
- .syncPublisher(YouModelChangeNotifier.YOU_MODEL_CHANGE_NOTIFIER_TOPIC)
- .modelChanged(selected);
- YouSettingsState.getInstance().setUseGPT4Model(selected);
- });
- return gpt4CheckBox;
+ private JTextPane createYouCouponTextPane() {
+ var textPane = SwingUtils.createTextPane(
+ "\n"
+ + "\n"
+ + " Use CodeGPT coupon for free month of GPT-4.
\n"
+ + " \n"
+ + " Sign up here\n"
+ + "
\n"
+ + "\n"
+ + ""
+ );
+ textPane.setBackground(getPanelBackgroundColor());
+ textPane.setFocusable(false);
+ return textPane;
}
- private String getTooltipText(boolean selected) {
- if (YouUserManager.getInstance().isSubscribed()) {
- return selected ?
- CodeGPTBundle.get("toolwindow.chat.youProCheckBox.disable") :
- CodeGPTBundle.get("toolwindow.chat.youProCheckBox.enable");
- }
- return CodeGPTBundle.get("toolwindow.chat.youProCheckBox.notAllowed");
- }
+ private class ChatToolWindowCompletionEventListener implements ToolWindowCompletionEventListener {
- private String getModel() {
- var settings = SettingsState.getInstance();
- if (settings.getSelectedService() == ServiceType.OPENAI) {
- return OpenAISettingsState.getInstance().getModel();
+ private final Logger LOG = Logger.getInstance(ChatToolWindowCompletionEventListener.class);
+
+ private final ResponsePanel responsePanel;
+ private final ChatMessageResponseBody responseContainer;
+
+ public ChatToolWindowCompletionEventListener(ResponsePanel responsePanel) {
+ this.responsePanel = responsePanel;
+ this.responseContainer = (ChatMessageResponseBody) responsePanel.getContent();
}
- if (settings.getSelectedService() == ServiceType.AZURE) {
- return AzureSettingsState.getInstance().getModel();
- }
- if (settings.getSelectedService() == ServiceType.YOU) {
- return "YouCode";
- }
- if (settings.getSelectedService() == ServiceType.LLAMA_CPP) {
- var llamaSettings = LlamaSettingsState.getInstance();
- if (llamaSettings.isUseCustomModel()) {
- var filePath = llamaSettings.getCustomLlamaModelPath();
- int lastSeparatorIndex = filePath.lastIndexOf('/');
- if (lastSeparatorIndex == -1) {
- return filePath;
- }
- return filePath.substring(lastSeparatorIndex + 1);
+
+ @Override
+ public void handleMessage(String message) {
+ try {
+ LOG.debug(message);
+ ApplicationManager.getApplication()
+ .invokeLater(() -> responseContainer.update(message));
+ } catch (Exception e) {
+ responseContainer.displayDefaultError();
+ throw new RuntimeException("Error while updating the content", e);
}
- var huggingFaceModel = llamaSettings.getHuggingFaceModel();
- var llamaModel = LlamaModel.findByHuggingFaceModel(huggingFaceModel);
- return String.format(
- "%s %dB (Q%d)",
- llamaModel.getLabel(),
- huggingFaceModel.getParameterSize(),
- huggingFaceModel.getQuantization());
}
- return "Unknown";
+ @Override
+ public void handleError(ErrorDetails error, Throwable ex) {
+ try {
+ if ("insufficient_quota".equals(error.getCode())) {
+ if (SettingsState.getInstance().getSelectedService() == ServiceType.OPENAI) {
+ OpenAISettingsState.getInstance().setOpenAIQuotaExceeded(true);
+ }
+ responseContainer.displayQuotaExceeded();
+ } else {
+ responseContainer.displayError(error.getMessage());
+ }
+ } finally {
+ LOG.error(error.getMessage(), ex);
+ responsePanel.enableActions();
+ stopStreaming(responseContainer);
+ }
+ }
+
+ @Override
+ public void handleTokensExceeded(Conversation conversation, Message message) {
+ var answer = OverlayUtils.showTokenLimitExceededDialog();
+ if (answer == OK) {
+ TelemetryAction.IDE_ACTION.createActionMessage()
+ .property("action", "DISCARD_TOKEN_LIMIT")
+ .property("model", conversation.getModel())
+ .send();
+
+ conversationService.discardTokenLimits(conversation);
+ call(conversation, message, responsePanel, true);
+ } else {
+ stopStreaming(responseContainer);
+ }
+ }
+
+ @Override
+ public void handleCompleted(
+ String fullMessage,
+ Message message,
+ Conversation conversation,
+ boolean isRetry) {
+ responsePanel.enableActions();
+ conversationService.saveMessage(fullMessage, message, conversation, isRetry);
+ stopStreaming(responseContainer);
+
+ var serpResults = serpResultsMapping.get(message.getId());
+ var containsResults = serpResults != null && !serpResults.isEmpty();
+ if (YouSettingsState.getInstance().isDisplayWebSearchResults() && containsResults) {
+ responseContainer.displaySerpResults(serpResults);
+ }
+
+ if (containsResults) {
+ message.setSerpResults(serpResults);
+ }
+ }
+
+ @Override
+ public void handleSerpResults(List results, Message message) {
+ serpResultsMapping.put(message.getId(), results);
+ }
+
+ private void stopStreaming(ChatMessageResponseBody responseContainer) {
+ SwingUtilities.invokeLater(() -> {
+ userPromptTextArea.setSubmitEnabled(true);
+ responseContainer.hideCarets();
+ });
+ }
}
}
diff --git a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/components/ChatMessageResponseBody.java b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/components/ChatMessageResponseBody.java
index f7efdf3c..7a9e5be9 100644
--- a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/components/ChatMessageResponseBody.java
+++ b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/components/ChatMessageResponseBody.java
@@ -20,7 +20,6 @@ import com.vladsch.flexmark.html.HtmlRenderer;
import com.vladsch.flexmark.parser.Parser;
import com.vladsch.flexmark.util.data.MutableDataSet;
import ee.carlrobert.codegpt.actions.ActionType;
-import ee.carlrobert.codegpt.completions.you.YouSerpResult;
import ee.carlrobert.codegpt.settings.SettingsConfigurable;
import ee.carlrobert.codegpt.telemetry.TelemetryAction;
import ee.carlrobert.codegpt.toolwindow.chat.ResponseNodeRenderer;
@@ -29,6 +28,7 @@ import ee.carlrobert.codegpt.toolwindow.chat.StreamResponseType;
import ee.carlrobert.codegpt.toolwindow.chat.editor.ResponseEditor;
import ee.carlrobert.codegpt.util.MarkdownUtils;
import ee.carlrobert.codegpt.util.SwingUtils;
+import ee.carlrobert.llm.client.you.completion.YouSerpResult;
import java.awt.BorderLayout;
import java.awt.Color;
import java.util.List;
@@ -145,9 +145,7 @@ public class ChatMessageResponseBody extends JPanel {
"%s
",
message);
if (responseReceived) {
- var errorPane = createTextPane();
- errorPane.setText(errorText);
- add(new ResponseWrapper().add(errorPane));
+ add(new ResponseWrapper().add(createTextPane(errorText)));
} else {
currentlyProcessedTextPane.setText(errorText);
}
@@ -158,22 +156,24 @@ public class ChatMessageResponseBody extends JPanel {
}
public void displaySerpResults(List serpResults) {
+ var html = getSearchResultsHtml(serpResults);
+ if (responseReceived) {
+ add(new ResponseWrapper().add(createTextPane(html)));
+ } else {
+ currentlyProcessedTextPane.setText(html);
+ }
+ }
+
+ private String getSearchResultsHtml(List serpResults) {
var titles = serpResults.stream()
.map(result -> format("%s",
result.getUrl(), result.getName()))
.collect(Collectors.joining());
- var html = format(
+ return format(
"" +
"Search results:
" +
"%s
" +
"", titles);
- if (responseReceived) {
- var textPane = createTextPane();
- textPane.setText(html);
- add(new ResponseWrapper().add(textPane));
- } else {
- currentlyProcessedTextPane.setText(html);
- }
}
public void clear() {
@@ -224,7 +224,7 @@ public class ChatMessageResponseBody extends JPanel {
private void prepareProcessingTextResponse() {
hideCarets();
currentlyProcessedEditor = null;
- currentlyProcessedTextPane = createTextPane();
+ currentlyProcessedTextPane = createTextPane("");
currentlyProcessedElement = new ResponseWrapper();
currentlyProcessedElement.add(currentlyProcessedTextPane);
add(currentlyProcessedElement);
@@ -263,8 +263,8 @@ public class ChatMessageResponseBody extends JPanel {
}
}
- private JTextPane createTextPane() {
- var textPane = SwingUtils.createTextPane(event -> {
+ private JTextPane createTextPane(String text) {
+ var textPane = SwingUtils.createTextPane(text, event -> {
if (FileUtil.exists(event.getDescription()) && ACTIVATED.equals(event.getEventType())) {
VirtualFile file = LocalFileSystem.getInstance().findFileByPath(event.getDescription());
FileEditorManager.getInstance(project).openFile(Objects.requireNonNull(file), true);
diff --git a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/components/YouProCheckbox.java b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/components/YouProCheckbox.java
new file mode 100644
index 00000000..ce21bc34
--- /dev/null
+++ b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/components/YouProCheckbox.java
@@ -0,0 +1,40 @@
+package ee.carlrobert.codegpt.toolwindow.chat.components;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.ui.components.JBCheckBox;
+import ee.carlrobert.codegpt.CodeGPTBundle;
+import ee.carlrobert.codegpt.completions.you.YouUserManager;
+import ee.carlrobert.codegpt.settings.state.YouSettingsState;
+import ee.carlrobert.codegpt.toolwindow.chat.YouModelChangeNotifier;
+import org.jetbrains.annotations.NotNull;
+
+public class YouProCheckbox extends JBCheckBox {
+
+ public YouProCheckbox(@NotNull Project project) {
+ super(CodeGPTBundle.get("toolwindow.chat.youProCheckBox.text"));
+ var youSettings = YouSettingsState.getInstance();
+ var youUserManager = YouUserManager.getInstance();
+ setOpaque(false);
+ setEnabled(youUserManager.isSubscribed());
+ setSelected(youSettings.isUseGPT4Model());
+ setToolTipText(getTooltipText(youUserManager, isSelected()));
+ addChangeListener(e -> {
+ var selected = ((JBCheckBox) e.getSource()).isSelected();
+ setToolTipText(getTooltipText(youUserManager, selected));
+ // TODO: Remove
+ project.getMessageBus()
+ .syncPublisher(YouModelChangeNotifier.YOU_MODEL_CHANGE_NOTIFIER_TOPIC)
+ .modelChanged(selected);
+ youSettings.setUseGPT4Model(selected);
+ });
+ }
+
+ private String getTooltipText(YouUserManager youUserManager, boolean selected) {
+ if (youUserManager.isSubscribed()) {
+ return selected ?
+ CodeGPTBundle.get("toolwindow.chat.youProCheckBox.disable") :
+ CodeGPTBundle.get("toolwindow.chat.youProCheckBox.enable");
+ }
+ return CodeGPTBundle.get("toolwindow.chat.youProCheckBox.notAllowed");
+ }
+}
diff --git a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/contextual/ContextualChatToolWindowLandingPanel.java b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/contextual/ContextualChatToolWindowLandingPanel.java
index f8892c52..983863c9 100644
--- a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/contextual/ContextualChatToolWindowLandingPanel.java
+++ b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/contextual/ContextualChatToolWindowLandingPanel.java
@@ -71,7 +71,7 @@ class ContextualChatToolWindowLandingPanel extends ResponsePanel {
}
private JTextPane createTextPane() {
- var textPane = SwingUtils.createTextPane(this::handleHyperlinkClicked);
+ var textPane = SwingUtils.createTextPane("", this::handleHyperlinkClicked);
textPane.setBackground(getPanelBackgroundColor());
return textPane;
}
diff --git a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/editor/ResponseEditor.java b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/editor/ResponseEditor.java
index b22459b4..d8f04735 100644
--- a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/editor/ResponseEditor.java
+++ b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/editor/ResponseEditor.java
@@ -65,12 +65,12 @@ public class ResponseEditor extends JPanel implements Disposable {
var settings = editorEx.getSettings();
settings.setAdditionalColumnsCount(0);
- settings.setAdditionalLinesCount(0);
+ settings.setAdditionalLinesCount(1);
settings.setAdditionalPageAtBottom(false);
settings.setVirtualSpace(false);
settings.setUseSoftWraps(false);
- settings.setLineMarkerAreaShown(true);
- settings.setGutterIconsShown(true);
+ settings.setLineMarkerAreaShown(false);
+ settings.setGutterIconsShown(false);
add(createHeaderComponent(), BorderLayout.NORTH);
add(editor.getComponent(), BorderLayout.SOUTH);
diff --git a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/standard/StandardChatToolWindowLandingPanel.java b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/standard/StandardChatToolWindowLandingPanel.java
index b13c7fb3..718ff11b 100644
--- a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/standard/StandardChatToolWindowLandingPanel.java
+++ b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/standard/StandardChatToolWindowLandingPanel.java
@@ -1,6 +1,7 @@
package ee.carlrobert.codegpt.toolwindow.chat.standard;
import static ee.carlrobert.codegpt.util.ThemeUtils.getPanelBackgroundColor;
+import static java.lang.String.format;
import static javax.swing.event.HyperlinkEvent.EventType.ACTIVATED;
import com.intellij.openapi.diagnostic.Logger;
@@ -23,25 +24,20 @@ class StandardChatToolWindowLandingPanel extends ResponsePanel {
}
private JTextPane createContent() {
- var description = createTextPane();
- description.setText("" +
- String.format(
- "Welcome %s, I'm your intelligent code companion, here to be your partner-in-crime for getting things done in a flash. Together, we'll tackle tasks swiftly and efficiently, making your coding experience a joy.
",
- SettingsState.getInstance().getDisplayName()) +
- "Feel free to ask me anything you'd like, but my true superpower lies in assisting you with your code! Here are a few examples of how I can assist you:
" +
- "Being an AI-powered assistant, I may occasionally have surprises or make mistakes. Therefore, it's wise to double-check any code or suggestions I provide.
" +
- "");
-
- return description;
- }
-
- private JTextPane createTextPane() {
- var textPane = SwingUtils.createTextPane(this::handleHyperlinkClicked);
+ var textPane = SwingUtils.createTextPane(
+ "" +
+ format(
+ "Welcome %s, I'm your intelligent code companion, here to be your partner-in-crime for getting things done in a flash. Together, we'll tackle tasks swiftly and efficiently, making your coding experience a joy.
",
+ SettingsState.getInstance().getDisplayName()) +
+ "Feel free to ask me anything you'd like, but my true superpower lies in assisting you with your code! Here are a few examples of how I can assist you:
" +
+ "Being an AI-powered assistant, I may occasionally have surprises or make mistakes. Therefore, it's wise to double-check any code or suggestions I provide." +
+ "",
+ this::handleHyperlinkClicked);
textPane.setBackground(getPanelBackgroundColor());
return textPane;
}
diff --git a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/standard/StandardChatToolWindowTabPanel.java b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/standard/StandardChatToolWindowTabPanel.java
index 188c7d2a..1f78350f 100644
--- a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/standard/StandardChatToolWindowTabPanel.java
+++ b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/standard/StandardChatToolWindowTabPanel.java
@@ -6,15 +6,14 @@ import com.intellij.openapi.editor.impl.EditorImpl;
import com.intellij.openapi.project.Project;
import ee.carlrobert.codegpt.conversations.Conversation;
import ee.carlrobert.codegpt.conversations.message.Message;
-import ee.carlrobert.codegpt.settings.state.SettingsState;
import ee.carlrobert.codegpt.settings.state.YouSettingsState;
import ee.carlrobert.codegpt.toolwindow.chat.BaseChatToolWindowTabPanel;
import ee.carlrobert.codegpt.toolwindow.chat.components.ChatMessageResponseBody;
import ee.carlrobert.codegpt.toolwindow.chat.components.ResponsePanel;
import ee.carlrobert.codegpt.toolwindow.chat.components.UserMessagePanel;
import ee.carlrobert.codegpt.util.EditorUtils;
-import ee.carlrobert.codegpt.util.file.FileUtils;
import ee.carlrobert.codegpt.util.OverlayUtils;
+import ee.carlrobert.codegpt.util.file.FileUtils;
import javax.swing.JComponent;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -25,7 +24,8 @@ public class StandardChatToolWindowTabPanel extends BaseChatToolWindowTabPanel {
this(project, null);
}
- public StandardChatToolWindowTabPanel(@NotNull Project project,
+ public StandardChatToolWindowTabPanel(
+ @NotNull Project project,
@Nullable Conversation conversation) {
super(project, false);
if (conversation == null) {
@@ -79,7 +79,7 @@ public class StandardChatToolWindowTabPanel extends BaseChatToolWindowTabPanel {
messageWrapper.add(new UserMessagePanel(project, message, false, this));
messageWrapper.add(new ResponsePanel()
.withReloadAction(() -> reloadMessage(message, conversation))
- .withDeleteAction(() -> deleteMessage(message.getId(), messageWrapper, conversation))
+ .withDeleteAction(() -> removeMessage(message.getId(), messageWrapper, conversation))
.addContent(messageResponseBody));
});
setConversation(conversation);
diff --git a/src/main/java/ee/carlrobert/codegpt/util/SwingUtils.java b/src/main/java/ee/carlrobert/codegpt/util/SwingUtils.java
index 8cdc579d..0b4f1f83 100644
--- a/src/main/java/ee/carlrobert/codegpt/util/SwingUtils.java
+++ b/src/main/java/ee/carlrobert/codegpt/util/SwingUtils.java
@@ -22,12 +22,17 @@ import javax.swing.event.HyperlinkListener;
public class SwingUtils {
- public static JTextPane createTextPane(HyperlinkListener listener) {
+ public static JTextPane createTextPane(String text) {
+ return createTextPane(text, SwingUtils::handleHyperlinkClicked);
+ }
+
+ public static JTextPane createTextPane(String text, HyperlinkListener listener) {
var textPane = new JTextPane();
textPane.putClientProperty(JTextPane.HONOR_DISPLAY_PROPERTIES, true);
textPane.addHyperlinkListener(listener);
textPane.setContentType("text/html");
textPane.setEditable(false);
+ textPane.setText(text);
return textPane;
}
diff --git a/src/test/java/ee/carlrobert/codegpt/completions/DefaultCompletionRequestHandlerTest.java b/src/test/java/ee/carlrobert/codegpt/completions/DefaultCompletionRequestHandlerTest.java
index 7fc79608..6143650b 100644
--- a/src/test/java/ee/carlrobert/codegpt/completions/DefaultCompletionRequestHandlerTest.java
+++ b/src/test/java/ee/carlrobert/codegpt/completions/DefaultCompletionRequestHandlerTest.java
@@ -13,6 +13,7 @@ import static org.awaitility.Awaitility.await;
import com.intellij.testFramework.fixtures.BasePlatformTestCase;
import ee.carlrobert.codegpt.CodeGPTPlugin;
+import ee.carlrobert.codegpt.conversations.Conversation;
import ee.carlrobert.codegpt.conversations.ConversationService;
import ee.carlrobert.codegpt.conversations.message.Message;
import ee.carlrobert.codegpt.credentials.AzureCredentialsManager;
@@ -58,8 +59,7 @@ public class DefaultCompletionRequestHandlerTest extends BasePlatformTestCase {
public void testOpenAIChatCompletionCall() {
var message = new Message("TEST_PROMPT");
var conversation = ConversationService.getInstance().startConversation();
- var requestHandler = new CompletionRequestHandler();
- requestHandler.addRequestCompletedListener(message::setResponse);
+ var requestHandler = new CompletionRequestHandler(false, getRequestEventListener(message));
SettingsState.getInstance().setSelectedService(ServiceType.OPENAI);
expectStreamRequest("/v1/chat/completions", request -> {
assertThat(request.getMethod()).isEqualTo("POST");
@@ -94,9 +94,8 @@ public class DefaultCompletionRequestHandlerTest extends BasePlatformTestCase {
azureSettings.setApiVersion("TEST_API_VERSION");
azureSettings.setDeploymentId("TEST_DEPLOYMENT_ID");
var conversationService = ConversationService.getInstance();
- var requestHandler = new CompletionRequestHandler();
var message = new Message("TEST_PROMPT");
- requestHandler.addRequestCompletedListener(message::setResponse);
+ var requestHandler = new CompletionRequestHandler(false, getRequestEventListener(message));
var prevMessage = new Message("TEST_PREV_PROMPT");
prevMessage.setResponse("TEST_PREV_RESPONSE");
var conversation = conversationService.startConversation();
@@ -132,8 +131,7 @@ public class DefaultCompletionRequestHandlerTest extends BasePlatformTestCase {
var message = new Message("TEST_PROMPT");
var conversation = ConversationService.getInstance().startConversation();
conversation.addMessage(new Message("Ping", "Pong"));
- var requestHandler = new CompletionRequestHandler();
- requestHandler.addRequestCompletedListener(message::setResponse);
+ var requestHandler = new CompletionRequestHandler(false, getRequestEventListener(message));
SettingsState.getInstance().setSelectedService(ServiceType.YOU);
expectStreamRequest("/api/streamingSearch", request -> {
assertThat(request.getMethod()).isEqualTo("GET");
@@ -182,8 +180,7 @@ public class DefaultCompletionRequestHandlerTest extends BasePlatformTestCase {
var message = new Message("TEST_PROMPT");
var conversation = ConversationService.getInstance().startConversation();
conversation.addMessage(new Message("Ping", "Pong"));
- var requestHandler = new CompletionRequestHandler();
- requestHandler.addRequestCompletedListener(message::setResponse);
+ var requestHandler = new CompletionRequestHandler(false, getRequestEventListener(message));
SettingsState.getInstance().setSelectedService(ServiceType.LLAMA_CPP);
expectStreamRequest("/completion", request -> {
assertThat(request.getBody())
@@ -214,4 +211,17 @@ public class DefaultCompletionRequestHandlerTest extends BasePlatformTestCase {
private void expectStreamRequest(String path, StreamHttpExchange exchange) {
server.addExpectation(new StreamExpectation(path, exchange));
}
+
+ private ToolWindowCompletionEventListener getRequestEventListener(Message message) {
+ return new ToolWindowCompletionEventListener() {
+ @Override
+ public void handleCompleted(
+ String fullMessage,
+ Message conversationMessage,
+ Conversation conversation,
+ boolean isRetry) {
+ message.setResponse(fullMessage);
+ }
+ };
+ }
}