mirror of
https://github.com/carlrobertoh/ProxyAI.git
synced 2026-05-07 17:23:11 +00:00
Clean up BaseChatToolWindowTabPanel code
This commit is contained in:
parent
bfd50b18ad
commit
c3da76f2bd
16 changed files with 450 additions and 400 deletions
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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<Void, String> swingWorker;
|
||||
private EventSource eventSource;
|
||||
private @Nullable Consumer<String> messageListener;
|
||||
private @Nullable BiConsumer<ErrorDetails, Throwable> errorListener;
|
||||
private @Nullable Consumer<String> completedListener;
|
||||
private @Nullable Consumer<List<YouSerpResult>> 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<String> messageListener) {
|
||||
this.messageListener = messageListener;
|
||||
}
|
||||
|
||||
public void addErrorListener(BiConsumer<ErrorDetails, Throwable> errorListener) {
|
||||
this.errorListener = errorListener;
|
||||
}
|
||||
|
||||
public void addRequestCompletedListener(Consumer<String> completedListener) {
|
||||
this.completedListener = completedListener;
|
||||
}
|
||||
|
||||
public void addTokensExceededListener(Runnable tokensExceededListener) {
|
||||
this.tokensExceededListener = tokensExceededListener;
|
||||
}
|
||||
|
||||
public void addSerpResultsListener(Consumer<List<YouSerpResult>> 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<YouSerpResult> 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<YouSerpResult> results) {
|
||||
if (serpResultsListener != null) {
|
||||
serpResultsListener.accept(results);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void sendInfo(SettingsState settings) {
|
||||
TelemetryAction.COMPLETION.createActionMessage()
|
||||
.property("conversationId", conversation.getId().toString())
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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<YouSerpResult> results, Message message) {}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -134,15 +134,10 @@ public class YouServiceSelectionForm extends JPanel {
|
|||
}
|
||||
|
||||
private JTextPane createSignUpTextPane() {
|
||||
var textPane = createTextPane(
|
||||
var textPane = SwingUtils.createTextPane(
|
||||
"<html><a href=\"https://you.com/code\">Don't have an account? Sign up</a></html>");
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<SettingsState> {
|
|||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
"<html>\n"
|
||||
+ "<body>\n"
|
||||
+ " <p style=\"margin: 4px 0;\">Use CodeGPT coupon for free month of GPT-4.</p>\n"
|
||||
+ " <p style=\"margin: 4px 0;\">\n"
|
||||
+ " <a href=\"https://you.com/plans\">Sign up here</a>\n"
|
||||
+ " </p>\n"
|
||||
+ "</body>\n"
|
||||
+ "</html>");
|
||||
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(
|
||||
"<html>\n"
|
||||
+ "<body>\n"
|
||||
+ " <p style=\"margin: 4px 0;\">Use CodeGPT coupon for free month of GPT-4.</p>\n"
|
||||
+ " <p style=\"margin: 4px 0;\">\n"
|
||||
+ " <a href=\"https://you.com/plans\">Sign up here</a>\n"
|
||||
+ " </p>\n"
|
||||
+ "</body>\n"
|
||||
+ "</html>"
|
||||
);
|
||||
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<YouSerpResult> results, Message message) {
|
||||
serpResultsMapping.put(message.getId(), results);
|
||||
}
|
||||
|
||||
private void stopStreaming(ChatMessageResponseBody responseContainer) {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
userPromptTextArea.setSubmitEnabled(true);
|
||||
responseContainer.hideCarets();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
|||
"<html><p style=\"margin-top: 4px; margin-bottom: 8px;\">%s</p></html>",
|
||||
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<YouSerpResult> serpResults) {
|
||||
var html = getSearchResultsHtml(serpResults);
|
||||
if (responseReceived) {
|
||||
add(new ResponseWrapper().add(createTextPane(html)));
|
||||
} else {
|
||||
currentlyProcessedTextPane.setText(html);
|
||||
}
|
||||
}
|
||||
|
||||
private String getSearchResultsHtml(List<YouSerpResult> serpResults) {
|
||||
var titles = serpResults.stream()
|
||||
.map(result -> format("<li style=\"margin-bottom: 4px;\"><a href=\"%s\">%s</a></li>",
|
||||
result.getUrl(), result.getName()))
|
||||
.collect(Collectors.joining());
|
||||
var html = format(
|
||||
return format(
|
||||
"<html>" +
|
||||
"<p><strong>Search results:</strong></p>" +
|
||||
"<ol>%s</ol>" +
|
||||
"</html>", 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);
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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("<html>" +
|
||||
String.format(
|
||||
"<p style=\"margin-top: 4px; margin-bottom: 4px;\">Welcome <strong>%s</strong>, 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.</p>",
|
||||
SettingsState.getInstance().getDisplayName()) +
|
||||
"<p style=\"margin-top: 4px; margin-bottom: 4px;\">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:</p>" +
|
||||
"<ul style=\"margin-top: 4px; margin-bottom: 4px;\">" +
|
||||
"<li><a href=\"GENERATE_UNIT_TESTS\">Generate unit tests for the selected code</a></li" +
|
||||
"<li><a href=\"EXPLAIN_CODE\">Explain the selected code</a></li>" +
|
||||
"<li><a href=\"FIND_BUGS\">Find bugs in the selected code</a></li>" +
|
||||
"</ul" +
|
||||
"<p style=\"margin-top: 4px; margin-bottom: 4px;\">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.</p>" +
|
||||
"</html>");
|
||||
|
||||
return description;
|
||||
}
|
||||
|
||||
private JTextPane createTextPane() {
|
||||
var textPane = SwingUtils.createTextPane(this::handleHyperlinkClicked);
|
||||
var textPane = SwingUtils.createTextPane(
|
||||
"<html>" +
|
||||
format(
|
||||
"<p style=\"margin-top: 4px; margin-bottom: 4px;\">Welcome <strong>%s</strong>, 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.</p>",
|
||||
SettingsState.getInstance().getDisplayName()) +
|
||||
"<p style=\"margin-top: 4px; margin-bottom: 4px;\">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:</p>" +
|
||||
"<ul style=\"margin-top: 4px; margin-bottom: 4px;\">" +
|
||||
"<li><a href=\"GENERATE_UNIT_TESTS\">Generate unit tests for the selected code</a></li" +
|
||||
"<li><a href=\"EXPLAIN_CODE\">Explain the selected code</a></li>" +
|
||||
"<li><a href=\"FIND_BUGS\">Find bugs in the selected code</a></li>" +
|
||||
"</ul" +
|
||||
"<p style=\"margin-top: 4px; margin-bottom: 4px;\">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.</p>" +
|
||||
"</html>",
|
||||
this::handleHyperlinkClicked);
|
||||
textPane.setBackground(getPanelBackgroundColor());
|
||||
return textPane;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue