open-agent-sdk 0.6.4

Production-ready Rust SDK for building AI agents with local OpenAI-compatible servers (LMStudio, Ollama, llama.cpp, vLLM). Features streaming, tools, hooks, retry logic, and comprehensive examples.
Documentation
//! Test example to verify tool call serialization fix
//!
//! This example demonstrates that tool calls and tool results are now properly
//! serialized into the OpenAI message format, fixing the infinite loop bug.

use open_agent::{AgentOptions, Client, ContentBlock, Tool};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    println!("=== Tool Serialization Test ===\n");

    // Create a simple calculator tool
    let calculator = Tool::new(
        "calculator",
        "Performs arithmetic operations",
        json!({
            "type": "object",
            "properties": {
                "operation": {
                    "type": "string",
                    "enum": ["add", "multiply"],
                    "description": "The operation to perform"
                },
                "a": {"type": "number", "description": "First number"},
                "b": {"type": "number", "description": "Second number"}
            },
            "required": ["operation", "a", "b"]
        }),
        |input| {
            Box::pin(async move {
                let operation = input["operation"].as_str().unwrap_or("add");
                let a = input["a"].as_f64().unwrap_or(0.0);
                let b = input["b"].as_f64().unwrap_or(0.0);

                let result = match operation {
                    "add" => a + b,
                    "multiply" => a * b,
                    _ => 0.0,
                };

                Ok(json!({
                    "result": result,
                    "operation": operation,
                    "operands": [a, b]
                }))
            })
        },
    );

    // Configure client with auto-execution enabled
    let options = AgentOptions::builder()
        .system_prompt("You are a helpful math assistant. When asked to perform calculations, use the calculator tool. After getting the result, provide a clear answer.")
        .model("gpt-3.5-turbo")  // Placeholder - won't actually connect
        .base_url("http://localhost:1234/v1")
        .temperature(0.7)
        .tool(calculator)
        .auto_execute_tools(true)
        .max_tool_iterations(3)
        .build()?;

    let mut client = Client::new(options)?;

    println!("Client created with auto_execute_tools enabled");
    println!(
        "Attempting to send a query (this will fail without a running server, but shows the fix)...\n"
    );

    // Try to send a query - this will fail without a server, but the debug logs
    // will show that tool calls and results are now properly serialized
    match client.send("What is 5 + 3?").await {
        Ok(_) => {
            println!("✓ send() succeeded");

            // Try to receive response
            let mut block_count = 0;
            while let Some(block) = client.receive().await? {
                block_count += 1;
                match block {
                    ContentBlock::Text(text) => {
                        println!("✓ Received text block: {}", text.text);
                    }
                    ContentBlock::ToolUse(tool_use) => {
                        println!(
                            "✓ Received tool use: {} (id: {})",
                            tool_use.name(),
                            tool_use.id()
                        );
                    }
                    ContentBlock::ToolResult(result) => {
                        println!("✓ Received tool result for: {}", result.tool_use_id());
                    }
                    ContentBlock::Image(_) => {
                        // Images not relevant for this example
                    }
                }
            }

            println!("\n✓ Test completed successfully!");
            println!("  Total blocks received: {}", block_count);
            println!(
                "  Check the debug logs above to verify tool_calls and tool_call_id are populated"
            );
        }
        Err(e) => {
            println!("✗ Expected error (no server running): {}", e);
            println!("\nHowever, check the [DEBUG] logs above:");
            println!("- Look for 'Creating assistant message with X tool calls'");
            println!("- Look for 'Creating tool result message: tool_call_id=...'");
            println!("- These indicate the fix is working!");
        }
    }

    println!("\n=== Fix Verification ===");
    println!("Before the fix:");
    println!("  - Messages would show: 'role=user, content_len=0, blocks=1'");
    println!("  - Tool results were dropped from conversation history");
    println!("  - Same tool called 50+ times in infinite loop");
    println!("\nAfter the fix:");
    println!("  - Messages show: 'Creating tool result message: tool_call_id=...'");
    println!("  - Tool results properly serialized with role='tool'");
    println!("  - Tool calls include proper tool_calls array");
    println!("  - Each tool called only once per unique request");

    Ok(())
}