vector-inspector 0.2.6__py3-none-any.whl → 0.2.7__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,13 +3,15 @@
3
3
  from typing import Optional, Dict, Any
4
4
  from PySide6.QtWidgets import (
5
5
  QWidget, QVBoxLayout, QHBoxLayout, QLabel,
6
- QGroupBox, QScrollArea, QFrame
6
+ QGroupBox, QScrollArea, QFrame, QPushButton
7
7
  )
8
8
  from PySide6.QtCore import Qt, QObject
9
+ from PySide6.QtWidgets import QDialog
9
10
 
10
11
  from vector_inspector.core.connections.base_connection import VectorDBConnection
11
12
  from vector_inspector.core.connections.chroma_connection import ChromaDBConnection
12
13
  from vector_inspector.core.connections.qdrant_connection import QdrantConnection
14
+ from vector_inspector.core.cache_manager import get_cache_manager
13
15
 
14
16
 
15
17
  class InfoPanel(QWidget):
@@ -18,7 +20,10 @@ class InfoPanel(QWidget):
18
20
  def __init__(self, connection: VectorDBConnection, parent=None):
19
21
  super().__init__(parent)
20
22
  self.connection = connection
23
+ self.connection_id: str = "" # Will be set when collection is set
21
24
  self.current_collection: str = ""
25
+ self.current_database: str = ""
26
+ self.cache_manager = get_cache_manager()
22
27
  self._setup_ui()
23
28
 
24
29
  def _setup_ui(self):
@@ -65,10 +70,31 @@ class InfoPanel(QWidget):
65
70
  self.distance_metric_label = self._create_info_row("Distance Metric:", "N/A")
66
71
  self.total_points_label = self._create_info_row("Total Points:", "0")
67
72
 
73
+ # Embedding model row with configure button
74
+ embedding_row = QWidget()
75
+ embedding_layout = QHBoxLayout(embedding_row)
76
+ embedding_layout.setContentsMargins(0, 2, 0, 2)
77
+
78
+ embedding_label = QLabel("<b>Embedding Model:</b>")
79
+ embedding_label.setMinimumWidth(150)
80
+ self.embedding_model_label = QLabel("Auto-detect")
81
+ self.embedding_model_label.setStyleSheet("color: gray;")
82
+ self.embedding_model_label.setWordWrap(True)
83
+
84
+ self.configure_embedding_btn = QPushButton("Configure...")
85
+ self.configure_embedding_btn.setMaximumWidth(100)
86
+ self.configure_embedding_btn.clicked.connect(self._configure_embedding_model)
87
+ self.configure_embedding_btn.setEnabled(False)
88
+
89
+ embedding_layout.addWidget(embedding_label)
90
+ embedding_layout.addWidget(self.embedding_model_label, 1)
91
+ embedding_layout.addWidget(self.configure_embedding_btn)
92
+
68
93
  collection_layout.addWidget(self.collection_name_label)
69
94
  collection_layout.addWidget(self.vector_dim_label)
70
95
  collection_layout.addWidget(self.distance_metric_label)
71
96
  collection_layout.addWidget(self.total_points_label)
97
+ collection_layout.addWidget(embedding_row)
72
98
 
73
99
  # Payload Schema subsection
74
100
  schema_label = QLabel("<b>Payload Schema:</b>")
@@ -200,7 +226,7 @@ class InfoPanel(QWidget):
200
226
  return
201
227
 
202
228
  try:
203
- # Get collection info
229
+ # Get collection info from database
204
230
  collection_info = self.connection.get_collection_info(self.current_collection)
205
231
 
206
232
  if not collection_info:
@@ -212,59 +238,18 @@ class InfoPanel(QWidget):
212
238
  self.provider_details_label.setText("N/A")
213
239
  return
214
240
 
215
- # Update basic info
216
- self._update_label(self.collection_name_label, self.current_collection)
217
-
218
- # Vector dimension
219
- vector_dim = collection_info.get("vector_dimension", "Unknown")
220
- self._update_label(self.vector_dim_label, str(vector_dim))
221
-
222
- # Distance metric
223
- distance = collection_info.get("distance_metric", "Unknown")
224
- self._update_label(self.distance_metric_label, distance)
225
-
226
- # Total points
227
- count = collection_info.get("count", 0)
228
- self._update_label(self.total_points_label, f"{count:,}")
241
+ # Display the info
242
+ self._display_collection_info(collection_info)
229
243
 
