Skip to content

Reranker test plan: Postgres parity, live-provider smoke, quality regression harness (post-#1143) #1231

Description

@phernandez

Context

PR #1143 (ironhalik) added the optional cross-encoder rerank stage: RerankProvider
protocol, FastEmbed (local ONNX) and LiteLLM (hosted) providers, factory with process-wide
singleton cache, opt-in config under reranker_*, fixed-prefix + demoted-tail pagination
stability, and fail-fast 503/502 error mapping. Unit coverage from the PR is strong
(pipeline math, provider contracts, factory, router mapping, redaction, config validation —
all in the default CI suites).

An audit of the merged feature found the remaining gaps concentrated in four areas, plus a
few small code fixes. This issue tracks closing them.

Phase 1 — CI-visible coverage gaps (one PR, no external deps)

  • Postgres parity: the 9 repository-integration rerank tests in
    tests/repository/test_rerank_pipeline.py (:527-841) skip on Postgres
    ("sqlite-vec repository behavior is local SQLite-only"), so hybrid/vector rerank
    ordering on pgvector is asserted only by the single semantic-marked benchmark test.
    Port them to a Postgres variant (recording-reranker fixture; test-int/semantic/ +
    semantic marker if they need real vector tables, main suite otherwise).
  • FTS negative guard: provider configured + FTS/title/permalink search → provider
    never invoked, ranking identical to baseline. (Only the inverse — no provider — is
    tested today: test_search_without_reranker_keeps_baseline.)
  • Wiring seams: create_search_repository injects the singleton provider into both
    backends (search_repository.py:175-207); repository __init__ self-resolution
    (sqlite_search_repository.py:84-86, postgres_search_repository.py:100-102) respects
    semantic_search_enabled.
  • Config-to-pipeline: reranker_max_document_chars set via config truncates the
    document text the provider receives (currently only the pure helper is tested).
  • ChatGPT adapter opacity: chatgpt_tools.py:205's broad except Exception
    collapses rerank 503s into a generic "Internal search error". Distinguish retryable
    unavailability in the returned payload, and pin it with a test.

Small fixes (ride the Phase 1 PR as separate commits)

  • Stale constructor default in fastembed_rerank_provider.py:71
    (Xenova/ms-marco-MiniLM-L-6-v2; the real default is jinaai/jina-reranker-v1-tiny-en
    from config_models.py:61) — dead today but a trap for direct construction.
  • reranker_timeout config key: the LiteLLM provider hardcodes timeout=30.0
    (litellm_rerank_provider.py:34) and the factory never passes it. Add the config field
    (validated gt=0, default 30), thread it through the factory, document it in
    docs/semantic-search.md.

Phase 2 — real-model and live-provider verification

  • Real-FastEmbed smoke (semantic-marked, runs in the test-semantic CI job with model
    caching): load actual jinaai/jina-reranker-v1-tiny-en, tiny fixed corpus, assert the
    relevant doc ranks first and scores are [0,1]-calibrated.
  • Extend test-int/semantic/litellm_live_harness.py (embeddings-only today) with
    rerank: Cohere rerank-v3.5 + one alternate (Jina or Voyage), env-keyed, run manually
    with real keys — contract shape, ordering sanity, auth-failure classification
    (permanent vs transient). Mirrors the feat(core): add LiteLLM embedding provider #809 embedding live-smoke pattern.

Phase 3 — quality regression harness

Phase 4 — operational drills (manual, test-live style)

  • Config UX: enable without semantic_search_enabled → clear error at
    bm config set time; model typo rejected at set-time; secrets absent from
    config list / diagnostics output.
  • Live failure drill: kill network mid-query → API 503; multi-project MCP aggregate
    aborts rather than returning a partial page; recovery without restart.
  • Pagination stability under a live provider: page 1 → concurrent writes → page 2,
    no duplicates or omissions.

Notes for implementers

  • Reranking applies to vector and hybrid modes only; FTS/title/permalink keep their
    ranking (search_repository_base.py:1646). Scores replace fused scores for the fixed
    20-candidate prefix; the tail is demoted rank-only (rerank_provider.py:31-42) — the
    zero-floor tie-break is order-dependent, so downstream score-only re-sorts would break
    pagination stability.
  • Failure semantics are deliberately fail-fast (no silent un-reranked fallback): a
    degraded page could duplicate/omit results relative to an already-served reranked page.
    Tests must not "fix" this.

Refs: #1143 (feature), #950 (motivation, closed), #618 #666 #951 #1155 (open follow-ons).

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions