hermes-client-typescript 1.8.145 → 1.8.146
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.
- package/README.md +59 -225
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -22,6 +22,7 @@ try {
|
|
|
22
22
|
"articles",
|
|
23
23
|
`
|
|
24
24
|
index articles {
|
|
25
|
+
field id: text<raw> [primary, stored]
|
|
25
26
|
field title: text<simple> [indexed, stored]
|
|
26
27
|
field body: text<simple> [indexed, stored]
|
|
27
28
|
}
|
|
@@ -31,8 +32,8 @@ try {
|
|
|
31
32
|
const [indexedCount, errorCount, errors] = await client.indexDocuments(
|
|
32
33
|
"articles",
|
|
33
34
|
[
|
|
34
|
-
{ title: "Hello", body: "First article" },
|
|
35
|
-
{ title: "Hermes", body: "Fast search" },
|
|
35
|
+
{ id: "1", title: "Hello", body: "First article" },
|
|
36
|
+
{ id: "2", title: "Hermes", body: "Fast search" },
|
|
36
37
|
],
|
|
37
38
|
);
|
|
38
39
|
if (errorCount) throw new Error(JSON.stringify(errors));
|
|
@@ -61,148 +62,94 @@ try {
|
|
|
61
62
|
}
|
|
62
63
|
```
|
|
63
64
|
|
|
64
|
-
Call `connect()` before the first RPC and `close()` when
|
|
65
|
-
longer needed.
|
|
65
|
+
Call `connect()` before the first RPC and `close()` when finished.
|
|
66
66
|
|
|
67
67
|
## Index management
|
|
68
68
|
|
|
69
69
|
```typescript
|
|
70
|
-
await client.
|
|
71
|
-
const names = await client.listIndexes();
|
|
70
|
+
await client.listIndexes();
|
|
72
71
|
const info = await client.getIndexInfo("articles");
|
|
73
|
-
|
|
74
|
-
await client.forceMerge("articles");
|
|
75
72
|
await client.reorder("articles");
|
|
76
73
|
await client.retrainVectorIndex("articles");
|
|
77
|
-
await client.deleteIndex("articles");
|
|
78
74
|
```
|
|
79
75
|
|
|
80
|
-
|
|
76
|
+
`indexDocuments` returns `[indexedCount, errorCount, errors]`;
|
|
77
|
+
`indexDocumentsStream` takes an async iterable. Inspect errors, then `commit()`
|
|
78
|
+
to publish accepted work. Arrays hold repeated values; flat numeric arrays are
|
|
79
|
+
dense vectors. One sparse vector uses `[[[1, 0.5], [8, 0.25]]]`: the outer array
|
|
80
|
+
is required because `[[1, 0.5], [8, 0.25]]` means two dense vectors.
|
|
81
|
+
|
|
82
|
+
### Delete and upsert documents
|
|
81
83
|
|
|
82
|
-
|
|
84
|
+
With a text primary key, delete by exact key or supply a complete replacement:
|
|
83
85
|
|
|
84
86
|
```typescript
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
87
|
+
await client.deleteDocument("articles", "2");
|
|
88
|
+
await client.upsertDocument("articles", { id: "1", title: "Updated title" });
|
|
89
|
+
await client.commit("articles");
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
`deleteDocuments` / `upsertDocuments` return `DocumentMutationResult` with
|
|
93
|
+
`acceptedCount` and `errors: [{index, error}]`. Single-item helpers throw on
|
|
94
|
+
rejection. Missing deletes succeed; upserts insert missing keys. Staged rows can
|
|
95
|
+
be replaced/deleted again before commit; the latest accepted version wins.
|
|
96
|
+
Optional [content hashes](../docs/content-deduplication.md) skip unchanged writes.
|
|
97
|
+
|
|
98
|
+
Limits: 100,000 deletion keys / 8 MiB key bytes; 1,000 replacements / 32 MiB
|
|
99
|
+
encoded protobuf, or one replacement / 200 MiB including the request envelope.
|
|
100
|
+
Broker commits are atomic per partition. See [mutation semantics](../docs/row-deletion.md).
|
|
101
|
+
|
|
102
|
+
### Compact deleted rows
|
|
95
103
|
|
|
96
|
-
|
|
104
|
+
```typescript
|
|
105
|
+
await client.forceMerge("articles"); // Retain tombstones.
|
|
106
|
+
await client.forceMerge("articles", undefined, true); // Physically remove deleted rows.
|
|
97
107
|
```
|
|
98
108
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
sparse vector. The outer array is required because `[[1, 0.5], [8, 0.25]]` is
|
|
103
|
-
the legacy shape for two dense vectors.
|
|
109
|
+
The second argument remains the timeout in milliseconds. Compaction handles
|
|
110
|
+
singletons and may change addresses and BM25 scores. Index info exposes
|
|
111
|
+
`numDocs`, `physicalNumDocs`, `numDeletedDocs`, and `deletedRatio`.
|
|
104
112
|
|
|
105
113
|
## Searching
|
|
106
114
|
|
|
107
|
-
`search()` accepts a `SearchRequest`. Its `query`
|
|
108
|
-
|
|
115
|
+
`search(indexName, request)` accepts a typed `SearchRequest`. Its `query` selects
|
|
116
|
+
one variant: `term`, `match`, `phrase`, `boolean`, `sparseVector`, `denseVector`,
|
|
117
|
+
`binaryDenseVector`, `boost`, `range`, `prefix`, `all`, or `fusion`.
|
|
118
|
+
See [client types](src/types.ts) and the [protocol](../hermes-proto/hermes.proto).
|
|
109
119
|
|
|
110
120
|
```typescript
|
|
111
|
-
|
|
112
|
-
await client.search("articles", {
|
|
113
|
-
query: { term: { field: "title", term: "hermes" } },
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
// Recursive Boolean query
|
|
117
|
-
await client.search("articles", {
|
|
121
|
+
const results = await client.search("articles", {
|
|
118
122
|
query: {
|
|
119
123
|
boolean: {
|
|
120
|
-
must: [{ match: { field: "
|
|
124
|
+
must: [{ match: { field: "title", text: "search" } }],
|
|
121
125
|
mustNot: [{ term: { field: "title", term: "draft" } }],
|
|
122
126
|
},
|
|
123
127
|
},
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
// Dense retrieval with reranking
|
|
127
|
-
await client.search("articles", {
|
|
128
|
-
query: {
|
|
129
|
-
denseVector: {
|
|
130
|
-
field: "embedding",
|
|
131
|
-
vector: [0.1, 0.2, 0.3],
|
|
132
|
-
nprobe: 16,
|
|
133
|
-
},
|
|
134
|
-
},
|
|
135
|
-
reranker: {
|
|
136
|
-
field: "embedding",
|
|
137
|
-
vector: [0.1, 0.2, 0.3],
|
|
138
|
-
},
|
|
139
|
-
candidateLimit: 20,
|
|
140
|
-
limit: 10,
|
|
141
128
|
fieldsToLoad: ["title"],
|
|
142
129
|
});
|
|
143
|
-
|
|
144
|
-
// Hybrid union fusion
|
|
145
|
-
await client.search("articles", {
|
|
146
|
-
query: {
|
|
147
|
-
fusion: {
|
|
148
|
-
method: "rrf",
|
|
149
|
-
rrfK: 60,
|
|
150
|
-
queries: [
|
|
151
|
-
{
|
|
152
|
-
query: {
|
|
153
|
-
sparseVector: {
|
|
154
|
-
field: "sparseEmbedding",
|
|
155
|
-
indices: [1, 5],
|
|
156
|
-
values: [0.8, 0.2],
|
|
157
|
-
},
|
|
158
|
-
},
|
|
159
|
-
},
|
|
160
|
-
{
|
|
161
|
-
query: {
|
|
162
|
-
denseVector: {
|
|
163
|
-
field: "embedding",
|
|
164
|
-
vector: [0.1, 0.2, 0.3],
|
|
165
|
-
},
|
|
166
|
-
},
|
|
167
|
-
},
|
|
168
|
-
],
|
|
169
|
-
},
|
|
170
|
-
},
|
|
171
|
-
});
|
|
172
130
|
```
|
|
173
131
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
`fusion`.
|
|
132
|
+
`getDocument(indexName, hit.address)` uses the full segment/document address
|
|
133
|
+
and returns `null` on `NOT_FOUND`. Use primary keys for durable identity.
|
|
177
134
|
|
|
178
|
-
|
|
135
|
+
## Ranking diagnostics and recall traces
|
|
179
136
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
137
|
+
Search options `includeRrfScores: true` and `tracing: true` default to false.
|
|
138
|
+
RRF diagnostics describe organic branch nominations; traces retain bounded
|
|
139
|
+
candidates and query trees, including hits outside the final page. Neither
|
|
140
|
+
changes retrieval depth or ranking. Oversized exports fail explicitly.
|
|
183
141
|
|
|
184
|
-
|
|
142
|
+
For named branches, use `l1: { formula: "0.2 * title + 0.8 * body + 3 * rrf" }`.
|
|
143
|
+
The formula is the only L1 scoring interface; old coefficient fields are removed.
|
|
144
|
+
See [candidate scoring](../docs/candidate-rescoring.md) for backfill, passage
|
|
145
|
+
selection, expression limits, capability versions, and distributed behavior.
|
|
185
146
|
|
|
186
147
|
## Deadlines
|
|
187
148
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
const client = new HermesClient("localhost:50051", {
|
|
193
|
-
defaultTimeoutMs: 5_000,
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
client.connect();
|
|
197
|
-
try {
|
|
198
|
-
await client.search("articles", { query: { all: {} } }, 500);
|
|
199
|
-
await client.forceMerge("articles", 3_600_000);
|
|
200
|
-
} finally {
|
|
201
|
-
client.close();
|
|
202
|
-
}
|
|
203
|
-
```
|
|
204
|
-
|
|
205
|
-
Expired calls reject with a gRPC `DEADLINE_EXCEEDED` error.
|
|
149
|
+
RPCs accept a trailing timeout in milliseconds, overriding the constructor's
|
|
150
|
+
`defaultTimeoutMs`. Expired calls reject with gRPC `DEADLINE_EXCEEDED`.
|
|
151
|
+
An expired mutation may already be staged, and an accepted commit continues
|
|
152
|
+
after disconnection. Resolve the outcome before retrying replacements.
|
|
206
153
|
|
|
207
154
|
## Development
|
|
208
155
|
|
|
@@ -211,119 +158,6 @@ pnpm install --frozen-lockfile
|
|
|
211
158
|
pnpm check
|
|
212
159
|
```
|
|
213
160
|
|
|
214
|
-
`
|
|
215
|
-
|
|
216
|
-
the generated source
|
|
217
|
-
|
|
218
|
-
```bash
|
|
219
|
-
pnpm generate
|
|
220
|
-
pnpm check
|
|
221
|
-
```
|
|
222
|
-
|
|
223
|
-
## License
|
|
224
|
-
|
|
225
|
-
MIT
|
|
226
|
-
|
|
227
|
-
## Ranking diagnostics and recall traces
|
|
228
|
-
|
|
229
|
-
Both options default to false and preserve the requested ranking:
|
|
230
|
-
|
|
231
|
-
```typescript
|
|
232
|
-
const response = await client.search("articles", {
|
|
233
|
-
query: {
|
|
234
|
-
fusion: {
|
|
235
|
-
queries: [
|
|
236
|
-
{ name: "title", query: { match: { field: "title", text: "rust" } } },
|
|
237
|
-
{ name: "body", query: { match: { field: "body", text: "rust" } } },
|
|
238
|
-
],
|
|
239
|
-
},
|
|
240
|
-
},
|
|
241
|
-
includeRrfScores: true,
|
|
242
|
-
tracing: true,
|
|
243
|
-
});
|
|
244
|
-
for (const hit of response.hits) {
|
|
245
|
-
console.log(hit.score, hit.rrfScore, hit.rrfContributions);
|
|
246
|
-
}
|
|
247
|
-
for (const shard of response.trace?.shards ?? []) {
|
|
248
|
-
for (const branch of shard.queries) {
|
|
249
|
-
console.log(shard.shardId, branch.queryName, branch.candidates);
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
```
|
|
253
|
-
|
|
254
|
-
RRF diagnostics use organic nomination ranks merged across all shards, separate
|
|
255
|
-
from L1/reranker scores. Each vote carries the branch index/name, one-based rank,
|
|
256
|
-
weighted contribution and optional ordinal. An absent ordinal denotes document
|
|
257
|
-
context; ordinal 0 denotes a real passage. Backfilled and score-only features do
|
|
258
|
-
not vote.
|
|
259
|
-
|
|
260
|
-
The trace retains all bounded branch nominations, query trees, common filters
|
|
261
|
-
and shard selections, including candidates discarded before the final page.
|
|
262
|
-
Trace candidates contain addresses, raw scores and ordinals, without stored
|
|
263
|
-
fields. Tracing does not increase retrieval depth or rerun Boolean clauses.
|
|
264
|
-
Oversized diagnostics and unsupported backends fail explicitly. See the
|
|
265
|
-
[scoring and tracing contract](../docs/candidate-rescoring.md).
|
|
266
|
-
|
|
267
|
-
For a named, scoped L1 query, specify the complete scoring formula:
|
|
268
|
-
|
|
269
|
-
```typescript
|
|
270
|
-
l1: {
|
|
271
|
-
formula: "0.2 * title + 0.8 * log1p(body) + 3 * rrf";
|
|
272
|
-
}
|
|
273
|
-
```
|
|
274
|
-
|
|
275
|
-
`formula` is the only L1 scoring interface. Coefficients, offsets and RRF
|
|
276
|
-
multipliers go in the expression; the former coefficient fields are removed.
|
|
277
|
-
Arithmetic, powers, logarithms, `sqrt`, `abs`, `exp`, `min`/`max` and trigonometry
|
|
278
|
-
are supported. Use `{body.bm25}` for punctuated branch names. `log` and `ln` are
|
|
279
|
-
natural logarithms; `log2` and `log10` select those bases. Missing branch values
|
|
280
|
-
use configured missing defaults, otherwise zero. Backfill remains optional.
|
|
281
|
-
|
|
282
|
-
The formula runs before passage selection and the document combiner. A formula
|
|
283
|
-
using `rrf` makes the broker obtain the complete bounded candidate and passage
|
|
284
|
-
union before global inference. Exports that exceed budgets fail explicitly.
|
|
285
|
-
Expressions are bounded to 4 KiB, 256 tokens and 32 parenthesis levels. Invalid
|
|
286
|
-
variables, invalid syntax and non-finite predictions fail explicitly. Servers
|
|
287
|
-
and brokers must support `formula_v1` (`candidate_scoring_version=3`).
|
|
288
|
-
|
|
289
|
-
### Compact deleted rows
|
|
290
|
-
|
|
291
|
-
```typescript
|
|
292
|
-
await client.forceMerge("articles"); // Retain deletion masks.
|
|
293
|
-
await client.forceMerge("articles", undefined, true); // Physically compact final outputs.
|
|
294
|
-
const info = await client.getIndexInfo("articles");
|
|
295
|
-
console.log(info.numDeletedDocs, info.physicalNumDocs, info.deletedRatio);
|
|
296
|
-
```
|
|
297
|
-
|
|
298
|
-
The optional second argument remains the timeout in milliseconds. Compaction
|
|
299
|
-
also handles a singleton and can change document addresses and BM25 statistics.
|
|
300
|
-
|
|
301
|
-
### Delete and upsert documents
|
|
302
|
-
|
|
303
|
-
With a text field declared `[primary]`, delete by exact key and replace by passing
|
|
304
|
-
the complete document. Every chunk belongs to its document and is deleted with it.
|
|
305
|
-
|
|
306
|
-
```typescript
|
|
307
|
-
await client.deleteDocument("articles", "obsolete-key");
|
|
308
|
-
await client.upsertDocument("articles", {
|
|
309
|
-
id: "article-42",
|
|
310
|
-
body: ["replacement chunk one", "replacement chunk two"],
|
|
311
|
-
});
|
|
312
|
-
await client.commit("articles");
|
|
313
|
-
|
|
314
|
-
const result = await client.deleteDocuments("articles", ["old-a", "old-b"]);
|
|
315
|
-
console.log(result.acceptedCount, result.errors); // errors: [{ index, error }]
|
|
316
|
-
await client.commit("articles"); // publishes accepted operations
|
|
317
|
-
```
|
|
318
|
-
|
|
319
|
-
`upsertDocuments` accepts a list of full replacements and returns the same
|
|
320
|
-
`DocumentMutationResult`. Single-item helpers throw on rejection. Missing deletes
|
|
321
|
-
are accepted; upserts insert missing keys. Replacing/deleting a pending insertion
|
|
322
|
-
is supported; the latest accepted version is published at commit. With a schema
|
|
323
|
-
content hash, an identical retry of the latest staged version is a no-op.
|
|
324
|
-
Limits are 100,000 deletion keys / 8 MiB key bytes and 1,000
|
|
325
|
-
replacement documents / 32 MiB encoded bytes, or one replacement / 200 MiB
|
|
326
|
-
including the request envelope. Normal deadlines apply; an expired
|
|
327
|
-
RPC can have staged work, so do not blindly retry replacements. Broker commits
|
|
328
|
-
are atomic within each partition. Physical cleanup remains
|
|
329
|
-
`await client.forceMerge("articles", undefined, true)`.
|
|
161
|
+
`check` compiles strict TypeScript and runs converter tests. After
|
|
162
|
+
[protocol changes](../hermes-proto/README.md#regeneration-and-validation), run
|
|
163
|
+
`pnpm generate && pnpm check` and include the generated source.
|