vector-inspector 0.3.9__py3-none-any.whl → 0.3.12__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.
Files changed (33) hide show
  1. vector_inspector/__init__.py +10 -1
  2. vector_inspector/core/connection_manager.py +91 -19
  3. vector_inspector/core/connections/base_connection.py +43 -43
  4. vector_inspector/core/connections/chroma_connection.py +1 -1
  5. vector_inspector/core/connections/pgvector_connection.py +12 -172
  6. vector_inspector/core/connections/pinecone_connection.py +596 -99
  7. vector_inspector/core/connections/qdrant_connection.py +35 -44
  8. vector_inspector/core/embedding_utils.py +14 -5
  9. vector_inspector/core/logging.py +3 -1
  10. vector_inspector/extensions/__init__.py +6 -0
  11. vector_inspector/extensions/telemetry_settings_panel.py +25 -0
  12. vector_inspector/main.py +45 -2
  13. vector_inspector/services/backup_restore_service.py +228 -15
  14. vector_inspector/services/settings_service.py +79 -19
  15. vector_inspector/services/telemetry_service.py +88 -0
  16. vector_inspector/ui/components/backup_restore_dialog.py +215 -101
  17. vector_inspector/ui/components/connection_manager_panel.py +155 -14
  18. vector_inspector/ui/dialogs/cross_db_migration.py +126 -99
  19. vector_inspector/ui/dialogs/settings_dialog.py +13 -6
  20. vector_inspector/ui/loading_screen.py +169 -0
  21. vector_inspector/ui/main_window.py +44 -19
  22. vector_inspector/ui/services/dialog_service.py +1 -0
  23. vector_inspector/ui/views/collection_browser.py +36 -34
  24. vector_inspector/ui/views/connection_view.py +7 -1
  25. vector_inspector/ui/views/info_panel.py +118 -52
  26. vector_inspector/ui/views/metadata_view.py +30 -31
  27. vector_inspector/ui/views/search_view.py +20 -19
  28. vector_inspector/ui/views/visualization_view.py +18 -15
  29. {vector_inspector-0.3.9.dist-info → vector_inspector-0.3.12.dist-info}/METADATA +19 -37
  30. {vector_inspector-0.3.9.dist-info → vector_inspector-0.3.12.dist-info}/RECORD +33 -29
  31. {vector_inspector-0.3.9.dist-info → vector_inspector-0.3.12.dist-info}/WHEEL +1 -1
  32. vector_inspector-0.3.12.dist-info/licenses/LICENSE +1 -0
  33. {vector_inspector-0.3.9.dist-info → vector_inspector-0.3.12.dist-info}/entry_points.txt +0 -0
@@ -1,33 +1,34 @@
1
1
  """Information panel for displaying database and collection metadata."""
2
2
 
3
- from typing import Optional, Dict, Any
3
+ from typing import Any, Optional
4
+
5
+ from PySide6.QtCore import Qt
4
6
  from PySide6.QtWidgets import (
5
- QWidget,
6
- QVBoxLayout,
7
+ QApplication,
8
+ QDialog,
9
+ QGroupBox,
7
10
  QHBoxLayout,
8
11
  QLabel,
9
- QGroupBox,
10
- QScrollArea,
11
- QFrame,
12
12
  QPushButton,
13
+ QScrollArea,
14
+ QVBoxLayout,
15
+ QWidget,
13
16
  )
14
- from PySide6.QtCore import Qt, QObject
15
- from PySide6.QtWidgets import QDialog
16
- from PySide6.QtWidgets import QApplication
17
17
 
18
- from vector_inspector.core.connections.base_connection import VectorDBConnection
18
+ from vector_inspector.core.cache_manager import get_cache_manager
19
+ from vector_inspector.core.connection_manager import ConnectionInstance
19
20
  from vector_inspector.core.connections.chroma_connection import ChromaDBConnection
20
- from vector_inspector.core.connections.qdrant_connection import QdrantConnection
21
21
  from vector_inspector.core.connections.pinecone_connection import PineconeConnection
22
- from vector_inspector.core.cache_manager import get_cache_manager
22
+ from vector_inspector.core.connections.qdrant_connection import QdrantConnection
23
23
  from vector_inspector.core.logging import log_info
