Skip to content

Commit

Permalink
Introduce NotifcationService
Browse files Browse the repository at this point in the history
Also:

- Move openAiChat from AiService (logic) to GroupTree (gui)
  • Loading branch information
koppor committed Sep 8, 2024
1 parent 7e2b0e0 commit 2bc0c63
Show file tree
Hide file tree
Showing 10 changed files with 64 additions and 65 deletions.
10 changes: 2 additions & 8 deletions src/main/java/org/jabref/gui/DialogService.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,15 @@
import org.jabref.gui.util.DirectoryDialogConfiguration;
import org.jabref.gui.util.FileDialogConfiguration;
import org.jabref.logic.importer.FetcherException;
import org.jabref.logic.util.NotificationService;

import org.controlsfx.control.textfield.CustomPasswordField;
import org.controlsfx.dialog.ProgressDialog;

/**
* This interface provides methods to create dialogs and show them to the user.
*/
public interface DialogService {
public interface DialogService extends NotificationService {

/**
* This will create and display new {@link ChoiceDialog} of type T with a default choice and a collection of possible choices
Expand Down Expand Up @@ -251,13 +252,6 @@ Optional<ButtonType> showCustomButtonDialogAndWait(Alert.AlertType type, String
*/
<V> Optional<ButtonType> showBackgroundProgressDialogAndWait(String title, String content, StateManager stateManager);

/**
* Notify the user in a non-blocking way (i.e., in form of toast in a snackbar).
*
* @param message the message to show.
*/
void notify(String message);

/**
* Shows a new file save dialog. The method doesn't return until the
* displayed file save dialog is dismissed. The return value specifies the
Expand Down
29 changes: 28 additions & 1 deletion src/main/java/org/jabref/gui/groups/GroupTreeViewModel.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import org.jabref.gui.AbstractViewModel;
import org.jabref.gui.DialogService;
import org.jabref.gui.StateManager;
import org.jabref.gui.ai.components.aichat.AiChatWindow;
import org.jabref.gui.util.CustomLocalDragboard;
import org.jabref.logic.ai.AiService;
import org.jabref.logic.l10n.Localization;
Expand All @@ -42,6 +43,7 @@
import org.jabref.model.metadata.MetaData;
import org.jabref.preferences.PreferencesService;

import com.airhacks.afterburner.injection.Injector;
import com.tobiasdiez.easybind.EasyBind;
import dev.langchain4j.data.message.ChatMessage;

Expand Down Expand Up @@ -399,7 +401,32 @@ public void chatWithGroup(GroupNodeViewModel group) {
ObservableList<ChatMessage> chatHistory = aiService.getChatHistoryService().getChatHistoryForGroup(group.getGroupNode());
ObservableList<BibEntry> bibEntries = FXCollections.observableArrayList(group.getGroupNode().findMatches(currentDatabase.get().getDatabase()));

aiService.openAiChat(nameProperty, chatHistory, currentDatabase.get(), bibEntries);
openAiChat(nameProperty, chatHistory, currentDatabase.get(), bibEntries);
}

private void openAiChat(StringProperty name, ObservableList<ChatMessage> chatHistory, BibDatabaseContext bibDatabaseContext, ObservableList<BibEntry> entries) {
Optional<AiChatWindow> existingWindow = stateManager.getAiChatWindows().stream().filter(window -> window.getChatName().equals(name.get())).findFirst();

if (existingWindow.isPresent()) {
existingWindow.get().requestFocus();
} else {
AiChatWindow aiChatWindow = new AiChatWindow(
Injector.instantiateModelOrService(AiService.class),
dialogService,
preferences.getAiPreferences(),
preferences.getFilePreferences(),
taskExecutor
);

aiChatWindow.setOnCloseRequest(event ->
stateManager.getAiChatWindows().remove(aiChatWindow)
);

stateManager.getAiChatWindows().add(aiChatWindow);
dialogService.showCustomWindow(aiChatWindow);
aiChatWindow.setChat(name, chatHistory, bibDatabaseContext, entries);
aiChatWindow.requestFocus();
}
}

public void removeSubgroups(GroupNodeViewModel group) {
Expand Down
50 changes: 9 additions & 41 deletions src/main/java/org/jabref/logic/ai/AiService.java
Original file line number Diff line number Diff line change
@@ -1,17 +1,12 @@
package org.jabref.logic.ai;

import java.util.Optional;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.ObservableList;

import org.jabref.gui.DialogService;
import org.jabref.gui.StateManager;
import org.jabref.gui.ai.components.aichat.AiChatWindow;
import org.jabref.gui.desktop.JabRefDesktop;
import org.jabref.logic.ai.chatting.AiChatService;
import org.jabref.logic.ai.chatting.chathistory.ChatHistoryService;
Expand All @@ -24,15 +19,13 @@
import org.jabref.logic.ai.summarization.SummariesService;
import org.jabref.logic.ai.summarization.storages.MVStoreSummariesStorage;
import org.jabref.logic.citationkeypattern.CitationKeyPatternPreferences;
import org.jabref.logic.util.NotificationService;
import org.jabref.logic.util.TaskExecutor;
import org.jabref.model.database.BibDatabaseContext;
import org.jabref.model.entry.BibEntry;
import org.jabref.preferences.FilePreferences;
import org.jabref.preferences.ai.AiPreferences;

import com.airhacks.afterburner.injection.Injector;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import dev.langchain4j.data.message.ChatMessage;

/**
* The main class for the AI functionality.
Expand All @@ -51,7 +44,7 @@ public class AiService implements AutoCloseable {

private final AiPreferences aiPreferences;
private final FilePreferences filePreferences;
private final DialogService dialogService;
private final NotificationService notificationService;
private final TaskExecutor taskExecutor;

// This field is used to shut down AI-related background tasks.
Expand All @@ -78,23 +71,23 @@ public class AiService implements AutoCloseable {
public AiService(AiPreferences aiPreferences,
FilePreferences filePreferences,
CitationKeyPatternPreferences citationKeyPatternPreferences,
DialogService dialogService,
NotificationService notificationService,
TaskExecutor taskExecutor
) {
this.aiPreferences = aiPreferences;
this.filePreferences = filePreferences;
this.dialogService = dialogService;
this.notificationService = notificationService;
this.taskExecutor = taskExecutor;

this.jabRefChatLanguageModel = new JabRefChatLanguageModel(aiPreferences);

this.mvStoreEmbeddingStore = new MVStoreEmbeddingStore(JabRefDesktop.getAiFilesDirectory().resolve(EMBEDDINGS_FILE_NAME), dialogService);
this.mvStoreFullyIngestedDocumentsTracker = new MVStoreFullyIngestedDocumentsTracker(JabRefDesktop.getAiFilesDirectory().resolve(FULLY_INGESTED_FILE_NAME), dialogService);
this.mvStoreSummariesStorage = new MVStoreSummariesStorage(JabRefDesktop.getAiFilesDirectory().resolve(SUMMARIES_FILE_NAME), dialogService);
this.mvStoreChatHistoryStorage = new MVStoreChatHistoryStorage(JabRefDesktop.getAiFilesDirectory().resolve(CHAT_HISTORY_FILE_NAME), dialogService);
this.mvStoreEmbeddingStore = new MVStoreEmbeddingStore(JabRefDesktop.getAiFilesDirectory().resolve(EMBEDDINGS_FILE_NAME), notificationService);
this.mvStoreFullyIngestedDocumentsTracker = new MVStoreFullyIngestedDocumentsTracker(JabRefDesktop.getAiFilesDirectory().resolve(FULLY_INGESTED_FILE_NAME), notificationService);
this.mvStoreSummariesStorage = new MVStoreSummariesStorage(JabRefDesktop.getAiFilesDirectory().resolve(SUMMARIES_FILE_NAME), notificationService);
this.mvStoreChatHistoryStorage = new MVStoreChatHistoryStorage(JabRefDesktop.getAiFilesDirectory().resolve(CHAT_HISTORY_FILE_NAME), notificationService);

this.chatHistoryService = new ChatHistoryService(citationKeyPatternPreferences, mvStoreChatHistoryStorage);
this.jabRefEmbeddingModel = new JabRefEmbeddingModel(aiPreferences, dialogService, taskExecutor);
this.jabRefEmbeddingModel = new JabRefEmbeddingModel(aiPreferences, notificationService, taskExecutor);
this.aiChatService = new AiChatService(aiPreferences, jabRefChatLanguageModel, jabRefEmbeddingModel, mvStoreEmbeddingStore, cachedThreadPool);
this.ingestionService = new IngestionService(
aiPreferences,
Expand Down Expand Up @@ -132,31 +125,6 @@ public SummariesService getSummariesService() {
return summariesService;
}

public void openAiChat(StringProperty name, ObservableList<ChatMessage> chatHistory, BibDatabaseContext bibDatabaseContext, ObservableList<BibEntry> entries) {
Optional<AiChatWindow> existingWindow = stateManager.getAiChatWindows().stream().filter(window -> window.getChatName().equals(name.get())).findFirst();

if (existingWindow.isPresent()) {
existingWindow.get().requestFocus();
} else {
AiChatWindow aiChatWindow = new AiChatWindow(
this,
dialogService,
aiPreferences,
filePreferences,
taskExecutor
);

aiChatWindow.setOnCloseRequest(event ->
stateManager.getAiChatWindows().remove(aiChatWindow)
);

stateManager.getAiChatWindows().add(aiChatWindow);
dialogService.showCustomWindow(aiChatWindow);
aiChatWindow.setChat(name, chatHistory, bibDatabaseContext, entries);
aiChatWindow.requestFocus();
}
}

@Override
public void close() {
shutdownSignal.set(true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@
import java.util.List;
import java.util.Map;

import org.jabref.gui.DialogService;
import org.jabref.logic.ai.chatting.chathistory.ChatHistoryStorage;
import org.jabref.logic.ai.util.ErrorMessage;
import org.jabref.logic.ai.util.MVStoreBase;
import org.jabref.logic.l10n.Localization;
import org.jabref.logic.util.NotificationService;

import dev.langchain4j.data.message.AiMessage;
import dev.langchain4j.data.message.ChatMessage;
Expand Down Expand Up @@ -65,7 +65,7 @@ public ChatMessage toLangchainMessage() {
}
}

public MVStoreChatHistoryStorage(Path path, DialogService dialogService) {
public MVStoreChatHistoryStorage(Path path, NotificationService dialogService) {
super(path, dialogService);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@
import java.util.stream.IntStream;
import java.util.stream.Stream;

import org.jabref.gui.DialogService;
import org.jabref.logic.ai.util.MVStoreBase;
import org.jabref.logic.l10n.Localization;
import org.jabref.logic.util.NotificationService;

import dev.langchain4j.data.document.Metadata;
import dev.langchain4j.data.embedding.Embedding;
Expand Down Expand Up @@ -51,7 +51,7 @@ private record EmbeddingRecord(@Nullable String file, String content, float[] em

private final Map<String, EmbeddingRecord> embeddingsMap;

public MVStoreEmbeddingStore(Path path, DialogService dialogService) {
public MVStoreEmbeddingStore(Path path, NotificationService dialogService) {
super(path, dialogService);

this.embeddingsMap = this.mvStore.openMap(EMBEDDINGS_MAP_NAME);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;

import org.jabref.gui.DialogService;
import org.jabref.logic.l10n.Localization;
import org.jabref.logic.util.NotificationService;
import org.jabref.logic.util.TaskExecutor;
import org.jabref.preferences.ai.AiPreferences;

Expand All @@ -31,7 +31,7 @@ public class JabRefEmbeddingModel implements EmbeddingModel, AutoCloseable {
private static final Logger LOGGER = LoggerFactory.getLogger(JabRefEmbeddingModel.class);

private final AiPreferences aiPreferences;
private final DialogService dialogService;
private final NotificationService notificationService;
private final TaskExecutor taskExecutor;

private final ExecutorService executorService = Executors.newCachedThreadPool(
Expand All @@ -50,9 +50,9 @@ public static class EmbeddingModelBuildingErrorEvent { }
// Empty if there is no error.
private String errorWhileBuildingModel = "";

public JabRefEmbeddingModel(AiPreferences aiPreferences, DialogService dialogService, TaskExecutor taskExecutor) {
public JabRefEmbeddingModel(AiPreferences aiPreferences, NotificationService notificationService, TaskExecutor taskExecutor) {
this.aiPreferences = aiPreferences;
this.dialogService = dialogService;
this.notificationService = notificationService;
this.taskExecutor = taskExecutor;

startRebuildingTask();
Expand All @@ -79,7 +79,7 @@ public void startRebuildingTask() {
})
.onFailure(e -> {
LOGGER.error("An error occurred while building the embedding model", e);
dialogService.notify(Localization.lang("An error occurred while building the embedding model"));
notificationService.notify(Localization.lang("An error occurred while building the embedding model"));
errorWhileBuildingModel = e.getMessage();
eventBus.post(new EmbeddingModelBuildingErrorEvent());
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
import java.util.Map;
import java.util.Optional;

import org.jabref.gui.DialogService;
import org.jabref.logic.ai.ingestion.FullyIngestedDocumentsTracker;
import org.jabref.logic.ai.util.MVStoreBase;
import org.jabref.logic.l10n.Localization;
import org.jabref.logic.util.NotificationService;

/**
* This class is responsible for recording the information about which documents (or documents) have been fully ingested.
Expand All @@ -24,7 +24,7 @@ public class MVStoreFullyIngestedDocumentsTracker extends MVStoreBase implements
// it doesn't mean the document is fully ingested.
private final Map<String, Long> ingestedMap;

public MVStoreFullyIngestedDocumentsTracker(Path path, DialogService dialogService) {
public MVStoreFullyIngestedDocumentsTracker(Path path, NotificationService dialogService) {
super(path, dialogService);

this.ingestedMap = this.mvStore.openMap(INGESTED_MAP_NAME);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@
import java.util.Map;
import java.util.Optional;

import org.jabref.gui.DialogService;
import org.jabref.logic.ai.summarization.SummariesStorage;
import org.jabref.logic.ai.summarization.Summary;
import org.jabref.logic.ai.util.MVStoreBase;
import org.jabref.logic.l10n.Localization;
import org.jabref.logic.util.NotificationService;

public class MVStoreSummariesStorage extends MVStoreBase implements SummariesStorage {
private static final String SUMMARIES_MAP_PREFIX = "summaries";

public MVStoreSummariesStorage(Path path, DialogService dialogService) {
public MVStoreSummariesStorage(Path path, NotificationService dialogService) {
super(path, dialogService);
}

Expand Down
4 changes: 2 additions & 2 deletions src/main/java/org/jabref/logic/ai/util/MVStoreBase.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import java.nio.file.Files;
import java.nio.file.Path;

import org.jabref.gui.DialogService;
import org.jabref.logic.util.NotificationService;

import jakarta.annotation.Nullable;
import org.h2.mvstore.MVStore;
Expand All @@ -15,7 +15,7 @@ public abstract class MVStoreBase implements AutoCloseable {

protected final MVStore mvStore;

public MVStoreBase(Path path, DialogService dialogService) {
public MVStoreBase(Path path, NotificationService dialogService) {
@Nullable Path mvStorePath = path;

try {
Expand Down
10 changes: 10 additions & 0 deletions src/main/java/org/jabref/logic/util/NotificationService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package org.jabref.logic.util;

public interface NotificationService {
/**
* Notify the user in a non-blocking way (i.e., in form of toast in a snackbar).
*
* @param message the message to show.
*/
void notify(String message);
}

0 comments on commit 2bc0c63

Please sign in to comment.