objectiveai_sdk/agent/completions/message/
mod.rs1mod assistant_message;
7mod developer_message;
8mod file_content;
9mod pipe_ack;
10mod rich_content;
11mod simple_content;
12mod system_message;
13mod tool_message;
14mod user_message;
15
16pub use assistant_message::*;
17pub use developer_message::*;
18pub use file_content::*;
19pub use pipe_ack::*;
20pub use rich_content::*;
21pub use simple_content::*;
22pub use system_message::*;
23pub use tool_message::*;
24pub use user_message::*;
25
26#[cfg(test)]
27mod assistant_message_tests;
28#[cfg(all(test, feature = "mcp"))]
29mod rich_content_tests;
30
31use crate::functions;
32use functions::expression::{ExpressionError, FromStarlarkValue};
33use schemars::JsonSchema;
34use serde::{Deserialize, Serialize};
35use starlark::values::dict::DictRef as StarlarkDictRef;
36use starlark::values::{UnpackValue, Value as StarlarkValue};
37
38pub mod prompt {
40 use super::Message;
41 use schemars::JsonSchema;
42
43 fn is_chain(a: &Message, b: &Message) -> bool {
47 match (a, b) {
48 (Message::Developer(a), Message::Developer(b)) => {
49 !a.has_name() || !b.has_name() || a.name == b.name
50 }
51 (Message::System(a), Message::System(b)) => {
52 !a.has_name() || !b.has_name() || a.name == b.name
53 }
54 (Message::User(a), Message::User(b)) => {
55 !a.has_name() || !b.has_name() || a.name == b.name
56 }
57 _ => false,
58 }
59 }
60
61 fn push(target: &mut Message, other: &Message) {
63 match (target, other) {
64 (Message::Developer(t), Message::Developer(o)) => t.push(o),
65 (Message::System(t), Message::System(o)) => t.push(o),
66 (Message::User(t), Message::User(o)) => t.push(o),
67 _ => unreachable!(),
68 }
69 }
70
71 pub fn prepare(messages: &mut Vec<Message>) {
75 messages.iter_mut().for_each(Message::prepare);
76
77 let has_chain = messages.windows(2).any(|w| is_chain(&w[0], &w[1]));
79 if !has_chain {
80 return;
81 }
82
83 let mut merged = Vec::with_capacity(messages.len());
84 for msg in messages.drain(..) {
85 if let Some(last) = merged.last_mut() {
86 if is_chain(last, &msg) {
87 push(last, &msg);
88 continue;
89 }
90 }
91 merged.push(msg);
92 }
93 *messages = merged;
94
95 prepare(messages);
97 }
98
99 pub fn id(messages: &[Message]) -> String {
101 let mut hasher = twox_hash::XxHash3_128::with_seed(0);
102 hasher.write(serde_json::to_string(messages).unwrap().as_bytes());
103 format!("{:0>22}", base62::encode(hasher.finish_128()))
104 }
105}
106
107#[derive(
109 Debug,
110 Clone,
111 PartialEq,
112 Serialize,
113 Deserialize,
114 JsonSchema,
115 arbitrary::Arbitrary,
116)]
117#[serde(tag = "role")]
118#[schemars(rename = "agent.completions.message.Message")]
119pub enum Message {
120 #[schemars(title = "Developer")]
122 #[serde(rename = "developer")]
123 Developer(DeveloperMessage),
124 #[schemars(title = "System")]
126 #[serde(rename = "system")]
127 System(SystemMessage),
128 #[schemars(title = "User")]
130 #[serde(rename = "user")]
131 User(UserMessage),
132 #[schemars(title = "Assistant")]
134 #[serde(rename = "assistant")]
135 Assistant(AssistantMessage),
136 #[schemars(title = "Tool")]
138 #[serde(rename = "tool")]
139 Tool(ToolMessage),
140}
141
142impl Message {
143 pub fn prepare(&mut self) {
148 match self {
149 Message::Developer(msg) => msg.prepare(),
150 Message::System(msg) => msg.prepare(),
151 Message::User(msg) => msg.prepare(),
152 Message::Assistant(msg) => msg.prepare(),
153 Message::Tool(msg) => msg.prepare(),
154 }
155 }
156
157}
158
159impl FromStarlarkValue for Message {
160 fn from_starlark_value(
161 value: &StarlarkValue,
162 ) -> Result<Self, ExpressionError> {
163 let dict = StarlarkDictRef::from_value(*value).ok_or_else(|| {
164 ExpressionError::StarlarkConversionError(
165 "Message: expected dict".into(),
166 )
167 })?;
168 let mut role = None;
170 for (k, v) in dict.iter() {
171 if let Ok(Some("role")) = <&str as UnpackValue>::unpack_value(k) {
172 role = Some(
173 <&str as UnpackValue>::unpack_value(v)
174 .map_err(|e| {
175 ExpressionError::StarlarkConversionError(
176 e.to_string(),
177 )
178 })?
179 .ok_or_else(|| {
180 ExpressionError::StarlarkConversionError(
181 "Message: expected string role".into(),
182 )
183 })?,
184 );
185 break;
186 }
187 }
188 let role = role.ok_or_else(|| {
189 ExpressionError::StarlarkConversionError(
190 "Message: missing role".into(),
191 )
192 })?;
193 match role {
194 "developer" => DeveloperMessage::from_starlark_value(value)
195 .map(Message::Developer),
196 "system" => {
197 SystemMessage::from_starlark_value(value).map(Message::System)
198 }
199 "user" => {
200 UserMessage::from_starlark_value(value).map(Message::User)
201 }
202 "assistant" => AssistantMessage::from_starlark_value(value)
203 .map(Message::Assistant),
204 "tool" => {
205 ToolMessage::from_starlark_value(value).map(Message::Tool)
206 }
207 _ => Err(ExpressionError::StarlarkConversionError(format!(
208 "Message: unknown role: {}",
209 role
210 ))),
211 }
212 }
213}
214
215#[derive(
221 Debug,
222 Clone,
223 PartialEq,
224 Serialize,
225 Deserialize,
226 JsonSchema,
227 arbitrary::Arbitrary,
228)]
229#[serde(tag = "role")]
230#[schemars(rename = "agent.completions.message.MessageExpression")]
231pub enum MessageExpression {
232 #[schemars(title = "Developer")]
233 #[serde(rename = "developer")]
234 Developer(DeveloperMessageExpression),
235 #[schemars(title = "System")]
236 #[serde(rename = "system")]
237 System(SystemMessageExpression),
238 #[schemars(title = "User")]
239 #[serde(rename = "user")]
240 User(UserMessageExpression),
241 #[schemars(title = "Assistant")]
242 #[serde(rename = "assistant")]
243 Assistant(AssistantMessageExpression),
244 #[schemars(title = "Tool")]
245 #[serde(rename = "tool")]
246 Tool(ToolMessageExpression),
247}
248
249impl MessageExpression {
250 pub fn compile(
259 self,
260 params: &functions::expression::Params,
261 ) -> Result<Message, functions::expression::ExpressionError> {
262 match self {
263 MessageExpression::Developer(msg) => {
264 msg.compile(params).map(Message::Developer)
265 }
266 MessageExpression::System(msg) => {
267 msg.compile(params).map(Message::System)
268 }
269 MessageExpression::User(msg) => {
270 msg.compile(params).map(Message::User)
271 }
272 MessageExpression::Assistant(msg) => {
273 msg.compile(params).map(Message::Assistant)
274 }
275 MessageExpression::Tool(msg) => {
276 msg.compile(params).map(Message::Tool)
277 }
278 }
279 }
280}
281
282impl FromStarlarkValue for MessageExpression {
283 fn from_starlark_value(
284 value: &StarlarkValue,
285 ) -> Result<Self, ExpressionError> {
286 let dict = StarlarkDictRef::from_value(*value).ok_or_else(|| {
287 ExpressionError::StarlarkConversionError(
288 "MessageExpression: expected dict".into(),
289 )
290 })?;
291 let mut role = None;
293 for (k, v) in dict.iter() {
294 if let Ok(Some("role")) = <&str as UnpackValue>::unpack_value(k) {
295 role = Some(
296 <&str as UnpackValue>::unpack_value(v)
297 .map_err(|e| {
298 ExpressionError::StarlarkConversionError(
299 e.to_string(),
300 )
301 })?
302 .ok_or_else(|| {
303 ExpressionError::StarlarkConversionError(
304 "MessageExpression: expected string role"
305 .into(),
306 )
307 })?,
308 );
309 break;
310 }
311 }
312 let role = role.ok_or_else(|| {
313 ExpressionError::StarlarkConversionError(
314 "MessageExpression: missing role".into(),
315 )
316 })?;
317 match role {
318 "developer" => {
319 DeveloperMessageExpression::from_starlark_value(value)
320 .map(MessageExpression::Developer)
321 }
322 "system" => SystemMessageExpression::from_starlark_value(value)
323 .map(MessageExpression::System),
324 "user" => UserMessageExpression::from_starlark_value(value)
325 .map(MessageExpression::User),
326 "assistant" => {
327 AssistantMessageExpression::from_starlark_value(value)
328 .map(MessageExpression::Assistant)
329 }
330 "tool" => ToolMessageExpression::from_starlark_value(value)
331 .map(MessageExpression::Tool),
332 _ => Err(ExpressionError::StarlarkConversionError(format!(
333 "MessageExpression: unknown role: {}",
334 role
335 ))),
336 }
337 }
338}
339
340crate::functions::expression::impl_from_special_unsupported!(MessageExpression,);
341
342impl crate::functions::expression::FromSpecial
343 for Vec<crate::functions::expression::WithExpression<MessageExpression>>
344{
345 fn from_special(
346 _special: &crate::functions::expression::Special,
347 _params: &crate::functions::expression::Params,
348 ) -> Result<Self, crate::functions::expression::ExpressionError> {
349 Err(crate::functions::expression::ExpressionError::UnsupportedSpecial)
350 }
351}