vector-inspector 0.3.8__py3-none-any.whl → 0.3.11__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.
@@ -1,3 +1,12 @@
1
1
  """Vector Inspector - A comprehensive desktop application for vector database visualization."""
2
2
 
3
- __version__ = "0.1.0"
3
+ __version__ = "0.3.11" # Keep in sync with pyproject.toml for dev mode fallback
4
+
5
+
6
+ def get_version():
7
+ try:
8
+ from importlib.metadata import version, PackageNotFoundError
9
+
10
+ return version("vector-inspector")
11
+ except Exception:
12
+ return __version__
@@ -5,6 +5,8 @@ import json
5
5
  import psycopg2
6
6
  from psycopg2 import sql
7
7
 
8
+ from vector_inspector.core.logging import log_info
9
+
8
10
  ## No need to import register_vector; pgvector extension is enabled at table creation
9
11
  from vector_inspector.core.connections.base_connection import VectorDBConnection
10
12
  from vector_inspector.core.logging import log_error, log_info
@@ -1043,12 +1045,35 @@ class PgVectorConnection(VectorDBConnection):
1043
1045
  Returns:
1044
1046
  List of floats
1045
1047
  """
1048
+ # log_info("[pgvector] _parse_vector raw value: %r (type: %s)", vector_str, type(vector_str))
1046
1049
  if isinstance(vector_str, list):
1050
+ log_info("[pgvector] _parse_vector: already list, len=%d", len(vector_str))
1047
1051
  return vector_str
1052
+ # Handle numpy arrays
1053
+ try:
1054
+ import numpy as np
1055
+
1056
+ if isinstance(vector_str, np.ndarray):
1057
+ log_info("[pgvector] _parse_vector: numpy array, shape=%s", vector_str.shape)
1058
+ return vector_str.tolist()
1059
+ except ImportError:
1060
+ pass
1048
1061
  if isinstance(vector_str, str):
1049
1062
  # Remove brackets and split by comma
1050
1063
  vector_str = vector_str.strip("[]")
1051
- return [float(x) for x in vector_str.split(",")]
1064
+ if not vector_str:
1065
+ log_info("[pgvector] _parse_vector: empty string after strip")
1066
+ return []
1067
+ try:
1068
+ parsed = [float(x) for x in vector_str.split(",")]
1069
+ log_info("[pgvector] _parse_vector: parsed list of len %d", len(parsed))
1070
+ return parsed
1071
+ except Exception as e:
1072
+ log_info(
1073
+ "[pgvector] _parse_vector: failed to parse '%s' with error: %s", vector_str, e
1074
+ )
1075
+ return []
1076
+ log_info("[pgvector] _parse_vector: unhandled type %s, returning []", type(vector_str))
1052
1077
  return []
1053
1078
 
1054
1079
  def compute_embeddings_for_documents(
@@ -0,0 +1,111 @@
1
+ """Extension points for Vector Inspector.
2
+
3
+ This module provides hooks and callbacks that allow pro versions
4
+ or plugins to extend core functionality without modifying the base code.
5
+ """
6
+
7
+ from typing import Any, ClassVar
8
+ from collections.abc import Callable
9
+ from PySide6.QtWidgets import QMenu, QTableWidget
10
+
11
+
12
+ class TableContextMenuHook:
13
+ """Hook for adding custom context menu items to table widgets."""
14
+
15
+ _handlers: ClassVar[list[Callable]] = []
16
+
17
+ @classmethod
18
+ def register(cls, handler: Callable):
19
+ """Register a context menu handler.
20
+
21
+ Args:
22
+ handler: Callable that takes (menu: QMenu, table: QTableWidget, row: int, data: Dict)
23
+ and adds menu items to the menu.
24
+ """
25
+ if handler not in cls._handlers:
26
+ cls._handlers.append(handler)
27
+
28
+ @classmethod
29
+ def unregister(cls, handler: Callable):
30
+ """Unregister a context menu handler."""
31
+ if handler in cls._handlers:
32
+ cls._handlers.remove(handler)
33
+
34
+ @classmethod
35
+ def trigger(
36
+ cls,
37
+ menu: QMenu,
38
+ table: QTableWidget,
39
+ row: int,
40
+ data: dict[str, Any] | None = None,
41
+ ):
42
+ """Trigger all registered handlers.
43
+
44
+ Args:
45
+ menu: The QMenu to add items to
46
+ table: The QTableWidget that was right-clicked
47
+ row: The row number that was clicked
48
+ data: Optional data dictionary with context (ids, documents, metadatas, etc.)
49
+ """
50
+ for handler in cls._handlers:
51
+ try:
52
+ handler(menu, table, row, data)
53
+ except Exception as e:
54
+ # Log but don't break if a handler fails
55
+ from vector_inspector.core.logging import log_error
56
+
57
+ log_error("Context menu handler error: %s", e)
58
+
59
+ @classmethod
60
+ def clear(cls):
61
+ """Clear all registered handlers."""
62
+ cls._handlers.clear()
63
+
64
+
65
+ # Global singleton instance
66
+ table_context_menu_hook = TableContextMenuHook()
67
+
68
+
69
+ class SettingsPanelHook:
70
+ """Hook for adding custom sections to the Settings/Preferences dialog."""
71
+
72
+ _handlers: ClassVar[list[Callable]] = []
73
+
74
+ @classmethod
75
+ def register(cls, handler: Callable):
76
+ """Register a settings panel provider.
77
+
78
+ Handler signature: (parent_layout, settings_service, dialog)
79
+ where `parent_layout` is a QLayout the handler can add widgets to.
80
+ """
81
+ if handler not in cls._handlers:
82
+ cls._handlers.append(handler)
83
+
84
+ @classmethod
85
+ def unregister(cls, handler: Callable):
86
+ if handler in cls._handlers:
87
+ cls._handlers.remove(handler)
88
+
89
+ @classmethod
90
+ def trigger(cls, parent_layout, settings_service, dialog=None):
91
+ for handler in cls._handlers:
92
+ try:
93
+ handler(parent_layout, settings_service, dialog)
94
+ except Exception as e:
95
+ from vector_inspector.core.logging import log_error
96
+
97
+ log_error("Settings panel handler error: %s", e)
98
+
99
+ @classmethod
100
+ def clear(cls):
101
+ cls._handlers.clear()
102
+
103
+
104
+ # Global singleton instance
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
@@ -4,6 +4,7 @@ import sys
4
4
  import os
5
5
  from PySide6.QtWidgets import QApplication
6
6
  from vector_inspector.ui.main_window import MainWindow
7
+ from vector_inspector.core.logging import log_error
7
8
 
8
9
  # Ensures the app looks in its own folder for the raw libraries
9
10
  sys.path.append(os.path.dirname(sys.executable))
@@ -15,6 +16,21 @@ def main():
15
16
  app.setApplicationName("Vector Inspector")
16
17
  app.setOrganizationName("Vector Inspector")
17
18
 
19
+ # Telemetry: send launch ping if enabled
20
+ try:
21
+ from vector_inspector.services.telemetry_service import TelemetryService
22
+ from vector_inspector import get_version, __version__
23
+
24
+ app_version = None
25
+ try:
26
+ app_version = get_version()
27
+ except Exception:
28
+ app_version = __version__
29
+ telemetry = TelemetryService()
30
+ telemetry.send_launch_ping(app_version=app_version)
31
+ except Exception as e:
32
+ log_error(f"[Telemetry] Failed to send launch ping: {e}")
33
+
18
34
  window = MainWindow()
19
35
  window.show()
20
36
 
@@ -1,6 +1,8 @@
1
1
  """Service for persisting application settings."""
2
2
 
3
3
  import json
4
+ import base64
5
+ from PySide6.QtCore import QObject, Signal
4
6
  from pathlib import Path
5
7
  from typing import Dict, Any, Optional, List
6
8
  from vector_inspector.core.cache_manager import invalidate_cache_on_settings_change
@@ -12,6 +14,18 @@ class SettingsService:
12
14
 
13
15
  def __init__(self):
14
16
  """Initialize settings service."""
17
+
18
+ # Expose a shared QObject-based signal emitter so UI can react to
19
+ # settings changes without polling.
20
+ class _Signals(QObject):
21
+ setting_changed = Signal(str, object)
22
+
23
+ # singleton-like per-process signals instance
24
+ try:
25
+ self.signals
26
+ except Exception:
27
+ self.signals = _Signals()
28
+
15
29
  self.settings_dir = Path.home() / ".vector-inspector"
16
30
  self.settings_file = self.settings_dir / "settings.json"
17
31
  self.settings: Dict[str, Any] = {}
@@ -51,6 +65,62 @@ class SettingsService:
51
65
  """Get a setting value."""
52
66
  return self.settings.get(key, default)
53
67
 
68
+ # Convenience accessors for common settings
69
+ def get_breadcrumb_enabled(self) -> bool:
70
+ return bool(self.settings.get("breadcrumb.enabled", True))
71
+
72
+ def set_breadcrumb_enabled(self, enabled: bool):
73
+ self.set("breadcrumb.enabled", bool(enabled))
74
+
75
+ def get_breadcrumb_elide_mode(self) -> str:
76
+ return str(self.settings.get("breadcrumb.elide_mode", "left"))
77
+
78
+ def set_breadcrumb_elide_mode(self, mode: str):
79
+ if mode not in ("left", "middle"):
80
+ mode = "left"
81
+ self.set("breadcrumb.elide_mode", mode)
82
+
83
+ def get_default_n_results(self) -> int:
84
+ return int(self.settings.get("search.default_n_results", 10))
85
+
86
+ def set_default_n_results(self, n: int):
87
+ self.set("search.default_n_results", int(n))
88
+
89
+ def get_auto_generate_embeddings(self) -> bool:
90
+ return bool(self.settings.get("embeddings.auto_generate", True))
91
+
92
+ def set_auto_generate_embeddings(self, enabled: bool):
93
+ self.set("embeddings.auto_generate", bool(enabled))
94
+
95
+ def get_window_restore_geometry(self) -> bool:
96
+ return bool(self.settings.get("window.restore_geometry", True))
97
+
98
+ def set_window_restore_geometry(self, enabled: bool):
99
+ self.set("window.restore_geometry", bool(enabled))
100
+
101
+ def set_window_geometry(self, geometry_bytes: bytes):
102
+ """Save window geometry as base64 string."""
103
+ try:
104
+ if isinstance(geometry_bytes, str):
105
+ # assume base64 already
106
+ b64 = geometry_bytes
107
+ else:
108
+ b64 = base64.b64encode(bytes(geometry_bytes)).decode("ascii")
109
+ self.set("window.geometry", b64)
110
+ except Exception as e:
111
+ log_error("Failed to set window geometry: %s", e)
112
+
113
+ def get_window_geometry(self) -> Optional[bytes]:
114
+ """Return geometry bytes or None."""
115
+ try:
116
+ b64 = self.settings.get("window.geometry")
117
+ if not b64:
118
+ return None
119
+ return base64.b64decode(b64)
120
+ except Exception as e:
121
+ log_error("Failed to get window geometry: %s", e)
122
+ return None
123
+
54
124
  def get_cache_enabled(self) -> bool:
55
125
  """Get whether caching is enabled (default: True)."""
56
126
  return self.settings.get("cache_enabled", True)
@@ -67,6 +137,14 @@ class SettingsService:
67
137
  else:
68
138
  cache.disable()
69
139
 
140
+ def get_telemetry_enabled(self) -> bool:
141
+ """Get whether telemetry is enabled (default: True)."""
142
+ return bool(self.settings.get("telemetry.enabled", True))
143
+
144
+ def set_telemetry_enabled(self, enabled: bool):
145
+ """Set whether telemetry is enabled."""
146
+ self.set("telemetry.enabled", bool(enabled))
147
+
70
148
  def set(self, key: str, value: Any):
71
149
  """Set a setting value."""
72
150
  self.settings[key] = value
@@ -74,6 +152,12 @@ class SettingsService:
74
152
  # Invalidate cache when settings change (only if cache is enabled)
75
153
  if key != "cache_enabled": # Don't invalidate when toggling cache itself
76
154
  invalidate_cache_on_settings_change()
155
+ # Emit change signal for UI/reactive components
156
+ try:
157
+ # Emit the raw python object (value) for convenience
158
+ self.signals.setting_changed.emit(key, value)
159
+ except Exception:
160
+ pass
77
161
 
78
162
  def clear(self):
79
163
  """Clear all settings."""
@@ -0,0 +1,88 @@
1
+ import json
2
+ import platform
3
+ import uuid
4
+ import requests
5
+ from pathlib import Path
6
+ from vector_inspector.services.settings_service import SettingsService
7
+ from vector_inspector.core.logging import log_info, log_error
8
+
9
+ TELEMETRY_ENDPOINT = "https://api.divinedevops.com/api/v1/telemetry"
10
+
11
+
12
+ class TelemetryService:
13
+ def __init__(self, settings_service=None):
14
+ self.settings = settings_service or SettingsService()
15
+ self.queue_file = Path.home() / ".vector-inspector" / "telemetry_queue.json"
16
+ self._load_queue()
17
+
18
+ def _load_queue(self):
19
+ if self.queue_file.exists():
20
+ try:
21
+ with open(self.queue_file, encoding="utf-8") as f:
22
+ self.queue = json.load(f)
23
+ except Exception:
24
+ self.queue = []
25
+ else:
26
+ self.queue = []
27
+
28
+ def _save_queue(self):
29
+ self.queue_file.parent.mkdir(parents=True, exist_ok=True)
30
+ with open(self.queue_file, "w", encoding="utf-8") as f:
31
+ json.dump(self.queue, f, indent=2)
32
+
33
+ def is_enabled(self):
34
+ return bool(self.settings.get("telemetry.enabled", True))
35
+
36
+ def get_hwid(self):
37
+ # Use a persistent UUID for this client
38
+ hwid = self.settings.get("telemetry.hwid")
39
+ if not hwid:
40
+ hwid = str(uuid.uuid4())
41
+ self.settings.set("telemetry.hwid", hwid)
42
+ return hwid
43
+
44
+ def queue_event(self, event):
45
+ self.queue.append(event)
46
+ self._save_queue()
47
+
48
+ def send_batch(self):
49
+ if not self.is_enabled() or not self.queue:
50
+ return
51
+ sent = []
52
+ for event in self.queue:
53
+ try:
54
+ log_info(
55
+ f"[Telemetry] Sending to {TELEMETRY_ENDPOINT}\nPayload: {json.dumps(event, indent=2)}"
56
+ )
57
+ resp = requests.post(TELEMETRY_ENDPOINT, json=event, timeout=5)
58
+ log_info(f"[Telemetry] Response: {resp.status_code} {resp.text}")
59
+ if resp.status_code in (200, 201):
60
+ sent.append(event)
61
+ except Exception as e:
62
+ log_error(f"[Telemetry] Exception: {e}")
63
+ # Remove sent events
64
+ self.queue = [e for e in self.queue if e not in sent]
65
+ self._save_queue()
66
+
67
+ def send_launch_ping(self, app_version, client_type="vector-inspector"):
68
+ log_info("[Telemetry] send_launch_ping called")
69
+ if not self.is_enabled():
70
+ log_info("[Telemetry] Telemetry is not enabled; skipping launch ping.")
71
+ return
72
+ event = {
73
+ "hwid": self.get_hwid(),
74
+ "event_name": "app_launch",
75
+ "app_version": app_version,
76
+ "client_type": client_type,
77
+ "metadata": {"os": platform.system() + "-" + platform.release()},
78
+ }
79
+ log_info(f"[Telemetry] Launch event payload: {json.dumps(event, indent=2)}")
80
+ self.queue_event(event)
81
+ self.send_batch()
82
+
83
+ def purge(self):
84
+ self.queue = []
85
+ self._save_queue()
86
+
87
+ def get_queue(self):
88
+ return list(self.queue)
@@ -0,0 +1,124 @@
1
+ from PySide6.QtWidgets import (
2
+ QDialog,
3
+ QVBoxLayout,
4
+ QHBoxLayout,
5
+ QLabel,
6
+ QCheckBox,
7
+ QComboBox,
8
+ QSpinBox,
9
+ QPushButton,
10
+ )
11
+ from PySide6.QtCore import Qt
12
+
13
+ from vector_inspector.services.settings_service import SettingsService
14
+ from vector_inspector.extensions import settings_panel_hook
15
+
16
+
17
+ class SettingsDialog(QDialog):
18
+ """Modal settings dialog backed by SettingsService."""
19
+
20
+ def __init__(self, settings_service: SettingsService = None, parent=None):
21
+ super().__init__(parent)
22
+ self.setWindowTitle("Preferences")
23
+ self.settings = settings_service or SettingsService()
24
+ self._init_ui()
25
+ self._load_values()
26
+
27
+ def _init_ui(self):
28
+ layout = QVBoxLayout(self)
29
+
30
+ # Breadcrumb controls are provided by pro extensions (vector-studio)
31
+ # via the settings_panel_hook. Core does not add breadcrumb options.
32
+
33
+ # Search defaults
34
+ search_layout = QHBoxLayout()
35
+ search_layout.addWidget(QLabel("Default results:"))
36
+ self.default_results = QSpinBox()
37
+ self.default_results.setMinimum(1)
38
+ self.default_results.setMaximum(1000)
39
+ search_layout.addWidget(self.default_results)
40
+ layout.addLayout(search_layout)
41
+
42
+ # Embeddings
43
+ self.auto_embed_checkbox = QCheckBox("Auto-generate embeddings for new text")
44
+ layout.addWidget(self.auto_embed_checkbox)
45
+
46
+ # Window geometry
47
+ self.restore_geometry_checkbox = QCheckBox("Restore window size/position on startup")
48
+ layout.addWidget(self.restore_geometry_checkbox)
49
+
50
+ # Buttons
51
+ btn_layout = QHBoxLayout()
52
+ self.apply_btn = QPushButton("Apply")
53
+ self.ok_btn = QPushButton("OK")
54
+ self.cancel_btn = QPushButton("Cancel")
55
+ self.reset_btn = QPushButton("Reset to defaults")
56
+ btn_layout.addWidget(self.reset_btn)
57
+ btn_layout.addStretch()
58
+ btn_layout.addWidget(self.apply_btn)
59
+ btn_layout.addWidget(self.ok_btn)
60
+ btn_layout.addWidget(self.cancel_btn)
61
+ # Allow external extensions to add sections before the buttons
62
+ try:
63
+ # Handlers receive (parent_layout, settings_service, dialog)
64
+ settings_panel_hook.trigger(layout, self.settings, self)
65
+ except Exception:
66
+ pass
67
+
68
+ layout.addLayout(btn_layout)
69
+
70
+ # Signals
71
+ self.apply_btn.clicked.connect(self._apply)
72
+ self.ok_btn.clicked.connect(self._ok)
73
+ self.cancel_btn.clicked.connect(self.reject)
74
+ self.reset_btn.clicked.connect(self._reset_defaults)
75
+
76
+ # Immediate apply on change for some controls
77
+ self.default_results.valueChanged.connect(lambda v: self.settings.set_default_n_results(v))
78
+ self.auto_embed_checkbox.stateChanged.connect(
79
+ lambda s: self.settings.set_auto_generate_embeddings(bool(s))
80
+ )
81
+ self.restore_geometry_checkbox.stateChanged.connect(
82
+ lambda s: self.settings.set_window_restore_geometry(bool(s))
83
+ )
84
+
85
+ # Container for programmatic sections
86
+ self._extra_sections = []
87
+
88
+ def add_section(self, widget_or_layout):
89
+ """Programmatically add a section (widget or layout) to the dialog.
90
+
91
+ `widget_or_layout` can be a QWidget or QLayout. It will be added
92
+ immediately to the dialog's main layout.
93
+ """
94
+ try:
95
+ if hasattr(widget_or_layout, "setParent"):
96
+ # QWidget
97
+ self.layout().addWidget(widget_or_layout)
98
+ else:
99
+ # Assume QLayout
100
+ self.layout().addLayout(widget_or_layout)
101
+ self._extra_sections.append(widget_or_layout)
102
+ except Exception:
103
+ pass
104
+
105
+ def _load_values(self):
106
+ # Breadcrumb controls are not present in core dialog.
107
+ self.default_results.setValue(self.settings.get_default_n_results())
108
+ self.auto_embed_checkbox.setChecked(self.settings.get_auto_generate_embeddings())
109
+ self.restore_geometry_checkbox.setChecked(self.settings.get_window_restore_geometry())
110
+
111
+ def _apply(self):
112
+ # Values are already applied on change; ensure persistence and close
113
+ self.settings._save_settings()
114
+
115
+ def _ok(self):
116
+ self._apply()
117
+ self.accept()
118
+
119
+ def _reset_defaults(self):
120
+ # Reset to recommended defaults
121
+ self.default_results.setValue(10)
122
+ self.auto_embed_checkbox.setChecked(True)
123
+ self.restore_geometry_checkbox.setChecked(True)
124
+ self._apply()
@@ -8,7 +8,7 @@ from PySide6.QtWidgets import (
8
8
  QToolBar,
9
9
  QStatusBar,
10
10
  )
11
- from PySide6.QtCore import Qt, QTimer
11
+ from PySide6.QtCore import Qt, QTimer, QByteArray
12
12
  from PySide6.QtGui import QAction
13
13
 
14
14
  from vector_inspector.core.connection_manager import ConnectionManager
@@ -58,6 +58,29 @@ class MainWindow(InspectorShell):
58
58
  self._setup_statusbar()
59
59
  self._connect_signals()
60
60
  self._restore_session()
61
+ # Listen for settings changes so updates apply immediately
62
+ try:
63
+ self.settings_service.signals.setting_changed.connect(self._on_setting_changed)
64
+ except Exception:
65
+ pass
66
+ # Restore window geometry if present
67
+ try:
68
+ geom = self.settings_service.get_window_geometry()
69
+ if geom and self.settings_service.get_window_restore_geometry():
70
+ try:
71
+ # restoreGeometry accepts QByteArray; wrap bytes accordingly
72
+ if isinstance(geom, (bytes, bytearray)):
73
+ self.restoreGeometry(QByteArray(geom))
74
+ else:
75
+ self.restoreGeometry(geom)
76
+ except Exception:
77
+ # fallback: try passing raw bytes
78
+ try:
79
+ self.restoreGeometry(geom)
80
+ except Exception:
81
+ pass
82
+ except Exception:
83
+ pass
61
84
  # Show splash after main window is visible
62
85
  QTimer.singleShot(0, self._maybe_show_splash)
63
86
 
@@ -121,6 +144,13 @@ class MainWindow(InspectorShell):
121
144
 
122
145
  file_menu.addSeparator()
123
146
 
147
+ prefs_action = QAction("Preferences...", self)
148
+ prefs_action.setShortcut("Ctrl+,")
149
+ prefs_action.triggered.connect(self._show_preferences_dialog)
150
+ file_menu.addAction(prefs_action)
151
+
152
+ file_menu.addSeparator()
153
+
124
154
  exit_action = QAction("E&xit", self)
125
155
  exit_action.setShortcut("Ctrl+Q")
126
156
  exit_action.triggered.connect(self.close)
@@ -249,6 +279,62 @@ class MainWindow(InspectorShell):
249
279
 
250
280
  threading.Thread(target=check_updates, daemon=True).start()
251
281
 
282
+ def _show_preferences_dialog(self):
283
+ try:
284
+ from vector_inspector.ui.dialogs.settings_dialog import SettingsDialog
285
+
286
+ dlg = SettingsDialog(self.settings_service, self)
287
+ if dlg.exec() == QDialog.Accepted:
288
+ self._apply_settings_to_views()
289
+ except Exception as e:
290
+ print(f"Failed to open preferences: {e}")
291
+
292
+ def _apply_settings_to_views(self):
293
+ """Apply relevant settings to existing views."""
294
+ try:
295
+ # Breadcrumb visibility
296
+ enabled = self.settings_service.get_breadcrumb_enabled()
297
+ if self.search_view is not None and hasattr(self.search_view, "breadcrumb_label"):
298
+ self.search_view.breadcrumb_label.setVisible(enabled)
299
+ # also set elide mode
300
+ mode = self.settings_service.get_breadcrumb_elide_mode()
301
+ try:
302
+ self.search_view.set_elide_mode(mode)
303
+ except Exception:
304
+ pass
305
+
306
+ # Default results
307
+ default_n = self.settings_service.get_default_n_results()
308
+ if self.search_view is not None and hasattr(self.search_view, "n_results_spin"):
309
+ try:
310
+ self.search_view.n_results_spin.setValue(int(default_n))
311
+ except Exception:
312
+ pass
313
+
314
+ except Exception:
315
+ pass
316
+
317
+ def _on_setting_changed(self, key: str, value: object):
318
+ """Handle granular setting change events."""
319
+ try:
320
+ if key == "breadcrumb.enabled":
321
+ enabled = bool(value)
322
+ if self.search_view is not None and hasattr(self.search_view, "breadcrumb_label"):
323
+ self.search_view.breadcrumb_label.setVisible(enabled)
324
+ elif key == "breadcrumb.elide_mode":
325
+ mode = str(value)
326
+ if self.search_view is not None and hasattr(self.search_view, "set_elide_mode"):
327
+ self.search_view.set_elide_mode(mode)
328
+ elif key == "search.default_n_results":
329
+ try:
330
+ n = int(value)
331
+ if self.search_view is not None and hasattr(self.search_view, "n_results_spin"):
332
+ self.search_view.n_results_spin.setValue(n)
333
+ except Exception:
334
+ pass
335
+ except Exception:
336
+ pass
337
+
252
338
  def _on_update_indicator_clicked(self, event):
253
339
  # Show update details dialog
254
340
  if not hasattr(self, "_latest_release"):
@@ -467,6 +553,9 @@ class MainWindow(InspectorShell):
467
553
  10000,
468
554
  )
469
555
 
556
+ # Apply settings to views after UI is built
557
+ self._apply_settings_to_views()
558
+
470
559
  def _show_about(self):
471
560
  """Show about dialog."""
472
561
  DialogService.show_about(self)
@@ -494,6 +583,35 @@ class MainWindow(InspectorShell):
494
583
  # Refresh collections after restore
495
584
  self._refresh_active_connection()
496
585
 
586
+ def show_search_results(self, collection_name: str, results: dict, context_info: str = ""):
587
+ """Display search results in the Search tab.
588
+
589
+ This is an extension point that allows external code (e.g., pro features)
590
+ to programmatically display search results.
591
+
592
+ Args:
593
+ collection_name: Name of the collection
594
+ results: Search results dictionary
595
+ context_info: Optional context string (e.g., "Similar to: item_123")
596
+ """
597
+ # Switch to search tab
598
+ self.set_main_tab_active(InspectorTabs.SEARCH_TAB)
599
+
600
+ # Set the collection if needed
601
+ if self.search_view.current_collection != collection_name:
602
+ active = self.connection_manager.get_active_connection()
603
+ database_name = active.id if active else ""
604
+ self.search_view.set_collection(collection_name, database_name)
605
+
606
+ # Display the results
607
+ self.search_view.search_results = results
608
+ self.search_view._display_results(results)
609
+
610
+ # Update status with context if provided
611
+ if context_info:
612
+ num_results = len(results.get("ids", [[]])[0])
613
+ self.search_view.results_status.setText(f"{context_info} - Found {num_results} results")
614
+
497
615
  def closeEvent(self, event):
498
616
  """Handle application close."""
499
617
  # Clean up connection controller
@@ -508,4 +626,23 @@ class MainWindow(InspectorShell):
508
626
  # Close all connections
509
627
  self.connection_manager.close_all_connections()
510
628
 
629
+ # Save window geometry if enabled
630
+ try:
631
+ if self.settings_service.get_window_restore_geometry():
632
+ geom = self.saveGeometry()
633
+ # geom may be a QByteArray; convert to raw bytes
634
+ try:
635
+ if isinstance(geom, QByteArray):
636
+ b = bytes(geom)
637
+ else:
638
+ b = bytes(geom)
639
+ self.settings_service.set_window_geometry(b)
640
+ except Exception:
641
+ try:
642
+ self.settings_service.set_window_geometry(bytes(geom))
643
+ except Exception:
644
+ pass
645
+ except Exception:
646
+ pass
647
+
511
648
  event.accept()
@@ -196,6 +196,9 @@ class MetadataView(QWidget):
196
196
  self.table.setAlternatingRowColors(True)
197
197
  self.table.horizontalHeader().setStretchLastSection(True)
198
198
  self.table.doubleClicked.connect(self._on_row_double_clicked)
199
+ # Enable context menu
200
+ self.table.setContextMenuPolicy(Qt.CustomContextMenu)
201
+ self.table.customContextMenuRequested.connect(self._show_context_menu)
199
202
  layout.addWidget(self.table)
200
203
 
201
204
  # Status bar
@@ -962,6 +965,48 @@ class MetadataView(QWidget):
962
965
  else:
963
966
  QMessageBox.warning(self, "Export Failed", "Failed to export data.")
964
967
 
968
+ def _show_context_menu(self, position):
969
+ """Show context menu for table rows."""
970
+ # Get the item at the position
971
+ item = self.table.itemAt(position)
972
+ if not item:
973
+ return
974
+
975
+ row = item.row()
976
+ if row < 0 or row >= self.table.rowCount():
977
+ return
978
+
979
+ # Create context menu
980
+ menu = QMenu(self)
981
+
982
+ # Add standard "Edit" action
983
+ edit_action = menu.addAction("✏️ Edit")
984
+ edit_action.triggered.connect(
985
+ lambda: self._on_row_double_clicked(self.table.model().index(row, 0))
986
+ )
987
+
988
+ # Call extension hooks to add custom menu items
989
+ try:
990
+ from vector_inspector.extensions import table_context_menu_hook
991
+
992
+ table_context_menu_hook.trigger(
993
+ menu=menu,
994
+ table=self.table,
995
+ row=row,
996
+ data={
997
+ "current_data": self.current_data,
998
+ "collection_name": self.current_collection,
999
+ "database_name": self.current_database,
1000
+ "connection": self.connection,
1001
+ "view_type": "metadata",
1002
+ },
1003
+ )
1004
+ except Exception as e:
1005
+ log_info("Extension hook error: %s", e)
1006
+
1007
+ # Show menu
1008
+ menu.exec(self.table.viewport().mapToGlobal(position))
1009
+
965
1010
  def _import_data(self, format_type: str):
966
1011
  """Import data from file into collection."""
967
1012
  if not self.current_collection:
@@ -8,6 +8,7 @@ from PySide6.QtWidgets import (
8
8
  QTextEdit,
9
9
  QPushButton,
10
10
  QLabel,
11
+ QSizePolicy,
11
12
  QSpinBox,
12
13
  QTableWidget,
13
14
  QTableWidgetItem,
@@ -15,8 +16,10 @@ from PySide6.QtWidgets import (
15
16
  QSplitter,
16
17
  QCheckBox,
17
18
  QApplication,
19
+ QMenu,
18
20
  )
19
21
  from PySide6.QtCore import Qt
22
+ from PySide6.QtGui import QFontMetrics
20
23
 
21
24
  from vector_inspector.core.connections.base_connection import VectorDBConnection
22
25
  from vector_inspector.ui.components.filter_builder import FilterBuilder
@@ -31,6 +34,19 @@ class SearchView(QWidget):
31
34
 
32
35
  def __init__(self, connection: VectorDBConnection, parent=None):
33
36
  super().__init__(parent)
37
+ # Initialize all UI attributes to None to avoid AttributeError
38
+ self.breadcrumb_label = None
39
+ self.query_input = None
40
+ self.results_table = None
41
+ self.results_status = None
42
+ self.refresh_button = None
43
+ self.n_results_spin = None
44
+ self.filter_builder = None
45
+ self.filter_group = None
46
+ self.search_button = None
47
+ self.loading_dialog = None
48
+ self.cache_manager = None
49
+
34
50
  self.connection = connection
35
51
  self.current_collection: str = ""
36
52
  self.current_database: str = ""
@@ -42,8 +58,32 @@ class SearchView(QWidget):
42
58
 
43
59
  def _setup_ui(self):
44
60
  """Setup widget UI."""
61
+ # Assign all UI attributes at the top to avoid NoneType errors
62
+ self.breadcrumb_label = QLabel("")
63
+ self.query_input = QTextEdit()
64
+ self.results_table = QTableWidget()
65
+ self.results_status = QLabel("No search performed")
66
+ self.refresh_button = QPushButton("Refresh")
67
+ self.n_results_spin = QSpinBox()
68
+ self.filter_builder = FilterBuilder()
69
+ self.filter_group = QGroupBox("Advanced Metadata Filters")
70
+ self.search_button = QPushButton("Search")
71
+
45
72
  layout = QVBoxLayout(self)
46
73
 
74
+ # Breadcrumb bar (for pro features)
75
+ self.breadcrumb_label.setStyleSheet(
76
+ "color: #2980b9; font-weight: bold; padding: 2px 0 4px 0;"
77
+ )
78
+ # Configure breadcrumb label sizing
79
+ self.breadcrumb_label.setWordWrap(False)
80
+ self.breadcrumb_label.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
81
+ # Store full breadcrumb text for tooltip and eliding
82
+ self._full_breadcrumb = ""
83
+ # Elide mode: 'left' or 'middle'
84
+ self._elide_mode = "left"
85
+ layout.addWidget(self.breadcrumb_label)
86
+
47
87
  # Create splitter for query and results
48
88
  splitter = QSplitter(Qt.Vertical)
49
89
 
@@ -56,7 +96,6 @@ class SearchView(QWidget):
56
96
 
57
97
  # Query input
58
98
  query_group_layout.addWidget(QLabel("Enter search text:"))
59
- self.query_input = QTextEdit()
60
99
  self.query_input.setMaximumHeight(100)
61
100
  self.query_input.setPlaceholderText("Enter text to search for similar vectors...")
62
101
  query_group_layout.addWidget(self.query_input)
@@ -65,7 +104,6 @@ class SearchView(QWidget):
65
104
  controls_layout = QHBoxLayout()
66
105
 
67
106
  controls_layout.addWidget(QLabel("Results:"))
68
- self.n_results_spin = QSpinBox()
69
107
  self.n_results_spin.setMinimum(1)
70
108
  self.n_results_spin.setMaximum(100)
71
109
  self.n_results_spin.setValue(10)
@@ -73,9 +111,13 @@ class SearchView(QWidget):
73
111
 
74
112
  controls_layout.addStretch()
75
113
 
76
- self.search_button = QPushButton("Search")
77
114
  self.search_button.clicked.connect(self._perform_search)
78
115
  self.search_button.setDefault(True)
116
+
117
+ self.refresh_button.setToolTip("Reset search input and results")
118
+ self.refresh_button.clicked.connect(self._refresh_search)
119
+ controls_layout.addWidget(self.refresh_button)
120
+
79
121
  controls_layout.addWidget(self.search_button)
80
122
 
81
123
  query_group_layout.addLayout(controls_layout)
@@ -83,18 +125,15 @@ class SearchView(QWidget):
83
125
  query_layout.addWidget(query_group)
84
126
 
85
127
  # Advanced filters section
86
- filter_group = QGroupBox("Advanced Metadata Filters")
87
- filter_group.setCheckable(True)
88
- filter_group.setChecked(False)
128
+ self.filter_group.setCheckable(True)
129
+ self.filter_group.setChecked(False)
89
130
  filter_group_layout = QVBoxLayout()
90
131
 
91
- # Filter builder
92
- self.filter_builder = FilterBuilder()
132
+ # Filter builder (already created at top)
93
133
  filter_group_layout.addWidget(self.filter_builder)
94
134
 
95
- filter_group.setLayout(filter_group_layout)
96
- query_layout.addWidget(filter_group)
97
- self.filter_group = filter_group
135
+ self.filter_group.setLayout(filter_group_layout)
136
+ query_layout.addWidget(self.filter_group)
98
137
 
99
138
  splitter.addWidget(query_widget)
100
139
 
@@ -106,12 +145,13 @@ class SearchView(QWidget):
106
145
  results_group = QGroupBox("Search Results")
107
146
  results_group_layout = QVBoxLayout()
108
147
 
109
- self.results_table = QTableWidget()
110
148
  self.results_table.setSelectionBehavior(QTableWidget.SelectRows)
111
149
  self.results_table.setAlternatingRowColors(True)
150
+ # Enable context menu
151
+ self.results_table.setContextMenuPolicy(Qt.CustomContextMenu)
152
+ self.results_table.customContextMenuRequested.connect(self._show_context_menu)
112
153
  results_group_layout.addWidget(self.results_table)
113
154
 
114
- self.results_status = QLabel("No search performed")
115
155
  self.results_status.setStyleSheet("color: gray;")
116
156
  results_group_layout.addWidget(self.results_status)
117
157
 
@@ -125,6 +165,57 @@ class SearchView(QWidget):
125
165
  splitter.setStretchFactor(1, 2)
126
166
 
127
167
  layout.addWidget(splitter)
168
+ self.setLayout(layout)
169
+
170
+ def set_breadcrumb(self, text: str):
171
+ """Set the breadcrumb indicator (for pro features)."""
172
+ # Keep the full breadcrumb for tooltip and compute an elided
173
+ # display that fits the current label width (elide from the left).
174
+ self._full_breadcrumb = text or ""
175
+ self.breadcrumb_label.setToolTip(self._full_breadcrumb)
176
+ self._update_breadcrumb_display()
177
+
178
+ def _update_breadcrumb_display(self):
179
+ """Compute and apply an elided breadcrumb display based on label width."""
180
+ if not hasattr(self, "breadcrumb_label") or self.breadcrumb_label is None:
181
+ return
182
+
183
+ fm = QFontMetrics(self.breadcrumb_label.font())
184
+ avail_width = max(10, self.breadcrumb_label.width())
185
+ if not self._full_breadcrumb:
186
+ self.breadcrumb_label.setText("")
187
+ return
188
+
189
+ # Choose elide mode from settings
190
+ elide_flag = Qt.ElideLeft if self._elide_mode == "left" else Qt.ElideMiddle
191
+ elided = fm.elidedText(self._full_breadcrumb, elide_flag, avail_width)
192
+ self.breadcrumb_label.setText(elided)
193
+
194
+ def set_elide_mode(self, mode: str):
195
+ """Set elide mode ('left' or 'middle') and refresh display."""
196
+ if mode not in ("left", "middle"):
197
+ mode = "left"
198
+ self._elide_mode = mode
199
+ self._update_breadcrumb_display()
200
+
201
+ def resizeEvent(self, event):
202
+ """Handle resize to recompute breadcrumb eliding."""
203
+ try:
204
+ super().resizeEvent(event)
205
+ finally:
206
+ self._update_breadcrumb_display()
207
+
208
+ def clear_breadcrumb(self):
209
+ """Clear the breadcrumb indicator."""
210
+ self.breadcrumb_label.setText("")
211
+
212
+ def _refresh_search(self):
213
+ """Reset search input, results, and breadcrumb."""
214
+ self.query_input.clear()
215
+ self.results_table.setRowCount(0)
216
+ self.results_status.setText("No search performed")
217
+ self.clear_breadcrumb()
218
+ self.search_results = None
128
219
 
129
220
  def set_collection(self, collection_name: str, database_name: str = ""):
130
221
  """Set the current collection to search."""
@@ -139,6 +230,11 @@ class SearchView(QWidget):
139
230
  collection_name,
140
231
  )
141
232
 
233
+ # Guard: if results_table is not yet initialized, do nothing
234
+ if self.results_table is None:
235
+ log_info("[SearchView] set_collection called before UI setup; skipping.")
236
+ return
237
+
142
238
  # Check cache first
143
239
  cached = self.cache_manager.get(self.current_database, self.current_collection)
144
240
  if cached:
@@ -281,6 +377,43 @@ class SearchView(QWidget):
281
377
  },
282
378
  )
283
379
 
380
+ def _show_context_menu(self, position):
381
+ """Show context menu for results table rows."""
382
+ # Get the item at the position
383
+ item = self.results_table.itemAt(position)
384
+ if not item:
385
+ return
386
+
387
+ row = item.row()
388
+ if row < 0 or row >= self.results_table.rowCount():
389
+ return
390
+
391
+ # Create context menu
392
+ menu = QMenu(self)
393
+
394
+ # Call extension hooks to add custom menu items
395
+ try:
396
+ from vector_inspector.extensions import table_context_menu_hook
397
+
398
+ table_context_menu_hook.trigger(
399
+ menu=menu,
400
+ table=self.results_table,
401
+ row=row,
402
+ data={
403
+ "current_data": self.search_results,
404
+ "collection_name": self.current_collection,
405
+ "database_name": self.current_database,
406
+ "connection": self.connection,
407
+ "view_type": "search",
408
+ },
409
+ )
410
+ except Exception as e:
411
+ log_info("Extension hook error: %s", e)
412
+
413
+ # Only show menu if it has items
414
+ if not menu.isEmpty():
415
+ menu.exec(self.results_table.viewport().mapToGlobal(position))
416
+
284
417
  def _display_results(self, results: Dict[str, Any]):
285
418
  """Display search results in table."""
286
419
  ids = results.get("ids", [[]])[0]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: vector-inspector
3
- Version: 0.3.8
3
+ Version: 0.3.11
4
4
  Summary: A comprehensive desktop application for visualizing, querying, and managing vector database data
5
5
  Author-Email: Anthony Dawson <anthonypdawson+github@gmail.com>
6
6
  License: MIT
@@ -8,7 +8,7 @@ Project-URL: Homepage, https://vector-inspector.divinedevops.com
8
8
  Project-URL: Source, https://github.com/anthonypdawson/vector-inspector
9
9
  Project-URL: Issues, https://github.com/anthonypdawson/vector-inspector/issues
10
10
  Project-URL: Documentation, https://github.com/anthonypdawson/vector-inspector#readme
11
- Requires-Python: <3.14,>=3.10
11
+ Requires-Python: >=3.10
12
12
  Requires-Dist: chromadb>=0.4.22
13
13
  Requires-Dist: qdrant-client>=1.7.0
14
14
  Requires-Dist: pyside6>=6.6.0
@@ -32,30 +32,8 @@ Description-Content-Type: text/markdown
32
32
 
33
33
  # Latest updates
34
34
 
35
- ## Vector Inspector 2026.01 Release Notes
36
-
37
- ### Major Refactor and Studio-Ready Architecture
38
- - Refactored main window into modular components:
39
- - InspectorShell: reusable UI shell (splitter, tabs, layout)
40
- - ProviderFactory: centralized connection creation
41
- - DialogService: dialog management
42
- - ConnectionController: connection lifecycle and threading
43
- - InspectorTabs: pluggable tab registry
44
- - MainWindow now inherits from InspectorShell and is fully reusable as a widget
45
- - Bootstrap logic is separated from UI logic—Studio can host Inspector as a component
46
- - Tab system is now pluggable: Studio and Inspector can add, remove, or override tabs via TabDefinition
47
- - All Inspector UI logic is self-contained; Studio can extend without modifying Inspector code
48
-
49
- ### Data Browser Improvements
50
- - Added a checkbox: Generate embeddings on edit (default: checked)
51
- - When unchecked, editing a row skips embedding regeneration
52
- - Setting is persisted per user
53
-
54
- ### Developer and Architecture Notes
55
- - All modules pass syntax checks and are ready for Studio integration
56
- - No breaking changes for existing Inspector users
57
- - Inspector is now a true UI module, not just an application
58
-
35
+ - Added optional telemetry opt-in in settings panel (File -> Preferences -> Telemetry). See docs/telemetry/model_telemetry_and_registry.md and docs/telemetry/unified_telemetry_architecture.md for details.
36
+ - Currently only 'launch' telemetry is sent. Model registry telemetry will be added later.
59
37
  ---
60
38
 
61
39
  # Vector Inspector
@@ -63,7 +41,7 @@ Description-Content-Type: text/markdown
63
41
  > **Disclaimer:** This tool is currently under active development and is **not production ready**. Not all features have been thoroughly tested and code is released frequently. Use with caution in critical or production environments.
64
42
 
65
43
  [![CI](https://github.com/anthonypdawson/vector-inspector/actions/workflows/ci-tests.yml/badge.svg?branch=master)](https://github.com/anthonypdawson/vector-inspector/actions/workflows/ci-tests.yml)
66
- [![Publish](https://github.com/anthonypdawson/vector-inspector/actions/workflows/publish%20copy.yml/badge.svg?branch=master)](https://github.com/anthonypdawson/vector-inspector/actions/workflows/publish%20copy.yml)
44
+ [![Publish](https://github.com/anthonypdawson/vector-inspector/actions/workflows/release-and-publish.yml/badge.svg?branch=master)](https://github.com/anthonypdawson/vector-inspector/actions/workflows/release-and-publish.yml)
67
45
 
68
46
  [![PyPI Version](https://img.shields.io/pypi/v/vector-inspector.svg?cacheSeconds=300)](https://pypi.org/project/vector-inspector/)
69
47
  [![PyPI Downloads](https://static.pepy.tech/personalized-badge/vector-inspector?period=total&units=INTERNATIONAL_SYSTEM&left_color=BLACK&right_color=GREEN&left_text=downloads)](https://pepy.tech/projects/vector-inspector)
@@ -92,6 +70,8 @@ Vector Inspector bridges the gap between vector databases and user-friendly data
92
70
 
93
71
  ## Key Features
94
72
 
73
+ > **Note:** Some features listed below may be not started or currently in progress.
74
+
95
75
  ### 1. **Multi-Provider Support**
96
76
  - Connect to vector databases:
97
77
  - ChromaDB (persistent local storage)
@@ -1,7 +1,7 @@
1
- vector_inspector-0.3.8.dist-info/METADATA,sha256=efuN0ywBf5QkEwGbRDiOV9YHG9pLNdBWBqnHPDsqUg0,11574
2
- vector_inspector-0.3.8.dist-info/WHEEL,sha256=tsUv_t7BDeJeRHaSrczbGeuK-TtDpGsWi_JfpzD255I,90
3
- vector_inspector-0.3.8.dist-info/entry_points.txt,sha256=u96envMI2NFImZUJDFutiiWl7ZoHrrev9joAgtyvTxo,80
4
- vector_inspector/__init__.py,sha256=Q8XbXn98o0eliQWPePhy-aGUz2KNnVg7bQq-sBPl7zQ,119
1
+ vector_inspector-0.3.11.dist-info/METADATA,sha256=UJw5xL019__f7EIgTcIxAqQ4Wq7SjTXxZJfNLNRwVtY,10781
2
+ vector_inspector-0.3.11.dist-info/WHEEL,sha256=Wb0ASbVj8JvWHpOiIpPi7ucfIgJeCi__PzivviEAQFc,90
3
+ vector_inspector-0.3.11.dist-info/entry_points.txt,sha256=u96envMI2NFImZUJDFutiiWl7ZoHrrev9joAgtyvTxo,80
4
+ vector_inspector/__init__.py,sha256=gBFBsYQUOvdcns7nbI7_gtyUyzNgU6O7XqppKmCQ8f0,370
5
5
  vector_inspector/__main__.py,sha256=Vdhw8YA1K3wPMlbJQYL5WqvRzAKVeZ16mZQFO9VRmCo,62
6
6
  vector_inspector/config/__init__.py,sha256=vHkVsXSUdInsfzWSOLPZzaaELa3SGenAgfpY5EYbsYA,95
7
7
  vector_inspector/config/known_embedding_models.json,sha256=tnTWI3OvdL2z0YreL_iBzbceIXR69NDj-0tBcEV6NVM,15701
@@ -11,7 +11,7 @@ vector_inspector/core/connection_manager.py,sha256=xNmgSXqJcMC-iaOY33-Xnfxq4QjUN
11
11
  vector_inspector/core/connections/__init__.py,sha256=lDZ-Qv-CbBvVcSlT8K2824zojovEIKhykHVSLARHZWs,345
12
12
  vector_inspector/core/connections/base_connection.py,sha256=jDA1cEeNbTghqcCZYoLRpRPXIUteU5mSdpKcjvr4JQI,12236
13
13
  vector_inspector/core/connections/chroma_connection.py,sha256=Mks17olaHIxvJAdCVB6EoQa5Tj7ScoD3_b2--Ra0Owk,21006
14
- vector_inspector/core/connections/pgvector_connection.py,sha256=7xzS3WRaa8IMt-RMU056kzxqiu2SLPgYxDMz_Xw5LVc,43812
14
+ vector_inspector/core/connections/pgvector_connection.py,sha256=7Iq6b1pWB5Rz-zva7ZvDQj098ZVMz0WUkVgarSgTpA4,44940
15
15
  vector_inspector/core/connections/pinecone_connection.py,sha256=V6nHLB6DXfQJG8LiH1uPtWFs6otACkaNxeYrM0uY0AQ,26954
16
16
  vector_inspector/core/connections/qdrant_connection.py,sha256=GkLmnwnswakSMRM16pmtjkR6QXBszDhvg-aKhmI9jxw,31958
17
17
  vector_inspector/core/connections/qdrant_helpers/__init__.py,sha256=u2YNjiW4sbNtqhTNOQr4hmOFqirlNlllsyBK2gWs9eU,262
@@ -27,7 +27,9 @@ vector_inspector/core/embedding_utils.py,sha256=UCnJllDS_YPqbOPVo_kxSCUxM64C5tmc
27
27
  vector_inspector/core/logging.py,sha256=HQ6_OZgZmaS3OMFOTAqc0oRbZujqo1W0w8OU4viXP1g,845
28
28
  vector_inspector/core/model_registry.py,sha256=fdofceD3iyNpECVC7djTEAaDYgHX_7JQ3ROh5A0plpY,6269
29
29
  vector_inspector/core/provider_factory.py,sha256=QFDpJTOBJVYAdOLlY0GxSWl87Yj_UT9ZzOm9cjVsGMU,3924
30
- vector_inspector/main.py,sha256=iZeMPH94q6Ma92hWQLJ7on3rwxytUoS0V8n9MkAuEaY,595
30
+ vector_inspector/extensions/__init__.py,sha256=cbN22B89XQT7uZRXBujqfbXDWm5w4rao0UHgMsUJqu0,3303
31
+ vector_inspector/extensions/telemetry_settings_panel.py,sha256=uYvxnjBj_sqD18TpxeZP1f1VRbYhSjQV5boVc53dx40,934
32
+ vector_inspector/main.py,sha256=XhoVqZJm49X5n5dGNcuTCJagU3WgCckgO0TifxAhl5A,1182
31
33
  vector_inspector/services/__init__.py,sha256=QLgH7oybjHuEYDFNiBgmJxvSpgAzHEuBEPXa3SKJb_I,67
32
34
  vector_inspector/services/backup_helpers.py,sha256=aX1ONFegERq6dpoNM1eJrbyE1gWCV3SuUHMyPpnxrYM,2005
33
35
  vector_inspector/services/backup_restore_service.py,sha256=8e64C4v8v-5Sw0upC4YaJWlLFkqAkspNt0k7yZjf_zk,7625
@@ -35,7 +37,8 @@ vector_inspector/services/credential_service.py,sha256=Ui4JzivQ5YCytQYsKdzpLs5nZ
35
37
  vector_inspector/services/filter_service.py,sha256=xDrMxNWsYzRcR1n0Fd-yp6Fo-4aLbVIDkhj2GKmrw5o,2370
36
38
  vector_inspector/services/import_export_service.py,sha256=4NOfAa6ZyvMyj5cDM4xu0Wqx0pgnK3cCNBGo3E6j4LE,10200
37
39
  vector_inspector/services/profile_service.py,sha256=AMeC6XOfI6Qumi0bKlTbqU-czMcle0rrHYK68ceK5r8,12856
38
- vector_inspector/services/settings_service.py,sha256=YfuPK4VIn7Kd36dpGsgzLAJw3fGybopDUcHhR30GSaY,7692
40
+ vector_inspector/services/settings_service.py,sha256=Li1JR_6Ihuta9ifo_heqbjT5e3e26kjcxR8OxPDiN58,10798
41
+ vector_inspector/services/telemetry_service.py,sha256=eWOmQxZjW2v_IK_HQ_uIRi4ostQyvo4ptxK3BB1cdq8,3066
39
42
  vector_inspector/services/update_service.py,sha256=B_l0qQUe0L_rYWNYnqvXET4UNf6bCSsl2LTkINtp0GE,2771
40
43
  vector_inspector/services/visualization_service.py,sha256=9TOK1S1u1U74wLpF5NdDiryyrjOzFnvE8kwjugf95Wk,4208
41
44
  vector_inspector/ui/__init__.py,sha256=262ZiXO6Luk8vZnhCIoYxOtGiny0bXK-BTKjxUNBx-w,43
@@ -54,7 +57,8 @@ vector_inspector/ui/dialogs/__init__.py,sha256=xtT77L91PFfm3zHYRENHkWHJaKPm1htuU
54
57
  vector_inspector/ui/dialogs/cross_db_migration.py,sha256=BaUyic8l5Ywwql2hQyxVrCXHMjGtqerNAQHDYxcbQ54,15872
55
58
  vector_inspector/ui/dialogs/embedding_config_dialog.py,sha256=1K5LBSBXp590BvKwtHx9qgPwGREsn1mJ8cjFGSZHnMA,12926
56
59
  vector_inspector/ui/dialogs/provider_type_dialog.py,sha256=W_FAJuvicwBUJJ7PyvKow9lc8_a5pnE3RIAsh-DVndQ,6809
57
- vector_inspector/ui/main_window.py,sha256=mZfUm29Ur-dfR4pbHXRSaDsQmIHFY1_SfOahHE_o1DI,21493
60
+ vector_inspector/ui/dialogs/settings_dialog.py,sha256=sMrt1uvkgu0ZPhGClKqtMYkkY2L53LyaQaWO6Cct1qc,4510
61
+ vector_inspector/ui/main_window.py,sha256=L8waeqVndK2YXgaf5bqHOIBbHRrcZuOkg-r_lAtrpeM,27219
58
62
  vector_inspector/ui/main_window_shell.py,sha256=0o4KxRc4KXu-mJxni9dv74a5DzP4OIvJoLTX7BLqDoo,3425
59
63
  vector_inspector/ui/services/__init__.py,sha256=m2DGkhYlcQQGMtNQsup5eKmhCFhOhXHi-g9Hw0GH1vE,55
60
64
  vector_inspector/ui/services/dialog_service.py,sha256=1NHWSMvNadcmoh8tgUMSa8N7g8xYDOTaWMr1G8i9e8A,4261
@@ -63,10 +67,10 @@ vector_inspector/ui/views/__init__.py,sha256=FeMtVzSbVFBMjdwLQSQqD0FRW4ieJ4ZKXtT
63
67
  vector_inspector/ui/views/collection_browser.py,sha256=oG9_YGPoVuMs-f_zSd4EcITmEU9caxvwuubsFUrNf-c,3991
64
68
  vector_inspector/ui/views/connection_view.py,sha256=3oGbClqwpVuUD3AIT8TuM-8heDvwMYw7RowHT3b1b8o,23749
65
69
  vector_inspector/ui/views/info_panel.py,sha256=6LwOQFmRdFOcNZo_BcZ8DZ5a5GTdGLwO2IIHqYWNBdQ,26179
66
- vector_inspector/ui/views/metadata_view.py,sha256=75ITKS-DPw1GVUBWI1ppTbADb5Ka4d82wPhM7iFxDGo,45776
67
- vector_inspector/ui/views/search_view.py,sha256=LFGmbyVoB04bE6KX-Gt-WYmFrFfnMy808iHWiWVH-gU,12291
70
+ vector_inspector/ui/views/metadata_view.py,sha256=fmofgvptCjcWrB4-M6kMN3Lbo5DYHn1VulBBrd8OYLQ,47314
71
+ vector_inspector/ui/views/search_view.py,sha256=xhyCf1JrqC1ZMIxTIm8nr5iC8OsbNxx1BCfabM5aRXs,17408
68
72
  vector_inspector/ui/views/visualization_view.py,sha256=wgkSkOM-ShOHDj1GCUtKnqH87Io5vYtiOdubGV5rN44,11050
69
73
  vector_inspector/utils/__init__.py,sha256=jhHBQC8C8bfhNlf6CAt07ejjStp_YAyleaYr2dm0Dk0,38
70
74
  vector_inspector/utils/lazy_imports.py,sha256=2XZ3ZnwTvZ5vvrh36nJ_TUjwwkgjoAED6i6P9yctvt0,1211
71
75
  vector_inspector/utils/version.py,sha256=2Xk9DEKlDRGEszNNiYnK7ps1i3OH56H2uZhR0_yZORs,382
72
- vector_inspector-0.3.8.dist-info/RECORD,,
76
+ vector_inspector-0.3.11.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: pdm-backend (2.4.6)
2
+ Generator: pdm-backend (2.4.7)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any