From 9067ccfa7224e2d88e57b0a87afa9d76a21fcab4 Mon Sep 17 00:00:00 2001 From: Patrick Hemmer Date: Mon, 20 Oct 2025 12:05:41 -0400 Subject: [PATCH] scope conversations to the project Previously conversations were shared among all projects. However even this wasn't working correctly. For example, if you had an conversation in one project, but then opened another project, the conversation would be visible in both. But if you added messages to the conversation, it wouldn't update in the other project. Also sometimes you could open a project, and the conversations weren't there at all. And then if you closed that project, it would nuke the conversations in other projects. This addresses the issues by making all conversations scoped to the project. --- .../actions/editor/OpenNewChatAction.java | 2 +- .../DeleteAllConversationsAction.java | 4 +- .../toolwindow/DeleteConversationAction.java | 8 +++- .../actions/toolwindow/MoveAction.java | 9 +++- .../actions/toolwindow/MoveDownAction.java | 2 +- .../actions/toolwindow/MoveUpAction.java | 2 +- .../toolwindow/OpenInEditorAction.java | 15 +++++-- .../conversations/ConversationService.java | 30 +++++++------ .../conversations/ConversationsState.java | 17 ++++---- .../chat/ChatToolWindowContentManager.java | 8 ++-- .../toolwindow/chat/ChatToolWindowPanel.java | 10 ++--- .../chat/ChatToolWindowTabPanel.java | 3 +- .../chat/ChatToolWindowTabbedPane.java | 8 ++-- ...WindowCompletionResponseEventListener.java | 6 +-- .../ee/carlrobert/codegpt/ui/OverlayUtil.java | 4 +- .../factory/OpenAIRequestFactory.kt | 16 +++++-- .../codegpt/inlineedit/InlineEditInlay.kt | 7 ++-- .../InlineEditSearchReplaceListener.kt | 4 +- .../history/ChatHistoryToolWindow.kt | 7 ++-- .../codegpt/toolwindow/ui/UserMessagePanel.kt | 2 +- .../codegpt/ui/textarea/SearchManager.kt | 4 +- .../ui/textarea/TagProcessorFactory.kt | 9 ++-- .../textarea/lookup/group/HistoryGroupItem.kt | 7 ++-- src/main/resources/META-INF/plugin.xml | 5 ++- .../CompletionRequestProviderTest.kt | 4 +- ...lwindowChatCompletionRequestHandlerTest.kt | 10 ++--- .../OpenAIRequestFactoryIntegrationTest.kt | 8 ++-- .../conversations/ConversationsStateTest.kt | 42 +++++++++---------- .../chat/ChatToolWindowTabPanelTest.kt | 10 ++--- .../chat/ChatToolWindowTabbedPaneTest.kt | 10 ++--- .../textarea/ConversationTagProcessorTest.kt | 20 ++++----- .../textarea/HistorySearchIntegrationTest.kt | 34 +++++++-------- .../ui/textarea/HistoryTagIntegrationTest.kt | 18 ++++---- 33 files changed, 190 insertions(+), 155 deletions(-) diff --git a/src/main/java/ee/carlrobert/codegpt/actions/editor/OpenNewChatAction.java b/src/main/java/ee/carlrobert/codegpt/actions/editor/OpenNewChatAction.java index dd4882e3..afd0fe07 100644 --- a/src/main/java/ee/carlrobert/codegpt/actions/editor/OpenNewChatAction.java +++ b/src/main/java/ee/carlrobert/codegpt/actions/editor/OpenNewChatAction.java @@ -23,7 +23,7 @@ public void update(@NotNull AnActionEvent event) { public void actionPerformed(@NotNull AnActionEvent event) { var project = event.getProject(); if (project != null) { - ConversationsState.getInstance().setCurrentConversation(null); + ConversationsState.getInstance(project).setCurrentConversation(null); var tabPanel = project.getService(ChatToolWindowContentManager.class).createNewTabPanel(); if (tabPanel != null) { diff --git a/src/main/java/ee/carlrobert/codegpt/actions/toolwindow/DeleteAllConversationsAction.java b/src/main/java/ee/carlrobert/codegpt/actions/toolwindow/DeleteAllConversationsAction.java index 6490853a..e00a43f2 100644 --- a/src/main/java/ee/carlrobert/codegpt/actions/toolwindow/DeleteAllConversationsAction.java +++ b/src/main/java/ee/carlrobert/codegpt/actions/toolwindow/DeleteAllConversationsAction.java @@ -28,7 +28,7 @@ public DeleteAllConversationsAction(Runnable onRefresh) { public void update(@NotNull AnActionEvent event) { var project = event.getProject(); if (project != null) { - var sortedConversations = ConversationService.getInstance().getSortedConversations(); + var sortedConversations = ConversationService.getInstance(project).getSortedConversations(); event.getPresentation().setEnabled(!sortedConversations.isEmpty()); } } @@ -43,7 +43,7 @@ public void actionPerformed(@NotNull AnActionEvent event) { var project = event.getProject(); if (project != null) { try { - ConversationService.getInstance().clearAll(); + ConversationService.getInstance(project).clearAll(); project.getService(ChatToolWindowContentManager.class).resetAll(); } finally { TelemetryAction.IDE_ACTION.createActionMessage() diff --git a/src/main/java/ee/carlrobert/codegpt/actions/toolwindow/DeleteConversationAction.java b/src/main/java/ee/carlrobert/codegpt/actions/toolwindow/DeleteConversationAction.java index d56d3b59..81fb8211 100644 --- a/src/main/java/ee/carlrobert/codegpt/actions/toolwindow/DeleteConversationAction.java +++ b/src/main/java/ee/carlrobert/codegpt/actions/toolwindow/DeleteConversationAction.java @@ -4,6 +4,7 @@ import com.intellij.openapi.actionSystem.ActionUpdateThread; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; import ee.carlrobert.codegpt.actions.ActionType; import ee.carlrobert.codegpt.actions.editor.EditorActionsUtil; @@ -24,7 +25,12 @@ public DeleteConversationAction(Runnable onDelete) { @Override public void update(@NotNull AnActionEvent event) { - event.getPresentation().setEnabled(ConversationsState.getCurrentConversation() != null); + Project project = event.getProject(); + if (project == null) { + event.getPresentation().setEnabled(false); + return; + } + event.getPresentation().setEnabled(ConversationsState.getInstance(project).getCurrentConversation() != null); } @Override diff --git a/src/main/java/ee/carlrobert/codegpt/actions/toolwindow/MoveAction.java b/src/main/java/ee/carlrobert/codegpt/actions/toolwindow/MoveAction.java index ab3ba200..b690faaf 100644 --- a/src/main/java/ee/carlrobert/codegpt/actions/toolwindow/MoveAction.java +++ b/src/main/java/ee/carlrobert/codegpt/actions/toolwindow/MoveAction.java @@ -23,7 +23,12 @@ protected MoveAction(String text, String description, Icon icon, Runnable onRefr @Override public void update(@NotNull AnActionEvent event) { - event.getPresentation().setEnabled(ConversationsState.getCurrentConversation() != null); + Project project = event.getProject(); + if (project == null) { + event.getPresentation().setEnabled(false); + return; + } + event.getPresentation().setEnabled(ConversationsState.getInstance(project).getCurrentConversation() != null); } @Override @@ -32,7 +37,7 @@ public void actionPerformed(@NotNull AnActionEvent event) { if (project != null) { getConversation(project) .ifPresent(conversation -> { - ConversationsState.getInstance().setCurrentConversation(conversation); + ConversationsState.getInstance(project).setCurrentConversation(conversation); onRefresh.run(); }); } diff --git a/src/main/java/ee/carlrobert/codegpt/actions/toolwindow/MoveDownAction.java b/src/main/java/ee/carlrobert/codegpt/actions/toolwindow/MoveDownAction.java index 9784cac8..8c25c31e 100644 --- a/src/main/java/ee/carlrobert/codegpt/actions/toolwindow/MoveDownAction.java +++ b/src/main/java/ee/carlrobert/codegpt/actions/toolwindow/MoveDownAction.java @@ -17,6 +17,6 @@ public MoveDownAction(Runnable onRefresh) { @Override protected Optional getConversation(@NotNull Project project) { - return ConversationService.getInstance().getPreviousConversation(); + return ConversationService.getInstance(project).getPreviousConversation(); } } diff --git a/src/main/java/ee/carlrobert/codegpt/actions/toolwindow/MoveUpAction.java b/src/main/java/ee/carlrobert/codegpt/actions/toolwindow/MoveUpAction.java index 1af04037..294b1963 100644 --- a/src/main/java/ee/carlrobert/codegpt/actions/toolwindow/MoveUpAction.java +++ b/src/main/java/ee/carlrobert/codegpt/actions/toolwindow/MoveUpAction.java @@ -17,6 +17,6 @@ public MoveUpAction(Runnable onRefresh) { @Override protected Optional getConversation(@NotNull Project project) { - return ConversationService.getInstance().getNextConversation(); + return ConversationService.getInstance(project).getNextConversation(); } } diff --git a/src/main/java/ee/carlrobert/codegpt/actions/toolwindow/OpenInEditorAction.java b/src/main/java/ee/carlrobert/codegpt/actions/toolwindow/OpenInEditorAction.java index 904ba6f2..1e12d23a 100644 --- a/src/main/java/ee/carlrobert/codegpt/actions/toolwindow/OpenInEditorAction.java +++ b/src/main/java/ee/carlrobert/codegpt/actions/toolwindow/OpenInEditorAction.java @@ -8,6 +8,7 @@ import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.fileEditor.FileEditorManager; +import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.wm.ToolWindowManager; import com.intellij.testFramework.LightVirtualFile; @@ -29,7 +30,13 @@ public OpenInEditorAction() { @Override public void update(@NotNull AnActionEvent event) { super.update(event); - var currentConversation = ConversationsState.getCurrentConversation(); + Project project = event.getProject(); + if (project == null) { + event.getPresentation().setEnabled(false); + return; + } + + var currentConversation = ConversationsState.getInstance(project).getCurrentConversation(); var isEnabled = currentConversation != null && !currentConversation.getMessages().isEmpty(); event.getPresentation().setEnabled(isEnabled); } @@ -38,8 +45,9 @@ public void update(@NotNull AnActionEvent event) { public void actionPerformed(@NotNull AnActionEvent e) { try { var project = e.getProject(); - var currentConversation = ConversationsState.getCurrentConversation(); - if (project != null && currentConversation != null) { + if (project != null) { + var currentConversation = ConversationsState.getInstance(project).getCurrentConversation(); + if (currentConversation != null) { var dateTimeStamp = currentConversation.getUpdatedOn() .format(DateTimeFormatter.ofPattern("yyyyMMddHHmm")); var fileName = format("proxyai_conversation_%s.md", dateTimeStamp); @@ -55,6 +63,7 @@ public void actionPerformed(@NotNull AnActionEvent e) { ToolWindowManager.getInstance(project).getToolWindow("ProxyAI")); toolWindow.hide(); } + } } finally { TelemetryAction.IDE_ACTION.createActionMessage() .property("action", ActionType.OPEN_CONVERSATION_IN_EDITOR.name()) diff --git a/src/main/java/ee/carlrobert/codegpt/conversations/ConversationService.java b/src/main/java/ee/carlrobert/codegpt/conversations/ConversationService.java index 22e094d3..4d248098 100644 --- a/src/main/java/ee/carlrobert/codegpt/conversations/ConversationService.java +++ b/src/main/java/ee/carlrobert/codegpt/conversations/ConversationService.java @@ -1,6 +1,5 @@ package ee.carlrobert.codegpt.conversations; -import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.components.Service; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; @@ -12,19 +11,23 @@ import java.util.Optional; import java.util.UUID; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; -@Service +@Service(Service.Level.PROJECT) public final class ConversationService { private static final Logger LOG = Logger.getInstance(ConversationService.class); - private final ConversationsState conversationState = ConversationsState.getInstance(); + private final Project project; + private final ConversationsState conversationState; - private ConversationService() { + public ConversationService(Project project) { + this.project = project; + this.conversationState = ConversationsState.getInstance(project); } - public static ConversationService getInstance() { - return ApplicationManager.getApplication().getService(ConversationService.class); + public static ConversationService getInstance(@NotNull Project project) { + return project.getService(ConversationService.class); } public List getSortedConversations() { @@ -75,11 +78,8 @@ public void saveConversation(Conversation conversation) { conversationState.setCurrentConversation(conversation); } - public Conversation startConversation(Project project) { - return startConversation(project != null ? project.getBasePath() : null); - } - - private Conversation startConversation(String projectPath) { + public Conversation startConversation() { + var projectPath = project.getBasePath(); var conversation = createConversation(); conversation.setProjectPath(projectPath); conversationState.setCurrentConversation(conversation); @@ -102,7 +102,7 @@ public void deleteSelectedConversation() { nextConversation = getNextConversation(); } - var currentConversation = ConversationsState.getCurrentConversation(); + var currentConversation = getCurrentConversation(); if (currentConversation != null) { deleteConversation(currentConversation); nextConversation.ifPresent(conversationState::setCurrentConversation); @@ -116,6 +116,10 @@ public void discardTokenLimits(Conversation conversation) { saveConversation(conversation); } + public @Nullable Conversation getCurrentConversation() { + return conversationState.getCurrentConversation(); + } + public Optional getPreviousConversation() { return tryGetNextOrPreviousConversation(true); } @@ -125,7 +129,7 @@ public Optional getNextConversation() { } private Optional tryGetNextOrPreviousConversation(boolean isPrevious) { - var currentConversation = ConversationsState.getCurrentConversation(); + var currentConversation = getCurrentConversation(); if (currentConversation != null) { var sortedConversations = getSortedConversations(); for (int i = 0; i < sortedConversations.size(); i++) { diff --git a/src/main/java/ee/carlrobert/codegpt/conversations/ConversationsState.java b/src/main/java/ee/carlrobert/codegpt/conversations/ConversationsState.java index 50d545f4..bdbe2dba 100644 --- a/src/main/java/ee/carlrobert/codegpt/conversations/ConversationsState.java +++ b/src/main/java/ee/carlrobert/codegpt/conversations/ConversationsState.java @@ -1,9 +1,10 @@ package ee.carlrobert.codegpt.conversations; -import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.components.PersistentStateComponent; +import com.intellij.openapi.components.Service; import com.intellij.openapi.components.State; import com.intellij.openapi.components.Storage; +import com.intellij.openapi.project.Project; import com.intellij.util.xmlb.XmlSerializerUtil; import com.intellij.util.xmlb.annotations.OptionTag; import ee.carlrobert.codegpt.conversations.converter.ConversationConverter; @@ -16,9 +17,10 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +@Service(Service.Level.PROJECT) @State( - name = "ee.carlrobert.codegpt.state.conversations.ConversationsState", - storages = @Storage("ChatGPTConversations_170.xml")) + name = "ee.carlrobert.codegpt.conversations.ConversationsState", + storages = @Storage("proxyai_conversations.xml")) public class ConversationsState implements PersistentStateComponent { @Deprecated @@ -33,8 +35,8 @@ public class ConversationsState implements PersistentStateComponent { var panel = new ChatToolWindowTabPanel( project, - ConversationService.getInstance().startConversation(project)); + ConversationService.getInstance(project).startConversation()); item.addNewTab(panel); return panel; }) @@ -117,7 +117,7 @@ public void resetAll() { tabbedPane.clearAll(); tabbedPane.addNewTab(new ChatToolWindowTabPanel( project, - ConversationService.getInstance().startConversation(project))); + ConversationService.getInstance(project).startConversation())); }); } @@ -142,4 +142,4 @@ private Optional tryFindFirstChatTabContent() { public void clearAllTags() { tryFindActiveChatTabPanel().ifPresent(ChatToolWindowTabPanel::clearAllTags); } -} \ No newline at end of file +} diff --git a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/ChatToolWindowPanel.java b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/ChatToolWindowPanel.java index d1748915..840cdef5 100644 --- a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/ChatToolWindowPanel.java +++ b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/ChatToolWindowPanel.java @@ -62,7 +62,7 @@ public ChatToolWindowPanel( upgradePlanLink.setVisible(false); var tabPanel = new ChatToolWindowTabPanel(project, getConversation()); - tabbedPane = new ChatToolWindowTabbedPane(parentDisposable); + tabbedPane = new ChatToolWindowTabbedPane(project, parentDisposable); tabbedPane.addNewTab(tabPanel); initToolWindowPanel(project); @@ -72,9 +72,9 @@ public ChatToolWindowPanel( } private Conversation getConversation() { - var conversation = ConversationsState.getCurrentConversation(); + var conversation = ConversationsState.getInstance(project).getCurrentConversation(); if (conversation == null) { - return ConversationService.getInstance().startConversation(project); + return ConversationService.getInstance(project).startConversation(); } return conversation; } @@ -120,7 +120,7 @@ private void initToolWindowPanel(Project project) { Runnable onAddNewTab = () -> { tabbedPane.addNewTab(new ChatToolWindowTabPanel( project, - ConversationService.getInstance().startConversation(project))); + ConversationService.getInstance(project).startConversation())); repaint(); revalidate(); }; @@ -224,4 +224,4 @@ private PersonaPromptDetailsState getSelectedPersona() { .getSelectedPersona(); } } -} \ No newline at end of file +} diff --git a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/ChatToolWindowTabPanel.java b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/ChatToolWindowTabPanel.java index 91629b48..438d4384 100644 --- a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/ChatToolWindowTabPanel.java +++ b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/ChatToolWindowTabPanel.java @@ -89,7 +89,7 @@ public ChatToolWindowTabPanel(@NotNull Project project, @NotNull Conversation co this.project = project; this.conversation = conversation; this.chatSession = new ChatSession(); - conversationService = ConversationService.getInstance(); + conversationService = ConversationService.getInstance(project); toolWindowScrollablePanel = new ChatToolWindowScrollablePanel(); tagManager = new TagManager(); this.psiStructureRepository = new PsiStructureRepository( @@ -220,6 +220,7 @@ private List getHistory(List tags) { .map(it -> { if (it instanceof HistoryTagDetails tagDetails) { return ConversationTagProcessor.Companion.getConversation( + project, tagDetails.getConversationId()); } return null; diff --git a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/ChatToolWindowTabbedPane.java b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/ChatToolWindowTabbedPane.java index 1eb68901..c90a9a77 100644 --- a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/ChatToolWindowTabbedPane.java +++ b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/ChatToolWindowTabbedPane.java @@ -26,6 +26,7 @@ public class ChatToolWindowTabbedPane extends JBTabbedPane { + private final Project project; private final Map activeTabMapping = new TreeMap<>( (o1, o2) -> { String nums1 = o1.replaceAll("\\D", ""); @@ -48,7 +49,8 @@ public class ChatToolWindowTabbedPane extends JBTabbedPane { }); private final Disposable parentDisposable; - public ChatToolWindowTabbedPane(Disposable parentDisposable) { + public ChatToolWindowTabbedPane(Project project, Disposable parentDisposable) { + this.project = project; this.parentDisposable = parentDisposable; setTabComponentInsets(null); setComponentPopupMenu(new TabPopupMenu()); @@ -180,7 +182,7 @@ private void refreshTabState() { if (toolWindowPanel != null) { var conversation = toolWindowPanel.getConversation(); if (conversation != null) { - ConversationsState.getInstance().setCurrentConversation(conversation); + ConversationsState.getInstance(project).setCurrentConversation(conversation); } } } @@ -192,7 +194,7 @@ public void resetCurrentlyActiveTabPanel(Project project) { removeTabAt(getSelectedIndex()); addNewTab(new ChatToolWindowTabPanel( project, - ConversationService.getInstance().startConversation(project))); + ConversationService.getInstance(project).startConversation())); repaint(); revalidate(); }); diff --git a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/ToolWindowCompletionResponseEventListener.java b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/ToolWindowCompletionResponseEventListener.java index 981e939a..ecc5feb5 100644 --- a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/ToolWindowCompletionResponseEventListener.java +++ b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/ToolWindowCompletionResponseEventListener.java @@ -102,13 +102,13 @@ public void handleError(ErrorDetails error, Throwable ex) { @Override public void handleTokensExceeded(Conversation conversation, Message message) { ApplicationManager.getApplication().invokeLater(() -> { - var answer = OverlayUtil.showTokenLimitExceededDialog(); + var answer = OverlayUtil.showTokenLimitExceededDialog(project); if (answer == OK) { TelemetryAction.IDE_ACTION.createActionMessage() .property("action", "DISCARD_TOKEN_LIMIT") .send(); - ConversationService.getInstance().discardTokenLimits(conversation); + ConversationService.getInstance(project).discardTokenLimits(conversation); handleTokensExceededPolicyAccepted(); } else { stopStreaming(responseContainer); @@ -118,7 +118,7 @@ public void handleTokensExceeded(Conversation conversation, Message message) { @Override public void handleCompleted(String fullMessage, ChatCompletionParameters callParameters) { - ConversationService.getInstance().saveMessage(fullMessage, callParameters); + ConversationService.getInstance(project).saveMessage(fullMessage, callParameters); ApplicationManager.getApplication().invokeLater(() -> { try { diff --git a/src/main/java/ee/carlrobert/codegpt/ui/OverlayUtil.java b/src/main/java/ee/carlrobert/codegpt/ui/OverlayUtil.java index 03796e31..6e2ce234 100644 --- a/src/main/java/ee/carlrobert/codegpt/ui/OverlayUtil.java +++ b/src/main/java/ee/carlrobert/codegpt/ui/OverlayUtil.java @@ -105,7 +105,7 @@ public static int showDeleteConversationDialog() { Default); } - public static int showTokenLimitExceededDialog() { + public static int showTokenLimitExceededDialog(Project project) { return MessageDialogBuilder.okCancel( CodeGPTBundle.get("dialog.tokenLimitExceeded.title"), CodeGPTBundle.get("dialog.tokenLimitExceeded.description")) @@ -116,7 +116,7 @@ public static int showTokenLimitExceededDialog() { @Override public void rememberChoice(boolean isSelected, int exitCode) { if (isSelected) { - ConversationsState.getInstance().discardAllTokenLimits(); + ConversationsState.getInstance(project).discardAllTokenLimits(); } } diff --git a/src/main/kotlin/ee/carlrobert/codegpt/completions/factory/OpenAIRequestFactory.kt b/src/main/kotlin/ee/carlrobert/codegpt/completions/factory/OpenAIRequestFactory.kt index 5d07362a..440a5f0d 100644 --- a/src/main/kotlin/ee/carlrobert/codegpt/completions/factory/OpenAIRequestFactory.kt +++ b/src/main/kotlin/ee/carlrobert/codegpt/completions/factory/OpenAIRequestFactory.kt @@ -1,6 +1,7 @@ package ee.carlrobert.codegpt.completions.factory import com.intellij.openapi.components.service +import com.intellij.openapi.project.ProjectManager import com.intellij.openapi.vfs.readText import ee.carlrobert.codegpt.EncodingManager import ee.carlrobert.codegpt.ReferencedFile @@ -233,7 +234,7 @@ class OpenAIRequestFactory : BaseRequestFactory() { } return tryReducingMessagesOrThrow( messages, - callParameters.conversation.isDiscardTokenLimit, + callParameters.conversation, totalUsage, modelMaxTokens ) @@ -362,16 +363,23 @@ class OpenAIRequestFactory : BaseRequestFactory() { private fun tryReducingMessagesOrThrow( messages: MutableList, - discardTokenLimit: Boolean, + conversation: Conversation, totalInputUsage: Int, modelMaxTokens: Int ): List { val result: MutableList = messages.toMutableList() var totalUsage = totalInputUsage - if (!ConversationsState.getInstance().discardAllTokenLimits) { - if (!discardTokenLimit) { + val project = ProjectManager.getInstance().openProjects.find { it.basePath == conversation.projectPath } + if (project == null) { + if (!conversation.isDiscardTokenLimit) { throw TotalUsageExceededException() } + } else { + if (!ConversationsState.getInstance(project).discardAllTokenLimits) { + if (!conversation.isDiscardTokenLimit) { + throw TotalUsageExceededException() + } + } } val encodingManager = EncodingManager.getInstance() // skip the system prompt diff --git a/src/main/kotlin/ee/carlrobert/codegpt/inlineedit/InlineEditInlay.kt b/src/main/kotlin/ee/carlrobert/codegpt/inlineedit/InlineEditInlay.kt index 4fc34b80..c8978492 100644 --- a/src/main/kotlin/ee/carlrobert/codegpt/inlineedit/InlineEditInlay.kt +++ b/src/main/kotlin/ee/carlrobert/codegpt/inlineedit/InlineEditInlay.kt @@ -22,6 +22,7 @@ import ee.carlrobert.codegpt.CodeGPTKeys import ee.carlrobert.codegpt.ReferencedFile import ee.carlrobert.codegpt.actions.editor.EditorComponentInlaysManager import ee.carlrobert.codegpt.conversations.Conversation +import ee.carlrobert.codegpt.conversations.ConversationService import ee.carlrobert.codegpt.conversations.message.Message import ee.carlrobert.codegpt.psistructure.PsiStructureProvider import ee.carlrobert.codegpt.settings.service.FeatureType @@ -290,8 +291,8 @@ class InlineEditInlay(private var editor: Editor) : Disposable { projectPath = sessionConversation.projectPath setMessages(sessionConversation.messages) } - ee.carlrobert.codegpt.conversations.ConversationService.getInstance().addConversation(newConversation) - ee.carlrobert.codegpt.conversations.ConversationService.getInstance().saveConversation(newConversation) + ConversationService.getInstance(project).addConversation(newConversation) + ConversationService.getInstance(project).saveConversation(newConversation) openedChatConversation = newConversation project.service() .displayConversation(newConversation) @@ -526,7 +527,7 @@ class InlineEditInlay(private var editor: Editor) : Disposable { return tags .filter { it.selected && it is HistoryTagDetails } .map { (it as HistoryTagDetails).conversationId } - .mapNotNull { ConversationTagProcessor.getConversation(it) } + .mapNotNull { ConversationTagProcessor.getConversation(project, it) } .distinct() } diff --git a/src/main/kotlin/ee/carlrobert/codegpt/inlineedit/InlineEditSearchReplaceListener.kt b/src/main/kotlin/ee/carlrobert/codegpt/inlineedit/InlineEditSearchReplaceListener.kt index 3bb9b480..fa56f68e 100644 --- a/src/main/kotlin/ee/carlrobert/codegpt/inlineedit/InlineEditSearchReplaceListener.kt +++ b/src/main/kotlin/ee/carlrobert/codegpt/inlineedit/InlineEditSearchReplaceListener.kt @@ -472,8 +472,8 @@ class InlineEditSearchReplaceListener( newConversation.addMessage(copy) } - ConversationService.getInstance().addConversation(newConversation) - ConversationService.getInstance().saveConversation(newConversation) + ConversationService.getInstance(project).addConversation(newConversation) + ConversationService.getInstance(project).saveConversation(newConversation) project.service().displayConversation(newConversation) } diff --git a/src/main/kotlin/ee/carlrobert/codegpt/toolwindow/history/ChatHistoryToolWindow.kt b/src/main/kotlin/ee/carlrobert/codegpt/toolwindow/history/ChatHistoryToolWindow.kt index 8ce62f2b..39eb5e04 100644 --- a/src/main/kotlin/ee/carlrobert/codegpt/toolwindow/history/ChatHistoryToolWindow.kt +++ b/src/main/kotlin/ee/carlrobert/codegpt/toolwindow/history/ChatHistoryToolWindow.kt @@ -19,7 +19,6 @@ import ee.carlrobert.codegpt.CodeGPTBundle import ee.carlrobert.codegpt.actions.toolwindow.DeleteAllConversationsAction import ee.carlrobert.codegpt.conversations.Conversation import ee.carlrobert.codegpt.conversations.ConversationService -import ee.carlrobert.codegpt.conversations.ConversationsState import ee.carlrobert.codegpt.toolwindow.chat.ChatToolWindowContentManager import ee.carlrobert.codegpt.util.ProjectPathUtils import javax.swing.JOptionPane @@ -37,7 +36,7 @@ class ChatHistoryToolWindow(private val project: Project) : BorderLayoutPanel() private const val MAX_MESSAGES_TO_SEARCH = 5 } - private val conversationService = ConversationService.getInstance() + private val conversationService = project.getService(ConversationService::class.java) private val chatHistoryListPanel = ChatHistoryListPanel() private val searchField = SearchTextField() private var allConversations = mutableListOf() @@ -106,7 +105,7 @@ class ChatHistoryToolWindow(private val project: Project) : BorderLayoutPanel() private fun setupUI() { chatHistoryListPanel.apply { setOnConversationSelected { conversation -> - ConversationsState.getInstance().setCurrentConversation(conversation) + conversationService.saveConversation(conversation) } setOnConversationDoubleClicked { conversation -> @@ -580,7 +579,7 @@ class ChatHistoryToolWindow(private val project: Project) : BorderLayoutPanel() } private fun updateSelectedConversation(conversations: List) { - ConversationsState.getCurrentConversation()?.let { current -> + conversationService.currentConversation?.let { current -> conversations.find { it.id == current.id }?.let { conversation -> chatHistoryListPanel.setSelectedConversation(conversation) } diff --git a/src/main/kotlin/ee/carlrobert/codegpt/toolwindow/ui/UserMessagePanel.kt b/src/main/kotlin/ee/carlrobert/codegpt/toolwindow/ui/UserMessagePanel.kt index 5bfee8a0..cf382462 100644 --- a/src/main/kotlin/ee/carlrobert/codegpt/toolwindow/ui/UserMessagePanel.kt +++ b/src/main/kotlin/ee/carlrobert/codegpt/toolwindow/ui/UserMessagePanel.kt @@ -181,7 +181,7 @@ class UserMessagePanel( layout = BoxLayout(this, BoxLayout.Y_AXIS) isOpaque = false ids.forEach { - ConversationTagProcessor.getConversation(it)?.let { conversation -> + ConversationTagProcessor.getConversation(project, it)?.let { conversation -> val title = HistoryActionItem.getConversationTitle(conversation) val titleLink = ActionLink(title) { project.service() diff --git a/src/main/kotlin/ee/carlrobert/codegpt/ui/textarea/SearchManager.kt b/src/main/kotlin/ee/carlrobert/codegpt/ui/textarea/SearchManager.kt index d1109784..85cd7ec2 100644 --- a/src/main/kotlin/ee/carlrobert/codegpt/ui/textarea/SearchManager.kt +++ b/src/main/kotlin/ee/carlrobert/codegpt/ui/textarea/SearchManager.kt @@ -39,7 +39,7 @@ class SearchManager( FilesGroupItem(project, tagManager), FoldersGroupItem(project, tagManager), GitGroupItem(project), - HistoryGroupItem(), + HistoryGroupItem(project), DiagnosticsActionItem(tagManager) ).filter { it.enabled } @@ -47,7 +47,7 @@ class SearchManager( FilesGroupItem(project, tagManager), FoldersGroupItem(project, tagManager), GitGroupItem(project), - HistoryGroupItem(), + HistoryGroupItem(project), PersonasGroupItem(tagManager), DocsGroupItem(tagManager), CodeAnalyzeActionItem(tagManager), diff --git a/src/main/kotlin/ee/carlrobert/codegpt/ui/textarea/TagProcessorFactory.kt b/src/main/kotlin/ee/carlrobert/codegpt/ui/textarea/TagProcessorFactory.kt index 4a60fe30..f54c7bc6 100644 --- a/src/main/kotlin/ee/carlrobert/codegpt/ui/textarea/TagProcessorFactory.kt +++ b/src/main/kotlin/ee/carlrobert/codegpt/ui/textarea/TagProcessorFactory.kt @@ -228,11 +228,10 @@ class ConversationTagProcessor( ) : TagProcessor { companion object { - fun getConversation(conversationId: UUID) = - ConversationsState.getCurrentConversation()?.takeIf { - it.id.equals(conversationId) - } ?: ConversationsState.getInstance().conversations.find { - it.id.equals(conversationId) + fun getConversation(project: Project, conversationId: UUID): Conversation? { + val state = ConversationsState.getInstance(project) + return state.currentConversation?.takeIf { it.id == conversationId } + ?: state.conversations.find { it.id == conversationId } } fun formatConversation(conversation: Conversation): String { diff --git a/src/main/kotlin/ee/carlrobert/codegpt/ui/textarea/lookup/group/HistoryGroupItem.kt b/src/main/kotlin/ee/carlrobert/codegpt/ui/textarea/lookup/group/HistoryGroupItem.kt index 59475338..faadc054 100644 --- a/src/main/kotlin/ee/carlrobert/codegpt/ui/textarea/lookup/group/HistoryGroupItem.kt +++ b/src/main/kotlin/ee/carlrobert/codegpt/ui/textarea/lookup/group/HistoryGroupItem.kt @@ -3,6 +3,7 @@ package ee.carlrobert.codegpt.ui.textarea.lookup.group import com.intellij.codeInsight.lookup.impl.LookupImpl import com.intellij.icons.AllIcons import com.intellij.openapi.application.runInEdt +import com.intellij.openapi.project.Project import ee.carlrobert.codegpt.CodeGPTBundle import ee.carlrobert.codegpt.conversations.ConversationsState import ee.carlrobert.codegpt.ui.textarea.lookup.DynamicLookupGroupItem @@ -12,7 +13,7 @@ import ee.carlrobert.codegpt.ui.textarea.lookup.action.HistoryActionItem import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext -class HistoryGroupItem : AbstractLookupGroupItem(), DynamicLookupGroupItem { +class HistoryGroupItem(private val project: Project) : AbstractLookupGroupItem(), DynamicLookupGroupItem { override val displayName: String = CodeGPTBundle.get("suggestionGroupItem.history.displayName") @@ -21,7 +22,7 @@ class HistoryGroupItem : AbstractLookupGroupItem(), DynamicLookupGroupItem { private val addedItems = mutableSetOf() override suspend fun getLookupItems(searchText: String): List { - return ConversationsState.getInstance().conversations + return ConversationsState.getInstance(project).conversations .sortedByDescending { it.updatedOn } .filter { conversation -> if (searchText.isEmpty()) { @@ -55,4 +56,4 @@ class HistoryGroupItem : AbstractLookupGroupItem(), DynamicLookupGroupItem { } } } -} \ No newline at end of file +} diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 0fd2ef00..c696da4b 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -44,7 +44,7 @@ - - + + diff --git a/src/test/kotlin/ee/carlrobert/codegpt/completions/CompletionRequestProviderTest.kt b/src/test/kotlin/ee/carlrobert/codegpt/completions/CompletionRequestProviderTest.kt index c2fabc48..494d5612 100644 --- a/src/test/kotlin/ee/carlrobert/codegpt/completions/CompletionRequestProviderTest.kt +++ b/src/test/kotlin/ee/carlrobert/codegpt/completions/CompletionRequestProviderTest.kt @@ -23,7 +23,7 @@ class CompletionRequestProviderTest : IntegrationTest() { instructions = "TEST_SYSTEM_PROMPT" } service().state.personas.selectedPersona = customPersona - val conversation = ConversationService.getInstance().startConversation(project) + val conversation = ConversationService.getInstance(project).startConversation() val firstMessage = createDummyMessage(500) val secondMessage = createDummyMessage(250) conversation.addMessage(firstMessage) @@ -56,7 +56,7 @@ class CompletionRequestProviderTest : IntegrationTest() { instructions = "TEST_SYSTEM_PROMPT" } service().state.personas.selectedPersona = customPersona - val conversation = ConversationService.getInstance().startConversation(project) + val conversation = ConversationService.getInstance(project).startConversation() val firstMessage = createDummyMessage("FIRST_TEST_PROMPT", 500) val secondMessage = createDummyMessage("SECOND_TEST_PROMPT", 250) conversation.addMessage(firstMessage) diff --git a/src/test/kotlin/ee/carlrobert/codegpt/completions/DefaultToolwindowChatCompletionRequestHandlerTest.kt b/src/test/kotlin/ee/carlrobert/codegpt/completions/DefaultToolwindowChatCompletionRequestHandlerTest.kt index 0d55932c..2923b9b5 100644 --- a/src/test/kotlin/ee/carlrobert/codegpt/completions/DefaultToolwindowChatCompletionRequestHandlerTest.kt +++ b/src/test/kotlin/ee/carlrobert/codegpt/completions/DefaultToolwindowChatCompletionRequestHandlerTest.kt @@ -27,7 +27,7 @@ class DefaultToolwindowChatCompletionRequestHandlerTest : IntegrationTest() { } service().state.personas.selectedPersona = customPersona val message = Message("TEST_PROMPT") - val conversation = ConversationService.getInstance().startConversation(project) + val conversation = ConversationService.getInstance(project).startConversation() expectOpenAI(StreamHttpExchange { request: RequestEntity -> assertThat(request.uri.path).isEqualTo("/v1/chat/completions") assertThat(request.method).isEqualTo("POST") @@ -74,7 +74,7 @@ class DefaultToolwindowChatCompletionRequestHandlerTest : IntegrationTest() { } service().state.personas.selectedPersona = customPersona val message = Message("TEST_PROMPT") - val conversation = ConversationService.getInstance().startConversation(project) + val conversation = ConversationService.getInstance(project).startConversation() conversation.addMessage(Message("Ping", "Pong")) expectLlama(StreamHttpExchange { request: RequestEntity -> assertThat(request.uri.path).isEqualTo("/v1/chat/completions") @@ -122,7 +122,7 @@ class DefaultToolwindowChatCompletionRequestHandlerTest : IntegrationTest() { } service().state.personas.selectedPersona = customPersona val message = Message("TEST_PROMPT") - val conversation = ConversationService.getInstance().startConversation(project) + val conversation = ConversationService.getInstance(project).startConversation() expectOllama(NdJsonStreamHttpExchange { request: RequestEntity -> assertThat(request.uri.path).isEqualTo("/v1/chat/completions") assertThat(request.method).isEqualTo("POST") @@ -170,7 +170,7 @@ class DefaultToolwindowChatCompletionRequestHandlerTest : IntegrationTest() { } service().state.personas.selectedPersona = customPersona val message = Message("TEST_PROMPT") - val conversation = ConversationService.getInstance().startConversation(project) + val conversation = ConversationService.getInstance(project).startConversation() expectGoogle(StreamHttpExchange { request: RequestEntity -> assertThat(request.uri.path).isEqualTo("/v1/models/gemini-2.0-flash:streamGenerateContent") assertThat(request.method).isEqualTo("POST") @@ -218,7 +218,7 @@ class DefaultToolwindowChatCompletionRequestHandlerTest : IntegrationTest() { } service().state.personas.selectedPersona = customPersona val message = Message("TEST_PROMPT") - val conversation = ConversationService.getInstance().startConversation(project) + val conversation = ConversationService.getInstance(project).startConversation() expectCodeGPT(StreamHttpExchange { request: RequestEntity -> assertThat(request.uri.path).isEqualTo("/v1/chat/completions") assertThat(request.method).isEqualTo("POST") diff --git a/src/test/kotlin/ee/carlrobert/codegpt/completions/OpenAIRequestFactoryIntegrationTest.kt b/src/test/kotlin/ee/carlrobert/codegpt/completions/OpenAIRequestFactoryIntegrationTest.kt index 41a4e36b..db34d339 100644 --- a/src/test/kotlin/ee/carlrobert/codegpt/completions/OpenAIRequestFactoryIntegrationTest.kt +++ b/src/test/kotlin/ee/carlrobert/codegpt/completions/OpenAIRequestFactoryIntegrationTest.kt @@ -24,7 +24,7 @@ class OpenAIRequestFactoryIntegrationTest : IntegrationTest() { fun testDefaultPersonaUsesEditModePromptWhenEnabled() { useOpenAIService(OpenAIChatCompletionModel.GPT_4_O.code) service().state.personas.selectedPersona = PersonasState.DEFAULT_PERSONA - val conversation = ConversationService.getInstance().startConversation(project) + val conversation = ConversationService.getInstance(project).startConversation() val message = Message("Please refactor this code") val callParameters = ChatCompletionParameters .builder(conversation, message) @@ -129,7 +129,7 @@ class OpenAIRequestFactoryIntegrationTest : IntegrationTest() { fun testDefaultPersonaIsFilteredInAskMode() { useOpenAIService(OpenAIChatCompletionModel.GPT_4_O.code) service().state.personas.selectedPersona = PersonasState.DEFAULT_PERSONA - val conversation = ConversationService.getInstance().startConversation(project) + val conversation = ConversationService.getInstance(project).startConversation() val message = Message("Please refactor this code") val callParameters = ChatCompletionParameters .builder(conversation, message) @@ -248,7 +248,7 @@ class OpenAIRequestFactoryIntegrationTest : IntegrationTest() { instructions = personaPromptWithSearchReplace } service().state.personas.selectedPersona = customPersona - val conversation = ConversationService.getInstance().startConversation(project) + val conversation = ConversationService.getInstance(project).startConversation() val message = Message("Please refactor this code") val callParameters = ChatCompletionParameters .builder(conversation, message) @@ -293,7 +293,7 @@ class OpenAIRequestFactoryIntegrationTest : IntegrationTest() { """.trimIndent() service().state.personas.selectedPersona.instructions = personaPromptWithSearchReplace - val conversation = ConversationService.getInstance().startConversation(project) + val conversation = ConversationService.getInstance(project).startConversation() val message = Message("Please refactor this code") val callParameters = ChatCompletionParameters .builder(conversation, message) diff --git a/src/test/kotlin/ee/carlrobert/codegpt/conversations/ConversationsStateTest.kt b/src/test/kotlin/ee/carlrobert/codegpt/conversations/ConversationsStateTest.kt index 09a04fa3..40caa888 100644 --- a/src/test/kotlin/ee/carlrobert/codegpt/conversations/ConversationsStateTest.kt +++ b/src/test/kotlin/ee/carlrobert/codegpt/conversations/ConversationsStateTest.kt @@ -2,22 +2,18 @@ package ee.carlrobert.codegpt.conversations import com.intellij.testFramework.fixtures.BasePlatformTestCase import ee.carlrobert.codegpt.conversations.message.Message -import ee.carlrobert.codegpt.settings.GeneralSettings -import ee.carlrobert.codegpt.settings.service.ServiceType -import ee.carlrobert.codegpt.settings.service.openai.OpenAISettings -import ee.carlrobert.llm.client.openai.completion.OpenAIChatCompletionModel import org.assertj.core.api.Assertions.assertThat class ConversationsStateTest : BasePlatformTestCase() { fun testStartNewDefaultConversation() { - val conversation = ConversationService.getInstance().startConversation(project) + val conversation = ConversationService.getInstance(project).startConversation() - assertThat(conversation).isEqualTo(ConversationsState.getCurrentConversation()) + assertThat(conversation).isEqualTo(ConversationService.getInstance(project).currentConversation) } fun testSaveConversation() { - val service = ConversationService.getInstance() + val service = ConversationService.getInstance(project) val conversation = service.createConversation() service.addConversation(conversation) val message = Message("TEST_PROMPT") @@ -26,7 +22,7 @@ class ConversationsStateTest : BasePlatformTestCase() { service.saveConversation(conversation) - val currentConversation = ConversationsState.getCurrentConversation() + val currentConversation = ConversationService.getInstance(project).currentConversation assertThat(currentConversation).isNotNull() assertThat(currentConversation!!.messages) .flatExtracting("prompt", "response") @@ -34,9 +30,9 @@ class ConversationsStateTest : BasePlatformTestCase() { } fun testGetPreviousConversation() { - val service = ConversationService.getInstance() - val firstConversation = service.startConversation(project) - service.startConversation(project) + val service = ConversationService.getInstance(project) + val firstConversation = service.startConversation() + service.startConversation() val previousConversation = service.previousConversation @@ -45,10 +41,10 @@ class ConversationsStateTest : BasePlatformTestCase() { } fun testGetNextConversation() { - val service = ConversationService.getInstance() - val firstConversation = service.startConversation(project) - val secondConversation = service.startConversation(project) - ConversationsState.getInstance().setCurrentConversation(firstConversation) + val service = ConversationService.getInstance(project) + val firstConversation = service.startConversation() + val secondConversation = service.startConversation() + ConversationsState.getInstance(project).setCurrentConversation(firstConversation) val nextConversation = service.nextConversation @@ -57,13 +53,13 @@ class ConversationsStateTest : BasePlatformTestCase() { } fun testDeleteSelectedConversation() { - val service = ConversationService.getInstance() - val firstConversation = service.startConversation(project) - service.startConversation(project) + val service = ConversationService.getInstance(project) + val firstConversation = service.startConversation() + service.startConversation() service.deleteSelectedConversation() - assertThat(ConversationsState.getCurrentConversation()).isEqualTo(firstConversation) + assertThat(ConversationService.getInstance(project).currentConversation).isEqualTo(firstConversation) assertThat(service.sortedConversations.size).isEqualTo(1) assertThat(service.sortedConversations) .extracting("id") @@ -71,13 +67,13 @@ class ConversationsStateTest : BasePlatformTestCase() { } fun testClearAllConversations() { - val service = ConversationService.getInstance() - service.startConversation(project) - service.startConversation(project) + val service = ConversationService.getInstance(project) + service.startConversation() + service.startConversation() service.clearAll() - assertThat(ConversationsState.getCurrentConversation()).isNull() + assertThat(ConversationService.getInstance(project).currentConversation).isNull() assertThat(service.sortedConversations.size).isEqualTo(0) } } diff --git a/src/test/kotlin/ee/carlrobert/codegpt/toolwindow/chat/ChatToolWindowTabPanelTest.kt b/src/test/kotlin/ee/carlrobert/codegpt/toolwindow/chat/ChatToolWindowTabPanelTest.kt index 88b36476..c8c7c8db 100644 --- a/src/test/kotlin/ee/carlrobert/codegpt/toolwindow/chat/ChatToolWindowTabPanelTest.kt +++ b/src/test/kotlin/ee/carlrobert/codegpt/toolwindow/chat/ChatToolWindowTabPanelTest.kt @@ -31,7 +31,7 @@ class ChatToolWindowTabPanelTest : IntegrationTest() { service().state.personas.selectedPersona.instructions = "TEST_SYSTEM_PROMPT" val message = Message("Hello!") - val conversation = ConversationService.getInstance().startConversation(project) + val conversation = ConversationService.getInstance(project).startConversation() val panel = ChatToolWindowTabPanel(project, conversation) expectOpenAI(StreamHttpExchange { request: RequestEntity -> assertThat(request.uri.path).isEqualTo("/v1/chat/completions") @@ -100,7 +100,7 @@ class ChatToolWindowTabPanelTest : IntegrationTest() { val message = Message("TEST_MESSAGE") message.referencedFilePaths = listOf("TEST_FILE_PATH_1", "TEST_FILE_PATH_2", "TEST_FILE_PATH_3") - val conversation = ConversationService.getInstance().startConversation(project) + val conversation = ConversationService.getInstance(project).startConversation() val panel = ChatToolWindowTabPanel(project, conversation) panel.includeFiles(listOf( LightVirtualFile("TEST_FILE_NAME_1", "TEST_FILE_CONTENT_1"), @@ -201,7 +201,7 @@ class ChatToolWindowTabPanelTest : IntegrationTest() { service().state.personas.selectedPersona.instructions = "TEST_SYSTEM_PROMPT" val message = Message("TEST_MESSAGE") - val conversation = ConversationService.getInstance().startConversation(project) + val conversation = ConversationService.getInstance(project).startConversation() val panel = ChatToolWindowTabPanel(project, conversation) expectOpenAI(StreamHttpExchange { request: RequestEntity -> assertThat(request.uri.path).isEqualTo("/v1/chat/completions") @@ -287,7 +287,7 @@ class ChatToolWindowTabPanelTest : IntegrationTest() { val message = Message("TEST_MESSAGE") message.referencedFilePaths = listOf("TEST_FILE_PATH_1", "TEST_FILE_PATH_2", "TEST_FILE_PATH_3") - val conversation = ConversationService.getInstance().startConversation(project) + val conversation = ConversationService.getInstance(project).startConversation() val panel = ChatToolWindowTabPanel(project, conversation) panel.includeFiles( listOf( @@ -398,7 +398,7 @@ class ChatToolWindowTabPanelTest : IntegrationTest() { llamaSettings.minP = 0.03 llamaSettings.repeatPenalty = 1.3 val message = Message("TEST_PROMPT") - val conversation = ConversationService.getInstance().startConversation(project) + val conversation = ConversationService.getInstance(project).startConversation() val panel = ChatToolWindowTabPanel(project, conversation) expectLlama(StreamHttpExchange { request: RequestEntity -> assertThat(request.uri.path).isEqualTo("/v1/chat/completions") diff --git a/src/test/kotlin/ee/carlrobert/codegpt/toolwindow/chat/ChatToolWindowTabbedPaneTest.kt b/src/test/kotlin/ee/carlrobert/codegpt/toolwindow/chat/ChatToolWindowTabbedPaneTest.kt index 572ffab9..5f81b7af 100644 --- a/src/test/kotlin/ee/carlrobert/codegpt/toolwindow/chat/ChatToolWindowTabbedPaneTest.kt +++ b/src/test/kotlin/ee/carlrobert/codegpt/toolwindow/chat/ChatToolWindowTabbedPaneTest.kt @@ -9,7 +9,7 @@ import org.assertj.core.api.Assertions.assertThat class ChatToolWindowTabbedPaneTest : BasePlatformTestCase() { fun testClearAllTabs() { - val tabbedPane = ChatToolWindowTabbedPane(Disposer.newDisposable()) + val tabbedPane = ChatToolWindowTabbedPane(project, Disposer.newDisposable()) tabbedPane.addNewTab(createNewTabPanel()) tabbedPane.clearAll() @@ -19,7 +19,7 @@ class ChatToolWindowTabbedPaneTest : BasePlatformTestCase() { fun testAddingNewTabs() { - val tabbedPane = ChatToolWindowTabbedPane(Disposer.newDisposable()) + val tabbedPane = ChatToolWindowTabbedPane(project, Disposer.newDisposable()) tabbedPane.addNewTab(createNewTabPanel()) tabbedPane.addNewTab(createNewTabPanel()) @@ -30,8 +30,8 @@ class ChatToolWindowTabbedPaneTest : BasePlatformTestCase() { } fun testResetCurrentlyActiveTabPanel() { - val tabbedPane = ChatToolWindowTabbedPane(Disposer.newDisposable()) - val conversation = ConversationService.getInstance().startConversation(project) + val tabbedPane = ChatToolWindowTabbedPane(project, Disposer.newDisposable()) + val conversation = ConversationService.getInstance(project).startConversation() conversation.addMessage(Message("TEST_PROMPT", "TEST_RESPONSE")) tabbedPane.addNewTab(ChatToolWindowTabPanel(project, conversation)) @@ -44,7 +44,7 @@ class ChatToolWindowTabbedPaneTest : BasePlatformTestCase() { private fun createNewTabPanel(): ChatToolWindowTabPanel { return ChatToolWindowTabPanel( project, - ConversationService.getInstance().startConversation(project) + ConversationService.getInstance(project).startConversation() ) } } diff --git a/src/test/kotlin/ee/carlrobert/codegpt/ui/textarea/ConversationTagProcessorTest.kt b/src/test/kotlin/ee/carlrobert/codegpt/ui/textarea/ConversationTagProcessorTest.kt index 784a4011..85da37ca 100644 --- a/src/test/kotlin/ee/carlrobert/codegpt/ui/textarea/ConversationTagProcessorTest.kt +++ b/src/test/kotlin/ee/carlrobert/codegpt/ui/textarea/ConversationTagProcessorTest.kt @@ -15,8 +15,8 @@ class ConversationTagProcessorTest : IntegrationTest() { public override fun setUp() { super.setUp() - conversationService = service() - ConversationsState.getInstance().conversations.clear() + conversationService = project.service() + ConversationsState.getInstance(project).conversations.clear() } fun `test should format conversation with single message`() { @@ -100,12 +100,12 @@ class ConversationTagProcessorTest : IntegrationTest() { } fun `test should find current conversation by id`() { - val conversation = conversationService.startConversation(project) + val conversation = conversationService.startConversation() conversation.addMessage(Message("Current conversation test").apply { response = "This is the current conversation" }) - val foundConversation = ConversationTagProcessor.getConversation(conversation.id) + val foundConversation = ConversationTagProcessor.getConversation(project, conversation.id) assertThat(foundConversation).isNotNull assertThat(foundConversation!!.id).isEqualTo(conversation.id) @@ -120,7 +120,7 @@ class ConversationTagProcessorTest : IntegrationTest() { }) conversationService.addConversation(conversation) - val foundConversation = ConversationTagProcessor.getConversation(conversation.id) + val foundConversation = ConversationTagProcessor.getConversation(project, conversation.id) assertThat(foundConversation).isNotNull assertThat(foundConversation!!.id).isEqualTo(conversation.id) @@ -134,13 +134,13 @@ class ConversationTagProcessorTest : IntegrationTest() { response = "This is stored" }) conversationService.addConversation(storedConversation) - val currentConversation = conversationService.startConversation(project) + val currentConversation = conversationService.startConversation() currentConversation.id = storedConversation.id currentConversation.addMessage(Message("Current version").apply { response = "This is current" }) - val foundConversation = ConversationTagProcessor.getConversation(storedConversation.id) + val foundConversation = ConversationTagProcessor.getConversation(project, storedConversation.id) assertThat(foundConversation).isNotNull assertThat(foundConversation!!.messages[0].prompt).isEqualTo("Current version") @@ -149,7 +149,7 @@ class ConversationTagProcessorTest : IntegrationTest() { fun `test should return null for non-existent conversation`() { val nonExistentId = UUID.randomUUID() - val foundConversation = ConversationTagProcessor.getConversation(nonExistentId) + val foundConversation = ConversationTagProcessor.getConversation(project, nonExistentId) assertThat(foundConversation).isNull() } @@ -161,7 +161,7 @@ class ConversationTagProcessorTest : IntegrationTest() { }) conversationService.addConversation(conversation) val historyActionItem = HistoryActionItem(conversation) - val foundConversation = ConversationTagProcessor.getConversation(conversation.id) + val foundConversation = ConversationTagProcessor.getConversation(project, conversation.id) val conversationTitle = historyActionItem.displayName val formatted = ConversationTagProcessor.formatConversation(foundConversation!!) @@ -201,4 +201,4 @@ class ConversationTagProcessorTest : IntegrationTest() { val titleInFormatted = formatted.substringAfter("## Conversation: ").substringBefore("\n") assertThat(titleInFormatted).hasSize(60) } -} \ No newline at end of file +} diff --git a/src/test/kotlin/ee/carlrobert/codegpt/ui/textarea/HistorySearchIntegrationTest.kt b/src/test/kotlin/ee/carlrobert/codegpt/ui/textarea/HistorySearchIntegrationTest.kt index 7f45a8e3..1184bd88 100644 --- a/src/test/kotlin/ee/carlrobert/codegpt/ui/textarea/HistorySearchIntegrationTest.kt +++ b/src/test/kotlin/ee/carlrobert/codegpt/ui/textarea/HistorySearchIntegrationTest.kt @@ -19,12 +19,12 @@ class HistorySearchIntegrationTest : IntegrationTest() { public override fun setUp() { super.setUp() - conversationService = service() - ConversationsState.getInstance().conversations.clear() - + conversationService = project.service() + ConversationsState.getInstance(project).conversations.clear() + val tagManager = TagManager() searchManager = SearchManager(project, tagManager) - historyGroupItem = HistoryGroupItem() + historyGroupItem = HistoryGroupItem(project) } fun `test should include history group in search manager default groups`() { @@ -44,7 +44,7 @@ class HistorySearchIntegrationTest : IntegrationTest() { } fun `test should filter conversations by search terms`() { - ConversationsState.getInstance().conversations.clear() + ConversationsState.getInstance(project).conversations.clear() createTestConversations() val testCases = mapOf( "java" to 3, @@ -88,8 +88,8 @@ class HistorySearchIntegrationTest : IntegrationTest() { fun `test should handle special characters in search`() { val conversation = conversationService.createConversation() - conversation.addMessage(Message("What is C++ programming?").apply { - response = "C++ is a powerful programming language" + conversation.addMessage(Message("What is C++ programming?").apply { + response = "C++ is a powerful programming language" }) conversationService.addConversation(conversation) @@ -104,13 +104,13 @@ class HistorySearchIntegrationTest : IntegrationTest() { val conversation = conversationService.createConversation() val topic = when (i % 5) { 0 -> "Java" - 1 -> "Python" + 1 -> "Python" 2 -> "JavaScript" 3 -> "Database" else -> "General" } - conversation.addMessage(Message("Question about $topic #$i").apply { - response = "Answer about $topic #$i" + conversation.addMessage(Message("Question about $topic #$i").apply { + response = "Answer about $topic #$i" }) conversationService.addConversation(conversation) } @@ -139,13 +139,13 @@ class HistorySearchIntegrationTest : IntegrationTest() { fun `test should search in conversation titles`() { val conversation1 = conversationService.createConversation() - conversation1.addMessage(Message("How to use Docker containers?").apply { - response = "Docker containers are lightweight virtualization" + conversation1.addMessage(Message("How to use Docker containers?").apply { + response = "Docker containers are lightweight virtualization" }) conversationService.addConversation(conversation1) val conversation2 = conversationService.createConversation() - conversation2.addMessage(Message("What is virtualization?").apply { - response = "Virtualization allows running multiple Docker instances" + conversation2.addMessage(Message("What is virtualization?").apply { + response = "Virtualization allows running multiple Docker instances" }) conversationService.addConversation(conversation2) @@ -160,7 +160,7 @@ class HistorySearchIntegrationTest : IntegrationTest() { fun `test should match history aliases in search manager`() { val historyAliases = listOf("history", "hist", "h") - + historyAliases.forEach { alias -> val matches = searchManager.matchesAnyDefaultGroup(alias) assertThat(matches) @@ -178,7 +178,7 @@ class HistorySearchIntegrationTest : IntegrationTest() { private fun createTestConversations() { val testData = listOf( "How to write Java code?" to "Use Java syntax and compile with javac", - "Python vs JavaScript comparison" to "Python is interpreted, JavaScript runs in browsers", + "Python vs JavaScript comparison" to "Python is interpreted, JavaScript runs in browsers", "What is database normalization?" to "Database normalization reduces redundancy", "Best practices for programming" to "Write clean, readable, and testable code", "JavaScript async/await tutorial" to "Use async/await for asynchronous programming" @@ -190,4 +190,4 @@ class HistorySearchIntegrationTest : IntegrationTest() { conversationService.addConversation(conversation) } } -} \ No newline at end of file +} diff --git a/src/test/kotlin/ee/carlrobert/codegpt/ui/textarea/HistoryTagIntegrationTest.kt b/src/test/kotlin/ee/carlrobert/codegpt/ui/textarea/HistoryTagIntegrationTest.kt index 38b2d714..26610d66 100644 --- a/src/test/kotlin/ee/carlrobert/codegpt/ui/textarea/HistoryTagIntegrationTest.kt +++ b/src/test/kotlin/ee/carlrobert/codegpt/ui/textarea/HistoryTagIntegrationTest.kt @@ -21,8 +21,8 @@ class HistoryTagIntegrationTest : IntegrationTest() { public override fun setUp() { super.setUp() - conversationService = service() - ConversationsState.getInstance().conversations.clear() + conversationService = project.service() + ConversationsState.getInstance(project).conversations.clear() } fun testShouldDisplayCorrectNameForHistoryActionItem() { @@ -88,7 +88,7 @@ class HistoryTagIntegrationTest : IntegrationTest() { } fun testShouldFilterConversationsBySearchText() { - val historyGroupItem = HistoryGroupItem() + val historyGroupItem = HistoryGroupItem(project) val javaConversation = conversationService.createConversation() javaConversation.addMessage(Message("How to write Java code?").apply { response = "Use Java syntax" }) conversationService.addConversation(javaConversation) @@ -106,7 +106,7 @@ class HistoryTagIntegrationTest : IntegrationTest() { } fun testShouldHandleEmptyConversationsList() { - val historyGroupItem = HistoryGroupItem() + val historyGroupItem = HistoryGroupItem(project) val results = runBlocking { historyGroupItem.getLookupItems("") } @@ -114,7 +114,7 @@ class HistoryTagIntegrationTest : IntegrationTest() { } fun testShouldPerformCaseInsensitiveSearch() { - val historyGroupItem = HistoryGroupItem() + val historyGroupItem = HistoryGroupItem(project) val conversation = conversationService.createConversation() conversation.addMessage(Message("JavaScript Tutorial").apply { response = "Learn JS" }) conversationService.addConversation(conversation) @@ -165,7 +165,7 @@ class HistoryTagIntegrationTest : IntegrationTest() { conversation.addMessage(Message("Test question").apply { response = "Test answer" }) conversationService.addConversation(conversation) - val foundConversation = ConversationTagProcessor.getConversation(conversation.id) + val foundConversation = ConversationTagProcessor.getConversation(project, conversation.id) assertThat(foundConversation).isNotNull assertThat(foundConversation!!.id).isEqualTo(conversation.id) @@ -176,7 +176,7 @@ class HistoryTagIntegrationTest : IntegrationTest() { fun testShouldReturnNullForNonExistentConversation() { val nonExistentId = UUID.randomUUID() - val foundConversation = ConversationTagProcessor.getConversation(nonExistentId) + val foundConversation = ConversationTagProcessor.getConversation(project, nonExistentId) assertThat(foundConversation).isNull() } @@ -208,7 +208,7 @@ class HistoryTagIntegrationTest : IntegrationTest() { } fun testShouldSortConversationsByUpdatedDateDescending() { - val historyGroupItem = HistoryGroupItem() + val historyGroupItem = HistoryGroupItem(project) val now = LocalDateTime.now() val oldConversation = conversationService.createConversation() oldConversation.updatedOn = now.minusDays(3) @@ -232,4 +232,4 @@ class HistoryTagIntegrationTest : IntegrationTest() { assertThat(displayNames[2]).isEqualTo("Old conversation") } -} \ No newline at end of file +}