Skip to content

feat: add optional cross-encoder rerank in FTS5 fast-path - #166

Merged
lyonzin merged 1 commit into
masterfrom
feat/fts5-rerank-toggle
Aug 10, 2026
Merged

feat: add optional cross-encoder rerank in FTS5 fast-path#166
lyonzin merged 1 commit into
masterfrom
feat/fts5-rerank-toggle

Conversation

@lyonzin

@lyonzin lyonzin commented Aug 10, 2026

Copy link
Copy Markdown
Owner

Summary

Adds an opt-in cross-encoder rerank pass on FTS5 fast-path results, gated by config.fts5_rerank_enabled (default False per ADR-003).

Fase 3.5 of FTS5 Lexical Fast-Path plan (workflow artifacts local-only, gitignored).

What ships

  • New private helper KnowledgeOrchestrator._rerank_fts5_results - applies existing cross-encoder to FTS5 hit list when the toggle is on
  • Conditional wire in _run_fts5_search: rerank only fires when config.fts5_rerank_enabled=True AND we have formatted results
  • 4 new tests in tests/test_search.py:
    • UT-062: rerank_enabled=False skips reranker (default behavior preserved)
    • UT-063: rerank_enabled=True applies reranker
    • IT-024: end-to-end rerank ON changes result order vs BM25 raw
    • IT-025: end-to-end rerank OFF matches BM25 raw order
  • Test count baseline 304 -> 308 (+4)

