feat: add support for auto resolving compilation errors (#318)

This commit is contained in:
Carl-Robert 2023-12-29 16:41:47 +02:00 committed by GitHub
parent 7031a6dc73
commit f831a1facd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
51 changed files with 919 additions and 595 deletions

View file

@ -1,10 +1,10 @@
package ee.carlrobert.codegpt;
import com.intellij.openapi.util.Key;
import ee.carlrobert.embedding.CheckedFile;
import ee.carlrobert.embedding.ReferencedFile;
import java.util.List;
public class CodeGPTKeys {
public static final Key<List<CheckedFile>> SELECTED_FILES = Key.create("selectedFiles");
public static final Key<List<ReferencedFile>> SELECTED_FILES = Key.create("selectedFiles");
}

View file

@ -0,0 +1,109 @@
package ee.carlrobert.codegpt;
import static ee.carlrobert.codegpt.completions.ConversationType.FIX_COMPILE_ERRORS;
import static java.lang.String.format;
import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toList;
import com.intellij.compiler.CompilerMessageImpl;
import com.intellij.notification.NotificationAction;
import com.intellij.notification.NotificationType;
import com.intellij.openapi.compiler.CompilationStatusListener;
import com.intellij.openapi.compiler.CompileContext;
import com.intellij.openapi.compiler.CompilerMessage;
import com.intellij.openapi.compiler.CompilerMessageCategory;
import com.intellij.openapi.project.Project;
import ee.carlrobert.codegpt.completions.CompletionRequestProvider;
import ee.carlrobert.codegpt.conversations.message.Message;
import ee.carlrobert.codegpt.settings.configuration.ConfigurationState;
import ee.carlrobert.codegpt.toolwindow.chat.standard.StandardChatToolWindowContentManager;
import ee.carlrobert.codegpt.ui.OverlayUtil;
import ee.carlrobert.embedding.ReferencedFile;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import org.jetbrains.annotations.NotNull;
public class ProjectCompilationStatusListener implements CompilationStatusListener {
private final Project project;
public ProjectCompilationStatusListener(Project project) {
this.project = project;
}
@Override
public void compilationFinished(
boolean aborted,
int errors,
int warnings,
@NotNull CompileContext compileContext) {
var configuration = ConfigurationState.getInstance();
var success = !configuration.isCaptureCompileErrors()
|| (!aborted && errors == 0 && warnings == 0);
if (success) {
return;
}
if (errors > 0) {
OverlayUtil.getDefaultNotification(
CodeGPTBundle.get("notification.compilationError.description"),
NotificationType.INFORMATION)
.addAction(NotificationAction.createSimpleExpiring(
CodeGPTBundle.get("notification.compilationError.okLabel"),
() -> project.getService(StandardChatToolWindowContentManager.class)
.sendMessage(getMultiFileMessage(compileContext), FIX_COMPILE_ERRORS)))
.addAction(NotificationAction.createSimpleExpiring(
CodeGPTBundle.get("checkForUpdatesTask.notification.hideButton"),
() -> ConfigurationState.getInstance().setCaptureCompileErrors(false)))
.notify(project);
}
}
private Message getMultiFileMessage(CompileContext compileContext) {
var errorMapping = getErrorMapping(compileContext);
var prompt = errorMapping.values().stream()
.flatMap(Collection::stream)
.collect(joining("\n\n"));
var message = new Message("Fix the following compile errors:\n\n" + prompt);
message.setReferencedFilePaths(errorMapping.keySet().stream()
.map(ReferencedFile::getFilePath)
.collect(toList()));
message.setUserMessage(message.getPrompt());
message.setPrompt(CompletionRequestProvider.getPromptWithContext(
new ArrayList<>(errorMapping.keySet()),
prompt));
return message;
}
private HashMap<ReferencedFile, List<String>> getErrorMapping(CompileContext compileContext) {
var errorMapping = new HashMap<ReferencedFile, List<String>>();
for (var compilerMessage : compileContext.getMessages(CompilerMessageCategory.ERROR)) {
var key = new ReferencedFile(new File(compilerMessage.getVirtualFile().getPath()));
var prevValue = errorMapping.get(key);
if (prevValue == null) {
prevValue = new ArrayList<>();
}
prevValue.add(getCompilerErrorDetails(compilerMessage));
errorMapping.put(key, prevValue);
}
return errorMapping;
}
private String getCompilerErrorDetails(CompilerMessage compilerMessage) {
if (compilerMessage instanceof CompilerMessageImpl) {
return format(
"%s:%d:%d - `%s`",
compilerMessage.getVirtualFile().getName(),
((CompilerMessageImpl) compilerMessage).getLine(),
((CompilerMessageImpl) compilerMessage).getColumn(),
compilerMessage.getMessage());
}
return format(
"%s - `%s`",
compilerMessage.getVirtualFile().getName(),
compilerMessage.getMessage());
}
}

View file

@ -23,17 +23,13 @@ import com.intellij.openapi.vcs.ui.CommitMessage;
import ee.carlrobert.codegpt.CodeGPTBundle;
import ee.carlrobert.codegpt.EncodingManager;
import ee.carlrobert.codegpt.Icons;
import ee.carlrobert.codegpt.completions.CompletionClientProvider;
import ee.carlrobert.codegpt.completions.CompletionRequestService;
import ee.carlrobert.codegpt.credentials.AzureCredentialsManager;
import ee.carlrobert.codegpt.credentials.OpenAICredentialsManager;
import ee.carlrobert.codegpt.settings.configuration.ConfigurationState;
import ee.carlrobert.codegpt.settings.service.ServiceType;
import ee.carlrobert.codegpt.settings.state.OpenAISettingsState;
import ee.carlrobert.codegpt.settings.state.SettingsState;
import ee.carlrobert.codegpt.ui.OverlayUtil;
import ee.carlrobert.llm.client.openai.completion.ErrorDetails;
import ee.carlrobert.llm.client.openai.completion.request.OpenAIChatCompletionMessage;
import ee.carlrobert.llm.client.openai.completion.request.OpenAIChatCompletionRequest;
import ee.carlrobert.llm.completion.CompletionEventListener;
import java.io.BufferedReader;
import java.io.File;
@ -59,7 +55,7 @@ public class GenerateGitCommitMessageAction extends AnAction {
public void update(@NotNull AnActionEvent event) {
var selectedService = SettingsState.getInstance().getSelectedService();
if (selectedService == ServiceType.OPENAI || selectedService == ServiceType.AZURE) {
var filesSelected = !getCheckedFilePaths(event).isEmpty();
var filesSelected = !getReferencedFilePaths(event).isEmpty();
var callAllowed = (selectedService == ServiceType.OPENAI
&& OpenAICredentialsManager.getInstance().isApiKeySet())
|| (selectedService == ServiceType.AZURE
@ -82,7 +78,7 @@ public class GenerateGitCommitMessageAction extends AnAction {
return;
}
var gitDiff = getGitDiff(project, getCheckedFilePaths(event));
var gitDiff = getGitDiff(project, getReferencedFilePaths(event));
var tokenCount = encodingManager.countTokens(gitDiff);
if (tokenCount > 4096 && OverlayUtil.showTokenSoftLimitWarningDialog(tokenCount) != OK) {
return;
@ -91,25 +87,8 @@ public class GenerateGitCommitMessageAction extends AnAction {
var editor = getCommitMessageEditor(event);
if (editor != null) {
((EditorEx) editor).setCaretVisible(false);
generateMessage(project, editor, gitDiff);
}
}
private void generateMessage(Project project, Editor editor, String gitDiff) {
var request = new OpenAIChatCompletionRequest.Builder(List.of(
new OpenAIChatCompletionMessage("system",
ConfigurationState.getInstance().getCommitMessagePrompt()),
new OpenAIChatCompletionMessage("user", gitDiff)))
.setModel(OpenAISettingsState.getInstance().getModel())
.build();
var selectedService = SettingsState.getInstance().getSelectedService();
if (selectedService == ServiceType.OPENAI) {
CompletionClientProvider.getOpenAIClient()
.getChatCompletion(request, getEventListener(project, editor.getDocument()));
}
if (selectedService == ServiceType.AZURE) {
CompletionClientProvider.getAzureClient()
.getChatCompletion(request, getEventListener(project, editor.getDocument()));
CompletionRequestService.getInstance()
.generateCommitMessageAsync(gitDiff, getEventListener(project, editor.getDocument()));
}
}
@ -166,7 +145,7 @@ public class GenerateGitCommitMessageAction extends AnAction {
}
}
private @NotNull List<String> getCheckedFilePaths(AnActionEvent event) {
private @NotNull List<String> getReferencedFilePaths(AnActionEvent event) {
var changesBrowserBase = event.getData(ChangesBrowserBase.DATA_KEY);
if (changesBrowserBase == null) {
return List.of();

View file

@ -30,7 +30,7 @@ import ee.carlrobert.codegpt.ui.checkbox.FileCheckboxTree;
import ee.carlrobert.codegpt.ui.checkbox.PsiElementCheckboxTree;
import ee.carlrobert.codegpt.ui.checkbox.VirtualFileCheckboxTree;
import ee.carlrobert.codegpt.util.file.FileUtil;
import ee.carlrobert.embedding.CheckedFile;
import ee.carlrobert.embedding.ReferencedFile;
import java.awt.Dimension;
import java.io.IOException;
import java.nio.file.Files;
@ -62,7 +62,7 @@ public class IncludeFilesInContextAction extends AnAction {
throw new RuntimeException("Could not obtain file tree");
}
var totalTokensLabel = new TotalTokensLabel(checkboxTree.getCheckedFiles());
var totalTokensLabel = new TotalTokensLabel(checkboxTree.getReferencedFiles());
checkboxTree.addCheckboxTreeListener(new CheckboxTreeListener() {
@Override
public void nodeStateChanged(@NotNull CheckedTreeNode node) {
@ -81,10 +81,10 @@ public class IncludeFilesInContextAction extends AnAction {
totalTokensLabel,
checkboxTree);
if (show == OK_EXIT_CODE) {
project.putUserData(CodeGPTKeys.SELECTED_FILES, checkboxTree.getCheckedFiles());
project.putUserData(CodeGPTKeys.SELECTED_FILES, checkboxTree.getReferencedFiles());
project.getMessageBus()
.syncPublisher(IncludeFilesInContextNotifier.FILES_INCLUDED_IN_CONTEXT_TOPIC)
.filesIncluded(checkboxTree.getCheckedFiles());
.filesIncluded(checkboxTree.getReferencedFiles());
includedFilesSettings.setPromptTemplate(promptTemplateTextArea.getText());
includedFilesSettings.setRepeatableContext(repeatableContextTextArea.getText());
}
@ -111,9 +111,9 @@ public class IncludeFilesInContextAction extends AnAction {
private int fileCount;
private int totalTokens;
TotalTokensLabel(List<CheckedFile> checkedFiles) {
fileCount = checkedFiles.size();
totalTokens = calculateTotalTokens(checkedFiles);
TotalTokensLabel(List<ReferencedFile> referencedFiles) {
fileCount = referencedFiles.size();
totalTokens = calculateTotalTokens(referencedFiles);
updateText();
}
@ -167,8 +167,8 @@ public class IncludeFilesInContextAction extends AnAction {
FileUtil.convertLongValue(totalTokens)));
}
private int calculateTotalTokens(List<CheckedFile> checkedFiles) {
return checkedFiles.stream()
private int calculateTotalTokens(List<ReferencedFile> referencedFiles) {
return referencedFiles.stream()
.mapToInt(file -> encodingManager.countTokens(file.getFileContent()))
.sum();
}

View file

@ -1,7 +1,7 @@
package ee.carlrobert.codegpt.actions;
import com.intellij.util.messages.Topic;
import ee.carlrobert.embedding.CheckedFile;
import ee.carlrobert.embedding.ReferencedFile;
import java.util.List;
public interface IncludeFilesInContextNotifier {
@ -9,5 +9,5 @@ public interface IncludeFilesInContextNotifier {
Topic<IncludeFilesInContextNotifier> FILES_INCLUDED_IN_CONTEXT_TOPIC =
Topic.create("filesIncludedInContext", IncludeFilesInContextNotifier.class);
void filesIncluded(List<CheckedFile> includedFiles);
void filesIncluded(List<ReferencedFile> includedFiles);
}

View file

@ -15,7 +15,7 @@ import ee.carlrobert.codegpt.conversations.message.Message;
import ee.carlrobert.codegpt.settings.configuration.ConfigurationState;
import ee.carlrobert.codegpt.toolwindow.chat.standard.StandardChatToolWindowContentManager;
import ee.carlrobert.codegpt.util.file.FileUtil;
import ee.carlrobert.embedding.CheckedFile;
import ee.carlrobert.embedding.ReferencedFile;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
@ -67,15 +67,12 @@ public class EditorActionsUtil {
message.setUserMessage(prompt.replace("{{selectedCode}}", ""));
var toolWindowContentManager =
project.getService(StandardChatToolWindowContentManager.class);
var toolWindow = toolWindowContentManager.getToolWindow();
if (toolWindow != null) {
toolWindow.show();
}
toolWindowContentManager.getToolWindow().show();
message.setReferencedFilePaths(
Stream.ofNullable(project.getUserData(CodeGPTKeys.SELECTED_FILES))
.flatMap(Collection::stream)
.map(CheckedFile::getFilePath)
.map(ReferencedFile::getFilePath)
.collect(toList()));
toolWindowContentManager.sendMessage(message);
}

View file

@ -1,5 +1,6 @@
package ee.carlrobert.codegpt.actions.toolwindow;
import static java.lang.String.format;
import static java.util.Objects.requireNonNull;
import com.intellij.icons.AllIcons;
@ -40,11 +41,11 @@ public class OpenInEditorAction extends AnAction {
if (project != null && currentConversation != null) {
var dateTimeStamp = currentConversation.getUpdatedOn()
.format(DateTimeFormatter.ofPattern("yyyyMMddHHmm"));
var fileName = String.format("%s_%s.md", currentConversation.getModel(), dateTimeStamp);
var fileName = format("%s_%s.md", currentConversation.getModel(), dateTimeStamp);
var fileContent = currentConversation
.getMessages()
.stream()
.map(it -> String.format("### User:\n%s\n### CodeGPT:\n%s\n", it.getPrompt(),
.map(it -> format("### User:\n%s\n### CodeGPT:\n%s\n", it.getPrompt(),
it.getResponse()))
.collect(Collectors.joining());
VirtualFile file = new LightVirtualFile(fileName, fileContent);

View file

@ -0,0 +1,39 @@
package ee.carlrobert.codegpt.completions;
import ee.carlrobert.codegpt.conversations.Conversation;
import ee.carlrobert.codegpt.conversations.message.Message;
public class CallParameters {
private final Conversation conversation;
private final ConversationType conversationType;
private final Message message;
private final boolean retry;
public CallParameters(
Conversation conversation,
ConversationType conversationType,
Message message,
boolean retry) {
this.conversation = conversation;
this.conversationType = conversationType;
this.message = message;
this.retry = retry;
}
public Conversation getConversation() {
return conversation;
}
public ConversationType getConversationType() {
return conversationType;
}
public Message getMessage() {
return message;
}
public boolean isRetry() {
return retry;
}
}

View file

@ -1,5 +1,7 @@
package ee.carlrobert.codegpt.completions;
import static java.lang.String.format;
import ee.carlrobert.codegpt.CodeGPTPlugin;
import ee.carlrobert.codegpt.completions.you.YouUserManager;
import ee.carlrobert.codegpt.credentials.AzureCredentialsManager;
@ -43,7 +45,7 @@ public class CompletionClientProvider {
.setActiveDirectoryAuthentication(settings.isUseAzureActiveDirectoryAuthentication());
var baseHost = settings.getBaseHost();
if (baseHost != null) {
builder.setUrl(String.format(baseHost, params.getResourceName()));
builder.setUrl(format(baseHost, params.getResourceName()));
}
return builder.build();
}

View file

@ -1,8 +1,6 @@
package ee.carlrobert.codegpt.completions;
import com.intellij.openapi.diagnostic.Logger;
import ee.carlrobert.codegpt.conversations.Conversation;
import ee.carlrobert.codegpt.conversations.message.Message;
import ee.carlrobert.codegpt.settings.state.SettingsState;
import ee.carlrobert.codegpt.telemetry.TelemetryAction;
import ee.carlrobert.llm.client.openai.completion.ErrorDetails;
@ -12,7 +10,6 @@ import ee.carlrobert.llm.completion.CompletionEventListener;
import java.util.List;
import javax.swing.SwingWorker;
import okhttp3.sse.EventSource;
import org.jetbrains.annotations.NotNull;
public class CompletionRequestHandler {
@ -31,8 +28,8 @@ public class CompletionRequestHandler {
this.completionResponseEventListener = completionResponseEventListener;
}
public void call(Conversation conversation, Message message, boolean retry) {
swingWorker = new CompletionRequestWorker(conversation, message, retry);
public void call(CallParameters callParameters) {
swingWorker = new CompletionRequestWorker(callParameters);
swingWorker.execute();
}
@ -44,13 +41,11 @@ public class CompletionRequestHandler {
}
private EventSource startCall(
@NotNull Conversation conversation,
@NotNull Message message,
boolean retry,
CallParameters callParameters,
CompletionEventListener eventListener) {
try {
return CompletionRequestService.getInstance()
.getChatCompletionAsync(conversation, message, retry, useContextualSearch, eventListener);
.getChatCompletionAsync(callParameters, useContextualSearch, eventListener);
} catch (Throwable ex) {
handleCallException(ex);
throw ex;
@ -69,26 +64,20 @@ public class CompletionRequestHandler {
private class CompletionRequestWorker extends SwingWorker<Void, String> {
private final Conversation conversation;
private final Message message;
private final boolean retry;
private final CallParameters callParameters;
public CompletionRequestWorker(Conversation conversation, Message message, boolean retry) {
this.conversation = conversation;
this.message = message;
this.retry = retry;
public CompletionRequestWorker(CallParameters callParameters) {
this.callParameters = callParameters;
}
protected Void doInBackground() {
var settings = SettingsState.getInstance();
try {
eventSource = startCall(
conversation,
message,
retry,
new YouRequestCompletionEventListener());
eventSource = startCall(callParameters, new YouRequestCompletionEventListener());
} catch (TotalUsageExceededException e) {
completionResponseEventListener.handleTokensExceeded(conversation, message);
completionResponseEventListener.handleTokensExceeded(
callParameters.getConversation(),
callParameters.getMessage());
} finally {
sendInfo(settings);
}
@ -96,7 +85,7 @@ public class CompletionRequestHandler {
}
protected void process(List<String> chunks) {
message.setResponse(messageBuilder.toString());
callParameters.getMessage().setResponse(messageBuilder.toString());
for (String text : chunks) {
messageBuilder.append(text);
completionResponseEventListener.handleMessage(text);
@ -107,7 +96,7 @@ public class CompletionRequestHandler {
@Override
public void onSerpResults(List<YouSerpResult> results) {
completionResponseEventListener.handleSerpResults(results, message);
completionResponseEventListener.handleSerpResults(results, callParameters.getMessage());
}
@Override
@ -117,11 +106,7 @@ public class CompletionRequestHandler {
@Override
public void onComplete(StringBuilder messageBuilder) {
completionResponseEventListener.handleCompleted(
messageBuilder.toString(),
message,
conversation,
retry);
completionResponseEventListener.handleCompleted(messageBuilder.toString(), callParameters);
}
@Override
@ -136,8 +121,8 @@ public class CompletionRequestHandler {
private void sendInfo(SettingsState settings) {
TelemetryAction.COMPLETION.createActionMessage()
.property("conversationId", conversation.getId().toString())
.property("model", conversation.getModel())
.property("conversationId", callParameters.getConversation().getId().toString())
.property("model", callParameters.getConversation().getModel())
.property("service", settings.getSelectedService().getCode().toLowerCase())
.send();
}
@ -150,8 +135,8 @@ public class CompletionRequestHandler {
.property("code", "INSUFFICIENT_QUOTA");
} else {
telemetryMessage
.property("conversationId", conversation.getId().toString())
.property("model", conversation.getModel())
.property("conversationId", callParameters.getConversation().getId().toString())
.property("model", callParameters.getConversation().getModel())
.error(new RuntimeException(error.toString(), ex));
}
telemetryMessage.send();

View file

@ -1,6 +1,8 @@
package ee.carlrobert.codegpt.completions;
import static ee.carlrobert.codegpt.util.file.FileUtil.getResourceContent;
import static java.lang.String.format;
import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toList;
import com.intellij.openapi.application.ApplicationManager;
@ -14,6 +16,7 @@ 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.service.ServiceType;
import ee.carlrobert.codegpt.settings.state.IncludedFilesSettingsState;
import ee.carlrobert.codegpt.settings.state.LlamaSettingsState;
import ee.carlrobert.codegpt.settings.state.OpenAISettingsState;
import ee.carlrobert.codegpt.settings.state.SettingsState;
@ -21,6 +24,7 @@ import ee.carlrobert.codegpt.settings.state.YouSettingsState;
import ee.carlrobert.codegpt.telemetry.core.configuration.TelemetryConfiguration;
import ee.carlrobert.codegpt.telemetry.core.service.UserId;
import ee.carlrobert.embedding.EmbeddingsService;
import ee.carlrobert.embedding.ReferencedFile;
import ee.carlrobert.llm.client.llama.completion.LlamaCompletionRequest;
import ee.carlrobert.llm.client.openai.completion.OpenAIChatCompletionModel;
import ee.carlrobert.llm.client.openai.completion.OpenAICompletionRequest;
@ -39,32 +43,14 @@ public class CompletionRequestProvider {
private static final Logger LOG = Logger.getInstance(CompletionRequestProvider.class);
public static final String COMPLETION_SYSTEM_PROMPT = "You are an AI programming assistant.\n"
+ "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, you 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"
+ "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"
+ "You can only give one reply for each conversation turn.";
public static final String COMPLETION_SYSTEM_PROMPT = getResourceContent(
"/prompts/default-completion-system-prompt.txt");
public static final String COMPLETION_COMMIT_MESSAGE_PROMPT =
"Write a short and descriptive git commit message for the following git diff.\n"
+ "Use imperative mood, present tense, active voice and verbs.\n"
+ "Your entire response will be passed directly into git commit.";
public static final String GENERATE_COMMIT_MESSAGE_SYSTEM_PROMPT = getResourceContent(
"/prompts/generate-commit-message-system-prompt.txt");
public static final String FIX_COMPILE_ERRORS_SYSTEM_PROMPT = getResourceContent(
"/prompts/fix-compile-errors.txt");
private final EncodingManager encodingManager = EncodingManager.getInstance();
private final EmbeddingsService embeddingsService;
@ -77,6 +63,23 @@ public class CompletionRequestProvider {
this.conversation = conversation;
}
public static String getPromptWithContext(List<ReferencedFile> referencedFiles,
String userPrompt) {
var includedFilesSettings = IncludedFilesSettingsState.getInstance();
var repeatableContext = referencedFiles.stream()
.map(item -> includedFilesSettings.getRepeatableContext()
.replace("{FILE_PATH}", item.getFilePath())
.replace("{FILE_CONTENT}", format(
"```%s\n%s\n```",
item.getFileExtension(),
item.getFileContent().trim())))
.collect(joining("\n\n"));
return includedFilesSettings.getPromptTemplate()
.replace("{REPEATABLE_CONTEXT}", repeatableContext)
.replace("{QUESTION}", userPrompt);
}
public static OpenAICompletionRequest buildOpenAILookupCompletionRequest(
String context) {
return new OpenAIChatCompletionRequest.Builder(
@ -96,13 +99,21 @@ public class CompletionRequestProvider {
.build();
}
public LlamaCompletionRequest buildLlamaCompletionRequest(Message message) {
public LlamaCompletionRequest buildLlamaCompletionRequest(
Message message,
ConversationType conversationType) {
var settings = LlamaSettingsState.getInstance();
var promptTemplate = settings.isUseCustomModel()
? settings.getPromptTemplate()
: LlamaModel.findByHuggingFaceModel(settings.getHuggingFaceModel()).getPromptTemplate();
var systemPrompt = COMPLETION_SYSTEM_PROMPT;
if (conversationType == ConversationType.FIX_COMPILE_ERRORS) {
systemPrompt = FIX_COMPILE_ERRORS_SYSTEM_PROMPT;
}
var prompt = promptTemplate.buildPrompt(
COMPLETION_SYSTEM_PROMPT,
systemPrompt,
message.getPrompt(),
conversation.getMessages());
var configuration = ConfigurationState.getInstance();
@ -131,21 +142,13 @@ public class CompletionRequestProvider {
return requestBuilder.build();
}
public OpenAIChatCompletionRequest buildOpenAIChatCompletionRequest(
String model,
Message message,
boolean retry) {
return buildOpenAIChatCompletionRequest(model, message, retry, false, null);
}
public OpenAIChatCompletionRequest buildOpenAIChatCompletionRequest(
@Nullable String model,
Message message,
boolean retry,
CallParameters callParameters,
boolean useContextualSearch,
@Nullable String overriddenPath) {
var builder = new OpenAIChatCompletionRequest.Builder(
buildMessages(model, message, retry, useContextualSearch))
buildMessages(model, callParameters, useContextualSearch))
.setModel(model)
.setMaxTokens(ConfigurationState.getInstance().getMaxTokens())
.setTemperature(ConfigurationState.getInstance().getTemperature());
@ -158,21 +161,27 @@ public class CompletionRequestProvider {
}
public List<OpenAIChatCompletionMessage> buildMessages(
Message message,
boolean retry,
CallParameters callParameters,
boolean useContextualSearch) {
var message = callParameters.getMessage();
var messages = new ArrayList<OpenAIChatCompletionMessage>();
if (useContextualSearch) {
var prompt = embeddingsService.buildPromptWithContext(message.getPrompt());
var prompt = embeddingsService.buildPromptWithContext(
message.getPrompt());
LOG.info("Retrieved context:\n" + prompt);
messages.add(new OpenAIChatCompletionMessage("user", prompt));
} else {
var systemPrompt = ConfigurationState.getInstance().getSystemPrompt();
messages.add(new OpenAIChatCompletionMessage("system",
systemPrompt.isEmpty() ? COMPLETION_SYSTEM_PROMPT : systemPrompt));
if (callParameters.getConversationType() == ConversationType.DEFAULT) {
messages.add(new OpenAIChatCompletionMessage(
"system",
ConfigurationState.getInstance().getSystemPrompt()));
}
if (callParameters.getConversationType() == ConversationType.FIX_COMPILE_ERRORS) {
messages.add(new OpenAIChatCompletionMessage("system", FIX_COMPILE_ERRORS_SYSTEM_PROMPT));
}
for (var prevMessage : conversation.getMessages()) {
if (retry && prevMessage.getId().equals(message.getId())) {
if (callParameters.isRetry() && prevMessage.getId().equals(message.getId())) {
break;
}
messages.add(new OpenAIChatCompletionMessage("user", prevMessage.getPrompt()));
@ -185,10 +194,9 @@ public class CompletionRequestProvider {
private List<OpenAIChatCompletionMessage> buildMessages(
@Nullable String model,
Message message,
boolean retry,
CallParameters callParameters,
boolean useContextualSearch) {
var messages = buildMessages(message, retry, useContextualSearch);
var messages = buildMessages(callParameters, useContextualSearch);
if (model == null || SettingsState.getInstance().getSelectedService() == ServiceType.YOU) {
return messages;

View file

@ -1,18 +1,24 @@
package ee.carlrobert.codegpt.completions;
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.application.ApplicationManager;
import com.intellij.openapi.components.Service;
import ee.carlrobert.codegpt.conversations.Conversation;
import ee.carlrobert.codegpt.conversations.message.Message;
import ee.carlrobert.codegpt.credentials.AzureCredentialsManager;
import ee.carlrobert.codegpt.credentials.OpenAICredentialsManager;
import ee.carlrobert.codegpt.settings.configuration.ConfigurationState;
import ee.carlrobert.codegpt.settings.service.ServiceType;
import ee.carlrobert.codegpt.settings.state.AzureSettingsState;
import ee.carlrobert.codegpt.settings.state.OpenAISettingsState;
import ee.carlrobert.codegpt.settings.state.SettingsState;
import ee.carlrobert.llm.client.openai.completion.request.OpenAIChatCompletionMessage;
import ee.carlrobert.llm.client.openai.completion.request.OpenAIChatCompletionRequest;
import ee.carlrobert.llm.completion.CompletionEventListener;
import java.util.List;
import java.util.Optional;
import okhttp3.sse.EventSource;
import org.jetbrains.annotations.NotNull;
@Service
public final class CompletionRequestService {
@ -25,46 +31,79 @@ public final class CompletionRequestService {
}
public EventSource getChatCompletionAsync(
@NotNull Conversation conversation,
@NotNull Message message,
boolean retry,
CallParameters callParameters,
boolean useContextualSearch,
CompletionEventListener eventListener) {
var requestProvider = new CompletionRequestProvider(conversation);
var requestProvider = new CompletionRequestProvider(callParameters.getConversation());
switch (SettingsState.getInstance().getSelectedService()) {
case OPENAI:
var openAISettings = OpenAISettingsState.getInstance();
return CompletionClientProvider.getOpenAIClient().getChatCompletion(
return CompletionClientProvider.getOpenAIClient().getChatCompletionAsync(
requestProvider.buildOpenAIChatCompletionRequest(
openAISettings.getModel(),
message,
retry,
callParameters,
useContextualSearch,
openAISettings.isUsingCustomPath() ? openAISettings.getPath() : null),
eventListener);
case AZURE:
var azureSettings = AzureSettingsState.getInstance();
return CompletionClientProvider.getAzureClient().getChatCompletion(
return CompletionClientProvider.getAzureClient().getChatCompletionAsync(
requestProvider.buildOpenAIChatCompletionRequest(
null,
message,
retry,
callParameters,
useContextualSearch,
azureSettings.isUsingCustomPath() ? azureSettings.getPath() : null),
eventListener);
case YOU:
return CompletionClientProvider.getYouClient().getChatCompletion(
requestProvider.buildYouCompletionRequest(message),
return CompletionClientProvider.getYouClient().getChatCompletionAsync(
requestProvider.buildYouCompletionRequest(callParameters.getMessage()),
eventListener);
case LLAMA_CPP:
return CompletionClientProvider.getLlamaClient().getChatCompletion(
requestProvider.buildLlamaCompletionRequest(message),
return CompletionClientProvider.getLlamaClient().getChatCompletionAsync(
requestProvider.buildLlamaCompletionRequest(
callParameters.getMessage(),
callParameters.getConversationType()),
eventListener);
default:
throw new IllegalArgumentException();
}
}
public void generateCommitMessageAsync(
String prompt,
CompletionEventListener eventListener) {
var request = new OpenAIChatCompletionRequest.Builder(List.of(
new OpenAIChatCompletionMessage("system",
ConfigurationState.getInstance().getCommitMessagePrompt()),
new OpenAIChatCompletionMessage("user", prompt)))
.setModel(OpenAISettingsState.getInstance().getModel())
.build();
var selectedService = SettingsState.getInstance().getSelectedService();
if (selectedService == ServiceType.OPENAI) {
CompletionClientProvider.getOpenAIClient().getChatCompletionAsync(request, eventListener);
}
if (selectedService == ServiceType.AZURE) {
CompletionClientProvider.getAzureClient().getChatCompletionAsync(request, eventListener);
}
}
public Optional<String> getLookupCompletion(String prompt) {
var selectedService = SettingsState.getInstance().getSelectedService();
if (selectedService == YOU || selectedService == LLAMA_CPP) {
return Optional.empty();
}
var request = CompletionRequestProvider.buildOpenAILookupCompletionRequest(prompt);
var response = selectedService == OPENAI
? CompletionClientProvider.getOpenAIClient().getChatCompletion(request)
: CompletionClientProvider.getAzureClient().getChatCompletion(request);
return response
.getChoices()
.stream()
.findFirst()
.map(item -> item.getMessage().getContent());
}
public boolean isRequestAllowed() {
var selectedService = SettingsState.getInstance().getSelectedService();
if (selectedService == ServiceType.AZURE) {

View file

@ -8,17 +8,18 @@ import java.util.List;
public interface CompletionResponseEventListener {
default void handleMessage(String message) {}
default void handleMessage(String message) {
}
default void handleError(ErrorDetails error, Throwable ex) {}
default void handleError(ErrorDetails error, Throwable ex) {
}
default void handleTokensExceeded(Conversation conversation, Message message) {}
default void handleTokensExceeded(Conversation conversation, Message message) {
}
default void handleCompleted(
String fullMessage,
Message message,
Conversation conversation,
boolean retry) {}
default void handleCompleted(String fullMessage, CallParameters callParameters) {
}
default void handleSerpResults(List<YouSerpResult> results, Message message) {}
default void handleSerpResults(List<YouSerpResult> results, Message message) {
}
}

View file

@ -0,0 +1,9 @@
package ee.carlrobert.codegpt.completions;
public enum ConversationType {
CUSTOM_PROMPT,
DEFAULT,
EDITOR_ACTION,
FIX_COMPILE_ERRORS,
MULTI_FILE,
}

View file

@ -1,8 +1,5 @@
package ee.carlrobert.codegpt.completions;
import static ee.carlrobert.codegpt.settings.service.ServiceType.AZURE;
import static ee.carlrobert.codegpt.settings.service.ServiceType.OPENAI;
import com.intellij.codeInsight.completion.PrefixMatcher;
import com.intellij.codeInsight.lookup.Lookup;
import com.intellij.codeInsight.lookup.LookupElementBuilder;
@ -15,7 +12,6 @@ import com.intellij.psi.util.PsiUtilCore;
import ee.carlrobert.codegpt.Icons;
import ee.carlrobert.codegpt.credentials.OpenAICredentialsManager;
import ee.carlrobert.codegpt.settings.configuration.ConfigurationState;
import ee.carlrobert.codegpt.settings.state.SettingsState;
import java.util.Optional;
import org.jetbrains.annotations.Nullable;
@ -51,35 +47,16 @@ public class MethodNameLookupListener implements LookupManagerListener {
LookupImpl lookup,
Application application,
String prompt) {
getCompletionResponse(prompt).ifPresent(response -> {
for (var value : response.split(",")) {
application.runReadAction(() -> {
lookup.addItem(
LookupElementBuilder.create(value.trim()).withIcon(Icons.Sparkle),
PrefixMatcher.ALWAYS_TRUE);
application.invokeLater(() -> lookup.refreshUi(true, true));
CompletionRequestService.getInstance().getLookupCompletion(prompt)
.ifPresent(response -> {
for (var value : response.split(",")) {
application.runReadAction(() -> {
lookup.addItem(
LookupElementBuilder.create(value.trim()).withIcon(Icons.Sparkle),
PrefixMatcher.ALWAYS_TRUE);
application.invokeLater(() -> lookup.refreshUi(true, true));
});
}
});
}
});
}
// TODO: Refactor
private Optional<String> getCompletionResponse(String prompt) {
var selectedService = SettingsState.getInstance().getSelectedService();
if (selectedService == OPENAI) {
return Optional.ofNullable(CompletionClientProvider.getOpenAIClient()
.getChatCompletion(
CompletionRequestProvider.buildOpenAILookupCompletionRequest(prompt))
.getChoices())
.map(choices -> choices.get(0).getMessage().getContent());
}
if (selectedService == AZURE) {
return Optional.ofNullable(CompletionClientProvider.getAzureClient()
.getChatCompletion(
CompletionRequestProvider.buildOpenAILookupCompletionRequest(prompt))
.getChoices())
.map(choices -> choices.get(0).getMessage().getContent());
}
return Optional.empty();
}
}

View file

@ -83,8 +83,4 @@ public class Conversation {
.filter(message -> !message.getId().equals(messageId))
.collect(toList()));
}
public void removeMessages() {
messages.clear();
}
}

View file

@ -4,6 +4,7 @@ import static java.util.stream.Collectors.toList;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.Service;
import ee.carlrobert.codegpt.completions.CallParameters;
import ee.carlrobert.codegpt.conversations.message.Message;
import ee.carlrobert.codegpt.settings.service.ServiceType;
import ee.carlrobert.codegpt.settings.state.AzureSettingsState;
@ -14,6 +15,7 @@ import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.ListIterator;
import java.util.Optional;
import java.util.UUID;
import org.jetbrains.annotations.NotNull;
@ -60,13 +62,11 @@ public final class ConversationService {
conversationsMapping.put(conversation.getClientCode(), conversations);
}
public void saveMessage(
String response,
Message message,
Conversation conversation,
boolean retry) {
public void saveMessage(String response, CallParameters callParameters) {
var conversation = callParameters.getConversation();
var message = callParameters.getMessage();
var conversationMessages = conversation.getMessages();
if (retry && !conversationMessages.isEmpty()) {
if (callParameters.isRetry() && !conversationMessages.isEmpty()) {
var messageToBeSaved = conversationMessages.stream()
.filter(item -> item.getId().equals(message.getId()))
.findFirst().orElseThrow();
@ -82,9 +82,7 @@ public final class ConversationService {
public void saveMessage(@NotNull Conversation conversation, @NotNull Message message) {
conversation.setUpdatedOn(LocalDateTime.now());
var iterator = conversationState.getConversationsMapping()
.get(conversation.getClientCode())
.listIterator();
var iterator = getIterator(conversation.getClientCode());
while (iterator.hasNext()) {
var next = iterator.next();
next.setMessages(
@ -102,9 +100,7 @@ public final class ConversationService {
public void saveConversation(Conversation conversation) {
conversation.setUpdatedOn(LocalDateTime.now());
var iterator = conversationState.getConversationsMapping()
.get(conversation.getClientCode())
.listIterator();
var iterator = getIterator(conversation.getClientCode());
while (iterator.hasNext()) {
var next = iterator.next();
if (next.getId().equals(conversation.getId())) {
@ -128,9 +124,7 @@ public final class ConversationService {
}
public void deleteConversation(Conversation conversation) {
var iterator = conversationState.getConversationsMapping()
.get(conversation.getClientCode())
.listIterator();
var iterator = getIterator(conversation.getClientCode());
while (iterator.hasNext()) {
var next = iterator.next();
if (next.getId().equals(conversation.getId())) {
@ -168,6 +162,12 @@ public final class ConversationService {
return tryGetNextOrPreviousConversation(false);
}
private ListIterator<Conversation> getIterator(String clientCode) {
return conversationState.getConversationsMapping()
.get(clientCode)
.listIterator();
}
private Optional<Conversation> tryGetNextOrPreviousConversation(boolean isPrevious) {
var currentConversation = ConversationsState.getCurrentConversation();
if (currentConversation != null) {

View file

@ -21,7 +21,7 @@ public class CodebaseIndexingAction extends AnAction {
var folderStructureTreePanel = new FolderStructureTreePanel(project);
var show = OverlayUtil.showFileStructureDialog(project, folderStructureTreePanel);
if (show == OK_EXIT_CODE) {
new CodebaseIndexingTask(project, folderStructureTreePanel.getCheckedFiles()).run();
new CodebaseIndexingTask(project, folderStructureTreePanel.getReferencedFiles()).run();
}
}
}

View file

@ -15,8 +15,8 @@ import ee.carlrobert.codegpt.CodeGPTPlugin;
import ee.carlrobert.codegpt.completions.CompletionClientProvider;
import ee.carlrobert.codegpt.ui.OverlayUtil;
import ee.carlrobert.codegpt.util.file.FileUtil;
import ee.carlrobert.embedding.CheckedFile;
import ee.carlrobert.embedding.EmbeddingsService;
import ee.carlrobert.embedding.ReferencedFile;
import ee.carlrobert.vector.VectorStore;
import java.util.List;
import java.util.Map;
@ -26,13 +26,13 @@ public class CodebaseIndexingTask extends Task.Backgroundable {
private static final Logger LOG = Logger.getInstance(CodebaseIndexingTask.class);
private final Project project;
private final List<CheckedFile> checkedFiles;
private final List<ReferencedFile> referencedFiles;
private final EmbeddingsService embeddingsService;
public CodebaseIndexingTask(Project project, List<CheckedFile> checkedFiles) {
public CodebaseIndexingTask(Project project, List<ReferencedFile> referencedFiles) {
super(project, CodeGPTBundle.get("codebaseIndexing.task.title"), true);
this.project = project;
this.checkedFiles = checkedFiles;
this.referencedFiles = referencedFiles;
this.embeddingsService = new EmbeddingsService(
CompletionClientProvider.getOpenAIClient(),
CodeGPTPlugin.getPluginBasePath());
@ -49,7 +49,7 @@ public class CodebaseIndexingTask extends Task.Backgroundable {
String fileContent;
try {
fileContent = new ObjectMapper().writeValueAsString(Map.of("content", checkedFiles));
fileContent = new ObjectMapper().writeValueAsString(Map.of("content", referencedFiles));
} catch (JsonProcessingException e) {
throw new RuntimeException("Unable to serialize json file");
}
@ -63,7 +63,7 @@ public class CodebaseIndexingTask extends Task.Backgroundable {
try {
indicator.setFraction(0);
List<Item<Object, double[]>> embeddings =
embeddingsService.createEmbeddings(checkedFiles, indicator);
embeddingsService.createEmbeddings(referencedFiles, indicator);
VectorStore.getInstance(CodeGPTPlugin.getPluginBasePath()).save(embeddings);
OverlayUtil.showNotification("Indexing completed", NotificationType.INFORMATION);

View file

@ -20,7 +20,7 @@ import com.intellij.ui.components.JBLabel;
import com.intellij.util.ui.AsyncProcessIcon;
import com.intellij.util.ui.JBUI;
import ee.carlrobert.codegpt.util.file.FileUtil;
import ee.carlrobert.embedding.CheckedFile;
import ee.carlrobert.embedding.ReferencedFile;
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.event.MouseAdapter;
@ -132,9 +132,9 @@ public class FolderStructureTreePanel {
return panel;
}
public List<CheckedFile> getCheckedFiles() {
public List<ReferencedFile> getReferencedFiles() {
return getCheckedVirtualFiles().stream()
.map(item -> new CheckedFile(new File(item.getPath())))
.map(item -> new ReferencedFile(new File(item.getPath())))
.collect(toList());
}

View file

@ -1,7 +1,7 @@
package ee.carlrobert.codegpt.settings.configuration;
import static ee.carlrobert.codegpt.completions.CompletionRequestProvider.COMPLETION_COMMIT_MESSAGE_PROMPT;
import static ee.carlrobert.codegpt.completions.CompletionRequestProvider.COMPLETION_SYSTEM_PROMPT;
import static ee.carlrobert.codegpt.completions.CompletionRequestProvider.GENERATE_COMMIT_MESSAGE_SYSTEM_PROMPT;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.PersistentStateComponent;
@ -19,13 +19,14 @@ import org.jetbrains.annotations.Nullable;
public class ConfigurationState implements PersistentStateComponent<ConfigurationState> {
private String systemPrompt = COMPLETION_SYSTEM_PROMPT;
private String commitMessagePrompt = COMPLETION_COMMIT_MESSAGE_PROMPT;
private String commitMessagePrompt = GENERATE_COMMIT_MESSAGE_SYSTEM_PROMPT;
private int maxTokens = 1000;
private double temperature = 0.1;
private boolean checkForPluginUpdates = true;
private boolean createNewChatOnEachAction;
private boolean ignoreGitCommitTokenLimit;
private boolean methodNameGenerationEnabled = true;
private boolean captureCompileErrors = true;
private boolean autoFormattingEnabled = true;
private Map<String, String> tableData = EditorActionsUtil.DEFAULT_ACTIONS;
@ -116,6 +117,14 @@ public class ConfigurationState implements PersistentStateComponent<Configuratio
this.methodNameGenerationEnabled = methodNameGenerationEnabled;
}
public boolean isCaptureCompileErrors() {
return captureCompileErrors;
}
public void setCaptureCompileErrors(boolean captureCompileErrors) {
this.captureCompileErrors = captureCompileErrors;
}
public boolean isAutoFormattingEnabled() {
return autoFormattingEnabled;
}

View file

@ -1,5 +1,6 @@
package ee.carlrobert.codegpt.settings.service;
import static java.lang.String.format;
import static java.util.stream.Collectors.toList;
import com.intellij.icons.AllIcons.Actions;
@ -315,7 +316,7 @@ public class LlamaModelPreferencesForm {
return "";
}
return String.format("<html>"
return format("<html>"
+ "<p style=\"margin: 0\"><small>File Size: <strong>%.2f GB</strong></small></p>"
+ "<p style=\"margin: 0\"><small>Max RAM Required: <strong>%.2f GB</strong></small></p>"
+ "</html>", details.fileSize, details.maxRAMRequired);

View file

@ -1,5 +1,7 @@
package ee.carlrobert.codegpt.settings.state;
import static java.lang.String.format;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.PersistentStateComponent;
import com.intellij.openapi.components.State;
@ -82,7 +84,7 @@ public class SettingsState implements PersistentStateComponent<SettingsState> {
}
var huggingFaceModel = llamaSettings.getHuggingFaceModel();
var llamaModel = LlamaModel.findByHuggingFaceModel(huggingFaceModel);
return String.format(
return format(
"%s %dB (Q%d)",
llamaModel.getLabel(),
huggingFaceModel.getParameterSize(),

View file

@ -1,276 +0,0 @@
package ee.carlrobert.codegpt.toolwindow.chat;
import static ee.carlrobert.codegpt.ui.UIUtil.createScrollPaneWithSmartScroller;
import static java.lang.String.format;
import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toList;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.impl.EditorImpl;
import com.intellij.openapi.project.Project;
import com.intellij.ui.JBColor;
import com.intellij.util.ui.JBUI;
import ee.carlrobert.codegpt.CodeGPTKeys;
import ee.carlrobert.codegpt.EncodingManager;
import ee.carlrobert.codegpt.actions.ActionType;
import ee.carlrobert.codegpt.completions.CompletionRequestHandler;
import ee.carlrobert.codegpt.completions.CompletionRequestService;
import ee.carlrobert.codegpt.conversations.Conversation;
import ee.carlrobert.codegpt.conversations.ConversationService;
import ee.carlrobert.codegpt.conversations.message.Message;
import ee.carlrobert.codegpt.settings.service.ServiceType;
import ee.carlrobert.codegpt.settings.state.IncludedFilesSettingsState;
import ee.carlrobert.codegpt.settings.state.SettingsState;
import ee.carlrobert.codegpt.telemetry.TelemetryAction;
import ee.carlrobert.codegpt.toolwindow.chat.standard.StandardChatToolWindowContentManager;
import ee.carlrobert.codegpt.toolwindow.chat.standard.StandardChatToolWindowPanel;
import ee.carlrobert.codegpt.toolwindow.chat.ui.ChatMessageResponseBody;
import ee.carlrobert.codegpt.toolwindow.chat.ui.ChatToolWindowScrollablePanel;
import ee.carlrobert.codegpt.toolwindow.chat.ui.ResponsePanel;
import ee.carlrobert.codegpt.toolwindow.chat.ui.UserMessagePanel;
import ee.carlrobert.codegpt.toolwindow.chat.ui.textarea.TotalTokensDetails;
import ee.carlrobert.codegpt.toolwindow.chat.ui.textarea.TotalTokensPanel;
import ee.carlrobert.codegpt.toolwindow.chat.ui.textarea.UserPromptTextArea;
import ee.carlrobert.codegpt.toolwindow.chat.ui.textarea.UserPromptTextAreaHeader;
import ee.carlrobert.codegpt.util.EditorUtil;
import ee.carlrobert.codegpt.util.file.FileUtil;
import ee.carlrobert.embedding.CheckedFile;
import java.awt.BorderLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.util.List;
import java.util.UUID;
import javax.swing.JComponent;
import javax.swing.JPanel;
import org.jetbrains.annotations.NotNull;
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 Conversation conversation;
private final UserPromptTextArea userPromptTextArea;
private final ConversationService conversationService;
protected final Project project;
protected final TotalTokensPanel totalTokensPanel;
protected final ChatToolWindowScrollablePanel toolWindowScrollablePanel;
protected abstract JComponent getLandingView();
public BaseChatToolWindowTabPanel(
@NotNull Project project,
@NotNull Conversation conversation,
boolean useContextualSearch) {
this.project = project;
this.conversation = conversation;
this.useContextualSearch = useContextualSearch;
conversationService = ConversationService.getInstance();
toolWindowScrollablePanel = new ChatToolWindowScrollablePanel();
totalTokensPanel = new TotalTokensPanel(
project,
conversation,
EditorUtil.getSelectedEditorSelectedText(project),
this);
userPromptTextArea = new UserPromptTextArea(this::handleSubmit, totalTokensPanel);
rootPanel = createRootPanel();
userPromptTextArea.requestFocusInWindow();
userPromptTextArea.requestFocus();
}
@Override
public void dispose() {
}
@Override
public JComponent getContent() {
return rootPanel;
}
@Override
public Conversation getConversation() {
return conversation;
}
@Override
public void sendMessage(Message message) {
var referencedFiles = project.getUserData(CodeGPTKeys.SELECTED_FILES);
if (referencedFiles != null && !referencedFiles.isEmpty()) {
var referencedFilePaths = referencedFiles.stream()
.map(CheckedFile::getFilePath)
.collect(toList());
message.setReferencedFilePaths(referencedFilePaths);
message.setUserMessage(message.getPrompt());
message.setPrompt(getPromptWithContext(referencedFiles, message.getPrompt()));
totalTokensPanel.updateReferencedFilesTokens(referencedFiles);
}
var userMessagePanel = new UserMessagePanel(project, message, this);
var messagePanel = toolWindowScrollablePanel.addMessage(message.getId());
messagePanel.add(userMessagePanel);
var responsePanel = new ResponsePanel()
.withReloadAction(() -> reloadMessage(message, conversation))
.withDeleteAction(() -> removeMessage(message.getId(), conversation))
.addContent(new ChatMessageResponseBody(project, true, this));
messagePanel.add(responsePanel);
var userPromptTokens = EncodingManager.getInstance().countTokens(message.getPrompt());
var conversationTokens = EncodingManager.getInstance().countConversationTokens(conversation);
totalTokensPanel.updateConversationTokens(conversationTokens + userPromptTokens);
call(message, responsePanel, false);
project.getService(StandardChatToolWindowContentManager.class)
.tryFindChatToolWindowPanel()
.ifPresent(StandardChatToolWindowPanel::clearSelectedFilesNotification);
}
private String getPromptWithContext(List<CheckedFile> referencedFiles, String userPrompt) {
var includedFilesSettings = IncludedFilesSettingsState.getInstance();
var repeatableContext = referencedFiles.stream()
.map(item -> includedFilesSettings.getRepeatableContext()
.replace("{FILE_PATH}", item.getFilePath())
.replace("{FILE_CONTENT}", format(
"```%s\n%s\n```",
item.getFileExtension(),
item.getFileContent().trim())))
.collect(joining("\n\n"));
return includedFilesSettings.getPromptTemplate()
.replace("{REPEATABLE_CONTEXT}", repeatableContext)
.replace("{QUESTION}", userPrompt);
}
@Override
public TotalTokensDetails getTokenDetails() {
return totalTokensPanel.getTokenDetails();
}
@Override
public void requestFocusForTextArea() {
userPromptTextArea.focus();
}
@Override
public void displayLandingView() {
toolWindowScrollablePanel.displayLandingView(getLandingView());
totalTokensPanel.updateConversationTokens(conversation);
}
protected void reloadMessage(Message message, Conversation conversation) {
ResponsePanel responsePanel = null;
try {
responsePanel = toolWindowScrollablePanel.getMessageResponsePanel(message.getId());
((ChatMessageResponseBody) responsePanel.getContent()).clear();
toolWindowScrollablePanel.update();
} catch (Exception e) {
throw new RuntimeException("Could not delete the existing message component", e);
} finally {
LOG.debug("Reloading message: " + message.getId());
if (responsePanel != null) {
message.setResponse("");
conversationService.saveMessage(conversation, message);
call(message, responsePanel, true);
}
totalTokensPanel.updateConversationTokens(conversation);
TelemetryAction.IDE_ACTION.createActionMessage()
.property("action", ActionType.RELOAD_MESSAGE.name())
.send();
}
}
protected void removeMessage(UUID messageId, Conversation conversation) {
toolWindowScrollablePanel.removeMessage(messageId);
conversation.removeMessage(messageId);
conversationService.saveConversation(conversation);
totalTokensPanel.updateConversationTokens(conversation);
if (conversation.getMessages().isEmpty()) {
displayLandingView();
}
}
protected void clearWindow() {
toolWindowScrollablePanel.clearAll();
totalTokensPanel.updateConversationTokens(conversation);
}
private void call(Message message, ResponsePanel responsePanel, boolean retry) {
var responseContainer = (ChatMessageResponseBody) responsePanel.getContent();
if (!CompletionRequestService.getInstance().isRequestAllowed()) {
responseContainer.displayMissingCredential();
return;
}
var requestHandler = new CompletionRequestHandler(
useContextualSearch,
new ToolWindowCompletionResponseEventListener(
conversationService,
responsePanel,
totalTokensPanel,
userPromptTextArea) {
@Override
public void handleTokensExceededPolicyAccepted() {
call(message, responsePanel, true);
}
});
userPromptTextArea.setRequestHandler(requestHandler);
userPromptTextArea.setSubmitEnabled(false);
requestHandler.call(conversation, message, retry);
}
private void handleSubmit(String text) {
var message = new Message(text);
var editor = EditorUtil.getSelectedEditor(project);
if (editor != null) {
var selectionModel = editor.getSelectionModel();
var selectedText = selectionModel.getSelectedText();
if (selectedText != null && !selectedText.isEmpty()) {
var fileExtension = FileUtil.getFileExtension(
((EditorImpl) editor).getVirtualFile().getName());
message = new Message(text + format("\n```%s\n%s\n```", fileExtension, selectedText));
selectionModel.removeSelection();
}
}
message.setUserMessage(text);
sendMessage(message);
}
private JPanel createUserPromptPanel(ServiceType selectedService) {
var panel = new JPanel(new BorderLayout());
panel.setBorder(JBUI.Borders.compound(
JBUI.Borders.customLine(JBColor.border(), 1, 0, 0, 0),
JBUI.Borders.empty(8)));
var contentManager = project.getService(StandardChatToolWindowContentManager.class);
panel.add(JBUI.Panels.simplePanel(new UserPromptTextAreaHeader(
selectedService,
totalTokensPanel,
contentManager::createNewTabPanel)), BorderLayout.NORTH);
panel.add(JBUI.Panels.simplePanel(userPromptTextArea), BorderLayout.CENTER);
return panel;
}
private JPanel createRootPanel() {
var gbc = new GridBagConstraints();
gbc.fill = GridBagConstraints.BOTH;
gbc.weighty = 1;
gbc.weightx = 1;
gbc.gridx = 0;
gbc.gridy = 0;
var rootPanel = new JPanel(new GridBagLayout());
rootPanel.add(createScrollPaneWithSmartScroller(toolWindowScrollablePanel), gbc);
gbc.weighty = 0;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.gridy = 1;
rootPanel.add(
createUserPromptPanel(SettingsState.getInstance().getSelectedService()), gbc);
return rootPanel;
}
}

View file

@ -1,23 +1,286 @@
package ee.carlrobert.codegpt.toolwindow.chat;
import static ee.carlrobert.codegpt.completions.CompletionRequestProvider.getPromptWithContext;
import static ee.carlrobert.codegpt.ui.UIUtil.createScrollPaneWithSmartScroller;
import static java.lang.String.format;
import static java.util.stream.Collectors.toList;
import com.intellij.openapi.Disposable;
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.ui.JBColor;
import com.intellij.util.ui.JBUI;
import ee.carlrobert.codegpt.CodeGPTKeys;
import ee.carlrobert.codegpt.EncodingManager;
import ee.carlrobert.codegpt.actions.ActionType;
import ee.carlrobert.codegpt.completions.CallParameters;
import ee.carlrobert.codegpt.completions.CompletionRequestHandler;
import ee.carlrobert.codegpt.completions.CompletionRequestService;
import ee.carlrobert.codegpt.completions.ConversationType;
import ee.carlrobert.codegpt.conversations.Conversation;
import ee.carlrobert.codegpt.conversations.ConversationService;
import ee.carlrobert.codegpt.conversations.message.Message;
import ee.carlrobert.codegpt.settings.service.ServiceType;
import ee.carlrobert.codegpt.settings.state.SettingsState;
import ee.carlrobert.codegpt.telemetry.TelemetryAction;
import ee.carlrobert.codegpt.toolwindow.chat.standard.StandardChatToolWindowContentManager;
import ee.carlrobert.codegpt.toolwindow.chat.standard.StandardChatToolWindowPanel;
import ee.carlrobert.codegpt.toolwindow.chat.ui.ChatMessageResponseBody;
import ee.carlrobert.codegpt.toolwindow.chat.ui.ChatToolWindowScrollablePanel;
import ee.carlrobert.codegpt.toolwindow.chat.ui.ResponsePanel;
import ee.carlrobert.codegpt.toolwindow.chat.ui.UserMessagePanel;
import ee.carlrobert.codegpt.toolwindow.chat.ui.textarea.TotalTokensDetails;
import ee.carlrobert.codegpt.toolwindow.chat.ui.textarea.TotalTokensPanel;
import ee.carlrobert.codegpt.toolwindow.chat.ui.textarea.UserPromptTextArea;
import ee.carlrobert.codegpt.toolwindow.chat.ui.textarea.UserPromptTextAreaHeader;
import ee.carlrobert.codegpt.util.EditorUtil;
import ee.carlrobert.codegpt.util.file.FileUtil;
import ee.carlrobert.embedding.ReferencedFile;
import java.awt.BorderLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.util.UUID;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import org.jetbrains.annotations.NotNull;
public interface ChatToolWindowTabPanel extends Disposable {
public abstract class ChatToolWindowTabPanel implements Disposable {
JComponent getContent();
private static final Logger LOG = Logger.getInstance(ChatToolWindowTabPanel.class);
Conversation getConversation();
private final boolean useContextualSearch;
private final JPanel rootPanel;
private final Conversation conversation;
private final UserPromptTextArea userPromptTextArea;
private final ConversationService conversationService;
TotalTokensDetails getTokenDetails();
protected final Project project;
protected final TotalTokensPanel totalTokensPanel;
protected final ChatToolWindowScrollablePanel toolWindowScrollablePanel;
void displayLandingView();
protected abstract JComponent getLandingView();
void sendMessage(Message message);
public ChatToolWindowTabPanel(
@NotNull Project project,
@NotNull Conversation conversation,
boolean useContextualSearch) {
this.project = project;
this.conversation = conversation;
this.useContextualSearch = useContextualSearch;
conversationService = ConversationService.getInstance();
toolWindowScrollablePanel = new ChatToolWindowScrollablePanel();
totalTokensPanel = new TotalTokensPanel(
project,
conversation,
EditorUtil.getSelectedEditorSelectedText(project),
this);
userPromptTextArea = new UserPromptTextArea(this::handleSubmit, totalTokensPanel);
rootPanel = createRootPanel();
userPromptTextArea.requestFocusInWindow();
userPromptTextArea.requestFocus();
}
void requestFocusForTextArea();
}
public void dispose() {
LOG.info("Disposing BaseChatToolWindowTabPanel component");
}
public JComponent getContent() {
return rootPanel;
}
public Conversation getConversation() {
return conversation;
}
public void sendMessage(Message message) {
sendMessage(message, ConversationType.DEFAULT);
}
public void sendMessage(Message message, ConversationType conversationType) {
Runnable runnable = () -> {
var referencedFiles = project.getUserData(CodeGPTKeys.SELECTED_FILES);
if (referencedFiles != null && !referencedFiles.isEmpty()) {
var referencedFilePaths = referencedFiles.stream()
.map(ReferencedFile::getFilePath)
.collect(toList());
message.setReferencedFilePaths(referencedFilePaths);
message.setUserMessage(message.getPrompt());
message.setPrompt(getPromptWithContext(referencedFiles, message.getPrompt()));
totalTokensPanel.updateReferencedFilesTokens(referencedFiles);
project.getService(StandardChatToolWindowContentManager.class)
.tryFindChatToolWindowPanel()
.ifPresent(StandardChatToolWindowPanel::clearSelectedFilesNotification);
}
var messagePanel = toolWindowScrollablePanel.addMessage(message.getId());
messagePanel.add(new UserMessagePanel(project, message, this));
var responsePanel = createResponsePanel(message, conversationType);
messagePanel.add(responsePanel);
updateTotalTokens(message);
call(message, conversationType, responsePanel, false);
};
// TODO
if (ApplicationManager.getApplication().isUnitTestMode()) {
runnable.run();
} else {
SwingUtilities.invokeLater(runnable);
}
}
private void updateTotalTokens(Message message) {
int userPromptTokens = EncodingManager.getInstance().countTokens(message.getPrompt());
int conversationTokens = EncodingManager.getInstance().countConversationTokens(conversation);
totalTokensPanel.updateConversationTokens(conversationTokens + userPromptTokens);
}
private ResponsePanel createResponsePanel(Message message, ConversationType conversationType) {
return new ResponsePanel()
.withReloadAction(() -> reloadMessage(message, conversation, conversationType))
.withDeleteAction(() -> removeMessage(message.getId(), conversation))
.addContent(new ChatMessageResponseBody(project, true, this));
}
public TotalTokensDetails getTokenDetails() {
return totalTokensPanel.getTokenDetails();
}
public void requestFocusForTextArea() {
userPromptTextArea.focus();
}
public void displayLandingView() {
toolWindowScrollablePanel.displayLandingView(getLandingView());
totalTokensPanel.updateConversationTokens(conversation);
}
protected void reloadMessage(
Message message,
Conversation conversation,
ConversationType conversationType) {
ResponsePanel responsePanel = null;
try {
responsePanel = toolWindowScrollablePanel.getMessageResponsePanel(message.getId());
((ChatMessageResponseBody) responsePanel.getContent()).clear();
toolWindowScrollablePanel.update();
} catch (Exception e) {
throw new RuntimeException("Could not delete the existing message component", e);
} finally {
LOG.debug("Reloading message: " + message.getId());
if (responsePanel != null) {
message.setResponse("");
conversationService.saveMessage(conversation, message);
call(message, conversationType, responsePanel, true);
}
totalTokensPanel.updateConversationTokens(conversation);
TelemetryAction.IDE_ACTION.createActionMessage()
.property("action", ActionType.RELOAD_MESSAGE.name())
.send();
}
}
protected void removeMessage(UUID messageId, Conversation conversation) {
toolWindowScrollablePanel.removeMessage(messageId);
conversation.removeMessage(messageId);
conversationService.saveConversation(conversation);
totalTokensPanel.updateConversationTokens(conversation);
if (conversation.getMessages().isEmpty()) {
displayLandingView();
}
}
protected void clearWindow() {
toolWindowScrollablePanel.clearAll();
totalTokensPanel.updateConversationTokens(conversation);
}
private void call(
Message message,
ConversationType conversationType,
ResponsePanel responsePanel,
boolean retry) {
var responseContainer = (ChatMessageResponseBody) responsePanel.getContent();
if (!CompletionRequestService.getInstance().isRequestAllowed()) {
responseContainer.displayMissingCredential();
return;
}
var requestHandler = new CompletionRequestHandler(
useContextualSearch,
new ToolWindowCompletionResponseEventListener(
conversationService,
responsePanel,
totalTokensPanel,
userPromptTextArea) {
@Override
public void handleTokensExceededPolicyAccepted() {
call(message, conversationType, responsePanel, true);
}
});
userPromptTextArea.setRequestHandler(requestHandler);
userPromptTextArea.setSubmitEnabled(false);
requestHandler.call(new CallParameters(conversation, conversationType, message, retry));
}
private void handleSubmit(String text) {
var message = new Message(text);
var editor = EditorUtil.getSelectedEditor(project);
if (editor != null) {
var selectionModel = editor.getSelectionModel();
var selectedText = selectionModel.getSelectedText();
if (selectedText != null && !selectedText.isEmpty()) {
var fileExtension = FileUtil.getFileExtension(
((EditorImpl) editor).getVirtualFile().getName());
message = new Message(text + format("\n```%s\n%s\n```", fileExtension, selectedText));
selectionModel.removeSelection();
}
}
message.setUserMessage(text);
sendMessage(message, ConversationType.DEFAULT);
}
private JPanel createUserPromptPanel(ServiceType selectedService) {
var panel = new JPanel(new BorderLayout());
panel.setBorder(JBUI.Borders.compound(
JBUI.Borders.customLine(JBColor.border(), 1, 0, 0, 0),
JBUI.Borders.empty(8)));
var contentManager = project.getService(StandardChatToolWindowContentManager.class);
panel.add(JBUI.Panels.simplePanel(new UserPromptTextAreaHeader(
selectedService,
totalTokensPanel,
contentManager::createNewTabPanel)), BorderLayout.NORTH);
panel.add(JBUI.Panels.simplePanel(userPromptTextArea), BorderLayout.CENTER);
return panel;
}
private JPanel createRootPanel() {
var gbc = new GridBagConstraints();
gbc.fill = GridBagConstraints.BOTH;
gbc.weighty = 1;
gbc.weightx = 1;
gbc.gridx = 0;
gbc.gridy = 0;
var rootPanel = new JPanel(new GridBagLayout());
rootPanel.add(createScrollPaneWithSmartScroller(toolWindowScrollablePanel), gbc);
gbc.weighty = 0;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.gridy = 1;
rootPanel.add(
createUserPromptPanel(SettingsState.getInstance().getSelectedService()), gbc);
return rootPanel;
}
}

View file

@ -5,6 +5,7 @@ import static com.intellij.openapi.ui.Messages.OK;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import ee.carlrobert.codegpt.EncodingManager;
import ee.carlrobert.codegpt.completions.CallParameters;
import ee.carlrobert.codegpt.completions.CompletionResponseEventListener;
import ee.carlrobert.codegpt.conversations.Conversation;
import ee.carlrobert.codegpt.conversations.ConversationService;
@ -118,12 +119,9 @@ abstract class ToolWindowCompletionResponseEventListener implements
}
@Override
public void handleCompleted(
String fullMessage,
Message message,
Conversation conversation,
boolean retry) {
conversationService.saveMessage(fullMessage, message, conversation, retry);
public void handleCompleted(String fullMessage, CallParameters callParameters) {
var message = callParameters.getMessage();
conversationService.saveMessage(fullMessage, callParameters);
var serpResults = serpResultsMapping.get(message.getId());
var containsResults = serpResults != null && !serpResults.isEmpty();
@ -139,7 +137,7 @@ abstract class ToolWindowCompletionResponseEventListener implements
responseContainer.displaySerpResults(serpResults);
}
totalTokensPanel.updateUserPromptTokens(userPromptTextArea.getText());
totalTokensPanel.updateConversationTokens(conversation);
totalTokensPanel.updateConversationTokens(callParameters.getConversation());
} finally {
stopStreaming(responseContainer);
}

View file

@ -97,7 +97,8 @@ class ContextualChatToolWindowLandingPanel extends ResponsePanel {
var folderStructureTreePanel = new FolderStructureTreePanel(project);
var show = OverlayUtil.showFileStructureDialog(project, folderStructureTreePanel);
if (show == OK_EXIT_CODE) {
new CodebaseIndexingTask(project, folderStructureTreePanel.getCheckedFiles()).run();
new CodebaseIndexingTask(project, folderStructureTreePanel.getReferencedFiles())
.run();
}
break;
default:

View file

@ -1,13 +1,14 @@
package ee.carlrobert.codegpt.toolwindow.chat.contextual;
import com.intellij.openapi.project.Project;
import ee.carlrobert.codegpt.completions.ConversationType;
import ee.carlrobert.codegpt.conversations.Conversation;
import ee.carlrobert.codegpt.conversations.message.Message;
import ee.carlrobert.codegpt.toolwindow.chat.BaseChatToolWindowTabPanel;
import ee.carlrobert.codegpt.toolwindow.chat.ChatToolWindowTabPanel;
import javax.swing.JComponent;
import org.jetbrains.annotations.NotNull;
public class ContextualChatToolWindowTabPanel extends BaseChatToolWindowTabPanel {
public class ContextualChatToolWindowTabPanel extends ChatToolWindowTabPanel {
public ContextualChatToolWindowTabPanel(
@NotNull Project project,
@ -20,6 +21,6 @@ public class ContextualChatToolWindowTabPanel extends BaseChatToolWindowTabPanel
protected JComponent getLandingView() {
return new ContextualChatToolWindowLandingPanel(
project,
(prompt) -> sendMessage(new Message(prompt)));
(prompt) -> sendMessage(new Message(prompt), ConversationType.DEFAULT));
}
}

View file

@ -11,6 +11,7 @@ import com.intellij.openapi.wm.ToolWindowAnchor;
import com.intellij.openapi.wm.ToolWindowManager;
import com.intellij.ui.content.Content;
import ee.carlrobert.codegpt.Icons;
import ee.carlrobert.codegpt.completions.ConversationType;
import ee.carlrobert.codegpt.conversations.Conversation;
import ee.carlrobert.codegpt.conversations.ConversationService;
import ee.carlrobert.codegpt.conversations.ConversationsState;
@ -30,18 +31,22 @@ public final class StandardChatToolWindowContentManager {
}
public void sendMessage(Message message) {
sendMessage(message, ConversationType.DEFAULT);
}
public void sendMessage(Message message, ConversationType conversationType) {
getToolWindow().show();
if (ConfigurationState.getInstance().isCreateNewChatOnEachAction()
|| ConversationsState.getCurrentConversation() == null) {
createNewTabPanel().sendMessage(message);
createNewTabPanel().sendMessage(message, conversationType);
return;
}
tryFindChatTabbedPane()
.map(tabbedPane -> tabbedPane.tryFindActiveTabPanel().orElseGet(this::createNewTabPanel))
.orElseGet(this::createNewTabPanel)
.sendMessage(message);
.sendMessage(message, conversationType);
}
public void displayConversation(@NotNull Conversation conversation) {

View file

@ -15,7 +15,7 @@ import ee.carlrobert.codegpt.actions.toolwindow.OpenInEditorAction;
import ee.carlrobert.codegpt.conversations.ConversationService;
import ee.carlrobert.codegpt.conversations.ConversationsState;
import ee.carlrobert.codegpt.toolwindow.chat.ui.SelectedFilesNotification;
import ee.carlrobert.embedding.CheckedFile;
import ee.carlrobert.embedding.ReferencedFile;
import java.awt.BorderLayout;
import java.util.List;
import javax.swing.JPanel;
@ -39,8 +39,8 @@ public class StandardChatToolWindowPanel extends SimpleToolWindowPanel {
(IncludeFilesInContextNotifier) this::displaySelectedFilesNotification);
}
public void displaySelectedFilesNotification(List<CheckedFile> checkedFiles) {
selectedFilesNotification.displaySelectedFilesNotification(checkedFiles);
public void displaySelectedFilesNotification(List<ReferencedFile> referencedFiles) {
selectedFilesNotification.displaySelectedFilesNotification(referencedFiles);
}
public void clearSelectedFilesNotification() {

View file

@ -4,10 +4,11 @@ import static java.lang.String.format;
import com.intellij.openapi.editor.impl.EditorImpl;
import com.intellij.openapi.project.Project;
import ee.carlrobert.codegpt.completions.ConversationType;
import ee.carlrobert.codegpt.conversations.Conversation;
import ee.carlrobert.codegpt.conversations.message.Message;
import ee.carlrobert.codegpt.settings.state.YouSettingsState;
import ee.carlrobert.codegpt.toolwindow.chat.BaseChatToolWindowTabPanel;
import ee.carlrobert.codegpt.toolwindow.chat.ChatToolWindowTabPanel;
import ee.carlrobert.codegpt.toolwindow.chat.ui.ChatMessageResponseBody;
import ee.carlrobert.codegpt.toolwindow.chat.ui.ResponsePanel;
import ee.carlrobert.codegpt.toolwindow.chat.ui.UserMessagePanel;
@ -17,7 +18,7 @@ import ee.carlrobert.codegpt.util.file.FileUtil;
import javax.swing.JComponent;
import org.jetbrains.annotations.NotNull;
public class StandardChatToolWindowTabPanel extends BaseChatToolWindowTabPanel {
public class StandardChatToolWindowTabPanel extends ChatToolWindowTabPanel {
public StandardChatToolWindowTabPanel(
@NotNull Project project,
@ -49,7 +50,7 @@ public class StandardChatToolWindowTabPanel extends BaseChatToolWindowTabPanel {
format("\n```%s\n%s\n```", fileExtension, editor.getSelectionModel().getSelectedText())));
message.setUserMessage(action.getUserMessage());
sendMessage(message);
sendMessage(message, ConversationType.DEFAULT);
});
}
@ -69,7 +70,7 @@ public class StandardChatToolWindowTabPanel extends BaseChatToolWindowTabPanel {
var messagePanel = toolWindowScrollablePanel.addMessage(message.getId());
messagePanel.add(new UserMessagePanel(project, message, this));
messagePanel.add(new ResponsePanel()
.withReloadAction(() -> reloadMessage(message, conversation))
.withReloadAction(() -> reloadMessage(message, conversation, ConversationType.DEFAULT))
.withDeleteAction(() -> removeMessage(message.getId(), conversation))
.addContent(messageResponseBody));
});

View file

@ -11,12 +11,10 @@ import com.intellij.util.ui.JBUI;
import ee.carlrobert.codegpt.conversations.ConversationService;
import ee.carlrobert.codegpt.conversations.ConversationsState;
import ee.carlrobert.codegpt.settings.state.SettingsState;
import ee.carlrobert.embedding.CheckedFile;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.TreeMap;

View file

@ -39,7 +39,7 @@ public class SelectedFilesAccordion extends JPanel {
panel.setOpaque(false);
panel.setVisible(false);
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
panel.setBorder(JBUI.Borders.emptyBottom(4));
panel.setBorder(JBUI.Borders.empty(4, 0));
referencedFilePaths.stream()
.map(filePath -> LocalFileSystem.getInstance().findFileByPath(filePath))
.filter(Objects::nonNull)

View file

@ -12,7 +12,7 @@ import com.intellij.util.ui.JBUI;
import com.intellij.util.ui.JBUI.CurrentTheme.NotificationInfo;
import ee.carlrobert.codegpt.CodeGPTKeys;
import ee.carlrobert.codegpt.actions.IncludeFilesInContextNotifier;
import ee.carlrobert.embedding.CheckedFile;
import ee.carlrobert.embedding.ReferencedFile;
import java.awt.BorderLayout;
import java.nio.file.Paths;
import java.util.List;
@ -47,14 +47,14 @@ public class SelectedFilesNotification extends JPanel {
}), BorderLayout.LINE_END);
}
public void displaySelectedFilesNotification(@NotNull List<CheckedFile> checkedFiles) {
if (checkedFiles.isEmpty()) {
public void displaySelectedFilesNotification(@NotNull List<ReferencedFile> referencedFiles) {
if (referencedFiles.isEmpty()) {
return;
}
label.setText(checkedFiles.size() + " files selected");
var referencedFilePaths = checkedFiles.stream()
.map(CheckedFile::getFilePath)
label.setText(referencedFiles.size() + " files selected");
var referencedFilePaths = referencedFiles.stream()
.map(ReferencedFile::getFilePath)
.collect(Collectors.toList());
label.setToolTipText(getHtml(referencedFilePaths));
setVisible(true);

View file

@ -16,8 +16,7 @@ import ee.carlrobert.codegpt.CodeGPTKeys;
import ee.carlrobert.codegpt.EncodingManager;
import ee.carlrobert.codegpt.actions.IncludeFilesInContextNotifier;
import ee.carlrobert.codegpt.conversations.Conversation;
import ee.carlrobert.codegpt.indexes.CodebaseIndexingCompletedNotifier;
import ee.carlrobert.embedding.CheckedFile;
import ee.carlrobert.embedding.ReferencedFile;
import java.awt.FlowLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
@ -114,7 +113,7 @@ public class TotalTokensPanel extends JPanel {
update();
}
public void updateReferencedFilesTokens(List<CheckedFile> includedFiles) {
public void updateReferencedFilesTokens(List<ReferencedFile> includedFiles) {
totalTokensDetails.setReferencedFilesTokens(includedFiles.stream()
.mapToInt(file -> encodingManager.countTokens(file.getFileContent()))
.sum());
@ -123,7 +122,7 @@ public class TotalTokensPanel extends JPanel {
private TotalTokensDetails createTokenDetails(
Conversation conversation,
List<CheckedFile> includedFiles,
List<ReferencedFile> includedFiles,
@Nullable String highlightedText) {
var tokenDetails = new TotalTokensDetails(encodingManager);
tokenDetails.setConversationTokens(encodingManager.countConversationTokens(conversation));

View file

@ -6,7 +6,7 @@ import com.intellij.ui.CheckboxTree;
import com.intellij.ui.CheckedTreeNode;
import com.intellij.ui.ColoredTreeCellRenderer;
import ee.carlrobert.codegpt.util.file.FileUtil;
import ee.carlrobert.embedding.CheckedFile;
import ee.carlrobert.embedding.ReferencedFile;
import java.util.List;
import org.jetbrains.annotations.NotNull;
@ -16,7 +16,7 @@ public abstract class FileCheckboxTree extends CheckboxTree {
super(cellRenderer, node);
}
public abstract List<CheckedFile> getCheckedFiles();
public abstract List<ReferencedFile> getReferencedFiles();
protected static void updateFilePresentation(
ColoredTreeCellRenderer textRenderer,

View file

@ -9,7 +9,7 @@ import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.impl.file.PsiDirectoryImpl;
import com.intellij.ui.CheckedTreeNode;
import ee.carlrobert.embedding.CheckedFile;
import ee.carlrobert.embedding.ReferencedFile;
import java.io.File;
import java.util.Arrays;
import java.util.List;
@ -23,7 +23,7 @@ public class PsiElementCheckboxTree extends FileCheckboxTree {
setRootVisible(true);
}
public List<CheckedFile> getCheckedFiles() {
public List<ReferencedFile> getReferencedFiles() {
var checkedNodes = getCheckedNodes(
PsiElement.class,
node -> Optional.ofNullable(node.getContainingFile())
@ -34,7 +34,8 @@ public class PsiElementCheckboxTree extends FileCheckboxTree {
}
return Arrays.stream(checkedNodes)
.map(item -> new CheckedFile(new File(item.getContainingFile().getVirtualFile().getPath())))
.map(item -> new ReferencedFile(
new File(item.getContainingFile().getVirtualFile().getPath())))
.collect(toList());
}

View file

@ -6,7 +6,7 @@ import com.intellij.icons.AllIcons;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.ui.CheckedTreeNode;
import com.intellij.util.PlatformIcons;
import ee.carlrobert.embedding.CheckedFile;
import ee.carlrobert.embedding.ReferencedFile;
import java.io.File;
import java.util.Arrays;
import java.util.List;
@ -19,14 +19,14 @@ public class VirtualFileCheckboxTree extends FileCheckboxTree {
super(createFileTypesRenderer(), createRootNode(rootFiles));
}
public List<CheckedFile> getCheckedFiles() {
public List<ReferencedFile> getReferencedFiles() {
var checkedNodes = getCheckedNodes(VirtualFile.class, Objects::nonNull);
if (checkedNodes.length > 1000) {
throw new RuntimeException("Too many files selected");
}
return Arrays.stream(checkedNodes)
.map(item -> new CheckedFile(new File(item.getPath())))
.map(item -> new ReferencedFile(new File(item.getPath())))
.collect(toList());
}