lean_ctx/tools/
search_hook.rs1use std::sync::atomic::{AtomicUsize, Ordering};
4
5use crate::core::context_kernel::kernel_config;
6use crate::tools::search_kernel;
7
8static SEARCHES_RECORDED: AtomicUsize = AtomicUsize::new(0);
9static REPEATED_WARNED: AtomicUsize = AtomicUsize::new(0);
10
11#[derive(Debug, Clone, Default, serde::Serialize)]
13pub struct SearchHookSummary {
14 pub searches_recorded: usize,
16 pub repeated_queries_warned: usize,
18}
19
20pub fn on_search(query: &str, action: &str, result_count: usize, tokens: usize) {
22 if !kernel_config::is_enabled() {
23 return;
24 }
25 let _ = action;
26 search_kernel::record_search(query, result_count, tokens);
27 SEARCHES_RECORDED.fetch_add(1, Ordering::Relaxed);
28}
29
30#[must_use]
32pub fn maybe_warn_repeat(query: &str) -> Option<String> {
33 if !kernel_config::is_enabled() || !search_kernel::is_repeated_query(query) {
34 return None;
35 }
36 REPEATED_WARNED.fetch_add(1, Ordering::Relaxed);
37 Some("⚠ Repeated query detected; consider refining it or reusing prior results.".to_owned())
38}
39
40#[must_use]
42pub fn summary() -> SearchHookSummary {
43 SearchHookSummary {
44 searches_recorded: SEARCHES_RECORDED.load(Ordering::Relaxed),
45 repeated_queries_warned: REPEATED_WARNED.load(Ordering::Relaxed),
46 }
47}
48
49pub fn reset() {
51 SEARCHES_RECORDED.store(0, Ordering::Relaxed);
52 REPEATED_WARNED.store(0, Ordering::Relaxed);
53 search_kernel::reset();
54}
55
56#[cfg(test)]
57mod tests {
58 use super::{maybe_warn_repeat, on_search, reset, summary};
59 use crate::core::context_kernel::kernel_config::{self, KERNEL_TEST_LOCK, KernelFeatures};
60
61 fn isolated() -> std::sync::MutexGuard<'static, ()> {
62 let guard = KERNEL_TEST_LOCK
63 .lock()
64 .unwrap_or_else(std::sync::PoisonError::into_inner);
65 kernel_config::reset_features();
66 reset();
67 guard
68 }
69
70 #[test]
71 fn records_search() {
72 let _guard = isolated();
73 for _ in 0..3 {
74 on_search("query", "regex", 2, 10);
75 }
76 assert_eq!(summary().searches_recorded, 3);
77 }
78
79 #[test]
80 fn detects_repeat() {
81 let _guard = isolated();
82 on_search("same", "regex", 1, 10);
83 on_search("same", "regex", 1, 10);
84 assert!(maybe_warn_repeat("same").is_some());
85 assert_eq!(summary().repeated_queries_warned, 1);
86 }
87
88 #[test]
89 fn disabled_kernel_noop() {
90 let _guard = isolated();
91 let features = KernelFeatures {
92 enabled: false,
93 ..KernelFeatures::default()
94 };
95 kernel_config::update_features(features);
96 on_search("same", "regex", 1, 10);
97 assert!(maybe_warn_repeat("same").is_none());
98 assert_eq!(summary().searches_recorded, 0);
99 assert_eq!(summary().repeated_queries_warned, 0);
100 kernel_config::reset_features();
101 }
102}