Skip to content

Commit 02cb540

Browse files
authored
Merge pull request #67 from scijava/chatGPT
introducing chatGPT support
2 parents c5b08d1 + dd18ea4 commit 02cb540

File tree

3 files changed

+150
-27
lines changed

3 files changed

+150
-27
lines changed

pom.xml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,17 @@
207207
<groupId>com.formdev</groupId>
208208
<artifactId>flatlaf</artifactId>
209209
</dependency>
210+
211+
<dependency>
212+
<groupId>com.theokanning.openai-gpt3-java</groupId>
213+
<artifactId>client</artifactId>
214+
<version>0.14.0</version>
215+
</dependency>
216+
<dependency>
217+
<groupId>com.theokanning.openai-gpt3-java</groupId>
218+
<artifactId>service</artifactId>
219+
<version>0.14.0</version>
220+
</dependency>
210221
</dependencies>
211222

212223
<repositories>
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
* #%L
3+
* Script Editor and Interpreter for SciJava script languages.
4+
* %%
5+
* Copyright (C) 2009 - 2023 SciJava developers.
6+
* %%
7+
* Redistribution and use in source and binary forms, with or without
8+
* modification, are permitted provided that the following conditions are met:
9+
*
10+
* 1. Redistributions of source code must retain the above copyright notice,
11+
* this list of conditions and the following disclaimer.
12+
* 2. Redistributions in binary form must reproduce the above copyright notice,
13+
* this list of conditions and the following disclaimer in the documentation
14+
* and/or other materials provided with the distribution.
15+
*
16+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19+
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
20+
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21+
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22+
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23+
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24+
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25+
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26+
* POSSIBILITY OF SUCH DAMAGE.
27+
* #L%
28+
*/
29+
30+
package org.scijava.ui.swing.script;
31+
32+
import org.scijava.options.OptionsPlugin;
33+
import org.scijava.plugin.Parameter;
34+
import org.scijava.plugin.Plugin;
35+
36+
/**
37+
* Runs the Edit::Options::OpenAI dialog.
38+
*
39+
* @author Curtis Rueden
40+
*/
41+
@Plugin(type = OptionsPlugin.class, menuPath = "Edit>Options>OpenAI...")
42+
public class OpenAIOptions extends OptionsPlugin {
43+
44+
@Parameter(label = "OpenAI key")
45+
private String openAIKey;
46+
47+
public String getOpenAIKey() {
48+
return openAIKey;
49+
}
50+
51+
public void setOpenAIKey(final String openAIKey) {
52+
this.openAIKey = openAIKey;
53+
}
54+
}

src/main/java/org/scijava/ui/swing/script/TextEditor.java

Lines changed: 85 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -106,32 +106,8 @@
106106

107107
import javax.script.ScriptEngine;
108108
import javax.script.ScriptException;
109-
import javax.swing.AbstractAction;
110-
import javax.swing.AbstractButton;
111-
import javax.swing.BoxLayout;
112-
import javax.swing.ButtonGroup;
113-
import javax.swing.JButton;
114-
import javax.swing.JCheckBoxMenuItem;
115-
import javax.swing.JDialog;
116-
import javax.swing.JFrame;
117-
import javax.swing.JLabel;
118-
import javax.swing.JMenu;
119-
import javax.swing.JMenuBar;
120-
import javax.swing.JMenuItem;
121-
import javax.swing.JOptionPane;
122-
import javax.swing.JPanel;
123-
import javax.swing.JPopupMenu;
124-
import javax.swing.JRadioButtonMenuItem;
125-
import javax.swing.JScrollPane;
126-
import javax.swing.JSplitPane;
127-
import javax.swing.JTabbedPane;
128-
import javax.swing.JTextArea;
129-
import javax.swing.JTextField;
130-
import javax.swing.JTextPane;
131-
import javax.swing.JTree;
132-
import javax.swing.KeyStroke;
133-
import javax.swing.SwingUtilities;
134-
import javax.swing.UIManager;
109+
import javax.swing.*;
110+
import javax.swing.border.BevelBorder;
135111
import javax.swing.event.ChangeEvent;
136112
import javax.swing.event.ChangeListener;
137113
import javax.swing.event.DocumentEvent;
@@ -142,6 +118,10 @@
142118
import javax.swing.text.Position;
143119
import javax.swing.tree.TreePath;
144120

121+
import com.theokanning.openai.completion.chat.ChatCompletionRequest;
122+
import com.theokanning.openai.completion.chat.ChatMessage;
123+
import com.theokanning.openai.completion.chat.ChatMessageRole;
124+
import com.theokanning.openai.service.OpenAiService;
145125
import org.fife.ui.rsyntaxtextarea.AbstractTokenMakerFactory;
146126
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
147127
import org.fife.ui.rsyntaxtextarea.Theme;
@@ -162,6 +142,7 @@
162142
import org.scijava.log.LogService;
163143
import org.scijava.module.ModuleException;
164144
import org.scijava.module.ModuleService;
145+
import org.scijava.options.OptionsService;
165146
import org.scijava.platform.PlatformService;
166147
import org.scijava.plugin.Parameter;
167148
import org.scijava.plugin.PluginInfo;
@@ -238,7 +219,7 @@ public class TextEditor extends JFrame implements ActionListener,
238219
gotoLine, makeJar, makeJarWithSource, removeUnusedImports, sortImports,
239220
removeTrailingWhitespace, findNext, findPrevious, openHelp, addImport,
240221
nextError, previousError, openHelpWithoutFrames, nextTab,
241-
previousTab, runSelection, extractSourceJar,
222+
previousTab, runSelection, extractSourceJar, askChatGPTtoGenerateCode,
242223
openSourceForClass,
243224
//openSourceForMenuItem, // this never had an actionListener!??
244225
openMacroFunctions, decreaseFontSize, increaseFontSize, chooseFontSize,
@@ -296,6 +277,8 @@ public class TextEditor extends JFrame implements ActionListener,
296277
private AppService appService;
297278
@Parameter
298279
private BatchService batchService;
280+
@Parameter(required = false)
281+
private OptionsService optionsService;
299282

