Skip to main content

simple_agent_type/
message.rs

1//! Message types for LLM interactions.
2//!
3//! Provides role-based messages compatible with OpenAI's message format.
4
5use serde::{Deserialize, Serialize};
6
7use crate::tool::ToolCall;
8
9/// Role of a message in a conversation.
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
11#[serde(rename_all = "lowercase")]
12pub enum Role {
13    /// User message
14    User,
15    /// Assistant (LLM) message
16    Assistant,
17    /// System instruction message
18    System,
19    /// Tool/function call result
20    #[serde(rename = "tool")]
21    Tool,
22}
23
24/// A message in a conversation.
25#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
26pub struct Message {
27    /// Role of the message sender
28    pub role: Role,
29    /// Content of the message
30    pub content: String,
31    /// Optional name (for multi-user conversations or tool calls)
32    #[serde(skip_serializing_if = "Option::is_none")]
33    pub name: Option<String>,
34    /// Tool call ID (for tool role messages)
35    #[serde(skip_serializing_if = "Option::is_none")]
36    pub tool_call_id: Option<String>,
37    /// Tool calls emitted by the assistant.
38    #[serde(skip_serializing_if = "Option::is_none")]
39    pub tool_calls: Option<Vec<ToolCall>>,
40}
41
42impl Message {
43    /// Create a user message.
44    ///
45    /// # Example
46    /// ```
47    /// use simple_agent_type::message::{Message, Role};
48    ///
49    /// let msg = Message::user("Hello!");
50    /// assert_eq!(msg.role, Role::User);
51    /// assert_eq!(msg.content, "Hello!");
52    /// ```
53    pub fn user(content: impl Into<String>) -> Self {
54        Self {
55            role: Role::User,
56            content: content.into(),
57            name: None,
58            tool_call_id: None,
59            tool_calls: None,
60        }
61    }
62
63    /// Create an assistant message.
64    ///
65    /// # Example
66    /// ```
67    /// use simple_agent_type::message::{Message, Role};
68    ///
69    /// let msg = Message::assistant("Hi there!");
70    /// assert_eq!(msg.role, Role::Assistant);
71    /// ```
72    pub fn assistant(content: impl Into<String>) -> Self {
73        Self {
74            role: Role::Assistant,
75            content: content.into(),
76            name: None,
77            tool_call_id: None,
78            tool_calls: None,
79        }
80    }
81
82    /// Create a system message.
83    ///
84    /// # Example
85    /// ```
86    /// use simple_agent_type::message::{Message, Role};
87    ///
88    /// let msg = Message::system("You are a helpful assistant.");
89    /// assert_eq!(msg.role, Role::System);
90    /// ```
91    pub fn system(content: impl Into<String>) -> Self {
92        Self {
93            role: Role::System,
94            content: content.into(),
95            name: None,
96            tool_call_id: None,
97            tool_calls: None,
98        }
99    }
100
101    /// Create a tool message.
102    ///
103    /// # Example
104    /// ```
105    /// use simple_agent_type::message::{Message, Role};
106    ///
107    /// let msg = Message::tool("result", "call_123");
108    /// assert_eq!(msg.role, Role::Tool);
109    /// assert_eq!(msg.tool_call_id, Some("call_123".to_string()));
110    /// ```
111    pub fn tool(content: impl Into<String>, tool_call_id: impl Into<String>) -> Self {
112        Self {
113            role: Role::Tool,
114            content: content.into(),
115            name: None,
116            tool_call_id: Some(tool_call_id.into()),
117            tool_calls: None,
118        }
119    }
120
121    /// Set the name field (builder pattern).
122    ///
123    /// # Example
124    /// ```
125    /// use simple_agent_type::message::Message;
126    ///
127    /// let msg = Message::user("Hello").with_name("Alice");
128    /// assert_eq!(msg.name, Some("Alice".to_string()));
129    /// ```
130    pub fn with_name(mut self, name: impl Into<String>) -> Self {
131        self.name = Some(name.into());
132        self
133    }
134
135    /// Set tool calls for assistant messages.
136    pub fn with_tool_calls(mut self, tool_calls: Vec<ToolCall>) -> Self {
137        self.tool_calls = Some(tool_calls);
138        self
139    }
140}
141
142#[cfg(test)]
143mod tests {
144    use super::*;
145
146    #[test]
147    fn test_message_user() {
148        let msg = Message::user("test");
149        assert_eq!(msg.role, Role::User);
150        assert_eq!(msg.content, "test");
151        assert_eq!(msg.name, None);
152        assert_eq!(msg.tool_call_id, None);
153        assert_eq!(msg.tool_calls, None);
154    }
155
156    #[test]
157    fn test_message_assistant() {
158        let msg = Message::assistant("response");
159        assert_eq!(msg.role, Role::Assistant);
160        assert_eq!(msg.content, "response");
161        assert_eq!(msg.tool_calls, None);
162    }
163
164    #[test]
165    fn test_message_system() {
166        let msg = Message::system("instruction");
167        assert_eq!(msg.role, Role::System);
168        assert_eq!(msg.content, "instruction");
169        assert_eq!(msg.tool_calls, None);
170    }
171
172    #[test]
173    fn test_message_tool() {
174        let msg = Message::tool("result", "call_123");
175        assert_eq!(msg.role, Role::Tool);
176        assert_eq!(msg.content, "result");
177        assert_eq!(msg.tool_call_id, Some("call_123".to_string()));
178        assert_eq!(msg.tool_calls, None);
179    }
180
181    #[test]
182    fn test_message_with_name() {
183        let msg = Message::user("test").with_name("Alice");
184        assert_eq!(msg.name, Some("Alice".to_string()));
185    }
186
187    #[test]
188    fn test_role_serialization() {
189        let json = serde_json::to_string(&Role::User).unwrap();
190        assert_eq!(json, "\"user\"");
191
192        let json = serde_json::to_string(&Role::Assistant).unwrap();
193        assert_eq!(json, "\"assistant\"");
194
195        let json = serde_json::to_string(&Role::System).unwrap();
196        assert_eq!(json, "\"system\"");
197
198        let json = serde_json::to_string(&Role::Tool).unwrap();
199        assert_eq!(json, "\"tool\"");
200    }
201
202    #[test]
203    fn test_message_serialization() {
204        let msg = Message::user("Hello");
205        let json = serde_json::to_string(&msg).unwrap();
206        let parsed: Message = serde_json::from_str(&json).unwrap();
207        assert_eq!(msg, parsed);
208    }
209
210    #[test]
211    fn test_message_optional_fields_not_serialized() {
212        let msg = Message::user("test");
213        let json = serde_json::to_value(&msg).unwrap();
214        assert!(json.get("name").is_none());
215        assert!(json.get("tool_call_id").is_none());
216        assert!(json.get("tool_calls").is_none());
217    }
218
219    #[test]
220    fn test_message_with_name_serialized() {
221        let msg = Message::user("test").with_name("Alice");
222        let json = serde_json::to_value(&msg).unwrap();
223        assert_eq!(json.get("name").and_then(|v| v.as_str()), Some("Alice"));
224    }
225}