From 3f73beb334509f41f94ccc801eb376e7a6722c1f Mon Sep 17 00:00:00 2001 From: InAnYan Date: Sun, 8 Sep 2024 11:23:00 +0300 Subject: [PATCH 1/3] Remove unnecessary AI dependencies --- src/main/java/org/jabref/gui/LibraryTab.java | 5 ----- src/main/java/org/jabref/gui/entryeditor/EntryEditor.java | 2 -- .../java/org/jabref/gui/preferences/ai/AiTabViewModel.java | 4 ---- src/main/java/org/jabref/preferences/ai/AiPreferences.java | 4 ---- 4 files changed, 15 deletions(-) diff --git a/src/main/java/org/jabref/gui/LibraryTab.java b/src/main/java/org/jabref/gui/LibraryTab.java index ecfaa501e70..43b4f6fdd87 100644 --- a/src/main/java/org/jabref/gui/LibraryTab.java +++ b/src/main/java/org/jabref/gui/LibraryTab.java @@ -118,7 +118,6 @@ private enum PanelMode { MAIN_TABLE, MAIN_TABLE_AND_ENTRY_EDITOR } private final CountingUndoManager undoManager; private final DialogService dialogService; private final PreferencesService preferencesService; - private final AiService aiService; private final FileUpdateMonitor fileUpdateMonitor; private final StateManager stateManager; private final BibEntryTypesManager entryTypesManager; @@ -174,7 +173,6 @@ private LibraryTab(BibDatabaseContext bibDatabaseContext, LibraryTabContainer tabContainer, DialogService dialogService, PreferencesService preferencesService, - AiService aiService, StateManager stateManager, FileUpdateMonitor fileUpdateMonitor, BibEntryTypesManager entryTypesManager, @@ -187,7 +185,6 @@ private LibraryTab(BibDatabaseContext bibDatabaseContext, this.undoManager = undoManager; this.dialogService = dialogService; this.preferencesService = Objects.requireNonNull(preferencesService); - this.aiService = Objects.requireNonNull(aiService); this.stateManager = Objects.requireNonNull(stateManager); this.fileUpdateMonitor = fileUpdateMonitor; this.entryTypesManager = entryTypesManager; @@ -1007,7 +1004,6 @@ public static LibraryTab createLibraryTab(BackgroundTask dataLoadi tabContainer, dialogService, preferencesService, - aiService, stateManager, fileUpdateMonitor, entryTypesManager, @@ -1043,7 +1039,6 @@ public static LibraryTab createLibraryTab(BibDatabaseContext databaseContext, tabContainer, dialogService, preferencesService, - aiService, stateManager, fileUpdateMonitor, entryTypesManager, diff --git a/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java b/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java index 67be8a5f908..fea4acffc45 100644 --- a/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java +++ b/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java @@ -63,7 +63,6 @@ import org.jabref.model.util.DirectoryMonitorManager; import org.jabref.model.util.FileUpdateMonitor; import org.jabref.preferences.PreferencesService; -import org.jabref.preferences.ai.AiApiKeyProvider; import com.airhacks.afterburner.views.ViewLoader; import com.tobiasdiez.easybind.EasyBind; @@ -111,7 +110,6 @@ public class EntryEditor extends BorderPane { @Inject private DialogService dialogService; @Inject private TaskExecutor taskExecutor; @Inject private PreferencesService preferencesService; - @Inject private AiApiKeyProvider aiApiKeyProvider; @Inject private StateManager stateManager; @Inject private ThemeManager themeManager; @Inject private FileUpdateMonitor fileMonitor; diff --git a/src/main/java/org/jabref/gui/preferences/ai/AiTabViewModel.java b/src/main/java/org/jabref/gui/preferences/ai/AiTabViewModel.java index b8bc2d4bc3f..b019c68ee9d 100644 --- a/src/main/java/org/jabref/gui/preferences/ai/AiTabViewModel.java +++ b/src/main/java/org/jabref/gui/preferences/ai/AiTabViewModel.java @@ -389,10 +389,6 @@ public BooleanProperty enableAi() { return enableAi; } - public boolean getEnableAi() { - return enableAi.get(); - } - public ReadOnlyListProperty aiProvidersProperty() { return aiProvidersList; } diff --git a/src/main/java/org/jabref/preferences/ai/AiPreferences.java b/src/main/java/org/jabref/preferences/ai/AiPreferences.java index a3ae3b2fefb..8f6d8574d82 100644 --- a/src/main/java/org/jabref/preferences/ai/AiPreferences.java +++ b/src/main/java/org/jabref/preferences/ai/AiPreferences.java @@ -17,8 +17,6 @@ import org.jabref.preferences.PreferencesService; public class AiPreferences { - private final PreferencesService preferencesService; - private final BooleanProperty enableAi; private final ObjectProperty aiProvider; @@ -63,8 +61,6 @@ public AiPreferences(PreferencesService preferencesService, int ragMaxResultsCount, double ragMinScore ) { - this.preferencesService = preferencesService; - this.enableAi = new SimpleBooleanProperty(enableAi); this.aiProvider = new SimpleObjectProperty<>(aiProvider); From 144b102afa2f9c258671e031761da4d9b04697cc Mon Sep 17 00:00:00 2001 From: InAnYan Date: Sun, 8 Sep 2024 11:38:25 +0300 Subject: [PATCH 2/3] Remove AiApiKeyProvider.java --- src/main/java/org/jabref/Launcher.java | 2 - src/main/java/org/jabref/gui/JabRefGUI.java | 2 - .../org/jabref/gui/preferences/ai/AiTab.java | 5 +- .../gui/preferences/ai/AiTabViewModel.java | 17 ++-- .../java/org/jabref/logic/ai/AiService.java | 4 +- .../model/JabRefChatLanguageModel.java | 14 ++-- .../model/JvmOpenAiChatLanguageModel.java | 5 +- .../jabref/preferences/JabRefPreferences.java | 35 +-------- .../preferences/PreferencesService.java | 8 -- .../preferences/ai/AiApiKeyProvider.java | 7 -- .../jabref/preferences/ai/AiPreferences.java | 78 ++++++++++++------- 11 files changed, 68 insertions(+), 109 deletions(-) delete mode 100644 src/main/java/org/jabref/preferences/ai/AiApiKeyProvider.java diff --git a/src/main/java/org/jabref/Launcher.java b/src/main/java/org/jabref/Launcher.java index 117c9fd5c1c..fa891388f98 100644 --- a/src/main/java/org/jabref/Launcher.java +++ b/src/main/java/org/jabref/Launcher.java @@ -36,7 +36,6 @@ import org.jabref.model.util.FileUpdateMonitor; import org.jabref.preferences.JabRefPreferences; import org.jabref.preferences.PreferencesService; -import org.jabref.preferences.ai.AiApiKeyProvider; import com.airhacks.afterburner.injection.Injector; import org.apache.commons.cli.ParseException; @@ -64,7 +63,6 @@ public static void main(String[] args) { // Initialize preferences final JabRefPreferences preferences = JabRefPreferences.getInstance(); Injector.setModelOrService(PreferencesService.class, preferences); - Injector.setModelOrService(AiApiKeyProvider.class, preferences); // Early exit in case another instance is already running if (!handleMultipleAppInstances(args, preferences.getRemotePreferences())) { diff --git a/src/main/java/org/jabref/gui/JabRefGUI.java b/src/main/java/org/jabref/gui/JabRefGUI.java index 3e09d3f5c39..159d8bf5bea 100644 --- a/src/main/java/org/jabref/gui/JabRefGUI.java +++ b/src/main/java/org/jabref/gui/JabRefGUI.java @@ -40,7 +40,6 @@ import org.jabref.model.util.FileUpdateMonitor; import org.jabref.preferences.GuiPreferences; import org.jabref.preferences.JabRefPreferences; -import org.jabref.preferences.ai.AiApiKeyProvider; import com.airhacks.afterburner.injection.Injector; import com.tobiasdiez.easybind.EasyBind; @@ -161,7 +160,6 @@ public void initialize() { preferencesService.getAiPreferences(), preferencesService.getFilePreferences(), preferencesService.getCitationKeyPatternPreferences(), - Injector.instantiateModelOrService(AiApiKeyProvider.class), dialogService, taskExecutor); Injector.setModelOrService(AiService.class, aiService); diff --git a/src/main/java/org/jabref/gui/preferences/ai/AiTab.java b/src/main/java/org/jabref/gui/preferences/ai/AiTab.java index daff2c9999e..700548a41b4 100644 --- a/src/main/java/org/jabref/gui/preferences/ai/AiTab.java +++ b/src/main/java/org/jabref/gui/preferences/ai/AiTab.java @@ -15,7 +15,6 @@ import org.jabref.gui.util.ViewModelListCellFactory; import org.jabref.logic.help.HelpFile; import org.jabref.logic.l10n.Localization; -import org.jabref.preferences.ai.AiApiKeyProvider; import org.jabref.preferences.ai.AiProvider; import org.jabref.preferences.ai.EmbeddingModel; @@ -30,8 +29,6 @@ public class AiTab extends AbstractPreferenceTabView implements PreferencesTab { private static final String HUGGING_FACE_CHAT_MODEL_PROMPT = "TinyLlama/TinyLlama_v1.1 (or any other model name)"; - @Inject private AiApiKeyProvider aiApiKeyProvider; - @FXML private CheckBox enableAi; @FXML private ComboBox aiProviderComboBox; @@ -73,7 +70,7 @@ public AiTab() { } public void initialize() { - this.viewModel = new AiTabViewModel(preferencesService, aiApiKeyProvider); + this.viewModel = new AiTabViewModel(preferencesService); enableAi.selectedProperty().bindBidirectional(viewModel.enableAi()); diff --git a/src/main/java/org/jabref/gui/preferences/ai/AiTabViewModel.java b/src/main/java/org/jabref/gui/preferences/ai/AiTabViewModel.java index b019c68ee9d..031803d9f73 100644 --- a/src/main/java/org/jabref/gui/preferences/ai/AiTabViewModel.java +++ b/src/main/java/org/jabref/gui/preferences/ai/AiTabViewModel.java @@ -24,7 +24,6 @@ import org.jabref.logic.util.LocalizedNumbers; import org.jabref.model.strings.StringUtil; import org.jabref.preferences.PreferencesService; -import org.jabref.preferences.ai.AiApiKeyProvider; import org.jabref.preferences.ai.AiPreferences; import org.jabref.preferences.ai.AiProvider; import org.jabref.preferences.ai.EmbeddingModel; @@ -83,7 +82,6 @@ public class AiTabViewModel implements PreferenceTabViewModel { private final BooleanProperty disableExpertSettings = new SimpleBooleanProperty(true); private final AiPreferences aiPreferences; - private final AiApiKeyProvider aiApiKeyProvider; private final Validator apiKeyValidator; private final Validator chatModelValidator; @@ -99,11 +97,10 @@ public class AiTabViewModel implements PreferenceTabViewModel { private final Validator ragMinScoreTypeValidator; private final Validator ragMinScoreRangeValidator; - public AiTabViewModel(PreferencesService preferencesService, AiApiKeyProvider aiApiKeyProvider) { + public AiTabViewModel(PreferencesService preferencesService) { this.oldLocale = Locale.getDefault(); this.aiPreferences = preferencesService.getAiPreferences(); - this.aiApiKeyProvider = aiApiKeyProvider; this.enableAi.addListener((observable, oldValue, newValue) -> { disableBasicSettings.set(!newValue); @@ -266,9 +263,9 @@ public AiTabViewModel(PreferencesService preferencesService, AiApiKeyProvider ai @Override public void setValues() { - openAiApiKey.setValue(aiApiKeyProvider.getApiKeyForAiProvider(AiProvider.OPEN_AI)); - mistralAiApiKey.setValue(aiApiKeyProvider.getApiKeyForAiProvider(AiProvider.MISTRAL_AI)); - huggingFaceApiKey.setValue(aiApiKeyProvider.getApiKeyForAiProvider(AiProvider.HUGGING_FACE)); + openAiApiKey.setValue(aiPreferences.getApiKeyForAiProvider(AiProvider.OPEN_AI)); + mistralAiApiKey.setValue(aiPreferences.getApiKeyForAiProvider(AiProvider.MISTRAL_AI)); + huggingFaceApiKey.setValue(aiPreferences.getApiKeyForAiProvider(AiProvider.HUGGING_FACE)); openAiApiBaseUrl.setValue(aiPreferences.getOpenAiApiBaseUrl()); mistralAiApiBaseUrl.setValue(aiPreferences.getMistralAiApiBaseUrl()); @@ -305,9 +302,9 @@ public void storeSettings() { aiPreferences.setMistralAiChatModel(mistralAiChatModel.get() == null ? "" : mistralAiChatModel.get()); aiPreferences.setHuggingFaceChatModel(huggingFaceChatModel.get() == null ? "" : huggingFaceChatModel.get()); - aiApiKeyProvider.storeAiApiKeyInKeyring(AiProvider.OPEN_AI, openAiApiKey.get() == null ? "" : openAiApiKey.get()); - aiApiKeyProvider.storeAiApiKeyInKeyring(AiProvider.MISTRAL_AI, mistralAiApiKey.get() == null ? "" : mistralAiApiKey.get()); - aiApiKeyProvider.storeAiApiKeyInKeyring(AiProvider.HUGGING_FACE, huggingFaceApiKey.get() == null ? "" : huggingFaceApiKey.get()); + aiPreferences.storeAiApiKeyInKeyring(AiProvider.OPEN_AI, openAiApiKey.get() == null ? "" : openAiApiKey.get()); + aiPreferences.storeAiApiKeyInKeyring(AiProvider.MISTRAL_AI, mistralAiApiKey.get() == null ? "" : mistralAiApiKey.get()); + aiPreferences.storeAiApiKeyInKeyring(AiProvider.HUGGING_FACE, huggingFaceApiKey.get() == null ? "" : huggingFaceApiKey.get()); // We notify in all cases without a real check if something was changed aiPreferences.apiKeyUpdated(); diff --git a/src/main/java/org/jabref/logic/ai/AiService.java b/src/main/java/org/jabref/logic/ai/AiService.java index 0503307df27..a33e254c722 100644 --- a/src/main/java/org/jabref/logic/ai/AiService.java +++ b/src/main/java/org/jabref/logic/ai/AiService.java @@ -28,7 +28,6 @@ import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntry; import org.jabref.preferences.FilePreferences; -import org.jabref.preferences.ai.AiApiKeyProvider; import org.jabref.preferences.ai.AiPreferences; import com.airhacks.afterburner.injection.Injector; @@ -79,7 +78,6 @@ public class AiService implements AutoCloseable { public AiService(AiPreferences aiPreferences, FilePreferences filePreferences, CitationKeyPatternPreferences citationKeyPatternPreferences, - AiApiKeyProvider aiApiKeyProvider, DialogService dialogService, TaskExecutor taskExecutor ) { @@ -88,7 +86,7 @@ public AiService(AiPreferences aiPreferences, this.dialogService = dialogService; this.taskExecutor = taskExecutor; - this.jabRefChatLanguageModel = new JabRefChatLanguageModel(aiPreferences, aiApiKeyProvider); + 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); diff --git a/src/main/java/org/jabref/logic/ai/chatting/model/JabRefChatLanguageModel.java b/src/main/java/org/jabref/logic/ai/chatting/model/JabRefChatLanguageModel.java index 8bd71112cb8..5e9c424e3ab 100644 --- a/src/main/java/org/jabref/logic/ai/chatting/model/JabRefChatLanguageModel.java +++ b/src/main/java/org/jabref/logic/ai/chatting/model/JabRefChatLanguageModel.java @@ -9,7 +9,6 @@ import org.jabref.logic.ai.chatting.AiChatLogic; import org.jabref.logic.l10n.Localization; -import org.jabref.preferences.ai.AiApiKeyProvider; import org.jabref.preferences.ai.AiPreferences; import com.google.common.util.concurrent.ThreadFactoryBuilder; @@ -24,13 +23,13 @@ /** * Wrapper around langchain4j chat language model. *

- * This class listens to preferences changes. + * Notice, that the real chat model is created lazily, when it's needed. This is done, so API key is fetched only, + * when user wants to chat with AI. */ public class JabRefChatLanguageModel implements ChatLanguageModel, AutoCloseable { private static final Duration CONNECTION_TIMEOUT = Duration.ofSeconds(5); private final AiPreferences aiPreferences; - private final AiApiKeyProvider apiKeyProvider; private final HttpClient httpClient; private final ExecutorService executorService = Executors.newSingleThreadExecutor( @@ -39,9 +38,8 @@ public class JabRefChatLanguageModel implements ChatLanguageModel, AutoCloseable private Optional langchainChatModel = Optional.empty(); - public JabRefChatLanguageModel(AiPreferences aiPreferences, AiApiKeyProvider apiKeyProvider) { + public JabRefChatLanguageModel(AiPreferences aiPreferences) { this.aiPreferences = aiPreferences; - this.apiKeyProvider = apiKeyProvider; this.httpClient = HttpClient.newBuilder().connectTimeout(CONNECTION_TIMEOUT).executor(executorService).build(); setupListeningToPreferencesChanges(); @@ -54,7 +52,7 @@ public JabRefChatLanguageModel(AiPreferences aiPreferences, AiApiKeyProvider api * and see {@link org.jabref.logic.ai.chatting.chathistory.ChatHistoryStorage}. */ private void rebuild() { - String apiKey = apiKeyProvider.getApiKeyForAiProvider(aiPreferences.getAiProvider()); + String apiKey = aiPreferences.getApiKeyForAiProvider(aiPreferences.getAiProvider()); if (!aiPreferences.getEnableAi() || apiKey.isEmpty()) { langchainChatModel = Optional.empty(); return; @@ -62,7 +60,7 @@ private void rebuild() { switch (aiPreferences.getAiProvider()) { case OPEN_AI -> { - langchainChatModel = Optional.of(new JvmOpenAiChatLanguageModel(aiPreferences, apiKeyProvider, httpClient)); + langchainChatModel = Optional.of(new JvmOpenAiChatLanguageModel(aiPreferences, httpClient)); } case MISTRAL_AI -> { @@ -118,7 +116,7 @@ public Response generate(List list) { if (langchainChatModel.isEmpty()) { if (!aiPreferences.getEnableAi()) { throw new RuntimeException(Localization.lang("In order to use AI chat, you need to enable chatting with attached PDF files in JabRef preferences (AI tab).")); - } else if (apiKeyProvider.getApiKeyForAiProvider(aiPreferences.getAiProvider()).isEmpty()) { + } else if (aiPreferences.getApiKeyForAiProvider(aiPreferences.getAiProvider()).isEmpty()) { throw new RuntimeException(Localization.lang("In order to use AI chat, set an API key inside JabRef preferences (AI tab).")); } else { rebuild(); diff --git a/src/main/java/org/jabref/logic/ai/chatting/model/JvmOpenAiChatLanguageModel.java b/src/main/java/org/jabref/logic/ai/chatting/model/JvmOpenAiChatLanguageModel.java index f38435aaf8b..06b029b6551 100644 --- a/src/main/java/org/jabref/logic/ai/chatting/model/JvmOpenAiChatLanguageModel.java +++ b/src/main/java/org/jabref/logic/ai/chatting/model/JvmOpenAiChatLanguageModel.java @@ -3,7 +3,6 @@ import java.net.http.HttpClient; import java.util.List; -import org.jabref.preferences.ai.AiApiKeyProvider; import org.jabref.preferences.ai.AiPreferences; import dev.langchain4j.data.message.AiMessage; @@ -30,11 +29,11 @@ public class JvmOpenAiChatLanguageModel implements ChatLanguageModel { private final ChatClient chatClient; - public JvmOpenAiChatLanguageModel(AiPreferences aiPreferences, AiApiKeyProvider aiApiKeyProvider, HttpClient httpClient) { + public JvmOpenAiChatLanguageModel(AiPreferences aiPreferences, HttpClient httpClient) { this.aiPreferences = aiPreferences; OpenAI openAI = OpenAI - .newBuilder(aiApiKeyProvider.getApiKeyForAiProvider(aiPreferences.getAiProvider())) + .newBuilder(aiPreferences.getApiKeyForAiProvider(aiPreferences.getAiProvider())) .httpClient(httpClient) .baseUrl(aiPreferences.getSelectedApiBaseUrl()) .build(); diff --git a/src/main/java/org/jabref/preferences/JabRefPreferences.java b/src/main/java/org/jabref/preferences/JabRefPreferences.java index 7a370e2dd49..9e069357f5c 100644 --- a/src/main/java/org/jabref/preferences/JabRefPreferences.java +++ b/src/main/java/org/jabref/preferences/JabRefPreferences.java @@ -127,7 +127,6 @@ import org.jabref.model.metadata.SelfContainedSaveOrder; import org.jabref.model.search.SearchFlags; import org.jabref.model.strings.StringUtil; -import org.jabref.preferences.ai.AiApiKeyProvider; import org.jabref.preferences.ai.AiPreferences; import org.jabref.preferences.ai.AiProvider; import org.jabref.preferences.ai.EmbeddingModel; @@ -155,7 +154,7 @@ */ @Singleton @Service -public class JabRefPreferences implements PreferencesService, AiApiKeyProvider { +public class JabRefPreferences implements PreferencesService { // Push to application preferences public static final String PUSH_EMACS_PATH = "emacsPath"; @@ -485,9 +484,6 @@ public class JabRefPreferences implements PreferencesService, AiApiKeyProvider { private static final String AI_RAG_MAX_RESULTS_COUNT = "aiRagMaxResultsCount"; private static final String AI_RAG_MIN_SCORE = "aiRagMinScore"; - private static final String KEYRING_AI_SERVICE = "org.jabref.ai"; - private static final String KEYRING_AI_SERVICE_ACCOUNT = "apiKey"; - private static final Logger LOGGER = LoggerFactory.getLogger(JabRefPreferences.class); private static final Preferences PREFS_NODE = Preferences.userRoot().node("/org/jabref"); @@ -2793,7 +2789,6 @@ public AiPreferences getAiPreferences() { boolean aiEnabled = getBoolean(AI_ENABLED); aiPreferences = new AiPreferences( - this, aiEnabled, AiProvider.valueOf(get(AI_PROVIDER)), get(AI_OPEN_AI_CHAT_MODEL), @@ -2838,34 +2833,6 @@ public AiPreferences getAiPreferences() { return aiPreferences; } - public String getApiKeyForAiProvider(AiProvider aiProvider) { - try (final Keyring keyring = Keyring.create()) { - return keyring.getPassword(KEYRING_AI_SERVICE, KEYRING_AI_SERVICE_ACCOUNT + "-" + aiProvider.name()); - } catch (PasswordAccessException e) { - LOGGER.debug("No API key stored for provider {}. Returning an empty string", aiProvider.getLabel()); - return ""; - } catch (Exception e) { - LOGGER.warn("JabRef could not open keyring for retrieving {} API token", aiProvider.getLabel(), e); - return ""; - } - } - - public void storeAiApiKeyInKeyring(AiProvider aiProvider, String newKey) { - try (final Keyring keyring = Keyring.create()) { - if (StringUtil.isNullOrEmpty(newKey)) { - try { - keyring.deletePassword(KEYRING_AI_SERVICE, KEYRING_AI_SERVICE_ACCOUNT + "-" + aiProvider.name()); - } catch (PasswordAccessException ex) { - LOGGER.debug("API key for provider {} not stored in keyring. JabRef does not store an empty key.", aiProvider.getLabel()); - } - } else { - keyring.setPassword(KEYRING_AI_SERVICE, KEYRING_AI_SERVICE_ACCOUNT + "-" + aiProvider.name(), newKey); - } - } catch (Exception e) { - LOGGER.warn("JabRef could not open keyring for storing {} API token", aiProvider.getLabel(), e); - } - } - //************************************************************************************************************* // Misc preferences //************************************************************************************************************* diff --git a/src/main/java/org/jabref/preferences/PreferencesService.java b/src/main/java/org/jabref/preferences/PreferencesService.java index f7aff7fc0b1..30e307c66e1 100644 --- a/src/main/java/org/jabref/preferences/PreferencesService.java +++ b/src/main/java/org/jabref/preferences/PreferencesService.java @@ -152,12 +152,4 @@ public interface PreferencesService { UnlinkedFilesDialogPreferences getUnlinkedFilesDialogPreferences(); AiPreferences getAiPreferences(); - - /** - * Retrieves the API key for the specified AI provider. - * - * @param provider the AI provider for which the API key is requested - * @return the API key for the specified AI provider, or empty string if no key is found - */ - String getApiKeyForAiProvider(AiProvider provider); } diff --git a/src/main/java/org/jabref/preferences/ai/AiApiKeyProvider.java b/src/main/java/org/jabref/preferences/ai/AiApiKeyProvider.java deleted file mode 100644 index cedec6b8606..00000000000 --- a/src/main/java/org/jabref/preferences/ai/AiApiKeyProvider.java +++ /dev/null @@ -1,7 +0,0 @@ -package org.jabref.preferences.ai; - -public interface AiApiKeyProvider { - String getApiKeyForAiProvider(AiProvider provider); - - void storeAiApiKeyInKeyring(AiProvider aiProvider, String newKey); -} diff --git a/src/main/java/org/jabref/preferences/ai/AiPreferences.java b/src/main/java/org/jabref/preferences/ai/AiPreferences.java index 8f6d8574d82..acbf25b19df 100644 --- a/src/main/java/org/jabref/preferences/ai/AiPreferences.java +++ b/src/main/java/org/jabref/preferences/ai/AiPreferences.java @@ -1,11 +1,14 @@ package org.jabref.preferences.ai; +import java.util.List; import java.util.Objects; +import javafx.beans.Observable; import javafx.beans.property.BooleanProperty; import javafx.beans.property.DoubleProperty; import javafx.beans.property.IntegerProperty; import javafx.beans.property.ObjectProperty; +import javafx.beans.property.Property; import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleDoubleProperty; import javafx.beans.property.SimpleIntegerProperty; @@ -14,9 +17,20 @@ import javafx.beans.property.StringProperty; import org.jabref.logic.ai.AiDefaultPreferences; +import org.jabref.model.strings.StringUtil; import org.jabref.preferences.PreferencesService; +import com.github.javakeyring.Keyring; +import com.github.javakeyring.PasswordAccessException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + public class AiPreferences { + private static final Logger LOGGER = LoggerFactory.getLogger(AiPreferences.class); + + private static final String KEYRING_AI_SERVICE = "org.jabref.ai"; + private static final String KEYRING_AI_SERVICE_ACCOUNT = "apiKey"; + private final BooleanProperty enableAi; private final ObjectProperty aiProvider; @@ -42,8 +56,7 @@ public class AiPreferences { private Runnable apiKeyChangeListener; - public AiPreferences(PreferencesService preferencesService, - boolean enableAi, + public AiPreferences(boolean enableAi, AiProvider aiProvider, String openAiChatModel, String mistralAiChatModel, @@ -85,6 +98,35 @@ public AiPreferences(PreferencesService preferencesService, this.ragMinScore = new SimpleDoubleProperty(ragMinScore); } + public String getApiKeyForAiProvider(AiProvider aiProvider) { + try (final Keyring keyring = Keyring.create()) { + return keyring.getPassword(KEYRING_AI_SERVICE, KEYRING_AI_SERVICE_ACCOUNT + "-" + aiProvider.name()); + } catch ( + PasswordAccessException e) { + LOGGER.debug("No API key stored for provider {}. Returning an empty string", aiProvider.getLabel()); + return ""; + } catch (Exception e) { + LOGGER.warn("JabRef could not open keyring for retrieving {} API token", aiProvider.getLabel(), e); + return ""; + } + } + + public void storeAiApiKeyInKeyring(AiProvider aiProvider, String newKey) { + try (final Keyring keyring = Keyring.create()) { + if (StringUtil.isNullOrEmpty(newKey)) { + try { + keyring.deletePassword(KEYRING_AI_SERVICE, KEYRING_AI_SERVICE_ACCOUNT + "-" + aiProvider.name()); + } catch (PasswordAccessException ex) { + LOGGER.debug("API key for provider {} not stored in keyring. JabRef does not store an empty key.", aiProvider.getLabel()); + } + } else { + keyring.setPassword(KEYRING_AI_SERVICE, KEYRING_AI_SERVICE_ACCOUNT + "-" + aiProvider.name(), newKey); + } + } catch (Exception e) { + LOGGER.warn("JabRef could not open keyring for storing {} API token", aiProvider.getLabel(), e); + } + } + public BooleanProperty enableAiProperty() { return enableAi; } @@ -351,43 +393,23 @@ public void addListenerToEmbeddingsParametersChange(Runnable runnable) { } public void addListenerToChatModels(Runnable runnable) { - openAiChatModel.addListener((observableValue, oldValue, newValue) -> { - if (!newValue.equals(oldValue)) { - runnable.run(); - } - }); - - mistralAiChatModel.addListener((observableValue, oldValue, newValue) -> { - if (!newValue.equals(oldValue)) { - runnable.run(); - } - }); + List> observables = List.of(openAiChatModel, mistralAiChatModel, huggingFaceChatModel); - huggingFaceChatModel.addListener((observableValue, oldValue, newValue) -> { + observables.forEach(obs -> obs.addListener((observableValue, oldValue, newValue) -> { if (!newValue.equals(oldValue)) { runnable.run(); } - }); + })); } public void addListenerToApiBaseUrls(Runnable runnable) { - openAiApiBaseUrl.addListener((observableValue, oldValue, newValue) -> { - if (!newValue.equals(oldValue)) { - runnable.run(); - } - }); + List> observables = List.of(openAiApiBaseUrl, mistralAiApiBaseUrl, huggingFaceApiBaseUrl); - mistralAiApiBaseUrl.addListener((observableValue, oldValue, newValue) -> { + observables.forEach(obs -> obs.addListener((observableValue, oldValue, newValue) -> { if (!newValue.equals(oldValue)) { runnable.run(); } - }); - - huggingFaceApiBaseUrl.addListener((observableValue, oldValue, newValue) -> { - if (!newValue.equals(oldValue)) { - runnable.run(); - } - }); + })); } public String getSelectedChatModel() { From 8fd6853c1e924017d09b14c0cc748126736add04 Mon Sep 17 00:00:00 2001 From: InAnYan Date: Sun, 8 Sep 2024 11:56:54 +0300 Subject: [PATCH 3/3] Fix checkers --- src/main/java/org/jabref/gui/preferences/ai/AiTab.java | 1 - src/main/java/org/jabref/preferences/PreferencesService.java | 1 - src/main/java/org/jabref/preferences/ai/AiPreferences.java | 2 -- 3 files changed, 4 deletions(-) diff --git a/src/main/java/org/jabref/gui/preferences/ai/AiTab.java b/src/main/java/org/jabref/gui/preferences/ai/AiTab.java index 700548a41b4..d557ef919c3 100644 --- a/src/main/java/org/jabref/gui/preferences/ai/AiTab.java +++ b/src/main/java/org/jabref/gui/preferences/ai/AiTab.java @@ -22,7 +22,6 @@ import com.dlsc.gemsfx.ResizableTextArea; import com.dlsc.unitfx.IntegerInputField; import de.saxsys.mvvmfx.utils.validation.visualization.ControlsFxVisualizer; -import jakarta.inject.Inject; import org.controlsfx.control.SearchableComboBox; import org.controlsfx.control.textfield.CustomPasswordField; diff --git a/src/main/java/org/jabref/preferences/PreferencesService.java b/src/main/java/org/jabref/preferences/PreferencesService.java index 30e307c66e1..ea7e99c99fd 100644 --- a/src/main/java/org/jabref/preferences/PreferencesService.java +++ b/src/main/java/org/jabref/preferences/PreferencesService.java @@ -34,7 +34,6 @@ import org.jabref.logic.xmp.XmpPreferences; import org.jabref.model.entry.BibEntryTypesManager; import org.jabref.preferences.ai.AiPreferences; -import org.jabref.preferences.ai.AiProvider; import org.jvnet.hk2.annotations.Contract; diff --git a/src/main/java/org/jabref/preferences/ai/AiPreferences.java b/src/main/java/org/jabref/preferences/ai/AiPreferences.java index acbf25b19df..53f13f210a1 100644 --- a/src/main/java/org/jabref/preferences/ai/AiPreferences.java +++ b/src/main/java/org/jabref/preferences/ai/AiPreferences.java @@ -3,7 +3,6 @@ import java.util.List; import java.util.Objects; -import javafx.beans.Observable; import javafx.beans.property.BooleanProperty; import javafx.beans.property.DoubleProperty; import javafx.beans.property.IntegerProperty; @@ -18,7 +17,6 @@ import org.jabref.logic.ai.AiDefaultPreferences; import org.jabref.model.strings.StringUtil; -import org.jabref.preferences.PreferencesService; import com.github.javakeyring.Keyring; import com.github.javakeyring.PasswordAccessException;