Skip to main content

cortexai_core/
message.rs

1//! Message types for agent communication
2
3use crate::types::AgentId;
4use chrono::{DateTime, Utc};
5use serde::{Deserialize, Serialize};
6
7/// Message content types
8#[derive(Debug, Clone, Serialize, Deserialize)]
9#[serde(tag = "type", content = "data")]
10pub enum Content {
11    /// Plain text message
12    Text(String),
13
14    /// Tool/function call request
15    ToolCall(Vec<ToolCall>),
16
17    /// Tool/function execution result
18    ToolResult(Vec<ToolResult>),
19
20    /// Structured data
21    StructuredData(serde_json::Value),
22
23    /// Image data (base64 encoded)
24    Image { data: String, mime_type: String },
25
26    /// Audio data (base64 encoded)
27    Audio { data: String, mime_type: String },
28
29    /// File reference
30    File { path: String, mime_type: String },
31
32    /// Error message
33    Error { code: String, message: String },
34}
35
36/// Tool/function call
37#[derive(Debug, Clone, Serialize, Deserialize)]
38pub struct ToolCall {
39    /// Unique call ID
40    pub id: String,
41
42    /// Tool/function name
43    pub name: String,
44
45    /// Arguments as JSON
46    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/// Tool/function execution result
60#[derive(Debug, Clone, Serialize, Deserialize)]
61pub struct ToolResult {
62    /// Call ID this result corresponds to
63    pub call_id: String,
64
65    /// Success status
66    pub success: bool,
67
68    /// Result data
69    pub data: serde_json::Value,
70
71    /// Error message if failed
72    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/// Message between agents
96#[derive(Debug, Clone, Serialize, Deserialize)]
97pub struct Message {
98    /// Unique message ID
99    pub id: String,
100
101    /// Sender agent ID
102    pub from: AgentId,
103
104    /// Recipient agent ID
105    pub to: AgentId,
106
107    /// Message content
108    pub content: Content,
109
110    /// Message timestamp
111    pub timestamp: DateTime<Utc>,
112
113    /// Message metadata
114    pub metadata: std::collections::HashMap<String, serde_json::Value>,
115
116    /// Message priority (0-255, higher = more urgent)
117    pub priority: u8,
118}
119
120impl Message {
121    /// Create a new message
122    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, // Default medium priority
131        }
132    }
133
134    /// Create a system message (from system to agent)
135    pub fn system(to: AgentId, content: Content) -> Self {
136        Self::new(AgentId::new("system"), to, content)
137    }
138
139    /// Create a system welcome message
140    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    /// Create a user message
148    pub fn user(to: AgentId, text: impl Into<String>) -> Self {
149        Self::new(AgentId::new("user"), to, Content::Text(text.into()))
150    }
151
152    /// Set message priority
153    pub fn with_priority(mut self, priority: u8) -> Self {
154        self.priority = priority;
155        self
156    }
157
158    /// Add metadata
159    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    /// Check if message is a tool call
165    pub fn is_tool_call(&self) -> bool {
166        matches!(self.content, Content::ToolCall(_))
167    }
168
169    /// Check if message is a tool result
170    pub fn is_tool_result(&self) -> bool {
171        matches!(self.content, Content::ToolResult(_))
172    }
173
174    /// Check if message is plain text
175    pub fn is_text(&self) -> bool {
176        matches!(self.content, Content::Text(_))
177    }
178
179    /// Get text content if available
180    pub fn as_text(&self) -> Option<&str> {
181        match &self.content {
182            Content::Text(text) => Some(text),
183            _ => None,
184        }
185    }
186}
187
188/// Message role for LLM conversation
189#[derive(Debug, Clone, Serialize, Deserialize)]
190pub enum MessageRole {
191    /// System message (instructions)
192    System,
193    /// User message
194    User,
195    /// Assistant message
196    Assistant,
197    /// Tool/function result
198    Tool,
199}
200
201/// LLM conversation message format
202#[derive(Debug, Clone, Serialize, Deserialize)]
203pub struct LLMMessage {
204    /// Message role
205    pub role: MessageRole,
206
207    /// Message content
208    pub content: String,
209
210    /// Tool calls (if any)
211    #[serde(skip_serializing_if = "Option::is_none")]
212    pub tool_calls: Option<Vec<ToolCall>>,
213
214    /// Tool call ID (for tool role)
215    #[serde(skip_serializing_if = "Option::is_none")]
216    pub tool_call_id: Option<String>,
217
218    /// Function name (for tool role)
219    #[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}