1use 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#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema, arbitrary::Arbitrary)]
15#[schemars(rename = "agent.completions.message.AssistantMessage")]
16pub struct AssistantMessage {
17 #[serde(skip_serializing_if = "Option::is_none")]
19 #[schemars(extend("omitempty" = true))]
20 pub content: Option<RichContent>,
21 #[serde(skip_serializing_if = "Option::is_none")]
23 #[schemars(extend("omitempty" = true))]
24 pub name: Option<String>,
25 #[serde(skip_serializing_if = "Option::is_none")]
27 #[schemars(extend("omitempty" = true))]
28 pub refusal: Option<String>,
29 #[serde(skip_serializing_if = "Option::is_none")]
31 #[schemars(extend("omitempty" = true))]
32 pub tool_calls: Option<Vec<AssistantToolCall>>,
33 #[serde(skip_serializing_if = "Option::is_none")]
35 #[schemars(extend("omitempty" = true))]
36 pub reasoning: Option<String>,
37}
38
39impl AssistantMessage {
40 pub fn prepare(&mut self) {
42 if let Some(content) = &mut self.content {
43 content.prepare();
44 if content.is_empty() {
45 self.content = None;
46 }
47 }
48 if self.name.as_ref().is_some_and(String::is_empty) {
49 self.name = None;
50 }
51 if self.refusal.as_ref().is_some_and(String::is_empty) {
52 self.refusal = None;
53 }
54 if let Some(tool_calls) = &mut self.tool_calls {
55 tool_calls.retain(|tool_call| !tool_call.is_empty());
56 if tool_calls.is_empty() {
57 self.tool_calls = None;
58 }
59 }
60 if self.reasoning.as_ref().is_some_and(String::is_empty) {
61 self.reasoning = None;
62 }
63 }
64}
65
66impl FromStarlarkValue for AssistantMessage {
67 fn from_starlark_value(
68 value: &StarlarkValue,
69 ) -> Result<Self, ExpressionError> {
70 let dict = StarlarkDictRef::from_value(*value).ok_or_else(|| {
71 ExpressionError::StarlarkConversionError(
72 "AssistantMessage: expected dict".into(),
73 )
74 })?;
75 let mut content = None;
76 let mut name = None;
77 let mut refusal = None;
78 let mut tool_calls = None;
79 let mut reasoning = None;
80 for (k, v) in dict.iter() {
81 let key = <&str as UnpackValue>::unpack_value(k)
82 .map_err(|e| {
83 ExpressionError::StarlarkConversionError(e.to_string())
84 })?
85 .ok_or_else(|| {
86 ExpressionError::StarlarkConversionError(
87 "AssistantMessage: expected string key".into(),
88 )
89 })?;
90 match key {
91 "content" => {
92 content = Option::<RichContent>::from_starlark_value(&v)?
93 }
94 "name" => name = Option::<String>::from_starlark_value(&v)?,
95 "refusal" => {
96 refusal = Option::<String>::from_starlark_value(&v)?
97 }
98 "tool_calls" => {
99 tool_calls =
100 Option::<Vec<AssistantToolCall>>::from_starlark_value(
101 &v,
102 )?
103 }
104 "reasoning" => {
105 reasoning = Option::<String>::from_starlark_value(&v)?
106 }
107 _ => {}
108 }
109 }
110 Ok(AssistantMessage {
111 content,
112 name,
113 refusal,
114 tool_calls,
115 reasoning,
116 })
117 }
118}
119
120#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema, arbitrary::Arbitrary)]
122#[schemars(rename = "agent.completions.message.AssistantMessageExpression")]
123pub struct AssistantMessageExpression {
124 #[serde(default, skip_serializing_if = "functions::expression::WithExpression::is_none")]
126 #[schemars(with = "Option<functions::expression::WithExpression<RichContentExpression>>", extend("omitempty" = true))]
127 pub content:
128 functions::expression::WithExpression<Option<RichContentExpression>>,
129 #[serde(default, skip_serializing_if = "functions::expression::WithExpression::is_none")]
130 #[schemars(with = "Option<functions::expression::WithExpression<String>>", extend("omitempty" = true))]
131 pub name: functions::expression::WithExpression<Option<String>>,
132 #[serde(default, skip_serializing_if = "functions::expression::WithExpression::is_none")]
133 #[schemars(with = "Option<functions::expression::WithExpression<String>>", extend("omitempty" = true))]
134 pub refusal: functions::expression::WithExpression<Option<String>>,
135 #[serde(default, skip_serializing_if = "functions::expression::WithExpression::is_none")]
136 #[schemars(with = "Option<functions::expression::WithExpression<Vec<functions::expression::WithExpression<AssistantToolCallExpression>>>>", extend("omitempty" = true))]
137 pub tool_calls:
138 functions::expression::WithExpression<
139 Option<
140 Vec<
141 functions::expression::WithExpression<
142 AssistantToolCallExpression,
143 >,
144 >,
145 >,
146 >,
147 #[serde(default, skip_serializing_if = "functions::expression::WithExpression::is_none")]
148 #[schemars(with = "Option<functions::expression::WithExpression<String>>", extend("omitempty" = true))]
149 pub reasoning:
150 functions::expression::WithExpression<Option<String>>,
151}
152
153impl AssistantMessageExpression {
154 pub fn compile(
156 self,
157 params: &functions::expression::Params,
158 ) -> Result<AssistantMessage, functions::expression::ExpressionError> {
159 let content = self
160 .content
161 .compile_one(params)?
162 .map(|content| content.compile(params))
163 .transpose()?;
164 let name = self.name.compile_one(params)?;
165 let refusal = self.refusal.compile_one(params)?;
166 let tool_calls = self
167 .tool_calls
168 .compile_one(params)?
169 .map(|tool_calls| {
170 let mut compiled_tool_calls =
171 Vec::with_capacity(tool_calls.len());
172 for tool_call in tool_calls {
173 match tool_call.compile_one_or_many(params)? {
174 functions::expression::OneOrMany::One(
175 one_tool_call,
176 ) => {
177 compiled_tool_calls
178 .push(one_tool_call.compile(params)?);
179 }
180 functions::expression::OneOrMany::Many(
181 many_tool_calls,
182 ) => {
183 for tool_call in many_tool_calls {
184 compiled_tool_calls
185 .push(tool_call.compile(params)?);
186 }
187 }
188 }
189 }
190 Ok::<_, functions::expression::ExpressionError>(
191 compiled_tool_calls,
192 )
193 })
194 .transpose()?;
195 let reasoning = self.reasoning.compile_one(params)?;
196 Ok(AssistantMessage {
197 content,
198 name,
199 refusal,
200 tool_calls,
201 reasoning,
202 })
203 }
204}
205
206impl FromStarlarkValue for AssistantMessageExpression {
207 fn from_starlark_value(
208 value: &StarlarkValue,
209 ) -> Result<Self, ExpressionError> {
210 let dict = StarlarkDictRef::from_value(*value).ok_or_else(|| {
211 ExpressionError::StarlarkConversionError(
212 "AssistantMessageExpression: expected dict".into(),
213 )
214 })?;
215 let mut content = WithExpression::Value(None);
216 let mut name = WithExpression::Value(None);
217 let mut refusal = WithExpression::Value(None);
218 let mut tool_calls = WithExpression::Value(None);
219 let mut reasoning = WithExpression::Value(None);
220 for (k, v) in dict.iter() {
221 let key = <&str as UnpackValue>::unpack_value(k)
222 .map_err(|e| {
223 ExpressionError::StarlarkConversionError(e.to_string())
224 })?
225 .ok_or_else(|| {
226 ExpressionError::StarlarkConversionError(
227 "AssistantMessageExpression: expected string key"
228 .into(),
229 )
230 })?;
231 match key {
232 "content" => {
233 content = WithExpression::Value(if v.is_none() {
234 None
235 } else {
236 Some(RichContentExpression::from_starlark_value(&v)?)
237 });
238 }
239 "name" => {
240 name = WithExpression::Value(if v.is_none() {
241 None
242 } else {
243 Some(String::from_starlark_value(&v)?)
244 });
245 }
246 "refusal" => {
247 refusal = WithExpression::Value(if v.is_none() {
248 None
249 } else {
250 Some(String::from_starlark_value(&v)?)
251 });
252 }
253 "tool_calls" => {
254 tool_calls = WithExpression::Value(if v.is_none() {
255 None
256 } else {
257 Some(Vec::<WithExpression<AssistantToolCallExpression>>::from_starlark_value(&v)?)
258 });
259 }
260 "reasoning" => {
261 reasoning = WithExpression::Value(if v.is_none() {
262 None
263 } else {
264 Some(String::from_starlark_value(&v)?)
265 });
266 }
267 _ => {}
268 }
269 }
270 Ok(AssistantMessageExpression {
271 content,
272 name,
273 refusal,
274 tool_calls,
275 reasoning,
276 })
277 }
278}
279
280#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema, arbitrary::Arbitrary)]
282#[serde(tag = "type", rename_all = "snake_case")]
283#[schemars(rename = "agent.completions.message.AssistantToolCall")]
284pub enum AssistantToolCall {
285 Function {
287 id: String,
289 function: AssistantToolCallFunction,
291 },
292}
293
294impl AssistantToolCall {
295 pub fn is_empty(&self) -> bool {
297 match self {
298 AssistantToolCall::Function { id, function } => {
299 id.is_empty() && function.is_empty()
300 }
301 }
302 }
303}
304
305impl From<AssistantToolCallDelta> for AssistantToolCall {
306 fn from(tc: AssistantToolCallDelta) -> Self {
307 AssistantToolCall::Function {
308 id: tc.id.unwrap_or_default(),
309 function: tc.function.unwrap_or_default().into(),
310 }
311 }
312}
313
314impl FromStarlarkValue for AssistantToolCall {
315 fn from_starlark_value(
316 value: &StarlarkValue,
317 ) -> Result<Self, ExpressionError> {
318 let dict = StarlarkDictRef::from_value(*value).ok_or_else(|| {
319 ExpressionError::StarlarkConversionError(
320 "AssistantToolCall: expected dict".into(),
321 )
322 })?;
323 let mut id = None;
324 let mut function = None;
325 for (k, v) in dict.iter() {
326 let key = <&str as UnpackValue>::unpack_value(k)
327 .map_err(|e| {
328 ExpressionError::StarlarkConversionError(e.to_string())
329 })?
330 .ok_or_else(|| {
331 ExpressionError::StarlarkConversionError(
332 "AssistantToolCall: expected string key".into(),
333 )
334 })?;
335 match key {
336 "id" => id = Some(String::from_starlark_value(&v)?),
337 "function" => {
338 function = Some(
339 AssistantToolCallFunction::from_starlark_value(&v)?,
340 )
341 }
342 _ => {}
343 }
344 if id.is_some() && function.is_some() {
345 break;
346 }
347 }
348 Ok(AssistantToolCall::Function {
349 id: id.unwrap_or_default(),
350 function: function.ok_or_else(|| {
351 ExpressionError::StarlarkConversionError(
352 "AssistantToolCall: missing function".into(),
353 )
354 })?,
355 })
356 }
357}
358
359#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema, arbitrary::Arbitrary)]
361#[serde(tag = "type", rename_all = "snake_case")]
362#[schemars(rename = "agent.completions.message.AssistantToolCallExpression")]
363pub enum AssistantToolCallExpression {
364 Function {
366 id: functions::expression::WithExpression<String>,
368 function: functions::expression::WithExpression<
370 AssistantToolCallFunctionExpression,
371 >,
372 },
373}
374
375impl AssistantToolCallExpression {
376 pub fn compile(
378 self,
379 params: &functions::expression::Params,
380 ) -> Result<AssistantToolCall, functions::expression::ExpressionError> {
381 match self {
382 AssistantToolCallExpression::Function { id, function } => {
383 let id = id.compile_one(params)?;
384 let function = function.compile_one(params)?.compile(params)?;
385 Ok(AssistantToolCall::Function { id, function })
386 }
387 }
388 }
389}
390
391impl FromStarlarkValue for AssistantToolCallExpression {
392 fn from_starlark_value(
393 value: &StarlarkValue,
394 ) -> Result<Self, ExpressionError> {
395 let call = AssistantToolCall::from_starlark_value(value)?;
396 match call {
397 AssistantToolCall::Function { id, function } => {
398 Ok(AssistantToolCallExpression::Function {
399 id: WithExpression::Value(id),
400 function: WithExpression::Value(
401 AssistantToolCallFunctionExpression {
402 name: WithExpression::Value(function.name),
403 arguments: WithExpression::Value(
404 function.arguments,
405 ),
406 },
407 ),
408 })
409 }
410 }
411 }
412}
413
414#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema, arbitrary::Arbitrary)]
416#[schemars(rename = "agent.completions.message.AssistantToolCallFunction")]
417pub struct AssistantToolCallFunction {
418 pub name: String,
420 pub arguments: String,
422}
423
424impl AssistantToolCallFunction {
425 pub fn is_empty(&self) -> bool {
427 self.name.is_empty() && self.arguments.is_empty()
428 }
429}
430
431impl From<AssistantToolCallFunctionDelta> for AssistantToolCallFunction {
432 fn from(f: AssistantToolCallFunctionDelta) -> Self {
433 Self {
434 name: f.name.unwrap_or_default(),
435 arguments: f.arguments.unwrap_or_default(),
436 }
437 }
438}
439
440impl FromStarlarkValue for AssistantToolCallFunction {
441 fn from_starlark_value(
442 value: &StarlarkValue,
443 ) -> Result<Self, ExpressionError> {
444 let dict = StarlarkDictRef::from_value(*value).ok_or_else(|| {
445 ExpressionError::StarlarkConversionError(
446 "AssistantToolCallFunction: expected dict".into(),
447 )
448 })?;
449 let mut name = None;
450 let mut arguments = None;
451 for (k, v) in dict.iter() {
452 let key = <&str as UnpackValue>::unpack_value(k)
453 .map_err(|e| {
454 ExpressionError::StarlarkConversionError(e.to_string())
455 })?
456 .ok_or_else(|| {
457 ExpressionError::StarlarkConversionError(
458 "AssistantToolCallFunction: expected string key".into(),
459 )
460 })?;
461 match key {
462 "name" => name = Some(String::from_starlark_value(&v)?),
463 "arguments" => {
464 arguments = Some(String::from_starlark_value(&v)?)
465 }
466 _ => {}
467 }
468 if name.is_some() && arguments.is_some() {
469 break;
470 }
471 }
472 Ok(AssistantToolCallFunction {
473 name: name.ok_or_else(|| {
474 ExpressionError::StarlarkConversionError(
475 "AssistantToolCallFunction: missing name".into(),
476 )
477 })?,
478 arguments: arguments.ok_or_else(|| {
479 ExpressionError::StarlarkConversionError(
480 "AssistantToolCallFunction: missing arguments".into(),
481 )
482 })?,
483 })
484 }
485}
486
487#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema, arbitrary::Arbitrary)]
489#[schemars(rename = "agent.completions.message.AssistantToolCallDelta")]
490pub struct AssistantToolCallDelta {
491 #[arbitrary(with = crate::arbitrary_util::arbitrary_u64)]
493 pub index: u64,
494 #[serde(skip_serializing_if = "Option::is_none")]
496 #[schemars(extend("omitempty" = true))]
497 pub r#type: Option<AssistantToolCallType>,
498 #[serde(skip_serializing_if = "Option::is_none")]
500 #[schemars(extend("omitempty" = true))]
501 pub id: Option<String>,
502 #[serde(skip_serializing_if = "Option::is_none")]
504 #[schemars(extend("omitempty" = true))]
505 pub function: Option<AssistantToolCallFunctionDelta>,
506}
507
508impl AssistantToolCallDelta {
509 pub fn push(&mut self, other: &AssistantToolCallDelta) {
511 if self.r#type.is_none() {
512 self.r#type = other.r#type;
513 }
514 if self.id.is_none() {
515 self.id = other.id.clone();
516 }
517 match (&mut self.function, &other.function) {
518 (Some(self_function), Some(other_function)) => {
519 self_function.push(other_function);
520 }
521 (None, Some(other_function)) => {
522 self.function = Some(other_function.clone());
523 }
524 _ => {}
525 }
526 }
527}
528
529#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, Default, JsonSchema, arbitrary::Arbitrary)]
531#[schemars(rename = "agent.completions.message.AssistantToolCallType")]
532pub enum AssistantToolCallType {
533 #[serde(rename = "function")]
535 #[default]
536 Function,
537}
538
539#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default, JsonSchema, arbitrary::Arbitrary)]
541#[schemars(rename = "agent.completions.message.AssistantToolCallFunctionDelta")]
542pub struct AssistantToolCallFunctionDelta {
543 #[serde(skip_serializing_if = "Option::is_none")]
545 #[schemars(extend("omitempty" = true))]
546 pub name: Option<String>,
547 #[serde(skip_serializing_if = "Option::is_none")]
549 #[schemars(extend("omitempty" = true))]
550 pub arguments: Option<String>,
551}
552
553impl AssistantToolCallFunctionDelta {
554 pub fn push(&mut self, other: &AssistantToolCallFunctionDelta) {
556 if self.name.is_none() {
557 self.name = other.name.clone();
558 }
559 match (&mut self.arguments, &other.arguments) {
560 (Some(self_arguments), Some(other_arguments)) => {
561 self_arguments.push_str(other_arguments);
562 }
563 (None, Some(other_arguments)) => {
564 self.arguments = Some(other_arguments.clone());
565 }
566 _ => {}
567 }
568 }
569}
570
571#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema, arbitrary::Arbitrary)]
573#[schemars(rename = "agent.completions.message.AssistantToolCallFunctionExpression")]
574pub struct AssistantToolCallFunctionExpression {
575 pub name: functions::expression::WithExpression<String>,
577 pub arguments: functions::expression::WithExpression<String>,
579}
580
581impl AssistantToolCallFunctionExpression {
582 pub fn compile(
584 self,
585 params: &functions::expression::Params,
586 ) -> Result<AssistantToolCallFunction, functions::expression::ExpressionError>
587 {
588 let name = self.name.compile_one(params)?;
589 let arguments = self.arguments.compile_one(params)?;
590 Ok(AssistantToolCallFunction { name, arguments })
591 }
592}
593
594impl FromStarlarkValue for AssistantToolCallFunctionExpression {
595 fn from_starlark_value(
596 value: &StarlarkValue,
597 ) -> Result<Self, ExpressionError> {
598 let f = AssistantToolCallFunction::from_starlark_value(value)?;
599 Ok(AssistantToolCallFunctionExpression {
600 name: WithExpression::Value(f.name),
601 arguments: WithExpression::Value(f.arguments),
602 })
603 }
604}
605
606crate::functions::expression::impl_from_special_unsupported!(
607 AssistantToolCallExpression,
608 AssistantToolCallFunctionExpression,
609);
610
611impl crate::functions::expression::FromSpecial
612 for Vec<
613 crate::functions::expression::WithExpression<
614 AssistantToolCallExpression,
615 >,
616 >
617{
618 fn from_special(
619 _special: &crate::functions::expression::Special,
620 _params: &crate::functions::expression::Params,
621 ) -> Result<Self, crate::functions::expression::ExpressionError> {
622 Err(crate::functions::expression::ExpressionError::UnsupportedSpecial)
623 }
624}