From 3805661606b7757b7cd7fe54a5b3c82382a7a384 Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Fri, 11 Oct 2024 17:26:01 +0200 Subject: [PATCH 1/4] Refine PDF importers (#11910) * Refine PdfImporter hierarchy (and fix typo in class name) * Fix checkstyle * Strong typing also in PdfMergeMetadataImporter --- .../jabref/gui/fieldeditors/LinkedFileViewModel.java | 4 ++-- .../org/jabref/logic/importer/ImportFormatReader.java | 4 ++-- .../logic/importer/fileformat/PdfContentImporter.java | 5 +++-- .../fileformat/PdfEmbeddedBibFileImporter.java | 3 +-- .../logic/importer/fileformat/PdfGrobidImporter.java | 3 +-- .../jabref/logic/importer/fileformat/PdfImporter.java | 9 +++++++++ .../importer/fileformat/PdfMergeMetadataImporter.java | 11 +++++------ ...xtImporter.java => PdfVerbatimBibtexImporter.java} | 5 ++--- .../logic/importer/fileformat/PdfXmpImporter.java | 3 +-- ...erTest.java => PdfVerbatimBibtexImporterTest.java} | 10 +++++----- 10 files changed, 31 insertions(+), 26 deletions(-) create mode 100644 src/main/java/org/jabref/logic/importer/fileformat/PdfImporter.java rename src/main/java/org/jabref/logic/importer/fileformat/{PdfVerbatimBibTextImporter.java => PdfVerbatimBibtexImporter.java} (94%) rename src/test/java/org/jabref/logic/importer/fileformat/{PdfVerbatimBibTextImporterTest.java => PdfVerbatimBibtexImporterTest.java} (85%) diff --git a/src/main/java/org/jabref/gui/fieldeditors/LinkedFileViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/LinkedFileViewModel.java index 7d72252ada2..b1a407e40a1 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/LinkedFileViewModel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/LinkedFileViewModel.java @@ -40,7 +40,7 @@ import org.jabref.logic.importer.fileformat.PdfContentImporter; import org.jabref.logic.importer.fileformat.PdfEmbeddedBibFileImporter; import org.jabref.logic.importer.fileformat.PdfGrobidImporter; -import org.jabref.logic.importer.fileformat.PdfVerbatimBibTextImporter; +import org.jabref.logic.importer.fileformat.PdfVerbatimBibtexImporter; import org.jabref.logic.importer.fileformat.PdfXmpImporter; import org.jabref.logic.l10n.Localization; import org.jabref.logic.util.TaskExecutor; @@ -460,7 +460,7 @@ public void parsePdfMetadataAndShowMergeDialog() { MultiMergeEntriesView dialog = new MultiMergeEntriesView(preferences, taskExecutor); dialog.setTitle(Localization.lang("Merge PDF metadata")); dialog.addSource(Localization.lang("Entry"), entry); - dialog.addSource(Localization.lang("Verbatim"), wrapImporterToSupplier(new PdfVerbatimBibTextImporter(preferences.getImportFormatPreferences()), filePath)); + dialog.addSource(Localization.lang("Verbatim"), wrapImporterToSupplier(new PdfVerbatimBibtexImporter(preferences.getImportFormatPreferences()), filePath)); dialog.addSource(Localization.lang("Embedded"), wrapImporterToSupplier(new PdfEmbeddedBibFileImporter(preferences.getImportFormatPreferences()), filePath)); if (preferences.getGrobidPreferences().isGrobidEnabled()) { dialog.addSource("Grobid", wrapImporterToSupplier(new PdfGrobidImporter(preferences.getImportFormatPreferences()), filePath)); diff --git a/src/main/java/org/jabref/logic/importer/ImportFormatReader.java b/src/main/java/org/jabref/logic/importer/ImportFormatReader.java index cb963a46c57..068f6452071 100644 --- a/src/main/java/org/jabref/logic/importer/ImportFormatReader.java +++ b/src/main/java/org/jabref/logic/importer/ImportFormatReader.java @@ -28,7 +28,7 @@ import org.jabref.logic.importer.fileformat.PdfEmbeddedBibFileImporter; import org.jabref.logic.importer.fileformat.PdfGrobidImporter; import org.jabref.logic.importer.fileformat.PdfMergeMetadataImporter; -import org.jabref.logic.importer.fileformat.PdfVerbatimBibTextImporter; +import org.jabref.logic.importer.fileformat.PdfVerbatimBibtexImporter; import org.jabref.logic.importer.fileformat.PdfXmpImporter; import org.jabref.logic.importer.fileformat.RepecNepImporter; import org.jabref.logic.importer.fileformat.RisImporter; @@ -75,7 +75,7 @@ public void reset() { formats.add(new MsBibImporter()); formats.add(new OvidImporter()); formats.add(new PdfMergeMetadataImporter(importFormatPreferences)); - formats.add(new PdfVerbatimBibTextImporter(importFormatPreferences)); + formats.add(new PdfVerbatimBibtexImporter(importFormatPreferences)); formats.add(new PdfContentImporter()); formats.add(new PdfEmbeddedBibFileImporter(importFormatPreferences)); if (importFormatPreferences.grobidPreferences().isGrobidEnabled()) { diff --git a/src/main/java/org/jabref/logic/importer/fileformat/PdfContentImporter.java b/src/main/java/org/jabref/logic/importer/fileformat/PdfContentImporter.java index 5006eed92c9..b8a74ee790d 100644 --- a/src/main/java/org/jabref/logic/importer/fileformat/PdfContentImporter.java +++ b/src/main/java/org/jabref/logic/importer/fileformat/PdfContentImporter.java @@ -12,7 +12,6 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -import org.jabref.logic.importer.Importer; import org.jabref.logic.importer.ParserResult; import org.jabref.logic.l10n.Localization; import org.jabref.logic.os.OS; @@ -38,8 +37,10 @@ *

* In case one wants to have a list of {@link BibEntry} matching the bibliography of a PDF, * please see {@link BibliographyFromPdfImporter}. + *

+ * If several PDF importers should be tried, use {@link PdfMergeMetadataImporter}. */ -public class PdfContentImporter extends Importer { +public class PdfContentImporter extends PdfImporter { private static final Pattern YEAR_EXTRACT_PATTERN = Pattern.compile("\\d{4}"); diff --git a/src/main/java/org/jabref/logic/importer/fileformat/PdfEmbeddedBibFileImporter.java b/src/main/java/org/jabref/logic/importer/fileformat/PdfEmbeddedBibFileImporter.java index 0406488f8ba..93420b4065a 100644 --- a/src/main/java/org/jabref/logic/importer/fileformat/PdfEmbeddedBibFileImporter.java +++ b/src/main/java/org/jabref/logic/importer/fileformat/PdfEmbeddedBibFileImporter.java @@ -9,7 +9,6 @@ import java.util.Objects; import org.jabref.logic.importer.ImportFormatPreferences; -import org.jabref.logic.importer.Importer; import org.jabref.logic.importer.ParseException; import org.jabref.logic.importer.ParserResult; import org.jabref.logic.l10n.Localization; @@ -32,7 +31,7 @@ /** * PdfEmbeddedBibFileImporter imports an embedded Bib-File from the PDF. */ -public class PdfEmbeddedBibFileImporter extends Importer { +public class PdfEmbeddedBibFileImporter extends PdfImporter { private final BibtexParser bibtexParser; diff --git a/src/main/java/org/jabref/logic/importer/fileformat/PdfGrobidImporter.java b/src/main/java/org/jabref/logic/importer/fileformat/PdfGrobidImporter.java index 95ee3f4b6f8..105935b06c0 100644 --- a/src/main/java/org/jabref/logic/importer/fileformat/PdfGrobidImporter.java +++ b/src/main/java/org/jabref/logic/importer/fileformat/PdfGrobidImporter.java @@ -8,7 +8,6 @@ import java.util.Optional; import org.jabref.logic.importer.ImportFormatPreferences; -import org.jabref.logic.importer.Importer; import org.jabref.logic.importer.ParserResult; import org.jabref.logic.importer.util.GrobidService; import org.jabref.logic.l10n.Localization; @@ -20,7 +19,7 @@ /** * Wraps the GrobidService function to be used as an Importer. */ -public class PdfGrobidImporter extends Importer { +public class PdfGrobidImporter extends PdfImporter { private final GrobidService grobidService; private final ImportFormatPreferences importFormatPreferences; diff --git a/src/main/java/org/jabref/logic/importer/fileformat/PdfImporter.java b/src/main/java/org/jabref/logic/importer/fileformat/PdfImporter.java new file mode 100644 index 00000000000..b36975e7d89 --- /dev/null +++ b/src/main/java/org/jabref/logic/importer/fileformat/PdfImporter.java @@ -0,0 +1,9 @@ +package org.jabref.logic.importer.fileformat; + +import org.jabref.logic.importer.Importer; + +/** + * Intermediate class to bundle all PdfImporters + */ +public abstract class PdfImporter extends Importer { +} diff --git a/src/main/java/org/jabref/logic/importer/fileformat/PdfMergeMetadataImporter.java b/src/main/java/org/jabref/logic/importer/fileformat/PdfMergeMetadataImporter.java index 197a99e543c..7ecf2fdd1ec 100644 --- a/src/main/java/org/jabref/logic/importer/fileformat/PdfMergeMetadataImporter.java +++ b/src/main/java/org/jabref/logic/importer/fileformat/PdfMergeMetadataImporter.java @@ -14,7 +14,6 @@ import org.jabref.logic.importer.EntryBasedFetcher; import org.jabref.logic.importer.FetcherException; import org.jabref.logic.importer.ImportFormatPreferences; -import org.jabref.logic.importer.Importer; import org.jabref.logic.importer.ParserResult; import org.jabref.logic.importer.fetcher.DoiFetcher; import org.jabref.logic.importer.fetcher.isbntobibtex.IsbnFetcher; @@ -32,23 +31,23 @@ import org.slf4j.LoggerFactory; /** - * Tries to import BibTeX data trying multiple PDF content importers and merging the results. + * Tries to import BibTeX data trying multiple {@Link PdfImporter}s and merging the results. * See {@Link org.jabref.logic.importer.fileformat.PdfMergeMetadataImporter#metadataImporters} for the list of importers used. * * After all importers are applied, this importer tries to fetch additional metadata for the entry using the DOI and ISBN. */ -public class PdfMergeMetadataImporter extends Importer { +public class PdfMergeMetadataImporter extends PdfImporter { private static final Logger LOGGER = LoggerFactory.getLogger(PdfMergeMetadataImporter.class); private final ImportFormatPreferences importFormatPreferences; - private final List metadataImporters; + private final List metadataImporters; public PdfMergeMetadataImporter(ImportFormatPreferences importFormatPreferences) { this.importFormatPreferences = importFormatPreferences; this.metadataImporters = new ArrayList<>(5); - this.metadataImporters.add(new PdfVerbatimBibTextImporter(importFormatPreferences)); + this.metadataImporters.add(new PdfVerbatimBibtexImporter(importFormatPreferences)); this.metadataImporters.add(new PdfEmbeddedBibFileImporter(importFormatPreferences)); if (importFormatPreferences.grobidPreferences().isGrobidEnabled()) { this.metadataImporters.add(new PdfGrobidImporter(importFormatPreferences)); @@ -80,7 +79,7 @@ public ParserResult importDatabase(String data) throws IOException { public ParserResult importDatabase(Path filePath) throws IOException { List candidates = new ArrayList<>(); - for (Importer metadataImporter : metadataImporters) { + for (PdfImporter metadataImporter : metadataImporters) { List extractedEntries = metadataImporter.importDatabase(filePath).getDatabase().getEntries(); if (extractedEntries.isEmpty()) { continue; diff --git a/src/main/java/org/jabref/logic/importer/fileformat/PdfVerbatimBibTextImporter.java b/src/main/java/org/jabref/logic/importer/fileformat/PdfVerbatimBibtexImporter.java similarity index 94% rename from src/main/java/org/jabref/logic/importer/fileformat/PdfVerbatimBibTextImporter.java rename to src/main/java/org/jabref/logic/importer/fileformat/PdfVerbatimBibtexImporter.java index 9848075eedf..ba95b28230e 100644 --- a/src/main/java/org/jabref/logic/importer/fileformat/PdfVerbatimBibTextImporter.java +++ b/src/main/java/org/jabref/logic/importer/fileformat/PdfVerbatimBibtexImporter.java @@ -8,7 +8,6 @@ import java.util.Objects; import org.jabref.logic.importer.ImportFormatPreferences; -import org.jabref.logic.importer.Importer; import org.jabref.logic.importer.ParseException; import org.jabref.logic.importer.ParserResult; import org.jabref.logic.l10n.Localization; @@ -24,11 +23,11 @@ /** * This importer imports a verbatim BibTeX entry from the first page of the PDF. */ -public class PdfVerbatimBibTextImporter extends Importer { +public class PdfVerbatimBibtexImporter extends PdfImporter { private final ImportFormatPreferences importFormatPreferences; - public PdfVerbatimBibTextImporter(ImportFormatPreferences importFormatPreferences) { + public PdfVerbatimBibtexImporter(ImportFormatPreferences importFormatPreferences) { this.importFormatPreferences = importFormatPreferences; } diff --git a/src/main/java/org/jabref/logic/importer/fileformat/PdfXmpImporter.java b/src/main/java/org/jabref/logic/importer/fileformat/PdfXmpImporter.java index f9398633b78..0e3a0944506 100644 --- a/src/main/java/org/jabref/logic/importer/fileformat/PdfXmpImporter.java +++ b/src/main/java/org/jabref/logic/importer/fileformat/PdfXmpImporter.java @@ -5,7 +5,6 @@ import java.nio.file.Path; import java.util.Objects; -import org.jabref.logic.importer.Importer; import org.jabref.logic.importer.ParserResult; import org.jabref.logic.l10n.Localization; import org.jabref.logic.util.StandardFileType; @@ -16,7 +15,7 @@ /** * Wraps the XMPUtility function to be used as an Importer. */ -public class PdfXmpImporter extends Importer { +public class PdfXmpImporter extends PdfImporter { private final XmpPreferences xmpPreferences; diff --git a/src/test/java/org/jabref/logic/importer/fileformat/PdfVerbatimBibTextImporterTest.java b/src/test/java/org/jabref/logic/importer/fileformat/PdfVerbatimBibtexImporterTest.java similarity index 85% rename from src/test/java/org/jabref/logic/importer/fileformat/PdfVerbatimBibTextImporterTest.java rename to src/test/java/org/jabref/logic/importer/fileformat/PdfVerbatimBibtexImporterTest.java index a5ddb3ec89b..d70d1695d4c 100644 --- a/src/test/java/org/jabref/logic/importer/fileformat/PdfVerbatimBibTextImporterTest.java +++ b/src/test/java/org/jabref/logic/importer/fileformat/PdfVerbatimBibtexImporterTest.java @@ -20,27 +20,27 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -class PdfVerbatimBibTextImporterTest { +class PdfVerbatimBibtexImporterTest { - private PdfVerbatimBibTextImporter importer; + private PdfVerbatimBibtexImporter importer; @BeforeEach void setUp() { ImportFormatPreferences importFormatPreferences = mock(ImportFormatPreferences.class, Answers.RETURNS_DEEP_STUBS); when(importFormatPreferences.fieldPreferences().getNonWrappableFields()).thenReturn(FXCollections.emptyObservableList()); - importer = new PdfVerbatimBibTextImporter(importFormatPreferences); + importer = new PdfVerbatimBibtexImporter(importFormatPreferences); } @Test void doesNotHandleEncryptedPdfs() throws Exception { - Path file = Path.of(PdfVerbatimBibTextImporter.class.getResource("/pdfs/encrypted.pdf").toURI()); + Path file = Path.of(PdfVerbatimBibtexImporter.class.getResource("/pdfs/encrypted.pdf").toURI()); List result = importer.importDatabase(file).getDatabase().getEntries(); assertEquals(Collections.emptyList(), result); } @Test void importTwiceWorksAsExpected() throws Exception { - Path file = Path.of(PdfVerbatimBibTextImporterTest.class.getResource("mixedMetadata.pdf").toURI()); + Path file = Path.of(PdfVerbatimBibtexImporterTest.class.getResource("mixedMetadata.pdf").toURI()); List result = importer.importDatabase(file).getDatabase().getEntries(); BibEntry expected = new BibEntry(StandardEntryType.Article); From 43426602f616ed4b6914db504e12c1f0e58cb1e3 Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Fri, 11 Oct 2024 23:59:37 +0200 Subject: [PATCH 2/4] Fix typo for CLI usage help (#11917) --- src/main/java/org/jabref/cli/JabRefCLI.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/jabref/cli/JabRefCLI.java b/src/main/java/org/jabref/cli/JabRefCLI.java index 0cd74a0bba4..e79533470a5 100644 --- a/src/main/java/org/jabref/cli/JabRefCLI.java +++ b/src/main/java/org/jabref/cli/JabRefCLI.java @@ -211,7 +211,7 @@ private static Options getOptions() { .longOpt("importBibtex") .desc("%s: '%s'".formatted(Localization.lang("Import BibTeX"), "-ib @article{entry}")) .hasArg() - .argName("BIBTEXT_STRING") + .argName("BIBTEX_STRING") .build()); options.addOption(Option From b646c7d089f6f88d6cb29651ceda6d36136845bc Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Sat, 12 Oct 2024 00:36:41 +0200 Subject: [PATCH 3/4] Add link to "twin" interface for file type interfaces (#11918) --- .../java/org/jabref/gui/externalfiletype/ExternalFileType.java | 3 +++ src/main/java/org/jabref/logic/util/FileType.java | 2 ++ 2 files changed, 5 insertions(+) diff --git a/src/main/java/org/jabref/gui/externalfiletype/ExternalFileType.java b/src/main/java/org/jabref/gui/externalfiletype/ExternalFileType.java index 0a5a44259cf..fff38ae4daa 100644 --- a/src/main/java/org/jabref/gui/externalfiletype/ExternalFileType.java +++ b/src/main/java/org/jabref/gui/externalfiletype/ExternalFileType.java @@ -4,6 +4,9 @@ import org.jabref.model.entry.field.Field; import org.jabref.model.entry.field.FieldFactory; +/** + * "Twin" interface: {@link org.jabref.logic.util.FileType} + */ public interface ExternalFileType { String getName(); diff --git a/src/main/java/org/jabref/logic/util/FileType.java b/src/main/java/org/jabref/logic/util/FileType.java index c054d54d7bc..1d5c1f5821d 100644 --- a/src/main/java/org/jabref/logic/util/FileType.java +++ b/src/main/java/org/jabref/logic/util/FileType.java @@ -5,6 +5,8 @@ /** * Interface for {@link StandardFileType} which allows us to extend the underlying enum with own filetypes for custom exporters + * + * "Twin" interface: {@link org.jabref.gui.externalfiletype.ExternalFileType} */ public interface FileType { From 1b7cd413306652fd41c218f5161790b842db68e8 Mon Sep 17 00:00:00 2001 From: Ruslan Date: Sat, 12 Oct 2024 14:09:07 +0300 Subject: [PATCH 4/4] Improve default AI preferences (#11935) --- .../privacynotice/PrivacyNoticeComponent.java | 6 +- .../gui/preferences/ai/AiTabViewModel.java | 17 +-- .../jabref/logic/ai/AiDefaultPreferences.java | 108 ++++++++++-------- .../org/jabref/logic/ai/AiPreferences.java | 2 +- .../jabref/logic/ai/chatting/AiChatLogic.java | 3 +- .../preferences/JabRefCliPreferences.java | 18 +-- .../java/org/jabref/model/ai/AiProvider.java | 22 +++- 7 files changed, 94 insertions(+), 82 deletions(-) diff --git a/src/main/java/org/jabref/gui/ai/components/privacynotice/PrivacyNoticeComponent.java b/src/main/java/org/jabref/gui/ai/components/privacynotice/PrivacyNoticeComponent.java index 9681379d60d..12dad69d580 100644 --- a/src/main/java/org/jabref/gui/ai/components/privacynotice/PrivacyNoticeComponent.java +++ b/src/main/java/org/jabref/gui/ai/components/privacynotice/PrivacyNoticeComponent.java @@ -11,7 +11,6 @@ import org.jabref.gui.DialogService; import org.jabref.gui.desktop.os.NativeDesktop; import org.jabref.gui.frame.ExternalApplicationsPreferences; -import org.jabref.logic.ai.AiDefaultPreferences; import org.jabref.logic.ai.AiPreferences; import org.jabref.model.ai.AiProvider; @@ -72,12 +71,11 @@ private void initPrivacyHyperlink(TextFlow textFlow, AiProvider aiProvider) { text.setText(replacedText); text.wrappingWidthProperty().bind(this.widthProperty()); - String link = AiDefaultPreferences.PROVIDERS_PRIVACY_POLICIES.get(aiProvider); - Hyperlink hyperlink = new Hyperlink(link); + Hyperlink hyperlink = new Hyperlink(aiProvider.getApiUrl()); hyperlink.setWrapText(true); hyperlink.setFont(text.getFont()); hyperlink.setOnAction(event -> { - openBrowser(link); + openBrowser(aiProvider.getApiUrl()); }); textFlow.getChildren().add(hyperlink); 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 877bf17b499..c1c4ef5f6a5 100644 --- a/src/main/java/org/jabref/gui/preferences/ai/AiTabViewModel.java +++ b/src/main/java/org/jabref/gui/preferences/ai/AiTabViewModel.java @@ -2,7 +2,6 @@ import java.util.List; import java.util.Locale; -import java.util.Map; import java.util.Objects; import javafx.beans.property.BooleanProperty; @@ -119,7 +118,7 @@ public AiTabViewModel(CliPreferences preferences) { ); this.selectedAiProvider.addListener((observable, oldValue, newValue) -> { - List models = AiDefaultPreferences.AVAILABLE_CHAT_MODELS.get(newValue); + List models = AiDefaultPreferences.getAvailableModels(newValue); // When we setAll on Hugging Face, models are empty, and currentChatModel become null. // It becomes null beause currentChatModel is binded to combobox, and this combobox becomes empty. @@ -186,14 +185,7 @@ public AiTabViewModel(CliPreferences preferences) { case HUGGING_FACE -> huggingFaceChatModel.set(newValue); } - Map modelContextWindows = AiDefaultPreferences.CONTEXT_WINDOW_SIZES.get(selectedAiProvider.get()); - - if (modelContextWindows == null) { - contextWindowSize.set(AiDefaultPreferences.CONTEXT_WINDOW_SIZE); - return; - } - - contextWindowSize.set(modelContextWindows.getOrDefault(newValue, AiDefaultPreferences.CONTEXT_WINDOW_SIZE)); + contextWindowSize.set(AiDefaultPreferences.getContextWindowSize(selectedAiProvider.get(), newValue)); }); this.currentApiKey.addListener((observable, oldValue, newValue) -> { @@ -356,13 +348,12 @@ public void storeSettings() { } public void resetExpertSettings() { - String resetApiBaseUrl = AiDefaultPreferences.PROVIDERS_API_URLS.get(selectedAiProvider.get()); + String resetApiBaseUrl = selectedAiProvider.get().getApiUrl(); currentApiBaseUrl.set(resetApiBaseUrl); instruction.set(AiDefaultPreferences.SYSTEM_MESSAGE); - int resetContextWindowSize = AiDefaultPreferences.CONTEXT_WINDOW_SIZES.getOrDefault(selectedAiProvider.get(), Map.of()).getOrDefault(currentChatModel.get(), 0); - contextWindowSize.set(resetContextWindowSize); + contextWindowSize.set(AiDefaultPreferences.getContextWindowSize(selectedAiProvider.get(), currentChatModel.get())); temperature.set(LocalizedNumbers.doubleToString(AiDefaultPreferences.TEMPERATURE)); documentSplitterChunkSize.set(AiDefaultPreferences.DOCUMENT_SPLITTER_CHUNK_SIZE); diff --git a/src/main/java/org/jabref/logic/ai/AiDefaultPreferences.java b/src/main/java/org/jabref/logic/ai/AiDefaultPreferences.java index 2228de6eb84..e83c1a084b5 100644 --- a/src/main/java/org/jabref/logic/ai/AiDefaultPreferences.java +++ b/src/main/java/org/jabref/logic/ai/AiDefaultPreferences.java @@ -1,5 +1,6 @@ package org.jabref.logic.ai; +import java.util.Arrays; import java.util.List; import java.util.Map; @@ -7,49 +8,49 @@ import org.jabref.model.ai.EmbeddingModel; public class AiDefaultPreferences { - public static final Map> AVAILABLE_CHAT_MODELS = Map.of( - AiProvider.OPEN_AI, List.of("gpt-4o-mini", "gpt-4o", "gpt-4", "gpt-4-turbo", "gpt-3.5-turbo"), - // "mistral" and "mixtral" are not language mistakes. - AiProvider.MISTRAL_AI, List.of("open-mistral-nemo", "open-mistral-7b", "open-mixtral-8x7b", "open-mixtral-8x22b", "mistral-large-latest"), - AiProvider.GEMINI, List.of("gemini-1.5-flash", "gemini-1.5-pro", "gemini-1.0-pro"), - AiProvider.HUGGING_FACE, List.of() - ); + public enum PredefinedChatModel { + GPT_4O_MINI(AiProvider.OPEN_AI, "gpt-4o-mini", 128000), + GPT_4O(AiProvider.OPEN_AI, "gpt-4o", 128000), + GPT_4(AiProvider.OPEN_AI, "gpt-4", 8192), + GPT_4_TURBO(AiProvider.OPEN_AI, "gpt-4-turbo", 128000), + GPT_3_5_TURBO(AiProvider.OPEN_AI, "gpt-3.5-turbo", 16385), + OPEN_MISTRAL_NEMO(AiProvider.MISTRAL_AI, "open-mistral-nemo", 128000), + OPEN_MISTRAL_7B(AiProvider.MISTRAL_AI, "open-mistral-7b", 32000), + // "mixtral" is not a typo. + OPEN_MIXTRAL_8X7B(AiProvider.MISTRAL_AI, "open-mixtral-8x7b", 32000), + OPEN_MIXTRAL_8X22B(AiProvider.MISTRAL_AI, "open-mixtral-8x22b", 64000), + GEMINI_1_5_FLASH(AiProvider.GEMINI, "gemini-1.5-flash", 1048576), + GEMINI_1_5_PRO(AiProvider.GEMINI, "gemini-1.5-pro", 2097152), + GEMINI_1_0_PRO(AiProvider.GEMINI, "gemini-1.0-pro", 32000), + // Dummy variant for Hugging Face models. + HUGGING_FACE(AiProvider.HUGGING_FACE, "", 0); - public static final Map PROVIDERS_PRIVACY_POLICIES = Map.of( - AiProvider.OPEN_AI, "https://openai.com/policies/privacy-policy/", - AiProvider.MISTRAL_AI, "https://mistral.ai/terms/#privacy-policy", - AiProvider.GEMINI, "https://ai.google.dev/gemini-api/terms", - AiProvider.HUGGING_FACE, "https://huggingface.co/privacy" - ); + private final AiProvider aiProvider; + private final String name; + private final int contextWindowSize; - public static final Map PROVIDERS_API_URLS = Map.of( - AiProvider.OPEN_AI, "https://api.openai.com/v1", - AiProvider.MISTRAL_AI, "https://api.mistral.ai/v1", - AiProvider.GEMINI, "https://generativelanguage.googleapis.com/v1beta/", - AiProvider.HUGGING_FACE, "https://huggingface.co/api" - ); + PredefinedChatModel(AiProvider aiProvider, String name, int contextWindowSize) { + this.aiProvider = aiProvider; + this.name = name; + this.contextWindowSize = contextWindowSize; + } - public static final Map> CONTEXT_WINDOW_SIZES = Map.of( - AiProvider.OPEN_AI, Map.of( - "gpt-4o-mini", 128000, - "gpt-4o", 128000, - "gpt-4", 8192, - "gpt-4-turbo", 128000, - "gpt-3.5-turbo", 16385 - ), - AiProvider.MISTRAL_AI, Map.of( - "mistral-large-latest", 128000, - "open-mistral-nemo", 128000, - "open-mistral-7b", 32000, - "open-mixtral-8x7b", 32000, - "open-mixtral-8x22b", 64000 - ), - AiProvider.GEMINI, Map.of( - "gemini-1.5-flash", 1048576, - "gemini-1.5-pro", 2097152, - "gemini-1.0-pro", 32000 - ) - ); + public AiProvider getAiProvider() { + return aiProvider; + } + + public String getName() { + return name; + } + + public int getContextWindowSize() { + return contextWindowSize; + } + + public String toString() { + return aiProvider.toString() + " " + name; + } + } public static final boolean ENABLE_CHAT = false; public static final boolean AUTO_GENERATE_EMBEDDINGS = false; @@ -57,11 +58,11 @@ public class AiDefaultPreferences { public static final AiProvider PROVIDER = AiProvider.OPEN_AI; - public static final Map CHAT_MODELS = Map.of( - AiProvider.OPEN_AI, "gpt-4o-mini", - AiProvider.MISTRAL_AI, "open-mixtral-8x22b", - AiProvider.GEMINI, "gemini-1.5-flash", - AiProvider.HUGGING_FACE, "" + public static final Map CHAT_MODELS = Map.of( + AiProvider.OPEN_AI, PredefinedChatModel.GPT_4O_MINI, + AiProvider.MISTRAL_AI, PredefinedChatModel.OPEN_MIXTRAL_8X22B, + AiProvider.GEMINI, PredefinedChatModel.GEMINI_1_5_FLASH, + AiProvider.HUGGING_FACE, PredefinedChatModel.HUGGING_FACE ); public static final boolean CUSTOMIZE_SETTINGS = false; @@ -74,9 +75,20 @@ public class AiDefaultPreferences { public static final int RAG_MAX_RESULTS_COUNT = 10; public static final double RAG_MIN_SCORE = 0.3; - public static final int CONTEXT_WINDOW_SIZE = 8196; + public static final int FALLBACK_CONTEXT_WINDOW_SIZE = 8196; + + public static List getAvailableModels(AiProvider aiProvider) { + return Arrays.stream(AiDefaultPreferences.PredefinedChatModel.values()) + .filter(model -> model.getAiProvider() == aiProvider) + .map(AiDefaultPreferences.PredefinedChatModel::getName) + .toList(); + } - public static int getContextWindowSize(AiProvider aiProvider, String model) { - return CONTEXT_WINDOW_SIZES.getOrDefault(aiProvider, Map.of()).getOrDefault(model, 0); + public static int getContextWindowSize(AiProvider aiProvider, String modelName) { + return Arrays.stream(AiDefaultPreferences.PredefinedChatModel.values()) + .filter(model -> model.getAiProvider() == aiProvider && model.getName().equals(modelName)) + .map(AiDefaultPreferences.PredefinedChatModel::getContextWindowSize) + .findFirst() + .orElse(AiDefaultPreferences.FALLBACK_CONTEXT_WINDOW_SIZE); } } diff --git a/src/main/java/org/jabref/logic/ai/AiPreferences.java b/src/main/java/org/jabref/logic/ai/AiPreferences.java index f675ab37a35..3a07a02e70c 100644 --- a/src/main/java/org/jabref/logic/ai/AiPreferences.java +++ b/src/main/java/org/jabref/logic/ai/AiPreferences.java @@ -497,7 +497,7 @@ public String getSelectedApiBaseUrl() { geminiApiBaseUrl.get(); }; } else { - return AiDefaultPreferences.PROVIDERS_API_URLS.get(aiProvider.get()); + return aiProvider.get().getApiUrl(); } } diff --git a/src/main/java/org/jabref/logic/ai/chatting/AiChatLogic.java b/src/main/java/org/jabref/logic/ai/chatting/AiChatLogic.java index 8a802847d5b..f079b093604 100644 --- a/src/main/java/org/jabref/logic/ai/chatting/AiChatLogic.java +++ b/src/main/java/org/jabref/logic/ai/chatting/AiChatLogic.java @@ -8,7 +8,6 @@ import javafx.collections.ListChangeListener; import javafx.collections.ObservableList; -import org.jabref.logic.ai.AiDefaultPreferences; import org.jabref.logic.ai.AiPreferences; import org.jabref.logic.ai.ingestion.FileEmbeddingsManager; import org.jabref.logic.ai.util.ErrorMessage; @@ -160,7 +159,7 @@ public AiMessage execute(UserMessage message) { // Message will be automatically added to ChatMemory through ConversationalRetrievalChain. LOGGER.info("Sending message to AI provider ({}) for answering in {}: {}", - AiDefaultPreferences.PROVIDERS_API_URLS.get(aiPreferences.getAiProvider()), + aiPreferences.getAiProvider().getApiUrl(), name.get(), message.singleText()); diff --git a/src/main/java/org/jabref/logic/preferences/JabRefCliPreferences.java b/src/main/java/org/jabref/logic/preferences/JabRefCliPreferences.java index 853d6359fa2..a15d1014c87 100644 --- a/src/main/java/org/jabref/logic/preferences/JabRefCliPreferences.java +++ b/src/main/java/org/jabref/logic/preferences/JabRefCliPreferences.java @@ -628,19 +628,19 @@ protected JabRefCliPreferences() { defaults.put(AI_AUTO_GENERATE_EMBEDDINGS, AiDefaultPreferences.AUTO_GENERATE_EMBEDDINGS); defaults.put(AI_AUTO_GENERATE_SUMMARIES, AiDefaultPreferences.AUTO_GENERATE_SUMMARIES); defaults.put(AI_PROVIDER, AiDefaultPreferences.PROVIDER.name()); - defaults.put(AI_OPEN_AI_CHAT_MODEL, AiDefaultPreferences.CHAT_MODELS.get(AiProvider.OPEN_AI)); - defaults.put(AI_MISTRAL_AI_CHAT_MODEL, AiDefaultPreferences.CHAT_MODELS.get(AiProvider.MISTRAL_AI)); - defaults.put(AI_GEMINI_CHAT_MODEL, AiDefaultPreferences.CHAT_MODELS.get(AiProvider.GEMINI)); - defaults.put(AI_HUGGING_FACE_CHAT_MODEL, AiDefaultPreferences.CHAT_MODELS.get(AiProvider.HUGGING_FACE)); + defaults.put(AI_OPEN_AI_CHAT_MODEL, AiDefaultPreferences.CHAT_MODELS.get(AiProvider.OPEN_AI).getName()); + defaults.put(AI_MISTRAL_AI_CHAT_MODEL, AiDefaultPreferences.CHAT_MODELS.get(AiProvider.MISTRAL_AI).getName()); + defaults.put(AI_GEMINI_CHAT_MODEL, AiDefaultPreferences.CHAT_MODELS.get(AiProvider.GEMINI).getName()); + defaults.put(AI_HUGGING_FACE_CHAT_MODEL, AiDefaultPreferences.CHAT_MODELS.get(AiProvider.HUGGING_FACE).getName()); defaults.put(AI_CUSTOMIZE_SETTINGS, AiDefaultPreferences.CUSTOMIZE_SETTINGS); defaults.put(AI_EMBEDDING_MODEL, AiDefaultPreferences.EMBEDDING_MODEL.name()); - defaults.put(AI_OPEN_AI_API_BASE_URL, AiDefaultPreferences.PROVIDERS_API_URLS.get(AiProvider.OPEN_AI)); - defaults.put(AI_MISTRAL_AI_API_BASE_URL, AiDefaultPreferences.PROVIDERS_API_URLS.get(AiProvider.MISTRAL_AI)); - defaults.put(AI_GEMINI_API_BASE_URL, AiDefaultPreferences.PROVIDERS_API_URLS.get(AiProvider.GEMINI)); - defaults.put(AI_HUGGING_FACE_API_BASE_URL, AiDefaultPreferences.PROVIDERS_API_URLS.get(AiProvider.HUGGING_FACE)); + defaults.put(AI_OPEN_AI_API_BASE_URL, AiProvider.OPEN_AI.getApiUrl()); + defaults.put(AI_MISTRAL_AI_API_BASE_URL, AiProvider.MISTRAL_AI.getApiUrl()); + defaults.put(AI_GEMINI_API_BASE_URL, AiProvider.GEMINI.getApiUrl()); + defaults.put(AI_HUGGING_FACE_API_BASE_URL, AiProvider.HUGGING_FACE.getApiUrl()); defaults.put(AI_SYSTEM_MESSAGE, AiDefaultPreferences.SYSTEM_MESSAGE); defaults.put(AI_TEMPERATURE, AiDefaultPreferences.TEMPERATURE); - defaults.put(AI_CONTEXT_WINDOW_SIZE, AiDefaultPreferences.CONTEXT_WINDOW_SIZES.get(AiDefaultPreferences.PROVIDER).get(AiDefaultPreferences.CHAT_MODELS.get(AiDefaultPreferences.PROVIDER))); + defaults.put(AI_CONTEXT_WINDOW_SIZE, AiDefaultPreferences.getContextWindowSize(AiDefaultPreferences.PROVIDER, AiDefaultPreferences.CHAT_MODELS.get(AiDefaultPreferences.PROVIDER).getName())); defaults.put(AI_DOCUMENT_SPLITTER_CHUNK_SIZE, AiDefaultPreferences.DOCUMENT_SPLITTER_CHUNK_SIZE); defaults.put(AI_DOCUMENT_SPLITTER_OVERLAP_SIZE, AiDefaultPreferences.DOCUMENT_SPLITTER_OVERLAP); defaults.put(AI_RAG_MAX_RESULTS_COUNT, AiDefaultPreferences.RAG_MAX_RESULTS_COUNT); diff --git a/src/main/java/org/jabref/model/ai/AiProvider.java b/src/main/java/org/jabref/model/ai/AiProvider.java index ae33acb97cd..00382e98d36 100644 --- a/src/main/java/org/jabref/model/ai/AiProvider.java +++ b/src/main/java/org/jabref/model/ai/AiProvider.java @@ -3,21 +3,33 @@ import java.io.Serializable; public enum AiProvider implements Serializable { - OPEN_AI("OpenAI"), - MISTRAL_AI("Mistral AI"), - GEMINI("Gemini"), - HUGGING_FACE("Hugging Face"); + OPEN_AI("OpenAI", "https://openai.com/policies/privacy-policy/", "https://openai.com/policies/privacy-policy/"), + MISTRAL_AI("Mistral AI", "https://mistral.ai/terms/#privacy-policy", "https://mistral.ai/terms/#privacy-policy"), + GEMINI("Gemini", "https://huggingface.co/privacy", "https://ai.google.dev/gemini-api/terms"), + HUGGING_FACE("Hugging Face", "https://huggingface.co/api", "https://huggingface.co/privacy"); private final String label; + private final String apiUrl; + private final String privacyPolicyUrl; - AiProvider(String label) { + AiProvider(String label, String apiUrl, String privacyPolicyUrl) { this.label = label; + this.apiUrl = apiUrl; + this.privacyPolicyUrl = privacyPolicyUrl; } public String getLabel() { return label; } + public String getApiUrl() { + return apiUrl; + } + + public String getPrivacyPolicyUrl() { + return privacyPolicyUrl; + } + public String toString() { return label; }