24
24
 
25
25
 
26
26
  class InfoPanel(QWidget):
27
27
  """Panel for displaying database and collection information."""
28
28
 
29
- def __init__(self, connection: VectorDBConnection, parent=None):
29
+ def __init__(self, connection: Optional[ConnectionInstance] = None, parent=None):
30
30
  super().__init__(parent)
31
+ # Expects a ConnectionInstance wrapper.
31
32
  self.connection = connection
32
33
  self.connection_id: str = "" # Will be set when collection is set
33
34
  self.current_collection: str = ""
@@ -149,8 +150,29 @@ class InfoPanel(QWidget):
149
150
  """Clear the embedding model configuration for this collection (reset to autodetect)."""
150
151
  from vector_inspector.services.settings_service import SettingsService
151
152
 
153
+ # Ensure we have a valid connection_id
154
+ effective_connection_id = self.connection_id or (
155
+ self.connection.id if self.connection else None
156
+ )
157
+
158
+ if not effective_connection_id:
159
+ log_info("Cannot clear embedding model: no connection_id available")
160
+ return
161
+
152
162
  settings = SettingsService()
153
- settings.remove_embedding_model(self.connection_id, self.current_collection)
163
+ settings.remove_embedding_model(
164
+ self.connection.name if self.connection else "",
165
+ self.current_collection,
166
+ )
167
+
168
+ # Clear cache to ensure fresh collection info on next load
169
+ if effective_connection_id and self.current_collection:
170
+ self.cache_manager.invalidate(effective_connection_id, self.current_collection)
171
+ log_info(
172
+ "Cleared cache for collection after clearing embedding model: %s",
173
+ self.current_collection,
174
+ )
175
+
154
176
  # Refresh display (force reload collection info)
155
177
  if self.current_collection:
156
178
  self.set_collection(self.current_collection, self.current_database)
@@ -163,13 +185,21 @@ class InfoPanel(QWidget):
163
185
  """Update the clear button state based on current configuration."""
164
186
  from vector_inspector.services.settings_service import SettingsService
165
187
 
166
- if not self.connection_id or not self.current_collection:
188
+ # Ensure we have valid identifiers
189
+ effective_connection_id = self.connection_id or (
190
+ self.connection.id if self.connection else None
191
+ )
192
+
193
+ if not effective_connection_id or not self.current_collection:
167
194
  self.clear_embedding_btn.setEnabled(False)
168
195
  return
169
196
 
170
197
  # Check if there's a user-configured model in settings
171
198
  settings = SettingsService()
172
- model_info = settings.get_embedding_model(self.connection_id, self.current_collection)
199
+ model_info = settings.get_embedding_model(
200
+ self.connection.name if self.connection else "",
201
+ self.current_collection,
202
+ )
173
203
 
174
204
  # Enable button if there's a user-configured model
175
205
  self.clear_embedding_btn.setEnabled(model_info is not None)
@@ -213,50 +243,48 @@ class InfoPanel(QWidget):
213
243
  return
214
244
 
215
245
  # Get provider name
216
- provider_name = self.connection.__class__.__name__.replace("Connection", "")
246
+ # Extract the underlying database connection from ConnectionInstance wrapper.
247
+ backend = getattr(self.connection, "database", self.connection)
248
+ provider_name = (
249
+ backend.__class__.__name__.replace("Connection", "") if backend else "Unknown"
250
+ )
217
251
  self._update_label(self.provider_label, provider_name)
218
252
 
219
253
  # Get connection details
220
- if isinstance(self.connection, ChromaDBConnection):
221
- if self.connection.path:
254
+ if isinstance(backend, ChromaDBConnection):
255
+ if getattr(backend, "path", None):
222
256
  self._update_label(self.connection_type_label, "Persistent (Local)")
223
- self._update_label(self.endpoint_label, self.connection.path)
224
- elif self.connection.host and self.connection.port:
257
+ self._update_label(self.endpoint_label, backend.path)
258
+ elif getattr(backend, "host", None) and getattr(backend, "port", None):
225
259
  self._update_label(self.connection_type_label, "HTTP (Remote)")
