Skip to main content

objectiveai_sdk/agent/completions/message/
simple_content.rs

1//! Simple text content types for system/developer messages.
2
3use crate::functions;
4use functions::expression::{
5    ExpressionError, FromStarlarkValue, WithExpression,
6};
7use schemars::JsonSchema;
8use serde::{Deserialize, Serialize};
9use starlark::values::dict::DictRef as StarlarkDictRef;
10use starlark::values::{UnpackValue, Value as StarlarkValue};
11
12/// Simple text content for system/developer messages.
13#[derive(
14    Debug,
15    Clone,
16    PartialEq,
17    Serialize,
18    Deserialize,
19    JsonSchema,
20    arbitrary::Arbitrary,
21)]
22#[serde(untagged)]
23#[schemars(rename = "agent.completions.message.SimpleContent")]
24pub enum SimpleContent {
25    /// Plain text content.
26    #[schemars(title = "Text")]
27    Text(String),
28    /// Multi-part text content.
29    #[schemars(title = "Parts")]
30    Parts(Vec<SimpleContentPart>),
31}
32
33impl SimpleContent {
34    pub fn push(&mut self, other: &SimpleContent) {
35        match (&mut *self, other) {
36            (
37                SimpleContent::Text(self_text),
38                SimpleContent::Text(other_text),
39            ) => {
40                self_text.push_str(&other_text);
41            }
42            (
43                SimpleContent::Text(self_text),
44                SimpleContent::Parts(other_parts),
45            ) => {
46                let mut parts = Vec::with_capacity(1 + other_parts.len());
47                parts.push(SimpleContentPart::Text {
48                    text: std::mem::take(self_text),
49                });
50                parts.extend(other_parts.iter().cloned());
51                *self = SimpleContent::Parts(parts);
52            }
53            (
54                SimpleContent::Parts(self_parts),
55                SimpleContent::Text(other_text),
56            ) => {
57                self_parts.push(SimpleContentPart::Text {
58                    text: other_text.clone(),
59                });
60            }
61            (
62                SimpleContent::Parts(self_parts),
63                SimpleContent::Parts(other_parts),
64            ) => {
65                self_parts.extend(other_parts.iter().cloned());
66            }
67        }
68    }
69
70    /// Prepares the content by consolidating parts into a single text string.
71    pub fn prepare(&mut self) {
72        match self {
73            SimpleContent::Text(_) => {}
74            SimpleContent::Parts(parts) => {
75                let text_len = parts
76                    .iter()
77                    .map(|part| match part {
78                        SimpleContentPart::Text { text } => text.len(),
79                    })
80                    .sum();
81                let mut text = String::with_capacity(text_len);
82                for part in parts {
83                    match part {
84                        SimpleContentPart::Text { text: part_text } => {
85                            text.push_str(&part_text);
86                        }
87                    }
88                }
89                *self = SimpleContent::Text(text)
90            }
91        }
92    }
93}
94
95impl FromStarlarkValue for SimpleContent {
96    fn from_starlark_value(
97        value: &StarlarkValue,
98    ) -> Result<Self, ExpressionError> {
99        if let Ok(Some(s)) = <&str as UnpackValue>::unpack_value(*value) {
100            return Ok(SimpleContent::Text(s.to_owned()));
101        }
102        let parts = Vec::<SimpleContentPart>::from_starlark_value(value)?;
103        Ok(SimpleContent::Parts(parts))
104    }
105}
106
107/// Expression variant of [`SimpleContent`] for dynamic content.
108#[derive(
109    Debug,
110    Clone,
111    PartialEq,
112    Serialize,
113    Deserialize,
114    JsonSchema,
115    arbitrary::Arbitrary,
116)]
117#[serde(untagged)]
118#[schemars(rename = "agent.completions.message.SimpleContentExpression")]
119pub enum SimpleContentExpression {
120    /// Plain text content.
121    #[schemars(title = "Text")]
122    Text(String),
123    /// Multi-part text content expressions.
124    #[schemars(title = "Parts")]
125    Parts(
126        Vec<functions::expression::WithExpression<SimpleContentPartExpression>>,
127    ),
128}
129
130impl SimpleContentExpression {
131    /// Compiles the expression into a concrete [`SimpleContent`].
132    pub fn compile(
133        self,
134        params: &functions::expression::Params,
135    ) -> Result<SimpleContent, functions::expression::ExpressionError> {
136        match self {
137            SimpleContentExpression::Text(text) => {
138                Ok(SimpleContent::Text(text))
139            }
140            SimpleContentExpression::Parts(parts) => {
141                let mut compiled_parts = Vec::with_capacity(parts.len());
142                for part in parts {
143                    match part.compile_one_or_many(params)? {
144                        functions::expression::OneOrMany::One(one_part) => {
145                            compiled_parts.push(one_part.compile(params)?);
146                        }
147                        functions::expression::OneOrMany::Many(many_parts) => {
148                            for part in many_parts {
149                                compiled_parts.push(part.compile(params)?);
150                            }
151                        }
152                    }
153                }
154                Ok(SimpleContent::Parts(compiled_parts))
155            }
156        }
157    }
158}
159
160impl FromStarlarkValue for SimpleContentExpression {
161    fn from_starlark_value(
162        value: &StarlarkValue,
163    ) -> Result<Self, ExpressionError> {
164        if let Ok(Some(s)) = <&str as UnpackValue>::unpack_value(*value) {
165            return Ok(SimpleContentExpression::Text(s.to_owned()));
166        }
167        let parts = Vec::<WithExpression<SimpleContentPartExpression>>::from_starlark_value(value)?;
168        Ok(SimpleContentExpression::Parts(parts))
169    }
170}
171
172/// A part of simple text content.
173#[derive(
174    Debug,
175    Clone,
176    PartialEq,
177    Serialize,
178    Deserialize,
179    JsonSchema,
180    arbitrary::Arbitrary,
181)]
182#[serde(tag = "type", rename_all = "snake_case")]
183#[schemars(rename = "agent.completions.message.SimpleContentPart")]
184pub enum SimpleContentPart {
185    /// A text part.
186    Text {
187        /// The text content.
188        text: String,
189    },
190}
191
192impl FromStarlarkValue for SimpleContentPart {
193    fn from_starlark_value(
194        value: &StarlarkValue,
195    ) -> Result<Self, ExpressionError> {
196        let dict = StarlarkDictRef::from_value(*value).ok_or_else(|| {
197            ExpressionError::StarlarkConversionError(
198                "SimpleContentPart: expected dict".into(),
199            )
200        })?;
201        for (k, v) in dict.iter() {
202            if let Ok(Some("text")) = <&str as UnpackValue>::unpack_value(k) {
203                return Ok(SimpleContentPart::Text {
204                    text: String::from_starlark_value(&v)?,
205                });
206            }
207        }
208        Err(ExpressionError::StarlarkConversionError(
209            "SimpleContentPart: missing text".into(),
210        ))
211    }
212}
213
214/// Expression variant of [`SimpleContentPart`] for dynamic content.
215#[derive(
216    Debug,
217    Clone,
218    PartialEq,
219    Serialize,
220    Deserialize,
221    JsonSchema,
222    arbitrary::Arbitrary,
223)]
224#[serde(tag = "type", rename_all = "snake_case")]
225#[schemars(rename = "agent.completions.message.SimpleContentPartExpression")]
226pub enum SimpleContentPartExpression {
227    /// A text part expression.
228    Text {
229        /// The text expression.
230        text: functions::expression::WithExpression<String>,
231    },
232}
233
234impl SimpleContentPartExpression {
235    /// Compiles the expression into a concrete [`SimpleContentPart`].
236    pub fn compile(
237        self,
238        params: &functions::expression::Params,
239    ) -> Result<SimpleContentPart, functions::expression::ExpressionError> {
240        match self {
241            SimpleContentPartExpression::Text { text } => {
242                let text = text.compile_one(params)?;
243                Ok(SimpleContentPart::Text { text })
244            }
245        }
246    }
247}
248
249impl FromStarlarkValue for SimpleContentPartExpression {
250    fn from_starlark_value(
251        value: &StarlarkValue,
252    ) -> Result<Self, ExpressionError> {
253        let part = SimpleContentPart::from_starlark_value(value)?;
254        match part {
255            SimpleContentPart::Text { text } => {
256                Ok(SimpleContentPartExpression::Text {
257                    text: WithExpression::Value(text),
258                })
259            }
260        }
261    }
262}
263
264crate::functions::expression::impl_from_special_unsupported!(
265    SimpleContentExpression,
266    SimpleContentPartExpression,
267);