300283
private Map<ScriptLanguage, JRadioButtonMenuItem> languageMenuItems;
301284
private JRadioButtonMenuItem noneLanguageItem;
@@ -534,6 +517,11 @@ public TextEditor(final Context context) {
534517
openSourceForClass.setMnemonic(KeyEvent.VK_J);
535518
//openSourceForMenuItem = addToMenu(toolsMenu, "Open Java File for Menu Item...", 0, 0);
536519
//openSourceForMenuItem.setMnemonic(KeyEvent.VK_M);
520+
521+
GuiUtils.addMenubarSeparator(toolsMenu, "chatGPT");
522+
askChatGPTtoGenerateCode = addToMenu(toolsMenu, "Ask chatGPT...", 0, 0);
523+
524+
537525
addScritpEditorMacroCommands(toolsMenu);
538526
mbar.add(toolsMenu);
539527

@@ -1638,6 +1626,7 @@ else if (source == chooseTabSize) {
16381626
}
16391627
else if (source == openClassOrPackageHelp) openClassOrPackageHelp(null);
16401628
else if (source == extractSourceJar) extractSourceJar();
1629+
else if (source == askChatGPTtoGenerateCode) askChatGPTtoGenerateCode();
16411630
else if (source == openSourceForClass) {
16421631
final String className = getSelectedClassNameOrAsk("Class (fully qualified name):", "Which Class?");
16431632
if (className != null) {
@@ -3254,6 +3243,75 @@ public void extractSourceJar() {
32543243
if (file != null) extractSourceJar(file);
32553244
}
32563245

3246+
public void askChatGPTtoGenerateCode() {
3247+
SwingUtilities.invokeLater(() -> {
3248+
3249+
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
3250+
3251+
// setup default prompt
3252+
String prompt =
3253+
"Write code in " + getCurrentLanguage().getLanguageName() + ".\n" +
3254+
"Write concise and high quality code for ImageJ/Fiji.\n" +
3255+
"Put minimal comments explaining what the code does.\n" +
3256+
"The code should do the following:\n" +
3257+
getTextArea().getSelectedText();
3258+
3259+
String answer = askChatGPT(prompt);
3260+
3261+
if (answer.contains("```")) {
3262+
// clean answer by removing blabla outside the code block
3263+
answer = answer.replace("```java", "```");
3264+
answer = answer.replace("```javascript", "```");
3265+
answer = answer.replace("```python", "```");
3266+
answer = answer.replace("```jython", "```");
3267+
answer = answer.replace("```macro", "```");
3268+
answer = answer.replace("```groovy", "```");
3269+
3270+
String[] temp = answer.split("```");
3271+
answer = temp[1];
3272+
}
3273+
3274+
//getTextArea().insert(answer, getTextArea().getCaretPosition());
3275+
getTextArea().replaceSelection(answer + "\n");
3276+
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
3277+
});
3278+
}
3279+
3280+
private String askChatGPT(String text) {
3281+
// Modified from: https://github.com/TheoKanning/openai-java/blob/main/example/src/main/java/example/OpenAiApiFunctionsExample.java
3282+
String token = apiKey();
3283+
3284+
OpenAiService service = new OpenAiService(token);
3285+
3286+
List<ChatMessage> messages = new ArrayList<>();
3287+
ChatMessage userMessage = new ChatMessage(ChatMessageRole.USER.value(), text);
3288+
messages.add(userMessage);
3289+
3290+
ChatCompletionRequest chatCompletionRequest = ChatCompletionRequest
3291+
.builder()
3292+
.model("gpt-3.5-turbo-0613")
3293+
.messages(messages).build();
3294+
3295+
ChatMessage responseMessage = service.createChatCompletion(chatCompletionRequest).getChoices().get(0).getMessage();
3296+
messages.add(responseMessage);
3297+
3298+
String result = responseMessage.getContent();
3299+
System.out.println(result);
3300+
return result;
3301+
}
3302+
3303+
private String apiKey() {
3304+
if (optionsService != null) {
3305+
final OpenAIOptions openAIOptions =
3306+
optionsService.getOptions(OpenAIOptions.class);
3307+
if (openAIOptions != null) {
3308+
final String key = openAIOptions.getOpenAIKey();
3309+
if (key != null && !key.isEmpty()) return key;
3310+
}
3311+
}
3312+
return System.getenv("OPENAI_API_KEY");
3313+
}
3314+
32573315
public void extractSourceJar(final File file) {
32583316
try {
32593317
final FileFunctions functions = new FileFunctions(this);

0 commit comments

Comments
 (0)