mirror of
https://github.com/carlrobertoh/ProxyAI.git
synced 2026-05-11 21:31:04 +00:00
Add interactive total token count label, codebase refactoring
This commit is contained in:
parent
d8e5e18998
commit
ec3120a5e6
31 changed files with 804 additions and 322 deletions
|
|
@ -6,6 +6,7 @@ import com.knuddels.jtokkit.Encodings;
|
|||
import com.knuddels.jtokkit.api.Encoding;
|
||||
import com.knuddels.jtokkit.api.EncodingRegistry;
|
||||
import com.knuddels.jtokkit.api.EncodingType;
|
||||
import ee.carlrobert.codegpt.conversations.Conversation;
|
||||
import ee.carlrobert.llm.client.openai.completion.chat.request.OpenAIChatCompletionMessage;
|
||||
|
||||
@Service
|
||||
|
|
@ -21,9 +22,23 @@ public final class EncodingManager {
|
|||
return ApplicationManager.getApplication().getService(EncodingManager.class);
|
||||
}
|
||||
|
||||
public int countConversationTokens(Conversation conversation) {
|
||||
if (conversation != null) {
|
||||
return conversation.getMessages().stream()
|
||||
.mapToInt(
|
||||
message -> countTokens(message.getPrompt()) + countTokens(message.getResponse()))
|
||||
.sum();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public int countMessageTokens(OpenAIChatCompletionMessage message) {
|
||||
return countMessageTokens(message.getRole(), message.getContent());
|
||||
}
|
||||
|
||||
public int countMessageTokens(String role, String content) {
|
||||
var tokensPerMessage = 4; // every message follows <|start|>{role/name}\n{content}<|end|>\n
|
||||
return encoding.countTokens(message.getRole() + message.getContent()) + tokensPerMessage;
|
||||
return countTokens(role + content) + tokensPerMessage;
|
||||
}
|
||||
|
||||
public int countTokens(String text) {
|
||||
|
|
|
|||
|
|
@ -7,9 +7,10 @@ public final class Icons {
|
|||
|
||||
public static final Icon DefaultIcon = IconLoader.getIcon("/icons/codegpt.svg", Icons.class);
|
||||
public static final Icon DefaultSmallIcon = IconLoader.getIcon("/icons/codegpt-small.svg", Icons.class);
|
||||
public static final Icon SendIcon = IconLoader.getIcon("/icons/send.svg", Icons.class);
|
||||
public static final Icon OpenAIIcon = IconLoader.getIcon("/icons/openai.svg", Icons.class);
|
||||
public static final Icon AzureIcon = IconLoader.getIcon("/icons/azure.svg", Icons.class);
|
||||
public static final Icon YouIcon = IconLoader.getIcon("/icons/you.svg", Icons.class);
|
||||
public static final Icon LlamaIcon = IconLoader.getIcon("/icons/llama.svg", Icons.class);
|
||||
public static final Icon OpenAIIcon = IconLoader.getIcon("/icons/openai.svg", Icons.class);
|
||||
public static final Icon SendIcon = IconLoader.getIcon("/icons/send.svg", Icons.class);
|
||||
public static final Icon SparkleIcon = IconLoader.getIcon("/icons/sparkle.svg", Icons.class);
|
||||
public static final Icon YouIcon = IconLoader.getIcon("/icons/you.svg", Icons.class);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
package ee.carlrobert.codegpt.actions.editor;
|
||||
|
||||
import com.intellij.icons.AllIcons;
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import ee.carlrobert.codegpt.Icons;
|
||||
import ee.carlrobert.codegpt.conversations.ConversationsState;
|
||||
import ee.carlrobert.codegpt.toolwindow.chat.standard.StandardChatToolWindowContentManager;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
|
@ -10,7 +10,7 @@ import org.jetbrains.annotations.NotNull;
|
|||
public class AskAction extends AnAction {
|
||||
|
||||
public AskAction() {
|
||||
super("New Chat", "Chat with CodeGPT", AllIcons.Actions.Find);
|
||||
super("New Chat", "Chat with CodeGPT", Icons.SparkleIcon);
|
||||
EditorActionsUtil.registerOrReplaceAction(this);
|
||||
}
|
||||
|
||||
|
|
@ -24,7 +24,8 @@ public class AskAction extends AnAction {
|
|||
var project = event.getProject();
|
||||
if (project != null) {
|
||||
ConversationsState.getInstance().setCurrentConversation(null);
|
||||
var tabPanel = project.getService(StandardChatToolWindowContentManager.class).createNewTabPanel();
|
||||
var tabPanel =
|
||||
project.getService(StandardChatToolWindowContentManager.class).createNewTabPanel();
|
||||
if (tabPanel != null) {
|
||||
tabPanel.displayLandingView();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@ import ee.carlrobert.codegpt.settings.state.SettingsState;
|
|||
import ee.carlrobert.codegpt.settings.state.YouSettingsState;
|
||||
import ee.carlrobert.codegpt.telemetry.core.configuration.TelemetryConfiguration;
|
||||
import ee.carlrobert.codegpt.telemetry.core.service.UserId;
|
||||
import ee.carlrobert.codegpt.util.ApplicationUtils;
|
||||
import ee.carlrobert.embedding.EmbeddingsService;
|
||||
import ee.carlrobert.llm.client.llama.completion.LlamaCompletionRequest;
|
||||
import ee.carlrobert.llm.client.openai.completion.chat.OpenAIChatCompletionModel;
|
||||
|
|
@ -37,13 +36,12 @@ public class CompletionRequestProvider {
|
|||
private static final Logger LOG = Logger.getInstance(CompletionRequestProvider.class);
|
||||
|
||||
public static final String COMPLETION_SYSTEM_PROMPT = "You are an AI programming assistant.\n" +
|
||||
"When asked for you name, you must respond with \"CodeGPT\".\n" +
|
||||
"Follow the user's requirements carefully & to the letter.\n" +
|
||||
"Your responses should be informative and logical.\n" +
|
||||
"You should always adhere to technical information.\n" +
|
||||
"If the user asks for code or technical questions, you must provide code suggestions and " +
|
||||
"adhere to technical information.\n" +
|
||||
"If the question is related to a developer, CodeGPT must respond with " +
|
||||
"If the question is related to a developer, you must respond with " +
|
||||
"content related to a developer.\n" +
|
||||
"First think step-by-step - describe your plan for what to build in pseudocode, " +
|
||||
"written out in great detail.\n" +
|
||||
|
|
@ -125,8 +123,7 @@ public class CompletionRequestProvider {
|
|||
return (OpenAIChatCompletionRequest) builder.build();
|
||||
}
|
||||
|
||||
private List<OpenAIChatCompletionMessage> buildMessages(
|
||||
@Nullable String model,
|
||||
public List<OpenAIChatCompletionMessage> buildMessages(
|
||||
Message message,
|
||||
boolean isRetry,
|
||||
boolean useContextualSearch) {
|
||||
|
|
@ -149,6 +146,15 @@ public class CompletionRequestProvider {
|
|||
}
|
||||
messages.add(new OpenAIChatCompletionMessage("user", message.getPrompt()));
|
||||
}
|
||||
return messages;
|
||||
}
|
||||
|
||||
private List<OpenAIChatCompletionMessage> buildMessages(
|
||||
@Nullable String model,
|
||||
Message message,
|
||||
boolean isRetry,
|
||||
boolean useContextualSearch) {
|
||||
var messages = buildMessages(message, isRetry, useContextualSearch);
|
||||
|
||||
if (model == null || SettingsState.getInstance().getSelectedService() == ServiceType.YOU) {
|
||||
return messages;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package ee.carlrobert.codegpt.settings.configuration;
|
||||
|
||||
import static ee.carlrobert.codegpt.actions.editor.EditorActionsUtil.DEFAULT_ACTIONS_ARRAY;
|
||||
import static ee.carlrobert.codegpt.completions.CompletionRequestProvider.COMPLETION_SYSTEM_PROMPT;
|
||||
|
||||
import com.intellij.icons.AllIcons;
|
||||
import com.intellij.icons.AllIcons.Nodes;
|
||||
|
|
@ -87,6 +88,12 @@ public class ConfigurationComponent {
|
|||
maxTokensField.setValue(configuration.getMaxTokens());
|
||||
|
||||
systemPromptTextArea = new JTextArea();
|
||||
if (configuration.getSystemPrompt().isEmpty()) {
|
||||
// for backward compatibility
|
||||
systemPromptTextArea.setText(COMPLETION_SYSTEM_PROMPT);
|
||||
} else {
|
||||
systemPromptTextArea.setText(configuration.getSystemPrompt());
|
||||
}
|
||||
systemPromptTextArea.setLineWrap(true);
|
||||
systemPromptTextArea.setBorder(JBUI.Borders.empty(8, 4));
|
||||
systemPromptTextArea.setColumns(60);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
package ee.carlrobert.codegpt.settings.configuration;
|
||||
|
||||
import static ee.carlrobert.codegpt.completions.CompletionRequestProvider.COMPLETION_SYSTEM_PROMPT;
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.components.PersistentStateComponent;
|
||||
import com.intellij.openapi.components.State;
|
||||
|
|
@ -13,7 +15,7 @@ import org.jetbrains.annotations.Nullable;
|
|||
@State(name = "CodeGPT_ConfigurationSettings_210", storages = @Storage("CodeGPT_ConfigurationSettings_210.xml"))
|
||||
public class ConfigurationState implements PersistentStateComponent<ConfigurationState> {
|
||||
|
||||
private String systemPrompt = "";
|
||||
private String systemPrompt = COMPLETION_SYSTEM_PROMPT;
|
||||
private int maxTokens = 1000;
|
||||
private double temperature = 0.2;
|
||||
private boolean createNewChatOnEachAction;
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ public class LlamaSettingsState implements PersistentStateComponent<LlamaSetting
|
|||
private String customLlamaModelPath = "";
|
||||
private HuggingFaceModel huggingFaceModel = HuggingFaceModel.CODE_LLAMA_7B_Q4;
|
||||
private PromptTemplate promptTemplate = PromptTemplate.LLAMA;
|
||||
private int serverPort = getRandomAvailablePortOrDefault();
|
||||
private Integer serverPort = getRandomAvailablePortOrDefault();
|
||||
private int contextSize = 2048;
|
||||
|
||||
public LlamaSettingsState() {
|
||||
|
|
@ -70,11 +70,11 @@ public class LlamaSettingsState implements PersistentStateComponent<LlamaSetting
|
|||
this.promptTemplate = promptTemplate;
|
||||
}
|
||||
|
||||
public int getServerPort() {
|
||||
public Integer getServerPort() {
|
||||
return serverPort;
|
||||
}
|
||||
|
||||
public void setServerPort(int serverPort) {
|
||||
public void setServerPort(Integer serverPort) {
|
||||
this.serverPort = serverPort;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,28 +1,28 @@
|
|||
package ee.carlrobert.codegpt.toolwindow.chat;
|
||||
|
||||
import static com.intellij.openapi.ui.Messages.OK;
|
||||
import static ee.carlrobert.codegpt.util.SwingUtils.createScrollPaneWithSmartScroller;
|
||||
import static ee.carlrobert.codegpt.util.ThemeUtils.getPanelBackgroundColor;
|
||||
import static java.lang.String.format;
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.EditorFactory;
|
||||
import com.intellij.openapi.editor.event.EditorFactoryEvent;
|
||||
import com.intellij.openapi.editor.event.EditorFactoryListener;
|
||||
import com.intellij.openapi.editor.event.SelectionEvent;
|
||||
import com.intellij.openapi.editor.event.SelectionListener;
|
||||
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.DocumentAdapter;
|
||||
import com.intellij.ui.JBColor;
|
||||
import com.intellij.ui.ScrollPaneFactory;
|
||||
import com.intellij.ui.components.JBCheckBox;
|
||||
import com.intellij.util.messages.MessageBusConnection;
|
||||
import com.intellij.util.ui.JBUI;
|
||||
import com.intellij.util.ui.JBUI.Borders;
|
||||
import ee.carlrobert.codegpt.EncodingManager;
|
||||
import ee.carlrobert.codegpt.actions.ActionType;
|
||||
import ee.carlrobert.codegpt.completions.CompletionRequestHandler;
|
||||
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;
|
||||
|
|
@ -31,34 +31,29 @@ import ee.carlrobert.codegpt.settings.state.OpenAISettingsState;
|
|||
import ee.carlrobert.codegpt.settings.state.SettingsState;
|
||||
import ee.carlrobert.codegpt.settings.state.YouSettingsState;
|
||||
import ee.carlrobert.codegpt.telemetry.TelemetryAction;
|
||||
import ee.carlrobert.codegpt.toolwindow.ModelIconLabel;
|
||||
import ee.carlrobert.codegpt.toolwindow.chat.components.ChatMessageResponseBody;
|
||||
import ee.carlrobert.codegpt.toolwindow.chat.components.ResponsePanel;
|
||||
import ee.carlrobert.codegpt.toolwindow.chat.components.SmartScroller;
|
||||
import ee.carlrobert.codegpt.toolwindow.chat.components.TotalTokensPanel;
|
||||
import ee.carlrobert.codegpt.toolwindow.chat.components.UserMessagePanel;
|
||||
import ee.carlrobert.codegpt.toolwindow.chat.components.UserPromptTextArea;
|
||||
import ee.carlrobert.codegpt.toolwindow.chat.components.UserPromptTextAreaHeader;
|
||||
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;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
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;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.text.BadLocationException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
|
|
@ -67,18 +62,18 @@ 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;
|
||||
private final Map<UUID, JPanel> visibleMessagePanels = new HashMap<>();
|
||||
private final Map<UUID, List<YouSerpResult>> serpResultsMapping = new HashMap<>();
|
||||
private final JBCheckBox gpt4CheckBox;
|
||||
|
||||
protected final TotalTokensPanel totalTokensPanel;
|
||||
protected final Project project;
|
||||
protected final UserPromptTextArea userPromptTextArea;
|
||||
protected final ConversationService conversationService;
|
||||
protected final ChatToolWindowScrollablePanel toolWindowScrollablePanel;
|
||||
private final EncodingManager encodingManager;
|
||||
|
||||
private boolean streaming;
|
||||
protected @Nullable Conversation conversation;
|
||||
|
||||
protected abstract JComponent getLandingView();
|
||||
|
|
@ -86,22 +81,24 @@ public abstract class BaseChatToolWindowTabPanel implements ChatToolWindowTabPan
|
|||
public BaseChatToolWindowTabPanel(@NotNull Project project, boolean useContextualSearch) {
|
||||
this.project = project;
|
||||
this.useContextualSearch = useContextualSearch;
|
||||
this.conversationService = ConversationService.getInstance();
|
||||
this.scrollablePanel = new ScrollablePanel(new VerticalStackLayout());
|
||||
this.userPromptTextArea = new UserPromptTextArea(this::handleSubmit);
|
||||
this.gpt4CheckBox = new YouProCheckbox(project);
|
||||
this.settings = SettingsState.getInstance();
|
||||
this.youUserManager = YouUserManager.getInstance();
|
||||
this.rootPanel = createRootPanel();
|
||||
conversationService = ConversationService.getInstance();
|
||||
encodingManager = EncodingManager.getInstance();
|
||||
settings = SettingsState.getInstance();
|
||||
toolWindowScrollablePanel = new ChatToolWindowScrollablePanel(settings);
|
||||
gpt4CheckBox = new YouProCheckbox(project);
|
||||
userPromptTextArea = new UserPromptTextArea(this::handleSubmit, getUserPromptDocumentAdapter());
|
||||
totalTokensPanel = new TotalTokensPanel(
|
||||
null,
|
||||
userPromptTextArea.getText(),
|
||||
EditorUtils.getSelectedEditorSelectedText(project));
|
||||
rootPanel = createRootPanel();
|
||||
|
||||
addSelectionListeners();
|
||||
|
||||
userPromptTextArea.requestFocusInWindow();
|
||||
userPromptTextArea.requestFocus();
|
||||
}
|
||||
|
||||
public void requestFocusForTextArea() {
|
||||
userPromptTextArea.focus();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JPanel getContent() {
|
||||
return rootPanel;
|
||||
|
|
@ -119,13 +116,7 @@ public abstract class BaseChatToolWindowTabPanel implements ChatToolWindowTabPan
|
|||
|
||||
@Override
|
||||
public void displayLandingView() {
|
||||
scrollablePanel.removeAll();
|
||||
scrollablePanel.add(getLandingView());
|
||||
if (settings.getSelectedService() == ServiceType.YOU &&
|
||||
(!youUserManager.isAuthenticated() || !youUserManager.isSubscribed())) {
|
||||
scrollablePanel.add(new ResponsePanel().addContent(createYouCouponTextPane()));
|
||||
}
|
||||
revalidateScrollablePanel();
|
||||
toolWindowScrollablePanel.displayLandingView(getLandingView());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -136,36 +127,53 @@ public abstract class BaseChatToolWindowTabPanel implements ChatToolWindowTabPan
|
|||
|
||||
@Override
|
||||
public void sendMessage(Message message) {
|
||||
streaming = true;
|
||||
if (conversation == null) {
|
||||
conversation = conversationService.startConversation();
|
||||
}
|
||||
|
||||
var messageWrapper = createNewMessageWrapper(message.getId());
|
||||
messageWrapper.add(new UserMessagePanel(project, message, this));
|
||||
var messagePanel = toolWindowScrollablePanel.addMessage(message.getId());
|
||||
messagePanel.add(new UserMessagePanel(project, message, this));
|
||||
var responsePanel = new ResponsePanel()
|
||||
.withReloadAction(() -> reloadMessage(message, conversation))
|
||||
.withDeleteAction(() -> removeMessage(message.getId(), messageWrapper, conversation))
|
||||
.withDeleteAction(() -> removeMessage(message.getId(), conversation))
|
||||
.addContent(new ChatMessageResponseBody(project, true, this));
|
||||
messageWrapper.add(responsePanel);
|
||||
messagePanel.add(responsePanel);
|
||||
|
||||
totalTokensPanel.updateUserPromptTokens(message.getPrompt());
|
||||
|
||||
call(conversation, message, responsePanel, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TokenDetails getTokenDetails() {
|
||||
return totalTokensPanel.getTokenDetails();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
}
|
||||
|
||||
public void requestFocusForTextArea() {
|
||||
userPromptTextArea.focus();
|
||||
}
|
||||
|
||||
public void updateConversationTokens() {
|
||||
totalTokensPanel.updateConversationTokens(conversation);
|
||||
}
|
||||
|
||||
public boolean isStreaming() {
|
||||
return streaming;
|
||||
}
|
||||
|
||||
protected void reloadMessage(Message message, Conversation conversation) {
|
||||
ResponsePanel responsePanel = null;
|
||||
try {
|
||||
responsePanel = (ResponsePanel) Arrays.stream(
|
||||
visibleMessagePanels.get(message.getId()).getComponents())
|
||||
.filter(component -> component instanceof ResponsePanel)
|
||||
.findFirst().orElseThrow();
|
||||
responsePanel = toolWindowScrollablePanel.getMessageResponsePanel(message.getId());
|
||||
((ChatMessageResponseBody) responsePanel.getContent()).clear();
|
||||
revalidateScrollablePanel();
|
||||
toolWindowScrollablePanel.update();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Couldn't delete the existing message component", e);
|
||||
throw new RuntimeException("Could not delete the existing message component", e);
|
||||
} finally {
|
||||
LOG.debug("Reloading message: " + message.getId());
|
||||
|
||||
|
|
@ -175,22 +183,16 @@ public abstract class BaseChatToolWindowTabPanel implements ChatToolWindowTabPan
|
|||
call(conversation, message, responsePanel, true);
|
||||
}
|
||||
|
||||
totalTokensPanel.updateConversationTokens(conversation);
|
||||
|
||||
TelemetryAction.IDE_ACTION.createActionMessage()
|
||||
.property("action", ActionType.RELOAD_MESSAGE.name())
|
||||
.send();
|
||||
}
|
||||
}
|
||||
|
||||
private void revalidateScrollablePanel() {
|
||||
scrollablePanel.repaint();
|
||||
scrollablePanel.revalidate();
|
||||
}
|
||||
|
||||
protected void removeMessage(UUID messageId, JPanel messageWrapper, Conversation conversation) {
|
||||
scrollablePanel.remove(messageWrapper);
|
||||
revalidateScrollablePanel();
|
||||
|
||||
visibleMessagePanels.remove(messageId);
|
||||
protected void removeMessage(UUID messageId, Conversation conversation) {
|
||||
toolWindowScrollablePanel.removeMessage(messageId);
|
||||
conversation.removeMessage(messageId);
|
||||
conversationService.saveConversation(conversation);
|
||||
|
||||
|
|
@ -201,18 +203,10 @@ public abstract class BaseChatToolWindowTabPanel implements ChatToolWindowTabPan
|
|||
}
|
||||
}
|
||||
|
||||
protected JPanel createNewMessageWrapper(UUID messageId) {
|
||||
var messageWrapper = new JPanel();
|
||||
messageWrapper.setLayout(new BoxLayout(messageWrapper, BoxLayout.PAGE_AXIS));
|
||||
scrollablePanel.add(messageWrapper);
|
||||
revalidateScrollablePanel();
|
||||
visibleMessagePanels.put(messageId, messageWrapper);
|
||||
return messageWrapper;
|
||||
}
|
||||
|
||||
protected void clearWindow() {
|
||||
scrollablePanel.removeAll();
|
||||
revalidateScrollablePanel();
|
||||
toolWindowScrollablePanel.clearAll();
|
||||
totalTokensPanel.updateConversationTokens(conversation);
|
||||
updateConversationTokens();
|
||||
}
|
||||
|
||||
private void call(
|
||||
|
|
@ -257,11 +251,17 @@ public abstract class BaseChatToolWindowTabPanel implements ChatToolWindowTabPan
|
|||
}
|
||||
}
|
||||
|
||||
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 createUserPromptPanel() {
|
||||
var panel = new JPanel(new BorderLayout());
|
||||
panel.setBorder(JBUI.Borders.compound(
|
||||
JBUI.Borders.customLine(JBColor.border(), 1, 0, 0, 0),
|
||||
JBUI.Borders.empty(8)));
|
||||
panel.setBackground(getPanelBackgroundColor());
|
||||
panel.add(
|
||||
new UserPromptTextAreaHeader(project, settings, totalTokensPanel, gpt4CheckBox),
|
||||
BorderLayout.NORTH);
|
||||
panel.add(userPromptTextArea, BorderLayout.SOUTH);
|
||||
return panel;
|
||||
}
|
||||
|
||||
private JPanel createRootPanel() {
|
||||
|
|
@ -272,88 +272,20 @@ public abstract class BaseChatToolWindowTabPanel implements ChatToolWindowTabPan
|
|||
gbc.weightx = 1;
|
||||
gbc.gridx = 0;
|
||||
gbc.gridy = 0;
|
||||
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);
|
||||
rootPanel.add(createScrollPaneWithSmartScroller(toolWindowScrollablePanel), gbc);
|
||||
|
||||
gbc.weighty = 0;
|
||||
gbc.fill = GridBagConstraints.HORIZONTAL;
|
||||
gbc.gridy = 1;
|
||||
rootPanel.add(wrapper, gbc);
|
||||
|
||||
rootPanel.add(createUserPromptPanel(), 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();
|
||||
subscribeToYouSubscriptionTopic(messageBusConnection);
|
||||
subscribeToSignedOutTopic(messageBusConnection);
|
||||
header.add(gpt4CheckBox, BorderLayout.LINE_START);
|
||||
}
|
||||
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() {
|
||||
project.getMessageBus()
|
||||
.connect()
|
||||
.subscribe(
|
||||
YouModelChangeNotifier.YOU_MODEL_CHANGE_NOTIFIER_TOPIC,
|
||||
(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,
|
||||
(YouSubscriptionNotifier) () -> {
|
||||
displayLandingView();
|
||||
gpt4CheckBox.setEnabled(true);
|
||||
});
|
||||
}
|
||||
|
||||
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 class ChatToolWindowCompletionEventListener implements ToolWindowCompletionEventListener {
|
||||
|
||||
private final Logger LOG = Logger.getInstance(ChatToolWindowCompletionEventListener.class);
|
||||
|
||||
private final StringBuilder messageBuilder = new StringBuilder();
|
||||
private final ResponsePanel responsePanel;
|
||||
private final ChatMessageResponseBody responseContainer;
|
||||
|
||||
|
|
@ -363,11 +295,18 @@ public abstract class BaseChatToolWindowTabPanel implements ChatToolWindowTabPan
|
|||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(String message) {
|
||||
public void handleMessage(String partialMessage) {
|
||||
try {
|
||||
LOG.debug(message);
|
||||
LOG.debug(partialMessage);
|
||||
ApplicationManager.getApplication()
|
||||
.invokeLater(() -> responseContainer.update(message));
|
||||
.invokeLater(() -> {
|
||||
responseContainer.update(partialMessage);
|
||||
messageBuilder.append(partialMessage);
|
||||
|
||||
var ongoingTokens = encodingManager.countTokens(messageBuilder.toString());
|
||||
totalTokensPanel.update(
|
||||
totalTokensPanel.getTokenDetails().getTotal() + ongoingTokens);
|
||||
});
|
||||
} catch (Exception e) {
|
||||
responseContainer.displayDefaultError();
|
||||
throw new RuntimeException("Error while updating the content", e);
|
||||
|
|
@ -414,18 +353,24 @@ public abstract class BaseChatToolWindowTabPanel implements ChatToolWindowTabPan
|
|||
Message message,
|
||||
Conversation conversation,
|
||||
boolean isRetry) {
|
||||
responsePanel.enableActions();
|
||||
conversationService.saveMessage(fullMessage, message, conversation, isRetry);
|
||||
stopStreaming(responseContainer);
|
||||
try {
|
||||
responsePanel.enableActions();
|
||||
conversationService.saveMessage(fullMessage, message, conversation, isRetry);
|
||||
|
||||
var serpResults = serpResultsMapping.get(message.getId());
|
||||
var containsResults = serpResults != null && !serpResults.isEmpty();
|
||||
if (YouSettingsState.getInstance().isDisplayWebSearchResults() && containsResults) {
|
||||
responseContainer.displaySerpResults(serpResults);
|
||||
}
|
||||
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);
|
||||
if (containsResults) {
|
||||
message.setSerpResults(serpResults);
|
||||
}
|
||||
|
||||
totalTokensPanel.updateUserPromptTokens(userPromptTextArea.getText());
|
||||
totalTokensPanel.updateConversationTokens(conversation);
|
||||
} finally {
|
||||
stopStreaming(responseContainer);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -435,10 +380,51 @@ public abstract class BaseChatToolWindowTabPanel implements ChatToolWindowTabPan
|
|||
}
|
||||
|
||||
private void stopStreaming(ChatMessageResponseBody responseContainer) {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
userPromptTextArea.setSubmitEnabled(true);
|
||||
responseContainer.hideCarets();
|
||||
});
|
||||
streaming = false;
|
||||
userPromptTextArea.setSubmitEnabled(true);
|
||||
responseContainer.hideCarets();
|
||||
}
|
||||
}
|
||||
|
||||
private void addSelectionListeners() {
|
||||
var editorFactory = EditorFactory.getInstance();
|
||||
for (var editor : editorFactory.getAllEditors()) {
|
||||
editor.getSelectionModel().addSelectionListener(getSelectionListener());
|
||||
}
|
||||
editorFactory.addEditorFactoryListener(new EditorFactoryListener() {
|
||||
@Override
|
||||
public void editorCreated(@NotNull EditorFactoryEvent event) {
|
||||
event.getEditor().getSelectionModel().addSelectionListener(getSelectionListener());
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
|
||||
private SelectionListener getSelectionListener() {
|
||||
return new SelectionListener() {
|
||||
@Override
|
||||
public void selectionChanged(@NotNull SelectionEvent e) {
|
||||
var selectedText = e.getEditor().getDocument().getText(e.getNewRange());
|
||||
totalTokensPanel.updateHighlightedTokens(selectedText);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private DocumentAdapter getUserPromptDocumentAdapter() {
|
||||
return new DocumentAdapter() {
|
||||
@Override
|
||||
protected void textChanged(@NotNull DocumentEvent event) {
|
||||
try {
|
||||
if (!streaming) {
|
||||
var document = event.getDocument();
|
||||
var text = document.getText(
|
||||
document.getStartPosition().getOffset(),
|
||||
document.getEndPosition().getOffset() - 1);
|
||||
totalTokensPanel.updateUserPromptTokens(text);
|
||||
}
|
||||
} catch (BadLocationException ex) {
|
||||
LOG.error("Something went wrong while processing user input tokens", ex);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,91 @@
|
|||
package ee.carlrobert.codegpt.toolwindow.chat;
|
||||
|
||||
import static ee.carlrobert.codegpt.util.ThemeUtils.getPanelBackgroundColor;
|
||||
|
||||
import com.intellij.openapi.roots.ui.componentsList.components.ScrollablePanel;
|
||||
import com.intellij.openapi.roots.ui.componentsList.layout.VerticalStackLayout;
|
||||
import ee.carlrobert.codegpt.completions.you.YouUserManager;
|
||||
import ee.carlrobert.codegpt.settings.service.ServiceType;
|
||||
import ee.carlrobert.codegpt.settings.state.SettingsState;
|
||||
import ee.carlrobert.codegpt.toolwindow.chat.components.ResponsePanel;
|
||||
import ee.carlrobert.codegpt.util.SwingUtils;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JTextPane;
|
||||
|
||||
public class ChatToolWindowScrollablePanel extends ScrollablePanel {
|
||||
|
||||
private final SettingsState settings;
|
||||
private final YouUserManager youUserManager;
|
||||
private final Map<UUID, JPanel> visibleMessagePanels;
|
||||
|
||||
ChatToolWindowScrollablePanel(SettingsState settings) {
|
||||
super(new VerticalStackLayout());
|
||||
this.settings = settings;
|
||||
this.youUserManager = YouUserManager.getInstance();
|
||||
this.visibleMessagePanels = new HashMap<>();
|
||||
}
|
||||
|
||||
public void displayLandingView(JComponent landingView) {
|
||||
removeAll();
|
||||
add(landingView);
|
||||
if (settings.getSelectedService() == ServiceType.YOU &&
|
||||
(!youUserManager.isAuthenticated() || !youUserManager.isSubscribed())) {
|
||||
add(new ResponsePanel().addContent(createYouCouponTextPane()));
|
||||
}
|
||||
update();
|
||||
}
|
||||
|
||||
public ResponsePanel getMessageResponsePanel(UUID messageId) {
|
||||
return (ResponsePanel) Arrays.stream(visibleMessagePanels.get(messageId).getComponents())
|
||||
.filter(component -> component instanceof ResponsePanel)
|
||||
.findFirst().orElseThrow();
|
||||
}
|
||||
|
||||
public JPanel addMessage(UUID messageId) {
|
||||
var messageWrapper = new JPanel();
|
||||
messageWrapper.setLayout(new BoxLayout(messageWrapper, BoxLayout.PAGE_AXIS));
|
||||
add(messageWrapper);
|
||||
visibleMessagePanels.put(messageId, messageWrapper);
|
||||
return messageWrapper;
|
||||
}
|
||||
|
||||
public void removeMessage(UUID messageId) {
|
||||
remove(visibleMessagePanels.get(messageId));
|
||||
update();
|
||||
visibleMessagePanels.remove(messageId);
|
||||
}
|
||||
|
||||
public void clearAll() {
|
||||
visibleMessagePanels.clear();
|
||||
removeAll();
|
||||
update();
|
||||
}
|
||||
|
||||
public void update() {
|
||||
repaint();
|
||||
revalidate();
|
||||
}
|
||||
|
||||
// TODO: Move
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -12,6 +12,10 @@ public interface ChatToolWindowTabPanel extends Disposable {
|
|||
|
||||
@Nullable Conversation getConversation();
|
||||
|
||||
TokenDetails getTokenDetails();
|
||||
|
||||
boolean isStreaming();
|
||||
|
||||
void setConversation(@Nullable Conversation conversation);
|
||||
|
||||
void displayLandingView();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,49 @@
|
|||
package ee.carlrobert.codegpt.toolwindow.chat;
|
||||
|
||||
import ee.carlrobert.codegpt.EncodingManager;
|
||||
import ee.carlrobert.codegpt.settings.configuration.ConfigurationState;
|
||||
|
||||
public class TokenDetails {
|
||||
|
||||
private final int systemPromptTokens;
|
||||
private int conversationTokens;
|
||||
private int userPromptTokens;
|
||||
private int highlightedTokens;
|
||||
|
||||
public TokenDetails(EncodingManager encodingManager) {
|
||||
systemPromptTokens = encodingManager.countTokens(
|
||||
ConfigurationState.getInstance().getSystemPrompt());
|
||||
}
|
||||
|
||||
public int getSystemPromptTokens() {
|
||||
return systemPromptTokens;
|
||||
}
|
||||
|
||||
public void setConversationTokens(int conversationTokens) {
|
||||
this.conversationTokens = conversationTokens;
|
||||
}
|
||||
|
||||
public int getConversationTokens() {
|
||||
return conversationTokens;
|
||||
}
|
||||
|
||||
public void setUserPromptTokens(int userPromptTokens) {
|
||||
this.userPromptTokens = userPromptTokens;
|
||||
}
|
||||
|
||||
public int getUserPromptTokens() {
|
||||
return userPromptTokens;
|
||||
}
|
||||
|
||||
public void setHighlightedTokens(int highlightedTokens) {
|
||||
this.highlightedTokens = highlightedTokens;
|
||||
}
|
||||
|
||||
public int getHighlightedTokens() {
|
||||
return highlightedTokens;
|
||||
}
|
||||
|
||||
public int getTotal() {
|
||||
return systemPromptTokens + conversationTokens + userPromptTokens + highlightedTokens;
|
||||
}
|
||||
}
|
||||
|
|
@ -142,13 +142,17 @@ public class ChatMessageResponseBody extends JPanel {
|
|||
}
|
||||
|
||||
public void hideCarets() {
|
||||
if (currentlyProcessedEditor != null) {
|
||||
((EditorEx) currentlyProcessedEditor.getEditor()).setCaretVisible(false);
|
||||
((EditorEx) currentlyProcessedEditor.getEditor()).setCaretEnabled(false);
|
||||
}
|
||||
if (currentlyProcessedTextPane != null && currentlyProcessedTextPane.getCaret().isVisible()) {
|
||||
currentlyProcessedTextPane.getCaret().setVisible(false);
|
||||
}
|
||||
ApplicationManager.getApplication().invokeLater(() ->
|
||||
ApplicationManager.getApplication().runWriteAction(() -> {
|
||||
if (currentlyProcessedEditor != null) {
|
||||
((EditorEx) currentlyProcessedEditor.getEditor()).setCaretVisible(false);
|
||||
((EditorEx) currentlyProcessedEditor.getEditor()).setCaretEnabled(false);
|
||||
}
|
||||
if (currentlyProcessedTextPane != null && currentlyProcessedTextPane.getCaret()
|
||||
.isVisible()) {
|
||||
currentlyProcessedTextPane.getCaret().setVisible(false);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
public void displayError(String message) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,105 @@
|
|||
package ee.carlrobert.codegpt.toolwindow.chat.components;
|
||||
|
||||
import static java.lang.String.format;
|
||||
|
||||
import com.intellij.icons.AllIcons.General;
|
||||
import com.intellij.ui.components.JBLabel;
|
||||
import ee.carlrobert.codegpt.EncodingManager;
|
||||
import ee.carlrobert.codegpt.conversations.Conversation;
|
||||
import ee.carlrobert.codegpt.toolwindow.chat.TokenDetails;
|
||||
import java.awt.FlowLayout;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.swing.Box;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.SwingUtilities;
|
||||
|
||||
public class TotalTokensPanel extends JPanel {
|
||||
|
||||
private final EncodingManager encodingManager;
|
||||
private final TokenDetails tokenDetails;
|
||||
private final JBLabel label;
|
||||
|
||||
public TotalTokensPanel(Conversation conversation, String userPrompt, String highlightedText) {
|
||||
super(new FlowLayout(FlowLayout.LEADING, 0, 0));
|
||||
this.encodingManager = EncodingManager.getInstance();
|
||||
this.tokenDetails = createTokenDetails(conversation, userPrompt, highlightedText);
|
||||
this.label = getLabel(tokenDetails);
|
||||
|
||||
setOpaque(false);
|
||||
add(getContextHelpIcon(tokenDetails));
|
||||
add(Box.createHorizontalStrut(4));
|
||||
add(label);
|
||||
}
|
||||
|
||||
public TokenDetails getTokenDetails() {
|
||||
return tokenDetails;
|
||||
}
|
||||
|
||||
public void update() {
|
||||
update(tokenDetails.getTotal());
|
||||
}
|
||||
|
||||
public void update(int total) {
|
||||
label.setText(getLabelHtml(total));
|
||||
}
|
||||
|
||||
public void updateConversationTokens(Conversation conversation) {
|
||||
tokenDetails.setConversationTokens(encodingManager.countConversationTokens(conversation));
|
||||
update();
|
||||
}
|
||||
|
||||
public void updateUserPromptTokens(String userPrompt) {
|
||||
tokenDetails.setUserPromptTokens(encodingManager.countTokens(userPrompt));
|
||||
update();
|
||||
}
|
||||
|
||||
public void updateHighlightedTokens(String highlightedText) {
|
||||
tokenDetails.setHighlightedTokens(encodingManager.countTokens(highlightedText));
|
||||
update();
|
||||
}
|
||||
|
||||
private TokenDetails createTokenDetails(
|
||||
Conversation conversation,
|
||||
String userPrompt,
|
||||
String highlightedText) {
|
||||
var tokenDetails = new TokenDetails(encodingManager);
|
||||
tokenDetails.setConversationTokens(encodingManager.countConversationTokens(conversation));
|
||||
tokenDetails.setUserPromptTokens(encodingManager.countTokens(userPrompt));
|
||||
tokenDetails.setHighlightedTokens(encodingManager.countTokens(highlightedText));
|
||||
return tokenDetails;
|
||||
}
|
||||
|
||||
private JBLabel getContextHelpIcon(TokenDetails tokenDetails) {
|
||||
var iconLabel = new JBLabel(General.ContextHelp);
|
||||
iconLabel.addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseEntered(MouseEvent e) {
|
||||
var html = new LinkedHashMap<>(Map.of(
|
||||
"System Prompt", tokenDetails.getSystemPromptTokens(),
|
||||
"Conversation Tokens", tokenDetails.getConversationTokens(),
|
||||
"Input Tokens", tokenDetails.getUserPromptTokens(),
|
||||
"Highlighted Tokens", tokenDetails.getHighlightedTokens()))
|
||||
.entrySet().stream()
|
||||
.map(entry -> format(
|
||||
"<p style=\"margin: 0;\"><small>%s: <strong>%d</strong></small></p>",
|
||||
entry.getKey(),
|
||||
entry.getValue()))
|
||||
.collect(Collectors.joining());
|
||||
iconLabel.setToolTipText("<html>" + html + "</html>");
|
||||
}
|
||||
});
|
||||
return iconLabel;
|
||||
}
|
||||
|
||||
private String getLabelHtml(int total) {
|
||||
return format("<html><small>Total Tokens: <strong>%d</strong></small></html>", total);
|
||||
}
|
||||
|
||||
private JBLabel getLabel(TokenDetails tokenDetails) {
|
||||
return new JBLabel(getLabelHtml(tokenDetails.getTotal()));
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ package ee.carlrobert.codegpt.toolwindow.chat.components;
|
|||
import com.intellij.icons.AllIcons;
|
||||
import com.intellij.openapi.editor.ex.util.EditorUtil;
|
||||
import com.intellij.openapi.util.registry.Registry;
|
||||
import com.intellij.ui.DocumentAdapter;
|
||||
import com.intellij.ui.JBColor;
|
||||
import com.intellij.ui.components.JBTextArea;
|
||||
import com.intellij.util.ui.JBUI;
|
||||
|
|
@ -30,7 +31,6 @@ import javax.swing.JPanel;
|
|||
import javax.swing.KeyStroke;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
|
|
@ -38,7 +38,8 @@ public class UserPromptTextArea extends JPanel {
|
|||
|
||||
private static final String TEXT_SUBMIT = "text-submit";
|
||||
private static final String INSERT_BREAK = "insert-break";
|
||||
private static final JBColor BACKGROUND_COLOR = JBColor.namedColor("Editor.SearchField.background", UIUtil.getTextFieldBackground());
|
||||
private static final JBColor BACKGROUND_COLOR = JBColor.namedColor(
|
||||
"Editor.SearchField.background", UIUtil.getTextFieldBackground());
|
||||
|
||||
private final JBTextArea textArea;
|
||||
|
||||
|
|
@ -48,10 +49,11 @@ public class UserPromptTextArea extends JPanel {
|
|||
private JPanel iconsPanel;
|
||||
private boolean submitEnabled = true;
|
||||
|
||||
public UserPromptTextArea(Consumer<String> onSubmit) {
|
||||
public UserPromptTextArea(Consumer<String> onSubmit, DocumentAdapter documentAdapter) {
|
||||
this.onSubmit = onSubmit;
|
||||
|
||||
textArea = new JBTextArea();
|
||||
textArea.getDocument().addDocumentListener(documentAdapter);
|
||||
textArea.setOpaque(false);
|
||||
textArea.setBackground(BACKGROUND_COLOR);
|
||||
textArea.setLineWrap(true);
|
||||
|
|
@ -78,29 +80,20 @@ public class UserPromptTextArea extends JPanel {
|
|||
UserPromptTextArea.super.paintBorder(UserPromptTextArea.super.getGraphics());
|
||||
}
|
||||
});
|
||||
textArea.getDocument().addDocumentListener(new DocumentListener() {
|
||||
textArea.getDocument().addDocumentListener(new DocumentAdapter() {
|
||||
@Override
|
||||
public void removeUpdate(DocumentEvent e) {
|
||||
if (e.getDocument().getLength() == 0) {
|
||||
iconsPanel.getComponents()[0].setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertUpdate(DocumentEvent e) {
|
||||
if (e.getDocument().getLength() == 1) {
|
||||
iconsPanel.getComponents()[0].setEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changedUpdate(DocumentEvent e) {
|
||||
protected void textChanged(@NotNull DocumentEvent e) {
|
||||
iconsPanel.getComponents()[0].setEnabled(e.getDocument().getLength() > 0);
|
||||
}
|
||||
});
|
||||
updateFont();
|
||||
init();
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return textArea.getText().trim();
|
||||
}
|
||||
|
||||
public void focus() {
|
||||
textArea.requestFocus();
|
||||
textArea.requestFocusInWindow();
|
||||
|
|
@ -136,10 +129,6 @@ public class UserPromptTextArea extends JPanel {
|
|||
stopButton.setEnabled(!submitEnabled);
|
||||
}
|
||||
|
||||
public void setTextAreaEnabled(boolean textAreaEnabled) {
|
||||
textArea.setEnabled(textAreaEnabled);
|
||||
}
|
||||
|
||||
private void handleSubmit() {
|
||||
if (submitEnabled && !textArea.getText().isEmpty()) {
|
||||
// Replacing each newline with two newlines to ensure proper Markdown formatting
|
||||
|
|
|
|||
|
|
@ -0,0 +1,77 @@
|
|||
package ee.carlrobert.codegpt.toolwindow.chat.components;
|
||||
|
||||
import static ee.carlrobert.codegpt.util.ThemeUtils.getPanelBackgroundColor;
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.ui.components.JBCheckBox;
|
||||
import com.intellij.util.messages.MessageBusConnection;
|
||||
import com.intellij.util.ui.JBUI;
|
||||
import com.intellij.util.ui.JBUI.Borders;
|
||||
import ee.carlrobert.codegpt.completions.you.YouSubscriptionNotifier;
|
||||
import ee.carlrobert.codegpt.completions.you.auth.SignedOutNotifier;
|
||||
import ee.carlrobert.codegpt.settings.state.SettingsState;
|
||||
import ee.carlrobert.codegpt.toolwindow.ModelIconLabel;
|
||||
import ee.carlrobert.codegpt.toolwindow.chat.YouModelChangeNotifier;
|
||||
import java.awt.BorderLayout;
|
||||
import javax.swing.JPanel;
|
||||
|
||||
public class UserPromptTextAreaHeader extends JPanel {
|
||||
|
||||
public UserPromptTextAreaHeader(
|
||||
Project project,
|
||||
SettingsState settings,
|
||||
TotalTokensPanel totalTokensPanel,
|
||||
JBCheckBox gpt4CheckBox) {
|
||||
super(new BorderLayout());
|
||||
setBackground(getPanelBackgroundColor());
|
||||
setBorder(JBUI.Borders.emptyBottom(8));
|
||||
switch (settings.getSelectedService()) {
|
||||
case OPENAI:
|
||||
case AZURE:
|
||||
add(totalTokensPanel, BorderLayout.LINE_START);
|
||||
break;
|
||||
case YOU:
|
||||
subscribeToYouTopics(project, gpt4CheckBox);
|
||||
add(gpt4CheckBox, BorderLayout.LINE_START);
|
||||
break;
|
||||
}
|
||||
add(JBUI.Panels
|
||||
.simplePanel(new ModelIconLabel(
|
||||
settings.getSelectedService().getCompletionCode(),
|
||||
settings.getModel()))
|
||||
.withBorder(Borders.emptyRight(4))
|
||||
.withBackground(getPanelBackgroundColor()), BorderLayout.LINE_END);
|
||||
}
|
||||
|
||||
private void subscribeToYouTopics(Project project, JBCheckBox gpt4CheckBox) {
|
||||
var messageBusConnection = ApplicationManager.getApplication().getMessageBus().connect();
|
||||
subscribeToYouModelChangeTopic(project, gpt4CheckBox);
|
||||
subscribeToYouSubscriptionTopic(messageBusConnection, gpt4CheckBox);
|
||||
subscribeToSignedOutTopic(messageBusConnection, gpt4CheckBox);
|
||||
}
|
||||
|
||||
private void subscribeToYouModelChangeTopic(Project project, JBCheckBox gpt4CheckBox) {
|
||||
project.getMessageBus()
|
||||
.connect()
|
||||
.subscribe(
|
||||
YouModelChangeNotifier.YOU_MODEL_CHANGE_NOTIFIER_TOPIC,
|
||||
(YouModelChangeNotifier) gpt4CheckBox::setSelected);
|
||||
}
|
||||
|
||||
private void subscribeToSignedOutTopic(
|
||||
MessageBusConnection messageBusConnection,
|
||||
JBCheckBox gpt4CheckBox) {
|
||||
messageBusConnection.subscribe(
|
||||
SignedOutNotifier.SIGNED_OUT_TOPIC,
|
||||
(SignedOutNotifier) () -> gpt4CheckBox.setEnabled(false));
|
||||
}
|
||||
|
||||
private void subscribeToYouSubscriptionTopic(
|
||||
MessageBusConnection messageBusConnection,
|
||||
JBCheckBox gpt4CheckBox) {
|
||||
messageBusConnection.subscribe(
|
||||
YouSubscriptionNotifier.SUBSCRIPTION_TOPIC,
|
||||
(YouSubscriptionNotifier) () -> gpt4CheckBox.setEnabled(true));
|
||||
}
|
||||
}
|
||||
|
|
@ -32,6 +32,7 @@ public class StandardChatToolWindowTabPanel extends BaseChatToolWindowTabPanel {
|
|||
displayLandingView();
|
||||
} else {
|
||||
displayConversation(conversation);
|
||||
totalTokensPanel.updateConversationTokens(conversation);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -75,11 +76,11 @@ public class StandardChatToolWindowTabPanel extends BaseChatToolWindowTabPanel {
|
|||
messageResponseBody.displaySerpResults(serpResults);
|
||||
}
|
||||
|
||||
var messageWrapper = createNewMessageWrapper(message.getId());
|
||||
messageWrapper.add(new UserMessagePanel(project, message, this));
|
||||
messageWrapper.add(new ResponsePanel()
|
||||
var messagePanel = toolWindowScrollablePanel.addMessage(message.getId());
|
||||
messagePanel.add(new UserMessagePanel(project, message, this));
|
||||
messagePanel.add(new ResponsePanel()
|
||||
.withReloadAction(() -> reloadMessage(message, conversation))
|
||||
.withDeleteAction(() -> removeMessage(message.getId(), messageWrapper, conversation))
|
||||
.withDeleteAction(() -> removeMessage(message.getId(), conversation))
|
||||
.addContent(messageResponseBody));
|
||||
});
|
||||
setConversation(conversation);
|
||||
|
|
|
|||
|
|
@ -112,6 +112,7 @@ public class StandardChatToolWindowTabbedPane extends JBTabbedPane {
|
|||
tryFindActiveTabPanel().ifPresent(tabPanel -> {
|
||||
tabPanel.displayLandingView();
|
||||
tabPanel.setConversation(null);
|
||||
tabPanel.updateConversationTokens();
|
||||
});
|
||||
ConversationsState.getInstance().setCurrentConversation(null);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,6 +52,15 @@ public final class EditorUtils {
|
|||
return editorManager != null ? editorManager.getSelectedTextEditor() : null;
|
||||
}
|
||||
|
||||
public static @NotNull String getSelectedEditorSelectedText(@NotNull Project project) {
|
||||
var selectedEditor = EditorUtils.getSelectedEditor(project);
|
||||
var selectedText = "";
|
||||
if (selectedEditor != null) {
|
||||
selectedText = selectedEditor.getSelectionModel().getSelectedText();
|
||||
}
|
||||
return selectedText == null ? "" : selectedText;
|
||||
}
|
||||
|
||||
public static boolean isMainEditorTextSelected(@NotNull Project project) {
|
||||
return hasSelection(getSelectedEditor(project));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,10 @@ package ee.carlrobert.codegpt.util;
|
|||
import static javax.swing.event.HyperlinkEvent.EventType.ACTIVATED;
|
||||
|
||||
import com.intellij.ide.BrowserUtil;
|
||||
import com.intellij.openapi.roots.ui.componentsList.components.ScrollablePanel;
|
||||
import com.intellij.ui.ScrollPaneFactory;
|
||||
import com.intellij.util.ui.UI;
|
||||
import ee.carlrobert.codegpt.toolwindow.chat.components.SmartScroller;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.net.URISyntaxException;
|
||||
|
|
@ -14,9 +17,11 @@ import javax.swing.JButton;
|
|||
import javax.swing.JComponent;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTextArea;
|
||||
import javax.swing.JTextPane;
|
||||
import javax.swing.KeyStroke;
|
||||
import javax.swing.ScrollPaneConstants;
|
||||
import javax.swing.event.HyperlinkEvent;
|
||||
import javax.swing.event.HyperlinkListener;
|
||||
|
||||
|
|
@ -44,6 +49,13 @@ public class SwingUtils {
|
|||
return button;
|
||||
}
|
||||
|
||||
public static JScrollPane createScrollPaneWithSmartScroller(ScrollablePanel scrollablePanel) {
|
||||
var scrollPane = ScrollPaneFactory.createScrollPane(scrollablePanel, true);
|
||||
scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
|
||||
new SmartScroller(scrollPane);
|
||||
return scrollPane;
|
||||
}
|
||||
|
||||
public static void setEqualLabelWidths(JPanel firstPanel, JPanel secondPanel) {
|
||||
var firstLabel = firstPanel.getComponents()[0];
|
||||
var secondLabel = secondPanel.getComponents()[0];
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue