vector-inspector 0.3.4__py3-none-any.whl → 0.3.6__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.
@@ -2,9 +2,17 @@
2
2
 
3
3
  from typing import Optional, Tuple
4
4
  from PySide6.QtWidgets import (
5
- QDialog, QVBoxLayout, QHBoxLayout, QLabel,
6
- QComboBox, QPushButton, QGroupBox, QTextEdit,
7
- QMessageBox, QLineEdit, QFormLayout
5
+ QDialog,
6
+ QVBoxLayout,
7
+ QHBoxLayout,
8
+ QLabel,
9
+ QComboBox,
10
+ QPushButton,
11
+ QGroupBox,
12
+ QTextEdit,
13
+ QMessageBox,
14
+ QLineEdit,
15
+ QFormLayout,
8
16
  )
9
17
  from PySide6.QtCore import Qt
10
18
 
@@ -14,12 +22,16 @@ from vector_inspector.core.model_registry import get_model_registry
14
22
 
15
23
  class EmbeddingConfigDialog(QDialog):
16
24
  """Dialog for selecting embedding model for a collection."""
17
-
18
- def __init__(self, collection_name: str, vector_dimension: int,
19
- provider_type: Optional[str] = None,
20
- current_model: Optional[str] = None,
21
- current_type: Optional[str] = None,
22
- parent=None):
25
+
26
+ def __init__(
27
+ self,
28
+ collection_name: str,
29
+ vector_dimension: int,
30
+ provider_type: Optional[str] = None,
31
+ current_model: Optional[str] = None,
32
+ current_type: Optional[str] = None,
33
+ parent=None,
34
+ ):
23
35
  super().__init__(parent)
24
36
  self.collection_name = collection_name
25
37
  self.vector_dimension = vector_dimension
@@ -28,7 +40,7 @@ class EmbeddingConfigDialog(QDialog):
28
40
  self.current_type = current_type
29
41
  self.selected_model = None
30
42
  self.selected_type = None
31
-
43
+
32
44
  # Determine title based on provider type
33
45
  if provider_type == "custom":
34
46
  title = "Enter Custom Model"
@@ -39,17 +51,17 @@ class EmbeddingConfigDialog(QDialog):
39
51
  "openai": "OpenAI API",
40
52
  "cohere": "Cohere API",
41
53
  "vertex-ai": "Google Vertex AI",
42
- "voyage": "Voyage AI"
54
+ "voyage": "Voyage AI",
43
55
  }
44
56
  type_name = type_names.get(provider_type, provider_type.title())
45
57
  title = f"Select Model: {type_name}"
46
58
  else:
47
59
  title = f"Configure Embedding Model - {collection_name}"
48
-
60
+
49
61
  self.setWindowTitle(title)
50
62
  self.setMinimumWidth(500)
51
63
  self._setup_ui()
52
-
64
+
53
65
  def _setup_ui(self):
54
66
  """Setup dialog UI."""
55
67
  layout = QVBoxLayout(self)
@@ -58,54 +70,57 @@ class EmbeddingConfigDialog(QDialog):
58
70
  if self.provider_type == "custom":
59
71
  self._setup_custom_ui(layout)
60
72
  return
61
-
73
+
62
74
  # Info section
63
75
  info_group = QGroupBox("Collection Information")
64
76
  info_layout = QVBoxLayout()
65
-
77
+
66
78
  info_layout.addWidget(QLabel(f"<b>Collection:</b> {self.collection_name}"))
67
79
  info_layout.addWidget(QLabel(f"<b>Vector Dimension:</b> {self.vector_dimension}"))
68
-
80
+
69
81
  if self.current_model:
70
- info_layout.addWidget(QLabel(f"<b>Current Model:</b> {self.current_model} ({self.current_type})"))
82
+ info_layout.addWidget(
83
+ QLabel(f"<b>Current Model:</b> {self.current_model} ({self.current_type})")
84
+ )
71
85
  else:
72
86
  warning = QLabel("⚠️ No embedding model configured - using automatic detection")
