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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
//! `[code_search]` config table — knobs for the semantic code-search tier.
//!
//! Split from `v1.rs` to keep both files under the 1000-line cap. Every field has
//! `#[serde(default)]` so adding this table never breaks an older TOML file, and the whole
//! table is inert unless the `code-search` cargo feature is compiled in.
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use super::RerankerConfig;
/// `[code_search]` table. Chunk + embed source code for the `code` tool's `semantic` mode.
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct CodeSearchConfig {
/// Master switch. Only meaningful when the `code-search` cargo feature is compiled in.
/// Default `true` — a `code-search` build chunks source + builds the BM25 keyword lane on scan
/// unless disabled. Embedding is a separate opt-in gated by `embed` (off by default).
#[serde(default = "CodeSearchConfig::default_enabled")]
pub enabled: bool,
/// Maximum chunk size in characters. Chunks longer than this are split into overlapping
/// windows. Mirrors the document tier's `max_characters`.
#[serde(default = "CodeSearchConfig::default_max_characters")]
#[schemars(range(min = 64))]
pub max_characters: usize,
/// Overlap between split windows, in characters. Mirrors the document tier's `overlap`.
#[serde(default = "CodeSearchConfig::default_overlap")]
#[schemars(range(min = 0))]
pub overlap: usize,
/// Hard cap on chunks indexed per source file. Mirrors the document tier's
/// `max_chunks_per_document`. A pathological input — a generated parser table, a checked-in
/// bundle, a file of tens of thousands of tiny symbols — otherwise fans one file out into a
/// chunk count bounded only by `[scan] max_file_bytes`, and every chunk costs a BM25
/// `code_bm25_by_path` entry carrying its full term list, a LanceDB row, and (when `embed`) an
/// ONNX embed. Files over the cap are still chunked and cached (the `.chunk.msgpack` sidecar is
/// written, so outline and grep are unaffected) but contribute no keyword postings and no
/// vector rows, with a warning.
#[serde(default = "CodeSearchConfig::default_max_chunks_per_file")]
#[schemars(range(min = 1))]
pub max_chunks_per_file: usize,
/// Generate embeddings (`true`) or chunk-only without vector storage (`false`). **Default
/// `false`**: local embeddings on *code* aren't worth their cost — code is embedded with a
/// general English model and the one real win (NL→symbol) is already served by the BM25 keyword
/// lane over the same text. With embeddings off the `.chunk.msgpack` cache is still written and
/// the BM25 keyword lane still works (so `lane: "keyword"` functions), but no LanceDB
/// rows land — the semantic (vector) lane returns nothing and no ONNX model is downloaded. Flip
/// to `true` only if you specifically want vector search over code.
#[serde(default = "CodeSearchConfig::default_embed")]
pub embed: bool,
/// Allow-list of globs for embedding: when non-empty only matching files are embedded; the rest
/// are still chunked + keyword-searchable. Empty (default) = every chunked file is embedded.
/// Only consulted when `embed = true`.
#[serde(default)]
#[schemars(inner(length(min = 1)))]
pub embed_include: Vec<String>,
/// Glob patterns (repo-relative, forward-slash) for files that are still chunked + indexed
/// (BM25 / code-map) but **never** embedded. Use it to keep vectors off large generated or
/// vendored files while leaving them searchable by keyword. Beats `embed_include`. Empty by
/// default. Only consulted when `embed = true`; changing it removes the vector rows of newly
/// excluded files on the next scan.
#[serde(default)]
#[schemars(inner(length(min = 1)))]
pub embed_exclude: Vec<String>,
/// Optional cross-encoder rerank of the fused `semantic` hits. Reuses the same xberg reranker
/// as the documents tier. Off by default — the first call downloads an ONNX model. Enable via
/// `[code_search.reranker] enabled = true` or the per-query `rerank_enabled` override.
#[serde(default)]
pub reranker: RerankerConfig,
}
impl CodeSearchConfig {
/// Cross-field validation the per-field JSON-schema bounds can't express: `overlap` must be
/// strictly less than `max_characters`. When `overlap >= max_characters` the chunker's
/// `split_oversized` computes `step = max_characters.saturating_sub(overlap).max(1) = 1`,
/// emitting one degenerate window per character of an oversized chunk instead of a handful.
/// Returns a human-readable error naming both offending values on violation.
pub fn validate(&self) -> Result<(), String> {
if self.overlap >= self.max_characters {
return Err(format!(
"[code_search] overlap ({}) must be less than max_characters ({}); an overlap \
>= max_characters collapses the chunker step to 1 character",
self.overlap, self.max_characters
));
}
Ok(())
}
fn default_enabled() -> bool {
true
}
fn default_max_characters() -> usize {
1500
}
fn default_overlap() -> usize {
200
}
fn default_max_chunks_per_file() -> usize {
2000
}
fn default_embed() -> bool {
false
}
}
impl Default for CodeSearchConfig {
fn default() -> Self {
Self {
enabled: Self::default_enabled(),
max_characters: Self::default_max_characters(),
overlap: Self::default_overlap(),
max_chunks_per_file: Self::default_max_chunks_per_file(),
embed: Self::default_embed(),
embed_include: Vec::new(),
embed_exclude: Vec::new(),
reranker: RerankerConfig::default(),
}
}
}