Skip to content

Commit a66abe5

Browse files
docs: registry-first README for @corbits/reranking (CL-8775) (#4)
* docs: registry-first README for @corbits/reranking Replace the git-install README with the Interchange section shape: Install / Use / Full example / How it works / Contributing. * docs: align README headings with Interchange (CL-8775) Use Quickstart / How it works / Development / License instead of Install / Use / Full example / Contributing. * docs: product-voice README (CL-8775) * docs(readme): runnable-only Quickstart (CL-8775) * docs(readme): Quickstart shows real host usage (CL-8775) The prior Quickstart hardcoded a localhost baseURL and always-succeeds call with no failure handling. Real retrieval pipelines read the reranker endpoint from host config at boot (fail fast on a half-configured reranker) and degrade to their own fused order when a rerank call fails, so a reranker outage never fails search. * docs(readme): report and surface rerank fallback errors (CL-8775)
1 parent 2378477 commit a66abe5

1 file changed

Lines changed: 64 additions & 89 deletions

File tree

‎README.md‎

Lines changed: 64 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -1,121 +1,96 @@
11
# @corbits/reranking
22

3-
Cross-encoder rerank client. Given a query and a candidate set, `rerankDocuments`
4-
posts them to a reranker, maps the reply back to your own document ids, and
5-
returns `{id, score}` sorted descending.
3+
Cross-encoder reranking for retrieval pipelines. Send a query plus candidates, get scored ids back in ranked order.
64

7-
## Install
5+
Give `rerankDocuments` a query and your own `{id, text}` docs; it posts them to the reranker, maps the reply back to your ids, and returns `{id, score}` sorted descending.
86

9-
Not published to npm yet. Until a registry publish, `npm install @corbits/reranking`
10-
(and `bun add @corbits/reranking`) 404. Git is the install path.
7+
## Runtime support
118

12-
```bash
13-
bun add github:corbitsdev/corbits-reranking
14-
```
9+
Runs on Bun >= 1.2 or Node >= 24. The built `dist/` entry is the default import.
1510

16-
Unlike embeddings, reranking has **no OpenAI-compatible standard** and is not
17-
converging on one — TEI does not serve an OpenAI-shaped rerank route at all
18-
([text-embeddings-inference#683](https://github.com/huggingface/text-embeddings-inference/issues/683)).
19-
So this is a real adapter boundary, not a knob:
20-
21-
| `apiStyle` | endpoint | request | response |
22-
| ---------- | ------------ | -------------------- | --------------------------------------- |
23-
| `tei` | `/rerank` | `{query, texts}` | `[{index, score}]` |
24-
| `cohere` | `/v2/rerank` | `{query, documents}` | `{results: [{index, relevance_score}]}` |
25-
| `voyage` | `/v1/rerank` | `{query, documents}` | `{data: [{index, relevance_score}]}` |
11+
## Quickstart
2612

27-
Jina's `/rerank` uses Cohere's shape and is served by the `cohere` adapter.
13+
```bash
14+
bun add @corbits/reranking
15+
```
2816

29-
Transport, error classification and retry come from `@intx/inference` rather
30-
than being reimplemented: requests are `BuiltRequest`s sent through the single
31-
`deps.fetch` path, failures become an `InferenceError` through the same
32-
classifiers a chat call uses, and `createDefaultRetryPolicy` decides whether to
33-
back off or abort.
17+
`rerankDocuments(query, docs, config, options)` validates config with `RerankConfigSchema`, short-circuits empty input with no request, and returns ranked results. Options carry `deps`, with optional `retryPolicy`, `registry`, and `signal`. Config takes `baseURL` and `apiStyle`, with optional `model` (used by Cohere and Voyage), `apiKey`, and `timeoutMs`.
3418

35-
`RerankAdapter` deliberately mirrors inference's `ProviderAdapter` — same
36-
`buildRequest` / `parseResponse` members, same optional `extractRetryAfterMs`
37-
for a provider that signals pacing its own way. Styles resolve through
38-
`createRerankAdapterRegistry`, which closes over a private `Map` copy so an
39-
`apiStyle` arriving from config or an environment variable cannot reach an
40-
inherited member of `Object.prototype` and fail later as an opaque `TypeError`.
41-
Pass your own `registry` to add a house format without forking the package.
19+
A retrieval pipeline reads its reranker endpoint from host config at boot — a half-configured reranker should fail boot, not degrade silently later — then reranks each query's fused candidates, falling back to the caller's own ordering on failure so a reranker outage never fails search:
4220

4321
```ts
4422
import { createDefaultScheduler } from "@intx/inference";
45-
import { rerankDocuments } from "@corbits/reranking";
23+
import {
24+
rerankDocuments,
25+
type RerankDoc,
26+
type RerankResult,
27+
} from "@corbits/reranking";
4628

47-
// Only `fetch` and `scheduler` are needed — a full harness `Dependencies`
48-
// also satisfies this if you already have one.
4929
const deps = { fetch, scheduler: createDefaultScheduler() };
5030

51-
const ranked = await rerankDocuments(
52-
"how do I deploy to staging?",
53-
[
54-
{ id: "doc-1", text: "Staging deploys run from main…" },
55-
{ id: "doc-2", text: "On-call rotation is weekly…" },
56-
],
57-
{ baseURL: "http://localhost:8085", apiStyle: "tei" },
58-
{ deps },
59-
);
31+
function requireEnv(name: string): string {
32+
const value = process.env[name];
33+
if (!value) throw new Error(`${name} is required`);
34+
return value;
35+
}
36+
37+
const baseURL = requireEnv("RERANK_BASE_URL");
38+
const model = process.env.RERANK_MODEL;
39+
40+
export async function rerankFusedCandidates(
41+
query: string,
42+
candidates: readonly RerankDoc[],
43+
onError: (error: unknown) => void,
44+
): Promise<RerankResult[]> {
45+
try {
46+
return await rerankDocuments(
47+
query,
48+
candidates,
49+
{
50+
baseURL,
51+
apiStyle: "tei",
52+
...(model !== undefined ? { model } : {}),
53+
},
54+
{ deps },
55+
);
56+
} catch (error) {
57+
// Reranker unavailable or timed out — report it, then degrade to the
58+
// fused order the caller already computed rather than fail the search.
59+
onError(error);
60+
return candidates.map((doc, i) => ({
61+
id: doc.id,
62+
score: candidates.length - i,
63+
}));
64+
}
65+
}
6066
```
6167

62-
## API
63-
64-
- `rerankDocuments(query, docs, config, options)` — rerank `RerankDoc[]`
65-
(`{id, text}`) against the query and return `RerankResult[]`
66-
(`{id, score}`), sorted descending. Empty input short-circuits with no
67-
request. Config is validated by `RerankConfigSchema` (`RerankConfig`);
68-
options (`RerankOptions`) take `deps`, an optional `retryPolicy`,
69-
`registry`, and `signal`.
70-
- Adapters — `RerankAdapter` (`buildRequest` / `parseResponse`, optional
71-
`extractRetryAfterMs`), `RerankAPIStyle` (`tei` | `cohere` | `voyage`),
72-
`rerankAdapters`, `rerankAdapterRegistry`, and
73-
`createRerankAdapterRegistry` for house formats. Supporting types:
74-
`RerankDoc`, `RerankResult`, `RerankRequestConfig`,
75-
`RerankRequestBuilder`, `RerankResponseParser`, `RerankAdapterRegistry`.
76-
- Transport — `runJSONRequest` sends the built request through `deps.fetch`
77-
with retry; `extractRetryAfterMs` and `RetryAfterExtractor` handle
78-
provider pacing; `ModelRequestError` carries the classified
79-
`InferenceError` as `reason` with the URL (`RunRequestOptions` configures
80-
the call).
68+
## How it works
8169

82-
## Scores are mapped by index, never by position
70+
Reranking has no single OpenAI-style standard, so this package draws the adapter boundary explicitly:
8371

84-
Every one of these protocols addresses documents by their position in the
85-
_request_ array, and TEI's reply is explicitly unordered. Results are therefore
86-
mapped back through `docs[index]`, and an out-of-range index raises rather than
87-
silently attaching a score to the wrong document.
88-
89-
`RerankDoc.id` exists for this reason: the wire formats only speak in array
90-
offsets, so translating back to a stable identifier is this package's job rather
91-
than the caller's.
72+
| `apiStyle` | Endpoint | Request shape | Response shape |
73+
| ---------- | ------------ | -------------------- | --------------------------------------- |
74+
| `tei` | `/rerank` | `{query, texts}` | `[{index, score}]` |
75+
| `cohere` | `/v2/rerank` | `{query, documents}` | `{results: [{index, relevance_score}]}` |
76+
| `voyage` | `/v1/rerank` | `{query, documents}` | `{data: [{index, relevance_score}]}` |
9277

93-
## Degrading soft is the caller's decision
78+
Jina's `/rerank` follows the Cohere shape and is served by the `cohere` adapter.
9479

95-
This package raises on failure. It does not swallow errors, because a reranker
96-
outage should be a policy decision at the call site — a retrieval pipeline
97-
typically wants to fall back to its fused ordering and carry on, which it cannot
98-
do if the failure is hidden here.
80+
`RerankAdapter` mirrors inference's `ProviderAdapter` with `buildRequest` / `parseResponse` plus optional `extractRetryAfterMs`. Styles resolve through `createRerankAdapterRegistry`, which keeps a private `Map` copy so config-supplied style names resolve safely. Pass a custom `registry` to add a house format alongside the built-ins (`rerankAdapters`, `rerankAdapterRegistry`).
9981

100-
## Errors
82+
Every protocol addresses documents by position in the request array, and TEI replies unordered. Results map back through `docs[index]`, with an out-of-range index raising so a score always lands on the right document. `RerankDoc.id` carries your stable identifier across that index-based wire, and the final list sorts by score descending.
10183

102-
Every failure — transport, HTTP status, or a 200 whose body is not JSON —
103-
raises `ModelRequestError`, carrying the classified `InferenceError` as `reason`
104-
and the URL.
84+
This package raises on failure, leaving fallback policy at the call site. Retrieval pipelines commonly fall back to their fused ordering when the reranker is unavailable and carry on serving.
10585

106-
Note that `@corbits/embedding` and `@corbits/reranking` each carry their own
107-
copy of this class until the shared transport is upstreamed, so `instanceof`
108-
does not hold across the two. Catching both? Discriminate on
109-
`error.name === "ModelRequestError"`.
86+
Every failure mode — transport, HTTP status, or a 200 with an unexpected body — raises `ModelRequestError`, carrying the classified `InferenceError` as `reason` plus the request URL. Transport, classification, and retry come from `@intx/inference`: built requests travel the shared `deps.fetch` path with `createDefaultRetryPolicy` guiding backoff. The embedding and reranking packages each carry their own copy of this class while the shared transport is upstreamed, so code catching both discriminates on `error.name === "ModelRequestError"`.
11087

111-
## Versioning
88+
Transport helpers (`runJSONRequest`, `extractRetryAfterMs`, `ModelRequestError`, `RunRequestOptions`, `RetryAfterExtractor`) are also re-exported for sibling one-shot JSON clients.
11289

113-
Semver. Released manually with `bun run build && npm publish` once CI is green.
90+
Interchange retrieval pipelines call `rerankDocuments` after hybrid search to lift the strongest candidates to the top. The shared `@intx/inference` transport keeps rerank retries and error taxonomy consistent with chat and embeddings across the hub.
11491

11592
## Development
11693

117-
Requires Node.js >= 24 and Bun >= 1.2.0.
118-
11994
```bash
12095
bun install
12196
bun test ./src

0 commit comments

Comments
 (0)