vector-inspector 0.2.6__py3-none-any.whl → 0.3.1__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 (38) hide show
  1. vector_inspector/config/__init__.py +4 -0
  2. vector_inspector/config/known_embedding_models.json +432 -0
  3. vector_inspector/core/cache_manager.py +159 -0
  4. vector_inspector/core/connection_manager.py +277 -0
  5. vector_inspector/core/connections/__init__.py +2 -1
  6. vector_inspector/core/connections/base_connection.py +42 -1
  7. vector_inspector/core/connections/chroma_connection.py +137 -16
  8. vector_inspector/core/connections/pinecone_connection.py +768 -0
  9. vector_inspector/core/connections/qdrant_connection.py +62 -8
  10. vector_inspector/core/embedding_providers/__init__.py +14 -0
  11. vector_inspector/core/embedding_providers/base_provider.py +128 -0
  12. vector_inspector/core/embedding_providers/clip_provider.py +260 -0
  13. vector_inspector/core/embedding_providers/provider_factory.py +176 -0
  14. vector_inspector/core/embedding_providers/sentence_transformer_provider.py +203 -0
  15. vector_inspector/core/embedding_utils.py +167 -0
  16. vector_inspector/core/model_registry.py +205 -0
  17. vector_inspector/services/backup_restore_service.py +19 -29
  18. vector_inspector/services/credential_service.py +130 -0
  19. vector_inspector/services/filter_service.py +1 -1
  20. vector_inspector/services/profile_service.py +409 -0
  21. vector_inspector/services/settings_service.py +136 -1
  22. vector_inspector/ui/components/connection_manager_panel.py +327 -0
  23. vector_inspector/ui/components/profile_manager_panel.py +565 -0
  24. vector_inspector/ui/dialogs/__init__.py +6 -0
  25. vector_inspector/ui/dialogs/cross_db_migration.py +383 -0
  26. vector_inspector/ui/dialogs/embedding_config_dialog.py +315 -0
  27. vector_inspector/ui/dialogs/provider_type_dialog.py +189 -0
  28. vector_inspector/ui/main_window.py +456 -190
  29. vector_inspector/ui/views/connection_view.py +55 -10
  30. vector_inspector/ui/views/info_panel.py +272 -55
  31. vector_inspector/ui/views/metadata_view.py +71 -3
  32. vector_inspector/ui/views/search_view.py +44 -4
  33. vector_inspector/ui/views/visualization_view.py +19 -5
  34. {vector_inspector-0.2.6.dist-info → vector_inspector-0.3.1.dist-info}/METADATA +3 -1
  35. vector_inspector-0.3.1.dist-info/RECORD +55 -0
  36. vector_inspector-0.2.6.dist-info/RECORD +0 -35
  37. {vector_inspector-0.2.6.dist-info → vector_inspector-0.3.1.dist-info}/WHEEL +0 -0
  38. {vector_inspector-0.2.6.dist-info → vector_inspector-0.3.1.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,327 @@
1
+ """Connection manager panel showing multiple active connections."""
2
+
3
+ from PySide6.QtWidgets import (
4
+ QWidget, QVBoxLayout, QTreeWidget, QTreeWidgetItem, QPushButton,
5
+ QHBoxLayout, QMenu, QMessageBox, QInputDialog, QLabel
6
+ )
7
+ from PySide6.QtCore import Qt, Signal
8
+ from PySide6.QtGui import QIcon, QColor, QBrush
9
+
10
+ from vector_inspector.core.connection_manager import ConnectionManager, ConnectionInstance, ConnectionState
11
+
12
+
13
+ class ConnectionManagerPanel(QWidget):
14
+ """Panel for managing multiple database connections.
15
+
16
+ Signals:
17
+ connection_selected: Emitted when a connection is clicked (connection_id)
18
+ collection_selected: Emitted when a collection is clicked (connection_id, collection_name)
19
+ """
20
+
21
+ connection_selected = Signal(str) # connection_id
22
+ collection_selected = Signal(str, str) # connection_id, collection_name
23
+
24
+ def __init__(self, connection_manager: ConnectionManager, parent=None):
25
+ """
26
+ Initialize connection manager panel.
27
+
28
+ Args:
29
+ connection_manager: The ConnectionManager instance
30
+ parent: Parent widget
31
+ """
32
+ super().__init__(parent)
33
+ self.connection_manager = connection_manager
34
+ self._connection_items = {} # Map connection_id to tree item
35
+
36
+ self._setup_ui()
37
+ self._connect_signals()
38
+
39
+ def _setup_ui(self):
40
+ """Setup the UI."""
41
+ layout = QVBoxLayout(self)
42
+ layout.setContentsMargins(0, 0, 0, 0)
43
+
44
+ # Header
45
+ header_layout = QHBoxLayout()
46
+ header_label = QLabel("Connections")
47
+ header_label.setStyleSheet("font-weight: bold; font-size: 12px;")
48
+ header_layout.addWidget(header_label)
49
+ header_layout.addStretch()
50
+
51
+ # Add connection button
52
+ self.add_connection_btn = QPushButton("+")
53
+ self.add_connection_btn.setMaximumWidth(30)
54
+ self.add_connection_btn.setToolTip("Add new connection")
55
+ header_layout.addWidget(self.add_connection_btn)
56
+
57
+ layout.addLayout(header_layout)
58
+
59
+ # Connection tree
60
+ self.connection_tree = QTreeWidget()
61
+ self.connection_tree.setHeaderHidden(True)
62
+ self.connection_tree.setContextMenuPolicy(Qt.CustomContextMenu)
63
+ self.connection_tree.customContextMenuRequested.connect(self._show_context_menu)
64
+ self.connection_tree.itemClicked.connect(self._on_item_clicked)
65
+ self.connection_tree.itemExpanded.connect(self._on_item_expanded)
66
+ # Match QListWidget selection style - use subtle highlight
67
+ self.connection_tree.setStyleSheet('''
68
+ QTreeWidget::item:selected {
69
+ background: palette(highlight);
70
+ color: palette(highlighted-text);
71
+ }
72
+ ''')
73
+ layout.addWidget(self.connection_tree)
74
+
75
+ def _connect_signals(self):
76
+ """Connect to connection manager signals."""
77
+ self.connection_manager.connection_opened.connect(self._on_connection_opened)
78
+ self.connection_manager.connection_closed.connect(self._on_connection_closed)
79
+ self.connection_manager.connection_state_changed.connect(self._on_connection_state_changed)
80
+ self.connection_manager.active_connection_changed.connect(self._on_active_connection_changed)
81
+ self.connection_manager.active_collection_changed.connect(self._on_active_collection_changed)
82
+ self.connection_manager.collections_updated.connect(self._on_collections_updated)
83
+
84
+ def _on_connection_opened(self, connection_id: str):
85
+ """Handle new connection opened (after successful connection)."""
86
+ instance = self.connection_manager.get_connection(connection_id)
87
+ if not instance:
88
+ return
89
+
90
+ # Only show if not already shown
91
+ if connection_id in self._connection_items:
92
+ return
93
+
94
+ # Create tree item for connection
95
+ item = QTreeWidgetItem(self.connection_tree)
96
+ item.setText(0, instance.get_display_name())
97
+ item.setData(0, Qt.UserRole, {"type": "connection", "connection_id": connection_id})
98
+
99
+ # Set icon/indicator based on state
100
+ self._update_connection_indicator(item, instance.state)
101
+
102
+ self._connection_items[connection_id] = item
103
+
104
+ # Expand by default to show collections
105
+ item.setExpanded(True)
106
+
107
+ # Select if active
108
+ if self.connection_manager.get_active_connection_id() == connection_id:
109
+ self.connection_tree.setCurrentItem(item)
110
+
111
+ def _on_connection_closed(self, connection_id: str):
112
+ """Handle connection closed."""
113
+ item = self._connection_items.pop(connection_id, None)
114
+ if item:
115
+ index = self.connection_tree.indexOfTopLevelItem(item)
116
+ if index >= 0:
117
+ self.connection_tree.takeTopLevelItem(index)
118
+
119
+ def _on_connection_state_changed(self, connection_id: str, state: ConnectionState):
120
+ """Handle connection state change."""
121
+ item = self._connection_items.get(connection_id)
122
+ if item:
123
+ self._update_connection_indicator(item, state)
124
+
125
+ def _on_active_connection_changed(self, connection_id):
126
+ """Handle active connection change."""
127
+ # Select the active connection item in the tree
128
+ if connection_id:
129
+ item = self._connection_items.get(connection_id)
130
+ if item:
131
+ self.connection_tree.setCurrentItem(item)
132
+
133
+ def _on_active_collection_changed(self, connection_id: str, collection_name):
134
+ """Handle active collection change."""
135
+ item = self._connection_items.get(connection_id)
136
+ if not item:
137
+ return
138
+
139
+ # Select the active collection in the tree
140
+ if collection_name:
141
+ for i in range(item.childCount()):
142
+ child = item.child(i)
143
+ data = child.data(0, Qt.UserRole)
144
+ if data and data.get("collection_name") == collection_name:
145
+ self.connection_tree.setCurrentItem(child)
146
+ break
147
+
148
+ def _on_collections_updated(self, connection_id: str, collections: list):
149
+ """Handle collections list updated."""
150
+ item = self._connection_items.get(connection_id)
151
+ if not item:
152
+ return
153
+
154
+ # Remove existing collection items
155
+ while item.childCount() > 0:
156
+ item.removeChild(item.child(0))
157
+
158
+ # Add new collection items
159
+ for collection_name in collections:
160
+ child = QTreeWidgetItem(item)
161
+ child.setText(0, collection_name)
162
+ child.setData(0, Qt.UserRole, {
163
+ "type": "collection",
164
+ "connection_id": connection_id,
165
+ "collection_name": collection_name
166
+ })
167
+
168
+ def _update_connection_indicator(self, item: QTreeWidgetItem, state: ConnectionState):
169
+ """Update visual indicator for connection state."""
170
+ if state == ConnectionState.CONNECTED:
171
+ indicator = "🟢"
172
+ elif state == ConnectionState.CONNECTING:
173
+ indicator = "🟡"
174
+ elif state == ConnectionState.ERROR:
175
+ indicator = "🔴"
176
+ else:
177
+ indicator = "⚪"
178
+
179
+ data = item.data(0, Qt.UserRole)
180
+ connection_id = data.get("connection_id")
181
+ instance = self.connection_manager.get_connection(connection_id)
182
+
183
+ if instance:
184
+ item.setText(0, f"{indicator} {instance.get_display_name()}")
185
+
186
+ def _on_item_clicked(self, item: QTreeWidgetItem, column: int):
187
+ """Handle tree item click."""
188
+ data = item.data(0, Qt.UserRole)
189
+ if not data:
190
+ return
191
+
192
+ item_type = data.get("type")
193
+ connection_id = data.get("connection_id")
194
+
195
+ if item_type == "connection":
196
+ # Set as active connection
197
+ self.connection_manager.set_active_connection(connection_id)
198
+ self.connection_selected.emit(connection_id)
199
+ elif item_type == "collection":
200
+ # Set active connection first (if different)
201
+ if connection_id != self.connection_manager.get_active_connection_id():
202
+ self.connection_manager.set_active_connection(connection_id)
203
+
204
+ # Then set as active collection
205
+ collection_name = data.get("collection_name")
206
+ self.connection_manager.set_active_collection(connection_id, collection_name)
207
+ self.collection_selected.emit(connection_id, collection_name)
208
+
209
+ def _on_item_expanded(self, item: QTreeWidgetItem):
210
+ """Handle tree item expansion."""
211
+ # Could trigger lazy loading of collections here if needed
212
+ pass
213
+
214
+ def _show_context_menu(self, pos):
215
+ """Show context menu for connection/collection."""
216
+ item = self.connection_tree.itemAt(pos)
217
+ if not item:
218
+ return
219
+
220
+ data = item.data(0, Qt.UserRole)
221
+ if not data:
222
+ return
223
+
224
+ menu = QMenu(self)
225
+ item_type = data.get("type")
226
+ connection_id = data.get("connection_id")
227
+
228
+ if item_type == "connection":
229
+ # Connection context menu
230
+ set_active_action = menu.addAction("Set as Active")
231
+ set_active_action.triggered.connect(
232
+ lambda: self.connection_manager.set_active_connection(connection_id)
233
+ )
234
+
235
+ menu.addSeparator()
236
+
237
+ rename_action = menu.addAction("Rename...")
238
+ rename_action.triggered.connect(lambda: self._rename_connection(connection_id))
239
+
240
+ refresh_action = menu.addAction("Refresh Collections")
241
+ refresh_action.triggered.connect(lambda: self._refresh_collections(connection_id))
242
+
243
+ menu.addSeparator()
244
+
245
+ disconnect_action = menu.addAction("Disconnect")
246
+ disconnect_action.triggered.connect(lambda: self._disconnect_connection(connection_id))
247
+
248
+ elif item_type == "collection":
249
+ # Collection context menu
250
+ collection_name = data.get("collection_name")
251
+
252
+ select_action = menu.addAction("Select Collection")
253
+ select_action.triggered.connect(
254
+ lambda: self.connection_manager.set_active_collection(connection_id, collection_name)
255
+ )
256
+
257
+ menu.addSeparator()
258
+
259
+ info_action = menu.addAction("View Info")
260
+ info_action.triggered.connect(
261
+ lambda: self._view_collection_info(connection_id, collection_name)
262
+ )
263
+
264
+ menu.exec_(self.connection_tree.mapToGlobal(pos))
265
+
266
+ def _rename_connection(self, connection_id: str):
267
+ """Rename a connection."""
268
+ instance = self.connection_manager.get_connection(connection_id)
269
+ if not instance:
270
+ return
271
+
272
+ new_name, ok = QInputDialog.getText(
273
+ self,
274
+ "Rename Connection",
275
+ "Enter new name:",
276
+ text=instance.name
277
+ )
278
+
279
+ if ok and new_name:
280
+ if self.connection_manager.rename_connection(connection_id, new_name):
281
+ # Update tree item
282
+ item = self._connection_items.get(connection_id)
283
+ if item:
284
+ self._update_connection_indicator(item, instance.state)
285
+
286
+ def _refresh_collections(self, connection_id: str):
287
+ """Refresh collections for a connection."""
288
+ instance = self.connection_manager.get_connection(connection_id)
289
+ if not instance or not instance.connection.is_connected:
290
+ return
291
+
292
+ # Show loading while refreshing
293
+ from ..components.loading_dialog import LoadingDialog
294
+ loading = LoadingDialog("Refreshing collections...", self)
295
+ loading.show_loading("Refreshing collections...")
296
+ QApplication.processEvents()
297
+ try:
298
+ collections = instance.connection.list_collections()
299
+ self.connection_manager.update_collections(connection_id, collections)
300
+ except Exception as e:
301
+ QMessageBox.warning(self, "Error", f"Failed to refresh collections: {e}")
302
+ finally:
303
+ loading.hide_loading()
304
+
305
+ def _disconnect_connection(self, connection_id: str):
306
+ """Disconnect a connection."""
307
+ instance = self.connection_manager.get_connection(connection_id)
308
+ if not instance:
309
+ return
310
+
311
+ reply = QMessageBox.question(
312
+ self,
313
+ "Disconnect",
314
+ f"Disconnect from '{instance.name}'?",
315
+ QMessageBox.Yes | QMessageBox.No
316
+ )
317
+
318
+ if reply == QMessageBox.Yes:
319
+ self.connection_manager.close_connection(connection_id)
320
+
321
+ def _view_collection_info(self, connection_id: str, collection_name: str):
322
+ """View collection info."""
323
+ # This would trigger showing collection details
324
+ # For now, just select it
325
+ self.connection_manager.set_active_collection(connection_id, collection_name)
326
+ self.collection_selected.emit(connection_id, collection_name)
327
+