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