Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 16 additions & 44 deletions crates/code_assistant/src/agent/runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -288,13 +288,7 @@ impl Agent {
// Check for pending user message and add it to history at start of each iteration
if let Some(pending_message) = self.get_and_clear_pending_message() {
debug!("Processing pending user message: {}", pending_message);
let user_msg = Message {
role: MessageRole::User,
content: MessageContent::Text(pending_message.clone()),
request_id: None,
usage: None,
};
self.append_message(user_msg)?;
self.append_message(Message::new_user(pending_message.clone()))?;

// Notify UI about the user message
self.ui
Expand All @@ -312,12 +306,11 @@ impl Agent {

// 2. Add original LLM response to message history if it has content
if !llm_response.content.is_empty() {
self.append_message(Message {
role: MessageRole::Assistant,
content: MessageContent::Structured(llm_response.content.clone()),
request_id: Some(request_id),
usage: Some(llm_response.usage.clone()),
})?;
self.append_message(
Message::new_assistant_content(llm_response.content.clone())
.with_request_id(request_id)
.with_usage(llm_response.usage.clone()),
)?;
}

// 3. Extract tool requests from LLM response and get truncated response
Expand Down Expand Up @@ -528,12 +521,7 @@ impl Agent {
ToolSyntax::Native => {
// For native mode, keep text message since parsing errors occur before
// we have any LLM-provided tool IDs to reference
Message {
role: MessageRole::User,
content: MessageContent::Text(error_text),
request_id: None,
usage: None,
}
Message::new_user(error_text)
}
_ => {
// For custom tool-syntax modes, create structured tool-result message like regular tool results
Expand All @@ -545,18 +533,13 @@ impl Agent {
ToolExecution::create_parse_error(tool_id.clone(), error_text.clone());
self.tool_executions.push(tool_execution);

Message {
role: MessageRole::User,
content: MessageContent::Structured(vec![ContentBlock::ToolResult {
tool_use_id: tool_id,
content: error_text,
is_error: Some(true),
start_time: Some(SystemTime::now()),
end_time: None,
}]),
request_id: None,
usage: None,
}
Message::new_user_content(vec![ContentBlock::ToolResult {
tool_use_id: tool_id,
content: error_text,
is_error: Some(true),
start_time: Some(SystemTime::now()),
end_time: None,
}])
}
};

Expand Down Expand Up @@ -603,12 +586,7 @@ impl Agent {

// Only add message if there were actual tool executions (not just complete_task)
if !content_blocks.is_empty() {
let result_message = Message {
role: MessageRole::User,
content: MessageContent::Structured(content_blocks),
request_id: None,
usage: None,
};
let result_message = Message::new_user_content(content_blocks);
self.append_message(result_message)?;
}
Ok(LoopFlow::Continue)
Expand All @@ -630,13 +608,7 @@ impl Agent {
.await?;

// Create the initial user message
let user_msg = Message {
role: MessageRole::User,
content: MessageContent::Text(task.clone()),
request_id: None,
usage: None,
};
self.append_message(user_msg)?;
self.append_message(Message::new_user(task.clone()))?;

// Notify UI of initial working memory
let _ = self
Expand Down
115 changes: 28 additions & 87 deletions crates/code_assistant/src/agent/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -520,13 +520,7 @@ async fn test_invalid_xml_tool_error_handling() -> Result<()> {
agent.disable_naming_reminders();

// Add an initial user message like the working test does
let user_msg = Message {
role: MessageRole::User,
content: MessageContent::Text("Test task".to_string()),
request_id: None,
usage: None,
};
agent.append_message(user_msg)?;
agent.append_message(Message::new_user("Test task"))?;

agent.run_single_iteration().await?;

Expand Down Expand Up @@ -755,53 +749,23 @@ fn test_ui_filtering_with_failed_tool_messages() -> Result<()> {
);
session.messages = vec![
// Regular user message - should be included
Message {
role: MessageRole::User,
content: MessageContent::Text("Hello, please help me".to_string()),
request_id: None,
usage: None,
},
Message::new_user("Hello, please help me"),
// Assistant response
Message {
role: MessageRole::Assistant,
content: MessageContent::Text("I'll help you".to_string()),
request_id: Some(1),
usage: None,
},
Message::new_assistant("I'll help you").with_request_id(1),
// Parse error message in XML mode - should be filtered out
Message {
role: MessageRole::User,
content: MessageContent::Structured(vec![ContentBlock::new_error_tool_result(
"tool-1-0",
"Tool error: Unknown tool 'invalid_tool'. Please use only available tools.",
)]),
request_id: None,
usage: None,
},
Message::new_user_content(vec![ContentBlock::new_error_tool_result(
"tool-1-0",
"Tool error: Unknown tool 'invalid_tool'. Please use only available tools.",
)]),
// Regular tool result - should be filtered out
Message {
role: MessageRole::User,
content: MessageContent::Structured(vec![ContentBlock::new_tool_result(
"regular-tool-123",
"File contents here",
)]),
request_id: None,
usage: None,
},
Message::new_user_content(vec![ContentBlock::new_tool_result(
"regular-tool-123",
"File contents here",
)]),
// Empty user message (legacy) - should be filtered out
Message {
role: MessageRole::User,
content: MessageContent::Text("".to_string()),
request_id: None,
usage: None,
},
Message::new_user(""),
// Another regular user message - should be included
Message {
role: MessageRole::User,
content: MessageContent::Text("Thank you for the help!".to_string()),
request_id: None,
usage: None,
},
Message::new_user("Thank you for the help!"),
];
session.tool_executions = Vec::new();
session.working_memory = crate::types::WorkingMemory::default();
Expand Down Expand Up @@ -1151,12 +1115,7 @@ fn test_inject_naming_reminder_skips_tool_result_messages() -> Result<()> {
let mut agent = Agent::new(components, session_config);

// Test case 1: User message with text content should get reminder
let messages = vec![Message {
role: MessageRole::User,
content: MessageContent::Text("Hello, help me with a task".to_string()),
request_id: None,
usage: None,
}];
let messages = vec![Message::new_user("Hello, help me with a task")];

let result_messages = agent.inject_naming_reminder_if_needed(messages.clone());
assert_eq!(result_messages.len(), 1);
Expand Down Expand Up @@ -1185,29 +1144,16 @@ fn test_inject_naming_reminder_skips_tool_result_messages() -> Result<()> {

// Test case 2: User message with only tool results should be skipped
let messages_with_tool_results = vec![
Message {
role: MessageRole::User,
content: MessageContent::Text("Hello, help me with a task".to_string()),
request_id: None,
usage: None,
},
Message {
role: MessageRole::Assistant,
content: MessageContent::Structured(vec![ContentBlock::new_text(
"I'll help you with that task.",
)]),
request_id: Some(1),
usage: Some(Usage::zero()),
},
Message {
role: MessageRole::User,
content: MessageContent::Structured(vec![ContentBlock::new_tool_result(
"tool-1-1",
"Tool execution result",
)]),
request_id: None,
usage: None,
},
Message::new_user("Hello, help me with a task"),
Message::new_assistant_content(vec![ContentBlock::new_text(
"I'll help you with that task.",
)])
.with_request_id(1)
.with_usage(Usage::zero()),
Message::new_user_content(vec![ContentBlock::new_tool_result(
"tool-1-1",
"Tool execution result",
)]),
];

let result_messages =
Expand Down Expand Up @@ -1252,15 +1198,10 @@ fn test_inject_naming_reminder_skips_tool_result_messages() -> Result<()> {
}

// Test case 3: User message with mixed content (text + tool results) should get reminder
let mixed_message = vec![Message {
role: MessageRole::User,
content: MessageContent::Structured(vec![
ContentBlock::new_text("Please analyze this file"),
ContentBlock::new_tool_result("tool-1-1", "Previous tool result"),
]),
request_id: None,
usage: None,
}];
let mixed_message = vec![Message::new_user_content(vec![
ContentBlock::new_text("Please analyze this file"),
ContentBlock::new_tool_result("tool-1-1", "Previous tool result"),
])];

let result_messages = agent.inject_naming_reminder_if_needed(mixed_message.clone());
assert_eq!(result_messages.len(), 1);
Expand Down
8 changes: 1 addition & 7 deletions crates/code_assistant/src/session/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,13 +221,7 @@ impl SessionManager {
session_instance.reload_from_persistence(&self.persistence)?;

// Add structured user message to session
let user_msg = Message {
role: llm::MessageRole::User,
content: llm::MessageContent::Structured(content_blocks),
request_id: None,
usage: None,
};
session_instance.add_message(user_msg);
session_instance.add_message(Message::new_user_content(content_blocks));

// Clone all needed data to avoid borrowing conflicts
let name = session_instance.session.name.clone();
Expand Down
7 changes: 1 addition & 6 deletions crates/code_assistant/src/tests/integration_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,12 +176,7 @@ async fn test_tool_limit_with_realistic_anthropic_chunks() -> Result<()> {

// Create the request
let request = LLMRequest {
messages: vec![Message {
role: MessageRole::User,
content: MessageContent::Text("Please refactor the Anthropic client".to_string()),
request_id: None,
usage: None,
}],
messages: vec![Message::new_user("Please refactor the Anthropic client")],
system_prompt: "You are a helpful assistant.".to_string(),
..Default::default()
};
Expand Down
Loading