hermes-client-python 1.0.4__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,14 @@
1
+ """Async Python client for Hermes search server."""
2
+
3
+ from .client import HermesClient
4
+ from .types import Document, IndexInfo, SearchHit, SearchResponse
5
+
6
+ __all__ = [
7
+ "HermesClient",
8
+ "Document",
9
+ "SearchHit",
10
+ "SearchResponse",
11
+ "IndexInfo",
12
+ ]
13
+
14
+ __version__ = "1.0.2"
@@ -0,0 +1,403 @@
1
+ """Async Hermes client implementation."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from collections.abc import AsyncIterator
7
+ from typing import Any
8
+
9
+ import grpc
10
+ from grpc import aio
11
+
12
+ from . import hermes_pb2 as pb
13
+ from . import hermes_pb2_grpc as pb_grpc
14
+ from .types import Document, IndexInfo, SearchHit, SearchResponse
15
+
16
+
17
+ class HermesClient:
18
+ """Async client for Hermes search server.
19
+
20
+ Example:
21
+ async with HermesClient("localhost:50051") as client:
22
+ # Create index
23
+ await client.create_index("articles", '''
24
+ index articles {
25
+ title: text indexed stored
26
+ body: text indexed stored
27
+ }
28
+ ''')
29
+
30
+ # Index documents
31
+ await client.index_documents("articles", [
32
+ {"title": "Hello", "body": "World"},
33
+ {"title": "Foo", "body": "Bar"},
34
+ ])
35
+ await client.commit("articles")
36
+
37
+ # Search
38
+ results = await client.search("articles", term=("title", "hello"))
39
+ for hit in results.hits:
40
+ print(hit.doc_id, hit.score)
41
+ """
42
+
43
+ def __init__(self, address: str = "localhost:50051"):
44
+ """Initialize client.
45
+
46
+ Args:
47
+ address: Server address in format "host:port"
48
+ """
49
+ self.address = address
50
+ self._channel: aio.Channel | None = None
51
+ self._index_stub: pb_grpc.IndexServiceStub | None = None
52
+ self._search_stub: pb_grpc.SearchServiceStub | None = None
53
+
54
+ async def connect(self) -> None:
55
+ """Connect to the server."""
56
+ self._channel = aio.insecure_channel(self.address)
57
+ self._index_stub = pb_grpc.IndexServiceStub(self._channel)
58
+ self._search_stub = pb_grpc.SearchServiceStub(self._channel)
59
+
60
+ async def close(self) -> None:
61
+ """Close the connection."""
62
+ if self._channel:
63
+ await self._channel.close()
64
+ self._channel = None
65
+ self._index_stub = None
66
+ self._search_stub = None
67
+
68
+ async def __aenter__(self) -> HermesClient:
69
+ await self.connect()
70
+ return self
71
+
72
+ async def __aexit__(self, exc_type, exc_val, exc_tb) -> None:
73
+ await self.close()
74
+
75
+ def _ensure_connected(self) -> None:
76
+ if self._index_stub is None or self._search_stub is None:
77
+ raise RuntimeError(
78
+ "Client not connected. Use 'async with' or call connect() first."
79
+ )
80
+
81
+ # =========================================================================
82
+ # Index Management
83
+ # =========================================================================
84
+
85
+ async def create_index(self, index_name: str, schema: str) -> bool:
86
+ """Create a new index.
87
+
88
+ Args:
89
+ index_name: Name of the index
90
+ schema: Schema definition in SDL or JSON format
91
+
92
+ Returns:
93
+ True if successful
94
+
95
+ Example SDL schema:
96
+ index myindex {
97
+ title: text indexed stored
98
+ body: text indexed stored
99
+ score: f64 stored
100
+ }
101
+
102
+ Example JSON schema:
103
+ {
104
+ "fields": [
105
+ {"name": "title", "type": "text", "indexed": true, "stored": true}
106
+ ]
107
+ }
108
+ """
109
+ self._ensure_connected()
110
+ request = pb.CreateIndexRequest(index_name=index_name, schema=schema)
111
+ response = await self._index_stub.CreateIndex(request)
112
+ return response.success
113
+
114
+ async def delete_index(self, index_name: str) -> bool:
115
+ """Delete an index.
116
+
117
+ Args:
118
+ index_name: Name of the index to delete
119
+
120
+ Returns:
121
+ True if successful
122
+ """
123
+ self._ensure_connected()
124
+ request = pb.DeleteIndexRequest(index_name=index_name)
125
+ response = await self._index_stub.DeleteIndex(request)
126
+ return response.success
127
+
128
+ async def get_index_info(self, index_name: str) -> IndexInfo:
129
+ """Get information about an index.
130
+
131
+ Args:
132
+ index_name: Name of the index
133
+
134
+ Returns:
135
+ IndexInfo with document count, segments, and schema
136
+ """
137
+ self._ensure_connected()
138
+ request = pb.GetIndexInfoRequest(index_name=index_name)
139
+ response = await self._search_stub.GetIndexInfo(request)
140
+ return IndexInfo(
141
+ index_name=response.index_name,
142
+ num_docs=response.num_docs,
143
+ num_segments=response.num_segments,
144
+ schema=response.schema,
145
+ )
146
+
147
+ # =========================================================================
148
+ # Document Indexing
149
+ # =========================================================================
150
+
151
+ async def index_documents(
152
+ self, index_name: str, documents: list[dict[str, Any]]
153
+ ) -> tuple[int, int]:
154
+ """Index multiple documents in batch.
155
+
156
+ Args:
157
+ index_name: Name of the index
158
+ documents: List of documents (dicts with field names as keys)
159
+
160
+ Returns:
161
+ Tuple of (indexed_count, error_count)
162
+ """
163
+ self._ensure_connected()
164
+
165
+ named_docs = []
166
+ for doc in documents:
167
+ fields = {k: _to_field_value(v) for k, v in doc.items()}
168
+ named_docs.append(pb.NamedDocument(fields=fields))
169
+
170
+ request = pb.BatchIndexDocumentsRequest(
171
+ index_name=index_name, documents=named_docs
172
+ )
173
+ response = await self._index_stub.BatchIndexDocuments(request)
174
+ return response.indexed_count, response.error_count
175
+
176
+ async def index_document(self, index_name: str, document: dict[str, Any]) -> None:
177
+ """Index a single document.
178
+
179
+ Args:
180
+ index_name: Name of the index
181
+ document: Document as dict with field names as keys
182
+ """
183
+ await self.index_documents(index_name, [document])
184
+
185
+ async def index_documents_stream(
186
+ self, index_name: str, documents: AsyncIterator[dict[str, Any]]
187
+ ) -> int:
188
+ """Stream documents for indexing.
189
+
190
+ Args:
191
+ index_name: Name of the index
192
+ documents: Async iterator of documents
193
+
194
+ Returns:
195
+ Number of indexed documents
196
+ """
197
+ self._ensure_connected()
198
+
199
+ async def request_iterator():
200
+ async for doc in documents:
201
+ fields = {k: _to_field_value(v) for k, v in doc.items()}
202
+ yield pb.IndexDocumentRequest(index_name=index_name, fields=fields)
203
+
204
+ response = await self._index_stub.IndexDocuments(request_iterator())
205
+ return response.indexed_count
206
+
207
+ async def commit(self, index_name: str) -> int:
208
+ """Commit pending changes.
209
+
210
+ Args:
211
+ index_name: Name of the index
212
+
213
+ Returns:
214
+ Total number of documents in the index
215
+ """
216
+ self._ensure_connected()
217
+ request = pb.CommitRequest(index_name=index_name)
218
+ response = await self._index_stub.Commit(request)
219
+ return response.num_docs
220
+
221
+ async def force_merge(self, index_name: str) -> int:
222
+ """Force merge all segments.
223
+
224
+ Args:
225
+ index_name: Name of the index
226
+
227
+ Returns:
228
+ Number of segments after merge
229
+ """
230
+ self._ensure_connected()
231
+ request = pb.ForceMergeRequest(index_name=index_name)
232
+ response = await self._index_stub.ForceMerge(request)
233
+ return response.num_segments
234
+
235
+ # =========================================================================
236
+ # Search
237
+ # =========================================================================
238
+
239
+ async def search(
240
+ self,
241
+ index_name: str,
242
+ *,
243
+ term: tuple[str, str] | None = None,
244
+ boolean: dict[str, list[tuple[str, str]]] | None = None,
245
+ limit: int = 10,
246
+ offset: int = 0,
247
+ fields_to_load: list[str] | None = None,
248
+ ) -> SearchResponse:
249
+ """Search for documents.
250
+
251
+ Args:
252
+ index_name: Name of the index
253
+ term: Term query as (field, term) tuple
254
+ boolean: Boolean query with "must", "should", "must_not" keys
255
+ limit: Maximum number of results
256
+ offset: Offset for pagination
257
+ fields_to_load: List of fields to include in results
258
+
259
+ Returns:
260
+ SearchResponse with hits
261
+
262
+ Examples:
263
+ # Term query
264
+ results = await client.search("articles", term=("title", "hello"))
265
+
266
+ # Boolean query
267
+ results = await client.search("articles", boolean={
268
+ "must": [("title", "hello")],
269
+ "should": [("body", "world")],
270
+ })
271
+ """
272
+ self._ensure_connected()
273
+
274
+ query = _build_query(term=term, boolean=boolean)
275
+
276
+ request = pb.SearchRequest(
277
+ index_name=index_name,
278
+ query=query,
279
+ limit=limit,
280
+ offset=offset,
281
+ fields_to_load=fields_to_load or [],
282
+ )
283
+
284
+ response = await self._search_stub.Search(request)
285
+
286
+ hits = [
287
+ SearchHit(
288
+ doc_id=hit.doc_id,
289
+ score=hit.score,
290
+ fields={k: _from_field_value(v) for k, v in hit.fields.items()},
291
+ )
292
+ for hit in response.hits
293
+ ]
294
+
295
+ return SearchResponse(
296
+ hits=hits,
297
+ total_hits=response.total_hits,
298
+ took_ms=response.took_ms,
299
+ )
300
+
301
+ async def get_document(self, index_name: str, doc_id: int) -> Document | None:
302
+ """Get a document by ID.
303
+
304
+ Args:
305
+ index_name: Name of the index
306
+ doc_id: Document ID
307
+
308
+ Returns:
309
+ Document or None if not found
310
+ """
311
+ self._ensure_connected()
312
+ request = pb.GetDocumentRequest(index_name=index_name, doc_id=doc_id)
313
+ try:
314
+ response = await self._search_stub.GetDocument(request)
315
+ fields = {k: _from_field_value(v) for k, v in response.fields.items()}
316
+ return Document(fields=fields)
317
+ except grpc.RpcError as e:
318
+ if e.code() == grpc.StatusCode.NOT_FOUND:
319
+ return None
320
+ raise
321
+
322
+
323
+ # =============================================================================
324
+ # Helper functions
325
+ # =============================================================================
326
+
327
+
328
+ def _to_field_value(value: Any) -> pb.FieldValue:
329
+ """Convert Python value to protobuf FieldValue."""
330
+ if isinstance(value, str):
331
+ return pb.FieldValue(text=value)
332
+ elif isinstance(value, bool):
333
+ return pb.FieldValue(u64=1 if value else 0)
334
+ elif isinstance(value, int):
335
+ if value >= 0:
336
+ return pb.FieldValue(u64=value)
337
+ else:
338
+ return pb.FieldValue(i64=value)
339
+ elif isinstance(value, float):
340
+ return pb.FieldValue(f64=value)
341
+ elif isinstance(value, bytes):
342
+ return pb.FieldValue(bytes_value=value)
343
+ elif isinstance(value, (list, dict)):
344
+ # Assume JSON for complex types
345
+ return pb.FieldValue(json_value=json.dumps(value))
346
+ else:
347
+ return pb.FieldValue(text=str(value))
348
+
349
+
350
+ def _from_field_value(fv: pb.FieldValue) -> Any:
351
+ """Convert protobuf FieldValue to Python value."""
352
+ which = fv.WhichOneof("value")
353
+ if which == "text":
354
+ return fv.text
355
+ elif which == "u64":
356
+ return fv.u64
357
+ elif which == "i64":
358
+ return fv.i64
359
+ elif which == "f64":
360
+ return fv.f64
361
+ elif which == "bytes_value":
362
+ return fv.bytes_value
363
+ elif which == "json_value":
364
+ return json.loads(fv.json_value)
365
+ elif which == "sparse_vector":
366
+ return {
367
+ "indices": list(fv.sparse_vector.indices),
368
+ "values": list(fv.sparse_vector.values),
369
+ }
370
+ elif which == "dense_vector":
371
+ return list(fv.dense_vector.values)
372
+ return None
373
+
374
+
375
+ def _build_query(
376
+ *,
377
+ term: tuple[str, str] | None = None,
378
+ boolean: dict[str, list[tuple[str, str]]] | None = None,
379
+ ) -> pb.Query:
380
+ """Build a protobuf Query from parameters."""
381
+ if term is not None:
382
+ field, value = term
383
+ return pb.Query(term=pb.TermQuery(field=field, term=value))
384
+
385
+ if boolean is not None:
386
+ must = [
387
+ pb.Query(term=pb.TermQuery(field=f, term=t))
388
+ for f, t in boolean.get("must", [])
389
+ ]
390
+ should = [
391
+ pb.Query(term=pb.TermQuery(field=f, term=t))
392
+ for f, t in boolean.get("should", [])
393
+ ]
394
+ must_not = [
395
+ pb.Query(term=pb.TermQuery(field=f, term=t))
396
+ for f, t in boolean.get("must_not", [])
397
+ ]
398
+ return pb.Query(
399
+ boolean=pb.BooleanQuery(must=must, should=should, must_not=must_not)
400
+ )
401
+
402
+ # Default: match all (empty boolean query)
403
+ return pb.Query(boolean=pb.BooleanQuery())
@@ -0,0 +1,106 @@
1
+ # Generated by the protocol buffer compiler. DO NOT EDIT!
2
+ # NO CHECKED-IN PROTOBUF GENCODE
3
+ # source: hermes.proto
4
+ # Protobuf Python Version: 6.31.1
5
+ """Generated protocol buffer code."""
6
+
7
+ from google.protobuf import descriptor as _descriptor
8
+ from google.protobuf import descriptor_pool as _descriptor_pool
9
+ from google.protobuf import runtime_version as _runtime_version
10
+ from google.protobuf import symbol_database as _symbol_database
11
+ from google.protobuf.internal import builder as _builder
12
+
13
+ _runtime_version.ValidateProtobufRuntimeVersion(
14
+ _runtime_version.Domain.PUBLIC, 6, 31, 1, "", "hermes.proto"
15
+ )
16
+ # @@protoc_insertion_point(imports)
17
+
18
+ _sym_db = _symbol_database.Default()
19
+
20
+
21
+ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(
22
+ b'\n\x0chermes.proto\x12\x06hermes"\xa2\x01\n\x05Query\x12!\n\x04term\x18\x01 \x01(\x0b\x32\x11.hermes.TermQueryH\x00\x12\'\n\x07\x62oolean\x18\x02 \x01(\x0b\x32\x14.hermes.BooleanQueryH\x00\x12#\n\x05\x62oost\x18\x03 \x01(\x0b\x32\x12.hermes.BoostQueryH\x00\x12\x1f\n\x03\x61ll\x18\x04 \x01(\x0b\x32\x10.hermes.AllQueryH\x00\x42\x07\n\x05query"(\n\tTermQuery\x12\r\n\x05\x66ield\x18\x01 \x01(\t\x12\x0c\n\x04term\x18\x02 \x01(\t"k\n\x0c\x42ooleanQuery\x12\x1b\n\x04must\x18\x01 \x03(\x0b\x32\r.hermes.Query\x12\x1d\n\x06should\x18\x02 \x03(\x0b\x32\r.hermes.Query\x12\x1f\n\x08must_not\x18\x03 \x03(\x0b\x32\r.hermes.Query"9\n\nBoostQuery\x12\x1c\n\x05query\x18\x01 \x01(\x0b\x32\r.hermes.Query\x12\r\n\x05\x62oost\x18\x02 \x01(\x02"\n\n\x08\x41llQuery"x\n\rSearchRequest\x12\x12\n\nindex_name\x18\x01 \x01(\t\x12\x1c\n\x05query\x18\x02 \x01(\x0b\x32\r.hermes.Query\x12\r\n\x05limit\x18\x03 \x01(\r\x12\x0e\n\x06offset\x18\x04 \x01(\r\x12\x16\n\x0e\x66ields_to_load\x18\x05 \x03(\t"\x9c\x01\n\tSearchHit\x12\x0e\n\x06\x64oc_id\x18\x01 \x01(\r\x12\r\n\x05score\x18\x02 \x01(\x02\x12-\n\x06\x66ields\x18\x03 \x03(\x0b\x32\x1d.hermes.SearchHit.FieldsEntry\x1a\x41\n\x0b\x46ieldsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12!\n\x05value\x18\x02 \x01(\x0b\x32\x12.hermes.FieldValue:\x02\x38\x01"\xdb\x01\n\nFieldValue\x12\x0e\n\x04text\x18\x01 \x01(\tH\x00\x12\r\n\x03u64\x18\x02 \x01(\x04H\x00\x12\r\n\x03i64\x18\x03 \x01(\x03H\x00\x12\r\n\x03\x66\x36\x34\x18\x04 \x01(\x01H\x00\x12\x15\n\x0b\x62ytes_value\x18\x05 \x01(\x0cH\x00\x12-\n\rsparse_vector\x18\x06 \x01(\x0b\x32\x14.hermes.SparseVectorH\x00\x12+\n\x0c\x64\x65nse_vector\x18\x07 \x01(\x0b\x32\x13.hermes.DenseVectorH\x00\x12\x14\n\njson_value\x18\x08 \x01(\tH\x00\x42\x07\n\x05value"/\n\x0cSparseVector\x12\x0f\n\x07indices\x18\x01 \x03(\r\x12\x0e\n\x06values\x18\x02 \x03(\x02"\x1d\n\x0b\x44\x65nseVector\x12\x0e\n\x06values\x18\x01 \x03(\x02"V\n\x0eSearchResponse\x12\x1f\n\x04hits\x18\x01 \x03(\x0b\x32\x11.hermes.SearchHit\x12\x12\n\ntotal_hits\x18\x02 \x01(\r\x12\x0f\n\x07took_ms\x18\x03 \x01(\x04"8\n\x12GetDocumentRequest\x12\x12\n\nindex_name\x18\x01 \x01(\t\x12\x0e\n\x06\x64oc_id\x18\x02 \x01(\r"\x91\x01\n\x13GetDocumentResponse\x12\x37\n\x06\x66ields\x18\x01 \x03(\x0b\x32\'.hermes.GetDocumentResponse.FieldsEntry\x1a\x41\n\x0b\x46ieldsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12!\n\x05value\x18\x02 \x01(\x0b\x32\x12.hermes.FieldValue:\x02\x38\x01")\n\x13GetIndexInfoRequest\x12\x12\n\nindex_name\x18\x01 \x01(\t"b\n\x14GetIndexInfoResponse\x12\x12\n\nindex_name\x18\x01 \x01(\t\x12\x10\n\x08num_docs\x18\x02 \x01(\r\x12\x14\n\x0cnum_segments\x18\x03 \x01(\r\x12\x0e\n\x06schema\x18\x04 \x01(\t"8\n\x12\x43reateIndexRequest\x12\x12\n\nindex_name\x18\x01 \x01(\t\x12\x0e\n\x06schema\x18\x02 \x01(\t"&\n\x13\x43reateIndexResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08"\x85\x01\n\rNamedDocument\x12\x31\n\x06\x66ields\x18\x01 \x03(\x0b\x32!.hermes.NamedDocument.FieldsEntry\x1a\x41\n\x0b\x46ieldsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12!\n\x05value\x18\x02 \x01(\x0b\x32\x12.hermes.FieldValue:\x02\x38\x01"Z\n\x1a\x42\x61tchIndexDocumentsRequest\x12\x12\n\nindex_name\x18\x01 \x01(\t\x12(\n\tdocuments\x18\x02 \x03(\x0b\x32\x15.hermes.NamedDocument"I\n\x1b\x42\x61tchIndexDocumentsResponse\x12\x15\n\rindexed_count\x18\x01 \x01(\r\x12\x13\n\x0b\x65rror_count\x18\x02 \x01(\r"\xa7\x01\n\x14IndexDocumentRequest\x12\x12\n\nindex_name\x18\x01 \x01(\t\x12\x38\n\x06\x66ields\x18\x02 \x03(\x0b\x32(.hermes.IndexDocumentRequest.FieldsEntry\x1a\x41\n\x0b\x46ieldsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12!\n\x05value\x18\x02 \x01(\x0b\x32\x12.hermes.FieldValue:\x02\x38\x01"/\n\x16IndexDocumentsResponse\x12\x15\n\rindexed_count\x18\x01 \x01(\r"#\n\rCommitRequest\x12\x12\n\nindex_name\x18\x01 \x01(\t"3\n\x0e\x43ommitResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x10\n\x08num_docs\x18\x02 \x01(\r"\'\n\x11\x46orceMergeRequest\x12\x12\n\nindex_name\x18\x01 \x01(\t";\n\x12\x46orceMergeResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x14\n\x0cnum_segments\x18\x02 \x01(\r"(\n\x12\x44\x65leteIndexRequest\x12\x12\n\nindex_name\x18\x01 \x01(\t"&\n\x13\x44\x65leteIndexResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x32\xdb\x01\n\rSearchService\x12\x37\n\x06Search\x12\x15.hermes.SearchRequest\x1a\x16.hermes.SearchResponse\x12\x46\n\x0bGetDocument\x12\x1a.hermes.GetDocumentRequest\x1a\x1b.hermes.GetDocumentResponse\x12I\n\x0cGetIndexInfo\x12\x1b.hermes.GetIndexInfoRequest\x1a\x1c.hermes.GetIndexInfoResponse2\xce\x03\n\x0cIndexService\x12\x46\n\x0b\x43reateIndex\x12\x1a.hermes.CreateIndexRequest\x1a\x1b.hermes.CreateIndexResponse\x12P\n\x0eIndexDocuments\x12\x1c.hermes.IndexDocumentRequest\x1a\x1e.hermes.IndexDocumentsResponse(\x01\x12^\n\x13\x42\x61tchIndexDocuments\x12".hermes.BatchIndexDocumentsRequest\x1a#.hermes.BatchIndexDocumentsResponse\x12\x37\n\x06\x43ommit\x12\x15.hermes.CommitRequest\x1a\x16.hermes.CommitResponse\x12\x43\n\nForceMerge\x12\x19.hermes.ForceMergeRequest\x1a\x1a.hermes.ForceMergeResponse\x12\x46\n\x0b\x44\x65leteIndex\x12\x1a.hermes.DeleteIndexRequest\x1a\x1b.hermes.DeleteIndexResponseb\x06proto3'
23
+ )
24
+
25
+ _globals = globals()
26
+ _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
27
+ _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "hermes_pb2", _globals)
28
+ if not _descriptor._USE_C_DESCRIPTORS:
29
+ DESCRIPTOR._loaded_options = None
30
+ _globals["_SEARCHHIT_FIELDSENTRY"]._loaded_options = None
31
+ _globals["_SEARCHHIT_FIELDSENTRY"]._serialized_options = b"8\001"
32
+ _globals["_GETDOCUMENTRESPONSE_FIELDSENTRY"]._loaded_options = None
33
+ _globals["_GETDOCUMENTRESPONSE_FIELDSENTRY"]._serialized_options = b"8\001"
34
+ _globals["_NAMEDDOCUMENT_FIELDSENTRY"]._loaded_options = None
35
+ _globals["_NAMEDDOCUMENT_FIELDSENTRY"]._serialized_options = b"8\001"
36
+ _globals["_INDEXDOCUMENTREQUEST_FIELDSENTRY"]._loaded_options = None
37
+ _globals["_INDEXDOCUMENTREQUEST_FIELDSENTRY"]._serialized_options = b"8\001"
38
+ _globals["_QUERY"]._serialized_start = 25
39
+ _globals["_QUERY"]._serialized_end = 187
40
+ _globals["_TERMQUERY"]._serialized_start = 189
41
+ _globals["_TERMQUERY"]._serialized_end = 229
42
+ _globals["_BOOLEANQUERY"]._serialized_start = 231
43
+ _globals["_BOOLEANQUERY"]._serialized_end = 338
44
+ _globals["_BOOSTQUERY"]._serialized_start = 340
45
+ _globals["_BOOSTQUERY"]._serialized_end = 397
46
+ _globals["_ALLQUERY"]._serialized_start = 399
47
+ _globals["_ALLQUERY"]._serialized_end = 409
48
+ _globals["_SEARCHREQUEST"]._serialized_start = 411
49
+ _globals["_SEARCHREQUEST"]._serialized_end = 531
50
+ _globals["_SEARCHHIT"]._serialized_start = 534
51
+ _globals["_SEARCHHIT"]._serialized_end = 690
52
+ _globals["_SEARCHHIT_FIELDSENTRY"]._serialized_start = 625
53
+ _globals["_SEARCHHIT_FIELDSENTRY"]._serialized_end = 690
54
+ _globals["_FIELDVALUE"]._serialized_start = 693
55
+ _globals["_FIELDVALUE"]._serialized_end = 912
56
+ _globals["_SPARSEVECTOR"]._serialized_start = 914
57
+ _globals["_SPARSEVECTOR"]._serialized_end = 961
58
+ _globals["_DENSEVECTOR"]._serialized_start = 963
59
+ _globals["_DENSEVECTOR"]._serialized_end = 992
60
+ _globals["_SEARCHRESPONSE"]._serialized_start = 994
61
+ _globals["_SEARCHRESPONSE"]._serialized_end = 1080
62
+ _globals["_GETDOCUMENTREQUEST"]._serialized_start = 1082
63
+ _globals["_GETDOCUMENTREQUEST"]._serialized_end = 1138
64
+ _globals["_GETDOCUMENTRESPONSE"]._serialized_start = 1141
65
+ _globals["_GETDOCUMENTRESPONSE"]._serialized_end = 1286
66
+ _globals["_GETDOCUMENTRESPONSE_FIELDSENTRY"]._serialized_start = 625
67
+ _globals["_GETDOCUMENTRESPONSE_FIELDSENTRY"]._serialized_end = 690
68
+ _globals["_GETINDEXINFOREQUEST"]._serialized_start = 1288
69
+ _globals["_GETINDEXINFOREQUEST"]._serialized_end = 1329
70
+ _globals["_GETINDEXINFORESPONSE"]._serialized_start = 1331
71
+ _globals["_GETINDEXINFORESPONSE"]._serialized_end = 1429
72
+ _globals["_CREATEINDEXREQUEST"]._serialized_start = 1431
73
+ _globals["_CREATEINDEXREQUEST"]._serialized_end = 1487
74
+ _globals["_CREATEINDEXRESPONSE"]._serialized_start = 1489
75
+ _globals["_CREATEINDEXRESPONSE"]._serialized_end = 1527
76
+ _globals["_NAMEDDOCUMENT"]._serialized_start = 1530
77
+ _globals["_NAMEDDOCUMENT"]._serialized_end = 1663
78
+ _globals["_NAMEDDOCUMENT_FIELDSENTRY"]._serialized_start = 625
79
+ _globals["_NAMEDDOCUMENT_FIELDSENTRY"]._serialized_end = 690
80
+ _globals["_BATCHINDEXDOCUMENTSREQUEST"]._serialized_start = 1665
81
+ _globals["_BATCHINDEXDOCUMENTSREQUEST"]._serialized_end = 1755
82
+ _globals["_BATCHINDEXDOCUMENTSRESPONSE"]._serialized_start = 1757
83
+ _globals["_BATCHINDEXDOCUMENTSRESPONSE"]._serialized_end = 1830
84
+ _globals["_INDEXDOCUMENTREQUEST"]._serialized_start = 1833
85
+ _globals["_INDEXDOCUMENTREQUEST"]._serialized_end = 2000
86
+ _globals["_INDEXDOCUMENTREQUEST_FIELDSENTRY"]._serialized_start = 625
87
+ _globals["_INDEXDOCUMENTREQUEST_FIELDSENTRY"]._serialized_end = 690
88
+ _globals["_INDEXDOCUMENTSRESPONSE"]._serialized_start = 2002
89
+ _globals["_INDEXDOCUMENTSRESPONSE"]._serialized_end = 2049
90
+ _globals["_COMMITREQUEST"]._serialized_start = 2051
91
+ _globals["_COMMITREQUEST"]._serialized_end = 2086
92
+ _globals["_COMMITRESPONSE"]._serialized_start = 2088
93
+ _globals["_COMMITRESPONSE"]._serialized_end = 2139
94
+ _globals["_FORCEMERGEREQUEST"]._serialized_start = 2141
95
+ _globals["_FORCEMERGEREQUEST"]._serialized_end = 2180
96
+ _globals["_FORCEMERGERESPONSE"]._serialized_start = 2182
97
+ _globals["_FORCEMERGERESPONSE"]._serialized_end = 2241
98
+ _globals["_DELETEINDEXREQUEST"]._serialized_start = 2243
99
+ _globals["_DELETEINDEXREQUEST"]._serialized_end = 2283
100
+ _globals["_DELETEINDEXRESPONSE"]._serialized_start = 2285
101
+ _globals["_DELETEINDEXRESPONSE"]._serialized_end = 2323
102
+ _globals["_SEARCHSERVICE"]._serialized_start = 2326
103
+ _globals["_SEARCHSERVICE"]._serialized_end = 2545
104
+ _globals["_INDEXSERVICE"]._serialized_start = 2548
105
+ _globals["_INDEXSERVICE"]._serialized_end = 3010
106
+ # @@protoc_insertion_point(module_scope)
@@ -0,0 +1,511 @@
1
+ # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
2
+ """Client and server classes corresponding to protobuf-defined services."""
3
+
4
+ import grpc
5
+
6
+ from . import hermes_pb2 as hermes__pb2
7
+
8
+ GRPC_GENERATED_VERSION = "1.76.0"
9
+ GRPC_VERSION = grpc.__version__
10
+ _version_not_supported = False
11
+
12
+ try:
13
+ from grpc._utilities import first_version_is_lower
14
+
15
+ _version_not_supported = first_version_is_lower(
16
+ GRPC_VERSION, GRPC_GENERATED_VERSION
17
+ )
18
+ except ImportError:
19
+ _version_not_supported = True
20
+
21
+ if _version_not_supported:
22
+ raise RuntimeError(
23
+ f"The grpc package installed is at version {GRPC_VERSION},"
24
+ + " but the generated code in hermes_pb2_grpc.py depends on"
25
+ + f" grpcio>={GRPC_GENERATED_VERSION}."
26
+ + f" Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}"
27
+ + f" or downgrade your generated code using grpcio-tools<={GRPC_VERSION}."
28
+ )
29
+
30
+
31
+ class SearchServiceStub:
32
+ """Search service"""
33
+
34
+ def __init__(self, channel):
35
+ """Constructor.
36
+
37
+ Args:
38
+ channel: A grpc.Channel.
39
+ """
40
+ self.Search = channel.unary_unary(
41
+ "/hermes.SearchService/Search",
42
+ request_serializer=hermes__pb2.SearchRequest.SerializeToString,
43
+ response_deserializer=hermes__pb2.SearchResponse.FromString,
44
+ _registered_method=True,
45
+ )
46
+ self.GetDocument = channel.unary_unary(
47
+ "/hermes.SearchService/GetDocument",
48
+ request_serializer=hermes__pb2.GetDocumentRequest.SerializeToString,
49
+ response_deserializer=hermes__pb2.GetDocumentResponse.FromString,
50
+ _registered_method=True,
51
+ )
52
+ self.GetIndexInfo = channel.unary_unary(
53
+ "/hermes.SearchService/GetIndexInfo",
54
+ request_serializer=hermes__pb2.GetIndexInfoRequest.SerializeToString,
55
+ response_deserializer=hermes__pb2.GetIndexInfoResponse.FromString,
56
+ _registered_method=True,
57
+ )
58
+
59
+
60
+ class SearchServiceServicer:
61
+ """Search service"""
62
+
63
+ def Search(self, request, context):
64
+ """Search for documents"""
65
+ context.set_code(grpc.StatusCode.UNIMPLEMENTED)
66
+ context.set_details("Method not implemented!")
67
+ raise NotImplementedError("Method not implemented!")
68
+
69
+ def GetDocument(self, request, context):
70
+ """Get document by ID"""
71
+ context.set_code(grpc.StatusCode.UNIMPLEMENTED)
72
+ context.set_details("Method not implemented!")
73
+ raise NotImplementedError("Method not implemented!")
74
+
75
+ def GetIndexInfo(self, request, context):
76
+ """Get index info"""
77
+ context.set_code(grpc.StatusCode.UNIMPLEMENTED)
78
+ context.set_details("Method not implemented!")
79
+ raise NotImplementedError("Method not implemented!")
80
+
81
+
82
+ def add_SearchServiceServicer_to_server(servicer, server):
83
+ rpc_method_handlers = {
84
+ "Search": grpc.unary_unary_rpc_method_handler(
85
+ servicer.Search,
86
+ request_deserializer=hermes__pb2.SearchRequest.FromString,
87
+ response_serializer=hermes__pb2.SearchResponse.SerializeToString,
88
+ ),
89
+ "GetDocument": grpc.unary_unary_rpc_method_handler(
90
+ servicer.GetDocument,
91
+ request_deserializer=hermes__pb2.GetDocumentRequest.FromString,
92
+ response_serializer=hermes__pb2.GetDocumentResponse.SerializeToString,
93
+ ),
94
+ "GetIndexInfo": grpc.unary_unary_rpc_method_handler(
95
+ servicer.GetIndexInfo,
96
+ request_deserializer=hermes__pb2.GetIndexInfoRequest.FromString,
97
+ response_serializer=hermes__pb2.GetIndexInfoResponse.SerializeToString,
98
+ ),
99
+ }
100
+ generic_handler = grpc.method_handlers_generic_handler(
101
+ "hermes.SearchService", rpc_method_handlers
102
+ )
103
+ server.add_generic_rpc_handlers((generic_handler,))
104
+ server.add_registered_method_handlers("hermes.SearchService", rpc_method_handlers)
105
+
106
+
107
+ # This class is part of an EXPERIMENTAL API.
108
+ class SearchService:
109
+ """Search service"""
110
+
111
+ @staticmethod
112
+ def Search(
113
+ request,
114
+ target,
115
+ options=(),
116
+ channel_credentials=None,
117
+ call_credentials=None,
118
+ insecure=False,
119
+ compression=None,
120
+ wait_for_ready=None,
121
+ timeout=None,
122
+ metadata=None,
123
+ ):
124
+ return grpc.experimental.unary_unary(
125
+ request,
126
+ target,
127
+ "/hermes.SearchService/Search",
128
+ hermes__pb2.SearchRequest.SerializeToString,
129
+ hermes__pb2.SearchResponse.FromString,
130
+ options,
131
+ channel_credentials,
132
+ insecure,
133
+ call_credentials,
134
+ compression,
135
+ wait_for_ready,
136
+ timeout,
137
+ metadata,
138
+ _registered_method=True,
139
+ )
140
+
141
+ @staticmethod
142
+ def GetDocument(
143
+ request,
144
+ target,
145
+ options=(),
146
+ channel_credentials=None,
147
+ call_credentials=None,
148
+ insecure=False,
149
+ compression=None,
150
+ wait_for_ready=None,
151
+ timeout=None,
152
+ metadata=None,
153
+ ):
154
+ return grpc.experimental.unary_unary(
155
+ request,
156
+ target,
157
+ "/hermes.SearchService/GetDocument",
158
+ hermes__pb2.GetDocumentRequest.SerializeToString,
159
+ hermes__pb2.GetDocumentResponse.FromString,
160
+ options,
161
+ channel_credentials,
162
+ insecure,
163
+ call_credentials,
164
+ compression,
165
+ wait_for_ready,
166
+ timeout,
167
+ metadata,
168
+ _registered_method=True,
169
+ )
170
+
171
+ @staticmethod
172
+ def GetIndexInfo(
173
+ request,
174
+ target,
175
+ options=(),
176
+ channel_credentials=None,
177
+ call_credentials=None,
178
+ insecure=False,
179
+ compression=None,
180
+ wait_for_ready=None,
181
+ timeout=None,
182
+ metadata=None,
183
+ ):
184
+ return grpc.experimental.unary_unary(
185
+ request,
186
+ target,
187
+ "/hermes.SearchService/GetIndexInfo",
188
+ hermes__pb2.GetIndexInfoRequest.SerializeToString,
189
+ hermes__pb2.GetIndexInfoResponse.FromString,
190
+ options,
191
+ channel_credentials,
192
+ insecure,
193
+ call_credentials,
194
+ compression,
195
+ wait_for_ready,
196
+ timeout,
197
+ metadata,
198
+ _registered_method=True,
199
+ )
200
+
201
+
202
+ class IndexServiceStub:
203
+ """Index service"""
204
+
205
+ def __init__(self, channel):
206
+ """Constructor.
207
+
208
+ Args:
209
+ channel: A grpc.Channel.
210
+ """
211
+ self.CreateIndex = channel.unary_unary(
212
+ "/hermes.IndexService/CreateIndex",
213
+ request_serializer=hermes__pb2.CreateIndexRequest.SerializeToString,
214
+ response_deserializer=hermes__pb2.CreateIndexResponse.FromString,
215
+ _registered_method=True,
216
+ )
217
+ self.IndexDocuments = channel.stream_unary(
218
+ "/hermes.IndexService/IndexDocuments",
219
+ request_serializer=hermes__pb2.IndexDocumentRequest.SerializeToString,
220
+ response_deserializer=hermes__pb2.IndexDocumentsResponse.FromString,
221
+ _registered_method=True,
222
+ )
223
+ self.BatchIndexDocuments = channel.unary_unary(
224
+ "/hermes.IndexService/BatchIndexDocuments",
225
+ request_serializer=hermes__pb2.BatchIndexDocumentsRequest.SerializeToString,
226
+ response_deserializer=hermes__pb2.BatchIndexDocumentsResponse.FromString,
227
+ _registered_method=True,
228
+ )
229
+ self.Commit = channel.unary_unary(
230
+ "/hermes.IndexService/Commit",
231
+ request_serializer=hermes__pb2.CommitRequest.SerializeToString,
232
+ response_deserializer=hermes__pb2.CommitResponse.FromString,
233
+ _registered_method=True,
234
+ )
235
+ self.ForceMerge = channel.unary_unary(
236
+ "/hermes.IndexService/ForceMerge",
237
+ request_serializer=hermes__pb2.ForceMergeRequest.SerializeToString,
238
+ response_deserializer=hermes__pb2.ForceMergeResponse.FromString,
239
+ _registered_method=True,
240
+ )
241
+ self.DeleteIndex = channel.unary_unary(
242
+ "/hermes.IndexService/DeleteIndex",
243
+ request_serializer=hermes__pb2.DeleteIndexRequest.SerializeToString,
244
+ response_deserializer=hermes__pb2.DeleteIndexResponse.FromString,
245
+ _registered_method=True,
246
+ )
247
+
248
+
249
+ class IndexServiceServicer:
250
+ """Index service"""
251
+
252
+ def CreateIndex(self, request, context):
253
+ """Create a new index (supports both structured schema and SDL string)"""
254
+ context.set_code(grpc.StatusCode.UNIMPLEMENTED)
255
+ context.set_details("Method not implemented!")
256
+ raise NotImplementedError("Method not implemented!")
257
+
258
+ def IndexDocuments(self, request_iterator, context):
259
+ """Add documents to index (streaming)"""
260
+ context.set_code(grpc.StatusCode.UNIMPLEMENTED)
261
+ context.set_details("Method not implemented!")
262
+ raise NotImplementedError("Method not implemented!")
263
+
264
+ def BatchIndexDocuments(self, request, context):
265
+ """Add documents in batch"""
266
+ context.set_code(grpc.StatusCode.UNIMPLEMENTED)
267
+ context.set_details("Method not implemented!")
268
+ raise NotImplementedError("Method not implemented!")
269
+
270
+ def Commit(self, request, context):
271
+ """Commit pending changes"""
272
+ context.set_code(grpc.StatusCode.UNIMPLEMENTED)
273
+ context.set_details("Method not implemented!")
274
+ raise NotImplementedError("Method not implemented!")
275
+
276
+ def ForceMerge(self, request, context):
277
+ """Force merge segments"""
278
+ context.set_code(grpc.StatusCode.UNIMPLEMENTED)
279
+ context.set_details("Method not implemented!")
280
+ raise NotImplementedError("Method not implemented!")
281
+
282
+ def DeleteIndex(self, request, context):
283
+ """Delete an index"""
284
+ context.set_code(grpc.StatusCode.UNIMPLEMENTED)
285
+ context.set_details("Method not implemented!")
286
+ raise NotImplementedError("Method not implemented!")
287
+
288
+
289
+ def add_IndexServiceServicer_to_server(servicer, server):
290
+ rpc_method_handlers = {
291
+ "CreateIndex": grpc.unary_unary_rpc_method_handler(
292
+ servicer.CreateIndex,
293
+ request_deserializer=hermes__pb2.CreateIndexRequest.FromString,
294
+ response_serializer=hermes__pb2.CreateIndexResponse.SerializeToString,
295
+ ),
296
+ "IndexDocuments": grpc.stream_unary_rpc_method_handler(
297
+ servicer.IndexDocuments,
298
+ request_deserializer=hermes__pb2.IndexDocumentRequest.FromString,
299
+ response_serializer=hermes__pb2.IndexDocumentsResponse.SerializeToString,
300
+ ),
301
+ "BatchIndexDocuments": grpc.unary_unary_rpc_method_handler(
302
+ servicer.BatchIndexDocuments,
303
+ request_deserializer=hermes__pb2.BatchIndexDocumentsRequest.FromString,
304
+ response_serializer=hermes__pb2.BatchIndexDocumentsResponse.SerializeToString,
305
+ ),
306
+ "Commit": grpc.unary_unary_rpc_method_handler(
307
+ servicer.Commit,
308
+ request_deserializer=hermes__pb2.CommitRequest.FromString,
309
+ response_serializer=hermes__pb2.CommitResponse.SerializeToString,
310
+ ),
311
+ "ForceMerge": grpc.unary_unary_rpc_method_handler(
312
+ servicer.ForceMerge,
313
+ request_deserializer=hermes__pb2.ForceMergeRequest.FromString,
314
+ response_serializer=hermes__pb2.ForceMergeResponse.SerializeToString,
315
+ ),
316
+ "DeleteIndex": grpc.unary_unary_rpc_method_handler(
317
+ servicer.DeleteIndex,
318
+ request_deserializer=hermes__pb2.DeleteIndexRequest.FromString,
319
+ response_serializer=hermes__pb2.DeleteIndexResponse.SerializeToString,
320
+ ),
321
+ }
322
+ generic_handler = grpc.method_handlers_generic_handler(
323
+ "hermes.IndexService", rpc_method_handlers
324
+ )
325
+ server.add_generic_rpc_handlers((generic_handler,))
326
+ server.add_registered_method_handlers("hermes.IndexService", rpc_method_handlers)
327
+
328
+
329
+ # This class is part of an EXPERIMENTAL API.
330
+ class IndexService:
331
+ """Index service"""
332
+
333
+ @staticmethod
334
+ def CreateIndex(
335
+ request,
336
+ target,
337
+ options=(),
338
+ channel_credentials=None,
339
+ call_credentials=None,
340
+ insecure=False,
341
+ compression=None,
342
+ wait_for_ready=None,
343
+ timeout=None,
344
+ metadata=None,
345
+ ):
346
+ return grpc.experimental.unary_unary(
347
+ request,
348
+ target,
349
+ "/hermes.IndexService/CreateIndex",
350
+ hermes__pb2.CreateIndexRequest.SerializeToString,
351
+ hermes__pb2.CreateIndexResponse.FromString,
352
+ options,
353
+ channel_credentials,
354
+ insecure,
355
+ call_credentials,
356
+ compression,
357
+ wait_for_ready,
358
+ timeout,
359
+ metadata,
360
+ _registered_method=True,
361
+ )
362
+
363
+ @staticmethod
364
+ def IndexDocuments(
365
+ request_iterator,
366
+ target,
367
+ options=(),
368
+ channel_credentials=None,
369
+ call_credentials=None,
370
+ insecure=False,
371
+ compression=None,
372
+ wait_for_ready=None,
373
+ timeout=None,
374
+ metadata=None,
375
+ ):
376
+ return grpc.experimental.stream_unary(
377
+ request_iterator,
378
+ target,
379
+ "/hermes.IndexService/IndexDocuments",
380
+ hermes__pb2.IndexDocumentRequest.SerializeToString,
381
+ hermes__pb2.IndexDocumentsResponse.FromString,
382
+ options,
383
+ channel_credentials,
384
+ insecure,
385
+ call_credentials,
386
+ compression,
387
+ wait_for_ready,
388
+ timeout,
389
+ metadata,
390
+ _registered_method=True,
391
+ )
392
+
393
+ @staticmethod
394
+ def BatchIndexDocuments(
395
+ request,
396
+ target,
397
+ options=(),
398
+ channel_credentials=None,
399
+ call_credentials=None,
400
+ insecure=False,
401
+ compression=None,
402
+ wait_for_ready=None,
403
+ timeout=None,
404
+ metadata=None,
405
+ ):
406
+ return grpc.experimental.unary_unary(
407
+ request,
408
+ target,
409
+ "/hermes.IndexService/BatchIndexDocuments",
410
+ hermes__pb2.BatchIndexDocumentsRequest.SerializeToString,
411
+ hermes__pb2.BatchIndexDocumentsResponse.FromString,
412
+ options,
413
+ channel_credentials,
414
+ insecure,
415
+ call_credentials,
416
+ compression,
417
+ wait_for_ready,
418
+ timeout,
419
+ metadata,
420
+ _registered_method=True,
421
+ )
422
+
423
+ @staticmethod
424
+ def Commit(
425
+ request,
426
+ target,
427
+ options=(),
428
+ channel_credentials=None,
429
+ call_credentials=None,
430
+ insecure=False,
431
+ compression=None,
432
+ wait_for_ready=None,
433
+ timeout=None,
434
+ metadata=None,
435
+ ):
436
+ return grpc.experimental.unary_unary(
437
+ request,
438
+ target,
439
+ "/hermes.IndexService/Commit",
440
+ hermes__pb2.CommitRequest.SerializeToString,
441
+ hermes__pb2.CommitResponse.FromString,
442
+ options,
443
+ channel_credentials,
444
+ insecure,
445
+ call_credentials,
446
+ compression,
447
+ wait_for_ready,
448
+ timeout,
449
+ metadata,
450
+ _registered_method=True,
451
+ )
452
+
453
+ @staticmethod
454
+ def ForceMerge(
455
+ request,
456
+ target,
457
+ options=(),
458
+ channel_credentials=None,
459
+ call_credentials=None,
460
+ insecure=False,
461
+ compression=None,
462
+ wait_for_ready=None,
463
+ timeout=None,
464
+ metadata=None,
465
+ ):
466
+ return grpc.experimental.unary_unary(
467
+ request,
468
+ target,
469
+ "/hermes.IndexService/ForceMerge",
470
+ hermes__pb2.ForceMergeRequest.SerializeToString,
471
+ hermes__pb2.ForceMergeResponse.FromString,
472
+ options,
473
+ channel_credentials,
474
+ insecure,
475
+ call_credentials,
476
+ compression,
477
+ wait_for_ready,
478
+ timeout,
479
+ metadata,
480
+ _registered_method=True,
481
+ )
482
+
483
+ @staticmethod
484
+ def DeleteIndex(
485
+ request,
486
+ target,
487
+ options=(),
488
+ channel_credentials=None,
489
+ call_credentials=None,
490
+ insecure=False,
491
+ compression=None,
492
+ wait_for_ready=None,
493
+ timeout=None,
494
+ metadata=None,
495
+ ):
496
+ return grpc.experimental.unary_unary(
497
+ request,
498
+ target,
499
+ "/hermes.IndexService/DeleteIndex",
500
+ hermes__pb2.DeleteIndexRequest.SerializeToString,
501
+ hermes__pb2.DeleteIndexResponse.FromString,
502
+ options,
503
+ channel_credentials,
504
+ insecure,
505
+ call_credentials,
506
+ compression,
507
+ wait_for_ready,
508
+ timeout,
509
+ metadata,
510
+ _registered_method=True,
511
+ )
@@ -0,0 +1,48 @@
1
+ """Type definitions for Hermes client."""
2
+
3
+ from dataclasses import dataclass, field
4
+ from typing import Any
5
+
6
+
7
+ @dataclass
8
+ class Document:
9
+ """A document with field values."""
10
+
11
+ fields: dict[str, Any] = field(default_factory=dict)
12
+
13
+ def __getitem__(self, key: str) -> Any:
14
+ return self.fields[key]
15
+
16
+ def __setitem__(self, key: str, value: Any) -> None:
17
+ self.fields[key] = value
18
+
19
+ def get(self, key: str, default: Any = None) -> Any:
20
+ return self.fields.get(key, default)
21
+
22
+
23
+ @dataclass
24
+ class SearchHit:
25
+ """A single search result."""
26
+
27
+ doc_id: int
28
+ score: float
29
+ fields: dict[str, Any] = field(default_factory=dict)
30
+
31
+
32
+ @dataclass
33
+ class SearchResponse:
34
+ """Search response with hits and metadata."""
35
+
36
+ hits: list[SearchHit]
37
+ total_hits: int
38
+ took_ms: int
39
+
40
+
41
+ @dataclass
42
+ class IndexInfo:
43
+ """Information about an index."""
44
+
45
+ index_name: str
46
+ num_docs: int
47
+ num_segments: int
48
+ schema: str
@@ -0,0 +1,232 @@
1
+ Metadata-Version: 2.4
2
+ Name: hermes-client-python
3
+ Version: 1.0.4
4
+ Summary: Async Python client for Hermes search server
5
+ Project-URL: Homepage, https://github.com/SpaceFrontiers/hermes
6
+ Project-URL: Repository, https://github.com/SpaceFrontiers/hermes
7
+ Author: izihawa
8
+ License-Expression: MIT
9
+ Keywords: async,full-text-search,grpc,search
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Topic :: Database :: Database Engines/Servers
19
+ Classifier: Topic :: Text Processing :: Indexing
20
+ Requires-Python: >=3.10
21
+ Requires-Dist: grpcio>=1.76.0
22
+ Requires-Dist: protobuf>=6.33.4
23
+ Description-Content-Type: text/markdown
24
+
25
+ # Hermes Client
26
+
27
+ Async Python client for [Hermes](https://github.com/SpaceFrontiers/hermes) search server.
28
+
29
+ ## Installation
30
+
31
+ ```bash
32
+ pip install hermes-client-python
33
+ ```
34
+
35
+ ## Quick Start
36
+
37
+ ```python
38
+ import asyncio
39
+ from hermes_client_python import HermesClient
40
+
41
+ async def main():
42
+ async with HermesClient("localhost:50051") as client:
43
+ # Create index with SDL schema
44
+ await client.create_index("articles", '''
45
+ index articles {
46
+ title: text indexed stored
47
+ body: text indexed stored
48
+ score: f64 stored
49
+ }
50
+ ''')
51
+
52
+ # Index documents
53
+ await client.index_documents("articles", [
54
+ {"title": "Hello World", "body": "First article", "score": 1.5},
55
+ {"title": "Goodbye World", "body": "Last article", "score": 2.0},
56
+ ])
57
+
58
+ # Commit changes
59
+ await client.commit("articles")
60
+
61
+ # Search
62
+ results = await client.search("articles", term=("title", "hello"), limit=10)
63
+ for hit in results.hits:
64
+ print(f"Doc {hit.doc_id}: score={hit.score}, fields={hit.fields}")
65
+
66
+ # Get document by ID
67
+ doc = await client.get_document("articles", 0)
68
+ print(doc.fields)
69
+
70
+ # Delete index
71
+ await client.delete_index("articles")
72
+
73
+ asyncio.run(main())
74
+ ```
75
+
76
+ ## API Reference
77
+
78
+ ### HermesClient
79
+
80
+ ```python
81
+ client = HermesClient(address="localhost:50051")
82
+ ```
83
+
84
+ #### Connection
85
+
86
+ ```python
87
+ # Using context manager (recommended)
88
+ async with HermesClient("localhost:50051") as client:
89
+ ...
90
+
91
+ # Manual connection
92
+ client = HermesClient("localhost:50051")
93
+ await client.connect()
94
+ # ... use client ...
95
+ await client.close()
96
+ ```
97
+
98
+ #### Index Management
99
+
100
+ ```python
101
+ # Create index with SDL schema
102
+ await client.create_index("myindex", '''
103
+ index myindex {
104
+ title: text indexed stored
105
+ body: text indexed stored
106
+ }
107
+ ''')
108
+
109
+ # Create index with JSON schema
110
+ await client.create_index("myindex", '''
111
+ {
112
+ "fields": [
113
+ {"name": "title", "type": "text", "indexed": true, "stored": true},
114
+ {"name": "body", "type": "text", "indexed": true, "stored": true}
115
+ ]
116
+ }
117
+ ''')
118
+
119
+ # Get index info
120
+ info = await client.get_index_info("myindex")
121
+ print(f"Documents: {info.num_docs}, Segments: {info.num_segments}")
122
+
123
+ # Delete index
124
+ await client.delete_index("myindex")
125
+ ```
126
+
127
+ #### Document Indexing
128
+
129
+ ```python
130
+ # Index multiple documents (batch)
131
+ indexed, errors = await client.index_documents("myindex", [
132
+ {"title": "Doc 1", "body": "Content 1"},
133
+ {"title": "Doc 2", "body": "Content 2"},
134
+ ])
135
+
136
+ # Index single document
137
+ await client.index_document("myindex", {"title": "Doc", "body": "Content"})
138
+
139
+ # Stream documents (for large datasets)
140
+ async def doc_generator():
141
+ for i in range(10000):
142
+ yield {"title": f"Doc {i}", "body": f"Content {i}"}
143
+
144
+ count = await client.index_documents_stream("myindex", doc_generator())
145
+
146
+ # Commit changes (required to make documents searchable)
147
+ num_docs = await client.commit("myindex")
148
+
149
+ # Force merge segments (for optimization)
150
+ num_segments = await client.force_merge("myindex")
151
+ ```
152
+
153
+ #### Searching
154
+
155
+ ```python
156
+ # Term query
157
+ results = await client.search("myindex", term=("title", "hello"), limit=10)
158
+
159
+ # Boolean query
160
+ results = await client.search("myindex", boolean={
161
+ "must": [("title", "hello")],
162
+ "should": [("body", "world")],
163
+ "must_not": [("title", "spam")],
164
+ })
165
+
166
+ # With pagination
167
+ results = await client.search("myindex", term=("title", "hello"), limit=10, offset=20)
168
+
169
+ # With field loading
170
+ results = await client.search(
171
+ "myindex",
172
+ term=("title", "hello"),
173
+ fields_to_load=["title", "body"]
174
+ )
175
+
176
+ # Access results
177
+ for hit in results.hits:
178
+ print(f"Doc {hit.doc_id}: {hit.score}")
179
+ print(f" Title: {hit.fields.get('title')}")
180
+
181
+ print(f"Total hits: {results.total_hits}")
182
+ print(f"Took: {results.took_ms}ms")
183
+ ```
184
+
185
+ #### Document Retrieval
186
+
187
+ ```python
188
+ # Get document by ID
189
+ doc = await client.get_document("myindex", doc_id=42)
190
+ if doc:
191
+ print(doc.fields["title"])
192
+ ```
193
+
194
+ ## Field Types
195
+
196
+ | Type | Python Type | Description |
197
+ | --------------- | --------------- | ------------------------------------- |
198
+ | `text` | `str` | Full-text searchable string |
199
+ | `u64` | `int` (>= 0) | Unsigned 64-bit integer |
200
+ | `i64` | `int` | Signed 64-bit integer |
201
+ | `f64` | `float` | 64-bit floating point |
202
+ | `bytes` | `bytes` | Binary data |
203
+ | `json` | `dict` / `list` | JSON object (auto-serialized) |
204
+ | `dense_vector` | `list[float]` | Dense vector for semantic search |
205
+ | `sparse_vector` | `dict` | Sparse vector with indices and values |
206
+
207
+ ## Error Handling
208
+
209
+ ```python
210
+ import grpc
211
+
212
+ try:
213
+ await client.search("nonexistent", term=("field", "value"))
214
+ except grpc.RpcError as e:
215
+ if e.code() == grpc.StatusCode.NOT_FOUND:
216
+ print("Index not found")
217
+ else:
218
+ raise
219
+ ```
220
+
221
+ ## Development
222
+
223
+ Generate protobuf stubs:
224
+
225
+ ```bash
226
+ pip install grpcio-tools
227
+ python generate_proto.py
228
+ ```
229
+
230
+ ## License
231
+
232
+ MIT
@@ -0,0 +1,8 @@
1
+ hermes_client_python/__init__.py,sha256=qUaFnTSVr4fSOXfRHJIu8ur5IJEChfIJPOqTC0MiIMw,282
2
+ hermes_client_python/client.py,sha256=E12sJL7d7pih2kamiQXarvmdRESqN5WgKVVek1HF6xQ,12850
3
+ hermes_client_python/hermes_pb2.py,sha256=6mPuThsTsivqjEBtT7Zi4G3z4X3x3RbIzTkJex_bYdo,10672
4
+ hermes_client_python/hermes_pb2_grpc.py,sha256=Oehwc9-0IlluummU8rJJ-ftbEK9AK2ZOBJyj7MnymkQ,16863
5
+ hermes_client_python/types.py,sha256=e6_amiC4F9AkOSzAB9woAfLf9gooN9Y-rzJShrR9YEo,954
6
+ hermes_client_python-1.0.4.dist-info/METADATA,sha256=KLT944lRtT_grd1Yj1gVqfAMwJ6iOAafwgK1YxO_dnQ,6120
7
+ hermes_client_python-1.0.4.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
8
+ hermes_client_python-1.0.4.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