lean_ctx_server/tools/
ctx_context.rs1use crate::tools::CrpMode;
3use lean_ctx::core::cache::SessionCache;
4
5pub fn handle_status(cache: &SessionCache, turn_count: usize, crp_mode: CrpMode) -> String {
6 let entries = cache.get_all_entries();
7 let mut result = Vec::new();
8
9 result.push(format!("Multi-turn context (turn {turn_count}):"));
10 result.push(format!(" Cached files: {}", entries.len()));
11
12 let total_tokens: usize = entries.iter().map(|(_, e)| e.original_tokens).sum();
13 let total_reads: u32 = entries.iter().map(|(_, e)| e.read_count()).sum();
14 result.push(format!(" Total original tokens: {total_tokens}"));
15 result.push(format!(" Total reads: {total_reads}"));
16
17 let frequent: Vec<_> = entries.iter().filter(|(_, e)| e.read_count() > 1).collect();
18 if !frequent.is_empty() {
19 result.push(format!("\n Frequently accessed ({}):", frequent.len()));
20 for (path, entry) in &frequent {
21 result.push(format!(
22 " {} ({}x, {} tok)",
23 lean_ctx::core::protocol::shorten_path(path),
24 entry.read_count(),
25 entry.original_tokens
26 ));
27 }
28 }
29
30 let mode_label = match crp_mode {
31 CrpMode::Off => "off",
32 CrpMode::Compact => "compact",
33 CrpMode::Tdd => "tdd",
34 };
35 result.push(format!("\n CRP mode: {mode_label}"));
36
37 let complexity = lean_ctx::core::adaptive::classify_from_context(cache);
38 result.push(format!("\n {}", complexity.encoded_suffix()));
39
40 let hints = generate_prefill_hints(cache);
41 if !hints.is_empty() {
42 result.push("\nSMART HINTS:".to_string());
43 for hint in &hints {
44 result.push(format!(" → {hint}"));
45 }
46 }
47
48 result.join("\n")
49}
50
51fn generate_prefill_hints(cache: &SessionCache) -> Vec<String> {
52 let entries = cache.get_all_entries();
53 let mut hints = Vec::new();
54
55 let read_heavy: Vec<_> = entries
56 .iter()
57 .filter(|(_, e)| e.read_count() >= 3 && e.original_tokens > 500)
58 .collect();
59 for (path, entry) in &read_heavy {
60 let short = lean_ctx::core::protocol::shorten_path(path);
61 hints.push(format!(
62 "{short} read {}x ({} tok) — consider mode=map for future reads",
63 entry.read_count(),
64 entry.original_tokens
65 ));
66 }
67
68 let large: Vec<_> = entries
69 .iter()
70 .filter(|(_, e)| e.original_tokens > 2000 && e.read_count() <= 1)
71 .collect();
72 for (path, entry) in &large {
73 let short = lean_ctx::core::protocol::shorten_path(path);
74 hints.push(format!(
75 "{short} is large ({} tok) — consider mode=signatures or aggressive",
76 entry.original_tokens
77 ));
78 }
79
80 let stale_count = entries.iter().filter(|(_, e)| e.read_count() == 1).count();
81 if stale_count > 5 {
82 hints.push(format!(
83 "{stale_count} files read only once — ctx_control(action=\"evict\", targets=…) frees their context"
84 ));
85 }
86
87 hints.truncate(5);
88 hints
89}