vector-inspector 0.3.9__py3-none-any.whl → 0.3.12__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/__init__.py +10 -1
- vector_inspector/core/connection_manager.py +91 -19
- vector_inspector/core/connections/base_connection.py +43 -43
- vector_inspector/core/connections/chroma_connection.py +1 -1
- vector_inspector/core/connections/pgvector_connection.py +12 -172
- vector_inspector/core/connections/pinecone_connection.py +596 -99
- vector_inspector/core/connections/qdrant_connection.py +35 -44
- vector_inspector/core/embedding_utils.py +14 -5
- vector_inspector/core/logging.py +3 -1
- vector_inspector/extensions/__init__.py +6 -0
- vector_inspector/extensions/telemetry_settings_panel.py +25 -0
- vector_inspector/main.py +45 -2
- vector_inspector/services/backup_restore_service.py +228 -15
- vector_inspector/services/settings_service.py +79 -19
- vector_inspector/services/telemetry_service.py +88 -0
- vector_inspector/ui/components/backup_restore_dialog.py +215 -101
- vector_inspector/ui/components/connection_manager_panel.py +155 -14
- vector_inspector/ui/dialogs/cross_db_migration.py +126 -99
- vector_inspector/ui/dialogs/settings_dialog.py +13 -6
- vector_inspector/ui/loading_screen.py +169 -0
- vector_inspector/ui/main_window.py +44 -19
- vector_inspector/ui/services/dialog_service.py +1 -0
- vector_inspector/ui/views/collection_browser.py +36 -34
- vector_inspector/ui/views/connection_view.py +7 -1
- vector_inspector/ui/views/info_panel.py +118 -52
- vector_inspector/ui/views/metadata_view.py +30 -31
- vector_inspector/ui/views/search_view.py +20 -19
- vector_inspector/ui/views/visualization_view.py +18 -15
- {vector_inspector-0.3.9.dist-info → vector_inspector-0.3.12.dist-info}/METADATA +19 -37
- {vector_inspector-0.3.9.dist-info → vector_inspector-0.3.12.dist-info}/RECORD +33 -29
- {vector_inspector-0.3.9.dist-info → vector_inspector-0.3.12.dist-info}/WHEEL +1 -1
- vector_inspector-0.3.12.dist-info/licenses/LICENSE +1 -0
- {vector_inspector-0.3.9.dist-info → vector_inspector-0.3.12.dist-info}/entry_points.txt +0 -0
|
@@ -1,27 +1,22 @@
|
|
|
1
1
|
"""Qdrant connection manager."""
|
|
2
2
|
|
|
3
|
-
from typing import Optional, List, Dict, Any
|
|
4
3
|
import uuid
|
|
4
|
+
from typing import Any, Optional
|
|
5
|
+
|
|
5
6
|
from qdrant_client import QdrantClient
|
|
6
7
|
from qdrant_client.models import (
|
|
7
8
|
Distance,
|
|
8
|
-
VectorParams,
|
|
9
|
-
PointStruct,
|
|
10
9
|
Filter,
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
MatchText,
|
|
14
|
-
MatchAny,
|
|
15
|
-
MatchExcept,
|
|
16
|
-
Range,
|
|
10
|
+
PointStruct,
|
|
11
|
+
VectorParams,
|
|
17
12
|
)
|
|
18
13
|
|
|
19
14
|
from vector_inspector.core.connections.base_connection import VectorDBConnection
|
|
20
|
-
from vector_inspector.core.logging import log_info, log_error, log_debug
|
|
21
|
-
from vector_inspector.core.connections.qdrant_helpers.qdrant_filter_builder import build_filter
|
|
22
15
|
from vector_inspector.core.connections.qdrant_helpers.qdrant_embedding_resolver import (
|
|
23
16
|
resolve_embedding_model,
|
|
24
17
|
)
|
|
18
|
+
from vector_inspector.core.connections.qdrant_helpers.qdrant_filter_builder import build_filter
|
|
19
|
+
from vector_inspector.core.logging import log_error, log_info
|
|
25
20
|
|
|
26
21
|
|
|
27
22
|
class QdrantConnection(VectorDBConnection):
|
|
@@ -133,7 +128,7 @@ class QdrantConnection(VectorDBConnection):
|
|
|
133
128
|
except Exception:
|
|
134
129
|
return 0
|
|
135
130
|
|
|
136
|
-
def get_items(self, name: str, ids:
|
|
131
|
+
def get_items(self, name: str, ids: list[str]) -> dict[str, Any]:
|
|
137
132
|
"""
|
|
138
133
|
Get items by IDs (implementation for compatibility).
|
|
139
134
|
|
|
@@ -162,7 +157,7 @@ class QdrantConnection(VectorDBConnection):
|
|
|
162
157
|
log_error("Failed to get items: %s", e)
|
|
163
158
|
return {"documents": [], "metadatas": []}
|
|
164
159
|
|
|
165
|
-
def list_collections(self) ->
|
|
160
|
+
def list_collections(self) -> list[str]:
|
|
166
161
|
"""
|
|
167
162
|
Get list of all collections.
|
|
168
163
|
|
|
@@ -178,7 +173,7 @@ class QdrantConnection(VectorDBConnection):
|
|
|
178
173
|
log_error("Failed to list collections: %s", e)
|
|
179
174
|
return []
|
|
180
175
|
|
|
181
|
-
def get_collection_info(self, name: str) -> Optional[
|
|
176
|
+
def get_collection_info(self, name: str) -> Optional[dict[str, Any]]:
|
|
182
177
|
"""
|
|
183
178
|
Get collection metadata and statistics.
|
|
184
179
|
|
|
@@ -296,7 +291,7 @@ class QdrantConnection(VectorDBConnection):
|
|
|
296
291
|
model = load_embedding_model(model_name, model_type)
|
|
297
292
|
return (model, model_name, model_type)
|
|
298
293
|
|
|
299
|
-
def _build_qdrant_filter(self, where: Optional[
|
|
294
|
+
def _build_qdrant_filter(self, where: Optional[dict[str, Any]] = None) -> Optional[Filter]:
|
|
300
295
|
"""Delegate filter construction to helper module."""
|
|
301
296
|
try:
|
|
302
297
|
return build_filter(where)
|
|
@@ -307,12 +302,12 @@ class QdrantConnection(VectorDBConnection):
|
|
|
307
302
|
def query_collection(
|
|
308
303
|
self,
|
|
309
304
|
collection_name: str,
|
|
310
|
-
query_texts: Optional[
|
|
311
|
-
query_embeddings: Optional[
|
|
305
|
+
query_texts: Optional[list[str]] = None,
|
|
306
|
+
query_embeddings: Optional[list[list[float]]] = None,
|
|
312
307
|
n_results: int = 10,
|
|
313
|
-
where: Optional[
|
|
314
|
-
where_document: Optional[
|
|
315
|
-
) -> Optional[
|
|
308
|
+
where: Optional[dict[str, Any]] = None,
|
|
309
|
+
where_document: Optional[dict[str, Any]] = None,
|
|
310
|
+
) -> Optional[dict[str, Any]]:
|
|
316
311
|
"""
|
|
317
312
|
Query a collection for similar vectors.
|
|
318
313
|
|
|
@@ -357,9 +352,9 @@ class QdrantConnection(VectorDBConnection):
|
|
|
357
352
|
for query in queries:
|
|
358
353
|
# Embed text queries if needed
|
|
359
354
|
if isinstance(query, str):
|
|
360
|
-
# Generate embeddings for text query using
|
|
355
|
+
# Generate embeddings for text query using inherited method
|
|
361
356
|
try:
|
|
362
|
-
model, model_name, model_type = self.
|
|
357
|
+
model, model_name, model_type = self.load_embedding_model_for_collection(
|
|
363
358
|
collection_name
|
|
364
359
|
)
|
|
365
360
|
|
|
@@ -425,8 +420,8 @@ class QdrantConnection(VectorDBConnection):
|
|
|
425
420
|
collection_name: str,
|
|
426
421
|
limit: Optional[int] = None,
|
|
427
422
|
offset: Optional[int] = None,
|
|
428
|
-
where: Optional[
|
|
429
|
-
) -> Optional[
|
|
423
|
+
where: Optional[dict[str, Any]] = None,
|
|
424
|
+
) -> Optional[dict[str, Any]]:
|
|
430
425
|
"""
|
|
431
426
|
Get all items from a collection.
|
|
432
427
|
|
|
@@ -492,10 +487,10 @@ class QdrantConnection(VectorDBConnection):
|
|
|
492
487
|
def add_items(
|
|
493
488
|
self,
|
|
494
489
|
collection_name: str,
|
|
495
|
-
documents:
|
|
496
|
-
metadatas: Optional[
|
|
497
|
-
ids: Optional[
|
|
498
|
-
embeddings: Optional[
|
|
490
|
+
documents: list[str],
|
|
491
|
+
metadatas: Optional[list[dict[str, Any]]] = None,
|
|
492
|
+
ids: Optional[list[str]] = None,
|
|
493
|
+
embeddings: Optional[list[list[float]]] = None,
|
|
499
494
|
) -> bool:
|
|
500
495
|
"""
|
|
501
496
|
Add items to a collection.
|
|
@@ -532,9 +527,7 @@ class QdrantConnection(VectorDBConnection):
|
|
|
532
527
|
embeddings = self.compute_embeddings_for_documents(
|
|
533
528
|
collection_name,
|
|
534
529
|
documents,
|
|
535
|
-
getattr(self, "
|
|
536
|
-
or getattr(self, "url", None)
|
|
537
|
-
or getattr(self, "host", None),
|
|
530
|
+
getattr(self, "profile_name", None),
|
|
538
531
|
)
|
|
539
532
|
except Exception as e:
|
|
540
533
|
log_error("Embeddings are required for Qdrant and computing them failed: %s", e)
|
|
@@ -569,10 +562,10 @@ class QdrantConnection(VectorDBConnection):
|
|
|
569
562
|
def update_items(
|
|
570
563
|
self,
|
|
571
564
|
collection_name: str,
|
|
572
|
-
ids:
|
|
573
|
-
documents: Optional[
|
|
574
|
-
metadatas: Optional[
|
|
575
|
-
embeddings: Optional[
|
|
565
|
+
ids: list[str],
|
|
566
|
+
documents: Optional[list[str]] = None,
|
|
567
|
+
metadatas: Optional[list[dict[str, Any]]] = None,
|
|
568
|
+
embeddings: Optional[list[list[float]]] = None,
|
|
576
569
|
) -> bool:
|
|
577
570
|
"""
|
|
578
571
|
Update items in a collection.
|
|
@@ -627,9 +620,7 @@ class QdrantConnection(VectorDBConnection):
|
|
|
627
620
|
computed = self.compute_embeddings_for_documents(
|
|
628
621
|
collection_name,
|
|
629
622
|
[documents[i]],
|
|
630
|
-
getattr(self, "
|
|
631
|
-
or getattr(self, "url", None)
|
|
632
|
-
or getattr(self, "host", None),
|
|
623
|
+
getattr(self, "profile_name", None),
|
|
633
624
|
)
|
|
634
625
|
vector = computed[0] if computed else vector
|
|
635
626
|
except Exception as e:
|
|
@@ -651,8 +642,8 @@ class QdrantConnection(VectorDBConnection):
|
|
|
651
642
|
def delete_items(
|
|
652
643
|
self,
|
|
653
644
|
collection_name: str,
|
|
654
|
-
ids: Optional[
|
|
655
|
-
where: Optional[
|
|
645
|
+
ids: Optional[list[str]] = None,
|
|
646
|
+
where: Optional[dict[str, Any]] = None,
|
|
656
647
|
) -> bool:
|
|
657
648
|
"""
|
|
658
649
|
Delete items from a collection.
|
|
@@ -739,7 +730,7 @@ class QdrantConnection(VectorDBConnection):
|
|
|
739
730
|
log_error(f"Failed to create collection: {e}")
|
|
740
731
|
return False
|
|
741
732
|
|
|
742
|
-
def prepare_restore(self, metadata:
|
|
733
|
+
def prepare_restore(self, metadata: dict[str, Any], data: dict[str, Any]) -> bool:
|
|
743
734
|
"""Provider-specific hook invoked before restoring data.
|
|
744
735
|
|
|
745
736
|
The connection can use metadata and data to pre-create collections,
|
|
@@ -832,9 +823,9 @@ class QdrantConnection(VectorDBConnection):
|
|
|
832
823
|
log_error("prepare_restore failed: %s", e)
|
|
833
824
|
return False
|
|
834
825
|
|
|
835
|
-
def get_connection_info(self) ->
|
|
826
|
+
def get_connection_info(self) -> dict[str, Any]:
|
|
836
827
|
"""Get information about the current connection."""
|
|
837
|
-
info:
|
|
828
|
+
info: dict[str, Any] = {
|
|
838
829
|
"provider": "Qdrant",
|
|
839
830
|
"connected": self.is_connected,
|
|
840
831
|
}
|
|
@@ -852,7 +843,7 @@ class QdrantConnection(VectorDBConnection):
|
|
|
852
843
|
info["mode"] = "memory"
|
|
853
844
|
return info
|
|
854
845
|
|
|
855
|
-
def get_supported_filter_operators(self) ->
|
|
846
|
+
def get_supported_filter_operators(self) -> list[dict[str, Any]]:
|
|
856
847
|
"""Get filter operators supported by Qdrant."""
|
|
857
848
|
return [
|
|
858
849
|
{"name": "=", "server_side": True},
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"""Utilities for managing embedding models and vector dimensions."""
|
|
2
2
|
|
|
3
|
-
from
|
|
4
|
-
from
|
|
3
|
+
from __future__ import annotations # Allows us to use class names in typehints while lazyloading
|
|
4
|
+
from typing import Optional, Tuple, Any
|
|
5
|
+
|
|
6
|
+
# Lazy import: see below
|
|
5
7
|
from vector_inspector.core.logging import log_info
|
|
6
8
|
|
|
7
9
|
from vector_inspector.core.model_registry import get_model_registry
|
|
@@ -101,7 +103,7 @@ def get_available_models_for_dimension(dimension: int) -> list:
|
|
|
101
103
|
return models
|
|
102
104
|
|
|
103
105
|
|
|
104
|
-
def load_embedding_model(model_name: str, model_type: str) ->
|
|
106
|
+
def load_embedding_model(model_name: str, model_type: str) -> SentenceTransformer | Any:
|
|
105
107
|
"""
|
|
106
108
|
Load an embedding model (sentence-transformer or CLIP).
|
|
107
109
|
|
|
@@ -117,12 +119,16 @@ def load_embedding_model(model_name: str, model_type: str) -> Union[SentenceTran
|
|
|
117
119
|
|
|
118
120
|
model = CLIPModel.from_pretrained(model_name)
|
|
119
121
|
processor = CLIPProcessor.from_pretrained(model_name)
|
|
122
|
+
# Returns a tuple: (CLIPModel, CLIPProcessor)
|
|
120
123
|
return (model, processor)
|
|
121
124
|
else:
|
|
125
|
+
from sentence_transformers import SentenceTransformer
|
|
126
|
+
|
|
127
|
+
# Returns a SentenceTransformer instance
|
|
122
128
|
return SentenceTransformer(model_name)
|
|
123
129
|
|
|
124
130
|
|
|
125
|
-
def encode_text(text: str, model:
|
|
131
|
+
def encode_text(text: str, model: "SentenceTransformer" | Tuple, model_type: str) -> list:
|
|
126
132
|
"""
|
|
127
133
|
Encode text using the appropriate model.
|
|
128
134
|
|
|
@@ -146,13 +152,15 @@ def encode_text(text: str, model: Union[SentenceTransformer, Tuple], model_type:
|
|
|
146
152
|
return text_features[0].cpu().numpy().tolist()
|
|
147
153
|
else:
|
|
148
154
|
# sentence-transformer
|
|
155
|
+
# Lazy import for type hint only
|
|
156
|
+
# from sentence_transformers import SentenceTransformer
|
|
149
157
|
embedding = model.encode(text)
|
|
150
158
|
return embedding.tolist()
|
|
151
159
|
|
|
152
160
|
|
|
153
161
|
def get_embedding_model_for_dimension(
|
|
154
162
|
dimension: int,
|
|
155
|
-
) -> Tuple[
|
|
163
|
+
) -> Tuple["SentenceTransformer" | Tuple, str, str]:
|
|
156
164
|
"""
|
|
157
165
|
Get a loaded embedding model for a specific dimension.
|
|
158
166
|
|
|
@@ -164,4 +172,5 @@ def get_embedding_model_for_dimension(
|
|
|
164
172
|
"""
|
|
165
173
|
model_name, model_type = get_model_for_dimension(dimension)
|
|
166
174
|
model = load_embedding_model(model_name, model_type)
|
|
175
|
+
# Returns a tuple: (loaded_model, model_name, model_type)
|
|
167
176
|
return (model, model_name, model_type)
|
vector_inspector/core/logging.py
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
Provides `log_info`, `log_error`, and `log_debug` helpers that delegate
|
|
4
4
|
to the standard `logging` module but keep call sites concise.
|
|
5
5
|
"""
|
|
6
|
+
|
|
6
7
|
import logging
|
|
7
8
|
from typing import Any
|
|
8
9
|
|
|
@@ -12,7 +13,8 @@ if not _logger.handlers:
|
|
|
12
13
|
formatter = logging.Formatter("%(asctime)s %(levelname)s %(name)s: %(message)s")
|
|
13
14
|
handler.setFormatter(formatter)
|
|
14
15
|
_logger.addHandler(handler)
|
|
15
|
-
|
|
16
|
+
# Default to WARNING to reduce console noise; set to DEBUG for troubleshooting
|
|
17
|
+
_logger.setLevel(logging.WARNING)
|
|
16
18
|
|
|
17
19
|
|
|
18
20
|
def log_info(msg: str, *args: Any, **kwargs: Any) -> None:
|
|
@@ -103,3 +103,9 @@ class SettingsPanelHook:
|
|
|
103
103
|
|
|
104
104
|
# Global singleton instance
|
|
105
105
|
settings_panel_hook = SettingsPanelHook()
|
|
106
|
+
|
|
107
|
+
# Register built-in settings panel extensions
|
|
108
|
+
try:
|
|
109
|
+
import vector_inspector.extensions.telemetry_settings_panel
|
|
110
|
+
except Exception:
|
|
111
|
+
pass
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from PySide6.QtWidgets import QCheckBox, QHBoxLayout
|
|
2
|
+
from vector_inspector.extensions import settings_panel_hook
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def add_telemetry_section(parent_layout, settings_service, dialog=None):
|
|
6
|
+
telemetry_checkbox = QCheckBox("Enable anonymous telemetry (app launch events)")
|
|
7
|
+
# Default to checked if not set
|
|
8
|
+
checked = settings_service.get("telemetry.enabled")
|
|
9
|
+
if checked is None:
|
|
10
|
+
checked = True
|
|
11
|
+
telemetry_checkbox.setChecked(checked)
|
|
12
|
+
telemetry_checkbox.setToolTip(
|
|
13
|
+
"Allow the app to send anonymous launch events to help improve reliability. No personal or document data is sent."
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
def on_state_changed(state):
|
|
17
|
+
settings_service.set_telemetry_enabled(bool(state))
|
|
18
|
+
|
|
19
|
+
telemetry_checkbox.stateChanged.connect(on_state_changed)
|
|
20
|
+
layout = QHBoxLayout()
|
|
21
|
+
layout.addWidget(telemetry_checkbox)
|
|
22
|
+
parent_layout.addLayout(layout)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
settings_panel_hook.register(add_telemetry_section)
|
vector_inspector/main.py
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
"""Main entry point for Vector Inspector application."""
|
|
2
2
|
|
|
3
|
-
import sys
|
|
4
3
|
import os
|
|
4
|
+
import sys
|
|
5
|
+
|
|
6
|
+
from PySide6.QtCore import QTimer
|
|
5
7
|
from PySide6.QtWidgets import QApplication
|
|
6
|
-
|
|
8
|
+
|
|
9
|
+
from vector_inspector import get_version
|
|
10
|
+
from vector_inspector.ui.loading_screen import show_loading_screen
|
|
7
11
|
|
|
8
12
|
# Ensures the app looks in its own folder for the raw libraries
|
|
9
13
|
sys.path.append(os.path.dirname(sys.executable))
|
|
@@ -15,9 +19,48 @@ def main():
|
|
|
15
19
|
app.setApplicationName("Vector Inspector")
|
|
16
20
|
app.setOrganizationName("Vector Inspector")
|
|
17
21
|
|
|
22
|
+
# Get version once for all uses
|
|
23
|
+
app_version = get_version()
|
|
24
|
+
|
|
25
|
+
# Show loading screen (if not disabled in settings)
|
|
26
|
+
loading = show_loading_screen(
|
|
27
|
+
app_name="Vector Inspector",
|
|
28
|
+
version=f"v{app_version}",
|
|
29
|
+
tagline="The missing toolset for your vector data",
|
|
30
|
+
loading_text="Initializing providers…"
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
# Heavy imports after loading screen is visible
|
|
34
|
+
if loading:
|
|
35
|
+
loading.set_loading_text("Loading main window...")
|
|
36
|
+
app.processEvents()
|
|
37
|
+
|
|
38
|
+
from vector_inspector.core.logging import log_error
|
|
39
|
+
from vector_inspector.ui.main_window import MainWindow
|
|
40
|
+
|
|
41
|
+
def send_ping():
|
|
42
|
+
# Telemetry: send launch ping if enabled
|
|
43
|
+
try:
|
|
44
|
+
from vector_inspector.services.telemetry_service import TelemetryService
|
|
45
|
+
|
|
46
|
+
telemetry = TelemetryService()
|
|
47
|
+
telemetry.send_launch_ping(app_version=app_version)
|
|
48
|
+
except Exception as e:
|
|
49
|
+
log_error(f"[Telemetry] Failed to send launch ping: {e}")
|
|
50
|
+
|
|
51
|
+
if loading:
|
|
52
|
+
loading.set_loading_text("Preparing interface...")
|
|
53
|
+
app.processEvents()
|
|
54
|
+
|
|
18
55
|
window = MainWindow()
|
|
19
56
|
window.show()
|
|
20
57
|
|
|
58
|
+
# Always fade out loading screen automatically
|
|
59
|
+
if loading:
|
|
60
|
+
loading.fade_out()
|
|
61
|
+
|
|
62
|
+
QTimer.singleShot(0, lambda: send_ping())
|
|
63
|
+
|
|
21
64
|
sys.exit(app.exec())
|
|
22
65
|
|
|
23
66
|
|