vector-inspector 0.2.5__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 +20 -1
- vector_inspector/services/visualization_service.py +11 -7
- 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 +429 -181
- vector_inspector/ui/views/connection_view.py +43 -8
- vector_inspector/ui/views/info_panel.py +226 -80
- vector_inspector/ui/views/metadata_view.py +136 -28
- vector_inspector/ui/views/search_view.py +43 -3
- {vector_inspector-0.2.5.dist-info → vector_inspector-0.2.7.dist-info}/METADATA +5 -3
- vector_inspector-0.2.7.dist-info/RECORD +45 -0
- vector_inspector-0.2.5.dist-info/RECORD +0 -35
- {vector_inspector-0.2.5.dist-info → vector_inspector-0.2.7.dist-info}/WHEEL +0 -0
- {vector_inspector-0.2.5.dist-info → vector_inspector-0.2.7.dist-info}/entry_points.txt +0 -0
|
@@ -5,7 +5,7 @@ from PySide6.QtWidgets import (
|
|
|
5
5
|
QPushButton, QDialog, QFormLayout, QLineEdit,
|
|
6
6
|
QRadioButton, QButtonGroup, QGroupBox, QFileDialog, QComboBox, QApplication, QCheckBox
|
|
7
7
|
)
|
|
8
|
-
from PySide6.QtCore import Signal
|
|
8
|
+
from PySide6.QtCore import Signal, QThread
|
|
9
9
|
|
|
10
10
|
from vector_inspector.core.connections.base_connection import VectorDBConnection
|
|
11
11
|
from vector_inspector.core.connections.chroma_connection import ChromaDBConnection
|
|
@@ -14,6 +14,28 @@ from vector_inspector.ui.components.loading_dialog import LoadingDialog
|
|
|
14
14
|
from vector_inspector.services.settings_service import SettingsService
|
|
15
15
|
|
|
16
16
|
|
|
17
|
+
class ConnectionThread(QThread):
|
|
18
|
+
"""Background thread for connecting to database."""
|
|
19
|
+
|
|
20
|
+
finished = Signal(bool, list) # success, collections
|
|
21
|
+
|
|
22
|
+
def __init__(self, connection):
|
|
23
|
+
super().__init__()
|
|
24
|
+
self.connection = connection
|
|
25
|
+
|
|
26
|
+
def run(self):
|
|
27
|
+
"""Connect to database and get collections."""
|
|
28
|
+
try:
|
|
29
|
+
success = self.connection.connect()
|
|
30
|
+
if success:
|
|
31
|
+
collections = self.connection.list_collections()
|
|
32
|
+
self.finished.emit(True, collections)
|
|
33
|
+
else:
|
|
34
|
+
self.finished.emit(False, [])
|
|
35
|
+
except Exception:
|
|
36
|
+
self.finished.emit(False, [])
|
|
37
|
+
|
|
38
|
+
|
|
17
39
|
class ConnectionDialog(QDialog):
|
|
18
40
|
"""Dialog for configuring database connection."""
|
|
19
41
|
|
|
@@ -175,6 +197,9 @@ class ConnectionDialog(QDialog):
|
|
|
175
197
|
|
|
176
198
|
def get_connection_config(self):
|
|
177
199
|
"""Get connection configuration from dialog."""
|
|
200
|
+
# Get current provider from combo box to ensure it's up to date
|
|
201
|
+
self.provider = self.provider_combo.currentData()
|
|
202
|
+
|
|
178
203
|
config = {"provider": self.provider}
|
|
179
204
|
|
|
180
205
|
if self.persistent_radio.isChecked():
|
|
@@ -294,6 +319,7 @@ class ConnectionView(QWidget):
|
|
|
294
319
|
self.connection = connection
|
|
295
320
|
self.loading_dialog = LoadingDialog("Connecting to database...", self)
|
|
296
321
|
self.settings_service = SettingsService()
|
|
322
|
+
self.connection_thread = None
|
|
297
323
|
self._setup_ui()
|
|
298
324
|
|
|
299
325
|
# Try to auto-connect if enabled in settings
|
|
@@ -339,7 +365,6 @@ class ConnectionView(QWidget):
|
|
|
339
365
|
def _connect_with_config(self, config: dict):
|
|
340
366
|
"""Connect to database with given configuration."""
|
|
341
367
|
self.loading_dialog.show_loading("Connecting to database...")
|
|
342
|
-
QApplication.processEvents()
|
|
343
368
|
|
|
344
369
|
provider = config.get("provider", "chromadb")
|
|
345
370
|
conn_type = config.get("type")
|
|
@@ -367,11 +392,25 @@ class ConnectionView(QWidget):
|
|
|
367
392
|
else: # ephemeral
|
|
368
393
|
self.connection = ChromaDBConnection()
|
|
369
394
|
|
|
395
|
+
# Store config for later use
|
|
396
|
+
self._pending_config = config
|
|
397
|
+
|
|
370
398
|
# Notify parent that connection instance changed
|
|
371
399
|
self.connection_created.emit(self.connection)
|
|
372
|
-
|
|
373
|
-
|
|
400
|
+
|
|
401
|
+
# Start background thread to connect
|
|
402
|
+
self.connection_thread = ConnectionThread(self.connection)
|
|
403
|
+
self.connection_thread.finished.connect(self._on_connection_finished)
|
|
404
|
+
self.connection_thread.start()
|
|
405
|
+
|
|
406
|
+
def _on_connection_finished(self, success: bool, collections: list):
|
|
407
|
+
"""Handle connection thread completion."""
|
|
408
|
+
self.loading_dialog.hide_loading()
|
|
409
|
+
|
|
374
410
|
if success:
|
|
411
|
+
config = self._pending_config
|
|
412
|
+
provider = config.get("provider", "chromadb")
|
|
413
|
+
|
|
375
414
|
# Show provider, path/host + collection count for clarity
|
|
376
415
|
details = []
|
|
377
416
|
details.append(f"provider: {provider}")
|
|
@@ -380,7 +419,6 @@ class ConnectionView(QWidget):
|
|
|
380
419
|
if hasattr(self.connection, 'host') and self.connection.host:
|
|
381
420
|
port = getattr(self.connection, 'port', None)
|
|
382
421
|
details.append(f"host: {self.connection.host}:{port}")
|
|
383
|
-
collections = self.connection.list_collections()
|
|
384
422
|
count_text = f"collections: {len(collections)}"
|
|
385
423
|
info = ", ".join(details)
|
|
386
424
|
self.status_label.setText(f"Status: Connected ({info}, {count_text})")
|
|
@@ -401,9 +439,6 @@ class ConnectionView(QWidget):
|
|
|
401
439
|
self.disconnect_button.setEnabled(False)
|
|
402
440
|
self.connection_changed.emit(False)
|
|
403
441
|
|
|
404
|
-
# Close loading dialog after everything is complete
|
|
405
|
-
self.loading_dialog.hide_loading()
|
|
406
|
-
|
|
407
442
|
def _disconnect(self):
|
|
408
443
|
"""Disconnect from database."""
|
|
409
444
|
self.connection.disconnect()
|
|
@@ -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):
|
|
@@ -56,18 +61,6 @@ class InfoPanel(QWidget):
|
|
|
56
61
|
self.db_group.setLayout(db_layout)
|
|
57
62
|
container_layout.addWidget(self.db_group)
|
|
58
63
|
|
|
59
|
-
# Collections List Section
|
|
60
|
-
self.collections_group = QGroupBox("Available Collections")
|
|
61
|
-
collections_layout = QVBoxLayout()
|
|
62
|
-
|
|
63
|
-
self.collections_list_label = QLabel("No collections")
|
|
64
|
-
self.collections_list_label.setWordWrap(True)
|
|
65
|
-
self.collections_list_label.setStyleSheet("color: gray; padding: 10px;")
|
|
66
|
-
collections_layout.addWidget(self.collections_list_label)
|
|
67
|
-
|
|
68
|
-
self.collections_group.setLayout(collections_layout)
|
|
69
|
-
container_layout.addWidget(self.collections_group)
|
|
70
|
-
|
|
71
64
|
# Collection Information Section
|
|
72
65
|
self.collection_group = QGroupBox("Collection Information")
|
|
73
66
|
collection_layout = QVBoxLayout()
|
|
@@ -77,10 +70,31 @@ class InfoPanel(QWidget):
|
|
|
77
70
|
self.distance_metric_label = self._create_info_row("Distance Metric:", "N/A")
|
|
78
71
|
self.total_points_label = self._create_info_row("Total Points:", "0")
|
|
79
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
|
+
|
|
80
93
|
collection_layout.addWidget(self.collection_name_label)
|
|
81
94
|
collection_layout.addWidget(self.vector_dim_label)
|
|
82
95
|
collection_layout.addWidget(self.distance_metric_label)
|
|
83
96
|
collection_layout.addWidget(self.total_points_label)
|
|
97
|
+
collection_layout.addWidget(embedding_row)
|
|
84
98
|
|
|
85
99
|
# Payload Schema subsection
|
|
86
100
|
schema_label = QLabel("<b>Payload Schema:</b>")
|
|
@@ -141,8 +155,6 @@ class InfoPanel(QWidget):
|
|
|
141
155
|
self._update_label(self.api_key_label, "N/A")
|
|
142
156
|
self._update_label(self.status_label, "Disconnected")
|
|
143
157
|
self._update_label(self.collections_count_label, "0")
|
|
144
|
-
self.collections_list_label.setText("No collections")
|
|
145
|
-
self.collections_list_label.setStyleSheet("color: gray; padding: 10px;")
|
|
146
158
|
# Also clear collection info
|
|
147
159
|
self._update_label(self.collection_name_label, "No collection selected")
|
|
148
160
|
self._update_label(self.vector_dim_label, "N/A")
|
|
@@ -195,22 +207,12 @@ class InfoPanel(QWidget):
|
|
|
195
207
|
# Status
|
|
196
208
|
self._update_label(self.status_label, "Connected" if self.connection.is_connected else "Disconnected")
|
|
197
209
|
|
|
198
|
-
#
|
|
210
|
+
# Count collections
|
|
199
211
|
try:
|
|
200
212
|
collections = self.connection.list_collections()
|
|
201
213
|
self._update_label(self.collections_count_label, str(len(collections)))
|
|
202
|
-
|
|
203
|
-
if collections:
|
|
204
|
-
collections_text = "\n".join([f"• {name}" for name in sorted(collections)])
|
|
205
|
-
self.collections_list_label.setText(collections_text)
|
|
206
|
-
self.collections_list_label.setStyleSheet("color: white; padding: 10px; font-family: monospace;")
|
|
207
|
-
else:
|
|
208
|
-
self.collections_list_label.setText("No collections found")
|
|
209
|
-
self.collections_list_label.setStyleSheet("color: gray; padding: 10px;")
|
|
210
214
|
except Exception as e:
|
|
211
215
|
self._update_label(self.collections_count_label, "Error")
|
|
212
|
-
self.collections_list_label.setText(f"Error loading collections: {str(e)}")
|
|
213
|
-
self.collections_list_label.setStyleSheet("color: red; padding: 10px;")
|
|
214
216
|
|
|
215
217
|
def refresh_collection_info(self):
|
|
216
218
|
"""Refresh collection-specific information."""
|
|
@@ -224,7 +226,7 @@ class InfoPanel(QWidget):
|
|
|
224
226
|
return
|
|
225
227
|
|
|
226
228
|
try:
|
|
227
|
-
# Get collection info
|
|
229
|
+
# Get collection info from database
|
|
228
230
|
collection_info = self.connection.get_collection_info(self.current_collection)
|
|
229
231
|
|
|
230
232
|
if not collection_info:
|
|
@@ -236,59 +238,18 @@ class InfoPanel(QWidget):
|
|
|
236
238
|
self.provider_details_label.setText("N/A")
|
|
237
239
|
return
|
|
238
240
|
|
|
239
|
-
#
|
|
240
|
-
self.
|
|
241
|
-
|
|
242
|
-
# Vector dimension
|
|
243
|
-
vector_dim = collection_info.get("vector_dimension", "Unknown")
|
|
244
|
-
self._update_label(self.vector_dim_label, str(vector_dim))
|
|
245
|
-
|
|
246
|
-
# Distance metric
|
|
247
|
-
distance = collection_info.get("distance_metric", "Unknown")
|
|
248
|
-
self._update_label(self.distance_metric_label, distance)
|
|
249
|
-
|
|
250
|
-
# Total points
|
|
251
|
-
count = collection_info.get("count", 0)
|
|
252
|
-
self._update_label(self.total_points_label, f"{count:,}")
|
|
253
|
-
|
|
254
|
-
# Metadata schema
|
|
255
|
-
metadata_fields = collection_info.get("metadata_fields", [])
|
|
256
|
-
if metadata_fields:
|
|
257
|
-
schema_text = "\n".join([f"• {field}" for field in sorted(metadata_fields)])
|
|
258
|
-
self.schema_label.setText(schema_text)
|
|
259
|
-
self.schema_label.setStyleSheet("color: white; padding-left: 20px; font-family: monospace;")
|
|
260
|
-
else:
|
|
261
|
-
self.schema_label.setText("No metadata fields found")
|
|
262
|
-
self.schema_label.setStyleSheet("color: gray; padding-left: 20px;")
|
|
263
|
-
|
|
264
|
-
# Provider-specific details
|
|
265
|
-
details_list = []
|
|
266
|
-
|
|
267
|
-
if isinstance(self.connection, ChromaDBConnection):
|
|
268
|
-
details_list.append("• Provider: ChromaDB")
|
|
269
|
-
details_list.append("• Supports: Documents, Metadata, Embeddings")
|
|
270
|
-
details_list.append("• Default embedding: all-MiniLM-L6-v2")
|
|
271
|
-
|
|
272
|
-
elif isinstance(self.connection, QdrantConnection):
|
|
273
|
-
details_list.append("• Provider: Qdrant")
|
|
274
|
-
details_list.append("• Supports: Points, Payload, Vectors")
|
|
275
|
-
# Get additional Qdrant-specific info if available
|
|
276
|
-
if "config" in collection_info:
|
|
277
|
-
config = collection_info["config"]
|
|
278
|
-
if "hnsw_config" in config:
|
|
279
|
-
hnsw = config["hnsw_config"]
|
|
280
|
-
details_list.append(f"• HNSW M: {hnsw.get('m', 'N/A')}")
|
|
281
|
-
details_list.append(f"• HNSW ef_construct: {hnsw.get('ef_construct', 'N/A')}")
|
|
282
|
-
if "optimizer_config" in config:
|
|
283
|
-
opt = config["optimizer_config"]
|
|
284
|
-
details_list.append(f"• Indexing threshold: {opt.get('indexing_threshold', 'N/A')}")
|
|
241
|
+
# Display the info
|
|
242
|
+
self._display_collection_info(collection_info)
|
|
285
243
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
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.")
|
|
292
253
|
|
|
293
254
|
except Exception as e:
|
|
294
255
|
self._update_label(self.collection_name_label, self.current_collection)
|
|
@@ -299,9 +260,89 @@ class InfoPanel(QWidget):
|
|
|
299
260
|
self.schema_label.setStyleSheet("color: red; padding-left: 20px;")
|
|
300
261
|
self.provider_details_label.setText("N/A")
|
|
301
262
|
|
|
302
|
-
def
|
|
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 = ""):
|
|
303
328
|
"""Set the current collection and refresh its information."""
|
|
304
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...")
|
|
305
346
|
self.refresh_collection_info()
|
|
306
347
|
|
|
307
348
|
def _update_label(self, row_widget: QWidget, value: str):
|
|
@@ -309,3 +350,108 @@ class InfoPanel(QWidget):
|
|
|
309
350
|
value_label = row_widget.property("value_label")
|
|
310
351
|
if value_label and isinstance(value_label, QLabel):
|
|
311
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}'")
|