cloudllm 0.15.9

A batteries-included Rust toolkit for building intelligent agents with LLM integration, multi-protocol tool support, multi-agent orchestration, and MentisDB-backed durable memory.
Documentation
use cloudllm::tool_protocol::{ToolMetadata, ToolParameter, ToolParameterType, ToolProtocol};
use cloudllm::tool_protocols::{CustomToolProtocol, OpenAIFunctionsProtocol};
use std::sync::Arc;

#[tokio::test]
async fn test_custom_adapter_sync_tool() {
    let adapter = CustomToolProtocol::new();

    let metadata = ToolMetadata::new("add", "Adds two numbers")
        .with_parameter(ToolParameter::new("a", ToolParameterType::Number).required())
        .with_parameter(ToolParameter::new("b", ToolParameterType::Number).required());

    adapter
        .register_tool(
            metadata,
            Arc::new(|params| {
                let a = params["a"].as_f64().unwrap_or(0.0);
                let b = params["b"].as_f64().unwrap_or(0.0);
                Ok(cloudllm::tool_protocol::ToolResult::success(
                    serde_json::json!({"result": a + b}),
                ))
            }),
        )
        .await;

    let result = adapter
        .execute("add", serde_json::json!({"a": 5.0, "b": 3.0}))
        .await
        .unwrap();

    assert!(result.success);
    assert_eq!(result.output["result"], 8.0);
}

#[tokio::test]
async fn test_custom_adapter_async_tool() {
    let adapter = CustomToolProtocol::new();

    let metadata = ToolMetadata::new("fetch", "Fetches data asynchronously");

    adapter
        .register_async_tool(
            metadata,
            Arc::new(|_params| {
                Box::pin(async {
                    tokio::time::sleep(tokio::time::Duration::from_millis(10)).await;
                    Ok(cloudllm::tool_protocol::ToolResult::success(
                        serde_json::json!({"data": "fetched"}),
                    ))
                })
            }),
        )
        .await;

    let result = adapter
        .execute("fetch", serde_json::json!({}))
        .await
        .unwrap();

    assert!(result.success);
    assert_eq!(result.output["data"], "fetched");
}

#[tokio::test]
async fn test_custom_adapter_list_tools() {
    let adapter = CustomToolProtocol::new();

    let metadata1 = ToolMetadata::new("tool1", "First tool");
    let metadata2 = ToolMetadata::new("tool2", "Second tool");

    adapter
        .register_tool(
            metadata1,
            Arc::new(|_| {
                Ok(cloudllm::tool_protocol::ToolResult::success(
                    serde_json::json!({}),
                ))
            }),
        )
        .await;

    adapter
        .register_tool(
            metadata2,
            Arc::new(|_| {
                Ok(cloudllm::tool_protocol::ToolResult::success(
                    serde_json::json!({}),
                ))
            }),
        )
        .await;

    let tools = adapter.list_tools().await.unwrap();
    assert_eq!(tools.len(), 2);
}

#[tokio::test]
async fn test_openai_function_adapter() {
    let adapter = OpenAIFunctionsProtocol::new();

    let metadata = ToolMetadata::new("search", "Searches the web").with_parameter(
        ToolParameter::new("query", ToolParameterType::String)
            .with_description("The search query")
            .required(),
    );

    adapter
        .register_function(
            metadata,
            Arc::new(|params| {
                Box::pin(async move {
                    let query = params["query"].as_str().unwrap_or("");
                    Ok(cloudllm::tool_protocol::ToolResult::success(
                        serde_json::json!({
                            "results": [
                                {"title": "Result 1", "url": "http://example.com/1"},
                                {"title": "Result 2", "url": "http://example.com/2"}
                            ],
                            "query": query
                        }),
                    ))
                })
            }),
        )
        .await;

    let functions = adapter.get_openai_functions().await;
    assert_eq!(functions.len(), 1);
    assert_eq!(functions[0]["name"], "search");
    assert_eq!(functions[0]["description"], "Searches the web");

    let result = adapter
        .execute("search", serde_json::json!({"query": "rust programming"}))
        .await
        .unwrap();

    assert!(result.success);
    assert_eq!(result.output["query"], "rust programming");
}