ambi 0.3.0

A flexible, multi-backend, customizable AI agent framework, entirely based on Rust.
// src/types/message.rs

use serde::{Deserialize, Serialize};
use std::fmt;

/// Represents a distinct segment within a message's content.
/// Essential for accommodating multi-modal models (VLM).
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
#[serde(tag = "type", rename_all = "lowercase")]
pub enum ContentPart {
    /// A standard textual segment.
    Text {
        /// The raw text string.
        text: String,
    },
    /// An image data segment.
    Image {
        /// The base64 encoded image string or standard HTTP URL.
        base64: String,
    },
}

/// Represents the entities and conversational turns within the dialogue history.
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
pub enum Message {
    /// Represents static system-level directives and rules.
    System {
        /// The textual instruction content.
        content: String,
    },
    /// Represents input provided by the human user. Supports multimodal parts.
    User {
        /// The ordered collection of text and media segments.
        content: Vec<ContentPart>,
    },
    /// Represents the strict JSON string output returned from an executed Tool.
    Tool {
        /// The raw string result generated by the tool.
        content: String,
        /// The unique invocation ID linking this result to the Assistant's request.
        tool_id: Option<String>,
    },
    /// Represents the response generated by the LLM (Assistant).
    Assistant {
        /// The conversational text generated by the model.
        content: String,
        /// Holds parsed tool requests generated by the Assistant.
        /// Tuple signature: (tool_name, tool_arguments, tool_call_id)
        tool_calls: Vec<(String, serde_json::Value, String)>,
    },
}

impl Message {
    /// Calculates the raw character length of the textual content within the message.
    pub fn text_len(&self) -> usize {
        match self {
            Message::System { content } => content.len(),
            Message::User { content } => content
                .iter()
                .map(|p| match p {
                    ContentPart::Text { text } => text.len(),
                    _ => 0,
                })
                .sum(),
            Message::Tool { content, .. } => content.len(),
            Message::Assistant { content, .. } => content.len(),
        }
    }

    /// Convenience helper to quickly create a multimodal user message.
    pub fn user_multimodal(text: &str, image_base64: &str) -> Self {
        Self::User {
            content: vec![
                ContentPart::Text {
                    text: text.to_string(),
                },
                ContentPart::Image {
                    base64: image_base64.to_string(),
                },
            ],
        }
    }
}

impl fmt::Display for Message {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Message::System { content } => write!(f, "{}", content),
            Message::User { content } => {
                let text: String = content
                    .iter()
                    .filter_map(|p| match p {
                        ContentPart::Text { text } => Some(text.as_str()),
                        _ => None,
                    })
                    .collect();
                write!(f, "{}", text)
            }
            Message::Tool { content, .. } => write!(f, "{}", content),
            Message::Assistant { content, .. } => write!(f, "{}", content),
        }
    }
}