use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type")]
pub enum Message {
#[serde(rename = "user")]
User(UserMessage),
#[serde(rename = "assistant")]
Assistant(AssistantMessage),
#[serde(rename = "system")]
System(SystemMessage),
#[serde(rename = "result")]
Result(ResultMessage),
#[serde(rename = "stream_event")]
StreamEvent(StreamEventMessage),
}
impl Message {
pub fn user(content: impl Into<String>) -> Self {
Message::User(UserMessage {
message: UserMessageContent {
role: "user".to_string(),
content: ContentValue::Text(content.into()),
},
parent_tool_use_id: None,
})
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UserMessage {
pub message: UserMessageContent,
#[serde(skip_serializing_if = "Option::is_none")]
pub parent_tool_use_id: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UserMessageContent {
pub role: String,
pub content: ContentValue,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum ContentValue {
Text(String),
Blocks(Vec<ContentBlock>),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AssistantMessage {
pub message: AssistantMessageContent,
#[serde(skip_serializing_if = "Option::is_none")]
pub parent_tool_use_id: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AssistantMessageContent {
pub model: String,
pub content: Vec<ContentBlock>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SystemMessage {
pub subtype: String,
pub data: serde_json::Value,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ResultMessage {
pub subtype: String,
pub duration_ms: u64,
pub duration_api_ms: u64,
pub is_error: bool,
pub num_turns: u32,
pub session_id: String,
pub total_cost_usd: Option<f64>,
pub usage: Option<serde_json::Value>,
pub result: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StreamEventMessage {
pub uuid: String,
pub session_id: String,
pub event: serde_json::Value,
#[serde(skip_serializing_if = "Option::is_none")]
pub parent_tool_use_id: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type")]
pub enum ContentBlock {
#[serde(rename = "text")]
Text { text: String },
#[serde(rename = "thinking")]
Thinking { thinking: String, signature: String },
#[serde(rename = "tool_use")]
ToolUse {
id: String,
name: String,
input: serde_json::Value,
},
#[serde(rename = "tool_result")]
ToolResult {
tool_use_id: String,
#[serde(skip_serializing_if = "Option::is_none")]
content: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
is_error: Option<bool>,
},
}
pub fn parse_message(value: serde_json::Value) -> crate::error::Result<Message> {
serde_json::from_value(value).map_err(|e| crate::error::Error::ParseError(e.to_string()))
}