73
87
  warning.setStyleSheet("color: orange;")
74
88
  info_layout.addWidget(warning)
75
-
89
+
76
90
  info_group.setLayout(info_layout)
77
91
  layout.addWidget(info_group)
78
-
92
+
79
93
  # Model selection section
80
94
  model_group = QGroupBox("Embedding Model Selection")
81
95
  model_layout = QVBoxLayout()
82
-
96
+
83
97
  # Get available models for this dimension, filtered by provider type
84
98
  if self.provider_type:
85
99
  registry = get_model_registry()
86
100
  registry_models = registry.get_models_by_dimension(self.vector_dimension)
87
101
  filtered_models = [m for m in registry_models if m.type == self.provider_type]
88
102
  available_models = [(m.name, m.type, m.description) for m in filtered_models]
89
-
103
+
90
104
  # Add custom models from settings
91
105
  try:
92
- from ...services.settings_service import SettingsService
106
+ from vector_inspector.services.settings_service import SettingsService
107
+
93
108
  settings = SettingsService()
94
109
  custom_models = settings.get_custom_embedding_models(self.vector_dimension)
95
110
  for model in custom_models:
96
111
  if model["type"] == self.provider_type:
97
- available_models.append((
98
- model["name"],
99
- model["type"],
100
- f"{model['description']} (custom)"
101
- ))
112
+ available_models.append(
113
+ (model["name"], model["type"], f"{model['description']} (custom)")
114
+ )
102
115
  except Exception:
103
116
  pass
104
117
  else:
105
118
  available_models = get_available_models_for_dimension(self.vector_dimension)
106
-
119
+
107
120
  if available_models:
108
- model_layout.addWidget(QLabel(f"Available models for {self.vector_dimension}-dimensional vectors:"))
121
+ model_layout.addWidget(
122
+ QLabel(f"Available models for {self.vector_dimension}-dimensional vectors:")
123
+ )
109
124
 
110
125
  self.model_combo = QComboBox()
111
126
  for model_name, model_type, description in available_models:
@@ -129,7 +144,9 @@ class EmbeddingConfigDialog(QDialog):
129
144
  self.description_text = QTextEdit()
130
145
  self.description_text.setReadOnly(True)
131
146
  self.description_text.setMaximumHeight(100)
132
- self.description_text.setStyleSheet("background-color: #f5f5f5; border: 1px solid #ccc; color: #000000;")
147
+ self.description_text.setStyleSheet(
148
+ "background-color: #f5f5f5; border: 1px solid #ccc; color: #000000;"
149
+ )
133
150
  model_layout.addWidget(self.description_text)
134
151
 
135
152
  # Update description when selection changes
@@ -139,7 +156,9 @@ class EmbeddingConfigDialog(QDialog):
139
156
  else:
140
157
  # No models for this type + dimension
141
158
  type_name = self.provider_type or "any type"
142
- warning = QLabel(f"⚠️ No models of type '{type_name}' available for {self.vector_dimension} dimensions.")
159
+ warning = QLabel(
160
+ f"⚠️ No models of type '{type_name}' available for {self.vector_dimension} dimensions."
161
+ )
143
162
  warning.setWordWrap(True)
144
163
  model_layout.addWidget(warning)
145
164
 
@@ -149,32 +168,32 @@ class EmbeddingConfigDialog(QDialog):
149
168
  model_layout.addWidget(QLabel(dims_text))
150
169
 
151
170
  self.model_combo = None
152
-
171
+
153
172
  model_group.setLayout(model_layout)
154
173
  layout.addWidget(model_group)
155
-
174
+
156
175
  # Buttons
157
176
  button_layout = QHBoxLayout()
158
177
  button_layout.addStretch()
159
-
178
+
160
179
  self.save_btn = QPushButton("Save Configuration")
161
180
  self.save_btn.clicked.connect(self._on_save)
162
181
  # Always enabled - user can choose from combo OR enter custom
163
182
  self.save_btn.setEnabled(True)
