Skip to content

Commit

Permalink
Fix #399 Use TextContent for creation of ChatMessages + Corrected log…
Browse files Browse the repository at this point in the history
…ging
  • Loading branch information
stephanj committed Dec 17, 2024
1 parent 10efaa0 commit d94f09e
Show file tree
Hide file tree
Showing 21 changed files with 212 additions and 97 deletions.
1 change: 1 addition & 0 deletions src/main/java/com/devoxx/genie/model/Constant.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ private Constant() {
public static final String FIND_PROMPT = "Perform semantic search on the project files using RAG and show matching files. (NOTE: The /find command requires RAG to be enabled in settings)";
public static final String HELP_PROMPT = "Display help and available commands for the Genie Devoxx Plugin";

public static final String COMMAND_PREFIX = "/";
public static final String TEST_COMMAND = "test";
public static final String FIND_COMMAND = "find";
public static final String REVIEW_COMMAND = "review";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,27 @@
import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.chat.StreamingChatLanguageModel;
import dev.langchain4j.model.output.TokenUsage;
import lombok.Builder;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import lombok.*;

import java.time.LocalDateTime;
import java.util.List;

/**
* Represents the context of a chat message with all the necessary information.
*/
@ToString
@Data
@Builder
public class ChatMessageContext {
private final LocalDateTime createdOn = LocalDateTime.now();
private String id;
private Project project;
private Integer timeout;
private String userPrompt;
private UserMessage userMessage;
private AiMessage aiMessage;
private String context;
private EditorInfo editorInfo;
private String userPrompt; // The user prompt
private UserMessage userMessage; // The user message
private AiMessage aiMessage; // The LLM response message
private String context; // The context of the prompt
private EditorInfo editorInfo; // The editor info
private LanguageModel languageModel;
private ChatLanguageModel chatLanguageModel;
private StreamingChatLanguageModel streamingChatLanguageModel;
Expand Down
11 changes: 5 additions & 6 deletions src/main/java/com/devoxx/genie/service/ChatMemoryService.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,7 @@
import com.devoxx.genie.util.ChatMessageContextUtil;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.project.Project;
import dev.langchain4j.data.message.AiMessage;
import dev.langchain4j.data.message.ChatMessage;
import dev.langchain4j.data.message.SystemMessage;
import dev.langchain4j.data.message.UserMessage;
import dev.langchain4j.data.message.*;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import dev.langchain4j.store.memory.chat.InMemoryChatMemoryStore;
import org.jetbrains.annotations.NotNull;
Expand All @@ -36,6 +33,8 @@ public void init(@NotNull Project project) {
createChangeListener();
}

// TODO - This method is currently not used anywhere in the codebase
// TODO - Should be triggered when user changes the chat memory size in the settings
private void createChangeListener() {
ApplicationManager.getApplication().getMessageBus()
.connect()
Expand Down Expand Up @@ -102,9 +101,9 @@ public void restoreConversation(@NotNull Project project, @NotNull Conversation
clear(project);
for (com.devoxx.genie.model.conversation.ChatMessage message : conversation.getMessages()) {
if (message.isUser()) {
add(project, new UserMessage(message.getContent()));
add(project, UserMessage.from(new TextContent(message.getContent())));
} else {
add(project, new AiMessage(message.getContent()));
add(project, AiMessage.from(message.getContent()));
}
}
}
Expand Down
15 changes: 9 additions & 6 deletions src/main/java/com/devoxx/genie/service/ChatPromptExecutor.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,7 @@
import java.util.concurrent.CancellationException;
import java.util.concurrent.ConcurrentHashMap;

import static com.devoxx.genie.model.Constant.FIND_COMMAND;
import static com.devoxx.genie.model.Constant.HELP_COMMAND;
import static com.devoxx.genie.model.Constant.*;

public class ChatPromptExecutor {

Expand Down Expand Up @@ -182,16 +181,20 @@ public void stopPromptExecution(Project project) {
* @param promptOutputPanel the prompt output panel
* @return the command
*/
private Optional<String> getCommandFromPrompt(@NotNull ChatMessageContext chatMessageContext,
public Optional<String> getCommandFromPrompt(@NotNull ChatMessageContext chatMessageContext,
PromptOutputPanel promptOutputPanel) {
String prompt = chatMessageContext.getUserPrompt().trim();

// Early exit if not a command
if (!prompt.startsWith(COMMAND_PREFIX)) {
return Optional.of(prompt);
}

DevoxxGenieSettingsService settings = DevoxxGenieStateService.getInstance();
List<CustomPrompt> customPrompts = settings.getCustomPrompts();

Optional<CustomPrompt> matchingPrompt = customPrompts.stream()
.filter(customPrompt ->
prompt.equalsIgnoreCase("/" + customPrompt.getName())
)
.filter(customPrompt -> prompt.equalsIgnoreCase(COMMAND_PREFIX + customPrompt.getName()))
.findFirst();

// if OK
Expand Down
41 changes: 16 additions & 25 deletions src/main/java/com/devoxx/genie/service/MessageCreationService.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@
import com.devoxx.genie.ui.util.NotificationUtil;
import com.devoxx.genie.util.ChatMessageContextUtil;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import dev.langchain4j.data.message.TextContent;
import dev.langchain4j.data.message.UserMessage;
import org.jetbrains.annotations.NotNull;

Expand All @@ -24,7 +26,6 @@
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.logging.Logger;
import java.util.stream.Collectors;

import static com.devoxx.genie.action.AddSnippetAction.SELECTED_TEXT_KEY;
Expand All @@ -34,9 +35,8 @@
* Here's where also the basic prompt "engineering" is happening, including calling the AST magic.
*/
public class MessageCreationService {
private static final Logger LOG = Logger.getLogger(MessageCreationService.class.getName());

public static final String CONTEXT_PROMPT = "Context: \n";
private static final Logger LOG = Logger.getInstance(MessageCreationService.class.getName());

private static final String GIT_DIFF_INSTRUCTIONS = """
Please analyze the code and provide ONLY the modified code in your response.
Expand All @@ -61,23 +61,18 @@ public static MessageCreationService getInstance() {
/**
* Create user message.
* @param chatMessageContext the chat message context
* @return the user message
*/
@NotNull
public UserMessage createUserMessage(@NotNull ChatMessageContext chatMessageContext) {
UserMessage userMessage;
public void addUserMessageToContext(@NotNull ChatMessageContext chatMessageContext) {
String context = chatMessageContext.getContext();

if (context != null && !context.isEmpty()) {
userMessage = constructUserMessageWithFullContext(chatMessageContext, context);
constructUserMessageWithFullContext(chatMessageContext, context);
} else {
userMessage = constructUserMessageWithCombinedContext(chatMessageContext);
constructUserMessageWithCombinedContext(chatMessageContext);
}

return userMessage;
}

private @NotNull UserMessage constructUserMessageWithCombinedContext(@NotNull ChatMessageContext chatMessageContext) {
private void constructUserMessageWithCombinedContext(@NotNull ChatMessageContext chatMessageContext) {
LOG.debug("Constructing user message with combined context");

StringBuilder stringBuilder = new StringBuilder();

Expand Down Expand Up @@ -107,14 +102,10 @@ public UserMessage createUserMessage(@NotNull ChatMessageContext chatMessageCont
// Add editor content or selected text
String editorContent = getEditorContentOrSelectedText(chatMessageContext);
if (!editorContent.isEmpty()) {
stringBuilder.append("<EditorContext>\n");
stringBuilder.append(editorContent);
stringBuilder.append("\n</EditorContext>\n\n");
}

UserMessage userMessage = new UserMessage(stringBuilder.toString());
chatMessageContext.setUserMessage(userMessage);
return userMessage;
chatMessageContext.setUserMessage(UserMessage.from(new TextContent(stringBuilder.toString())));
}

/**
Expand All @@ -123,6 +114,8 @@ public UserMessage createUserMessage(@NotNull ChatMessageContext chatMessageCont
* @return the user message
*/
private @NotNull String addSemanticSearchResults(@NotNull ChatMessageContext chatMessageContext) {
LOG.debug("Adding semantic search results to user message");

StringBuilder contextBuilder = new StringBuilder();

try {
Expand Down Expand Up @@ -157,7 +150,7 @@ public UserMessage createUserMessage(@NotNull ChatMessageContext chatMessageCont
);
}
} catch (Exception e) {
LOG.warning("Failed to get semantic search results: " + e.getMessage());
LOG.warn("Failed to get semantic search results: " + e.getMessage());
}

return contextBuilder.toString();
Expand Down Expand Up @@ -227,10 +220,10 @@ public static List<SemanticFile> extractFileReferences(@NotNull Map<String, Sear
*
* @param chatMessageContext the chat message context
* @param context the context
* @return the user message
*/
private @NotNull UserMessage constructUserMessageWithFullContext(@NotNull ChatMessageContext chatMessageContext,
String context) {
private void constructUserMessageWithFullContext(@NotNull ChatMessageContext chatMessageContext,
String context) {
LOG.debug("Constructing user message with full context");
StringBuilder stringBuilder = new StringBuilder();

// If git diff is enabled, add special instructions at the beginning
Expand All @@ -251,9 +244,7 @@ public static List<SemanticFile> extractFileReferences(@NotNull Map<String, Sear
stringBuilder.append(chatMessageContext.getUserPrompt());
stringBuilder.append("</UserPrompt>");

UserMessage userMessage = new UserMessage("user_message", stringBuilder.toString());
chatMessageContext.setUserMessage(userMessage);
return userMessage;
chatMessageContext.setUserMessage(UserMessage.from(new TextContent(stringBuilder.toString())));
}

/**
Expand Down
38 changes: 20 additions & 18 deletions src/main/java/com/devoxx/genie/service/PromptExecutionService.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,13 @@
import com.devoxx.genie.util.ChatMessageContextUtil;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import dev.langchain4j.data.message.AiMessage;
import dev.langchain4j.data.message.SystemMessage;
import dev.langchain4j.data.message.UserMessage;
import dev.langchain4j.data.message.*;
import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.output.Response;
import lombok.Getter;
import org.jetbrains.annotations.NotNull;

import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
Expand Down Expand Up @@ -47,30 +46,28 @@ static PromptExecutionService getInstance() {
* @return the response
*/
public @NotNull CompletableFuture<Response<AiMessage>> executeQuery(@NotNull ChatMessageContext chatMessageContext) {
LOG.info("Execute query : " + chatMessageContext);
LOG.debug("Execute query : " + chatMessageContext);

queryLock.lock();
try {
if (isCanceled()) return CompletableFuture.completedFuture(null);

MessageCreationService messageCreationService = MessageCreationService.getInstance();
ChatMemoryService chatMemoryService = ChatMemoryService.getInstance();

// Add System Message if ChatMemoryService is empty
if (ChatMemoryService.getInstance().isEmpty(chatMessageContext.getProject())) {
LOG.info("ChatMemoryService is empty, adding a new SystemMessage");
LOG.debug("ChatMemoryService is empty, adding a new SystemMessage");

if (!ChatMessageContextUtil.isOpenAIo1Model(chatMessageContext.getLanguageModel())) {
ChatMemoryService
.getInstance()
.add(chatMessageContext.getProject(),
new SystemMessage(DevoxxGenieStateService.getInstance().getSystemPrompt() + Constant.MARKDOWN)
);
String systemPrompt = DevoxxGenieStateService.getInstance().getSystemPrompt() + Constant.MARKDOWN;
chatMemoryService.add(chatMessageContext.getProject(), SystemMessage.from(systemPrompt));
}
}

UserMessage userMessage = messageCreationService.createUserMessage(chatMessageContext);
LOG.info("Created UserMessage: " + userMessage);
// Add User message to context
MessageCreationService.getInstance().addUserMessageToContext(chatMessageContext);

ChatMemoryService.getInstance().add(chatMessageContext.getProject(), userMessage);
// chatMemoryService.add(chatMessageContext.getProject(), chatMessageContext.getUserMessage());

long startTime = System.currentTimeMillis();

Expand Down Expand Up @@ -117,12 +114,17 @@ private boolean isCanceled() {
private @NotNull Response<AiMessage> processChatMessage(ChatMessageContext chatMessageContext) {
try {
ChatLanguageModel chatLanguageModel = chatMessageContext.getChatLanguageModel();
Response<AiMessage> response =
chatLanguageModel
.generate(ChatMemoryService.getInstance().messages(chatMessageContext.getProject()));
ChatMemoryService.getInstance().add(chatMessageContext.getProject(), response.content());

ChatMemoryService chatMemoryService = ChatMemoryService.getInstance();
List<ChatMessage> messages = chatMemoryService.messages(chatMessageContext.getProject());

Response<AiMessage> response = chatLanguageModel.generate(messages);

chatMemoryService.add(chatMessageContext.getProject(), response.content());

return response;
} catch (Exception e) {
LOG.error("Error occurred while processing chat message", e);
if (chatMessageContext.getLanguageModel().getProvider().equals(ModelProvider.Jan)) {
throw new ModelNotActiveException("Selected Jan model is not active. Download and make it active or add API Key in Jan settings.");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public void startChromaDB(Project project, ChromaDBStatusCallback callback) {
try {
// We assume Docker is already installed and running
if (isChromaDBRunning()) {
LOG.info("ChromaDB is already running");
LOG.debug("ChromaDB is already running");
callback.onSuccess();
return;
}
Expand Down Expand Up @@ -182,7 +182,7 @@ public void deleteCollectionData(@NotNull Project project, @NotNull String colle
Path collectionPath = volumePath.resolve(collectionName);

if (!Files.exists(collectionPath)) {
LOG.info("Collection directory does not exist: " + collectionPath);
LOG.debug("Collection directory does not exist: " + collectionPath);
return;
}

Expand All @@ -197,7 +197,7 @@ private void deleteCollectionData(@NotNull Path collectionPath, @NotNull String
.forEach(path -> {
try {
Files.delete(path);
LOG.info("Deleted: " + path);
LOG.debug("Deleted: " + path);
} catch (IOException e) {
LOG.warn("Failed to delete: " + path, e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ private StringBuilder getContentFromModules(Project project,
readFileContent(file, fullContent, scanContentResult);
} else {
scanContentResult.incrementSkippedFileCount();
LOG.info("Skipping file: " + file.getPath() + " (excluded by settings or .gitignore)");
LOG.debug("Skipping file: " + file.getPath() + " (excluded by settings or .gitignore)");
}
return fullContent;
}
Expand All @@ -210,7 +210,7 @@ public boolean visitFile(@NotNull VirtualFile file) {
readFileContent(file, fullContent, scanContentResult);
} else {
scanContentResult.incrementSkippedFileCount();
LOG.info("Skipping file: " + file.getPath() + " (not in content, excluded by settings, or .gitignore)");
LOG.debug("Skipping file: " + file.getPath() + " (not in content, excluded by settings, or .gitignore)");
}
}
return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ public void indexFiles(Project project,

VirtualFile baseDir = LocalFileSystem.getInstance().findFileByPath(basePath);
if (baseDir == null) {
LOG.info("Could not find base directory: " + basePath);
LOG.debug("Could not find base directory: " + basePath);
return;
}

Expand Down Expand Up @@ -151,17 +151,17 @@ public void indexFiles(Project project,
* @param filePath Path to the file to index
*/
private void indexSingleFile(Path filePath) {
LOG.info("Indexing file: " + filePath);
LOG.debug("Indexing file: " + filePath);
try {
if (isFileIndexed(filePath)) {
LOG.info("File already indexed: " + filePath);
LOG.debug("File already indexed: " + filePath);
return;
}

processPath(filePath);
LOG.info("File successfully indexed: " + filePath);
LOG.debug("File successfully indexed: " + filePath);
} catch (Exception e) {
LOG.error("Error indexing file: " + filePath + " - " + e.getMessage());
LOG.warn("Error indexing file: " + filePath + " - " + e.getMessage());
}
}

Expand Down Expand Up @@ -208,7 +208,7 @@ private void markFileAsIndexed(Path filePath, TextSegment segment) {

private void processPath(Path path) {
try {
LOG.info("Processing file: " + path);
LOG.debug("Processing file: " + path);

String content = Files.readString(path);
if (content.isBlank()) {
Expand Down
Loading

0 comments on commit d94f09e

Please sign in to comment.