diff --git a/src/praisonai-agents/praisonaiagents/agent/agent.py b/src/praisonai-agents/praisonaiagents/agent/agent.py index 40f187145..42aa947fe 100644 --- a/src/praisonai-agents/praisonaiagents/agent/agent.py +++ b/src/praisonai-agents/praisonaiagents/agent/agent.py @@ -217,7 +217,9 @@ def __init__( max_guardrail_retries: int = 3, handoffs: Optional[List[Union['Agent', 'Handoff']]] = None, base_url: Optional[str] = None, - api_key: Optional[str] = None + api_key: Optional[str] = None, + gemini_google_search: bool = False, + gemini_code_execution: bool = False, ): """Initialize an Agent instance. @@ -308,6 +310,10 @@ def __init__( If provided, automatically creates a custom LLM instance. Defaults to None. api_key (Optional[str], optional): API key for LLM provider. If not provided, falls back to environment variables. Defaults to None. + gemini_google_search (bool, optional): Enable Google Search grounding for Gemini models. + Defaults to False. + gemini_code_execution (bool, optional): Enable code execution for Gemini models. + Defaults to False. Raises: ValueError: If all of name, role, goal, backstory, and instructions are None. @@ -479,6 +485,10 @@ def __init__( self.handoffs = handoffs if handoffs else [] self._process_handoffs() + # Gemini-specific tools + self.gemini_google_search = gemini_google_search + self.gemini_code_execution = gemini_code_execution + # Check if knowledge parameter has any values if not knowledge: self.knowledge = None @@ -588,6 +598,19 @@ def _setup_guardrail(self): else: raise ValueError("Agent guardrail must be either a callable or a string description") + def _prepare_gemini_tools(self): + """Prepare Gemini internal tools based on configuration. + + Returns: + List of Gemini internal tool dictionaries + """ + gemini_tools = [] + if self.gemini_google_search: + gemini_tools.append({"google_search_retrieval": {}}) + if self.gemini_code_execution: + gemini_tools.append({"code_execution": {}}) + return gemini_tools + def _process_handoffs(self): """Process handoffs and convert them to tools that can be used by the agent.""" if not self.handoffs: @@ -822,6 +845,7 @@ def _format_tools_for_completion(self, tools=None): - Callable functions - String function names - Objects with to_openai_tool() method + - Gemini internal tools (google_search_retrieval, code_execution) Args: tools: List of tools in various formats or None to use self.tools @@ -837,8 +861,12 @@ def _format_tools_for_completion(self, tools=None): formatted_tools = [] for tool in tools: + # Handle Gemini-specific internal tools + if isinstance(tool, dict) and ("google_search_retrieval" in tool or "code_execution" in tool): + formatted_tools.append(tool) + continue # Handle pre-formatted OpenAI tools - if isinstance(tool, dict) and tool.get('type') == 'function': + elif isinstance(tool, dict) and tool.get('type') == 'function': # Validate nested dictionary structure before accessing if 'function' in tool and isinstance(tool['function'], dict) and 'name' in tool['function']: formatted_tools.append(tool) @@ -1199,6 +1227,13 @@ def chat(self, prompt, temperature=0.2, tools=None, output_json=None, output_pyd tool_param = [openai_tool] logging.debug(f"Converted MCP tool: {tool_param}") + # Add Gemini-specific tools if enabled + gemini_tools = self._prepare_gemini_tools() + if gemini_tools: + if tool_param is None: + tool_param = [] + tool_param.extend(gemini_tools) + # Store chat history length for potential rollback chat_history_length = len(self.chat_history) @@ -1264,6 +1299,17 @@ def chat(self, prompt, temperature=0.2, tools=None, output_json=None, output_pyd display_error(f"Error in LLM chat: {e}") return None else: + # Add Gemini-specific tools if enabled (for non-custom LLM path) + if tools is None or (isinstance(tools, list) and len(tools) == 0): + # Check if we need to add Gemini tools + gemini_tools = self._prepare_gemini_tools() + if gemini_tools: + if tools is None: + tools = gemini_tools + else: + tools = list(tools) # Make a copy + tools.extend(gemini_tools) + # Use the new _build_messages helper method messages, original_prompt = self._build_messages(prompt, temperature, output_json, output_pydantic) @@ -1517,6 +1563,13 @@ async def achat(self, prompt: str, temperature=0.2, tools=None, output_json=None prompt = f"{prompt}\n\nKnowledge: {knowledge_content}" if self._using_custom_llm: + # Add Gemini-specific tools if enabled + gemini_tools = self._prepare_gemini_tools() + if gemini_tools: + if tools is None: + tools = [] + tools.extend(gemini_tools) + # Store chat history length for potential rollback chat_history_length = len(self.chat_history) @@ -1580,6 +1633,17 @@ async def achat(self, prompt: str, temperature=0.2, tools=None, output_json=None return None # For OpenAI client + # Add Gemini-specific tools if enabled (for non-custom LLM path) + if tools is None or (isinstance(tools, list) and len(tools) == 0): + # Check if we need to add Gemini tools + gemini_tools = self._prepare_gemini_tools() + if gemini_tools: + if tools is None: + tools = gemini_tools + else: + tools = list(tools) # Make a copy + tools.extend(gemini_tools) + # Use the new _build_messages helper method messages, original_prompt = self._build_messages(prompt, temperature, output_json, output_pydantic) diff --git a/src/praisonai-agents/praisonaiagents/llm/llm.py b/src/praisonai-agents/praisonaiagents/llm/llm.py index 1f9f6ae27..4f1a7a168 100644 --- a/src/praisonai-agents/praisonaiagents/llm/llm.py +++ b/src/praisonai-agents/praisonaiagents/llm/llm.py @@ -519,6 +519,10 @@ def _format_tools_for_litellm(self, tools: Optional[List[Any]]) -> Optional[List formatted_tools = [] for tool in tools: + # Check for Gemini-specific tools + if isinstance(tool, dict) and ("google_search_retrieval" in tool or "code_execution" in tool): + formatted_tools.append(tool) + continue # Check if the tool is already in OpenAI format (e.g. from MCP.to_openai_tool()) if isinstance(tool, dict) and 'type' in tool and tool['type'] == 'function': # Validate nested dictionary structure before accessing