use serde::{Deserialize, Serialize};
use serde_json::Value;
use crate::harness::tool::ToolCall;
use crate::harness::usage::Usage;
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ContentBlock {
Text(String),
Json(Value),
Image(ImageRef),
Thinking {
text: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
signature: Option<String>,
},
RedactedThinking {
data: String,
},
ProviderExtension(Value),
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct ImageRef {
pub url: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub mime_type: Option<String>,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct SystemMessage {
pub content: Vec<ContentBlock>,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct UserMessage {
pub content: Vec<ContentBlock>,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct AssistantMessage {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub id: Option<String>,
pub content: Vec<ContentBlock>,
#[serde(default)]
pub tool_calls: Vec<ToolCall>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub usage: Option<Usage>,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct ToolMessage {
pub tool_call_id: String,
pub content: Vec<ContentBlock>,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum Message {
System(SystemMessage),
User(UserMessage),
Assistant(AssistantMessage),
Tool(ToolMessage),
}
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
pub struct MessageDelta {
#[serde(default)]
pub text: String,
#[serde(default, skip_serializing_if = "String::is_empty")]
pub reasoning: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub tool_call: Option<crate::harness::tool::ToolDelta>,
}
impl MessageDelta {
pub fn text(text: impl Into<String>) -> Self {
Self {
text: text.into(),
..Self::default()
}
}
pub fn reasoning(reasoning: impl Into<String>) -> Self {
Self {
reasoning: reasoning.into(),
..Self::default()
}
}
}