sf-vector-sdk 0.2.2__tar.gz → 0.2.3__tar.gz

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 (30) hide show
  1. {sf_vector_sdk-0.2.2 → sf_vector_sdk-0.2.3}/PKG-INFO +1 -1
  2. {sf_vector_sdk-0.2.2 → sf_vector_sdk-0.2.3}/vector_sdk/__init__.py +1 -1
  3. {sf_vector_sdk-0.2.2 → sf_vector_sdk-0.2.3}/vector_sdk/namespaces/embeddings.py +31 -57
  4. {sf_vector_sdk-0.2.2 → sf_vector_sdk-0.2.3}/vector_sdk/namespaces/search.py +38 -60
  5. {sf_vector_sdk-0.2.2 → sf_vector_sdk-0.2.3}/.gitignore +0 -0
  6. {sf_vector_sdk-0.2.2 → sf_vector_sdk-0.2.3}/README.md +0 -0
  7. {sf_vector_sdk-0.2.2 → sf_vector_sdk-0.2.3}/pyproject.toml +0 -0
  8. {sf_vector_sdk-0.2.2 → sf_vector_sdk-0.2.3}/tests/__init__.py +0 -0
  9. {sf_vector_sdk-0.2.2 → sf_vector_sdk-0.2.3}/tests/test_content_hash.py +0 -0
  10. {sf_vector_sdk-0.2.2 → sf_vector_sdk-0.2.3}/vector_sdk/client.py +0 -0
  11. {sf_vector_sdk-0.2.2 → sf_vector_sdk-0.2.3}/vector_sdk/content_types.py +0 -0
  12. {sf_vector_sdk-0.2.2 → sf_vector_sdk-0.2.3}/vector_sdk/generated/embedding_pipeline/content_types/v1/content_types_pb2.py +0 -0
  13. {sf_vector_sdk-0.2.2 → sf_vector_sdk-0.2.3}/vector_sdk/generated/embedding_pipeline/content_types/v1/content_types_pb2.pyi +0 -0
  14. {sf_vector_sdk-0.2.2 → sf_vector_sdk-0.2.3}/vector_sdk/generated/embedding_pipeline/db/vectors/v1/vectors_pb2.py +0 -0
  15. {sf_vector_sdk-0.2.2 → sf_vector_sdk-0.2.3}/vector_sdk/generated/embedding_pipeline/db/vectors/v1/vectors_pb2.pyi +0 -0
  16. {sf_vector_sdk-0.2.2 → sf_vector_sdk-0.2.3}/vector_sdk/generated/embedding_pipeline/query/v1/query_pb2.py +0 -0
  17. {sf_vector_sdk-0.2.2 → sf_vector_sdk-0.2.3}/vector_sdk/generated/embedding_pipeline/query/v1/query_pb2.pyi +0 -0
  18. {sf_vector_sdk-0.2.2 → sf_vector_sdk-0.2.3}/vector_sdk/generated/embedding_pipeline/tools/v1/tools_pb2.py +0 -0
  19. {sf_vector_sdk-0.2.2 → sf_vector_sdk-0.2.3}/vector_sdk/generated/embedding_pipeline/tools/v1/tools_pb2.pyi +0 -0
  20. {sf_vector_sdk-0.2.2 → sf_vector_sdk-0.2.3}/vector_sdk/hash/__init__.py +0 -0
  21. {sf_vector_sdk-0.2.2 → sf_vector_sdk-0.2.3}/vector_sdk/hash/hasher.py +0 -0
  22. {sf_vector_sdk-0.2.2 → sf_vector_sdk-0.2.3}/vector_sdk/hash/types.py +0 -0
  23. {sf_vector_sdk-0.2.2 → sf_vector_sdk-0.2.3}/vector_sdk/namespaces/__init__.py +0 -0
  24. {sf_vector_sdk-0.2.2 → sf_vector_sdk-0.2.3}/vector_sdk/namespaces/base.py +0 -0
  25. {sf_vector_sdk-0.2.2 → sf_vector_sdk-0.2.3}/vector_sdk/namespaces/db.py +0 -0
  26. {sf_vector_sdk-0.2.2 → sf_vector_sdk-0.2.3}/vector_sdk/structured/__init__.py +0 -0
  27. {sf_vector_sdk-0.2.2 → sf_vector_sdk-0.2.3}/vector_sdk/structured/router.py +0 -0
  28. {sf_vector_sdk-0.2.2 → sf_vector_sdk-0.2.3}/vector_sdk/structured/structured_embeddings.py +0 -0
  29. {sf_vector_sdk-0.2.2 → sf_vector_sdk-0.2.3}/vector_sdk/structured/tool_config.py +0 -0
  30. {sf_vector_sdk-0.2.2 → sf_vector_sdk-0.2.3}/vector_sdk/types.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sf-vector-sdk