226
- self._update_label(
227
- self.endpoint_label, f"{self.connection.host}:{self.connection.port}"
228
- )
260
+ self._update_label(self.endpoint_label, f"{backend.host}:{backend.port}")
229
261
  else:
230
262
  self._update_label(self.connection_type_label, "Ephemeral (In-Memory)")
231
263
  self._update_label(self.endpoint_label, "N/A")
232
264
  self._update_label(self.api_key_label, "Not required")
233
-
234
- elif isinstance(self.connection, QdrantConnection):
235
- if self.connection.path:
265
+ elif isinstance(backend, QdrantConnection):
266
+ if getattr(backend, "path", None):
236
267
  self._update_label(self.connection_type_label, "Embedded (Local)")
237
- self._update_label(self.endpoint_label, self.connection.path)
238
- elif self.connection.url:
268
+ self._update_label(self.endpoint_label, backend.path)
269
+ elif getattr(backend, "url", None):
239
270
  self._update_label(self.connection_type_label, "Remote (URL)")
240
- self._update_label(self.endpoint_label, self.connection.url)
241
- elif self.connection.host:
271
+ self._update_label(self.endpoint_label, backend.url)
272
+ elif getattr(backend, "host", None):
242
273
  self._update_label(self.connection_type_label, "Remote (Host)")
243
- self._update_label(
244
- self.endpoint_label, f"{self.connection.host}:{self.connection.port}"
245
- )
274
+ self._update_label(self.endpoint_label, f"{backend.host}:{backend.port}")
246
275
  else:
247
276
  self._update_label(self.connection_type_label, "In-Memory")
248
277
  self._update_label(self.endpoint_label, "N/A")
249
-
250
- if self.connection.api_key:
278
+ if getattr(backend, "api_key", None):
251
279
  self._update_label(self.api_key_label, "Present (hidden)")
252
280
  else:
253
281
  self._update_label(self.api_key_label, "Not configured")
254
282
 
255
- elif isinstance(self.connection, PineconeConnection):
283
+ elif isinstance(backend, PineconeConnection):
284
+ # backend already assigned above
256
285
  self._update_label(self.connection_type_label, "Cloud")
257
286
  self._update_label(self.endpoint_label, "Pinecone Cloud")
258
-
259
- if self.connection.api_key:
287
+ if getattr(backend, "api_key", None):
260
288
  self._update_label(self.api_key_label, "Present (hidden)")
261
289
  else:
262
290
  self._update_label(self.api_key_label, "Not configured")
@@ -274,7 +302,7 @@ class InfoPanel(QWidget):
274
302
  try:
275
303
  collections = self.connection.list_collections()
276
304
  self._update_label(self.collections_count_label, str(len(collections)))
277
- except Exception as e:
305
+ except Exception:
278
306
  self._update_label(self.collections_count_label, "Error")
279
307
 
280
308
  def refresh_collection_info(self):
@@ -323,11 +351,11 @@ class InfoPanel(QWidget):
323
351
  self._update_label(self.vector_dim_label, "Error")
324
352
  self._update_label(self.distance_metric_label, "Error")
325
353
  self._update_label(self.total_points_label, "Error")
326
- self.schema_label.setText(f"Error: {str(e)}")
354
+ self.schema_label.setText(f"Error: {e!s}")
327
355
  self.schema_label.setStyleSheet("color: red; padding-left: 20px;")
328
356
  self.provider_details_label.setText("N/A")
329
357
 
330
- def _display_collection_info(self, collection_info: Dict[str, Any]):
358
+ def _display_collection_info(self, collection_info: dict[str, Any]):
331
359
  """Display collection information (from cache or fresh query)."""
332
360
  # Update basic info
333
361
  self._update_label(self.collection_name_label, self.current_collection)
@@ -395,6 +423,11 @@ class InfoPanel(QWidget):
395
423
  details_list.append("• Provider: Pinecone")
396
424
  details_list.append("• Supports: Vectors, Metadata")
397
425
  details_list.append("• Cloud-hosted vector database")
426
+ # Check if using Pinecone-hosted embedding model
427
+ if collection_info.get("embedding_model_type") == "pinecone-hosted":
428
+ hosted_model = collection_info.get("embedding_model", "Unknown")
429
+ details_list.append(f"• Hosted Model: {hosted_model}")
430
+ details_list.append("• Supports text-based queries (no local embedding needed)")
398
431
  # Add Pinecone-specific info if available
