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
|
@@ -5,24 +5,25 @@ import uuid
|
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
from typing import List, Dict, Any, Optional
|
|
7
7
|
from PySide6.QtCore import QObject, Signal
|
|
8
|
+
from vector_inspector.core.logging import log_error
|
|
8
9
|
|
|
9
10
|
from .credential_service import CredentialService
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
class ConnectionProfile:
|
|
13
14
|
"""Represents a saved connection profile."""
|
|
14
|
-
|
|
15
|
+
|
|
15
16
|
def __init__(
|
|
16
17
|
self,
|
|
17
18
|
profile_id: str,
|
|
18
19
|
name: str,
|
|
19
20
|
provider: str,
|
|
20
21
|
config: Dict[str, Any],
|
|
21
|
-
credential_fields: Optional[List[str]] = None
|
|
22
|
+
credential_fields: Optional[List[str]] = None,
|
|
22
23
|
):
|
|
23
24
|
"""
|
|
24
25
|
Initialize a connection profile.
|
|
25
|
-
|
|
26
|
+
|
|
26
27
|
Args:
|
|
27
28
|
profile_id: Unique profile identifier
|
|
28
29
|
name: User-friendly profile name
|
|
@@ -35,7 +36,7 @@ class ConnectionProfile:
|
|
|
35
36
|
self.provider = provider
|
|
36
37
|
self.config = config
|
|
37
38
|
self.credential_fields = credential_fields or []
|
|
38
|
-
|
|
39
|
+
|
|
39
40
|
def to_dict(self) -> Dict[str, Any]:
|
|
40
41
|
"""Convert profile to dictionary (without credentials)."""
|
|
41
42
|
return {
|
|
@@ -43,9 +44,9 @@ class ConnectionProfile:
|
|
|
43
44
|
"name": self.name,
|
|
44
45
|
"provider": self.provider,
|
|
45
46
|
"config": self.config,
|
|
46
|
-
"credential_fields": self.credential_fields
|
|
47
|
+
"credential_fields": self.credential_fields,
|
|
47
48
|
}
|
|
48
|
-
|
|
49
|
+
|
|
49
50
|
@classmethod
|
|
50
51
|
def from_dict(cls, data: Dict[str, Any]) -> "ConnectionProfile":
|
|
51
52
|
"""Create profile from dictionary."""
|
|
@@ -54,24 +55,24 @@ class ConnectionProfile:
|
|
|
54
55
|
name=data["name"],
|
|
55
56
|
provider=data["provider"],
|
|
56
57
|
config=data.get("config", {}),
|
|
57
|
-
credential_fields=data.get("credential_fields", [])
|
|
58
|
+
credential_fields=data.get("credential_fields", []),
|
|
58
59
|
)
|
|
59
60
|
|
|
60
61
|
|
|
61
62
|
class ProfileService(QObject):
|
|
62
63
|
"""Manages connection profiles and persistence.
|
|
63
|
-
|
|
64
|
+
|
|
64
65
|
Signals:
|
|
65
66
|
profile_added: Emitted when a profile is added (profile_id)
|
|
66
67
|
profile_updated: Emitted when a profile is updated (profile_id)
|
|
67
68
|
profile_deleted: Emitted when a profile is deleted (profile_id)
|
|
68
69
|
"""
|
|
69
|
-
|
|
70
|
+
|
|
70
71
|
# Signals
|
|
71
72
|
profile_added = Signal(str) # profile_id
|
|
72
73
|
profile_updated = Signal(str) # profile_id
|
|
73
74
|
profile_deleted = Signal(str) # profile_id
|
|
74
|
-
|
|
75
|
+
|
|
75
76
|
def __init__(self):
|
|
76
77
|
"""Initialize profile service."""
|
|
77
78
|
super().__init__()
|
|
@@ -81,218 +82,218 @@ class ProfileService(QObject):
|
|
|
81
82
|
self._profiles: Dict[str, ConnectionProfile] = {}
|
|
82
83
|
self._last_active_connections: List[str] = []
|
|
83
84
|
self._load_profiles()
|
|
84
|
-
|
|
85
|
+
|
|
85
86
|
def _load_profiles(self):
|
|
86
87
|
"""Load profiles from disk."""
|
|
87
88
|
try:
|
|
88
89
|
if self.profiles_file.exists():
|
|
89
|
-
with open(self.profiles_file,
|
|
90
|
+
with open(self.profiles_file, "r", encoding="utf-8") as f:
|
|
90
91
|
data = json.load(f)
|
|
91
|
-
|
|
92
|
+
|
|
92
93
|
# Load profiles
|
|
93
94
|
for profile_data in data.get("profiles", []):
|
|
94
95
|
profile = ConnectionProfile.from_dict(profile_data)
|
|
95
96
|
self._profiles[profile.id] = profile
|
|
96
|
-
|
|
97
|
+
|
|
97
98
|
# Load last active connections
|
|
98
99
|
self._last_active_connections = data.get("last_active_connections", [])
|
|
99
100
|
except Exception as e:
|
|
100
|
-
|
|
101
|
+
log_error("Failed to load profiles: %s", e)
|
|
101
102
|
self._profiles = {}
|
|
102
103
|
self._last_active_connections = []
|
|
103
|
-
|
|
104
|
+
|
|
104
105
|
def _save_profiles(self):
|
|
105
106
|
"""Save profiles to disk."""
|
|
106
107
|
try:
|
|
107
108
|
# Create directory if it doesn't exist
|
|
108
109
|
self.profiles_dir.mkdir(parents=True, exist_ok=True)
|
|
109
|
-
|
|
110
|
+
|
|
110
111
|
# Prepare data
|
|
111
112
|
data = {
|
|
112
113
|
"profiles": [profile.to_dict() for profile in self._profiles.values()],
|
|
113
|
-
"last_active_connections": self._last_active_connections
|
|
114
|
+
"last_active_connections": self._last_active_connections,
|
|
114
115
|
}
|
|
115
|
-
|
|
116
|
+
|
|
116
117
|
# Write to file
|
|
117
|
-
with open(self.profiles_file,
|
|
118
|
+
with open(self.profiles_file, "w", encoding="utf-8") as f:
|
|
118
119
|
json.dump(data, f, indent=2, ensure_ascii=False)
|
|
119
120
|
except Exception as e:
|
|
120
|
-
|
|
121
|
-
|
|
121
|
+
log_error("Failed to save profiles: %s", e)
|
|
122
|
+
|
|
122
123
|
def create_profile(
|
|
123
124
|
self,
|
|
124
125
|
name: str,
|
|
125
126
|
provider: str,
|
|
126
127
|
config: Dict[str, Any],
|
|
127
|
-
credentials: Optional[Dict[str, Any]] = None
|
|
128
|
+
credentials: Optional[Dict[str, Any]] = None,
|
|
128
129
|
) -> str:
|
|
129
130
|
"""
|
|
130
131
|
Create a new connection profile.
|
|
131
|
-
|
|
132
|
+
|
|
132
133
|
Args:
|
|
133
134
|
name: Profile name
|
|
134
135
|
provider: Provider type
|
|
135
136
|
config: Connection configuration (non-sensitive)
|
|
136
137
|
credentials: Credential data to store securely (optional)
|
|
137
|
-
|
|
138
|
+
|
|
138
139
|
Returns:
|
|
139
140
|
The profile ID
|
|
140
141
|
"""
|
|
141
142
|
profile_id = str(uuid.uuid4())
|
|
142
143
|
credential_fields = list(credentials.keys()) if credentials else []
|
|
143
|
-
|
|
144
|
+
|
|
144
145
|
profile = ConnectionProfile(
|
|
145
146
|
profile_id=profile_id,
|
|
146
147
|
name=name,
|
|
147
148
|
provider=provider,
|
|
148
149
|
config=config,
|
|
149
|
-
credential_fields=credential_fields
|
|
150
|
+
credential_fields=credential_fields,
|
|
150
151
|
)
|
|
151
|
-
|
|
152
|
+
|
|
152
153
|
self._profiles[profile_id] = profile
|
|
153
|
-
|
|
154
|
+
|
|
154
155
|
# Store credentials if provided
|
|
155
156
|
if credentials:
|
|
156
157
|
self.credential_service.store_credentials(profile_id, credentials)
|
|
157
|
-
|
|
158
|
+
|
|
158
159
|
self._save_profiles()
|
|
159
160
|
self.profile_added.emit(profile_id)
|
|
160
|
-
|
|
161
|
+
|
|
161
162
|
return profile_id
|
|
162
|
-
|
|
163
|
+
|
|
163
164
|
def get_profile(self, profile_id: str) -> Optional[ConnectionProfile]:
|
|
164
165
|
"""Get a profile by ID."""
|
|
165
166
|
return self._profiles.get(profile_id)
|
|
166
|
-
|
|
167
|
+
|
|
167
168
|
def get_all_profiles(self) -> List[ConnectionProfile]:
|
|
168
169
|
"""Get all saved profiles."""
|
|
169
170
|
return list(self._profiles.values())
|
|
170
|
-
|
|
171
|
+
|
|
171
172
|
def update_profile(
|
|
172
173
|
self,
|
|
173
174
|
profile_id: str,
|
|
174
175
|
name: Optional[str] = None,
|
|
175
176
|
config: Optional[Dict[str, Any]] = None,
|
|
176
|
-
credentials: Optional[Dict[str, Any]] = None
|
|
177
|
+
credentials: Optional[Dict[str, Any]] = None,
|
|
177
178
|
) -> bool:
|
|
178
179
|
"""
|
|
179
180
|
Update an existing profile.
|
|
180
|
-
|
|
181
|
+
|
|
181
182
|
Args:
|
|
182
183
|
profile_id: ID of profile to update
|
|
183
184
|
name: New name (optional)
|
|
184
185
|
config: New configuration (optional)
|
|
185
186
|
credentials: New credentials (optional)
|
|
186
|
-
|
|
187
|
+
|
|
187
188
|
Returns:
|
|
188
189
|
True if successful, False if profile not found
|
|
189
190
|
"""
|
|
190
191
|
profile = self._profiles.get(profile_id)
|
|
191
192
|
if not profile:
|
|
192
193
|
return False
|
|
193
|
-
|
|
194
|
+
|
|
194
195
|
if name is not None:
|
|
195
196
|
profile.name = name
|
|
196
|
-
|
|
197
|
+
|
|
197
198
|
if config is not None:
|
|
198
199
|
profile.config = config
|
|
199
|
-
|
|
200
|
+
|
|
200
201
|
if credentials is not None:
|
|
201
202
|
profile.credential_fields = list(credentials.keys())
|
|
202
203
|
self.credential_service.store_credentials(profile_id, credentials)
|
|
203
|
-
|
|
204
|
+
|
|
204
205
|
self._save_profiles()
|
|
205
206
|
self.profile_updated.emit(profile_id)
|
|
206
|
-
|
|
207
|
+
|
|
207
208
|
return True
|
|
208
|
-
|
|
209
|
+
|
|
209
210
|
def delete_profile(self, profile_id: str) -> bool:
|
|
210
211
|
"""
|
|
211
212
|
Delete a profile.
|
|
212
|
-
|
|
213
|
+
|
|
213
214
|
Args:
|
|
214
215
|
profile_id: ID of profile to delete
|
|
215
|
-
|
|
216
|
+
|
|
216
217
|
Returns:
|
|
217
218
|
True if successful, False if profile not found
|
|
218
219
|
"""
|
|
219
220
|
if profile_id not in self._profiles:
|
|
220
221
|
return False
|
|
221
|
-
|
|
222
|
+
|
|
222
223
|
# Delete credentials
|
|
223
224
|
self.credential_service.delete_credentials(profile_id)
|
|
224
|
-
|
|
225
|
+
|
|
225
226
|
# Remove from profiles
|
|
226
227
|
del self._profiles[profile_id]
|
|
227
|
-
|
|
228
|
+
|
|
228
229
|
# Remove from last active connections if present
|
|
229
230
|
if profile_id in self._last_active_connections:
|
|
230
231
|
self._last_active_connections.remove(profile_id)
|
|
231
|
-
|
|
232
|
+
|
|
232
233
|
self._save_profiles()
|
|
233
234
|
self.profile_deleted.emit(profile_id)
|
|
234
|
-
|
|
235
|
+
|
|
235
236
|
return True
|
|
236
|
-
|
|
237
|
+
|
|
237
238
|
def duplicate_profile(self, profile_id: str, new_name: str) -> Optional[str]:
|
|
238
239
|
"""
|
|
239
240
|
Duplicate an existing profile.
|
|
240
|
-
|
|
241
|
+
|
|
241
242
|
Args:
|
|
242
243
|
profile_id: ID of profile to duplicate
|
|
243
244
|
new_name: Name for the new profile
|
|
244
|
-
|
|
245
|
+
|
|
245
246
|
Returns:
|
|
246
247
|
New profile ID, or None if source profile not found
|
|
247
248
|
"""
|
|
248
249
|
source_profile = self._profiles.get(profile_id)
|
|
249
250
|
if not source_profile:
|
|
250
251
|
return None
|
|
251
|
-
|
|
252
|
+
|
|
252
253
|
# Get credentials from source
|
|
253
254
|
credentials = self.credential_service.get_credentials(profile_id)
|
|
254
|
-
|
|
255
|
+
|
|
255
256
|
# Create new profile
|
|
256
257
|
new_id = self.create_profile(
|
|
257
258
|
name=new_name,
|
|
258
259
|
provider=source_profile.provider,
|
|
259
260
|
config=source_profile.config.copy(),
|
|
260
|
-
credentials=credentials
|
|
261
|
+
credentials=credentials,
|
|
261
262
|
)
|
|
262
|
-
|
|
263
|
+
|
|
263
264
|
return new_id
|
|
264
|
-
|
|
265
|
+
|
|
265
266
|
def get_profile_with_credentials(self, profile_id: str) -> Optional[Dict[str, Any]]:
|
|
266
267
|
"""
|
|
267
268
|
Get a profile along with its credentials.
|
|
268
|
-
|
|
269
|
+
|
|
269
270
|
Args:
|
|
270
271
|
profile_id: ID of profile
|
|
271
|
-
|
|
272
|
+
|
|
272
273
|
Returns:
|
|
273
274
|
Dictionary with profile data and credentials, or None if not found
|
|
274
275
|
"""
|
|
275
276
|
profile = self._profiles.get(profile_id)
|
|
276
277
|
if not profile:
|
|
277
278
|
return None
|
|
278
|
-
|
|
279
|
+
|
|
279
280
|
credentials = self.credential_service.get_credentials(profile_id)
|
|
280
|
-
|
|
281
|
+
|
|
281
282
|
return {
|
|
282
283
|
"id": profile.id,
|
|
283
284
|
"name": profile.name,
|
|
284
285
|
"provider": profile.provider,
|
|
285
286
|
"config": profile.config,
|
|
286
|
-
"credentials": credentials or {}
|
|
287
|
+
"credentials": credentials or {},
|
|
287
288
|
}
|
|
288
|
-
|
|
289
|
+
|
|
289
290
|
def export_profiles(self, include_credentials: bool = False) -> List[Dict[str, Any]]:
|
|
290
291
|
"""
|
|
291
292
|
Export all profiles for backup/sharing.
|
|
292
|
-
|
|
293
|
+
|
|
293
294
|
Args:
|
|
294
295
|
include_credentials: Whether to include credentials (NOT RECOMMENDED)
|
|
295
|
-
|
|
296
|
+
|
|
296
297
|
Returns:
|
|
297
298
|
List of profile dictionaries
|
|
298
299
|
"""
|
|
@@ -305,83 +306,81 @@ class ProfileService(QObject):
|
|
|
305
306
|
data["credentials"] = credentials
|
|
306
307
|
exported.append(data)
|
|
307
308
|
return exported
|
|
308
|
-
|
|
309
|
+
|
|
309
310
|
def import_profiles(
|
|
310
|
-
self,
|
|
311
|
-
profiles_data: List[Dict[str, Any]],
|
|
312
|
-
overwrite: bool = False
|
|
311
|
+
self, profiles_data: List[Dict[str, Any]], overwrite: bool = False
|
|
313
312
|
) -> Dict[str, str]:
|
|
314
313
|
"""
|
|
315
314
|
Import profiles from exported data.
|
|
316
|
-
|
|
315
|
+
|
|
317
316
|
Args:
|
|
318
317
|
profiles_data: List of profile dictionaries
|
|
319
318
|
overwrite: Whether to overwrite existing profiles with same ID
|
|
320
|
-
|
|
319
|
+
|
|
321
320
|
Returns:
|
|
322
321
|
Dictionary mapping old IDs to new IDs
|
|
323
322
|
"""
|
|
324
323
|
id_mapping = {}
|
|
325
|
-
|
|
324
|
+
|
|
326
325
|
for profile_data in profiles_data:
|
|
327
326
|
old_id = profile_data.get("id")
|
|
328
|
-
|
|
327
|
+
|
|
329
328
|
# Generate new ID if not overwriting or ID exists
|
|
330
329
|
if not overwrite or old_id in self._profiles:
|
|
331
330
|
new_id = str(uuid.uuid4())
|
|
332
331
|
else:
|
|
333
332
|
new_id = str(old_id) if old_id else str(uuid.uuid4())
|
|
334
|
-
|
|
333
|
+
|
|
335
334
|
credentials = profile_data.pop("credentials", None)
|
|
336
|
-
|
|
335
|
+
|
|
337
336
|
# Create profile
|
|
338
337
|
profile = ConnectionProfile(
|
|
339
338
|
profile_id=new_id,
|
|
340
339
|
name=profile_data["name"],
|
|
341
340
|
provider=profile_data["provider"],
|
|
342
341
|
config=profile_data.get("config", {}),
|
|
343
|
-
credential_fields=profile_data.get("credential_fields", [])
|
|
342
|
+
credential_fields=profile_data.get("credential_fields", []),
|
|
344
343
|
)
|
|
345
|
-
|
|
344
|
+
|
|
346
345
|
self._profiles[new_id] = profile
|
|
347
|
-
|
|
346
|
+
|
|
348
347
|
# Store credentials if provided
|
|
349
348
|
if credentials:
|
|
350
349
|
self.credential_service.store_credentials(new_id, credentials)
|
|
351
|
-
|
|
350
|
+
|
|
352
351
|
id_mapping[old_id] = new_id
|
|
353
|
-
|
|
352
|
+
|
|
354
353
|
self._save_profiles()
|
|
355
|
-
|
|
354
|
+
|
|
356
355
|
return id_mapping
|
|
357
|
-
|
|
356
|
+
|
|
358
357
|
def save_last_active_connections(self, connection_ids: List[str]):
|
|
359
358
|
"""
|
|
360
359
|
Save list of last active connection profile IDs for session restore.
|
|
361
|
-
|
|
360
|
+
|
|
362
361
|
Args:
|
|
363
362
|
connection_ids: List of profile IDs that were active
|
|
364
363
|
"""
|
|
365
364
|
self._last_active_connections = connection_ids
|
|
366
365
|
self._save_profiles()
|
|
367
|
-
|
|
366
|
+
|
|
368
367
|
def get_last_active_connections(self) -> List[str]:
|
|
369
368
|
"""Get list of last active connection profile IDs."""
|
|
370
369
|
return self._last_active_connections.copy()
|
|
371
|
-
|
|
370
|
+
|
|
372
371
|
def migrate_legacy_connection(self, config: Dict[str, Any]) -> str:
|
|
373
372
|
"""
|
|
374
373
|
Migrate a legacy single-connection configuration to a profile.
|
|
375
|
-
|
|
374
|
+
|
|
376
375
|
Args:
|
|
377
376
|
config: Legacy connection configuration
|
|
378
|
-
|
|
377
|
+
|
|
379
378
|
Returns:
|
|
380
379
|
The new profile ID
|
|
381
380
|
"""
|
|
382
381
|
provider = config.get("provider", "chromadb")
|
|
383
382
|
conn_type = config.get("type", "persistent")
|
|
384
|
-
|
|
383
|
+
|
|
385
384
|
# Create a name based on connection type
|
|
386
385
|
if conn_type == "persistent":
|
|
387
386
|
name = f"Legacy {provider.title()} (Persistent)"
|
|
@@ -390,20 +389,19 @@ class ProfileService(QObject):
|
|
|
390
389
|
name = f"Legacy {provider.title()} ({host})"
|
|
391
390
|
else:
|
|
392
391
|
name = f"Legacy {provider.title()} (Ephemeral)"
|
|
393
|
-
|
|
392
|
+
|
|
394
393
|
# Extract credentials if any
|
|
395
394
|
credentials = {}
|
|
396
395
|
if "api_key" in config and config["api_key"]:
|
|
397
396
|
credentials["api_key"] = config["api_key"]
|
|
398
397
|
del config["api_key"] # Remove from config
|
|
399
|
-
|
|
398
|
+
|
|
400
399
|
# Create profile
|
|
401
400
|
profile_id = self.create_profile(
|
|
402
401
|
name=name,
|
|
403
402
|
provider=provider,
|
|
404
403
|
config=config,
|
|
405
|
-
credentials=credentials if credentials else None
|
|
404
|
+
credentials=credentials if credentials else None,
|
|
406
405
|
)
|
|
407
|
-
|
|
408
|
-
return profile_id
|
|
409
406
|
|
|
407
|
+
return profile_id
|