The agent would stop mid-task because native tool calls were stored as inline JSON text in Message.content. When sent back to the Anthropic API via convert_messages(), they went as plain text instead of structured tool_use/tool_result blocks. The model would occasionally get confused and emit text describing what it wanted to do instead of invoking the tool mechanism. Changes: - Add MessageToolCall struct and tool_calls/tool_result_id fields to Message - Add id field to core ToolCall struct to preserve provider tool call IDs - Update Anthropic convert_messages() to emit tool_use and tool_result blocks - Add ToolResult variant to AnthropicContent enum - Store tool calls structurally in tool message construction (not inline JSON) - Fix add_message() to preserve empty-content messages with tool_calls - Fix check_duplicate_in_previous_message() to check structured tool_calls - Generate valid IDs for JSON fallback tool calls (Anthropic pattern requirement) - Update planner create_tool_message() to use structured tool calls
105 lines
2.4 KiB
Rust
105 lines
2.4 KiB
Rust
//! Integration tests for g3-planner
|
|
|
|
use g3_planner::{create_tool_message, explore_codebase, extract_shell_commands};
|
|
use g3_providers::MessageRole;
|
|
|
|
#[test]
|
|
fn test_create_tool_message_format() {
|
|
let msg = create_tool_message("shell", "ls -la");
|
|
|
|
assert!(matches!(msg.role, MessageRole::Assistant));
|
|
|
|
assert_eq!(msg.tool_calls.len(), 1);
|
|
assert_eq!(msg.tool_calls[0].name, "shell");
|
|
assert_eq!(msg.tool_calls[0].input["command"], "ls -la");
|
|
assert!(!msg.tool_calls[0].id.is_empty());
|
|
}
|
|
|
|
#[test]
|
|
fn test_explore_codebase_returns_report() {
|
|
// Test with current directory (should find Rust files in g3 project)
|
|
let report = explore_codebase(".");
|
|
|
|
// Should return a non-empty report
|
|
assert!(!report.is_empty(), "Report should not be empty");
|
|
|
|
// Should contain the codebase analysis header
|
|
assert!(
|
|
report.contains("CODEBASE ANALYSIS") || report.contains("No recognized"),
|
|
"Report should have analysis header or indicate no languages found"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_extract_shell_commands_basic() {
|
|
let response = r#"
|
|
Some text here.
|
|
|
|
{{CODE EXPLORATION COMMANDS}}
|
|
|
|
```bash
|
|
ls -la
|
|
cat README.md
|
|
rg --files -g '*.rs'
|
|
```
|
|
|
|
More text.
|
|
"#;
|
|
|
|
let commands = extract_shell_commands(response);
|
|
assert_eq!(commands.len(), 3);
|
|
assert_eq!(commands[0], "ls -la");
|
|
assert_eq!(commands[1], "cat README.md");
|
|
assert_eq!(commands[2], "rg --files -g '*.rs'");
|
|
}
|
|
|
|
#[test]
|
|
fn test_extract_shell_commands_with_comments() {
|
|
let response = r#"
|
|
{{CODE EXPLORATION COMMANDS}}
|
|
|
|
```
|
|
# This is a comment
|
|
ls -la
|
|
# Another comment
|
|
cat file.txt
|
|
```
|
|
"#;
|
|
|
|
let commands = extract_shell_commands(response);
|
|
assert_eq!(commands.len(), 2);
|
|
assert_eq!(commands[0], "ls -la");
|
|
assert_eq!(commands[1], "cat file.txt");
|
|
}
|
|
|
|
#[test]
|
|
fn test_extract_shell_commands_no_section() {
|
|
let response = "Some response without the expected section.";
|
|
let commands = extract_shell_commands(response);
|
|
assert!(commands.is_empty());
|
|
}
|
|
|
|
#[test]
|
|
fn test_extract_shell_commands_multiple_code_blocks() {
|
|
let response = r#"
|
|
{{CODE EXPLORATION COMMANDS}}
|
|
|
|
```bash
|
|
ls -la
|
|
```
|
|
|
|
Some explanation text.
|
|
|
|
```
|
|
cat README.md
|
|
head -50 src/main.rs
|
|
```
|
|
"#;
|
|
|
|
let commands = extract_shell_commands(response);
|
|
assert_eq!(commands.len(), 3);
|
|
assert_eq!(commands[0], "ls -la");
|
|
assert_eq!(commands[1], "cat README.md");
|
|
assert_eq!(commands[2], "head -50 src/main.rs");
|
|
}
|