Skip to main content

ai_agent/services/context_collapse/
mod.rs

1// Source: /data/home/swei/claudecode/openclaudecode/src/services/contextCollapse/index.ts
2//! Context collapse module for managing context compression.
3//!
4//! Translated from TypeScript contextCollapse/index.ts
5
6pub mod operations;
7pub mod persist;
8
9pub use operations::*;
10pub use persist::*;
11
12/// Statistics about context collapse operations
13#[derive(Debug, Clone, Default)]
14pub struct ContextCollapseStats {
15    pub collapsed_spans: usize,
16    pub staged_spans: usize,
17    pub health: ContextCollapseHealth,
18}
19
20/// Health metrics for context collapse
21#[derive(Debug, Clone, Default)]
22pub struct ContextCollapseHealth {
23    pub total_errors: usize,
24    pub total_empty_spawns: usize,
25    pub empty_spawn_warning_emitted: bool,
26}
27
28/// Module-level stats
29static STATS: std::sync::LazyLock<std::sync::Mutex<ContextCollapseStats>> =
30    std::sync::LazyLock::new(|| std::sync::Mutex::new(ContextCollapseStats::default()));
31
32/// Check if context collapse is enabled
33/// In TypeScript, this always returns false (feature gate)
34/// In Rust, we check for the feature flag
35pub fn is_context_collapse_enabled() -> bool {
36    // Feature gate: context collapse is disabled by default
37    // In full implementation, this would check the feature flag
38    false
39}
40
41/// Reset context collapse state
42pub fn reset_context_collapse() {
43    let mut stats = STATS.lock().unwrap();
44    *stats = ContextCollapseStats::default();
45}
46
47/// Apply collapses to messages if needed
48/// Returns the messages and whether they were changed
49pub fn apply_collapses_if_needed<T>(messages: T) -> ContextCollapseApplyResult<T>
50where
51    T: Clone,
52{
53    // In full implementation, this would apply context collapse
54    // For now, just return the messages unchanged
55    ContextCollapseApplyResult {
56        messages,
57        changed: false,
58    }
59}
60
61/// Check if withheld prompt is too long
62pub fn is_withheld_prompt_too_long() -> bool {
63    false
64}
65
66/// Recover from overflow situation
67pub fn recover_from_overflow<T>(messages: T) -> T
68where
69    T: Clone,
70{
71    // In full implementation, this would attempt recovery
72    messages
73}
74
75/// Result of applying collapses
76#[derive(Debug, Clone)]
77pub struct ContextCollapseApplyResult<T> {
78    pub messages: T,
79    pub changed: bool,
80}
81
82/// Get context collapse statistics
83pub fn get_stats() -> ContextCollapseStats {
84    STATS.lock().unwrap().clone()
85}
86
87/// Increment collapsed spans counter
88pub fn increment_collapsed_spans() {
89    let mut stats = STATS.lock().unwrap();
90    stats.collapsed_spans += 1;
91}
92
93/// Increment staged spans counter
94pub fn increment_staged_spans() {
95    let mut stats = STATS.lock().unwrap();
96    stats.staged_spans += 1;
97}
98
99/// Record an error
100pub fn record_error() {
101    let mut stats = STATS.lock().unwrap();
102    stats.health.total_errors += 1;
103}
104
105/// Record an empty spawn
106pub fn record_empty_spawn() {
107    let mut stats = STATS.lock().unwrap();
108    stats.health.total_empty_spawns += 1;
109}
110
111/// Emit empty spawn warning
112pub fn emit_empty_spawn_warning() {
113    let mut stats = STATS.lock().unwrap();
114    stats.health.empty_spawn_warning_emitted = true;
115}
116
117#[cfg(test)]
118mod tests {
119    use super::*;
120
121    #[test]
122    fn test_is_context_collapse_enabled() {
123        // Should return false by default
124        assert!(!is_context_collapse_enabled());
125    }
126
127    #[test]
128    fn test_get_stats_default() {
129        let stats = get_stats();
130        assert_eq!(stats.collapsed_spans, 0);
131        assert_eq!(stats.staged_spans, 0);
132        assert_eq!(stats.health.total_errors, 0);
133        assert_eq!(stats.health.total_empty_spawns, 0);
134        assert!(!stats.health.empty_spawn_warning_emitted);
135    }
136
137    #[test]
138    fn test_increment_collapsed_spans() {
139        increment_collapsed_spans();
140        let stats = get_stats();
141        assert_eq!(stats.collapsed_spans, 1);
142        // Reset for other tests
143        reset_context_collapse();
144    }
145
146    #[test]
147    fn test_apply_collapses_if_needed_no_change() {
148        let messages = vec![1, 2, 3];
149        let result = apply_collapses_if_needed(messages.clone());
150        assert!(!result.changed);
151        assert_eq!(result.messages, messages);
152    }
153
154    #[test]
155    fn test_recover_from_overflow() {
156        let messages = vec![1, 2, 3];
157        let result = recover_from_overflow(messages.clone());
158        assert_eq!(result, messages);
159    }
160}