feat(cli): kebab search --trace flag + wire trace + pretty print (fb-37)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
th-kim0823
2026-05-10 13:08:48 +09:00
parent 69037c313a
commit 72c227af23
2 changed files with 77 additions and 2 deletions

View File

@@ -163,6 +163,13 @@ enum Cmd {
/// p9-fb-36: filter to a single doc by id.
#[arg(long)]
doc_id: Option<String>,
/// p9-fb-37: emit pre-fusion lexical / vector / RRF candidate
/// lists + per-stage timing in the response. Bypasses cache
/// (debug intent — fresh run guaranteed). Requires embeddings
/// to be enabled.
#[arg(long)]
trace: bool,
},
/// Retrieval-augmented question answering.
@@ -669,6 +676,7 @@ fn run(cli: &Cli) -> anyhow::Result<()> {
media,
ingested_after,
doc_id,
trace,
} => {
let cfg = kebab_config::Config::load(cli.config.as_deref())?;
@@ -732,7 +740,7 @@ fn run(cli: &Cli) -> anyhow::Result<()> {
max_tokens: *max_tokens,
snippet_chars: *snippet_chars,
cursor: cursor.clone(),
trace: false,
trace: *trace,
};
// p9-fb-34: budget-aware path. --no-cache still bypasses the
// App-level LRU; wire wrapper applies regardless.
@@ -790,6 +798,22 @@ fn run(cli: &Cli) -> anyhow::Result<()> {
let next = resp.next_cursor.as_deref().unwrap_or("(none)");
eprintln!("[truncated; use --cursor {next} for the next page]");
}
if *trace {
if let Some(t) = &resp.trace {
eprintln!();
eprintln!("Trace:");
eprintln!(" lexical ({} hits, {}ms):", t.lexical.len(), t.timing.lexical_ms);
for c in t.lexical.iter().take(3) {
eprintln!(" rank={} score={:.4} chunk={}", c.rank, c.score, c.chunk_id.0);
}
eprintln!(" vector ({} hits, {}ms):", t.vector.len(), t.timing.vector_ms);
for c in t.vector.iter().take(3) {
eprintln!(" rank={} score={:.4} chunk={}", c.rank, c.score, c.chunk_id.0);
}
eprintln!(" fusion ({} inputs, {}ms)", t.rrf_inputs.len(), t.timing.fusion_ms);
eprintln!(" total: {}ms", t.timing.total_ms);
}
}
}
Ok(())
}