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