Hermes Python client
Async Python client for the Hermes gRPC search server.
Installation
pip install hermes-client-python
Python 3.10 or newer is required.
Quick start
import asyncio
from hermes_client_python import HermesClient
async def main():
async with HermesClient("localhost:50051") as client:
await client.create_index(
"articles",
"""
index articles {
field title: text<simple> [indexed, stored]
field body: text<simple> [indexed, stored]
}
""",
)
indexed, error_count, errors = await client.index_documents(
"articles",
[
{"title": "Hello World", "body": "First article"},
{"title": "Hermes Search", "body": "Fast retrieval"},
],
)
if error_count:
raise RuntimeError(errors)
print(f"Indexed {indexed} documents")
await client.commit("articles")
results = await client.search(
"articles",
query={"match": {"field": "title", "text": "hello"}},
fields_to_load=["title", "body"],
)
for hit in results.hits:
print(hit.address, hit.score, hit.fields)
if results.hits:
document = await client.get_document("articles", results.hits[0].address)
print(document.fields if document else "document not found")
await client.delete_index("articles")
asyncio.run(main())
The context manager calls connect() and close() automatically. For manual
lifecycle management:
client = HermesClient("localhost:50051")
await client.connect()
try:
...
finally:
await client.close()
Index management
await client.create_index("articles", schema_sdl)
names = await client.list_indexes()
info = await client.get_index_info("articles")
print(info.num_docs, info.num_segments, info.vector_stats)
await client.force_merge("articles")
await client.reorder("articles")
await client.retrain_vector_index("articles")
await client.delete_index("articles")
commit() is required before newly indexed documents become searchable.
Batch and streaming indexing
indexed, error_count, errors = await client.index_documents(
"articles",
[
{"title": "One", "tags": ["search", "rust"]},
{"title": "Two", "tags": ["python"]},
],
)
async def documents():
for number in range(10_000):
yield {"title": f"Document {number}"}
streamed, stream_errors = await client.index_documents_stream("articles", documents())
Repeated list values become repeated field entries. Flat numeric lists are
dense vectors; lists of (dimension, weight) pairs are sparse vectors.
Searching
Every search takes one query object whose single key matches a Hermes query
variant:
# Exact term
await client.search(
"articles",
query={"term": {"field": "title", "term": "hermes"}},
)
# Tokenized full-text match
await client.search(
"articles",
query={"match": {"field": "body", "text": "fast retrieval"}},
)
# Recursive boolean query
await client.search(
"articles",
query={
"boolean": {
"must": [{"match": {"field": "body", "text": "retrieval"}}],
"must_not": [{"term": {"field": "title", "term": "draft"}}],
}
},
)
# Dense vector query and optional reranking
await client.search(
"articles",
query={
"dense_vector": {
"field": "embedding",
"vector": [0.1, 0.2, 0.3],
"nprobe": 16,
}
},
reranker={"field": "embedding", "vector": [0.1, 0.2, 0.3]},
candidate_limit=20,
limit=10,
fields_to_load=["title"],
)
# Hybrid union fusion
await client.search(
"articles",
query={
"fusion": {
"method": "rrf",
"rrf_k": 60,
"queries": [
{
"query": {
"sparse_vector": {
"field": "sparse_embedding",
"indices": [1, 5],
"values": [0.8, 0.2],
}
},
"weight": 1.0,
},
{
"query": {
"dense_vector": {
"field": "embedding",
"vector": [0.1, 0.2, 0.3],
}
},
"weight": 1.0,
},
],
}
},
)
Other supported variants are phrase, binary_dense_vector, boost, range,
prefix, and all. Search results expose the full DocAddress needed by
get_document():
hit = results.hits[0]
document = await client.get_document("articles", hit.address)
Deadlines and errors
Every RPC accepts an optional timeout in seconds. A per-call value overrides
the client default:
async with HermesClient("localhost:50051", default_timeout=5.0) as client:
results = await client.search(
"articles",
query={"all": {}},
timeout=0.5,
)
await client.force_merge("articles", timeout=3600)
gRPC failures raise grpc.RpcError (normally
grpc.aio.AioRpcError). get_document() is the exception: it returns None
for NOT_FOUND.
import grpc
try:
await client.search("missing", query={"all": {}})
except grpc.RpcError as error:
if error.code() == grpc.StatusCode.NOT_FOUND:
print("index not found")
else:
raise
Development
From hermes-client-python:
uv sync --group dev --group test
uv run ruff check .
uv run ruff format --check .
uv run pytest tests/test_client_unit.py
The remaining tests are integration tests and expect a debug
target/debug/hermes-server binary. Regenerate checked-in protobuf stubs after
changing hermes-proto/hermes.proto:
uv run --group dev python generate_proto.py
License
MIT
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file hermes_client_python-1.8.127.tar.gz.
File metadata
- Download URL: hermes_client_python-1.8.127.tar.gz
- Upload date:
- Size: 21.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
uv/0.12.10 {"installer":{"name":"uv","version":"0.12.10","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
61bf1eb5f8ed4add460c0c10b9b40a2f0db0b18144427099fadc51d41b21dd3d
|
|
| MD5 |
5739e41dfd1377dae0106dd7c2b64ebf
|
|
| BLAKE2b-256 |
36a6a9e4a176391d3c122beb8caabcfef1aed2dc7ec2d9cf12f11503ac879d68
|
File details
Details for the file hermes_client_python-1.8.127-py3-none-any.whl.
File metadata
- Download URL: hermes_client_python-1.8.127-py3-none-any.whl
- Upload date:
- Size: 22.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
uv/0.12.10 {"installer":{"name":"uv","version":"0.12.10","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dc9bf9148ea5b20275cdf31fc024a968ed28d6a3a4e6e6dfe9b4c87fe68114a4
|
|
| MD5 |
0ac77d122ed9a896768f0e8b894b53c9
|
|
| BLAKE2b-256 |
55bbceec5e2c32aa966a4dae93da72566512a2b6a16def63688cfba74384b9a9
|