feat: extract providers into their standalone configurables (#538)

* fix: extract services to their own configurables

* feat: switch to selected provider automatically upon apply

* fix: credentials loading at once

* fix: rename llama.cpp title
This commit is contained in:
Carl-Robert 2024-05-09 11:16:09 +03:00 committed by GitHub
parent 0852c27170
commit 7bee59a90e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
36 changed files with 579 additions and 418 deletions

View file

@ -5,7 +5,7 @@ import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.options.ShowSettingsUtil;
import ee.carlrobert.codegpt.CodeGPTBundle;
import ee.carlrobert.codegpt.settings.GeneralSettingsConfigurable;
import ee.carlrobert.codegpt.settings.service.ServiceConfigurable;
import org.jetbrains.annotations.NotNull;
public class OpenSettingsAction extends AnAction {
@ -18,7 +18,6 @@ public class OpenSettingsAction extends AnAction {
@Override
public void actionPerformed(@NotNull AnActionEvent e) {
ShowSettingsUtil.getInstance()
.showSettingsDialog(e.getProject(), GeneralSettingsConfigurable.class);
ShowSettingsUtil.getInstance().showSettingsDialog(e.getProject(), ServiceConfigurable.class);
}
}

View file

@ -1,151 +1,26 @@
package ee.carlrobert.codegpt.settings;
import static ee.carlrobert.codegpt.settings.service.ServiceType.ANTHROPIC;
import static ee.carlrobert.codegpt.settings.service.ServiceType.AZURE;
import static ee.carlrobert.codegpt.settings.service.ServiceType.CODEGPT;
import static ee.carlrobert.codegpt.settings.service.ServiceType.CUSTOM_OPENAI;
import static ee.carlrobert.codegpt.settings.service.ServiceType.GOOGLE;
import static ee.carlrobert.codegpt.settings.service.ServiceType.LLAMA_CPP;
import static ee.carlrobert.codegpt.settings.service.ServiceType.OLLAMA;
import static ee.carlrobert.codegpt.settings.service.ServiceType.OPENAI;
import static ee.carlrobert.codegpt.settings.service.ServiceType.YOU;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.ui.ComboBox;
import com.intellij.ui.components.JBTextField;
import com.intellij.util.ui.FormBuilder;
import ee.carlrobert.codegpt.CodeGPTBundle;
import ee.carlrobert.codegpt.settings.service.ServiceType;
import ee.carlrobert.codegpt.settings.service.anthropic.AnthropicSettings;
import ee.carlrobert.codegpt.settings.service.anthropic.AnthropicSettingsForm;
import ee.carlrobert.codegpt.settings.service.azure.AzureSettings;
import ee.carlrobert.codegpt.settings.service.azure.AzureSettingsForm;
import ee.carlrobert.codegpt.settings.service.codegpt.CodeGPTServiceForm;
import ee.carlrobert.codegpt.settings.service.custom.CustomServiceForm;
import ee.carlrobert.codegpt.settings.service.google.GoogleSettingsForm;
import ee.carlrobert.codegpt.settings.service.llama.LlamaSettings;
import ee.carlrobert.codegpt.settings.service.llama.form.LlamaSettingsForm;
import ee.carlrobert.codegpt.settings.service.ollama.OllamaSettingsForm;
import ee.carlrobert.codegpt.settings.service.openai.OpenAISettings;
import ee.carlrobert.codegpt.settings.service.openai.OpenAISettingsForm;
import ee.carlrobert.codegpt.settings.service.you.YouSettings;
import ee.carlrobert.codegpt.settings.service.you.YouSettingsForm;
import java.awt.CardLayout;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Insets;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JComponent;
import javax.swing.JPanel;
public class GeneralSettingsComponent {
private final JPanel mainPanel;
private final JBTextField displayNameField;
private final ComboBox<ServiceType> serviceComboBox;
private final CodeGPTServiceForm codeGPTSettingsForm;
private final OpenAISettingsForm openAISettingsForm;
private final CustomServiceForm customConfigurationSettingsForm;
private final AnthropicSettingsForm anthropicSettingsForm;
private final AzureSettingsForm azureSettingsForm;
private final YouSettingsForm youSettingsForm;
private final LlamaSettingsForm llamaSettingsForm;
private final OllamaSettingsForm ollamaSettingsForm;
private final GoogleSettingsForm googleSettingsForm;
public GeneralSettingsComponent(Disposable parentDisposable, GeneralSettings settings) {
public GeneralSettingsComponent(GeneralSettings settings) {
displayNameField = new JBTextField(settings.getState().getDisplayName(), 20);
codeGPTSettingsForm = new CodeGPTServiceForm();
openAISettingsForm = new OpenAISettingsForm(OpenAISettings.getCurrentState());
customConfigurationSettingsForm = new CustomServiceForm();
anthropicSettingsForm = new AnthropicSettingsForm(AnthropicSettings.getCurrentState());
azureSettingsForm = new AzureSettingsForm(AzureSettings.getCurrentState());
youSettingsForm = new YouSettingsForm(YouSettings.getCurrentState(), parentDisposable);
llamaSettingsForm = new LlamaSettingsForm(LlamaSettings.getCurrentState());
ollamaSettingsForm = new OllamaSettingsForm();
googleSettingsForm = new GoogleSettingsForm();
var cardLayout = new DynamicCardLayout();
var cards = new JPanel(cardLayout);
cards.add(codeGPTSettingsForm.getForm(), CODEGPT.getCode());
cards.add(openAISettingsForm.getForm(), OPENAI.getCode());
cards.add(customConfigurationSettingsForm.getForm(), CUSTOM_OPENAI.getCode());
cards.add(anthropicSettingsForm.getForm(), ANTHROPIC.getCode());
cards.add(azureSettingsForm.getForm(), AZURE.getCode());
cards.add(youSettingsForm, YOU.getCode());
cards.add(llamaSettingsForm, LLAMA_CPP.getCode());
cards.add(ollamaSettingsForm.getForm(), OLLAMA.getCode());
cards.add(googleSettingsForm.getForm(), GOOGLE.getCode());
cardLayout.show(cards, settings.getState().getSelectedService().getCode());
serviceComboBox = new ComboBox<>(new DefaultComboBoxModel<>(ServiceType.values()));
serviceComboBox.setSelectedItem(settings.getState().getSelectedService());
serviceComboBox.setPreferredSize(displayNameField.getPreferredSize());
serviceComboBox.addItemListener(e -> {
ServiceType selectedService = (ServiceType) e.getItem();
cardLayout.show(cards, selectedService.getCode());
if (selectedService == OLLAMA) {
ollamaSettingsForm.refreshModels();
}
});
mainPanel = FormBuilder.createFormBuilder()
.addLabeledComponent(
CodeGPTBundle.get("settingsConfigurable.displayName.label"),
displayNameField)
.addLabeledComponent(
CodeGPTBundle.get("settingsConfigurable.service.label"),
serviceComboBox)
.addComponent(cards)
.addComponentFillVertically(new JPanel(), 0)
.getPanel();
}
public CodeGPTServiceForm getCodeGPTSettingsForm() {
return codeGPTSettingsForm;
}
public OpenAISettingsForm getOpenAISettingsForm() {
return openAISettingsForm;
}
public CustomServiceForm getCustomConfigurationSettingsForm() {
return customConfigurationSettingsForm;
}
public AnthropicSettingsForm getAnthropicSettingsForm() {
return anthropicSettingsForm;
}
public AzureSettingsForm getAzureSettingsForm() {
return azureSettingsForm;
}
public LlamaSettingsForm getLlamaSettingsForm() {
return llamaSettingsForm;
}
public YouSettingsForm getYouSettingsForm() {
return youSettingsForm;
}
public OllamaSettingsForm getOllamaSettingsForm() {
return ollamaSettingsForm;
}
public GoogleSettingsForm getGoogleSettingsForm() {
return googleSettingsForm;
}
public ServiceType getSelectedService() {
return serviceComboBox.getItem();
}
public void setSelectedService(ServiceType serviceType) {
serviceComboBox.setSelectedItem(serviceType);
}
public JPanel getPanel() {
return mainPanel;
return FormBuilder.createFormBuilder()
.addLabeledComponent(
CodeGPTBundle.get("settingsConfigurable.displayName.label"),
displayNameField)
.addComponentFillVertically(new JPanel(), 0)
.getPanel();
}
public JComponent getPreferredFocusedComponent() {
@ -159,41 +34,4 @@ public class GeneralSettingsComponent {
public void setDisplayName(String displayName) {
displayNameField.setText(displayName);
}
public void resetForms() {
codeGPTSettingsForm.resetForm();
openAISettingsForm.resetForm();
customConfigurationSettingsForm.resetForm();
anthropicSettingsForm.resetForm();
azureSettingsForm.resetForm();
youSettingsForm.resetForm();
llamaSettingsForm.resetForm();
ollamaSettingsForm.resetForm();
googleSettingsForm.resetForm();
}
static class DynamicCardLayout extends CardLayout {
@Override
public Dimension preferredLayoutSize(Container parent) {
Component current = findVisibleComponent(parent);
if (current != null) {
Insets insets = parent.getInsets();
Dimension preferredSize = current.getPreferredSize();
preferredSize.width += insets.left + insets.right;
preferredSize.height += insets.top + insets.bottom;
return preferredSize;
}
return super.preferredLayoutSize(parent);
}
private Component findVisibleComponent(Container parent) {
for (Component comp : parent.getComponents()) {
if (comp.isVisible()) {
return comp;
}
}
return null;
}
}
}

View file

@ -1,44 +1,13 @@
package ee.carlrobert.codegpt.settings;
import static ee.carlrobert.codegpt.credentials.CredentialsStore.CredentialKey.ANTHROPIC_API_KEY;
import static ee.carlrobert.codegpt.credentials.CredentialsStore.CredentialKey.AZURE_ACTIVE_DIRECTORY_TOKEN;
import static ee.carlrobert.codegpt.credentials.CredentialsStore.CredentialKey.AZURE_OPENAI_API_KEY;
import static ee.carlrobert.codegpt.credentials.CredentialsStore.CredentialKey.CODEGPT_API_KEY;
import static ee.carlrobert.codegpt.credentials.CredentialsStore.CredentialKey.CUSTOM_SERVICE_API_KEY;
import static ee.carlrobert.codegpt.credentials.CredentialsStore.CredentialKey.GOOGLE_API_KEY;
import static ee.carlrobert.codegpt.credentials.CredentialsStore.CredentialKey.LLAMA_API_KEY;
import static ee.carlrobert.codegpt.credentials.CredentialsStore.CredentialKey.OPENAI_API_KEY;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.options.Configurable;
import com.intellij.openapi.util.Disposer;
import ee.carlrobert.codegpt.CodeGPTBundle;
import ee.carlrobert.codegpt.conversations.ConversationsState;
import ee.carlrobert.codegpt.credentials.CredentialsStore;
import ee.carlrobert.codegpt.settings.service.anthropic.AnthropicSettings;
import ee.carlrobert.codegpt.settings.service.anthropic.AnthropicSettingsForm;
import ee.carlrobert.codegpt.settings.service.azure.AzureSettings;
import ee.carlrobert.codegpt.settings.service.azure.AzureSettingsForm;
import ee.carlrobert.codegpt.settings.service.codegpt.CodeGPTServiceForm;
import ee.carlrobert.codegpt.settings.service.custom.CustomServiceForm;
import ee.carlrobert.codegpt.settings.service.google.GoogleSettingsForm;
import ee.carlrobert.codegpt.settings.service.llama.LlamaSettings;
import ee.carlrobert.codegpt.settings.service.llama.form.LlamaSettingsForm;
import ee.carlrobert.codegpt.settings.service.openai.OpenAISettings;
import ee.carlrobert.codegpt.settings.service.openai.OpenAISettingsForm;
import ee.carlrobert.codegpt.settings.service.you.YouSettings;
import ee.carlrobert.codegpt.settings.service.you.YouSettingsForm;
import ee.carlrobert.codegpt.telemetry.TelemetryAction;
import ee.carlrobert.codegpt.toolwindow.chat.ChatToolWindowContentManager;
import ee.carlrobert.codegpt.util.ApplicationUtil;
import javax.swing.JComponent;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.Nullable;
public class GeneralSettingsConfigurable implements Configurable {
private Disposable parentDisposable;
private GeneralSettingsComponent component;
@Nls(capitalization = Nls.Capitalization.Title)
@ -55,127 +24,23 @@ public class GeneralSettingsConfigurable implements Configurable {
@Nullable
@Override
public JComponent createComponent() {
var settings = GeneralSettings.getInstance();
parentDisposable = Disposer.newDisposable();
component = new GeneralSettingsComponent(parentDisposable, settings);
component = new GeneralSettingsComponent(GeneralSettings.getInstance());
return component.getPanel();
}
@Override
public boolean isModified() {
var settings = GeneralSettings.getCurrentState();
return !component.getDisplayName().equals(settings.getDisplayName())
|| component.getSelectedService() != settings.getSelectedService()
|| component.getCodeGPTSettingsForm().isModified()
|| OpenAISettings.getInstance().isModified(component.getOpenAISettingsForm())
|| component.getCustomConfigurationSettingsForm().isModified()
|| AnthropicSettings.getInstance().isModified(component.getAnthropicSettingsForm())
|| AzureSettings.getInstance().isModified(component.getAzureSettingsForm())
|| YouSettings.getInstance().isModified(component.getYouSettingsForm())
|| LlamaSettings.getInstance().isModified(component.getLlamaSettingsForm())
|| component.getOllamaSettingsForm().isModified()
|| component.getGoogleSettingsForm().isModified();
return !component.getDisplayName().equals(settings.getDisplayName());
}
@Override
public void apply() {
var settings = GeneralSettings.getCurrentState();
settings.setDisplayName(component.getDisplayName());
settings.setSelectedService(component.getSelectedService());
applyCodeGPTServiceSettings(component.getCodeGPTSettingsForm());
var openAISettingsForm = component.getOpenAISettingsForm();
applyOpenAISettings(openAISettingsForm);
applyCustomOpenAISettings(component.getCustomConfigurationSettingsForm());
applyAnthropicSettings(component.getAnthropicSettingsForm());
applyAzureSettings(component.getAzureSettingsForm());
applyYouSettings(component.getYouSettingsForm());
applyLlamaSettings(component.getLlamaSettingsForm());
component.getOllamaSettingsForm().applyChanges();
applyGoogleSettings(component.getGoogleSettingsForm());
var serviceChanged = component.getSelectedService() != settings.getSelectedService();
var modelChanged = !OpenAISettings.getCurrentState().getModel()
.equals(openAISettingsForm.getModel());
if (serviceChanged || modelChanged) {
resetActiveTab();
if (serviceChanged) {
TelemetryAction.SETTINGS_CHANGED.createActionMessage()
.property("service", component.getSelectedService().getCode().toLowerCase())
.send();
}
}
}
private void applyCodeGPTServiceSettings(CodeGPTServiceForm form) {
CredentialsStore.INSTANCE.setCredential(CODEGPT_API_KEY, form.getApiKey());
form.applyChanges();
}
private void applyOpenAISettings(OpenAISettingsForm form) {
CredentialsStore.INSTANCE.setCredential(OPENAI_API_KEY, form.getApiKey());
OpenAISettings.getInstance().loadState(form.getCurrentState());
}
private void applyCustomOpenAISettings(CustomServiceForm form) {
CredentialsStore.INSTANCE.setCredential(CUSTOM_SERVICE_API_KEY, form.getApiKey());
form.applyChanges();
}
private void applyLlamaSettings(LlamaSettingsForm form) {
CredentialsStore.INSTANCE.setCredential(
LLAMA_API_KEY,
form.getLlamaServerPreferencesForm().getApiKey());
LlamaSettings.getInstance().loadState(form.getCurrentState());
}
private void applyYouSettings(YouSettingsForm form) {
YouSettings.getInstance().loadState(form.getCurrentState());
}
private void applyAnthropicSettings(AnthropicSettingsForm form) {
CredentialsStore.INSTANCE.setCredential(ANTHROPIC_API_KEY, form.getApiKey());
AnthropicSettings.getInstance().loadState(form.getCurrentState());
}
private void applyAzureSettings(AzureSettingsForm form) {
AzureSettings.getInstance().loadState(form.getCurrentState());
CredentialsStore.INSTANCE.setCredential(AZURE_OPENAI_API_KEY, form.getApiKey());
CredentialsStore.INSTANCE.setCredential(
AZURE_ACTIVE_DIRECTORY_TOKEN,
form.getActiveDirectoryToken());
}
private void applyGoogleSettings(GoogleSettingsForm form) {
form.applyChanges();
CredentialsStore.INSTANCE.setCredential(GOOGLE_API_KEY, form.getApiKey());
GeneralSettings.getCurrentState().setDisplayName(component.getDisplayName());
}
@Override
public void reset() {
var settings = GeneralSettings.getCurrentState();
component.setDisplayName(settings.getDisplayName());
component.setSelectedService(settings.getSelectedService());
component.resetForms();
}
@Override
public void disposeUIResources() {
if (parentDisposable != null) {
Disposer.dispose(parentDisposable);
}
component = null;
}
private void resetActiveTab() {
ConversationsState.getInstance().setCurrentConversation(null);
var project = ApplicationUtil.findCurrentProject();
if (project == null) {
throw new RuntimeException("Could not find current project.");
}
project.getService(ChatToolWindowContentManager.class).resetAll();
component.setDisplayName(GeneralSettings.getCurrentState().getDisplayName());
}
}

View file

@ -8,10 +8,10 @@ public enum ServiceType {
CUSTOM_OPENAI("CUSTOM_OPENAI", "service.custom.openai.title", "custom.openai.chat.completion"),
ANTHROPIC("ANTHROPIC", "service.anthropic.title", "anthropic.chat.completion"),
AZURE("AZURE", "service.azure.title", "azure.chat.completion"),
GOOGLE("GOOGLE", "service.google.title", "google.chat.completion"),
YOU("YOU", "service.you.title", "you.chat.completion"),
LLAMA_CPP("LLAMA_CPP", "service.llama.title", "llama.chat.completion"),
OLLAMA("OLLAMA", "service.ollama.title", "ollama.chat.completion"),
GOOGLE("GOOGLE", "service.google.title", "google.chat.completion");
OLLAMA("OLLAMA", "service.ollama.title", "ollama.chat.completion");
private final String code;
private final String label;

View file

@ -4,9 +4,6 @@ import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.PersistentStateComponent;
import com.intellij.openapi.components.State;
import com.intellij.openapi.components.Storage;
import ee.carlrobert.codegpt.credentials.CredentialsStore;
import ee.carlrobert.codegpt.credentials.CredentialsStore.CredentialKey;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
@State(name = "CodeGPT_AnthropicSettings", storages = @Storage("CodeGPT_AnthropicSettings.xml"))
@ -32,11 +29,4 @@ public class AnthropicSettings implements PersistentStateComponent<AnthropicSett
public static AnthropicSettings getInstance() {
return ApplicationManager.getApplication().getService(AnthropicSettings.class);
}
public boolean isModified(AnthropicSettingsForm form) {
return !form.getCurrentState().equals(state)
|| !StringUtils.equals(
form.getApiKey(),
CredentialsStore.getCredential(CredentialKey.ANTHROPIC_API_KEY));
}
}

View file

@ -1,9 +1,7 @@
package ee.carlrobert.codegpt.settings.service.anthropic;
import static ee.carlrobert.codegpt.credentials.CredentialsStore.CredentialKey.ANTHROPIC_API_KEY;
import static ee.carlrobert.codegpt.ui.UIUtil.withEmptyLeftBorder;
import com.intellij.ui.TitledSeparator;
import com.intellij.ui.components.JBPasswordField;
import com.intellij.ui.components.JBTextField;
import com.intellij.util.ui.FormBuilder;
@ -30,8 +28,7 @@ public class AnthropicSettingsForm {
public JPanel getForm() {
return FormBuilder.createFormBuilder()
.addComponent(new TitledSeparator(CodeGPTBundle.get("shared.configuration")))
.addComponent(withEmptyLeftBorder(UI.PanelFactory.grid()
.addComponent(UI.PanelFactory.grid()
.add(UI.PanelFactory.panel(apiKeyField)
.withLabel(CodeGPTBundle.get("settingsConfigurable.shared.apiKey.label"))
.resizeX(false)
@ -49,7 +46,7 @@ public class AnthropicSettingsForm {
.withComment(CodeGPTBundle.get(
"settingsConfigurable.service.anthropic.model.comment"))
.resizeX(false))
.createPanel()))
.createPanel())
.addComponentFillVertically(new JPanel(), 0)
.getPanel();
}

View file

@ -1,14 +1,9 @@
package ee.carlrobert.codegpt.settings.service.azure;
import static ee.carlrobert.codegpt.credentials.CredentialsStore.CredentialKey.AZURE_ACTIVE_DIRECTORY_TOKEN;
import static ee.carlrobert.codegpt.credentials.CredentialsStore.CredentialKey.AZURE_OPENAI_API_KEY;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.PersistentStateComponent;
import com.intellij.openapi.components.State;
import com.intellij.openapi.components.Storage;
import ee.carlrobert.codegpt.credentials.CredentialsStore;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
@State(name = "CodeGPT_AzureSettings_210", storages = @Storage("CodeGPT_AzureSettings_210.xml"))
@ -34,14 +29,4 @@ public class AzureSettings implements PersistentStateComponent<AzureSettingsStat
public static AzureSettings getInstance() {
return ApplicationManager.getApplication().getService(AzureSettings.class);
}
public boolean isModified(AzureSettingsForm form) {
return !form.getCurrentState().equals(state)
|| !StringUtils.equals(
form.getActiveDirectoryToken(),
CredentialsStore.getCredential(AZURE_ACTIVE_DIRECTORY_TOKEN))
|| !StringUtils.equals(
form.getApiKey(),
CredentialsStore.getCredential(AZURE_OPENAI_API_KEY));
}
}

View file

@ -13,12 +13,12 @@ import java.util.Map.Entry;
import javax.swing.JPanel;
import javax.swing.table.DefaultTableModel;
class CustomServiceFormTabbedPane extends JBTabbedPane {
public class CustomServiceFormTabbedPane extends JBTabbedPane {
private final JBTable headersTable;
private final JBTable bodyTable;
CustomServiceFormTabbedPane(Map<String, String> headers, Map<String, ?> body) {
public CustomServiceFormTabbedPane(Map<String, String> headers, Map<String, ?> body) {
headersTable = new JBTable(
new DefaultTableModel(toArray(headers),
new Object[]{"Key", "Value"}));

View file

@ -1,13 +1,9 @@
package ee.carlrobert.codegpt.settings.service.openai;
import static ee.carlrobert.codegpt.credentials.CredentialsStore.CredentialKey.OPENAI_API_KEY;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.PersistentStateComponent;
import com.intellij.openapi.components.State;
import com.intellij.openapi.components.Storage;
import ee.carlrobert.codegpt.credentials.CredentialsStore;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
@State(name = "CodeGPT_OpenAISettings_210", storages = @Storage("CodeGPT_OpenAISettings_210.xml"))
@ -33,11 +29,4 @@ public class OpenAISettings implements PersistentStateComponent<OpenAISettingsSt
public static OpenAISettings getInstance() {
return ApplicationManager.getApplication().getService(OpenAISettings.class);
}
public boolean isModified(OpenAISettingsForm form) {
return !form.getCurrentState().equals(state)
|| !StringUtils.equals(
form.getApiKey(),
CredentialsStore.getCredential(OPENAI_API_KEY));
}
}

View file

@ -29,8 +29,4 @@ public class YouSettings implements PersistentStateComponent<YouSettingsState> {
public static YouSettings getInstance() {
return ApplicationManager.getApplication().getService(YouSettings.class);
}
public boolean isModified(YouSettingsForm form) {
return !form.getCurrentState().equals(state);
}
}