230
- # Metadata schema
231
- metadata_fields = collection_info.get("metadata_fields", [])
232
- if metadata_fields:
233
- schema_text = "\n".join([f"• {field}" for field in sorted(metadata_fields)])
234
- self.schema_label.setText(schema_text)
235
- self.schema_label.setStyleSheet("color: white; padding-left: 20px; font-family: monospace;")
236
- else:
237
- self.schema_label.setText("No metadata fields found")
238
- self.schema_label.setStyleSheet("color: gray; padding-left: 20px;")
239
-
240
- # Provider-specific details
241
- details_list = []
242
-
243
- if isinstance(self.connection, ChromaDBConnection):
244
- details_list.append("• Provider: ChromaDB")
245
- details_list.append("• Supports: Documents, Metadata, Embeddings")
246
- details_list.append("• Default embedding: all-MiniLM-L6-v2")
247
-
248
- elif isinstance(self.connection, QdrantConnection):
249
- details_list.append("• Provider: Qdrant")
250
- details_list.append("• Supports: Points, Payload, Vectors")
251
- # Get additional Qdrant-specific info if available
252
- if "config" in collection_info:
253
- config = collection_info["config"]
254
- if "hnsw_config" in config:
255
- hnsw = config["hnsw_config"]
256
- details_list.append(f"• HNSW M: {hnsw.get('m', 'N/A')}")
257
- details_list.append(f"• HNSW ef_construct: {hnsw.get('ef_construct', 'N/A')}")
258
- if "optimizer_config" in config:
259
- opt = config["optimizer_config"]
260
- details_list.append(f"• Indexing threshold: {opt.get('indexing_threshold', 'N/A')}")
261
-
262
- if details_list:
263
- self.provider_details_label.setText("\n".join(details_list))
264
- self.provider_details_label.setStyleSheet("color: white; padding-left: 20px; font-family: monospace;")
265
- else:
266
- self.provider_details_label.setText("No additional details available")
267
- self.provider_details_label.setStyleSheet("color: gray; padding-left: 20px;")
244
+ # Save to cache
245
+ if self.current_database and self.current_collection:
246
+ print(f"[InfoPanel] Saving collection info to cache: db='{self.current_database}', coll='{self.current_collection}'")
247
+ self.cache_manager.update(
248
+ self.current_database,
249
+ self.current_collection,
250
+ user_inputs={'collection_info': collection_info}
251
+ )
252
+ print(f"[InfoPanel] Saved collection info to cache.")
268
253
 
269
254
  except Exception as e:
270
255
  self._update_label(self.collection_name_label, self.current_collection)
@@ -275,9 +260,89 @@ class InfoPanel(QWidget):
275
260
  self.schema_label.setStyleSheet("color: red; padding-left: 20px;")
276
261
  self.provider_details_label.setText("N/A")
277
262
 
278
- def set_collection(self, collection_name: str):
263
+ def _display_collection_info(self, collection_info: Dict[str, Any]):
264
+ """Display collection information (from cache or fresh query)."""
265
+ # Update basic info
266
+ self._update_label(self.collection_name_label, self.current_collection)
267
+
268
+ # Vector dimension
269
+ vector_dim = collection_info.get("vector_dimension", "Unknown")
270
+ self._update_label(self.vector_dim_label, str(vector_dim))
271
+
272
+ # Enable configure button if we have a valid dimension
273
+ self.configure_embedding_btn.setEnabled(
274
+ vector_dim != "Unknown" and isinstance(vector_dim, int)
275
+ )
276
+
277
+ # Update embedding model display
278
+ self._update_embedding_model_display(collection_info)
279
+
280
+ # Distance metric
281
+ distance = collection_info.get("distance_metric", "Unknown")
282
+ self._update_label(self.distance_metric_label, distance)
283
+
284
+ # Total points
285
+ count = collection_info.get("count", 0)
286
+ self._update_label(self.total_points_label, f"{count:,}")
287
+
288
+ # Metadata schema
289
+ metadata_fields = collection_info.get("metadata_fields", [])
290
+ if metadata_fields:
291
+ schema_text = "\n".join([f"• {field}" for field in sorted(metadata_fields)])
292
+ self.schema_label.setText(schema_text)
293
+ self.schema_label.setStyleSheet("color: white; padding-left: 20px; font-family: monospace;")
294
+ else:
295
+ self.schema_label.setText("No metadata fields found")
296
+ self.schema_label.setStyleSheet("color: gray; padding-left: 20px;")
297
+
298
+ # Provider-specific details
299
+ details_list = []
300
+
301
+ if isinstance(self.connection, ChromaDBConnection):
302
+ details_list.append("• Provider: ChromaDB")
303
+ details_list.append("• Supports: Documents, Metadata, Embeddings")
304
+ details_list.append("• Default embedding: all-MiniLM-L6-v2")
305
+
306
+ elif isinstance(self.connection, QdrantConnection):
307
+ details_list.append("• Provider: Qdrant")
308
+ details_list.append("• Supports: Points, Payload, Vectors")
309
+ # Get additional Qdrant-specific info if available
310
+ if "config" in collection_info:
311
+ config = collection_info["config"]
312
+ if "hnsw_config" in config:
313
+ hnsw = config["hnsw_config"]
314
+ details_list.append(f"• HNSW M: {hnsw.get('m', 'N/A')}")
315
+ details_list.append(f"• HNSW ef_construct: {hnsw.get('ef_construct', 'N/A')}")
316
+ if "optimizer_config" in config:
317
+ opt = config["optimizer_config"]
318
+ details_list.append(f"• Indexing threshold: {opt.get('indexing_threshold', 'N/A')}")
319
+
320
+ if details_list:
321
+ self.provider_details_label.setText("\n".join(details_list))
322
+ self.provider_details_label.setStyleSheet("color: white; padding-left: 20px; font-family: monospace;")
323
+ else:
324
+ self.provider_details_label.setText("No additional details available")
325
+ self.provider_details_label.setStyleSheet("color: gray; padding-left: 20px;")
326
+
327
+ def set_collection(self, collection_name: str, database_name: str = ""):
279
328
  """Set the current collection and refresh its information."""
