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