Merge remote-tracking branch 'origin/master' into platform/2024.1

This commit is contained in:
Carl-Robert Linnupuu 2024-05-09 14:00:56 +03:00
commit 8883200817
82 changed files with 2555 additions and 645 deletions

View file

@ -9,15 +9,15 @@ dependencies {
implementation("io.github.bonede:tree-sitter-dockerfile:0.1.2")
implementation("io.github.bonede:tree-sitter-dart:master")
implementation("io.github.bonede:tree-sitter-css:0.20.0")
implementation("io.github.bonede:tree-sitter-cpp:0.20.3")
implementation("io.github.bonede:tree-sitter-cpp:0.22.0")
implementation("io.github.bonede:tree-sitter-c-sharp:0.20.0")
implementation("io.github.bonede:tree-sitter-fortran:master")
implementation("io.github.bonede:tree-sitter-gitattributes:0.1.3")
implementation("io.github.bonede:tree-sitter-go:0.20.0")
implementation("io.github.bonede:tree-sitter-go:0.21.0")
implementation("io.github.bonede:tree-sitter-graphql:master")
implementation("io.github.bonede:tree-sitter-html:0.19.0")
implementation("io.github.bonede:tree-sitter-html:0.20.2")
implementation("io.github.bonede:tree-sitter-javascript:0.20.1")
implementation("io.github.bonede:tree-sitter-json:0.20.1")
implementation("io.github.bonede:tree-sitter-json:0.21.0")
implementation("io.github.bonede:tree-sitter-kotlin:0.3.1")
implementation("io.github.bonede:tree-sitter-latex:0.3.0")
implementation("io.github.bonede:tree-sitter-lua:2.1.3")
@ -25,16 +25,16 @@ dependencies {
implementation("io.github.bonede:tree-sitter-markdown:0.7.1")
implementation("io.github.bonede:tree-sitter-objc:main")
implementation("io.github.bonede:tree-sitter-perl:0.4.0")
implementation("io.github.bonede:tree-sitter-ruby:0.19.0")
implementation("io.github.bonede:tree-sitter-rust:0.20.4")
implementation("io.github.bonede:tree-sitter-scala:0.20.2")
implementation("io.github.bonede:tree-sitter-ruby:0.20.1")
implementation("io.github.bonede:tree-sitter-rust:0.21.0")
implementation("io.github.bonede:tree-sitter-scala:0.21.0")
implementation("io.github.bonede:tree-sitter-scss:1.0.0")
implementation("io.github.bonede:tree-sitter-svelte:0.11.0")
implementation("io.github.bonede:tree-sitter-swift:0.3.6")
implementation("io.github.bonede:tree-sitter-yaml:0.5.0")
implementation("io.github.bonede:tree-sitter-java:0.20.2")
implementation("io.github.bonede:tree-sitter-python:0.20.4")
implementation("io.github.bonede:tree-sitter-php:0.20.0")
implementation("io.github.bonede:tree-sitter-typescript:0.20.3")
implementation("io.github.bonede:tree-sitter-java:0.21.0")
implementation("io.github.bonede:tree-sitter-python:0.21.0")
implementation("io.github.bonede:tree-sitter-php:0.22.2")
implementation("io.github.bonede:tree-sitter-typescript:0.20.6")
implementation("io.github.bonede:tree-sitter-query:0.1.0")
}

View file

@ -3,18 +3,18 @@ analytics = "3.0.0"
assertj = "3.25.3"
changelog = "2.2.0"
checkstyle = "10.15.0"
commons-text = "1.11.0"
commons-text = "1.12.0"
flexmark = "0.64.8"
gradle-intellij-plugin-version = "1.17.3"
gson = "2.10.1"
jackson = "2.17.0"
jackson = "2.17.1"
jsoup = "1.17.2"
jtokkit = "1.0.0"
junit = "5.10.2"
kotlin = "1.9.23"
llm-client = "0.7.2"
kotlin = "1.9.24"
llm-client = "0.8.1"
okio = "3.9.0"
tree-sitter = "0.22.2"
tree-sitter = "0.22.5"
[libraries]
analytics = { module = "com.rudderstack.sdk.java.analytics:analytics", version.ref = "analytics" }

@ -1 +1 @@
Subproject commit 7dbdba5690ca61b3ee8c92cfac8e7e251042e787
Subproject commit 46e12c4692a37bdd31a0432fc5153d7d22bc7f72

View file

@ -8,14 +8,18 @@ public final class Icons {
public static final Icon Default = IconLoader.getIcon("/icons/codegpt.svg", Icons.class);
public static final Icon DefaultSmall =
IconLoader.getIcon("/icons/codegpt-small.svg", Icons.class);
public static final Icon CodeGPTModel =
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 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 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);
public static final Icon YouSmall = IconLoader.getIcon("/icons/you_small.png", Icons.class);
public static final Icon Ollama = IconLoader.getIcon("/icons/ollama.svg", Icons.class);
public static final Icon User = IconLoader.getIcon("/icons/user.svg", Icons.class);
public static final Icon Upload = IconLoader.getIcon("/icons/upload.svg", Icons.class);
}

View file

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

View file

