Skip to content
Closed
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
68 changes: 66 additions & 2 deletions src/praisonai-agents/praisonaiagents/agent/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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
Expand All @@ -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)
Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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)

Expand Down
4 changes: 4 additions & 0 deletions src/praisonai-agents/praisonaiagents/llm/llm.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading