pe-core 0.1.0

Core types for Potential Expectations — messages, channels, state, traits
Documentation
//! Message formatter trait -- decouples wire format from LLM provider.
//!
//! Each LLM provider (OpenAI, Anthropic, custom) has its own message format.
//! The `MessageFormatter` trait handles translation so providers only deal
//! with their native format.
//!
//! # Example
//!
//! ```
//! use pe_core::formatter::MessageFormatter;
//! use pe_core::openai_formatter::OpenAiFormatter;
//! use pe_core::{Message, ToolSchema, LlmResponse};
//!
//! let formatter = OpenAiFormatter;
//! let messages = vec![Message::human("Hello"), Message::system("Be helpful")];
//! let wire = formatter.format_messages(&messages).unwrap();
//! assert!(wire.is_array());
//! ```

use crate::error::PeError;
use crate::llm::{LlmResponse, ToolSchema};
use crate::message::Message;

/// Trait for converting between library Message types and provider-specific wire formats.
///
/// Each LLM provider has its own message format. The formatter handles
/// the translation so providers only deal with their native format.
///
/// Implementors produce `serde_json::Value` so the provider HTTP layer can
/// embed the result directly into request bodies without further conversion.
pub trait MessageFormatter: Send + Sync + 'static {
    /// Provider name for debugging and logging.
    ///
    /// # Example
    ///
    /// ```
    /// use pe_core::openai_formatter::OpenAiFormatter;
    /// use pe_core::formatter::MessageFormatter;
    ///
    /// assert_eq!(OpenAiFormatter.name(), "openai");
    /// ```
    fn name(&self) -> &str;

    /// Convert library messages to the provider's wire format (as JSON Value).
    ///
    /// Returns an array of message objects in the provider's expected format.
    /// Unknown or unsupported message variants are silently skipped.
    fn format_messages(&self, messages: &[Message]) -> Result<serde_json::Value, PeError>;

    /// Convert tool schemas to the provider's tool format (as JSON Value).
    ///
    /// Returns an array of tool definition objects. Returns an empty array
    /// for empty input.
    fn format_tools(&self, tools: &[ToolSchema]) -> Result<serde_json::Value, PeError>;

    /// Parse a provider response back into library types.
    ///
    /// Takes the raw JSON response body from the provider and extracts
    /// the AI message, usage metadata, and provider-specific metadata.
    fn parse_response(&self, raw: &serde_json::Value) -> Result<LlmResponse, PeError>;
}

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

    /// Verify the trait is object-safe (can be used as `Box<dyn MessageFormatter>`).
    #[test]
    fn test_message_formatter_is_object_safe() {
        fn _accepts_boxed(_f: Box<dyn MessageFormatter>) {}
        // Compiles = object-safe. No runtime assertion needed.
    }
}