diff --git a/src/types/common.rs b/src/types/common.rs index f17d522..e5ed019 100644 --- a/src/types/common.rs +++ b/src/types/common.rs @@ -83,7 +83,7 @@ impl FromStr for Role { } } -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize)] #[serde(rename_all = "camelCase")] pub enum Part { Text(String), @@ -100,3 +100,66 @@ pub enum Part { args: HashMap, }, } + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +struct PartHelper { + text: Option, + inline_data: Option, + file_data: Option, + function_call: Option, +} + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +struct InlineDataHelper { + mime_type: String, + data: String, +} + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +struct FileDataHelper { + mime_type: String, + file_uri: String, +} + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +struct FunctionCallHelper { + name: String, + args: HashMap, +} + +impl<'de> Deserialize<'de> for Part { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let helper = PartHelper::deserialize(deserializer)?; + if let Some(text) = helper.text { + return Ok(Part::Text(text)); + } + if let Some(inline_data) = helper.inline_data { + return Ok(Part::InlineData { + mime_type: inline_data.mime_type, + data: inline_data.data, + }); + } + if let Some(file_data) = helper.file_data { + return Ok(Part::FileData { + mime_type: file_data.mime_type, + file_uri: file_data.file_uri, + }); + } + if let Some(function_call) = helper.function_call { + return Ok(Part::FunctionCall { + name: function_call.name, + args: function_call.args, + }); + } + Err(serde::de::Error::custom( + "Part does not contain any recognizable variant", + )) + } +} diff --git a/src/types/generate_content.rs b/src/types/generate_content.rs index cfbc92e..ada928d 100644 --- a/src/types/generate_content.rs +++ b/src/types/generate_content.rs @@ -531,4 +531,34 @@ mod tests { }"#; serde_json::from_str::(input).unwrap(); } + + #[test] + fn parses_response_with_thought_signature() { + let input = r#"{ + "candidates": [ + { + "content": { + "role": "model", + "parts": [ + { + "text": "Hello world", + "thoughtSignature": "AY89a1+twFsQ+oYDReN5PU76yp0ciDinNh3MgyCbe/BLvyh93lje7qHOCJuNxaPULnqmgvKvtuQnjyf2wwD20Dl2rWnxbdZZAeqhRJEdvKOc4LQ=" + } + ] + }, + "finishReason": "STOP" + } + ], + "usageMetadata": { + "promptTokenCount": 877, + "candidatesTokenCount": 547, + "totalTokenCount": 1424, + "trafficType": "ON_DEMAND" + }, + "modelVersion": "gemini-3.5-flash-lite", + "createTime": "2026-09-06T10:36:16.330983Z", + "responseId": "IEKdaueZFKyrz_IPp8Sc4QQ" + }"#; + serde_json::from_str::(input).unwrap(); + } }