sf-vector-sdk 0.2.0__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.
@@ -0,0 +1,476 @@
1
+ Metadata-Version: 2.4
2
+ Name: sf-vector-sdk
3
+ Version: 0.2.0
4
+ Summary: Python SDK for the Vector Gateway service (embeddings and vector search)
5
+ Requires-Python: >=3.11
6
+ Requires-Dist: redis>=5.0.0
7
+ Provides-Extra: dev
8
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == 'dev'
9
+ Requires-Dist: pytest>=7.0.0; extra == 'dev'
10
+ Requires-Dist: ruff>=0.1.0; extra == 'dev'
11
+ Description-Content-Type: text/markdown
12
+
13
+ # Vector SDK for Python
14
+
15
+ A lightweight Python client for submitting embedding requests and vector search queries to the Vector Gateway service.
16
+
17
+ ## Overview
18
+
19
+ The Vector SDK provides a simple interface for generating embeddings via the centralized Vector Gateway service. The SDK communicates directly with Redis Streams, making it efficient and suitable for any Python service that can reach the shared Redis VM.
20
+
21
+ **Key Features:**
22
+ - Simple, Pythonic API with namespace-based organization
23
+ - Intuitive methods: `client.embeddings`, `client.search`, `client.db`
24
+ - Asynchronous request submission with optional waiting
25
+ - Full type hints and documentation
26
+ - Multiple embedding model support (Google Vertex AI and OpenAI)
27
+ - Client-side model validation before submission
28
+ - Minimal dependencies (just Redis)
29
+
30
+ ## Installation
31
+
32
+ ### From Source (Monorepo)
33
+
34
+ ```bash
35
+ cd packages/py/vector-sdk
36
+ pip install -e .
37
+ # Or with uv
38
+ uv pip install -e .
39
+ ```
40
+
41
+ ### From Package Registry (when published)
42
+
43
+ ```bash
44
+ pip install sf-vector-sdk
45
+ ```
46
+
47
+ ## Quick Start
48
+
49
+ ### Basic Usage
50
+
51
+ ```python
52
+ from vector_sdk import VectorClient
53
+
54
+ # Create client
55
+ client = VectorClient(
56
+ redis_url="redis://your-redis-host:6379",
57
+ http_url="http://localhost:8080", # Required for db operations
58
+ )
59
+
60
+ # Create embeddings
61
+ result = client.embeddings.create_and_wait(
62
+ texts=[
63
+ {"id": "doc1", "text": "Introduction to machine learning"},
64
+ {"id": "doc2", "text": "Deep neural networks explained"},
65
+ ],
66
+ content_type="topic",
67
+ )
68
+ print(f"Processed: {result.processed_count}, Failed: {result.failed_count}")
69
+
70
+ # Vector search
71
+ search_result = client.search.query_and_wait(
72
+ query_text="What is machine learning?",
73
+ database="turbopuffer",
74
+ namespace="topics",
75
+ top_k=10,
76
+ )
77
+ for match in search_result.matches:
78
+ print(f"{match.id}: {match.score}")
79
+
80
+ # Direct database lookup (no embedding)
81
+ docs = client.db.get_by_ids(
82
+ ids=["doc1"],
83
+ database="turbopuffer",
84
+ namespace="topics",
85
+ )
86
+
87
+ client.close()
88
+ ```
89
+
90
+ ### With Storage Configuration
91
+
92
+ ```python
93
+ from vector_sdk import VectorClient, StorageConfig, MongoDBStorage, TurboPufferStorage
94
+
95
+ client = VectorClient(redis_url="redis://your-redis-host:6379")
96
+
97
+ # Create embeddings with storage configuration
98
+ result = client.embeddings.create_and_wait(
99
+ texts=[
100
+ {
101
+ "id": "tool123",
102
+ "text": "Term: Photosynthesis. Definition: The process by which plants convert sunlight into energy.",
103
+ "document": {
104
+ "toolId": "tool123",
105
+ "toolCollection": "FlashCard",
106
+ "userId": "user456",
107
+ "contentHash": "abc123",
108
+ }
109
+ }
110
+ ],
111
+ content_type="flashcard",
112
+ priority="high",
113
+ storage=StorageConfig(
114
+ mongodb=MongoDBStorage(
115
+ database="events_new",
116
+ collection="tool_vectors",
117
+ embedding_field="toolEmbedding",
118
+ upsert_key="contentHash",
119
+ ),
120
+ turbopuffer=TurboPufferStorage(
121
+ namespace="tool_vectors",
122
+ id_field="_id",
123
+ metadata=["toolId", "toolCollection", "userId"],
124
+ ),
125
+ ),
126
+ metadata={"source": "my-service"},
127
+ )
128
+
129
+ client.close()
130
+ ```
131
+
132
+ ### Context Manager
133
+
134
+ ```python
135
+ with VectorClient(redis_url="redis://localhost:6379") as client:
136
+ result = client.embeddings.create_and_wait(
137
+ texts=[{"id": "doc1", "text": "Hello world"}],
138
+ content_type="document",
139
+ )
140
+ # Connection automatically closed
141
+ ```
142
+
143
+ ## API Reference
144
+
145
+ ### VectorClient
146
+
147
+ The main client class providing namespaced access to all SDK functionality.
148
+
149
+ #### Constructor
150
+
151
+ ```python
152
+ client = VectorClient(
153
+ redis_url="redis://localhost:6379",
154
+ http_url="http://localhost:8080", # Optional, required for db operations
155
+ )
156
+ ```
157
+
158
+ ### Namespaces
159
+
160
+ #### `client.embeddings`
161
+
162
+ Embedding generation operations.
163
+
164
+ | Method | Description |
165
+ |--------|-------------|
166
+ | `create(texts, content_type, ...)` | Submit embedding request, return request ID |
167
+ | `wait_for(request_id, timeout)` | Wait for request completion |
168
+ | `create_and_wait(texts, content_type, ...)` | Submit and wait for result |
169
+ | `get_queue_depth()` | Get current queue depth for each priority |
170
+
171
+ ```python
172
+ # Async: create and wait separately
173
+ request_id = client.embeddings.create(texts, content_type)
174
+ result = client.embeddings.wait_for(request_id)
175
+
176
+ # Sync: create and wait in one call
177
+ result = client.embeddings.create_and_wait(texts, content_type)
178
+
179
+ # Check queue depth
180
+ depths = client.embeddings.get_queue_depth()
181
+ ```
182
+
183
+ #### `client.search`
184
+
185
+ Vector similarity search operations.
186
+
187
+ | Method | Description |
188
+ |--------|-------------|
189
+ | `query(query_text, database, ...)` | Submit search query, return request ID |
190
+ | `wait_for(request_id, timeout)` | Wait for query completion |
191
+ | `query_and_wait(query_text, database, ...)` | Submit and wait for result |
192
+
193
+ ```python
194
+ # Vector search with semantic similarity
195
+ result = client.search.query_and_wait(
196
+ query_text="What is machine learning?",
197
+ database="turbopuffer",
198
+ namespace="topics",
199
+ top_k=10,
200
+ include_metadata=True,
201
+ )
202
+ ```
203
+
204
+ #### `client.db`
205
+
206
+ Direct database operations (no embedding required). Requires `http_url`.
207
+
208
+ | Method | Description |
209
+ |--------|-------------|
210
+ | `get_by_ids(ids, database, ...)` | Lookup documents by ID |
211
+ | `find_by_metadata(filters, database, ...)` | Search by metadata filters |
212
+ | `clone(id, source_namespace, destination_namespace)` | Clone document between namespaces |
213
+ | `delete(id, namespace)` | Delete document from namespace |
214
+
215
+ #### `client.structured_embeddings`
216
+
217
+ Type-safe embedding for known tool types (FlashCard, TestQuestion, etc.) with automatic text extraction, content hash computation, and database routing.
218
+
219
+ | Method | Description |
220
+ |--------|-------------|
221
+ | `embed_flashcard(data, metadata)` | Embed a flashcard, return request ID |
222
+ | `embed_flashcard_and_wait(data, metadata, timeout)` | Embed and wait for result |
223
+ | `embed_test_question(data, metadata)` | Embed a test question, return request ID |
224
+ | `embed_test_question_and_wait(data, metadata, timeout)` | Embed and wait for result |
225
+ | `embed_spaced_test_question(data, metadata)` | Embed a spaced test question, return request ID |
226
+ | `embed_spaced_test_question_and_wait(data, metadata, timeout)` | Embed and wait for result |
227
+ | `embed_audio_recap(data, metadata)` | Embed an audio recap section, return request ID |
228
+ | `embed_audio_recap_and_wait(data, metadata, timeout)` | Embed and wait for result |
229
+
230
+ ```python
231
+ from vector_sdk import VectorClient, ToolMetadata, TestQuestionInput
232
+
233
+ client = VectorClient(redis_url="redis://localhost:6379")
234
+
235
+ # Embed a flashcard - SDK handles text extraction, hashing, and routing
236
+ result = client.structured_embeddings.embed_flashcard_and_wait(
237
+ data={"type": "BASIC", "term": "Mitochondria", "definition": "The powerhouse of the cell"},
238
+ metadata=ToolMetadata(tool_id="tool123", user_id="user456", topic_id="topic789"),
239
+ )
240
+
241
+ # Embed a test question
242
+ result = client.structured_embeddings.embed_test_question_and_wait(
243
+ data=TestQuestionInput(
244
+ question="What is the capital?",
245
+ answers=[...],
246
+ question_type="multiplechoice",
247
+ ),
248
+ metadata=ToolMetadata(tool_id="tool456"),
249
+ )
250
+ ```
251
+
252
+ **Database Routing:**
253
+
254
+ Set the `STRUCTURED_EMBEDDING_DATABASE_ROUTER` environment variable:
255
+
256
+ | Value | Behavior |
257
+ |-------|----------|
258
+ | `dual` | Write to both TurboPuffer AND Pinecone if both have `enabled: True` |
259
+ | `turbopuffer` | Only write to TurboPuffer |
260
+ | `pinecone` | Only write to Pinecone |
261
+ | undefined | Defaults to `turbopuffer` |
262
+
263
+ ```python
264
+ # Lookup by IDs
265
+ result = client.db.get_by_ids(
266
+ ids=["doc1", "doc2"],
267
+ database="turbopuffer",
268
+ namespace="topics",
269
+ )
270
+
271
+ # Find by metadata
272
+ result = client.db.find_by_metadata(
273
+ filters={"userId": "user123"},
274
+ database="mongodb",
275
+ collection="vectors",
276
+ database_name="mydb",
277
+ )
278
+
279
+ # Clone between namespaces
280
+ result = client.db.clone("doc1", "ns1", "ns2")
281
+
282
+ # Delete
283
+ result = client.db.delete("doc1", "ns1")
284
+ ```
285
+
286
+ ### Types
287
+
288
+ #### Result Types
289
+
290
+ ```python
291
+ @dataclass
292
+ class EmbeddingResult:
293
+ request_id: str
294
+ status: str # "success", "partial", "failed"
295
+ processed_count: int
296
+ failed_count: int
297
+ errors: list[EmbeddingError]
298
+ timing: Optional[TimingBreakdown]
299
+ completed_at: datetime
300
+
301
+ @property
302
+ def is_success(self) -> bool: ...
303
+ @property
304
+ def is_partial(self) -> bool: ...
305
+ @property
306
+ def is_failed(self) -> bool: ...
307
+
308
+ @dataclass
309
+ class QueryResult:
310
+ request_id: str
311
+ status: str # "success", "failed"
312
+ matches: list[VectorMatch]
313
+ error: Optional[str]
314
+ timing: Optional[QueryTiming]
315
+ completed_at: datetime
316
+
317
+ @dataclass
318
+ class VectorMatch:
319
+ id: str
320
+ score: float # Similarity score (0-1, higher is more similar)
321
+ metadata: Optional[dict]
322
+ vector: Optional[list[float]]
323
+ ```
324
+
325
+ ## Priority Levels
326
+
327
+ | Priority | Use Case | Description |
328
+ |----------|----------|-------------|
329
+ | `critical` | Real-time user requests | Reserved quota, processed first |
330
+ | `high` | New content embeddings | Standard processing priority |
331
+ | `normal` | Updates, re-embeddings | Default priority |
332
+ | `low` | Backfill, batch jobs | Processed when capacity available |
333
+
334
+ ```python
335
+ result = client.embeddings.create_and_wait(texts, content_type="topic", priority="critical")
336
+ ```
337
+
338
+ ## Embedding Models
339
+
340
+ ### Supported Models
341
+
342
+ | Model | Provider | Dimensions | Custom Dims |
343
+ |-------|----------|------------|-------------|
344
+ | `gemini-embedding-001` | Google | 3072 | No |
345
+ | `text-embedding-004` | Google | 768 | No |
346
+ | `text-multilingual-embedding-002` | Google | 768 | No |
347
+ | `text-embedding-3-small` | OpenAI | 1536 | Yes |
348
+ | `text-embedding-3-large` | OpenAI | 3072 | Yes |
349
+
350
+ ### Using a Specific Model
351
+
352
+ ```python
353
+ result = client.embeddings.create_and_wait(
354
+ texts=[{"id": "doc1", "text": "Hello world"}],
355
+ content_type="document",
356
+ embedding_model="text-embedding-3-small",
357
+ embedding_dimensions=512, # Custom dimensions (only for models that support it)
358
+ )
359
+ ```
360
+
361
+ ## Content Hash
362
+
363
+ The SDK provides deterministic content hashing for learning tools.
364
+
365
+ ```python
366
+ from vector_sdk import compute_content_hash, extract_tool_text
367
+
368
+ # Compute hash for a FlashCard
369
+ hash = compute_content_hash(
370
+ "FlashCard",
371
+ {"type": "BASIC", "term": "Mitochondria", "definition": "The powerhouse of the cell"}
372
+ )
373
+
374
+ # Extract text for embedding
375
+ text = extract_tool_text(
376
+ "FlashCard",
377
+ {"type": "BASIC", "term": "Mitochondria", "definition": "The powerhouse of the cell"}
378
+ )
379
+ ```
380
+
381
+ ## Migration from EmbeddingClient
382
+
383
+ The SDK now uses a namespace-based API with `VectorClient`. The old `EmbeddingClient` is preserved for backward compatibility.
384
+
385
+ ### Method Mapping
386
+
387
+ | Old (EmbeddingClient) | New (VectorClient) |
388
+ |----------------------|-------------------|
389
+ | `submit()` | `client.embeddings.create()` |
390
+ | `wait_for_result()` | `client.embeddings.wait_for()` |
391
+ | `submit_and_wait()` | `client.embeddings.create_and_wait()` |
392
+ | `get_queue_depth()` | `client.embeddings.get_queue_depth()` |
393
+ | `query()` | `client.search.query()` |
394
+ | `wait_for_query_result()` | `client.search.wait_for()` |
395
+ | `query_and_wait()` | `client.search.query_and_wait()` |
396
+ | `lookup_by_ids()` | `client.db.get_by_ids()` |
397
+ | `search_by_metadata()` | `client.db.find_by_metadata()` |
398
+ | `clone_from_namespace()` | `client.db.clone()` |
399
+ | `delete_from_namespace()` | `client.db.delete()` |
400
+
401
+ ### Migration Example
402
+
403
+ ```python
404
+ # Old API (still works, emits deprecation warnings)
405
+ from vector_sdk import EmbeddingClient
406
+
407
+ client = EmbeddingClient("redis://localhost:6379")
408
+ result = client.submit_and_wait(texts, content_type)
409
+ client.close()
410
+
411
+ # New API (recommended)
412
+ from vector_sdk import VectorClient
413
+
414
+ client = VectorClient(redis_url="redis://localhost:6379")
415
+ result = client.embeddings.create_and_wait(texts, content_type)
416
+ client.close()
417
+ ```
418
+
419
+ ## Error Handling
420
+
421
+ ```python
422
+ from vector_sdk import VectorClient, ModelValidationError
423
+
424
+ try:
425
+ with VectorClient(redis_url="redis://localhost:6379") as client:
426
+ result = client.embeddings.create_and_wait(
427
+ texts=[{"id": "doc1", "text": "Hello"}],
428
+ content_type="test",
429
+ embedding_model="text-embedding-3-small",
430
+ timeout=30,
431
+ )
432
+
433
+ if result.is_success:
434
+ print("Success!")
435
+ elif result.is_partial:
436
+ print("Partial success. Errors:")
437
+ for err in result.errors:
438
+ print(f" - {err.id}: {err.error}")
439
+
440
+ except ModelValidationError as e:
441
+ print(f"Model validation failed: {e}")
442
+ except TimeoutError as e:
443
+ print(f"Request timed out: {e}")
444
+ except ValueError as e:
445
+ print(f"Invalid input: {e}")
446
+ ```
447
+
448
+ ## Best Practices
449
+
450
+ ### 1. Use Appropriate Priority
451
+
452
+ ```python
453
+ # Use appropriate priority levels
454
+ client.embeddings.create(texts, content_type="backfill", priority="low")
455
+ client.embeddings.create(texts, content_type="userRequest", priority="critical")
456
+ ```
457
+
458
+ ### 2. Batch Your Requests
459
+
460
+ ```python
461
+ # Batch multiple texts per request for efficiency
462
+ texts = [{"id": doc.id, "text": doc.text} for doc in documents]
463
+ client.embeddings.create(texts, content_type)
464
+ ```
465
+
466
+ ### 3. Use Context Managers
467
+
468
+ ```python
469
+ with VectorClient(redis_url="redis://...") as client:
470
+ # Client automatically closed on exit
471
+ pass
472
+ ```
473
+
474
+ ## License
475
+
476
+ Proprietary - All rights reserved.
@@ -0,0 +1,27 @@
1
+ vector_sdk/__init__.py,sha256=Fq4Pqq-xbDX_4M_lQhw6DVXAggUB-bjscNrUk8GEk2o,6632
2
+ vector_sdk/client.py,sha256=NQFGHyR1aM0UToRFy6e9Xm_v6mk0opqzKN8UlHu97n0,17186
3
+ vector_sdk/content_types.py,sha256=krvFOR58iUZPfYlEVsk0sXD6_ANAFbxEBQGNpt1YPDU,7381
4
+ vector_sdk/types.py,sha256=rQgA2z3ls21vY-DRPZgfmm8gYFkWJk1dQaJI-nbc0no,25514
5
+ vector_sdk/generated/embedding_pipeline/content_types/v1/content_types_pb2.py,sha256=HOyqisydOUgjQ2yEcLdAuW4O46ghcL7W--Sykc9Iwzc,5962
6
+ vector_sdk/generated/embedding_pipeline/content_types/v1/content_types_pb2.pyi,sha256=fOw6liHkiXSEyvEZ_QKexDUgFNhbemuGuk52hwQ5pnQ,6738
7
+ vector_sdk/generated/embedding_pipeline/db/vectors/v1/vectors_pb2.py,sha256=xwujSU8GXborGSDgKoRHQq_DL5CA6YAGX8L_Om35lRc,7057
8
+ vector_sdk/generated/embedding_pipeline/db/vectors/v1/vectors_pb2.pyi,sha256=lxZ27fReDhHv2mKDCiPvKpicXuJObZX2zpfVYuGTk3I,8068
9
+ vector_sdk/generated/embedding_pipeline/query/v1/query_pb2.py,sha256=E7uzz9w9xQlGbe_ZfO2MN_H8B-xpyFkkyixlDfaoGv0,5579
10
+ vector_sdk/generated/embedding_pipeline/query/v1/query_pb2.pyi,sha256=a-rWfFQVAdZM5jK1qHB0bUiuSo6brcB-zUIHQezi0I8,5598
11
+ vector_sdk/generated/embedding_pipeline/tools/v1/tools_pb2.py,sha256=i2VcFAoa8xVFsiXTxseDnZwpwl6iYv4CgwNV18mHHqU,2266
12
+ vector_sdk/generated/embedding_pipeline/tools/v1/tools_pb2.pyi,sha256=KlMGcmAOXgCxVUmtqlyir_fNyRan9Bm3VEv4yGsWgHk,1453
13
+ vector_sdk/hash/__init__.py,sha256=xyy3ezP4o58IkfUr2Kk-YwuAVtNH5uUG6QyUIed4Psw,691
14
+ vector_sdk/hash/hasher.py,sha256=BjQ5d-dhJy2Smo-dssss2JDHcIfHjsvf08O_1h7Dq2I,7976
15
+ vector_sdk/hash/types.py,sha256=QWSmGhbNcYZsRPZKWxgHC-XBOf-OBtE3XMGoTb2hm9Y,1785
16
+ vector_sdk/namespaces/__init__.py,sha256=S9dJfB39s2zjYOpFn9Fvf8bk7mLKcXk5aPatKOA-xO0,374
17
+ vector_sdk/namespaces/base.py,sha256=lioZBcd43mijnN0JwTMMEpQ6whiAjaueTDAAIZS1JM0,1156
18
+ vector_sdk/namespaces/db.py,sha256=a5sEHrfy1xAjRjyM9qfZxr3IznZVA8BnY5W1Hq5jr4I,7230
19
+ vector_sdk/namespaces/embeddings.py,sha256=7hH0hvBAeDf-ypTtOzUAqzc3W6wci_dbt_ZPavcRVyU,8950
20
+ vector_sdk/namespaces/search.py,sha256=bwtZ_rTiP6q-dg8oOM5YA6taDHSphO88aq7RSuzc-tQ,8894
21
+ vector_sdk/structured/__init__.py,sha256=eRiH-V6U-TSl98peoGdsRQukTysW9ZhTsous0fwHU-I,1468
22
+ vector_sdk/structured/router.py,sha256=F3O1TYtbVFCPqVWCCYCt5QcRffX5WPlPQ7K3KlayooQ,5792
23
+ vector_sdk/structured/structured_embeddings.py,sha256=Z93Bcf38fpk8jhQvXTeioxt-yyhtQXo1fMoUKqgVlus,13031
24
+ vector_sdk/structured/tool_config.py,sha256=sv0mRNUcuPO9C8Oh0_Y52YTyakbLk6gjrBW0C04Jt_w,7462
25
+ sf_vector_sdk-0.2.0.dist-info/METADATA,sha256=HA0KnQ9SAIRYk7M-g_IOn5o1y1yL7ISpQS0j_T7u0tw,13671
26
+ sf_vector_sdk-0.2.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
27
+ sf_vector_sdk-0.2.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.28.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any