1use serde::de::{self, Visitor};
4use serde::ser::SerializeMap;
5use serde::{Deserialize, Deserializer, Serialize, Serializer};
6use std::fmt;
7
8use crate::content::Content;
9use crate::environment::EnvironmentSpec;
10use crate::response_format::ResponseFormatSpec;
11use crate::safety::SafetySetting;
12use crate::steps::Step;
13use crate::tools::{Tool, ToolChoice};
14use crate::webhooks::WebhookConfig;
15
16#[derive(Clone, Debug, PartialEq)]
37#[non_exhaustive]
38pub enum Role {
39 User,
41 Model,
43 Unknown {
45 role_type: String,
47 data: serde_json::Value,
49 },
50}
51
52impl Role {
53 #[must_use]
55 pub const fn is_unknown(&self) -> bool {
56 matches!(self, Self::Unknown { .. })
57 }
58
59 #[must_use]
61 pub fn unknown_role_type(&self) -> Option<&str> {
62 match self {
63 Self::Unknown { role_type, .. } => Some(role_type),
64 _ => None,
65 }
66 }
67
68 #[must_use]
70 pub fn unknown_data(&self) -> Option<&serde_json::Value> {
71 match self {
72 Self::Unknown { data, .. } => Some(data),
73 _ => None,
74 }
75 }
76}
77
78impl fmt::Display for Role {
79 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
80 match self {
81 Self::User => write!(f, "user"),
82 Self::Model => write!(f, "model"),
83 Self::Unknown { role_type, .. } => write!(f, "{}", role_type),
84 }
85 }
86}
87
88impl Serialize for Role {
89 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
90 where
91 S: Serializer,
92 {
93 match self {
94 Role::User => serializer.serialize_str("user"),
95 Role::Model => serializer.serialize_str("model"),
96 Role::Unknown { role_type, .. } => serializer.serialize_str(role_type),
97 }
98 }
99}
100
101impl<'de> Deserialize<'de> for Role {
102 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
103 where
104 D: Deserializer<'de>,
105 {
106 let s = String::deserialize(deserializer)?;
107 match s.as_str() {
108 "user" => Ok(Role::User),
109 "model" => Ok(Role::Model),
110 other => {
111 tracing::warn!(
112 "Encountered unknown Role '{}' - using Unknown variant (Evergreen)",
113 other
114 );
115 Ok(Role::Unknown {
116 role_type: other.to_string(),
117 data: serde_json::Value::String(other.to_string()),
118 })
119 }
120 }
121 }
122}
123
124#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
145#[serde(untagged)]
146#[non_exhaustive]
147pub enum TurnContent {
148 Text(String),
150 Parts(Vec<Content>),
152}
153
154impl From<String> for TurnContent {
155 fn from(s: String) -> Self {
156 Self::Text(s)
157 }
158}
159
160impl From<&str> for TurnContent {
161 fn from(s: &str) -> Self {
162 Self::Text(s.to_string())
163 }
164}
165
166impl From<Vec<Content>> for TurnContent {
167 fn from(parts: Vec<Content>) -> Self {
168 Self::Parts(parts)
169 }
170}
171
172impl TurnContent {
173 #[must_use]
175 pub fn as_text(&self) -> Option<&str> {
176 match self {
177 Self::Text(t) => Some(t),
178 Self::Parts(_) => None,
179 }
180 }
181
182 #[must_use]
184 pub fn as_parts(&self) -> Option<&[Content]> {
185 match self {
186 Self::Parts(p) => Some(p),
187 Self::Text(_) => None,
188 }
189 }
190
191 #[must_use]
193 pub const fn is_text(&self) -> bool {
194 matches!(self, Self::Text(_))
195 }
196
197 #[must_use]
199 pub const fn is_parts(&self) -> bool {
200 matches!(self, Self::Parts(_))
201 }
202}
203
204#[derive(Clone, Debug, PartialEq)]
234#[non_exhaustive]
235pub enum InteractionInput {
236 Text(String),
238 Content(Vec<Content>),
240 Steps(Vec<Step>),
243}
244
245impl Default for InteractionInput {
246 fn default() -> Self {
250 Self::Text(String::new())
251 }
252}
253
254impl Serialize for InteractionInput {
255 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
256 where
257 S: Serializer,
258 {
259 match self {
260 Self::Text(t) => serializer.serialize_str(t),
261 Self::Content(c) => c.serialize(serializer),
262 Self::Steps(s) => s.serialize(serializer),
263 }
264 }
265}
266
267const CONTENT_TYPE_TAGS: &[&str] = &["text", "image", "audio", "video", "document"];
269
270impl<'de> Deserialize<'de> for InteractionInput {
271 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
272 where
273 D: Deserializer<'de>,
274 {
275 let value = serde_json::Value::deserialize(deserializer)?;
276 input_from_value(value).map_err(serde::de::Error::custom)
277 }
278}
279
280pub(crate) fn input_from_value(value: serde_json::Value) -> Result<InteractionInput, String> {
283 match value {
284 serde_json::Value::String(s) => Ok(InteractionInput::Text(s)),
285 serde_json::Value::Array(items) => {
286 let is_content = items.iter().all(|item| {
292 item.get("type")
293 .and_then(|t| t.as_str())
294 .is_some_and(|t| CONTENT_TYPE_TAGS.contains(&t))
295 });
296 if is_content && !items.is_empty() {
297 let contents = serde_json::from_value(serde_json::Value::Array(items))
298 .map_err(|e| e.to_string())?;
299 Ok(InteractionInput::Content(contents))
300 } else {
301 let steps = serde_json::from_value(serde_json::Value::Array(items))
302 .map_err(|e| e.to_string())?;
303 Ok(InteractionInput::Steps(steps))
304 }
305 }
306 other @ serde_json::Value::Object(_) => {
307 let is_content = other
309 .get("type")
310 .and_then(|t| t.as_str())
311 .is_some_and(|t| CONTENT_TYPE_TAGS.contains(&t));
312 if is_content {
313 let content: Content = serde_json::from_value(other).map_err(|e| e.to_string())?;
314 Ok(InteractionInput::Content(vec![content]))
315 } else {
316 let step: Step = serde_json::from_value(other).map_err(|e| e.to_string())?;
317 Ok(InteractionInput::Steps(vec![step]))
318 }
319 }
320 other => Err(format!(
321 "InteractionInput must be a string, array, or object; got {other}"
322 )),
323 }
324}
325
326#[derive(Clone, Debug, PartialEq, Eq)]
339#[non_exhaustive]
340pub enum ThinkingLevel {
341 Minimal,
348 Low,
350 Medium,
352 High,
354 Unknown {
356 level_type: String,
358 data: serde_json::Value,
360 },
361}
362
363impl ThinkingLevel {
364 #[must_use]
366 pub const fn is_unknown(&self) -> bool {
367 matches!(self, Self::Unknown { .. })
368 }
369
370 #[must_use]
372 pub fn unknown_level_type(&self) -> Option<&str> {
373 match self {
374 Self::Unknown { level_type, .. } => Some(level_type),
375 _ => None,
376 }
377 }
378
379 #[must_use]
381 pub fn unknown_data(&self) -> Option<&serde_json::Value> {
382 match self {
383 Self::Unknown { data, .. } => Some(data),
384 _ => None,
385 }
386 }
387}
388
389impl Serialize for ThinkingLevel {
390 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
391 where
392 S: Serializer,
393 {
394 match self {
395 ThinkingLevel::Minimal => serializer.serialize_str("minimal"),
396 ThinkingLevel::Low => serializer.serialize_str("low"),
397 ThinkingLevel::Medium => serializer.serialize_str("medium"),
398 ThinkingLevel::High => serializer.serialize_str("high"),
399 ThinkingLevel::Unknown { level_type, data } => {
400 if data.is_string() || data.is_null() {
402 serializer.serialize_str(level_type)
403 } else {
404 let mut map = serializer.serialize_map(None)?;
406 map.serialize_entry("level", level_type)?;
407 map.serialize_entry("data", data)?;
408 map.end()
409 }
410 }
411 }
412 }
413}
414
415impl<'de> Deserialize<'de> for ThinkingLevel {
416 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
417 where
418 D: Deserializer<'de>,
419 {
420 deserializer.deserialize_any(ThinkingLevelVisitor)
421 }
422}
423
424struct ThinkingLevelVisitor;
425
426impl<'de> Visitor<'de> for ThinkingLevelVisitor {
427 type Value = ThinkingLevel;
428
429 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
430 formatter.write_str("a thinking level string or object")
431 }
432
433 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
434 where
435 E: de::Error,
436 {
437 match value {
438 "minimal" => Ok(ThinkingLevel::Minimal),
439 "low" => Ok(ThinkingLevel::Low),
440 "medium" => Ok(ThinkingLevel::Medium),
441 "high" => Ok(ThinkingLevel::High),
442 other => {
443 tracing::warn!(
444 "Encountered unknown ThinkingLevel '{}' - using Unknown variant (Evergreen)",
445 other
446 );
447 Ok(ThinkingLevel::Unknown {
448 level_type: other.to_string(),
449 data: serde_json::Value::String(other.to_string()),
450 })
451 }
452 }
453 }
454
455 fn visit_map<A>(self, map: A) -> Result<Self::Value, A::Error>
456 where
457 A: de::MapAccess<'de>,
458 {
459 let value: serde_json::Value =
461 Deserialize::deserialize(de::value::MapAccessDeserializer::new(map))?;
462 let level_type = value
463 .get("level")
464 .and_then(|v| v.as_str())
465 .unwrap_or("unknown")
466 .to_string();
467
468 tracing::warn!(
469 "Encountered unknown ThinkingLevel object '{}' - using Unknown variant (Evergreen)",
470 level_type
471 );
472 Ok(ThinkingLevel::Unknown {
473 level_type,
474 data: value,
475 })
476 }
477}
478
479#[derive(Clone, Serialize, Deserialize, Debug, Default, PartialEq)]
481pub struct GenerationConfig {
482 #[serde(skip_serializing_if = "Option::is_none")]
483 pub temperature: Option<f32>,
484 #[serde(skip_serializing_if = "Option::is_none")]
485 pub max_output_tokens: Option<i32>,
486 #[serde(skip_serializing_if = "Option::is_none")]
487 pub top_p: Option<f32>,
488 #[serde(skip_serializing_if = "Option::is_none")]
490 pub thinking_level: Option<ThinkingLevel>,
491 #[serde(skip_serializing_if = "Option::is_none")]
496 pub seed: Option<i64>,
497 #[serde(skip_serializing_if = "Option::is_none")]
501 pub stop_sequences: Option<Vec<String>>,
502 #[serde(skip_serializing_if = "Option::is_none")]
506 pub thinking_summaries: Option<ThinkingSummaries>,
507 #[serde(skip_serializing_if = "Option::is_none")]
512 pub tool_choice: Option<ToolChoice>,
513 #[serde(skip_serializing_if = "Option::is_none")]
516 pub presence_penalty: Option<f32>,
517 #[serde(skip_serializing_if = "Option::is_none")]
520 pub frequency_penalty: Option<f32>,
521 #[serde(
530 default,
531 skip_serializing_if = "Option::is_none",
532 deserialize_with = "deserialize_speech_configs"
533 )]
534 pub speech_config: Option<Vec<SpeechConfig>>,
535 #[serde(skip_serializing_if = "Option::is_none")]
539 pub image_config: Option<ImageConfig>,
540 #[serde(skip_serializing_if = "Option::is_none")]
545 pub video_config: Option<VideoConfig>,
546 #[serde(skip_serializing_if = "Option::is_none")]
551 pub transcription_config: Option<TranscriptionConfig>,
552}
553
554#[derive(Clone, Serialize, Deserialize, Debug, Default, PartialEq)]
580pub struct TranscriptionConfig {
581 #[serde(skip_serializing_if = "Option::is_none")]
583 pub adaptation_phrases: Option<Vec<String>>,
584 #[serde(skip_serializing_if = "Option::is_none")]
586 pub custom_vocabulary: Option<Vec<String>>,
587 #[serde(skip_serializing_if = "Option::is_none")]
591 pub diarization_mode: Option<String>,
592 #[serde(skip_serializing_if = "Option::is_none")]
595 pub language_codes: Option<Vec<String>>,
596 #[serde(skip_serializing_if = "Option::is_none")]
600 pub timestamp_granularities: Option<Vec<String>>,
601}
602
603impl TranscriptionConfig {
604 #[must_use]
606 pub fn new() -> Self {
607 Self::default()
608 }
609
610 #[must_use]
612 pub fn with_language_codes(
613 mut self,
614 language_codes: impl IntoIterator<Item = impl Into<String>>,
615 ) -> Self {
616 self.language_codes = Some(language_codes.into_iter().map(Into::into).collect());
617 self
618 }
619
620 #[must_use]
623 pub fn with_diarization_mode(mut self, mode: impl Into<String>) -> Self {
624 self.diarization_mode = Some(mode.into());
625 self
626 }
627
628 #[must_use]
631 pub fn with_timestamp_granularities(
632 mut self,
633 granularities: impl IntoIterator<Item = impl Into<String>>,
634 ) -> Self {
635 self.timestamp_granularities = Some(granularities.into_iter().map(Into::into).collect());
636 self
637 }
638
639 #[must_use]
642 pub fn with_custom_vocabulary(
643 mut self,
644 vocabulary: impl IntoIterator<Item = impl Into<String>>,
645 ) -> Self {
646 self.custom_vocabulary = Some(vocabulary.into_iter().map(Into::into).collect());
647 self
648 }
649
650 #[must_use]
656 pub fn with_adaptation_phrases(
657 mut self,
658 phrases: impl IntoIterator<Item = impl Into<String>>,
659 ) -> Self {
660 self.adaptation_phrases = Some(phrases.into_iter().map(Into::into).collect());
661 self
662 }
663
664 #[must_use]
667 pub fn add_adaptation_phrase(mut self, phrase: impl Into<String>) -> Self {
668 self.adaptation_phrases
669 .get_or_insert_with(Vec::new)
670 .push(phrase.into());
671 self
672 }
673}
674
675fn deserialize_speech_configs<'de, D>(
678 deserializer: D,
679) -> Result<Option<Vec<SpeechConfig>>, D::Error>
680where
681 D: Deserializer<'de>,
682{
683 #[derive(Deserialize)]
684 #[serde(untagged)]
685 enum ListOrSingle {
686 List(Vec<SpeechConfig>),
687 Single(SpeechConfig),
688 }
689
690 Ok(
691 Option::<ListOrSingle>::deserialize(deserializer)?.map(|value| match value {
692 ListOrSingle::List(list) => list,
693 ListOrSingle::Single(single) => vec![single],
694 }),
695 )
696}
697
698#[derive(Clone, Serialize, Deserialize, Debug, Default, PartialEq)]
720pub struct SpeechConfig {
721 #[serde(skip_serializing_if = "Option::is_none")]
725 pub voice: Option<String>,
726
727 #[serde(skip_serializing_if = "Option::is_none")]
731 pub language: Option<String>,
732
733 #[serde(skip_serializing_if = "Option::is_none")]
738 pub speaker: Option<String>,
739}
740
741impl SpeechConfig {
742 #[must_use]
744 pub fn with_voice(voice: impl Into<String>) -> Self {
745 Self {
746 voice: Some(voice.into()),
747 ..Default::default()
748 }
749 }
750
751 #[must_use]
753 pub fn with_voice_and_language(voice: impl Into<String>, language: impl Into<String>) -> Self {
754 Self {
755 voice: Some(voice.into()),
756 language: Some(language.into()),
757 ..Default::default()
758 }
759 }
760}
761
762#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
777#[serde(default)]
778pub struct ImageConfig {
779 #[serde(skip_serializing_if = "Option::is_none")]
781 pub aspect_ratio: Option<ImageAspectRatio>,
782 #[serde(skip_serializing_if = "Option::is_none")]
784 pub image_size: Option<ImageSize>,
785}
786
787#[derive(Clone, Debug, PartialEq, Eq)]
801#[non_exhaustive]
802pub enum ImageAspectRatio {
803 Square,
805 Portrait2x3,
807 Landscape3x2,
809 Portrait3x4,
811 Landscape4x3,
813 Portrait4x5,
815 Landscape5x4,
817 Portrait9x16,
819 Widescreen16x9,
821 Ultrawide21x9,
823 Tall1x8,
825 Wide8x1,
827 Tall1x4,
829 Wide4x1,
831 Unknown {
833 ratio_type: String,
835 data: serde_json::Value,
837 },
838}
839
840impl ImageAspectRatio {
841 #[must_use]
843 pub const fn is_unknown(&self) -> bool {
844 matches!(self, Self::Unknown { .. })
845 }
846
847 #[must_use]
849 pub fn unknown_ratio_type(&self) -> Option<&str> {
850 match self {
851 Self::Unknown { ratio_type, .. } => Some(ratio_type),
852 _ => None,
853 }
854 }
855
856 #[must_use]
858 pub fn unknown_data(&self) -> Option<&serde_json::Value> {
859 match self {
860 Self::Unknown { data, .. } => Some(data),
861 _ => None,
862 }
863 }
864}
865
866impl Serialize for ImageAspectRatio {
867 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
868 where
869 S: Serializer,
870 {
871 match self {
872 Self::Square => serializer.serialize_str("1:1"),
873 Self::Portrait2x3 => serializer.serialize_str("2:3"),
874 Self::Landscape3x2 => serializer.serialize_str("3:2"),
875 Self::Portrait3x4 => serializer.serialize_str("3:4"),
876 Self::Landscape4x3 => serializer.serialize_str("4:3"),
877 Self::Portrait4x5 => serializer.serialize_str("4:5"),
878 Self::Landscape5x4 => serializer.serialize_str("5:4"),
879 Self::Portrait9x16 => serializer.serialize_str("9:16"),
880 Self::Widescreen16x9 => serializer.serialize_str("16:9"),
881 Self::Ultrawide21x9 => serializer.serialize_str("21:9"),
882 Self::Tall1x8 => serializer.serialize_str("1:8"),
883 Self::Wide8x1 => serializer.serialize_str("8:1"),
884 Self::Tall1x4 => serializer.serialize_str("1:4"),
885 Self::Wide4x1 => serializer.serialize_str("4:1"),
886 Self::Unknown { ratio_type, .. } => serializer.serialize_str(ratio_type),
887 }
888 }
889}
890
891impl<'de> Deserialize<'de> for ImageAspectRatio {
892 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
893 where
894 D: Deserializer<'de>,
895 {
896 let value = serde_json::Value::deserialize(deserializer)?;
897 match value.as_str() {
898 Some("1:1") => Ok(Self::Square),
899 Some("2:3") => Ok(Self::Portrait2x3),
900 Some("3:2") => Ok(Self::Landscape3x2),
901 Some("3:4") => Ok(Self::Portrait3x4),
902 Some("4:3") => Ok(Self::Landscape4x3),
903 Some("4:5") => Ok(Self::Portrait4x5),
904 Some("5:4") => Ok(Self::Landscape5x4),
905 Some("9:16") => Ok(Self::Portrait9x16),
906 Some("16:9") => Ok(Self::Widescreen16x9),
907 Some("21:9") => Ok(Self::Ultrawide21x9),
908 Some("1:8") => Ok(Self::Tall1x8),
909 Some("8:1") => Ok(Self::Wide8x1),
910 Some("1:4") => Ok(Self::Tall1x4),
911 Some("4:1") => Ok(Self::Wide4x1),
912 Some(other) => {
913 tracing::warn!(
914 "Encountered unknown ImageAspectRatio '{}'. \
915 Preserving in Unknown variant.",
916 other
917 );
918 Ok(Self::Unknown {
919 ratio_type: other.to_string(),
920 data: value,
921 })
922 }
923 None => {
924 let ratio_type = format!("<non-string: {}>", value);
925 tracing::warn!(
926 "ImageAspectRatio received non-string value: {}. \
927 Preserving in Unknown variant.",
928 value
929 );
930 Ok(Self::Unknown {
931 ratio_type,
932 data: value,
933 })
934 }
935 }
936 }
937}
938
939#[derive(Clone, Debug, PartialEq, Eq)]
953#[non_exhaustive]
954pub enum ImageSize {
955 Sd512,
957 Hd1k,
959 Hd2k,
961 Uhd4k,
963 Unknown {
965 size_type: String,
967 data: serde_json::Value,
969 },
970}
971
972impl ImageSize {
973 #[must_use]
975 pub const fn is_unknown(&self) -> bool {
976 matches!(self, Self::Unknown { .. })
977 }
978
979 #[must_use]
981 pub fn unknown_size_type(&self) -> Option<&str> {
982 match self {
983 Self::Unknown { size_type, .. } => Some(size_type),
984 _ => None,
985 }
986 }
987
988 #[must_use]
990 pub fn unknown_data(&self) -> Option<&serde_json::Value> {
991 match self {
992 Self::Unknown { data, .. } => Some(data),
993 _ => None,
994 }
995 }
996}
997
998impl Serialize for ImageSize {
999 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1000 where
1001 S: Serializer,
1002 {
1003 match self {
1004 Self::Sd512 => serializer.serialize_str("512"),
1005 Self::Hd1k => serializer.serialize_str("1K"),
1006 Self::Hd2k => serializer.serialize_str("2K"),
1007 Self::Uhd4k => serializer.serialize_str("4K"),
1008 Self::Unknown { size_type, .. } => serializer.serialize_str(size_type),
1009 }
1010 }
1011}
1012
1013impl<'de> Deserialize<'de> for ImageSize {
1014 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1015 where
1016 D: Deserializer<'de>,
1017 {
1018 let value = serde_json::Value::deserialize(deserializer)?;
1019 match value.as_str() {
1020 Some("512") => Ok(Self::Sd512),
1021 Some("1K") => Ok(Self::Hd1k),
1022 Some("2K") => Ok(Self::Hd2k),
1023 Some("4K") => Ok(Self::Uhd4k),
1024 Some(other) => {
1025 tracing::warn!(
1026 "Encountered unknown ImageSize '{}'. \
1027 Preserving in Unknown variant.",
1028 other
1029 );
1030 Ok(Self::Unknown {
1031 size_type: other.to_string(),
1032 data: value,
1033 })
1034 }
1035 None => {
1036 let size_type = format!("<non-string: {}>", value);
1037 tracing::warn!(
1038 "ImageSize received non-string value: {}. \
1039 Preserving in Unknown variant.",
1040 value
1041 );
1042 Ok(Self::Unknown {
1043 size_type,
1044 data: value,
1045 })
1046 }
1047 }
1048 }
1049}
1050
1051#[derive(Clone, Debug, PartialEq)]
1070#[non_exhaustive]
1071pub enum VideoTask {
1072 TextToVideo,
1074 ImageToVideo,
1076 ReferenceToVideo,
1078 Edit,
1080 Extend,
1082 Unknown {
1084 task_type: String,
1086 data: serde_json::Value,
1088 },
1089}
1090
1091impl VideoTask {
1092 #[must_use]
1094 pub const fn is_unknown(&self) -> bool {
1095 matches!(self, Self::Unknown { .. })
1096 }
1097
1098 #[must_use]
1100 pub fn unknown_task_type(&self) -> Option<&str> {
1101 match self {
1102 Self::Unknown { task_type, .. } => Some(task_type),
1103 _ => None,
1104 }
1105 }
1106
1107 #[must_use]
1109 pub fn unknown_data(&self) -> Option<&serde_json::Value> {
1110 match self {
1111 Self::Unknown { data, .. } => Some(data),
1112 _ => None,
1113 }
1114 }
1115}
1116
1117impl Serialize for VideoTask {
1118 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1119 where
1120 S: Serializer,
1121 {
1122 match self {
1123 Self::TextToVideo => serializer.serialize_str("text_to_video"),
1124 Self::ImageToVideo => serializer.serialize_str("image_to_video"),
1125 Self::ReferenceToVideo => serializer.serialize_str("reference_to_video"),
1126 Self::Edit => serializer.serialize_str("edit"),
1127 Self::Extend => serializer.serialize_str("extend"),
1128 Self::Unknown { task_type, .. } => serializer.serialize_str(task_type),
1129 }
1130 }
1131}
1132
1133impl<'de> Deserialize<'de> for VideoTask {
1134 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1135 where
1136 D: Deserializer<'de>,
1137 {
1138 let value = serde_json::Value::deserialize(deserializer)?;
1139 match value.as_str() {
1140 Some("text_to_video") => Ok(Self::TextToVideo),
1141 Some("image_to_video") => Ok(Self::ImageToVideo),
1142 Some("reference_to_video") => Ok(Self::ReferenceToVideo),
1143 Some("edit") => Ok(Self::Edit),
1144 Some("extend") => Ok(Self::Extend),
1145 Some(other) => {
1146 tracing::warn!(
1147 "Encountered unknown VideoTask '{}' - using Unknown variant (Evergreen)",
1148 other
1149 );
1150 Ok(Self::Unknown {
1151 task_type: other.to_string(),
1152 data: value,
1153 })
1154 }
1155 None => {
1156 let task_type = format!("<non-string: {}>", value);
1157 tracing::warn!(
1158 "VideoTask received non-string value: {}. \
1159 Preserving in Unknown variant.",
1160 value
1161 );
1162 Ok(Self::Unknown {
1163 task_type,
1164 data: value,
1165 })
1166 }
1167 }
1168 }
1169}
1170
1171#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
1182#[serde(default)]
1183pub struct VideoConfig {
1184 #[serde(skip_serializing_if = "Option::is_none")]
1187 pub task: Option<VideoTask>,
1188}
1189
1190impl VideoConfig {
1191 #[must_use]
1193 pub fn new() -> Self {
1194 Self::default()
1195 }
1196
1197 #[must_use]
1199 pub fn with_task(mut self, task: VideoTask) -> Self {
1200 self.task = Some(task);
1201 self
1202 }
1203}
1204
1205#[derive(Clone, Serialize, Deserialize, Debug, Default, PartialEq)]
1269pub struct InteractionRequest {
1270 #[serde(skip_serializing_if = "Option::is_none")]
1272 pub model: Option<String>,
1273
1274 #[serde(skip_serializing_if = "Option::is_none")]
1276 pub agent: Option<String>,
1277
1278 #[serde(rename = "agent_config", skip_serializing_if = "Option::is_none")]
1280 pub agent_config: Option<AgentConfig>,
1281
1282 pub input: InteractionInput,
1301
1302 #[serde(skip_serializing_if = "Option::is_none")]
1304 pub previous_interaction_id: Option<String>,
1305
1306 #[serde(skip_serializing_if = "Option::is_none")]
1308 pub tools: Option<Vec<Tool>>,
1309
1310 #[serde(skip_serializing_if = "Option::is_none")]
1318 pub response_modalities: Option<Vec<String>>,
1319
1320 #[serde(skip_serializing_if = "Option::is_none")]
1326 pub response_format: Option<ResponseFormatSpec>,
1327
1328 #[serde(skip_serializing_if = "Option::is_none")]
1330 pub generation_config: Option<GenerationConfig>,
1331
1332 #[serde(skip_serializing_if = "Option::is_none")]
1334 pub stream: Option<bool>,
1335
1336 #[serde(skip_serializing_if = "Option::is_none")]
1338 pub background: Option<bool>,
1339
1340 #[serde(skip_serializing_if = "Option::is_none")]
1342 pub store: Option<bool>,
1343
1344 #[serde(skip_serializing_if = "Option::is_none")]
1346 pub system_instruction: Option<String>,
1347
1348 #[serde(skip_serializing_if = "Option::is_none")]
1350 pub service_tier: Option<ServiceTier>,
1351
1352 #[serde(skip_serializing_if = "Option::is_none")]
1355 pub cached_content: Option<String>,
1356
1357 #[serde(skip_serializing_if = "Option::is_none")]
1361 pub webhook_config: Option<WebhookConfig>,
1362
1363 #[serde(skip_serializing_if = "Option::is_none")]
1366 pub environment: Option<EnvironmentSpec>,
1367
1368 #[serde(skip_serializing_if = "Option::is_none")]
1375 pub safety_settings: Option<Vec<SafetySetting>>,
1376
1377 #[serde(skip_serializing_if = "Option::is_none")]
1388 pub labels: Option<std::collections::BTreeMap<String, String>>,
1389}
1390
1391#[derive(Clone, Debug, PartialEq)]
1404#[non_exhaustive]
1405pub enum ServiceTier {
1406 Flex,
1408 Standard,
1410 Priority,
1412 Unknown {
1414 tier_type: String,
1416 data: serde_json::Value,
1418 },
1419}
1420
1421impl ServiceTier {
1422 #[must_use]
1424 pub const fn is_unknown(&self) -> bool {
1425 matches!(self, Self::Unknown { .. })
1426 }
1427
1428 #[must_use]
1430 pub fn unknown_tier_type(&self) -> Option<&str> {
1431 match self {
1432 Self::Unknown { tier_type, .. } => Some(tier_type),
1433 _ => None,
1434 }
1435 }
1436
1437 #[must_use]
1439 pub fn unknown_data(&self) -> Option<&serde_json::Value> {
1440 match self {
1441 Self::Unknown { data, .. } => Some(data),
1442 _ => None,
1443 }
1444 }
1445}
1446
1447impl fmt::Display for ServiceTier {
1448 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1449 match self {
1450 Self::Flex => write!(f, "flex"),
1451 Self::Standard => write!(f, "standard"),
1452 Self::Priority => write!(f, "priority"),
1453 Self::Unknown { tier_type, .. } => write!(f, "{}", tier_type),
1454 }
1455 }
1456}
1457
1458impl Serialize for ServiceTier {
1459 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1460 where
1461 S: Serializer,
1462 {
1463 match self {
1464 Self::Flex => serializer.serialize_str("flex"),
1465 Self::Standard => serializer.serialize_str("standard"),
1466 Self::Priority => serializer.serialize_str("priority"),
1467 Self::Unknown { tier_type, .. } => serializer.serialize_str(tier_type),
1468 }
1469 }
1470}
1471
1472impl<'de> Deserialize<'de> for ServiceTier {
1473 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1474 where
1475 D: Deserializer<'de>,
1476 {
1477 let value = serde_json::Value::deserialize(deserializer)?;
1478 match value.as_str() {
1479 Some("flex") => Ok(Self::Flex),
1480 Some("standard") => Ok(Self::Standard),
1481 Some("priority") => Ok(Self::Priority),
1482 Some(other) => {
1483 tracing::warn!(
1484 "Encountered unknown ServiceTier '{}' - using Unknown variant (Evergreen)",
1485 other
1486 );
1487 Ok(Self::Unknown {
1488 tier_type: other.to_string(),
1489 data: value,
1490 })
1491 }
1492 None => {
1493 let tier_type = format!("<non-string: {}>", value);
1494 tracing::warn!(
1495 "ServiceTier received non-string value: {}. \
1496 Preserving in Unknown variant.",
1497 value
1498 );
1499 Ok(Self::Unknown {
1500 tier_type,
1501 data: value,
1502 })
1503 }
1504 }
1505 }
1506}
1507
1508#[derive(Clone, Debug, PartialEq, Eq)]
1525#[non_exhaustive]
1526pub enum ThinkingSummaries {
1527 Auto,
1529 None,
1531 Unknown {
1533 summaries_type: String,
1535 data: serde_json::Value,
1537 },
1538}
1539
1540impl ThinkingSummaries {
1541 #[must_use]
1543 pub const fn is_unknown(&self) -> bool {
1544 matches!(self, Self::Unknown { .. })
1545 }
1546
1547 #[must_use]
1549 pub fn unknown_summaries_type(&self) -> Option<&str> {
1550 match self {
1551 Self::Unknown { summaries_type, .. } => Some(summaries_type),
1552 _ => None,
1553 }
1554 }
1555
1556 #[must_use]
1558 pub fn unknown_data(&self) -> Option<&serde_json::Value> {
1559 match self {
1560 Self::Unknown { data, .. } => Some(data),
1561 _ => None,
1562 }
1563 }
1564
1565 #[must_use]
1581 pub fn to_agent_config_value(&self) -> serde_json::Value {
1582 match self {
1583 ThinkingSummaries::Auto => serde_json::Value::String("auto".to_string()),
1584 ThinkingSummaries::None => serde_json::Value::String("none".to_string()),
1585 ThinkingSummaries::Unknown { summaries_type, .. } => {
1586 serde_json::Value::String(summaries_type.clone())
1588 }
1589 }
1590 }
1591}
1592
1593impl Serialize for ThinkingSummaries {
1594 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1595 where
1596 S: Serializer,
1597 {
1598 match self {
1601 ThinkingSummaries::Auto => serializer.serialize_str("auto"),
1602 ThinkingSummaries::None => serializer.serialize_str("none"),
1603 ThinkingSummaries::Unknown {
1604 summaries_type,
1605 data,
1606 } => {
1607 if data.is_string() || data.is_null() {
1609 serializer.serialize_str(summaries_type)
1610 } else {
1611 let mut map = serializer.serialize_map(None)?;
1613 map.serialize_entry("summaries", summaries_type)?;
1614 map.serialize_entry("data", data)?;
1615 map.end()
1616 }
1617 }
1618 }
1619 }
1620}
1621
1622impl<'de> Deserialize<'de> for ThinkingSummaries {
1623 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1624 where
1625 D: Deserializer<'de>,
1626 {
1627 deserializer.deserialize_any(ThinkingSummariesVisitor)
1628 }
1629}
1630
1631struct ThinkingSummariesVisitor;
1632
1633impl<'de> Visitor<'de> for ThinkingSummariesVisitor {
1634 type Value = ThinkingSummaries;
1635
1636 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
1637 formatter.write_str("a thinking summaries string or object")
1638 }
1639
1640 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
1641 where
1642 E: de::Error,
1643 {
1644 match value {
1645 "THINKING_SUMMARIES_AUTO" | "auto" => Ok(ThinkingSummaries::Auto),
1647 "THINKING_SUMMARIES_NONE" | "none" => Ok(ThinkingSummaries::None),
1648 other => {
1649 tracing::warn!(
1650 "Encountered unknown ThinkingSummaries '{}' - using Unknown variant (Evergreen)",
1651 other
1652 );
1653 Ok(ThinkingSummaries::Unknown {
1654 summaries_type: other.to_string(),
1655 data: serde_json::Value::String(other.to_string()),
1656 })
1657 }
1658 }
1659 }
1660
1661 fn visit_map<A>(self, map: A) -> Result<Self::Value, A::Error>
1662 where
1663 A: de::MapAccess<'de>,
1664 {
1665 let value: serde_json::Value =
1667 Deserialize::deserialize(de::value::MapAccessDeserializer::new(map))?;
1668 let summaries_type = value
1669 .get("summaries")
1670 .and_then(|v| v.as_str())
1671 .unwrap_or("unknown")
1672 .to_string();
1673
1674 tracing::warn!(
1675 "Encountered unknown ThinkingSummaries object '{}' - using Unknown variant (Evergreen)",
1676 summaries_type
1677 );
1678 Ok(ThinkingSummaries::Unknown {
1679 summaries_type,
1680 data: value,
1681 })
1682 }
1683}
1684
1685#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1712#[serde(transparent)]
1713pub struct AgentConfig(serde_json::Value);
1714
1715impl AgentConfig {
1716 #[must_use]
1720 pub fn from_value(value: serde_json::Value) -> Self {
1721 Self(value)
1722 }
1723
1724 #[must_use]
1726 pub fn as_value(&self) -> &serde_json::Value {
1727 &self.0
1728 }
1729
1730 #[must_use]
1732 pub fn config_type(&self) -> Option<&str> {
1733 self.0.get("type").and_then(|v| v.as_str())
1734 }
1735}
1736
1737#[derive(Clone, Debug, PartialEq)]
1752#[non_exhaustive]
1753pub enum Visualization {
1754 Off,
1756 Auto,
1758 Unknown {
1760 visualization_type: String,
1762 data: serde_json::Value,
1764 },
1765}
1766
1767impl Visualization {
1768 #[must_use]
1770 pub const fn is_unknown(&self) -> bool {
1771 matches!(self, Self::Unknown { .. })
1772 }
1773
1774 #[must_use]
1776 pub fn unknown_visualization_type(&self) -> Option<&str> {
1777 match self {
1778 Self::Unknown {
1779 visualization_type, ..
1780 } => Some(visualization_type),
1781 _ => None,
1782 }
1783 }
1784
1785 #[must_use]
1787 pub fn unknown_data(&self) -> Option<&serde_json::Value> {
1788 match self {
1789 Self::Unknown { data, .. } => Some(data),
1790 _ => None,
1791 }
1792 }
1793}
1794
1795impl Serialize for Visualization {
1796 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1797 where
1798 S: Serializer,
1799 {
1800 match self {
1801 Self::Off => serializer.serialize_str("off"),
1802 Self::Auto => serializer.serialize_str("auto"),
1803 Self::Unknown {
1804 visualization_type, ..
1805 } => serializer.serialize_str(visualization_type),
1806 }
1807 }
1808}
1809
1810impl<'de> Deserialize<'de> for Visualization {
1811 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1812 where
1813 D: Deserializer<'de>,
1814 {
1815 let value = serde_json::Value::deserialize(deserializer)?;
1816 match value.as_str() {
1817 Some("off") => Ok(Self::Off),
1818 Some("auto") => Ok(Self::Auto),
1819 Some(other) => {
1820 tracing::warn!(
1821 "Encountered unknown Visualization '{}' - using Unknown variant (Evergreen)",
1822 other
1823 );
1824 Ok(Self::Unknown {
1825 visualization_type: other.to_string(),
1826 data: value,
1827 })
1828 }
1829 None => {
1830 let visualization_type = format!("<non-string: {}>", value);
1831 tracing::warn!(
1832 "Visualization received non-string value: {}. \
1833 Preserving in Unknown variant.",
1834 value
1835 );
1836 Ok(Self::Unknown {
1837 visualization_type,
1838 data: value,
1839 })
1840 }
1841 }
1842 }
1843}
1844
1845#[derive(Clone, Debug, Default)]
1868pub struct DeepResearchConfig {
1869 thinking_summaries: Option<ThinkingSummaries>,
1870 visualization: Option<Visualization>,
1871 collaborative_planning: Option<bool>,
1872 enable_bigquery_tool: Option<bool>,
1873}
1874
1875impl DeepResearchConfig {
1876 #[must_use]
1878 pub fn new() -> Self {
1879 Self::default()
1880 }
1881
1882 #[must_use]
1886 pub fn with_thinking_summaries(mut self, summaries: ThinkingSummaries) -> Self {
1887 self.thinking_summaries = Some(summaries);
1888 self
1889 }
1890
1891 #[must_use]
1893 pub fn with_visualization(mut self, visualization: Visualization) -> Self {
1894 self.visualization = Some(visualization);
1895 self
1896 }
1897
1898 #[must_use]
1903 pub fn with_collaborative_planning(mut self, enabled: bool) -> Self {
1904 self.collaborative_planning = Some(enabled);
1905 self
1906 }
1907
1908 #[must_use]
1915 pub fn with_bigquery_tool(mut self, enabled: bool) -> Self {
1916 self.enable_bigquery_tool = Some(enabled);
1917 self
1918 }
1919}
1920
1921impl From<DeepResearchConfig> for AgentConfig {
1922 fn from(config: DeepResearchConfig) -> Self {
1923 let mut map = serde_json::Map::new();
1924 map.insert(
1925 "type".into(),
1926 serde_json::Value::String("deep-research".into()),
1927 );
1928 if let Some(ts) = config.thinking_summaries {
1929 map.insert("thinking_summaries".into(), ts.to_agent_config_value());
1931 }
1932 if let Some(visualization) = config.visualization {
1933 map.insert(
1934 "visualization".into(),
1935 serde_json::to_value(&visualization)
1936 .expect("Visualization serialization is infallible"),
1937 );
1938 }
1939 if let Some(planning) = config.collaborative_planning {
1940 map.insert(
1941 "collaborative_planning".into(),
1942 serde_json::Value::Bool(planning),
1943 );
1944 }
1945 if let Some(bigquery) = config.enable_bigquery_tool {
1946 map.insert(
1947 "enable_bigquery_tool".into(),
1948 serde_json::Value::Bool(bigquery),
1949 );
1950 }
1951 AgentConfig(serde_json::Value::Object(map))
1952 }
1953}
1954
1955#[derive(Clone, Debug, Default)]
1968pub struct DynamicConfig;
1969
1970impl DynamicConfig {
1971 #[must_use]
1973 pub fn new() -> Self {
1974 Self
1975 }
1976}
1977
1978impl From<DynamicConfig> for AgentConfig {
1979 fn from(_: DynamicConfig) -> Self {
1980 AgentConfig(serde_json::json!({"type": "dynamic"}))
1981 }
1982}
1983
1984#[derive(Clone, Debug, Default)]
2015pub struct AntigravityConfig {
2016 model: Option<String>,
2017 max_total_tokens: Option<i64>,
2018}
2019
2020impl AntigravityConfig {
2021 #[must_use]
2023 pub fn new() -> Self {
2024 Self::default()
2025 }
2026
2027 #[must_use]
2033 pub fn with_model(mut self, model: impl Into<String>) -> Self {
2034 self.model = Some(model.into());
2035 self
2036 }
2037
2038 #[must_use]
2040 pub fn with_max_total_tokens(mut self, max_total_tokens: i64) -> Self {
2041 self.max_total_tokens = Some(max_total_tokens);
2042 self
2043 }
2044}
2045
2046impl From<AntigravityConfig> for AgentConfig {
2047 fn from(config: AntigravityConfig) -> Self {
2048 let mut map = serde_json::Map::new();
2049 map.insert(
2050 "type".into(),
2051 serde_json::Value::String("antigravity".into()),
2052 );
2053 if let Some(model) = config.model {
2054 map.insert("model".into(), serde_json::Value::String(model));
2055 }
2056 if let Some(max) = config.max_total_tokens {
2057 map.insert("max_total_tokens".into(), serde_json::Value::from(max));
2058 }
2059 AgentConfig(serde_json::Value::Object(map))
2060 }
2061}
2062
2063#[cfg(test)]
2064mod tests {
2065 use super::*;
2066
2067 #[test]
2072 fn test_thinking_summaries_serialization() {
2073 assert_eq!(
2075 serde_json::to_string(&ThinkingSummaries::Auto).unwrap(),
2076 "\"auto\""
2077 );
2078
2079 assert_eq!(
2080 serde_json::to_string(&ThinkingSummaries::None).unwrap(),
2081 "\"none\""
2082 );
2083 }
2084
2085 #[test]
2086 fn test_thinking_summaries_agent_config_format() {
2087 assert_eq!(
2089 ThinkingSummaries::Auto.to_agent_config_value(),
2090 serde_json::Value::String("auto".to_string())
2091 );
2092
2093 assert_eq!(
2094 ThinkingSummaries::None.to_agent_config_value(),
2095 serde_json::Value::String("none".to_string())
2096 );
2097 }
2098
2099 #[test]
2100 fn test_thinking_summaries_deserialization() {
2101 assert_eq!(
2103 serde_json::from_str::<ThinkingSummaries>("\"THINKING_SUMMARIES_AUTO\"").unwrap(),
2104 ThinkingSummaries::Auto
2105 );
2106 assert_eq!(
2107 serde_json::from_str::<ThinkingSummaries>("\"THINKING_SUMMARIES_NONE\"").unwrap(),
2108 ThinkingSummaries::None
2109 );
2110
2111 assert_eq!(
2113 serde_json::from_str::<ThinkingSummaries>("\"auto\"").unwrap(),
2114 ThinkingSummaries::Auto
2115 );
2116 assert_eq!(
2117 serde_json::from_str::<ThinkingSummaries>("\"none\"").unwrap(),
2118 ThinkingSummaries::None
2119 );
2120 }
2121
2122 #[test]
2123 fn test_thinking_summaries_unknown_roundtrip() {
2124 let unknown: ThinkingSummaries = serde_json::from_str("\"future_variant\"").unwrap();
2125 assert!(unknown.is_unknown());
2126 assert_eq!(unknown.unknown_summaries_type(), Some("future_variant"));
2127
2128 let json = serde_json::to_string(&unknown).unwrap();
2130 assert_eq!(json, "\"future_variant\"");
2131 }
2132
2133 #[test]
2134 fn test_deep_research_config_serialization() {
2135 let config: AgentConfig = DeepResearchConfig::new()
2136 .with_thinking_summaries(ThinkingSummaries::Auto)
2137 .into();
2138
2139 let json = serde_json::to_string(&config).expect("Serialization failed");
2140 let value: serde_json::Value = serde_json::from_str(&json).unwrap();
2141
2142 assert_eq!(value["type"], "deep-research");
2143 assert_eq!(value["thinking_summaries"], "auto");
2144 }
2145
2146 #[test]
2147 fn test_deep_research_config_without_thinking_summaries() {
2148 let config: AgentConfig = DeepResearchConfig::new().into();
2149
2150 let json = serde_json::to_string(&config).expect("Serialization failed");
2151 let value: serde_json::Value = serde_json::from_str(&json).unwrap();
2152
2153 assert_eq!(value["type"], "deep-research");
2154 assert!(value.get("thinking_summaries").is_none());
2155 }
2156
2157 #[test]
2158 fn test_dynamic_config_serialization() {
2159 let config: AgentConfig = DynamicConfig::new().into();
2160
2161 let json = serde_json::to_string(&config).expect("Serialization failed");
2162 let value: serde_json::Value = serde_json::from_str(&json).unwrap();
2163
2164 assert_eq!(value["type"], "dynamic");
2165 }
2166
2167 #[test]
2168 fn test_agent_config_from_raw_json() {
2169 let config = AgentConfig::from_value(serde_json::json!({
2170 "type": "custom-agent",
2171 "option1": true,
2172 "option2": "value"
2173 }));
2174
2175 assert_eq!(config.config_type(), Some("custom-agent"));
2176 assert_eq!(config.as_value()["option1"], true);
2177 }
2178
2179 #[test]
2180 fn test_agent_config_roundtrip() {
2181 let config: AgentConfig = DeepResearchConfig::new()
2182 .with_thinking_summaries(ThinkingSummaries::Auto)
2183 .into();
2184
2185 let json = serde_json::to_string(&config).expect("Serialization failed");
2186 let parsed: AgentConfig = serde_json::from_str(&json).expect("Deserialization failed");
2187
2188 assert_eq!(config, parsed);
2189 }
2190
2191 #[test]
2196 fn test_speech_config_with_voice() {
2197 let config = SpeechConfig::with_voice("Kore");
2198 assert_eq!(config.voice, Some("Kore".to_string()));
2199 assert_eq!(config.language, None);
2200 assert_eq!(config.speaker, None);
2201 }
2202
2203 #[test]
2204 fn test_speech_config_with_voice_and_language() {
2205 let config = SpeechConfig::with_voice_and_language("Puck", "en-GB");
2206 assert_eq!(config.voice, Some("Puck".to_string()));
2207 assert_eq!(config.language, Some("en-GB".to_string()));
2208 assert_eq!(config.speaker, None);
2209 }
2210
2211 #[test]
2212 fn test_speech_config_serialization() {
2213 let config = SpeechConfig {
2214 voice: Some("Fenrir".to_string()),
2215 language: Some("en-US".to_string()),
2216 speaker: None,
2217 };
2218
2219 let json = serde_json::to_string(&config).expect("Serialization failed");
2220 let value: serde_json::Value = serde_json::from_str(&json).unwrap();
2221
2222 assert_eq!(value["voice"], "Fenrir");
2224 assert_eq!(value["language"], "en-US");
2225 assert!(value.get("speaker").is_none()); assert!(
2231 value.get("voiceConfig").is_none(),
2232 "Should use flat format, not nested voiceConfig"
2233 );
2234 assert!(
2235 value.get("prebuiltVoiceConfig").is_none(),
2236 "Should use flat format, not nested prebuiltVoiceConfig"
2237 );
2238 }
2239
2240 #[test]
2241 fn test_speech_config_roundtrip() {
2242 let config = SpeechConfig {
2243 voice: Some("Aoede".to_string()),
2244 language: Some("es-ES".to_string()),
2245 speaker: Some("narrator".to_string()),
2246 };
2247
2248 let json = serde_json::to_string(&config).expect("Serialization failed");
2249 let parsed: SpeechConfig = serde_json::from_str(&json).expect("Deserialization failed");
2250
2251 assert_eq!(config.voice, parsed.voice);
2252 assert_eq!(config.language, parsed.language);
2253 assert_eq!(config.speaker, parsed.speaker);
2254 }
2255
2256 #[test]
2257 fn test_speech_config_default() {
2258 let config = SpeechConfig::default();
2259 assert_eq!(config.voice, None);
2260 assert_eq!(config.language, None);
2261 assert_eq!(config.speaker, None);
2262 }
2263
2264 #[test]
2269 fn test_image_aspect_ratio_serialization() {
2270 assert_eq!(
2271 serde_json::to_string(&ImageAspectRatio::Square).unwrap(),
2272 "\"1:1\""
2273 );
2274 assert_eq!(
2275 serde_json::to_string(&ImageAspectRatio::Portrait2x3).unwrap(),
2276 "\"2:3\""
2277 );
2278 assert_eq!(
2279 serde_json::to_string(&ImageAspectRatio::Landscape3x2).unwrap(),
2280 "\"3:2\""
2281 );
2282 assert_eq!(
2283 serde_json::to_string(&ImageAspectRatio::Portrait3x4).unwrap(),
2284 "\"3:4\""
2285 );
2286 assert_eq!(
2287 serde_json::to_string(&ImageAspectRatio::Landscape4x3).unwrap(),
2288 "\"4:3\""
2289 );
2290 assert_eq!(
2291 serde_json::to_string(&ImageAspectRatio::Portrait4x5).unwrap(),
2292 "\"4:5\""
2293 );
2294 assert_eq!(
2295 serde_json::to_string(&ImageAspectRatio::Landscape5x4).unwrap(),
2296 "\"5:4\""
2297 );
2298 assert_eq!(
2299 serde_json::to_string(&ImageAspectRatio::Portrait9x16).unwrap(),
2300 "\"9:16\""
2301 );
2302 assert_eq!(
2303 serde_json::to_string(&ImageAspectRatio::Widescreen16x9).unwrap(),
2304 "\"16:9\""
2305 );
2306 assert_eq!(
2307 serde_json::to_string(&ImageAspectRatio::Ultrawide21x9).unwrap(),
2308 "\"21:9\""
2309 );
2310 assert_eq!(
2311 serde_json::to_string(&ImageAspectRatio::Tall1x8).unwrap(),
2312 "\"1:8\""
2313 );
2314 assert_eq!(
2315 serde_json::to_string(&ImageAspectRatio::Wide8x1).unwrap(),
2316 "\"8:1\""
2317 );
2318 assert_eq!(
2319 serde_json::to_string(&ImageAspectRatio::Tall1x4).unwrap(),
2320 "\"1:4\""
2321 );
2322 assert_eq!(
2323 serde_json::to_string(&ImageAspectRatio::Wide4x1).unwrap(),
2324 "\"4:1\""
2325 );
2326 }
2327
2328 #[test]
2329 fn test_image_aspect_ratio_deserialization_roundtrip() {
2330 let ratios = vec![
2331 ("\"1:1\"", ImageAspectRatio::Square),
2332 ("\"2:3\"", ImageAspectRatio::Portrait2x3),
2333 ("\"3:2\"", ImageAspectRatio::Landscape3x2),
2334 ("\"3:4\"", ImageAspectRatio::Portrait3x4),
2335 ("\"4:3\"", ImageAspectRatio::Landscape4x3),
2336 ("\"4:5\"", ImageAspectRatio::Portrait4x5),
2337 ("\"5:4\"", ImageAspectRatio::Landscape5x4),
2338 ("\"9:16\"", ImageAspectRatio::Portrait9x16),
2339 ("\"16:9\"", ImageAspectRatio::Widescreen16x9),
2340 ("\"21:9\"", ImageAspectRatio::Ultrawide21x9),
2341 ("\"1:8\"", ImageAspectRatio::Tall1x8),
2342 ("\"8:1\"", ImageAspectRatio::Wide8x1),
2343 ("\"1:4\"", ImageAspectRatio::Tall1x4),
2344 ("\"4:1\"", ImageAspectRatio::Wide4x1),
2345 ];
2346
2347 for (json, expected) in ratios {
2348 let parsed: ImageAspectRatio = serde_json::from_str(json).unwrap();
2349 assert_eq!(parsed, expected);
2350
2351 let serialized = serde_json::to_string(&parsed).unwrap();
2353 assert_eq!(serialized, json);
2354 }
2355 }
2356
2357 #[test]
2358 fn test_image_aspect_ratio_unknown_roundtrip() {
2359 let unknown: ImageAspectRatio = serde_json::from_str("\"7:3\"").unwrap();
2360 assert!(unknown.is_unknown());
2361 assert_eq!(unknown.unknown_ratio_type(), Some("7:3"));
2362 assert!(unknown.unknown_data().is_some());
2363
2364 let json = serde_json::to_string(&unknown).unwrap();
2366 assert_eq!(json, "\"7:3\"");
2367 }
2368
2369 #[test]
2370 fn test_image_aspect_ratio_known_not_unknown() {
2371 assert!(!ImageAspectRatio::Square.is_unknown());
2372 assert_eq!(ImageAspectRatio::Widescreen16x9.unknown_ratio_type(), None);
2373 assert_eq!(ImageAspectRatio::Portrait2x3.unknown_data(), None);
2374 }
2375
2376 #[test]
2381 fn test_image_size_serialization() {
2382 assert_eq!(serde_json::to_string(&ImageSize::Sd512).unwrap(), "\"512\"");
2383 assert_eq!(serde_json::to_string(&ImageSize::Hd1k).unwrap(), "\"1K\"");
2384 assert_eq!(serde_json::to_string(&ImageSize::Hd2k).unwrap(), "\"2K\"");
2385 assert_eq!(serde_json::to_string(&ImageSize::Uhd4k).unwrap(), "\"4K\"");
2386 }
2387
2388 #[test]
2389 fn test_image_size_deserialization_roundtrip() {
2390 let sizes = vec![
2391 ("\"512\"", ImageSize::Sd512),
2392 ("\"1K\"", ImageSize::Hd1k),
2393 ("\"2K\"", ImageSize::Hd2k),
2394 ("\"4K\"", ImageSize::Uhd4k),
2395 ];
2396
2397 for (json, expected) in sizes {
2398 let parsed: ImageSize = serde_json::from_str(json).unwrap();
2399 assert_eq!(parsed, expected);
2400
2401 let serialized = serde_json::to_string(&parsed).unwrap();
2402 assert_eq!(serialized, json);
2403 }
2404 }
2405
2406 #[test]
2407 fn test_image_size_unknown_roundtrip() {
2408 let unknown: ImageSize = serde_json::from_str("\"8K\"").unwrap();
2409 assert!(unknown.is_unknown());
2410 assert_eq!(unknown.unknown_size_type(), Some("8K"));
2411 assert!(unknown.unknown_data().is_some());
2412
2413 let json = serde_json::to_string(&unknown).unwrap();
2414 assert_eq!(json, "\"8K\"");
2415 }
2416
2417 #[test]
2418 fn test_image_size_known_not_unknown() {
2419 assert!(!ImageSize::Sd512.is_unknown());
2420 assert_eq!(ImageSize::Hd1k.unknown_size_type(), None);
2421 assert_eq!(ImageSize::Uhd4k.unknown_data(), None);
2422 }
2423
2424 #[test]
2429 fn test_image_config_serialization_roundtrip() {
2430 let config = ImageConfig {
2431 aspect_ratio: Some(ImageAspectRatio::Widescreen16x9),
2432 image_size: Some(ImageSize::Hd2k),
2433 };
2434
2435 let json = serde_json::to_string(&config).expect("Serialization failed");
2436 let parsed: ImageConfig = serde_json::from_str(&json).expect("Deserialization failed");
2437
2438 assert_eq!(config, parsed);
2439 }
2440
2441 #[test]
2442 fn test_image_config_default() {
2443 let config = ImageConfig::default();
2444 assert_eq!(config.aspect_ratio, None);
2445 assert_eq!(config.image_size, None);
2446 }
2447
2448 #[test]
2449 fn test_image_config_partial_fields() {
2450 let config = ImageConfig {
2451 aspect_ratio: Some(ImageAspectRatio::Square),
2452 image_size: None,
2453 };
2454
2455 let json = serde_json::to_string(&config).unwrap();
2456 let value: serde_json::Value = serde_json::from_str(&json).unwrap();
2457
2458 assert_eq!(value["aspect_ratio"], "1:1");
2459 assert!(value.get("image_size").is_none());
2460 }
2461
2462 #[test]
2463 fn test_image_config_skip_serializing_none() {
2464 let config = ImageConfig::default();
2465 let json = serde_json::to_string(&config).unwrap();
2466 assert_eq!(json, "{}");
2467 }
2468
2469 #[test]
2470 fn test_generation_config_with_image_config() {
2471 let config = GenerationConfig {
2472 image_config: Some(ImageConfig {
2473 aspect_ratio: Some(ImageAspectRatio::Portrait9x16),
2474 image_size: Some(ImageSize::Uhd4k),
2475 }),
2476 ..Default::default()
2477 };
2478
2479 let json = serde_json::to_string(&config).expect("Serialization failed");
2480 let value: serde_json::Value = serde_json::from_str(&json).unwrap();
2481
2482 assert_eq!(value["image_config"]["aspect_ratio"], "9:16");
2483 assert_eq!(value["image_config"]["image_size"], "4K");
2484 }
2485
2486 #[test]
2491 fn test_generation_config_tool_choice_mode_serializes_lowercase() {
2492 let config = GenerationConfig {
2493 tool_choice: Some(ToolChoice::Mode(crate::tools::FunctionCallingMode::Any)),
2494 ..Default::default()
2495 };
2496
2497 let value = serde_json::to_value(&config).unwrap();
2498 assert_eq!(value["tool_choice"], "any");
2499 }
2500
2501 #[test]
2502 fn test_generation_config_tool_choice_allowed_tools_object() {
2503 let config = GenerationConfig {
2504 tool_choice: Some(ToolChoice::allowed_tools(
2505 Some(crate::tools::FunctionCallingMode::Any),
2506 vec!["get_weather".to_string(), "get_time".to_string()],
2507 )),
2508 ..Default::default()
2509 };
2510
2511 let value = serde_json::to_value(&config).unwrap();
2512 assert_eq!(value["tool_choice"]["allowed_tools"]["mode"], "any");
2513 assert_eq!(
2514 value["tool_choice"]["allowed_tools"]["tools"][0],
2515 "get_weather"
2516 );
2517 assert!(
2518 value.get("allowed_tools").is_none(),
2519 "top-level allowed_tools was removed from generation_config"
2520 );
2521 }
2522
2523 #[test]
2524 fn test_generation_config_penalties_serialize() {
2525 let config = GenerationConfig {
2526 presence_penalty: Some(0.5),
2527 frequency_penalty: Some(-0.5),
2528 ..Default::default()
2529 };
2530 let value = serde_json::to_value(&config).unwrap();
2531 assert_eq!(value["presence_penalty"], 0.5);
2532 assert_eq!(value["frequency_penalty"], -0.5);
2533 }
2534
2535 #[test]
2536 fn test_generation_config_has_no_top_k() {
2537 let config = GenerationConfig {
2539 temperature: Some(0.3),
2540 ..Default::default()
2541 };
2542 let value = serde_json::to_value(&config).unwrap();
2543 assert!(value.get("top_k").is_none());
2544 }
2545
2546 #[test]
2551 fn test_service_tier_roundtrip() {
2552 for (tier, wire) in [
2553 (ServiceTier::Flex, "\"flex\""),
2554 (ServiceTier::Standard, "\"standard\""),
2555 (ServiceTier::Priority, "\"priority\""),
2556 ] {
2557 assert_eq!(serde_json::to_string(&tier).unwrap(), wire);
2558 let parsed: ServiceTier = serde_json::from_str(wire).unwrap();
2559 assert_eq!(parsed, tier);
2560 }
2561 }
2562
2563 #[test]
2564 fn test_service_tier_unknown_roundtrip() {
2565 let unknown: ServiceTier = serde_json::from_str("\"turbo\"").unwrap();
2566 assert!(unknown.is_unknown());
2567 assert_eq!(unknown.unknown_tier_type(), Some("turbo"));
2568 assert!(unknown.unknown_data().is_some());
2569 assert_eq!(serde_json::to_string(&unknown).unwrap(), "\"turbo\"");
2570 }
2571
2572 #[test]
2577 fn test_interaction_input_text_roundtrip() {
2578 let input = InteractionInput::Text("Hello".into());
2579 let json = serde_json::to_string(&input).unwrap();
2580 assert_eq!(json, "\"Hello\"");
2581 let back: InteractionInput = serde_json::from_str(&json).unwrap();
2582 assert!(matches!(back, InteractionInput::Text(t) if t == "Hello"));
2583 }
2584
2585 #[test]
2586 fn test_interaction_input_content_array_roundtrip() {
2587 let json = r#"[{"type":"text","text":"hi"},{"type":"image","uri":"files/x","mime_type":"image/png"}]"#;
2588 let input: InteractionInput = serde_json::from_str(json).unwrap();
2589 match &input {
2590 InteractionInput::Content(c) => assert_eq!(c.len(), 2),
2591 other => panic!("Expected Content, got {other:?}"),
2592 }
2593 }
2594
2595 #[test]
2596 fn test_interaction_input_steps_array_roundtrip() {
2597 let json = r#"[
2598 {"type":"user_input","content":[{"type":"text","text":"hi"}]},
2599 {"type":"model_output","content":[{"type":"text","text":"hello"}]},
2600 {"type":"function_result","call_id":"c1","result":"done"}
2601 ]"#;
2602 let input: InteractionInput = serde_json::from_str(json).unwrap();
2603 match &input {
2604 InteractionInput::Steps(s) => assert_eq!(s.len(), 3),
2605 other => panic!("Expected Steps, got {other:?}"),
2606 }
2607 }
2608
2609 #[test]
2610 fn test_interaction_input_single_content_object() {
2611 let json = r#"{"type":"text","text":"hi"}"#;
2612 let input: InteractionInput = serde_json::from_str(json).unwrap();
2613 match &input {
2614 InteractionInput::Content(c) => assert_eq!(c.len(), 1),
2615 other => panic!("Expected Content, got {other:?}"),
2616 }
2617 }
2618}