What does NOT ship

  • No wiring outside FTS5 fast-path - hybrid pipeline reranker unchanged
  • No public API change - search_knowledge signature intact
  • No new config field - reuses fts5_rerank_enabled already introduced in Task 03 (PR feat: wire FTS5 fast-path (default OFF) #160)

LEI 1 preserved

Zero change to the 13 frozen MCP tools. mcp_server/server.py change is internal helper + conditional call inside existing _run_fts5_search.

Local validation

Rollback

git revert <sha> - feature is opt-in default-off, revert is safe.

Summary by CodeRabbit

  • New Features

    • Added an optional cross-encoder reranking fast path for FTS5 searches.
    • When enabled, results are reordered by reranker scores and include reranking scores.
    • FTS5 search metadata remains preserved.
  • Documentation

    • Added unreleased changelog documentation for the reranking option, including its disabled-by-default behavior and related references.
  • Tests

    • Added coverage for enabled and disabled reranking, score propagation, ordering, metadata preservation, and latency behavior.

Greptile Summary

Adds an optional cross-encoder pass to FTS5 fast-path results while preserving the default-off behavior.

  • Wires the existing reranker into _run_fts5_search behind fts5_rerank_enabled.
  • Adapts FTS5 result dictionaries to the reranker's document-field contract.
  • Adds toggle, ordering, and latency tests and updates documentation and the test-count baseline.

Confidence Score: 3/5

The PR should be fixed before merging because enabled FTS5 reranking discards surplus candidates before scoring and can silently do nothing under a valid configuration.

The new path cannot promote results outside the raw FTS5 top limit, and its independent toggle reports reranking as applied even when the global reranker disables the operation.

Files Needing Attention: mcp_server/server.py, tests/test_search.py

Important Files Changed

Filename Overview
mcp_server/server.py Adds FTS5 reranking, but truncates the candidate pool before reranking and allows a silent no-op when the global reranker is disabled.
tests/test_search.py Adds toggle and ordering coverage, but tests only rerank a set already equal to max_results and do not cover candidates promoted from the wider FTS5 pool.
README.md Documents the new opt-in rerank behavior and its latency trade-off.
.github/test-count-baseline.txt Updates the expected test count from 304 to 308.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[FTS5 search: fetch 3x candidates] --> B[Format and truncate to max_results]
    B --> C{FTS5 rerank enabled?}
    C -->|Yes| D[Cross-encoder rerank]
    C -->|No| E[Increment skipped counter]
    D --> F[Expand adjacent chunks]
    E --> F
    F --> G[Return FTS5 results]
Loading

Fix All in Claude Code

Prompt To Fix All With AI
### Issue 1
mcp_server/server.py:2455-2457
**Reranker receives truncated candidates**

When a cross-encoder-preferred result ranks below `max_results` in the wider FTS5 candidate pool, `_format_fts5_results` discards it before reranking, so it cannot be promoted into the returned results.

### Issue 2
mcp_server/server.py:2456-2459
**Disabled reranker becomes silent no-op**

If `fts5_rerank_enabled` is true while the independent global reranker setting is false, this branch treats reranking as applied even though `CrossEncoderReranker.rerank` returns the unchanged documents, leaving scores null and failing to increment the skipped counter.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Reviews (1): Last reviewed commit: "feat: add optional cross-encoder rerank ..." | Re-trigger Greptile

Greptile also left 2 inline comments on this PR.

Wires config.fts5_rerank_enabled (default False per ADR-003) to trigger
cross-encoder rerank on FTS5 hit results when the toggle is on. Default
behavior unchanged - fast-path stays lean (1-5ms) unless operator opts
in to trade latency for ranking quality.

- New private helper _rerank_fts5_results in KnowledgeOrchestrator
- Conditional wire in _run_fts5_search
- 4 tests (UT-062, UT-063, IT-024, IT-025) covering both toggle states
- Test count baseline 304 -> 308 (+4)

Fase 3.5 of FTS5 Lexical Fast-Path plan.
Zero LEI 1 impact - internal helper only, no MCP tool signature change.
@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The FTS5 fast path now supports optional cross-encoder reranking. Disabled reranking records a skip metric. Enabled reranking populates scores, reorders results, and preserves FTS5 metadata. Tests cover both modes, ordering, and latency.

Changes

FTS5 reranking

Layer / File(s) Summary
FTS5 reranking path
mcp_server/server.py, README.md, .github/test-count-baseline.txt
The FTS5 path conditionally invokes the cross-encoder, restores the result schema, documents the toggle, and updates the test baseline.
Reranking behavior validation
tests/test_search.py
Tests cover skip behavior, score propagation, metadata preservation, latency, and reranker-based ordering.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant FTS5FastPath
  participant _rerank_fts5_results
  participant CrossEncoderReranker
  FTS5FastPath->>_rerank_fts5_results: pass formatted FTS5 results
  _rerank_fts5_results->>CrossEncoderReranker: rerank mapped documents
  CrossEncoderReranker-->>_rerank_fts5_results: return scores and ordering
  _rerank_fts5_results-->>FTS5FastPath: return restored FTS5 results
Loading

Possibly related PRs

Suggested reviewers: hohlas

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: optional cross-encoder reranking for the FTS5 fast path.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/fts5-rerank-toggle

Comment @coderabbitai help to get the list of available commands.

Comment thread mcp_server/server.py
Comment on lines 2455 to +2457
formatted = self._format_fts5_results(hits, max_results, category_filter)
if config.fts5_rerank_enabled and formatted:
formatted = self._rerank_fts5_results(query_text, formatted, max_results)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Reranker receives truncated candidates

When a cross-encoder-preferred result ranks below max_results in the wider FTS5 candidate pool, _format_fts5_results discards it before reranking, so it cannot be promoted into the returned results.

Prompt To Fix With AI
This is a comment left during a code review.
Path: mcp_server/server.py
Line: 2455-2457

Comment:
**Reranker receives truncated candidates**

When a cross-encoder-preferred result ranks below `max_results` in the wider FTS5 candidate pool, `_format_fts5_results` discards it before reranking, so it cannot be promoted into the returned results.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Fix in Claude Code

Comment thread mcp_server/server.py
Comment on lines +2456 to +2459
if config.fts5_rerank_enabled and formatted:
formatted = self._rerank_fts5_results(query_text, formatted, max_results)
else:
metrics.inc(FAST_PATH_RERANK_SKIPPED_TOTAL)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Disabled reranker becomes silent no-op

If fts5_rerank_enabled is true while the independent global reranker setting is false, this branch treats reranking as applied even though CrossEncoderReranker.rerank returns the unchanged documents, leaving scores null and failing to increment the skipped counter.

Prompt To Fix With AI
This is a comment left during a code review.
Path: mcp_server/server.py
Line: 2456-2459

Comment:
**Disabled reranker becomes silent no-op**

If `fts5_rerank_enabled` is true while the independent global reranker setting is false, this branch treats reranking as applied even though `CrossEncoderReranker.rerank` returns the unchanged documents, leaving scores null and failing to increment the skipped counter.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Fix in Claude Code

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@mcp_server/server.py`:
- Around line 2456-2459: Establish one consistent contract for FTS5 reranking
across server.py lines 2439-2443 and 2456-2459: either let fts5_rerank_enabled
override reranker_enabled, or require both flags and increment
FAST_PATH_RERANK_SKIPPED_TOTAL when reranking is unavailable. Document the
chosen contract in README.md line 1417 and add mixed-flag coverage in
tests/test_search.py lines 932-933.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 5aca34ca-aea1-4eff-ac5c-d4b4df971be0

📥 Commits

Reviewing files that changed from the base of the PR and between b0a5668 and ec40358.

📒 Files selected for processing (4)
  • .github/test-count-baseline.txt
  • README.md
  • mcp_server/server.py
  • tests/test_search.py

Comment thread mcp_server/server.py
Comment on lines +2456 to +2459
if config.fts5_rerank_enabled and formatted:
formatted = self._rerank_fts5_results(query_text, formatted, max_results)
else:
metrics.inc(FAST_PATH_RERANK_SKIPPED_TOTAL)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -C 3 \
  'fts5_rerank_enabled|reranker_enabled' \
  -g '*.py' -g '*.yaml' -g '*.yml' -g '*.md' .

Repository: lyonzin/knowledge-rag

Length of output: 17913


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- CrossEncoderReranker and FTS5 helper ---'
sed -n '730,810p' mcp_server/server.py
sed -n '2380,2485p' mcp_server/server.py

printf '%s\n' '--- FTS5 rerank tests and metric assertions ---'
sed -n '880,1025p' tests/test_search.py
rg -n -C 5 'FAST_PATH_RERANK_SKIPPED_TOTAL|rerank_skipped|reranker_score|_rerank_fts5_results' \
  mcp_server tests

printf '%s\n' '--- Configuration documentation and defaults ---'
sed -n '620,675p' mcp_server/config.py
sed -n '890,915p' mcp_server/config.py
sed -n '1000,1025p' mcp_server/config.py

Repository: lyonzin/knowledge-rag

Length of output: 42081


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path

source = Path("mcp_server/server.py").read_text()
start = source.index("class CrossEncoderReranker")
rerank_start = source.index("def rerank", start)
rerank_end = source.index("\n    def ", rerank_start + 1)
rerank_body = source[rerank_start:rerank_end]

fts_start = source.index("def _run_fts5_search")
fts_body = source[fts_start:source.index("\n    def ", fts_start + 1)]

required = [
    "if not documents or not config.reranker_enabled:",
    "if config.fts5_rerank_enabled and formatted:",
    "else:\n            metrics.inc(FAST_PATH_RERANK_SKIPPED_TOTAL)",
]
for text in required:
    assert text in source, f"missing source condition: {text!r}"

print("rerank early-return:", "config.reranker_enabled" in rerank_body)
print("fts5 caller gate:", "config.fts5_rerank_enabled and formatted" in fts_body)
print("mixed flags (fts5=True, shared=False): caller enters =", True)
print("mixed flags (fts5=True, shared=False): reranker returns unchanged =", True)
print("mixed flags (fts5=True, shared=False): caller skip metric branch =", False)
PY

Repository: lyonzin/knowledge-rag

Length of output: 414


Define one configuration contract for FTS5 reranking.

When fts5_rerank_enabled=True and reranker_enabled=False, CrossEncoderReranker.rerank returns the original order with reranker_score=None. FAST_PATH_RERANK_SKIPPED_TOTAL does not increment. Either make the FTS5 flag override the shared gate or require both flags and record the skip. Update both documentation sites and add mixed-flag coverage.

📍 Affects 3 files
  • mcp_server/server.py#L2456-L2459 (this comment)
  • mcp_server/server.py#L2439-L2443
  • README.md#L1417-L1417
  • tests/test_search.py#L932-L933
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mcp_server/server.py` around lines 2456 - 2459, Establish one consistent
contract for FTS5 reranking across server.py lines 2439-2443 and 2456-2459:
either let fts5_rerank_enabled override reranker_enabled, or require both flags
and increment FAST_PATH_RERANK_SKIPPED_TOTAL when reranking is unavailable.
Document the chosen contract in README.md line 1417 and add mixed-flag coverage
in tests/test_search.py lines 932-933.

@lyonzin
lyonzin merged commit 3733dce into master Aug 10, 2026
38 checks passed
@lyonzin
lyonzin deleted the feat/fts5-rerank-toggle branch August 10, 2026 18:40
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