mirror of
https://github.com/carlrobertoh/ProxyAI.git
synced 2026-05-12 22:31:24 +00:00
* Initial implementation of integrating llama.cpp to run LLaMA models locally * Move submodule * Copy llama submodule to bundle * Support for downloading models from IDE * Code cleanup * Store port field * Replace service selection radio group with dropdown * Add quantization support + other fixes * Add option to override host * Fix override host handler * Disable port field when override host enabled * Design updates * Fix llama settings configuration, design changes, clean up code * Improve You.com coupon design * Add new Phind model and help tooltip * Fetch you.com subscription * Add CodeBooga model, fix downloadable model selection * Chat history support * Code refactoring, minor bug fixes * UI updates, several bug fixes, removed code llama python model * Code cleanup, enable llama port only on macOS * Change downloaded gguf models path * Move some of the labels to codegpt bundle * Minor fixes * Remove ToRA model, add help texts * Fix test * Modify description
This commit is contained in:
parent
ca2eb9b6fa
commit
45908e69df
71 changed files with 2748 additions and 533 deletions
|
|
@ -6,8 +6,10 @@ import com.intellij.ide.plugins.PluginManagerCore;
|
|||
import com.intellij.openapi.application.PathManager;
|
||||
import com.intellij.openapi.extensions.PluginId;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import ee.carlrobert.codegpt.telemetry.core.util.Directories;
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public final class CodeGPTPlugin {
|
||||
|
|
@ -33,6 +35,14 @@ public final class CodeGPTPlugin {
|
|||
return getPluginOptionsPath() + File.separator + "indexes";
|
||||
}
|
||||
|
||||
public static @NotNull String getLlamaSourcePath() {
|
||||
return getPluginBasePath() + File.separator + "llama.cpp";
|
||||
}
|
||||
|
||||
public static @NotNull String getLlamaModelsPath() {
|
||||
return Paths.get(System.getProperty("user.home"), ".codegpt/models/gguf").toString();
|
||||
}
|
||||
|
||||
public static @NotNull String getProjectIndexStorePath(@NotNull Project project) {
|
||||
return getIndexStorePath() + File.separator + project.getName();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,4 +11,5 @@ public final class Icons {
|
|||
public static final Icon OpenAIIcon = IconLoader.getIcon("/icons/openai.svg", Icons.class);
|
||||
public static final Icon AzureIcon = IconLoader.getIcon("/icons/azure.svg", Icons.class);
|
||||
public static final Icon YouIcon = IconLoader.getIcon("/icons/you.svg", Icons.class);
|
||||
public static final Icon LlamaIcon = IconLoader.getIcon("/icons/llama.svg", Icons.class);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ public class EditorActionsUtil {
|
|||
}
|
||||
|
||||
public static void refreshActions() {
|
||||
AnAction actionGroup = ActionManager.getInstance().getAction("action.editor.group.EditorActionGroup");
|
||||
AnAction actionGroup = ActionManager.getInstance().getAction("project.label");
|
||||
if (actionGroup instanceof DefaultActionGroup) {
|
||||
DefaultActionGroup group = (DefaultActionGroup) actionGroup;
|
||||
group.removeAll();
|
||||
|
|
|
|||
|
|
@ -5,11 +5,14 @@ import ee.carlrobert.codegpt.credentials.AzureCredentialsManager;
|
|||
import ee.carlrobert.codegpt.credentials.OpenAICredentialsManager;
|
||||
import ee.carlrobert.codegpt.settings.advanced.AdvancedSettingsState;
|
||||
import ee.carlrobert.codegpt.settings.state.AzureSettingsState;
|
||||
import ee.carlrobert.codegpt.settings.state.LlamaSettingsState;
|
||||
import ee.carlrobert.codegpt.settings.state.OpenAISettingsState;
|
||||
import ee.carlrobert.codegpt.settings.state.YouSettingsState;
|
||||
import ee.carlrobert.llm.client.Client;
|
||||
import ee.carlrobert.llm.client.ProxyAuthenticator;
|
||||
import ee.carlrobert.llm.client.azure.AzureClient;
|
||||
import ee.carlrobert.llm.client.azure.AzureCompletionRequestParams;
|
||||
import ee.carlrobert.llm.client.llama.LlamaClient;
|
||||
import ee.carlrobert.llm.client.openai.OpenAIClient;
|
||||
import ee.carlrobert.llm.client.you.UTMParameters;
|
||||
import ee.carlrobert.llm.client.you.YouClient;
|
||||
|
|
@ -33,8 +36,16 @@ public class CompletionClientProvider {
|
|||
utmParameters.setMedium("jetbrains");
|
||||
utmParameters.setCampaign(CodeGPTPlugin.getVersion());
|
||||
utmParameters.setContent("CodeGPT");
|
||||
return new YouClient.Builder(sessionId, accessToken)
|
||||
// FIXME
|
||||
return (YouClient) new YouClient.Builder(sessionId, accessToken)
|
||||
.setUTMParameters(utmParameters)
|
||||
.setHost(YouSettingsState.getInstance().getBaseHost())
|
||||
.build();
|
||||
}
|
||||
|
||||
public static LlamaClient getLlamaClient() {
|
||||
return new LlamaClient.Builder()
|
||||
.setPort(LlamaSettingsState.getInstance().getServerPort())
|
||||
.build();
|
||||
}
|
||||
|
||||
|
|
@ -65,10 +76,9 @@ public class CompletionClientProvider {
|
|||
builder.setProxy(
|
||||
new Proxy(advancedSettings.getProxyType(), new InetSocketAddress(proxyHost, proxyPort)));
|
||||
if (advancedSettings.isProxyAuthSelected()) {
|
||||
builder.setProxyAuthenticator(
|
||||
new ProxyAuthenticator(
|
||||
advancedSettings.getProxyUsername(),
|
||||
advancedSettings.getProxyPassword()));
|
||||
builder.setProxyAuthenticator(new ProxyAuthenticator(
|
||||
advancedSettings.getProxyUsername(),
|
||||
advancedSettings.getProxyPassword()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -80,6 +80,11 @@ public class CompletionRequestHandler {
|
|||
var requestProvider = new CompletionRequestProvider(conversation);
|
||||
|
||||
try {
|
||||
if (settings.isUseLlamaService()) {
|
||||
return CompletionClientProvider.getLlamaClient()
|
||||
.getChatCompletion(requestProvider.buildLlamaCompletionRequest(message), eventListener);
|
||||
}
|
||||
|
||||
if (settings.isUseYouService()) {
|
||||
var sessionId = "";
|
||||
var accessToken = "";
|
||||
|
|
|
|||
|
|
@ -2,19 +2,23 @@ package ee.carlrobert.codegpt.completions;
|
|||
|
||||
import static java.util.stream.Collectors.toList;
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import ee.carlrobert.codegpt.CodeGPTPlugin;
|
||||
import ee.carlrobert.codegpt.EncodingManager;
|
||||
import ee.carlrobert.codegpt.completions.llama.LlamaModel;
|
||||
import ee.carlrobert.codegpt.conversations.Conversation;
|
||||
import ee.carlrobert.codegpt.conversations.ConversationsState;
|
||||
import ee.carlrobert.codegpt.conversations.message.Message;
|
||||
import ee.carlrobert.codegpt.settings.configuration.ConfigurationState;
|
||||
import ee.carlrobert.codegpt.settings.state.LlamaSettingsState;
|
||||
import ee.carlrobert.codegpt.settings.state.SettingsState;
|
||||
import ee.carlrobert.codegpt.settings.state.YouSettingsState;
|
||||
import ee.carlrobert.codegpt.telemetry.core.configuration.TelemetryConfiguration;
|
||||
import ee.carlrobert.codegpt.telemetry.core.service.TelemetryService;
|
||||
import ee.carlrobert.codegpt.telemetry.core.service.UserId;
|
||||
import ee.carlrobert.codegpt.util.ApplicationUtils;
|
||||
import ee.carlrobert.embedding.EmbeddingsService;
|
||||
import ee.carlrobert.llm.client.llama.completion.LlamaCompletionRequest;
|
||||
import ee.carlrobert.llm.client.openai.completion.chat.OpenAIChatCompletionModel;
|
||||
import ee.carlrobert.llm.client.openai.completion.chat.request.OpenAIChatCompletionMessage;
|
||||
import ee.carlrobert.llm.client.openai.completion.chat.request.OpenAIChatCompletionRequest;
|
||||
|
|
@ -36,17 +40,22 @@ public class CompletionRequestProvider {
|
|||
"Follow the user's requirements carefully & to the letter.\n" +
|
||||
"Your responses should be informative and logical.\n" +
|
||||
"You should always adhere to technical information.\n" +
|
||||
"If the user asks for code or technical questions, you must provide code suggestions and adhere to technical information.\n" +
|
||||
"If the question is related to a developer, CodeGPT must respond with content related to a developer.\n" +
|
||||
"First think step-by-step - describe your plan for what to build in pseudocode, written out in great detail.\n" +
|
||||
"If the user asks for code or technical questions, you must provide code suggestions and " +
|
||||
"adhere to technical information.\n" +
|
||||
"If the question is related to a developer, CodeGPT must respond with " +
|
||||
"content related to a developer.\n" +
|
||||
"First think step-by-step - describe your plan for what to build in pseudocode, " +
|
||||
"written out in great detail.\n" +
|
||||
"Then output the code in a single code block.\n" +
|
||||
"Minimize any other prose.\n" +
|
||||
"Keep your answers short and impersonal.\n" +
|
||||
"Use Markdown formatting in your answers.\n" +
|
||||
"Make sure to include the programming language name at the start of the Markdown code blocks.\n" +
|
||||
"Make sure to include the programming language name at the start of the " +
|
||||
"Markdown code blocks.\n" +
|
||||
"Avoid wrapping the whole response in triple backticks.\n" +
|
||||
"The user works in an IDE built by JetBrains which has a concept for editors with open files, integrated unit test support, " +
|
||||
"and output pane that shows the output of running the code as well as an integrated terminal.\n" +
|
||||
"The user works in an IDE built by JetBrains which has a concept for editors " +
|
||||
"with open files, integrated unit test support, and output pane that shows " +
|
||||
"the output of running the code as well as an integrated terminal.\n" +
|
||||
"You can only give one reply for each conversation turn.";
|
||||
|
||||
private final EncodingManager encodingManager = EncodingManager.getInstance();
|
||||
|
|
@ -60,6 +69,20 @@ public class CompletionRequestProvider {
|
|||
this.conversation = conversation;
|
||||
}
|
||||
|
||||
public LlamaCompletionRequest buildLlamaCompletionRequest(Message message) {
|
||||
var settings = LlamaSettingsState.getInstance();
|
||||
var promptTemplate = settings.isUseCustomModel() ?
|
||||
settings.getPromptTemplate() :
|
||||
LlamaModel.findByHuggingFaceModel(settings.getHuggingFaceModel()).getPromptTemplate();
|
||||
var prompt = promptTemplate.buildPrompt(
|
||||
COMPLETION_SYSTEM_PROMPT,
|
||||
message.getPrompt(),
|
||||
conversation.getMessages());
|
||||
return new LlamaCompletionRequest.Builder(prompt)
|
||||
.setN_predict(512)
|
||||
.build();
|
||||
}
|
||||
|
||||
public YouCompletionRequest buildYouCompletionRequest(Message message) {
|
||||
var requestBuilder = new YouCompletionRequest.Builder(message.getPrompt())
|
||||
.setUseGPT4Model(YouSettingsState.getInstance().isUseGPT4Model())
|
||||
|
|
@ -68,7 +91,8 @@ public class CompletionRequestProvider {
|
|||
prevMessage.getPrompt(),
|
||||
prevMessage.getResponse()))
|
||||
.collect(toList()));
|
||||
if (TelemetryConfiguration.getInstance().isEnabled()) {
|
||||
if (TelemetryConfiguration.getInstance().isEnabled() &&
|
||||
!ApplicationManager.getApplication().isUnitTestMode()) {
|
||||
requestBuilder.setUserId(UUID.fromString(UserId.INSTANCE.get()));
|
||||
}
|
||||
return requestBuilder.build();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,85 @@
|
|||
package ee.carlrobert.codegpt.completions;
|
||||
|
||||
import static java.lang.String.format;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
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_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"),
|
||||
|
||||
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"),
|
||||
|
||||
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");
|
||||
|
||||
private final int parameterSize;
|
||||
private final int quantization;
|
||||
private final String modelName;
|
||||
|
||||
HuggingFaceModel(int parameterSize, int quantization, String modelName) {
|
||||
this.parameterSize = parameterSize;
|
||||
this.quantization = quantization;
|
||||
this.modelName = modelName;
|
||||
}
|
||||
|
||||
public int getParameterSize() {
|
||||
return parameterSize;
|
||||
}
|
||||
|
||||
public int getQuantization() {
|
||||
return quantization;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return name();
|
||||
}
|
||||
|
||||
public String getFileName() {
|
||||
return modelName.toLowerCase().replace("-gguf", format(".Q%d_K_M.gguf", quantization));
|
||||
}
|
||||
|
||||
public URL getFileURL() {
|
||||
try {
|
||||
return new URL(
|
||||
format("https://huggingface.co/TheBloke/%s/resolve/main/%s", modelName, getFileName()));
|
||||
} catch (MalformedURLException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public URL getHuggingFaceURL() {
|
||||
try {
|
||||
return new URL("https://huggingface.co/TheBloke/" + modelName);
|
||||
} catch (MalformedURLException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return format("%d-bit precision", quantization);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,124 @@
|
|||
package ee.carlrobert.codegpt.completions.llama;
|
||||
|
||||
import static java.lang.String.format;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
import static java.util.stream.Collectors.toSet;
|
||||
|
||||
import ee.carlrobert.codegpt.completions.HuggingFaceModel;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public enum LlamaModel {
|
||||
CODE_LLAMA(
|
||||
"Code Llama",
|
||||
"Code Llama is a family of large language models for code based on Llama 2 providing state-of-the-art performance among open models, infilling capabilities, support for large input contexts, and zero-shot instruction following ability for programming tasks.",
|
||||
PromptTemplate.LLAMA,
|
||||
List.of(
|
||||
HuggingFaceModel.CODE_LLAMA_7B_Q3,
|
||||
HuggingFaceModel.CODE_LLAMA_7B_Q4,
|
||||
HuggingFaceModel.CODE_LLAMA_7B_Q5,
|
||||
HuggingFaceModel.CODE_LLAMA_13B_Q3,
|
||||
HuggingFaceModel.CODE_LLAMA_13B_Q4,
|
||||
HuggingFaceModel.CODE_LLAMA_13B_Q5,
|
||||
HuggingFaceModel.CODE_LLAMA_34B_Q3,
|
||||
HuggingFaceModel.CODE_LLAMA_34B_Q4,
|
||||
HuggingFaceModel.CODE_LLAMA_34B_Q5)
|
||||
),
|
||||
CODE_BOOGA(
|
||||
"CodeBooga",
|
||||
"CodeBooga is a high-performing code instruct model created by merging two existing code models: <ol><li>Phind-CodeLlama-34B-v2</li><li>WizardCoder-Python-34B-V1.0</li></ol>",
|
||||
PromptTemplate.ALPACA,
|
||||
List.of(
|
||||
HuggingFaceModel.CODE_BOOGA_34B_Q3,
|
||||
HuggingFaceModel.CODE_BOOGA_34B_Q4,
|
||||
HuggingFaceModel.CODE_BOOGA_34B_Q5)),
|
||||
PHIND_CODE_LLAMA(
|
||||
"Phind Code Llama",
|
||||
"This model is fine-tuned from Phind-CodeLlama-34B-v1 on an additional 1.5B tokens high-quality programming-related data, achieving 73.8% pass@1 on HumanEval. It's the current state-of-the-art amongst open-source models.",
|
||||
PromptTemplate.ALPACA,
|
||||
List.of(
|
||||
HuggingFaceModel.PHIND_CODE_LLAMA_34B_Q3,
|
||||
HuggingFaceModel.PHIND_CODE_LLAMA_34B_Q4,
|
||||
HuggingFaceModel.PHIND_CODE_LLAMA_34B_Q5)),
|
||||
WIZARD_CODER_PYTHON(
|
||||
"WizardCoder - Python",
|
||||
"WizardCoder, a Code Evol-Instruct fine-tuned Code LLM, which achieves the 73.2 pass@1 and surpasses GPT4 (2023/03/15), ChatGPT-3.5, and Claude2 on the HumanEval Benchmarks.",
|
||||
PromptTemplate.ALPACA,
|
||||
List.of(
|
||||
HuggingFaceModel.WIZARD_CODER_PYTHON_7B_Q3,
|
||||
HuggingFaceModel.WIZARD_CODER_PYTHON_7B_Q4,
|
||||
HuggingFaceModel.WIZARD_CODER_PYTHON_7B_Q5,
|
||||
HuggingFaceModel.WIZARD_CODER_PYTHON_13B_Q3,
|
||||
HuggingFaceModel.WIZARD_CODER_PYTHON_13B_Q4,
|
||||
HuggingFaceModel.WIZARD_CODER_PYTHON_13B_Q5,
|
||||
HuggingFaceModel.WIZARD_CODER_PYTHON_34B_Q3,
|
||||
HuggingFaceModel.WIZARD_CODER_PYTHON_34B_Q4,
|
||||
HuggingFaceModel.WIZARD_CODER_PYTHON_34B_Q5));
|
||||
|
||||
private final String label;
|
||||
private final String description;
|
||||
private final PromptTemplate promptTemplate;
|
||||
private final List<HuggingFaceModel> huggingFaceModels;
|
||||
|
||||
LlamaModel(
|
||||
String label,
|
||||
String description,
|
||||
PromptTemplate promptTemplate,
|
||||
List<HuggingFaceModel> huggingFaceModels) {
|
||||
this.label = label;
|
||||
this.description = description;
|
||||
this.promptTemplate = promptTemplate;
|
||||
this.huggingFaceModels = huggingFaceModels;
|
||||
}
|
||||
|
||||
public static @NotNull LlamaModel findByHuggingFaceModel(HuggingFaceModel huggingFaceModel) {
|
||||
for (var llamaModel : LlamaModel.values()) {
|
||||
if (llamaModel.getHuggingFaceModels().contains(huggingFaceModel)) {
|
||||
return llamaModel;
|
||||
}
|
||||
}
|
||||
|
||||
throw new RuntimeException("Unable to find correct LLM");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.join(" ", label, getFormattedModelSizeRange());
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public PromptTemplate getPromptTemplate() {
|
||||
return promptTemplate;
|
||||
}
|
||||
|
||||
public List<HuggingFaceModel> getHuggingFaceModels() {
|
||||
return huggingFaceModels;
|
||||
}
|
||||
|
||||
public String getFormattedModelSizeRange() {
|
||||
var parameters = huggingFaceModels.stream()
|
||||
.map(HuggingFaceModel::getParameterSize)
|
||||
.collect(toSet());
|
||||
if (parameters.size() == 1) {
|
||||
return parameters.iterator().next() + "B";
|
||||
}
|
||||
return format("(%dB - %dB)", Collections.min(parameters), Collections.max(parameters));
|
||||
}
|
||||
|
||||
public List<Integer> getSortedUniqueModelSizes() {
|
||||
return huggingFaceModels.stream()
|
||||
.map(HuggingFaceModel::getParameterSize)
|
||||
.collect(toSet())
|
||||
.stream()
|
||||
.sorted()
|
||||
.collect(toList());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,156 @@
|
|||
package ee.carlrobert.codegpt.completions.llama;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.intellij.execution.ExecutionException;
|
||||
import com.intellij.execution.configurations.GeneralCommandLine;
|
||||
import com.intellij.execution.process.OSProcessHandler;
|
||||
import com.intellij.execution.process.ProcessAdapter;
|
||||
import com.intellij.execution.process.ProcessEvent;
|
||||
import com.intellij.execution.process.ProcessListener;
|
||||
import com.intellij.execution.process.ProcessOutputType;
|
||||
import com.intellij.icons.AllIcons.Actions;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.components.Service;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.ui.components.JBLabel;
|
||||
import ee.carlrobert.codegpt.CodeGPTPlugin;
|
||||
import ee.carlrobert.codegpt.settings.service.ServerProgressPanel;
|
||||
import ee.carlrobert.codegpt.settings.state.LlamaSettingsState;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import javax.swing.SwingConstants;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@Service
|
||||
public final class LlamaServerAgent implements Disposable {
|
||||
|
||||
private static final Logger LOG = Logger.getInstance(LlamaServerAgent.class);
|
||||
|
||||
private static @Nullable OSProcessHandler makeProcessHandler;
|
||||
private static @Nullable OSProcessHandler startServerProcessHandler;
|
||||
|
||||
public void startAgent(
|
||||
String modelPath,
|
||||
int contextLength,
|
||||
int port,
|
||||
ServerProgressPanel serverProgressPanel,
|
||||
Runnable onSuccess) {
|
||||
ApplicationManager.getApplication().invokeLater(() -> {
|
||||
try {
|
||||
serverProgressPanel.updateText("Building llama.cpp...");
|
||||
makeProcessHandler = new OSProcessHandler(getMakeCommandLinde());
|
||||
makeProcessHandler.addProcessListener(
|
||||
getMakeProcessListener(modelPath, contextLength, port, serverProgressPanel, onSuccess));
|
||||
makeProcessHandler.startNotify();
|
||||
} catch (ExecutionException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void stopAgent() {
|
||||
if (startServerProcessHandler != null) {
|
||||
startServerProcessHandler.destroyProcess();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isServerRunning() {
|
||||
return startServerProcessHandler != null &&
|
||||
startServerProcessHandler.isStartNotified() &&
|
||||
!startServerProcessHandler.isProcessTerminated();
|
||||
}
|
||||
|
||||
private ProcessListener getMakeProcessListener(
|
||||
String modelPath,
|
||||
int contextLength,
|
||||
int port,
|
||||
ServerProgressPanel serverProgressPanel,
|
||||
Runnable onSuccess) {
|
||||
return new ProcessAdapter() {
|
||||
@Override
|
||||
public void onTextAvailable(@NotNull ProcessEvent event, @NotNull Key outputType) {
|
||||
LOG.info(event.getText());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processTerminated(@NotNull ProcessEvent event) {
|
||||
try {
|
||||
serverProgressPanel.updateText("Booting up server...");
|
||||
startServerProcessHandler = new OSProcessHandler(
|
||||
getServerCommandLine(modelPath, contextLength, port));
|
||||
startServerProcessHandler.addProcessListener(
|
||||
getProcessListener(port, serverProgressPanel, onSuccess));
|
||||
startServerProcessHandler.startNotify();
|
||||
} catch (ExecutionException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private ProcessListener getProcessListener(
|
||||
int port,
|
||||
ServerProgressPanel serverProgressPanel,
|
||||
Runnable onSuccess) {
|
||||
return new ProcessAdapter() {
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
@Override
|
||||
public void processTerminated(@NotNull ProcessEvent event) {
|
||||
serverProgressPanel.displayComponent(new JBLabel(
|
||||
"Server terminated",
|
||||
Actions.Cancel,
|
||||
SwingConstants.LEADING));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextAvailable(@NotNull ProcessEvent event, @NotNull Key outputType) {
|
||||
LOG.debug(event.getText());
|
||||
|
||||
if (outputType == ProcessOutputType.STDOUT) {
|
||||
try {
|
||||
var serverMessage = objectMapper.readValue(event.getText(), LlamaServerMessage.class);
|
||||
if ("HTTP server listening".equals(serverMessage.getMessage())) {
|
||||
LlamaSettingsState.getInstance().setServerPort(port);
|
||||
onSuccess.run();
|
||||
}
|
||||
} catch (Exception ignore) {
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static GeneralCommandLine getMakeCommandLinde() {
|
||||
GeneralCommandLine commandLine = new GeneralCommandLine().withCharset(StandardCharsets.UTF_8);
|
||||
commandLine.setExePath("make");
|
||||
commandLine.withWorkDirectory(CodeGPTPlugin.getLlamaSourcePath());
|
||||
commandLine.addParameters("-j");
|
||||
commandLine.setRedirectErrorStream(false);
|
||||
return commandLine;
|
||||
}
|
||||
|
||||
private GeneralCommandLine getServerCommandLine(String modelPath, int contextLength, int port) {
|
||||
GeneralCommandLine commandLine = new GeneralCommandLine().withCharset(StandardCharsets.UTF_8);
|
||||
commandLine.setExePath("./server");
|
||||
commandLine.withWorkDirectory(CodeGPTPlugin.getLlamaSourcePath());
|
||||
commandLine.addParameters(
|
||||
"-m", modelPath,
|
||||
"-c", String.valueOf(contextLength),
|
||||
"--port", String.valueOf(port));
|
||||
commandLine.setRedirectErrorStream(false);
|
||||
return commandLine;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
if (makeProcessHandler != null && !makeProcessHandler.isProcessTerminated()) {
|
||||
makeProcessHandler.destroyProcess();
|
||||
}
|
||||
if (startServerProcessHandler != null && !startServerProcessHandler.isProcessTerminated()) {
|
||||
startServerProcessHandler.destroyProcess();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package ee.carlrobert.codegpt.completions.llama;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class LlamaServerMessage {
|
||||
|
||||
private final String level;
|
||||
private final String message;
|
||||
|
||||
public LlamaServerMessage(
|
||||
@JsonProperty("level") String level,
|
||||
@JsonProperty("message") String message) {
|
||||
this.level = level;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public String getLevel() {
|
||||
return level;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
package ee.carlrobert.codegpt.completions.llama;
|
||||
|
||||
import ee.carlrobert.codegpt.conversations.message.Message;
|
||||
import java.util.List;
|
||||
|
||||
public enum PromptTemplate {
|
||||
|
||||
CHAT_ML("Chat Markup Language (ChatML)") {
|
||||
@Override
|
||||
public String buildPrompt(String systemPrompt, String userPrompt, List<Message> history) {
|
||||
StringBuilder prompt = new StringBuilder();
|
||||
|
||||
if (systemPrompt != null && !systemPrompt.isEmpty()) {
|
||||
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")
|
||||
.append("<|im_start|>assistant\n")
|
||||
.append(message.getResponse())
|
||||
.append("<|im_end|>\n");
|
||||
}
|
||||
|
||||
return prompt.append("<|im_start|>user\n")
|
||||
.append(userPrompt)
|
||||
.append("<|im_end|>")
|
||||
.toString();
|
||||
}
|
||||
},
|
||||
LLAMA("Llama") {
|
||||
@Override
|
||||
public String buildPrompt(String systemPrompt, String userPrompt, List<Message> history) {
|
||||
StringBuilder prompt = new StringBuilder();
|
||||
|
||||
if (systemPrompt != null && !systemPrompt.isEmpty()) {
|
||||
prompt.append("<<SYS>>")
|
||||
.append(systemPrompt)
|
||||
.append("<</SYS>>\n");
|
||||
}
|
||||
|
||||
for (Message message : history) {
|
||||
prompt.append("[INST]")
|
||||
.append(message.getPrompt())
|
||||
.append("[/INST]\n")
|
||||
.append(message.getResponse()).append("\n");
|
||||
}
|
||||
|
||||
return prompt.append("[INST]")
|
||||
.append(userPrompt)
|
||||
.append("[/INST]")
|
||||
.toString();
|
||||
}
|
||||
},
|
||||
TORA("ToRA") {
|
||||
@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("\n<|assistant|>\n")
|
||||
.append(message.getResponse()).append("\n");
|
||||
}
|
||||
|
||||
return prompt.append("<|user|>\n")
|
||||
.append(userPrompt)
|
||||
.append("\n<|assistant|>")
|
||||
.toString();
|
||||
}
|
||||
},
|
||||
ALPACA("Alpaca/Vicuna") {
|
||||
@Override
|
||||
public String buildPrompt(String systemPrompt, String userPrompt, List<Message> history) {
|
||||
StringBuilder prompt = new StringBuilder();
|
||||
|
||||
prompt.append(
|
||||
"Below is an instruction that describes a task. Write a response that appropriately completes the request.\n\n");
|
||||
|
||||
for (Message message : history) {
|
||||
prompt.append("### Instruction\n")
|
||||
.append(message.getPrompt())
|
||||
.append("\n\n")
|
||||
.append("### Response:\n")
|
||||
.append(message.getResponse())
|
||||
.append("\n\n");
|
||||
}
|
||||
|
||||
return prompt.append("### Instruction\n")
|
||||
.append(userPrompt)
|
||||
.append("\n\n")
|
||||
.append("### Response:\n")
|
||||
.toString();
|
||||
}
|
||||
};
|
||||
|
||||
private final String label;
|
||||
|
||||
PromptTemplate(String label) {
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
public abstract String buildPrompt(String systemPrompt, String userPrompt, List<Message> history);
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return label;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,51 +1,56 @@
|
|||
package ee.carlrobert.codegpt.completions.you;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.components.Service;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Base64;
|
||||
import java.util.Map;
|
||||
import okhttp3.Callback;
|
||||
import ee.carlrobert.codegpt.completions.you.auth.response.YouAuthenticationResponse;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import okhttp3.Headers;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.RequestBody;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@Service
|
||||
public final class YouApiClient {
|
||||
|
||||
private static final String API_BASE_URL = "https://web.stytch.com/sdk";
|
||||
private static final String publicToken = "public-token-live-507a52ad-7e69-496b-aee0-1c9863c7c819";
|
||||
private static final String API_BASE_URL = "https://you.com/api";
|
||||
|
||||
public static YouApiClient getInstance() {
|
||||
return ApplicationManager.getApplication().getService(YouApiClient.class);
|
||||
}
|
||||
|
||||
public void authenticate(String email, String password, Callback callback) {
|
||||
try {
|
||||
new OkHttpClient()
|
||||
.newCall(new Request.Builder()
|
||||
.url(API_BASE_URL + "/v1/passwords/authenticate")
|
||||
.headers(Headers.of(
|
||||
"content-type", "application/json",
|
||||
"authority", "web.stytch.com",
|
||||
"authorization", "Basic " + Base64.getEncoder().encodeToString((publicToken + ":" + publicToken).getBytes()),
|
||||
"x-sdk-client", "eyJldmVudF9pZCI6ImV2ZW50LWlkLWY5YmU4YWU5LWE3MjctNGFlYy1hNzY0LTk4NDg1NDFkZjcwYSIsImFwcF9zZXNzaW9uX2lkIjoiYXBwLXNlc3Npb24taWQtYjY1NzcwZjMtMWFkMy00YjlhLWFjYzctMzJjNWQyMGMxNGU0IiwicGVyc2lzdGVudF9pZCI6InBlcnNpc3RlbnQtaWQtYzY0M2M0YTMtZDg5MC00ZGJkLTk3YjQtMjY0MmFlODdkMTZhIiwiY2xpZW50X3NlbnRfYXQiOiIyMDIzLTA5LTAxVDIyOjMwOjU1LjIzNFoiLCJ0aW1lem9uZSI6IkV1cm9wZS9UYWxsaW5uIiwiYXBwIjp7ImlkZW50aWZpZXIiOiJ5b3UuY29tIn0sInNkayI6eyJpZGVudGlmaWVyIjoiU3R5dGNoLmpzIEphdmFzY3JpcHQgU0RLIC0gWU9VLkNPTSBERUJVRyBCVUlMRCIsInZlcnNpb24iOiI0LjAuMCJ9fQ==",
|
||||
"x-sdk-parent-host", "https://you.com"
|
||||
))
|
||||
.post(RequestBody.create(new ObjectMapper()
|
||||
.writerWithDefaultPrettyPrinter()
|
||||
.writeValueAsString(Map.of(
|
||||
"email", email,
|
||||
"password", password,
|
||||
"session_duration_minutes", 129_600))
|
||||
.getBytes(StandardCharsets.UTF_8)))
|
||||
.build())
|
||||
.enqueue(callback);
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new RuntimeException("Could not process request", e);
|
||||
public @Nullable YouSubscription getSubscription(YouAuthenticationResponse auth) {
|
||||
var sessionId = auth.getData().getSession().getSessionId();
|
||||
var sessionJwt = auth.getData().getSessionJwt();
|
||||
var request = new Request.Builder()
|
||||
.url(API_BASE_URL + "/payments/orders/subscriptions/current")
|
||||
.header("Accept", "application/json")
|
||||
.header("Cache-Control", "no-cache")
|
||||
.header("User-Agent", "youide CodeGPT")
|
||||
.header("Cookie", (
|
||||
"stytch_session=" + sessionId + "; " +
|
||||
"ydc_stytch_session=" + sessionId + "; " +
|
||||
"stytch_session_jwt=" + sessionJwt + "; " +
|
||||
"ydc_stytch_session_jwt=" + sessionJwt + "; "))
|
||||
.get()
|
||||
.build();
|
||||
|
||||
try (var response = new OkHttpClient().newCall(request).execute()) {
|
||||
var body = response.body();
|
||||
if (body == null || !response.isSuccessful()) {
|
||||
return null;
|
||||
}
|
||||
List<YouSubscription> subscriptions =
|
||||
new ObjectMapper().readValue(body.string(), new TypeReference<>() {
|
||||
});
|
||||
if (subscriptions == null || subscriptions.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return subscriptions.get(0);
|
||||
} catch (IOException ex) {
|
||||
throw new RuntimeException("Could not get You.com subscription", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,34 @@
|
|||
package ee.carlrobert.codegpt.completions.you;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class YouSubscription {
|
||||
|
||||
private final String service;
|
||||
private final String tier;
|
||||
private final String month;
|
||||
|
||||
public YouSubscription(
|
||||
@JsonProperty("service") String service,
|
||||
@JsonProperty("tier") String tier,
|
||||
@JsonProperty("month") String month) {
|
||||
this.service = service;
|
||||
this.tier = tier;
|
||||
this.month = month;
|
||||
}
|
||||
|
||||
public String getService() {
|
||||
return service;
|
||||
}
|
||||
|
||||
public String getTier() {
|
||||
return tier;
|
||||
}
|
||||
|
||||
public String getMonth() {
|
||||
return month;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package ee.carlrobert.codegpt.completions.you;
|
||||
|
||||
import com.intellij.util.messages.Topic;
|
||||
|
||||
public interface YouSubscriptionNotifier {
|
||||
|
||||
Topic<YouSubscriptionNotifier> SUBSCRIPTION_TOPIC =
|
||||
Topic.create("subscriptionTopic", YouSubscriptionNotifier.class);
|
||||
|
||||
void subscribed();
|
||||
}
|
||||
|
|
@ -9,6 +9,7 @@ import ee.carlrobert.codegpt.completions.you.auth.response.YouAuthenticationResp
|
|||
public final class YouUserManager {
|
||||
|
||||
private YouAuthenticationResponse authenticationResponse;
|
||||
private boolean subscribed;
|
||||
|
||||
private YouUserManager() {
|
||||
}
|
||||
|
|
@ -27,14 +28,19 @@ public final class YouUserManager {
|
|||
|
||||
public void clearSession() {
|
||||
authenticationResponse = null;
|
||||
subscribed = false;
|
||||
|
||||
ApplicationManager.getApplication().getMessageBus()
|
||||
.syncPublisher(SignedOutNotifier.SIGNED_OUT_TOPIC)
|
||||
.signedOut();
|
||||
}
|
||||
|
||||
public void setSubscribed(boolean subscribed) {
|
||||
this.subscribed = subscribed;
|
||||
}
|
||||
|
||||
public boolean isSubscribed() {
|
||||
return true; // TODO
|
||||
return subscribed;
|
||||
}
|
||||
|
||||
public boolean isAuthenticated() {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,51 @@
|
|||
package ee.carlrobert.codegpt.completions.you.auth;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.components.Service;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Base64;
|
||||
import java.util.Map;
|
||||
import okhttp3.Callback;
|
||||
import okhttp3.Headers;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.RequestBody;
|
||||
|
||||
@Service
|
||||
public final class YouAuthClient {
|
||||
|
||||
private static final String API_BASE_URL = "https://web.stytch.com/sdk";
|
||||
private static final String publicToken = "public-token-live-507a52ad-7e69-496b-aee0-1c9863c7c819";
|
||||
|
||||
public static YouAuthClient getInstance() {
|
||||
return ApplicationManager.getApplication().getService(YouAuthClient.class);
|
||||
}
|
||||
|
||||
public void authenticate(String email, String password, Callback callback) {
|
||||
try {
|
||||
new OkHttpClient()
|
||||
.newCall(new Request.Builder()
|
||||
.url(API_BASE_URL + "/v1/passwords/authenticate")
|
||||
.headers(Headers.of(
|
||||
"content-type", "application/json",
|
||||
"authority", "web.stytch.com",
|
||||
"authorization", "Basic " + Base64.getEncoder().encodeToString((publicToken + ":" + publicToken).getBytes()),
|
||||
"x-sdk-client", "eyJldmVudF9pZCI6ImV2ZW50LWlkLWY5YmU4YWU5LWE3MjctNGFlYy1hNzY0LTk4NDg1NDFkZjcwYSIsImFwcF9zZXNzaW9uX2lkIjoiYXBwLXNlc3Npb24taWQtYjY1NzcwZjMtMWFkMy00YjlhLWFjYzctMzJjNWQyMGMxNGU0IiwicGVyc2lzdGVudF9pZCI6InBlcnNpc3RlbnQtaWQtYzY0M2M0YTMtZDg5MC00ZGJkLTk3YjQtMjY0MmFlODdkMTZhIiwiY2xpZW50X3NlbnRfYXQiOiIyMDIzLTA5LTAxVDIyOjMwOjU1LjIzNFoiLCJ0aW1lem9uZSI6IkV1cm9wZS9UYWxsaW5uIiwiYXBwIjp7ImlkZW50aWZpZXIiOiJ5b3UuY29tIn0sInNkayI6eyJpZGVudGlmaWVyIjoiU3R5dGNoLmpzIEphdmFzY3JpcHQgU0RLIC0gWU9VLkNPTSBERUJVRyBCVUlMRCIsInZlcnNpb24iOiI0LjAuMCJ9fQ==",
|
||||
"x-sdk-parent-host", "https://you.com"
|
||||
))
|
||||
.post(RequestBody.create(new ObjectMapper()
|
||||
.writerWithDefaultPrettyPrinter()
|
||||
.writeValueAsString(Map.of(
|
||||
"email", email,
|
||||
"password", password,
|
||||
"session_duration_minutes", 129_600))
|
||||
.getBytes(StandardCharsets.UTF_8)))
|
||||
.build())
|
||||
.enqueue(callback);
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new RuntimeException("Could not process request", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ import com.intellij.openapi.application.ApplicationManager;
|
|||
import com.intellij.openapi.components.Service;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import ee.carlrobert.codegpt.completions.you.YouApiClient;
|
||||
import ee.carlrobert.codegpt.completions.you.YouSubscriptionNotifier;
|
||||
import ee.carlrobert.codegpt.completions.you.YouUserManager;
|
||||
import ee.carlrobert.codegpt.completions.you.auth.response.YouAuthenticationResponse;
|
||||
import ee.carlrobert.codegpt.util.OverlayUtils;
|
||||
|
|
@ -19,7 +20,7 @@ import org.jetbrains.annotations.NotNull;
|
|||
public final class YouAuthenticationService {
|
||||
|
||||
private static final Logger LOG = Logger.getInstance(YouAuthenticationService.class);
|
||||
private static final YouApiClient client = YouApiClient.getInstance();
|
||||
private static final YouAuthClient authClient = YouAuthClient.getInstance();
|
||||
|
||||
private YouAuthenticationService() {
|
||||
}
|
||||
|
|
@ -28,8 +29,9 @@ public final class YouAuthenticationService {
|
|||
return ApplicationManager.getApplication().getService(YouAuthenticationService.class);
|
||||
}
|
||||
|
||||
public void signInAsync(String email, String password, AuthenticationHandler authenticationHandler) {
|
||||
client.authenticate(email, password, new AuthenticationCallback(authenticationHandler));
|
||||
public void signInAsync(String email, String password,
|
||||
AuthenticationHandler authenticationHandler) {
|
||||
authClient.authenticate(email, password, new AuthenticationCallback(authenticationHandler));
|
||||
}
|
||||
|
||||
static class AuthenticationCallback implements Callback {
|
||||
|
|
@ -56,11 +58,23 @@ public final class YouAuthenticationService {
|
|||
|
||||
if (response.code() == 200) {
|
||||
try {
|
||||
var authenticationResponse = new ObjectMapper().readValue(body.string(), YouAuthenticationResponse.class);
|
||||
YouUserManager.getInstance().setAuthenticationResponse(authenticationResponse);
|
||||
var messageBus = ApplicationManager.getApplication().getMessageBus();
|
||||
var userManager = YouUserManager.getInstance();
|
||||
|
||||
var authenticationResponse =
|
||||
new ObjectMapper().readValue(body.string(), YouAuthenticationResponse.class);
|
||||
userManager.setAuthenticationResponse(authenticationResponse);
|
||||
authenticationHandler.handleAuthenticated(authenticationResponse);
|
||||
|
||||
ApplicationManager.getApplication().getMessageBus()
|
||||
var subscription =
|
||||
YouApiClient.getInstance().getSubscription(authenticationResponse);
|
||||
var subscribed = subscription != null && "youpro".equals(subscription.getService());
|
||||
userManager.setSubscribed(subscribed);
|
||||
if (subscribed) {
|
||||
messageBus.syncPublisher(YouSubscriptionNotifier.SUBSCRIPTION_TOPIC).subscribed();
|
||||
}
|
||||
|
||||
messageBus
|
||||
.syncPublisher(AuthenticationNotifier.AUTHENTICATION_TOPIC)
|
||||
.authenticationSuccessful();
|
||||
return;
|
||||
|
|
@ -70,7 +84,8 @@ public final class YouAuthenticationService {
|
|||
}
|
||||
|
||||
try {
|
||||
authenticationHandler.handleError(new ObjectMapper().readValue(body.string(), YouAuthenticationError.class));
|
||||
authenticationHandler.handleError(
|
||||
new ObjectMapper().readValue(body.string(), YouAuthenticationError.class));
|
||||
} catch (Throwable ex) {
|
||||
authenticationHandler.handleGenericError();
|
||||
throw new RuntimeException(ex);
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import com.intellij.openapi.application.ApplicationManager;
|
|||
import com.intellij.openapi.components.Service;
|
||||
import ee.carlrobert.codegpt.conversations.message.Message;
|
||||
import ee.carlrobert.codegpt.settings.state.AzureSettingsState;
|
||||
import ee.carlrobert.codegpt.settings.state.LlamaSettingsState;
|
||||
import ee.carlrobert.codegpt.settings.state.OpenAISettingsState;
|
||||
import ee.carlrobert.codegpt.settings.state.SettingsState;
|
||||
import java.time.LocalDateTime;
|
||||
|
|
@ -46,8 +47,10 @@ public final class ConversationService {
|
|||
conversation.setModel("YouCode");
|
||||
} else if (settings.isUseAzureService()) {
|
||||
conversation.setModel(AzureSettingsState.getInstance().getModel());
|
||||
} else {
|
||||
} else if (settings.isUseOpenAIService()) {
|
||||
conversation.setModel(OpenAISettingsState.getInstance().getModel());
|
||||
} else {
|
||||
conversation.setModel(LlamaSettingsState.getInstance().getHuggingFaceModel().getCode());
|
||||
}
|
||||
conversation.setCreatedOn(LocalDateTime.now());
|
||||
conversation.setUpdatedOn(LocalDateTime.now());
|
||||
|
|
@ -64,7 +67,11 @@ public final class ConversationService {
|
|||
conversationsMapping.put(conversation.getClientCode(), conversations);
|
||||
}
|
||||
|
||||
public void saveMessage(String response, Message message, Conversation conversation, boolean isRetry) {
|
||||
public void saveMessage(
|
||||
String response,
|
||||
Message message,
|
||||
Conversation conversation,
|
||||
boolean isRetry) {
|
||||
var conversationMessages = conversation.getMessages();
|
||||
if (isRetry && !conversationMessages.isEmpty()) {
|
||||
var messageToBeSaved = conversationMessages.stream()
|
||||
|
|
@ -122,6 +129,9 @@ public final class ConversationService {
|
|||
if (settings.isUseAzureService()) {
|
||||
return "azure.chat.completion";
|
||||
}
|
||||
if (settings.isUseLlamaService()) {
|
||||
return "llama.chat.completion";
|
||||
}
|
||||
return "you.chat.completion";
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,8 +19,8 @@ import com.intellij.ui.ScrollPaneFactory;
|
|||
import com.intellij.ui.components.JBLabel;
|
||||
import com.intellij.util.ui.AsyncProcessIcon;
|
||||
import com.intellij.util.ui.JBUI;
|
||||
import ee.carlrobert.embedding.CheckedFile;
|
||||
import ee.carlrobert.codegpt.util.file.FileUtils;
|
||||
import ee.carlrobert.embedding.CheckedFile;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.FlowLayout;
|
||||
import java.awt.event.MouseAdapter;
|
||||
|
|
@ -123,7 +123,7 @@ public class FolderStructureTreePanel {
|
|||
panel.add(loadingFilesSpinner);
|
||||
} else {
|
||||
panel.add(new JBLabel("Total size: " +
|
||||
convertFileSize(totalSize) + " ~ " +
|
||||
FileUtils.convertFileSize(totalSize) + " ~ " +
|
||||
(convertLongValue(totalSize / 4)) + " tokens " + " ~ " +
|
||||
new DecimalFormat("#.##").format(((double) (totalSize / 4) / 1000) * 0.0001) + " $"));
|
||||
}
|
||||
|
|
@ -137,7 +137,9 @@ public class FolderStructureTreePanel {
|
|||
}
|
||||
|
||||
private List<VirtualFileImpl> getCheckedVirtualFiles() {
|
||||
return Arrays.stream(checkboxTree.getCheckedNodes(VirtualFileSystemEntry.class, node -> node instanceof VirtualFileImpl))
|
||||
return Arrays.stream(checkboxTree.getCheckedNodes(
|
||||
VirtualFileSystemEntry.class,
|
||||
node -> node instanceof VirtualFileImpl))
|
||||
.map(entry -> (VirtualFileImpl) entry)
|
||||
.collect(toList());
|
||||
}
|
||||
|
|
@ -160,12 +162,15 @@ public class FolderStructureTreePanel {
|
|||
}
|
||||
}
|
||||
|
||||
private void traverseDirectory(@NotNull CheckedTreeNode parentNode, @NotNull VirtualFile projectDirectory) {
|
||||
private void traverseDirectory(@NotNull CheckedTreeNode parentNode,
|
||||
@NotNull VirtualFile projectDirectory) {
|
||||
for (VirtualFile childFile : projectDirectory.getChildren()) {
|
||||
var node = new CheckedTreeNode(childFile);
|
||||
parentNode.add(node);
|
||||
|
||||
if (!parentNode.isChecked() || ignoredFileDirectories.parallelStream().anyMatch(it -> it.equalsIgnoreCase(childFile.getName()))) {
|
||||
var potentiallyIgnored = ignoredFileDirectories.parallelStream()
|
||||
.anyMatch(it -> it.equalsIgnoreCase(childFile.getName()));
|
||||
if (!parentNode.isChecked() || potentiallyIgnored) {
|
||||
node.setChecked(false);
|
||||
}
|
||||
|
||||
|
|
@ -180,7 +185,13 @@ public class FolderStructureTreePanel {
|
|||
private @NotNull CheckboxTree.CheckboxTreeCellRenderer createFileTypesRenderer() {
|
||||
return new CheckboxTree.CheckboxTreeCellRenderer() {
|
||||
@Override
|
||||
public void customizeRenderer(JTree t, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean focus) {
|
||||
public void customizeRenderer(JTree t,
|
||||
Object value,
|
||||
boolean selected,
|
||||
boolean expanded,
|
||||
boolean leaf,
|
||||
int row,
|
||||
boolean focus) {
|
||||
if (!(value instanceof CheckedTreeNode)) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -194,28 +205,18 @@ public class FolderStructureTreePanel {
|
|||
if (userObject instanceof VirtualDirectoryImpl) {
|
||||
getTextRenderer().setIcon(AllIcons.Nodes.Folder);
|
||||
} else {
|
||||
var fileType = FileTypeManager.getInstance().getFileTypeByFile((VirtualFileSystemEntry) userObject);
|
||||
var fileType = FileTypeManager.getInstance()
|
||||
.getFileTypeByFile((VirtualFileSystemEntry) userObject);
|
||||
getTextRenderer().setIcon(fileType.getIcon());
|
||||
getTextRenderer().append(" - " + convertFileSize(((VirtualFileSystemEntry) userObject).getLength()));
|
||||
getTextRenderer().append(
|
||||
" - " + FileUtils.convertFileSize(
|
||||
((VirtualFileSystemEntry) userObject).getLength()));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static String convertFileSize(long fileSizeInBytes) {
|
||||
String[] units = {"B", "KB", "MB", "GB"};
|
||||
int unitIndex = 0;
|
||||
double fileSize = fileSizeInBytes;
|
||||
|
||||
while (fileSize >= 1024 && unitIndex < units.length - 1) {
|
||||
fileSize /= 1024;
|
||||
unitIndex++;
|
||||
}
|
||||
|
||||
return new DecimalFormat("#.##").format(fileSize) + " " + units[unitIndex];
|
||||
}
|
||||
|
||||
private static String convertLongValue(long value) {
|
||||
if (value >= 1_000_000) {
|
||||
return value / 1_000_000 + "M";
|
||||
|
|
|
|||
|
|
@ -1,30 +0,0 @@
|
|||
package ee.carlrobert.codegpt.settings;
|
||||
|
||||
import com.intellij.openapi.ui.ComboBox;
|
||||
import ee.carlrobert.llm.completion.CompletionModel;
|
||||
import java.awt.Component;
|
||||
import javax.swing.JList;
|
||||
import javax.swing.plaf.basic.BasicComboBoxRenderer;
|
||||
|
||||
public class ModelComboBox extends ComboBox<CompletionModel> {
|
||||
|
||||
public ModelComboBox(CompletionModel[] options, CompletionModel selectedModel) {
|
||||
super(options);
|
||||
setSelectedItem(selectedModel);
|
||||
setRenderer(getBasicComboBoxRenderer());
|
||||
}
|
||||
|
||||
private BasicComboBoxRenderer getBasicComboBoxRenderer() {
|
||||
return new BasicComboBoxRenderer() {
|
||||
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
|
||||
super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
|
||||
|
||||
if (value != null) {
|
||||
CompletionModel model = (CompletionModel) value;
|
||||
setText(model.getDescription());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
package ee.carlrobert.codegpt.settings;public class ServiceChangeNotifier {
|
||||
}
|
||||
|
|
@ -1,12 +1,22 @@
|
|||
package ee.carlrobert.codegpt.settings;
|
||||
|
||||
import static java.util.stream.Collectors.toList;
|
||||
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.ui.TitledSeparator;
|
||||
import com.intellij.openapi.ui.ComboBox;
|
||||
import com.intellij.openapi.ui.ComponentValidator;
|
||||
import com.intellij.openapi.ui.ValidationInfo;
|
||||
import com.intellij.openapi.util.SystemInfoRt;
|
||||
import com.intellij.ui.components.JBTextField;
|
||||
import com.intellij.util.ui.FormBuilder;
|
||||
import com.intellij.util.ui.UI;
|
||||
import ee.carlrobert.codegpt.CodeGPTBundle;
|
||||
import ee.carlrobert.codegpt.settings.service.ServiceSelectionForm;
|
||||
import ee.carlrobert.codegpt.settings.service.ServiceType;
|
||||
import ee.carlrobert.codegpt.settings.state.OpenAISettingsState;
|
||||
import ee.carlrobert.codegpt.settings.state.SettingsState;
|
||||
import java.awt.CardLayout;
|
||||
import java.util.Arrays;
|
||||
import javax.swing.DefaultComboBoxModel;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JPanel;
|
||||
|
||||
|
|
@ -14,25 +24,53 @@ public class SettingsComponent {
|
|||
|
||||
private final JPanel mainPanel;
|
||||
private final JBTextField displayNameField;
|
||||
private final ComboBox<ServiceType> serviceComboBox;
|
||||
private final ServiceSelectionForm serviceSelectionForm;
|
||||
private final YouServiceSelectionPanel youServiceSelectionPanel;
|
||||
|
||||
public SettingsComponent(Disposable parentDisposable, SettingsState settings) {
|
||||
serviceSelectionForm = new ServiceSelectionForm(parentDisposable, settings);
|
||||
displayNameField = new JBTextField(settings.getDisplayName(), 20);
|
||||
youServiceSelectionPanel = new YouServiceSelectionPanel(parentDisposable);
|
||||
|
||||
serviceSelectionForm = new ServiceSelectionForm(parentDisposable);
|
||||
var cardLayout = new CardLayout();
|
||||
var cards = new JPanel(cardLayout);
|
||||
cards.add(serviceSelectionForm.getOpenAIServiceSectionPanel(), ServiceType.OPENAI.getCode());
|
||||
cards.add(serviceSelectionForm.getAzureServiceSectionPanel(), ServiceType.AZURE.getCode());
|
||||
cards.add(serviceSelectionForm.getYouServiceSectionPanel(), ServiceType.YOU.getCode());
|
||||
cards.add(serviceSelectionForm.getLlamaServiceSectionPanel(), ServiceType.LLAMA_CPP.getCode());
|
||||
var serviceComboBoxModel = new DefaultComboBoxModel<ServiceType>();
|
||||
serviceComboBoxModel.addAll(Arrays.stream(ServiceType.values())
|
||||
.filter(it -> !"LLAMA_CPP".equals(it.getCode()) || SystemInfoRt.isUnix)
|
||||
.collect(toList()));
|
||||
serviceComboBox = new ComboBox<>(serviceComboBoxModel);
|
||||
serviceComboBox.setSelectedItem(ServiceType.OPENAI);
|
||||
serviceComboBox.setPreferredSize(displayNameField.getPreferredSize());
|
||||
var serviceInputValidator = createInputValidator(parentDisposable, serviceComboBox);
|
||||
serviceInputValidator.revalidate();
|
||||
serviceComboBox.addItemListener(e -> {
|
||||
serviceInputValidator.revalidate();
|
||||
cardLayout.show(cards, ((ServiceType) e.getItem()).getCode());
|
||||
});
|
||||
|
||||
mainPanel = FormBuilder.createFormBuilder()
|
||||
.addComponent(UI.PanelFactory.panel(displayNameField)
|
||||
.withLabel(CodeGPTBundle.get("settingsConfigurable.section.integration.displayNameFieldLabel"))
|
||||
.resizeX(false)
|
||||
.createPanel())
|
||||
.addComponent(new TitledSeparator(CodeGPTBundle.get("settingsConfigurable.section.service.title")))
|
||||
.addComponent(serviceSelectionForm.getForm())
|
||||
.addVerticalGap(8)
|
||||
.addLabeledComponent(
|
||||
CodeGPTBundle.get("settingsConfigurable.displayName.label"),
|
||||
displayNameField)
|
||||
.addLabeledComponent(
|
||||
CodeGPTBundle.get("settingsConfigurable.service.label"),
|
||||
serviceComboBox)
|
||||
.addComponent(cards)
|
||||
.addComponentFillVertically(new JPanel(), 0)
|
||||
.getPanel();
|
||||
}
|
||||
|
||||
public ServiceType getSelectedService() {
|
||||
return serviceComboBox.getItem();
|
||||
}
|
||||
|
||||
public void setSelectedService(ServiceType serviceType) {
|
||||
serviceComboBox.setSelectedItem(serviceType);
|
||||
}
|
||||
|
||||
public JPanel getPanel() {
|
||||
return mainPanel;
|
||||
}
|
||||
|
|
@ -41,18 +79,6 @@ public class SettingsComponent {
|
|||
return displayNameField;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return youServiceSelectionPanel.getEmail();
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
youServiceSelectionPanel.setEmail(email);
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return youServiceSelectionPanel.getPassword();
|
||||
}
|
||||
|
||||
public ServiceSelectionForm getServiceSelectionForm() {
|
||||
return serviceSelectionForm;
|
||||
}
|
||||
|
|
@ -64,4 +90,27 @@ public class SettingsComponent {
|
|||
public void setDisplayName(String displayName) {
|
||||
displayNameField.setText(displayName);
|
||||
}
|
||||
|
||||
private ComponentValidator createInputValidator(
|
||||
Disposable parentDisposable,
|
||||
JComponent component) {
|
||||
var validator = new ComponentValidator(parentDisposable)
|
||||
.withValidator(() -> {
|
||||
if (component instanceof ComboBox) {
|
||||
var selectedItem = ((ComboBox<?>) component).getSelectedItem();
|
||||
if (selectedItem == ServiceType.OPENAI &&
|
||||
OpenAISettingsState.getInstance().isOpenAIQuotaExceeded()) {
|
||||
return new ValidationInfo(
|
||||
CodeGPTBundle.get("settings.openaiQuotaExceeded"),
|
||||
component);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
})
|
||||
.andStartOnFocusLost()
|
||||
.installOn(component);
|
||||
validator.enableValidation();
|
||||
return validator;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,10 @@
|
|||
package ee.carlrobert.codegpt.settings;
|
||||
|
||||
import static ee.carlrobert.codegpt.settings.service.ServiceType.AZURE;
|
||||
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.options.Configurable;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
|
|
@ -7,7 +12,9 @@ import ee.carlrobert.codegpt.CodeGPTBundle;
|
|||
import ee.carlrobert.codegpt.conversations.ConversationsState;
|
||||
import ee.carlrobert.codegpt.credentials.AzureCredentialsManager;
|
||||
import ee.carlrobert.codegpt.credentials.OpenAICredentialsManager;
|
||||
import ee.carlrobert.codegpt.settings.service.ServiceType;
|
||||
import ee.carlrobert.codegpt.settings.state.AzureSettingsState;
|
||||
import ee.carlrobert.codegpt.settings.state.LlamaSettingsState;
|
||||
import ee.carlrobert.codegpt.settings.state.OpenAISettingsState;
|
||||
import ee.carlrobert.codegpt.settings.state.SettingsState;
|
||||
import ee.carlrobert.codegpt.settings.state.YouSettingsState;
|
||||
|
|
@ -49,14 +56,24 @@ public class SettingsConfigurable implements Configurable {
|
|||
var settings = SettingsState.getInstance();
|
||||
var openAISettings = OpenAISettingsState.getInstance();
|
||||
var azureSettings = AzureSettingsState.getInstance();
|
||||
var llamaSettings = LlamaSettingsState.getInstance();
|
||||
|
||||
var serviceSelectionForm = settingsComponent.getServiceSelectionForm();
|
||||
var llamaModelPreferencesForm = serviceSelectionForm.getLlamaModelPreferencesForm();
|
||||
return !settingsComponent.getDisplayName().equals(settings.getDisplayName()) ||
|
||||
isServiceChanged(serviceSelectionForm, settings) ||
|
||||
isServiceChanged(settings) ||
|
||||
openAISettings.isModified(serviceSelectionForm) ||
|
||||
azureSettings.isModified(serviceSelectionForm) ||
|
||||
serviceSelectionForm.isDisplayWebSearchResults() !=
|
||||
YouSettingsState.getInstance().isDisplayWebSearchResults();
|
||||
YouSettingsState.getInstance().isDisplayWebSearchResults() ||
|
||||
|
||||
llamaSettings.isUseCustomModel() != llamaModelPreferencesForm.isUseCustomLlamaModel() ||
|
||||
llamaSettings.getServerPort() != serviceSelectionForm.getLlamaServerPort() ||
|
||||
llamaSettings.getContextSize() != serviceSelectionForm.getContextSize() ||
|
||||
llamaSettings.getHuggingFaceModel() != llamaModelPreferencesForm.getSelectedModel() ||
|
||||
!llamaSettings.getPromptTemplate().equals(llamaModelPreferencesForm.getPromptTemplate()) ||
|
||||
!llamaSettings.getCustomLlamaModelPath()
|
||||
.equals(llamaModelPreferencesForm.getCustomLlamaModelPath());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -65,7 +82,8 @@ public class SettingsConfigurable implements Configurable {
|
|||
var settings = SettingsState.getInstance();
|
||||
var openAISettings = OpenAISettingsState.getInstance();
|
||||
var azureSettings = AzureSettingsState.getInstance();
|
||||
var serviceChanged = isServiceChanged(serviceSelectionForm, settings);
|
||||
var llamaSettings = LlamaSettingsState.getInstance();
|
||||
var serviceChanged = isServiceChanged(settings);
|
||||
var modelChanged = openAISettings.getModel().equals(serviceSelectionForm.getOpenAIModel()) ||
|
||||
azureSettings.getModel().equals(serviceSelectionForm.getAzureModel());
|
||||
|
||||
|
|
@ -80,11 +98,21 @@ public class SettingsConfigurable implements Configurable {
|
|||
.setAzureActiveDirectoryToken(serviceSelectionForm.getAzureActiveDirectoryToken());
|
||||
|
||||
settings.setDisplayName(settingsComponent.getDisplayName());
|
||||
settings.setUseOpenAIService(serviceSelectionForm.isOpenAIServiceSelected());
|
||||
settings.setUseAzureService(serviceSelectionForm.isAzureServiceSelected());
|
||||
settings.setUseYouService(serviceSelectionForm.isYouServiceSelected());
|
||||
// TODO: Store as single enum value
|
||||
settings.setUseOpenAIService(settingsComponent.getSelectedService() == OPENAI);
|
||||
settings.setUseAzureService(settingsComponent.getSelectedService() == ServiceType.AZURE);
|
||||
settings.setUseYouService(settingsComponent.getSelectedService() == ServiceType.YOU);
|
||||
YouSettingsState.getInstance()
|
||||
.setDisplayWebSearchResults(serviceSelectionForm.isDisplayWebSearchResults());
|
||||
settings.setUseLlamaService(settingsComponent.getSelectedService() == ServiceType.LLAMA_CPP);
|
||||
|
||||
var llamaModelPreferencesForm = serviceSelectionForm.getLlamaModelPreferencesForm();
|
||||
llamaSettings.setCustomLlamaModelPath(llamaModelPreferencesForm.getCustomLlamaModelPath());
|
||||
llamaSettings.setHuggingFaceModel(llamaModelPreferencesForm.getSelectedModel());
|
||||
llamaSettings.setUseCustomModel(llamaModelPreferencesForm.isUseCustomLlamaModel());
|
||||
llamaSettings.setPromptTemplate(llamaModelPreferencesForm.getPromptTemplate());
|
||||
llamaSettings.setServerPort(serviceSelectionForm.getLlamaServerPort());
|
||||
llamaSettings.setContextSize(serviceSelectionForm.getContextSize());
|
||||
|
||||
openAISettings.apply(serviceSelectionForm);
|
||||
azureSettings.apply(serviceSelectionForm);
|
||||
|
|
@ -93,7 +121,7 @@ public class SettingsConfigurable implements Configurable {
|
|||
resetActiveTab();
|
||||
if (serviceChanged) {
|
||||
TelemetryAction.SETTINGS_CHANGED.createActionMessage()
|
||||
.property("service", getServiceCode(serviceSelectionForm))
|
||||
.property("service", getServiceCode())
|
||||
.send();
|
||||
}
|
||||
}
|
||||
|
|
@ -104,15 +132,32 @@ public class SettingsConfigurable implements Configurable {
|
|||
var settings = SettingsState.getInstance();
|
||||
var openAISettings = OpenAISettingsState.getInstance();
|
||||
var azureSettings = AzureSettingsState.getInstance();
|
||||
var llamaSettings = LlamaSettingsState.getInstance();
|
||||
var serviceSelectionForm = settingsComponent.getServiceSelectionForm();
|
||||
|
||||
settingsComponent.setEmail(settings.getEmail());
|
||||
// settingsComponent.setEmail(settings.getEmail());
|
||||
settingsComponent.setDisplayName(settings.getDisplayName());
|
||||
|
||||
serviceSelectionForm.setOpenAIServiceSelected(settings.isUseOpenAIService());
|
||||
serviceSelectionForm.setAzureServiceSelected(settings.isUseAzureService());
|
||||
serviceSelectionForm.setYouServiceSelected(settings.isUseYouService());
|
||||
|
||||
// TODO
|
||||
if (settings.isUseOpenAIService()) {
|
||||
settingsComponent.setSelectedService(OPENAI);
|
||||
}
|
||||
if (settings.isUseAzureService()) {
|
||||
settingsComponent.setSelectedService(ServiceType.AZURE);
|
||||
}
|
||||
if (settings.isUseYouService()) {
|
||||
settingsComponent.setSelectedService(ServiceType.YOU);
|
||||
}
|
||||
if (settings.isUseLlamaService()) {
|
||||
settingsComponent.setSelectedService(ServiceType.LLAMA_CPP);
|
||||
}
|
||||
var llamaModelPreferencesForm = serviceSelectionForm.getLlamaModelPreferencesForm();
|
||||
llamaModelPreferencesForm.setSelectedModel(llamaSettings.getHuggingFaceModel());
|
||||
llamaModelPreferencesForm.setCustomLlamaModelPath(llamaSettings.getCustomLlamaModelPath());
|
||||
llamaModelPreferencesForm.setUseCustomLlamaModel(llamaSettings.isUseCustomModel());
|
||||
llamaModelPreferencesForm.setPromptTemplate(llamaSettings.getPromptTemplate());
|
||||
serviceSelectionForm.setLlamaServerPort(llamaSettings.getServerPort());
|
||||
serviceSelectionForm.setContextSize(llamaSettings.getContextSize());
|
||||
openAISettings.reset(serviceSelectionForm);
|
||||
azureSettings.reset(serviceSelectionForm);
|
||||
|
||||
|
|
@ -128,12 +173,11 @@ public class SettingsConfigurable implements Configurable {
|
|||
settingsComponent = null;
|
||||
}
|
||||
|
||||
private boolean isServiceChanged(
|
||||
ServiceSelectionForm serviceSelectionForm,
|
||||
SettingsState settings) {
|
||||
return serviceSelectionForm.isOpenAIServiceSelected() != settings.isUseOpenAIService() ||
|
||||
serviceSelectionForm.isAzureServiceSelected() != settings.isUseAzureService() ||
|
||||
serviceSelectionForm.isYouServiceSelected() != settings.isUseYouService();
|
||||
private boolean isServiceChanged(SettingsState settings) {
|
||||
return (settingsComponent.getSelectedService() == OPENAI) != settings.isUseOpenAIService() ||
|
||||
(settingsComponent.getSelectedService() == AZURE) != settings.isUseAzureService() ||
|
||||
(settingsComponent.getSelectedService() == YOU) != settings.isUseYouService() ||
|
||||
(settingsComponent.getSelectedService() == LLAMA_CPP) != settings.isUseLlamaService();
|
||||
}
|
||||
|
||||
private void resetActiveTab() {
|
||||
|
|
@ -146,16 +190,19 @@ public class SettingsConfigurable implements Configurable {
|
|||
project.getService(StandardChatToolWindowContentManager.class).resetActiveTab();
|
||||
}
|
||||
|
||||
private String getServiceCode(ServiceSelectionForm serviceSelectionForm) {
|
||||
if (serviceSelectionForm.isOpenAIServiceSelected()) {
|
||||
private String getServiceCode() {
|
||||
if (settingsComponent.getSelectedService() == OPENAI) {
|
||||
return "openai";
|
||||
}
|
||||
if (serviceSelectionForm.isAzureServiceSelected()) {
|
||||
if (settingsComponent.getSelectedService() == AZURE) {
|
||||
return "azure";
|
||||
}
|
||||
if (serviceSelectionForm.isYouServiceSelected()) {
|
||||
if (settingsComponent.getSelectedService() == YOU) {
|
||||
return "you";
|
||||
}
|
||||
if (settingsComponent.getSelectedService() == LLAMA_CPP) {
|
||||
return "llama.cpp";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,7 +38,8 @@ public class AdvancedSettingsComponent {
|
|||
proxyTypeComboBox.setSelectedItem(advancedSettings.getProxyType());
|
||||
proxyHostField = new JBTextField(advancedSettings.getProxyHost(), 20);
|
||||
proxyPortField = new PortField();
|
||||
proxyAuthCheckbox = new JBCheckBox(CodeGPTBundle.get("advancedSettingsConfigurable.section.proxy.authCheckBoxField.label"));
|
||||
proxyAuthCheckbox = new JBCheckBox(CodeGPTBundle.get(
|
||||
"advancedSettingsConfigurable.proxy.authCheckBoxField.label"));
|
||||
proxyAuthUsername = new JBTextField(20);
|
||||
proxyAuthUsername.setEnabled(advancedSettings.isProxyAuthSelected());
|
||||
proxyAuthPassword = new JBPasswordField();
|
||||
|
|
@ -52,10 +53,11 @@ public class AdvancedSettingsComponent {
|
|||
readTimeoutField = new PortField(advancedSettings.getReadTimeout());
|
||||
|
||||
mainPanel = FormBuilder.createFormBuilder()
|
||||
.addComponent(new TitledSeparator(CodeGPTBundle.get("advancedSettingsConfigurable.section.proxy.title")))
|
||||
.addComponent(new TitledSeparator(CodeGPTBundle.get(
|
||||
"advancedSettingsConfigurable.proxy.title")))
|
||||
.addComponent(createProxySettingsForm())
|
||||
.addVerticalGap(4)
|
||||
.addComponent(new TitledSeparator("Connection Settings"))
|
||||
.addComponent(new TitledSeparator(CodeGPTBundle.get("advancedSettingsConfigurable.connectionSettings.title")))
|
||||
.addComponent(createConnectionSettingsForm())
|
||||
.addComponentFillVertically(new JPanel(), 0)
|
||||
.getPanel();
|
||||
|
|
@ -63,8 +65,8 @@ public class AdvancedSettingsComponent {
|
|||
|
||||
private JPanel createConnectionSettingsForm() {
|
||||
var panel = FormBuilder.createFormBuilder()
|
||||
.addLabeledComponent("Connection timeout (s):", connectionTimeoutField)
|
||||
.addLabeledComponent("Read timeout (s):", readTimeoutField)
|
||||
.addLabeledComponent(CodeGPTBundle.get("advancedSettingsConfigurable.connectionSettings.connectionTimeout.label"), connectionTimeoutField)
|
||||
.addLabeledComponent(CodeGPTBundle.get("advancedSettingsConfigurable.connectionSettings.readTimeout.label"), readTimeoutField)
|
||||
.getPanel();
|
||||
panel.setBorder(JBUI.Borders.emptyLeft(16));
|
||||
return panel;
|
||||
|
|
@ -145,15 +147,15 @@ public class AdvancedSettingsComponent {
|
|||
|
||||
var proxyTypePanel = SwingUtils.createPanel(
|
||||
proxyTypeComboBox,
|
||||
CodeGPTBundle.get("advancedSettingsConfigurable.section.proxy.typeComboBoxField.label"),
|
||||
CodeGPTBundle.get("advancedSettingsConfigurable.proxy.typeComboBoxField.label"),
|
||||
false);
|
||||
var proxyHostPanel = SwingUtils.createPanel(
|
||||
proxyHostField,
|
||||
CodeGPTBundle.get("advancedSettingsConfigurable.section.proxy.hostField.label"),
|
||||
CodeGPTBundle.get("advancedSettingsConfigurable.proxy.hostField.label"),
|
||||
false);
|
||||
var proxyPortPanel = SwingUtils.createPanel(
|
||||
proxyPortField,
|
||||
CodeGPTBundle.get("advancedSettingsConfigurable.section.proxy.portField.label"),
|
||||
CodeGPTBundle.get("advancedSettingsConfigurable.proxy.portField.label"),
|
||||
false);
|
||||
SwingUtils.setEqualLabelWidths(proxyTypePanel, proxyHostPanel);
|
||||
SwingUtils.setEqualLabelWidths(proxyPortPanel, proxyHostPanel);
|
||||
|
|
@ -166,10 +168,10 @@ public class AdvancedSettingsComponent {
|
|||
.createPanel());
|
||||
|
||||
var proxyUsernamePanel = SwingUtils.createPanel(proxyAuthUsername,
|
||||
CodeGPTBundle.get("advancedSettingsConfigurable.section.proxy.usernameField.label"),
|
||||
CodeGPTBundle.get("advancedSettingsConfigurable.proxy.usernameField.label"),
|
||||
false);
|
||||
var proxyPasswordPanel = SwingUtils.createPanel(proxyAuthPassword,
|
||||
CodeGPTBundle.get("advancedSettingsConfigurable.section.proxy.passwordField.label"),
|
||||
CodeGPTBundle.get("advancedSettingsConfigurable.proxy.passwordField.label"),
|
||||
false);
|
||||
SwingUtils.setEqualLabelWidths(proxyPasswordPanel, proxyUsernamePanel);
|
||||
|
||||
|
|
|
|||
|
|
@ -180,10 +180,10 @@ public class ConfigurationComponent {
|
|||
try {
|
||||
var value = Double.parseDouble(valueText);
|
||||
if (value > 1.0 || value < 0.0) {
|
||||
return new ValidationInfo("Value must be between 0 and 1.", component);
|
||||
return new ValidationInfo(CodeGPTBundle.get("validation.error.mustBeBetweenZeroAndOne"), component);
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
return new ValidationInfo("Value must be number.", component);
|
||||
return new ValidationInfo(CodeGPTBundle.get("validation.error.mustBeNumber"), component);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,98 @@
|
|||
package ee.carlrobert.codegpt.settings.service;
|
||||
|
||||
import static java.lang.String.format;
|
||||
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.progress.ProgressIndicator;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.progress.Task;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import ee.carlrobert.codegpt.CodeGPTBundle;
|
||||
import ee.carlrobert.codegpt.completions.HuggingFaceModel;
|
||||
import ee.carlrobert.codegpt.util.DownloadingUtils;
|
||||
import ee.carlrobert.codegpt.util.file.FileUtils;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
import javax.swing.DefaultComboBoxModel;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class DownloadModelAction extends AnAction {
|
||||
|
||||
private static final Logger LOG = Logger.getInstance(DownloadModelAction.class);
|
||||
|
||||
private final Consumer<ProgressIndicator> onDownload;
|
||||
private final Runnable onDownloaded;
|
||||
private final Consumer<Exception> onFailed;
|
||||
private final Consumer<String> onUpdateProgress;
|
||||
private final DefaultComboBoxModel<HuggingFaceModel> comboBoxModel;
|
||||
|
||||
public DownloadModelAction(
|
||||
Consumer<ProgressIndicator> onDownload,
|
||||
Runnable onDownloaded,
|
||||
Consumer<Exception> onFailed,
|
||||
Consumer<String> onUpdateProgress,
|
||||
DefaultComboBoxModel<HuggingFaceModel> comboBoxModel) {
|
||||
this.onDownload = onDownload;
|
||||
this.onDownloaded = onDownloaded;
|
||||
this.onFailed = onFailed;
|
||||
this.onUpdateProgress = onUpdateProgress;
|
||||
this.comboBoxModel = comboBoxModel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(@NotNull AnActionEvent e) {
|
||||
ProgressManager.getInstance().run(new DownloadBackgroundTask(e.getProject()));
|
||||
}
|
||||
|
||||
class DownloadBackgroundTask extends Task.Backgroundable {
|
||||
|
||||
DownloadBackgroundTask(Project project) {
|
||||
super(project, CodeGPTBundle.get("settingsConfigurable.service.llama.progress.downloadingModel.title"), true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(@NotNull ProgressIndicator indicator) {
|
||||
var model = (HuggingFaceModel) comboBoxModel.getSelectedItem();
|
||||
URL url = model.getFileURL();
|
||||
ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
|
||||
ScheduledFuture<?> progressUpdateScheduler = null;
|
||||
|
||||
try {
|
||||
onDownload.accept(indicator);
|
||||
|
||||
indicator.setIndeterminate(false);
|
||||
indicator.setText(format(CodeGPTBundle.get("settingsConfigurable.service.llama.progress.downloadingModelIndicator.text"), model.getFileName()));
|
||||
|
||||
long fileSize = url.openConnection().getContentLengthLong();
|
||||
long[] bytesRead = {0};
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
||||
progressUpdateScheduler = executorService.scheduleAtFixedRate(() ->
|
||||
onUpdateProgress.accept(
|
||||
DownloadingUtils.getFormattedDownloadProgress(startTime, fileSize, bytesRead[0])),
|
||||
0, 1, TimeUnit.SECONDS);
|
||||
FileUtils.copyFileWithProgress(model.getFileName(), url, bytesRead, fileSize, indicator);
|
||||
} catch (IOException ex) {
|
||||
LOG.error("Unable to open connection", ex);
|
||||
onFailed.accept(ex);
|
||||
} finally {
|
||||
if (progressUpdateScheduler != null) {
|
||||
progressUpdateScheduler.cancel(true);
|
||||
}
|
||||
executorService.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess() {
|
||||
onDownloaded.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,442 @@
|
|||
package ee.carlrobert.codegpt.settings.service;
|
||||
|
||||
import static java.util.stream.Collectors.toList;
|
||||
|
||||
import com.intellij.icons.AllIcons.Actions;
|
||||
import com.intellij.icons.AllIcons.General;
|
||||
import com.intellij.ide.HelpTooltip;
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
|
||||
import com.intellij.openapi.progress.ProgressIndicator;
|
||||
import com.intellij.openapi.ui.ComboBox;
|
||||
import com.intellij.openapi.ui.TextBrowseFolderListener;
|
||||
import com.intellij.openapi.ui.TextFieldWithBrowseButton;
|
||||
import com.intellij.openapi.ui.panel.ComponentPanelBuilder;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.ui.EnumComboBoxModel;
|
||||
import com.intellij.ui.components.AnActionLink;
|
||||
import com.intellij.ui.components.JBCheckBox;
|
||||
import com.intellij.ui.components.JBLabel;
|
||||
import com.intellij.util.ui.FormBuilder;
|
||||
import com.intellij.util.ui.JBUI;
|
||||
import ee.carlrobert.codegpt.CodeGPTBundle;
|
||||
import ee.carlrobert.codegpt.CodeGPTPlugin;
|
||||
import ee.carlrobert.codegpt.completions.HuggingFaceModel;
|
||||
import ee.carlrobert.codegpt.completions.llama.LlamaModel;
|
||||
import ee.carlrobert.codegpt.completions.llama.LlamaServerAgent;
|
||||
import ee.carlrobert.codegpt.completions.llama.PromptTemplate;
|
||||
import ee.carlrobert.codegpt.settings.state.LlamaSettingsState;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.FlowLayout;
|
||||
import java.io.File;
|
||||
import java.util.Map;
|
||||
import javax.swing.Box;
|
||||
import javax.swing.DefaultComboBoxModel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.SwingUtilities;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class LlamaModelPreferencesForm {
|
||||
|
||||
private static final Map<Integer, Map<Integer, ModelDetails>> modelDetailsMap = Map.of(
|
||||
7, Map.of(
|
||||
3, new ModelDetails(3.30, 5.80),
|
||||
4, new ModelDetails(4.08, 6.58),
|
||||
5, new ModelDetails(4.78, 7.28)),
|
||||
13, Map.of(
|
||||
3, new ModelDetails(6.34, 8.84),
|
||||
4, new ModelDetails(7.87, 10.37),
|
||||
5, new ModelDetails(9.23, 11.73)),
|
||||
34, Map.of(
|
||||
3, new ModelDetails(16.28, 18.78),
|
||||
4, new ModelDetails(20.22, 22.72),
|
||||
5, new ModelDetails(23.84, 26.34)));
|
||||
|
||||
private final TextFieldWithBrowseButton customModelPathBrowserButton;
|
||||
private final ComboBox<LlamaModel> modelComboBox;
|
||||
private final ComboBox<ModelSize> modelSizeComboBox;
|
||||
private final ComboBox<HuggingFaceModel> huggingFaceModelComboBox;
|
||||
private final ComboBox<PromptTemplate> promptTemplateComboBox;
|
||||
private final JBLabel modelExistsIcon;
|
||||
private final DefaultComboBoxModel<HuggingFaceModel> huggingFaceComboBoxModel;
|
||||
private final JBCheckBox useCustomModelCheckBox;
|
||||
private final JBLabel helpIcon;
|
||||
private final JPanel downloadModelActionLinkWrapper;
|
||||
private final JBLabel progressLabel;
|
||||
private final JBLabel modelDetailsLabel;
|
||||
|
||||
public TextFieldWithBrowseButton getCustomModelPathBrowserButton() {
|
||||
return customModelPathBrowserButton;
|
||||
}
|
||||
|
||||
public ComboBox<HuggingFaceModel> getHuggingFaceModelComboBox() {
|
||||
return huggingFaceModelComboBox;
|
||||
}
|
||||
|
||||
public LlamaModelPreferencesForm() {
|
||||
var llamaServerAgent = ApplicationManager.getApplication().getService(LlamaServerAgent.class);
|
||||
var llamaSettings = LlamaSettingsState.getInstance();
|
||||
customModelPathBrowserButton = createCustomModelPathBrowseButton(
|
||||
llamaSettings.isUseCustomModel() && !llamaServerAgent.isServerRunning());
|
||||
customModelPathBrowserButton.setText(llamaSettings.getCustomLlamaModelPath());
|
||||
progressLabel = new JBLabel("");
|
||||
progressLabel.setBorder(JBUI.Borders.emptyLeft(2));
|
||||
progressLabel.setFont(JBUI.Fonts.smallFont());
|
||||
modelExistsIcon = new JBLabel(Actions.Commit);
|
||||
modelExistsIcon.setVisible(isModelExists(llamaSettings.getHuggingFaceModel()));
|
||||
helpIcon = new JBLabel(General.ContextHelp);
|
||||
huggingFaceComboBoxModel = new DefaultComboBoxModel<>();
|
||||
var llm = llamaSettings.getHuggingFaceModel();
|
||||
var llamaModel = LlamaModel.findByHuggingFaceModel(llm);
|
||||
|
||||
var selectableModels = llamaModel.getHuggingFaceModels().stream()
|
||||
.filter(model -> model.getParameterSize() == llm.getParameterSize())
|
||||
.collect(toList());
|
||||
huggingFaceComboBoxModel.addAll(selectableModels);
|
||||
huggingFaceComboBoxModel.setSelectedItem(selectableModels.get(0));
|
||||
downloadModelActionLinkWrapper = new JPanel(new BorderLayout());
|
||||
downloadModelActionLinkWrapper.setBorder(JBUI.Borders.emptyLeft(2));
|
||||
downloadModelActionLinkWrapper.add(
|
||||
createDownloadModelLink(
|
||||
progressLabel,
|
||||
downloadModelActionLinkWrapper,
|
||||
huggingFaceComboBoxModel),
|
||||
BorderLayout.WEST);
|
||||
modelDetailsLabel = new JBLabel();
|
||||
huggingFaceModelComboBox = createHuggingFaceComboBox(
|
||||
huggingFaceComboBoxModel,
|
||||
modelExistsIcon,
|
||||
modelDetailsLabel,
|
||||
downloadModelActionLinkWrapper);
|
||||
huggingFaceModelComboBox.setEnabled(!llamaServerAgent.isServerRunning());
|
||||
var modelSizeComboBoxModel = new DefaultComboBoxModel<ModelSize>();
|
||||
var initialModelSizes = llamaModel.getSortedUniqueModelSizes().stream()
|
||||
.map(ModelSize::new)
|
||||
.collect(toList());
|
||||
modelSizeComboBoxModel.addAll(initialModelSizes);
|
||||
modelSizeComboBoxModel.setSelectedItem(initialModelSizes.get(0));
|
||||
var modelComboBoxModel = new EnumComboBoxModel<>(LlamaModel.class);
|
||||
modelComboBox = createModelComboBox(modelComboBoxModel, llamaModel, modelSizeComboBoxModel);
|
||||
modelComboBox.setEnabled(!llamaServerAgent.isServerRunning());
|
||||
modelSizeComboBox = createModelSizeComboBox(
|
||||
modelComboBoxModel,
|
||||
modelSizeComboBoxModel,
|
||||
huggingFaceComboBoxModel);
|
||||
modelSizeComboBox.setEnabled(initialModelSizes.size() > 1 && !llamaServerAgent.isServerRunning());
|
||||
promptTemplateComboBox = new ComboBox<>(new EnumComboBoxModel<>(PromptTemplate.class));
|
||||
promptTemplateComboBox.setSelectedItem(llamaSettings.getPromptTemplate());
|
||||
promptTemplateComboBox.setEnabled(
|
||||
llamaSettings.isUseCustomModel() && !llamaServerAgent.isServerRunning());
|
||||
promptTemplateComboBox.setPreferredSize(modelComboBox.getPreferredSize());
|
||||
useCustomModelCheckBox = new JBCheckBox(CodeGPTBundle.get(
|
||||
"settingsConfigurable.service.llama.useCustomModel.label"), llamaSettings.isUseCustomModel());
|
||||
useCustomModelCheckBox.setEnabled(!llamaServerAgent.isServerRunning());
|
||||
useCustomModelCheckBox.addChangeListener(e -> {
|
||||
var selected = ((JBCheckBox) e.getSource()).isSelected();
|
||||
customModelPathBrowserButton.setEnabled(selected && !llamaServerAgent.isServerRunning());
|
||||
promptTemplateComboBox.setEnabled(selected && !llamaServerAgent.isServerRunning());
|
||||
modelComboBox.setEnabled(!selected);
|
||||
modelSizeComboBox.setEnabled((!selected));
|
||||
huggingFaceModelComboBox.setEnabled((!selected));
|
||||
});
|
||||
}
|
||||
|
||||
public JPanel getForm() {
|
||||
var customModelHelpText = ComponentPanelBuilder.createCommentComponent(
|
||||
CodeGPTBundle.get("settingsConfigurable.service.llama.customModelPath.comment"),
|
||||
true);
|
||||
customModelHelpText.setBorder(JBUI.Borders.empty(0, 4));
|
||||
var quantizationHelpText = ComponentPanelBuilder.createCommentComponent(
|
||||
CodeGPTBundle.get("settingsConfigurable.service.llama.quantization.comment"),
|
||||
true);
|
||||
quantizationHelpText.setBorder(JBUI.Borders.empty(0, 4));
|
||||
|
||||
var modelComboBoxWrapper = new JPanel(new FlowLayout(FlowLayout.LEADING, 0, 0));
|
||||
modelComboBoxWrapper.add(modelComboBox);
|
||||
modelComboBoxWrapper.add(Box.createHorizontalStrut(8));
|
||||
modelComboBoxWrapper.add(helpIcon);
|
||||
modelComboBoxWrapper.add(Box.createHorizontalStrut(4));
|
||||
modelComboBoxWrapper.add(modelExistsIcon);
|
||||
|
||||
var huggingFaceModelComboBoxWrapper = new JPanel(new FlowLayout(FlowLayout.LEADING, 0, 0));
|
||||
huggingFaceModelComboBoxWrapper.add(huggingFaceModelComboBox);
|
||||
huggingFaceModelComboBoxWrapper.add(Box.createHorizontalStrut(8));
|
||||
huggingFaceModelComboBoxWrapper.add(modelDetailsLabel);
|
||||
|
||||
return FormBuilder.createFormBuilder()
|
||||
.addLabeledComponent(CodeGPTBundle.get("settingsConfigurable.shared.model.label"), modelComboBoxWrapper)
|
||||
.addLabeledComponent(CodeGPTBundle.get("settingsConfigurable.service.llama.modelSize.label"), modelSizeComboBox)
|
||||
.addLabeledComponent(CodeGPTBundle.get("settingsConfigurable.service.llama.quantization.label"), huggingFaceModelComboBoxWrapper)
|
||||
.addComponentToRightColumn(quantizationHelpText)
|
||||
.addComponentToRightColumn(downloadModelActionLinkWrapper)
|
||||
.addComponentToRightColumn(progressLabel)
|
||||
.addVerticalGap(8)
|
||||
.addComponent(useCustomModelCheckBox)
|
||||
.addLabeledComponent(CodeGPTBundle.get("settingsConfigurable.service.llama.promptTemplate.label"), promptTemplateComboBox)
|
||||
.addLabeledComponent(CodeGPTBundle.get("settingsConfigurable.service.llama.customModelPath.label"), customModelPathBrowserButton)
|
||||
.addComponentToRightColumn(customModelHelpText)
|
||||
.addVerticalGap(4)
|
||||
.getPanel();
|
||||
}
|
||||
|
||||
public void enableFields(boolean enabled) {
|
||||
modelComboBox.setEnabled(enabled);
|
||||
modelSizeComboBox.setEnabled(enabled);
|
||||
huggingFaceModelComboBox.setEnabled(enabled);
|
||||
useCustomModelCheckBox.setEnabled(enabled);
|
||||
promptTemplateComboBox.setEnabled(enabled && useCustomModelCheckBox.isSelected());
|
||||
customModelPathBrowserButton.setEnabled(enabled && useCustomModelCheckBox.isSelected());
|
||||
}
|
||||
|
||||
private static class ModelDetails {
|
||||
|
||||
double fileSize;
|
||||
double maxRAMRequired;
|
||||
|
||||
public ModelDetails(double fileSize, double maxRAMRequired) {
|
||||
this.fileSize = fileSize;
|
||||
this.maxRAMRequired = maxRAMRequired;
|
||||
}
|
||||
}
|
||||
|
||||
private String getHuggingFaceModelDetailsHtml(HuggingFaceModel model) {
|
||||
int parameterSize = model.getParameterSize();
|
||||
int quantization = model.getQuantization();
|
||||
|
||||
if (!modelDetailsMap.containsKey(parameterSize)) {
|
||||
return "";
|
||||
}
|
||||
|
||||
ModelDetails details = modelDetailsMap.get(parameterSize).get(quantization);
|
||||
if (details == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return String.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>"
|
||||
+ "</html>", details.fileSize, details.maxRAMRequired);
|
||||
}
|
||||
|
||||
public void setSelectedModel(HuggingFaceModel model) {
|
||||
huggingFaceComboBoxModel.setSelectedItem(model);
|
||||
}
|
||||
|
||||
public HuggingFaceModel getSelectedModel() {
|
||||
return (HuggingFaceModel) huggingFaceComboBoxModel.getSelectedItem();
|
||||
}
|
||||
|
||||
public void setCustomLlamaModelPath(String modelPath) {
|
||||
customModelPathBrowserButton.setText(modelPath);
|
||||
}
|
||||
|
||||
public String getCustomLlamaModelPath() {
|
||||
return customModelPathBrowserButton.getText();
|
||||
}
|
||||
|
||||
public void setUseCustomLlamaModel(boolean useCustomLlamaModel) {
|
||||
useCustomModelCheckBox.setSelected(useCustomLlamaModel);
|
||||
}
|
||||
|
||||
public boolean isUseCustomLlamaModel() {
|
||||
return useCustomModelCheckBox.isSelected();
|
||||
}
|
||||
|
||||
public void setPromptTemplate(PromptTemplate promptTemplate) {
|
||||
promptTemplateComboBox.setSelectedItem(promptTemplate);
|
||||
}
|
||||
|
||||
public PromptTemplate getPromptTemplate() {
|
||||
return promptTemplateComboBox.getItem();
|
||||
}
|
||||
|
||||
private ComboBox<LlamaModel> createModelComboBox(
|
||||
EnumComboBoxModel<LlamaModel> llamaModelEnumComboBoxModel,
|
||||
LlamaModel llamaModel,
|
||||
DefaultComboBoxModel<ModelSize> modelSizeComboBoxModel) {
|
||||
var comboBox = new ComboBox<>(llamaModelEnumComboBoxModel);
|
||||
comboBox.setPreferredSize(new Dimension(280, comboBox.getPreferredSize().height));
|
||||
comboBox.setSelectedItem(llamaModel);
|
||||
comboBox.addItemListener(e -> {
|
||||
var selectedModel = (LlamaModel) e.getItem();
|
||||
var modelSizes = selectedModel.getSortedUniqueModelSizes().stream()
|
||||
.map(ModelSize::new)
|
||||
.collect(toList());
|
||||
|
||||
modelSizeComboBoxModel.removeAllElements();
|
||||
modelSizeComboBoxModel.addAll(modelSizes);
|
||||
modelSizeComboBoxModel.setSelectedItem(modelSizes.get(0));
|
||||
modelSizeComboBox.setEnabled(modelSizes.size() > 1);
|
||||
|
||||
var huggingFaceModels = selectedModel.getHuggingFaceModels().stream()
|
||||
.filter(model -> {
|
||||
var size = ((ModelSize) modelSizeComboBoxModel.getSelectedItem()).getSize();
|
||||
return size == model.getParameterSize();
|
||||
})
|
||||
.collect(toList());
|
||||
|
||||
huggingFaceComboBoxModel.removeAllElements();
|
||||
huggingFaceComboBoxModel.addAll(huggingFaceModels);
|
||||
huggingFaceComboBoxModel.setSelectedItem(huggingFaceModels.get(0));
|
||||
});
|
||||
return comboBox;
|
||||
}
|
||||
|
||||
private ComboBox<ModelSize> createModelSizeComboBox(
|
||||
EnumComboBoxModel<LlamaModel> llamaModelComboBoxModel,
|
||||
DefaultComboBoxModel<ModelSize> modelSizeComboBoxModel,
|
||||
DefaultComboBoxModel<HuggingFaceModel> huggingFaceComboBoxModel) {
|
||||
var comboBox = new ComboBox<>(modelSizeComboBoxModel);
|
||||
comboBox.setPreferredSize(modelComboBox.getPreferredSize());
|
||||
comboBox.setSelectedItem(modelSizeComboBoxModel.getSelectedItem());
|
||||
comboBox.addItemListener(e -> {
|
||||
var selectedModel = llamaModelComboBoxModel.getSelectedItem();
|
||||
var models = selectedModel.getHuggingFaceModels().stream()
|
||||
.filter(model -> {
|
||||
var selectedModelSize = (ModelSize) modelSizeComboBoxModel.getSelectedItem();
|
||||
return selectedModelSize != null &&
|
||||
selectedModelSize.getSize() == model.getParameterSize();
|
||||
})
|
||||
.collect(toList());
|
||||
if (!models.isEmpty()) {
|
||||
huggingFaceComboBoxModel.removeAllElements();
|
||||
huggingFaceComboBoxModel.addAll(models);
|
||||
huggingFaceComboBoxModel.setSelectedItem(models.get(0));
|
||||
}
|
||||
});
|
||||
return comboBox;
|
||||
}
|
||||
|
||||
private ComboBox<HuggingFaceModel> createHuggingFaceComboBox(
|
||||
DefaultComboBoxModel<HuggingFaceModel> huggingFaceComboBoxModel,
|
||||
JBLabel modelExistsIcon,
|
||||
JBLabel modelDetailsLabel,
|
||||
JPanel downloadModelActionLinkWrapper) {
|
||||
var comboBox = new ComboBox<>(huggingFaceComboBoxModel);
|
||||
comboBox.addItemListener(e -> {
|
||||
var selectedModel = (HuggingFaceModel) e.getItem();
|
||||
var modelExists = isModelExists(selectedModel);
|
||||
|
||||
updateModelHelpTooltip(selectedModel);
|
||||
modelDetailsLabel.setText(getHuggingFaceModelDetailsHtml(selectedModel));
|
||||
modelExistsIcon.setVisible(modelExists);
|
||||
downloadModelActionLinkWrapper.setVisible(!modelExists);
|
||||
});
|
||||
return comboBox;
|
||||
}
|
||||
|
||||
private TextFieldWithBrowseButton createCustomModelPathBrowseButton(boolean enabled) {
|
||||
var browseButton = new TextFieldWithBrowseButton();
|
||||
browseButton.setEnabled(enabled);
|
||||
|
||||
var fileChooserDescriptor = FileChooserDescriptorFactory.createSingleFileDescriptor("gguf");
|
||||
fileChooserDescriptor.setForcedToUseIdeaFileChooser(true);
|
||||
fileChooserDescriptor.setHideIgnored(false);
|
||||
browseButton.addBrowseFolderListener(new TextBrowseFolderListener(fileChooserDescriptor));
|
||||
return browseButton;
|
||||
}
|
||||
|
||||
private boolean isModelExists(HuggingFaceModel model) {
|
||||
return FileUtil.exists(
|
||||
CodeGPTPlugin.getLlamaModelsPath() + File.separator + model.getFileName());
|
||||
}
|
||||
|
||||
private AnActionLink createCancelDownloadLink(
|
||||
JBLabel progressLabel,
|
||||
JPanel actionLinkWrapper,
|
||||
DefaultComboBoxModel<HuggingFaceModel> huggingFaceComboBoxModel,
|
||||
ProgressIndicator progressIndicator) {
|
||||
return new AnActionLink(CodeGPTBundle.get("settingsConfigurable.service.llama.cancelDownloadLink.label"), new AnAction() {
|
||||
@Override
|
||||
public void actionPerformed(@NotNull AnActionEvent e) {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
configureFieldsForDownloading(false);
|
||||
updateActionLink(
|
||||
actionLinkWrapper,
|
||||
createDownloadModelLink(progressLabel, actionLinkWrapper, huggingFaceComboBoxModel));
|
||||
progressIndicator.cancel();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void updateActionLink(JPanel actionLinkWrapper, AnActionLink actionLink) {
|
||||
actionLinkWrapper.removeAll();
|
||||
actionLinkWrapper.add(actionLink, BorderLayout.WEST);
|
||||
actionLinkWrapper.revalidate();
|
||||
actionLinkWrapper.repaint();
|
||||
}
|
||||
|
||||
void configureFieldsForDownloading(boolean downloading) {
|
||||
progressLabel.setText("");
|
||||
progressLabel.setVisible(downloading);
|
||||
modelComboBox.setEnabled(!downloading);
|
||||
modelSizeComboBox.setEnabled(!downloading);
|
||||
huggingFaceModelComboBox.setEnabled(!downloading);
|
||||
modelExistsIcon.setVisible(!downloading);
|
||||
}
|
||||
|
||||
private AnActionLink createDownloadModelLink(
|
||||
JBLabel progressLabel,
|
||||
JPanel actionLinkWrapper,
|
||||
DefaultComboBoxModel<HuggingFaceModel> huggingFaceComboBoxModel) {
|
||||
return new AnActionLink(CodeGPTBundle.get("settingsConfigurable.service.llama.downloadModelLink.label"), new DownloadModelAction(
|
||||
progressIndicator -> {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
configureFieldsForDownloading(true);
|
||||
updateActionLink(
|
||||
actionLinkWrapper,
|
||||
createCancelDownloadLink(
|
||||
progressLabel,
|
||||
actionLinkWrapper,
|
||||
huggingFaceComboBoxModel,
|
||||
progressIndicator));
|
||||
});
|
||||
},
|
||||
() -> SwingUtilities.invokeLater(() -> {
|
||||
configureFieldsForDownloading(false);
|
||||
updateActionLink(
|
||||
actionLinkWrapper,
|
||||
createDownloadModelLink(progressLabel, actionLinkWrapper, huggingFaceComboBoxModel));
|
||||
actionLinkWrapper.setVisible(false);
|
||||
LlamaSettingsState.getInstance()
|
||||
.setHuggingFaceModel((HuggingFaceModel) huggingFaceComboBoxModel.getSelectedItem());
|
||||
}),
|
||||
(error) -> {
|
||||
throw new RuntimeException(error);
|
||||
},
|
||||
(text) -> SwingUtilities.invokeLater(() -> progressLabel.setText(text)),
|
||||
huggingFaceComboBoxModel), "unknown");
|
||||
}
|
||||
|
||||
private void updateModelHelpTooltip(HuggingFaceModel model) {
|
||||
helpIcon.setToolTipText(null);
|
||||
var llamaModel = LlamaModel.findByHuggingFaceModel(model);
|
||||
new HelpTooltip()
|
||||
.setTitle(llamaModel.getLabel())
|
||||
.setDescription("<html><p>" + llamaModel.getDescription() + "</p></html>")
|
||||
.setBrowserLink(CodeGPTBundle.get("settingsConfigurable.service.llama.linkToModel.label"), model.getHuggingFaceURL())
|
||||
.installOn(helpIcon);
|
||||
}
|
||||
|
||||
static class ModelSize {
|
||||
|
||||
private final int size;
|
||||
|
||||
ModelSize(int size) {
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
int getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return size + "B";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,163 @@
|
|||
package ee.carlrobert.codegpt.settings.service;
|
||||
|
||||
import com.intellij.icons.AllIcons.Actions;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.ui.MessageType;
|
||||
import com.intellij.openapi.ui.panel.ComponentPanelBuilder;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.ui.PortField;
|
||||
import com.intellij.ui.TitledSeparator;
|
||||
import com.intellij.ui.components.JBLabel;
|
||||
import com.intellij.ui.components.fields.IntegerField;
|
||||
import com.intellij.util.ui.FormBuilder;
|
||||
import com.intellij.util.ui.JBUI;
|
||||
import ee.carlrobert.codegpt.CodeGPTBundle;
|
||||
import ee.carlrobert.codegpt.CodeGPTPlugin;
|
||||
import ee.carlrobert.codegpt.completions.HuggingFaceModel;
|
||||
import ee.carlrobert.codegpt.completions.llama.LlamaServerAgent;
|
||||
import ee.carlrobert.codegpt.settings.state.LlamaSettingsState;
|
||||
import ee.carlrobert.codegpt.util.OverlayUtils;
|
||||
import java.awt.BorderLayout;
|
||||
import java.io.File;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.SwingConstants;
|
||||
|
||||
public class LlamaServiceSelectionForm extends JPanel {
|
||||
|
||||
private final LlamaModelPreferencesForm llamaModelPreferencesForm;
|
||||
private final PortField portField;
|
||||
private final IntegerField maxTokensField;
|
||||
|
||||
public LlamaServiceSelectionForm() {
|
||||
var llamaServerAgent = ApplicationManager.getApplication().getService(LlamaServerAgent.class);
|
||||
var serverRunning = llamaServerAgent.isServerRunning();
|
||||
portField = new PortField(LlamaSettingsState.getInstance().getServerPort());
|
||||
portField.setEnabled(!serverRunning);
|
||||
|
||||
var serverProgressPanel = new ServerProgressPanel();
|
||||
llamaModelPreferencesForm = new LlamaModelPreferencesForm();
|
||||
|
||||
maxTokensField = new IntegerField("max_tokens", 256, 4096);
|
||||
maxTokensField.setColumns(12);
|
||||
maxTokensField.setValue(2048);
|
||||
maxTokensField.setEnabled(!serverRunning);
|
||||
|
||||
var serverButton = new JButton();
|
||||
serverButton.setText(serverRunning ?
|
||||
CodeGPTBundle.get("settingsConfigurable.service.llama.stopServer.label") :
|
||||
CodeGPTBundle.get("settingsConfigurable.service.llama.startServer.label"));
|
||||
serverButton.setIcon(serverRunning ? Actions.Suspend : Actions.Execute);
|
||||
serverButton.addActionListener(event -> {
|
||||
if (llamaModelPreferencesForm.isUseCustomLlamaModel()) {
|
||||
var customModelPath = llamaModelPreferencesForm.getCustomLlamaModelPath();
|
||||
if (customModelPath == null || customModelPath.isEmpty()) {
|
||||
OverlayUtils.showBalloon(
|
||||
CodeGPTBundle.get("validation.error.fieldRequired"),
|
||||
MessageType.ERROR,
|
||||
llamaModelPreferencesForm.getCustomModelPathBrowserButton());
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (!isModelExists(llamaModelPreferencesForm.getSelectedModel())) {
|
||||
OverlayUtils.showBalloon(
|
||||
CodeGPTBundle.get("settingsConfigurable.service.llama.overlay.modelNotDownloaded.text"),
|
||||
MessageType.ERROR,
|
||||
llamaModelPreferencesForm.getHuggingFaceModelComboBox());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (llamaServerAgent.isServerRunning()) {
|
||||
setFormEnabled(true);
|
||||
serverButton.setText(CodeGPTBundle.get("settingsConfigurable.service.llama.startServer.label"));
|
||||
serverButton.setIcon(Actions.Execute);
|
||||
serverProgressPanel.updateText(CodeGPTBundle.get("settingsConfigurable.service.llama.progress.stoppingServer"));
|
||||
llamaServerAgent.stopAgent();
|
||||
} else {
|
||||
setFormEnabled(false);
|
||||
serverButton.setText(CodeGPTBundle.get("settingsConfigurable.service.llama.stopServer.label"));
|
||||
serverButton.setIcon(Actions.Suspend);
|
||||
serverProgressPanel.startProgress(CodeGPTBundle.get("settingsConfigurable.service.llama.progress.startingServer"));
|
||||
|
||||
// TODO: Move to LlamaModelPreferencesForm
|
||||
var modelPath = llamaModelPreferencesForm.isUseCustomLlamaModel() ?
|
||||
llamaModelPreferencesForm.getCustomLlamaModelPath() :
|
||||
CodeGPTPlugin.getLlamaModelsPath() +
|
||||
File.separator +
|
||||
llamaModelPreferencesForm.getSelectedModel().getFileName();
|
||||
llamaServerAgent.startAgent(
|
||||
modelPath,
|
||||
maxTokensField.getValue(),
|
||||
portField.getNumber(),
|
||||
serverProgressPanel,
|
||||
() -> {
|
||||
setFormEnabled(false);
|
||||
serverProgressPanel.displayComponent(new JBLabel(
|
||||
"Server running",
|
||||
Actions.Commit,
|
||||
SwingConstants.LEADING));
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
var contextSizeHelpText = ComponentPanelBuilder.createCommentComponent(
|
||||
CodeGPTBundle.get("settingsConfigurable.service.llama.contextSize.comment"),
|
||||
true);
|
||||
contextSizeHelpText.setBorder(JBUI.Borders.empty(0, 4));
|
||||
|
||||
setLayout(new BorderLayout());
|
||||
add(FormBuilder.createFormBuilder()
|
||||
.addComponent(new TitledSeparator(CodeGPTBundle.get("settingsConfigurable.service.llama.modelPreferences.title")))
|
||||
.addComponent(withEmptyLeftBorder(llamaModelPreferencesForm.getForm()))
|
||||
.addComponent(new TitledSeparator(CodeGPTBundle.get("settingsConfigurable.service.llama.serverPreferences.title")))
|
||||
.addComponent(withEmptyLeftBorder(FormBuilder.createFormBuilder()
|
||||
.addLabeledComponent(CodeGPTBundle.get("settingsConfigurable.service.llama.contextSize.label"), maxTokensField)
|
||||
.addComponentToRightColumn(contextSizeHelpText)
|
||||
.addLabeledComponent(CodeGPTBundle.get("settingsConfigurable.service.llama.port.label"), JBUI.Panels.simplePanel()
|
||||
.addToLeft(portField)
|
||||
.addToRight(serverButton))
|
||||
.getPanel()))
|
||||
.addVerticalGap(4)
|
||||
.addComponent(withEmptyLeftBorder(serverProgressPanel))
|
||||
.addComponentFillVertically(new JPanel(), 0)
|
||||
.getPanel());
|
||||
}
|
||||
|
||||
private boolean isModelExists(HuggingFaceModel model) {
|
||||
return FileUtil.exists(
|
||||
CodeGPTPlugin.getLlamaModelsPath() + File.separator + model.getFileName());
|
||||
}
|
||||
|
||||
private void setFormEnabled(boolean enabled) {
|
||||
llamaModelPreferencesForm.enableFields(enabled);
|
||||
portField.setEnabled(enabled);
|
||||
maxTokensField.setEnabled(enabled);
|
||||
}
|
||||
|
||||
public void setServerPort(int serverPort) {
|
||||
portField.setNumber(serverPort);
|
||||
}
|
||||
|
||||
public int getServerPort() {
|
||||
return portField.getNumber();
|
||||
}
|
||||
|
||||
public LlamaModelPreferencesForm getLlamaModelPreferencesForm() {
|
||||
return llamaModelPreferencesForm;
|
||||
}
|
||||
|
||||
private JComponent withEmptyLeftBorder(JComponent component) {
|
||||
component.setBorder(JBUI.Borders.emptyLeft(16));
|
||||
return component;
|
||||
}
|
||||
|
||||
public int getContextSize() {
|
||||
return maxTokensField.getValue();
|
||||
}
|
||||
|
||||
public void setContextSize(int contextSize) {
|
||||
maxTokensField.setValue(contextSize);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
package ee.carlrobert.codegpt.settings.service;
|
||||
|
||||
import com.intellij.ui.components.JBLabel;
|
||||
import com.intellij.util.ui.AsyncProcessIcon;
|
||||
import java.awt.FlowLayout;
|
||||
import javax.swing.Box;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JPanel;
|
||||
|
||||
public class ServerProgressPanel extends JPanel {
|
||||
|
||||
private final JBLabel label = new JBLabel();
|
||||
|
||||
public ServerProgressPanel() {
|
||||
super(new FlowLayout(FlowLayout.LEADING, 0, 0));
|
||||
setVisible(false);
|
||||
add(new AsyncProcessIcon("sign_in_spinner"));
|
||||
add(Box.createHorizontalStrut(4));
|
||||
add(label);
|
||||
}
|
||||
|
||||
public void startProgress(String text) {
|
||||
setVisible(true);
|
||||
updateText(text);
|
||||
}
|
||||
|
||||
public void updateText(String text) {
|
||||
label.setText(text);
|
||||
}
|
||||
|
||||
public void displayComponent(JComponent component) {
|
||||
removeAll();
|
||||
add(component);
|
||||
revalidate();
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,58 +1,46 @@
|
|||
package ee.carlrobert.codegpt.settings;
|
||||
package ee.carlrobert.codegpt.settings.service;
|
||||
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.editor.colors.EditorColorsManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.ui.ComboBox;
|
||||
import com.intellij.ui.JBColor;
|
||||
import com.intellij.ui.EnumComboBoxModel;
|
||||
import com.intellij.ui.TitledSeparator;
|
||||
import com.intellij.ui.components.*;
|
||||
import com.intellij.ui.components.JBCheckBox;
|
||||
import com.intellij.ui.components.JBPasswordField;
|
||||
import com.intellij.ui.components.JBRadioButton;
|
||||
import com.intellij.ui.components.JBTextField;
|
||||
import com.intellij.util.ui.FormBuilder;
|
||||
import com.intellij.util.ui.JBUI;
|
||||
import com.intellij.util.ui.UI;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import ee.carlrobert.codegpt.CodeGPTBundle;
|
||||
import ee.carlrobert.codegpt.completions.you.YouUserManager;
|
||||
import ee.carlrobert.codegpt.completions.you.auth.AuthenticationNotifier;
|
||||
import ee.carlrobert.codegpt.credentials.AzureCredentialsManager;
|
||||
import ee.carlrobert.codegpt.credentials.OpenAICredentialsManager;
|
||||
import ee.carlrobert.codegpt.settings.state.AzureSettingsState;
|
||||
import ee.carlrobert.codegpt.settings.state.OpenAISettingsState;
|
||||
import ee.carlrobert.codegpt.settings.state.SettingsState;
|
||||
import ee.carlrobert.codegpt.completions.you.YouUserManager;
|
||||
import ee.carlrobert.codegpt.completions.you.auth.AuthenticationNotifier;
|
||||
import ee.carlrobert.codegpt.settings.state.YouSettingsState;
|
||||
import ee.carlrobert.codegpt.telemetry.ui.utils.JBLabelUtils;
|
||||
import ee.carlrobert.codegpt.util.SwingUtils;
|
||||
import ee.carlrobert.llm.client.openai.completion.chat.OpenAIChatCompletionModel;
|
||||
import ee.carlrobert.llm.completion.CompletionModel;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.swing.Box;
|
||||
import javax.swing.ButtonGroup;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JPanel;
|
||||
|
||||
public class ServiceSelectionForm {
|
||||
|
||||
private static final Logger LOG = Logger.getInstance(ServiceSelectionForm.class);
|
||||
|
||||
private final Disposable parentDisposable;
|
||||
|
||||
private static final OpenAIChatCompletionModel[] DEFAULT_OPENAI_MODELS = new OpenAIChatCompletionModel[]{
|
||||
OpenAIChatCompletionModel.GPT_3_5,
|
||||
OpenAIChatCompletionModel.GPT_3_5_16k,
|
||||
OpenAIChatCompletionModel.GPT_4,
|
||||
OpenAIChatCompletionModel.GPT_4_32k
|
||||
};
|
||||
|
||||
private final JBRadioButton useOpenAIServiceRadioButton;
|
||||
private final JBRadioButton useAzureServiceRadioButton;
|
||||
|
||||
private final JBPasswordField openAIApiKeyField;
|
||||
private final JBTextField openAIBaseHostField;
|
||||
private final JBTextField openAIPathField;
|
||||
private final JBTextField openAIOrganizationField;
|
||||
private final JPanel openAIServiceSectionPanel;
|
||||
private final ComboBox<CompletionModel> openAICompletionModelComboBox;
|
||||
private final ComboBox<OpenAIChatCompletionModel> openAICompletionModelComboBox;
|
||||
|
||||
private final JBRadioButton useAzureApiKeyAuthenticationRadioButton;
|
||||
private final JBPasswordField azureApiKeyField;
|
||||
|
|
@ -66,13 +54,14 @@ public class ServiceSelectionForm {
|
|||
private final JBTextField azureDeploymentIdField;
|
||||
private final JBTextField azureApiVersionField;
|
||||
private final JPanel azureServiceSectionPanel;
|
||||
private final ComboBox<CompletionModel> azureCompletionModelComboBox;
|
||||
private final ComboBox<OpenAIChatCompletionModel> azureCompletionModelComboBox;
|
||||
|
||||
private final JBRadioButton useYouServiceRadioButton;
|
||||
private final JPanel youServiceSectionPanel;
|
||||
private final JBCheckBox displayWebSearchResultsCheckBox;
|
||||
|
||||
public ServiceSelectionForm(Disposable parentDisposable, SettingsState settings) {
|
||||
private final LlamaServiceSelectionForm llamaServiceSectionPanel;
|
||||
|
||||
public ServiceSelectionForm(Disposable parentDisposable) {
|
||||
this.parentDisposable = parentDisposable;
|
||||
var openAISettings = OpenAISettingsState.getInstance();
|
||||
var azureSettings = AzureSettingsState.getInstance();
|
||||
|
|
@ -83,68 +72,58 @@ public class ServiceSelectionForm {
|
|||
azureApiKeyField = new JBPasswordField();
|
||||
azureApiKeyField.setColumns(30);
|
||||
azureApiKeyField.setText(AzureCredentialsManager.getInstance().getAzureOpenAIApiKey());
|
||||
|
||||
azureApiKeyFieldPanel = UI.PanelFactory.panel(azureApiKeyField)
|
||||
.withLabel("API key:")
|
||||
.withLabel(CodeGPTBundle.get("settingsConfigurable.shared.apiKey.label"))
|
||||
.resizeX(false)
|
||||
.createPanel();
|
||||
|
||||
azureActiveDirectoryTokenField = new JBPasswordField();
|
||||
azureActiveDirectoryTokenField.setColumns(30);
|
||||
azureActiveDirectoryTokenField.setText(
|
||||
AzureCredentialsManager.getInstance().getAzureActiveDirectoryToken());
|
||||
|
||||
azureActiveDirectoryTokenFieldPanel = UI.PanelFactory.panel(azureActiveDirectoryTokenField)
|
||||
.withLabel("Bearer token:")
|
||||
.withLabel(CodeGPTBundle.get("settingsConfigurable.service.azure.bearerToken.label"))
|
||||
.resizeX(false)
|
||||
.createPanel();
|
||||
|
||||
useAzureApiKeyAuthenticationRadioButton = new JBRadioButton(
|
||||
"Use API Key authentication",
|
||||
CodeGPTBundle.get("settingsConfigurable.service.azure.useApiKeyAuth.label"),
|
||||
azureSettings.isUseAzureApiKeyAuthentication());
|
||||
useAzureActiveDirectoryAuthenticationRadioButton = new JBRadioButton(
|
||||
"Use Active Directory authentication",
|
||||
CodeGPTBundle.get("settingsConfigurable.service.azure.useActiveDirectoryAuth.label"),
|
||||
azureSettings.isUseAzureActiveDirectoryAuthentication());
|
||||
|
||||
useOpenAIServiceRadioButton = new JBRadioButton(
|
||||
CodeGPTBundle.get("settingsConfigurable.section.service.useOpenAIServiceRadioButtonLabel"),
|
||||
settings.isUseOpenAIService());
|
||||
useAzureServiceRadioButton = new JBRadioButton(
|
||||
CodeGPTBundle.get("settingsConfigurable.section.service.useAzureServiceRadioButtonLabel"),
|
||||
settings.isUseAzureService());
|
||||
useYouServiceRadioButton = new JBRadioButton(
|
||||
CodeGPTBundle.get("settingsConfigurable.section.service.useYouServiceRadioButtonLabel"),
|
||||
settings.isUseYouService());
|
||||
|
||||
openAIBaseHostField = new JBTextField(openAISettings.getBaseHost(), 30);
|
||||
openAIPathField = new JBTextField(openAISettings.getPath(), 30);
|
||||
openAIOrganizationField = new JBTextField(openAISettings.getOrganization(), 30);
|
||||
openAICompletionModelComboBox = new ModelComboBox(
|
||||
DEFAULT_OPENAI_MODELS,
|
||||
OpenAIChatCompletionModel.findByCode(openAISettings.getModel()));
|
||||
|
||||
var selectedOpenAIModel = OpenAIChatCompletionModel.findByCode(openAISettings.getModel());
|
||||
|
||||
openAICompletionModelComboBox = new ComboBox<>(
|
||||
new EnumComboBoxModel<>(OpenAIChatCompletionModel.class));
|
||||
openAICompletionModelComboBox.setSelectedItem(selectedOpenAIModel);
|
||||
|
||||
azureBaseHostField = new JBTextField(azureSettings.getBaseHost(), 35);
|
||||
azurePathField = new JBTextField(azureSettings.getPath(), 35);
|
||||
azureResourceNameField = new JBTextField(azureSettings.getResourceName(), 35);
|
||||
azureDeploymentIdField = new JBTextField(azureSettings.getDeploymentId(), 35);
|
||||
azureApiVersionField = new JBTextField(azureSettings.getApiVersion(), 35);
|
||||
azureCompletionModelComboBox = new ModelComboBox(
|
||||
DEFAULT_OPENAI_MODELS,
|
||||
OpenAIChatCompletionModel.findByCode(azureSettings.getModel()));
|
||||
azureCompletionModelComboBox = new ComboBox<>(
|
||||
new EnumComboBoxModel<>(OpenAIChatCompletionModel.class));
|
||||
azureCompletionModelComboBox.setSelectedItem(selectedOpenAIModel);
|
||||
azureCompletionModelComboBox.getEditor()
|
||||
.getEditorComponent()
|
||||
.setMaximumSize(azureBaseHostField.getPreferredSize());
|
||||
|
||||
displayWebSearchResultsCheckBox = new JBCheckBox(
|
||||
"Display web search results",
|
||||
CodeGPTBundle.get("settingsConfigurable.service.you.displayResults.label"),
|
||||
YouSettingsState.getInstance().isDisplayWebSearchResults());
|
||||
displayWebSearchResultsCheckBox.setEnabled(YouUserManager.getInstance().isAuthenticated());
|
||||
|
||||
openAIServiceSectionPanel = createOpenAIServiceSectionPanel();
|
||||
azureServiceSectionPanel = createAzureServiceSectionPanel();
|
||||
youServiceSectionPanel = createYouServiceSectionPanel();
|
||||
llamaServiceSectionPanel = new LlamaServiceSelectionForm();
|
||||
|
||||
registerPanelsVisibility(settings, azureSettings);
|
||||
registerPanelsVisibility(azureSettings);
|
||||
registerRadioButtons();
|
||||
|
||||
ApplicationManager.getApplication()
|
||||
|
|
@ -154,58 +133,42 @@ public class ServiceSelectionForm {
|
|||
(AuthenticationNotifier) () -> displayWebSearchResultsCheckBox.setEnabled(true));
|
||||
}
|
||||
|
||||
public JPanel getForm() {
|
||||
var panel = new JPanel(new FlowLayout(FlowLayout.LEADING, 0, 0));
|
||||
panel.add(useOpenAIServiceRadioButton);
|
||||
if (OpenAISettingsState.getInstance().isOpenAIQuotaExceeded()) {
|
||||
panel.add(Box.createHorizontalStrut(4));
|
||||
panel.add(new JBLabel("<html><sup style=\"color: #cc3300;\">quota exceeded</sup></html>"));
|
||||
}
|
||||
// flow layout's horizontal gap adds annoying horizontal padding on each sides
|
||||
panel.add(Box.createHorizontalStrut(16));
|
||||
panel.add(useAzureServiceRadioButton);
|
||||
panel.add(Box.createHorizontalStrut(16));
|
||||
panel.add(useYouServiceRadioButton);
|
||||
|
||||
return FormBuilder.createFormBuilder()
|
||||
.addComponent(withEmptyLeftBorder(panel))
|
||||
.addComponent(openAIServiceSectionPanel)
|
||||
.addComponent(azureServiceSectionPanel)
|
||||
.addComponent(youServiceSectionPanel)
|
||||
.getPanel();
|
||||
}
|
||||
|
||||
private JPanel createOpenAIServiceSectionPanel() {
|
||||
var requestConfigurationPanel = UI.PanelFactory.grid()
|
||||
.add(UI.PanelFactory.panel(openAIOrganizationField)
|
||||
.withLabel(CodeGPTBundle.get(
|
||||
"settingsConfigurable.section.service.openai.organizationField.label"))
|
||||
"settingsConfigurable.service.openai.organization.label"))
|
||||
.resizeX(false)
|
||||
.withComment(CodeGPTBundle.get(
|
||||
"settingsConfigurable.section.service.openai.organizationField.comment")))
|
||||
"settingsConfigurable.section.openai.organization.comment")))
|
||||
.add(UI.PanelFactory.panel(openAIBaseHostField)
|
||||
.withLabel("Base host:")
|
||||
.withLabel(CodeGPTBundle.get(
|
||||
"settingsConfigurable.shared.baseHost.label"))
|
||||
.resizeX(false))
|
||||
.add(UI.PanelFactory.panel(openAIPathField)
|
||||
.withLabel("Path:")
|
||||
.withLabel(CodeGPTBundle.get(
|
||||
"settingsConfigurable.shared.path.label"))
|
||||
.resizeX(false))
|
||||
.add(UI.PanelFactory.panel(openAICompletionModelComboBox)
|
||||
.withLabel("Model:")
|
||||
.withLabel(CodeGPTBundle.get(
|
||||
"settingsConfigurable.shared.model.label"))
|
||||
.resizeX(false))
|
||||
.createPanel();
|
||||
|
||||
var apiKeyFieldPanel = UI.PanelFactory.panel(openAIApiKeyField)
|
||||
.withLabel(CodeGPTBundle.get("settingsConfigurable.section.integration.apiKeyField.label"))
|
||||
.withLabel(CodeGPTBundle.get("settingsConfigurable.shared.apiKey.label"))
|
||||
.resizeX(false)
|
||||
.withComment(
|
||||
CodeGPTBundle.get("settingsConfigurable.section.integration.apiKeyField.comment"))
|
||||
CodeGPTBundle.get("settingsConfigurable.service.openai.apiKey.comment"))
|
||||
.withCommentHyperlinkListener(SwingUtils::handleHyperlinkClicked)
|
||||
.createPanel();
|
||||
|
||||
return FormBuilder.createFormBuilder()
|
||||
.addComponent(new TitledSeparator("Authentication"))
|
||||
.addComponent(new TitledSeparator(
|
||||
CodeGPTBundle.get("settingsConfigurable.shared.authentication.title")))
|
||||
.addComponent(withEmptyLeftBorder(apiKeyFieldPanel))
|
||||
.addComponent(new TitledSeparator("Request Configuration"))
|
||||
.addComponent(new TitledSeparator(
|
||||
CodeGPTBundle.get("settingsConfigurable.shared.requestConfiguration.title")))
|
||||
.addComponent(withEmptyLeftBorder(requestConfigurationPanel))
|
||||
.addComponentFillVertically(new JPanel(), 0)
|
||||
.getPanel();
|
||||
|
|
@ -228,46 +191,51 @@ public class ServiceSelectionForm {
|
|||
var configPanel = withEmptyLeftBorder(UI.PanelFactory.grid()
|
||||
.add(UI.PanelFactory.panel(azureResourceNameField)
|
||||
.withLabel(CodeGPTBundle.get(
|
||||
"settingsConfigurable.section.service.azure.resourceNameField.label"))
|
||||
"settingsConfigurable.service.azure.resourceName.label"))
|
||||
.resizeX(false)
|
||||
.withComment(CodeGPTBundle.get(
|
||||
"settingsConfigurable.section.service.azure.resourceNameField.comment")))
|
||||
"settingsConfigurable.service.azure.resourceName.comment")))
|
||||
.add(UI.PanelFactory.panel(azureDeploymentIdField)
|
||||
.withLabel(CodeGPTBundle.get(
|
||||
"settingsConfigurable.section.service.azure.deploymentIdField.label"))
|
||||
"settingsConfigurable.service.azure.deploymentId.label"))
|
||||
.resizeX(false)
|
||||
.withComment(CodeGPTBundle.get(
|
||||
"settingsConfigurable.section.service.azure.deploymentIdField.comment")))
|
||||
"settingsConfigurable.service.azure.deploymentId.comment")))
|
||||
.add(UI.PanelFactory.panel(azureApiVersionField)
|
||||
.withLabel(CodeGPTBundle.get(
|
||||
"settingsConfigurable.section.service.azure.apiVersionField.label"))
|
||||
"settingsConfigurable.service.azure.apiVersion.label"))
|
||||
.resizeX(false)
|
||||
.withComment(CodeGPTBundle.get(
|
||||
"settingsConfigurable.section.service.azure.apiVersionField.comment")))
|
||||
"settingsConfigurable.service.azure.apiVersion.comment")))
|
||||
.add(UI.PanelFactory.panel(azureBaseHostField)
|
||||
.withLabel("Base host:")
|
||||
.withLabel(CodeGPTBundle.get("settingsConfigurable.shared.baseHost.label"))
|
||||
.resizeX(false))
|
||||
.add(UI.PanelFactory.panel(azurePathField)
|
||||
.withLabel("Path:")
|
||||
.withLabel(CodeGPTBundle.get("settingsConfigurable.shared.path.label"))
|
||||
.resizeX(false))
|
||||
.add(UI.PanelFactory.panel(azureCompletionModelComboBox)
|
||||
.withLabel("Model:")
|
||||
.withLabel(CodeGPTBundle.get("settingsConfigurable.shared.model.label"))
|
||||
.resizeX(false))
|
||||
.createPanel());
|
||||
|
||||
return FormBuilder.createFormBuilder()
|
||||
.addComponent(new TitledSeparator("Authentication"))
|
||||
.addComponent(new TitledSeparator(
|
||||
CodeGPTBundle.get("settingsConfigurable.shared.authentication.title")))
|
||||
.addComponent(authPanel)
|
||||
.addComponent(new TitledSeparator("Request Configuration"))
|
||||
.addComponent(new TitledSeparator(
|
||||
CodeGPTBundle.get("settingsConfigurable.shared.requestConfiguration.title")))
|
||||
.addComponent(configPanel)
|
||||
.addComponentFillVertically(new JPanel(), 0)
|
||||
.getPanel();
|
||||
}
|
||||
|
||||
private JPanel createYouServiceSectionPanel() {
|
||||
return FormBuilder.createFormBuilder()
|
||||
.addComponent(new YouServiceSelectionPanel(parentDisposable))
|
||||
.addComponent(new TitledSeparator("Chat Preferences"))
|
||||
.addComponent(new YouServiceSelectionForm(parentDisposable))
|
||||
.addComponent(new TitledSeparator(
|
||||
CodeGPTBundle.get("settingsConfigurable.service.you.chatPreferences.title")))
|
||||
.addComponent(withEmptyLeftBorder(displayWebSearchResultsCheckBox))
|
||||
.addComponentFillVertically(new JPanel(), 0)
|
||||
.getPanel();
|
||||
}
|
||||
|
||||
|
|
@ -276,32 +244,22 @@ public class ServiceSelectionForm {
|
|||
return component;
|
||||
}
|
||||
|
||||
private void registerPanelsVisibility(SettingsState settings, AzureSettingsState azureSettings) {
|
||||
openAIServiceSectionPanel.setVisible(settings.isUseOpenAIService());
|
||||
azureServiceSectionPanel.setVisible(settings.isUseAzureService());
|
||||
private void registerPanelsVisibility(AzureSettingsState azureSettings) {
|
||||
azureApiKeyFieldPanel.setVisible(azureSettings.isUseAzureApiKeyAuthentication());
|
||||
azureActiveDirectoryTokenFieldPanel.setVisible(
|
||||
azureSettings.isUseAzureActiveDirectoryAuthentication());
|
||||
youServiceSectionPanel.setVisible(settings.isUseYouService());
|
||||
}
|
||||
|
||||
private void registerRadioButtons() {
|
||||
registerRadioButtons(
|
||||
List.of(
|
||||
Map.entry(useOpenAIServiceRadioButton, openAIServiceSectionPanel),
|
||||
Map.entry(useAzureServiceRadioButton, azureServiceSectionPanel),
|
||||
Map.entry(useYouServiceRadioButton, youServiceSectionPanel)));
|
||||
registerRadioButtons(
|
||||
List.of(
|
||||
Map.entry(useAzureApiKeyAuthenticationRadioButton, azureApiKeyFieldPanel),
|
||||
Map.entry(useAzureActiveDirectoryAuthenticationRadioButton,
|
||||
azureActiveDirectoryTokenFieldPanel)));
|
||||
registerRadioButtons(List.of(
|
||||
Map.entry(useAzureApiKeyAuthenticationRadioButton, azureApiKeyFieldPanel),
|
||||
Map.entry(useAzureActiveDirectoryAuthenticationRadioButton,
|
||||
azureActiveDirectoryTokenFieldPanel)));
|
||||
}
|
||||
|
||||
private void registerRadioButtons(List<Map.Entry<JBRadioButton, JPanel>> entries) {
|
||||
var buttonGroup = new ButtonGroup();
|
||||
entries.forEach(entry -> buttonGroup.add(entry.getKey()));
|
||||
|
||||
entries.forEach(entry -> entry.getKey().addActionListener((e) -> {
|
||||
for (Map.Entry<JBRadioButton, JPanel> innerEntry : entries) {
|
||||
innerEntry.getValue().setVisible(innerEntry.equals(entry));
|
||||
|
|
@ -309,42 +267,6 @@ public class ServiceSelectionForm {
|
|||
}));
|
||||
}
|
||||
|
||||
public OpenAIChatCompletionModel getSelectedCompletionModel() {
|
||||
return (OpenAIChatCompletionModel) (isOpenAIServiceSelected() ?
|
||||
openAICompletionModelComboBox.getSelectedItem() :
|
||||
azureCompletionModelComboBox.getSelectedItem());
|
||||
}
|
||||
|
||||
public void setSelectedChatCompletionModel(OpenAIChatCompletionModel chatCompletionModel) {
|
||||
if (isOpenAIServiceSelected()) {
|
||||
openAICompletionModelComboBox.setSelectedItem(chatCompletionModel);
|
||||
}
|
||||
}
|
||||
|
||||
public void setOpenAIServiceSelected(boolean selected) {
|
||||
useOpenAIServiceRadioButton.setSelected(selected);
|
||||
}
|
||||
|
||||
public boolean isOpenAIServiceSelected() {
|
||||
return useOpenAIServiceRadioButton.isSelected();
|
||||
}
|
||||
|
||||
public void setAzureServiceSelected(boolean selected) {
|
||||
useAzureServiceRadioButton.setSelected(selected);
|
||||
}
|
||||
|
||||
public boolean isAzureServiceSelected() {
|
||||
return useAzureServiceRadioButton.isSelected();
|
||||
}
|
||||
|
||||
public boolean isYouServiceSelected() {
|
||||
return useYouServiceRadioButton.isSelected();
|
||||
}
|
||||
|
||||
public void setYouServiceSelected(boolean selected) {
|
||||
useYouServiceRadioButton.setSelected(selected);
|
||||
}
|
||||
|
||||
public void setOpenAIApiKey(String apiKey) {
|
||||
openAIApiKeyField.setText(apiKey);
|
||||
}
|
||||
|
|
@ -461,6 +383,10 @@ public class ServiceSelectionForm {
|
|||
return displayWebSearchResultsCheckBox.isSelected();
|
||||
}
|
||||
|
||||
public LlamaModelPreferencesForm getLlamaModelPreferencesForm() {
|
||||
return llamaServiceSectionPanel.getLlamaModelPreferencesForm();
|
||||
}
|
||||
|
||||
public void setOpenAIPath(String path) {
|
||||
openAIPathField.setText(path);
|
||||
}
|
||||
|
|
@ -476,4 +402,36 @@ public class ServiceSelectionForm {
|
|||
public String getAzurePath() {
|
||||
return azurePathField.getText();
|
||||
}
|
||||
|
||||
public void setLlamaServerPort(int serverPort) {
|
||||
llamaServiceSectionPanel.setServerPort(serverPort);
|
||||
}
|
||||
|
||||
public int getLlamaServerPort() {
|
||||
return llamaServiceSectionPanel.getServerPort();
|
||||
}
|
||||
|
||||
public JPanel getOpenAIServiceSectionPanel() {
|
||||
return openAIServiceSectionPanel;
|
||||
}
|
||||
|
||||
public JPanel getAzureServiceSectionPanel() {
|
||||
return azureServiceSectionPanel;
|
||||
}
|
||||
|
||||
public JPanel getYouServiceSectionPanel() {
|
||||
return youServiceSectionPanel;
|
||||
}
|
||||
|
||||
public JPanel getLlamaServiceSectionPanel() {
|
||||
return llamaServiceSectionPanel;
|
||||
}
|
||||
|
||||
public int getContextSize() {
|
||||
return llamaServiceSectionPanel.getContextSize();
|
||||
}
|
||||
|
||||
public void setContextSize(int contextSize) {
|
||||
llamaServiceSectionPanel.setContextSize(contextSize);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
package ee.carlrobert.codegpt.settings.service;
|
||||
|
||||
import ee.carlrobert.codegpt.CodeGPTBundle;
|
||||
|
||||
public enum ServiceType {
|
||||
OPENAI("OPENAI", CodeGPTBundle.get("service.openai.title")),
|
||||
AZURE("AZURE", CodeGPTBundle.get("service.azure.title")),
|
||||
YOU("YOU", CodeGPTBundle.get("service.you.title")),
|
||||
LLAMA_CPP("LLAMA_CPP", CodeGPTBundle.get("service.llama.title"));
|
||||
|
||||
private final String code;
|
||||
private final String label;
|
||||
|
||||
ServiceType(String code, String label) {
|
||||
this.code = code;
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return label;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package ee.carlrobert.codegpt.settings;
|
||||
package ee.carlrobert.codegpt.settings.service;
|
||||
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.ui.ComponentValidator;
|
||||
|
|
@ -9,7 +9,6 @@ import com.intellij.ui.TitledSeparator;
|
|||
import com.intellij.ui.components.JBLabel;
|
||||
import com.intellij.ui.components.JBPasswordField;
|
||||
import com.intellij.ui.components.JBTextField;
|
||||
import com.intellij.ui.components.OnOffButton;
|
||||
import com.intellij.util.ui.AsyncProcessIcon;
|
||||
import com.intellij.util.ui.FormBuilder;
|
||||
import com.intellij.util.ui.JBFont;
|
||||
|
|
@ -35,7 +34,7 @@ import javax.swing.JTextPane;
|
|||
import javax.swing.SwingUtilities;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class YouServiceSelectionPanel extends JPanel {
|
||||
public class YouServiceSelectionForm extends JPanel {
|
||||
|
||||
private final JBTextField emailField;
|
||||
private final JBPasswordField passwordField;
|
||||
|
|
@ -43,7 +42,7 @@ public class YouServiceSelectionPanel extends JPanel {
|
|||
private final JTextPane signUpTextPane;
|
||||
private final AsyncProcessIcon loadingSpinner;
|
||||
|
||||
public YouServiceSelectionPanel(Disposable parentDisposable) {
|
||||
public YouServiceSelectionForm(Disposable parentDisposable) {
|
||||
super(new BorderLayout());
|
||||
var settings = SettingsState.getInstance();
|
||||
emailField = new JBTextField(settings.getEmail(), 25);
|
||||
|
|
@ -52,8 +51,7 @@ public class YouServiceSelectionPanel extends JPanel {
|
|||
if (!settings.getEmail().isEmpty()) {
|
||||
passwordField.setText(YouCredentialsManager.getInstance().getAccountPassword());
|
||||
}
|
||||
signInButton = new JButton(
|
||||
CodeGPTBundle.get("settingsConfigurable.section.userAuthentication.signIn.label"));
|
||||
signInButton = new JButton(CodeGPTBundle.get("settingsConfigurable.service.you.signIn.label"));
|
||||
signUpTextPane = createSignUpTextPane();
|
||||
loadingSpinner = new AsyncProcessIcon("sign_in_spinner");
|
||||
loadingSpinner.setBorder(JBUI.Borders.emptyLeft(8));
|
||||
|
|
@ -106,7 +104,8 @@ public class YouServiceSelectionPanel extends JPanel {
|
|||
if (component instanceof JBTextField) {
|
||||
value = ((JBTextField) component).getText();
|
||||
if (!isValidEmail(value)) {
|
||||
return new ValidationInfo("The email you entered is invalid.", component)
|
||||
return new ValidationInfo(
|
||||
CodeGPTBundle.get("validation.error.invalidEmail"), component)
|
||||
.withOKEnabled();
|
||||
}
|
||||
} else {
|
||||
|
|
@ -114,7 +113,9 @@ public class YouServiceSelectionPanel extends JPanel {
|
|||
}
|
||||
|
||||
if (StringUtil.isEmpty(value)) {
|
||||
return new ValidationInfo("This field is required.", component).withOKEnabled();
|
||||
return new ValidationInfo(
|
||||
CodeGPTBundle.get("validation.error.fieldRequired"), component)
|
||||
.withOKEnabled();
|
||||
}
|
||||
|
||||
return null;
|
||||
|
|
@ -134,18 +135,14 @@ public class YouServiceSelectionPanel extends JPanel {
|
|||
|
||||
private JTextPane createSignUpTextPane() {
|
||||
var textPane = createTextPane(
|
||||
"<html><a href=\"https://you.com/code\" style=\"padding:0;\">Don't have an account?<br/>Sign up with 'CodeGPT' coupon for free GPT-4</a></html>");
|
||||
"<html><a href=\"https://you.com/code\">Don't have an account? Sign up</a></html>");
|
||||
textPane.setBorder(JBUI.Borders.emptyLeft(4));
|
||||
return textPane;
|
||||
}
|
||||
|
||||
private JTextPane createTextPane(String htmlContent) {
|
||||
var textPane = new JTextPane();
|
||||
textPane.setContentType("text/html");
|
||||
textPane.putClientProperty(JTextPane.HONOR_DISPLAY_PROPERTIES, true);
|
||||
var textPane = SwingUtils.createTextPane(SwingUtils::handleHyperlinkClicked);
|
||||
textPane.setText(htmlContent);
|
||||
textPane.addHyperlinkListener(SwingUtils::handleHyperlinkClicked);
|
||||
textPane.setEditable(false);
|
||||
return textPane;
|
||||
}
|
||||
|
||||
|
|
@ -162,9 +159,23 @@ public class YouServiceSelectionPanel extends JPanel {
|
|||
JBTextField emailAddressField,
|
||||
JBPasswordField passwordField,
|
||||
@Nullable YouAuthenticationError error) {
|
||||
var couponLabel = new JBLabel(
|
||||
"<html>"
|
||||
+ "<body>"
|
||||
+ "<h1 style=\"text-align: center; padding: 0; margin: 0;\">Free GPT-4</h1>"
|
||||
+ "<p style=\"text-align: center; margin-top: 8px; margin-bottom: 8px;\">Your coupon code</p>"
|
||||
+ "<h1 style=\"text-align: center; border: 2px dotted #646464; padding: 4px 32px; margin: 0 0 12px 0; background-color: #45494a; cursor: pointer;\">CODEGPT</h1>"
|
||||
+ "</body>"
|
||||
+ "</html>")
|
||||
.withBorder(JBUI.Borders.emptyLeft(45)) // TODO
|
||||
.setCopyable(true);
|
||||
|
||||
var contentPanelBuilder = FormBuilder.createFormBuilder()
|
||||
.addLabeledComponent("Email address:", emailAddressField)
|
||||
.addLabeledComponent("Password:", passwordField)
|
||||
.addComponentToRightColumn(JBUI.Panels.simplePanel().addToLeft(couponLabel))
|
||||
.addLabeledComponent(CodeGPTBundle.get("settingsConfigurable.service.you.email.label"),
|
||||
emailAddressField)
|
||||
.addLabeledComponent(CodeGPTBundle.get("settingsConfigurable.service.you.password.label"),
|
||||
passwordField)
|
||||
.addVerticalGap(4)
|
||||
.addComponentToRightColumn(createFooterPanel())
|
||||
.addVerticalGap(4);
|
||||
|
|
@ -176,22 +187,24 @@ public class YouServiceSelectionPanel extends JPanel {
|
|||
contentPanelBuilder.addComponentToRightColumn(invalidCredentialsLabel);
|
||||
}
|
||||
|
||||
var contentPanel = contentPanelBuilder.getPanel();
|
||||
contentPanel.setBorder(JBUI.Borders.emptyLeft(16));
|
||||
|
||||
return FormBuilder.createFormBuilder()
|
||||
.addComponent(new TitledSeparator(
|
||||
CodeGPTBundle.get("settingsConfigurable.section.userAuthentication.title")))
|
||||
.addComponent(JBUI.Panels
|
||||
.simplePanel(contentPanelBuilder.getPanel())
|
||||
.withBorder(JBUI.Borders.emptyLeft(16)))
|
||||
CodeGPTBundle.get("settingsConfigurable.service.you.authentication.title")))
|
||||
.addComponent(contentPanel)
|
||||
.getPanel();
|
||||
}
|
||||
|
||||
private JPanel createUserInformationPanel(YouUser user) {
|
||||
var userManager = YouUserManager.getInstance();
|
||||
var contentPanelBuilder = FormBuilder.createFormBuilder()
|
||||
.addLabeledComponent("Email address:",
|
||||
.addLabeledComponent(CodeGPTBundle.get("settingsConfigurable.service.you.email.label"),
|
||||
new JBLabel(user.getEmails().get(0).getEmail()).withFont(JBFont.label().asBold()));
|
||||
|
||||
var signOutButton = new JButton("Sign Out");
|
||||
var signOutButton = new JButton(
|
||||
CodeGPTBundle.get("settingsConfigurable.service.you.signOut.label"));
|
||||
signOutButton.addActionListener(e -> {
|
||||
userManager.clearSession();
|
||||
refreshView(createUserAuthenticationPanel(emailField, passwordField, null));
|
||||
|
|
@ -199,7 +212,7 @@ public class YouServiceSelectionPanel extends JPanel {
|
|||
|
||||
return FormBuilder.createFormBuilder()
|
||||
.addComponent(new TitledSeparator(
|
||||
CodeGPTBundle.get("settingsConfigurable.section.userInformation.title")))
|
||||
CodeGPTBundle.get("settingsConfigurable.service.you.userInformation.title")))
|
||||
.addVerticalGap(8)
|
||||
.addComponent(JBUI.Panels
|
||||
.simplePanel(contentPanelBuilder.addVerticalGap(4)
|
||||
|
|
@ -6,7 +6,7 @@ import com.intellij.openapi.components.State;
|
|||
import com.intellij.openapi.components.Storage;
|
||||
import com.intellij.util.xmlb.XmlSerializerUtil;
|
||||
import ee.carlrobert.codegpt.credentials.AzureCredentialsManager;
|
||||
import ee.carlrobert.codegpt.settings.ServiceSelectionForm;
|
||||
import ee.carlrobert.codegpt.settings.service.ServiceSelectionForm;
|
||||
import ee.carlrobert.llm.client.openai.completion.chat.OpenAIChatCompletionModel;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,96 @@
|
|||
package ee.carlrobert.codegpt.settings.state;
|
||||
|
||||
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 com.intellij.util.xmlb.XmlSerializerUtil;
|
||||
import ee.carlrobert.codegpt.completions.HuggingFaceModel;
|
||||
import ee.carlrobert.codegpt.completions.llama.PromptTemplate;
|
||||
import java.io.IOException;
|
||||
import java.net.ServerSocket;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@State(name = "CodeGPT_LlamaSettings", storages = @Storage("CodeGPT_CodeGPT_LlamaSettings.xml"))
|
||||
public class LlamaSettingsState implements PersistentStateComponent<LlamaSettingsState> {
|
||||
|
||||
private boolean useCustomModel;
|
||||
private String customLlamaModelPath = "";
|
||||
private HuggingFaceModel huggingFaceModel = HuggingFaceModel.CODE_LLAMA_7B_Q4;
|
||||
private PromptTemplate promptTemplate = PromptTemplate.LLAMA;
|
||||
private int serverPort = getRandomAvailablePortOrDefault();
|
||||
private int contextSize = 2048;
|
||||
|
||||
public LlamaSettingsState() {
|
||||
}
|
||||
|
||||
public static LlamaSettingsState getInstance() {
|
||||
return ApplicationManager.getApplication().getService(LlamaSettingsState.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LlamaSettingsState getState() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadState(@NotNull LlamaSettingsState state) {
|
||||
XmlSerializerUtil.copyBean(state, this);
|
||||
}
|
||||
|
||||
public boolean isUseCustomModel() {
|
||||
return useCustomModel;
|
||||
}
|
||||
|
||||
public void setUseCustomModel(boolean useCustomModel) {
|
||||
this.useCustomModel = useCustomModel;
|
||||
}
|
||||
|
||||
public String getCustomLlamaModelPath() {
|
||||
return customLlamaModelPath;
|
||||
}
|
||||
|
||||
public void setCustomLlamaModelPath(String customLlamaModelPath) {
|
||||
this.customLlamaModelPath = customLlamaModelPath;
|
||||
}
|
||||
|
||||
public HuggingFaceModel getHuggingFaceModel() {
|
||||
return huggingFaceModel;
|
||||
}
|
||||
|
||||
public void setHuggingFaceModel(HuggingFaceModel huggingFaceModel) {
|
||||
this.huggingFaceModel = huggingFaceModel;
|
||||
}
|
||||
|
||||
public PromptTemplate getPromptTemplate() {
|
||||
return promptTemplate;
|
||||
}
|
||||
|
||||
public void setPromptTemplate(PromptTemplate promptTemplate) {
|
||||
this.promptTemplate = promptTemplate;
|
||||
}
|
||||
|
||||
public int getServerPort() {
|
||||
return serverPort;
|
||||
}
|
||||
|
||||
public void setServerPort(int serverPort) {
|
||||
this.serverPort = serverPort;
|
||||
}
|
||||
|
||||
public int getContextSize() {
|
||||
return contextSize;
|
||||
}
|
||||
|
||||
public void setContextSize(int contextSize) {
|
||||
this.contextSize = contextSize;
|
||||
}
|
||||
|
||||
private static Integer getRandomAvailablePortOrDefault() {
|
||||
try (ServerSocket socket = new ServerSocket(0)) {
|
||||
return socket.getLocalPort();
|
||||
} catch (IOException e) {
|
||||
return 8080;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -6,7 +6,7 @@ import com.intellij.openapi.components.State;
|
|||
import com.intellij.openapi.components.Storage;
|
||||
import com.intellij.util.xmlb.XmlSerializerUtil;
|
||||
import ee.carlrobert.codegpt.credentials.OpenAICredentialsManager;
|
||||
import ee.carlrobert.codegpt.settings.ServiceSelectionForm;
|
||||
import ee.carlrobert.codegpt.settings.service.ServiceSelectionForm;
|
||||
import ee.carlrobert.llm.client.openai.completion.chat.OpenAIChatCompletionModel;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import com.intellij.openapi.components.PersistentStateComponent;
|
|||
import com.intellij.openapi.components.State;
|
||||
import com.intellij.openapi.components.Storage;
|
||||
import com.intellij.util.xmlb.XmlSerializerUtil;
|
||||
import ee.carlrobert.codegpt.completions.HuggingFaceModel;
|
||||
import ee.carlrobert.codegpt.conversations.Conversation;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
|
|
@ -17,6 +18,7 @@ public class SettingsState implements PersistentStateComponent<SettingsState> {
|
|||
private boolean useOpenAIService = true;
|
||||
private boolean useAzureService;
|
||||
private boolean useYouService;
|
||||
private boolean useLlamaService;
|
||||
|
||||
public SettingsState() {
|
||||
}
|
||||
|
|
@ -43,10 +45,15 @@ public class SettingsState implements PersistentStateComponent<SettingsState> {
|
|||
if ("azure.chat.completion".equals(clientCode)) {
|
||||
AzureSettingsState.getInstance().setModel(conversation.getModel());
|
||||
}
|
||||
if ("llama.chat.completion".equals(clientCode)) {
|
||||
LlamaSettingsState.getInstance().setHuggingFaceModel(
|
||||
HuggingFaceModel.valueOf(conversation.getModel()));
|
||||
}
|
||||
|
||||
setUseOpenAIService("chat.completion".equals(clientCode));
|
||||
setUseAzureService("azure.chat.completion".equals(clientCode));
|
||||
setUseYouService("you.chat.completion".equals(clientCode));
|
||||
setUseLlamaService("llama.chat.completion".equals(clientCode));
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
|
|
@ -103,4 +110,12 @@ public class SettingsState implements PersistentStateComponent<SettingsState> {
|
|||
public void setUseYouService(boolean useYouService) {
|
||||
this.useYouService = useYouService;
|
||||
}
|
||||
|
||||
public boolean isUseLlamaService() {
|
||||
return useLlamaService;
|
||||
}
|
||||
|
||||
public void setUseLlamaService(boolean useLlamaService) {
|
||||
this.useLlamaService = useLlamaService;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ public class YouSettingsState implements PersistentStateComponent<YouSettingsSta
|
|||
|
||||
private boolean displayWebSearchResults = true;
|
||||
private boolean useGPT4Model;
|
||||
private String baseHost;
|
||||
|
||||
public static YouSettingsState getInstance() {
|
||||
return ApplicationManager.getApplication().getService(YouSettingsState.class);
|
||||
|
|
@ -42,4 +43,12 @@ public class YouSettingsState implements PersistentStateComponent<YouSettingsSta
|
|||
public void setUseGPT4Model(boolean useGPT4Model) {
|
||||
this.useGPT4Model = useGPT4Model;
|
||||
}
|
||||
|
||||
public void setBaseHost(String baseHost) {
|
||||
this.baseHost = baseHost;
|
||||
}
|
||||
|
||||
public String getBaseHost() {
|
||||
return baseHost;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,9 @@ public class ModelIconLabel extends JBLabel {
|
|||
if ("azure.chat.completion".equals(clientCode)) {
|
||||
setIcon(Icons.AzureIcon);
|
||||
}
|
||||
if ("llama.chat.completion".equals(clientCode)) {
|
||||
setIcon(Icons.LlamaIcon);
|
||||
}
|
||||
setText(formatModelName(modelCode));
|
||||
setFont(JBFont.small().asBold());
|
||||
setHorizontalAlignment(SwingConstants.LEADING);
|
||||
|
|
|
|||
|
|
@ -5,27 +5,32 @@ import static ee.carlrobert.codegpt.util.ThemeUtils.getPanelBackgroundColor;
|
|||
import static java.lang.String.format;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
|
||||
import com.intellij.ide.HelpTooltip;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.impl.EditorImpl;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.roots.ui.componentsList.components.ScrollablePanel;
|
||||
import com.intellij.ui.JBColor;
|
||||
import com.intellij.ui.components.JBCheckBox;
|
||||
import com.intellij.ui.components.JBScrollPane;
|
||||
import com.intellij.util.messages.MessageBusConnection;
|
||||
import com.intellij.util.ui.JBUI;
|
||||
import com.intellij.util.ui.JBUI.Borders;
|
||||
import ee.carlrobert.codegpt.CodeGPTBundle;
|
||||
import ee.carlrobert.codegpt.actions.ActionType;
|
||||
import ee.carlrobert.codegpt.completions.CompletionRequestHandler;
|
||||
import ee.carlrobert.codegpt.completions.llama.LlamaModel;
|
||||
import ee.carlrobert.codegpt.completions.you.YouSerpResult;
|
||||
import ee.carlrobert.codegpt.completions.you.YouSubscriptionNotifier;
|
||||
import ee.carlrobert.codegpt.completions.you.YouUserManager;
|
||||
import ee.carlrobert.codegpt.completions.you.auth.AuthenticationNotifier;
|
||||
import ee.carlrobert.codegpt.completions.you.auth.SignedOutNotifier;
|
||||
import ee.carlrobert.codegpt.conversations.Conversation;
|
||||
import ee.carlrobert.codegpt.conversations.ConversationService;
|
||||
import ee.carlrobert.codegpt.conversations.message.Message;
|
||||
import ee.carlrobert.codegpt.credentials.AzureCredentialsManager;
|
||||
import ee.carlrobert.codegpt.credentials.OpenAICredentialsManager;
|
||||
import ee.carlrobert.codegpt.settings.state.AzureSettingsState;
|
||||
import ee.carlrobert.codegpt.settings.state.LlamaSettingsState;
|
||||
import ee.carlrobert.codegpt.settings.state.OpenAISettingsState;
|
||||
import ee.carlrobert.codegpt.settings.state.SettingsState;
|
||||
import ee.carlrobert.codegpt.settings.state.YouSettingsState;
|
||||
|
|
@ -38,6 +43,7 @@ import ee.carlrobert.codegpt.toolwindow.chat.components.UserMessagePanel;
|
|||
import ee.carlrobert.codegpt.toolwindow.chat.components.UserPromptTextArea;
|
||||
import ee.carlrobert.codegpt.util.EditorUtils;
|
||||
import ee.carlrobert.codegpt.util.OverlayUtils;
|
||||
import ee.carlrobert.codegpt.util.SwingUtils;
|
||||
import ee.carlrobert.codegpt.util.file.FileUtils;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.GridBagConstraints;
|
||||
|
|
@ -50,6 +56,7 @@ import java.util.UUID;
|
|||
import javax.swing.BoxLayout;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JTextPane;
|
||||
import javax.swing.ScrollPaneConstants;
|
||||
import javax.swing.SwingUtilities;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
|
@ -57,6 +64,8 @@ import org.jetbrains.annotations.Nullable;
|
|||
|
||||
public abstract class BaseChatToolWindowTabPanel implements ChatToolWindowTabPanel {
|
||||
|
||||
private static final Logger LOG = Logger.getInstance(BaseChatToolWindowTabPanel.class);
|
||||
|
||||
private final boolean useContextualSearch;
|
||||
private final JPanel rootPanel;
|
||||
private final ScrollablePanel scrollablePanel;
|
||||
|
|
@ -105,10 +114,31 @@ public abstract class BaseChatToolWindowTabPanel implements ChatToolWindowTabPan
|
|||
public void displayLandingView() {
|
||||
scrollablePanel.removeAll();
|
||||
scrollablePanel.add(getLandingView());
|
||||
var youUserManager = YouUserManager.getInstance();
|
||||
if (SettingsState.getInstance().isUseYouService() &&
|
||||
(!youUserManager.isAuthenticated() || !youUserManager.isSubscribed())) {
|
||||
scrollablePanel.add(new ResponsePanel().addContent(createTextPane()));
|
||||
}
|
||||
scrollablePanel.repaint();
|
||||
scrollablePanel.revalidate();
|
||||
}
|
||||
|
||||
private JTextPane createTextPane() {
|
||||
var textPane = SwingUtils.createTextPane(SwingUtils::handleHyperlinkClicked);
|
||||
textPane.setBackground(getPanelBackgroundColor());
|
||||
textPane.setFocusable(false);
|
||||
textPane.setText(
|
||||
"<html>\n"
|
||||
+ "<body>\n"
|
||||
+ " <p style=\"margin: 4px 0;\">Use CodeGPT coupon for free month of GPT-4.</p>\n"
|
||||
+ " <p style=\"margin: 4px 0;\">\n"
|
||||
+ " <a href=\"https://you.com/plans\">Sign up here</a>\n"
|
||||
+ " </p>\n"
|
||||
+ "</body>\n"
|
||||
+ "</html>");
|
||||
return textPane;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startNewConversation(Message message) {
|
||||
conversation = conversationService.startConversation();
|
||||
|
|
@ -165,7 +195,9 @@ public abstract class BaseChatToolWindowTabPanel implements ChatToolWindowTabPan
|
|||
requestHandler.withContextualSearch(useContextualSearch);
|
||||
requestHandler.addMessageListener(partialMessage -> {
|
||||
try {
|
||||
responseContainer.update(partialMessage);
|
||||
LOG.debug(partialMessage);
|
||||
ApplicationManager.getApplication()
|
||||
.invokeLater(() -> responseContainer.update(partialMessage));
|
||||
} catch (Exception e) {
|
||||
responseContainer.displayDefaultError();
|
||||
throw new RuntimeException("Error while updating the content", e);
|
||||
|
|
@ -354,23 +386,33 @@ public abstract class BaseChatToolWindowTabPanel implements ChatToolWindowTabPan
|
|||
JBUI.Borders.empty(8)));
|
||||
wrapper.setBackground(getPanelBackgroundColor());
|
||||
wrapper.add(userPromptTextArea, BorderLayout.SOUTH);
|
||||
|
||||
if (model != null) {
|
||||
var header = new JPanel(new BorderLayout());
|
||||
header.setBackground(getPanelBackgroundColor());
|
||||
header.setBorder(JBUI.Borders.emptyBottom(8));
|
||||
if ("YouCode".equals(model)) {
|
||||
var messageBusConnection = ApplicationManager.getApplication().getMessageBus().connect();
|
||||
subscribeToYouModelChangeTopic();
|
||||
subscribeToYouAuthTopic();
|
||||
subscribeToYouSubscriptionTopic(messageBusConnection);
|
||||
subscribeToSignedOutTopic(messageBusConnection);
|
||||
header.add(gpt4CheckBox, BorderLayout.LINE_START);
|
||||
}
|
||||
header.add(modelIconWrapper, BorderLayout.LINE_END);
|
||||
wrapper.add(header);
|
||||
}
|
||||
|
||||
rootPanel.add(wrapper, gbc);
|
||||
userPromptTextArea.requestFocusInWindow();
|
||||
userPromptTextArea.requestFocus();
|
||||
}
|
||||
|
||||
private void subscribeToSignedOutTopic(MessageBusConnection messageBusConnection) {
|
||||
messageBusConnection.subscribe(
|
||||
SignedOutNotifier.SIGNED_OUT_TOPIC,
|
||||
(SignedOutNotifier) () -> gpt4CheckBox.setEnabled(false));
|
||||
}
|
||||
|
||||
private void subscribeToYouModelChangeTopic() {
|
||||
project.getMessageBus()
|
||||
.connect()
|
||||
|
|
@ -379,24 +421,26 @@ public abstract class BaseChatToolWindowTabPanel implements ChatToolWindowTabPan
|
|||
(YouModelChangeNotifier) gpt4CheckBox::setSelected);
|
||||
}
|
||||
|
||||
private void subscribeToYouAuthTopic() {
|
||||
ApplicationManager.getApplication()
|
||||
.getMessageBus()
|
||||
.connect()
|
||||
.subscribe(AuthenticationNotifier.AUTHENTICATION_TOPIC,
|
||||
(AuthenticationNotifier) () -> gpt4CheckBox.setEnabled(true));
|
||||
private void subscribeToYouSubscriptionTopic(MessageBusConnection messageBusConnection) {
|
||||
messageBusConnection.subscribe(
|
||||
YouSubscriptionNotifier.SUBSCRIPTION_TOPIC,
|
||||
(YouSubscriptionNotifier) () -> {
|
||||
displayLandingView();
|
||||
gpt4CheckBox.setEnabled(true);
|
||||
});
|
||||
}
|
||||
|
||||
private JBCheckBox createGPT4ModelCheckBox() {
|
||||
var gpt4CheckBox = new JBCheckBox("Use GPT-4 model");
|
||||
var gpt4CheckBox = new JBCheckBox(CodeGPTBundle.get("toolwindow.chat.youProCheckBox.text"));
|
||||
gpt4CheckBox.setOpaque(false);
|
||||
gpt4CheckBox.setEnabled(YouUserManager.getInstance().isAuthenticated());
|
||||
gpt4CheckBox.setEnabled(YouUserManager.getInstance().isSubscribed());
|
||||
gpt4CheckBox.setSelected(YouSettingsState.getInstance().isUseGPT4Model());
|
||||
gpt4CheckBox.setToolTipText(getTooltipText(gpt4CheckBox.isSelected()));
|
||||
gpt4CheckBox.addChangeListener(e -> {
|
||||
var selected = ((JBCheckBox) e.getSource()).isSelected();
|
||||
var tooltipText = getTooltipText(selected);
|
||||
gpt4CheckBox.setToolTipText(tooltipText);
|
||||
// TODO: Remove
|
||||
project.getMessageBus()
|
||||
.syncPublisher(YouModelChangeNotifier.YOU_MODEL_CHANGE_NOTIFIER_TOPIC)
|
||||
.modelChanged(selected);
|
||||
|
|
@ -406,9 +450,12 @@ public abstract class BaseChatToolWindowTabPanel implements ChatToolWindowTabPan
|
|||
}
|
||||
|
||||
private String getTooltipText(boolean selected) {
|
||||
return selected ?
|
||||
"Turn off for faster responses" :
|
||||
"<html>Turn on for complex queries, enable by creating an account on you.com<br />and signing in from plugin settings.<br />Use CodeGPT coupon for free month of GPT-4.</html>";
|
||||
if (YouUserManager.getInstance().isSubscribed()) {
|
||||
return selected ?
|
||||
CodeGPTBundle.get("toolwindow.chat.youProCheckBox.disable") :
|
||||
CodeGPTBundle.get("toolwindow.chat.youProCheckBox.enable");
|
||||
}
|
||||
return CodeGPTBundle.get("toolwindow.chat.youProCheckBox.notAllowed");
|
||||
}
|
||||
|
||||
private String getClientCode() {
|
||||
|
|
@ -422,6 +469,9 @@ public abstract class BaseChatToolWindowTabPanel implements ChatToolWindowTabPan
|
|||
if (settings.isUseYouService()) {
|
||||
return "you.chat.completion";
|
||||
}
|
||||
if (settings.isUseLlamaService()) {
|
||||
return "llama.chat.completion";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
@ -436,7 +486,25 @@ public abstract class BaseChatToolWindowTabPanel implements ChatToolWindowTabPan
|
|||
if (settings.isUseYouService()) {
|
||||
return "YouCode";
|
||||
}
|
||||
if (settings.isUseLlamaService()) {
|
||||
var llamaSettings = LlamaSettingsState.getInstance();
|
||||
if (llamaSettings.isUseCustomModel()) {
|
||||
var filePath = llamaSettings.getCustomLlamaModelPath();
|
||||
int lastSeparatorIndex = filePath.lastIndexOf('/');
|
||||
if (lastSeparatorIndex == -1) {
|
||||
return filePath;
|
||||
}
|
||||
return filePath.substring(lastSeparatorIndex + 1);
|
||||
}
|
||||
var huggingFaceModel = llamaSettings.getHuggingFaceModel();
|
||||
var llamaModel = LlamaModel.findByHuggingFaceModel(huggingFaceModel);
|
||||
return String.format(
|
||||
"%s %dB (Q%d)",
|
||||
llamaModel.getLabel(),
|
||||
huggingFaceModel.getParameterSize(),
|
||||
huggingFaceModel.getQuantization());
|
||||
}
|
||||
|
||||
return null;
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ public class StreamParser {
|
|||
private boolean isProcessingCode;
|
||||
|
||||
public List<StreamParseResponse> parse(String message) {
|
||||
message = message.replace("\r", "");
|
||||
messageBuilder.append(message);
|
||||
|
||||
Pattern pattern = Pattern.compile(CODE_BLOCK_STARTING_REGEX);
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ import com.vladsch.flexmark.util.data.MutableDataSet;
|
|||
import ee.carlrobert.codegpt.actions.ActionType;
|
||||
import ee.carlrobert.codegpt.completions.you.YouSerpResult;
|
||||
import ee.carlrobert.codegpt.settings.SettingsConfigurable;
|
||||
import ee.carlrobert.codegpt.settings.state.SettingsState;
|
||||
import ee.carlrobert.codegpt.telemetry.TelemetryAction;
|
||||
import ee.carlrobert.codegpt.toolwindow.chat.ResponseNodeRenderer;
|
||||
import ee.carlrobert.codegpt.toolwindow.chat.StreamParser;
|
||||
|
|
@ -231,13 +230,13 @@ public class ChatMessageResponseBody extends JPanel {
|
|||
add(currentlyProcessedElement);
|
||||
}
|
||||
|
||||
private void prepareProcessingCodeResponse(String code, String language) {
|
||||
private void prepareProcessingCodeResponse(String code, String markdownLanguage) {
|
||||
hideCarets();
|
||||
currentlyProcessedTextPane = null;
|
||||
currentlyProcessedEditor = new ResponseEditor(
|
||||
project,
|
||||
code,
|
||||
language,
|
||||
markdownLanguage,
|
||||
parentDisposable);
|
||||
currentlyProcessedElement = new ResponseWrapper();
|
||||
|
||||
|
|
@ -249,15 +248,13 @@ public class ChatMessageResponseBody extends JPanel {
|
|||
var editor = currentlyProcessedEditor.getEditor();
|
||||
var document = editor.getDocument();
|
||||
var application = ApplicationManager.getApplication();
|
||||
Runnable updateDocumentRunnable = () -> {
|
||||
application.runWriteAction(() ->
|
||||
WriteCommandAction.runWriteCommandAction(project, () -> {
|
||||
document.replaceString(0, document.getTextLength(), code);
|
||||
editor.getCaretModel().moveToOffset(code.length());
|
||||
editor.getComponent().revalidate();
|
||||
editor.getComponent().repaint();
|
||||
}));
|
||||
};
|
||||
Runnable updateDocumentRunnable = () -> application.runWriteAction(() ->
|
||||
WriteCommandAction.runWriteCommandAction(project, () -> {
|
||||
document.replaceString(0, document.getTextLength(), code);
|
||||
editor.getCaretModel().moveToOffset(code.length());
|
||||
editor.getComponent().revalidate();
|
||||
editor.getComponent().repaint();
|
||||
}));
|
||||
|
||||
if (application.isUnitTestMode()) {
|
||||
application.invokeAndWait(updateDocumentRunnable);
|
||||
|
|
@ -267,8 +264,7 @@ public class ChatMessageResponseBody extends JPanel {
|
|||
}
|
||||
|
||||
private JTextPane createTextPane() {
|
||||
var textPane = new JTextPane();
|
||||
textPane.addHyperlinkListener(event -> {
|
||||
var textPane = SwingUtils.createTextPane(event -> {
|
||||
if (FileUtil.exists(event.getDescription()) && ACTIVATED.equals(event.getEventType())) {
|
||||
VirtualFile file = LocalFileSystem.getInstance().findFileByPath(event.getDescription());
|
||||
FileEditorManager.getInstance(project).openFile(Objects.requireNonNull(file), true);
|
||||
|
|
@ -277,14 +273,10 @@ public class ChatMessageResponseBody extends JPanel {
|
|||
|
||||
SwingUtils.handleHyperlinkClicked(event);
|
||||
});
|
||||
textPane.setContentType("text/html");
|
||||
textPane.putClientProperty(JTextPane.HONOR_DISPLAY_PROPERTIES, true);
|
||||
textPane.setCaretPosition(textPane.getDocument().getLength());
|
||||
textPane.setBackground(getBackground());
|
||||
textPane.setFocusable(true);
|
||||
textPane.getCaret().setVisible(true);
|
||||
textPane.setEditable(false);
|
||||
textPane.setCaretPosition(textPane.getDocument().getLength());
|
||||
textPane.setBorder(JBUI.Borders.empty());
|
||||
textPane.setBackground(getBackground());
|
||||
return textPane;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import com.intellij.ui.JBColor;
|
|||
import com.intellij.ui.components.JBLabel;
|
||||
import com.intellij.util.ui.JBFont;
|
||||
import com.intellij.util.ui.JBUI;
|
||||
import ee.carlrobert.codegpt.CodeGPTBundle;
|
||||
import ee.carlrobert.codegpt.Icons;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.FlowLayout;
|
||||
|
|
@ -82,7 +83,10 @@ public class ResponsePanel extends JPanel {
|
|||
|
||||
public void addReloadAction(Runnable onReload) {
|
||||
addIconActionButton(new IconActionButton(
|
||||
new AnAction("Reload Response", "Reload response description", Actions.Refresh) {
|
||||
new AnAction(
|
||||
CodeGPTBundle.get("toolwindow.chat.response.action.reloadResponse.text"),
|
||||
CodeGPTBundle.get("toolwindow.chat.response.action.reloadResponse.description"),
|
||||
Actions.Refresh) {
|
||||
@Override
|
||||
public void actionPerformed(@NotNull AnActionEvent e) {
|
||||
enableActions(false);
|
||||
|
|
@ -93,7 +97,10 @@ public class ResponsePanel extends JPanel {
|
|||
|
||||
public void addDeleteAction(Runnable onDelete) {
|
||||
addIconActionButton(new IconActionButton(
|
||||
new AnAction("Delete Response", "Delete response description", Actions.GC) {
|
||||
new AnAction(
|
||||
CodeGPTBundle.get("toolwindow.chat.response.action.deleteResponse.text"),
|
||||
CodeGPTBundle.get("toolwindow.chat.response.action.deleteResponse.description"),
|
||||
Actions.GC) {
|
||||
@Override
|
||||
public void actionPerformed(@NotNull AnActionEvent e) {
|
||||
onDelete.run();
|
||||
|
|
@ -109,7 +116,10 @@ public class ResponsePanel extends JPanel {
|
|||
}
|
||||
|
||||
private JBLabel getIconLabel() {
|
||||
return new JBLabel("CodeGPT", Icons.DefaultIcon, SwingConstants.LEADING)
|
||||
return new JBLabel(
|
||||
CodeGPTBundle.get("project.label"),
|
||||
Icons.DefaultIcon,
|
||||
SwingConstants.LEADING)
|
||||
.setAllowAutoWrapping(true)
|
||||
.withFont(JBFont.label().asBold());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import com.intellij.ui.JBColor;
|
|||
import com.intellij.ui.components.JBTextArea;
|
||||
import com.intellij.util.ui.JBUI;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import ee.carlrobert.codegpt.CodeGPTBundle;
|
||||
import ee.carlrobert.codegpt.Icons;
|
||||
import ee.carlrobert.codegpt.completions.CompletionRequestHandler;
|
||||
import ee.carlrobert.codegpt.util.SwingUtils;
|
||||
|
|
@ -55,7 +56,7 @@ public class UserPromptTextArea extends JPanel {
|
|||
textArea.setBackground(BACKGROUND_COLOR);
|
||||
textArea.setLineWrap(true);
|
||||
textArea.setWrapStyleWord(true);
|
||||
textArea.getEmptyText().setText("Ask me anything");
|
||||
textArea.getEmptyText().setText(CodeGPTBundle.get("toolwindow.chat.textArea.emptyText"));
|
||||
textArea.setBorder(JBUI.Borders.empty(8, 4));
|
||||
var input = textArea.getInputMap();
|
||||
input.put(KeyStroke.getKeyStroke("ENTER"), TEXT_SUBMIT);
|
||||
|
|
@ -171,6 +172,7 @@ public class UserPromptTextArea extends JPanel {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: IconActionButton?
|
||||
private JButton createIconButton(Icon icon, @Nullable Runnable submitListener) {
|
||||
var button = SwingUtils.createIconButton(icon);
|
||||
if (submitListener != null) {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import static com.intellij.openapi.ui.DialogWrapper.OK_EXIT_CODE;
|
|||
import static ee.carlrobert.codegpt.util.ThemeUtils.getPanelBackgroundColor;
|
||||
import static javax.swing.event.HyperlinkEvent.EventType.ACTIVATED;
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.options.ShowSettingsUtil;
|
||||
import com.intellij.openapi.project.Project;
|
||||
|
|
@ -14,9 +13,6 @@ import ee.carlrobert.codegpt.indexes.CodebaseIndexingTask;
|
|||
import ee.carlrobert.codegpt.indexes.FolderStructureTreePanel;
|
||||
import ee.carlrobert.codegpt.settings.SettingsConfigurable;
|
||||
import ee.carlrobert.codegpt.toolwindow.chat.components.ResponsePanel;
|
||||
import ee.carlrobert.codegpt.completions.you.YouUserManager;
|
||||
import ee.carlrobert.codegpt.completions.you.auth.AuthenticationNotifier;
|
||||
import ee.carlrobert.codegpt.completions.you.auth.SignedOutNotifier;
|
||||
import ee.carlrobert.codegpt.util.OverlayUtils;
|
||||
import ee.carlrobert.codegpt.util.SwingUtils;
|
||||
import ee.carlrobert.vector.VectorStore;
|
||||
|
|
@ -25,6 +21,7 @@ import javax.swing.event.HyperlinkEvent;
|
|||
|
||||
@FunctionalInterface
|
||||
interface ActionEvent {
|
||||
|
||||
void handleAction(String prompt);
|
||||
}
|
||||
|
||||
|
|
@ -43,43 +40,30 @@ class ContextualChatToolWindowLandingPanel extends ResponsePanel {
|
|||
.connect()
|
||||
.subscribe(CodebaseIndexingCompletedNotifier.INDEXING_COMPLETED_TOPIC,
|
||||
(CodebaseIndexingCompletedNotifier) () -> updateContent(createContent()));
|
||||
|
||||
var messageBusConnection = ApplicationManager.getApplication().getMessageBus().connect();
|
||||
messageBusConnection.subscribe(AuthenticationNotifier.AUTHENTICATION_TOPIC, (AuthenticationNotifier) () -> updateContent(createContent()));
|
||||
messageBusConnection.subscribe(SignedOutNotifier.SIGNED_OUT_TOPIC, (SignedOutNotifier) () -> updateContent(createContent()));
|
||||
}
|
||||
|
||||
private JTextPane createContent() {
|
||||
var description = createTextPane();
|
||||
var userManager = YouUserManager.getInstance();
|
||||
|
||||
if (userManager.getAuthenticationResponse() == null) {
|
||||
description.setText("<html>" +
|
||||
"<p style=\"margin-top: 4px; margin-bottom: 4px;\">It looks like you haven't logged in. Please <a href=\"LOGIN\">log in</a> to use the feature.</p>" +
|
||||
"</html>");
|
||||
return description;
|
||||
}
|
||||
|
||||
if (!userManager.isSubscribed()) {
|
||||
description.setText("<html>" +
|
||||
"<p style=\"margin-top: 4px; margin-bottom: 4px;\">You are not currently subscribed to any plan.</p>" +
|
||||
"</html>");
|
||||
return description;
|
||||
}
|
||||
|
||||
if (VectorStore.getInstance(CodeGPTPlugin.getPluginBasePath()).isIndexExists()) {
|
||||
description.setText("<html>" +
|
||||
"<p style=\"margin-top: 4px; margin-bottom: 4px;\">Feel free to ask me anything about your codebase, and I'll be your helpful guide, dedicated to providing you with the best answers possible!</p>" +
|
||||
"<p style=\"margin-top: 4px; margin-bottom: 4px;\">Here are a few examples of how I might be helpful:</p>" +
|
||||
"<p style=\"margin-top: 4px; margin-bottom: 4px;\">Feel free to ask me anything about your codebase, and I'll be your helpful guide, dedicated to providing you with the best answers possible!</p>"
|
||||
+
|
||||
"<p style=\"margin-top: 4px; margin-bottom: 4px;\">Here are a few examples of how I might be helpful:</p>"
|
||||
+
|
||||
"<ul>" +
|
||||
"<li><a href=\"LIST_DEPENDENCIES\">List all the dependencies that the project uses</a></li" +
|
||||
"<li><a href=\"SCHEDULED_TASKS\">Are there any scheduled tasks or background jobs running in our codebase, and if so, what are they responsible for?</a></li>" +
|
||||
"<li><a href=\"AUTHENTICATION_MECHANISM\">Can you provide an overview of the authentication and authorization mechanism implemented in our application?</a></li>" +
|
||||
"<li><a href=\"LIST_DEPENDENCIES\">List all the dependencies that the project uses</a></li"
|
||||
+
|
||||
"<li><a href=\"SCHEDULED_TASKS\">Are there any scheduled tasks or background jobs running in our codebase, and if so, what are they responsible for?</a></li>"
|
||||
+
|
||||
"<li><a href=\"AUTHENTICATION_MECHANISM\">Can you provide an overview of the authentication and authorization mechanism implemented in our application?</a></li>"
|
||||
+
|
||||
"</html>");
|
||||
} else {
|
||||
description.setText("<html>" +
|
||||
"<p style=\"margin-top: 4px; margin-bottom: 4px;\">It looks like you haven't indexed your codebase yet.</p>" +
|
||||
"<p style=\"margin-top: 4px; margin-bottom: 4px;\"><a href=\"START_INDEXING\">Start indexing</a> your codebase to get access to contextual chat experience.</p>" +
|
||||
"<p style=\"margin-top: 4px; margin-bottom: 4px;\">It looks like you haven't indexed your codebase yet.</p>"
|
||||
+
|
||||
"<p style=\"margin-top: 4px; margin-bottom: 4px;\"><a href=\"START_INDEXING\">Start indexing</a> your codebase to get access to contextual chat experience.</p>"
|
||||
+
|
||||
"</html>");
|
||||
}
|
||||
|
||||
|
|
@ -87,13 +71,8 @@ class ContextualChatToolWindowLandingPanel extends ResponsePanel {
|
|||
}
|
||||
|
||||
private JTextPane createTextPane() {
|
||||
var textPane = new JTextPane();
|
||||
textPane.addHyperlinkListener(this::handleHyperlinkClicked);
|
||||
var textPane = SwingUtils.createTextPane(this::handleHyperlinkClicked);
|
||||
textPane.setBackground(getPanelBackgroundColor());
|
||||
textPane.setContentType("text/html");
|
||||
textPane.putClientProperty(JTextPane.HONOR_DISPLAY_PROPERTIES, true);
|
||||
textPane.setFocusable(false);
|
||||
textPane.setEditable(false);
|
||||
return textPane;
|
||||
}
|
||||
|
||||
|
|
@ -112,7 +91,8 @@ class ContextualChatToolWindowLandingPanel extends ResponsePanel {
|
|||
"Are there any scheduled tasks or background jobs running in our codebase, and if so, what are they responsible for?");
|
||||
break;
|
||||
case "AUTHENTICATION_MECHANISM":
|
||||
actionEvent.handleAction("Can you provide an overview of the authentication and authorization mechanism implemented in our application?");
|
||||
actionEvent.handleAction(
|
||||
"Can you provide an overview of the authentication and authorization mechanism implemented in our application?");
|
||||
break;
|
||||
case "START_INDEXING":
|
||||
var folderStructureTreePanel = new FolderStructureTreePanel(project);
|
||||
|
|
|
|||
|
|
@ -1,14 +1,9 @@
|
|||
package ee.carlrobert.codegpt.toolwindow.chat.contextual;
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import ee.carlrobert.codegpt.completions.you.YouUserManager;
|
||||
import ee.carlrobert.codegpt.conversations.Conversation;
|
||||
import ee.carlrobert.codegpt.conversations.message.Message;
|
||||
import ee.carlrobert.codegpt.indexes.CodebaseIndexingCompletedNotifier;
|
||||
import ee.carlrobert.codegpt.toolwindow.chat.BaseChatToolWindowTabPanel;
|
||||
import ee.carlrobert.codegpt.completions.you.auth.AuthenticationNotifier;
|
||||
import ee.carlrobert.codegpt.completions.you.auth.SignedOutNotifier;
|
||||
import javax.swing.JComponent;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
|
|
@ -17,19 +12,6 @@ public class ContextualChatToolWindowTabPanel extends BaseChatToolWindowTabPanel
|
|||
public ContextualChatToolWindowTabPanel(@NotNull Project project) {
|
||||
super(project, true);
|
||||
displayLandingView();
|
||||
userPromptTextArea.setTextAreaEnabled(YouUserManager.getInstance().isSubscribed());
|
||||
|
||||
project.getMessageBus()
|
||||
.connect()
|
||||
.subscribe(CodebaseIndexingCompletedNotifier.INDEXING_COMPLETED_TOPIC,
|
||||
(CodebaseIndexingCompletedNotifier) () -> userPromptTextArea.setTextAreaEnabled(
|
||||
YouUserManager.getInstance().isSubscribed()));
|
||||
|
||||
var messageBusConnection = ApplicationManager.getApplication().getMessageBus().connect();
|
||||
messageBusConnection.subscribe(AuthenticationNotifier.AUTHENTICATION_TOPIC,
|
||||
(AuthenticationNotifier) () -> userPromptTextArea.setTextAreaEnabled(
|
||||
YouUserManager.getInstance().isSubscribed()));
|
||||
messageBusConnection.subscribe(SignedOutNotifier.SIGNED_OUT_TOPIC, (SignedOutNotifier) () -> userPromptTextArea.setTextAreaEnabled(false));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -33,6 +33,8 @@ public class CopyAction extends TrackableAction {
|
|||
var locationOnScreen = ((MouseEvent) event.getInputEvent()).getLocationOnScreen();
|
||||
locationOnScreen.y = locationOnScreen.y - 16;
|
||||
|
||||
OverlayUtils.showInfoBalloon("Code copied!", locationOnScreen);
|
||||
OverlayUtils.showInfoBalloon(
|
||||
CodeGPTBundle.get("toolwindow.chat.editor.action.copy.success"),
|
||||
locationOnScreen);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,7 +34,9 @@ public class EditAction extends TrackableAction {
|
|||
settings.setCaretRowShown(!viewer);
|
||||
|
||||
event.getPresentation().setIcon(viewer ? Actions.EditSource : Actions.Show);
|
||||
event.getPresentation().setText(viewer ? "Edit Source" : "Disable Editing");
|
||||
event.getPresentation().setText(viewer ?
|
||||
CodeGPTBundle.get("toolwindow.chat.editor.action.edit.title") :
|
||||
CodeGPTBundle.get("toolwindow.chat.editor.action.disableEditing.title"));
|
||||
|
||||
var locationOnScreen = ((MouseEvent) event.getInputEvent()).getLocationOnScreen();
|
||||
locationOnScreen.y = locationOnScreen.y - 16;
|
||||
|
|
|
|||
|
|
@ -41,13 +41,8 @@ class StandardChatToolWindowLandingPanel extends ResponsePanel {
|
|||
}
|
||||
|
||||
private JTextPane createTextPane() {
|
||||
var textPane = new JTextPane();
|
||||
textPane.addHyperlinkListener(this::handleHyperlinkClicked);
|
||||
var textPane = SwingUtils.createTextPane(this::handleHyperlinkClicked);
|
||||
textPane.setBackground(getPanelBackgroundColor());
|
||||
textPane.setContentType("text/html");
|
||||
textPane.putClientProperty(JTextPane.HONOR_DISPLAY_PROPERTIES, true);
|
||||
textPane.setFocusable(false);
|
||||
textPane.setEditable(false);
|
||||
return textPane;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,8 +3,6 @@ package ee.carlrobert.codegpt.toolwindow.chat.standard;
|
|||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.actionSystem.ActionManager;
|
||||
import com.intellij.openapi.actionSystem.ActionToolbar;
|
||||
import com.intellij.openapi.actionSystem.Constraints;
|
||||
import com.intellij.openapi.actionSystem.DefaultActionGroup;
|
||||
import com.intellij.openapi.actionSystem.DefaultCompactActionGroup;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.SimpleToolWindowPanel;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
package ee.carlrobert.codegpt.util;
|
||||
|
||||
import static java.lang.String.format;
|
||||
|
||||
import ee.carlrobert.codegpt.util.file.FileUtils;
|
||||
|
||||
public class DownloadingUtils {
|
||||
|
||||
private static final int BYTES_IN_MB = 1024 * 1024;
|
||||
|
||||
public static String getFormattedDownloadProgress(long startTime, long fileSize, long bytesRead) {
|
||||
long timeElapsed = System.currentTimeMillis() - startTime;
|
||||
|
||||
double speed = ((double) bytesRead / timeElapsed) * 1000 / BYTES_IN_MB;
|
||||
double percent = (double) bytesRead / fileSize * 100;
|
||||
double downloadedMB = (double) bytesRead / BYTES_IN_MB;
|
||||
double totalMB = (double) fileSize / BYTES_IN_MB;
|
||||
double remainingMB = totalMB - downloadedMB;
|
||||
|
||||
return format(
|
||||
"%s of %s (%.2f%%), Speed: %.2f MB/sec, Time left: %s",
|
||||
FileUtils.convertFileSize((long) downloadedMB * BYTES_IN_MB),
|
||||
FileUtils.convertFileSize((long) totalMB * BYTES_IN_MB),
|
||||
percent,
|
||||
speed,
|
||||
getTimeLeftFormattedString(speed, remainingMB));
|
||||
}
|
||||
|
||||
private static String getTimeLeftFormattedString(double speed, double remainingMB) {
|
||||
double timeLeftSec = speed > 0 ? remainingMB / speed : 0;
|
||||
long hours = (long) (timeLeftSec / 3600);
|
||||
long minutes = (long) ((timeLeftSec % 3600) / 60);
|
||||
long seconds = (long) (timeLeftSec % 60);
|
||||
|
||||
return format("%02d:%02d:%02d", hours, minutes, seconds);
|
||||
}
|
||||
}
|
||||
|
|
@ -63,7 +63,11 @@ public final class EditorUtils {
|
|||
var editor = getSelectedEditor(project);
|
||||
if (editor != null) {
|
||||
var selectionModel = editor.getSelectionModel();
|
||||
editor.getDocument().replaceString(selectionModel.getSelectionStart(), selectionModel.getSelectionEnd(), text);
|
||||
editor.getDocument()
|
||||
.replaceString(
|
||||
selectionModel.getSelectionStart(),
|
||||
selectionModel.getSelectionEnd(),
|
||||
text);
|
||||
editor.getContentComponent().requestFocus();
|
||||
selectionModel.removeSelection();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import com.intellij.openapi.ui.MessageDialogBuilder;
|
|||
import com.intellij.openapi.ui.MessageType;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
import com.intellij.openapi.ui.popup.Balloon;
|
||||
import com.intellij.openapi.ui.popup.Balloon.Position;
|
||||
import com.intellij.openapi.ui.popup.JBPopupFactory;
|
||||
import com.intellij.ui.awt.RelativePoint;
|
||||
import com.intellij.ui.components.JBLabel;
|
||||
|
|
@ -27,6 +28,7 @@ import ee.carlrobert.codegpt.conversations.ConversationsState;
|
|||
import ee.carlrobert.codegpt.indexes.FolderStructureTreePanel;
|
||||
import java.awt.Point;
|
||||
import java.awt.event.MouseEvent;
|
||||
import javax.swing.JComponent;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class OverlayUtils {
|
||||
|
|
@ -117,4 +119,12 @@ public class OverlayUtils {
|
|||
.createBalloon()
|
||||
.show(RelativePoint.fromScreen(locationOnScreen), Balloon.Position.above);
|
||||
}
|
||||
|
||||
public static void showBalloon(String content, MessageType messageType, JComponent component) {
|
||||
JBPopupFactory.getInstance()
|
||||
.createHtmlTextBalloonBuilder(content, messageType, null)
|
||||
.setFadeoutTime(2500)
|
||||
.createBalloon()
|
||||
.show(RelativePoint.getSouthOf(component), Position.below);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,26 +4,33 @@ import static javax.swing.event.HyperlinkEvent.EventType.ACTIVATED;
|
|||
|
||||
import com.intellij.ide.BrowserUtil;
|
||||
import com.intellij.util.ui.UI;
|
||||
import java.awt.Component;
|
||||
import java.awt.Desktop;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.io.IOException;
|
||||
import java.net.URISyntaxException;
|
||||
import javax.swing.AbstractAction;
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.Box;
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JTextArea;
|
||||
import javax.swing.JTextPane;
|
||||
import javax.swing.KeyStroke;
|
||||
import javax.swing.event.HyperlinkEvent;
|
||||
import javax.swing.event.HyperlinkListener;
|
||||
|
||||
public class SwingUtils {
|
||||
|
||||
public static JTextPane createTextPane(HyperlinkListener listener) {
|
||||
var textPane = new JTextPane();
|
||||
textPane.putClientProperty(JTextPane.HONOR_DISPLAY_PROPERTIES, true);
|
||||
textPane.addHyperlinkListener(listener);
|
||||
textPane.setContentType("text/html");
|
||||
textPane.setEditable(false);
|
||||
return textPane;
|
||||
}
|
||||
|
||||
public static JButton createIconButton(Icon icon) {
|
||||
var button = new JButton(icon);
|
||||
button.setBorder(BorderFactory.createEmptyBorder());
|
||||
|
|
@ -32,13 +39,6 @@ public class SwingUtils {
|
|||
return button;
|
||||
}
|
||||
|
||||
public static Box justifyLeft(Component component) {
|
||||
Box box = Box.createHorizontalBox();
|
||||
box.add(component);
|
||||
box.add(Box.createHorizontalGlue());
|
||||
return box;
|
||||
}
|
||||
|
||||
public static void setEqualLabelWidths(JPanel firstPanel, JPanel secondPanel) {
|
||||
var firstLabel = firstPanel.getComponents()[0];
|
||||
var secondLabel = secondPanel.getComponents()[0];
|
||||
|
|
@ -47,10 +47,6 @@ public class SwingUtils {
|
|||
}
|
||||
}
|
||||
|
||||
public static JPanel createPanel(JComponent component, String label) {
|
||||
return createPanel(component, label, false);
|
||||
}
|
||||
|
||||
public static JPanel createPanel(JComponent component, String label, boolean resizeX) {
|
||||
return UI.PanelFactory.panel(component)
|
||||
.withLabel(label)
|
||||
|
|
|
|||
|
|
@ -6,16 +6,23 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
|||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager;
|
||||
import com.intellij.openapi.progress.ProgressIndicator;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import ee.carlrobert.codegpt.CodeGPTPlugin;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.net.URL;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.Channels;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
|
@ -40,6 +47,33 @@ public class FileUtils {
|
|||
}
|
||||
}
|
||||
|
||||
public static void copyFileWithProgress(
|
||||
String fileName,
|
||||
URL url,
|
||||
long[] bytesRead,
|
||||
long fileSize,
|
||||
ProgressIndicator indicator) throws IOException {
|
||||
FileUtils.tryCreateDirectory(CodeGPTPlugin.getLlamaModelsPath());
|
||||
|
||||
try (
|
||||
var readableByteChannel = Channels.newChannel(url.openStream());
|
||||
var fileOutputStream = new FileOutputStream(
|
||||
CodeGPTPlugin.getLlamaModelsPath() + File.separator + fileName)) {
|
||||
var buffer = ByteBuffer.allocateDirect(1024 * 10);
|
||||
|
||||
while (readableByteChannel.read(buffer) != -1) {
|
||||
if (indicator.isCanceled()) {
|
||||
readableByteChannel.close();
|
||||
break;
|
||||
}
|
||||
buffer.flip();
|
||||
bytesRead[0] += fileOutputStream.getChannel().write(buffer);
|
||||
buffer.clear();
|
||||
indicator.setFraction((double) bytesRead[0] / fileSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static VirtualFile getEditorFile(@NotNull Editor editor) {
|
||||
return FileDocumentManager.getInstance().getFile(editor.getDocument());
|
||||
}
|
||||
|
|
@ -115,6 +149,19 @@ public class FileUtils {
|
|||
}
|
||||
}
|
||||
|
||||
public static String convertFileSize(long fileSizeInBytes) {
|
||||
String[] units = {"B", "KB", "MB", "GB"};
|
||||
int unitIndex = 0;
|
||||
double fileSize = fileSizeInBytes;
|
||||
|
||||
while (fileSize >= 1024 && unitIndex < units.length - 1) {
|
||||
fileSize /= 1024;
|
||||
unitIndex++;
|
||||
}
|
||||
|
||||
return new DecimalFormat("#.##").format(fileSize) + " " + units[unitIndex];
|
||||
}
|
||||
|
||||
private static Optional<Map.Entry<String, String>> findFirstExtension(
|
||||
List<LanguageFileExtensionDetails> languageFileExtensionMappings,
|
||||
String language) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue