magi-openai 0.1.0

OpenAI compatible API SDK for Magi AI agents
Documentation
use magi_tool::Tool as MagiTool;

use crate::completions::request::{FunctionTool, Tool, ToolType};

impl From<MagiTool> for Tool {
    fn from(magi_tool: MagiTool) -> Self {
        Tool {
            r#type: ToolType::Function,
            function: FunctionTool {
                name: magi_tool.name.clone(),
                description: magi_tool.description,
                parameters: Some(magi_tool.input_schema),
            },
        }
    }
}

impl From<Tool> for MagiTool {
    fn from(tool: Tool) -> Self {
        MagiTool {
            name: tool.function.name.clone(),
            description: tool.function.description.clone(),
            input_schema: tool.function.parameters.unwrap_or_default(),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use serde_json::json;
    use std::borrow::Cow;

    #[test]
    fn test_convert_magi_tool_to_openai_tool() {
        // Create a sample MagiTool
        let magi_tool = MagiTool {
            name: "get_weather".into(),
            description: Some("Get the current weather in a location".into()),
            input_schema: json!({
                "type": "object",
                "properties": {
                    "location": {
                        "type": "string",
                        "description": "The city and state, e.g. San Francisco, CA"
                    },
                    "unit": {
                        "type": "string",
                        "enum": ["celsius", "fahrenheit"]
                    }
                },
                "required": ["location"]
            }),
        };

        // Convert to OpenAI Tool
        let openai_tool: Tool = magi_tool.into();

        // Verify conversion
        assert_eq!(openai_tool.r#type, ToolType::Function);
        assert_eq!(openai_tool.function.name, "get_weather");
        assert_eq!(
            openai_tool.function.description,
            Some(Cow::from("Get the current weather in a location"))
        );

        let params = openai_tool.function.parameters.unwrap();
        assert_eq!(params["type"], "object");
        assert!(params["properties"].is_object());
        assert!(params["properties"]["location"].is_object());
        assert_eq!(params["properties"]["location"]["type"], "string");
        assert!(params["required"].is_array());
        assert_eq!(params["required"][0], "location");
    }

    #[test]
    fn test_convert_openai_tool_to_magi_tool() {
        // Create a sample OpenAI Tool
        let openai_tool = Tool {
            r#type: ToolType::Function,
            function: FunctionTool {
                name: "get_weather".into(),
                description: Some("Get the current weather in a location".into()),
                parameters: Some(json!({
                    "type": "object",
                    "properties": {
                        "location": {
                            "type": "string",
                            "description": "The city and state, e.g. San Francisco, CA"
                        },
                        "unit": {
                            "type": "string",
                            "enum": ["celsius", "fahrenheit"]
                        }
                    },
                    "required": ["location"]
                })),
            },
        };

        // Convert to MagiTool
        let magi_tool: MagiTool = openai_tool.into();

        // Verify conversion
        assert_eq!(magi_tool.name, "get_weather");
        assert_eq!(
            magi_tool.description,
            Some("Get the current weather in a location".into())
        );

        let schema = &magi_tool.input_schema;
        assert_eq!(schema["type"], "object");
        assert!(schema["properties"].is_object());
        assert!(schema["properties"]["location"].is_object());
        assert_eq!(schema["properties"]["location"]["type"], "string");
        assert!(schema["required"].is_array());
        assert_eq!(schema["required"][0], "location");
    }

    #[test]
    fn test_handle_none_parameters() {
        // Create a tool with None parameters
        let openai_tool = Tool {
            r#type: ToolType::Function,
            function: FunctionTool {
                name: "simple_tool".into(),
                description: Some("A tool with no parameters".into()),
                parameters: None,
            },
        };

        // Convert to MagiTool
        let magi_tool: MagiTool = openai_tool.into();

        // Verify conversion
        assert_eq!(magi_tool.name, "simple_tool");
        assert_eq!(
            magi_tool.description,
            Some("A tool with no parameters".into())
        );

        // The input_schema should be a null value when parameters is None
        assert!(magi_tool.input_schema.is_null());
    }

    #[test]
    fn test_roundtrip_conversion() {
        // Create a sample MagiTool
        let original_magi_tool = MagiTool {
            name: "test_function".into(),
            description: Some("A test function".into()),
            input_schema: json!({
                "type": "object",
                "properties": {
                    "arg1": {
                        "type": "string"
                    }
                },
                "required": ["arg1"]
            }),
        };

        // Convert to OpenAI Tool and back
        let openai_tool: Tool = original_magi_tool.clone().into();
        let roundtrip_magi_tool: MagiTool = openai_tool.into();

        // Verify the roundtrip conversion preserves data
        assert_eq!(roundtrip_magi_tool.name, original_magi_tool.name);
        assert_eq!(
            roundtrip_magi_tool.description,
            original_magi_tool.description
        );
        assert_eq!(
            roundtrip_magi_tool.input_schema,
            original_magi_tool.input_schema
        );
    }
}