mirror of
https://github.com/MervinPraison/PraisonAI.git
synced 2026-11-07 17:33:33 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
68664fdd55 | ||
|
|
bcda52cc44 | ||
|
|
4888dfb993 | ||
|
|
c5a3fc89cc | ||
|
|
ca33559d4c | ||
|
|
b02449dfdb | ||
|
|
c4c0c1c146 | ||
|
|
aa2d7e4c31 | ||
|
|
b339c5b3aa | ||
|
|
e535f98f4e | ||
|
|
bc349ad09f | ||
|
|
5b8da1180e | ||
|
|
8f6d8f0a48 | ||
|
|
3730b4d094 | ||
|
|
7f3810ed98 | ||
|
|
c84a59ab4d | ||
|
|
c6a6bfa827 | ||
|
|
a604cd9584 | ||
|
|
cb043da19c |
No files matched your search
@@ -22,5 +22,5 @@ jobs:
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: '@claude review this issue and do a detailed analysis and fix this if the existing code doesn\'t have the solution implemented. Making sure it has backward compatibility, no existing features removed. After making those changes, again review the applied changes. Use @web to search if you dont know any information or to find the latest documentation or to find the latest version. Run the code if you think you need to run it to test it. Minimal code change to start with if required any changes. **Please create a PR with your changes**.'
|
||||
body: '@claude review this issue and do a detailed analysis and fix this if the existing code doesn\'t have the solution implemented. Making sure it has backward compatibility, no existing features removed. After making those changes, again review the applied changes. Use @web to search if you dont know any information or to find the latest documentation or to find the latest version. Run the code if you think you need to run it to test it. Minimal code change to start with if required any changes. **Please create a PR using gh tool with your changes**.'
|
||||
})
|
||||
@@ -42,6 +42,7 @@ jobs:
|
||||
Bash(pip:*)
|
||||
Bash(conda:*)
|
||||
Bash(pytest:*)
|
||||
Bash(gh:*)
|
||||
Bash(python -m pytest:*)
|
||||
Bash(python -m pip:*)
|
||||
Bash(poetry:*)
|
||||
@@ -129,23 +130,32 @@ jobs:
|
||||
github-token: ${{ secrets.GH_TOKEN }}
|
||||
script: |
|
||||
let issueNumber;
|
||||
let isPR = false;
|
||||
|
||||
// Determine issue/PR number based on event type
|
||||
// Determine issue/PR number and type based on event type
|
||||
if (context.eventName === 'pull_request_review_comment' || context.eventName === 'pull_request_review') {
|
||||
issueNumber = context.payload.pull_request?.number || context.issue.number;
|
||||
isPR = true;
|
||||
} else if (context.eventName === 'issue_comment' && context.payload.issue.pull_request) {
|
||||
// This is a comment on a PR (not an issue)
|
||||
issueNumber = context.payload.issue.number;
|
||||
isPR = true;
|
||||
} else {
|
||||
// This is a regular issue
|
||||
// This is a regular issue - don't post review comment
|
||||
issueNumber = context.issue.number;
|
||||
isPR = false;
|
||||
}
|
||||
|
||||
console.log(`Posting review comment on issue/PR #${issueNumber}`);
|
||||
|
||||
await github.rest.issues.createComment({
|
||||
issue_number: issueNumber,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: '@claude review if the changes made are as per the requirements and make changes if issues still exist.'
|
||||
});
|
||||
// Only post review comment on PRs, not regular issues
|
||||
if (isPR) {
|
||||
console.log(`Posting review comment on PR #${issueNumber}`);
|
||||
|
||||
await github.rest.issues.createComment({
|
||||
issue_number: issueNumber,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: '@claude review if the changes made in the above branch are as per the requirements and make changes to the above branch if issues still exist.'
|
||||
});
|
||||
} else {
|
||||
console.log(`Skipping review comment for regular issue #${issueNumber}`);
|
||||
}
|
||||
+1
-1
@@ -16,7 +16,7 @@ RUN mkdir -p /root/.praison
|
||||
# Install Python packages (using latest versions)
|
||||
RUN pip install --no-cache-dir \
|
||||
flask \
|
||||
"praisonai>=2.2.58" \
|
||||
"praisonai>=2.2.61" \
|
||||
"praisonai[api]" \
|
||||
gunicorn \
|
||||
markdown
|
||||
|
||||
@@ -16,7 +16,7 @@ RUN mkdir -p /root/.praison
|
||||
# Install Python packages (using latest versions)
|
||||
RUN pip install --no-cache-dir \
|
||||
praisonai_tools \
|
||||
"praisonai>=2.2.58" \
|
||||
"praisonai>=2.2.61" \
|
||||
"praisonai[chat]" \
|
||||
"embedchain[github,youtube]"
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ RUN mkdir -p /root/.praison
|
||||
# Install Python packages (using latest versions)
|
||||
RUN pip install --no-cache-dir \
|
||||
praisonai_tools \
|
||||
"praisonai>=2.2.58" \
|
||||
"praisonai>=2.2.61" \
|
||||
"praisonai[ui]" \
|
||||
"praisonai[chat]" \
|
||||
"praisonai[realtime]" \
|
||||
|
||||
@@ -16,7 +16,7 @@ RUN mkdir -p /root/.praison
|
||||
# Install Python packages (using latest versions)
|
||||
RUN pip install --no-cache-dir \
|
||||
praisonai_tools \
|
||||
"praisonai>=2.2.58" \
|
||||
"praisonai>=2.2.61" \
|
||||
"praisonai[ui]" \
|
||||
"praisonai[crewai]"
|
||||
|
||||
|
||||
+2
-2
@@ -121,7 +121,7 @@ healthcheck:
|
||||
## 📦 Package Versions
|
||||
|
||||
All Docker images use consistent, up-to-date versions:
|
||||
- PraisonAI: `>=2.2.58`
|
||||
- PraisonAI: `>=2.2.61`
|
||||
- PraisonAI Agents: `>=0.0.92`
|
||||
- Python: `3.11-slim`
|
||||
|
||||
@@ -218,7 +218,7 @@ docker-compose up -d
|
||||
### Version Pinning
|
||||
To use specific versions, update the Dockerfile:
|
||||
```dockerfile
|
||||
RUN pip install "praisonai==2.2.58" "praisonaiagents==0.0.92"
|
||||
RUN pip install "praisonai==2.2.61" "praisonaiagents==0.0.92"
|
||||
```
|
||||
|
||||
## 🌐 Production Deployment
|
||||
|
||||
@@ -0,0 +1,215 @@
|
||||
# Gemini Internal Tools Examples
|
||||
|
||||
This directory contains examples demonstrating how to use Google Gemini's built-in internal tools through PraisonAI. These tools are natively supported by Gemini models and provide powerful capabilities without requiring external tool implementations.
|
||||
|
||||
## Available Internal Tools
|
||||
|
||||
### 1. Google Search (`googleSearch`)
|
||||
- **Purpose**: Real-time web search with automatic result grounding
|
||||
- **Use Cases**: Current events, latest information, fact-checking
|
||||
- **Example**: `gemini-google-search.py`
|
||||
|
||||
### 2. URL Context (`urlContext`)
|
||||
- **Purpose**: Fetch and analyze content from specific URLs
|
||||
- **Use Cases**: Document analysis, web content summarization, research
|
||||
- **Example**: `gemini-url-context.py`
|
||||
|
||||
### 3. Code Execution (`codeExecution`)
|
||||
- **Purpose**: Execute Python code snippets within the conversation
|
||||
- **Use Cases**: Calculations, data analysis, code validation
|
||||
- **Example**: `gemini-code-execution.py`
|
||||
|
||||
## Prerequisites
|
||||
|
||||
1. **Gemini API Key**: Set your Google AI Studio API key
|
||||
```bash
|
||||
export GEMINI_API_KEY="your-api-key-here"
|
||||
```
|
||||
|
||||
2. **Compatible Model**: Use Gemini models that support internal tools:
|
||||
- `gemini/gemini-2.0-flash` (recommended)
|
||||
- `gemini/gemini-2.0-flash-thinking-exp`
|
||||
- Other Gemini 2.0+ models
|
||||
|
||||
3. **PraisonAI Installation**: Ensure you have the latest version
|
||||
```bash
|
||||
pip install "praisonaiagents[llm]"
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Individual Tool Examples
|
||||
|
||||
1. **Google Search Example** (`gemini-google-search.py`)
|
||||
```python
|
||||
agent = Agent(
|
||||
instructions="Research assistant with web search",
|
||||
llm="gemini/gemini-2.0-flash",
|
||||
tools=[{"googleSearch": {}}]
|
||||
)
|
||||
|
||||
response = agent.start("What's the latest news about AI?")
|
||||
```
|
||||
|
||||
2. **URL Context Example** (`gemini-url-context.py`)
|
||||
```python
|
||||
agent = Agent(
|
||||
instructions="Content analyzer",
|
||||
llm="gemini/gemini-2.0-flash",
|
||||
tools=[{"urlContext": {}}]
|
||||
)
|
||||
|
||||
response = agent.start("Summarize this: https://example.com/article")
|
||||
```
|
||||
|
||||
3. **Code Execution Example** (`gemini-code-execution.py`)
|
||||
```python
|
||||
agent = Agent(
|
||||
instructions="Data analyst with code execution",
|
||||
llm="gemini/gemini-2.0-flash",
|
||||
tools=[{"codeExecution": {}}]
|
||||
)
|
||||
|
||||
response = agent.start("Calculate compound interest for $10,000 at 5% for 10 years")
|
||||
```
|
||||
|
||||
### Combined Tools Example
|
||||
|
||||
**All Tools Together** (`gemini-internal-tools-combined.py`)
|
||||
```python
|
||||
agent = Agent(
|
||||
instructions="Advanced research assistant",
|
||||
llm="gemini/gemini-2.0-flash",
|
||||
tools=[
|
||||
{"googleSearch": {}},
|
||||
{"urlContext": {}},
|
||||
{"codeExecution": {}}
|
||||
]
|
||||
)
|
||||
|
||||
# Now the agent can search, analyze URLs, and execute code as needed
|
||||
```
|
||||
|
||||
## How It Works
|
||||
|
||||
### PraisonAI Integration
|
||||
|
||||
PraisonAI's tool system is designed to pass tools directly to LiteLLM without modification:
|
||||
|
||||
1. **Tool Definition**: Define tools using the special internal tool syntax
|
||||
```python
|
||||
tools=[{"googleSearch": {}}] # Special format for internal tools
|
||||
```
|
||||
|
||||
2. **Pass-Through**: PraisonAI passes these tools directly to LiteLLM
|
||||
3. **Execution**: LiteLLM sends them to Gemini as internal tool configurations
|
||||
4. **Results**: Gemini executes the tools natively and returns integrated responses
|
||||
|
||||
### Mixing with External Tools
|
||||
|
||||
You can combine internal tools with custom external tools:
|
||||
|
||||
```python
|
||||
def custom_calculator(expression: str) -> str:
|
||||
"""Custom calculator function - safely evaluates basic math expressions"""
|
||||
import ast
|
||||
import operator
|
||||
|
||||
# Define safe operations
|
||||
ops = {
|
||||
ast.Add: operator.add,
|
||||
ast.Sub: operator.sub,
|
||||
ast.Mult: operator.mul,
|
||||
ast.Div: operator.truediv,
|
||||
ast.Pow: operator.pow,
|
||||
ast.USub: operator.neg,
|
||||
}
|
||||
|
||||
def eval_expr(node):
|
||||
if isinstance(node, ast.Constant): # Numbers
|
||||
return node.value
|
||||
elif isinstance(node, ast.BinOp): # Binary operations
|
||||
return ops[type(node.op)](eval_expr(node.left), eval_expr(node.right))
|
||||
elif isinstance(node, ast.UnaryOp): # Unary operations
|
||||
return ops[type(node.op)](eval_expr(node.operand))
|
||||
else:
|
||||
raise TypeError(f"Unsupported operation: {type(node)}")
|
||||
|
||||
try:
|
||||
return str(eval_expr(ast.parse(expression, mode='eval').body))
|
||||
except Exception as e:
|
||||
return f"Error: {e}"
|
||||
|
||||
agent = Agent(
|
||||
tools=[
|
||||
{"googleSearch": {}}, # Internal tool
|
||||
{"codeExecution": {}}, # Internal tool
|
||||
custom_calculator # External tool
|
||||
]
|
||||
)
|
||||
```
|
||||
|
||||
## Running the Examples
|
||||
|
||||
1. **Set API Key**:
|
||||
```bash
|
||||
export GEMINI_API_KEY="your-api-key"
|
||||
```
|
||||
|
||||
2. **Run Individual Examples**:
|
||||
```bash
|
||||
python gemini-google-search.py
|
||||
python gemini-url-context.py
|
||||
python gemini-code-execution.py
|
||||
```
|
||||
|
||||
3. **Run Combined Example**:
|
||||
```bash
|
||||
python gemini-internal-tools-combined.py
|
||||
```
|
||||
|
||||
## Benefits Over External Tools
|
||||
|
||||
### Advantages of Internal Tools
|
||||
|
||||
1. **Native Integration**: No need to implement or maintain external tool code
|
||||
2. **Optimized Performance**: Tools are executed within Gemini's environment
|
||||
3. **Automatic Grounding**: Search results are automatically integrated into responses
|
||||
4. **No Rate Limits**: No separate API calls or rate limiting concerns
|
||||
5. **Security**: Code execution is sandboxed within Gemini's environment
|
||||
|
||||
### When to Use External Tools
|
||||
|
||||
- When you need specific custom logic
|
||||
- When you require integration with private APIs
|
||||
- When you need persistent data storage
|
||||
- When you want full control over tool behavior
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
1. **API Key Error**: Ensure `GEMINI_API_KEY` is set correctly
|
||||
2. **Model Not Supported**: Use Gemini 2.0+ models for internal tools
|
||||
3. **Tool Not Available**: Some tools may have regional or quota restrictions
|
||||
|
||||
### Debug Mode
|
||||
|
||||
Enable verbose logging to see tool usage:
|
||||
```python
|
||||
agent = Agent(
|
||||
# ... config
|
||||
verbose=True # Shows detailed tool usage
|
||||
)
|
||||
```
|
||||
|
||||
## References
|
||||
|
||||
- [Gemini API: Google Search Grounding](https://ai.google.dev/gemini-api/docs/google-search)
|
||||
- [Gemini API: URL Context](https://ai.google.dev/gemini-api/docs/url-context)
|
||||
- [Gemini API: Code Execution](https://ai.google.dev/gemini-api/docs/code-execution)
|
||||
- [LiteLLM Gemini Provider Documentation](https://docs.litellm.ai/docs/providers/gemini)
|
||||
|
||||
## Contributing
|
||||
|
||||
If you create additional examples or find improvements, please contribute back to the PraisonAI project!
|
||||
@@ -0,0 +1,99 @@
|
||||
"""
|
||||
Gemini Code Execution Tool Example
|
||||
|
||||
This example demonstrates how to use Gemini's built-in Code Execution functionality
|
||||
through PraisonAI. The Code Execution tool allows the model to run Python code
|
||||
snippets directly within the conversation for dynamic computation and validation.
|
||||
|
||||
Prerequisites:
|
||||
- Set GEMINI_API_KEY environment variable
|
||||
- Use a Gemini model that supports internal tools (gemini-2.0-flash, etc.)
|
||||
|
||||
Features:
|
||||
- Direct Python code execution within the model
|
||||
- Real-time calculations and data processing
|
||||
- Code validation and testing
|
||||
- Mathematical computations and visualizations
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent
|
||||
|
||||
# Ensure you have your Gemini API key set
|
||||
# import os; os.environ["GEMINI_API_KEY"] = "your-api-key-here"
|
||||
|
||||
def main():
|
||||
# Create agent with Code Execution internal tool
|
||||
agent = Agent(
|
||||
instructions="""You are a data analyst and programmer that can execute Python code.
|
||||
Use the Code Execution tool to perform calculations, analyze data, create visualizations,
|
||||
and validate logic. Always show your work by executing relevant code snippets.""",
|
||||
|
||||
llm="gemini/gemini-2.0-flash",
|
||||
|
||||
# Enable Code Execution internal tool
|
||||
tools=[{"codeExecution": {}}],
|
||||
|
||||
verbose=True
|
||||
)
|
||||
|
||||
# Example tasks that benefit from code execution
|
||||
tasks = [
|
||||
"Calculate the compound interest on $10,000 invested at 5% annual rate for 10 years, compounded monthly. Show the calculation step by step.",
|
||||
|
||||
"Generate the first 20 Fibonacci numbers and find their sum. Also calculate the ratio of consecutive Fibonacci numbers.",
|
||||
|
||||
"Create a simple linear regression model for the data points: (1,2), (2,4), (3,6), (4,8), (5,10). Calculate the R-squared value.",
|
||||
|
||||
"Solve the quadratic equation 2x² + 5x - 3 = 0 using the quadratic formula. Verify the solutions by substituting back.",
|
||||
|
||||
"Calculate the statistical summary (mean, median, mode, standard deviation) for this dataset: [12, 15, 18, 12, 20, 25, 18, 12, 22, 30, 15, 18]",
|
||||
|
||||
"Write a function to check if a number is prime, then find all prime numbers between 1 and 100.",
|
||||
|
||||
"Calculate the area under the curve y = x² from x = 0 to x = 5 using numerical integration (trapezoidal rule)."
|
||||
]
|
||||
|
||||
print("=== Gemini Code Execution Tool Demo ===\n")
|
||||
|
||||
for i, task in enumerate(tasks, 1):
|
||||
print(f"Task {i}: {task}")
|
||||
print("-" * 80)
|
||||
|
||||
try:
|
||||
response = agent.start(task)
|
||||
print(f"Response: {response}")
|
||||
print("\n" + "="*100 + "\n")
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error: {e}")
|
||||
print("\n" + "="*100 + "\n")
|
||||
|
||||
def demonstrate_direct_usage():
|
||||
"""
|
||||
Example of how the Code Execution tool works at the LiteLLM level
|
||||
(for reference - this requires direct LiteLLM usage)
|
||||
"""
|
||||
print("=== Direct LiteLLM Code Execution Usage (Reference) ===")
|
||||
print("""
|
||||
# This is how it works at the LiteLLM level:
|
||||
|
||||
import litellm
|
||||
import os
|
||||
|
||||
os.environ["GEMINI_API_KEY"] = "your-api-key"
|
||||
|
||||
response = litellm.completion(
|
||||
model="gemini/gemini-2.0-flash",
|
||||
messages=[{
|
||||
"role": "user",
|
||||
"content": "Calculate the factorial of 10 and show the code"
|
||||
}],
|
||||
tools=[{"codeExecution": {}}],
|
||||
)
|
||||
|
||||
print(response.choices[0].message.content)
|
||||
""")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
demonstrate_direct_usage()
|
||||
@@ -0,0 +1,63 @@
|
||||
"""
|
||||
Gemini Google Search Tool Example
|
||||
|
||||
This example demonstrates how to use Gemini's built-in Google Search functionality
|
||||
through PraisonAI. The Google Search tool allows the model to perform real-time
|
||||
web searches and ground responses in current information.
|
||||
|
||||
Prerequisites:
|
||||
- Set GEMINI_API_KEY environment variable
|
||||
- Use a Gemini model that supports internal tools (gemini-2.0-flash, etc.)
|
||||
|
||||
Features:
|
||||
- Real-time web search capabilities
|
||||
- Automatic grounding of responses in search results
|
||||
- No need to implement external search tools
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent
|
||||
|
||||
# Ensure you have your Gemini API key set
|
||||
# import os; os.environ["GEMINI_API_KEY"] = "your-api-key-here"
|
||||
|
||||
def main():
|
||||
# Create agent with Google Search internal tool
|
||||
agent = Agent(
|
||||
instructions="""You are a research assistant that can search the web for current information.
|
||||
Use the Google Search tool to find up-to-date information when answering questions.
|
||||
Always cite your sources and indicate when information comes from search results.""",
|
||||
|
||||
llm="gemini/gemini-2.0-flash",
|
||||
|
||||
# Enable Google Search internal tool
|
||||
tools=[{"googleSearch": {}}],
|
||||
|
||||
verbose=True
|
||||
)
|
||||
|
||||
# Example queries that benefit from real-time search
|
||||
queries = [
|
||||
"What's the latest news about AI developments this week?",
|
||||
"What's the current weather in San Francisco?",
|
||||
"What are the latest stock prices for major tech companies?",
|
||||
"What are the most recent breakthroughs in quantum computing?",
|
||||
"Who won the latest major sports championships?"
|
||||
]
|
||||
|
||||
print("=== Gemini Google Search Tool Demo ===\n")
|
||||
|
||||
for i, query in enumerate(queries, 1):
|
||||
print(f"Query {i}: {query}")
|
||||
print("-" * 50)
|
||||
|
||||
try:
|
||||
response = agent.start(query)
|
||||
print(f"Response: {response}")
|
||||
print("\n" + "="*80 + "\n")
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error: {e}")
|
||||
print("\n" + "="*80 + "\n")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,187 @@
|
||||
"""
|
||||
Gemini Internal Tools Combined Example
|
||||
|
||||
This example demonstrates how to use all three of Gemini's built-in internal tools
|
||||
together through PraisonAI:
|
||||
1. Google Search - for real-time web information
|
||||
2. URL Context - for analyzing web content
|
||||
3. Code Execution - for computations and analysis
|
||||
|
||||
Prerequisites:
|
||||
- Set GEMINI_API_KEY environment variable
|
||||
- Use a Gemini model that supports internal tools (gemini-2.0-flash, etc.)
|
||||
|
||||
Features:
|
||||
- Multi-tool agent combining search, content analysis, and computation
|
||||
- Real-world research and analysis workflows
|
||||
- Comprehensive data gathering and processing capabilities
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent
|
||||
|
||||
# Ensure you have your Gemini API key set
|
||||
# import os; os.environ["GEMINI_API_KEY"] = "your-api-key-here"
|
||||
|
||||
def main():
|
||||
# Create agent with all Gemini internal tools enabled
|
||||
agent = Agent(
|
||||
instructions="""You are an advanced research and analysis assistant with access to:
|
||||
1. Google Search - for finding current information
|
||||
2. URL Context - for analyzing web content
|
||||
3. Code Execution - for calculations and data processing
|
||||
|
||||
Use these tools strategically to provide comprehensive, well-researched, and
|
||||
analytically sound responses. Always show your work and cite sources.""",
|
||||
|
||||
llm="gemini/gemini-2.0-flash",
|
||||
|
||||
# Enable all three Gemini internal tools
|
||||
tools=[
|
||||
{"googleSearch": {}},
|
||||
{"urlContext": {}},
|
||||
{"codeExecution": {}}
|
||||
],
|
||||
|
||||
verbose=True
|
||||
)
|
||||
|
||||
# Complex tasks that benefit from multiple internal tools
|
||||
complex_tasks = [
|
||||
"""Research the current market cap of the top 5 technology companies,
|
||||
then calculate their combined market value and determine what percentage
|
||||
each company represents of the total. Present the results in a formatted table.""",
|
||||
|
||||
"""Find recent research papers about large language models from arXiv,
|
||||
analyze the abstracts of 2-3 papers, and calculate the average number
|
||||
of authors across these papers.""",
|
||||
|
||||
"""Look up the current population of the 10 largest cities in the world,
|
||||
then calculate statistical measures (mean, median, standard deviation)
|
||||
and create a simple visualization of the data distribution.""",
|
||||
|
||||
"""Research the latest Python release notes, analyze the key new features,
|
||||
and write example code demonstrating 2-3 of the most important new features.""",
|
||||
|
||||
"""Find information about recent climate data, analyze a specific dataset
|
||||
or report, and perform calculations to show trends or comparisons over time."""
|
||||
]
|
||||
|
||||
print("=== Gemini Combined Internal Tools Demo ===\n")
|
||||
|
||||
for i, task in enumerate(complex_tasks, 1):
|
||||
print(f"Complex Task {i}:")
|
||||
print(task)
|
||||
print("-" * 100)
|
||||
|
||||
try:
|
||||
response = agent.start(task)
|
||||
print(f"Response: {response}")
|
||||
print("\n" + "="*120 + "\n")
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error: {e}")
|
||||
print("\n" + "="*120 + "\n")
|
||||
|
||||
def demonstrate_tool_synergy():
|
||||
"""
|
||||
Example showing how the tools work together for a research workflow
|
||||
"""
|
||||
print("=== Tool Synergy Example ===")
|
||||
|
||||
agent = Agent(
|
||||
instructions="""You are a market research analyst. Use all available tools
|
||||
to gather information, analyze content, and perform calculations as needed.""",
|
||||
|
||||
llm="gemini/gemini-2.0-flash",
|
||||
tools=[
|
||||
{"googleSearch": {}},
|
||||
{"urlContext": {}},
|
||||
{"codeExecution": {}}
|
||||
],
|
||||
verbose=True
|
||||
)
|
||||
|
||||
# Multi-step research task
|
||||
research_query = """
|
||||
Conduct a mini market analysis on electric vehicle adoption:
|
||||
1. Search for recent statistics on global EV sales
|
||||
2. Find and analyze a recent report or article about EV market trends
|
||||
3. Calculate the year-over-year growth rate if you can find data for multiple years
|
||||
4. Provide a summary with key insights and numerical analysis
|
||||
"""
|
||||
|
||||
print("Research Query:", research_query)
|
||||
print("-" * 80)
|
||||
|
||||
try:
|
||||
response = agent.start(research_query)
|
||||
print(f"Research Results: {response}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error: {e}")
|
||||
|
||||
def show_configuration_options():
|
||||
"""
|
||||
Show different ways to configure the internal tools
|
||||
"""
|
||||
print("\n=== Configuration Examples ===")
|
||||
print("""
|
||||
# Basic usage (all tools with default settings)
|
||||
tools=[
|
||||
{"googleSearch": {}},
|
||||
{"urlContext": {}},
|
||||
{"codeExecution": {}}
|
||||
]
|
||||
|
||||
# Can also be used individually
|
||||
tools=[{"googleSearch": {}}] # Only Google Search
|
||||
tools=[{"urlContext": {}}] # Only URL Context
|
||||
tools=[{"codeExecution": {}}] # Only Code Execution
|
||||
|
||||
# Mix with custom tools
|
||||
from praisonaiagents import Agent
|
||||
|
||||
def custom_calculator(expression: str) -> str:
|
||||
'''Custom calculator function - safely evaluates basic math expressions'''
|
||||
import ast
|
||||
import operator
|
||||
|
||||
# Define safe operations
|
||||
ops = {
|
||||
ast.Add: operator.add,
|
||||
ast.Sub: operator.sub,
|
||||
ast.Mult: operator.mul,
|
||||
ast.Div: operator.truediv,
|
||||
ast.Pow: operator.pow,
|
||||
ast.USub: operator.neg,
|
||||
}
|
||||
|
||||
def eval_expr(node):
|
||||
if isinstance(node, ast.Constant): # Numbers
|
||||
return node.value
|
||||
elif isinstance(node, ast.BinOp): # Binary operations
|
||||
return ops[type(node.op)](eval_expr(node.left), eval_expr(node.right))
|
||||
elif isinstance(node, ast.UnaryOp): # Unary operations
|
||||
return ops[type(node.op)](eval_expr(node.operand))
|
||||
else:
|
||||
raise TypeError(f"Unsupported operation: {type(node)}")
|
||||
|
||||
try:
|
||||
return str(eval_expr(ast.parse(expression, mode='eval').body))
|
||||
except Exception as e:
|
||||
return f"Error: {e}"
|
||||
|
||||
agent = Agent(
|
||||
tools=[
|
||||
{"googleSearch": {}}, # Gemini internal tool
|
||||
{"codeExecution": {}}, # Gemini internal tool
|
||||
custom_calculator # Custom external tool
|
||||
]
|
||||
)
|
||||
""")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
print("\n" + "="*50)
|
||||
demonstrate_tool_synergy()
|
||||
show_configuration_options()
|
||||
@@ -0,0 +1,115 @@
|
||||
"""
|
||||
Gemini URL Context Tool Example
|
||||
|
||||
This example demonstrates how to use Gemini's built-in URL Context functionality
|
||||
through PraisonAI. The URL Context tool allows the model to fetch and analyze
|
||||
content from specific URLs in real-time.
|
||||
|
||||
Prerequisites:
|
||||
- Set GEMINI_API_KEY environment variable
|
||||
- Use a Gemini model that supports internal tools (gemini-2.0-flash, etc.)
|
||||
|
||||
Features:
|
||||
- Real-time URL content fetching
|
||||
- Automatic content analysis and summarization
|
||||
- Support for various web content types
|
||||
- Access to URL metadata through response.model_extra
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent
|
||||
|
||||
# Ensure you have your Gemini API key set
|
||||
# import os; os.environ["GEMINI_API_KEY"] = "your-api-key-here"
|
||||
|
||||
def main():
|
||||
# Create agent with URL Context internal tool
|
||||
agent = Agent(
|
||||
instructions="""You are a content analyst that can fetch and analyze web content.
|
||||
Use the URL Context tool to access and analyze content from provided URLs.
|
||||
Provide detailed summaries, extract key information, and answer questions about the content.""",
|
||||
|
||||
llm="gemini/gemini-2.0-flash",
|
||||
|
||||
# Enable URL Context internal tool
|
||||
tools=[{"urlContext": {}}],
|
||||
|
||||
verbose=True
|
||||
)
|
||||
|
||||
# Example URLs for analysis
|
||||
url_tasks = [
|
||||
{
|
||||
"url": "https://ai.google.dev/gemini-api/docs/models",
|
||||
"task": "Summarize the key information about Gemini models and their capabilities"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/MervinPraison/PraisonAI",
|
||||
"task": "Analyze this GitHub repository and explain what PraisonAI does"
|
||||
},
|
||||
{
|
||||
"url": "https://docs.python.org/3/tutorial/introduction.html",
|
||||
"task": "Extract the main learning objectives from this Python tutorial"
|
||||
},
|
||||
{
|
||||
"url": "https://arxiv.org/abs/2301.00234",
|
||||
"task": "Provide a summary of this research paper's abstract and key contributions"
|
||||
}
|
||||
]
|
||||
|
||||
print("=== Gemini URL Context Tool Demo ===\n")
|
||||
|
||||
for i, item in enumerate(url_tasks, 1):
|
||||
print(f"Analysis {i}: {item['url']}")
|
||||
print(f"Task: {item['task']}")
|
||||
print("-" * 50)
|
||||
|
||||
# Combine URL and task in the query
|
||||
query = f"{item['task']}: {item['url']}"
|
||||
|
||||
try:
|
||||
response = agent.start(query)
|
||||
print(f"Analysis: {response}")
|
||||
|
||||
# Note: URL metadata access requires direct LiteLLM response
|
||||
# In PraisonAI, this would need to be accessed through the LLM response
|
||||
print(f"\nAnalyzed URL: {item['url']}")
|
||||
print("\n" + "="*80 + "\n")
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error: {e}")
|
||||
print("\n" + "="*80 + "\n")
|
||||
|
||||
def demonstrate_direct_usage():
|
||||
"""
|
||||
Example of how the URL Context tool works at the LiteLLM level
|
||||
(for reference - this requires direct LiteLLM usage)
|
||||
"""
|
||||
print("=== Direct LiteLLM URL Context Usage (Reference) ===")
|
||||
print("""
|
||||
# This is how it works at the LiteLLM level:
|
||||
|
||||
import litellm
|
||||
import os
|
||||
|
||||
os.environ["GEMINI_API_KEY"] = "your-api-key"
|
||||
|
||||
response = litellm.completion(
|
||||
model="gemini/gemini-2.0-flash",
|
||||
messages=[{
|
||||
"role": "user",
|
||||
"content": "Summarize this document: https://ai.google.dev/gemini-api/docs/models"
|
||||
}],
|
||||
tools=[{"urlContext": {}}],
|
||||
)
|
||||
|
||||
# Access URL metadata
|
||||
if hasattr(response, 'model_extra') and 'vertex_ai_url_context_metadata' in response.model_extra:
|
||||
url_context_metadata = response.model_extra['vertex_ai_url_context_metadata']
|
||||
urlMetadata = url_context_metadata[0]['urlMetadata'][0]
|
||||
print(f"Retrieved URL: {urlMetadata['retrievedUrl']}")
|
||||
print(f"Retrieval Status: {urlMetadata['urlRetrievalStatus']}")
|
||||
""")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
demonstrate_direct_usage()
|
||||
@@ -0,0 +1,11 @@
|
||||
from praisonaiagents import Agent
|
||||
|
||||
agent = Agent(
|
||||
instructions="You are an audio tour creation AI agent. "
|
||||
"Help users create engaging audio tours, guided experiences, and interactive storytelling content. "
|
||||
"Provide guidance on script writing, audio production, location-based content, and immersive experiences.",
|
||||
llm="openrouter/moonshotai/kimi-k2"
|
||||
)
|
||||
|
||||
response = agent.start("Hello! I'm your audio tour creation assistant. "
|
||||
"How can I help you create captivating audio experiences today?")
|
||||
@@ -0,0 +1,11 @@
|
||||
from praisonaiagents import Agent
|
||||
|
||||
agent = Agent(
|
||||
instructions="You are a chess AI agent. "
|
||||
"Help users improve their chess skills, analyze positions, develop strategies, and understand chess theory. "
|
||||
"Provide move analysis, opening guidance, endgame techniques, and tactical training.",
|
||||
llm="openrouter/moonshotai/kimi-k2"
|
||||
)
|
||||
|
||||
response = agent.start("Hello! I'm your chess assistant. "
|
||||
"How can I help you improve your chess game today?")
|
||||
@@ -0,0 +1,11 @@
|
||||
from praisonaiagents import Agent
|
||||
|
||||
agent = Agent(
|
||||
instructions="You are a creative writing AI agent. "
|
||||
"Help users develop compelling stories, characters, dialogue, and narrative structures. "
|
||||
"Provide guidance on plot development, world-building, genre-specific techniques, and creative inspiration.",
|
||||
llm="openrouter/moonshotai/kimi-k2"
|
||||
)
|
||||
|
||||
response = agent.start("Hello! I'm your creative writing assistant. "
|
||||
"How can I help you bring your stories and ideas to life today?")
|
||||
@@ -0,0 +1,11 @@
|
||||
from praisonaiagents import Agent
|
||||
|
||||
agent = Agent(
|
||||
instructions="You are a cybersecurity AI agent. "
|
||||
"Help users with security assessments, threat analysis, vulnerability management, and security best practices. "
|
||||
"Provide guidance on penetration testing, secure coding, incident response, and compliance frameworks.",
|
||||
llm="openrouter/moonshotai/kimi-k2"
|
||||
)
|
||||
|
||||
response = agent.start("Hello! I'm your cybersecurity assistant. "
|
||||
"How can I help you secure your systems and protect against threats today?")
|
||||
@@ -0,0 +1,11 @@
|
||||
from praisonaiagents import Agent
|
||||
|
||||
agent = Agent(
|
||||
instructions="You are a data visualization AI agent. "
|
||||
"Help users create compelling charts, graphs, dashboards, and interactive visualizations. "
|
||||
"Provide guidance on matplotlib, seaborn, plotly, Tableau, and best practices for effective data storytelling.",
|
||||
llm="openrouter/moonshotai/kimi-k2"
|
||||
)
|
||||
|
||||
response = agent.start("Hello! I'm your data visualization assistant. "
|
||||
"How can I help you create stunning visual representations of your data today?")
|
||||
@@ -0,0 +1,11 @@
|
||||
from praisonaiagents import Agent
|
||||
|
||||
agent = Agent(
|
||||
instructions="You are a 3D PyGame development AI agent. "
|
||||
"Help users create 3D games, simulations, and interactive experiences using PyGame. "
|
||||
"Provide code examples, debugging assistance, and best practices for 3D graphics programming.",
|
||||
llm="openrouter/moonshotai/kimi-k2"
|
||||
)
|
||||
|
||||
response = agent.start("Hello! I'm your 3D PyGame development assistant. "
|
||||
"How can I help you create amazing 3D games today?")
|
||||
@@ -0,0 +1,11 @@
|
||||
from praisonaiagents import Agent
|
||||
|
||||
agent = Agent(
|
||||
instructions="You are a self-evolving AI agent. "
|
||||
"Continuously learn from interactions, adapt your responses, and improve your capabilities. "
|
||||
"Help users with complex problem-solving, creative tasks, and provide insights that evolve based on context and user needs.",
|
||||
llm="openrouter/moonshotai/kimi-k2"
|
||||
)
|
||||
|
||||
response = agent.start("Hello! I'm your self-evolving AI assistant. "
|
||||
"How can I help you today, and how can I learn and improve from our interaction?")
|
||||
@@ -0,0 +1,11 @@
|
||||
from praisonaiagents import Agent
|
||||
|
||||
agent = Agent(
|
||||
instructions="You are a startup insight AI agent. "
|
||||
"Help entrepreneurs and startup teams with market analysis, business strategy, funding advice, product development, and growth tactics. "
|
||||
"Provide actionable insights for building successful startups.",
|
||||
llm="openrouter/moonshotai/kimi-k2"
|
||||
)
|
||||
|
||||
response = agent.start("Hello! I'm your startup insight assistant. "
|
||||
"How can I help you build and grow your startup today?")
|
||||
@@ -0,0 +1,11 @@
|
||||
from praisonaiagents import Agent
|
||||
|
||||
agent = Agent(
|
||||
instructions="You are a system architecture AI agent. "
|
||||
"Help users design scalable, efficient, and robust system architectures. "
|
||||
"Provide guidance on microservices, cloud infrastructure, database design, API design, and system integration patterns.",
|
||||
llm="openrouter/moonshotai/kimi-k2"
|
||||
)
|
||||
|
||||
response = agent.start("Hello! I'm your system architecture assistant. "
|
||||
"How can I help you design and optimize your system architecture today?")
|
||||
@@ -0,0 +1,11 @@
|
||||
from praisonaiagents import Agent
|
||||
|
||||
agent = Agent(
|
||||
instructions="You are a web scraping AI agent. "
|
||||
"Help users create efficient and ethical web scrapers, data extraction tools, and automation scripts. "
|
||||
"Provide guidance on BeautifulSoup, Selenium, Scrapy, and best practices for responsible web scraping.",
|
||||
llm="openrouter/moonshotai/kimi-k2"
|
||||
)
|
||||
|
||||
response = agent.start("Hello! I'm your web scraping assistant. "
|
||||
"How can I help you extract and analyze web data today?")
|
||||
@@ -0,0 +1,9 @@
|
||||
from praisonaiagents import Agent
|
||||
|
||||
agent = Agent(
|
||||
instructions="Research assistant with web search",
|
||||
llm="gemini/gemini-2.5-flash",
|
||||
tools=[{"googleSearch": {}}, ]
|
||||
)
|
||||
|
||||
response = agent.start("Who is Mervin Praison?")
|
||||
@@ -2,6 +2,7 @@ import logging
|
||||
import os
|
||||
import warnings
|
||||
import re
|
||||
import inspect
|
||||
from typing import Any, Dict, List, Optional, Union, Literal, Callable
|
||||
from pydantic import BaseModel
|
||||
import time
|
||||
@@ -379,6 +380,65 @@ class LLM:
|
||||
|
||||
return function_name, arguments, tool_call_id
|
||||
|
||||
def _validate_and_filter_ollama_arguments(self, function_name: str, arguments: Dict[str, Any], available_tools: List) -> Dict[str, Any]:
|
||||
"""
|
||||
Validate and filter tool call arguments for Ollama provider.
|
||||
|
||||
Ollama sometimes generates tool calls with mixed parameters where arguments
|
||||
from different functions are combined. This method validates arguments against
|
||||
the actual function signature and removes invalid parameters.
|
||||
|
||||
Args:
|
||||
function_name: Name of the function to call
|
||||
arguments: Arguments provided in the tool call
|
||||
available_tools: List of available tool functions
|
||||
|
||||
Returns:
|
||||
Filtered arguments dictionary with only valid parameters
|
||||
"""
|
||||
if not available_tools:
|
||||
logging.debug(f"[OLLAMA_FIX] No available tools provided for validation")
|
||||
return arguments
|
||||
|
||||
# Find the target function
|
||||
target_function = None
|
||||
for tool in available_tools:
|
||||
tool_name = getattr(tool, '__name__', str(tool))
|
||||
if tool_name == function_name:
|
||||
target_function = tool
|
||||
break
|
||||
|
||||
if not target_function:
|
||||
logging.debug(f"[OLLAMA_FIX] Function {function_name} not found in available tools")
|
||||
return arguments
|
||||
|
||||
try:
|
||||
# Get function signature
|
||||
sig = inspect.signature(target_function)
|
||||
valid_params = set(sig.parameters.keys())
|
||||
|
||||
# Filter arguments to only include valid parameters
|
||||
filtered_args = {}
|
||||
invalid_params = []
|
||||
|
||||
for param_name, param_value in arguments.items():
|
||||
if param_name in valid_params:
|
||||
filtered_args[param_name] = param_value
|
||||
else:
|
||||
invalid_params.append(param_name)
|
||||
|
||||
if invalid_params:
|
||||
logging.debug(f"[OLLAMA_FIX] Function {function_name} received invalid parameters: {invalid_params}")
|
||||
logging.debug(f"[OLLAMA_FIX] Valid parameters for {function_name}: {list(valid_params)}")
|
||||
logging.debug(f"[OLLAMA_FIX] Original arguments: {arguments}")
|
||||
logging.debug(f"[OLLAMA_FIX] Filtered arguments: {filtered_args}")
|
||||
|
||||
return filtered_args
|
||||
|
||||
except Exception as e:
|
||||
logging.debug(f"[OLLAMA_FIX] Error validating arguments for {function_name}: {e}")
|
||||
return arguments
|
||||
|
||||
def _needs_system_message_skip(self) -> bool:
|
||||
"""Check if this model requires skipping system messages"""
|
||||
if not self.model:
|
||||
@@ -542,6 +602,7 @@ class LLM:
|
||||
- Lists of pre-formatted tools
|
||||
- Callable functions
|
||||
- String function names
|
||||
- Gemini internal tools ({"googleSearch": {}}, {"urlContext": {}}, {"codeExecution": {}})
|
||||
|
||||
Args:
|
||||
tools: List of tools in various formats
|
||||
@@ -588,6 +649,15 @@ class LLM:
|
||||
tool_def = self._generate_tool_definition(tool)
|
||||
if tool_def:
|
||||
formatted_tools.append(tool_def)
|
||||
# Handle Gemini internal tools (e.g., {"googleSearch": {}}, {"urlContext": {}}, {"codeExecution": {}})
|
||||
elif isinstance(tool, dict) and len(tool) == 1:
|
||||
tool_name = next(iter(tool.keys()))
|
||||
gemini_internal_tools = {'googleSearch', 'urlContext', 'codeExecution'}
|
||||
if tool_name in gemini_internal_tools:
|
||||
logging.debug(f"Using Gemini internal tool: {tool_name}")
|
||||
formatted_tools.append(tool)
|
||||
else:
|
||||
logging.debug(f"Skipping unknown tool: {tool_name}")
|
||||
else:
|
||||
logging.debug(f"Skipping tool of unsupported type: {type(tool)}")
|
||||
|
||||
@@ -952,6 +1022,10 @@ class LLM:
|
||||
is_ollama = self._is_ollama_provider()
|
||||
function_name, arguments, tool_call_id = self._extract_tool_call_info(tool_call, is_ollama)
|
||||
|
||||
# Validate and filter arguments for Ollama provider
|
||||
if is_ollama and tools:
|
||||
arguments = self._validate_and_filter_ollama_arguments(function_name, arguments, tools)
|
||||
|
||||
logging.debug(f"[TOOL_EXEC_DEBUG] About to execute tool {function_name} with args: {arguments}")
|
||||
tool_result = execute_tool_fn(function_name, arguments)
|
||||
logging.debug(f"[TOOL_EXEC_DEBUG] Tool execution result: {tool_result}")
|
||||
@@ -1603,6 +1677,10 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
|
||||
is_ollama = self._is_ollama_provider()
|
||||
function_name, arguments, tool_call_id = self._extract_tool_call_info(tool_call, is_ollama)
|
||||
|
||||
# Validate and filter arguments for Ollama provider
|
||||
if is_ollama and tools:
|
||||
arguments = self._validate_and_filter_ollama_arguments(function_name, arguments, tools)
|
||||
|
||||
tool_result = await execute_tool_fn(function_name, arguments)
|
||||
tool_results.append(tool_result) # Store the result
|
||||
|
||||
|
||||
@@ -104,4 +104,27 @@ def supports_streaming_with_tools(model_name: str) -> bool:
|
||||
"""
|
||||
# For now, use the same logic as structured outputs
|
||||
# In the future, this could be a separate list if needed
|
||||
return supports_structured_outputs(model_name)
|
||||
return supports_structured_outputs(model_name)
|
||||
|
||||
|
||||
# Supported Gemini internal tools
|
||||
GEMINI_INTERNAL_TOOLS = {'googleSearch', 'urlContext', 'codeExecution'}
|
||||
|
||||
|
||||
def is_gemini_internal_tool(tool) -> bool:
|
||||
"""
|
||||
Check if a tool is a Gemini internal tool and should be included in formatted tools.
|
||||
|
||||
Gemini internal tools are single-key dictionaries with specific tool names.
|
||||
Examples: {"googleSearch": {}}, {"urlContext": {}}, {"codeExecution": {}}
|
||||
|
||||
Args:
|
||||
tool: The tool to check
|
||||
|
||||
Returns:
|
||||
bool: True if the tool is a recognized Gemini internal tool, False otherwise
|
||||
"""
|
||||
if isinstance(tool, dict) and len(tool) == 1:
|
||||
tool_name = next(iter(tool.keys()))
|
||||
return tool_name in GEMINI_INTERNAL_TOOLS
|
||||
return False
|
||||
@@ -360,6 +360,7 @@ class OpenAIClient:
|
||||
- Callable functions
|
||||
- String function names
|
||||
- MCP tools
|
||||
- Gemini internal tools ({"googleSearch": {}}, {"urlContext": {}}, {"codeExecution": {}})
|
||||
|
||||
Args:
|
||||
tools: List of tools in various formats
|
||||
@@ -404,6 +405,15 @@ class OpenAIClient:
|
||||
tool_def = self._generate_tool_definition_from_name(tool)
|
||||
if tool_def:
|
||||
formatted_tools.append(tool_def)
|
||||
# Handle Gemini internal tools (e.g., {"googleSearch": {}}, {"urlContext": {}}, {"codeExecution": {}})
|
||||
elif isinstance(tool, dict) and len(tool) == 1:
|
||||
tool_name = next(iter(tool.keys()))
|
||||
gemini_internal_tools = {'googleSearch', 'urlContext', 'codeExecution'}
|
||||
if tool_name in gemini_internal_tools:
|
||||
logging.debug(f"Using Gemini internal tool: {tool_name}")
|
||||
formatted_tools.append(tool)
|
||||
else:
|
||||
logging.debug(f"Skipping unknown tool: {tool_name}")
|
||||
else:
|
||||
logging.debug(f"Skipping tool of unsupported type: {type(tool)}")
|
||||
|
||||
|
||||
@@ -116,8 +116,17 @@ class MinimalTelemetry:
|
||||
|
||||
self._metrics["agent_executions"] += 1
|
||||
|
||||
# In a real implementation, this would send to a backend
|
||||
# For now, just log at debug level
|
||||
# Send event to PostHog
|
||||
if self._posthog:
|
||||
self._posthog.capture(
|
||||
distinct_id=self.session_id,
|
||||
event='agent_execution',
|
||||
properties={
|
||||
'success': success,
|
||||
'session_id': self.session_id
|
||||
}
|
||||
)
|
||||
|
||||
self.logger.debug(f"Agent execution tracked: success={success}")
|
||||
|
||||
def track_task_completion(self, task_name: str = None, success: bool = True):
|
||||
@@ -133,6 +142,17 @@ class MinimalTelemetry:
|
||||
|
||||
self._metrics["task_completions"] += 1
|
||||
|
||||
# Send event to PostHog
|
||||
if self._posthog:
|
||||
self._posthog.capture(
|
||||
distinct_id=self.session_id,
|
||||
event='task_completion',
|
||||
properties={
|
||||
'success': success,
|
||||
'session_id': self.session_id
|
||||
}
|
||||
)
|
||||
|
||||
self.logger.debug(f"Task completion tracked: success={success}")
|
||||
|
||||
def track_tool_usage(self, tool_name: str, success: bool = True):
|
||||
@@ -148,6 +168,18 @@ class MinimalTelemetry:
|
||||
|
||||
self._metrics["tool_calls"] += 1
|
||||
|
||||
# Send event to PostHog
|
||||
if self._posthog:
|
||||
self._posthog.capture(
|
||||
distinct_id=self.session_id,
|
||||
event='tool_usage',
|
||||
properties={
|
||||
'tool_name': tool_name,
|
||||
'success': success,
|
||||
'session_id': self.session_id
|
||||
}
|
||||
)
|
||||
|
||||
# Only track tool name, not arguments or results
|
||||
self.logger.debug(f"Tool usage tracked: {tool_name}, success={success}")
|
||||
|
||||
@@ -163,6 +195,17 @@ class MinimalTelemetry:
|
||||
|
||||
self._metrics["errors"] += 1
|
||||
|
||||
# Send event to PostHog
|
||||
if self._posthog:
|
||||
self._posthog.capture(
|
||||
distinct_id=self.session_id,
|
||||
event='error',
|
||||
properties={
|
||||
'error_type': error_type or 'unknown',
|
||||
'session_id': self.session_id
|
||||
}
|
||||
)
|
||||
|
||||
# Only track error type, not full error messages
|
||||
self.logger.debug(f"Error tracked: type={error_type or 'unknown'}")
|
||||
|
||||
@@ -176,6 +219,17 @@ class MinimalTelemetry:
|
||||
if not self.enabled:
|
||||
return
|
||||
|
||||
# Send event to PostHog
|
||||
if self._posthog:
|
||||
self._posthog.capture(
|
||||
distinct_id=self.session_id,
|
||||
event='feature_usage',
|
||||
properties={
|
||||
'feature_name': feature_name,
|
||||
'session_id': self.session_id
|
||||
}
|
||||
)
|
||||
|
||||
# Track which features are being used
|
||||
self.logger.debug(f"Feature usage tracked: {feature_name}")
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "praisonaiagents"
|
||||
version = "0.0.132"
|
||||
version = "0.0.134"
|
||||
description = "Praison AI agents for completing complex tasks with Self Reflection Agents"
|
||||
requires-python = ">=3.10"
|
||||
authors = [
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Test script to verify Ollama sequential tool calling argument mixing fix.
|
||||
|
||||
This test validates that the parameter validation and filtering fix correctly handles
|
||||
the case where Ollama generates tool calls with mixed parameters from different functions.
|
||||
"""
|
||||
|
||||
import logging
|
||||
from praisonaiagents.llm.llm import LLM
|
||||
|
||||
# Enable debug logging
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
|
||||
# Test functions matching the issue description
|
||||
def get_stock_price(company_name: str) -> str:
|
||||
"""
|
||||
Get the stock price of a company
|
||||
|
||||
Args:
|
||||
company_name (str): The name of the company
|
||||
|
||||
Returns:
|
||||
str: The stock price of the company
|
||||
"""
|
||||
return f"The stock price of {company_name} is 100"
|
||||
|
||||
def multiply(a: int, b: int) -> int:
|
||||
"""
|
||||
Multiply two numbers
|
||||
"""
|
||||
return a * b
|
||||
|
||||
def test_ollama_argument_validation():
|
||||
"""
|
||||
Test the Ollama argument validation and filtering functionality.
|
||||
"""
|
||||
print("Testing Ollama argument validation and filtering...")
|
||||
|
||||
llm = LLM(model="ollama/llama3.2")
|
||||
tools = [get_stock_price, multiply]
|
||||
|
||||
# Test case 1: Valid arguments (should pass through unchanged)
|
||||
print("\n1. Testing valid arguments:")
|
||||
valid_args = {"a": 100, "b": 2}
|
||||
filtered_args = llm._validate_and_filter_ollama_arguments("multiply", valid_args, tools)
|
||||
print(f"Original: {valid_args}")
|
||||
print(f"Filtered: {filtered_args}")
|
||||
assert filtered_args == valid_args, "Valid arguments should pass through unchanged"
|
||||
print("✅ Valid arguments test passed")
|
||||
|
||||
# Test case 2: Mixed arguments (the actual issue from #918)
|
||||
print("\n2. Testing mixed arguments (the main issue):")
|
||||
mixed_args = {"a": "get_stock_price", "company_name": "Google", "b": "2"}
|
||||
filtered_args = llm._validate_and_filter_ollama_arguments("multiply", mixed_args, tools)
|
||||
expected_filtered = {"a": "get_stock_price", "b": "2"} # Should remove 'company_name'
|
||||
print(f"Original: {mixed_args}")
|
||||
print(f"Filtered: {filtered_args}")
|
||||
print(f"Expected: {expected_filtered}")
|
||||
assert filtered_args == expected_filtered, f"Expected {expected_filtered}, got {filtered_args}"
|
||||
print("✅ Mixed arguments filtering test passed")
|
||||
|
||||
# Test case 3: All invalid arguments
|
||||
print("\n3. Testing all invalid arguments:")
|
||||
invalid_args = {"invalid_param1": "value1", "invalid_param2": "value2"}
|
||||
filtered_args = llm._validate_and_filter_ollama_arguments("multiply", invalid_args, tools)
|
||||
expected_empty = {}
|
||||
print(f"Original: {invalid_args}")
|
||||
print(f"Filtered: {filtered_args}")
|
||||
assert filtered_args == expected_empty, "All invalid arguments should be filtered out"
|
||||
print("✅ Invalid arguments filtering test passed")
|
||||
|
||||
# Test case 4: Function not found in tools
|
||||
print("\n4. Testing function not found:")
|
||||
some_args = {"param": "value"}
|
||||
filtered_args = llm._validate_and_filter_ollama_arguments("nonexistent_function", some_args, tools)
|
||||
print(f"Original: {some_args}")
|
||||
print(f"Filtered: {filtered_args}")
|
||||
assert filtered_args == some_args, "Arguments should pass through if function not found"
|
||||
print("✅ Function not found test passed")
|
||||
|
||||
# Test case 5: Empty tools list
|
||||
print("\n5. Testing empty tools list:")
|
||||
some_args = {"param": "value"}
|
||||
filtered_args = llm._validate_and_filter_ollama_arguments("multiply", some_args, [])
|
||||
print(f"Original: {some_args}")
|
||||
print(f"Filtered: {filtered_args}")
|
||||
assert filtered_args == some_args, "Arguments should pass through if no tools provided"
|
||||
print("✅ Empty tools test passed")
|
||||
|
||||
print("\n🎉 All Ollama argument validation tests passed!")
|
||||
return True
|
||||
|
||||
def test_provider_detection():
|
||||
"""
|
||||
Test the Ollama provider detection functionality.
|
||||
"""
|
||||
print("\nTesting Ollama provider detection...")
|
||||
|
||||
# Test Ollama provider detection
|
||||
ollama_llm = LLM(model="ollama/llama3.2")
|
||||
assert ollama_llm._is_ollama_provider(), "Should detect ollama/ prefix"
|
||||
print("✅ Ollama prefix detection works")
|
||||
|
||||
# Test non-Ollama provider
|
||||
openai_llm = LLM(model="gpt-4o-mini")
|
||||
assert not openai_llm._is_ollama_provider(), "Should not detect OpenAI as Ollama"
|
||||
print("✅ Non-Ollama provider detection works")
|
||||
|
||||
print("✅ Provider detection tests passed!")
|
||||
return True
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("Running Ollama sequential tool calling fix tests...")
|
||||
print("=" * 60)
|
||||
|
||||
# Run tests
|
||||
try:
|
||||
test_provider_detection()
|
||||
test_ollama_argument_validation()
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("🎉 ALL TESTS PASSED!")
|
||||
print("The Ollama sequential tool calling argument mixing issue has been fixed!")
|
||||
|
||||
except Exception as e:
|
||||
print(f"\n❌ TEST FAILED: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
Generated
+1
-1
@@ -2375,7 +2375,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "praisonaiagents"
|
||||
version = "0.0.132"
|
||||
version = "0.0.134"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "aiohttp" },
|
||||
|
||||
@@ -3,8 +3,8 @@ class Praisonai < Formula
|
||||
|
||||
desc "AI tools for various AI applications"
|
||||
homepage "https://github.com/MervinPraison/PraisonAI"
|
||||
url "https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v2.2.58.tar.gz"
|
||||
sha256 `curl -sL https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v2.2.58.tar.gz | shasum -a 256`.split.first
|
||||
url "https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v2.2.61.tar.gz"
|
||||
sha256 `curl -sL https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v2.2.61.tar.gz | shasum -a 256`.split.first
|
||||
license "MIT"
|
||||
|
||||
depends_on "[email protected]"
|
||||
|
||||
@@ -57,7 +57,7 @@ class CloudDeployer:
|
||||
file.write("FROM python:3.11-slim\n")
|
||||
file.write("WORKDIR /app\n")
|
||||
file.write("COPY . .\n")
|
||||
file.write("RUN pip install flask praisonai==2.2.58 gunicorn markdown\n")
|
||||
file.write("RUN pip install flask praisonai==2.2.61 gunicorn markdown\n")
|
||||
file.write("EXPOSE 8080\n")
|
||||
file.write('CMD ["gunicorn", "-b", "0.0.0.0:8080", "api:app"]\n')
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "PraisonAI"
|
||||
version = "2.2.58"
|
||||
version = "2.2.61"
|
||||
description = "PraisonAI is an AI Agents Framework with Self Reflection. PraisonAI application combines PraisonAI Agents, AutoGen, and CrewAI into a low-code solution for building and managing multi-agent LLM systems, focusing on simplicity, customisation, and efficient human-agent collaboration."
|
||||
readme = "README.md"
|
||||
license = ""
|
||||
@@ -12,7 +12,7 @@ dependencies = [
|
||||
"rich>=13.7",
|
||||
"markdown>=3.5",
|
||||
"pyparsing>=3.0.0",
|
||||
"praisonaiagents>=0.0.132",
|
||||
"praisonaiagents>=0.0.134",
|
||||
"python-dotenv>=0.19.0",
|
||||
"instructor>=1.3.3",
|
||||
"PyYAML>=6.0",
|
||||
@@ -90,12 +90,12 @@ call = [
|
||||
"openai>=1.54.0",
|
||||
]
|
||||
train = []
|
||||
crewai = ["crewai>=0.32.0", "praisonai-tools>=0.0.15"]
|
||||
autogen = ["pyautogen>=0.2.19", "praisonai-tools>=0.0.15", "crewai"]
|
||||
crewai = ["crewai>=0.32.0", "praisonai-tools>=0.0.22"]
|
||||
autogen = ["pyautogen>=0.10.0", "praisonai-tools>=0.0.22", "crewai"]
|
||||
|
||||
[tool.poetry]
|
||||
name = "PraisonAI"
|
||||
version = "2.2.58"
|
||||
version = "2.2.61"
|
||||
description = "PraisonAI is an AI Agents Framework with Self Reflection. PraisonAI application combines PraisonAI Agents, AutoGen, and CrewAI into a low-code solution for building and managing multi-agent LLM systems, focusing on simplicity, customisation, and efficient human-agent collaboration."
|
||||
authors = ["Mervin Praison"]
|
||||
license = ""
|
||||
@@ -113,14 +113,14 @@ python = ">=3.10,<3.13"
|
||||
rich = ">=13.7"
|
||||
markdown = ">=3.5"
|
||||
pyparsing = ">=3.0.0"
|
||||
praisonaiagents = ">=0.0.132"
|
||||
praisonaiagents = ">=0.0.134"
|
||||
python-dotenv = ">=0.19.0"
|
||||
instructor = ">=1.3.3"
|
||||
PyYAML = ">=6.0"
|
||||
mcp = ">=1.6.0"
|
||||
pyautogen = {version = ">=0.2.19", optional = true}
|
||||
crewai = {version = ">=0.32.0", optional = true}
|
||||
praisonai-tools = {version = ">=0.0.15", optional = true}
|
||||
praisonai-tools = {version = ">=0.0.22", optional = true}
|
||||
chainlit = {version = "==2.5.5", optional = true}
|
||||
gradio = {version = ">=4.26.0", optional = true}
|
||||
flask = {version = ">=3.0.0", optional = true}
|
||||
|
||||
Generated
+597
-310
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,40 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Test script to verify the refactored Gemini tools logic."""
|
||||
|
||||
import logging
|
||||
import sys
|
||||
import os
|
||||
|
||||
# Setup logging to see debug messages
|
||||
logging.basicConfig(level=logging.DEBUG, format='%(levelname)s: %(message)s')
|
||||
|
||||
# Add the src directory to the Python path
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'src', 'praisonai-agents'))
|
||||
|
||||
try:
|
||||
from praisonaiagents.llm.llm import LLM
|
||||
from praisonaiagents.llm.openai_client import OpenAIClient
|
||||
|
||||
print("Testing LLM tool formatting...")
|
||||
llm = LLM(model="gpt-4o-mini")
|
||||
tools = [
|
||||
{'googleSearch': {}}, # Valid Gemini tool
|
||||
{'urlContext': {}}, # Valid Gemini tool
|
||||
{'codeExecution': {}}, # Valid Gemini tool
|
||||
{'unknown': {}} # Invalid tool - should be skipped
|
||||
]
|
||||
|
||||
formatted = llm._format_tools_for_litellm(tools)
|
||||
print(f"LLM formatted tools ({len(formatted)} tools):", formatted)
|
||||
|
||||
print("\nTesting OpenAI client tool formatting...")
|
||||
client = OpenAIClient(api_key="not-needed")
|
||||
formatted = client.format_tools(tools)
|
||||
print(f"OpenAI client formatted tools ({len(formatted)} tools):", formatted)
|
||||
|
||||
print("\nTest completed successfully!")
|
||||
|
||||
except Exception as e:
|
||||
import traceback
|
||||
print(f"Error: {e}")
|
||||
traceback.print_exc()
|
||||
Reference in new issue
Block a user