Mirrowel commited on
Commit
3ff0b2d
Β·
1 Parent(s): 5c4d30a

fix(tools): πŸ› inject dummy parameter for tools with empty schemas

Browse files

Gemini/Antigravity API fails to emit functionCall when tool schemas have empty properties {}. This commit adds a workaround by injecting a dummy optional parameter "_" into empty tool schemas to ensure function calls are properly emitted.

- Add dummy parameter injection in both antigravity_provider and gemini_cli_provider
- Apply workaround to both schema transformation paths and default empty schema cases
- Update docstring for _apply_claude_tool_transform to clarify its purpose
- Improve inline comments for error body reading logic

src/rotator_library/providers/antigravity_provider.py CHANGED
@@ -2450,9 +2450,23 @@ class AntigravityProvider(AntigravityAuthBase, ProviderInterface):
2450
  schema.pop("$schema", None)
2451
  schema.pop("strict", None)
2452
  schema = _normalize_type_arrays(schema)
 
 
 
 
 
 
 
 
 
 
2453
  func_decl["parametersJsonSchema"] = schema
2454
  else:
2455
- func_decl["parametersJsonSchema"] = {"type": "object", "properties": {}}
 
 
 
 
2456
 
2457
  gemini_tools.append({"functionDeclarations": [func_decl]})
2458
 
@@ -2577,7 +2591,11 @@ class AntigravityProvider(AntigravityAuthBase, ProviderInterface):
2577
  return antigravity_payload
2578
 
2579
  def _apply_claude_tool_transform(self, payload: Dict[str, Any]) -> None:
2580
- """Apply Claude-specific tool schema transformations."""
 
 
 
 
2581
  tools = payload["request"].get("tools", [])
2582
  for tool in tools:
2583
  for func_decl in tool.get("functionDeclarations", []):
@@ -3219,8 +3237,8 @@ class AntigravityProvider(AntigravityAuthBase, ProviderInterface):
3219
  "POST", url, headers=headers, json=payload, timeout=600.0
3220
  ) as response:
3221
  if response.status_code >= 400:
3222
- # Read error body for raise_for_status to include in exception
3223
- # Terminal logging commented out - errors are logged in failures.log
3224
  try:
3225
  await response.aread()
3226
  # lib_logger.error(
 
2450
  schema.pop("$schema", None)
2451
  schema.pop("strict", None)
2452
  schema = _normalize_type_arrays(schema)
2453
+
2454
+ # Workaround: Antigravity/Gemini fails to emit functionCall
2455
+ # when tool has empty properties {}. Inject a dummy optional
2456
+ # parameter to ensure the tool call is emitted.
2457
+ props = schema.get("properties", {})
2458
+ if not props:
2459
+ schema["properties"] = {
2460
+ "_": {"type": "string", "description": "Unused"}
2461
+ }
2462
+
2463
  func_decl["parametersJsonSchema"] = schema
2464
  else:
2465
+ # No parameters provided - use default with dummy param
2466
+ func_decl["parametersJsonSchema"] = {
2467
+ "type": "object",
2468
+ "properties": {"_": {"type": "string", "description": "Unused"}},
2469
+ }
2470
 
2471
  gemini_tools.append({"functionDeclarations": [func_decl]})
2472
 
 
2591
  return antigravity_payload
2592
 
2593
  def _apply_claude_tool_transform(self, payload: Dict[str, Any]) -> None:
2594
+ """Apply Claude-specific tool schema transformations.
2595
+
2596
+ Converts parametersJsonSchema to parameters and applies Claude-specific
2597
+ schema cleaning (removes unsupported JSON Schema fields).
2598
+ """
2599
  tools = payload["request"].get("tools", [])
2600
  for tool in tools:
2601
  for func_decl in tool.get("functionDeclarations", []):
 
3237
  "POST", url, headers=headers, json=payload, timeout=600.0
3238
  ) as response:
3239
  if response.status_code >= 400:
3240
+ # Read error body so it's available in response.text for logging
3241
+ # The actual logging happens in failure_logger via _extract_response_body
3242
  try:
3243
  await response.aread()
3244
  # lib_logger.error(
src/rotator_library/providers/gemini_cli_provider.py CHANGED
@@ -1256,13 +1256,22 @@ class GeminiCliProvider(GeminiAuthBase, ProviderInterface):
1256
  schema = self._gemini_cli_transform_schema(
1257
  new_function["parameters"]
1258
  )
 
 
 
 
 
 
 
1259
  new_function["parametersJsonSchema"] = schema
1260
  del new_function["parameters"]
1261
  elif "parametersJsonSchema" not in new_function:
1262
- # Set default empty schema if neither exists
1263
  new_function["parametersJsonSchema"] = {
1264
  "type": "object",
1265
- "properties": {},
 
 
1266
  }
1267
 
1268
  # Gemini 3 specific transformations
 
1256
  schema = self._gemini_cli_transform_schema(
1257
  new_function["parameters"]
1258
  )
1259
+ # Workaround: Gemini fails to emit functionCall for tools
1260
+ # with empty properties {}. Inject a dummy optional param.
1261
+ props = schema.get("properties", {})
1262
+ if not props:
1263
+ schema["properties"] = {
1264
+ "_": {"type": "string", "description": "Unused"}
1265
+ }
1266
  new_function["parametersJsonSchema"] = schema
1267
  del new_function["parameters"]
1268
  elif "parametersJsonSchema" not in new_function:
1269
+ # Set default schema with dummy param if neither exists
1270
  new_function["parametersJsonSchema"] = {
1271
  "type": "object",
1272
+ "properties": {
1273
+ "_": {"type": "string", "description": "Unused"}
1274
+ },
1275
  }
1276
 
1277
  # Gemini 3 specific transformations