From 7ff0b66f4a648595e48f6fbf89c04a028bc22798 Mon Sep 17 00:00:00 2001 From: li Date: Fri, 27 Feb 2026 00:46:05 +0100 Subject: [PATCH] fix(gemini): strip x-extension fields from tool schemas and handle empty safety blocks --- vtcode-core/src/gemini/streaming/processor.rs | 4 ++++ vtcode-core/src/llm/providers/gemini/sanitize.rs | 2 +- vtcode-core/src/tools/registry/declarations.rs | 14 ++++++++++---- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/vtcode-core/src/gemini/streaming/processor.rs b/vtcode-core/src/gemini/streaming/processor.rs index 4f2c3cc36..322716c99 100644 --- a/vtcode-core/src/gemini/streaming/processor.rs +++ b/vtcode-core/src/gemini/streaming/processor.rs @@ -532,6 +532,10 @@ impl StreamingProcessor { { let mut _has_valid_content = false; + if candidate.finish_reason.is_some() { + _has_valid_content = true; + } + // Process each part of the content for part in &candidate.content.parts { match part { diff --git a/vtcode-core/src/llm/providers/gemini/sanitize.rs b/vtcode-core/src/llm/providers/gemini/sanitize.rs index ba4ce4660..c160d0dbc 100644 --- a/vtcode-core/src/llm/providers/gemini/sanitize.rs +++ b/vtcode-core/src/llm/providers/gemini/sanitize.rs @@ -33,7 +33,7 @@ pub fn sanitize_function_parameters(parameters: Value) -> Value { let mut sanitized = Map::new(); for (key, value) in map { // Skip unsupported fields at this level - if UNSUPPORTED_FIELDS.contains(&key.as_str()) { + if UNSUPPORTED_FIELDS.contains(&key.as_str()) || key.starts_with("x-") { continue; } // Recursively sanitize nested values diff --git a/vtcode-core/src/tools/registry/declarations.rs b/vtcode-core/src/tools/registry/declarations.rs index 2c71f65e1..c24813f72 100644 --- a/vtcode-core/src/tools/registry/declarations.rs +++ b/vtcode-core/src/tools/registry/declarations.rs @@ -904,22 +904,22 @@ fn annotate_parameters(params: &mut Value, meta: &super::registration::ToolMetad }; if let Some(permission) = meta.default_permission() { - map.insert( + insert_if_allowed(map, "x-default-permission".to_string(), json!(permission_label(&permission)), ); } if !meta.aliases().is_empty() { - map.insert("x-aliases".to_string(), json!(meta.aliases().to_vec())); + insert_if_allowed(map, "x-aliases".to_string(), json!(meta.aliases().to_vec())); } if let Some(schema) = meta.config_schema() { - map.insert("x-config-schema".to_string(), schema.clone()); + insert_if_allowed(map, "x-config-schema".to_string(), schema.clone()); } if let Some(schema) = meta.state_schema() { - map.insert("x-state-schema".to_string(), schema.clone()); + insert_if_allowed(map, "x-state-schema".to_string(), schema.clone()); } } @@ -930,3 +930,9 @@ fn permission_label(permission: &ToolPolicy) -> &'static str { ToolPolicy::Prompt => "prompt", } } + +fn insert_if_allowed(map: &mut serde_json::Map, key: String, value: Value) { + if !key.starts_with("x-") { + map.insert(key, value); + } +}