Skip to content

Commit 8ec4808

Browse files
Changing the AI model in the preferences does not change the text of the status bar (#14053)
* changed initializeNotice() in AiChatComponent class / now should work properly / need to add test * add test for checking if the value of ai model is changing * changed CHANGELOG.md * deleted System.out from AiChatComponent.java * some changes in CHANGELOG.md * another changes in CHANGELOG.md * updates test for AiChatComponent class * CHANGELOG.md changes * edit CHANGELOG.md * update for tests in AiChatComponentTest class / update for function computeNoticeText() in AiChatComponent class * Fix submodules * Fix submodules * Update * Update csl-locales * Update submodules * Fix submodules * Update jabgui/src/test/java/org/jabref/gui/ai/components/aichat/AiChatComponentTest.java Co-authored-by: Subhramit Basu <[email protected]> * Update jabgui/src/test/java/org/jabref/gui/ai/components/aichat/AiChatComponentTest.java Co-authored-by: Subhramit Basu <[email protected]> * Update AiChatComponentTest.java * Update AiChatComponentTest.java - make som cleanup changes --------- Co-authored-by: Subhramit Basu <[email protected]>
1 parent d9b90c6 commit 8ec4808

File tree

3 files changed

+174
-4
lines changed

3 files changed

+174
-4
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv
152152
- We fixed an issue where button-bar buttons truncated long text with ellipsis. [#13877](https://github.com/JabRef/jabref/pull/13877)
153153
- We fixed an issue where ignoring of subdirectories via `.gitingore` patterns did not work in the "Find unlinked files dialog". [forum#5425](https://discourse.jabref.org/t/set-list-of-ignored-folders-paths/5425/6)
154154
- We fixed an issue where the "Applications to push entries to" list in the preferences was not sorted alphabetically. [#14058](https://github.com/JabRef/jabref/issues/14058)
155+
- We fixed an issue where notice text in AI chat was not automatically refreshed when the user changed preferences.[#13855](https://github.com/JabRef/jabref/issues/13855)
155156

156157
### Removed
157158

jabgui/src/main/java/org/jabref/gui/ai/components/aichat/AiChatComponent.java

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
import java.util.List;
66
import java.util.stream.Stream;
77

8+
import javafx.beans.Observable;
9+
import javafx.beans.binding.Bindings;
810
import javafx.beans.property.StringProperty;
911
import javafx.collections.FXCollections;
1012
import javafx.collections.ObservableList;
@@ -38,6 +40,7 @@
3840
import org.jabref.model.util.ListUtil;
3941

4042
import com.airhacks.afterburner.views.ViewLoader;
43+
import com.google.common.annotations.VisibleForTesting;
4144
import dev.langchain4j.data.message.AiMessage;
4245
import dev.langchain4j.data.message.ChatMessage;
4346
import dev.langchain4j.data.message.UserMessage;
@@ -74,6 +77,8 @@ public class AiChatComponent extends VBox {
7477
@FXML private Hyperlink exQuestion3;
7578
@FXML private HBox exQuestionBox;
7679

80+
private String noticeTemplate;
81+
7782
public AiChatComponent(AiService aiService,
7883
StringProperty name,
7984
ObservableList<ChatMessage> chatHistory,
@@ -117,11 +122,27 @@ private void initializeNotifications() {
117122
}
118123

119124
private void initializeNotice() {
120-
String newNotice = noticeText
121-
.getText()
122-
.replaceAll("%0", aiPreferences.getAiProvider().getLabel() + " " + aiPreferences.getSelectedChatModel());
125+
this.noticeTemplate = noticeText.getText();
126+
127+
noticeText.textProperty().bind(Bindings.createStringBinding(this::computeNoticeText, noticeDependencies()));
128+
}
129+
130+
@VisibleForTesting
131+
String computeNoticeText() {
132+
String provider = aiPreferences.getAiProvider().getLabel();
133+
String model = aiPreferences.getSelectedChatModel();
134+
return noticeTemplate.replace("%0", provider + " " + model);
135+
}
123136

124-
noticeText.setText(newNotice);
137+
private Observable[] noticeDependencies() {
138+
return new Observable[] {
139+
aiPreferences.aiProviderProperty(),
140+
aiPreferences.openAiChatModelProperty(),
141+
aiPreferences.mistralAiChatModelProperty(),
142+
aiPreferences.geminiChatModelProperty(),
143+
aiPreferences.huggingFaceChatModelProperty(),
144+
aiPreferences.gpt4AllChatModelProperty()
145+
};
125146
}
126147

127148
private void initializeExampleQuestions() {
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
package org.jabref.gui.ai.components.aichat;
2+
3+
import java.util.concurrent.CountDownLatch;
4+
import java.util.concurrent.TimeUnit;
5+
6+
import javafx.application.Platform;
7+
import javafx.beans.property.SimpleObjectProperty;
8+
import javafx.beans.property.SimpleStringProperty;
9+
import javafx.collections.FXCollections;
10+
11+
import org.jabref.gui.DialogService;
12+
import org.jabref.logic.ai.AiPreferences;
13+
import org.jabref.logic.ai.AiService;
14+
import org.jabref.logic.ai.chatting.AiChatLogic;
15+
import org.jabref.logic.ai.chatting.AiChatService;
16+
import org.jabref.logic.ai.ingestion.IngestionService;
17+
import org.jabref.logic.l10n.Language;
18+
import org.jabref.logic.l10n.Localization;
19+
import org.jabref.logic.util.TaskExecutor;
20+
import org.jabref.model.ai.AiProvider;
21+
import org.jabref.model.database.BibDatabaseContext;
22+
23+
import org.junit.jupiter.api.BeforeEach;
24+
import org.junit.jupiter.api.extension.ExtendWith;
25+
import org.junit.jupiter.params.ParameterizedTest;
26+
import org.junit.jupiter.params.provider.EnumSource;
27+
import org.mockito.Mockito;
28+
import org.testfx.framework.junit5.ApplicationExtension;
29+
30+
import static org.junit.jupiter.api.Assertions.assertEquals;
31+
import static org.mockito.Mockito.mock;
32+
import static org.mockito.Mockito.when;
33+
34+
@ExtendWith(ApplicationExtension.class)
35+
class AiChatComponentTest {
36+
private AiPreferences prefs;
37+
private AiService aiService;
38+
private BibDatabaseContext bibDatabaseContext;
39+
private DialogService dialogService;
40+
private TaskExecutor taskExecutor;
41+
42+
@BeforeEach
43+
void setUp() {
44+
Localization.setLanguage(Language.ENGLISH);
45+
46+
prefs = mock(AiPreferences.class, Mockito.RETURNS_DEEP_STUBS);
47+
SimpleObjectProperty<AiProvider> providerProp = new SimpleObjectProperty<>(AiProvider.OPEN_AI);
48+
SimpleStringProperty openAiModelProp = new SimpleStringProperty("gpt-4o");
49+
SimpleStringProperty mistralModelProp = new SimpleStringProperty("mistral-large");
50+
SimpleStringProperty geminiModelProp = new SimpleStringProperty("gemini-1.5-pro");
51+
SimpleStringProperty hfModelProp = new SimpleStringProperty("hf-model");
52+
SimpleStringProperty gpt4AllModelProp = new SimpleStringProperty("gpt4all");
53+
54+
when(prefs.aiProviderProperty()).thenReturn(providerProp);
55+
when(prefs.openAiChatModelProperty()).thenReturn(openAiModelProp);
56+
when(prefs.mistralAiChatModelProperty()).thenReturn(mistralModelProp);
57+
when(prefs.geminiChatModelProperty()).thenReturn(geminiModelProp);
58+
when(prefs.huggingFaceChatModelProperty()).thenReturn(hfModelProp);
59+
when(prefs.gpt4AllChatModelProperty()).thenReturn(gpt4AllModelProp);
60+
61+
when(prefs.getAiProvider()).thenAnswer(_ -> providerProp.get());
62+
when(prefs.getSelectedChatModel()).thenAnswer(_ -> {
63+
return switch (prefs.getAiProvider()) {
64+
case OPEN_AI ->
65+
openAiModelProp.get();
66+
case MISTRAL_AI ->
67+
mistralModelProp.get();
68+
case GEMINI ->
69+
geminiModelProp.get();
70+
case HUGGING_FACE ->
71+
hfModelProp.get();
72+
case GPT4ALL ->
73+
gpt4AllModelProp.get();
74+
};
75+
});
76+
77+
aiService = mock(AiService.class, Mockito.RETURNS_DEEP_STUBS);
78+
AiChatService chatService = mock(AiChatService.class);
79+
AiChatLogic chatLogic = mock(AiChatLogic.class, Mockito.RETURNS_DEEP_STUBS);
80+
when(chatLogic.getChatHistory()).thenReturn(FXCollections.observableArrayList());
81+
when(chatService.makeChat(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any())).thenReturn(chatLogic);
82+
when(aiService.getAiChatService()).thenReturn(chatService);
83+
84+
IngestionService ingestionService = mock(IngestionService.class, Mockito.RETURNS_DEEP_STUBS);
85+
when(aiService.getIngestionService()).thenReturn(ingestionService);
86+
87+
bibDatabaseContext = mock(BibDatabaseContext.class);
88+
dialogService = mock(DialogService.class);
89+
taskExecutor = mock(TaskExecutor.class);
90+
}
91+
92+
private AiChatComponent createComponent() throws Exception {
93+
CountDownLatch latch = new CountDownLatch(1);
94+
final AiChatComponent[] holder = new AiChatComponent[1];
95+
Platform.runLater(() -> {
96+
holder[0] = new AiChatComponent(
97+
aiService,
98+
new SimpleStringProperty("entry"),
99+
FXCollections.observableArrayList(),
100+
FXCollections.observableArrayList(),
101+
bibDatabaseContext,
102+
prefs,
103+
dialogService,
104+
taskExecutor
105+
);
106+
latch.countDown();
107+
});
108+
latch.await(5, TimeUnit.SECONDS);
109+
return holder[0];
110+
}
111+
112+
@ParameterizedTest
113+
@EnumSource(AiProvider.class)
114+
void noticeTextUpdatesWhenProviderChanges(AiProvider provider) throws Exception {
115+
AiChatComponent component = createComponent();
116+
117+
// Change provider
118+
prefs.aiProviderProperty().set(provider);
119+
120+
Thread.sleep(50);
121+
122+
String expected = "Current AI model: " + provider.getLabel() + " " + prefs.getSelectedChatModel()
123+
+ ". The AI may generate inaccurate or inappropriate responses. Please verify any information provided.";
124+
assertEquals(expected, component.computeNoticeText());
125+
}
126+
127+
@ParameterizedTest
128+
@EnumSource(AiProvider.class)
129+
void noticeTextUpdatesWhenCurrentModelChangesForSelectedProvider(AiProvider provider) throws Exception {
130+
AiChatComponent component = createComponent();
131+
132+
// Select provider
133+
prefs.aiProviderProperty().set(provider);
134+
135+
// Change current model for all providers; only the selected provider should influence the notice text.
136+
String newModel = "new-model";
137+
prefs.openAiChatModelProperty().set(newModel);
138+
prefs.mistralAiChatModelProperty().set(newModel);
139+
prefs.geminiChatModelProperty().set(newModel);
140+
prefs.huggingFaceChatModelProperty().set(newModel);
141+
prefs.gpt4AllChatModelProperty().set(newModel);
142+
143+
Thread.sleep(50);
144+
String expected = "Current AI model: " + provider.getLabel() + " " + newModel
145+
+ ". The AI may generate inaccurate or inappropriate responses. Please verify any information provided.";
146+
assertEquals(expected, component.computeNoticeText());
147+
}
148+
}

0 commit comments

Comments
 (0)