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.
- vector_inspector/core/cache_manager.py +159 -0
- vector_inspector/core/connection_manager.py +277 -0
- vector_inspector/core/connections/chroma_connection.py +90 -5
- vector_inspector/core/connections/qdrant_connection.py +62 -8
- vector_inspector/core/embedding_utils.py +140 -0
- vector_inspector/services/backup_restore_service.py +3 -29
- vector_inspector/services/credential_service.py +130 -0
- vector_inspector/services/filter_service.py +1 -1
- vector_inspector/services/profile_service.py +409 -0
- vector_inspector/services/settings_service.py +19 -0
- vector_inspector/ui/components/connection_manager_panel.py +320 -0
- vector_inspector/ui/components/profile_manager_panel.py +518 -0
- vector_inspector/ui/dialogs/__init__.py +5 -0
- vector_inspector/ui/dialogs/cross_db_migration.py +364 -0
- vector_inspector/ui/dialogs/embedding_config_dialog.py +176 -0
- vector_inspector/ui/main_window.py +425 -190
- vector_inspector/ui/views/info_panel.py +225 -55
- vector_inspector/ui/views/metadata_view.py +71 -3
- vector_inspector/ui/views/search_view.py +43 -3
- {vector_inspector-0.2.6.dist-info → vector_inspector-0.2.7.dist-info}/METADATA +3 -1
- vector_inspector-0.2.7.dist-info/RECORD +45 -0
- vector_inspector-0.2.6.dist-info/RECORD +0 -35
- {vector_inspector-0.2.6.dist-info → vector_inspector-0.2.7.dist-info}/WHEEL +0 -0
- {vector_inspector-0.2.6.dist-info → vector_inspector-0.2.7.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,320 @@
|
|
|
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
|
+
try:
|
|
293
|
+
collections = instance.connection.list_collections()
|
|
294
|
+
self.connection_manager.update_collections(connection_id, collections)
|
|
295
|
+
except Exception as e:
|
|
296
|
+
QMessageBox.warning(self, "Error", f"Failed to refresh collections: {e}")
|
|
297
|
+
|
|
298
|
+
def _disconnect_connection(self, connection_id: str):
|
|
299
|
+
"""Disconnect a connection."""
|
|
300
|
+
instance = self.connection_manager.get_connection(connection_id)
|
|
301
|
+
if not instance:
|
|
302
|
+
return
|
|
303
|
+
|
|
304
|
+
reply = QMessageBox.question(
|
|
305
|
+
self,
|
|
306
|
+
"Disconnect",
|
|
307
|
+
f"Disconnect from '{instance.name}'?",
|
|
308
|
+
QMessageBox.Yes | QMessageBox.No
|
|
309
|
+
)
|
|
310
|
+
|
|
311
|
+
if reply == QMessageBox.Yes:
|
|
312
|
+
self.connection_manager.close_connection(connection_id)
|
|
313
|
+
|
|
314
|
+
def _view_collection_info(self, connection_id: str, collection_name: str):
|
|
315
|
+
"""View collection info."""
|
|
316
|
+
# This would trigger showing collection details
|
|
317
|
+
# For now, just select it
|
|
318
|
+
self.connection_manager.set_active_collection(connection_id, collection_name)
|
|
319
|
+
self.collection_selected.emit(connection_id, collection_name)
|
|
320
|
+
|