Skip to main content

_diffctx/config/
limits.rs

1use once_cell::sync::Lazy;
2
3use crate::config::env_overrides::{read_env_f64, read_env_fraction, read_env_open_fraction};
4
5pub struct AlgorithmLimits {
6    pub max_file_size: usize,
7    pub max_changed_file_size: usize,
8    pub max_fragments: usize,
9    pub max_generated_fragments: usize,
10    pub max_generated_lines: usize,
11    pub skip_expensive_threshold: usize,
12    pub rare_identifier_threshold: usize,
13    pub overhead_per_fragment: u32,
14}
15
16impl Default for AlgorithmLimits {
17    fn default() -> Self {
18        let max_fragments = std::env::var("DIFFCTX_MAX_FRAGMENTS")
19            .ok()
20            .and_then(|v| v.parse::<usize>().ok())
21            .filter(|&v| v >= 1)
22            .unwrap_or(200);
23        Self {
24            max_file_size: 100_000,
25            max_changed_file_size: 5_000_000,
26            max_fragments,
27            max_generated_fragments: 5,
28            max_generated_lines: 30,
29            skip_expensive_threshold: 2000,
30            rare_identifier_threshold: 3,
31            // Measured against real YAML serialization (path/lines/kind/symbol
32            // keys + quoting): actual scaffold runs ~40-45 tokens/fragment,
33            // not 18 - the old estimate let --budget overshoot by ~18%.
34            overhead_per_fragment: 40,
35        }
36    }
37}
38
39/// Hard ceiling for a single `git cat-file` blob read. Sized with headroom
40/// above `max_changed_file_size` (5 MB) so legitimate large changed files still
41/// load, while a pathological multi-hundred-MB blob is drained and rejected
42/// instead of allocated up front.
43pub const MAX_BLOB_READ_BYTES: usize = 16_000_000;
44
45pub const DEFAULT_PPR_ALPHA: f64 = 0.60;
46/// Confirmed by the v4 re-calibration on the EGO pipeline (5x3 grid,
47/// v1 calibration manifest): validated winner (tau, cbf) = (0.12, 0.5)
48/// at min(per_benchmark file_recall) = 0.6532, validation 0.6604.
49/// Surface flat and monotone (top-3 within 0.004) — choice is robust.
50pub const DEFAULT_STOPPING_THRESHOLD: f64 = 0.12;
51pub const DEFAULT_PIPELINE_TIMEOUT_SECONDS: u64 = 300;
52
53pub struct PPRConfig {
54    pub alpha: f64,
55    pub default_seed_epsilon: f64,
56    pub push_scale_factor: usize,
57    pub max_pushes_cap: usize,
58    pub convergence_tolerance: f64,
59    pub forward_blend: f64,
60}
61
62impl Default for PPRConfig {
63    fn default() -> Self {
64        Self {
65            alpha: DEFAULT_PPR_ALPHA,
66            default_seed_epsilon: 0.1,
67            push_scale_factor: 100,
68            max_pushes_cap: 2_000_000,
69            convergence_tolerance: 1e-4,
70            forward_blend: 0.4,
71        }
72    }
73}
74
75pub struct LexicalConfig {
76    pub min_similarity: f64,
77    pub top_k_neighbors: usize,
78    pub max_df_ratio: f64,
79    pub min_idf: f64,
80    pub max_postings: usize,
81    pub weight_min: f64,
82    pub weight_max: f64,
83    pub backward_factor: f64,
84}
85
86impl Default for LexicalConfig {
87    fn default() -> Self {
88        Self {
89            min_similarity: 0.30,
90            top_k_neighbors: 5,
91            max_df_ratio: 0.15,
92            min_idf: 2.0,
93            max_postings: 100,
94            weight_min: 0.05,
95            weight_max: 0.15,
96            backward_factor: 0.5,
97        }
98    }
99}
100
101pub struct CochangeConfig {
102    pub min_count: usize,
103    pub max_files_per_commit: usize,
104    pub commits_limit: usize,
105    pub log_scale_factor: f64,
106}
107
108impl Default for CochangeConfig {
109    fn default() -> Self {
110        Self {
111            min_count: 2,
112            max_files_per_commit: 30,
113            commits_limit: 500,
114            log_scale_factor: 0.1,
115        }
116    }
117}
118
119pub struct SiblingConfig {
120    pub max_files_per_dir: usize,
121}
122
123impl Default for SiblingConfig {
124    fn default() -> Self {
125        Self {
126            max_files_per_dir: 20,
127        }
128    }
129}
130
131pub struct UtilityConfig {
132    pub eta: f64,
133    pub structural_bonus_weight: f64,
134    pub r_cap_sigma: f64,
135    pub proximity_decay: f64,
136}
137
138impl Default for UtilityConfig {
139    fn default() -> Self {
140        Self {
141            eta: 0.20,
142            structural_bonus_weight: 0.10,
143            r_cap_sigma: 2.0,
144            proximity_decay: 0.30,
145        }
146    }
147}
148
149pub static LIMITS: Lazy<AlgorithmLimits> = Lazy::new(AlgorithmLimits::default);
150pub static PPR: Lazy<PPRConfig> = Lazy::new(|| PPRConfig {
151    alpha: read_env_open_fraction("DIFFCTX_OP_PPR_ALPHA", DEFAULT_PPR_ALPHA),
152    forward_blend: read_env_fraction("DIFFCTX_OP_PPR_FORWARD_BLEND", 0.4),
153    ..PPRConfig::default()
154});
155pub static LEXICAL: Lazy<LexicalConfig> = Lazy::new(LexicalConfig::default);
156pub static COCHANGE: Lazy<CochangeConfig> = Lazy::new(CochangeConfig::default);
157pub static SIBLING: Lazy<SiblingConfig> = Lazy::new(SiblingConfig::default);
158pub static UTILITY: Lazy<UtilityConfig> = Lazy::new(|| UtilityConfig {
159    eta: read_env_f64("DIFFCTX_OP_UTILITY_ETA", 0.20),
160    structural_bonus_weight: read_env_f64("DIFFCTX_OP_UTILITY_STRUCTURAL_BONUS_WEIGHT", 0.10),
161    r_cap_sigma: read_env_f64("DIFFCTX_OP_UTILITY_R_CAP_SIGMA", 2.0),
162    proximity_decay: read_env_f64("DIFFCTX_OP_UTILITY_PROXIMITY_DECAY", 0.30),
163});