399
432
  if "host" in collection_info:
400
433
  details_list.append(f"• Host: {collection_info['host']}")
@@ -443,7 +476,7 @@ class InfoPanel(QWidget):
443
476
  if value_label and isinstance(value_label, QLabel):
444
477
  value_label.setText(value)
445
478
 
446
- def _update_embedding_model_display(self, collection_info: Dict[str, Any]):
479
+ def _update_embedding_model_display(self, collection_info: dict[str, Any]):
447
480
  """Update the embedding model label based on current configuration."""
448
481
  from vector_inspector.services.settings_service import SettingsService
449
482
 
@@ -459,20 +492,28 @@ class InfoPanel(QWidget):
459
492
  self.clear_embedding_btn.setEnabled(True)
460
493
  return
461
494
 
495
+ # Ensure we have a valid connection_id for settings lookup
496
+ # Fallback to connection.id if connection_id not set
497
+ effective_connection_id = self.connection_id or (
498
+ self.connection.id if self.connection else None
499
+ )
500
+
462
501
  # Try to get from connection using the helper method
463
502
  if self.connection and self.current_collection:
464
- detected_model = self.connection.get_embedding_model(
465
- self.current_collection, self.connection_id
466
- )
503
+ detected_model = self.connection.get_embedding_model(self.current_collection)
467
504
  if detected_model:
468
505
  self.embedding_model_label.setText(f"{detected_model} (detected)")
469
506
  self.embedding_model_label.setStyleSheet("color: lightgreen;")
470
507
  self.clear_embedding_btn.setEnabled(False)
471
508
  return
472
509
 
473
- # Check user settings
510
+ # Check user settings directly
474
511
  settings = SettingsService()
475
- model_info = settings.get_embedding_model(self.connection_id, self.current_collection)
512
+ profile_name = self.connection.name if self.connection else ""
513
+ model_info = settings.get_embedding_model(
514
+ profile_name,
515
+ self.current_collection,
516
+ )
476
517
 
477
518
  if model_info:
478
519
  model_name = model_info["model"]
@@ -492,6 +533,14 @@ class InfoPanel(QWidget):
492
533
  if not self.current_collection:
493
534
  return
494
535
 
536
+ # Ensure we have a valid connection_id
537
+ effective_connection_id = self.connection_id or (
538
+ self.connection.id if self.connection else None
539
+ )
540
+
541
+ if not effective_connection_id:
542
+ return
543
+
495
544
  # Show loading immediately; preparing can touch DB/registry
496
545
  from vector_inspector.ui.components.loading_dialog import LoadingDialog
497
546
 
@@ -499,8 +548,8 @@ class InfoPanel(QWidget):
499
548
  loading.show_loading("Preparing model configuration...")
500
549
  QApplication.processEvents()
501
550
 
502
- from vector_inspector.ui.dialogs import ProviderTypeDialog, EmbeddingConfigDialog
503
551
  from vector_inspector.services.settings_service import SettingsService
552
+ from vector_inspector.ui.dialogs import EmbeddingConfigDialog, ProviderTypeDialog
504
553
 
505
554
  # Get current collection info
506
555
  try:
@@ -527,7 +576,10 @@ class InfoPanel(QWidget):
527
576
  current_type = collection_info.get("embedding_model_type", "stored")
528
577
  # Then check settings
529
578
  else:
530
- model_info = settings.get_embedding_model(self.connection_id, self.current_collection)
579
+ model_info = settings.get_embedding_model(
580
+ self.connection.name if self.connection else "",
581
+ self.current_collection,
582
+ )
531
583
  if model_info:
532
584
  current_model = model_info.get("model")
533
585
  current_type = model_info.get("type")
@@ -558,9 +610,20 @@ class InfoPanel(QWidget):
558
610
  if selection:
559
611
  model_name, model_type = selection
560
612
  settings.save_embedding_model(
561
- self.connection_id, self.current_collection, model_name, model_type
613
+ self.connection.name if self.connection else "",
614
+ self.current_collection,
615
+ model_name,
616
+ model_type,
562
617
  )
563
618
 
