diff --git a/crates/goose/src/providers/formats/google.rs b/crates/goose/src/providers/formats/google.rs index 35dea95a483f..7fd1dd075197 100644 --- a/crates/goose/src/providers/formats/google.rs +++ b/crates/goose/src/providers/formats/google.rs @@ -160,7 +160,6 @@ pub fn get_accepted_keys(parent_key: Option<&str>) -> Vec<&str> { "anyOf", "allOf", "type", - // "format", // Google's APIs don't support this well "description", "nullable", "enum", @@ -169,26 +168,37 @@ pub fn get_accepted_keys(parent_key: Option<&str>) -> Vec<&str> { "items", ], Some("items") => vec!["type", "properties", "items", "required"], - // This is the top-level schema. _ => vec!["type", "properties", "required", "anyOf", "allOf"], } } +pub fn process_value(value: &Value, parent_key: Option<&str>) -> Value { + match value { + Value::Object(map) => process_map(map, parent_key), + Value::Array(arr) if parent_key == Some("type") => arr + .iter() + .find(|v| v.as_str() != Some("null")) + .cloned() + .unwrap_or_else(|| json!("string")), + _ => value.clone(), + } +} + /// Process a JSON map to filter out unsupported attributes, mirroring the logic /// from the official Google Gemini CLI. /// See: https://github.com/google-gemini/gemini-cli/blob/8a6509ffeba271a8e7ccb83066a9a31a5d72a647/packages/core/src/tools/tool-registry.ts#L356 pub fn process_map(map: &Map, parent_key: Option<&str>) -> Value { let accepted_keys = get_accepted_keys(parent_key); + let filtered_map: Map = map .iter() .filter_map(|(key, value)| { if !accepted_keys.contains(&key.as_str()) { - return None; // Skip if key is not accepted + return None; } - match key.as_str() { + let processed_value = match key.as_str() { "properties" => { - // Process each property within the properties object if let Some(nested_map) = value.as_object() { let processed_properties: Map = nested_map .iter() @@ -200,29 +210,44 @@ pub fn process_map(map: &Map, parent_key: Option<&str>) -> Value } }) .collect(); - Some((key.clone(), Value::Object(processed_properties))) + Value::Object(processed_properties) } else { - None + value.clone() } } "items" => { - // If it's a nested structure, recurse if it's an object. - value.as_object().map(|nested_map| { - (key.clone(), process_map(nested_map, Some(key.as_str()))) - }) + if let Some(items_map) = value.as_object() { + process_map(items_map, Some("items")) + } else { + value.clone() + } } - _ => { - // For other accepted keys, just clone the value. - Some((key.clone(), value.clone())) + "anyOf" | "allOf" => { + if let Some(arr) = value.as_array() { + let processed_arr: Vec = arr + .iter() + .map(|item| { + item.as_object().map_or_else( + || item.clone(), + |obj| process_map(obj, parent_key), + ) + }) + .collect(); + Value::Array(processed_arr) + } else { + value.clone() + } } - } + _ => value.clone(), + }; + + Some((key.clone(), processed_value)) }) .collect(); Value::Object(filtered_map) } -/// Convert Google's API response to internal Message format pub fn response_to_message(response: Value) -> Result { let mut content = Vec::new(); let binding = vec![]; diff --git a/crates/goose/src/providers/openrouter.rs b/crates/goose/src/providers/openrouter.rs index f890e87b704e..f9acb72152f6 100644 --- a/crates/goose/src/providers/openrouter.rs +++ b/crates/goose/src/providers/openrouter.rs @@ -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-2.5", + "google/gemini-2.5-flash", "deepseek/deepseek-r1-0528", "qwen/qwen3-coder", "moonshotai/kimi-k2", diff --git a/scripts/test_providers.sh b/scripts/test_providers.sh index e0b5d82a45f9..078b51553bba 100755 --- a/scripts/test_providers.sh +++ b/scripts/test_providers.sh @@ -15,10 +15,10 @@ fi SCRIPT_DIR=$(pwd) PROVIDERS=( - "openrouter:anthropic/claude-sonnet-4.5:qwen/qwen3-coder" - "openai:gpt-4o:gpt-4o-mini:gpt-3.5-turbo" + "openrouter:google/gemini-2.5-pro:google/gemini-2.5-flash:anthropic/claude-sonnet-4.5:qwen/qwen3-coder" + "openai:gpt-4o:gpt-4o-mini:gpt-3.5-turbo:gpt-5" "anthropic:claude-sonnet-4-5-20250929:claude-opus-4-1-20250805" - "google:gemini-2.5-pro:gemini-2.5-pro:gemini-2.5-flash" + "google:gemini-2.5-pro:gemini-2.5-flash" ) # In CI, only run Databricks tests if DATABRICKS_HOST and DATABRICKS_TOKEN are set @@ -50,14 +50,14 @@ for provider_config in "${PROVIDERS[@]}"; do echo "Model: ${MODEL}" echo "" TMPFILE=$(mktemp) - (cd "$TESTDIR" && "$SCRIPT_DIR/target/release/goose" run --text "please list files in the current directory" --with-builtin developer 2>&1) | tee "$TMPFILE" + (cd "$TESTDIR" && "$SCRIPT_DIR/target/release/goose" run --text "please list files in the current directory" --with-builtin developer,autovisualiser,computercontroller,tutorial 2>&1) | tee "$TMPFILE" echo "" if grep -q "shell | developer" "$TMPFILE"; then echo "✓ SUCCESS: Test passed - developer tool called" - RESULTS+=("✓ ${PROVIDER}/${MODEL}") + RESULTS+=("✓ ${PROVIDER}: ${MODEL}") else echo "✗ FAILED: Test failed - no developer tools called" - RESULTS+=("✗ ${PROVIDER}/${MODEL}") + RESULTS+=("✗ ${PROVIDER}: ${MODEL}") fi rm "$TMPFILE" rm -rf "$TESTDIR"