1use serde::{Deserialize, Serialize};
7use serde_json::Value;
8use std::collections::HashMap;
9
10#[derive(Debug, Clone, Serialize, Deserialize)]
17#[serde(tag = "role")]
18#[non_exhaustive]
19pub enum Message {
20 Human(HumanMessage),
21 Ai(AiMessage),
22 System(SystemMessage),
23 Tool(ToolMessage),
24}
25
26#[derive(Debug, Clone, Serialize, Deserialize)]
28pub struct HumanMessage {
29 pub content: MessageContent,
30 #[serde(default)]
31 pub id: Option<String>,
32 #[serde(default)]
33 pub name: Option<String>,
34}
35
36#[derive(Debug, Clone, Serialize, Deserialize)]
38pub struct AiMessage {
39 pub content: MessageContent,
40 #[serde(default)]
41 pub tool_calls: Vec<ToolCall>,
42 #[serde(default)]
43 pub invalid_tool_calls: Vec<InvalidToolCall>,
44 #[serde(default)]
45 pub usage_metadata: Option<UsageMetadata>,
46 #[serde(default)]
47 pub response_metadata: HashMap<String, serde_json::Value>,
48 #[serde(default)]
49 pub id: Option<String>,
50}
51
52#[derive(Debug, Clone, Serialize, Deserialize)]
54pub struct SystemMessage {
55 pub content: String,
56 #[serde(default)]
57 pub id: Option<String>,
58}
59
60#[derive(Debug, Clone, Serialize, Deserialize)]
62pub struct ToolMessage {
63 pub content: String,
64 pub tool_call_id: String,
65 #[serde(default)]
66 pub name: Option<String>,
67 #[serde(default)]
68 pub id: Option<String>,
69 #[serde(default, skip_serializing_if = "Option::is_none")]
71 pub metadata: Option<HashMap<String, Value>>,
72 #[serde(default, skip_serializing_if = "Option::is_none")]
74 pub duration_ms: Option<u64>,
75}
76
77#[derive(Debug, Clone, Serialize, Deserialize)]
79#[serde(untagged)]
80#[non_exhaustive]
81pub enum MessageContent {
82 Text(String),
83 Blocks(Vec<ContentBlock>),
84}
85
86#[derive(Debug, Clone, Serialize, Deserialize)]
88#[serde(tag = "type")]
89#[non_exhaustive]
90pub enum ContentBlock {
91 Text { text: String },
92 Image { url: String },
93 Audio { data: String },
94 File { path: String, mime_type: String },
95 Reasoning { content: String },
96 Citation { source: String, quote: String },
97}
98
99#[derive(Debug, Clone, Serialize, Deserialize)]
101pub struct ToolCall {
102 pub id: String,
103 pub name: String,
104 pub args: serde_json::Value,
105}
106
107#[derive(Debug, Clone, Serialize, Deserialize)]
109pub struct InvalidToolCall {
110 pub id: String,
111 pub name: String,
112 pub args: String,
113 pub error: String,
114}
115
116#[derive(Debug, Clone, Serialize, Deserialize)]
118pub struct UsageMetadata {
119 pub input_tokens: u32,
120 pub output_tokens: u32,
121 pub total_tokens: u32,
122 #[serde(default)]
123 pub input_token_details: Option<InputTokenDetails>,
124 #[serde(default)]
125 pub output_token_details: Option<OutputTokenDetails>,
126}
127
128#[derive(Debug, Clone, Serialize, Deserialize, Default)]
129pub struct InputTokenDetails {
130 #[serde(default)]
131 pub cache_read: u32,
132 #[serde(default)]
133 pub cache_write: u32,
134}
135
136#[derive(Debug, Clone, Serialize, Deserialize, Default)]
137pub struct OutputTokenDetails {
138 #[serde(default)]
139 pub reasoning_tokens: u32,
140}
141
142pub const REMOVE_ALL_MESSAGES: &str = "__REMOVE_ALL_MESSAGES__";
144
145impl Message {
146 pub fn human(content: impl Into<String>) -> Self {
147 Message::Human(HumanMessage {
148 content: MessageContent::Text(content.into()),
149 id: None,
150 name: None,
151 })
152 }
153
154 pub fn system(content: impl Into<String>) -> Self {
155 Message::System(SystemMessage {
156 content: content.into(),
157 id: None,
158 })
159 }
160
161 pub fn ai(content: impl Into<String>) -> Self {
162 Message::Ai(AiMessage {
163 content: MessageContent::Text(content.into()),
164 tool_calls: vec![],
165 invalid_tool_calls: vec![],
166 usage_metadata: None,
167 response_metadata: HashMap::new(),
168 id: None,
169 })
170 }
171
172 pub fn tool(content: impl Into<String>, tool_call_id: impl Into<String>) -> Self {
173 Message::Tool(ToolMessage {
174 content: content.into(),
175 tool_call_id: tool_call_id.into(),
176 name: None,
177 id: None,
178 metadata: None,
179 duration_ms: None,
180 })
181 }
182
183 pub fn tool_with_metadata(
185 content: impl Into<String>,
186 tool_call_id: impl Into<String>,
187 metadata: Option<HashMap<String, Value>>,
188 duration_ms: Option<u64>,
189 ) -> Self {
190 Message::Tool(ToolMessage {
191 content: content.into(),
192 tool_call_id: tool_call_id.into(),
193 name: None,
194 id: None,
195 metadata,
196 duration_ms,
197 })
198 }
199
200 pub fn id(&self) -> Option<&str> {
201 match self {
202 Message::Human(m) => m.id.as_deref(),
203 Message::Ai(m) => m.id.as_deref(),
204 Message::System(m) => m.id.as_deref(),
205 Message::Tool(m) => m.id.as_deref(),
206 }
207 }
208}
209
210impl MessageContent {
211 pub fn as_text(&self) -> Option<&str> {
212 match self {
213 MessageContent::Text(t) => Some(t),
214 MessageContent::Blocks(_) => None,
215 }
216 }
217}
218
219