vector-inspector 0.3.1__py3-none-any.whl → 0.3.3__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/connection_manager.py +55 -49
- vector_inspector/core/connections/base_connection.py +41 -41
- vector_inspector/core/connections/chroma_connection.py +110 -86
- vector_inspector/core/connections/pinecone_connection.py +168 -182
- vector_inspector/core/connections/qdrant_connection.py +109 -126
- vector_inspector/core/connections/qdrant_helpers/__init__.py +4 -0
- vector_inspector/core/connections/qdrant_helpers/qdrant_embedding_resolver.py +35 -0
- vector_inspector/core/connections/qdrant_helpers/qdrant_filter_builder.py +51 -0
- vector_inspector/core/connections/template_connection.py +55 -65
- vector_inspector/core/embedding_utils.py +32 -32
- vector_inspector/core/logging.py +27 -0
- vector_inspector/core/model_registry.py +4 -3
- vector_inspector/main.py +6 -2
- vector_inspector/services/backup_helpers.py +63 -0
- vector_inspector/services/backup_restore_service.py +73 -152
- vector_inspector/services/credential_service.py +33 -40
- vector_inspector/services/import_export_service.py +70 -67
- vector_inspector/services/profile_service.py +92 -94
- vector_inspector/services/settings_service.py +68 -48
- vector_inspector/services/visualization_service.py +40 -39
- vector_inspector/ui/components/splash_window.py +57 -0
- vector_inspector/ui/dialogs/cross_db_migration.py +6 -5
- vector_inspector/ui/main_window.py +200 -146
- vector_inspector/ui/views/info_panel.py +208 -127
- vector_inspector/ui/views/metadata_view.py +8 -7
- vector_inspector/ui/views/search_view.py +97 -75
- vector_inspector/ui/views/visualization_view.py +140 -97
- vector_inspector/utils/version.py +5 -0
- {vector_inspector-0.3.1.dist-info → vector_inspector-0.3.3.dist-info}/METADATA +9 -2
- {vector_inspector-0.3.1.dist-info → vector_inspector-0.3.3.dist-info}/RECORD +32 -25
- {vector_inspector-0.3.1.dist-info → vector_inspector-0.3.3.dist-info}/WHEEL +0 -0
- {vector_inspector-0.3.1.dist-info → vector_inspector-0.3.3.dist-info}/entry_points.txt +0 -0
|
@@ -2,8 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
from typing import Optional, Dict, Any
|
|
4
4
|
from PySide6.QtWidgets import (
|
|
5
|
-
QWidget,
|
|
6
|
-
|
|
5
|
+
QWidget,
|
|
6
|
+
QVBoxLayout,
|
|
7
|
+
QHBoxLayout,
|
|
8
|
+
QLabel,
|
|
9
|
+
QGroupBox,
|
|
10
|
+
QScrollArea,
|
|
11
|
+
QFrame,
|
|
12
|
+
QPushButton,
|
|
7
13
|
)
|
|
8
14
|
from PySide6.QtCore import Qt, QObject
|
|
9
15
|
from PySide6.QtWidgets import QDialog
|
|
@@ -14,11 +20,12 @@ from vector_inspector.core.connections.chroma_connection import ChromaDBConnecti
|
|
|
14
20
|
from vector_inspector.core.connections.qdrant_connection import QdrantConnection
|
|
15
21
|
from vector_inspector.core.connections.pinecone_connection import PineconeConnection
|
|
16
22
|
from vector_inspector.core.cache_manager import get_cache_manager
|
|
23
|
+
from vector_inspector.core.logging import log_info
|
|
17
24
|
|
|
18
25
|
|
|
19
26
|
class InfoPanel(QWidget):
|
|
20
27
|
"""Panel for displaying database and collection information."""
|
|
21
|
-
|
|
28
|
+
|
|
22
29
|
def __init__(self, connection: VectorDBConnection, parent=None):
|
|
23
30
|
super().__init__(parent)
|
|
24
31
|
self.connection = connection
|
|
@@ -27,127 +34,166 @@ class InfoPanel(QWidget):
|
|
|
27
34
|
self.current_database: str = ""
|
|
28
35
|
self.cache_manager = get_cache_manager()
|
|
29
36
|
self._setup_ui()
|
|
30
|
-
|
|
37
|
+
|
|
31
38
|
def _setup_ui(self):
|
|
32
39
|
"""Setup widget UI."""
|
|
33
40
|
layout = QVBoxLayout(self)
|
|
34
|
-
|
|
41
|
+
|
|
35
42
|
# Create scroll area for content
|
|
36
43
|
scroll = QScrollArea()
|
|
37
44
|
scroll.setWidgetResizable(True)
|
|
38
45
|
scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
|
|
39
|
-
|
|
46
|
+
|
|
40
47
|
# Container for all info sections
|
|
41
48
|
container = QWidget()
|
|
42
49
|
container_layout = QVBoxLayout(container)
|
|
43
50
|
container_layout.setSpacing(10)
|
|
44
|
-
|
|
51
|
+
|
|
45
52
|
# Database Information Section
|
|
46
53
|
self.db_group = QGroupBox("Database Information")
|
|
47
54
|
db_layout = QVBoxLayout()
|
|
48
|
-
|
|
55
|
+
|
|
49
56
|
self.provider_label = self._create_info_row("Provider:", "Not connected")
|
|
50
57
|
self.connection_type_label = self._create_info_row("Connection Type:", "N/A")
|
|
51
58
|
self.endpoint_label = self._create_info_row("Endpoint:", "N/A")
|
|
52
59
|
self.api_key_label = self._create_info_row("API Key:", "N/A")
|
|
53
60
|
self.status_label = self._create_info_row("Status:", "Disconnected")
|
|
54
61
|
self.collections_count_label = self._create_info_row("Total Collections:", "0")
|
|
55
|
-
|
|
62
|
+
|
|
56
63
|
db_layout.addWidget(self.provider_label)
|
|
57
64
|
db_layout.addWidget(self.connection_type_label)
|
|
58
65
|
db_layout.addWidget(self.endpoint_label)
|
|
59
66
|
db_layout.addWidget(self.api_key_label)
|
|
60
67
|
db_layout.addWidget(self.status_label)
|
|
61
68
|
db_layout.addWidget(self.collections_count_label)
|
|
62
|
-
|
|
69
|
+
|
|
63
70
|
self.db_group.setLayout(db_layout)
|
|
64
71
|
container_layout.addWidget(self.db_group)
|
|
65
|
-
|
|
72
|
+
|
|
66
73
|
# Collection Information Section
|
|
67
74
|
self.collection_group = QGroupBox("Collection Information")
|
|
68
75
|
collection_layout = QVBoxLayout()
|
|
69
|
-
|
|
76
|
+
|
|
70
77
|
self.collection_name_label = self._create_info_row("Name:", "No collection selected")
|
|
71
78
|
self.vector_dim_label = self._create_info_row("Vector Dimension:", "N/A")
|
|
72
79
|
self.distance_metric_label = self._create_info_row("Distance Metric:", "N/A")
|
|
73
80
|
self.total_points_label = self._create_info_row("Total Points:", "0")
|
|
74
|
-
|
|
81
|
+
|
|
75
82
|
# Embedding model row with configure button
|
|
76
83
|
embedding_row = QWidget()
|
|
77
84
|
embedding_layout = QHBoxLayout(embedding_row)
|
|
78
85
|
embedding_layout.setContentsMargins(0, 2, 0, 2)
|
|
79
|
-
|
|
86
|
+
|
|
80
87
|
embedding_label = QLabel("<b>Embedding Model:</b>")
|
|
81
88
|
embedding_label.setMinimumWidth(150)
|
|
82
89
|
self.embedding_model_label = QLabel("Auto-detect")
|
|
83
90
|
self.embedding_model_label.setStyleSheet("color: gray;")
|
|
84
91
|
self.embedding_model_label.setWordWrap(True)
|
|
85
|
-
|
|
92
|
+
|
|
86
93
|
self.configure_embedding_btn = QPushButton("Configure...")
|
|
87
94
|
self.configure_embedding_btn.setMaximumWidth(100)
|
|
88
95
|
self.configure_embedding_btn.clicked.connect(self._configure_embedding_model)
|
|
89
96
|
self.configure_embedding_btn.setEnabled(False)
|
|
90
|
-
|
|
97
|
+
|
|
98
|
+
self.clear_embedding_btn = QPushButton("Reset to Auto-Detect")
|
|
99
|
+
self.clear_embedding_btn.setMaximumWidth(140)
|
|
100
|
+
self.clear_embedding_btn.setToolTip(
|
|
101
|
+
"Remove custom embedding model and use automatic detection based on collection dimension."
|
|
102
|
+
)
|
|
103
|
+
self.clear_embedding_btn.clicked.connect(self._clear_embedding_model)
|
|
104
|
+
self.clear_embedding_btn.setEnabled(False)
|
|
105
|
+
|
|
91
106
|
embedding_layout.addWidget(embedding_label)
|
|
92
107
|
embedding_layout.addWidget(self.embedding_model_label, 1)
|
|
93
108
|
embedding_layout.addWidget(self.configure_embedding_btn)
|
|
94
|
-
|
|
109
|
+
embedding_layout.addWidget(self.clear_embedding_btn)
|
|
110
|
+
|
|
111
|
+
# Add the embedding row and other collection info widgets to the collection layout
|
|
95
112
|
collection_layout.addWidget(self.collection_name_label)
|
|
96
113
|
collection_layout.addWidget(self.vector_dim_label)
|
|
97
114
|
collection_layout.addWidget(self.distance_metric_label)
|
|
98
115
|
collection_layout.addWidget(self.total_points_label)
|
|
99
116
|
collection_layout.addWidget(embedding_row)
|
|
100
|
-
|
|
117
|
+
|
|
101
118
|
# Payload Schema subsection
|
|
102
119
|
schema_label = QLabel("<b>Payload Schema:</b>")
|
|
103
120
|
collection_layout.addWidget(schema_label)
|
|
104
|
-
|
|
121
|
+
|
|
105
122
|
self.schema_label = QLabel("N/A")
|
|
106
123
|
self.schema_label.setWordWrap(True)
|
|
107
124
|
self.schema_label.setStyleSheet("color: gray; padding-left: 20px;")
|
|
108
125
|
collection_layout.addWidget(self.schema_label)
|
|
109
|
-
|
|
126
|
+
|
|
110
127
|
# Provider-specific details
|
|
111
128
|
provider_details_label = QLabel("<b>Provider-Specific Details:</b>")
|
|
112
129
|
collection_layout.addWidget(provider_details_label)
|
|
113
|
-
|
|
130
|
+
|
|
114
131
|
self.provider_details_label = QLabel("N/A")
|
|
115
132
|
self.provider_details_label.setWordWrap(True)
|
|
116
133
|
self.provider_details_label.setStyleSheet("color: gray; padding-left: 20px;")
|
|
117
134
|
collection_layout.addWidget(self.provider_details_label)
|
|
118
|
-
|
|
135
|
+
|
|
119
136
|
self.collection_group.setLayout(collection_layout)
|
|
120
137
|
container_layout.addWidget(self.collection_group)
|
|
121
|
-
|
|
138
|
+
|
|
122
139
|
# Add stretch to push content to top
|
|
123
140
|
container_layout.addStretch()
|
|
124
|
-
|
|
141
|
+
|
|
125
142
|
scroll.setWidget(container)
|
|
126
143
|
layout.addWidget(scroll)
|
|
127
|
-
|
|
144
|
+
|
|
128
145
|
# Initial state
|
|
129
146
|
self.refresh_database_info()
|
|
130
|
-
|
|
147
|
+
|
|
148
|
+
def _clear_embedding_model(self):
|
|
149
|
+
"""Clear the embedding model configuration for this collection (reset to autodetect)."""
|
|
150
|
+
from ...services.settings_service import SettingsService
|
|
151
|
+
|
|
152
|
+
settings = SettingsService()
|
|
153
|
+
settings.remove_embedding_model(self.connection_id, self.current_collection)
|
|
154
|
+
# Refresh display (force reload collection info)
|
|
155
|
+
if self.current_collection:
|
|
156
|
+
self.set_collection(self.current_collection, self.current_database)
|
|
157
|
+
log_info(
|
|
158
|
+
"✓ Cleared embedding model configuration for '%s' (via info panel button)",
|
|
159
|
+
self.current_collection,
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
def _update_clear_button_state(self):
|
|
163
|
+
"""Update the clear button state based on current configuration."""
|
|
164
|
+
from ...services.settings_service import SettingsService
|
|
165
|
+
|
|
166
|
+
if not self.connection_id or not self.current_collection:
|
|
167
|
+
self.clear_embedding_btn.setEnabled(False)
|
|
168
|
+
return
|
|
169
|
+
|
|
170
|
+
# Check if there's a user-configured model in settings
|
|
171
|
+
settings = SettingsService()
|
|
172
|
+
model_info = settings.get_embedding_model(self.connection_id, self.current_collection)
|
|
173
|
+
|
|
174
|
+
# Enable button if there's a user-configured model
|
|
175
|
+
self.clear_embedding_btn.setEnabled(model_info is not None)
|
|
176
|
+
|
|
131
177
|
def _create_info_row(self, label: str, value: str) -> QWidget:
|
|
132
178
|
"""Create a row with label and value."""
|
|
133
179
|
row = QWidget()
|
|
134
180
|
row_layout = QHBoxLayout(row)
|
|
135
181
|
row_layout.setContentsMargins(0, 2, 0, 2)
|
|
136
|
-
|
|
182
|
+
|
|
137
183
|
label_widget = QLabel(f"<b>{label}</b>")
|
|
138
184
|
label_widget.setMinimumWidth(150)
|
|
139
185
|
row_layout.addWidget(label_widget)
|
|
140
|
-
|
|
186
|
+
|
|
141
187
|
value_widget = QLabel(value)
|
|
142
188
|
value_widget.setWordWrap(True)
|
|
143
189
|
value_widget.setStyleSheet("color: white;")
|
|
144
190
|
row_layout.addWidget(value_widget, stretch=1)
|
|
145
|
-
|
|
191
|
+
|
|
146
192
|
# Store value widget for later updates (use setProperty for type safety)
|
|
147
193
|
row.setProperty("value_label", value_widget)
|
|
148
|
-
|
|
194
|
+
|
|
149
195
|
return row
|
|
150
|
-
|
|
196
|
+
|
|
151
197
|
def refresh_database_info(self):
|
|
152
198
|
"""Refresh database connection information."""
|
|
153
199
|
if not self.connection or not self.connection.is_connected:
|
|
@@ -165,11 +211,11 @@ class InfoPanel(QWidget):
|
|
|
165
211
|
self.schema_label.setText("N/A")
|
|
166
212
|
self.provider_details_label.setText("N/A")
|
|
167
213
|
return
|
|
168
|
-
|
|
214
|
+
|
|
169
215
|
# Get provider name
|
|
170
216
|
provider_name = self.connection.__class__.__name__.replace("Connection", "")
|
|
171
217
|
self._update_label(self.provider_label, provider_name)
|
|
172
|
-
|
|
218
|
+
|
|
173
219
|
# Get connection details
|
|
174
220
|
if isinstance(self.connection, ChromaDBConnection):
|
|
175
221
|
if self.connection.path:
|
|
@@ -177,12 +223,14 @@ class InfoPanel(QWidget):
|
|
|
177
223
|
self._update_label(self.endpoint_label, self.connection.path)
|
|
178
224
|
elif self.connection.host and self.connection.port:
|
|
179
225
|
self._update_label(self.connection_type_label, "HTTP (Remote)")
|
|
180
|
-
self._update_label(
|
|
226
|
+
self._update_label(
|
|
227
|
+
self.endpoint_label, f"{self.connection.host}:{self.connection.port}"
|
|
228
|
+
)
|
|
181
229
|
else:
|
|
182
230
|
self._update_label(self.connection_type_label, "Ephemeral (In-Memory)")
|
|
183
231
|
self._update_label(self.endpoint_label, "N/A")
|
|
184
232
|
self._update_label(self.api_key_label, "Not required")
|
|
185
|
-
|
|
233
|
+
|
|
186
234
|
elif isinstance(self.connection, QdrantConnection):
|
|
187
235
|
if self.connection.path:
|
|
188
236
|
self._update_label(self.connection_type_label, "Embedded (Local)")
|
|
@@ -192,20 +240,22 @@ class InfoPanel(QWidget):
|
|
|
192
240
|
self._update_label(self.endpoint_label, self.connection.url)
|
|
193
241
|
elif self.connection.host:
|
|
194
242
|
self._update_label(self.connection_type_label, "Remote (Host)")
|
|
195
|
-
self._update_label(
|
|
243
|
+
self._update_label(
|
|
244
|
+
self.endpoint_label, f"{self.connection.host}:{self.connection.port}"
|
|
245
|
+
)
|
|
196
246
|
else:
|
|
197
247
|
self._update_label(self.connection_type_label, "In-Memory")
|
|
198
248
|
self._update_label(self.endpoint_label, "N/A")
|
|
199
|
-
|
|
249
|
+
|
|
200
250
|
if self.connection.api_key:
|
|
201
251
|
self._update_label(self.api_key_label, "Present (hidden)")
|
|
202
252
|
else:
|
|
203
253
|
self._update_label(self.api_key_label, "Not configured")
|
|
204
|
-
|
|
254
|
+
|
|
205
255
|
elif isinstance(self.connection, PineconeConnection):
|
|
206
256
|
self._update_label(self.connection_type_label, "Cloud")
|
|
207
257
|
self._update_label(self.endpoint_label, "Pinecone Cloud")
|
|
208
|
-
|
|
258
|
+
|
|
209
259
|
if self.connection.api_key:
|
|
210
260
|
self._update_label(self.api_key_label, "Present (hidden)")
|
|
211
261
|
else:
|
|
@@ -214,17 +264,19 @@ class InfoPanel(QWidget):
|
|
|
214
264
|
self._update_label(self.connection_type_label, "Unknown")
|
|
215
265
|
self._update_label(self.endpoint_label, "N/A")
|
|
216
266
|
self._update_label(self.api_key_label, "Unknown")
|
|
217
|
-
|
|
267
|
+
|
|
218
268
|
# Status
|
|
219
|
-
self._update_label(
|
|
220
|
-
|
|
269
|
+
self._update_label(
|
|
270
|
+
self.status_label, "Connected" if self.connection.is_connected else "Disconnected"
|
|
271
|
+
)
|
|
272
|
+
|
|
221
273
|
# Count collections
|
|
222
274
|
try:
|
|
223
275
|
collections = self.connection.list_collections()
|
|
224
276
|
self._update_label(self.collections_count_label, str(len(collections)))
|
|
225
277
|
except Exception as e:
|
|
226
278
|
self._update_label(self.collections_count_label, "Error")
|
|
227
|
-
|
|
279
|
+
|
|
228
280
|
def refresh_collection_info(self):
|
|
229
281
|
"""Refresh collection-specific information."""
|
|
230
282
|
if not self.current_collection or not self.connection or not self.connection.is_connected:
|
|
@@ -235,11 +287,11 @@ class InfoPanel(QWidget):
|
|
|
235
287
|
self.schema_label.setText("N/A")
|
|
236
288
|
self.provider_details_label.setText("N/A")
|
|
237
289
|
return
|
|
238
|
-
|
|
290
|
+
|
|
239
291
|
try:
|
|
240
292
|
# Get collection info from database
|
|
241
293
|
collection_info = self.connection.get_collection_info(self.current_collection)
|
|
242
|
-
|
|
294
|
+
|
|
243
295
|
if not collection_info:
|
|
244
296
|
self._update_label(self.collection_name_label, self.current_collection)
|
|
245
297
|
self._update_label(self.vector_dim_label, "Unable to retrieve")
|
|
@@ -248,20 +300,24 @@ class InfoPanel(QWidget):
|
|
|
248
300
|
self.schema_label.setText("Unable to retrieve collection info")
|
|
249
301
|
self.provider_details_label.setText("N/A")
|
|
250
302
|
return
|
|
251
|
-
|
|
303
|
+
|
|
252
304
|
# Display the info
|
|
253
305
|
self._display_collection_info(collection_info)
|
|
254
|
-
|
|
306
|
+
|
|
255
307
|
# Save to cache
|
|
256
308
|
if self.current_database and self.current_collection:
|
|
257
|
-
|
|
309
|
+
log_info(
|
|
310
|
+
"[InfoPanel] Saving collection info to cache: db='%s', coll='%s'",
|
|
311
|
+
self.current_database,
|
|
312
|
+
self.current_collection,
|
|
313
|
+
)
|
|
258
314
|
self.cache_manager.update(
|
|
259
315
|
self.current_database,
|
|
260
316
|
self.current_collection,
|
|
261
|
-
user_inputs={
|
|
317
|
+
user_inputs={"collection_info": collection_info},
|
|
262
318
|
)
|
|
263
|
-
|
|
264
|
-
|
|
319
|
+
log_info("[InfoPanel] ✓ Saved collection info to cache.")
|
|
320
|
+
|
|
265
321
|
except Exception as e:
|
|
266
322
|
self._update_label(self.collection_name_label, self.current_collection)
|
|
267
323
|
self._update_label(self.vector_dim_label, "Error")
|
|
@@ -270,50 +326,55 @@ class InfoPanel(QWidget):
|
|
|
270
326
|
self.schema_label.setText(f"Error: {str(e)}")
|
|
271
327
|
self.schema_label.setStyleSheet("color: red; padding-left: 20px;")
|
|
272
328
|
self.provider_details_label.setText("N/A")
|
|
273
|
-
|
|
329
|
+
|
|
274
330
|
def _display_collection_info(self, collection_info: Dict[str, Any]):
|
|
275
331
|
"""Display collection information (from cache or fresh query)."""
|
|
276
332
|
# Update basic info
|
|
277
333
|
self._update_label(self.collection_name_label, self.current_collection)
|
|
278
|
-
|
|
334
|
+
|
|
279
335
|
# Vector dimension
|
|
280
336
|
vector_dim = collection_info.get("vector_dimension", "Unknown")
|
|
281
337
|
self._update_label(self.vector_dim_label, str(vector_dim))
|
|
282
|
-
|
|
338
|
+
|
|
283
339
|
# Enable configure button if we have a valid dimension
|
|
284
340
|
self.configure_embedding_btn.setEnabled(
|
|
285
341
|
vector_dim != "Unknown" and isinstance(vector_dim, int)
|
|
286
342
|
)
|
|
287
|
-
|
|
343
|
+
|
|
288
344
|
# Update embedding model display
|
|
289
345
|
self._update_embedding_model_display(collection_info)
|
|
290
|
-
|
|
346
|
+
|
|
347
|
+
# Update clear button state
|
|
348
|
+
self._update_clear_button_state()
|
|
349
|
+
|
|
291
350
|
# Distance metric
|
|
292
351
|
distance = collection_info.get("distance_metric", "Unknown")
|
|
293
352
|
self._update_label(self.distance_metric_label, distance)
|
|
294
|
-
|
|
353
|
+
|
|
295
354
|
# Total points
|
|
296
355
|
count = collection_info.get("count", 0)
|
|
297
356
|
self._update_label(self.total_points_label, f"{count:,}")
|
|
298
|
-
|
|
357
|
+
|
|
299
358
|
# Metadata schema
|
|
300
359
|
metadata_fields = collection_info.get("metadata_fields", [])
|
|
301
360
|
if metadata_fields:
|
|
302
361
|
schema_text = "\n".join([f"• {field}" for field in sorted(metadata_fields)])
|
|
303
362
|
self.schema_label.setText(schema_text)
|
|
304
|
-
self.schema_label.setStyleSheet(
|
|
363
|
+
self.schema_label.setStyleSheet(
|
|
364
|
+
"color: white; padding-left: 20px; font-family: monospace;"
|
|
365
|
+
)
|
|
305
366
|
else:
|
|
306
367
|
self.schema_label.setText("No metadata fields found")
|
|
307
368
|
self.schema_label.setStyleSheet("color: gray; padding-left: 20px;")
|
|
308
|
-
|
|
369
|
+
|
|
309
370
|
# Provider-specific details
|
|
310
371
|
details_list = []
|
|
311
|
-
|
|
372
|
+
|
|
312
373
|
if isinstance(self.connection, ChromaDBConnection):
|
|
313
374
|
details_list.append("• Provider: ChromaDB")
|
|
314
375
|
details_list.append("• Supports: Documents, Metadata, Embeddings")
|
|
315
376
|
details_list.append("• Default embedding: all-MiniLM-L6-v2")
|
|
316
|
-
|
|
377
|
+
|
|
317
378
|
elif isinstance(self.connection, QdrantConnection):
|
|
318
379
|
details_list.append("• Provider: Qdrant")
|
|
319
380
|
details_list.append("• Supports: Points, Payload, Vectors")
|
|
@@ -326,8 +387,10 @@ class InfoPanel(QWidget):
|
|
|
326
387
|
details_list.append(f"• HNSW ef_construct: {hnsw.get('ef_construct', 'N/A')}")
|
|
327
388
|
if "optimizer_config" in config:
|
|
328
389
|
opt = config["optimizer_config"]
|
|
329
|
-
details_list.append(
|
|
330
|
-
|
|
390
|
+
details_list.append(
|
|
391
|
+
f"• Indexing threshold: {opt.get('indexing_threshold', 'N/A')}"
|
|
392
|
+
)
|
|
393
|
+
|
|
331
394
|
elif isinstance(self.connection, PineconeConnection):
|
|
332
395
|
details_list.append("• Provider: Pinecone")
|
|
333
396
|
details_list.append("• Supports: Vectors, Metadata")
|
|
@@ -339,14 +402,16 @@ class InfoPanel(QWidget):
|
|
|
339
402
|
details_list.append(f"• Status: {collection_info['status']}")
|
|
340
403
|
if "spec" in collection_info:
|
|
341
404
|
details_list.append(f"• Spec: {collection_info['spec']}")
|
|
342
|
-
|
|
405
|
+
|
|
343
406
|
if details_list:
|
|
344
407
|
self.provider_details_label.setText("\n".join(details_list))
|
|
345
|
-
self.provider_details_label.setStyleSheet(
|
|
408
|
+
self.provider_details_label.setStyleSheet(
|
|
409
|
+
"color: white; padding-left: 20px; font-family: monospace;"
|
|
410
|
+
)
|
|
346
411
|
else:
|
|
347
412
|
self.provider_details_label.setText("No additional details available")
|
|
348
413
|
self.provider_details_label.setStyleSheet("color: gray; padding-left: 20px;")
|
|
349
|
-
|
|
414
|
+
|
|
350
415
|
def set_collection(self, collection_name: str, database_name: str = ""):
|
|
351
416
|
"""Set the current collection and refresh its information."""
|
|
352
417
|
self.current_collection = collection_name
|
|
@@ -354,75 +419,89 @@ class InfoPanel(QWidget):
|
|
|
354
419
|
if database_name:
|
|
355
420
|
self.current_database = database_name
|
|
356
421
|
self.connection_id = database_name # database_name is the connection ID
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
422
|
+
|
|
423
|
+
log_info(
|
|
424
|
+
"[InfoPanel] Setting collection: db='%s', coll='%s'",
|
|
425
|
+
self.current_database,
|
|
426
|
+
collection_name,
|
|
427
|
+
)
|
|
428
|
+
|
|
360
429
|
# Check cache first for collection info
|
|
361
430
|
cached = self.cache_manager.get(self.current_database, self.current_collection)
|
|
362
|
-
if cached and hasattr(cached,
|
|
363
|
-
|
|
364
|
-
collection_info = cached.user_inputs[
|
|
431
|
+
if cached and hasattr(cached, "user_inputs") and cached.user_inputs.get("collection_info"):
|
|
432
|
+
log_info("[InfoPanel] ✓ Cache HIT! Loading collection info from cache.")
|
|
433
|
+
collection_info = cached.user_inputs["collection_info"]
|
|
365
434
|
self._display_collection_info(collection_info)
|
|
366
435
|
return
|
|
367
|
-
|
|
368
|
-
|
|
436
|
+
|
|
437
|
+
log_info("[InfoPanel] ✗ Cache MISS. Loading collection info from database...")
|
|
369
438
|
self.refresh_collection_info()
|
|
370
|
-
|
|
439
|
+
|
|
371
440
|
def _update_label(self, row_widget: QWidget, value: str):
|
|
372
441
|
"""Update the value label in an info row."""
|
|
373
442
|
value_label = row_widget.property("value_label")
|
|
374
443
|
if value_label and isinstance(value_label, QLabel):
|
|
375
444
|
value_label.setText(value)
|
|
376
|
-
|
|
445
|
+
|
|
377
446
|
def _update_embedding_model_display(self, collection_info: Dict[str, Any]):
|
|
378
447
|
"""Update the embedding model label based on current configuration."""
|
|
379
448
|
from ...services.settings_service import SettingsService
|
|
380
|
-
|
|
449
|
+
|
|
381
450
|
# Check if stored in collection metadata
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
451
|
+
# Default: disable clear button
|
|
452
|
+
self.clear_embedding_btn.setEnabled(False)
|
|
453
|
+
|
|
454
|
+
if "embedding_model" in collection_info:
|
|
455
|
+
model_name = collection_info["embedding_model"]
|
|
456
|
+
model_type = collection_info.get("embedding_model_type", "stored")
|
|
385
457
|
self.embedding_model_label.setText(f"{model_name} ({model_type})")
|
|
386
458
|
self.embedding_model_label.setStyleSheet("color: lightgreen;")
|
|
459
|
+
self.clear_embedding_btn.setEnabled(True)
|
|
387
460
|
return
|
|
388
|
-
|
|
461
|
+
|
|
389
462
|
# Try to get from connection using the helper method
|
|
390
463
|
if self.connection and self.current_collection:
|
|
391
|
-
detected_model = self.connection.get_embedding_model(
|
|
464
|
+
detected_model = self.connection.get_embedding_model(
|
|
465
|
+
self.current_collection, self.connection_id
|
|
466
|
+
)
|
|
392
467
|
if detected_model:
|
|
393
468
|
self.embedding_model_label.setText(f"{detected_model} (detected)")
|
|
394
469
|
self.embedding_model_label.setStyleSheet("color: lightgreen;")
|
|
470
|
+
self.clear_embedding_btn.setEnabled(False)
|
|
395
471
|
return
|
|
396
|
-
|
|
472
|
+
|
|
397
473
|
# Check user settings
|
|
398
474
|
settings = SettingsService()
|
|
399
475
|
model_info = settings.get_embedding_model(self.connection_id, self.current_collection)
|
|
400
|
-
|
|
476
|
+
|
|
401
477
|
if model_info:
|
|
402
|
-
model_name = model_info[
|
|
403
|
-
model_type = model_info.get(
|
|
478
|
+
model_name = model_info["model"]
|
|
479
|
+
model_type = model_info.get("type", "unknown")
|
|
404
480
|
self.embedding_model_label.setText(f"{model_name} ({model_type})")
|
|
405
481
|
self.embedding_model_label.setStyleSheet("color: lightblue;")
|
|
482
|
+
self.clear_embedding_btn.setEnabled(True)
|
|
406
483
|
return
|
|
407
|
-
|
|
484
|
+
|
|
408
485
|
# No configuration - using auto-detect
|
|
409
486
|
self.embedding_model_label.setText("Auto-detect (dimension-based)")
|
|
410
487
|
self.embedding_model_label.setStyleSheet("color: orange;")
|
|
411
|
-
|
|
488
|
+
self.clear_embedding_btn.setEnabled(False)
|
|
489
|
+
|
|
412
490
|
def _configure_embedding_model(self):
|
|
413
491
|
"""Open dialog to configure embedding model for current collection."""
|
|
414
492
|
if not self.current_collection:
|
|
415
493
|
return
|
|
416
|
-
|
|
494
|
+
|
|
417
495
|
# Show loading immediately; preparing can touch DB/registry
|
|
418
496
|
from ..components.loading_dialog import LoadingDialog
|
|
497
|
+
|
|
419
498
|
loading = LoadingDialog("Preparing model configuration...", self)
|
|
420
499
|
loading.show_loading("Preparing model configuration...")
|
|
421
500
|
QApplication.processEvents()
|
|
422
501
|
|
|
423
502
|
from ..dialogs import ProviderTypeDialog, EmbeddingConfigDialog
|
|
424
503
|
from ...services.settings_service import SettingsService
|
|
425
|
-
|
|
504
|
+
|
|
426
505
|
# Get current collection info
|
|
427
506
|
try:
|
|
428
507
|
collection_info = self.connection.get_collection_info(self.current_collection)
|
|
@@ -431,74 +510,76 @@ class InfoPanel(QWidget):
|
|
|
431
510
|
loading.hide_loading()
|
|
432
511
|
if not collection_info:
|
|
433
512
|
return
|
|
434
|
-
|
|
513
|
+
|
|
435
514
|
vector_dim = collection_info.get("vector_dimension")
|
|
436
515
|
if not vector_dim or vector_dim == "Unknown":
|
|
437
516
|
return
|
|
438
|
-
|
|
517
|
+
|
|
439
518
|
# Get current configuration if any
|
|
440
519
|
settings = SettingsService()
|
|
441
|
-
|
|
520
|
+
|
|
442
521
|
current_model = None
|
|
443
522
|
current_type = None
|
|
444
|
-
|
|
523
|
+
|
|
445
524
|
# Check metadata first
|
|
446
|
-
if
|
|
447
|
-
current_model = collection_info[
|
|
448
|
-
current_type = collection_info.get(
|
|
525
|
+
if "embedding_model" in collection_info:
|
|
526
|
+
current_model = collection_info["embedding_model"]
|
|
527
|
+
current_type = collection_info.get("embedding_model_type", "stored")
|
|
449
528
|
# Then check settings
|
|
450
529
|
else:
|
|
451
530
|
model_info = settings.get_embedding_model(self.connection_id, self.current_collection)
|
|
452
531
|
if model_info:
|
|
453
|
-
current_model = model_info.get(
|
|
454
|
-
current_type = model_info.get(
|
|
455
|
-
|
|
532
|
+
current_model = model_info.get("model")
|
|
533
|
+
current_type = model_info.get("type")
|
|
534
|
+
|
|
456
535
|
# Step 1: Provider Type Selection
|
|
457
|
-
type_dialog = ProviderTypeDialog(
|
|
458
|
-
|
|
459
|
-
vector_dim,
|
|
460
|
-
self
|
|
461
|
-
)
|
|
462
|
-
|
|
536
|
+
type_dialog = ProviderTypeDialog(self.current_collection, vector_dim, self)
|
|
537
|
+
|
|
463
538
|
type_result = type_dialog.exec()
|
|
464
539
|
if type_result != QDialog.DialogCode.Accepted:
|
|
465
540
|
return # User cancelled
|
|
466
|
-
|
|
541
|
+
|
|
467
542
|
provider_type = type_dialog.get_selected_type()
|
|
468
543
|
if not provider_type:
|
|
469
544
|
return
|
|
470
|
-
|
|
545
|
+
|
|
471
546
|
# Step 2: Model Selection (filtered by provider type)
|
|
472
547
|
model_dialog = EmbeddingConfigDialog(
|
|
473
|
-
self.current_collection,
|
|
474
|
-
vector_dim,
|
|
475
|
-
provider_type,
|
|
476
|
-
current_model,
|
|
477
|
-
current_type,
|
|
478
|
-
self
|
|
548
|
+
self.current_collection, vector_dim, provider_type, current_model, current_type, self
|
|
479
549
|
)
|
|
480
|
-
|
|
550
|
+
|
|
481
551
|
# Optionally show brief loading while populating models
|
|
482
552
|
# (dialog itself handles content; only show if provider lists are large)
|
|
483
553
|
result = model_dialog.exec()
|
|
484
|
-
|
|
554
|
+
|
|
485
555
|
if result == QDialog.DialogCode.Accepted:
|
|
486
556
|
# Save the configuration using the new SettingsService method
|
|
487
557
|
selection = model_dialog.get_selection()
|
|
488
558
|
if selection:
|
|
489
559
|
model_name, model_type = selection
|
|
490
|
-
settings.save_embedding_model(
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
560
|
+
settings.save_embedding_model(
|
|
561
|
+
self.connection_id, self.current_collection, model_name, model_type
|
|
562
|
+
)
|
|
563
|
+
|
|
564
|
+
# Update the display immediately to show new model
|
|
565
|
+
self.embedding_model_label.setText(f"{model_name} ({model_type})")
|
|
566
|
+
self.embedding_model_label.setStyleSheet("color: lightblue;")
|
|
567
|
+
|
|
568
|
+
# Enable the clear button
|
|
569
|
+
self._update_clear_button_state()
|
|
570
|
+
|
|
571
|
+
log_info(
|
|
572
|
+
"✓ Configured embedding model for '%s': %s (%s)",
|
|
573
|
+
self.current_collection,
|
|
574
|
+
model_name,
|
|
575
|
+
model_type,
|
|
576
|
+
)
|
|
577
|
+
|
|
497
578
|
elif result == 2: # Clear configuration
|
|
498
579
|
# Remove from settings using the new SettingsService method
|
|
499
580
|
settings.remove_embedding_model(self.connection_id, self.current_collection)
|
|
500
|
-
|
|
581
|
+
|
|
501
582
|
# Refresh display
|
|
502
583
|
self._update_embedding_model_display(collection_info)
|
|
503
|
-
|
|
504
|
-
|
|
584
|
+
|
|
585
|
+
log_info("✓ Cleared embedding model configuration for '%s'", self.current_collection)
|