use crate::messages::request::content::{CacheControl, ContentBlock, MediaType};
use crate::messages::request::role::Role;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Message {
pub role: Role,
pub content: Vec<ContentBlock>,
}
impl Message {
pub fn new(role: Role, content: Vec<ContentBlock>) -> Self {
Message { role, content }
}
pub fn user<T: AsRef<str>>(text: T) -> Self {
Message {
role: Role::User,
content: vec![ContentBlock::text(text)],
}
}
pub fn assistant<T: AsRef<str>>(text: T) -> Self {
Message {
role: Role::Assistant,
content: vec![ContentBlock::text(text)],
}
}
pub fn user_with_image<T: AsRef<str>>(text: T, media_type: MediaType, image_path: T) -> Self {
Message {
role: Role::User,
content: vec![
ContentBlock::image_from_path(media_type, image_path),
ContentBlock::text(text),
],
}
}
pub fn user_with_image_url<T: AsRef<str>>(text: T, image_url: T) -> Self {
Message {
role: Role::User,
content: vec![
ContentBlock::image_from_url(image_url),
ContentBlock::text(text),
],
}
}
pub fn tool_result<S: AsRef<str>>(tool_use_id: S, result_text: S) -> Self {
Message {
role: Role::User,
content: vec![ContentBlock::tool_result_text(tool_use_id, result_text)],
}
}
pub fn tool_error<S: AsRef<str>>(tool_use_id: S, error_message: S) -> Self {
Message {
role: Role::User,
content: vec![ContentBlock::tool_result_error(tool_use_id, error_message)],
}
}
pub fn add_content(&mut self, block: ContentBlock) -> &mut Self {
self.content.push(block);
self
}
pub fn add_text<T: AsRef<str>>(&mut self, text: T) -> &mut Self {
self.content.push(ContentBlock::text(text));
self
}
pub fn add_image_from_path<T: AsRef<str>>(
&mut self,
media_type: MediaType,
path: T,
) -> &mut Self {
self.content
.push(ContentBlock::image_from_path(media_type, path));
self
}
pub fn add_image_from_url<T: AsRef<str>>(&mut self, url: T) -> &mut Self {
self.content.push(ContentBlock::image_from_url(url));
self
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(untagged)]
pub enum SystemPrompt {
Text(String),
Blocks(Vec<SystemBlock>),
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct SystemBlock {
#[serde(rename = "type")]
pub type_name: String,
pub text: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub cache_control: Option<CacheControl>,
}
impl SystemPrompt {
pub fn text<T: AsRef<str>>(text: T) -> Self {
SystemPrompt::Text(text.as_ref().to_string())
}
pub fn with_cache<T: AsRef<str>>(text: T) -> Self {
SystemPrompt::Blocks(vec![SystemBlock {
type_name: "text".to_string(),
text: text.as_ref().to_string(),
cache_control: Some(CacheControl::ephemeral()),
}])
}
pub fn blocks(blocks: Vec<SystemBlock>) -> Self {
SystemPrompt::Blocks(blocks)
}
}
impl SystemBlock {
pub fn text<T: AsRef<str>>(text: T) -> Self {
SystemBlock {
type_name: "text".to_string(),
text: text.as_ref().to_string(),
cache_control: None,
}
}
pub fn text_with_cache<T: AsRef<str>>(text: T) -> Self {
SystemBlock {
type_name: "text".to_string(),
text: text.as_ref().to_string(),
cache_control: Some(CacheControl::ephemeral()),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::messages::request::content::ImageSource;
#[test]
fn test_user_message() {
let msg = Message::user("Hello!");
assert_eq!(msg.role, Role::User);
assert_eq!(msg.content.len(), 1);
let json = serde_json::to_string(&msg).unwrap();
assert!(json.contains("\"role\":\"user\""));
assert!(json.contains("\"text\":\"Hello!\""));
}
#[test]
fn test_assistant_message() {
let msg = Message::assistant("Hi there!");
assert_eq!(msg.role, Role::Assistant);
let json = serde_json::to_string(&msg).unwrap();
assert!(json.contains("\"role\":\"assistant\""));
}
#[test]
fn test_tool_result_message() {
let msg = Message::tool_result("tool_123", "Result data");
let json = serde_json::to_string(&msg).unwrap();
assert!(json.contains("\"role\":\"user\""));
assert!(json.contains("\"tool_use_id\":\"tool_123\""));
}
#[test]
fn test_system_prompt_text() {
let system = SystemPrompt::text("You are a helpful assistant.");
let json = serde_json::to_string(&system).unwrap();
assert_eq!(json, "\"You are a helpful assistant.\"");
}
#[test]
fn test_system_prompt_with_cache() {
let system = SystemPrompt::with_cache("Cached system prompt");
let json = serde_json::to_string(&system).unwrap();
assert!(json.contains("\"cache_control\""));
assert!(json.contains("\"type\":\"ephemeral\""));
}
#[test]
fn test_message_builder() {
let mut msg = Message::user("Initial text");
msg.add_text("More text")
.add_image_from_url("https://example.com/image.png");
assert_eq!(msg.content.len(), 3);
}
#[tokio::test]
async fn test_image_source_from_url_async() {
let source = ImageSource::from_url("https://example.com/image.png");
assert_eq!(source.type_name, "url");
assert!(source.url.is_some());
}
}