fix: support Gemini 3's thought signatures (#5806)
Signed-off-by: Salvatore Testa <sal@withpersona.com>
This commit is contained in:
@@ -53,6 +53,8 @@ pub struct ToolRequest {
|
||||
#[serde(with = "tool_result_serde")]
|
||||
#[schema(value_type = Object)]
|
||||
pub tool_call: ToolResult<CallToolRequestParam>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub thought_signature: Option<String>,
|
||||
}
|
||||
|
||||
impl ToolRequest {
|
||||
@@ -201,6 +203,19 @@ impl MessageContent {
|
||||
MessageContent::ToolRequest(ToolRequest {
|
||||
id: id.into(),
|
||||
tool_call,
|
||||
thought_signature: None,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn tool_request_with_signature<S1: Into<String>, S2: Into<String>>(
|
||||
id: S1,
|
||||
tool_call: ToolResult<CallToolRequestParam>,
|
||||
thought_signature: Option<S2>,
|
||||
) -> Self {
|
||||
MessageContent::ToolRequest(ToolRequest {
|
||||
id: id.into(),
|
||||
tool_call,
|
||||
thought_signature: thought_signature.map(|s| s.into()),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -53,9 +53,14 @@ pub fn format_messages(messages: &[Message]) -> Vec<Value> {
|
||||
}
|
||||
}
|
||||
|
||||
parts.push(json!({
|
||||
"functionCall": function_call_part
|
||||
}));
|
||||
let mut part = Map::new();
|
||||
part.insert("functionCall".to_string(), json!(function_call_part));
|
||||
|
||||
if let Some(signature) = &request.thought_signature {
|
||||
part.insert("thoughtSignature".to_string(), json!(signature));
|
||||
}
|
||||
|
||||
parts.push(json!(part));
|
||||
}
|
||||
Err(e) => {
|
||||
parts.push(json!({"text":format!("Error: {}", e)}));
|
||||
@@ -121,6 +126,12 @@ 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));
|
||||
parts.push(json!(part));
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
@@ -269,8 +280,17 @@ pub fn response_to_message(response: Value) -> Result<Message> {
|
||||
.unwrap_or(&binding);
|
||||
|
||||
for part in parts {
|
||||
let thought_signature = part
|
||||
.get("thoughtSignature")
|
||||
.and_then(|v| v.as_str())
|
||||
.map(|s| s.to_string());
|
||||
|
||||
if let Some(text) = part.get("text").and_then(|v| v.as_str()) {
|
||||
content.push(MessageContent::text(text.to_string()));
|
||||
if let Some(sig) = thought_signature {
|
||||
content.push(MessageContent::thinking(text.to_string(), sig));
|
||||
} else {
|
||||
content.push(MessageContent::text(text.to_string()));
|
||||
}
|
||||
} else if let Some(function_call) = part.get("functionCall") {
|
||||
let id: String = rand::thread_rng()
|
||||
.sample_iter(&Alphanumeric)
|
||||
@@ -294,12 +314,13 @@ pub fn response_to_message(response: Value) -> Result<Message> {
|
||||
} else {
|
||||
let parameters = function_call.get("args");
|
||||
if let Some(params) = parameters {
|
||||
content.push(MessageContent::tool_request(
|
||||
content.push(MessageContent::tool_request_with_signature(
|
||||
id,
|
||||
Ok(CallToolRequestParam {
|
||||
name: name.into(),
|
||||
arguments: Some(object(params.clone())),
|
||||
}),
|
||||
thought_signature,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ pub const GOOGLE_KNOWN_MODELS: &[&str] = &[
|
||||
"gemini-2.0-flash-exp",
|
||||
"gemini-2.0-flash-preview-image-generation",
|
||||
"gemini-2.0-flash-lite",
|
||||
"gemini-3-pro-preview",
|
||||
];
|
||||
|
||||
pub const GOOGLE_DOC_URL: &str = "https://ai.google.dev/gemini-api/docs/models";
|
||||
|
||||
@@ -113,6 +113,7 @@ mod tests {
|
||||
name: "shell".into(),
|
||||
arguments: Some(object!({"command": "rm -rf /"})),
|
||||
}),
|
||||
thought_signature: None,
|
||||
}];
|
||||
|
||||
let results = inspector.inspect(&tool_requests, &[]).await.unwrap();
|
||||
|
||||
@@ -295,6 +295,7 @@ mod tests {
|
||||
name: "test_tool".into(),
|
||||
arguments: Some(object!({})),
|
||||
}),
|
||||
thought_signature: None,
|
||||
};
|
||||
|
||||
let permission_result = PermissionCheckResult {
|
||||
|
||||
Reference in New Issue
Block a user