You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Add a cross-encoder reranker backend to the sidecar: a new rerank(query, [candidate]) -> [score] op that returns an absolute relevance score in [0,1] per (query, candidate) pair. This is the sidecar half of RFC-021 — Cross-Encoder Relevance Arbiter (travsr repo, docs/rfcs/RFC-021-cross-encoder-relevance-arbiter.md); the travsr-mcp seed-selection/confidence integration is tracked separately on the travsr side.
Builds directly on #6. This is a sibling backend, not a new subsystem — it rides the EmbedBackend/BackendFactory capability resolver, the family tag, and the shared encode.rs pipeline that #6 introduces. It should land after (or alongside) #6.
Why
get_context / travsr ask return confidently-labelled irrelevant seeds for broad/off-topic NL queries. Reproduced on the travsr repo with a full arctic-embed-m index:
query: "delete all user accounts and drop the database"
→ confidence: STRONG, 80 nodes
fn:Guard.drop (Rust Drop impl) [score 1.00]
fn:SqliteStore.delete_file [score 0.82]
Root cause is architectural: the confidence gate approximates relevance with geometric signals (BM25 term overlap + bi-encoder cosine), both token-overlap-driven. A bi-encoder encodes query and candidate separately, so it cannot tell "the user's words are also symbol names" from "the user's intent matches this code" (the RFC-019 comment records BGE scoring RemovePod at cosine 0.751 to GetWarningsForPod purely on shared tokens). A cross-encoder encodes the pair jointly and produces an absolute, model-agnostic relevance score — the one signal the geometric stack lacks.
Model
cross-encoder/ms-marco-MiniLM-L6-v2, tagged family = "bert".
Property
Value
Rationale
Architecture
standard BERT, 6 layers, 22.7M params
Only standard BERT runs on tract. DeBERTa/JinaBERT/ModernBERT rerankers fail at graph load (#6) → unavailable on the default + OCI aarch64 builds. family="bert" → TractFactory.can_run already accepts it; ORT-accelerated for free on CoreML/CUDA builds.
Accuracy
NDCG@10 74.30 (TREC DL19)
Ties L12-v2 (74.31); distilled from L12.
Throughput
~1800 docs/s CPU
2x L12-v2.
Primary artifact
model_fp16.onnx = 45.6 MB
Under the 60 MB budget; best tract compatibility below fp32.
ORT-target artifact
model_int8.onnx = 23 MB
int8 is solid on ORT; per-target artifacts already supported by #6.
Rejected
fp32 (91 MB, over budget); L12 (fp16 ~66 MB → over budget → would force risky int8-on-tract)
—
Precision rule: fp16 on tract targets, int8 on ORT targets. tract's int8 QDQ support is the weakest path; fp16 avoids it while staying under budget.
Sequence length capped at 256 tokens. Candidate text = signature + ~10-line skeleton (supplied by the caller). Seq length is the dominant latency lever.
Batched: all K candidates in one forward pass (batch dim = K), K ~ 20-40.
Sidecar protocol
New op over the existing line protocol (same framing as embed / cancel-sentinel):
REQUEST : {"op":"rerank","query":"<str>","candidates":["<str>", ...]} // len <= K
RESPONSE: {"scores":[0.83, 0.02, 0.41, ...]} // aligned to candidates, [0,1]
One process, one model load — reuses the already-spawned sidecar. No second sidecar.
Distribution / catalog
New catalog entry (mirrors the embed model catalog) with family = "bert", artifact URLs for model_fp16.onnx (tract targets) and model_int8.onnx (ORT targets), tokenizer vocab.
Downloaded to ~/.travsr/models/<backend>/ via the existing init flow (travsr side wires a rerank init / bundles default-on per RFC-021 decision).
Summary
Add a cross-encoder reranker backend to the sidecar: a new
rerank(query, [candidate]) -> [score]op that returns an absolute relevance score in[0,1]per(query, candidate)pair. This is the sidecar half of RFC-021 — Cross-Encoder Relevance Arbiter (travsr repo,docs/rfcs/RFC-021-cross-encoder-relevance-arbiter.md); thetravsr-mcpseed-selection/confidence integration is tracked separately on the travsr side.Builds directly on #6. This is a sibling backend, not a new subsystem — it rides the
EmbedBackend/BackendFactorycapability resolver, thefamilytag, and the sharedencode.rspipeline that #6 introduces. It should land after (or alongside) #6.Why
get_context/travsr askreturn confidently-labelled irrelevant seeds for broad/off-topic NL queries. Reproduced on the travsr repo with a full arctic-embed-m index:Root cause is architectural: the confidence gate approximates relevance with geometric signals (BM25 term overlap + bi-encoder cosine), both token-overlap-driven. A bi-encoder encodes query and candidate separately, so it cannot tell "the user's words are also symbol names" from "the user's intent matches this code" (the RFC-019 comment records BGE scoring
RemovePodat cosine 0.751 toGetWarningsForPodpurely on shared tokens). A cross-encoder encodes the pair jointly and produces an absolute, model-agnostic relevance score — the one signal the geometric stack lacks.Model
cross-encoder/ms-marco-MiniLM-L6-v2, taggedfamily = "bert".tract. DeBERTa/JinaBERT/ModernBERT rerankers fail at graph load (#6) → unavailable on the default + OCI aarch64 builds.family="bert"→TractFactory.can_runalready accepts it; ORT-accelerated for free on CoreML/CUDA builds.L12-v2(74.31); distilled from L12.L12-v2.model_fp16.onnx= 45.6 MBtractcompatibility below fp32.model_int8.onnx= 23 MBL12(fp16 ~66 MB → over budget → would force risky int8-on-tract)Precision rule: fp16 on
tracttargets, int8 on ORT targets.tract's int8 QDQ support is the weakest path; fp16 avoids it while staying under budget.Architecture (rides #6)
encode.rstokenize/truncate/pad. The only structural differences fromEmbedBackend:[CLS] query [SEP] candidate [SEP](+token_type_ids);family— no new resolver. ARerankFactoryper engine mirrorsEmbedFactory(TractRerankFactoryallowlist includesbert/minilm;OrtRerankFactory.can_run= true).signature + ~10-line skeleton(supplied by the caller). Seq length is the dominant latency lever.Sidecar protocol
New op over the existing line protocol (same framing as embed / cancel-sentinel):
One process, one model load — reuses the already-spawned sidecar. No second sidecar.
Distribution / catalog
family = "bert", artifact URLs formodel_fp16.onnx(tract targets) andmodel_int8.onnx(ORT targets), tokenizer vocab.~/.travsr/models/<backend>/via the existinginitflow (travsr side wires arerank init/ bundles default-on per RFC-021 decision).model.tomlcarriesfamily/requires_engineso a model the installed sidecar can't run is refused at selection time ([travsr-embed] GPU inference via ORT engine cascade with tract CPU fallback #6 UX guard pattern).Acceptance criteria
RerankBackendtrait +rerankop implemented; selected by the [travsr-embed] GPU inference via ORT engine cascade with tract CPU fallback #6 capability resolver viafamily.family="bert"reranker runs on the default tract build and OCI aarch64; ORT-accelerated on CoreML/CUDA builds.model_fp16.onnx(45.6 MB); ORT builds usemodel_int8.onnx(23 MB); cross-backend parity >= 0.999 on a fixed pair set.rerank(query, candidates)returns scores aligned to input order, in[0,1], batched in one forward pass.encode.rsused for tokenization (pair input +token_type_ids); only the head differs from the embed path.cargo testgreen on x86_64 and aarch64.familythe installed sidecar can't run is refused at selection time (reuses [travsr-embed] GPU inference via ORT engine cascade with tract CPU fallback #6 capability handshake).Out of scope
travsr-mcpbuild_seed_set/classify_confidence/askintegration (tracked on the travsr side, RFC-021 sections 6-7).Ref: RFC-021 (travsr repo). Depends on #6.