Repository: phidatahq/phidata
Issue category: Bug Report

ToolKit functions with no arguments cause an error when using Gemini models. 
phidata version: 2.7.2

**To reproduce**: Use a Gemini model and provide a toolkit with a registered method that takes no arguments.
**Expected behaviour**: Model can successfully use the tool.
**Actual behaviour**: The gemini library returns this error:

```
400 * GenerateContentRequest.tools[0].function_declarations[11].parameters.properties: should be non-empty for OBJECT type
grpc._channel._InactiveRpcError: <_InactiveRpcError of RPC that terminated with:
	status = StatusCode.INVALID_ARGUMENT
	details = "* GenerateContentRequest.tools[0].function_declarations[11].parameters.properties: should be non-empty for OBJECT type
```
**A minimal code to reproduce is attached** [reproduce.zip](https://github.com/user-attachments/files/18121422/reproduce.zip).

Workaround: Adding a dummy parameter to the method seems to fix the issue.

Can this be fixed with ToolKit.register(), or where are the model.function_declarations being setup? In the former case, adding a dummy parameter would be easy, but feels messy.



Functions to edit:
phi/model/google/gemini.py:Gemini.add_tool
Functions added: phi/model/google/gemini.py:Gemini._build_function_declaration

Patch:
diff --git a/phi/model/google/gemini.py b/phi/model/google/gemini.py
index 4a11c1c43..263d3afb0 100644
--- a/phi/model/google/gemini.py
+++ b/phi/model/google/gemini.py
@@ -23,7 +23,7 @@
         GenerateContentResponse as ResultGenerateContentResponse,
     )
     from google.protobuf.struct_pb2 import Struct
-except ImportError:
+except (ModuleNotFoundError, ImportError):
     logger.error("`google-generativeai` not installed. Please install it using `pip install google-generativeai`")
     raise
 
@@ -301,6 +301,7 @@ def format_functions(self, params: Dict[str, Any]) -> Dict[str, Any]:
             Dict[str, Any]: The converted parameters dictionary compatible with Gemini.
         """
         formatted_params = {}
+
         for key, value in params.items():
             if key == "properties" and isinstance(value, dict):
                 converted_properties = {}
@@ -322,8 +323,33 @@ def format_functions(self, params: Dict[str, Any]) -> Dict[str, Any]:
                 formatted_params[key] = converted_properties
             else:
                 formatted_params[key] = value
+
         return formatted_params
 
+    def _build_function_declaration(self, func: Function) -> FunctionDeclaration:
+        """
+        Builds the function declaration for Gemini tool calling.
+
+        Args:
+            func: An instance of the function.
+
+        Returns:
+            FunctionDeclaration: The formatted function declaration.
+        """
+        formatted_params = self.format_functions(func.parameters)
+        if "properties" in formatted_params and formatted_params["properties"]:
+            # We have parameters to add
+            return FunctionDeclaration(
+                name=func.name,
+                description=func.description,
+                parameters=formatted_params,
+            )
+        else:
+            return FunctionDeclaration(
+                name=func.name,
+                description=func.description,
+            )
+
     def add_tool(
         self,
         tool: Union["Tool", "Toolkit", Callable, dict, "Function"],
@@ -356,11 +382,7 @@ def add_tool(
                         func._agent = agent
                         func.process_entrypoint()
                         self.functions[name] = func
-                        function_declaration = FunctionDeclaration(
-                            name=func.name,
-                            description=func.description,
-                            parameters=self.format_functions(func.parameters),
-                        )
+                        function_declaration = self._build_function_declaration(func)
                         self.function_declarations.append(function_declaration)
                         logger.debug(f"Function {name} from {tool.name} added to model.")
 
@@ -369,11 +391,8 @@ def add_tool(
                     tool._agent = agent
                     tool.process_entrypoint()
                     self.functions[tool.name] = tool
-                    function_declaration = FunctionDeclaration(
-                        name=tool.name,
-                        description=tool.description,
-                        parameters=self.format_functions(tool.parameters),
-                    )
+
+                    function_declaration = self._build_function_declaration(tool)
                     self.function_declarations.append(function_declaration)
                     logger.debug(f"Function {tool.name} added to model.")
 
@@ -383,11 +402,7 @@ def add_tool(
                     if function_name not in self.functions:
                         func = Function.from_callable(tool)
                         self.functions[func.name] = func
-                        function_declaration = FunctionDeclaration(
-                            name=func.name,
-                            description=func.description,
-                            parameters=self.format_functions(func.parameters),
-                        )
+                        function_declaration = self._build_function_declaration(func)
                         self.function_declarations.append(function_declaration)
                         logger.debug(f"Function '{func.name}' added to model.")
                 except Exception as e: