refactor: rename Mode variants C/A to Lexical/Hybrid (closes #2)
The retrieval-mode enum used opaque single letters `Mode { C, A }`;
neither the identifier nor the operator-facing `--mode a` token
conveyed what the mode does. Rename to self-describing names that map
directly onto the glossary definitions:
Mode::C -> Mode::Lexical (BM25 + tag filter, offline, no IONOS)
Mode::A -> Mode::Hybrid (lexical u semantic -> RRF -> rerank)
Decisions taken (the issue's open questions, resolved):
- CLI canonical tokens are now `lexical` / `hybrid`; the default is
`lexical`. The legacy `c` / `a` tokens stay accepted as
case-insensitive input aliases (zero cost; protects hand-typed
invocations). A unit test pins the alias contract.
- The diagnostics `mode` string (Debug-derived) now emits
"Lexical"/"Hybrid". Verified safe: no downstream consumer parses it.
doctate is the future integrator and embeds the library (the `Mode`
enum), not the JSON string (design spec §103).
- The reserved, out-of-scope generative tier (spec's "Mode B") keeps
conceptual room: a future `Mode::Generative` does not collide with
the retrieval-strategy names Lexical/Hybrid.
Surface: enum + FromStr, CLI defaults and `eval --mode both`
expansion, the private `collect_mode_c`/`collect_mode_a` methods (now
`collect_lexical`/`collect_hybrid`) and their comments, the two
pipeline mode test files (renamed), the eval CLI test, the glossary
(old letter forms demoted to Avoid lists), and the spec CLI synopsis.
closes #2
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+11
-11
@@ -38,8 +38,8 @@ impl Pipeline {
|
||||
m
|
||||
};
|
||||
// Build the semantic index only if EVERY corpus entry already has a
|
||||
// cached embedding; otherwise leave it `None` so Mode A degrades to C
|
||||
// rather than mispairing partial vectors.
|
||||
// cached embedding; otherwise leave it `None` so Mode Hybrid degrades to
|
||||
// Lexical rather than mispairing partial vectors.
|
||||
let vector = match EmbeddingStore::open(&cfg.index_dir, &cfg.embed_model) {
|
||||
Ok(store) if entries.iter().all(|e| store.has(&e.text)) => {
|
||||
let vecs: Option<Vec<Vec<f32>>> =
|
||||
@@ -108,9 +108,9 @@ impl Pipeline {
|
||||
})
|
||||
}
|
||||
|
||||
/// Mode C candidate collection: lexical pool per segment. This is the
|
||||
/// exact original behavior; it is also the degrade fallback for Mode A.
|
||||
fn collect_mode_c(&self, segments: &[String]) -> Vec<Candidate> {
|
||||
/// Mode Lexical candidate collection: lexical pool per segment. This is the
|
||||
/// exact original behavior; it is also the degrade fallback for Mode Hybrid.
|
||||
fn collect_lexical(&self, segments: &[String]) -> Vec<Candidate> {
|
||||
let mut candidates: Vec<Candidate> = Vec::new();
|
||||
for (si, seg) in segments.iter().enumerate() {
|
||||
let lex = self.lexical.search(seg, self.pool_size).unwrap_or_default();
|
||||
@@ -131,11 +131,11 @@ impl Pipeline {
|
||||
candidates
|
||||
}
|
||||
|
||||
/// Mode A candidate collection: per segment, fuse a lexical pool with a
|
||||
/// Mode Hybrid candidate collection: per segment, fuse a lexical pool with a
|
||||
/// semantic pool (IONOS embedding + in-RAM vector search) via RRF, then
|
||||
/// cross-encoder rerank the fused pool. ANY error (IONOS unreachable, no
|
||||
/// vector index, embed/rerank failure) propagates so `suggest` degrades.
|
||||
fn collect_mode_a(&self, segments: &[String], calls: &mut usize)
|
||||
fn collect_hybrid(&self, segments: &[String], calls: &mut usize)
|
||||
-> Result<Vec<Candidate>, AppError> {
|
||||
let client = IonosClient::new(&self.cfg.ionos_base_url, &self.cfg.token_path)?;
|
||||
let vector = self.vector.as_ref()
|
||||
@@ -199,14 +199,14 @@ impl Pipeline {
|
||||
let mut degraded = false;
|
||||
|
||||
let candidates: Vec<Candidate> = match mode {
|
||||
Mode::C => self.collect_mode_c(&segments),
|
||||
Mode::A => match self.collect_mode_a(&segments, &mut ionos_calls) {
|
||||
Mode::Lexical => self.collect_lexical(&segments),
|
||||
Mode::Hybrid => match self.collect_hybrid(&segments, &mut ionos_calls) {
|
||||
Ok(c) => c,
|
||||
Err(_) => {
|
||||
// Total degradation: any failure → Mode C fallback,
|
||||
// Total degradation: any failure → Mode Lexical fallback,
|
||||
// request still answered, never panics.
|
||||
degraded = true;
|
||||
self.collect_mode_c(&segments)
|
||||
self.collect_lexical(&segments)
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user