caliban-provider 0.3.0

Provider-neutral message IR and trait for the caliban agent harness — internal crate for the caliban binary; no API stability, pin exact versions
Documentation
//! Core message types: roles, messages, content blocks.

use serde::{Deserialize, Serialize};

use crate::cache::CacheControl;
use crate::thinking::ThinkingBlock;
use crate::tool::{ToolResultBlock, ToolUseBlock};

/// The conversational role of a message.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum Role {
    /// A message from the end user.
    User,
    /// A message generated by the assistant.
    Assistant,
    /// An instruction message that precedes the conversation.
    System,
}

/// A single turn in a conversation.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Message {
    /// The role of the speaker.
    pub role: Role,
    /// The content of the message as an ordered list of blocks.
    pub content: Vec<ContentBlock>,
}

impl Message {
    /// Construct a single-block user text message.
    pub fn user_text(text: impl Into<String>) -> Self {
        Self {
            role: Role::User,
            content: vec![ContentBlock::Text(TextBlock {
                text: text.into(),
                cache_control: None,
            })],
        }
    }

    /// Construct a single-block assistant text message.
    pub fn assistant_text(text: impl Into<String>) -> Self {
        Self {
            role: Role::Assistant,
            content: vec![ContentBlock::Text(TextBlock {
                text: text.into(),
                cache_control: None,
            })],
        }
    }

    /// Construct a single-block system text message.
    pub fn system_text(text: impl Into<String>) -> Self {
        Self {
            role: Role::System,
            content: vec![ContentBlock::Text(TextBlock {
                text: text.into(),
                cache_control: None,
            })],
        }
    }
}

/// A typed content block within a message.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum ContentBlock {
    /// A plain-text block.
    Text(TextBlock),
    /// An image block.
    Image(ImageBlock),
    /// A tool-use invocation block.
    ToolUse(ToolUseBlock),
    /// A tool-result block.
    ToolResult(ToolResultBlock),
    /// An extended-thinking block.
    Thinking(ThinkingBlock),
}

/// A plain-text content block.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct TextBlock {
    /// The text content.
    pub text: String,
    /// Optional cache-control marker.
    #[serde(skip_serializing_if = "Option::is_none", default)]
    pub cache_control: Option<CacheControl>,
}

/// An image content block.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ImageBlock {
    /// The source of the image data.
    pub source: ImageSource,
    /// Optional cache-control marker.
    #[serde(skip_serializing_if = "Option::is_none", default)]
    pub cache_control: Option<CacheControl>,
    /// SHA-256 fingerprint (64-char hex) of the (post-resize) image bytes.
    /// Populated by [`caliban-images`] at ingest time; the provider IR
    /// itself does not require it.
    #[serde(skip_serializing_if = "Option::is_none", default)]
    pub sha256: Option<String>,
    /// (width, height) in pixels, populated at ingest.
    #[serde(skip_serializing_if = "Option::is_none", default)]
    pub dims: Option<(u32, u32)>,
}

/// The source of image data.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum ImageSource {
    /// Base64-encoded image with a MIME type.
    Base64 {
        /// MIME type of the image (e.g. `"image/png"`).
        media_type: String,
        /// Base64-encoded image bytes.
        data: String,
    },
    /// A URL pointing to the image.
    Url {
        /// The URL of the image.
        url: String,
    },
    /// A reference to a session-local blob, identified by SHA-256.
    ///
    /// **Never** sent to a provider: provider adapters must reject `BlobRef`
    /// (or, equivalently, the session loader resolves it to
    /// [`ImageSource::Base64`] before dispatch). Round-trips through
    /// `serde_json` because session storage persists this variant on-disk.
    BlobRef {
        /// SHA-256 fingerprint (64-char hex).
        sha256: String,
        /// MIME type of the referenced blob.
        media_type: String,
    },
}