Skip to main content

objectiveai_sdk/agent/completions/message/
tool_message.rs

1//! Tool message types.
2
3use super::rich_content::{RichContent, RichContentExpression};
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 tool message containing the result of a tool call.
14#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema, arbitrary::Arbitrary)]
15#[schemars(rename = "agent.completions.message.ToolMessage")]
16pub struct ToolMessage {
17    /// The content of the tool response.
18    pub content: RichContent,
19    /// The ID of the tool call this message responds to.
20    pub tool_call_id: String,
21}
22
23impl ToolMessage {
24    /// Prepares the message by normalizing its content.
25    pub fn prepare(&mut self) {
26        self.content.prepare();
27    }
28}
29
30impl FromStarlarkValue for ToolMessage {
31    fn from_starlark_value(
32        value: &StarlarkValue,
33    ) -> Result<Self, ExpressionError> {
34        let dict = StarlarkDictRef::from_value(*value).ok_or_else(|| {
35            ExpressionError::StarlarkConversionError(
36                "ToolMessage: expected dict".into(),
37            )
38        })?;
39        let mut content = None;
40        let mut tool_call_id = None;
41        for (k, v) in dict.iter() {
42            let key = <&str as UnpackValue>::unpack_value(k)
43                .map_err(|e| {
44                    ExpressionError::StarlarkConversionError(e.to_string())
45                })?
46                .ok_or_else(|| {
47                    ExpressionError::StarlarkConversionError(
48                        "ToolMessage: expected string key".into(),
49                    )
50                })?;
51            match key {
52                "content" => {
53                    content = Some(RichContent::from_starlark_value(&v)?)
54                }
55                "tool_call_id" => {
56                    tool_call_id = Some(String::from_starlark_value(&v)?)
57                }
58                _ => {}
59            }
60            if content.is_some() && tool_call_id.is_some() {
61                break;
62            }
63        }
64        Ok(ToolMessage {
65            content: content.ok_or_else(|| {
66                ExpressionError::StarlarkConversionError(
67                    "ToolMessage: missing content".into(),
68                )
69            })?,
70            tool_call_id: tool_call_id.ok_or_else(|| {
71                ExpressionError::StarlarkConversionError(
72                    "ToolMessage: missing tool_call_id".into(),
73                )
74            })?,
75        })
76    }
77}
78
79/// Expression variant of [`ToolMessage`] for dynamic content.
80#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema, arbitrary::Arbitrary)]
81#[schemars(rename = "agent.completions.message.ToolMessageExpression")]
82pub struct ToolMessageExpression {
83    /// The content expression.
84    pub content: functions::expression::WithExpression<RichContentExpression>,
85    /// The tool call ID expression.
86    pub tool_call_id: functions::expression::WithExpression<String>,
87}
88
89impl ToolMessageExpression {
90    /// Compiles the expression into a concrete [`ToolMessage`].
91    pub fn compile(
92        self,
93        params: &functions::expression::Params,
94    ) -> Result<ToolMessage, functions::expression::ExpressionError> {
95        let content = self.content.compile_one(params)?.compile(params)?;
96        let tool_call_id = self.tool_call_id.compile_one(params)?;
97        Ok(ToolMessage {
98            content,
99            tool_call_id,
100        })
101    }
102}
103
104impl FromStarlarkValue for ToolMessageExpression {
105    fn from_starlark_value(
106        value: &StarlarkValue,
107    ) -> Result<Self, ExpressionError> {
108        let dict = StarlarkDictRef::from_value(*value).ok_or_else(|| {
109            ExpressionError::StarlarkConversionError(
110                "ToolMessageExpression: expected dict".into(),
111            )
112        })?;
113        let mut content = None;
114        let mut tool_call_id = None;
115        for (k, v) in dict.iter() {
116            let key = <&str as UnpackValue>::unpack_value(k)
117                .map_err(|e| {
118                    ExpressionError::StarlarkConversionError(e.to_string())
119                })?
120                .ok_or_else(|| {
121                    ExpressionError::StarlarkConversionError(
122                        "ToolMessageExpression: expected string key".into(),
123                    )
124                })?;
125            match key {
126                "content" => {
127                    content = Some(WithExpression::Value(
128                        RichContentExpression::from_starlark_value(&v)?,
129                    ))
130                }
131                "tool_call_id" => {
132                    tool_call_id = Some(WithExpression::Value(
133                        String::from_starlark_value(&v)?,
134                    ))
135                }
136                _ => {}
137            }
138            if content.is_some() && tool_call_id.is_some() {
139                break;
140            }
141        }
142        Ok(ToolMessageExpression {
143            content: content.ok_or_else(|| {
144                ExpressionError::StarlarkConversionError(
145                    "ToolMessageExpression: missing content".into(),
146                )
147            })?,
148            tool_call_id: tool_call_id.ok_or_else(|| {
149                ExpressionError::StarlarkConversionError(
150                    "ToolMessageExpression: missing tool_call_id".into(),
151                )
152            })?,
153        })
154    }
155}