619
+ # Clear cache to ensure fresh collection info on next load
620
+ if effective_connection_id and self.current_collection:
621
+ self.cache_manager.invalidate(effective_connection_id, self.current_collection)
622
+ log_info(
623
+ "Cleared cache for collection after configuring embedding model: %s",
624
+ self.current_collection,
625
+ )
626
+
564
627
  # Update the display immediately to show new model
565
628
  self.embedding_model_label.setText(f"{model_name} ({model_type})")
566
629
  self.embedding_model_label.setStyleSheet("color: lightblue;")
@@ -577,7 +640,10 @@ class InfoPanel(QWidget):
577
640
 
578
641
  elif result == 2: # Clear configuration
579
642
  # Remove from settings using the new SettingsService method
580
- settings.remove_embedding_model(self.connection_id, self.current_collection)
643
+ settings.remove_embedding_model(
644
+ self.connection.name if self.connection else "",
645
+ self.current_collection,
646
+ )
581
647
 
582
648
  # Refresh display
583
649
  self._update_embedding_model_display(collection_info)
@@ -1,38 +1,36 @@
1
1
  """Metadata browsing and data view."""
2
2
 
3
- from typing import Optional, Dict, Any, List
3
+ import math
4
+ from typing import Any, Optional
5
+
6
+ from PySide6.QtCore import Qt, QThread, QTimer, Signal
4
7
  from PySide6.QtWidgets import (
5
- QWidget,
6
- QVBoxLayout,
7
- QHBoxLayout,
8
- QTableWidget,
9
- QTableWidgetItem,
10
- QPushButton,
8
+ QApplication,
11
9
  QCheckBox,
12
- QLabel,
13
- QSpinBox,
14
- QLineEdit,
15
- QComboBox,
16
- QGroupBox,
17
- QHeaderView,
18
- QMessageBox,
19
10
  QDialog,
20
11
  QFileDialog,
12
+ QGroupBox,
13
+ QHBoxLayout,
14
+ QLabel,
21
15
  QMenu,
16
+ QMessageBox,
17
+ QPushButton,
18
+ QSpinBox,
19
+ QTableWidget,
20
+ QTableWidgetItem,
21
+ QVBoxLayout,
22
+ QWidget,
22
23
  )
23
- from PySide6.QtCore import Qt, QTimer, QThread, Signal
24
- import math
25
24
 
26
- from vector_inspector.core.connections.base_connection import VectorDBConnection
27
- from vector_inspector.ui.components.item_dialog import ItemDialog
28
- from vector_inspector.ui.components.loading_dialog import LoadingDialog
29
- from vector_inspector.ui.components.filter_builder import FilterBuilder
30
- from vector_inspector.services.import_export_service import ImportExportService
25
+ from vector_inspector.core.cache_manager import CacheEntry, get_cache_manager
26
+ from vector_inspector.core.connection_manager import ConnectionInstance
27
+ from vector_inspector.core.logging import log_info
31
28
  from vector_inspector.services.filter_service import apply_client_side_filters
29
+ from vector_inspector.services.import_export_service import ImportExportService
32
30
  from vector_inspector.services.settings_service import SettingsService
33
- from vector_inspector.core.cache_manager import get_cache_manager, CacheEntry
34
- from PySide6.QtWidgets import QApplication
35
- from vector_inspector.core.logging import log_info
31
+ from vector_inspector.ui.components.filter_builder import FilterBuilder
32
+ from vector_inspector.ui.components.item_dialog import ItemDialog
33
+ from vector_inspector.ui.components.loading_dialog import LoadingDialog
36
34
 
37
35
 
38
36
  class DataLoadThread(QThread):
@@ -66,12 +64,13 @@ class DataLoadThread(QThread):
66
64
  class MetadataView(QWidget):
67
65
  """View for browsing collection data and metadata."""
68
66
 
69
- def __init__(self, connection: VectorDBConnection, parent=None):
67
+ def __init__(self, connection: Optional[ConnectionInstance] = None, parent=None):
70
68
  super().__init__(parent)
69
+ # Expects a ConnectionInstance wrapper.
71
70
  self.connection = connection
72
71
  self.current_collection: str = ""
73
72
  self.current_database: str = ""
