Skip to main content

objectiveai_sdk/agent/completions/message/
system_message.rs

1//! System message types.
2
3use super::simple_content::{SimpleContent, SimpleContentExpression};
4use crate::functions;
5use functions::expression::{
6    ExpressionError, FromStarlarkValue, WithExpression,
7};
8use serde::{Deserialize, Serialize};
9use starlark::values::dict::DictRef as StarlarkDictRef;
10use starlark::values::{UnpackValue, Value as StarlarkValue};
11use schemars::JsonSchema;
12
13/// A system message setting context or instructions.
14#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema, arbitrary::Arbitrary)]
15#[schemars(rename = "agent.completions.message.SystemMessage")]
16pub struct SystemMessage {
17    /// The message content.
18    pub content: SimpleContent,
19    /// Optional name for the message author.
20    #[serde(skip_serializing_if = "Option::is_none")]
21    #[schemars(extend("omitempty" = true))]
22    pub name: Option<String>,
23}
24
25impl SystemMessage {
26    pub fn push(&mut self, other: &SystemMessage) {
27        self.content.push(&other.content);
28        if self.name.is_none() {
29            self.name.clone_from(&other.name);
30        }
31    }
32
33    pub fn has_name(&self) -> bool {
34        self.name.as_ref().is_some_and(|n| !n.is_empty())
35    }
36
37    /// Prepares the message by normalizing content and optional fields.
38    pub fn prepare(&mut self) {
39        self.content.prepare();
40        if self.name.as_ref().is_some_and(String::is_empty) {
41            self.name = None;
42        }
43    }
44}
45
46impl FromStarlarkValue for SystemMessage {
47    fn from_starlark_value(
48        value: &StarlarkValue,
49    ) -> Result<Self, ExpressionError> {
50        let dict = StarlarkDictRef::from_value(*value).ok_or_else(|| {
51            ExpressionError::StarlarkConversionError(
52                "SystemMessage: expected dict".into(),
53            )
54        })?;
55        let mut content = None;
56        let mut name = None;
57        for (k, v) in dict.iter() {
58            let key = <&str as UnpackValue>::unpack_value(k)
59                .map_err(|e| {
60                    ExpressionError::StarlarkConversionError(e.to_string())
61                })?
62                .ok_or_else(|| {
63                    ExpressionError::StarlarkConversionError(
64                        "SystemMessage: expected string key".into(),
65                    )
66                })?;
67            match key {
68                "content" => {
69                    content = Some(SimpleContent::from_starlark_value(&v)?)
70                }
71                "name" => name = Option::<String>::from_starlark_value(&v)?,
72                _ => {}
73            }
74        }
75        Ok(SystemMessage {
76            content: content.ok_or_else(|| {
77                ExpressionError::StarlarkConversionError(
78                    "SystemMessage: missing content".into(),
79                )
80            })?,
81            name,
82        })
83    }
84}
85
86/// Expression variant of [`SystemMessage`] for dynamic content.
87#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema, arbitrary::Arbitrary)]
88#[schemars(rename = "agent.completions.message.SystemMessageExpression")]
89pub struct SystemMessageExpression {
90    /// The message content expression.
91    pub content: functions::expression::WithExpression<SimpleContentExpression>,
92    /// Optional name expression.
93    #[serde(default, skip_serializing_if = "functions::expression::WithExpression::is_none")]
94    #[schemars(with = "Option<functions::expression::WithExpression<String>>", extend("omitempty" = true))]
95    pub name: functions::expression::WithExpression<Option<String>>,
96}
97
98impl SystemMessageExpression {
99    /// Compiles the expression into a concrete [`SystemMessage`].
100    pub fn compile(
101        self,
102        params: &functions::expression::Params,
103    ) -> Result<SystemMessage, functions::expression::ExpressionError> {
104        let content = self.content.compile_one(params)?.compile(params)?;
105        let name = self.name.compile_one(params)?;
106        Ok(SystemMessage { content, name })
107    }
108}
109
110impl FromStarlarkValue for SystemMessageExpression {
111    fn from_starlark_value(
112        value: &StarlarkValue,
113    ) -> Result<Self, ExpressionError> {
114        let dict = StarlarkDictRef::from_value(*value).ok_or_else(|| {
115            ExpressionError::StarlarkConversionError(
116                "SystemMessageExpression: expected dict".into(),
117            )
118        })?;
119        let mut content = None;
120        let mut name = WithExpression::Value(None);
121        for (k, v) in dict.iter() {
122            let key = <&str as UnpackValue>::unpack_value(k)
123                .map_err(|e| {
124                    ExpressionError::StarlarkConversionError(e.to_string())
125                })?
126                .ok_or_else(|| {
127                    ExpressionError::StarlarkConversionError(
128                        "SystemMessageExpression: expected string key".into(),
129                    )
130                })?;
131            match key {
132                "content" => {
133                    content = Some(WithExpression::Value(
134                        SimpleContentExpression::from_starlark_value(&v)?,
135                    ))
136                }
137                "name" => {
138                    name = WithExpression::Value(if v.is_none() {
139                        None
140                    } else {
141                        Some(String::from_starlark_value(&v)?)
142                    });
143                }
144                _ => {}
145            }
146        }
147        Ok(SystemMessageExpression {
148            content: content.ok_or_else(|| {
149                ExpressionError::StarlarkConversionError(
150                    "SystemMessageExpression: missing content".into(),
151                )
152            })?,
153            name,
154        })
155    }
156}