ic-rig 0.2.0

A lean, modular library for building LLM applications. Bring your own HTTP client.
Documentation
//! Core message types shared across all providers.
//!
//! A [`Message`] represents a single turn in a conversation. Providers are
//! responsible for converting these generic types into their API-specific format
//! via `From`/`TryFrom`.

use serde::{Deserialize, Serialize};

// ── Content primitives ────────────────────────────────────────────────────────

/// Plain text content.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Text {
    pub text: String,
}

impl From<&str> for Text {
    fn from(s: &str) -> Self {
        Self { text: s.to_owned() }
    }
}

impl From<String> for Text {
    fn from(s: String) -> Self {
        Self { text: s }
    }
}

/// A tool invocation requested by the model.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ToolCall {
    /// Provider-assigned identifier for this call (used to correlate results).
    pub id: String,
    /// Name of the tool to call.
    pub name: String,
    /// JSON-encoded arguments for the tool.
    pub arguments: serde_json::Value,
}

/// The result of executing a tool, sent back to the model.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ToolResult {
    /// Must match the [`ToolCall::id`] this result answers.
    pub call_id: String,
    /// Tool name (some providers require this in addition to the id).
    pub name: String,
    /// The serialised output returned by the tool.
    pub content: String,
}

// ── User content ──────────────────────────────────────────────────────────────

/// Content that can appear in a user-turn message.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum UserContent {
    /// A text message from the user.
    Text(Text),
    /// The result of a previous tool call.
    ToolResult(ToolResult),
}

impl From<&str> for UserContent {
    fn from(s: &str) -> Self {
        UserContent::Text(s.into())
    }
}

impl From<String> for UserContent {
    fn from(s: String) -> Self {
        UserContent::Text(s.into())
    }
}

impl From<ToolResult> for UserContent {
    fn from(r: ToolResult) -> Self {
        UserContent::ToolResult(r)
    }
}

// ── Assistant content ─────────────────────────────────────────────────────────

/// Content that can appear in an assistant-turn message.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum AssistantContent {
    /// A text reply from the model.
    Text(Text),
    /// A tool call issued by the model.
    ToolCall(ToolCall),
}

// ── Message ───────────────────────────────────────────────────────────────────

/// A single turn in a conversation.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "role", rename_all = "lowercase")]
pub enum Message {
    /// Instruction context prepended before the conversation (system prompt).
    System { content: String },
    /// Input from the human side of the conversation.
    User { content: Vec<UserContent> },
    /// Output from the model.
    Assistant { content: Vec<AssistantContent> },
}

impl Message {
    /// Convenience constructor for a plain text user message.
    pub fn user(text: impl Into<String>) -> Self {
        Message::User {
            content: vec![UserContent::Text(Text { text: text.into() })],
        }
    }

    /// Convenience constructor for a plain text assistant message.
    pub fn assistant(text: impl Into<String>) -> Self {
        Message::Assistant {
            content: vec![AssistantContent::Text(Text { text: text.into() })],
        }
    }

    /// Convenience constructor for a system message.
    pub fn system(text: impl Into<String>) -> Self {
        Message::System { content: text.into() }
    }
}