perf(google): avoid accumulating thoughtSignatures across conversation history (#6462)

Signed-off-by: rabi <ramishra@redhat.com>
This commit is contained in:
Rabi Mishra
2026-01-14 19:06:41 +05:30
committed by GitHub
parent a66e5d306a
commit 7b59f554d7
+58 -16
View File
@@ -31,7 +31,7 @@ pub fn get_thought_signature(metadata: &Option<ProviderMetadata>) -> Option<&str
/// Convert internal Message format to Google's API message specification
pub fn format_messages(messages: &[Message]) -> Vec<Value> {
messages
let filtered: Vec<_> = messages
.iter()
.filter(|m| m.is_agent_visible())
.filter(|message| {
@@ -42,12 +42,28 @@ pub fn format_messages(messages: &[Message]) -> Vec<Value> {
)
})
})
.map(|message| {
.collect();
let last_assistant_idx = filtered
.iter()
.enumerate()
.filter(|(_, m)| m.role != Role::User)
.map(|(i, _)| i)
.next_back();
filtered
.iter()
.enumerate()
.map(|(idx, message)| {
let role = if message.role == Role::User {
"user"
} else {
"model"
};
let include_signature = match last_assistant_idx {
Some(last_idx) => idx >= last_idx,
None => false,
};
let mut parts = Vec::new();
for message_content in message.content.iter() {
match message_content {
@@ -74,8 +90,13 @@ pub fn format_messages(messages: &[Message]) -> Vec<Value> {
let mut part = Map::new();
part.insert("functionCall".to_string(), json!(function_call_part));
if let Some(signature) = get_thought_signature(&request.metadata) {
part.insert(THOUGHT_SIGNATURE_KEY.to_string(), json!(signature));
if include_signature {
if let Some(signature) = get_thought_signature(&request.metadata) {
part.insert(
THOUGHT_SIGNATURE_KEY.to_string(),
json!(signature),
);
}
}
parts.push(json!(part));
@@ -144,11 +165,15 @@ pub fn format_messages(messages: &[Message]) -> Vec<Value> {
"functionResponse".to_string(),
json!(function_response),
);
if let Some(signature) = get_thought_signature(&response.metadata) {
part.insert(
THOUGHT_SIGNATURE_KEY.to_string(),
json!(signature),
);
if include_signature {
if let Some(signature) =
get_thought_signature(&response.metadata)
{
part.insert(
THOUGHT_SIGNATURE_KEY.to_string(),
json!(signature),
);
}
}
parts.push(json!(part));
}
@@ -164,11 +189,15 @@ pub fn format_messages(messages: &[Message]) -> Vec<Value> {
"functionResponse".to_string(),
json!(function_response),
);
if let Some(signature) = get_thought_signature(&response.metadata) {
part.insert(
THOUGHT_SIGNATURE_KEY.to_string(),
json!(signature),
);
if include_signature {
if let Some(signature) =
get_thought_signature(&response.metadata)
{
part.insert(
THOUGHT_SIGNATURE_KEY.to_string(),
json!(signature),
);
}
}
parts.push(json!(part));
}
@@ -177,7 +206,9 @@ pub fn format_messages(messages: &[Message]) -> Vec<Value> {
MessageContent::Thinking(thinking) => {
let mut part = Map::new();
part.insert("text".to_string(), json!(thinking.thinking));
part.insert("thoughtSignature".to_string(), json!(thinking.signature));
if include_signature {
part.insert("thoughtSignature".to_string(), json!(thinking.signature));
}
parts.push(json!(part));
}
@@ -1230,10 +1261,21 @@ mod tests {
Ok(tool_result("output")),
req1.metadata.as_ref(),
);
let google_out = format_messages(&[native, tool_response]);
let google_out = format_messages(&[native.clone(), tool_response.clone()]);
assert_eq!(google_out[0]["parts"][0]["thoughtSignature"], SIG);
assert_eq!(google_out[1]["parts"][0]["thoughtSignature"], SIG);
let second_assistant =
Message::assistant().with_thinking("More thinking".to_string(), "sig_456".to_string());
let google_multi = format_messages(&[native, tool_response, second_assistant]);
assert!(google_multi[0]["parts"][0]
.get("thoughtSignature")
.is_none());
assert!(google_multi[1]["parts"][0]
.get("thoughtSignature")
.is_none());
assert_eq!(google_multi[2]["parts"][0]["thoughtSignature"], "sig_456");
// Text-only response WITH signature but WITHOUT function calls should be regular text
// (per original behavior: thinking is only when reasoning before tool calls)
let final_response_with_sig =