Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 41 additions & 16 deletions crates/goose/src/providers/formats/google.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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<String, Value>, parent_key: Option<&str>) -> Value {
let accepted_keys = get_accepted_keys(parent_key);

let filtered_map: Map<String, Value> = 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<String, Value> = nested_map
.iter()
Expand All @@ -200,29 +210,44 @@ pub fn process_map(map: &Map<String, Value>, 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<Value> = 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<Message> {
let mut content = Vec::new();
let binding = vec![];
Expand Down
2 changes: 1 addition & 1 deletion crates/goose/src/providers/openrouter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
12 changes: 6 additions & 6 deletions scripts/test_providers.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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"
Expand Down
Loading