mirror of
https://github.com/carlrobertoh/ProxyAI.git
synced 2026-05-20 09:24:08 +00:00
feat: introduce openai and anthropic models for subscribed users
This commit is contained in:
parent
4e12034d0d
commit
d92e98ba98
20 changed files with 317 additions and 100 deletions
|
|
@ -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"
|
||||
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 && (
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
)
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
6
src/main/resources/icons/dbrx.svg
Normal file
6
src/main/resources/icons/dbrx.svg
Normal 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 |
1
src/main/resources/icons/meta.svg
Normal file
1
src/main/resources/icons/meta.svg
Normal 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 |
Loading…
Add table
Add a link
Reference in a new issue