3
- Version: 0.2.2
3
+ Version: 0.2.3
4
4
  Summary: Python SDK for the Vector Gateway service (embeddings and vector search)
5
5
  Requires-Python: >=3.11
6
6
  Requires-Dist: redis>=5.0.0
@@ -166,7 +166,7 @@ from vector_sdk.types import (
166
166
  validate_model,
167
167
  )
168
168
 
169
- __version__ = "0.2.2"
169
+ __version__ = "0.2.3"
170
170
 
171
171
  __all__ = [
172
172
  # Clients (New API)
@@ -154,26 +154,21 @@ class EmbeddingsNamespace(BaseNamespace):
154
154
  Raises:
155
155
  TimeoutError: If no result is received within the timeout
156
156
  """
157
- channel = f"embedding:result:{request_id}"
158
- pubsub = self._redis.pubsub()
159
- pubsub.subscribe(channel)
160
-
161
- try:
162
- start_time = datetime.utcnow()
163
- while True:
164
- message = pubsub.get_message(timeout=1.0)
165
- if message and message["type"] == "message":
166
- data = json.loads(message["data"])
167
- return EmbeddingResult.from_dict(data)
168
-
169
- elapsed = (datetime.utcnow() - start_time).total_seconds()
170
- if elapsed >= timeout:
171
- raise TimeoutError(
172
- f"No result received for {request_id} within {timeout}s"
173
- )
174
- finally:
175
- pubsub.unsubscribe(channel)
176
- pubsub.close()
157
+ list_key = f"embedding:response:{request_id}"
158
+
159
+ # BRPOP blocks until result is available or timeout
160
+ result = self._redis.brpop(list_key, timeout=timeout)
161
+
162
+ if result is None:
163
+ raise TimeoutError(
164
+ f"No result received for {request_id} within {timeout}s"
165
+ )
166
+
167
+ # result = (key, value)
168
+ data = json.loads(result[1])
169
+ # Cleanup the response list
170
+ self._redis.delete(list_key)
171
+ return EmbeddingResult.from_dict(data)
177
172
 
178
173
  def create_and_wait(
179
174
  self,
@@ -189,8 +184,8 @@ class EmbeddingsNamespace(BaseNamespace):
189
184
  """
190
185
  Create embeddings and wait for the result.
191
186
 
192
- This method subscribes to the result channel BEFORE submitting the request,
193
- ensuring no race condition where the result is published before we're listening.
187
+ Uses BRPOP for efficient blocking wait - no race condition since the result
188
+ is pushed to a list that persists until consumed.
194
189
 
195
190
  Args:
196
191
  texts: List of text inputs
@@ -205,43 +200,22 @@ class EmbeddingsNamespace(BaseNamespace):
205
200
  Returns:
206
201
  The embedding result
207
202
  """
208
- # Generate request ID upfront so we can subscribe before submitting
209
203
  request_id = str(uuid.uuid4())
210
- channel = f"embedding:result:{request_id}"
211
-
212
- # Subscribe BEFORE submitting to avoid race condition
213
- pubsub = self._redis.pubsub()
214
- pubsub.subscribe(channel)
215
-
216
- try:
217
- # Now submit the request (subscription is already active)
218
- self.create(
219
- texts=texts,
220
- content_type=content_type,
221
- priority=priority,
222
- storage=storage,
223
- metadata=metadata,
224
- request_id=request_id,
225
- embedding_model=embedding_model,
226
- embedding_dimensions=embedding_dimensions,
227
- )
228
204
 
229
- # Wait for message with timeout
230
- start_time = datetime.utcnow()
231
- while True:
232
- message = pubsub.get_message(timeout=1.0)
233
- if message and message["type"] == "message":
234
- data = json.loads(message["data"])
235
- return EmbeddingResult.from_dict(data)
236
-
237
- elapsed = (datetime.utcnow() - start_time).total_seconds()
238
- if elapsed >= timeout:
239
- raise TimeoutError(
240
- f"No result received for {request_id} within {timeout}s"
241
- )
242
- finally:
243
- pubsub.unsubscribe(channel)
244
- pubsub.close()
205
+ # Submit the request first
206
+ self.create(
207
+ texts=texts,
208
+ content_type=content_type,
209
+ priority=priority,
210
+ storage=storage,
211
+ metadata=metadata,
212
+ request_id=request_id,
213
+ embedding_model=embedding_model,
214
+ embedding_dimensions=embedding_dimensions,
215
+ )
216
+
217
+ # Wait for result via BRPOP
218
+ return self.wait_for(request_id, timeout)
245
219
 
246
220
  def get_queue_depth(self) -> dict[str, int]:
247
221
  """
@@ -151,26 +151,21 @@ class SearchNamespace(BaseNamespace):
151
151
  Raises:
152
152
  TimeoutError: If no result is received within the timeout
153
153
  """
154
- channel = f"query:result:{request_id}"
155
- pubsub = self._redis.pubsub()
156
- pubsub.subscribe(channel)
157
-
158
- try:
159
- start_time = datetime.utcnow()
160
- while True:
161
- message = pubsub.get_message(timeout=1.0)
162
- if message and message["type"] == "message":
163
- data = json.loads(message["data"])
164
- return QueryResult.from_dict(data)
165
-
166
- elapsed = (datetime.utcnow() - start_time).total_seconds()
167
- if elapsed >= timeout:
168
- raise TimeoutError(
169
- f"No query result received for {request_id} within {timeout}s"
170
- )
171
- finally:
172
- pubsub.unsubscribe(channel)
173
- pubsub.close()
154
+ list_key = f"query:response:{request_id}"
155
+
156
+ # BRPOP blocks until result is available or timeout
157
+ result = self._redis.brpop(list_key, timeout=timeout)
158
+
159
+ if result is None:
160
+ raise TimeoutError(
161
+ f"No query result received for {request_id} within {timeout}s"
162
+ )
163
+
164
+ # result = (key, value)
165
+ data = json.loads(result[1])
166
+ # Cleanup the response list
167
+ self._redis.delete(list_key)
168
+ return QueryResult.from_dict(data)
174
169
 
175
170
  def query_and_wait(
176
171
  self,
@@ -193,8 +188,8 @@ class SearchNamespace(BaseNamespace):
193
188
  """
194
189
  Submit a search query and wait for the result.
195
190
 
196
- This method subscribes to the result channel BEFORE submitting the request,
197
- ensuring no race condition where the result is published before we're listening.
191
+ Uses BRPOP for efficient blocking wait - no race condition since the result
192
+ is pushed to a list that persists until consumed.
198
193
 
199
194
  Args:
200
195
  query_text: The text to search for
@@ -217,42 +212,25 @@ class SearchNamespace(BaseNamespace):
217
212
  The query result
218
213
  """
219
214
  request_id = str(uuid.uuid4())
220
- channel = f"query:result:{request_id}"
221
-
222
- pubsub = self._redis.pubsub()
223
- pubsub.subscribe(channel)
224
-
225
- try:
226
- self.query(
227
- query_text=query_text,
228
- database=database,
229
- top_k=top_k,
230
- min_score=min_score,
231
- filters=filters,
232
- namespace=namespace,
233
- collection=collection,
234
- database_name=database_name,
235
- include_vectors=include_vectors,
236
- include_metadata=include_metadata,
237
- embedding_model=embedding_model,
238
- embedding_dimensions=embedding_dimensions,
239
- priority=priority,
240
- metadata=metadata,
241
- request_id=request_id,
242
- )
243
215
 
244
- start_time = datetime.utcnow()
245
- while True:
246
- message = pubsub.get_message(timeout=1.0)
247
- if message and message["type"] == "message":
248
- data = json.loads(message["data"])
249
- return QueryResult.from_dict(data)
250
-
251
- elapsed = (datetime.utcnow() - start_time).total_seconds()
252
- if elapsed >= timeout:
253
- raise TimeoutError(
254
- f"No query result received for {request_id} within {timeout}s"
255
- )
256
- finally:
257
- pubsub.unsubscribe(channel)
258
- pubsub.close()
216
+ # Submit the request first
217
+ self.query(
218
+ query_text=query_text,
219
+ database=database,
220
+ top_k=top_k,
221
+ min_score=min_score,
222
+ filters=filters,
223
+ namespace=namespace,
224
+ collection=collection,
225
+ database_name=database_name,
226
+ include_vectors=include_vectors,
227
+ include_metadata=include_metadata,
228
+ embedding_model=embedding_model,
229
+ embedding_dimensions=embedding_dimensions,
230
+ priority=priority,
231
+ metadata=metadata,
232
+ request_id=request_id,
233
+ )
234
+
235
+ # Wait for result via BRPOP
236
+ return self.wait_for(request_id, timeout)
File without changes
File without changes