magi-openai 0.1.0

OpenAI compatible API SDK for Magi AI agents
Documentation
use serde::{Deserialize, Serialize};

/// Request body for the Images API.
#[derive(Clone, Serialize, Default, Debug, Deserialize, PartialEq)]
pub struct RequestBody {
    /// Background mode for generated images.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub background: Option<String>,

    /// ID of the image model to use.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub model: Option<String>,

    /// Text description of the image to generate.
    pub prompt: String,

    /// Content moderation level for GPT image models.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub moderation: Option<String>,

    /// Number of images to generate.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub n: Option<u32>,

    /// Compression level for `webp` or `jpeg` output from GPT image models.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub output_compression: Option<u8>,

    /// Output image format for GPT image models.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub output_format: Option<String>,

    /// Number of partial images to generate for streaming responses.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub partial_images: Option<u8>,

    /// Requested image quality.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub quality: Option<String>,

    /// Response format for DALL-E image models.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub response_format: Option<String>,

    /// Requested image size, such as `1536x656`.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub size: Option<String>,

    /// Whether to stream image generation events.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub stream: Option<bool>,

    /// Image style for DALL-E 3.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub style: Option<String>,

    /// End-user identifier for abuse monitoring.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub user: Option<String>,
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn serde_gpt_image_2_request() {
        let json = r##"{
            "model": "gpt-image-2",
            "prompt": "A clean academic cover for a WeChat article about Structural Equation Modeling. Design: solid deep navy (#1f3a5f) background, restrained. Left zone: Title '问卷论文如何做出高级感?' in white bold; subtitle '一文读懂结构方程模型(SEM)' in 85% white. Right zone: a minimal path diagram showing latent variables (circles) connected by arrows to observed variables (rectangles), forming a clean SEM path diagram, thin white lines, one amber (#d97706) accent on the main path coefficient between two latent circles. No gradients, no clutter. Style: academic, rigorous, minimal. All Chinese text in simplified Chinese, clear and readable. 2.35:1 aspect ratio.",
            "n": 1,
            "size": "1536x656",
            "quality": "low"
        }"##;

        let expected = RequestBody {
            background: None,
            model: Some("gpt-image-2".to_string()),
            prompt: "A clean academic cover for a WeChat article about Structural Equation Modeling. Design: solid deep navy (#1f3a5f) background, restrained. Left zone: Title '问卷论文如何做出高级感?' in white bold; subtitle '一文读懂结构方程模型(SEM)' in 85% white. Right zone: a minimal path diagram showing latent variables (circles) connected by arrows to observed variables (rectangles), forming a clean SEM path diagram, thin white lines, one amber (#d97706) accent on the main path coefficient between two latent circles. No gradients, no clutter. Style: academic, rigorous, minimal. All Chinese text in simplified Chinese, clear and readable. 2.35:1 aspect ratio.".to_string(),
            moderation: None,
            n: Some(1),
            output_compression: None,
            output_format: None,
            partial_images: None,
            quality: Some("low".to_string()),
            response_format: None,
            size: Some("1536x656".to_string()),
            stream: None,
            style: None,
            user: None,
        };

        let actual: RequestBody = serde_json::from_str(json).unwrap();
        assert_eq!(actual, expected);

        let serialized = serde_json::to_value(&expected).unwrap();
        let source: serde_json::Value = serde_json::from_str(json).unwrap();
        assert_eq!(serialized, source);

        let roundtrip: RequestBody = serde_json::from_value(serialized).unwrap();
        assert_eq!(roundtrip, expected);
    }

    #[test]
    fn serde_official_reference_request_example() {
        let json = r#"{
            "model": "gpt-image-1.5",
            "prompt": "A cute baby sea otter",
            "n": 1,
            "size": "1024x1024"
        }"#;

        let expected = RequestBody {
            background: None,
            model: Some("gpt-image-1.5".to_string()),
            prompt: "A cute baby sea otter".to_string(),
            moderation: None,
            n: Some(1),
            output_compression: None,
            output_format: None,
            partial_images: None,
            quality: None,
            response_format: None,
            size: Some("1024x1024".to_string()),
            stream: None,
            style: None,
            user: None,
        };

        let actual: RequestBody = serde_json::from_str(json).unwrap();
        assert_eq!(actual, expected);

        let serialized = serde_json::to_value(&expected).unwrap();
        let source: serde_json::Value = serde_json::from_str(json).unwrap();
        assert_eq!(serialized, source);

        let roundtrip: RequestBody = serde_json::from_value(serialized).unwrap();
        assert_eq!(roundtrip, expected);
    }
}