280
329
  self.current_collection = collection_name
330
+ # Always update database_name if provided
331
+ if database_name:
332
+ self.current_database = database_name
333
+ self.connection_id = database_name # database_name is the connection ID
334
+
335
+ print(f"[InfoPanel] Setting collection: db='{self.current_database}', coll='{collection_name}'")
336
+
337
+ # Check cache first for collection info
338
+ cached = self.cache_manager.get(self.current_database, self.current_collection)
339
+ if cached and hasattr(cached, 'user_inputs') and cached.user_inputs.get('collection_info'):
340
+ print(f"[InfoPanel] ✓ Cache HIT! Loading collection info from cache.")
341
+ collection_info = cached.user_inputs['collection_info']
342
+ self._display_collection_info(collection_info)
343
+ return
344
+
345
+ print(f"[InfoPanel] ✗ Cache MISS. Loading collection info from database...")
281
346
  self.refresh_collection_info()
282
347
 
283
348
  def _update_label(self, row_widget: QWidget, value: str):
@@ -285,3 +350,108 @@ class InfoPanel(QWidget):
285
350
  value_label = row_widget.property("value_label")
286
351
  if value_label and isinstance(value_label, QLabel):
287
352
  value_label.setText(value)
353
+
354
+ def _update_embedding_model_display(self, collection_info: Dict[str, Any]):
355
+ """Update the embedding model label based on current configuration."""
356
+ from ...services.settings_service import SettingsService
357
+
358
+ # Check if stored in collection metadata
359
+ if 'embedding_model' in collection_info:
360
+ model_name = collection_info['embedding_model']
361
+ model_type = collection_info.get('embedding_model_type', 'unknown')
362
+ self.embedding_model_label.setText(f"{model_name} ({model_type})")
363
+ self.embedding_model_label.setStyleSheet("color: lightgreen;")
364
+ return
365
+
366
+ # Check user settings
367
+ settings = SettingsService()
368
+ collection_models = settings.get('collection_embedding_models', {})
369
+ collection_key = f"{self.connection_id}:{self.current_collection}"
370
+
371
+ if collection_key in collection_models:
372
+ model_info = collection_models[collection_key]
373
+ model_name = model_info['model']
374
+ model_type = model_info.get('type', 'unknown')
375
+ self.embedding_model_label.setText(f"{model_name} ({model_type})")
376
+ self.embedding_model_label.setStyleSheet("color: lightblue;")
377
+ return
378
+
379
+ # No configuration - using auto-detect
380
+ self.embedding_model_label.setText("Auto-detect (dimension-based)")
381
+ self.embedding_model_label.setStyleSheet("color: orange;")
382
+
383
+ def _configure_embedding_model(self):
384
+ """Open dialog to configure embedding model for current collection."""
385
+ if not self.current_collection:
386
+ return
387
+
388
+ from ..dialogs.embedding_config_dialog import EmbeddingConfigDialog
389
+ from ...services.settings_service import SettingsService
390
+
391
+ # Get current collection info
392
+ collection_info = self.connection.get_collection_info(self.current_collection)
393
+ if not collection_info:
394
+ return
395
+
396
+ vector_dim = collection_info.get("vector_dimension")
397
+ if not vector_dim or vector_dim == "Unknown":
398
+ return
399
+
400
+ # Get current configuration if any
401
+ settings = SettingsService()
402
+ collection_models = settings.get('collection_embedding_models', {})
403
+ collection_key = f"{self.connection_id}:{self.current_collection}"
404
+
405
+ current_model = None
406
+ current_type = None
407
+
408
+ # Check metadata first
409
+ if 'embedding_model' in collection_info:
410
+ current_model = collection_info['embedding_model']
411
+ current_type = collection_info.get('embedding_model_type')
412
+ # Then check settings
413
+ elif collection_key in collection_models:
414
+ model_info = collection_models[collection_key]
415
+ current_model = model_info.get('model')
416
+ current_type = model_info.get('type')
417
+
418
+ # Open dialog
419
+ dialog = EmbeddingConfigDialog(
420
+ self.current_collection,
421
+ vector_dim,
422
+ current_model,
423
+ current_type,
424
+ self
425
+ )
426
+
427
+ result = dialog.exec()
428
+
429
+ if result == QDialog.DialogCode.Accepted:
430
+ # Save the configuration
431
+ selection = dialog.get_selection()
432
+ if selection:
433
+ model_name, model_type = selection
434
+
435
+ if collection_key not in collection_models:
436
+ collection_models[collection_key] = {}
437
+
438
+ collection_models[collection_key]['model'] = model_name
439
+ collection_models[collection_key]['type'] = model_type
440
+
441
+ settings.set('collection_embedding_models', collection_models)
442
+
443
+ # Refresh display
444
+ self._update_embedding_model_display(collection_info)
445
+
446
+ print(f"✓ Configured embedding model for '{self.current_collection}': {model_name} ({model_type})")
447
+
448
+ elif result == 2: # Clear configuration
449
+ # Remove from settings
450
+ if collection_key in collection_models:
451
+ del collection_models[collection_key]
452
+ settings.set('collection_embedding_models', collection_models)
453
+
454
+ # Refresh display
455
+ self._update_embedding_model_display(collection_info)
456
+
457
+ print(f"✓ Cleared embedding model configuration for '{self.current_collection}'")
@@ -16,6 +16,7 @@ from vector_inspector.ui.components.filter_builder import FilterBuilder
16
16
  from vector_inspector.services.import_export_service import ImportExportService
