Skip to content

Commit 710b57f

Browse files
committed
feat(demo): add Material UI vertical tabs and improve retrieval
UI Improvements: - Add VerticalTabPane component with Material UI-style tabs - Implement SettingsPanel with cache dropdown defaulting to NONE - Simplify LogPanel to extend BorderPane - Fix dropdown font visibility with explicit text colors Retrieval Improvements: - Increase maxResults from 15 to 40 for better TEXT/IMAGE balance - Lower minScore from 0.2 to 0.1 for broader retrieval - Add TEXT vs IMAGE breakdown logging The vertical tabs use a ToggleGroup for mutual exclusivity - clicking the active tab collapses the panel. Includes smooth 250ms animations.
1 parent fba1b14 commit 710b57f

File tree

7 files changed

+567
-220
lines changed

7 files changed

+567
-220
lines changed

demos/rag-multimodal/src/main/java/com/redis/vl/demo/rag/service/RAGService.java

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -140,11 +140,6 @@ public ChatMessage query(String userQuery, CacheType cacheType) {
140140
Query query = Query.from(userQuery);
141141
List<Content> retrievedContent = contentRetriever.retrieve(query);
142142
System.out.println("→ Retrieved " + retrievedContent.size() + " content items for query: " + userQuery);
143-
for (int i = 0; i < Math.min(3, retrievedContent.size()); i++) {
144-
String preview = retrievedContent.get(i).textSegment().text();
145-
if (preview.length() > 100) preview = preview.substring(0, 100) + "...";
146-
System.out.println(" [" + i + "] " + preview);
147-
}
148143

149144
// 2. Separate text and image content + extract references
150145
List<Content> textContent = new ArrayList<>();
@@ -171,6 +166,7 @@ public ChatMessage query(String userQuery, CacheType cacheType) {
171166
textContent.add(content);
172167
}
173168
}
169+
System.out.println("→ Breakdown: " + textContent.size() + " TEXT, " + imageContent.size() + " IMAGE");
174170

175171
// Deduplicate references by page (keep first occurrence of each page)
176172
List<Reference> uniqueRefs = new ArrayList<>();

demos/rag-multimodal/src/main/java/com/redis/vl/demo/rag/service/ServiceFactory.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -143,15 +143,15 @@ public RAGService createRAGService(LLMConfig config) {
143143
}
144144

145145
// Create content retriever
146-
// Use higher maxResults to ensure TEXT chunks are retrieved (IMAGE chunks
146+
// Use high maxResults to ensure TEXT chunks are retrieved (IMAGE chunks
147147
// have generic descriptions that may match better semantically but contain
148148
// no useful content). RAGService separates TEXT vs IMAGE for processing.
149149
RedisVLContentRetriever retriever =
150150
RedisVLContentRetriever.builder()
151151
.embeddingStore(embeddingStore)
152152
.embeddingModel(embeddingModel)
153-
.maxResults(15)
154-
.minScore(0.2) // Low threshold since we'll filter in RAGService
153+
.maxResults(40) // Get many results to ensure we have TEXT content
154+
.minScore(0.1) // Very low threshold - let RAGService filter
155155
.build();
156156

157157
// Create chat model based on provider

demos/rag-multimodal/src/main/java/com/redis/vl/demo/rag/ui/ChatController.java

Lines changed: 36 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
import javafx.collections.ObservableList;
1616
import javafx.geometry.Insets;
1717
import javafx.geometry.Orientation;
18-
import javafx.geometry.Pos;
1918
import javafx.scene.control.*;
2019
import javafx.scene.layout.*;
2120
import javafx.stage.FileChooser;
@@ -27,17 +26,16 @@
2726
*/
2827
public class ChatController extends BorderPane {
2928

29+
private static final int SETTINGS_TAB = 0;
30+
private static final int LOG_TAB = 1;
31+
3032
private final ObservableList<ChatMessage> messages = FXCollections.observableArrayList();
3133
private final ListView<ChatMessage> messageListView;
3234
private final TextField inputField;
3335
private final Button sendButton;
34-
private final ComboBox<CacheType> cacheComboBox;
35-
private final Label statusLabel;
36-
private final Label providerLabel;
37-
private final Label modelLabel;
38-
private final Label loadedPdfLabel;
39-
private Button uploadPdfButton;
4036
private final PDFViewerPanel pdfViewer;
37+
private final VerticalTabPane tabPane;
38+
private final SettingsPanel settingsPanel;
4139
private final LogPanel logPanel;
4240
private final EventLogger eventLogger;
4341
private File currentPdfFile;
@@ -54,20 +52,33 @@ public ChatController() {
5452
eventLogger = new EventLogger();
5553
eventLogger.info(Category.SYSTEM, "Application starting...");
5654

57-
// Initialize fields first
58-
uploadPdfButton = new Button("Upload PDF");
59-
uploadPdfButton.setOnAction(e -> uploadPdf());
60-
6155
// PDF viewer panel (left side)
6256
pdfViewer = new PDFViewerPanel();
57+
pdfViewer.setPrefWidth(450);
58+
pdfViewer.setMinWidth(300);
59+
60+
// Settings panel content
61+
settingsPanel = new SettingsPanel();
62+
settingsPanel.getUploadPdfButton().setOnAction(e -> uploadPdf());
6363

64-
// Log panel (bottom)
64+
// Log panel content
6565
logPanel = new LogPanel();
6666
logPanel.setEventLogger(eventLogger);
67-
pdfViewer.setPrefWidth(450);
68-
pdfViewer.setMinWidth(300);
6967

70-
// Message list (center/right side)
68+
// Vertical tab pane (right side)
69+
tabPane = new VerticalTabPane();
70+
tabPane.addTab("⚙", "Settings", settingsPanel);
71+
tabPane.addTab("📋", "Event Log", logPanel);
72+
73+
// Update log badge when count changes
74+
logPanel.setCountListener(count -> {
75+
tabPane.setBadge(LOG_TAB, count > 0 ? String.valueOf(count) : null);
76+
});
77+
78+
// Start with settings tab open
79+
tabPane.selectTab(SETTINGS_TAB);
80+
81+
// Message list (center)
7182
messageListView = new ListView<>(messages);
7283
messageListView.setCellFactory(
7384
lv ->
@@ -95,74 +106,6 @@ protected void updateItem(ChatMessage item, boolean empty) {
95106

96107
setCenter(splitPane);
97108

98-
// Right control panel
99-
VBox rightPanel = new VBox(15);
100-
rightPanel.setPadding(new Insets(15));
101-
rightPanel.getStyleClass().add("control-panel");
102-
rightPanel.setPrefWidth(300);
103-
rightPanel.setMinWidth(300);
104-
105-
// Configuration section
106-
Label configTitle = new Label("Configuration");
107-
configTitle.getStyleClass().add("section-title");
108-
109-
providerLabel = new Label("Provider: " + config.getLLMProvider().getDisplayName());
110-
providerLabel.getStyleClass().add("info-label");
111-
112-
modelLabel = new Label("Model: " + config.getLLMConfig().model());
113-
modelLabel.getStyleClass().add("info-label");
114-
115-
loadedPdfLabel = new Label("No PDF loaded");
116-
loadedPdfLabel.getStyleClass().add("info-label");
117-
loadedPdfLabel.setWrapText(true);
118-
119-
Separator configSeparator = new Separator();
120-
121-
// Cache section
122-
Label cacheTitle = new Label("Semantic Cache");
123-
cacheTitle.getStyleClass().add("section-title");
124-
125-
cacheComboBox = new ComboBox<>();
126-
cacheComboBox.getItems().addAll(CacheType.values());
127-
cacheComboBox.setValue(config.isLangCacheEnabled() ? CacheType.LANGCACHE : CacheType.NONE);
128-
cacheComboBox.getStyleClass().add("cache-combobox");
129-
cacheComboBox.setMaxWidth(Double.MAX_VALUE);
130-
131-
Label cacheInfo = new Label("Select caching mode:\n• No Cache: Always call LLM\n• Local: Redis-based cache\n• LangCache: Cloud cache");
132-
cacheInfo.getStyleClass().add("info-text");
133-
cacheInfo.setWrapText(true);
134-
135-
Separator cacheSeparator = new Separator();
136-
137-
// Actions section
138-
Label actionsTitle = new Label("Actions");
139-
actionsTitle.getStyleClass().add("section-title");
140-
141-
uploadPdfButton.getStyleClass().add("action-button");
142-
uploadPdfButton.setMaxWidth(Double.MAX_VALUE);
143-
144-
Separator actionsSeparator = new Separator();
145-
146-
// Status section
147-
Label statusTitle = new Label("Status");
148-
statusTitle.getStyleClass().add("section-title");
149-
150-
statusLabel = new Label("Ready");
151-
statusLabel.getStyleClass().add("status-label");
152-
statusLabel.setWrapText(true);
153-
154-
// Spacer to push status to bottom
155-
Region spacer = new Region();
156-
VBox.setVgrow(spacer, Priority.ALWAYS);
157-
158-
rightPanel.getChildren().addAll(
159-
configTitle, providerLabel, modelLabel, loadedPdfLabel, configSeparator,
160-
cacheTitle, cacheComboBox, cacheInfo, cacheSeparator,
161-
actionsTitle, uploadPdfButton, actionsSeparator,
162-
spacer,
163-
statusTitle, statusLabel
164-
);
165-
166109
// Bottom input area
167110
HBox inputSection = new HBox(10);
168111
inputSection.setPadding(new Insets(10));
@@ -182,10 +125,8 @@ protected void updateItem(ChatMessage item, boolean empty) {
182125
inputSection.getChildren().addAll(inputField, sendButton);
183126
setBottom(inputSection);
184127

185-
// Right side: control panel + log panel (far right edge)
186-
HBox rightContainer = new HBox();
187-
rightContainer.getChildren().addAll(rightPanel, logPanel);
188-
setRight(rightContainer);
128+
// Right side: vertical tab pane
129+
setRight(tabPane);
189130

190131
// Add welcome message
191132
addSystemMessage("Welcome to RedisVL Multimodal RAG Demo!\n\nConfiguration loaded from application.properties:\n• Provider: " +
@@ -218,9 +159,9 @@ private void sendMessage() {
218159

219160
// Disable input while processing
220161
setInputEnabled(false);
221-
statusLabel.setText("Thinking...");
162+
settingsPanel.setStatus("Thinking...");
222163

223-
CacheType cacheType = cacheComboBox.getValue();
164+
CacheType cacheType = settingsPanel.getCacheComboBox().getValue();
224165
eventLogger.info(Category.LLM, "Query: \"" + truncate(userInput, 50) + "\" (cache: " + cacheType + ")");
225166

226167
// Process in background
@@ -255,7 +196,7 @@ private void sendMessage() {
255196
Platform.runLater(
256197
() -> {
257198
setInputEnabled(true);
258-
statusLabel.setText("Ready");
199+
settingsPanel.setStatus("Ready");
259200
inputField.requestFocus();
260201
});
261202
}
@@ -304,7 +245,7 @@ private void uploadPdf() {
304245
File file = fileChooser.showOpenDialog(getScene().getWindow());
305246
if (file != null) {
306247
currentPdfFile = file;
307-
statusLabel.setText("Processing PDF: " + file.getName());
248+
settingsPanel.setStatus("Processing PDF: " + file.getName());
308249
addSystemMessage("Processing PDF: " + file.getName() + "...");
309250
eventLogger.info(Category.PDF, "Loading PDF: " + file.getName());
310251

@@ -338,15 +279,15 @@ private void uploadPdf() {
338279
String.format(
339280
"PDF processed successfully. Indexed %d chunks. You can now ask questions about the document.",
340281
chunks));
341-
loadedPdfLabel.setText("Loaded: " + file.getName() + " (" + chunks + " chunks)");
342-
statusLabel.setText("Ready");
282+
settingsPanel.setLoadedPdf(file.getName() + " (" + chunks + " chunks)");
283+
settingsPanel.setStatus("Ready");
343284
eventLogger.info(Category.PDF, "Indexed " + chunks + " chunks in " + elapsed + "ms");
344285
});
345286
} catch (Exception e) {
346287
Platform.runLater(
347288
() -> {
348289
addSystemMessage("Error processing PDF: " + e.getMessage());
349-
statusLabel.setText("Ready");
290+
settingsPanel.setStatus("Ready");
350291
eventLogger.error(Category.PDF, "Indexing failed: " + e.getMessage());
351292
});
352293
}
@@ -391,21 +332,10 @@ private void scrollToBottom() {
391332
*/
392333
public void setServiceFactory(com.redis.vl.demo.rag.service.ServiceFactory serviceFactory) {
393334
this.serviceFactory = serviceFactory;
394-
updateStatusLabel("Connected to Redis. Select provider and enter API key to start.");
335+
settingsPanel.setStatus("Connected to Redis. Upload a PDF to start.");
395336
eventLogger.info(Category.SYSTEM, "Connected to Redis");
396337
}
397338

398-
/**
399-
* Updates the status label text.
400-
*
401-
* @param text Status text
402-
*/
403-
private void updateStatusLabel(String text) {
404-
if (statusLabel != null) {
405-
Platform.runLater(() -> statusLabel.setText(text));
406-
}
407-
}
408-
409339
/**
410340
* Shutdown executor and close resources.
411341
*/

0 commit comments

Comments
 (0)