fix: support response parts with extra fields like thoughtSignature
Some checks failed
Rust / build (push) Has been cancelled

Implement custom Deserialize for Part to tolerate additional response fields (such as thoughtSignature or thought) returned by thinking models.
This commit is contained in:
2026-09-06 13:13:49 +01:00
parent f9c50a3f45
commit ad82fc712b
2 changed files with 94 additions and 1 deletions

View File

@@ -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<String, String>,
},
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct PartHelper {
text: Option<String>,
inline_data: Option<InlineDataHelper>,
file_data: Option<FileDataHelper>,
function_call: Option<FunctionCallHelper>,
}
#[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<String, String>,
}
impl<'de> Deserialize<'de> for Part {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
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",
))
}
}

View File

@@ -531,4 +531,34 @@ mod tests {
}"#;
serde_json::from_str::<GenerateContentResponse>(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::<GenerateContentResponse>(input).unwrap();
}
}