74
- self.current_data: Optional[Dict[str, Any]] = None
73
+ self.current_data: Optional[dict[str, Any]] = None
75
74
  self.page_size = 50
76
75
  self.current_page = 0
77
76
  self.loading_dialog = LoadingDialog("Loading data...", self)
@@ -308,7 +307,7 @@ class MetadataView(QWidget):
308
307
  self.load_thread.error.connect(self._on_load_error)
309
308
  self.load_thread.start()
310
309
 
311
- def _on_data_loaded(self, data: Dict[str, Any]):
310
+ def _on_data_loaded(self, data: dict[str, Any]):
312
311
  """Handle data loaded from background thread."""
313
312
  # If no data returned
314
313
  if not data:
@@ -358,7 +357,7 @@ class MetadataView(QWidget):
358
357
  data=full_data,
359
358
  scroll_position=self.table.verticalScrollBar().value(),
360
359
  search_query=(
361
- getattr(self.filter_builder, "to_dict")()
360
+ self.filter_builder.to_dict()
362
361
  if callable(getattr(self.filter_builder, "to_dict", None))
363
362
  else ""
364
363
  ),
@@ -401,7 +400,7 @@ class MetadataView(QWidget):
401
400
  data=data,
402
401
  scroll_position=self.table.verticalScrollBar().value(),
403
402
  search_query=(
404
- getattr(self.filter_builder, "to_dict")()
403
+ self.filter_builder.to_dict()
405
404
  if callable(getattr(self.filter_builder, "to_dict", None))
406
405
  else ""
407
406
  ),
@@ -422,7 +421,7 @@ class MetadataView(QWidget):
422
421
  self.status_label.setText(f"Failed to load data: {error_msg}")
423
422
  self.table.setRowCount(0)
424
423
 
425
- def _update_filter_fields(self, data: Dict[str, Any]):
424
+ def _update_filter_fields(self, data: dict[str, Any]):
426
425
  """Update filter builder with available metadata field names."""
427
426
  field_names = []
428
427
 
@@ -441,7 +440,7 @@ class MetadataView(QWidget):
441
440
  if field_names:
442
441
  self.filter_builder.set_available_fields(field_names)
443
442
 
444
- def _populate_table(self, data: Dict[str, Any]):
443
+ def _populate_table(self, data: dict[str, Any]):
445
444
  """Populate table with data."""
446
445
  ids = data.get("ids", [])
447
446
  documents = data.get("documents", [])
@@ -1,38 +1,38 @@
1
1
  """Search interface for similarity queries."""
2
2
 
3
- from typing import Optional, Dict, Any
3
+ from typing import Any, Optional
4
+
5
+ from PySide6.QtCore import Qt
6
+ from PySide6.QtGui import QFontMetrics
4
7
  from PySide6.QtWidgets import (
5
- QWidget,
6
- QVBoxLayout,
8
+ QApplication,
9
+ QGroupBox,
7
10
  QHBoxLayout,
8
- QTextEdit,
9
- QPushButton,
10
11
  QLabel,
12
+ QMenu,
13
+ QPushButton,
11
14
  QSizePolicy,
12
15
  QSpinBox,
16
+ QSplitter,
13
17
  QTableWidget,
14
18
  QTableWidgetItem,
15
- QGroupBox,
16
- QSplitter,
17
- QCheckBox,
18
- QApplication,
19
- QMenu,
19
+ QTextEdit,
20
+ QVBoxLayout,
21
+ QWidget,
20
22
  )
21
- from PySide6.QtCore import Qt
22
- from PySide6.QtGui import QFontMetrics
23
23
 
24
- from vector_inspector.core.connections.base_connection import VectorDBConnection
24
+ from vector_inspector.core.cache_manager import get_cache_manager
25
+ from vector_inspector.core.connection_manager import ConnectionInstance
26
+ from vector_inspector.core.logging import log_info
27
+ from vector_inspector.services.filter_service import apply_client_side_filters
25
28
  from vector_inspector.ui.components.filter_builder import FilterBuilder
26
29
  from vector_inspector.ui.components.loading_dialog import LoadingDialog
27
- from vector_inspector.services.filter_service import apply_client_side_filters
28
- from vector_inspector.core.cache_manager import get_cache_manager, CacheEntry
29
- from vector_inspector.core.logging import log_info
30
30
 
