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