From e13c711394898fc3308ac16170d233fd8e36490d Mon Sep 17 00:00:00 2001 From: Wuzi Date: Wed, 19 Apr 2023 00:52:06 +0800 Subject: [PATCH] Fix #191 --- .../com/obiscr/chatgpt/ChatGPTHandler.java | 11 + .../com/obiscr/chatgpt/RequestProvider.java | 2 +- .../chatgpt/settings/CustomActionsPanel.java | 316 +++++++++++++----- .../chatgpt/settings/OpenAISettingsState.java | 2 + 4 files changed, 240 insertions(+), 91 deletions(-) diff --git a/src/main/java/com/obiscr/chatgpt/ChatGPTHandler.java b/src/main/java/com/obiscr/chatgpt/ChatGPTHandler.java index 6cd5741..a6007d1 100644 --- a/src/main/java/com/obiscr/chatgpt/ChatGPTHandler.java +++ b/src/main/java/com/obiscr/chatgpt/ChatGPTHandler.java @@ -2,6 +2,7 @@ import com.alibaba.fastjson2.JSON; import com.intellij.openapi.project.Project; +import com.obiscr.chatgpt.core.builder.OfficialBuilder; import com.obiscr.chatgpt.core.parser.OfficialParser; import com.obiscr.chatgpt.settings.OpenAISettingsState; import com.obiscr.chatgpt.ui.MainPanel; @@ -21,6 +22,7 @@ import java.io.IOException; import java.net.Proxy; import java.nio.charset.StandardCharsets; +import java.util.Stack; import java.util.concurrent.TimeUnit; /** @@ -32,6 +34,7 @@ public class ChatGPTHandler extends AbstractHandler { private static final Logger LOG = LoggerFactory.getLogger(ChatGPTHandler.class); + private final Stack gpt35Stack = new Stack<>(); public EventSource handle(MainPanel mainPanel, MessageComponent component, String question) { myProject = mainPanel.getProject(); @@ -102,6 +105,14 @@ public void onEvent(@NotNull EventSource eventSource, @Nullable String id, @Null if (parseResult == null) { return; } + if (!mainPanel.isChatGPTModel()) { + if (data.contains("\"finish_reason\":\"stop\"")) { + mainPanel.getContentPanel().getMessages().add(OfficialBuilder.assistantMessage(gpt35Stack.pop())); + gpt35Stack.clear(); + } else { + gpt35Stack.push(parseResult.getSource()); + } + } // Copy action only needed source content component.setSourceContent(parseResult.getSource()); component.setContent(parseResult.getHtml()); diff --git a/src/main/java/com/obiscr/chatgpt/RequestProvider.java b/src/main/java/com/obiscr/chatgpt/RequestProvider.java index a62ab20..d42017c 100644 --- a/src/main/java/com/obiscr/chatgpt/RequestProvider.java +++ b/src/main/java/com/obiscr/chatgpt/RequestProvider.java @@ -19,7 +19,7 @@ */ public class RequestProvider { - public static final String OFFICIAL_CONVERSATION_URL = "https://bypass.churchless.tech/api/conversation"; + public static final String OFFICIAL_CONVERSATION_URL = "https://ai.fakeopen.com/api/conversation"; public static final String OFFICIAL_GPT35_TURBO_URL = "https://api.openai.com/v1/chat/completions"; private Project myProject; private String url; diff --git a/src/main/java/com/obiscr/chatgpt/settings/CustomActionsPanel.java b/src/main/java/com/obiscr/chatgpt/settings/CustomActionsPanel.java index 455d592..9e9c43f 100644 --- a/src/main/java/com/obiscr/chatgpt/settings/CustomActionsPanel.java +++ b/src/main/java/com/obiscr/chatgpt/settings/CustomActionsPanel.java @@ -1,26 +1,29 @@ package com.obiscr.chatgpt.settings; +import com.intellij.icons.AllIcons; import com.intellij.openapi.Disposable; import com.intellij.openapi.options.Configurable; -import com.intellij.openapi.ui.cellvalidators.*; +import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.Disposer; -import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.util.NlsContexts; import com.intellij.ui.*; import com.intellij.ui.components.JBLabel; -import com.intellij.ui.components.fields.ExtendableTextField; import com.intellij.ui.table.JBTable; import com.intellij.util.ui.ColumnInfo; -import com.intellij.util.ui.JBInsets; import com.intellij.util.ui.ListTableModel; -import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; -import javax.swing.event.ChangeEvent; -import javax.swing.event.TableModelEvent; +import javax.swing.table.AbstractTableModel; +import javax.swing.table.DefaultTableCellRenderer; +import javax.swing.table.TableCellRenderer; import java.awt.*; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; import java.util.ArrayList; +import java.util.Comparator; import java.util.List; +import java.util.Map; /** * @author Wuzi @@ -28,26 +31,11 @@ public class CustomActionsPanel implements Configurable, Disposable { private JPanel myMainPanel; - private JPanel customActionsTitledBorderBox; private final Disposable myDisposable = Disposer.newDisposable(); - private final ListTableModel myModel = new ListTableModel<>() { - @Override - public void addRow() { - addRow(""); - } - }; - - private final JBTable myTable = new JBTable(myModel) { - @Override - public void editingCanceled(ChangeEvent e) { - int row = getEditingRow(); - super.editingCanceled(e); - if (row >= 0 && row < myModel.getRowCount() && StringUtil.isEmpty(myModel.getRowValue(row))) { - myModel.removeRow(row); - } - } - }; + private final List promptList = new ArrayList<>(); + private final MyTableModel myModel = new MyTableModel(promptList); + private final JBTable myTable = new JBTable(myModel); public CustomActionsPanel() { init(); @@ -70,93 +58,91 @@ public String getDisplayName() { @Override public @Nullable JComponent createComponent() { - myModel.setColumnInfos(new ColumnInfo[]{new ColumnInfo(""){ - - @Override - public @Nullable String valueOf(String s) { - return s; - } - - @Override - public boolean isCellEditable(String s) { - return true; - } - - @Override - public void setValue(String s, String value) { - int row = myTable.getSelectedRow(); - if (StringUtil.isEmpty(value) && row >= 0 && row < myModel.getRowCount()) { - myModel.removeRow(row); - } - - List items = new ArrayList<>(myModel.getItems()); - if(row >= items.size()) { - return; - } - items.set(row, value); - myModel.setItems(items); - myModel.fireTableCellUpdated(row, TableModelEvent.ALL_COLUMNS); - - myTable.repaint(); - } - }}); myTable.getColumnModel().setColumnMargin(0); - myTable.setShowColumns(false); - myTable.setShowGrid(false); - myTable.getEmptyText().setText("No prefix configured"); + myTable.setShowColumns(true); + myTable.setShowGrid(true); + myTable.getEmptyText().setText("No prompt configured"); myTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); myTable.setToolTipText("Double click to edit it"); - - ExtendableTextField cellEditor = new ExtendableTextField(); - DefaultCellEditor editor = new StatefulValidatingCellEditor(cellEditor, myDisposable). - withStateUpdater(vi -> ValidationUtils.setExtension(cellEditor, vi)); - editor.setClickCountToStart(2); - myTable.setDefaultEditor(Object.class, editor); - - myTable.setDefaultRenderer(Object.class, new ValidatingTableCellRendererWrapper(new ColoredTableCellRenderer() { - { - setIpad(new JBInsets(0, 0, 0, 0));} - + myTable.getTableHeader().setDefaultRenderer(new MyTableCellRenderer()); + myTable.addMouseListener(new MouseAdapter() { @Override - protected void customizeCellRenderer(@NotNull JTable table, @Nullable Object value, boolean selected, boolean hasFocus, int row, int column) { - if (row >= 0 && row < myModel.getRowCount()) { - String prefix = myModel.getRowValue(row); - setForeground(selected ? table.getSelectionForeground() : table.getForeground()); - setBackground(selected ? table.getSelectionBackground() : table.getBackground()); - append(prefix, SimpleTextAttributes.REGULAR_ATTRIBUTES); - setToolTipText("Double click to edit it."); + public void mouseClicked(MouseEvent e) { + if (e.getClickCount() == 2) { + doEditAction(); } } + }); + return ToolbarDecorator.createDecorator(myTable) + .setAddAction(anActionButton -> { + doAddAction(); + }) + .setEditAction(anActionButton -> { + doEditAction(); + }) + .createPanel(); + } - @Override - protected SimpleTextAttributes modifyAttributes(SimpleTextAttributes attributes) { - return attributes; - } - }).bindToEditorSize(cellEditor::getPreferredSize)); + public void doAddAction() { + final CustomPromptEditor macroEditor = new CustomPromptEditor("Add Custom Prompt", "", "", new AddValidator("Add Custom Prompt",myModel)); + if (macroEditor.showAndGet()) { + final String key = macroEditor.getKey(); + final String value = macroEditor.getValue(); + myModel.addRow(new MyPrompt(key,value,myModel.getRowCount())); + } + } - return ToolbarDecorator.createDecorator(myTable).disableUpDownActions().createPanel(); + public void doEditAction() { + int selectedRow = myTable.getSelectedRow(); + if (selectedRow < 0) { + return; + } + MyPrompt rowValue = myModel.getRowValue(selectedRow); + final CustomPromptEditor macroEditor = new CustomPromptEditor("Edit Custom Prompt", rowValue.name, rowValue.value, new EditValidator()); + if (macroEditor.showAndGet()) { + myModel.removeRow(selectedRow); + final String key = macroEditor.getKey(); + final String value = macroEditor.getValue(); + myModel.addRow(new MyPrompt(key,value,myModel.getRowCount())); + } } @Override public boolean isModified() { - List prefix = new ArrayList<>(myModel.getItems()); - return !OpenAISettingsState.getInstance().customActionsPrefix.equals(prefix); + List prompts = new ArrayList<>(myModel.getItems()); + Map customPrompts = OpenAISettingsState.getInstance().customPrompts; + for (MyPrompt prompt : prompts) { + if (!customPrompts.containsKey(prompt.name)) { + return true; + } + String value = customPrompts.get(prompt.name); + if (!prompt.value.equals(value)) { + return true; + } + } + return false; } @Override public void apply() { myTable.editingStopped(null); - List list = OpenAISettingsState.getInstance().customActionsPrefix; - list.clear(); - list.addAll(myModel.getItems()); + Map customPrompts = OpenAISettingsState.getInstance().customPrompts; + customPrompts.clear(); + List prompts = new ArrayList<>(myModel.getItems()); + for (MyPrompt prompt : prompts) { + customPrompts.put(prompt.name, prompt.value); + } } @Override public void reset() { - List prefix = new ArrayList<>(OpenAISettingsState. - getInstance().customActionsPrefix); - myModel.setItems(prefix); + List prompts = new ArrayList<>(); + Map customPrompts = OpenAISettingsState.getInstance().customPrompts; + for (Map.Entry prompt : customPrompts.entrySet()) { + prompts.add(new MyPrompt(prompt.getKey(), prompt.getValue(), myModel.getRowCount())); + } + myModel.setItems(prompts); } private void createUIComponents() { @@ -171,4 +157,154 @@ private void createUIComponents() { public void disposeUIResources() { Disposer.dispose(myDisposable); } + + static class MyColumnInfo extends ColumnInfo { + + public MyColumnInfo(String name) { + super(name); + } + + @Override + public @Nullable String valueOf(MyPrompt prompt) { + return getName().equals("Prompt Name") ? prompt.name : prompt.value; + } + } + + static class MyPrompt { + private final String name; + private final String value; + private final int order; + + public MyPrompt(String value, int order) { + this(value,value,order); + } + public MyPrompt(String name, String value, int order) { + this.name = name; + this.value = value; + this.order = order; + } + } + + private static final class AddValidator implements CustomPromptEditor.Validator { + private final String myTitle; + private final MyTableModel myModel; + AddValidator(String title, MyTableModel model) { + myTitle = title; + myModel = model; + } + + @Override + public boolean checkName(String name) { + return name.length() != 0; + } + + @Override + public boolean isOK(String name, String value) { + if(name.length() == 0) { + return false; + } + if (myModel.containsName(name)) { + Messages.showErrorDialog( + "Prompt with name " + name + " already exists.", myTitle); + return false; + } + return true; + } + } + + private static final class EditValidator implements CustomPromptEditor.Validator { + + @Override + public boolean checkName(String name) { + return name.length() != 0; + } + + @Override + public boolean isOK(String name, String value) { + return checkName(name); + } + } + + static class MyTableModel extends AbstractTableModel { + + private List prompts; + + public MyTableModel(List prompts) { + this.prompts = new ArrayList<>(prompts); + } + + @Override + public int getRowCount() { + return prompts.size(); + } + + @Override + public int getColumnCount() { + return 2; + } + + @Override + public Object getValueAt(int rowIndex, int columnIndex) { + MyPrompt prompt = prompts.get(rowIndex); + return columnIndex == 0 ? prompt.name : prompt.value; + } + + public List getItems() { + return prompts; + } + + public boolean containsName(String name) { + for (MyPrompt prompt : prompts) { + if (prompt.name.equals(name)) { + return true; + } + } + return false; + } + + public void addRow(MyPrompt prompt) { + prompts.add(prompt); + fireTableDataChanged(); + } + + public MyPrompt getRowValue(int selectedIndex) { + return prompts.get(selectedIndex); + } + + public void removeRow(int selectedIndex) { + prompts.remove(selectedIndex); + fireTableDataChanged(); + } + + public void setItems(List prompts) { + this.prompts = new ArrayList<>(prompts); + fireTableDataChanged(); + } + + @Override + public String getColumnName(int column) { + return column == 0 ? "Prompt Name" : "Prompt Value"; + } + + @Override + public Class getColumnClass(int columnIndex) { + return String.class; + } + } + + static class MyTableCellRenderer extends DefaultTableCellRenderer { + @Override + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { + Component component = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + setHorizontalTextPosition(SwingConstants.LEFT); + if (column == 0) { + setToolTipText("The name displayed in the menu, and it should be as short as possible."); + } else { + setToolTipText("When asking a question, the prompt content sent to AI."); + } + setIcon(AllIcons.General.ContextHelp); + return component; + } + } } + diff --git a/src/main/java/com/obiscr/chatgpt/settings/OpenAISettingsState.java b/src/main/java/com/obiscr/chatgpt/settings/OpenAISettingsState.java index a2a143d..b0c80c9 100644 --- a/src/main/java/com/obiscr/chatgpt/settings/OpenAISettingsState.java +++ b/src/main/java/com/obiscr/chatgpt/settings/OpenAISettingsState.java @@ -51,6 +51,7 @@ public class OpenAISettingsState implements PersistentStateComponent customActionsPrefix = new ArrayList<>(); public String chatGptModel = "text-davinci-002-render-sha"; @@ -70,6 +71,7 @@ public class OpenAISettingsState implements PersistentStateComponent customPrompts = new HashMap<>(); public static OpenAISettingsState getInstance() { return ApplicationManager.getApplication().getService(OpenAISettingsState.class); }