1use crate::types::AgentId;
4use chrono::{DateTime, Utc};
5use serde::{Deserialize, Serialize};
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
9#[serde(tag = "type", content = "data")]
10pub enum Content {
11 Text(String),
13
14 ToolCall(Vec<ToolCall>),
16
17 ToolResult(Vec<ToolResult>),
19
20 StructuredData(serde_json::Value),
22
23 Image { data: String, mime_type: String },
25
26 Audio { data: String, mime_type: String },
28
29 File { path: String, mime_type: String },
31
32 Error { code: String, message: String },
34}
35
36#[derive(Debug, Clone, Serialize, Deserialize)]
38pub struct ToolCall {
39 pub id: String,
41
42 pub name: String,
44
45 pub arguments: serde_json::Value,
47}
48
49impl ToolCall {
50 pub fn new(name: impl Into<String>, arguments: serde_json::Value) -> Self {
51 Self {
52 id: uuid::Uuid::new_v4().to_string(),
53 name: name.into(),
54 arguments,
55 }
56 }
57}
58
59#[derive(Debug, Clone, Serialize, Deserialize)]
61pub struct ToolResult {
62 pub call_id: String,
64
65 pub success: bool,
67
68 pub data: serde_json::Value,
70
71 pub error: Option<String>,
73}
74
75impl ToolResult {
76 pub fn success(call_id: String, data: serde_json::Value) -> Self {
77 Self {
78 call_id,
79 success: true,
80 data,
81 error: None,
82 }
83 }
84
85 pub fn failure(call_id: String, error: impl Into<String>) -> Self {
86 Self {
87 call_id,
88 success: false,
89 data: serde_json::Value::Null,
90 error: Some(error.into()),
91 }
92 }
93}
94
95#[derive(Debug, Clone, Serialize, Deserialize)]
97pub struct Message {
98 pub id: String,
100
101 pub from: AgentId,
103
104 pub to: AgentId,
106
107 pub content: Content,
109
110 pub timestamp: DateTime<Utc>,
112
113 pub metadata: std::collections::HashMap<String, serde_json::Value>,
115
116 pub priority: u8,
118}
119
120impl Message {
121 pub fn new(from: AgentId, to: AgentId, content: Content) -> Self {
123 Self {
124 id: uuid::Uuid::new_v4().to_string(),
125 from,
126 to,
127 content,
128 timestamp: Utc::now(),
129 metadata: std::collections::HashMap::new(),
130 priority: 128, }
132 }
133
134 pub fn system(to: AgentId, content: Content) -> Self {
136 Self::new(AgentId::new("system"), to, content)
137 }
138
139 pub fn system_welcome(to: &AgentId) -> Self {
141 Self::system(
142 to.clone(),
143 Content::Text("Welcome! I'm ready to assist you.".to_string()),
144 )
145 }
146
147 pub fn user(to: AgentId, text: impl Into<String>) -> Self {
149 Self::new(AgentId::new("user"), to, Content::Text(text.into()))
150 }
151
152 pub fn with_priority(mut self, priority: u8) -> Self {
154 self.priority = priority;
155 self
156 }
157
158 pub fn with_metadata(mut self, key: impl Into<String>, value: serde_json::Value) -> Self {
160 self.metadata.insert(key.into(), value);
161 self
162 }
163
164 pub fn is_tool_call(&self) -> bool {
166 matches!(self.content, Content::ToolCall(_))
167 }
168
169 pub fn is_tool_result(&self) -> bool {
171 matches!(self.content, Content::ToolResult(_))
172 }
173
174 pub fn is_text(&self) -> bool {
176 matches!(self.content, Content::Text(_))
177 }
178
179 pub fn as_text(&self) -> Option<&str> {
181 match &self.content {
182 Content::Text(text) => Some(text),
183 _ => None,
184 }
185 }
186}
187
188#[derive(Debug, Clone, Serialize, Deserialize)]
190pub enum MessageRole {
191 System,
193 User,
195 Assistant,
197 Tool,
199}
200
201#[derive(Debug, Clone, Serialize, Deserialize)]
203pub struct LLMMessage {
204 pub role: MessageRole,
206
207 pub content: String,
209
210 #[serde(skip_serializing_if = "Option::is_none")]
212 pub tool_calls: Option<Vec<ToolCall>>,
213
214 #[serde(skip_serializing_if = "Option::is_none")]
216 pub tool_call_id: Option<String>,
217
218 #[serde(skip_serializing_if = "Option::is_none")]
220 pub name: Option<String>,
221}
222
223impl LLMMessage {
224 pub fn system(content: impl Into<String>) -> Self {
225 Self {
226 role: MessageRole::System,
227 content: content.into(),
228 tool_calls: None,
229 tool_call_id: None,
230 name: None,
231 }
232 }
233
234 pub fn user(content: impl Into<String>) -> Self {
235 Self {
236 role: MessageRole::User,
237 content: content.into(),
238 tool_calls: None,
239 tool_call_id: None,
240 name: None,
241 }
242 }
243
244 pub fn assistant(content: impl Into<String>) -> Self {
245 Self {
246 role: MessageRole::Assistant,
247 content: content.into(),
248 tool_calls: None,
249 tool_call_id: None,
250 name: None,
251 }
252 }
253
254 pub fn assistant_with_tools(tool_calls: Vec<ToolCall>) -> Self {
255 Self {
256 role: MessageRole::Assistant,
257 content: String::new(),
258 tool_calls: Some(tool_calls),
259 tool_call_id: None,
260 name: None,
261 }
262 }
263
264 pub fn tool(tool_call_id: String, name: String, content: impl Into<String>) -> Self {
265 Self {
266 role: MessageRole::Tool,
267 content: content.into(),
268 tool_calls: None,
269 tool_call_id: Some(tool_call_id),
270 name: Some(name),
271 }
272 }
273}