1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
//! lunaris-rerank — trait + NoopReranker for the v0 recall hot path (RETRIEVE-06).
//!
//! v0.4 N-03 cutover: this crate slimmed from "default backend host" to
//! "trait + Noop seam." The concrete cross-encoders moved out:
//!
//! - `BgeRerankerV2M3` (candle) is deleted. The replacement is
//! `lunaris_llamacpp::LlamaCppReranker` (llama.cpp + bge-reranker-v2-m3
//! FP32, sigmoid output ∈ [0, 1]).
//! - `FastembedReranker` (ORT) and the `fastembed_exec` EP helper are deleted
//! with the rest of the fastembed transitive surface.
//!
//! What stays:
//!
//! - [`Reranker`] trait — async, dyn-compatible. Implemented by
//! `NativeReranker` (default), `NativeQuantizedReranker` (Q4 GGUF), and
//! downstream BYO impls.
//! - [`RerankCandidate`] DTO — input/output of `Reranker::rerank`.
//! - [`NoopReranker`] — passthrough fallback for the RETRIEVE-06 contract.
//!
//! ## Cold-start contract
//!
//! The umbrella `Lunaris::open(url)` catches `Err` from
//! `NativeReranker::open` (cache miss) and substitutes [`NoopReranker`],
//! emitting `tracing::warn!` so the operator sees the degradation. Callers
//! wire their own reranker via `Lunaris::with_reranker(reranker)`.
//!
//! ## Latency budget
//!
//! Per blueprint §4.2 the budget is 12 ms p50 / 35 ms p99 on CPU.
use async_trait;
use LunarisError;
use ;
pub use NoopReranker;
/// One pre-rerank candidate. The operator hydrates the chunk text BEFORE
/// calling the reranker (see `lunaris_retrieve::hydrate::partial_hydrate_text`)
/// so the cross-encoder can pair-encode `(query, doc.text)` for scoring.
///
/// `score` is the upstream operator's score — the reranker REPLACES this with
/// its own logit so downstream `.top(n)` ranks by cross-encoder relevance.
/// Async cross-encoder reranker.
///
/// Implementors MUST return exactly `docs.len()` items — preserving the input
/// set, just re-ordered + re-scored. The retriever's `top(n)` modifier
/// truncates downstream so the reranker doesn't need to know the user-facing k.
///
/// `applies()` reports whether this impl actually applies a model pass (true)
/// or is a NO-OP passthrough (false). The DSL operator reads this to set
/// `Hit { rerank_applied }` so callers can tell whether they got the budgeted
/// 12 ms cross-encoder pass or the degraded path.