Skip to content

perf: overlap query embedding with lexical retrieval + typeahead catalog cache (round 2, phase 5)#486

Merged
BigSimmo merged 1 commit into
claude/perf-r2-plan-cache-migrationfrom
claude/perf-r2-eval-gated
Jul 11, 2026
Merged

perf: overlap query embedding with lexical retrieval + typeahead catalog cache (round 2, phase 5)#486
BigSimmo merged 1 commit into
claude/perf-r2-plan-cache-migrationfrom
claude/perf-r2-eval-gated

Conversation

@BigSimmo

Copy link
Copy Markdown
Owner

Summary

Phase 5 of the round-2 performance initiative (stacked on #484). Retrieval-adjacent latency work, gated on the golden evals per docs/capacity-review.md — provider runs were user-approved.

  1. Speculative embedding. The embedding round trip started only after every lexical layer had run, making it fully additive on the cold hybrid path. It now starts alongside the lexical RPCs — with a correctness guard the original plan missed: the lexical layers can enrich expandedQuery with candidate metadata before the embed site, so the speculative result is consumed only when the query is still byte-identical there; enriched queries take the serial path exactly as before. Results cannot change by construction. Cost: one wasted cached/coalesced embedding call when a lexical fast path wins (~72% of golden queries; fractions of a cent each). Never speculates for typeahead (lexical-only) or source-only retrieval.
  2. Per-owner typeahead catalog cache (60s TTL). Signed-in typeahead re-read the medications/services/forms catalogs from Supabase on every debounced keystroke (3 reads/keystroke). Mapped records now cache per owner; ranking still runs per keystroke, so ranked output within the TTL is identical.
  3. OPENAI_FAST_ANSWER_TIMEOUT_MS (optional): a tighter fast-tier-only deadline so a hung fast generation can fall back/escalate before the full 30s answer timeout. Ships unset — behavior unchanged. Tune in production against the Server-Timing / rag_queries generation-latency signal (perf: bundle analyzer + Server-Timing measurement rail (perf round 2, phase 0) #458) rather than local noise.

Deliberately not done: trimming the per-keystroke lexical enrichment layers (ranking-metadata / memory-boost / visual-evidence). Memory boosting affects typeahead ordering — a real behavior change with poor risk/benefit after round 1 already removed the per-keystroke embedding.

Clinical governance preflight

  • eval:retrieval (golden set, live) — baseline vs post-change BIT-IDENTICAL correctness: 36 cases, document/content recall@5 = 1, mrr@10 0.8148, content_mrr@10 0.9279, identical retrieval strategy/plan/layer counts, failed_cases=0 (p90 latency 10,407 → 9,658 ms)
  • eval:quality sanity (44 cases, live): citation failures 0, numeric grounding failures 0, governance danger failures 0, grounded-supported 0.9667, P95 16.7s. One case (clozapine-fbc-acronym-threshold) failed its 20s latency threshold on the strong route with correct retrieval ("missing: none") — consistent with local-box latency noise; no generation behavior changes by default in this diff
  • tsc / eslint / prettier / targeted vitest green
  • Source-backed claims, governance conservatism, Supabase target, key handling: unchanged
  • No patient-identifiable workflow introduced

🤖 Generated with Claude Code

…head catalogs

Round-2 phase 5 (eval-gated; user-approved provider runs).

- Speculative embedding: on the cold hybrid path the embedding round trip
  started only after every lexical layer had run, making it fully additive.
  It now starts alongside the lexical RPCs — but is consumed ONLY when
  expandedQuery is still byte-identical at the embed site, because the lexical
  layers can enrich the query with candidate metadata before embedding.
  Enriched queries take the serial path exactly as before, so retrieval
  results cannot change by construction. Cost: one wasted cached/coalesced
  embedding call when a lexical fast path wins (embedding_skipped_rate is
  ~0.72 on the golden set; embeddings are fractions of a cent). Never
  speculates for lexical-only (typeahead) or source-only retrieval.
- Per-owner typeahead catalog cache (60s TTL): signed-in typeahead re-read the
  medications/services/forms catalogs from Supabase on every debounced
  keystroke; the mapped records are now cached per owner while ranking still
  runs per keystroke, so ranked output within the TTL is identical.
- OPENAI_FAST_ANSWER_TIMEOUT_MS: optional tighter deadline for the fast answer
  tier only, so a hung fast generation can fall back / escalate before the
  full 30s answer timeout. Ships UNSET (behavior unchanged) — to be tuned in
  production against the Server-Timing/rag_queries generation-latency signal
  rather than local noise.

Deliberately NOT done: trimming the per-keystroke lexical enrichment layers
(ranking-metadata / memory-boost / visual-evidence) — memory boosting affects
typeahead ORDERING, a real behavior change with poor risk/benefit after
round 1 already removed the per-keystroke embedding.

Eval gate (live Supabase + OpenAI, user-approved):
- eval:retrieval baseline vs post-change: BIT-IDENTICAL correctness — 36
  cases, document/content recall@5 = 1, mrr@10 0.8148, content_mrr@10 0.9279,
  identical retrieval strategy/plan/layer counts, failed_cases 0
  (p90 latency 10407 -> 9658 ms).
- eval:quality sanity (44 cases): citation failures 0, numeric grounding
  failures 0, governance danger failures 0, grounded-supported 0.9667, P95
  16.7s. One case (clozapine-fbc-acronym-threshold) failed its 20s latency
  threshold on the strong route with correct retrieval — consistent with
  local-box latency noise; no generation behavior changes by default in this
  diff.

Also: tsc, eslint, prettier, targeted vitest green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 4e7998d0-e10f-4a35-94d8-44b75c86aca5

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/perf-r2-eval-gated
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch claude/perf-r2-eval-gated

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

@supabase

supabase Bot commented Jul 11, 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 ↗︎.

@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: 901fae59ec

ℹ️ 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/rag.ts
Comment on lines +3434 to +3435
const speculativeEmbedding =
sourceOnlyRetrieval || args.lexicalOnly ? null : embedTextWithTelemetry(speculativeEmbeddingQuery);

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 Gate speculative embeddings on request cancellation

When an answer request is cancelled while the lexical Supabase work is still running, request.signal is checked after that work and the function exits, but this line has already started embedTextWithTelemetry() without any abort signal. The added .catch() only hides the rejection; it cannot cancel the OpenAI request, so abandoned answer/search requests can still spend embedding calls that the previous serial path would never have started. A focused abort test where the signal fires during searchTextChunkCandidates would catch this.

Useful? React with 👍 / 👎.

@BigSimmo
BigSimmo merged commit 471099c into claude/perf-r2-plan-cache-migration Jul 11, 2026
4 checks passed
@BigSimmo
BigSimmo deleted the claude/perf-r2-eval-gated branch July 13, 2026 16:35
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