use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use crate::tool::ToolCall;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum Role {
System,
User,
Assistant,
Tool,
Signal,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum ContentBlock {
Text {
text: String,
},
Image {
image: ImageContent,
},
ToolUse {
id: String,
#[serde(flatten)]
call: ToolCall,
},
ToolResult {
tool_use_id: String,
content: Value,
},
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ImageContent {
#[serde(rename = "media_type")]
pub media_type: String,
#[serde(with = "serde_base64")]
pub data: Vec<u8>,
}
mod serde_base64 {
use base64::{engine::general_purpose::STANDARD, Engine};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
pub fn serialize<S: Serializer>(v: &[u8], s: S) -> Result<S::Ok, S::Error> {
STANDARD.encode(v).serialize(s)
}
pub fn deserialize<'de, D: Deserializer<'de>>(d: D) -> Result<Vec<u8>, D::Error> {
let s = String::deserialize(d)?;
STANDARD
.decode(s.as_bytes())
.map_err(serde::de::Error::custom)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SignalMessage {
pub role: Role,
#[serde(rename = "type")]
pub kind: String,
#[serde(rename = "tag_name", skip_serializing_if = "Option::is_none")]
pub tag_name: Option<String>,
pub content: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub attributes: Option<std::collections::BTreeMap<String, String>>,
pub timestamp: DateTime<Utc>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AgentMessage {
pub role: Role,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub content: Vec<ContentBlock>,
}