starweaver-model 0.2.0

Provider-neutral model protocol and wire adapters for Starweaver
Documentation
//! Provider content part mapping helpers.

use base64::{engine::general_purpose::STANDARD, Engine as _};
use serde_json::{json, Value};

use crate::message::ContentPart;

#[cfg(test)]
pub fn text_from_content(content: &[ContentPart]) -> String {
    content
        .iter()
        .filter_map(|part| match part {
            ContentPart::Text { text } => Some(text.as_str()),
            ContentPart::ImageUrl { .. }
            | ContentPart::FileUrl { .. }
            | ContentPart::Binary { .. }
            | ContentPart::ResourceRef { .. }
            | ContentPart::DataUrl { .. } => None,
        })
        .collect::<Vec<_>>()
        .join("")
}

pub fn openai_chat_content(content: &[ContentPart]) -> Value {
    if content.len() == 1 {
        if let ContentPart::Text { text } = &content[0] {
            return json!(text);
        }
    }
    Value::Array(
        content
            .iter()
            .map(|part| match part {
                ContentPart::Text { text } => json!({"type": "text", "text": text}),
                ContentPart::ImageUrl { url } => {
                    json!({"type": "image_url", "image_url": {"url": url}})
                }
                ContentPart::FileUrl { url, media_type } => json!({
                    "type": "file",
                    "file": {"file_data": url, "media_type": media_type}
                }),
                ContentPart::Binary { data, media_type } => {
                    if media_type.starts_with("image/") {
                        json!({"type": "image_url", "image_url": {"url": data_url(media_type, data)}})
                    } else {
                        json!({"type": "file", "file": {"file_data": data_url(media_type, data), "media_type": media_type}})
                    }
                }
                ContentPart::ResourceRef { uri, media_type, .. } => {
                    if media_type.starts_with("image/") {
                        json!({"type": "image_url", "image_url": {"url": uri}})
                    } else {
                        json!({"type": "file", "file": {"file_data": uri, "media_type": media_type}})
                    }
                }
                ContentPart::DataUrl { data_url, media_type } => {
                    if media_type.starts_with("image/") {
                        json!({"type": "image_url", "image_url": {"url": data_url}})
                    } else {
                        json!({"type": "file", "file": {"file_data": data_url, "media_type": media_type}})
                    }
                }
            })
            .collect(),
    )
}

pub fn openai_responses_content(content: &[ContentPart]) -> Vec<Value> {
    content
        .iter()
        .map(|part| match part {
            ContentPart::Text { text } => json!({"type": "input_text", "text": text}),
            ContentPart::ImageUrl { url } => json!({"type": "input_image", "image_url": url}),
            ContentPart::FileUrl { url, media_type } => json!({
                "type": "input_file",
                "file_url": url,
                "media_type": media_type
            }),
            ContentPart::Binary { data, media_type } => {
                if media_type.starts_with("image/") {
                    json!({"type": "input_image", "image_url": data_url(media_type, data)})
                } else {
                    json!({"type": "input_file", "file_url": data_url(media_type, data), "media_type": media_type})
                }
            }
            ContentPart::ResourceRef { uri, media_type, .. } => {
                if media_type.starts_with("image/") {
                    json!({"type": "input_image", "image_url": uri})
                } else {
                    json!({"type": "input_file", "file_url": uri, "media_type": media_type})
                }
            }
            ContentPart::DataUrl { data_url, media_type } => {
                if media_type.starts_with("image/") {
                    json!({"type": "input_image", "image_url": data_url})
                } else {
                    json!({"type": "input_file", "file_url": data_url, "media_type": media_type})
                }
            }
        })
        .collect()
}

pub fn gemini_parts_from_content(content: &[ContentPart]) -> Vec<Value> {
    content
        .iter()
        .map(|part| match part {
            ContentPart::Text { text } => json!({"text": text}),
            ContentPart::ImageUrl { url } => json!({
                "fileData": {"fileUri": url, "mimeType": "image/*"}
            }),
            ContentPart::FileUrl { url, media_type } => json!({
                "fileData": {"fileUri": url, "mimeType": media_type}
            }),
            ContentPart::Binary { data, media_type } => json!({
                "inlineData": {"data": base64_payload(data), "mimeType": media_type}
            }),
            ContentPart::ResourceRef {
                uri, media_type, ..
            } => json!({
                "fileData": {"fileUri": uri, "mimeType": media_type}
            }),
            ContentPart::DataUrl {
                data_url,
                media_type,
            } => json!({
                "fileData": {"fileUri": data_url, "mimeType": media_type}
            }),
        })
        .collect()
}

pub fn bedrock_content_from_content(content: &[ContentPart]) -> Vec<Value> {
    content
        .iter()
        .map(|part| match part {
            ContentPart::Text { text } => json!({"text": text}),
            ContentPart::ImageUrl { url } => json!({"image": {"source": {"bytes": url}}}),
            ContentPart::FileUrl { url, media_type } => json!({
                "document": {
                    "format": media_type,
                    "source": {"bytes": url},
                }
            }),
            ContentPart::Binary { data, media_type } => {
                if media_type.starts_with("image/") {
                    json!({"image": {"format": bedrock_media_format(media_type), "source": {"bytes": base64_payload(data)}}})
                } else {
                    json!({"document": {"format": bedrock_media_format(media_type), "source": {"bytes": base64_payload(data)}}})
                }
            }
            ContentPart::ResourceRef { uri, media_type, .. } | ContentPart::DataUrl { data_url: uri, media_type } => {
                if media_type.starts_with("image/") {
                    json!({"image": {"format": bedrock_media_format(media_type), "source": {"bytes": uri}}})
                } else {
                    json!({"document": {"format": bedrock_media_format(media_type), "source": {"bytes": uri}}})
                }
            }
        })
        .collect()
}

fn data_url(media_type: &str, data: &[u8]) -> String {
    format!("data:{media_type};base64,{}", base64_payload(data))
}

fn base64_payload(data: &[u8]) -> String {
    STANDARD.encode(data)
}

fn bedrock_media_format(media_type: &str) -> &str {
    media_type
        .strip_prefix("image/")
        .or_else(|| media_type.strip_prefix("application/"))
        .unwrap_or(media_type)
}