17
17
  from vector_inspector.services.filter_service import apply_client_side_filters
18
18
  from vector_inspector.services.settings_service import SettingsService
19
+ from vector_inspector.core.cache_manager import get_cache_manager, CacheEntry
19
20
  from PySide6.QtWidgets import QApplication
20
21
 
21
22
 
@@ -57,12 +58,14 @@ class MetadataView(QWidget):
57
58
  super().__init__(parent)
58
59
  self.connection = connection
59
60
  self.current_collection: str = ""
61
+ self.current_database: str = ""
60
62
  self.current_data: Optional[Dict[str, Any]] = None
61
63
  self.page_size = 50
62
64
  self.current_page = 0
63
65
  self.loading_dialog = LoadingDialog("Loading data...", self)
64
66
  self.settings_service = SettingsService()
65
67
  self.load_thread: Optional[DataLoadThread] = None
68
+ self.cache_manager = get_cache_manager()
66
69
 
67
70
  # Debounce timer for filter changes
68
71
  self.filter_reload_timer = QTimer()
@@ -107,8 +110,9 @@ class MetadataView(QWidget):
107
110
  controls_layout.addStretch()
108
111
 
109
112
  # Refresh button
110
- self.refresh_button = QPushButton("Refresh")
111
- self.refresh_button.clicked.connect(self._load_data)
113
+ self.refresh_button = QPushButton("🔄 Refresh")
114
+ self.refresh_button.clicked.connect(self._refresh_data)
115
+ self.refresh_button.setToolTip("Refresh data and clear cache")
112
116
  controls_layout.addWidget(self.refresh_button)
113
117
 
114
118
  # Add/Delete buttons
@@ -172,9 +176,40 @@ class MetadataView(QWidget):
172
176
  self.status_label.setStyleSheet("color: gray;")
