fix: store tool calls structurally for proper API roundtripping
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
This commit is contained in:
@@ -126,14 +126,16 @@ pub async fn get_initial_discovery_messages(
|
||||
|
||||
/// Creates an Assistant message with a tool call in g3's JSON format.
|
||||
pub fn create_tool_message(tool: &str, command: &str) -> Message {
|
||||
let tool_call = serde_json::json!({
|
||||
"tool": tool,
|
||||
"args": {
|
||||
"command": command
|
||||
}
|
||||
let mut msg = Message::new(
|
||||
MessageRole::Assistant,
|
||||
String::new(),
|
||||
);
|
||||
msg.tool_calls.push(g3_providers::MessageToolCall {
|
||||
id: format!("plan_{}", msg.id),
|
||||
name: tool.to_string(),
|
||||
input: serde_json::json!({ "command": command }),
|
||||
});
|
||||
|
||||
Message::new(MessageRole::Assistant, tool_call.to_string())
|
||||
msg
|
||||
}
|
||||
|
||||
/// Extract shell commands from the LLM response.
|
||||
@@ -269,10 +271,10 @@ mod tests {
|
||||
let msg = create_tool_message("shell", "ls -la");
|
||||
|
||||
assert!(matches!(msg.role, MessageRole::Assistant));
|
||||
|
||||
let parsed: serde_json::Value = serde_json::from_str(&msg.content).unwrap();
|
||||
assert_eq!(parsed["tool"], "shell");
|
||||
assert_eq!(parsed["args"]["command"], "ls -la");
|
||||
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]
|
||||
|
||||
@@ -9,9 +9,10 @@ fn test_create_tool_message_format() {
|
||||
|
||||
assert!(matches!(msg.role, MessageRole::Assistant));
|
||||
|
||||
let parsed: serde_json::Value = serde_json::from_str(&msg.content).unwrap();
|
||||
assert_eq!(parsed["tool"], "shell");
|
||||
assert_eq!(parsed["args"]["command"], "ls -la");
|
||||
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]
|
||||
|
||||
Reference in New Issue
Block a user