mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-04 16:37:46 +08:00
fix(formats): enable mixed Gemini tool calls
This commit is contained in:
@@ -187,9 +187,60 @@ pub fn to_raw(
|
|||||||
) -> Option<Value> {
|
) -> Option<Value> {
|
||||||
let mut output = canonical_to_gemini_request_body(canonical, mapped_model, upstream_is_stream)?;
|
let mut output = canonical_to_gemini_request_body(canonical, mapped_model, upstream_is_stream)?;
|
||||||
apply_gemini_request_extensions(&mut output, &canonical.extensions)?;
|
apply_gemini_request_extensions(&mut output, &canonical.extensions)?;
|
||||||
|
if !canonical_has_raw_gemini_tools(canonical) {
|
||||||
|
enable_server_side_tool_invocations_for_mixed_tools(&mut output)?;
|
||||||
|
}
|
||||||
Some(output)
|
Some(output)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn canonical_has_raw_gemini_tools(canonical: &CanonicalRequest) -> bool {
|
||||||
|
canonical
|
||||||
|
.extensions
|
||||||
|
.get("gemini")
|
||||||
|
.and_then(Value::as_object)
|
||||||
|
.is_some_and(|gemini| gemini.contains_key("raw_tools"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn enable_server_side_tool_invocations_for_mixed_tools(output: &mut Value) -> Option<()> {
|
||||||
|
let output_object = output.as_object_mut()?;
|
||||||
|
let tools = output_object.get("tools").and_then(Value::as_array);
|
||||||
|
let Some(tools) = tools else {
|
||||||
|
return Some(());
|
||||||
|
};
|
||||||
|
let has_function_declarations = tools.iter().any(|tool| {
|
||||||
|
tool.as_object().is_some_and(|tool| {
|
||||||
|
tool.get("functionDeclarations")
|
||||||
|
.or_else(|| tool.get("function_declarations"))
|
||||||
|
.and_then(Value::as_array)
|
||||||
|
.is_some_and(|declarations| !declarations.is_empty())
|
||||||
|
})
|
||||||
|
});
|
||||||
|
let has_builtin_tools = tools.iter().any(|tool| {
|
||||||
|
tool.as_object().is_some_and(|tool| {
|
||||||
|
tool.keys().any(|key| {
|
||||||
|
!matches!(
|
||||||
|
key.as_str(),
|
||||||
|
"functionDeclarations" | "function_declarations"
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
});
|
||||||
|
if !has_function_declarations || !has_builtin_tools {
|
||||||
|
return Some(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let tool_config = output_object
|
||||||
|
.entry("toolConfig".to_string())
|
||||||
|
.or_insert_with(|| Value::Object(Map::new()))
|
||||||
|
.as_object_mut()?;
|
||||||
|
tool_config.remove("include_server_side_tool_invocations");
|
||||||
|
tool_config.insert(
|
||||||
|
"includeServerSideToolInvocations".to_string(),
|
||||||
|
Value::Bool(true),
|
||||||
|
);
|
||||||
|
Some(())
|
||||||
|
}
|
||||||
|
|
||||||
fn canonical_to_gemini_request_body(
|
fn canonical_to_gemini_request_body(
|
||||||
canonical: &CanonicalRequest,
|
canonical: &CanonicalRequest,
|
||||||
mapped_model: &str,
|
mapped_model: &str,
|
||||||
|
|||||||
@@ -2029,4 +2029,52 @@ mod tests {
|
|||||||
"surface conversion should preserve the Claude tool schema before transport envelopes"
|
"surface conversion should preserve the Claude tool schema before transport envelopes"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn openai_responses_builtin_and_function_tools_enable_gemini_server_invocations() {
|
||||||
|
let request = json!({
|
||||||
|
"model": "gpt-5",
|
||||||
|
"input": "Search first, then save the result.",
|
||||||
|
"tools": [
|
||||||
|
{"type": "web_search_preview"},
|
||||||
|
{
|
||||||
|
"type": "function",
|
||||||
|
"name": "save_result",
|
||||||
|
"description": "Save a search result",
|
||||||
|
"parameters": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"result": {"type": "string"}
|
||||||
|
},
|
||||||
|
"required": ["result"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"tool_choice": "required"
|
||||||
|
});
|
||||||
|
|
||||||
|
let gemini = build_standard_request_body(
|
||||||
|
&request,
|
||||||
|
"openai:responses",
|
||||||
|
"gemini-2.5-pro",
|
||||||
|
"google",
|
||||||
|
"gemini:generate_content",
|
||||||
|
"/v1/responses",
|
||||||
|
true,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.expect("openai responses should convert to gemini generate content");
|
||||||
|
|
||||||
|
assert_eq!(gemini["tools"][0]["googleSearch"], json!({}));
|
||||||
|
assert_eq!(
|
||||||
|
gemini["tools"][1]["functionDeclarations"][0]["name"],
|
||||||
|
"save_result"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
gemini["toolConfig"]["includeServerSideToolInvocations"],
|
||||||
|
true
|
||||||
|
);
|
||||||
|
assert_eq!(gemini["toolConfig"]["functionCallingConfig"]["mode"], "ANY");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user