173
177
  layout.addWidget(self.status_label)
174
178
 
175
- def set_collection(self, collection_name: str):
179
+ def set_collection(self, collection_name: str, database_name: str = ""):
176
180
  """Set the current collection to display."""
177
181
  self.current_collection = collection_name
182
+ # Always update database_name if provided (even if empty string on first call)
183
+ if database_name: # Only update if non-empty
184
+ self.current_database = database_name
185
+
186
+ # Debug: Check cache status
187
+ print(f"[MetadataView] Setting collection: db='{self.current_database}', coll='{collection_name}'")
188
+ print(f"[MetadataView] Cache enabled: {self.cache_manager.is_enabled()}")
189
+
190
+ # Check cache first
191
+ cached = self.cache_manager.get(self.current_database, self.current_collection)
192
+ if cached and cached.data:
193
+ print(f"[MetadataView] ✓ Cache HIT! Loading from cache.")
194
+ # Restore from cache
195
+ self.current_page = 0
196
+ self.current_data = cached.data
197
+ self._populate_table(cached.data)
198
+ self._update_pagination_controls()
199
+ self._update_filter_fields(cached.data)
200
+
201
+ # Restore UI state
202
+ if cached.scroll_position:
203
+ self.table.verticalScrollBar().setValue(cached.scroll_position)
204
+ if cached.search_query:
205
+ # Restore filter state if applicable
206
+ pass
207
+
208
+ self.status_label.setText(f"✓ Loaded from cache - {len(cached.data.get('ids', []))} items")
209
+ return
210
+
211
+ print(f"[MetadataView] ✗ Cache MISS. Loading from database...")
212
+ # Not in cache, load from database
178
213
  self.current_page = 0
179
214
 
180
215
  # Update filter builder with supported operators
@@ -246,6 +281,19 @@ class MetadataView(QWidget):
246
281
 
247
282
  # Update filter builder with available metadata fields
248
283
  self._update_filter_fields(data)
284
+
285
+ # Save to cache
286
+ if self.current_database and self.current_collection:
287
+ print(f"[MetadataView] Saving to cache: db='{self.current_database}', coll='{self.current_collection}'")
288
+ cache_entry = CacheEntry(
289
+ data=data,
290
+ scroll_position=self.table.verticalScrollBar().value(),
291
+ search_query=self.filter_builder.to_dict() if hasattr(self.filter_builder, 'to_dict') else ""
292
+ )
293
+ self.cache_manager.set(self.current_database, self.current_collection, cache_entry)
294
+ print(f"[MetadataView] ✓ Saved to cache. Total entries: {len(self.cache_manager._cache)}")
295
+ else:
296
+ print(f"[MetadataView] ✗ NOT saving to cache - db='{self.current_database}', coll='{self.current_collection}'")
249
297
 
250
298
  def _on_load_error(self, error_msg: str):
251
299
  """Handle error from background thread."""
@@ -365,6 +413,9 @@ class MetadataView(QWidget):
365
413
  )
366
414
 
367
415
  if success:
416
+ # Invalidate cache after adding item
417
+ if self.current_database and self.current_collection:
418
+ self.cache_manager.invalidate(self.current_database, self.current_collection)
368
419
  QMessageBox.information(self, "Success", "Item added successfully.")
369
420
  self._load_data()
370
421
  else:
@@ -399,6 +450,9 @@ class MetadataView(QWidget):
399
450
  if reply == QMessageBox.Yes:
400
451
  success = self.connection.delete_items(self.current_collection, ids=ids_to_delete)
401
452
  if success:
453
+ # Invalidate cache after deletion
454
+ if self.current_database and self.current_collection:
455
+ self.cache_manager.invalidate(self.current_database, self.current_collection)
402
456
  QMessageBox.information(self, "Success", "Items deleted successfully.")
403
457
  self._load_data()
404
458
  else:
@@ -422,6 +476,13 @@ class MetadataView(QWidget):
422
476
  self.current_page = 0
423
477
  self._load_data()
424
478
 
479
+ def _refresh_data(self):
480
+ """Refresh data and invalidate cache."""
481
+ if self.current_database and self.current_collection:
482
+ self.cache_manager.invalidate(self.current_database, self.current_collection)
483
+ self.current_page = 0
484
+ self._load_data()
485
+
425
486
  def _on_row_double_clicked(self, index):
426
487
  """Handle double-click on a row to edit item."""
427
488
  if not self.current_collection or not self.current_data:
@@ -462,6 +523,9 @@ class MetadataView(QWidget):
462
523
  )
