Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove unnecessary AI dependencies #11727

Merged
merged 3 commits into from
Sep 8, 2024
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: 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
5 changes: 0 additions & 5 deletions src/main/java/org/jabref/gui/LibraryTab.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -174,7 +173,6 @@ private LibraryTab(BibDatabaseContext bibDatabaseContext,
LibraryTabContainer tabContainer,
DialogService dialogService,
PreferencesService preferencesService,
AiService aiService,
StateManager stateManager,
FileUpdateMonitor fileUpdateMonitor,
BibEntryTypesManager entryTypesManager,
Expand All @@ -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;
Expand Down Expand Up @@ -1007,7 +1004,6 @@ public static LibraryTab createLibraryTab(BackgroundTask<ParserResult> dataLoadi
tabContainer,
dialogService,
preferencesService,
aiService,
stateManager,
fileUpdateMonitor,
entryTypesManager,
Expand Down Expand Up @@ -1043,7 +1039,6 @@ public static LibraryTab createLibraryTab(BibDatabaseContext databaseContext,
tabContainer,
dialogService,
preferencesService,
aiService,
stateManager,
fileUpdateMonitor,
entryTypesManager,
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
Loading