From ef2ed2c9de18b7d3d2dbf037a085df85f30ab765 Mon Sep 17 00:00:00 2001 From: Shiwani Gupta Date: Sun, 27 Oct 2024 04:06:53 +0530 Subject: [PATCH] Streaming support for AI chat interactions Integrate a streaming feature into the AI assistant, facilitating continuous interaction and dynamic responses. This enhancement allows users to engage with the assistant more fluidly during coding tasks, improving overall efficiency and satisfaction with the tool. --- pom.xml | 1 + .../completion/JeddictCompletionProvider.java | 2 +- .../ai/components/AssistantTopComponent.java | 9 + .../jeddict/ai/hints/ExpressionFix.java | 5 +- .../jeddict/ai/hints/JavaDocFixImpl.java | 15 +- .../github/jeddict/ai/hints/JeddictHint.java | 4 +- .../io/github/jeddict/ai/hints/LearnFix.java | 173 +++++++++++------- .../io/github/jeddict/ai/hints/MethodFix.java | 9 +- .../jeddict/ai/hints/RestEndpointFix.java | 5 +- .../io/github/jeddict/ai/hints/TextFix.java | 7 +- .../github/jeddict/ai/hints/VariableFix.java | 5 +- .../jeddict/ai/hints/VariableNameFix.java | 5 +- .../ai/{ => lang}/JeddictChatModel.java | 170 ++++++++++++----- .../jeddict/ai/lang/JeddictStreamHandler.java | 72 ++++++++ .../github/jeddict/ai/{ => lang}/Snippet.java | 8 +- .../ai/scanner/ProjectClassScanner.java | 2 +- .../ai/settings/AIAssistancePanel.form | 68 ++++--- .../ai/settings/AIAssistancePanel.java | 136 +++++++------- .../ai/settings/PreferencesManager.java | 27 +-- .../io/github/jeddict/ai/util/EditorUtil.java | 12 +- .../jeddict/ai/settings/Bundle.properties | 58 +++--- 21 files changed, 501 insertions(+), 292 deletions(-) rename src/main/java/io/github/jeddict/ai/{ => lang}/JeddictChatModel.java (89%) create mode 100644 src/main/java/io/github/jeddict/ai/lang/JeddictStreamHandler.java rename src/main/java/io/github/jeddict/ai/{ => lang}/Snippet.java (90%) diff --git a/pom.xml b/pom.xml index ac03fdc..746884e 100644 --- a/pom.xml +++ b/pom.xml @@ -147,6 +147,7 @@ io.github.jeddict.ai + io.github.jeddict.ai.lang false diff --git a/src/main/java/io/github/jeddict/ai/completion/JeddictCompletionProvider.java b/src/main/java/io/github/jeddict/ai/completion/JeddictCompletionProvider.java index 2b64ff8..844830f 100644 --- a/src/main/java/io/github/jeddict/ai/completion/JeddictCompletionProvider.java +++ b/src/main/java/io/github/jeddict/ai/completion/JeddictCompletionProvider.java @@ -48,7 +48,7 @@ import com.sun.source.tree.Tree; import com.sun.source.tree.VariableTree; import com.sun.source.util.DocTrees; -import io.github.jeddict.ai.Snippet; +import io.github.jeddict.ai.lang.Snippet; import static io.github.jeddict.ai.scanner.ProjectClassScanner.getFileObjectFromEditor; import io.github.jeddict.ai.scanner.ClassData; import java.util.stream.Collectors; diff --git a/src/main/java/io/github/jeddict/ai/components/AssistantTopComponent.java b/src/main/java/io/github/jeddict/ai/components/AssistantTopComponent.java index 8b77de6..de5f01a 100644 --- a/src/main/java/io/github/jeddict/ai/components/AssistantTopComponent.java +++ b/src/main/java/io/github/jeddict/ai/components/AssistantTopComponent.java @@ -22,6 +22,7 @@ import static io.github.jeddict.ai.util.EditorUtil.isSuitableForWebAppDirectory; import static io.github.jeddict.ai.util.StringUtil.convertToCapitalized; import java.awt.BorderLayout; +import java.awt.Color; import java.awt.Desktop; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; @@ -114,6 +115,14 @@ public JEditorPane createHtmlPane(String content) { parentPanel.add(editorPane); return editorPane; } + + public JEditorPane createPane() { + JEditorPane editorPane = new JEditorPane(); + editorPane.setEditable(false); + editorPane.setBackground(Color.WHITE); + parentPanel.add(editorPane); + return editorPane; + } public JEditorPane createCodePane(String content) { JEditorPane editorPane = new JEditorPane(); diff --git a/src/main/java/io/github/jeddict/ai/hints/ExpressionFix.java b/src/main/java/io/github/jeddict/ai/hints/ExpressionFix.java index abac2bc..bc3cb9e 100644 --- a/src/main/java/io/github/jeddict/ai/hints/ExpressionFix.java +++ b/src/main/java/io/github/jeddict/ai/hints/ExpressionFix.java @@ -22,8 +22,9 @@ import com.sun.source.tree.ExpressionTree; import com.sun.source.tree.Tree; import com.sun.source.util.TreePath; +import io.github.jeddict.ai.JeddictUpdateManager; import io.github.jeddict.ai.completion.Action; -import io.github.jeddict.ai.JeddictChatModel; +import io.github.jeddict.ai.lang.JeddictChatModel; import io.github.jeddict.ai.util.StringUtil; import org.netbeans.api.java.source.JavaSource; import org.netbeans.api.java.source.TreePathHandle; @@ -48,7 +49,7 @@ public ExpressionFix(TreePathHandle tpHandle, Action action, TreePath treePath) @Override protected String getText() { - return NbBundle.getMessage(JeddictChatModel.class, "HINT_ENHANCE_EXPRESSION"); + return NbBundle.getMessage(JeddictUpdateManager.class, "HINT_ENHANCE_EXPRESSION"); } @Override diff --git a/src/main/java/io/github/jeddict/ai/hints/JavaDocFixImpl.java b/src/main/java/io/github/jeddict/ai/hints/JavaDocFixImpl.java index f1bb227..1069cf6 100644 --- a/src/main/java/io/github/jeddict/ai/hints/JavaDocFixImpl.java +++ b/src/main/java/io/github/jeddict/ai/hints/JavaDocFixImpl.java @@ -18,9 +18,7 @@ */ package io.github.jeddict.ai.hints; -import io.github.jeddict.ai.util.StringUtil; import com.sun.source.doctree.DocCommentTree; -import com.sun.source.doctree.DocTree; import com.sun.source.tree.CompilationUnitTree; import com.sun.source.tree.MethodTree; import com.sun.source.tree.Tree; @@ -31,21 +29,18 @@ import com.sun.source.tree.VariableTree; import com.sun.source.util.DocTrees; import com.sun.source.util.TreePath; +import io.github.jeddict.ai.JeddictUpdateManager; import io.github.jeddict.ai.completion.Action; -import io.github.jeddict.ai.JeddictChatModel; import static io.github.jeddict.ai.completion.Action.ENHANCE; +import io.github.jeddict.ai.lang.JeddictChatModel; import static io.github.jeddict.ai.util.SourceUtil.geIndentaion; +import io.github.jeddict.ai.util.StringUtil; import static io.github.jeddict.ai.util.StringUtil.removeCodeBlockMarkers; -import static io.github.jeddict.ai.util.StringUtil.trimLeadingSpaces; -import java.util.LinkedList; -import java.util.List; import javax.lang.model.element.Element; -import javax.lang.model.element.ElementKind; import javax.swing.text.BadLocationException; import javax.swing.text.Document; import org.netbeans.api.java.source.ElementHandle; import org.netbeans.api.java.source.JavaSource; -import org.netbeans.api.java.source.TreeMaker; import org.netbeans.api.java.source.TreePathHandle; import org.netbeans.api.java.source.WorkingCopy; import org.netbeans.spi.java.hints.JavaFix; @@ -71,9 +66,9 @@ public JavaDocFixImpl(TreePathHandle tpHandle, Action type, protected String getText() { switch (action) { case ENHANCE: - return NbBundle.getMessage(JeddictChatModel.class, "HINT_JAVADOC_GENERATED", StringUtil.convertToCapitalized(classType.getKind().toString()));//NOI18N + return NbBundle.getMessage(JeddictUpdateManager.class, "HINT_JAVADOC_GENERATED", StringUtil.convertToCapitalized(classType.getKind().toString()));//NOI18N default: - return NbBundle.getMessage(JeddictChatModel.class, "HINT_JAVADOC", StringUtil.convertToCapitalized(classType.getKind().toString()));//NOI18N + return NbBundle.getMessage(JeddictUpdateManager.class, "HINT_JAVADOC", StringUtil.convertToCapitalized(classType.getKind().toString()));//NOI18N } } diff --git a/src/main/java/io/github/jeddict/ai/hints/JeddictHint.java b/src/main/java/io/github/jeddict/ai/hints/JeddictHint.java index f66740d..c39cb29 100644 --- a/src/main/java/io/github/jeddict/ai/hints/JeddictHint.java +++ b/src/main/java/io/github/jeddict/ai/hints/JeddictHint.java @@ -31,7 +31,7 @@ import com.sun.source.util.SourcePositions; import com.sun.source.util.TreePath; import com.sun.source.util.Trees; -import io.github.jeddict.ai.JeddictChatModel; +import io.github.jeddict.ai.JeddictUpdateManager; import io.github.jeddict.ai.completion.Action; import io.github.jeddict.ai.settings.PreferencesManager; import java.util.ArrayList; @@ -189,7 +189,7 @@ public static ErrorDescription run(HintContext ctx) { return null; } Fix[] fixesArray = fixes.toArray(new Fix[0]); // Convert to array - String desc = NbBundle.getMessage(JeddictChatModel.class, "ERR_HINT"); //NOI18N + String desc = NbBundle.getMessage(JeddictUpdateManager.class, "ERR_HINT"); //NOI18N return ErrorDescriptionFactory.forTree(ctx, ctx.getPath(), desc, fixesArray); //NOI18N } diff --git a/src/main/java/io/github/jeddict/ai/hints/LearnFix.java b/src/main/java/io/github/jeddict/ai/hints/LearnFix.java index e86676e..81648ad 100644 --- a/src/main/java/io/github/jeddict/ai/hints/LearnFix.java +++ b/src/main/java/io/github/jeddict/ai/hints/LearnFix.java @@ -26,9 +26,11 @@ import static com.sun.source.tree.Tree.Kind.INTERFACE; import static com.sun.source.tree.Tree.Kind.METHOD; import com.sun.source.util.TreePath; +import dev.langchain4j.data.message.AiMessage; +import dev.langchain4j.model.output.Response; import io.github.jeddict.ai.JeddictUpdateManager; import io.github.jeddict.ai.completion.Action; -import io.github.jeddict.ai.JeddictChatModel; +import io.github.jeddict.ai.lang.JeddictChatModel; import io.github.jeddict.ai.completion.SQLCompletion; import io.github.jeddict.ai.components.AssistantTopComponent; import static io.github.jeddict.ai.components.AssistantTopComponent.attachIcon; @@ -87,6 +89,7 @@ import static io.github.jeddict.ai.components.AssistantTopComponent.saveToEditorIcon; import static io.github.jeddict.ai.components.AssistantTopComponent.settingsIcon; import io.github.jeddict.ai.components.ContextDialog; +import io.github.jeddict.ai.lang.JeddictStreamHandler; import io.github.jeddict.ai.util.EditorUtil; import java.awt.Toolkit; import java.awt.datatransfer.Clipboard; @@ -175,13 +178,13 @@ public LearnFix(Action action, FileObject selectedFileObject) { @Override protected String getText() { if (action == Action.LEARN) { - return NbBundle.getMessage(JeddictChatModel.class, "HINT_LEARN", + return NbBundle.getMessage(JeddictUpdateManager.class, "HINT_LEARN", StringUtil.convertToCapitalized(treePath.getLeaf().getKind().toString())); } else if (action == Action.QUERY) { - return NbBundle.getMessage(JeddictChatModel.class, "HINT_QUERY", + return NbBundle.getMessage(JeddictUpdateManager.class, "HINT_QUERY", StringUtil.convertToCapitalized(treePath.getLeaf().getKind().toString())); } else if (action == Action.TEST) { - return NbBundle.getMessage(JeddictChatModel.class, "HINT_TEST", + return NbBundle.getMessage(JeddictUpdateManager.class, "HINT_TEST", StringUtil.convertToCapitalized(treePath.getLeaf().getKind().toString())); } return null; @@ -201,31 +204,44 @@ protected void performRewrite(JavaFix.TransformationContext tc) throws Exception || leaf.getKind() == ENUM || leaf.getKind() == METHOD) { executorService.submit(() -> { - String response; - String fileName = fileObject != null ? fileObject.getName() : null; - if (action == Action.TEST) { - if (leaf instanceof MethodTree) { - response = new JeddictChatModel().generateTestCase(null, null, leaf.toString(), null, null); - } else { - response = new JeddictChatModel().generateTestCase(null, treePath.getCompilationUnit().toString(), null, null, null); - } - } else { - if (leaf instanceof MethodTree) { - response = new JeddictChatModel().assistJavaMethod(leaf.toString()); - } else { - response = new JeddictChatModel().assistJavaClass(treePath.getCompilationUnit().toString()); - } - } String name; if (leaf instanceof MethodTree) { name = ((MethodTree) leaf).getName().toString(); } else { name = ((ClassTree) leaf).getSimpleName().toString(); - } + String fileName = fileObject != null ? fileObject.getName() : null; + SwingUtilities.invokeLater(() -> { - displayHtmlContent(removeCodeBlockMarkers(response), fileName, name + " AI Assistant"); + displayHtmlContent(fileName, name + " AI Assistant"); + JeddictStreamHandler handler = new JeddictStreamHandler(topComponent) { + @Override + public void onComplete(String response) { + sourceCode = EditorUtil.updateEditors(topComponent, response); + responseHistory.add(response); + currentResponseIndex = responseHistory.size() - 1; + } + }; + String response; + if (action == Action.TEST) { + if (leaf instanceof MethodTree) { + response = new JeddictChatModel(handler).generateTestCase(null, null, leaf.toString(), null, null); + } else { + response = new JeddictChatModel(handler).generateTestCase(null, treePath.getCompilationUnit().toString(), null, null, null); + } + } else { + if (leaf instanceof MethodTree) { + response = new JeddictChatModel(handler).assistJavaMethod(leaf.toString()); + } else { + response = new JeddictChatModel(handler).assistJavaClass(treePath.getCompilationUnit().toString()); + } + } + if (response != null && !response.isEmpty()) { + response = removeCodeBlockMarkers(response); + handler.onComplete(response); + } }); + }); } } @@ -337,33 +353,42 @@ public String getFileContext() { public void askQueryForProjectCommit(Project project, String commitChanges, String intitalCommitMessage) { ProjectInformation info = ProjectUtils.getInformation(project); String projectName = info.getDisplayName(); - String response = new JeddictChatModel().generateCommitMessageSuggestions(commitChanges, intitalCommitMessage); - displayHtmlContent(removeCodeBlockMarkers(response), null, projectName + " GenAI Commit"); - this.commitChanges = commitChanges; - } - - public void displayHtmlContent(final String response, String filename, String title) { SwingUtilities.invokeLater(() -> { - Preferences prefs = Preferences.userNodeForPackage(AssistantTopComponent.class); - prefs.putBoolean(AssistantTopComponent.PREFERENCE_KEY, true); - topComponent = new AssistantTopComponent(title, null, getProject()); - sourceCode = EditorUtil.updateEditors(topComponent, response); - - JScrollPane scrollPane = new JScrollPane(topComponent.getParentPanel()); - topComponent.add(scrollPane, BorderLayout.CENTER); - - responseHistory.add(response); - currentResponseIndex = responseHistory.size() - 1; + displayHtmlContent(null, projectName + " GenAI Commit"); + JeddictStreamHandler handler = new JeddictStreamHandler(topComponent) { + @Override + public void onComplete(String response) { + response = removeCodeBlockMarkers(response); + sourceCode = EditorUtil.updateEditors(topComponent, response); + responseHistory.add(response); + currentResponseIndex = responseHistory.size() - 1; + LearnFix.this.commitChanges = commitChanges; + } + }; + String response = new JeddictChatModel(handler).generateCommitMessageSuggestions(commitChanges, intitalCommitMessage); + if (response != null && !response.isEmpty()) { + response = removeCodeBlockMarkers(response); + handler.onComplete(response); + } + }); + } - topComponent.add(createBottomPanel(null, filename, title, null), BorderLayout.SOUTH); - topComponent.open(); - topComponent.requestActive(); - Runtime.getRuntime().addShutdownHook(new Thread(() -> { + public void displayHtmlContent(String filename, String title) { + Preferences prefs = Preferences.userNodeForPackage(AssistantTopComponent.class); + prefs.putBoolean(AssistantTopComponent.PREFERENCE_KEY, true); + topComponent = new AssistantTopComponent(title, null, getProject()); + JScrollPane scrollPane = new JScrollPane(topComponent.getParentPanel()); + topComponent.add(scrollPane, BorderLayout.CENTER); + topComponent.add(createBottomPanel(null, filename, title, null), BorderLayout.SOUTH); + topComponent.open(); + topComponent.requestActive(); + Runtime.getRuntime().addShutdownHook(new Thread(() -> { + SwingUtilities.invokeLater(() -> { if (topComponent != null) { topComponent.close(); } - })); - }); + }); + })); } public void openChat(String type, final String query, String fileName, String title, Consumer action) { @@ -465,7 +490,7 @@ private JPanel createBottomPanel(String type, String fileName, String title, Con openInBrowserButton.setMaximumSize(buttonSize); openInBrowserButton.setEnabled(topComponent.getAllEditorCount() > 0); westButtonPanel.add(openInBrowserButton); - + JButton optionsButton = createButton(settingsIcon); // Replace with actual icon path optionsButton.setToolTipText("Open Jeddict AI Assistant Settings"); optionsButton.setPreferredSize(buttonSize); @@ -485,7 +510,6 @@ private JPanel createBottomPanel(String type, String fileName, String title, Con // } // }); // westButtonPanel.add(jeddictButton); - // New Chat Button (West) JButton newChatButton = createButton(newEditorIcon); newChatButton.setToolTipText("Start a new chat"); @@ -613,7 +637,7 @@ private void showFilePathPopup() { boolean enableRules = true; String rules = pm.getCommonPromptRules(); - if(commitChanges != null) { + if (commitChanges != null) { rules = commitChanges; enableRules = false; } @@ -624,11 +648,11 @@ private void showFilePathPopup() { dialog.setSize(800, 800); dialog.setLocationRelativeTo(SwingUtilities.windowForComponent(topComponent)); dialog.setVisible(true); - if(commitChanges == null) { + if (commitChanges == null) { pm.setCommonPromptRules(dialog.getRules()); } } - + private JButton createButton(ImageIcon icon) { JButton button = new JButton(icon); // Set button preferred size to match the icon's size (24x24) @@ -671,42 +695,51 @@ private void handleQuestion(String question, JButton submitButton) { if (prevChat != null) { prevChat = responseHistory.get(currentResponseIndex); } + JeddictStreamHandler handler = new JeddictStreamHandler(topComponent) { + @Override + public void onComplete(String response) { + if (responseHistory.isEmpty() || !response.equals(responseHistory.get(responseHistory.size() - 1))) { + responseHistory.add(response); + currentResponseIndex = responseHistory.size() - 1; + } + String finalResponse = response; + SwingUtilities.invokeLater(() -> { + sourceCode = EditorUtil.updateEditors(topComponent, finalResponse); + submitButton.setIcon(startIcon); + submitButton.setEnabled(true); + saveButton.setEnabled(sourceCode != null); + updateButtons(prevButton, nextButton); + questionPane.setText(""); + }); + } + }; String response; if (sqlCompletion != null) { - response = new JeddictChatModel().assistDbMetadata(sqlCompletion.getMetaData(), question); + response = new JeddictChatModel(handler).assistDbMetadata(sqlCompletion.getMetaData(), question); } else if (commitChanges != null) { - response = new JeddictChatModel().generateCommitMessageSuggestions(commitChanges, question); + response = new JeddictChatModel(handler).generateCommitMessageSuggestions(commitChanges, question); } else if (project != null) { - response = new JeddictChatModel().generateDescription(getProjectContext(), null, null, prevChat, question); + response = new JeddictChatModel(handler).generateDescription(getProjectContext(), null, null, prevChat, question); } else if (selectedFileObjects != null) { - response = new JeddictChatModel().generateDescription(getFilesContext(), null, null, prevChat, question); + response = new JeddictChatModel(handler).generateDescription(getFilesContext(), null, null, prevChat, question); } else if (selectedFileObject != null) { - response = new JeddictChatModel().generateDescription(null, getFileContext(), null, prevChat, question); + response = new JeddictChatModel(handler).generateDescription(null, getFileContext(), null, prevChat, question); } else if (treePath == null) { - response = new JeddictChatModel().generateDescription(null, null, null, prevChat, question); + response = new JeddictChatModel(handler).generateDescription(null, null, null, prevChat, question); } else if (action == Action.TEST) { if (leaf instanceof MethodTree) { - response = new JeddictChatModel().generateTestCase(null, null, leaf.toString(), prevChat, question); + response = new JeddictChatModel(handler).generateTestCase(null, null, leaf.toString(), prevChat, question); } else { - response = new JeddictChatModel().generateTestCase(null, treePath.getCompilationUnit().toString(), null, prevChat, question); + response = new JeddictChatModel(handler).generateTestCase(null, treePath.getCompilationUnit().toString(), null, prevChat, question); } } else { - response = new JeddictChatModel().generateDescription(null, treePath.getCompilationUnit().toString(), treePath.getLeaf() instanceof MethodTree ? treePath.getLeaf().toString() : null, prevChat, question); + response = new JeddictChatModel(handler).generateDescription(null, treePath.getCompilationUnit().toString(), treePath.getLeaf() instanceof MethodTree ? treePath.getLeaf().toString() : null, prevChat, question); } -// response = removeCodeBlockMarkers(response); - if (responseHistory.isEmpty() || !response.equals(responseHistory.get(responseHistory.size() - 1))) { - responseHistory.add(response); - currentResponseIndex = responseHistory.size() - 1; + + if (response != null && !response.isEmpty()) { + response = removeCodeBlockMarkers(response); + handler.onComplete(response); } - String finalResponse = response; - SwingUtilities.invokeLater(() -> { - sourceCode = EditorUtil.updateEditors(topComponent, finalResponse); - submitButton.setIcon(startIcon); - submitButton.setEnabled(true); - saveButton.setEnabled(sourceCode != null); - updateButtons(prevButton, nextButton); - questionPane.setText(""); - }); } catch (Exception e) { e.printStackTrace(); submitButton.setIcon(startIcon); diff --git a/src/main/java/io/github/jeddict/ai/hints/MethodFix.java b/src/main/java/io/github/jeddict/ai/hints/MethodFix.java index 26b1cbc..db056cf 100644 --- a/src/main/java/io/github/jeddict/ai/hints/MethodFix.java +++ b/src/main/java/io/github/jeddict/ai/hints/MethodFix.java @@ -23,8 +23,9 @@ import com.sun.source.tree.Tree; import static com.sun.source.tree.Tree.Kind.METHOD; import com.sun.source.util.TreePath; +import io.github.jeddict.ai.JeddictUpdateManager; import io.github.jeddict.ai.completion.Action; -import io.github.jeddict.ai.JeddictChatModel; +import io.github.jeddict.ai.lang.JeddictChatModel; import io.github.jeddict.ai.util.SourceUtil; import static io.github.jeddict.ai.util.SourceUtil.geIndentaion; import static io.github.jeddict.ai.util.StringUtil.removeCodeBlockMarkers; @@ -67,11 +68,11 @@ public MethodFix(TreePathHandle tpHandle, String compliationError, String action @Override protected String getText() { if (action == Action.COMPILATION_ERROR) { - return NbBundle.getMessage(JeddictChatModel.class, "HINT_METHOD_COMPILATION_ERROR", actionTitleParam); + return NbBundle.getMessage(JeddictUpdateManager.class, "HINT_METHOD_COMPILATION_ERROR", actionTitleParam); } else if (action == Action.ENHANCE) { - return NbBundle.getMessage(JeddictChatModel.class, "HINT_METHOD_ENHANCE"); + return NbBundle.getMessage(JeddictUpdateManager.class, "HINT_METHOD_ENHANCE"); } else { - return NbBundle.getMessage(JeddictChatModel.class, "HINT_METHOD_QUERY"); + return NbBundle.getMessage(JeddictUpdateManager.class, "HINT_METHOD_QUERY"); } } diff --git a/src/main/java/io/github/jeddict/ai/hints/RestEndpointFix.java b/src/main/java/io/github/jeddict/ai/hints/RestEndpointFix.java index d5ebaea..8023881 100644 --- a/src/main/java/io/github/jeddict/ai/hints/RestEndpointFix.java +++ b/src/main/java/io/github/jeddict/ai/hints/RestEndpointFix.java @@ -23,8 +23,9 @@ import static com.sun.source.tree.Tree.Kind.CLASS; import static com.sun.source.tree.Tree.Kind.INTERFACE; import com.sun.source.util.TreePath; +import io.github.jeddict.ai.JeddictUpdateManager; import io.github.jeddict.ai.completion.Action; -import io.github.jeddict.ai.JeddictChatModel; +import io.github.jeddict.ai.lang.JeddictChatModel; import io.github.jeddict.ai.util.SourceUtil; import static io.github.jeddict.ai.util.StringUtil.removeCodeBlockMarkers; import javax.lang.model.element.Element; @@ -54,7 +55,7 @@ public RestEndpointFix(TreePathHandle tpHandle, Action action, ElementHandle cla @Override protected String getText() { - return NbBundle.getMessage(JeddictChatModel.class, "HINT_REST_ENDPOINT"); + return NbBundle.getMessage(JeddictUpdateManager.class, "HINT_REST_ENDPOINT"); } @Override diff --git a/src/main/java/io/github/jeddict/ai/hints/TextFix.java b/src/main/java/io/github/jeddict/ai/hints/TextFix.java index 7ce8408..09683c9 100644 --- a/src/main/java/io/github/jeddict/ai/hints/TextFix.java +++ b/src/main/java/io/github/jeddict/ai/hints/TextFix.java @@ -22,8 +22,9 @@ import com.sun.source.tree.Tree; import static com.sun.source.tree.Tree.Kind.STRING_LITERAL; import com.sun.source.util.TreePath; +import io.github.jeddict.ai.JeddictUpdateManager; import io.github.jeddict.ai.completion.Action; -import io.github.jeddict.ai.JeddictChatModel; +import io.github.jeddict.ai.lang.JeddictChatModel; import org.netbeans.api.java.source.JavaSource; import org.netbeans.api.java.source.TreePathHandle; import org.netbeans.api.java.source.WorkingCopy; @@ -48,9 +49,9 @@ public TextFix(TreePathHandle tpHandle, Action action, TreePath treePath) { @Override protected String getText() { if (action == Action.ENHANCE) { - return NbBundle.getMessage(JeddictChatModel.class, "HINT_ENHANCE_TEXT"); + return NbBundle.getMessage(JeddictUpdateManager.class, "HINT_ENHANCE_TEXT"); } else { - return NbBundle.getMessage(JeddictChatModel.class, "HINT_FIX_GRAMMAR"); + return NbBundle.getMessage(JeddictUpdateManager.class, "HINT_FIX_GRAMMAR"); } } diff --git a/src/main/java/io/github/jeddict/ai/hints/VariableFix.java b/src/main/java/io/github/jeddict/ai/hints/VariableFix.java index 1846ad8..0ccf059 100644 --- a/src/main/java/io/github/jeddict/ai/hints/VariableFix.java +++ b/src/main/java/io/github/jeddict/ai/hints/VariableFix.java @@ -20,8 +20,9 @@ import com.sun.source.tree.Tree; import com.sun.source.util.TreePath; +import io.github.jeddict.ai.JeddictUpdateManager; import io.github.jeddict.ai.completion.Action; -import io.github.jeddict.ai.JeddictChatModel; +import io.github.jeddict.ai.lang.JeddictChatModel; import io.github.jeddict.ai.util.SourceUtil; import static io.github.jeddict.ai.util.StringUtil.removeCodeBlockMarkers; import javax.lang.model.element.Element; @@ -60,7 +61,7 @@ public VariableFix(TreePathHandle tpHandle, String compliationError, String acti @Override protected String getText() { - return NbBundle.getMessage(JeddictChatModel.class, "HINT_VARIABLE_COMPILATION_ERROR", actionTitleParam); + return NbBundle.getMessage(JeddictUpdateManager.class, "HINT_VARIABLE_COMPILATION_ERROR", actionTitleParam); } @Override diff --git a/src/main/java/io/github/jeddict/ai/hints/VariableNameFix.java b/src/main/java/io/github/jeddict/ai/hints/VariableNameFix.java index 9b07afd..655bf45 100644 --- a/src/main/java/io/github/jeddict/ai/hints/VariableNameFix.java +++ b/src/main/java/io/github/jeddict/ai/hints/VariableNameFix.java @@ -28,8 +28,9 @@ import com.sun.source.tree.VariableTree; import com.sun.source.util.TreePath; import com.sun.source.util.TreeScanner; +import io.github.jeddict.ai.JeddictUpdateManager; import io.github.jeddict.ai.completion.Action; -import io.github.jeddict.ai.JeddictChatModel; +import io.github.jeddict.ai.lang.JeddictChatModel; import java.util.List; import java.util.stream.Collectors; import javax.lang.model.element.Element; @@ -59,7 +60,7 @@ public VariableNameFix(TreePathHandle tpHandle, Action action, TreePath treePath @Override protected String getText() { - return NbBundle.getMessage(JeddictChatModel.class, "HINT_VARIABLE_NAME_ENHANCE"); + return NbBundle.getMessage(JeddictUpdateManager.class, "HINT_VARIABLE_NAME_ENHANCE"); } @Override diff --git a/src/main/java/io/github/jeddict/ai/JeddictChatModel.java b/src/main/java/io/github/jeddict/ai/lang/JeddictChatModel.java similarity index 89% rename from src/main/java/io/github/jeddict/ai/JeddictChatModel.java rename to src/main/java/io/github/jeddict/ai/lang/JeddictChatModel.java index 6c2020f..cc380c9 100644 --- a/src/main/java/io/github/jeddict/ai/JeddictChatModel.java +++ b/src/main/java/io/github/jeddict/ai/lang/JeddictChatModel.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package io.github.jeddict.ai; +package io.github.jeddict.ai.lang; /** * @@ -24,18 +24,31 @@ */ import com.sun.source.tree.Tree; import com.sun.source.util.TreePath; +import dev.langchain4j.model.StreamingResponseHandler; import dev.langchain4j.model.anthropic.AnthropicChatModel; +import dev.langchain4j.model.anthropic.AnthropicStreamingChatModel; import dev.langchain4j.model.chat.ChatLanguageModel; +import dev.langchain4j.model.chat.StreamingChatLanguageModel; import dev.langchain4j.model.googleai.GoogleAiGeminiChatModel; import dev.langchain4j.model.localai.LocalAiChatModel; +import dev.langchain4j.model.localai.LocalAiStreamingChatModel; import dev.langchain4j.model.mistralai.MistralAiChatModel; +import dev.langchain4j.model.mistralai.MistralAiStreamingChatModel; import dev.langchain4j.model.ollama.OllamaChatModel; +import dev.langchain4j.model.ollama.OllamaStreamingChatModel; import dev.langchain4j.model.openai.OpenAiChatModel; -import static io.github.jeddict.ai.settings.GenAIProvider.ANTHROPIC; -import static io.github.jeddict.ai.settings.GenAIProvider.OLLAMA; +import dev.langchain4j.model.openai.OpenAiStreamingChatModel; import io.github.jeddict.ai.models.LMStudioChatModel; +import static io.github.jeddict.ai.settings.GenAIProvider.ANTHROPIC; +import static io.github.jeddict.ai.settings.GenAIProvider.CUSTOM_OPEN_AI; import static io.github.jeddict.ai.settings.GenAIProvider.DEEPINFRA; +import static io.github.jeddict.ai.settings.GenAIProvider.DEEPSEEK; +import static io.github.jeddict.ai.settings.GenAIProvider.GOOGLE; +import static io.github.jeddict.ai.settings.GenAIProvider.GPT4ALL; +import static io.github.jeddict.ai.settings.GenAIProvider.GROQ; import static io.github.jeddict.ai.settings.GenAIProvider.LM_STUDIO; +import static io.github.jeddict.ai.settings.GenAIProvider.MISTRAL; +import static io.github.jeddict.ai.settings.GenAIProvider.OLLAMA; import static io.github.jeddict.ai.settings.GenAIProvider.OPEN_AI; import io.github.jeddict.ai.settings.PreferencesManager; import static io.github.jeddict.ai.util.MimeUtil.MIME_TYPE_DESCRIPTIONS; @@ -58,66 +71,123 @@ public class JeddictChatModel { private ChatLanguageModel model; + private StreamingChatLanguageModel streamModel; private int cachedClassDatasLength = -1; // Cache the length of classDatas - PreferencesManager preferencesManager = PreferencesManager.getInstance(); - + private PreferencesManager preferencesManager = PreferencesManager.getInstance(); + private StreamingResponseHandler handler; + public JeddictChatModel() { + this(null); + } + + public JeddictChatModel(StreamingResponseHandler handler) { + this.handler = handler; if (null != preferencesManager.getModel()) { - switch (preferencesManager.getProvider()) { - case GOOGLE -> - model = GoogleAiGeminiChatModel.builder() - .apiKey(preferencesManager.getApiKey()) - .modelName(preferencesManager.getModelName()) - .build(); - case OPEN_AI -> - model = OpenAiChatModel.builder() - .apiKey(preferencesManager.getApiKey()) - .modelName(preferencesManager.getModelName()) - .build(); - case DEEPINFRA, DEEPSEEK, GROQ, CUSTOM_OPEN_AI -> - model = OpenAiChatModel.builder() - .baseUrl(preferencesManager.getProviderLocation()) - .apiKey(preferencesManager.getApiKey()) - .modelName(preferencesManager.getModelName()) - .build(); - case MISTRAL -> - model = MistralAiChatModel.builder() - .apiKey(preferencesManager.getApiKey()) - .modelName(preferencesManager.getModelName()) - .build(); - case ANTHROPIC -> - model = AnthropicChatModel.builder() - .apiKey(preferencesManager.getApiKey()) - .modelName(preferencesManager.getModelName()) - .build(); - case OLLAMA -> - model = OllamaChatModel.builder() - .baseUrl(preferencesManager.getProviderLocation()) - .modelName(preferencesManager.getModelName()) - .build(); - case LM_STUDIO -> - model = LMStudioChatModel.builder() - .baseUrl(preferencesManager.getProviderLocation()) - .modelName(preferencesManager.getModelName()) - .build(); - case GPT4ALL -> - model = LocalAiChatModel.builder() - .baseUrl(preferencesManager.getProviderLocation()) - .modelName(preferencesManager.getModelName()) - .build(); + if (preferencesManager.isStreamEnabled() && handler != null) { + switch (preferencesManager.getProvider()) { + case GOOGLE -> + model = GoogleAiGeminiChatModel.builder() + .apiKey(preferencesManager.getApiKey()) + .modelName(preferencesManager.getModelName()) + .build(); + case OPEN_AI -> + streamModel = OpenAiStreamingChatModel.builder() + .apiKey(preferencesManager.getApiKey()) + .modelName(preferencesManager.getModelName()) + .build(); + case DEEPINFRA, DEEPSEEK, GROQ, CUSTOM_OPEN_AI -> + streamModel = OpenAiStreamingChatModel.builder() + .baseUrl(preferencesManager.getProviderLocation()) + .apiKey(preferencesManager.getApiKey()) + .modelName(preferencesManager.getModelName()) + .build(); + case MISTRAL -> + streamModel = MistralAiStreamingChatModel.builder() + .apiKey(preferencesManager.getApiKey()) + .modelName(preferencesManager.getModelName()) + .build(); + case ANTHROPIC -> + streamModel = AnthropicStreamingChatModel.builder() + .apiKey(preferencesManager.getApiKey()) + .modelName(preferencesManager.getModelName()) + .build(); + case OLLAMA -> + streamModel = OllamaStreamingChatModel.builder() + .baseUrl(preferencesManager.getProviderLocation()) + .modelName(preferencesManager.getModelName()) + .build(); + case LM_STUDIO -> + model = LMStudioChatModel.builder() + .baseUrl(preferencesManager.getProviderLocation()) + .modelName(preferencesManager.getModelName()) + .build(); + case GPT4ALL -> + streamModel = LocalAiStreamingChatModel.builder() + .baseUrl(preferencesManager.getProviderLocation()) + .modelName(preferencesManager.getModelName()) + .build(); + } + } else { + switch (preferencesManager.getProvider()) { + case GOOGLE -> + model = GoogleAiGeminiChatModel.builder() + .apiKey(preferencesManager.getApiKey()) + .modelName(preferencesManager.getModelName()) + .build(); + case OPEN_AI -> + model = OpenAiChatModel.builder() + .apiKey(preferencesManager.getApiKey()) + .modelName(preferencesManager.getModelName()) + .build(); + case DEEPINFRA, DEEPSEEK, GROQ, CUSTOM_OPEN_AI -> + model = OpenAiChatModel.builder() + .baseUrl(preferencesManager.getProviderLocation()) + .apiKey(preferencesManager.getApiKey()) + .modelName(preferencesManager.getModelName()) + .build(); + case MISTRAL -> + model = MistralAiChatModel.builder() + .apiKey(preferencesManager.getApiKey()) + .modelName(preferencesManager.getModelName()) + .build(); + case ANTHROPIC -> + model = AnthropicChatModel.builder() + .apiKey(preferencesManager.getApiKey()) + .modelName(preferencesManager.getModelName()) + .build(); + case OLLAMA -> + model = OllamaChatModel.builder() + .baseUrl(preferencesManager.getProviderLocation()) + .modelName(preferencesManager.getModelName()) + .build(); + case LM_STUDIO -> + model = LMStudioChatModel.builder() + .baseUrl(preferencesManager.getProviderLocation()) + .modelName(preferencesManager.getModelName()) + .build(); + case GPT4ALL -> + model = LocalAiChatModel.builder() + .baseUrl(preferencesManager.getProviderLocation()) + .modelName(preferencesManager.getModelName()) + .build(); + } } } } private String generate(String prompt) { - if (model == null) { + if (model == null && handler == null) { JOptionPane.showMessageDialog(null, "AI assistance model not intitalized.", "Error in AI Assistance", JOptionPane.ERROR_MESSAGE); } try { - return model.generate(prompt); + if (streamModel != null) { + streamModel.generate(prompt, handler); + } else { + return model.generate(prompt); + } } catch (Exception e) { String errorMessage = e.getMessage(); if (e.getCause() != null @@ -974,7 +1044,7 @@ public String generateTestCase( promptExtend.append("Java Class Content:\n").append(classContent).append("\n\n") .append("Generate ").append(testCaseType).append(" test cases for this class. Include assertions and necessary mock setups. "); } - + promptBuilder.append("You are an API server that provides "); String rules = preferencesManager.getCommonPromptRules(); if (rules != null && !rules.isEmpty()) { diff --git a/src/main/java/io/github/jeddict/ai/lang/JeddictStreamHandler.java b/src/main/java/io/github/jeddict/ai/lang/JeddictStreamHandler.java new file mode 100644 index 0000000..697cd2e --- /dev/null +++ b/src/main/java/io/github/jeddict/ai/lang/JeddictStreamHandler.java @@ -0,0 +1,72 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.github.jeddict.ai.lang; + +import dev.langchain4j.data.message.AiMessage; +import dev.langchain4j.model.StreamingResponseHandler; +import dev.langchain4j.model.output.Response; +import io.github.jeddict.ai.components.AssistantTopComponent; +import static io.github.jeddict.ai.util.StringUtil.removeCodeBlockMarkers; +import javax.swing.JEditorPane; +import javax.swing.SwingUtilities; + +/** + * + * @author Shiwani Gupta + */ +public abstract class JeddictStreamHandler implements StreamingResponseHandler { + + private final AssistantTopComponent topComponent; + private boolean init = true; + private JEditorPane editorPane; + + public JeddictStreamHandler(AssistantTopComponent topComponent) { + this.topComponent = topComponent; + } + + @Override + public void onNext(String string) { + if (init) { + topComponent.clear(); + editorPane = topComponent.createPane(); + editorPane.setText(string); + init = false; + } else { + String partial = editorPane.getText() + string; + editorPane.setText(partial); + } + } + + @Override + public void onError(Throwable thrwbl) { + } + + @Override + public void onComplete(Response out) { + SwingUtilities.invokeLater(() -> { + String response = out.content().text(); + if (response != null && !response.isEmpty()) { + response = removeCodeBlockMarkers(response); + onComplete(response); + } + }); + } + + public abstract void onComplete(String response); +} diff --git a/src/main/java/io/github/jeddict/ai/Snippet.java b/src/main/java/io/github/jeddict/ai/lang/Snippet.java similarity index 90% rename from src/main/java/io/github/jeddict/ai/Snippet.java rename to src/main/java/io/github/jeddict/ai/lang/Snippet.java index ea8f8f0..139aa89 100644 --- a/src/main/java/io/github/jeddict/ai/Snippet.java +++ b/src/main/java/io/github/jeddict/ai/lang/Snippet.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package io.github.jeddict.ai; +package io.github.jeddict.ai.lang; import java.util.ArrayList; import java.util.List; @@ -27,11 +27,11 @@ */ public class Snippet { - List imports = new ArrayList<>(); + private List imports = new ArrayList<>(); - String snippet; + private final String snippet; - String description; + private String description; public Snippet(String snippet, String description, List imports) { this.snippet = snippet; diff --git a/src/main/java/io/github/jeddict/ai/scanner/ProjectClassScanner.java b/src/main/java/io/github/jeddict/ai/scanner/ProjectClassScanner.java index 03559c8..df56a5e 100644 --- a/src/main/java/io/github/jeddict/ai/scanner/ProjectClassScanner.java +++ b/src/main/java/io/github/jeddict/ai/scanner/ProjectClassScanner.java @@ -23,7 +23,7 @@ import com.sun.source.tree.ModifiersTree; import com.sun.source.tree.Tree; import com.sun.source.tree.VariableTree; -import io.github.jeddict.ai.JeddictChatModel; +import io.github.jeddict.ai.lang.JeddictChatModel; import io.github.jeddict.ai.settings.AIClassContext; import org.netbeans.api.java.source.JavaSource; import org.netbeans.api.project.Project; diff --git a/src/main/java/io/github/jeddict/ai/settings/AIAssistancePanel.form b/src/main/java/io/github/jeddict/ai/settings/AIAssistancePanel.form index 0c7db09..a958548 100644 --- a/src/main/java/io/github/jeddict/ai/settings/AIAssistancePanel.form +++ b/src/main/java/io/github/jeddict/ai/settings/AIAssistancePanel.form @@ -37,17 +37,14 @@ - - - - + - - + + @@ -298,27 +295,6 @@ - - - - - - - - - - - - - - - - - - - - - @@ -326,7 +302,7 @@ - + @@ -351,7 +327,7 @@ - + @@ -368,7 +344,7 @@ - + @@ -444,6 +420,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/java/io/github/jeddict/ai/settings/AIAssistancePanel.java b/src/main/java/io/github/jeddict/ai/settings/AIAssistancePanel.java index ceaadb2..325b6b0 100644 --- a/src/main/java/io/github/jeddict/ai/settings/AIAssistancePanel.java +++ b/src/main/java/io/github/jeddict/ai/settings/AIAssistancePanel.java @@ -86,8 +86,6 @@ private void initComponents() { aiAssistantActivationCheckBox = new javax.swing.JCheckBox(); enableHintCheckBox = new javax.swing.JCheckBox(); enableSmartCodeCheckBox = new javax.swing.JCheckBox(); - excludeJavadocCommentsPane = new javax.swing.JLayeredPane(); - excludeJavadocCommentsCheckBox = new javax.swing.JCheckBox(); askAIPane = new javax.swing.JLayeredPane(); fileFilterationPane = new javax.swing.JLayeredPane(); fileExtLabel = new javax.swing.JLabel(); @@ -97,6 +95,9 @@ private void initComponents() { jLayeredPane2 = new javax.swing.JLayeredPane(); jScrollPane2 = new javax.swing.JScrollPane(); excludeDirTable = new javax.swing.JTable(); + jLayeredPane1 = new javax.swing.JLayeredPane(); + enableStreamCheckbox = new javax.swing.JCheckBox(); + excludeJavadocCommentsCheckBox = new javax.swing.JCheckBox(); inlineCompletionPane = new javax.swing.JLayeredPane(); classContextPane = new javax.swing.JLayeredPane(); classContextLabelPane = new javax.swing.JLayeredPane(); @@ -235,19 +236,6 @@ public void actionPerformed(java.awt.event.ActionEvent evt) { activationParentPane.add(activationPane); - excludeJavadocCommentsPane.setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.LEFT)); - - org.openide.awt.Mnemonics.setLocalizedText(excludeJavadocCommentsCheckBox, org.openide.util.NbBundle.getMessage(AIAssistancePanel.class, "AIAssistancePanel.excludeJavadocCommentsCheckBox.text")); // NOI18N - excludeJavadocCommentsCheckBox.setToolTipText(org.openide.util.NbBundle.getMessage(AIAssistancePanel.class, "AIAssistancePanel.excludeJavadocCommentsCheckBox.toolTipText")); // NOI18N - excludeJavadocCommentsCheckBox.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - excludeJavadocCommentsCheckBoxActionPerformed(evt); - } - }); - excludeJavadocCommentsPane.add(excludeJavadocCommentsCheckBox); - - activationParentPane.add(excludeJavadocCommentsPane); - providersPane.add(activationParentPane); jTabbedPane1.addTab(org.openide.util.NbBundle.getMessage(AIAssistancePanel.class, "AIAssistancePanel.providersPane.TabConstraints.tabTitle"), providersPane); // NOI18N @@ -279,7 +267,7 @@ public void actionPerformed(java.awt.event.ActionEvent evt) { fileFilterationPane.setLayout(fileFilterationPaneLayout); fileFilterationPaneLayout.setHorizontalGroup( fileFilterationPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(jScrollPane3, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 543, Short.MAX_VALUE) + .addComponent(jScrollPane3, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 558, Short.MAX_VALUE) .addGroup(fileFilterationPaneLayout.createSequentialGroup() .addContainerGap() .addComponent(fileExtLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 201, javax.swing.GroupLayout.PREFERRED_SIZE) @@ -293,7 +281,7 @@ public void actionPerformed(java.awt.event.ActionEvent evt) { .addGroup(fileFilterationPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(fileExtLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(jLabel1)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 7, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(jScrollPane3, javax.swing.GroupLayout.PREFERRED_SIZE, 80, javax.swing.GroupLayout.PREFERRED_SIZE) .addContainerGap()) ); @@ -309,6 +297,23 @@ public void actionPerformed(java.awt.event.ActionEvent evt) { askAIPane.add(jLayeredPane2); + jLayeredPane1.setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.LEFT, 15, 5)); + + org.openide.awt.Mnemonics.setLocalizedText(enableStreamCheckbox, org.openide.util.NbBundle.getMessage(AIAssistancePanel.class, "AIAssistancePanel.enableStreamCheckbox.text")); // NOI18N + enableStreamCheckbox.setToolTipText(org.openide.util.NbBundle.getMessage(AIAssistancePanel.class, "AIAssistancePanel.enableStreamCheckbox.toolTipText")); // NOI18N + jLayeredPane1.add(enableStreamCheckbox); + + org.openide.awt.Mnemonics.setLocalizedText(excludeJavadocCommentsCheckBox, org.openide.util.NbBundle.getMessage(AIAssistancePanel.class, "AIAssistancePanel.excludeJavadocCommentsCheckBox.text")); // NOI18N + excludeJavadocCommentsCheckBox.setToolTipText(org.openide.util.NbBundle.getMessage(AIAssistancePanel.class, "AIAssistancePanel.excludeJavadocCommentsCheckBox.toolTipText")); // NOI18N + excludeJavadocCommentsCheckBox.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + excludeJavadocCommentsCheckBoxActionPerformed(evt); + } + }); + jLayeredPane1.add(excludeJavadocCommentsCheckBox); + + askAIPane.add(jLayeredPane1); + jTabbedPane1.addTab(org.openide.util.NbBundle.getMessage(AIAssistancePanel.class, "AIAssistancePanel.askAIPane.TabConstraints.tabTitle"), askAIPane); // NOI18N inlineCompletionPane.setLayout(new java.awt.GridLayout(4, 1)); @@ -420,18 +425,32 @@ public void actionPerformed(java.awt.event.ActionEvent evt) { this.setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addComponent(jTabbedPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 543, Short.MAX_VALUE) - .addContainerGap()) + .addComponent(jTabbedPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 558, Short.MAX_VALUE) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() - .addComponent(jTabbedPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 344, javax.swing.GroupLayout.PREFERRED_SIZE) - .addContainerGap(80, Short.MAX_VALUE)) + .addComponent(jTabbedPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 329, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(0, 95, Short.MAX_VALUE)) ); }// //GEN-END:initComponents + private void cleanDataButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cleanDataButtonActionPerformed + ProjectClassScanner.clear(); + JOptionPane.showMessageDialog(this, "Cache has been cleared successfully!", "Information", JOptionPane.INFORMATION_MESSAGE); + }//GEN-LAST:event_cleanDataButtonActionPerformed + + private void showDescriptionCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_showDescriptionCheckBoxActionPerformed + // TODO add your handling code here: + }//GEN-LAST:event_showDescriptionCheckBoxActionPerformed + + private void varContextComboBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_varContextComboBoxActionPerformed + AIClassContext selectedContext = (AIClassContext) varContextComboBox.getSelectedItem(); + if (selectedContext != null) { + varContextHelp.setText(selectedContext.getDescription()); + } + }//GEN-LAST:event_varContextComboBoxActionPerformed + private void classContextComboBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_classContextComboBoxActionPerformed AIClassContext selectedContext = (AIClassContext) classContextComboBox.getSelectedItem(); if (selectedContext != null) { @@ -439,6 +458,14 @@ private void classContextComboBoxActionPerformed(java.awt.event.ActionEvent evt) } }//GEN-LAST:event_classContextComboBoxActionPerformed + private void excludeJavadocCommentsCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_excludeJavadocCommentsCheckBoxActionPerformed + // TODO add your handling code here: + }//GEN-LAST:event_excludeJavadocCommentsCheckBoxActionPerformed + + private void enableHintCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_enableHintCheckBoxActionPerformed + // TODO add your handling code here: + }//GEN-LAST:event_enableHintCheckBoxActionPerformed + private void aiAssistantActivationCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_aiAssistantActivationCheckBoxActionPerformed // if unchecked then disable the hint and smart checkbox if (!aiAssistantActivationCheckBox.isSelected()) { @@ -450,10 +477,6 @@ private void aiAssistantActivationCheckBoxActionPerformed(java.awt.event.ActionE } }//GEN-LAST:event_aiAssistantActivationCheckBoxActionPerformed - private void enableHintCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_enableHintCheckBoxActionPerformed - // TODO add your handling code here: - }//GEN-LAST:event_enableHintCheckBoxActionPerformed - private void modelComboBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_modelComboBoxActionPerformed String selectedContext = (String) modelComboBox.getSelectedItem(); if (selectedContext != null && getModel(selectedContext) != null) { @@ -463,21 +486,19 @@ private void modelComboBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN } }//GEN-LAST:event_modelComboBoxActionPerformed - private void cleanDataButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cleanDataButtonActionPerformed - ProjectClassScanner.clear(); - JOptionPane.showMessageDialog(this, "Cache has been cleared successfully!", "Information", JOptionPane.INFORMATION_MESSAGE); - }//GEN-LAST:event_cleanDataButtonActionPerformed - - private void showDescriptionCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_showDescriptionCheckBoxActionPerformed - // TODO add your handling code here: - }//GEN-LAST:event_showDescriptionCheckBoxActionPerformed + private void apiKeyFieldFocusLost(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_apiKeyFieldFocusLost + GenAIProvider selectedProvider = (GenAIProvider) providerComboBox.getSelectedItem(); + if (selectedProvider != null) { + updateModelComboBox(selectedProvider); + } + }//GEN-LAST:event_apiKeyFieldFocusLost private void providerComboBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_providerComboBoxActionPerformed GenAIProvider selectedProvider = (GenAIProvider) providerComboBox.getSelectedItem(); if (selectedProvider == GenAIProvider.DEEPINFRA - || selectedProvider == GenAIProvider.DEEPSEEK - || selectedProvider == GenAIProvider.GROQ - || selectedProvider == GenAIProvider.CUSTOM_OPEN_AI) { + || selectedProvider == GenAIProvider.DEEPSEEK + || selectedProvider == GenAIProvider.GROQ + || selectedProvider == GenAIProvider.CUSTOM_OPEN_AI) { apiKeyLabel.setVisible(true); apiKeyField.setVisible(true); apiKeyPane.setVisible(true); @@ -494,9 +515,9 @@ private void providerComboBoxActionPerformed(java.awt.event.ActionEvent evt) {// providerLocationField.setVisible(true); providerLocationPane.setVisible(true); } else if (selectedProvider == GenAIProvider.GOOGLE - || selectedProvider == GenAIProvider.OPEN_AI - || selectedProvider == GenAIProvider.MISTRAL - || selectedProvider == GenAIProvider.ANTHROPIC) { + || selectedProvider == GenAIProvider.OPEN_AI + || selectedProvider == GenAIProvider.MISTRAL + || selectedProvider == GenAIProvider.ANTHROPIC) { apiKeyLabel.setVisible(true); apiKeyField.setVisible(true); apiKeyPane.setVisible(true); @@ -515,18 +536,18 @@ private void providerComboBoxActionPerformed(java.awt.event.ActionEvent evt) {// if (null != selectedProvider) { switch (selectedProvider) { case OLLAMA -> - providerLocationField.setText(new OllamaModelFetcher().getAPIUrl()); + providerLocationField.setText(new OllamaModelFetcher().getAPIUrl()); case LM_STUDIO -> - providerLocationField.setText(new LMStudioModelFetcher().getAPIUrl()); + providerLocationField.setText(new LMStudioModelFetcher().getAPIUrl()); case GPT4ALL -> - providerLocationField.setText(new GPT4AllModelFetcher().getAPIUrl()); + providerLocationField.setText(new GPT4AllModelFetcher().getAPIUrl()); } } } apiKeyField.setText(preferencesManager.getApiKey((GenAIProvider) providerComboBox.getSelectedItem())); if (apiKeyLabel.isVisible() - && selectedProvider != null - && !selectedProvider.getApiKeyUrl().isEmpty()) { + && selectedProvider != null + && !selectedProvider.getApiKeyUrl().isEmpty()) { String apiKeyUrl = selectedProvider.getApiKeyUrl(); apiKeyInfo.setText("" + apiKeyUrl + ""); apiKeyInfo.setCursor(new Cursor(Cursor.HAND_CURSOR)); @@ -552,7 +573,7 @@ public void mouseExited(MouseEvent e) { }); } if (selectedProvider != null - && !selectedProvider.getModelInfoUrl().isEmpty()) { + && !selectedProvider.getModelInfoUrl().isEmpty()) { String modelInfoUrl = selectedProvider.getModelInfoUrl(); modelsInfo.setText("" + modelInfoUrl + ""); modelsInfo.setCursor(new Cursor(Cursor.HAND_CURSOR)); @@ -582,24 +603,6 @@ public void mouseExited(MouseEvent e) { } }//GEN-LAST:event_providerComboBoxActionPerformed - private void apiKeyFieldFocusLost(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_apiKeyFieldFocusLost - GenAIProvider selectedProvider = (GenAIProvider) providerComboBox.getSelectedItem(); - if (selectedProvider != null) { - updateModelComboBox(selectedProvider); - } - }//GEN-LAST:event_apiKeyFieldFocusLost - - private void excludeJavadocCommentsCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_excludeJavadocCommentsCheckBoxActionPerformed - // TODO add your handling code here: - }//GEN-LAST:event_excludeJavadocCommentsCheckBoxActionPerformed - - private void varContextComboBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_varContextComboBoxActionPerformed - AIClassContext selectedContext = (AIClassContext) varContextComboBox.getSelectedItem(); - if (selectedContext != null) { - varContextHelp.setText(selectedContext.getDescription()); - } - }//GEN-LAST:event_varContextComboBoxActionPerformed - private void updateModelComboBox(GenAIProvider selectedProvider) { modelComboBox.removeAllItems(); for (String model : getModelList(selectedProvider)) { @@ -659,6 +662,7 @@ void load() { modelComboBox.setSelectedItem(preferencesManager.getModel()); showDescriptionCheckBox.setSelected(preferencesManager.isDescriptionEnabled()); fileExtField.setText(preferencesManager.getFileExtensionToInclude()); + enableStreamCheckbox.setSelected(preferencesManager.isStreamEnabled()); excludeJavadocCommentsCheckBox.setSelected(preferencesManager.isExcludeJavadocEnabled()); testPromptField.setText(preferencesManager.getTestCasePrompt()); @@ -692,6 +696,7 @@ void store() { preferencesManager.setDescriptionEnabled(showDescriptionCheckBox.isSelected()); preferencesManager.setFileExtensionToInclude(fileExtField.getText()); preferencesManager.setExcludeDirs(getCommaSeparatedValues(excludeTableModel)); + preferencesManager.setStreamEnabled(enableStreamCheckbox.isSelected()); preferencesManager.setExcludeJavadocEnabled(excludeJavadocCommentsCheckBox.isSelected()); preferencesManager.setTestCasePrompt(testPromptField.getText()); @@ -806,9 +811,9 @@ boolean valid() { private javax.swing.JButton cleanDataButton; private javax.swing.JCheckBox enableHintCheckBox; private javax.swing.JCheckBox enableSmartCodeCheckBox; + private javax.swing.JCheckBox enableStreamCheckbox; private javax.swing.JTable excludeDirTable; private javax.swing.JCheckBox excludeJavadocCommentsCheckBox; - private javax.swing.JLayeredPane excludeJavadocCommentsPane; private javax.swing.JTextArea fileExtField; private javax.swing.JLabel fileExtLabel; private javax.swing.JLayeredPane fileFilterationPane; @@ -817,6 +822,7 @@ boolean valid() { private javax.swing.JLayeredPane hintPane; private javax.swing.JLayeredPane inlineCompletionPane; private javax.swing.JLabel jLabel1; + private javax.swing.JLayeredPane jLayeredPane1; private javax.swing.JLayeredPane jLayeredPane2; private javax.swing.JScrollPane jScrollPane2; private javax.swing.JScrollPane jScrollPane3; diff --git a/src/main/java/io/github/jeddict/ai/settings/PreferencesManager.java b/src/main/java/io/github/jeddict/ai/settings/PreferencesManager.java index 7f2ec05..de02ca2 100644 --- a/src/main/java/io/github/jeddict/ai/settings/PreferencesManager.java +++ b/src/main/java/io/github/jeddict/ai/settings/PreferencesManager.java @@ -41,6 +41,8 @@ public class PreferencesManager { private static final String PROVIDER_LOCATION_PREFERENCES = "provider_location"; private static final String PROVIDER_PREFERENCE = "provider"; private static final String MODEL_PREFERENCE = "model"; + private static final String COMMON_PROMPT_RULES_PREFERENCE = "commonPromptRules"; + private static final String ENABLE_STREAM_PREFERENCE = "enableStream"; private final List EXCLUDE_DIR_DEFAULT = Arrays.asList( // Test Resources @@ -77,7 +79,6 @@ public class PreferencesManager { "trace", "cache", "backup", - // Other Configuration/Files ".env", // Environment variable definitions "docker-compose.yml", // Docker Compose config @@ -100,14 +101,12 @@ public class PreferencesManager { "CODE_OF_CONDUCT.md", "CONTRIBUTING.md", "LICENSE", - // Security and Configuration Files "secrets", // Directory for secrets "credentials", // Directory for credentials "private", "confidential", "vault", - // CI configuration ".gitlab-ci.yml", ".travis.yml", @@ -129,17 +128,17 @@ public static PreferencesManager getInstance() { } public void clearApiKey() { - preferences.remove(getProvider().name()+ API_KEY_PREFERENCES); + preferences.remove(getProvider().name() + API_KEY_PREFERENCES); } public void setApiKey(String key) { - preferences.put(getProvider().name()+API_KEY_PREFERENCES, key); + preferences.put(getProvider().name() + API_KEY_PREFERENCES, key); } - + public String getApiKey() { return getApiKey(false); } - + String getApiKey(GenAIProvider provider) { return preferences.get(provider.name() + API_KEY_PREFERENCES, null); } @@ -153,7 +152,7 @@ public String getApiKey(boolean headless) { } if (apiKey == null || apiKey.isEmpty()) { // If not found in environment or system properties, check Preferences - apiKey = preferences.get(getProvider().name() +API_KEY_PREFERENCES, null); + apiKey = preferences.get(getProvider().name() + API_KEY_PREFERENCES, null); } if (apiKey == null || apiKey.isEmpty()) { @@ -359,11 +358,9 @@ public void setTestCasePrompt(String prompt) { preferences.put("testCasePrompt", prompt); } } - - private static final String COMMON_PROMPT_RULES_PREFERENCE = "commonPromptRules"; public String getCommonPromptRules() { - return preferences.get(COMMON_PROMPT_RULES_PREFERENCE, "Default rules for prompts."); + return preferences.get(COMMON_PROMPT_RULES_PREFERENCE, ""); } public void setCommonPromptRules(String rules) { @@ -372,4 +369,12 @@ public void setCommonPromptRules(String rules) { } } + public boolean isStreamEnabled() { + return preferences.getBoolean(ENABLE_STREAM_PREFERENCE, false); // Default to false + } + + public void setStreamEnabled(boolean enabled) { + preferences.putBoolean(ENABLE_STREAM_PREFERENCE, enabled); + } + } diff --git a/src/main/java/io/github/jeddict/ai/util/EditorUtil.java b/src/main/java/io/github/jeddict/ai/util/EditorUtil.java index bae9816..4c1520e 100644 --- a/src/main/java/io/github/jeddict/ai/util/EditorUtil.java +++ b/src/main/java/io/github/jeddict/ai/util/EditorUtil.java @@ -63,15 +63,14 @@ public static String updateEditors(AssistantTopComponent topComponent, String te code.append('\n').append(codeContent).append('\n'); editorPane = topComponent.createCodePane(getMimeType(codeType), codeContent); } else { - String html = renderer.render(parser.parse(parts[i].trim())); - editorPane = topComponent.createHtmlPane(html); + editorPane = topComponent.createCodePane(getMimeType(null), parts[i]); } } } - if (editorPane != null) { - editorPane.setCaretPosition(0); - } +// if (editorPane != null) { +// editorPane.setCaretPosition(0); +// } return code.toString(); } @@ -220,6 +219,9 @@ private static String breakLongLine(String line, int maxLineLength) { // Method to get the NetBeans MIME type for a given ChatGPT code block type public static String getMimeType(String chatGptType) { + if (chatGptType == null) { + return "text/plain"; + } return OPENAI_NETBEANS_EDITOR_MAP.getOrDefault(chatGptType, "text/plain"); // Default to binary if not found } diff --git a/src/main/resources/io/github/jeddict/ai/settings/Bundle.properties b/src/main/resources/io/github/jeddict/ai/settings/Bundle.properties index f2e3b21..4770c69 100644 --- a/src/main/resources/io/github/jeddict/ai/settings/Bundle.properties +++ b/src/main/resources/io/github/jeddict/ai/settings/Bundle.properties @@ -1,38 +1,40 @@ +AIAssistancePanel.excludeDir.text=Directories and Files to Exclude from Context (This setting is applicable at the Project Level) +AIAssistancePanel.testPromptField.text=Type the prompt for generating test cases based on your class context or project. +AIAssistancePanel.testPromptLabel.text=Generate Test Case Prompt: +AIAssistancePanel.hintPane.TabConstraints.tabTitle=Hints +AIAssistancePanel.cleanDataButton.text=Clear Cache +AIAssistancePanel.showDescriptionCheckBox.toolTipText=Show a description along with code snippets for improved understanding of AI-generated suggestions. Helps to explain the reasoning behind the suggested code. +AIAssistancePanel.showDescriptionCheckBox.text=Show Snippet with Description +AIAssistancePanel.varContextComboBox.toolTipText=Select the classes to share with the AI assistant for enhanced context and more accurate suggestions. +AIAssistancePanel.varContextHelp.text=Provide context for the assistant to suggest better variable names, method names, and strings based on your code structure. +AIAssistancePanel.varContextLabel.text=Class Context Scope for Variable Name, Method Name, String Literals: AIAssistancePanel.classContextComboBox.toolTipText=Select the classes to share with the AI assistant for enhanced context and more accurate suggestions. -AIAssistancePanel.aiAssistantActivationCheckBox.text=Activate AI Assistant AIAssistancePanel.classContextHelp.text=Sharing your class context enables the AI assistant to analyze and understand your codebase better. AIAssistancePanel.classContextLabel.text=Class Context Scope (Default): -AIAssistancePanel.cleanDataButton.text=Clear Cache -AIAssistancePanel.gptModelHelp.text=Choose a model based on your performance and accuracy requirements. -AIAssistancePanel.gptModelLabel.text=Model: -AIAssistancePanel.enableHintCheckBox.text=Enable Hints -AIAssistancePanel.aiAssistantActivationCheckBox.toolTipText=Turn the AI assistant on or off to enable smart code completion and suggestions for a better coding experience. -AIAssistancePanel.enableHintCheckBox.toolTipText=Activate hints and suggestions while you work to receive guidance in real time. +AIAssistancePanel.inlineCompletionPane.TabConstraints.tabTitle=Inline Completion +AIAssistancePanel.jLabel1.text=(This setting applies at both Project and Folder Levels) +AIAssistancePanel.fileExtField.text=e.g., java, xml, yaml +AIAssistancePanel.fileExtLabel.text=File Extensions to Include in Context: +AIAssistancePanel.askAIPane.TabConstraints.tabTitle=Chat +AIAssistancePanel.excludeJavadocCommentsCheckBox.toolTipText=Exclude Javadoc comments when generating context to focus solely on code and reduce token usage for better performance. Helps improve accuracy and avoid irrelevant suggestions. +AIAssistancePanel.excludeJavadocCommentsCheckBox.text=Exclude Javadoc Comments in Context AIAssistancePanel.enableSmartCodeCheckBox.toolTipText=Enable inline AI-powered smart code suggestions for a smoother and faster coding experience. AIAssistancePanel.enableSmartCodeCheckBox.text=Enable Inline Completion -AIAssistancePanel.showDescriptionCheckBox.toolTipText=Show a description along with code snippets for improved understanding of AI-generated suggestions. Helps to explain the reasoning behind the suggested code. -AIAssistancePanel.showDescriptionCheckBox.text=Show Snippet with Description -AIAssistancePanel.providerComboBox.toolTipText=Select the Provider for AI assistant. Providers can offer different types of models and capabilities, such as speed, context size, and accuracy. +AIAssistancePanel.enableHintCheckBox.toolTipText=Activate hints and suggestions while you work to receive guidance in real time. +AIAssistancePanel.enableHintCheckBox.text=Enable Hints +AIAssistancePanel.aiAssistantActivationCheckBox.toolTipText=Turn the AI assistant on or off to enable smart code completion and suggestions for a better coding experience. +AIAssistancePanel.aiAssistantActivationCheckBox.text=Activate AI Assistant +AIAssistancePanel.modelsInfo.text= AIAssistancePanel.modelComboBox.toolTipText=Choose the specific Model for AI assistant. Models may vary in complexity, speed, and token limits, allowing you to select the best fit for your task. -AIAssistancePanel.providerLabel.text=Provider: -AIAssistancePanel.providerLocationField.text= +AIAssistancePanel.gptModelHelp.text=Choose a model based on your performance and accuracy requirements. +AIAssistancePanel.gptModelLabel.text=Model: AIAssistancePanel.apiKeyField.text= +AIAssistancePanel.apiKeyInfo.text= AIAssistancePanel.apiKeyLabel.text=API Key: +AIAssistancePanel.providerLocationField.text= AIAssistancePanel.providerLocationLabel.text=URL: -AIAssistancePanel.modelsInfo.text= -AIAssistancePanel.apiKeyInfo.text= +AIAssistancePanel.providerComboBox.toolTipText=Select the Provider for AI assistant. Providers can offer different types of models and capabilities, such as speed, context size, and accuracy. +AIAssistancePanel.providerLabel.text=Provider: AIAssistancePanel.providersPane.TabConstraints.tabTitle=Providers -AIAssistancePanel.inlineCompletionPane.TabConstraints.tabTitle=Inline Completion -AIAssistancePanel.excludeJavadocCommentsCheckBox.text=Exclude Javadoc Comments in Context -AIAssistancePanel.excludeJavadocCommentsCheckBox.toolTipText=Exclude Javadoc comments when generating context to focus solely on code and reduce token usage for better performance. Helps improve accuracy and avoid irrelevant suggestions. -AIAssistancePanel.hintPane.TabConstraints.tabTitle=Hints -AIAssistancePanel.testPromptLabel.text=Generate Test Case Prompt: -AIAssistancePanel.testPromptField.text=Type the prompt for generating test cases based on your class context or project. -AIAssistancePanel.varContextComboBox.toolTipText=Select the classes to share with the AI assistant for enhanced context and more accurate suggestions. -AIAssistancePanel.varContextLabel.text=Class Context Scope for Variable Name, Method Name, String Literals: -AIAssistancePanel.varContextHelp.text=Provide context for the assistant to suggest better variable names, method names, and strings based on your code structure. -AIAssistancePanel.fileExtLabel.text=File Extensions to Include in Context: -AIAssistancePanel.askAIPane.TabConstraints.tabTitle=AI Chat -AIAssistancePanel.fileExtField.text=e.g., java, xml, yaml -AIAssistancePanel.jLabel1.text=(This setting applies at both Project and Folder Levels) -AIAssistancePanel.excludeDir.text=Directories and Files to Exclude from Context (This setting is applicable at the Project Level) +AIAssistancePanel.enableStreamCheckbox.toolTipText=Activating streaming mode allows for continuous data processing, which enhances the responsiveness of the AI assistant, making the chat experience feel more dynamic and interactive. +AIAssistancePanel.enableStreamCheckbox.text=Enable Stream