Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
4d18454
Add follow-up questions feature to AI chat
Muskan244 Nov 19, 2025
c86fd77
Add CHANGELOG.md entry
Muskan244 Nov 19, 2025
311022b
Add follow-up questions feature to AI chat
Muskan244 Nov 19, 2025
e822313
Add CHANGELOG.md entry
Muskan244 Nov 19, 2025
010e33d
Merge branch 'fix-for-issue-12243' of https://github.com/Muskan244/ja…
Muskan244 Nov 19, 2025
6f17bb1
Fix submodules, CHANGELOG.md and overwrite issues
Muskan244 Nov 19, 2025
604f44d
Fixed issues in rewritten files to match original files
Muskan244 Nov 19, 2025
49ac25e
Fix submodules modified test
Muskan244 Nov 19, 2025
f368e18
Fix format indentation
Muskan244 Nov 19, 2025
ee8b971
Merge branch 'main' into fix-for-issue-12243
Muskan244 Nov 20, 2025
45d6dbb
Add preference for max follow-up questions and migrate prompt to AiTe…
Muskan244 Nov 21, 2025
338d91e
Merge branch 'fix-for-issue-12243' of https://github.com/Muskan244/ja…
Muskan244 Nov 21, 2025
97709ff
Merge branch 'main' into fix-for-issue-12243
Muskan244 Nov 21, 2025
7d50165
Improved follow-up question settings and UI improvements
Muskan244 Nov 22, 2025
265af2f
Merge branch 'fix-for-issue-12243' of https://github.com/Muskan244/ja…
Muskan244 Nov 22, 2025
6314492
Update follow-up questions localization and switch to UiTaskExecutor.…
Muskan244 Nov 22, 2025
cdb788e
Merge branch 'main' into fix-for-issue-12243
Muskan244 Nov 22, 2025
992c78d
Update jablib/src/main/java/org/jabref/logic/ai/templates/AiTemplate.…
Muskan244 Nov 22, 2025
3d894db
Improve follow-up questions UI and behavior
Muskan244 Nov 23, 2025
d72d9ca
Merge branch 'fix-for-issue-12243' of https://github.com/Muskan244/ja…
Muskan244 Nov 23, 2025
0607394
Hide Try with examples also during follow-up question generation
Muskan244 Nov 24, 2025
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv
- We added the possibility to configure the email provided to unpaywall. [#14340](https://github.com/JabRef/jabref/pull/14340)
- We added a "Regenerate" button for the AI chat allowing the user to make the language model reformulate its response to the previous prompt. [#12191](https://github.com/JabRef/jabref/issues/12191)
- We added support for transliteration of fields to English and automatic transliteration of generated citation key. [#11377](https://github.com/JabRef/jabref/issues/11377)
- We added a generate follow-up questions feature to AI chat that suggests relevant questions after each AI response. [#12243](https://github.com/JabRef/jabref/issues/12243)

### Changed

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import javafx.scene.control.Button;
import javafx.scene.control.Hyperlink;
import javafx.scene.control.Label;
import javafx.scene.control.Tooltip;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
Expand Down Expand Up @@ -78,6 +79,7 @@ public class AiChatComponent extends VBox {
@FXML private Hyperlink exQuestion2;
@FXML private Hyperlink exQuestion3;
@FXML private HBox exQuestionBox;
@FXML private HBox followUpQuestionsBox;

private String noticeTemplate;

Expand Down Expand Up @@ -114,6 +116,7 @@ public void initialize() {
initializeNotifications();
sendExampleQuestions();
initializeExampleQuestions();
initializeFollowUpQuestions();
}

private void initializeNotifications() {
Expand Down Expand Up @@ -214,6 +217,42 @@ private void initializeChatPrompt() {
updatePromptHistory();
}

private void initializeFollowUpQuestions() {
aiChatLogic.getFollowUpQuestions().addListener((javafx.collections.ListChangeListener<String>) change -> {
updateFollowUpQuestions();
});
}

private void updateFollowUpQuestions() {
List<String> questions = new ArrayList<>(aiChatLogic.getFollowUpQuestions());

UiTaskExecutor.runInJavaFXThread(() -> {
followUpQuestionsBox.getChildren().removeIf(node -> node instanceof Hyperlink);

if (questions.isEmpty()) {
followUpQuestionsBox.setVisible(false);
followUpQuestionsBox.setManaged(false);
exQuestionBox.setVisible(true);
exQuestionBox.setManaged(true);
} else {
followUpQuestionsBox.setVisible(true);
followUpQuestionsBox.setManaged(true);
exQuestionBox.setVisible(false);
exQuestionBox.setManaged(false);

for (String question : questions) {
Hyperlink link = new Hyperlink(question);
link.getStyleClass().add("exampleQuestionStyle");
link.setTooltip(new Tooltip(question));
link.setOnAction(event -> {
onSendMessage(question);
});
followUpQuestionsBox.getChildren().add(link);
}
}
});
}

private void updateNotifications() {
notifications.clear();
notifications.addAll(entries.stream().map(this::updateNotificationsForEntry).flatMap(List::stream).toList());
Expand Down Expand Up @@ -278,6 +317,13 @@ private List<Notification> updateNotificationsForEntry(BibEntry entry) {
}

private void onSendMessage(String userPrompt) {
aiChatLogic.getFollowUpQuestions().clear();

UiTaskExecutor.runInJavaFXThread(() -> {
exQuestionBox.setVisible(false);
exQuestionBox.setManaged(false);
});

UserMessage userMessage = new UserMessage(userPrompt);
updatePromptHistory();
setLoading(true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,4 +198,3 @@ private void showFailures(List<JabRefException> failures) {
);
}
}

16 changes: 15 additions & 1 deletion jabgui/src/main/java/org/jabref/gui/preferences/ai/AiTab.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.Spinner;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.control.TextArea;
Expand Down Expand Up @@ -40,6 +42,11 @@ public class AiTab extends AbstractPreferenceTabView<AiTabViewModel> implements
@FXML private CheckBox enableAi;
@FXML private CheckBox autoGenerateEmbeddings;
@FXML private CheckBox autoGenerateSummaries;
@FXML private CheckBox generateFollowUpQuestions;
@FXML private Spinner<Integer> followUpQuestionsCountSpinner;
@FXML private Tab followUpQuestionsTab;
@FXML private TextArea followUpQuestionsTextArea;
@FXML private Label followUpQuestionsCountLabel;

@FXML private ComboBox<AiProvider> aiProviderComboBox;
@FXML private ComboBox<String> chatModelComboBox;
Expand Down Expand Up @@ -117,7 +124,7 @@ private void initializeTemplates() {
summarizationCombineUserMessageTextArea.textProperty().bindBidirectional(viewModel.getTemplateSources().get(AiTemplate.SUMMARIZATION_COMBINE_USER_MESSAGE));
citationParsingSystemMessageTextArea.textProperty().bindBidirectional(viewModel.getTemplateSources().get(AiTemplate.CITATION_PARSING_SYSTEM_MESSAGE));
citationParsingUserMessageTextArea.textProperty().bindBidirectional(viewModel.getTemplateSources().get(AiTemplate.CITATION_PARSING_USER_MESSAGE));

followUpQuestionsTextArea.textProperty().bindBidirectional(viewModel.getTemplateSources().get(AiTemplate.FOLLOW_UP_QUESTIONS));
templatesTabPane.getSelectionModel().selectedItemProperty().addListener(_ -> viewModel.selectedTemplateProperty().set(getAiTemplate()));
}

Expand Down Expand Up @@ -248,6 +255,11 @@ private void initializeEnableAi() {
autoGenerateSummaries.disableProperty().bind(viewModel.disableAutoGenerateSummaries());
autoGenerateEmbeddings.selectedProperty().bindBidirectional(viewModel.autoGenerateEmbeddings());
autoGenerateEmbeddings.disableProperty().bind(viewModel.disableAutoGenerateEmbeddings());
generateFollowUpQuestions.selectedProperty().bindBidirectional(viewModel.generateFollowUpQuestions());
followUpQuestionsCountSpinner.setValueFactory(AiTabViewModel.followUpQuestionsCountValueFactory);
followUpQuestionsCountSpinner.getValueFactory().valueProperty().bindBidirectional(viewModel.followUpQuestionsCountProperty().asObject());
followUpQuestionsCountSpinner.disableProperty().bind(generateFollowUpQuestions.selectedProperty().not());
followUpQuestionsCountLabel.disableProperty().bind(generateFollowUpQuestions.selectedProperty().not());
}

@Override
Expand Down Expand Up @@ -292,6 +304,8 @@ public Optional<AiTemplate> getAiTemplate() {
return Optional.of(AiTemplate.CITATION_PARSING_SYSTEM_MESSAGE);
} else if (selectedTab == citationParsingUserMessageTab) {
return Optional.of(AiTemplate.CITATION_PARSING_USER_MESSAGE);
} else if (selectedTab == followUpQuestionsTab) {
return Optional.of(AiTemplate.FOLLOW_UP_QUESTIONS);
}

return Optional.empty();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.scene.control.SpinnerValueFactory;

import org.jabref.gui.preferences.PreferenceTabViewModel;
import org.jabref.logic.ai.AiDefaultPreferences;
Expand All @@ -37,13 +38,17 @@
import de.saxsys.mvvmfx.utils.validation.Validator;

public class AiTabViewModel implements PreferenceTabViewModel {
protected static SpinnerValueFactory<Integer> followUpQuestionsCountValueFactory = new SpinnerValueFactory.IntegerSpinnerValueFactory(1, 5, 3);

private final Locale oldLocale;

private final BooleanProperty enableAi = new SimpleBooleanProperty();
private final BooleanProperty autoGenerateEmbeddings = new SimpleBooleanProperty();
private final BooleanProperty disableAutoGenerateEmbeddings = new SimpleBooleanProperty();
private final BooleanProperty autoGenerateSummaries = new SimpleBooleanProperty();
private final BooleanProperty disableAutoGenerateSummaries = new SimpleBooleanProperty();
private final BooleanProperty generateFollowUpQuestions = new SimpleBooleanProperty();
private final IntegerProperty followUpQuestionsCount = new SimpleIntegerProperty();

private final ListProperty<AiProvider> aiProvidersList =
new SimpleListProperty<>(FXCollections.observableArrayList(AiProvider.values()));
Expand Down Expand Up @@ -91,7 +96,8 @@ AiTemplate.SUMMARIZATION_CHUNK_USER_MESSAGE, new SimpleStringProperty(),
AiTemplate.SUMMARIZATION_COMBINE_SYSTEM_MESSAGE, new SimpleStringProperty(),
AiTemplate.SUMMARIZATION_COMBINE_USER_MESSAGE, new SimpleStringProperty(),
AiTemplate.CITATION_PARSING_SYSTEM_MESSAGE, new SimpleStringProperty(),
AiTemplate.CITATION_PARSING_USER_MESSAGE, new SimpleStringProperty()
AiTemplate.CITATION_PARSING_USER_MESSAGE, new SimpleStringProperty(),
AiTemplate.FOLLOW_UP_QUESTIONS, new SimpleStringProperty()
);

private final OptionalObjectProperty<AiTemplate> selectedTemplate = OptionalObjectProperty.empty();
Expand Down Expand Up @@ -341,6 +347,8 @@ public void setValues() {
enableAi.setValue(aiPreferences.getEnableAi());
autoGenerateSummaries.setValue(aiPreferences.getAutoGenerateSummaries());
autoGenerateEmbeddings.setValue(aiPreferences.getAutoGenerateEmbeddings());
generateFollowUpQuestions.setValue(aiPreferences.getGenerateFollowUpQuestions());
followUpQuestionsCount.setValue(aiPreferences.getFollowUpQuestionsCount());

selectedAiProvider.setValue(aiPreferences.getAiProvider());

Expand All @@ -364,6 +372,8 @@ public void storeSettings() {
aiPreferences.setEnableAi(enableAi.get());
aiPreferences.setAutoGenerateEmbeddings(autoGenerateEmbeddings.get());
aiPreferences.setAutoGenerateSummaries(autoGenerateSummaries.get());
aiPreferences.setGenerateFollowUpQuestions(generateFollowUpQuestions.get());
aiPreferences.setFollowUpQuestionsCount(followUpQuestionsCount.get());

aiPreferences.setAiProvider(selectedAiProvider.get());

Expand Down Expand Up @@ -414,6 +424,7 @@ public void resetExpertSettings() {
documentSplitterOverlapSize.set(AiDefaultPreferences.DOCUMENT_SPLITTER_OVERLAP);
ragMaxResultsCount.set(AiDefaultPreferences.RAG_MAX_RESULTS_COUNT);
ragMinScore.set(LocalizedNumbers.doubleToString(AiDefaultPreferences.RAG_MIN_SCORE));
followUpQuestionsCount.set(AiDefaultPreferences.FOLLOW_UP_QUESTIONS_COUNT);
}

public void resetTemplates() {
Expand Down Expand Up @@ -487,6 +498,18 @@ public BooleanProperty disableAutoGenerateSummaries() {
return disableAutoGenerateSummaries;
}

public BooleanProperty generateFollowUpQuestions() {
return generateFollowUpQuestions;
}

public IntegerProperty followUpQuestionsCountProperty() {
return followUpQuestionsCount;
}

public StringProperty followUpQuestionsTemplateProperty() {
return aiPreferences.templateProperty(AiTemplate.FOLLOW_UP_QUESTIONS);
}

public ReadOnlyListProperty<AiProvider> aiProvidersProperty() {
return aiProvidersList;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,16 @@
</Loadable>

<HBox spacing="10" alignment="CENTER" fx:id="exQuestionBox">
<Label text="%Try with examples" BorderPane.alignment="CENTER"/>
<Label text="%Try with examples:" BorderPane.alignment="CENTER"/>
<Hyperlink fx:id="exQuestion1" BorderPane.alignment="CENTER" styleClass="exampleQuestionStyle"/>
<Hyperlink fx:id="exQuestion2" BorderPane.alignment="CENTER" styleClass="exampleQuestionStyle"/>
<Hyperlink fx:id="exQuestion3" BorderPane.alignment="CENTER" styleClass="exampleQuestionStyle"/>
</HBox>

<HBox spacing="10" alignment="CENTER" fx:id="followUpQuestionsBox" managed="false" visible="false">
<Label text="%Follow-up questions:" BorderPane.alignment="CENTER"/>
</HBox>

<HBox spacing="10">
<Button alignment="CENTER"
fx:id="notificationsButton"
Expand Down
26 changes: 26 additions & 0 deletions jabgui/src/main/resources/org/jabref/gui/preferences/ai/AiTab.fxml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<?import javafx.scene.control.CheckBox?>
<?import javafx.scene.control.ComboBox?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.Spinner?>
<?import javafx.scene.control.Tab?>
<?import javafx.scene.control.TabPane?>
<?import javafx.scene.control.TextArea?>
Expand All @@ -21,6 +22,7 @@
<?import javafx.scene.layout.Region?>
<?import com.dlsc.gemsfx.Spacer?>
<?import javafx.scene.layout.Pane?>
<?import com.sun.javafx.scene.control.IntegerField?>
<fx:root
spacing="10.0"
type="VBox"
Expand Down Expand Up @@ -296,6 +298,11 @@
closable="false">
<TextArea fx:id="citationParsingUserMessageTextArea"/>
</Tab>
<Tab fx:id="followUpQuestionsTab"
text="%Prompt for generating follow-up questions"
closable="false">
<TextArea fx:id="followUpQuestionsTextArea"/>
</Tab>
</TabPane>

<HBox spacing="10" alignment="CENTER_LEFT">
Expand Down Expand Up @@ -330,4 +337,23 @@
text="%Automatically generate summaries for new entries"
HBox.hgrow="ALWAYS"
maxWidth="Infinity"/>

<CheckBox fx:id="generateFollowUpQuestions"
mnemonicParsing="false"
text="%Generate follow-up questions"
HBox.hgrow="ALWAYS"
maxWidth="Infinity"/>

<HBox alignment="CENTER_LEFT" spacing="10.0">
<padding>
<Insets left="20.0"/>
</padding>
<Label fx:id="followUpQuestionsCountLabel"
alignment="BASELINE_CENTER"
text="%Number of follow-up questions"/>
<Spinner fx:id="followUpQuestionsCountSpinner"
editable="true"
maxWidth="100"/>
</HBox>

</fx:root>
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ public String toString() {
public static final boolean ENABLE_CHAT = false;
public static final boolean AUTO_GENERATE_EMBEDDINGS = false;
public static final boolean AUTO_GENERATE_SUMMARIES = false;
public static final boolean GENERATE_FOLLOW_UP_QUESTIONS = false;
public static final int FOLLOW_UP_QUESTIONS_COUNT = 3;

public static final AiProvider PROVIDER = AiProvider.OPEN_AI;

Expand Down Expand Up @@ -115,7 +117,20 @@ public String toString() {
AiTemplate.SUMMARIZATION_COMBINE_USER_MESSAGE, "$chunks",

AiTemplate.CITATION_PARSING_SYSTEM_MESSAGE, "You are a bot to convert a plain text citation to a BibTeX entry. The user you talk to understands only BibTeX code, so provide it plainly without any wrappings.",
AiTemplate.CITATION_PARSING_USER_MESSAGE, "Please convert this plain text citation to a BibTeX entry:\n$citation\nIn your output, please provide only BibTeX code as your message."
AiTemplate.CITATION_PARSING_USER_MESSAGE, "Please convert this plain text citation to a BibTeX entry:\n$citation\nIn your output, please provide only BibTeX code as your message.",

AiTemplate.FOLLOW_UP_QUESTIONS, """
Based on this conversation:
User: $userMessage
Assistant: $aiResponse

Generate $count short follow-up questions (maximum 10 words each) that the user might want to ask next.
Format your response as a numbered list:
1. [question]
2. [question]
3. [question]

Only provide the numbered list, nothing else."""
);

public static List<String> getAvailableModels(AiProvider aiProvider) {
Expand Down
33 changes: 32 additions & 1 deletion jablib/src/main/java/org/jabref/logic/ai/AiPreferences.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ public class AiPreferences {
private final BooleanProperty enableAi;
private final BooleanProperty autoGenerateEmbeddings;
private final BooleanProperty autoGenerateSummaries;
private final BooleanProperty generateFollowUpQuestions;
private final IntegerProperty followUpQuestionsCount;

private final ObjectProperty<AiProvider> aiProvider;

Expand Down Expand Up @@ -67,6 +69,8 @@ public class AiPreferences {
public AiPreferences(boolean enableAi,
boolean autoGenerateEmbeddings,
boolean autoGenerateSummaries,
boolean generateFollowUpQuestions,
int followUpQuestionsCount,
AiProvider aiProvider,
String openAiChatModel,
String mistralAiChatModel,
Expand All @@ -91,6 +95,8 @@ public AiPreferences(boolean enableAi,
this.enableAi = new SimpleBooleanProperty(enableAi);
this.autoGenerateEmbeddings = new SimpleBooleanProperty(autoGenerateEmbeddings);
this.autoGenerateSummaries = new SimpleBooleanProperty(autoGenerateSummaries);
this.generateFollowUpQuestions = new SimpleBooleanProperty(generateFollowUpQuestions);
this.followUpQuestionsCount = new SimpleIntegerProperty(followUpQuestionsCount);

this.aiProvider = new SimpleObjectProperty<>(aiProvider);

Expand Down Expand Up @@ -127,7 +133,8 @@ AiTemplate.SUMMARIZATION_CHUNK_USER_MESSAGE, new SimpleStringProperty(templates.
AiTemplate.SUMMARIZATION_COMBINE_SYSTEM_MESSAGE, new SimpleStringProperty(templates.get(AiTemplate.SUMMARIZATION_COMBINE_SYSTEM_MESSAGE)),
AiTemplate.SUMMARIZATION_COMBINE_USER_MESSAGE, new SimpleStringProperty(templates.get(AiTemplate.SUMMARIZATION_COMBINE_USER_MESSAGE)),
AiTemplate.CITATION_PARSING_SYSTEM_MESSAGE, new SimpleStringProperty(templates.get(AiTemplate.CITATION_PARSING_SYSTEM_MESSAGE)),
AiTemplate.CITATION_PARSING_USER_MESSAGE, new SimpleStringProperty(templates.get(AiTemplate.CITATION_PARSING_USER_MESSAGE))
AiTemplate.CITATION_PARSING_USER_MESSAGE, new SimpleStringProperty(templates.get(AiTemplate.CITATION_PARSING_USER_MESSAGE)),
AiTemplate.FOLLOW_UP_QUESTIONS, new SimpleStringProperty(templates.get(AiTemplate.FOLLOW_UP_QUESTIONS))
);
}

Expand Down Expand Up @@ -195,6 +202,30 @@ public void setAutoGenerateSummaries(boolean autoGenerateSummaries) {
this.autoGenerateSummaries.set(autoGenerateSummaries);
}

public BooleanProperty generateFollowUpQuestionsProperty() {
return generateFollowUpQuestions;
}

public IntegerProperty followUpQuestionsCountProperty() {
return followUpQuestionsCount;
}

public int getFollowUpQuestionsCount() {
return followUpQuestionsCount.get();
}

public void setFollowUpQuestionsCount(int followUpQuestionsCount) {
this.followUpQuestionsCount.set(followUpQuestionsCount);
}

public boolean getGenerateFollowUpQuestions() {
return generateFollowUpQuestions.get();
}

public void setGenerateFollowUpQuestions(boolean generateFollowUpQuestions) {
this.generateFollowUpQuestions.set(generateFollowUpQuestions);
}

public ObjectProperty<AiProvider> aiProviderProperty() {
return aiProvider;
}
Expand Down
Loading