463
524
 
464
525
  if success:
526
+ # Invalidate cache after updating item
527
+ if self.current_database and self.current_collection:
528
+ self.cache_manager.invalidate(self.current_database, self.current_collection)
465
529
  QMessageBox.information(self, "Success", "Item updated successfully.")
466
530
  self._load_data()
467
531
  else:
@@ -653,6 +717,10 @@ class MetadataView(QWidget):
653
717
  self.loading_dialog.hide_loading()
654
718
 
655
719
  if success:
720
+ # Invalidate cache after import
721
+ if self.current_database and self.current_collection:
722
+ self.cache_manager.invalidate(self.current_database, self.current_collection)
723
+
656
724
  # Save the directory for next time
657
725
  from pathlib import Path
658
726
  self.settings_service.set("last_import_export_dir", str(Path(file_path).parent))
@@ -12,6 +12,7 @@ from vector_inspector.core.connections.base_connection import VectorDBConnection
12
12
  from vector_inspector.ui.components.filter_builder import FilterBuilder
13
13
  from vector_inspector.ui.components.loading_dialog import LoadingDialog
14
14
  from vector_inspector.services.filter_service import apply_client_side_filters
15
+ from vector_inspector.core.cache_manager import get_cache_manager, CacheEntry
15
16
 
16
17
 
17
18
  class SearchView(QWidget):
@@ -21,8 +22,10 @@ class SearchView(QWidget):
21
22
  super().__init__(parent)
22
23
  self.connection = connection
23
24
  self.current_collection: str = ""
25
+ self.current_database: str = ""
24
26
  self.search_results: Optional[Dict[str, Any]] = None
25
27
  self.loading_dialog = LoadingDialog("Searching...", self)
28
+ self.cache_manager = get_cache_manager()
26
29
 
27
30
  self._setup_ui()
28
31
 
@@ -112,12 +115,30 @@ class SearchView(QWidget):
112
115
 
113
116
  layout.addWidget(splitter)
114
117
 
115
- def set_collection(self, collection_name: str):
118
+ def set_collection(self, collection_name: str, database_name: str = ""):
116
119
  """Set the current collection to search."""
117
120
  self.current_collection = collection_name
121
+ # Always update database_name if provided (even if empty string on first call)
122
+ if database_name: # Only update if non-empty
123
+ self.current_database = database_name
124
+
125
+ print(f"[SearchView] Setting collection: db='{self.current_database}', coll='{collection_name}'")
126
+
127
+ # Check cache first
128
+ cached = self.cache_manager.get(self.current_database, self.current_collection)
129
+ if cached:
130
+ print(f"[SearchView] ✓ Cache HIT! Restoring search state.")
131
+ # Restore search query and results from cache
132
+ if cached.search_query:
133
+ self.query_input.setPlainText(cached.search_query)
134
+ if cached.search_results:
135
+ self.search_results = cached.search_results
136
+ self._display_results(cached.search_results)
137
+ return
138
+
139
+ print(f"[SearchView] ✗ Cache MISS or no cached search.")
140
+ # Not in cache, clear form
118
141
  self.search_results = None
119
-
120
- # Clear search form inputs
121
142
  self.query_input.clear()
122
143
  self.results_table.setRowCount(0)
123
144
  self.results_status.setText(f"Collection: {collection_name}")
@@ -196,6 +217,12 @@ class SearchView(QWidget):
196
217
  self.results_table.setRowCount(0)
197
218
  return
198
219
 
220
+ # Check if results have the expected structure
221
+ if not results.get("ids") or not isinstance(results["ids"], list) or len(results["ids"]) == 0:
222
+ self.results_status.setText("No results found or query failed")
223
+ self.results_table.setRowCount(0)
224
+ return
225
+
199
226
  # Apply client-side filters if any
200
227
  if client_filters and results:
201
228
  # Restructure results for filtering
@@ -219,6 +246,19 @@ class SearchView(QWidget):
219
246
  self.search_results = results
220
247
  self._display_results(results)
221
248
 
249
+ # Save to cache
250
+ if self.current_database and self.current_collection:
251
+ self.cache_manager.update(
252
+ self.current_database,
253
+ self.current_collection,
254
+ search_query=query_text,
255
+ search_results=results,
256
+ user_inputs={
257
+ 'n_results': n_results,
258
+ 'filters': self.filter_builder.to_dict() if hasattr(self.filter_builder, 'to_dict') else {}
259
+ }
260
+ )
261
+
222
262
  def _display_results(self, results: Dict[str, Any]):
