Skip to main content

ai_agent/services/compact/
compact.rs

1// Source: /data/home/swei/claudecode/openclaudecode/src/commands/compact/compact.ts
2//! Main compact service - handles conversation compaction
3//!
4//! This is a stub implementation. The full implementation handles
5//! the complex logic of compacting conversation history.
6
7use serde::{Deserialize, Serialize};
8
9/// Compact direction
10#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)]
11#[serde(rename_all = "lowercase")]
12pub enum CompactDirection {
13    /// Compact from the beginning (oldest messages)
14    Head,
15    /// Compact from the end (newest messages)
16    Tail,
17    /// Smart compaction based on token budget
18    #[default]
19    Smart,
20}
21
22/// Compact result
23#[derive(Debug, Clone, Serialize, Deserialize)]
24pub struct CompactResult {
25    /// Whether compaction was successful
26    pub success: bool,
27    /// Number of messages removed
28    pub messages_removed: usize,
29    /// Token count before compaction
30    pub tokens_before: u64,
31    /// Token count after compaction
32    pub tokens_after: u64,
33    /// Direction used
34    pub direction: CompactDirection,
35    /// Error message if failed
36    pub error: Option<String>,
37}
38
39/// Compact options
40#[derive(Debug, Clone, Default)]
41pub struct CompactOptions {
42    /// Maximum tokens to keep after compaction
43    pub max_tokens: Option<u64>,
44    /// Direction to compact
45    pub direction: CompactDirection,
46    /// Whether to create a boundary message
47    pub create_boundary: bool,
48    /// Custom system prompt to include
49    pub system_prompt: Option<String>,
50}
51
52/// Execute conversation compaction
53pub async fn compact_messages(
54    _messages: &[impl AsRef<dyn std::any::Any>],
55    options: CompactOptions,
56) -> Result<CompactResult, String> {
57    // This is a stub implementation
58    // Full implementation would involve:
59    // 1. Analyzing token counts
60    // 2. Selecting messages to remove
61    // 3. Creating boundary messages
62    // 4. Updating session state
63
64    Ok(CompactResult {
65        success: true,
66        messages_removed: 0,
67        tokens_before: 0,
68        tokens_after: 0,
69        direction: options.direction,
70        error: None,
71    })
72}
73
74/// Get the recommended compact direction based on message count and tokens
75pub fn get_recommended_direction(
76    message_count: usize,
77    total_tokens: u64,
78    max_tokens: u64,
79) -> CompactDirection {
80    if total_tokens <= max_tokens {
81        return CompactDirection::Smart;
82    }
83
84    // If more than half the messages are from the user, compact from head
85    // to preserve recent assistant responses
86    if message_count > 10 {
87        CompactDirection::Head
88    } else {
89        CompactDirection::Smart
90    }
91}
92
93/// Calculate the number of messages to remove for compact
94pub fn calculate_messages_to_remove(
95    current_tokens: u64,
96    target_tokens: u64,
97    avg_tokens_per_message: u64,
98) -> usize {
99    if current_tokens <= target_tokens {
100        return 0;
101    }
102
103    let tokens_to_remove = current_tokens - target_tokens;
104    (tokens_to_remove / avg_tokens_per_message) as usize
105}
106
107#[cfg(test)]
108mod tests {
109    use super::*;
110
111    #[test]
112    fn test_compact_direction_default() {
113        let options = CompactOptions::default();
114        assert_eq!(options.direction, CompactDirection::Smart);
115    }
116
117    #[test]
118    fn test_get_recommended_direction_no_compact() {
119        let dir = get_recommended_direction(5, 1000, 2000);
120        assert_eq!(dir, CompactDirection::Smart);
121    }
122
123    #[test]
124    fn test_calculate_messages_to_remove() {
125        let count = calculate_messages_to_remove(5000, 2000, 500);
126        assert_eq!(count, 6);
127    }
128
129    #[test]
130    fn test_calculate_messages_to_remove_no_need() {
131        let count = calculate_messages_to_remove(1000, 2000, 500);
132        assert_eq!(count, 0);
133    }
134}