Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

115 changes: 73 additions & 42 deletions client/python/USAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ async with AsyncVortexDB(
api_key="your-api-key",
) as db:
point_id = await db.insert(
vector=DenseVector([0.1, 0.2, 0.3]),
dense_vector=DenseVector([0.1, 0.2, 0.3]),
payload=Payload.text("hello async vortex"),
)
```
Expand Down Expand Up @@ -109,32 +109,40 @@ VortexDB(

#### **Insert**

Insert a vector with an associated payload
Insert a point with a dense vector, a sparse vector, or both, and an optional payload
```
insert(*, vector: DenseVector, payload: Payload) -> str
insert(
*,
dense_vector: DenseVector | None = None,
sparse_vector: SparseVector | None = None,
payload: Payload | None = None,
point_id: str | None = None,
) -> str
```

Returns
- `point_id` (UUID string)
- `point_id` (UUID string, generated by the server when not given)

Raises
- `TypeError` if `vector` is not a `DenseVector`
- `ValueError` if neither vector is given
- `TypeError` if an argument has the wrong type
- gRPC-mapped errors (see Error Handling)

---

#### **Batch Insert**

Insert multiple vectors with payloads in a single request
Insert multiple points in a single request. The server validates the whole batch before writing anything
```
batch_insert(*, items: list[tuple[DenseVector, Payload]]) -> list[str]
batch_insert(*, points: list[PointInput]) -> list[str]
```

Returns
- List of `point_id` (UUID string)
- List of `point_id` (UUID string), in input order

Raises
- `TypeError` if input structure is invalid
- `TypeError` if `points` is not a list of `PointInput`
- `ValueError` if `points` is empty
- gRPC-mapped errors (see Error Handling)

---
Expand All @@ -154,48 +162,33 @@ Returns

#### **Search**

Search for nearest neighbours to a query vector
Search for the nearest points to a dense or a sparse query
```
search(
*,
vector: DenseVector,
similarity: Similarity,
limit: int,
) -> list[tuple[str, float]]
search(*, query: SearchQuery) -> list[SearchHit]
```

Returns
- List of `(point_id, score)` tuples, where score is the distance (lower is closer)
- List of `SearchHit(id, score)`, best first. For dense queries the score is a distance (lower is closer). For sparse queries it is a dot product (higher is closer)

Raises
- `TypeError` if `vector` is not a `DenseVector`
- `TypeError` if `query` is not a `SearchQuery`
- `InvalidArgumentError` for invalid parameters

---

#### **Batch Search**

Search for nearest neighbours for multiple queries in a single request
Run multiple queries in a single request. Dense and sparse queries can be mixed
```
batch_search(
*,
queries,
similarity: Similarity | None = None,
limit: int | None = None,
) -> list[list[tuple[str, float]]]
batch_search(*, queries: list[SearchQuery]) -> list[list[SearchHit]]
```

Returns
- `TypeError` for invalid query formats
- `ValueError` if required parameters are missing
- One list of `SearchHit` per query, in order

Supported Input Formats:
The `queries` parameter is flexible and supports multiple formats:
- List of `SearchQuery` objects
- List of `(DenseVector, Similarity, Limit)` tuples
- List of `(DenseVector, Similarity)` tuples with a global `Limit`
- List of `(DenseVector, Limit)` tuples with a global `Similarity`
- List of `DenseVector` with global `Similarity` and `Limit`
Raises
- `TypeError` if `queries` is not a list of `SearchQuery`
- `ValueError` if `queries` is empty

---

Expand Down Expand Up @@ -247,33 +240,70 @@ Factory Helpers:

---

### `SparseVector`

```
SparseVector(token_ids: list[int], weights: list[float])
```
- `token_ids` must be non-empty, non-negative and strictly increasing
- one finite, non-negative weight per token id
- Immutable (`frozen=True`)

---

### `PointInput`

```
PointInput(
dense_vector: DenseVector | None = None,
sparse_vector: SparseVector | None = None,
payload: Payload | None = None,
point_id: str | None = None,
)
```
A point to insert with `batch_insert`. At least one vector is required

---

### `Point`

```
Point(
id: str,
vector: DenseVector,
payload: Payload,
dense_vector: DenseVector | None,
sparse_vector: SparseVector | None,
payload: Payload | None,
)
```
Additional `pretty()` method provided to properly format output
All fields are directly accessible:
- `point.id`
- `point.vector`
- `point.payload`
Fields that the point does not have are `None`

---

### `SearchQuery`

```
SearchQuery(
limit: int,
query: DenseQuery | SparseVector,
)

DenseQuery(
vector: DenseVector,
similarity: Similarity,
limit: int,
ef: int | None = None,
)
```
Structured representation of a search request
Structured representation of a search request. `ef` is the HNSW search breadth and uses the server default when not set

---

### `SearchHit`

```
SearchHit(id: str, score: float)
```
One search result

---

Expand Down Expand Up @@ -307,6 +337,7 @@ All client exceptions inherit from `VortexDBError`
| `UNAUTHENTICATED` | `AuthenticationError` |
| `NOT_FOUND` | `NotFoundError` |
| `INVALID_ARGUMENT` | `InvalidArgumentError` |
| `ALREADY_EXISTS` | `AlreadyExistsError` |
| `DEADLINE_EXCEEDED` | `TimeoutError` |
| `UNAVAILABLE` | `ServiceUnavailableError` |
| Any other error | `InternalServerError` |
Expand Down
80 changes: 25 additions & 55 deletions client/python/examples/async_batch_usage.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import asyncio

from vortexdb import AsyncVortexDB
from vortexdb import Payload, Similarity, SearchQuery, to_dense_vectors
from vortexdb import DenseQuery, PointInput, Similarity, SearchQuery, SparseVector
from vortexdb import to_dense_vectors


async def main():
Expand All @@ -16,66 +17,35 @@ async def main():
]
vectors = to_dense_vectors(raw_vectors)

p1 = Payload.text("hello world")
p2 = Payload.image("/img/a.png")
p3 = Payload.text("foo bar")

items = [
(vectors[0], p1),
(vectors[1], p2),
(vectors[2], p3),
]

# Batch Insert
point_ids = await db.batch_insert(items=items)
print("Inserted ids:\n", point_ids)

q = SearchQuery(
vector=vectors[0],
similarity=Similarity.COSINE,
limit=3,
point_ids = await db.batch_insert(
points=[
PointInput(
dense_vector=vectors[0],
sparse_vector=SparseVector([1, 5], [1.0, 0.5]),
),
PointInput(
dense_vector=vectors[1],
sparse_vector=SparseVector([5, 9], [2.0, 1.0]),
),
PointInput(dense_vector=vectors[2]),
]
)
res = await db.search(query=q)
print("\nSingle SearchQuery:\n", res)

# List of SearchQuery
queries = [
SearchQuery(vectors[0], Similarity.HAMMING, 3),
SearchQuery(vectors[1], Similarity.EUCLIDEAN, 2),
q,
]
res = await db.batch_search(queries=queries)
print("\nBatch SearchQuery:\n", res)

# List of vectors with global Similarity and Limit
res = await db.batch_search(
queries=vectors,
similarity=Similarity.COSINE,
limit=3,
)
print("\nList of DenseVectors:\n", res)
q = SearchQuery(limit=3, query=DenseQuery(vectors[0], Similarity.COSINE))
res = await db.search(query=q)
print("Dense search:\n", res)

# List of tuple (DenseVector, Similarity) with global Limit
queries = [
(vectors[0], Similarity.COSINE),
(vectors[1], Similarity.MANHATTAN),
]
res = await db.batch_search(
queries=queries,
limit=3,
)
print("\nList of (DenseVector, Similarity):\n", res)
sparse_q = SearchQuery(limit=3, query=SparseVector([5], [1.0]))
res = await db.search(query=sparse_q)
print("\nSparse search:\n", res)

# List of tuple (DenseVector, Limit) with global Similarity
queries = [
(vectors[0], 2),
(vectors[1], 4),
SearchQuery(3, DenseQuery(vectors[0], Similarity.HAMMING)),
SearchQuery(2, DenseQuery(vectors[1], Similarity.EUCLIDEAN)),
sparse_q,
]
res = await db.batch_search(
queries=queries,
similarity=Similarity.COSINE,
)
print("\nList of (DenseVector, Limit):\n", res)
res = await db.batch_search(queries=queries)
print("\nBatch search:\n", res)

for pid in point_ids:
await db.delete(point_id=pid)
Expand Down
18 changes: 13 additions & 5 deletions client/python/examples/async_usage.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
import asyncio

from vortexdb import AsyncVortexDB, DenseVector, Payload, Similarity
from vortexdb import (
AsyncVortexDB,
DenseQuery,
DenseVector,
Payload,
SearchQuery,
Similarity,
)


async def main():
Expand All @@ -9,7 +16,7 @@ async def main():
api_key="my-secret-password",
) as db:
point_id = await db.insert(
vector=DenseVector([0.1, 0.2, 0.3]),
dense_vector=DenseVector([0.1, 0.2, 0.3]),
payload=Payload.text("hello async vortex"),
)

Expand All @@ -18,9 +25,10 @@ async def main():
print(point.pretty())

results = await db.search(
vector=DenseVector([0.1, 0.2, 0.3]),
similarity=Similarity.COSINE,
limit=5,
query=SearchQuery(
limit=5,
query=DenseQuery(DenseVector([0.1, 0.2, 0.3]), Similarity.COSINE),
)
)
print(results)

Expand Down
17 changes: 12 additions & 5 deletions client/python/examples/basic_usage.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
from vortexdb import VortexDB
from vortexdb import DenseVector, Payload, Similarity # from vortexdb.models
from vortexdb import (
DenseQuery,
DenseVector,
Payload,
SearchQuery,
Similarity,
) # from vortexdb.models


def main():
Expand All @@ -11,7 +17,7 @@ def main():

# Insert a vector
point_id = db.insert(
vector=DenseVector([0.1, 0.2, 0.3]),
dense_vector=DenseVector([0.1, 0.2, 0.3]),
payload=Payload.text("hello world"),
)

Expand All @@ -21,9 +27,10 @@ def main():

# Search
results = db.search(
vector=DenseVector([0.1, 0.2, 0.3]),
similarity=Similarity.COSINE,
limit=3,
query=SearchQuery(
limit=3,
query=DenseQuery(DenseVector([0.1, 0.2, 0.3]), Similarity.COSINE),
)
)
print("Search results:", results)

Expand Down
Loading
Loading