Skip to content

Commit 5c3e2e0

Browse files
Alex HolmbergAlex Holmberg
authored andcommitted
feat: updated agenet behavior for better tool calling and context mngmt
1 parent 96b6f5f commit 5c3e2e0

2 files changed

Lines changed: 591 additions & 158 deletions

File tree

src/agent/history.rs

Lines changed: 101 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ pub struct ToolCallRecord {
3535
pub tool_name: String,
3636
pub args_summary: String,
3737
pub result_summary: String,
38+
/// Tool call ID for proper message pairing (optional for backwards compat)
39+
#[serde(default)]
40+
pub tool_id: Option<String>,
3841
}
3942

4043
/// Conversation history manager with compaction support
@@ -163,36 +166,97 @@ impl ConversationHistory {
163166
}
164167

165168
/// Create a text summary of conversation turns
169+
/// Includes detailed tool call information to preserve context
166170
fn create_summary(&self, turns: &[ConversationTurn]) -> String {
171+
use std::collections::HashSet;
172+
167173
let mut summary_parts = Vec::new();
174+
let mut all_files_read: HashSet<String> = HashSet::new();
175+
let mut all_files_written: HashSet<String> = HashSet::new();
168176

169177
for (i, turn) in turns.iter().enumerate() {
170178
let mut turn_summary = format!(
171-
"Turn {}: User asked about: {}",
179+
"Turn {}: User: {}",
172180
i + 1,
173-
Self::truncate_text(&turn.user_message, 100)
181+
Self::truncate_text(&turn.user_message, 150)
174182
);
175183

176184
if !turn.tool_calls.is_empty() {
177-
let tool_names: Vec<_> = turn.tool_calls.iter()
178-
.map(|tc| tc.tool_name.as_str())
179-
.collect();
180-
turn_summary.push_str(&format!(". Tools used: {}", tool_names.join(", ")));
185+
// Group tool calls by type for better summary
186+
let mut reads = Vec::new();
187+
let mut writes = Vec::new();
188+
let mut other = Vec::new();
189+
190+
for tc in &turn.tool_calls {
191+
match tc.tool_name.as_str() {
192+
"read_file" => {
193+
reads.push(tc.args_summary.clone());
194+
all_files_read.insert(tc.args_summary.clone());
195+
}
196+
"write_file" | "write_files" => {
197+
writes.push(tc.args_summary.clone());
198+
all_files_written.insert(tc.args_summary.clone());
199+
}
200+
"list_directory" => {
201+
other.push(format!("listed {}", tc.args_summary));
202+
}
203+
_ => {
204+
other.push(format!("{}({})", tc.tool_name, Self::truncate_text(&tc.args_summary, 30)));
205+
}
206+
}
207+
}
208+
209+
if !reads.is_empty() {
210+
turn_summary.push_str(&format!("\n - Read {} files: {}",
211+
reads.len(),
212+
reads.iter().take(5).cloned().collect::<Vec<_>>().join(", ")
213+
));
214+
if reads.len() > 5 {
215+
turn_summary.push_str(&format!(" (+{} more)", reads.len() - 5));
216+
}
217+
}
218+
if !writes.is_empty() {
219+
turn_summary.push_str(&format!("\n - Wrote: {}", writes.join(", ")));
220+
}
221+
if !other.is_empty() {
222+
turn_summary.push_str(&format!("\n - Other: {}", other.join(", ")));
223+
}
181224
}
182225

183226
turn_summary.push_str(&format!(
184-
". Response summary: {}",
185-
Self::truncate_text(&turn.assistant_response, 200)
227+
"\n - Response: {}",
228+
Self::truncate_text(&turn.assistant_response, 300)
186229
));
187230

188231
summary_parts.push(turn_summary);
189232
}
190233

191-
format!(
192-
"Previous conversation summary ({} turns compressed):\n{}",
234+
// Add a cumulative context section
235+
let mut context = format!(
236+
"=== Conversation Summary ({} turns compressed) ===\n\n{}",
193237
turns.len(),
194-
summary_parts.join("\n")
195-
)
238+
summary_parts.join("\n\n")
239+
);
240+
241+
// Add cumulative file context
242+
if !all_files_read.is_empty() {
243+
context.push_str("\n\n=== Files Previously Read (content is in context) ===\n");
244+
for file in all_files_read.iter().take(30) {
245+
context.push_str(&format!(" - {}\n", file));
246+
}
247+
if all_files_read.len() > 30 {
248+
context.push_str(&format!(" ... and {} more files\n", all_files_read.len() - 30));
249+
}
250+
}
251+
252+
if !all_files_written.is_empty() {
253+
context.push_str("\n=== Files Previously Written ===\n");
254+
for file in &all_files_written {
255+
context.push_str(&format!(" - {}\n", file));
256+
}
257+
}
258+
259+
context
196260
}
197261

198262
/// Truncate text with ellipsis
@@ -205,6 +269,8 @@ impl ConversationHistory {
205269
}
206270

207271
/// Convert history to Rig Message format for the agent
272+
/// Uses text summaries to preserve context without breaking Rig's internal tool call tracking
273+
/// Note: We can't use proper ToolCall/ToolResult messages because Rig expects real API tool IDs
208274
pub fn to_messages(&self) -> Vec<Message> {
209275
use rig::completion::message::{Text, UserContent, AssistantContent};
210276
use rig::OneOrMany;
@@ -227,7 +293,7 @@ impl ConversationHistory {
227293
});
228294
}
229295

230-
// Add recent turns
296+
// Add recent turns with tool call context as text (not as actual ToolCall messages)
231297
for turn in &self.turns {
232298
// User message
233299
messages.push(Message::User {
@@ -236,12 +302,30 @@ impl ConversationHistory {
236302
})),
237303
});
238304

239-
// Assistant response (simplified - just the text response)
240-
// Note: Tool calls are implicitly part of the response context
305+
// Build assistant response that includes tool call context
306+
let mut response_text = String::new();
307+
308+
// If there were tool calls, include them as text context
309+
if !turn.tool_calls.is_empty() {
310+
response_text.push_str("[Tools used in this turn:\n");
311+
for tc in &turn.tool_calls {
312+
response_text.push_str(&format!(
313+
" - {}({}) → {}\n",
314+
tc.tool_name,
315+
Self::truncate_text(&tc.args_summary, 50),
316+
Self::truncate_text(&tc.result_summary, 100)
317+
));
318+
}
319+
response_text.push_str("]\n\n");
320+
}
321+
322+
// Add the actual response
323+
response_text.push_str(&turn.assistant_response);
324+
241325
messages.push(Message::Assistant {
242326
id: None,
243327
content: OneOrMany::one(AssistantContent::Text(Text {
244-
text: turn.assistant_response.clone(),
328+
text: response_text,
245329
})),
246330
});
247331
}
@@ -299,6 +383,7 @@ mod tests {
299383
tool_name: "analyze".to_string(),
300384
args_summary: "path: .".to_string(),
301385
result_summary: "Found rust project".to_string(),
386+
tool_id: Some(format!("tool_{}", i)),
302387
}],
303388
);
304389
}

0 commit comments

Comments
 (0)