@ -1,18 +1,24 @@
package ee.carlrobert.codegpt.completions;
import static ee.carlrobert.codegpt.credentials.CredentialsStore.getCredential;
import com.intellij.openapi.application.ApplicationManager;
import ee.carlrobert.codegpt.CodeGPTPlugin;
import ee.carlrobert.codegpt.completions.you.YouUserManager;
import ee.carlrobert.codegpt.credentials.CredentialsStore;
import ee.carlrobert.codegpt.credentials.CredentialsStore.CredentialKey;
import ee.carlrobert.codegpt.settings.advanced.AdvancedSettings;
import ee.carlrobert.codegpt.settings.service.anthropic.AnthropicSettings;
import ee.carlrobert.codegpt.settings.service.azure.AzureSettings;
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.llm.client.anthropic.ClaudeClient;
import ee.carlrobert.llm.client.azure.AzureClient;
import ee.carlrobert.llm.client.azure.AzureCompletionRequestParams;
import ee.carlrobert.llm.client.codegpt.CodeGPTClient;
import ee.carlrobert.llm.client.google.GoogleClient;
import ee.carlrobert.llm.client.llama.LlamaClient;
import ee.carlrobert.llm.client.ollama.OllamaClient;
import ee.carlrobert.llm.client.openai.OpenAIClient;
import ee.carlrobert.llm.client.you.UTMParameters;
import ee.carlrobert.llm.client.you.YouClient;
@ -21,12 +27,13 @@ import java.net.Proxy;
import java.util.concurrent.TimeUnit;
import okhttp3.Credentials;
import okhttp3.OkHttpClient;
import org.jetbrains.annotations.Nullable;
public class CompletionClientProvider {
private static @Nullable String getCredential(CredentialKey key) {
return CredentialsStore.INSTANCE.getCredential(key);
public static CodeGPTClient getCodeGPTClient() {
return new CodeGPTClient(
getCredential(CredentialKey.CODEGPT_API_KEY),
getDefaultClientBuilder());
}
public static OpenAIClient getOpenAIClient() {
@ -92,6 +99,22 @@ public class CompletionClientProvider {
return builder.build(getDefaultClientBuilder());
}
public static OllamaClient getOllamaClient() {
var host = ApplicationManager.getApplication()
.getService(OllamaSettings.class)
.getState()
.getHost();
return new OllamaClient.Builder()
.setHost(host)
.build(getDefaultClientBuilder());
}
public static GoogleClient getGoogleClient() {
return new GoogleClient.Builder(getCredential(CredentialKey.GOOGLE_API_KEY))
.build(getDefaultClientBuilder());
}
public static OkHttpClient.Builder getDefaultClientBuilder() {
OkHttpClient.Builder builder = new OkHttpClient.Builder();
var advancedSettings = AdvancedSettings.getCurrentState();

View file

@ -26,8 +26,8 @@ import ee.carlrobert.codegpt.settings.service.ServiceType;
import ee.carlrobert.codegpt.settings.service.anthropic.AnthropicSettings;
import ee.carlrobert.codegpt.settings.service.custom.CustomServiceChatCompletionSettingsState;
import ee.carlrobert.codegpt.settings.service.custom.CustomServiceSettings;
import ee.carlrobert.codegpt.settings.service.custom.CustomServiceState;
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.settings.service.you.YouSettings;
import ee.carlrobert.codegpt.telemetry.core.configuration.TelemetryConfiguration;
@ -40,7 +40,15 @@ import ee.carlrobert.llm.client.anthropic.completion.ClaudeCompletionRequest;
import ee.carlrobert.llm.client.anthropic.completion.ClaudeCompletionStandardMessage;
import ee.carlrobert.llm.client.anthropic.completion.ClaudeMessageImageContent;
import ee.carlrobert.llm.client.anthropic.completion.ClaudeMessageTextContent;
import ee.carlrobert.llm.client.google.completion.GoogleCompletionContent;
import ee.carlrobert.llm.client.google.completion.GoogleCompletionRequest;
import ee.carlrobert.llm.client.google.completion.GoogleContentPart;
import ee.carlrobert.llm.client.google.completion.GoogleContentPart.Blob;
import ee.carlrobert.llm.client.google.completion.GoogleGenerationConfig;
import ee.carlrobert.llm.client.google.models.GoogleModel;
import ee.carlrobert.llm.client.llama.completion.LlamaCompletionRequest;
import ee.carlrobert.llm.client.ollama.completion.request.OllamaChatCompletionMessage;
import ee.carlrobert.llm.client.ollama.completion.request.OllamaChatCompletionRequest;
import ee.carlrobert.llm.client.openai.completion.OpenAIChatCompletionModel;
import ee.carlrobert.llm.client.openai.completion.request.OpenAIChatCompletionDetailedMessage;
import ee.carlrobert.llm.client.openai.completion.request.OpenAIChatCompletionMessage;
@ -56,6 +64,7 @@ import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
@ -116,7 +125,8 @@ public class CompletionRequestProvider {
public static Request buildCustomOpenAICompletionRequest(String system, String context) {
return buildCustomOpenAIChatCompletionRequest(
ApplicationManager.getApplication().getService(CustomServiceState.class)
ApplicationManager.getApplication().getService(CustomServiceSettings.class)
.getState()
.getChatCompletionSettings(),
List.of(
new OpenAIChatCompletionStandardMessage("system", system),
@ -124,18 +134,23 @@ public class CompletionRequestProvider {
true);
}
public static Request buildCustomOpenAICompletionRequest(String input) {
public static Request buildCustomOpenAICompletionRequest(String context, String url,
Map<String, String> headers, Map<String, Object> body, String credential) {
var usedSettings = new CustomServiceChatCompletionSettingsState();
usedSettings.setBody(body);
usedSettings.setHeaders(headers);
usedSettings.setUrl(url);
return buildCustomOpenAIChatCompletionRequest(
ApplicationManager.getApplication().getService(CustomServiceSettings.class)
.getState()
.getChatCompletionSettings(),
List.of(new OpenAIChatCompletionStandardMessage("user", input)),
true);
usedSettings,
List.of(new OpenAIChatCompletionStandardMessage("user", context)),
true,
credential);
}
public static Request buildCustomOpenAILookupCompletionRequest(String context) {
return buildCustomOpenAIChatCompletionRequest(
ApplicationManager.getApplication().getService(CustomServiceState.class)
ApplicationManager.getApplication().getService(CustomServiceSettings.class)
.getState()
.getChatCompletionSettings(),
List.of(
new OpenAIChatCompletionStandardMessage(
@ -205,19 +220,29 @@ public class CompletionRequestProvider {
@Nullable String model,
CallParameters callParameters) {
var configuration = ConfigurationSettings.getCurrentState();
return new OpenAIChatCompletionRequest.Builder(buildMessages(model, callParameters))
return new OpenAIChatCompletionRequest.Builder(buildOpenAIMessages(model, callParameters))
.setModel(model)
.setMaxTokens(configuration.getMaxTokens())
.setStream(true)
.setTemperature(configuration.getTemperature()).build();
}
public GoogleCompletionRequest buildGoogleChatCompletionRequest(
@Nullable String model,
CallParameters callParameters) {
var configuration = ConfigurationSettings.getCurrentState();
return new GoogleCompletionRequest.Builder(buildGoogleMessages(model, callParameters))
.generationConfig(new GoogleGenerationConfig.Builder()
.maxOutputTokens(configuration.getMaxTokens())
.temperature(configuration.getTemperature()).build()).build();
}
public Request buildCustomOpenAIChatCompletionRequest(
CustomServiceChatCompletionSettingsState settings,
CallParameters callParameters) {
return buildCustomOpenAIChatCompletionRequest(
settings,
buildMessages(callParameters),
buildOpenAIMessages(callParameters),
true);
}
@ -225,8 +250,16 @@ public class CompletionRequestProvider {
CustomServiceChatCompletionSettingsState settings,
List<OpenAIChatCompletionMessage> messages,
boolean streamRequest) {
return buildCustomOpenAIChatCompletionRequest(settings, messages, streamRequest,
CredentialsStore.getCredential(CUSTOM_SERVICE_API_KEY));
}
private static Request buildCustomOpenAIChatCompletionRequest(
CustomServiceChatCompletionSettingsState settings,
List<OpenAIChatCompletionMessage> messages,
boolean streamRequest,
String credential) {
var requestBuilder = new Request.Builder().url(requireNonNull(settings.getUrl()).trim());
var credential = CredentialsStore.INSTANCE.getCredential(CUSTOM_SERVICE_API_KEY);
for (var entry : settings.getHeaders().entrySet()) {
String value = entry.getValue();
if (credential != null && value.contains("$CUSTOM_SERVICE_API_KEY")) {
@ -294,7 +327,68 @@ public class CompletionRequestProvider {
return request;
}
private List<OpenAIChatCompletionMessage> buildMessages(CallParameters callParameters) {
public OllamaChatCompletionRequest buildOllamaChatCompletionRequest(
CallParameters callParameters
) {
var settings = ApplicationManager.getApplication().getService(OllamaSettings.class).getState();
return new OllamaChatCompletionRequest
.Builder(settings.getModel(), buildOllamaMessages(callParameters))
.build();
}
private List<OllamaChatCompletionMessage> buildOllamaMessages(CallParameters callParameters) {
var message = callParameters.getMessage();
var messages = new ArrayList<OllamaChatCompletionMessage>();
if (callParameters.getConversationType() == ConversationType.DEFAULT) {
String systemPrompt = ConfigurationSettings.getCurrentState().getSystemPrompt();
messages.add(new OllamaChatCompletionMessage("system", systemPrompt, null));
}
if (callParameters.getConversationType() == ConversationType.FIX_COMPILE_ERRORS) {
messages.add(
new OllamaChatCompletionMessage("system", FIX_COMPILE_ERRORS_SYSTEM_PROMPT, null)
);
}
for (var prevMessage : conversation.getMessages()) {
if (callParameters.isRetry() && prevMessage.getId().equals(message.getId())) {
break;
}
var prevMessageImageFilePath = prevMessage.getImageFilePath();
if (prevMessageImageFilePath != null && !prevMessageImageFilePath.isEmpty()) {
try {
var imageFilePath = Path.of(prevMessageImageFilePath);
var imageBytes = Files.readAllBytes(imageFilePath);
var imageBase64 = Base64.getEncoder().encodeToString(imageBytes);
messages.add(
new OllamaChatCompletionMessage(
"user", prevMessage.getPrompt(), List.of(imageBase64)
)
);
} catch (IOException e) {
throw new RuntimeException(e);
}
} else {
messages.add(
new OllamaChatCompletionMessage("user", prevMessage.getPrompt(), null)
);
}
messages.add(
new OllamaChatCompletionMessage("assistant", prevMessage.getResponse(), null)
);
}
if (callParameters.getImageMediaType() != null && callParameters.getImageData().length > 0) {
var imageBase64 = Base64.getEncoder().encodeToString(callParameters.getImageData());
messages.add(
new OllamaChatCompletionMessage("user", message.getPrompt(), List.of(imageBase64))
);
} else {
messages.add(new OllamaChatCompletionMessage("user", message.getPrompt(), null));
}
return messages;
}
private List<OpenAIChatCompletionMessage> buildOpenAIMessages(CallParameters callParameters) {
var message = callParameters.getMessage();
var messages = new ArrayList<OpenAIChatCompletionMessage>();
if (callParameters.getConversationType() == ConversationType.DEFAULT) {
@ -326,7 +420,9 @@ public class CompletionRequestProvider {
} else {
messages.add(new OpenAIChatCompletionStandardMessage("user", prevMessage.getPrompt()));
}
messages.add(new OpenAIChatCompletionStandardMessage("assistant", prevMessage.getResponse()));
messages.add(
new OpenAIChatCompletionStandardMessage("assistant", prevMessage.getResponse())
);
}
if (callParameters.getImageMediaType() != null && callParameters.getImageData().length > 0) {
@ -342,10 +438,10 @@ public class CompletionRequestProvider {
return messages;
}
private List<OpenAIChatCompletionMessage> buildMessages(
private List<OpenAIChatCompletionMessage> buildOpenAIMessages(
@Nullable String model,
CallParameters callParameters) {
var messages = buildMessages(callParameters);
var messages = buildOpenAIMessages(callParameters);
if (model == null
|| GeneralSettings.getCurrentState().getSelectedService() == ServiceType.YOU) {
@ -368,6 +464,83 @@ public class CompletionRequestProvider {
return tryReducingMessagesOrThrow(messages, totalUsage, modelMaxTokens);
}
private List<GoogleCompletionContent> buildGoogleMessages(CallParameters callParameters) {
var message = callParameters.getMessage();
var messages = new ArrayList<GoogleCompletionContent>();
// Gemini API does not support direct 'system' prompts:
// see https://www.reddit.com/r/Bard/comments/1b90i8o/does_gemini_have_a_system_prompt_option_while/
if (callParameters.getConversationType() == ConversationType.DEFAULT) {
String systemPrompt = ConfigurationSettings.getCurrentState().getSystemPrompt();
messages.add(new GoogleCompletionContent("user", List.of(systemPrompt)));
messages.add(new GoogleCompletionContent("model", List.of("Understood.")));
}
if (callParameters.getConversationType() == ConversationType.FIX_COMPILE_ERRORS) {
messages.add(
new GoogleCompletionContent("user", List.of(FIX_COMPILE_ERRORS_SYSTEM_PROMPT)));
messages.add(new GoogleCompletionContent("model", List.of("Understood.")));
}
for (var prevMessage : conversation.getMessages()) {
if (callParameters.isRetry() && prevMessage.getId().equals(message.getId())) {
break;
}
var prevMessageImageFilePath = prevMessage.getImageFilePath();
if (prevMessageImageFilePath != null && !prevMessageImageFilePath.isEmpty()) {
try {
var imageFilePath = Path.of(prevMessageImageFilePath);
var imageData = Files.readAllBytes(imageFilePath);
var imageMediaType = FileUtil.getImageMediaType(imageFilePath.getFileName().toString());
messages.add(new GoogleCompletionContent(
List.of(
new GoogleContentPart(null, new Blob(imageMediaType, imageData)),
new GoogleContentPart(prevMessage.getPrompt())), "user"));
} catch (IOException e) {
throw new RuntimeException(e);
}
} else {
messages.add(new GoogleCompletionContent("user", List.of(prevMessage.getPrompt())));
}
messages.add(new GoogleCompletionContent("model", List.of(prevMessage.getResponse())));
}
if (callParameters.getImageMediaType() != null && callParameters.getImageData().length > 0) {
messages.add(new GoogleCompletionContent(
List.of(
new GoogleContentPart(null,
new Blob(callParameters.getImageMediaType(), callParameters.getImageData())),
new GoogleContentPart(message.getPrompt())), "user"));
} else {
messages.add(new GoogleCompletionContent("user", List.of(message.getPrompt())));
}
return messages;
}
private List<GoogleCompletionContent> buildGoogleMessages(
@Nullable String model,
CallParameters callParameters) {
var messages = buildGoogleMessages(callParameters);
if (model == null) {
return messages;
}
int totalUsage = messages.parallelStream()
.mapToInt(message -> encodingManager.countMessageTokens(message.getRole(),
String.join(",", message.getParts().stream().map(GoogleContentPart::getText).toList())))
.sum() + ConfigurationSettings.getCurrentState().getMaxTokens();
int modelMaxTokens;
try {
modelMaxTokens = GoogleModel.findByCode(model).getMaxTokens();
if (totalUsage <= modelMaxTokens) {
return messages;
}
} catch (NoSuchElementException ex) {
return messages;
}
return tryReducingGoogleMessagesOrThrow(messages, totalUsage, modelMaxTokens);
}
private List<OpenAIChatCompletionMessage> tryReducingMessagesOrThrow(
List<OpenAIChatCompletionMessage> messages,
int totalUsage,
@ -393,4 +566,29 @@ public class CompletionRequestProvider {
return messages.stream().filter(Objects::nonNull).toList();
}
private List<GoogleCompletionContent> tryReducingGoogleMessagesOrThrow(
List<GoogleCompletionContent> messages,
int totalUsage,
int modelMaxTokens) {
if (!ConversationsState.getInstance().discardAllTokenLimits) {
if (!conversation.isDiscardTokenLimit()) {
throw new TotalUsageExceededException();
}
}
// skip the system prompt
for (int i = 1; i < messages.size(); i++) {
if (totalUsage <= modelMaxTokens) {
break;
}
var message = messages.get(i);
totalUsage -= encodingManager.countMessageTokens(message.getRole(),
String.join(",", message.getParts().stream().map(GoogleContentPart::getText).toList()));
messages.set(i, null);
}
return messages.stream().filter(Objects::nonNull).toList();
}
}

View file

@ -1,7 +1,5 @@
package ee.carlrobert.codegpt.completions;
import static ee.carlrobert.codegpt.settings.service.ServiceType.ANTHROPIC;
import static ee.carlrobert.codegpt.settings.service.ServiceType.AZURE;
import static ee.carlrobert.codegpt.settings.service.ServiceType.CUSTOM_OPENAI;
import static ee.carlrobert.codegpt.settings.service.ServiceType.LLAMA_CPP;
import static ee.carlrobert.codegpt.settings.service.ServiceType.OPENAI;
@ -10,8 +8,6 @@ import static ee.carlrobert.codegpt.settings.service.ServiceType.YOU;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.Service;
import com.intellij.openapi.diagnostic.Logger;
import ee.carlrobert.codegpt.codecompletions.CodeCompletionRequestFactory;
import ee.carlrobert.codegpt.codecompletions.InfillRequestDetails;
import ee.carlrobert.codegpt.completions.llama.LlamaModel;
import ee.carlrobert.codegpt.completions.llama.PromptTemplate;
import ee.carlrobert.codegpt.credentials.CredentialsStore;
@ -21,16 +17,25 @@ import ee.carlrobert.codegpt.settings.configuration.ConfigurationSettings;
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.CodeGPTServiceSettings;
import ee.carlrobert.codegpt.settings.service.custom.CustomServiceSettings;
import ee.carlrobert.codegpt.settings.service.google.GoogleSettings;
import ee.carlrobert.codegpt.settings.service.google.GoogleSettingsState;
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.llm.client.DeserializationUtil;
import ee.carlrobert.llm.client.anthropic.completion.ClaudeCompletionRequest;
import ee.carlrobert.llm.client.anthropic.completion.ClaudeCompletionStandardMessage;
import ee.carlrobert.llm.client.google.completion.GoogleCompletionContent;
import ee.carlrobert.llm.client.google.completion.GoogleCompletionRequest;
import ee.carlrobert.llm.client.google.completion.GoogleGenerationConfig;
import ee.carlrobert.llm.client.llama.completion.LlamaCompletionRequest;
import ee.carlrobert.llm.client.ollama.completion.request.OllamaChatCompletionMessage;
import ee.carlrobert.llm.client.ollama.completion.request.OllamaChatCompletionRequest;
import ee.carlrobert.llm.client.openai.completion.OpenAIChatCompletionEventSourceListener;
import ee.carlrobert.llm.client.openai.completion.OpenAITextCompletionEventSourceListener;
import ee.carlrobert.llm.client.openai.completion.request.OpenAIChatCompletionRequest;
import ee.carlrobert.llm.client.openai.completion.request.OpenAIChatCompletionRequest.Builder;
import ee.carlrobert.llm.client.openai.completion.request.OpenAIChatCompletionStandardMessage;
import ee.carlrobert.llm.client.openai.completion.response.OpenAIChatCompletionResponse;
import ee.carlrobert.llm.client.openai.completion.response.OpenAIChatCompletionResponseChoice;
@ -77,8 +82,18 @@ public final class CompletionRequestService {
public EventSource getChatCompletionAsync(
CallParameters callParameters,
CompletionEventListener<String> eventListener) {
var application = ApplicationManager.getApplication();
var requestProvider = new CompletionRequestProvider(callParameters.getConversation());
return switch (GeneralSettings.getCurrentState().getSelectedService()) {
case CODEGPT -> CompletionClientProvider.getCodeGPTClient().getChatCompletionAsync(
requestProvider.buildOpenAIChatCompletionRequest(
application.getService(CodeGPTServiceSettings.class)
.getState()
.getChatCompletionSettings()
.getModel(),
callParameters),
eventListener
);
case OPENAI -> CompletionClientProvider.getOpenAIClient().getChatCompletionAsync(
requestProvider.buildOpenAIChatCompletionRequest(
OpenAISettings.getCurrentState().getModel(),
@ -86,8 +101,7 @@ public final class CompletionRequestService {
eventListener);
case CUSTOM_OPENAI -> getCustomOpenAIChatCompletionAsync(
requestProvider.buildCustomOpenAIChatCompletionRequest(
ApplicationManager.getApplication()
.getService(CustomServiceSettings.class)
application.getService(CustomServiceSettings.class)
.getState()
.getChatCompletionSettings(),
callParameters),
@ -106,27 +120,18 @@ public final class CompletionRequestService {
callParameters.getMessage(),
callParameters.getConversationType()),
eventListener);
};
}
public EventSource getCodeCompletionAsync(
InfillRequestDetails requestDetails,
CompletionEventListener<String> eventListener) {
var httpClient = CompletionClientProvider.getDefaultClientBuilder().build();
return switch (GeneralSettings.getCurrentState().getSelectedService()) {
case OPENAI -> CompletionClientProvider.getOpenAIClient()
.getCompletionAsync(
CodeCompletionRequestFactory.buildOpenAIRequest(requestDetails),
eventListener);
case CUSTOM_OPENAI -> EventSources.createFactory(httpClient).newEventSource(
CodeCompletionRequestFactory.buildCustomRequest(requestDetails),
new OpenAITextCompletionEventSourceListener(eventListener));
case LLAMA_CPP -> CompletionClientProvider.getLlamaClient()
.getChatCompletionAsync(
CodeCompletionRequestFactory.buildLlamaRequest(requestDetails),
eventListener);
default ->
throw new IllegalArgumentException("Code completion not supported for selected service");
case OLLAMA -> CompletionClientProvider.getOllamaClient().getChatCompletionAsync(
requestProvider.buildOllamaChatCompletionRequest(callParameters),
eventListener);
case GOOGLE -> {
var settings = application.getService(GoogleSettings.class).getState();
yield CompletionClientProvider.getGoogleClient().getChatCompletionAsync(
requestProvider.buildGoogleChatCompletionRequest(
settings.getModel(),
callParameters),
settings.getModel(),
eventListener);
}
};
}
@ -135,13 +140,17 @@ public final class CompletionRequestService {
String gitDiff,
CompletionEventListener<String> eventListener) {
var configuration = ConfigurationSettings.getCurrentState();
var openaiRequest = new OpenAIChatCompletionRequest.Builder(List.of(
var openaiRequest = new Builder(List.of(
new OpenAIChatCompletionStandardMessage("system", systemPrompt),
new OpenAIChatCompletionStandardMessage("user", gitDiff)))
.setModel(OpenAISettings.getCurrentState().getModel())
.build();
var selectedService = GeneralSettings.getCurrentState().getSelectedService();
switch (selectedService) {
case CODEGPT:
CompletionClientProvider.getCodeGPTClient()
.getChatCompletionAsync(openaiRequest, eventListener);
break;
case OPENAI:
CompletionClientProvider.getOpenAIClient()
.getChatCompletionAsync(openaiRequest, eventListener);
@ -191,6 +200,35 @@ public final class CompletionRequestService {
.setRepeat_penalty(settings.getRepeatPenalty())
.build(), eventListener);
break;
case OLLAMA:
var model = ApplicationManager.getApplication()
.getService(OllamaSettings.class)
.getState()
.getModel();
var request = new OllamaChatCompletionRequest.Builder(
model,
List.of(
new OllamaChatCompletionMessage("system", systemPrompt, null),
new OllamaChatCompletionMessage("user", gitDiff, null)
)
).build();
CompletionClientProvider.getOllamaClient().getChatCompletionAsync(request, eventListener);
break;
case GOOGLE:
GoogleSettingsState state = ApplicationManager.getApplication()
.getService(GoogleSettings.class).getState();
CompletionClientProvider.getGoogleClient()
.getChatCompletionAsync(new GoogleCompletionRequest.Builder(
List.of(
new GoogleCompletionContent("user", List.of(systemPrompt)),
new GoogleCompletionContent("model", List.of("Understood.")),
new GoogleCompletionContent("user", List.of(gitDiff))
))
.generationConfig(new GoogleGenerationConfig.Builder()
.maxOutputTokens(configuration.getMaxTokens())
.temperature(configuration.getTemperature()).build())
.build(), state.getModel(), eventListener);
break;
default:
LOG.debug("Unknown service: {}", selectedService);
break;
@ -226,19 +264,16 @@ public final class CompletionRequestService {
}
public static boolean isRequestAllowed(ServiceType serviceType) {
if (serviceType == OPENAI
&& CredentialsStore.INSTANCE.isCredentialSet(CredentialKey.OPENAI_API_KEY)) {
return true;
}
var azureCredentialKey = AzureSettings.getCurrentState().isUseAzureApiKeyAuthentication()
? CredentialKey.AZURE_OPENAI_API_KEY
: CredentialKey.AZURE_ACTIVE_DIRECTORY_TOKEN;
if (serviceType == AZURE && CredentialsStore.INSTANCE.isCredentialSet(azureCredentialKey)) {
return true;
}
return List.of(LLAMA_CPP, ANTHROPIC, CUSTOM_OPENAI).contains(serviceType);
return switch (serviceType) {
case OPENAI -> CredentialsStore.INSTANCE.isCredentialSet(CredentialKey.OPENAI_API_KEY);
case AZURE -> CredentialsStore.INSTANCE.isCredentialSet(
AzureSettings.getCurrentState().isUseAzureApiKeyAuthentication()
? CredentialKey.AZURE_OPENAI_API_KEY
: CredentialKey.AZURE_ACTIVE_DIRECTORY_TOKEN);
case CODEGPT, CUSTOM_OPENAI, ANTHROPIC, LLAMA_CPP, OLLAMA -> true;
case YOU -> false;
case GOOGLE -> CredentialsStore.INSTANCE.isCredentialSet(CredentialKey.GOOGLE_API_KEY);
};
}
/**

View file

@ -7,67 +7,138 @@ import java.net.URL;
public enum HuggingFaceModel {
CODE_LLAMA_7B_Q3(7, 3, "CodeLlama-7B-Instruct-GGUF"),
CODE_LLAMA_7B_Q4(7, 4, "CodeLlama-7B-Instruct-GGUF"),
CODE_LLAMA_7B_Q5(7, 5, "CodeLlama-7B-Instruct-GGUF"),
CODE_LLAMA_13B_Q3(13, 3, "CodeLlama-13B-Instruct-GGUF"),
CODE_LLAMA_13B_Q4(13, 4, "CodeLlama-13B-Instruct-GGUF"),
CODE_LLAMA_13B_Q5(13, 5, "CodeLlama-13B-Instruct-GGUF"),
CODE_LLAMA_34B_Q3(34, 3, "CodeLlama-34B-Instruct-GGUF"),
CODE_LLAMA_34B_Q4(34, 4, "CodeLlama-34B-Instruct-GGUF"),
CODE_LLAMA_34B_Q5(34, 5, "CodeLlama-34B-Instruct-GGUF"),
CODE_LLAMA_7B_Q3(7, 3, "CodeLlama-7B-Instruct-GGUF", "codellama-7b-instruct.Q3_K_M.gguf"),
CODE_LLAMA_7B_Q4(7, 4, "CodeLlama-7B-Instruct-GGUF", "codellama-7b-instruct.Q4_K_M.gguf"),
CODE_LLAMA_7B_Q5(7, 5, "CodeLlama-7B-Instruct-GGUF", "codellama-7b-instruct.Q5_K_M.gguf"),
CODE_LLAMA_13B_Q3(13, 3, "CodeLlama-13B-Instruct-GGUF", "codellama-13b-instruct.Q3_K_M.gguf"),
CODE_LLAMA_13B_Q4(13, 4, "CodeLlama-13B-Instruct-GGUF", "codellama-13b-instruct.Q4_K_M.gguf"),
CODE_LLAMA_13B_Q5(13, 5, "CodeLlama-13B-Instruct-GGUF", "codellama-13b-instruct.Q5_K_M.gguf"),
CODE_LLAMA_34B_Q3(34, 3, "CodeLlama-34B-Instruct-GGUF", "codellama-34b-instruct.Q3_K_M.gguf"),
CODE_LLAMA_34B_Q4(34, 4, "CodeLlama-34B-Instruct-GGUF", "codellama-34b-instruct.Q4_K_M.gguf"),
CODE_LLAMA_34B_Q5(34, 5, "CodeLlama-34B-Instruct-GGUF", "codellama-34b-instruct.Q5_K_M.gguf"),
CODE_BOOGA_34B_Q3(34, 3, "CodeBooga-34B-v0.1-GGUF"),
CODE_BOOGA_34B_Q4(34, 4, "CodeBooga-34B-v0.1-GGUF"),
CODE_BOOGA_34B_Q5(34, 5, "CodeBooga-34B-v0.1-GGUF"),
CODE_BOOGA_34B_Q3(34, 3, "CodeBooga-34B-v0.1-GGUF", "codebooga-34b-v0.1.Q3_K_M.gguf"),
CODE_BOOGA_34B_Q4(34, 4, "CodeBooga-34B-v0.1-GGUF", "codebooga-34b-v0.1.Q4_K_M.gguf"),
CODE_BOOGA_34B_Q5(34, 5, "CodeBooga-34B-v0.1-GGUF", "codebooga-34b-v0.1.Q5_K_M.gguf"),
DEEPSEEK_CODER_1_3B_Q3(1, 3, "deepseek-coder-1.3b-instruct-GGUF"),
DEEPSEEK_CODER_1_3B_Q4(1, 4, "deepseek-coder-1.3b-instruct-GGUF"),
DEEPSEEK_CODER_1_3B_Q5(1, 5, "deepseek-coder-1.3b-instruct-GGUF"),
DEEPSEEK_CODER_6_7B_Q3(7, 3, "deepseek-coder-6.7b-instruct-GGUF"),
DEEPSEEK_CODER_6_7B_Q4(7, 4, "deepseek-coder-6.7b-instruct-GGUF"),
DEEPSEEK_CODER_6_7B_Q5(7, 5, "deepseek-coder-6.7b-instruct-GGUF"),
DEEPSEEK_CODER_33B_Q3(33, 3, "deepseek-coder-33b-instruct-GGUF"),
DEEPSEEK_CODER_33B_Q4(33, 4, "deepseek-coder-33b-instruct-GGUF"),
DEEPSEEK_CODER_33B_Q5(33, 5, "deepseek-coder-33b-instruct-GGUF"),
DEEPSEEK_CODER_1_3B_Q3(1, 3, "deepseek-coder-1.3b-instruct-GGUF",
"deepseek-coder-1.3b-instruct.Q3_K_M.gguf", 0.705),
DEEPSEEK_CODER_1_3B_Q4(1, 4, "deepseek-coder-1.3b-instruct-GGUF",
"deepseek-coder-1.3b-instruct.Q4_K_M.gguf", 0.874),
DEEPSEEK_CODER_1_3B_Q5(1, 5, "deepseek-coder-1.3b-instruct-GGUF",
"deepseek-coder-1.3b-instruct.Q5_K_M.gguf", 1.0),
DEEPSEEK_CODER_6_7B_Q3(7, 3, "deepseek-coder-6.7b-instruct-GGUF",
"deepseek-coder-6.7b-instruct.Q3_K_M.gguf"),
DEEPSEEK_CODER_6_7B_Q4(7, 4, "deepseek-coder-6.7b-instruct-GGUF",
"deepseek-coder-6.7b-instruct.Q4_K_M.gguf"),
DEEPSEEK_CODER_6_7B_Q5(7, 5, "deepseek-coder-6.7b-instruct-GGUF",
"deepseek-coder-6.7b-instruct.Q5_K_M.gguf"),
DEEPSEEK_CODER_33B_Q3(33, 3, "deepseek-coder-33b-instruct-GGUF",
"deepseek-coder-33b-instruct.Q3_K_M.gguf", 16.1),
DEEPSEEK_CODER_33B_Q4(33, 4, "deepseek-coder-33b-instruct-GGUF",
"deepseek-coder-33b-instruct.Q4_K_M.gguf", 19.9),
DEEPSEEK_CODER_33B_Q5(33, 5, "deepseek-coder-33b-instruct-GGUF",
"deepseek-coder-33b-instruct.Q5_K_M.gguf", 23.5),
PHIND_CODE_LLAMA_34B_Q3(34, 3, "Phind-CodeLlama-34B-v2-GGUF"),
PHIND_CODE_LLAMA_34B_Q4(34, 4, "Phind-CodeLlama-34B-v2-GGUF"),
PHIND_CODE_LLAMA_34B_Q5(34, 5, "Phind-CodeLlama-34B-v2-GGUF"),
PHIND_CODE_LLAMA_34B_Q3(34, 3, "Phind-CodeLlama-34B-v2-GGUF",
"phind-codellama-34b-v2.Q3_K_M.gguf"),
PHIND_CODE_LLAMA_34B_Q4(34, 4, "Phind-CodeLlama-34B-v2-GGUF",
"phind-codellama-34b-v2.Q4_K_M.gguf"),
PHIND_CODE_LLAMA_34B_Q5(34, 5, "Phind-CodeLlama-34B-v2-GGUF",
"phind-codellama-34b-v2.Q5_K_M.gguf"),
WIZARD_CODER_PYTHON_7B_Q3(7, 3, "WizardCoder-Python-7B-V1.0-GGUF"),
WIZARD_CODER_PYTHON_7B_Q4(7, 4, "WizardCoder-Python-7B-V1.0-GGUF"),
WIZARD_CODER_PYTHON_7B_Q5(7, 5, "WizardCoder-Python-7B-V1.0-GGUF"),
WIZARD_CODER_PYTHON_13B_Q3(13, 3, "WizardCoder-Python-13B-V1.0-GGUF"),
WIZARD_CODER_PYTHON_13B_Q4(13, 4, "WizardCoder-Python-13B-V1.0-GGUF"),
WIZARD_CODER_PYTHON_13B_Q5(13, 5, "WizardCoder-Python-13B-V1.0-GGUF"),
WIZARD_CODER_PYTHON_34B_Q3(34, 3, "WizardCoder-Python-34B-V1.0-GGUF"),
WIZARD_CODER_PYTHON_34B_Q4(34, 4, "WizardCoder-Python-34B-V1.0-GGUF"),
WIZARD_CODER_PYTHON_34B_Q5(34, 5, "WizardCoder-Python-34B-V1.0-GGUF"),
WIZARD_CODER_PYTHON_7B_Q3(7, 3, "WizardCoder-Python-7B-V1.0-GGUF",
"wizardcoder-python-7b-v1.0.Q3_K_M.gguf"),
WIZARD_CODER_PYTHON_7B_Q4(7, 4, "WizardCoder-Python-7B-V1.0-GGUF",
"wizardcoder-python-7b-v1.0.Q4_K_M.gguf"),
WIZARD_CODER_PYTHON_7B_Q5(7, 5, "WizardCoder-Python-7B-V1.0-GGUF",
"wizardcoder-python-7b-v1.0.Q5_K_M.gguf"),
WIZARD_CODER_PYTHON_13B_Q3(13, 3, "WizardCoder-Python-13B-V1.0-GGUF",
"wizardcoder-python-13b-v1.0.Q3_K_M.gguf"),
WIZARD_CODER_PYTHON_13B_Q4(13, 4, "WizardCoder-Python-13B-V1.0-GGUF",
"wizardcoder-python-13b-v1.0.Q4_K_M.gguf"),
WIZARD_CODER_PYTHON_13B_Q5(13, 5, "WizardCoder-Python-13B-V1.0-GGUF",
"wizardcoder-python-13b-v1.0.Q5_K_M.gguf"),
WIZARD_CODER_PYTHON_34B_Q3(34, 3, "WizardCoder-Python-34B-V1.0-GGUF",
"wizardcoder-python-34b-v1.0.Q3_K_M.gguf"),
WIZARD_CODER_PYTHON_34B_Q4(34, 4, "WizardCoder-Python-34B-V1.0-GGUF",
"wizardcoder-python-34b-v1.0.Q4_K_M.gguf"),
WIZARD_CODER_PYTHON_34B_Q5(34, 5, "WizardCoder-Python-34B-V1.0-GGUF",
"wizardcoder-python-34b-v1.0.Q5_K_M.gguf"),
LLAMA_3_8B_IQ3_M(8, 3, "Meta-Llama-3-8B-Instruct-IQ3_M.gguf", "lmstudio-community"),
LLAMA_3_8B_Q4_K_M(8, 4, "Meta-Llama-3-8B-Instruct-Q4_K_M.gguf", "lmstudio-community"),
LLAMA_3_8B_Q5_K_M(8, 5, "Meta-Llama-3-8B-Instruct-Q5_K_M.gguf", "lmstudio-community"),
LLAMA_3_8B_Q6_K(8, 6, "Meta-Llama-3-8B-Instruct-Q6_K.gguf", "lmstudio-community"),
LLAMA_3_8B_Q8_0(8, 8, "Meta-Llama-3-8B-Instruct-Q8_0.gguf", "lmstudio-community"),
LLAMA_3_70B_IQ1(70, 1, "Meta-Llama-3-70B-Instruct-IQ1_M.gguf", "lmstudio-community"),
LLAMA_3_70B_IQ2_XS(70, 2, "Meta-Llama-3-70B-Instruct-IQ2_XS.gguf", "lmstudio-community"),
LLAMA_3_70B_Q4_K_M(70, 4, "Meta-Llama-3-70B-Instruct-Q4_K_M.gguf", "lmstudio-community");
LLAMA_3_8B_IQ3_M(8, 3, "Meta-Llama-3-8B-Instruct-GGUF", "Meta-Llama-3-8B-Instruct-IQ3_M.gguf",
"lmstudio-community", 3.78),
LLAMA_3_8B_Q4_K_M(8, 4, "Meta-Llama-3-8B-Instruct-GGUF", "Meta-Llama-3-8B-Instruct-Q4_K_M.gguf",
"lmstudio-community", 4.92),
LLAMA_3_8B_Q5_K_M(8, 5, "Meta-Llama-3-8B-Instruct-GGUF", "Meta-Llama-3-8B-Instruct-Q5_K_M.gguf",
"lmstudio-community", 5.73),
LLAMA_3_8B_Q6_K(8, 6, "Meta-Llama-3-8B-Instruct-GGUF", "Meta-Llama-3-8B-Instruct-Q6_K.gguf",
"lmstudio-community", 6.6),
LLAMA_3_8B_Q8_0(8, 8, "Meta-Llama-3-8B-Instruct-GGUF", "Meta-Llama-3-8B-Instruct-Q8_0.gguf",
"lmstudio-community", 8.54),
LLAMA_3_70B_IQ1(70, 1, "Meta-Llama-3-70B-Instruct-GGUF", "Meta-Llama-3-70B-Instruct-IQ1_M.gguf",
"lmstudio-community", 16.8),
LLAMA_3_70B_IQ2_XS(70, 2, "Meta-Llama-3-70B-Instruct-GGUF",
"Meta-Llama-3-70B-Instruct-IQ2_XS.gguf", "lmstudio-community", 21.1),
LLAMA_3_70B_Q4_K_M(70, 4, "Meta-Llama-3-70B-Instruct-GGUF",
"Meta-Llama-3-70B-Instruct-Q4_K_M.gguf", "lmstudio-community", 42.5),
PHI_3_3_8B_4K_IQ4_NL(4, 4, "Phi-3-mini-4k-instruct-GGUF", "Phi-3-mini-4k-instruct-IQ4_NL.gguf",
"lmstudio-community", 2.18),
PHI_3_3_8B_4K_Q5_K_M(4, 5, "Phi-3-mini-4k-instruct-GGUF", "Phi-3-mini-4k-instruct-Q5_K_M.gguf",
"lmstudio-community", 2.64),
PHI_3_3_8B_4K_Q6_K(4, 6, "Phi-3-mini-4k-instruct-GGUF", "Phi-3-mini-4k-instruct-Q6_K.gguf",
"lmstudio-community", 3.14),
PHI_3_3_8B_4K_Q8_0(4, 8, "Phi-3-mini-4k-instruct-GGUF", "Phi-3-mini-4k-instruct-Q8_0.gguf",
"lmstudio-community", 4.06),
PHI_3_3_8B_4K_FP16(4, 16, "Phi-3-mini-4k-instruct-GGUF", "Phi-3-mini-4k-instruct-fp16.gguf",
"lmstudio-community", 7.64),
CODE_GEMMA_7B_Q3_K_L(7, 3, "codegemma-1.1-7b-it-GGUF", "codegemma-1.1-7b-it-Q3_K_L.gguf",
"lmstudio-community", 4.71),
CODE_GEMMA_7B_Q4_K_M(7, 4, "codegemma-1.1-7b-it-GGUF", "codegemma-1.1-7b-it-Q4_K_M.gguf",
"lmstudio-community", 5.33),
CODE_GEMMA_7B_Q5_K_M(7, 5, "codegemma-1.1-7b-it-GGUF", "codegemma-1.1-7b-it-Q5_K_M.gguf",
"lmstudio-community", 6.14),
CODE_GEMMA_7B_Q6_K(7, 6, "codegemma-1.1-7b-it-GGUF", "codegemma-1.1-7b-it-Q6_K.gguf",
"lmstudio-community", 7.01),
CODE_GEMMA_7B_Q8_0(7, 8, "codegemma-1.1-7b-it-GGUF", "codegemma-1.1-7b-it-Q8_0.gguf",
"lmstudio-community", 9.08),
CODE_QWEN_1_5_7B_Q3_K_M(7, 3, "Qwen_-_CodeQwen1.5-7B-Chat-gguf",
"CodeQwen1.5-7B-Chat.Q3_K_M.gguf", "RichardErkhov", 3.81),
CODE_QWEN_1_5_7B_Q4_K_M(7, 4, "Qwen_-_CodeQwen1.5-7B-Chat-gguf",
"CodeQwen1.5-7B-Chat.Q4_K_M.gguf", "RichardErkhov", 4.74),
CODE_QWEN_1_5_7B_Q5_K_M(7, 5, "Qwen_-_CodeQwen1.5-7B-Chat-gguf",
"CodeQwen1.5-7B-Chat.Q5_K_M.gguf", "RichardErkhov", 5.43),
CODE_QWEN_1_5_7B_Q6_K(7, 6, "Qwen_-_CodeQwen1.5-7B-Chat-gguf",
"CodeQwen1.5-7B-Chat.Q6_K.gguf", "RichardErkhov", 6.38),
;
private final int parameterSize;
private final int quantization;
private final String modelName;
private final String directory;
private final String fileName;
private final String user;
private final Double downloadSize; // in GB
HuggingFaceModel(int parameterSize, int quantization, String modelName) {
this(parameterSize, quantization, modelName, "TheBloke");
HuggingFaceModel(int parameterSize, int quantization, String directory, String fileName) {
this(parameterSize, quantization, directory, fileName, "TheBloke", null);
}
HuggingFaceModel(int parameterSize, int quantization, String modelName, String user) {
HuggingFaceModel(int parameterSize, int quantization, String directory, String fileName,
Double downloadSize) {
this(parameterSize, quantization, directory, fileName, "TheBloke", downloadSize);
}
HuggingFaceModel(int parameterSize, int quantization, String directory, String fileName,
String user, Double downloadSize) {
this.parameterSize = parameterSize;
this.quantization = quantization;
this.modelName = modelName;
this.directory = directory;
this.fileName = fileName;
this.user = user;
this.downloadSize = downloadSize;
}
public int getParameterSize() {
@ -82,17 +153,18 @@ public enum HuggingFaceModel {
return name();
}
public Double getDownloadSize() {
return downloadSize;
}
public String getFileName() {
if ("TheBloke".equals(user)) {
return modelName.toLowerCase().replace("-gguf", format(".Q%d_K_M.gguf", quantization));
}
return modelName;
return fileName;
}
public URL getFileURL() {
try {
return new URL(
"https://huggingface.co/%s/%s/resolve/main/%s".formatted(user, getDirectory(), getFileName()));
"https://huggingface.co/%s/%s/resolve/main/%s".formatted(user, directory, fileName));
} catch (MalformedURLException ex) {
throw new RuntimeException(ex);
}
@ -100,22 +172,13 @@ public enum HuggingFaceModel {
public URL getHuggingFaceURL() {
try {
return new URL("https://huggingface.co/%s/%s".formatted(user, getDirectory()));
return new URL("https://huggingface.co/%s/%s".formatted(user, directory));
} catch (MalformedURLException ex) {
throw new RuntimeException(ex);
}
}
private String getDirectory() {
if ("lmstudio-community".equals(user)) {
// Meta-Llama-3-8B-Instruct-Q4_K_M.gguf -> Meta-Llama-3-8B-Instruct-GGUF
return modelName.replaceFirst("-[^.-]+\\.gguf$", "-GGUF");
}
return modelName;
}
@Override
public String toString() {
public String getQuantizationLabel() {
return format("%d-bit precision", quantization);
}
}

View file

@ -17,7 +17,7 @@ public enum LlamaModel {
+ "support for large input contexts, and zero-shot instruction following ability for "
+ "programming tasks.",
PromptTemplate.LLAMA,
InfillPromptTemplate.LLAMA,
InfillPromptTemplate.CODE_LLAMA,
List.of(
HuggingFaceModel.CODE_LLAMA_7B_Q3,
HuggingFaceModel.CODE_LLAMA_7B_Q4,
@ -99,7 +99,52 @@ public enum LlamaModel {
HuggingFaceModel.LLAMA_3_8B_Q8_0,
HuggingFaceModel.LLAMA_3_70B_IQ1,
HuggingFaceModel.LLAMA_3_70B_IQ2_XS,
HuggingFaceModel.LLAMA_3_70B_Q4_K_M));
HuggingFaceModel.LLAMA_3_70B_Q4_K_M)),
PHI_3(
"Phi-3 Mini",
"Phi-3 Mini is a 3.8B parameters, lightweight, state-of-the-art open model. "
+ "When assessed against benchmarks testing common sense, language understanding, math, "
+ "code, long context and logical reasoning, Phi-3 Mini-4K-Instruct showcased a robust "
+ "and state-of-the-art performance among models with less than 13 billion parameters.",
PromptTemplate.PHI_3,
List.of(
HuggingFaceModel.PHI_3_3_8B_4K_IQ4_NL,
HuggingFaceModel.PHI_3_3_8B_4K_Q5_K_M,
HuggingFaceModel.PHI_3_3_8B_4K_Q6_K,
HuggingFaceModel.PHI_3_3_8B_4K_Q8_0,
HuggingFaceModel.PHI_3_3_8B_4K_FP16)),
CODE_GEMMA(
"CodeGemma Instruct",
"CodeGemma Instruct is the first in a series of coding models released by Google. "
+ "As an instruct model, it specializes in being asked coding related questions, but can "
+ "also function as an autocomplete/fill-in-middle model for tools like co-pilot.\n"
+ "This model is perfect for general coding questions or code generation.",
PromptTemplate.CODE_GEMMA,
InfillPromptTemplate.CODE_GEMMA,
List.of(
HuggingFaceModel.CODE_GEMMA_7B_Q3_K_L,
HuggingFaceModel.CODE_GEMMA_7B_Q4_K_M,
HuggingFaceModel.CODE_GEMMA_7B_Q5_K_M,
HuggingFaceModel.CODE_GEMMA_7B_Q6_K,
HuggingFaceModel.CODE_GEMMA_7B_Q8_0)),
CODE_QWEN(
"CodeQwen1.5", """
A specialized codeLLM built upon the Qwen1.5 language model. \
CodeQwen1.5-7B has been pretrained with around 3 trillion tokens of code-related data. \
It supports an extensive repertoire of 92 programming languages, and it exhibits \
exceptional capacity in long-context understanding and generation with the ability to \
process information of 64K tokens. In terms of performance, CodeQwen1.5 demonstrates \
impressive capabilities in basic code generation, long-context modelling, code editing \
and SQL. We believe this model can significantly enhance developer productivity and \
streamline software development workflows within diverse technological environments.""",
PromptTemplate.CODE_QWEN,
InfillPromptTemplate.CODE_QWEN,
List.of(
HuggingFaceModel.CODE_QWEN_1_5_7B_Q3_K_M,
HuggingFaceModel.CODE_QWEN_1_5_7B_Q4_K_M,
HuggingFaceModel.CODE_QWEN_1_5_7B_Q5_K_M,
HuggingFaceModel.CODE_QWEN_1_5_7B_Q6_K)),
;
private final String label;
private final String description;

View file

@ -125,6 +125,67 @@ public enum PromptTemplate {
.toString();
}
},
PHI_3("Phi-3 Mini", List.of("<|end|>")) {
@Override
public String buildPrompt(String systemPrompt, String userPrompt, List<Message> history) {
StringBuilder prompt = new StringBuilder();
for (Message message : history) {
prompt.append("<|user|>\n")
.append(message.getPrompt())
.append("<|end|>\n<|assistant|>\n")
.append(message.getResponse())
.append("<|end|>\n");
}
return prompt.append("<|user|>\n")
.append(userPrompt)
.append("<|end|>\n<|assistant|>")
.toString();
}
},
CODE_GEMMA("CodeGemma Instruct") {
@Override
public String buildPrompt(String systemPrompt, String userPrompt, List<Message> history) {
StringBuilder prompt = new StringBuilder();
for (Message message : history) {
prompt.append("<start_of_turn>user\n")
.append(message.getPrompt())
.append("<end_of_turn>\n<start_of_turn>model\n")
.append(message.getResponse()).append("<end_of_turn>\n");
}
return prompt.append("<start_of_turn>user\n")
.append(userPrompt)
.append("<end_of_turn>\n<start_of_turn>model\n")
.toString();
}
},
CODE_QWEN("CodeQwen1.5", List.of("<|endoftext|>")) {
@Override
public String buildPrompt(String systemPrompt, String userPrompt, List<Message> history) {
StringBuilder prompt = new StringBuilder();
if (systemPrompt != null && !systemPrompt.isBlank()) {
prompt.append("<|im_start|>system\n")
.append(systemPrompt)
.append("<|im_end|>\n");
}
for (Message message : history) {
prompt.append("<|im_start|>user\n")
.append(message.getPrompt())
.append("<|im_end|>\n<|im_start|>assistant\n")
.append(message.getResponse()).append("<|im_end|>\n");
}
return prompt.append("<|im_start|>user\n")
.append(userPrompt)
.append("<|im_end|>\n<|im_start|>assistant\n")
.toString();
}
},
ALPACA("Alpaca/Vicuna") {
@Override
public String buildPrompt(String systemPrompt, String userPrompt, List<Message> history) {

View file

@ -29,7 +29,7 @@ public class Conversation {
}
public void setMessages(List<Message> messages) {
this.messages = messages;
this.messages = new ArrayList<>(messages);
}
public String getClientCode() {
@ -77,7 +77,7 @@ public class Conversation {
}
public void removeMessage(UUID messageId) {
setMessages(messages.stream()
messages = new ArrayList<>(messages.stream()
.filter(message -> !message.getId().equals(messageId))
.toList());
}

View file

@ -8,7 +8,10 @@ import ee.carlrobert.codegpt.settings.GeneralSettings;
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.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 java.time.LocalDateTime;
import java.util.ArrayList;
@ -186,7 +189,12 @@ public final class ConversationService {
}
private static String getModelForSelectedService(ServiceType serviceType) {
var application = ApplicationManager.getApplication();
return switch (serviceType) {
case CODEGPT -> application.getService(CodeGPTServiceSettings.class)
.getState()
.getChatCompletionSettings()
.getModel();
case OPENAI -> OpenAISettings.getCurrentState().getModel();
case CUSTOM_OPENAI -> "CustomService";
case ANTHROPIC -> AnthropicSettings.getCurrentState().getModel();
@ -195,9 +203,15 @@ public final class ConversationService {
case LLAMA_CPP -> {
var llamaSettings = LlamaSettings.getCurrentState();
yield llamaSettings.isUseCustomModel()
? llamaSettings.getCustomLlamaModelPath()
: llamaSettings.getHuggingFaceModel().getCode();
? llamaSettings.getCustomLlamaModelPath()
: llamaSettings.getHuggingFaceModel().getCode();
}
case OLLAMA -> application.getService(OllamaSettings.class)
.getState()
.getModel();
case GOOGLE -> application.getService(GoogleSettings.class)
.getState()
.getModel();
};
}
}

View file

@ -10,7 +10,10 @@ import ee.carlrobert.codegpt.conversations.Conversation;
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.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 org.jetbrains.annotations.NotNull;
@ -69,10 +72,18 @@ public class GeneralSettings implements PersistentStateComponent<GeneralSettings
if ("you.chat.completion".equals(clientCode)) {
state.setSelectedService(ServiceType.YOU);
}
if ("ollama.chat.completion".equals(clientCode)) {
state.setSelectedService(ServiceType.OLLAMA);
}
}
public String getModel() {
switch (state.getSelectedService()) {
case CODEGPT:
return ApplicationManager.getApplication().getService(CodeGPTServiceSettings.class)
.getState()
.getCodeCompletionSettings()
.getModel();
case OPENAI:
return OpenAISettings.getCurrentState().getModel();
case ANTHROPIC:
@ -98,6 +109,16 @@ public class GeneralSettings implements PersistentStateComponent<GeneralSettings
llamaModel.getLabel(),
huggingFaceModel.getParameterSize(),
huggingFaceModel.getQuantization());
case OLLAMA:
return ApplicationManager.getApplication()
.getService(OllamaSettings.class)
.getState()
.getModel();
case GOOGLE:
return ApplicationManager.getApplication()
.getService(GoogleSettings.class)
.getState()
.getModel();
default:
return "Unknown";
}

View file

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

View file

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

View file

@ -5,7 +5,7 @@ import ee.carlrobert.codegpt.settings.service.ServiceType;
public class GeneralSettingsState {
private String displayName = "";
private ServiceType selectedService = ServiceType.OPENAI;
private ServiceType selectedService = ServiceType.CODEGPT;
public String getDisplayName() {
if (displayName == null || displayName.isEmpty()) {

View file

@ -190,7 +190,12 @@ public class ConfigurationComponent {
private JPanel createTablePanel() {
return ToolbarDecorator.createDecorator(table)
.setPreferredSize(new Dimension(table.getPreferredSize().width, 140))
.setAddAction(anActionButton -> getModel().addRow(new Object[]{"", ""}))
.setAddAction(anActionButton -> {
getModel().addRow(new Object[]{"", ""});
int lastRowIndex = getModel().getRowCount() - 1;
table.changeSelection(lastRowIndex, 0, false, false);
table.editCellAt(lastRowIndex, 0);
})
.setRemoveAction(anActionButton -> getModel().removeRow(table.getSelectedRow()))
.disableUpAction()
.disableDownAction()

View file

@ -4,6 +4,7 @@ import com.intellij.openapi.Disposable;
import com.intellij.openapi.options.Configurable;
import com.intellij.openapi.util.Disposer;
import ee.carlrobert.codegpt.CodeGPTBundle;
import ee.carlrobert.codegpt.actions.editor.EditorActionsUtil;
import javax.swing.JComponent;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.Nullable;
@ -39,6 +40,7 @@ public class ConfigurationConfigurable implements Configurable {
@Override
public void apply() {
ConfigurationSettings.getInstance().loadState(component.getCurrentFormState());
EditorActionsUtil.refreshActions();
}
@Override

View file

@ -3,12 +3,15 @@ package ee.carlrobert.codegpt.settings.service;
import ee.carlrobert.codegpt.CodeGPTBundle;
public enum ServiceType {
CODEGPT("CODEGPT", "service.codegpt.title", "codegpt.chat.completion"),
OPENAI("OPENAI", "service.openai.title", "chat.completion"),
CUSTOM_OPENAI("CUSTOM_OPENAI", "service.custom.openai.title", "custom.openai.chat.completion"),
ANTHROPIC("ANTHROPIC", "service.anthropic.title", "anthropic.chat.completion"),
AZURE("AZURE", "service.azure.title", "azure.chat.completion"),
GOOGLE("GOOGLE", "service.google.title", "google.chat.completion"),
YOU("YOU", "service.you.title", "you.chat.completion"),
LLAMA_CPP("LLAMA_CPP", "service.llama.title", "llama.chat.completion");
LLAMA_CPP("LLAMA_CPP", "service.llama.title", "llama.chat.completion"),
OLLAMA("OLLAMA", "service.ollama.title", "ollama.chat.completion");
private final String code;
private final String label;

View file

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

View file

@ -1,9 +1,8 @@
package ee.carlrobert.codegpt.settings.service.anthropic;
import static ee.carlrobert.codegpt.credentials.CredentialsStore.CredentialKey.ANTHROPIC_API_KEY;
import static ee.carlrobert.codegpt.ui.UIUtil.withEmptyLeftBorder;
import com.intellij.ui.TitledSeparator;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.ui.components.JBPasswordField;
import com.intellij.ui.components.JBTextField;
import com.intellij.util.ui.FormBuilder;
@ -12,6 +11,7 @@ import ee.carlrobert.codegpt.CodeGPTBundle;
import ee.carlrobert.codegpt.credentials.CredentialsStore;
import ee.carlrobert.codegpt.ui.UIUtil;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import org.jetbrains.annotations.Nullable;
public class AnthropicSettingsForm {
@ -23,15 +23,17 @@ public class AnthropicSettingsForm {
public AnthropicSettingsForm(AnthropicSettingsState settings) {
apiKeyField = new JBPasswordField();
apiKeyField.setColumns(30);
apiKeyField.setText(CredentialsStore.INSTANCE.getCredential(ANTHROPIC_API_KEY));
ApplicationManager.getApplication().executeOnPooledThread(() -> {
var apiKey = CredentialsStore.getCredential(ANTHROPIC_API_KEY);
SwingUtilities.invokeLater(() -> apiKeyField.setText(apiKey));
});
apiVersionField = new JBTextField(settings.getApiVersion(), 35);
modelField = new JBTextField(settings.getModel(), 35);
}
public JPanel getForm() {
return FormBuilder.createFormBuilder()
.addComponent(new TitledSeparator(CodeGPTBundle.get("shared.configuration")))
.addComponent(withEmptyLeftBorder(UI.PanelFactory.grid()
.addComponent(UI.PanelFactory.grid()
.add(UI.PanelFactory.panel(apiKeyField)
.withLabel(CodeGPTBundle.get("settingsConfigurable.shared.apiKey.label"))
.resizeX(false)
@ -49,7 +51,7 @@ public class AnthropicSettingsForm {
.withComment(CodeGPTBundle.get(
"settingsConfigurable.service.anthropic.model.comment"))
.resizeX(false))
.createPanel()))
.createPanel())
.addComponentFillVertically(new JPanel(), 0)
.getPanel();
}
@ -63,7 +65,7 @@ public class AnthropicSettingsForm {
public void resetForm() {
var state = AnthropicSettings.getCurrentState();
apiKeyField.setText(CredentialsStore.INSTANCE.getCredential(ANTHROPIC_API_KEY));
apiKeyField.setText(CredentialsStore.getCredential(ANTHROPIC_API_KEY));
apiVersionField.setText(state.getApiVersion());
modelField.setText(state.getModel());
}

View file

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

View file

@ -2,6 +2,7 @@ package ee.carlrobert.codegpt.settings.service.azure;
import static ee.carlrobert.codegpt.ui.UIUtil.withEmptyLeftBorder;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.ui.TitledSeparator;
import com.intellij.ui.components.JBPasswordField;
import com.intellij.ui.components.JBRadioButton;
@ -15,6 +16,7 @@ import java.util.List;
import java.util.Map;
import javax.swing.ButtonGroup;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import org.jetbrains.annotations.Nullable;
public class AzureSettingsForm {
@ -38,16 +40,20 @@ public class AzureSettingsForm {
settings.isUseAzureActiveDirectoryAuthentication());
azureApiKeyField = new JBPasswordField();
azureApiKeyField.setColumns(30);
azureApiKeyField.setText(
CredentialsStore.INSTANCE.getCredential(CredentialKey.AZURE_OPENAI_API_KEY));
ApplicationManager.getApplication().executeOnPooledThread(() -> {
var apiKey = CredentialsStore.getCredential(CredentialKey.AZURE_OPENAI_API_KEY);
SwingUtilities.invokeLater(() -> azureApiKeyField.setText(apiKey));
});
azureApiKeyFieldPanel = UI.PanelFactory.panel(azureApiKeyField)
.withLabel(CodeGPTBundle.get("settingsConfigurable.shared.apiKey.label"))
.resizeX(false)
.createPanel();
azureActiveDirectoryTokenField = new JBPasswordField();
azureActiveDirectoryTokenField.setColumns(30);
azureActiveDirectoryTokenField.setText(
CredentialsStore.INSTANCE.getCredential(CredentialKey.AZURE_ACTIVE_DIRECTORY_TOKEN));
ApplicationManager.getApplication().executeOnPooledThread(() -> {
var apiKey = CredentialsStore.getCredential(CredentialKey.AZURE_ACTIVE_DIRECTORY_TOKEN);
SwingUtilities.invokeLater(() -> azureActiveDirectoryTokenField.setText(apiKey));
});
azureActiveDirectoryTokenFieldPanel = UI.PanelFactory.panel(azureActiveDirectoryTokenField)
.withLabel(CodeGPTBundle.get("settingsConfigurable.service.azure.bearerToken.label"))
.resizeX(false)
@ -119,10 +125,9 @@ public class AzureSettingsForm {
public void resetForm() {
var state = AzureSettings.getCurrentState();
azureApiKeyField.setText(
CredentialsStore.INSTANCE.getCredential(CredentialKey.AZURE_OPENAI_API_KEY));
azureApiKeyField.setText(CredentialsStore.getCredential(CredentialKey.AZURE_OPENAI_API_KEY));
azureActiveDirectoryTokenField.setText(
CredentialsStore.INSTANCE.getCredential(CredentialKey.AZURE_ACTIVE_DIRECTORY_TOKEN));
CredentialsStore.getCredential(CredentialKey.AZURE_ACTIVE_DIRECTORY_TOKEN));
useAzureApiKeyAuthenticationRadioButton.setSelected(state.isUseAzureApiKeyAuthentication());
useAzureActiveDirectoryAuthenticationRadioButton.setSelected(
state.isUseAzureActiveDirectoryAuthentication());

View file

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

View file

@ -6,6 +6,9 @@ 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.codecompletions.InfillPromptTemplate;
import ee.carlrobert.codegpt.completions.HuggingFaceModel;
import ee.carlrobert.codegpt.completions.llama.LlamaModel;
import ee.carlrobert.codegpt.credentials.CredentialsStore;
import ee.carlrobert.codegpt.settings.service.llama.form.LlamaSettingsForm;
import org.apache.commons.lang3.StringUtils;
@ -25,12 +28,32 @@ public class LlamaSettings implements PersistentStateComponent<LlamaSettingsStat
@Override
public void loadState(@NotNull LlamaSettingsState state) {
this.state = state;
// Catch if model's name has changed which could lead to
// HuggingFaceModel or PromptTemplates not being found
if (this.state.getHuggingFaceModel() == null) {
this.state.setHuggingFaceModel(HuggingFaceModel.CODE_LLAMA_7B_Q4);
}
if (this.state.getRemoteModelInfillPromptTemplate() == null) {
this.state.setRemoteModelInfillPromptTemplate(InfillPromptTemplate.CODE_LLAMA);
}
if (this.state.getLocalModelPromptTemplate() == null) {
this.state.setLocalModelInfillPromptTemplate(InfillPromptTemplate.CODE_LLAMA);
}
}
public static LlamaSettingsState getCurrentState() {
return getInstance().getState();
}
/**
* Code Completions enabled in settings and a model with InfillPromptTemplate selected.
*/
public static boolean isCodeCompletionsPossible() {
return getInstance().getState().isCodeCompletionsEnabled()
&& LlamaModel.findByHuggingFaceModel(getInstance().getState().getHuggingFaceModel())
.getInfillPromptTemplate() != null;
}
public static LlamaSettings getInstance() {
return ApplicationManager.getApplication().getService(LlamaSettings.class);
}
@ -39,6 +62,6 @@ public class LlamaSettings implements PersistentStateComponent<LlamaSettingsStat
return !form.getCurrentState().equals(state)
|| !StringUtils.equals(
form.getLlamaServerPreferencesForm().getApiKey(),
CredentialsStore.INSTANCE.getCredential(LLAMA_API_KEY));
CredentialsStore.getCredential(LLAMA_API_KEY));
}
}

View file

@ -16,8 +16,8 @@ public class LlamaSettingsState {
private HuggingFaceModel huggingFaceModel = HuggingFaceModel.CODE_LLAMA_7B_Q4;
private PromptTemplate localModelPromptTemplate = PromptTemplate.LLAMA;
private PromptTemplate remoteModelPromptTemplate = PromptTemplate.LLAMA;
private InfillPromptTemplate localModelInfillPromptTemplate = InfillPromptTemplate.LLAMA;
private InfillPromptTemplate remoteModelInfillPromptTemplate = InfillPromptTemplate.LLAMA;
private InfillPromptTemplate localModelInfillPromptTemplate = InfillPromptTemplate.CODE_LLAMA;
private InfillPromptTemplate remoteModelInfillPromptTemplate = InfillPromptTemplate.CODE_LLAMA;
private String baseHost = "http://localhost:8080";
private Integer serverPort = getRandomAvailablePortOrDefault();
private int contextSize = 2048;

View file

@ -1,6 +1,7 @@
package ee.carlrobert.codegpt.settings.service.llama.form;
import static java.lang.String.format;
import static java.util.Collections.emptyMap;
import com.intellij.icons.AllIcons.Actions;
import com.intellij.icons.AllIcons.General;
@ -32,6 +33,7 @@ import ee.carlrobert.codegpt.settings.service.llama.LlamaSettings;
import ee.carlrobert.codegpt.settings.service.llama.LlamaSettingsState;
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.io.File;
@ -40,6 +42,8 @@ import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.ButtonGroup;
import javax.swing.DefaultComboBoxModel;
import javax.swing.DefaultListCellRenderer;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import org.jetbrains.annotations.NotNull;
@ -105,7 +109,7 @@ public class LlamaModelPreferencesForm {
huggingFaceComboBoxModel),
BorderLayout.WEST);
modelDetailsLabel = new JBLabel();
huggingFaceModelComboBox = createHuggingFaceComboBox(
huggingFaceModelComboBox = createModelQuantizationComboBox(
huggingFaceComboBoxModel,
modelExistsIcon,
modelDetailsLabel,
@ -291,15 +295,15 @@ public class LlamaModelPreferencesForm {
int parameterSize = model.getParameterSize();
int quantization = model.getQuantization();
if (!modelDetailsMap.containsKey(parameterSize)) {
var details = modelDetailsMap.getOrDefault(parameterSize, emptyMap()).get(quantization);
if (details == null && model.getDownloadSize() == null) {
return "";
}
ModelDetails details = modelDetailsMap.get(parameterSize).get(quantization);
if (details == null) {
return "";
return format("<html>"
+ "<p style=\"margin: 0\"><small>File Size: <strong>%.2f GB</strong></small></p>"
+ "</html>", model.getDownloadSize());
}
return format("<html>"
+ "<p style=\"margin: 0\"><small>File Size: <strong>%.2f GB</strong></small></p>"
+ "<p style=\"margin: 0\"><small>Max RAM Required: <strong>%.2f GB</strong></small></p>"
@ -364,7 +368,7 @@ public class LlamaModelPreferencesForm {
return comboBox;
}
private ComboBox<HuggingFaceModel> createHuggingFaceComboBox(
private ComboBox<HuggingFaceModel> createModelQuantizationComboBox(
DefaultComboBoxModel<HuggingFaceModel> huggingFaceComboBoxModel,
JBLabel modelExistsIcon,
JBLabel modelDetailsLabel,
@ -379,6 +383,17 @@ public class LlamaModelPreferencesForm {
modelExistsIcon.setVisible(modelExists);
downloadModelActionLinkWrapper.setVisible(!modelExists);
});
comboBox.setRenderer(new DefaultListCellRenderer() {
@Override
public Component getListCellRendererComponent(JList list, Object value, int index,
boolean isSelected, boolean cellHasFocus) {
Object item = value;
if (item instanceof HuggingFaceModel) {
item = ((HuggingFaceModel) item).getQuantizationLabel();
}
return super.getListCellRendererComponent(list, item, index, isSelected, cellHasFocus);
}
});
return comboBox;
}

View file

@ -28,6 +28,7 @@ import ee.carlrobert.codegpt.completions.llama.LlamaServerAgent;
import ee.carlrobert.codegpt.completions.llama.LlamaServerStartupParams;
import ee.carlrobert.codegpt.completions.llama.PromptTemplate;
import ee.carlrobert.codegpt.credentials.CredentialsStore;
import ee.carlrobert.codegpt.credentials.CredentialsStore.CredentialKey;
import ee.carlrobert.codegpt.settings.service.llama.LlamaSettingsState;
import ee.carlrobert.codegpt.ui.OverlayUtil;
import ee.carlrobert.codegpt.ui.UIUtil;
@ -40,6 +41,7 @@ import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import org.jetbrains.annotations.Nullable;
public class LlamaServerPreferencesForm {
@ -86,7 +88,10 @@ public class LlamaServerPreferencesForm {
baseHostField = new JBTextField(settings.getBaseHost(), 30);
apiKeyField = new JBPasswordField();
apiKeyField.setColumns(30);
apiKeyField.setText(CredentialsStore.INSTANCE.getCredential(LLAMA_API_KEY));
ApplicationManager.getApplication().executeOnPooledThread(() -> {
var apiKey = CredentialsStore.getCredential(CredentialKey.LLAMA_API_KEY);
SwingUtilities.invokeLater(() -> apiKeyField.setText(apiKey));
});
llamaModelPreferencesForm = new LlamaModelPreferencesForm();
runLocalServerRadioButton = new JBRadioButton("Run local server",
@ -131,7 +136,7 @@ public class LlamaServerPreferencesForm {
additionalBuildParametersField.setText(state.getAdditionalBuildParameters());
remotePromptTemplatePanel.setPromptTemplate(state.getRemoteModelPromptTemplate()); // ?
infillPromptTemplatePanel.setPromptTemplate(state.getRemoteModelInfillPromptTemplate());
apiKeyField.setText(CredentialsStore.INSTANCE.getCredential(LLAMA_API_KEY));
apiKeyField.setText(CredentialsStore.getCredential(LLAMA_API_KEY));
}
public JComponent createUseExistingServerForm() {
@ -189,17 +194,17 @@ public class LlamaServerPreferencesForm {
createComment("settingsConfigurable.service.llama.threads.comment"))
.addLabeledComponent(
CodeGPTBundle.get("settingsConfigurable.service.llama.additionalParameters.label"),
additionalParametersField)
.addComponentToRightColumn(
createComment(
"settingsConfigurable.service.llama.additionalParameters.comment"))
.addLabeledComponent(
CodeGPTBundle.get(
"settingsConfigurable.service.llama.additionalBuildParameters.label"),
additionalBuildParametersField)
.addComponentToRightColumn(
createComment(
"settingsConfigurable.service.llama.additionalBuildParameters.comment"))
additionalParametersField)
.addComponentToRightColumn(
createComment(
"settingsConfigurable.service.llama.additionalParameters.comment"))
.addLabeledComponent(
CodeGPTBundle.get(
"settingsConfigurable.service.llama.additionalBuildParameters.label"),
additionalBuildParametersField)
.addComponentToRightColumn(
createComment(
"settingsConfigurable.service.llama.additionalBuildParameters.comment"))
.addVerticalGap(4)
.addComponentFillVertically(new JPanel(), 0)
.getPanel()))
@ -354,9 +359,9 @@ public class LlamaServerPreferencesForm {
public List<String> getListOfAdditionalParameters() {
return Arrays.stream(additionalParametersField.getText().split(","))
.map(String::trim)
.filter(s -> !s.isBlank())
.toList();
.map(String::trim)
.filter(s -> !s.isBlank())
.toList();
}
public String getAdditionalBuildParameters() {
@ -365,9 +370,9 @@ public class LlamaServerPreferencesForm {
public List<String> getListOfAdditionalBuildParameters() {
return Arrays.stream(additionalBuildParametersField.getText().split(","))
.map(String::trim)
.filter(s -> !s.isBlank())
.toList();
.map(String::trim)
.filter(s -> !s.isBlank())
.toList();
}
public PromptTemplate getPromptTemplate() {

View file

@ -22,7 +22,8 @@ public class LlamaSettingsForm extends JPanel {
llamaRequestPreferencesForm = new LlamaRequestPreferencesForm(settings);
codeCompletionConfigurationForm = new CodeCompletionConfigurationForm(
settings.isCodeCompletionsEnabled(),
settings.getCodeCompletionMaxTokens());
settings.getCodeCompletionMaxTokens(),
null);
init();
}

View file

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

View file

@ -3,6 +3,7 @@ package ee.carlrobert.codegpt.settings.service.openai;
import static ee.carlrobert.codegpt.credentials.CredentialsStore.CredentialKey.OPENAI_API_KEY;
import static ee.carlrobert.codegpt.ui.UIUtil.withEmptyLeftBorder;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.ui.ComboBox;
import com.intellij.ui.EnumComboBoxModel;
import com.intellij.ui.TitledSeparator;
@ -12,10 +13,12 @@ import com.intellij.util.ui.FormBuilder;
import com.intellij.util.ui.UI;
import ee.carlrobert.codegpt.CodeGPTBundle;
import ee.carlrobert.codegpt.credentials.CredentialsStore;
import ee.carlrobert.codegpt.credentials.CredentialsStore.CredentialKey;
import ee.carlrobert.codegpt.settings.service.CodeCompletionConfigurationForm;
import ee.carlrobert.codegpt.ui.UIUtil;
import ee.carlrobert.llm.client.openai.completion.OpenAIChatCompletionModel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import org.jetbrains.annotations.Nullable;
public class OpenAISettingsForm {
@ -28,7 +31,10 @@ public class OpenAISettingsForm {
public OpenAISettingsForm(OpenAISettingsState settings) {
apiKeyField = new JBPasswordField();
apiKeyField.setColumns(30);
apiKeyField.setText(CredentialsStore.INSTANCE.getCredential(OPENAI_API_KEY));
ApplicationManager.getApplication().executeOnPooledThread(() -> {
var apiKey = CredentialsStore.getCredential(CredentialKey.OPENAI_API_KEY);
SwingUtilities.invokeLater(() -> apiKeyField.setText(apiKey));
});
organizationField = new JBTextField(settings.getOrganization(), 30);
completionModelComboBox = new ComboBox<>(
new EnumComboBoxModel<>(OpenAIChatCompletionModel.class));
@ -36,7 +42,8 @@ public class OpenAISettingsForm {
OpenAIChatCompletionModel.findByCode(settings.getModel()));
codeCompletionConfigurationForm = new CodeCompletionConfigurationForm(
settings.isCodeCompletionsEnabled(),
settings.getCodeCompletionMaxTokens());
settings.getCodeCompletionMaxTokens(),
null);
}
public JPanel getForm() {
@ -57,10 +64,10 @@ public class OpenAISettingsForm {
.createPanel();
return FormBuilder.createFormBuilder()
.addComponent(new TitledSeparator(CodeGPTBundle.get("shared.codeCompletions")))
.addComponent(withEmptyLeftBorder(codeCompletionConfigurationForm.getForm()))
.addComponent(new TitledSeparator(CodeGPTBundle.get("shared.configuration")))
.addComponent(withEmptyLeftBorder(configurationGrid))
.addComponent(new TitledSeparator(CodeGPTBundle.get("shared.codeCompletions")))
.addComponent(withEmptyLeftBorder(codeCompletionConfigurationForm.getForm()))
.addComponentFillVertically(new JPanel(), 0)
.getPanel();
}
@ -87,7 +94,7 @@ public class OpenAISettingsForm {
public void resetForm() {
var state = OpenAISettings.getCurrentState();
apiKeyField.setText(CredentialsStore.INSTANCE.getCredential(OPENAI_API_KEY));
apiKeyField.setText(CredentialsStore.getCredential(OPENAI_API_KEY));
completionModelComboBox.setSelectedItem(
OpenAIChatCompletionModel.findByCode(state.getModel()));
organizationField.setText(state.getOrganization());

View file

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

View file

@ -1,9 +1,9 @@
package ee.carlrobert.codegpt.settings.service.you;
import static ee.carlrobert.codegpt.credentials.CredentialsStore.CredentialKey.YOU_ACCOUNT_PASSWORD;
import static ee.carlrobert.codegpt.ui.UIUtil.withEmptyLeftBorder;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.ui.ComponentValidator;
import com.intellij.openapi.ui.ValidationInfo;
import com.intellij.openapi.util.text.StringUtil;
@ -53,7 +53,10 @@ public class YouSettingsForm extends JPanel {
passwordField = new JBPasswordField();
passwordField.setColumns(25);
if (!settings.getEmail().isEmpty()) {
passwordField.setText(CredentialsStore.INSTANCE.getCredential(YOU_ACCOUNT_PASSWORD));
ApplicationManager.getApplication().executeOnPooledThread(() -> {
var apiKey = CredentialsStore.getCredential(CredentialKey.YOU_ACCOUNT_PASSWORD);
SwingUtilities.invokeLater(() -> passwordField.setText(apiKey));
});
}
signInButton = new JButton(CodeGPTBundle.get("settingsConfigurable.service.you.signIn.label"));
signUpTextPane = createSignUpTextPane();

View file

@ -2,6 +2,13 @@ package ee.carlrobert.codegpt.toolwindow.chat.ui;
import com.intellij.openapi.roots.ui.componentsList.components.ScrollablePanel;
import com.intellij.openapi.roots.ui.componentsList.layout.VerticalStackLayout;
import com.intellij.ui.JBColor;
import com.intellij.util.ui.JBUI;
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.ui.UIUtil;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
@ -21,6 +28,22 @@ public class ChatToolWindowScrollablePanel extends ScrollablePanel {
public void displayLandingView(JComponent landingView) {
clearAll();
add(landingView);
if (GeneralSettings.getCurrentState().getSelectedService() == ServiceType.CODEGPT
&& !CredentialsStore.INSTANCE.isCredentialSet(CredentialKey.CODEGPT_API_KEY)) {
var panel = new ResponsePanel()
.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="https://codegpt.carlrobert.ee/account">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.
</p>
</html>""", false, UIUtil::handleHyperlinkClicked));
panel.setBorder(JBUI.Borders.customLine(JBColor.border(), 1, 0, 0, 0));
add(panel);
}
}
public ResponsePanel getMessageResponsePanel(UUID messageId) {

View file

@ -1,8 +1,11 @@
package ee.carlrobert.codegpt.toolwindow.chat.ui.textarea;
import static ee.carlrobert.codegpt.settings.service.ServiceType.CODEGPT;
import static ee.carlrobert.codegpt.settings.service.ServiceType.CUSTOM_OPENAI;
import static ee.carlrobert.codegpt.settings.service.ServiceType.OLLAMA;
import static ee.carlrobert.codegpt.settings.service.ServiceType.OPENAI;
import static ee.carlrobert.codegpt.settings.service.ServiceType.YOU;
import static ee.carlrobert.llm.client.codegpt.CodeGPTAvailableModels.AVAILABLE_CHAT_MODELS;
import static java.lang.String.format;
import com.intellij.openapi.actionSystem.ActionUpdateThread;
@ -18,15 +21,17 @@ 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.GeneralSettingsState;
import ee.carlrobert.codegpt.settings.service.ServiceType;
import ee.carlrobert.codegpt.settings.service.codegpt.CodeGPTServiceSettings;
import ee.carlrobert.codegpt.settings.service.custom.CustomServiceSettings;
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.settings.service.openai.OpenAISettingsState;
import ee.carlrobert.codegpt.settings.service.you.YouSettings;
import ee.carlrobert.codegpt.settings.service.you.YouSettingsState;
import ee.carlrobert.llm.client.codegpt.CodeGPTModel;
import ee.carlrobert.llm.client.openai.completion.OpenAIChatCompletionModel;
import ee.carlrobert.llm.client.you.completion.YouCompletionCustomModel;
import ee.carlrobert.llm.client.you.completion.YouCompletionMode;
@ -38,15 +43,9 @@ import org.jetbrains.annotations.NotNull;
public class ModelComboBoxAction extends ComboBoxAction {
private final Runnable onModelChange;
private final GeneralSettingsState settings;
private final OpenAISettingsState openAISettings;
private final YouSettingsState youSettings;
public ModelComboBoxAction(Runnable onModelChange, ServiceType selectedService) {
this.onModelChange = onModelChange;
settings = GeneralSettings.getCurrentState();
openAISettings = OpenAISettings.getCurrentState();
youSettings = YouSettings.getCurrentState();
updateTemplatePresentation(selectedService);
subscribeToYouSignedOutTopic(ApplicationManager.getApplication().getMessageBus().connect());
@ -66,18 +65,28 @@ public class ModelComboBoxAction extends ComboBoxAction {
return button;
}
private AnAction[] getCodeGPTModelActions(Presentation presentation) {
var apiKey = CredentialsStore.getCredential(CredentialKey.CODEGPT_API_KEY);
return AVAILABLE_CHAT_MODELS.stream()
.map(model -> {
var enabled = "codellama/CodeLlama-13b-Instruct-hf".equals(model.getCode())
|| (apiKey != null && !apiKey.isEmpty());
return createCodeGPTModelAction(model, enabled, presentation);
})
.toArray(AnAction[]::new);
}
@Override
protected @NotNull DefaultActionGroup createPopupActionGroup(JComponent button) {
var presentation = ((ComboBoxButton) button).getPresentation();
var actionGroup = new DefaultActionGroup();
actionGroup.addSeparator("CodeGPT");
actionGroup.addAll(getCodeGPTModelActions(presentation));
actionGroup.addSeparator("OpenAI");
List.of(
OpenAIChatCompletionModel.GPT_4_VISION_PREVIEW,
OpenAIChatCompletionModel.GPT_4_0125_128k,
OpenAIChatCompletionModel.GPT_3_5_0125_16k,
OpenAIChatCompletionModel.GPT_4_32k,
OpenAIChatCompletionModel.GPT_4,
OpenAIChatCompletionModel.GPT_3_5)
OpenAIChatCompletionModel.GPT_3_5_0125_16k)
.forEach(model -> actionGroup.add(createOpenAIModelAction(model, presentation)));
actionGroup.addSeparator("Custom OpenAI Service");
actionGroup.add(createModelAction(
@ -103,6 +112,19 @@ public class ModelComboBoxAction extends ComboBoxAction {
getLlamaCppPresentationText(),
Icons.Llama,
presentation));
actionGroup.addSeparator("Ollama");
ApplicationManager.getApplication()
.getService(OllamaSettings.class)
.getState()
.getAvailableModels()
.forEach(model ->
actionGroup.add(createOllamaModelAction(model, presentation)));
actionGroup.addSeparator();
actionGroup.add(createModelAction(
ServiceType.GOOGLE,
"Google (Gemini)",
Icons.Google,
presentation));
if (YouUserManager.getInstance().isSubscribed()) {
actionGroup.addSeparator("You.com");
@ -144,20 +166,33 @@ public class ModelComboBoxAction extends ComboBoxAction {
}
private void updateTemplatePresentation(ServiceType selectedService) {
var application = ApplicationManager.getApplication();
var templatePresentation = getTemplatePresentation();
switch (selectedService) {
case CODEGPT:
var model = application.getService(CodeGPTServiceSettings.class)
.getState()
.getChatCompletionSettings()
.getModel();
var modelName = AVAILABLE_CHAT_MODELS.stream()
.filter(it -> it.getCode().equals(model))
.map(CodeGPTModel::getName)
.findFirst().orElse("Unknown");
templatePresentation.setIcon(Icons.CodeGPTModel);
templatePresentation.setText(modelName);
break;
case OPENAI:
templatePresentation.setIcon(Icons.OpenAI);
templatePresentation.setText(
OpenAIChatCompletionModel.findByCode(openAISettings.getModel()).getDescription());
OpenAIChatCompletionModel.findByCode(OpenAISettings.getCurrentState().getModel())
.getDescription());
break;
case CUSTOM_OPENAI:
templatePresentation.setIcon(Icons.OpenAI);
templatePresentation.setText(
ApplicationManager.getApplication().getService(CustomServiceSettings.class)
.getState()
.getTemplate()
.getProviderName());
templatePresentation.setText(application.getService(CustomServiceSettings.class)
.getState()
.getTemplate()
.getProviderName());
break;
case ANTHROPIC:
templatePresentation.setIcon(Icons.Anthropic);
@ -168,18 +203,30 @@ public class ModelComboBoxAction extends ComboBoxAction {
templatePresentation.setText("Azure OpenAI");
break;
case YOU:
var settings = YouSettings.getCurrentState();
templatePresentation.setIcon(Icons.YouSmall);
templatePresentation.setText(
youSettings.getChatMode() == YouCompletionMode.CUSTOM
? youSettings.getCustomModel().getDescription()
: youSettings.getChatMode().getDescription()
settings.getChatMode() == YouCompletionMode.CUSTOM
? settings.getCustomModel().getDescription()
: settings.getChatMode().getDescription()
);
break;
case LLAMA_CPP:
templatePresentation.setText(getLlamaCppPresentationText());
templatePresentation.setIcon(Icons.Llama);
break;
case OLLAMA:
templatePresentation.setIcon(Icons.Ollama);
templatePresentation.setText(application.getService(OllamaSettings.class)
.getState()
.getModel());
break;
case GOOGLE:
templatePresentation.setText("Google (Gemini)");
templatePresentation.setIcon(Icons.Google);
break;
default:
break;
}
}
@ -229,12 +276,73 @@ public class ModelComboBoxAction extends ComboBoxAction {
String label,
Icon icon,
Presentation comboBoxPresentation) {
settings.setSelectedService(serviceType);
GeneralSettings.getCurrentState().setSelectedService(serviceType);
comboBoxPresentation.setIcon(icon);
comboBoxPresentation.setText(label);
onModelChange.run();
}
private AnAction createCodeGPTModelAction(CodeGPTModel model, boolean enabled,
Presentation comboBoxPresentation) {
return new DumbAwareAction(model.getName(), "", Icons.CodeGPTModel) {
@Override
public void update(@NotNull AnActionEvent event) {
var presentation = event.getPresentation();
presentation.setEnabled(
enabled && !presentation.getText().equals(comboBoxPresentation.getText()));
}
@Override
public void actionPerformed(@NotNull AnActionEvent e) {
ApplicationManager.getApplication().getService(CodeGPTServiceSettings.class)
.getState()
.getChatCompletionSettings()
.setModel(model.getCode());
handleModelChange(
CODEGPT,
model.getName(),
Icons.OpenAI,
comboBoxPresentation);
}
@Override
public @NotNull ActionUpdateThread getActionUpdateThread() {
return ActionUpdateThread.BGT;
}
};
}
private AnAction createOllamaModelAction(
String model,
Presentation comboBoxPresentation
) {
return new DumbAwareAction(model, "", Icons.Ollama) {
@Override
public void update(@NotNull AnActionEvent event) {
var presentation = event.getPresentation();
presentation.setEnabled(!presentation.getText().equals(comboBoxPresentation.getText()));
}
@Override
public void actionPerformed(@NotNull AnActionEvent e) {
ApplicationManager.getApplication()
.getService(OllamaSettings.class)
.getState()
.setModel(model);
handleModelChange(
OLLAMA,
model,
Icons.Ollama,
comboBoxPresentation);
}
@Override
public @NotNull ActionUpdateThread getActionUpdateThread() {
return ActionUpdateThread.BGT;
}
};
}
private AnAction createOpenAIModelAction(
OpenAIChatCompletionModel model,
Presentation comboBoxPresentation) {
@ -249,7 +357,7 @@ public class ModelComboBoxAction extends ComboBoxAction {
@Override
public void actionPerformed(@NotNull AnActionEvent e) {
openAISettings.setModel(model.getCode());
OpenAISettings.getCurrentState().setModel(model.getCode());
handleModelChange(
OPENAI,
model.getDescription(),
@ -278,7 +386,7 @@ public class ModelComboBoxAction extends ComboBoxAction {
@Override
public void actionPerformed(@NotNull AnActionEvent e) {
youSettings.setChatMode(mode);
YouSettings.getCurrentState().setChatMode(mode);
handleModelChange(
YOU,
mode.getDescription(),
@ -307,8 +415,9 @@ public class ModelComboBoxAction extends ComboBoxAction {
@Override
public void actionPerformed(@NotNull AnActionEvent e) {
youSettings.setCustomModel(model);
youSettings.setChatMode(YouCompletionMode.CUSTOM);
var settings = YouSettings.getCurrentState();
settings.setCustomModel(model);
settings.setChatMode(YouCompletionMode.CUSTOM);
handleModelChange(
YOU,
model.getDescription(),

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.OLLAMA;
import static ee.carlrobert.codegpt.settings.service.ServiceType.OPENAI;
import static ee.carlrobert.llm.client.openai.completion.OpenAIChatCompletionModel.GPT_4_VISION_PREVIEW;
@ -192,6 +193,7 @@ public class UserPromptTextArea extends JPanel {
}));
var selectedService = GeneralSettings.getCurrentState().getSelectedService();
if (selectedService == ANTHROPIC
|| selectedService == OLLAMA
|| (selectedService == OPENAI
&& GPT_4_VISION_PREVIEW.getCode().equals(OpenAISettings.getCurrentState().getModel()))) {
iconsPanel.add(new IconActionButton(new AttachImageAction()));

View file

@ -27,6 +27,9 @@ public class ModelIconLabel extends JBLabel {
if ("llama.chat.completion".equals(clientCode)) {
setIcon(Icons.Llama);
}
if ("google.chat.completion".equals(clientCode)) {
setIcon(Icons.Google);
}
setText(formatModelName(modelCode));
setFont(JBFont.small());
setHorizontalAlignment(SwingConstants.LEADING);

View file

@ -29,7 +29,6 @@ class CodeGPTProjectActivity : ProjectActivity {
override suspend fun execute(project: Project) {
EditorActionsUtil.refreshActions()
CredentialsStore.loadAll()
if (YouUserManager.getInstance().authenticationResponse == null) {
handleYouServiceAuthenticationAsync()

View file

@ -4,57 +4,69 @@ import com.intellij.openapi.actionSystem.ActionUpdateThread
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.components.service
import com.intellij.openapi.project.DumbAwareAction
import ee.carlrobert.codegpt.codecompletions.CodeCompletionService
import ee.carlrobert.codegpt.settings.GeneralSettings
import ee.carlrobert.codegpt.settings.service.ServiceType
import ee.carlrobert.codegpt.settings.service.ServiceType.*
import ee.carlrobert.codegpt.settings.service.codegpt.CodeGPTServiceSettings
import ee.carlrobert.codegpt.settings.service.custom.CustomServiceSettings
import ee.carlrobert.codegpt.settings.service.llama.LlamaSettings
import ee.carlrobert.codegpt.settings.service.ollama.OllamaSettings
import ee.carlrobert.codegpt.settings.service.openai.OpenAISettings
abstract class CodeCompletionFeatureToggleActions(
private val enableFeatureAction: Boolean
) : DumbAwareAction() {
override fun actionPerformed(e: AnActionEvent) {
GeneralSettings.getCurrentState().selectedService
.takeIf { it in listOf(OPENAI, CUSTOM_OPENAI, LLAMA_CPP) }
?.also { selectedService ->
if (OPENAI == selectedService) {
OpenAISettings.getCurrentState().isCodeCompletionsEnabled = enableFeatureAction
} else if (CUSTOM_OPENAI == selectedService) {
service<CustomServiceSettings>().state.codeCompletionSettings.codeCompletionsEnabled =
enableFeatureAction
} else {
LlamaSettings.getCurrentState().isCodeCompletionsEnabled = enableFeatureAction
}
when (GeneralSettings.getCurrentState().selectedService) {
CODEGPT ->
service<CodeGPTServiceSettings>().state.codeCompletionSettings.codeCompletionsEnabled
OPENAI ->
OpenAISettings.getCurrentState().isCodeCompletionsEnabled = enableFeatureAction
LLAMA_CPP ->
LlamaSettings.getCurrentState().isCodeCompletionsEnabled = enableFeatureAction
OLLAMA -> service<OllamaSettings>().state.codeCompletionsEnabled = enableFeatureAction
CUSTOM_OPENAI -> service<CustomServiceSettings>().state
.codeCompletionSettings
.codeCompletionsEnabled = enableFeatureAction
ANTHROPIC,
AZURE,
YOU,
GOOGLE,
null -> { /* no-op for these services */
}
}
}
override fun update(e: AnActionEvent) {
val selectedService = GeneralSettings.getCurrentState().selectedService
val codeCompletionEnabled = isCodeCompletionsEnabled(selectedService)
e.presentation.isEnabled = codeCompletionEnabled != enableFeatureAction
e.presentation.isVisible =
e.presentation.isEnabled && listOf(OPENAI, CUSTOM_OPENAI, LLAMA_CPP).contains(
selectedService
)
val codeCompletionEnabled =
service<CodeCompletionService>().isCodeCompletionsEnabled(selectedService)
e.presentation.isVisible = codeCompletionEnabled != enableFeatureAction
e.presentation.isEnabled = when (selectedService) {
CODEGPT,
OPENAI,
CUSTOM_OPENAI,
LLAMA_CPP,
OLLAMA -> true
ANTHROPIC,
AZURE,
YOU,
GOOGLE,
null -> false
}
}
override fun getActionUpdateThread(): ActionUpdateThread {
return ActionUpdateThread.BGT
}
private fun isCodeCompletionsEnabled(serviceType: ServiceType): Boolean {
return when (serviceType) {
OPENAI -> OpenAISettings.getCurrentState().isCodeCompletionsEnabled
CUSTOM_OPENAI -> service<CustomServiceSettings>().state.codeCompletionSettings.codeCompletionsEnabled
LLAMA_CPP -> LlamaSettings.getCurrentState().isCodeCompletionsEnabled
else -> false
}
}
}
class EnableCompletionsAction : CodeCompletionFeatureToggleActions(true)
class DisableCompletionsAction : CodeCompletionFeatureToggleActions(false)
class DisableCompletionsAction : CodeCompletionFeatureToggleActions(false)

View file

@ -7,11 +7,15 @@ import ee.carlrobert.codegpt.completions.llama.LlamaModel
import ee.carlrobert.codegpt.credentials.CredentialsStore.CredentialKey
import ee.carlrobert.codegpt.credentials.CredentialsStore.getCredential
import ee.carlrobert.codegpt.settings.configuration.Placeholder
import ee.carlrobert.codegpt.settings.service.codegpt.CodeGPTServiceSettings
import ee.carlrobert.codegpt.settings.service.custom.CustomServiceSettings
import ee.carlrobert.codegpt.settings.service.llama.LlamaSettings
import ee.carlrobert.codegpt.settings.service.llama.LlamaSettingsState
import ee.carlrobert.codegpt.settings.service.ollama.OllamaSettings
import ee.carlrobert.codegpt.settings.service.openai.OpenAISettings
import ee.carlrobert.llm.client.llama.completion.LlamaCompletionRequest
import ee.carlrobert.llm.client.ollama.completion.request.OllamaCompletionRequest
import ee.carlrobert.llm.client.ollama.completion.request.OllamaParameters
import ee.carlrobert.llm.client.openai.completion.request.OpenAITextCompletionRequest
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.Request
@ -20,6 +24,18 @@ import java.nio.charset.StandardCharsets
object CodeCompletionRequestFactory {
@JvmStatic
fun buildCodeGPTRequest(details: InfillRequestDetails): OpenAITextCompletionRequest {
val settings = service<CodeGPTServiceSettings>().state.codeCompletionSettings
return OpenAITextCompletionRequest.Builder(details.prefix)
.setSuffix(details.suffix)
.setStream(true)
.setModel(settings.model)
.setMaxTokens(settings.maxTokens)
.setTemperature(0.4)
.build()
}
@JvmStatic
fun buildOpenAIRequest(details: InfillRequestDetails): OpenAITextCompletionRequest {
return OpenAITextCompletionRequest.Builder(details.prefix)
@ -33,17 +49,36 @@ object CodeCompletionRequestFactory {
@JvmStatic
fun buildCustomRequest(details: InfillRequestDetails): Request {
val settings = service<CustomServiceSettings>().state.codeCompletionSettings
val requestBuilder = Request.Builder().url(settings.url!!)
val credential = getCredential(CredentialKey.CUSTOM_SERVICE_API_KEY)
for (entry in settings.headers.entries) {
return buildCustomRequest(
details,
settings.url!!,
settings.headers,
settings.body,
settings.infillTemplate,
credential
)
}
@JvmStatic
fun buildCustomRequest(
details: InfillRequestDetails,
url: String,
headers: Map<String, String>,
body: Map<String, Any>,
infillTemplate: InfillPromptTemplate,
credential: String?
): Request {
val requestBuilder = Request.Builder().url(url)
for (entry in headers.entries) {
var value = entry.value
if (credential != null && value.contains("\$CUSTOM_SERVICE_API_KEY")) {
value = value.replace("\$CUSTOM_SERVICE_API_KEY", credential)
}
requestBuilder.addHeader(entry.key, value)
}
val transformedBody = settings.body.entries.associate { (key, value) ->
key to transformValue(value, settings.infillTemplate, details)
val transformedBody = body.entries.associate { (key, value) ->
key to transformValue(value, infillTemplate, details)
}
try {
@ -71,6 +106,22 @@ object CodeCompletionRequestFactory {
.build()
}
fun buildOllamaRequest(details: InfillRequestDetails): OllamaCompletionRequest {
val settings = service<OllamaSettings>().state
return OllamaCompletionRequest.Builder(
settings.model,
settings.fimTemplate.buildPrompt(details.prefix, details.suffix)
)
.setOptions(
OllamaParameters.Builder()
.stop(settings.fimTemplate.stopTokens)
.numPredict(settings.codeCompletionMaxTokens)
.build()
)
.setRaw(true)
.build()
}
private fun getLlamaInfillPromptTemplate(settings: LlamaSettingsState): InfillPromptTemplate {
if (!settings.isRunLocalServer) {
return settings.remoteModelInfillPromptTemplate
@ -94,4 +145,4 @@ object CodeCompletionRequestFactory {
else -> value
}
}
}
}

View file

@ -0,0 +1,59 @@
package ee.carlrobert.codegpt.codecompletions
import com.intellij.openapi.components.Service
import com.intellij.openapi.components.service
import ee.carlrobert.codegpt.codecompletions.CodeCompletionRequestFactory.buildCodeGPTRequest
import ee.carlrobert.codegpt.codecompletions.CodeCompletionRequestFactory.buildCustomRequest
import ee.carlrobert.codegpt.codecompletions.CodeCompletionRequestFactory.buildLlamaRequest
import ee.carlrobert.codegpt.codecompletions.CodeCompletionRequestFactory.buildOpenAIRequest
import ee.carlrobert.codegpt.completions.CompletionClientProvider
import ee.carlrobert.codegpt.settings.GeneralSettings
import ee.carlrobert.codegpt.settings.service.ServiceType
import ee.carlrobert.codegpt.settings.service.ServiceType.*
import ee.carlrobert.codegpt.settings.service.codegpt.CodeGPTServiceSettings
import ee.carlrobert.codegpt.settings.service.custom.CustomServiceSettings
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.llm.client.openai.completion.OpenAITextCompletionEventSourceListener
import ee.carlrobert.llm.completion.CompletionEventListener
import okhttp3.sse.EventSource
import okhttp3.sse.EventSources.createFactory
@Service(Service.Level.PROJECT)
class CodeCompletionService {
fun isCodeCompletionsEnabled(selectedService: ServiceType): Boolean =
when (selectedService) {
CODEGPT -> service<CodeGPTServiceSettings>().state.codeCompletionSettings.codeCompletionsEnabled
OPENAI -> OpenAISettings.getCurrentState().isCodeCompletionsEnabled
CUSTOM_OPENAI -> service<CustomServiceSettings>().state.codeCompletionSettings.codeCompletionsEnabled
LLAMA_CPP -> LlamaSettings.isCodeCompletionsPossible()
OLLAMA -> service<OllamaSettings>().state.codeCompletionsEnabled
else -> false
}
fun getCodeCompletionAsync(
requestDetails: InfillRequestDetails,
eventListener: CompletionEventListener<String>
): EventSource =
when (val selectedService = GeneralSettings.getCurrentState().selectedService) {
CODEGPT -> CompletionClientProvider.getCodeGPTClient()
.getCompletionAsync(buildCodeGPTRequest(requestDetails), eventListener)
OPENAI -> CompletionClientProvider.getOpenAIClient()
.getCompletionAsync(buildOpenAIRequest(requestDetails), eventListener)
CUSTOM_OPENAI -> createFactory(
CompletionClientProvider.getDefaultClientBuilder().build()
).newEventSource(
buildCustomRequest(requestDetails),
OpenAITextCompletionEventSourceListener(eventListener)
)
LLAMA_CPP -> CompletionClientProvider.getLlamaClient()
.getChatCompletionAsync(buildLlamaRequest(requestDetails), eventListener)
else -> throw IllegalArgumentException("Code completion not supported for ${selectedService.name}")
}
}

View file

@ -16,11 +16,12 @@ import com.intellij.openapi.application.EDT
import com.intellij.openapi.components.service
import com.intellij.openapi.diagnostic.thisLogger
import ee.carlrobert.codegpt.CodeGPTKeys
import ee.carlrobert.codegpt.completions.CompletionRequestService
import ee.carlrobert.codegpt.settings.GeneralSettings
import ee.carlrobert.codegpt.settings.service.ServiceType
import ee.carlrobert.codegpt.settings.service.codegpt.CodeGPTServiceSettings
import ee.carlrobert.codegpt.settings.service.custom.CustomServiceSettings
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.ui.OverlayUtil.showNotification
import ee.carlrobert.llm.client.openai.completion.ErrorDetails
@ -48,7 +49,8 @@ class CodeGPTInlineCompletionProvider : InlineCompletionProvider {
get() = CodeCompletionSuggestionUpdateAdapter()
override suspend fun getSuggestion(request: InlineCompletionRequest): InlineCompletionSingleSuggestion {
if (request.editor.project == null) {
val project = request.editor.project
if (project == null) {
logger.error("Could not find project")
return InlineCompletionSingleSuggestion.build(elements = emptyFlow())
}
@ -58,7 +60,7 @@ class CodeGPTInlineCompletionProvider : InlineCompletionProvider {
InfillRequestDetails.fromInlineCompletionRequest(request)
}
currentCall.set(
CompletionRequestService.getInstance().getCodeCompletionAsync(
project.service<CodeCompletionService>().getCodeCompletionAsync(
infillRequest,
CodeCompletionEventListener {
val inlineText = it.takeWhile { message -> message != '\n' }.toString()
@ -80,10 +82,16 @@ class CodeGPTInlineCompletionProvider : InlineCompletionProvider {
override fun isEnabled(event: InlineCompletionEvent): Boolean {
val selectedService = GeneralSettings.getCurrentState().selectedService
val codeCompletionsEnabled = when (selectedService) {
ServiceType.CODEGPT -> service<CodeGPTServiceSettings>().state.codeCompletionSettings.codeCompletionsEnabled
ServiceType.OPENAI -> OpenAISettings.getCurrentState().isCodeCompletionsEnabled
ServiceType.CUSTOM_OPENAI -> service<CustomServiceSettings>().state.codeCompletionSettings.codeCompletionsEnabled
ServiceType.LLAMA_CPP -> LlamaSettings.getCurrentState().isCodeCompletionsEnabled
else -> false
ServiceType.OLLAMA -> service<OllamaSettings>().state.codeCompletionsEnabled
ServiceType.ANTHROPIC,
ServiceType.AZURE,
ServiceType.YOU,
ServiceType.GOOGLE,
null -> false
}
return event is InlineCompletionEvent.DocumentChange && codeCompletionsEnabled
}

View file

@ -7,11 +7,24 @@ enum class InfillPromptTemplate(val label: String, val stopTokens: List<String>?
return "<|fim_prefix|> $prefix <|fim_suffix|>$suffix <|fim_middle|>"
}
},
LLAMA("Llama", listOf("<EOT>")) {
CODE_LLAMA("Code Llama", listOf("<EOT>")) {
override fun buildPrompt(prefix: String, suffix: String): String {
return "<PRE> $prefix <SUF>$suffix <MID>"
}
},
CODE_GEMMA(
"CodeGemma Instruct",
listOf("<|file_separator|>", "<|fim_prefix|>", "<|fim_suffix|>", "<|fim_middle|>", "<eos>")
) {
override fun buildPrompt(prefix: String, suffix: String): String {
return "<|fim_prefix|>$prefix<|fim_suffix|>$suffix<|fim_middle|>"
}
},
CODE_QWEN("CodeQwen1.5", listOf("<|endoftext|>")) {
override fun buildPrompt(prefix: String, suffix: String): String {
return "<fim_prefix>$prefix<fim_suffix>$suffix<fim_middle>"
}
},
STABILITY("Stability AI", listOf("<|endoftext|>")) {
override fun buildPrompt(prefix: String, suffix: String): String {
return "<fim_prefix>$prefix<fim_suffix>$suffix<fim_middle>"

View file

@ -3,30 +3,27 @@ package ee.carlrobert.codegpt.credentials
import com.intellij.credentialStore.CredentialAttributes
import com.intellij.credentialStore.generateServiceName
import com.intellij.ide.passwordSafe.PasswordSafe
import com.intellij.util.concurrency.annotations.RequiresBackgroundThread
object CredentialsStore {
private val credentialsMap = mutableMapOf<CredentialKey, String?>()
fun loadAll() {
CredentialKey.values().forEach {
val credentialAttributes = CredentialAttributes(generateServiceName("CodeGPT", it.name))
val password = PasswordSafe.instance.getPassword(credentialAttributes)
// Avoid calling setCredential here since it will persist
// the password back into the PasswordSafe unnecessarily.
credentialsMap[it] = password
}
@JvmStatic
@RequiresBackgroundThread
fun getCredential(key: CredentialKey): String? = credentialsMap.getOrPut(key) {
PasswordSafe.instance.getPassword(
CredentialAttributes(generateServiceName("CodeGPT", key.name))
) ?: ""
}
fun getCredential(key: CredentialKey): String? = credentialsMap[key]
fun setCredential(key: CredentialKey, password: String?) {
val prevPassword = credentialsMap[key]
credentialsMap[key] = password
if (prevPassword != password) {
val credentialAttributes = CredentialAttributes(generateServiceName("CodeGPT", key.name))
val credentialAttributes =
CredentialAttributes(generateServiceName("CodeGPT", key.name))
PasswordSafe.instance.setPassword(credentialAttributes, password)
}
}
@ -34,12 +31,14 @@ object CredentialsStore {
fun isCredentialSet(key: CredentialKey): Boolean = !getCredential(key).isNullOrEmpty()
enum class CredentialKey {
CODEGPT_API_KEY,
OPENAI_API_KEY,
CUSTOM_SERVICE_API_KEY,
ANTHROPIC_API_KEY,
AZURE_OPENAI_API_KEY,
AZURE_ACTIVE_DIRECTORY_TOKEN,
YOU_ACCOUNT_PASSWORD,
LLAMA_API_KEY
LLAMA_API_KEY,
GOOGLE_API_KEY
}
}

View file

@ -0,0 +1,40 @@
package ee.carlrobert.codegpt.settings.service
import com.intellij.openapi.components.service
import com.intellij.openapi.options.Configurable
import ee.carlrobert.codegpt.credentials.CredentialsStore.CredentialKey.ANTHROPIC_API_KEY
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.anthropic.AnthropicSettings
import ee.carlrobert.codegpt.settings.service.anthropic.AnthropicSettingsForm
import javax.swing.JComponent
class AnthropicServiceConfigurable : Configurable {
private lateinit var component: AnthropicSettingsForm
override fun getDisplayName(): String {
return "CodeGPT: Anthropic Service"
}
override fun createComponent(): JComponent {
component = AnthropicSettingsForm(service<AnthropicSettings>().state)
return component.form
}
override fun isModified(): Boolean {
return component.getCurrentState() != service<AnthropicSettings>().state
|| component.getApiKey() != getCredential(ANTHROPIC_API_KEY)
}
override fun apply() {
setCredential(ANTHROPIC_API_KEY, component.getApiKey())
service<GeneralSettings>().state.selectedService = ServiceType.ANTHROPIC
service<AnthropicSettings>().loadState(component.getCurrentState())
}
override fun reset() {
component.resetForm()
}
}

View file

@ -0,0 +1,43 @@
package ee.carlrobert.codegpt.settings.service
import com.intellij.openapi.components.service
import com.intellij.openapi.options.Configurable
import ee.carlrobert.codegpt.credentials.CredentialsStore.CredentialKey.AZURE_ACTIVE_DIRECTORY_TOKEN
import ee.carlrobert.codegpt.credentials.CredentialsStore.CredentialKey.AZURE_OPENAI_API_KEY
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.azure.AzureSettings
import ee.carlrobert.codegpt.settings.service.azure.AzureSettingsForm
import javax.swing.JComponent
class AzureServiceConfigurable : Configurable {
private lateinit var component: AzureSettingsForm
override fun getDisplayName(): String {
return "CodeGPT: Azure Service"
}
override fun createComponent(): JComponent {
component = AzureSettingsForm(service<AzureSettings>().state)
return component.getForm()
}
override fun isModified(): Boolean {
return component.getCurrentState() != service<AzureSettings>().state
|| component.getActiveDirectoryToken() != getCredential(AZURE_ACTIVE_DIRECTORY_TOKEN)
|| component.getApiKey() != getCredential(AZURE_OPENAI_API_KEY)
}
override fun apply() {
service<GeneralSettings>().state.selectedService = ServiceType.AZURE
service<AzureSettings>().loadState(component.currentState)
setCredential(AZURE_OPENAI_API_KEY, component.getApiKey())
setCredential(AZURE_ACTIVE_DIRECTORY_TOKEN, component.getActiveDirectoryToken())
}
override fun reset() {
component.resetForm()
}
}

View file

@ -1,13 +1,26 @@
package ee.carlrobert.codegpt.settings.service
import com.intellij.icons.AllIcons.General
import com.intellij.ide.HelpTooltip
import com.intellij.openapi.ui.ComboBox
import com.intellij.openapi.ui.panel.ComponentPanelBuilder
import com.intellij.ui.EnumComboBoxModel
import com.intellij.ui.components.JBCheckBox
import com.intellij.ui.components.JBLabel
import com.intellij.ui.components.fields.IntegerField
import com.intellij.util.ui.FormBuilder
import ee.carlrobert.codegpt.CodeGPTBundle
import ee.carlrobert.codegpt.codecompletions.InfillPromptTemplate
import org.apache.commons.text.StringEscapeUtils
import java.awt.FlowLayout
import javax.swing.Box
import javax.swing.JPanel
class CodeCompletionConfigurationForm(codeCompletionsEnabled: Boolean, maxTokens: Int) {
class CodeCompletionConfigurationForm(
codeCompletionsEnabled: Boolean,
maxTokens: Int,
fimTemplate: InfillPromptTemplate?
) {
private val codeCompletionsEnabledCheckBox = JBCheckBox(
CodeGPTBundle.get("codeCompletionsForm.enableFeatureText"),
@ -18,15 +31,33 @@ class CodeCompletionConfigurationForm(codeCompletionsEnabled: Boolean, maxTokens
columns = 12
value = maxTokens
}
private val promptTemplateComboBox =
ComboBox(EnumComboBoxModel(InfillPromptTemplate::class.java)).apply {
item = fimTemplate
addItemListener {
updatePromptTemplateHelpTooltip(it.item as InfillPromptTemplate)
}
}
private val promptTemplateHelpText = JBLabel(General.ContextHelp)
fun getForm(): JPanel {
return FormBuilder.createFormBuilder()
val formBuilder = FormBuilder.createFormBuilder()
.addComponent(codeCompletionsEnabledCheckBox)
.addVerticalGap(4)
.addLabeledComponent(
CodeGPTBundle.get("codeCompletionsForm.maxTokensLabel"),
codeCompletionMaxTokensField
)
.addVerticalGap(4);
if (fimTemplate != null) {
formBuilder.addVerticalGap(4)
.addLabeledComponent(
"FIM template:",
JPanel(FlowLayout(FlowLayout.LEADING, 0, 0)).apply {
add(promptTemplateComboBox)
add(Box.createHorizontalStrut(4))
add(promptTemplateHelpText)
})
}
return formBuilder.addLabeledComponent(
CodeGPTBundle.get("codeCompletionsForm.maxTokensLabel"),
codeCompletionMaxTokensField
)
.addComponentToRightColumn(
ComponentPanelBuilder.createCommentComponent(
CodeGPTBundle.get("codeCompletionsForm.maxTokensComment"), true, 48, true
@ -46,4 +77,20 @@ class CodeCompletionConfigurationForm(codeCompletionsEnabled: Boolean, maxTokens
set(maxTokens) {
codeCompletionMaxTokensField.value = maxTokens
}
var fimTemplate: InfillPromptTemplate?
get() = promptTemplateComboBox.item
set(template) {
promptTemplateComboBox.item = template
}
private fun updatePromptTemplateHelpTooltip(template: InfillPromptTemplate) {
promptTemplateHelpText.setToolTipText(null)
val description = StringEscapeUtils.escapeHtml4(template.buildPrompt("PREFIX", "SUFFIX"))
HelpTooltip()
.setTitle(template.toString())
.setDescription("<html><p>$description</p></html>")
.installOn(promptTemplateHelpText)
}
}

View file

@ -0,0 +1,40 @@
package ee.carlrobert.codegpt.settings.service
import com.intellij.openapi.components.service
import com.intellij.openapi.options.Configurable
import ee.carlrobert.codegpt.credentials.CredentialsStore.CredentialKey.LLAMA_API_KEY
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.llama.LlamaSettings
import ee.carlrobert.codegpt.settings.service.llama.form.LlamaSettingsForm
import javax.swing.JComponent
class LlamaServiceConfigurable : Configurable {
private lateinit var component: LlamaSettingsForm
override fun getDisplayName(): String {
return "CodeGPT: Custom Service"
}
override fun createComponent(): JComponent {
component = LlamaSettingsForm(service<LlamaSettings>().state)
return component
}
override fun isModified(): Boolean {
return component.getCurrentState() != service<LlamaSettings>().state
|| component.llamaServerPreferencesForm.getApiKey() != getCredential(LLAMA_API_KEY)
}
override fun apply() {
service<GeneralSettings>().state.selectedService = ServiceType.LLAMA_CPP
setCredential(LLAMA_API_KEY, component.llamaServerPreferencesForm.getApiKey())
service<LlamaSettings>().loadState(component.currentState)
}
override fun reset() {
component.resetForm()
}
}

View file

@ -0,0 +1,40 @@
package ee.carlrobert.codegpt.settings.service
import com.intellij.openapi.components.service
import com.intellij.openapi.options.Configurable
import ee.carlrobert.codegpt.credentials.CredentialsStore.CredentialKey.OPENAI_API_KEY
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.openai.OpenAISettings
import ee.carlrobert.codegpt.settings.service.openai.OpenAISettingsForm
import javax.swing.JComponent
class OpenAIServiceConfigurable : Configurable {
private lateinit var component: OpenAISettingsForm
override fun getDisplayName(): String {
return "CodeGPT: OpenAI Service"
}
override fun createComponent(): JComponent {
component = OpenAISettingsForm(service<OpenAISettings>().state)
return component.getForm()
}
override fun isModified(): Boolean {
return component.getCurrentState() != service<OpenAISettings>().state
|| component.getApiKey() != getCredential(OPENAI_API_KEY)
}
override fun apply() {
service<GeneralSettings>().state.selectedService = ServiceType.OPENAI
setCredential(OPENAI_API_KEY, component.getApiKey())
service<OpenAISettings>().loadState(component.getCurrentState())
}
override fun reset() {
component.resetForm()
}
}

View file

@ -0,0 +1,52 @@
package ee.carlrobert.codegpt.settings.service
import com.intellij.openapi.components.service
import com.intellij.openapi.options.Configurable
import ee.carlrobert.codegpt.conversations.ConversationsState
import ee.carlrobert.codegpt.settings.GeneralSettings
import ee.carlrobert.codegpt.telemetry.TelemetryAction
import ee.carlrobert.codegpt.toolwindow.chat.ChatToolWindowContentManager
import ee.carlrobert.codegpt.util.ApplicationUtil.findCurrentProject
import javax.swing.JComponent
class ServiceConfigurable : Configurable {
private lateinit var component: ServiceConfigurableComponent
override fun getDisplayName(): String {
return "CodeGPT: Services"
}
override fun createComponent(): JComponent {
component = ServiceConfigurableComponent()
return component.getPanel()
}
override fun isModified(): Boolean {
return component.getSelectedService() != service<GeneralSettings>().state.selectedService
}
override fun apply() {
val state = service<GeneralSettings>().state
state.selectedService = component.getSelectedService()
val serviceChanged = component.getSelectedService() != state.selectedService
if (serviceChanged) {
resetActiveTab()
TelemetryAction.SETTINGS_CHANGED.createActionMessage()
.property("service", component.getSelectedService().code.lowercase())
.send()
}
}
override fun reset() {
component.setSelectedService(service<GeneralSettings>().state.selectedService)
}
private fun resetActiveTab() {
service<ConversationsState>().currentConversation = null
val project = findCurrentProject()
?: throw RuntimeException("Could not find current project.")
project.getService(ChatToolWindowContentManager::class.java).resetAll()
}
}

View file

@ -0,0 +1,72 @@
package ee.carlrobert.codegpt.settings.service
import com.intellij.ide.DataManager
import com.intellij.openapi.components.service
import com.intellij.openapi.options.ex.Settings
import com.intellij.openapi.ui.ComboBox
import com.intellij.ui.EnumComboBoxModel
import com.intellij.ui.components.ActionLink
import com.intellij.ui.components.JBLabel
import com.intellij.util.ui.FormBuilder
import ee.carlrobert.codegpt.CodeGPTBundle
import ee.carlrobert.codegpt.settings.GeneralSettings
import ee.carlrobert.codegpt.settings.service.codegpt.CodeGPTServiceConfigurable
import ee.carlrobert.codegpt.settings.service.codegpt.CodeGPTServiceForm
import ee.carlrobert.codegpt.settings.service.custom.CustomServiceConfigurable
import ee.carlrobert.codegpt.settings.service.google.GoogleSettingsConfigurable
import ee.carlrobert.codegpt.settings.service.ollama.OllamaSettingsConfigurable
import javax.swing.JPanel
class ServiceConfigurableComponent {
var form: CodeGPTServiceForm = CodeGPTServiceForm()
private var serviceComboBox: ComboBox<ServiceType> =
ComboBox(EnumComboBoxModel(ServiceType::class.java)).apply {
selectedItem = service<GeneralSettings>().state.selectedService
}
fun getSelectedService(): ServiceType {
return serviceComboBox.item
}
fun setSelectedService(serviceType: ServiceType?) {
serviceComboBox.selectedItem = serviceType
}
fun getPanel(): JPanel = FormBuilder.createFormBuilder()
.addLabeledComponent(
CodeGPTBundle.get("settingsConfigurable.service.label"),
serviceComboBox
)
.addVerticalGap(8)
.addComponent(JBLabel("All available providers that can be used with CodeGPT:"))
.addVerticalGap(8)
.addComponent(FormBuilder.createFormBuilder()
.setFormLeftIndent(16).apply {
addLinks(this)
}
.panel)
.addComponentFillVertically(JPanel(), 0)
.panel
private fun addLinks(formBuilder: FormBuilder) {
mapOf(
"CodeGPT" to CodeGPTServiceConfigurable::class.java,
"OpenAI" to OpenAIServiceConfigurable::class.java,
"Custom OpenAI" to CustomServiceConfigurable::class.java,
"Azure" to AzureServiceConfigurable::class.java,
"Anthropic" to AnthropicServiceConfigurable::class.java,
"Google" to GoogleSettingsConfigurable::class.java,
"You.com" to YouServiceConfigurable::class.java,
"LLaMA C/C++ (Local)" to LlamaServiceConfigurable::class.java,
"Ollama (Local)" to OllamaSettingsConfigurable::class.java,
).entries.forEach { (name, configurableClass) ->
formBuilder.addComponent(ActionLink(name) {
val context = service<DataManager>().getDataContext(it.source as ActionLink)
val settings = Settings.KEY.getData(context)
settings?.select(settings.find(configurableClass))
})
}
}
}

View file

@ -0,0 +1,42 @@
package ee.carlrobert.codegpt.settings.service
import com.intellij.openapi.components.service
import com.intellij.openapi.options.Configurable
import com.intellij.openapi.util.Disposer
import ee.carlrobert.codegpt.settings.GeneralSettings
import ee.carlrobert.codegpt.settings.service.you.YouSettings
import ee.carlrobert.codegpt.settings.service.you.YouSettingsForm
import javax.swing.JComponent
class YouServiceConfigurable : Configurable {
private var parentDisposable = Disposer.newDisposable()
private lateinit var component: YouSettingsForm
override fun getDisplayName(): String {
return "CodeGPT: You.com Service"
}
override fun createComponent(): JComponent {
parentDisposable = Disposer.newDisposable();
component = YouSettingsForm(service<YouSettings>().state, parentDisposable)
return component
}
override fun isModified(): Boolean {
return component.getCurrentState() != service<YouSettings>().state
}
override fun apply() {
service<GeneralSettings>().state.selectedService = ServiceType.YOU
service<YouSettings>().loadState(component.currentState)
}
override fun disposeUIResources() {
Disposer.dispose(parentDisposable)
}
override fun reset() {
component.resetForm()
}
}

View file

@ -0,0 +1,38 @@
package ee.carlrobert.codegpt.settings.service.codegpt
import com.intellij.openapi.components.service
import com.intellij.openapi.options.Configurable
import ee.carlrobert.codegpt.credentials.CredentialsStore.CredentialKey.CODEGPT_API_KEY
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 javax.swing.JComponent
class CodeGPTServiceConfigurable : Configurable {
private lateinit var component: CodeGPTServiceForm
override fun getDisplayName(): String {
return "CodeGPT: CodeGPT Service"
}
override fun createComponent(): JComponent {
component = CodeGPTServiceForm()
return component.getForm()
}
override fun isModified(): Boolean {
return component.isModified() || component.getApiKey() != getCredential(CODEGPT_API_KEY)
}
override fun apply() {
setCredential(CODEGPT_API_KEY, component.getApiKey())
service<GeneralSettings>().state.selectedService = ServiceType.CODEGPT
component.applyChanges()
}
override fun reset() {
component.resetForm()
}
}

View file

@ -0,0 +1,148 @@
package ee.carlrobert.codegpt.settings.service.codegpt
import com.intellij.openapi.components.service
import com.intellij.openapi.ui.ComboBox
import com.intellij.openapi.ui.panel.ComponentPanelBuilder
import com.intellij.ui.TitledSeparator
import com.intellij.ui.components.JBCheckBox
import com.intellij.ui.components.JBPasswordField
import com.intellij.ui.components.fields.IntegerField
import com.intellij.util.ui.FormBuilder
import ee.carlrobert.codegpt.CodeGPTBundle
import ee.carlrobert.codegpt.credentials.CredentialsStore.CredentialKey.CODEGPT_API_KEY
import ee.carlrobert.codegpt.credentials.CredentialsStore.getCredential
import ee.carlrobert.codegpt.credentials.CredentialsStore.setCredential
import ee.carlrobert.codegpt.ui.UIUtil
import ee.carlrobert.llm.client.codegpt.CodeGPTAvailableModels
import ee.carlrobert.llm.client.codegpt.CodeGPTAvailableModels.AVAILABLE_CHAT_MODELS
import ee.carlrobert.llm.client.codegpt.CodeGPTAvailableModels.AVAILABLE_CODE_MODELS
import ee.carlrobert.llm.client.codegpt.CodeGPTModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import org.jdesktop.swingx.combobox.ListComboBoxModel
import java.awt.Component
import javax.swing.DefaultListCellRenderer
import javax.swing.JList
import javax.swing.JPanel
class CodeGPTServiceForm {
private val apiKeyField = JBPasswordField().apply {
columns = 30
}
private val chatCompletionModelComboBox =
ComboBox(ListComboBoxModel(AVAILABLE_CHAT_MODELS)).apply {
selectedItem =
CodeGPTAvailableModels.findByCode(service<CodeGPTServiceSettings>().state.chatCompletionSettings.model)
renderer = CustomComboBoxRenderer()
}
private val codeCompletionsEnabledCheckBox = JBCheckBox(
CodeGPTBundle.get("codeCompletionsForm.enableFeatureText"),
service<CodeGPTServiceSettings>().state.codeCompletionSettings.codeCompletionsEnabled
)
private val codeCompletionModelComboBox =
ComboBox(ListComboBoxModel(AVAILABLE_CODE_MODELS)).apply {
selectedItem =
CodeGPTAvailableModels.findByCode(service<CodeGPTServiceSettings>().state.codeCompletionSettings.model)
renderer = CustomComboBoxRenderer()
}
private val codeCompletionMaxTokensField =
IntegerField("completion_max_tokens", 8, 4096).apply {
columns = 12
value = service<CodeGPTServiceSettings>().state.codeCompletionSettings.maxTokens
}
init {
apiKeyField.text = runBlocking(Dispatchers.IO) {
getCredential(CODEGPT_API_KEY)
}
}
fun getForm(): JPanel = FormBuilder.createFormBuilder()
.addComponent(TitledSeparator(CodeGPTBundle.get("shared.configuration")))
.addComponent(
FormBuilder.createFormBuilder()
.setFormLeftIndent(16)
.addLabeledComponent(
CodeGPTBundle.get("settingsConfigurable.shared.apiKey.label"),
apiKeyField
)
.addComponentToRightColumn(
UIUtil.createComment("settingsConfigurable.service.codegpt.apiKey.comment")
)
.addLabeledComponent("Model:", chatCompletionModelComboBox)
.addVerticalGap(4)
.panel
)
.addComponent(TitledSeparator("Code Completions"))
.addComponent(
FormBuilder.createFormBuilder()
.setFormLeftIndent(16)
.addComponent(codeCompletionsEnabledCheckBox)
.addLabeledComponent("Model:", codeCompletionModelComboBox)
.addLabeledComponent("Max tokens:", codeCompletionMaxTokensField)
.addComponentToRightColumn(
ComponentPanelBuilder.createCommentComponent(
CodeGPTBundle.get("codeCompletionsForm.maxTokensComment"), true, 48, true
)
)
.panel
)
.addComponentFillVertically(JPanel(), 0)
.panel
fun getApiKey() = String(apiKeyField.password).ifEmpty { null }
fun isModified() = service<CodeGPTServiceSettings>().state.run {
(chatCompletionModelComboBox.selectedItem as CodeGPTModel).code != chatCompletionSettings.model
|| (codeCompletionModelComboBox.selectedItem as CodeGPTModel).code != codeCompletionSettings.model
|| codeCompletionMaxTokensField.value != codeCompletionSettings.maxTokens
|| codeCompletionsEnabledCheckBox.isSelected != codeCompletionSettings.codeCompletionsEnabled
|| getApiKey() != getCredential(CODEGPT_API_KEY)
}
fun applyChanges() {
service<CodeGPTServiceSettings>().state.run {
chatCompletionSettings.model =
(chatCompletionModelComboBox.selectedItem as CodeGPTModel).code
codeCompletionSettings.codeCompletionsEnabled =
codeCompletionsEnabledCheckBox.isSelected
codeCompletionSettings.maxTokens = codeCompletionMaxTokensField.value
codeCompletionSettings.model =
(codeCompletionModelComboBox.selectedItem as CodeGPTModel).code
}
setCredential(CODEGPT_API_KEY, getApiKey())
}
fun resetForm() {
service<CodeGPTServiceSettings>().state.run {
chatCompletionModelComboBox.selectedItem = chatCompletionSettings.model
codeCompletionModelComboBox.selectedItem = codeCompletionSettings.model
codeCompletionMaxTokensField.value = codeCompletionSettings.maxTokens
codeCompletionsEnabledCheckBox.isSelected =
codeCompletionSettings.codeCompletionsEnabled
}
apiKeyField.text = getCredential(CODEGPT_API_KEY)
}
private class CustomComboBoxRenderer : DefaultListCellRenderer() {
override fun getListCellRendererComponent(
list: JList<*>,
value: Any?,
index: Int,
isSelected: Boolean,
cellHasFocus: Boolean
): Component {
val component =
super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus)
if (value is CodeGPTModel) {
text = value.name
}
return component
}
}
}

View file

@ -0,0 +1,26 @@
package ee.carlrobert.codegpt.settings.service.codegpt
import com.intellij.openapi.components.*
@Service
@State(
name = "CodeGPT_CodeGPTServiceSettings",
storages = [Storage("CodeGPT_CodeGPTServiceSettings.xml")]
)
class CodeGPTServiceSettings :
SimplePersistentStateComponent<CodeGPTServiceSettingsState>(CodeGPTServiceSettingsState())
class CodeGPTServiceSettingsState : BaseState() {
var chatCompletionSettings by property(CodeGPTServiceChatCompletionSettingsState())
var codeCompletionSettings by property(CodeGPTServiceCodeCompletionSettingsState())
}
class CodeGPTServiceChatCompletionSettingsState : BaseState() {
var model by string("meta-llama/Llama-3-70b-chat-hf")
}
class CodeGPTServiceCodeCompletionSettingsState : BaseState() {
var codeCompletionsEnabled by property(true)
var model by string("codellama/CodeLlama-70b-hf")
var maxTokens by property(128)
}

View file

@ -0,0 +1,40 @@
package ee.carlrobert.codegpt.settings.service.custom
import com.intellij.openapi.components.service
import com.intellij.openapi.options.Configurable
import ee.carlrobert.codegpt.credentials.CredentialsStore.CredentialKey.CUSTOM_SERVICE_API_KEY
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.settings.service.custom.form.CustomServiceForm
import javax.swing.JComponent
class CustomServiceConfigurable : Configurable {
private lateinit var component: CustomServiceForm
override fun getDisplayName(): String {
return "CodeGPT: Custom Service"
}
override fun createComponent(): JComponent {
component = CustomServiceForm()
return component.getForm()
}
override fun isModified(): Boolean {
return component.isModified()
|| component.getApiKey() != getCredential(CUSTOM_SERVICE_API_KEY)
}
override fun apply() {
setCredential(CUSTOM_SERVICE_API_KEY, component.getApiKey())
service<GeneralSettings>().state.selectedService = ServiceType.CUSTOM_OPENAI
component.applyChanges()
}
override fun reset() {
component.resetForm()
}
}

View file

@ -3,6 +3,9 @@ package ee.carlrobert.codegpt.settings.service.custom
import com.intellij.openapi.components.*
import com.intellij.util.xmlb.annotations.OptionTag
import ee.carlrobert.codegpt.codecompletions.InfillPromptTemplate
import ee.carlrobert.codegpt.settings.service.custom.template.CustomServiceChatCompletionTemplate
import ee.carlrobert.codegpt.settings.service.custom.template.CustomServiceCodeCompletionTemplate
import ee.carlrobert.codegpt.settings.service.custom.template.CustomServiceTemplate
import ee.carlrobert.codegpt.util.MapConverter
@Service

View file

@ -1,4 +1,4 @@
package ee.carlrobert.codegpt.settings.service.custom
package ee.carlrobert.codegpt.settings.service.custom.form
import com.intellij.openapi.ui.MessageType
import com.intellij.ui.components.JBTextField
@ -6,6 +6,8 @@ import com.intellij.util.ui.FormBuilder
import ee.carlrobert.codegpt.CodeGPTBundle
import ee.carlrobert.codegpt.completions.CompletionRequestProvider
import ee.carlrobert.codegpt.completions.CompletionRequestService
import ee.carlrobert.codegpt.settings.service.custom.CustomServiceChatCompletionSettingsState
import ee.carlrobert.codegpt.settings.service.custom.CustomServiceFormTabbedPane
import ee.carlrobert.codegpt.ui.OverlayUtil
import ee.carlrobert.llm.client.openai.completion.ErrorDetails
import ee.carlrobert.llm.completion.CompletionEventListener
@ -15,7 +17,10 @@ import javax.swing.JButton
import javax.swing.JPanel
import javax.swing.SwingUtilities
class CustomServiceChatCompletionForm(state: CustomServiceChatCompletionSettingsState) {
class CustomServiceChatCompletionForm(
state: CustomServiceChatCompletionSettingsState,
val getApiKey: () -> String?
) {
private val urlField = JBTextField(state.url, 30)
private val tabbedPane = CustomServiceFormTabbedPane(state.headers, state.body)
@ -67,7 +72,13 @@ class CustomServiceChatCompletionForm(state: CustomServiceChatCompletionSettings
private fun testConnection() {
CompletionRequestService.getInstance().getCustomOpenAIChatCompletionAsync(
CompletionRequestProvider.buildCustomOpenAICompletionRequest("Hello!"),
CompletionRequestProvider.buildCustomOpenAICompletionRequest(
"Test",
urlField.text,
tabbedPane.headers,
tabbedPane.body,
getApiKey.invoke()
),
TestConnectionEventListener()
)
}

View file

@ -1,4 +1,4 @@
package ee.carlrobert.codegpt.settings.service.custom
package ee.carlrobert.codegpt.settings.service.custom.form
import com.intellij.icons.AllIcons.General
import com.intellij.ide.HelpTooltip
@ -16,6 +16,8 @@ import ee.carlrobert.codegpt.codecompletions.InfillPromptTemplate
import ee.carlrobert.codegpt.codecompletions.InfillRequestDetails
import ee.carlrobert.codegpt.completions.CompletionRequestService
import ee.carlrobert.codegpt.settings.configuration.Placeholder
import ee.carlrobert.codegpt.settings.service.custom.CustomServiceCodeCompletionSettingsState
import ee.carlrobert.codegpt.settings.service.custom.CustomServiceFormTabbedPane
import ee.carlrobert.codegpt.ui.OverlayUtil
import ee.carlrobert.llm.client.openai.completion.ErrorDetails
import ee.carlrobert.llm.completion.CompletionEventListener
@ -28,7 +30,10 @@ import javax.swing.JButton
import javax.swing.JPanel
import javax.swing.SwingUtilities
class CustomServiceCodeCompletionForm(state: CustomServiceCodeCompletionSettingsState) {
class CustomServiceCodeCompletionForm(
state: CustomServiceCodeCompletionSettingsState,
val getApiKey: () -> String?
) {
private val featureEnabledCheckBox = JBCheckBox(
CodeGPTBundle.get("codeCompletionsForm.enableFeatureText"),
@ -37,7 +42,7 @@ class CustomServiceCodeCompletionForm(state: CustomServiceCodeCompletionSettings
private val promptTemplateComboBox =
ComboBox(EnumComboBoxModel(InfillPromptTemplate::class.java)).apply {
selectedItem = state.infillTemplate
setSelectedItem(InfillPromptTemplate.LLAMA)
setSelectedItem(InfillPromptTemplate.CODE_LLAMA)
addItemListener {
updatePromptTemplateHelpTooltip(it.item as InfillPromptTemplate)
}
@ -138,7 +143,14 @@ class CustomServiceCodeCompletionForm(state: CustomServiceCodeCompletionSettings
private fun testConnection() {
CompletionRequestService.getInstance().getCustomOpenAICompletionAsync(
CodeCompletionRequestFactory.buildCustomRequest(InfillRequestDetails("Hello", "!")),
CodeCompletionRequestFactory.buildCustomRequest(
InfillRequestDetails("Hello", "!"),
urlField.text,
tabbedPane.headers,
tabbedPane.body,
promptTemplateComboBox.selectedItem as InfillPromptTemplate,
getApiKey.invoke()
),
TestConnectionEventListener()
)
}

View file

@ -1,18 +1,23 @@
package ee.carlrobert.codegpt.settings.service.custom
package ee.carlrobert.codegpt.settings.service.custom.form
import com.intellij.icons.AllIcons.General
import com.intellij.ide.HelpTooltip
import com.intellij.openapi.components.service
import com.intellij.openapi.ui.ComboBox
import com.intellij.ui.EnumComboBoxModel
import com.intellij.ui.TitledSeparator
import com.intellij.ui.components.JBLabel
import com.intellij.ui.components.JBPasswordField
import com.intellij.util.ui.FormBuilder
import ee.carlrobert.codegpt.CodeGPTBundle
import ee.carlrobert.codegpt.credentials.CredentialsStore.CredentialKey
import ee.carlrobert.codegpt.credentials.CredentialsStore.getCredential
import ee.carlrobert.codegpt.settings.service.custom.CustomServiceChatCompletionSettingsState
import ee.carlrobert.codegpt.settings.service.custom.CustomServiceCodeCompletionSettingsState
import ee.carlrobert.codegpt.settings.service.custom.CustomServiceSettings
import ee.carlrobert.codegpt.settings.service.custom.template.CustomServiceTemplate
import ee.carlrobert.codegpt.ui.UIUtil
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import java.awt.FlowLayout
import java.net.MalformedURLException
import java.net.URL
@ -24,7 +29,6 @@ class CustomServiceForm {
private val apiKeyField = JBPasswordField().apply {
columns = 30
text = getCredential(CredentialKey.CUSTOM_SERVICE_API_KEY)
}
private val templateHelpText = JBLabel(General.ContextHelp)
private val templateComboBox = ComboBox(EnumComboBoxModel(CustomServiceTemplate::class.java))
@ -34,8 +38,13 @@ class CustomServiceForm {
init {
val state = service<CustomServiceSettings>().state
chatCompletionsForm = CustomServiceChatCompletionForm(state.chatCompletionSettings)
codeCompletionsForm = CustomServiceCodeCompletionForm(state.codeCompletionSettings)
apiKeyField.text = runBlocking(Dispatchers.IO) {
getCredential(CredentialKey.CUSTOM_SERVICE_API_KEY)
}
chatCompletionsForm =
CustomServiceChatCompletionForm(state.chatCompletionSettings, this::getApiKey)
codeCompletionsForm =
CustomServiceCodeCompletionForm(state.codeCompletionSettings, this::getApiKey)
tabbedPane = JTabbedPane().apply {
add(CodeGPTBundle.get("shared.chatCompletions"), chatCompletionsForm.form)
add(CodeGPTBundle.get("shared.codeCompletions"), codeCompletionsForm.form)
@ -65,29 +74,24 @@ class CustomServiceForm {
}
fun getForm(): JPanel = FormBuilder.createFormBuilder()
.addComponent(TitledSeparator(CodeGPTBundle.get("shared.configuration")))
.addComponent(
FormBuilder.createFormBuilder()
.setFormLeftIndent(16)
.addLabeledComponent(
CodeGPTBundle.get("settingsConfigurable.service.custom.openai.presetTemplate.label"),
JPanel(FlowLayout(FlowLayout.LEADING, 0, 0)).apply {
add(templateComboBox)
add(Box.createHorizontalStrut(8))
add(templateHelpText)
}
)
.addLabeledComponent(
CodeGPTBundle.get("settingsConfigurable.shared.apiKey.label"),
apiKeyField
)
.addComponentToRightColumn(
UIUtil.createComment("settingsConfigurable.service.custom.openai.apiKey.comment")
)
.addVerticalGap(4)
.addComponent(tabbedPane)
.panel
.addLabeledComponent(
CodeGPTBundle.get("settingsConfigurable.service.custom.openai.presetTemplate.label"),
JPanel(FlowLayout(FlowLayout.LEADING, 0, 0)).apply {
add(templateComboBox)
add(Box.createHorizontalStrut(8))
add(templateHelpText)
}
)
.addLabeledComponent(
CodeGPTBundle.get("settingsConfigurable.shared.apiKey.label"),
apiKeyField
)
.addComponentToRightColumn(
UIUtil.createComment("settingsConfigurable.service.custom.openai.apiKey.comment")
)
.addVerticalGap(4)
.addComponent(tabbedPane)
.addComponentFillVertically(JPanel(), 0)
.panel
fun getApiKey() = String(apiKeyField.password).ifEmpty { null }
@ -102,7 +106,6 @@ class CustomServiceForm {
|| codeCompletionsForm.url != codeCompletionSettings.url
|| codeCompletionsForm.headers != codeCompletionSettings.headers
|| codeCompletionsForm.body != codeCompletionSettings.body
|| getApiKey() != getCredential(CredentialKey.CUSTOM_SERVICE_API_KEY)
}
fun applyChanges() {
@ -126,6 +129,7 @@ class CustomServiceForm {
fun resetForm() {
service<CustomServiceSettings>().state.run {
templateComboBox.item = template
apiKeyField.text = getCredential(CredentialKey.CUSTOM_SERVICE_API_KEY)
chatCompletionsForm.resetForm(chatCompletionSettings)
codeCompletionsForm.resetForm(codeCompletionSettings)
}

View file

@ -1,4 +1,4 @@
package ee.carlrobert.codegpt.settings.service.custom
package ee.carlrobert.codegpt.settings.service.custom.template
enum class CustomServiceChatCompletionTemplate(
val url: String,
@ -89,6 +89,16 @@ enum class CustomServiceChatCompletionTemplate(
"http://localhost:8080/v1/chat/completions",
getDefaultHeaders(),
getDefaultBodyParams(emptyMap())
),
MISTRAL_AI(
"https://api.mistral.ai/v1/chat/completions",
getDefaultHeaders("Authorization", "Bearer \$CUSTOM_SERVICE_API_KEY"),
getDefaultBodyParams(
mapOf(
"model" to "open-mistral-7b",
"max_tokens" to 1024
)
)
);
}

View file

@ -1,4 +1,4 @@
package ee.carlrobert.codegpt.settings.service.custom
package ee.carlrobert.codegpt.settings.service.custom.template
enum class CustomServiceCodeCompletionTemplate(
val url: String,

View file

@ -1,4 +1,4 @@
package ee.carlrobert.codegpt.settings.service.custom
package ee.carlrobert.codegpt.settings.service.custom.template
enum class CustomServiceTemplate(
val providerName: String,
@ -61,6 +61,11 @@ enum class CustomServiceTemplate(
"LLaMA C/C++",
"https://github.com/ggerganov/llama.cpp/blob/master/examples/server/README.md",
CustomServiceChatCompletionTemplate.LLAMA_CPP
),
MISTRAL_AI(
"Mistral AI",
"https://docs.mistral.ai/getting-started/quickstart",
CustomServiceChatCompletionTemplate.MISTRAL_AI
);
override fun toString(): String {

View file

@ -0,0 +1,12 @@
package ee.carlrobert.codegpt.settings.service.google
import com.intellij.openapi.components.*
import ee.carlrobert.llm.client.google.models.GoogleModel
@Service
@State(name = "CodeGPT_GoogleSettings_210", storages = [Storage("CodeGPT_GoogleSettings_210.xml")])
class GoogleSettings : SimplePersistentStateComponent<GoogleSettingsState>(GoogleSettingsState())
class GoogleSettingsState : BaseState() {
var model by string(GoogleModel.GEMINI_PRO.code)
}

View file

@ -0,0 +1,38 @@
package ee.carlrobert.codegpt.settings.service.google;
import com.intellij.openapi.components.service
import com.intellij.openapi.options.Configurable
import ee.carlrobert.codegpt.credentials.CredentialsStore.CredentialKey.GOOGLE_API_KEY
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 javax.swing.JComponent
class GoogleSettingsConfigurable : Configurable {
private lateinit var component: GoogleSettingsForm
override fun getDisplayName(): String {
return "CodeGPT: Google Service"
}
override fun createComponent(): JComponent {
component = GoogleSettingsForm()
return component.getForm()
}
override fun isModified(): Boolean {
return component.isModified() || component.getApiKey() != getCredential(GOOGLE_API_KEY)
}
override fun apply() {
setCredential(GOOGLE_API_KEY, component.getApiKey())
service<GeneralSettings>().state.selectedService = ServiceType.GOOGLE
component.applyChanges()
}
override fun reset() {
component.resetForm()
}
}

View file

@ -0,0 +1,94 @@
package ee.carlrobert.codegpt.settings.service.google
import com.intellij.openapi.components.service
import com.intellij.openapi.ui.ComboBox
import com.intellij.ui.EnumComboBoxModel
import com.intellij.ui.TitledSeparator
import com.intellij.ui.components.JBPasswordField
import com.intellij.util.ui.FormBuilder
import com.intellij.util.ui.UI
import ee.carlrobert.codegpt.CodeGPTBundle
import ee.carlrobert.codegpt.credentials.CredentialsStore.CredentialKey.GOOGLE_API_KEY
import ee.carlrobert.codegpt.credentials.CredentialsStore.getCredential
import ee.carlrobert.codegpt.ui.UIUtil
import ee.carlrobert.llm.client.google.models.GoogleModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import javax.swing.JPanel
import javax.swing.event.HyperlinkEvent
class GoogleSettingsForm {
private val apiKeyField = JBPasswordField()
private val completionModelComboBox: ComboBox<GoogleModel>
init {
val state = service<GoogleSettings>().state
apiKeyField.columns = 30
apiKeyField.text = runBlocking(Dispatchers.IO) {
getCredential(GOOGLE_API_KEY)
}
completionModelComboBox = ComboBox(
EnumComboBoxModel(GoogleModel::class.java)
)
completionModelComboBox.selectedItem = GoogleModel.findByCode(state.model)
}
fun getForm(): JPanel = FormBuilder.createFormBuilder()
.addComponent(TitledSeparator(CodeGPTBundle.get("shared.configuration")))
.addComponent(
UIUtil.withEmptyLeftBorder(
UI.PanelFactory.grid()
.add(
UI.PanelFactory.panel(apiKeyField)
.withLabel(CodeGPTBundle.get("settingsConfigurable.shared.apiKey.label"))
.resizeX(false)
.withComment(CodeGPTBundle.get("settingsConfigurable.service.google.apiKey.comment"))
.withCommentHyperlinkListener { event: HyperlinkEvent? ->
UIUtil.handleHyperlinkClicked(
event
)
})
.add(
UI.PanelFactory.panel(completionModelComboBox)
.withLabel(CodeGPTBundle.get("settingsConfigurable.shared.model.label"))
.resizeX(false)
.withComment(CodeGPTBundle.get("settingsConfigurable.service.google.model.comment"))
.withCommentHyperlinkListener { event: HyperlinkEvent? ->
UIUtil.handleHyperlinkClicked(
event
)
}
)
.createPanel()
)
)
.addComponentFillVertically(JPanel(), 0)
.panel
fun getApiKey(): String? = String(apiKeyField.password).ifEmpty { null }
fun getModel(): String = (completionModelComboBox.model
.selectedItem as GoogleModel)
.code
fun getCurrentState() = GoogleSettingsState().apply { model = getModel() }
fun resetForm() {
val state = service<GoogleSettings>().state
apiKeyField.text = getCredential(GOOGLE_API_KEY)
completionModelComboBox.selectedItem = GoogleModel.findByCode(state.model)
}
fun isModified(): Boolean = service<GoogleSettings>().state.run {
model != getModel() || getApiKey() != getCredential(GOOGLE_API_KEY)
}
fun applyChanges() {
service<GoogleSettings>().state.run {
model = getModel()
}
}
}

View file

@ -0,0 +1,20 @@
package ee.carlrobert.codegpt.settings.service.ollama
import com.intellij.openapi.components.BaseState
import com.intellij.openapi.components.SimplePersistentStateComponent
import com.intellij.openapi.components.State
import com.intellij.openapi.components.Storage
import ee.carlrobert.codegpt.codecompletions.InfillPromptTemplate
@State(name = "CodeGPT_OllamaSettings_210", storages = [Storage("CodeGPT_OllamaSettings_210.xml")])
class OllamaSettings :
SimplePersistentStateComponent<OllamaSettingsState>(OllamaSettingsState())
class OllamaSettingsState : BaseState() {
var host by string("http://localhost:11434")
var model by string()
var codeCompletionsEnabled by property(true)
var codeCompletionMaxTokens by property(128)
var fimTemplate by enum<InfillPromptTemplate>(InfillPromptTemplate.CODE_LLAMA)
var availableModels by list<String>()
}

View file

@ -0,0 +1,35 @@
package ee.carlrobert.codegpt.settings.service.ollama
import com.intellij.openapi.components.service
import com.intellij.openapi.options.Configurable
import ee.carlrobert.codegpt.settings.GeneralSettings
import ee.carlrobert.codegpt.settings.service.ServiceType
import javax.swing.JComponent
class OllamaSettingsConfigurable : Configurable {
private lateinit var component: OllamaSettingsForm
override fun getDisplayName(): String {
return "CodeGPT: Ollama Service"
}
override fun createComponent(): JComponent {
component = OllamaSettingsForm()
return component.getForm()
}
override fun isModified(): Boolean {
return component.isModified()
}
override fun apply() {
component.applyChanges()
service<GeneralSettings>().state.selectedService = ServiceType.OLLAMA
}
override fun reset() {
component.resetForm()
}
}

View file

@ -0,0 +1,163 @@
package ee.carlrobert.codegpt.settings.service.ollama
import com.intellij.notification.NotificationType
import com.intellij.openapi.application.invokeLater
import com.intellij.openapi.components.service
import com.intellij.openapi.diagnostic.thisLogger
import com.intellij.openapi.observable.util.whenTextChangedFromUi
import com.intellij.openapi.ui.ComboBox
import com.intellij.ui.TitledSeparator
import com.intellij.ui.components.JBTextField
import com.intellij.util.ui.FormBuilder
import ee.carlrobert.codegpt.CodeGPTBundle
import ee.carlrobert.codegpt.settings.service.CodeCompletionConfigurationForm
import ee.carlrobert.codegpt.ui.OverlayUtil
import ee.carlrobert.codegpt.ui.UIUtil
import ee.carlrobert.llm.client.ollama.OllamaClient
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import java.awt.BorderLayout
import java.net.ConnectException
import javax.swing.ComboBoxModel
import javax.swing.DefaultComboBoxModel
import javax.swing.JButton
import javax.swing.JPanel
class OllamaSettingsForm {
private val refreshModelsButton =
JButton(CodeGPTBundle.get("settingsConfigurable.service.ollama.models.refresh"))
private val hostField: JBTextField
private val modelComboBox: ComboBox<String>
private val codeCompletionConfigurationForm: CodeCompletionConfigurationForm
companion object {
private val logger = thisLogger()
}
init {
val settings = service<OllamaSettings>().state
codeCompletionConfigurationForm = CodeCompletionConfigurationForm(
settings.codeCompletionsEnabled,
settings.codeCompletionMaxTokens,
settings.fimTemplate
)
val emptyModelsComboBoxModel =
DefaultComboBoxModel(arrayOf("Hit refresh to see models for this host"))
modelComboBox = ComboBox(emptyModelsComboBoxModel).apply {
isEnabled = false
}
hostField = JBTextField().apply {
text = settings.host
whenTextChangedFromUi {
modelComboBox.model = emptyModelsComboBoxModel
modelComboBox.isEnabled = false
}
}
refreshModelsButton.addActionListener { refreshModels() }
}
fun getForm(): JPanel = FormBuilder.createFormBuilder()
.addComponent(TitledSeparator(CodeGPTBundle.get("shared.configuration")))
.addComponent(
FormBuilder.createFormBuilder()
.setFormLeftIndent(16)
.addLabeledComponent(
CodeGPTBundle.get("settingsConfigurable.shared.baseHost.label"),
hostField
)
.addLabeledComponent(
CodeGPTBundle.get("settingsConfigurable.shared.model.label"),
JPanel(BorderLayout(8, 0)).apply {
add(modelComboBox, BorderLayout.CENTER)
add(refreshModelsButton, BorderLayout.EAST)
}
)
.panel
)
.addComponent(TitledSeparator(CodeGPTBundle.get("shared.codeCompletions")))
.addComponent(UIUtil.withEmptyLeftBorder(codeCompletionConfigurationForm.getForm()))
.addComponentFillVertically(JPanel(), 0)
.panel
fun getModel(): String {
return if (modelComboBox.isEnabled) {
modelComboBox.item
} else {
""
}
}
fun resetForm() {
service<OllamaSettings>().state.run {
hostField.text = host
modelComboBox.item = model
codeCompletionConfigurationForm.isCodeCompletionsEnabled = codeCompletionsEnabled
codeCompletionConfigurationForm.maxTokens = codeCompletionMaxTokens
codeCompletionConfigurationForm.fimTemplate = fimTemplate
}
}
fun applyChanges() {
service<OllamaSettings>().state.run {
host = hostField.text
model = modelComboBox.item
codeCompletionsEnabled = codeCompletionConfigurationForm.isCodeCompletionsEnabled
codeCompletionMaxTokens = codeCompletionConfigurationForm.maxTokens
fimTemplate = codeCompletionConfigurationForm.fimTemplate!!
}
}
fun isModified() = service<OllamaSettings>().state.run {
hostField.text != host
|| modelComboBox.item != model
|| codeCompletionConfigurationForm.isCodeCompletionsEnabled != codeCompletionsEnabled
|| codeCompletionConfigurationForm.maxTokens != codeCompletionMaxTokens
|| codeCompletionConfigurationForm.fimTemplate != fimTemplate
}
fun refreshModels() {
disableModelComboBoxWithPlaceholder(DefaultComboBoxModel(arrayOf("Loading")))
try {
val models = runBlocking(Dispatchers.IO) {
OllamaClient.Builder()
.setHost(hostField.text)
.build()
.modelTags
.models
.map { it.name }
}
service<OllamaSettings>().state.availableModels = models.toMutableList()
invokeLater {
modelComboBox.apply {
if (models.isNotEmpty()) {
model = DefaultComboBoxModel(models.toTypedArray())
isEnabled = true
} else {
model = DefaultComboBoxModel(arrayOf("No models"))
}
}
}
} catch (ex: RuntimeException) {
logger.error(ex)
if (ex.cause is ConnectException) {
OverlayUtil.showNotification(
"Unable to connect to Ollama server",
NotificationType.ERROR
)
} else {
OverlayUtil.showNotification(ex.message, NotificationType.ERROR)
}
disableModelComboBoxWithPlaceholder(DefaultComboBoxModel(arrayOf("Unable to load models")))
}
}
private fun disableModelComboBoxWithPlaceholder(placeholderModel: ComboBoxModel<String>) {
invokeLater {
modelComboBox.apply {
model = placeholderModel
isEnabled = false
}
}
}
}

View file

@ -5,6 +5,7 @@
<depends>com.intellij.modules.platform</depends>
<depends>com.intellij.modules.lang</depends>
<depends optional="true" config-file="plugin-java.xml">com.intellij.modules.java</depends>
<depends optional="true">Git4Idea</depends>
<projectListeners>
<listener topic="com.intellij.codeInsight.lookup.LookupManagerListener"
@ -18,6 +19,26 @@
<postStartupActivity implementation="ee.carlrobert.codegpt.CodeGPTUpdateActivity"/>
<applicationConfigurable id="settings.codegpt" parentId="tools" displayName="CodeGPT"
instance="ee.carlrobert.codegpt.settings.GeneralSettingsConfigurable"/>
<applicationConfigurable id="settings.codegpt.services" parentId="settings.codegpt" displayName="Providers"
instance="ee.carlrobert.codegpt.settings.service.ServiceConfigurable"/>
<applicationConfigurable id="settings.codegpt.services.codegpt" parentId="settings.codegpt.services" displayName="CodeGPT"
instance="ee.carlrobert.codegpt.settings.service.codegpt.CodeGPTServiceConfigurable"/>
<applicationConfigurable id="settings.codegpt.services.openai" parentId="settings.codegpt.services" displayName="OpenAI"
instance="ee.carlrobert.codegpt.settings.service.OpenAIServiceConfigurable"/>
<applicationConfigurable id="settings.codegpt.services.custom" parentId="settings.codegpt.services" displayName="Custom OpenAI"
instance="ee.carlrobert.codegpt.settings.service.custom.CustomServiceConfigurable"/>
<applicationConfigurable id="settings.codegpt.services.azure" parentId="settings.codegpt.services" displayName="Azure"
instance="ee.carlrobert.codegpt.settings.service.AzureServiceConfigurable"/>
<applicationConfigurable id="settings.codegpt.services.anthropic" parentId="settings.codegpt.services" displayName="Anthropic"
instance="ee.carlrobert.codegpt.settings.service.AnthropicServiceConfigurable"/>
<applicationConfigurable id="settings.codegpt.services.google" parentId="settings.codegpt.services" displayName="Google"
instance="ee.carlrobert.codegpt.settings.service.google.GoogleSettingsConfigurable"/>
<applicationConfigurable id="settings.codegpt.services.you" parentId="settings.codegpt.services" displayName="You.com"
instance="ee.carlrobert.codegpt.settings.service.YouServiceConfigurable"/>
<applicationConfigurable id="settings.codegpt.services.llama_cpp" parentId="settings.codegpt.services" displayName="LLaMA C/C++ (Local)"
instance="ee.carlrobert.codegpt.settings.service.LlamaServiceConfigurable"/>
<applicationConfigurable id="settings.codegpt.services.ollama" parentId="settings.codegpt.services" displayName="Ollama (Local)"
instance="ee.carlrobert.codegpt.settings.service.ollama.OllamaSettingsConfigurable"/>
<applicationConfigurable id="settings.codegpt.configuration" parentId="settings.codegpt" displayName="Configuration"
instance="ee.carlrobert.codegpt.settings.configuration.ConfigurationConfigurable"/>
<applicationConfigurable id="settings.codegpt.advanced" parentId="settings.codegpt" displayName="Advanced Settings"
@ -35,6 +56,7 @@
<applicationService serviceImplementation="ee.carlrobert.codegpt.settings.service.openai.OpenAISettings"/>
<applicationService serviceImplementation="ee.carlrobert.codegpt.settings.service.you.YouSettings"/>
<applicationService serviceImplementation="ee.carlrobert.codegpt.settings.service.llama.LlamaSettings"/>
<applicationService serviceImplementation="ee.carlrobert.codegpt.settings.service.ollama.OllamaSettings"/>
<applicationService serviceImplementation="ee.carlrobert.codegpt.settings.IncludedFilesSettings"/>
<applicationService serviceImplementation="ee.carlrobert.codegpt.settings.configuration.ConfigurationSettings"/>
<applicationService serviceImplementation="ee.carlrobert.codegpt.settings.advanced.AdvancedSettings"/>

View file

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="13px" height="13px" viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--emojione-monotone" preserveAspectRatio="xMidYMid meet"><path d="M32 2C15.432 2 2 15.432 2 32s13.432 30 30 30s30-13.432 30-30S48.568 2 32 2m-4.592 37.521c1.316 1.621 2.988 2.431 5.018 2.431c2.078 0 3.664-.695 4.756-2.086c.604-.748 1.104-1.87 1.502-3.366h6.023c-.52 3.163-1.848 5.735-3.982 7.717c-2.137 1.981-4.871 2.972-8.209 2.972c-4.129 0-7.375-1.338-9.736-4.016c-2.363-2.689-3.545-6.378-3.545-11.063c0-5.066 1.344-8.971 4.031-11.713c2.338-2.389 5.311-3.583 8.92-3.583c4.83 0 8.361 1.601 10.594 4.804c1.234 1.798 1.896 3.602 1.986 5.413h-6.063c-.387-1.392-.881-2.44-1.484-3.149c-1.08-1.26-2.682-1.891-4.803-1.891c-2.16 0-3.863.89-5.111 2.668c-1.246 1.778-1.869 4.295-1.869 7.549c0 3.254.658 5.691 1.972 7.313" fill="#000000"></path></svg>

After

Width:  |  Height:  |  Size: 1 KiB

View file

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="13px" height="13px" viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--emojione-monotone" preserveAspectRatio="xMidYMid meet"><path d="M32 2C15.432 2 2 15.432 2 32s13.432 30 30 30s30-13.432 30-30S48.568 2 32 2m-4.592 37.521c1.316 1.621 2.988 2.431 5.018 2.431c2.078 0 3.664-.695 4.756-2.086c.604-.748 1.104-1.87 1.502-3.366h6.023c-.52 3.163-1.848 5.735-3.982 7.717c-2.137 1.981-4.871 2.972-8.209 2.972c-4.129 0-7.375-1.338-9.736-4.016c-2.363-2.689-3.545-6.378-3.545-11.063c0-5.066 1.344-8.971 4.031-11.713c2.338-2.389 5.311-3.583 8.92-3.583c4.83 0 8.361 1.601 10.594 4.804c1.234 1.798 1.896 3.602 1.986 5.413h-6.063c-.387-1.392-.881-2.44-1.484-3.149c-1.08-1.26-2.682-1.891-4.803-1.891c-2.16 0-3.863.89-5.111 2.668c-1.246 1.778-1.869 4.295-1.869 7.549c0 3.254.658 5.691 1.972 7.313" fill="#ffffff"></path></svg>

After

Width:  |  Height:  |  Size: 1 KiB

View file

@ -0,0 +1,14 @@
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
<path
d="M16 8.016A8.522 8.522 0 008.016 16h-.032A8.521 8.521 0 000 8.016v-.032A8.521 8.521 0 007.984 0h.032A8.522 8.522 0 0016 7.984v.032z"
fill="url(#prefix__paint0_radial_980_20147)"/>
<defs>
<radialGradient id="prefix__paint0_radial_980_20147" cx="0" cy="0" r="1"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(16.1326 5.4553 -43.70045 129.2322 1.588 6.503)">
<stop offset=".067" stop-color="#9168C0"/>
<stop offset=".343" stop-color="#5684D1"/>
<stop offset=".672" stop-color="#1BA1E3"/>
</radialGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 660 B

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.3 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.3 KiB

View file

@ -17,12 +17,15 @@ action.statusbar.disableCompletions=Disable Completions
settings.displayName=CodeGPT: Settings
settings.openaiQuotaExceeded=OpenAI quota exceeded.
settingsConfigurable.displayName.label=Display name:
settingsConfigurable.service.label=Service:
settingsConfigurable.service.label=Selected provider:
settingsConfigurable.service.codegpt.apiKey.comment=You can find the API key in your <a href="https://codegpt.carlrobert.ee/account">User settings</a>.
settingsConfigurable.service.custom.openai.apiKey.comment=A secret value stored in the system's Keychain or KeePass, depending on your OS. This approach is recommended over storing the secret in the header as plain text.
settingsConfigurable.service.openai.apiKey.comment=You can find the API key in your <a href="https://platform.openai.com/account/api-keys">User settings</a>.
settingsConfigurable.service.openai.customModel.label=Custom model:
settingsConfigurable.service.openai.organization.label=Organization:
settingsConfigurable.section.openai.organization.comment=Useful when you are part of multiple organizations <sup><strong>optional</strong></sup>
settingsConfigurable.service.google.apiKey.comment=You can find the API key in your <a href="https://aistudio.google.com/app/apikey">User settings</a>.
settingsConfigurable.service.google.model.comment=Note: Gemini Vision models <a href="https://ai.google.dev/gemini-api/docs/get-started/web?multi-turn-conversations-chat&hl=en#multi-turn-conversations-chat">do not yet support chats</a>.
settingsConfigurable.service.anthropic.apiKey.comment=You can find the API key in your <a href="https://console.anthropic.com/settings/keys">User settings</a>.
settingsConfigurable.service.anthropic.apiVersion.comment=We always recommend using the <a href="https://docs.anthropic.com/claude/reference/versions">latest API version</a> whenever possible.
settingsConfigurable.service.anthropic.model.comment=For details on model comparison metrics, see <a href="https://docs.anthropic.com/claude/docs/models-overview#model-comparison">model comparison</a>.
@ -116,6 +119,7 @@ settingsConfigurable.service.custom.openai.url.label=URL:
settingsConfigurable.service.custom.openai.linkToDocs=Link to API docs
settingsConfigurable.service.custom.openai.connectionSuccess=Connection successful.
settingsConfigurable.service.custom.openai.connectionFailed=Connection failed.
settingsConfigurable.service.ollama.models.refresh=Refresh Models
configurationConfigurable.section.commitMessage.title=Commit Message Template
configurationConfigurable.section.commitMessage.systemPromptField.label=Prompt template:
configurationConfigurable.section.inlineCompletion.title=Inline Completion
@ -167,12 +171,15 @@ toolwindow.chat.youProCheckBox.enable=Turn on for complex queries
toolwindow.chat.youProCheckBox.disable=Turn off for faster responses
toolwindow.chat.youProCheckBox.notAllowed=Enable by subscribing to YouPro plan
toolwindow.chat.textArea.emptyText=Ask me anything...
service.openai.title=OpenAI Service
service.custom.openai.title=Custom OpenAI Service
service.anthropic.title=Anthropic Service
service.azure.title=Azure Service
service.you.title=You.com Service (Free, Cloud)
service.llama.title=LLaMA C/C++ Port (Free, Local)
service.codegpt.title=CodeGPT
service.openai.title=OpenAI
service.custom.openai.title=Custom OpenAI
service.anthropic.title=Anthropic
service.azure.title=Azure
service.google.title=Google
service.you.title=You.com
service.llama.title=LLaMA C/C++ (Local)
service.ollama.title=Ollama (Local)
validation.error.fieldRequired=This field is required.
validation.error.invalidEmail=The email you entered is invalid.
validation.error.mustBeNumber=Value must be number.
@ -204,4 +211,4 @@ shared.chatCompletions=Chat Completions
shared.codeCompletions=Code Completions
codeCompletionsForm.enableFeatureText=Enable code completions
codeCompletionsForm.maxTokensLabel=Max tokens:
codeCompletionsForm.maxTokensComment=The maximum number of tokens that can be generated in the code completion.
codeCompletionsForm.maxTokensComment=The maximum number of tokens that will be generated in the code completion.

View file

@ -39,7 +39,7 @@ class CodeCompletionServiceTest : IntegrationTest() {
assertThat(request.method).isEqualTo("POST")
assertThat(request.body)
.extracting("prompt")
.isEqualTo(InfillPromptTemplate.LLAMA.buildPrompt(prefix, suffix))
.isEqualTo(InfillPromptTemplate.CODE_LLAMA.buildPrompt(prefix, suffix))
listOf(
jsonMapResponse(
e("content", expectedCompletion),

View file

@ -3,8 +3,6 @@ package ee.carlrobert.codegpt.completions
import ee.carlrobert.codegpt.completions.CompletionRequestProvider.COMPLETION_SYSTEM_PROMPT
import ee.carlrobert.codegpt.conversations.ConversationService
import ee.carlrobert.codegpt.conversations.message.Message
import ee.carlrobert.codegpt.credentials.CredentialsStore.CredentialKey
import ee.carlrobert.codegpt.credentials.CredentialsStore.setCredential
import ee.carlrobert.codegpt.settings.configuration.ConfigurationSettings
import ee.carlrobert.llm.client.openai.completion.OpenAIChatCompletionModel
import org.assertj.core.api.Assertions.assertThat
@ -14,7 +12,7 @@ import testsupport.IntegrationTest
class CompletionRequestProviderTest : IntegrationTest() {
fun testChatCompletionRequestWithSystemPromptOverride() {
setCredential(CredentialKey.OPENAI_API_KEY, "TEST_API_KEY")
useOpenAIService()
ConfigurationSettings.getCurrentState().systemPrompt = "TEST_SYSTEM_PROMPT"
val conversation = ConversationService.getInstance().startConversation()
val firstMessage = createDummyMessage(500)
@ -43,6 +41,7 @@ class CompletionRequestProviderTest : IntegrationTest() {
}
fun testChatCompletionRequestWithoutSystemPromptOverride() {
useOpenAIService()
ConfigurationSettings.getCurrentState().systemPrompt = COMPLETION_SYSTEM_PROMPT
val conversation = ConversationService.getInstance().startConversation()
val firstMessage = createDummyMessage(500)
@ -71,6 +70,7 @@ class CompletionRequestProviderTest : IntegrationTest() {
}
fun testChatCompletionRequestRetry() {
useOpenAIService()
ConfigurationSettings.getCurrentState().systemPrompt = "TEST_SYSTEM_PROMPT"
val conversation = ConversationService.getInstance().startConversation()
val firstMessage = createDummyMessage("FIRST_TEST_PROMPT", 500)
@ -97,6 +97,7 @@ class CompletionRequestProviderTest : IntegrationTest() {
}
fun testReducedChatCompletionRequest() {
useOpenAIService()
ConfigurationSettings.getCurrentState().systemPrompt = COMPLETION_SYSTEM_PROMPT
val conversation = ConversationService.getInstance().startConversation()
conversation.addMessage(createDummyMessage(50))
@ -126,6 +127,7 @@ class CompletionRequestProviderTest : IntegrationTest() {
}
fun testTotalUsageExceededException() {
useOpenAIService()
val conversation = ConversationService.getInstance().startConversation()
conversation.addMessage(createDummyMessage(1500))
conversation.addMessage(createDummyMessage(1500))

View file

@ -7,10 +7,7 @@ import ee.carlrobert.codegpt.conversations.message.Message
import ee.carlrobert.codegpt.settings.configuration.ConfigurationSettings
import ee.carlrobert.llm.client.http.RequestEntity
import ee.carlrobert.llm.client.http.exchange.StreamHttpExchange
import ee.carlrobert.llm.client.util.JSONUtil.e
import ee.carlrobert.llm.client.util.JSONUtil.jsonArray
import ee.carlrobert.llm.client.util.JSONUtil.jsonMap
import ee.carlrobert.llm.client.util.JSONUtil.jsonMapResponse
import ee.carlrobert.llm.client.util.JSONUtil.*
import org.apache.http.HttpHeaders
import org.assertj.core.api.Assertions.assertThat
import testsupport.IntegrationTest
@ -171,6 +168,74 @@ class DefaultCompletionRequestHandlerTest : IntegrationTest() {
waitExpecting { "Hello!" == message.response }
}
fun testGoogleChatCompletionCall() {
useGoogleService()
ConfigurationSettings.getCurrentState().systemPrompt = "TEST_SYSTEM_PROMPT"
val message = Message("TEST_PROMPT")
val conversation = ConversationService.getInstance().startConversation()
val requestHandler = CompletionRequestHandler(getRequestEventListener(message))
expectGoogle(StreamHttpExchange { request: RequestEntity ->
assertThat(request.uri.path).isEqualTo("/v1/models/gemini-pro:streamGenerateContent")
assertThat(request.method).isEqualTo("POST")
assertThat(request.uri.query).isEqualTo("key=TEST_API_KEY&alt=sse")
assertThat(request.body)
.extracting("contents")
.isEqualTo(
listOf(
mapOf("parts" to listOf(mapOf("text" to "TEST_SYSTEM_PROMPT")), "role" to "user"),
mapOf("parts" to listOf(mapOf("text" to "Understood.")), "role" to "model"),
mapOf("parts" to listOf(mapOf("text" to "TEST_PROMPT")), "role" to "user"),
)
)
listOf(
jsonMapResponse(
"candidates",
jsonArray(jsonMap("content", jsonMap("parts", jsonArray(jsonMap("text", "Hello")))))
),
jsonMapResponse(
"candidates",
jsonArray(jsonMap("content", jsonMap("parts", jsonArray(jsonMap("text", "!")))))
)
)
})
requestHandler.call(CallParameters(conversation, ConversationType.DEFAULT, message, false))
waitExpecting { "Hello!" == message.response }
}
fun testCodeGPTServiceChatCompletionCall() {
useCodeGPTService()
ConfigurationSettings.getCurrentState().systemPrompt = "TEST_SYSTEM_PROMPT"
val message = Message("TEST_PROMPT")
val conversation = ConversationService.getInstance().startConversation()
val requestHandler = CompletionRequestHandler(getRequestEventListener(message))
expectCodeGPT(StreamHttpExchange { request: RequestEntity ->
assertThat(request.uri.path).isEqualTo("/v1/chat/completions")
assertThat(request.method).isEqualTo("POST")
assertThat(request.headers[HttpHeaders.AUTHORIZATION]!![0]).isEqualTo("Bearer TEST_API_KEY")
assertThat(request.body)
.extracting(
"model",
"messages")
.containsExactly(
"TEST_MODEL",
listOf(
mapOf("role" to "system", "content" to "TEST_SYSTEM_PROMPT"),
mapOf("role" to "user", "content" to "TEST_PROMPT")))
listOf(
jsonMapResponse("choices", jsonArray(jsonMap("delta", jsonMap("role", "assistant")))),
jsonMapResponse("choices", jsonArray(jsonMap("delta", jsonMap("content", "Hel")))),
jsonMapResponse("choices", jsonArray(jsonMap("delta", jsonMap("content", "lo")))),
jsonMapResponse("choices", jsonArray(jsonMap("delta", jsonMap("content", "!")))))
})
requestHandler.call(CallParameters(conversation, ConversationType.DEFAULT, message, false))
waitExpecting { "Hello!" == message.response }
}
private fun getRequestEventListener(message: Message): CompletionResponseEventListener {
return object : CompletionResponseEventListener {
override fun handleCompleted(fullMessage: String, callParameters: CallParameters) {

View file

@ -2,8 +2,11 @@ package ee.carlrobert.codegpt.completions
import ee.carlrobert.codegpt.completions.llama.PromptTemplate.ALPACA
import ee.carlrobert.codegpt.completions.llama.PromptTemplate.CHAT_ML
import ee.carlrobert.codegpt.completions.llama.PromptTemplate.CODE_GEMMA
import ee.carlrobert.codegpt.completions.llama.PromptTemplate.CODE_QWEN
import ee.carlrobert.codegpt.completions.llama.PromptTemplate.LLAMA
import ee.carlrobert.codegpt.completions.llama.PromptTemplate.LLAMA_3
import ee.carlrobert.codegpt.completions.llama.PromptTemplate.PHI_3
import ee.carlrobert.codegpt.completions.llama.PromptTemplate.TORA
import ee.carlrobert.codegpt.conversations.message.Message
import org.assertj.core.api.Assertions.assertThat
@ -104,6 +107,208 @@ class PromptTemplateTest {
TEST_USER_PROMPT<|eot_id|><|start_header_id|>assistant<|end_header_id|>""".trimIndent())
}
@Test
fun shouldBuildPhi3PromptWithoutHistory() {
val prompt = PHI_3.buildPrompt(SYSTEM_PROMPT, USER_PROMPT, listOf())
assertThat(prompt).isEqualTo("""
<|user|>
TEST_USER_PROMPT<|end|>
<|assistant|>""".trimIndent()
)
}
@ParameterizedTest
@NullAndEmptySource
@ValueSource(strings = [" ", "\t", "\n"])
fun shouldBuildPhi3PromptWithoutHistorySkippingBlankSystemPrompt(systemPrompt: String?) {
val prompt = PHI_3.buildPrompt(systemPrompt, USER_PROMPT, listOf())
assertThat(prompt).isEqualTo("""
<|user|>
TEST_USER_PROMPT<|end|>
<|assistant|>""".trimIndent()
)
}
@Test
fun shouldBuildPhi3PromptWithHistory() {
val prompt = PHI_3.buildPrompt(SYSTEM_PROMPT, USER_PROMPT, HISTORY)
assertThat(prompt).isEqualTo("""
<|user|>
TEST_PREV_PROMPT_1<|end|>
<|assistant|>
TEST_PREV_RESPONSE_1<|end|>
<|user|>
TEST_PREV_PROMPT_2<|end|>
<|assistant|>
TEST_PREV_RESPONSE_2<|end|>
<|user|>
TEST_USER_PROMPT<|end|>
<|assistant|>""".trimIndent())
}
@ParameterizedTest
@NullAndEmptySource
@ValueSource(strings = [" ", "\t", "\n"])
fun shouldBuildPhi3PromptWithHistorySkippingBlankSystemPrompt(systemPrompt: String?) {
val prompt = PHI_3.buildPrompt(systemPrompt, USER_PROMPT, HISTORY)
assertThat(prompt).isEqualTo("""
<|user|>
TEST_PREV_PROMPT_1<|end|>
<|assistant|>
TEST_PREV_RESPONSE_1<|end|>
<|user|>
TEST_PREV_PROMPT_2<|end|>
<|assistant|>
TEST_PREV_RESPONSE_2<|end|>
<|user|>
TEST_USER_PROMPT<|end|>
<|assistant|>""".trimIndent())
}
@Test
fun shouldBuildCodeGemmaPromptWithoutHistory() {
val prompt = CODE_GEMMA.buildPrompt(SYSTEM_PROMPT, USER_PROMPT, listOf())
assertThat(prompt).isEqualTo("""
<start_of_turn>user
TEST_USER_PROMPT<end_of_turn>
<start_of_turn>model
""".trimIndent())
}
@ParameterizedTest
@NullAndEmptySource
@ValueSource(strings = [" ", "\t", "\n"])
fun shouldBuildCodeGemmaPromptWithoutHistorySkippingBlankSystemPrompt(systemPrompt: String?) {
val prompt = CODE_GEMMA.buildPrompt(systemPrompt, USER_PROMPT, listOf())
assertThat(prompt).isEqualTo("""
<start_of_turn>user
TEST_USER_PROMPT<end_of_turn>
<start_of_turn>model
""".trimIndent())
}
@Test
fun shouldBuildCodeGemmaPromptWithHistory() {
val prompt = CODE_GEMMA.buildPrompt(SYSTEM_PROMPT, USER_PROMPT, HISTORY)
assertThat(prompt).isEqualTo("""
<start_of_turn>user
TEST_PREV_PROMPT_1<end_of_turn>
<start_of_turn>model
TEST_PREV_RESPONSE_1<end_of_turn>
<start_of_turn>user
TEST_PREV_PROMPT_2<end_of_turn>
<start_of_turn>model
TEST_PREV_RESPONSE_2<end_of_turn>
<start_of_turn>user
TEST_USER_PROMPT<end_of_turn>
<start_of_turn>model
""".trimIndent())
}
@ParameterizedTest
@NullAndEmptySource
@ValueSource(strings = [" ", "\t", "\n"])
fun shouldBuildCodeGemmaPromptWithHistorySkippingBlankSystemPrompt(systemPrompt: String?) {
val prompt = CODE_GEMMA.buildPrompt(systemPrompt, USER_PROMPT, HISTORY)
assertThat(prompt).isEqualTo("""
<start_of_turn>user
TEST_PREV_PROMPT_1<end_of_turn>
<start_of_turn>model
TEST_PREV_RESPONSE_1<end_of_turn>
<start_of_turn>user
TEST_PREV_PROMPT_2<end_of_turn>
<start_of_turn>model
TEST_PREV_RESPONSE_2<end_of_turn>
<start_of_turn>user
TEST_USER_PROMPT<end_of_turn>
<start_of_turn>model
""".trimIndent())
}
@Test
fun shouldBuildCodeQwenPromptWithoutHistory() {
val prompt = CODE_QWEN.buildPrompt(SYSTEM_PROMPT, USER_PROMPT, listOf())
assertThat(prompt).isEqualTo("""
<|im_start|>system
TEST_SYSTEM_PROMPT<|im_end|>
<|im_start|>user
TEST_USER_PROMPT<|im_end|>
<|im_start|>assistant
""".trimIndent())
}
@ParameterizedTest
@NullAndEmptySource
@ValueSource(strings = [" ", "\t", "\n"])
fun shouldBuildCodeQwenPromptWithoutHistorySkippingBlankSystemPrompt(systemPrompt: String?) {
val prompt = CODE_QWEN.buildPrompt(systemPrompt, USER_PROMPT, listOf())
assertThat(prompt).isEqualTo("""
<|im_start|>user
TEST_USER_PROMPT<|im_end|>
<|im_start|>assistant
""".trimIndent())
}
@Test
fun shouldBuildCodeQwenPromptWithHistory() {
val prompt = CODE_QWEN.buildPrompt(SYSTEM_PROMPT, USER_PROMPT, HISTORY)
assertThat(prompt).isEqualTo("""
<|im_start|>system
TEST_SYSTEM_PROMPT<|im_end|>
<|im_start|>user
TEST_PREV_PROMPT_1<|im_end|>
<|im_start|>assistant
TEST_PREV_RESPONSE_1<|im_end|>
<|im_start|>user
TEST_PREV_PROMPT_2<|im_end|>
<|im_start|>assistant
TEST_PREV_RESPONSE_2<|im_end|>
<|im_start|>user
TEST_USER_PROMPT<|im_end|>
<|im_start|>assistant
""".trimIndent())
}
@ParameterizedTest
@NullAndEmptySource
@ValueSource(strings = [" ", "\t", "\n"])
fun shouldBuildCodeQwenPromptWithHistorySkippingBlankSystemPrompt(systemPrompt: String?) {
val prompt = CODE_QWEN.buildPrompt(systemPrompt, USER_PROMPT, HISTORY)
assertThat(prompt).isEqualTo("""
<|im_start|>user
TEST_PREV_PROMPT_1<|im_end|>
<|im_start|>assistant
TEST_PREV_RESPONSE_1<|im_end|>
<|im_start|>user
TEST_PREV_PROMPT_2<|im_end|>
<|im_start|>assistant
TEST_PREV_RESPONSE_2<|im_end|>
<|im_start|>user
TEST_USER_PROMPT<|im_end|>
<|im_start|>assistant
""".trimIndent())
}
@Test
fun shouldBuildAlpacaPromptWithHistory() {
val prompt = ALPACA.buildPrompt(SYSTEM_PROMPT, USER_PROMPT, HISTORY)

View file

@ -1,18 +1,27 @@
package testsupport.mixin
import com.intellij.openapi.components.service
import com.intellij.testFramework.PlatformTestUtil
import ee.carlrobert.codegpt.credentials.CredentialsStore.CredentialKey.AZURE_OPENAI_API_KEY
import ee.carlrobert.codegpt.credentials.CredentialsStore.CredentialKey.OPENAI_API_KEY
import ee.carlrobert.codegpt.credentials.CredentialsStore.CredentialKey.*
import ee.carlrobert.codegpt.credentials.CredentialsStore.setCredential
import ee.carlrobert.codegpt.settings.GeneralSettings
import ee.carlrobert.codegpt.settings.service.ServiceType
import ee.carlrobert.codegpt.settings.service.azure.AzureSettings
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.openai.OpenAISettings
import ee.carlrobert.llm.client.google.models.GoogleModel
import java.util.function.BooleanSupplier
interface ShortcutsTestMixin {
fun useCodeGPTService() {
GeneralSettings.getCurrentState().selectedService = ServiceType.CODEGPT
setCredential(CODEGPT_API_KEY, "TEST_API_KEY")
service<CodeGPTServiceSettings>().state.chatCompletionSettings.model = "TEST_MODEL"
}
fun useOpenAIService() {
useOpenAIService("gpt-4")
}
@ -41,6 +50,12 @@ interface ShortcutsTestMixin {
LlamaSettings.getCurrentState().serverPort = null
}
fun useGoogleService() {
GeneralSettings.getCurrentState().selectedService = ServiceType.GOOGLE
setCredential(GOOGLE_API_KEY, "TEST_API_KEY")
service<GoogleSettings>().state.model = GoogleModel.GEMINI_PRO.code
}
fun waitExpecting(condition: BooleanSupplier?) {
PlatformTestUtil.waitWithEventsDispatching(
"Waiting for message response timed out or did not meet expected conditions",
@ -48,5 +63,4 @@ interface ShortcutsTestMixin {
5
)
}
}