223
263
  """Display search results in table."""
224
264
  ids = results.get("ids", [[]])[0]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: vector-inspector
3
- Version: 0.2.6
3
+ Version: 0.2.7
4
4
  Summary: A comprehensive desktop application for visualizing, querying, and managing vector database data
5
5
  Author-Email: Anthony Dawson <anthonypdawson+github@gmail.com>
6
6
  License: MIT
@@ -22,6 +22,8 @@ Requires-Dist: sentence-transformers>=2.2.0
22
22
  Requires-Dist: fastembed>=0.7.4
23
23
  Requires-Dist: pyarrow>=14.0.0
24
24
  Requires-Dist: pinecone>=8.0.0
25
+ Requires-Dist: keyring>=25.7.0
26
+ Requires-Dist: hf-xet>=1.2.0
25
27
  Description-Content-Type: text/markdown
26
28
 
27
29
 
@@ -0,0 +1,45 @@
1
+ vector_inspector-0.2.7.dist-info/METADATA,sha256=mahmP5eIlgej1osckY_twV3dQhSnX7BT20B1ivrsiu4,9684
2
+ vector_inspector-0.2.7.dist-info/WHEEL,sha256=tsUv_t7BDeJeRHaSrczbGeuK-TtDpGsWi_JfpzD255I,90
3
+ vector_inspector-0.2.7.dist-info/entry_points.txt,sha256=u96envMI2NFImZUJDFutiiWl7ZoHrrev9joAgtyvTxo,80
4
+ vector_inspector/__init__.py,sha256=Q8XbXn98o0eliQWPePhy-aGUz2KNnVg7bQq-sBPl7zQ,119
5
+ vector_inspector/__main__.py,sha256=Vdhw8YA1K3wPMlbJQYL5WqvRzAKVeZ16mZQFO9VRmCo,62
6
+ vector_inspector/core/__init__.py,sha256=hjOqiJwF1P0rXjiOKhK4qDTvBY7G3m4kq8taH-gKrFM,57
7
+ vector_inspector/core/cache_manager.py,sha256=cHdbIYR-eS9vLLTqvq4Xejyi5Z7Fm9DqMhb_PMZjnjY,5695
8
+ vector_inspector/core/connection_manager.py,sha256=WwiVedHWTfqIuJKV7D52bYEupmpnnSPKcgG1odJCJjs,10003
9
+ vector_inspector/core/connections/__init__.py,sha256=cCwDy69Jy8ajiFQICcWfPnGoMtfrq69brzXJ4sVYCQ0,271
10
+ vector_inspector/core/connections/base_connection.py,sha256=PiwVlrk-eUKSka6gmE2GSyml0xe48PrIWAcKhBJMBv8,7131
11
+ vector_inspector/core/connections/chroma_connection.py,sha256=eBMrJQg_J01mWrEmKdK_p3-zJRNA7sOuFWd_bYit3y8,18298
12
+ vector_inspector/core/connections/qdrant_connection.py,sha256=m0EFgSbWRVh3uv_JkbB0WEotu20G967TrHvwJrtedO4,31861
13
+ vector_inspector/core/connections/template_connection.py,sha256=wsJiE4ma3cLUXk2eW5rnLMS5wG8JTekgEn46lHHQNoc,10642
14
+ vector_inspector/core/embedding_utils.py,sha256=LJK3YXwng7nO3csSkPHrixXIXBFf06tspq1ZSxSGgsM,4933
15
+ vector_inspector/main.py,sha256=puu1Fur298j6H8fG3_wF85RMhi4tjLZ0Are16kloMqM,479
16
+ vector_inspector/services/__init__.py,sha256=QLgH7oybjHuEYDFNiBgmJxvSpgAzHEuBEPXa3SKJb_I,67
17
+ vector_inspector/services/backup_restore_service.py,sha256=3lZ25FXG6DHQtJNiaIe8ZJgvxMRWuPrIJIBKPGWe7gA,10618
18
+ vector_inspector/services/credential_service.py,sha256=pdpglvLABnYQRvklgK38iPyG91IZ9Nm8WQFT9V0LofM,4354
19
+ vector_inspector/services/filter_service.py,sha256=xDrMxNWsYzRcR1n0Fd-yp6Fo-4aLbVIDkhj2GKmrw5o,2370
20
+ vector_inspector/services/import_export_service.py,sha256=OPCrBXBewCznu5o8wFGvduU0jGZAcBvp_Fpv15kdoJ4,10712
21
+ vector_inspector/services/profile_service.py,sha256=Uhl9urxeSRI0p-mfaYgBrMI99byNIxJSodcXOgD_ybw,13408
22
+ vector_inspector/services/settings_service.py,sha256=g07fyPPgI4VvidJ_xgpuj5id-M1pDXvIh6_ZXnkUqAI,2914
23
+ vector_inspector/services/visualization_service.py,sha256=mHI4qxT-V4R1kcOhE508vFTZ0HcmQICHvJ7dIoRracQ,4373
24
+ vector_inspector/ui/__init__.py,sha256=262ZiXO6Luk8vZnhCIoYxOtGiny0bXK-BTKjxUNBx-w,43
25
+ vector_inspector/ui/components/__init__.py,sha256=S-GWU1P820dJ6mHmeeBEy-CGF9fjpBeNf8vrbhRlFMk,30
26
+ vector_inspector/ui/components/backup_restore_dialog.py,sha256=CrZ2u8vXzggv3aBkYR4FulpY74oZWMLW5BHU4dMiWug,13073
27
+ vector_inspector/ui/components/connection_manager_panel.py,sha256=6d_uNaaNaayYz4HV3LndiQsYFISpsSCwj4-sN-Qt8uc,12963
28
+ vector_inspector/ui/components/filter_builder.py,sha256=NSR_hp-rzUZVAca6dIJhTxZA3igOKFM1g-YXiYPhFos,13360
29
+ vector_inspector/ui/components/item_dialog.py,sha256=VMwehEjQ6xrdxWygR9J-hHsLfzOVb_E3ePUGYO_c7XA,3951
30
+ vector_inspector/ui/components/loading_dialog.py,sha256=YEKYGU-R-Zz4CjXSArJtkNxgTy4O9hI5Bbt6qlIzD8U,1018
31
+ vector_inspector/ui/components/profile_manager_panel.py,sha256=mXs2vOazUbmqzF7SNABtdKmeys6RjwtcgKaEKYK5MuI,18702
32
+ vector_inspector/ui/dialogs/__init__.py,sha256=U49PF3Jn57qkdjJ0hjMnhvQKv2xeB5e2SretF_24T_U,136
33
+ vector_inspector/ui/dialogs/cross_db_migration.py,sha256=fIhnD5kJeqSPe5bwf2KFkjk3dQY_kda0zZVsPHtB1w8,14500
34
+ vector_inspector/ui/dialogs/embedding_config_dialog.py,sha256=g-MVuVE4_ZAZjA-yVa6ZaFU2-vrEQnVYiHqEmjoBqmM,7358
35
+ vector_inspector/ui/main_window.py,sha256=OPuAODir7LPUenNetNYwhwQ49p_zFA3axx014yQhJSg,24554
36
+ vector_inspector/ui/views/__init__.py,sha256=FeMtVzSbVFBMjdwLQSQqD0FRW4ieJ4ZKXtTBci2e_bw,30
37
+ vector_inspector/ui/views/collection_browser.py,sha256=oG9_YGPoVuMs-f_zSd4EcITmEU9caxvwuubsFUrNf-c,3991
38
+ vector_inspector/ui/views/connection_view.py,sha256=CpHPSDee3RhH3_wNu62RI3Fn5ffBIGt_Va5iC0p1IoY,18116
39
+ vector_inspector/ui/views/info_panel.py,sha256=tNKlWsb5KalxstOmC0xYY5UH4pW4qY_ysJcRrPQMcWk,21624
40
+ vector_inspector/ui/views/metadata_view.py,sha256=dIe7aH47xd6TBK2g-WogR2Ts0enHGeSAEUlu48sdTF8,30535
41
+ vector_inspector/ui/views/search_view.py,sha256=IMPZE7SPGoNuVbbffIjv1HxM-qZpmx0L1qtB9EYizjI,12474
42
+ vector_inspector/ui/views/visualization_view.py,sha256=uPOUpKJ00eFqCCohYEfMD8D9lNv2cy2FymPHouXK0Go,9163
43
+ vector_inspector/utils/__init__.py,sha256=jhHBQC8C8bfhNlf6CAt07ejjStp_YAyleaYr2dm0Dk0,38
44
+ vector_inspector/utils/lazy_imports.py,sha256=2XZ3ZnwTvZ5vvrh36nJ_TUjwwkgjoAED6i6P9yctvt0,1211
45
+ vector_inspector-0.2.7.dist-info/RECORD,,