1.10.8 - Ability to use custom server/models, support api key retrieval from env var (closes #90)

This commit is contained in:
Carl-Robert Linnupuu 2023-05-17 22:58:43 +01:00
parent b26681ed7b
commit 0eba3a16af
16 changed files with 595 additions and 324 deletions

View file

@ -13,7 +13,7 @@ public class PluginStartupActivity implements StartupActivity {
public void runActivity(@NotNull Project project) {
ActionsUtil.refreshActions();
var settings = SettingsState.getInstance();
if (!settings.apiKey.isEmpty() && settings.useOpenAIAccountName) {
if (!settings.getApiKey().isEmpty() && settings.useOpenAIAccountName) {
ClientProvider.getDashboardClient()
.getSubscriptionAsync(subscription ->
settings.displayName = subscription.getAccountName());

View file

@ -1,5 +1,7 @@
package ee.carlrobert.codegpt.client;
import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager;
import ee.carlrobert.codegpt.state.settings.SettingsState;
import ee.carlrobert.codegpt.state.settings.advanced.AdvancedSettingsState;
import ee.carlrobert.openai.client.AzureClient;
@ -16,7 +18,7 @@ import java.util.concurrent.TimeUnit;
public class ClientProvider {
public static DashboardClient getDashboardClient() {
return getOpenAIClientBuilder().buildDashboardClient();
return getOpenAIClientBuilder(false).buildDashboardClient();
}
public static CompletionClient getChatCompletionClient(SettingsState settings) {
@ -32,25 +34,32 @@ public class ClientProvider {
}
private static OpenAIClient.Builder getOpenAIClientBuilder() {
return getOpenAIClientBuilder(true);
}
private static OpenAIClient.Builder getOpenAIClientBuilder(boolean isCustomServiceEnabled) {
var settings = SettingsState.getInstance();
var builder = new OpenAIClient.Builder(settings.apiKey);
var builder = new OpenAIClient.Builder(settings.getApiKey());
if (settings.useOpenAIService) {
builder.setOrganization(settings.organization);
}
return (OpenAIClient.Builder) addDefaultClientParams(builder);
return (OpenAIClient.Builder) addDefaultClientParams(builder, isCustomServiceEnabled);
}
private static AzureClient.Builder getAzureClientBuilder() {
var settings = SettingsState.getInstance();
var params = new AzureClientRequestParams(
settings.resourceName, settings.deploymentId, settings.apiVersion);
var builder = new AzureClient.Builder(settings.apiKey, params)
var builder = new AzureClient.Builder(settings.getApiKey(), params)
.setActiveDirectoryAuthentication(settings.useActiveDirectoryAuthentication);
return (AzureClient.Builder) addDefaultClientParams(builder);
return (AzureClient.Builder) addDefaultClientParams(builder, true);
}
private static Client.Builder addDefaultClientParams(Client.Builder builder) {
private static Client.Builder addDefaultClientParams(
Client.Builder builder, boolean isCustomServiceEnabled) {
var settings = SettingsState.getInstance();
var advancedSettings = AdvancedSettingsState.getInstance();
var proxyHost = advancedSettings.proxyHost;
var proxyPort = advancedSettings.proxyPort;
@ -63,13 +72,18 @@ public class ClientProvider {
}
}
if (!advancedSettings.host.isEmpty()) {
builder.setHost(advancedSettings.host);
if (isUnitTestingMode() || (isCustomServiceEnabled && settings.useCustomService)) {
builder.setHost(settings.customHost);
}
return builder
.setConnectTimeout(60L, TimeUnit.SECONDS)
.setReadTimeout(30L, TimeUnit.SECONDS);
.setConnectTimeout(120L, TimeUnit.SECONDS)
.setReadTimeout(60L, TimeUnit.SECONDS);
}
private static boolean isUnitTestingMode() {
Application app = ApplicationManager.getApplication();
return app != null && app.isUnitTestMode();
}
}

View file

@ -5,6 +5,7 @@ import static java.util.stream.Collectors.toList;
import ee.carlrobert.codegpt.EncodingManager;
import ee.carlrobert.codegpt.state.conversations.Conversation;
import ee.carlrobert.codegpt.state.conversations.ConversationsState;
import ee.carlrobert.codegpt.state.settings.SettingsState;
import ee.carlrobert.openai.client.completion.chat.ChatCompletionModel;
import ee.carlrobert.openai.client.completion.chat.request.ChatCompletionMessage;
import ee.carlrobert.openai.client.completion.chat.request.ChatCompletionRequest;
@ -53,7 +54,14 @@ class CompletionRequestProvider {
});
messages.add(new ChatCompletionMessage("user", prompt));
int totalUsage = messages.parallelStream().mapToInt(encodingManager::countMessageTokens).sum() + MAX_COMPLETION_TOKENS;
// Do not calculate total usage for custom services
if (SettingsState.getInstance().useCustomService) {
return messages;
}
int totalUsage = messages.parallelStream()
.mapToInt(encodingManager::countMessageTokens)
.sum() + MAX_COMPLETION_TOKENS;
int modelMaxTokens = ChatCompletionModel.findByCode(model).getMaxTokens();
if (totalUsage <= modelMaxTokens) {
@ -63,7 +71,8 @@ class CompletionRequestProvider {
return tryReducingMessagesOrThrow(messages, totalUsage, modelMaxTokens);
}
private List<ChatCompletionMessage> tryReducingMessagesOrThrow(List<ChatCompletionMessage> messages, int totalUsage, int modelMaxTokens) {
private List<ChatCompletionMessage> tryReducingMessagesOrThrow(
List<ChatCompletionMessage> messages, int totalUsage, int modelMaxTokens) {
if (!ConversationsState.getInstance().discardAllTokenLimits) {
if (!conversation.isDiscardTokenLimit()) {
throw new TotalUsageExceededException();

View file

@ -62,11 +62,11 @@ public class RequestHandler implements ActionListener {
if (settings.isChatCompletionOptionSelected) {
return ClientProvider.getChatCompletionClient(settings).stream(
requestProvider.buildChatCompletionRequest(settings.chatCompletionBaseModel),
requestProvider.buildChatCompletionRequest(settings.getChatCompletionModel()),
eventListener);
}
return ClientProvider.getTextCompletionClient(settings).stream(
requestProvider.buildTextCompletionRequest(settings.textCompletionBaseModel),
requestProvider.buildTextCompletionRequest(settings.getTextCompletionModel()),
eventListener);
}
}

View file

@ -77,8 +77,8 @@ public class ConversationsState implements PersistentStateComponent<Conversation
conversation.setId(UUID.randomUUID());
conversation.setClientCode(clientCode);
conversation.setModel(settings.isChatCompletionOptionSelected ?
settings.chatCompletionBaseModel :
settings.textCompletionBaseModel);
settings.getChatCompletionModel() :
settings.getTextCompletionModel());
conversation.setCreatedOn(LocalDateTime.now());
conversation.setUpdatedOn(LocalDateTime.now());
return conversation;

View file

@ -1,22 +1,18 @@
package ee.carlrobert.codegpt.state.settings;
import com.intellij.openapi.ui.ComboBox;
import com.intellij.ui.TitledSeparator;
import com.intellij.ui.components.JBCheckBox;
import com.intellij.ui.components.JBRadioButton;
import com.intellij.ui.components.JBTextField;
import com.intellij.util.ui.FormBuilder;
import com.intellij.util.ui.JBUI;
import com.intellij.util.ui.UI;
import ee.carlrobert.codegpt.state.settings.components.ModelSelectionForm;
import ee.carlrobert.codegpt.state.settings.components.ServiceSelectionForm;
import ee.carlrobert.codegpt.util.SwingUtils;
import ee.carlrobert.openai.client.completion.CompletionModel;
import ee.carlrobert.openai.client.completion.chat.ChatCompletionModel;
import ee.carlrobert.openai.client.completion.text.TextCompletionModel;
import java.awt.Desktop;
import java.awt.event.ItemEvent;
import java.io.IOException;
import java.net.URISyntaxException;
import javax.swing.BoxLayout;
import javax.swing.ButtonGroup;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.event.HyperlinkEvent;
@ -25,55 +21,23 @@ public class SettingsComponent {
private final JPanel mainPanel;
private final JBTextField apiKeyField;
private final JBTextField organizationField;
private final JBTextField resourceNameField;
private final JBTextField deploymentIdField;
private final JBTextField apiVersionField;
private final JBTextField displayNameField;
private final JBCheckBox useApiKeyFromEnvVarCheckBox;
private final JBCheckBox useOpenAIAccountNameCheckBox;
private final JBCheckBox useActiveDirectoryAuthenticationCheckBox;
private final JBRadioButton useAzureServiceRadioButton;
private final JBRadioButton useOpenAIServiceRadioButton;
private final ComboBox<CompletionModel> chatCompletionBaseModelComboBox;
private final ComboBox<CompletionModel> textCompletionBaseModelComboBox;
private final JBRadioButton useChatCompletionRadioButton;
private final JBRadioButton useTextCompletionRadioButton;
private final ServiceSelectionForm serviceSelectionForm;
private final ModelSelectionForm modelSelectionForm;
public SettingsComponent(SettingsState settings) {
modelSelectionForm = new ModelSelectionForm(settings);
serviceSelectionForm = new ServiceSelectionForm(settings, modelSelectionForm);
apiKeyField = new JBTextField(settings.apiKey, 40);
useOpenAIServiceRadioButton = new JBRadioButton("Use OpenAI service API",
settings.useOpenAIService);
useAzureServiceRadioButton = new JBRadioButton("Use Azure OpenAI service API",
settings.useAzureService);
resourceNameField = new JBTextField(settings.resourceName, 40);
deploymentIdField = new JBTextField(settings.resourceName, 40);
apiVersionField = new JBTextField(settings.resourceName, 40);
organizationField = new JBTextField(settings.organization, 40);
displayNameField = new JBTextField(settings.displayName, 20);
useApiKeyFromEnvVarCheckBox = new JBCheckBox(
"Use API Key from Environment Variable (recommended)", settings.useApiKeyFromEnvVar);
useOpenAIAccountNameCheckBox = new JBCheckBox(
"Use OpenAI account name", settings.useOpenAIAccountName);
useActiveDirectoryAuthenticationCheckBox = new JBCheckBox(
"Use Azure Active Directory authentication", settings.useActiveDirectoryAuthentication);
chatCompletionBaseModelComboBox = new BaseModelComboBox(
new ChatCompletionModel[]{
ChatCompletionModel.GPT_3_5,
ChatCompletionModel.GPT_3_5_SNAPSHOT,
ChatCompletionModel.GPT_4,
ChatCompletionModel.GPT_4_32k
},
ChatCompletionModel.findByCode(settings.chatCompletionBaseModel));
textCompletionBaseModelComboBox = new BaseModelComboBox(
new TextCompletionModel[]{
TextCompletionModel.DAVINCI,
TextCompletionModel.CURIE,
TextCompletionModel.BABBAGE,
TextCompletionModel.ADA,
},
TextCompletionModel.findByCode(settings.textCompletionBaseModel));
useChatCompletionRadioButton = new JBRadioButton("Use chat completion",
settings.isChatCompletionOptionSelected);
useTextCompletionRadioButton = new JBRadioButton("Use text completion",
settings.isTextCompletionOptionSelected);
mainPanel = FormBuilder.createFormBuilder()
.addComponent(new TitledSeparator("Integration Preference"))
.addVerticalGap(8)
@ -81,15 +45,14 @@ public class SettingsComponent {
.addVerticalGap(8)
.addComponent(new TitledSeparator("Service Preference"))
.addVerticalGap(8)
.addComponent(createServiceSelectionForm())
.addComponent(serviceSelectionForm.getForm())
.addVerticalGap(8)
.addComponent(new TitledSeparator("Model Preference"))
.addVerticalGap(8)
.addComponent(createModelSelectionForm())
.addComponent(modelSelectionForm.getForm())
.addComponentFillVertically(new JPanel(), 0)
.getPanel();
registerCompletionButtons();
registerFields();
}
@ -101,6 +64,14 @@ public class SettingsComponent {
return apiKeyField;
}
public ServiceSelectionForm getServiceSelectionForm() {
return serviceSelectionForm;
}
public ModelSelectionForm getModelSelectionForm() {
return modelSelectionForm;
}
public String getApiKey() {
return apiKeyField.getText();
}
@ -109,62 +80,6 @@ public class SettingsComponent {
apiKeyField.setText(apiKey);
}
public void setUseOpenAIServiceSelected(boolean selected) {
useOpenAIServiceRadioButton.setSelected(selected);
}
public boolean isUseOpenAIService() {
return useOpenAIServiceRadioButton.isSelected();
}
public void setUseAzureServiceSelected(boolean selected) {
useAzureServiceRadioButton.setSelected(selected);
}
public boolean isUseActiveDirectoryAuthentication() {
return useActiveDirectoryAuthenticationCheckBox.isSelected();
}
public void setUseActiveDirectoryAuthenticationSelected(boolean selected) {
useActiveDirectoryAuthenticationCheckBox.setSelected(selected);
}
public boolean isUseAzureService() {
return useAzureServiceRadioButton.isSelected();
}
public String getResourceName() {
return resourceNameField.getText();
}
public void setResourceName(String resourceName) {
resourceNameField.setText(resourceName);
}
public String getDeploymentId() {
return deploymentIdField.getText();
}
public void setDeploymentId(String deploymentId) {
deploymentIdField.setText(deploymentId);
}
public String getApiVersion() {
return apiVersionField.getText();
}
public void setApiVersionField(String apiVersion) {
apiVersionField.setText(apiVersion);
}
public String getOrganization() {
return organizationField.getText();
}
public void setOrganization(String organization) {
organizationField.setText(organization);
}
public String getDisplayName() {
return displayNameField.getText();
}
@ -173,6 +88,14 @@ public class SettingsComponent {
displayNameField.setText(displayName);
}
public void setUseApiKeyFromEnvVarCheckBox(boolean selected) {
useApiKeyFromEnvVarCheckBox.setSelected(selected);
}
public boolean isUseApiKeyFromEnvVar() {
return useApiKeyFromEnvVarCheckBox.isSelected();
}
public void setUseOpenAIAccountNameCheckBox(boolean selected) {
useOpenAIAccountNameCheckBox.setSelected(selected);
}
@ -181,38 +104,6 @@ public class SettingsComponent {
return useOpenAIAccountNameCheckBox.isSelected();
}
public boolean isChatCompletionOptionSelected() {
return useChatCompletionRadioButton.isSelected();
}
public void setUseChatCompletionSelected(boolean isSelected) {
useChatCompletionRadioButton.setSelected(isSelected);
}
public boolean isTextCompletionOptionSelected() {
return useTextCompletionRadioButton.isSelected();
}
public void setUseTextCompletionSelected(boolean isSelected) {
useTextCompletionRadioButton.setSelected(isSelected);
}
public TextCompletionModel getTextCompletionBaseModel() {
return (TextCompletionModel) textCompletionBaseModelComboBox.getSelectedItem();
}
public void setTextCompletionBaseModel(String modelCode) {
textCompletionBaseModelComboBox.setSelectedItem(TextCompletionModel.findByCode(modelCode));
}
public ChatCompletionModel getChatCompletionBaseModel() {
return (ChatCompletionModel) chatCompletionBaseModelComboBox.getSelectedItem();
}
public void setChatCompletionBaseModel(String modelCode) {
chatCompletionBaseModelComboBox.setSelectedItem(ChatCompletionModel.findByCode(modelCode));
}
private JPanel createMainSelectionForm() {
var apiKeyFieldPanel = UI.PanelFactory.panel(apiKeyField)
.withLabel("API key:")
@ -222,12 +113,23 @@ public class SettingsComponent {
.withCommentHyperlinkListener(this::handleHyperlinkClicked)
.createPanel();
var displayNameFieldPanel = SwingUtils.createPanel(displayNameField, "Display name:", false);
var apiKeyEnvVarFromCheckboxFieldPanel = UI.PanelFactory.panel(useApiKeyFromEnvVarCheckBox)
.resizeX(false)
.withComment(
"Set your OpenAI API key as OPENAI_API_KEY in <a href=\"https://help.openai.com/en/articles/5112595-best-practices-for-api-key-safety#h_a1ab3ba7b2\">environment variables</a>.")
.withCommentHyperlinkListener(this::handleHyperlinkClicked)
.createPanel();
SwingUtils.setEqualLabelWidths(apiKeyFieldPanel, displayNameFieldPanel);
useApiKeyFromEnvVarCheckBox.addItemListener(event ->
apiKeyField.setEnabled(event.getStateChange() != ItemEvent.SELECTED));
var panel = FormBuilder.createFormBuilder()
.addComponent(FormBuilder.createFormBuilder()
.addComponent(apiKeyEnvVarFromCheckboxFieldPanel)
.addComponent(apiKeyFieldPanel)
.addVerticalGap(8)
.addComponent(displayNameFieldPanel)
.addComponent(useOpenAIAccountNameCheckBox)
.getPanel())
@ -236,127 +138,8 @@ public class SettingsComponent {
return panel;
}
private JPanel createServiceSelectionForm() {
var organizationFieldPanel = UI.PanelFactory.panel(organizationField)
.withLabel("Organization:")
.resizeX(false)
.withComment(
"<span>Useful when you are part of multiple organizations <sup><strong>optional</strong></sup></span")
.createPanel();
organizationFieldPanel.setBorder(JBUI.Borders.empty(8, 16, 0, 0));
var azureRelatedFieldsPanel = createAzureServicePanel();
registerServiceButtons(azureRelatedFieldsPanel, organizationFieldPanel);
var panel = FormBuilder.createFormBuilder()
.addComponent(FormBuilder.createFormBuilder()
.addComponent(useOpenAIServiceRadioButton)
.addComponent(organizationFieldPanel)
.addVerticalGap(8)
.addComponent(useAzureServiceRadioButton)
.addComponent(azureRelatedFieldsPanel)
.getPanel())
.getPanel();
panel.setBorder(JBUI.Borders.emptyLeft(16));
return panel;
}
private JPanel createModelSelectionForm() {
var chatCompletionModelsPanel = SwingUtils.createPanel(
chatCompletionBaseModelComboBox, "Model:", false);
chatCompletionModelsPanel.setBorder(JBUI.Borders.emptyLeft(16));
var textCompletionModelsPanel = SwingUtils.createPanel(
textCompletionBaseModelComboBox, "Model:", false);
textCompletionModelsPanel.setBorder(JBUI.Borders.emptyLeft(16));
var panel = FormBuilder.createFormBuilder()
.addComponent(FormBuilder.createFormBuilder()
.addComponent(UI.PanelFactory.panel(useChatCompletionRadioButton)
.withComment("OpenAIs most advanced language model")
.createPanel())
.addComponent(chatCompletionModelsPanel)
.addComponent(UI.PanelFactory.panel(useTextCompletionRadioButton)
.withComment("Best for high-quality texts")
.createPanel())
.addComponent(textCompletionModelsPanel)
.getPanel())
.getPanel();
panel.setBorder(JBUI.Borders.emptyLeft(16));
return panel;
}
private JPanel createAzureServicePanel() {
JPanel azureRelatedFieldsPanel = new JPanel();
var resourceNameFieldPanel = UI.PanelFactory.panel(resourceNameField)
.withLabel("Resource name:")
.resizeX(false)
.withComment(
"Azure OpenAI Service resource name")
.createPanel();
var deploymentIdFieldPanel = UI.PanelFactory.panel(deploymentIdField)
.withLabel("Deployment ID:")
.resizeX(false)
.withComment(
"Azure OpenAI Service deployment ID")
.createPanel();
var apiVersionFieldPanel = UI.PanelFactory.panel(apiVersionField)
.withLabel("API version:")
.resizeX(false)
.withComment(
"API version to be used for Azure OpenAI Service")
.createPanel();
var authFieldPanel = UI.PanelFactory.panel(useActiveDirectoryAuthenticationCheckBox)
.resizeX(false)
.createPanel();
azureRelatedFieldsPanel.setLayout(new BoxLayout(azureRelatedFieldsPanel, BoxLayout.Y_AXIS));
azureRelatedFieldsPanel.setBorder(JBUI.Borders.empty(8, 16, 0, 0));
azureRelatedFieldsPanel.add(resourceNameFieldPanel);
azureRelatedFieldsPanel.add(deploymentIdFieldPanel);
azureRelatedFieldsPanel.add(apiVersionFieldPanel);
azureRelatedFieldsPanel.add(authFieldPanel);
SwingUtils.setEqualLabelWidths(deploymentIdFieldPanel, resourceNameFieldPanel);
SwingUtils.setEqualLabelWidths(apiVersionFieldPanel, resourceNameFieldPanel);
azureRelatedFieldsPanel.setVisible(SettingsState.getInstance().useAzureService);
return azureRelatedFieldsPanel;
}
private void registerServiceButtons(
JPanel azureRelatedFieldsPanel, JPanel organizationFieldPanel) {
var serviceButtonGroup = new ButtonGroup();
serviceButtonGroup.add(useOpenAIServiceRadioButton);
serviceButtonGroup.add(useAzureServiceRadioButton);
useOpenAIServiceRadioButton.addActionListener(e -> {
azureRelatedFieldsPanel.setVisible(false);
organizationFieldPanel.setVisible(true);
});
useAzureServiceRadioButton.addActionListener(e -> {
azureRelatedFieldsPanel.setVisible(true);
organizationFieldPanel.setVisible(false);
});
}
private void registerCompletionButtons() {
var completionButtonGroup = new ButtonGroup();
completionButtonGroup.add(useChatCompletionRadioButton);
completionButtonGroup.add(useTextCompletionRadioButton);
useChatCompletionRadioButton.addActionListener(e -> {
chatCompletionBaseModelComboBox.setEnabled(true);
textCompletionBaseModelComboBox.setEnabled(false);
});
useTextCompletionRadioButton.addActionListener(e -> {
chatCompletionBaseModelComboBox.setEnabled(false);
textCompletionBaseModelComboBox.setEnabled(true);
});
}
private void registerFields() {
chatCompletionBaseModelComboBox.setEnabled(useChatCompletionRadioButton.isSelected());
textCompletionBaseModelComboBox.setEnabled(useTextCompletionRadioButton.isSelected());
apiKeyField.setEnabled(!useApiKeyFromEnvVarCheckBox.isSelected());
}
private void handleHyperlinkClicked(HyperlinkEvent event) {

View file

@ -36,19 +36,28 @@ public class SettingsConfigurable implements Configurable {
@Override
public boolean isModified() {
var settings = SettingsState.getInstance();
var serviceSelectionForm = settingsComponent.getServiceSelectionForm();
var modelSelectionForm = settingsComponent.getModelSelectionForm();
return !settingsComponent.getApiKey().equals(settings.apiKey) ||
settingsComponent.isUseOpenAIService() != settings.useOpenAIService ||
settingsComponent.isUseAzureService() != settings.useAzureService ||
settingsComponent.isUseActiveDirectoryAuthentication() !=
settings.useActiveDirectoryAuthentication ||
!settingsComponent.getResourceName().equals(settings.resourceName) ||
!settingsComponent.getDeploymentId().equals(settings.deploymentId) ||
!settingsComponent.getApiVersion().equals(settings.apiVersion) ||
!settingsComponent.getOrganization().equals(settings.organization) ||
!settingsComponent.getDisplayName().equals(settings.displayName) ||
settingsComponent.isUseApiKeyFromEnvVar() != settings.useApiKeyFromEnvVar ||
settingsComponent.isUseOpenAIAccountName() != settings.useOpenAIAccountName ||
serviceSelectionForm.isUseOpenAIService() != settings.useOpenAIService ||
serviceSelectionForm.isUseAzureService() != settings.useAzureService ||
serviceSelectionForm.isUseCustomService() != settings.useCustomService ||
serviceSelectionForm.isUseActiveDirectoryAuthentication() !=
settings.useActiveDirectoryAuthentication ||
!serviceSelectionForm.getCustomHost().equals(settings.customHost) ||
!serviceSelectionForm.getResourceName().equals(settings.resourceName) ||
!serviceSelectionForm.getDeploymentId().equals(settings.deploymentId) ||
!serviceSelectionForm.getApiVersion().equals(settings.apiVersion) ||
!serviceSelectionForm.getOrganization().equals(settings.organization) ||
!modelSelectionForm.getCustomChatCompletionModel()
.equals(settings.customChatCompletionModel) ||
!modelSelectionForm.getCustomTextCompletionModel()
.equals(settings.customTextCompletionModel) ||
isModelChanged(settings) ||
isClientChanged(settings);
isCompletionOptionChanged(settings);
}
@Override
@ -62,7 +71,7 @@ public class SettingsConfigurable implements Configurable {
: settings.textCompletionBaseModel);
}
if (isClientChanged(settings) || isModelChanged) {
if (isCompletionOptionChanged(settings) || isModelChanged) {
ConversationsState.getInstance().setCurrentConversation(null);
var project = ProjectUtil.guessCurrentProject(
settingsComponent.getPanel()); // TODO: Find a better way
@ -76,40 +85,56 @@ public class SettingsConfigurable implements Configurable {
});
}
var serviceSelectionForm = settingsComponent.getServiceSelectionForm();
var modelSelectionForm = settingsComponent.getModelSelectionForm();
settings.apiKey = settingsComponent.getApiKey();
settings.useOpenAIService = settingsComponent.isUseOpenAIService();
settings.useAzureService = settingsComponent.isUseAzureService();
settings.useActiveDirectoryAuthentication = settingsComponent.isUseActiveDirectoryAuthentication();
settings.resourceName = settingsComponent.getResourceName();
settings.deploymentId = settingsComponent.getDeploymentId();
settings.apiVersion = settingsComponent.getApiVersion();
settings.organization = settingsComponent.getOrganization();
settings.displayName = settingsComponent.getDisplayName();
settings.useApiKeyFromEnvVar = settingsComponent.isUseApiKeyFromEnvVar();
settings.useOpenAIAccountName = settingsComponent.isUseOpenAIAccountName();
settings.chatCompletionBaseModel = settingsComponent.getChatCompletionBaseModel().getCode();
settings.isChatCompletionOptionSelected = settingsComponent.isChatCompletionOptionSelected();
settings.isTextCompletionOptionSelected = settingsComponent.isTextCompletionOptionSelected();
settings.textCompletionBaseModel = settingsComponent.getTextCompletionBaseModel().getCode();
settings.useOpenAIService = serviceSelectionForm.isUseOpenAIService();
settings.useAzureService = serviceSelectionForm.isUseAzureService();
settings.useCustomService = serviceSelectionForm.isUseCustomService();
settings.customHost = serviceSelectionForm.getCustomHost();
settings.useActiveDirectoryAuthentication = serviceSelectionForm.isUseActiveDirectoryAuthentication();
settings.resourceName = serviceSelectionForm.getResourceName();
settings.deploymentId = serviceSelectionForm.getDeploymentId();
settings.apiVersion = serviceSelectionForm.getApiVersion();
settings.organization = serviceSelectionForm.getOrganization();
settings.chatCompletionBaseModel = modelSelectionForm.getChatCompletionBaseModel().getCode();
settings.textCompletionBaseModel = modelSelectionForm.getTextCompletionBaseModel().getCode();
settings.customChatCompletionModel = modelSelectionForm.getCustomChatCompletionModel();
settings.customTextCompletionModel = modelSelectionForm.getCustomTextCompletionModel();
settings.isChatCompletionOptionSelected = modelSelectionForm.isChatCompletionOptionSelected();
settings.isTextCompletionOptionSelected = modelSelectionForm.isTextCompletionOptionSelected();
}
@Override
public void reset() {
var settings = SettingsState.getInstance();
settingsComponent.setUseChatCompletionSelected(settings.isChatCompletionOptionSelected);
settingsComponent.setUseTextCompletionSelected(settings.isTextCompletionOptionSelected);
var serviceSelectionForm = settingsComponent.getServiceSelectionForm();
var modelSelectionForm = settingsComponent.getModelSelectionForm();
settingsComponent.setApiKey(settings.apiKey);
settingsComponent.setUseOpenAIServiceSelected(settings.useAzureService);
settingsComponent.setUseAzureServiceSelected(settings.useAzureService);
settingsComponent.setUseActiveDirectoryAuthenticationSelected(
settings.useActiveDirectoryAuthentication);
settingsComponent.setResourceName(settings.resourceName);
settingsComponent.setDeploymentId(settings.deploymentId);
settingsComponent.setApiVersionField(settings.apiVersion);
settingsComponent.setOrganization(settings.organization);
settingsComponent.setUseApiKeyFromEnvVarCheckBox(settings.useApiKeyFromEnvVar);
settingsComponent.setDisplayName(settings.displayName);
settingsComponent.setUseOpenAIAccountNameCheckBox(settings.useOpenAIAccountName);
settingsComponent.setChatCompletionBaseModel(settings.chatCompletionBaseModel);
settingsComponent.setTextCompletionBaseModel(settings.textCompletionBaseModel);
serviceSelectionForm.setUseOpenAIServiceSelected(settings.useAzureService);
serviceSelectionForm.setUseAzureServiceSelected(settings.useAzureService);
serviceSelectionForm.setUseCustomServiceSelected(settings.useCustomService);
serviceSelectionForm.setCustomHost(settings.customHost);
serviceSelectionForm.setUseActiveDirectoryAuthenticationSelected(
settings.useActiveDirectoryAuthentication);
serviceSelectionForm.setResourceName(settings.resourceName);
serviceSelectionForm.setDeploymentId(settings.deploymentId);
serviceSelectionForm.setApiVersionField(settings.apiVersion);
serviceSelectionForm.setOrganization(settings.organization);
modelSelectionForm.setUseChatCompletionSelected(settings.isChatCompletionOptionSelected);
modelSelectionForm.setUseTextCompletionSelected(settings.isTextCompletionOptionSelected);
modelSelectionForm.setChatCompletionBaseModel(settings.chatCompletionBaseModel);
modelSelectionForm.setTextCompletionBaseModel(settings.textCompletionBaseModel);
modelSelectionForm.setCustomChatCompletionModel(settings.customChatCompletionModel);
modelSelectionForm.setCustomTextCompletionModel(settings.customTextCompletionModel);
}
@Override
@ -117,17 +142,19 @@ public class SettingsConfigurable implements Configurable {
settingsComponent = null;
}
private boolean isClientChanged(SettingsState settings) {
return settingsComponent.isChatCompletionOptionSelected()
private boolean isCompletionOptionChanged(SettingsState settings) {
var modelSelectionForm = settingsComponent.getModelSelectionForm();
return modelSelectionForm.isChatCompletionOptionSelected()
!= settings.isChatCompletionOptionSelected ||
settingsComponent.isTextCompletionOptionSelected()
modelSelectionForm.isTextCompletionOptionSelected()
!= settings.isTextCompletionOptionSelected;
}
private boolean isModelChanged(SettingsState settings) {
return !settingsComponent.getChatCompletionBaseModel().getCode()
var modelSelectionForm = settingsComponent.getModelSelectionForm();
return !modelSelectionForm.getChatCompletionBaseModel().getCode()
.equals(settings.chatCompletionBaseModel) ||
!settingsComponent.getTextCompletionBaseModel().getCode()
!modelSelectionForm.getTextCompletionBaseModel().getCode()
.equals(settings.textCompletionBaseModel);
}
}

View file

@ -19,17 +19,22 @@ import org.jetbrains.annotations.Nullable;
public class SettingsState implements PersistentStateComponent<SettingsState> {
public String apiKey = "";
public boolean useApiKeyFromEnvVar = false;
public boolean useOpenAIService = true;
public boolean useAzureService;
public boolean useCustomService;
public String resourceName = "";
public String deploymentId = "";
public String apiVersion = "";
public boolean useActiveDirectoryAuthentication;
public String organization = "";
public String customHost = "";
public String displayName = getDisplayName();
public boolean useOpenAIAccountName = true;
public String textCompletionBaseModel = TextCompletionModel.DAVINCI.getCode();
public String chatCompletionBaseModel = ChatCompletionModel.GPT_3_5.getCode();
public String customChatCompletionModel = "";
public String customTextCompletionModel = "";
public boolean isChatCompletionOptionSelected = true;
public boolean isTextCompletionOptionSelected;
@ -50,15 +55,46 @@ public class SettingsState implements PersistentStateComponent<SettingsState> {
public void syncSettings(Conversation conversation) {
var isChatCompletions = ClientCode.CHAT_COMPLETION.equals(conversation.getClientCode());
var model = conversation.getModel();
if (isChatCompletions) {
chatCompletionBaseModel = conversation.getModel();
if (useCustomService) {
customChatCompletionModel = model;
} else {
chatCompletionBaseModel = model;
}
} else {
textCompletionBaseModel = conversation.getModel();
if (useCustomService) {
customTextCompletionModel = model;
} else {
textCompletionBaseModel = model;
}
}
isChatCompletionOptionSelected = isChatCompletions;
isTextCompletionOptionSelected = !isChatCompletions;
}
public String getApiKey() {
if (useApiKeyFromEnvVar) {
var envApiKey = System.getenv("OPENAI_API_KEY");
return envApiKey == null ? "" : envApiKey;
}
return apiKey;
}
public String getChatCompletionModel() {
if (useCustomService) {
return customChatCompletionModel;
}
return chatCompletionBaseModel;
}
public String getTextCompletionModel() {
if (useCustomService) {
return customTextCompletionModel;
}
return textCompletionBaseModel;
}
private String getDisplayName() {
var name = System.getProperty("user.name");
return name == null || name.isEmpty() ? "User" : name;

View file

@ -15,7 +15,6 @@ import org.jetbrains.annotations.Nullable;
)
public class AdvancedSettingsState implements PersistentStateComponent<AdvancedSettingsState> {
public String host = "";
public String proxyHost = "";
public int proxyPort;
public Proxy.Type proxyType = Proxy.Type.SOCKS;

View file

@ -0,0 +1,161 @@
package ee.carlrobert.codegpt.state.settings.components;
import com.intellij.openapi.ui.ComboBox;
import com.intellij.ui.components.JBRadioButton;
import com.intellij.ui.components.JBTextField;
import com.intellij.util.ui.FormBuilder;
import com.intellij.util.ui.JBUI;
import com.intellij.util.ui.UI;
import ee.carlrobert.codegpt.state.settings.BaseModelComboBox;
import ee.carlrobert.codegpt.state.settings.SettingsState;
import ee.carlrobert.codegpt.util.SwingUtils;
import ee.carlrobert.openai.client.completion.CompletionModel;
import ee.carlrobert.openai.client.completion.chat.ChatCompletionModel;
import ee.carlrobert.openai.client.completion.text.TextCompletionModel;
import javax.swing.ButtonGroup;
import javax.swing.JPanel;
public class ModelSelectionForm {
private final ComboBox<CompletionModel> chatCompletionBaseModelComboBox;
private final ComboBox<CompletionModel> textCompletionBaseModelComboBox;
private final JBTextField customChatCompletionModelField;
private final JBTextField customTextCompletionModelField;
private final JBRadioButton useChatCompletionRadioButton;
private final JBRadioButton useTextCompletionRadioButton;
private final JPanel chatCompletionModelsPanel;
private final JPanel customChatCompletionModelPanel;
private final JPanel customTextCompletionModelPanel;
private final JPanel textCompletionModelsPanel;
public ModelSelectionForm(SettingsState settings) {
chatCompletionBaseModelComboBox = new BaseModelComboBox(
new ChatCompletionModel[]{
ChatCompletionModel.GPT_3_5,
ChatCompletionModel.GPT_3_5_SNAPSHOT,
ChatCompletionModel.GPT_4,
ChatCompletionModel.GPT_4_32k
},
ChatCompletionModel.findByCode(settings.chatCompletionBaseModel));
chatCompletionModelsPanel = SwingUtils.createPanel(
chatCompletionBaseModelComboBox, "Model:", false);
chatCompletionModelsPanel.setBorder(JBUI.Borders.emptyLeft(16));
textCompletionBaseModelComboBox = new BaseModelComboBox(
new TextCompletionModel[]{
TextCompletionModel.DAVINCI,
TextCompletionModel.CURIE,
TextCompletionModel.BABBAGE,
TextCompletionModel.ADA,
},
TextCompletionModel.findByCode(settings.textCompletionBaseModel));
customChatCompletionModelField = new JBTextField(settings.customChatCompletionModel, 20);
customChatCompletionModelPanel = SwingUtils.createPanel(
customChatCompletionModelField, "Model:", false);
customChatCompletionModelPanel.setBorder(JBUI.Borders.emptyLeft(16));
customTextCompletionModelField = new JBTextField(settings.customTextCompletionModel, 20);
customTextCompletionModelPanel = SwingUtils.createPanel(
customTextCompletionModelField, "Model:", false);
customTextCompletionModelPanel.setBorder(JBUI.Borders.emptyLeft(16));
textCompletionModelsPanel = SwingUtils.createPanel(
textCompletionBaseModelComboBox, "Model:", false);
textCompletionModelsPanel.setBorder(JBUI.Borders.emptyLeft(16));
useChatCompletionRadioButton = new JBRadioButton("Use chat completion",
settings.isChatCompletionOptionSelected);
useTextCompletionRadioButton = new JBRadioButton("Use text completion",
settings.isTextCompletionOptionSelected);
registerFields();
registerRadioButtons();
}
public JPanel getForm() {
var panel = FormBuilder.createFormBuilder()
.addComponent(UI.PanelFactory.panel(useChatCompletionRadioButton)
.withComment("OpenAIs most advanced language model")
.createPanel())
.addComponent(customChatCompletionModelPanel)
.addComponent(chatCompletionModelsPanel)
.addComponent(UI.PanelFactory.panel(useTextCompletionRadioButton)
.withComment("Best for high-quality texts")
.createPanel())
.addComponent(customTextCompletionModelPanel)
.addComponent(textCompletionModelsPanel)
.getPanel();
panel.setBorder(JBUI.Borders.emptyLeft(16));
return panel;
}
public void changeModelPanelsVisibility(boolean isCustomModelsVisible) {
chatCompletionModelsPanel.setVisible(!isCustomModelsVisible);
textCompletionModelsPanel.setVisible(!isCustomModelsVisible);
customChatCompletionModelPanel.setVisible(isCustomModelsVisible);
customTextCompletionModelPanel.setVisible(isCustomModelsVisible);
}
public boolean isChatCompletionOptionSelected() {
return useChatCompletionRadioButton.isSelected();
}
public void setUseChatCompletionSelected(boolean isSelected) {
useChatCompletionRadioButton.setSelected(isSelected);
}
public boolean isTextCompletionOptionSelected() {
return useTextCompletionRadioButton.isSelected();
}
public void setUseTextCompletionSelected(boolean isSelected) {
useTextCompletionRadioButton.setSelected(isSelected);
}
public TextCompletionModel getTextCompletionBaseModel() {
return (TextCompletionModel) textCompletionBaseModelComboBox.getSelectedItem();
}
public void setTextCompletionBaseModel(String modelCode) {
textCompletionBaseModelComboBox.setSelectedItem(TextCompletionModel.findByCode(modelCode));
}
public ChatCompletionModel getChatCompletionBaseModel() {
return (ChatCompletionModel) chatCompletionBaseModelComboBox.getSelectedItem();
}
public void setChatCompletionBaseModel(String modelCode) {
chatCompletionBaseModelComboBox.setSelectedItem(ChatCompletionModel.findByCode(modelCode));
}
public String getCustomChatCompletionModel() {
return customChatCompletionModelField.getText();
}
public void setCustomChatCompletionModel(String model) {
customChatCompletionModelField.setText(model);
}
public String getCustomTextCompletionModel() {
return customTextCompletionModelField.getText();
}
public void setCustomTextCompletionModel(String model) {
customTextCompletionModelField.setText(model);
}
private void registerRadioButtons() {
var completionButtonGroup = new ButtonGroup();
completionButtonGroup.add(useChatCompletionRadioButton);
completionButtonGroup.add(useTextCompletionRadioButton);
useChatCompletionRadioButton.addActionListener(e -> enableModelFields(true));
useTextCompletionRadioButton.addActionListener(e -> enableModelFields(false));
}
private void registerFields() {
enableModelFields(useChatCompletionRadioButton.isSelected());
}
private void enableModelFields(boolean isChatCompletionModel) {
chatCompletionBaseModelComboBox.setEnabled(isChatCompletionModel);
customChatCompletionModelField.setEnabled(isChatCompletionModel);
textCompletionBaseModelComboBox.setEnabled(!isChatCompletionModel);
customTextCompletionModelField.setEnabled(!isChatCompletionModel);
}
}

View file

@ -0,0 +1,234 @@
package ee.carlrobert.codegpt.state.settings.components;
import com.intellij.ui.components.JBCheckBox;
import com.intellij.ui.components.JBRadioButton;
import com.intellij.ui.components.JBTextField;
import com.intellij.util.ui.FormBuilder;
import com.intellij.util.ui.JBUI;
import com.intellij.util.ui.JBUI.Borders;
import com.intellij.util.ui.UI;
import ee.carlrobert.codegpt.state.settings.SettingsState;
import ee.carlrobert.codegpt.util.SwingUtils;
import javax.swing.ButtonGroup;
import javax.swing.JComponent;
import javax.swing.JPanel;
public class ServiceSelectionForm {
private final SettingsState settings;
private final JBTextField openAIOrganizationField;
private final JBTextField azureResourceNameField;
private final JBTextField azureDeploymentIdField;
private final JBTextField azureApiVersionField;
private final JBTextField customHostField;
private final JBCheckBox useActiveDirectoryAuthenticationCheckBox;
private final JBRadioButton useCustomServiceRadioButton;
private final JBRadioButton useAzureServiceRadioButton;
private final JBRadioButton useOpenAIServiceRadioButton;
private final JPanel openAIServiceSectionPanel;
private final JPanel azureServiceSectionPanel;
private final JPanel customServiceSectionPanel;
private final ModelSelectionForm modelSelectionForm;
public ServiceSelectionForm(SettingsState settings, ModelSelectionForm modelSelectionForm) {
this.settings = settings;
this.modelSelectionForm = modelSelectionForm;
useOpenAIServiceRadioButton = new JBRadioButton(
"Use OpenAI service API", settings.useOpenAIService);
useAzureServiceRadioButton = new JBRadioButton(
"Use Azure OpenAI service API", settings.useAzureService);
useCustomServiceRadioButton = new JBRadioButton(
"Use custom service", settings.useCustomService);
useActiveDirectoryAuthenticationCheckBox = new JBCheckBox(
"Use Azure Active Directory authentication", settings.useActiveDirectoryAuthentication);
openAIOrganizationField = new JBTextField(settings.organization, 40);
azureResourceNameField = new JBTextField(settings.resourceName, 40);
azureDeploymentIdField = new JBTextField(settings.deploymentId, 40);
azureApiVersionField = new JBTextField(settings.apiVersion, 40);
customHostField = new JBTextField(settings.customHost, 40);
openAIServiceSectionPanel = createOpenAIServiceSectionPanel();
azureServiceSectionPanel = createAzureServiceSectionPanel();
customServiceSectionPanel = createCustomServiceSectionPanel();
registerSectionPanels();
registerRadioButtons();
}
public JPanel getForm() {
var panel = FormBuilder.createFormBuilder()
.addComponent(useOpenAIServiceRadioButton)
.addComponent(openAIServiceSectionPanel)
.addVerticalGap(8)
.addComponent(useAzureServiceRadioButton)
.addComponent(azureServiceSectionPanel)
.addVerticalGap(8)
.addComponent(useCustomServiceRadioButton)
.addComponent(customServiceSectionPanel)
.getPanel();
panel.setBorder(JBUI.Borders.emptyLeft(16));
return panel;
}
public void setUseOpenAIServiceSelected(boolean selected) {
useOpenAIServiceRadioButton.setSelected(selected);
}
public boolean isUseOpenAIService() {
return useOpenAIServiceRadioButton.isSelected();
}
public void setUseAzureServiceSelected(boolean selected) {
useAzureServiceRadioButton.setSelected(selected);
}
public boolean isUseActiveDirectoryAuthentication() {
return useActiveDirectoryAuthenticationCheckBox.isSelected();
}
public void setUseActiveDirectoryAuthenticationSelected(boolean selected) {
useActiveDirectoryAuthenticationCheckBox.setSelected(selected);
}
public boolean isUseAzureService() {
return useAzureServiceRadioButton.isSelected();
}
public void setUseCustomServiceSelected(boolean selected) {
useCustomServiceRadioButton.setSelected(selected);
}
public boolean isUseCustomService() {
return useCustomServiceRadioButton.isSelected();
}
public String getCustomHost() {
return customHostField.getText();
}
public void setCustomHost(String customHost) {
customHostField.setText(customHost);
}
public String getResourceName() {
return azureResourceNameField.getText();
}
public void setResourceName(String resourceName) {
azureResourceNameField.setText(resourceName);
}
public String getDeploymentId() {
return azureDeploymentIdField.getText();
}
public void setDeploymentId(String deploymentId) {
azureDeploymentIdField.setText(deploymentId);
}
public String getApiVersion() {
return azureApiVersionField.getText();
}
public void setApiVersionField(String apiVersion) {
azureApiVersionField.setText(apiVersion);
}
public String getOrganization() {
return openAIOrganizationField.getText();
}
public void setOrganization(String organization) {
openAIOrganizationField.setText(organization);
}
private JPanel createPanel(JComponent component, String label, String comment) {
return UI.PanelFactory.panel(component)
.withLabel(label)
.resizeX(false)
.withComment(comment)
.createPanel();
}
private JPanel createOpenAIServiceSectionPanel() {
var organizationFieldPanel = UI.PanelFactory.panel(openAIOrganizationField)
.withLabel("Organization:")
.resizeX(false)
.withComment(
"Useful when you are part of multiple organizations <sup><strong>optional</strong></sup>")
.createPanel();
organizationFieldPanel.setBorder(JBUI.Borders.empty(8, 16, 0, 0));
return organizationFieldPanel;
}
private JPanel createAzureServiceSectionPanel() {
var resourceNameFieldPanel = createPanel(
azureResourceNameField, "Resource name:", "Azure OpenAI Service resource name");
var deploymentIdFieldPanel = createPanel(
azureDeploymentIdField, "Deployment ID:", "Azure OpenAI Service deployment ID");
var apiVersionFieldPanel = createPanel(
azureApiVersionField, "API version:", "API version to be used for Azure OpenAI Service");
var authFieldPanel = UI.PanelFactory.panel(useActiveDirectoryAuthenticationCheckBox)
.resizeX(false)
.createPanel();
var azureRelatedFieldsPanel = FormBuilder.createFormBuilder()
.addVerticalGap(8)
.addComponent(resourceNameFieldPanel)
.addComponent(deploymentIdFieldPanel)
.addComponent(apiVersionFieldPanel)
.addComponent(authFieldPanel)
.getPanel();
azureRelatedFieldsPanel.setBorder(Borders.emptyLeft(16));
SwingUtils.setEqualLabelWidths(deploymentIdFieldPanel, resourceNameFieldPanel);
SwingUtils.setEqualLabelWidths(apiVersionFieldPanel, resourceNameFieldPanel);
return azureRelatedFieldsPanel;
}
private JPanel createCustomServiceSectionPanel() {
var customHostFieldPanel = UI.PanelFactory.panel(customHostField)
.withLabel("Custom host:")
.withComment("Example: <strong>http://localhost:8080</strong>")
.resizeX(false)
.createPanel();
customHostFieldPanel.setBorder(JBUI.Borders.empty(8, 16, 0, 0));
return customHostFieldPanel;
}
private void registerSectionPanels() {
openAIServiceSectionPanel.setVisible(settings.useOpenAIService);
azureServiceSectionPanel.setVisible(settings.useAzureService);
customServiceSectionPanel.setVisible(settings.useCustomService);
modelSelectionForm.changeModelPanelsVisibility(settings.useCustomService);
}
private void registerRadioButtons() {
var serviceButtonGroup = new ButtonGroup();
serviceButtonGroup.add(useOpenAIServiceRadioButton);
serviceButtonGroup.add(useAzureServiceRadioButton);
serviceButtonGroup.add(useCustomServiceRadioButton);
useOpenAIServiceRadioButton.addActionListener(e -> {
openAIServiceSectionPanel.setVisible(true);
azureServiceSectionPanel.setVisible(false);
customServiceSectionPanel.setVisible(false);
modelSelectionForm.changeModelPanelsVisibility(false);
});
useAzureServiceRadioButton.addActionListener(e -> {
openAIServiceSectionPanel.setVisible(false);
azureServiceSectionPanel.setVisible(true);
customServiceSectionPanel.setVisible(false);
modelSelectionForm.changeModelPanelsVisibility(false);
});
useCustomServiceRadioButton.addActionListener(e -> {
openAIServiceSectionPanel.setVisible(false);
azureServiceSectionPanel.setVisible(false);
customServiceSectionPanel.setVisible(true);
modelSelectionForm.changeModelPanelsVisibility(true);
});
}
}

View file

@ -83,7 +83,7 @@ public class ChatToolWindowTabHtmlPanel implements ToolWindowTabPanel {
markdownHtmlPanel.runWhenLoaded(() -> {
markdownHtmlPanel.displayUserMessage(message);
var settings = SettingsState.getInstance();
if (settings.apiKey.isEmpty()) {
if (settings.getApiKey().isEmpty()) {
markdownHtmlPanel.displayMissingCredential(message.getId());
} else {
SwingUtilities.invokeLater(() -> call(message, isRetry));

View file

@ -124,7 +124,7 @@ public class ChatToolWindowTabPanel implements ToolWindowTabPanel {
}
var settings = SettingsState.getInstance();
if (settings.apiKey.isEmpty()) {
if (settings.getApiKey().isEmpty()) {
notifyMissingCredential(project, "API key not provided.");
} else {
SyntaxTextArea textArea;