Skip to content

rag: add deterministic and opt-in semantic reranking#901

Merged
BigSimmo merged 5 commits into
mainfrom
codex/rag-ranking-main-20260719
Jul 18, 2026
Merged

rag: add deterministic and opt-in semantic reranking#901
BigSimmo merged 5 commits into
mainfrom
codex/rag-ranking-main-20260719

Conversation

@BigSimmo

@BigSimmo BigSimmo commented Jul 18, 2026

Copy link
Copy Markdown
Owner

Summary

  • Add deterministic per-query-class ranking snapshots and tuner support.
  • Separate rank/fusion signals from confidence and make feature-fusion weights explicit.
  • Add an ambiguity-only semantic reranker behind RAG_SEMANTIC_RERANK_ENABLED=false; it fails open to deterministic ordering and uses structured output.

Verification

  • npm run test — 315 files, 2,853 tests passed.
  • Focused ranking tests — 6 files, 86 tests passed.
  • npm run eval:rag:offline — 36 golden cases, 21 suites, 294 tests passed.
  • npm run typecheck
  • Changed-file ESLint
  • npm run check:production-readiness:ci — READY; only expected missing-local-env warnings.
  • npm run verify:pr-local

Verification not run: npm run verify:pr-local was not repeated after its constituent full unit, typecheck, lint, offline RAG, and readiness checks passed; hosted CI remains responsible for the clean production build.

Live verification not run: npm run eval:retrieval:quality and semantic-rerank canary require provider credentials and separate approval. No provider calls were made.

UI verification not run: no UI, routing, styling, or browser behavior changed.

Risk and rollout

  • Risk: deterministic rank weighting changes can alter source ordering; the semantic stage is strictly default-off and fail-open.
  • Rollback: revert the single commit. Keep RAG_SEMANTIC_RERANK_ENABLED=false to disable the semantic stage independently.
  • Provider or production effects: None. No live OpenAI, Supabase, deployment, or production workflow was invoked.

Clinical Governance Preflight

  • Source-backed claims still require linked source verification before clinical use
  • No patient-identifiable document workflow was introduced or expanded without explicit governance approval
  • Supabase target remains Clinical KB Database (sjrfecxgysukkwxsowpy)
  • Service-role keys and private document access remain server-only
  • Demo/synthetic content remains clearly separated from real clinical sources
  • Source metadata, review status, and outdated/unknown-source behavior remain conservative
  • Deployment classification/TGA SaMD impact was checked when clinical decision-support behavior changed
    • This PR changes retrieval ordering only and does not widen the clinical-use boundary.

Notes

  • The live 36/36 retrieval gate is intentionally outstanding and should be run only with explicit provider approval before enabling semantic reranking.

Summary by CodeRabbit

  • New Features

    • Added ambiguity-only semantic reranking for search results, configurable via OPENAI_RERANK_MODEL and RAG_SEMANTIC_RERANK_ENABLED.
    • Introduced per–query-class “feature fusion” weights and richer score explanations (including rankScore and fusion signals).
    • Added offline ranking snapshot generation/validation and snapshot-based weight tuning.
  • Bug Fixes

    • Improved result ordering consistency to prefer rankScore, with updated telemetry and second-stage reranking behavior.
  • Tests

    • Added/expanded coverage for semantic reranking eligibility, fail-open telemetry, ranking config merging, and snapshot tuning determinism.

@supabase

supabase Bot commented Jul 18, 2026

Copy link
Copy Markdown

This pull request has been ignored for the connected project sjrfecxgysukkwxsowpy because there are no changes detected in supabase directory. You can change this behaviour in Project Integrations Settings ↗︎.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.

@coderabbitai

coderabbitai Bot commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 6a5a8be1-1f36-4b7b-8090-32622edbfdfe

📥 Commits

Reviewing files that changed from the base of the PR and between a0780d3 and 44937db.

📒 Files selected for processing (11)
  • scripts/build-ranking-snapshot.ts
  • scripts/lib/ranking-snapshot-builder.ts
  • scripts/lib/ranking-tuning.ts
  • src/lib/rag-cache.ts
  • src/lib/rag-contracts.ts
  • src/lib/rag.ts
  • src/lib/semantic-rerank.ts
  • tests/rag-generation-fingerprint.test.ts
  • tests/rag-second-stage-ranking.test.ts
  • tests/ranking-tuning.test.ts
  • tests/semantic-rerank.test.ts
🚧 Files skipped from review as they are similar to previous changes (6)
  • src/lib/rag-contracts.ts
  • tests/ranking-tuning.test.ts
  • tests/semantic-rerank.test.ts
  • src/lib/semantic-rerank.ts
  • scripts/lib/ranking-tuning.ts
  • src/lib/rag.ts

📝 Walkthrough

Walkthrough

This PR adds offline ranking snapshot generation and tuning, query-class feature-fusion weights, separate rank and confidence scores, and ambiguity-only semantic reranking with strict validation, fail-open behavior, cache awareness, and telemetry across RAG retrieval paths.

Changes

Ranking and semantic reranking

Layer / File(s) Summary
Offline ranking snapshot and tuner
scripts/build-ranking-snapshot.ts, scripts/lib/*, scripts/fixtures/*, scripts/tune-search-weights.ts, tests/ranking-tuning.test.ts, .gitleaksignore
Generates sanitized 36-case snapshots, validates candidates and hard negatives, evaluates ranking metrics, and emits deterministic query-class tuning recommendations.
Feature-fusion ranking scores
src/lib/ranking-config.ts, src/lib/types.ts, src/lib/clinical-search.ts, src/lib/rag.ts, tests/*ranking*.test.ts, tests/retrieval-selection.test.ts
Adds configurable feature-fusion weights and separates unbounded rankScore ordering from clamped finalScore confidence throughout ranking and second-stage reranking.
Semantic reranking configuration and telemetry
.env.example, src/lib/env.ts, src/lib/openai.ts, src/lib/rag-contracts.ts, src/lib/rag-cache.ts, tests/rag-generation-fingerprint.test.ts
Adds reranker configuration, the rerank OpenAI operation, semantic telemetry fields, model-aware cache keys, and default telemetry for cached results.
Ambiguity-only semantic rerank pipeline
src/lib/semantic-rerank.ts, src/lib/rag.ts, tests/semantic-rerank.test.ts
Builds bounded candidate evidence, validates structured reranking responses, records outcomes, fails open on invalid/provider responses, and invokes reranking once across retrieval exit paths.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Search as searchChunksWithTelemetry
  participant Reranker as semanticRerankIfAmbiguous
  participant Provider as SemanticRerankGenerator
  participant Cache as RAG cache
  Search->>Reranker: candidate results and eligibility
  Reranker->>Provider: bounded evidence with structured schema
  Provider-->>Reranker: validated ranking response
  Reranker-->>Search: reordered or unchanged results
  Search->>Cache: cache final results and telemetry
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and clearly captures the new opt-in semantic reranking behavior, even though it doesn’t mention the snapshot tuner work.
Description check ✅ Passed The description matches the template sections and includes verification, risk, rollout, governance, and notes, with missing checks clearly explained.
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.
✨ 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 codex/rag-ranking-main-20260719

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

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d650ecb89b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/lib/semantic-rerank.ts Outdated
Comment thread src/lib/rag-cache.ts
@BigSimmo

Copy link
Copy Markdown
Owner Author

@codex resolve actionable Codex review findings for this pull request and current head using the repository instructions. This is the pull request's single automatic repair pass: do not perform a fresh review, create new standalone findings, or request another review. Work only the existing unresolved Codex threads on the current head. Always fix P0 and P1 findings. For P2 and lower findings, fix only clear, scoped, low-risk issues; otherwise disposition them with a concise reason. After fixing or dispositioning a thread, reply in that thread with as the first line, followed by a concise summary; that marker authorizes the workflow to close that exact thread. If human input or new authorization is required, do not use the marker and leave the thread open with the blocker. Finish only after every actionable thread is fixed or dispositioned and closed, or explicitly left open for a human decision. Do not update the branch from main, address unrelated reviews, broaden scope, or create more than one scoped fix commit. Do not use external APIs, paid services, credentials, dependency changes, or broad refactors unless explicitly authorized. Add targeted tests where behavior changes and run the narrowest relevant validation.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/lib/rag.ts (1)

713-732: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Preserve the computed rank for results without score_explanation.

When the optional explanation is absent, Line 715 leaves it undefined, so the comparator falls back to the original hybrid score and discards every computed second-stage adjustment. Sort using an internal rankScore field, then remove it from the returned result.

🤖 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 `@src/lib/rag.ts` around lines 713 - 732, Update the second-stage rerank
mapping and subsequent sort in the results pipeline to preserve the computed
rank when score_explanation is absent. Store the computed rankScore in an
internal field for every result, use that field in the comparator instead of
falling back to hybrid_score or similarity, then remove the internal field
before returning results while retaining the existing explanation updates.
🤖 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 `@scripts/build-ranking-snapshot.ts`:
- Around line 64-69: Update labelMatches so each normalized label alternative is
matched as a standalone token rather than via arbitrary substring matching,
while preserving case-insensitive OR handling and full-word labels. Add
regression cases covering short abbreviations such as “ed”, “im”, and “po” not
matching embedded characters in unrelated words, and retain matches when they
appear as distinct tokens.

In `@scripts/lib/ranking-tuning.ts`:
- Around line 214-224: Update the hard-negative evaluation around
firstRelevantIndex so cases with no relevant candidate are not counted as
hard-negative ordering failures. Track missing-positive retrieval separately (or
reject/repair invalid snapshots), and ensure high-risk failure and
correctHardNegatives metrics only reflect cases where firstRelevantIndex is
nonnegative, including the agitation-im-po-options and flowchart-next-step
fixtures.
- Around line 125-143: Extend the validation in the ranking snapshot parser
before the `return value as RankingSnapshot` cast to cover every field consumed
by evaluation: require boolean `documentMatch` and sanitization values, validate
labels, hard-negative category and risk fields, and ensure `sourceCaseCount`
equals `cases.length`. Update the `hardNegative` check to reject invalid
non-boolean/null values so only valid hard negatives contribute to
`hardNegativeCount`, while preserving the existing candidate hash, grade,
feature, and minimum-count checks.

In `@src/lib/rag.ts`:
- Around line 729-734: Update the final result-ordering flow in the relevant
reranking function to reassign each result’s score_explanation.finalRank after
every reorder, including the second-stage sort and subsequent semantic rerank.
Ensure the same rank refresh runs when semantic reranking is disabled or fails
open, so finalRank always matches the returned array position.

In `@src/lib/semantic-rerank.ts`:
- Around line 174-178: Update the error handling around providerFailureReason
and the generator catch path to distinguish caller cancellation from provider
failures: when args.signal is aborted, re-throw the cancellation error so
retrieval stops, while retaining "timeout" classification and fail-open behavior
for provider or internal timeout errors.

In `@src/lib/types.ts`:
- Around line 429-438: Update scripts/build-ranking-snapshot.ts to use the exact
runtime components from each record’s fusionSignals as the snapshot inputs for
metadata, clinical, and fixed adjustments. Keep the existing aggregate-field
reconstruction only as a fallback for legacy fixtures that lack fusionSignals,
ensuring tuning evaluates the same scoring function as runtime.

---

Outside diff comments:
In `@src/lib/rag.ts`:
- Around line 713-732: Update the second-stage rerank mapping and subsequent
sort in the results pipeline to preserve the computed rank when
score_explanation is absent. Store the computed rankScore in an internal field
for every result, use that field in the comparator instead of falling back to
hybrid_score or similarity, then remove the internal field before returning
results while retaining the existing explanation updates.
🪄 Autofix (Beta)

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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 321f501c-e664-4ed7-bba5-5fa5534ec086

📥 Commits

Reviewing files that changed from the base of the PR and between 68841ae and d650ecb.

📒 Files selected for processing (21)
  • .env.example
  • scripts/build-ranking-snapshot.ts
  • scripts/fixtures/rag-ranking-candidate-snapshot.v1.json
  • scripts/lib/ranking-tuning.ts
  • scripts/tune-search-weights.ts
  • src/lib/clinical-search.ts
  • src/lib/env.ts
  • src/lib/openai.ts
  • src/lib/rag-cache.ts
  • src/lib/rag-contracts.ts
  • src/lib/rag.ts
  • src/lib/ranking-config.ts
  • src/lib/retrieval-selection.ts
  • src/lib/semantic-rerank.ts
  • src/lib/types.ts
  • tests/clinical-search.test.ts
  • tests/rag-second-stage-ranking.test.ts
  • tests/ranking-config.test.ts
  • tests/ranking-tuning.test.ts
  • tests/retrieval-selection.test.ts
  • tests/semantic-rerank.test.ts

Comment thread scripts/build-ranking-snapshot.ts Outdated
Comment thread scripts/lib/ranking-tuning.ts
Comment thread scripts/lib/ranking-tuning.ts
Comment thread src/lib/rag.ts Outdated
Comment thread src/lib/semantic-rerank.ts
Comment thread src/lib/types.ts
@chatgpt-codex-connector

Copy link
Copy Markdown

Summary

Git / PR

  • Created commit 18c9fcf with message fix(rag): respect semantic rerank cache and abort boundaries.
  • Prepared a new PR via the make_pr tool with title: fix(rag): resolve semantic rerank abort and answer-cache findings.
  • Worktree is clean after commit (git status --short --branch showed ## work).

Testing

  • ⚠️ npm ci — initially failed under Node v20.20.2 because the repo requires Node 24.x.
  • export PATH=/root/.nvm/versions/node/v24.15.0/bin:$PATH; npm ci
  • export PATH=/root/.nvm/versions/node/v24.15.0/bin:$PATH; npm run test -- tests/semantic-rerank.test.ts tests/rag-generation-fingerprint.test.ts
  • export PATH=/root/.nvm/versions/node/v24.15.0/bin:$PATH; npm run typecheck
  • export PATH=/root/.nvm/versions/node/v24.15.0/bin:$PATH; node ./node_modules/eslint/bin/eslint.js src/lib/semantic-rerank.ts src/lib/rag-cache.ts tests/semantic-rerank.test.ts tests/rag-generation-fingerprint.test.ts --max-warnings 0
  • git diff --check

View task →

@BigSimmo
BigSimmo merged commit a871dd7 into main Jul 18, 2026
15 checks passed
@BigSimmo
BigSimmo deleted the codex/rag-ranking-main-20260719 branch July 18, 2026 20:04

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 419ebf9197

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +295 to +297
const reordered = args.results.map((result) =>
bandIds.has(result.id) ? sortedBand[replacementIndex++].result : result,
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve semantic rerank through answer ranking

When RAG_SEMANTIC_RERANK_ENABLED=true and the model reorders an ambiguous answer search, this code only swaps array positions and leaves each result's rankScore, hybrid_score, and answer-evidence inputs unchanged. answerQuestionWithScopeUncoalesced immediately feeds the returned results to rankAnswerEvidence (src/lib/rag.ts lines 3455-3456), which sorts by its own evidence score and then hybrid_score before using original index (src/lib/answer-ranking.ts lines 238-248), so any semantic reorder where those scores differ is discarded before source selection/answer generation. Propagate a semantic rank signal into the downstream ranking or make the answer path preserve semantic order.

Useful? React with 👍 / 👎.

Comment on lines +272 to +276
reasoningEffort: "none",
textVerbosity: "low",
timeoutMs: 3_000,
maxRetries: 0,
signal: args.signal,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Thread the safety identifier into rerank requests

When semantic reranking is enabled for an authenticated owner and OPENAI_SAFETY_IDENTIFIER_SECRET is set, this new Responses request sends the user's query plus retrieved clinical candidate snippets but omits the HMAC safetyIdentifier. The repo's PIA and OpenAI operations docs state authenticated Responses requests should carry that pseudonym while raw owner IDs are never sent (docs/privacy-impact-assessment.md lines 162-164; docs/openai-rag-operations.md lines 55-56), and the existing classifier/answer/summary provider calls already do so. Add ownerId/safetyIdentifier plumbing for semantic rerank so canary traffic keeps the same privacy and abuse-tracing invariant.

Useful? React with 👍 / 👎.

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