Skip to content

Commit

Permalink
Remove unnecessary AI dependencies (JabRef#11727)
Browse files Browse the repository at this point in the history
* Remove unnecessary AI dependencies

* Remove AiApiKeyProvider.java

* Fix checkers
  • Loading branch information
InAnYan authored Sep 8, 2024
1 parent 1389b14 commit 51f04a3
Show file tree
Hide file tree
Showing 12 changed files with 66 additions and 121 deletions.
2 changes: 0 additions & 2 deletions src/main/java/org/jabref/Launcher.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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())) {
Expand Down
2 changes: 0 additions & 2 deletions src/main/java/org/jabref/gui/JabRefGUI.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -161,7 +160,6 @@ public void initialize() {
preferencesService.getAiPreferences(),
preferencesService.getFilePreferences(),
preferencesService.getCitationKeyPatternPreferences(),
Injector.instantiateModelOrService(AiApiKeyProvider.class),
dialogService,
taskExecutor);
Injector.setModelOrService(AiService.class, aiService);
Expand Down
2 changes: 0 additions & 2 deletions src/main/java/org/jabref/gui/entryeditor/EntryEditor.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
6 changes: 1 addition & 5 deletions src/main/java/org/jabref/gui/preferences/ai/AiTab.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,19 @@
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;

import com.airhacks.afterburner.views.ViewLoader;
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;

public class AiTab extends AbstractPreferenceTabView<AiTabViewModel> 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<AiProvider> aiProviderComboBox;
Expand Down Expand Up @@ -73,7 +69,7 @@ public AiTab() {
}

public void initialize() {
this.viewModel = new AiTabViewModel(preferencesService, aiApiKeyProvider);
this.viewModel = new AiTabViewModel(preferencesService);

enableAi.selectedProperty().bindBidirectional(viewModel.enableAi());

Expand Down
21 changes: 7 additions & 14 deletions src/main/java/org/jabref/gui/preferences/ai/AiTabViewModel.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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);
Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -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();

Expand Down Expand Up @@ -389,10 +386,6 @@ public BooleanProperty enableAi() {
return enableAi;
}

public boolean getEnableAi() {
return enableAi.get();
}

public ReadOnlyListProperty<AiProvider> aiProvidersProperty() {
return aiProvidersList;
}
Expand Down
4 changes: 1 addition & 3 deletions src/main/java/org/jabref/logic/ai/AiService.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -79,7 +78,6 @@ public class AiService implements AutoCloseable {
public AiService(AiPreferences aiPreferences,
FilePreferences filePreferences,
CitationKeyPatternPreferences citationKeyPatternPreferences,
AiApiKeyProvider aiApiKeyProvider,
DialogService dialogService,
TaskExecutor taskExecutor
) {
Expand All @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -24,13 +23,13 @@
/**
* Wrapper around langchain4j chat language model.
* <p>
* 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(
Expand All @@ -39,9 +38,8 @@ public class JabRefChatLanguageModel implements ChatLanguageModel, AutoCloseable

private Optional<ChatLanguageModel> 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();
Expand All @@ -54,15 +52,15 @@ 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;
}

switch (aiPreferences.getAiProvider()) {
case OPEN_AI -> {
langchainChatModel = Optional.of(new JvmOpenAiChatLanguageModel(aiPreferences, apiKeyProvider, httpClient));
langchainChatModel = Optional.of(new JvmOpenAiChatLanguageModel(aiPreferences, httpClient));
}

case MISTRAL_AI -> {
Expand Down Expand Up @@ -118,7 +116,7 @@ public Response<AiMessage> generate(List<ChatMessage> 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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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();
Expand Down
35 changes: 1 addition & 34 deletions src/main/java/org/jabref/preferences/JabRefPreferences.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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";
Expand Down Expand Up @@ -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");

Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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
//*************************************************************************************************************
Expand Down
9 changes: 0 additions & 9 deletions src/main/java/org/jabref/preferences/PreferencesService.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -152,12 +151,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);
}
7 changes: 0 additions & 7 deletions src/main/java/org/jabref/preferences/ai/AiApiKeyProvider.java

This file was deleted.

Loading

0 comments on commit 51f04a3

Please sign in to comment.