agent_io/llm/types/
message.rs1use serde::{Deserialize, Serialize};
4
5use super::content::ContentPart;
6use super::tool::ToolCall;
7
8#[derive(Debug, Clone, Serialize, Deserialize)]
10pub struct UserMessage {
11 pub role: String,
12 pub content: Vec<ContentPart>,
13 #[serde(skip_serializing_if = "Option::is_none")]
14 pub name: Option<String>,
15}
16
17impl UserMessage {
18 pub fn new(content: impl Into<String>) -> Self {
19 Self {
20 role: "user".to_string(),
21 content: vec![ContentPart::text(content)],
22 name: None,
23 }
24 }
25
26 pub fn with_parts(content: Vec<ContentPart>) -> Self {
27 Self {
28 role: "user".to_string(),
29 content,
30 name: None,
31 }
32 }
33
34 pub fn with_name(mut self, name: impl Into<String>) -> Self {
35 self.name = Some(name.into());
36 self
37 }
38}
39
40#[derive(Debug, Clone, Serialize, Deserialize)]
42pub struct SystemMessage {
43 pub role: String,
44 pub content: String,
45}
46
47impl SystemMessage {
48 pub fn new(content: impl Into<String>) -> Self {
49 Self {
50 role: "system".to_string(),
51 content: content.into(),
52 }
53 }
54}
55
56#[derive(Debug, Clone, Serialize, Deserialize)]
58pub struct DeveloperMessage {
59 pub role: String,
60 pub content: String,
61}
62
63impl DeveloperMessage {
64 pub fn new(content: impl Into<String>) -> Self {
65 Self {
66 role: "developer".to_string(),
67 content: content.into(),
68 }
69 }
70}
71
72#[derive(Debug, Clone, Serialize, Deserialize)]
74pub struct AssistantMessage {
75 pub role: String,
76 #[serde(skip_serializing_if = "Option::is_none")]
77 pub content: Option<String>,
78 #[serde(skip_serializing_if = "Option::is_none")]
79 pub thinking: Option<String>,
80 #[serde(skip_serializing_if = "Option::is_none")]
81 pub redacted_thinking: Option<String>,
82 #[serde(default)]
83 pub tool_calls: Vec<ToolCall>,
84 #[serde(skip_serializing_if = "Option::is_none")]
85 pub refusal: Option<String>,
86}
87
88impl AssistantMessage {
89 pub fn new(content: impl Into<String>) -> Self {
90 Self {
91 role: "assistant".to_string(),
92 content: Some(content.into()),
93 thinking: None,
94 redacted_thinking: None,
95 tool_calls: Vec::new(),
96 refusal: None,
97 }
98 }
99
100 pub fn with_tool_calls(mut self, tool_calls: Vec<ToolCall>) -> Self {
101 self.tool_calls = tool_calls;
102 self
103 }
104
105 pub fn with_thinking(mut self, thinking: impl Into<String>) -> Self {
106 self.thinking = Some(thinking.into());
107 self
108 }
109
110 pub fn is_empty(&self) -> bool {
111 self.content.is_none() && self.thinking.is_none() && self.tool_calls.is_empty()
112 }
113}
114
115#[derive(Debug, Clone, Serialize, Deserialize)]
117pub struct ToolMessage {
118 pub role: String,
119 pub content: String,
120 pub tool_call_id: String,
121 #[serde(skip_serializing_if = "Option::is_none")]
123 pub tool_name: Option<String>,
124 #[serde(default)]
126 pub ephemeral: bool,
127 #[serde(default)]
129 pub destroyed: bool,
130}
131
132impl ToolMessage {
133 pub fn new(tool_call_id: impl Into<String>, content: impl Into<String>) -> Self {
134 Self {
135 role: "tool".to_string(),
136 content: content.into(),
137 tool_call_id: tool_call_id.into(),
138 tool_name: None,
139 ephemeral: false,
140 destroyed: false,
141 }
142 }
143
144 pub fn with_tool_name(mut self, name: impl Into<String>) -> Self {
145 self.tool_name = Some(name.into());
146 self
147 }
148
149 pub fn with_ephemeral(mut self, ephemeral: bool) -> Self {
150 self.ephemeral = ephemeral;
151 self
152 }
153
154 pub fn destroy(&mut self) {
155 self.destroyed = true;
156 self.content = "<removed to save context>".to_string();
157 }
158}
159
160#[derive(Debug, Clone, Serialize, Deserialize)]
162#[serde(tag = "role")]
163#[serde(rename_all = "lowercase")]
164pub enum Message {
165 User(UserMessage),
166 Assistant(AssistantMessage),
167 System(SystemMessage),
168 Developer(DeveloperMessage),
169 Tool(ToolMessage),
170}
171
172impl Message {
173 pub fn user(content: impl Into<String>) -> Self {
174 Message::User(UserMessage::new(content))
175 }
176
177 pub fn assistant(content: impl Into<String>) -> Self {
178 Message::Assistant(AssistantMessage::new(content))
179 }
180
181 pub fn system(content: impl Into<String>) -> Self {
182 Message::System(SystemMessage::new(content))
183 }
184
185 pub fn tool(tool_call_id: impl Into<String>, content: impl Into<String>) -> Self {
186 Message::Tool(ToolMessage::new(tool_call_id, content))
187 }
188
189 pub fn role(&self) -> &str {
190 match self {
191 Message::User(_) => "user",
192 Message::Assistant(_) => "assistant",
193 Message::System(_) => "system",
194 Message::Developer(_) => "developer",
195 Message::Tool(_) => "tool",
196 }
197 }
198}
199
200impl From<UserMessage> for Message {
201 fn from(msg: UserMessage) -> Self {
202 Message::User(msg)
203 }
204}
205
206impl From<AssistantMessage> for Message {
207 fn from(msg: AssistantMessage) -> Self {
208 Message::Assistant(msg)
209 }
210}
211
212impl From<SystemMessage> for Message {
213 fn from(msg: SystemMessage) -> Self {
214 Message::System(msg)
215 }
216}
217
218impl From<ToolMessage> for Message {
219 fn from(msg: ToolMessage) -> Self {
220 Message::Tool(msg)
221 }
222}