vector-inspector 0.3.2__py3-none-any.whl → 0.3.4__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 (32) hide show
  1. vector_inspector/core/connection_manager.py +55 -49
  2. vector_inspector/core/connections/base_connection.py +41 -41
  3. vector_inspector/core/connections/chroma_connection.py +110 -86
  4. vector_inspector/core/connections/pinecone_connection.py +168 -182
  5. vector_inspector/core/connections/qdrant_connection.py +109 -126
  6. vector_inspector/core/connections/qdrant_helpers/__init__.py +4 -0
  7. vector_inspector/core/connections/qdrant_helpers/qdrant_embedding_resolver.py +35 -0
  8. vector_inspector/core/connections/qdrant_helpers/qdrant_filter_builder.py +51 -0
  9. vector_inspector/core/connections/template_connection.py +55 -65
  10. vector_inspector/core/embedding_utils.py +32 -32
  11. vector_inspector/core/logging.py +27 -0
  12. vector_inspector/core/model_registry.py +4 -3
  13. vector_inspector/main.py +6 -2
  14. vector_inspector/services/backup_helpers.py +63 -0
  15. vector_inspector/services/backup_restore_service.py +73 -152
  16. vector_inspector/services/credential_service.py +33 -40
  17. vector_inspector/services/import_export_service.py +70 -67
  18. vector_inspector/services/profile_service.py +92 -94
  19. vector_inspector/services/settings_service.py +68 -48
  20. vector_inspector/services/visualization_service.py +40 -39
  21. vector_inspector/ui/components/splash_window.py +57 -0
  22. vector_inspector/ui/dialogs/cross_db_migration.py +6 -5
  23. vector_inspector/ui/main_window.py +200 -146
  24. vector_inspector/ui/views/info_panel.py +208 -127
  25. vector_inspector/ui/views/metadata_view.py +8 -7
  26. vector_inspector/ui/views/search_view.py +97 -75
  27. vector_inspector/ui/views/visualization_view.py +140 -97
  28. vector_inspector/utils/version.py +5 -0
  29. {vector_inspector-0.3.2.dist-info → vector_inspector-0.3.4.dist-info}/METADATA +10 -2
  30. {vector_inspector-0.3.2.dist-info → vector_inspector-0.3.4.dist-info}/RECORD +32 -25
  31. {vector_inspector-0.3.2.dist-info → vector_inspector-0.3.4.dist-info}/WHEEL +0 -0
  32. {vector_inspector-0.3.2.dist-info → vector_inspector-0.3.4.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, 'r', encoding='utf-8') as f:
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
- print(f"Failed to load profiles: {e}")
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, 'w', encoding='utf-8') as f:
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
- print(f"Failed to save profiles: {e}")
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