164
-
183
+
165
184
  self.clear_btn = QPushButton("Clear Configuration")
166
185
  self.clear_btn.clicked.connect(self._clear_config)
167
186
  self.clear_btn.setEnabled(self.current_model is not None)
168
-
187
+
169
188
  cancel_btn = QPushButton("Cancel")
170
189
  cancel_btn.clicked.connect(self.reject)
171
-
190
+
172
191
  button_layout.addWidget(self.save_btn)
173
192
  button_layout.addWidget(self.clear_btn)
174
193
  button_layout.addWidget(cancel_btn)
175
-
194
+
176
195
  layout.addLayout(button_layout)
177
-
196
+
178
197
  def _setup_custom_ui(self, layout):
179
198
  """Setup UI for custom model entry."""
180
199
  # Info section
@@ -194,14 +213,18 @@ class EmbeddingConfigDialog(QDialog):
194
213
  custom_layout.addRow("Model Name:", self.custom_name_input)
195
214
 
196
215
  self.custom_type_combo = QComboBox()
197
- self.custom_type_combo.addItems(["sentence-transformer", "clip", "openai", "cohere", "vertex-ai", "voyage", "custom"])
216
+ self.custom_type_combo.addItems(
217
+ ["sentence-transformer", "clip", "openai", "cohere", "vertex-ai", "voyage", "custom"]
218
+ )
198
219
  custom_layout.addRow("Model Type:", self.custom_type_combo)
199
220
 
200
221
  self.custom_desc_input = QLineEdit()
201
222
  self.custom_desc_input.setPlaceholderText("Brief description (optional)")
202
223
  custom_layout.addRow("Description:", self.custom_desc_input)
203
224
 
204
- custom_note = QLabel("💡 Custom models will be saved and available for future use with this dimension.")
225
+ custom_note = QLabel(
226
+ "💡 Custom models will be saved and available for future use with this dimension."
227
+ )
205
228
  custom_note.setWordWrap(True)
206
229
  custom_note.setStyleSheet("color: #666; font-size: 10px; padding: 4px;")
207
230
  custom_layout.addRow(custom_note)
@@ -227,7 +250,7 @@ class EmbeddingConfigDialog(QDialog):
227
250
 
228
251
  # No combo or description for custom mode
229
252
  self.model_combo = None
230
-
253
+
231
254
  def _save_custom_model(self):
232
255
  """Save custom model entry."""
233
256
  custom_name = self.custom_name_input.text().strip()
@@ -240,27 +263,28 @@ class EmbeddingConfigDialog(QDialog):
240
263
 
241
264
  # Save custom model to registry
242
265
  from vector_inspector.services.settings_service import SettingsService
266
+
243
267
  settings = SettingsService()
244
268
 
245
269
  settings.add_custom_embedding_model(
246
270
  model_name=custom_name,
247
271
  dimension=self.vector_dimension,
248
272
  model_type=custom_type,
249
- description=custom_desc if custom_desc else f"Custom {custom_type} model"
273
+ description=custom_desc if custom_desc else f"Custom {custom_type} model",
250
274
  )
251
275
 
252
276
  # Set selection to custom model
253
277
  self.selected_model = custom_name
254
278
  self.selected_type = custom_type
255
279
  self.accept()
256
-
280
+
257
281
  def _update_description(self):
258
282
  """Update the description text based on selected model."""
259
283
  if not self.model_combo:
260
284
  return
261
-
285
+
262
286
  model_name, model_type = self.model_combo.currentData()
263
-
287
+
264
288
  descriptions = {
265
289
  "sentence-transformer": (
266
290
  "Sentence-Transformers are text-only embedding models optimized for semantic similarity. "
@@ -270,17 +294,14 @@ class EmbeddingConfigDialog(QDialog):
270
294
  "CLIP (Contrastive Language-Image Pre-training) is a multi-modal model that can embed both "
271
295
  "text and images into the same vector space. This allows text queries to find semantically "
272
296
  "similar images, and vice versa. Perfect for image search with text descriptions."
273
- )
297
+ ),
274
298
  }
