Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ plugins {
}

group = "com.devoxx.genie"
version = "0.5.0"
version = "0.5.1"

repositories {
mavenCentral()
Expand Down
42 changes: 33 additions & 9 deletions src/main/java/com/devoxx/genie/service/MessageCreationService.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.devoxx.genie.model.request.ChatMessageContext;
import com.devoxx.genie.model.request.EditorInfo;
import com.devoxx.genie.model.request.SemanticFile;
import com.devoxx.genie.service.mcp.MCPService;
import com.devoxx.genie.service.rag.SearchResult;
import com.devoxx.genie.service.rag.SemanticSearchService;
import com.devoxx.genie.ui.settings.DevoxxGenieStateService;
Expand Down Expand Up @@ -63,10 +64,18 @@ public static MessageCreationService getInstance() {

/**
* Create user message.
* IMPORTANT: This method should be called only once per user message to avoid duplicates
* in the chat memory. It is called by ChatMemoryManager.addUserMessage().
*
* @param chatMessageContext the chat message context
*/
public void addUserMessageToContext(@NotNull ChatMessageContext chatMessageContext) {
// Check if user message already exists to prevent duplicates
if (chatMessageContext.getUserMessage() != null) {
// Message already exists, skip creating another one
return;
}

String context = chatMessageContext.getFilesContext();
if (context != null && !context.isEmpty()) {
constructUserMessageWithFullContext(chatMessageContext, context);
Expand All @@ -78,7 +87,7 @@ public void addUserMessageToContext(@NotNull ChatMessageContext chatMessageConte

private void addImages(@NotNull ChatMessageContext chatMessageContext) {
List<VirtualFile> imageFiles = FileListManager.getInstance().getImageFiles(chatMessageContext.getProject());
if (imageFiles != null && !imageFiles.isEmpty()) {
if (!imageFiles.isEmpty()) {
// Add each image as content
for (VirtualFile imageFile: imageFiles) {
try {
Expand Down Expand Up @@ -111,12 +120,19 @@ private void constructUserMessageWithFullContext(@NotNull ChatMessageContext cha

// If git diff is enabled, add special instructions at the beginning
if (Boolean.TRUE.equals(DevoxxGenieStateService.getInstance().getUseSimpleDiff())) {
stringBuilder.append("<DiffInstructions>").append(GIT_DIFF_INSTRUCTIONS).append("</DiffInstructions>\n\n");
stringBuilder.append("<DiffInstructions>").append(GIT_DIFF_INSTRUCTIONS).append("</DiffInstructions>\n");
}

stringBuilder.append("<Context>");
stringBuilder.append(context);
stringBuilder.append("</Context>\n\n");
if (!context.isEmpty()) {
stringBuilder.append("<Context>");
stringBuilder.append(context);
stringBuilder.append("</Context>\n");
}

stringBuilder
.append("<ProjectPath>")
.append(chatMessageContext.getProject().getBasePath())
.append("</ProjectPath>\n");

stringBuilder.append("<UserPrompt>");
stringBuilder.append(chatMessageContext.getUserPrompt());
Expand Down Expand Up @@ -161,13 +177,21 @@ private void constructUserMessageWithCombinedContext(@NotNull ChatMessageContext
}
}

stringBuilder
.append("<ProjectPath>\n")
.append(chatMessageContext.getProject().getBasePath())
.append("</ProjectPath>");

// Add the user's prompt
stringBuilder.append("<UserPrompt>\n").append(chatMessageContext.getUserPrompt()).append("\n</UserPrompt>\n\n");

// Add editor content or selected text
String editorContent = getEditorContentOrSelectedText(chatMessageContext);
if (!editorContent.isEmpty()) {
stringBuilder.append(editorContent);
// Only include the currently open editor file when MCP is disabled
if (!MCPService.isMCPEnabled()) {
// Add editor content or selected text
String editorContent = getEditorContentOrSelectedText(chatMessageContext);
if (!editorContent.isEmpty()) {
stringBuilder.append(editorContent);
}
}

chatMessageContext.setUserMessage(UserMessage.from(stringBuilder.toString()));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
package com.devoxx.genie.service.prompt.error;

import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

/**
* Base exception class for all prompt-related errors.
* Provides a consistent way to handle and categorize errors in the prompt package.
*/
@Slf4j
@Getter
public class PromptException extends RuntimeException {
private final ErrorSeverity severity;
private final boolean userVisible;
Expand All @@ -19,19 +24,13 @@ public PromptException(String message, ErrorSeverity severity, boolean userVisib
super(message);
this.severity = severity;
this.userVisible = userVisible;
log.error("{}:{} - {}", severity, message, userVisible);
}

public PromptException(String message, Throwable cause, ErrorSeverity severity, boolean userVisible) {
super(message, cause);
this.severity = severity;
this.userVisible = userVisible;
}

public ErrorSeverity getSeverity() {
return severity;
}

public boolean isUserVisible() {
return userVisible;
log.error("{}:{} - {}", severity, message, userVisible);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import com.devoxx.genie.model.LanguageModel;
import com.devoxx.genie.model.conversation.Conversation;
import com.devoxx.genie.model.request.ChatMessageContext;
import com.devoxx.genie.service.MessageCreationService;
import com.devoxx.genie.service.mcp.MCPService;
import com.devoxx.genie.service.prompt.error.MemoryException;
import com.devoxx.genie.ui.settings.DevoxxGenieStateService;
Expand Down Expand Up @@ -33,15 +32,13 @@
public class ChatMemoryManager {

private final ChatMemoryService chatMemoryService;
private final MessageCreationService messageCreationService;

public static ChatMemoryManager getInstance() {
return ApplicationManager.getApplication().getService(ChatMemoryManager.class);
}

public ChatMemoryManager() {
this.chatMemoryService = ChatMemoryService.getInstance();
this.messageCreationService = MessageCreationService.getInstance();
}

/**
Expand Down Expand Up @@ -81,29 +78,18 @@ public void prepareMemory(@NotNull ChatMessageContext context) {
}
}

/**
* Adds user message to memory from the provided context
* @param context The chat message context containing the user message
*/
public void addUserMessage(@NotNull ChatMessageContext context) {
try {
messageCreationService.addUserMessageToContext(context);
chatMemoryService.addMessage(context.getProject(), context.getUserMessage());
log.debug("Added user message to memory");
} catch (Exception e) {
throw new MemoryException("Failed to add user message to memory", e);
}
}

/**
* Adds AI response to memory from the provided context
* @param context The chat message context containing the AI message
*/
public void addAiResponse(@NotNull ChatMessageContext context) {
try {
if (context.getAiMessage() != null) {
log.debug("Adding AI response to memory for context ID: {}", context.getId());
chatMemoryService.addMessage(context.getProject(), context.getAiMessage());
log.debug("Added AI response to memory");
log.debug("Successfully added AI response to memory");
} else {
log.warn("Attempted to add null AI message to memory for context ID: {}", context.getId());
}
} catch (Exception e) {
throw new MemoryException("Failed to add AI response to memory", e);
Expand Down Expand Up @@ -184,19 +170,6 @@ public void removeLastMessage(@NotNull Project project) {
}
}

/**
* Clears all messages from memory for a project
* @param project The project to clear memory for
*/
public void clearMemory(@NotNull Project project) {
try {
chatMemoryService.clearMemory(project);
log.debug("Cleared all messages from memory for project: {}", project.getLocationHash());
} catch (Exception e) {
throw new MemoryException("Failed to clear memory", e);
}
}

/**
* Gets all messages from memory for a project
* @param project The project to get messages for
Expand All @@ -210,19 +183,6 @@ public List<ChatMessage> getMessages(@NotNull Project project) {
}
}

/**
* Checks if memory is empty for a project
* @param project The project to check
* @return true if memory is empty, false otherwise
*/
public boolean isMemoryEmpty(@NotNull Project project) {
try {
return chatMemoryService.isEmpty(project);
} catch (Exception e) {
throw new MemoryException("Failed to check if memory is empty", e);
}
}

/**
* Gets the chat memory instance for a project
* @param projectHash The hash of the project to get memory for
Expand All @@ -235,6 +195,26 @@ public ChatMemory getChatMemory(String projectHash) {
throw new MemoryException("Failed to get chat memory", e);
}
}

/**
* Logs the current state of the chat memory for debugging purposes
* @param project The project to log memory for
*/
public void logMemoryState(@NotNull Project project) {
try {
List<ChatMessage> messages = getMessages(project);
log.debug("Current memory state for project {}, total messages: {}", project.getName(), messages.size());

for (int i = 0; i < messages.size(); i++) {
ChatMessage message = messages.get(i);
String type = message.getClass().getSimpleName();
String content = message.toString();
log.debug("Message[{}] - Type: {} - Content: {}", i, type, content);
}
} catch (Exception e) {
log.warn("Failed to log memory state: {}", e.getMessage());
}
}

/**
* Restores a conversation from a saved model
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,25 @@ public void addMessage(@NotNull Project project, ChatMessage chatMessage) {
String projectHash = project.getLocationHash();
MessageWindowChatMemory memory = projectConversations.get(projectHash);
if (memory != null) {
// Check for duplicate messages to prevent adding the same message multiple times
List<ChatMessage> currentMessages = memory.messages();
if (!currentMessages.isEmpty()) {
ChatMessage lastMessage = currentMessages.get(currentMessages.size() - 1);

// Check if the last message is of the same type and has the same content
if (lastMessage.getClass().equals(chatMessage.getClass()) &&
lastMessage.toString().equals(chatMessage.toString())) {
log.warn("Prevented duplicate message addition for project: {}", projectHash);
return; // Skip adding duplicate message
}
}

// Log the message content for debugging XML issues
log.debug("Adding message to memory - Type: {}, Content: {}",
chatMessage.getClass().getSimpleName(), chatMessage);

memory.add(chatMessage);
log.debug("Added message to project: {}", projectHash);
log.debug("Successfully added message to project: {}, message type: {}", projectHash, chatMessage.getClass().getSimpleName());
} else {
throw new MemoryException("Chat memory not initialized for project: " + projectHash);
}
Expand Down Expand Up @@ -223,7 +240,7 @@ private void createChatMemory(@NotNull String projectHash, int chatMemorySize) {
@Override
public ChatMemory get(Object projectHash) {
try {
return projectConversations.get((String) projectHash);
return projectConversations.get(projectHash.toString());
} catch (Exception e) {
throw new MemoryException("Failed to get chat memory for project hash: " + projectHash, e);
}
Expand Down
Loading
Loading