1use crate::config::constants::tools;
4use crate::config::models::ModelId;
5use crate::config::types::AgentConfig;
6use crate::gemini::models::SystemInstruction;
7use crate::gemini::{Content, FunctionResponse, GenerateContentRequest, Part};
8use crate::llm::make_client;
9use anyhow::Result;
10use console::style;
11use serde_json::json;
12
13pub async fn handle_compress_context_command(
15 config: AgentConfig,
16 _input: Option<std::path::PathBuf>,
17 _output: Option<std::path::PathBuf>,
18) -> Result<()> {
19 println!("{}", style("[CONTEXT] Compression Demo").cyan().bold());
20 println!(
21 "{}",
22 style("Following Cognition's context engineering principles...").dim()
23 );
24
25 let sample_conversation = vec![
27 Content::user_text("I want to create a Rust web application with user authentication"),
28 Content::system_text(
29 "I'll help you create a Rust web application with authentication. Let me start by exploring the current directory structure.",
30 ),
31 Content::user_parts(vec![Part::FunctionResponse {
32 function_response: FunctionResponse {
33 name: tools::LIST_FILES.to_string(),
34 response: json!({"path": ".", "files": ["Cargo.toml", "src/main.rs"], "directories": ["src", "tests"]}),
35 },
36 }]),
37 Content::system_text(
38 "I can see you already have a basic Rust project. Let me check what's in the main.rs file.",
39 ),
40 Content::user_parts(vec![Part::FunctionResponse {
41 function_response: FunctionResponse {
42 name: tools::READ_FILE.to_string(),
43 response: json!({"path": "src/main.rs", "content": "fn main() {\n println!(\"Hello World!\");\n}", "metadata": {"size": 45}}),
44 },
45 }]),
46 Content::system_text(
47 "Now I need to add web framework dependencies. I'll update Cargo.toml to include Axum and other necessary crates.",
48 ),
49 Content::user_parts(vec![Part::FunctionResponse {
50 function_response: FunctionResponse {
51 name: tools::EDIT_FILE.to_string(),
52 response: json!({"status": "modified", "path": "Cargo.toml", "action": {"replacements_made": 1}}),
53 },
54 }]),
55 Content::system_text("Good! Now let me create the authentication module structure."),
56 Content::user_parts(vec![Part::FunctionResponse {
57 function_response: FunctionResponse {
58 name: tools::WRITE_FILE.to_string(),
59 response: json!({"status": "created", "path": "src/auth.rs", "bytes_written": 234}),
60 },
61 }]),
62 Content::system_text("Now I'll create the main web server with authentication endpoints."),
63 Content::user_parts(vec![Part::FunctionResponse {
64 function_response: FunctionResponse {
65 name: tools::EDIT_FILE.to_string(),
66 response: json!({"status": "modified", "path": "src/main.rs", "action": {"replacements_made": 3}}),
67 },
68 }]),
69 ];
70
71 println!(
72 "{} {}",
73 style("Original conversation length:").yellow(),
74 sample_conversation.len()
75 );
76 println!(
77 "{} {:.1}KB",
78 style("Estimated token usage:").yellow(),
79 sample_conversation.len() as f64 * 0.5
80 ); let compression_prompt = r#"You are a context compression specialist. Your task is to compress the following agent conversation history while preserving:
84
851. KEY DECISIONS made by the agent
862. IMPORTANT ACTIONS taken (tool calls and their results)
873. CRITICAL CONTEXT about the current state
884. USER INTENT and requirements
895. TECHNICAL DECISIONS (frameworks, libraries, architecture choices)
90
91IMPORTANT: Do NOT lose information about:
92- What files were created/modified and why
93- What dependencies were added
94- What the current state of the project is
95- What the user's original request was
96
97Compress this conversation into a concise summary that captures all essential information:
98
99ORIGINAL CONVERSATION:"#;
100
101 let mut compression_content = vec![Content::user_text(compression_prompt)];
103
104 for (i, content) in sample_conversation.iter().enumerate() {
106 let role_indicator = match content.role.as_str() {
107 "user" => "USER",
108 "system" => "AGENT",
109 _ => "UNKNOWN",
110 };
111
112 let mut content_summary = format!("\n--- Turn {} ({}) ---\n", i + 1, role_indicator);
113
114 for part in &content.parts {
115 if let Some(text) = part.as_text() {
116 content_summary.push_str(text);
117 } else if let Part::FunctionCall { function_call } = part {
118 content_summary.push_str(&format!(
119 "\n[TOOL CALL: {}({})]",
120 function_call.name, function_call.args
121 ));
122 } else if let Part::FunctionResponse { function_response } = part {
123 content_summary.push_str(&format!(
124 "\n[TOOL RESULT: {}]",
125 serde_json::to_string_pretty(&function_response.response).unwrap_or_default()
126 ));
127 }
128 }
129
130 if i == 0 {
131 compression_content[0] =
132 Content::user_text(format!("{}{}", compression_prompt, content_summary));
133 } else {
134 compression_content.push(Content::user_text(content_summary));
135 }
136 }
137
138 compression_content.push(Content::user_text(
140 r#"
141COMPRESSION REQUIREMENTS:
142- Preserve all key decisions and their rationale
143- Keep track of what files were created/modified
144- Maintain information about current project state
145- Include user's original intent
146- Note any important technical choices made
147
148COMPRESSED SUMMARY:"#,
149 ));
150
151 let compression_request = GenerateContentRequest {
153 contents: compression_content,
154 tools: None,
155 tool_config: None,
156 generation_config: Some(json!({
157 "maxOutputTokens": 1000,
158 "temperature": 0.1
159 })),
160 system_instruction: Some(SystemInstruction::new(
161 r#"You are an expert at compressing agent conversation history.
162Your goal is to create a compressed summary that maintains all critical information while being concise.
163Focus on: key decisions, actions taken, current state, and user requirements."#,
164 )),
165 reasoning_config: None,
166 };
167
168 let model_id = config
169 .model
170 .parse::<ModelId>()
171 .map_err(|_| anyhow::anyhow!("Invalid model: {}", config.model))?;
172 let mut client = make_client(config.api_key.clone(), model_id);
173 println!("{}", style("Compressing conversation...").cyan());
174
175 let _prompt = compression_request
177 .contents
178 .iter()
179 .map(|content| {
180 content
181 .parts
182 .iter()
183 .map(|part| match part {
184 crate::gemini::Part::Text { text } => text.clone(),
185 _ => String::new(),
186 })
187 .collect::<Vec<_>>()
188 .join("\n")
189 })
190 .collect::<Vec<_>>()
191 .join("\n\n");
192
193 let prompt = compression_request
195 .contents
196 .iter()
197 .map(|content| {
198 content
199 .parts
200 .iter()
201 .map(|part| match part {
202 crate::gemini::Part::Text { text } => text.clone(),
203 _ => String::new(),
204 })
205 .collect::<Vec<_>>()
206 .join("\n")
207 })
208 .collect::<Vec<_>>()
209 .join("\n\n");
210
211 let compressed_response = client.generate(&prompt).await?;
212
213 println!("{}", style("Compressed Summary:").green().bold());
215 println!("{}", compressed_response.content);
216
217 println!("\n{}", style(" Key Principles Applied:").yellow().bold());
218 println!(" • {}", style("Share full context and traces").dim());
219 println!(" • {}", style("Actions carry implicit decisions").dim());
220 println!(
221 " • {}",
222 style("Single-threaded agents are more reliable").dim()
223 );
224 println!(
225 " • {}",
226 style("Context compression enables longer conversations").dim()
227 );
228
229 Ok(())
230}