275
-
299
+
276
300
  desc = descriptions.get(model_type, "Embedding model for vector similarity search.")
277
301
  self.description_text.setPlainText(
278
- f"Model: {model_name}\n"
279
- f"Type: {model_type}\n"
280
- f"Dimension: {self.vector_dimension}\n\n"
281
- f"{desc}"
302
+ f"Model: {model_name}\nType: {model_type}\nDimension: {self.vector_dimension}\n\n{desc}"
282
303
  )
283
-
304
+
284
305
  def _on_save(self):
285
306
  """Handle save button click."""
286
307
  if self.model_combo and self.model_combo.currentData():
@@ -291,23 +312,23 @@ class EmbeddingConfigDialog(QDialog):
291
312
  else:
292
313
  QMessageBox.warning(self, "No Selection", "Please select a model from the list.")
293
314
  return
294
-
315
+
295
316
  self.accept()
296
-
317
+
297
318
  def _clear_config(self):
298
319
  """Clear the embedding model configuration."""
299
320
  reply = QMessageBox.question(
300
321
  self,
301
322
  "Clear Configuration",
302
323
  "This will remove the custom embedding model configuration and use automatic detection. Continue?",
303
- QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No
324
+ QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
304
325
  )
305
-
326
+
306
327
  if reply == QMessageBox.StandardButton.Yes:
307
328
  self.selected_model = None
308
329
  self.selected_type = None
309
330
  self.done(2) # Custom code for "clear"
310
-
331
+
311
332
  def get_selection(self) -> Optional[Tuple[str, str]]:
312
333
  """Get the selected model and type (from either combo or custom entry)."""
313
334
  if self.selected_model and self.selected_type:
@@ -24,6 +24,7 @@ from vector_inspector.core.connections.base_connection import VectorDBConnection
24
24
  from vector_inspector.core.connections.chroma_connection import ChromaDBConnection
25
25
  from vector_inspector.core.connections.qdrant_connection import QdrantConnection
26
26
  from vector_inspector.core.connections.pinecone_connection import PineconeConnection
27
+ from vector_inspector.core.connections.pgvector_connection import PgVectorConnection
27
28
  from vector_inspector.services.profile_service import ProfileService
28
29
  from vector_inspector.services.settings_service import SettingsService
29
30
  from vector_inspector.ui.components.connection_manager_panel import ConnectionManagerPanel
@@ -433,6 +434,8 @@ class MainWindow(QMainWindow):
433
434
  connection = self._create_qdrant_connection(config, credentials)
434
435
  elif provider == "pinecone":
435
436
  connection = self._create_pinecone_connection(config, credentials)
437
+ elif provider == "pgvector":
438
+ connection = self._create_pgvector_connection(config, credentials)
436
439
  else:
437
440
  QMessageBox.warning(self, "Error", f"Unsupported provider: {provider}")
438
441
  return
@@ -500,6 +503,25 @@ class MainWindow(QMainWindow):
500
503
 
501
504
  return PineconeConnection(api_key=api_key)
502
505
 
506
+ def _create_pgvector_connection(self, config: dict, credentials: dict) -> PgVectorConnection:
507
+ """Create a PgVector/Postgres connection from profile config/credentials."""
508
+ conn_type = config.get("type")
509
+
510
+ # We expect HTTP-style profile for pgvector (host/port + db creds)
511
+ if conn_type == "http":
512
+ host = config.get("host", "localhost")
513
+ port = int(config.get("port", 5432))
514
+ database = config.get("database")
515
+ user = config.get("user")
516
+ # Prefer password from credentials
517
+ password = credentials.get("password")
518
+
519
+ return PgVectorConnection(
520
+ host=host, port=port, database=database, user=user, password=password
521
+ )
522
+
523
+ raise ValueError("Unsupported connection type for PgVector profile")
524
+
503
525
  def _on_connection_finished(
504
526
  self, connection_id: str, success: bool, collections: list, error: str
505
527
  ):