31
31
 
32
32
  class SearchView(QWidget):
33
33
  """View for performing similarity searches."""
34
34
 
35
- def __init__(self, connection: VectorDBConnection, parent=None):
35
+ def __init__(self, connection: Optional[ConnectionInstance] = None, parent=None):
36
36
  super().__init__(parent)
37
37
  # Initialize all UI attributes to None to avoid AttributeError
38
38
  self.breadcrumb_label = None
@@ -47,10 +47,11 @@ class SearchView(QWidget):
47
47
  self.loading_dialog = None
48
48
  self.cache_manager = None
49
49
 
50
+ # Expects a ConnectionInstance wrapper.
50
51
  self.connection = connection
51
52
  self.current_collection: str = ""
52
53
  self.current_database: str = ""
53
- self.search_results: Optional[Dict[str, Any]] = None
54
+ self.search_results: Optional[dict[str, Any]] = None
54
55
  self.loading_dialog = LoadingDialog("Searching...", self)
55
56
  self.cache_manager = get_cache_manager()
56
57
 
@@ -414,7 +415,7 @@ class SearchView(QWidget):
414
415
  if not menu.isEmpty():
415
416
  menu.exec(self.results_table.viewport().mapToGlobal(position))
416
417
 
417
- def _display_results(self, results: Dict[str, Any]):
418
+ def _display_results(self, results: dict[str, Any]):
418
419
  """Display search results in table."""
419
420
  ids = results.get("ids", [[]])[0]
420
421
  documents = results.get("documents", [[]])[0]
@@ -1,28 +1,30 @@
1
1
  """Vector visualization view with dimensionality reduction."""
2
2
 
3
3
  from __future__ import annotations
4
- from typing import Any
4
+
5
5
  import traceback
6
+ from typing import Any, Optional
7
+
8
+ import numpy as np
9
+ from PySide6.QtCore import QThread, Signal
10
+ from PySide6.QtWebEngineWidgets import QWebEngineView
6
11
  from PySide6.QtWidgets import (
7
- QWidget,
8
- QVBoxLayout,
9
- QHBoxLayout,
10
- QPushButton,
11
- QLabel,
12
+ QApplication,
12
13
  QComboBox,
13
- QSpinBox,
14
14
  QGroupBox,
15
+ QHBoxLayout,
16
+ QLabel,
15
17
  QMessageBox,
16
- QApplication,
18
+ QPushButton,
19
+ QSpinBox,
20
+ QVBoxLayout,
21
+ QWidget,
17
22
  )
18
- from PySide6.QtCore import QThread, Signal
19
- from PySide6.QtWebEngineWidgets import QWebEngineView
20
- import numpy as np
21
23
 
22
- from vector_inspector.core.connections.base_connection import VectorDBConnection
24
+ from vector_inspector.core.connection_manager import ConnectionInstance
25
+ from vector_inspector.core.logging import log_error
23
26
  from vector_inspector.services.visualization_service import VisualizationService
24
27
  from vector_inspector.ui.components.loading_dialog import LoadingDialog
25
- from vector_inspector.core.logging import log_error
26
28
 
27
29
 
28
30
  class VisualizationThread(QThread):
@@ -55,8 +57,9 @@ class VisualizationThread(QThread):
55
57
  class VisualizationView(QWidget):
56
58
  """View for visualizing vectors in 2D/3D."""
57
59
 
58
- def __init__(self, connection: VectorDBConnection, parent=None):
60
+ def __init__(self, connection: Optional[ConnectionInstance] = None, parent=None):
59
61
  super().__init__(parent)
62
+ # Expects a ConnectionInstance wrapper.
60
63
  self.connection = connection
61
64
  self.current_collection: str = ""
62
65
  self.current_data: dict[str, Any] | None = None
@@ -296,8 +299,8 @@ class VisualizationView(QWidget):
296
299
  webbrowser.open(f"file://{self._last_temp_html}")
297
300
 
298
301
  def cleanup_temp_html(self):
299
- import os
300
302
  import contextlib
303
+ import os
301
304
 
302
305
  for f in getattr(self, "temp_html_files", []):
303
306
  with contextlib.suppress(Exception):