objectiveai_sdk/agent/completions/message/
mod.rs1mod assistant_message;
7mod file_content;
8mod pipe_ack;
9mod rich_content;
10mod tool_message;
11mod user_message;
12
13pub use assistant_message::*;
14pub use file_content::*;
15pub use pipe_ack::*;
16pub use rich_content::*;
17pub use tool_message::*;
18pub use user_message::*;
19
20#[cfg(test)]
21mod assistant_message_tests;
22#[cfg(all(test, feature = "mcp"))]
23mod rich_content_tests;
24
25use crate::functions;
26use functions::expression::{ExpressionError, FromStarlarkValue};
27use schemars::JsonSchema;
28use serde::{Deserialize, Serialize};
29use starlark::values::dict::DictRef as StarlarkDictRef;
30use starlark::values::{UnpackValue, Value as StarlarkValue};
31
32pub mod prompt {
34 use super::Message;
35 use schemars::JsonSchema;
36
37 fn is_chain(a: &Message, b: &Message) -> bool {
40 matches!((a, b), (Message::User(_), Message::User(_)))
41 }
42
43 fn push(target: &mut Message, other: &Message) {
45 match (target, other) {
46 (Message::User(t), Message::User(o)) => t.push(o),
47 _ => unreachable!(),
48 }
49 }
50
51 pub fn prepare(messages: &mut Vec<Message>) {
54 messages.iter_mut().for_each(Message::prepare);
55
56 let has_chain = messages.windows(2).any(|w| is_chain(&w[0], &w[1]));
58 if !has_chain {
59 return;
60 }
61
62 let mut merged = Vec::with_capacity(messages.len());
63 for msg in messages.drain(..) {
64 if let Some(last) = merged.last_mut() {
65 if is_chain(last, &msg) {
66 push(last, &msg);
67 continue;
68 }
69 }
70 merged.push(msg);
71 }
72 *messages = merged;
73
74 prepare(messages);
76 }
77
78 pub fn id(messages: &[Message]) -> String {
80 let mut hasher = twox_hash::XxHash3_128::with_seed(0);
81 hasher.write(serde_json::to_string(messages).unwrap().as_bytes());
82 format!("{:0>22}", base62::encode(hasher.finish_128()))
83 }
84}
85
86#[derive(
88 Debug,
89 Clone,
90 PartialEq,
91 Serialize,
92 Deserialize,
93 JsonSchema,
94 arbitrary::Arbitrary,
95)]
96#[serde(tag = "role")]
97#[schemars(rename = "agent.completions.message.Message")]
98pub enum Message {
99 #[schemars(title = "User")]
101 #[serde(rename = "user")]
102 User(UserMessage),
103 #[schemars(title = "Assistant")]
105 #[serde(rename = "assistant")]
106 Assistant(AssistantMessage),
107 #[schemars(title = "Tool")]
109 #[serde(rename = "tool")]
110 Tool(ToolMessage),
111}
112
113impl Message {
114 pub fn prepare(&mut self) {
119 match self {
120 Message::User(msg) => msg.prepare(),
121 Message::Assistant(msg) => msg.prepare(),
122 Message::Tool(msg) => msg.prepare(),
123 }
124 }
125
126}
127
128impl FromStarlarkValue for Message {
129 fn from_starlark_value(
130 value: &StarlarkValue,
131 ) -> Result<Self, ExpressionError> {
132 let dict = StarlarkDictRef::from_value(*value).ok_or_else(|| {
133 ExpressionError::StarlarkConversionError(
134 "Message: expected dict".into(),
135 )
136 })?;
137 let mut role = None;
139 for (k, v) in dict.iter() {
140 if let Ok(Some("role")) = <&str as UnpackValue>::unpack_value(k) {
141 role = Some(
142 <&str as UnpackValue>::unpack_value(v)
143 .map_err(|e| {
144 ExpressionError::StarlarkConversionError(
145 e.to_string(),
146 )
147 })?
148 .ok_or_else(|| {
149 ExpressionError::StarlarkConversionError(
150 "Message: expected string role".into(),
151 )
152 })?,
153 );
154 break;
155 }
156 }
157 let role = role.ok_or_else(|| {
158 ExpressionError::StarlarkConversionError(
159 "Message: missing role".into(),
160 )
161 })?;
162 match role {
163 "user" => {
164 UserMessage::from_starlark_value(value).map(Message::User)
165 }
166 "assistant" => AssistantMessage::from_starlark_value(value)
167 .map(Message::Assistant),
168 "tool" => {
169 ToolMessage::from_starlark_value(value).map(Message::Tool)
170 }
171 _ => Err(ExpressionError::StarlarkConversionError(format!(
172 "Message: unknown role: {}",
173 role
174 ))),
175 }
176 }
177}
178
179#[derive(
185 Debug,
186 Clone,
187 PartialEq,
188 Serialize,
189 Deserialize,
190 JsonSchema,
191 arbitrary::Arbitrary,
192)]
193#[serde(tag = "role")]
194#[schemars(rename = "agent.completions.message.MessageExpression")]
195pub enum MessageExpression {
196 #[schemars(title = "User")]
197 #[serde(rename = "user")]
198 User(UserMessageExpression),
199 #[schemars(title = "Assistant")]
200 #[serde(rename = "assistant")]
201 Assistant(AssistantMessageExpression),
202 #[schemars(title = "Tool")]
203 #[serde(rename = "tool")]
204 Tool(ToolMessageExpression),
205}
206
207impl MessageExpression {
208 pub fn compile(
217 self,
218 params: &functions::expression::Params,
219 ) -> Result<Message, functions::expression::ExpressionError> {
220 match self {
221 MessageExpression::User(msg) => {
222 msg.compile(params).map(Message::User)
223 }
224 MessageExpression::Assistant(msg) => {
225 msg.compile(params).map(Message::Assistant)
226 }
227 MessageExpression::Tool(msg) => {
228 msg.compile(params).map(Message::Tool)
229 }
230 }
231 }
232}
233
234impl FromStarlarkValue for MessageExpression {
235 fn from_starlark_value(
236 value: &StarlarkValue,
237 ) -> Result<Self, ExpressionError> {
238 let dict = StarlarkDictRef::from_value(*value).ok_or_else(|| {
239 ExpressionError::StarlarkConversionError(
240 "MessageExpression: expected dict".into(),
241 )
242 })?;
243 let mut role = None;
245 for (k, v) in dict.iter() {
246 if let Ok(Some("role")) = <&str as UnpackValue>::unpack_value(k) {
247 role = Some(
248 <&str as UnpackValue>::unpack_value(v)
249 .map_err(|e| {
250 ExpressionError::StarlarkConversionError(
251 e.to_string(),
252 )
253 })?
254 .ok_or_else(|| {
255 ExpressionError::StarlarkConversionError(
256 "MessageExpression: expected string role"
257 .into(),
258 )
259 })?,
260 );
261 break;
262 }
263 }
264 let role = role.ok_or_else(|| {
265 ExpressionError::StarlarkConversionError(
266 "MessageExpression: missing role".into(),
267 )
268 })?;
269 match role {
270 "user" => UserMessageExpression::from_starlark_value(value)
271 .map(MessageExpression::User),
272 "assistant" => {
273 AssistantMessageExpression::from_starlark_value(value)
274 .map(MessageExpression::Assistant)
275 }
276 "tool" => ToolMessageExpression::from_starlark_value(value)
277 .map(MessageExpression::Tool),
278 _ => Err(ExpressionError::StarlarkConversionError(format!(
279 "MessageExpression: unknown role: {}",
280 role
281 ))),
282 }
283 }
284}
285
286crate::functions::expression::impl_from_special_unsupported!(MessageExpression,);
287
288impl crate::functions::expression::FromSpecial
289 for Vec<crate::functions::expression::WithExpression<MessageExpression>>
290{
291 fn from_special(
292 _special: &crate::functions::expression::Special,
293 _params: &crate::functions::expression::Params,
294 ) -> Result<Self, crate::functions::expression::ExpressionError> {
295 Err(crate::functions::expression::ExpressionError::UnsupportedSpecial)
296 }
297}