Skip to content

feat(astra): native hybrid + rerank via findAndRerank#59

Merged
erichare merged 1 commit intomainfrom
feat/astra-hybrid-rerank
Apr 24, 2026
Merged

feat(astra): native hybrid + rerank via findAndRerank#59
erichare merged 1 commit intomainfrom
feat/astra-hybrid-rerank

Conversation

@erichare
Copy link
Copy Markdown
Collaborator

Summary

Wires astra-db-ts's `findAndRerank` through the Astra driver so hybrid search on real Astra collections returns reranked hits instead of 501.

  • createCollection forwards `lexical` + `rerank` options when the descriptor opts in. `reranking.enabled` without provider/model throws `WorkspaceMisconfiguredError`.
  • searchHybrid uses `coll.findAndRerank({}, { sort: { $hybrid: { $vector, $lexical } }, includeScores: true })`. Hits return the `$reranker` score (fallback `$vector` / `$lexical`).
  • Standalone rerank stays unimplemented on Astra — the Data API has no primitive to re-score an existing hit set. Callers set `hybrid: true` to get the combined path.
  • Contract gating on `searchHybrid`: 501 if the descriptor has `lexical.enabled: false` OR `reranking.enabled: false`, with a message telling the caller which flag to flip.

Type additions

  • `AstraCreateCollectionOptions.lexical` / `.rerank` — narrow mirrors of astra-db-ts's `CollectionLexicalOptions` / `CollectionRerankOptions`
  • `AstraCollectionLike.findAndRerank?(...)` — narrow return type `{ document, scores }`
  • `AstraHybridSortObject` — mirror of `HybridSortObject`
  • `rerankedToHit(row)` helper: score priority `$reranker > $vector > $lexical`

Test surface

`FakeDb` / `FakeCollection` grow `findAndRerank`, gated on `opts.lexical.enabled` + `opts.rerank.enabled` captured at `createCollection`. The fake scores via cosine(vector) + token-overlap(lexical), blended 50/50 as `$reranker` — faithful enough for ordering assertions. Six new tests cover create options, misconfiguration, the successful hybrid path, and both not-supported branches.

Test plan

  • `npm test` — 449 passing (was 443; +6 astra hybrid)
  • `npm run typecheck` — clean
  • `npm run build` — clean
  • `npm run lint` — clean

Not in scope

Real-Astra integration tests — this PR validates the driver wiring against the fake. Enabling lexical + reranker on a real collection requires Astra-side service provisioning that varies by tenant; a gated CI job with creds can run the live path later.

`lexicalWeight` stays on the request body but Astra ignores it — the reranker owns the blend. Documented in api-spec.md.

Second of three PRs in this Phase 2b-tail batch. UI catch-up follows.

Wires astra-db-ts's `findAndRerank` through the Astra driver so
hybrid search against real Astra collections returns reranked hits
instead of 501.

## What changed

- **createCollection** now forwards `lexical` + `rerank` options to
  the Data API when the descriptor opts in:
    - `lexical.enabled: true` → Astra provisions a BM25-style
      lexical index (analyzer optional)
    - `reranking.enabled: true` + `provider` + `model` → Astra wires
      a reranker service to the collection
  Setting `reranking.enabled` without provider/model now throws
  `WorkspaceMisconfiguredError` rather than silently dropping the
  option.
- **searchHybrid** uses `coll.findAndRerank({}, { sort: { $hybrid:
  { $vector, $lexical } }, includeScores: true })`. Hits return
  the `$reranker` score (or `$vector` / `$lexical` as a fallback).
- **Standalone rerank** is intentionally not exposed on Astra — the
  Data API doesn't have a primitive to re-score an existing hit
  set. Callers wanting rerank on Astra set `hybrid: true`.
- **Contract gating**: `searchHybrid` throws `NotSupportedError`
  (→ 501) if the descriptor has `lexical.enabled: false` OR
  `reranking.enabled: false`, with a clear message telling the
  caller which flag to flip.

## Types

- `AstraCreateCollectionOptions` gains `lexical?` and `rerank?`
  narrow shapes mirroring astra-db-ts's `CollectionLexicalOptions`
  and `CollectionRerankOptions`.
- `AstraCollectionLike` gains `findAndRerank?(...)` with a
  narrow `{ document, scores }` return type — enough to populate
  `SearchHit`.
- New `AstraHybridSortObject` mirror of astra-db-ts's
  `HybridSortObject`.
- New `rerankedToHit(row)` helper: score priority
  `$reranker > $vector > $lexical`.

## Tests

- `FakeDb` / `FakeCollection` grow `findAndRerank` support gated
  on the collection's lexical+rerank flags (captured at
  `createCollection` time from the options the driver passes).
  The fake scores docs with cosine on the vector side + token-
  overlap on the lexical side, blended 50/50 as `$reranker`.
- 6 new tests in `tests/drivers/astra.test.ts`:
  - create passes lexical + rerank through to Astra
  - create throws when reranking is enabled without provider/model
  - searchHybrid orders by the reranker score
  - NotSupported when lexical disabled
  - NotSupported when reranking disabled
  - standalone rerank not exposed

Total: 449 tests (was 443; +6 astra hybrid).

## Wire contract

Unchanged. The 501 paths are the same errors; what changed is
which codepath inside the Astra driver produces them. `mock`
behavior is untouched. `lexicalWeight` stays on the request body
but Astra ignores it (documented in api-spec).

Second of three PRs in this Phase 2b-tail batch. UI catch-up follows.
@erichare erichare merged commit f1d6cff into main Apr 24, 2026
9 of 10 checks passed
@erichare erichare deleted the feat/astra-hybrid-rerank branch April 24, 2026 21:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant