use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::collections::HashMap;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Message {
pub role: Role,
pub content: Vec<ContentBlock>,
#[serde(default)]
pub metadata: HashMap<String, Value>,
}
impl Message {
pub fn new(role: Role, content: Vec<ContentBlock>) -> Self {
Self {
role,
content,
metadata: HashMap::new(),
}
}
pub fn system(text: impl Into<String>) -> Self {
Self::new(Role::System, vec![ContentBlock::Text(text.into())])
}
pub fn user(text: impl Into<String>) -> Self {
Self::new(Role::User, vec![ContentBlock::Text(text.into())])
}
pub fn assistant(text: impl Into<String>) -> Self {
Self::new(Role::Assistant, vec![ContentBlock::Text(text.into())])
}
pub fn tool_result(tool_use_id: impl Into<String>, content: Value, is_error: bool) -> Self {
Self::new(
Role::Tool,
vec![ContentBlock::ToolResult {
tool_use_id: tool_use_id.into(),
content,
is_error,
}],
)
}
pub fn text_content(&self) -> Option<String> {
let texts: Vec<&str> = self
.content
.iter()
.filter_map(|c| match c {
ContentBlock::Text(t) => Some(t.as_str()),
_ => None,
})
.collect();
if texts.is_empty() {
None
} else {
Some(texts.join(""))
}
}
pub fn tool_uses(&self) -> Vec<ToolCall> {
self
.content
.iter()
.filter_map(|c| match c {
ContentBlock::ToolUse { id, name, input } => Some(ToolCall {
id: id.clone(),
name: name.clone(),
input: input.clone(),
}),
_ => None,
})
.collect()
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Role {
System,
User,
Assistant,
Tool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum ContentBlock {
Text(String),
Image { url: String, media_type: String },
ToolUse { id: String, name: String, input: Value },
ToolResult { tool_use_id: String, content: Value, is_error: bool },
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ToolCall {
pub id: String,
pub name: String,
pub input: Value,
}