feat: introduce openai and anthropic models for subscribed users

This commit is contained in:
Carl-Robert Linnupuu 2024-06-06 00:03:25 +03:00
parent 4e12034d0d
commit d92e98ba98
20 changed files with 317 additions and 100 deletions

View file

@ -12,7 +12,7 @@ jsoup = "1.17.2"
jtokkit = "1.0.0"
junit = "5.10.2"
kotlin = "2.0.0"
llm-client = "0.8.5"
llm-client = "0.8.6"
okio = "3.9.0"
tree-sitter = "0.22.5"

View file

@ -3,6 +3,7 @@ package ee.carlrobert.codegpt;
import com.intellij.openapi.editor.EditorCustomElementRenderer;
import com.intellij.openapi.editor.Inlay;
import com.intellij.openapi.util.Key;
import ee.carlrobert.llm.client.codegpt.CodeGPTUserDetails;
import java.util.List;
public class CodeGPTKeys {
@ -17,4 +18,6 @@ public class CodeGPTKeys {
Key.create("codegpt.selectedFiles");
public static final Key<String> IMAGE_ATTACHMENT_FILE_PATH =
Key.create("codegpt.imageAttachmentFilePath");
public static final Key<CodeGPTUserDetails> CODEGPT_USER_DETAILS =
Key.create("codegpt.userDetails");
}

View file

@ -12,9 +12,11 @@ public final class Icons {
IconLoader.getIcon("/icons/codegpt-model.svg", Icons.class);
public static final Icon Anthropic = IconLoader.getIcon("/icons/anthropic.svg", Icons.class);
public static final Icon Azure = IconLoader.getIcon("/icons/azure.svg", Icons.class);
public static final Icon Databricks = IconLoader.getIcon("/icons/dbrx.svg", Icons.class);
public static final Icon Google = IconLoader.getIcon("/icons/google.svg", Icons.class);
public static final Icon Llama = IconLoader.getIcon("/icons/llama.svg", Icons.class);
public static final Icon OpenAI = IconLoader.getIcon("/icons/openai.svg", Icons.class);
public static final Icon Meta = IconLoader.getIcon("/icons/meta.svg", Icons.class);
public static final Icon Send = IconLoader.getIcon("/icons/send.svg", Icons.class);
public static final Icon Sparkle = IconLoader.getIcon("/icons/sparkle.svg", Icons.class);
public static final Icon You = IconLoader.getIcon("/icons/you.svg", Icons.class);

View file

@ -4,17 +4,21 @@ 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.CodeGPTKeys;
import ee.carlrobert.codegpt.completions.HuggingFaceModel;
import ee.carlrobert.codegpt.completions.llama.LlamaModel;
import ee.carlrobert.codegpt.conversations.Conversation;
import ee.carlrobert.codegpt.settings.service.ProviderChangeNotifier;
import ee.carlrobert.codegpt.settings.service.ServiceType;
import ee.carlrobert.codegpt.settings.service.anthropic.AnthropicSettings;
import ee.carlrobert.codegpt.settings.service.azure.AzureSettings;
import ee.carlrobert.codegpt.settings.service.codegpt.CodeGPTService;
import ee.carlrobert.codegpt.settings.service.codegpt.CodeGPTServiceSettings;
import ee.carlrobert.codegpt.settings.service.google.GoogleSettings;
import ee.carlrobert.codegpt.settings.service.llama.LlamaSettings;
import ee.carlrobert.codegpt.settings.service.ollama.OllamaSettings;
import ee.carlrobert.codegpt.settings.service.openai.OpenAISettings;
import ee.carlrobert.codegpt.util.ApplicationUtil;
import org.jetbrains.annotations.NotNull;
@State(name = "CodeGPT_GeneralSettings_270", storages = @Storage("CodeGPT_GeneralSettings_270.xml"))
@ -50,38 +54,52 @@ public class GeneralSettings implements PersistentStateComponent<GeneralSettings
}
public void sync(Conversation conversation) {
var clientCode = conversation.getClientCode();
if ("chat.completion".equals(clientCode)) {
state.setSelectedService(ServiceType.OPENAI);
OpenAISettings.getCurrentState().setModel(conversation.getModel());
var project = ApplicationUtil.findCurrentProject();
var provider = ServiceType.fromClientCode(conversation.getClientCode());
switch (provider) {
case OPENAI:
OpenAISettings.getCurrentState().setModel(conversation.getModel());
break;
case LLAMA_CPP:
var llamaSettings = LlamaSettings.getCurrentState();
try {
var huggingFaceModel = HuggingFaceModel.valueOf(conversation.getModel());
llamaSettings.setHuggingFaceModel(huggingFaceModel);
llamaSettings.setUseCustomModel(false);
} catch (IllegalArgumentException ignore) {
llamaSettings.setCustomLlamaModelPath(conversation.getModel());
llamaSettings.setUseCustomModel(true);
}
break;
case CODEGPT:
ApplicationManager.getApplication().getService(CodeGPTServiceSettings.class).getState()
.getChatCompletionSettings().setModel(conversation.getModel());
var existingUserDetails = CodeGPTKeys.CODEGPT_USER_DETAILS.get(project);
if (project != null && existingUserDetails == null) {
project.getService(CodeGPTService.class).syncUserDetailsAsync();
}
break;
case ANTHROPIC:
ApplicationManager.getApplication().getService(AnthropicSettings.class).getState()
.setModel(conversation.getModel());
break;
case GOOGLE:
ApplicationManager.getApplication().getService(GoogleSettings.class).getState()
.setModel(conversation.getModel());
break;
case OLLAMA:
ApplicationManager.getApplication().getService(OllamaSettings.class).getState()
.setModel(conversation.getModel());
break;
default:
break;
}
if ("anthropic.chat.completion".equals(clientCode)) {
state.setSelectedService(ServiceType.ANTHROPIC);
AnthropicSettings.getCurrentState().setModel(conversation.getModel());
}
if ("azure.chat.completion".equals(clientCode)) {
state.setSelectedService(ServiceType.AZURE);
}
if ("custom.openai.chat.completion".equals(clientCode)) {
state.setSelectedService(ServiceType.CUSTOM_OPENAI);
}
if ("llama.chat.completion".equals(clientCode)) {
state.setSelectedService(ServiceType.LLAMA_CPP);
var llamaSettings = LlamaSettings.getCurrentState();
try {
var huggingFaceModel = HuggingFaceModel.valueOf(conversation.getModel());
llamaSettings.setHuggingFaceModel(huggingFaceModel);
llamaSettings.setUseCustomModel(false);
} catch (IllegalArgumentException ignore) {
llamaSettings.setCustomLlamaModelPath(conversation.getModel());
llamaSettings.setUseCustomModel(true);
}
}
if ("you.chat.completion".equals(clientCode)) {
state.setSelectedService(ServiceType.YOU);
}
if ("ollama.chat.completion".equals(clientCode)) {
state.setSelectedService(ServiceType.OLLAMA);
state.setSelectedService(provider);
if (project != null) {
project.getMessageBus()
.syncPublisher(ProviderChangeNotifier.getPROVIDER_CHANGE_TOPIC())
.providerChanged(provider);
}
}

View file

@ -1,6 +1,8 @@
package ee.carlrobert.codegpt.settings.service;
import ee.carlrobert.codegpt.CodeGPTBundle;
import java.util.HashMap;
import java.util.Map;
public enum ServiceType {
CODEGPT("CODEGPT", "service.codegpt.title", "codegpt.chat.completion"),
@ -17,6 +19,14 @@ public enum ServiceType {
private final String label;
private final String completionCode;
private static final Map<String, ServiceType> CLIENT_CODE_MAP = new HashMap<>();
static {
for (ServiceType type : values()) {
CLIENT_CODE_MAP.put(type.getCompletionCode(), type);
}
}
ServiceType(String code, String messageKey, String completionCode) {
this.code = code;
this.label = CodeGPTBundle.get(messageKey);
@ -39,4 +49,12 @@ public enum ServiceType {
public String toString() {
return label;
}
public static ServiceType fromClientCode(String clientCode) {
ServiceType serviceType = CLIENT_CODE_MAP.get(clientCode);
if (serviceType == null) {
throw new RuntimeException("Provided client code '" + clientCode + "' is not supported");
}
return serviceType;
}
}

View file

@ -3,13 +3,16 @@ package ee.carlrobert.codegpt.toolwindow.chat;
import static java.lang.String.format;
import static java.util.Collections.emptyList;
import com.intellij.ide.BrowserUtil;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.actionSystem.ActionManager;
import com.intellij.openapi.actionSystem.ActionToolbar;
import com.intellij.openapi.actionSystem.DefaultCompactActionGroup;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.SimpleToolWindowPanel;
import com.intellij.openapi.util.Disposer;
import com.intellij.ui.components.ActionLink;
import com.intellij.util.ui.JBUI;
import ee.carlrobert.codegpt.CodeGPTKeys;
import ee.carlrobert.codegpt.ReferencedFile;
@ -19,8 +22,13 @@ import ee.carlrobert.codegpt.actions.toolwindow.CreateNewConversationAction;
import ee.carlrobert.codegpt.actions.toolwindow.OpenInEditorAction;
import ee.carlrobert.codegpt.conversations.ConversationService;
import ee.carlrobert.codegpt.conversations.ConversationsState;
import ee.carlrobert.codegpt.settings.GeneralSettings;
import ee.carlrobert.codegpt.settings.service.ProviderChangeNotifier;
import ee.carlrobert.codegpt.settings.service.ServiceType;
import ee.carlrobert.codegpt.settings.service.codegpt.CodeGPTUserDetailsNotifier;
import ee.carlrobert.codegpt.toolwindow.chat.ui.ToolWindowFooterNotification;
import ee.carlrobert.codegpt.toolwindow.chat.ui.textarea.AttachImageNotifier;
import ee.carlrobert.llm.client.codegpt.PricingPlan;
import java.awt.BorderLayout;
import java.nio.file.Path;
import java.nio.file.Paths;
@ -34,6 +42,7 @@ public class ChatToolWindowPanel extends SimpleToolWindowPanel {
private final ToolWindowFooterNotification selectedFilesNotification;
private final ToolWindowFooterNotification imageFileAttachmentNotification;
private final ActionLink upgradePlanLink;
private ChatToolWindowTabbedPane tabbedPane;
public ChatToolWindowPanel(
@ -44,18 +53,42 @@ public class ChatToolWindowPanel extends SimpleToolWindowPanel {
() -> clearSelectedFilesNotification(project));
imageFileAttachmentNotification = new ToolWindowFooterNotification(() ->
project.putUserData(CodeGPTKeys.IMAGE_ATTACHMENT_FILE_PATH, ""));
upgradePlanLink = new ActionLink("Upgrade your plan", event -> {
BrowserUtil.browse("https://codegpt.carlrobert.ee/#pricing");
});
upgradePlanLink.setFont(JBUI.Fonts.smallFont());
upgradePlanLink.setExternalLinkIcon();
upgradePlanLink.setVisible(false);
init(project, parentDisposable);
project.getMessageBus()
.connect()
.subscribe(IncludeFilesInContextNotifier.FILES_INCLUDED_IN_CONTEXT_TOPIC,
(IncludeFilesInContextNotifier) this::displaySelectedFilesNotification);
project.getMessageBus()
.connect()
.subscribe(AttachImageNotifier.IMAGE_ATTACHMENT_FILE_PATH_TOPIC,
(AttachImageNotifier) filePath -> imageFileAttachmentNotification.show(
Path.of(filePath).getFileName().toString(),
"File path: " + filePath));
var messageBusConnection = project.getMessageBus().connect();
messageBusConnection.subscribe(IncludeFilesInContextNotifier.FILES_INCLUDED_IN_CONTEXT_TOPIC,
(IncludeFilesInContextNotifier) this::displaySelectedFilesNotification);
messageBusConnection.subscribe(AttachImageNotifier.IMAGE_ATTACHMENT_FILE_PATH_TOPIC,
(AttachImageNotifier) filePath -> imageFileAttachmentNotification.show(
Path.of(filePath).getFileName().toString(),
"File path: " + filePath));
messageBusConnection.subscribe(ProviderChangeNotifier.getPROVIDER_CHANGE_TOPIC(),
(ProviderChangeNotifier) provider -> {
if (provider == ServiceType.CODEGPT) {
var userDetails = CodeGPTKeys.CODEGPT_USER_DETAILS.get(project);
upgradePlanLink.setVisible(
userDetails != null && userDetails.getPricingPlan() != PricingPlan.INDIVIDUAL);
} else {
upgradePlanLink.setVisible(false);
}
});
messageBusConnection.subscribe(CodeGPTUserDetailsNotifier.getCODEGPT_USER_DETAILS_TOPIC(),
(CodeGPTUserDetailsNotifier) userDetails -> {
if (userDetails != null) {
var provider = ApplicationManager.getApplication().getService(GeneralSettings.class)
.getState()
.getSelectedService();
upgradePlanLink.setVisible(provider == ServiceType.CODEGPT
&& userDetails.getPricingPlan() != PricingPlan.INDIVIDUAL);
}
});
}
public ChatToolWindowTabbedPane getChatTabbedPane() {
@ -109,6 +142,7 @@ public class ChatToolWindowPanel extends SimpleToolWindowPanel {
actionToolbarPanel.add(
createActionToolbar(project, tabbedPane, onAddNewTab).getComponent(),
BorderLayout.LINE_START);
actionToolbarPanel.add(upgradePlanLink, BorderLayout.LINE_END);
setToolbar(actionToolbarPanel);
var notificationContainer = new JPanel(new BorderLayout());

View file

@ -275,6 +275,7 @@ public class ChatToolWindowTabPanel implements Disposable {
JBUI.Borders.empty(8)));
var contentManager = project.getService(ChatToolWindowContentManager.class);
panel.add(JBUI.Panels.simplePanel(createUserPromptTextAreaHeader(
project,
selectedService,
() -> {
ConversationService.getInstance().startConversation();
@ -285,13 +286,14 @@ public class ChatToolWindowTabPanel implements Disposable {
}
private JPanel createUserPromptTextAreaHeader(
Project project,
ServiceType selectedService,
Runnable onModelChange) {
return JBUI.Panels.simplePanel()
.withBorder(Borders.emptyBottom(8))
.andTransparent()
.addToLeft(totalTokensPanel)
.addToRight(new ModelComboBoxAction(onModelChange, selectedService)
.addToRight(new ModelComboBoxAction(project, onModelChange, selectedService)
.createCustomComponent(ActionPlaces.UNKNOWN));
}

View file

@ -40,7 +40,7 @@ public class ChatToolWindowScrollablePanel extends ScrollablePanel {
.addContent(UIUtil.createTextPane("""
<html>
<p style="margin-top: 4px; margin-bottom: 4px;">
It looks like you haven't configured your API key yet. Visit the <a href="#OPEN_SETTINGS">CodeGPT settings</a> to do so.
It looks like you haven't configured your API key yet. Visit <a href="#OPEN_SETTINGS">CodeGPT settings</a> to do so.
</p>
<p style="margin-top: 4px; margin-bottom: 4px;">
Don't have an account? <a href="https://codegpt.carlrobert.ee/signin">Sign up</a> for free access to all open-source models.

View file

@ -15,13 +15,13 @@ import com.intellij.openapi.actionSystem.Presentation;
import com.intellij.openapi.actionSystem.ex.ComboBoxAction;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.project.DumbAwareAction;
import com.intellij.openapi.project.Project;
import com.intellij.util.messages.MessageBusConnection;
import ee.carlrobert.codegpt.CodeGPTKeys;
import ee.carlrobert.codegpt.Icons;
import ee.carlrobert.codegpt.completions.llama.LlamaModel;
import ee.carlrobert.codegpt.completions.you.YouUserManager;
import ee.carlrobert.codegpt.completions.you.auth.SignedOutNotifier;
import ee.carlrobert.codegpt.credentials.CredentialsStore;
import ee.carlrobert.codegpt.credentials.CredentialsStore.CredentialKey;
import ee.carlrobert.codegpt.settings.GeneralSettings;
import ee.carlrobert.codegpt.settings.service.ServiceType;
import ee.carlrobert.codegpt.settings.service.codegpt.CodeGPTAvailableModels;
@ -43,8 +43,10 @@ import org.jetbrains.annotations.NotNull;
public class ModelComboBoxAction extends ComboBoxAction {
private final Runnable onModelChange;
private final Project project;
public ModelComboBoxAction(Runnable onModelChange, ServiceType selectedService) {
public ModelComboBoxAction(Project project, Runnable onModelChange, ServiceType selectedService) {
this.project = project;
this.onModelChange = onModelChange;
updateTemplatePresentation(selectedService);
@ -65,14 +67,11 @@ public class ModelComboBoxAction extends ComboBoxAction {
return button;
}
private AnAction[] getCodeGPTModelActions(Presentation presentation) {
var apiKey = CredentialsStore.getCredential(CredentialKey.CODEGPT_API_KEY);
return CodeGPTAvailableModels.getCHAT_MODELS().stream()
.map(model -> {
var enabled = "meta-llama/Llama-3-8b-chat-hf".equals(model.getCode())
|| (apiKey != null && !apiKey.isEmpty());
return createCodeGPTModelAction(model, enabled, presentation);
})
private AnAction[] getCodeGPTModelActions(Project project, Presentation presentation) {
var userDetails = CodeGPTKeys.CODEGPT_USER_DETAILS.get(project);
return CodeGPTAvailableModels.getToolWindowModels(
userDetails == null ? null : userDetails.getPricingPlan()).stream()
.map(model -> createCodeGPTModelAction(model, presentation))
.toArray(AnAction[]::new);
}
@ -81,7 +80,7 @@ public class ModelComboBoxAction extends ComboBoxAction {
var presentation = ((ComboBoxButton) button).getPresentation();
var actionGroup = new DefaultActionGroup();
actionGroup.addSeparator("CodeGPT");
actionGroup.addAll(getCodeGPTModelActions(presentation));
actionGroup.addAll(getCodeGPTModelActions(project, presentation));
actionGroup.addSeparator("OpenAI");
List.of(
OpenAIChatCompletionModel.GPT_4_O,
@ -171,16 +170,15 @@ public class ModelComboBoxAction extends ComboBoxAction {
var templatePresentation = getTemplatePresentation();
switch (selectedService) {
case CODEGPT:
var model = application.getService(CodeGPTServiceSettings.class)
var modelCode = application.getService(CodeGPTServiceSettings.class)
.getState()
.getChatCompletionSettings()
.getModel();
var modelName = CodeGPTAvailableModels.getCHAT_MODELS().stream()
.filter(it -> it.getCode().equals(model))
.map(CodeGPTModel::getName)
.findFirst().orElse("Unknown");
templatePresentation.setIcon(Icons.CodeGPTModel);
templatePresentation.setText(modelName);
var model = CodeGPTAvailableModels.getALL_CHAT_MODELS().stream()
.filter(it -> it.getCode().equals(modelCode))
.findFirst();
templatePresentation.setIcon(model.map(CodeGPTModel::getIcon).orElse(Icons.CodeGPTModel));
templatePresentation.setText(model.map(CodeGPTModel::getName).orElse("Unknown"));
break;
case OPENAI:
templatePresentation.setIcon(Icons.OpenAI);
@ -284,14 +282,12 @@ public class ModelComboBoxAction extends ComboBoxAction {
onModelChange.run();
}
private AnAction createCodeGPTModelAction(CodeGPTModel model, boolean enabled,
Presentation comboBoxPresentation) {
return new DumbAwareAction(model.getName(), "", Icons.CodeGPTModel) {
private AnAction createCodeGPTModelAction(CodeGPTModel model, Presentation comboBoxPresentation) {
return new DumbAwareAction(model.getName(), "", model.getIcon()) {
@Override
public void update(@NotNull AnActionEvent event) {
var presentation = event.getPresentation();
presentation.setEnabled(
enabled && !presentation.getText().equals(comboBoxPresentation.getText()));
presentation.setEnabled(!presentation.getText().equals(comboBoxPresentation.getText()));
}
@Override
@ -303,7 +299,7 @@ public class ModelComboBoxAction extends ComboBoxAction {
handleModelChange(
CODEGPT,
model.getName(),
Icons.OpenAI,
model.getIcon(),
comboBoxPresentation);
}

View file

@ -1,6 +1,7 @@
package ee.carlrobert.codegpt.toolwindow.chat.ui.textarea;
import static ee.carlrobert.codegpt.settings.service.ServiceType.ANTHROPIC;
import static ee.carlrobert.codegpt.settings.service.ServiceType.CODEGPT;
import static ee.carlrobert.codegpt.settings.service.ServiceType.OLLAMA;
import static ee.carlrobert.codegpt.settings.service.ServiceType.OPENAI;
import static ee.carlrobert.llm.client.openai.completion.OpenAIChatCompletionModel.GPT_4_O;
@ -9,6 +10,7 @@ import static ee.carlrobert.llm.client.openai.completion.OpenAIChatCompletionMod
import com.intellij.icons.AllIcons;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.ex.util.EditorUtil;
import com.intellij.openapi.util.registry.Registry;
@ -21,6 +23,7 @@ import ee.carlrobert.codegpt.Icons;
import ee.carlrobert.codegpt.actions.AttachImageAction;
import ee.carlrobert.codegpt.completions.CompletionRequestHandler;
import ee.carlrobert.codegpt.settings.GeneralSettings;
import ee.carlrobert.codegpt.settings.service.codegpt.CodeGPTServiceSettings;
import ee.carlrobert.codegpt.settings.service.openai.OpenAISettings;
import ee.carlrobert.codegpt.ui.IconActionButton;
import ee.carlrobert.codegpt.ui.UIUtil;
@ -34,6 +37,7 @@ import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import javax.swing.AbstractAction;
@ -204,6 +208,13 @@ public class UserPromptTextArea extends JPanel {
if (selectedService == ANTHROPIC || selectedService == OLLAMA) {
return true;
}
if (selectedService == CODEGPT) {
var model = ApplicationManager.getApplication().getService(CodeGPTServiceSettings.class)
.getState()
.getChatCompletionSettings()
.getModel();
return List.of("gpt-4o", "claude-3-opus").contains(model);
}
var model = OpenAISettings.getCurrentState().getModel();
return selectedService == OPENAI && (

View file

@ -14,7 +14,10 @@ import ee.carlrobert.codegpt.completions.you.auth.YouAuthenticationService
import ee.carlrobert.codegpt.completions.you.auth.response.YouAuthenticationResponse
import ee.carlrobert.codegpt.credentials.CredentialsStore.CredentialKey
import ee.carlrobert.codegpt.credentials.CredentialsStore.getCredential
import ee.carlrobert.codegpt.settings.GeneralSettings
import ee.carlrobert.codegpt.settings.configuration.ConfigurationSettings
import ee.carlrobert.codegpt.settings.service.ServiceType
import ee.carlrobert.codegpt.settings.service.codegpt.CodeGPTService
import ee.carlrobert.codegpt.settings.service.you.YouSettings
import ee.carlrobert.codegpt.toolwindow.chat.ui.textarea.AttachImageNotifier
import ee.carlrobert.codegpt.ui.OverlayUtil
@ -29,6 +32,11 @@ class CodeGPTProjectActivity : StartupActivity.Background {
override fun runActivity(project: Project) {
EditorActionsUtil.refreshActions()
val settings = service<GeneralSettings>().state
if (settings.selectedService == ServiceType.CODEGPT) {
project.service<CodeGPTService>().syncUserDetailsAsync()
}
if (YouUserManager.getInstance().authenticationResponse == null) {
handleYouServiceAuthenticationAsync()
}
@ -39,7 +47,10 @@ class CodeGPTProjectActivity : StartupActivity.Background {
val desktopPath = Paths.get(System.getProperty("user.home"), "Desktop")
project.service<FileWatcher>().watch(desktopPath) {
if (watchExtensions.contains(it.extension.lowercase())) {
showImageAttachmentNotification(project, desktopPath.resolve(it).absolutePathString())
showImageAttachmentNotification(
project,
desktopPath.resolve(it).absolutePathString()
)
}
}
}

View file

@ -0,0 +1,14 @@
package ee.carlrobert.codegpt.settings.service
import com.intellij.util.messages.Topic
interface ProviderChangeNotifier {
fun providerChanged(provider: ServiceType)
companion object {
@JvmStatic
val PROVIDER_CHANGE_TOPIC =
Topic.create("providerChange", ProviderChangeNotifier::class.java)
}
}

View file

@ -1,31 +1,81 @@
package ee.carlrobert.codegpt.settings.service.codegpt
import ee.carlrobert.codegpt.Icons
import ee.carlrobert.llm.client.codegpt.PricingPlan
import ee.carlrobert.llm.client.codegpt.PricingPlan.*
import javax.swing.Icon
object CodeGPTAvailableModels {
@JvmStatic
val CHAT_MODELS: List<CodeGPTModel> = listOf(
CodeGPTModel("Llama 3 (70B)", "meta-llama/Llama-3-70b-chat-hf"),
CodeGPTModel("Llama 3 (8B)", "meta-llama/Llama-3-8b-chat-hf"),
CodeGPTModel("Code Llama (70B)", "codellama/CodeLlama-70b-Instruct-hf"),
CodeGPTModel("Mixtral (8x22B)", "mistralai/Mixtral-8x22B-Instruct-v0.1"),
CodeGPTModel("DBRX (132B)", "databricks/dbrx-instruct"),
CodeGPTModel("DeepSeek Coder (33B)", "deepseek-ai/deepseek-coder-33b-instruct"),
CodeGPTModel("WizardLM-2 (8x22B)", "microsoft/WizardLM-2-8x22B")
fun getToolWindowModels(pricingPlan: PricingPlan?): List<CodeGPTModel> {
val anonymousModels = BASE_CHAT_MODELS + CodeGPTModel(
"Llama 3 (8B) - FREE",
"llama-3-8b",
Icons.Meta,
ANONYMOUS
)
if (pricingPlan == null) {
return anonymousModels
}
return when (pricingPlan) {
ANONYMOUS -> anonymousModels
FREE -> BASE_CHAT_MODELS + listOf(
CodeGPTModel("Code Llama (70B)", "codellama:chat", Icons.Meta, FREE),
CodeGPTModel("Mixtral (8x22B)", "mixtral-8x22b", Icons.CodeGPTModel, FREE),
CodeGPTModel(
"DeepSeek Coder (33B)",
"deepseek-coder-33b",
Icons.CodeGPTModel,
FREE
),
CodeGPTModel("WizardLM-2 (8x22B)", "wizardlm-2-8x22b", Icons.CodeGPTModel, FREE)
)
else -> BASE_CHAT_MODELS
}
}
@JvmStatic
val BASE_CHAT_MODELS: List<CodeGPTModel> = listOf(
CodeGPTModel("GPT-4o", "gpt-4o", Icons.OpenAI, INDIVIDUAL),
CodeGPTModel("GPT-3.5 Turbo", "gpt-3.5-turbo", Icons.OpenAI, INDIVIDUAL),
CodeGPTModel("Claude 3 Opus", "claude-3-opus", Icons.Anthropic, INDIVIDUAL),
CodeGPTModel("Claude 3 Sonnet", "claude-3-sonnet", Icons.Anthropic, INDIVIDUAL),
CodeGPTModel("DBRX", "dbrx", Icons.Databricks, INDIVIDUAL),
CodeGPTModel("Llama 3 (70B)", "llama-3-70b", Icons.Meta, FREE),
)
@JvmStatic
val ALL_CHAT_MODELS: List<CodeGPTModel> = BASE_CHAT_MODELS + listOf(
CodeGPTModel("Llama 3 (8B) - FREE", "llama-3-8b", Icons.Meta, ANONYMOUS),
CodeGPTModel("Code Llama (70B)", "codellama:chat", Icons.Meta, FREE),
CodeGPTModel("Mixtral (8x22B)", "mixtral-8x22b", Icons.CodeGPTModel, FREE),
CodeGPTModel("DeepSeek Coder (33B)", "deepseek-coder-33b", Icons.CodeGPTModel, FREE),
CodeGPTModel("WizardLM-2 (8x22B)", "wizardlm-2-8x22b", Icons.CodeGPTModel, FREE)
)
@JvmStatic
val CODE_MODELS: List<CodeGPTModel> = listOf(
CodeGPTModel("StarCoder (16B)", "starcoder-16b"),
CodeGPTModel("Code Llama (70B)", "codellama/CodeLlama-70b-hf"),
CodeGPTModel("Code Llama Python (70B)", "codellama/CodeLlama-70b-Python-hf"),
CodeGPTModel("WizardCoder Python (34B)", "WizardLM/WizardCoder-Python-34B-V1.0"),
CodeGPTModel("Phind Code LLaMA v2 (34B)", "Phind/Phind-CodeLlama-34B-v2")
CodeGPTModel("GPT-3.5 Turbo Instruct", "gpt-3.5-turbo-instruct", Icons.OpenAI, INDIVIDUAL),
CodeGPTModel("StarCoder (16B)", "starcoder-16b", Icons.CodeGPTModel, FREE),
CodeGPTModel("StarCoder (7B) - FREE", "starcoder-7b", Icons.CodeGPTModel, FREE),
CodeGPTModel("Code Llama (70B)", "codellama:code", Icons.CodeGPTModel, FREE),
CodeGPTModel("Code Llama Python (70B)", "codellama-python", Icons.CodeGPTModel, FREE),
CodeGPTModel("WizardCoder Python (34B)", "wizardcoder-python", Icons.CodeGPTModel, FREE),
CodeGPTModel("Phind Code LLaMA v2 (34B)", "phind-codellama", Icons.CodeGPTModel, FREE)
)
@JvmStatic
fun findByCode(code: String?): CodeGPTModel? {
return CHAT_MODELS.union(CODE_MODELS).firstOrNull { it.code == code }
return ALL_CHAT_MODELS.union(CODE_MODELS).firstOrNull { it.code == code }
}
}
data class CodeGPTModel(val name: String, val code: String)
data class CodeGPTModel(
val name: String,
val code: String,
val icon: Icon,
val individual: PricingPlan
)

View file

@ -0,0 +1,40 @@
package ee.carlrobert.codegpt.settings.service.codegpt
import com.intellij.openapi.components.Service
import com.intellij.openapi.components.service
import com.intellij.openapi.project.Project
import ee.carlrobert.codegpt.CodeGPTKeys.CODEGPT_USER_DETAILS
import ee.carlrobert.codegpt.completions.CompletionClientProvider
import ee.carlrobert.codegpt.credentials.CredentialsStore.CredentialKey.CODEGPT_API_KEY
import ee.carlrobert.codegpt.credentials.CredentialsStore.getCredential
import ee.carlrobert.codegpt.settings.GeneralSettings
import ee.carlrobert.codegpt.settings.service.codegpt.CodeGPTUserDetailsNotifier.Companion.CODEGPT_USER_DETAILS_TOPIC
import kotlinx.coroutines.*
@Service
class CodeGPTService private constructor(val project: Project) {
private val serviceScope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
fun syncUserDetailsAsync() {
syncUserDetailsAsync(getCredential(CODEGPT_API_KEY))
}
fun syncUserDetailsAsync(apiKey: String?) {
serviceScope.launch {
val userDetails = withContext(Dispatchers.IO) {
if (apiKey.isNullOrEmpty()) null
else CompletionClientProvider.getCodeGPTClient().getUserDetails(apiKey)
}
if (userDetails != null && userDetails.pricingPlan != null) {
CODEGPT_USER_DETAILS.set(project, userDetails)
if (!userDetails.fullName.isNullOrEmpty()) {
service<GeneralSettings>().state.displayName = userDetails.fullName
}
}
project.messageBus
.syncPublisher<CodeGPTUserDetailsNotifier>(CODEGPT_USER_DETAILS_TOPIC)
.userDetailsObtained(userDetails)
}
}
}

View file

@ -7,6 +7,7 @@ import ee.carlrobert.codegpt.credentials.CredentialsStore.getCredential
import ee.carlrobert.codegpt.credentials.CredentialsStore.setCredential
import ee.carlrobert.codegpt.settings.GeneralSettings
import ee.carlrobert.codegpt.settings.service.ServiceType
import ee.carlrobert.codegpt.util.ApplicationUtil
import javax.swing.JComponent
class CodeGPTServiceConfigurable : Configurable {
@ -28,7 +29,12 @@ class CodeGPTServiceConfigurable : Configurable {
override fun apply() {
setCredential(CODEGPT_API_KEY, component.getApiKey())
service<GeneralSettings>().state.selectedService = ServiceType.CODEGPT
service<GeneralSettings>().state.apply {
selectedService = ServiceType.CODEGPT
}
ApplicationUtil.findCurrentProject()
?.service<CodeGPTService>()
?.syncUserDetailsAsync(component.getApiKey())
component.applyChanges()
}

View file

@ -2,7 +2,6 @@ package ee.carlrobert.codegpt.settings.service.codegpt
import com.intellij.openapi.components.service
import com.intellij.openapi.ui.ComboBox
import com.intellij.ui.DocumentAdapter
import com.intellij.ui.components.JBCheckBox
import com.intellij.ui.components.JBPasswordField
import com.intellij.util.ui.FormBuilder
@ -18,7 +17,6 @@ import java.awt.Component
import javax.swing.DefaultListCellRenderer
import javax.swing.JList
import javax.swing.JPanel
import javax.swing.event.DocumentEvent
class CodeGPTServiceForm {
@ -27,7 +25,7 @@ class CodeGPTServiceForm {
}
private val chatCompletionModelComboBox =
ComboBox(ListComboBoxModel(CodeGPTAvailableModels.CHAT_MODELS)).apply {
ComboBox(ListComboBoxModel(CodeGPTAvailableModels.ALL_CHAT_MODELS)).apply {
selectedItem =
CodeGPTAvailableModels.findByCode(service<CodeGPTServiceSettings>().state.chatCompletionSettings.model)
renderer = CustomComboBoxRenderer()
@ -49,13 +47,6 @@ class CodeGPTServiceForm {
apiKeyField.text = runBlocking(Dispatchers.IO) {
getCredential(CODEGPT_API_KEY)
}
val apiKeyDefined = apiKeyField.password.isNotEmpty()
codeCompletionModelComboBox.isEnabled = apiKeyDefined
apiKeyField.document.addDocumentListener(object : DocumentAdapter() {
override fun textChanged(e: DocumentEvent) {
codeCompletionModelComboBox.isEnabled = apiKeyDefined
}
})
}
fun getForm(): JPanel = FormBuilder.createFormBuilder()

View file

@ -4,8 +4,8 @@ import com.intellij.openapi.components.*
@Service
@State(
name = "CodeGPT_CodeGPTServiceSettings",
storages = [Storage("CodeGPT_CodeGPTServiceSettings.xml")]
name = "CodeGPT_CodeGPTServiceSettings_280",
storages = [Storage("CodeGPT_CodeGPTServiceSettings_280.xml")]
)
class CodeGPTServiceSettings :
SimplePersistentStateComponent<CodeGPTServiceSettingsState>(CodeGPTServiceSettingsState())
@ -16,10 +16,10 @@ class CodeGPTServiceSettingsState : BaseState() {
}
class CodeGPTServiceChatCompletionSettingsState : BaseState() {
var model by string("meta-llama/Llama-3-70b-chat-hf")
var model by string("llama-3-8b")
}
class CodeGPTServiceCodeCompletionSettingsState : BaseState() {
var codeCompletionsEnabled by property(false)
var model by string("codellama/CodeLlama-70b-hf")
var model by string("starcoder-7b")
}

View file

@ -0,0 +1,14 @@
package ee.carlrobert.codegpt.settings.service.codegpt
import com.intellij.util.messages.Topic
import ee.carlrobert.llm.client.codegpt.CodeGPTUserDetails
interface CodeGPTUserDetailsNotifier {
fun userDetailsObtained(userDetails: CodeGPTUserDetails?)
companion object {
@JvmStatic
val CODEGPT_USER_DETAILS_TOPIC =
Topic.create("codegptUserDetails", CodeGPTUserDetailsNotifier::class.java)
}
}

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13px" height="14px" viewBox="0 0 13 13" version="1.1">
<g id="surface1">
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,21.176471%,12.941176%);fill-opacity:1;" d="M 13 9.617188 L 13 7.332031 L 12.742188 7.179688 L 6.515625 10.421875 L 0.617188 7.332031 L 0.617188 6.007812 L 6.515625 9.0625 L 13.03125 5.695312 L 13.03125 3.441406 L 12.773438 3.289062 L 6.515625 6.5625 L 0.84375 3.597656 L 6.515625 0.632812 L 11.085938 3.011719 L 11.445312 2.824219 L 11.445312 2.578125 L 6.515625 0.015625 L 0 3.382812 L 0 3.722656 L 6.515625 7.117188 L 12.417969 4.03125 L 12.417969 5.386719 L 6.515625 8.476562 L 0.257812 5.203125 L 0 5.359375 L 0 7.640625 L 6.515625 11.007812 L 12.417969 7.949219 L 12.417969 9.277344 L 6.515625 12.367188 L 0.257812 9.125 L 0 9.277344 L 0 9.617188 L 6.515625 12.984375 Z M 13 9.617188 "/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 965 B

View file

@ -0,0 +1 @@
<svg viewBox="56.329999999999984 457.15000000000003 2387.3400000000006 1585.74" xmlns="http://www.w3.org/2000/svg" width="13" height="13"><linearGradient id="a" gradientTransform="matrix(1 0 0 -1 0 2500)" gradientUnits="userSpaceOnUse" x1="573.85" x2="2217.65" y1="1291.09" y2="1208.07"><stop offset="0" stop-color="#0064e1"/><stop offset=".4" stop-color="#0064e1"/><stop offset=".83" stop-color="#0073ee"/><stop offset="1" stop-color="#0082fb"/></linearGradient><linearGradient id="b" gradientTransform="matrix(1 0 0 -1 0 2500)" gradientUnits="userSpaceOnUse" x1="400.24" x2="400.24" y1="888.86" y2="1494.91"><stop offset="0" stop-color="#0082fb"/><stop offset="1" stop-color="#0064e0"/></linearGradient><path d="M314.19 1502.88c0 91.16 20 161.14 46.16 203.48 34.29 55.46 85.43 79 137.57 79 67.24 0 128.76-16.69 247.31-180.66 95-131.42 206.89-315.89 282.19-431.54l127.52-195.93c88.58-136.07 191.11-287.33 308.67-389.86 96-83.69 199.5-130.18 303.69-130.18 174.93 0 341.55 101.37 469.07 291.49 139.56 208.21 207.3 470.48 207.3 741.12 0 160.9-31.71 279.12-85.68 372.52-52.13 90.32-153.75 180.57-324.69 180.57v-257.57c146.37 0 182.89-134.5 182.89-288.42 0-219.34-51.14-462.75-163.8-636.68-79.94-123.37-183.55-198.75-297.54-198.75-123.29 0-222.5 93-334 258.77-59.28 88.09-120.13 195.43-188.46 316.56l-75.22 133.25C1006.09 1638 967.81 1699 892.26 1799.68 759.85 1976 646.77 2042.85 497.92 2042.85c-176.59 0-288.25-76.47-357.41-191.7-56.45-93.89-84.18-217.1-84.18-357.48z" fill="#0081fb"/><path d="M259.65 766.82c118.22-182.23 288.83-309.67 484.51-309.67 113.32 0 226 33.54 343.62 129.6 128.68 105 265.83 278 436.94 563l61.35 102.25c148.11 246.74 232.37 373.68 281.69 433.54 63.42 76.87 107.84 99.79 165.54 99.79 146.37 0 182.89-134.5 182.89-288.42l227.48-7.14c0 160.9-31.71 279.12-85.68 372.52-52.13 90.32-153.75 180.57-324.69 180.57-106.27 0-200.41-23.08-304.52-121.3-80-75.38-173.6-209.29-245.58-329.67l-214.11-357.65c-107.42-179.49-206-313.32-263-373.93C944.73 795.13 865.86 716.43 740 716.43c-101.86 0-188.37 71.48-260.76 180.82z" fill="url(#a)"/><path d="M740 716.43c-101.86 0-188.37 71.48-260.76 180.82-102.37 154.5-165 384.63-165 605.63 0 91.16 20 161.14 46.16 203.48l-219.89 144.79c-56.45-93.89-84.18-217.1-84.18-357.48 0-255.29 70.07-521.37 203.32-726.85 118.22-182.23 288.83-309.67 484.51-309.67z" fill="url(#b)"/></svg>

After

Width:  |  Height:  |  Size: 2.3 KiB