Skip subagents for gemini (#5257)

Co-authored-by: Douwe Osinga <douwe@squareup.com>
This commit is contained in:
Douwe Osinga
2025-10-18 17:35:29 -04:00
committed by GitHub
parent 890393bb68
commit 64b37339e0
12 changed files with 156 additions and 298 deletions
-1
View File
@@ -46,7 +46,6 @@ pub const DATABRICKS_KNOWN_MODELS: &[&str] = &[
"databricks-meta-llama-3-3-70b-instruct",
"databricks-meta-llama-3-1-405b-instruct",
"databricks-dbrx-instruct",
"databricks-mixtral-8x7b-instruct",
];
pub const DATABRICKS_DOC_URL: &str =
@@ -264,23 +264,31 @@ fn format_messages(messages: &[Message], image_format: &ImageFormat) -> Vec<Data
result
}
/// Convert internal Tool format to OpenAI's API tool specification
pub fn format_tools(tools: &[Tool]) -> anyhow::Result<Vec<Value>> {
pub fn format_tools(tools: &[Tool], model_name: &str) -> anyhow::Result<Vec<Value>> {
let mut tool_names = std::collections::HashSet::new();
let mut result = Vec::new();
let is_gemini = model_name.starts_with("gemini");
for tool in tools {
if !tool_names.insert(&tool.name) {
return Err(anyhow!("Duplicate tool name: {}", tool.name));
}
let parameters = if is_gemini {
let mut cleaned_schema = tool.input_schema.as_ref().clone();
cleaned_schema.remove("$schema");
json!(cleaned_schema)
} else {
json!(tool.input_schema)
};
result.push(json!({
"type": "function",
"function": {
"name": tool.name,
// do not silently truncate description
"description": tool.description,
"parameters": tool.input_schema,
"parameters": parameters,
}
}));
}
@@ -544,7 +552,7 @@ pub fn create_request(
let messages_spec = format_messages(messages, image_format);
let mut tools_spec = if !tools.is_empty() {
format_tools(tools)?
format_tools(tools, &model_config.model_name)?
} else {
vec![]
};
@@ -639,82 +647,6 @@ mod tests {
use rmcp::object;
use serde_json::json;
#[test]
fn test_validate_tool_schemas() {
// Test case 1: Empty parameters object
// Input JSON with an incomplete parameters object
let mut actual = vec![json!({
"type": "function",
"function": {
"name": "test_func",
"description": "test description",
"parameters": {
"type": "object"
}
}
})];
// Run the function to validate and update schemas
validate_tool_schemas(&mut actual);
// Expected JSON after validation
let expected = vec![json!({
"type": "function",
"function": {
"name": "test_func",
"description": "test description",
"parameters": {
"type": "object",
"properties": {},
"required": []
}
}
})];
// Compare entire JSON structures instead of individual fields
assert_eq!(actual, expected);
// Test case 2: Missing type field
let mut tools = vec![json!({
"type": "function",
"function": {
"name": "test_func",
"description": "test description",
"parameters": {
"properties": {}
}
}
})];
validate_tool_schemas(&mut tools);
let params = tools[0]["function"]["parameters"].as_object().unwrap();
assert_eq!(params["type"], "object");
// Test case 3: Complete valid schema should remain unchanged
let original_schema = json!({
"type": "function",
"function": {
"name": "test_func",
"description": "test description",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "City and country"
}
},
"required": ["location"]
}
}
});
let mut tools = vec![original_schema.clone()];
validate_tool_schemas(&mut tools);
assert_eq!(tools[0], original_schema);
}
const OPENAI_TOOL_USE_RESPONSE: &str = r#"{
"choices": [{
"role": "assistant",
@@ -752,6 +684,7 @@ mod tests {
"test_tool",
"A test tool",
object!({
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"input": {
@@ -763,11 +696,16 @@ mod tests {
}),
);
let spec = format_tools(&[tool])?;
let spec = format_tools(&[tool.clone()], "gpt-4o")?;
assert_eq!(
spec[0]["function"]["parameters"]["$schema"],
"http://json-schema.org/draft-07/schema#"
);
let spec = format_tools(&[tool], "gemini-2-5-flash")?;
assert!(spec[0]["function"]["parameters"].get("$schema").is_none());
assert_eq!(spec[0]["function"]["parameters"]["type"], "object");
assert_eq!(spec.len(), 1);
assert_eq!(spec[0]["type"], "function");
assert_eq!(spec[0]["function"]["name"], "test_tool");
Ok(())
}
@@ -785,7 +723,6 @@ mod tests {
),
];
// Get the ID from the tool request to use in the response
let tool_id = if let MessageContent::ToolRequest(request) = &messages[2].content[0] {
&request.id
} else {
@@ -823,7 +760,6 @@ mod tests {
}),
)];
// Get the ID from the tool request to use in the response
let tool_id = if let MessageContent::ToolRequest(request) = &messages[0].content[0] {
&request.id
} else {
@@ -879,7 +815,7 @@ mod tests {
}),
);
let result = format_tools(&[tool1, tool2]);
let result = format_tools(&[tool1, tool2], "gpt-4o");
assert!(result.is_err());
assert!(result
.unwrap_err()
@@ -889,16 +825,8 @@ mod tests {
Ok(())
}
#[test]
fn test_format_tools_empty() -> anyhow::Result<()> {
let spec = format_tools(&[])?;
assert!(spec.is_empty());
Ok(())
}
#[test]
fn test_format_messages_with_image_path() -> anyhow::Result<()> {
// Create a temporary PNG file with valid PNG magic numbers
let temp_dir = tempfile::tempdir()?;
let png_path = temp_dir.path().join("test.png");
let png_data = [
+1 -2
View File
@@ -130,7 +130,6 @@ pub fn format_messages(messages: &[Message]) -> Vec<Value> {
.collect()
}
/// Convert internal Tool format to Google's API tool specification
pub fn format_tools(tools: &[Tool]) -> Vec<Value> {
tools
.iter()
@@ -139,7 +138,7 @@ pub fn format_tools(tools: &[Tool]) -> Vec<Value> {
parameters.insert("name".to_string(), json!(tool.name));
parameters.insert("description".to_string(), json!(tool.description));
let tool_input_schema = &tool.input_schema;
// Only add the parameters key if the tool schema has non-empty properties.
if tool_input_schema
.get("properties")
.and_then(|v| v.as_object())
+2 -16
View File
@@ -13,10 +13,9 @@ use rmcp::model::Tool;
use serde_json::Value;
pub const GOOGLE_API_HOST: &str = "https://generativelanguage.googleapis.com";
pub const GOOGLE_DEFAULT_MODEL: &str = "gemini-2.5-flash";
pub const GOOGLE_DEFAULT_FAST_MODEL: &str = "gemini-1.5-flash";
pub const GOOGLE_DEFAULT_MODEL: &str = "gemini-2.5-pro";
pub const GOOGLE_DEFAULT_FAST_MODEL: &str = "gemini-2.5-flash";
pub const GOOGLE_KNOWN_MODELS: &[&str] = &[
// Gemini 2.5 models (latest generation)
"gemini-2.5-pro",
"gemini-2.5-pro-preview-06-05",
"gemini-2.5-pro-preview-05-06",
@@ -27,20 +26,10 @@ pub const GOOGLE_KNOWN_MODELS: &[&str] = &[
"gemini-2.5-flash-exp-native-audio-thinking-dialog",
"gemini-2.5-flash-preview-tts",
"gemini-2.5-pro-preview-tts",
// Gemini 2.0 models
"gemini-2.0-flash",
"gemini-2.0-flash-exp",
"gemini-2.0-flash-preview-image-generation",
"gemini-2.0-flash-lite",
// Gemini 1.5 models
"gemini-1.5-flash",
"gemini-1.5-flash-latest",
"gemini-1.5-flash-002",
"gemini-1.5-flash-8b",
"gemini-1.5-flash-8b-latest",
"gemini-1.5-pro",
"gemini-1.5-pro-latest",
"gemini-1.5-pro-002",
];
pub const GOOGLE_DOC_URL: &str = "https://ai.google.dev/gemini-api/docs/models";
@@ -115,7 +104,6 @@ impl Provider for GoogleProvider {
let payload = create_request(model_config, system, messages, tools)?;
let mut log = RequestLog::start(model_config, &payload)?;
// Make request
let response = self
.with_retry(|| async {
let payload_clone = payload.clone();
@@ -123,7 +111,6 @@ impl Provider for GoogleProvider {
})
.await?;
// Parse response
let message = response_to_message(unescape_json_values(&response))?;
let usage = get_usage(&response)?;
let response_model = match response.get("modelVersion") {
@@ -135,7 +122,6 @@ impl Provider for GoogleProvider {
Ok((message, provider_usage))
}
/// Fetch supported models from Google Generative Language API; returns Err on failure, Ok(None) if not present
async fn fetch_supported_models(&self) -> Result<Option<Vec<String>>, ProviderError> {
let response = self.api_client.response_get("v1beta/models").await?;
let json: serde_json::Value = response.json().await?;
+2 -2
View File
@@ -17,7 +17,7 @@ use crate::providers::formats::openai::{create_request, get_usage, response_to_m
use rmcp::model::Tool;
pub const OPENROUTER_DEFAULT_MODEL: &str = "anthropic/claude-sonnet-4";
pub const OPENROUTER_DEFAULT_FAST_MODEL: &str = "google/gemini-flash-1.5";
pub const OPENROUTER_DEFAULT_FAST_MODEL: &str = "google/gemini-flash-2.5";
pub const OPENROUTER_MODEL_PREFIX_ANTHROPIC: &str = "anthropic";
// OpenRouter can run many models, we suggest the default
@@ -28,7 +28,7 @@ pub const OPENROUTER_KNOWN_MODELS: &[&str] = &[
"anthropic/claude-opus-4",
"anthropic/claude-3.7-sonnet",
"google/gemini-2.5-pro",
"google/gemini-flash-1.5",
"google/gemini-flash-2.5",
"deepseek/deepseek-r1-0528",
"qwen/qwen3-coder",
"moonshotai/kimi-k2",