Skip subagents for gemini (#5257)
Co-authored-by: Douwe Osinga <douwe@squareup.com>
This commit is contained in:
@@ -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 = [
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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?;
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user